Blame view

pages-sub/daily/attendanceRecord.vue 10.5 KB
9441e28e   刘淇   考勤打卡
1
  <template>
bc215493   刘淇   考勤记录
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    <view class="attendance-page">
      <!-- 顶部统计卡片 -->
      <view class="stat-card">
        <view class="stat-item">
          <view class="stat-label">本月打卡</view>
          <view class="stat-value">{{ statData.checkInDays }}</view>
        </view>
        <view class="stat-item">
          <view class="stat-label">正常天数</view>
          <view class="stat-value">{{ statData.normalDays }}</view>
        </view>
        <view class="stat-item">
          <view class="stat-label">异常天数</view>
          <view class="stat-value">{{ statData.errorDays }}</view>
        </view>
9441e28e   刘淇   考勤打卡
17
      </view>
bc215493   刘淇   考勤记录
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
      <!-- 手写内联日历,无弹窗 -->
      <view class="calendar-wrap">
        <view class="calendar-header">
          <!-- 左右横向排列:上一月 | 年月标题 | 下一月 -->
          <view class="calendar-btn" @click="prevMonth">上一月</view>
          <view class="calendar-title">{{ currentYear }}年{{ currentMonth }}月</view>
          <!-- 当前是最新月份时,隐藏下个月按钮 -->
          <view v-if="canNext" class="calendar-btn" @click="nextMonth">下一月</view>
          <view v-else class="calendar-btn empty-btn"></view>
        </view>
        <!-- 星期头部 -->
        <view class="week-row">
          <view class="week-item" v-for="w in weekList" :key="w">{{ w }}</view>
        </view>
        <!-- 日期网格 -->
        <view class="day-row" v-for="(row, rIdx) in dayRows" :key="rIdx">
          <view class="day-item" v-for="(day, dIdx) in row" :key="dIdx" :class="{
9441e28e   刘淇   考勤打卡
35
36
            'day-item--other': day.otherMonth,
            'day-item--active': day.dateStr === selectDate,
bc215493   刘淇   考勤记录
37
38
39
40
          }" @click="onDayClick(day)">
            <view class="day-text">{{ day.day }}</view>
            <view v-if="day.dot" class="day-dot" :style="{ backgroundColor: day.dotColor }"></view>
          </view>
9441e28e   刘淇   考勤打卡
41
42
        </view>
      </view>
bc215493   刘淇   考勤记录
43
44
45
46
      <!-- 打卡详情区域 -->
      <view class="detail-card">
        <view v-if="!currentAttendance">
          <view class="empty-text">暂无考勤记录</view>
9441e28e   刘淇   考勤打卡
47
        </view>
bc215493   刘淇   考勤记录
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
        <view v-else>
          <view class="detail-row">
            <view class="detail-title">上班打卡时间:</view>
            <view class="detail-text">{{ currentAttendance.onDutyTime || '缺少打卡记录' }}</view>
          </view>
          <view class="detail-row">
            <view class="detail-title">上班打卡地点:</view>
            <view class="detail-text">{{ currentAttendance.onDutyAddress || '-' }}</view>
          </view>
          <view class="divider"></view>
          <view class="detail-row">
            <view class="detail-title">下班打卡时间:</view>
            <view class="detail-text">{{ currentAttendance.offDutyTime || '缺少打卡记录' }}</view>
          </view>
          <view class="detail-row">
            <view class="detail-title">下班打卡地点:</view>
            <view class="detail-text">{{ currentAttendance.offDutyAddress || '-' }}</view>
          </view>
          <!-- 可选:展示异常原因 -->
          <view v-if="currentAttendance.abnormalReason" class="detail-row">
            <view class="detail-title">异常原因:</view>
            <view class="detail-text" style="color:#f56c6c">{{ currentAttendance.abnormalReason }}</view>
          </view>
9441e28e   刘淇   考勤打卡
71
72
        </view>
      </view>
bc215493   刘淇   考勤记录
73
      </view>
9441e28e   刘淇   考勤打卡
74
  </template>
9441e28e   刘淇   考勤打卡
75
  <script setup lang="ts">
bc215493   刘淇   考勤记录
76
77
78
  import { ref, onMounted, computed, watch } from 'vue';
  import { attendanceDaily, dayDetail } from '@/api/user'
  const weekList = ['日', '一', '二', '三', '四', '五', '六'];
9441e28e   刘淇   考勤打卡
79
80
81
82
83
84
85
  const today = new Date();
  const todayTs = today.getTime();
  // 当前显示月份
  const currentYear = ref(today.getFullYear());
  const currentMonth = ref(today.getMonth() + 1);
  const selectDate = ref('');
  const dayRows = ref<any[]>([]);
