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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.no }}
+
+
+
+
+ {{ row.customerName }}
+
+
+
+
+ {{ row.contract.no }}
+
+ --
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.customerName }}
+
+
+
+
+ {{ row.period }}
+
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+
+