Merge pull request #1 from Powerful-517/develop

v2.0.0
pull/4/head
rookie 3 weeks ago committed by GitHub
commit fb9d7580ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      .gitignore
  2. 379
      package-lock.json
  3. 2
      package.json
  4. BIN
      public/assets/Other/Contact.png
  5. 157
      src/App.vue
  6. 106
      src/components/ProjectExplorer.vue
  7. 3
      src/components/ShikigamiProperty.vue
  8. 119
      src/components/Toolbar.vue
  9. 205
      src/components/Yys.vue
  10. 18
      src/data/updateLog.json
  11. 33
      src/main.js
  12. 252
      src/ts/files.ts
  13. 14
      src/ts/useGlobalMessage.ts

1
.gitignore vendored

@ -2,3 +2,4 @@
/node_modules
/.idea
/dist
/dist.zip

379
package-lock.json generated

@ -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",

@ -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": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

@ -1,40 +1,157 @@
<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 "@/ts/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 onHandleInport = (file) => {
yysRef.value.importGroups(file);
const onResizing = (x, y, width, height) => {
width.value = width;
height.value = height;
};
const onHandleExport = () => {
yysRef.value.exportGroups();
const element = ref({
x: 400,
y: 20,
width: 1080,
height: windowHeight.value - toolbarHeight,
isActive: false,
});
const handleTabsEdit = (
targetName: String | undefined,
action: 'remove' | 'add'
) => {
if (action === 'remove') {
filesStore.closeTab(targetName);
} else if (action === 'add') {
const newFileName = `File ${filesStore.fileList.length + 1}`;
filesStore.addFile({
label: newFileName,
name: newFileName,
visible: true,
type:'PVE',
groups:[
{
shortDescription: " ",
groupInfo: [{}, {}, {}, {}, {}],
details: ''
},
{
shortDescription: '',
groupInfo: [{}, {}, {}, {}, {}],
details: ''
}
]
});
}
};
onMounted(() => {
window.addEventListener('resize', () => {
windowHeight.value = window.innerHeight;
});
});
onUnmounted(() => {
window.removeEventListener('resize', () => {
windowHeight.value = window.innerHeight;
});
});
const activeFileGroups = computed(() => {
const activeFile = filesStore.fileList.find(file => file.name === filesStore.activeFile);
return activeFile ? activeFile.groups : [];
});
</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="示例用户"/>
<!-- 侧边栏和工作区 -->
<div class="main-content">
<!-- 侧边栏 -->
<aside class="sidebar">
<ProjectExplorer :allFiles="filesStore.fileList" />
</aside>
</main>
<!-- 工作区 -->
<div class="workspace">
<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="`${file.name}-${filesStore.activeFile}`"
:label="file.label"
:name="file.name.toString()"
>
<main id="main-container" :style="{ height: contentHeight, overflow: 'auto' }">
<Yys :groups="activeFileGroups"/>
</main>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</template>
<style scoped>
/* 确保 #main-container 具有相对定位 */
.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 下方 */
}
.sidebar {
width: 20%; /* 侧边栏宽度 */
background-color: #f0f0f0; /* 背景色 */
flex-shrink: 0; /* 防止侧边栏被压缩 */
overflow-y: auto; /* 允许侧边栏内容滚动 */
}
.workspace {
//flex: 1; /* */
overflow: hidden; /* 防止内容溢出 */
display: inline-block;
}
#main-container {
margin-top: 48px; /* 与工具栏高度相同 */
position: relative;
height: 100%; /* 确保内容区域占满父容器 */
overflow-y: auto; /* 允许内容滚动 */
min-height: 100vh; /* 允许容器扩展 */
display: inline-block;
max-width: 100%;
}
/* 如果 Yys 组件需要特定的高度或布局,可以根据需要调整 */
</style>

@ -0,0 +1,106 @@
<template>
<div class="project-explorer">
<el-tree
: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, ref} from 'vue';
import {useFilesStore} from "@/ts/files";
import {ElTree, ElButton, ElDropdownMenu, ElDropdownItem} from 'element-plus';
const filesStore = useFilesStore();
const props = defineProps({
allFiles: {
type: Array,
required: true,
},
});
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>

@ -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()
}

