添加代码格式化

This commit is contained in:
2025-07-15 13:08:20 +08:00
parent 616bb2bfec
commit 2ad2a971c0
5 changed files with 80 additions and 35 deletions

8
package-lock.json generated
View File

@@ -33,7 +33,7 @@
"@rsbuild/core": "^1.3.13", "@rsbuild/core": "^1.3.13",
"@rsbuild/plugin-vue": "^1.0.7", "@rsbuild/plugin-vue": "^1.0.7",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"prettier": "^3.5.3", "prettier": "^3.6.2",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"unplugin-vue-components": "^28.5.0" "unplugin-vue-components": "^28.5.0"
} }
@@ -2957,9 +2957,9 @@
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "3.5.3", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {

View File

@@ -34,7 +34,7 @@
"@rsbuild/core": "^1.3.13", "@rsbuild/core": "^1.3.13",
"@rsbuild/plugin-vue": "^1.0.7", "@rsbuild/plugin-vue": "^1.0.7",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"prettier": "^3.5.3", "prettier": "^3.6.2",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"unplugin-vue-components": "^28.5.0" "unplugin-vue-components": "^28.5.0"
} }

View File

@@ -4,12 +4,7 @@
打开{{ TASK_LABEL[taskTab] }} 打开{{ TASK_LABEL[taskTab] }}
</n-button> </n-button>
<template v-if="user.loaded && authed"> <template v-if="user.loaded && authed">
<n-button <n-button quaternary @click="emit('format')">整理</n-button>
quaternary
@click="$router.push({ name: 'submissions', params: { page: 1 } })"
>
所有提交
</n-button>
<n-button <n-button
type="primary" type="primary"
secondary secondary
@@ -17,7 +12,7 @@
:loading="submitLoading" :loading="submitLoading"
@click="submit" @click="submit"
> >
提交代码 提交
</n-button> </n-button>
<n-dropdown :options="menu" @select="clickMenu"> <n-dropdown :options="menu" @select="clickMenu">
<n-button>{{ user.username }}</n-button> <n-button>{{ user.username }}</n-button>
@@ -48,6 +43,7 @@ import { router } from "../router"
import { ADMIN_URL, TASK_LABEL } from "../utils/const" import { ADMIN_URL, TASK_LABEL } from "../utils/const"
const message = useMessage() const message = useMessage()
const emit = defineEmits(["format"])
const submitLoading = ref(false) const submitLoading = ref(false)

View File

