From 93e9bb604d4bc3521bc253fb78b1d5f1dae577f2 Mon Sep 17 00:00:00 2001 From: fuenmao <980740792@qq.com> Date: Mon, 21 Sep 2026 15:53:02 +0800 Subject: [PATCH] feat: initialize Evergreen Health mobile app --- .env.development | 2 + .gitignore | 10 + index.html | 13 + package.json | 27 + pnpm-lock.yaml | 1189 ++++++++++++++++++++++++++++++++++ src/App.vue | 3 + src/env.d.ts | 1 + src/main.ts | 8 + src/router.ts | 22 + src/services/auth.ts | 20 + src/services/health.ts | 61 ++ src/services/http.ts | 41 ++ src/styles.css | 724 +++++++++++++++++++++ src/views/AuthView.vue | 114 ++++ src/views/DashboardView.vue | 256 ++++++++ src/views/OnboardingView.vue | 189 ++++++ tsconfig.json | 17 + vite.config.ts | 18 + 18 files changed, 2715 insertions(+) create mode 100644 .env.development create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/App.vue create mode 100644 src/env.d.ts create mode 100644 src/main.ts create mode 100644 src/router.ts create mode 100644 src/services/auth.ts create mode 100644 src/services/health.ts create mode 100644 src/services/http.ts create mode 100644 src/styles.css create mode 100644 src/views/AuthView.vue create mode 100644 src/views/DashboardView.vue create mode 100644 src/views/OnboardingView.vue create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..943ddad --- /dev/null +++ b/.env.development @@ -0,0 +1,2 @@ +# 后端开发地址;通过 Vite 代理访问,避免浏览器跨域。 +VITE_API_BASE_URL=http://localhost:5005 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be0c678 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +.vite/ +*.local +.env.local +.env.*.local +*.log +.DS_Store +Thumbs.db + diff --git a/index.html b/index.html new file mode 100644 index 0000000..f06247f --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + 常青树健康管理 + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..9e98fe7 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "evergreen-health-mobile", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite --host 0.0.0.0", + "build": "vue-tsc --noEmit && vite build", + "preview": "vite preview --host 0.0.0.0" + }, + "dependencies": { + "axios": "1.7.9", + "pinia": "2.3.1", + "vant": "4.9.21", + "vue": "3.5.13", + "vue-router": "4.5.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "5.2.1", + "typescript": "5.7.3", + "vite": "5.4.14", + "vue-tsc": "2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..9fdb489 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1189 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + axios: + specifier: 1.7.9 + version: 1.7.9 + pinia: + specifier: 2.3.1 + version: 2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)) + vant: + specifier: 4.9.21 + version: 4.9.21(vue@3.5.13(typescript@5.7.3)) + vue: + specifier: 3.5.13 + version: 3.5.13(typescript@5.7.3) + vue-router: + specifier: 4.5.0 + version: 4.5.0(vue@3.5.13(typescript@5.7.3)) + devDependencies: + '@vitejs/plugin-vue': + specifier: 5.2.1 + version: 5.2.1(vite@5.4.14)(vue@3.5.13(typescript@5.7.3)) + typescript: + specifier: 5.7.3 + version: 5.7.3 + vite: + specifier: 5.4.14 + version: 5.4.14 + vue-tsc: + specifier: 2.2.0 + version: 2.2.0(typescript@5.7.3) + +packages: + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + + '@rollup/rollup-android-arm-eabi@4.63.4': + resolution: {integrity: sha512-I+BSHzTAhKN2n7ZwGZsegGcZjDpLqFOMAtJz/u6uFGe0pUFbq56dEHjqJV/ZUdRJtNXNxA+hREUatZBvMR3Oiw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.63.4': + resolution: {integrity: sha512-pu3BdjS2LtEzRu2elmGzS3fIeWSZy4BMDIaLNwjorO76+k2d0LMluijhsDx3KQyQBQ/lLUZCQA9/s6csvUfuhw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.63.4': + resolution: {integrity: sha512-xfSrj9MHnWK9GaSqT9U0ImHtH/N8WZlHLx4cZHiuLcqs640hvZ3hLPd5UR2AZS57FaE8HrRUSpltbZdWRxHiDA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.63.4': + resolution: {integrity: sha512-bqU99PLJb/dqb3S0GIMdeuyAEETSUgZBoqXYd3Sd+WCsV+MmPhnN6JrotWyir31+QgH7EvvE5/mwGJlEoci8Fw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.63.4': + resolution: {integrity: sha512-JinsFZ5G40oXQb+sUuiA5x689vhr6dDYK0H0NL+rwKdL6CqnmYN8PE4ZwfRSoIjrCxqTQG/SLfTtSvHeGxoVlw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.63.4': + resolution: {integrity: sha512-GAdA4UxpiNm27cLHr2GqXBpAD0x9FqwYBY7/YSP0Ss0/PNi4k8gbviqpIpYbVSRBaS2ZcegXEzgTQMbRNCwxCw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.63.4': + resolution: {integrity: sha512-qDd6NoA1znaLjp4jR5U/KWCdLAKDJNB8W9ChbbDaKbo0xA+Atln5HK6LFCZ4oJQpemtRZA288DCirFRjrspptw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.63.4': + resolution: {integrity: sha512-WtB5Tz5KTNINb8ZA+8sQ7bmjuS1JrRT7YverYIhUGdWWDlpzVWmIwuZE+jidkEXUn1l0zrEkaIMa8dHF3NGcsA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.63.4': + resolution: {integrity: sha512-VcQ3L1tjnkKzWjryAVaFhHEWcqOfICX9uxVVoDzm2t0DpgKRHd2zOpVrJc0xsWeBZcBFyYROCIBdyR/fS174pg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.63.4': + resolution: {integrity: sha512-6+ZQX6P5s0cMDN2Ypb8Lbm2+/sZYmZjdaYny992ujUU9UKi/4CWoJWsl1pNvjWJHNHGK51m+jKGLlh1ylb2ifQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.63.4': + resolution: {integrity: sha512-D72ZnvkFkBXOfzMMQLcwfPLyGkKb7HZ9/mf97B7v6/P5Lbv4oFOtSY/uHbS8lH6uKUOxoKiuokdb50XZSzzbJw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.63.4': + resolution: {integrity: sha512-piU6BxeqA3O9KSu3kRCIQQtNqFFaTu21SEV4FwaRZowpnj3bLaWPZHw+xFqCs0XlJ+aOH3PTRWGoglH+mKA/OA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.63.4': + resolution: {integrity: sha512-/5PGpHwqt2EEEOUs1XwzubE/ucr0dWDQ+to3zqi4Ds7EWpwtQ79wXc4JBoxqj/OwpawTsKWzJxHfSuBOq3DrWA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.63.4': + resolution: {integrity: sha512-cX3beZDLWt7G2oJF+nhChiT+qtaihs+S2xi7ziGmVB+2pwPng6D0Ed0HmElQOgv2UsUmSJJLGwpBao/3TDx3VA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.63.4': + resolution: {integrity: sha512-1uz2mGWHyptR7DgHHrlbdRAjXK7v7elGZ9lMja910/RP+ZYbX6xAmCiU9UZSX4hqmgtHMv6lr5l3kq1HIOpcag==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.63.4': + resolution: {integrity: sha512-nLS8topojxyz7SRpKR2IODRpQ0XPZ+xaOXvT3+hqK/Uy8Lo5HFgkkIBiIrCu5tL5YqzTvgovGw55PwpahTAGig==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.63.4': + resolution: {integrity: sha512-gs7DRKotr3l3q+jGPQBjH0ng1FjlEDm5ueQrkw5JtQvtLyEIcLASqAEaor56BhkKRzk+IcQzrcanBdb/bBQn8g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.63.4': + resolution: {integrity: sha512-791ET7W17NnScOZM7h4dX5hYspxE28htPFsb1awY/NRR8+PRNkS53e475rDdxXXDrP+kwnCcNWg9CX5ztn/Aqw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.63.4': + resolution: {integrity: sha512-iwZQRcmj7g88g3tzefIrQY7qvmuA/cfYwhrDtTBhsmukO4U2huVO5W+86XacUMRvdSFVAc6kZUZy21JaRwiB9w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.63.4': + resolution: {integrity: sha512-dVHFp9gRWrdTpnqQuGfCwd7hOQDatK1VCP2iWhLY/cGrOQs/ucFzJ6A5SRqbXX12ZDI8EUuejSM5kwg+ja7Png==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.63.4': + resolution: {integrity: sha512-t3NlauOW6gxZVVFcBEnO62Cb4wbyDFL416gTg1uFI/2tgqYQlf69FbSE115Ajre9I+c26Lk4mcmdFUsS/DGifQ==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.63.4': + resolution: {integrity: sha512-xWuIaSye5FWZF8+UYtVEcHtRJDN5kN9Kfgxx3Kq8XIov9KSKbc1fiqQCm90SKrgQbUXZelbnUhnlUJmfSE7P9A==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.63.4': + resolution: {integrity: sha512-9ALJJUOg/ZflMJepVo2PlgsGxSaxN7SQ4Z8GoZfVlarWr6r3rkHUNsd/zAio7p4YMtChSMXPionxej4Hkf6CXQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.63.4': + resolution: {integrity: sha512-blj9z5qx/Pv4WU0W1NMFDB97e0JH5ed+aZGywW8WCvp/NhWX/4PFAq5uu6Q0AebNn+Vo6KzUYDT++JzTT5ojlQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.63.4': + resolution: {integrity: sha512-Erx822VRBwLa124shbj+wNXe//BOgMEctDV0m1aqTQdNO1S69DgNUCFKC1RCeZfixs1J31l6igk1ziyXErbigQ==} + cpu: [x64] + os: [win32] + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@vant/popperjs@1.3.0': + resolution: {integrity: sha512-hB+czUG+aHtjhaEmCJDuXOep0YTZjdlRR+4MSmIFnkCQIxJaXLQdSsR90XWvAI2yvKUI7TCGqR8pQg2RtvkMHw==} + + '@vant/use@1.6.0': + resolution: {integrity: sha512-PHHxeAASgiOpSmMjceweIrv2AxDZIkWXyaczksMoWvKV2YAYEhoizRuk/xFnKF+emUIi46TsQ+rvlm/t2BBCfA==} + peerDependencies: + vue: ^3.0.0 + + '@vitejs/plugin-vue@5.2.1': + resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vue/compiler-core@3.5.13': + resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} + + '@vue/compiler-core@3.5.43': + resolution: {integrity: sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==} + + '@vue/compiler-dom@3.5.13': + resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + + '@vue/compiler-dom@3.5.43': + resolution: {integrity: sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==} + + '@vue/compiler-sfc@3.5.13': + resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} + + '@vue/compiler-ssr@3.5.13': + resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/language-core@2.2.0': + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.13': + resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} + + '@vue/runtime-core@3.5.13': + resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} + + '@vue/runtime-dom@3.5.13': + resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} + + '@vue/server-renderer@3.5.13': + resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} + peerDependencies: + vue: 3.5.13 + + '@vue/shared@3.5.13': + resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + + '@vue/shared@3.5.43': + resolution: {integrity: sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==} + + alien-signals@0.4.14: + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.7.9: + resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + pinia@2.3.1: + resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==} + peerDependencies: + typescript: '>=4.4.4' + vue: ^2.7.0 || ^3.5.11 + peerDependenciesMeta: + typescript: + optional: true + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + rollup@4.63.4: + resolution: {integrity: sha512-4U0liVayNIoLp3GFl1FcI8561WepLnZ1rqfraGh7S9B3Ur5F9S283y8Futii7RUU2C/97tOBmBy7nYvhoiOpbQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + + vant@4.9.21: + resolution: {integrity: sha512-hXUoZMrLLjykimFRLDlGNd+K2iYSRh9YwLMKnsVdVZ+9inUKxpqnjhOqlZwocbnYkvJlS+febf9u9aJpDol4Pw==} + peerDependencies: + vue: ^3.0.0 + + vite@5.4.14: + resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-router@4.5.0: + resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} + peerDependencies: + vue: ^3.2.0 + + vue-tsc@2.2.0: + resolution: {integrity: sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.13: + resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + +snapshots: + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@rollup/rollup-android-arm-eabi@4.63.4': + optional: true + + '@rollup/rollup-android-arm64@4.63.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.63.4': + optional: true + + '@rollup/rollup-darwin-x64@4.63.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.63.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.63.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.63.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.63.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.63.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.63.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.63.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.63.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.63.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.63.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.63.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.63.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.63.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.63.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.63.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.63.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.63.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.63.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.63.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.63.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.63.4': + optional: true + + '@types/estree@1.0.9': {} + + '@vant/popperjs@1.3.0': {} + + '@vant/use@1.6.0(vue@3.5.13(typescript@5.7.3))': + dependencies: + vue: 3.5.13(typescript@5.7.3) + + '@vitejs/plugin-vue@5.2.1(vite@5.4.14)(vue@3.5.13(typescript@5.7.3))': + dependencies: + vite: 5.4.14 + vue: 3.5.13(typescript@5.7.3) + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.2.0 + + '@vue/compiler-core@3.5.13': + dependencies: + '@babel/parser': 7.29.9 + '@vue/shared': 3.5.13 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-core@3.5.43': + dependencies: + '@babel/parser': 7.29.9 + '@vue/shared': 3.5.43 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.13': + dependencies: + '@vue/compiler-core': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/compiler-dom@3.5.43': + dependencies: + '@vue/compiler-core': 3.5.43 + '@vue/shared': 3.5.43 + + '@vue/compiler-sfc@3.5.13': + dependencies: + '@babel/parser': 7.29.9 + '@vue/compiler-core': 3.5.13 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.28 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.13': + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.4': {} + + '@vue/language-core@2.2.0(typescript@5.7.3)': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.43 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.43 + alien-signals: 0.4.14 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.7.3 + + '@vue/reactivity@3.5.13': + dependencies: + '@vue/shared': 3.5.13 + + '@vue/runtime-core@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/runtime-dom@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/runtime-core': 3.5.13 + '@vue/shared': 3.5.13 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + vue: 3.5.13(typescript@5.7.3) + + '@vue/shared@3.5.13': {} + + '@vue/shared@3.5.43': {} + + alien-signals@0.4.14: {} + + asynckit@0.4.0: {} + + axios@1.7.9: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.6 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + brace-expansion@2.1.7: + dependencies: + balanced-match: 1.0.2 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + csstype@3.2.3: {} + + de-indent@1.0.2: {} + + delayed-stream@1.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + entities@4.5.0: {} + + entities@7.0.1: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + estree-walker@2.0.2: {} + + follow-redirects@1.16.0: {} + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + math-intrinsics@1.1.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.7 + + muggle-string@0.4.1: {} + + nanoid@3.3.19: {} + + path-browserify@1.0.1: {} + + picocolors@1.1.1: {} + + pinia@2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.13(typescript@5.7.3) + vue-demi: 0.14.10(vue@3.5.13(typescript@5.7.3)) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - '@vue/composition-api' + + postcss@8.5.28: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + proxy-from-env@1.1.0: {} + + rollup@4.63.4: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.63.4 + '@rollup/rollup-android-arm64': 4.63.4 + '@rollup/rollup-darwin-arm64': 4.63.4 + '@rollup/rollup-darwin-x64': 4.63.4 + '@rollup/rollup-freebsd-arm64': 4.63.4 + '@rollup/rollup-freebsd-x64': 4.63.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.4 + '@rollup/rollup-linux-arm-musleabihf': 4.63.4 + '@rollup/rollup-linux-arm64-gnu': 4.63.4 + '@rollup/rollup-linux-arm64-musl': 4.63.4 + '@rollup/rollup-linux-loong64-gnu': 4.63.4 + '@rollup/rollup-linux-loong64-musl': 4.63.4 + '@rollup/rollup-linux-ppc64-gnu': 4.63.4 + '@rollup/rollup-linux-ppc64-musl': 4.63.4 + '@rollup/rollup-linux-riscv64-gnu': 4.63.4 + '@rollup/rollup-linux-riscv64-musl': 4.63.4 + '@rollup/rollup-linux-s390x-gnu': 4.63.4 + '@rollup/rollup-linux-x64-gnu': 4.63.4 + '@rollup/rollup-linux-x64-musl': 4.63.4 + '@rollup/rollup-openbsd-x64': 4.63.4 + '@rollup/rollup-openharmony-arm64': 4.63.4 + '@rollup/rollup-win32-arm64-msvc': 4.63.4 + '@rollup/rollup-win32-ia32-msvc': 4.63.4 + '@rollup/rollup-win32-x64-gnu': 4.63.4 + '@rollup/rollup-win32-x64-msvc': 4.63.4 + fsevents: 2.3.3 + + source-map-js@1.2.1: {} + + typescript@5.7.3: {} + + vant@4.9.21(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@vant/popperjs': 1.3.0 + '@vant/use': 1.6.0(vue@3.5.13(typescript@5.7.3)) + '@vue/shared': 3.5.43 + vue: 3.5.13(typescript@5.7.3) + + vite@5.4.14: + dependencies: + esbuild: 0.21.5 + postcss: 8.5.28 + rollup: 4.63.4 + optionalDependencies: + fsevents: 2.3.3 + + vscode-uri@3.2.0: {} + + vue-demi@0.14.10(vue@3.5.13(typescript@5.7.3)): + dependencies: + vue: 3.5.13(typescript@5.7.3) + + vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.13(typescript@5.7.3) + + vue-tsc@2.2.0(typescript@5.7.3): + dependencies: + '@volar/typescript': 2.4.28 + '@vue/language-core': 2.2.0(typescript@5.7.3) + typescript: 5.7.3 + + vue@3.5.13(typescript@5.7.3): + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-sfc': 3.5.13 + '@vue/runtime-dom': 3.5.13 + '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.7.3)) + '@vue/shared': 3.5.13 + optionalDependencies: + typescript: 5.7.3 diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/src/App.vue @@ -0,0 +1,3 @@ + diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..7efcf41 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,8 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import 'vant/lib/index.css' +import './styles.css' +import App from './App.vue' +import router from './router' + +createApp(App).use(createPinia()).use(router).mount('#app') diff --git a/src/router.ts b/src/router.ts new file mode 100644 index 0000000..555768c --- /dev/null +++ b/src/router.ts @@ -0,0 +1,22 @@ +import { createRouter, createWebHistory } from 'vue-router' +import AuthView from './views/AuthView.vue' +import OnboardingView from './views/OnboardingView.vue' +import DashboardView from './views/DashboardView.vue' +import { hasToken } from './services/auth' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', redirect: '/dashboard' }, + { path: '/auth', component: AuthView, meta: { guest: true } }, + { path: '/onboarding', component: OnboardingView }, + { path: '/dashboard', component: DashboardView } + ] +}) + +router.beforeEach((to) => { + if (!to.meta.guest && !hasToken()) return '/auth' + if (to.meta.guest && hasToken()) return '/dashboard' +}) + +export default router diff --git a/src/services/auth.ts b/src/services/auth.ts new file mode 100644 index 0000000..6635be6 --- /dev/null +++ b/src/services/auth.ts @@ -0,0 +1,20 @@ +const ACCESS_TOKEN_KEY = 'evergreen-health-access-token' +const REFRESH_TOKEN_KEY = 'evergreen-health-refresh-token' + +export function getToken() { + return localStorage.getItem(ACCESS_TOKEN_KEY) +} + +export function hasToken() { + return Boolean(getToken()) +} + +export function saveTokens(accessToken: string, refreshToken: string) { + localStorage.setItem(ACCESS_TOKEN_KEY, accessToken) + localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken) +} + +export function clearTokens() { + localStorage.removeItem(ACCESS_TOKEN_KEY) + localStorage.removeItem(REFRESH_TOKEN_KEY) +} diff --git a/src/services/health.ts b/src/services/health.ts new file mode 100644 index 0000000..7b6d672 --- /dev/null +++ b/src/services/health.ts @@ -0,0 +1,61 @@ +import http from './http' + +export type HealthUser = { + id: number + phone: string + displayName: string + avatarUrl?: string +} + +export type AuthOutput = { + accessToken: string + refreshToken: string + user: HealthUser +} + +export type HealthProfile = { + birthday?: string + gender?: string + heightCm?: number + weightKg?: number + averageSleepHours?: number + dailyExerciseMinutes?: number + dietPreference?: string + healthGoal?: string +} + +export type PlanItem = { + moduleCode: string + title: string + target: string + advice: string +} + +export type HealthPlan = { + id: number + version: number + title: string + summary: string + createTime: string + items: PlanItem[] +} + +export type Dashboard = { + user: HealthUser + profile?: HealthProfile + selectedModules: string[] + currentPlan?: HealthPlan +} + +export const healthApi = { + register: (payload: { phone: string; password: string; displayName: string }) => + http.post('/api/health/auth/register', payload), + login: (payload: { phone: string; password: string }) => + http.post('/api/health/auth/login', payload), + dashboard: () => http.get('/api/health/dashboard'), + saveProfile: (payload: HealthProfile) => + http.put('/api/health/profile', payload), + saveModules: (moduleCodes: string[]) => + http.put('/api/health/modules', { moduleCodes }), + generatePlan: () => http.post('/api/health/plans/generate') +} diff --git a/src/services/http.ts b/src/services/http.ts new file mode 100644 index 0000000..2e8165f --- /dev/null +++ b/src/services/http.ts @@ -0,0 +1,41 @@ +import axios from 'axios' +import { clearTokens, getToken } from './auth' + +type UnifiedResult = { + type?: string + message?: string + result?: T + errors?: unknown +} + +const http = axios.create({ + baseURL: '/', + timeout: 15000 +}) + +http.interceptors.request.use((config) => { + const token = getToken() + if (token) config.headers.Authorization = `Bearer ${token}` + return config +}) + +http.interceptors.response.use( + ((response: any) => { + const body = response.data as UnifiedResult + if (body && typeof body === 'object' && 'result' in body) { + if (body.type && body.type !== 'success') { + return Promise.reject(new Error(body.message || '请求失败')) + } + return body.result + } + return body + }) as any, + (error) => { + if (error.response?.status === 401) clearTokens() + const body = error.response?.data as UnifiedResult | undefined + const message = body?.message || error.message || '网络请求失败' + return Promise.reject(new Error(message)) + } +) + +export default http diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..607e1a5 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,724 @@ +:root { + font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #17322a; + background-color: #edebe4; + + /* Brand Theme Tokens */ + --primary-deep: #09372d; + --primary: #0c4d3f; + --primary-light: #156655; + --primary-subtle: #e8f2ef; + --accent-gold: #c89547; + --accent-light: #fdf6ea; + --accent-warm: #e3b063; + + /* Neutrals */ + --bg-app: #f6f5f0; + --card-bg: #ffffff; + --line: rgba(12, 77, 63, 0.08); + --line-light: rgba(12, 77, 63, 0.04); + --text-main: #142e27; + --text-sub: #576d65; + --text-muted: #8d9e97; + + /* Shadows */ + --shadow-card: 0 4px 16px -2px rgba(9, 55, 45, 0.05), 0 2px 6px -1px rgba(9, 55, 45, 0.03); + --shadow-float: 0 12px 32px -4px rgba(9, 55, 45, 0.12); +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; + -webkit-tap-highlight-color: transparent; +} + +body { + min-height: 100vh; + background-color: #edebe4; + display: flex; + justify-content: center; +} + +#app { + width: 100%; + max-width: 460px; + min-height: 100vh; + background: var(--bg-app); + box-shadow: 0 0 40px rgba(0, 0, 0, 0.06); + position: relative; + overflow-x: hidden; + display: flex; + flex-direction: column; +} + +button, input, textarea { + font: inherit; + border: none; + outline: none; +} + +button { + cursor: pointer; +} + +/* Common Typography */ +.eyebrow { + display: inline-flex; + align-items: center; + gap: 4px; + margin: 0 0 6px; + color: var(--accent-gold); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; +} + +/* Page Shell Layout */ +.page-shell { + min-height: 100vh; + padding: 16px 16px calc(90px + env(safe-area-inset-bottom)); + background: var(--bg-app); + display: flex; + flex-direction: column; +} + +/* Card Common */ +.section-card { + background: var(--card-bg); + border-radius: 20px; + padding: 18px 16px; + margin-bottom: 14px; + border: 1px solid var(--line); + box-shadow: var(--shadow-card); +} + +.section-title { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 14px; +} + +.section-title > span { + font-size: 16px; + font-weight: 700; + color: var(--text-main); + letter-spacing: -0.01em; +} + +.section-title small { + color: var(--text-muted); + font-size: 12px; +} + +.section-title button { + background: transparent; + color: var(--primary); + font-size: 13px; + font-weight: 600; + padding: 4px 8px; + border-radius: 6px; +} + +/* Auth Page */ +.auth-page { + min-height: 100vh; + background: var(--bg-app); + display: flex; + flex-direction: column; +} + +.brand-panel { + padding: calc(36px + env(safe-area-inset-top)) 24px 38px; + color: #fff; + background: + radial-gradient(circle at 85% 15%, rgba(227, 176, 99, 0.24), transparent 45%), + radial-gradient(circle at 10% 90%, rgba(21, 102, 85, 0.4), transparent 50%), + linear-gradient(155deg, var(--primary-deep) 0%, var(--primary) 65%, #13463b 100%); + border-radius: 0 0 32px 32px; + position: relative; +} + +.brand-badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 5px 12px 5px 6px; + background: rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 24px; + font-size: 12px; + margin-bottom: 20px; + backdrop-filter: blur(8px); +} + +.brand-badge-icon { + width: 24px; + height: 24px; + background: var(--accent-gold); + color: #fff; + border-radius: 50%; + display: grid; + place-items: center; + font-weight: 700; + font-size: 13px; +} + +.brand-panel h1 { + font-size: 28px; + font-weight: 700; + line-height: 1.25; + margin-bottom: 10px; + letter-spacing: -0.02em; +} + +.brand-copy { + font-size: 13px; + color: rgba(255, 255, 255, 0.76); + line-height: 1.6; + margin-bottom: 18px; +} + +.feature-pill-row { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.feature-pill-row span { + padding: 5px 11px; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: 16px; + font-size: 11px; + color: rgba(255, 255, 255, 0.9); +} + +.auth-card { + width: calc(100% - 32px); + margin: -24px auto 24px; + padding: 22px 20px 24px; + background: #fff; + border-radius: 24px; + box-shadow: var(--shadow-float); + border: 1px solid var(--line); + position: relative; + z-index: 2; +} + +.auth-tab-switch { + display: flex; + background: #f1efea; + border-radius: 12px; + padding: 3px; + margin-bottom: 20px; +} + +.auth-tab-switch button { + flex: 1; + padding: 9px 0; + font-size: 14px; + font-weight: 600; + border-radius: 9px; + color: var(--text-sub); + background: transparent; + transition: all 0.2s ease; +} + +.auth-tab-switch button.active { + background: #ffffff; + color: var(--primary); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.auth-card .van-field { + margin-bottom: 12px; + padding: 12px 14px; + background: #f8f7f4; + border-radius: 14px; + border: 1px solid transparent; + transition: border-color 0.2s; +} + +.auth-card .van-field:focus-within { + border-color: var(--primary); + background: #ffffff; +} + +.auth-card .van-button { + margin-top: 14px; + height: 48px; + font-weight: 600; + font-size: 15px; + box-shadow: 0 8px 20px -2px rgba(12, 77, 63, 0.28); +} + +.privacy-note { + margin-top: 18px; + font-size: 11px; + color: var(--text-muted); + text-align: center; + line-height: 1.5; +} + +/* Onboarding Header */ +.page-header { + margin-bottom: 18px; + padding: 6px 4px 0; +} + +.page-header h1 { + font-size: 24px; + font-weight: 700; + color: var(--text-main); + margin: 4px 0 8px; + letter-spacing: -0.02em; +} + +.page-header p { + color: var(--text-sub); + font-size: 13px; + line-height: 1.5; +} + +/* Module Selection Grid (3 Columns) */ +.module-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; +} + +.module-card { + padding: 14px 10px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + border: 1.5px solid var(--line); + border-radius: 16px; + background: #faf9f6; + color: var(--text-main); + transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); + position: relative; +} + +.module-card.selected { + border-color: var(--primary); + background: var(--primary-subtle); + color: var(--primary); +} + +.module-icon { + width: 36px; + height: 36px; + border-radius: 50%; + background: #ffffff; + display: grid; + place-items: center; + font-size: 18px; + margin-bottom: 8px; + border: 1px solid var(--line); + box-shadow: var(--shadow-card); +} + +.module-card.selected .module-icon { + background: var(--primary); + color: #fff; + border-color: transparent; +} + +.module-card strong { + font-size: 13px; + font-weight: 700; + margin-bottom: 4px; +} + +.module-card small { + font-size: 10px; + color: var(--text-muted); + line-height: 1.3; +} + +.module-card.selected small { + color: var(--primary-light); +} + +.module-check { + margin-top: 8px; + padding: 2px 8px; + border-radius: 10px; + font-size: 10px; + font-weight: 600; + background: #eee; + color: var(--text-muted); +} + +.module-card.selected .module-check { + background: var(--primary); + color: #fff; +} + +/* Profile Form Elements */ +.form-group-title { + font-size: 12px; + font-weight: 700; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + margin: 12px 0 8px; +} + +.two-column { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; +} + +.custom-field-box { + background: #f8f7f3; + border-radius: 14px; + padding: 8px 12px; + margin-bottom: 8px; + border: 1px solid transparent; + transition: all 0.2s; +} + +.custom-field-box:focus-within { + background: #fff; + border-color: var(--primary); +} + +.custom-field-box .van-field { + padding: 0; + background: transparent; +} + +/* Sticky Bottom Action */ +.sticky-action { + position: fixed; + z-index: 50; + left: 50%; + transform: translateX(-50%); + width: 100%; + max-width: 460px; + bottom: 0; + padding: 12px 16px calc(14px + env(safe-area-inset-bottom)); + background: rgba(246, 245, 240, 0.92); + backdrop-filter: blur(16px); + border-top: 1px solid var(--line); +} + +.sticky-action .van-button { + height: 48px; + font-size: 15px; + font-weight: 600; + box-shadow: 0 8px 24px -2px rgba(12, 77, 63, 0.25); +} + +/* Dashboard Header */ +.dashboard-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; + padding: 8px 2px 0; +} + +.header-meta h1 { + font-size: 22px; + font-weight: 700; + color: var(--text-main); + letter-spacing: -0.02em; + line-height: 1.3; +} + +.date-badge { + display: inline-flex; + align-items: center; + gap: 4px; + margin-top: 4px; + color: var(--text-sub); + font-size: 12px; +} + +.user-chip { + display: flex; + align-items: center; + gap: 8px; + padding: 5px 12px 5px 6px; + background: #fff; + border: 1px solid var(--line); + border-radius: 20px; + box-shadow: var(--shadow-card); + transition: transform 0.15s; +} + +.user-chip:active { + transform: scale(0.97); +} + +.avatar-badge { + width: 28px; + height: 28px; + border-radius: 50%; + background: var(--primary); + color: #fff; + display: grid; + place-items: center; + font-size: 13px; + font-weight: 700; +} + +.user-chip-name { + font-size: 12px; + font-weight: 600; + color: var(--text-main); +} + +/* Hero Health Card */ +.hero-card { + padding: 22px 20px; + color: #fff; + border-radius: 24px; + background: + radial-gradient(circle at 90% 10%, rgba(227, 176, 99, 0.28), transparent 45%), + linear-gradient(145deg, var(--primary-deep) 0%, var(--primary) 70%, #155749 100%); + box-shadow: var(--shadow-float); + border: 1px solid rgba(255, 255, 255, 0.12); + margin-bottom: 14px; +} + +.hero-top-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; +} + +.hero-card h2 { + font-size: 22px; + font-weight: 700; + line-height: 1.3; + margin-bottom: 8px; + letter-spacing: -0.01em; +} + +.hero-summary { + font-size: 13px; + color: rgba(255, 255, 255, 0.8); + line-height: 1.65; + margin-bottom: 16px; +} + +.tag-row { + display: flex; + gap: 6px; + flex-wrap: wrap; + margin-bottom: 14px; +} + +.tag-pill { + padding: 4px 10px; + background: rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.16); + border-radius: 12px; + font-size: 11px; + color: #fff; +} + +.hero-action-row { + display: flex; + gap: 10px; + align-items: center; + margin-top: 8px; +} + +/* Metrics Strip (3 Cards) */ +.metric-strip { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + margin-bottom: 14px; +} + +.metric-card { + background: #fff; + border-radius: 16px; + padding: 12px 10px; + text-align: center; + border: 1px solid var(--line); + box-shadow: var(--shadow-card); +} + +.metric-label { + display: flex; + align-items: center; + justify-content: center; + gap: 3px; + font-size: 11px; + color: var(--text-muted); + margin-bottom: 4px; +} + +.metric-card strong { + display: block; + font-size: 19px; + font-weight: 800; + color: var(--text-main); + letter-spacing: -0.02em; +} + +.metric-card em { + font-size: 11px; + font-style: normal; + font-weight: 500; + color: var(--text-sub); + margin-left: 2px; +} + +.metric-desc { + font-size: 10px; + color: var(--accent-gold); + margin-top: 2px; +} + +/* Actionable Plan Items */ +.plan-item { + display: flex; + gap: 12px; + padding: 14px 0; + border-top: 1px solid var(--line); +} + +.plan-item:first-of-type { + border-top: none; + padding-top: 2px; +} + +.plan-item-icon { + width: 36px; + height: 36px; + border-radius: 12px; + background: var(--primary-subtle); + color: var(--primary); + display: grid; + place-items: center; + font-size: 16px; + flex-shrink: 0; +} + +.plan-item-content { + flex: 1; +} + +.plan-item-header { + display: flex; + align-items: baseline; + justify-content: space-between; + margin-bottom: 4px; +} + +.plan-item-header strong { + font-size: 14px; + font-weight: 700; + color: var(--text-main); +} + +.plan-item-target { + font-size: 11px; + color: var(--accent-gold); + font-weight: 600; + background: var(--accent-light); + padding: 2px 7px; + border-radius: 6px; +} + +.plan-item-advice { + font-size: 12px; + color: var(--text-sub); + line-height: 1.6; +} + +/* Smart Space / IoT Card */ +.smart-space-card { + background: linear-gradient(135deg, #ffffff 0%, #f7faf9 100%); + border: 1px solid rgba(12, 77, 63, 0.12); +} + +.smart-scene-box { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 14px; + background: #fff; + border: 1px solid var(--line); + border-radius: 14px; +} + +.scene-icon-title { + display: flex; + align-items: center; + gap: 10px; +} + +.scene-icon { + width: 34px; + height: 34px; + border-radius: 10px; + background: #f2f5f4; + color: var(--primary); + display: grid; + place-items: center; + font-size: 16px; +} + +.scene-text strong { + display: block; + font-size: 13px; + font-weight: 700; + color: var(--text-main); +} + +.scene-text p { + font-size: 11px; + color: var(--text-muted); + margin-top: 2px; +} + +.smart-badge { + font-size: 10px; + padding: 4px 8px; + border-radius: 8px; + background: #f1efea; + color: var(--text-sub); + font-weight: 600; +} + +.medical-disclaimer { + text-align: center; + font-size: 11px; + color: var(--text-muted); + line-height: 1.6; + margin: 18px 8px 10px; +} + +/* Mobile Touch Optimization */ +@media (max-width: 360px) { + .module-grid { + gap: 6px; + } + .module-card { + padding: 10px 6px; + } + .module-card strong { + font-size: 12px; + } + .metric-card strong { + font-size: 16px; + } +} + diff --git a/src/views/AuthView.vue b/src/views/AuthView.vue new file mode 100644 index 0000000..8862ed6 --- /dev/null +++ b/src/views/AuthView.vue @@ -0,0 +1,114 @@ + + + + diff --git a/src/views/DashboardView.vue b/src/views/DashboardView.vue new file mode 100644 index 0000000..7cfc875 --- /dev/null +++ b/src/views/DashboardView.vue @@ -0,0 +1,256 @@ + + + + diff --git a/src/views/OnboardingView.vue b/src/views/OnboardingView.vue new file mode 100644 index 0000000..b6f4866 --- /dev/null +++ b/src/views/OnboardingView.vue @@ -0,0 +1,189 @@ + + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d93ad63 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..ad0b284 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig, loadEnv } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), '') + return { + plugins: [vue()], + server: { + port: 5173, + proxy: { + '/api': { + target: env.VITE_API_BASE_URL || 'http://localhost:5005', + changeOrigin: true + } + } + } + } +})