修复bug
This commit is contained in:
parent
c799752f63
commit
2521a0f7f8
@ -5,4 +5,4 @@ ENV = development
|
||||
VITE_API_URL = /api
|
||||
|
||||
# 本地后端接口
|
||||
VITE_API_TARGET = http://127.0.0.1:9997
|
||||
VITE_API_TARGET = http://8.153.108.90:9996
|
||||
|
||||
@ -228,6 +228,12 @@ export function dynamicImport(dynamicViewsModules: Record<string, Function>, com
|
||||
return dynamicViewsModules[matchKey];
|
||||
}
|
||||
if (matchKeys?.length > 1) {
|
||||
const exactKey = matchKeys.find((key) => {
|
||||
const k = key.replace(/..\/views|../, '');
|
||||
return k === `${component}.vue` || k === `/${component}.vue` || k === `${component}.tsx` || k === `/${component}.tsx`;
|
||||
});
|
||||
if (exactKey) return dynamicViewsModules[exactKey];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
src/utils/scoringContext.ts
Normal file
57
src/utils/scoringContext.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 打分系统工作台的赛事上下文。
|
||||
* 工作台各业务页(秩序册、成绩管理、成绩投屏)都要共用同一个赛事,
|
||||
* 从菜单直接进入时地址栏没有 query,因此统一从 sessionStorage 读取。
|
||||
*/
|
||||
|
||||
const CONTEXT_KEY = 'scoring-context';
|
||||
const MODE_KEY = 'scoring-mode';
|
||||
|
||||
export interface ScoringContext {
|
||||
eventId: number;
|
||||
eventName: string;
|
||||
mode: string;
|
||||
sourceMode: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取已保存的赛事上下文,没有或格式损坏时返回空上下文
|
||||
*/
|
||||
export function readScoringContext(): ScoringContext {
|
||||
let saved: any = {};
|
||||
try {
|
||||
saved = JSON.parse(sessionStorage.getItem(CONTEXT_KEY) || '{}') ?? {};
|
||||
} catch {
|
||||
saved = {};
|
||||
}
|
||||
return {
|
||||
eventId: Number(saved.eventId) || 0,
|
||||
eventName: String(saved.eventName || ''),
|
||||
mode: String(saved.mode || sessionStorage.getItem(MODE_KEY) || ''),
|
||||
sourceMode: String(saved.sourceMode || ''),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并写入赛事上下文,只覆盖传入的字段
|
||||
*/
|
||||
export function saveScoringContext(patch: Partial<ScoringContext>) {
|
||||
const next = { ...readScoringContext(), ...patch };
|
||||
sessionStorage.setItem(CONTEXT_KEY, JSON.stringify(next));
|
||||
if (next.mode) sessionStorage.setItem(MODE_KEY, next.mode);
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析当前页面使用的赛事:地址栏 query 优先(从工作台点进来时带参),
|
||||
* 否则回退到 sessionStorage(从左侧菜单点进来时没有 query)。
|
||||
* 解析出的赛事会回写上下文,保证后续页面刷新仍能拿到。
|
||||
*/
|
||||
export function resolveScoringEvent(query: Record<string, any>) {
|
||||
const saved = readScoringContext();
|
||||
const eventId = Number(query?.eventId) || saved.eventId;
|
||||
if (!eventId) return { eventId: 0, eventName: '未选择赛事', sourceMode: saved.sourceMode };
|
||||
const eventName = String(query?.eventName || (eventId === saved.eventId ? saved.eventName : '') || '未选择赛事');
|
||||
saveScoringContext({ eventId, eventName, mode: saved.mode || 'freestyle' });
|
||||
return { eventId, eventName, sourceMode: saved.sourceMode };
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="launcher-page">
|
||||
<div class="brand"><span class="brand-mark">Y</span><div><strong>跃动赛事</strong><small>打分系统工作台</small></div></div>
|
||||
|
||||
<main class="launcher-shell">
|
||||
<header><p>SCORING CONSOLE</p><h1>{{ step === 'mode' ? '请选择计分模式' : '请选择赛事' }}</h1><span>{{ step === 'mode' ? '进入系统前,请先选择本次比赛的计分模式' : '选择数据来源和赛事后,各业务菜单将使用同一赛事上下文' }}</span></header>
|
||||
<div v-if="step === 'mode'" class="mode-grid">
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
<el-table-column label="状态" width="100"><template #default="{ row }"><el-tag v-if="row.result" :type="resultTag(row.result.resultStatus)" effect="light">{{ row.result.resultStatusName }}</el-tag><span v-else class="muted">计算中</span></template></el-table-column>
|
||||
<el-table-column label="操作" :width="activeTab === 'screen' ? 150 : 330" fixed="right"><template #default="{ row }">
|
||||
<template v-if="activeTab === 'screen'"><el-tooltip :content="castDisabledReason(row)" :disabled="canCast(row)"><span><el-button text type="primary" :disabled="!canCast(row)" @click="castScore(row)">投出成绩</el-button></span></el-tooltip><el-button text @click="showDetail(row)">详情</el-button></template>
|
||||
<template v-else><el-button text type="primary" @click="showDetail(row)">详情</el-button><el-button v-if="canConfirm(row)" text type="success" @click="confirmOne(row)">确认</el-button><el-button text type="warning" :disabled="!row.judgeScores?.length" @click="openCorrection(row)">成绩修正</el-button><el-button text type="danger" @click="confirmRematch(row)">重赛</el-button><el-dropdown trigger="click"><el-button text>更多</el-button><template #dropdown><el-dropdown-menu><el-dropdown-item @click="openReason('void', row)">作废成绩</el-dropdown-item><el-dropdown-item @click="exportSheets(row)">导出评分单</el-dropdown-item></el-dropdown-menu></template></el-dropdown></template>
|
||||
<template v-else><el-button text type="primary" @click="showDetail(row)">详情</el-button><el-button v-if="canConfirm(row)" text type="success" @click="confirmOne(row)">确认</el-button><el-button text type="warning" :disabled="!row.judgeScores?.length" @click="openCorrection(row)">成绩修正</el-button><el-button text type="danger" @click="openReason('rematch', row)">重赛</el-button><el-dropdown trigger="click"><el-button text>更多</el-button><template #dropdown><el-dropdown-menu><el-dropdown-item @click="openReason('void', row)">作废成绩</el-dropdown-item><el-dropdown-item @click="exportSheets(row)">导出评分单</el-dropdown-item></el-dropdown-menu></template></el-dropdown></template>
|
||||
</template></el-table-column>
|
||||
</el-table>
|
||||
</main>
|
||||
@ -71,10 +71,9 @@
|
||||
|
||||
<el-dialog v-model="rankingVisible" title="投出组别排名" width="520px"><el-alert type="info" :closable="false" title="请选择需要投屏的项目和组别"/><el-select v-model="rankingKey" placeholder="选择项目 / 组别" style="width:100%;margin-top:16px"><el-option v-for="x in rankingOptions" :key="x.key" :label="`${x.projectName} / ${x.groupName}`" :value="x.key"/></el-select><template #footer><el-button @click="rankingVisible=false">取消</el-button><el-button type="primary" :loading="actionLoading" :disabled="!rankingKey" @click="castRanking">确定投屏</el-button></template></el-dialog>
|
||||
|
||||
<el-dialog v-model="reasonVisible" title="作废成绩" width="520px"><el-form label-position="top" class="reason-form"><el-form-item label="作废原因" required><el-input v-model="reasonText" type="textarea" :rows="4" maxlength="200" show-word-limit placeholder="请填写 2-200 个字符"/></el-form-item></el-form><template #footer><el-button @click="reasonVisible=false">取消</el-button><el-button type="danger" :loading="actionLoading" @click="submitReason">确认</el-button></template></el-dialog>
|
||||
<el-dialog v-model="reasonVisible" :title="reasonAction === 'rematch' ? '安排重赛' : '作废成绩'" width="520px"><el-form label-position="top" class="reason-form"><el-form-item :label="reasonAction === 'rematch' ? '重赛原因' : '作废原因'" required><el-input v-model="reasonText" type="textarea" :rows="4" maxlength="200" show-word-limit placeholder="请填写 2-200 个字符"/></el-form-item></el-form><template #footer><el-button @click="reasonVisible=false">取消</el-button><el-button type="danger" :loading="actionLoading" @click="submitReason">{{ reasonAction === 'rematch' ? '确认重赛' : '确认作废' }}</el-button></template></el-dialog>
|
||||
|
||||
<el-dialog v-model="correctionVisible" title="发起成绩修正" width="560px"><el-form label-position="top"><el-form-item label="裁判类型" required><el-select v-model="correctionForm.judgeRoleType" placeholder="请选择裁判类型" style="width:100%" @change="onCorrectionRoleChange"><el-option v-for="group in correctionGroups" :key="group.judgeRoleType" :label="group.judgeRoleName" :value="group.judgeRoleType"/></el-select></el-form-item><el-form-item label="评分单" required><el-select v-model="correctionForm.scoreSheetId" :disabled="!correctionForm.judgeRoleType" placeholder="请选择已提交评分单" style="width:100%"><el-option v-for="x in correctionSheetOptions" :key="x.scoreSheetId" :disabled="!isCorrectionSheetAvailable(x)" :label="`${x.judgeRole}号位 · ${x.judgeName} · ${score(x.totalScore)}分 · ${x.sheetStatusName}${x.hasPendingCorrection?'(处理中)':''}`" :value="x.scoreSheetId"/></el-select></el-form-item><el-form-item label="修正原因" required><el-input v-model="correctionForm.reason" type="textarea" :rows="3" maxlength="200" show-word-limit placeholder="请填写 2-200 个字符"/></el-form-item></el-form><template #footer><el-button @click="correctionVisible=false">取消</el-button><el-button type="primary" :loading="actionLoading" @click="submitCorrection">发送修正任务</el-button></template></el-dialog>
|
||||
<el-dialog v-model="rematchVisible" title="这是进行一项操作时必须了解的重要信息" width="520px"><div class="dialog-desc">确认重赛,本组选手将顺延至当前组别最后出场进行重赛,你还要继续吗?</div><template #footer><el-button @click="rematchVisible=false">取消</el-button><el-button type="danger" :loading="actionLoading" @click="submitRematch">继续</el-button></template></el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -98,7 +97,7 @@ if(eventId.value)sessionStorage.setItem('scoring-context',JSON.stringify({...sav
|
||||
const activeTab=ref('screen'), sessions=ref([]), selectedSession=ref(null), entries=ref([]), currentCast=ref(null);
|
||||
const loadingSessions=ref(false), loadingEntries=ref(false), offline=ref(false), sessionKeyword=ref(''), sessionStatus=ref(null), entryKeyword=ref(''), resultStatus=ref(null), selectedResultIds=ref([]);
|
||||
const detailVisible=ref(false), detailRow=ref(null), rankingVisible=ref(false), reasonVisible=ref(false), reasonAction=ref(''), reasonText=ref(''), reasonRow=ref(null), actionLoading=ref(false);
|
||||
const correctionVisible=ref(false), correctionRow=ref(null), correctionForm=ref({scoreSheetId:null,reason:''});
|
||||
const correctionVisible=ref(false), correctionRow=ref(null), correctionForm=ref({judgeRoleType:null,scoreSheetId:null,reason:''});
|
||||
const corrections=ref([]), loadingCorrections=ref(false), correctionKeyword=ref(''), correctionStatus=ref(null);
|
||||
let pollTimer;
|
||||
let entriesRequesting=false,currentCastRequesting=false;
|
||||
@ -112,6 +111,10 @@ const showDifficulty=computed(()=>!String(selectedSession.value?.scoreRuleCode||
|
||||
const detailColumns=computed(()=>{const map=new Map();for(const sheet of detailRow.value?.judgeScores||[])for(const item of sheet.scoreItems||[]){const key=`${item.itemCode}:${item.subjectCode||''}`;if(!map.has(key))map.set(key,{key,label:item.subjectCode?`${item.itemName}-${item.subjectCode}`:item.itemName,sort:item.sort})}return [...map.values()].sort((a,b)=>a.sort-b.sort)});
|
||||
const detailRows=computed(()=>(detailRow.value?.judgeScores||[]).map(sheet=>({...sheet,itemValues:Object.fromEntries((sheet.scoreItems||[]).map(item=>[`${item.itemCode}:${item.subjectCode||''}`,item.inputValue??item.countValue??item.subtotal]))})));
|
||||
const correctionStatuses=[{value:0,label:'已申请'},{value:10,label:'已批准'},{value:20,label:'已拒绝'},{value:30,label:'裁判修改中'},{value:40,label:'已完成'}];
|
||||
const correctionGroups=computed(()=>{const map=new Map();for(const s of correctionRow.value?.judgeScores||[]){if(s.judgeRoleType!==undefined&&!map.has(s.judgeRoleType)){map.set(s.judgeRoleType,{judgeRoleType:s.judgeRoleType,judgeRoleName:s.judgeRoleName||`${s.judgeRoleType}号类型`})}}return [...map.values()]});
|
||||
const correctionSheetOptions=computed(()=>(correctionRow.value?.judgeScores||[]).filter(s=>s.judgeRoleType===correctionForm.value.judgeRoleType));
|
||||
function isCorrectionSheetAvailable(x){return Boolean(x&&x.sheetStatus>=10&&x.sheetStatus<90&&!x.hasPendingCorrection)}
|
||||
function onCorrectionRoleChange(){correctionForm.value.scoreSheetId=null}
|
||||
const tabTitle=computed(()=>({screen:'成绩投屏',results:'成绩管理',corrections:'成绩修正申请'})[activeTab.value]);
|
||||
const tabDescription=computed(()=>({screen:'选择已完成成绩投到现场大屏',results:'复核成绩、查看评分单并处理异常比赛',corrections:'审批裁判发起的修正申请并追踪处理进度'})[activeTab.value]);
|
||||
|
||||
@ -148,7 +151,7 @@ async function confirmOne(row){if(actionLoading.value)return;actionLoading.value
|
||||
async function batchConfirm(){if(actionLoading.value)return;await ElMessageBox.confirm(`确认选中的 ${selectedResultIds.value.length} 条成绩?`,'批量确认');actionLoading.value=true;try{await workbenchResultsBatchConfirmPost({sessionId:selectedSession.value.sessionId,resultIds:selectedResultIds.value});ElMessage.success('批量确认完成');await loadEntries()}finally{actionLoading.value=false}}
|
||||
function openReason(action,row){reasonAction.value=action;reasonRow.value=row;reasonText.value='';reasonVisible.value=true}
|
||||
async function submitReason(){const text=reasonText.value.trim();if(text.length<2)return ElMessage.warning('请填写至少 2 个字符的原因');actionLoading.value=true;try{if(reasonAction.value==='rematch')await workbenchEntryRematchPost({originalSessionEntryId:reasonRow.value.sessionEntryId,rematchReason:text});else await workbenchResultVoidPost({sessionEntryId:reasonRow.value.sessionEntryId,voidReason:text});reasonVisible.value=false;ElMessage.success(reasonAction.value==='rematch'?'重赛已安排到场次末尾':'成绩已作废');loadEntries()}finally{actionLoading.value=false}}
|
||||
function openCorrection(row){correctionRow.value=row;correctionForm.value={scoreSheetId:null,reason:''};correctionVisible.value=true}
|
||||
function openCorrection(row){correctionRow.value=row;const firstRoleType=(row.judgeScores||[])[0]?.judgeRoleType??null;correctionForm.value={judgeRoleType:firstRoleType,scoreSheetId:null,reason:''};correctionVisible.value=true}
|
||||
async function submitCorrection(){const reason=correctionForm.value.reason.trim();if(!correctionForm.value.scoreSheetId)return ElMessage.warning('请选择裁判评分单');if(reason.length<2)return ElMessage.warning('请填写至少 2 个字符的修正原因');actionLoading.value=true;try{await judgeScoreCorrectionInitiatePost({scoreSheetId:correctionForm.value.scoreSheetId,reason});correctionVisible.value=false;ElMessage.success('修正任务已发送给裁判');loadEntries()}finally{actionLoading.value=false}}
|
||||
async function approveCorrection(row,isApproved){let remark='';if(!isApproved)remark=await ElMessageBox.prompt('请输入驳回原因','驳回修正申请',{inputValidator:v=>String(v||'').trim().length>=2||'至少输入2个字符'}).then(x=>x.value);else await ElMessageBox.confirm('批准后将向裁判下发修正任务,是否继续?','批准申请');await workbenchCorrectionApprovePost({correctionId:row.correctionId,isApproved,approveRemark:remark});ElMessage.success('申请已处理');loadCorrections()}
|
||||
async function showCorrectionDetail(row){const res=await workbenchCorrectionDetailGet(row.correctionId);const x=res?.data;if(!x)return;ElMessageBox.alert(`选手:${x.entry?.participantName||'-'}\n裁判:${x.originalScore?.judgeName||'-'}(${x.originalScore?.judgeRoleName||'-'})\n原分:${score(x.originalScore?.totalScore)}\n原因:${x.applyReason}`,'修正详情',{confirmButtonText:'关闭'})}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user