fix(settings): fix variable slot dropdown and saving multi-variables

This commit is contained in:
2026-08-15 22:07:19 +03:30
parent 9fbaf3c7fe
commit 407a5124ac
+80 -32
View File
@@ -14,7 +14,7 @@
<div class="flex-grow-1"> <div class="flex-grow-1">
<h2 class="text-lg font-bold text-color m-0 mb-1">قالبهای پیامک</h2> <h2 class="text-lg font-bold text-color m-0 mb-1">قالبهای پیامک</h2>
<p class="text-sm text-muted m-0 line-height-3"> <p class="text-sm text-muted m-0 line-height-3">
شناسه قالب و تعداد و نام متغیرهای sms.ir را بر اساس قالب تعریف شده در پنل پیامک تنظیم کنید. میتوانید هر تعداد متغیر که میخواهید تعریف یا حذف نمایید. شناسه قالب و تعداد و نام متغیرهای sms.ir را بر اساس قالب پنل پیامک تنظیم کنید. میتوانید هر تعداد متغیر که میخواهید تعریف یا حذف نمایید.
</p> </p>
</div> </div>
</div> </div>
@@ -77,7 +77,7 @@
</div> </div>
<div <div
v-if="!templateForm[template.key].variables || templateForm[template.key].variables.length === 0" v-if="!templateForm[template.key]?.variables || templateForm[template.key]?.variables.length === 0"
class="p-3 text-center border-1 border-dashed border-round surface-card text-muted text-xs line-height-3" class="p-3 text-center border-1 border-dashed border-round surface-card text-muted text-xs line-height-3"
> >
هیچ متغیری برای این قالب تعریف نشده است. پیامک بدون متغیر ارسال خواهد شد. هیچ متغیری برای این قالب تعریف نشده است. پیامک بدون متغیر ارسال خواهد شد.
@@ -87,13 +87,13 @@
<div <div
v-for="(variable, vIdx) in templateForm[template.key].variables" v-for="(variable, vIdx) in templateForm[template.key].variables"
:key="variable.id || vIdx" :key="variable.id || vIdx"
class="flex flex-column sm:flex-row sm:align-items-center gap-2 p-2 border-round surface-card border-1 border-color" class="flex flex-column md:flex-row md:align-items-center gap-3 p-3 border-round surface-card border-1 border-color"
> >
<div class="flex align-items-center gap-2 sm:w-14rem flex-shrink-0"> <div class="flex flex-column gap-1 md:w-16rem flex-shrink-0">
<span class="text-xs text-muted font-bold ml-1">{{ vIdx + 1 }}.</span> <span class="text-xs text-muted font-semibold">مقدار ارسالی از سیستم:</span>
<Select <Select
v-model="variable.slot" v-model="variable.slot"
:options="template.availableSlots || []" :options="getAvailableSlots(template.key)"
optionLabel="label" optionLabel="label"
optionValue="slot" optionValue="slot"
placeholder="انتخاب مقدار داده" placeholder="انتخاب مقدار داده"
@@ -102,16 +102,18 @@
/> />
</div> </div>
<div class="flex-grow-1 flex align-items-center gap-2"> <div class="flex-grow-1 flex flex-column gap-1">
<span class="text-xs text-muted font-semibold">نام متغیر در sms.ir:</span>
<div class="flex align-items-center gap-2">
<InputText <InputText
v-model.trim="variable.name" v-model.trim="variable.name"
class="w-full text-sm" class="w-full text-sm"
dir="ltr" dir="ltr"
:placeholder="getSlotDefaultName(template, variable.slot) || 'نام متغیر در sms.ir'" :placeholder="getSlotDefaultName(template.key, variable.slot) || 'نام متغیر در sms.ir'"
autocomplete="off" autocomplete="off"
/> />
<Tag <Tag
:value="`#${variable.name || getSlotDefaultName(template, variable.slot) || '...'}#`" :value="`#${variable.name || getSlotDefaultName(template.key, variable.slot) || '...'}#`"
severity="secondary" severity="secondary"
class="text-xs flex-shrink-0 font-mono" class="text-xs flex-shrink-0 font-mono"
/> />
@@ -129,6 +131,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
</div> </div>
@@ -278,6 +281,45 @@ const selectedFileName = ref('');
const isImporting = ref(false); const isImporting = ref(false);
const importResult = ref(null); const importResult = ref(null);
const TEMPLATE_SLOT_DEFS = {
accountCreated: [
{ slot: 'username', label: 'نام کاربری', defaultName: 'user' },
{ slot: 'password', label: 'رمز عبور', defaultName: 'password' },
{ slot: 'fullName', label: 'نام و نام خانوادگی', defaultName: 'name' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
],
classRegistered: [
{ slot: 'className', label: 'نام کلاس', defaultName: 'className' },
{ slot: 'courseName', label: 'نام دوره', defaultName: 'courseName' },
{ slot: 'fullName', label: 'نام کارآموز', defaultName: 'fullName' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
],
classReminder: [
{ slot: 'className', label: 'نام کلاس', defaultName: 'className' },
{ slot: 'time', label: 'ساعت برگزاری', defaultName: 'time' },
{ slot: 'place', label: 'مکان برگزاری', defaultName: 'place' },
{ slot: 'courseName', label: 'نام دوره', defaultName: 'courseName' },
{ slot: 'fullName', label: 'نام کاربر', defaultName: 'fullName' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
],
invoiceCreated: [
{ slot: 'amount', label: 'مبلغ صورتحساب (تومان)', defaultName: 'PAYMENT_PRICE' },
{ slot: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ slot: 'course', label: 'نام دوره / کلاس', defaultName: 'COURSE' },
{ slot: 'phoneNumber', label: 'شماره همراه', defaultName: 'MOBILE' }
]
};
const getAvailableSlots = (templateKey) => {
return TEMPLATE_SLOT_DEFS[templateKey] || [];
};
const getSlotDefaultName = (templateKey, slotKey) => {
const slots = getAvailableSlots(templateKey);
const found = slots.find((s) => s.slot === slotKey);
return found?.defaultName || '';
};
const lockedAtLabel = computed(() => { const lockedAtLabel = computed(() => {
if (!lockedAt.value) return ''; if (!lockedAt.value) return '';
const date = new Date(lockedAt.value); const date = new Date(lockedAt.value);
@@ -285,30 +327,24 @@ const lockedAtLabel = computed(() => {
return `قفل شده در ${date.toLocaleString('fa-IR')}`; return `قفل شده در ${date.toLocaleString('fa-IR')}`;
}); });
const getSlotDefaultName = (template, slotKey) => {
const slot = (template?.availableSlots || []).find((s) => s.slot === slotKey);
return slot?.defaultName || '';
};
const onSlotChange = (templateKey, vIdx) => { const onSlotChange = (templateKey, vIdx) => {
const form = templateForm.value[templateKey]; const form = templateForm.value[templateKey];
if (!form || !form.variables[vIdx]) return; if (!form || !form.variables[vIdx]) return;
const template = smsTemplates.value.find((t) => t.key === templateKey);
const currentVar = form.variables[vIdx]; const currentVar = form.variables[vIdx];
if (!currentVar.name) { const defaultName = getSlotDefaultName(templateKey, currentVar.slot);
currentVar.name = getSlotDefaultName(template, currentVar.slot); if (!currentVar.name || currentVar.name === 'param') {
currentVar.name = defaultName;
} }
}; };
const addVariable = (templateKey) => { const addVariable = (templateKey) => {
const form = templateForm.value[templateKey]; const form = templateForm.value[templateKey];
if (!form) return; if (!form) return;
const template = smsTemplates.value.find((t) => t.key === templateKey); const availableSlots = getAvailableSlots(templateKey);
const availableSlots = template?.availableSlots || [];
const existingSlots = new Set((form.variables || []).map((v) => v.slot)); const existingSlots = new Set((form.variables || []).map((v) => v.slot));
const unusedSlot = availableSlots.find((s) => !existingSlots.has(s.slot)); const unusedSlot = availableSlots.find((s) => !existingSlots.has(s.slot));
const selectedSlot = unusedSlot || availableSlots[0] || { slot: 'param', defaultName: 'param' }; const selectedSlot = unusedSlot || availableSlots[0] || { slot: 'amount', defaultName: 'PAYMENT_PRICE' };
form.variables.push({ form.variables.push({
id: `var_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`, id: `var_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
@@ -408,16 +444,28 @@ const confirmImport = () => {
}); });
}; };
const emptyFormEntry = (template) => ({ const emptyFormEntry = (template) => {
templateId: template?.templateId || '', const slots = getAvailableSlots(template?.key);
variables: Array.isArray(template?.variables) let vars = [];
? template.variables.map((variable) => ({ if (Array.isArray(template?.variables) && template.variables.length > 0) {
vars = template.variables.map((variable) => ({
id: `var_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`, id: `var_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
slot: variable.slot, slot: variable.slot || slots[0]?.slot || 'amount',
name: variable.name || '' name: variable.name || getSlotDefaultName(template?.key, variable.slot) || ''
})) }));
: [] } else if (!template?.variables) {
}); vars = slots.map((s) => ({
id: `var_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
slot: s.slot,
name: s.defaultName
}));
}
return {
templateId: template?.templateId || '',
variables: vars
};
};
const applySmsTemplates = (payload) => { const applySmsTemplates = (payload) => {
const data = payload?.data?.data || payload?.data || payload || {}; const data = payload?.data?.data || payload?.data || payload || {};
@@ -449,12 +497,12 @@ const saveSmsTemplates = async () => {
smsTemplates.value.forEach((template) => { smsTemplates.value.forEach((template) => {
const entry = templateForm.value[template.key] || emptyFormEntry(template); const entry = templateForm.value[template.key] || emptyFormEntry(template);
smsTemplatesPayload[template.key] = { smsTemplatesPayload[template.key] = {
templateId: entry.templateId || '', templateId: String(entry.templateId || '').trim(),
variables: (entry.variables || []) variables: (entry.variables || [])
.filter((v) => v.slot) .filter((v) => v.slot && String(v.name || '').trim())
.map((v) => ({ .map((v) => ({
slot: v.slot, slot: v.slot,
name: v.name || getSlotDefaultName(template, v.slot) || v.slot name: String(v.name).trim().replace(/^#+|#+$/g, '')
})) }))
}; };
}); });