refactor(achievement): 稀有度配色收进 constants,文字色适配浅色模式
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

四份重复的 RARITY_COLOR / RARITY_LABEL 合并到 utils/constants。
拆成两套:边框色块继续用饱和的原色,文字改走 useRarityColor,
按明暗主题切换,两套都压到 4.5:1 以上——原色是照深色底调的,
白底上白金只有 1.7:1、黄金 2.2:1,卡片上的稀有度 tag 看不清。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 11:24:04 -06:00
parent 4db0dc7e83
commit 0d7336bce8
7 changed files with 69 additions and 59 deletions

View File

@@ -7,6 +7,12 @@ import {
type MetricOption,
} from "admin/api"
import AchievementIcon from "shared/components/AchievementIcon.vue"
import { RARITY_LABEL } from "utils/constants"
const rarityOptions = Object.entries(RARITY_LABEL).map(([value, label]) => ({
label,
value,
}))
const props = defineProps<{
show: boolean
@@ -118,15 +124,7 @@ async function save() {
</n-flex>
</n-form-item>
<n-form-item label="稀有度">
<n-select
v-model:value="form.rarity"
:options="[
{ label: '青铜', value: 'bronze' },
{ label: '白银', value: 'silver' },
{ label: '黄金', value: 'gold' },
{ label: '白金', value: 'platinum' },
]"
/>
<n-select v-model:value="form.rarity" :options="rarityOptions" />
</n-form-item>
<n-form-item label="指标" required>
<n-select

View File

@@ -7,6 +7,7 @@ import {
} from "admin/api"
import AchievementIcon from "shared/components/AchievementIcon.vue"
import AchievementModal from "./components/AchievementModal.vue"
import { RARITY_LABEL } from "utils/constants"
const message = useMessage()
const dialog = useDialog()
@@ -16,13 +17,6 @@ const loading = ref(false)
const showModal = ref(false)
const editing = ref<AdminAchievement | null>(null)
const RARITY_LABEL: Record<string, string> = {
bronze: "青铜",
silver: "白银",
gold: "黄金",
platinum: "白金",
}
// 下架的只在有的时候才提,全部上架时标题不啰嗦
const title = computed(() => {
if (!list.value.length) return "成就管理"

View File

@@ -1,22 +1,13 @@
<script setup lang="ts">
import AchievementIcon from "shared/components/AchievementIcon.vue"
import { useRarityColor } from "shared/composables/rarity"
import { RARITY_COLOR, RARITY_LABEL } from "utils/constants"
import type { Achievement } from "utils/types"
const props = defineProps<{ achievement: Achievement }>()
const RARITY_COLOR: Record<string, string> = {
bronze: "#b87333",
silver: "#9fa6b2",
gold: "#e0a300",
platinum: "#7dd3fc",
}
const RARITY_LABEL: Record<string, string> = {
bronze: "青铜",
silver: "白银",
gold: "黄金",
platinum: "白金",
}
// 边框用原色tag 里的文字用跟主题走的那套
const rarityTextColor = useRarityColor()
// 隐藏且未解锁:后端已把名称/描述/图标和条件三件套都遮成 ??? 和 null
// 这里只负责不要把 null 渲染出来,也不要画出会泄露门槛的进度条
@@ -78,7 +69,7 @@ const unlockDate = computed(() => {
size="tiny"
:color="{
borderColor: RARITY_COLOR[achievement.rarity],
textColor: RARITY_COLOR[achievement.rarity],
textColor: rarityTextColor[achievement.rarity],
}"
>
{{ RARITY_LABEL[achievement.rarity] }}

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { getAchievements, getAchievementSummary } from "oj/achievement/api"
import { getUserBadges } from "oj/api"
import { useRarityColor } from "shared/composables/rarity"
import type { Achievement, AchievementSummary } from "utils/types"
import AchievementCard from "./components/AchievementCard.vue"
@@ -18,27 +19,8 @@ interface UserBadge {
const route = useRoute()
const name = computed(() => (route.query.name as string) || undefined)
// 稀有度配色分明暗两套AchievementCard 那组原色是照着深色底调的,
// 直接拿到白底上,白金只有 1.7:1、黄金 2.2:1小字根本看不清
const isDark = useDark()
const RARITY_COLOR_DARK: Record<string, string> = {
bronze: "#d18d4f",
silver: "#b6bcc7",
gold: "#e0a300",
platinum: "#7dd3fc",
}
const RARITY_COLOR_LIGHT: Record<string, string> = {
bronze: "#9a5b25",
silver: "#6b7280",
gold: "#a37500",
platinum: "#0284c7",
}
const rarityColor = computed(() =>
isDark.value ? RARITY_COLOR_DARK : RARITY_COLOR_LIGHT,
)
// 标签和进度条同色,整行读作一个单位
const rarityColor = useRarityColor()
const achievements = ref<Achievement[]>([])
const summary = ref<AchievementSummary | null>(null)

View File

@@ -1,18 +1,12 @@
<script setup lang="ts">
import AchievementIcon from "shared/components/AchievementIcon.vue"
import { useAchievementStore } from "shared/store/achievement"
import { RARITY_COLOR } from "utils/constants"
const store = useAchievementStore()
const { current, queue } = storeToRefs(store)
const visible = ref(false)
const RARITY_COLOR: Record<string, string> = {
bronze: "#b87333",
silver: "#9fa6b2",
gold: "#e0a300",
platinum: "#7dd3fc",
}
let timer: ReturnType<typeof setTimeout> | null = null
let gapTimer: ReturnType<typeof setTimeout> | null = null

View File

@@ -0,0 +1,15 @@
import { useDark } from "@vueuse/core"
import { computed } from "vue"
import { RARITY_TEXT_COLOR } from "utils/constants"
/**
* 成就稀有度的文字配色,跟随明暗主题切换。
* 两套色值都压到 4.5:1 以上,不然浅色模式下白金和黄金的小字看不清。
* 边框和色块不用这个,直接用 RARITY_COLOR 的原色。
*/
export function useRarityColor() {
const isDark = useDark()
return computed(() =>
isDark.value ? RARITY_TEXT_COLOR.dark : RARITY_TEXT_COLOR.light,
)
}

View File

@@ -1,4 +1,4 @@
import type { SUBMISSION_RESULT } from "./types"
import type { AchievementRarity, SUBMISSION_RESULT } from "./types"
export enum SubmissionStatus {
compile_error = -2,
@@ -276,6 +276,42 @@ export enum ChartType {
Activity,
}
// 成就稀有度
export const RARITY_LABEL: Record<AchievementRarity, string> = {
bronze: "青铜",
silver: "白银",
gold: "黄金",
platinum: "白金",
}
// 边框、色块用的原色:饱和度够高,明暗底上都醒目
export const RARITY_COLOR: Record<AchievementRarity, string> = {
bronze: "#b87333",
silver: "#9fa6b2",
gold: "#e0a300",
platinum: "#7dd3fc",
}
// 文字要另配一套。上面那组是照深色底调的,搬到白底上白金只有 1.7:1、
// 黄金 2.2:112px 的稀有度标签根本看不清。取色见 useRarityColor
export const RARITY_TEXT_COLOR: Record<
"dark" | "light",
Record<AchievementRarity, string>
> = {
dark: {
bronze: "#d18d4f",
silver: "#b6bcc7",
gold: "#e0a300",
platinum: "#7dd3fc",
},
light: {
bronze: "#9a5b25",
silver: "#6b7280",
gold: "#a37500",
platinum: "#0284c7",
},
}
// 时间范围配置
export const DURATION_OPTIONS = [
{ label: "本节课内", value: "hours:1" },