diff --git a/src/views/scoring/score-format.mjs b/src/views/scoring/score-format.mjs new file mode 100644 index 0000000..59aa2ef --- /dev/null +++ b/src/views/scoring/score-format.mjs @@ -0,0 +1,42 @@ +// 花样展示值来自后端评分单/计算快照,查询页面不重算历史总分。 +export function isFactorRule(code) { + return /FREESTYLE|SHOWCASE/i.test(String(code || '')); +} + +export function fixedScore(value, decimals = 2) { + if (value === null || value === undefined || value === '') return '—'; + const number = Number(value); + return Number.isFinite(number) ? number.toFixed(decimals) : String(value); +} + +export function judgeScoreText(sheet, ruleCode) { + if (!sheet) return '—'; + if (sheet.isDisqualified) return 'DQ'; + if (!isFactorRule(ruleCode)) return fixedScore(sheet.totalScore); + const role = String(sheet.judgeRoleType || '').toUpperCase(); + if (role === 'T') { + if (sheet.deductionScore == null) return '—'; + return fixedScore(Math.max(0, 1 - Number(sheet.deductionScore)), 3); + } + if (role === 'P') return fixedScore(sheet.performanceScore); + if (role.startsWith('D')) return fixedScore(sheet.difficultyScore); + return fixedScore(sheet.totalScore); +} + +export function calculationSummary(json) { + if (!json) return ''; + try { + const data = typeof json === 'string' ? JSON.parse(json) : json; + const factors = data.factors; + if (!factors) return ''; + const lines = [`公式:${data.formula};D=${fixedScore(factors.d)},P=${fixedScore(factors.p)},Q=${fixedScore(factors.q)},M=${fixedScore(factors.m, 3)}`]; + const failure = data.failureAggregation; + if (failure?.performanceAverageFailureCount != null) { + lines.push(`失误:表现裁判 [${failure.performanceFailureCounts.join('、')}],平均 ${failure.performanceAverageFailureCount};主裁 ${failure.chiefFailureCount};合计 ${failure.combinedFailureCount} → ${failure.roundedFailureCount} 次;失误扣分 ${fixedScore(failure.failureDeduction, 3)},其他扣分 ${fixedScore(failure.otherDeduction, 3)}`); + } else if (failure) { + lines.push(`失误(历史口径):表现裁判 [${failure.performanceFailureCounts.join('、')}],主裁 ${failure.chiefFailureCount};平均 ${failure.averageFailureCount} → ${failure.roundedFailureCount} 次;失误扣分 ${fixedScore(failure.failureDeduction, 3)},其他扣分 ${fixedScore(failure.otherDeduction, 3)}`); + } + lines.push(data.calculationVersion ? '按 BUG-71:因子先舍入,再计算总分。' : '历史计算快照:可能尚未按新规则重算。'); + return lines.join('\n'); + } catch { return ''; } +} diff --git a/src/views/scoring/screen/index.vue b/src/views/scoring/screen/index.vue index f94df4b..b190200 100644 --- a/src/views/scoring/screen/index.vue +++ b/src/views/scoring/screen/index.vue @@ -8,7 +8,7 @@
项目 / 组别{{ projectGroup }}
场次 / 场地{{ sessionVenue }}
{{ displayKind === 'team' ? 'TEAM' : 'PLAYER' }}{{ displayKindLabel }}成绩
{{ singleName }}
{{ singleAffiliation }}
出场号 {{ payload.entryNo }}当前排名 第 {{ payload.rank }} 名
{{ animatedValue('hero', payload.totalScore ?? payload.score) }}分
DQ
-
{{ item.en }}
{{ animatedValue(`metric:${item.key}`, payload[item.field]) }}
{{ item.label }}
+
{{ item.en }}
{{ item.field === 'deductionFactor' ? Number(payload[item.field]).toFixed(3) : animatedValue(`metric:${item.key}`, payload[item.field]) }}
{{ item.label }}
{{ displayKind === 'team' ? 'TEAM RANKING' : 'INDIVIDUAL RANKING' }}

