Skip to content

Commit 455f269

Browse files
committed
feat: 新增分析页
1 parent ad31d9f commit 455f269

File tree

11 files changed

+766
-10
lines changed

11 files changed

+766
-10
lines changed

src/apis/common/home.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,23 @@ export function listDashboardAccessTrend(days: number) {
1414
export function listDashboardNotice() {
1515
return http.get<T.DashboardNoticeResp[]>(`${BASE_URL}/notice`)
1616
}
17+
18+
/** @desc 查询访问时段分析 */
19+
export function getAnalysisTimeslot() {
20+
return http.get<T.DashboardChartCommonResp[]>(`${BASE_URL}/analysis/timeslot`)
21+
}
22+
23+
/** @desc 查询模块分析 */
24+
export function getAnalysisModule() {
25+
return http.get<T.DashboardChartCommonResp[]>(`${BASE_URL}/analysis/module`)
26+
}
27+
28+
/** @desc 查询终端分析 */
29+
export function getAnalysisOs() {
30+
return http.get<T.DashboardChartCommonResp[]>(`${BASE_URL}/analysis/os`)
31+
}
32+
33+
/** @desc 查询浏览器分析 */
34+
export function getAnalysisBrowser() {
35+
return http.get<T.DashboardChartCommonResp[]>(`${BASE_URL}/analysis/browser`)
36+
}

src/apis/common/type.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ export interface DashboardAccessTrendResp {
1212
ipCount: number
1313
}
1414

15+
/** 仪表盘图表类型 */
16+
export interface DashboardChartCommonResp {
17+
name: string
18+
value: number
19+
}
20+
1521
/** 仪表盘公告类型 */
1622
export interface DashboardNoticeResp {
1723
id: number

src/components/Chart/index.vue

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<template>
2+
<VCharts
3+
v-if="renderChart"
4+
:option="option"
5+
:autoresize="autoResize"
6+
:style="{ width, height }"
7+
/>
8+
</template>
9+
10+
<script lang="ts" setup>
11+
import { nextTick, ref } from 'vue'
12+
import VCharts from 'vue-echarts'
13+
14+
defineProps({
15+
option: {
16+
type: Object,
17+
default() {
18+
return {}
19+
}
20+
},
21+
autoResize: {
22+
type: Boolean,
23+
default: true
24+
},
25+
width: {
26+
type: String,
27+
default: '100%'
28+
},
29+
height: {
30+
type: String,
31+
default: '100%'
32+
}
33+
})
34+
const renderChart = ref(false)
35+
// wait container expand
36+
nextTick(() => {
37+
renderChart.value = true
38+
})
39+
</script>
40+
41+
<style scoped lang="less"></style>

src/styles/global.scss

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
padding: $margin;
189189
box-sizing: border-box;
190190
overflow-y: auto;
191+
overflow-x: hidden;
191192
}
192193

193194
// 表格页面
@@ -311,18 +312,11 @@
311312

312313
// 通用卡片
313314
.general-card {
314-
height: 100%;
315-
overflow-y: auto;
315+
border: none;
316316
& > .arco-card-header {
317317
height: auto;
318318
padding: $padding;
319319
border: none;
320-
.arco-card-header-title {
321-
color: var(--color-text-1);
322-
font-size: 18px;
323-
font-weight: 500;
324-
line-height: 1.5;
325-
}
326320
}
327321
& > .arco-card-body {
328322
padding: 0 $padding $padding $padding;

src/types/components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export {}
88
declare module 'vue' {
99
export interface GlobalComponents {
1010
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
11+
Chart: typeof import('./../components/Chart/index.vue')['default']
1112
CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default']
1213
CronModel: typeof import('./../components/GenCron/CronModel/index.vue')['default']
1314
DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default']
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<template>
2+
<a-spin :loading="loading" style="width: 100%">
3+
<a-card title="访问时段分析" class="general-card" :header-style="{ paddingBottom: '16px' }">
4+
<Chart style="width: 100%; height: 370px" :option="option" />
5+
</a-card>
6+
</a-spin>
7+
</template>
8+
9+
<script lang="ts" setup>
10+
import { graphic } from 'echarts'
11+
import { useChart } from '@/hooks'
12+
import { type DashboardChartCommonResp, getAnalysisTimeslot as getData } from '@/apis/common'
13+
14+
// 提示框
15+
const tooltipItemsHtmlString = (items) => {
16+
return items
17+
.map(
18+
(el) => `<div class="content-panel">
19+
<p>
20+
<span style="background-color: ${el.color}" class="tooltip-item-icon"></span>
21+
<span>${el.seriesName}</span>
22+
</p>
23+
<span class="tooltip-value">
24+
${el.value}
25+
</span>
26+
</div>`
27+
)
28+
.join('')
29+
}
30+
31+
const xAxis = ref<string[]>([])
32+
const dataList = ref<number[]>([])
33+
const { option } = useChart((isDark) => {
34+
return {
35+
grid: {
36+
left: '40',
37+
right: 0,
38+
top: '20',
39+
bottom: '100'
40+
},
41+
xAxis: {
42+
type: 'category',
43+
offset: 2,
44+
data: xAxis.value,
45+
boundaryGap: false,
46+
axisLabel: {
47+
color: '#4E5969',
48+
formatter(value: number, idx: number) {
49+
if (idx === 0) return ''
50+
if (idx === xAxis.value.length - 1) return ''
51+
return `${value}`
52+
}
53+
},
54+
axisLine: {
55+
lineStyle: {
56+
color: isDark ? '#3f3f3f' : '#A9AEB8'
57+
}
58+
},
59+
axisTick: {
60+
show: true,
61+
alignWithLabel: true,
62+
lineStyle: {
63+
color: '#86909C'
64+
},
65+
interval(idx: number) {
66+
if (idx === 0) return false
67+
if (idx === xAxis.value.length - 1) return false
68+
return true
69+
}
70+
},
71+
splitLine: {
72+
show: true,
73+
interval: (idx: number) => {
74+
if (idx === 0) return false
75+
return idx !== xAxis.value.length - 1
76+
},
77+
lineStyle: {
78+
color: isDark ? '#3F3F3F' : '#E5E8EF'
79+
}
80+
},
81+
axisPointer: {
82+
show: true,
83+
lineStyle: {
84+
color: '#23ADFF',
85+
width: 2
86+
}
87+
}
88+
},
89+
yAxis: {
90+
type: 'value',
91+
axisLabel: {
92+
formatter(value: any, idx: number) {
93+
if (idx === 0) return value
94+
if (value >= 1000) {
95+
return `${value / 1000}k`
96+
}
97+
return `${value}`
98+
}
99+
},
100+
axisLine: {
101+
show: false
102+
},
103+
splitLine: {
104+
lineStyle: {
105+
type: 'dashed',
106+
color: isDark ? '#3F3F3F' : '#E5E8EF'
107+
}
108+
}
109+
},
110+
tooltip: {
111+
show: true,
112+
trigger: 'axis',
113+
formatter(params) {
114+
const [firstElement] = params
115+
return `<div>
116+
<p class="tooltip-title">${firstElement.axisValueLabel}</p>
117+
${tooltipItemsHtmlString(params)}
118+
</div>`
119+
},
120+
className: 'echarts-tooltip-diy'
121+
},
122+
series: [
123+
{
124+
name: '浏览量(PV)',
125+
data: dataList.value,
126+
type: 'line',
127+
smooth: true,
128+
showSymbol: false,
129+
color: '#246EFF',
130+
emphasis: {
131+
focus: 'series',
132+
itemStyle: {
133+
borderWidth: 2,
134+
borderColor: '#E0E3FF'
135+
}
136+
},
137+
areaStyle: {
138+
opacity: 0.8,
139+
color: new graphic.LinearGradient(0, 0, 0, 1, [
140+
{
141+
offset: 0,
142+
color: 'rgba(17, 126, 255, 0.16)'
143+
},
144+
{
145+
offset: 1,
146+
color: 'rgba(17, 128, 255, 0)'
147+
}
148+
])
149+
}
150+
}
151+
],
152+
dataZoom: [
153+
{
154+
bottom: 40,
155+
type: 'slider',
156+
left: 40,
157+
right: 14,
158+
height: 14,
159+
borderColor: 'transparent',
160+
handleIcon:
161+
'image://http://p3-armor.byteimg.com/tos-cn-i-49unhts6dw/1ee5a8c6142b2bcf47d2a9f084096447.svg~tplv-49unhts6dw-image.image',
162+
handleSize: '20',
163+
handleStyle: {
164+
shadowColor: 'rgba(0, 0, 0, 0.2)',
165+
shadowBlur: 4
166+
},
167+
brushSelect: false,
168+
backgroundColor: isDark ? '#313132' : '#F2F3F5'
169+
},
170+
{
171+
type: 'inside',
172+
start: 0,
173+
end: 100,
174+
zoomOnMouseWheel: false
175+
}
176+
]
177+
}
178+
})
179+
180+
const loading = ref(false)
181+
// 查询图表数据
182+
const getChartData = async () => {
183+
try {
184+
loading.value = true
185+
const { data } = await getData()
186+
data.forEach((item: DashboardChartCommonResp) => {
187+
xAxis.value.push(item.name)
188+
dataList.value.push(item.value)
189+
})
190+
} finally {
191+
loading.value = false
192+
}
193+
}
194+
195+
onMounted(() => {
196+
getChartData()
197+
})
198+
</script>
199+
200+
<style lang="scss" scoped>
201+
:deep(.arco-card-body) {
202+
padding-bottom: 0;
203+
}
204+
</style>

0 commit comments

Comments
 (0)