Skip to content

Commit 0e15d6c

Browse files
committed
feat: 客户成交周期分析(按区域、按产品)
1 parent f6e4753 commit 0e15d6c

File tree

5 files changed

+355
-7
lines changed

5 files changed

+355
-7
lines changed

src/api/crm/statistics/customer.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ export interface CrmStatisticsCustomerDealCycleByUserRespVO {
6767
customerDealCount: number
6868
}
6969

70+
export interface CrmStatisticsCustomerDealCycleByAreaRespVO {
71+
areaName: string
72+
customerDealCycle: number
73+
customerDealCount: number
74+
}
75+
76+
export interface CrmStatisticsCustomerDealCycleByProductRespVO {
77+
productName: string
78+
customerDealCycle: number
79+
customerDealCount: number
80+
}
81+
7082
// 客户分析 API
7183
export const StatisticsCustomerApi = {
7284
// 1.1 客户总量分析(按日期)
@@ -138,5 +150,19 @@ export const StatisticsCustomerApi = {
138150
url: '/crm/statistics-customer/get-customer-deal-cycle-by-user',
139151
params
140152
})
153+
},
154+
// 6.2 获取客户成交周期(按用户)
155+
getCustomerDealCycleByArea: (params: any) => {
156+
return request.get({
157+
url: '/crm/statistics-customer/get-customer-deal-cycle-by-area',
158+
params
159+
})
160+
},
161+
// 6.2 获取客户成交周期(按用户)
162+
getCustomerDealCycleByProduct: (params: any) => {
163+
return request.get({
164+
url: '/crm/statistics-customer/get-customer-deal-cycle-by-product',
165+
params
166+
})
141167
}
142168
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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" />
14+
<el-table-column
15+
label="区域"
16+
align="center"
17+
prop="areaName"
18+
min-width="200"
19+
:formatter="(_, __, val: any) => val ?? '未知'"
20+
/>
21+
<el-table-column
22+
label="成交周期(天)"
23+
align="center"
24+
prop="customerDealCycle"
25+
min-width="200"
26+
/>
27+
<el-table-column label="成交客户数" align="center" prop="customerDealCount" min-width="200" />
28+
</el-table>
29+
</el-card>
30+
</template>
31+
<script setup lang="ts">
32+
import {
33+
StatisticsCustomerApi,
34+
CrmStatisticsCustomerDealCycleByAreaRespVO
35+
} from '@/api/crm/statistics/customer'
36+
import { EChartsOption } from 'echarts'
37+
38+
defineOptions({ name: 'CustomerDealCycleByArea' })
39+
40+
const props = defineProps<{ queryParams: any }>() // 搜索参数
41+
42+
const loading = ref(false) // 加载中
43+
const list = ref<CrmStatisticsCustomerDealCycleByAreaRespVO[]>([]) // 列表的数据
44+
45+
/** 柱状图配置:纵向 */
46+
const echartsOption = reactive<EChartsOption>({
47+
grid: {
48+
left: 20,
49+
right: 40, // 让 X 轴右侧显示完整
50+
bottom: 20,
51+
containLabel: true
52+
},
53+
legend: {},
54+
series: [
55+
{
56+
name: '成交周期(天)',
57+
type: 'bar',
58+
data: [],
59+
yAxisIndex: 0
60+
},
61+
{
62+
name: '成交客户数',
63+
type: 'bar',
64+
data: [],
65+
yAxisIndex: 1
66+
}
67+
],
68+
toolbox: {
69+
feature: {
70+
dataZoom: {
71+
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
72+
},
73+
brush: {
74+
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
75+
},
76+
saveAsImage: { show: true, name: '成交周期分析' } // 保存为图片
77+
}
78+
},
79+
tooltip: {
80+
trigger: 'axis',
81+
axisPointer: {
82+
type: 'shadow'
83+
}
84+
},
85+
yAxis: [
86+
{
87+
type: 'value',
88+
name: '成交周期(天)',
89+
min: 0,
90+
minInterval: 1 // 显示整数刻度
91+
},
92+
{
93+
type: 'value',
94+
name: '成交客户数',
95+
min: 0,
96+
minInterval: 1, // 显示整数刻度
97+
splitLine: {
98+
lineStyle: {
99+
type: 'dotted', // 右侧网格线虚化, 减少混乱
100+
opacity: 0.7
101+
}
102+
}
103+
}
104+
],
105+
xAxis: {
106+
type: 'category',
107+
name: '区域',
108+
data: []
109+
}
110+
}) as EChartsOption
111+
112+
/** 获取数据并填充图表 */
113+
const fetchAndFill = async () => {
114+
// 1. 加载统计数据
115+
const customerDealCycleByArea = await StatisticsCustomerApi.getCustomerDealCycleByArea(
116+
props.queryParams
117+
)
118+
// 2.1 更新 Echarts 数据
119+
if (echartsOption.xAxis && echartsOption.xAxis['data']) {
120+
echartsOption.xAxis['data'] = customerDealCycleByArea.map(
121+
(s: CrmStatisticsCustomerDealCycleByAreaRespVO) => s.areaName ?? '未知'
122+
)
123+
}
124+
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
125+
echartsOption.series[0]['data'] = customerDealCycleByArea.map(
126+
(s: CrmStatisticsCustomerDealCycleByAreaRespVO) => s.customerDealCycle
127+
)
128+
}
129+
if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
130+
echartsOption.series[1]['data'] = customerDealCycleByArea.map(
131+
(s: CrmStatisticsCustomerDealCycleByAreaRespVO) => s.customerDealCount
132+
)
133+
}
134+
// 2.2 更新列表数据
135+
list.value = customerDealCycleByArea
136+
}
137+
138+
/** 获取统计数据 */
139+
const loadData = async () => {
140+
loading.value = true
141+
try {
142+
await fetchAndFill()
143+
} finally {
144+
loading.value = false
145+
}
146+
}
147+
defineExpose({ loadData })
148+
149+
/** 初始化 */
150+
onMounted(() => {
151+
loadData()
152+
})
153+
</script>
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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" />
14+
<el-table-column
15+
label="产品名称"
16+
align="center"
17+
prop="productName"
18+
min-width="200"
19+
:formatter="(_, __, val: any) => val ?? '未知'"
20+
/>
21+
<el-table-column
22+
label="成交周期(天)"
23+
align="center"
24+
prop="customerDealCycle"
25+
min-width="200"
26+
/>
27+
<el-table-column label="成交客户数" align="center" prop="customerDealCount" min-width="200" />
28+
</el-table>
29+
</el-card>
30+
</template>
31+
<script setup lang="ts">
32+
import {
33+
StatisticsCustomerApi,
34+
CrmStatisticsCustomerDealCycleByProductRespVO
35+
} from '@/api/crm/statistics/customer'
36+
import { EChartsOption } from 'echarts'
37+
38+
defineOptions({ name: 'CustomerDealCycleByProduct' })
39+
40+
const props = defineProps<{ queryParams: any }>() // 搜索参数
41+
42+
const loading = ref(false) // 加载中
43+
const list = ref<CrmStatisticsCustomerDealCycleByProductRespVO[]>([]) // 列表的数据
44+
45+
/** 柱状图配置:纵向 */
46+
const echartsOption = reactive<EChartsOption>({
47+
grid: {
48+
left: 20,
49+
right: 40, // 让 X 轴右侧显示完整
50+
bottom: 20,
51+
containLabel: true
52+
},
53+
legend: {},
54+
series: [
55+
{
56+
name: '成交周期(天)',
57+
type: 'bar',
58+
data: [],
59+
yAxisIndex: 0
60+
},
61+
{
62+
name: '成交客户数',
63+
type: 'bar',
64+
data: [],
65+
yAxisIndex: 1
66+
}
67+
],
68+
toolbox: {
69+
feature: {
70+
dataZoom: {
71+
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
72+
},
73+
brush: {
74+
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
75+
},
76+
saveAsImage: { show: true, name: '成交周期分析' } // 保存为图片
77+
}
78+
},
79+
tooltip: {
80+
trigger: 'axis',
81+
axisPointer: {
82+
type: 'shadow'
83+
}
84+
},
85+
yAxis: [
86+
{
87+
type: 'value',
88+
name: '成交周期(天)',
89+
min: 0,
90+
minInterval: 1 // 显示整数刻度
91+
},
92+
{
93+
type: 'value',
94+
name: '成交客户数',
95+
min: 0,
96+
minInterval: 1, // 显示整数刻度
97+
splitLine: {
98+
lineStyle: {
99+
type: 'dotted', // 右侧网格线虚化, 减少混乱
100+
opacity: 0.7
101+
}
102+
}
103+
}
104+
],
105+
xAxis: {
106+
type: 'category',
107+
name: '产品名称',
108+
data: []
109+
}
110+
}) as EChartsOption
111+
112+
/** 获取数据并填充图表 */
113+
const fetchAndFill = async () => {
114+
// 1. 加载统计数据
115+
const customerDealCycleByProduct = await StatisticsCustomerApi.getCustomerDealCycleByProduct(
116+
props.queryParams
117+
)
118+
// 2.1 更新 Echarts 数据
119+
if (echartsOption.xAxis && echartsOption.xAxis['data']) {
120+
echartsOption.xAxis['data'] = customerDealCycleByProduct.map(
121+
(s: CrmStatisticsCustomerDealCycleByProductRespVO) => s.productName ?? '未知'
122+
)
123+
}
124+
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
125+
echartsOption.series[0]['data'] = customerDealCycleByProduct.map(
126+
(s: CrmStatisticsCustomerDealCycleByProductRespVO) => s.customerDealCycle
127+
)
128+
}
129+
if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
130+
echartsOption.series[1]['data'] = customerDealCycleByProduct.map(
131+
(s: CrmStatisticsCustomerDealCycleByProductRespVO) => s.customerDealCount
132+
)
133+
}
134+
// 2.2 更新列表数据
135+
list.value = customerDealCycleByProduct
136+
}
137+
138+
/** 获取统计数据 */
139+
const loadData = async () => {
140+
loading.value = true
141+
try {
142+
await fetchAndFill()
143+
} finally {
144+
loading.value = false
145+
}
146+
}
147+
defineExpose({ loadData })
148+
149+
/** 初始化 */
150+
onMounted(() => {
151+
loadData()
152+
})
153+
</script>

src/views/crm/statistics/customer/components/CustomerDealCycle.vue renamed to src/views/crm/statistics/customer/components/CustomerDealCycleByUser.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
} from '@/api/crm/statistics/customer'
3131
import { EChartsOption } from 'echarts'
3232
33-
defineOptions({ name: 'CustomerDealCycle' })
33+
defineOptions({ name: 'CustomerDealCycleByUser' })
3434
3535
const props = defineProps<{ queryParams: any }>() // 搜索参数
3636

0 commit comments

Comments
 (0)