Compare commits
No commits in common. "67d9c204dd93d89072f5318f3dc9b105bb025c9e" and "970d688dd44c2e43d4eaa6e270993f56399ebaba" have entirely different histories.
67d9c204dd
...
970d688dd4
@ -54,8 +54,6 @@
|
||||
"oxlint": "~1.42.0",
|
||||
"postcss": "^8.5.8",
|
||||
"prettier": "3.8.1",
|
||||
"sass": "^1.99.0",
|
||||
"sass-loader": "^16.0.8",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-node-polyfills": "^0.25.0",
|
||||
|
||||
309
pnpm-lock.yaml
generated
309
pnpm-lock.yaml
generated
@ -56,13 +56,13 @@ importers:
|
||||
version: 24.10.13
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^6.0.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))
|
||||
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))
|
||||
'@vitejs/plugin-vue-jsx':
|
||||
specifier: ^5.1.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))
|
||||
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))
|
||||
'@vitest/eslint-plugin':
|
||||
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))(sass@1.99.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))(yaml@2.8.2))
|
||||
'@vue/eslint-config-typescript':
|
||||
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)
|
||||
@ -111,27 +111,21 @@ importers:
|
||||
prettier:
|
||||
specifier: 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:
|
||||
specifier: ~5.9.3
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: ^7.3.1
|
||||
version: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
|
||||
version: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)
|
||||
vite-plugin-node-polyfills:
|
||||
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)(sass@1.99.0)(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)(yaml@2.8.2))
|
||||
vite-plugin-vue-devtools:
|
||||
specifier: ^8.0.5
|
||||
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))
|
||||
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))
|
||||
vitest:
|
||||
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))(sass@1.99.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))(yaml@2.8.2)
|
||||
vue-tsc:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(typescript@5.9.3)
|
||||
@ -652,94 +646,6 @@ packages:
|
||||
cpu: [x64]
|
||||
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':
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
@ -1404,10 +1310,6 @@ packages:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chokidar@4.0.3:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
|
||||
chokidar@5.0.0:
|
||||
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
@ -1533,10 +1435,6 @@ packages:
|
||||
des.js@1.1.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
|
||||
|
||||
@ -1888,9 +1786,6 @@ packages:
|
||||
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
immutable@5.1.5:
|
||||
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
||||
engines: {node: '>=6'}
|
||||
@ -2162,12 +2057,6 @@ packages:
|
||||
natural-compare@1.4.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
|
||||
|
||||
@ -2414,10 +2303,6 @@ packages:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
readdirp@4.1.2:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
|
||||
readdirp@5.0.0:
|
||||
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
@ -2468,32 +2353,6 @@ packages:
|
||||
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
||||
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:
|
||||
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
|
||||
engines: {node: '>=v12.22.7'}
|
||||
@ -3495,67 +3354,6 @@ snapshots:
|
||||
'@oxlint/win32-x64@1.42.0':
|
||||
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':
|
||||
optional: true
|
||||
|
||||
@ -3802,32 +3600,32 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.56.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)(sass@1.99.0)(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)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@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)
|
||||
'@rolldown/pluginutils': 1.0.0-rc.4
|
||||
'@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)(sass@1.99.0)(yaml@2.8.2)
|
||||
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)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@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))':
|
||||
'@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))':
|
||||
dependencies:
|
||||
'@rolldown/pluginutils': 1.0.0-rc.2
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
|
||||
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)
|
||||
|
||||
'@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))':
|
||||
'@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))':
|
||||
dependencies:
|
||||
'@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)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
optionalDependencies:
|
||||
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)
|
||||
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)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -3840,13 +3638,13 @@ snapshots:
|
||||
chai: 6.2.2
|
||||
tinyrainbow: 3.0.3
|
||||
|
||||
'@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/mocker@4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@vitest/spy': 4.0.18
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)
|
||||
|
||||
'@vitest/pretty-format@4.0.18':
|
||||
dependencies:
|
||||
@ -3990,14 +3788,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@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)(sass@1.99.0)(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)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@vue/devtools-kit': 8.0.6
|
||||
'@vue/devtools-shared': 8.0.6
|
||||
mitt: 3.0.1
|
||||
nanoid: 5.1.6
|
||||
pathe: 2.0.3
|
||||
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))
|
||||
vue: 3.5.28(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- vite
|
||||
@ -4300,10 +4098,6 @@ snapshots:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
chokidar@4.0.3:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
|
||||
chokidar@5.0.0:
|
||||
dependencies:
|
||||
readdirp: 5.0.0
|
||||
@ -4445,9 +4239,6 @@ snapshots:
|
||||
inherits: 2.0.4
|
||||
minimalistic-assert: 1.0.1
|
||||
|
||||
detect-libc@2.1.2:
|
||||
optional: true
|
||||
|
||||
diffie-hellman@5.0.3:
|
||||
dependencies:
|
||||
bn.js: 4.12.2
|
||||
@ -4852,8 +4643,6 @@ snapshots:
|
||||
|
||||
ignore@7.0.5: {}
|
||||
|
||||
immutable@5.1.5: {}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
dependencies:
|
||||
parent-module: 1.0.1
|
||||
@ -5106,11 +4895,6 @@ snapshots:
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
||||
node-releases@2.0.27: {}
|
||||
|
||||
node-stdlib-browser@1.3.1:
|
||||
@ -5392,8 +5176,6 @@ snapshots:
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
readdirp@5.0.0: {}
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
@ -5462,20 +5244,6 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
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:
|
||||
dependencies:
|
||||
xmlchars: 2.2.0
|
||||
@ -5731,17 +5499,17 @@ snapshots:
|
||||
is-typed-array: 1.1.15
|
||||
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)(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)):
|
||||
dependencies:
|
||||
birpc: 2.9.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)(sass@1.99.0)(yaml@2.8.2))
|
||||
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)(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)):
|
||||
dependencies:
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
|
||||
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-inspect@11.3.3(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)):
|
||||
dependencies:
|
||||
ansis: 4.2.0
|
||||
debug: 4.4.3
|
||||
@ -5751,34 +5519,34 @@ snapshots:
|
||||
perfect-debounce: 2.1.0
|
||||
sirv: 3.0.2
|
||||
unplugin-utils: 0.3.1
|
||||
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)(sass@1.99.0)(yaml@2.8.2))
|
||||
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)(yaml@2.8.2))
|
||||
transitivePeerDependencies:
|
||||
- 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)(sass@1.99.0)(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)(yaml@2.8.2)):
|
||||
dependencies:
|
||||
'@rollup/plugin-inject': 5.0.5(rollup@4.57.1)
|
||||
node-stdlib-browser: 1.3.1
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
|
||||
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)):
|
||||
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)):
|
||||
dependencies:
|
||||
'@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-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-kit': 8.0.6
|
||||
'@vue/devtools-shared': 8.0.6
|
||||
sirv: 3.0.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)(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)(sass@1.99.0)(yaml@2.8.2))
|
||||
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)(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))
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
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)):
|
||||
vite-plugin-vue-inspector@5.3.2(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)):
|
||||
dependencies:
|
||||
'@babel/core': 7.29.0
|
||||
'@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0)
|
||||
@ -5789,11 +5557,11 @@ snapshots:
|
||||
'@vue/compiler-dom': 3.5.28
|
||||
kolorist: 1.8.0
|
||||
magic-string: 0.30.21
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2):
|
||||
vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2):
|
||||
dependencies:
|
||||
esbuild: 0.27.3
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@ -5805,13 +5573,12 @@ snapshots:
|
||||
'@types/node': 24.10.13
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.6.1
|
||||
sass: 1.99.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):
|
||||
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):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.0.18
|
||||
'@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/mocker': 4.0.18(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2))
|
||||
'@vitest/pretty-format': 4.0.18
|
||||
'@vitest/runner': 4.0.18
|
||||
'@vitest/snapshot': 4.0.18
|
||||
@ -5828,7 +5595,7 @@ snapshots:
|
||||
tinyexec: 1.0.2
|
||||
tinyglobby: 0.2.15
|
||||
tinyrainbow: 3.0.3
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(sass@1.99.0)(yaml@2.8.2)
|
||||
vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(yaml@2.8.2)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 24.10.13
|
||||
|
||||
393
src/App.vue
393
src/App.vue
@ -5,9 +5,6 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useUserStore } from './stores/user'
|
||||
import { useLocaleStore } from './stores/locale'
|
||||
import { useMenuStore } from './stores/menu'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useSearchHistory } from './composables/useSearchHistory'
|
||||
import type { LocaleCode } from './plugins/i18n'
|
||||
import Toast from './components/Toast.vue'
|
||||
|
||||
@ -16,16 +13,8 @@ const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const localeStore = useLocaleStore()
|
||||
const menuStore = useMenuStore()
|
||||
const localeMenuOpen = ref(false)
|
||||
|
||||
const { categoryLayers, layerActiveValues } = storeToRefs(menuStore)
|
||||
const searchHistory = useSearchHistory()
|
||||
const searchHistoryList = computed(() => searchHistory.list.value)
|
||||
const searchExpanded = ref(false)
|
||||
const searchKeyword = ref('')
|
||||
const searchInputRef = ref<{ focus: () => void } | null>(null)
|
||||
|
||||
function chooseLocale(loc: LocaleCode) {
|
||||
localeStore.setLocale(loc)
|
||||
localeMenuOpen.value = false
|
||||
@ -72,39 +61,6 @@ async function refreshUserData() {
|
||||
await userStore.fetchPositionsValue()
|
||||
}
|
||||
|
||||
function expandSearch() {
|
||||
searchExpanded.value = true
|
||||
nextTick(() => {
|
||||
searchInputRef.value?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
function collapseSearch() {
|
||||
searchExpanded.value = false
|
||||
searchKeyword.value = ''
|
||||
}
|
||||
|
||||
function onSearchBlur() {
|
||||
setTimeout(() => {
|
||||
collapseSearch()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function onSearchSubmit() {
|
||||
const kw = searchKeyword.value.trim()
|
||||
if (kw) {
|
||||
searchHistory.add(kw)
|
||||
// 触发搜索事件的逻辑,可以利用 store 或者 router 跳转
|
||||
router.push({ path: '/', query: { q: kw } })
|
||||
}
|
||||
collapseSearch()
|
||||
}
|
||||
|
||||
function selectHistoryItem(item: string) {
|
||||
searchKeyword.value = item
|
||||
onSearchSubmit()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refreshUserData()
|
||||
})
|
||||
@ -119,109 +75,90 @@ watch(
|
||||
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar color="surface" elevation="0" height="112">
|
||||
<div class="flex-column header-content">
|
||||
<div class="app-bar-inner">
|
||||
<v-btn v-if="currentRoute !== '/'" icon variant="text" class="back-btn" :aria-label="t('common.back')"
|
||||
@click="onBackClick">
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<v-app-bar-title v-if="currentRoute === '/'" class="brand-title">
|
||||
<div class="brand-lockup">
|
||||
<div class="brand-mark-wrap">
|
||||
<img src="/brand-logo.svg?v=2" alt="Alpha Market logo" class="brand-logo" />
|
||||
</div>
|
||||
<div class="brand-copy">
|
||||
<span class="brand-kicker">Prediction Markets</span>
|
||||
<span class="brand-name">Alpha Market</span>
|
||||
</div>
|
||||
<v-app-bar color="surface" elevation="0">
|
||||
<div class="app-bar-inner">
|
||||
<v-btn
|
||||
v-if="currentRoute !== '/'"
|
||||
icon
|
||||
variant="text"
|
||||
class="back-btn"
|
||||
:aria-label="t('common.back')"
|
||||
@click="onBackClick"
|
||||
>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<v-app-bar-title v-if="currentRoute === '/'" class="brand-title">
|
||||
<div class="brand-lockup">
|
||||
<div class="brand-mark-wrap">
|
||||
<img src="/brand-logo.svg?v=2" alt="Alpha Market logo" class="brand-logo" />
|
||||
</div>
|
||||
<div class="brand-copy">
|
||||
<span class="brand-kicker">Prediction Markets</span>
|
||||
<span class="brand-name">Alpha Market</span>
|
||||
</div>
|
||||
</v-app-bar-title>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<template v-if="!userStore.isLoggedIn">
|
||||
<v-menu v-model="localeMenuOpen" :close-on-content-click="true" location="bottom"
|
||||
transition="scale-transition">
|
||||
<template #activator="{ props }">
|
||||
<v-btn icon variant="text" class="locale-btn" :aria-label="t('profile.selectLanguage')" v-bind="props">
|
||||
<v-icon>mdi-earth</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item v-for="opt in localeStore.localeOptions" :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>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-btn text to="/login" :class="{ active: currentRoute === '/login' }">
|
||||
{{ t('common.login') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-btn class="balance-btn" variant="text" min-width="auto" padding="4 12" @click="$router.push('/wallet')">
|
||||
<span class="balance-text">${{ userStore.totalAssetValue }}</span>
|
||||
</v-btn>
|
||||
<v-btn icon variant="text" class="avatar-btn" :aria-label="t('common.user')"
|
||||
@click="$router.push('/profile')">
|
||||
<v-avatar size="36" color="primary">
|
||||
<v-img v-if="userStore.avatarUrl" :src="userStore.avatarUrl" cover alt="avatar" />
|
||||
<v-icon v-else>mdi-account</v-icon>
|
||||
</v-avatar>
|
||||
</v-btn>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 提取的顶部菜单栏与搜索功能 -->
|
||||
<div v-if="currentRoute === '/' && categoryLayers.length > 0" class="home-category-layer1-wrap">
|
||||
<div class="home-category-layer1-row">
|
||||
<v-tabs :model-value="layerActiveValues[0]" class="home-tab-bar home-tab-bar--inline" height="48"
|
||||
@update:model-value="menuStore.onCategorySelect(0, $event)">
|
||||
<v-tab v-for="item in categoryLayers[0]" :key="item.id" :value="item.id" :ripple="false" >
|
||||
{{ item.label }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-btn icon variant="text" class="home-search-btn" :aria-label="t('common.search')" @click="expandSearch">
|
||||
<v-icon size="24">mdi-magnify</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<!-- 搜索展开时:浮层输入框 + 历史记录 -->
|
||||
<transition name="home-search-overlay">
|
||||
<div v-if="searchExpanded" class="home-search-overlay">
|
||||
<div class="home-search-overlay-inner">
|
||||
<v-text-field ref="searchInputRef" v-model="searchKeyword" density="compact" hide-details
|
||||
: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-btn>
|
||||
</div>
|
||||
<div v-if="searchHistoryList.length > 0" class="home-search-history">
|
||||
<div class="home-search-history-header">
|
||||
<span class="home-search-history-title">{{ t('home.searchHistory') }}</span>
|
||||
<v-btn variant="text" size="x-small" color="primary" class="home-search-history-clear"
|
||||
@click="searchHistory.clearAll">
|
||||
{{ t('common.clear') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
<ul class="home-search-history-list">
|
||||
<li v-for="(item, idx) in searchHistoryList" :key="`${item}-${idx}`" class="home-search-history-item">
|
||||
<button type="button" class="home-search-history-text" @click="selectHistoryItem(item)">
|
||||
{{ item }}
|
||||
</button>
|
||||
<v-btn icon 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-btn>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</v-app-bar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<template v-if="!userStore.isLoggedIn">
|
||||
<v-menu
|
||||
v-model="localeMenuOpen"
|
||||
:close-on-content-click="true"
|
||||
location="bottom"
|
||||
transition="scale-transition"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
icon
|
||||
variant="text"
|
||||
class="locale-btn"
|
||||
:aria-label="t('profile.selectLanguage')"
|
||||
v-bind="props"
|
||||
>
|
||||
<v-icon>mdi-earth</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item
|
||||
v-for="opt in localeStore.localeOptions"
|
||||
: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>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-btn
|
||||
text
|
||||
to="/login"
|
||||
:class="{ active: currentRoute === '/login' }"
|
||||
>
|
||||
{{ t('common.login') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-btn
|
||||
class="balance-btn"
|
||||
variant="text"
|
||||
min-width="auto"
|
||||
padding="4 12"
|
||||
@click="$router.push('/wallet')"
|
||||
>
|
||||
<span class="balance-text">${{ userStore.totalAssetValue }}</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
variant="text"
|
||||
class="avatar-btn"
|
||||
:aria-label="t('common.user')"
|
||||
@click="$router.push('/profile')"
|
||||
>
|
||||
<v-avatar size="36" color="primary">
|
||||
<v-img v-if="userStore.avatarUrl" :src="userStore.avatarUrl" cover alt="avatar" />
|
||||
<v-icon v-else>mdi-account</v-icon>
|
||||
</v-avatar>
|
||||
</v-btn>
|
||||
</template>
|
||||
</div>
|
||||
</v-app-bar>
|
||||
<v-main class="app-main">
|
||||
@ -236,7 +173,13 @@ watch(
|
||||
</div>
|
||||
</v-main>
|
||||
|
||||
<v-bottom-navigation v-if="showBottomNav" v-model="bottomNavValue" app height="56" elevation="0">
|
||||
<v-bottom-navigation
|
||||
v-if="showBottomNav"
|
||||
v-model="bottomNavValue"
|
||||
app
|
||||
height="56"
|
||||
elevation="0"
|
||||
>
|
||||
<v-btn value="/" :ripple="false">
|
||||
<v-icon size="24">mdi-home-outline</v-icon>
|
||||
<span>{{ t('nav.home') }}</span>
|
||||
@ -259,15 +202,9 @@ watch(
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.header-content {
|
||||
width: 100%;
|
||||
max-width: 1440px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
height: 112px;
|
||||
}
|
||||
|
||||
<style scoped>
|
||||
.app-bar-inner {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@ -344,15 +281,15 @@ watch(
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.app-main-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -415,12 +352,10 @@ watch(
|
||||
background: #fff !important;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* 底部导航:三个入口等分屏幕宽度 */
|
||||
:deep(.v-bottom-navigation__content) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.v-bottom-navigation__content > .v-btn) {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
@ -430,7 +365,6 @@ watch(
|
||||
:deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active)) {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
:deep(.v-bottom-navigation__content > .v-btn:not(.v-btn--selected):not(.v-btn--active) .v-icon) {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
@ -441,7 +375,6 @@ watch(
|
||||
font-weight: 700;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
:deep(.v-bottom-navigation__content > .v-btn .v-btn__overlay),
|
||||
:deep(.v-bottom-navigation__content > .v-btn .v-btn__underlay) {
|
||||
display: none !important;
|
||||
@ -451,138 +384,18 @@ watch(
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 第一层:置顶、全宽;sticky 参照 app-main-scroll,top:0 贴容器顶(即 app-bar 下方) */
|
||||
.home-category-layer1-wrap {
|
||||
width: 100vw;
|
||||
background-color: white;
|
||||
/* 全局:禁止 body 滚动,由 app-main-scroll 内部滚动,滚动条不覆盖底部导航 */
|
||||
:global(html),
|
||||
:global(body) {
|
||||
background: rgb(252, 252, 252);
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.home-category-layer1-row {
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.home-tab-bar--inline {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.home-tab-bar--inline :deep(.v-tabs) {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.home-tab-bar--inline :deep(.v-tab) {
|
||||
height: 48px;
|
||||
padding: 0 8px 0 8px;
|
||||
}
|
||||
|
||||
.home-category-layer1-actions {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.home-search-btn {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-search-overlay-enter-active,
|
||||
.home-search-overlay-leave-active {
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.home-search-overlay-enter-from,
|
||||
.home-search-overlay-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
.v-tabs-height {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.home-search-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
z-index: 2000;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
padding: 8px;
|
||||
:global(.v-application) {
|
||||
background: rgb(252, 252, 252);
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.home-search-overlay-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.home-search-overlay-field {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.home-search-history {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.home-search-history-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.home-search-history-title {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.home-search-history-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.home-search-history-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.home-search-history-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.home-search-history-text {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
font-size: 0.9rem;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.home-category-layer1-wrap {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
.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; }
|
||||
@ -1,93 +0,0 @@
|
||||
// // 水平垂直居中
|
||||
// @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;
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
/* 全局:禁止 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;
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
// 主题色
|
||||
$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);
|
||||
@ -8,10 +8,6 @@ import { i18n } from './plugins/i18n'
|
||||
import { setHttpUnauthorizedHandler } from './api/request'
|
||||
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 pinia = createPinia()
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { CategoryTreeNode } from '@/api/category'
|
||||
|
||||
export const useMenuStore = defineStore('menu', () => {
|
||||
/** 分类树(顶层) */
|
||||
const categoryTree = ref<CategoryTreeNode[]>([])
|
||||
|
||||
/** 每层选中的 id,[layer0, layer1?, layer2?] */
|
||||
const layerActiveValues = ref<string[]>([])
|
||||
|
||||
/** 过滤:仅当 forceShow 明确为 false 时不显示,其他(null/undefined/true 等)均显示;排除 forceHide 的节点 */
|
||||
function filterVisible(nodes: CategoryTreeNode[] | undefined): CategoryTreeNode[] {
|
||||
if (!nodes?.length) return []
|
||||
return nodes.filter((n) => n.forceShow !== false && !n.forceHide)
|
||||
}
|
||||
|
||||
/** 当前展示的层级数据:[[layer0], [layer1]?, [layer2]?] */
|
||||
const categoryLayers = computed(() => {
|
||||
const root = filterVisible(categoryTree.value)
|
||||
if (root.length === 0) return []
|
||||
|
||||
const layers: CategoryTreeNode[][] = [root]
|
||||
const active = layerActiveValues.value
|
||||
|
||||
let currentNodes = root
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const selectedId = active[i]
|
||||
const node = selectedId ? currentNodes.find((n) => n.id === selectedId) : currentNodes[0]
|
||||
const children = filterVisible(node?.children)
|
||||
if (children.length === 0) break
|
||||
layers.push(children)
|
||||
currentNodes = children
|
||||
}
|
||||
return layers
|
||||
})
|
||||
|
||||
/** 分类切换时的防抖状态,防止重复请求 */
|
||||
let categorySelectTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
/** 触发事件加载的回调函数(由 Home 页面注册) */
|
||||
const loadEventsCallback = ref<(() => void) | null>(null)
|
||||
|
||||
/** 触发清除事件列表缓存的回调函数(由 Home 页面注册) */
|
||||
const clearCacheCallback = ref<(() => void) | null>(null)
|
||||
|
||||
/** 分类选中时:若有 children 则展开下一层并默认选中第一个,并重新加载列表 */
|
||||
function onCategorySelect(layerIndex: number, selectedId: string) {
|
||||
if (!selectedId || layerActiveValues.value[layerIndex] === selectedId) {
|
||||
return
|
||||
}
|
||||
|
||||
if (categorySelectTimer) {
|
||||
clearTimeout(categorySelectTimer)
|
||||
}
|
||||
|
||||
const nextValues = [...layerActiveValues.value]
|
||||
nextValues[layerIndex] = selectedId
|
||||
nextValues.length = layerIndex + 1
|
||||
|
||||
const layers = categoryLayers.value
|
||||
const layer = layers[layerIndex]
|
||||
const node = layer?.find((n) => n.id === selectedId)
|
||||
const children = filterVisible(node?.children)
|
||||
|
||||
const firstChild = children[0]
|
||||
if (firstChild && layerIndex < 2) {
|
||||
nextValues.push(firstChild.id)
|
||||
}
|
||||
layerActiveValues.value = nextValues
|
||||
|
||||
if (clearCacheCallback.value) {
|
||||
clearCacheCallback.value()
|
||||
}
|
||||
|
||||
categorySelectTimer = setTimeout(() => {
|
||||
categorySelectTimer = null
|
||||
if (loadEventsCallback.value) {
|
||||
loadEventsCallback.value()
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
return {
|
||||
categoryTree,
|
||||
layerActiveValues,
|
||||
categoryLayers,
|
||||
filterVisible,
|
||||
onCategorySelect,
|
||||
loadEventsCallback,
|
||||
clearCacheCallback
|
||||
}
|
||||
})
|
||||
@ -1,22 +1,120 @@
|
||||
<template>
|
||||
<div class="home-page">
|
||||
<!-- 第一层:单行紧凑布局,tabs + 搜索/筛选图标 -->
|
||||
<div v-if="categoryLayers.length > 0" class="home-category-layer1-wrap">
|
||||
<div class="home-category-layer1-row">
|
||||
<v-tabs
|
||||
:model-value="layerActiveValues[0]"
|
||||
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">
|
||||
{{ item.label }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
</div>
|
||||
<!-- 搜索展开时:浮层输入框 + 历史记录 -->
|
||||
<transition name="home-search-overlay">
|
||||
<div v-if="searchExpanded" class="home-search-overlay">
|
||||
<div class="home-search-overlay-inner">
|
||||
<v-text-field
|
||||
ref="searchInputRef"
|
||||
v-model="searchKeyword"
|
||||
density="compact"
|
||||
hide-details
|
||||
: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-btn>
|
||||
</div>
|
||||
<div v-if="searchHistoryList.length > 0" class="home-search-history">
|
||||
<div class="home-search-history-header">
|
||||
<span class="home-search-history-title">{{ t('home.searchHistory') }}</span>
|
||||
<v-btn
|
||||
variant="text"
|
||||
size="x-small"
|
||||
color="primary"
|
||||
class="home-search-history-clear"
|
||||
@click="searchHistory.clearAll"
|
||||
>
|
||||
{{ t('common.clear') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
<ul class="home-search-history-list">
|
||||
<li
|
||||
v-for="(item, idx) in searchHistoryList"
|
||||
:key="`${item}-${idx}`"
|
||||
class="home-search-history-item"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="home-search-history-text"
|
||||
@click="selectHistoryItem(item)"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
<v-btn
|
||||
icon
|
||||
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-btn>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<v-container fluid class="home-container">
|
||||
<!-- 第二三层:随内容滚动,回顶部时与列表第一行一起出现 -->
|
||||
<div v-if="categoryLayers.length > 1" 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-icon-row">
|
||||
<v-chip v-for="item in categoryLayers[1]" :key="item.id" class="home-category-icon-item"
|
||||
<v-chip
|
||||
v-for="item in categoryLayers[1]"
|
||||
:key="item.id"
|
||||
class="home-category-icon-item"
|
||||
:color="layerActiveValues[1] === item.id ? 'primary' : undefined"
|
||||
:variant="layerActiveValues[1] === item.id ? 'tonal' : 'outlined'" size="small"
|
||||
@click="menuStore.onCategorySelect(1, item.id)">
|
||||
:variant="layerActiveValues[1] === item.id ? 'tonal' : 'outlined'"
|
||||
size="small"
|
||||
@click="onCategorySelect(1, item.id)"
|
||||
>
|
||||
<span class="home-category-icon-label">{{ item.label }}</span>
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="categoryLayers.length >= 3" class="home-category-layer home-category-layer--third">
|
||||
<v-tabs :model-value="layerActiveValues[2]" class="home-tab-bar home-tab-bar--compact"
|
||||
@update:model-value="menuStore.onCategorySelect(2, $event)">
|
||||
<v-tab v-for="item in categoryLayers[2]" :key="item.id" :value="item.id" :ripple="false">
|
||||
<div
|
||||
v-if="categoryLayers.length >= 3"
|
||||
class="home-category-layer home-category-layer--third"
|
||||
>
|
||||
<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 }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
@ -27,13 +125,28 @@
|
||||
<v-pull-to-refresh class="pull-to-refresh" @load="onRefresh">
|
||||
<div class="pull-to-refresh-inner">
|
||||
<div ref="listRef" class="home-list" :style="gridListStyle">
|
||||
<MarketCard v-for="card in eventList" :key="card.id" :id="card.id" :slug="card.slug"
|
||||
:market-title="card.marketTitle" :chance-value="card.chanceValue" :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" />
|
||||
<MarketCard
|
||||
v-for="card in eventList"
|
||||
:key="card.id"
|
||||
:id="card.id"
|
||||
:slug="card.slug"
|
||||
:market-title="card.marketTitle"
|
||||
:chance-value="card.chanceValue"
|
||||
: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">
|
||||
<v-progress-circular indeterminate size="40" width="2" />
|
||||
<span>{{ t('common.loading') }}</span>
|
||||
@ -49,8 +162,14 @@
|
||||
<span>{{ t('common.loading') }}</span>
|
||||
</div>
|
||||
<div v-else-if="noMoreEvents" class="no-more-tip">{{ t('home.noMore') }}</div>
|
||||
<v-btn v-else class="load-more-btn" variant="outlined" color="primary" :disabled="loadingMore"
|
||||
@click="loadMore">
|
||||
<v-btn
|
||||
v-else
|
||||
class="load-more-btn"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
:disabled="loadingMore"
|
||||
@click="loadMore"
|
||||
>
|
||||
{{ t('home.loadMore') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
@ -59,28 +178,43 @@
|
||||
</div>
|
||||
|
||||
<!-- PC:对话框;手机:底部 sheet,直接显示交易表单 -->
|
||||
<v-dialog v-if="!isMobile" v-model="tradeDialogOpen" max-width="420" scrollable
|
||||
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-if="!isMobile"
|
||||
v-model="tradeDialogOpen"
|
||||
max-width="420"
|
||||
scrollable
|
||||
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-bottom-sheet v-else v-model="tradeDialogOpen" content-class="trade-bottom-sheet">
|
||||
<TradeComponent :key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`" :market="homeTradeMarketPayload"
|
||||
:initial-option="tradeDialogSide" embedded-in-sheet @order-success="onOrderSuccess" />
|
||||
<TradeComponent
|
||||
:key="`trade-${tradeDialogMarket?.id}-${tradeDialogSide}`"
|
||||
:market="homeTradeMarketPayload"
|
||||
:initial-option="tradeDialogSide"
|
||||
embedded-in-sheet
|
||||
@order-success="onOrderSuccess"
|
||||
/>
|
||||
</v-bottom-sheet>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// const props = withDefaults(
|
||||
// defineProps<{
|
||||
// /** 进入页面时是否自动展开搜索(供 /search 路由使用) */
|
||||
// initialSearchExpanded?: boolean
|
||||
// }>(),
|
||||
// { initialSearchExpanded: false },
|
||||
// )
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 进入页面时是否自动展开搜索(供 /search 路由使用) */
|
||||
initialSearchExpanded?: boolean
|
||||
}>(),
|
||||
{ initialSearchExpanded: false },
|
||||
)
|
||||
|
||||
defineOptions({ name: 'HomePage' })
|
||||
import {
|
||||
@ -110,23 +244,68 @@ import {
|
||||
enrichWithIcons,
|
||||
getPmTagMain,
|
||||
MOCK_CATEGORY_TREE,
|
||||
type CategoryTreeNode,
|
||||
} from '../api/category'
|
||||
import { USE_MOCK_CATEGORY } from '../config/mock'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useSearchHistory } from '../composables/useSearchHistory'
|
||||
import { useToastStore } from '../stores/toast'
|
||||
import { useLocaleStore } from '../stores/locale'
|
||||
import { useMenuStore } from '../stores/menu'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const { mobile } = useDisplay()
|
||||
const { t } = useI18n()
|
||||
const searchHistory = useSearchHistory()
|
||||
const searchHistoryList = computed(() => searchHistory.list.value)
|
||||
const isMobile = computed(() => mobile.value)
|
||||
|
||||
const menuStore = useMenuStore()
|
||||
const { categoryTree, layerActiveValues, categoryLayers } = storeToRefs(menuStore)
|
||||
const { filterVisible } = menuStore
|
||||
/** 分类树(顶层) */
|
||||
const categoryTree = ref<CategoryTreeNode[]>([])
|
||||
/** 每层选中的 id,[layer0, layer1?, layer2?] */
|
||||
const layerActiveValues = ref<string[]>([])
|
||||
/** 第三层搜索框是否展开 */
|
||||
const searchExpanded = ref(false)
|
||||
/** 搜索关键词 */
|
||||
const searchKeyword = ref('')
|
||||
const searchInputRef = ref<{ focus: () => void } | null>(null)
|
||||
|
||||
function expandSearch() {
|
||||
searchExpanded.value = true
|
||||
nextTick(() => {
|
||||
const el = searchInputRef.value as { focus?: () => void } | null
|
||||
el?.focus?.()
|
||||
})
|
||||
}
|
||||
|
||||
function collapseSearch() {
|
||||
searchExpanded.value = false
|
||||
searchKeyword.value = ''
|
||||
if (activeSearchKeyword.value) {
|
||||
activeSearchKeyword.value = ''
|
||||
clearEventListCache()
|
||||
eventPage.value = 1
|
||||
loadEvents(1, false, '')
|
||||
}
|
||||
}
|
||||
|
||||
function onSearchBlur() {
|
||||
setTimeout(() => {
|
||||
if (!searchKeyword.value.trim()) searchExpanded.value = false
|
||||
}, 100)
|
||||
}
|
||||
|
||||
function onSearchSubmit() {
|
||||
const k = searchKeyword.value.trim()
|
||||
if (k) {
|
||||
searchHistory.add(k)
|
||||
doSearch(k)
|
||||
}
|
||||
}
|
||||
|
||||
function selectHistoryItem(item: string) {
|
||||
searchKeyword.value = item
|
||||
searchHistory.add(item)
|
||||
doSearch(item)
|
||||
}
|
||||
|
||||
function doSearch(keyword: string) {
|
||||
activeSearchKeyword.value = keyword
|
||||
@ -135,6 +314,12 @@ function doSearch(keyword: string) {
|
||||
loadEvents(1, false, keyword)
|
||||
}
|
||||
|
||||
/** 过滤:仅当 forceShow 明确为 false 时不显示,其他(null/undefined/true 等)均显示;排除 forceHide 的节点 */
|
||||
function filterVisible(nodes: CategoryTreeNode[] | undefined): CategoryTreeNode[] {
|
||||
if (!nodes?.length) return []
|
||||
return nodes.filter((n) => n.forceShow !== false && !n.forceHide)
|
||||
}
|
||||
|
||||
/** 当前选中分类的 tagIds:收集所有选中层级节点的 tagId 数组(含父级),用于事件筛选 */
|
||||
const activeTagIds = computed(() => {
|
||||
const activeIds = layerActiveValues.value
|
||||
@ -159,6 +344,59 @@ const activeTagIds = computed(() => {
|
||||
return Array.from(tagIdSet)
|
||||
})
|
||||
|
||||
/** 当前展示的层级数据:[[layer0], [layer1]?, [layer2]?] */
|
||||
const categoryLayers = computed(() => {
|
||||
const root = filterVisible(categoryTree.value)
|
||||
if (root.length === 0) return []
|
||||
|
||||
const layers: CategoryTreeNode[][] = [root]
|
||||
const active = layerActiveValues.value
|
||||
|
||||
let currentNodes = root
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const selectedId = active[i]
|
||||
const node = selectedId ? currentNodes.find((n) => n.id === selectedId) : currentNodes[0]
|
||||
const children = filterVisible(node?.children)
|
||||
if (children.length === 0) break
|
||||
layers.push(children)
|
||||
currentNodes = children
|
||||
}
|
||||
return layers
|
||||
})
|
||||
|
||||
/** 分类选中时:若有 children 则展开下一层并默认选中第一个,并重新加载列表 */
|
||||
function onCategorySelect(layerIndex: number, selectedId: string) {
|
||||
if (!selectedId || layerActiveValues.value[layerIndex] === selectedId) {
|
||||
return
|
||||
}
|
||||
|
||||
if (categorySelectTimer) {
|
||||
clearTimeout(categorySelectTimer)
|
||||
}
|
||||
|
||||
const nextValues = [...layerActiveValues.value]
|
||||
nextValues[layerIndex] = selectedId
|
||||
nextValues.length = layerIndex + 1
|
||||
|
||||
const layers = categoryLayers.value
|
||||
const layer = layers[layerIndex]
|
||||
const node = layer?.find((n) => n.id === selectedId)
|
||||
const children = filterVisible(node?.children)
|
||||
|
||||
const firstChild = children[0]
|
||||
if (firstChild && layerIndex < 2) {
|
||||
nextValues.push(firstChild.id)
|
||||
}
|
||||
layerActiveValues.value = nextValues
|
||||
|
||||
clearEventListCache()
|
||||
eventPage.value = 1
|
||||
|
||||
categorySelectTimer = setTimeout(() => {
|
||||
categorySelectTimer = null
|
||||
loadEvents(1, false)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
/** 初始化分类选中:默认选中第一个,若有 children 则递归展开 */
|
||||
function initCategorySelection() {
|
||||
@ -181,7 +419,7 @@ function initCategorySelection() {
|
||||
const PAGE_SIZE = 10
|
||||
|
||||
/** 分类切换时的防抖状态,防止重复请求 */
|
||||
|
||||
let categorySelectTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
/** 接口返回的列表(已映射为卡片所需结构) */
|
||||
const eventList = ref<EventCardItem[]>([])
|
||||
@ -333,21 +571,8 @@ function updateGridColumns() {
|
||||
gridColumns.value = Math.max(1, n)
|
||||
}
|
||||
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
/** 当前生效的搜索关键词(用于分页加载) */
|
||||
const activeSearchKeyword = ref((route.query.q as string) || '')
|
||||
|
||||
watch(
|
||||
() => route.query.q,
|
||||
(newQ) => {
|
||||
if (newQ !== undefined) {
|
||||
doSearch(newQ as string)
|
||||
}
|
||||
}
|
||||
)
|
||||
const activeSearchKeyword = ref('')
|
||||
|
||||
/** 请求事件列表并追加或覆盖到 eventList(公开接口,无需鉴权);成功后会更新内存缓存 */
|
||||
async function loadEvents(page: number, append: boolean, keyword?: string) {
|
||||
@ -427,8 +652,7 @@ onBeforeRouteLeave(() => {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 如果有初始化要求,可以调用相关方法或 emit 事件
|
||||
// if (props.initialSearchExpanded) expandSearch()
|
||||
if (props.initialSearchExpanded) expandSearch()
|
||||
loadCategory()
|
||||
nextTick(() => {
|
||||
const scrollEl = getMainScrollEl()
|
||||
@ -494,14 +718,18 @@ onActivated(() => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style scoped>
|
||||
/* fluid 后无断点 max-width,用自定义 max-width 让列表在 2137px 等宽屏下能算到 6 列 */
|
||||
.home-container {
|
||||
flex: 1 1 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
margin: 112px 0 0 0;
|
||||
min-height: 100vh;
|
||||
max-width: 2560px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 0 8px !important;
|
||||
}
|
||||
|
||||
.home-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.home-title {
|
||||
@ -551,7 +779,7 @@ onActivated(() => {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.home-list>* {
|
||||
.home-list > * {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
@ -595,7 +823,6 @@ onActivated(() => {
|
||||
padding: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.trade-dialog--bare :deep(.v-card) {
|
||||
box-shadow: none;
|
||||
}
|
||||
@ -618,6 +845,164 @@ onActivated(() => {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
/* 第一层:置顶、全宽;sticky 参照 app-main-scroll,top:0 贴容器顶(即 app-bar 下方) */
|
||||
.home-category-layer1-wrap {
|
||||
width: 100vw;
|
||||
max-width: 100vw;
|
||||
margin-left: calc(50% - 50vw);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background-color: white;
|
||||
/* box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); */
|
||||
}
|
||||
|
||||
.home-category-layer1-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 48px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.home-tab-bar--inline {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.home-tab-bar--inline :deep(.v-tabs) {
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.home-tab-bar--inline :deep(.v-tab) {
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.home-category-layer1-actions {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* 搜索浮层:绝对定位,遮挡第二三层,不顶开布局 */
|
||||
.home-search-overlay {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 11;
|
||||
padding: 8px 12px 12px;
|
||||
background-color: white;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.home-search-overlay-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.home-search-overlay-field {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.home-search-close-btn {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home-search-overlay-field :deep(.v-field) {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
.home-search-overlay-field :deep(.v-field__prepend-inner .v-icon) {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.home-search-overlay-enter-active,
|
||||
.home-search-overlay-leave-active {
|
||||
transition:
|
||||
opacity 0.15s ease,
|
||||
transform 0.15s ease;
|
||||
}
|
||||
|
||||
.home-search-overlay-enter-from,
|
||||
.home-search-overlay-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.home-search-history {
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.home-search-history-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.home-search-history-title {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.home-search-history-clear {
|
||||
min-width: auto;
|
||||
padding: 0 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.home-search-history-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.home-search-history-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.home-search-history-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.home-search-history-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 14px;
|
||||
color: #1e293b;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.home-search-history-text:hover {
|
||||
color: rgb(var(--v-theme-primary));
|
||||
}
|
||||
|
||||
.home-search-history-delete {
|
||||
flex-shrink: 0;
|
||||
min-width: 28px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.home-search-history-delete:hover {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
/* 第二三层:随内容滚动,全宽 */
|
||||
.home-category-layers-23-scroll {
|
||||
@ -626,10 +1011,6 @@ onActivated(() => {
|
||||
margin-left: calc(50% - 50vw);
|
||||
margin-bottom: 0;
|
||||
background-color: white;
|
||||
|
||||
@include gt1024 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.home-category-layer {
|
||||
@ -662,7 +1043,6 @@ onActivated(() => {
|
||||
}
|
||||
|
||||
.home-category-icon-item {
|
||||
padding: 0 8px 0 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@ -697,7 +1077,13 @@ onActivated(() => {
|
||||
|
||||
.home-tab-bar {
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
transform: none;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.home-tab-bar :deep(.v-tab__slider),
|
||||
@ -723,7 +1109,6 @@ onActivated(() => {
|
||||
.home-tab-bar :deep(.v-btn__underlay) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.home-tab-bar :deep(.v-tab:hover) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
@ -746,12 +1131,10 @@ onActivated(() => {
|
||||
min-height: 28px !important;
|
||||
height: 28px !important;
|
||||
}
|
||||
|
||||
.home-tab-bar--compact :deep(.v-slide-group__container) {
|
||||
min-height: 28px !important;
|
||||
height: 28px !important;
|
||||
}
|
||||
|
||||
.home-tab-bar--compact :deep(.v-slide-group__content) {
|
||||
align-items: center;
|
||||
}
|
||||
@ -787,9 +1170,10 @@ onActivated(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 页面布局:flex 列 */
|
||||
.home-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -29,17 +29,6 @@ export default defineConfig({
|
||||
define: {
|
||||
'process.env': {},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
// 全局注入变量 + 混入
|
||||
additionalData: `
|
||||
@import "@/assets/styles/variables.scss";
|
||||
@import "@/assets/styles/mixin.scss";
|
||||
`
|
||||
}
|
||||
}
|
||||
},
|
||||
build: {
|
||||
target: 'es2020',
|
||||
cssTarget: 'chrome64',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user