main/src/views/enterprise-dashboard/function-usage/index.vue

375 lines
9.6 KiB
Vue

<template>
<div class="app-container">
<a-card class="general-card" title="功能使用情况" :bordered="false">
<a-row :gutter="16">
<a-col :span="24">
<a-card title="功能模块使用频率" :bordered="false">
<div ref="moduleUsageChart" style="height: 400px"></div>
</a-card>
</a-col>
</a-row>
<a-divider />
<a-row :gutter="16">
<a-col :span="12">
<a-card title="各部门功能使用分布" :bordered="false">
<div ref="departmentUsageChart" style="height: 350px"></div>
</a-card>
</a-col>
<a-col :span="12">
<a-card title="功能使用时长占比" :bordered="false">
<div ref="usageTimeChart" style="height: 350px"></div>
</a-card>
</a-col>
</a-row>
<a-divider />
<a-card title="功能使用详情" :bordered="false">
<a-table :columns="functionColumns" :data-source="functionData" :pagination="{ pageSize: 10 }">
<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 }}%
<arrow-up-outlined v-if="record.trend > 0" style="color: #52c41a" />
<arrow-down-outlined v-if="record.trend < 0" style="color: #ff4d4f" />
<minus-outlined v-if="record.trend === 0" style="color: #1890ff" />
</span>
</template>
</template>
</a-table>
</a-card>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, nextTick } from 'vue'
import * as echarts from 'echarts'
// 由于找不到 @ant-design/icons-vue 模块,注释掉相关导入
// import { ArrowUpOutlined, ArrowDownOutlined, MinusOutlined } from '@ant-design/icons-vue'
const moduleUsageChart = ref(null)
const departmentUsageChart = ref(null)
const usageTimeChart = ref(null)
// 功能使用详情表格数据
const functionColumns = [
{
title: '功能模块',
dataIndex: 'module',
key: 'module',
},
{
title: '子功能',
dataIndex: 'function',
key: 'function',
},
{
title: '使用次数',
dataIndex: 'usageCount',
key: 'usageCount',
sorter: (a, b) => a.usageCount - b.usageCount,
},
{
title: '使用率',
dataIndex: 'usageRate',
key: 'usageRate',
sorter: (a, b) => a.usageRate - b.usageRate,
},
{
title: '平均使用时长',
dataIndex: 'averageTime',
key: 'averageTime',
sorter: (a, b) => a.averageTime - b.averageTime,
},
{
title: '环比上月',
dataIndex: 'trend',
key: 'trend',
sorter: (a, b) => a.trend - b.trend,
}
]
const functionData = [
{
key: '1',
module: '组织架构',
function: '人员管理',
usageCount: 1245,
usageRate: 92,
averageTime: '15分钟',
trend: 5.2
},
{
key: '2',
module: '组织架构',
function: '角色管理',
usageCount: 865,
usageRate: 78,
averageTime: '12分钟',
trend: 3.8
},
{
key: '3',
module: '资产管理',
function: '设备管理',
usageCount: 1056,
usageRate: 85,
averageTime: '18分钟',
trend: 7.5
},
{
key: '4',
module: '资产管理',
function: '库存管理',
usageCount: 932,
usageRate: 80,
averageTime: '14分钟',
trend: -2.1
},
{
key: '5',
module: '产品与服务',
function: '产品管理',
usageCount: 1120,
usageRate: 88,
averageTime: '20分钟',
trend: 4.3
},
{
key: '6',
module: '产品与服务',
function: '服务管理',
usageCount: 986,
usageRate: 82,
averageTime: '16分钟',
trend: 0
},
{
key: '7',
module: '项目管理',
function: '项目模板',
usageCount: 1320,
usageRate: 95,
averageTime: '25分钟',
trend: 8.7
},
{
key: '8',
module: '项目管理',
function: '合同管理',
usageCount: 1150,
usageRate: 90,
averageTime: '22分钟',
trend: 6.2
},
{
key: '9',
module: '施工操作台',
function: '外业施工',
usageCount: 1280,
usageRate: 93,
averageTime: '30分钟',
trend: 9.5
},
{
key: '10',
module: '施工操作台',
function: '数据处理',
usageCount: 1180,
usageRate: 91,
averageTime: '28分钟',
trend: 5.8
},
{
key: '11',
module: '聊天平台',
function: '消息管理',
usageCount: 1420,
usageRate: 98,
averageTime: '35分钟',
trend: 12.3
},
{
key: '12',
module: '企业设置',
function: '企业信息',
usageCount: 720,
usageRate: 65,
averageTime: '10分钟',
trend: -1.5
}
]
// 颜色处理函数
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 (moduleUsageChart.value) {
const moduleChart = echarts.init(moduleUsageChart.value)
moduleChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['使用次数', '使用人数']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value'
},
yAxis: {
type: 'category',
data: ['组织架构', '资产管理', '产品与服务', '项目管理', '施工操作台', '聊天平台', '企业设置', '系统资源管理']
},
series: [
{
name: '使用次数',
type: 'bar',
data: [2110, 1988, 2106, 2470, 2460, 1420, 720, 650]
},
{
name: '使用人数',
type: 'bar',
data: [320, 302, 315, 335, 340, 356, 120, 85]
}
]
})
}
// 初始化各部门功能使用分布图表
if (departmentUsageChart.value) {
const departmentChart = echarts.init(departmentUsageChart.value)
departmentChart.setOption({
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '部门使用分布',
type: 'pie',
radius: '70%',
data: [
{ value: 35, name: '技术部' },
{ value: 25, name: '市场部' },
{ value: 20, name: '销售部' },
{ value: 10, name: '人事部' },
{ value: 10, name: '财务部' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
})
}
// 初始化功能使用时长占比图表
if (usageTimeChart.value) {
const timeChart = echarts.init(usageTimeChart.value)
timeChart.setOption({
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '使用时长占比',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '16',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 20, name: '组织架构' },
{ value: 15, name: '资产管理' },
{ value: 15, name: '产品与服务' },
{ value: 20, name: '项目管理' },
{ value: 20, name: '施工操作台' },
{ value: 5, name: '聊天平台' },
{ value: 3, name: '企业设置' },
{ value: 2, name: '系统资源管理' }
]
}
]
})
}
// 监听窗口大小变化,调整图表大小
window.addEventListener('resize', () => {
if (moduleUsageChart.value) {
const moduleChart = echarts.getInstanceByDom(moduleUsageChart.value)
moduleChart?.resize()
}
if (departmentUsageChart.value) {
const departmentChart = echarts.getInstanceByDom(departmentUsageChart.value)
departmentChart?.resize()
}
if (usageTimeChart.value) {
const timeChart = echarts.getInstanceByDom(usageTimeChart.value)
timeChart?.resize()
}
})
})
})
</script>
<style scoped>
.general-card {
margin-bottom: 20px;
}
</style>