import type { JudgeStatusValue } from '@oj2/contract' import type { AchievementRarity, SUBMISSION_RESULT, ReactionKey } from './types' // 与后端 judge/status.ts 的 JudgeStatus 逐条对齐(submitting 除外,见下)。 // 注意:原来 time_limit_exceeded 写成 `1 | 2`,TS 按位或算成 3,和 // memory_limit_exceeded 撞了同一个值 —— 后端这两个是分开的两个码。 export enum SubmissionStatus { compile_error = -2, wrong_answer = -1, accepted = 0, cpu_time_limit_exceeded = 1, real_time_limit_exceeded = 2, memory_limit_exceeded = 3, runtime_error = 4, system_error = 5, pending = 6, judging = 7, partial_accepted = 8, // 前端自造的伪状态:点了提交、还没拿到判题结果时本地先填 9。 // 后端永远不会下发它,所以契约的 judgeStatusSchema 里没有。 submitting = 9, ast_check_failed = 10, } // 编译期对齐契约:契约加/改一个码而这里没跟,下面两行会当场编译不过。 type SyncedWithContract = Exclude<`${SubmissionStatus}`, `${SubmissionStatus.submitting}`> extends `${JudgeStatusValue}` ? `${JudgeStatusValue}` extends `${Exclude}` ? true : never : never export const _submissionStatusSynced: SyncedWithContract = true export enum ContestStatus { initial = '2', // 这里不需要传入到后端,只是为了一开始加载数据的时候,做一个初始位 not_started = '1', underway = '0', finished = '-1', } export enum ContestType { public = 'Public', private = 'Password Protected', } export const JUDGE_STATUS: { [key in SUBMISSION_RESULT]: { name: string title: string type: 'error' | 'success' | 'warning' | 'info' } } = { '-2': { name: '编译失败', title: '编译失败', type: 'warning', }, '-1': { name: '答案错误', title: '答案错误', type: 'error', }, '0': { name: '答案正确', title: '答案正确', type: 'success', }, '1': { name: '运行超时', title: '运行超时', type: 'error', }, '2': { name: '运行超时', title: '运行超时', type: 'error', }, '3': { name: '内存超限', title: '内存超限', type: 'error', }, '4': { name: '运行时错误', title: '运行时错误', type: 'warning', }, '5': { name: '系统错误', title: '系统错误', type: 'error', }, '6': { name: '等待评分', title: '等待评分', type: 'warning', }, '7': { name: '正在评分', title: '正在评分', type: 'warning', }, '8': { name: '部分正确', title: '部分正确', type: 'warning', }, '9': { name: '正在提交', title: '正在提交', type: 'info', }, '10': { name: '语法未通过', title: '答案正确,但语法未通过', type: 'success', }, } export const CONTEST_STATUS: { [key in ContestStatus]: { name: string type: 'default' | 'success' | 'warning' } } = { // 这里不需要传入到后端,只是为了一开始加载数据的时候,做一个初始位 '2': { name: '未开始', type: 'warning', }, '1': { name: '未开始', type: 'warning', }, '0': { name: '进行中', type: 'success', }, // 灰的不是红的:列表里九成是已结束的比赛,一片红像满屏报错 '-1': { name: '已结束', type: 'default', }, } // 角色与题目权限的字符串在 `@oj2/contract` 的 roles.ts 定义,这里只是转出去, // 调用方照旧写 `USER_TYPE.SUPER_ADMIN`。原先这里是这批字符串的第三份手抄副本。 export { USER_TYPE, PROBLEM_PERMISSION } from '@oj2/contract' export const STORAGE_KEY = { AUTHED: 'authed', ACCESS_TOKEN: 'accessToken', REFRESH_TOKEN: 'refreshToken', LANGUAGE: 'problemLanguage', ADMIN_PROBLEM: 'adminProblem', ADMIN_SQL_PROBLEM: 'adminSqlProblem', ADMIN_CODE_PROBLEM: 'adminCodeProblem', ADMIN_PROBLEM_TAGS: 'adminProblemTags', ADMIN_SIDER_COLLAPSED: 'adminSiderCollapsed', DEMO_MODE: 'demoMode', LOGIN_CLASS: 'loginClass', LOGIN_MODE: 'loginMode', RANK_CLASS: 'rankClass', } export const DIFFICULTY = { Low: '简单', Mid: '中等', High: '困难', } as const const cSource = '#include\r\n\r\nint main()\r\n{\r\n \r\n return 0;\r\n}' const cppSource = '#include\r\n\r\nusing namespace std;\r\n\r\nint main()\r\n{\r\n \r\n return 0;\r\n}' const pythonSource = '' const javaSource = 'public class Main {\r\n public static void main(String[] args) {\r\n System.out.println("黄岩一职");\r\n }\r\n}' export const SOURCES = { C: cSource, 'C++': cppSource, Java: javaSource, Python: pythonSource, JavaScript: '', Golang: '', Flowchart: '', SQL: '', } as const export const LANGUAGE_FORMAT_VALUE = { C: 'c', 'C++': 'cpp', Java: 'java', Python: 'python', JavaScript: 'javascript', Golang: 'go', Flowchart: 'flowchart', SQL: 'sql', } as const export const LANGUAGE_SHOW_VALUE = { Flowchart: '流程图', C: 'C语言', 'C++': 'C++', Java: 'Java', Python: 'Python', JavaScript: 'JS', Golang: 'Go', SQL: 'SQL', } as const export const ICON_SET = { Flowchart: 'vscode-icons:file-type-drawio', Python: 'devicon:python', C: 'devicon:c', 'C++': 'devicon:cplusplus', Java: 'devicon:java', JavaScript: 'devicon:javascript', Golang: 'devicon:go', SQL: 'devicon:sqlite', } as const const cTemplate = `//TEMPLATE BEGIN #include int main() { printf("黄岩一职"); return 0; } //TEMPLATE END` const cppTemplate = `//TEMPLATE BEGIN #include int main() { return 0; } //TEMPLATE END` const blankTemplate = `//PREPEND BEGIN //PREPEND END //TEMPLATE BEGIN //TEMPLATE END //APPEND BEGIN //APPEND END` export const CODE_TEMPLATES = { C: cTemplate, 'C++': cppTemplate, Python: blankTemplate, Java: blankTemplate, JavaScript: blankTemplate, Golang: blankTemplate, Flowchart: blankTemplate, SQL: blankTemplate, } as const // 成就稀有度 export const RARITY_LABEL: Record = { bronze: '青铜', silver: '白银', gold: '黄金', platinum: '白金', } // 边框、色块用的原色:饱和度够高,明暗底上都醒目。 // 青铜往红偏(色相 23),黄金往纯黄推(色相 47),中间隔开两档亮度, // 不然两个都落在橙棕区,扫一眼分不出来 export const RARITY_COLOR: Record = { bronze: '#c2703d', silver: '#9fa6b2', gold: '#f2c012', platinum: '#7dd3fc', } // 文字要另配一套。上面那组是照深色底调的,搬到白底上白金只有 1.7:1、 // 黄金 2.2:1,12px 的稀有度标签根本看不清。取色见 useRarityColor export const RARITY_TEXT_COLOR: Record<'dark' | 'light', Record> = { dark: { bronze: '#e08d63', silver: '#b6bcc7', gold: '#f2c012', platinum: '#7dd3fc', }, light: { bronze: '#a13d1e', silver: '#6b7280', gold: '#8f6f00', platinum: '#0369a1', }, } // 时间范围配置 /** * 流程图评分项的展示顺序(按分值从高到低)。 * * `ai_criteria_details` 存在 jsonb 列里,而 Postgres 的 jsonb **不保留键序** —— * 它按「键长度 + 字节序」重排,读出来会变成 完整性 / 清晰度 / 规范性 / 逻辑正确性, * 40 分的那项排到最后。凡是要展示评分明细的地方都按这个顺序排,别直接遍历对象。 */ export const FLOWCHART_CRITERIA_ORDER = ['逻辑正确性', '完整性', '规范性', '清晰度'] /** 按 FLOWCHART_CRITERIA_ORDER 排序,表里没有的键排在后面并保持原有相对顺序 */ export function sortFlowchartCriteria(details: Record): [string, T][] { const rank = (key: string) => { const i = FLOWCHART_CRITERIA_ORDER.indexOf(key) return i === -1 ? Number.MAX_SAFE_INTEGER : i } return Object.entries(details).sort(([a], [b]) => rank(a) - rank(b)) } // 班级号的位数范围。学生用户名形如 ks<班级号><姓名>,班级号还要跟 // 网站配置里的班级列表对得上。 // 后端 OnlineJudge/utils/shortcuts.py 的 CLASS_NAME_MIN/MAX_DIGITS // 是同一条规则的另一份,改这里必须同时改那边。 export const CLASS_NAME_MIN_DIGITS = 3 export const CLASS_NAME_MAX_DIGITS = 4 /** 合法班级号:3~4 位纯数字 */ export const CLASS_NAME_RE = new RegExp(`^\\d{${CLASS_NAME_MIN_DIGITS},${CLASS_NAME_MAX_DIGITS}}$`) /** 用户名开头的 ks<班级号>,用于从用户名里认出班级 */ export const USERNAME_CLASS_RE = new RegExp(`^ks\\d{${CLASS_NAME_MIN_DIGITS},${CLASS_NAME_MAX_DIGITS}}`) /** 班级号作为数字时的上下界,给 n-input-number 用 */ export const CLASS_NAME_MIN_VALUE = 10 ** (CLASS_NAME_MIN_DIGITS - 1) export const CLASS_NAME_MAX_VALUE = 10 ** CLASS_NAME_MAX_DIGITS - 1 export const REACTIONS: { key: ReactionKey label: string icon: string }[] = [ { key: 'too_easy', label: '太简单', icon: 'fluent-emoji:smiling-face-with-sunglasses', }, { key: 'too_hard', label: '太难了', icon: 'fluent-emoji:exploding-head' }, { key: 'confusing', label: '没看懂', icon: 'fluent-emoji:face-with-spiral-eyes', }, { key: 'buggy', label: '题目有错', icon: 'fluent-emoji:bug' }, { key: 'learned', label: '学到了', icon: 'fluent-emoji:light-bulb' }, { key: 'interesting', label: '有意思', icon: 'fluent-emoji:star-struck' }, { key: 'want_explain', label: '想听讲解', icon: 'fluent-emoji:books' }, ]