修改样式

This commit is contained in:
zhangxiaohua 2026-01-16 14:48:14 +08:00
parent b002e3ca1c
commit 8411df8ad6
9 changed files with 464 additions and 229 deletions

View File

@ -17,15 +17,15 @@ description: "比赛管理系统设计规范。当用户提出页面开发需求
// ========================================== // ==========================================
// 项目统一设计变量 - 必须复制 // 项目统一设计变量 - 必须复制
// ========================================== // ==========================================
$primary: #0958d9; $primary: #1890ff;
$primary-light: #1677ff; $primary-dark: #0958d9;
$primary-dark: #003eb3; $primary-light: #40a9ff;
$secondary: #4096ff; $secondary: #4096ff;
$success: #52c41a; $success: #52c41a;
$warning: #faad14; $warning: #faad14;
$error: #ff4d4f; $error: #ff4d4f;
$background: #f5f5f5; $background: #f5f7fa;
$surface: #ffffff; $surface: #ffffff;
$text: rgba(0, 0, 0, 0.85); $text: rgba(0, 0, 0, 0.85);
@ -35,7 +35,7 @@ $text-muted: rgba(0, 0, 0, 0.45);
$border: #d9d9d9; $border: #d9d9d9;
$border-light: #e8e8e8; $border-light: #e8e8e8;
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%); $gradient-primary: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
``` ```
--- ---
@ -46,9 +46,9 @@ $gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%);
| 变量 | 色值 | 用途 | | 变量 | 色值 | 用途 |
|------|------|------| |------|------|------|
| `$primary` | `#0958d9` | 主色、按钮、链接 | | `$primary` | `#1890ff` | 主色、按钮、链接 |
| `$primary-light` | `#1677ff` | 悬停态 | | `$primary-dark` | `#0958d9` | 渐变深色、激活态 |
| `$primary-dark` | `#003eb3` | 激活态 | | `$primary-light` | `#40a9ff` | 悬停态、浅色 |
| `$secondary` | `#4096ff` | 辅助蓝 | | `$secondary` | `#4096ff` | 辅助蓝 |
### 功能色 ### 功能色
@ -156,20 +156,45 @@ $gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%);
</a-button> </a-button>
``` ```
### 主按钮 ### 主按钮(渐变样式)
```scss ```scss
.primary-btn { .primary-btn,
background: $gradient-primary !important; .gradient-btn {
background: linear-gradient(135deg, $primary 0%, $primary-dark 100%) !important;
border: none !important; border: none !important;
color: #fff !important; color: #fff !important;
font-weight: 500 !important; font-weight: 500 !important;
border-radius: 20px !important;
padding: 6px 16px !important;
transition: all 0.3s ease !important; transition: all 0.3s ease !important;
box-shadow: 0 2px 8px rgba($primary, 0.3);
&:hover { &:hover {
filter: brightness(1.1);
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 12px 24px rgba($primary, 0.2); box-shadow: 0 6px 20px rgba($primary, 0.4);
}
}
// 用于 hero 区域的大按钮
.action-btn.primary {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 28px;
border-radius: 24px;
font-size: 15px;
font-weight: 600;
background: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
color: #fff;
border: none;
cursor: pointer;
box-shadow: 0 4px 15px rgba($primary, 0.4);
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba($primary, 0.5);
} }
} }
``` ```

View File

