Files
OJ2/apps/web/src/oj/user/components/AchievementCard.vue
T
xuyueandClaude Opus 5.5 e368ed3eca feat(个人主页): 主页改成名片 + 两栏,做对的题按天带题名;设置并进主页,点头像换头像、点笔改签名
- 名片:头像、班级、签名、做对几道 / 本班第几 / 成就,底下一排四个时间(第一次提交、最近一次提交、距上次提交、学习天数)
- 做对的题按日期或按知识点,能搜;最上面一行是交过还没做对的
- 知识点本人和老师看得到;同学看多一张「你和他」
- 老师看学生:看他的智能分析 / 看他的提交 /「⋯」不计入排名、清空签名
- 换头像弹框:拖圆圈选一块,看领奖台和排名里多大,可退回名字的字
- 去掉「我的设置」页和下拉菜单里那一项;手机三个页签

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:03:37 +08:00

134 lines
3.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { ProfileOverview } from "@oj2/contract"
import { useThemeVars } from "naive-ui"
import AchievementIcon from "shared/components/AchievementIcon.vue"
import { RARITY_COLOR } from "utils/constants"
import { parseTime } from "utils/functions"
/** 拿到的成就一个一个摆出来,新的在前;全部 41 个(含没拿到的)在成就页 */
const props = defineProps<{
achievements: ProfileOverview["achievements"]
username: string
/** 看的是别人:「全部成就」带上他的名字 */
other: boolean
compact?: boolean
}>()
const theme = useThemeVars()
const allLink = computed(() => ({
path: "/achievement",
query: props.other ? { name: props.username } : {},
}))
</script>
<template>
<section class="card" :class="{ compact }">
<h3>
成就 <small>{{ achievements.unlocked.length }} / {{ achievements.total }}</small>
<span class="grow"></span>
<router-link :to="allLink" class="link">全部成就 ›</router-link>
</h3>
<div v-if="achievements.unlocked.length" class="grid">
<n-tooltip v-for="item in achievements.unlocked" :key="item.id" :delay="200">
<template #trigger>
<span class="medal">
<span class="ring" :style="{ '--rarity': RARITY_COLOR[item.rarity] }">
<AchievementIcon :icon="item.icon" :size="26" />
</span>
<span class="name">{{ item.name }}</span>
</span>
</template>
{{ item.description }} · {{ parseTime(item.unlockTime, "YYYY年M月D日") }}
</n-tooltip>
</div>
<div v-else class="empty">还没有成就</div>
</section>
</template>
<style scoped>
.card {
padding: 14px 18px 16px;
border: 1px solid v-bind("theme.borderColor");
border-radius: var(--oj-radius);
background: v-bind("theme.cardColor");
}
h3 {
margin: 0;
display: flex;
align-items: baseline;
gap: 8px;
font-size: var(--oj-fs-h2);
}
h3 small {
font-size: var(--oj-fs-sec);
font-weight: 400;
color: v-bind("theme.textColor3");
}
.grow {
flex: 1;
}
.link {
font-size: var(--oj-fs-meta);
font-weight: 400;
color: #18a058;
text-decoration: none;
}
.grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
row-gap: 14px;
margin-top: 12px;
}
.medal {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
cursor: default;
}
.ring {
width: 40px;
height: 40px;
box-sizing: border-box;
border-radius: 50%;
border: 2px solid var(--rarity);
display: flex;
align-items: center;
justify-content: center;
}
.name {
max-width: 100%;
font-size: 12px;
line-height: 16px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.empty {
padding: 16px 0 4px;
font-size: var(--oj-fs-sec);
color: v-bind("theme.textColor3");
}
.compact {
padding: 4px 0 0;
border: 0;
border-radius: 0;
background: none;
}
.compact .grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
</style>