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/AttendanceCalendar.vue | 542 +++--------------------------------------------------
1 files changed, 35 insertions(+), 507 deletions(-)
diff --git a/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue b/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
index c43976e..5f90278 100644
--- a/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
+++ b/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
@@ -1,132 +1,23 @@
<template>
<div class="attendance-calendar">
- <!-- 鏃ュ巻澶撮儴缁熻淇℃伅 -->
- <div class="calendar-stats">
- <div class="stat-item">
- <span class="stat-dot present"></span>
- <span>姝e父鍑哄嫟: {{ stats.presentDays }}澶�</span>
- </div>
- <div class="stat-item">
- <span class="stat-dot absent"></span>
- <span>缂哄嫟/寮傚父: {{ stats.absentDays }}澶�</span>
- </div>
- <div class="stat-item">
- <span class="stat-dot trip"></span>
- <span>鍑哄樊: {{ stats.tripDays }}澶�</span>
- </div>
- <div class="stat-item">
- <span class="stat-dot late"></span>
- <span>杩熷埌/鏃╅��: {{ stats.lateDays }}澶�</span>
- </div>
- </div>
-
- <!-- Element UI 鏃ュ巻缁勪欢 -->
- <el-calendar v-model="calendarValue" :first-day-of-week="1">
+ <el-calendar v-model="calendarValue">
<template #date-cell="{ data }">
- <div
- class="calendar-date"
- :class="getDateStatusClass(data.day)"
- @click="handleDateClick(data)"
- >
+ <div class="calendar-date">
<div class="date-number">{{ data.day.split('-')[2] }}</div>
-
- <!-- 鐘舵�佽儗鏅壊鍧� -->
- <div class="status-background" :class="getBackgroundStatus(data.day)"></div>
-
<div class="date-events">
- <!-- 鍑哄嫟鐘舵�佹爣璁� -->
- <div v-if="getAttendanceStatus(data.day) !== 'absent'" class="status-mark">
- <el-tooltip
- :content="getAttendanceTooltip(data.day)"
- placement="top"
- >
- <span class="status-dot" :class="getAttendanceStatus(data.day)"></span>
- </el-tooltip>
- </div>
-
- <!-- 鍑哄樊鏍囪 -->
- <div v-if="hasBusinessTrip(data.day)" class="trip-mark">
- <el-tooltip content="鍑哄樊" placement="top">
- <i class="el-icon-location-outline"></i>
- </el-tooltip>
- </div>
-
- <!-- 缂哄嫟鏍囪 -->
- <div v-if="getAttendanceStatus(data.day) === 'absent'" class="absent-mark">
- <el-tooltip content="缂哄嫟" placement="top">
- <i class="el-icon-close"></i>
- </el-tooltip>
- </div>
- </div>
-
- <!-- 绠�鐣ヤ俊鎭樉绀� -->
- <div class="brief-info">
- <div v-if="getAttendanceStatus(data.day) === 'present'" class="info-item present-info">
- 鈭�
- </div>
- <div v-else-if="getAttendanceStatus(data.day) === 'late'" class="info-item late-info">
- !
- </div>
- <div v-else-if="getAttendanceStatus(data.day) === 'absent'" class="info-item absent-info">
- 脳
- </div>
- <div v-if="hasBusinessTrip(data.day)" class="info-item trip-info">
- 鉁�
- </div>
- </div>
-
- <!-- 鏃ユ湡璇︾粏淇℃伅锛堟偓娴樉绀猴級 -->
- <div class="date-details">
<div
v-for="event in getDateEvents(data.day)"
:key="event.id"
- class="detail-item"
+ class="event-item"
+ :class="event.type"
>
- <span class="detail-type">{{ event.type === 'attendance' ? '鍑哄嫟' : '鍑哄樊' }}</span>
- <span class="detail-info">{{ event.text }}</span>
- </div>
- <div v-if="getDateEvents(data.day).length === 0" class="detail-item">
- <span class="detail-type">鏃犺褰�</span>
+ <span class="event-dot"></span>
+ <span class="event-text">{{ event.text }}</span>
</div>
</div>
</div>
</template>
</el-calendar>
-
- <!-- 鏃ユ湡璇︽儏瀵硅瘽妗� -->
- <el-dialog
- :title="`${selectedDate} 鑰冨嫟璇︽儏`"
- v-model="detailDialogVisible"
- width="500px"
- >
- <div v-if="selectedDateInfo">
- <div class="detail-section">
- <h4>鍑哄嫟淇℃伅</h4>
- <div v-if="selectedDateInfo.attendance">
- <p>涓婄彮鏃堕棿: {{ selectedDateInfo.attendance.checkIn || '鏈墦鍗�' }}</p>
- <p>涓嬬彮鏃堕棿: {{ selectedDateInfo.attendance.checkOut || '鏈墦鍗�' }}</p>
- <p>鐘舵��:
- <el-tag :type="getStatusTagType(selectedDateInfo.attendance.status)">
- {{ getStatusText(selectedDateInfo.attendance.status) }}
- </el-tag>
- </p>
- </div>
- <div v-else>
- <p class="no-data">鏃犲嚭鍕よ褰�</p>
- </div>
- </div>
-
- <div class="detail-section" v-if="selectedDateInfo.businessTrip">
- <h4>鍑哄樊淇℃伅</h4>
- <p>鐩殑鍦�: {{ selectedDateInfo.businessTrip.destination }}</p>
- <p>浜嬬敱: {{ selectedDateInfo.businessTrip.reason }}</p>
- <p>閲岀▼: {{ selectedDateInfo.businessTrip.distance }}鍏噷</p>
- </div>
- </div>
- <template #footer>
- <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
- </template>
- </el-dialog>
</div>
</template>
@@ -134,211 +25,41 @@
export default {
name: 'AttendanceCalendar',
props: {
- attendanceData: {
- type: Array,
- default: () => []
- },
- businessTripData: {
- type: Array,
- default: () => []
- }
+ attendanceData: Array,
+ businessTripData: Array
},
data() {
return {
- calendarValue: new Date(),
- detailDialogVisible: false,
- selectedDate: '',
- selectedDateInfo: null,
- stats: {
- presentDays: 0,
- absentDays: 0,
- tripDays: 0,
- lateDays: 0
- }
- }
- },
- mounted() {
- this.calculateStats()
- },
- watch: {
- attendanceData: {
- handler() {
- this.calculateStats()
- },
- deep: true
- },
- businessTripData: {
- handler() {
- this.calculateStats()
- },
- deep: true
+ calendarValue: new Date()
}
},
methods: {
- // 鑾峰彇鏃ユ湡鐘舵�佺被鍚�
- getDateStatusClass(date) {
- const classes = []
- if (this.isToday(date)) {
- classes.push('today')
- }
- if (this.isSelected(date)) {
- classes.push('selected')
- }
-
- // 娣诲姞鐘舵�佺被鍚�
- const status = this.getBackgroundStatus(date)
- if (status) {
- classes.push(status)
- }
-
- return classes
- },
-
- // 鑾峰彇鑳屾櫙鐘舵�侊紙鐢ㄤ簬鑳屾櫙鑹诧級
- getBackgroundStatus(date) {
- const attendance = this.attendanceData.find(item => item.date === date)
- const hasTrip = this.hasBusinessTrip(date)
-
- if (hasTrip) {
- return 'has-trip'
- }
-
- if (attendance) {
- switch (attendance.status) {
- case 'present': return 'has-attendance'
- case 'late': return 'has-late'
- case 'absent': return 'has-absent'
- default: return ''
- }
- }
-
- return ''
- },
-
- // 鍒ゆ柇鏄惁涓轰粖澶�
- isToday(date) {
- const today = new Date()
- const compareDate = new Date(date)
- return today.toDateString() === compareDate.toDateString()
- },
-
- // 鍒ゆ柇鏄惁琚�変腑
- isSelected(date) {
- return this.selectedDate === date
- },
-
- // 鑾峰彇鑰冨嫟鐘舵��
- getAttendanceStatus(date) {
- const attendance = this.attendanceData.find(item => item.date === date)
- if (!attendance) return 'absent'
-
- switch (attendance.status) {
- case 'present': return 'present'
- case 'late': return 'late'
- case 'absent': return 'absent'
- default: return 'absent'
- }
- },
-
- // 鑾峰彇鐘舵�佹枃鏈�
- getStatusText(status) {
- const statusMap = {
- present: '姝e父鍑哄嫟',
- late: '杩熷埌/鏃╅��',
- absent: '缂哄嫟/寮傚父'
- }
- return statusMap[status] || '鏈煡鐘舵��'
- },
-
- // 鑾峰彇鑰冨嫟鐘舵�佹彁绀�
- getAttendanceTooltip(date) {
- const statusMap = {
- present: '姝e父鍑哄嫟',
- late: '杩熷埌/鏃╅��',
- absent: '缂哄嫟/寮傚父'
- }
- return statusMap[this.getAttendanceStatus(date)] || '鏃犺褰�'
- },
-
- // 鍒ゆ柇鏄惁鏈夊嚭宸�
- hasBusinessTrip(date) {
- return this.businessTripData.some(item =>
- date >= item.startDate && date <= item.endDate
- )
- },
-
- // 鑾峰彇鏃ユ湡浜嬩欢
getDateEvents(date) {
const events = []
- const attendance = this.attendanceData.find(item => item.date === date)
+ // 妫�鏌ュ嚭鍕よ褰�
+ const attendance = this.attendanceData.find(item => item.date === date)
if (attendance) {
events.push({
id: `attendance-${date}`,
type: 'attendance',
- text: `${attendance.checkIn || '鏈墦鍗�'} - ${attendance.checkOut || '鏈墦鍗�'}`
+ text: `${attendance.checkIn}-${attendance.checkOut}`
})
}
+ // 妫�鏌ュ嚭宸褰�
const businessTrip = this.businessTripData.find(item =>
date >= item.startDate && date <= item.endDate
)
if (businessTrip) {
events.push({
id: `business-trip-${date}`,
- type: 'businessTrip',
- text: `鍓嶅線${businessTrip.destination}`
+ type: 'business-trip',
+ text: `鍑哄樊: ${businessTrip.endCity}`
})
}
return events
- },
-
- // 澶勭悊鏃ユ湡鐐瑰嚮浜嬩欢
- handleDateClick(data) {
- this.selectedDate = data.day
- this.selectedDateInfo = {
- attendance: this.attendanceData.find(item => item.date === data.day),
- businessTrip: this.businessTripData.find(item =>
- data.day >= item.startDate && data.day <= item.endDate
- )
- }
- this.detailDialogVisible = true
- },
-
- // 鑾峰彇鐘舵�佹爣绛剧被鍨�
- getStatusTagType(status) {
- const typeMap = {
- present: 'success',
- late: 'warning',
- absent: 'danger'
- }
- return typeMap[status] || 'info'
- },
-
- // 璁$畻缁熻淇℃伅
- calculateStats() {
- // 閲嶇疆缁熻
- this.stats = { presentDays: 0, absentDays: 0, tripDays: 0, lateDays: 0 }
-
- this.attendanceData.forEach(item => {
- switch (item.status) {
- case 'present': this.stats.presentDays++; break
- case 'late': this.stats.lateDays++; break
- case 'absent': this.stats.absentDays++; break
- }
- })
-
- // 璁$畻鍑哄樊澶╂暟
- const tripDays = new Set()
- this.businessTripData.forEach(item => {
- const start = new Date(item.startDate)
- const end = new Date(item.endDate)
- for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
- tripDays.add(d.toISOString().split('T')[0])
- }
- })
- this.stats.tripDays = tripDays.size
}
}
}
@@ -347,242 +68,49 @@
<style scoped>
.attendance-calendar {
padding: 20px;
- max-width: 100%;
}
-
-.calendar-stats {
- display: flex;
- justify-content: space-around;
- margin-bottom: 20px;
- padding: 15px;
- background: #f5f7fa;
- border-radius: 8px;
-}
-
-.stat-item {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.stat-dot {
- width: 12px;
- height: 12px;
- border-radius: 50%;
- display: inline-block;
-}
-
-.stat-dot.present { background-color: #67c23a; }
-.stat-dot.absent { background-color: #f56c6c; }
-.stat-dot.trip { background-color: #409eff; }
-.stat-dot.late { background-color: #e6a23c; }
.calendar-date {
- height: 80px;
- padding: 4px;
- border: 1px solid #ebeef5;
- border-radius: 4px;
- cursor: pointer;
- transition: all 0.3s;
- position: relative;
- overflow: hidden;
-}
-
-.calendar-date:hover {
- background-color: #f0f9ff;
- border-color: #409eff;
- transform: translateY(-2px);
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-}
-
-.calendar-date.today {
- border-color: #409eff;
- background-color: #f0f9ff;
-}
-
-.calendar-date.selected {
- background-color: #ecf5ff;
- border-color: #409eff;
-}
-
-/* 鐘舵�佽儗鏅壊 */
-.status-background {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- opacity: 0.1;
- z-index: 0;
-}
-
-.calendar-date.has-attendance .status-background {
- background-color: #67c23a;
-}
-
-.calendar-date.has-late .status-background {
- background-color: #e6a23c;
-}
-
-.calendar-date.has-absent .status-background {
- background-color: #f56c6c;
-}
-
-.calendar-date.has-trip .status-background {
- background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
+ height: 100%;
+ display: flex;
+ flex-direction: column;
}
.date-number {
font-weight: bold;
- font-size: 14px;
- margin-bottom: 2px;
- position: relative;
- z-index: 1;
+ margin-bottom: 4px;
}
.date-events {
- display: flex;
- flex-direction: column;
- gap: 2px;
- position: relative;
- z-index: 1;
+ flex: 1;
+ overflow: hidden;
}
-.status-mark, .trip-mark, .absent-mark {
+.event-item {
display: flex;
align-items: center;
- gap: 4px;
+ margin-bottom: 2px;
+ font-size: 12px;
}
-.status-dot {
- width: 8px;
- height: 8px;
+.event-dot {
+ width: 6px;
+ height: 6px;
border-radius: 50%;
- display: inline-block;
-}
-
-.status-dot.present { background-color: #67c23a; }
-.status-dot.late { background-color: #e6a23c; }
-.status-dot.absent { background-color: #f56c6c; }
-
-.trip-mark i {
- color: #409eff;
- font-size: 12px;
-}
-
-.absent-mark i {
- color: #f56c6c;
- font-size: 12px;
-}
-
-/* 绠�鐣ヤ俊鎭樉绀� */
-.brief-info {
- position: absolute;
- bottom: 4px;
- right: 4px;
- display: flex;
- gap: 2px;
- z-index: 1;
-}
-
-.info-item {
- width: 16px;
- height: 16px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 10px;
- font-weight: bold;
-}
-
-.present-info {
- background-color: #67c23a;
- color: white;
-}
-
-.late-info {
- background-color: #e6a23c;
- color: white;
-}
-
-.absent-info {
- background-color: #f56c6c;
- color: white;
-}
-
-.trip-info {
- background-color: #409eff;
- color: white;
-}
-
-.date-details {
- position: absolute;
- top: 100%;
- left: 0;
- right: 0;
- background: white;
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 8px;
- z-index: 1000;
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
- display: none;
-}
-
-.calendar-date:hover .date-details {
- display: block;
-}
-
-.detail-item {
- font-size: 12px;
- margin-bottom: 4px;
- display: flex;
- align-items: center;
-}
-
-.detail-type {
- font-weight: bold;
margin-right: 4px;
- min-width: 40px;
}
-.detail-info {
- color: #606266;
+.event-item.attendance .event-dot {
+ background-color: #67c23a;
}
-.detail-section {
- margin-bottom: 20px;
+.event-item.business-trip .event-dot {
+ background-color: #409eff;
}
-.detail-section h4 {
- margin-bottom: 10px;
- color: #303133;
- border-left: 4px solid #409eff;
- padding-left: 8px;
-}
-
-.no-data {
- color: #909399;
- font-style: italic;
-}
-
-:deep(.el-calendar__header) {
- padding: 10px;
- border-bottom: 1px solid #ebeef5;
-}
-
-:deep(.el-calendar-day) {
- padding: 0 !important;
- height: 80px;
-}
-
-:deep(.el-calendar-table:not(.is-range) td) {
- border: 1px solid #f0f0f0;
-}
-
-:deep(.el-calendar-table .el-calendar-day) {
- height: 80px !important;
- padding: 0 !important;
+.event-text {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
</style>
--
Gitblit v1.9.3