Skip to content

Commit 3c54f5f

Browse files
committed
feat: 增加[客户公海分析]tab
1 parent 13cf1fe commit 3c54f5f

File tree

3 files changed

+207
-13
lines changed

3 files changed

+207
-13
lines changed

src/api/crm/statistics/customer.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ export interface CrmStatisticsCustomerContractSummaryRespVO {
4444
orderDate: Date
4545
}
4646

47+
export interface CrmStatisticsPoolSummaryByDateRespVO {
48+
time: string
49+
customerPutCount: number
50+
customerTakeCount: number
51+
}
52+
53+
export interface CrmStatisticsPoolSummaryByUserRespVO {
54+
ownerUserName: string
55+
customerPutCount: number
56+
customerTakeCount: number
57+
}
58+
4759
export interface CrmStatisticsCustomerDealCycleByDateRespVO {
4860
time: string
4961
customerDealCycle: number
@@ -99,14 +111,28 @@ export const StatisticsCustomerApi = {
99111
params
100112
})
101113
},
102-
// 5.1 获取客户成交周期(按日期)
114+
// 5.1 获取客户公海分析(按日期)
115+
getPoolSummaryByDate: (param: any) => {
116+
return request.get({
117+
url: '/crm/statistics-customer/get-pool-summary-by-date',
118+
params: param
119+
})
120+
},
121+
// 5.2 获取客户公海分析(按用户)
122+
getPoolSummaryByUser: (param: any) => {
123+
return request.get({
124+
url: '/crm/statistics-customer/get-pool-summary-by-user',
125+
params: param
126+
})
127+
},
128+
// 6.1 获取客户成交周期(按日期)
103129
getCustomerDealCycleByDate: (params: any) => {
104130
return request.get({
105131
url: '/crm/statistics-customer/get-customer-deal-cycle-by-date',
106132
params
107133
})
108134
},
109-
// 5.2 获取客户成交周期(按用户)
135+
// 6.2 获取客户成交周期(按用户)
110136
getCustomerDealCycleByUser: (params: any) => {
111137
return request.get({
112138
url: '/crm/statistics-customer/get-customer-deal-cycle-by-user',
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<!-- 客户总量统计 -->
2+
<template>
3+
<!-- Echarts图 -->
4+
<el-card shadow="never">
5+
<el-skeleton :loading="loading" animated>
6+
<Echart :height="500" :options="echartsOption" />
7+
</el-skeleton>
8+
</el-card>
9+
10+
<!-- 统计列表 -->
11+
<el-card shadow="never" class="mt-16px">
12+
<el-table v-loading="loading" :data="list">
13+
<el-table-column label="序号" align="center" type="index" width="80" fixed="left" />
14+
<el-table-column label="员工姓名" prop="ownerUserName" min-width="100" fixed="left" />
15+
<el-table-column label="进入公海客户数" align="right" prop="customerPutCount" min-width="200" />
16+
<el-table-column label="公海领取客户数" align="right" prop="customerTakeCount" min-width="200" />
17+
</el-table>
18+
</el-card>
19+
</template>
20+
<script setup lang="ts">
21+
import {
22+
StatisticsCustomerApi,
23+
CrmStatisticsPoolSummaryByDateRespVO,
24+
CrmStatisticsPoolSummaryByUserRespVO
25+
} from '@/api/crm/statistics/customer'
26+
import { EChartsOption } from 'echarts'
27+
28+
defineOptions({ name: 'CustomerSummary' })
29+
30+
const props = defineProps<{ queryParams: any }>() // 搜索参数
31+
32+
const loading = ref(false) // 加载中
33+
const list = ref<CrmStatisticsPoolSummaryByUserRespVO[]>([]) // 列表的数据
34+
35+
/** 柱状图配置:纵向 */
36+
const echartsOption = reactive<EChartsOption>({
37+
grid: {
38+
left: 20,
39+
right: 40, // 让X轴右侧显示完整
40+
bottom: 20,
41+
containLabel: true
42+
},
43+
legend: {},
44+
series: [
45+
{
46+
name: '进入公海客户数',
47+
type: 'bar',
48+
yAxisIndex: 0,
49+
data: []
50+
},
51+
{
52+
name: '公海领取客户数',
53+
type: 'bar',
54+
yAxisIndex: 1,
55+
data: []
56+
}
57+
],
58+
toolbox: {
59+
feature: {
60+
dataZoom: {
61+
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
62+
},
63+
brush: {
64+
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
65+
},
66+
saveAsImage: { show: true, name: '公海客户分析' } // 保存为图片
67+
}
68+
},
69+
tooltip: {
70+
trigger: 'axis',
71+
axisPointer: {
72+
type: 'shadow'
73+
}
74+
},
75+
yAxis: [
76+
{
77+
type: 'value',
78+
name: '进入公海客户数',
79+
min: 0,
80+
minInterval: 1, // 显示整数刻度
81+
},
82+
{
83+
type: 'value',
84+
name: '公海领取客户数',
85+
min: 0,
86+
minInterval: 1, // 显示整数刻度
87+
splitLine: {
88+
lineStyle: {
89+
type: 'dotted', // 右侧网格线虚化, 减少混乱
90+
opacity: 0.7,
91+
}
92+
}
93+
}
94+
],
95+
xAxis: {
96+
type: 'category',
97+
name: '日期',
98+
data: [],
99+
}
100+
}) as EChartsOption
101+
102+
/** 获取数据并填充图表 */
103+
const fetchAndFill = async () => {
104+
// 1. 加载统计数据
105+
const poolSummaryByDate = await StatisticsCustomerApi.getPoolSummaryByDate(
106+
props.queryParams
107+
)
108+
const poolSummaryByUser = await StatisticsCustomerApi.getPoolSummaryByUser(
109+
props.queryParams
110+
)
111+
// 2.1 更新 Echarts 数据
112+
if (echartsOption.xAxis && echartsOption.xAxis['data']) {
113+
echartsOption.xAxis['data'] = poolSummaryByDate.map(
114+
(s: CrmStatisticsPoolSummaryByDateRespVO) => s.time
115+
)
116+
}
117+
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
118+
echartsOption.series[0]['data'] = poolSummaryByDate.map(
119+
(s: CrmStatisticsPoolSummaryByDateRespVO) => s.customerPutCount
120+
)
121+
}
122+
if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
123+
echartsOption.series[1]['data'] = poolSummaryByDate.map(
124+
(s: CrmStatisticsPoolSummaryByDateRespVO) => s.customerTakeCount
125+
)
126+
}
127+
128+
// 2.2 更新列表数据
129+
list.value = poolSummaryByUser
130+
}
131+
132+
/** 获取统计数据 */
133+
const loadData = async () => {
134+
loading.value = true
135+
try {
136+
fetchAndFill()
137+
} finally {
138+
loading.value = false
139+
}
140+
}
141+
142+
defineExpose({ loadData })
143+
144+
/** 初始化 */
145+
onMounted(() => {
146+
loadData()
147+
})
148+
</script>

src/views/crm/statistics/customer/index.vue

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@
1919
start-placeholder="开始日期"
2020
type="daterange"
2121
value-format="YYYY-MM-DD HH:mm:ss"
22+
@change="handleQuery"
2223
/>
2324
</el-form-item>
2425
<el-form-item label="时间间隔" prop="interval">
25-
<el-select v-model="queryParams.interval" class="!w-240px" placeholder="间隔类型">
26+
<el-select
27+
v-model="queryParams.interval"
28+
class="!w-240px"
29+
placeholder="间隔类型"
30+
@change="handleQuery"
31+
>
2632
<el-option
2733
v-for="dict in getIntDictOptions(DICT_TYPE.DATE_INTERVAL)"
2834
:key="dict.value"
@@ -40,11 +46,18 @@
4046
class="!w-240px"
4147
node-key="id"
4248
placeholder="请选择归属部门"
43-
@change="queryParams.userId = undefined"
49+
@change="queryParams.userId = undefined;handleQuery()
50+
"
4451
/>
4552
</el-form-item>
4653
<el-form-item label="员工" prop="userId">
47-
<el-select v-model="queryParams.userId" class="!w-240px" clearable placeholder="员工">
54+
<el-select
55+
v-model="queryParams.userId"
56+
class="!w-240px"
57+
clearable
58+
placeholder="员工"
59+
@change="handleQuery"
60+
>
4861
<el-option
4962
v-for="(user, index) in userListByDeptId"
5063
:key="index"
@@ -56,7 +69,7 @@
5669
<el-form-item>
5770
<el-button @click="handleQuery">
5871
<Icon class="mr-5px" icon="ep:search" />
59-
搜索
72+
查询
6073
</el-button>
6174
<el-button @click="resetQuery">
6275
<Icon class="mr-5px" icon="ep:refresh" />
@@ -85,6 +98,10 @@
8598
<el-tab-pane label="客户转化率分析" lazy name="conversionStat">
8699
<CustomerConversionStat ref="conversionStatRef" :query-params="queryParams" />
87100
</el-tab-pane>
101+
<!-- 公海客户分析 -->
102+
<el-tab-pane label="公海客户分析" lazy name="poolSummary">
103+
<PoolSummary ref="poolSummaryRef" :query-params="queryParams" />
104+
</el-tab-pane>
88105
<!-- 成交周期分析 -->
89106
<el-tab-pane label="成交周期分析" lazy name="dealCycle">
90107
<CustomerDealCycle ref="dealCycleRef" :query-params="queryParams" />
@@ -97,19 +114,20 @@
97114
import * as DeptApi from '@/api/system/dept'
98115
import * as UserApi from '@/api/system/user'
99116
import { useUserStore } from '@/store/modules/user'
117+
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
100118
import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
101119
import { defaultProps, handleTree } from '@/utils/tree'
102-
import CustomerSummary from './components/CustomerSummary.vue'
103-
import CustomerFollowUpSummary from './components/CustomerFollowUpSummary.vue'
104-
import CustomerFollowUpType from './components/CustomerFollowUpType.vue'
105120
import CustomerConversionStat from './components/CustomerConversionStat.vue'
106121
import CustomerDealCycle from './components/CustomerDealCycle.vue'
107-
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
122+
import CustomerFollowUpSummary from './components/CustomerFollowUpSummary.vue'
123+
import CustomerFollowUpType from './components/CustomerFollowUpType.vue'
124+
import CustomerSummary from './components/CustomerSummary.vue'
125+
import PoolSummary from './components/PoolSummary.vue'
108126
109127
defineOptions({ name: 'CrmStatisticsCustomer' })
110128
111129
const queryParams = reactive({
112-
interval: 1,
130+
interval: 2, // WEEK, 周
113131
deptId: useUserStore().getUser.deptId,
114132
userId: undefined,
115133
times: [
@@ -135,8 +153,7 @@ const customerSummaryRef = ref() // 1. 客户总量分析
135153
const followUpSummaryRef = ref() // 2. 客户跟进次数分析
136154
const followUpTypeRef = ref() // 3. 客户跟进方式分析
137155
const conversionStatRef = ref() // 4. 客户转化率分析
138-
// 5. TODO 公海客户分析
139-
// 缺 crm_owner_record 表 TODO @dhb52:可以先做界面 + 接口,接口数据直接写死返回,相当于 mock 出来
156+
const poolSummaryRef = ref() // 5. 客户公海分析
140157
const dealCycleRef = ref() // 6. 成交周期分析
141158
142159
/** 搜索按钮操作 */
@@ -154,6 +171,9 @@ const handleQuery = () => {
154171
case 'conversionStat': // 客户转化率分析
155172
conversionStatRef.value?.loadData?.()
156173
break
174+
case 'poolSummary': // 公海客户分析
175+
poolSummaryRef.value?.loadData?.()
176+
break
157177
case 'dealCycle': // 成交周期分析
158178
dealCycleRef.value?.loadData?.()
159179
break

0 commit comments

Comments
 (0)