2026-08-07 11:28:38 +08:00
|
|
|
|
const competition = require('../../../utils/competition');
|
|
|
|
|
|
|
|
|
|
|
|
Page({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
loading: true,
|
|
|
|
|
|
context: null,
|
|
|
|
|
|
projects: [],
|
|
|
|
|
|
projectNames: [],
|
|
|
|
|
|
projectIndex: 0,
|
|
|
|
|
|
groups: [],
|
|
|
|
|
|
groupNames: [],
|
|
|
|
|
|
groupIndex: 0,
|
|
|
|
|
|
units: [],
|
|
|
|
|
|
pendingUnits: [],
|
|
|
|
|
|
checkedUnits: [],
|
|
|
|
|
|
checkedGroups: [],
|
|
|
|
|
|
lockedUnits: [],
|
|
|
|
|
|
checkedCount: 0,
|
|
|
|
|
|
pendingCount: 0,
|
|
|
|
|
|
progressPercent: 0,
|
2026-09-01 23:13:21 +08:00
|
|
|
|
updatingKey: '',
|
|
|
|
|
|
actionLoading: false
|
2026-08-07 11:28:38 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onLoad() { this.loadContext(); },
|
|
|
|
|
|
|
|
|
|
|
|
onPullDownRefresh() {
|
|
|
|
|
|
this.loadContext(this.currentProjectId(), this.currentGroupId()).finally(() => wx.stopPullDownRefresh());
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
currentProjectId() {
|
|
|
|
|
|
const item = this.data.projects[this.data.projectIndex];
|
|
|
|
|
|
return item && item.eventProjectId;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
currentGroupId() {
|
|
|
|
|
|
const item = this.data.groups[this.data.groupIndex];
|
|
|
|
|
|
return item && item.id;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
loadContext(eventProjectId, groupId) {
|
|
|
|
|
|
this.setData({ loading: true });
|
|
|
|
|
|
return competition.fetchCurrentCheckin({ eventProjectId, groupId })
|
|
|
|
|
|
.then((context) => this.applyContext(context))
|
|
|
|
|
|
.catch((err) => wx.showToast({ title: (err && err.message) || '检录信息加载失败', icon: 'none' }))
|
|
|
|
|
|
.finally(() => this.setData({ loading: false }));
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
applyContext(context) {
|
|
|
|
|
|
const projects = (context && context.projects) || [];
|
|
|
|
|
|
let projectIndex = projects.findIndex((item) => String(item.eventProjectId) === String(context.selectedEventProjectId));
|
|
|
|
|
|
if (projectIndex < 0) projectIndex = 0;
|
|
|
|
|
|
const groups = (projects[projectIndex] && projects[projectIndex].groups) || [];
|
|
|
|
|
|
let groupIndex = groups.findIndex((item) => String(item.id) === String(context.selectedGroupId));
|
|
|
|
|
|
if (groupIndex < 0) groupIndex = 0;
|
|
|
|
|
|
const units = (context && context.units) || [];
|
2026-09-01 23:13:21 +08:00
|
|
|
|
const pendingUnits = Array.isArray(context && context.pendingUnits) ? context.pendingUnits : units.filter((item) => item.canCheck && !item.checked);
|
|
|
|
|
|
const checkedUnits = Array.isArray(context && context.checkedUnits) ? context.checkedUnits : units.filter((item) => item.checked);
|
|
|
|
|
|
const lockedUnits = Array.isArray(context && context.lockedUnits) ? context.lockedUnits : units.filter((item) => !item.canCheck);
|
2026-08-07 11:28:38 +08:00
|
|
|
|
const totalRounds = Number(context && (context.totalCheckins || context.totalRounds)) || 0;
|
|
|
|
|
|
const currentRound = Number(context && (context.currentCheckinNo || context.currentRound)) || 0;
|
|
|
|
|
|
const rounds = Array.from({ length: totalRounds }, (_, index) => {
|
|
|
|
|
|
const number = index + 1;
|
|
|
|
|
|
return { number, state: number < currentRound ? 'done' : number === currentRound ? 'active' : '', label: number < currentRound ? '✓' : String(number) };
|
|
|
|
|
|
});
|
|
|
|
|
|
const checkedCount = units.filter((item) => item.checked).length;
|
|
|
|
|
|
const checkedGroups = [];
|
|
|
|
|
|
checkedUnits.forEach((item) => {
|
|
|
|
|
|
const key = `${item.eventProjectId || item.projectId}-${item.groupId || item.groupName}`;
|
|
|
|
|
|
let group = checkedGroups.find((entry) => entry.key === key);
|
|
|
|
|
|
if (!group) {
|
|
|
|
|
|
group = {
|
|
|
|
|
|
key,
|
|
|
|
|
|
projectName: item.projectName || '未命名项目',
|
|
|
|
|
|
groupName: item.groupName || '未分组',
|
|
|
|
|
|
units: []
|
|
|
|
|
|
};
|
|
|
|
|
|
checkedGroups.push(group);
|
|
|
|
|
|
}
|
|
|
|
|
|
group.units.push(item);
|
|
|
|
|
|
});
|
|
|
|
|
|
this.setData({
|
|
|
|
|
|
context: context ? Object.assign({}, context, { rounds }) : null,
|
|
|
|
|
|
projects,
|
|
|
|
|
|
projectNames: projects.map((item) => item.projectName),
|
|
|
|
|
|
projectIndex,
|
|
|
|
|
|
groups,
|
|
|
|
|
|
groupNames: groups.map((item) => item.groupName),
|
|
|
|
|
|
groupIndex,
|
|
|
|
|
|
units,
|
|
|
|
|
|
pendingUnits,
|
|
|
|
|
|
checkedUnits,
|
|
|
|
|
|
checkedGroups,
|
|
|
|
|
|
lockedUnits,
|
|
|
|
|
|
checkedCount,
|
|
|
|
|
|
pendingCount: pendingUnits.length,
|
|
|
|
|
|
progressPercent: units.length ? Math.round(checkedCount * 100 / units.length) : 0
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onProjectTap(e) {
|
|
|
|
|
|
if (this.data.loading) return;
|
|
|
|
|
|
const projectIndex = Number(e.currentTarget.dataset.index) || 0;
|
|
|
|
|
|
if (projectIndex === this.data.projectIndex) return;
|
|
|
|
|
|
const project = this.data.projects[projectIndex];
|
|
|
|
|
|
const group = project && project.groups && project.groups[0];
|
|
|
|
|
|
if (project && group) this.loadContext(project.eventProjectId, group.id);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onGroupTap(e) {
|
|
|
|
|
|
if (this.data.loading) return;
|
|
|
|
|
|
const groupIndex = Number(e.currentTarget.dataset.index) || 0;
|
|
|
|
|
|
if (groupIndex === this.data.groupIndex) return;
|
|
|
|
|
|
const group = this.data.groups[groupIndex];
|
|
|
|
|
|
if (group) this.loadContext(this.currentProjectId(), group.id);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-09-01 23:13:21 +08:00
|
|
|
|
onStartCheckin() {
|
|
|
|
|
|
if (this.data.actionLoading) return;
|
|
|
|
|
|
const eventProjectId = this.currentProjectId();
|
|
|
|
|
|
const groupId = this.currentGroupId();
|
|
|
|
|
|
wx.showModal({
|
|
|
|
|
|
title: '开始检录',
|
|
|
|
|
|
content: '开始后将通知本组报名人员,确认继续吗?',
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (!res.confirm) return;
|
|
|
|
|
|
this.setData({ actionLoading: true });
|
|
|
|
|
|
competition.startCheckin({ eventProjectId, groupId })
|
|
|
|
|
|
.then((result) => {
|
|
|
|
|
|
if (result && result.context) this.applyContext(result.context);
|
|
|
|
|
|
wx.showToast({ title: (result && result.message) || '检录已开始', icon: 'none' });
|
|
|
|
|
|
if (!(result && result.context)) return this.loadContext(eventProjectId, groupId);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((err) => wx.showToast({ title: (err && err.message) || '开始检录失败', icon: 'none' }))
|
|
|
|
|
|
.finally(() => this.setData({ actionLoading: false }));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onScanCheckin() {
|
|
|
|
|
|
if (this.data.actionLoading) return;
|
|
|
|
|
|
wx.scanCode({
|
|
|
|
|
|
scanType: ['qrCode', 'barCode'],
|
|
|
|
|
|
success: (scan) => {
|
|
|
|
|
|
this.setData({ actionLoading: true });
|
|
|
|
|
|
competition.scanCheckin({
|
|
|
|
|
|
code: scan.result,
|
|
|
|
|
|
eventProjectId: this.currentProjectId(),
|
|
|
|
|
|
groupId: this.currentGroupId()
|
|
|
|
|
|
}).then((result) => {
|
|
|
|
|
|
// 后端在号码布不匹配时返回 success:false + message 且不带 context,
|
|
|
|
|
|
// 必须照样给出提示,否则检录员扫错码时收不到任何反馈。
|
|
|
|
|
|
const ok = !result || result.success !== false;
|
|
|
|
|
|
if (result && result.context) this.applyContext(result.context);
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
|
title: (result && result.message) || (ok ? '扫码检录成功' : '扫码检录失败'),
|
|
|
|
|
|
icon: ok ? 'success' : 'none'
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!(result && result.context)) return this.loadContext(this.currentProjectId(), this.currentGroupId());
|
|
|
|
|
|
}).catch((err) => wx.showToast({ title: (err && err.message) || '扫码检录失败', icon: 'none' }))
|
|
|
|
|
|
.finally(() => this.setData({ actionLoading: false }));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onMarkOnStage(e) {
|
|
|
|
|
|
const unitKey = e.currentTarget.dataset.key;
|
|
|
|
|
|
if (!unitKey || this.data.updatingKey) return;
|
|
|
|
|
|
this.setData({ updatingKey: unitKey });
|
|
|
|
|
|
competition.markCheckinOnStage({
|
|
|
|
|
|
unitKey,
|
|
|
|
|
|
eventProjectId: this.currentProjectId(),
|
|
|
|
|
|
groupId: this.currentGroupId()
|
|
|
|
|
|
}).then((context) => {
|
|
|
|
|
|
if (context) this.applyContext(context);
|
|
|
|
|
|
wx.showToast({ title: '已标记上场', icon: 'success' });
|
|
|
|
|
|
}).catch((err) => wx.showToast({ title: (err && err.message) || '标记上场失败', icon: 'none' }))
|
|
|
|
|
|
.finally(() => this.setData({ updatingKey: '' }));
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-08-07 11:28:38 +08:00
|
|
|
|
onToggleUnit(e) {
|
|
|
|
|
|
const unitKey = e.currentTarget.dataset.key;
|
|
|
|
|
|
const current = this.data.units.find((item) => item.unitKey === unitKey)
|
|
|
|
|
|
|| this.data.checkedUnits.find((item) => item.unitKey === unitKey);
|
|
|
|
|
|
if (!current || !current.canCheck || this.data.updatingKey) return;
|
|
|
|
|
|
const checked = !current.checked;
|
|
|
|
|
|
if (current.checked) {
|
|
|
|
|
|
wx.showModal({
|
|
|
|
|
|
title: '取消检录',
|
|
|
|
|
|
content: `确认取消${current.teamName || current.athletes.map((item) => item.name).join('、')}的当前检录吗?`,
|
|
|
|
|
|
confirmText: '确认取消',
|
|
|
|
|
|
confirmColor: '#d94b4b',
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) this.updateUnitStatus(current, checked);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.updateUnitStatus(current, checked);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
updateUnitStatus(current, checked) {
|
|
|
|
|
|
const unitKey = current.unitKey;
|
|
|
|
|
|
this.setData({ updatingKey: unitKey });
|
|
|
|
|
|
competition.updateCheckinStatus({
|
|
|
|
|
|
unitKey,
|
|
|
|
|
|
eventProjectId: this.currentProjectId(),
|
|
|
|
|
|
groupId: this.currentGroupId(),
|
|
|
|
|
|
checked
|
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
|
this.applyContext(res);
|
|
|
|
|
|
wx.showToast({ title: checked ? '该列已完成检录' : '已取消该列检录', icon: 'none' });
|
|
|
|
|
|
}).catch((err) => wx.showToast({ title: (err && err.message) || '操作失败', icon: 'none' }))
|
|
|
|
|
|
.finally(() => this.setData({ updatingKey: '' }));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|