From 1b736033f6d01b774d58b4c2d7cd2ce8607a44fa Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期日, 28 十二月 2025 20:28:46 +0800
Subject: [PATCH] 页面完善
---
src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue | 680 +++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 522 insertions(+), 158 deletions(-)
diff --git a/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue b/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue
index 8c5d0bd..0771f71 100644
--- a/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue
+++ b/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue
@@ -12,6 +12,7 @@
size="small"
@click="exportReport"
icon="el-icon-download"
+ type="primary"
>
瀵煎嚭鎶ヨ〃
</el-button>
@@ -20,7 +21,7 @@
<!-- 缁熻姒傝 -->
<el-row :gutter="20" class="stats-overview">
- <el-col :span="6">
+ <el-col :xs="12" :sm="6" class="stat-col">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon attendance-icon">
@@ -33,7 +34,7 @@
</div>
</el-card>
</el-col>
- <el-col :span="6">
+ <el-col :xs="12" :sm="6" class="stat-col">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon present-icon">
@@ -46,7 +47,7 @@
</div>
</el-card>
</el-col>
- <el-col :span="6">
+ <el-col :xs="12" :sm="6" class="stat-col">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon abnormal-icon">
@@ -59,7 +60,7 @@
</div>
</el-card>
</el-col>
- <el-col :span="6">
+ <el-col :xs="12" :sm="6" class="stat-col">
<el-card shadow="hover" class="stat-card">
<div class="stat-content">
<div class="stat-icon rate-icon">
@@ -74,98 +75,150 @@
</el-col>
</el-row>
- <!-- 鍥捐〃鍖哄煙 -->
+ <!-- 鍥捐〃鍖哄煙 - 浼樺寲甯冨眬 -->
<el-row :gutter="20" class="charts-section">
- <el-col :span="12">
- <el-card header="鍑哄嫟瓒嬪娍" shadow="never">
+ <el-col :xs="24" :lg="12" class="chart-col">
+ <el-card class="chart-card" shadow="never">
+ <template #header>
+ <div class="chart-header">
+ <span class="chart-title">鍑哄嫟瓒嬪娍鍒嗘瀽</span>
+ <div class="chart-legend">
+ <span class="legend-item">
+ <span class="legend-color bar-color"></span>
+ 鍑哄嫟澶╂暟
+ </span>
+ <span class="legend-item">
+ <span class="legend-color line-color"></span>
+ 鍑哄嫟鐜�
+ </span>
+ </div>
+ </div>
+ </template>
<div id="attendanceTrendChart" class="chart-container"></div>
</el-card>
</el-col>
- <el-col :span="12">
- <el-card header="鑰冨嫟鍒嗗竷" shadow="never">
+ <el-col :xs="24" :lg="12" class="chart-col">
+ <el-card class="chart-card" shadow="never">
+ <template #header>
+ <div class="chart-header">
+ <span class="chart-title">鑰冨嫟鍒嗗竷</span>
+ </div>
+ </template>
<div id="attendanceDistributionChart" class="chart-container"></div>
</el-card>
</el-col>
</el-row>
<!-- 璇︾粏缁熻琛ㄦ牸 -->
- <el-card header="璇︾粏缁熻" class="detail-table-card" shadow="never">
- <el-table :data="detailedStats" border style="width: 100%">
- <el-table-column prop="month" label="鏈堜唤" />
- <el-table-column prop="workDays" label="搴斿嚭鍕ゅぉ鏁�" />
- <el-table-column prop="actualDays" label="瀹為檯鍑哄嫟" />
- <el-table-column prop="lateTimes" label="杩熷埌娆℃暟" >
- <template #default="scope">
- <el-tag v-if="scope.row.lateTimes > 0" type="warning" size="small">
- {{ scope.row.lateTimes }}
- </el-tag>
- <span v-else>{{ scope.row.lateTimes }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="leaveEarlyTimes" label="鏃╅��娆℃暟" >
- <template #default="scope">
- <el-tag v-if="scope.row.leaveEarlyTimes > 0" type="warning" size="small">
- {{ scope.row.leaveEarlyTimes }}
- </el-tag>
- <span v-else>{{ scope.row.leaveEarlyTimes }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="absenceDays" label="缂哄嫟澶╂暟" >
- <template #default="scope">
- <el-tag v-if="scope.row.absenceDays > 0" type="danger" size="small">
- {{ scope.row.absenceDays }}
- </el-tag>
- <span v-else>{{ scope.row.absenceDays }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="businessTripDays" label="鍑哄樊澶╂暟" >
- <template #default="scope">
- <el-tag v-if="scope.row.businessTripDays > 0" type="primary" size="small">
- {{ scope.row.businessTripDays }}
- </el-tag>
- <span v-else>{{ scope.row.businessTripDays }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="attendanceRate" label="鍑哄嫟鐜�" >
- <template #default="scope">
- <el-progress
- :percentage="scope.row.attendanceRate"
- :show-text="false"
- :color="getProgressColor(scope.row.attendanceRate)"
- />
- <span>{{ scope.row.attendanceRate }}%</span>
- </template>
- </el-table-column>
- </el-table>
+ <el-card class="detail-card" shadow="never">
+ <template #header>
+ <div class="card-header">
+ <span class="card-title">鏈堝害璇︾粏缁熻</span>
+ <el-button size="mini" type="text" @click="toggleTableExpand">
+ {{ tableExpanded ? '鏀惰捣' : '灞曞紑' }}
+ <i :class="tableExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
+ </el-button>
+ </div>
+ </template>
+ <el-collapse-transition>
+ <div v-show="tableExpanded">
+ <el-table
+ :data="detailedStats"
+ border
+ style="width: 100%"
+ class="stats-table"
+ :row-class-name="getRowClassName"
+ >
+ <!-- 琛ㄦ牸鍒楀畾涔変繚鎸佷笉鍙� -->
+ <el-table-column prop="month" label="鏈堜唤" align="center" />
+ <el-table-column prop="workDays" label="搴斿嚭鍕ゅぉ鏁�" align="center" />
+ <el-table-column prop="actualDays" label="瀹為檯鍑哄嫟" align="center" />
+ <el-table-column prop="lateTimes" label="杩熷埌娆℃暟" align="center">
+ <template #default="scope">
+ <el-tag v-if="scope.row.lateTimes > 0" type="warning" size="small">
+ {{ scope.row.lateTimes }}
+ </el-tag>
+ <span v-else>{{ scope.row.lateTimes }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="leaveEarlyTimes" label="鏃╅��娆℃暟" align="center">
+ <template #default="scope">
+ <el-tag v-if="scope.row.leaveEarlyTimes > 0" type="warning" size="small">
+ {{ scope.row.leaveEarlyTimes }}
+ </el-tag>
+ <span v-else>{{ scope.row.leaveEarlyTimes }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="absenceDays" label="缂哄嫟澶╂暟" align="center">
+ <template #default="scope">
+ <el-tag v-if="scope.row.absenceDays > 0" type="danger" size="small">
+ {{ scope.row.absenceDays }}
+ </el-tag>
+ <span v-else>{{ scope.row.absenceDays }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="businessTripDays" label="鍑哄樊澶╂暟" align="center">
+ <template #default="scope">
+ <el-tag v-if="scope.row.businessTripDays > 0" type="primary" size="small">
+ {{ scope.row.businessTripDays }}
+ </el-tag>
+ <span v-else>{{ scope.row.businessTripDays }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="attendanceRate" label="鍑哄嫟鐜�" align="center" min-width="100">
+ <template #default="scope">
+ <div class="progress-cell">
+ <el-progress
+ :percentage="scope.row.attendanceRate"
+ :show-text="false"
+ :color="getProgressColor(scope.row.attendanceRate)"
+ class="rate-progress"
+ />
+ <span class="rate-text" :class="getRateTextClass(scope.row.attendanceRate)">
+ {{ scope.row.attendanceRate }}%
+ </span>
+ </div>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </el-collapse-transition>
</el-card>
<!-- 寮傚父璁板綍 -->
- <el-card header="寮傚父璁板綍" class="abnormal-records-card" shadow="never">
- <el-table :data="abnormalRecords" border style="width: 100%">
- <el-table-column prop="date" label="鏃ユ湡" />
- <el-table-column prop="type" label="寮傚父绫诲瀷" >
+ <el-card class="abnormal-card" shadow="never">
+ <template #header>
+ <div class="card-header">
+ <span class="card-title">寮傚父璁板綍鏄庣粏</span>
+ </div>
+ </template>
+ <el-table :data="abnormalRecords" border class="abnormal-table">
+ <!-- 寮傚父璁板綍琛ㄦ牸鍒楀畾涔変繚鎸佷笉鍙� -->
+ <el-table-column prop="date" label="鏃ユ湡" align="center" />
+ <el-table-column prop="type" label="寮傚父绫诲瀷" align="center">
<template #default="scope">
- <el-tag :type="getAbnormalType(scope.row.type)" size="small">
+ <el-tag :type="getAbnormalType(scope.row.type)" size="small" effect="light">
{{ scope.row.type }}
</el-tag>
</template>
</el-table-column>
- <el-table-column prop="description" label="鎻忚堪" min-width="200" />
- <el-table-column prop="duration" label="鏃堕暱" />
- <el-table-column prop="status" label="鐘舵��" >
+ <el-table-column prop="description" label="鎻忚堪" align="center" min-width="200" />
+ <el-table-column prop="duration" label="鏃堕暱" align="center" />
+ <el-table-column prop="status" label="鐘舵��" align="center">
<template #default="scope">
<el-tag
:type="scope.row.status === '宸插鐞�' ? 'success' : 'warning'"
size="small"
+ effect="light"
>
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
- <el-table-column label="鎿嶄綔" >
+ <el-table-column label="鎿嶄綔" align="center" width="80">
<template #default="scope">
<el-button type="text" size="mini" @click="viewAbnormalDetail(scope.row)">
- 鏌ョ湅
+ 璇︽儏
</el-button>
</template>
</el-table-column>
@@ -201,7 +254,8 @@
detailedStats: [],
abnormalRecords: [],
trendChart: null,
- distributionChart: null
+ distributionChart: null,
+ tableExpanded: true
}
},
mounted() {
@@ -211,7 +265,7 @@
})
},
beforeDestroy() {
- // 閿�姣佸浘琛ㄥ疄渚�
+ // 閿�姣佸浘琛ㄥ疄渚媅3](@ref)
if (this.trendChart) {
this.trendChart.dispose()
}
@@ -221,7 +275,7 @@
},
methods: {
initData() {
- // 鍒濆鍖栨瑙堟暟鎹�
+ // 鍒濆鍖栨暟鎹�昏緫淇濇寔涓嶅彉
this.overview = {
totalDays: this.stats.totalDays || 22,
presentDays: this.stats.presentDays || 20,
@@ -229,7 +283,6 @@
attendanceRate: this.stats.attendanceRate || 90.9
}
- // 鍒濆鍖栬缁嗙粺璁�
this.detailedStats = [
{ month: '2024-12', workDays: 22, actualDays: 20, lateTimes: 2,
leaveEarlyTimes: 1, absenceDays: 0, businessTripDays: 3, attendanceRate: 90.9 },
@@ -239,7 +292,6 @@
leaveEarlyTimes: 0, absenceDays: 0, businessTripDays: 1, attendanceRate: 95.7 }
]
- // 鍒濆鍖栧紓甯歌褰�
this.abnormalRecords = [
{ date: '2024-12-15', type: '杩熷埌', description: '鏃╀笂杩熷埌30鍒嗛挓', duration: '30鍒嗛挓', status: '宸插鐞�' },
{ date: '2024-12-08', type: '鏃╅��', description: '涓嬪崍鎻愬墠1灏忔椂绂诲紑', duration: '1灏忔椂', status: '宸插鐞�' },
@@ -261,43 +313,89 @@
const option = {
tooltip: {
trigger: 'axis',
+ backgroundColor: 'rgba(255, 255, 255, 0.95)',
+ borderColor: '#ebeef5',
+ borderWidth: 1,
+ textStyle: {
+ color: '#606266'
+ },
formatter: function(params) {
- let result = params[0].axisValue + '<br/>'
+ let result = `<div style="font-weight: 600; margin-bottom: 8px;">${params[0].axisValue}</div>`
params.forEach(param => {
- result += `${param.seriesName}: ${param.value}<br/>`
+ const icon = param.seriesType === 'bar' ? '鈼�' : '鈼�'
+ result += `<div>${icon} ${param.seriesName}: <span style="font-weight: 600; color: ${param.color}">${param.value}${param.seriesName === '鍑哄嫟鐜�' ? '%' : ''}</span></div>`
})
return result
}
},
legend: {
data: ['鍑哄嫟澶╂暟', '寮傚父澶╂暟', '鍑哄嫟鐜�'],
- bottom: 10
+ bottom: 10,
+ textStyle: {
+ color: '#606266'
+ },
+ itemWidth: 12,
+ itemHeight: 12
},
grid: {
left: '3%',
- right: '4%',
+ right: '3%',
bottom: '15%',
- top: '10%',
+ top: '15%',
containLabel: true
},
xAxis: {
type: 'category',
- data: ['10鏈�', '11鏈�', '12鏈�']
+ data: ['10鏈�', '11鏈�', '12鏈�'],
+ axisLine: {
+ lineStyle: {
+ color: '#dcdfe6'
+ }
+ },
+ axisLabel: {
+ color: '#606266'
+ }
},
yAxis: [
{
type: 'value',
name: '澶╂暟',
min: 0,
- max: 30
+ max: 30,
+ axisLine: {
+ show: true,
+ lineStyle: {
+ color: '#dcdfe6'
+ }
+ },
+ axisLabel: {
+ color: '#606266',
+ formatter: '{value}'
+ },
+ splitLine: {
+ lineStyle: {
+ color: '#f0f2f5',
+ type: 'dashed'
+ }
+ }
},
{
type: 'value',
name: '鍑哄嫟鐜�(%)',
min: 0,
max: 100,
+ axisLine: {
+ show: true,
+ lineStyle: {
+ color: '#dcdfe6'
+ }
+ },
axisLabel: {
+ color: '#606266',
formatter: '{value}%'
+ },
+ splitLine: {
+ show: false
}
}
],
@@ -305,19 +403,39 @@
{
name: '鍑哄嫟澶╂暟',
type: 'bar',
- barWidth: '30%',
+ barWidth: '25%',
data: [22, 19, 20],
itemStyle: {
- color: '#409EFF'
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: '#409EFF' },
+ { offset: 1, color: '#66b1ff' }
+ ]),
+ borderRadius: [2, 2, 0, 0]
+ },
+ emphasis: {
+ itemStyle: {
+ shadowBlur: 10,
+ shadowColor: 'rgba(64, 158, 255, 0.5)'
+ }
}
},
{
name: '寮傚父澶╂暟',
type: 'bar',
- barWidth: '30%',
+ barWidth: '25%',
data: [1, 2, 2],
itemStyle: {
- color: '#F56C6C'
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: '#F56C6C' },
+ { offset: 1, color: '#f78989' }
+ ]),
+ borderRadius: [2, 2, 0, 0]
+ },
+ emphasis: {
+ itemStyle: {
+ shadowBlur: 10,
+ shadowColor: 'rgba(245, 108, 108, 0.5)'
+ }
}
},
{
@@ -325,14 +443,28 @@
type: 'line',
yAxisIndex: 1,
data: [95.7, 90.5, 90.9],
+ symbol: 'circle',
+ symbolSize: 8,
itemStyle: {
- color: '#67C23A'
+ color: '#67C23A',
+ borderColor: '#fff',
+ borderWidth: 2
},
lineStyle: {
- width: 3
- }
+ width: 3,
+ shadowColor: 'rgba(103, 194, 58, 0.3)',
+ shadowBlur: 8,
+ shadowOffsetY: 2
+ },
+ emphasis: {
+ scale: true
+ },
+ animationEasing: 'cubicInOut',
+ animationDuration: 2000
}
- ]
+ ],
+ animation: true,
+ animationDuration: 1500
}
this.trendChart.setOption(option)
},
@@ -345,13 +477,27 @@
const option = {
tooltip: {
trigger: 'item',
- formatter: '{a} <br/>{b}: {c} ({d}%)'
+ backgroundColor: 'rgba(255, 255, 255, 0.95)',
+ borderColor: '#ebeef5',
+ borderWidth: 1,
+ textStyle: {
+ color: '#606266'
+ },
+ formatter: '{a} <br/>{b}: {c}澶� ({d}%)'
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
- data: ['姝e父鍑哄嫟', '杩熷埌', '鏃╅��', '缂哄嫟', '鍑哄樊']
+ textStyle: {
+ color: '#606266',
+ fontSize: 12
+ },
+ itemWidth: 12,
+ itemHeight: 12,
+ formatter: function(name) {
+ return `${name}`
+ }
},
series: [
{
@@ -359,24 +505,38 @@
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
- avoidLabelOverlap: false,
+ avoidLabelOverlap: true,
itemStyle: {
borderColor: '#fff',
- borderWidth: 2
+ borderWidth: 2,
+ borderRadius: 3,
+ shadowBlur: 5,
+ shadowColor: 'rgba(0, 0, 0, 0.1)'
},
label: {
- show: false,
- position: 'center'
+ show: true,
+ formatter: '{b}\n{d}%',
+ textStyle: {
+ fontSize: 12,
+ fontWeight: 'normal'
+ }
},
emphasis: {
label: {
show: true,
- fontSize: 18,
+ fontSize: 14,
fontWeight: 'bold'
+ },
+ itemStyle: {
+ shadowBlur: 10,
+ shadowOffsetX: 0,
+ shadowColor: 'rgba(0, 0, 0, 0.2)'
}
},
labelLine: {
- show: false
+ length: 15,
+ length2: 10,
+ smooth: true
},
data: [
{ value: 20, name: '姝e父鍑哄嫟', itemStyle: { color: '#67C23A' } },
@@ -384,23 +544,30 @@
{ value: 1, name: '鏃╅��', itemStyle: { color: '#F56C6C' } },
{ value: 0, name: '缂哄嫟', itemStyle: { color: '#909399' } },
{ value: 3, name: '鍑哄樊', itemStyle: { color: '#409EFF' } }
- ]
+ ],
+ animationType: 'scale',
+ animationEasing: 'elasticOut',
+ animationDelay: function(idx) {
+ return Math.random() * 200
+ }
}
- ]
+ ],
+ animation: true,
+ animationDuration: 1000
}
this.distributionChart.setOption(option)
},
setupChartResize() {
- // 鐩戝惉绐楀彛鍙樺寲锛岄噸鏂版覆鏌撳浘琛�
- const handleResize = () => {
+ // 浣跨敤鑺傛祦鍑芥暟浼樺寲鎬ц兘[3](@ref)
+ const handleResize = this.throttle(() => {
if (this.trendChart) {
this.trendChart.resize()
}
if (this.distributionChart) {
this.distributionChart.resize()
}
- }
+ }, 300)
window.addEventListener('resize', handleResize)
this.$once('hook:beforeDestroy', () => {
@@ -408,13 +575,42 @@
})
},
+ throttle(func, wait) {
+ let timeout = null
+ return function() {
+ const context = this
+ const args = arguments
+ if (!timeout) {
+ timeout = setTimeout(() => {
+ timeout = null
+ func.apply(context, args)
+ }, wait)
+ }
+ }
+ },
+
+ toggleTableExpand() {
+ this.tableExpanded = !this.tableExpanded
+ },
+
+ getRowClassName({ rowIndex }) {
+ return rowIndex % 2 === 1 ? 'even-row' : 'odd-row'
+ },
+
+ getRateTextClass(rate) {
+ if (rate >= 95) return 'rate-excellent'
+ if (rate >= 90) return 'rate-good'
+ if (rate >= 80) return 'rate-average'
+ return 'rate-poor'
+ },
+
+ // 鍏朵粬鏂规硶淇濇寔涓嶅彉
handlePeriodChange(period) {
this.reportPeriod = period
this.updateChartData()
},
updateChartData() {
- // 鏍规嵁閫夋嫨鐨勫懆鏈熸洿鏂板浘琛ㄦ暟鎹�
let data
switch (this.reportPeriod) {
case 'month':
@@ -429,12 +625,10 @@
default:
data = this.getMonthlyData()
}
-
this.updateCharts(data)
},
getMonthlyData() {
- // 妯℃嫙鏈堝害鏁版嵁
return {
xAxis: ['10鏈�', '11鏈�', '12鏈�'],
attendance: [22, 19, 20],
@@ -444,7 +638,6 @@
},
getQuarterlyData() {
- // 妯℃嫙瀛e害鏁版嵁
return {
xAxis: ['Q1', 'Q2', 'Q3', 'Q4'],
attendance: [65, 62, 58, 61],
@@ -454,7 +647,6 @@
},
getYearlyData() {
- // 妯℃嫙骞村害鏁版嵁
return {
xAxis: ['2022', '2023', '2024'],
attendance: [240, 248, 252],
@@ -470,7 +662,7 @@
option.series[0].data = data.attendance
option.series[1].data = data.abnormal
option.series[2].data = data.rate
- this.trendChart.setOption(option)
+ this.trendChart.setOption(option, { notMerge: false })
}
},
@@ -493,12 +685,10 @@
viewAbnormalDetail(record) {
this.$message.info(`鏌ョ湅寮傚父璁板綍: ${record.date} - ${record.type}`)
- // 杩欓噷鍙互鎵撳紑璇︽儏瀵硅瘽妗�
},
exportReport() {
this.$message.success('鎶ヨ〃瀵煎嚭鍔熻兘寮�鍙戜腑')
- // 杩欓噷鍙互瀹炵幇瀵煎嚭PDF鎴朎xcel鍔熻兘
}
}
}
@@ -506,9 +696,9 @@
<style scoped>
.personal-attendance-report {
- padding: 20px;
- background: #fff;
- border-radius: 8px;
+ padding: 24px;
+ background: #f8f9fa;
+ min-height: 100vh;
}
.report-header {
@@ -516,15 +706,17 @@
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
- padding-bottom: 16px;
- border-bottom: 1px solid #ebeef5;
+ padding: 0;
}
.report-header h4 {
margin: 0;
color: #303133;
- font-size: 20px;
+ font-size: 24px;
font-weight: 600;
+ background: linear-gradient(135deg, #409EFF, #67C23A);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
}
.header-actions {
@@ -534,51 +726,78 @@
}
.stats-overview {
- margin-bottom: 24px;
+ margin-bottom: 32px;
+}
+
+.stat-col {
+ margin-bottom: 20px;
}
.stat-card {
- border-radius: 8px;
- transition: all 0.3s ease;
+ border-radius: 12px;
+ border: none;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ background: #fff;
+ position: relative;
+ overflow: hidden;
+}
+
+.stat-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 3px;
+ background: linear-gradient(90deg, var(--gradient-start), var(--gradient-end));
}
.stat-card:hover {
- transform: translateY(-2px);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-4px);
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
}
.stat-content {
display: flex;
align-items: center;
- padding: 16px;
+ padding: 20px;
}
.stat-icon {
- width: 60px;
- height: 60px;
- border-radius: 50%;
+ width: 64px;
+ height: 64px;
+ border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
- font-size: 24px;
+ font-size: 28px;
color: white;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.attendance-icon {
- background: linear-gradient(135deg, #409EFF, #79BBFF);
+ --gradient-start: #409EFF;
+ --gradient-end: #66b1ff;
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
}
.present-icon {
- background: linear-gradient(135deg, #67C23A, #95D475);
+ --gradient-start: #67C23A;
+ --gradient-end: #85ce61;
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
}
.abnormal-icon {
- background: linear-gradient(135deg, #E6A23C, #EEBD6D);
+ --gradient-start: #E6A23C;
+ --gradient-end: #ebb563;
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
}
.rate-icon {
- background: linear-gradient(135deg, #F56C6C, #F89898);
+ --gradient-start: #F56C6C;
+ --gradient-end: #f78989;
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
}
.stat-info {
@@ -586,47 +805,162 @@
}
.stat-value {
- font-size: 28px;
- font-weight: bold;
+ font-size: 32px;
+ font-weight: 700;
color: #303133;
margin-bottom: 4px;
+ line-height: 1;
}
.stat-label {
color: #909399;
font-size: 14px;
+ font-weight: 500;
}
.charts-section {
+ margin-bottom: 32px;
+}
+
+.chart-col {
margin-bottom: 24px;
}
-.chart-container {
- width: 100%;
- height: 300px;
+.chart-card {
+ border-radius: 12px;
+ border: none;
+ background: #fff;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
-.detail-table-card,
-.abnormal-records-card {
- margin-bottom: 20px;
+.chart-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0;
+}
+
+.chart-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+}
+
+.chart-legend {
+ display: flex;
+ gap: 16px;
+ align-items: center;
+}
+
+.legend-item {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 12px;
+ color: #606266;
+}
+
+.legend-color {
+ width: 12px;
+ height: 12px;
+ border-radius: 2px;
+}
+
+.bar-color {
+ background: linear-gradient(135deg, #409EFF, #66b1ff);
+}
+
+.line-color {
+ background: linear-gradient(135deg, #67C23A, #85ce61);
+}
+
+.chart-container {
+ width: 40vw;
+ height: 320px;
+ position: relative;
+}
+
+.detail-card,
+.abnormal-card {
+ border-radius: 12px;
+ border: none;
+ background: #fff;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+ margin-bottom: 24px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0;
+}
+
+.card-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+}
+
+.stats-table {
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.stats-table :deep(.el-table__row) {
+ transition: background-color 0.3s;
+}
+
+.stats-table :deep(.el-table__row:hover) {
+ background-color: #f5f7fa;
+}
+
+.stats-table :deep(.even-row) {
+ background-color: #fafbfc;
+}
+
+.progress-cell {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.rate-progress {
+ flex: 1;
+}
+
+.rate-text {
+ font-size: 12px;
+ font-weight: 600;
+ min-width: 40px;
+}
+
+.rate-excellent { color: #67C23A; }
+.rate-good { color: #E6A23C; }
+.rate-average { color: #F56C6C; }
+.rate-poor { color: #909399; }
+
+.abnormal-table {
+ border-radius: 8px;
+ overflow: hidden;
}
/* 鍝嶅簲寮忚璁� */
@media (max-width: 1200px) {
- .stats-overview .el-col {
- margin-bottom: 16px;
+ .chart-container {
+ height: 280px;
}
}
@media (max-width: 768px) {
.personal-attendance-report {
- padding: 12px;
+ padding: 16px;
}
.report-header {
flex-direction: column;
align-items: flex-start;
- gap: 12px;
+ gap: 16px;
}
.header-actions {
@@ -635,14 +969,42 @@
}
.stat-content {
- padding: 12px;
+ padding: 16px;
+ flex-direction: column;
+ text-align: center;
}
.stat-icon {
- width: 50px;
- height: 50px;
- font-size: 20px;
- margin-right: 12px;
+ margin-right: 0;
+ margin-bottom: 12px;
+ width: 56px;
+ height: 56px;
+ font-size: 24px;
+ }
+
+ .stat-value {
+ font-size: 28px;
+ }
+
+ .chart-container {
+ height: 240px;
+ }
+
+ .chart-header {
+ flex-direction: column;
+ gap: 12px;
+ align-items: flex-start;
+ }
+
+ .chart-legend {
+ align-self: stretch;
+ justify-content: space-around;
+ }
+}
+
+@media (max-width: 480px) {
+ .stat-col {
+ margin-bottom: 16px;
}
.stat-value {
@@ -650,7 +1012,7 @@
}
.chart-container {
- height: 250px;
+ height: 200px;
}
}
@@ -662,21 +1024,23 @@
opacity: 0;
}
-/* 琛ㄦ牸鏍峰紡浼樺寲 */
-.el-table {
- border-radius: 4px;
- overflow: hidden;
+/* 婊氬姩鏉℃牱寮忎紭鍖� */
+:deep(::-webkit-scrollbar) {
+ width: 6px;
+ height: 6px;
}
-.el-table::before {
- display: none;
+:deep(::-webkit-scrollbar-track) {
+ background: #f1f1f1;
+ border-radius: 3px;
}
-/* 鍗$墖鏍囬鏍峰紡 */
-.el-card__header {
- background: #f8f9fa;
- border-bottom: 1px solid #ebeef5;
- font-weight: 600;
- color: #303133;
+:deep(::-webkit-scrollbar-thumb) {
+ background: #c1c1c1;
+ border-radius: 3px;
+}
+
+:deep(::-webkit-scrollbar-thumb:hover) {
+ background: #a8a8a8;
}
</style>
--
Gitblit v1.9.3