原来只有 `apps/web` 在 Prettier 下(配置在 `apps/web/.prettierrc.toml`、脚本在 web 的 package.json),后端和契约从来没格式化过 —— 手写在 100 列上下,`db/schema.ts` 还是 drizzle-kit pull 留下的 tab 缩进。两套口径分叉久了,跨端改一处就得记着「这边 什么风格」。 - 配置搬到根目录 `.prettierrc.toml`,内容不变(`semi=false`,其余全默认, printWidth 80 —— 和前端已有的格式一致,不另立一套宽度); - 脚本统一成根目录 `bun run fmt`,覆盖 `apps/*/src`、`apps/web/tests` 和两个构建 配置;web 自己那份 `fmt` 和重复的 prettier 依赖删掉; - `.prettierignore` 挡掉两类不该碰的:drizzle-kit 生成的 `src/db/meta/` 结构快照 (它是 db:generate 的比对输入,只该由 drizzle-kit 写)、unplugin 每次 dev 都会 重写的 `auto-imports.d.ts` / `components.d.ts`; - 全量跑了一遍。纯格式,无行为改动:api typecheck / check:routes / check:ast、 前端 type-check 全过,起 api 打了接口确认正常。前端这 39 个文件的小改动是 prettier 版本漂移(类型断言的换行口径变了),不是新配置带来的。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e600fd24cf
commit
ed56a209ea
122 files changed
+10557
-4843
No files matched your search
@@ -94,9 +94,7 @@ export function editProblem(problem: AdminProblem | BlankProblem) {
|
||||
}
|
||||
|
||||
export function toggleProblemVisible(problemID: number) {
|
||||
return api.put<{ visible: boolean }>(
|
||||
`admin/problems/${problemID}/visibility`,
|
||||
)
|
||||
return api.put<{ visible: boolean }>(`admin/problems/${problemID}/visibility`)
|
||||
}
|
||||
|
||||
export function generateFlowchartFromPythonCode(python: string) {
|
||||
@@ -135,7 +133,11 @@ export function batchTagProblems(
|
||||
}
|
||||
|
||||
// 用户排名(后台版,无 100 名上限;公开榜单是 oj/api.ts 的 getRank)
|
||||
export function getAdminUserRank(offset: number, limit: number, keyword: string) {
|
||||
export function getAdminUserRank(
|
||||
offset: number,
|
||||
limit: number,
|
||||
keyword: string,
|
||||
) {
|
||||
return api.get<AdminUserRank>("admin/rankings/users", {
|
||||
params: { offset, limit, keyword },
|
||||
})
|
||||
@@ -236,9 +238,7 @@ export function previewSQLTestcase(data: {
|
||||
|
||||
// 回显已上传的 SQL 测试点脚本内容(按 1.sql, 2.sql... 排序)
|
||||
export function getSQLTestcaseScripts(problemId: number) {
|
||||
return api.get<SqlTestCaseScript[]>(
|
||||
`admin/problems/${problemId}/sql-scripts`,
|
||||
)
|
||||
return api.get<SqlTestCaseScript[]>(`admin/problems/${problemId}/sql-scripts`)
|
||||
}
|
||||
|
||||
// AI 根据标准答案生成一个 SQL 测试点初始化脚本
|
||||
@@ -412,10 +412,7 @@ export function createTutorial(data: Partial<Tutorial>) {
|
||||
}
|
||||
|
||||
export function updateTutorial(data: Partial<Tutorial>) {
|
||||
return api.put<Tutorial>(
|
||||
`admin/tutorials/${data.id}`,
|
||||
toTutorialBody(data),
|
||||
)
|
||||
return api.put<Tutorial>(`admin/tutorials/${data.id}`, toTutorialBody(data))
|
||||
}
|
||||
|
||||
export function deleteTutorial(id: number) {
|
||||
|
||||
@@ -168,10 +168,14 @@ const tutorialColumns = computed<DataTableColumn<LearnTutorialProgress>[]>(
|
||||
|
||||
const exerciseColumns = computed<DataTableColumn<LearnExerciseProgress>[]>(
|
||||
() => [
|
||||
{ type: "expand", renderExpand: (row) => h(ExerciseAttempts, {
|
||||
exerciseId: row.exerciseId,
|
||||
className: className.value.trim(),
|
||||
}) },
|
||||
{
|
||||
type: "expand",
|
||||
renderExpand: (row) =>
|
||||
h(ExerciseAttempts, {
|
||||
exerciseId: row.exerciseId,
|
||||
className: className.value.trim(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: "课",
|
||||
key: "tutorialOrder",
|
||||
@@ -289,7 +293,8 @@ onMounted(load)
|
||||
</n-text>
|
||||
<!-- 口径写在表上方,免得老师对着「已读 0 课 / 累计 25 分钟」猜是不是坏了 -->
|
||||
<n-text depth="3" style="font-size: 12px">
|
||||
「已读」按累计停留满 {{ TUTORIAL_READ_SECONDS / 60 }} 分钟算,不足的只计时长
|
||||
「已读」按累计停留满
|
||||
{{ TUTORIAL_READ_SECONDS / 60 }} 分钟算,不足的只计时长
|
||||
</n-text>
|
||||
</n-flex>
|
||||
|
||||
|
||||
@@ -78,7 +78,10 @@ function nodeTargetOptions(lang: string): SelectOption[] {
|
||||
// 逻辑名 and/or/not 在 C 里显示成 && / || / !,存进去的还是逻辑名
|
||||
function operatorTargetOptions(lang: string): SelectOption[] {
|
||||
return Object.entries(AST_OPERATOR_TARGETS_BY_LANGUAGE[lang] ?? {}).map(
|
||||
([value, label]) => ({ label: label === value ? value : `${label}(${value})`, value }),
|
||||
([value, label]) => ({
|
||||
label: label === value ? value : `${label}(${value})`,
|
||||
value,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -173,7 +176,8 @@ function getTargetLabel(
|
||||
engine: string,
|
||||
target: string,
|
||||
): string | undefined {
|
||||
if (isNodeEngine(engine)) return AST_NODE_TARGETS_BY_LANGUAGE[lang]?.[target]?.label
|
||||
if (isNodeEngine(engine))
|
||||
return AST_NODE_TARGETS_BY_LANGUAGE[lang]?.[target]?.label
|
||||
// 运算符不写 label:判题结果的文案按语言翻译(astOperatorLabel),
|
||||
// 存一个固定 label 反而会把 C 的 && 钉死成 and
|
||||
return undefined
|
||||
@@ -252,7 +256,8 @@ watch(supportedLanguages, (langs) => {
|
||||
:bordered="false"
|
||||
style="margin-bottom: 8px"
|
||||
>
|
||||
{{ unsupportedLanguages.join("、") }} 暂不支持代码规则检查,判题机只能检查
|
||||
{{ unsupportedLanguages.join("、") }}
|
||||
暂不支持代码规则检查,判题机只能检查
|
||||
{{ AST_SUPPORTED_LANGUAGES.join(" / ") }}
|
||||
</n-alert>
|
||||
<n-tabs
|
||||
@@ -393,9 +398,7 @@ watch(supportedLanguages, (langs) => {
|
||||
<n-empty
|
||||
v-else
|
||||
:description="
|
||||
languages.length
|
||||
? '当前语言不支持代码规则检查'
|
||||
: '请先选择编程语言'
|
||||
languages.length ? '当前语言不支持代码规则检查' : '请先选择编程语言'
|
||||
"
|
||||
/>
|
||||
</n-collapse-item>
|
||||
|
||||
@@ -59,9 +59,7 @@ async function submit() {
|
||||
props.action,
|
||||
)
|
||||
const verb = props.action === "add" ? "添加" : "移除"
|
||||
message.success(
|
||||
`已为 ${res.problemCount} 道题${verb} ${res.tagCount} 个标签`,
|
||||
)
|
||||
message.success(`已为 ${res.problemCount} 道题${verb} ${res.tagCount} 个标签`)
|
||||
close()
|
||||
emit("done")
|
||||
}
|
||||
|
||||
@@ -179,11 +179,10 @@ async function run() {
|
||||
async function upload() {
|
||||
isUploading.value = true
|
||||
try {
|
||||
const data = uploadable.value
|
||||
.flatMap((f, i) => [
|
||||
{ name: `${i + 1}.in`, content: f.in },
|
||||
{ name: `${i + 1}.out`, content: f.out },
|
||||
])
|
||||
const data = uploadable.value.flatMap((f, i) => [
|
||||
{ name: `${i + 1}.in`, content: f.in },
|
||||
{ name: `${i + 1}.out`, content: f.out },
|
||||
])
|
||||
|
||||
const blob = createZipBlob(data)
|
||||
const file = new File([blob], "testcase.zip", { type: "application/zip" })
|
||||
|
||||
@@ -896,7 +896,11 @@ watch(
|
||||
v-model:value="problem.showFlowchart"
|
||||
:disabled="problem.allowFlowchart"
|
||||
/>
|
||||
<n-text v-if="problem.allowFlowchart" depth="3" style="font-size: 12px">
|
||||
<n-text
|
||||
v-if="problem.allowFlowchart"
|
||||
depth="3"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
让学生自己画图时,标准流程图不会下发给学生,这个开关没有意义
|
||||
</n-text>
|
||||
</n-flex>
|
||||
|
||||
@@ -122,9 +122,7 @@ async function saveTag(tag: AdminTag) {
|
||||
}
|
||||
const res = await renameTag(tag.id, name)
|
||||
if (res.merged) {
|
||||
message.success(
|
||||
`已合并到「${res.name}」,影响 ${res.affectedCount} 道题`,
|
||||
)
|
||||
message.success(`已合并到「${res.name}」,影响 ${res.affectedCount} 道题`)
|
||||
} else {
|
||||
message.success("已重命名")
|
||||
}
|
||||
|
||||
@@ -110,9 +110,7 @@ function startRolling(finalName: string) {
|
||||
|
||||
async function getRandom() {
|
||||
const res = await randomUser10(query.classroom)
|
||||
const names = (res as string[]).map(
|
||||
(name) => name.split(query.classroom)[1],
|
||||
)
|
||||
const names = (res as string[]).map((name) => name.split(query.classroom)[1])
|
||||
rollingNames.value = names
|
||||
const finalName = names[names.length - 1]
|
||||
startRolling(finalName)
|
||||
|
||||
@@ -23,8 +23,7 @@ defineEmits<{
|
||||
*/
|
||||
const maskable = computed(
|
||||
() =>
|
||||
props.user.adminType !== USER_TYPE.REGULAR_USER &&
|
||||
!!props.user.rawPassword,
|
||||
props.user.adminType !== USER_TYPE.REGULAR_USER && !!props.user.rawPassword,
|
||||
)
|
||||
</script>
|
||||
<template>
|
||||
|
||||
@@ -57,7 +57,9 @@ async function uploadUsers() {
|
||||
message.success("用户已上传成功")
|
||||
// 只导出用户名和密码两列 —— 发给学生的就是这两样,邮箱是占位生成的、
|
||||
// 真名本来就是老师粘进来的那一列,都不用回传
|
||||
const csv = users.value.map(([username, password]) => `${username},${password}`).join("\n")
|
||||
const csv = users.value
|
||||
.map(([username, password]) => `${username},${password}`)
|
||||
.join("\n")
|
||||
const hiddenElement = document.createElement("a")
|
||||
hiddenElement.href = "data:text/csv;charset=utf-8," + encodeURI(csv)
|
||||
hiddenElement.target = "_blank"
|
||||
|
||||
@@ -77,7 +77,8 @@ const columns: DataTableColumn<User>[] = [
|
||||
user: row,
|
||||
revealed: revealedPasswords.value.has(row.id),
|
||||
onToggle: (id: number) => {
|
||||
if (!revealedPasswords.value.delete(id)) revealedPasswords.value.add(id)
|
||||
if (!revealedPasswords.value.delete(id))
|
||||
revealedPasswords.value.add(id)
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user