9441e28e   刘淇   考勤打卡
86
87
  // 是否允许点击下一月:判断当前展示月份是否小于本月
  const canNext = computed(() => {
bc215493   刘淇   考勤记录
88
    const showMonthDate = new Date(currentYear.value, currentMonth.value - 1, 1);
9441e28e   刘淇   考勤打卡
89
90
91
92
    const nowMonthDate = new Date(today.getFullYear(), today.getMonth(), 1);
    return showMonthDate < nowMonthDate;
  });
  
bc215493   刘淇   考勤记录
93
  // 统计数据
9441e28e   刘淇   考勤打卡
94
  const statData = ref({
bc215493   刘淇   考勤记录
95
96
97
    checkInDays: 0,
    normalDays: 0,
    errorDays: 0
9441e28e   刘淇   考勤打卡
98
  });
bc215493   刘淇   考勤记录
99
100
  // 当月考勤打点字典 {dateStr: {dot:string, abnormalReason:string}}
  const monthDayMap = ref<Record<string, { dot: string | null, abnormalReason: string | null }>>({});
9441e28e   刘淇   考勤打卡
101
  
bc215493   刘淇   考勤记录
102
103
  // 选中日期详情
  const currentAttendance = ref<any>(null);
9441e28e   刘淇   考勤打卡
104
  
bc215493   刘淇   考勤记录
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  // 拉取月度考勤汇总(stat + 当月日期打点)
  const getMonthAttendance = async () => {
    try {
      const month = `${currentYear.value}-${String(currentMonth.value).padStart(2, '0')}`;
      const res = await attendanceDaily({ month });
      // 赋值顶部统计
      statData.value.checkInDays = res.stat.clockDays;
      statData.value.normalDays = res.stat.normalDays;
      statData.value.errorDays = res.stat.abnormalDays;
      // 构建日期映射字典
      const map: Record<string, { dot: string | null, abnormalReason: string | null }> = {};
      res.days.forEach((item: any) => {
        map[item.date] = {
          dot: item.dot,
          abnormalReason: item.abnormalReason
        };
      })
      monthDayMap.value = map;
      // 重新渲染日历
      renderMonth();
    } catch (err) {
      console.error('获取月度考勤失败', err);
      uni.showToast({ title: '加载月度考勤失败', icon: 'none' });
9441e28e   刘淇   考勤打卡
128
    }
bc215493   刘淇   考勤记录
129
  };
