feat(激励): 知识点地图——每个知识点做对几道、在哪一档、再做几道升级,首页一句话提示本周进步
Deploy / deploy (push) Canceled after 0s

现有 38 个成就全是累计量(通过几题、提交几次、连续几天),起步的人人都有、往上的够不着,
而且没有一个在说「你学会了什么」。连续打卡在这里结构上做不到:2025 秋学生最长连续交题
天数的中位数是 1 天,连续 7 天的只有 3 人。

- GET /me/knowledge:题数 ≥10 的知识点标签(10 个),每个给做对几道、档位、周一时的档位、
  下一档门槛;四档 入门 1 / 会了 3 / 熟练 题数 40%(≤10)/ 精通 75%(≤20)。
  回测 2025 秋:中位数学生一学期升 5 档;「入门」一档是给只做对一两道的 71 人加的
- 同班 ≥30% 点亮过的知识点才会出现在「还没碰过」里,免得 Python 班看到「C 语言」
- 个人主页加「我的知识点」,只在看自己时显示;首页一句话:本周升级 > 离下一档最近的 >
  去点亮一个班里在学的,点了去按知识点筛的题目列表

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 23:17:43 +08:00
1 parent a63bf6ea07
commit a7f6ebaf49
8 files changed
+446

No files matched your search

+31
View File
@@ -4,6 +4,7 @@ import {
getAnnouncement,
getAnnouncementList,
getClassActivity,
getKnowledgeMap,
getContestList,
getLearnProgress,
getSubmissions,
@@ -13,6 +14,7 @@ import {
import SubmissionResultTag from "shared/components/SubmissionResultTag.vue"
import { useBreakpoints } from "shared/composables/breakpoints"
import { useProblemJump } from "shared/composables/problemJump"
import { pickHeadline, type Headline } from "oj/user/knowledge"
import { useUserStore } from "shared/store/user"
import { ContestStatus, CONTEST_STATUS } from "utils/constants"
import { duration, parseTime, zonedParts } from "utils/functions"
@@ -49,6 +51,8 @@ const submissions = ref<SubmissionListItem[]>([])
const announcements = ref<AnnouncementListItem[]>([])
const weekly = ref<WeeklyRank | null>(null)
const classActivity = ref<ClassActivity | null>(null)
/** 知识点地图挑出来的那一句(本周升级 / 再做几道升档 / 去点亮一个),见 oj/user/knowledge.ts */
const knowledgeHeadline = ref<Headline | null>(null)
const loaded = ref(false)
const keyword = ref("")
@@ -121,6 +125,10 @@ async function loadAnnouncements() {
announcements.value = res.results
}
async function loadKnowledge() {
knowledgeHeadline.value = pickHeadline(await getKnowledgeMap())
}
async function loadClassActivity() {
classActivity.value = await getClassActivity()
}
@@ -160,6 +168,7 @@ async function load() {
loadAnnouncements(),
loadWeekly(),
loadClassActivity(),
loadKnowledge(),
])
loaded.value = true
}
@@ -226,6 +235,13 @@ async function openAnnouncement(item: AnnouncementListItem) {
<router-link to="/rank" class="plain-link">{{ weeklyText }}</router-link>
</template>
</n-text>
<div v-if="knowledgeHeadline" class="knowledge-line">
<router-link v-if="knowledgeHeadline.to" :to="knowledgeHeadline.to" class="plain-link">
{{ knowledgeHeadline.text }}
</router-link>
<span v-else>{{ knowledgeHeadline.text }}</span>
<router-link to="/user" class="more">我的知识点</router-link>
</div>
</div>
<n-input-group class="search">
<n-input
@@ -421,6 +437,21 @@ async function openAnnouncement(item: AnnouncementListItem) {
margin: 0 0 6px;
}
.knowledge-line {
margin-top: 6px;
display: flex;
gap: 12px;
align-items: baseline;
color: #18a058;
}
/* 这一行本身就是链接(去按知识点筛的题目列表),下划线说明它能点 */
.knowledge-line .plain-link {
color: #18a058;
text-decoration: underline;
text-underline-offset: 3px;
}
.search {
width: 320px;
max-width: 100%;