优化:自适应v0.1

This commit is contained in:
马丁 2026-05-19 13:02:13 +08:00
parent 970d688dd4
commit 85a2590676
12 changed files with 568 additions and 250 deletions

View File

@ -54,6 +54,8 @@
"oxlint": "~1.42.0", "oxlint": "~1.42.0",
"postcss": "^8.5.8", "postcss": "^8.5.8",
"prettier": "3.8.1", "prettier": "3.8.1",
"sass": "^1.99.0",
"sass-loader": "^16.0.8",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.3.1", "vite": "^7.3.1",
"vite-plugin-node-polyfills": "^0.25.0", "vite-plugin-node-polyfills": "^0.25.0",

309
pnpm-lock.yaml generated
View File

@ -56,13 +56,13 @@ importers:
version: 24.10.13 version: 24.10.13
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^6.0.3 specifier: ^6.0.3
version: 6.0.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) version: 6.0.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))
'@vitejs/plugin-vue-jsx': '@vitejs/plugin-vue-jsx':
specifier: ^5.1.3 specifier: ^5.1.3
version: 5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) version: 5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))
'@vitest/eslint-plugin': '@vitest/eslint-plugin':
specifier: ^1.6.6 specifier: ^1.6.6
version: 1.6.9(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(yaml@2.8.2)) version: 1.6.9(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(sass@1.99.0)(yaml@2.8.2))
'@vue/eslint-config-typescript': '@vue/eslint-config-typescript':
specifier: ^14.6.0 specifier: ^14.6.0
version: 14.7.0(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.56.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) version: 14.7.0(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.56.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
@ -111,21 +111,27 @@ importers:
prettier: prettier:
specifier: 3.8.1 specifier: 3.8.1
version: 3.8.1 version: 3.8.1
sass:
specifier: ^1.99.0
version: 1.99.0
sass-loader:
specifier: ^16.0.8
version: 16.0.8(sass@1.99.0)
typescript: typescript:
specifier: ~5.9.3 specifier: ~5.9.3
version: 5.9.3 version: 5.9.3
vite: vite:
specifier: ^7.3.1 specifier: ^7.3.1
version: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) version: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
vite-plugin-node-polyfills: vite-plugin-node-polyfills:
specifier: ^0.25.0 specifier: ^0.25.0
version: 0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)) version: 0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))
vite-plugin-vue-devtools: vite-plugin-vue-devtools:
specifier: ^8.0.5 specifier: ^8.0.5
version: 8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) version: 8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))
vitest: vitest:
specifier: ^4.0.18 specifier: ^4.0.18
version: 4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(yaml@2.8.2) version: 4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(sass@1.99.0)(yaml@2.8.2)
vue-tsc: vue-tsc:
specifier: ^3.2.4 specifier: ^3.2.4
version: 3.2.4(typescript@5.9.3) version: 3.2.4(typescript@5.9.3)
@ -646,6 +652,94 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@parcel/watcher-android-arm64@2.5.6':
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [android]
'@parcel/watcher-darwin-arm64@2.5.6':
resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [darwin]
'@parcel/watcher-darwin-x64@2.5.6':
resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [darwin]
'@parcel/watcher-freebsd-x64@2.5.6':
resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [freebsd]
'@parcel/watcher-linux-arm-glibc@2.5.6':
resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.6':
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.6':
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.6':
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.6':
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.6':
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@parcel/watcher-win32-arm64@2.5.6':
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [win32]
'@parcel/watcher-win32-ia32@2.5.6':
resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==}
engines: {node: '>= 10.0.0'}
cpu: [ia32]
os: [win32]
'@parcel/watcher-win32-x64@2.5.6':
resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [win32]
'@parcel/watcher@2.5.6':
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@ -1310,6 +1404,10 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'} engines: {node: '>=10'}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
chokidar@5.0.0: chokidar@5.0.0:
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
engines: {node: '>= 20.19.0'} engines: {node: '>= 20.19.0'}
@ -1435,6 +1533,10 @@ packages:
des.js@1.1.0: des.js@1.1.0:
resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
diffie-hellman@5.0.3: diffie-hellman@5.0.3:
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
@ -1786,6 +1888,9 @@ packages:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
immutable@5.1.5:
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
import-fresh@3.3.1: import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2057,6 +2162,12 @@ packages:
natural-compare@1.4.0: natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
neo-async@2.6.2:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
node-releases@2.0.27: node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
@ -2303,6 +2414,10 @@ packages:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
readdirp@5.0.0: readdirp@5.0.0:
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
engines: {node: '>= 20.19.0'} engines: {node: '>= 20.19.0'}
@ -2353,6 +2468,32 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
sass-loader@16.0.8:
resolution: {integrity: sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==}
engines: {node: '>= 18.12.0'}
peerDependencies:
'@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0
node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
sass: ^1.3.0
sass-embedded: '*'
webpack: ^5.0.0
peerDependenciesMeta:
'@rspack/core':
optional: true
node-sass:
optional: true
sass:
optional: true
sass-embedded:
optional: true
webpack:
optional: true
sass@1.99.0:
resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==}
engines: {node: '>=14.0.0'}
hasBin: true
saxes@6.0.0: saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'} engines: {node: '>=v12.22.7'}
@ -3354,6 +3495,67 @@ snapshots:
'@oxlint/win32-x64@1.42.0': '@oxlint/win32-x64@1.42.0':
optional: true optional: true
'@parcel/watcher-android-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-x64@2.5.6':
optional: true
'@parcel/watcher-freebsd-x64@2.5.6':
optional: true
'@parcel/watcher-linux-arm-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm-musl@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-musl@2.5.6':
optional: true
'@parcel/watcher-linux-x64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-x64-musl@2.5.6':
optional: true
'@parcel/watcher-win32-arm64@2.5.6':
optional: true
'@parcel/watcher-win32-ia32@2.5.6':
optional: true
'@parcel/watcher-win32-x64@2.5.6':
optional: true
'@parcel/watcher@2.5.6':
dependencies:
detect-libc: 2.1.2
is-glob: 4.0.3
node-addon-api: 7.1.1
picomatch: 4.0.3
optionalDependencies:
'@parcel/watcher-android-arm64': 2.5.6
'@parcel/watcher-darwin-arm64': 2.5.6
'@parcel/watcher-darwin-x64': 2.5.6
'@parcel/watcher-freebsd-x64': 2.5.6
'@parcel/watcher-linux-arm-glibc': 2.5.6
'@parcel/watcher-linux-arm-musl': 2.5.6
'@parcel/watcher-linux-arm64-glibc': 2.5.6
'@parcel/watcher-linux-arm64-musl': 2.5.6
'@parcel/watcher-linux-x64-glibc': 2.5.6
'@parcel/watcher-linux-x64-musl': 2.5.6
'@parcel/watcher-win32-arm64': 2.5.6
'@parcel/watcher-win32-ia32': 2.5.6
'@parcel/watcher-win32-x64': 2.5.6
optional: true
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
optional: true optional: true
@ -3600,32 +3802,32 @@ snapshots:
'@typescript-eslint/types': 8.56.0 '@typescript-eslint/types': 8.56.0
eslint-visitor-keys: 5.0.0 eslint-visitor-keys: 5.0.0
'@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))': '@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))':
dependencies: dependencies:
'@babel/core': 7.29.0 '@babel/core': 7.29.0
'@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0)
'@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0)
'@rolldown/pluginutils': 1.0.0-rc.4 '@rolldown/pluginutils': 1.0.0-rc.4
'@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0) '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0)
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
vue: 3.5.28(typescript@5.9.3) vue: 3.5.28(typescript@5.9.3)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))': '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))':
dependencies: dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2 '@rolldown/pluginutils': 1.0.0-rc.2
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
vue: 3.5.28(typescript@5.9.3) vue: 3.5.28(typescript@5.9.3)
'@vitest/eslint-plugin@1.6.9(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(yaml@2.8.2))': '@vitest/eslint-plugin@1.6.9(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(sass@1.99.0)(yaml@2.8.2))':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.56.0 '@typescript-eslint/scope-manager': 8.56.0
'@typescript-eslint/utils': 8.56.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.56.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1) eslint: 9.39.2(jiti@2.6.1)
optionalDependencies: optionalDependencies:
typescript: 5.9.3 typescript: 5.9.3
vitest: 4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(yaml@2.8.2) vitest: 4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(sass@1.99.0)(yaml@2.8.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -3638,13 +3840,13 @@ snapshots:
chai: 6.2.2 chai: 6.2.2
tinyrainbow: 3.0.3 tinyrainbow: 3.0.3
'@vitest/mocker@4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))': '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))':
dependencies: dependencies:
'@vitest/spy': 4.0.18 '@vitest/spy': 4.0.18
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.21 magic-string: 0.30.21
optionalDependencies: optionalDependencies:
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
'@vitest/pretty-format@4.0.18': '@vitest/pretty-format@4.0.18':
dependencies: dependencies:
@ -3788,14 +3990,14 @@ snapshots:
dependencies: dependencies:
'@vue/devtools-kit': 8.0.6 '@vue/devtools-kit': 8.0.6
'@vue/devtools-core@8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))': '@vue/devtools-core@8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))':
dependencies: dependencies:
'@vue/devtools-kit': 8.0.6 '@vue/devtools-kit': 8.0.6
'@vue/devtools-shared': 8.0.6 '@vue/devtools-shared': 8.0.6
mitt: 3.0.1 mitt: 3.0.1
nanoid: 5.1.6 nanoid: 5.1.6
pathe: 2.0.3 pathe: 2.0.3
vite-hot-client: 2.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)) vite-hot-client: 2.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))
vue: 3.5.28(typescript@5.9.3) vue: 3.5.28(typescript@5.9.3)
transitivePeerDependencies: transitivePeerDependencies:
- vite - vite
@ -4098,6 +4300,10 @@ snapshots:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
supports-color: 7.2.0 supports-color: 7.2.0
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
chokidar@5.0.0: chokidar@5.0.0:
dependencies: dependencies:
readdirp: 5.0.0 readdirp: 5.0.0
@ -4239,6 +4445,9 @@ snapshots:
inherits: 2.0.4 inherits: 2.0.4
minimalistic-assert: 1.0.1 minimalistic-assert: 1.0.1
detect-libc@2.1.2:
optional: true
diffie-hellman@5.0.3: diffie-hellman@5.0.3:
dependencies: dependencies:
bn.js: 4.12.2 bn.js: 4.12.2
@ -4643,6 +4852,8 @@ snapshots:
ignore@7.0.5: {} ignore@7.0.5: {}
immutable@5.1.5: {}
import-fresh@3.3.1: import-fresh@3.3.1:
dependencies: dependencies:
parent-module: 1.0.1 parent-module: 1.0.1
@ -4895,6 +5106,11 @@ snapshots:
natural-compare@1.4.0: {} natural-compare@1.4.0: {}
neo-async@2.6.2: {}
node-addon-api@7.1.1:
optional: true
node-releases@2.0.27: {} node-releases@2.0.27: {}
node-stdlib-browser@1.3.1: node-stdlib-browser@1.3.1:
@ -5176,6 +5392,8 @@ snapshots:
string_decoder: 1.3.0 string_decoder: 1.3.0
util-deprecate: 1.0.2 util-deprecate: 1.0.2
readdirp@4.1.2: {}
readdirp@5.0.0: {} readdirp@5.0.0: {}
require-from-string@2.0.2: {} require-from-string@2.0.2: {}
@ -5244,6 +5462,20 @@ snapshots:
es-errors: 1.3.0 es-errors: 1.3.0
is-regex: 1.2.1 is-regex: 1.2.1
sass-loader@16.0.8(sass@1.99.0):
dependencies:
neo-async: 2.6.2
optionalDependencies:
sass: 1.99.0
sass@1.99.0:
dependencies:
chokidar: 4.0.3
immutable: 5.1.5
source-map-js: 1.2.1
optionalDependencies:
'@parcel/watcher': 2.5.6
saxes@6.0.0: saxes@6.0.0:
dependencies: dependencies:
xmlchars: 2.2.0 xmlchars: 2.2.0
@ -5499,17 +5731,17 @@ snapshots:
is-typed-array: 1.1.15 is-typed-array: 1.1.15
which-typed-array: 1.1.20 which-typed-array: 1.1.20
vite-dev-rpc@1.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)): vite-dev-rpc@1.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)):
dependencies: dependencies:
birpc: 2.9.0 birpc: 2.9.0
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
vite-hot-client: 2.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)) vite-hot-client: 2.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))
vite-hot-client@2.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)): vite-hot-client@2.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)):
dependencies: dependencies:
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
vite-plugin-inspect@11.3.3(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)): vite-plugin-inspect@11.3.3(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)):
dependencies: dependencies:
ansis: 4.2.0 ansis: 4.2.0
debug: 4.4.3 debug: 4.4.3
@ -5519,34 +5751,34 @@ snapshots:
perfect-debounce: 2.1.0 perfect-debounce: 2.1.0
sirv: 3.0.2 sirv: 3.0.2
unplugin-utils: 0.3.1 unplugin-utils: 0.3.1
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
vite-dev-rpc: 1.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)) vite-dev-rpc: 1.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
vite-plugin-node-polyfills@0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)): vite-plugin-node-polyfills@0.25.0(rollup@4.57.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)):
dependencies: dependencies:
'@rollup/plugin-inject': 5.0.5(rollup@4.57.1) '@rollup/plugin-inject': 5.0.5(rollup@4.57.1)
node-stdlib-browser: 1.3.1 node-stdlib-browser: 1.3.1
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
transitivePeerDependencies: transitivePeerDependencies:
- rollup - rollup
vite-plugin-vue-devtools@8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)): vite-plugin-vue-devtools@8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)):
dependencies: dependencies:
'@vue/devtools-core': 8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) '@vue/devtools-core': 8.0.6(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))
'@vue/devtools-kit': 8.0.6 '@vue/devtools-kit': 8.0.6
'@vue/devtools-shared': 8.0.6 '@vue/devtools-shared': 8.0.6
sirv: 3.0.2 sirv: 3.0.2
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
vite-plugin-inspect: 11.3.3(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)) vite-plugin-inspect: 11.3.3(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))
vite-plugin-vue-inspector: 5.3.2(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)) vite-plugin-vue-inspector: 5.3.2(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))
transitivePeerDependencies: transitivePeerDependencies:
- '@nuxt/kit' - '@nuxt/kit'
- supports-color - supports-color
- vue - vue
vite-plugin-vue-inspector@5.3.2(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)): vite-plugin-vue-inspector@5.3.2(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)):
dependencies: dependencies:
'@babel/core': 7.29.0 '@babel/core': 7.29.0
'@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0)
@ -5557,11 +5789,11 @@ snapshots:
'@vue/compiler-dom': 3.5.28 '@vue/compiler-dom': 3.5.28
kolorist: 1.8.0 kolorist: 1.8.0
magic-string: 0.30.21 magic-string: 0.30.21
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2): vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2):
dependencies: dependencies:
esbuild: 0.27.3 esbuild: 0.27.3
fdir: 6.5.0(picomatch@4.0.3) fdir: 6.5.0(picomatch@4.0.3)
@ -5573,12 +5805,13 @@ snapshots:
'@types/node': 24.10.13 '@types/node': 24.10.13
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.6.1 jiti: 2.6.1
sass: 1.99.0
yaml: 2.8.2 yaml: 2.8.2
vitest@4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(yaml@2.8.2): vitest@4.0.18(@types/node@24.10.13)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@1.8.0))(sass@1.99.0)(yaml@2.8.2):
dependencies: dependencies:
'@vitest/expect': 4.0.18 '@vitest/expect': 4.0.18
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)) '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2))
'@vitest/pretty-format': 4.0.18 '@vitest/pretty-format': 4.0.18
'@vitest/runner': 4.0.18 '@vitest/runner': 4.0.18
'@vitest/snapshot': 4.0.18 '@vitest/snapshot': 4.0.18
@ -5595,7 +5828,7 @@ snapshots:
tinyexec: 1.0.2 tinyexec: 1.0.2
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tinyrainbow: 3.0.3 tinyrainbow: 3.0.3
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2) vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 24.10.13 '@types/node': 24.10.13

