Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
0e15d6c9
authored
Apr 09, 2024
by
dhb52
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 客户成交周期分析(按区域、按产品)
parent
f6e4753b
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
355 additions
and
7 deletions
+355
-7
src/api/crm/statistics/customer.ts
+26
-0
src/views/crm/statistics/customer/components/CustomerDealCycleByArea.vue
+153
-0
src/views/crm/statistics/customer/components/CustomerDealCycleByProduct.vue
+153
-0
src/views/crm/statistics/customer/components/CustomerDealCycleByUser.vue
+1
-1
src/views/crm/statistics/customer/index.vue
+22
-6
No files found.
src/api/crm/statistics/customer.ts
View file @
0e15d6c9
...
...
@@ -67,6 +67,18 @@ export interface CrmStatisticsCustomerDealCycleByUserRespVO {
customerDealCount
:
number
}
export
interface
CrmStatisticsCustomerDealCycleByAreaRespVO
{
areaName
:
string
customerDealCycle
:
number
customerDealCount
:
number
}
export
interface
CrmStatisticsCustomerDealCycleByProductRespVO
{
productName
:
string
customerDealCycle
:
number
customerDealCount
:
number
}
// 客户分析 API
export
const
StatisticsCustomerApi
=
{
// 1.1 客户总量分析(按日期)
...
...
@@ -138,5 +150,19 @@ export const StatisticsCustomerApi = {
url
:
'/crm/statistics-customer/get-customer-deal-cycle-by-user'
,
params
})
},
// 6.2 获取客户成交周期(按用户)
getCustomerDealCycleByArea
:
(
params
:
any
)
=>
{
return
request
.
get
({
url
:
'/crm/statistics-customer/get-customer-deal-cycle-by-area'
,
params
})
},
// 6.2 获取客户成交周期(按用户)
getCustomerDealCycleByProduct
:
(
params
:
any
)
=>
{
return
request
.
get
({
url
:
'/crm/statistics-customer/get-customer-deal-cycle-by-product'
,
params
})
}
}
src/views/crm/statistics/customer/components/CustomerDealCycleByArea.vue
0 → 100644
View file @
0e15d6c9
<!-- 成交周期分析 -->
<
template
>
<!-- Echarts图 -->
<el-card
shadow=
"never"
>
<el-skeleton
:loading=
"loading"
animated
>
<Echart
:height=
"500"
:options=
"echartsOption"
/>
</el-skeleton>
</el-card>
<!-- 统计列表 -->
<el-card
shadow=
"never"
class=
"mt-16px"
>
<el-table
v-loading=
"loading"
:data=
"list"
>
<el-table-column
label=
"序号"
align=
"center"
type=
"index"
width=
"80"
/>
<el-table-column
label=
"区域"
align=
"center"
prop=
"areaName"
min-width=
"200"
:formatter=
"(_, __, val: any) => val ?? '未知'"
/>
<el-table-column
label=
"成交周期(天)"
align=
"center"
prop=
"customerDealCycle"
min-width=
"200"
/>
<el-table-column
label=
"成交客户数"
align=
"center"
prop=
"customerDealCount"
min-width=
"200"
/>
</el-table>
</el-card>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
StatisticsCustomerApi
,
CrmStatisticsCustomerDealCycleByAreaRespVO
}
from
'@/api/crm/statistics/customer'
import
{
EChartsOption
}
from
'echarts'
defineOptions
({
name
:
'CustomerDealCycleByArea'
})
const
props
=
defineProps
<
{
queryParams
:
any
}
>
()
// 搜索参数
const
loading
=
ref
(
false
)
// 加载中
const
list
=
ref
<
CrmStatisticsCustomerDealCycleByAreaRespVO
[]
>
([])
// 列表的数据
/** 柱状图配置:纵向 */
const
echartsOption
=
reactive
<
EChartsOption
>
({
grid
:
{
left
:
20
,
right
:
40
,
// 让 X 轴右侧显示完整
bottom
:
20
,
containLabel
:
true
},
legend
:
{},
series
:
[
{
name
:
'成交周期(天)'
,
type
:
'bar'
,
data
:
[],
yAxisIndex
:
0
},
{
name
:
'成交客户数'
,
type
:
'bar'
,
data
:
[],
yAxisIndex
:
1
}
],
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
,
// 显示整数刻度
splitLine
:
{
lineStyle
:
{
type
:
'dotted'
,
// 右侧网格线虚化, 减少混乱
opacity
:
0.7
}
}
}
],
xAxis
:
{
type
:
'category'
,
name
:
'区域'
,
data
:
[]
}
})
as
EChartsOption
/** 获取数据并填充图表 */
const
fetchAndFill
=
async
()
=>
{
// 1. 加载统计数据
const
customerDealCycleByArea
=
await
StatisticsCustomerApi
.
getCustomerDealCycleByArea
(
props
.
queryParams
)
// 2.1 更新 Echarts 数据
if
(
echartsOption
.
xAxis
&&
echartsOption
.
xAxis
[
'data'
])
{
echartsOption
.
xAxis
[
'data'
]
=
customerDealCycleByArea
.
map
(
(
s
:
CrmStatisticsCustomerDealCycleByAreaRespVO
)
=>
s
.
areaName
??
'未知'
)
}
if
(
echartsOption
.
series
&&
echartsOption
.
series
[
0
]
&&
echartsOption
.
series
[
0
][
'data'
])
{
echartsOption
.
series
[
0
][
'data'
]
=
customerDealCycleByArea
.
map
(
(
s
:
CrmStatisticsCustomerDealCycleByAreaRespVO
)
=>
s
.
customerDealCycle
)
}
if
(
echartsOption
.
series
&&
echartsOption
.
series
[
1
]
&&
echartsOption
.
series
[
1
][
'data'
])
{
echartsOption
.
series
[
1
][
'data'
]
=
customerDealCycleByArea
.
map
(
(
s
:
CrmStatisticsCustomerDealCycleByAreaRespVO
)
=>
s
.
customerDealCount
)
}
// 2.2 更新列表数据
list
.
value
=
customerDealCycleByArea
}
/** 获取统计数据 */
const
loadData
=
async
()
=>
{
loading
.
value
=
true
try
{
await
fetchAndFill
()
}
finally
{
loading
.
value
=
false
}
}
defineExpose
({
loadData
})
/** 初始化 */
onMounted
(()
=>
{
loadData
()
})
</
script
>
src/views/crm/statistics/customer/components/CustomerDealCycleByProduct.vue
0 → 100644
View file @
0e15d6c9
<!-- 成交周期分析 -->
<
template
>
<!-- Echarts图 -->
<el-card
shadow=
"never"
>
<el-skeleton
:loading=
"loading"
animated
>
<Echart
:height=
"500"
:options=
"echartsOption"
/>
</el-skeleton>
</el-card>
<!-- 统计列表 -->
<el-card
shadow=
"never"
class=
"mt-16px"
>
<el-table
v-loading=
"loading"
:data=
"list"
>
<el-table-column
label=
"序号"
align=
"center"
type=
"index"
width=
"80"
/>
<el-table-column
label=
"产品名称"
align=
"center"
prop=
"productName"
min-width=
"200"
:formatter=
"(_, __, val: any) => val ?? '未知'"
/>
<el-table-column
label=
"成交周期(天)"
align=
"center"
prop=
"customerDealCycle"
min-width=
"200"
/>
<el-table-column
label=
"成交客户数"
align=
"center"
prop=
"customerDealCount"
min-width=
"200"
/>
</el-table>
</el-card>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
StatisticsCustomerApi
,
CrmStatisticsCustomerDealCycleByProductRespVO
}
from
'@/api/crm/statistics/customer'
import
{
EChartsOption
}
from
'echarts'
defineOptions
({
name
:
'CustomerDealCycleByProduct'
})
const
props
=
defineProps
<
{
queryParams
:
any
}
>
()
// 搜索参数
const
loading
=
ref
(
false
)
// 加载中
const
list
=
ref
<
CrmStatisticsCustomerDealCycleByProductRespVO
[]
>
([])
// 列表的数据
/** 柱状图配置:纵向 */
const
echartsOption
=
reactive
<
EChartsOption
>
({
grid
:
{
left
:
20
,
right
:
40
,
// 让 X 轴右侧显示完整
bottom
:
20
,
containLabel
:
true
},
legend
:
{},
series
:
[
{
name
:
'成交周期(天)'
,
type
:
'bar'
,
data
:
[],
yAxisIndex
:
0
},
{
name
:
'成交客户数'
,
type
:
'bar'
,
data
:
[],
yAxisIndex
:
1
}
],
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
,
// 显示整数刻度
splitLine
:
{
lineStyle
:
{
type
:
'dotted'
,
// 右侧网格线虚化, 减少混乱
opacity
:
0.7
}
}
}
],
xAxis
:
{
type
:
'category'
,
name
:
'产品名称'
,
data
:
[]
}
})
as
EChartsOption
/** 获取数据并填充图表 */
const
fetchAndFill
=
async
()
=>
{
// 1. 加载统计数据
const
customerDealCycleByProduct
=
await
StatisticsCustomerApi
.
getCustomerDealCycleByProduct
(
props
.
queryParams
)
// 2.1 更新 Echarts 数据
if
(
echartsOption
.
xAxis
&&
echartsOption
.
xAxis
[
'data'
])
{
echartsOption
.
xAxis
[
'data'
]
=
customerDealCycleByProduct
.
map
(
(
s
:
CrmStatisticsCustomerDealCycleByProductRespVO
)
=>
s
.
productName
??
'未知'
)
}
if
(
echartsOption
.
series
&&
echartsOption
.
series
[
0
]
&&
echartsOption
.
series
[
0
][
'data'
])
{
echartsOption
.
series
[
0
][
'data'
]
=
customerDealCycleByProduct
.
map
(
(
s
:
CrmStatisticsCustomerDealCycleByProductRespVO
)
=>
s
.
customerDealCycle
)
}
if
(
echartsOption
.
series
&&
echartsOption
.
series
[
1
]
&&
echartsOption
.
series
[
1
][
'data'
])
{
echartsOption
.
series
[
1
][
'data'
]
=
customerDealCycleByProduct
.
map
(
(
s
:
CrmStatisticsCustomerDealCycleByProductRespVO
)
=>
s
.
customerDealCount
)
}
// 2.2 更新列表数据
list
.
value
=
customerDealCycleByProduct
}
/** 获取统计数据 */
const
loadData
=
async
()
=>
{
loading
.
value
=
true
try
{
await
fetchAndFill
()
}
finally
{
loading
.
value
=
false
}
}
defineExpose
({
loadData
})
/** 初始化 */
onMounted
(()
=>
{
loadData
()
})
</
script
>
src/views/crm/statistics/customer/components/CustomerDealCycle.vue
→
src/views/crm/statistics/customer/components/CustomerDealCycle
ByUser
.vue
View file @
0e15d6c9
...
...
@@ -30,7 +30,7 @@ import {
}
from
'@/api/crm/statistics/customer'
import
{
EChartsOption
}
from
'echarts'
defineOptions
({
name
:
'CustomerDealCycle'
})
defineOptions
({
name
:
'CustomerDealCycle
ByUser
'
})
const
props
=
defineProps
<
{
queryParams
:
any
}
>
()
// 搜索参数
...
...
src/views/crm/statistics/customer/index.vue
View file @
0e15d6c9
...
...
@@ -102,8 +102,14 @@
<CustomerPoolSummary
ref=
"customerPoolSummaryRef"
:query-params=
"queryParams"
/>
</el-tab-pane>
<!-- 成交周期分析 -->
<el-tab-pane
label=
"成交周期分析"
lazy
name=
"dealCycle"
>
<CustomerDealCycle
ref=
"dealCycleRef"
:query-params=
"queryParams"
/>
<el-tab-pane
label=
"员工客户成交周期分析"
lazy
name=
"dealCycleByUser"
>
<CustomerDealCycleByUser
ref=
"dealCycleByUserRef"
:query-params=
"queryParams"
/>
</el-tab-pane>
<el-tab-pane
label=
"地区客户成交周期分析"
lazy
name=
"dealCycleByArea"
>
<CustomerDealCycleByArea
ref=
"dealCycleByAreaRef"
:query-params=
"queryParams"
/>
</el-tab-pane>
<el-tab-pane
label=
"产品客户成交周期分析"
lazy
name=
"dealCycleByProduct"
>
<CustomerDealCycleByProduct
ref=
"dealCycleByProductRef"
:query-params=
"queryParams"
/>
</el-tab-pane>
</el-tabs>
</el-col>
...
...
@@ -117,7 +123,9 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import
{
beginOfDay
,
defaultShortcuts
,
endOfDay
,
formatDate
}
from
'@/utils/formatTime'
import
{
defaultProps
,
handleTree
}
from
'@/utils/tree'
import
CustomerConversionStat
from
'./components/CustomerConversionStat.vue'
import
CustomerDealCycle
from
'./components/CustomerDealCycle.vue'
import
CustomerDealCycleByUser
from
'./components/CustomerDealCycleByUser.vue'
import
CustomerDealCycleByArea
from
'./components/CustomerDealCycleByArea.vue'
import
CustomerDealCycleByProduct
from
'./components/CustomerDealCycleByProduct.vue'
import
CustomerFollowUpSummary
from
'./components/CustomerFollowUpSummary.vue'
import
CustomerFollowUpType
from
'./components/CustomerFollowUpType.vue'
import
CustomerSummary
from
'./components/CustomerSummary.vue'
...
...
@@ -153,7 +161,9 @@ const followUpSummaryRef = ref() // 2. 客户跟进次数分析
const
followUpTypeRef
=
ref
()
// 3. 客户跟进方式分析
const
conversionStatRef
=
ref
()
// 4. 客户转化率分析
const
customerPoolSummaryRef
=
ref
()
// 5. 客户公海分析
const
dealCycleRef
=
ref
()
// 6. 成交周期分析
const
dealCycleByUserRef
=
ref
()
// 6. 成交周期分析(按员工)
const
dealCycleByAreaRef
=
ref
()
// 7. 成交周期分析(按地区)
const
dealCycleByProductRef
=
ref
()
// 8. 成交周期分析(按产品)
/** 搜索按钮操作 */
const
handleQuery
=
()
=>
{
...
...
@@ -173,8 +183,14 @@ const handleQuery = () => {
case
'poolSummary'
:
// 公海客户分析
customerPoolSummaryRef
.
value
?.
loadData
?.()
break
case
'dealCycle'
:
// 成交周期分析
dealCycleRef
.
value
?.
loadData
?.()
case
'dealCycleByUser'
:
// 成交周期分析
dealCycleByUserRef
.
value
?.
loadData
?.()
break
case
'dealCycleByArea'
:
// 成交周期分析
dealCycleByAreaRef
.
value
?.
loadData
?.()
break
case
'dealCycleByProduct'
:
// 成交周期分析
dealCycleByProductRef
.
value
?.
loadData
?.()
break
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment