feat(前台): 题单卡片显示完成进度,手机上题目列表不再横向溢出,个人主页数据排得更紧凑

- 题单卡片:完成进度条和「已完成 x / y 题」,「已完成」改成绿色,未获得的徽章置灰;
  进度按加入时的快照算,题单后来加了题就注明「新加了 N 题」,不再出现「已完成」却是 5 / 6
- 题目列表:手机上只留状态、编号、题目、难度四列;标签默认收起,选中的标签显示在按钮旁
- 个人主页:六项数据保留,改成三列两行的小卡片,头像缩小

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 20:27:00 +08:00
1 parent 6bf8c6e1d9
commit e1b6a9e9fa
3 files changed
+254 -142

No files matched your search

+24 -5
View File
@@ -63,12 +63,11 @@ const router = useRouter()
const userStore = useUserStore()
const { isDesktop } = useBreakpoints()
const { isDesktop, isMobile } = useBreakpoints()
const problems = ref<ProblemRow[]>([])
const total = ref(0)
const tags = ref<Tag[]>([])
const [showTag, toggleShowTag] = useToggle(isDesktop.value)
// 使用分页 composable
const { query, clearQuery } = usePagination<ProblemQuery>({
@@ -79,6 +78,10 @@ const { query, clearQuery } = usePagination<ProblemQuery>({
sort: useRouteQuery("sort", "").value,
})
// 标签默认收起:四十多个标签在桌面上要占三行,把题目表格顶到半屏以下。
// 从带 ?tag= 的链接进来时展开,让人看得见是哪个标签在起作用
const [showTag, toggleShowTag] = useToggle(!!query.tag)
async function listProblems() {
if (query.page < 1) query.page = 1
const offset = (query.page - 1) * query.limit
@@ -208,8 +211,21 @@ const baseColumns: DataTableColumn<ProblemRow>[] = [
},
]
// 手机上只留判断「做不做这题」要的几列,其余的挤进 390 宽的屏幕只会横向溢出
const MOBILE_WIDTHS: Record<string, { width?: number; minWidth?: number }> = {
status: { width: 60 },
_id: { width: 72 },
title: { minWidth: 120 },
difficulty: { width: 72 },
}
const columns = computed(() =>
userStore.isAuthed ? baseColumns : baseColumns.filter((c: any) => c.key !== "status"),
baseColumns
.filter((c: any) => {
if (c.key === "status" && !userStore.isAuthed) return false
return !isMobile.value || c.key in MOBILE_WIDTHS
})
.map((c: any) => (isMobile.value ? { ...c, ...MOBILE_WIDTHS[c.key] } : c)),
)
function rowProps(row: ProblemRow) {
@@ -263,12 +279,15 @@ function rowProps(row: ProblemRow) {
<n-form-item>
<n-button @click="toggleShowTag()" quaternary icon-placement="right">
<template #icon>
<Icon v-if="showTag" icon="ph:caret-down"></Icon>
<Icon v-else icon="ph:caret-up"></Icon>
<Icon v-if="showTag" icon="ph:caret-up"></Icon>
<Icon v-else icon="ph:caret-down"></Icon>
</template>
标签
</n-button>
</n-form-item>
<n-form-item v-if="query.tag && !showTag">
<n-tag type="success" closable @close="query.tag = ''">{{ query.tag }}</n-tag>
</n-form-item>
</n-form>
</n-space>
<Hitokoto v-if="isDesktop" class="problem-list-hitokoto" />