chore(格式): Prettier 统一到全仓,后端和契约一次性格式化
Deploy / deploy (push) Has been cancelled

原来只有 `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:
xuyueandClaude Opus 5 committed 2026-09-16 08:27:34 -06:00
1 parent e600fd24cf
commit ed56a209ea
122 files changed
+10557 -4843

No files matched your search

+8 -11
View File
@@ -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) {
+10 -5
View File
@@ -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" })
+5 -1
View File
@@ -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>
+1 -3
View File
@@ -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("已重命名")
}
+1 -3
View File
@@ -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>
+3 -1
View File
@@ -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"
+2 -1
View File
@@ -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)
},
}),
},