From 0d493c2c14c59f3ece5735ea141abf9e2f823a51 Mon Sep 17 00:00:00 2001 From: rookie4show <rookie4show@gmail.com> Date: Sun, 16 Mar 2025 14:04:01 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E5=A4=9A=E6=96=87=E4=BB=B6=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 379 +++++++++++++++++++++-------- package.json | 2 + src/App.vue | 141 +++++++++-- src/components/ProjectExplorer.vue | 38 +++ src/components/Toolbar.vue | 36 ++- src/components/Yys.vue | 12 + src/main.js | 20 +- src/stores/files.ts | 16 ++ 8 files changed, 508 insertions(+), 136 deletions(-) create mode 100644 src/components/ProjectExplorer.vue create mode 100644 src/stores/files.ts diff --git a/package-lock.json b/package-lock.json index 3b0fac8..d766a13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,10 @@ "@vueup/vue-quill": "^1.2.0", "element-plus": "^2.9.1", "html2canvas": "^1.4.1", + "pinia": "^3.0.1", "vue": "^3.3.10", "vue-i18n": "^11.1.1", + "vue3-draggable-resizable": "^1.6.5", "vuedraggable": "^4.1.0" }, "devDependencies": { @@ -35,10 +37,29 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "version": "7.26.10", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "dependencies": { + "@babel/types": "^7.26.10" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -46,6 +67,18 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/types": { + "version": "7.26.10", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", @@ -560,9 +593,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -835,49 +868,49 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.11.tgz", - "integrity": "sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "dependencies": { - "@babel/parser": "^7.23.5", - "@vue/shared": "3.3.11", + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.11.tgz", - "integrity": "sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "dependencies": { - "@vue/compiler-core": "3.3.11", - "@vue/shared": "3.3.11" + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.11.tgz", - "integrity": "sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==", - "dependencies": { - "@babel/parser": "^7.23.5", - "@vue/compiler-core": "3.3.11", - "@vue/compiler-dom": "3.3.11", - "@vue/compiler-ssr": "3.3.11", - "@vue/reactivity-transform": "3.3.11", - "@vue/shared": "3.3.11", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", - "magic-string": "^0.30.5", - "postcss": "^8.4.32", - "source-map-js": "^1.0.2" + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.11.tgz", - "integrity": "sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "dependencies": { - "@vue/compiler-dom": "3.3.11", - "@vue/shared": "3.3.11" + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/devtools-api": { @@ -885,6 +918,28 @@ "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz", + "integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==", + "dependencies": { + "@vue/devtools-shared": "^7.7.2", + "birpc": "^0.2.19", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.1" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz", + "integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==", + "dependencies": { + "rfdc": "^1.4.1" + } + }, "node_modules/@vue/eslint-config-prettier": { "version": "8.0.0", "resolved": "https://registry.npmmirror.com/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz", @@ -900,60 +955,49 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.11.tgz", - "integrity": "sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==", - "dependencies": { - "@vue/shared": "3.3.11" - } - }, - "node_modules/@vue/reactivity-transform": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.11.tgz", - "integrity": "sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", "dependencies": { - "@babel/parser": "^7.23.5", - "@vue/compiler-core": "3.3.11", - "@vue/shared": "3.3.11", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.5" + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-core": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.11.tgz", - "integrity": "sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", "dependencies": { - "@vue/reactivity": "3.3.11", - "@vue/shared": "3.3.11" + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-dom": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.11.tgz", - "integrity": "sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", "dependencies": { - "@vue/runtime-core": "3.3.11", - "@vue/shared": "3.3.11", - "csstype": "^3.1.2" + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", + "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.11.tgz", - "integrity": "sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", "dependencies": { - "@vue/compiler-ssr": "3.3.11", - "@vue/shared": "3.3.11" + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { - "vue": "3.3.11" + "vue": "3.5.13" } }, "node_modules/@vue/shared": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.11.tgz", - "integrity": "sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==" + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==" }, "node_modules/@vueup/vue-quill": { "version": "1.2.0", @@ -1123,6 +1167,14 @@ "node": ">=0.6" } }, + "node_modules/birpc": { + "version": "0.2.19", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-0.2.19.tgz", + "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", @@ -1273,6 +1325,20 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1478,6 +1544,17 @@ "vue": "^3.2.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2092,6 +2169,11 @@ "node": ">= 0.4" } }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" + }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", @@ -2282,6 +2364,17 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", @@ -2426,14 +2519,11 @@ } }, "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "version": "0.30.17", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/math-intrinsics": { @@ -2498,6 +2588,11 @@ "node": "*" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", @@ -2505,9 +2600,15 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.9", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.9.tgz", + "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2700,10 +2801,15 @@ "node": ">=8" } }, - "node_modules/picocolors": { + "node_modules/perfect-debounce": { "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -2714,14 +2820,56 @@ "node": ">=8.6" } }, + "node_modules/pinia": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.1.tgz", + "integrity": "sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg==", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/@vue/devtools-api": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.2.tgz", + "integrity": "sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==", + "dependencies": { + "@vue/devtools-kit": "^7.7.2" + } + }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.5.3", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2872,6 +3020,11 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", @@ -3092,9 +3245,17 @@ "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==" }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", "engines": { "node": ">=0.10.0" } @@ -3129,6 +3290,17 @@ "node": ">=8" } }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", @@ -3301,15 +3473,15 @@ } }, "node_modules/vue": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.11.tgz", - "integrity": "sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==", + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "dependencies": { - "@vue/compiler-dom": "3.3.11", - "@vue/compiler-sfc": "3.3.11", - "@vue/runtime-dom": "3.3.11", - "@vue/server-renderer": "3.3.11", - "@vue/shared": "3.3.11" + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" @@ -3360,6 +3532,11 @@ "vue": "^3.0.0" } }, + "node_modules/vue3-draggable-resizable": { + "version": "1.6.5", + "resolved": "https://registry.npmmirror.com/vue3-draggable-resizable/-/vue3-draggable-resizable-1.6.5.tgz", + "integrity": "sha512-31142E31fGNnq3HKqvmFLSsqIbhck7TyGuQWhUKrDw6DOcGAuRx4ddRjaxvT6fe7dgeKH53qAh+i0ZlWtPLl2g==" + }, "node_modules/vuedraggable": { "version": "4.1.0", "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz", diff --git a/package.json b/package.json index 0932920..668f5af 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,10 @@ "@vueup/vue-quill": "^1.2.0", "element-plus": "^2.9.1", "html2canvas": "^1.4.1", + "pinia": "^3.0.1", "vue": "^3.3.10", "vue-i18n": "^11.1.1", + "vue3-draggable-resizable": "^1.6.5", "vuedraggable": "^4.1.0" }, "devDependencies": { diff --git a/src/App.vue b/src/App.vue index 23175a7..8a18e7e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,9 +1,25 @@ -<script setup> -import Yys from './components/Yys.vue' -import Toolbar from './components/Toolbar.vue' -import {ref} from "vue"; +<script setup lang="ts"> +import Yys from './components/Yys.vue'; +import Toolbar from './components/Toolbar.vue'; +import ProjectExplorer from './components/ProjectExplorer.vue'; +import { computed, ref, onMounted, onUnmounted } from "vue"; +import { useFilesStore } from "@/stores/files"; +import Vue3DraggableResizable from 'vue3-draggable-resizable'; +import {TabPaneName, TabsPaneContext} from "element-plus"; + +const filesStore = useFilesStore(); const yysRef = ref(null); +const width = ref('100%'); +const height = ref('100vh'); +const toolbarHeight = 48; // 工具栏的高度 +const windowHeight = ref(window.innerHeight); +const contentHeight = computed(() => `${windowHeight.value - toolbarHeight}px`); + +const onResizing = (x, y, width, height) => { + width.value = width; + height.value = height; +}; const onHandleInport = (file) => { yysRef.value.importGroups(file); @@ -12,29 +28,118 @@ const onHandleInport = (file) => { const onHandleExport = () => { yysRef.value.exportGroups(); }; + +const element = ref({ + x: 400, + y: 20, + width: 1080, + height: windowHeight.value - toolbarHeight, + isActive: false, +}); + +const handleFileSelected = (fileId) => { + filesStore.setActiveFile(fileId); +}; + +const handleTabsEdit = ( + targetName: TabPaneName | undefined, + action: 'remove' | 'add' +)=> { + const tabIndex = filesStore.fileList.findIndex(file => file.name === parseInt(name.toString())); + if (tabIndex !== -1) { + filesStore.fileList.splice(tabIndex, 1); + if (filesStore.fileList.length > 0) { + filesStore.setActiveFile(filesStore.fileList[0].name); + } else { + filesStore.setActiveFile(-1); // 或者其他适当的值表示没有活动文件 + } + } +}; + +onMounted(() => { + window.addEventListener('resize', () => { + windowHeight.value = window.innerHeight; + }); +}); + +onUnmounted(() => { + window.removeEventListener('resize', () => { + windowHeight.value = window.innerHeight; + }); +}); </script> <template> - <main id="main-container"> - <!-- 添加工具栏 --> - <Toolbar title="yys-editor" username="示例用户" data-html2canvas-ignore="true" @handleExport="onHandleExport" @handleImport="onHandleInport"/> - <!-- 添加 Watermark 组件 --> - <Yys ref="yysRef"/> + <div class="container"> + <!-- 工具栏 --> + <Toolbar title="yys-editor" username="示例用户" @handleExport="onHandleExport" @handleImport="onHandleInport" /> + <!-- 侧边栏和工作区 --> + <div class="main-content"> + <!-- 侧边栏 --> + <aside class="sidebar"> + <ProjectExplorer :files="filesStore.fileList" @file-selected="handleFileSelected" /> + </aside> - </main> + <!-- 工作区 --> + <div class="workspace"> + <main id="main-container" :style="{ height: contentHeight, overflow: 'auto' }"> + <el-tabs + v-model="filesStore.activeFile" + type="card" + class="demo-tabs" + editable + @edit="handleTabsEdit" + > + <el-tab-pane + v-for="(file, index) in filesStore.fileList" + :key="index" + :label="file.label" + :name="file.name.toString()" + > + <Yys ref="yysRef" /> + </el-tab-pane> + </el-tabs> + </main> + </div> + </div> + </div> </template> <style scoped> -/* 确保 #main-container 具有相对定位 */ -#main-container { - margin-top: 48px; /* 与工具栏高度相同 */ - position: relative; +.container { + display: flex; + flex-direction: column; +} + +.toolbar { + height: 48px; /* 与 toolbarHeight 一致 */ + flex-shrink: 0; /* 防止 Toolbar 被压缩 */ + background-color: #fff; /* 添加背景色以便观察 */ + z-index: 1; /* 确保 Toolbar 在上层 */ +} + +.main-content { + display: flex; + flex: 1; /* 占据剩余空间 */ + overflow: hidden; /* 防止内容溢出 */ + margin-top: 48px; /* 确保 main-content 在 Toolbar 下方 */ +} - min-height: 100vh; /* 允许容器扩展 */ - display: inline-block; - max-width: 100%; +.sidebar { + width: 200px; /* 侧边栏宽度 */ + background-color: #f0f0f0; /* 背景色 */ + flex-shrink: 0; /* 防止侧边栏被压缩 */ + overflow-y: auto; /* 允许侧边栏内容滚动 */ +} +.workspace { + flex: 1; /* 占据剩余空间 */ + overflow: hidden; /* 防止内容溢出 */ } -/* 如果 Yys 组件需要特定的高度或布局,可以根据需要调整 */ +#main-container { + position: relative; + height: 100%; /* 确保内容区域占满父容器 */ + overflow-y: auto; /* 允许内容滚动 */ +} </style> \ No newline at end of file diff --git a/src/components/ProjectExplorer.vue b/src/components/ProjectExplorer.vue new file mode 100644 index 0000000..377a62f --- /dev/null +++ b/src/components/ProjectExplorer.vue @@ -0,0 +1,38 @@ +<template> + <div class="project-explorer"> + <el-tree + :data="files" + :props="defaultProps" + @node-click="handleNodeClick" + /> + </div> +</template> + +<script setup lang="ts"> +import { ref } from 'vue'; + +const props = defineProps({ + files: { + type: Array, + required: true, + }, +}); + +const emit = defineEmits(['file-selected']); + +const defaultProps = { + children: 'children', + label: 'label', +}; + +const handleNodeClick = (data) => { + emit('file-selected', data.name); +}; +</script> + +<style scoped> +.project-explorer { + height: 50%; /* 占据侧边栏的一半 */ + overflow-y: auto; /* 允许内容滚动 */ +} +</style> \ No newline at end of file diff --git a/src/components/Toolbar.vue b/src/components/Toolbar.vue index 58ccf80..7d4e127 100644 --- a/src/components/Toolbar.vue +++ b/src/components/Toolbar.vue @@ -152,7 +152,7 @@ function calculateVisualHeight(selector) { const ignoreElements = (element) => { - return element.classList.contains('ql-toolbar'); + return element.classList.contains('ql-toolbar') || element.classList.contains('el-tabs__header'); }; const prepareCapture = async () => { @@ -163,19 +163,34 @@ const prepareCapture = async () => { style.textContent = `.ql-container.ql-snow { border: none !important; }`; document.head.appendChild(style); + // 获取目标元素 + const element = document.querySelector('#main-container'); + if (!element) { + console.error('Element not found'); + return; + } + + // 保存原始 overflow 样式 + const originalOverflow = element.style.overflow; + try { - const element = document.querySelector('#main-container'); + // 临时隐藏 overflow 样式 + element.style.overflow = 'visible'; + + // 计算需要忽略的元素高度 let totalHeight = calculateVisualHeight('[data-html2canvas-ignore]') + calculateVisualHeight('.ql-toolbar'); console.log('所有携带指定属性的元素高度之和:', totalHeight); - if (!element) { - console.error('Element not found'); - return; - } + + console.log('主元素宽度', element.scrollWidth); + console.log('主元素高度', element.scrollHeight); // 1. 生成原始截图 const canvas = await html2canvas(element, { ignoreElements: ignoreElements, - height: element.scrollHeight - totalHeight + scrollX: 0, + scrollY: 0, + width: element.scrollWidth, + height: element.scrollHeight - totalHeight, }); // 2. 创建新Canvas添加水印 @@ -220,16 +235,19 @@ const prepareCapture = async () => { } ctx.restore(); // 恢复原始状态 + // 3. 存储带水印的图片 state.previewImage = watermarkedCanvas.toDataURL(); - } catch (error) { console.error('Capture failed', error); } finally { + // 恢复原始 overflow 样式 + element.style.overflow = originalOverflow; + + // 移除临时样式 document.head.removeChild(style); } }; - const downloadImage = () => { if (state.previewImage) { const link = document.createElement('a'); diff --git a/src/components/Yys.vue b/src/components/Yys.vue index 3f0604d..a939010 100644 --- a/src/components/Yys.vue +++ b/src/components/Yys.vue @@ -153,6 +153,18 @@ const state = reactive({ shortDescription: '', groupInfo: [{}, {}, {}, {}, {}], details: '' + },{ + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + },{ + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + },{ + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' }, ], groupIndex: 0, diff --git a/src/main.js b/src/main.js index 70b3dcc..0c8769c 100644 --- a/src/main.js +++ b/src/main.js @@ -3,22 +3,26 @@ import App from './App.vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' - import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import Vue3DraggableResizable from 'vue3-draggable-resizable' +// default styles +import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css' + import { createI18n } from 'vue-i18n' // 引入语言文件 import zh from './locales/zh.json' import ja from './locales/ja.json' +import { createPinia } from 'pinia' // 导入 Pinia + const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } - // 获取用户的首选语言 const userLanguage = navigator.language @@ -40,10 +44,10 @@ const i18n = createI18n({ }, }) -app.use(i18n) -app.use(ElementPlus) -app.mount('#app') - - - +const pinia = createPinia() // 创建 Pinia 实例 +app.use(pinia) // 使用 Pinia + .use(i18n) + .use(ElementPlus) + .use(Vue3DraggableResizable) + .mount('#app') \ No newline at end of file diff --git a/src/stores/files.ts b/src/stores/files.ts new file mode 100644 index 0000000..fd93c71 --- /dev/null +++ b/src/stores/files.ts @@ -0,0 +1,16 @@ +import { defineStore } from 'pinia'; + +export const useFilesStore = defineStore('files', { + state: () => ({ + fileList: [{ label: 'File 1', name: 1 },{ label: 'File 2', name: 2 }], + activeFile: 1, + }), + actions: { + addFile(file: { label: string; name: number }) { + this.fileList.push(file); + }, + setActiveFile(fileId: number) { + this.activeFile = fileId; + }, + }, +}); \ No newline at end of file From 8839c3725630816f9142d0ef3772a8b78aacbbcd Mon Sep 17 00:00:00 2001 From: rookie4show <rookie4show@gmail.com> Date: Sun, 16 Mar 2025 23:58:56 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=A4=9A=E6=96=87=E4=BB=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/assets/Other/Contact.png | Bin 0 -> 52290 bytes src/App.vue | 72 +++++++++++++++++++++------ src/components/ProjectExplorer.vue | 6 +-- src/components/ShikigamiProperty.vue | 3 +- src/components/Toolbar.vue | 66 +++++++++++++++++++++--- src/components/Yys.vue | 60 ++++++++-------------- src/stores/files.ts | 61 +++++++++++++++++++++-- 7 files changed, 197 insertions(+), 71 deletions(-) create mode 100644 public/assets/Other/Contact.png diff --git a/public/assets/Other/Contact.png b/public/assets/Other/Contact.png new file mode 100644 index 0000000000000000000000000000000000000000..67e0d5ada0f78423270548325804f3f8347e08e3 GIT binary patch literal 52290 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 YxK;S+k%YC|68xvDWuRHM<KUV90)4j*5&!@I literal 0 HcmV?d00001 diff --git a/src/App.vue b/src/App.vue index 8a18e7e..f770b26 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,7 +5,7 @@ import ProjectExplorer from './components/ProjectExplorer.vue'; import { computed, ref, onMounted, onUnmounted } from "vue"; import { useFilesStore } from "@/stores/files"; import Vue3DraggableResizable from 'vue3-draggable-resizable'; -import {TabPaneName, TabsPaneContext} from "element-plus"; +import { TabPaneName, TabsPaneContext } from "element-plus"; const filesStore = useFilesStore(); @@ -21,12 +21,49 @@ const onResizing = (x, y, width, height) => { height.value = height; }; +const handleExport = () => { + const dataStr = JSON.stringify(filesStore.fileList, null, 2); + const blob = new Blob([dataStr], { type: 'application/json;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = 'files.json'; + link.click(); + URL.revokeObjectURL(url); +}; + const onHandleInport = (file) => { - yysRef.value.importGroups(file); + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = JSON.parse(e.target.result as string); + if (data[0].visible === true) { + // 新版本格式:直接替换 fileList + filesStore.$patch({ fileList: data }); + } else { + // 旧版本格式:仅包含 groups 数组 + const newFile = { + label: `File ${filesStore.fileList.length + 1}`, + name: String(filesStore.fileList.length + 1), + visible: true, + groups: data + }; + filesStore.addFile(newFile); + } + } catch (error) { + console.error('Failed to import file', error); + } + }; + reader.readAsText(file); }; +// const onHandleInport = (file) => { +// +// handleImport(file); +// }; + const onHandleExport = () => { - yysRef.value.exportGroups(); + handleExport(); }; const element = ref({ @@ -39,20 +76,18 @@ const element = ref({ const handleFileSelected = (fileId) => { filesStore.setActiveFile(fileId); + filesStore.setVisible(fileId, true); }; const handleTabsEdit = ( - targetName: TabPaneName | undefined, + targetName: String | undefined, action: 'remove' | 'add' -)=> { - const tabIndex = filesStore.fileList.findIndex(file => file.name === parseInt(name.toString())); - if (tabIndex !== -1) { - filesStore.fileList.splice(tabIndex, 1); - if (filesStore.fileList.length > 0) { - filesStore.setActiveFile(filesStore.fileList[0].name); - } else { - filesStore.setActiveFile(-1); // 或者其他适当的值表示没有活动文件 - } +) => { + if (action === 'remove') { + filesStore.closeTab(targetName); + } else if (action === 'add') { + const newFileName = `File ${filesStore.fileList.length + 1}`; + filesStore.addFile({ label: newFileName, name: newFileName }); } }; @@ -67,6 +102,11 @@ onUnmounted(() => { windowHeight.value = window.innerHeight; }); }); + +const activeFileGroups = computed(() => { + const activeFile = filesStore.fileList.find(file => file.name === filesStore.activeFile); + return activeFile ? activeFile.groups : []; +}); </script> <template> @@ -77,7 +117,7 @@ onUnmounted(() => { <div class="main-content"> <!-- 侧边栏 --> <aside class="sidebar"> - <ProjectExplorer :files="filesStore.fileList" @file-selected="handleFileSelected" /> + <ProjectExplorer :allFiles="filesStore.fileList" @file-selected="handleFileSelected" /> </aside> <!-- 工作区 --> @@ -91,12 +131,12 @@ onUnmounted(() => { @edit="handleTabsEdit" > <el-tab-pane - v-for="(file, index) in filesStore.fileList" + v-for="(file, index) in filesStore.visibleFiles" :key="index" :label="file.label" :name="file.name.toString()" > - <Yys ref="yysRef" /> + <Yys :groups="activeFileGroups" ref="yysRef" /> </el-tab-pane> </el-tabs> </main> diff --git a/src/components/ProjectExplorer.vue b/src/components/ProjectExplorer.vue index 377a62f..59d636b 100644 --- a/src/components/ProjectExplorer.vue +++ b/src/components/ProjectExplorer.vue @@ -1,7 +1,7 @@ <template> <div class="project-explorer"> <el-tree - :data="files" + :data="allFiles" :props="defaultProps" @node-click="handleNodeClick" /> @@ -9,10 +9,10 @@ </template> <script setup lang="ts"> -import { ref } from 'vue'; +import { defineProps, defineEmits } from 'vue'; const props = defineProps({ - files: { + allFiles: { type: Array, required: true, }, diff --git a/src/components/ShikigamiProperty.vue b/src/components/ShikigamiProperty.vue index 80846b3..7b30a20 100644 --- a/src/components/ShikigamiProperty.vue +++ b/src/components/ShikigamiProperty.vue @@ -130,6 +130,7 @@ import YuhunSelect from "@/components/YuhunSelect.vue"; import {useI18n} from 'vue-i18n' // import YuhunSelect from "./YuhunSelect.vue"; + // 获取当前的 i18n 实例 const {t} = useI18n() @@ -339,7 +340,7 @@ const cancel = () => { const confirm = () => { shikigami.value.edit = true - emit('updateProperty', JSON.parse(JSON.stringify(shikigami.value))) + emit('updateProperty', shikigami.value); resetData() } diff --git a/src/components/Toolbar.vue b/src/components/Toolbar.vue index 7d4e127..9fe7f25 100644 --- a/src/components/Toolbar.vue +++ b/src/components/Toolbar.vue @@ -8,14 +8,37 @@ t('setWatermark') }} </el-button> + <!-- 新增的按钮 --> + <el-button type="info" @click="showUpdateLog">更新日志</el-button> + <el-button type="warning" @click="showFeedbackForm">问题反馈</el-button> </div> + <!-- 更新日志对话框 --> + <el-dialog v-model="state.showUpdateLogDialog" title="更新日志" width="60%"> + <ul> + <li v-for="(log, index) in updateLogs" :key="index"> + <strong>版本 {{ log.version }} - {{ log.date }}</strong> + <ul> + <li v-for="(change, idx) in log.changes" :key="idx">{{ change }}</li> + </ul> + </li> + </ul> + </el-dialog> + + <!-- 更新日志对话框 --> + <el-dialog v-model="state.showFeedbackFormDialog" title="更新日志" width="60%"> + <span style="font-size: 24px;">备注阴阳师</span> + <br/> + <img src="/assets/Other/Contact.png" + style="cursor: pointer; vertical-align: bottom; width: 200px; height: auto;"/> + </el-dialog> + <!-- 预览弹窗 --> - <el-dialog id="preview-container" v-model="state.previewVisible" width="80%" height="80%" :before-close="handleClose"> + <el-dialog id="preview-container" v-model="state.previewVisible" width="80%" height="80%" + :before-close="handleClose"> <div style="max-height: 500px; overflow-y: auto;"> <img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; display: block;"/> </div> - <!-- <img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; height: auto;" />--> <span slot="footer" class="dialog-footer"> <el-button @click="state.previewVisible = false">取 消</el-button> <el-button type="primary" @click="downloadImage">下 载</el-button> @@ -62,15 +85,46 @@ import {useI18n} from 'vue-i18n'; // 获取当前的 i18n 实例 const {t} = useI18n(); -const emit = defineEmits(['handleExport', 'handleImport']) +const emit = defineEmits(['handleExport', 'handleImport']); // 定义响应式数据 const state = reactive({ previewImage: null, // 用于存储预览图像的数据URL previewVisible: false, // 控制预览弹窗的显示状态 - showWatermarkDialog: false, // 控制水印设置弹窗的显示状态 + showWatermarkDialog: false, // 控制水印设置弹窗的显示状态, + showUpdateLogDialog: false, // 控制更新日志对话框的显示状态 + showFeedbackFormDialog: false, // 控制反馈表单对话框的显示状态 }); +// 版本记录数据 +const updateLogs = [ + { + version: '2.0.0', + date: '2025-03-16', + changes: [ + '修复了相同式神不能正确设置属性的问题', + '支持了多文件编辑', + 'PS:当前导出截图宽度无法' + ] + }, + { + version: '1.0.0', + date: '2025-03-09', + changes: [ + '首次发布' + ] + }, + +]; + +const showUpdateLog = () => { + state.showUpdateLogDialog = !state.showUpdateLogDialog; +}; + +const showFeedbackForm = () => { + state.showFeedbackFormDialog = !state.showFeedbackFormDialog; +}; + const handleExport = () => { emit('handleExport'); }; @@ -86,7 +140,6 @@ const handleImport = () => { input.click(); }; - const watermark = reactive({ text: '示例水印', fontSize: 30, @@ -150,7 +203,6 @@ function calculateVisualHeight(selector) { return rows.reduce((sum, row) => sum + row.maxHeight, 0); } - const ignoreElements = (element) => { return element.classList.contains('ql-toolbar') || element.classList.contains('el-tabs__header'); }; @@ -248,6 +300,7 @@ const prepareCapture = async () => { document.head.removeChild(style); } }; + const downloadImage = () => { if (state.previewImage) { const link = document.createElement('a'); @@ -272,7 +325,6 @@ const handleClose = (done) => { right: 0; height: 48px; background: #f8f8f8; - //border-bottom: 1px solid #eee; display: flex; align-items: center; padding: 0 8px; z-index: 100; diff --git a/src/components/Yys.vue b/src/components/Yys.vue index a6cceb8..93e831b 100644 --- a/src/components/Yys.vue +++ b/src/components/Yys.vue @@ -14,7 +14,7 @@ /> - <draggable :list="state.groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;" + <draggable :list="groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;" handle=".drag-handle"> @@ -126,7 +126,7 @@ </div> </template> -<script setup> +<script setup lang="ts"> import {ref, reactive, toRefs} from 'vue'; import draggable from 'vuedraggable'; import ShikigamiSelect from './ShikigamiSelect.vue'; @@ -139,35 +139,15 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue' import shikigamiData from '../data/Shikigami.json'; import _ from 'lodash'; +const props = defineProps<{ + groups: any[]; +}>(); + const dialogTableVisible = ref(false) // 定义响应式数据 const state = reactive({ showSelectShikigami: false, showProperty: false, - groups: [ - { - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - }, - { - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - },{ - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - },{ - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - },{ - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - }, - ], groupIndex: 0, positionIndex: 0, currentShikigami: {}, @@ -187,9 +167,9 @@ const copy = (str) => { const paste = (groupIndex, type) => { if ('shortDescription' == type) - state.groups[groupIndex].shortDescription = clipboard.value + props.groups[groupIndex].shortDescription = clipboard.value else if ('details' == type) - state.groups[groupIndex].details = clipboard.value + props.groups[groupIndex].details = clipboard.value } // 定义工具栏选项 @@ -223,23 +203,23 @@ const editShikigami = (groupIndex, positionIndex) => { state.showSelectShikigami = true; state.groupIndex = groupIndex; state.positionIndex = positionIndex; - state.currentShikigami = state.groups[groupIndex].groupInfo[positionIndex]; + state.currentShikigami = props.groups[groupIndex].groupInfo[positionIndex]; }; const updateShikigami = (shikigami) => { console.log("parent====> ", shikigami); state.showSelectShikigami = false; - const oldProperties = state.groups[state.groupIndex].groupInfo[state.positionIndex].properties; - state.groups[state.groupIndex].groupInfo[state.positionIndex] = _.cloneDeep(shikigami); - state.groups[state.groupIndex].groupInfo[state.positionIndex].properties = oldProperties; + const oldProperties = props.groups[state.groupIndex].groupInfo[state.positionIndex].properties; + props.groups[state.groupIndex].groupInfo[state.positionIndex] = _.cloneDeep(shikigami); + props.groups[state.groupIndex].groupInfo[state.positionIndex].properties = oldProperties; }; const editProperty = (groupIndex, positionIndex) => { state.showProperty = true; state.groupIndex = groupIndex; state.positionIndex = positionIndex; - state.currentShikigami = state.groups[groupIndex].groupInfo[positionIndex]; + state.currentShikigami = props.groups[groupIndex].groupInfo[positionIndex]; }; const closeProperty = () => { @@ -250,19 +230,19 @@ const closeProperty = () => { const updateProperty = (property) => { state.showProperty = false; state.currentShikigami = {}; - state.groups[state.groupIndex].groupInfo[state.positionIndex].properties = _.cloneDeep(property); + props.groups[state.groupIndex].groupInfo[state.positionIndex].properties = _.cloneDeep(property); }; const removeGroupElement = (groupIndex, positionIndex) => { - state.groups[groupIndex].groupInfo.splice(positionIndex, 1); + props.groups[groupIndex].groupInfo.splice(positionIndex, 1); }; const removeGroup = (groupIndex) => { - state.groups.splice(groupIndex, 1); + props.groups.splice(groupIndex, 1); }; const addGroup = () => { - state.groups.push({ + props.groups.push({ shortDescription: '', groupInfo: [{}, {}, {}, {}, {}], details: '' @@ -270,12 +250,12 @@ const addGroup = () => { }; const addGroupElement = (groupIndex) => { - state.groups[groupIndex].groupInfo.push({}); + props.groups[groupIndex].groupInfo.push({}); }; const exportGroups = () => { - const dataStr = JSON.stringify(state.groups, null, 2); + const dataStr = JSON.stringify(props.groups, null, 2); const blob = new Blob([dataStr], {type: 'application/json'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); @@ -326,7 +306,7 @@ const importGroups = (file) => { reader.onload = (e) => { try { const importedData = JSON.parse(e.target.result); - state.groups = importedData; + props.groups = importedData; ElMessage.success('导入成功'); } catch (error) { ElMessage.error('文件格式错误'); diff --git a/src/stores/files.ts b/src/stores/files.ts index fd93c71..19c95a8 100644 --- a/src/stores/files.ts +++ b/src/stores/files.ts @@ -1,16 +1,69 @@ -import { defineStore } from 'pinia'; +import {defineStore} from 'pinia'; export const useFilesStore = defineStore('files', { state: () => ({ - fileList: [{ label: 'File 1', name: 1 },{ label: 'File 2', name: 2 }], - activeFile: 1, + fileList: [ + { + label: 'File 1', + name: "1", + visible: true, + groups: [ + { + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + }, + { + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + } + ] + }, { + label: 'File 2', + name: "2", + visible: true, + groups:[ + { + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + }, + { + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + } + ] + }], + activeFile: "1", }), + getters: { + visibleFiles: (state) => state.fileList.filter(file => file.visible), + }, actions: { addFile(file: { label: string; name: number }) { - this.fileList.push(file); + this.fileList.push({...file, visible: true}); + this.activeFile = file.name; }, setActiveFile(fileId: number) { this.activeFile = fileId; }, + setVisible(fileId: number, visibility: boolean) { + const file = this.fileList.find(file => file.name === fileId); + if (file) { + file.visible = visibility; + } + }, + closeTab(fileName: String) { + const file = this.fileList.find(file => file.name === fileName); + if (file) { + file.visible = false; + if (this.activeFile === fileName) { + const nextVisibleFile = this.visibleFiles[0]; + this.activeFile = nextVisibleFile ? nextVisibleFile.name : -1; + } + } + }, }, }); \ No newline at end of file From d788fd08677fd56156b03ae871cfbaab0187ce4a Mon Sep 17 00:00:00 2001 From: rookie4show <rookie4show@gmail.com> Date: Mon, 17 Mar 2025 00:08:29 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6=E5=92=8C=E6=96=B0=E5=A2=9E=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 22 +++++++++++++++++++++- src/stores/files.ts | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/App.vue b/src/App.vue index f770b26..a8b7b97 100644 --- a/src/App.vue +++ b/src/App.vue @@ -87,7 +87,24 @@ const handleTabsEdit = ( filesStore.closeTab(targetName); } else if (action === 'add') { const newFileName = `File ${filesStore.fileList.length + 1}`; - filesStore.addFile({ label: newFileName, name: newFileName }); + + filesStore.addFile({ + label: newFileName, + name: newFileName, + visible: true, + groups:[ + { + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + }, + { + shortDescription: '', + groupInfo: [{}, {}, {}, {}, {}], + details: '' + } + ] + }); } }; @@ -181,5 +198,8 @@ const activeFileGroups = computed(() => { position: relative; height: 100%; /* 确保内容区域占满父容器 */ overflow-y: auto; /* 允许内容滚动 */ + min-height: 100vh; /* 允许容器扩展 */ + display: inline-block; + max-width: 100%; } </style> \ No newline at end of file diff --git a/src/stores/files.ts b/src/stores/files.ts index 19c95a8..a0cddcb 100644 --- a/src/stores/files.ts +++ b/src/stores/files.ts @@ -42,7 +42,7 @@ export const useFilesStore = defineStore('files', { visibleFiles: (state) => state.fileList.filter(file => file.visible), }, actions: { - addFile(file: { label: string; name: number }) { + addFile(file) { this.fileList.push({...file, visible: true}); this.activeFile = file.name; }, From 56998e58244d12901476f827568b96f8f3f54039 Mon Sep 17 00:00:00 2001 From: zhangqi <395733086@qq.com> Date: Mon, 17 Mar 2025 17:04:58 +0800 Subject: [PATCH 4/7] refine: modify class --- src/components/Yys.vue | 119 +++++++++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 35 deletions(-) diff --git a/src/components/Yys.vue b/src/components/Yys.vue index 93e831b..aaffc51 100644 --- a/src/components/Yys.vue +++ b/src/components/Yys.vue @@ -18,12 +18,12 @@ handle=".drag-handle"> - <template #item="{ element: group, index: groupIndex }"> + <template class="group" #item="{ element: group, index: groupIndex }"> <el-row :span="24"> - <div> - <div> - <div style="display: flex; justify-content: space-between;" data-html2canvas-ignore="true"> - <div> + <div class="group-item"> + <div class="group-header"> + <div class="group-opt" data-html2canvas-ignore="true"> + <div class="opt-left"> <el-button type="primary" icon="CopyDocument" @click="copy(group.shortDescription)">{{ t('Copy') }} </el-button> <el-button type="primary" icon="Document" @click="paste(groupIndex,'shortDescription')">{{ @@ -31,7 +31,7 @@ }} </el-button> </div> - <div> + <div class="opt-right"> <el-button class="drag-handle" type="primary" icon="Rank" circle></el-button> <el-button type="primary" icon="Plus" circle @click="addGroup"></el-button> <el-button type="danger" icon="Delete" circle @click="removeGroup(groupIndex)"></el-button> @@ -40,20 +40,17 @@ <QuillEditor v-model:content="group.shortDescription" contentType="html" theme="snow" :toolbar="toolbarOptions"/> </div> - <div> - <draggable :list="group.groupInfo" item-key="name" style="display: flex; flex-direction: row; width: 20%;"> + <div class="group-body"> + <draggable :list="group.groupInfo" item-key="name" class="body-content"> <template #item="{element : position, index:positionIndex}"> - <div> - <el-col> - <el-card shadow="never" - :body-style="{ display: 'flex', 'flex-direction': 'column', 'justify-content': 'center', 'align-items': 'center' }"> - - <div data-html2canvas-ignore="true"> + <el-col> + <el-card class="group-card" shadow="never"> + <div class="opt-btn" data-html2canvas-ignore="true"> <!-- Add delete button here --> <el-button type="danger" icon="Delete" circle @click="removeGroupElement(groupIndex, positionIndex)"/> <el-button type="primary" icon="Plus" circle @click="addGroupElement(groupIndex)"/> </div> - <div style="position: relative; display: inline-block;"> + <div class="avatar-container"> <!-- 头像图片 --> <img :src="position.avatar || '/assets/Shikigami/default.png'" style="cursor: pointer; vertical-align: bottom;" @@ -61,15 +58,10 @@ @click="editShikigami(groupIndex, positionIndex)"/> <!-- 文字图层 --> - <span v-if="position.properties" - style="position: absolute; bottom: 0; left: 50%; transform: translateX(-50%) translateY(50%); - font-size: 24px; color: white; text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, 1px 1px 0 black; - white-space: nowrap; padding: 0 8px; margin: 0; display: flex; align-items: center; justify-content: center;"> - {{ position.properties.levelRequired }}级 {{ position.properties.skillRequired.join('') }} - </span> + <span v-if="position.properties">{{ position.properties.levelRequired }}级 {{ position.properties.skillRequired.join('') }}</span> </div> - <div style="padding: 14px; width: 120px;"> + <div class="property-wrap"> <div style="display: flex; justify-content: center;" data-html2canvas-ignore="true"> <span>{{ position.name || "" }}</span> </div> @@ -96,13 +88,11 @@ </div> </el-card> </el-col> - </div> </template> </draggable> - </div> - <div> - <div data-html2canvas-ignore="true"> + <div class="group-footer"> + <div class="opt-left" data-html2canvas-ignore="true"> <el-button type="primary" icon="CopyDocument" @click="copy(group.details)">{{ t('Copy') }}</el-button> <el-button type="primary" icon="Document" @click="paste(groupIndex,'details')">{{ t('Paste') @@ -111,19 +101,10 @@ </div> <QuillEditor v-model:content="group.details" contentType="html" theme="snow" :toolbar="toolbarOptions"/> </div> - </div> </el-row> - - </template> - - <!-- </el-row>--> </draggable> - <div style="margin: 20px"> - - - </div> </template> <script setup lang="ts"> @@ -361,5 +342,73 @@ defineExpose({ border: 0; } +.group-header { + padding: 10px; +} + +.group-opt { + padding: 10px; + display: flex; + justify-content: space-between; +} + +.group-body { + padding: 20px; + width: 80%; +} + +.body-content { + display: flex; + flex-direction: row; + width: 20%; +} + +.group-card { + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + .avatar-container { + position: relative; + } + + .avatar-container span { + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%) translateY(50%); + font-size: 24px; + color: white; + text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, 1px 1px 0 black; + white-space: nowrap; + padding: 0 8px; + margin: 0; + display: flex; + align-items: center; + justify-content: center; + } + + .opt-btn { + position: absolute; + top: -10px; + opacity: 0; + } +} + +.property-wrap { + margin: 10px 0; +} + +/* 当鼠标悬停在容器上时显示按钮 */ +.group-card:hover .opt-btn { + opacity: 1; +} + +.group-footer { + margin: 10px; + padding: 10px; +} </style> From 20232a5d7cf1ffbaca3cecc6b5d69d33a45da72d Mon Sep 17 00:00:00 2001 From: rookie4show <rookie4show@gmail.com> Date: Mon, 17 Mar 2025 18:30:34 +0800 Subject: [PATCH 5/7] =?UTF-8?q?1=E3=80=82=E6=A0=B9=E6=8D=AEpinia=E8=B0=83?= =?UTF-8?q?=E6=95=B4toolbar=E5=92=8CprojecExplorer=202.=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E5=A2=9E=E5=8A=A0=E7=A1=AE=E8=AE=A4=EF=BC=8C?= =?UTF-8?q?=E7=A6=81=E6=AD=A2=E5=88=A0=E9=99=A4=E6=9C=80=E5=90=8E=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/App.vue | 54 +------------------------- src/components/ProjectExplorer.vue | 7 +++- src/components/Toolbar.vue | 61 ++++++++++++++++++------------ src/components/Yys.vue | 54 ++++++++++++++++++++++++-- src/data/updateLog.json | 18 +++++++++ 6 files changed, 114 insertions(+), 81 deletions(-) create mode 100644 src/data/updateLog.json diff --git a/.gitignore b/.gitignore index d51dd05..891cb3d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /node_modules /.idea /dist +/dist.zip diff --git a/src/App.vue b/src/App.vue index a8b7b97..b4c332d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -21,51 +21,6 @@ const onResizing = (x, y, width, height) => { height.value = height; }; -const handleExport = () => { - const dataStr = JSON.stringify(filesStore.fileList, null, 2); - const blob = new Blob([dataStr], { type: 'application/json;charset=utf-8' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = 'files.json'; - link.click(); - URL.revokeObjectURL(url); -}; - -const onHandleInport = (file) => { - const reader = new FileReader(); - reader.onload = (e) => { - try { - const data = JSON.parse(e.target.result as string); - if (data[0].visible === true) { - // 新版本格式:直接替换 fileList - filesStore.$patch({ fileList: data }); - } else { - // 旧版本格式:仅包含 groups 数组 - const newFile = { - label: `File ${filesStore.fileList.length + 1}`, - name: String(filesStore.fileList.length + 1), - visible: true, - groups: data - }; - filesStore.addFile(newFile); - } - } catch (error) { - console.error('Failed to import file', error); - } - }; - reader.readAsText(file); -}; - -// const onHandleInport = (file) => { -// -// handleImport(file); -// }; - -const onHandleExport = () => { - handleExport(); -}; - const element = ref({ x: 400, y: 20, @@ -74,11 +29,6 @@ const element = ref({ isActive: false, }); -const handleFileSelected = (fileId) => { - filesStore.setActiveFile(fileId); - filesStore.setVisible(fileId, true); -}; - const handleTabsEdit = ( targetName: String | undefined, action: 'remove' | 'add' @@ -129,12 +79,12 @@ const activeFileGroups = computed(() => { <template> <div class="container"> <!-- 工具栏 --> - <Toolbar title="yys-editor" username="示例用户" @handleExport="onHandleExport" @handleImport="onHandleInport" /> + <Toolbar title="yys-editor" username="示例用户"/> <!-- 侧边栏和工作区 --> <div class="main-content"> <!-- 侧边栏 --> <aside class="sidebar"> - <ProjectExplorer :allFiles="filesStore.fileList" @file-selected="handleFileSelected" /> + <ProjectExplorer :allFiles="filesStore.fileList" /> </aside> <!-- 工作区 --> diff --git a/src/components/ProjectExplorer.vue b/src/components/ProjectExplorer.vue index 59d636b..6d709b0 100644 --- a/src/components/ProjectExplorer.vue +++ b/src/components/ProjectExplorer.vue @@ -10,6 +10,9 @@ <script setup lang="ts"> import { defineProps, defineEmits } from 'vue'; +import { useFilesStore } from "@/stores/files"; + +const filesStore = useFilesStore(); const props = defineProps({ allFiles: { @@ -18,7 +21,6 @@ const props = defineProps({ }, }); -const emit = defineEmits(['file-selected']); const defaultProps = { children: 'children', @@ -26,7 +28,8 @@ const defaultProps = { }; const handleNodeClick = (data) => { - emit('file-selected', data.name); + filesStore.setActiveFile(data.name); + filesStore.setVisible(data.name, true); }; </script> diff --git a/src/components/Toolbar.vue b/src/components/Toolbar.vue index 9fe7f25..ce151b2 100644 --- a/src/components/Toolbar.vue +++ b/src/components/Toolbar.vue @@ -78,14 +78,17 @@ </div> </template> -<script setup> +<script setup lang="ts"> import {ref, reactive} from 'vue'; import html2canvas from "html2canvas"; import {useI18n} from 'vue-i18n'; +import updateLogs from "../data/updateLog.json" +import {useFilesStore} from "@/stores/files"; + +const filesStore = useFilesStore(); // 获取当前的 i18n 实例 const {t} = useI18n(); -const emit = defineEmits(['handleExport', 'handleImport']); // 定义响应式数据 const state = reactive({ @@ -96,26 +99,6 @@ const state = reactive({ showFeedbackFormDialog: false, // 控制反馈表单对话框的显示状态 }); -// 版本记录数据 -const updateLogs = [ - { - version: '2.0.0', - date: '2025-03-16', - changes: [ - '修复了相同式神不能正确设置属性的问题', - '支持了多文件编辑', - 'PS:当前导出截图宽度无法' - ] - }, - { - version: '1.0.0', - date: '2025-03-09', - changes: [ - '首次发布' - ] - }, - -]; const showUpdateLog = () => { state.showUpdateLogDialog = !state.showUpdateLogDialog; @@ -126,7 +109,14 @@ const showFeedbackForm = () => { }; const handleExport = () => { - emit('handleExport'); + const dataStr = JSON.stringify(filesStore.fileList, null, 2); + const blob = new Blob([dataStr], { type: 'application/json;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = 'files.json'; + link.click(); + URL.revokeObjectURL(url); }; const handleImport = () => { @@ -135,7 +125,30 @@ const handleImport = () => { input.accept = '.json'; input.onchange = (e) => { const file = e.target.files[0]; - if (file) emit('handleImport', file); + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = JSON.parse(e.target.result as string); + if (data[0].visible === true) { + // 新版本格式:直接替换 fileList + filesStore.$patch({ fileList: data }); + } else { + // 旧版本格式:仅包含 groups 数组 + const newFile = { + label: `File ${filesStore.fileList.length + 1}`, + name: String(filesStore.fileList.length + 1), + visible: true, + groups: data + }; + filesStore.addFile(newFile); + } + } catch (error) { + console.error('Failed to import file', error); + } + }; + reader.readAsText(file); + } }; input.click(); }; diff --git a/src/components/Yys.vue b/src/components/Yys.vue index aaffc51..71b9a40 100644 --- a/src/components/Yys.vue +++ b/src/components/Yys.vue @@ -119,6 +119,7 @@ import '@vueup/vue-quill/dist/vue-quill.snow.css' // 引入样式文件 import * as ElementPlusIconsVue from '@element-plus/icons-vue' import shikigamiData from '../data/Shikigami.json'; import _ from 'lodash'; +import {Action, ElMessage, ElMessageBox} from "element-plus"; const props = defineProps<{ groups: any[]; @@ -215,11 +216,57 @@ const updateProperty = (property) => { }; const removeGroupElement = (groupIndex, positionIndex) => { - props.groups[groupIndex].groupInfo.splice(positionIndex, 1); + const group = props.groups[groupIndex]; + + if (group.groupInfo.length === 1) { + ElMessageBox.alert('无法删除', '提示', { + confirmButtonText: '确定', + }); + return; + } + + ElMessageBox.confirm('确定要删除此元素吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }).then(() => { + group.groupInfo.splice(positionIndex, 1); + ElMessage({ + type: 'success', + message: '删除成功!', + }); + }).catch(() => { + ElMessage({ + type: 'info', + message: '已取消删除', + }); + }); }; const removeGroup = (groupIndex) => { - props.groups.splice(groupIndex, 1); + if (props.groups.length === 1) { + ElMessageBox.alert('无法删除最后一个队伍', '提示', { + confirmButtonText: '确定', + }); + return; + } + + ElMessageBox.confirm('确定要删除此组吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }).then(() => { + props.groups.splice(groupIndex, 1); + ElMessage({ + type: 'success', + message: '删除成功!', + }); + }).catch(() => { + ElMessage({ + type: 'info', + message: '已取消删除', + }); + }); }; const addGroup = () => { @@ -392,7 +439,8 @@ defineExpose({ .opt-btn { position: absolute; - top: -10px; + top: 0px; + z-index: 10; opacity: 0; } } diff --git a/src/data/updateLog.json b/src/data/updateLog.json new file mode 100644 index 0000000..5f45dd8 --- /dev/null +++ b/src/data/updateLog.json @@ -0,0 +1,18 @@ +[ + { + "version": "2.0.0", + "date": "2025-03-16", + "changes": [ + "修复了相同式神不能正确设置属性的问题", + "支持了多文件编辑", + "PS:当前导出截图宽度无法" + ] + }, + { + "version": "1.0.0", + "date": "2025-03-09", + "changes": [ + "首次发布" + ] + } +] \ No newline at end of file From 5336b57d9bdc7e0ea85378752f86216dedd433c7 Mon Sep 17 00:00:00 2001 From: rookie4show <rookie4show@gmail.com> Date: Tue, 18 Mar 2025 13:32:11 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=BC=94=E7=A4=BA?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=EF=BC=8C=E4=BF=AE=E5=A4=8Dcss=E5=BC=95?= =?UTF-8?q?=E8=B5=B7=E7=9A=84=E7=9B=B8=E5=85=B3=E6=95=85=E9=9A=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 34 +++++++++++++++++----------------- src/components/Yys.vue | 8 ++++++-- src/stores/files.ts | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/App.vue b/src/App.vue index b4c332d..97c2adc 100644 --- a/src/App.vue +++ b/src/App.vue @@ -89,24 +89,24 @@ const activeFileGroups = computed(() => { <!-- 工作区 --> <div class="workspace"> - <main id="main-container" :style="{ height: contentHeight, overflow: 'auto' }"> - <el-tabs - v-model="filesStore.activeFile" - type="card" - class="demo-tabs" - editable - @edit="handleTabsEdit" + <el-tabs + v-model="filesStore.activeFile" + type="card" + class="demo-tabs" + editable + @edit="handleTabsEdit" + > + <el-tab-pane + v-for="(file, index) in filesStore.visibleFiles" + :key="index" + :label="file.label" + :name="file.name.toString()" > - <el-tab-pane - v-for="(file, index) in filesStore.visibleFiles" - :key="index" - :label="file.label" - :name="file.name.toString()" - > - <Yys :groups="activeFileGroups" ref="yysRef" /> - </el-tab-pane> - </el-tabs> - </main> + <main id="main-container" :style="{ height: contentHeight, overflow: 'auto' }"> + <Yys :groups="activeFileGroups" ref="yysRef"/> + </main> + </el-tab-pane> + </el-tabs> </div> </div> </div> diff --git a/src/components/Yys.vue b/src/components/Yys.vue index 71b9a40..67f5d5d 100644 --- a/src/components/Yys.vue +++ b/src/components/Yys.vue @@ -43,7 +43,8 @@ <div class="group-body"> <draggable :list="group.groupInfo" item-key="name" class="body-content"> <template #item="{element : position, index:positionIndex}"> - <el-col> + <div> + <el-col> <el-card class="group-card" shadow="never"> <div class="opt-btn" data-html2canvas-ignore="true"> <!-- Add delete button here --> @@ -88,6 +89,7 @@ </div> </el-card> </el-col> + </div> </template> </draggable> </div> @@ -390,7 +392,8 @@ defineExpose({ } .group-header { - padding: 10px; + margin: 10px; + padding: 10px; } .group-opt { @@ -419,6 +422,7 @@ defineExpose({ .avatar-container { position: relative; + min-width: 100px; } .avatar-container span { diff --git a/src/stores/files.ts b/src/stores/files.ts index a0cddcb..dae4bdf 100644 --- a/src/stores/files.ts +++ b/src/stores/files.ts @@ -4,13 +4,42 @@ export const useFilesStore = defineStore('files', { state: () => ({ fileList: [ { - label: 'File 1', + label: 'Welcome', name: "1", visible: true, groups: [ { - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], + shortDescription: '<h1>鬼灵歌姬</h1><p>这是一个演示项目,用于测试显示效果</p>', + groupInfo: [{ + "avatar": "/assets/Shikigami/sp/372.png", + "name": "因幡辉夜姬", + "rarity": "SP" + }, + { + "avatar": "/assets/Shikigami/ssr/356.png", + "name": "千姬", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/sp/554.png", + "name": "纺愿缘结神", + "rarity": "SP" + }, + { + "avatar": "/assets/Shikigami/ssr/556.png", + "name": "天照", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/ssr/557.png", + "name": "伊邪那美", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/sp/367.png", + "name": "绘世花鸟卷", + "rarity": "SP" + }], details: '' }, { From 74d8d556327a3ec3d7d9e9611b520dd39b1f45eb Mon Sep 17 00:00:00 2001 From: rookie4show <rookie4show@gmail.com> Date: Tue, 18 Mar 2025 15:04:46 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=A4=9A=E6=96=87=E4=BB=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E4=BC=98=E5=8C=96=EF=BC=8C=E9=83=A8=E5=88=86=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 14 +- src/components/ProjectExplorer.vue | 73 ++++++++- src/components/Toolbar.vue | 2 +- src/components/Yys.vue | 66 +++----- src/data/updateLog.json | 6 +- src/main.js | 11 +- src/stores/files.ts | 98 ----------- src/ts/files.ts | 252 +++++++++++++++++++++++++++++ src/ts/useGlobalMessage.ts | 14 ++ 9 files changed, 383 insertions(+), 153 deletions(-) delete mode 100644 src/stores/files.ts create mode 100644 src/ts/files.ts create mode 100644 src/ts/useGlobalMessage.ts diff --git a/src/App.vue b/src/App.vue index 97c2adc..fe674fa 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,7 +3,7 @@ import Yys from './components/Yys.vue'; import Toolbar from './components/Toolbar.vue'; import ProjectExplorer from './components/ProjectExplorer.vue'; import { computed, ref, onMounted, onUnmounted } from "vue"; -import { useFilesStore } from "@/stores/files"; +import { useFilesStore } from "@/ts/files"; import Vue3DraggableResizable from 'vue3-draggable-resizable'; import { TabPaneName, TabsPaneContext } from "element-plus"; @@ -42,9 +42,10 @@ const handleTabsEdit = ( label: newFileName, name: newFileName, visible: true, + type:'PVE', groups:[ { - shortDescription: '', + shortDescription: " ", groupInfo: [{}, {}, {}, {}, {}], details: '' }, @@ -98,12 +99,12 @@ const activeFileGroups = computed(() => { > <el-tab-pane v-for="(file, index) in filesStore.visibleFiles" - :key="index" + :key="`${file.name}-${filesStore.activeFile}`" :label="file.label" :name="file.name.toString()" > <main id="main-container" :style="{ height: contentHeight, overflow: 'auto' }"> - <Yys :groups="activeFileGroups" ref="yysRef"/> + <Yys :groups="activeFileGroups"/> </main> </el-tab-pane> </el-tabs> @@ -133,15 +134,16 @@ const activeFileGroups = computed(() => { } .sidebar { - width: 200px; /* 侧边栏宽度 */ + width: 20%; /* 侧边栏宽度 */ background-color: #f0f0f0; /* 背景色 */ flex-shrink: 0; /* 防止侧边栏被压缩 */ overflow-y: auto; /* 允许侧边栏内容滚动 */ } .workspace { - flex: 1; /* 占据剩余空间 */ + //flex: 1; /* 占据剩余空间 */ overflow: hidden; /* 防止内容溢出 */ + display: inline-block; } #main-container { diff --git a/src/components/ProjectExplorer.vue b/src/components/ProjectExplorer.vue index 6d709b0..d23fade 100644 --- a/src/components/ProjectExplorer.vue +++ b/src/components/ProjectExplorer.vue @@ -4,13 +4,38 @@ :data="allFiles" :props="defaultProps" @node-click="handleNodeClick" - /> + node-key="name" + default-expand-all + :expand-on-click-node="false" + > + <template #default="{ node, data }"> + <div class="custom-tree-node"> + <span>{{ node.label }}</span> + <div> + <el-dropdown> + <el-button + style="margin-left: 4px" + icon="MoreFilled" + link + /> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item @click="() => renameFile(data)">Rename</el-dropdown-item> + <el-dropdown-item @click="() => deleteFile(data)">Delete</el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + </div> + </div> + </template> + </el-tree> </div> </template> <script setup lang="ts"> -import { defineProps, defineEmits } from 'vue'; -import { useFilesStore } from "@/stores/files"; +import {defineProps, defineEmits, ref} from 'vue'; +import {useFilesStore} from "@/ts/files"; +import {ElTree, ElButton, ElDropdownMenu, ElDropdownItem} from 'element-plus'; const filesStore = useFilesStore(); @@ -21,21 +46,61 @@ const props = defineProps({ }, }); - const defaultProps = { children: 'children', label: 'label', }; +const visibleDropdown = ref(false); +const dropdownLeft = ref(0); +const dropdownTop = ref(0); +const selectedData = ref(null); + const handleNodeClick = (data) => { filesStore.setActiveFile(data.name); filesStore.setVisible(data.name, true); }; + +const showOptions = (node, data) => { + visibleDropdown.value = true; + dropdownLeft.value = node.$el.offsetLeft + 20; + dropdownTop.value = node.$el.offsetTop + 20; + selectedData.value = data; +}; + +const hideDropdown = () => { + visibleDropdown.value = false; +}; + +document.addEventListener('click', hideDropdown); + +const deleteFile = (data) => { + filesStore.deleteFile(data.name); + hideDropdown(); +}; + +const renameFile = (data) => { + const newName = prompt("Enter new name:", data.label); + if (newName && newName !== data.label) { + filesStore.renameFile(data.name, newName); + } + hideDropdown(); +}; </script> <style scoped> .project-explorer { height: 50%; /* 占据侧边栏的一半 */ + min-height: 400px; overflow-y: auto; /* 允许内容滚动 */ } + +.custom-tree-node { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 14px; + padding-right: 8px; +} </style> \ No newline at end of file diff --git a/src/components/Toolbar.vue b/src/components/Toolbar.vue index ce151b2..93e191b 100644 --- a/src/components/Toolbar.vue +++ b/src/components/Toolbar.vue @@ -83,7 +83,7 @@ import {ref, reactive} from 'vue'; import html2canvas from "html2canvas"; import {useI18n} from 'vue-i18n'; import updateLogs from "../data/updateLog.json" -import {useFilesStore} from "@/stores/files"; +import {useFilesStore} from "@/ts/files"; const filesStore = useFilesStore(); diff --git a/src/components/Yys.vue b/src/components/Yys.vue index 67f5d5d..8cbdb4b 100644 --- a/src/components/Yys.vue +++ b/src/components/Yys.vue @@ -122,7 +122,7 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue' import shikigamiData from '../data/Shikigami.json'; import _ from 'lodash'; import {Action, ElMessage, ElMessageBox} from "element-plus"; - +import { useGlobalMessage } from '../ts/useGlobalMessage'; // 引入全局消息通知工具 const props = defineProps<{ groups: any[]; }>(); @@ -141,6 +141,7 @@ const state = reactive({ const clipboard = ref(''); +const { showMessage } = useGlobalMessage(); // 获取当前的 i18n 实例 const {t} = useI18n() @@ -217,60 +218,45 @@ const updateProperty = (property) => { props.groups[state.groupIndex].groupInfo[state.positionIndex].properties = _.cloneDeep(property); }; -const removeGroupElement = (groupIndex, positionIndex) => { +const removeGroupElement = async (groupIndex: number, positionIndex: number) => { const group = props.groups[groupIndex]; if (group.groupInfo.length === 1) { - ElMessageBox.alert('无法删除', '提示', { - confirmButtonText: '确定', - }); + showMessage('warning', '无法删除'); return; } - ElMessageBox.confirm('确定要删除此元素吗?', '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning', - }).then(() => { - group.groupInfo.splice(positionIndex, 1); - ElMessage({ - type: 'success', - message: '删除成功!', - }); - }).catch(() => { - ElMessage({ - type: 'info', - message: '已取消删除', + try { + await ElMessageBox.confirm('确定要删除此元素吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', }); - }); + group.groupInfo.splice(positionIndex, 1); + showMessage('success', '删除成功!'); + } catch (error) { + showMessage('info', '已取消删除'); + } }; -const removeGroup = (groupIndex) => { +const removeGroup = async (groupIndex: number) => { if (props.groups.length === 1) { - ElMessageBox.alert('无法删除最后一个队伍', '提示', { - confirmButtonText: '确定', - }); + showMessage('warning', '无法删除最后一个队伍'); return; } - ElMessageBox.confirm('确定要删除此组吗?', '提示', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning', - }).then(() => { - props.groups.splice(groupIndex, 1); - ElMessage({ - type: 'success', - message: '删除成功!', - }); - }).catch(() => { - ElMessage({ - type: 'info', - message: '已取消删除', + try { + await ElMessageBox.confirm('确定要删除此组吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', }); - }); + props.groups.splice(groupIndex, 1); + showMessage('success', '删除成功!'); + } catch (error) { + showMessage('info', '已取消删除'); + } }; - const addGroup = () => { props.groups.push({ shortDescription: '', diff --git a/src/data/updateLog.json b/src/data/updateLog.json index 5f45dd8..ef78e93 100644 --- a/src/data/updateLog.json +++ b/src/data/updateLog.json @@ -1,11 +1,11 @@ [ { "version": "2.0.0", - "date": "2025-03-16", + "date": "2025-03-18", "changes": [ "修复了相同式神不能正确设置属性的问题", - "支持了多文件编辑", - "PS:当前导出截图宽度无法" + "支持了多文件编辑,重命名,删除效果", + "增加了最后一个文件/队伍/式神的删除限制" ] }, { diff --git a/src/main.js b/src/main.js index 0c8769c..6cac8e5 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,7 @@ import { createApp } from 'vue' import App from './App.vue' -import ElementPlus from 'element-plus' +import ElementPlus, {ElMessageBox} from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' @@ -44,6 +44,15 @@ const i18n = createI18n({ }, }) +// 设置ElMessageBox的默认配置 +ElMessageBox.defaults = { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', // 默认类型为警告 + center: true, // 文字居中 + customClass: 'my-message-box', // 自定义类名,用于CSS样式覆盖 +}; + const pinia = createPinia() // 创建 Pinia 实例 app.use(pinia) // 使用 Pinia diff --git a/src/stores/files.ts b/src/stores/files.ts deleted file mode 100644 index dae4bdf..0000000 --- a/src/stores/files.ts +++ /dev/null @@ -1,98 +0,0 @@ -import {defineStore} from 'pinia'; - -export const useFilesStore = defineStore('files', { - state: () => ({ - fileList: [ - { - label: 'Welcome', - name: "1", - visible: true, - groups: [ - { - shortDescription: '<h1>鬼灵歌姬</h1><p>这是一个演示项目,用于测试显示效果</p>', - groupInfo: [{ - "avatar": "/assets/Shikigami/sp/372.png", - "name": "因幡辉夜姬", - "rarity": "SP" - }, - { - "avatar": "/assets/Shikigami/ssr/356.png", - "name": "千姬", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/sp/554.png", - "name": "纺愿缘结神", - "rarity": "SP" - }, - { - "avatar": "/assets/Shikigami/ssr/556.png", - "name": "天照", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/ssr/557.png", - "name": "伊邪那美", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/sp/367.png", - "name": "绘世花鸟卷", - "rarity": "SP" - }], - details: '' - }, - { - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - } - ] - }, { - label: 'File 2', - name: "2", - visible: true, - groups:[ - { - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - }, - { - shortDescription: '', - groupInfo: [{}, {}, {}, {}, {}], - details: '' - } - ] - }], - activeFile: "1", - }), - getters: { - visibleFiles: (state) => state.fileList.filter(file => file.visible), - }, - actions: { - addFile(file) { - this.fileList.push({...file, visible: true}); - this.activeFile = file.name; - }, - setActiveFile(fileId: number) { - this.activeFile = fileId; - }, - setVisible(fileId: number, visibility: boolean) { - const file = this.fileList.find(file => file.name === fileId); - if (file) { - file.visible = visibility; - } - }, - closeTab(fileName: String) { - const file = this.fileList.find(file => file.name === fileName); - if (file) { - file.visible = false; - if (this.activeFile === fileName) { - const nextVisibleFile = this.visibleFiles[0]; - this.activeFile = nextVisibleFile ? nextVisibleFile.name : -1; - } - } - }, - }, -}); \ No newline at end of file diff --git a/src/ts/files.ts b/src/ts/files.ts new file mode 100644 index 0000000..9896c95 --- /dev/null +++ b/src/ts/files.ts @@ -0,0 +1,252 @@ +import {defineStore} from 'pinia'; +import {ElMessageBox} from "element-plus"; +import {useGlobalMessage} from "./useGlobalMessage"; + +const { showMessage } = useGlobalMessage(); +export const useFilesStore = defineStore('files', { + state: () => ({ + fileList: [ + { + "label": "Welcome", + "name": "1", + "type":"PVE", + "visible": true, + "groups": [ + { + "shortDescription": "<h1>鬼灵歌姬</h1><h2><em>这是一个演示项目,用于测试显示6个式神的对齐效果</em></h2>", + "groupInfo": [ + { + "avatar": "/assets/Shikigami/sp/372.png", + "name": "因幡辉夜姬", + "rarity": "SP" + }, + { + "avatar": "/assets/Shikigami/ssr/356.png", + "name": "千姬", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/sp/554.png", + "name": "纺愿缘结神", + "rarity": "SP" + }, + { + "avatar": "/assets/Shikigami/ssr/556.png", + "name": "天照", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/ssr/557.png", + "name": "伊邪那美", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/sp/367.png", + "name": "绘世花鸟卷", + "rarity": "SP" + } + ], + "details": "<h2><strong>开局因幡普攻主怪后锁二</strong></h2>" + }, + { + "shortDescription": "<h1>魂土15秒</h1><p><em>相同式神编辑不同属性</em></p>", + "groupInfo": [ + { + "avatar": "/assets/Shikigami/ssr/364.png", + "name": "阿修罗", + "rarity": "SSR", + "properties": { + "edit": true, + "yuhun": { + "yuhunSetEffect": [ + { + "name": "狂骨", + "shortName": "狂", + "type": "attack", + "avatar": "/assets/Yuhun/狂骨.png" + }, + { + "name": "荒骷髅", + "shortName": "荒", + "type": "PVE", + "avatar": "/assets/Yuhun/荒骷髅.png" + } + ], + "target": "1", + "property2": [ + "Attack" + ], + "property4": [ + "Attack" + ], + "property6": [ + "Crit", + "CritDamage" + ] + }, + "levelRequired": "40", + "speed": "", + "skillRequiredMode": "all", + "skillRequired": [ + "5", + "5", + "5" + ] + } + }, + { + "avatar": "/assets/Shikigami/ssr/364.png", + "name": "阿修罗", + "rarity": "SSR", + "properties": { + "edit": true, + "yuhun": { + "yuhunSetEffect": [ + { + "name": "狂骨", + "shortName": "狂", + "type": "attack", + "avatar": "/assets/Yuhun/狂骨.png" + }, + { + "name": "鬼灵歌伎", + "shortName": "歌伎", + "type": "PVE", + "avatar": "/assets/Yuhun/鬼灵歌伎.png" + } + ], + "target": "0", + "property2": [ + "Attack" + ], + "property4": [ + "Attack" + ], + "property6": [ + "Crit", + "CritDamage" + ] + }, + "levelRequired": "40", + "speed": "", + "skillRequiredMode": "all", + "skillRequired": [ + "5", + "5", + "5" + ] + } + }, + { + "avatar": "/assets/Shikigami/ssr/370.png", + "name": "饭笥", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/ssr/369.png", + "name": "食灵", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/r/205.png", + "name": "座敷童子", + "rarity": "R" + } + ], + "details": "" + } + ] + }, + { + "label": "Test", + "name": "2", + "visible": true, + "type":"PVE", + "groups": [ + { + "shortDescription": "<h1>御魂·悲鸣</h1><p>这是一个演示项目,用于测试不同标签页的切换效果</p>", + "groupInfo": [ + {}, + {}, + {}, + {}, + {} + ], + "details": "" + }, + { + "shortDescription": "", + "groupInfo": [ + {}, + {}, + {}, + {}, + {} + ], + "details": "" + } + ] + } + ], + activeFile: "1", + }), + getters: { + visibleFiles: (state) => state.fileList.filter(file => file.visible), + }, + actions: { + addFile(file) { + this.fileList.push({...file, visible: true}); + this.activeFile = file.name; + }, + setActiveFile(fileId: number) { + this.activeFile = fileId; + }, + setVisible(fileId: number, visibility: boolean) { + const file = this.fileList.find(file => file.name === fileId); + if (file) { + file.visible = visibility; + } + }, + closeTab(fileName: String) { + const file = this.fileList.find(file => file.name === fileName); + if (file) { + file.visible = false; + if (this.activeFile === fileName) { + const nextVisibleFile = this.visibleFiles[0]; + this.activeFile = nextVisibleFile ? nextVisibleFile.name : -1; + } + } + }, + async deleteFile(fileId: string) { + try { + if (this.fileList.length === 1) { + showMessage('warning', '无法删除'); + return; + } + await ElMessageBox.confirm('确定要删除此文件吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }); + + const index = this.fileList.findIndex(file => file.name === fileId); + if (index > -1) { + this.fileList.splice(index, 1); + if (this.activeFile === fileId) { + const nextVisibleFile = this.visibleFiles[0]; + this.activeFile = nextVisibleFile ? nextVisibleFile.name : "-1"; + } + } + showMessage('success', '删除成功!'); + } catch (error) { + showMessage('info', '已取消删除'); + } + }, + renameFile(fileId, newName) { + const file = this.fileList.find(file => file.name === fileId); + if (file) { + file.label = newName; + } + }, + }, +}); \ No newline at end of file diff --git a/src/ts/useGlobalMessage.ts b/src/ts/useGlobalMessage.ts new file mode 100644 index 0000000..388f552 --- /dev/null +++ b/src/ts/useGlobalMessage.ts @@ -0,0 +1,14 @@ +import { ElMessage } from 'element-plus'; + +export function useGlobalMessage() { + const showMessage = (type: 'success' | 'warning' | 'info' | 'error', message: string) => { + ElMessage({ + type, + message, + }); + }; + + return { + showMessage, + }; +} \ No newline at end of file