From ffd74a749fd7e9b8af88ba6177da7f8bc3e8d53d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 18 Nov 2025 10:58:27 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E3=80=90ele=E3=80=91=E3=80=90crm?= =?UTF-8?q?=E3=80=91receivable=20=E8=BF=81=E7=A7=BB=E7=9A=84=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-ele/src/router/routes/modules/crm.ts | 36 +-- .../views/crm/receivable/components/data.ts | 73 +++++ .../crm/receivable/components/detail-list.vue | 144 +++++++++ .../views/crm/receivable/components/index.ts | 1 + apps/web-ele/src/views/crm/receivable/data.ts | 300 ++++++++++++++++++ .../src/views/crm/receivable/detail/data.ts | 105 ++++++ .../src/views/crm/receivable/detail/index.vue | 133 ++++++++ .../crm/receivable/detail/modules/info.vue | 37 +++ .../src/views/crm/receivable/index.vue | 253 +++++++++++++++ .../src/views/crm/receivable/modules/form.vue | 102 ++++++ .../crm/receivable/plan/components/data.ts | 62 ++++ .../plan/components/detail-list.vue | 147 +++++++++ .../crm/receivable/plan/components/index.ts | 2 + .../src/views/crm/receivable/plan/data.ts | 288 +++++++++++++++++ .../views/crm/receivable/plan/detail/data.ts | 124 ++++++++ .../crm/receivable/plan/detail/index.vue | 138 ++++++++ .../receivable/plan/detail/modules/info.vue | 37 +++ .../src/views/crm/receivable/plan/index.vue | 218 +++++++++++++ .../crm/receivable/plan/modules/form.vue | 88 +++++ 19 files changed, 2270 insertions(+), 18 deletions(-) create mode 100644 apps/web-ele/src/views/crm/receivable/components/data.ts create mode 100644 apps/web-ele/src/views/crm/receivable/components/detail-list.vue create mode 100644 apps/web-ele/src/views/crm/receivable/components/index.ts create mode 100644 apps/web-ele/src/views/crm/receivable/data.ts create mode 100644 apps/web-ele/src/views/crm/receivable/detail/data.ts create mode 100644 apps/web-ele/src/views/crm/receivable/detail/index.vue create mode 100644 apps/web-ele/src/views/crm/receivable/detail/modules/info.vue create mode 100644 apps/web-ele/src/views/crm/receivable/index.vue create mode 100644 apps/web-ele/src/views/crm/receivable/modules/form.vue create mode 100644 apps/web-ele/src/views/crm/receivable/plan/components/data.ts create mode 100644 apps/web-ele/src/views/crm/receivable/plan/components/detail-list.vue create mode 100644 apps/web-ele/src/views/crm/receivable/plan/components/index.ts create mode 100644 apps/web-ele/src/views/crm/receivable/plan/data.ts create mode 100644 apps/web-ele/src/views/crm/receivable/plan/detail/data.ts create mode 100644 apps/web-ele/src/views/crm/receivable/plan/detail/index.vue create mode 100644 apps/web-ele/src/views/crm/receivable/plan/detail/modules/info.vue create mode 100644 apps/web-ele/src/views/crm/receivable/plan/index.vue create mode 100644 apps/web-ele/src/views/crm/receivable/plan/modules/form.vue diff --git a/apps/web-ele/src/router/routes/modules/crm.ts b/apps/web-ele/src/router/routes/modules/crm.ts index 5875211ba..0b36acbf7 100644 --- a/apps/web-ele/src/router/routes/modules/crm.ts +++ b/apps/web-ele/src/router/routes/modules/crm.ts @@ -47,24 +47,24 @@ const routes: RouteRecordRaw[] = [ // }, // component: () => import('#/views/crm/contract/detail/index.vue'), // }, - // { - // path: 'receivable-plan/detail/:id', - // name: 'CrmReceivablePlanDetail', - // meta: { - // title: '回款计划详情', - // activePath: '/crm/receivable-plan', - // }, - // component: () => import('#/views/crm/receivable/plan/detail/index.vue'), - // }, - // { - // path: 'receivable/detail/:id', - // name: 'CrmReceivableDetail', - // meta: { - // title: '回款详情', - // activePath: '/crm/receivable', - // }, - // component: () => import('#/views/crm/receivable/detail/index.vue'), - // }, + { + path: 'receivable-plan/detail/:id', + name: 'CrmReceivablePlanDetail', + meta: { + title: '回款计划详情', + activePath: '/crm/receivable-plan', + }, + component: () => import('#/views/crm/receivable/plan/detail/index.vue'), + }, + { + path: 'receivable/detail/:id', + name: 'CrmReceivableDetail', + meta: { + title: '回款详情', + activePath: '/crm/receivable', + }, + component: () => import('#/views/crm/receivable/detail/index.vue'), + }, // { // path: 'contact/detail/:id', // name: 'CrmContactDetail', diff --git a/apps/web-ele/src/views/crm/receivable/components/data.ts b/apps/web-ele/src/views/crm/receivable/components/data.ts new file mode 100644 index 000000000..4f3f0e134 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/components/data.ts @@ -0,0 +1,73 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 详情列表的字段 */ +export function useDetailListColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '回款编号', + field: 'no', + minWidth: 150, + fixed: 'left', + }, + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + }, + { + title: '合同编号', + field: 'contract.no', + minWidth: 150, + }, + { + title: '回款日期', + field: 'returnTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '回款金额(元)', + field: 'price', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '回款方式', + field: 'returnType', + minWidth: 150, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE }, + }, + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 150, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '回款状态', + field: 'auditStatus', + minWidth: 100, + fixed: 'right', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, + }, + }, + { + title: '操作', + field: 'actions', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/components/detail-list.vue b/apps/web-ele/src/views/crm/receivable/components/detail-list.vue new file mode 100644 index 000000000..fb2f93576 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/components/detail-list.vue @@ -0,0 +1,144 @@ + + + + diff --git a/apps/web-ele/src/views/crm/receivable/components/index.ts b/apps/web-ele/src/views/crm/receivable/components/index.ts new file mode 100644 index 000000000..971ab650b --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/components/index.ts @@ -0,0 +1 @@ +export { default as ReceivableDetailsList } from './detail-list.vue'; diff --git a/apps/web-ele/src/views/crm/receivable/data.ts b/apps/web-ele/src/views/crm/receivable/data.ts new file mode 100644 index 000000000..adbbbd17e --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/data.ts @@ -0,0 +1,300 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; + +import { getContractSimpleList } from '#/api/crm/contract'; +import { getCustomerSimpleList } from '#/api/crm/customer'; +import { + getReceivablePlan, + getReceivablePlanSimpleList, +} from '#/api/crm/receivable/plan'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '回款编号', + component: 'Input', + componentProps: { + placeholder: '保存时自动生成', + disabled: true, + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + rules: 'required', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + allowClear: true, + }, + defaultValue: userStore.userInfo?.id, + }, + { + fieldName: 'customerId', + label: '客户名称', + component: 'ApiSelect', + rules: 'required', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + }, + { + fieldName: 'contractId', + label: '合同名称', + component: 'Select', + rules: 'required', + dependencies: { + triggerFields: ['customerId'], + disabled: (values) => !values.customerId || values.id, + async componentProps(values) { + if (values.customerId) { + if (!values.id) { + // 特殊:只有在【新增】时,才清空合同编号 + values.contractId = undefined; + } + const contracts = await getContractSimpleList(values.customerId); + return { + options: contracts.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择合同', + } as any; + } + }, + }, + }, + { + fieldName: 'planId', + label: '回款期数', + component: 'Select', + rules: 'required', + dependencies: { + triggerFields: ['contractId'], + disabled: (values) => !values.contractId, + async componentProps(values) { + if (values.contractId) { + values.planId = undefined; + const plans = await getReceivablePlanSimpleList( + values.customerId, + values.contractId, + ); + return { + options: plans.map((item) => ({ + label: item.period, + value: item.id, + })), + placeholder: '请选择回款期数', + onChange: async (value: any) => { + const plan = await getReceivablePlan(value); + values.returnTime = plan?.returnTime; + values.price = plan?.price; + values.returnType = plan?.returnType; + }, + } as any; + } + }, + }, + }, + { + fieldName: 'returnType', + label: '回款方式', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, 'number'), + placeholder: '请选择回款方式', + }, + }, + { + fieldName: 'price', + label: '回款金额', + component: 'InputNumber', + rules: 'required', + componentProps: { + placeholder: '请输入回款金额', + min: 0, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'returnTime', + label: '回款日期', + component: 'DatePicker', + rules: 'required', + componentProps: { + placeholder: '请选择回款日期', + valueFormat: 'x', + format: 'YYYY-MM-DD', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + formItemClass: 'md:col-span-2', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '回款编号', + component: 'Input', + componentProps: { + placeholder: '请输入回款编号', + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + allowClear: true, + }, + }, + ]; +} + +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '回款编号', + field: 'no', + minWidth: 160, + fixed: 'left', + slots: { default: 'no' }, + }, + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + slots: { default: 'customerName' }, + }, + { + title: '合同编号', + field: 'contract', + minWidth: 160, + slots: { default: 'contractNo' }, + }, + { + title: '回款日期', + field: 'returnTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '回款金额(元)', + field: 'price', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '回款方式', + field: 'returnType', + minWidth: 150, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE }, + }, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '合同金额(元)', + field: 'contract.totalPrice', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 150, + }, + { + title: '所属部门', + field: 'ownerUserDeptName', + minWidth: 150, + }, + { + title: '更新时间', + field: 'updateTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '创建人', + field: 'creatorName', + minWidth: 150, + }, + { + title: '回款状态', + field: 'auditStatus', + minWidth: 100, + fixed: 'right', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, + }, + }, + { + title: '操作', + field: 'actions', + minWidth: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/detail/data.ts b/apps/web-ele/src/views/crm/receivable/detail/data.ts new file mode 100644 index 000000000..dcceec53a --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/detail/data.ts @@ -0,0 +1,105 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'totalPrice', + label: '合同金额(元)', + render: (val, data) => + erpPriceInputFormatter(val ?? data?.contract?.totalPrice ?? 0), + }, + { + field: 'returnTime', + label: '回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'price', + label: '回款金额(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'ownerUserName', + label: '负责人', + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'no', + label: '回款编号', + }, + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'contract', + label: '合同编号', + render: (val, data) => + val && data?.contract?.no ? data?.contract?.no : '', + }, + { + field: 'returnTime', + label: '回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'price', + label: '回款金额', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'returnType', + label: '回款方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, + value: val, + }), + }, + { + field: 'remark', + label: '备注', + }, + ]; +} + +/** 系统信息字段 */ +export function useDetailSystemSchema(): DescriptionItemSchema[] { + return [ + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'creatorName', + label: '创建人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/detail/index.vue b/apps/web-ele/src/views/crm/receivable/detail/index.vue new file mode 100644 index 000000000..8908632ea --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/detail/index.vue @@ -0,0 +1,133 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/detail/modules/info.vue b/apps/web-ele/src/views/crm/receivable/detail/modules/info.vue new file mode 100644 index 000000000..6cf92313e --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/detail/modules/info.vue @@ -0,0 +1,37 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/index.vue b/apps/web-ele/src/views/crm/receivable/index.vue new file mode 100644 index 000000000..7c24caba2 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/index.vue @@ -0,0 +1,253 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/modules/form.vue b/apps/web-ele/src/views/crm/receivable/modules/form.vue new file mode 100644 index 000000000..ada8f56f9 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/modules/form.vue @@ -0,0 +1,102 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/components/data.ts b/apps/web-ele/src/views/crm/receivable/plan/components/data.ts new file mode 100644 index 000000000..9a67c0be2 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/components/data.ts @@ -0,0 +1,62 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 详情列表的字段 */ +export function useDetailListColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + }, + { + title: '合同编号', + field: 'contractNo', + minWidth: 150, + }, + { + title: '期数', + field: 'period', + minWidth: 150, + }, + { + title: '计划回款(元)', + field: 'price', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '计划回款日期', + field: 'returnTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '提前几天提醒', + field: 'remindDays', + minWidth: 150, + }, + { + title: '提醒日期', + field: 'remindTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 150, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '操作', + field: 'actions', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/plan/components/detail-list.vue b/apps/web-ele/src/views/crm/receivable/plan/components/detail-list.vue new file mode 100644 index 000000000..1aa30b9dc --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/components/detail-list.vue @@ -0,0 +1,147 @@ + + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/components/index.ts b/apps/web-ele/src/views/crm/receivable/plan/components/index.ts new file mode 100644 index 000000000..cbb4b1216 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/components/index.ts @@ -0,0 +1,2 @@ +export { default as ReceivablePlanDetailsInfo } from '../detail/modules/info.vue'; +export { default as ReceivablePlanDetailsList } from './detail-list.vue'; diff --git a/apps/web-ele/src/views/crm/receivable/plan/data.ts b/apps/web-ele/src/views/crm/receivable/plan/data.ts new file mode 100644 index 000000000..a5edc762a --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/data.ts @@ -0,0 +1,288 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { getContractSimpleList } from '#/api/crm/contract'; +import { getCustomerSimpleList } from '#/api/crm/customer'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'period', + label: '期数', + component: 'Input', + componentProps: { + placeholder: '保存时自动生成', + disabled: true, + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + defaultValue: userStore.userInfo?.id, + rules: 'required', + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + rules: 'required', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + allowClear: true, + }, + }, + { + fieldName: 'contractId', + label: '合同', + component: 'Select', + rules: 'required', + componentProps: { + options: [], + placeholder: '请选择合同', + allowClear: true, + }, + dependencies: { + triggerFields: ['customerId'], + disabled: (values) => !values.customerId, + async componentProps(values) { + if (!values.customerId) { + return { + options: [], + placeholder: '请选择客户', + }; + } + const res = await getContractSimpleList(values.customerId); + return { + options: res.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择合同', + onChange: (value: number) => { + const contract = res.find((item) => item.id === value); + if (contract) { + values.price = + contract.totalPrice - contract.totalReceivablePrice; + } + }, + }; + }, + }, + }, + { + fieldName: 'price', + label: '计划回款金额', + component: 'InputNumber', + rules: 'required', + componentProps: { + placeholder: '请输入计划回款金额', + min: 0, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'returnTime', + label: '计划回款日期', + component: 'DatePicker', + rules: 'required', + componentProps: { + placeholder: '请选择计划回款日期', + valueFormat: 'x', + format: 'YYYY-MM-DD', + }, + defaultValue: new Date(), + }, + { + fieldName: 'remindDays', + label: '提前几天提醒', + component: 'InputNumber', + componentProps: { + placeholder: '请输入提前几天提醒', + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'returnType', + label: '回款方式', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, 'number'), + placeholder: '请选择回款方式', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + formItemClass: 'md:col-span-2', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + allowClear: true, + }, + }, + { + fieldName: 'contractNo', + label: '合同编号', + component: 'Input', + componentProps: { + placeholder: '请输入合同编号', + allowClear: true, + }, + }, + ]; +} + +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + fixed: 'left', + slots: { default: 'customerName' }, + }, + { + title: '合同编号', + field: 'contractNo', + minWidth: 200, + }, + { + title: '期数', + field: 'period', + minWidth: 150, + slots: { default: 'period' }, + }, + { + title: '计划回款金额(元)', + field: 'price', + minWidth: 160, + formatter: 'formatAmount2', + }, + { + title: '计划回款日期', + field: 'returnTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '提前几天提醒', + field: 'remindDays', + minWidth: 150, + }, + { + title: '提醒日期', + field: 'remindTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '回款方式', + field: 'returnType', + minWidth: 130, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE }, + }, + }, + { + title: '备注', + field: 'remark', + minWidth: 120, + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 120, + }, + { + title: '实际回款金额(元)', + field: 'receivable.price', + minWidth: 160, + formatter: 'formatAmount2', + }, + { + title: '实际回款日期', + field: 'receivable.returnTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '未回款金额(元)', + field: 'unpaidPrice', + minWidth: 160, + formatter: ({ row }) => { + if (row.receivable) { + return erpPriceInputFormatter(row.price - row.receivable.price); + } + return erpPriceInputFormatter(row.price); + }, + }, + { + title: '更新时间', + field: 'updateTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建人', + field: 'creatorName', + minWidth: 100, + }, + { + title: '操作', + field: 'actions', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/plan/detail/data.ts b/apps/web-ele/src/views/crm/receivable/plan/detail/data.ts new file mode 100644 index 000000000..5059f02ab --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/detail/data.ts @@ -0,0 +1,124 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'contractNo', + label: '合同编号', + }, + { + field: 'price', + label: '计划回款金额', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'returnTime', + label: '计划回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'receivable', + label: '实际回款金额', + render: (val) => erpPriceInputFormatter(val?.price ?? 0), + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'period', + label: '期数', + }, + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'contractNo', + label: '合同编号', + }, + { + field: 'returnTime', + label: '计划回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'price', + label: '计划回款金额', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'returnType', + label: '计划回款方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, + value: val, + }), + }, + { + field: 'remindDays', + label: '提前几天提醒', + }, + { + field: 'receivable', + label: '实际回款金额', + render: (val) => erpPriceInputFormatter(val ?? 0), + }, + { + field: 'receivableRemain', + label: '未回款金额', + render: (val, data) => { + const paid = data?.receivable?.price ?? 0; + return erpPriceInputFormatter(Math.max(val - paid, 0)); + }, + }, + { + field: 'receivable.returnTime', + label: '实际回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'remark', + label: '备注', + }, + ]; +} + +/** 系统信息字段 */ +export function useDetailSystemSchema(): DescriptionItemSchema[] { + return [ + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'creatorName', + label: '创建人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/plan/detail/index.vue b/apps/web-ele/src/views/crm/receivable/plan/detail/index.vue new file mode 100644 index 000000000..67a4c05fa --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/detail/index.vue @@ -0,0 +1,138 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/detail/modules/info.vue b/apps/web-ele/src/views/crm/receivable/plan/detail/modules/info.vue new file mode 100644 index 000000000..9db52c05a --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/detail/modules/info.vue @@ -0,0 +1,37 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/index.vue b/apps/web-ele/src/views/crm/receivable/plan/index.vue new file mode 100644 index 000000000..73aeff374 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/index.vue @@ -0,0 +1,218 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/modules/form.vue b/apps/web-ele/src/views/crm/receivable/plan/modules/form.vue new file mode 100644 index 000000000..7b6230fe0 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/modules/form.vue @@ -0,0 +1,88 @@ + + +