9441e28e   刘淇   考勤打卡
130
131
132
133
134
135
136
137
138
139
140
141
142
  
  // 生成月份日期网格
  const renderMonth = () => {
    const year = currentYear.value;
    const month = currentMonth.value;
    // 当月第一天
    const firstDay = new Date(year, month - 1, 1);
    // 当月最后一天
    const lastDay = new Date(year, month, 0);
    // 上个月最后一天
    const prevLastDay = new Date(year, month - 1, 0).getDate();
    // 当月第一天星期 0周日~6周六
    const firstWeek = firstDay.getDay();
9441e28e   刘淇   考勤打卡
143
144
145
    const rows: any[] = [];
    let row: any[] = [];
    // 填充上月尾部
bc215493   刘淇   考勤记录
146
    for (let i = 0; i < firstWeek; i++) {
9441e28e   刘淇   考勤打卡
147
148
149
150
      const dayNum = prevLastDay - firstWeek + i + 1;
      row.push({
        day: dayNum,
        otherMonth: true,
bc215493   刘淇   考勤记录
151
        dateStr: `${year}-${String(month - 1).padStart(2, '0')}-${String(dayNum).padStart(2, '0')}`
9441e28e   刘淇   考勤打卡
152
153
154
      })
    }
    // 填充当月日期
bc215493   刘淇   考勤记录
155
156
157
158
    for (let d = 1; d <= lastDay.getDate(); d++) {
      const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
      const mark = monthDayMap.value[dateStr];
      const item: any = {
9441e28e   刘淇   考勤打卡
159
160
161
162
163
164
        day: d,
        otherMonth: false,
        dateStr,
        dot: false,
        dotColor: ''
      };
bc215493   刘淇   考勤记录
165
      if (mark?.dot) {
9441e28e   刘淇   考勤打卡
166
        item.dot = true;
bc215493   刘淇   考勤记录
167
168
169
170
171
        if (mark.dot === 'abnormal') {
          item.dotColor = '#f56c6c';
        } else if (mark.dot === 'partial') {
          item.dotColor = '#cccccc';
        }
9441e28e   刘淇   考勤打卡
172
173
      }
      row.push(item);
bc215493   刘淇   考勤记录
174
      if (row.length === 7) {
9441e28e   刘淇   考勤打卡
175
176
177
178
179
180
        rows.push(row);
        row = [];
      }
    }
    // 填充下月开头
    let nextDay = 1;
bc215493   刘淇   考勤记录
181
    while (row.length < 7) {
9441e28e   刘淇   考勤打卡
182
183
      row.push({
        day: nextDay,
bc215493   刘淇   考勤记录
184
185
        otherMonth: true,
        dateStr: `${year}-${String(month + 1).padStart(2, '0')}-${String(nextDay).padStart(2, '0')}`
9441e28e   刘淇   考勤打卡
186
187
188
189
190
191
192
193
194
      })
      nextDay++;
    }
    rows.push(row);
    dayRows.value = rows;
  };
  
  // 上一月
  const prevMonth = () => {
bc215493   刘淇   考勤记录
195
    if (currentMonth.value === 1) {
9441e28e   刘淇   考勤打卡
196
197
      currentYear.value--;
      currentMonth.value = 12;
bc215493   刘淇   考勤记录
198
    } else {
9441e28e   刘淇   考勤打卡
199
200
      currentMonth.value--;
    }
bc215493   刘淇   考勤记录
201
    getMonthAttendance();
9441e28e   刘淇   考勤打卡
202
203
204
205
206
  };
  // 下一月
  const nextMonth = () => {
    let nextY = currentYear.value;
    let nextM = currentMonth.value + 1;
bc215493   刘淇   考勤记录
207
    if (nextM > 12) {
9441e28e   刘淇   考勤打卡
208
      nextY++;
bc215493   刘淇   考勤记录
209
      nextM = 1;
9441e28e   刘淇   考勤打卡
210
211
212
    }
    currentYear.value = nextY;
    currentMonth.value = nextM;
bc215493   刘淇   考勤记录
213
    getMonthAttendance();
9441e28e   刘淇   考勤打卡
214
215
  };
  
bc215493   刘淇   考勤记录
216
217
  // 点击日期:获取当日详情
  const onDayClick = async (day: any) => {
9441e28e   刘淇   考勤打卡
218
    // 禁止点击其他月份
bc215493   刘淇   考勤记录
219
    if (day.otherMonth) return;
9441e28e   刘淇   考勤打卡
220
    const selectDay = new Date(day.dateStr);
bc215493   刘淇   考勤记录
221
    if (selectDay > today) {
9441e28e   刘淇   考勤打卡
222
      currentAttendance.value = null;
bc215493   刘淇   考勤记录
223
      selectDate.value = day.dateStr;
9441e28e   刘淇   考勤打卡
224
225
226
      return;
    }
    selectDate.value = day.dateStr;
bc215493   刘淇   考勤记录
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
    try {
      const res = await dayDetail({ date: day.dateStr });
      // 取第一条记录,大多数场景单条记录
      const record = res.records?.[0] ?? null;
      if (record) {
        currentAttendance.value = {
          ...record,
          abnormalReason: res.abnormalReason
        };
      } else {
        currentAttendance.value = null;
      }
    } catch (err) {
      console.error('获取当日考勤详情失败', err);
      uni.showToast({ title: '加载当日考勤失败', icon: 'none' });
      currentAttendance.value = null;
    }
9441e28e   刘淇   考勤打卡
244
245
  };
  
bc215493   刘淇   考勤记录
246
247
248
  // 页面初始化
  onMounted(async () => {
    const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
9441e28e   刘淇   考勤打卡
249
    selectDate.value = todayStr;
bc215493   刘淇   考勤记录
250
251
252
    await getMonthAttendance();
    // 默认选中今天,加载当日详情
    await onDayClick({ dateStr: todayStr, otherMonth: false });
9441e28e   刘淇   考勤打卡
253
  });
9441e28e   刘淇   考勤打卡
254
  </script>
9441e28e   刘淇   考勤打卡
255
  <style scoped lang="scss">
bc215493   刘淇   考勤记录
256
  .attendance-page {
9441e28e   刘淇   考勤打卡
257
258
259
260
    padding: 20rpx;
    background-color: #f5f5f7;
    min-height: 100vh;
  }
