mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-13 02:57:11 +08:00
feat: 分析页增加数据总览和热门模块列表,调整分析页布局
This commit is contained in:
@@ -5,16 +5,26 @@ export type * from './type'
|
|||||||
|
|
||||||
const BASE_URL = '/dashboard'
|
const BASE_URL = '/dashboard'
|
||||||
|
|
||||||
/** @desc 查询访问趋势 */
|
|
||||||
export function listDashboardAccessTrend(days: number) {
|
|
||||||
return http.get<T.DashboardAccessTrendResp[]>(`${BASE_URL}/access/trend/${days}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 查询公告列表 */
|
/** @desc 查询公告列表 */
|
||||||
export function listDashboardNotice() {
|
export function listDashboardNotice() {
|
||||||
return http.get<T.DashboardNoticeResp[]>(`${BASE_URL}/notice`)
|
return http.get<T.DashboardNoticeResp[]>(`${BASE_URL}/notice`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @desc 查询 PV 总览 */
|
||||||
|
export function getDashboardOverviewPv() {
|
||||||
|
return http.get<T.DashboardOverviewCommonResp>(`${BASE_URL}/analysis/overview/pv`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 查询 IP 总览 */
|
||||||
|
export function getDashboardOverviewIp() {
|
||||||
|
return http.get<T.DashboardOverviewCommonResp>(`${BASE_URL}/analysis/overview/ip`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 查询访问趋势 */
|
||||||
|
export function getDashboardAccessTrend(days: number) {
|
||||||
|
return http.get<T.DashboardAccessTrendResp[]>(`${BASE_URL}/access/trend/${days}`)
|
||||||
|
}
|
||||||
|
|
||||||
/** @desc 查询访问时段分析 */
|
/** @desc 查询访问时段分析 */
|
||||||
export function getAnalysisTimeslot() {
|
export function getAnalysisTimeslot() {
|
||||||
return http.get<T.DashboardChartCommonResp[]>(`${BASE_URL}/analysis/timeslot`)
|
return http.get<T.DashboardChartCommonResp[]>(`${BASE_URL}/analysis/timeslot`)
|
@@ -1,3 +1,3 @@
|
|||||||
export * from './common'
|
export * from './common'
|
||||||
export * from './captcha'
|
export * from './captcha'
|
||||||
export * from './home'
|
export * from './dashboard'
|
||||||
|
@@ -5,6 +5,13 @@ export interface ImageCaptchaResp {
|
|||||||
expireTime: number
|
expireTime: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 仪表盘公告类型 */
|
||||||
|
export interface DashboardNoticeResp {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
type: number
|
||||||
|
}
|
||||||
|
|
||||||
/** 仪表盘访问趋势类型 */
|
/** 仪表盘访问趋势类型 */
|
||||||
export interface DashboardAccessTrendResp {
|
export interface DashboardAccessTrendResp {
|
||||||
date: string
|
date: string
|
||||||
@@ -12,19 +19,20 @@ export interface DashboardAccessTrendResp {
|
|||||||
ipCount: number
|
ipCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 仪表盘图表类型 */
|
/** 仪表盘通用总览类型 */
|
||||||
|
export interface DashboardOverviewCommonResp {
|
||||||
|
total: number
|
||||||
|
today: number
|
||||||
|
growth: number
|
||||||
|
dataList: DashboardChartCommonResp[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 仪表盘通用图表类型 */
|
||||||
export interface DashboardChartCommonResp {
|
export interface DashboardChartCommonResp {
|
||||||
name: string
|
name: string
|
||||||
value: number
|
value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 仪表盘公告类型 */
|
|
||||||
export interface DashboardNoticeResp {
|
|
||||||
id: number
|
|
||||||
title: string
|
|
||||||
type: number
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 行为验证码类型 */
|
/* 行为验证码类型 */
|
||||||
export interface BehaviorCaptchaResp {
|
export interface BehaviorCaptchaResp {
|
||||||
originalImageBase64: string
|
originalImageBase64: string
|
||||||
|
@@ -18,9 +18,9 @@ export function useChart(sourceOption: optionsFn) {
|
|||||||
// echarts support https://echarts.apache.org/zh/theme-builder.html
|
// echarts support https://echarts.apache.org/zh/theme-builder.html
|
||||||
// 这里不使用
|
// 这里不使用
|
||||||
// TODO 图表主题
|
// TODO 图表主题
|
||||||
const option = computed<EChartsOption>(() => {
|
const chartOption = computed<EChartsOption>(() => {
|
||||||
return sourceOption(isDark.value)
|
return sourceOption(isDark.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
return { option }
|
return { chartOption }
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-spin :loading="loading" style="width: 100%">
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
<a-card title="访问时段分析" class="general-card" :header-style="{ paddingBottom: '16px' }">
|
<a-card class="general-card" title="访问时段分析">
|
||||||
<Chart style="width: 100%; height: 370px" :option="option" />
|
<Chart :option="chartOption" style="width: 100%; height: 370px" />
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { graphic } from 'echarts'
|
import { type EChartsOption, graphic } from 'echarts'
|
||||||
import { useChart } from '@/hooks'
|
import { useChart } from '@/hooks'
|
||||||
import { type DashboardChartCommonResp, getAnalysisTimeslot as getData } from '@/apis/common'
|
import { type DashboardChartCommonResp, getAnalysisTimeslot as getData } from '@/apis/common'
|
||||||
|
|
||||||
@@ -29,8 +29,8 @@ const tooltipItemsHtmlString = (items) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const xAxis = ref<string[]>([])
|
const xAxis = ref<string[]>([])
|
||||||
const dataList = ref<number[]>([])
|
const chartData = ref<number[]>([])
|
||||||
const { option } = useChart((isDark) => {
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
return {
|
return {
|
||||||
grid: {
|
grid: {
|
||||||
left: '40',
|
left: '40',
|
||||||
@@ -121,12 +121,14 @@ const { option } = useChart((isDark) => {
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '浏览量(PV)',
|
name: '访问次数',
|
||||||
data: dataList.value,
|
data: chartData.value,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
showSymbol: false,
|
showSymbol: false,
|
||||||
color: '#246EFF',
|
color: isDark ? '#3D72F6' : '#246EFF',
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 10,
|
||||||
emphasis: {
|
emphasis: {
|
||||||
focus: 'series',
|
focus: 'series',
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
@@ -185,7 +187,7 @@ const getChartData = async () => {
|
|||||||
const { data } = await getData()
|
const { data } = await getData()
|
||||||
data.forEach((item: DashboardChartCommonResp) => {
|
data.forEach((item: DashboardChartCommonResp) => {
|
||||||
xAxis.value.push(item.name)
|
xAxis.value.push(item.name)
|
||||||
dataList.value.push(item.value)
|
chartData.value.push(item.value)
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-spin :loading="loading" style="width: 100%">
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
<a-card title="访问趋势" class="general-card">
|
<a-card class="general-card" title="访问趋势">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-radio-group v-model:model-value="dateRange" type="button" size="small" @change="onChange as any">
|
<a-radio-group v-model:model-value="dateRange" type="button" size="small" @change="onChange as any">
|
||||||
<a-radio :value="7">近7天</a-radio>
|
<a-radio :value="7">近7天</a-radio>
|
||||||
<a-radio :value="30">近30天</a-radio>
|
<a-radio :value="30">近30天</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</template>
|
</template>
|
||||||
<Chart :option="option" :style="{ height: '326px' }" />
|
<Chart :option="chartOption" style="height: 460px" />
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { graphic } from 'echarts'
|
import { type EChartsOption, graphic } from 'echarts'
|
||||||
import { type DashboardAccessTrendResp, listDashboardAccessTrend } from '@/apis'
|
import { type DashboardAccessTrendResp, getDashboardAccessTrend as getData } from '@/apis'
|
||||||
import { useChart } from '@/hooks'
|
import { useChart } from '@/hooks'
|
||||||
|
|
||||||
// 提示框
|
// 提示框
|
||||||
@@ -34,14 +34,14 @@ const tooltipItemsHtmlString = (items) => {
|
|||||||
.join('')
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
const xData = ref<string[]>([])
|
const xAxis = ref<string[]>([])
|
||||||
const pvStatisticsData = ref<number[]>([])
|
const pvChartData = ref<number[]>([])
|
||||||
const ipStatisticsData = ref<number[]>([])
|
const ipChartData = ref<number[]>([])
|
||||||
const { option } = useChart((isDark) => {
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
return {
|
return {
|
||||||
grid: {
|
grid: {
|
||||||
left: '38',
|
left: '38',
|
||||||
right: '0',
|
right: '5',
|
||||||
top: '10',
|
top: '10',
|
||||||
bottom: '50'
|
bottom: '50'
|
||||||
},
|
},
|
||||||
@@ -55,13 +55,13 @@ const { option } = useChart((isDark) => {
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
offset: 2,
|
offset: 2,
|
||||||
data: xData.value,
|
data: xAxis.value,
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: '#4E5969',
|
color: '#4E5969',
|
||||||
formatter(value: number, idx: number) {
|
formatter(value: number, idx: number) {
|
||||||
if (idx === 0) return ''
|
if (idx === 0) return ''
|
||||||
if (idx === xData.value.length - 1) return ''
|
if (idx === xAxis.value.length - 1) return ''
|
||||||
return `${value}`
|
return `${value}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -75,7 +75,7 @@ const { option } = useChart((isDark) => {
|
|||||||
show: true,
|
show: true,
|
||||||
interval: (idx: number) => {
|
interval: (idx: number) => {
|
||||||
if (idx === 0) return false
|
if (idx === 0) return false
|
||||||
return idx !== xData.value.length - 1
|
return idx !== xAxis.value.length - 1
|
||||||
},
|
},
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: isDark ? '#3F3F3F' : '#E5E8EF'
|
color: isDark ? '#3F3F3F' : '#E5E8EF'
|
||||||
@@ -124,8 +124,8 @@ const { option } = useChart((isDark) => {
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '浏览量(PV)',
|
name: '访问次数',
|
||||||
data: pvStatisticsData.value,
|
data: pvChartData.value,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
showSymbol: false,
|
showSymbol: false,
|
||||||
@@ -154,8 +154,8 @@ const { option } = useChart((isDark) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'IP数',
|
name: '独立IP',
|
||||||
data: ipStatisticsData.value,
|
data: ipChartData.value,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
showSymbol: false,
|
showSymbol: false,
|
||||||
@@ -193,14 +193,14 @@ const dateRange = ref(30)
|
|||||||
const getChartData = async (days: number) => {
|
const getChartData = async (days: number) => {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
xData.value = []
|
xAxis.value = []
|
||||||
pvStatisticsData.value = []
|
pvChartData.value = []
|
||||||
ipStatisticsData.value = []
|
ipChartData.value = []
|
||||||
const { data: chartData } = await listDashboardAccessTrend(days)
|
const { data: chartData } = await getData(days)
|
||||||
chartData.forEach((el: DashboardAccessTrendResp) => {
|
chartData.forEach((item: DashboardAccessTrendResp) => {
|
||||||
xData.value.unshift(el.date)
|
xAxis.value.push(item.date)
|
||||||
pvStatisticsData.value.unshift(el.pvCount)
|
pvChartData.value.push(item.pvCount)
|
||||||
ipStatisticsData.value.unshift(el.ipCount)
|
ipChartData.value.push(item.ipCount)
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
@@ -1,23 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-spin :loading="loading" style="width: 100%">
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
<a-card class="general-card" title="浏览器分析" :header-style="{ paddingBottom: '12px' }">
|
<a-card class="general-card" title="浏览器">
|
||||||
<div class="chart">
|
<div class="chart">
|
||||||
<Chart v-if="!loading" style="height: 210px" :option="option" />
|
<Chart v-if="!loading" :option="chartOption" style="height: 190px" />
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
import { useChart } from '@/hooks'
|
import { useChart } from '@/hooks'
|
||||||
import { type DashboardChartCommonResp, getAnalysisBrowser as getData } from '@/apis/common'
|
import { type DashboardChartCommonResp, getAnalysisBrowser as getData } from '@/apis/common'
|
||||||
|
|
||||||
const xAxis = ref<string[]>([])
|
const xAxis = ref<string[]>([])
|
||||||
const dataList = ref([])
|
const chartData = ref([])
|
||||||
const { option } = useChart((isDark) => {
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
return {
|
return {
|
||||||
legend: {
|
legend: {
|
||||||
bottom: 'center',
|
|
||||||
data: xAxis.value,
|
data: xAxis.value,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
@@ -36,8 +36,8 @@ const { option } = useChart((isDark) => {
|
|||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['50%', '70%'],
|
radius: ['35%', '60%'],
|
||||||
center: ['50%', '45%'],
|
center: ['50%', '42%'],
|
||||||
label: {
|
label: {
|
||||||
formatter: '{d}% ',
|
formatter: '{d}% ',
|
||||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969'
|
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969'
|
||||||
@@ -46,22 +46,22 @@ const { option } = useChart((isDark) => {
|
|||||||
borderColor: isDark ? '#000' : '#fff',
|
borderColor: isDark ? '#000' : '#fff',
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
},
|
},
|
||||||
data: dataList.value
|
data: chartData.value
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const colors = ['#249EFF', '#846BCE', '#21CCFF', '#0E42D2', '#86DF6C']
|
const colors = ['#246EFF', '#00B2FF', '#81E2FF', '#846BCE', '#86DF6C']
|
||||||
// 查询图表数据
|
// 查询图表数据
|
||||||
const getChartData = async () => {
|
const getChartData = async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const { data } = await getData()
|
const { data } = await getData()
|
||||||
data.forEach((item: DashboardChartCommonResp, index) => {
|
data.forEach((item: DashboardChartCommonResp, index: number) => {
|
||||||
xAxis.value.push(item.name)
|
xAxis.value.push(item.name)
|
||||||
dataList.value.push({
|
chartData.value.push({
|
||||||
...item,
|
...item,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: data.length > 1 && index === data.length - 1 ? colors[colors.length - 1] : colors[index]
|
color: data.length > 1 && index === data.length - 1 ? colors[colors.length - 1] : colors[index]
|
30
src/views/dashboard/analysis/components/DataOverview.vue
Normal file
30
src/views/dashboard/analysis/components/DataOverview.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<a-card class="general-card" title="数据总览">
|
||||||
|
<a-grid :cols="24" :col-gap="12" :row-gap="12">
|
||||||
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 6, xxl: 6 }">
|
||||||
|
<Pv />
|
||||||
|
</a-grid-item>
|
||||||
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 6, xxl: 6 }">
|
||||||
|
<Ip />
|
||||||
|
</a-grid-item>
|
||||||
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 6, xxl: 6 }">
|
||||||
|
<Demo1 />
|
||||||
|
</a-grid-item>
|
||||||
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 6, xxl: 6 }">
|
||||||
|
<Demo2 />
|
||||||
|
</a-grid-item>
|
||||||
|
</a-grid>
|
||||||
|
<template #extra>
|
||||||
|
<slot name="extra"></slot>
|
||||||
|
</template>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import Pv from './flow/Pv.vue'
|
||||||
|
import Ip from './flow/Ip.vue'
|
||||||
|
import Demo1 from './flow/Demo1.vue'
|
||||||
|
import Demo2 from './flow/Demo2.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
101
src/views/dashboard/analysis/components/Module.vue
Normal file
101
src/views/dashboard/analysis/components/Module.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
|
<a-card class="general-card" title="热门模块 (Top10)">
|
||||||
|
<div class="chart">
|
||||||
|
<Chart v-if="!loading" :option="chartOption" style="width: 100%; height: 355px" />
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useChart } from '@/hooks'
|
||||||
|
import { type DashboardChartCommonResp, getAnalysisModule as getData } from '@/apis/common'
|
||||||
|
|
||||||
|
const yAxis = ref<string[]>([])
|
||||||
|
const chartData = ref([])
|
||||||
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
|
return {
|
||||||
|
grid: {
|
||||||
|
left: 55,
|
||||||
|
right: 20,
|
||||||
|
top: 0,
|
||||||
|
bottom: 20
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
formatter(value: number, idx: number) {
|
||||||
|
if (idx === 0) return String(value)
|
||||||
|
return `${Number(value) / 1000}k`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: isDark ? '#484849' : '#E5E8EF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: yAxis.value,
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
color: '#4E5969'
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: true,
|
||||||
|
length: 2,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#A9AEB8'
|
||||||
|
},
|
||||||
|
alignWithLabel: true
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: isDark ? '#484849' : '#A9AEB8'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: chartData.value,
|
||||||
|
type: 'bar',
|
||||||
|
barWidth: 7,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#4086FF',
|
||||||
|
borderRadius: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
// 查询图表数据
|
||||||
|
const getChartData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const { data } = await getData()
|
||||||
|
data.forEach((item: DashboardChartCommonResp) => {
|
||||||
|
yAxis.value.unshift(item.name)
|
||||||
|
chartData.value.unshift(item.value)
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getChartData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
</style>
|
@@ -1,82 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-spin :loading="loading" style="width: 100%">
|
|
||||||
<a-card class="general-card" title="模块分析" :header-style="{ paddingBottom: '12px' }">
|
|
||||||
<div class="chart">
|
|
||||||
<Chart v-if="!loading" style="height: 210px" :option="option" />
|
|
||||||
</div>
|
|
||||||
</a-card>
|
|
||||||
</a-spin>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useChart } from '@/hooks'
|
|
||||||
import { type DashboardChartCommonResp, getAnalysisModule as getData } from '@/apis/common'
|
|
||||||
|
|
||||||
const xAxis = ref<string[]>([])
|
|
||||||
const dataList = ref([])
|
|
||||||
const { option } = useChart((isDark) => {
|
|
||||||
return {
|
|
||||||
legend: {
|
|
||||||
bottom: 'center',
|
|
||||||
data: xAxis.value,
|
|
||||||
bottom: 0,
|
|
||||||
icon: 'circle',
|
|
||||||
itemWidth: 8,
|
|
||||||
textStyle: {
|
|
||||||
color: isDark ? 'rgba(255,255,255,0.7)' : '#4E5969'
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
borderWidth: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
show: true,
|
|
||||||
trigger: 'item'
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: 'pie',
|
|
||||||
radius: ['50%', '70%'],
|
|
||||||
center: ['50%', '45%'],
|
|
||||||
label: {
|
|
||||||
formatter: '{d}% ',
|
|
||||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969'
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
borderColor: isDark ? '#000' : '#fff',
|
|
||||||
borderWidth: 1
|
|
||||||
},
|
|
||||||
data: dataList.value
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const colors = ['#249EFF', '#846BCE', '#21CCFF', '#0E42D2', '#86DF6C']
|
|
||||||
// 查询图表数据
|
|
||||||
const getChartData = async () => {
|
|
||||||
try {
|
|
||||||
loading.value = true
|
|
||||||
const { data } = await getData()
|
|
||||||
data.forEach((item: DashboardChartCommonResp, index) => {
|
|
||||||
xAxis.value.push(item.name)
|
|
||||||
dataList.value.push({
|
|
||||||
...item,
|
|
||||||
itemStyle: {
|
|
||||||
color: data.length > 1 && index === data.length - 1 ? colors[colors.length - 1] : colors[index]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getChartData()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
</style>
|
|
@@ -1,23 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-spin :loading="loading" style="width: 100%">
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
<a-card class="general-card" title="终端分析" :header-style="{ paddingBottom: '12px' }">
|
<a-card class="general-card" title="终端">
|
||||||
<div class="chart">
|
<div class="chart">
|
||||||
<Chart v-if="!loading" style="height: 210px" :option="option" />
|
<Chart v-if="!loading" :option="chartOption" style="height: 190px" />
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
import { useChart } from '@/hooks'
|
import { useChart } from '@/hooks'
|
||||||
import { type DashboardChartCommonResp, getAnalysisOs as getData } from '@/apis/common'
|
import { type DashboardChartCommonResp, getAnalysisOs as getData } from '@/apis/common'
|
||||||
|
|
||||||
const xAxis = ref<string[]>([])
|
const xAxis = ref<string[]>([])
|
||||||
const dataList = ref([])
|
const chartData = ref([])
|
||||||
const { option } = useChart((isDark) => {
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
return {
|
return {
|
||||||
legend: {
|
legend: {
|
||||||
bottom: 'center',
|
|
||||||
data: xAxis.value,
|
data: xAxis.value,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
@@ -36,8 +36,8 @@ const { option } = useChart((isDark) => {
|
|||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['50%', '70%'],
|
radius: ['35%', '60%'],
|
||||||
center: ['50%', '45%'],
|
center: ['50%', '42%'],
|
||||||
label: {
|
label: {
|
||||||
formatter: '{d}% ',
|
formatter: '{d}% ',
|
||||||
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969'
|
color: isDark ? 'rgba(255, 255, 255, 0.7)' : '#4E5969'
|
||||||
@@ -46,22 +46,22 @@ const { option } = useChart((isDark) => {
|
|||||||
borderColor: isDark ? '#000' : '#fff',
|
borderColor: isDark ? '#000' : '#fff',
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
},
|
},
|
||||||
data: dataList.value
|
data: chartData.value
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const colors = ['#249EFF', '#846BCE', '#21CCFF', '#0E42D2', '#86DF6C']
|
const colors = ['#246EFF', '#00B2FF', '#81E2FF', '#846BCE', '#86DF6C']
|
||||||
// 查询图表数据
|
// 查询图表数据
|
||||||
const getChartData = async () => {
|
const getChartData = async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const { data } = await getData()
|
const { data } = await getData()
|
||||||
data.forEach((item: DashboardChartCommonResp, index) => {
|
data.forEach((item: DashboardChartCommonResp, index: number) => {
|
||||||
xAxis.value.push(item.name)
|
xAxis.value.push(item.name)
|
||||||
dataList.value.push({
|
chartData.value.push({
|
||||||
...item,
|
...item,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: data.length > 1 && index === data.length - 1 ? colors[colors.length - 1] : colors[index]
|
color: data.length > 1 && index === data.length - 1 ? colors[colors.length - 1] : colors[index]
|
148
src/views/dashboard/analysis/components/flow/Demo1.vue
Normal file
148
src/views/dashboard/analysis/components/flow/Demo1.vue
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
|
<a-card
|
||||||
|
class="general-card"
|
||||||
|
:style="{
|
||||||
|
background: isDark
|
||||||
|
? 'linear-gradient(180deg, #284991 0%, #122B62 100%)'
|
||||||
|
: 'linear-gradient(180deg, #f2f9fe 0%, #e6f4fe 100%)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="content">
|
||||||
|
<a-statistic
|
||||||
|
title="统计示例"
|
||||||
|
:value="count"
|
||||||
|
:value-from="0"
|
||||||
|
animation
|
||||||
|
show-group-separator
|
||||||
|
/>
|
||||||
|
<div class="desc">
|
||||||
|
<a-typography-text type="secondary" class="label">较昨日</a-typography-text>
|
||||||
|
<a-typography-text v-if="growth > 0" type="success" :title="`${growth}%`">
|
||||||
|
{{ growth }}
|
||||||
|
<icon-arrow-rise />
|
||||||
|
</a-typography-text>
|
||||||
|
<a-typography-text v-else type="danger" :title="`${growth}%`">
|
||||||
|
{{ growth }}
|
||||||
|
<icon-arrow-fall />
|
||||||
|
</a-typography-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart">
|
||||||
|
<Chart v-if="!loading" :option="chartOption" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useChart } from '@/hooks'
|
||||||
|
import { useAppStore } from '@/stores'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const isDark = computed(() => appStore.theme === 'dark')
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
const growth = ref(0)
|
||||||
|
const xAxis = ref<string[]>([])
|
||||||
|
const chartData = ref<number[]>([])
|
||||||
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
|
return {
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 30,
|
||||||
|
top: 10,
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis.value
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '示例',
|
||||||
|
data: chartData.value,
|
||||||
|
type: 'line',
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#246EFF',
|
||||||
|
width: 2,
|
||||||
|
type: 'dashed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
// 查询图表数据
|
||||||
|
const getChartData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
xAxis.value = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
||||||
|
count.value = 88888
|
||||||
|
growth.value = 88.8
|
||||||
|
chartData.value = [10, 2, 4, 30, 21, 6, 7, 8, 1, 2, 3, 10]
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getChartData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.arco-card) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
:deep(.arco-card-body) {
|
||||||
|
width: 100%;
|
||||||
|
height: 134px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.content-wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.content) {
|
||||||
|
float: left;
|
||||||
|
width: 108px;
|
||||||
|
height: 102px;
|
||||||
|
}
|
||||||
|
:deep(.arco-statistic) {
|
||||||
|
.arco-statistic-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.arco-statistic-content {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
float: right;
|
||||||
|
width: calc(100% - 108px);
|
||||||
|
height: 90px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
padding-right: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
159
src/views/dashboard/analysis/components/flow/Demo2.vue
Normal file
159
src/views/dashboard/analysis/components/flow/Demo2.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
|
<a-card
|
||||||
|
class="general-card"
|
||||||
|
:style="{
|
||||||
|
background: isDark
|
||||||
|
? 'linear-gradient(180deg, #312565 0%, #201936 100%)'
|
||||||
|
: 'linear-gradient(180deg, #F7F7FF 0%, #ECECFF 100%)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="content">
|
||||||
|
<a-statistic
|
||||||
|
title="统计示例"
|
||||||
|
:value="count"
|
||||||
|
:value-from="0"
|
||||||
|
animation
|
||||||
|
show-group-separator
|
||||||
|
/>
|
||||||
|
<div class="desc">
|
||||||
|
<a-typography-text type="secondary" class="label">较昨日</a-typography-text>
|
||||||
|
<a-typography-text v-if="growth > 0" type="success" :title="`${growth}%`">
|
||||||
|
{{ growth }}
|
||||||
|
<icon-arrow-rise />
|
||||||
|
</a-typography-text>
|
||||||
|
<a-typography-text v-else type="danger" :title="`${growth}%`">
|
||||||
|
{{ growth }}
|
||||||
|
<icon-arrow-fall />
|
||||||
|
</a-typography-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart">
|
||||||
|
<Chart v-if="!loading" :option="chartOption" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useChart } from '@/hooks'
|
||||||
|
import { useAppStore } from '@/stores'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const isDark = computed(() => appStore.theme === 'dark')
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
const growth = ref(0)
|
||||||
|
const chartData = ref<number[]>([])
|
||||||
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
|
return {
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
top: 'center',
|
||||||
|
right: '20%',
|
||||||
|
orient: 'vertical',
|
||||||
|
icon: 'circle',
|
||||||
|
itemWidth: 6,
|
||||||
|
itemHeight: 6,
|
||||||
|
textStyle: {
|
||||||
|
color: '#4E5969'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '总计',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['50%', '70%'],
|
||||||
|
center: ['30%', '50%'],
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: chartData.value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const colors = ['#8D4EDA', '#00B2FF', '#86DF6C']
|
||||||
|
// 查询图表数据
|
||||||
|
const getChartData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
count.value = 88888
|
||||||
|
growth.value = 88.8
|
||||||
|
const data = [30, 20, 10]
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
chartData.value.push({
|
||||||
|
name: `示例${index + 1}`,
|
||||||
|
value: item,
|
||||||
|
itemStyle: {
|
||||||
|
color: data.length > 1 && index === data.length - 1 ? colors[colors.length - 1] : colors[index]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getChartData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.arco-card) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
:deep(.arco-card-body) {
|
||||||
|
width: 100%;
|
||||||
|
height: 134px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.content-wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.content) {
|
||||||
|
float: left;
|
||||||
|
width: 108px;
|
||||||
|
height: 102px;
|
||||||
|
}
|
||||||
|
:deep(.arco-statistic) {
|
||||||
|
.arco-statistic-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.arco-statistic-content {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
float: right;
|
||||||
|
width: calc(100% - 108px);
|
||||||
|
height: 90px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
padding-right: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
153
src/views/dashboard/analysis/components/flow/Ip.vue
Normal file
153
src/views/dashboard/analysis/components/flow/Ip.vue
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
|
<a-card
|
||||||
|
class="general-card"
|
||||||
|
:style="{
|
||||||
|
background: isDark
|
||||||
|
? ' linear-gradient(180deg, #3D492E 0%, #263827 100%)'
|
||||||
|
: 'linear-gradient(180deg, #F5FEF2 0%, #E6FEEE 100%)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="content">
|
||||||
|
<a-statistic
|
||||||
|
title="独立IP"
|
||||||
|
:value="count"
|
||||||
|
:value-from="0"
|
||||||
|
animation
|
||||||
|
show-group-separator
|
||||||
|
/>
|
||||||
|
<div class="desc">
|
||||||
|
<a-typography-text type="secondary" class="label">今日</a-typography-text>
|
||||||
|
<a-typography-text v-if="growth > 0" type="success" :title="`${growth}%`">
|
||||||
|
{{ today }}
|
||||||
|
<icon-arrow-rise />
|
||||||
|
</a-typography-text>
|
||||||
|
<a-typography-text v-else type="danger" :title="`${growth}%`">
|
||||||
|
{{ today }}
|
||||||
|
<icon-arrow-fall />
|
||||||
|
</a-typography-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart">
|
||||||
|
<Chart v-if="!loading" :option="chartOption" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useChart } from '@/hooks'
|
||||||
|
import { useAppStore } from '@/stores'
|
||||||
|
import { type DashboardChartCommonResp, getDashboardOverviewIp as getData } from '@/apis'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const isDark = computed(() => appStore.theme === 'dark')
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
const today = ref(0)
|
||||||
|
const growth = ref(0)
|
||||||
|
const xAxis = ref<string[]>([])
|
||||||
|
const chartData = ref<number[]>([])
|
||||||
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
|
return {
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 30,
|
||||||
|
top: 10,
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis.value
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '独立IP',
|
||||||
|
data: chartData.value,
|
||||||
|
type: 'line',
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2CAB40',
|
||||||
|
width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
// 查询图表数据
|
||||||
|
const getChartData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const { data } = await getData()
|
||||||
|
count.value = data.total
|
||||||
|
today.value = data.today
|
||||||
|
growth.value = data.growth
|
||||||
|
data.dataList.forEach((item: DashboardChartCommonResp) => {
|
||||||
|
xAxis.value.push(item.name)
|
||||||
|
chartData.value.push(item.value)
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getChartData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.arco-card) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
:deep(.arco-card-body) {
|
||||||
|
width: 100%;
|
||||||
|
height: 134px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.content-wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.content) {
|
||||||
|
float: left;
|
||||||
|
width: 108px;
|
||||||
|
height: 102px;
|
||||||
|
}
|
||||||
|
:deep(.arco-statistic) {
|
||||||
|
.arco-statistic-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.arco-statistic-content {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
float: right;
|
||||||
|
width: calc(100% - 108px);
|
||||||
|
height: 90px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
padding-right: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
153
src/views/dashboard/analysis/components/flow/Pv.vue
Normal file
153
src/views/dashboard/analysis/components/flow/Pv.vue
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
|
<a-card
|
||||||
|
class="general-card"
|
||||||
|
:style="{
|
||||||
|
background: isDark
|
||||||
|
? 'linear-gradient(180deg, #284991 0%, #122B62 100%)'
|
||||||
|
: 'linear-gradient(180deg, #f2f9fe 0%, #e6f4fe 100%)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="content">
|
||||||
|
<a-statistic
|
||||||
|
title="访问次数"
|
||||||
|
:value="count"
|
||||||
|
:value-from="0"
|
||||||
|
animation
|
||||||
|
show-group-separator
|
||||||
|
/>
|
||||||
|
<div class="desc">
|
||||||
|
<a-typography-text type="secondary" class="label">今日</a-typography-text>
|
||||||
|
<a-typography-text v-if="growth > 0" type="success" :title="`${growth}%`">
|
||||||
|
{{ today }}
|
||||||
|
<icon-arrow-rise />
|
||||||
|
</a-typography-text>
|
||||||
|
<a-typography-text v-else type="danger" :title="`${growth}%`">
|
||||||
|
{{ today }}
|
||||||
|
<icon-arrow-fall />
|
||||||
|
</a-typography-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chart">
|
||||||
|
<Chart v-if="!loading" :option="chartOption" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { useChart } from '@/hooks'
|
||||||
|
import { useAppStore } from '@/stores'
|
||||||
|
import { type DashboardChartCommonResp, getDashboardOverviewPv as getData } from '@/apis'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const isDark = computed(() => appStore.theme === 'dark')
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
const today = ref(0)
|
||||||
|
const growth = ref(0)
|
||||||
|
const xAxis = ref<string[]>([])
|
||||||
|
const chartData = ref<number[]>([])
|
||||||
|
const { chartOption } = useChart((isDark: EChartsOption) => {
|
||||||
|
return {
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 30,
|
||||||
|
top: 10,
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis.value
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '访问次数',
|
||||||
|
data: chartData.value,
|
||||||
|
type: 'line',
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#246EFF',
|
||||||
|
width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
// 查询图表数据
|
||||||
|
const getChartData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const { data } = await getData()
|
||||||
|
count.value = data.total
|
||||||
|
today.value = data.today
|
||||||
|
growth.value = data.growth
|
||||||
|
data.dataList.forEach((item: DashboardChartCommonResp) => {
|
||||||
|
xAxis.value.push(item.name)
|
||||||
|
chartData.value.push(item.value)
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getChartData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
:deep(.arco-card) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
:deep(.arco-card-body) {
|
||||||
|
width: 100%;
|
||||||
|
height: 134px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.content-wrap {
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.content) {
|
||||||
|
float: left;
|
||||||
|
width: 108px;
|
||||||
|
height: 102px;
|
||||||
|
}
|
||||||
|
:deep(.arco-statistic) {
|
||||||
|
.arco-statistic-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.arco-statistic-content {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
float: right;
|
||||||
|
width: calc(100% - 108px);
|
||||||
|
height: 90px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
padding-right: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,34 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="gi_page container">
|
<div class="gi_page container">
|
||||||
<a-space direction="vertical" :size="16" fill>
|
<a-space direction="vertical" :size="14" fill>
|
||||||
<div>
|
<div>
|
||||||
<AccessTrend />
|
<DataOverview />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a-grid :cols="24" :col-gap="16" :row-gap="16">
|
<a-grid :cols="24" :col-gap="14" :row-gap="14">
|
||||||
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 8, xxl: 8 }">
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 18, xxl: 18 }">
|
||||||
<ModuleItem />
|
<AccessTrend />
|
||||||
</a-grid-item>
|
</a-grid-item>
|
||||||
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 8, xxl: 8 }">
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 6, xxl: 6 }">
|
||||||
<OsItem />
|
<Os style="margin-bottom: 16px" />
|
||||||
</a-grid-item>
|
<Browser />
|
||||||
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 8, xxl: 8 }">
|
|
||||||
<BrowserItem />
|
|
||||||
</a-grid-item>
|
</a-grid-item>
|
||||||
</a-grid>
|
</a-grid>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<AccessTimeslot />
|
<a-grid :cols="24" :col-gap="16" :row-gap="16">
|
||||||
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 18, xxl: 18 }">
|
||||||
|
<AccessTimeslot />
|
||||||
|
</a-grid-item>
|
||||||
|
<a-grid-item :span="{ xs: 24, sm: 24, md: 24, lg: 24, xl: 6, xxl: 6 }">
|
||||||
|
<Module />
|
||||||
|
</a-grid-item>
|
||||||
|
</a-grid>
|
||||||
</div>
|
</div>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DataOverview from './components/DataOverview.vue'
|
||||||
import AccessTrend from './components/AccessTrend.vue'
|
import AccessTrend from './components/AccessTrend.vue'
|
||||||
import ModuleItem from './components/ModuleItem.vue'
|
import Os from './components/Os.vue'
|
||||||
import OsItem from './components/OsItem.vue'
|
import Browser from './components/Browser.vue'
|
||||||
import BrowserItem from './components/BrowserItem.vue'
|
import Module from './components/Module.vue'
|
||||||
import AccessTimeslot from './components/AccessTimeslot.vue'
|
import AccessTimeslot from './components/AccessTimeslot.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'Analysis' })
|
defineOptions({ name: 'Analysis' })
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<a-radio :value="30">近30天</a-radio>
|
<a-radio :value="30">近30天</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</template>
|
</template>
|
||||||
<VCharts :option="option" autoresize :style="{ height: '326px' }"></VCharts>
|
<VCharts :option="chartOption" autoresize :style="{ height: '326px' }"></VCharts>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import VCharts from 'vue-echarts'
|
import VCharts from 'vue-echarts'
|
||||||
import { graphic } from 'echarts'
|
import { graphic } from 'echarts'
|
||||||
import { type DashboardAccessTrendResp, listDashboardAccessTrend } from '@/apis'
|
import { type DashboardAccessTrendResp, getDashboardAccessTrend as getData } from '@/apis'
|
||||||
import { useChart } from '@/hooks'
|
import { useChart } from '@/hooks'
|
||||||
|
|
||||||
// 提示框
|
// 提示框
|
||||||
@@ -38,7 +38,7 @@ const tooltipItemsHtmlString = (items) => {
|
|||||||
const xData = ref<string[]>([])
|
const xData = ref<string[]>([])
|
||||||
const pvStatisticsData = ref<number[]>([])
|
const pvStatisticsData = ref<number[]>([])
|
||||||
const ipStatisticsData = ref<number[]>([])
|
const ipStatisticsData = ref<number[]>([])
|
||||||
const { option } = useChart((isDark) => {
|
const { chartOption } = useChart((isDark) => {
|
||||||
return {
|
return {
|
||||||
grid: {
|
grid: {
|
||||||
left: '38',
|
left: '38',
|
||||||
@@ -197,11 +197,11 @@ const getChartData = async (days: number) => {
|
|||||||
xData.value = []
|
xData.value = []
|
||||||
pvStatisticsData.value = []
|
pvStatisticsData.value = []
|
||||||
ipStatisticsData.value = []
|
ipStatisticsData.value = []
|
||||||
const { data: chartData } = await listDashboardAccessTrend(days)
|
const { data } = await getData(days)
|
||||||
chartData.forEach((el: DashboardAccessTrendResp) => {
|
data.forEach((el: DashboardAccessTrendResp) => {
|
||||||
xData.value.unshift(el.date)
|
xData.value.push(el.date)
|
||||||
pvStatisticsData.value.unshift(el.pvCount)
|
pvStatisticsData.value.push(el.pvCount)
|
||||||
ipStatisticsData.value.unshift(el.ipCount)
|
ipStatisticsData.value.push(el.ipCount)
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
Reference in New Issue
Block a user