fmt
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

This commit is contained in:
2026-07-03 09:59:15 -06:00
parent 47281029ec
commit 1cac0d01f8
10 changed files with 79 additions and 44 deletions

View File

@@ -871,7 +871,10 @@ const radarChartOptions = {
class="stat-avg-ac" class="stat-avg-ac"
> >
<template #suffix> <template #suffix>
<Icon icon="streamline-ultimate-color:analytics-pie-2" width="20" /> <Icon
icon="streamline-ultimate-color:analytics-pie-2"
width="20"
/>
</template> </template>
</n-statistic> </n-statistic>
</n-gi> </n-gi>
@@ -883,7 +886,10 @@ const radarChartOptions = {
class="stat-median-ac" class="stat-median-ac"
> >
<template #suffix> <template #suffix>
<Icon icon="streamline-ultimate-color:cursor-target-1" width="20" /> <Icon
icon="streamline-ultimate-color:cursor-target-1"
width="20"
/>
</template> </template>
</n-statistic> </n-statistic>
</n-gi> </n-gi>
@@ -895,7 +901,10 @@ const radarChartOptions = {
class="stat-total-submission" class="stat-total-submission"
> >
<template #suffix> <template #suffix>
<Icon icon="streamline-ultimate-color:common-file-text" width="20" /> <Icon
icon="streamline-ultimate-color:common-file-text"
width="20"
/>
</template> </template>
</n-statistic> </n-statistic>
</n-gi> </n-gi>

View File

@@ -1,8 +1,7 @@
import type { Exercise } from "utils/types" import type { Exercise } from "utils/types"
type Segment = type Segment =
| { type: "md"; content: string } { type: "md"; content: string } | { type: "exercise"; exercise: Exercise }
| { type: "exercise"; exercise: Exercise }
export function parseExercises( export function parseExercises(
content: string, content: string,

View File

@@ -195,13 +195,6 @@ onMounted(() => {
/> />
</template> </template>
<IconButton
v-if="isDesktop && userStore.isSuperAdmin"
icon="streamline-ultimate-color:common-file-edit"
tip="编辑题目"
@click="goEdit"
/>
<template v-if="showSyncFeature"> <template v-if="showSyncFeature">
<IconButton <IconButton
:icon=" :icon="
@@ -231,6 +224,13 @@ onMounted(() => {
</n-tag> </n-tag>
</template> </template>
</template> </template>
<IconButton
v-if="isDesktop && userStore.isSuperAdmin"
icon="streamline-ultimate-color:common-file-edit"
tip="编辑题目"
@click="goEdit"
/>
</n-flex> </n-flex>
<n-modal <n-modal

View File

@@ -77,10 +77,7 @@ const columns: DataTableColumn<Submission>[] = [
render: (row) => h(SubmissionResultTag, { result: row.result }), render: (row) => h(SubmissionResultTag, { result: row.result }),
}, },
{ {
title: renderTableTitle( title: renderTableTitle("语言", "streamline-ultimate-color:earth-pin-2"),
"语言",
"streamline-ultimate-color:earth-pin-2",
),
key: "language", key: "language",
width: 100, width: 100,
render: (row) => LANGUAGE_SHOW_VALUE[row.language], render: (row) => LANGUAGE_SHOW_VALUE[row.language],

View File

@@ -160,12 +160,18 @@ const baseColumns: DataTableColumn<ProblemFiltered>[] = [
render: (row) => h(ProblemStatus, { status: row.status }), render: (row) => h(ProblemStatus, { status: row.status }),
}, },
{ {
title: renderTableTitle("编号", "streamline-ultimate-color:board-game-dice-1"), title: renderTableTitle(
"编号",
"streamline-ultimate-color:board-game-dice-1",
),
key: "_id", key: "_id",
width: 100, width: 100,
}, },
{ {
title: renderTableTitle("题目", "streamline-ultimate-color:fruit-watermelon"), title: renderTableTitle(
"题目",
"streamline-ultimate-color:fruit-watermelon",
),
key: "title", key: "title",
minWidth: 200, minWidth: 200,
render: (row) => h(ProblemListTitle, { problem: row }), render: (row) => h(ProblemListTitle, { problem: row }),

View File

@@ -110,7 +110,11 @@ async function analyzeSingleClassWithAI() {
if (event === "end" && !hasStarted) classDetailAiLoading.value = false if (event === "end" && !hasStarted) classDetailAiLoading.value = false
}, },
onMessage(payload) { onMessage(payload) {
const parsed = payload as { type?: string; content?: string; message?: string } const parsed = payload as {
type?: string
content?: string
message?: string
}
if (parsed.type === "delta" && parsed.content) { if (parsed.type === "delta" && parsed.content) {
if (!hasStarted) { if (!hasStarted) {
hasStarted = true hasStarted = true
@@ -221,7 +225,10 @@ const columns: DataTableColumn<Rank>[] = [
align: "center", align: "center",
}, },
{ {
title: renderTableTitle("提交数", "streamline-ultimate-color:space-rocket-earth"), title: renderTableTitle(
"提交数",
"streamline-ultimate-color:space-rocket-earth",
),
key: "submission_number", key: "submission_number",
width: 120, width: 120,
align: "center", align: "center",
@@ -606,7 +613,10 @@ watch(
class="stat-avg-ac" class="stat-avg-ac"
> >
<template #suffix> <template #suffix>
<Icon icon="streamline-ultimate-color:analytics-pie-2" width="20" /> <Icon
icon="streamline-ultimate-color:analytics-pie-2"
width="20"
/>
</template> </template>
</n-statistic> </n-statistic>
</n-gi> </n-gi>
@@ -618,7 +628,10 @@ watch(
class="stat-median-ac" class="stat-median-ac"
> >
<template #suffix> <template #suffix>
<Icon icon="streamline-ultimate-color:cursor-target-1" width="20" /> <Icon
icon="streamline-ultimate-color:cursor-target-1"
width="20"
/>
</template> </template>
</n-statistic> </n-statistic>
</n-gi> </n-gi>
@@ -630,7 +643,10 @@ watch(
class="stat-total-submission" class="stat-total-submission"
> >
<template #suffix> <template #suffix>
<Icon icon="streamline-ultimate-color:common-file-text" width="20" /> <Icon
icon="streamline-ultimate-color:common-file-text"
width="20"
/>
</template> </template>
</n-statistic> </n-statistic>
</n-gi> </n-gi>
@@ -736,7 +752,12 @@ watch(
</n-space> </n-space>
</n-card> </n-card>
<n-flex justify="center" align="center" :size="12" style="margin-top: 12px"> <n-flex
justify="center"
align="center"
:size="12"
style="margin-top: 12px"
>
<n-tag type="success" size="large"> <n-tag type="success" size="large">
综合分: {{ classDetailData.composite_score.toFixed(1) }} 综合分: {{ classDetailData.composite_score.toFixed(1) }}
</n-tag> </n-tag>
@@ -769,7 +790,10 @@ watch(
> >
<n-spin :show="classDetailAiLoading" :delay="50"> <n-spin :show="classDetailAiLoading" :delay="50">
<div style="min-height: 200px"> <div style="min-height: 200px">
<MdPreview v-if="classDetailAiContent" :model-value="classDetailAiContent" /> <MdPreview
v-if="classDetailAiContent"
:model-value="classDetailAiContent"
/>
<n-flex <n-flex
v-else-if="!classDetailAiLoading" v-else-if="!classDetailAiLoading"
align="center" align="center"

View File

@@ -267,10 +267,7 @@ const columns = computed(() => {
), ),
}, },
{ {
title: renderTableTitle( title: renderTableTitle("语言", "streamline-ultimate-color:earth-pin-2"),
"语言",
"streamline-ultimate-color:earth-pin-2",
),
key: "language", key: "language",
minWidth: 120, minWidth: 120,
render: (row) => LANGUAGE_SHOW_VALUE[row.language], render: (row) => LANGUAGE_SHOW_VALUE[row.language],
@@ -347,7 +344,10 @@ const flowchartColumns = computed(() => {
), ),
}, },
{ {
title: renderTableTitle("评分", "streamline-ultimate-color:analytics-bars-3d"), title: renderTableTitle(
"评分",
"streamline-ultimate-color:analytics-bars-3d",
),
key: "ai_score", key: "ai_score",
render: (row) => h(Grade, { score: row.ai_score, grade: row.ai_grade }), render: (row) => h(Grade, { score: row.ai_score, grade: row.ai_grade }),
}, },

View File

@@ -7,7 +7,11 @@ interface Props {
page: number page: number
} }
const { total, limit: initialLimit = 10, page: initialPage = 1 } = defineProps<Props>() const {
total,
limit: initialLimit = 10,
page: initialPage = 1,
} = defineProps<Props>()
const emit = defineEmits(["update:limit", "update:page"]) const emit = defineEmits(["update:limit", "update:page"])

View File

@@ -23,27 +23,26 @@ export const SYNC_ERROR_CODES = {
// 界面和通知文案 // 界面和通知文案
export const SYNC_MESSAGES = { export const SYNC_MESSAGES = {
// 超管离开 // 超管离开
SUPER_ADMIN_LEFT: (name: string) => `👋 超管 ${name} 已离开`, SUPER_ADMIN_LEFT: (name: string) => `超管 ${name} 已离开`,
// 缺少超管 // 缺少超管
MISSING_SUPER_ADMIN: "⚠️ 协同编辑需要超管", MISSING_SUPER_ADMIN: "协同编辑需要超管",
// 连接成功 // 连接成功
SYNC_ACTIVE: "协同编辑已激活!", SYNC_ACTIVE: "协同编辑已激活!",
// 连接断开 // 连接断开
CONNECTION_LOST: "⚠️ 协同编辑已断开", CONNECTION_LOST: "协同编辑已断开",
// 等待相关 // 等待相关
WAITING_STUDENT: "正在等待学生加入...", WAITING_STUDENT: "正在等待学生加入...",
WAITING_ADMIN: "正在等待超管加入...", WAITING_ADMIN: "正在等待超管加入...",
// Form.vue 界面文案 // Form.vue 界面文案
SYNC_ON: "断开同步", SYNC_ON: "断开同步",
SYNC_OFF: "开启同步", SYNC_OFF: "开启同步",
SYNCING_WITH: (name: string) => `🔗 ${name} 同步中`, SYNCING_WITH: (name: string) => `${name} 同步中`,
STUDENT_LEFT: (name?: string) => STUDENT_LEFT: (name?: string) => (name ? `${name}已离开` : "可以关闭同步"),
name ? `💡 ${name}已离开` : "💡 可以关闭同步",
} as const } as const
// 类型定义 // 类型定义

View File

@@ -4,10 +4,7 @@ import { ref, onUnmounted, type Ref } from "vue"
* WebSocket 连接状态 * WebSocket 连接状态
*/ */
export type ConnectionStatus = export type ConnectionStatus =
| "disconnected" "disconnected" | "connecting" | "connected" | "error"
| "connecting"
| "connected"
| "error"
/** /**
* WebSocket 消息类型 * WebSocket 消息类型