style(格式化): 改成单引号、一行最长 160,全仓重新格式化
Deploy / deploy (push) Canceled after 0s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-26 13:52:24 +08:00
1 parent 530e1a3d5d
commit 195069450f
480 files changed
+15507 -24650

No files matched your search

+139 -261
View File
@@ -1,17 +1,17 @@
<script setup lang="ts">
import type { ContestClassView, ContestScoreCell, ContestScoreRow } from "@oj2/contract"
import { useThemeVars } from "naive-ui"
import { updateACMHelperChecked } from "admin/api"
import { getContestClassView, getSubmission, getSubmissions } from "oj/api"
import { useContestStore } from "oj/store/contest"
import UserName from "shared/components/UserName.vue"
import { useTone } from "shared/composables/tone"
import { classLabel } from "utils/classes"
import { useLeaveStudents } from "shared/composables/leaveStudents"
import { ContestStatus, LANGUAGE_FORMAT_VALUE } from "utils/constants"
import { parseTime, secondsToDuration } from "utils/functions"
import type { Submission } from "utils/types"
import { useVisibleInterval } from "shared/composables/visibleInterval"
import type { ContestClassView, ContestScoreCell, ContestScoreRow } from '@oj2/contract'
import { useThemeVars } from 'naive-ui'
import { updateACMHelperChecked } from 'admin/api'
import { getContestClassView, getSubmission, getSubmissions } from 'oj/api'
import { useContestStore } from 'oj/store/contest'
import UserName from 'shared/components/UserName.vue'
import { useTone } from 'shared/composables/tone'
import { classLabel } from 'utils/classes'
import { useLeaveStudents } from 'shared/composables/leaveStudents'
import { ContestStatus, LANGUAGE_FORMAT_VALUE } from 'utils/constants'
import { parseTime, secondsToDuration } from 'utils/functions'
import type { Submission } from 'utils/types'
import { useVisibleInterval } from 'shared/composables/visibleInterval'
/**
* 老师的「全班情况」,考完叫「成绩」(设计稿「比赛重设计」老师两块)。学生看不到这一页。
@@ -28,7 +28,7 @@ const contestStore = useContestStore()
const theme = useThemeVars()
const tone = useTone()
const message = useMessage()
const { onLeave } = useLeaveStudents("oj_leave_students")
const { onLeave } = useLeaveStudents('oj_leave_students')
const view = ref<ContestClassView | null>(null)
const failed = ref(false)
@@ -78,17 +78,12 @@ function flagOf(row: ContestScoreRow) {
for (const p of problems.value) {
const cell = cellOf(row, p.id)
if (cell && !cell.isAc && cell.errors >= STUCK_ERRORS) {
return { kind: "stuck" as const, text: `卡在第 ${p._id} 题,错 ${cell.errors} 次` }
return { kind: 'stuck' as const, text: `卡在第 ${p._id} 题,错 ${cell.errors} 次` }
}
}
const idle = minutesSinceLast(row)
if (
running.value &&
idle !== null &&
idle >= IDLE_MINUTES &&
row.solved < problems.value.length
) {
return { kind: "idle" as const, text: `${idle} 分钟没交` }
if (running.value && idle !== null && idle >= IDLE_MINUTES && row.solved < problems.value.length) {
return { kind: 'idle' as const, text: `${idle} 分钟没交` }
}
return null
}
@@ -97,60 +92,39 @@ const attention = computed(() =>
rows.value
.map((row) => ({ row, flag: flagOf(row) }))
.filter((item) => item.flag !== null)
.sort((a, b) =>
a.flag!.kind === b.flag!.kind
? a.row.solved - b.row.solved
: a.flag!.kind === "stuck"
? -1
: 1,
),
.sort((a, b) => (a.flag!.kind === b.flag!.kind ? a.row.solved - b.row.solved : a.flag!.kind === 'stuck' ? -1 : 1)),
)
const others = computed(() => {
const flagged = new Set(attention.value.map((item) => item.row.userId))
return rows.value
.filter((row) => !flagged.has(row.userId))
.sort((a, b) => a.solved - b.solved || (minutesSinceLast(b) ?? 0) - (minutesSinceLast(a) ?? 0))
return rows.value.filter((row) => !flagged.has(row.userId)).sort((a, b) => a.solved - b.solved || (minutesSinceLast(b) ?? 0) - (minutesSinceLast(a) ?? 0))
})
const absent = computed(() =>
(view.value?.absent ?? []).map((user) => ({ ...user, leave: onLeave(user.username) })),
)
const absent = computed(() => (view.value?.absent ?? []).map((user) => ({ ...user, leave: onLeave(user.username) })))
const leaveCount = computed(() => absent.value.filter((user) => user.leave).length)
const rosterSize = computed(() => {
const classes = new Set(view.value?.classes ?? [])
return (
rows.value.filter((row) => row.className && classes.has(row.className)).length +
absent.value.length
)
return rows.value.filter((row) => row.className && classes.has(row.className)).length + absent.value.length
})
const average = computed(() =>
rows.value.length
? (rows.value.reduce((sum, row) => sum + row.solved, 0) / rows.value.length).toFixed(1)
: "0",
)
const stuckCount = computed(
() => attention.value.filter((item) => item.flag!.kind === "stuck").length,
)
const idleCount = computed(
() => attention.value.filter((item) => item.flag!.kind === "idle").length,
)
const average = computed(() => (rows.value.length ? (rows.value.reduce((sum, row) => sum + row.solved, 0) / rows.value.length).toFixed(1) : '0'))
const stuckCount = computed(() => attention.value.filter((item) => item.flag!.kind === 'stuck').length)
const idleCount = computed(() => attention.value.filter((item) => item.flag!.kind === 'idle').length)
function lastText(row: ContestScoreRow) {
const minutes = minutesSinceLast(row)
if (minutes === null) return ""
return minutes === 0 ? "刚刚" : `${minutes} 分钟前`
if (minutes === null) return ''
return minutes === 0 ? '刚刚' : `${minutes} 分钟前`
}
const classNames = computed(() => (view.value?.classes ?? []).map(classLabel).join("、"))
const classNames = computed(() => (view.value?.classes ?? []).map(classLabel).join('、'))
// ---------------------------------------------------------------- 考完:成绩
/** 按姓名(用户名,中文按拼音)/ 名次 / 做对少的在前 */
type Sort = "username" | "rank" | "weak"
const sort = ref<Sort>("username")
type Sort = 'username' | 'rank' | 'weak'
const sort = ref<Sort>('username')
const gradeRows = computed(() => {
const list = [...rows.value]
if (sort.value === "username") list.sort((a, b) => a.username.localeCompare(b.username, "zh-CN"))
else if (sort.value === "weak") list.sort((a, b) => a.solved - b.solved || b.rank! - a.rank!)
if (sort.value === 'username') list.sort((a, b) => a.username.localeCompare(b.username, 'zh-CN'))
else if (sort.value === 'weak') list.sort((a, b) => a.solved - b.solved || b.rank! - a.rank!)
return list
})
@@ -159,51 +133,36 @@ const distribution = computed(() => {
for (const row of rows.value) count.set(row.solved, (count.get(row.solved) ?? 0) + 1)
return [...count.entries()].sort(([a], [b]) => b - a)
})
const acTotal = computed(() =>
rows.value.reduce((sum, row) => sum + Object.values(row.cells).filter((c) => c.isAc).length, 0),
)
const checkedTotal = computed(() =>
rows.value.reduce(
(sum, row) => sum + Object.values(row.cells).filter((c) => c.isAc && c.checked).length,
0,
),
)
const acTotal = computed(() => rows.value.reduce((sum, row) => sum + Object.values(row.cells).filter((c) => c.isAc).length, 0))
const checkedTotal = computed(() => rows.value.reduce((sum, row) => sum + Object.values(row.cells).filter((c) => c.isAc && c.checked).length, 0))
function barColor(solved: number) {
const n = problems.value.length || 1
const ratio = solved / n
if (ratio >= 1) return theme.value.successColor
if (ratio >= 0.7) return tone("success").solid + "b3"
if (ratio >= 0.5) return tone("success").solid + "66"
if (ratio >= 0.7) return tone('success').solid + 'b3'
if (ratio >= 0.5) return tone('success').solid + '66'
if (ratio >= 0.3) return theme.value.warningColor
return theme.value.errorColor + "aa"
return theme.value.errorColor + 'aa'
}
const selected = ref<{ userId: number; problemId: number } | null>(null)
const code = ref<Submission | null>(null)
const codeState = ref<"idle" | "loading" | "none" | "failed">("idle")
const codeState = ref<'idle' | 'loading' | 'none' | 'failed'>('idle')
const selectedRow = computed(() =>
selected.value ? rows.value.find((row) => row.userId === selected.value!.userId) : undefined,
)
const selectedProblem = computed(() =>
selected.value ? problems.value.find((p) => p.id === selected.value!.problemId) : undefined,
)
const selectedCell = computed(() =>
selectedRow.value && selectedProblem.value
? cellOf(selectedRow.value, selectedProblem.value.id)
: undefined,
)
const selectedRow = computed(() => (selected.value ? rows.value.find((row) => row.userId === selected.value!.userId) : undefined))
const selectedProblem = computed(() => (selected.value ? problems.value.find((p) => p.id === selected.value!.problemId) : undefined))
const selectedCell = computed(() => (selectedRow.value && selectedProblem.value ? cellOf(selectedRow.value, selectedProblem.value.id) : undefined))
async function pick(row: ContestScoreRow, problemId: number) {
selected.value = { userId: row.userId, problemId }
code.value = null
const problem = problems.value.find((p) => p.id === problemId)
if (!problem || !cellOf(row, problemId)) {
codeState.value = "none"
codeState.value = 'none'
return
}
codeState.value = "loading"
codeState.value = 'loading'
try {
const list = await getSubmissions({
contestId: props.contestID,
@@ -215,15 +174,15 @@ async function pick(row: ContestScoreRow, problemId: number) {
const mine = list.results.filter((item) => item.username === row.username)
const target = mine.find((item) => item.result === 0) ?? mine[0]
if (!target) {
codeState.value = "none"
codeState.value = 'none'
return
}
const detail = await getSubmission(target.id)
if (selected.value?.userId !== row.userId || selected.value.problemId !== problemId) return
code.value = detail
codeState.value = "idle"
codeState.value = 'idle'
} catch {
codeState.value = "failed"
codeState.value = 'failed'
}
}
@@ -232,15 +191,10 @@ async function toggleChecked(value: boolean) {
const cell = selectedCell.value
if (!row || !cell || !selected.value) return
try {
await updateACMHelperChecked(
Number(props.contestID),
row.rankId,
String(selected.value.problemId),
value,
)
await updateACMHelperChecked(Number(props.contestID), row.rankId, String(selected.value.problemId), value)
cell.checked = value
} catch {
message.error("没标上,只有出这场比赛的老师能标")
message.error('没标上,只有出这场比赛的老师能标')
}
}
@@ -254,10 +208,10 @@ function step(dx: number, dy: number) {
if (list[nr] && problems.value[nc]) pick(list[nr], problems.value[nc].id)
}
onKeyStroke(["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"], (event) => {
onKeyStroke(['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'], (event) => {
if (!ended.value || !selected.value) return
const target = event.target as HTMLElement | null
if (target && ["INPUT", "TEXTAREA"].includes(target.tagName)) return
if (target && ['INPUT', 'TEXTAREA'].includes(target.tagName)) return
event.preventDefault()
const map = {
ArrowLeft: [-1, 0],
@@ -270,59 +224,41 @@ onKeyStroke(["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"], (event) => {
})
function exportGrades() {
const title = contestStore.contest?.title ?? "比赛"
const head = [
"用户名",
"班级",
"做对",
"名次",
"用时",
...problems.value.map((p) => `第${p._id}题 ${p.title}`),
]
const title = contestStore.contest?.title ?? '比赛'
const head = ['用户名', '班级', '做对', '名次', '用时', ...problems.value.map((p) => `第${p._id}题 ${p.title}`)]
const lines = [head]
const byUsername = [...rows.value].sort((a, b) => a.username.localeCompare(b.username, "zh-CN"))
const byUsername = [...rows.value].sort((a, b) => a.username.localeCompare(b.username, 'zh-CN'))
for (const row of byUsername) {
lines.push([
row.username,
row.className ? classLabel(row.className) : "",
row.className ? classLabel(row.className) : '',
String(row.solved),
String(row.rank ?? ""),
String(row.rank ?? ''),
secondsToDuration(row.totalTime),
...problems.value.map((p) => {
const cell = cellOf(row, p.id)
return !cell ? "没交" : cell.isAc ? "对" : "错"
return !cell ? '没交' : cell.isAc ? '对' : '错'
}),
])
}
for (const user of absent.value) {
lines.push([
user.username,
user.className ? classLabel(user.className) : "",
user.leave ? "请假" : "没考",
"",
"",
...problems.value.map(() => ""),
])
lines.push([user.username, user.className ? classLabel(user.className) : '', user.leave ? '请假' : '没考', '', '', ...problems.value.map(() => '')])
}
const csv = lines
.map((line) => line.map((value) => `"${value.replaceAll('"', '""')}"`).join(","))
.join("\r\n")
const csv = lines.map((line) => line.map((value) => `"${value.replaceAll('"', '""')}"`).join(',')).join('\r\n')
// 带 BOM,Excel 双击打开中文才不乱码
const blob = new Blob(["" + csv], { type: "text/csv;charset=utf-8" })
const blob = new Blob(['' + csv], { type: 'text/csv;charset=utf-8' })
const url = URL.createObjectURL(blob)
const a = document.createElement("a")
const a = document.createElement('a')
a.href = url
a.download = `${title} 成绩.csv`
a.click()
URL.revokeObjectURL(url)
}
const success = computed(() => tone("success"))
const danger = computed(() => tone("error"))
const warning = computed(() => tone("warning"))
const liveColumns = computed(
() => `130px 48px repeat(${problems.value.length}, minmax(70px, 96px)) 90px 1fr`,
)
const success = computed(() => tone('success'))
const danger = computed(() => tone('error'))
const warning = computed(() => tone('warning'))
const liveColumns = computed(() => `130px 48px repeat(${problems.value.length}, minmax(70px, 96px)) 90px 1fr`)
const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.value.length}, 50px)`)
</script>
@@ -336,9 +272,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
<div class="stat">
<b>{{ rows.length }}</b>
<span
>进来了<template v-if="classNames">
· {{ classNames }} {{ rosterSize }} 人</template
></span
>进来了<template v-if="classNames"> · {{ classNames }} {{ rosterSize }} 人</template></span
>
</div>
<div v-if="absent.length" class="stat">
@@ -377,24 +311,16 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
<template v-if="attention.length">
<div class="group red-group">要看一眼的 {{ attention.length }} 人</div>
<div
v-for="{ row, flag } in attention"
:key="row.userId"
class="tr"
:style="{ gridTemplateColumns: liveColumns }"
>
<div v-for="{ row, flag } in attention" :key="row.userId" class="tr" :style="{ gridTemplateColumns: liveColumns }">
<UserName :username="row.username" />
<span class="num solved">{{ row.solved }}</span>
<span v-for="p in problems" :key="p.id" class="lc">
<template v-if="cellOf(row, p.id)">
<span v-if="cellOf(row, p.id)!.isAc" class="cell ac"
>✓ {{ Math.floor(cellOf(row, p.id)!.acTime / 60) }}′<small
v-if="cellOf(row, p.id)!.errors"
>-{{ cellOf(row, p.id)!.errors }}</small
></span
>✓ {{ Math.floor(cellOf(row, p.id)!.acTime / 60) }}′<small v-if="cellOf(row, p.id)!.errors">-{{ cellOf(row, p.id)!.errors }}</small></span
>
<span v-else class="cell no" :class="{ hot: cellOf(row, p.id)!.errors >= 3 }">{{
cellOf(row, p.id)!.errors ? `错 ${cellOf(row, p.id)!.errors}` : "交过"
cellOf(row, p.id)!.errors ? `错 ${cellOf(row, p.id)!.errors}` : '交过'
}}</span>
</template>
<span v-else class="dot">·</span>
@@ -407,25 +333,15 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
</template>
<div class="group">其余 {{ others.length }} 人 · 做对少的在前</div>
<div
v-for="row in others"
:key="row.userId"
class="tr"
:style="{ gridTemplateColumns: liveColumns }"
>
<div v-for="row in others" :key="row.userId" class="tr" :style="{ gridTemplateColumns: liveColumns }">
<UserName :username="row.username" />
<span class="num solved">{{ row.solved }}</span>
<span v-for="p in problems" :key="p.id" class="lc">
<template v-if="cellOf(row, p.id)">
<span v-if="cellOf(row, p.id)!.isAc" class="cell ac"
>✓ {{ Math.floor(cellOf(row, p.id)!.acTime / 60) }}′<small
v-if="cellOf(row, p.id)!.errors"
>-{{ cellOf(row, p.id)!.errors }}</small
></span
>✓ {{ Math.floor(cellOf(row, p.id)!.acTime / 60) }}′<small v-if="cellOf(row, p.id)!.errors">-{{ cellOf(row, p.id)!.errors }}</small></span
>
<span v-else class="cell no">{{
cellOf(row, p.id)!.errors ? `错 ${cellOf(row, p.id)!.errors}` : "交过"
}}</span>
<span v-else class="cell no">{{ cellOf(row, p.id)!.errors ? `错 ${cellOf(row, p.id)!.errors}` : '交过' }}</span>
</template>
<span v-else class="dot">·</span>
</span>
@@ -435,18 +351,13 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
<template v-if="absent.length">
<div class="group">没进来 {{ absent.length }} 人</div>
<div
v-for="user in absent"
:key="user.userId"
class="tr absent"
:style="{ gridTemplateColumns: liveColumns }"
>
<div v-for="user in absent" :key="user.userId" class="tr absent" :style="{ gridTemplateColumns: liveColumns }">
<UserName :username="user.username" muted />
<span></span>
<span v-for="p in problems" :key="p.id"></span>
<span></span>
<span class="flag-wrap">
<span class="flag grey">{{ user.leave ? "请假(看板里标的)" : "没进来" }}</span>
<span class="flag grey">{{ user.leave ? '请假(看板里标的)' : '没进来' }}</span>
</span>
</div>
</template>
@@ -466,16 +377,12 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
></span>
</div>
<div class="dist-legend">
<span v-for="[solved, n] in distribution" :key="solved">
<i :style="{ background: barColor(solved) }"></i>{{ solved }} 道 {{ n }} 人
</span>
<span v-for="[solved, n] in distribution" :key="solved"> <i :style="{ background: barColor(solved) }"></i>{{ solved }} 道 {{ n }} 人 </span>
</div>
</div>
<span class="sec small">
{{ rows.length }} 人考了<template v-if="absent.length">
· {{ absent.length }} 人没考<template v-if="leaveCount"
>({{ leaveCount }} 人请假)</template
></template
· {{ absent.length }} 人没考<template v-if="leaveCount">({{ leaveCount }} 人请假)</template></template
>
· 平均做对 {{ average }} 道
</span>
@@ -488,13 +395,9 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
<section class="sheet">
<div class="sort">
<span class="muted small">排序</span>
<button class="chip" :class="{ on: sort === 'username' }" @click="sort = 'username'">
姓名
</button>
<button class="chip" :class="{ on: sort === 'username' }" @click="sort = 'username'">姓名</button>
<button class="chip" :class="{ on: sort === 'rank' }" @click="sort = 'rank'">名次</button>
<button class="chip" :class="{ on: sort === 'weak' }" @click="sort = 'weak'">
做对少的在前
</button>
<button class="chip" :class="{ on: sort === 'weak' }" @click="sort = 'weak'">做对少的在前</button>
</div>
<div class="sheet-scroll">
<div class="tr th" :style="{ gridTemplateColumns: gradeColumns }">
@@ -536,15 +439,10 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
<i v-if="cellOf(row, p.id)?.checked" class="seen"></i>
</button>
</div>
<div
v-for="user in absent"
:key="user.userId"
class="tr absent"
:style="{ gridTemplateColumns: gradeColumns }"
>
<div v-for="user in absent" :key="user.userId" class="tr absent" :style="{ gridTemplateColumns: gradeColumns }">
<span></span>
<UserName :username="user.username" muted />
<span class="tiny">{{ user.leave ? "请假" : "没考" }}</span>
<span class="tiny">{{ user.leave ? '请假' : '没考' }}</span>
</div>
</div>
</section>
@@ -555,49 +453,29 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
<UserName :username="selectedRow.username" />
<span class="muted">·</span>
<span
><span class="muted">{{ selectedProblem._id }}</span>
{{ selectedProblem.title }}</span
><span class="muted">{{ selectedProblem._id }}</span> {{ selectedProblem.title }}</span
>
<span v-if="selectedCell?.isAc" class="pill ok">
{{
Math.floor(selectedCell.acTime / 60) === 0
? "1 分钟内做对"
: `第 ${Math.floor(selectedCell.acTime / 60)} 分钟做对`
}}
{{ Math.floor(selectedCell.acTime / 60) === 0 ? '1 分钟内做对' : `第 ${Math.floor(selectedCell.acTime / 60)} 分钟做对` }}
</span>
<span v-else-if="selectedCell" class="pill bad">没做对</span>
<span v-if="selectedCell?.errors" class="muted small"
>错过 {{ selectedCell.errors }} 次</span
>
<span v-if="selectedCell?.errors" class="muted small">错过 {{ selectedCell.errors }} 次</span>
<span v-if="selectedCell?.firstAc" class="muted small">· 全班最先做对</span>
</div>
<div class="code-box">
<n-code
v-if="code"
:code="code.code"
:language="LANGUAGE_FORMAT_VALUE[code.language]"
show-line-numbers
/>
<n-code v-if="code" :code="code.code" :language="LANGUAGE_FORMAT_VALUE[code.language]" show-line-numbers />
<span v-else-if="codeState === 'loading'" class="muted">代码加载中…</span>
<span v-else-if="codeState === 'none'" class="muted">这题没交</span>
<span v-else-if="codeState === 'failed'" class="muted">代码没拉下来,再点一次</span>
</div>
<div class="code-foot">
<n-checkbox
v-if="selectedCell?.isAc"
:checked="selectedCell.checked"
:disabled="!contestStore.isContestAdmin"
@update:checked="toggleChecked"
<n-checkbox v-if="selectedCell?.isAc" :checked="selectedCell.checked" :disabled="!contestStore.isContestAdmin" @update:checked="toggleChecked"
>看过了,没问题</n-checkbox
>
<span v-if="code" class="muted tiny">
{{ code.language }} · {{ parseTime(code.createTime, "M月D日 HH:mm:ss") }}
</span>
<span v-if="code" class="muted tiny"> {{ code.language }} · {{ parseTime(code.createTime, 'M月D日 HH:mm:ss') }} </span>
</div>
</template>
<div v-else class="code-empty muted">
点左边任意一格,这里看那个人那道题的代码。<br />做对的格子看完可以标「看过了」,查抄袭用。
</div>
<div v-else class="code-empty muted">点左边任意一格,这里看那个人那道题的代码。<br />做对的格子看完可以标「看过了」,查抄袭用。</div>
</section>
</div>
</div>
@@ -607,15 +485,15 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
.empty {
padding: 60px 20px;
text-align: center;
color: v-bind("theme.textColor3");
color: v-bind('theme.textColor3');
}
.muted {
color: v-bind("theme.textColor3");
color: v-bind('theme.textColor3');
}
.sec {
color: v-bind("theme.textColor2");
color: v-bind('theme.textColor2');
}
.small {
@@ -649,7 +527,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
align-items: center;
flex-wrap: wrap;
gap: 16px 36px;
border-bottom: 1px solid v-bind("theme.dividerColor");
border-bottom: 1px solid v-bind('theme.dividerColor');
}
.stat {
@@ -666,19 +544,19 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
.stat span {
font-size: var(--oj-fs-meta);
color: v-bind("theme.textColor3");
color: v-bind('theme.textColor3');
}
.stat .grey {
color: v-bind("theme.textColor3");
color: v-bind('theme.textColor3');
}
.stat .red {
color: v-bind("danger.color");
color: v-bind('danger.color');
}
.stat .amber {
color: v-bind("warning.color");
color: v-bind('warning.color');
}
.grid-wrap {
@@ -694,14 +572,14 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
padding: 0 var(--oj-pad-x);
font-size: var(--oj-fs-body);
box-sizing: border-box;
border-bottom: 1px solid v-bind("theme.dividerColor");
border-bottom: 1px solid v-bind('theme.dividerColor');
}
.th {
min-height: 50px;
font-size: var(--oj-fs-meta);
color: v-bind("theme.textColor3");
background: v-bind("theme.actionColor");
color: v-bind('theme.textColor3');
background: v-bind('theme.actionColor');
}
.ph {
@@ -712,7 +590,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
}
.ph b {
color: v-bind("theme.textColor2");
color: v-bind('theme.textColor2');
}
.ph.center {
@@ -751,24 +629,24 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
.cell.ac {
font-weight: 600;
background: v-bind("success.background");
color: v-bind("success.color");
background: v-bind('success.background');
color: v-bind('success.color');
}
.cell.no {
background: v-bind("danger.background");
color: v-bind("danger.color");
background: v-bind('danger.background');
color: v-bind('danger.color');
}
.cell.no.hot {
font-weight: 700;
box-shadow: inset 0 0 0 1px v-bind("danger.color");
box-shadow: inset 0 0 0 1px v-bind('danger.color');
}
.dot {
width: 100%;
text-align: center;
color: v-bind("theme.textColor3");
color: v-bind('theme.textColor3');
opacity: 0.5;
}
@@ -779,14 +657,14 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
align-items: center;
font-size: var(--oj-fs-meta);
font-weight: 600;
color: v-bind("theme.textColor3");
background: v-bind("theme.actionColor");
border-bottom: 1px solid v-bind("theme.dividerColor");
color: v-bind('theme.textColor3');
background: v-bind('theme.actionColor');
border-bottom: 1px solid v-bind('theme.dividerColor');
}
.red-group {
color: v-bind("danger.color");
background: v-bind("danger.background");
color: v-bind('danger.color');
background: v-bind('danger.background');
}
.flag-wrap {
@@ -805,23 +683,23 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
}
.flag.stuck {
background: v-bind("danger.background");
color: v-bind("danger.color");
background: v-bind('danger.background');
color: v-bind('danger.color');
}
.flag.idle {
background: v-bind("warning.background");
color: v-bind("warning.color");
background: v-bind('warning.background');
color: v-bind('warning.color');
}
.flag.grey {
background: v-bind("theme.actionColor");
color: v-bind("theme.textColor3");
background: v-bind('theme.actionColor');
color: v-bind('theme.textColor3');
}
.tr.absent {
color: v-bind("theme.textColor3");
background: v-bind("theme.actionColor");
color: v-bind('theme.textColor3');
background: v-bind('theme.actionColor');
}
/* ---------- 成绩 ---------- */
@@ -834,7 +712,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
align-items: center;
flex-wrap: wrap;
gap: 12px 24px;
border-bottom: 1px solid v-bind("theme.dividerColor");
border-bottom: 1px solid v-bind('theme.dividerColor');
}
.dist {
@@ -857,7 +735,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
flex-wrap: wrap;
gap: 4px 14px;
font-size: var(--oj-fs-meta);
color: v-bind("theme.textColor2");
color: v-bind('theme.textColor2');
}
.dist-legend span {
@@ -880,7 +758,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
.sheet {
flex: 1 1 560px;
min-width: 0;
border-right: 1px solid v-bind("theme.dividerColor");
border-right: 1px solid v-bind('theme.dividerColor');
}
.sort {
@@ -889,25 +767,25 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
display: flex;
align-items: center;
gap: 6px;
border-bottom: 1px solid v-bind("theme.dividerColor");
border-bottom: 1px solid v-bind('theme.dividerColor');
}
.chip {
height: 30px;
padding: 0 12px;
border-radius: 15px;
border: 1px solid v-bind("theme.borderColor");
border: 1px solid v-bind('theme.borderColor');
background: transparent;
font: inherit;
font-size: var(--oj-fs-sec);
color: v-bind("theme.textColor2");
color: v-bind('theme.textColor2');
cursor: pointer;
}
.chip.on {
border-color: transparent;
background: v-bind("success.background");
color: v-bind("success.color");
background: v-bind('success.background');
color: v-bind('success.color');
font-weight: 600;
}
@@ -929,7 +807,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
}
.tr.picked {
background: v-bind("theme.hoverColor");
background: v-bind('theme.hoverColor');
}
.gc {
@@ -940,22 +818,22 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
background: transparent;
font: inherit;
font-size: var(--oj-fs-sec);
color: v-bind("theme.textColor3");
color: v-bind('theme.textColor3');
cursor: pointer;
}
.gc.ac {
background: v-bind("success.background");
color: v-bind("success.color");
background: v-bind('success.background');
color: v-bind('success.color');
}
.gc.no {
background: v-bind("danger.background");
color: v-bind("danger.color");
background: v-bind('danger.background');
color: v-bind('danger.color');
}
.gc.sel {
box-shadow: 0 0 0 2px v-bind("theme.textColor1");
box-shadow: 0 0 0 2px v-bind('theme.textColor1');
}
.seen {
@@ -965,7 +843,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
width: 5px;
height: 5px;
border-radius: 50%;
background: v-bind("success.color");
background: v-bind('success.color');
}
.code-panel {
@@ -976,7 +854,7 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
display: flex;
flex-direction: column;
gap: 12px;
background: v-bind("theme.actionColor");
background: v-bind('theme.actionColor');
}
.code-head {
@@ -997,13 +875,13 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
}
.pill.ok {
background: v-bind("success.background");
color: v-bind("success.color");
background: v-bind('success.background');
color: v-bind('success.color');
}
.pill.bad {
background: v-bind("danger.background");
color: v-bind("danger.color");
background: v-bind('danger.background');
color: v-bind('danger.color');
}
.code-box {
@@ -1012,9 +890,9 @@ const gradeColumns = computed(() => `24px 124px 40px 36px repeat(${problems.valu
max-height: calc(100vh - 360px);
overflow: auto;
padding: 10px 12px;
border: 1px solid v-bind("theme.dividerColor");
border: 1px solid v-bind('theme.dividerColor');
border-radius: var(--oj-radius);
background: v-bind("theme.cardColor");
background: v-bind('theme.cardColor');
font-size: 15px;
}