From 96dd34f77d81db58f54e3d0ad4a8cc8082189a61 Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期四, 16 四月 2026 13:52:27 +0800
Subject: [PATCH] 考勤相关更改
---
src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue | 621 ++++++++++----------------------------------------------
1 files changed, 113 insertions(+), 508 deletions(-)
diff --git a/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue b/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
index c43976e..f0e23d1 100644
--- a/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
+++ b/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
@@ -1,588 +1,193 @@
<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>
+ <!-- <el-button @click="refreshCalendar" size="small" type="primary"
+ >鍒锋柊</el-button
+ > -->
- <!-- Element UI 鏃ュ巻缁勪欢 -->
- <el-calendar v-model="calendarValue" :first-day-of-week="1">
- <template #date-cell="{ data }">
- <div
- class="calendar-date"
- :class="getDateStatusClass(data.day)"
- @click="handleDateClick(data)"
- >
- <div class="date-number">{{ data.day.split('-')[2] }}</div>
-
- <!-- 鐘舵�佽儗鏅壊鍧� -->
- <div class="status-background" :class="getBackgroundStatus(data.day)"></div>
-
+ <!-- Element UI 鐨勬棩鍘嗙粍浠� -->
+ <el-calendar v-model="value">
+ <template slot="dateCell" slot-scope="{ date, data }">
+ <div class="calendar-date">
+ <div class="date-number">
+ {{
+ data.day
+ .split("-")
+ .slice(1)
+ .join("-")
+ }}
+ </div>
+ <!-- <div style="font-size: 10px; color: #666;">{{ 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">
+ <!-- Element UI 涓娇鐢ㄦ彃妲戒綔鐢ㄥ煙 -->
<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>
<script>
export default {
- name: 'AttendanceCalendar',
+ 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
+ value: new Date(), // Element UI 浣跨敤 value
+ localAttendanceData: [],
+ localBusinessTripData: []
+ };
+ },
+ watch: {
+ attendanceData: {
+ immediate: true,
+ handler(val) {
+ this.localAttendanceData = val || [];
+ console.log("鑰冨嫟鏁版嵁:", this.localAttendanceData);
+ this.$nextTick(() => {
+ this.$forceUpdate(); // Vue 2 鐨勫己鍒舵洿鏂�
+ });
+ }
+ },
+ businessTripData: {
+ immediate: true,
+ handler(val) {
+ this.localBusinessTripData = val || [];
+ console.log("鍑哄樊鏁版嵁:", this.localBusinessTripData);
+ this.$nextTick(() => {
+ this.$forceUpdate();
+ });
}
}
},
mounted() {
- this.calculateStats()
- },
- watch: {
- attendanceData: {
- handler() {
- this.calculateStats()
- },
- deep: true
- },
- businessTripData: {
- handler() {
- this.calculateStats()
- },
- deep: true
- }
+ console.log("Calendar mounted with Vue 2.6.12");
+ this.testMethod();
},
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
+ refreshCalendar() {
+ console.log("鎵嬪姩鍒锋柊鏃ュ巻");
+ this.$forceUpdate();
},
-
- // 鑾峰彇鑳屾櫙鐘舵�侊紙鐢ㄤ簬鑳屾櫙鑹诧級
- 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 ''
+ testMethod() {
+ console.log("娴嬭瘯鏂规硶鏄惁鍙皟鐢�");
+ console.log("getDateEvents 娴嬭瘯:", this.getDateEvents("2024-01-15"));
},
-
- // 鍒ゆ柇鏄惁涓轰粖澶�
- 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)
+ console.log("getDateEvents 琚皟鐢�, 鏃ユ湡:", date);
+ console.log("褰撳墠鑰冨嫟鏁版嵁:", this.localAttendanceData);
+ console.log("褰撳墠鍑哄樊鏁版嵁:", this.localBusinessTripData);
+
+ const events = [];
+
+ // 鑰冨嫟鏁版嵁
+ const attendance = (this.localAttendanceData || []).find(item => {
+ return item && item.date === date;
+ });
if (attendance) {
events.push({
id: `attendance-${date}`,
- type: 'attendance',
- text: `${attendance.checkIn || '鏈墦鍗�'} - ${attendance.checkOut || '鏈墦鍗�'}`
- })
+ type: "attendance",
+ text: `${attendance.checkIn || "--"}-${attendance.checkOut || "--"}`
+ });
}
- const businessTrip = this.businessTripData.find(item =>
- date >= item.startDate && date <= item.endDate
- )
+ // 鍑哄樊鏁版嵁
+ const businessTrip = (this.localBusinessTripData || []).find(item => {
+ return 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
+ console.log("鎵惧埌鐨勪簨浠�:", events);
+ return events;
}
}
-}
+};
</script>
<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%;
+ padding: 8px;
+ display: flex;
+ flex-direction: column;
}
.date-number {
font-weight: bold;
- font-size: 14px;
- margin-bottom: 2px;
- position: relative;
- z-index: 1;
+ margin-bottom: 4px;
+ font-size: 16px;
}
.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;
+ padding: 2px 4px;
+ border-radius: 2px;
+ background-color: #f5f7fa;
}
-.status-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- display: inline-block;
+.event-item.attendance {
+ background-color: #f0f9eb;
+ color: #67c23a;
}
-.status-dot.present { background-color: #67c23a; }
-.status-dot.late { background-color: #e6a23c; }
-.status-dot.absent { background-color: #f56c6c; }
-
-.trip-mark i {
+.event-item.business-trip {
+ background-color: #ecf5ff;
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;
+.event-dot {
+ width: 6px;
+ height: 6px;
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;
+::v-deep .el-calendar-table .el-calendar-day {
+ height: 115px;
}
-
-.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;
+ flex: 1;
}
</style>
--
Gitblit v1.9.3