feat(admin): 更新用户界面组件并优化样式

- 移除 uno.config.ts 中的注释以简化字体配置
- 更新 .env.dev 文件中的服务基础 URL
- 新增用户排名标题背景图片并优化相关组件样式
- 调整 ActionButton 组件的最小宽度
- 修改 CompetitionLayout 组件中的图像和样式以提升视觉效果
- 更新全局 CSS 文件以增加基础字体大小
- 优化 results-detail 视图中的发布逻辑延迟
- 调整用户分析和封面页面的文本样式
- 更新团队页面的团队名称样式以适应设计规范
This commit is contained in:
2026-03-26 15:18:45 +08:00
parent 3d3f7afcd2
commit c617307c79
14 changed files with 89 additions and 76 deletions

View File

@ -473,7 +473,7 @@ async function handleNexCurrentQuestion() {
function handlePublish() {
const endTimeStr = (currentQuestion.value as any)?.EndTime
if (endTimeStr && Date.now() < new Date(endTimeStr).getTime() + 5000) {
if (endTimeStr && Date.now() < new Date(endTimeStr).getTime() + 1000) {
window.$message?.warning('倒计时未结束需等待5秒延迟暂不能发布')
return
}
@ -589,7 +589,7 @@ const canPublish = computed(() => {
if (!endTime)
return true
// 结束时间 + 5秒延迟才能发布
const end = new Date(endTime).getTime() + 5000
const end = new Date(endTime).getTime() + 2000
return nowTs.value >= end
})
@ -599,8 +599,8 @@ function startPolling() {
// 检查是否已经结束
const endTime = (currentQuestion.value as any)?.EndTime
if (endTime) {
// 增加 12s 缓冲时间
const end = new Date(endTime).getTime() + 12000
// 增加 5s 缓冲时间
const end = new Date(endTime).getTime() + 3000
const now = new Date().getTime()
if (now > end) {
console.log('当前题目已结束含12s缓冲仅获取一次最终结果不启动轮询', endTime)
@ -622,10 +622,10 @@ function startPolling() {
// 每次轮询前再次检查是否过期
const currentEndTime = (currentQuestion.value as any)?.EndTime
if (currentEndTime) {
// 增加 12s 缓冲时间
const endTs = new Date(currentEndTime).getTime() + 12000
// 增加 5s 缓冲时间
const endTs = new Date(currentEndTime).getTime() + 3000
if (Date.now() > endTs) {
console.log('题目时间已到(含12s缓冲停止轮询', currentEndTime)
console.log('题目时间已到(含3s缓冲停止轮询', currentEndTime)
stopPolling()
return
}

View File

@ -161,7 +161,7 @@ onBeforeUnmount(() => {
<template>
<CompetitionLayout
: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"
back-btn-text="返回" :next-btn-text="isCompleted ? '已完成选组' : '下一题'" btn-theme="light" @back="handleBack"
@next="handleNext"
>
<div class="relative h-full w-full flex flex-col items-center px-12 pt-10 font-sans">

View File

@ -240,7 +240,7 @@ onMounted(async () => {
<!-- 文字内容 -->
<div class="relative z-10 transform text-center">
<div
class="card-text text-2xl text-[#333333] font-bold leading-tight font-serif drop-shadow-sm"
class="card-text text-26px text-[#333333] font-bold leading-tight font-serif drop-shadow-sm"
>
<span class="block">{{ node.moduleName.slice(0, 2) }}</span>
<span class="block">{{ node.moduleName.slice(2) }}</span>

View File

@ -285,7 +285,7 @@ const titleBgTypeNum = computed(() => RoundType.value === 0 ? 3 : 4) || 3
>
<!-- 图表标题栏 -->
<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">
<img src="@/assets/imgs/user/rank-bg-kuang.png" alt="答题进度" class="z-index-999 absolute left--30px top--25px h-auto max-h-16 w-auto">
</div>
<!-- 柱状图ECharts -->

View File

@ -126,7 +126,7 @@ watch(
{{ 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]">
<div class="rich-content content-with-bg pinyin-text ml-3 mt-4 flex text-7xl !max-w-100% !px-16 !py-4 !tracking-[0.08em]">
{{ content }}
</div>
</div>
@ -135,8 +135,8 @@ 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-6xl tracking-[0.06em]">
请写出含有 <span class="text-color-red !text-6xl">{{ content }}</span>的汉字书写时间为60秒
<div class="rich-content ml-3 mt-4 flex gap-1 text-5xl tracking-[0.06em]">
请写出含有 <span class="text-color-red !text-5xl">{{ content }}</span>的汉字书写时间为60秒
</div>
</div>
</div>
@ -153,7 +153,7 @@ watch(
<!-- 模板E: 成语-文字要求 -->
<div v-else-if="template === QuestionCategoryEnum.IdiomWriting2" class="text-center">
<div class="mb-2">
<img :src="ImageUrl" preview-disabled class="max-h-380px max-w-700px" object-fit="cover">
<img :src="ImageUrl" preview-disabled class="max-h-368px max-w-700px" object-fit="cover">
</div>
</div>
@ -173,8 +173,8 @@ watch(
<!-- 模板: 场景猜诗句 -->
<div v-else-if="template === QuestionCategoryEnum.SceneBasedPoemGuessing" class="w-full flex flex-col items-center">
<div class="mb-1 p-16 text-3xl text-gray-800 font-bold leading-relaxed line-height-20">
<div class="rich-content" v-html="content" />
<div class="mb-1px p-16 text-3xl text-gray-800 font-bold leading-relaxed line-height-16">
<div class="rich-content indent-[2em]" v-html="content" />
</div>
</div>

View File

@ -3,7 +3,7 @@ import type { DataTableColumns } from 'naive-ui'
import { NDataTable, NImage, NTag, useMessage } from 'naive-ui'
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import title from '@/assets/imgs/user/rank-title.svg'
import title from '@/assets/imgs/user/rank-title.png'
import ActionButton from '@/components/custom/user/ActionButton.vue'
import { fetchUserRankList } from '@/service/api/rank'
import ExtraTimeModal from './components/ExtraTimeModal.vue'
@ -57,6 +57,23 @@ const columns = ref<DataTableColumns<TeamData>>([
const tableData = ref<TeamData[]>([])
// eslint-disable-next-line unused-imports/no-unused-vars
const mockUserRankList: Api.Rank.ActivityRankResList = Array.from({ length: 13 }, (_, teamIndex) => {
// eslint-disable-next-line ts/no-shadow
const questions = Array.from({ length: 10 }, (_, questionIndex) => {
const QuestionNO = questionIndex + 1
const ResultPotin = Math.max(0, 10 - teamIndex - (questionIndex % 3))
return { QuestionNO, ResultPotin }
})
return {
TeamID: 1001 + teamIndex,
Name: `测试队伍${teamIndex + 1}`,
TotalPoint: questions.reduce((sum, item) => sum + item.ResultPotin, 0),
Questions: questions,
}
})
async function getUserRankList() {
const { data, error } = await fetchUserRankList(MainID, RoundType)
if (error) {
@ -64,9 +81,11 @@ async function getUserRankList() {
return
}
if (data?.data && data.data.length > 0) {
const rankSource = data?.data || []
if (rankSource.length > 0) {
// 动态生成题目列
const firstTeam = data.data[0]
const firstTeam = rankSource[0]
const questionColumns = firstTeam.Questions.map(q => ({
title: `${q.QuestionNO}`,
key: `q${q.QuestionNO}`,
@ -94,12 +113,12 @@ async function getUserRankList() {
return <span>{rank}</span>
},
},
{ title: '队伍名称', key: 'teamName', align: 'left', width: 150, className: 'team-name' },
{ title: '队伍名称', key: 'teamName', align: 'left', width: 190, className: 'team-name' },
...questionColumns,
{ title: '总积分', key: 'total', align: 'center', className: 'total-score' },
]
tableData.value = data.data.map((item, index) => {
tableData.value = rankSource.map((item, index) => {
const questions: Record<string, number> = {}
item.Questions.forEach((q) => {
questions[`q${q.QuestionNO}`] = q.ResultPotin
@ -114,7 +133,7 @@ async function getUserRankList() {
} as TeamData
})
teamOptions.value = data.data.map(item => ({ label: item.Name, value: item.TeamID }))
teamOptions.value = rankSource.map(item => ({ label: item.Name, value: item.TeamID }))
}
}
@ -137,21 +156,21 @@ function rowClassName(row: TeamData) {
<template>
<CompetitionLayout :show-back="false" :show-next="false" :show-title="false">
<div class="leaderboard-container">
<div class="leaderboard-container h-100%">
<div class="leaderboard-header">
<NImage :src="title" alt="logo" preview-disabled class="mt--60px" object-fit="contain" />
<p class="mt--20px text-center text-lg text-#6b778d">
<NImage :src="title" alt="logo" preview-disabled class="mt--42px" object-fit="contain" />
<!-- <p class="mt--16px text-center text-18px text-#6b778d">
"勤学如春起之苗,不见其增,日有所长"
</p>
</p> -->
<ActionButton
v-if="hasExtraTimeBtn" class="position-absolute bottom-20px right-20px" type="text" text="加时赛"
@click="handleAddTime"
v-if="hasExtraTimeBtn" class="z-index-99 position-absolute right-20px top-20px" type="text"
text="加时赛" @click="handleAddTime"
/>
</div>
<div class="leaderboard-content">
<NDataTable
:columns="columns" :data="tableData" :bordered="false" :bottom-bordered="false" size="large"
:row-class-name="rowClassName" class="rank-table"
:columns="columns" :data="tableData" :bordered="false" max-height="100%" :bottom-bordered="false"
size="large" :row-class-name="rowClassName" class="rank-table !h-100%"
/>
</div>
</div>
@ -163,7 +182,7 @@ function rowClassName(row: TeamData) {
/* 1. 整体容器和背景 */
.leaderboard-container {
width: 100%;
min-height: 100vh;
height: 100%;
background-image: url('https://raw.githubusercontent.com/0x3f3f3f3f/figure-bed/main/img/20240524150352.png');
background-size: cover;
background-position: center;
@ -193,19 +212,12 @@ function rowClassName(row: TeamData) {
}
.leaderboard-header h1 {
font-size: 2.5rem;
font-size: 2.2rem;
font-weight: 700;
color: #1a2a44;
margin: 0;
}
/* .leaderboard-header p {
font-size: 1rem;
color: #6b778d;
margin-top: 8px;
} */
/* 3. 表格内容区 */
.leaderboard-content {
width: 100%;
/* max-width: 1200px; */
@ -213,6 +225,7 @@ function rowClassName(row: TeamData) {
border-radius: 12px;
box-shadow: 0 8px 24px rgba(26, 42, 68, 0.05);
overflow: hidden;
height: calc(100% - 220px);
}
/* 4. Naive UI 表格深度定制 */
@ -221,7 +234,7 @@ function rowClassName(row: TeamData) {
}
:deep(.rank-table .n-data-table-th) {
font-size: 14px;
font-size: 24px;
font-weight: 600;
color: #495057;
border-bottom: 2px solid #dee2e6 !important;
@ -236,11 +249,11 @@ function rowClassName(row: TeamData) {
}
:deep(.rank-table .n-data-table-td) {
font-size: 15px;
font-size: 24px;
color: #333;
border-bottom: 1px solid #e9ecef !important;
padding-top: 16px !important;
padding-bottom: 16px !important;
/* padding-top: 16px !important; */
/* padding-bottom: 16px !important; */
}
/* 奇偶行交替色 */
@ -256,7 +269,7 @@ function rowClassName(row: TeamData) {
:deep(.total-score) {
font-weight: 700;
font-size: 1.1rem;
font-size: 2.1rem;
color: #e67e22;
}
@ -284,4 +297,11 @@ function rowClassName(row: TeamData) {
:deep(.rank-top-3 .total-score) {
color: #cd7f32;
}
/* 强制表格容器占满父级 */
.leaderboard-content :deep(.n-data-table) {
height: 100% !important;
max-height: 100% !important;
overflow-y: auto;
}
</style>

View File

@ -116,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 text-3xl">
<div class="team-name text-2xl">
{{ item.name }}
</div>
</div>