@ -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>
@ -55,24 +78,45 @@
</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 "@/ts/files";
const filesStore = useFilesStore();
// i18n
const {t} = useI18n();
const emit = defineEmits(['handleExport', 'handleImport'])
//
const state = reactive({
previewImage: null, // URL
previewVisible: false, //
showWatermarkDialog: false, //
showWatermarkDialog: false, // ,
showUpdateLogDialog: false, //
showFeedbackFormDialog: false, //
});
const showUpdateLog = () => {
state.showUpdateLogDialog = !state.showUpdateLogDialog;
};
const showFeedbackForm = () => {
state.showFeedbackFormDialog = !state.showFeedbackFormDialog;
};
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 = () => {
@ -81,12 +125,34 @@ 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();
};
const watermark = reactive({
text: '示例水印',
fontSize: 30,
@ -150,9 +216,8 @@ function calculateVisualHeight(selector) {
return rows.reduce((sum, row) => sum + row.maxHeight, 0);
}
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 +228,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,12 +300,16 @@ 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);
}
};
@ -254,7 +338,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;

@ -14,16 +14,16 @@
/>
<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">
<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,18 @@
<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-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 +59,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>
@ -99,10 +92,9 @@
</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,22 +103,13 @@
</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>
<script setup lang="ts">
import {ref, reactive, toRefs} from 'vue';
import draggable from 'vuedraggable';
import ShikigamiSelect from './ShikigamiSelect.vue';
@ -138,24 +121,17 @@ 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";
import { useGlobalMessage } from '../ts/useGlobalMessage'; //
const props = defineProps<{
groups: any[];
}>();
const dialogTableVisible = ref(false)
//
const state = reactive({
showSelectShikigami: false,
showProperty: false,
groups: [
{
shortDescription: '',
groupInfo: [{}, {}, {}, {}, {}],
details: ''
},
{
shortDescription: '',
groupInfo: [{}, {}, {}, {}, {}],
details: ''
},
],
groupIndex: 0,
positionIndex: 0,
currentShikigami: {},
@ -165,6 +141,7 @@ const state = reactive({
const clipboard = ref('');
const { showMessage } = useGlobalMessage();
// i18n
const {t} = useI18n()
@ -175,9 +152,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
}
//
@ -211,23 +188,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 = () => {
@ -238,19 +215,50 @@ 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);
};
const removeGroupElement = async (groupIndex: number, positionIndex: number) => {
const group = props.groups[groupIndex];
const removeGroup = (groupIndex) => {
state.groups.splice(groupIndex, 1);
if (group.groupInfo.length === 1) {
showMessage('warning', '无法删除');
return;
}
try {
await ElMessageBox.confirm('确定要删除此元素吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
group.groupInfo.splice(positionIndex, 1);
showMessage('success', '删除成功!');
} catch (error) {
showMessage('info', '已取消删除');
}
};
const removeGroup = async (groupIndex: number) => {
if (props.groups.length === 1) {
showMessage('warning', '无法删除最后一个队伍');
return;
}
try {
await ElMessageBox.confirm('确定要删除此组吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
props.groups.splice(groupIndex, 1);
showMessage('success', '删除成功!');
} catch (error) {
showMessage('info', '已取消删除');
}
};
const addGroup = () => {
state.groups.push({
props.groups.push({
shortDescription: '',
groupInfo: [{}, {}, {}, {}, {}],
details: ''
@ -258,12 +266,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');
@ -314,7 +322,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('文件格式错误');
@ -369,5 +377,76 @@ defineExpose({
border: 0;
}
.group-header {
margin: 10px;
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;
min-width: 100px;
}
.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: 0px;
z-index: 10;
opacity: 0;
}
}
.property-wrap {
margin: 10px 0;
}
/* 当鼠标悬停在容器上时显示按钮 */
.group-card:hover .opt-btn {
opacity: 1;
}
.group-footer {
margin: 10px;
padding: 10px;
}
</style>

@ -0,0 +1,18 @@
[
{
"version": "2.0.0",
"date": "2025-03-18",
"changes": [
"修复了相同式神不能正确设置属性的问题",
"支持了多文件编辑,重命名,删除效果",
"增加了最后一个文件/队伍/式神的删除限制"
]
},
{
"version": "1.0.0",
"date": "2025-03-09",
"changes": [
"首次发布"
]
}
]

@ -1,24 +1,28 @@
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'
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,19 @@ const i18n = createI18n({
},
})
app.use(i18n)
app.use(ElementPlus)
app.mount('#app')
// 设置ElMessageBox的默认配置
ElMessageBox.defaults = {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning', // 默认类型为警告
center: true, // 文字居中
customClass: 'my-message-box', // 自定义类名,用于CSS样式覆盖
};
const pinia = createPinia() // 创建 Pinia 实例
app.use(pinia) // 使用 Pinia
.use(i18n)
.use(ElementPlus)
.use(Vue3DraggableResizable)
.mount('#app')

@ -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;
}
},
},
});

@ -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,
};
}
Loading…
Cancel
Save