From 6acfc77d087edf33fff05e68bd063ab376575478 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 15 Apr 2024 15:37:37 +0800 Subject: [PATCH 1/3] =?UTF-8?q?CRM:=20=E5=AE=8C=E5=96=84=E9=94=80=E5=94=AE?= =?UTF-8?q?=E6=BC=8F=E6=96=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/crm/statistics/funnel.ts | 8 +++--- .../funnel/components/BusinessSummary.vue | 4 +-- .../funnel/components/FunnelBusiness.vue | 25 ++++++++++++++++--- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/api/crm/statistics/funnel.ts b/src/api/crm/statistics/funnel.ts index 0ba322b8..ccfd7432 100644 --- a/src/api/crm/statistics/funnel.ts +++ b/src/api/crm/statistics/funnel.ts @@ -3,13 +3,13 @@ import request from '@/config/axios' export interface CrmStatisticFunnelRespVO { customerCount: number // 客户数 businessCount: number // 商机数 - winCount: number // 赢单数 + businessWinCount: number // 赢单数 } export interface CrmStatisticsBusinessSummaryByDateRespVO { time: string // 时间 businessCreateCount: number // 商机数 - businessDealCount: number // 商机金额 + totalPrice: number | string // 商机金额 } // 客户分析 API @@ -22,9 +22,9 @@ export const StatisticFunnelApi = { }) }, // 2. 获取商机结束状态统计 - getBusinessEndStatusSummary: (params: any) => { + getBusinessSummaryByEndStatus: (params: any) => { return request.get({ - url: '/crm/statistics-funnel/get-business-end-status-summary', + url: '/crm/statistics-funnel/get-business-summary-by-end-status', params }) }, diff --git a/src/views/crm/statistics/funnel/components/BusinessSummary.vue b/src/views/crm/statistics/funnel/components/BusinessSummary.vue index 60dd8198..ae4b032a 100644 --- a/src/views/crm/statistics/funnel/components/BusinessSummary.vue +++ b/src/views/crm/statistics/funnel/components/BusinessSummary.vue @@ -118,7 +118,7 @@ const queryParams0 = reactive({ const loading = ref(false) // 加载中 const list = ref([]) // 列表的数据 const total = ref(0) -/** 将传进来的值赋值给 formData */ +/** 将传进来的值赋值给 queryParams0 */ watch( () => props.queryParams, (data) => { @@ -216,7 +216,7 @@ const fetchAndFill = async () => { } if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) { echartsOption.series[1]['data'] = businessSummaryByDate.map( - (s: CrmStatisticsBusinessSummaryByDateRespVO) => s.businessDealCount + (s: CrmStatisticsBusinessSummaryByDateRespVO) => s.totalPrice ) } diff --git a/src/views/crm/statistics/funnel/components/FunnelBusiness.vue b/src/views/crm/statistics/funnel/components/FunnelBusiness.vue index 2be24e8d..9b7b08ae 100644 --- a/src/views/crm/statistics/funnel/components/FunnelBusiness.vue +++ b/src/views/crm/statistics/funnel/components/FunnelBusiness.vue @@ -4,6 +4,10 @@ + + 客户视角 + 动态视角 + @@ -35,6 +39,7 @@ import { FunnelChart } from 'echarts/charts' defineOptions({ name: 'FunnelBusiness' }) const props = defineProps<{ queryParams: any }>() // 搜索参数 +const active = ref(true) const loading = ref(false) // 加载中 const list = ref([]) // 列表的数据 @@ -101,6 +106,11 @@ const echartsOption = reactive({ ] }) as EChartsOption +const handleActive = async (val: boolean) => { + active.value = val + await loadData() +} + /** 获取统计数据 */ const loadData = async () => { loading.value = true @@ -117,13 +127,20 @@ const loadData = async () => { ) { // tips:写死 value 值是为了保持漏斗顺序不变 const list: { value: number; name: string }[] = [] - list.push({ value: 60, name: `客户-${data.customerCount || 0}个` }) - list.push({ value: 40, name: `商机-${data.businessCount || 0}个` }) - list.push({ value: 20, name: `赢单-${data.winCount || 0}个` }) + if (active.value) { + list.push({ value: 60, name: `客户-${data.customerCount || 0}个` }) + list.push({ value: 40, name: `商机-${data.businessCount || 0}个` }) + list.push({ value: 20, name: `赢单-${data.businessWinCount || 0}个` }) + } else { + list.push({ value: data.customerCount || 0, name: `客户-${data.customerCount || 0}个` }) + list.push({ value: data.businessCount || 0, name: `商机-${data.businessCount || 0}个` }) + list.push({ value: data.businessWinCount || 0, name: `赢单-${data.businessWinCount || 0}个` }) + } + echartsOption.series[0]['data'] = list } // 2.2 获取商机结束状态统计 - list.value = await StatisticFunnelApi.getBusinessEndStatusSummary(props.queryParams) + list.value = await StatisticFunnelApi.getBusinessSummaryByEndStatus(props.queryParams) loading.value = false } defineExpose({ loadData }) From 014d1f8cd2c9a6b3e651d0e565ee9a5813e9db42 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 15 Apr 2024 16:32:48 +0800 Subject: [PATCH 2/3] =?UTF-8?q?CRM:=20=E6=96=B0=E5=A2=9E=E5=95=86=E6=9C=BA?= =?UTF-8?q?=E8=B5=A2=E5=8D=95=E8=BD=AC=E5=8C=96=E7=8E=87=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/crm/statistics/funnel.ts | 15 +- .../BusinessInversionRateSummary.vue | 278 ++++++++++++++++++ .../funnel/components/BusinessSummary.vue | 2 +- src/views/crm/statistics/funnel/index.vue | 14 +- .../components/PortraitCustomerArea.vue | 2 +- 5 files changed, 304 insertions(+), 7 deletions(-) create mode 100644 src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue diff --git a/src/api/crm/statistics/funnel.ts b/src/api/crm/statistics/funnel.ts index ccfd7432..574a5f4f 100644 --- a/src/api/crm/statistics/funnel.ts +++ b/src/api/crm/statistics/funnel.ts @@ -12,6 +12,12 @@ export interface CrmStatisticsBusinessSummaryByDateRespVO { totalPrice: number | string // 商机金额 } +export interface CrmStatisticsBusinessInversionRateSummaryByDateRespVO { + time: string // 时间 + businessCount: number // 商机数量 + businessWinCount: number // 赢单商机数 +} + // 客户分析 API export const StatisticFunnelApi = { // 1. 获取销售漏斗统计数据 @@ -35,7 +41,14 @@ export const StatisticFunnelApi = { params }) }, - // 4. 获取商机列表(按日期) + // 4. 获取商机转化率分析(按日期) + getBusinessInversionRateSummaryByDate: (params: any) => { + return request.get({ + url: '/crm/statistics-funnel/get-business-inversion-rate-summary-by-date', + params + }) + }, + // 5. 获取商机列表(按日期) getBusinessPageByDate: (params: any) => { return request.get({ url: '/crm/statistics-funnel/get-business-page-by-date', diff --git a/src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue b/src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue new file mode 100644 index 00000000..47d53557 --- /dev/null +++ b/src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue @@ -0,0 +1,278 @@ + + + diff --git a/src/views/crm/statistics/funnel/components/BusinessSummary.vue b/src/views/crm/statistics/funnel/components/BusinessSummary.vue index ae4b032a..942a7128 100644 --- a/src/views/crm/statistics/funnel/components/BusinessSummary.vue +++ b/src/views/crm/statistics/funnel/components/BusinessSummary.vue @@ -163,7 +163,7 @@ const echartsOption = reactive({ brush: { type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮 }, - saveAsImage: { show: true, name: '客户总量分析' } // 保存为图片 + saveAsImage: { show: true, name: '新增商机分析' } // 保存为图片 } }, tooltip: { diff --git a/src/views/crm/statistics/funnel/index.vue b/src/views/crm/statistics/funnel/index.vue index b8cddf8d..804cb49b 100644 --- a/src/views/crm/statistics/funnel/index.vue +++ b/src/views/crm/statistics/funnel/index.vue @@ -87,7 +87,12 @@ - + + + @@ -100,6 +105,7 @@ import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/form import { defaultProps, handleTree } from '@/utils/tree' import FunnelBusiness from './components/FunnelBusiness.vue' import BusinessSummary from './components/BusinessSummary.vue' +import BusinessInversionRateSummary from './components/BusinessInversionRateSummary.vue' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' defineOptions({ name: 'CrmStatisticsFunnel' }) @@ -129,7 +135,7 @@ const userListByDeptId = computed(() => const activeTab = ref('funnelRef') // 活跃标签 const funnelRef = ref() // 销售漏斗 const businessSummaryRef = ref() // 新增商机分析 -const sourceRef = ref() // 客户来源 +const businessInversionRateSummaryRef = ref() // 商机转化率分析 /** 搜索按钮操作 */ const handleQuery = () => { @@ -140,8 +146,8 @@ const handleQuery = () => { case 'businessSummaryRef': businessSummaryRef.value?.loadData?.() break - case 'sourceRef': - sourceRef.value?.loadData?.() + case 'businessInversionRateSummaryRef': + businessInversionRateSummaryRef.value?.loadData?.() break } } diff --git a/src/views/crm/statistics/portrait/components/PortraitCustomerArea.vue b/src/views/crm/statistics/portrait/components/PortraitCustomerArea.vue index 8ccd52c8..112e6090 100644 --- a/src/views/crm/statistics/portrait/components/PortraitCustomerArea.vue +++ b/src/views/crm/statistics/portrait/components/PortraitCustomerArea.vue @@ -106,7 +106,7 @@ const loadData = async () => { areaStatisticsList.value = areaList.map((item: CrmStatisticCustomerAreaRespVO) => { return { ...item, - areaName: item.areaName // TODO @puhui999:这里最好注释下原因哈, 🤣 我从 mall copy 过来的 + areaName: item.areaName // .replace('维吾尔自治区', '') // .replace('壮族自治区', '') // .replace('回族自治区', '') From 4e640bae88b3cbf8484c8b0d75606e4847ca5bd6 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 15 Apr 2024 16:38:24 +0800 Subject: [PATCH 3/3] =?UTF-8?q?CRM:=20=E6=96=B0=E5=A2=9E=E5=95=86=E6=9C=BA?= =?UTF-8?q?=E8=B5=A2=E5=8D=95=E8=BD=AC=E5=8C=96=E7=8E=87=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BusinessInversionRateSummary.vue | 177 ++++++++++-------- 1 file changed, 103 insertions(+), 74 deletions(-) diff --git a/src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue b/src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue index 47d53557..541d6fc4 100644 --- a/src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue +++ b/src/views/crm/statistics/funnel/components/BusinessInversionRateSummary.vue @@ -134,81 +134,109 @@ watch( ) /** 柱状图配置:纵向 */ const echartsOption = reactive({ - grid: { - left: 30, - right: 30, // 让 X 轴右侧显示完整 - bottom: 20, - containLabel: true + color: ['#6ca2ff', '#6ac9d7', '#ff7474'], + tooltip: { + trigger: 'axis', + axisPointer: { + // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } }, - legend: {}, + legend: { + data: ['赢单转化率', '商机总数', '赢单商机数'], + bottom: '0px', + itemWidth: 14 + }, + grid: { + top: '40px', + left: '40px', + right: '40px', + bottom: '40px', + containLabel: true, + borderColor: '#fff' + }, + xAxis: [ + { + type: 'category', + data: [], + axisTick: { + alignWithLabel: true, + lineStyle: { width: 0 } + }, + axisLabel: { + color: '#BDBDBD' + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { color: '#BDBDBD' } + }, + splitLine: { + show: false + } + } + ], + yAxis: [ + { + type: 'value', + name: '赢单转化率', + axisTick: { + alignWithLabel: true, + lineStyle: { width: 0 } + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}%' + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { color: '#BDBDBD' } + }, + splitLine: { + show: false + } + }, + { + type: 'value', + name: '商机数', + axisTick: { + alignWithLabel: true, + lineStyle: { width: 0 } + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}个' + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { color: '#BDBDBD' } + }, + splitLine: { + show: false + } + } + ], series: [ + { + name: '赢单转化率', + type: 'line', + yAxisIndex: 0, + data: [] + }, { name: '商机总数', type: 'bar', - yAxisIndex: 0, + yAxisIndex: 1, + barWidth: 15, data: [] }, { name: '赢单商机数', type: 'bar', yAxisIndex: 1, - data: [] - }, - { - name: '赢单转化率', - type: 'bar', - yAxisIndex: 2, + barWidth: 15, data: [] } - ], - toolbox: { - feature: { - dataZoom: { - xAxisIndex: false // 数据区域缩放:Y 轴不缩放 - }, - brush: { - type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮 - }, - saveAsImage: { show: true, name: '商机转化率分析' } // 保存为图片 - } - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow' - } - }, - yAxis: [ - { - type: 'value', - name: '商机总数', - min: 0, - minInterval: 1 // 显示整数刻度 - }, - { - type: 'value', - name: '赢单商机数', - min: 0, - minInterval: 1 // 显示整数刻度 - }, - { - type: 'value', - name: '赢单转化率', - min: 0, - minInterval: 1, // 显示整数刻度 - splitLine: { - lineStyle: { - type: 'dotted', // 右侧网格线虚化, 减少混乱 - opacity: 0.7 - } - } - } - ], - xAxis: { - type: 'category', - name: '日期', - data: [] - } + ] }) as EChartsOption /** 获取数据并填充图表 */ @@ -218,27 +246,28 @@ const fetchAndFill = async () => { props.queryParams ) // 2.1 更新 Echarts 数据 - if (echartsOption.xAxis && echartsOption.xAxis['data']) { - echartsOption.xAxis['data'] = businessSummaryByDate.map( + if (echartsOption.xAxis && echartsOption.xAxis[0] && echartsOption.xAxis[0]['data']) { + echartsOption.xAxis[0]['data'] = businessSummaryByDate.map( (s: CrmStatisticsBusinessInversionRateSummaryByDateRespVO) => s.time ) } if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) { echartsOption.series[0]['data'] = businessSummaryByDate.map( - (s: CrmStatisticsBusinessInversionRateSummaryByDateRespVO) => s.businessCount - ) - } - if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) { - echartsOption.series[1]['data'] = businessSummaryByDate.map( - (s: CrmStatisticsBusinessInversionRateSummaryByDateRespVO) => s.businessWinCount - ) - } - if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { - echartsOption.series[2]['data'] = businessSummaryByDate.map( (s: CrmStatisticsBusinessInversionRateSummaryByDateRespVO) => erpCalculatePercentage(s.businessWinCount, s.businessCount) ) } + if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) { + echartsOption.series[1]['data'] = businessSummaryByDate.map( + (s: CrmStatisticsBusinessInversionRateSummaryByDateRespVO) => s.businessCount + ) + } + if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { + echartsOption.series[2]['data'] = businessSummaryByDate.map( + (s: CrmStatisticsBusinessInversionRateSummaryByDateRespVO) => s.businessWinCount + ) + } + // 2.2 更新列表数据 await getList() }