const api = require('../../utils/api'); const auth = require('../../utils/auth'); const STATUSES = ['正常', '迟到', '请假', '不参加讨论', '早退']; const WEEKDAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; const STATUS_META = { 正常: '#2E7D32', 迟到: '#C67B30', 请假: '#3A7BD5', 早退: '#8B7A6A', '不参加讨论': '#C0392B' }; const STATUS_ITEMS = STATUSES.map((label) => ({ label, color: STATUS_META[label] || '#C67B30' })); function errMessage(err) { if (err && typeof err === 'object') { if (err.detail) return String(err.detail); if (err.error) return String(err.error); if (err.message) return err.message; } return ''; } function pad2(n) { return n < 10 ? '0' + n : String(n); } function fmtTime(ts) { if (!ts) return ''; const d = new Date(String(ts).replace(' ', 'T')); if (isNaN(d.getTime())) return String(ts); const now = new Date(); const startOfDay = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime(); const diff = Math.round((startOfDay(now) - startOfDay(d)) / 86400000); const hm = pad2(d.getHours()) + ':' + pad2(d.getMinutes()); if (diff === 0) return '今天 ' + hm; if (diff === 1) return '昨天 ' + hm; return pad2(d.getMonth() + 1) + '-' + pad2(d.getDate()) + ' ' + hm; } Page({ data: { statuses: STATUSES, statusItems: STATUS_ITEMS, statusIndex: -1, loading: true, classDate: '', weekday: '', dayNum: '', checked: false, record: null, statusColor: '', statusBg: '', history: [], group: [], groupName: '', checkedCount: 0, total: 0, tab: 'group', submitting: false, refreshing: false, loadError: '', }, onShow() { this.loadPage(); }, onPullDownRefresh() { this.loadPage().then(() => wx.stopPullDownRefresh()); }, loadPage() { this.setData({ loadError: '' }); return auth.ensureLogin() .then((token) => { if (!token) { wx.switchTab({ url: '/pages/profile/profile' }); return; } return this.loadWeek(); }) .catch((err) => { const msg = (err && (err.error || err.detail || err.message)) || '加载失败,请重试'; this.setData({ loading: false, loadError: msg }); wx.showToast({ title: msg, icon: 'none' }); }); }, onRetry() { this.loadPage(); }, loadWeek() { this.setData({ loading: true }); return api.request({ url: '/api/v1/checkin/week' }).then((res) => { const classDate = res.class_date || ''; const weekday = WEEKDAYS[new Date(classDate + 'T00:00:00').getDay()] || ''; const dayNum = classDate.split('-')[2] || ''; const statusIndex = res.record && res.record.status ? Math.max(0, STATUSES.indexOf(res.record.status)) : -1; const statusColor = res.record ? (STATUS_META[res.record.status] || '#C67B30') : ''; this.setData({ classDate, weekday, dayNum, checked: !!res.checked, record: res.record || null, statusIndex, statusColor, statusBg: statusColor ? statusColor + '1A' : '' }); return this.loadGroup(); }).then(() => { this.setData({ loading: false }); }).catch(() => { this.setData({ loading: false }); }); }, loadGroup() { const data = this.data.classDate ? { class_date: this.data.classDate } : {}; return api.request({ url: '/api/v1/checkin/group', data }).then((res) => { const members = (res.members || []).map((m) => ({ ...m, shortName: (m.name || '?').charAt(0), timeText: m.checked && m.record ? fmtTime(m.record.checked_at) : '' })); const checkedCount = members.filter((m) => m.checked).length; this.setData({ group: members, groupName: res.group_name || '', checkedCount, total: members.length }); }).catch(() => {}); }, loadHistory() { return api.request({ url: '/api/v1/checkin/list', data: { page: 1, size: 20 } }).then((res) => { const history = (res.list || []).map((item) => { const color = STATUS_META[item.status] || '#C67B30'; return { ...item, timeText: fmtTime(item.checked_at), color, bg: color + '1A' }; }); this.setData({ history }); }).catch(() => {}); }, onStatusTap(e) { if (this.data.submitting) { return; } const statusIndex = Number(e.currentTarget.dataset.index); const status = STATUSES[statusIndex]; if (!status) { return; } const prevIndex = this.data.statusIndex; this.setData({ statusIndex, submitting: true }); api.request({ url: '/api/v1/checkin', method: 'POST', data: { status, class_date: this.data.classDate, course_type: '会员课程' } }).then((res) => { const record = res.record || null; const statusColor = record ? (STATUS_META[record.status] || '#C67B30') : ''; this.setData({ submitting: false, checked: true, record, statusColor, statusBg: statusColor ? statusColor + '1A' : '' }); wx.showToast({ title: '打卡成功', icon: 'success' }); this.loadGroup(); if (this.data.tab === 'personal') { this.loadHistory(); } }).catch((err) => { this.setData({ submitting: false, statusIndex: prevIndex }); wx.showToast({ title: errMessage(err) || '打卡失败', icon: 'none' }); }); }, switchTab(e) { const tab = e.currentTarget.dataset.tab; if (tab !== 'group' && tab !== 'personal') { return; } this.setData({ tab }); if (tab === 'personal') { this.loadHistory(); } }, onRefresh() { this.setData({ refreshing: true }); const task = this.data.tab === 'group' ? this.loadWeek() : this.loadHistory(); Promise.resolve(task).then(() => { this.setData({ refreshing: false }); }); } });