feat(admin): 优化大屏开发体验并完善用户界面组件
- 新增大屏开发调试指南文档,包含设计基准、调试方法和字体配置说明 - 添加字体大小预览工具,支持多分辨率视口切换(2048x1080/1920x1080/3840x2160) - 新增字体资源 README 文档和团队标题背景图片 - 优化用户界面背景图片(title-bg3.png、title-bg4.png) - 更新 ActionButton 和 CompetitionLayout 组件样式 - 完善用户模块各页面布局(分析、封面、抽签、游戏、规则、团队) - 优化题目渲染器组件和成绩详情页面 - 更新 UnoCSS 配置以支持大屏字体缩放方案
This commit is contained in:
@ -127,7 +127,17 @@ async function fetchGetGameStatisticsData() {
|
||||
QuestionDetaiID: Number(QuestionDetailID.value),
|
||||
}
|
||||
const { data } = await fetchGetGameStatistics(_params)
|
||||
teams.value = (data?.data as any[]) || []
|
||||
const rawTeams = (data?.data as any[]) || []
|
||||
|
||||
// 为每个队伍的图片 URL 添加时间戳,防止缓存
|
||||
teams.value = rawTeams.map((team) => {
|
||||
if (team.UserAnswerPicture) {
|
||||
const timestamp = Date.now()
|
||||
const separator = team.UserAnswerPicture.includes('?') ? '&' : '?'
|
||||
team.UserAnswerPicture = `${team.UserAnswerPicture}${separator}_t=${timestamp}`
|
||||
}
|
||||
return team
|
||||
})
|
||||
}
|
||||
catch (error: any) {
|
||||
console.error('Failed to fetch game statistics', error)
|
||||
@ -150,7 +160,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
<template>
|
||||
<CompetitionLayout
|
||||
:show-title="true" :show-back="false" :show-next="showNextBtn" title="答题图解" action-position="top"
|
||||
:show-title="true" :show-back="false" :show-next="showNextBtn" :title-bg-type="3" title-text-color="#ffffff" title="答题图解" action-position="top"
|
||||
back-btn-text="返回" :next-btn-text="isCompleted ? '该组已完成,回到选组' : '下一题'" btn-theme="light" @back="handleBack"
|
||||
@next="handleNext"
|
||||
>
|
||||
@ -170,11 +180,12 @@ onBeforeUnmount(() => {
|
||||
class="mb-3 flex items-center gap-2 rounded-full from-sky-300 to-cyan-200 bg-gradient-to-r px-4 py-2 shadow-sm"
|
||||
>
|
||||
<span class="text-lg text-white font-bold drop-shadow">{{ team.TeamName }}</span>
|
||||
<span class="ml-2 rounded-full bg-white/40 px-2 py-0.5 text-sm">正确 <span class="text-md text-blue">{{
|
||||
team.correctCount }}</span> 字</span>
|
||||
<span class="text-s rounded-full bg-white/40 px-2 py-0.5">积分 <span class="text-md text-blue">{{
|
||||
team.Points
|
||||
}}</span></span>
|
||||
<span class="rounded-full bg-orange-100/60 px-5 py-2 text-sm">
|
||||
AI评分 <span class="text-md text-orange-600 font-bold">{{ team.Points }}</span>
|
||||
</span>
|
||||
<span class="rounded-full bg-green-100/60 px-5 py-2 text-sm">
|
||||
评委评分 <span class="text-md text-green-600 font-bold">{{ team.ResultPotins }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Answer Image Area -->
|
||||
|
||||
@ -154,9 +154,10 @@ const showCards = ref(false)
|
||||
// 默认按最小屏幕(2列)计算,如果超过2排则调整padding
|
||||
const needCompactPadding = computed(() => {
|
||||
// 假设最小屏幕每排2个卡片(w-[44%])
|
||||
const cardsPerRow = 2
|
||||
const totalRows = Math.ceil(questions.value.length / cardsPerRow)
|
||||
return totalRows > 2
|
||||
// const cardsPerRow = 2
|
||||
// const totalRows = Math.ceil(questions.value.length / cardsPerRow)
|
||||
// return totalRows >= 2
|
||||
return questions.value.length > 7 // 如果超过8张卡片就使用紧凑间距
|
||||
})
|
||||
|
||||
// 3D 悬停效果逻辑
|
||||
|
||||
@ -143,7 +143,7 @@ const titleBgTypeNum = computed(() => RoundType.value === 0 ? 3 : 4)
|
||||
@next="handleCardClick(question as Api.Competition.CurrentQuestionResponse)" @back="handleBack"
|
||||
>
|
||||
<div class="h-full w-full flex flex-col items-center pt-10 font-sans">
|
||||
<div v-if="question" class="mb-10 max-w-4xl text-center">
|
||||
<div v-if="question" class="mb-10 max-w-85% text-center">
|
||||
<div class="text-4xl text-gray-800 font-medium leading-relaxed tracking-wide">
|
||||
{{ questionTitle || '' }}
|
||||
</div>
|
||||
|
||||
@ -284,12 +284,8 @@ const titleBgTypeNum = computed(() => RoundType.value === 0 ? 3 : 4) || 3
|
||||
class="relative z-20 h-64 max-w-5xl w-full flex flex-col shrink-0 border-2 border-blue-300 rounded-xl bg-white/90 p-4 shadow-lg backdrop-blur"
|
||||
>
|
||||
<!-- 图表标题栏 -->
|
||||
<div v-if="isStarted" class="mb-4 flex items-center justify-between px-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="h-3 w-3 rotate-45 bg-orange-400" />
|
||||
<span class="text-lg text-blue-800 font-bold">答题进度</span>
|
||||
<span class="text-md text-gray-400">(仅为ai判定,不代表最终结果)</span>
|
||||
</div>
|
||||
<div v-if="isStarted" class="z-index-999 relative mb-4">
|
||||
<img src="@/assets/imgs/user/rank-bg-kuang.png" alt="答题进度" class="z-index-999 absolute left--26px top--22px h-auto max-h-16 w-auto">
|
||||
</div>
|
||||
|
||||
<!-- 柱状图(ECharts) -->
|
||||
|
||||
@ -24,6 +24,35 @@ const dictationParts = computed(() => {
|
||||
return []
|
||||
})
|
||||
|
||||
// 动态计算第二个框的样式(根据文字长度)
|
||||
const secondPartStyle = computed(() => {
|
||||
if (dictationParts.value.length < 2)
|
||||
return { fontSize: 'text-5xl', letterSpacing: 'tracking-widest' }
|
||||
|
||||
const secondPart = dictationParts.value[1]
|
||||
const length = secondPart.length
|
||||
|
||||
// 根据文字长度返回不同的样式配置
|
||||
if (length <= 16) {
|
||||
return {
|
||||
fontSize: 'text-5xl', // 51.2px - 保持大字体
|
||||
letterSpacing: 'tracking-widest', // 0.4em - 正常字间距
|
||||
}
|
||||
}
|
||||
else if (length <= 20) {
|
||||
return {
|
||||
fontSize: 'text-4xl', // 40px - 稍微缩小
|
||||
letterSpacing: 'tracking-wide', // 0.2em - 缩小字间距
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {
|
||||
fontSize: 'text-3xl', // 33.6px - 更小一点
|
||||
letterSpacing: 'tracking-normal', // 0.05em - 最小字间距
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 联想对对碰:上方词组
|
||||
const associationWords = computed(() => {
|
||||
const name = currentQuestionDetail.value?.Name || ''
|
||||
@ -71,13 +100,18 @@ watch(
|
||||
<!-- 模板A: 汉字听写1-提示 (拼音+提示) -->
|
||||
<div v-if="template === QuestionCategoryEnum.ChineseCharacterDictation1" class="text-center">
|
||||
<div class="mb-6 inline-block text-5xl font-bold leading-none">
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<div class="flex items-center justify-start gap-4">
|
||||
<!-- 第一个框:拼音 -->
|
||||
<div class="short-content-bg pinyin-text text-6xl">
|
||||
{{ dictationParts[0] }}
|
||||
</div>
|
||||
|
||||
<!-- 第二个框:意思提示 -->
|
||||
<div
|
||||
v-for="(part, index) in dictationParts" :key="`part-${index}`"
|
||||
class="text-4xl"
|
||||
:class="index === 0 ? 'short-content-bg' : 'content-with-bg'"
|
||||
v-if="dictationParts.length > 1" class="content-with-bg pinyin-text !max-w-85%"
|
||||
:class="[secondPartStyle.fontSize, secondPartStyle.letterSpacing]"
|
||||
>
|
||||
{{ part }}
|
||||
{{ dictationParts[1] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -88,8 +122,11 @@ watch(
|
||||
v-else-if="template === QuestionCategoryEnum.ChineseCharacterDictation2 || template === QuestionCategoryEnum.WordDictation"
|
||||
class="flex flex-col items-center"
|
||||
>
|
||||
<div class="mb-1 text-3xl text-gray-800 font-bold leading-relaxed">
|
||||
<div class="rich-content content-with-bg ml-3 mt-4 flex text-6xl !px-16 !py-10 !tracking-[0.1em]">
|
||||
<div class="mb-2 text-center text-2xl">
|
||||
{{ title }}
|
||||
</div>
|
||||
<div class="mb-1 text-gray-800 font-bold leading-relaxed">
|
||||
<div class="rich-content content-with-bg pinyin-text ml-3 mt-4 flex text-7xl !max-w-100% !px-16 !py-8 !tracking-[0.08em]">
|
||||
{{ content }}
|
||||
</div>
|
||||
</div>
|
||||
@ -98,7 +135,7 @@ watch(
|
||||
<!-- 模板C: 汉字加一加 (提示+部件) -->
|
||||
<div v-else-if="template === QuestionCategoryEnum.CharacterRadicalAddition" class="text-center">
|
||||
<div class="mb-6 inline-block text-5xl font-bold leading-none">
|
||||
<div class="rich-content ml-3 mt-4 flex gap-1 text-5xl tracking-[0.1em]">
|
||||
<div class="rich-content ml-3 mt-4 flex gap-1 text-6xl tracking-[0.06em]">
|
||||
请写出含有“ <span class="text-color-red !text-6xl">{{ content }}</span>”的汉字,书写时间为60秒。
|
||||
</div>
|
||||
</div>
|
||||
@ -107,7 +144,7 @@ watch(
|
||||
<!-- 模板D: 成语写一写1 (拼音+提示) -->
|
||||
<div v-else-if="template === QuestionCategoryEnum.IdiomWriting1" class="flex flex-col items-center">
|
||||
<div class="mb-1 text-gray-800 font-bold leading-relaxed">
|
||||
<div class="rich-content ml-3 mt-4 flex gap-1 text-4xl">
|
||||
<div class="rich-content ml-3 mt-4 flex gap-1 text-5xl">
|
||||
{{ content }}
|
||||
</div>
|
||||
</div>
|
||||
@ -115,8 +152,8 @@ watch(
|
||||
|
||||
<!-- 模板E: 成语-文字要求 -->
|
||||
<div v-else-if="template === QuestionCategoryEnum.IdiomWriting2" class="text-center">
|
||||
<div class="mb-8 text-4xl text-gray-800 font-bold">
|
||||
<NImage :src="ImageUrl" preview-disabled width="300" object-fit="contain" />
|
||||
<div class="mb-2">
|
||||
<img :src="ImageUrl" preview-disabled class="max-h-380px max-w-700px" object-fit="cover">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -173,7 +210,7 @@ watch(
|
||||
v-else-if="template === QuestionCategoryEnum.CharacterWordFlowerDrinkingGame"
|
||||
class="w-full flex flex-col items-center"
|
||||
>
|
||||
<div class="options-container text-5xl">
|
||||
<div class="options-container text-6xl">
|
||||
请回答带有“{{ content }}”的诗词。
|
||||
</div>
|
||||
</div>
|
||||
@ -408,7 +445,8 @@ watch(
|
||||
:deep(p),
|
||||
:deep(span),
|
||||
:deep(div) {
|
||||
font-size: 2rem; /* text-5xl */
|
||||
font-size: 2.2rem;
|
||||
/* text-5xl */
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,16 +454,41 @@ watch(
|
||||
background: url('@/assets/imgs/user/content-bg.png') no-repeat center;
|
||||
background-size: 100% 100%;
|
||||
border-radius: 12px;
|
||||
padding: 20px 30px;
|
||||
letter-spacing: 0.4em;
|
||||
padding: 30px 50px;
|
||||
/* 优化长文本显示 */
|
||||
max-width: 80%;
|
||||
/* 限制最大宽度 */
|
||||
min-height: 120px;
|
||||
/* 最小高度,与 short-content-bg 保持一致 */
|
||||
word-break: keep-all;
|
||||
/* 避免中文单词被截断 */
|
||||
white-space: normal;
|
||||
/* 允许换行 */
|
||||
line-height: 1.5;
|
||||
/* 行高,让换行更美观 */
|
||||
display: inline-flex;
|
||||
/* 使用 flex 布局 */
|
||||
align-items: center;
|
||||
/* 垂直居中 */
|
||||
justify-content: center;
|
||||
/* 水平居中 */
|
||||
text-align: center;
|
||||
/* 文字居中 */
|
||||
}
|
||||
|
||||
.short-content-bg {
|
||||
background: url('@/assets/imgs/user/short-content-bg.png') no-repeat center;
|
||||
background-size: 100% 100%;
|
||||
border-radius: 12px;
|
||||
padding: 20px 30px;
|
||||
letter-spacing: 0.1em;
|
||||
padding: 20px 80px;
|
||||
min-height: 120px;
|
||||
/* 最小高度,与 content-with-bg 保持一致 */
|
||||
display: inline-flex;
|
||||
/* 使用 flex 布局 */
|
||||
align-items: center;
|
||||
/* 垂直居中 */
|
||||
justify-content: center;
|
||||
/* 水平居中 */
|
||||
}
|
||||
|
||||
.association-word {
|
||||
@ -453,4 +516,35 @@ watch(
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 拼音字体样式 - 支持声调符号 */
|
||||
.pinyin-text {
|
||||
/* 字体回退顺序:
|
||||
1. GB Pinyinok-B - 专业拼音字体
|
||||
2. Microsoft YaHei - 微软雅黑(Windows 系统自带,支持拼音)
|
||||
3. PingFang SC - 苹方(macOS 系统自带,支持拼音)
|
||||
4. STHeiti - 华文黑体(支持拼音)
|
||||
5. sans-serif - 系统默认无衬线字体
|
||||
*/
|
||||
font-family: 'GB Pinyinok-B', 'Microsoft YaHei', 'PingFang SC', 'STHeiti', 'SimHei', sans-serif;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.08em;
|
||||
/* 确保拼音声调符号不被截断 */
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 如果需要加载自定义拼音字体 */
|
||||
@font-face {
|
||||
font-family: 'GB Pinyinok-B';
|
||||
/* 优先使用本地已安装的字体 */
|
||||
src: local('GB Pinyinok-B'), local('GBPinyinok-B'), local('GB Pinyinok B');
|
||||
/* 如果本地没有,则加载项目中的字体文件(需要先添加字体文件)*/
|
||||
/* src: url('@/assets/fonts/GBPinyinok-B.woff2') format('woff2'),
|
||||
url('@/assets/fonts/GBPinyinok-B.woff') format('woff'),
|
||||
url('@/assets/fonts/GBPinyinok-B.ttf') format('truetype'); */
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
/* 字体加载时先显示系统字体,加载完成后切换 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -49,7 +49,7 @@ function handleNext() {
|
||||
|
||||
<template>
|
||||
<CompetitionLayout
|
||||
:show-back="true" back-btn-text="返回" :show-next="true" :title="activityName" next-btn-text="下一步"
|
||||
:show-back="true" :title-bg-type="2" back-btn-text="返回" :show-next="true" :title="activityName" next-btn-text="下一步"
|
||||
@back="handleBack" @next="handleNext"
|
||||
>
|
||||
<div class="items-flex-start h-full w-full flex justify-center font-sans">
|
||||
|
||||
@ -87,7 +87,10 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CompetitionLayout :show-back="true" back-btn-text="返回" :show-next="false" next-btn-text="下一步" :show-title="false" @next="handleNext" @back="handleBack">
|
||||
<CompetitionLayout
|
||||
:show-back="true" back-btn-text="返回" :show-next="false" next-btn-text="下一步" :show-title="false"
|
||||
@next="handleNext" @back="handleBack"
|
||||
>
|
||||
<!-- Scroll Content -->
|
||||
<div class="scroll-container">
|
||||
<!-- 卷轴背景动画容器 -->
|
||||
@ -113,7 +116,7 @@ onMounted(() => {
|
||||
<div class="icon-wrapper">
|
||||
<img src="@/assets/imgs/user/team-title-icon.webp" alt="team-icon" class="h-full w-full object-cover">
|
||||
</div>
|
||||
<div class="team-name">
|
||||
<div class="team-name text-3xl">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
@ -181,6 +184,7 @@ onMounted(() => {
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-15px);
|
||||
}
|
||||
@ -189,7 +193,7 @@ onMounted(() => {
|
||||
.title-section {
|
||||
position: absolute;
|
||||
top: 130px; // 使用固定像素值,确保在卷轴内部
|
||||
left: 40px; // 距离左边缘的固定距离
|
||||
left: 47px; // 距离左边缘的固定距离
|
||||
z-index: 10;
|
||||
|
||||
.scroll-bg {
|
||||
@ -207,8 +211,8 @@ onMounted(() => {
|
||||
// 添加响应式适配
|
||||
@media (max-width: 1920px) {
|
||||
.title-section {
|
||||
top: 70px;
|
||||
left: 50px;
|
||||
top: 140px;
|
||||
left: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,14 +320,20 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.team-name {
|
||||
font-size: 1.5rem;
|
||||
// min-width: 130px;
|
||||
// width: 300px;
|
||||
// height: 120px;
|
||||
min-height: 60px;
|
||||
min-width: 250px;
|
||||
line-height: 60px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
|
||||
background: url('@/assets/imgs/user/team-title-bg.svg') no-repeat center center;
|
||||
background-size: contain;
|
||||
background: url('@/assets/imgs/user/team-title-bg.png') no-repeat center center;
|
||||
background-size: 100% 100%;
|
||||
padding: 15px 30px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user