style(排名): 到处是图例和规则说明,读起来像说明书
卡片头上那排图例(做出的题数 / 你 / 前一名 / 后一名 / 你们班 / 进步最大)、 右上角「9月1日起 · 每道题按第一次做对算 · ↑↓ 比周一」、副标题里的 「一样多的,先做到的在前 · 只列前面 N 名」、班级对抗的「全服 · 点班名看详情」、 各卡片标题后的「每周日晚上 · 越高越好」「下周一清零」、签名框下的提示、 班级详情抽屉里几行小字都拿掉。前一名 / 后一名的颜色靠「你」卡里同色标签认。 老师卡里「不计入排名的人」只在本班且真拿掉过人时出现。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aba3767fc1
commit
c2e05d423f
5 files changed
+17
-127
No files matched your search
@@ -55,7 +55,7 @@ function shortLabel(className: string) {
|
|||||||
<template>
|
<template>
|
||||||
<div class="battle" :class="{ bare }">
|
<div class="battle" :class="{ bare }">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<b v-if="!bare">班级对抗</b><span class="muted">人均做对 · 全服 · 点班名看详情</span>
|
<b v-if="!bare">班级对抗</b><span class="muted">人均做对</span>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="pk"
|
v-if="pk"
|
||||||
:to="{ path: '/class', query: mine ? { classes: mine } : {} }"
|
:to="{ path: '/class', query: mine ? { classes: mine } : {} }"
|
||||||
|
|||||||
@@ -125,9 +125,7 @@ function go(path: string, query: Record<string, string>) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="sec-title">
|
<div class="sec-title">全班分布</div>
|
||||||
全班分布<span class="muted">一个点一个同学,下面是这学期做对几道</span>
|
|
||||||
</div>
|
|
||||||
<div class="dots">
|
<div class="dots">
|
||||||
<div v-for="column in columns" :key="column.solved" class="dot-col">
|
<div v-for="column in columns" :key="column.solved" class="dot-col">
|
||||||
<div class="stack">
|
<div class="stack">
|
||||||
@@ -192,9 +190,7 @@ function go(path: string, query: Record<string, string>) {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section v-if="detail.care">
|
<section v-if="detail.care">
|
||||||
<div class="sec-title">
|
<div class="sec-title">要多关心的同学</div>
|
||||||
要多关心的同学<span class="muted">这学期做对不到 3 道 · 只有老师看得到</span>
|
|
||||||
</div>
|
|
||||||
<div v-for="row in detail.care" :key="row.user.id" class="care">
|
<div v-for="row in detail.care" :key="row.user.id" class="care">
|
||||||
<RankAvatar :username="row.user.username" :avatar="row.avatar" :size="20" />
|
<RankAvatar :username="row.user.username" :avatar="row.avatar" :size="20" />
|
||||||
<UserName :username="row.user.username" />
|
<UserName :username="row.user.username" />
|
||||||
@@ -230,7 +226,6 @@ function go(path: string, query: Record<string, string>) {
|
|||||||
<path d="M12 3l1.8 4.7L18.5 9.5l-4.7 1.8L12 16l-1.8-4.7L5.5 9.5l4.7-1.8z" />
|
<path d="M12 3l1.8 4.7L18.5 9.5l-4.7 1.8L12 16l-1.8-4.7L5.5 9.5l4.7-1.8z" />
|
||||||
</svg>
|
</svg>
|
||||||
<b>AI 分析这个班</b>
|
<b>AI 分析这个班</b>
|
||||||
<span class="muted">根据上面这些数字写,仅供参考</span>
|
|
||||||
<div class="spacer" />
|
<div class="spacer" />
|
||||||
<n-button
|
<n-button
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ function threatNote(me: RankRow, behind: RankRow) {
|
|||||||
placeholder="写点什么,让大家认识你"
|
placeholder="写点什么,让大家认识你"
|
||||||
autofocus
|
autofocus
|
||||||
/>
|
/>
|
||||||
<span class="mood-hint">排名、个人主页上都会显示</span>
|
|
||||||
<div class="mood-actions">
|
<div class="mood-actions">
|
||||||
<n-button size="small" @click="editing = false">取消</n-button>
|
<n-button size="small" @click="editing = false">取消</n-button>
|
||||||
<n-button size="small" type="primary" attr-type="submit" :loading="saving">
|
<n-button size="small" type="primary" attr-type="submit" :loading="saving">
|
||||||
@@ -439,11 +438,6 @@ function threatNote(me: RankRow, behind: RankRow) {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mood-hint {
|
|
||||||
font-size: var(--oj-fs-meta);
|
|
||||||
color: v-bind("theme.textColor3");
|
|
||||||
}
|
|
||||||
|
|
||||||
.mood-actions {
|
.mood-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import RankAvatar from "./RankAvatar.vue"
|
|||||||
* 重新挂载,折叠状态跟着复位。
|
* 重新挂载,折叠状态跟着复位。
|
||||||
*
|
*
|
||||||
* 桌面的条只有百来像素(老师那边多一个「⋯」,七十来像素),压不下文字标签:「前一名」
|
* 桌面的条只有百来像素(老师那边多一个「⋯」,七十来像素),压不下文字标签:「前一名」
|
||||||
* 「后一名」靠条的颜色和图例认,「进步最大」改成涨跌那一格的橙底徽章。手机每行有空,标签照旧。
|
* 「后一名」靠条的颜色认(和右边「你」卡里两个标签同色),「进步最大」改成涨跌那一格的橙底徽章。手机每行有空,标签照旧。
|
||||||
*
|
*
|
||||||
* 个性签名(设计稿「签名 A」):桌面一行只有一百来像素的条,放不下一句话,所以有签名的
|
* 个性签名(设计稿「签名 A」):桌面一行只有一百来像素的条,放不下一句话,所以有签名的
|
||||||
* 名字后面挂一个引号,指上去看;手机每行本来就有空,签名直接写在名字后面。
|
* 名字后面挂一个引号,指上去看;手机每行本来就有空,签名直接写在名字后面。
|
||||||
|
|||||||
+13
-112
@@ -15,7 +15,6 @@ import { useBreakpoints } from "shared/composables/breakpoints"
|
|||||||
import { useConfigStore } from "shared/store/config"
|
import { useConfigStore } from "shared/store/config"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
import { USERNAME_CLASS_RE } from "utils/constants"
|
import { USERNAME_CLASS_RE } from "utils/constants"
|
||||||
import { parseTime } from "utils/functions"
|
|
||||||
import ChampionList from "./components/ChampionList.vue"
|
import ChampionList from "./components/ChampionList.vue"
|
||||||
import ClassBattle from "./components/ClassBattle.vue"
|
import ClassBattle from "./components/ClassBattle.vue"
|
||||||
import ClassDetailDrawer from "./components/ClassDetailDrawer.vue"
|
import ClassDetailDrawer from "./components/ClassDetailDrawer.vue"
|
||||||
@@ -187,11 +186,6 @@ const scale = computed(() => rows.value[1]?.solved || rows.value[0]?.solved || 1
|
|||||||
const hot = computed(() => hottest(rows.value))
|
const hot = computed(() => hottest(rows.value))
|
||||||
|
|
||||||
const me = computed(() => board.value?.me ?? null)
|
const me = computed(() => board.value?.me ?? null)
|
||||||
/** 全服 100 名以外:「你」卡照样有,但名单上没有你这一行,图例和副标题别再提「你」 */
|
|
||||||
const meListed = computed(() => {
|
|
||||||
const b = board.value
|
|
||||||
return !!b?.me && !(b.cap && b.me.rank > b.cap)
|
|
||||||
})
|
|
||||||
const meId = computed(() => (teacher.value ? undefined : userStore.user?.id))
|
const meId = computed(() => (teacher.value ? undefined : userStore.user?.id))
|
||||||
const mateClass = computed(() => (board.value?.scope !== "class" ? classContext.value : null))
|
const mateClass = computed(() => (board.value?.scope !== "class" ? classContext.value : null))
|
||||||
|
|
||||||
@@ -200,35 +194,12 @@ const title = computed(() =>
|
|||||||
? `${groupTitle(board.value.scope, board.value.className)} · ${periodLabel(board.value.period)}`
|
? `${groupTitle(board.value.scope, board.value.className)} · ${periodLabel(board.value.period)}`
|
||||||
: "",
|
: "",
|
||||||
)
|
)
|
||||||
|
/** 只报人数。同分怎么排、名单折在哪,赛道自己看得出来,不再写一遍(用户嫌解释太多) */
|
||||||
const subtitle = computed(() => {
|
const subtitle = computed(() => {
|
||||||
const b = board.value
|
const b = board.value
|
||||||
if (!b) return ""
|
if (!b) return ""
|
||||||
const head = b.scope === "class" ? `${b.total} 人` : `${b.total} 人做对过`
|
return b.scope === "class" ? `${b.total} 人` : `${b.total} 人做对过`
|
||||||
const capped = !!b.cap && b.total > b.cap
|
|
||||||
const part = !b.complete
|
|
||||||
? meListed.value
|
|
||||||
? " · 前面一段 + 你附近一段"
|
|
||||||
: ` · 只列前面 ${b.rows.length} 名`
|
|
||||||
: capped
|
|
||||||
? ` · 只列前 ${b.cap} 名`
|
|
||||||
: ""
|
|
||||||
return `${head} · 一样多的,先做到的在前${part}`
|
|
||||||
})
|
})
|
||||||
/** 手机上标题旁边那句:只说从哪天起、跟什么时候比 */
|
|
||||||
const shortRule = computed(() => {
|
|
||||||
const b = board.value
|
|
||||||
if (!b) return ""
|
|
||||||
const from = b.start ? `${parseTime(b.start, "M月D日")}起` : "全部历史"
|
|
||||||
return `${from} · ↑↓ 比${b.period === "week" ? "今天早上" : "周一"}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const ruleText = computed(() => {
|
|
||||||
const b = board.value
|
|
||||||
if (!b) return ""
|
|
||||||
const from = b.start ? `${parseTime(b.start, "M月D日")}起` : "全部历史"
|
|
||||||
return `${from} · 每道题按第一次做对算 · ↑↓ 比${b.period === "week" ? "今天早上" : "周一"}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const miniScopes = computed(() =>
|
const miniScopes = computed(() =>
|
||||||
SCOPE_OPTIONS.map((option) => option.value).filter(
|
SCOPE_OPTIONS.map((option) => option.value).filter(
|
||||||
(s) => s !== scope.value && (s === "all" || !!myClass.value),
|
(s) => s !== scope.value && (s === "all" || !!myClass.value),
|
||||||
@@ -387,7 +358,6 @@ async function restore(userId: number) {
|
|||||||
<div class="toolbar" :class="{ single: !isDesktop }">
|
<div class="toolbar" :class="{ single: !isDesktop }">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h2>排名</h2>
|
<h2>排名</h2>
|
||||||
<span v-if="!isDesktop" class="rule">{{ shortRule }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="seg" role="group" aria-label="范围">
|
<div class="seg" role="group" aria-label="范围">
|
||||||
<button
|
<button
|
||||||
@@ -419,10 +389,6 @@ async function restore(userId: number) {
|
|||||||
placeholder="选班"
|
placeholder="选班"
|
||||||
:options="teacherClassOptions"
|
:options="teacherClassOptions"
|
||||||
/>
|
/>
|
||||||
<template v-if="isDesktop">
|
|
||||||
<div class="spacer" />
|
|
||||||
<span class="rule">{{ ruleText }}</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main" :class="{ single: !isDesktop }">
|
<div class="main" :class="{ single: !isDesktop }">
|
||||||
@@ -431,21 +397,6 @@ async function restore(userId: number) {
|
|||||||
<div class="card-head">
|
<div class="card-head">
|
||||||
<b>{{ title }}</b>
|
<b>{{ title }}</b>
|
||||||
<span class="muted">{{ subtitle }}</span>
|
<span class="muted">{{ subtitle }}</span>
|
||||||
<div class="spacer" />
|
|
||||||
<span v-if="isDesktop || (!teacher && me) || hot" class="legend">
|
|
||||||
<span v-if="isDesktop"
|
|
||||||
><i class="bar-key" :style="{ background: palette.track }"
|
|
||||||
><i :style="{ background: palette.bar }" /></i
|
|
||||||
>做出的题数</span
|
|
||||||
>
|
|
||||||
<template v-if="!teacher && me">
|
|
||||||
<span v-if="meListed"><i :style="{ background: palette.me }" />你</span>
|
|
||||||
<span v-if="board.ahead"><i :style="{ background: palette.chase }" />前一名</span>
|
|
||||||
<span v-if="board.behind"><i :style="{ background: palette.threat }" />后一名</span>
|
|
||||||
<span v-if="mateClass"><i :style="{ background: palette.mate }" />你们班</span>
|
|
||||||
</template>
|
|
||||||
<span v-if="hot"><b class="hot-chip">↑</b>进步最大</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<RankPodium
|
<RankPodium
|
||||||
:rows="podium"
|
:rows="podium"
|
||||||
@@ -500,7 +451,7 @@ async function restore(userId: number) {
|
|||||||
/>
|
/>
|
||||||
<div v-else-if="board.hidden" class="note-card">
|
<div v-else-if="board.hidden" class="note-card">
|
||||||
<b>你现在不计入排名</b>
|
<b>你现在不计入排名</b>
|
||||||
<span class="muted">老师把你从排名里拿掉了,榜上看不到你。有疑问去问老师。</span>
|
<span class="muted">有疑问去问老师</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="teacher" class="note-card">
|
<div v-else-if="teacher" class="note-card">
|
||||||
<b>{{ classContext ? classLabel(classContext) : "全服" }}</b>
|
<b>{{ classContext ? classLabel(classContext) : "全服" }}</b>
|
||||||
@@ -530,18 +481,13 @@ async function restore(userId: number) {
|
|||||||
>班级 PK</n-button
|
>班级 PK</n-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden-list">
|
<!-- 只在本班、且真拿掉过人时出现;怎么拿掉在名单每行的「⋯」里,不用再写一遍 -->
|
||||||
|
<div v-if="board.scope === 'class' && board.hiddenUsers.length" class="hidden-list">
|
||||||
<span class="hidden-title">不计入排名的人</span>
|
<span class="hidden-title">不计入排名的人</span>
|
||||||
<template v-if="board.scope === 'class'">
|
<div v-for="user in board.hiddenUsers" :key="user.id" class="hidden-row">
|
||||||
<div v-for="user in board.hiddenUsers" :key="user.id" class="hidden-row">
|
<UserName :username="user.username" />
|
||||||
<UserName :username="user.username" />
|
<n-button size="small" secondary @click="restore(user.id)">恢复</n-button>
|
||||||
<n-button size="small" secondary @click="restore(user.id)">恢复</n-button>
|
</div>
|
||||||
</div>
|
|
||||||
<span v-if="!board.hiddenUsers.length" class="muted">
|
|
||||||
还没有。怀疑抄代码的,在名单上点「⋯」拿掉。
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<span v-else class="muted">切到「本班」看这个班拿掉了谁</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="userStore.isAuthed" class="note-card">
|
<div v-else-if="userStore.isAuthed" class="note-card">
|
||||||
@@ -562,20 +508,20 @@ async function restore(userId: number) {
|
|||||||
<div v-if="isDesktop && classContext" class="below">
|
<div v-if="isDesktop && classContext" class="below">
|
||||||
<div v-if="board?.trend && me" class="card">
|
<div v-if="board?.trend && me" class="card">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<b>我和前后一名的名次</b><span class="muted">每周日晚上 · 越高越好</span>
|
<b>我和前后一名的名次</b>
|
||||||
</div>
|
</div>
|
||||||
<RankTrend :trend="board.trend" :lines="trendLines" />
|
<RankTrend :trend="board.trend" :lines="trendLines" />
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<b>{{ weekTitle }}</b>
|
<b>{{ weekTitle }}</b>
|
||||||
<span class="muted">还剩 {{ daysLeft }} 天 · 下周一清零</span>
|
<span class="muted">还剩 {{ daysLeft }} 天</span>
|
||||||
</div>
|
</div>
|
||||||
<WeekTop :rows="weekBoard?.rows ?? []" :me-id="meId" :loaded="!!weekBoard" @open="open" />
|
<WeekTop :rows="weekBoard?.rows ?? []" :me-id="meId" :loaded="!!weekBoard" @open="open" />
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<b>每周冠军</b><span class="muted">本班 · 每周一清零后重新比</span>
|
<b>每周冠军</b>
|
||||||
</div>
|
</div>
|
||||||
<ChampionList
|
<ChampionList
|
||||||
:champions="champions"
|
:champions="champions"
|
||||||
@@ -617,7 +563,7 @@ async function restore(userId: number) {
|
|||||||
:lines="trendLines"
|
:lines="trendLines"
|
||||||
/>
|
/>
|
||||||
<template v-else-if="phoneTab === 'week'">
|
<template v-else-if="phoneTab === 'week'">
|
||||||
<span class="muted">{{ weekTitle }} · 还剩 {{ daysLeft }} 天 · 下周一清零</span>
|
<span class="muted">{{ weekTitle }} · 还剩 {{ daysLeft }} 天</span>
|
||||||
<WeekTop :rows="weekBoard?.rows ?? []" :me-id="meId" :loaded="!!weekBoard" @open="open" />
|
<WeekTop :rows="weekBoard?.rows ?? []" :me-id="meId" :loaded="!!weekBoard" @open="open" />
|
||||||
</template>
|
</template>
|
||||||
<ChampionList
|
<ChampionList
|
||||||
@@ -699,11 +645,6 @@ async function restore(userId: number) {
|
|||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spacer {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule,
|
|
||||||
.muted {
|
.muted {
|
||||||
font-size: var(--oj-fs-meta);
|
font-size: var(--oj-fs-meta);
|
||||||
color: v-bind("theme.textColor3");
|
color: v-bind("theme.textColor3");
|
||||||
@@ -753,46 +694,6 @@ async function restore(userId: number) {
|
|||||||
font-size: var(--oj-fs-h2);
|
font-size: var(--oj-fs-h2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
font-size: var(--oj-fs-meta);
|
|
||||||
color: v-bind("theme.textColor2");
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend span {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 和赛道里涨跌那一格的徽章同一个样子 */
|
|
||||||
.legend .hot-chip {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #ffffff;
|
|
||||||
background: #c76a12;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 0 4px;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend i {
|
|
||||||
width: 14px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 条的图例:底槽里一截条,和赛道上的条同一个样子 */
|
|
||||||
.legend .bar-key {
|
|
||||||
width: 24px;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend .bar-key i {
|
|
||||||
width: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
padding: 60px 0;
|
padding: 60px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
Reference in new issue
Block a user