- classLabel / groupClassOptions / classSelectOptions 挪到 utils/classes.ts,useTone 挪到 shared/composables/tone.ts:原来放在 oj/submission/ 下,被前台、后台、shared 引了 50 多处 - 流程图编辑器的草稿键改由题目页传进来(键名不变,学生存着的草稿照样读得到)。原来编辑器 自己读题目页的 store 和路由拼,后台出题页也用这个组件,那边 store 是空的 - SQL 表名补全改由题目页传表进来。原来补全直接读题目页的 store,自学页的 SQL 代码块会 补出上一道看过的题的表名 实跑:提交列表、题目页、排名、后台题目页、SQL 题、流程图题打开无报错;SQL 题输入 stu 补出 students;流程图加一步,草稿照旧存在 flowchart-v2-problem-<题号>。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
465dbfd42f
commit
309ca0ece2
59 files changed
+168
-142
No files matched your search
@@ -2,7 +2,7 @@
|
|||||||
import { Icon } from "@iconify/vue"
|
import { Icon } from "@iconify/vue"
|
||||||
import PageHeader from "admin/components/PageHeader.vue"
|
import PageHeader from "admin/components/PageHeader.vue"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { useBackClose } from "shared/composables/backClose"
|
import { useBackClose } from "shared/composables/backClose"
|
||||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||||
import Pagination from "shared/components/Pagination.vue"
|
import Pagination from "shared/components/Pagination.vue"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getLearnBoard, getLearnExercises } from "admin/api"
|
|||||||
import { parseTime, readableDuration } from "utils/functions"
|
import { parseTime, readableDuration } from "utils/functions"
|
||||||
import { useConfigStore } from "shared/store/config"
|
import { useConfigStore } from "shared/store/config"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { groupClassOptions } from "oj/submission/utils"
|
import { groupClassOptions } from "utils/classes"
|
||||||
import { groupByProject, splitTitle, type TutorialType } from "oj/learn/lesson"
|
import { groupByProject, splitTitle, type TutorialType } from "oj/learn/lesson"
|
||||||
import "oj/learn/learn.css"
|
import "oj/learn/learn.css"
|
||||||
import ExerciseAttempts from "./ExerciseAttempts.vue"
|
import ExerciseAttempts from "./ExerciseAttempts.vue"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { StuckProblem, StuckProblemDetail, StuckStudent } from "utils/types
|
|||||||
import { getStuckProblemDetail, getStuckProblems } from "admin/api"
|
import { getStuckProblemDetail, getStuckProblems } from "admin/api"
|
||||||
import { useConfigStore } from "shared/store/config"
|
import { useConfigStore } from "shared/store/config"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { classLabel, groupClassOptions } from "oj/submission/utils"
|
import { classLabel, groupClassOptions } from "utils/classes"
|
||||||
import { periodRange, type Period } from "oj/statistics/period"
|
import { periodRange, type Period } from "oj/statistics/period"
|
||||||
import { resultName, useResultColor } from "oj/statistics/results"
|
import { resultName, useResultColor } from "oj/statistics/results"
|
||||||
import { useBackClose } from "shared/composables/backClose"
|
import { useBackClose } from "shared/composables/backClose"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PageHeader from "admin/components/PageHeader.vue"
|
import PageHeader from "admin/components/PageHeader.vue"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { errorMessage } from "utils/api"
|
import { errorMessage } from "utils/api"
|
||||||
import { CLASS_NAME_MAX_DIGITS, CLASS_NAME_MIN_DIGITS, CLASS_NAME_RE } from "utils/constants"
|
import { CLASS_NAME_MAX_DIGITS, CLASS_NAME_MIN_DIGITS, CLASS_NAME_RE } from "utils/constants"
|
||||||
import { copyToClipboard, parseTime } from "utils/functions"
|
import { copyToClipboard, parseTime } from "utils/functions"
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { BarElement, CategoryScale, Chart as ChartJS, LinearScale, Tooltip } fro
|
|||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { Bar } from "vue-chartjs"
|
import { Bar } from "vue-chartjs"
|
||||||
import { useChartTheme } from "shared/composables/chartTheme"
|
import { useChartTheme } from "shared/composables/chartTheme"
|
||||||
import { rgba } from "oj/submission/composables/tone"
|
import { rgba } from "shared/composables/tone"
|
||||||
import PageHeader from "admin/components/PageHeader.vue"
|
import PageHeader from "admin/components/PageHeader.vue"
|
||||||
import { classLabel, groupClassOptions } from "oj/submission/utils"
|
import { classLabel, groupClassOptions } from "utils/classes"
|
||||||
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 { SubmissionStatus } from "utils/constants"
|
import { SubmissionStatus } from "utils/constants"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { downloadAccountsCsv } from "./accountsCsv"
|
import { downloadAccountsCsv } from "./accountsCsv"
|
||||||
import PageHeader from "admin/components/PageHeader.vue"
|
import PageHeader from "admin/components/PageHeader.vue"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { errorMessage } from "utils/api"
|
import { errorMessage } from "utils/api"
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { PROBLEM_PERMISSION, USER_TYPE } from "utils/constants"
|
|||||||
import { useRouteQuery } from "@vueuse/router"
|
import { useRouteQuery } from "@vueuse/router"
|
||||||
import { NO_CLASS, STUDENT_ROLES } from "@oj2/contract"
|
import { NO_CLASS, STUDENT_ROLES } from "@oj2/contract"
|
||||||
import { downloadAccountsCsv } from "./accountsCsv"
|
import { downloadAccountsCsv } from "./accountsCsv"
|
||||||
import { classLabel, groupClassOptions } from "oj/submission/utils"
|
import { classLabel, groupClassOptions } from "utils/classes"
|
||||||
import { useConfigStore } from "shared/store/config"
|
import { useConfigStore } from "shared/store/config"
|
||||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useBreakpoints } from "shared/composables/breakpoints"
|
|||||||
import AdminLink from "shared/components/AdminLink.vue"
|
import AdminLink from "shared/components/AdminLink.vue"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import { getLearningReport } from "../api"
|
import { getLearningReport } from "../api"
|
||||||
import AiSummary from "./components/AiSummary.vue"
|
import AiSummary from "./components/AiSummary.vue"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { LearningDay } from "@oj2/contract"
|
import type { LearningDay } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import ProblemChip from "./ProblemChip.vue"
|
import ProblemChip from "./ProblemChip.vue"
|
||||||
import { dayLabel, isDone } from "../utils"
|
import { dayLabel, isDone } from "../utils"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ClassPk, ClassPkCell } from "@oj2/contract"
|
import type { ClassPk, ClassPkCell } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import { needToTie, pkColor, plateau, shortLabels, tint } from "../utils"
|
import { needToTie, pkColor, plateau, shortLabels, tint } from "../utils"
|
||||||
import PkHist from "./PkHist.vue"
|
import PkHist from "./PkHist.vue"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ClassPk } from "@oj2/contract"
|
import type { ClassPk } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { useRankPalette } from "oj/rank/palette"
|
import { useRankPalette } from "oj/rank/palette"
|
||||||
import { needToTie, pkColor, shortLabels, tint } from "../utils"
|
import { needToTie, pkColor, shortLabels, tint } from "../utils"
|
||||||
import PkRace from "./PkRace.vue"
|
import PkRace from "./PkRace.vue"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { ClassBattleItem, ClassPk, ClassPkPeriod } from "@oj2/contract"
|
import type { ClassBattleItem, ClassPk, ClassPkPeriod } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getClassBattle, getClassPk } from "oj/api"
|
import { getClassBattle, getClassPk } from "oj/api"
|
||||||
import { classLabel, groupClassOptions } from "oj/submission/utils"
|
import { classLabel, groupClassOptions } from "utils/classes"
|
||||||
import { useAIStream } from "shared/composables/aiStream"
|
import { useAIStream } from "shared/composables/aiStream"
|
||||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ClassPk, ClassPkCell } from "@oj2/contract"
|
import type { ClassPk, ClassPkCell } from "@oj2/contract"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每个班一种颜色,按选班的顺序给。蓝 / 橙打头:两个班对决时最常见,这两个色盲也分得开;
|
* 每个班一种颜色,按选班的顺序给。蓝 / 橙打头:两个班对决时最常见,这两个色盲也分得开;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import { Icon } from "@iconify/vue"
|
import { Icon } from "@iconify/vue"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getClassBoard, setClassLesson } from "oj/api"
|
import { getClassBoard, setClassLesson } from "oj/api"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { groupClassOptions } from "oj/submission/utils"
|
import { groupClassOptions } from "utils/classes"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { useLeaveStudents } from "shared/composables/leaveStudents"
|
import { useLeaveStudents } from "shared/composables/leaveStudents"
|
||||||
import { useTeacherHelp } from "shared/composables/teacherHelp"
|
import { useTeacherHelp } from "shared/composables/teacherHelp"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { isExamTag } from "@oj2/contract"
|
import { isExamTag } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { CONTEST_STATUS, ContestStatus } from "utils/constants"
|
import { CONTEST_STATUS, ContestStatus } from "utils/constants"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ContestScoreRow } from "@oj2/contract"
|
import type { ContestScoreRow } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { secondsToDuration } from "utils/functions"
|
import { secondsToDuration } from "utils/functions"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { ContestStatus } from "utils/constants"
|
import { ContestStatus } from "utils/constants"
|
||||||
import { parseTime, secondsToDuration } from "utils/functions"
|
import { parseTime, secondsToDuration } from "utils/functions"
|
||||||
import NotFound from "shared/components/NotFound.vue"
|
import NotFound from "shared/components/NotFound.vue"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { useRouteQuery } from "@vueuse/router"
|
import { useRouteQuery } from "@vueuse/router"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getContestList } from "oj/api"
|
import { getContestList } from "oj/api"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { contestLength, parseTime, secondsToDuration } from "utils/functions"
|
import { contestLength, parseTime, secondsToDuration } from "utils/functions"
|
||||||
import type { Contest } from "utils/types"
|
import type { Contest } from "utils/types"
|
||||||
import ContestTitle from "shared/components/ContestTitle.vue"
|
import ContestTitle from "shared/components/ContestTitle.vue"
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { updateACMHelperChecked } from "admin/api"
|
|||||||
import { getContestClassView, getSubmission, getSubmissions } from "oj/api"
|
import { getContestClassView, getSubmission, getSubmissions } from "oj/api"
|
||||||
import { useContestStore } from "oj/store/contest"
|
import { useContestStore } from "oj/store/contest"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { useLeaveStudents } from "shared/composables/leaveStudents"
|
import { useLeaveStudents } from "shared/composables/leaveStudents"
|
||||||
import { ContestStatus, LANGUAGE_FORMAT_VALUE } from "utils/constants"
|
import { ContestStatus, LANGUAGE_FORMAT_VALUE } from "utils/constants"
|
||||||
import { parseTime, secondsToDuration } from "utils/functions"
|
import { parseTime, secondsToDuration } from "utils/functions"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { ContestScoreRow } from "@oj2/contract"
|
import type { ContestScoreRow } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useContestStore } from "oj/store/contest"
|
import { useContestStore } from "oj/store/contest"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
import { ContestStatus } from "utils/constants"
|
import { ContestStatus } from "utils/constants"
|
||||||
import { parseTime, secondsToDuration } from "utils/functions"
|
import { parseTime, secondsToDuration } from "utils/functions"
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
import type { ContestScoreCell, ContestScoreRow } from "@oj2/contract"
|
import type { ContestScoreCell, ContestScoreRow } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useContestStore } from "oj/store/contest"
|
import { useContestStore } from "oj/store/contest"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
import { ContestStatus } from "utils/constants"
|
import { ContestStatus } from "utils/constants"
|
||||||
import { secondsToDuration } from "utils/functions"
|
import { secondsToDuration } from "utils/functions"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { useVisibleInterval } from "shared/composables/visibleInterval"
|
import { useVisibleInterval } from "shared/composables/visibleInterval"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { ClassToday } from "@oj2/contract"
|
|||||||
import type { AnnouncementListItem } from "utils/types"
|
import type { AnnouncementListItem } from "utils/types"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getAnnouncementList, getClassToday } from "oj/api"
|
import { getAnnouncementList, getClassToday } from "oj/api"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
import { parseTime, zonedParts, zonedYear } from "utils/functions"
|
import { parseTime, zonedParts, zonedYear } from "utils/functions"
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,19 @@ onMounted(loadCode)
|
|||||||
|
|
||||||
watch(() => problem.value?._id, loadCode)
|
watch(() => problem.value?._id, loadCode)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程图草稿:每道题一份;题单入口单独一份 —— 加入题单之前在题库里画到 A 的那张,不能一进
|
||||||
|
* 题单就摆在画布上(设计文档第 11 节 3,和代码草稿同理)。
|
||||||
|
* 键名带 v2:2026-10 之前的草稿是 vue-flow 的格式,读不了,也不去覆盖它
|
||||||
|
*/
|
||||||
|
const flowchartDraftKey = computed(() => {
|
||||||
|
const id = problem.value?._id
|
||||||
|
if (!id) return "flowchart-v2"
|
||||||
|
const base = `flowchart-v2-problem-${id}`
|
||||||
|
const problemSetId = route.params.problemSetId
|
||||||
|
return problemSetId ? `${base}-problemset-${problemSetId}` : base
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 存本地草稿。**协作中的教师不存** —— 那会儿编辑器里是学生的代码,存下去就把
|
* 存本地草稿。**协作中的教师不存** —— 那会儿编辑器里是学生的代码,存下去就把
|
||||||
* 老师自己在这道题上的草稿盖掉了(他可能正开着自己的解法)。学生照存,那是他本人的。
|
* 老师自己在这道题上的草稿盖掉了(他可能正开着自己的解法)。学生照存,那是他本人的。
|
||||||
@@ -219,6 +232,7 @@ onBeforeUnmount(() => flowchartStore.attachEditor(null))
|
|||||||
v-if="codeStore.code.language === 'Flowchart'"
|
v-if="codeStore.code.language === 'Flowchart'"
|
||||||
ref="flowchartEditorRef"
|
ref="flowchartEditorRef"
|
||||||
:height="editorHeight"
|
:height="editorHeight"
|
||||||
|
:draft-key="flowchartDraftKey"
|
||||||
/>
|
/>
|
||||||
<SyncCodeEditor
|
<SyncCodeEditor
|
||||||
v-else
|
v-else
|
||||||
@@ -226,6 +240,7 @@ onBeforeUnmount(() => flowchartStore.attachEditor(null))
|
|||||||
:language="codeStore.code.language"
|
:language="codeStore.code.language"
|
||||||
:problem-id="problem!._id"
|
:problem-id="problem!._id"
|
||||||
:collab="ctx.collab"
|
:collab="ctx.collab"
|
||||||
|
:sql-tables="problem?.sqlDisplay?.tables"
|
||||||
:height="editorHeight"
|
:height="editorHeight"
|
||||||
:extra-extensions="editorExtensions"
|
:extra-extensions="editorExtensions"
|
||||||
:font-size="isDesktop ? 20 : 15"
|
:font-size="isDesktop ? 20 : 15"
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import { storeToRefs } from "pinia"
|
|||||||
import { getProblemStats } from "oj/api"
|
import { getProblemStats } from "oj/api"
|
||||||
import { useContestStore } from "oj/store/contest"
|
import { useContestStore } from "oj/store/contest"
|
||||||
import { useProblemStore } from "oj/store/problem"
|
import { useProblemStore } from "oj/store/problem"
|
||||||
import { useTone, type Tone } from "oj/submission/composables/tone"
|
import { useTone, type Tone } from "shared/composables/tone"
|
||||||
import { classLabel, submissionDayText } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
|
import { submissionDayText } from "oj/submission/utils"
|
||||||
import { DIFFICULTY, JUDGE_STATUS } from "utils/constants"
|
import { DIFFICULTY, JUDGE_STATUS } from "utils/constants"
|
||||||
import { getTagColor, parseTime } from "utils/functions"
|
import { getTagColor, parseTime } from "utils/functions"
|
||||||
import type { ProblemStats, SUBMISSION_RESULT } from "utils/types"
|
import type { ProblemStats, SUBMISSION_RESULT } from "utils/types"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Icon } from "@iconify/vue"
|
|||||||
import type { ProblemProgress } from "@oj2/contract"
|
import type { ProblemProgress } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import AchievementIcon from "shared/components/AchievementIcon.vue"
|
import AchievementIcon from "shared/components/AchievementIcon.vue"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 题目列表顶上那行的左半边:先说做到了什么(设计稿「题目列表重设计 · 改版」)。
|
* 题目列表顶上那行的左半边:先说做到了什么(设计稿「题目列表重设计 · 改版」)。
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { Icon } from "@iconify/vue"
|
import { Icon } from "@iconify/vue"
|
||||||
import type { Tag, TagCategory } from "@oj2/contract"
|
import type { Tag, TagCategory } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 题目列表左栏:知识点 / 主题,带自己的进度(设计稿「题目列表重设计 · 改版」)。
|
* 题目列表左栏:知识点 / 主题,带自己的进度(设计稿「题目列表重设计 · 改版」)。
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useRouteQuery } from "@vueuse/router"
|
|||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getAuthors, getProblemList, getProblemProgress, getRandomProblem } from "oj/api"
|
import { getAuthors, getProblemList, getProblemProgress, getRandomProblem } from "oj/api"
|
||||||
import { useCodeStore } from "oj/store/code"
|
import { useCodeStore } from "oj/store/code"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { STORAGE_KEY } from "utils/constants"
|
import { STORAGE_KEY } from "utils/constants"
|
||||||
import storage from "utils/storage"
|
import storage from "utils/storage"
|
||||||
import type { ProblemRow } from "utils/types"
|
import type { ProblemRow } from "utils/types"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import type { ProblemSet } from "utils/types"
|
import type { ProblemSet } from "utils/types"
|
||||||
import { badgeCondition, badgeShortfall, ladder, nextBadge } from "../badges"
|
import { badgeCondition, badgeShortfall, ladder, nextBadge } from "../badges"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getProblemSetClassView } from "oj/api"
|
import { getProblemSetClassView } from "oj/api"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classLabel, groupClassOptions } from "oj/submission/utils"
|
import { classLabel, groupClassOptions } from "utils/classes"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import type { ProblemSetClassView } from "utils/types"
|
import type { ProblemSetClassView } from "utils/types"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { useRouteQuery } from "@vueuse/router"
|
import { useRouteQuery } from "@vueuse/router"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getProblemSetDetail, getProblemSetProblems, joinProblemSet } from "oj/api"
|
import { getProblemSetDetail, getProblemSetProblems, joinProblemSet } from "oj/api"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { errorMessage } from "utils/api"
|
import { errorMessage } from "utils/api"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import type { ProblemSet, ProblemSetProblem } from "utils/types"
|
import type { ProblemSet, ProblemSetProblem } from "utils/types"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import AdminLink from "shared/components/AdminLink.vue"
|
|||||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
import { getProblemSetList } from "oj/api"
|
import { getProblemSetList } from "oj/api"
|
||||||
import { rgba, useTone } from "oj/submission/composables/tone"
|
import { rgba, useTone } from "shared/composables/tone"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import type { ProblemSet } from "utils/types"
|
import type { ProblemSet } from "utils/types"
|
||||||
import { badgeCondition, ladder, nextBadge } from "./badges"
|
import { badgeCondition, ladder, nextBadge } from "./badges"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ClassBattleItem } from "@oj2/contract"
|
import type { ClassBattleItem } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { useRankPalette } from "../palette"
|
import { useRankPalette } from "../palette"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { ClassDetail } from "@oj2/contract"
|
import type { ClassDetail } from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { getClassDetail } from "oj/api"
|
import { getClassDetail } from "oj/api"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { useAIStream } from "shared/composables/aiStream"
|
import { useAIStream } from "shared/composables/aiStream"
|
||||||
import { useBackClose } from "shared/composables/backClose"
|
import { useBackClose } from "shared/composables/backClose"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDark } from "@vueuse/core"
|
import { useDark } from "@vueuse/core"
|
||||||
import { rgba } from "oj/submission/composables/tone"
|
import { rgba } from "shared/composables/tone"
|
||||||
import { USERNAME_CLASS_RE } from "utils/constants"
|
import { USERNAME_CLASS_RE } from "utils/constants"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {
|
|||||||
} from "@oj2/contract"
|
} from "@oj2/contract"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { clearMood, getClassBattle, getRankBoard, getWeeklyChampions, setRankHidden } from "oj/api"
|
import { clearMood, getClassBattle, getRankBoard, getWeeklyChampions, setRankHidden } from "oj/api"
|
||||||
import { classLabel, groupClassOptions } from "oj/submission/utils"
|
import { classLabel, groupClassOptions } from "utils/classes"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||||
import { useConfigStore } from "shared/store/config"
|
import { useConfigStore } from "shared/store/config"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { RankPeriod, RankRow, RankScope } from "@oj2/contract"
|
import type { RankPeriod, RankRow, RankScope } from "@oj2/contract"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
|
|
||||||
export const SCOPE_OPTIONS: { value: RankScope; label: string }[] = [
|
export const SCOPE_OPTIONS: { value: RankScope; label: string }[] = [
|
||||||
{ value: "class", label: "本班" },
|
{ value: "class", label: "本班" },
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { getSubmission } from "oj/api"
|
|||||||
import { JUDGE_STATUS, LANGUAGE_FORMAT_VALUE, SubmissionStatus } from "utils/constants"
|
import { JUDGE_STATUS, LANGUAGE_FORMAT_VALUE, SubmissionStatus } from "utils/constants"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import type { SUBMISSION_RESULT } from "utils/types"
|
import type { SUBMISSION_RESULT } from "utils/types"
|
||||||
import { useTone, type Tone } from "oj/submission/composables/tone"
|
import { useTone, type Tone } from "shared/composables/tone"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一个人在一道题上的每一次提交,一格一次、从早到晚(设计稿「统计重设计」)。
|
* 一个人在一道题上的每一次提交,一格一次、从早到晚(设计稿「统计重设计」)。
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { NO_CLASS, type SubmissionStatisticsGrid } from "@oj2/contract"
|
import { NO_CLASS, type SubmissionStatisticsGrid } from "@oj2/contract"
|
||||||
import { SubmissionStatus } from "utils/constants"
|
import { SubmissionStatus } from "utils/constants"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classLabel, submissionDayText } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
|
import { submissionDayText } from "oj/submission/utils"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 没选班级、也没填学生时(多半是从题目页进来,只带了题号):先按班级列出来,
|
* 没选班级、也没填学生时(多半是从题目页进来,只带了题号):先按班级列出来,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { SubmissionStatistics, SubmissionStatisticsGrid } from "@oj2/contra
|
|||||||
import { SubmissionStatus } from "utils/constants"
|
import { SubmissionStatus } from "utils/constants"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import StatusPill from "oj/submission/components/StatusPill.vue"
|
import StatusPill from "oj/submission/components/StatusPill.vue"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import AttemptSquares from "./AttemptSquares.vue"
|
import AttemptSquares from "./AttemptSquares.vue"
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { Bar, Doughnut, Radar } from "vue-chartjs"
|
|||||||
import type { FlowchartStatistics } from "@oj2/contract"
|
import type { FlowchartStatistics } from "@oj2/contract"
|
||||||
import { useChartTheme } from "shared/composables/chartTheme"
|
import { useChartTheme } from "shared/composables/chartTheme"
|
||||||
import { FLOWCHART_CRITERIA_ORDER } from "utils/constants"
|
import { FLOWCHART_CRITERIA_ORDER } from "utils/constants"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import RingChart from "./RingChart.vue"
|
import RingChart from "./RingChart.vue"
|
||||||
import StatItem from "./StatItem.vue"
|
import StatItem from "./StatItem.vue"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { Icon } from "@iconify/vue"
|
|||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import type { SubmissionLessons } from "@oj2/contract"
|
import type { SubmissionLessons } from "@oj2/contract"
|
||||||
import { parseTime, zonedParts } from "utils/functions"
|
import { parseTime, zonedParts } from "utils/functions"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import { resultName, useResultColor } from "../results"
|
import { resultName, useResultColor } from "../results"
|
||||||
|
|
||||||
type Lesson = SubmissionLessons["lessons"][number]
|
type Lesson = SubmissionLessons["lessons"][number]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { useTone, type Tone } from "oj/submission/composables/tone"
|
import { useTone, type Tone } from "shared/composables/tone"
|
||||||
|
|
||||||
/** 数字行的一格:小字标签 + 大号数字 + 单位 */
|
/** 数字行的一格:小字标签 + 大号数字 + 单位 */
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import {
|
|||||||
} from "oj/api"
|
} from "oj/api"
|
||||||
import { useConfigStore } from "shared/store/config"
|
import { useConfigStore } from "shared/store/config"
|
||||||
import { fromPickerValue, toPickerValue } from "utils/functions"
|
import { fromPickerValue, toPickerValue } from "utils/functions"
|
||||||
import { useTone } from "oj/submission/composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classSelectOptions } from "oj/submission/utils"
|
import { classSelectOptions } from "utils/classes"
|
||||||
import { DEFAULT_PERIOD, PERIOD_OPTIONS, dayStartOfDate, periodRange, type Period } from "../period"
|
import { DEFAULT_PERIOD, PERIOD_OPTIONS, dayStartOfDate, periodRange, type Period } from "../period"
|
||||||
import { resultName, useResultColor } from "../results"
|
import { resultName, useResultColor } from "../results"
|
||||||
import ByClass from "./ByClass.vue"
|
import ByClass from "./ByClass.vue"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { JUDGE_STATUS } from "utils/constants"
|
import { JUDGE_STATUS } from "utils/constants"
|
||||||
import type { SUBMISSION_RESULT } from "utils/types"
|
import type { SUBMISSION_RESULT } from "utils/types"
|
||||||
import { useTone, type Tone } from "oj/submission/composables/tone"
|
import { useTone, type Tone } from "shared/composables/tone"
|
||||||
|
|
||||||
export function resultName(result: number) {
|
export function resultName(result: number) {
|
||||||
return JUDGE_STATUS[result as SUBMISSION_RESULT]?.name ?? "其他"
|
return JUDGE_STATUS[result as SUBMISSION_RESULT]?.name ?? "其他"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useMermaid } from "shared/composables/useMermaid"
|
|||||||
import { sortFlowchartCriteria } from "utils/constants"
|
import { sortFlowchartCriteria } from "utils/constants"
|
||||||
import type { FlowchartSubmission, FlowchartSubmissionListItem } from "utils/types"
|
import type { FlowchartSubmission, FlowchartSubmissionListItem } from "utils/types"
|
||||||
import { FlowchartSubmissionStatus } from "utils/types"
|
import { FlowchartSubmissionStatus } from "utils/types"
|
||||||
import { useTone } from "../composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { submissionTimeText } from "../utils"
|
import { submissionTimeText } from "../utils"
|
||||||
import FlowchartState from "./FlowchartState.vue"
|
import FlowchartState from "./FlowchartState.vue"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FlowchartSubmissionStatus } from "utils/types"
|
import { FlowchartSubmissionStatus } from "utils/types"
|
||||||
import { useTone, type Tone } from "../composables/tone"
|
import { useTone, type Tone } from "shared/composables/tone"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程图提交在列表里的那个胶囊:评完的直接是「A 级」,没评完的是
|
* 流程图提交在列表里的那个胶囊:评完的直接是「A 级」,没评完的是
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { Icon } from "@iconify/vue"
|
import { Icon } from "@iconify/vue"
|
||||||
import { JUDGE_STATUS, SubmissionStatus } from "utils/constants"
|
import { JUDGE_STATUS, SubmissionStatus } from "utils/constants"
|
||||||
import type { SUBMISSION_RESULT } from "utils/types"
|
import type { SUBMISSION_RESULT } from "utils/types"
|
||||||
import { useTone, type Tone } from "../composables/tone"
|
import { useTone, type Tone } from "shared/composables/tone"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
result: SUBMISSION_RESULT
|
result: SUBMISSION_RESULT
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ import { submissionCaseResults } from "utils/functions"
|
|||||||
import type { Submission, SubmissionListItem } from "utils/types"
|
import type { Submission, SubmissionListItem } from "utils/types"
|
||||||
import { useCopySubmission } from "../composables/copySubmission"
|
import { useCopySubmission } from "../composables/copySubmission"
|
||||||
import { useSubmissionExplain } from "../composables/explain"
|
import { useSubmissionExplain } from "../composables/explain"
|
||||||
import { useTone } from "../composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classLabel, submissionTimeText } from "../utils"
|
import { classLabel } from "utils/classes"
|
||||||
|
import { submissionTimeText } from "../utils"
|
||||||
import CodeView from "./CodeView.vue"
|
import CodeView from "./CodeView.vue"
|
||||||
import ExplainNote from "./ExplainNote.vue"
|
import ExplainNote from "./ExplainNote.vue"
|
||||||
import StatusPill from "./StatusPill.vue"
|
import StatusPill from "./StatusPill.vue"
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ import { parseTime, submissionCaseResults } from "utils/functions"
|
|||||||
import type { Submission } from "utils/types"
|
import type { Submission } from "utils/types"
|
||||||
import { useCopySubmission } from "../composables/copySubmission"
|
import { useCopySubmission } from "../composables/copySubmission"
|
||||||
import { useSubmissionExplain } from "../composables/explain"
|
import { useSubmissionExplain } from "../composables/explain"
|
||||||
import { useTone } from "../composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classLabel, submissionTimeText } from "../utils"
|
import { classLabel } from "utils/classes"
|
||||||
|
import { submissionTimeText } from "../utils"
|
||||||
import CodeView from "./CodeView.vue"
|
import CodeView from "./CodeView.vue"
|
||||||
import ExplainNote from "./ExplainNote.vue"
|
import ExplainNote from "./ExplainNote.vue"
|
||||||
import StatusPill from "./StatusPill.vue"
|
import StatusPill from "./StatusPill.vue"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { explainPythonCompileError } from "oj/problem/utils/pythonError"
|
|||||||
import { explainRuntimeError } from "oj/problem/utils/runtimeError"
|
import { explainRuntimeError } from "oj/problem/utils/runtimeError"
|
||||||
import { SubmissionStatus } from "utils/constants"
|
import { SubmissionStatus } from "utils/constants"
|
||||||
import type { Submission } from "utils/types"
|
import type { Submission } from "utils/types"
|
||||||
import { useTone } from "./tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
|
|
||||||
export interface SubmissionExplain {
|
export interface SubmissionExplain {
|
||||||
/** 错在第几行(编辑器里那一行标红),说不出来的为 null */
|
/** 错在第几行(编辑器里那一行标红),说不出来的为 null */
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ import { FlowchartSubmissionStatus } from "utils/types"
|
|||||||
import FlowchartState from "./components/FlowchartState.vue"
|
import FlowchartState from "./components/FlowchartState.vue"
|
||||||
import StatusPill from "./components/StatusPill.vue"
|
import StatusPill from "./components/StatusPill.vue"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { useTone } from "./composables/tone"
|
import { useTone } from "shared/composables/tone"
|
||||||
import { classLabel, classSelectOptions, submissionClockText, submissionDayText } from "./utils"
|
import { classLabel, classSelectOptions } from "utils/classes"
|
||||||
|
import { submissionClockText, submissionDayText } from "./utils"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交列表(设计稿「提交列表重设计 · 定稿」):左边列表、右边常驻选中那条的代码。
|
* 提交列表(设计稿「提交列表重设计 · 定稿」):左边列表、右边常驻选中那条的代码。
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { NO_CLASS } from "@oj2/contract"
|
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,57 +32,3 @@ export function submissionDayText(time: string) {
|
|||||||
if (day.slice(0, 4) === now.slice(0, 4)) return parseTime(time, "M月D日")
|
if (day.slice(0, 4) === now.slice(0, 4)) return parseTime(time, "M月D日")
|
||||||
return parseTime(time, "YYYY年M月D日")
|
return parseTime(time, "YYYY年M月D日")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 班级下拉的标签:`253` → `25计算机3班`,和统计面板、班级 PK 同一个写法。
|
|
||||||
* null(接口里没填班级的号)和 NO_CLASS(筛选里的「没填班级」)都写成「没填班级」
|
|
||||||
*/
|
|
||||||
export function classLabel(className: string | null) {
|
|
||||||
if (className === null || className === NO_CLASS) return "没填班级"
|
|
||||||
if (!/^\d{3,}$/.test(className)) return className
|
|
||||||
return `${className.slice(0, 2)}计算机${className.slice(2)}班`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClassOption = { label: string; value: string }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 全站的班级下拉都按年级分组:「25 级」底下列 251、252……,新的年级在上,组里按班号排
|
|
||||||
* (11 班排在 2 班后面)。年级是班号前两位,和排名的「本年级」同一个口径。
|
|
||||||
* `first` 给了就把那个年级提到最前(班级 PK 加班时先给同年级的)。
|
|
||||||
* `toOption` 换掉选项长相(题单的「n/m 人加入」、登录框的 ks 前缀);不是数字班号的
|
|
||||||
* 名字不分组,按原顺序跟在最后。
|
|
||||||
*/
|
|
||||||
export function groupClassOptions<T extends ClassOption = ClassOption>(
|
|
||||||
names: string[],
|
|
||||||
toOption: (name: string) => T = (name) => ({ label: classLabel(name), value: name }) as T,
|
|
||||||
first?: string,
|
|
||||||
) {
|
|
||||||
const groups = new Map<string, string[]>()
|
|
||||||
const rest: string[] = []
|
|
||||||
for (const name of new Set(names)) {
|
|
||||||
if (!/^\d{3,}$/.test(name)) {
|
|
||||||
rest.push(name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
const grade = name.slice(0, 2)
|
|
||||||
groups.set(grade, [...(groups.get(grade) ?? []), name])
|
|
||||||
}
|
|
||||||
const grouped = [...groups]
|
|
||||||
.sort((a, b) => (a[0] === first ? -1 : b[0] === first ? 1 : b[0].localeCompare(a[0])))
|
|
||||||
.map(([grade, list]) => ({
|
|
||||||
type: "group" as const,
|
|
||||||
label: `${grade} 级`,
|
|
||||||
key: `grade-${grade}`,
|
|
||||||
children: list.sort((a, b) => a.localeCompare(b, "zh", { numeric: true })).map(toOption),
|
|
||||||
}))
|
|
||||||
return [...grouped, ...rest.map(toOption)]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 班级下拉的选项:配置里的班按年级分组,最后一项「没填班级」(这批号也要能和普通班一样
|
|
||||||
* 圈出来看)。带进来的班不在配置里(老班、手打的)也要显示得出来,不然框里是空的却在筛
|
|
||||||
*/
|
|
||||||
export function classSelectOptions(list: string[], current: string) {
|
|
||||||
const all = current && current !== NO_CLASS ? [current, ...list] : list
|
|
||||||
return [...groupClassOptions(all), { label: classLabel(NO_CLASS), value: NO_CLASS }]
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import { Icon } from "@iconify/vue"
|
|||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import { clearMood, setRankHidden, updateProfile } from "oj/api"
|
import { clearMood, setRankHidden, updateProfile } from "oj/api"
|
||||||
import RankAvatar from "oj/rank/components/RankAvatar.vue"
|
import RankAvatar from "oj/rank/components/RankAvatar.vue"
|
||||||
import { classLabel } from "oj/submission/utils"
|
import { classLabel } from "utils/classes"
|
||||||
import UserName from "shared/components/UserName.vue"
|
import UserName from "shared/components/UserName.vue"
|
||||||
import { useUserStore } from "shared/store/user"
|
import { useUserStore } from "shared/store/user"
|
||||||
import { durationToDays, parseTime } from "utils/functions"
|
import { durationToDays, parseTime } from "utils/functions"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import storage from "utils/storage"
|
import storage from "utils/storage"
|
||||||
import { useProblemStore } from "oj/store/problem"
|
|
||||||
import {
|
import {
|
||||||
cloneDoc,
|
cloneDoc,
|
||||||
emptyDoc,
|
emptyDoc,
|
||||||
@@ -35,8 +34,17 @@ interface Props {
|
|||||||
* false:图交给外面管(后台出题页的参考流程图,v-model:value),不存草稿
|
* false:图交给外面管(后台出题页的参考流程图,v-model:value),不存草稿
|
||||||
*/
|
*/
|
||||||
draft?: boolean
|
draft?: boolean
|
||||||
|
/**
|
||||||
|
* 草稿存在哪个键下,由题目页给(见 ProblemEditor.vue 的 flowchartDraftKey)。原来是这里
|
||||||
|
* 自己读题目页的 store 和路由拼出来的,后台出题页也用这个组件,那边 store 是空的
|
||||||
|
*/
|
||||||
|
draftKey?: string
|
||||||
}
|
}
|
||||||
const { height = "calc(100vh - 134px)", draft = true } = defineProps<Props>()
|
const {
|
||||||
|
height = "calc(100vh - 134px)",
|
||||||
|
draft = true,
|
||||||
|
draftKey = "flowchart-v2",
|
||||||
|
} = defineProps<Props>()
|
||||||
const model = defineModel<FlowDoc | null>("value", { default: null })
|
const model = defineModel<FlowDoc | null>("value", { default: null })
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
@@ -86,20 +94,7 @@ function redo() {
|
|||||||
|
|
||||||
// ==================== 草稿 ====================
|
// ==================== 草稿 ====================
|
||||||
|
|
||||||
const problemStore = useProblemStore()
|
const cacheKey = computed(() => draftKey)
|
||||||
const route = useRoute()
|
|
||||||
/**
|
|
||||||
* 每道题一份草稿;题单入口单独一份:加入题单之前在题库里画到 A 的那张,不能一进题单就
|
|
||||||
* 摆在画布上(设计文档第 11 节 3,和代码草稿同理)。
|
|
||||||
* 键名带 v2:2026-10 之前的草稿是 vue-flow 的格式,这里读不了,也不去覆盖它
|
|
||||||
*/
|
|
||||||
const cacheKey = computed(() => {
|
|
||||||
const id = problemStore.problem?._id
|
|
||||||
if (!id) return "flowchart-v2"
|
|
||||||
const base = `flowchart-v2-problem-${id}`
|
|
||||||
const problemSetId = route.params.problemSetId
|
|
||||||
return problemSetId ? `${base}-problemset-${problemSetId}` : base
|
|
||||||
})
|
|
||||||
|
|
||||||
const savedAt = ref<Date | null>(null)
|
const savedAt = ref<Date | null>(null)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { Icon } from "@iconify/vue"
|
import { Icon } from "@iconify/vue"
|
||||||
import { useThemeVars } from "naive-ui"
|
import { useThemeVars } from "naive-ui"
|
||||||
import RankAvatar from "oj/rank/components/RankAvatar.vue"
|
import RankAvatar from "oj/rank/components/RankAvatar.vue"
|
||||||
import { classLabel, groupClassOptions } from "oj/submission/utils"
|
import { classLabel, groupClassOptions } from "utils/classes"
|
||||||
import { errorCode } from "utils/api"
|
import { errorCode } from "utils/api"
|
||||||
import { STORAGE_KEY } from "utils/constants"
|
import { STORAGE_KEY } from "utils/constants"
|
||||||
import storage from "utils/storage"
|
import storage from "utils/storage"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { Extension } from "@codemirror/state"
|
|||||||
import type { LANGUAGE } from "utils/types"
|
import type { LANGUAGE } from "utils/types"
|
||||||
import { darkCodeTheme, lightCodeTheme } from "../themes/codeTheme"
|
import { darkCodeTheme, lightCodeTheme } from "../themes/codeTheme"
|
||||||
import { styleTheme } from "shared/extensions/baseTheme"
|
import { styleTheme } from "shared/extensions/baseTheme"
|
||||||
import { enhanceCompletion } from "shared/extensions/autocompletion"
|
import { enhanceCompletion, type SqlTables } from "shared/extensions/autocompletion"
|
||||||
import { languageExtension } from "shared/extensions/language"
|
import { languageExtension } from "shared/extensions/language"
|
||||||
import { useCollabDoc } from "../composables/collabDoc"
|
import { useCollabDoc } from "../composables/collabDoc"
|
||||||
import { useCollabStore } from "shared/store/collab"
|
import { useCollabStore } from "shared/store/collab"
|
||||||
@@ -38,6 +38,8 @@ interface Props {
|
|||||||
* 光比对题号挡不住(见 problemPageContext 的 `collab`)
|
* 光比对题号挡不住(见 problemPageContext 的 `collab`)
|
||||||
*/
|
*/
|
||||||
collab?: boolean
|
collab?: boolean
|
||||||
|
/** SQL 题的表,给补全用(表名、字段名) */
|
||||||
|
sqlTables?: SqlTables
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -49,6 +51,7 @@ const {
|
|||||||
extraExtensions = [],
|
extraExtensions = [],
|
||||||
problemId = "",
|
problemId = "",
|
||||||
collab = true,
|
collab = true,
|
||||||
|
sqlTables = [],
|
||||||
} = defineProps<Props>()
|
} = defineProps<Props>()
|
||||||
const code = defineModel<string>("value")
|
const code = defineModel<string>("value")
|
||||||
|
|
||||||
@@ -61,7 +64,7 @@ const extensions = computed(() => [
|
|||||||
closeBrackets(),
|
closeBrackets(),
|
||||||
isDark.value ? darkCodeTheme : lightCodeTheme,
|
isDark.value ? darkCodeTheme : lightCodeTheme,
|
||||||
autocompletion({
|
autocompletion({
|
||||||
override: [enhanceCompletion(language), completeAnyWord],
|
override: [enhanceCompletion(language, () => sqlTables), completeAnyWord],
|
||||||
}),
|
}),
|
||||||
getInitialExtension(),
|
getInitialExtension(),
|
||||||
...extraExtensions,
|
...extraExtensions,
|
||||||
|
|||||||
File renamed without changes.
@@ -5,7 +5,7 @@ import type {
|
|||||||
CompletionSource,
|
CompletionSource,
|
||||||
} from "@codemirror/autocomplete"
|
} from "@codemirror/autocomplete"
|
||||||
import type { EditorView } from "@codemirror/view"
|
import type { EditorView } from "@codemirror/view"
|
||||||
import { useProblemStore } from "oj/store/problem"
|
import type { SqlDisplay } from "@oj2/contract"
|
||||||
import type { LANGUAGE } from "utils/types"
|
import type { LANGUAGE } from "utils/types"
|
||||||
import { c } from "./c"
|
import { c } from "./c"
|
||||||
import { python } from "./python"
|
import { python } from "./python"
|
||||||
@@ -23,9 +23,10 @@ const chineseAnnotations: Record<string, ChineseCompletion[]> = {
|
|||||||
sql,
|
sql,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SqlTables = SqlDisplay["tables"]
|
||||||
|
|
||||||
// SQL 题:当前题目的表名和字段名补全,数据来自 sql_display
|
// SQL 题:当前题目的表名和字段名补全,数据来自 sql_display
|
||||||
function sqlSchemaCompletions(): Completion[] {
|
function sqlSchemaCompletions(tables: SqlTables): Completion[] {
|
||||||
const tables = useProblemStore().problem?.sqlDisplay?.tables ?? []
|
|
||||||
return tables.flatMap((table) => [
|
return tables.flatMap((table) => [
|
||||||
{
|
{
|
||||||
label: table.name,
|
label: table.name,
|
||||||
@@ -45,7 +46,14 @@ function sqlSchemaCompletions(): Completion[] {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enhanceCompletion(language: LANGUAGE): CompletionSource {
|
/**
|
||||||
|
* `sqlTables` 由用编辑器的地方给(题目页给这道题的表)。原来是这里直接读题目页的 store:
|
||||||
|
* 自学页的 SQL 代码块会补出上一道看过的题的表名
|
||||||
|
*/
|
||||||
|
export function enhanceCompletion(
|
||||||
|
language: LANGUAGE,
|
||||||
|
sqlTables: () => SqlTables = () => [],
|
||||||
|
): CompletionSource {
|
||||||
return async function (context: CompletionContext): Promise<CompletionResult | null> {
|
return async function (context: CompletionContext): Promise<CompletionResult | null> {
|
||||||
const word = context.matchBefore(/\w+/)
|
const word = context.matchBefore(/\w+/)
|
||||||
if (!word && !context.explicit) return null
|
if (!word && !context.explicit) return null
|
||||||
@@ -80,7 +88,7 @@ export function enhanceCompletion(language: LANGUAGE): CompletionSource {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (trulyLanguage === "sql") {
|
if (trulyLanguage === "sql") {
|
||||||
completions.push(...sqlSchemaCompletions())
|
completions.push(...sqlSchemaCompletions(sqlTables()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { NO_CLASS } from "@oj2/contract"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 班级下拉的标签:`253` → `25计算机3班`,和统计面板、班级 PK 同一个写法。
|
||||||
|
* null(接口里没填班级的号)和 NO_CLASS(筛选里的「没填班级」)都写成「没填班级」
|
||||||
|
*/
|
||||||
|
export function classLabel(className: string | null) {
|
||||||
|
if (className === null || className === NO_CLASS) return "没填班级"
|
||||||
|
if (!/^\d{3,}$/.test(className)) return className
|
||||||
|
return `${className.slice(0, 2)}计算机${className.slice(2)}班`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClassOption = { label: string; value: string }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全站的班级下拉都按年级分组:「25 级」底下列 251、252……,新的年级在上,组里按班号排
|
||||||
|
* (11 班排在 2 班后面)。年级是班号前两位,和排名的「本年级」同一个口径。
|
||||||
|
* `first` 给了就把那个年级提到最前(班级 PK 加班时先给同年级的)。
|
||||||
|
* `toOption` 换掉选项长相(题单的「n/m 人加入」、登录框的 ks 前缀);不是数字班号的
|
||||||
|
* 名字不分组,按原顺序跟在最后。
|
||||||
|
*/
|
||||||
|
export function groupClassOptions<T extends ClassOption = ClassOption>(
|
||||||
|
names: string[],
|
||||||
|
toOption: (name: string) => T = (name) => ({ label: classLabel(name), value: name }) as T,
|
||||||
|
first?: string,
|
||||||
|
) {
|
||||||
|
const groups = new Map<string, string[]>()
|
||||||
|
const rest: string[] = []
|
||||||
|
for (const name of new Set(names)) {
|
||||||
|
if (!/^\d{3,}$/.test(name)) {
|
||||||
|
rest.push(name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const grade = name.slice(0, 2)
|
||||||
|
groups.set(grade, [...(groups.get(grade) ?? []), name])
|
||||||
|
}
|
||||||
|
const grouped = [...groups]
|
||||||
|
.sort((a, b) => (a[0] === first ? -1 : b[0] === first ? 1 : b[0].localeCompare(a[0])))
|
||||||
|
.map(([grade, list]) => ({
|
||||||
|
type: "group" as const,
|
||||||
|
label: `${grade} 级`,
|
||||||
|
key: `grade-${grade}`,
|
||||||
|
children: list.sort((a, b) => a.localeCompare(b, "zh", { numeric: true })).map(toOption),
|
||||||
|
}))
|
||||||
|
return [...grouped, ...rest.map(toOption)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 班级下拉的选项:配置里的班按年级分组,最后一项「没填班级」(这批号也要能和普通班一样
|
||||||
|
* 圈出来看)。带进来的班不在配置里(老班、手打的)也要显示得出来,不然框里是空的却在筛
|
||||||
|
*/
|
||||||
|
export function classSelectOptions(list: string[], current: string) {
|
||||||
|
const all = current && current !== NO_CLASS ? [current, ...list] : list
|
||||||
|
return [...groupClassOptions(all), { label: classLabel(NO_CLASS), value: NO_CLASS }]
|
||||||
|
}
|
||||||
Reference in new issue
Block a user