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>
This commit is contained in:
@@ -1,13 +1,25 @@
|
||||
<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 { UiField, UiInput, UiSelect } from '@/components/ui';
|
||||
import type {
|
||||
CustomFieldDefinitionDto,
|
||||
CustomFieldEntity,
|
||||
} from '@mento/shared';
|
||||
|
||||
const { Title: ATypographyTitle } = Typography;
|
||||
|
||||
const props = defineProps<{
|
||||
entityType: CustomFieldEntity;
|
||||
modelValue: Record<string, unknown>;
|
||||
@@ -46,6 +58,10 @@ function displayValue(key: string) {
|
||||
return String(v);
|
||||
}
|
||||
|
||||
function selectOptions(field: CustomFieldDefinitionDto) {
|
||||
return (field.options ?? []).map((opt) => ({ value: opt, label: opt }));
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.entityType,
|
||||
() => {
|
||||
@@ -57,110 +73,61 @@ onMounted(load);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="loading" class="muted">بارگذاری فیلدهای سفارشی…</div>
|
||||
<div v-else-if="fields.length" class="custom-block">
|
||||
<h3 class="custom-title">فیلدهای سفارشی</h3>
|
||||
<div class="crm-form-grid-2">
|
||||
<UiField
|
||||
v-for="field in fields"
|
||||
:key="field.id"
|
||||
:label="field.label"
|
||||
:required="field.required"
|
||||
:for-id="`cf-${field.key}`"
|
||||
>
|
||||
<UiSelect
|
||||
v-if="field.fieldType === 'select'"
|
||||
:id="`cf-${field.key}`"
|
||||
:model-value="String(displayValue(field.key))"
|
||||
@update:model-value="setValue(field.key, $event)"
|
||||
>
|
||||
<option value="">انتخاب کنید</option>
|
||||
<option v-for="opt in field.options" :key="opt" :value="opt">
|
||||
{{ opt }}
|
||||
</option>
|
||||
</UiSelect>
|
||||
|
||||
<label v-else-if="field.fieldType === 'boolean'" class="cf-check">
|
||||
<input
|
||||
<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}`"
|
||||
type="checkbox"
|
||||
:checked="Boolean(props.modelValue[field.key])"
|
||||
@change="
|
||||
setValue(field.key, ($event.target as HTMLInputElement).checked)
|
||||
"
|
||||
allow-clear
|
||||
:value="String(displayValue(field.key)) || undefined"
|
||||
:options="selectOptions(field)"
|
||||
placeholder="انتخاب کنید"
|
||||
style="width: 100%"
|
||||
@update:value="setValue(field.key, $event ?? '')"
|
||||
/>
|
||||
<span>فعال</span>
|
||||
</label>
|
||||
|
||||
<UiInput
|
||||
v-else-if="field.fieldType === 'number'"
|
||||
:id="`cf-${field.key}`"
|
||||
type="number"
|
||||
:model-value="props.modelValue[field.key] != null ? Number(props.modelValue[field.key]) : ''"
|
||||
@update:model-value="
|
||||
setValue(
|
||||
field.key,
|
||||
$event === '' || $event == null ? null : Number($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>
|
||||
|
||||
<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-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))"
|
||||
/>
|
||||
|
||||
<UiInput
|
||||
v-else
|
||||
:id="`cf-${field.key}`"
|
||||
type="text"
|
||||
:model-value="String(displayValue(field.key))"
|
||||
@update:model-value="setValue(field.key, $event)"
|
||||
/>
|
||||
</UiField>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<style scoped>
|
||||
.custom-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-3);
|
||||
padding-top: var(--sp-4);
|
||||
border-top: 1px dashed var(--line);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-title {
|
||||
margin: 0;
|
||||
font-size: var(--fs-sm);
|
||||
font-weight: var(--fw-semibold);
|
||||
color: var(--ink-secondary);
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
font-size: var(--fs-xs);
|
||||
}
|
||||
|
||||
.cf-check {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-2);
|
||||
min-height: var(--control-h-md);
|
||||
font-size: var(--fs-base);
|
||||
font-weight: var(--fw-medium);
|
||||
color: var(--ink);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cf-check input[type='checkbox'] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
accent-color: var(--accent);
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user