bc215493   刘淇   考勤记录
261
262
  
  .stat-card {
9441e28e   刘淇   考勤打卡
263
264
265
    display: flex;
    background-color: #3c9cff;
    border-radius: 16rpx;
bc215493   刘淇   考勤记录
266
267
268
269
270
271
272
273
274
275
276
    padding: 30rpx 0;
    margin-bottom: 20rpx;
  
    .stat-item {
      flex: 1;
      text-align: center;
      color: #fff;
  
      .stat-label {
        font-size: 28rpx;
        opacity: 0.9;
9441e28e   刘淇   考勤打卡
277
      }
bc215493   刘淇   考勤记录
278
279
280
281
282
  
      .stat-value {
        font-size: 36rpx;
        margin-top: 10rpx;
        font-weight: bold;
9441e28e   刘淇   考勤打卡
283
284
285
      }
    }
  }
bc215493   刘淇   考勤记录
286
287
288
289
290
291
  
  .calendar-wrap {
    background: #fff;
    border-radius: 12rpx;
    padding: 20rpx;
    margin-bottom: 20rpx;
9441e28e   刘淇   考勤打卡
292
  }
bc215493   刘淇   考勤记录
293
294
295
  
  .calendar-header {
    display: flex;
9441e28e   刘淇   考勤打卡
296
    justify-content: space-between;
bc215493   刘淇   考勤记录
297
298
    align-items: center;
    margin-bottom: 20rpx;
9441e28e   刘淇   考勤打卡
299
  }
bc215493   刘淇   考勤记录
300
301
302
303
  
  .calendar-title {
    font-size: 32rpx;
    font-weight: bold;
9441e28e   刘淇   考勤打卡
304
  }
bc215493   刘淇   考勤记录
305
306
307
308
309
310
  
  .calendar-btn {
    padding: 8rpx 16rpx;
    border: 1rpx solid #eee;
    border-radius: 8rpx;
    font-size: 26rpx;
9441e28e   刘淇   考勤打卡
311
  }
bc215493   刘淇   考勤记录
312
313
314
315
  
  .empty-btn {
    border-color: transparent;
    color: transparent;
9441e28e   刘淇   考勤打卡
316
  }
bc215493   刘淇   考勤记录
317
318
319
320
321
322
323
324
325
326
  
  .week-row {
    display: flex;
  
    .week-item {
      flex: 1;
      text-align: center;
      font-size: 26rpx;
      color: #666;
      padding: 10rpx 0;
9441e28e   刘淇   考勤打卡
327
328
    }
  }
bc215493   刘淇   考勤记录
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  
  .day-row {
    display: flex;
  
    .day-item {
      flex: 1;
      min-height: 70rpx;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 8rpx 0;
      position: relative;
  
      .day-text {
        font-size: 28rpx;
9441e28e   刘淇   考勤打卡
345
      }
bc215493   刘淇   考勤记录
346
347
348
349
350
351
352
  
      .day-dot {
        position: absolute;
        bottom: 8rpx;
        width: 10rpx;
        height: 10rpx;
        border-radius: 50%;
9441e28e   刘淇   考勤打卡
353
354
      }
    }
bc215493   刘淇   考勤记录
355
356
357
  
    .day-item--other {
      color: #ccc;
9441e28e   刘淇   考勤打卡
358
    }
bc215493   刘淇   考勤记录
359
360
361
  
    .day-item--active {
      color: #f00;
9441e28e   刘淇   考勤打卡
362
363
    }
  }
bc215493   刘淇   考勤记录
364
365
366
367
368
369
370
371
372
373
374
  
  .detail-card {
    background: #fff;
    border-radius: 12rpx;
    padding: 30rpx;
  
    .empty-text {
      text-align: center;
      color: #999;
      font-size: 30rpx;
      padding: 40rpx 0;
9441e28e   刘淇   考勤打卡
375
    }
bc215493   刘淇   考勤记录
376
377
378
379
380
381
382
383
384
  
    .detail-row {
      margin-bottom: 20rpx;
      display: flex;
  
      .detail-title {
        font-size: 30rpx;
        color: #333;
        width: 220rpx;
9441e28e   刘淇   考勤打卡
385
      }
bc215493   刘淇   考勤记录
386
387
388
389
390
  
      .detail-text {
        flex: 1;
        font-size: 30rpx;
        color: #666;
9441e28e   刘淇   考勤打卡
391
392
      }
    }
bc215493   刘淇   考勤记录
393
394
395
396
397
  
    .divider {
      height: 1px;
      background: #eee;
      margin: 30rpx 0;
9441e28e   刘淇   考勤打卡
398
399
400
    }
  }
  </style>