@ -211,26 +211,25 @@ const handleLogout = async () => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
$primary: #1890ff;
$primary-dark: #0958d9;
$primary-light: #40a9ff;
.layout { .layout {
min-height: 100vh; min-height: 100vh;
} }
// - //
.custom-sider { .custom-sider {
background: var(--sidebar-bg, #f5f5f5) !important; // background: #fff !important;
border-right: none !important; // 线 border-right: none !important;
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.05);
:deep(.ant-layout-sider-children) { :deep(.ant-layout-sider-children) {
background: var(--sidebar-bg, #f5f5f5); background: #fff;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
padding-right: 10px;
}
// Ant Design Vue
:deep(.ant-layout-sider) {
border-right: none !important;
} }
} }
@ -244,10 +243,37 @@ const handleLogout = async () => {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
padding: 0 8px;
} }
// Logo -
.logo {
height: 64px;
display: flex;
align-items: center;
justify-content: center;
padding: 16px 12px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
margin-bottom: 8px;
img {
max-width: 100%;
max-height: 32px;
object-fit: contain;
}
.logo-text {
color: $primary;
font-size: 18px;
font-weight: 700;
margin: 0;
letter-spacing: 1px;
}
}
//
.sider-bottom { .sider-bottom {
padding: 12px; padding: 12px 16px;
border-top: 1px solid rgba(0, 0, 0, 0.06); border-top: 1px solid rgba(0, 0, 0, 0.06);
display: flex; display: flex;
align-items: center; align-items: center;
@ -258,7 +284,7 @@ const handleLogout = async () => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 8px; padding: 6px 10px;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
@ -266,12 +292,17 @@ const handleLogout = async () => {
min-width: 0; min-width: 0;
&:hover { &:hover {
background: var(--sidebar-menu-item-selected-bg, #e2f0ed); background: rgba($primary, 0.08);
}
:deep(.ant-avatar) {
border: 2px solid rgba($primary, 0.2);
} }
.username { .username {
font-size: 14px; font-size: 14px;
color: var(--sidebar-menu-text, rgba(0, 0, 0, 0.85)); font-weight: 500;
color: rgba(0, 0, 0, 0.85);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -280,7 +311,7 @@ const handleLogout = async () => {
.user-info-collapsed { .user-info-collapsed {
justify-content: center; justify-content: center;
padding: 8px; padding: 6px;
flex: unset; flex: unset;
} }
@ -290,15 +321,15 @@ const handleLogout = async () => {
justify-content: center; justify-content: center;
width: 32px; width: 32px;
height: 32px; height: 32px;
border-radius: 6px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
color: var(--sidebar-menu-text, rgba(0, 0, 0, 0.65)); color: rgba(0, 0, 0, 0.45);
flex-shrink: 0; flex-shrink: 0;
&:hover { &:hover {
background: var(--sidebar-menu-item-selected-bg, #e2f0ed); background: rgba($primary, 0.08);
color: var(--sidebar-menu-text-selected, #01412b); color: $primary;
} }
} }
} }
@ -306,7 +337,7 @@ const handleLogout = async () => {
.sider-bottom-collapsed { .sider-bottom-collapsed {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 12px; gap: 10px;
padding: 12px 8px; padding: 12px 8px;
.collapse-trigger { .collapse-trigger {
@ -314,59 +345,51 @@ const handleLogout = async () => {
} }
} }
.logo { // Ant Design
height: 64px;
display: flex;
align-items: center;
justify-content: center;
background: transparent; //
color: var(--sidebar-menu-text-selected, #01412b); // Logo 使
margin-bottom: 8px;
border-radius: 0; //
padding: 20px 12px;
img {
max-width: 100%;
max-height: 30px;
object-fit: contain;
}
.logo-text {
color: var(--sidebar-menu-text-selected, #01412b); // Logo 使
font-size: 18px;
font-weight: bold;
margin: 0;
}
}
// Ant Design 使
:deep(.ant-menu-light.ant-menu-root.ant-menu-inline), :deep(.ant-menu-light.ant-menu-root.ant-menu-inline),
:deep(.ant-menu-light.ant-menu-root.ant-menu-vertical) { :deep(.ant-menu-light.ant-menu-root.ant-menu-vertical) {
border-inline-end: none !important; border-inline-end: none !important;
} }
// // -
.custom-menu { .custom-menu {
background: transparent !important; background: transparent !important;
border-right: none !important; border-right: none !important;
border-inline-end: none !important; border-inline-end: none !important;
padding: 4px 0;
:deep(.ant-menu-item) { :deep(.ant-menu-item) {
color: var(--sidebar-menu-text, rgba(0, 0, 0, 0.85)); color: rgba(0, 0, 0, 0.75);
margin: 4px 8px; margin: 4px 0;
border-radius: 6px; border-radius: 10px;
transition: all 0.3s; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
background: transparent !important; background: transparent !important;
height: 44px;
line-height: 44px;
.anticon {
font-size: 16px;
transition: all 0.3s;
}
&:hover { &:hover {
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important; color: $primary !important;
color: var(--sidebar-menu-text-selected, #01412b) !important; background: rgba($primary, 0.08) !important;
.anticon {
color: $primary;
}
} }
&.ant-menu-item-selected { &.ant-menu-item-selected {
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important; color: #fff !important;
color: var(--sidebar-menu-text-selected, #01412b) !important; background: linear-gradient(135deg, $primary 0%, $primary-dark 100%) !important;
font-weight: 500; font-weight: 500;
box-shadow: 0 2px 8px rgba($primary, 0.3);
.anticon {
color: #fff;
}
&::after { &::after {
display: none; display: none;
@ -376,34 +399,41 @@ const handleLogout = async () => {
:deep(.ant-menu-submenu) { :deep(.ant-menu-submenu) {
.ant-menu-submenu-title { .ant-menu-submenu-title {
color: var(--sidebar-menu-text, rgba(0, 0, 0, 0.85)); color: rgba(0, 0, 0, 0.75);
margin: 4px 8px; margin: 4px 0;
border-radius: 6px; border-radius: 10px;
transition: all 0.3s; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
background: transparent !important; background: transparent !important;
height: 44px;
line-height: 44px;
.anticon {
font-size: 16px;
transition: all 0.3s;
}
&:hover { &:hover {
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important; color: $primary !important;
color: var(--sidebar-menu-text-selected, #01412b) !important; background: rgba($primary, 0.08) !important;
.anticon {
color: $primary;
}
} }
} }
&.ant-menu-submenu-open > .ant-menu-submenu-title { &.ant-menu-submenu-open > .ant-menu-submenu-title {
color: var(--sidebar-menu-text-selected, #01412b); color: $primary;
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important; background: rgba($primary, 0.08) !important;
.anticon {
color: $primary;
}
} }
&.ant-menu-submenu-selected > .ant-menu-submenu-title { &.ant-menu-submenu-selected > .ant-menu-submenu-title {
color: var(--sidebar-menu-text-selected, #01412b); color: $primary;
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important; font-weight: 500;
}
}
:deep(.ant-menu-submenu-inner) {
.ant-menu-submenu-title {
&:hover {
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important;
}
} }
} }
@ -412,20 +442,27 @@ const handleLogout = async () => {
.ant-menu-item { .ant-menu-item {
padding-left: 48px !important; padding-left: 48px !important;
background: transparent !important; height: 40px;
line-height: 40px;
&:hover { margin: 2px 0;
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important;
color: var(--sidebar-menu-text-selected, #01412b) !important;
}
&.ant-menu-item-selected { &.ant-menu-item-selected {
background: var(--sidebar-menu-item-selected-bg, #e2f0ed) !important; background: linear-gradient(135deg, $primary 0%, $primary-dark 100%) !important;
color: var(--sidebar-menu-text-selected, #01412b) !important; box-shadow: 0 2px 8px rgba($primary, 0.3);
font-weight: 500;
} }
} }
} }
//
:deep(.ant-menu-submenu-arrow) {
color: rgba(0, 0, 0, 0.45);
transition: all 0.3s;
}
:deep(.ant-menu-submenu-open > .ant-menu-submenu-title > .ant-menu-submenu-arrow),
:deep(.ant-menu-submenu:hover > .ant-menu-submenu-title > .ant-menu-submenu-arrow) {
color: $primary;
}
} }
.main-layout { .main-layout {
@ -435,7 +472,7 @@ const handleLogout = async () => {
.content { .content {
padding: 20px; padding: 20px;
background: #fff; background: #f5f7fa;
height: 100vh; height: 100vh;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;

View File

@ -523,9 +523,10 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// // -
$primary: #0958d9; $primary: #1890ff;
$primary-light: #1677ff; $primary-dark: #0958d9;
$primary-light: #40a9ff;
.contests-activities-page { .contests-activities-page {
padding: 24px; padding: 24px;
@ -582,7 +583,7 @@ $primary-light: #1677ff;
&.active { &.active {
color: #fff; color: #fff;
background: linear-gradient(135deg, $primary-light 0%, $primary 100%); background: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
box-shadow: 0 4px 12px rgba($primary, 0.35); box-shadow: 0 4px 12px rgba($primary, 0.35);
.anticon { .anticon {
@ -834,19 +835,19 @@ $primary-light: #1677ff;
padding: 6px 16px; padding: 6px 16px;
height: auto; height: auto;
font-size: 13px; font-size: 13px;
background: linear-gradient(135deg, #1890ff 0%, #0050b3 100%); background: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4); box-shadow: 0 2px 8px rgba($primary, 0.3);
transition: all 0.3s ease; transition: all 0.3s ease;
&:hover { &:hover {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%); background: linear-gradient(135deg, $primary-light 0%, $primary 100%);
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.5); box-shadow: 0 4px 12px rgba($primary, 0.4);
transform: translateY(-1px); transform: translateY(-1px);
} }
&:active { &:active {
transform: translateY(0); transform: translateY(0);
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.4); box-shadow: 0 2px 6px rgba($primary, 0.3);
} }
} }
@ -864,7 +865,7 @@ $primary-light: #1677ff;
&:hover { &:hover {
background: linear-gradient(135deg, #e8ecf0 0%, #dce1e6 100%); background: linear-gradient(135deg, #e8ecf0 0%, #dce1e6 100%);
color: #1890ff; color: $primary;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
transform: translateY(-1px); transform: translateY(-1px);
} }

View File

@ -30,7 +30,9 @@
<!-- 状态标签 --> <!-- 状态标签 -->
<div class="status-tags"> <div class="status-tags">
<span class="tag tag-type"> <span class="tag tag-type">
{{ contest?.contestType === "individual" ? "个人赛" : "团队赛" }} {{
contest?.contestType === "individual" ? "个人赛" : "团队赛"
}}
</span> </span>
<span v-if="getStageText()" class="tag" :class="getStageClass()"> <span v-if="getStageText()" class="tag" :class="getStageClass()">
{{ getStageText() }} {{ getStageText() }}
@ -44,11 +46,17 @@
<div class="hero-meta"> <div class="hero-meta">
<div class="meta-item"> <div class="meta-item">
<CalendarOutlined /> <CalendarOutlined />
<span>比赛时间{{ formatDate(contest?.startTime) }} ~ {{ formatDate(contest?.endTime) }}</span> <span
>比赛时间{{ formatDate(contest?.startTime) }} ~
{{ formatDate(contest?.endTime) }}</span
>
</div> </div>
<div class="meta-item"> <div class="meta-item">
<ClockCircleOutlined /> <ClockCircleOutlined />
<span>报名时间{{ formatDate(contest?.registerStartTime) }} ~ {{ formatDate(contest?.registerEndTime) }}</span> <span
>报名时间{{ formatDate(contest?.registerStartTime) }} ~
{{ formatDate(contest?.registerEndTime) }}</span
>
</div> </div>
</div> </div>
@ -76,7 +84,7 @@
disabled disabled
> >
<FormOutlined /> <FormOutlined />
{{ isRegistering ? '立即报名' : '报名已截止' }} {{ isRegistering ? "立即报名" : "报名已截止" }}
</button> </button>
<span v-if="isRegistering" class="countdown"> <span v-if="isRegistering" class="countdown">
距离报名截止还有 <strong>{{ daysRemaining }}</strong> 距离报名截止还有 <strong>{{ daysRemaining }}</strong>
@ -105,7 +113,9 @@
> >
<BellOutlined /> <BellOutlined />
<span>通知公告</span> <span>通知公告</span>
<span v-if="notices.length > 0" class="badge">{{ notices.length }}</span> <span v-if="notices.length > 0" class="badge">{{
notices.length
}}</span>
</div> </div>
<div <div
class="tab-item" class="tab-item"
@ -134,7 +144,11 @@
<h2>竞赛详情</h2> <h2>竞赛详情</h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<div v-if="contest.content" class="rich-content" v-html="contest.content"></div> <div
v-if="contest.content"
class="rich-content"
v-html="contest.content"
></div>
<a-empty v-else description="暂无详情内容" /> <a-empty v-else description="暂无详情内容" />
</div> </div>
</div> </div>
@ -151,11 +165,18 @@
<div v-if="noticesLoading" class="loading-placeholder"> <div v-if="noticesLoading" class="loading-placeholder">
<a-spin /> <a-spin />
</div> </div>
<div v-else-if="notices.length === 0" class="empty-placeholder"> <div
v-else-if="notices.length === 0"
class="empty-placeholder"
>
<a-empty description="暂无公告" /> <a-empty description="暂无公告" />
</div> </div>
<div v-else class="notice-list"> <div v-else class="notice-list">
<div v-for="item in notices" :key="item.id" class="notice-item"> <div
v-for="item in notices"
:key="item.id"
class="notice-item"
>
<div class="notice-header"> <div class="notice-header">
<span class="notice-title">{{ item.title }}</span> <span class="notice-title">{{ item.title }}</span>
<span class="notice-type" :class="item.noticeType"> <span class="notice-type" :class="item.noticeType">
@ -192,15 +213,26 @@
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'rank'"> <template v-if="column.key === 'rank'">
<div class="rank-badge" :class="getRankClass(record.rank)"> <div
class="rank-badge"
:class="getRankClass(record.rank)"
>
{{ record.rank || "-" }} {{ record.rank || "-" }}
</div> </div>
</template> </template>
<template v-else-if="column.key === 'author'"> <template v-else-if="column.key === 'author'">
{{ record.registration?.user?.nickname || record.registration?.team?.teamName || "-" }} {{
record.registration?.user?.nickname ||
record.registration?.team?.teamName ||
"-"
}}
</template> </template>
<template v-else-if="column.key === 'award'"> <template v-else-if="column.key === 'award'">
<span v-if="record.awardName" class="award-tag" :class="getAwardClass(record.awardName)"> <span
v-if="record.awardName"
class="award-tag"
:class="getAwardClass(record.awardName)"
>
{{ record.awardName }} {{ record.awardName }}
</span> </span>
<span v-else>-</span> <span v-else>-</span>
@ -230,8 +262,16 @@
主办单位 主办单位
</div> </div>
<div class="item-value"> <div class="item-value">
<template v-if="contest.organizers && contest.organizers.length"> <template
<div v-for="org in contest.organizers" :key="org" class="org-name">{{ org }}</div> v-if="contest.organizers && contest.organizers.length"
>
<div
v-for="org in contest.organizers"
:key="org"
class="org-name"
>
{{ org }}
</div>
</template> </template>
<span v-else class="empty-text">暂无</span> <span v-else class="empty-text">暂无</span>
</div> </div>
@ -243,8 +283,18 @@
协办单位 协办单位
</div> </div>
<div class="item-value"> <div class="item-value">
<template v-if="contest.coOrganizers && contest.coOrganizers.length"> <template
<div v-for="org in contest.coOrganizers" :key="org" class="org-name">{{ org }}</div> v-if="
contest.coOrganizers && contest.coOrganizers.length
"
>
<div
v-for="org in contest.coOrganizers"
:key="org"
class="org-name"
>
{{ org }}
</div>
</template> </template>
<span v-else class="empty-text">暂无</span> <span v-else class="empty-text">暂无</span>
</div> </div>
@ -256,8 +306,16 @@
赞助单位 赞助单位
</div> </div>
<div class="item-value"> <div class="item-value">
<template v-if="contest.sponsors && contest.sponsors.length"> <template
<div v-for="sp in contest.sponsors" :key="sp" class="org-name">{{ sp }}</div> v-if="contest.sponsors && contest.sponsors.length"
>
<div
v-for="sp in contest.sponsors"
:key="sp"
class="org-name"
>
{{ sp }}
</div>
</template> </template>
<span v-else class="empty-text">暂无</span> <span v-else class="empty-text">暂无</span>
</div> </div>
@ -266,7 +324,10 @@
</div> </div>
<!-- 联系方式 --> <!-- 联系方式 -->
<div v-if="contest.contactName || contest.contactPhone" class="sidebar-card"> <div
v-if="contest.contactName || contest.contactPhone"
class="sidebar-card"
>
<div class="sidebar-header"> <div class="sidebar-header">
<PhoneOutlined /> <PhoneOutlined />
<span>联系方式</span> <span>联系方式</span>
@ -293,7 +354,11 @@
</div> </div>
</div> </div>
<a-empty v-else-if="!loading" description="比赛不存在" style="padding: 100px 0" /> <a-empty
v-else-if="!loading"
description="比赛不存在"
style="padding: 100px 0"
/>
</a-spin> </a-spin>
</div> </div>
</template> </template>
@ -347,7 +412,10 @@ const myRegistration = ref<any>(null)
const canViewRegistration = computed(() => { const canViewRegistration = computed(() => {
const permissions = authStore.user?.permissions || [] const permissions = authStore.user?.permissions || []
return permissions.includes("registration:read") || permissions.includes("registration:create") return (
permissions.includes("registration:read") ||
permissions.includes("registration:create")
)
}) })
const isTeacher = computed(() => authStore.hasRole("teacher")) const isTeacher = computed(() => authStore.hasRole("teacher"))
@ -364,7 +432,13 @@ const resultColumns = [
{ title: "排名", key: "rank", dataIndex: "rank", width: 80 }, { title: "排名", key: "rank", dataIndex: "rank", width: 80 },
{ title: "作品名称", key: "title", dataIndex: "title", width: 200 }, { title: "作品名称", key: "title", dataIndex: "title", width: 200 },
{ title: "作者", key: "author", width: 150 }, { title: "作者", key: "author", width: 150 },
{ title: "最终得分", key: "finalScore", dataIndex: "finalScore", width: 120, sorter: true }, {
title: "最终得分",
key: "finalScore",
dataIndex: "finalScore",
width: 120,
sorter: true,
},
{ title: "奖项", key: "award", width: 120 }, { title: "奖项", key: "award", width: 120 },
] ]
@ -387,15 +461,23 @@ const isRegistering = computed(() => {
}) })
const isSubmitting = computed(() => { const isSubmitting = computed(() => {
if (!contest.value?.submitStartTime || !contest.value?.submitEndTime) return false if (!contest.value?.submitStartTime || !contest.value?.submitEndTime)
return false
const now = dayjs() const now = dayjs()
return now.isAfter(dayjs(contest.value.submitStartTime)) && now.isBefore(dayjs(contest.value.submitEndTime)) return (
now.isAfter(dayjs(contest.value.submitStartTime)) &&
now.isBefore(dayjs(contest.value.submitEndTime))
)
}) })
const isReviewing = computed(() => { const isReviewing = computed(() => {
if (!contest.value?.reviewStartTime || !contest.value?.reviewEndTime) return false if (!contest.value?.reviewStartTime || !contest.value?.reviewEndTime)
return false
const now = dayjs() const now = dayjs()
return now.isAfter(dayjs(contest.value.reviewStartTime)) && now.isBefore(dayjs(contest.value.reviewEndTime)) return (
now.isAfter(dayjs(contest.value.reviewStartTime)) &&
now.isBefore(dayjs(contest.value.reviewEndTime))
)
}) })
const daysRemaining = computed(() => { const daysRemaining = computed(() => {
@ -422,9 +504,12 @@ const getStageClass = () => {
const getNoticeTypeText = (type?: string) => { const getNoticeTypeText = (type?: string) => {
switch (type) { switch (type) {
case "urgent": return "紧急" case "urgent":
case "system": return "系统" return "紧急"
default: return "公告" case "system":
return "系统"
default:
return "公告"
} }
} }
@ -488,7 +573,11 @@ const fetchResults = async () => {
if (!contest.value || contest.value.resultState !== "published") return if (!contest.value || contest.value.resultState !== "published") return
resultsLoading.value = true resultsLoading.value = true
try { try {
const response = await resultsApi.getResults(contestId, resultsPagination.value.current, resultsPagination.value.pageSize) const response = await resultsApi.getResults(
contestId,
resultsPagination.value.current,
resultsPagination.value.pageSize
)
results.value = response.list || [] results.value = response.list || []
resultsPagination.value.total = response.total || 0 resultsPagination.value.total = response.total || 0
} catch (error: any) { } catch (error: any) {
@ -566,7 +655,11 @@ $primary-dark: #0958d9;
.hero-overlay { .hero-overlay {
position: absolute; position: absolute;
inset: 0; inset: 0;
background: linear-gradient(180deg, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.6) 100%); background: linear-gradient(
180deg,
rgba(0, 0, 0, 0.3) 0%,
rgba(0, 0, 0, 0.6) 100%
);
} }
.hero-nav { .hero-nav {
@ -628,10 +721,18 @@ $primary-dark: #0958d9;
border: 1px solid rgba(255, 255, 255, 0.3); border: 1px solid rgba(255, 255, 255, 0.3);
} }
.tag-registering { background: linear-gradient(135deg, #52c41a, #73d13d); } .tag-registering {
.tag-submitting { background: linear-gradient(135deg, #1890ff, #40a9ff); } background: linear-gradient(135deg, #52c41a, #73d13d);
.tag-reviewing { background: linear-gradient(135deg, #faad14, #ffc53d); } }
.tag-finished { background: linear-gradient(135deg, #8c8c8c, #bfbfbf); } .tag-submitting {
background: linear-gradient(135deg, #1890ff, #40a9ff);
}
.tag-reviewing {
background: linear-gradient(135deg, #faad14, #ffc53d);
}
.tag-finished {
background: linear-gradient(135deg, #8c8c8c, #bfbfbf);
}
} }
.hero-title { .hero-title {
@ -656,7 +757,9 @@ $primary-dark: #0958d9;
font-size: 14px; font-size: 14px;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
.anticon { font-size: 16px; } .anticon {
font-size: 16px;
}
} }
} }
@ -721,7 +824,7 @@ $primary-dark: #0958d9;
background: #fff; background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
position: sticky; position: sticky;
top: 0; top: -20px;
z-index: 100; z-index: 100;
.tab-container { .tab-container {
@ -782,7 +885,7 @@ $primary-dark: #0958d9;
padding: 24px 0 48px; padding: 24px 0 48px;
.main-container { .main-container {
padding: 0 24px; padding: 0;
} }
.content-grid { .content-grid {
@ -839,15 +942,24 @@ $primary-dark: #0958d9;
:deep(p) { :deep(p) {
margin-bottom: 16px; margin-bottom: 16px;
&:last-child { margin-bottom: 0; } &:last-child {
margin-bottom: 0;
}
} }
:deep(h1), :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) { :deep(h1),
:deep(h2),
:deep(h3),
:deep(h4),
:deep(h5),
:deep(h6) {
color: rgba(0, 0, 0, 0.85); color: rgba(0, 0, 0, 0.85);
font-weight: 600; font-weight: 600;
margin-top: 24px; margin-top: 24px;
margin-bottom: 12px; margin-bottom: 12px;
&:first-child { margin-top: 0; } &:first-child {
margin-top: 0;
}
} }
:deep(img) { :deep(img) {
@ -856,7 +968,8 @@ $primary-dark: #0958d9;
margin: 16px 0; margin: 16px 0;
} }
:deep(ul), :deep(ol) { :deep(ul),
:deep(ol) {
padding-left: 24px; padding-left: 24px;
margin-bottom: 16px; margin-bottom: 16px;
} }
@ -870,7 +983,8 @@ $primary-dark: #0958d9;
border-collapse: collapse; border-collapse: collapse;
margin: 16px 0; margin: 16px 0;
th, td { th,
td {
border: 1px solid #e8e8e8; border: 1px solid #e8e8e8;
padding: 12px; padding: 12px;
text-align: left; text-align: left;
@ -893,7 +1007,9 @@ $primary-dark: #0958d9;
:deep(a) { :deep(a) {
color: $primary; color: $primary;
text-decoration: none; text-decoration: none;
&:hover { text-decoration: underline; } &:hover {
text-decoration: underline;
}
} }
} }
@ -905,7 +1021,9 @@ $primary-dark: #0958d9;
border-radius: 12px; border-radius: 12px;
margin-bottom: 12px; margin-bottom: 12px;
&:last-child { margin-bottom: 0; } &:last-child {
margin-bottom: 0;
}
.notice-header { .notice-header {
display: flex; display: flex;
@ -925,8 +1043,14 @@ $primary-dark: #0958d9;
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
&.urgent { background: #fff2f0; color: #ff4d4f; } &.urgent {
&.system { background: #e6f7ff; color: #1890ff; } background: #fff2f0;
color: #ff4d4f;
}
&.system {
background: #e6f7ff;
color: #1890ff;
}
} }
} }
@ -960,9 +1084,18 @@ $primary-dark: #0958d9;
background: #f0f0f0; background: #f0f0f0;
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
&.rank-1 { background: linear-gradient(135deg, #ffd700, #ffb800); color: #fff; } &.rank-1 {
&.rank-2 { background: linear-gradient(135deg, #c0c0c0, #a0a0a0); color: #fff; } background: linear-gradient(135deg, #ffd700, #ffb800);
&.rank-3 { background: linear-gradient(135deg, #cd7f32, #b8860b); color: #fff; } color: #fff;
}
&.rank-2 {
background: linear-gradient(135deg, #c0c0c0, #a0a0a0);
color: #fff;
}
&.rank-3 {
background: linear-gradient(135deg, #cd7f32, #b8860b);
color: #fff;
}
} }
// //
@ -972,9 +1105,18 @@ $primary-dark: #0958d9;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
&.award-gold { background: #fffbe6; color: #d48806; } &.award-gold {
&.award-silver { background: #f5f5f5; color: #595959; } background: #fffbe6;
&.award-bronze { background: #fff7e6; color: #d46b08; } color: #d48806;
}
&.award-silver {
background: #f5f5f5;
color: #595959;
}
&.award-bronze {
background: #fff7e6;
color: #d46b08;
}
} }
// //
@ -985,20 +1127,29 @@ $primary-dark: #0958d9;
overflow: hidden; overflow: hidden;
margin-bottom: 20px; margin-bottom: 20px;
&:last-child { margin-bottom: 0; } &:last-child {
margin-bottom: 0;
}
.sidebar-header { .sidebar-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 16px 20px; padding: 16px 20px;
background: linear-gradient(135deg, rgba($primary, 0.05), rgba($primary-dark, 0.08)); background: linear-gradient(
135deg,
rgba($primary, 0.05),
rgba($primary-dark, 0.08)
);
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
color: rgba(0, 0, 0, 0.85); color: rgba(0, 0, 0, 0.85);
.anticon { color: $primary; font-size: 16px; } .anticon {
color: $primary;
font-size: 16px;
}
} }
.sidebar-body { .sidebar-body {
@ -1008,7 +1159,9 @@ $primary-dark: #0958d9;
.sidebar-item { .sidebar-item {
margin-bottom: 16px; margin-bottom: 16px;
&:last-child { margin-bottom: 0; } &:last-child {
margin-bottom: 0;
}
.item-label { .item-label {
display: flex; display: flex;
@ -1018,7 +1171,9 @@ $primary-dark: #0958d9;
color: rgba(0, 0, 0, 0.45); color: rgba(0, 0, 0, 0.45);
margin-bottom: 8px; margin-bottom: 8px;
.anticon { font-size: 14px; } .anticon {
font-size: 14px;
}
} }
.item-value { .item-value {
@ -1030,10 +1185,14 @@ $primary-dark: #0958d9;
padding: 4px 0; padding: 4px 0;
border-bottom: 1px dashed #f0f0f0; border-bottom: 1px dashed #f0f0f0;
&:last-child { border-bottom: none; } &:last-child {
border-bottom: none;
}
} }
.empty-text { color: rgba(0,0,0,0.25); } .empty-text {
color: rgba(0, 0, 0, 0.25);
}
} }
} }
} }
@ -1061,9 +1220,16 @@ $primary-dark: #0958d9;
height: auto; height: auto;
min-height: 360px; min-height: 360px;
.hero-title { font-size: 24px; } .hero-title {
.hero-meta { flex-direction: column; gap: 12px; } font-size: 24px;
.hero-actions { flex-wrap: wrap; } }
.hero-meta {
flex-direction: column;
gap: 12px;
}
.hero-actions {
flex-wrap: wrap;
}
} }
.tab-section .custom-tabs { .tab-section .custom-tabs {

View File

@ -361,17 +361,22 @@ const handlePreview3DModel = (fileUrl: string, index: number) => {
// URL // URL
const allModelUrls = modelItems.value.map((m) => m.fileUrl) const allModelUrls = modelItems.value.map((m) => m.fileUrl)
// sessionStorageURL
if (allModelUrls.length > 1) { if (allModelUrls.length > 1) {
sessionStorage.setItem("model-viewer-urls", JSON.stringify(allModelUrls)) sessionStorage.setItem("model-viewer-urls", JSON.stringify(allModelUrls))
sessionStorage.setItem("model-viewer-index", String(index)) sessionStorage.setItem("model-viewer-index", String(index))
// URL
sessionStorage.removeItem("model-viewer-url")
} else { } else {
sessionStorage.setItem("model-viewer-url", fileUrl)
sessionStorage.removeItem("model-viewer-urls") sessionStorage.removeItem("model-viewer-urls")
sessionStorage.removeItem("model-viewer-index") sessionStorage.removeItem("model-viewer-index")
} }
// URL
router.push({ router.push({
path: `/${tenantCode}/workbench/model-viewer`, path: `/${tenantCode}/workbench/model-viewer`,
query: { url: fileUrl },
}) })
} }

View File

@ -1489,13 +1489,13 @@ onUnmounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
// ========================================== // ==========================================
// - // -
// ========================================== // ==========================================
$primary: #0958d9; $primary: #1890ff;
$primary-light: #1677ff; $primary-dark: #0958d9;
$primary-dark: #003eb3; $primary-light: #40a9ff;
$secondary: #4096ff; $secondary: #4096ff;
$accent: #1677ff; $accent: #40a9ff;
$success: #52c41a; $success: #52c41a;
$warning: #faad14; $warning: #faad14;
$error: #ff4d4f; $error: #ff4d4f;
@ -1509,8 +1509,8 @@ $text-secondary: rgba(0, 0, 0, 0.65);
$text-muted: rgba(0, 0, 0, 0.45); $text-muted: rgba(0, 0, 0, 0.45);
$text-light: #ffffff; $text-light: #ffffff;
// // -
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%); $gradient-primary: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
.model-viewer-page { .model-viewer-page {
position: fixed; position: fixed;

View File

@ -355,13 +355,13 @@ onUnmounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
// ========================================== // ==========================================
// - Index.vue // -
// ========================================== // ==========================================
$primary: #0958d9; $primary: #1890ff;
$primary-light: #1677ff; $primary-dark: #0958d9;
$primary-dark: #003eb3; $primary-light: #40a9ff;
$secondary: #4096ff; $secondary: #4096ff;
$accent: #1677ff; $accent: #40a9ff;
$success: #52c41a; $success: #52c41a;
$warning: #faad14; $warning: #faad14;
$error: #ff4d4f; $error: #ff4d4f;
@ -376,8 +376,8 @@ $text: rgba(0, 0, 0, 0.85);
$text-secondary: rgba(0, 0, 0, 0.65); $text-secondary: rgba(0, 0, 0, 0.65);
$text-muted: rgba(0, 0, 0, 0.45); $text-muted: rgba(0, 0, 0, 0.45);
// // -
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%); $gradient-primary: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
$gradient-card: linear-gradient( $gradient-card: linear-gradient(
145deg, 145deg,
rgba($primary, 0.05) 0%, rgba($primary, 0.05) 0%,

View File

@ -391,11 +391,11 @@ onUnmounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
// ========================================== // ==========================================
// // -
// ========================================== // ==========================================
$primary: #0958d9; $primary: #1890ff;
$primary-light: #1677ff; $primary-dark: #0958d9;
$primary-dark: #003eb3; $primary-light: #40a9ff;
$secondary: #4096ff; $secondary: #4096ff;
$success: #52c41a; $success: #52c41a;
$warning: #faad14; $warning: #faad14;
@ -412,7 +412,8 @@ $text-muted: rgba(0, 0, 0, 0.45);
$border: #d9d9d9; $border: #d9d9d9;
$border-light: #e8e8e8; $border-light: #e8e8e8;
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%); // -
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
.history-page { .history-page {
min-height: 100vh; min-height: 100vh;

View File

@ -773,14 +773,14 @@ onUnmounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
// ========================================== // ==========================================
// - // -
// ========================================== // ==========================================
// - // -
$primary: #0958d9; // $primary: #1890ff;
$primary-light: #1677ff; $primary-dark: #0958d9;
$primary-dark: #003eb3; $primary-light: #40a9ff;
$secondary: #4096ff; // $secondary: #4096ff;
$accent: #1677ff; // $accent: #40a9ff;
$success: #52c41a; $success: #52c41a;
$warning: #faad14; $warning: #faad14;
$error: #ff4d4f; $error: #ff4d4f;
@ -796,9 +796,9 @@ $text-secondary: rgba(0, 0, 0, 0.65);
$text-muted: rgba(0, 0, 0, 0.45); $text-muted: rgba(0, 0, 0, 0.45);
$text-light: #ffffff; // $text-light: #ffffff; //
// - // -
$gradient-primary: linear-gradient(135deg, $primary 0%, $primary-light 100%); $gradient-primary: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
$gradient-secondary: linear-gradient(135deg, $accent 0%, $primary 100%); $gradient-secondary: linear-gradient(135deg, $primary-light 0%, $primary 100%);
.ai-3d-container { .ai-3d-container {
display: flex; display: flex;
@ -1335,8 +1335,8 @@ $gradient-secondary: linear-gradient(135deg, $accent 0%, $primary 100%);
line-height: 1.3; line-height: 1.3;
background: linear-gradient( background: linear-gradient(
135deg, 135deg,
$primary 0%, $primary-dark 0%,
$primary-light 40%, $primary 35%,
#8b5cf6 70%, #8b5cf6 70%,
#a855f7 100% #a855f7 100%
); );
@ -1391,16 +1391,16 @@ $gradient-secondary: linear-gradient(135deg, $accent 0%, $primary 100%);
flex-shrink: 0; flex-shrink: 0;
&.gradient-1 { &.gradient-1 {
background: linear-gradient(135deg, $primary 0%, $primary-light 100%); background: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
} }
&.gradient-2 { &.gradient-2 {
background: linear-gradient(135deg, $primary-light 0%, $accent 100%); background: linear-gradient(135deg, $primary-dark 0%, darken($primary-dark, 10%) 100%);
} }
&.gradient-3 { &.gradient-3 {
background: linear-gradient(135deg, $accent 0%, $primary 100%); background: linear-gradient(135deg, $primary 0%, $primary-dark 100%);
} }
&.gradient-4 { &.gradient-4 {
background: linear-gradient(135deg, $primary 0%, $primary-dark 100%); background: linear-gradient(135deg, $primary-dark 0%, $primary 100%);
} }
} }