diff --git a/apps/web-ele/src/components/operate-log/index.ts b/apps/web-ele/src/components/operate-log/index.ts new file mode 100644 index 000000000..cf38b5e70 --- /dev/null +++ b/apps/web-ele/src/components/operate-log/index.ts @@ -0,0 +1,3 @@ +export { default as OperateLog } from './operate-log.vue'; + +export type { OperateLogProps } from './typing'; diff --git a/apps/web-ele/src/components/operate-log/operate-log.vue b/apps/web-ele/src/components/operate-log/operate-log.vue new file mode 100644 index 000000000..989452483 --- /dev/null +++ b/apps/web-ele/src/components/operate-log/operate-log.vue @@ -0,0 +1,50 @@ + + diff --git a/apps/web-ele/src/components/operate-log/typing.ts b/apps/web-ele/src/components/operate-log/typing.ts new file mode 100644 index 000000000..773237b96 --- /dev/null +++ b/apps/web-ele/src/components/operate-log/typing.ts @@ -0,0 +1,5 @@ +import type { SystemOperateLogApi } from '#/api/system/operate-log'; + +export interface OperateLogProps { + logList: SystemOperateLogApi.OperateLog[]; // 操作日志列表 +} diff --git a/apps/web-ele/src/router/routes/modules/crm.ts b/apps/web-ele/src/router/routes/modules/crm.ts new file mode 100644 index 000000000..eb16f4b52 --- /dev/null +++ b/apps/web-ele/src/router/routes/modules/crm.ts @@ -0,0 +1,90 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/crm', + name: 'CrmCenter', + meta: { + title: '客户管理', + icon: 'simple-icons:civicrm', + keepAlive: true, + hideInMenu: true, + }, + children: [ + // { + // path: 'clue/detail/:id', + // name: 'CrmClueDetail', + // meta: { + // title: '线索详情', + // activePath: '/crm/clue', + // }, + // component: () => import('#/views/crm/clue/detail/index.vue'), + // }, + // { + // path: 'customer/detail/:id', + // name: 'CrmCustomerDetail', + // meta: { + // title: '客户详情', + // activePath: '/crm/customer', + // }, + // component: () => import('#/views/crm/customer/detail/index.vue'), + // }, + // { + // path: 'business/detail/:id', + // name: 'CrmBusinessDetail', + // meta: { + // title: '商机详情', + // activePath: '/crm/business', + // }, + // component: () => import('#/views/crm/business/detail/index.vue'), + // }, + // { + // path: 'contract/detail/:id', + // name: 'CrmContractDetail', + // meta: { + // title: '合同详情', + // activePath: '/crm/contract', + // }, + // 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: 'contact/detail/:id', + // name: 'CrmContactDetail', + // meta: { + // title: '联系人详情', + // activePath: '/crm/contact', + // }, + // component: () => import('#/views/crm/contact/detail/index.vue'), + // }, + { + path: 'product/detail/:id', + name: 'CrmProductDetail', + meta: { + title: '产品详情', + activePath: '/crm/product', + }, + component: () => import('#/views/crm/product/detail/index.vue'), + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-ele/src/views/crm/followup/data.ts b/apps/web-ele/src/views/crm/followup/data.ts new file mode 100644 index 000000000..aa0b83e5c --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/data.ts @@ -0,0 +1,194 @@ +import type { Ref } from 'vue'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { getBusinessPageByCustomer } from '#/api/crm/business'; +import { getContactPageByCustomer } from '#/api/crm/contact'; +import { BizTypeEnum } from '#/api/crm/permission'; + +/** 新增/修改的表单 */ +export function useFormSchema( + bizId: Ref, +): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'bizId', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'bizType', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'type', + label: '跟进类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_FOLLOW_UP_TYPE, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'nextTime', + label: '下次联系时间', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '跟进内容', + component: 'Textarea', + rules: 'required', + }, + { + fieldName: 'picUrls', + label: '图片', + component: 'ImageUpload', + }, + { + fieldName: 'fileUrls', + label: '附件', + component: 'FileUpload', + }, + { + fieldName: 'contactIds', + label: '关联联系人', + component: 'ApiSelect', + componentProps: { + api: async () => { + if (!bizId.value) { + return []; + } + const res = await getContactPageByCustomer({ + pageNo: 1, + pageSize: 100, + customerId: bizId.value, + }); + return res.list; + }, + labelField: 'name', + valueField: 'id', + mode: 'multiple', + }, + }, + { + fieldName: 'businessIds', + label: '关联商机', + component: 'ApiSelect', + componentProps: { + api: async () => { + if (!bizId.value) { + return []; + } + const res = await getBusinessPageByCustomer({ + pageNo: 1, + pageSize: 100, + customerId: bizId.value, + }); + return res.list; + }, + labelField: 'name', + valueField: 'id', + mode: 'multiple', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + bizType: number, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + }, + { field: 'creatorName', title: '跟进人' }, + { + field: 'type', + title: '跟进类型', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_FOLLOW_UP_TYPE }, + }, + }, + { field: 'content', title: '跟进内容' }, + { + field: 'nextTime', + title: '下次联系时间', + formatter: 'formatDateTime', + }, + { + field: 'contacts', + title: '关联联系人', + visible: bizType === BizTypeEnum.CRM_CUSTOMER, + slots: { default: 'contacts' }, + }, + { + field: 'businesses', + title: '关联商机', + visible: bizType === BizTypeEnum.CRM_CUSTOMER, + slots: { default: 'businesses' }, + }, + { + field: 'actions', + title: '操作', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的系统字段 */ +export function useFollowUpDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'contactLastContent', + label: '最后跟进记录', + }, + { + field: 'contactLastTime', + label: '最后跟进时间', + render: (val) => formatDateTime(val) as string, + }, + { + 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/followup/index.ts b/apps/web-ele/src/views/crm/followup/index.ts new file mode 100644 index 000000000..911cbed51 --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/index.ts @@ -0,0 +1 @@ +export { default as FollowUp } from './index.vue'; diff --git a/apps/web-ele/src/views/crm/followup/index.vue b/apps/web-ele/src/views/crm/followup/index.vue new file mode 100644 index 000000000..03fea5534 --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/index.vue @@ -0,0 +1,165 @@ + + + diff --git a/apps/web-ele/src/views/crm/followup/modules/form.vue b/apps/web-ele/src/views/crm/followup/modules/form.vue new file mode 100644 index 000000000..51df040a9 --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/modules/form.vue @@ -0,0 +1,81 @@ + + + diff --git a/apps/web-ele/src/views/crm/permission/index.ts b/apps/web-ele/src/views/crm/permission/index.ts new file mode 100644 index 000000000..2988df4dd --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/index.ts @@ -0,0 +1,2 @@ +export { default as PermissionList } from './modules/list.vue'; +export { default as TransferForm } from './modules/transfer-form.vue'; diff --git a/apps/web-ele/src/views/crm/permission/modules/data.ts b/apps/web-ele/src/views/crm/permission/modules/data.ts new file mode 100644 index 000000000..fdbe9bcbd --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/data.ts @@ -0,0 +1,233 @@ +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 { BizTypeEnum, PermissionLevelEnum } from '#/api/crm/permission'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useTransferFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'newOwnerUserId', + label: '选择新负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'oldOwnerHandler', + label: '老负责人', + component: 'RadioGroup', + componentProps: { + options: [ + { + label: '加入团队', + value: true, + }, + { + label: '移除', + value: false, + }, + ], + }, + rules: 'required', + }, + { + fieldName: 'oldOwnerPermissionLevel', + label: '老负责人权限级别', + component: 'RadioGroup', + componentProps: { + options: getDictOptions( + DICT_TYPE.CRM_PERMISSION_LEVEL, + 'number', + ).filter((dict) => dict.value !== PermissionLevelEnum.OWNER), + }, + dependencies: { + triggerFields: ['oldOwnerHandler'], + show: (values) => values.oldOwnerHandler, + trigger(values) { + if (!values.oldOwnerHandler) { + values.oldOwnerPermissionLevel = undefined; + } + }, + }, + rules: 'required', + }, + { + fieldName: 'toBizTypes', + label: '同时转移', + component: 'CheckboxGroup', + componentProps: { + options: [ + { + label: '联系人', + value: BizTypeEnum.CRM_CONTACT, + }, + { + label: '商机', + value: BizTypeEnum.CRM_BUSINESS, + }, + { + label: '合同', + value: BizTypeEnum.CRM_CONTRACT, + }, + ], + }, + }, + ]; +} + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'bizId', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'ids', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'userId', + label: '选择人员', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + dependencies: { + triggerFields: ['ids'], + show: (values) => { + return values.ids === undefined; + }, + }, + }, + { + fieldName: 'level', + label: '权限级别', + component: 'RadioGroup', + componentProps: { + options: getDictOptions( + DICT_TYPE.CRM_PERMISSION_LEVEL, + 'number', + ).filter((dict) => dict.value !== PermissionLevelEnum.OWNER), + }, + rules: 'required', + }, + { + fieldName: 'bizType', + label: 'Crm 类型', + component: 'RadioGroup', + componentProps: { + options: [ + { + label: '联系人', + value: BizTypeEnum.CRM_CONTACT, + }, + { + label: '商机', + value: BizTypeEnum.CRM_BUSINESS, + }, + { + label: '合同', + value: BizTypeEnum.CRM_CONTRACT, + }, + ], + }, + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'toBizTypes', + label: '同时添加至', + component: 'CheckboxGroup', + componentProps: { + options: [ + { + label: '联系人', + value: BizTypeEnum.CRM_CONTACT, + }, + { + label: '商机', + value: BizTypeEnum.CRM_BUSINESS, + }, + { + label: '合同', + value: BizTypeEnum.CRM_CONTRACT, + }, + ], + }, + dependencies: { + triggerFields: ['ids', 'bizType'], + show: (values) => { + return ( + values.ids === undefined && + values.bizType === BizTypeEnum.CRM_CUSTOMER + ); + }, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + }, + { + field: 'nickname', + title: '姓名', + }, + { + field: 'deptName', + title: '部门', + }, + { + field: 'postNames', + title: '岗位', + }, + { + field: 'level', + title: '权限级别', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PERMISSION_LEVEL }, + }, + }, + { + field: 'createTime', + title: '加入时间', + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/permission/modules/form.vue b/apps/web-ele/src/views/crm/permission/modules/form.vue new file mode 100644 index 000000000..949a77f6d --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/form.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-ele/src/views/crm/permission/modules/list.vue b/apps/web-ele/src/views/crm/permission/modules/list.vue new file mode 100644 index 000000000..957f0dd8b --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/list.vue @@ -0,0 +1,265 @@ + + + diff --git a/apps/web-ele/src/views/crm/permission/modules/transfer-form.vue b/apps/web-ele/src/views/crm/permission/modules/transfer-form.vue new file mode 100644 index 000000000..e823d4893 --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/transfer-form.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/category/data.ts b/apps/web-ele/src/views/crm/product/category/data.ts new file mode 100644 index 000000000..4fa90e0f3 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/category/data.ts @@ -0,0 +1,95 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { CrmProductCategoryApi } from '#/api/crm/product/category'; + +import { handleTree } from '@vben/utils'; + +import { getProductCategoryList } from '#/api/crm/product/category'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级分类', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getProductCategoryList(); + data.unshift({ + id: 0, + name: '顶级分类', + } as CrmProductCategoryApi.ProductCategory); + return handleTree(data); + }, + fieldNames: { label: 'name', value: 'id', children: 'children' }, + placeholder: '请选择上级分类', + showSearch: true, + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '分类名称', + component: 'Input', + componentProps: { + placeholder: '请输入分类名称', + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '分类名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入分类名称', + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '分类名称', + treeNode: true, + }, + { + field: 'id', + title: '分类编号', + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + }, + { + field: 'actions', + title: '操作', + width: 200, + fixed: 'right', + slots: { + default: 'actions', + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/category/index.vue b/apps/web-ele/src/views/crm/product/category/index.vue new file mode 100644 index 000000000..3aa4c03c3 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/category/index.vue @@ -0,0 +1,168 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/category/modules/form.vue b/apps/web-ele/src/views/crm/product/category/modules/form.vue new file mode 100644 index 000000000..c8d0c868f --- /dev/null +++ b/apps/web-ele/src/views/crm/product/category/modules/form.vue @@ -0,0 +1,92 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/components/data.ts b/apps/web-ele/src/views/crm/product/components/data.ts new file mode 100644 index 000000000..266a91291 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/data.ts @@ -0,0 +1,111 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 产品详情列表的列定义 */ +export function useDetailListColumns( + showBusinessPrice: boolean, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'productName', + title: '产品名称', + }, + { + field: 'productNo', + title: '产品条码', + }, + { + field: 'productUnit', + title: '产品单位', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_UNIT }, + }, + }, + { + field: 'productPrice', + title: '产品价格(元)', + formatter: 'formatAmount2', + }, + { + field: 'businessPrice', + title: '商机价格(元)', + formatter: 'formatAmount2', + visible: showBusinessPrice, + }, + { + field: 'contractPrice', + title: '合同价格(元)', + formatter: 'formatAmount2', + visible: !showBusinessPrice, + }, + { + field: 'count', + title: '数量', + formatter: 'formatAmount3', + }, + { + field: 'totalPrice', + title: '合计金额(元)', + formatter: 'formatAmount2', + }, + ]; +} + +/** 产品编辑表格的列定义 */ +export function useProductEditTableColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50 }, + { + field: 'productId', + title: '产品名称', + minWidth: 100, + slots: { default: 'productId' }, + }, + { + field: 'productNo', + title: '条码', + minWidth: 150, + }, + { + field: 'productUnit', + title: '单位', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_UNIT }, + }, + }, + { + field: 'productPrice', + title: '价格(元)', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'sellingPrice', + title: '售价(元)', + minWidth: 100, + slots: { default: 'sellingPrice' }, + }, + { + field: 'count', + title: '数量', + minWidth: 100, + slots: { default: 'count' }, + }, + { + field: 'totalPrice', + title: '合计', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/components/detail-list.vue b/apps/web-ele/src/views/crm/product/components/detail-list.vue new file mode 100644 index 000000000..5ce23bc0d --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/detail-list.vue @@ -0,0 +1,79 @@ + + + + diff --git a/apps/web-ele/src/views/crm/product/components/edit-table.vue b/apps/web-ele/src/views/crm/product/components/edit-table.vue new file mode 100644 index 000000000..bbbab489f --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/edit-table.vue @@ -0,0 +1,203 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/components/index.ts b/apps/web-ele/src/views/crm/product/components/index.ts new file mode 100644 index 000000000..dc527926a --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/index.ts @@ -0,0 +1,2 @@ +export { default as ProductDetailsList } from './detail-list.vue'; +export { default as ProductEditTable } from './edit-table.vue'; diff --git a/apps/web-ele/src/views/crm/product/data.ts b/apps/web-ele/src/views/crm/product/data.ts new file mode 100644 index 000000000..ef6efe9fe --- /dev/null +++ b/apps/web-ele/src/views/crm/product/data.ts @@ -0,0 +1,231 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getProductCategoryList } from '#/api/crm/product/category'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '产品名称', + rules: 'required', + componentProps: { + placeholder: '请输入产品名称', + clearable: true, + }, + }, + { + component: 'ApiSelect', + fieldName: 'ownerUserId', + label: '负责人', + rules: 'required', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + clearable: true, + }, + defaultValue: userStore.userInfo?.id, + }, + { + component: 'Input', + fieldName: 'no', + label: '产品编码', + rules: 'required', + componentProps: { + placeholder: '请输入产品编码', + clearable: true, + }, + }, + { + component: 'ApiTreeSelect', + fieldName: 'categoryId', + label: '产品类型', + rules: 'required', + componentProps: { + api: async () => { + const data = await getProductCategoryList(); + return handleTree(data); + }, + fieldNames: { label: 'name', value: 'id', children: 'children' }, + placeholder: '请选择产品类型', + clearable: true, + }, + }, + { + fieldName: 'unit', + label: '产品单位', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_PRODUCT_UNIT, 'number'), + placeholder: '请选择产品单位', + clearable: true, + }, + rules: 'required', + }, + { + component: 'InputNumber', + fieldName: 'price', + label: '价格(元)', + rules: 'required', + componentProps: { + min: 0, + precision: 2, + step: 0.1, + placeholder: '请输入产品价格', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + component: 'Textarea', + fieldName: 'description', + label: '产品描述', + componentProps: { + placeholder: '请输入产品描述', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '上架状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '产品名称', + component: 'Input', + componentProps: { + placeholder: '请输入产品名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '上架状态', + component: 'Select', + componentProps: { + clearable: true, + placeholder: '请选择上架状态', + options: getDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '产品编号', + visible: false, + }, + { + field: 'name', + title: '产品名称', + minWidth: 240, + slots: { default: 'name' }, + }, + { + field: 'categoryName', + title: '产品类型', + minWidth: 120, + }, + { + field: 'unit', + title: '产品单位', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_UNIT }, + }, + }, + { + field: 'no', + title: '产品编码', + minWidth: 120, + }, + { + field: 'price', + title: '价格(元)', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'description', + title: '产品描述', + minWidth: 200, + }, + { + field: 'status', + title: '上架状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_STATUS }, + }, + minWidth: 120, + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 120, + }, + { + field: 'updateTime', + title: '更新时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/detail/data.ts b/apps/web-ele/src/views/crm/product/detail/data.ts new file mode 100644 index 000000000..0adf792cb --- /dev/null +++ b/apps/web-ele/src/views/crm/product/detail/data.ts @@ -0,0 +1,72 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'categoryName', + label: '产品类别', + }, + { + field: 'unit', + label: '产品单位', + render: (val) => + h(DictTag, { type: DICT_TYPE.CRM_PRODUCT_UNIT, value: val }), + }, + { + field: 'price', + label: '产品价格(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'no', + label: '产品编码', + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'name', + label: '产品名称', + }, + { + field: 'no', + label: '产品编码', + }, + { + field: 'price', + label: '价格(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'description', + label: '产品描述', + }, + { + field: 'categoryName', + label: '产品类型', + }, + { + field: 'status', + label: '是否上下架', + render: (val) => + h(DictTag, { type: DICT_TYPE.CRM_PRODUCT_STATUS, value: val }), + }, + { + field: 'unit', + label: '产品单位', + render: (val) => + h(DictTag, { type: DICT_TYPE.CRM_PRODUCT_UNIT, value: val }), + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/detail/index.vue b/apps/web-ele/src/views/crm/product/detail/index.vue new file mode 100644 index 000000000..1c16c4a87 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/detail/index.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/detail/modules/info.vue b/apps/web-ele/src/views/crm/product/detail/modules/info.vue new file mode 100644 index 000000000..9fce6976a --- /dev/null +++ b/apps/web-ele/src/views/crm/product/detail/modules/info.vue @@ -0,0 +1,25 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/index.vue b/apps/web-ele/src/views/crm/product/index.vue new file mode 100644 index 000000000..07ad1caef --- /dev/null +++ b/apps/web-ele/src/views/crm/product/index.vue @@ -0,0 +1,157 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/modules/form.vue b/apps/web-ele/src/views/crm/product/modules/form.vue new file mode 100644 index 000000000..ef022e6a2 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/modules/form.vue @@ -0,0 +1,82 @@ + + +