@@ -67,15 +67,24 @@
</n-flex> </n-flex>
</n-tab-pane> </n-tab-pane>
<template #suffix> <template #suffix>
<Corner /> <Corner @format="format" />
</template> </template>
</n-tabs> </n-tabs>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Icon } from "@iconify/vue" import { Icon } from "@iconify/vue"
import prettier from "prettier/standalone"
import * as htmlParser from "prettier/parser-html"
import * as cssParser from "prettier/parser-postcss"
import * as babelParser from "prettier/parser-babel"
import * as estreeParser from "prettier/plugins/estree"
import Editor from "./Editor.vue" import Editor from "./Editor.vue"
import Corner from "./Corner.vue" import Corner from "./Corner.vue"
import { html, css, js, tab, size, reset } from "../store/editors" import { html, css, js, tab, size, reset } from "../store/editors"
import { NCode, useDialog } from "naive-ui"
import { h } from "vue"
const dialog = useDialog()
function changeTab(name: string) { function changeTab(name: string) {
tab.value = name tab.value = name
@@ -84,6 +93,39 @@ function changeTab(name: string) {
function changeSize(num: number) { function changeSize(num: number) {
size.value = num size.value = num
} }
async function format() {
try {
const [htmlFormatted, cssFormatted, jsFormatted] = await Promise.all([
prettier.format(html.value, {
parser: "html",
//@ts-ignore
plugins: [htmlParser, babelParser, estreeParser, cssParser],
tabWidth: 4,
}),
prettier.format(css.value, {
parser: "css",
plugins: [cssParser],
tabWidth: 4,
}),
prettier.format(js.value, {
parser: "babel",
//@ts-ignore
plugins: [babelParser, estreeParser],
tabWidth: 2,
}),
])
html.value = htmlFormatted
css.value = cssFormatted
js.value = jsFormatted
} catch (err: any) {
dialog.error({
title: "格式化失败",
content: () => h(NCode, { code: err.message }),
style: { width: "auto" },
})
}
}
</script> </script>
<style scoped> <style scoped>
.pane { .pane {

View File

@@ -11,7 +11,7 @@
:width="20" :width="20"
></Icon> ></Icon>
<n-tabs <n-tabs
style="width: 140px" style="width: 210px"
type="segment" type="segment"
animated animated
:value="taskTab" :value="taskTab"
@@ -19,6 +19,11 @@
> >
<n-tab name="tutorial" tab="教程"></n-tab> <n-tab name="tutorial" tab="教程"></n-tab>
<n-tab name="challenge" tab="挑战"></n-tab> <n-tab name="challenge" tab="挑战"></n-tab>
<n-tab
name="list"
tab="列表"
@click="$router.push({ name: 'submissions', params: { page: 1 } })"
></n-tab>
</n-tabs> </n-tabs>
<template v-if="!hideNav"> <template v-if="!hideNav">
<n-button text @click="prev" :disabled="prevDisabled"> <n-button text @click="prev" :disabled="prevDisabled">
@@ -60,7 +65,7 @@ import { taskId, taskTab } from "../store/task"
import { useRoute, useRouter } from "vue-router" import { useRoute, useRouter } from "vue-router"
import { TASK_TYPE } from "../utils/const" import { TASK_TYPE } from "../utils/const"
import Challenge from "./Challenge.vue" import Challenge from "./Challenge.vue"
import { NButton } from 'naive-ui' import { NButton } from "naive-ui"
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
@@ -71,10 +76,12 @@ const $content = useTemplateRef<any>("$content")
defineEmits(["hide"]) defineEmits(["hide"])
const hideNav = computed( const hideNav = computed(
() => taskTab.value === TASK_TYPE.Challenge || tutorialIds.value.length <= 1, () => taskTab.value !== TASK_TYPE.Tutorial || tutorialIds.value.length <= 1,
) )
function changeTab(v: TASK_TYPE) { function changeTab(v: TASK_TYPE & "list") {
// 排除 list
if (v === "list") return
taskTab.value = v taskTab.value = v
const query = { task: v } as any const query = { task: v } as any
if (v === TASK_TYPE.Tutorial) query.step = step.value if (v === TASK_TYPE.Tutorial) query.step = step.value
@@ -139,20 +146,20 @@ function addButton() {
let lang = "html" let lang = "html"
if (match) lang = match[1].toLowerCase() if (match) lang = match[1].toLowerCase()
const langSpan = document.createElement('span') const langSpan = document.createElement("span")
langSpan.className = 'lang' langSpan.className = "lang"
langSpan.textContent = lang.toUpperCase() langSpan.textContent = lang.toUpperCase()
const btnGroup = document.createElement('div') const btnGroup = document.createElement("div")
btnGroup.className = 'btn-group' btnGroup.className = "btn-group"
const copyBtn = document.createElement('button') const copyBtn = document.createElement("button")
copyBtn.className = 'action-btn' copyBtn.className = "action-btn"
copyBtn.textContent = '复制' copyBtn.textContent = "复制"
const replaceBtn = document.createElement('button') const replaceBtn = document.createElement("button")
replaceBtn.className = 'action-btn' replaceBtn.className = "action-btn"
replaceBtn.textContent = '替换' replaceBtn.textContent = "替换"
btnGroup.appendChild(copyBtn) btnGroup.appendChild(copyBtn)
btnGroup.appendChild(replaceBtn) btnGroup.appendChild(replaceBtn)