diff --git a/package-lock.json b/package-lock.json index 01367ed..102e784 100644 --- a/package-lock.json +++ b/package-lock.json @@ -523,7 +523,6 @@ "resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz", "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", "license": "MIT", - "peer": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -533,7 +532,6 @@ "resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.4.tgz", "integrity": "sha512-hduz0suCcUSC/kM8Fq3A9iLwInJDl8fD1xLpTIk+5xkNm8z/FT7UsIa9sOXrkpChh+XXc18RzswE8QqELsVl+g==", "license": "MIT", - "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -1186,7 +1184,6 @@ "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -1562,7 +1559,6 @@ "resolved": "https://registry.npmmirror.com/@uppy/core/-/core-2.3.4.tgz", "integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==", "license": "MIT", - "peer": true, "dependencies": { "@transloadit/prettier-bytes": "0.0.7", "@uppy/store-default": "^2.1.1", @@ -1612,7 +1608,6 @@ "resolved": "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz", "integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==", "license": "MIT", - "peer": true, "dependencies": { "@uppy/companion-client": "^2.2.2", "@uppy/utils": "^4.1.2", @@ -1687,7 +1682,6 @@ "resolved": "https://registry.npmjs.org/@vue-flow/core/-/core-1.48.2.tgz", "integrity": "sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==", "license": "MIT", - "peer": true, "dependencies": { "@vueuse/core": "^10.5.0", "d3-drag": "^3.0.0", @@ -2039,7 +2033,6 @@ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz", "integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==", "license": "MIT", - "peer": true, "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.3.0", @@ -2094,7 +2087,6 @@ "resolved": "https://registry.npmmirror.com/@wangeditor-next/basic-modules/-/basic-modules-2.0.0.tgz", "integrity": "sha512-oH7Cv6mHorvBkj5t3isP9wncgWABYLlQpoQZYOIFtWVwgsQatwoGVFHF6PoJzz+mTkt5UaJcyfDxFSo9Thvhdw==", "license": "MIT", - "peer": true, "dependencies": { "is-url": "^1.2.4" }, @@ -2127,7 +2119,6 @@ "resolved": "https://registry.npmmirror.com/@wangeditor-next/core/-/core-1.8.0.tgz", "integrity": "sha512-U2TlQ0Lpo6aLb0KD8oJgzG/rFAYO61cy+qbZu+t5lDfS3CECNjOhGIC3C7/dXIhiMQ8V/LY4cvrPt9R5G4vLjA==", "license": "MIT", - "peer": true, "dependencies": { "@types/event-emitter": "^0.3.3", "event-emitter": "^0.3.5", @@ -2157,7 +2148,6 @@ "resolved": "https://registry.npmmirror.com/@wangeditor-next/editor/-/editor-5.7.0.tgz", "integrity": "sha512-bxkw/TeWBJz7AU4qXZnx5tx/s1yzx8XdLmSRN9ev4btArKnfXR/6hr3dqxUSsyea8q//IpEv1qr9tc2ureEAsA==", "license": "MIT", - "peer": true, "dependencies": { "@uppy/core": "^2.1.1", "@uppy/xhr-upload": "^2.0.3", @@ -2449,7 +2439,6 @@ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "license": "MIT", - "peer": true, "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -2483,7 +2472,6 @@ "resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz", "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", "license": "MIT", - "peer": true, "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", @@ -2603,7 +2591,6 @@ "resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.15.14.tgz", "integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==", "license": "MIT", - "peer": true, "dependencies": { "@emotion/hash": "~0.8.0", "csstype": "~3.0.5" @@ -2632,7 +2619,6 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.3.tgz", "integrity": "sha512-Gej7U+OKR+LZ8kvX7rb2HhCYJ0IhvEFsnkud4SB1PR+BUY/TsSO0dmOW59WEVLu51b1Rm+gQRKoz4bLYxGSZ2g==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10" } @@ -3061,7 +3047,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -3496,7 +3481,6 @@ "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.1.0.tgz", "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" @@ -3557,7 +3541,6 @@ "resolved": "https://registry.npmmirror.com/dom7/-/dom7-4.0.6.tgz", "integrity": "sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA==", "license": "MIT", - "peer": true, "dependencies": { "ssr-window": "^4.0.0" } @@ -4076,8 +4059,7 @@ "version": "0.2.0", "resolved": "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz", "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/is-url": { "version": "1.2.4", @@ -4253,43 +4235,37 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.foreach": { "version": "4.5.0", "resolved": "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz", "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz", "integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lucide-vue-next": { "version": "0.543.0", @@ -4329,7 +4305,6 @@ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", "license": "MIT", - "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -4621,7 +4596,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.js" }, @@ -4699,7 +4673,6 @@ "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz", "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", "license": "MIT", - "peer": true, "dependencies": { "@vue/devtools-api": "^7.7.7" }, @@ -5061,8 +5034,7 @@ "version": "0.123.0", "resolved": "https://registry.npmmirror.com/slate/-/slate-0.123.0.tgz", "integrity": "sha512-Oon3HR/QzJQBjuOUJT1jGGlp8Ff7t3Bkr/rJ2lDqxNT4H+cBnXpEVQ/si6hn1ZCHhD2xY/2N91PQoH/rD7kxTg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/slate-history": { "version": "0.115.0", @@ -5078,7 +5050,6 @@ "resolved": "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.6.3.tgz", "integrity": "sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.17.0" } @@ -5225,7 +5196,6 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5440,7 +5410,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.34", "@vue/compiler-sfc": "3.5.34", @@ -5488,7 +5457,6 @@ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.6.tgz", "integrity": "sha512-9+kmUTGbKMyW9Asoy98IXXYIzrTMT7JDAdpDDeEkorHvybpUvBI2wsrSM5jFOXrFydpzRFJ9vAh+80DN2PGu9w==", "license": "MIT", - "peer": true, "dependencies": { "@babel/generator": "^7.28.6", "@vue-macros/common": "^3.1.1", @@ -5741,7 +5709,6 @@ "resolved": "https://registry.npmmirror.com/yjs/-/yjs-13.6.30.tgz", "integrity": "sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ==", "license": "MIT", - "peer": true, "dependencies": { "lib0": "^0.2.99" }, diff --git a/src/admin/problem/detail.vue b/src/admin/problem/detail.vue index 282dc56..721ab01 100644 --- a/src/admin/problem/detail.vue +++ b/src/admin/problem/detail.vue @@ -93,10 +93,41 @@ const problem = useLocalStorage(STORAGE_KEY.ADMIN_PROBLEM, { // 从服务器来的tag列表 const tagList = shallowRef([]) +const tagListLoaded = ref(false) const selectedTags = ref([]) const newTags = ref([]) const selectedTagSet = computed(() => new Set(selectedTags.value)) +let syncingTagInputs = false + +function normalizeTagNames(tags: unknown): string[] { + if (!Array.isArray(tags)) return [] + return unique( + tags + .map((tag) => (typeof tag === "string" ? tag : tag?.name)) + .filter((tag): tag is string => !!tag), + ) +} + +function syncProblemTags() { + problem.value.tags = unique([...selectedTags.value, ...newTags.value]) +} + +function syncTagInputsFromProblemTags(tags: unknown = problem.value.tags) { + const tagNames = normalizeTagNames(tags) + const existingTagNames = new Set(tagList.value.map((tag) => tag.name)) + + syncingTagInputs = true + if (!tagListLoaded.value) { + selectedTags.value = tagNames + newTags.value = [] + } else { + selectedTags.value = tagNames.filter((tag) => existingTagNames.has(tag)) + newTags.value = tagNames.filter((tag) => !existingTagNames.has(tag)) + } + syncingTagInputs = false + syncProblemTags() +} function toggleTag(name: string) { const set = new Set(selectedTags.value) @@ -144,6 +175,7 @@ const languageOptions = [ async function getProblemDetail() { if (!props.problemID) { + syncTagInputsFromProblemTags() toggleReady(true) return } @@ -161,7 +193,7 @@ async function getProblemDetail() { problem.value.difficulty = data.difficulty problem.value.visible = data.visible problem.value.share_submission = data.share_submission - problem.value.tags = data.tags + problem.value.tags = normalizeTagNames(data.tags) problem.value.languages = data.languages problem.value.template = data.template problem.value.samples = data.samples @@ -201,8 +233,7 @@ async function getProblemDetail() { } }) // 标签 - selectedTags.value = data.tags - newTags.value = [] + syncTagInputsFromProblemTags(problem.value.tags) toggleReady(true) } catch (error) { message.error("获取题目失败") @@ -213,6 +244,8 @@ async function getProblemDetail() { async function getTagList() { const res = await getProblemTagList() tagList.value = res.data + tagListLoaded.value = true + syncTagInputsFromProblemTags() } function addSample() { @@ -352,6 +385,7 @@ async function submit() { filterHint() getTemplate() filterAnswers() + syncProblemTags() const api = { "admin problem create": createProblem, "admin problem edit": editProblem, @@ -436,7 +470,8 @@ onMounted(() => { }) watch([selectedTags, newTags], ([sel, newT]) => { - problem.value.tags = [...sel, ...newT] + if (syncingTagInputs) return + problem.value.tags = unique([...sel, ...newT]) }) watch( () => problem.value.languages,