mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 10:57:10 +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