Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
baifang-java
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
单欣鑫
baifang-java
Commits
6a3f72ad
Commit
6a3f72ad
authored
Jun 06, 2025
by
赵乐
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
首页
parent
8628b9ca
Changes
10
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1895 additions
and
401 deletions
+1895
-401
index.ts
yudao-ui/yudao-ui-admin-vue3/src/api/visit/home/index.ts
+41
-0
Index copy.vue
yudao-ui/yudao-ui-admin-vue3/src/views/Home/Index copy.vue
+422
-0
Index.vue
yudao-ui/yudao-ui-admin-vue3/src/views/Home/Index.vue
+375
-401
YearRangeSelector.vue
.../yudao-ui-admin-vue3/src/views/Home/YearRangeSelector.vue
+316
-0
card.vue
yudao-ui/yudao-ui-admin-vue3/src/views/Home/card.vue
+102
-0
BarChart.vue
...udao-ui-admin-vue3/src/views/Home/components/BarChart.vue
+137
-0
LineChart.vue
...dao-ui-admin-vue3/src/views/Home/components/LineChart.vue
+154
-0
PieChart.vue
...udao-ui-admin-vue3/src/views/Home/components/PieChart.vue
+147
-0
barChart_1.vue
...ao-ui-admin-vue3/src/views/Home/components/barChart_1.vue
+128
-0
chartsCard.vue
...ao-ui-admin-vue3/src/views/Home/components/chartsCard.vue
+73
-0
No files found.
yudao-ui/yudao-ui-admin-vue3/src/api/visit/home/index.ts
0 → 100644
View file @
6a3f72ad
import
request
from
'
@/config/axios
'
// 客户信息 API
export
const
homesApi
=
{
// 查询卡片信息
getHomeInfoFirst
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoFirst`
,
params
})
},
// 竖柱状图数据
getHomeInfoBfztj
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoBfztj`
,
params
})
},
// 折线图数据
getHomeInfoBfrtj
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoBfrtj`
,
params
})
},
// 客户性质等级占比情况
getHomeInfoKhxzdjzbqk
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoKhxzdjzbqk`
,
params
})
},
// 拜访人均分布情况
getHomeInfoBfrjfbqk
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoBfrjfbqk`
,
params
})
},
// 客户拜访方式占比情况
getHomeInfoKhbffszbqk
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoKhbffszbqk`
,
params
})
},
// 客户部门占比情况
getHomeInfoKhbmzbqk
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoKhbmzbqk`
,
params
})
},
// 客户拜访类型占比情况
getHomeInfoKhbflxzbqk
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoKhbflxzbqk`
,
params
})
},
// 拜访产品类型占比情况
getHomeInfoBfcplxzbqk
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/home/getHomeInfoBfcplxzbqk`
,
params
})
},
}
yudao-ui/yudao-ui-admin-vue3/src/views/Home/Index copy.vue
0 → 100644
View file @
6a3f72ad
This diff is collapsed.
Click to expand it.
yudao-ui/yudao-ui-admin-vue3/src/views/Home/Index.vue
View file @
6a3f72ad
This diff is collapsed.
Click to expand it.
yudao-ui/yudao-ui-admin-vue3/src/views/Home/YearRangeSelector.vue
0 → 100644
View file @
6a3f72ad
<
template
>
<div
class=
"year-range-picker"
>
<el-input
v-model=
"displayValue"
readonly
clearable
@
click=
"toggleYearPanel"
@
clear=
"clearRange"
placeholder=
"选择年份范围"
class=
"year-input"
>
<template
#suffix
>
<i
class=
"el-icon-arrow-down"
:class=
"
{ 'is-reverse': panelVisible }">
</i>
</
template
>
</el-input>
<transition
name=
"fade"
>
<div
class=
"year-panel"
v-show=
"panelVisible"
>
<div
class=
"panel-header"
>
<div
class=
"year-type"
>
<el-radio-group
v-model=
"selectedType"
>
<el-radio-button
label=
"start"
>
开始年份
</el-radio-button>
<el-radio-button
label=
"end"
>
结束年份
</el-radio-button>
</el-radio-group>
</div>
<div
class=
"panel-actions"
>
<el-button
size=
"mini"
@
click=
"closePanel"
>
取消
</el-button>
<el-button
size=
"mini"
type=
"primary"
@
click=
"confirmRange"
:disabled=
"!isValidRange"
>
确定
</el-button
>
</div>
</div>
<div
class=
"year-grid"
>
<div
v-for=
"year in yearList"
:key=
"year"
:class=
"{
'year-item': true,
'is-selected': isSelectedYear(year),
'is-disabled': isDisabledYear(year)
}"
@
click=
"selectYear(year)"
>
{{ year }}年
</div>
</div>
<div
class=
"quick-options"
>
<el-button
size=
"small"
@
click=
"selectLastYear"
>
近1年
</el-button>
<el-button
size=
"small"
@
click=
"selectLast3Years"
>
近3年
</el-button>
<el-button
size=
"small"
@
click=
"selectLast5Years"
>
近5年
</el-button>
</div>
</div>
</transition>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
ref
,
computed
,
watch
,
defineProps
,
defineEmits
,
nextTick
}
from
'
vue
'
import
{
ElInput
,
ElRadioGroup
,
ElRadioButton
,
ElButton
}
from
'
element-plus
'
const
props
=
defineProps
<
{
modelValue
:
[
string
,
string
]
|
[]
showQuickOptions
:
boolean
yearRange
:
[
number
,
number
]
// 可选年份范围 [开始年份, 结束年份]
}
>
()
const
emits
=
defineEmits
<
{
(
event
:
'
update:modelValue
'
,
value
:
[
string
,
string
]):
void
(
event
:
'
change
'
,
value
:
[
string
,
string
]):
void
}
>
()
// 内部状态
const
panelVisible
=
ref
(
false
)
const
selectedType
=
ref
(
'
start
'
)
// 当前选择的是开始还是结束年份
const
startYear
=
ref
<
string
|
null
>
(
props
.
modelValue
[
0
]
||
null
)
const
endYear
=
ref
<
string
|
null
>
(
props
.
modelValue
[
1
]
||
null
)
// 生成年份列表(默认可选范围:当前年份前后10年)
const
yearRange
=
computed
(()
=>
{
const
[
min
,
max
]
=
props
.
yearRange
||
[]
const
currentYear
=
new
Date
().
getFullYear
()
const
defaultMin
=
currentYear
-
10
const
defaultMax
=
currentYear
+
10
return
[
min
||
defaultMin
,
max
||
defaultMax
]
})
const
yearList
=
computed
(()
=>
{
const
[
min
,
max
]
=
yearRange
.
value
return
Array
.
from
({
length
:
max
-
min
+
1
},
(
_
,
i
)
=>
min
+
i
)
})
// 显示值
const
displayValue
=
computed
(()
=>
{
if
(
startYear
.
value
&&
endYear
.
value
)
{
return
`
${
startYear
.
value
}
年 -
${
endYear
.
value
}
年`
}
else
if
(
startYear
.
value
)
{
return
`
${
startYear
.
value
}
年 - 未选择结束年份`
}
else
if
(
endYear
.
value
)
{
return
`未选择开始年份 -
${
endYear
.
value
}
年`
}
return
''
})
// 判断年份是否已选择
const
isSelectedYear
=
(
year
:
number
)
=>
{
if
(
selectedType
.
value
===
'
start
'
)
{
return
year
.
toString
()
===
startYear
.
value
}
else
{
return
year
.
toString
()
===
endYear
.
value
}
}
// 判断年份是否禁用
const
isDisabledYear
=
(
year
:
number
)
=>
{
if
(
selectedType
.
value
===
'
start
'
&&
endYear
.
value
)
{
return
year
>
parseInt
(
endYear
.
value
)
}
else
if
(
selectedType
.
value
===
'
end
'
&&
startYear
.
value
)
{
return
year
<
parseInt
(
startYear
.
value
)
}
return
false
}
// 判断范围是否有效
const
isValidRange
=
computed
(()
=>
{
return
!!
(
startYear
.
value
&&
endYear
.
value
&&
startYear
.
value
<=
endYear
.
value
)
})
// 监听外部值变化
watch
(
()
=>
props
.
modelValue
,
(
newVal
)
=>
{
startYear
.
value
=
newVal
[
0
]
||
null
endYear
.
value
=
newVal
[
1
]
||
null
}
)
// 监听内部值变化,同步到外部
watch
([
startYear
,
endYear
],
([
newStart
,
newEnd
])
=>
{
if
(
newStart
&&
newEnd
&&
newStart
<=
newEnd
)
{
emits
(
'
update:modelValue
'
,
[
newStart
,
newEnd
])
}
})
// 切换年份面板显示
const
toggleYearPanel
=
()
=>
{
panelVisible
.
value
=
!
panelVisible
.
value
}
// 关闭面板
const
closePanel
=
()
=>
{
panelVisible
.
value
=
false
}
// 确认选择
const
confirmRange
=
()
=>
{
if
(
isValidRange
.
value
)
{
emits
(
'
update:modelValue
'
,
[
startYear
.
value
!
,
endYear
.
value
!
])
emits
(
'
change
'
,
[
startYear
.
value
!
,
endYear
.
value
!
])
panelVisible
.
value
=
false
}
}
// 选择年份
const
selectYear
=
(
year
:
number
)
=>
{
if
(
selectedType
.
value
===
'
start
'
)
{
startYear
.
value
=
year
.
toString
()
// 如果开始年份大于结束年份,清空结束年份
if
(
endYear
.
value
&&
startYear
.
value
>
endYear
.
value
)
{
endYear
.
value
=
null
}
}
else
{
endYear
.
value
=
year
.
toString
()
}
}
// 清空选择
const
clearRange
=
()
=>
{
startYear
.
value
=
null
endYear
.
value
=
null
emits
(
'
update:modelValue
'
,
[])
}
// 快捷选择:近1年
const
selectLastYear
=
()
=>
{
const
currentYear
=
new
Date
().
getFullYear
().
toString
()
const
lastYear
=
(
new
Date
().
getFullYear
()
-
1
).
toString
()
startYear
.
value
=
lastYear
endYear
.
value
=
currentYear
confirmRange
()
}
// 快捷选择:近3年
const
selectLast3Years
=
()
=>
{
const
currentYear
=
new
Date
().
getFullYear
().
toString
()
const
threeYearsAgo
=
(
new
Date
().
getFullYear
()
-
3
).
toString
()
startYear
.
value
=
threeYearsAgo
endYear
.
value
=
currentYear
confirmRange
()
}
// 快捷选择:近5年
const
selectLast5Years
=
()
=>
{
const
currentYear
=
new
Date
().
getFullYear
().
toString
()
const
fiveYearsAgo
=
(
new
Date
().
getFullYear
()
-
5
).
toString
()
startYear
.
value
=
fiveYearsAgo
endYear
.
value
=
currentYear
confirmRange
()
}
// 点击外部关闭面板
const
clickOutside
=
(
e
:
MouseEvent
)
=>
{
const
target
=
e
.
target
as
HTMLElement
const
pickerEl
=
document
.
querySelector
(
'
.year-range-picker
'
)
as
HTMLElement
if
(
pickerEl
&&
!
pickerEl
.
contains
(
target
))
{
panelVisible
.
value
=
false
}
}
// 添加点击外部事件监听
onMounted
(()
=>
{
document
.
addEventListener
(
'
click
'
,
clickOutside
)
})
// 移除事件监听
onUnmounted
(()
=>
{
document
.
removeEventListener
(
'
click
'
,
clickOutside
)
})
</
script
>
<
style
scoped
lang=
"less"
>
.year-range-picker {
position: relative;
.year-input {
cursor: pointer;
}
.year-panel {
position: absolute;
top: 40px;
left: 0;
width: 300px;
background: #000;
border: 1px solid #dcdcdc;
border-radius: 4px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
z-index: 1000;
padding: 10px;
.panel-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
.year-type {
.el-radio-button__inner {
padding: 6px 15px;
}
}
.panel-actions {
display: flex;
gap: 10px;
}
}
.year-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin-bottom: 15px;
.year-item {
padding: 8px;
text-align: center;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
&:hover:not(.is-disabled) {
background-color: #f5f7fa;
}
&.is-selected {
background-color: #409eff;
color: #fff;
}
&.is-disabled {
color: #c0c4cc;
cursor: not-allowed;
}
}
}
.quick-options {
display: flex;
gap: 10px;
margin-top: 10px;
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
}
</
style
>
\ No newline at end of file
yudao-ui/yudao-ui-admin-vue3/src/views/Home/card.vue
0 → 100644
View file @
6a3f72ad
<
template
>
<div
class=
"stats-container"
>
<div
class=
"stat-card"
>
<div
class=
"stat-item single"
>
<span
class=
"stat-number"
>
{{
props
.
homeInfoFirst
?.
bfkhsl
||
0
}}
</span>
<span
class=
"stat-label"
>
拜访客户数量
</span>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-item"
>
<span
class=
"stat-number"
>
{{
props
.
homeInfoFirst
?.
sykhsl
||
0
}}
</span>
<span
class=
"stat-label"
>
商业客户
</span>
</div>
<div
class=
"stat-item"
>
<span
class=
"stat-number"
>
{{
props
.
homeInfoFirst
?.
ylkhsl
||
0
}}
</span>
<span
class=
"stat-label"
>
医疗客户数量
</span>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-item single"
>
<span
class=
"stat-number"
>
{{
props
.
homeInfoFirst
?.
visitCount
||
0
}}
</span>
<span
class=
"stat-label"
>
客户拜访总次数
</span>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-item"
>
<span
class=
"stat-number"
>
{{
props
.
homeInfoFirst
?.
sykfcs
||
0
}}
</span>
<span
class=
"stat-label"
>
商业拜访次数
</span>
</div>
<div
class=
"stat-item"
>
<span
class=
"stat-number"
>
{{
props
.
homeInfoFirst
?.
ylkfcs
||
0
}}
</span>
<span
class=
"stat-label"
>
日常拜访次数
</span>
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-item single"
>
<span
class=
"stat-number"
>
{{
props
.
homeInfoFirst
?.
ywysl
||
0
}}
</span>
<span
class=
"stat-label"
>
业务员数量
</span>
</div>
</div>
<!--
<div
class=
"stat-card"
>
<div
class=
"stat-item single"
>
<span
class=
"stat-number"
>
{{
statsData
.
card2
.
number
}}
</span>
<span
class=
"stat-label"
>
{{
statsData
.
card2
.
label
}}
</span>
</div>
</div>
-->
</div>
</
template
>
<
script
setup
lang=
"ts"
>
const
props
=
defineProps
({
homeInfoFirst
:
{
type
:
[
Object
,
String
],
// eslint-disable-next-line vue/require-valid-default-prop
default
:
{}
}
})
onMounted
(()
=>
{})
</
script
>
<
style
scoped
lang=
"css"
>
.stats-container
{
display
:
flex
;
justify-content
:
space-around
;
padding
:
20px
100px
;
gap
:
20px
;
/* 卡片之间的间距 */
flex-wrap
:
wrap
;
/* 适配小屏幕,自动换行 */
}
.stat-card
{
border
:
1px
solid
#ccc
;
border-radius
:
4px
;
padding
:
16px
;
min-width
:
200px
;
background-color
:
#fff
;
box-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.1
);
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
.stat-item
{
text-align
:
center
;
margin
:
0
10px
;
}
.stat-item.single
{
margin
:
0
;
}
.stat-number
{
font-size
:
24px
;
font-weight
:
bold
;
display
:
block
;
color
:
#000
;
}
.stat-label
{
font-size
:
14px
;
color
:
#666
;
}
</
style
>
\ No newline at end of file
yudao-ui/yudao-ui-admin-vue3/src/views/Home/components/BarChart.vue
0 → 100644
View file @
6a3f72ad
<
template
>
<div
class=
"bar-chart"
>
<h3
class=
"chart-title"
>
{{
title
}}
</h3>
<chartsCard
:option=
"chartOption"
:width=
"width"
:height=
"height"
@
chart-ready=
"handleChartReady"
/>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
}
from
'
vue
'
import
chartsCard
from
'
./chartsCard.vue
'
const
props
=
defineProps
({
title
:
{
type
:
String
,
default
:
'
柱状图
'
},
xData
:
{
type
:
Array
,
required
:
true
// X轴数据(通常是类别)
},
yData
:
{
type
:
Array
,
required
:
true
// Y轴数据(数值)
},
seriesName
:
{
type
:
String
,
default
:
'
数据
'
// 系列名称
},
width
:
{
type
:
String
,
default
:
'
100%
'
},
height
:
{
type
:
String
,
default
:
'
400px
'
},
color
:
{
type
:
String
,
default
:
'
#5793f3
'
// 柱子颜色
},
barWidth
:
{
type
:
[
String
,
Number
],
default
:
'
40%
'
// 柱子宽度
}
})
const
chartOption
=
computed
(()
=>
({
tooltip
:
{
trigger
:
'
axis
'
,
backgroundColor
:
'
rgba(255, 255, 255, 0.9)
'
,
borderColor
:
'
#ddd
'
,
borderWidth
:
1
,
textStyle
:
{
color
:
'
#333
'
},
formatter
:
(
params
:
any
)
=>
{
const
p
=
params
[
0
]
return
`
${
p
.
name
}
<br/>
${
p
.
seriesName
}
:
${
p
.
value
}
`
}
},
grid
:
{
left
:
'
3%
'
,
right
:
'
4%
'
,
bottom
:
'
3%
'
,
containLabel
:
true
},
xAxis
:
{
type
:
'
category
'
,
data
:
props
.
xData
,
axisTick
:
{
show
:
false
},
axisLine
:
{
lineStyle
:
{
color
:
'
#999
'
}
},
axisLabel
:
{
color
:
'
#666
'
,
interval
:
0
,
// 强制显示所有标签
rotate
:
45
// 标签旋转角度
}
},
yAxis
:
{
type
:
'
value
'
,
axisTick
:
{
show
:
false
},
axisLine
:
{
show
:
false
},
axisLabel
:
{
color
:
'
#666
'
},
splitLine
:
{
lineStyle
:
{
color
:
'
#eee
'
}
}
},
series
:
[
{
name
:
props
.
seriesName
,
type
:
'
bar
'
,
data
:
props
.
yData
,
barWidth
:
props
.
barWidth
,
itemStyle
:
{
color
:
props
.
color
,
borderRadius
:
[
4
,
4
,
0
,
0
]
// 柱子圆角
},
emphasis
:
{
itemStyle
:
{
color
:
`
${
props
.
color
}
DD`
// 高亮颜色
}
}
}
]
}))
const
handleChartReady
=
(
chart
:
any
)
=>
{
// 可添加交互逻辑
}
</
script
>
<
style
scoped
lang=
"less"
>
.bar-chart {
.chart-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
}
</
style
>
\ No newline at end of file
yudao-ui/yudao-ui-admin-vue3/src/views/Home/components/LineChart.vue
0 → 100644
View file @
6a3f72ad
<
template
>
<div
class=
"line-chart"
>
<h3
class=
"chart-title"
>
{{
title
}}
</h3>
<chartsCard
:option=
"chartOption"
:width=
"width"
:height=
"height"
@
chart-ready=
"handleChartReady"
/>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
}
from
'
vue
'
import
chartsCard
from
'
./chartsCard.vue
'
const
props
=
defineProps
({
title
:
{
type
:
String
,
default
:
'
折线图
'
},
xData
:
{
type
:
Array
,
required
:
true
// X轴数据(通常是时间或类别)
},
yData
:
{
type
:
Array
,
required
:
true
// Y轴数据(数值)
},
seriesName
:
{
type
:
String
,
default
:
'
数据
'
// 系列名称
},
width
:
{
type
:
String
,
default
:
'
100%
'
},
height
:
{
type
:
String
,
default
:
'
400px
'
},
color
:
{
type
:
String
,
default
:
'
#5793f3
'
// 线条颜色
},
isSmooth
:
{
type
:
Boolean
,
default
:
true
// 是否平滑曲线
}
})
const
chartOption
=
computed
(()
=>
({
tooltip
:
{
trigger
:
'
axis
'
,
backgroundColor
:
'
rgba(255, 255, 255, 0.9)
'
,
borderColor
:
'
#ddd
'
,
borderWidth
:
1
,
textStyle
:
{
color
:
'
#333
'
},
formatter
:
(
params
:
any
)
=>
{
const
p
=
params
[
0
]
return
`
${
p
.
name
}
<br/>
${
p
.
seriesName
}
:
${
p
.
value
}
`
}
},
grid
:
{
left
:
'
3%
'
,
right
:
'
4%
'
,
bottom
:
'
3%
'
,
containLabel
:
true
},
xAxis
:
{
type
:
'
category
'
,
data
:
props
.
xData
,
axisTick
:
{
show
:
false
},
axisLine
:
{
lineStyle
:
{
color
:
'
#999
'
}
},
axisLabel
:
{
color
:
'
#666
'
}
},
yAxis
:
{
type
:
'
value
'
,
axisTick
:
{
show
:
false
},
axisLine
:
{
show
:
false
},
axisLabel
:
{
color
:
'
#666
'
},
splitLine
:
{
lineStyle
:
{
color
:
'
#eee
'
}
}
},
series
:
[
{
name
:
props
.
seriesName
,
type
:
'
line
'
,
data
:
props
.
yData
,
smooth
:
props
.
isSmooth
,
lineStyle
:
{
width
:
2
,
color
:
props
.
color
},
itemStyle
:
{
color
:
props
.
color
,
borderWidth
:
2
},
symbol
:
'
circle
'
,
// 标记点形状
symbolSize
:
6
,
// 标记点大小
showSymbol
:
false
,
// 默认不显示标记点,鼠标悬停时显示
emphasis
:
{
symbol
:
'
circle
'
,
symbolSize
:
8
},
areaStyle
:
{
color
:
{
type
:
'
linear
'
,
x
:
0
,
y
:
0
,
x2
:
0
,
y2
:
1
,
colorStops
:
[
{
offset
:
0
,
color
:
`
${
props
.
color
}
80`
},
// 半透明
{
offset
:
1
,
color
:
`
${
props
.
color
}
10`
}
// 几乎透明
]
}
}
}
]
}))
const
handleChartReady
=
(
chart
:
any
)
=>
{
// 可添加交互逻辑
}
</
script
>
<
style
scoped
lang=
"less"
>
.line-chart {
.chart-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
}
</
style
>
\ No newline at end of file
yudao-ui/yudao-ui-admin-vue3/src/views/Home/components/PieChart.vue
0 → 100644
View file @
6a3f72ad
<
template
>
<div
class=
"visit-way-pie-chart"
>
<h3
class=
"chart-title"
>
客户拜访方式占比情况
</h3>
<chartsCard
:option=
"chartOption"
:width=
"width"
:height=
"height"
@
chart-ready=
"handleChartReady"
/>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
}
from
'
vue
'
import
chartsCard
from
'
./chartsCard.vue
'
const
props
=
defineProps
({
data
:
{
type
:
Array
,
required
:
true
// 数据格式示例:
// [
// { name: '电话', value: 765, color: '#5793f3' },
// { name: '上门', value: 560, color: '#a9d672' },
// { name: '社交网络', value: 400, color: '#fac858' },
// { name: '推广渠道', value: 200, color: '#f96c6a' },
// { name: '其他', value: 120, color: '#c2a6f2' },
// ]
},
total
:
{
type
:
Number
,
required
:
true
// 总拜访量
},
width
:
{
type
:
String
,
default
:
'
400px
'
},
height
:
{
type
:
String
,
default
:
'
400px
'
},
tooltipTip
:
{
type
:
String
,
default
:
'
拜访方式支持自定义类型添加
'
// 提示框内容
}
})
const
chartOption
=
computed
(()
=>
{
// console.log(props.data,"props.data");
const
legendData
=
props
.
data
.
map
((
item
)
=>
item
.
name
)
return
{
tooltip
:
{
trigger
:
'
item
'
,
formatter
:
'
{a} <br/>{b} : {c} ({d}%)
'
,
// 自定义提示框浮层样式
extraCssText
:
'
background: #fff9cc; border: 1px solid #ffe57f; padding: 8px;
'
,
// 提示框位置
position
:
(
point
)
=>
{
const
x
=
point
[
0
]
>
300
?
point
[
0
]
-
100
:
point
[
0
]
const
y
=
point
[
1
]
<
100
?
point
[
1
]
+
20
:
point
[
1
]
return
[
x
,
y
]
},
// 提示框内容追加说明
formatter
:
(
params
)
=>
{
const
baseInfo
=
`
${
params
.
seriesName
}
<br/>
${
params
.
marker
}
${
params
.
name
}
:
${
params
.
value
}
(
${
params
.
percent
}
%)`
return
`
${
baseInfo
}
<br/><span style="color: #999; font-size: 12px;">
${
props
.
tooltipTip
}
</span>`
}
},
legend
:
{
orient
:
'
vertical
'
,
right
:
10
,
top
:
'
center
'
,
align
:
'
left
'
,
icon
:
'
circle
'
,
// 图例标记为圆形
itemWidth
:
12
,
itemHeight
:
12
,
itemGap
:
15
,
textStyle
:
{
color
:
'
#333
'
,
fontSize
:
14
},
data
:
legendData
},
series
:
[
{
name
:
'
客户拜访方式占比情况
'
,
type
:
'
pie
'
,
radius
:
[
'
40%
'
,
'
70%
'
],
// 环形图内外半径
center
:
[
'
35%
'
,
'
50%
'
],
// 图表中心位置,留出右侧图例空间
label
:
{
show
:
false
// 隐藏饼图扇区默认标签
},
labelLine
:
{
show
:
false
// 隐藏标签连线
},
data
:
props
.
data
.
map
((
item
)
=>
({
name
:
item
.
name
,
value
:
item
.
value
,
itemStyle
:
{
color
:
item
.
color
}
})),
// 中心文字配置
emphasis
:
{
label
:
{
show
:
false
}
},
// 中心显示总拜访量
renderItem
:
(
params
,
api
)
=>
{
if
(
params
.
name
===
''
)
{
return
{
type
:
'
text
'
,
position
:
api
.
coord
([
api
.
value
(
0
),
api
.
value
(
1
)]),
content
:
`总拜访量\n
${
props
.
total
}
`
,
style
:
{
textAlign
:
'
center
'
,
fill
:
'
#333
'
,
fontSize
:
16
,
fontWeight
:
'
bold
'
,
lineHeight
:
1.4
}
}
}
return
null
}
}
]
}
})
const
handleChartReady
=
(
chart
:
echarts
.
ECharts
)
=>
{
// 可在此处对图表进行更多自定义操作,如监听事件等
}
</
script
>
<
style
scoped
lang=
"less"
>
.visit-way-pie-chart {
.chart-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
}
</
style
>
\ No newline at end of file
yudao-ui/yudao-ui-admin-vue3/src/views/Home/components/barChart_1.vue
0 → 100644
View file @
6a3f72ad
<
template
>
<div
class=
"horizontal-bar-chart"
>
<h3
class=
"chart-title"
>
{{
title
}}
</h3>
<chartsCard
:option=
"chartOption"
:width=
"width"
:height=
"height"
@
chart-ready=
"handleChartReady"
/>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
}
from
'
vue
'
import
chartsCard
from
'
./chartsCard.vue
'
const
props
=
defineProps
({
title
:
{
type
:
String
,
default
:
'
拜访人均分布情况
'
},
data
:
{
type
:
Array
,
required
:
true
// 数据格式示例:
// [
// { name: '业务员1', value: 78, color: '#fcd36e' },
// { name: '业务员2', value: 68, color: '#f28b8b' },
// { name: '业务员3', value: 50, color: '#a491f2' },
// { name: '业务员4', value: 42, color: '#fbbc58' },
// { name: '业务员5', value: 32, color: '#7ecb5c' },
// { name: '业务员6', value: 30, color: '#68c0cf' },
// { name: '业务员7', value: 28, color: '#66b1fc' },
// ]
},
width
:
{
type
:
String
,
default
:
'
100%
'
},
height
:
{
type
:
String
,
default
:
'
400px
'
}
})
const
colors
=
[
'
#fcd36e
'
,
'
#f28b8b
'
,
'
#a491f2
'
,
'
#fbbc58
'
,
'
#7ecb5c
'
,
'
#68c0cf
'
,
'
#66b1fc
'
]
const
chartOption
=
computed
(()
=>
({
tooltip
:
{
trigger
:
'
item
'
,
formatter
:
'
{b} : {c}
'
// 格式化提示内容,显示名称和数值
},
grid
:
{
left
:
'
60px
'
,
// 左侧留出空间显示业务员名称
right
:
'
40px
'
,
bottom
:
'
3%
'
,
containLabel
:
true
},
xAxis
:
{
type
:
'
value
'
,
// min: 0, // 最小值
// max: 100000, // 最大值
axisTick
:
{
show
:
false
},
axisLine
:
{
show
:
false
},
axisLabel
:
{
color
:
'
#666
'
},
splitLine
:
{
lineStyle
:
{
color
:
'
#eee
'
}
}
},
yAxis
:
{
type
:
'
category
'
,
data
:
props
.
data
.
map
((
item
)
=>
item
.
name
),
axisTick
:
{
show
:
false
},
axisLine
:
{
show
:
false
},
axisLabel
:
{
color
:
'
#333
'
,
fontSize
:
14
}
},
series
:
[
{
name
:
'
拜访人均分布
'
,
type
:
'
bar
'
,
barCategoryGap
:
'
10%
'
,
// 柱子之间的间距
barWidth
:
20
,
// 柱子宽度
data
:
props
.
data
.
map
((
item
)
=>
item
.
value
),
itemStyle
:
{
color
:
(
params
:
{
dataIndex
:
number
})
=>
{
const
item
=
props
.
data
[
params
.
dataIndex
]
return
item
.
color
||
colors
[
params
.
dataIndex
%
colors
.
length
]
},
borderRadius
:
[
0
,
6
,
6
,
0
]
// 仅右侧圆角,适配横向柱状图
},
label
:
{
show
:
true
,
position
:
'
right
'
,
// 数值显示在柱子右侧
color
:
'
#333
'
,
fontSize
:
12
}
}
]
}))
const
handleChartReady
=
(
chart
:
any
)
=>
{
// 可在此处对图表进行更多自定义操作,如监听事件等
}
</
script
>
<
style
scoped
lang=
"less"
>
.horizontal-bar-chart {
.chart-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
color: '#333';
}
}
</
style
>
\ No newline at end of file
yudao-ui/yudao-ui-admin-vue3/src/views/Home/components/chartsCard.vue
0 → 100644
View file @
6a3f72ad
<
template
>
<div
ref=
"chartRef"
class=
"echarts-base"
:style=
"
{ width, height }">
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
,
onUnmounted
,
watch
,
nextTick
}
from
'
vue
'
import
*
as
echarts
from
'
echarts
'
const
props
=
defineProps
({
option
:
{
type
:
Object
,
required
:
true
},
width
:
{
type
:
String
,
default
:
'
100%
'
},
height
:
{
type
:
String
,
default
:
'
400px
'
}
})
const
emits
=
defineEmits
([
'
chartReady
'
,
'
chartClick
'
])
const
chartRef
=
ref
<
HTMLElement
|
null
>
(
null
)
let
myChart
:
echarts
.
ECharts
|
null
=
null
const
initChart
=
()
=>
{
nextTick
(()
=>
{
if
(
chartRef
.
value
)
{
myChart
=
echarts
.
init
(
chartRef
.
value
)
myChart
.
setOption
(
props
.
option
)
emits
(
'
chartReady
'
,
myChart
)
myChart
.
on
(
'
click
'
,
(
params
)
=>
{
emits
(
'
chartClick
'
,
params
)
})
}
})
}
const
resizeChart
=
()
=>
{
myChart
?.
resize
()
}
onMounted
(()
=>
{
initChart
()
window
.
addEventListener
(
'
resize
'
,
resizeChart
)
})
onUnmounted
(()
=>
{
window
.
removeEventListener
(
'
resize
'
,
resizeChart
)
myChart
?.
dispose
()
myChart
=
null
})
watch
(
()
=>
props
.
option
,
()
=>
{
if
(
myChart
)
{
myChart
.
setOption
(
props
.
option
)
}
},
{
deep
:
true
}
)
</
script
>
<
style
scoped
>
.echarts-base
{
width
:
100%
;
height
:
100%
;
}
</
style
>
\ No newline at end of file
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