feat(achievement): 成就图标改用 iconify 渲染
Some checks failed
Deploy / deploy (build, debian, 22, /root/OJDeploy/data/clientnext) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822, /root/OJ/data/dist) (push) Has been cancelled

机房的老浏览器缺 emoji 字体,成就图标会渲染成方块。后端的 icon 字段
改存 iconify 图标名(noto 集),前端统一走新的 AchievementIcon 组件
渲染成 SVG。

组件按三种形态判断:图片 URL 走 img,含冒号走 Icon,其余当纯文本兜底,
所以存量的 emoji 数据不会瞎掉。

顺带修好解锁弹窗:题单奖章的 icon 是图片 URL,之前当文本渲染,会把
一串路径直接显示出来。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 19:20:10 -06:00
parent 0ba25dc4e3
commit 0932a05850
6 changed files with 100 additions and 6 deletions

View File

@@ -6,6 +6,7 @@ import {
type AdminAchievement,
type MetricOption,
} from "admin/api"
import AchievementIcon from "shared/components/AchievementIcon.vue"
const props = defineProps<{
show: boolean
@@ -21,7 +22,7 @@ function emptyForm() {
return {
name: "",
description: "",
icon: "🏆",
icon: "noto:trophy",
rarity: "bronze",
hidden: false,
metric: "",
@@ -99,7 +100,22 @@ async function save() {
/>
</n-form-item>
<n-form-item label="图标">
<n-input v-model:value="form.icon" placeholder="emoji例如 🦉" />
<n-flex vertical :size="4" style="flex: 1">
<n-flex align="center" :size="10">
<n-input
v-model:value="form.icon"
placeholder="iconify 图标名,例如 noto:owl"
/>
<div class="icon-preview">
<AchievementIcon :icon="form.icon" :size="28" />
</div>
</n-flex>
<n-text depth="3" style="font-size: 12px">
iconify 图标名推荐 noto: 开头的彩色 emoji 图标右侧是实时
预览预览不出来说明名字写错了图标名可在 icon-sets.iconify.design
搜索
</n-text>
</n-flex>
</n-form-item>
<n-form-item label="稀有度">
<n-select
@@ -158,3 +174,14 @@ async function save() {
</template>
</n-modal>
</template>
<style scoped>
.icon-preview {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 34px;
flex: none;
}
</style>

View File

@@ -5,6 +5,7 @@ import {
getAdminAchievements,
type AdminAchievement,
} from "admin/api"
import AchievementIcon from "shared/components/AchievementIcon.vue"
import AchievementModal from "./components/AchievementModal.vue"
const message = useMessage()
@@ -57,7 +58,12 @@ function remove(row: AdminAchievement) {
}
const columns: DataTableColumn<AdminAchievement>[] = [
{ title: "图标", key: "icon", width: 60 },
{
title: "图标",
key: "icon",
width: 60,
render: (row) => h(AchievementIcon, { icon: row.icon, size: 24 }),
},
{ title: "名称", key: "name" },
{
title: "稀有度",