Jelajahi Sumber

feat(attendance): 增强考勤状态标签,反映迟到和早退异常

xieyong 4 hari lalu
induk
melakukan
7db9ac2d4a
2 mengubah file dengan 18 tambahan dan 3 penghapusan
  1. 7 1
      src/api/attendance.js
  2. 11 2
      src/pages/attendance/index.vue

+ 7 - 1
src/api/attendance.js

@@ -328,7 +328,13 @@ export async function submitPunch({ method, time, lat, lng, accuracy, address, w
       vo.actualOut = hhmm
       vo.actualOut = hhmm
       const worked = vo.actualIn ? minutesBetween(vo.actualIn, hhmm) : 0
       const worked = vo.actualIn ? minutesBetween(vo.actualIn, hhmm) : 0
       vo.actualWorkMinutes = Math.max(0, worked)
       vo.actualWorkMinutes = Math.max(0, worked)
-      if (vo.actualWorkMinutes >= vo.requiredMinutes) {
+      // 早退:实际下班早于 scheduledEnd → 状态置 EARLY、计入异常
+      // 与上班 LATE 分支对称;不存在 scheduledEnd 的休息日 / 跨天班次跳过此判断
+      if (vo.scheduledEnd && hhmm < vo.scheduledEnd) {
+        vo.earlyMinutes = minutesBetween(hhmm, vo.scheduledEnd)
+        vo.statusList = ['EARLY']
+        vo.exceptionCount = 1
+      } else if (vo.actualWorkMinutes >= vo.requiredMinutes) {
         vo.statusList = ['NORMAL']
         vo.statusList = ['NORMAL']
         vo.exceptionCount = 0
         vo.exceptionCount = 0
       }
       }

+ 11 - 2
src/pages/attendance/index.vue

@@ -254,15 +254,24 @@ const detail = computed(() => {
     if ((d.lateMinutes || 0) > 0) workText += ` · 迟到 ${d.lateMinutes} 分钟`;
     if ((d.lateMinutes || 0) > 0) workText += ` · 迟到 ${d.lateMinutes} 分钟`;
     if ((d.earlyMinutes || 0) > 0) workText += ` · 早退 ${d.earlyMinutes} 分钟`;
     if ((d.earlyMinutes || 0) > 0) workText += ` · 早退 ${d.earlyMinutes} 分钟`;
   }
   }
-  const statusLabel = translateStatus(d.statusList);
+  // statusLabel 同时把 lateMinutes / earlyMinutes 这类"事实字段"补进去:
+  // 后端 statusList 可能仍是 NORMAL(漏标、或跨端数据没及时同步),
+  // 但迟到/早退本身需要在前端反映为异常,状态标签不能停在"正常"
+  const baseList = Array.isArray(d.statusList) ? d.statusList : []
+  const augmentedList = [...baseList]
+  if ((d.lateMinutes || 0) > 0 && !augmentedList.includes('LATE')) augmentedList.push('LATE')
+  if ((d.earlyMinutes || 0) > 0 && !augmentedList.includes('EARLY')) augmentedList.push('EARLY')
+  const statusLabel = augmentedList.length > 0 ? translateStatus(augmentedList) : ''
   return { shiftText, inText, outText, inLabel, outLabel, workText, statusLabel, actualOut: d.actualOut };
   return { shiftText, inText, outText, inLabel, outLabel, workText, statusLabel, actualOut: d.actualOut };
 });
 });
 
 
 function statusTagCls(label) {
 function statusTagCls(label) {
   if (!label) return "";
   if (!label) return "";
-  if (label.includes("正常")) return "status-approved";
+  // 异常关键字优先:statusLabel 可能拼接为 "正常 / 早退"、"迟到 / 早退" 等,
+  // 如果先匹配"正常"会把异常场景误判成绿色。这里反过来,先识别异常再放行"正常"
   if (label.includes("迟到") || label.includes("早退") || label.includes("异常") || label.includes("缺勤"))
   if (label.includes("迟到") || label.includes("早退") || label.includes("异常") || label.includes("缺勤"))
     return "status-rejected";
     return "status-rejected";
+  if (label.includes("正常")) return "status-approved";
   return "status-pending";
   return "status-pending";
 }
 }