299 lines
9.4 KiB
Vue
299 lines
9.4 KiB
Vue
<template>
|
|
<div class="checkin-manager">
|
|
<el-alert
|
|
v-if="scheduleError"
|
|
:title="scheduleError"
|
|
type="warning"
|
|
show-icon
|
|
:closable="false"
|
|
class="schedule-alert"
|
|
/>
|
|
|
|
<ProTable ref="checkerTable" :request-api="requestCheckers" :columns="columns" :pagination="true" :page-size="10" :tool-button="false" row-key="taskId">
|
|
<template #tools><el-button type="primary" icon="ele-Plus" :disabled="checkerList.length >= 3" @click="openCreateDialog">新增检录员</el-button></template>
|
|
<template #checkinNo="scope"><el-tag type="primary">{{ checkinName(scope.row.checkinNo) }}</el-tag></template>
|
|
<template #coverage>全部秩序册列</template>
|
|
<template #venue="scope">{{ scope.row.venue || '-' }}</template>
|
|
<template #taskStatus="scope"><el-tag :type="scope.row.taskStatus === 1 ? 'success' : 'info'">{{ scope.row.taskStatus === 1 ? '进行中' : '未启用' }}</el-tag></template>
|
|
<template #action="scope"><el-button text type="primary" @click="openEditDialog(scope.row)">编辑</el-button><el-button text type="danger" @click="unbindChecker(scope.row)">解除绑定</el-button></template>
|
|
</ProTable>
|
|
|
|
<el-dialog
|
|
v-model="dialogVisible"
|
|
:title="form.checkerUserId ? '编辑检录员' : '新增检录员'"
|
|
width="min(600px, 94vw)"
|
|
:close-on-click-modal="false"
|
|
destroy-on-close
|
|
>
|
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
|
|
<el-form-item label="姓名" prop="name">
|
|
<el-input v-model.trim="form.name" maxlength="32" placeholder="请输入检录员姓名" />
|
|
</el-form-item>
|
|
<el-form-item label="登录手机号" prop="mobile">
|
|
<el-input
|
|
v-model.trim="form.mobile"
|
|
maxlength="11"
|
|
placeholder="请输入11位手机号"
|
|
@blur="checkMobileAvailability"
|
|
/>
|
|
<div v-if="accountCheck.message" class="account-tip" :class="accountCheck.available ? 'success' : 'error'">
|
|
{{ accountCheck.message }}
|
|
</div>
|
|
</el-form-item>
|
|
<el-form-item :label="form.checkerUserId ? '重置密码' : '登录密码'" prop="password">
|
|
<el-input
|
|
v-model="form.password"
|
|
type="password"
|
|
show-password
|
|
maxlength="32"
|
|
:placeholder="form.checkerUserId ? '不修改请留空' : '请输入6-32位密码'"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="负责检次" prop="checkinNo">
|
|
<el-select v-model="form.checkinNo" placeholder="请选择检次" style="width: 100%" :disabled="!!form.checkerUserId">
|
|
<el-option
|
|
v-for="no in [1, 2, 3]"
|
|
:key="no"
|
|
:label="`${checkinName(no)}(全部秩序册列)`"
|
|
:value="no"
|
|
:disabled="assignedCheckinNumbers.includes(no) && no !== form.checkinNo"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="检录区域">
|
|
<el-input v-model.trim="form.venue" maxlength="128" placeholder="例如:一号检录区(选填)" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
<el-button type="primary" :loading="saving" @click="saveChecker">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { reactive, ref, watch } from 'vue';
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
import ProTable from '/@/components/ProTable/index.vue';
|
|
import {
|
|
checkinAdminMobileAvailabilityGet,
|
|
checkinAdminCheckerSavePost,
|
|
checkinAdminCheckersGet,
|
|
checkinAdminCheckerUnbindPost,
|
|
checkinAdminEventOptionsGet,
|
|
} from '/@/api/match/checkin';
|
|
|
|
const props = defineProps({
|
|
eventId: { type: [Number, String], required: true },
|
|
active: { type: Boolean, default: false },
|
|
});
|
|
|
|
const formRef = ref();
|
|
const checkerTable = ref();
|
|
const saving = ref(false);
|
|
const dialogVisible = ref(false);
|
|
const checkerList = ref([]);
|
|
const assignedCheckinNumbers = ref([]);
|
|
const scheduleError = ref('');
|
|
const accountCheck = reactive({ available: null, message: '' });
|
|
const form = reactive({
|
|
checkerUserId: null,
|
|
name: '',
|
|
mobile: '',
|
|
password: '',
|
|
checkinNo: 1,
|
|
venue: '',
|
|
});
|
|
|
|
const columns = [
|
|
{ prop: 'index', label: '序号', type: 'index', width: 80 },
|
|
{ prop: 'name', label: '姓名', minWidth: 110 },
|
|
{ prop: 'mobile', label: '登录手机号', minWidth: 140 },
|
|
{ prop: 'checkinNo', label: '负责检次', width: 110 },
|
|
{ prop: 'coverage', label: '检录范围', minWidth: 150 },
|
|
{ prop: 'venue', label: '检录区域', minWidth: 130 },
|
|
{ prop: 'taskStatus', label: '状态', width: 100 },
|
|
{ prop: 'createdTime', label: '添加时间', minWidth: 170 },
|
|
{ prop: 'action', label: '操作', width: 180, fixed: 'right' },
|
|
];
|
|
|
|
const validatePassword = (_rule, value, callback) => {
|
|
if (!form.checkerUserId && !value) return callback(new Error('请输入登录密码'));
|
|
if (value && (value.length < 6 || value.length > 32)) return callback(new Error('密码必须为6-32位'));
|
|
callback();
|
|
};
|
|
|
|
const rules = {
|
|
name: [{ required: true, message: '请输入检录员姓名', trigger: 'blur' }],
|
|
mobile: [
|
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
|
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' },
|
|
],
|
|
password: [{ validator: validatePassword, trigger: 'blur' }],
|
|
checkinNo: [{ required: true, message: '请选择负责检次', trigger: 'change' }],
|
|
};
|
|
|
|
const checkinName = (no) => ({ 1: '一检', 2: '二检', 3: '三检' }[Number(no)] || `第${no}检`);
|
|
|
|
const requestCheckers = async ({ pageNo = 1, pageSize = 10 }) => {
|
|
if (!props.active || !props.eventId) return { data: { rows: [], total: 0, pageNo, pageSize } };
|
|
const res = await checkinAdminCheckersGet(props.eventId);
|
|
checkerList.value = res?.data || [];
|
|
const start = (pageNo - 1) * pageSize;
|
|
return { ...res, data: { rows: checkerList.value.slice(start, start + pageSize), total: checkerList.value.length, pageNo, pageSize } };
|
|
};
|
|
|
|
const loadCheckers = async () => checkerTable.value?.getTableList();
|
|
|
|
const loadEventOptions = async () => {
|
|
scheduleError.value = '';
|
|
try {
|
|
const res = await checkinAdminEventOptionsGet(props.eventId);
|
|
const data = res?.data || {};
|
|
assignedCheckinNumbers.value = data.assignedCheckinNumbers || [];
|
|
if (!(data.units || []).length) scheduleError.value = '当前赛事秩序册没有可检录列';
|
|
} catch (error) {
|
|
assignedCheckinNumbers.value = [];
|
|
scheduleError.value = '请先在当前赛事的秩序册管理中完成赛事编排,再添加检录员';
|
|
}
|
|
};
|
|
|
|
const resetForm = () => {
|
|
Object.assign(form, {
|
|
checkerUserId: null,
|
|
name: '',
|
|
mobile: '',
|
|
password: '',
|
|
checkinNo: Math.min(checkerList.value.length + 1, 3),
|
|
venue: '',
|
|
});
|
|
accountCheck.available = null;
|
|
accountCheck.message = '';
|
|
};
|
|
|
|
const openCreateDialog = async () => {
|
|
await loadEventOptions();
|
|
if (scheduleError.value) {
|
|
ElMessage.warning(scheduleError.value);
|
|
return;
|
|
}
|
|
resetForm();
|
|
dialogVisible.value = true;
|
|
};
|
|
|
|
const openEditDialog = async (row) => {
|
|
await loadEventOptions();
|
|
Object.assign(form, {
|
|
checkerUserId: row.checkerUserId,
|
|
name: row.name,
|
|
mobile: row.mobile,
|
|
password: '',
|
|
checkinNo: row.checkinNo,
|
|
venue: row.venue || '',
|
|
});
|
|
accountCheck.available = true;
|
|
accountCheck.message = '当前检录员手机号';
|
|
dialogVisible.value = true;
|
|
};
|
|
|
|
const checkMobileAvailability = async () => {
|
|
if (!/^1[3-9]\d{9}$/.test(form.mobile)) {
|
|
accountCheck.available = false;
|
|
accountCheck.message = '手机号格式不正确';
|
|
return false;
|
|
}
|
|
const res = await checkinAdminMobileAvailabilityGet(form.mobile, form.checkerUserId || undefined);
|
|
accountCheck.available = !!res?.data?.available;
|
|
accountCheck.message = res?.data?.message || '';
|
|
return accountCheck.available;
|
|
};
|
|
|
|
const saveChecker = async () => {
|
|
if (!formRef.value) return;
|
|
const valid = await formRef.value.validate().catch(() => false);
|
|
if (!valid) return;
|
|
const available = await checkMobileAvailability();
|
|
if (!available) return;
|
|
|
|
saving.value = true;
|
|
try {
|
|
const res = await checkinAdminCheckerSavePost({
|
|
checkerUserId: form.checkerUserId || undefined,
|
|
eventId: Number(props.eventId),
|
|
name: form.name,
|
|
mobile: form.mobile,
|
|
password: form.password || undefined,
|
|
checkinNo: form.checkinNo,
|
|
venue: form.venue || undefined,
|
|
});
|
|
if (res?.succeeded === false) return;
|
|
ElMessage.success(form.checkerUserId ? '检录员信息已更新' : '检录员已添加并绑定当前赛事');
|
|
dialogVisible.value = false;
|
|
await loadCheckers();
|
|
await loadEventOptions();
|
|
} finally {
|
|
saving.value = false;
|
|
}
|
|
};
|
|
|
|
const unbindChecker = async (row) => {
|
|
const confirmed = await ElMessageBox.confirm(`确认解除“${row.name}”与当前赛事的检录绑定吗?`, '解除绑定', {
|
|
type: 'warning',
|
|
confirmButtonText: '确认解除',
|
|
cancelButtonText: '取消',
|
|
}).catch(() => false);
|
|
if (!confirmed) return;
|
|
const res = await checkinAdminCheckerUnbindPost(row.taskId);
|
|
if (res?.succeeded === false) return;
|
|
ElMessage.success('已解除赛事绑定');
|
|
await loadCheckers();
|
|
await loadEventOptions();
|
|
};
|
|
|
|
watch(
|
|
() => [props.active, props.eventId],
|
|
([active]) => {
|
|
if (active && props.eventId) {
|
|
loadCheckers();
|
|
loadEventOptions();
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
</script>
|
|
|
|
<style scoped>
|
|
.checkin-manager {
|
|
min-height: 100%;
|
|
padding: 0 2px 24px;
|
|
}
|
|
|
|
.schedule-alert {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.account-tip {
|
|
width: 100%;
|
|
margin-top: 4px;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
}
|
|
|
|
.account-tip.success {
|
|
color: #67c23a;
|
|
}
|
|
|
|
.account-tip.error {
|
|
color: #f56c6c;
|
|
}
|
|
|
|
:deep(.el-dialog .el-input), :deep(.el-dialog .el-select) {
|
|
width: 100%;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.checkin-manager { padding: 0 0 16px; }
|
|
}
|
|
</style>
|