Skip to content

Commit 6037ac4

Browse files
YunaiVgitee-org
authored andcommitted
!419 feat: 客户成交周期分析(按区域、按产品)
Merge pull request !419 from dhb52/dev
2 parents 1f0ef78 + 5358863 commit 6037ac4

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 label="区域" align="center" prop="areaName" min-width="200" />
15+
<el-table-column
16+
label="成交周期(天)"
17+
align="center"
18+
prop="customerDealCycle"
19+
min-width="200"
20+
/>
21+
<el-table-column label="成交客户数" align="center" prop="customerDealCount" min-width="200" />
22+
</el-table>
23+
</el-card>
24+
</template>
25+
<script setup lang="ts">
26+
import {
27+
StatisticsCustomerApi,
28+
CrmStatisticsCustomerDealCycleByAreaRespVO
29+
} from '@/api/crm/statistics/customer'
30+
import { EChartsOption } from 'echarts'
31+
32+
defineOptions({ name: 'CustomerDealCycleByArea' })
33+
34+
const props = defineProps<{ queryParams: any }>() // 搜索参数
35+
36+
const loading = ref(false) // 加载中
37+
const list = ref<CrmStatisticsCustomerDealCycleByAreaRespVO[]>([]) // 列表的数据
38+
39+
/** 柱状图配置:纵向 */
40+
const echartsOption = reactive<EChartsOption>({
41+
grid: {
42+
left: 20,
43+
right: 40, // 让 X 轴右侧显示完整
44+
bottom: 20,
45+
containLabel: true
46+
},
47+
legend: {},
48+
series: [
49+
{
50+
name: '成交周期(天)',
51+
type: 'bar',
52+
data: [],
53+
yAxisIndex: 0
54+
},
55+
{
56+
name: '成交客户数',
57+
type: 'bar',
58+
data: [],
59+
yAxisIndex: 1
60+
}
61+
],
62+
toolbox: {
63+
feature: {
64+
dataZoom: {
65+
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
66+
},
67+
brush: {
68+
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
69+
},
70+
saveAsImage: { show: true, name: '成交周期分析' } // 保存为图片
71+
}
72+
},
73+
tooltip: {
74+
trigger: 'axis',
75+
axisPointer: {
76+
type: 'shadow'
77+
}
78+
},
79+
yAxis: [
80+
{
81+
type: 'value',
82+
name: '成交周期(天)',
83+
min: 0,
84+
minInterval: 1 // 显示整数刻度
85+
},
86+
{
87+
type: 'value',
88+
name: '成交客户数',
89+
min: 0,
90+
minInterval: 1, // 显示整数刻度
91+
splitLine: {
92+
lineStyle: {
93+
type: 'dotted', // 右侧网格线虚化, 减少混乱
94+
opacity: 0.7
95+
}
96+
}
97+
}
98+
],
99+
xAxis: {
100+
type: 'category',
101+
name: '区域',
102+
data: []
103+
}
104+
}) as EChartsOption
105+
106+
/** 获取数据并填充图表 */
107+
const fetchAndFill = async () => {
108+
// 1. 加载统计数据
109+
const customerDealCycleByArea = (
110+
await StatisticsCustomerApi.getCustomerDealCycleByArea(props.queryParams)
111+
).map((s: CrmStatisticsCustomerDealCycleByAreaRespVO) => {
112+
return {
113+
areaName: s.areaName,
114+
customerDealCycle: s.customerDealCycle,
115+
customerDealCount: s.customerDealCount
116+
}
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 label="产品名称" align="center" prop="productName" min-width="200" />
15+
<el-table-column
16+
label="成交周期(天)"
17+
align="center"
18+
prop="customerDealCycle"
19+
min-width="200"
20+
/>
21+
<el-table-column label="成交客户数" align="center" prop="customerDealCount" min-width="200" />
22+
</el-table>
23+
</el-card>
24+
</template>
25+
<script setup lang="ts">
26+
import {
27+
StatisticsCustomerApi,
28+
CrmStatisticsCustomerDealCycleByProductRespVO
29+
} from '@/api/crm/statistics/customer'
30+
import { EChartsOption } from 'echarts'
31+
32+
defineOptions({ name: 'CustomerDealCycleByProduct' })
33+
34+
const props = defineProps<{ queryParams: any }>() // 搜索参数
35+
36+
const loading = ref(false) // 加载中
37+
const list = ref<CrmStatisticsCustomerDealCycleByProductRespVO[]>([]) // 列表的数据
38+
39+
/** 柱状图配置:纵向 */
40+
const echartsOption = reactive<EChartsOption>({
41+
grid: {
42+
left: 20,
43+
right: 40, // 让 X 轴右侧显示完整
44+
bottom: 20,
45+
containLabel: true
46+
},
47+
legend: {},
48+
series: [
49+
{
50+
name: '成交周期(天)',
51+
type: 'bar',
52+
data: [],
53+
yAxisIndex: 0
54+
},
55+
{
56+
name: '成交客户数',
57+
type: 'bar',
58+
data: [],
59+
yAxisIndex: 1
60+
}
61+
],
62+
toolbox: {
63+
feature: {
64+
dataZoom: {
65+
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
66+
},
67+
brush: {
68+
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
69+
},
70+
saveAsImage: { show: true, name: '成交周期分析' } // 保存为图片
71+
}
72+
},
73+
tooltip: {
74+
trigger: 'axis',
75+
axisPointer: {
76+
type: 'shadow'
77+
}
78+
},
79+
yAxis: [
80+
{
81+
type: 'value',
82+
name: '成交周期(天)',
83+
min: 0,
84+
minInterval: 1 // 显示整数刻度
85+
},
86+
{
87+
type: 'value',
88+
name: '成交客户数',
89+
min: 0,
90+
minInterval: 1, // 显示整数刻度
91+
splitLine: {
92+
lineStyle: {
93+
type: 'dotted', // 右侧网格线虚化, 减少混乱
94+
opacity: 0.7
95+
}
96+
}
97+
}
98+
],
99+
xAxis: {
100+
type: 'category',
101+
name: '产品名称',
102+
data: []
103+
}
104+
}) as EChartsOption
105+
106+
/** 获取数据并填充图表 */
107+
const fetchAndFill = async () => {
108+
// 1. 加载统计数据
109+
const customerDealCycleByProduct = (
110+
await StatisticsCustomerApi.getCustomerDealCycleByProduct(props.queryParams)
111+
).map((s: CrmStatisticsCustomerDealCycleByProductRespVO) => {
112+
return {
113+
productName: s.productName ?? '未知',
114+
customerDealCycle: s.customerDealCount,
115+
customerDealCount: s.customerDealCount
116+
}
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)