484 lines
13 KiB
Vue
484 lines
13 KiB
Vue
<template>
|
|
<div class="app-container">
|
|
<a-card class="general-card" title="应用使用数据" :bordered="false">
|
|
<a-row :gutter="16">
|
|
<a-col :span="6" v-for="(stat, index) in appStatistics" :key="index">
|
|
<a-card class="stat-card">
|
|
<a-statistic
|
|
:title="stat.title"
|
|
:value="stat.value"
|
|
:precision="stat.precision || 0"
|
|
:suffix="stat.suffix || ''"
|
|
>
|
|
<template #prefix>
|
|
<component :is="stat.icon" />
|
|
</template>
|
|
</a-statistic>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-divider />
|
|
|
|
<a-row :gutter="16">
|
|
<a-col :span="12">
|
|
<a-card title="应用访问量趋势" :bordered="false">
|
|
<div ref="appVisitChart" style="height: 350px"></div>
|
|
</a-card>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-card title="应用使用时长分布" :bordered="false">
|
|
<div ref="appTimeChart" style="height: 350px"></div>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-divider />
|
|
|
|
<a-card title="应用使用详情" :bordered="false">
|
|
<a-tabs default-active-key="1">
|
|
<a-tab-pane key="1" tab="Web端">
|
|
<a-table :columns="appColumns" :data-source="webAppData" :pagination="{ pageSize: 5 }">
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.dataIndex === 'usageRate'">
|
|
<a-progress :percent="record.usageRate" :stroke-color="getUsageRateColor(record.usageRate)" />
|
|
</template>
|
|
<template v-if="column.dataIndex === 'trend'">
|
|
<span :style="{ color: getTrendColor(record.trend) }">
|
|
{{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}%
|
|
<icon-up v-if="record.trend > 0" style="color: #52c41a" />
|
|
<icon-down v-if="record.trend < 0" style="color: #ff4d4f" />
|
|
<icon-minus v-if="record.trend === 0" style="color: #1890ff" />
|
|
</span>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-tab-pane>
|
|
<a-tab-pane key="2" tab="移动端">
|
|
<a-table :columns="appColumns" :data-source="mobileAppData" :pagination="{ pageSize: 5 }">
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.dataIndex === 'usageRate'">
|
|
<a-progress :percent="record.usageRate" :stroke-color="getUsageRateColor(record.usageRate)" />
|
|
</template>
|
|
<template v-if="column.dataIndex === 'trend'">
|
|
<span :style="{ color: getTrendColor(record.trend) }">
|
|
{{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}%
|
|
<icon-up v-if="record.trend > 0" style="color: #52c41a" />
|
|
<icon-down v-if="record.trend < 0" style="color: #ff4d4f" />
|
|
<icon-minus v-if="record.trend === 0" style="color: #1890ff" />
|
|
</span>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-tab-pane>
|
|
<a-tab-pane key="3" tab="微信小程序">
|
|
<a-table :columns="appColumns" :data-source="miniAppData" :pagination="{ pageSize: 5 }">
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.dataIndex === 'usageRate'">
|
|
<a-progress :percent="record.usageRate" :stroke-color="getUsageRateColor(record.usageRate)" />
|
|
</template>
|
|
<template v-if="column.dataIndex === 'trend'">
|
|
<span :style="{ color: getTrendColor(record.trend) }">
|
|
{{ record.trend >= 0 ? '+' : '' }}{{ record.trend }}%
|
|
<icon-up v-if="record.trend > 0" style="color: #52c41a" />
|
|
<icon-down v-if="record.trend < 0" style="color: #ff4d4f" />
|
|
<icon-minus v-if="record.trend === 0" style="color: #1890ff" />
|
|
</span>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-tab-pane>
|
|
</a-tabs>
|
|
</a-card>
|
|
|
|
<a-divider />
|
|
|
|
<a-row :gutter="16">
|
|
<a-col :span="24">
|
|
<a-card title="终端设备分布" :bordered="false">
|
|
<div ref="deviceDistributionChart" style="height: 400px"></div>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
</a-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, onMounted, reactive, nextTick } from 'vue'
|
|
import { Statistic } from '@arco-design/web-vue'
|
|
import * as echarts from 'echarts'
|
|
import {
|
|
IconApps,
|
|
IconMobile,
|
|
IconUser,
|
|
IconClockCircle,
|
|
IconUp,
|
|
IconDown,
|
|
IconMinus
|
|
} from '@arco-design/web-vue/es/icon'
|
|
|
|
|
|
const appVisitChart = ref(null)
|
|
const appTimeChart = ref(null)
|
|
const deviceDistributionChart = ref(null)
|
|
|
|
// 应用统计数据
|
|
const appStatistics = reactive([
|
|
{
|
|
title: '应用总数',
|
|
value: 12,
|
|
icon: IconApps
|
|
},
|
|
{
|
|
title: '活跃应用',
|
|
value: 8,
|
|
icon: IconApps
|
|
},
|
|
{
|
|
title: '日均访问量',
|
|
value: 1258,
|
|
icon: IconUser
|
|
},
|
|
{
|
|
title: '日均使用时长',
|
|
value: 3.5,
|
|
precision: 1,
|
|
suffix: '小时',
|
|
icon: IconClockCircle
|
|
}
|
|
])
|
|
|
|
// 应用使用详情表格列定义
|
|
const appColumns = [
|
|
{
|
|
title: '应用名称',
|
|
dataIndex: 'appName',
|
|
key: 'appName',
|
|
},
|
|
{
|
|
title: '访问量',
|
|
dataIndex: 'visitCount',
|
|
key: 'visitCount',
|
|
sorter: (a, b) => a.visitCount - b.visitCount,
|
|
},
|
|
{
|
|
title: '用户数',
|
|
dataIndex: 'userCount',
|
|
key: 'userCount',
|
|
sorter: (a, b) => a.userCount - b.userCount,
|
|
},
|
|
{
|
|
title: '使用率',
|
|
dataIndex: 'usageRate',
|
|
key: 'usageRate',
|
|
sorter: (a, b) => a.usageRate - b.usageRate,
|
|
},
|
|
{
|
|
title: '平均使用时长',
|
|
dataIndex: 'averageTime',
|
|
key: 'averageTime',
|
|
},
|
|
{
|
|
title: '环比上月',
|
|
dataIndex: 'trend',
|
|
key: 'trend',
|
|
sorter: (a, b) => a.trend - b.trend,
|
|
}
|
|
]
|
|
|
|
// Web端应用数据
|
|
const webAppData = [
|
|
{
|
|
key: '1',
|
|
appName: '企业管理后台',
|
|
visitCount: 3560,
|
|
userCount: 320,
|
|
usageRate: 95,
|
|
averageTime: '2.5小时',
|
|
trend: 8.5
|
|
},
|
|
{
|
|
key: '2',
|
|
appName: '项目管理系统',
|
|
visitCount: 2980,
|
|
userCount: 285,
|
|
usageRate: 90,
|
|
averageTime: '3小时',
|
|
trend: 5.2
|
|
},
|
|
{
|
|
key: '3',
|
|
appName: '数据分析平台',
|
|
visitCount: 2450,
|
|
userCount: 210,
|
|
usageRate: 85,
|
|
averageTime: '2小时',
|
|
trend: 3.8
|
|
},
|
|
{
|
|
key: '4',
|
|
appName: '客户管理系统',
|
|
visitCount: 1980,
|
|
userCount: 180,
|
|
usageRate: 75,
|
|
averageTime: '1.5小时',
|
|
trend: -2.1
|
|
},
|
|
{
|
|
key: '5',
|
|
appName: '知识库系统',
|
|
visitCount: 1560,
|
|
userCount: 150,
|
|
usageRate: 65,
|
|
averageTime: '1小时',
|
|
trend: 0
|
|
}
|
|
]
|
|
|
|
// 移动端应用数据
|
|
const mobileAppData = [
|
|
{
|
|
key: '1',
|
|
appName: '移动办公APP',
|
|
visitCount: 4250,
|
|
userCount: 350,
|
|
usageRate: 98,
|
|
averageTime: '3.5小时',
|
|
trend: 12.5
|
|
},
|
|
{
|
|
key: '2',
|
|
appName: '外勤管理APP',
|
|
visitCount: 3680,
|
|
userCount: 320,
|
|
usageRate: 92,
|
|
averageTime: '4小时',
|
|
trend: 9.8
|
|
},
|
|
{
|
|
key: '3',
|
|
appName: '项目跟踪APP',
|
|
visitCount: 2850,
|
|
userCount: 260,
|
|
usageRate: 88,
|
|
averageTime: '3小时',
|
|
trend: 7.5
|
|
},
|
|
{
|
|
key: '4',
|
|
appName: '客户拜访APP',
|
|
visitCount: 2120,
|
|
userCount: 180,
|
|
usageRate: 72,
|
|
averageTime: '2小时',
|
|
trend: -1.5
|
|
}
|
|
]
|
|
|
|
// 微信小程序数据
|
|
const miniAppData = [
|
|
{
|
|
key: '1',
|
|
appName: '企业服务小程序',
|
|
visitCount: 5680,
|
|
userCount: 420,
|
|
usageRate: 96,
|
|
averageTime: '1.5小时',
|
|
trend: 15.8
|
|
},
|
|
{
|
|
key: '2',
|
|
appName: '客户自助小程序',
|
|
visitCount: 4850,
|
|
userCount: 380,
|
|
usageRate: 90,
|
|
averageTime: '1小时',
|
|
trend: 10.5
|
|
},
|
|
{
|
|
key: '3',
|
|
appName: '员工工具小程序',
|
|
visitCount: 3920,
|
|
userCount: 345,
|
|
usageRate: 85,
|
|
averageTime: '0.8小时',
|
|
trend: 8.2
|
|
}
|
|
]
|
|
|
|
// 颜色处理函数
|
|
const getUsageRateColor = (rate) => {
|
|
if (rate >= 90) return '#52c41a'
|
|
if (rate >= 70) return '#1890ff'
|
|
return '#faad14'
|
|
}
|
|
|
|
const getTrendColor = (trend) => {
|
|
if (trend > 0) return '#52c41a'
|
|
if (trend < 0) return '#ff4d4f'
|
|
return '#1890ff'
|
|
}
|
|
|
|
onMounted(() => {
|
|
// 使用 nextTick 确保 DOM 已完全渲染
|
|
nextTick(() => {
|
|
// 初始化应用访问量趋势图表
|
|
if (appVisitChart.value) {
|
|
const visitChart = echarts.init(appVisitChart.value)
|
|
visitChart.setOption({
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
legend: {
|
|
data: ['Web端', '移动端', '小程序']
|
|
},
|
|
grid: {
|
|
left: '3%',
|
|
right: '4%',
|
|
bottom: '3%',
|
|
containLabel: true
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
boundaryGap: false,
|
|
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
|
},
|
|
yAxis: {
|
|
type: 'value'
|
|
},
|
|
series: [
|
|
{
|
|
name: 'Web端',
|
|
type: 'line',
|
|
data: [2500, 2800, 3200, 3100, 2950, 1800, 1200]
|
|
},
|
|
{
|
|
name: '移动端',
|
|
type: 'line',
|
|
data: [3200, 3500, 3800, 3600, 3400, 2800, 2500]
|
|
},
|
|
{
|
|
name: '小程序',
|
|
type: 'line',
|
|
data: [4500, 4800, 5200, 4900, 4700, 3900, 3500]
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
// 初始化应用使用时长分布图表
|
|
if (appTimeChart.value) {
|
|
const timeChart = echarts.init(appTimeChart.value)
|
|
timeChart.setOption({
|
|
tooltip: {
|
|
trigger: 'item'
|
|
},
|
|
legend: {
|
|
orient: 'vertical',
|
|
left: 'left'
|
|
},
|
|
series: [
|
|
{
|
|
name: '使用时长分布',
|
|
type: 'pie',
|
|
radius: '70%',
|
|
data: [
|
|
{ value: 35, name: 'Web端' },
|
|
{ value: 45, name: '移动端' },
|
|
{ value: 20, name: '小程序' }
|
|
],
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
}
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
// 初始化终端设备分布图表
|
|
if (deviceDistributionChart.value) {
|
|
const deviceChart = echarts.init(deviceDistributionChart.value)
|
|
deviceChart.setOption({
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'shadow'
|
|
}
|
|
},
|
|
legend: {},
|
|
grid: {
|
|
left: '3%',
|
|
right: '4%',
|
|
bottom: '3%',
|
|
containLabel: true
|
|
},
|
|
xAxis: {
|
|
type: 'value'
|
|
},
|
|
yAxis: {
|
|
type: 'category',
|
|
data: ['Windows PC', 'Mac', 'iOS', 'Android', '微信']
|
|
},
|
|
series: [
|
|
{
|
|
name: '访问量',
|
|
type: 'bar',
|
|
stack: 'total',
|
|
label: {
|
|
show: true
|
|
},
|
|
emphasis: {
|
|
focus: 'series'
|
|
},
|
|
data: [5200, 3800, 6500, 8200, 9500]
|
|
},
|
|
{
|
|
name: '用户数',
|
|
type: 'bar',
|
|
stack: 'total',
|
|
label: {
|
|
show: true
|
|
},
|
|
emphasis: {
|
|
focus: 'series'
|
|
},
|
|
data: [280, 220, 320, 380, 420]
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
// 监听窗口大小变化,调整图表大小
|
|
window.addEventListener('resize', () => {
|
|
if (appVisitChart.value) {
|
|
const visitChart = echarts.getInstanceByDom(appVisitChart.value)
|
|
visitChart?.resize()
|
|
}
|
|
if (appTimeChart.value) {
|
|
const timeChart = echarts.getInstanceByDom(appTimeChart.value)
|
|
timeChart?.resize()
|
|
}
|
|
if (deviceDistributionChart.value) {
|
|
const deviceChart = echarts.getInstanceByDom(deviceDistributionChart.value)
|
|
deviceChart?.resize()
|
|
}
|
|
})
|
|
})
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.general-card {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.stat-card {
|
|
margin-bottom: 20px;
|
|
text-align: center;
|
|
}
|
|
</style> |