2026-09-06 22:36:26 +08:00
|
|
|
|
<template>
|
2026-09-10 16:28:27 +08:00
|
|
|
|
<div :class="['score-screen',{embedded:!isBigScreen}]" @mousemove="showControls">
|
2026-09-06 22:36:26 +08:00
|
|
|
|
<div class="screen-lines"></div><div class="skyline"></div>
|
2026-09-10 16:28:27 +08:00
|
|
|
|
<div class="screen-controls" :class="{ visible: controlsVisible }">
|
|
|
|
|
|
<button @click="toggleFullscreen">{{ fullscreenButtonText }}</button>
|
|
|
|
|
|
<button @click="loadCast">刷新</button>
|
|
|
|
|
|
<button v-if="effectiveCast?.castType === 2" @click="manualMode = (manualMode === 'score' ? '' : 'score')">
|
|
|
|
|
|
{{ manualMode === 'score' ? '返回实时' : '查看单人成绩' }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button v-if="effectiveCast?.castType === 1" @click="manualMode = (manualMode === 'ranking' ? '' : 'ranking')">
|
|
|
|
|
|
{{ manualMode === 'ranking' ? '返回实时' : '查看组别排行' }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<span :class="{ offline }">
|
|
|
|
|
|
{{ offline ? '连接异常' : (activeViewMode === 'score' ? '单人成绩' : activeViewMode === 'ranking' ? '组别排行' : (cast ? (cast.castType === 1 ? '实时单人' : '实时排行') : '等待投屏')) }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<main v-if="effectiveCast" class="screen-content">
|
2026-09-06 22:36:26 +08:00
|
|
|
|
<h1><i></i>{{ title }}<i></i></h1>
|
|
|
|
|
|
<div class="meta-pill"><span class="people-icon">◎</span><b>项目-组别:{{ projectGroup }}</b><span class="meta-spacer"></span><span class="pin-icon">⌖</span><b>场次-场地:{{ sessionVenue }}</b></div>
|
|
|
|
|
|
|
2026-09-10 16:28:27 +08:00
|
|
|
|
<section v-if="effectiveCast.castType === 1" class="single-board">
|
2026-09-06 22:36:26 +08:00
|
|
|
|
<div class="board-label"><i></i>当前选手<i></i></div>
|
|
|
|
|
|
<div class="player-name">{{ singleName }}</div>
|
|
|
|
|
|
<div class="score-value">{{ value(payload.totalScore ?? payload.score) }}</div>
|
|
|
|
|
|
<div class="score-glow"></div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section v-else class="rank-board">
|
|
|
|
|
|
<div v-for="(row,index) in rankingRows" :key="`${row.entryNo}-${index}`" class="rank-row">
|
2026-09-10 16:52:56 +08:00
|
|
|
|
<div v-if="rankNumber(row,index) < 4" :class="['medal',`medal-${rankNumber(row,index)}`]"><span>{{ rankNumber(row,index) }}</span></div><div v-else class="rank-no">{{ rankNumber(row,index) }}</div>
|
2026-09-06 22:36:26 +08:00
|
|
|
|
<div class="rank-main"><strong><span class="people-icon small">◎</span>{{ row.teamName || row.participantName || '—' }}</strong><div class="bar"><i :style="{ width: rankWidth(row) }"></i></div></div>
|
|
|
|
|
|
<div class="rank-score">{{ value(row.totalScore ?? row.score) }}<small>分</small></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
2026-09-10 16:28:27 +08:00
|
|
|
|
<div v-if="effectiveCast.castType === 1" class="score-cards" :style="{gridTemplateColumns:`repeat(${Math.max(scoreCards.length,1)},1fr)`}">
|
2026-09-06 22:36:26 +08:00
|
|
|
|
<div v-for="item in scoreCards" :key="item.key" :class="['score-card',item.key]"><span class="card-icon">{{ item.icon }}</span><b>{{ value(payload[item.field]) }}</b><i></i><strong>{{ item.label }}</strong></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</main>
|
2026-09-10 16:52:56 +08:00
|
|
|
|
<div v-else key="waiting" class="waiting"><span class="waiting-ring"></span><h1>等待成绩投屏</h1><p>请在打分工作台选择成绩或组别排名</p></div>
|
|
|
|
|
|
</Transition>
|
2026-09-06 22:36:26 +08:00
|
|
|
|
</div>
|
2026-09-10 16:52:56 +08:00
|
|
|
|
</Teleport>
|
2026-09-06 22:36:26 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
2026-09-09 18:30:09 +08:00
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
2026-09-10 16:28:27 +08:00
|
|
|
|
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
2026-09-06 22:36:26 +08:00
|
|
|
|
import { workbenchScreenCastCurrentGet } from '/@/api/match/workbench';
|
|
|
|
|
|
|
2026-09-10 16:28:27 +08:00
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
viewMode: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: '',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-09-09 18:30:09 +08:00
|
|
|
|
const route=useRoute(), router=useRouter();
|
2026-09-10 16:28:27 +08:00
|
|
|
|
const storesTagsViewRoutes=useTagsViewRoutes();
|
2026-09-09 18:30:09 +08:00
|
|
|
|
let savedContext={};try{savedContext=JSON.parse(sessionStorage.getItem('scoring-context')||'{}')}catch{savedContext={}}
|
|
|
|
|
|
const eventId=Number(route.query.eventId)||Number(savedContext.eventId)||0, venueId=route.query.venueId?Number(route.query.venueId):(Number(savedContext.venueId)||undefined);
|
2026-09-10 16:28:27 +08:00
|
|
|
|
const cast=ref(null), offline=ref(false), controlsVisible=ref(true), rankPage=ref(0);
|
|
|
|
|
|
const manualMode=ref('');
|
2026-09-06 22:36:26 +08:00
|
|
|
|
let pollTimer, controlTimer, rankTimer;
|
2026-09-10 16:52:56 +08:00
|
|
|
|
function normalizePayload(value){
|
|
|
|
|
|
if(Array.isArray(value))return value.map(normalizePayload);
|
|
|
|
|
|
if(!value||typeof value!=='object')return value;
|
|
|
|
|
|
return Object.fromEntries(Object.entries(value).map(([key,item])=>[key.charAt(0).toLowerCase()+key.slice(1),normalizePayload(item)]));
|
|
|
|
|
|
}
|
2026-09-10 16:28:27 +08:00
|
|
|
|
|
|
|
|
|
|
const activeViewMode = computed(() => props.viewMode || manualMode.value || (route.query.viewMode || ''));
|
|
|
|
|
|
|
|
|
|
|
|
const effectiveCast = computed(() => {
|
|
|
|
|
|
const mode = activeViewMode.value;
|
|
|
|
|
|
if (mode === 'score') {
|
|
|
|
|
|
if (cast.value && cast.value.castType === 1) return cast.value;
|
|
|
|
|
|
const sample = {
|
|
|
|
|
|
participantName: payload.value?.participantName || '崔一',
|
|
|
|
|
|
groupName: cast.value?.groupName || '少儿组',
|
|
|
|
|
|
difficultyScore: 21,
|
|
|
|
|
|
performanceScore: 15,
|
|
|
|
|
|
requiredScore: 3,
|
|
|
|
|
|
deductionScore: 3,
|
|
|
|
|
|
totalScore: 85.5,
|
|
|
|
|
|
};
|
|
|
|
|
|
return {
|
|
|
|
|
|
castType: 1,
|
|
|
|
|
|
eventName: cast.value?.eventName || String(route.query.eventName || '赛事成绩大屏'),
|
|
|
|
|
|
projectName: cast.value?.projectName || '30秒单摇',
|
|
|
|
|
|
groupName: cast.value?.groupName || '少儿组',
|
|
|
|
|
|
sessionNo: cast.value?.sessionNo || '1',
|
|
|
|
|
|
venueName: cast.value?.venueName || '1',
|
|
|
|
|
|
scoreRuleCode: cast.value?.scoreRuleCode || '',
|
|
|
|
|
|
payloadJson: JSON.stringify(sample),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
if (mode === 'ranking') {
|
|
|
|
|
|
if (cast.value && cast.value.castType === 2) return cast.value;
|
|
|
|
|
|
const sample = [85.5, 83.2, 78, 75.5, 71, 68.5].map((score, index) => ({
|
|
|
|
|
|
entryNo: `A00${index + 1}`,
|
|
|
|
|
|
participantName: ['崔一', '崔二', '崔三', '崔四', '崔五', '崔六'][index],
|
|
|
|
|
|
totalScore: score,
|
|
|
|
|
|
rank: index + 1,
|
|
|
|
|
|
}));
|
|
|
|
|
|
return {
|
|
|
|
|
|
castType: 2,
|
|
|
|
|
|
eventName: cast.value?.eventName || String(route.query.eventName || '赛事成绩大屏'),
|
|
|
|
|
|
projectName: cast.value?.projectName || '30秒单摇',
|
|
|
|
|
|
groupName: cast.value?.groupName || '少儿组',
|
|
|
|
|
|
sessionNo: cast.value?.sessionNo || '1',
|
|
|
|
|
|
venueName: cast.value?.venueName || '1',
|
|
|
|
|
|
payloadJson: JSON.stringify(sample),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
return cast.value;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const payload=computed(()=>{try{return effectiveCast.value?.payloadJson?normalizePayload(JSON.parse(effectiveCast.value.payloadJson)): {}}catch{return {}}});
|
|
|
|
|
|
const title=computed(()=>effectiveCast.value?.eventName||String(route.query.eventName||'赛事成绩大屏'));
|
|
|
|
|
|
const projectGroup=computed(()=>[effectiveCast.value?.projectName,effectiveCast.value?.groupName||payload.value.groupName].filter(Boolean).join('-')||'—');
|
|
|
|
|
|
function formatScreenSession(sessionNo) {
|
|
|
|
|
|
if (!sessionNo) return '';
|
|
|
|
|
|
const match = String(sessionNo).match(/-(\d+)$/);
|
|
|
|
|
|
if (match) {
|
|
|
|
|
|
const n = parseInt(match[1], 10);
|
|
|
|
|
|
if (n > 0) return `第${n}场`;
|
|
|
|
|
|
}
|
|
|
|
|
|
const pure = String(sessionNo).replace(/^EP/i, '').trim();
|
|
|
|
|
|
if (/^\d+$/.test(pure)) {
|
|
|
|
|
|
const n = parseInt(pure, 10);
|
|
|
|
|
|
if (n > 0 && n < 1000) return `第${n}场`;
|
|
|
|
|
|
}
|
|
|
|
|
|
return String(sessionNo).replace(/^EP/i, '第') + (String(sessionNo).endsWith('场') ? '' : '场');
|
|
|
|
|
|
}
|
|
|
|
|
|
const sessionVenue=computed(()=>[formatScreenSession(effectiveCast.value?.sessionNo),effectiveCast.value?.venueName].filter(Boolean).join(' · ')||'—');
|
2026-09-06 22:36:26 +08:00
|
|
|
|
const singleName=computed(()=>payload.value.teamName||payload.value.participantName||'—');
|
2026-09-10 16:52:56 +08:00
|
|
|
|
const rankingPageIndex=computed(()=>{const all=Array.isArray(payload.value)?payload.value:[];return rankPage.value%Math.max(1,Math.ceil(all.length/6))});
|
|
|
|
|
|
const rankingRows=computed(()=>{const all=Array.isArray(payload.value)?payload.value:[];const page=rankingPageIndex.value;return all.slice(page*6,page*6+6)});
|
2026-09-06 22:36:26 +08:00
|
|
|
|
const scoreCards=computed(()=>[
|
|
|
|
|
|
{key:'difficulty',label:'难 度',field:'difficultyScore',icon:'✿'},
|
|
|
|
|
|
{key:'performance',label:'表 现',field:'performanceScore',icon:'◎'},
|
|
|
|
|
|
{key:'required',label:'规 定',field:'requiredScore',icon:'▣'},
|
|
|
|
|
|
{key:'deduction',label:'扣 分',field:'deductionScore',icon:'⚑'},
|
2026-09-10 16:28:27 +08:00
|
|
|
|
].filter(x=>x.key!=='difficulty'||!String(effectiveCast.value?.scoreRuleCode||'').toUpperCase().includes('PRESCRIBED')));
|
2026-09-06 22:36:26 +08:00
|
|
|
|
const maxRank=computed(()=>Math.max(...rankingRows.value.map(x=>Number(x.totalScore??x.score)||0),1));
|
2026-09-10 16:28:27 +08:00
|
|
|
|
const isBigScreen=computed(()=>storesTagsViewRoutes.isTagsViewCurrenFull);
|
|
|
|
|
|
const fullscreenButtonText=computed(()=>isBigScreen.value?'关闭大屏':'打开大屏');
|
2026-09-06 22:36:26 +08:00
|
|
|
|
|
|
|
|
|
|
let castRequesting=false;
|
2026-09-09 18:30:09 +08:00
|
|
|
|
async function loadCast(){if(!eventId||castRequesting)return;castRequesting=true;try{const res=await workbenchScreenCastCurrentGet({eventId,venueId,_t:Date.now()});const body=res?.data??res;const current=body?.data??body;cast.value=current?.screenCastId?current:null;offline.value=false}catch{offline.value=true}finally{castRequesting=false}}
|
2026-09-06 22:36:26 +08:00
|
|
|
|
function value(x){if(x===undefined||x===null||x==='')return '0';const n=Number(x);return Number.isFinite(n)?n.toFixed(2).replace(/\.00$/,'').replace(/(\.\d)0$/,'$1'):x}
|
2026-09-10 16:52:56 +08:00
|
|
|
|
function rankNumber(row,index){const rank=Number(row.rank);return Number.isFinite(rank)&&rank>0?rank:rankingPageIndex.value*6+index+1}
|
2026-09-06 22:36:26 +08:00
|
|
|
|
function rankWidth(row){return `${Math.max(18,Math.round((Number(row.totalScore??row.score)||0)/maxRank.value*86))}%`}
|
2026-09-10 16:28:27 +08:00
|
|
|
|
function toggleFullscreen(){const next=!isBigScreen.value;storesTagsViewRoutes.setCurrenFullscreen(next);if(next){if(!document.fullscreenElement)document.documentElement.requestFullscreen?.().catch(()=>{})}else{if(document.fullscreenElement)document.exitFullscreen?.().catch(()=>{})}}
|
2026-09-06 22:36:26 +08:00
|
|
|
|
function showControls(){controlsVisible.value=true;window.clearTimeout(controlTimer);controlTimer=window.setTimeout(()=>controlsVisible.value=false,3000)}
|
2026-09-10 16:28:27 +08:00
|
|
|
|
function fullscreenChange(){if(!document.fullscreenElement&&isBigScreen.value)storesTagsViewRoutes.setCurrenFullscreen(false)}
|
2026-09-06 22:36:26 +08:00
|
|
|
|
function setPreview(){
|
|
|
|
|
|
if(!route.query.preview)return;
|
|
|
|
|
|
const ranking=route.query.preview==='ranking';
|
|
|
|
|
|
const sample=ranking?[85.5,83.2,78,75.5,71,68.5].map((score,index)=>({entryNo:`A00${index+1}`,participantName:['崔一','崔二','崔三','崔四','崔五','崔六'][index],totalScore:score,rank:index+1})):{participantName:'崔一',groupName:'少儿组',difficultyScore:21,performanceScore:15,requiredScore:3,deductionScore:3,totalScore:85.5};
|
|
|
|
|
|
cast.value={castType:ranking?2:1,eventName:'2026年上海城市业余联赛跳绳联赛(杨浦站)',projectName:'30秒单摇',groupName:'少儿组',sessionNo:'2',venueName:'1',payloadJson:JSON.stringify(sample)};
|
|
|
|
|
|
}
|
|
|
|
|
|
onMounted(()=>{setPreview();if(!route.query.preview)loadCast();pollTimer=window.setInterval(()=>document.visibilityState==='visible'&&!route.query.preview&&loadCast(),3000);rankTimer=window.setInterval(()=>rankPage.value++,9000);document.addEventListener('fullscreenchange',fullscreenChange);showControls()});
|
|
|
|
|
|
onBeforeUnmount(()=>{window.clearInterval(pollTimer);window.clearInterval(rankTimer);window.clearTimeout(controlTimer);document.removeEventListener('fullscreenchange',fullscreenChange)});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
2026-09-09 18:30:09 +08:00
|
|
|
|
.score-screen{--cyan:#20dce8;position:relative;width:100vw;height:100vh;min-width:960px;min-height:620px;overflow:hidden;color:#fff;background:radial-gradient(circle at 30% 45%,#074dcc 0,#062d91 38%,#020a3c 78%);font-family:"Microsoft YaHei","PingFang SC",sans-serif}.score-screen.embedded{width:100%;height:calc(100vh - 85px)}.screen-lines{position:absolute;inset:0;opacity:.18;background-image:radial-gradient(ellipse at 50% 110%,transparent 0 36%,#38abf3 36.2% 36.35%,transparent 36.5% 39%,#38abf3 39.2% 39.35%,transparent 39.5%),linear-gradient(105deg,transparent 48%,rgba(41,167,242,.28) 48.2%,transparent 48.4%);background-size:100% 100%,24% 32%;background-position:center,bottom;background-repeat:no-repeat,repeat-x}.skyline{position:absolute;left:0;right:0;bottom:0;height:27%;opacity:.18;background:linear-gradient(90deg,transparent 0 3%,#26a6e4 3.1% 3.25%,transparent 3.35% 11%,#26a6e4 11.1% 11.2%,transparent 11.3% 84%,#26a6e4 84.1% 84.25%,transparent 84.35%);clip-path:polygon(0 100%,0 70%,3% 70%,3% 18%,4% 18%,4% 70%,10% 70%,10% 48%,12% 48%,12% 65%,17% 65%,17% 35%,19% 35%,19% 69%,24% 69%,24% 53%,27% 53%,27% 76%,72% 76%,72% 59%,75% 59%,75% 35%,77% 35%,77% 69%,83% 69%,83% 42%,85% 42%,85% 64%,91% 64%,91% 50%,94% 50%,94% 71%,100% 71%,100% 100%);background-color:#31adf3}.screen-content{position:relative;z-index:1;height:100%;box-sizing:border-box;padding:2.5vh 3.1vw 2.8vh;display:flex;flex-direction:column}.screen-content>h1{display:flex;align-items:center;justify-content:center;gap:2.2vw;margin:0 0 2.4vh;font-size:clamp(34px,3.7vw,74px);line-height:1.25;white-space:nowrap}.screen-content>h1 i{width:6vw;height:1.2vh;background:linear-gradient(90deg,transparent,#17dbe7);clip-path:polygon(0 35%,100% 0,85% 100%,0 70%)}.screen-content>h1 i:last-child{transform:scaleX(-1);background:linear-gradient(90deg,transparent,#c827bb)}.meta-pill{width:72%;align-self:center;display:flex;align-items:center;box-sizing:border-box;padding:1.2vh 2vw;margin-bottom:3.5vh;border:2px solid rgba(31,163,255,.55);border-radius:999px;background:rgba(0,8,56,.35);font-size:clamp(17px,1.55vw,31px)}.meta-pill b{font-weight:600}.meta-spacer{flex:1}.people-icon,.pin-icon{margin-right:.8vw;color:var(--cyan);font-size:1.5em}.single-board,.rank-board{position:relative;flex:1;min-height:0;border:2px solid rgba(80,186,255,.72);border-radius:1.4vw;background:linear-gradient(110deg,rgba(16,103,237,.82),rgba(0,20,105,.75));box-shadow:inset 0 0 40px rgba(4,99,238,.22)}.board-label{position:absolute;left:50%;top:0;transform:translate(-50%,-2px);min-width:20vw;padding:1.1vh 4vw 1.5vh;text-align:center;color:#9ee9ff;font-size:clamp(20px,2vw,39px);font-weight:700;border-bottom:2px solid #2b8ef2;clip-path:polygon(0 0,100% 0,86% 100%,14% 100%);background:rgba(0,35,132,.75)}.board-label i{display:inline-block;width:2vw;height:.65vh;margin:0 .8vw;background:repeating-linear-gradient(90deg,var(--cyan) 0 5px,transparent 5px 10px);transform:skew(30deg)}.player-name{position:absolute;left:0;right:0;top:20%;text-align:center;font-size:clamp(29px,3.2vw,60px);font-weight:700}.score-value{position:absolute;left:0;right:0;top:34%;text-align:center;font-size:clamp(110px,13vw,245px);font-weight:900;line-height:1;text-shadow:0 18px 38px rgba(0,0,0,.42),0 0 20px rgba(65,174,255,.3)}.score-glow{position:absolute;left:18%;right:18%;bottom:13%;height:3px;background:#bffcff;box-shadow:0 0 8px 4px #27bdff,0 0 25px 12px #1489f5}.score-cards{display:grid;grid-template-columns:repeat(4,1fr);gap:1.7vw;margin-top:2.8vh;height:19vh;min-height:125px}.score-card{display:grid;grid-template-columns:auto 1fr;grid-template-rows:1fr auto auto;align-items:center;column-gap:1.6vw;padding:1.8vh 2.6vw 1.4vh;border:2px solid rgba(126,255,255,.62);border-radius:1.4vw;background:linear-gradient(135deg,#37ad6b,#277c70);box-shadow:inset 0 0 30px rgba(255,255,255,.08)}.score-card.performance{background:linear-gradient(135deg,#20b3aa,#146d8e)}.score-card.required{background:linear-gradient(135deg,#f2aa21,#b95c16)}.score-card.deduction{background:linear-gradient(135deg,#d53989,#6e2793)}.card-icon{display:grid;place-items
|
2026-09-06 22:36:26 +08:00
|
|
|
|
@media(max-aspect-ratio:16/10){.screen-content{padding-left:2vw;padding-right:2vw}.meta-pill{width:88%}.score-cards{gap:1vw}.score-card{padding-left:1.7vw;padding-right:1.7vw}}
|
|
|
|
|
|
</style>
|
2026-09-10 16:52:56 +08:00
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.score-screen {
|
|
|
|
|
|
--cyan: #54e9ff;
|
|
|
|
|
|
--line: rgba(126, 218, 255, 0.48);
|
|
|
|
|
|
--panel: rgba(5, 35, 112, 0.7);
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
background:
|
|
|
|
|
|
radial-gradient(circle at 50% -18%, rgba(44, 153, 255, 0.5), transparent 43%),
|
|
|
|
|
|
linear-gradient(145deg, #063fb2 0%, #082b86 52%, #06195b 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-screen.ranking {
|
|
|
|
|
|
background:
|
|
|
|
|
|
radial-gradient(circle at 50% -10%, rgba(33, 130, 255, 0.38), transparent 42%),
|
|
|
|
|
|
linear-gradient(145deg, #03123f 0%, #061f68 56%, #063595 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.screen-lines {
|
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
|
background-image:
|
|
|
|
|
|
linear-gradient(90deg, transparent 0 8%, rgba(72, 210, 255, 0.18) 50%, transparent 92%),
|
|
|
|
|
|
radial-gradient(ellipse at 50% 110%, transparent 0 45%, rgba(77, 211, 255, 0.3) 45.2% 45.35%, transparent 45.55%);
|
|
|
|
|
|
background-size: 100% 1px, 100% 100%;
|
|
|
|
|
|
background-position: center 17%, center;
|
|
|
|
|
|
background-repeat: no-repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.screen-grid {
|
|
|
|
|
|
opacity: 0.07;
|
|
|
|
|
|
background-size: 64px 64px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.skyline {
|
|
|
|
|
|
height: 48%;
|
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
|
filter: saturate(0.85) brightness(1.15);
|
|
|
|
|
|
background-size: max(1200px, 105vw) auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.screen-content {
|
|
|
|
|
|
padding: clamp(18px, 2.8vh, 52px) clamp(16px, 3.15vw, 88px) clamp(20px, 3vh, 56px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.screen-content > h1 {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
gap: clamp(14px, 2.1vw, 58px);
|
|
|
|
|
|
max-width: 92%;
|
|
|
|
|
|
align-self: center;
|
|
|
|
|
|
margin: 0 0 clamp(14px, 2.2vh, 40px);
|
|
|
|
|
|
font-size: clamp(28px, 3vw, 84px);
|
|
|
|
|
|
line-height: 1.15;
|
|
|
|
|
|
letter-spacing: 0.02em;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
text-wrap: balance;
|
|
|
|
|
|
text-shadow: 0 4px 18px rgba(0, 4, 41, 0.55), 0 0 24px rgba(73, 210, 255, 0.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.screen-content > h1 i {
|
|
|
|
|
|
width: clamp(34px, 6vw, 168px);
|
|
|
|
|
|
height: clamp(5px, 0.7vh, 13px);
|
|
|
|
|
|
flex: none;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
clip-path: polygon(0 38%, 100% 0, 86% 100%, 18% 72%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.meta-pill {
|
|
|
|
|
|
width: min(78%, 2180px);
|
|
|
|
|
|
min-height: clamp(32px, 4.2vh, 76px);
|
|
|
|
|
|
padding: 0 clamp(12px, 1.1vw, 30px);
|
|
|
|
|
|
margin-bottom: clamp(16px, 2.5vh, 46px);
|
|
|
|
|
|
gap: clamp(7px, 0.7vw, 20px);
|
|
|
|
|
|
border: 1px solid rgba(93, 194, 255, 0.24);
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: linear-gradient(90deg, rgba(2, 26, 91, 0.08), rgba(19, 93, 181, 0.3), rgba(2, 26, 91, 0.08));
|
|
|
|
|
|
box-shadow: inset 0 1px rgba(255, 255, 255, 0.04);
|
|
|
|
|
|
font-size: clamp(12px, 1.25vw, 34px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.meta-pill b {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.meta-spacer {
|
|
|
|
|
|
min-width: clamp(18px, 4vw, 112px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.people-icon,
|
|
|
|
|
|
.pin-icon {
|
|
|
|
|
|
margin-right: 0;
|
|
|
|
|
|
font-size: 1.25em;
|
|
|
|
|
|
text-shadow: 0 0 12px rgba(84, 233, 255, 0.65);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.single-board,
|
|
|
|
|
|
.rank-board {
|
|
|
|
|
|
border: 1px solid var(--line);
|
|
|
|
|
|
border-radius: clamp(14px, 1.4vw, 40px);
|
|
|
|
|
|
background:
|
|
|
|
|
|
radial-gradient(circle at 50% 42%, rgba(31, 103, 230, 0.56), transparent 48%),
|
|
|
|
|
|
linear-gradient(135deg, rgba(12, 76, 190, 0.64), rgba(4, 27, 101, 0.78));
|
|
|
|
|
|
box-shadow:
|
|
|
|
|
|
inset 0 1px rgba(255, 255, 255, 0.08),
|
|
|
|
|
|
inset 0 -50px 100px rgba(0, 14, 73, 0.22),
|
|
|
|
|
|
0 20px 60px rgba(0, 4, 45, 0.28);
|
|
|
|
|
|
backdrop-filter: blur(5px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.single-board::before {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
|
background: linear-gradient(115deg, rgba(255, 255, 255, 0.07), transparent 22% 76%, rgba(40, 187, 255, 0.05));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.board-label {
|
|
|
|
|
|
top: clamp(12px, 1.8vh, 34px);
|
|
|
|
|
|
min-width: auto;
|
|
|
|
|
|
padding: clamp(7px, 0.75vh, 14px) clamp(20px, 2.2vw, 60px);
|
|
|
|
|
|
border: 1px solid rgba(80, 208, 255, 0.38);
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
clip-path: none;
|
|
|
|
|
|
background: rgba(4, 43, 131, 0.72);
|
|
|
|
|
|
box-shadow: 0 8px 24px rgba(0, 11, 68, 0.25), inset 0 1px rgba(255, 255, 255, 0.08);
|
|
|
|
|
|
font-size: clamp(15px, 1.55vw, 42px);
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.board-label i {
|
|
|
|
|
|
width: clamp(14px, 1.45vw, 40px);
|
|
|
|
|
|
height: 4px;
|
|
|
|
|
|
margin: 0 clamp(7px, 0.7vw, 18px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.player-name {
|
|
|
|
|
|
top: 23%;
|
|
|
|
|
|
font-size: clamp(26px, 3.05vw, 82px);
|
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
|
text-shadow: 0 5px 18px rgba(0, 5, 46, 0.48);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-value {
|
|
|
|
|
|
top: 38%;
|
|
|
|
|
|
font-size: clamp(96px, 12vw, 320px);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
letter-spacing: -0.045em;
|
|
|
|
|
|
text-shadow: 0 14px 36px rgba(0, 5, 48, 0.5), 0 0 28px rgba(84, 233, 255, 0.16);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-glow {
|
|
|
|
|
|
left: 26%;
|
|
|
|
|
|
right: 26%;
|
|
|
|
|
|
bottom: 13%;
|
|
|
|
|
|
height: 2px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: rgba(208, 251, 255, 0.92);
|
|
|
|
|
|
box-shadow: 0 0 9px 3px rgba(71, 222, 255, 0.9), 0 0 36px 13px rgba(25, 127, 255, 0.66);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-cards {
|
|
|
|
|
|
gap: clamp(8px, 1.55vw, 44px);
|
|
|
|
|
|
height: clamp(126px, 19.2vh, 354px);
|
|
|
|
|
|
margin-top: clamp(12px, 2.55vh, 48px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-card {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
grid-template-columns: auto minmax(0, 1fr);
|
|
|
|
|
|
column-gap: clamp(8px, 1.25vw, 34px);
|
|
|
|
|
|
padding: clamp(12px, 1.55vh, 28px) clamp(11px, 1.9vw, 52px) clamp(10px, 1.25vh, 23px);
|
|
|
|
|
|
border-color: rgba(255, 255, 255, 0.32);
|
|
|
|
|
|
border-radius: clamp(12px, 1.3vw, 36px);
|
|
|
|
|
|
background: linear-gradient(145deg, rgba(72, 184, 117, 0.96), rgba(24, 102, 103, 0.94));
|
|
|
|
|
|
box-shadow: inset 0 1px rgba(255, 255, 255, 0.22), 0 14px 32px rgba(0, 8, 54, 0.22);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-card::before {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
background: radial-gradient(circle at 18% 5%, rgba(255, 255, 255, 0.22), transparent 42%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-card.performance { background: linear-gradient(145deg, rgba(27, 174, 168, 0.96), rgba(14, 96, 133, 0.95)); }
|
|
|
|
|
|
.score-card.required { background: linear-gradient(145deg, rgba(227, 153, 42, 0.97), rgba(163, 83, 26, 0.95)); }
|
|
|
|
|
|
.score-card.deduction { background: linear-gradient(145deg, rgba(204, 57, 126, 0.96), rgba(82, 33, 137, 0.95)); }
|
|
|
|
|
|
|
|
|
|
|
|
.card-icon {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
width: clamp(40px, 4.5vw, 126px);
|
|
|
|
|
|
height: clamp(40px, 4.5vw, 126px);
|
|
|
|
|
|
border: clamp(3px, 0.32vw, 8px) solid rgba(255, 255, 255, 0.22);
|
|
|
|
|
|
background: rgba(5, 47, 104, 0.12);
|
|
|
|
|
|
box-shadow: 0 0 18px rgba(120, 255, 228, 0.28), inset 0 0 0 1px rgba(255, 255, 255, 0.58);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-icon svg {
|
|
|
|
|
|
width: 54%;
|
|
|
|
|
|
height: 54%;
|
|
|
|
|
|
fill: none;
|
|
|
|
|
|
stroke: #fff;
|
|
|
|
|
|
stroke-width: 2.3;
|
|
|
|
|
|
stroke-linecap: round;
|
|
|
|
|
|
stroke-linejoin: round;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-card b {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
font-size: clamp(34px, 4.8vw, 130px);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
letter-spacing: -0.04em;
|
|
|
|
|
|
text-shadow: 0 6px 16px rgba(0, 10, 53, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-card i {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
margin-top: clamp(3px, 0.6vh, 11px);
|
|
|
|
|
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.46), transparent);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score-card strong {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
padding-top: clamp(6px, 0.8vh, 15px);
|
|
|
|
|
|
font-size: clamp(15px, 1.65vw, 44px);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
letter-spacing: 0.22em;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-board {
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
padding: clamp(24px, 3.45vh, 64px) clamp(18px, 2.85vw, 80px) clamp(24px, 4vh, 74px);
|
|
|
|
|
|
gap: clamp(10px, 2.15vh, 40px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-row {
|
|
|
|
|
|
height: clamp(64px, 9.1vh, 168px);
|
|
|
|
|
|
gap: clamp(10px, 1.3vw, 36px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.medal,
|
|
|
|
|
|
.rank-no {
|
|
|
|
|
|
width: clamp(48px, 4.15vw, 116px);
|
|
|
|
|
|
height: clamp(48px, 4.15vw, 116px);
|
|
|
|
|
|
min-width: clamp(48px, 4.15vw, 116px);
|
|
|
|
|
|
min-height: clamp(48px, 4.15vw, 116px);
|
|
|
|
|
|
border-color: rgba(84, 221, 255, 0.82);
|
|
|
|
|
|
background: rgba(9, 47, 135, 0.72);
|
|
|
|
|
|
box-shadow: inset 0 0 18px rgba(43, 161, 255, 0.24), 0 8px 22px rgba(0, 7, 47, 0.28);
|
|
|
|
|
|
font-size: clamp(22px, 1.85vw, 50px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.medal {
|
|
|
|
|
|
color: #fff9dc;
|
|
|
|
|
|
border: 2px solid rgba(255, 224, 126, 0.78);
|
|
|
|
|
|
background: linear-gradient(145deg, #ffdc67, #9d5908);
|
|
|
|
|
|
box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.45), 0 8px 24px rgba(0, 7, 47, 0.34);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.medal::before { display: none; }
|
|
|
|
|
|
.medal-2 { filter: saturate(0.15) brightness(1.25); }
|
|
|
|
|
|
.medal-3 { filter: sepia(0.45) saturate(0.9) brightness(0.93); }
|
|
|
|
|
|
|
|
|
|
|
|
.rank-main {
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-main strong {
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
max-width: 76%;
|
|
|
|
|
|
height: 45%;
|
|
|
|
|
|
min-width: clamp(116px, 12vw, 336px);
|
|
|
|
|
|
padding: 0 clamp(12px, 1.05vw, 30px);
|
|
|
|
|
|
border: 1px solid rgba(75, 183, 255, 0.22);
|
|
|
|
|
|
border-bottom: 0;
|
|
|
|
|
|
border-radius: clamp(9px, 0.85vw, 24px) clamp(9px, 0.85vw, 24px) 0 0;
|
|
|
|
|
|
background: linear-gradient(90deg, rgba(8, 48, 150, 0.95), rgba(5, 51, 151, 0.58));
|
|
|
|
|
|
font-size: clamp(14px, 1.1vw, 30px);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bar {
|
|
|
|
|
|
height: 52%;
|
|
|
|
|
|
border: 1px solid rgba(76, 172, 255, 0.5);
|
|
|
|
|
|
border-radius: clamp(7px, 0.65vw, 18px);
|
|
|
|
|
|
background: rgba(2, 20, 78, 0.72);
|
|
|
|
|
|
box-shadow: inset 0 6px 16px rgba(0, 8, 54, 0.32);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bar i {
|
|
|
|
|
|
background: linear-gradient(90deg, #0875ff 0%, #12bcef 62%, #63f3ec 100%);
|
|
|
|
|
|
box-shadow: inset 0 1px rgba(255, 255, 255, 0.35), 0 0 18px rgba(25, 187, 255, 0.24);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-score {
|
|
|
|
|
|
width: clamp(72px, 6vw, 168px);
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
align-self: center;
|
|
|
|
|
|
font-size: clamp(30px, 2.65vw, 72px);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
letter-spacing: -0.04em;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-score small {
|
|
|
|
|
|
color: rgba(214, 243, 255, 0.78);
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
letter-spacing: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.screen-controls {
|
|
|
|
|
|
right: clamp(10px, 1vw, 24px);
|
|
|
|
|
|
top: clamp(10px, 1vw, 24px);
|
|
|
|
|
|
padding: 6px;
|
|
|
|
|
|
border-color: rgba(113, 202, 255, 0.22);
|
|
|
|
|
|
border-radius: 11px;
|
|
|
|
|
|
background: rgba(1, 18, 68, 0.7);
|
|
|
|
|
|
box-shadow: 0 8px 28px rgba(0, 4, 40, 0.25);
|
|
|
|
|
|
backdrop-filter: blur(12px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.screen-controls button {
|
|
|
|
|
|
border-color: rgba(255, 255, 255, 0.16);
|
|
|
|
|
|
border-radius: 7px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.waiting-ring {
|
|
|
|
|
|
box-shadow: 0 0 36px rgba(37, 211, 241, 0.18);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.waiting h1 {
|
|
|
|
|
|
margin: 28px 0 8px;
|
|
|
|
|
|
font-size: clamp(30px, 3vw, 58px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.waiting p {
|
|
|
|
|
|
font-size: clamp(15px, 1.2vw, 24px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 900px) {
|
|
|
|
|
|
.screen-content { padding-left: 16px; padding-right: 16px; }
|
|
|
|
|
|
.screen-content > h1 { max-width: 100%; }
|
|
|
|
|
|
.meta-pill { width: 94%; }
|
|
|
|
|
|
.score-card { padding-left: 10px; padding-right: 10px; }
|
|
|
|
|
|
.card-icon { width: 44px; height: 44px; }
|
|
|
|
|
|
.rank-board { padding-left: 18px; padding-right: 18px; }
|
|
|
|
|
|
.rank-score { width: 68px; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-height: 720px) {
|
|
|
|
|
|
.screen-content { padding-top: 14px; padding-bottom: 16px; }
|
|
|
|
|
|
.screen-content > h1 { margin-bottom: 10px; }
|
|
|
|
|
|
.meta-pill { margin-bottom: 12px; }
|
|
|
|
|
|
.score-cards { min-height: 112px; margin-top: 10px; }
|
|
|
|
|
|
.board-label { top: 8px; }
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|