{{ displayKindLabel }}排名

共 {{ rankingTotal }} 个{{ displayKind === 'team' ? '队伍' : '参赛者' }}
@@ -55,13 +55,13 @@ const sessionVenue=computed(()=>[formatScreenSession(effectiveCast.value?.sessio const singleName=computed(()=>displayKind.value==='team'?(payload.value.teamName||payload.value.participantName||'未命名队伍'):(payload.value.participantName||payload.value.teamName||'未命名选手')); const singleAffiliation=computed(()=>displayKind.value==='team'?(payload.value.unitName||''):(payload.value.teamName&&payload.value.teamName!==singleName.value?payload.value.teamName:(payload.value.unitName||''))); const scoreIconComponents={difficulty:Setting,performance:Medal,required:DocumentChecked,deduction:Flag}; -const scoreCards=computed(()=>[{key:'difficulty',label:'难度',en:'DIFFICULTY',field:'difficultyScore'},{key:'performance',label:'表现',en:'PERFORMANCE',field:'performanceScore'},{key:'required',label:'规定',en:'REQUIRED',field:'requiredScore'},{key:'deduction',label:'扣分',en:'DEDUCTION',field:'deductionScore'}].filter(item=>item.key!=='difficulty'||!String(effectiveCast.value?.scoreRuleCode||'').toUpperCase().includes('PRESCRIBED'))); +const scoreCards=computed(()=>[{key:'difficulty',label:'难度',en:'DIFFICULTY',field:'difficultyScore'},{key:'performance',label:'表现',en:'PERFORMANCE',field:'performanceScore'},{key:'required',label:'规定',en:'REQUIRED',field:'requiredScore'},{key:'deduction',label:payload.value.deductionFactor!=null?'得分系数 M':'扣分',en:'DEDUCTION',field:payload.value.deductionFactor!=null?'deductionFactor':'deductionScore'}].filter(item=>item.key!=='difficulty'||!String(effectiveCast.value?.scoreRuleCode||'').toUpperCase().includes('PRESCRIBED'))); 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)); function displayName(row){return displayKind.value==='team'?(row.teamName||row.participantName||'未命名队伍'):(row.participantName||row.teamName||'未命名选手')} function displaySubName(row){if(displayKind.value==='team')return row.unitName||'';return row.teamName&&row.teamName!==displayName(row)?row.teamName:(row.unitName||'')} function rankNumber(row,index){const rank=Number(row.rank);return Number.isFinite(rank)&&rank>0?rank:rankingPageIndex.value*6+index+1} function rankWidth(row){return `${Math.max(12,Math.round((Number(row.totalScore??row.score)||0)/maxRankScore.value*100))}%`} -function value(input){if(input===undefined||input===null||input==='')return '0';const n=Number(input);return Number.isFinite(n)?n.toFixed(2).replace(/\.00$/,'').replace(/(\.\d)0$/,'$1'):input} +function value(input){if(input===undefined||input===null||input==='')return '0';const n=Number(input);return Number.isFinite(n)?n.toFixed(2):input} function rankAnimationKey(row,index){return `rank:${row.entryNo||displayName(row)||rankingPageIndex.value*6+index}`} function animatedValue(key,fallback){return Object.prototype.hasOwnProperty.call(animatedNumbers.value,key)?value(animatedNumbers.value[key]):value(fallback)} function animateScoreNumbers(){ diff --git a/src/views/scoring/workbench/index.vue b/src/views/scoring/workbench/index.vue index df63301..e6348b2 100644 --- a/src/views/scoring/workbench/index.vue +++ b/src/views/scoring/workbench/index.vue @@ -55,11 +55,20 @@ + + + + + + + + + @@ -115,6 +124,7 @@