View File

@ -77,14 +77,8 @@ watch(
<v-app> <v-app>
<v-app-bar color="surface" elevation="0"> <v-app-bar color="surface" elevation="0">
<div class="app-bar-inner"> <div class="app-bar-inner">
<v-btn <v-btn v-if="currentRoute !== '/'" icon variant="text" class="back-btn" :aria-label="t('common.back')"
v-if="currentRoute !== '/'" @click="onBackClick">
icon
variant="text"
class="back-btn"
:aria-label="t('common.back')"
@click="onBackClick"
>
<v-icon>mdi-arrow-left</v-icon> <v-icon>mdi-arrow-left</v-icon>
</v-btn> </v-btn>
<v-app-bar-title v-if="currentRoute === '/'" class="brand-title"> <v-app-bar-title v-if="currentRoute === '/'" class="brand-title">
@ -100,59 +94,30 @@ watch(
</v-app-bar-title> </v-app-bar-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<template v-if="!userStore.isLoggedIn"> <template v-if="!userStore.isLoggedIn">
<v-menu <v-menu v-model="localeMenuOpen" :close-on-content-click="true" location="bottom"
v-model="localeMenuOpen" transition="scale-transition">
:close-on-content-click="true"
location="bottom"
transition="scale-transition"
>
<template #activator="{ props }"> <template #activator="{ props }">
<v-btn <v-btn icon variant="text" class="locale-btn" :aria-label="t('profile.selectLanguage')" v-bind="props">
icon
variant="text"
class="locale-btn"
:aria-label="t('profile.selectLanguage')"
v-bind="props"
>
<v-icon>mdi-earth</v-icon> <v-icon>mdi-earth</v-icon>
</v-btn> </v-btn>
</template> </template>
<v-list density="compact"> <v-list density="compact">
<v-list-item <v-list-item v-for="opt in localeStore.localeOptions" :key="opt.value"
v-for="opt in localeStore.localeOptions" :active="localeStore.currentLocale === opt.value" @click="chooseLocale(opt.value)">
:key="opt.value"
:active="localeStore.currentLocale === opt.value"
@click="chooseLocale(opt.value)"
>
<v-list-item-title>{{ opt.label }}</v-list-item-title> <v-list-item-title>{{ opt.label }}</v-list-item-title>
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
<v-btn <v-btn text to="/login" :class="{ active: currentRoute === '/login' }">
text
to="/login"
:class="{ active: currentRoute === '/login' }"
>
{{ t('common.login') }} {{ t('common.login') }}
</v-btn> </v-btn>
</template> </template>
<template v-else> <template v-else>
<v-btn <v-btn class="balance-btn" variant="text" min-width="auto" padding="4 12" @click="$router.push('/wallet')">
class="balance-btn"
variant="text"
min-width="auto"
padding="4 12"
@click="$router.push('/wallet')"
>
<span class="balance-text">${{ userStore.totalAssetValue }}</span> <span class="balance-text">${{ userStore.totalAssetValue }}</span>
</v-btn> </v-btn>
<v-btn <v-btn icon variant="text" class="avatar-btn" :aria-label="t('common.user')"
icon @click="$router.push('/profile')">
variant="text"
class="avatar-btn"
:aria-label="t('common.user')"
@click="$router.push('/profile')"
>
<v-avatar size="36" color="primary"> <v-avatar size="36" color="primary">
<v-img v-if="userStore.avatarUrl" :src="userStore.avatarUrl" cover alt="avatar" /> <v-img v-if="userStore.avatarUrl" :src="userStore.avatarUrl" cover alt="avatar" />
<v-icon v-else>mdi-account</v-icon> <v-icon v-else>mdi-account</v-icon>
@ -173,13 +138,7 @@ watch(
</div> </div>
</v-main> </v-main>
<v-bottom-navigation <v-bottom-navigation v-if="showBottomNav" v-model="bottomNavValue" app height="56" elevation="0">
v-if="showBottomNav"
v-model="bottomNavValue"
app
height="56"
elevation="0"
>
<v-btn value="/" :ripple="false"> <v-btn value="/" :ripple="false">
<v-icon size="24">mdi-home-outline</v-icon> <v-icon size="24">mdi-home-outline</v-icon>
<span>{{ t('nav.home') }}</span> <span>{{ t('nav.home') }}</span>
@ -204,7 +163,7 @@ watch(
<style scoped> <style scoped>
.app-bar-inner { .app-bar-inner {
max-width: 1280px; max-width: 1440px;
margin: 0 auto; margin: 0 auto;
width: 100%; width: 100%;
display: flex; display: flex;
@ -281,15 +240,15 @@ watch(
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.app-main-scroll { .app-main-scroll {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
overflow-y: auto; overflow-y: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
.main-content { .main-content {
max-width: 1280px;
margin: 0 auto;
width: 100%; width: 100%;
} }
@ -352,10 +311,12 @@ watch(
background: #fff !important; background: #fff !important;
transform: translateZ(0); transform: translateZ(0);
} }
/* 底部导航:三个入口等分屏幕宽度 */ /* 底部导航:三个入口等分屏幕宽度 */
:deep(.v-bottom-navigation__content) { :deep(.v-bottom-navigation__content) {
width: 100%; width: 100%;
} }
:deep(.v-bottom-navigation__content > .v-btn) { :deep(.v-bottom-navigation__content > .v-btn) {
flex: 1 1 0; flex: 1 1 0;
min-width: 0; min-width: 0;
@ -365,6 +326,7 @@ watch(
:deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active)) { :deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active)) {
color: rgba(0, 0, 0, 0.45); color: rgba(0, 0, 0, 0.45);
} }
:deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active) .v-icon) { :deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active) .v-icon) {
color: rgba(0, 0, 0, 0.45); color: rgba(0, 0, 0, 0.45);
} }
@ -375,6 +337,7 @@ watch(
font-weight: 700; font-weight: 700;
background: transparent !important; background: transparent !important;
} }
:deep(.v-bottom-navigation__content > .v-btn .v-btn__overlay), :deep(.v-bottom-navigation__content > .v-btn .v-btn__overlay),
:deep(.v-bottom-navigation__content > .v-btn .v-btn__underlay) { :deep(.v-bottom-navigation__content > .v-btn .v-btn__underlay) {
display: none !important; display: none !important;
@ -383,19 +346,4 @@ watch(
:deep(.v-bottom-navigation__content .v-ripple__container) { :deep(.v-bottom-navigation__content .v-ripple__container) {
display: none !important; display: none !important;
} }
/* 全局:禁止 body 滚动,由 app-main-scroll 内部滚动,滚动条不覆盖底部导航 */
:global(html),
:global(body) {
background: rgb(252, 252, 252);
height: 100%;
overflow: hidden;
}
:global(.v-application) {
background: rgb(252, 252, 252);
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
</style> </style>

View File

@ -0,0 +1,32 @@
body {
font-size: 14px;
color: $text-primary;
background-color: #ffffff;
}
/* Chrome / Edge / Safari 隐藏滚动条 */
::-webkit-scrollbar {
display: none !important;
width: 0 !important;
height: 0 !important;
background: transparent !important;
-webkit-appearance: none !important;
}
/* Firefox 隐藏滚动条 */
html, body {
scrollbar-width: none !important; /* 火狐专用 */
-ms-overflow-style: none !important; /* IE 专用 */
}
/* 全局所有容器统一隐藏滚动条 */
* {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
}

View File

@ -0,0 +1,17 @@
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.w-full { width: 100%; }
.h-full { height: 100%; }
// 外边距
.mt-sm { margin-top: $space-sm; }
.mt-md { margin-top: $space-md; }
.mb-sm { margin-bottom: $space-sm; }
// 内边距
.p-sm { padding: $space-sm; }
.p-md { padding: $space-md; }

View File

@ -0,0 +1,93 @@
// // 水平垂直居中
// @mixin flex-center {
// display: flex;
// align-items: center;
// justify-content: center;
// }
// // 单行省略
// @mixin text-ellipsis {
// overflow: hidden;
// white-space: nowrap;
// text-overflow: ellipsis;
// }
// // 多行省略
// @mixin line-ellipsis($line: 2) {
// display: -webkit-box;
// -webkit-line-clamp: $line;
// -webkit-box-orient: vertical;
// overflow: hidden;
// }
// // 适配移动端1px边框
// @mixin border-1px($color:#eee) {
// position: relative;
// &::after {
// content: '';
// position: absolute;
// left: 0;
// bottom: 0;
// width: 100%;
// height: 1px;
// background: $color;
// transform: scaleY(0.5);
// }
// }
// 全平台响应式 Mixin手机+平板+PC
@mixin m1 {
@media screen and (max-width: 767px) {
@content;
}
}
@mixin gt768 {
@media screen and (min-width: 768px) {
@content;
}
}
@mixin m768-1023 {
@media screen and (min-width: 768px) and (max-width: 1023px) {
@content;
}
}
@mixin m1024-1439 {
@media screen and (min-width: 1024px) and (max-width: 1439px) {
@content;
}
}
@mixin gt1024 {
@media screen and (min-width: 1024px) {
@content;
}
}
@mixin gt1440 {
@media screen and (min-width: 1440px) {
@content;
}
}
@mixin auto-space($prop) {
// 默认手机
#{$prop}: $space-xs;
// 平板 768-1023
@include m768-1023 {
#{$prop}: $space-sm;
}
// PC 1024-1439
@include m1024-1439 {
#{$prop}: $space-md;
}
// 大屏 1440+
@include gt1440 {
#{$prop}: $space-lg;
}
}

View File

@ -0,0 +1,38 @@
/* 全局:禁止 body 滚动,由 app-main-scroll 内部滚动,滚动条不覆盖底部导航 */
:global(html),
:global(body) {
background: rgb(252, 252, 252);
height: 100%;
overflow: hidden;
}
:global(.v-application) {
background: rgb(252, 252, 252);
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,body {
height: 100%;
}
ul,ol {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
img {
border: none;
}
button {
border: none;
background: none;
cursor: pointer;
}

View File

@ -0,0 +1,24 @@
// 主题色
$primary: #1677ff;
$success: #00b42a;
$warning: #ff7d00;
$danger: #f53f3f;
// 文字色
$text-primary: #1d2129;
$text-secondary: #666;
$text-placeholder: #c9cdd4;
// 间距
$space-xs: 4px;
$space-sm: 8px;
$space-md: 16px;
$space-lg: 24px;
// 圆角
$radius-sm: 4px;
$radius-md: 8px;
$radius-lg: 12px;
// 阴影
$shadow: 0 2px 12px 0 rgba(0,0,0,0.08);

View File

@ -8,6 +8,10 @@ import { i18n } from './plugins/i18n'
import { setHttpUnauthorizedHandler } from './api/request' import { setHttpUnauthorizedHandler } from './api/request'
import { useUserStore } from './stores/user' import { useUserStore } from './stores/user'
import '@/assets/styles/reset.scss'
import '@/assets/styles/base.scss'
import '@/assets/styles/global.scss'
const app = createApp(App) const app = createApp(App)
const pinia = createPinia() const pinia = createPinia()

0
src/stores/menu.ts Normal file
View File

View File

@ -3,11 +3,8 @@
<!-- 第一层单行紧凑布局tabs + 搜索/筛选图标 --> <!-- 第一层单行紧凑布局tabs + 搜索/筛选图标 -->
<div v-if="categoryLayers.length > 0" class="home-category-layer1-wrap"> <div v-if="categoryLayers.length > 0" class="home-category-layer1-wrap">
<div class="home-category-layer1-row"> <div class="home-category-layer1-row">
<v-tabs <v-tabs :model-value="layerActiveValues[0]" class="home-tab-bar home-tab-bar--inline"
:model-value="layerActiveValues[0]" @update:model-value="onCategorySelect(0, $event)">
class="home-tab-bar home-tab-bar--inline"
@update:model-value="onCategorySelect(0, $event)"
>
<v-tab v-for="item in categoryLayers[0]" :key="item.id" :value="item.id" :ripple="false"> <v-tab v-for="item in categoryLayers[0]" :key="item.id" :value="item.id" :ripple="false">
{{ item.label }} {{ item.label }}
</v-tab> </v-tab>
@ -17,63 +14,29 @@
<transition name="home-search-overlay"> <transition name="home-search-overlay">
<div v-if="searchExpanded" class="home-search-overlay"> <div v-if="searchExpanded" class="home-search-overlay">
<div class="home-search-overlay-inner"> <div class="home-search-overlay-inner">
<v-text-field <v-text-field ref="searchInputRef" v-model="searchKeyword" density="compact" hide-details
ref="searchInputRef" :placeholder="t('home.searchPlaceholder')" prepend-inner-icon="mdi-magnify" variant="outlined"
v-model="searchKeyword" class="home-search-overlay-field" @blur="onSearchBlur" @keydown.enter="onSearchSubmit" />
density="compact" <v-btn icon variant="text" size="small" class="home-search-close-btn" :aria-label="t('common.collapse')"
hide-details @click="collapseSearch">
:placeholder="t('home.searchPlaceholder')"
prepend-inner-icon="mdi-magnify"
variant="outlined"
class="home-search-overlay-field"
@blur="onSearchBlur"
@keydown.enter="onSearchSubmit"
/>
<v-btn
icon
variant="text"
size="small"
class="home-search-close-btn"
:aria-label="t('common.collapse')"
@click="collapseSearch"
>
<v-icon size="22">mdi-close</v-icon> <v-icon size="22">mdi-close</v-icon>
</v-btn> </v-btn>
</div> </div>
<div v-if="searchHistoryList.length > 0" class="home-search-history"> <div v-if="searchHistoryList.length > 0" class="home-search-history">
<div class="home-search-history-header"> <div class="home-search-history-header">
<span class="home-search-history-title">{{ t('home.searchHistory') }}</span> <span class="home-search-history-title">{{ t('home.searchHistory') }}</span>
<v-btn <v-btn variant="text" size="x-small" color="primary" class="home-search-history-clear"
variant="text" @click="searchHistory.clearAll">
size="x-small"
color="primary"
class="home-search-history-clear"
@click="searchHistory.clearAll"
>
{{ t('common.clear') }} {{ t('common.clear') }}
</v-btn> </v-btn>
</div> </div>
<ul class="home-search-history-list"> <ul class="home-search-history-list">
<li <li v-for="(item, idx) in searchHistoryList" :key="`${item}-${idx}`" class="home-search-history-item">
v-for="(item, idx) in searchHistoryList" <button type="button" class="home-search-history-text" @click="selectHistoryItem(item)">
:key="`${item}-${idx}`"
class="home-search-history-item"
>
<button
type="button"
class="home-search-history-text"
@click="selectHistoryItem(item)"
>
{{ item }} {{ item }}
</button> </button>
<v-btn <v-btn icon variant="text" size="x-small" class="home-search-history-delete"
icon :aria-label="t('common.delete')" @click.stop="searchHistory.remove(idx)">
variant="text"
size="x-small"
class="home-search-history-delete"
:aria-label="t('common.delete')"
@click.stop="searchHistory.remove(idx)"
>
<v-icon size="20">mdi-close</v-icon> <v-icon size="20">mdi-close</v-icon>
</v-btn> </v-btn>
</li> </li>
@ -87,34 +50,18 @@
<div v-if="categoryLayers.length >= 2" class="home-category-layers-23-scroll"> <div v-if="categoryLayers.length >= 2" class="home-category-layers-23-scroll">
<div class="home-category-layer home-category-layer--icon"> <div class="home-category-layer home-category-layer--icon">
<div class="home-category-icon-row"> <div class="home-category-icon-row">
<v-chip <v-chip v-for="item in categoryLayers[1]" :key="item.id" class="home-category-icon-item"
v-for="item in categoryLayers[1]"
:key="item.id"
class="home-category-icon-item"
:color="layerActiveValues[1] === item.id ? 'primary' : undefined" :color="layerActiveValues[1] === item.id ? 'primary' : undefined"
:variant="layerActiveValues[1] === item.id ? 'tonal' : 'outlined'" :variant="layerActiveValues[1] === item.id ? 'tonal' : 'outlined'" size="small"
size="small" @click="onCategorySelect(1, item.id)">
@click="onCategorySelect(1, item.id)"
>
<span class="home-category-icon-label">{{ item.label }}</span> <span class="home-category-icon-label">{{ item.label }}</span>
</v-chip> </v-chip>
</div> </div>
</div> </div>
<div <div v-if="categoryLayers.length >= 3" class="home-category-layer home-category-layer--third">
v-if="categoryLayers.length >= 3" <v-tabs :model-value="layerActiveValues[2]" class="home-tab-bar home-tab-bar--compact"
class="home-category-layer home-category-layer--third" @update:model-value="onCategorySelect(2, $event)">
> <v-tab v-for="item in categoryLayers[2]" :key="item.id" :value="item.id" :ripple="false">
<v-tabs
:model-value="layerActiveValues[2]"
class="home-tab-bar home-tab-bar--compact"
@update:model-value="onCategorySelect(2, $event)"
>
<v-tab
v-for="item in categoryLayers[2]"
:key="item.id"
:value="item.id"
:ripple="false"
>
{{ item.label }} {{ item.label }}
</v-tab> </v-tab>
</v-tabs> </v-tabs>
@ -125,28 +72,13 @@
<v-pull-to-refresh class="pull-to-refresh" @load="onRefresh"> <v-pull-to-refresh class="pull-to-refresh" @load="onRefresh">
<div class="pull-to-refresh-inner"> <div class="pull-to-refresh-inner">
<div ref="listRef" class="home-list" :style="gridListStyle"> <div ref="listRef" class="home-list" :style="gridListStyle">
<MarketCard <MarketCard v-for="card in eventList" :key="card.id" :id="card.id" :slug="card.slug"
v-for="card in eventList" :market-title="card.marketTitle" :chance-value="card.chanceValue" :market-info="card.marketInfo"
:key="card.id" :image-url="card.imageUrl" :category="card.category" :expires-at="card.expiresAt"
:id="card.id" :display-type="card.displayType" :outcomes="card.outcomes" :yes-label="card.yesLabel"
:slug="card.slug" :no-label="card.noLabel" :is-new="card.isNew" :market-id="card.marketId"
:market-title="card.marketTitle" :clob-token-ids="card.clobTokenIds" :yes-price="card.yesPrice" :no-price="card.noPrice"
:chance-value="card.chanceValue" @open-trade="onCardOpenTrade" />
:market-info="card.marketInfo"
:image-url="card.imageUrl"
:category="card.category"
:expires-at="card.expiresAt"
:display-type="card.displayType"
:outcomes="card.outcomes"
:yes-label="card.yesLabel"
:no-label="card.noLabel"
:is-new="card.isNew"
:market-id="card.marketId"
:clob-token-ids="card.clobTokenIds"
:yes-price="card.yesPrice"
:no-price="card.noPrice"
@open-trade="onCardOpenTrade"
/>
<div v-if="eventListLoading" class="home-list-empty home-list-loading"> <div v-if="eventListLoading" class="home-list-empty home-list-loading">
<v-progress-circular indeterminate size="40" width="2" /> <v-progress-circular indeterminate size="40" width="2" />
<span>{{ t('common.loading') }}</span> <span>{{ t('common.loading') }}</span>
@ -162,14 +94,8 @@
<span>{{ t('common.loading') }}</span> <span>{{ t('common.loading') }}</span>
</div> </div>
<div v-else-if="noMoreEvents" class="no-more-tip">{{ t('home.noMore') }}</div> <div v-else-if="noMoreEvents" class="no-more-tip">{{ t('home.noMore') }}</div>
<v-btn <v-btn v-else class="load-more-btn" variant="outlined" color="primary" :disabled="loadingMore"
v-else @click="loadMore">
class="load-more-btn"
variant="outlined"
color="primary"
:disabled="loadingMore"
@click="loadMore"
>
{{ t('home.loadMore') }} {{ t('home.loadMore') }}
</v-btn> </v-btn>
</div> </div>
@ -178,30 +104,15 @@
</div> </div>
<!-- PC对话框手机底部 sheet直接显示交易表单 --> <!-- PC对话框手机底部 sheet直接显示交易表单 -->
<v-dialog <v-dialog v-if="!isMobile" v-model="tradeDialogOpen" max-width="420" scrollable
v-if="!isMobile" content-class="trade-dialog trade-dialog--bare" transition="dialog-transition"
v-model="tradeDialogOpen" @click:outside="tradeDialogOpen = false">
max-width="420" <TradeComponent :key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`" :market="homeTradeMarketPayload"
scrollable :initial-option="tradeDialogSide" @order-success="onOrderSuccess" />
content-class="trade-dialog trade-dialog--bare"
transition="dialog-transition"
@click:outside="tradeDialogOpen = false"
>
<TradeComponent
:key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`"
:market="homeTradeMarketPayload"
:initial-option="tradeDialogSide"
@order-success="onOrderSuccess"
/>
</v-dialog> </v-dialog>
<v-bottom-sheet v-else v-model="tradeDialogOpen" content-class="trade-bottom-sheet"> <v-bottom-sheet v-else v-model="tradeDialogOpen" content-class="trade-bottom-sheet">
<TradeComponent <TradeComponent :key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`" :market="homeTradeMarketPayload"
:key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`" :initial-option="tradeDialogSide" embedded-in-sheet @order-success="onOrderSuccess" />
:market="homeTradeMarketPayload"
:initial-option="tradeDialogSide"
embedded-in-sheet
@order-success="onOrderSuccess"
/>
</v-bottom-sheet> </v-bottom-sheet>
</v-container> </v-container>
</div> </div>
@ -718,7 +629,7 @@ onActivated(() => {
}) })
</script> </script>
<style scoped> <style scoped lang="scss">
/* fluid 后无断点 max-width用自定义 max-width 让列表在 2137px 等宽屏下能算到 6 列 */ /* fluid 后无断点 max-width用自定义 max-width 让列表在 2137px 等宽屏下能算到 6 列 */
.home-container { .home-container {
min-height: 100vh; min-height: 100vh;
@ -823,6 +734,7 @@ onActivated(() => {
padding: 0; padding: 0;
overflow: visible; overflow: visible;
} }
.trade-dialog--bare :deep(.v-card) { .trade-dialog--bare :deep(.v-card) {
box-shadow: none; box-shadow: none;
} }
@ -847,21 +759,20 @@ onActivated(() => {
/* 第一层置顶、全宽sticky 参照 app-main-scrolltop:0 贴容器顶(即 app-bar 下方) */ /* 第一层置顶、全宽sticky 参照 app-main-scrolltop:0 贴容器顶(即 app-bar 下方) */
.home-category-layer1-wrap { .home-category-layer1-wrap {
width: 100vw; position: fixed;
max-width: 100vw;
margin-left: calc(50% - 50vw);
position: sticky;
top: 0;
z-index: 10; z-index: 10;
top: 64px;
width: 100vw;
background-color: white; background-color: white;
/* box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); */ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
} }
.home-category-layer1-row { .home-category-layer1-row {
max-width: 1440px;
margin: 0 auto;
display: flex; display: flex;
align-items: center; align-items: center;
min-height: 48px; @include auto-space(padding-left);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
} }
.home-tab-bar--inline { .home-tab-bar--inline {
@ -875,6 +786,7 @@ onActivated(() => {
.home-tab-bar--inline :deep(.v-tab) { .home-tab-bar--inline :deep(.v-tab) {
min-height: 48px; min-height: 48px;
padding: 0 8px 0 8px;
} }
.home-category-layer1-actions { .home-category-layer1-actions {
@ -915,6 +827,7 @@ onActivated(() => {
.home-search-overlay-field :deep(.v-field) { .home-search-overlay-field :deep(.v-field) {
background-color: #f8fafc; background-color: #f8fafc;
} }
.home-search-overlay-field :deep(.v-field__prepend-inner .v-icon) { .home-search-overlay-field :deep(.v-field__prepend-inner .v-icon) {
font-size: 22px; font-size: 22px;
} }
@ -1011,6 +924,10 @@ onActivated(() => {
margin-left: calc(50% - 50vw); margin-left: calc(50% - 50vw);
margin-bottom: 0; margin-bottom: 0;
background-color: white; background-color: white;
@include gt1024 {
display: none;
}
} }
.home-category-layer { .home-category-layer {
@ -1043,6 +960,7 @@ onActivated(() => {
} }
.home-category-icon-item { .home-category-icon-item {
padding: 0 8px 0 8px;
flex-shrink: 0; flex-shrink: 0;
} }
@ -1077,13 +995,7 @@ onActivated(() => {
.home-tab-bar { .home-tab-bar {
position: relative; position: relative;
top: auto;
left: auto;
transform: none; transform: none;
width: 100%;
background-color: transparent;
margin-bottom: 0;
box-shadow: none;
} }
.home-tab-bar :deep(.v-tab__slider), .home-tab-bar :deep(.v-tab__slider),
@ -1109,6 +1021,7 @@ onActivated(() => {
.home-tab-bar :deep(.v-btn__underlay) { .home-tab-bar :deep(.v-btn__underlay) {
display: none !important; display: none !important;
} }
.home-tab-bar :deep(.v-tab:hover) { .home-tab-bar :deep(.v-tab:hover) {
background-color: transparent !important; background-color: transparent !important;
} }
@ -1131,10 +1044,12 @@ onActivated(() => {
min-height: 28px !important; min-height: 28px !important;
height: 28px !important; height: 28px !important;
} }
.home-tab-bar--compact :deep(.v-slide-group__container) { .home-tab-bar--compact :deep(.v-slide-group__container) {
min-height: 28px !important; min-height: 28px !important;
height: 28px !important; height: 28px !important;
} }
.home-tab-bar--compact :deep(.v-slide-group__content) { .home-tab-bar--compact :deep(.v-slide-group__content) {
align-items: center; align-items: center;
} }
@ -1172,6 +1087,7 @@ onActivated(() => {
/* 页面布局flex 列 */ /* 页面布局flex 列 */
.home-page { .home-page {
margin-top: 112px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 100vh; min-height: 100vh;

View File

@ -29,6 +29,17 @@ export default defineConfig({
define: { define: {
'process.env': {}, 'process.env': {},
}, },
css: {
preprocessorOptions: {
scss: {
// 全局注入变量 + 混入
additionalData: `
@import "@/assets/styles/variables.scss";
@import "@/assets/styles/mixin.scss";
`
}
}
},
build: { build: {
target: 'es2020', target: 'es2020',
cssTarget: 'chrome64', cssTarget: 'chrome64',