style(前台): 列表页去掉表头 emoji、已结束的比赛改成灰色;比赛列表置顶进行中的比赛,排名页先看榜单
- 前台 53 处表头改成纯文字,删掉只剩这一个用途的 utils/renders.ts - 「已结束」从红色改灰色(比赛列表 / 详情 / 后台 / 首页一起) - 比赛列表第一页、无筛选时顶部列出进行中和即将开始的比赛 - 内容少的页面页脚贴底,不再悬在半屏 - 排名页两张柱状图挪到最后,第一屏是 Top100、本周进步榜和我的排名 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
31aa5f6e74
commit
6bf8c6e1d9
12 files changed
+183
-107
No files matched your search
@@ -9,8 +9,7 @@ import Pagination from "shared/components/Pagination.vue"
|
||||
import { useAuthModalStore } from "shared/store/authModal"
|
||||
import { usePagination } from "shared/composables/pagination"
|
||||
import { useUserStore } from "shared/store/user"
|
||||
import { CONTEST_STATUS, ContestType } from "utils/constants"
|
||||
import { renderTableTitle } from "utils/renders"
|
||||
import { CONTEST_STATUS, ContestStatus, ContestType } from "utils/constants"
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
@@ -48,7 +47,7 @@ const tags: SelectOption[] = [
|
||||
|
||||
const columns: DataTableColumn<Contest>[] = [
|
||||
{
|
||||
title: renderTableTitle("状态", "streamline-emojis:collision"),
|
||||
title: "状态",
|
||||
key: "status",
|
||||
width: 100,
|
||||
render: (row) =>
|
||||
@@ -59,25 +58,25 @@ const columns: DataTableColumn<Contest>[] = [
|
||||
),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("比赛", "streamline-emojis:bouquet"),
|
||||
title: "比赛",
|
||||
key: "title",
|
||||
minWidth: 360,
|
||||
render: (row) => h(ContestTitle, { contest: row }),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("标签", "fluent-emoji-flat:keycap-hashtag"),
|
||||
title: "标签",
|
||||
key: "tag",
|
||||
width: 100,
|
||||
render: (row) => h(NTag, () => row.tag),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("开始时间", "fluent-emoji-flat:eleven-thirty"),
|
||||
title: "开始时间",
|
||||
key: "start_time",
|
||||
width: 180,
|
||||
render: (row) => parseTime(row.startTime),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("比赛时长", "streamline-emojis:fishing-pole"),
|
||||
title: "比赛时长",
|
||||
key: "duration",
|
||||
width: 180,
|
||||
render: (row) => duration(row.startTime, row.endTime),
|
||||
@@ -116,18 +115,46 @@ watchDebounced(() => query.keyword, listContests, {
|
||||
// 监听其他查询条件变化
|
||||
watch(() => [query.page, query.limit, query.status, query.tag], listContests)
|
||||
|
||||
function openContest(row: Contest) {
|
||||
if (!userStore.isAuthed && row.contestType === ContestType.private) {
|
||||
authStore.openLoginModal()
|
||||
} else {
|
||||
router.push("/contest/" + row.id)
|
||||
}
|
||||
}
|
||||
|
||||
function rowProps(row: Contest) {
|
||||
return {
|
||||
style: "cursor: pointer",
|
||||
onClick() {
|
||||
if (!userStore.isAuthed && row.contestType === ContestType.private) {
|
||||
authStore.openLoginModal()
|
||||
} else {
|
||||
router.push("/contest/" + row.id)
|
||||
}
|
||||
},
|
||||
onClick: () => openContest(row),
|
||||
}
|
||||
}
|
||||
|
||||
// 进行中 / 即将开始的比赛单独拎到最上面:表格按开始时间倒序,一场开了一周的
|
||||
// 练习赛会被后来建的比赛压到第二页去,学生找不着正在比的那一场
|
||||
const active = ref<Contest[]>([])
|
||||
const showActive = computed(
|
||||
() =>
|
||||
active.value.length > 0 && query.page === 1 && !query.keyword && !query.status && !query.tag,
|
||||
)
|
||||
|
||||
async function listActive() {
|
||||
const base = { offset: 0, limit: 6, keyword: "", tag: "" }
|
||||
const [underway, upcoming] = await Promise.all([
|
||||
getContestList({ ...base, status: ContestStatus.underway }),
|
||||
getContestList({ ...base, status: ContestStatus.not_started }),
|
||||
])
|
||||
active.value = [...underway.results, ...upcoming.results].slice(0, 6)
|
||||
}
|
||||
|
||||
onMounted(listActive)
|
||||
|
||||
function activeTime(row: Contest) {
|
||||
if (row.status === ContestStatus.underway) {
|
||||
return `${parseTime(row.endTime, "M月D日 HH:mm")} 结束`
|
||||
}
|
||||
return `${parseTime(row.startTime, "M月D日 HH:mm")} 开始`
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<n-flex vertical size="large">
|
||||
@@ -157,7 +184,64 @@ function rowProps(row: Contest) {
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-space>
|
||||
<div v-if="showActive" class="active-grid">
|
||||
<div
|
||||
v-for="contest in active"
|
||||
:key="contest.id"
|
||||
class="active-card"
|
||||
:class="{ underway: contest.status === ContestStatus.underway }"
|
||||
@click="openContest(contest)"
|
||||
>
|
||||
<n-flex align="center" :size="8">
|
||||
<n-tag :type="CONTEST_STATUS[contest.status].type" size="small" :bordered="false">
|
||||
{{ CONTEST_STATUS[contest.status].name }}
|
||||
</n-tag>
|
||||
<n-text depth="3" class="active-meta">{{ contest.tag }}</n-text>
|
||||
</n-flex>
|
||||
<ContestTitle :contest="contest" class="active-title" />
|
||||
<n-text depth="3" class="active-meta">
|
||||
{{ activeTime(contest) }} · 时长 {{ duration(contest.startTime, contest.endTime) }}
|
||||
</n-text>
|
||||
</div>
|
||||
</div>
|
||||
<n-data-table :bordered="false" :columns="columns" :data="data" :row-props="rowProps" />
|
||||
</n-flex>
|
||||
<Pagination v-model:limit="query.limit" v-model:page="query.page" :total="total" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.active-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.active-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(128, 128, 128, 0.2);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.active-card.underway {
|
||||
border-color: rgba(24, 160, 88, 0.5);
|
||||
background-color: rgba(24, 160, 88, 0.06);
|
||||
}
|
||||
|
||||
.active-card:hover {
|
||||
border-color: #18a058;
|
||||
}
|
||||
|
||||
.active-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.active-meta {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -2,7 +2,6 @@
|
||||
import type { ProblemRow } from "utils/types"
|
||||
import ProblemStatus from "oj/problem/components/ProblemStatus.vue"
|
||||
import { useContestStore } from "oj/store/contest"
|
||||
import { renderTableTitle } from "utils/renders"
|
||||
|
||||
const props = defineProps<{ contestID: string }>()
|
||||
|
||||
@@ -10,29 +9,29 @@ const router = useRouter()
|
||||
const contestStore = useContestStore()
|
||||
const problemsColumns: DataTableColumn<ProblemRow>[] = [
|
||||
{
|
||||
title: renderTableTitle("状态", "streamline-ultimate-color:music-note-1"),
|
||||
title: "状态",
|
||||
key: "status",
|
||||
width: 100,
|
||||
render: (row) => h(ProblemStatus, { status: row.status }),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("编号", "fluent-emoji-flat:input-numbers"),
|
||||
title: "编号",
|
||||
key: "_id",
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("题目", "streamline-emojis:rice-ball"),
|
||||
title: "题目",
|
||||
key: "title",
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("提交数", "streamline-emojis:clinking-beer-mugs"),
|
||||
title: "提交数",
|
||||
key: "submissionCount",
|
||||
align: "center",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("通过率", "streamline-emojis:clapping-hands-1"),
|
||||
title: "通过率",
|
||||
key: "acRate",
|
||||
align: "center",
|
||||
width: 120,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useContestStore } from "oj/store/contest"
|
||||
import Pagination from "shared/components/Pagination.vue"
|
||||
import { usePagination } from "shared/composables/pagination"
|
||||
import { ContestStatus } from "utils/constants"
|
||||
import { renderTableTitle } from "utils/renders"
|
||||
import type { ContestRank, ProblemRow } from "utils/types"
|
||||
import AcAndSubmission from "../components/AcAndSubmission.vue"
|
||||
import LineChart from "../components/LineChart.vue"
|
||||
@@ -45,7 +44,7 @@ const { query } = usePagination({}, { defaultLimit: 50 })
|
||||
|
||||
const columns = ref<DataTableColumn<ContestRank>[]>([
|
||||
{
|
||||
title: renderTableTitle("编号", "fluent-emoji-flat:input-numbers"),
|
||||
title: "编号",
|
||||
key: "id",
|
||||
width: 80,
|
||||
fixed: "left",
|
||||
@@ -53,7 +52,7 @@ const columns = ref<DataTableColumn<ContestRank>[]>([
|
||||
render: (_, index) => index + (query.page - 1) * query.limit + 1,
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("用户", "streamline-emojis:smiling-face-with-sunglasses"),
|
||||
title: "用户",
|
||||
key: "username",
|
||||
width: 120,
|
||||
fixed: "left",
|
||||
@@ -70,7 +69,7 @@ const columns = ref<DataTableColumn<ContestRank>[]>([
|
||||
),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("正确数/总提交", "streamline-ultimate-color:color-palette"),
|
||||
title: "正确数/总提交",
|
||||
key: "submission",
|
||||
width: 140,
|
||||
align: "center",
|
||||
|
||||
Reference in new issue
Block a user