diff --git a/apps/web-antd/src/api/crm/performance/config/index.ts b/apps/web-antd/src/api/crm/performance/config/index.ts new file mode 100644 index 000000000..3a5d812a2 --- /dev/null +++ b/apps/web-antd/src/api/crm/performance/config/index.ts @@ -0,0 +1,67 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmPerformanceConfigApi { + /** 业绩目标设置 */ + export interface PerformanceConfig { + id?: number; + objectId: number; + objectName?: string; + objectType: number; + year: number | string; + januaryTargetPrice?: number; + februaryTargetPrice?: number; + marchTargetPrice?: number; + aprilTargetPrice?: number; + mayTargetPrice?: number; + juneTargetPrice?: number; + julyTargetPrice?: number; + augustTargetPrice?: number; + septemberTargetPrice?: number; + octoberTargetPrice?: number; + novemberTargetPrice?: number; + decemberTargetPrice?: number; + bizType: number; + yearTargetPrice?: number; + createTime?: Date; + } +} + +export enum PerformanceConfigObjectTypeEnum { + DEPT = 2, + USER = 3, +} + +/** 查询业绩目标设置分页 */ +export function getPerformanceConfigPage(params: PageParam) { + return requestClient.get< + PageResult + >('/crm/performance-config/page', { params }); +} + +/** 获得业绩目标设置详情 */ +export function getPerformanceConfig(id: number) { + return requestClient.get( + `/crm/performance-config/get?id=${id}`, + ); +} + +/** 新增业绩目标设置 */ +export function createPerformanceConfig( + data: CrmPerformanceConfigApi.PerformanceConfig, +) { + return requestClient.post('/crm/performance-config/create', data); +} + +/** 修改业绩目标设置 */ +export function updatePerformanceConfig( + data: CrmPerformanceConfigApi.PerformanceConfig, +) { + return requestClient.put('/crm/performance-config/update', data); +} + +/** 删除业绩目标设置 */ +export function deletePerformanceConfig(id: number) { + return requestClient.delete(`/crm/performance-config/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/crm/statistics/performanceTarget.ts b/apps/web-antd/src/api/crm/statistics/performanceTarget.ts new file mode 100644 index 000000000..79c5c59bd --- /dev/null +++ b/apps/web-antd/src/api/crm/statistics/performanceTarget.ts @@ -0,0 +1,30 @@ +import { requestClient } from '#/api/request'; + +export namespace CrmStatisticsPerformanceTargetApi { + /** 业绩目标完成情况请求 */ + export interface PerformanceTargetReqVO { + deptId: number; + userId?: number; + year: number; + bizType: number; + } + + /** 业绩目标完成情况响应 */ + export interface PerformanceTargetRespVO { + month: number; + targetPrice: number; + currentPrice: number; + completionRate: number; + } +} + +/** 获得业绩目标完成情况 */ +export function getPerformanceTargetSummary( + params: CrmStatisticsPerformanceTargetApi.PerformanceTargetReqVO, +) { + return requestClient.get< + CrmStatisticsPerformanceTargetApi.PerformanceTargetRespVO[] + >('/crm/statistics-performance-target/get-performance-target-summary', { + params, + }); +} diff --git a/apps/web-antd/src/api/crm/statistics/product.ts b/apps/web-antd/src/api/crm/statistics/product.ts new file mode 100644 index 000000000..af7030c93 --- /dev/null +++ b/apps/web-antd/src/api/crm/statistics/product.ts @@ -0,0 +1,46 @@ +import { requestClient } from '#/api/request'; + +export namespace CrmStatisticsProductApi { + /** 产品销售情况统计响应 */ + export interface ProductSalesRespVO { + categoryId: number; + categoryName: string; + productId: number; + productName: string; + contractId: number; + contractNo: string; + contractName: string; + ownerUserId: number; + ownerUserName: string; + customerId: number; + customerName: string; + productPrice: number; + productCount: number; + productTotalPrice: number; + } + + /** 产品分类销售分析响应 */ + export interface ProductCategoryRespVO { + categoryId: number; + categoryName: string; + contractCount: number; + productCount: number; + productTotalPrice: number; + } +} + +/** 获得产品销售情况统计 */ +export function getProductSalesList(params: any) { + return requestClient.get( + '/crm/statistics-product/get-product-sales-list', + { params }, + ); +} + +/** 获得产品分类销售分析 */ +export function getProductCategorySummary(params: any) { + return requestClient.get( + '/crm/statistics-product/get-product-category-summary', + { params }, + ); +} diff --git a/apps/web-antd/src/views/crm/performance/config/data.ts b/apps/web-antd/src/views/crm/performance/config/data.ts new file mode 100644 index 000000000..487675ce3 --- /dev/null +++ b/apps/web-antd/src/views/crm/performance/config/data.ts @@ -0,0 +1,263 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { handleTree } from '@vben/utils'; + +import { PerformanceConfigObjectTypeEnum } from '#/api/crm/performance/config'; +import { BizTypeEnum } from '#/api/crm/permission'; +import { getSimpleDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; + +export const bizTypeOptions = [ + { label: '销售目标', value: BizTypeEnum.CRM_CONTRACT }, + { label: '回款目标', value: BizTypeEnum.CRM_RECEIVABLE }, +]; + +export const objectTypeOptions = [ + { label: '部门', value: PerformanceConfigObjectTypeEnum.DEPT }, + { label: '员工', value: PerformanceConfigObjectTypeEnum.USER }, +]; + +export const monthFields = [ + { label: '一月', prop: 'januaryTargetPrice' }, + { label: '二月', prop: 'februaryTargetPrice' }, + { label: '三月', prop: 'marchTargetPrice' }, + { label: '四月', prop: 'aprilTargetPrice' }, + { label: '五月', prop: 'mayTargetPrice' }, + { label: '六月', prop: 'juneTargetPrice' }, + { label: '七月', prop: 'julyTargetPrice' }, + { label: '八月', prop: 'augustTargetPrice' }, + { label: '九月', prop: 'septemberTargetPrice' }, + { label: '十月', prop: 'octoberTargetPrice' }, + { label: '十一月', prop: 'novemberTargetPrice' }, + { label: '十二月', prop: 'decemberTargetPrice' }, +] as const; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'year', + label: '年份', + component: 'DatePicker', + componentProps: { + picker: 'year', + format: 'YYYY', + valueFormat: 'YYYY', + placeholder: '请选择年份', + }, + defaultValue: new Date().getFullYear().toString(), + }, + { + fieldName: 'bizType', + label: '目标类型', + component: 'Select', + componentProps: { + allowClear: true, + options: bizTypeOptions, + placeholder: '请选择目标类型', + }, + }, + { + fieldName: 'objectType', + label: '对象类型', + component: 'Select', + componentProps: { + allowClear: true, + options: objectTypeOptions, + placeholder: '请选择对象类型', + }, + }, + { + fieldName: 'deptObjectId', + label: '部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => handleTree(await getSimpleDeptList()), + labelField: 'name', + valueField: 'id', + childrenField: 'children', + treeDefaultExpandAll: true, + placeholder: '请选择部门', + allowClear: true, + }, + dependencies: { + triggerFields: ['objectType'], + show: (values) => + values.objectType === PerformanceConfigObjectTypeEnum.DEPT, + }, + }, + { + fieldName: 'userObjectId', + label: '员工', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + allowClear: true, + showSearch: true, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择员工', + }, + dependencies: { + triggerFields: ['objectType'], + show: (values) => + values.objectType === PerformanceConfigObjectTypeEnum.USER, + }, + }, + ]; +} + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'year', + label: '年份', + component: 'DatePicker', + componentProps: { + picker: 'year', + format: 'YYYY', + valueFormat: 'YYYY', + placeholder: '请选择年份', + }, + rules: 'required', + }, + { + fieldName: 'bizType', + label: '目标类型', + component: 'Select', + componentProps: { + options: bizTypeOptions, + placeholder: '请选择目标类型', + }, + rules: 'required', + }, + { + fieldName: 'objectType', + label: '对象类型', + component: 'Select', + componentProps: { + options: objectTypeOptions, + placeholder: '请选择对象类型', + }, + rules: 'required', + }, + { + fieldName: 'deptObjectId', + label: '目标对象', + component: 'ApiTreeSelect', + componentProps: { + api: async () => handleTree(await getSimpleDeptList()), + labelField: 'name', + valueField: 'id', + childrenField: 'children', + treeCheckStrictly: true, + treeDefaultExpandAll: true, + placeholder: '请选择部门', + }, + dependencies: { + triggerFields: ['objectType'], + show: (values) => + values.objectType === PerformanceConfigObjectTypeEnum.DEPT, + }, + }, + { + fieldName: 'userObjectId', + label: '目标对象', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + allowClear: true, + showSearch: true, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择员工', + }, + dependencies: { + triggerFields: ['objectType'], + show: (values) => + values.objectType === PerformanceConfigObjectTypeEnum.USER, + }, + }, + ...monthFields.map((item) => ({ + fieldName: item.prop, + label: item.label, + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + precision: 2, + step: 1000, + }, + })), + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { field: 'year', title: '年份', width: 90, fixed: 'left' }, + { + field: 'objectType', + title: '对象类型', + width: 100, + fixed: 'left', + slots: { default: 'objectType' }, + }, + { + field: 'objectName', + title: '目标对象', + minWidth: 140, + fixed: 'left', + }, + { + field: 'bizType', + title: '目标类型', + width: 100, + slots: { default: 'bizType' }, + }, + ...monthFields.map((item) => ({ + field: item.prop, + title: item.label, + formatter: 'formatAmount2', + width: 120, + })), + { + field: 'yearTargetPrice', + title: '年度目标', + formatter: 'formatAmount2', + width: 140, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + width: 180, + }, + { + title: '操作', + width: 140, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 获取目标类型名称 */ +export function getBizTypeLabel(value: number) { + return bizTypeOptions.find((item) => item.value === value)?.label || ''; +} + +/** 获取对象类型名称 */ +export function getObjectTypeLabel(value: number) { + return objectTypeOptions.find((item) => item.value === value)?.label || ''; +} diff --git a/apps/web-antd/src/views/crm/performance/config/index.vue b/apps/web-antd/src/views/crm/performance/config/index.vue new file mode 100644 index 000000000..39ce7d7fc --- /dev/null +++ b/apps/web-antd/src/views/crm/performance/config/index.vue @@ -0,0 +1,150 @@ + + + diff --git a/apps/web-antd/src/views/crm/performance/config/modules/form.vue b/apps/web-antd/src/views/crm/performance/config/modules/form.vue new file mode 100644 index 000000000..225c93ea5 --- /dev/null +++ b/apps/web-antd/src/views/crm/performance/config/modules/form.vue @@ -0,0 +1,132 @@ + + + diff --git a/apps/web-antd/src/views/crm/statistics/performanceTarget/index.vue b/apps/web-antd/src/views/crm/statistics/performanceTarget/index.vue new file mode 100644 index 000000000..9a6bede25 --- /dev/null +++ b/apps/web-antd/src/views/crm/statistics/performanceTarget/index.vue @@ -0,0 +1,285 @@ + + + diff --git a/apps/web-antd/src/views/crm/statistics/product/index.vue b/apps/web-antd/src/views/crm/statistics/product/index.vue new file mode 100644 index 000000000..89a1658da --- /dev/null +++ b/apps/web-antd/src/views/crm/statistics/product/index.vue @@ -0,0 +1,585 @@ + + + + +