Compare commits
22 Commits
21db1c3895
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f971782480 | |||
| af270b3d6a | |||
| ca5c3c8d21 | |||
| e1defbd2dc | |||
| 6153c7e87e | |||
| 1a8afd9573 | |||
| df4fad2c5a | |||
| dc1b192f47 | |||
| 235c130a6d | |||
| 193cbe74ba | |||
| 5be781a388 | |||
| f97d8d56e9 | |||
| e0d8cd6cb5 | |||
| e9b38b48e9 | |||
| e6fbcff21a | |||
| 8311456a9e | |||
| 1f59e81c52 | |||
| 9c82e12144 | |||
| 9043814c02 | |||
| 84ba18b34c | |||
| 5807fcad48 | |||
| bad60a8ee5 |
@@ -1 +1 @@
|
||||
VITE_SERVER=http://localhost:8080
|
||||
VITE_SERVER=http://localhost:48080
|
||||
184
bun.lock
@@ -5,17 +5,31 @@
|
||||
"": {
|
||||
"name": "template-vue-hucky",
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"axios": "^1.11.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"daisyui": "^5.0.50",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.2",
|
||||
"gsap": "^3.14.2",
|
||||
"lenis": "^1.3.17",
|
||||
"lucide-vue-next": "^0.563.0",
|
||||
"motion-v": "^1.6.1",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.4.1",
|
||||
"radash": "^12.1.1",
|
||||
"reka-ui": "^2.8.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"three": "^0.183.1",
|
||||
"vue": "^3.5.17",
|
||||
"vue-echarts": "^8.0.1",
|
||||
"vue-i18n": "11",
|
||||
"vue-router": "^4.5.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/material-symbols": "^1.2.55",
|
||||
"@iconify/tailwind4": "^1.2.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/node": "^24.1.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
@@ -28,9 +42,11 @@
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"prettier": "^3.6.2",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.43.0",
|
||||
"unplugin-auto-import": "^20.1.0",
|
||||
"unplugin-vue-components": "^31.0.0",
|
||||
"vite": "^7.0.0",
|
||||
"vitepress": "^2.0.0-alpha.15",
|
||||
"vitepress-plugin-group-icons": "^1.7.1",
|
||||
@@ -93,10 +109,16 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
||||
|
||||
"@ctrl/tinycolor": ["@ctrl/tinycolor@3.6.1", "", {}, "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA=="],
|
||||
|
||||
"@cyberalien/svg-utils": ["@cyberalien/svg-utils@1.1.4", "", { "dependencies": { "@iconify/types": "^2.0.0" } }, "sha512-LlBw+9nsQPMO1QNJMuJSgXGWK1/r4D0PohTK0Uq1ehyGE420Qxq4+LVYkA9W0xkrNOCVYvRegbSy5By4f30W7w=="],
|
||||
|
||||
"@docsearch/css": ["@docsearch/css@4.5.3", "", {}, "sha512-kUpHaxn0AgI3LQfyzTYkNUuaFY4uEz/Ym9/N/FvyDE+PzSgZsCyDH9jE49B6N6f1eLCm9Yp64J9wENd6vypdxA=="],
|
||||
|
||||
"@docsearch/js": ["@docsearch/js@4.5.3", "", {}, "sha512-rcBiUMCXbZLqrLIT6F6FDcrG/tyvM2WM0zum6NPbIiQNDQxbSgmNc+/bToS0rxBsXaxiU64esiWoS02WqrWLsg=="],
|
||||
|
||||
"@element-plus/icons-vue": ["@element-plus/icons-vue@2.3.2", "", { "peerDependencies": { "vue": "^3.2.0" } }, "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="],
|
||||
@@ -167,6 +189,14 @@
|
||||
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
|
||||
|
||||
"@floating-ui/core": ["@floating-ui/core@1.7.4", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg=="],
|
||||
|
||||
"@floating-ui/dom": ["@floating-ui/dom@1.7.5", "", { "dependencies": { "@floating-ui/core": "^1.7.4", "@floating-ui/utils": "^0.2.10" } }, "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg=="],
|
||||
|
||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
|
||||
|
||||
"@floating-ui/vue": ["@floating-ui/vue@1.1.10", "", { "dependencies": { "@floating-ui/dom": "^1.7.5", "@floating-ui/utils": "^0.2.10", "vue-demi": ">=0.13.0" } }, "sha512-vdf8f6rHnFPPLRsmL4p12wYl+Ux4mOJOkjzKEMYVnwdf7UFdvBtHlLvQyx8iKG5vhPRbDRgZxdtpmyigDPjzYg=="],
|
||||
|
||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||
|
||||
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
|
||||
@@ -177,14 +207,30 @@
|
||||
|
||||
"@iconify-json/logos": ["@iconify-json/logos@1.2.10", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-qxaXKJ6fu8jzTMPQdHtNxlfx6tBQ0jXRbHZIYy5Ilh8Lx9US9FsAdzZWUR8MXV8PnWTKGDFO4ZZee9VwerCyMA=="],
|
||||
|
||||
"@iconify-json/material-symbols": ["@iconify-json/material-symbols@1.2.55", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-9EHfqSpzXxQb5neA5/n5GcF15rccKG+oBJDwh/s8mmxfYswY1A4dKYPOWXCqrWMe/J3CmHb0tXFloscVarrqkQ=="],
|
||||
|
||||
"@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.68", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-bQPl1zuZlX6AnofreA1v7J+hoPncrFMppqGboe/SH54jZO37meiBUGBqNOxEpc0HKfZGxJaVVJwZd4gdMYu3hw=="],
|
||||
|
||||
"@iconify-json/vscode-icons": ["@iconify-json/vscode-icons@1.2.40", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-Q7JIWAxENwmcRg4EGRY+u16gBwrAj6mWeuSmuyuPvNvoTJHh8Ss8qoeDhrFYNgtWqNkzH5hSf4b2T9XLK5MsrA=="],
|
||||
|
||||
"@iconify/tailwind4": ["@iconify/tailwind4@1.2.1", "", { "dependencies": { "@iconify/tools": "^5.0.2", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0" }, "peerDependencies": { "tailwindcss": ">= 4.0.0" } }, "sha512-Hd7k8y7uzT3hk8ltw0jGku0r0wA8sc3d2iMvVTYv/9tMxBb+frZtWZGD9hDMU3EYuE+lMn58wi2lS8R2ZbwFcQ=="],
|
||||
|
||||
"@iconify/tools": ["@iconify/tools@5.0.3", "", { "dependencies": { "@cyberalien/svg-utils": "^1.1.1", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0", "fflate": "^0.8.2", "modern-tar": "^0.7.3", "pathe": "^2.0.3", "svgo": "^4.0.0" } }, "sha512-W5nbH5fNv20TvU49Al19Foos/ViAnmppbCNV9ieGl6/dRMDRzxeFol6peXX/NAgaOytQwZZxTTJRq/Kxd4eWsA=="],
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
|
||||
|
||||
"@internationalized/date": ["@internationalized/date@3.10.1", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-oJrXtQiAXLvT9clCf1K4kxp3eKsQhIaZqxEyowkBcsvZDdZkbWrVmnGknxs5flTD0VGsxrxKgBCZty1EzoiMzA=="],
|
||||
|
||||
"@internationalized/number": ["@internationalized/number@3.6.5", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g=="],
|
||||
|
||||
"@intlify/core-base": ["@intlify/core-base@11.2.8", "", { "dependencies": { "@intlify/message-compiler": "11.2.8", "@intlify/shared": "11.2.8" } }, "sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA=="],
|
||||
|
||||
"@intlify/message-compiler": ["@intlify/message-compiler@11.2.8", "", { "dependencies": { "@intlify/shared": "11.2.8", "source-map-js": "^1.0.2" } }, "sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ=="],
|
||||
|
||||
"@intlify/shared": ["@intlify/shared@11.2.8", "", {}, "sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw=="],
|
||||
|
||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
@@ -203,6 +249,8 @@
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@popperjs/core": ["@sxzz/popperjs-es@2.11.8", "", {}, "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.29", "", {}, "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.1", "", { "os": "android", "cpu": "arm" }, "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag=="],
|
||||
@@ -263,6 +311,8 @@
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.18", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.13", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.18", "source-map-js": "^1.2.1", "tailwindcss": "4.1.13" } }, "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.13", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.13", "@tailwindcss/oxide-darwin-arm64": "4.1.13", "@tailwindcss/oxide-darwin-x64": "4.1.13", "@tailwindcss/oxide-freebsd-x64": "4.1.13", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", "@tailwindcss/oxide-linux-x64-musl": "4.1.13", "@tailwindcss/oxide-wasm32-wasi": "4.1.13", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" } }, "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA=="],
|
||||
@@ -293,6 +343,10 @@
|
||||
|
||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.13", "", { "dependencies": { "@tailwindcss/node": "4.1.13", "@tailwindcss/oxide": "4.1.13", "tailwindcss": "4.1.13" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ=="],
|
||||
|
||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.18", "", {}, "sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg=="],
|
||||
|
||||
"@tanstack/vue-virtual": ["@tanstack/vue-virtual@3.13.18", "", { "dependencies": { "@tanstack/virtual-core": "3.13.18" }, "peerDependencies": { "vue": "^2.7.0 || ^3.0.0" } }, "sha512-6pT8HdHtTU5Z+t906cGdCroUNA5wHjFXsNss9gwk7QAr1VNZtz9IQCs2Nhx0gABK48c+OocHl2As+TMg8+Hy4A=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||
@@ -301,6 +355,10 @@
|
||||
|
||||
"@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
|
||||
|
||||
"@types/lodash": ["@types/lodash@4.17.23", "", {}, "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA=="],
|
||||
|
||||
"@types/lodash-es": ["@types/lodash-es@4.17.12", "", { "dependencies": { "@types/lodash": "*" } }, "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ=="],
|
||||
|
||||
"@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
|
||||
|
||||
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||
@@ -381,13 +439,13 @@
|
||||
|
||||
"@vue/tsconfig": ["@vue/tsconfig@0.7.0", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg=="],
|
||||
|
||||
"@vueuse/core": ["@vueuse/core@13.9.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "13.9.0", "@vueuse/shared": "13.9.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA=="],
|
||||
"@vueuse/core": ["@vueuse/core@14.1.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.1.0", "@vueuse/shared": "14.1.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw=="],
|
||||
|
||||
"@vueuse/integrations": ["@vueuse/integrations@14.1.0", "", { "dependencies": { "@vueuse/core": "14.1.0", "@vueuse/shared": "14.1.0" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7 || ^8", "vue": "^3.5.0" }, "optionalPeers": ["async-validator", "axios", "change-case", "drauu", "focus-trap", "fuse.js", "idb-keyval", "jwt-decode", "nprogress", "qrcode", "sortablejs", "universal-cookie"] }, "sha512-eNQPdisnO9SvdydTIXnTE7c29yOsJBD/xkwEyQLdhDC/LKbqrFpXHb3uS//7NcIrQO3fWVuvMGp8dbK6mNEMCA=="],
|
||||
|
||||
"@vueuse/metadata": ["@vueuse/metadata@13.9.0", "", {}, "sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg=="],
|
||||
"@vueuse/metadata": ["@vueuse/metadata@14.1.0", "", {}, "sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA=="],
|
||||
|
||||
"@vueuse/shared": ["@vueuse/shared@13.9.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g=="],
|
||||
"@vueuse/shared": ["@vueuse/shared@14.1.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
@@ -401,6 +459,8 @@
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
||||
|
||||
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
|
||||
|
||||
"array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="],
|
||||
@@ -417,6 +477,8 @@
|
||||
|
||||
"async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
||||
|
||||
"async-validator": ["async-validator@4.2.5", "", {}, "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||
@@ -453,8 +515,14 @@
|
||||
|
||||
"character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
|
||||
|
||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||
|
||||
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
@@ -463,6 +531,8 @@
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
|
||||
"commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
|
||||
@@ -473,8 +543,16 @@
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
||||
|
||||
"css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="],
|
||||
|
||||
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"daisyui": ["daisyui@5.1.10", "", {}, "sha512-p1J/HME2WmaSiy6u2alIbeP3gd5PNVft3+6Bdll0BRSm/UdI4084+pD01LxFug/5wGexNewWqbcEL6nB2n2o+Q=="],
|
||||
@@ -485,6 +563,8 @@
|
||||
|
||||
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
|
||||
|
||||
"dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="],
|
||||
|
||||
"de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
@@ -511,10 +591,22 @@
|
||||
|
||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"echarts": ["echarts@6.0.0", "", { "dependencies": { "tslib": "2.3.0", "zrender": "6.0.0" } }, "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.217", "", {}, "sha512-Pludfu5iBxp9XzNl0qq2G87hdD17ZV7h5T4n6rQXDi3nCyloBV3jreE9+8GC6g4X/5yxqVgXEURpcLtM0WS4jA=="],
|
||||
|
||||
"element-plus": ["element-plus@2.13.2", "", { "dependencies": { "@ctrl/tinycolor": "^3.4.1", "@element-plus/icons-vue": "^2.3.2", "@floating-ui/dom": "^1.0.1", "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", "@types/lodash": "^4.17.20", "@types/lodash-es": "^4.17.12", "@vueuse/core": "^10.11.0", "async-validator": "^4.2.5", "dayjs": "^1.11.19", "lodash": "^4.17.23", "lodash-es": "^4.17.23", "lodash-unified": "^1.0.3", "memoize-one": "^6.0.0", "normalize-wheel-es": "^1.2.0" }, "peerDependencies": { "vue": "^3.3.0" } }, "sha512-Zjzm1NnFXGhV4LYZ6Ze9skPlYi2B4KAmN18FL63A3PZcjhDfroHwhtM6RE8BonlOPHXUnPQynH0BgaoEfvhrGw=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
||||
|
||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
@@ -579,6 +671,8 @@
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
@@ -627,6 +721,8 @@
|
||||
|
||||
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
||||
|
||||
"gsap": ["gsap@3.14.2", "", {}, "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA=="],
|
||||
|
||||
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
@@ -739,6 +835,8 @@
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"lenis": ["lenis@1.3.17", "", { "peerDependencies": { "@nuxt/kit": ">=3.0.0", "react": ">=17.0.0", "vue": ">=3.0.0" }, "optionalPeers": ["@nuxt/kit", "react", "vue"] }, "sha512-k9T9rgcxne49ggJOvXCraWn5dt7u2mO+BNkhyu6yxuEnm9c092kAW5Bus5SO211zUvx7aCCEtzy9UWr0RB+oJw=="],
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
|
||||
@@ -767,12 +865,20 @@
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
|
||||
|
||||
"lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
|
||||
|
||||
"lodash-unified": ["lodash-unified@1.0.3", "", { "peerDependencies": { "@types/lodash-es": "*", "lodash": "*", "lodash-es": "*" } }, "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"lucide-vue-next": ["lucide-vue-next@0.563.0", "", { "peerDependencies": { "vue": ">=3.0.1" } }, "sha512-zsE/lCKtmaa7bGfhSpN84br1K9YoQ5pCN+2oKWjQQG3Lo6ufUUKBuHSjNFI6RvUevxaajNXb8XwFUKeTXG3sIA=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
||||
|
||||
"mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="],
|
||||
@@ -781,6 +887,10 @@
|
||||
|
||||
"mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
|
||||
|
||||
"mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="],
|
||||
|
||||
"memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||
@@ -813,6 +923,8 @@
|
||||
|
||||
"mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
|
||||
|
||||
"modern-tar": ["modern-tar@0.7.3", "", {}, "sha512-4W79zekKGyYU4JXVmB78DOscMFaJth2gGhgfTl2alWE4rNe3nf4N2pqenQ0rEtIewrnD79M687Ouba3YGTLOvg=="],
|
||||
|
||||
"motion-dom": ["motion-dom@12.23.12", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw=="],
|
||||
|
||||
"motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="],
|
||||
@@ -829,6 +941,8 @@
|
||||
|
||||
"node-releases": ["node-releases@2.0.20", "", {}, "sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA=="],
|
||||
|
||||
"normalize-wheel-es": ["normalize-wheel-es@1.2.0", "", {}, "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
@@ -845,6 +959,10 @@
|
||||
|
||||
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
|
||||
|
||||
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
|
||||
|
||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
|
||||
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
|
||||
|
||||
"oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="],
|
||||
@@ -909,6 +1027,8 @@
|
||||
|
||||
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
|
||||
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
||||
|
||||
"regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
|
||||
@@ -919,6 +1039,8 @@
|
||||
|
||||
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||
|
||||
"reka-ui": ["reka-ui@2.8.0", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "@floating-ui/vue": "^1.1.6", "@internationalized/date": "^3.5.0", "@internationalized/number": "^3.5.0", "@tanstack/vue-virtual": "^3.12.0", "@vueuse/core": "^14.1.0", "@vueuse/shared": "^14.1.0", "aria-hidden": "^1.2.4", "defu": "^6.1.4", "ohash": "^2.0.11" }, "peerDependencies": { "vue": ">= 3.2.0" } }, "sha512-N4JOyIrmDE7w2i06WytqcV2QICubtS2PsK5Uo8FIMAgmO13KhUAgAByP26cXjjm2oF/w7rTyRs8YaqtvaBT+SA=="],
|
||||
|
||||
"resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
|
||||
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
@@ -937,6 +1059,8 @@
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
|
||||
"sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="],
|
||||
|
||||
"scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
@@ -991,14 +1115,20 @@
|
||||
|
||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
|
||||
"svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="],
|
||||
|
||||
"tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.13", "", {}, "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w=="],
|
||||
|
||||
"tapable": ["tapable@2.2.3", "", {}, "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="],
|
||||
|
||||
"tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
|
||||
|
||||
"three": ["three@0.183.1", "", {}, "sha512-Psv6bbd3d/M/01MT2zZ+VmD0Vj2dbWTNhfe4CuSg7w5TuW96M3NOyCVuh9SZQ05CpGmD7NEcJhZw4GVjhCYxfQ=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
@@ -1009,7 +1139,9 @@
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
"tslib": ["tslib@2.3.0", "", {}, "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="],
|
||||
|
||||
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
@@ -1049,6 +1181,8 @@
|
||||
|
||||
"unplugin-utils": ["unplugin-utils@0.3.0", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg=="],
|
||||
|
||||
"unplugin-vue-components": ["unplugin-vue-components@31.0.0", "", { "dependencies": { "chokidar": "^5.0.0", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", "obug": "^2.1.1", "picomatch": "^4.0.3", "tinyglobby": "^0.2.15", "unplugin": "^2.3.11", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "@nuxt/kit": "^3.2.2 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@nuxt/kit"] }, "sha512-4ULwfTZTLuWJ7+S9P7TrcStYLsSRkk6vy2jt/WTfgUEUb0nW9//xxmrfhyHUEVpZ2UKRRwfRb8Yy15PDbVZf+Q=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
@@ -1069,8 +1203,14 @@
|
||||
|
||||
"vue": ["vue@3.5.21", "", { "dependencies": { "@vue/compiler-dom": "3.5.21", "@vue/compiler-sfc": "3.5.21", "@vue/runtime-dom": "3.5.21", "@vue/server-renderer": "3.5.21", "@vue/shared": "3.5.21" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA=="],
|
||||
|
||||
"vue-demi": ["vue-demi@0.14.10", "", { "peerDependencies": { "@vue/composition-api": "^1.0.0-rc.1", "vue": "^3.0.0-0 || ^2.6.0" }, "optionalPeers": ["@vue/composition-api"], "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" } }, "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="],
|
||||
|
||||
"vue-echarts": ["vue-echarts@8.0.1", "", { "peerDependencies": { "echarts": "^6.0.0", "vue": "^3.3.0" } }, "sha512-23rJTFLu1OUEGRWjJGmdGt8fP+8+ja1gVgzMYPIPaHWpXegcO1viIAaeu2H4QHESlVeHzUAHIxKXGrwjsyXAaA=="],
|
||||
|
||||
"vue-eslint-parser": ["vue-eslint-parser@10.2.0", "", { "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.6.0", "semver": "^7.6.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw=="],
|
||||
|
||||
"vue-i18n": ["vue-i18n@11.2.8", "", { "dependencies": { "@intlify/core-base": "11.2.8", "@intlify/shared": "11.2.8", "@vue/devtools-api": "^6.5.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg=="],
|
||||
|
||||
"vue-router": ["vue-router@4.5.1", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.2.0" } }, "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw=="],
|
||||
|
||||
"vue-tsc": ["vue-tsc@2.2.12", "", { "dependencies": { "@volar/typescript": "2.4.15", "@vue/language-core": "2.2.12" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw=="],
|
||||
@@ -1095,10 +1235,14 @@
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"zrender": ["zrender@6.0.0", "", { "dependencies": { "tslib": "2.3.0" } }, "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg=="],
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@swc/helpers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
|
||||
@@ -1125,14 +1269,18 @@
|
||||
|
||||
"@vue/language-core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"@vueuse/integrations/@vueuse/core": ["@vueuse/core@14.1.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.1.0", "@vueuse/shared": "14.1.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw=="],
|
||||
"aria-hidden/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@vueuse/integrations/@vueuse/shared": ["@vueuse/shared@14.1.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw=="],
|
||||
"csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
|
||||
|
||||
"element-plus/@vueuse/core": ["@vueuse/core@10.11.1", "", { "dependencies": { "@types/web-bluetooth": "^0.0.20", "@vueuse/metadata": "10.11.1", "@vueuse/shared": "10.11.1", "vue-demi": ">=0.14.8" } }, "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww=="],
|
||||
|
||||
"eslint-plugin-vue/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"framer-motion/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
@@ -1147,9 +1295,13 @@
|
||||
|
||||
"unimport/unplugin-utils": ["unplugin-utils@0.2.5", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg=="],
|
||||
|
||||
"vitepress/@vue/shared": ["@vue/shared@3.5.27", "", {}, "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ=="],
|
||||
"unplugin-vue-components/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"vitepress/@vueuse/core": ["@vueuse/core@14.1.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.1.0", "@vueuse/shared": "14.1.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw=="],
|
||||
"unplugin-vue-components/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||
|
||||
"unplugin-vue-components/unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="],
|
||||
|
||||
"vitepress/@vue/shared": ["@vue/shared@3.5.27", "", {}, "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ=="],
|
||||
|
||||
"vitepress/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
||||
|
||||
@@ -1157,6 +1309,8 @@
|
||||
|
||||
"vue-eslint-parser/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
"vue-i18n/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
|
||||
|
||||
"vue-router/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
||||
@@ -1179,16 +1333,18 @@
|
||||
|
||||
"@vue/language-core/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"@vueuse/integrations/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@14.1.0", "", {}, "sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA=="],
|
||||
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
||||
|
||||
"element-plus/@vueuse/core/@types/web-bluetooth": ["@types/web-bluetooth@0.0.20", "", {}, "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="],
|
||||
|
||||
"element-plus/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@10.11.1", "", {}, "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw=="],
|
||||
|
||||
"element-plus/@vueuse/core/@vueuse/shared": ["@vueuse/shared@10.11.1", "", { "dependencies": { "vue-demi": ">=0.14.8" } }, "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA=="],
|
||||
|
||||
"mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
||||
|
||||
"pinia/@vue/devtools-api/@vue/devtools-kit": ["@vue/devtools-kit@7.7.7", "", { "dependencies": { "@vue/devtools-shared": "^7.7.7", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA=="],
|
||||
|
||||
"vitepress/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@14.1.0", "", {}, "sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA=="],
|
||||
|
||||
"vitepress/@vueuse/core/@vueuse/shared": ["@vueuse/shared@14.1.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw=="],
|
||||
|
||||
"vitepress/vite/esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="],
|
||||
|
||||
"vitepress/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.27", "", { "dependencies": { "@vue/compiler-core": "3.5.27", "@vue/shared": "3.5.27" } }, "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w=="],
|
||||
|
||||
112
components.d.ts
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// biome-ignore lint: disable
|
||||
// oxlint-disable
|
||||
// ------
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import { GlobalComponents } from 'vue'
|
||||
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AnimatePresence: typeof import('motion-v')['AnimatePresence']
|
||||
AnimText: typeof import('./src/components/special/AnimText.vue')['default']
|
||||
AuthDialog: typeof import('./src/components/dialog/AuthDialog.vue')['default']
|
||||
BasicIntroCard: typeof import('./src/components/card/BasicIntroCard.vue')['default']
|
||||
BiliBiliIcon: typeof import('./src/components/icon/BiliBiliIcon.vue')['default']
|
||||
Button: typeof import('./src/components/ui/button/Button.vue')['default']
|
||||
Calendar: typeof import('./src/components/ui/calendar/Calendar.vue')['default']
|
||||
CalendarCell: typeof import('./src/components/ui/calendar/CalendarCell.vue')['default']
|
||||
CalendarCellTrigger: typeof import('./src/components/ui/calendar/CalendarCellTrigger.vue')['default']
|
||||
CalendarGrid: typeof import('./src/components/ui/calendar/CalendarGrid.vue')['default']
|
||||
CalendarGridBody: typeof import('./src/components/ui/calendar/CalendarGridBody.vue')['default']
|
||||
CalendarGridHead: typeof import('./src/components/ui/calendar/CalendarGridHead.vue')['default']
|
||||
CalendarGridRow: typeof import('./src/components/ui/calendar/CalendarGridRow.vue')['default']
|
||||
CalendarHeadCell: typeof import('./src/components/ui/calendar/CalendarHeadCell.vue')['default']
|
||||
CalendarHeader: typeof import('./src/components/ui/calendar/CalendarHeader.vue')['default']
|
||||
CalendarHeading: typeof import('./src/components/ui/calendar/CalendarHeading.vue')['default']
|
||||
CalendarNextButton: typeof import('./src/components/ui/calendar/CalendarNextButton.vue')['default']
|
||||
CalendarPrevButton: typeof import('./src/components/ui/calendar/CalendarPrevButton.vue')['default']
|
||||
ChangeLanguageDropdownButton: typeof import('./src/components/button/ChangeLanguageDropdownButton.vue')['default']
|
||||
ChangeThemeDropdownButton: typeof import('./src/components/button/ChangeThemeDropdownButton.vue')['default']
|
||||
CurtainReveal: typeof import('./src/components/layout/CurtainReveal.vue')['default']
|
||||
DatePicker: typeof import('./src/components/date-picker/DatePicker.vue')['default']
|
||||
DatePickerDisplayCard: typeof import('./src/components/card/DatePickerDisplayCard.vue')['default']
|
||||
DevelopProgressCard: typeof import('./src/components/card/DevelopProgressCard.vue')['default']
|
||||
DevelopProgressDiagram: typeof import('./src/components/diagram/DevelopProgressDiagram.vue')['default']
|
||||
DouYinIcon: typeof import('./src/components/icon/DouYinIcon.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
FooterBar: typeof import('./src/components/layout/FooterBar.vue')['default']
|
||||
FooterBarV2: typeof import('./src/components/layout/FooterBarV2.vue')['default']
|
||||
FooterBarV2Placeholder: typeof import('./src/components/layout/FooterBarV2Placeholder.vue')['default']
|
||||
FooterBarV2Space: typeof import('./src/components/layout/FooterBarV2Space.vue')['default']
|
||||
LogoIcon: typeof import('./src/components/icon/LogoIcon.vue')['default']
|
||||
LogoModel: typeof import('./src/components/three/LogoModel.vue')['default']
|
||||
ModelViewer: typeof import('./src/components/three/ModelViewer.vue')['default']
|
||||
NativeSelect: typeof import('./src/components/ui/native-select/NativeSelect.vue')['default']
|
||||
NativeSelectOptGroup: typeof import('./src/components/ui/native-select/NativeSelectOptGroup.vue')['default']
|
||||
NativeSelectOption: typeof import('./src/components/ui/native-select/NativeSelectOption.vue')['default']
|
||||
NavBar: typeof import('./src/components/menu/NavBar.vue')['default']
|
||||
Popover: typeof import('./src/components/ui/popover/Popover.vue')['default']
|
||||
PopoverAnchor: typeof import('./src/components/ui/popover/PopoverAnchor.vue')['default']
|
||||
PopoverContent: typeof import('./src/components/ui/popover/PopoverContent.vue')['default']
|
||||
PopoverTrigger: typeof import('./src/components/ui/popover/PopoverTrigger.vue')['default']
|
||||
RedBookIcon: typeof import('./src/components/icon/RedBookIcon.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
UserAuthNavButton: typeof import('./src/components/button/UserAuthNavButton.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
// For TSX support
|
||||
declare global {
|
||||
const AnimatePresence: typeof import('motion-v')['AnimatePresence']
|
||||
const AnimText: typeof import('./src/components/special/AnimText.vue')['default']
|
||||
const AuthDialog: typeof import('./src/components/dialog/AuthDialog.vue')['default']
|
||||
const BasicIntroCard: typeof import('./src/components/card/BasicIntroCard.vue')['default']
|
||||
const BiliBiliIcon: typeof import('./src/components/icon/BiliBiliIcon.vue')['default']
|
||||
const Button: typeof import('./src/components/ui/button/Button.vue')['default']
|
||||
const Calendar: typeof import('./src/components/ui/calendar/Calendar.vue')['default']
|
||||
const CalendarCell: typeof import('./src/components/ui/calendar/CalendarCell.vue')['default']
|
||||
const CalendarCellTrigger: typeof import('./src/components/ui/calendar/CalendarCellTrigger.vue')['default']
|
||||
const CalendarGrid: typeof import('./src/components/ui/calendar/CalendarGrid.vue')['default']
|
||||
const CalendarGridBody: typeof import('./src/components/ui/calendar/CalendarGridBody.vue')['default']
|
||||
const CalendarGridHead: typeof import('./src/components/ui/calendar/CalendarGridHead.vue')['default']
|
||||
const CalendarGridRow: typeof import('./src/components/ui/calendar/CalendarGridRow.vue')['default']
|
||||
const CalendarHeadCell: typeof import('./src/components/ui/calendar/CalendarHeadCell.vue')['default']
|
||||
const CalendarHeader: typeof import('./src/components/ui/calendar/CalendarHeader.vue')['default']
|
||||
const CalendarHeading: typeof import('./src/components/ui/calendar/CalendarHeading.vue')['default']
|
||||
const CalendarNextButton: typeof import('./src/components/ui/calendar/CalendarNextButton.vue')['default']
|
||||
const CalendarPrevButton: typeof import('./src/components/ui/calendar/CalendarPrevButton.vue')['default']
|
||||
const ChangeLanguageDropdownButton: typeof import('./src/components/button/ChangeLanguageDropdownButton.vue')['default']
|
||||
const ChangeThemeDropdownButton: typeof import('./src/components/button/ChangeThemeDropdownButton.vue')['default']
|
||||
const CurtainReveal: typeof import('./src/components/layout/CurtainReveal.vue')['default']
|
||||
const DatePicker: typeof import('./src/components/date-picker/DatePicker.vue')['default']
|
||||
const DatePickerDisplayCard: typeof import('./src/components/card/DatePickerDisplayCard.vue')['default']
|
||||
const DevelopProgressCard: typeof import('./src/components/card/DevelopProgressCard.vue')['default']
|
||||
const DevelopProgressDiagram: typeof import('./src/components/diagram/DevelopProgressDiagram.vue')['default']
|
||||
const DouYinIcon: typeof import('./src/components/icon/DouYinIcon.vue')['default']
|
||||
const ElButton: typeof import('element-plus/es')['ElButton']
|
||||
const FooterBar: typeof import('./src/components/layout/FooterBar.vue')['default']
|
||||
const FooterBarV2: typeof import('./src/components/layout/FooterBarV2.vue')['default']
|
||||
const FooterBarV2Placeholder: typeof import('./src/components/layout/FooterBarV2Placeholder.vue')['default']
|
||||
const FooterBarV2Space: typeof import('./src/components/layout/FooterBarV2Space.vue')['default']
|
||||
const LogoIcon: typeof import('./src/components/icon/LogoIcon.vue')['default']
|
||||
const LogoModel: typeof import('./src/components/three/LogoModel.vue')['default']
|
||||
const ModelViewer: typeof import('./src/components/three/ModelViewer.vue')['default']
|
||||
const NativeSelect: typeof import('./src/components/ui/native-select/NativeSelect.vue')['default']
|
||||
const NativeSelectOptGroup: typeof import('./src/components/ui/native-select/NativeSelectOptGroup.vue')['default']
|
||||
const NativeSelectOption: typeof import('./src/components/ui/native-select/NativeSelectOption.vue')['default']
|
||||
const NavBar: typeof import('./src/components/menu/NavBar.vue')['default']
|
||||
const Popover: typeof import('./src/components/ui/popover/Popover.vue')['default']
|
||||
const PopoverAnchor: typeof import('./src/components/ui/popover/PopoverAnchor.vue')['default']
|
||||
const PopoverContent: typeof import('./src/components/ui/popover/PopoverContent.vue')['default']
|
||||
const PopoverTrigger: typeof import('./src/components/ui/popover/PopoverTrigger.vue')['default']
|
||||
const RedBookIcon: typeof import('./src/components/icon/RedBookIcon.vue')['default']
|
||||
const RouterLink: typeof import('vue-router')['RouterLink']
|
||||
const RouterView: typeof import('vue-router')['RouterView']
|
||||
const UserAuthNavButton: typeof import('./src/components/button/UserAuthNavButton.vue')['default']
|
||||
}
|
||||
21
components.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://shadcn-vue.com/schema.json",
|
||||
"style": "new-york",
|
||||
"typescript": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/style.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "lucide",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"composables": "@/composables"
|
||||
},
|
||||
"registries": {}
|
||||
}
|
||||
@@ -22,7 +22,15 @@ export default defineConfig({
|
||||
},
|
||||
{
|
||||
text: "脚手架特色",
|
||||
items: [{ text: "自动路由", link: "/cli-feature/auto-router" }],
|
||||
items: [
|
||||
{ text: "自动路由", link: "/cli-feature/auto-router" },
|
||||
{ text: "组件库", link: "/cli-feature/component-lib" },
|
||||
{ text: "国际化", link: "/cli-feature/international" },
|
||||
{ text: "图表", link: "/cli-feature/chart" },
|
||||
{ text: "辅助工具", link: "/cli-feature/radash" },
|
||||
{ text: "动画", link: "/cli-feature/motion" },
|
||||
{ text: "字体", link: "/cli-feature/font" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
@@ -104,6 +104,12 @@ const router = createRouter({
|
||||
4. 中文命名:兼容性差,且不利于团队协作和项目维护。
|
||||
:::
|
||||
|
||||
## 路由跳转
|
||||
|
||||
Hucky 封装了 `utils/navigator.ts` 模块,提供了 `navigateTo` 函数,用于在 Vue 组件中进行路由跳转。
|
||||
|
||||
如果您以 `/` 开头,将会跳转到前端路由,如果您以 `http` 开头,将会跳转到外部链接。通过设定 `replace` 参数为 `true`,可以在跳转外部链接时,替换当前页面而不是打开新标签页。
|
||||
|
||||
## 特殊规则
|
||||
|
||||
:::danger 危险
|
||||
|
||||
42
docs/cli-feature/chart.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 图表
|
||||
|
||||
## 使用
|
||||
|
||||
Hucky 选用 Echarts + Vue Echarts 组件库来为项目绘制图表。首先您需要熟悉如何使用 Echarts 的 option 来绘制一个图表,您可以前往 [Echarts 官方示例站点](https://echarts.apache.org/examples/zh/index.html) 先选择一个参考图表,然后复制其 option 代码。
|
||||
|
||||
然后您需要前往 [Vue Echarts 站点](https://vue-echarts.dev/#codegen) 并使用其提供的引入代码生成器来生成 import 代码。将您的 option 对象粘贴并获取对应的 import 代码。注意这里粘贴的是具体的 option 对象,而不是以 option = 开头的赋值语句。
|
||||
|
||||
接下来在您需要用到图表的组件中引入 vue echarts 提供的 VChart 组件。并动态绑定 option 到 VChart 组件的 props 中。可参考如下代码。
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import VChart from "vue-echarts";
|
||||
import { type EChartsOption } from "echarts";
|
||||
|
||||
// 自动生成的 import 代码
|
||||
import { use } from "echarts/core";
|
||||
import { PieChart } from "echarts/charts";
|
||||
import { TooltipComponent, LegendComponent } from "echarts/components";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
|
||||
use([TooltipComponent, LegendComponent, PieChart, CanvasRenderer]);
|
||||
|
||||
const option = ref<EChartsOption>({
|
||||
// 此处为您复制的 Echarts 官方示例站点的 option 代码
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 必须设置图表容器的高度和宽度 -->
|
||||
<div class="h-96 w-96">
|
||||
<VChart :option="option" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
您可以前往 [Vue Echarts 文档](https://github.com/ecomfe/vue-echarts/blob/HEAD/README.zh-Hans.md) 查看更多使用方法。
|
||||
:::
|
||||
39
docs/cli-feature/component-lib.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# 组件库
|
||||
|
||||
## 必要前提
|
||||
|
||||
您必须熟悉 [tailwindcss](https://tailwindcss.com/) 的常见用法,这在 hucky 中基本上是不可或缺的。我们选用的是 v4.1 版本,请不要再研究其他旧版本。
|
||||
|
||||
## 首选
|
||||
|
||||
我们首选 tailwindcss 非常适配的 [daisyui](https://daisyui.com/) 作为我们的主要组件库,这个组件库通过配置 class 为普通的元素添加功能,同时通过 theme 规范主题样式。
|
||||
|
||||
当然我们也提供了目前前端最主流的组件库 [shadcn-vue](https://www.shadcn-vue.com/) 在项目中支持,这个组件库同样和 tailwindcss 紧密关联,通过将组件代码直接呈现在项目中,同时适配了 Vue 的组件化开发,您可以深入调整组件细节的同时拥有方便的组件调用。
|
||||
|
||||
考虑到国内环境,我们也提供了 [element-plus](https://element-plus.org/) 作为备用组件库,您可以根据项目需求选择使用。
|
||||
|
||||
:::tip 提示
|
||||
我们最不推荐您使用 element-plus,其难以调节的组件样式和重复化的审美使得在现代前端市场中被视为过时技术,尽管这可能是您的熟悉的组件库,我们仍然强烈推荐您学习 tailwindcss 相关的组件库。
|
||||
:::
|
||||
|
||||
## 主题
|
||||
|
||||
我们使用 daisyui 内置的 35 种主题,可以通过 `hooks/globalThemeHook` 中的方法来切换主题,我们也封装了 `components/button/ChangeThemeDropdownButton.vue` 下拉框按钮组件来方便切换主题。
|
||||
|
||||
如果您需要自定义主题,请查询 [daisyui 主题文档](https://daisyui.com/docs/themes/),Hucky 规定仅使用这种方式自定义主题。
|
||||
|
||||
当您需要调整的是某个具体的组件样式时,请优先使用 tailwindcss 深入组件内部进行覆盖调整,而不是全局调整 css 变量的值。
|
||||
|
||||
## 关于 Shadcn
|
||||
|
||||
:::warning 警告
|
||||
Shadcn 组件的样式将会被 Daisy-ui 组件样式所覆盖。阅读以下文档进行调整。
|
||||
:::
|
||||
|
||||
`components/ui` 目录下是 shadcn-vue 提供的组件代码,您可以在项目中直接调用这些组件,同时也可以根据需要进行调整。
|
||||
|
||||
由于 daisy-ui 和 shadcn 同时使用类似 --color-primary 的 css 变量来调整主题样式,Hucky 删除了 shadcn 在全局样式上做的调整,如果您需要调整 shadcn 中的组件样式,请通过在组件内部的 style 标签中设置 css 变量来实现。
|
||||
|
||||
:::tip 提示
|
||||
尽管 shadcn 组件的样式确实可以调整,但仅建议对 css 掌握度较高的开发者调整。如果您不熟悉 css 及 css 变量,仍然建议您接受 shadcn 的组件使用 daisy-ui 提供的默认样式,或者不使用 shadcn 组件。
|
||||
:::
|
||||
53
docs/cli-feature/font.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# 字体
|
||||
|
||||
## 自定义字体
|
||||
|
||||
脚手架提供了两种自定义字体
|
||||
|
||||
- [HarmonyOS_Sans_Regular.woff2](https://developer.huawei.com/consumer/cn/design/resource-V1/)
|
||||
- [LXGWBright-Regular.woff2](https://github.com/lxgw/LxgwWenKai)
|
||||
|
||||
## 使用字体
|
||||
|
||||
您可以通过 tailwindcss 的类来调整局部字体,例如:
|
||||
|
||||
```html
|
||||
<div class="font-Harmony">
|
||||
这是 Harmony 字体
|
||||
</div>
|
||||
|
||||
<div class="font-LXGW">
|
||||
这是落霞孤鹜字体
|
||||
</div>
|
||||
```
|
||||
|
||||
您可以在 `style.css` 中定义全局字体,例如:
|
||||
|
||||
```css
|
||||
@plugin "daisyui/theme" {
|
||||
name: "light";
|
||||
--color-primary: #5092d4;
|
||||
--color-secondary: #bfd7f1;
|
||||
--color-accent: #76b9ef;
|
||||
/* 自定义字体在 App.vue 中加载 */
|
||||
--font-sans: '"Harmony", "Microsoft YaHei", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
|
||||
}
|
||||
```
|
||||
|
||||
如果您需要其他自定义字体,请在 `App.vue` 中定义 font-face,例如:
|
||||
|
||||
```vue
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "LXGW";
|
||||
src: url("/fonts/LXGWBright-Regular.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 后续目标
|
||||
|
||||
后续我们将推出能够实时修改全局字体的组件
|
||||
41
docs/cli-feature/icon.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# 图标
|
||||
|
||||
## 使用图标
|
||||
|
||||
可以通过 tailwindcss 中的图标 class 来直接使用图标,例如
|
||||
|
||||
```html
|
||||
<span class="icon-[material-symbols--person-outline]"></span>
|
||||
```
|
||||
|
||||
而你需要的图标可以在 [Iconify Material 图标库](https://icon-sets.iconify.design/material-symbols/) 中搜索。
|
||||
|
||||
通过点击图标库中的某个图标,你需要在详情栏中选择 CSS - Tailwind Css 栏,其中会告知你该图标对应的 class 名称。
|
||||
|
||||
## 调整图标
|
||||
|
||||
如果您需要调整颜色,您可以在 class 中添加 `text-<color>` 来调整图标颜色,例如
|
||||
|
||||
```html
|
||||
<span class="icon-[material-symbols--person-outline] text-red-500"></span>
|
||||
```
|
||||
|
||||
如果您需要调整图标大小,您可以在 class 中添加 `text-<size>` 来调整图标大小,例如
|
||||
|
||||
```html
|
||||
<span class="icon-[material-symbols--person-outline] text-2xl"></span>
|
||||
```
|
||||
|
||||
更多的调整方法可以参考 [Iconify 图标库调整](https://iconify.design/docs/usage/css/tailwind/tailwind4/size-color.html)
|
||||
|
||||
## 需要更多图标
|
||||
|
||||
您可以在 [Iconify 图标库](https://icon-sets.iconify.design/) 中搜索更多图标。
|
||||
|
||||
我们为您预装的是 Material Symbols 图标库。如果您需要使用别的图标库,可以通过 `@iconify-json/{prefix}` 来 install,注意 {prefix} 的名称要改成中划线模式,例如您需要安装名为 Material Symbols Light 的图标库
|
||||
|
||||
```bash
|
||||
bun add -D @iconify-json/material-symbols-light
|
||||
```
|
||||
|
||||
更多的安装方法可以参考 [Iconify 图标库安装](https://iconify.design/docs/usage/css/tailwind/tailwind4/)
|
||||
39
docs/cli-feature/international.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# 国际化
|
||||
|
||||
## 使用 i18n
|
||||
|
||||
如果您需要适配国际化,Hucky 提供了 i18n 基础配置,首先您可以先查阅 [i18n 文档](https://vue-i18n.intlify.dev/guide/advanced/composition.html) 其中的 composition api 部分以获取基础知识。
|
||||
|
||||
其中您主要需要使用到的是 `useI18n` 函数,它返回一个对象,其中包含了 `t` 方法,用于翻译字符串。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<p>{{ t("nav.home") }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
```
|
||||
|
||||
关于 t 方法后方的参数,它来自于 `i18n/index.ts` 中的实现 `messagesInterface` 接口的对象。也就是说如果您需要添加新的翻译字符串,您需要首先在 `messagesInterface` 接口中添加新的属性,然后在类似 `zh_CNMessages` 和 `en_USMessages` 等具体语言文件中添加对应的翻译字符串。
|
||||
|
||||
## 切换语言
|
||||
|
||||
切换语言同样需要用到 `useI18n` 函数,其中包含了 `locale` 属性,您可以通过修改这个属性来切换语言。
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { locale } = useI18n();
|
||||
|
||||
locale.value = "EN"; // 切换到英文,由 i18n/index.ts 中的 locale 配置决定
|
||||
</script>
|
||||
```
|
||||
|
||||
Hucky 本身提供了 `ChangeLanguageDropdownButton` 组件以及相对应的一些封装方法,通过使用这个组件您可以让用户自行更改需要的语言。您也可以深入 `hooks/globalLanguageHook` 中的方法来拓展更多功能。
|
||||
37
docs/cli-feature/motion.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 动画
|
||||
|
||||
## 使用
|
||||
|
||||
:::warning 警告
|
||||
motion-v 的自动引入系统经过测试无法使用,您仍需要手动引入
|
||||
:::
|
||||
|
||||
Hucky 安装了 [motion-v](https://motion.net.cn/docs/vue) 库,您可以在组件中使用它来添加动画效果。
|
||||
|
||||
简单来看,您只需要引入 motion-v 的 `motion` 组件,并使用它来包裹需要添加动画效果的元素,然后编写 :animate 指令即可。
|
||||
|
||||
```html
|
||||
<motion.div :animate="{ rotate: 360 }" />
|
||||
```
|
||||
|
||||
您也可以通过设置 :initial 指令来设置元素的初始状态。
|
||||
|
||||
```html
|
||||
<motion.div :animate="{ rotate: 360 }" :initial="{ rotate: 0 }" />
|
||||
```
|
||||
|
||||
通过 `AnimatePresence` 组件,您可以在元素进入或退出时添加动画效果。
|
||||
|
||||
```html
|
||||
<AnimatePresence>
|
||||
<motion.div v-if="show" :exit="{ opacity: 0 }" />
|
||||
</AnimatePresence>
|
||||
```
|
||||
|
||||
:::tip 提示
|
||||
exit 会自动为元素的进入也添加动画效果。
|
||||
:::
|
||||
|
||||
## 3D 动画
|
||||
|
||||
Hucky 选用 [GSAP](https://gsap.com/docs/v3/) 作为 3D 动画库,您可以学习相关知识
|
||||
34
docs/cli-feature/radash.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# 辅助工具
|
||||
|
||||
## 使用
|
||||
|
||||
Hucky 安装了 [Radash](https://radash.uihtm.com/) 库,用于辅助处理数组、对象、字符串等数据。
|
||||
|
||||
您可以自行查询文档来了解更多用法。
|
||||
|
||||
## 节流
|
||||
|
||||
在查阅 Radash 文档时发现其 `throttle` 函数的使用存在错误,在这里补充纠正,在文档中的用法如下:
|
||||
|
||||
```ts
|
||||
import { throttle } from 'radash'
|
||||
|
||||
const throttledScroll = throttle(() => {
|
||||
console.log('Scroll event handled')
|
||||
}, 100)
|
||||
|
||||
window.addEventListener('scroll', throttledScroll)
|
||||
// 滚动时,函数最多每100ms执行一次
|
||||
```
|
||||
|
||||
事实上 `throttle` 函数的第一个参数才是节流时间,第二个参数是函数。正确写法应该如下:
|
||||
|
||||
```ts
|
||||
import { throttle } from 'radash'
|
||||
|
||||
const throttledScroll = throttle(100, () => {
|
||||
console.log('Scroll event handled')
|
||||
})
|
||||
|
||||
window.addEventListener('scroll', throttledScroll)
|
||||
```
|
||||
13
docs/cli-feature/scroll.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 滚动
|
||||
|
||||
## 平滑滚动
|
||||
|
||||
Hucky 选择了 [Lenis](https://lenis.darkroom.engineering/) 为项目提供平滑滚动,您可以在 `App.vue` 中控制平滑滚动的开关。
|
||||
|
||||
## 滚动失效
|
||||
|
||||
如果您遇到了滚动失效的情况,这是因为 Lenis 和普通滚动冲突,您可以通过添加 `data-lenis-prevent` 属性来防止 lenis 平滑滚动。例如:
|
||||
|
||||
```html
|
||||
<div data-lenis-prevent></div>
|
||||
```
|
||||
19
docs/cli-feature/three.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 3D
|
||||
|
||||
## Three.js
|
||||
|
||||
Hucky 选用 [three.js](https://threejs.org/manual/#zh) 作为 3D 框架,您可以学习相关知识
|
||||
|
||||
## 首页
|
||||
|
||||
Hucky 的首页层叠关系非常复杂,在这里做解释
|
||||
|
||||
- z-10 导航栏 fixed 位于所有元素的最上方
|
||||
- z-8 悬浮文字 仅次于导航栏,要求用户最先看到
|
||||
- z-7 Three.js Canvas 位于悬浮文字下方,要求用户在看到悬浮文字后立即看到 3D 模型,同时将 canvas 背景透明,让用户可以看到 canvas 下方的主内容
|
||||
- z-5 主内容区域 位于 Three.js Canvas 下方,主要提供了背景颜色
|
||||
- z-1 页脚 位于所有元素的最下方且 fixed,完成了类似幕布拉开的效果
|
||||
|
||||
## 3D 动画
|
||||
|
||||
Hucky 选用 [GSAP](https://gsap.com/docs/v3/) 作为 3D 动画库,您可以学习相关知识
|
||||
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 673 KiB |
@@ -24,6 +24,19 @@ export default typescriptEslint.config(
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/max-attributes-per-line": "off",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/html-self-closing": [
|
||||
"warn",
|
||||
{
|
||||
html: {
|
||||
void: "always",
|
||||
normal: "always",
|
||||
component: "always",
|
||||
},
|
||||
svg: "always",
|
||||
math: "always",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Hucky</title>
|
||||
<title>Hucky - 次世代 AI 脚手架</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
18
package.json
@@ -16,17 +16,31 @@
|
||||
"docs:preview": "vitepress preview docs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"axios": "^1.11.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"daisyui": "^5.0.50",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.2",
|
||||
"gsap": "^3.14.2",
|
||||
"lenis": "^1.3.17",
|
||||
"lucide-vue-next": "^0.563.0",
|
||||
"motion-v": "^1.6.1",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.4.1",
|
||||
"radash": "^12.1.1",
|
||||
"reka-ui": "^2.8.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"three": "^0.183.1",
|
||||
"vue": "^3.5.17",
|
||||
"vue-echarts": "^8.0.1",
|
||||
"vue-i18n": "11",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/material-symbols": "^1.2.55",
|
||||
"@iconify/tailwind4": "^1.2.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/node": "^24.1.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
@@ -39,9 +53,11 @@
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"prettier": "^3.6.2",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.43.0",
|
||||
"unplugin-auto-import": "^20.1.0",
|
||||
"unplugin-vue-components": "^31.0.0",
|
||||
"vite": "^7.0.0",
|
||||
"vitepress": "^2.0.0-alpha.15",
|
||||
"vitepress-plugin-group-icons": "^1.7.1",
|
||||
|
||||
BIN
public/contact/wechat.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
public/fonts/HarmonyOS_Sans_Regular.woff2
Normal file
BIN
public/fonts/LXGWBright-Regular.woff2
Normal file
BIN
public/logo.png
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 673 KiB |
BIN
public/model/christmas_ball.glb
Normal file
BIN
public/qq-rc.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
45
src/App.vue
@@ -1,4 +1,27 @@
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useLanguageStore } from "./stores/LanguageStore";
|
||||
import Lenis from "lenis";
|
||||
|
||||
// 开启 lenis 平滑滚动
|
||||
const lenis = new Lenis({
|
||||
// 通过添加 data-lenis-prevent 属性来防止 lenis 平滑滚动
|
||||
prevent: (node) =>
|
||||
node.tagName === "SELECT" || node.hasAttribute("data-lenis-prevent"),
|
||||
});
|
||||
|
||||
function raf(time) {
|
||||
lenis.raf(time);
|
||||
requestAnimationFrame(raf);
|
||||
}
|
||||
|
||||
requestAnimationFrame(raf);
|
||||
|
||||
onMounted(() => {
|
||||
// 加载 i18n 初始语言
|
||||
useI18n().locale.value = useLanguageStore().language;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
@@ -6,4 +29,22 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style>
|
||||
/* 接触 scoped 开启全局模式加载字体 */
|
||||
|
||||
@font-face {
|
||||
font-family: "LXGW";
|
||||
src: url("/fonts/LXGWBright-Regular.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Harmony";
|
||||
src: url("/fonts/HarmonyOS_Sans_Regular.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
</style>
|
||||
|
||||
28
src/apis/system/oauth.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { httpWithCustomReturn } from "@/utils/http";
|
||||
|
||||
export const loginByEmailPasswordWithOauthApi = ({ email, password }) => {
|
||||
return httpWithCustomReturn<{
|
||||
code: number;
|
||||
msg: string;
|
||||
data: {
|
||||
scope: string;
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
};
|
||||
}>({
|
||||
url: "/admin-api/system/oauth2/token",
|
||||
method: "POST",
|
||||
params: {
|
||||
grant_type: "password",
|
||||
username: email,
|
||||
password,
|
||||
client_id: "rc",
|
||||
client_secret: "rc",
|
||||
},
|
||||
headers: {
|
||||
"tenant-id": 1,
|
||||
},
|
||||
});
|
||||
};
|
||||
26
src/apis/system/user.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { httpWithCustomReturn } from "@/utils/http";
|
||||
|
||||
export const getUserInfoApi = () => {
|
||||
return httpWithCustomReturn<{
|
||||
code: number;
|
||||
msg: string;
|
||||
data: {
|
||||
id: number;
|
||||
username: string;
|
||||
nickname: string;
|
||||
email: string;
|
||||
mobile: string;
|
||||
sex: number;
|
||||
avatar: string;
|
||||
loginIp: string;
|
||||
loginDate: null;
|
||||
createTime: number;
|
||||
roles: object[];
|
||||
dept: null;
|
||||
posts: null;
|
||||
};
|
||||
}>({
|
||||
url: "/admin-api/system/user/profile/get",
|
||||
method: "GET",
|
||||
});
|
||||
};
|
||||
17
src/apis/temp/auth-qrcode.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { httpWithCustomReturn } from "@/utils/http";
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns 直接返回图片二进制数据
|
||||
*/
|
||||
export const getAuthQrcodeApi = () => {
|
||||
return httpWithCustomReturn<Blob>({
|
||||
url: "https://api.qrtool.cn",
|
||||
method: "get",
|
||||
responseType: "blob",
|
||||
params: {
|
||||
text: "测试",
|
||||
size: 600,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
import http from "../utils/http";
|
||||
|
||||
/**
|
||||
* This is an example, please remove if not needed
|
||||
*/
|
||||
export const getUserList = () => {
|
||||
return http({
|
||||
url: "/user/list",
|
||||
method: "get",
|
||||
});
|
||||
};
|
||||
|
||||
export const insertUser = (data: { account: string; passowrd: string }) => {
|
||||
return http({
|
||||
url: "/user/insert",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 673 KiB |
22
src/assets/logo.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="100%" height="100%">
|
||||
<g fill="none" stroke="#000000" stroke-width="8" stroke-linejoin="miter" stroke-linecap="square">
|
||||
|
||||
<!-- Bottom horizontal line has been removed -->
|
||||
|
||||
<!-- Left Angle Bracket < ─ farther left + shorter -->
|
||||
<polyline points="60,80 30,100 60,120" />
|
||||
|
||||
<!-- Right Angle Bracket > ─ farther right + shorter -->
|
||||
<polyline points="140,80 170,100 140,120" />
|
||||
|
||||
<!-- Ladder Vertical Rails ─ unchanged width -->
|
||||
<line x1="80" y1="50" x2="80" y2="150" />
|
||||
<line x1="120" y1="50" x2="120" y2="150" />
|
||||
|
||||
<!-- Ladder Rungs ─ unchanged -->
|
||||
<line x1="80" y1="75" x2="120" y2="75" />
|
||||
<line x1="80" y1="100" x2="120" y2="100" />
|
||||
<line x1="80" y1="125" x2="120" y2="125" />
|
||||
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 827 B |
68
src/components/button/ChangeLanguageDropdownButton.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import { useGlobalLanguageHook } from "@/hooks/globalLanguageHook";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { changeGlobalLanguage, curGlobalLanguage, optionalLanguages } =
|
||||
useGlobalLanguageHook();
|
||||
|
||||
const { locale, t } = useI18n();
|
||||
|
||||
const onClickChangeLocal = (newLanguage: string) => {
|
||||
changeGlobalLanguage(newLanguage, locale);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown dropdown-end">
|
||||
<div
|
||||
tabindex="0"
|
||||
role="button"
|
||||
class="btn btn-sm btn-ghost gap-1 px-1.5 font-bold"
|
||||
>
|
||||
<svg
|
||||
class="text-base-content/70 size-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linejoin="round"
|
||||
stroke-linecap="round"
|
||||
stroke-width="2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
d="M12 21a9 9 0 1 0 0-18m0 18a9 9 0 1 1 0-18m0 18c2.761 0 3.941-5.163 3.941-9S14.761 3 12 3m0 18c-2.761 0-3.941-5.163-3.941-9S9.239 3 12 3M3.5 9h17m-17 6h17"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-base-content/70">{{ t("nav.locale") }}</span>
|
||||
<span
|
||||
class="icon-[material-symbols--keyboard-arrow-down-rounded] text-lg text-base-content/70"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
tabindex="0"
|
||||
class="dropdown-content bg-base-200 text-base-content rounded-box top-px mt-14 w-56 overflow-y-auto border-[length:var(--border)] border-white/5 shadow-2xl outline-[length:var(--border)] outline-black/5"
|
||||
>
|
||||
<ul class="menu menu-sm w-full">
|
||||
<li class="menu-title text-xs">{{ t("nav.locale") }}</li>
|
||||
<li
|
||||
v-for="optionalLanguage in optionalLanguages"
|
||||
:key="optionalLanguage.label"
|
||||
>
|
||||
<button
|
||||
:class="[
|
||||
curGlobalLanguage === optionalLanguage.label ? 'menu-active' : '',
|
||||
]"
|
||||
@click="onClickChangeLocal(optionalLanguage.label)"
|
||||
>
|
||||
<span class="pe-4 font-mono font-bold opacity-40">
|
||||
{{ optionalLanguage.label }}
|
||||
</span>
|
||||
<span class="font-[sans-serif]">{{ optionalLanguage.value }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
72
src/components/button/ChangeThemeDropdownButton.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import { useGlobalThemeHook } from "@/hooks/globalThemeHook";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Lenis from "lenis";
|
||||
|
||||
// 开启 lenis 平滑滚动
|
||||
const lenis = new Lenis();
|
||||
|
||||
function raf(time) {
|
||||
lenis.raf(time);
|
||||
requestAnimationFrame(raf);
|
||||
}
|
||||
|
||||
requestAnimationFrame(raf);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { changeGlobalTheme, curGlobalTheme, optionalThemes } =
|
||||
useGlobalThemeHook();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-sm btn-ghost px-1.5">
|
||||
<div
|
||||
class="bg-base-100 group-hover:border-base-content/20 border-base-content/10 grid shrink-0 grid-cols-2 gap-0.5 rounded-md border p-1 transition-colors"
|
||||
>
|
||||
<div class="bg-base-content size-1 rounded-full" />
|
||||
<div class="bg-primary size-1 rounded-full" />
|
||||
<div class="bg-secondary size-1 rounded-full" />
|
||||
<div class="bg-accent size-1 rounded-full" />
|
||||
</div>
|
||||
<span class="text-base-content/70">{{ t("nav.theme") }}</span>
|
||||
<span
|
||||
class="icon-[material-symbols--keyboard-arrow-down-rounded] text-lg text-base-content/70"
|
||||
/>
|
||||
</div>
|
||||
<ul
|
||||
data-lenis-prevent
|
||||
tabindex="-1"
|
||||
class="dropdown-content bg-base-300 rounded-box z-1 w-52 p-2 shadow-2xl max-h-84 overflow-auto mt-6"
|
||||
>
|
||||
<li class="menu-title text-xs my-1">{{ t("nav.theme") }}</li>
|
||||
<li
|
||||
v-for="optionalTheme in optionalThemes"
|
||||
:key="optionalTheme"
|
||||
class="my-1"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="theme-dropdown"
|
||||
:class="[
|
||||
'theme-controller',
|
||||
'btn',
|
||||
'btn-sm',
|
||||
'btn-block',
|
||||
optionalTheme === curGlobalTheme ? 'btn-primary' : 'btn-ghost',
|
||||
'justify-start',
|
||||
]"
|
||||
:aria-label="optionalTheme"
|
||||
:value="optionalTheme"
|
||||
:checked="optionalTheme === curGlobalTheme"
|
||||
@change="changeGlobalTheme(optionalTheme)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
113
src/components/button/UserAuthNavButton.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<script setup lang="ts">
|
||||
import { renderAuthDialog } from "@/hooks/dialog/renderAuthDialog";
|
||||
import { useUserStore } from "@/stores/UserStore";
|
||||
|
||||
// 用户 store
|
||||
const userStore = useUserStore();
|
||||
|
||||
const dropdownVisible = ref(true);
|
||||
|
||||
const closeDropdown = () => {
|
||||
dropdownVisible.value = false;
|
||||
setTimeout(() => {
|
||||
dropdownVisible.value = true;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
userStore.clearUserInfo();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown dropdown-hover dropdown-end">
|
||||
<div
|
||||
v-if="!userStore.checkUserLogin()"
|
||||
role="button"
|
||||
class="btn btn-sm btn-primary px-3"
|
||||
@click="
|
||||
renderAuthDialog('login');
|
||||
closeDropdown();
|
||||
"
|
||||
>
|
||||
<span>
|
||||
{{ $t("nav.login") }}
|
||||
</span>
|
||||
<span class="icon-[material-symbols--login-rounded] text-sm" />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
role="button"
|
||||
class="text-sm text-base-content/70 px-3 cursor-pointer"
|
||||
>
|
||||
{{ userStore.userInfo?.nickname || userStore.userInfo?.username }}
|
||||
</div>
|
||||
|
||||
<!-- 未登录用户下拉框 -->
|
||||
<ul
|
||||
v-if="dropdownVisible && !userStore.checkUserLogin()"
|
||||
tabindex="-1"
|
||||
class="dropdown-content z-1 w-100 p-2"
|
||||
>
|
||||
<div class="card bg-base-200 w-96 mt-5 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ $t("nav.login_more_title") }}</h2>
|
||||
<p>{{ $t("nav.login_more") }}</p>
|
||||
<div class="card-actions justify-end">
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
@click="
|
||||
renderAuthDialog('register');
|
||||
closeDropdown();
|
||||
"
|
||||
>
|
||||
{{ $t("nav.register") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="
|
||||
renderAuthDialog('login');
|
||||
closeDropdown();
|
||||
"
|
||||
>
|
||||
{{ $t("nav.login") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
<!-- 已登录用户下拉框 -->
|
||||
<ul
|
||||
v-if="dropdownVisible && userStore.checkUserLogin()"
|
||||
tabindex="-1"
|
||||
class="dropdown-content z-1 w-100 p-2"
|
||||
>
|
||||
<div class="card bg-base-200 w-96 mt-5 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ $t("nav.logout") }}</h2>
|
||||
<p>{{ $t("nav.logout_more") }}</p>
|
||||
<div class="card-actions justify-end">
|
||||
<button class="btn btn-primary btn-sm" @click="handleLogout">
|
||||
{{ $t("nav.logout") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
<!-- 登录弹窗 -->
|
||||
<dialog id="my_modal" class="modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">Hello!</h3>
|
||||
<p class="py-4">Press ESC key or click outside to close</p>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
29
src/components/card/BasicIntroCard.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import AnimText from "@/components/special/AnimText.vue";
|
||||
import { navigateTo } from "@/utils/navigator";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="hero flex-1">
|
||||
<div class="hero-content text-center">
|
||||
<div class="max-w-md">
|
||||
<h1 class="text-5xl font-bold">
|
||||
<AnimText text="home.welcome" />
|
||||
</h1>
|
||||
<div class="py-6">
|
||||
<AnimText text="home.intro_line1" /><br />
|
||||
<AnimText text="home.intro_line2" /><br />
|
||||
<AnimText text="home.intro_line3" />
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="navigateTo('http://localhost:5174')"
|
||||
>
|
||||
<AnimText text="home.read_doc" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
17
src/components/card/DatePickerDisplayCard.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import DatePicker from "../date-picker/DatePicker.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card bg-base-100 w-96 shadow-sm">
|
||||
<div class="p-4 min-h-82">
|
||||
<DatePicker />
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">日期选择器</h2>
|
||||
<p>日期选择器组件演示</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
27
src/components/card/DevelopProgressCard.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import DevelopProgressDiagram from "../diagram/DevelopProgressDiagram.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
completed: number;
|
||||
pending: number;
|
||||
title: string;
|
||||
intro: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card bg-base-100 w-96 shadow-sm">
|
||||
<DevelopProgressDiagram
|
||||
:completed="props.completed"
|
||||
:pending="props.pending"
|
||||
/>
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ props.title }}</h2>
|
||||
<p>
|
||||
{{ props.intro }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
53
src/components/date-picker/DatePicker.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import type { DateValue } from "@internationalized/date";
|
||||
import {
|
||||
DateFormatter,
|
||||
getLocalTimeZone,
|
||||
today,
|
||||
} from "@internationalized/date";
|
||||
import { CalendarIcon } from "lucide-vue-next";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
|
||||
const defaultPlaceholder = today(getLocalTimeZone());
|
||||
const date = ref() as Ref<DateValue>;
|
||||
|
||||
const df = new DateFormatter("zh-CN", {
|
||||
dateStyle: "long",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover v-slot="{ close }">
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
variant="outline"
|
||||
:class="
|
||||
cn(
|
||||
'w-[240px] justify-start text-left font-normal',
|
||||
!date && 'text-muted-foreground',
|
||||
)
|
||||
"
|
||||
>
|
||||
<CalendarIcon />
|
||||
{{ date ? df.format(date.toDate(getLocalTimeZone())) : "选择日期" }}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
v-model="date"
|
||||
:default-placeholder="defaultPlaceholder"
|
||||
layout="month-and-year"
|
||||
initial-focus
|
||||
locale="zh-CN"
|
||||
@update:model-value="close"
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
62
src/components/diagram/DevelopProgressDiagram.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { use } from "echarts/core";
|
||||
import { PieChart } from "echarts/charts";
|
||||
import { TooltipComponent, LegendComponent } from "echarts/components";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import VChart from "vue-echarts";
|
||||
import { type EChartsOption } from "echarts";
|
||||
|
||||
use([TooltipComponent, LegendComponent, PieChart, CanvasRenderer]);
|
||||
|
||||
const props = defineProps<{
|
||||
completed: number;
|
||||
pending: number;
|
||||
}>();
|
||||
|
||||
const option = ref<EChartsOption>({
|
||||
color: ["#52c41a", "#ff4d4f"],
|
||||
legend: {
|
||||
top: "5%",
|
||||
left: "center",
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "开发进度",
|
||||
type: "pie",
|
||||
radius: ["40%", "70%"],
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: "#fff",
|
||||
borderWidth: 2,
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: "center",
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 32,
|
||||
fontWeight: "bold",
|
||||
formatter: "{d}%",
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: props.completed, name: "已完成" },
|
||||
{ value: props.pending, name: "待开发" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-96 w-96">
|
||||
<VChart :option="option" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
429
src/components/dialog/AuthDialog.vue
Normal file
@@ -0,0 +1,429 @@
|
||||
<script setup lang="ts">
|
||||
import { loginByEmailPasswordWithOauthApi } from "@/apis/system/oauth";
|
||||
import { getUserInfoApi } from "@/apis/system/user";
|
||||
import { getAuthQrcodeApi } from "@/apis/temp/auth-qrcode";
|
||||
import { closeAuthDialog } from "@/hooks/dialog/renderAuthDialog";
|
||||
import { useUserStore } from "@/stores/UserStore";
|
||||
import { AnimatePresence, motion } from "motion-v";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
defaultAuthFormType: {
|
||||
type: String as PropType<"login" | "register">,
|
||||
default: "login",
|
||||
},
|
||||
});
|
||||
|
||||
const authFormType = ref<"login" | "register" | "transition">(
|
||||
props.defaultAuthFormType,
|
||||
);
|
||||
|
||||
const changeAuthFormType = (type: "login" | "register") => {
|
||||
authFormType.value = "transition";
|
||||
setTimeout(() => {
|
||||
authFormType.value = type;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const isNotClose = ref(true);
|
||||
const closeAuthDialogWaitAnim = () => {
|
||||
isNotClose.value = false;
|
||||
setTimeout(() => {
|
||||
closeAuthDialog();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 登录方式
|
||||
const loginType = ref<"password" | "phone">("password");
|
||||
|
||||
// 登录二维码
|
||||
const authQrcode = ref("");
|
||||
|
||||
onMounted(async () => {
|
||||
const qrImg = await getAuthQrcodeApi();
|
||||
authQrcode.value = URL.createObjectURL(qrImg);
|
||||
});
|
||||
|
||||
// 控制登录表单宽度
|
||||
const loginFormWidth = computed(() => {
|
||||
return authFormType.value === "login" ? "900px" : "550px";
|
||||
});
|
||||
|
||||
// 登录表单数据绑定
|
||||
const loginForm = reactive({
|
||||
email: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
// 登录按钮点击事件
|
||||
const clickLoginButtonEvent = async () => {
|
||||
const { email, password } = loginForm;
|
||||
if (!email || !password) {
|
||||
return;
|
||||
}
|
||||
const res = await loginByEmailPasswordWithOauthApi({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
const store = useUserStore();
|
||||
store.setAccessToken(res.data.access_token);
|
||||
store.setRefreshToken(res.data.refresh_token);
|
||||
// 进一步获取用户信息
|
||||
const userInfoRes = await getUserInfoApi();
|
||||
store.setUserInfo(userInfoRes.data);
|
||||
closeAuthDialogWaitAnim();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="fixed inset-0 flex items-center justify-center z-50">
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
v-show="isNotClose"
|
||||
class="wrapper"
|
||||
:initial="{ opacity: 0, scale: 0 }"
|
||||
:animate="{ opacity: 1, scale: 1, width: loginFormWidth }"
|
||||
:transition="{
|
||||
duration: 0.4,
|
||||
scale: { type: 'spring', visualDuration: 0.4, bounce: 0.5 },
|
||||
}"
|
||||
:exit="{
|
||||
opacity: 0,
|
||||
scale: 0.2,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
class="icon-close bg-primary text-primary-content"
|
||||
@click="closeAuthDialogWaitAnim"
|
||||
>
|
||||
<span class="icon-[material-symbols--close-small]" />
|
||||
</span>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
v-if="authFormType === 'login'"
|
||||
class="form-box login"
|
||||
:initial="{
|
||||
y: -100,
|
||||
opacity: 0,
|
||||
}"
|
||||
:animate="{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
}"
|
||||
:transition="{
|
||||
duration: 0.3,
|
||||
scale: { type: 'spring', visualDuration: 0.3, bounce: 0.5 },
|
||||
}"
|
||||
:exit="{
|
||||
y: 100,
|
||||
opacity: 0,
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center gap-16">
|
||||
<!-- 二维码登录区域 -->
|
||||
<div class="flex flex-col items-center gap-4 w-[200px]">
|
||||
<div class="text-2xl select-none">
|
||||
{{ t("auth.scan_qr") }}
|
||||
</div>
|
||||
<img
|
||||
v-if="authQrcode"
|
||||
:src="authQrcode"
|
||||
alt="登录二维码"
|
||||
class="h-48 w-48"
|
||||
/>
|
||||
<div v-if="!authQrcode" class="skeleton h-48 w-48" />
|
||||
<div class="text-sm text-gray-500 select-none">
|
||||
{{ t("auth.scan_qr_more") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-48 w-[1px] bg-gray-300/70" />
|
||||
|
||||
<!-- 账号登录区域 -->
|
||||
<div>
|
||||
<div class="flex justify-center items-center gap-3">
|
||||
<div
|
||||
class="text-2xl cursor-pointer transition duration-300"
|
||||
:class="{ 'text-gray-400': loginType === 'phone' }"
|
||||
:transition="{
|
||||
duration: 0.3,
|
||||
}"
|
||||
@click="loginType = 'password'"
|
||||
>
|
||||
{{ t("auth.login_password") }}
|
||||
</div>
|
||||
<div class="h-8 w-[1px] bg-gray-300/70" />
|
||||
|
||||
<!-- 注册暂未开放 -->
|
||||
<div class="tooltip">
|
||||
<div class="tooltip-content">
|
||||
<div class="font-black">{{ t("auth.not_open") }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-2xl cursor-pointer transition duration-300"
|
||||
:class="{ 'text-gray-400': loginType === 'password' }"
|
||||
:transition="{
|
||||
duration: 0.3,
|
||||
}"
|
||||
>
|
||||
{{ t("auth.login_phone") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<form action="#">
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--mail-outline]" />
|
||||
</span>
|
||||
<input v-model="loginForm.email" type="text" required />
|
||||
<label>{{ t("auth.email") }}</label>
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--lock-outline]" />
|
||||
</span>
|
||||
<input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
required
|
||||
/>
|
||||
<label>{{ t("auth.password") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="remember-forgot">
|
||||
<label class="label">
|
||||
<input type="checkbox" class="checkbox checkbox-xs" />
|
||||
{{ t("auth.remember_me") }}
|
||||
</label>
|
||||
<a class="label" href="#">{{
|
||||
t("auth.forget_password")
|
||||
}}</a>
|
||||
</div>
|
||||
<button
|
||||
class="btn bg-primary text-primary-content"
|
||||
@click="clickLoginButtonEvent"
|
||||
>
|
||||
{{ t("auth.login") }}
|
||||
</button>
|
||||
<div
|
||||
class="login-register"
|
||||
@click="changeAuthFormType('register')"
|
||||
>
|
||||
<p>
|
||||
{{ t("auth.no_account") }}
|
||||
<a class="register-link cursor-pointer">
|
||||
{{ t("auth.register") }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
<!-- 注册表单 -->
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
v-if="authFormType === 'register'"
|
||||
class="form-box register"
|
||||
:initial="{
|
||||
y: -100,
|
||||
opacity: 0,
|
||||
}"
|
||||
:animate="{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
}"
|
||||
:transition="{
|
||||
duration: 0.3,
|
||||
scale: { type: 'spring', visualDuration: 0.3, bounce: 0.5 },
|
||||
}"
|
||||
:exit="{
|
||||
y: 100,
|
||||
opacity: 0,
|
||||
}"
|
||||
>
|
||||
<div class="flex justify-center items-center gap-3">
|
||||
<div class="text-2xl cursor-pointer">
|
||||
{{ t("auth.register") }}
|
||||
</div>
|
||||
</div>
|
||||
<form action="#">
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--mail-outline]" />
|
||||
</span>
|
||||
<input type="text" required />
|
||||
<label>{{ t("auth.email") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="input-box">
|
||||
<span class="icon">
|
||||
<span class="icon-[material-symbols--lock-outline]" />
|
||||
</span>
|
||||
<input type="password" required />
|
||||
<label>{{ t("auth.password") }}</label>
|
||||
</div>
|
||||
|
||||
<div class="remember-forgot">
|
||||
<label class="label">
|
||||
<input type="checkbox" class="checkbox checkbox-xs" />
|
||||
{{ t("auth.agree_terms") }}
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn bg-primary text-primary-content"
|
||||
>
|
||||
{{ t("auth.register") }}
|
||||
</button>
|
||||
<div
|
||||
class="login-register"
|
||||
@click="changeAuthFormType('login')"
|
||||
>
|
||||
<p>
|
||||
{{ t("auth.have_account") }}
|
||||
<a class="login-link cursor-pointer">
|
||||
{{ t("auth.login") }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.wrapper {
|
||||
height: 430px;
|
||||
position: relative;
|
||||
background: transparent;
|
||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 20px;
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wrapper .form-box {
|
||||
padding: 40px 80px;
|
||||
}
|
||||
|
||||
.wrapper .icon-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 2em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-bottom-left-radius: 20px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
position: relative;
|
||||
width: 350px;
|
||||
height: 50px;
|
||||
border-bottom: 2px solid;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.input-box label {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 5px;
|
||||
transform: translateY(-50%);
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
pointer-events: none;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.input-box input:focus ~ label,
|
||||
.input-box input:valid ~ label {
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
.input-box input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
padding: 0 35px 0 5px;
|
||||
}
|
||||
|
||||
.input-box .icon {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
font-size: 1.2rem;
|
||||
line-height: 57px;
|
||||
}
|
||||
|
||||
.remember-forgot {
|
||||
font-size: 0.9em;
|
||||
font-weight: 500;
|
||||
margin: -15px 0 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.remember-forgot label input {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.remember-forgot a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.remember-forgot a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-register {
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
margin: 25px 0 10px;
|
||||
}
|
||||
|
||||
.login-register p a {
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.login-register p a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
21
src/components/icon/BiliBiliIcon.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
height="2500"
|
||||
viewBox="51.2 51.2 921.6 921.6"
|
||||
width="2500"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g fill="#f16c8d">
|
||||
<path
|
||||
d="m729.329 373.95c-9.795-5.945-19.062-6.785-19.144-6.785l-1.065-.05c-57.2-3.866-121.165-5.832-190.126-5.832l-13.988.005c-68.956 0-132.925 1.96-190.12 5.831l-1.066.052c-.082 0-9.349.84-19.144 6.784-15.047 9.129-24.273 25.948-27.417 49.97-10.071 76.913-4.383 173.65.19 251.393 2.938 49.966 33.407 62.459 85.048 67.149 10.782.988 69.089 5.867 159.508 5.893v-.005c90.42-.02 148.726-4.905 159.514-5.888 51.64-4.69 82.11-17.183 85.043-67.15 4.577-77.741 10.26-174.479.19-251.391-3.15-24.028-12.376-40.848-27.423-49.977zm-390.99 172.718a23.65 23.65 0 0 1 -31.687-10.845 23.68 23.68 0 0 1 10.844-31.687c2.038-1.004 50.693-24.725 110.541-43.065a23.68 23.68 0 1 1 13.88 45.292c-56.294 17.25-103.111 40.074-103.577 40.305zm268.898 35.886c-.44 2.232-11.269 54.64-50.939 54.64-21.442 0-36.1-14.049-44.984-26.772-8.694 12.708-22.805 26.772-42.655 26.772-35.533 0-50.135-48.266-51.681-53.77a11.366 11.366 0 0 1 21.878-6.17c2.75 9.652 14.13 37.202 29.798 37.202 16.374 0 28.892-23.644 31.985-31.928a11.372 11.372 0 0 1 10.65-7.388h.06a11.376 11.376 0 0 1 10.63 7.506c.107.286 11.965 31.815 34.314 31.815 20.864 0 28.565-35.952 28.641-36.32a11.346 11.346 0 0 1 13.358-8.94 11.361 11.361 0 0 1 8.945 13.353zm110.116-46.736a23.68 23.68 0 0 1 -31.683 10.844c-.47-.23-47.472-23.116-103.572-40.31a23.69 23.69 0 0 1 -15.708-29.583 23.67 23.67 0 0 1 29.578-15.703c59.848 18.34 108.498 42.061 110.551 43.065a23.68 23.68 0 0 1 10.834 31.687z"
|
||||
/>
|
||||
<path
|
||||
d="m849.92 51.2h-675.84c-67.866 0-122.88 55.014-122.88 122.88v675.84c0 67.87 55.014 122.88 122.88 122.88h675.84c67.87 0 122.88-55.01 122.88-122.88v-675.84c0-67.86-55.01-122.88-122.88-122.88zm-36.603 627.45c-2.626 44.58-21.821 78.634-55.516 98.49-25.682 15.134-54.175 19.486-81.137 21.938-32.455 2.95-92.718 6.098-164.664 6.119-71.941-.02-132.209-3.164-164.664-6.119-26.962-2.452-55.455-6.804-81.132-21.939-33.695-19.855-52.89-53.903-55.51-98.483-4.706-80.133-10.574-179.855.194-262.108 10.654-81.383 70.102-104.976 100.612-106.168a2482.642 2482.642 0 0 1 81.423-4.086c-7.536-8.535-19.88-23.322-28.815-38.114-13.737-22.737 8.53-41.687 8.53-41.687s23.68-20.367 44.528 5.213c15.698 19.266 38.38 55.997 48.62 72.954l53.207-.215c13.26 0 26.332.072 39.22.215 10.24-16.957 32.92-53.683 48.619-72.954 20.843-25.58 44.528-5.213 44.528-5.213s22.262 18.95 8.525 41.687c-8.934 14.792-21.279 29.579-28.815 38.114 28.36.978 55.562 2.34 81.423 4.08 30.515 1.198 89.958 24.791 100.613 106.174 10.778 82.248 4.915 181.97.21 262.103z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
48
src/components/icon/DouYinIcon.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
t="1772042599876"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
p-id="1645"
|
||||
width="200"
|
||||
height="200"
|
||||
>
|
||||
<path
|
||||
d="M855.52032 0H165.44032A165.696 165.696 0 0 0 0.00032 165.92v689.376a165.696 165.696 0 0 0 165.344 166.016h690.272a165.696 165.696 0 0 0 165.408-165.984V166.016A165.76 165.76 0 0 0 855.52032 0z"
|
||||
fill="#170B1A"
|
||||
p-id="1646"
|
||||
/>
|
||||
<path
|
||||
d="M511.42432 302.08c0.576-64.32 0-128.576 0.576-192.832h131.392c-0.576 11.328 1.152 22.72 2.88 33.6h-96.704v522.24a124.8 124.8 0 0 1-16 63.68 107.84 107.84 0 0 1-83.008 52.864 111.424 111.424 0 0 1-63.168-13.12 108.416 108.416 0 0 1-36.928-32.96c33.536 18.816 77.312 17.088 109.76-3.968A111.68 111.68 0 0 0 512.00032 638.272c-0.576-112.064-0.576-224.128-0.576-336.192z m216.768-36.992c18.176 11.392 38.656 20.48 59.712 25.024 12.544 2.88 25.024 4.032 38.08 4.032v29.568a187.36 187.36 0 0 1-97.792-58.56v-0.064z"
|
||||
fill="#25F4EE"
|
||||
p-id="1647"
|
||||
/>
|
||||
<path
|
||||
d="M274.75232 428.928a238.016 238.016 0 0 1 159.36-33.6v31.36c-14.72 0.576-29.376 2.272-43.84 5.12a249.44 249.44 0 0 0-97.92 43.84c-31.232 23.296-55.104 55.104-71.68 90.368a243.424 243.424 0 0 0-23.296 108.16c0 40.96 11.392 80.768 30.72 116.608 9.152 16.448 19.392 32.384 33.024 45.44a233.44 233.44 0 0 1-68.288-75.072 246.24 246.24 0 0 1-33.6-131.392 250.144 250.144 0 0 1 35.904-120 240.352 240.352 0 0 1 79.616-80.832z"
|
||||
fill="#25F4EE"
|
||||
p-id="1648"
|
||||
/>
|
||||
<path
|
||||
d="M549.56832 142.784h97.28c3.392 18.752 10.24 36.416 18.752 53.504 13.632 26.176 33.024 49.472 58.048 64.832 1.664 1.152 2.88 2.304 3.968 3.968a186.688 186.688 0 0 0 98.432 58.56c0.576 34.176 0 68.864 0 103.04a308.16 308.16 0 0 1-180.928-57.472c0 81.92 0 163.84 0.576 245.76 0 10.816 0.576 21.632 0 32.96a268.8 268.8 0 0 1-35.264 113.792 247.392 247.392 0 0 1-68.288 77.44 219.296 219.296 0 0 1-124.608 42.56c-22.72 0.64-45.44-0.512-67.648-5.632a243.52 243.52 0 0 1-87.04-38.08l-1.728-1.728a202.464 202.464 0 0 1-33.024-45.504 244.32 244.32 0 0 1-30.72-116.672 245.6 245.6 0 0 1 23.36-108.096c16.512-35.2 40.96-67.072 71.68-90.432a249.44 249.44 0 0 1 141.632-48.896c0.576 13.12 0 26.176 0.576 38.656v66.56a94.176 94.176 0 0 0-51.776-1.728 128.32 128.32 0 0 0-55.68 27.328c-9.824 8.448-17.952 18.688-23.936 30.144a112.16 112.16 0 0 0-11.392 63.744c2.432 21.216 11.168 41.216 25.024 57.472 9.088 11.328 21.056 19.904 33.024 27.84 9.664 13.632 22.144 25.024 36.928 33.024 19.392 10.24 41.536 14.72 63.168 13.12 34.112-2.304 65.984-23.36 83.072-52.992a124.8 124.8 0 0 0 15.936-63.68c1.152-175.232 0.576-349.312 0.576-523.392z"
|
||||
fill="#FFFFFF"
|
||||
p-id="1649"
|
||||
/>
|
||||
<path
|
||||
d="M646.84832 142.784c11.328 0.576 22.72 0 34.688 0 0 38.08 11.968 76.224 34.112 107.52 2.88 3.968 5.76 7.36 8.576 10.816-25.088-15.36-44.992-38.656-58.048-64.832a214.208 214.208 0 0 1-19.328-53.504z m179.2 180.928c12.48 2.88 24.96 3.968 38.08 3.968v132.544c-64.832 0.576-129.664-21.056-182.592-59.136v262.784a229.76 229.76 0 0 1-5.696 59.2 244.32 244.32 0 0 1-96.704 147.328c-25.92 18.56-55.36 31.744-86.464 38.656a240.448 240.448 0 0 1-113.792-1.664 239.776 239.776 0 0 1-115.52-69.44 234.016 234.016 0 0 0 87.104 38.08c22.144 5.184 44.928 6.336 67.648 5.76a219.296 219.296 0 0 0 124.608-42.688 254.784 254.784 0 0 0 68.288-77.376 269.024 269.024 0 0 0 35.2-113.792 319.072 319.072 0 0 0 0-32.96c-0.512-81.92-0.512-163.84-0.512-245.76a308.16 308.16 0 0 0 180.928 57.472c-0.576-34.176 0-68.8-0.576-102.976z"
|
||||
fill="#FE2C55"
|
||||
p-id="1650"
|
||||
/>
|
||||
<path
|
||||
d="M434.62432 426.112c12.544 0 25.6 0.576 38.08 2.24v136a103.872 103.872 0 0 0-57.408-2.304c-35.84 8-65.984 35.264-78.528 70.016-12.48 34.112-7.36 73.92 14.208 102.912a119.552 119.552 0 0 1-32.96-27.84 107.328 107.328 0 0 1-25.024-57.472 112.16 112.16 0 0 1 11.392-63.68c5.632-11.392 14.208-21.632 23.872-30.208 15.936-13.632 35.84-22.144 55.744-27.264a94.176 94.176 0 0 1 51.776 1.664V463.68c-1.152-11.36-0.576-24.48-1.152-37.536v-0.032z"
|
||||
fill="#FE2C55"
|
||||
p-id="1651"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
37
src/components/icon/LogoIcon.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 200 200"
|
||||
stroke="currentColor"
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
stroke-width="8"
|
||||
stroke-linejoin="miter"
|
||||
stroke-linecap="square"
|
||||
>
|
||||
<!-- Bottom horizontal line has been removed -->
|
||||
|
||||
<!-- Left Angle Bracket < ─ farther left + shorter -->
|
||||
<polyline points="60,80 30,100 60,120" />
|
||||
|
||||
<!-- Right Angle Bracket > ─ farther right + shorter -->
|
||||
<polyline points="140,80 170,100 140,120" />
|
||||
|
||||
<!-- Ladder Vertical Rails ─ unchanged width -->
|
||||
<line x1="80" y1="50" x2="80" y2="150" />
|
||||
<line x1="120" y1="50" x2="120" y2="150" />
|
||||
|
||||
<!-- Ladder Rungs ─ unchanged -->
|
||||
<line x1="80" y1="75" x2="120" y2="75" />
|
||||
<line x1="80" y1="100" x2="120" y2="100" />
|
||||
<line x1="80" y1="125" x2="120" y2="125" />
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
27
src/components/icon/RedBookIcon.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 256 256"
|
||||
sodipodi:docname="XiaohongshuLOGO.png"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs id="defs1" />
|
||||
<g inkscape:groupmode="layer" inkscape:label="Image" id="g1">
|
||||
<path
|
||||
style="fill: #ff2842; stroke: none"
|
||||
d="M 29,0.33332825 C 13.959937,3.4666748 1.5356731,15.204498 0,31 -1.586103,47.314209 0,64.597672 0,81 v 102 c 0,18.76035 -4.7369685,44.19888 7.3333335,60 C 20.372129,260.06897 44.156731,256 63,256 h 111 35 c 5.78276,0 12.33244,0.84741 18,-0.33333 15.0401,-3.13336 27.46432,-14.87115 29,-30.66667 1.58612,-16.31419 0,-33.59769 0,-50 V 73 C 256,54.239685 260.73697,28.801102 248.66667,13 235.62787,-4.0689697 211.84329,0 193,0 H 82 47 C 41.217228,0 34.667561,-0.84741211 29,0.33332825 M 120,91 l -7,19 h 12 l -10,24 9,1 c -0.98794,2.68155 -2.31718,7.73317 -4.33334,9.83334 C 118.18945,146.3721 115.92654,146 114,146 c -4.35942,0 -13.16798,1.80539 -15.5,-3 -1.069664,-2.20416 0.465553,-4.98451 1.333336,-7 1.813624,-4.21228 4.222554,-8.51549 5.166664,-13 -2.17548,0 -4.92464,0.42967 -7,-0.33333 -7.778526,-2.85974 0.874031,-15.36435 2.66666,-19.66667 1.25875,-3.020981 2.75652,-9.584732 5.5,-11.5 C 110.01874,88.810822 115.88325,90.674988 120,91 m -79,63 c 2.750713,0 6.837379,0.81721 8.5,-2 1.769028,-2.99753 0.5,-9.58963 0.5,-13 V 106 C 50,102.90659 48.438198,93.464493 51.166668,91.5 53.41069,89.884308 62.832935,90.226166 63.833332,93 65.47065,97.539825 64,105.16241 64,110 v 32 c 0,5.48389 0.949112,11.8645 -1.333332,17 -2.177158,4.89861 -12.303417,9.27243 -17.333336,5.5 C 43.120155,162.84012 41.545292,156.59013 41,154 M 193,91 v 5 c 3.72887,0 8.4108,-0.763367 12,0.333328 11.97635,3.659424 11,15.422502 11,25.666672 1.99706,0 4.04419,-0.15562 6,0.33333 11.49335,2.87334 10,14.36401 10,23.66667 0,4.95615 0.93086,10.82184 -2.33333,15 -3.59567,4.60246 -9.48195,4 -14.66667,4 -1.6116,0 -4.26318,0.51051 -5.66667,-0.5 -2.62326,-1.88875 -3.78159,-7.50485 -4.33333,-10.5 3.28711,0 9.2179,1.12517 11.83333,-1.33334 C 219.9164,149.76859 218.65411,138.43454 215,136.5 c -1.93661,-1.02527 -4.88672,-0.5 -7,-0.5 h -15 v 29 h -14 v -29 h -14 v -14 h 14 v -12 h -9 V 96 h 9 v -5 h 14 m -32,5 v 14 h -8 v 42 h 13 v 13 H 120 L 125.33334,152.5 138,152 v -42 h -8 V 96 h 31 m 57,14 c 0,-2.84204 -0.51608,-6.25871 0.33333,-9 3.34434,-10.793121 19.61577,-2.093994 11.5,6.83333 -0.92279,1.01507 -2.54419,1.51106 -3.83333,1.83334 C 223.43948,110.30679 220.61993,110 218,110 M 41,110 36.833332,147 30,159 24,143 27,110 h 14 m 46,0 3,33 -6,15 h -2 c -5.366936,-8.49765 -6.053299,-17.26251 -7,-27 -0.672195,-6.91406 -2,-14.04004 -2,-21 h 14 m 106,0 v 12 h 9 v -12 h -9 m -75,42 -5,13 H 91 L 96.333336,151.5 104,151.66666 Z"
|
||||
id="path1"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
55
src/components/layout/FooterBar.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<footer class="footer sm:footer-horizontal bg-base-300 items-center p-4">
|
||||
<aside class="grid-flow-col items-center">
|
||||
<img src="@/assets/logo.svg" alt="hucky" class="h-18" />
|
||||
<p>Copyright © {{ new Date().getFullYear() }} - All right reserved</p>
|
||||
</aside>
|
||||
<nav class="grid-flow-col gap-4 md:place-self-center md:justify-self-end">
|
||||
<a>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="fill-current"
|
||||
>
|
||||
<path
|
||||
d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="fill-current"
|
||||
>
|
||||
<path
|
||||
d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="fill-current"
|
||||
>
|
||||
<path
|
||||
d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</nav>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
118
src/components/layout/FooterBarV2.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<script setup lang="ts">
|
||||
import BiliBiliIcon from "../icon/BiliBiliIcon.vue";
|
||||
import DouYinIcon from "../icon/DouYinIcon.vue";
|
||||
import LogoIcon from "../icon/LogoIcon.vue";
|
||||
import RedBookIcon from "../icon/RedBookIcon.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer
|
||||
class="w-full pt-16 pb-10 px-6 md:px-12 lg:px-20 text-sm font-[LXGW] lg:h-102 md:h-122 h-154"
|
||||
>
|
||||
<!-- Background overlay -->
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-t from-(--color-primary)/50 via-(--color-primary)/20 to-(--color-base-200) pointer-events-none"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="relative max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10 md:gap-6"
|
||||
>
|
||||
<!-- Left column - Logo + slogan -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="text-4xl font-bold tracking-tight">
|
||||
<LogoIcon class="h-16" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-lg font-medium tracking-wide">Hucky</div>
|
||||
<div class="text-xs text-gray-500">专注于开发体验提升</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-lg">重塑下一代前端脚手架</div>
|
||||
</div>
|
||||
|
||||
<!-- Middle scattered contact blocks -->
|
||||
<div class="space-y-8 md:space-y-10 lg:col-start-2">
|
||||
<!-- Email & resume -->
|
||||
<div>
|
||||
<div class="mb-1 text-gray-500">简历投递:</div>
|
||||
<a href="mailto:w56038009@outlook.com" class="font-bold underline">
|
||||
w56038009@outlook.com
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- WeChat / phone -->
|
||||
<div>
|
||||
<div class="mb-1 text-gray-500">业务联系:(微信)</div>
|
||||
<div class="font-bold">gushen622140</div>
|
||||
<!-- QR Code area -->
|
||||
<div class="mt-3">
|
||||
<div
|
||||
class="inline-block bg-white/10 backdrop-blur-sm rounded-lg border border-white/10"
|
||||
>
|
||||
<img src="/contact/wechat.png" alt="wechat" class="h-22" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Phone & address (right side feel) -->
|
||||
<div class="lg:hidden">
|
||||
<div class="font-bold">15381666371</div>
|
||||
<div class="text-gray-500 mt-1 text-xs leading-relaxed">
|
||||
浙江省金华市婺城区迎宾大道 667 号浙江师范大学<br />
|
||||
667, Zhejiang Normal University<br />
|
||||
Jinhua, Zhejiang, China
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right column - Phone + Address (desktop layout) -->
|
||||
<div class="hidden lg:block space-y-2">
|
||||
<div>
|
||||
<div class="font-bold text-lg">15381666371</div>
|
||||
</div>
|
||||
<div class="text-gray-500 leading-relaxed text-xs">
|
||||
浙江省金华市婺城区迎宾大道 667 号浙江师范大学<br />
|
||||
667, Zhejiang Normal University<br />
|
||||
Jinhua, Zhejiang, China
|
||||
</div>
|
||||
<div class="flex mt-4 gap-2">
|
||||
<a
|
||||
title="小红书"
|
||||
href="https://www.xiaohongshu.com/user/profile/61cf083b000000001000c7b2"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
<RedBookIcon class="h-6 w-6" />
|
||||
</a>
|
||||
<a
|
||||
title="B站"
|
||||
href="https://space.bilibili.com/34875940"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
<BiliBiliIcon class="h-6 w-6" />
|
||||
</a>
|
||||
<a
|
||||
title="抖音"
|
||||
href="https://www.douyin.com/user/MS4wLjABAAAAf6Q5VoHL_UfFQE8fmH3mgYCgL0tGxlpoSMLM9ZcKbpY"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
<DouYinIcon class="h-6 w-6" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom legal line -->
|
||||
<div class="mt-8 text-xs text-gray-500 text-left">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
Copyright © 2026 sunway.icu | 备案号:浙ICP备2023010223号 |
|
||||
增值电信业务经营许可证:浙B2-20110004
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
100
src/components/menu/NavBar.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
import ChangeLanguageDropdownButton from "@/components/button/ChangeLanguageDropdownButton.vue";
|
||||
import ChangeThemeDropdownButton from "@/components/button/ChangeThemeDropdownButton.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { throttle } from "radash";
|
||||
import { AnimatePresence, motion } from "motion-v";
|
||||
import { navigateTo } from "@/utils/navigator";
|
||||
import UserAuthNavButton from "../button/UserAuthNavButton.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const showNavBar = ref(true);
|
||||
const curScrollY = ref(0);
|
||||
|
||||
const handleThrottleScroll = throttle(100, () => {
|
||||
const scrollY = window.scrollY;
|
||||
if (scrollY > curScrollY.value) {
|
||||
showNavBar.value = false;
|
||||
} else {
|
||||
showNavBar.value = true;
|
||||
}
|
||||
curScrollY.value = scrollY;
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", handleThrottleScroll);
|
||||
|
||||
const navPageByResetScroll = (path: string) => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
navigateTo(path);
|
||||
};
|
||||
|
||||
const menuItems = ref([
|
||||
{
|
||||
path: "/",
|
||||
label: "nav.home",
|
||||
},
|
||||
{
|
||||
path: "/about",
|
||||
label: "nav.about",
|
||||
},
|
||||
{
|
||||
path: "/demo",
|
||||
label: "nav.demo",
|
||||
},
|
||||
{
|
||||
path: "/join",
|
||||
label: "nav.join",
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
v-show="showNavBar"
|
||||
class="navbar glass shadow-sm h-16"
|
||||
:exit="{
|
||||
y: -100,
|
||||
transition: {
|
||||
duration: 0.3,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
||||
<span class="icon-[material-symbols--menu-rounded] text-2xl" />
|
||||
</div>
|
||||
<ul
|
||||
tabindex="-1"
|
||||
class="menu dropdown-content bg-base-100 rounded-box z-1 mt-5 w-48 p-2 shadow"
|
||||
>
|
||||
<li v-for="item in menuItems" :key="item.path">
|
||||
<a @click="navPageByResetScroll(item.path)">{{
|
||||
t(item.label)
|
||||
}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="btn btn-ghost text-xl">Hucky</a>
|
||||
<ul class="menu menu-horizontal pl-6 hidden lg:flex gap-3">
|
||||
<li v-for="item in menuItems" :key="item.path">
|
||||
<a @click="navPageByResetScroll(item.path)">{{
|
||||
t(item.label)
|
||||
}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="navbar-end gap-2">
|
||||
<ChangeThemeDropdownButton />
|
||||
<ChangeLanguageDropdownButton />
|
||||
<UserAuthNavButton />
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
27
src/components/special/AnimText.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { motion, useAnimate } from "motion-v";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { languageChangeBus } from "@/hooks/bus/languageChangeBus";
|
||||
import type { Key, ResourceKeys } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
text: Key | ResourceKeys;
|
||||
}>();
|
||||
|
||||
const [scope, animate] = useAnimate();
|
||||
|
||||
languageChangeBus.on(async () => {
|
||||
await animate(scope.current, { opacity: 0, y: "-20px" });
|
||||
await animate(scope.current, { opacity: 1, y: "0" });
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<motion.span ref="scope">
|
||||
{{ t(props.text) }}
|
||||
</motion.span>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
130
src/components/three/LogoModel.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import * as THREE from "three";
|
||||
import { gsap } from "gsap";
|
||||
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
||||
import { M } from "motion-v";
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||
|
||||
let scene: THREE.Scene;
|
||||
let camera: THREE.PerspectiveCamera;
|
||||
let renderer: THREE.WebGLRenderer;
|
||||
let cube: THREE.Mesh;
|
||||
|
||||
// 动画循环的 raf id,用于销毁时清理
|
||||
let animationId: number;
|
||||
|
||||
function init() {
|
||||
if (!canvasRef.value) return; // 防御性编程
|
||||
|
||||
scene = new THREE.Scene();
|
||||
|
||||
camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000,
|
||||
);
|
||||
camera.position.z = 5;
|
||||
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
canvas: canvasRef.value,
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
});
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
// 透明
|
||||
renderer.setClearColor(0x000000, 0);
|
||||
|
||||
// ------------------- 几何体 -------------------
|
||||
const loader = new GLTFLoader();
|
||||
loader.load(
|
||||
"/model/christmas_ball.glb",
|
||||
(gltf) => {
|
||||
cube = gltf.scene;
|
||||
|
||||
// 可选:调整模型大小、位置
|
||||
cube.scale.set(0.01, 0.01, 0.01);
|
||||
cube.position.set(4, 0, 0);
|
||||
|
||||
scene.add(cube);
|
||||
|
||||
console.log("模型加载成功!", cube);
|
||||
},
|
||||
(xhr) => {
|
||||
console.log((xhr.loaded / xhr.total) * 100 + "% 已加载");
|
||||
},
|
||||
(error) => {
|
||||
console.error("模型加载失败:", error);
|
||||
},
|
||||
);
|
||||
|
||||
// 加一点环境光 + 点光(让边缘更亮)
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 1.2));
|
||||
const dirLight = new THREE.DirectionalLight(0xffffff, 2);
|
||||
dirLight.position.set(5, 10, 7.5);
|
||||
scene.add(dirLight);
|
||||
|
||||
animate();
|
||||
}
|
||||
|
||||
function animate() {
|
||||
animationId = requestAnimationFrame(animate);
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
|
||||
// 监听滚动事件
|
||||
window.addEventListener("scroll", () => {
|
||||
// 计算滚动进度
|
||||
const scrollY = window.scrollY;
|
||||
const maxScroll =
|
||||
document.documentElement.scrollHeight - window.innerHeight;
|
||||
const progress = scrollY / maxScroll;
|
||||
|
||||
cube.position.set(4 + progress, 0, 0);
|
||||
cube.rotation.y = -progress * Math.PI * 1;
|
||||
cube.scale.set(
|
||||
0.01 + progress * 0.001,
|
||||
0.01 + progress * 0.001,
|
||||
0.01 + progress * 0.001,
|
||||
);
|
||||
|
||||
// 接近页的脚时候淡出
|
||||
// if (progress > 0.8) {
|
||||
// gsap.to(cube.material, {
|
||||
// opacity: 0,
|
||||
// duration: 0.5,
|
||||
// });
|
||||
// }
|
||||
// if (progress < 0.8) {
|
||||
// gsap.to(cube.material, {
|
||||
// opacity: 1,
|
||||
// duration: 0.5,
|
||||
// });
|
||||
// }
|
||||
});
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener("resize", () => {
|
||||
if (!camera || !renderer) return;
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
cancelAnimationFrame(animationId);
|
||||
window.removeEventListener("resize", onResize); // 如果加了
|
||||
renderer?.dispose(); // 释放资源(推荐)
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<canvas ref="canvasRef" class="pointer-events-none" />
|
||||
</template>
|
||||
120
src/components/three/ModelViewer.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div ref="container" class="three-container" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import * as THREE from "three";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
||||
|
||||
const container = ref(null);
|
||||
let renderer = null;
|
||||
let scene = null;
|
||||
let camera = null;
|
||||
let controls = null;
|
||||
let animationFrameId = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (!container.value) return;
|
||||
|
||||
// ================== 初始化 ==================
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x0a0a1f);
|
||||
|
||||
camera = new THREE.PerspectiveCamera(
|
||||
60,
|
||||
container.value.clientWidth / container.value.clientHeight,
|
||||
0.1,
|
||||
1000,
|
||||
);
|
||||
camera.position.set(0, 1.5, 5);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(container.value.clientWidth, container.value.clientHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
container.value.appendChild(renderer.domElement);
|
||||
|
||||
// 控制器
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
controls.dampingFactor = 0.05;
|
||||
controls.enablePan = true;
|
||||
|
||||
// 灯光(很重要,否则模型可能很暗)
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 1.5));
|
||||
const dirLight = new THREE.DirectionalLight(0xffffff, 3);
|
||||
dirLight.position.set(5, 10, 7.5);
|
||||
scene.add(dirLight);
|
||||
|
||||
// ================== 加载模型 ==================
|
||||
const loader = new GLTFLoader();
|
||||
const modelUrl = "/model/christmas_ball.glb"; // ← 修改成你自己的模型路径
|
||||
|
||||
loader.load(
|
||||
modelUrl,
|
||||
(gltf) => {
|
||||
const model = gltf.scene;
|
||||
|
||||
// 可选调整
|
||||
model.scale.set(0.01, 0.01, 0.01); // 放大倍数,根据模型大小调整
|
||||
model.position.y = -0.5; // 稍微下移居中
|
||||
// model.rotation.y = Math.PI / 4 // 初始旋转(可选)
|
||||
|
||||
scene.add(model);
|
||||
console.log("模型加载成功", model);
|
||||
},
|
||||
(xhr) => {
|
||||
console.log(`加载进度: ${((xhr.loaded / xhr.total) * 100).toFixed(1)}%`);
|
||||
},
|
||||
(error) => {
|
||||
console.error("模型加载失败:", error);
|
||||
},
|
||||
);
|
||||
|
||||
// ================== 动画循环 ==================
|
||||
function animate() {
|
||||
animationFrameId = requestAnimationFrame(animate);
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (animationFrameId) cancelAnimationFrame(animationFrameId);
|
||||
if (controls) controls.dispose();
|
||||
if (renderer) {
|
||||
renderer.dispose();
|
||||
renderer.forceContextLoss();
|
||||
}
|
||||
scene = null;
|
||||
camera = null;
|
||||
renderer = null;
|
||||
controls = null;
|
||||
});
|
||||
|
||||
// 窗口 resize 处理
|
||||
const onResize = () => {
|
||||
if (!container.value || !camera || !renderer) return;
|
||||
|
||||
const width = container.value.clientWidth;
|
||||
const height = container.value.clientHeight;
|
||||
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(width, height);
|
||||
};
|
||||
|
||||
window.addEventListener("resize", onResize);
|
||||
onUnmounted(() => window.removeEventListener("resize", onResize));
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.three-container {
|
||||
width: 100%;
|
||||
height: 100vh; /* 或你想要的高度,例如 600px */
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
}
|
||||
</style>
|
||||
29
src/components/ui/button/Button.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import type { PrimitiveProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import type { ButtonVariants } from "."
|
||||
import { Primitive } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "."
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
variant?: ButtonVariants["variant"]
|
||||
size?: ButtonVariants["size"]
|
||||
class?: HTMLAttributes["class"]
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "button",
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
data-slot="button"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn(buttonVariants({ variant, size }), props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
38
src/components/ui/button/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { VariantProps } from "class-variance-authority"
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
export { default as Button } from "./Button.vue"
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
"default": "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
"sm": "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
"lg": "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
"icon": "size-9",
|
||||
"icon-sm": "size-8",
|
||||
"icon-lg": "size-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
)
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>
|
||||
225
src/components/ui/calendar/Calendar.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarRootEmits, CalendarRootProps, DateValue } from "reka-ui";
|
||||
import type { HTMLAttributes, Ref } from "vue";
|
||||
import type { LayoutTypes } from ".";
|
||||
import { getLocalTimeZone, today } from "@internationalized/date";
|
||||
import { createReusableTemplate, reactiveOmit, useVModel } from "@vueuse/core";
|
||||
import { CalendarRoot, useDateFormatter, useForwardPropsEmits } from "reka-ui";
|
||||
import { createYear, createYearRange, toDate } from "reka-ui/date";
|
||||
import { computed, toRaw } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
} from "@/components/ui/native-select";
|
||||
import {
|
||||
CalendarCell,
|
||||
CalendarCellTrigger,
|
||||
CalendarGrid,
|
||||
CalendarGridBody,
|
||||
CalendarGridHead,
|
||||
CalendarGridRow,
|
||||
CalendarHeadCell,
|
||||
CalendarHeader,
|
||||
CalendarHeading,
|
||||
CalendarNextButton,
|
||||
CalendarPrevButton,
|
||||
} from ".";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
CalendarRootProps & {
|
||||
class?: HTMLAttributes["class"];
|
||||
layout?: LayoutTypes;
|
||||
yearRange?: DateValue[];
|
||||
}
|
||||
>(),
|
||||
{
|
||||
modelValue: undefined,
|
||||
layout: undefined,
|
||||
},
|
||||
);
|
||||
const emits = defineEmits<CalendarRootEmits>();
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "layout", "placeholder");
|
||||
|
||||
const placeholder = useVModel(props, "placeholder", emits, {
|
||||
passive: true,
|
||||
defaultValue: props.defaultPlaceholder ?? today(getLocalTimeZone()),
|
||||
}) as Ref<DateValue>;
|
||||
|
||||
const formatter = useDateFormatter(props.locale ?? "en");
|
||||
|
||||
const yearRange = computed(() => {
|
||||
return (
|
||||
props.yearRange ??
|
||||
createYearRange({
|
||||
start:
|
||||
props?.minValue ??
|
||||
(
|
||||
toRaw(props.placeholder) ??
|
||||
props.defaultPlaceholder ??
|
||||
today(getLocalTimeZone())
|
||||
).cycle("year", -100),
|
||||
|
||||
end:
|
||||
props?.maxValue ??
|
||||
(
|
||||
toRaw(props.placeholder) ??
|
||||
props.defaultPlaceholder ??
|
||||
today(getLocalTimeZone())
|
||||
).cycle("year", 10),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const [DefineMonthTemplate, ReuseMonthTemplate] = createReusableTemplate<{
|
||||
date: DateValue;
|
||||
}>();
|
||||
const [DefineYearTemplate, ReuseYearTemplate] = createReusableTemplate<{
|
||||
date: DateValue;
|
||||
}>();
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DefineMonthTemplate v-slot="{ date }">
|
||||
<div class="**:data-[slot=native-select-icon]:right-1">
|
||||
<div class="relative">
|
||||
<div
|
||||
class="absolute inset-0 flex h-full items-center text-sm pl-2 pointer-events-none"
|
||||
>
|
||||
{{ formatter.custom(toDate(date), { month: "short" }) }}
|
||||
</div>
|
||||
<NativeSelect
|
||||
class="text-xs h-8 pr-6 pl-2 text-transparent relative"
|
||||
@change="
|
||||
(e: Event) => {
|
||||
placeholder = placeholder.set({
|
||||
month: Number((e?.target as any)?.value),
|
||||
});
|
||||
}
|
||||
"
|
||||
>
|
||||
<NativeSelectOption
|
||||
v-for="month in createYear({ dateObj: date })"
|
||||
:key="month.toString()"
|
||||
:value="month.month"
|
||||
:selected="date.month === month.month"
|
||||
>
|
||||
{{ formatter.custom(toDate(month), { month: "short" }) }}
|
||||
</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
</div>
|
||||
</DefineMonthTemplate>
|
||||
|
||||
<DefineYearTemplate v-slot="{ date }">
|
||||
<div class="**:data-[slot=native-select-icon]:right-1">
|
||||
<div class="relative">
|
||||
<div
|
||||
class="absolute inset-0 flex h-full items-center text-sm pl-2 pointer-events-none"
|
||||
>
|
||||
{{ formatter.custom(toDate(date), { year: "numeric" }) }}
|
||||
</div>
|
||||
<NativeSelect
|
||||
class="text-xs h-8 pr-6 pl-2 text-transparent relative"
|
||||
@change="
|
||||
(e: Event) => {
|
||||
placeholder = placeholder.set({
|
||||
year: Number((e?.target as any)?.value),
|
||||
});
|
||||
}
|
||||
"
|
||||
>
|
||||
<NativeSelectOption
|
||||
v-for="year in yearRange"
|
||||
:key="year.toString()"
|
||||
:value="year.year"
|
||||
:selected="date.year === year.year"
|
||||
>
|
||||
{{ formatter.custom(toDate(year), { year: "numeric" }) }}
|
||||
</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
</div>
|
||||
</DefineYearTemplate>
|
||||
|
||||
<CalendarRoot
|
||||
v-slot="{ grid, weekDays, date }"
|
||||
v-bind="forwarded"
|
||||
v-model:placeholder="placeholder"
|
||||
data-slot="calendar"
|
||||
:class="cn('p-3', props.class)"
|
||||
>
|
||||
<CalendarHeader class="pt-0">
|
||||
<nav
|
||||
class="flex items-center gap-1 absolute top-0 inset-x-0 justify-between"
|
||||
>
|
||||
<CalendarPrevButton>
|
||||
<slot name="calendar-prev-icon" />
|
||||
</CalendarPrevButton>
|
||||
<CalendarNextButton>
|
||||
<slot name="calendar-next-icon" />
|
||||
</CalendarNextButton>
|
||||
</nav>
|
||||
|
||||
<slot
|
||||
name="calendar-heading"
|
||||
:date="date"
|
||||
:month="ReuseMonthTemplate"
|
||||
:year="ReuseYearTemplate"
|
||||
>
|
||||
<template v-if="layout === 'month-and-year'">
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
<ReuseMonthTemplate :date="date" />
|
||||
<ReuseYearTemplate :date="date" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="layout === 'month-only'">
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
<ReuseMonthTemplate :date="date" />
|
||||
{{ formatter.custom(toDate(date), { year: "numeric" }) }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="layout === 'year-only'">
|
||||
<div class="flex items-center justify-center gap-1">
|
||||
{{ formatter.custom(toDate(date), { month: "short" }) }}
|
||||
<ReuseYearTemplate :date="date" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<CalendarHeading />
|
||||
</template>
|
||||
</slot>
|
||||
</CalendarHeader>
|
||||
|
||||
<div class="flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0">
|
||||
<CalendarGrid v-for="month in grid" :key="month.value.toString()">
|
||||
<CalendarGridHead>
|
||||
<CalendarGridRow>
|
||||
<CalendarHeadCell v-for="day in weekDays" :key="day">
|
||||
{{ day }}
|
||||
</CalendarHeadCell>
|
||||
</CalendarGridRow>
|
||||
</CalendarGridHead>
|
||||
<CalendarGridBody>
|
||||
<CalendarGridRow
|
||||
v-for="(weekDates, index) in month.rows"
|
||||
:key="`weekDate-${index}`"
|
||||
class="mt-2 w-full"
|
||||
>
|
||||
<CalendarCell
|
||||
v-for="weekDate in weekDates"
|
||||
:key="weekDate.toString()"
|
||||
:date="weekDate"
|
||||
>
|
||||
<CalendarCellTrigger :day="weekDate" :month="month.value" />
|
||||
</CalendarCell>
|
||||
</CalendarGridRow>
|
||||
</CalendarGridBody>
|
||||
</CalendarGrid>
|
||||
</div>
|
||||
</CalendarRoot>
|
||||
</template>
|
||||
30
src/components/ui/calendar/CalendarCell.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarCellProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { CalendarCell, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<
|
||||
CalendarCellProps & { class?: HTMLAttributes["class"] }
|
||||
>();
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarCell
|
||||
data-slot="calendar-cell"
|
||||
:class="
|
||||
cn(
|
||||
'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md [&:has([data-selected])]:bg-accent',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarCell>
|
||||
</template>
|
||||
44
src/components/ui/calendar/CalendarCellTrigger.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarCellTriggerProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { CalendarCellTrigger, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<CalendarCellTriggerProps & { class?: HTMLAttributes["class"] }>(),
|
||||
{
|
||||
as: "button",
|
||||
},
|
||||
);
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarCellTrigger
|
||||
data-slot="calendar-cell-trigger"
|
||||
:class="
|
||||
cn(
|
||||
buttonVariants({ variant: 'ghost' }),
|
||||
'size-8 p-0 font-normal aria-selected:opacity-100 cursor-default',
|
||||
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-content',
|
||||
// Selected
|
||||
'data-[selected]:bg-primary data-[selected]:text-primary-content data-[selected]:opacity-100 data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-content data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-content',
|
||||
// Disabled
|
||||
'data-[disabled]:text-muted-content data-[disabled]:opacity-50',
|
||||
// Unavailable
|
||||
'data-[unavailable]:text-destructive-content data-[unavailable]:line-through',
|
||||
// Outside months
|
||||
'data-[outside-view]:text-muted-content',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarCellTrigger>
|
||||
</template>
|
||||
23
src/components/ui/calendar/CalendarGrid.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarGridProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { CalendarGrid, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<CalendarGridProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGrid
|
||||
data-slot="calendar-grid"
|
||||
:class="cn('w-full border-collapse space-x-1', props.class)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarGrid>
|
||||
</template>
|
||||
15
src/components/ui/calendar/CalendarGridBody.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarGridBodyProps } from "reka-ui"
|
||||
import { CalendarGridBody } from "reka-ui"
|
||||
|
||||
const props = defineProps<CalendarGridBodyProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGridBody
|
||||
data-slot="calendar-grid-body"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</CalendarGridBody>
|
||||
</template>
|
||||
16
src/components/ui/calendar/CalendarGridHead.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarGridHeadProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { CalendarGridHead } from "reka-ui"
|
||||
|
||||
const props = defineProps<CalendarGridHeadProps & { class?: HTMLAttributes["class"] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGridHead
|
||||
data-slot="calendar-grid-head"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</CalendarGridHead>
|
||||
</template>
|
||||
22
src/components/ui/calendar/CalendarGridRow.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarGridRowProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { CalendarGridRow, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<CalendarGridRowProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarGridRow
|
||||
data-slot="calendar-grid-row"
|
||||
:class="cn('flex', props.class)" v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarGridRow>
|
||||
</template>
|
||||
23
src/components/ui/calendar/CalendarHeadCell.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarHeadCellProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { CalendarHeadCell, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<CalendarHeadCellProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarHeadCell
|
||||
data-slot="calendar-head-cell"
|
||||
:class="cn('text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem]', props.class)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarHeadCell>
|
||||
</template>
|
||||
23
src/components/ui/calendar/CalendarHeader.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarHeaderProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { CalendarHeader, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<CalendarHeaderProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarHeader
|
||||
data-slot="calendar-header"
|
||||
:class="cn('flex justify-center pt-1 relative items-center w-full px-8', props.class)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</CalendarHeader>
|
||||
</template>
|
||||
30
src/components/ui/calendar/CalendarHeading.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarHeadingProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { CalendarHeading, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<CalendarHeadingProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
defineSlots<{
|
||||
default: (props: { headingValue: string }) => any
|
||||
}>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarHeading
|
||||
v-slot="{ headingValue }"
|
||||
data-slot="calendar-heading"
|
||||
:class="cn('text-sm font-medium', props.class)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot :heading-value>
|
||||
{{ headingValue }}
|
||||
</slot>
|
||||
</CalendarHeading>
|
||||
</template>
|
||||
31
src/components/ui/calendar/CalendarNextButton.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarNextProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { ChevronRight } from "lucide-vue-next"
|
||||
import { CalendarNext, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
|
||||
const props = defineProps<CalendarNextProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarNext
|
||||
data-slot="calendar-next-button"
|
||||
:class="cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
props.class,
|
||||
)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot>
|
||||
<ChevronRight class="size-4" />
|
||||
</slot>
|
||||
</CalendarNext>
|
||||
</template>
|
||||
31
src/components/ui/calendar/CalendarPrevButton.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CalendarPrevProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import { ChevronLeft } from "lucide-vue-next"
|
||||
import { CalendarPrev, useForwardProps } from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
|
||||
const props = defineProps<CalendarPrevProps & { class?: HTMLAttributes["class"] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CalendarPrev
|
||||
data-slot="calendar-prev-button"
|
||||
:class="cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
props.class,
|
||||
)"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot>
|
||||
<ChevronLeft class="size-4" />
|
||||
</slot>
|
||||
</CalendarPrev>
|
||||
</template>
|
||||
14
src/components/ui/calendar/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export { default as Calendar } from "./Calendar.vue"
|
||||
export { default as CalendarCell } from "./CalendarCell.vue"
|
||||
export { default as CalendarCellTrigger } from "./CalendarCellTrigger.vue"
|
||||
export { default as CalendarGrid } from "./CalendarGrid.vue"
|
||||
export { default as CalendarGridBody } from "./CalendarGridBody.vue"
|
||||
export { default as CalendarGridHead } from "./CalendarGridHead.vue"
|
||||
export { default as CalendarGridRow } from "./CalendarGridRow.vue"
|
||||
export { default as CalendarHeadCell } from "./CalendarHeadCell.vue"
|
||||
export { default as CalendarHeader } from "./CalendarHeader.vue"
|
||||
export { default as CalendarHeading } from "./CalendarHeading.vue"
|
||||
export { default as CalendarNextButton } from "./CalendarNextButton.vue"
|
||||
export { default as CalendarPrevButton } from "./CalendarPrevButton.vue"
|
||||
|
||||
export type LayoutTypes = "month-and-year" | "month-only" | "year-only" | undefined
|
||||
55
src/components/ui/native-select/NativeSelect.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import type { AcceptableValue } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { reactiveOmit, useVModel } from "@vueuse/core";
|
||||
import { ChevronDownIcon } from "lucide-vue-next";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: AcceptableValue | AcceptableValue[];
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:modelValue": AcceptableValue;
|
||||
}>();
|
||||
|
||||
const modelValue = useVModel(props, "modelValue", emit, {
|
||||
passive: true,
|
||||
defaultValue: "",
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="group/native-select relative w-fit has-[select:disabled]:opacity-50"
|
||||
data-slot="native-select-wrapper"
|
||||
>
|
||||
<select
|
||||
v-bind="{ ...$attrs, ...delegatedProps }"
|
||||
v-model="modelValue"
|
||||
data-slot="native-select"
|
||||
:class="
|
||||
cn(
|
||||
'border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</select>
|
||||
<ChevronDownIcon
|
||||
class="text-muted-foreground pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 opacity-50 select-none"
|
||||
aria-hidden="true"
|
||||
data-slot="native-select-icon"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
15
src/components/ui/native-select/NativeSelectOptGroup.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- @fallthroughAttributes true -->
|
||||
<!-- @strictTemplates true -->
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<optgroup data-slot="native-select-optgroup" :class="cn('bg-popover text-popover-foreground', props.class)">
|
||||
<slot />
|
||||
</optgroup>
|
||||
</template>
|
||||
18
src/components/ui/native-select/NativeSelectOption.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<!-- @fallthroughAttributes true -->
|
||||
<!-- @strictTemplates true -->
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{ class?: HTMLAttributes["class"] }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<option
|
||||
data-slot="native-select-option"
|
||||
:class="cn('bg-popover text-popover-foreground text-black', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</option>
|
||||
</template>
|
||||
3
src/components/ui/native-select/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as NativeSelect } from "./NativeSelect.vue"
|
||||
export { default as NativeSelectOptGroup } from "./NativeSelectOptGroup.vue"
|
||||
export { default as NativeSelectOption } from "./NativeSelectOption.vue"
|
||||
19
src/components/ui/popover/Popover.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopoverRootEmits, PopoverRootProps } from "reka-ui"
|
||||
import { PopoverRoot, useForwardPropsEmits } from "reka-ui"
|
||||
|
||||
const props = defineProps<PopoverRootProps>()
|
||||
const emits = defineEmits<PopoverRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverRoot
|
||||
v-slot="slotProps"
|
||||
data-slot="popover"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot v-bind="slotProps" />
|
||||
</PopoverRoot>
|
||||
</template>
|
||||
15
src/components/ui/popover/PopoverAnchor.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopoverAnchorProps } from "reka-ui"
|
||||
import { PopoverAnchor } from "reka-ui"
|
||||
|
||||
const props = defineProps<PopoverAnchorProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverAnchor
|
||||
data-slot="popover-anchor"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</PopoverAnchor>
|
||||
</template>
|
||||
45
src/components/ui/popover/PopoverContent.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopoverContentEmits, PopoverContentProps } from "reka-ui"
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { reactiveOmit } from "@vueuse/core"
|
||||
import {
|
||||
PopoverContent,
|
||||
PopoverPortal,
|
||||
useForwardPropsEmits,
|
||||
} from "reka-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<PopoverContentProps & { class?: HTMLAttributes["class"] }>(),
|
||||
{
|
||||
align: "center",
|
||||
sideOffset: 4,
|
||||
},
|
||||
)
|
||||
const emits = defineEmits<PopoverContentEmits>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class")
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverPortal>
|
||||
<PopoverContent
|
||||
data-slot="popover-content"
|
||||
v-bind="{ ...$attrs, ...forwarded }"
|
||||
:class="
|
||||
cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md origin-(--reka-popover-content-transform-origin) outline-hidden',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</PopoverContent>
|
||||
</PopoverPortal>
|
||||
</template>
|
||||
15
src/components/ui/popover/PopoverTrigger.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopoverTriggerProps } from "reka-ui"
|
||||
import { PopoverTrigger } from "reka-ui"
|
||||
|
||||
const props = defineProps<PopoverTriggerProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverTrigger
|
||||
data-slot="popover-trigger"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</PopoverTrigger>
|
||||
</template>
|
||||
4
src/components/ui/popover/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as Popover } from "./Popover.vue"
|
||||
export { default as PopoverAnchor } from "./PopoverAnchor.vue"
|
||||
export { default as PopoverContent } from "./PopoverContent.vue"
|
||||
export { default as PopoverTrigger } from "./PopoverTrigger.vue"
|
||||
4
src/hooks/bus/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const keys = {
|
||||
user: "user",
|
||||
language: "language",
|
||||
};
|
||||
4
src/hooks/bus/languageChangeBus.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { useEventBus } from "@vueuse/core";
|
||||
import { keys } from "./index";
|
||||
|
||||
export const languageChangeBus = useEventBus<string>(keys.language);
|
||||
22
src/hooks/dialog/renderAuthDialog.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import AuthDialog from "@/components/dialog/AuthDialog.vue";
|
||||
import i18n from "@/i18n";
|
||||
|
||||
export const renderAuthDialog = (defaultAuthFormType: "login" | "register") => {
|
||||
const authDialogContainer = document.createElement("div");
|
||||
authDialogContainer.id = "auth-dialog-container";
|
||||
|
||||
document.body.appendChild(authDialogContainer);
|
||||
|
||||
createApp(AuthDialog, {
|
||||
defaultAuthFormType,
|
||||
})
|
||||
.use(i18n)
|
||||
.mount(authDialogContainer);
|
||||
};
|
||||
|
||||
export const closeAuthDialog = () => {
|
||||
const authDialogContainer = document.getElementById("auth-dialog-container");
|
||||
if (authDialogContainer) {
|
||||
authDialogContainer.remove();
|
||||
}
|
||||
};
|
||||
37
src/hooks/globalLanguageHook.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
useLanguageStore,
|
||||
GlobalLanguage,
|
||||
languageMap,
|
||||
} from "@/stores/LanguageStore";
|
||||
import { languageChangeBus } from "./bus/languageChangeBus";
|
||||
|
||||
const languageStore = useLanguageStore();
|
||||
|
||||
export const optionalLanguages = Object.entries(languageMap).map(
|
||||
([key, value]) => ({
|
||||
label: key as GlobalLanguage,
|
||||
value,
|
||||
}),
|
||||
);
|
||||
|
||||
// 一定不要通过改变 curGlobalLanguage 来改变语言,而要通过 changeGlobalLanguage 来改变语言
|
||||
const curGlobalLanguage = computed(() => languageStore.language);
|
||||
|
||||
function changeGlobalLanguage(
|
||||
language: GlobalLanguage,
|
||||
locale: WritableComputedRef<string>,
|
||||
) {
|
||||
// 延迟切换先通知其他组件播放动画
|
||||
languageChangeBus.emit("language changed");
|
||||
|
||||
setTimeout(() => {
|
||||
languageStore.setLanguage(language);
|
||||
locale.value = language;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
export const useGlobalLanguageHook = () => ({
|
||||
optionalLanguages,
|
||||
curGlobalLanguage,
|
||||
changeGlobalLanguage,
|
||||
});
|
||||
19
src/hooks/globalThemeHook.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// 存在一个重要问题: 不清楚 daisyui 是怎么替换主题的 目前没有做任何配置
|
||||
import { useThemeStore, GlobalTheme } from "@/stores/ThemeStore";
|
||||
|
||||
const themeStore = useThemeStore();
|
||||
|
||||
const optionalThemes = Object.values(GlobalTheme);
|
||||
|
||||
// 一定不要通过改变 curGlobalTheme 来改变主题,而要通过 changeGlobalTheme 来改变主题
|
||||
const curGlobalTheme = computed(() => themeStore.theme);
|
||||
|
||||
function changeGlobalTheme(theme: GlobalTheme) {
|
||||
themeStore.setTheme(theme);
|
||||
}
|
||||
|
||||
export const useGlobalThemeHook = () => ({
|
||||
optionalThemes,
|
||||
curGlobalTheme,
|
||||
changeGlobalTheme,
|
||||
});
|
||||
43
src/i18n/en_US.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { messagesInterface } from ".";
|
||||
|
||||
export const en_USMessages: messagesInterface = {
|
||||
nav: {
|
||||
home: "Home",
|
||||
about: "About",
|
||||
demo: "Demo",
|
||||
join: "Join",
|
||||
theme: "Theme",
|
||||
locale: "Locale",
|
||||
login: "Login",
|
||||
register: "Register",
|
||||
logout: "Logout",
|
||||
logout_more_title: "Logout Confirmation",
|
||||
logout_more: "Ready to change account?",
|
||||
login_more_title: "Join us Now!",
|
||||
login_more: "Start your new develop experience",
|
||||
},
|
||||
auth: {
|
||||
login: "Login",
|
||||
login_password: "Password Login",
|
||||
login_phone: "Phone Login",
|
||||
register: "Register",
|
||||
email: "Email",
|
||||
password: "Password",
|
||||
remember_me: "Remember Me",
|
||||
forget_password: "Forget Password",
|
||||
no_account: "Don't have an account?",
|
||||
agree_terms: "Agree to the Terms & Conditions",
|
||||
have_account: "Already have an account?",
|
||||
logout: "Logout",
|
||||
scan_qr: "Scan QR Code",
|
||||
scan_qr_more: "Please open the phone's WeChat APP to scan the QR code",
|
||||
not_open: "Not Open Yet",
|
||||
},
|
||||
home: {
|
||||
welcome: "Welcome to Hucky",
|
||||
intro_line1: "A Vue 3 + TypeScript + Vite template",
|
||||
intro_line2: "with Vue I18n, Vue Router, and Vuex",
|
||||
intro_line3: "and more...",
|
||||
read_doc: "Read Documentation",
|
||||
},
|
||||
};
|
||||
57
src/i18n/index.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { createI18n } from "vue-i18n";
|
||||
import { zh_CNMessages } from "./zh_CN";
|
||||
import { en_USMessages } from "./en_US";
|
||||
|
||||
export interface messagesInterface {
|
||||
nav: {
|
||||
home: string;
|
||||
about: string;
|
||||
demo: string;
|
||||
join: string;
|
||||
theme: string;
|
||||
locale: string;
|
||||
login: string;
|
||||
register: string;
|
||||
logout: string;
|
||||
logout_more_title: string;
|
||||
logout_more: string;
|
||||
login_more_title: string;
|
||||
login_more: string;
|
||||
};
|
||||
auth: {
|
||||
login: string;
|
||||
login_password: string;
|
||||
login_phone: string;
|
||||
register: string;
|
||||
email: string;
|
||||
password: string;
|
||||
remember_me: string;
|
||||
forget_password: string;
|
||||
no_account: string;
|
||||
agree_terms: string;
|
||||
have_account: string;
|
||||
logout: string;
|
||||
scan_qr: string;
|
||||
scan_qr_more: string;
|
||||
not_open: string;
|
||||
};
|
||||
home: {
|
||||
welcome: string;
|
||||
intro_line1: string;
|
||||
intro_line2: string;
|
||||
intro_line3: string;
|
||||
read_doc: string;
|
||||
};
|
||||
}
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: "ZH",
|
||||
fallbackLocale: "EN",
|
||||
messages: {
|
||||
ZH: zh_CNMessages,
|
||||
EN: en_USMessages,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
43
src/i18n/zh_CN.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { messagesInterface } from ".";
|
||||
|
||||
export const zh_CNMessages: messagesInterface = {
|
||||
nav: {
|
||||
home: "首页",
|
||||
about: "关于",
|
||||
demo: "演示",
|
||||
join: "加入",
|
||||
theme: "主题",
|
||||
locale: "语言",
|
||||
login: "登录",
|
||||
register: "注册",
|
||||
logout: "退出登录",
|
||||
logout_more_title: "退出登录",
|
||||
logout_more: "准备切换新的账号吗或者是退出以保证您的安全吗?",
|
||||
login_more_title: "加入我们",
|
||||
login_more: "开始您的新开发体验",
|
||||
},
|
||||
auth: {
|
||||
login: "登录",
|
||||
login_password: "密码登录",
|
||||
login_phone: "短信登录",
|
||||
register: "注册",
|
||||
email: "邮箱",
|
||||
password: "密码",
|
||||
remember_me: "记住我",
|
||||
forget_password: "忘记密码",
|
||||
no_account: "没有账号?",
|
||||
agree_terms: "我同意《用户协议》相关条款和条件",
|
||||
have_account: "已经有账号?",
|
||||
logout: "退出登录",
|
||||
scan_qr: "扫描二维码登录",
|
||||
scan_qr_more: "请打开手机微信 APP 扫码登录",
|
||||
not_open: "暂未开放",
|
||||
},
|
||||
home: {
|
||||
welcome: "欢迎来到 Hucky",
|
||||
intro_line1: "超现代化的 Vue3 Based 脚手架",
|
||||
intro_line2: "赋予您极致高效且规范的开发体验",
|
||||
intro_line3: "使用 bun docs 命令启动 Hucky 的文档服务器",
|
||||
read_doc: "阅读文档",
|
||||
},
|
||||
};
|
||||
7
src/lib/utils.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { ClassValue } from "clsx"
|
||||
import { clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
@@ -2,10 +2,12 @@ import { createApp } from "vue";
|
||||
import "./style.css";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import i18n from "./i18n";
|
||||
import { createPinia } from "pinia";
|
||||
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||
import "lenis/dist/lenis.css";
|
||||
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPluginPersistedstate);
|
||||
|
||||
createApp(App).use(router).use(pinia).mount("#app");
|
||||
createApp(App).use(router).use(pinia).use(i18n).mount("#app");
|
||||
|
||||
95
src/pages/about.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<script setup lang="ts">
|
||||
import NavBar from "@/components/menu/NavBar.vue";
|
||||
import { navigateTo } from "@/utils/navigator";
|
||||
import { motion } from "motion-v";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-base-200">
|
||||
<NavBar class="fixed top-0 left-0 z-10" />
|
||||
<!-- 同高度占位颜色叠加 -->
|
||||
<div class="h-16 bg-base-300" />
|
||||
<div class="p-4 gap-4 flex flex-col">
|
||||
<div class="mockup-browser border-base-300 border w-full">
|
||||
<div class="mockup-browser-toolbar">
|
||||
<motion.div
|
||||
class="input cursor-pointer"
|
||||
:while-hover="{ scale: 1.1 }"
|
||||
@click="navigateTo('https://daisyui.com')"
|
||||
>
|
||||
https://daisyui.com
|
||||
</motion.div>
|
||||
</div>
|
||||
<div class="grid place-content-center border-t border-base-300 h-16">
|
||||
由 DaisyUI 提供组件库支持
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mockup-browser border-base-300 border w-full">
|
||||
<div class="mockup-browser-toolbar">
|
||||
<motion.div
|
||||
class="input cursor-pointer"
|
||||
:while-hover="{ scale: 1.1 }"
|
||||
@click="navigateTo('https://motion.net.cn/')"
|
||||
>
|
||||
https://motion.net.cn/
|
||||
</motion.div>
|
||||
</div>
|
||||
<div class="grid place-content-center border-t border-base-300 h-16">
|
||||
由 Motion 提供动画库支持
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mockup-browser border-base-300 border w-full">
|
||||
<div class="mockup-browser-toolbar">
|
||||
<motion.div
|
||||
class="input cursor-pointer"
|
||||
:while-hover="{ scale: 1.1 }"
|
||||
@click="navigateTo('https://space.bilibili.com/34875940')"
|
||||
>
|
||||
https://space.bilibili.com/34875940
|
||||
</motion.div>
|
||||
</div>
|
||||
<div class="grid place-content-center border-t border-base-300 h-16">
|
||||
B站搜索谷神神神
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mockup-browser border-base-300 border w-full">
|
||||
<div class="mockup-browser-toolbar">
|
||||
<motion.div
|
||||
class="input cursor-pointer"
|
||||
:while-hover="{ scale: 1.1 }"
|
||||
@click="
|
||||
navigateTo(
|
||||
'https://www.douyin.com/user/MS4wLjABAAAAf6Q5VoHL_UfFQE8fmH3mgYCgL0tGxlpoSMLM9ZcKbpY',
|
||||
)
|
||||
"
|
||||
>
|
||||
https://www.douyin.com/search/谷神神神
|
||||
</motion.div>
|
||||
</div>
|
||||
<div class="grid place-content-center border-t border-base-300 h-16">
|
||||
抖音搜索谷神神神
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mockup-browser border-base-300 border w-full">
|
||||
<div class="mockup-browser-toolbar">
|
||||
<motion.div
|
||||
class="input cursor-pointer"
|
||||
:while-hover="{ scale: 1.1 }"
|
||||
@click="navigateTo('https://qm.qq.com/q/wt1ukamVJ6')"
|
||||
>
|
||||
https://qm.qq.com/q/wt1ukamVJ6
|
||||
</motion.div>
|
||||
</div>
|
||||
<div class="p-4 grid place-content-center border-t border-base-300">
|
||||
<img src="/qq-rc.jpg" alt="qq" class="h-96" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
22
src/pages/demo.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import NavBar from "@/components/menu/NavBar.vue";
|
||||
import FooterBarV2 from "@/components/layout/FooterBarV2.vue";
|
||||
import ModelViewer from "@/components/three/ModelViewer.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="lg:pb-102 md:pb-122 pb-154">
|
||||
<!-- 这里开 relative 形成 stack context -->
|
||||
<div class="bg-base-200 relative z-2">
|
||||
<div class="h-screen flex flex-col">
|
||||
<NavBar class="fixed top-0 left-0 z-10" />
|
||||
<!-- 同高度占位颜色叠加 -->
|
||||
<div class="h-16 bg-base-300" />
|
||||
<ModelViewer />
|
||||
</div>
|
||||
</div>
|
||||
<FooterBarV2 class="fixed bottom-0 left-0 z-1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -1,18 +1,85 @@
|
||||
<script lang="ts" setup>
|
||||
// Home page component
|
||||
import BasicIntroCard from "@/components/card/BasicIntroCard.vue";
|
||||
import DatePickerDisplayCard from "@/components/card/DatePickerDisplayCard.vue";
|
||||
import DevelopProgressCard from "@/components/card/DevelopProgressCard.vue";
|
||||
import FooterBarV2 from "@/components/layout/FooterBarV2.vue";
|
||||
import NavBar from "@/components/menu/NavBar.vue";
|
||||
import LogoModel from "@/components/three/LogoModel.vue";
|
||||
|
||||
const progress = ref([
|
||||
{
|
||||
name: "auto-router",
|
||||
completed: 80,
|
||||
pending: 20,
|
||||
title: "自动路由开发进度",
|
||||
intro: "自动将 pages 目录下的所有 vue 文件转换为路由",
|
||||
},
|
||||
{
|
||||
name: "component-list",
|
||||
completed: 95,
|
||||
pending: 5,
|
||||
title: "组件库开发进度",
|
||||
intro: "自动加载项目所需要的组件库",
|
||||
},
|
||||
{
|
||||
name: "international",
|
||||
completed: 95,
|
||||
pending: 5,
|
||||
title: "国际化开发进度",
|
||||
intro: "有序地组织项目翻译文件",
|
||||
},
|
||||
{
|
||||
name: "chart",
|
||||
completed: 95,
|
||||
pending: 5,
|
||||
title: "图表开发进度",
|
||||
intro: "使用精美的图表呈现内容",
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="hero min-h-screen bg-base-200">
|
||||
<div class="hero-content text-center">
|
||||
<div class="max-w-md">
|
||||
<h1 class="text-5xl font-bold">Hucky</h1>
|
||||
<p class="py-6">自动路由系统已加载</p>
|
||||
<div class="lg:pb-102 md:pb-122 pb-154">
|
||||
<!-- 主内容区域,在 footer 上方 -->
|
||||
<div class="bg-base-200 relative z-5">
|
||||
<!-- 顶部与首页内容 -->
|
||||
<div class="h-screen flex flex-col">
|
||||
<NavBar class="fixed top-0 left-0 z-10" />
|
||||
<!-- 同高度占位颜色叠加 -->
|
||||
<div class="h-16 bg-base-300" />
|
||||
<BasicIntroCard class="isolate z-8" />
|
||||
</div>
|
||||
|
||||
<!-- fixed three js 背景 -->
|
||||
<LogoModel class="fixed left-0 top-0 h-screen w-full z-7" />
|
||||
|
||||
<!-- 脚手架开发进度 -->
|
||||
<div class="p-4">
|
||||
<h1 class="text-4xl font-bold mb-12 ml-10">脚手架开发进度</h1>
|
||||
<div class="w-full grid grid-cols-3 gap-4 justify-items-center">
|
||||
<DevelopProgressCard
|
||||
v-for="item in progress"
|
||||
:key="item.title"
|
||||
:completed="item.completed"
|
||||
:pending="item.pending"
|
||||
:title="item.title"
|
||||
:intro="item.intro"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 组件演示 -->
|
||||
<div class="p-4">
|
||||
<h1 class="text-4xl font-bold mb-12 ml-10">组件演示</h1>
|
||||
<div class="w-full grid grid-cols-3 gap-4 justify-items-center">
|
||||
<DatePickerDisplayCard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- fixed footer -->
|
||||
<FooterBarV2 class="fixed bottom-0 left-0 z-1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Component styles */
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
||||
20
src/pages/join.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import NavBar from "@/components/menu/NavBar.vue";
|
||||
import FooterBarV2 from "@/components/layout/FooterBarV2.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="lg:pb-102 md:pb-122 pb-154">
|
||||
<!-- 这里开 relative 形成 stack context -->
|
||||
<div class="bg-base-200 relative z-2">
|
||||
<div class="h-screen flex flex-col">
|
||||
<NavBar class="fixed top-0 left-0 z-10" />
|
||||
<!-- 同高度占位颜色叠加 -->
|
||||
<div class="h-16 bg-base-300" />
|
||||
</div>
|
||||
</div>
|
||||
<FooterBarV2 />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
31
src/stores/LanguageStore.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export enum GlobalLanguage {
|
||||
ZH = "ZH",
|
||||
EN = "EN",
|
||||
}
|
||||
|
||||
export const languageMap = {
|
||||
[GlobalLanguage.ZH]: "简体中文",
|
||||
[GlobalLanguage.EN]: "English",
|
||||
};
|
||||
|
||||
export const useLanguageStore = defineStore(
|
||||
"language",
|
||||
() => {
|
||||
const language = ref<GlobalLanguage>(GlobalLanguage.ZH);
|
||||
|
||||
const setLanguage = (newLanguage: GlobalLanguage) => {
|
||||
language.value = newLanguage;
|
||||
};
|
||||
|
||||
return {
|
||||
language,
|
||||
setLanguage,
|
||||
};
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
);
|
||||
59
src/stores/ThemeStore.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export enum GlobalTheme {
|
||||
Light = "light",
|
||||
Dark = "dark",
|
||||
Cupcake = "cupcake",
|
||||
Bumblebee = "bumblebee",
|
||||
Emerald = "emerald",
|
||||
Corporate = "corporate",
|
||||
Synthwave = "synthwave",
|
||||
Retro = "retro",
|
||||
Cyberpunk = "cyberpunk",
|
||||
Valentine = "valentine",
|
||||
Halloween = "halloween",
|
||||
Garden = "garden",
|
||||
Forest = "forest",
|
||||
Aqua = "aqua",
|
||||
Lofi = "lofi",
|
||||
Pastel = "pastel",
|
||||
Fantasy = "fantasy",
|
||||
Wireframe = "wireframe",
|
||||
Black = "black",
|
||||
Luxury = "luxury",
|
||||
Dracula = "dracula",
|
||||
Cmyk = "cmyk",
|
||||
Autumn = "autumn",
|
||||
Business = "business",
|
||||
Acid = "acid",
|
||||
Lemonade = "lemonade",
|
||||
Night = "night",
|
||||
Coffee = "coffee",
|
||||
Winter = "winter",
|
||||
Dim = "dim",
|
||||
Nord = "nord",
|
||||
Sunset = "sunset",
|
||||
Caramellatte = "caramellatte",
|
||||
Abyss = "abyss",
|
||||
Silk = "silk",
|
||||
}
|
||||
|
||||
export const useThemeStore = defineStore(
|
||||
"theme",
|
||||
() => {
|
||||
const theme = ref<GlobalTheme>(GlobalTheme.Light);
|
||||
|
||||
const setTheme = (newTheme: GlobalTheme) => {
|
||||
theme.value = newTheme;
|
||||
};
|
||||
|
||||
return {
|
||||
theme,
|
||||
setTheme,
|
||||
};
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
);
|
||||
@@ -1,18 +1,89 @@
|
||||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export interface UserInfo {
|
||||
id: number;
|
||||
username: string;
|
||||
nickname: string;
|
||||
email: string;
|
||||
mobile: string;
|
||||
sex: number;
|
||||
avatar: string;
|
||||
loginIp: string;
|
||||
loginDate: null;
|
||||
createTime: number;
|
||||
roles: object[];
|
||||
dept: null;
|
||||
posts: null;
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore(
|
||||
"user",
|
||||
() => {
|
||||
const token = ref("");
|
||||
const accessToken = ref("");
|
||||
const refreshToken = ref("");
|
||||
|
||||
const setToken = (newToken: string) => {
|
||||
token.value = newToken;
|
||||
const setAccessToken = (newToken: string) => {
|
||||
accessToken.value = newToken;
|
||||
};
|
||||
|
||||
const setRefreshToken = (newToken: string) => {
|
||||
refreshToken.value = newToken;
|
||||
};
|
||||
|
||||
const userInfo = ref<UserInfo>({
|
||||
id: 0,
|
||||
username: "",
|
||||
nickname: "",
|
||||
email: "",
|
||||
mobile: "",
|
||||
sex: 0,
|
||||
avatar: "",
|
||||
loginIp: "",
|
||||
loginDate: null,
|
||||
createTime: 0,
|
||||
roles: [],
|
||||
dept: null,
|
||||
posts: null,
|
||||
});
|
||||
|
||||
const setUserInfo = (newInfo: UserInfo) => {
|
||||
userInfo.value = newInfo;
|
||||
};
|
||||
|
||||
const checkUserLogin = () => {
|
||||
return accessToken.value !== "";
|
||||
};
|
||||
|
||||
const clearUserInfo = () => {
|
||||
accessToken.value = "";
|
||||
refreshToken.value = "";
|
||||
userInfo.value = {
|
||||
id: 0,
|
||||
username: "",
|
||||
nickname: "",
|
||||
email: "",
|
||||
mobile: "",
|
||||
sex: 0,
|
||||
avatar: "",
|
||||
loginIp: "",
|
||||
loginDate: null,
|
||||
createTime: 0,
|
||||
roles: [],
|
||||
dept: null,
|
||||
posts: null,
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
token,
|
||||
setToken,
|
||||
accessToken,
|
||||
setAccessToken,
|
||||
refreshToken,
|
||||
setRefreshToken,
|
||||
userInfo,
|
||||
setUserInfo,
|
||||
checkUserLogin,
|
||||
clearUserInfo,
|
||||
};
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "daisyui";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@plugin "@iconify/tailwind4";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
@plugin "daisyui" {
|
||||
themes: all;
|
||||
};
|
||||
|
||||
@plugin "daisyui/theme" {
|
||||
name: "light";
|
||||
--color-primary: #5092d4;
|
||||
--color-secondary: #bfd7f1;
|
||||
--color-accent: #76b9ef;
|
||||
/* 自定义字体在 App.vue 中加载 */
|
||||
--font-sans: '"Harmony", "Microsoft YaHei", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
|
||||
}
|
||||
|
||||
/* Custom Scrollbar Styles */
|
||||
::-webkit-scrollbar {
|
||||
@@ -27,3 +43,5 @@
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, 0.2) rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
// 关于 shadcn 的主题, 请进入组件内部通过 style 设置 css 变量
|
||||
|
||||
@@ -5,22 +5,30 @@ const instance = axios.create({
|
||||
baseURL: import.meta.env.VITE_SERVER,
|
||||
});
|
||||
|
||||
// 所有请求自动加入 token
|
||||
instance.interceptors.request.use((config) => {
|
||||
const store = useUserStore();
|
||||
if (!store.token) {
|
||||
if (!store.accessToken) {
|
||||
return config;
|
||||
}
|
||||
const token = store.token;
|
||||
const token = store.accessToken;
|
||||
if (!token) {
|
||||
return config;
|
||||
}
|
||||
config.headers["token"] = token;
|
||||
config.headers["Authorization"] = `Bearer ${token}`;
|
||||
return config;
|
||||
});
|
||||
|
||||
const http = async <T>(config: AxiosRequestConfig): Promise<Result<T>> => {
|
||||
export const http = async <T>(
|
||||
config: AxiosRequestConfig,
|
||||
): Promise<Result<T>> => {
|
||||
const { data } = await instance.request<Result<T>>(config);
|
||||
return data;
|
||||
};
|
||||
|
||||
export default http;
|
||||
export const httpWithCustomReturn = async <T>(
|
||||
config: AxiosRequestConfig,
|
||||
): Promise<T> => {
|
||||
const { data } = await instance.request<T>(config);
|
||||
return data;
|
||||
};
|
||||
|
||||
13
src/utils/navigator.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import router from "@/router";
|
||||
|
||||
export const navigateTo = (path: string, replace?: boolean = false) => {
|
||||
if (path.startsWith("http")) {
|
||||
if (replace) {
|
||||
window.open(path, "_self");
|
||||
} else {
|
||||
window.open(path, "_blank");
|
||||
}
|
||||
return;
|
||||
}
|
||||
router.push(path);
|
||||
};
|
||||
@@ -3,5 +3,11 @@
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import { fileURLToPath, URL } from "node:url";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
import Components from "unplugin-vue-components/vite";
|
||||
import MotionResolver from "motion-v/resolver";
|
||||
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
@@ -14,6 +17,11 @@ export default defineConfig({
|
||||
AutoImport({
|
||||
imports: ["vue", "vue-router"],
|
||||
dts: true,
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver(), MotionResolver()],
|
||||
dts: true,
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
|
||||