feat(标签): 题目列表的标签分成「知识点」「主题」两组,新建的标签在后台标出待归类
Deploy / deploy (push) Canceled after 0s

- 迁移 0022:problem_tag 加 category(knowledge / theme)和 category_confirmed;
  存量 25 个题材、题集类标签归主题,其余归知识点,全部记为已确认
- 前台题目列表按两组显示标签,组内按题目数从多到少
- 后台标签管理:每行切换分类;出题时新建的标签默认知识点、标「待归类」并排在最前,
  页头提示待归类数量,可点「确认」保留默认;加分类筛选
- 新接口 PUT /admin/problem-tags/:id/category,设置分类即视为确认

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 20:38:43 +08:00
1 parent e1b6a9e9fa
commit d07459928d
12 files changed
+4226 -30

No files matched your search

+56 -12
View File
@@ -104,6 +104,22 @@ async function listTags() {
}))
}
// 知识点在前、主题在后;组内按题目数从多到少,最常用的「输入输出」「选择结构」
// 排在最前面,不再和一堆游戏名按拼音混排
const TAG_GROUPS = [
{ category: "knowledge", label: "知识点" },
{ category: "theme", label: "主题" },
] as const
const tagGroups = computed(() =>
TAG_GROUPS.map((group) => ({
...group,
tags: tags.value
.filter((t) => t.category === group.category)
.sort((a, b) => b.problemCount - a.problemCount),
})).filter((group) => group.tags.length > 0),
)
function chooseTag(tag: Tag) {
query.tag = tag.checked ? "" : tag.name
tags.value = tags.value.map((t) => {
@@ -293,18 +309,24 @@ function rowProps(row: ProblemRow) {
<Hitokoto v-if="isDesktop" class="problem-list-hitokoto" />
</div>
<n-collapse-transition :show="showTag">
<n-flex>
<n-tag
v-for="tag in tags"
:closable="tag.checked"
@close="chooseTag(tag)"
@click="chooseTag(tag)"
:key="tag.id"
:type="tag.checked ? 'success' : 'default'"
>
{{ tag.name }}
</n-tag>
</n-flex>
<div class="tag-groups">
<div v-for="group in tagGroups" :key="group.category" class="tag-group">
<n-text depth="3" class="tag-group-label">{{ group.label }}</n-text>
<n-flex>
<n-tag
v-for="tag in group.tags"
:closable="tag.checked"
@close="chooseTag(tag)"
@click="chooseTag(tag)"
:key="tag.id"
:type="tag.checked ? 'success' : 'default'"
class="tag-chip"
>
{{ tag.name }}
</n-tag>
</n-flex>
</div>
</div>
</n-collapse-transition>
<n-data-table :bordered="false" :data="problems" :columns="columns" :row-props="rowProps" />
</n-flex>
@@ -330,6 +352,28 @@ function rowProps(row: ProblemRow) {
min-width: 0;
}
.tag-groups {
display: flex;
flex-direction: column;
gap: 10px;
}
.tag-group {
display: grid;
grid-template-columns: 48px minmax(0, 1fr);
align-items: start;
gap: 8px;
}
.tag-group-label {
font-size: 13px;
line-height: 28px;
}
.tag-chip {
cursor: pointer;
}
@media (max-width: 768px) {
.problem-list-toolbar {
grid-template-columns: minmax(0, 1fr);