494 lines
12 KiB
Vue
494 lines
12 KiB
Vue
<template>
|
|
<div class="app-container">
|
|
<a-card class="general-card" title="成员活跃数据" :bordered="false">
|
|
<a-tabs default-active-key="1">
|
|
<a-tab-pane key="1" tab="活跃度分析">
|
|
<a-row :gutter="16">
|
|
<a-col :span="24">
|
|
<a-card title="部门活跃度对比" :bordered="false">
|
|
<div ref="departmentActivityChart" style="height: 300px"></div>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-divider />
|
|
|
|
<a-row :gutter="16">
|
|
<a-col :span="12">
|
|
<a-card title="每日活跃用户数" :bordered="false">
|
|
<div ref="dailyActiveUsersChart" style="height: 300px"></div>
|
|
</a-card>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-card title="平均在线时长" :bordered="false">
|
|
<div ref="onlineTimeChart" style="height: 300px"></div>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
</a-tab-pane>
|
|
|
|
<a-tab-pane key="2" tab="成员排行榜">
|
|
<a-card title="本月活跃度排名" :bordered="false">
|
|
<a-table :columns="rankColumns" :data-source="rankData" :pagination="{ pageSize: 10 }">
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.dataIndex === 'rank'">
|
|
<a-tag :color="getRankColor(record.rank)">{{ record.rank }}</a-tag>
|
|
</template>
|
|
<template v-if="column.dataIndex === 'activityScore'">
|
|
<a-progress :percent="record.activityScore" :stroke-color="getScoreColor(record.activityScore)" />
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-card>
|
|
</a-tab-pane>
|
|
|
|
<a-tab-pane key="3" tab="考勤数据">
|
|
<a-row :gutter="16">
|
|
<a-col :span="24">
|
|
<a-card title="部门考勤统计" :bordered="false">
|
|
<a-table :columns="attendanceColumns" :data-source="attendanceData" :pagination="{ pageSize: 5 }">
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.dataIndex === 'attendanceRate'">
|
|
<a-progress :percent="record.attendanceRate" :stroke-color="getAttendanceColor(record.attendanceRate)" />
|
|
</template>
|
|
<template v-if="column.dataIndex === 'lateCount'">
|
|
<a-tag :color="getLateCountColor(record.lateCount)">{{ record.lateCount }}</a-tag>
|
|
</template>
|
|
</template>
|
|
</a-table>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-divider />
|
|
|
|
<a-row :gutter="16">
|
|
<a-col :span="24">
|
|
<a-card title="考勤趋势" :bordered="false">
|
|
<div ref="attendanceTrendChart" style="height: 300px"></div>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
</a-tab-pane>
|
|
</a-tabs>
|
|
</a-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, onMounted, nextTick } from 'vue'
|
|
import * as echarts from 'echarts'
|
|
|
|
const departmentActivityChart = ref(null)
|
|
const dailyActiveUsersChart = ref(null)
|
|
const onlineTimeChart = ref(null)
|
|
const attendanceTrendChart = ref(null)
|
|
|
|
// 排行榜数据
|
|
const rankColumns = [
|
|
{
|
|
title: '排名',
|
|
dataIndex: 'rank',
|
|
key: 'rank',
|
|
width: 80,
|
|
},
|
|
{
|
|
title: '姓名',
|
|
dataIndex: 'name',
|
|
key: 'name',
|
|
},
|
|
{
|
|
title: '部门',
|
|
dataIndex: 'department',
|
|
key: 'department',
|
|
},
|
|
{
|
|
title: '活跃度',
|
|
dataIndex: 'activityScore',
|
|
key: 'activityScore',
|
|
},
|
|
{
|
|
title: '登录次数',
|
|
dataIndex: 'loginCount',
|
|
key: 'loginCount',
|
|
},
|
|
{
|
|
title: '操作次数',
|
|
dataIndex: 'operationCount',
|
|
key: 'operationCount',
|
|
}
|
|
]
|
|
|
|
const rankData = [
|
|
{
|
|
key: '1',
|
|
rank: 1,
|
|
name: '张三',
|
|
department: '技术部',
|
|
activityScore: 98,
|
|
loginCount: 45,
|
|
operationCount: 532
|
|
},
|
|
{
|
|
key: '2',
|
|
rank: 2,
|
|
name: '李四',
|
|
department: '市场部',
|
|
activityScore: 95,
|
|
loginCount: 42,
|
|
operationCount: 498
|
|
},
|
|
{
|
|
key: '3',
|
|
rank: 3,
|
|
name: '王五',
|
|
department: '销售部',
|
|
activityScore: 92,
|
|
loginCount: 40,
|
|
operationCount: 475
|
|
},
|
|
{
|
|
key: '4',
|
|
rank: 4,
|
|
name: '赵六',
|
|
department: '人事部',
|
|
activityScore: 88,
|
|
loginCount: 38,
|
|
operationCount: 450
|
|
},
|
|
{
|
|
key: '5',
|
|
rank: 5,
|
|
name: '钱七',
|
|
department: '财务部',
|
|
activityScore: 85,
|
|
loginCount: 36,
|
|
operationCount: 420
|
|
},
|
|
{
|
|
key: '6',
|
|
rank: 6,
|
|
name: '孙八',
|
|
department: '技术部',
|
|
activityScore: 82,
|
|
loginCount: 34,
|
|
operationCount: 405
|
|
},
|
|
{
|
|
key: '7',
|
|
rank: 7,
|
|
name: '周九',
|
|
department: '市场部',
|
|
activityScore: 79,
|
|
loginCount: 32,
|
|
operationCount: 380
|
|
},
|
|
{
|
|
key: '8',
|
|
rank: 8,
|
|
name: '吴十',
|
|
department: '销售部',
|
|
activityScore: 76,
|
|
loginCount: 30,
|
|
operationCount: 365
|
|
},
|
|
{
|
|
key: '9',
|
|
rank: 9,
|
|
name: '郑十一',
|
|
department: '人事部',
|
|
activityScore: 73,
|
|
loginCount: 28,
|
|
operationCount: 350
|
|
},
|
|
{
|
|
key: '10',
|
|
rank: 10,
|
|
name: '王十二',
|
|
department: '财务部',
|
|
activityScore: 70,
|
|
loginCount: 26,
|
|
operationCount: 335
|
|
}
|
|
]
|
|
|
|
// 考勤数据
|
|
const attendanceColumns = [
|
|
{
|
|
title: '部门',
|
|
dataIndex: 'department',
|
|
key: 'department',
|
|
},
|
|
{
|
|
title: '人数',
|
|
dataIndex: 'memberCount',
|
|
key: 'memberCount',
|
|
},
|
|
{
|
|
title: '出勤率',
|
|
dataIndex: 'attendanceRate',
|
|
key: 'attendanceRate',
|
|
},
|
|
{
|
|
title: '迟到次数',
|
|
dataIndex: 'lateCount',
|
|
key: 'lateCount',
|
|
},
|
|
{
|
|
title: '早退次数',
|
|
dataIndex: 'earlyLeaveCount',
|
|
key: 'earlyLeaveCount',
|
|
},
|
|
{
|
|
title: '缺勤次数',
|
|
dataIndex: 'absentCount',
|
|
key: 'absentCount',
|
|
}
|
|
]
|
|
|
|
const attendanceData = [
|
|
{
|
|
key: '1',
|
|
department: '技术部',
|
|
memberCount: 45,
|
|
attendanceRate: 98,
|
|
lateCount: 3,
|
|
earlyLeaveCount: 1,
|
|
absentCount: 0
|
|
},
|
|
{
|
|
key: '2',
|
|
department: '市场部',
|
|
memberCount: 32,
|
|
attendanceRate: 96,
|
|
lateCount: 5,
|
|
earlyLeaveCount: 2,
|
|
absentCount: 1
|
|
},
|
|
{
|
|
key: '3',
|
|
department: '销售部',
|
|
memberCount: 38,
|
|
attendanceRate: 95,
|
|
lateCount: 6,
|
|
earlyLeaveCount: 3,
|
|
absentCount: 1
|
|
},
|
|
{
|
|
key: '4',
|
|
department: '人事部',
|
|
memberCount: 15,
|
|
attendanceRate: 97,
|
|
lateCount: 2,
|
|
earlyLeaveCount: 1,
|
|
absentCount: 0
|
|
},
|
|
{
|
|
key: '5',
|
|
department: '财务部',
|
|
memberCount: 12,
|
|
attendanceRate: 99,
|
|
lateCount: 1,
|
|
earlyLeaveCount: 0,
|
|
absentCount: 0
|
|
}
|
|
]
|
|
|
|
// 颜色处理函数
|
|
const getRankColor = (rank) => {
|
|
if (rank <= 3) return '#f50'
|
|
if (rank <= 10) return '#2db7f5'
|
|
return '#87d068'
|
|
}
|
|
|
|
const getScoreColor = (score) => {
|
|
if (score >= 90) return '#52c41a'
|
|
if (score >= 70) return '#1890ff'
|
|
return '#faad14'
|
|
}
|
|
|
|
const getAttendanceColor = (rate) => {
|
|
if (rate >= 95) return '#52c41a'
|
|
if (rate >= 90) return '#1890ff'
|
|
return '#faad14'
|
|
}
|
|
|
|
const getLateCountColor = (count) => {
|
|
if (count <= 2) return 'green'
|
|
if (count <= 5) return 'orange'
|
|
return 'red'
|
|
}
|
|
|
|
onMounted(() => {
|
|
// 使用 nextTick 确保 DOM 已完全渲染
|
|
nextTick(() => {
|
|
// 初始化部门活跃度对比图表
|
|
if (departmentActivityChart.value) {
|
|
const departmentChart = echarts.init(departmentActivityChart.value)
|
|
departmentChart.setOption({
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'shadow'
|
|
}
|
|
},
|
|
legend: {},
|
|
grid: {
|
|
left: '3%',
|
|
right: '4%',
|
|
bottom: '3%',
|
|
containLabel: true
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: ['技术部', '市场部', '销售部', '人事部', '财务部']
|
|
},
|
|
yAxis: {
|
|
type: 'value'
|
|
},
|
|
series: [
|
|
{
|
|
name: '活跃度',
|
|
type: 'bar',
|
|
data: [92, 85, 88, 79, 82]
|
|
},
|
|
{
|
|
name: '登录次数',
|
|
type: 'bar',
|
|
data: [320, 280, 310, 240, 260]
|
|
},
|
|
{
|
|
name: '操作次数',
|
|
type: 'bar',
|
|
data: [2800, 2100, 2400, 1800, 2000]
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
// 初始化每日活跃用户数图表
|
|
if (dailyActiveUsersChart.value) {
|
|
const dailyActiveChart = echarts.init(dailyActiveUsersChart.value)
|
|
dailyActiveChart.setOption({
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
|
},
|
|
yAxis: {
|
|
type: 'value'
|
|
},
|
|
series: [
|
|
{
|
|
data: [120, 132, 145, 135, 128, 68, 42],
|
|
type: 'line',
|
|
areaStyle: {}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
// 初始化平均在线时长图表
|
|
if (onlineTimeChart.value) {
|
|
const onlineChart = echarts.init(onlineTimeChart.value)
|
|
onlineChart.setOption({
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: ['技术部', '市场部', '销售部', '人事部', '财务部']
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
axisLabel: {
|
|
formatter: '{value} 小时'
|
|
}
|
|
},
|
|
series: [
|
|
{
|
|
name: '平均在线时长',
|
|
type: 'bar',
|
|
data: [7.5, 6.8, 7.2, 6.5, 6.9]
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
// 初始化考勤趋势图表
|
|
if (attendanceTrendChart.value) {
|
|
const attendanceChart = echarts.init(attendanceTrendChart.value)
|
|
attendanceChart.setOption({
|
|
tooltip: {
|
|
trigger: 'axis'
|
|
},
|
|
legend: {
|
|
data: ['出勤率', '迟到率', '早退率']
|
|
},
|
|
grid: {
|
|
left: '3%',
|
|
right: '4%',
|
|
bottom: '3%',
|
|
containLabel: true
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
boundaryGap: false,
|
|
data: ['1月', '2月', '3月', '4月', '5月', '6月']
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
axisLabel: {
|
|
formatter: '{value}%'
|
|
}
|
|
},
|
|
series: [
|
|
{
|
|
name: '出勤率',
|
|
type: 'line',
|
|
data: [96.2, 97.1, 96.8, 97.5, 98.2, 97.8]
|
|
},
|
|
{
|
|
name: '迟到率',
|
|
type: 'line',
|
|
data: [2.8, 2.2, 2.5, 1.8, 1.2, 1.5]
|
|
},
|
|
{
|
|
name: '早退率',
|
|
type: 'line',
|
|
data: [1.0, 0.7, 0.7, 0.7, 0.6, 0.7]
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
// 监听窗口大小变化,调整图表大小
|
|
window.addEventListener('resize', () => {
|
|
if (departmentActivityChart.value) {
|
|
const departmentChart = echarts.getInstanceByDom(departmentActivityChart.value)
|
|
departmentChart?.resize()
|
|
}
|
|
if (dailyActiveUsersChart.value) {
|
|
const dailyActiveChart = echarts.getInstanceByDom(dailyActiveUsersChart.value)
|
|
dailyActiveChart?.resize()
|
|
}
|
|
if (onlineTimeChart.value) {
|
|
const onlineChart = echarts.getInstanceByDom(onlineTimeChart.value)
|
|
onlineChart?.resize()
|
|
}
|
|
if (attendanceTrendChart.value) {
|
|
const attendanceChart = echarts.getInstanceByDom(attendanceTrendChart.value)
|
|
attendanceChart?.resize()
|
|
}
|
|
})
|
|
})
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.general-card {
|
|
margin-bottom: 20px;
|
|
}
|
|
</style> |