feat: M3-M7 全模块落地 + 集成接线
- M3 认证/权限/审计/限流 + 全部 REST 接口 - M4 管理面板(MIUIx + 成绩录入/积分榜下钻/大屏控制) - M5a 大屏页面(六页 + 明细覆盖层 + 缩放适配) - M6 WebSocket 实时同步中心 - M7 xlsx 导出 + 赛事包 zip + 备份 - 规则包 YAML 导入导出(差异预览) - 字体 vendor 子集化(4 套 x2 字重) - 集成:realtime/export/rules 接入 main+httpapi;backup 子命令 - 修复:中间件包装 ResponseWriter 丢失 Hijacker,导致 WS 握手 501
This commit is contained in:
1 parent
d0e3b93e76
commit
bc8b1ee6c0
145 files changed
+25159
-321
No files matched your search
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onUnmounted, ref } from 'vue'
|
||||
import { formatClock } from '@/shared'
|
||||
import { useScoreboard } from '../useScoreboard'
|
||||
import OfflineBadge from './OfflineBadge.vue'
|
||||
|
||||
const { settings, options } = useScoreboard()
|
||||
|
||||
const now = ref(new Date().toISOString())
|
||||
const timer = window.setInterval(() => {
|
||||
now.value = new Date().toISOString()
|
||||
}, 15_000)
|
||||
onUnmounted(() => window.clearInterval(timer))
|
||||
|
||||
const display = computed(() => settings.value?.display ?? null)
|
||||
const title = computed(() => display.value?.title.trim() || '赛事标题待配置')
|
||||
const subtitle = computed(() => display.value?.subtitle?.trim() ?? '')
|
||||
const host = computed(() => settings.value?.event.host?.trim() || '主办方待配置')
|
||||
const logo = computed(() => display.value?.logo?.trim() || '')
|
||||
const clock = computed(() => formatClock(now.value))
|
||||
const identity = computed(() => {
|
||||
const parts: string[] = []
|
||||
if (options.value.instance !== null) parts.push(`实例 ${options.value.instance}`)
|
||||
if (options.value.screen !== null) parts.push(`屏 ${options.value.screen}`)
|
||||
return parts.join(' · ')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="top">
|
||||
<div class="logo">
|
||||
<img v-if="logo" :src="logo" alt="" />
|
||||
<span v-else>会标</span>
|
||||
</div>
|
||||
|
||||
<div class="titles">
|
||||
<h1>{{ title }}</h1>
|
||||
<p v-if="subtitle" class="subtitle">{{ subtitle }}</p>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<div class="clock">{{ clock }}</div>
|
||||
<div class="host">{{ host }}</div>
|
||||
<div v-if="identity" class="identity">{{ identity }}</div>
|
||||
<OfflineBadge />
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
min-height: 132px;
|
||||
flex: none;
|
||||
}
|
||||
.logo {
|
||||
width: 108px;
|
||||
height: 108px;
|
||||
flex: none;
|
||||
border-radius: 50%;
|
||||
border: 3px solid var(--primary);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 22px;
|
||||
letter-spacing: 2px;
|
||||
color: var(--text-dim);
|
||||
overflow: hidden;
|
||||
}
|
||||
.logo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.titles {
|
||||
min-width: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 62px;
|
||||
line-height: 1.1;
|
||||
letter-spacing: 8px;
|
||||
white-space: nowrap;
|
||||
background: linear-gradient(180deg, #ffffff 25%, var(--primary-light) 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
.subtitle {
|
||||
margin-top: 8px;
|
||||
font-size: 26px;
|
||||
letter-spacing: 6px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.right {
|
||||
margin-left: auto;
|
||||
text-align: right;
|
||||
flex: none;
|
||||
}
|
||||
.clock {
|
||||
font-size: 44px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 3px;
|
||||
color: var(--text);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.host {
|
||||
margin-top: 6px;
|
||||
font-size: 24px;
|
||||
letter-spacing: 3px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.identity {
|
||||
margin-top: 4px;
|
||||
font-size: 18px;
|
||||
letter-spacing: 1px;
|
||||
color: rgba(157, 180, 212, 0.6);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user