Files
CRM_Montzar/apps/web/src/components/CustomFieldsSection.vue
T
MahdiandCursor 4abab55a6b feat(web): migrate CRM screens to Ant Design Vue with central theme
Adopt ant-design-vue and a shared antdTheme for leads, pipeline, invoices, and shared form controls under strict single-page governance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 19:39:59 +03:30

134 lines
3.6 KiB
Vue

<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import {
Checkbox as ACheckbox,
Col as ACol,
FormItem as AFormItem,
Input as AInput,
InputNumber as AInputNumber,
Row as ARow,
Select as ASelect,
Spin as ASpin,
Typography,
} from 'ant-design-vue';
import { api } from '@/lib/api';
import JalaliDateInput from '@/components/JalaliDateInput.vue';
import type {
CustomFieldDefinitionDto,
CustomFieldEntity,
} from '@mento/shared';
const { Title: ATypographyTitle } = Typography;
const props = defineProps<{
entityType: CustomFieldEntity;
modelValue: Record<string, unknown>;
}>();
const emit = defineEmits<{
'update:modelValue': [value: Record<string, unknown>];
}>();
const fields = ref<CustomFieldDefinitionDto[]>([]);
const loading = ref(false);
async function load() {
loading.value = true;
try {
fields.value = (
await api.get<CustomFieldDefinitionDto[]>('/custom-fields', {
params: { entityType: props.entityType, activeOnly: true },
})
).data;
} catch {
fields.value = [];
} finally {
loading.value = false;
}
}
function setValue(key: string, value: unknown) {
emit('update:modelValue', { ...props.modelValue, [key]: value });
}
function displayValue(key: string) {
const v = props.modelValue[key];
if (v == null) return '';
if (typeof v === 'boolean') return v;
return String(v);
}
function selectOptions(field: CustomFieldDefinitionDto) {
return (field.options ?? []).map((opt) => ({ value: opt, label: opt }));
}
watch(
() => props.entityType,
() => {
void load();
},
);
onMounted(load);
</script>
<template>
<a-spin v-if="loading" />
<template v-else-if="fields.length">
<a-typography-title :level="5" :style="{ marginTop: '16px' }">
فیلدهای سفارشی
</a-typography-title>
<a-row :gutter="[16, 0]">
<a-col v-for="field in fields" :key="field.id" :xs="24" :md="12">
<a-form-item :label="field.label" :required="field.required">
<a-select
v-if="field.fieldType === 'select'"
:id="`cf-${field.key}`"
allow-clear
:value="String(displayValue(field.key)) || undefined"
:options="selectOptions(field)"
placeholder="انتخاب کنید"
style="width: 100%"
@update:value="setValue(field.key, $event ?? '')"
/>
<a-checkbox
v-else-if="field.fieldType === 'boolean'"
:id="`cf-${field.key}`"
:checked="Boolean(props.modelValue[field.key])"
@update:checked="setValue(field.key, $event)"
>
فعال
</a-checkbox>
<a-input-number
v-else-if="field.fieldType === 'number'"
:id="`cf-${field.key}`"
:value="
props.modelValue[field.key] != null
? Number(props.modelValue[field.key])
: undefined
"
style="width: 100%"
@update:value="setValue(field.key, $event == null ? null : Number($event))"
/>
<JalaliDateInput
v-else-if="field.fieldType === 'date'"
:id="`cf-${field.key}`"
:model-value="String(displayValue(field.key))"
@update:model-value="setValue(field.key, $event || null)"
/>
<a-input
v-else
:id="`cf-${field.key}`"
:value="String(displayValue(field.key))"
@update:value="setValue(field.key, $event)"
/>
</a-form-item>
</a-col>
</a-row>
</template>
</template>