update:优化

This commit is contained in:
ltx 2026-10-21 17:42:29 +08:00
parent 2dd6e48247
commit c1ce081e92
4 changed files with 70 additions and 38 deletions

View File

@ -28,7 +28,7 @@
</template>
</div>
<el-dialog v-model="assignVisible" title="分配裁判" width="min(920px, 94vw)" destroy-on-close>
<el-dialog v-model="assignVisible" title="分配裁判" width="min(920px, 94vw)" class="judge-assignment-dialog" append-to-body destroy-on-close>
<div :class="['assign-layout', { readonly: viewOnly }]">
<div class="role-area">
<div v-for="section in roleSections" :key="section.title" class="role-section">
@ -51,9 +51,11 @@
</div>
</div>
<div v-if="!viewOnly" class="candidate-area">
<div class="candidate-title">{{ selectedSlot ? `可选${selectedSlot.name}` : '裁判报名' }}</div>
<el-empty v-if="eligibleCandidates.length === 0" description="暂无符合资格的报名裁判" :image-size="60" />
<el-button v-for="item in eligibleCandidates" :key="item.eventRefereeId" class="candidate" @click="chooseCandidate(item)">{{ item.name }} / {{ item.mobile }}</el-button>
<div class="candidate-title">{{ selectedSlot ? `可选${selectedSlot.name}` : '裁判报名' }}({{ eligibleCandidates.length }} 人)</div>
<div class="candidate-list">
<el-empty v-if="eligibleCandidates.length === 0" description="暂无符合资格的报名裁判" :image-size="60" />
<el-button v-for="item in eligibleCandidates" :key="item.eventRefereeId" class="candidate" @click="chooseCandidate(item)">{{ item.name }} / {{ item.mobile }}</el-button>
</div>
</div>
</div>
<template #footer><el-button @click="assignVisible = false">{{ viewOnly ? '关闭' : '取消' }}</el-button><el-button v-if="!viewOnly" type="primary" @click="saveAssignments">确定</el-button></template>
@ -177,8 +179,9 @@ watch(() => props.active, value => value && load(), { immediate: true });
.project-name { font-size: 16px; font-weight: 600; }
.project-rule { padding: 3px 8px; font-size: 12px; color: var(--el-text-color-secondary); background: var(--el-fill-color-light); border-radius: 4px; }
.submit-bar { margin-top: 24px; text-align: center; }
.assign-layout { display: grid; grid-template-columns: minmax(0, 1fr) 210px; gap: 16px; }
.assign-layout { display: grid; grid-template-columns: minmax(0, 1fr) 230px; gap: 16px; height: min(680px, calc(100dvh - 170px)); min-height: 0; overflow: hidden; }
.assign-layout.readonly { grid-template-columns: 1fr; }
.role-area { min-width: 0; min-height: 0; overflow-y: auto; padding-right: 4px; scrollbar-gutter: stable; overscroll-behavior: contain; }
.role-section { margin-bottom: 12px; overflow: hidden; border: 1px solid var(--el-border-color); border-radius: 5px; }
.role-title, .candidate-title { padding: 9px; font-weight: 600; color: var(--el-color-primary); text-align: center; background: var(--el-color-primary-light-9); }
.role-slots { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@ -189,12 +192,21 @@ watch(() => props.active, value => value && load(), { immediate: true });
.slot-quota { flex: none; font-size: 12px; color: var(--el-text-color-secondary); }
.slot-referees { display: flex; flex-wrap: wrap; gap: 6px; }
.slot-empty { color: var(--el-text-color-placeholder); }
.candidate-area { padding-bottom: 10px; overflow: hidden; border: 1px solid var(--el-border-color); border-radius: 5px; }
.candidate { width: calc(100% - 16px); margin: 8px 8px 0; }
.candidate-area { display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; border: 1px solid var(--el-border-color); border-radius: 5px; }
.candidate-title { flex: none; }
.candidate-list { flex: 1; min-height: 0; overflow-y: auto; padding: 0 8px 10px; overscroll-behavior: contain; }
.candidate-list .candidate { width: 100%; min-height: 32px; height: auto; margin: 8px 0 0; white-space: normal; text-align: left; }
.code-dialog { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.qrcode { width: 264px; height: 264px; padding: 12px; background: #fff; border: 1px solid var(--el-border-color-lighter); border-radius: 8px; box-shadow: var(--el-box-shadow-light); }
.qrcode img { display: block; width: 240px; height: 240px; }
.scan-tip { max-width: 300px; font-size: 13px; line-height: 1.6; color: var(--el-text-color-secondary); text-align: center; }
.registration-code { font-size: 14px; color: var(--el-text-color-secondary); word-break: break-all; }
@media (max-width: 768px) { .judge-panel { padding: 0; } .project-heading { flex-direction: column; align-items: flex-start; gap: 6px; } .assign-layout { grid-template-columns: 1fr; } .role-slots { grid-template-columns: 1fr; } .submit-bar { position: sticky; bottom: 0; z-index: 2; padding: 12px; background: var(--el-bg-color); } }
@media (max-width: 768px) { .judge-panel { padding: 0; } .project-heading { flex-direction: column; align-items: flex-start; gap: 6px; } .assign-layout { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) minmax(120px, .8fr); gap: 10px; } .assign-layout.readonly { grid-template-rows: minmax(0, 1fr); } .role-slots { grid-template-columns: 1fr; } .submit-bar { position: sticky; bottom: 0; z-index: 2; padding: 12px; background: var(--el-bg-color); } }
</style>
<style>
/* 分配弹窗从全屏工作台中独立挂载,避免父级滚动容器裁切;弹窗页脚始终可见。 */
.judge-assignment-dialog { max-height: calc(100dvh - 40px); margin: 20px auto; display: flex; flex-direction: column; }
.judge-assignment-dialog .el-dialog__body { min-height: 0; overflow: hidden; }
.judge-assignment-dialog .el-dialog__header, .judge-assignment-dialog .el-dialog__footer { flex: none; }
</style>

View File

@ -26,7 +26,7 @@
<section class="main-board">
<div class="current-head">
<div class="section-title"><span class="section-icon"><el-icon><UserFilled /></el-icon></span><div><h2>当前检录项目</h2><small>CURRENT CHECK-IN</small></div></div>
<div class="section-title"><span class="section-icon"><el-icon><UserFilled /></el-icon></span><div><h2>{{ eventProjectId ? '投屏项目' : '当前检录项目' }}</h2><small>{{ eventProjectId ? 'ON SCREEN' : 'CURRENT CHECK-IN' }}</small></div></div>
<div class="live-badge"><i></i>实时更新</div>
</div>
<div class="checkin-panel">
@ -76,6 +76,7 @@ const fullscreenDocument=route.query.presentation==='1'&&window.parent!==window?
const viewportRef=ref(null),viewport=ref({width:window.innerWidth,height:window.innerHeight});
let savedContext={};try{savedContext=JSON.parse(sessionStorage.getItem('scoring-context')||'{}')}catch{savedContext={}}
const eventId=Number(route.query.eventId)||Number(savedContext.eventId)||0;
const eventProjectId=Number(route.query.eventProjectId)||undefined;
const isBigScreen=computed(()=>storesTagsViewRoutes.isTagsViewCurrenFull);
const fullscreenButtonText=computed(()=>fullscreenDocument.fullscreenElement?'退出全屏':'全屏展示');
const scale=computed(()=>Math.max(.01,Math.min(viewport.value.width/1920,viewport.value.height/1080)));
@ -86,19 +87,28 @@ const stageStyle=computed(()=>({
}));
const data=ref({stages:[],units:[]}),offline=ref(false),clock=ref(new Date()),controlsVisible=ref(false),unitPage=ref(0);
let pollTimer,clockTimer,controlTimer,unitTimer,serverBaseMs=Date.now(),syncedAtMs=Date.now(),requesting=false;
const stages=computed(()=>data.value.stages||[]),currentCheckin=computed(()=>data.value.currentCheckin),currentCompeting=computed(()=>data.value.currentCompeting);
const stages=computed(()=>data.value.stages||[]),currentCheckin=computed(()=>
(eventProjectId?stages.value.find(stage=>String(stage.eventProjectId)===String(eventProjectId)):null)||data.value.currentCheckin
),currentCompeting=computed(()=>data.value.currentCompeting);
// 项目沿用接口编排顺序;缺少序号时保留接口原始顺序。
function arrangementOrder(item,index){
const value=item?.order;
if(value===null||value===undefined||value==='')return index;
const order=Number(value);
return Number.isFinite(order)?order:index;
}
const projectStages=computed(()=>{
const map=new Map();
for(const stage of stages.value){
for(const [index,stage] of stages.value.entries()){
const key=String(stage.eventProjectId??stage.projectName??'');
const current=map.get(key);
if(!current){map.set(key,{...stage,groupId:null,order:Number(stage.order)||0,totalUnits:Number(stage.totalUnits)||0,checkedUnits:Number(stage.checkedUnits)||0,statuses:[Number(stage.stageStatus)||3]});continue}
current.order=Math.min(current.order,Number(stage.order)||current.order);
if(!current){map.set(key,{...stage,groupId:null,order:arrangementOrder(stage,index),_firstIndex:index,totalUnits:Number(stage.totalUnits)||0,checkedUnits:Number(stage.checkedUnits)||0,statuses:[Number(stage.stageStatus)||3]});continue}
current.order=Math.min(current.order,arrangementOrder(stage,index));
current.totalUnits+=Number(stage.totalUnits)||0;
current.checkedUnits+=Number(stage.checkedUnits)||0;
current.statuses.push(Number(stage.stageStatus)||3);
}
return [...map.values()].sort((a,b)=>a.order-b.order).map(stage=>{
return [...map.values()].sort((a,b)=>a.order-b.order||a._firstIndex-b._firstIndex).map(stage=>{
const status=stage.statuses.every(item=>item===4)?4:stage.statuses.includes(1)?1:stage.statuses.includes(2)?2:3;
return {...stage,stageStatus:status,stageStatusText:stageStatusLabel(status)};
});
@ -120,9 +130,16 @@ const visibleStageRange=computed(()=>{
const visibleProjectStages=computed(()=>projectStages.value.slice(visibleStageRange.value.start,visibleStageRange.value.end).map((stage,index)=>({...stage,_sequenceIndex:visibleStageRange.value.start+index,_isCurrent:visibleStageRange.value.start+index===currentStageIndex.value})));
const hiddenStageCount=computed(()=>({before:visibleStageRange.value.start,after:Math.max(0,projectStages.value.length-visibleStageRange.value.end)}));
const unitsPerPage=15;
function compareIntegerText(left,right){
const a=String(left).replace(/^0+/,'')||'0',b=String(right).replace(/^0+/,'')||'0';
return a.length-b.length||a.localeCompare(b);
}
const orderedUnits=computed(()=>(data.value.units||[]).map((unit,index)=>({...unit,_originalIndex:index})).sort((a,b)=>{
const checkedOrder=Number(b.checkinStatus>=2)-Number(a.checkinStatus>=2);
return checkedOrder||(Number(a.order)||a._originalIndex)-(Number(b.order)||b._originalIndex);
// 导入秩序册的 SE 后缀就是成绩管理列表使用的出场记录 ID,保留字符串精度排序。
const aId=/^SE(\d+)$/.exec(String(a.unitKey||''))?.[1];
const bId=/^SE(\d+)$/.exec(String(b.unitKey||''))?.[1];
if(aId&&bId)return compareIntegerText(aId,bId);
return arrangementOrder(a,a._originalIndex)-arrangementOrder(b,b._originalIndex)||a._originalIndex-b._originalIndex;
}));
const unitPageCount=computed(()=>Math.max(1,Math.ceil(orderedUnits.value.length/unitsPerPage)));
const visibleUnits=computed(()=>{const page=unitPage.value%unitPageCount.value;return orderedUnits.value.slice(page*unitsPerPage,page*unitsPerPage+unitsPerPage)});
@ -135,7 +152,23 @@ function formatUnitSession(unit){const index=Number(unit?.sessionIndex);return i
function resize(){const host=viewportRef.value;if(!host)return;viewport.value={width:host.clientWidth,height:host.clientHeight}}
function tick(){clock.value=new Date(serverBaseMs+(Date.now()-syncedAtMs))}
function syncClock(){const raw=data.value.serverTime||data.value.serverTimeText;const parsed=raw?new Date(String(raw).replace(' ','T')):new Date();serverBaseMs=Number.isNaN(parsed.getTime())?Date.now():parsed.getTime();syncedAtMs=Date.now();tick()}
async function load(){if(!eventId||requesting)return;requesting=true;try{const previousChecked=new Set((data.value.units||[]).filter(unit=>unit.checkinStatus>=2).map(unit=>String(unit.unitKey)));const res=await checkinScreenCastGet({eventId,_t:Date.now()});const body=res?.data??res;const nextData=body?.data??body??{stages:[],units:[]};const hasNewChecked=(nextData.units||[]).some(unit=>unit.checkinStatus>=2&&!previousChecked.has(String(unit.unitKey)));data.value=nextData;unitPage.value=hasNewChecked?0:Math.min(unitPage.value,unitPageCount.value-1);offline.value=false;syncClock()}catch{offline.value=true}finally{requesting=false}}
async function load(){
if(!eventId||requesting)return;
requesting=true;
try{
const res=await checkinScreenCastGet({eventId,eventProjectId,_t:Date.now()});
const body=res?.data??res;
const nextData=body?.data??body??{stages:[],units:[]};
const previousProjectId=currentCheckin.value?.eventProjectId;
data.value=nextData;
// 检录推进到下一项目时,从该项目第一位选手开始滚动。
unitPage.value=String(previousProjectId)===String(currentCheckin.value?.eventProjectId)
?Math.min(unitPage.value,unitPageCount.value-1):0;
offline.value=false;
syncClock();
}catch{offline.value=true}
finally{requesting=false}
}
function toggleFullscreen(){if(fullscreenDocument.fullscreenElement)fullscreenDocument.exitFullscreen?.().catch(()=>{});else fullscreenDocument.documentElement.requestFullscreen?.({navigationUI:'hide'}).catch(()=>{});storesTagsViewRoutes.setCurrenFullscreen(true)}
function requestAutoFullscreen(){if(route.query.autofullscreen==='1'&&!document.fullscreenElement)document.documentElement.requestFullscreen?.({navigationUI:'hide'}).catch(()=>{})}
function showControls(){controlsVisible.value=true;window.clearTimeout(controlTimer);controlTimer=window.setTimeout(()=>controlsVisible.value=false,2600)}

View File

@ -18,7 +18,6 @@
<div class="hero-score-label"><span>总得分</span><small>TOTAL SCORE</small></div>
<div class="hero-score"><strong class="score-ticker">{{ animatedValue('hero', payload.totalScore ?? payload.score) }}</strong><span class="score-unit"><b>分</b><small>PTS</small></span></div>
<div class="score-beam"><i></i></div>
<div class="hero-meta"><span v-if="payload.entryNo"><small>出场号<em>ENTRY NO.</em></small><b>{{ payload.entryNo }}</b></span><span v-if="payload.rank"><small>当前排名<em>CURRENT RANK</em></small><b>第 {{ payload.rank }} 名</b></span></div>
<div v-if="payload.isDisqualified" class="dq-stamp">DQ</div>
</div>
<div class="score-metrics" :style="{ gridTemplateColumns: `repeat(${Math.max(scoreCards.length, 1)}, 1fr)` }"><article v-for="(item,index) in scoreCards" :key="item.key" :class="['metric-card', item.key]" :style="{ '--delay': `${index * 105 + 340}ms` }"><div class="metric-head"><div class="metric-title"><b>{{ item.label }}</b><small>{{ item.en }}</small></div><span class="metric-icon"><el-icon><component :is="scoreIconComponents[item.key]" /></el-icon></span></div><strong class="score-ticker">{{ animatedValue(`metric:${item.key}`, payload[item.field], item.field === 'deductionFactor' ? 3 : 2) }}</strong><div class="metric-line"></div></article></div>
@ -90,7 +89,7 @@ const scoreCards=computed(()=>{
const prescribed=String(scoreRuleCode||'').toUpperCase().includes('PRESCRIBED');
const factor=isFactorRule(scoreRuleCode);
const wire=isWireRule(scoreRuleCode);
return [{key:'difficulty',label:'难度得分',en:'DIFFICULTY SCORE',field:'difficultyScore'},{key:'performance',label:prescribed?'创意编排分':factor?'表现系数':'表现',en:prescribed?'CREATIVE COMPOSITION':factor?'PERFORMANCE FACTOR':'PERFORMANCE SCORE',field:'performanceScore'},{key:'required',label:prescribed?'完成质量分':factor?'规定系数':'规定',en:prescribed?'EXECUTION QUALITY':factor?'REQUIRED FACTOR':'REQUIRED SCORE',field:'requiredScore'},{key:'deduction',label:factor?'扣分系数 M':'扣分',en:factor?'DEDUCTION FACTOR':'DEDUCTIONS',field:payload.value.deductionFactor!=null?'deductionFactor':'deductionScore'}]
return [{key:'difficulty',label:'难度得分',en:'DIFFICULTY SCORE',field:'difficultyScore'},{key:'performance',label:prescribed?'创意编排分':factor?'表现系数':'表现',en:prescribed?'CREATIVE COMPOSITION':factor?'PERFORMANCE FACTOR':'PERFORMANCE SCORE',field:'performanceScore'},{key:'required',label:prescribed?'完成质量分':factor?'规定系数':'规定',en:prescribed?'EXECUTION QUALITY':factor?'REQUIRED FACTOR':'REQUIRED SCORE',field:'requiredScore'},{key:'deduction',label:factor?'扣分系数':'扣分',en:factor?'DEDUCTION FACTOR':'DEDUCTIONS',field:payload.value.deductionFactor!=null?'deductionFactor':'deductionScore'}]
.filter(item=>(item.key!=='difficulty'||!prescribed)&&(item.key!=='required'||!wire));
});
const rankingAll=computed(()=>Array.isArray(payload.value)?payload.value:[]),rankingTotal=computed(()=>rankingAll.value.length),rankingPageCount=computed(()=>Math.max(1,Math.ceil(rankingTotal.value/6))),rankingPageIndex=computed(()=>rankPage.value%rankingPageCount.value),rankingRows=computed(()=>rankingAll.value.slice(rankingPageIndex.value*6,rankingPageIndex.value*6+6)),maxRankScore=computed(()=>Math.max(...rankingAll.value.map(row=>Number(row.totalScore??row.score)||0),1));
@ -399,9 +398,6 @@ onBeforeUnmount(()=>{window.clearInterval(pollTimer);window.clearInterval(rankTi
background: linear-gradient(90deg, transparent, rgba(132, 239, 255, 0.12), transparent);
animation: panel-sweep 7s ease-in-out infinite;
}
.hero-meta { position: absolute; z-index: 2; left: 28px; bottom: 20px; display: flex; gap: 10px; }
.hero-meta span { padding: 7px 12px; border: 1px solid rgba(100, 220, 255, 0.22); border-radius: 999px; color: #78b9e7; background: rgba(3, 28, 91, 0.5); font-size: 12px; backdrop-filter: blur(8px); }
.hero-meta b { margin-left: 5px; color: #d7f9ff; font-size: 13px; }
.score-ticker { display: inline-block; will-change: transform, contents; }
.hero-score strong { animation: score-arrive 0.72s cubic-bezier(0.18, 0.82, 0.3, 1) both; }
.metric-card .score-ticker { animation: number-pop 0.72s cubic-bezier(0.18, 0.9, 0.32, 1.18) both; animation-delay: calc(var(--delay) + 100ms); }
@ -521,11 +517,6 @@ onBeforeUnmount(()=>{window.clearInterval(pollTimer);window.clearInterval(rankTi
.view-score .score-unit small { margin: 0; color: #8fbcd0; font-size: 13px; letter-spacing: .14em; }
.view-score .score-beam { left: 29%; right: 29%; bottom: 77px; height: 1px; background: linear-gradient(90deg,transparent,#c7f3f5,transparent); box-shadow: 0 0 18px 2px rgba(87,206,237,.45); }
.view-score .score-beam i { width: 10px; height: 10px; border-width: 1px; background: #8de4eb; box-shadow: 0 0 12px #7fdce9; }
.view-score .hero-meta { left: 50%; bottom: 18px; gap: 14px; transform: translateX(-50%); white-space: nowrap; }
.view-score .hero-meta span { display: flex; align-items: center; gap: 12px; padding: 9px 15px; border-color: rgba(119,196,228,.2); border-radius: 8px; background: rgba(4,32,78,.6); }
.view-score .hero-meta small { display: flex; flex-direction: column; gap: 2px; color: #c0dcec; font-size: 11px; font-weight: 700; letter-spacing: .08em; }
.view-score .hero-meta small em { color: #83a9c4; font-size: 9px; font-style: normal; font-weight: 500; letter-spacing: .1em; }
.view-score .hero-meta b { margin: 0; color: #e4f7ff; font-size: 15px; }
.view-score .score-metrics { gap: 16px; }
.view-score .metric-card,
.view-score .metric-card.performance,
@ -592,9 +583,6 @@ onBeforeUnmount(()=>{window.clearInterval(pollTimer);window.clearInterval(rankTi
.view-score .hero-score strong { font-size: 178px; }
.view-score .score-unit b { font-size: 24px; }
.view-score .score-unit small { font-size: 15px; }
.view-score .hero-meta small { font-size: 13px; }
.view-score .hero-meta small em { font-size: 10px; }
.view-score .hero-meta b { font-size: 17px; }
.view-score .metric-title b { font-size: 25px; }
.view-score .metric-head small { font-size: 12px; }
.view-score .metric-card > strong { font-size: 70px; }
@ -620,9 +608,9 @@ onBeforeUnmount(()=>{window.clearInterval(pollTimer);window.clearInterval(rankTi
.score-screen .rank-row { background: linear-gradient(90deg,color-mix(in srgb,var(--screen-theme) 60%,#07183d),color-mix(in srgb,var(--screen-theme) 25%,#061135)); }
.score-screen .title-wing i { background: linear-gradient(90deg,transparent,var(--cyan)); }
.score-screen .context-icon { color: var(--cyan); border-color: color-mix(in srgb,var(--screen-theme) 45%,#b1f2ff); }
.score-screen :is(.event-title h1,.context-bar b,.entity-name,.hero-score-label,.hero-score strong,.score-unit b,.hero-meta b,.metric-title b,.metric-card > strong,.ranking-heading h2,.rank-title strong,.rank-value strong,.rank-value > span,.waiting h1) { color: var(--screen-text); }
.score-screen :is(.event-kicker span,.context-bar small,.entity-affiliation,.hero-score-label small,.score-unit small,.hero-meta small,.metric-head small,.ranking-status small,.rank-title small,.rank-value > span small,.screen-footer,.waiting p) { color: color-mix(in srgb,var(--screen-text) 78%,transparent); }
.score-screen :is(.event-kicker small,.context-bar small em,.entity-chip small,.hero-meta small em,.screen-footer em,.waiting > small) { color: color-mix(in srgb,var(--screen-text) 62%,transparent); }
.score-screen :is(.event-title h1,.context-bar b,.entity-name,.hero-score-label,.hero-score strong,.score-unit b,.metric-title b,.metric-card > strong,.ranking-heading h2,.rank-title strong,.rank-value strong,.rank-value > span,.waiting h1) { color: var(--screen-text); }
.score-screen :is(.event-kicker span,.context-bar small,.entity-affiliation,.hero-score-label small,.score-unit small,.metric-head small,.ranking-status small,.rank-title small,.rank-value > span small,.screen-footer,.waiting p) { color: color-mix(in srgb,var(--screen-text) 78%,transparent); }
.score-screen :is(.event-kicker small,.context-bar small em,.entity-chip small,.screen-footer em,.waiting > small) { color: color-mix(in srgb,var(--screen-text) 62%,transparent); }
.score-screen.view-score .entity-chip span { color: var(--screen-text); }
.score-screen.view-score .metric-card > strong { color: var(--screen-text); }
@ -660,7 +648,6 @@ onBeforeUnmount(()=>{window.clearInterval(pollTimer);window.clearInterval(rankTi
.score-screen.theme-light.view-score .hero-score strong { text-shadow: 0 12px 26px rgba(30,80,130,.14); }
.score-screen.theme-light.view-score .score-beam { background: linear-gradient(90deg,transparent,var(--screen-theme),transparent); box-shadow: 0 0 16px color-mix(in srgb,var(--screen-theme) 25%,transparent); }
.score-screen.theme-light.view-score .score-beam i { background: var(--screen-theme); box-shadow: 0 0 10px color-mix(in srgb,var(--screen-theme) 35%,transparent); }
.score-screen.theme-light.view-score .hero-meta span { border-color: #dbe7f2; background: rgba(246,250,254,.9); }
.score-screen.theme-light.view-score .metric-card {
border-color: color-mix(in srgb,var(--metric-accent) 25%,#dae6f0);
background: radial-gradient(circle at 90% 0,color-mix(in srgb,var(--metric-accent) 15%,transparent),transparent 55%),linear-gradient(145deg,#fff,#f6faff);

View File

@ -581,7 +581,7 @@ async function download(blob,name){const url=URL.createObjectURL(blob);const a=d
async function exportResults(){const blob=await workbenchResultsExportGet({eventId:eventId.value,eventProjectId:selectedSession.value?.eventProjectId,keyword:entryKeyword.value||undefined,resultStatus:resultStatus.value??undefined});download(blob,`成绩册_${selectedProject.value?.projectName||eventId.value}.xlsx`)}
async function exportSheets(row){const session=row?._session||selectedSession.value;if(!session)return;const blob=await workbenchScoreSheetsExportGet({eventId:eventId.value,sessionId:session.sessionId});download(blob,`评分单_${session.projectName || ''}_${formatSessionName(session,session._sourceIndex)}.xlsx`)}
const presentationFrameId='yd-event-presentation-frame';
function scoreScreenHref(venueId,displayKind=castEntityKind.value,castMode='live'){return router.resolve({path:'/scoring/screen',query:{eventId:eventId.value,venueId,eventName:eventName.value,mode:'freestyle',castMode,displayKind,autofullscreen:'1',presentation:'1'}}).href}
function scoreScreenHref(venueId,displayKind=castEntityKind.value,castMode='live',eventProjectId){return router.resolve({path:'/scoring/screen',query:{eventId:eventId.value,venueId,eventName:eventName.value,mode:'freestyle',castMode,displayKind,eventProjectId,autofullscreen:'1',presentation:'1'}}).href}
function ensurePresentationShell(popup){
const doc=popup.document;
let frame=doc.getElementById(presentationFrameId);
@ -665,9 +665,9 @@ function showPresentationScreen(presentation,href){
const targetUrl=new URL(href,window.location.href).href;
const target=new URL(targetUrl);
const mode=target.searchParams.get('castMode')==='checkin'?'checkin':'live';
const scope=`${target.searchParams.get('eventId')||''}:${target.searchParams.get('venueId')||''}`;
const scope=`${target.searchParams.get('eventId')||''}:${target.searchParams.get('venueId')||''}:${target.searchParams.get('eventProjectId')||''}`;
frame.dataset.presentationMode=mode;
if(frame.dataset.presentationScope!==scope){
if(frame.dataset.presentationScope!==scope||mode==='checkin'){
frame.dataset.presentationScope=scope;
frame.src=targetUrl;
}else{
@ -679,7 +679,7 @@ function showPresentationScreen(presentation,href){
function showScoreScreen(presentation,venueId,displayKind=castEntityKind.value){showPresentationScreen(presentation,scoreScreenHref(venueId,displayKind,'live'))}
function closeUnusedScreen(presentation){if(presentation?.created&&presentation.popup&&!presentation.popup.closed)presentation.popup.close()}
function openScoreScreen(){rememberCastKind();const presentation=prepareScoreScreen(selectedSession.value?.venueId);if(presentation)showScoreScreen(presentation,selectedSession.value?.venueId,castEntityKind.value)}
function openCheckinScreen(){const venueId=selectedSession.value?.venueId;const presentation=prepareScoreScreen();if(presentation)showPresentationScreen(presentation,scoreScreenHref(venueId,castEntityKind.value,'checkin'))}
function openCheckinScreen(){const venueId=selectedSession.value?.venueId;const eventProjectId=selectedSession.value?.eventProjectId;const presentation=prepareScoreScreen();if(presentation)showPresentationScreen(presentation,scoreScreenHref(venueId,castEntityKind.value,'checkin',eventProjectId))}
function goLauncher(){router.push({path:'/scoring/launcher'})}
function toggleCorrections(){activeTab.value=activeTab.value==='corrections'?'results':'corrections';if(activeTab.value==='corrections'){cancelEntriesRequest();void loadCorrections()}else pollNow(true)}
function pollNow(force=false){