From e546f5e93681f61d8d92257408a3b04876d06532 Mon Sep 17 00:00:00 2001 From: Lxy Date: Fri, 5 Jun 2026 23:08:25 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=88=9D=E5=A7=8B=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 24 + .vscode/extensions.json | 3 + README.md | 131 +++ index.html | 13 + package-lock.json | 1379 +++++++++++++++++++++++++++ package.json | 22 + public/favicon.svg | 1 + public/icons.svg | 24 + src/App.vue | 45 + src/components/ChatInterface.vue | 399 ++++++++ src/components/ComparisonView.vue | 340 +++++++ src/components/DetailPanel.vue | 268 ++++++ src/components/MapView.vue | 242 +++++ src/components/ModeToggle.vue | 59 ++ src/components/ReplacementPanel.vue | 71 ++ src/components/TimelinePanel.vue | 167 ++++ src/components/Workbench.vue | 146 +++ src/data/sampleData.js | 171 ++++ src/main.js | 9 + src/router.js | 15 + src/services/routeService.js | 54 ++ src/stores/itinerary.js | 196 ++++ src/stores/settings.js | 104 ++ src/utils/exporter.js | 267 ++++++ src/views/SettingsPage.vue | 500 ++++++++++ vite.config.js | 10 + 26 files changed, 4660 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 README.md create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/favicon.svg create mode 100644 public/icons.svg create mode 100644 src/App.vue create mode 100644 src/components/ChatInterface.vue create mode 100644 src/components/ComparisonView.vue create mode 100644 src/components/DetailPanel.vue create mode 100644 src/components/MapView.vue create mode 100644 src/components/ModeToggle.vue create mode 100644 src/components/ReplacementPanel.vue create mode 100644 src/components/TimelinePanel.vue create mode 100644 src/components/Workbench.vue create mode 100644 src/data/sampleData.js create mode 100644 src/main.js create mode 100644 src/router.js create mode 100644 src/services/routeService.js create mode 100644 src/stores/itinerary.js create mode 100644 src/stores/settings.js create mode 100644 src/utils/exporter.js create mode 100644 src/views/SettingsPage.vue create mode 100644 vite.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..117f47b --- /dev/null +++ b/README.md @@ -0,0 +1,131 @@ +# \ud83e\udded \u667a\u80fd\u884c\u7a0b\u89c4\u5212\u7cfb\u7edf + +> \u4e00\u7ad9\u5f0f\u65c5\u884c\u884c\u7a0b\u89c4\u5212\u5e73\u53f0\uff0c\u4ece\u201c\u7075\u611f\u8f93\u5165\u201d\u5230\u201c\u6700\u7ec8\u4ea4\u4ed8\u201d\u7684\u5b8c\u6574\u95ed\u73af\u3002 + +## \u9879\u76ee\u6982\u8ff0 + +\u57fa\u4e8e Vue 3 + Vite + Leaflet \u6784\u5efa\u7684\u4ea4\u4e92\u5f0f\u884c\u7a0b\u89c4\u5212\u7cfb\u7edf\uff0c\u5b9e\u73b0\u4e86 PRD \u4e2d\u5b9a\u4e49\u7684\u4e09\u9636\u6bb5\u4ea4\u4e92\u67b6\u6784\u3002 + +## \u6280\u672f\u6808 + +- **\u524d\u7aef\u6846\u67b6**: Vue 3 (Composition API) +- **\u6784\u5efa\u5de5\u5177**: Vite 5 +- **\u72b6\u6001\u7ba1\u7406**: Pinia +- **\u5730\u56fe\u5e93**: Leaflet +- **\u62d6\u62fd\u6392\u5e8f**: SortableJS +- **\u8def\u7531 API**: OSRM (\u5f00\u6e90\u514d\u8d39) + +## \u5feb\u901f\u5f00\u59cb + +```bash +# \u5b89\u88c5\u4f9d\u8d56 +NODE_ENV=development npm install + +# \u5f00\u53d1\u6a21\u5f0f +npm run dev + +# \u6784\u5efa\u751f\u4ea7\u7248\u672c +npm run build + +# \u9884\u89c8\u751f\u4ea7\u7248\u672c +npm run preview +``` + +## \u9879\u76ee\u7ed3\u6784 + +``` +src/ +\u251c\u2500\u2500 components/ +\u2502 \u251c\u2500\u2500 App.vue # \u6839\u7ec4\u4ef6\uff0c\u9636\u6bb5\u5207\u6362 +\u2502 \u251c\u2500\u2500 ChatInterface.vue # Phase 1: \u5bf9\u8bdd\u63a5\u53e3 +\u2502 \u251c\u2500\u2500 ComparisonView.vue # Phase 2: \u65b9\u6848\u5bf9\u6bd4 +\u2502 \u251c\u2500\u2500 Workbench.vue # Phase 3: \u6c89\u6d78\u5de5\u4f5c\u53f0 +\u2502 \u251c\u2500\u2500 TimelinePanel.vue # \u5de6\u4fa7\u65f6\u95f4\u7ebf\uff08SortableJS\u62d6\u62fd\uff09 +\u2502 \u251c\u2500\u2500 DetailPanel.vue # \u4e2d\u95f4\u8be6\u60c5\u9762\u677f +\u2502 \u251c\u2500\u2500 MapView.vue # Leaflet \u5730\u56fe\u7ec4\u4ef6\uff08\u771f\u5b9e\u8def\u7ebf\uff09 +\u2502 \u251c\u2500\u2500 ModeToggle.vue # \u9884\u89c8/\u7f16\u8f91\u6a21\u5f0f\u5207\u6362 +\u2502 \u2514\u2500\u2500 ReplacementPanel.vue # \u667a\u80fd\u66ff\u6362\u63a8\u8350\u9762\u677f\uff08\u60ac\u505c\u9884\u6f14\uff09 +\u251c\u2500\u2500 stores/ +\u2502 \u2514\u2500\u2500 itinerary.js # Pinia \u72b6\u6001\u7ba1\u7406\uff08\u8def\u7ebf\u4f18\u5316\uff09 +\u251c\u2500\u2500 data/ +\u2502 \u2514\u2500\u2500 sampleData.js # \u4e91\u53574\u5929\u884c\u7a0b\u793a\u4f8b\u6570\u636e + AI\u6a21\u677f +\u251c\u2500\u2500 services/ +\u2502 \u2514\u2500\u2500 routeService.js # OSRM \u8def\u5f84\u8ba1\u7b97\u670d\u52a1 + \u7ed5\u8def\u68c0\u6d4b +\u251c\u2500\u2500 utils/ +\u2502 \u2514\u2500\u2500 exporter.js # \u9759\u6001 HTML \u5bfc\u51fa\u5668 +\u2514\u2500\u2500 main.js # \u5165\u53e3\u6587\u4ef6 +``` + +## \u5df2\u5b9e\u73b0\u529f\u80fd\u2705 + +### Phase 1: \u5bf9\u8bdd\u63a5\u53e3 +- [x] ChatGPT \u98ce\u683c\u5bf9\u8bdd\u6d41 +- [x] \u7528\u6237\u8f93\u5165\u4e0e AI \u54cd\u5e94\u6a21\u62df +- [x] \u65b9\u6848\u5361\u7247\u5f39\u51fa\u52a8\u753b +- [x] \u601d\u8003\u4e2d\u52a8\u753b\u6548\u679c + +### Phase 2: \u65b9\u6848\u5bf9\u6bd4 +- [x] \u4e09\u65b9\u6848\u5e76\u5217\u5c55\u793a +- [x] \u5bf9\u6bd4\u7ef4\u5ea6\uff1a\u91cc\u7a0b/\u9a7e\u9a76/\u9884\u7b97/\u666f\u70b9\u5bc6\u5ea6 +- [x] \u8def\u7ebf\u6982\u89c8\u4e0e\u6838\u5fc3\u4eae\u70b9 +- [x] \u70b9\u51fb\u9009\u62e9\u8fdb\u5165\u5de5\u4f5c\u53f0 + +### Phase 3: \u6c89\u6d78\u5de5\u4f5c\u53f0 +- [x] \u9884\u89c8\u6a21\u5f0f\uff1a\u5168\u5c4f\u5730\u56fe + \u81ea\u52a8\u64ad\u653e\u5c0f\u8f66\u52a8\u753b + \u7cbe\u7f8e\u8be6\u60c5\u5361 +- [x] \u7f16\u8f91\u6a21\u5f0f\uff1a\u62d6\u62fd\u624b\u67c4 + \u5220\u9664/\u66ff\u6362\u6309\u94ae + \u865a\u7ebf\u8def\u7ebf +- [x] \u53cc\u6a21\u5f0f\u65e0\u7f1d\u5207\u6362 +- [x] \u5730\u56fe\u6807\u8bb0\u70b9\u51fb\u4ea4\u4e92 +- [x] \u5c0f\u8f66\u6cbf\u8def\u7ebf\u52a8\u753b\uff08requestAnimationFrame\uff09 +- [x] \u65f6\u95f4\u7ebf\u5bfc\u822a\uff08\u4e0a\u4e00\u7ad9/\u4e0b\u4e00\u7ad9/\u952e\u76d8\u65b9\u5411\u952e\uff09 +- [x] \u884c\u7a0b\u7edf\u8ba1\uff08\u603b\u91cc\u7a0b/\u9a7e\u9a76\u65f6\u95f4/\u5929\u6570\uff09 + +### Phase 4: \u667a\u80fd\u4e0e\u63a8\u8350 +- [x] \u667a\u80fd\u66ff\u6362\u63a8\u8350\u9762\u677f +- [x] **\u60ac\u505c\u865a\u7ebf\u9884\u6f14** - \u9f20\u6807\u60ac\u505c\u5373\u53ef\u5728\u5730\u56fe\u4e0a\u9884\u89c8\u66ff\u6362\u540e\u7684\u8def\u7ebf +- [x] **\u771f\u5b9e\u8def\u7ebf\u83b7\u53d6** - \u8c03\u7528 OSRM \u514d\u8d39 API \u83b7\u53d6\u771f\u5b9e\u9a7e\u8f66\u8def\u7ebf +- [x] **\u667a\u80fd\u7ea0\u504f** - \u81ea\u52a8\u68c0\u6d4b\u7ed5\u8def\u5e76\u5f39\u51fa\u4f18\u5316\u63d0\u793a +- [x] **\u4e00\u952e\u4f18\u5316** - \u81ea\u52a8\u6309\u5730\u7406\u987a\u5e8f\u91cd\u6392\u884c\u7a0b\u8282\u70b9 +- [x] **\u62d6\u62fd\u6392\u5e8f** - SortableJS \u5b9e\u73b0\u65f6\u95f4\u7ebf\u62d6\u62fd\uff0c\u5730\u56fe\u5b9e\u65f6\u66f4\u65b0 + +### Phase 5: \u5bfc\u51fa\u4e0e\u5206\u4eab +- [x] **\u5bfc\u51fa\u9759\u6001 HTML** - \u5c06\u5f53\u524d\u7f16\u8f91\u597d\u7684\u884c\u7a0b\u5bfc\u51fa\u4e3a\u72ec\u7acb HTML \u6587\u4ef6 +- [x] \u5bfc\u51fa\u6587\u4ef6\u5305\u542b\u5b8c\u6574\u5730\u56fe\u3001\u52a8\u753b\u3001\u8be6\u60c5\u5c55\u793a +- [x] \u53ef\u76f4\u63a5\u6253\u5f00\u6d4f\u89c8\uff0c\u65e0\u9700\u670d\u52a1\u5668 + +## \u5f85\u5b9e\u73b0\u529f\u80fd + +- [ ] \u771f\u5b9e AI \u63a5\u53e3\u8c03\u7528\uff08\u5df2\u6709\u914d\u7f6e\u9875\u9762\uff0c\u5f85\u63a5\u5165\u5b9e\u9645\u751f\u6210\uff09 +- [ ] \u751f\u6210\u5206\u4eab\u94fe\u63a5\uff08\u9700\u540e\u7aef\u652f\u6301\uff09 +- [ ] \u591a\u884c\u7a0b\u6587\u4ef6\u7ba1\u7406 +- [ ] \u7528\u6237\u6536\u85cf\u4e0e\u5386\u53f2\u8bb0\u5f55 + +## \u2699\ufe0f \u540e\u53f0\u914d\u7f6e + +\u7cfb\u7edf\u652f\u6301\u901a\u8fc7\u53f3\u4e0a\u89d2\u9f7f\u8f6e\u6309\u94ae\u8fdb\u5165 **AI \u6a21\u578b\u914d\u7f6e\u9875\u9762**\uff0c\u76ee\u524d\u4ec5\u652f\u6301 **\u963f\u91cc\u4e91 DashScope\uff08\u901a\u4e49\u5343\u95ee\uff09**\uff1a + +| \u914d\u7f6e\u9879 | \u8bf4\u660e | +|--------|------| +| API Key | \u4ece[\u963f\u91cc\u4e91\u767e\u70bc\u63a7\u5236\u53f0](https://bailian.console.aliyun.com/)\u83b7\u53d6 | +| \u6a21\u578b\u9009\u62e9 | Qwen-Turbo\uff08\u5feb\u901f\uff09\u3001Qwen-Plus\uff08\u5747\u8861\uff09\u3001Qwen-Max\uff08\u9ad8\u8d28\u91cf\uff09\u3001Qwen-Long\uff08\u957f\u6587\u672c\uff09 | +| Base URL | \u9ed8\u8ba4 `https://dashscope.aliyuncs.com/compatible-mode/v1` | +| Temperature | \u521b\u9020\u6027\u63a7\u5236\uff080=\u7cbe\u786e\uff0c1=\u968f\u673a\uff09 | +| Max Tokens | \u6700\u5927\u8f93\u51fa\u957f\u5ea6 | +| \u6d4b\u8bd5\u8fde\u63a5 | \u9a8c\u8bc1 API Key \u548c\u6a21\u578b\u662f\u5426\u53ef\u7528 | + +\u914d\u7f6e\u6570\u636e\u901a\u8fc7 `localStorage` \u6301\u4e45\u5316\u5b58\u50a8\uff0c\u5237\u65b0\u9875\u9762\u4e0d\u4e22\u5931\u3002 + +## \u6838\u5fc3\u4ea4\u4e92\u7ec6\u8282 + +### \u66ff\u6362\u9884\u6f14 +1. \u70b9\u51fb\u8282\u70b9 \u2192 \u9009\u62e9\u201c\u66ff\u6362\u201d +2. \u4fa7\u8fb9\u6ed1\u51fa\u63a8\u8350\u5217\u8868 +3. **\u60ac\u505c\u9884\u89c8**: \u9f20\u6807\u60ac\u505c\u5373\u5728\u5730\u56fe\u4e0a\u663e\u793a\u865a\u7ebf\u9884\u6f14 +4. \u786e\u8ba4\u66ff\u6362 \u2192 \u5b9e\u7ebf\u66f4\u65b0\uff0c\u8def\u7ebf\u81ea\u52a8\u91cd\u7b97 + +### \u667a\u80fd\u7ea0\u504f +- \u5f53\u7528\u6237\u62d6\u62fd\u5bfc\u81f4\u987a\u5e8f\u4e0d\u5408\u7406\uff0c\u9876\u90e8\u5f39\u51fa\u63d0\u793a +- \u70b9\u51fb\u201c\u4e00\u952e\u4f18\u5316\u201d\u5373\u53ef\u81ea\u52a8\u91cd\u6392\u4e3a\u6700\u77ed\u8def\u5f84 + +### \u5b9e\u65f6\u53cd\u9988 +- \u6bcf\u6b21\u4fee\u6539\u540e\uff0c\u9876\u90e8\u6570\u636e\u6761\u5b9e\u65f6\u66f4\u65b0\u603b\u91cc\u7a0b\u3001\u603b\u8017\u65f6 +- \u7ed5\u8def\u6bb5\u4ee5\u7ea2\u8272\u7a81\u51fa\u663e\u793a diff --git a/index.html b/index.html new file mode 100644 index 0000000..36b2367 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + 智能行程规划系统 + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..533685f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1379 @@ +{ + "name": "trip-planner", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "trip-planner", + "version": "0.0.0", + "dependencies": { + "leaflet": "^1.9.4", + "pinia": "^3.0.4", + "sortablejs": "^1.15.7", + "vue": "^3.5.34", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "vite": "^5.4.21" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz", + "integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/shared": "3.5.35", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz", + "integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz", + "integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.35", + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz", + "integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz", + "integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz", + "integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz", + "integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/runtime-core": "3.5.35", + "@vue/shared": "3.5.35", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz", + "integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "vue": "3.5.35" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz", + "integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==", + "license": "MIT" + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "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" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/pinia": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz", + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.7" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.5.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/sortablejs": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz", + "integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "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 + } + } + }, + "node_modules/vue": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz", + "integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-sfc": "3.5.35", + "@vue/runtime-dom": "3.5.35", + "@vue/server-renderer": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..431c58c --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "trip-planner", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "leaflet": "^1.9.4", + "pinia": "^3.0.4", + "sortablejs": "^1.15.7", + "vue": "^3.5.34", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "vite": "^5.4.21" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..d3dab7d --- /dev/null +++ b/src/App.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/src/components/ChatInterface.vue b/src/components/ChatInterface.vue new file mode 100644 index 0000000..7fb5653 --- /dev/null +++ b/src/components/ChatInterface.vue @@ -0,0 +1,399 @@ + + + + + diff --git a/src/components/ComparisonView.vue b/src/components/ComparisonView.vue new file mode 100644 index 0000000..c4b7410 --- /dev/null +++ b/src/components/ComparisonView.vue @@ -0,0 +1,340 @@ + + + + + diff --git a/src/components/DetailPanel.vue b/src/components/DetailPanel.vue new file mode 100644 index 0000000..5276f39 --- /dev/null +++ b/src/components/DetailPanel.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/src/components/MapView.vue b/src/components/MapView.vue new file mode 100644 index 0000000..d855fed --- /dev/null +++ b/src/components/MapView.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/src/components/ModeToggle.vue b/src/components/ModeToggle.vue new file mode 100644 index 0000000..ce17f4a --- /dev/null +++ b/src/components/ModeToggle.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/src/components/ReplacementPanel.vue b/src/components/ReplacementPanel.vue new file mode 100644 index 0000000..311fc94 --- /dev/null +++ b/src/components/ReplacementPanel.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/components/TimelinePanel.vue b/src/components/TimelinePanel.vue new file mode 100644 index 0000000..17b8659 --- /dev/null +++ b/src/components/TimelinePanel.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/src/components/Workbench.vue b/src/components/Workbench.vue new file mode 100644 index 0000000..852888f --- /dev/null +++ b/src/components/Workbench.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/src/data/sampleData.js b/src/data/sampleData.js new file mode 100644 index 0000000..9f134bf --- /dev/null +++ b/src/data/sampleData.js @@ -0,0 +1,171 @@ +// 云南4天自驾行程示例数据 +export const sampleItinerary = { + title: '云南4天自驾行程', + subtitle: '北京出发 · 9月 · 昆明落地自驾', + totalKm: '~1450km', + points: [ + { + id: 'p0', name: '昆明(起点)', lat: 25.04, lng: 102.71, + day: '出发日', badge: 'START', km: '0km', driveTime: '—', + desc: '北京飞抵昆明,取车出发', icon: '🏙️', + images: [ + 'https://picsum.photos/400/300?random=1', + 'https://picsum.photos/400/300?random=2', + 'https://picsum.photos/400/300?random=3' + ], + heroImage: 'https://picsum.photos/800/400?random=1', + schedule: [ + { time: '上午', content: '北京飞昆明(约3.5小时),抵达长水机场' }, + { time: '中午', content: '机场取车(推荐SUV),检查车况' }, + { time: '下午', content: '昆明市区:翠湖公园、斗南花市' }, + { time: '晚上', content: '南强街夜市吃野生菌火锅' } + ], + foods: ['野生菌火锅', '过桥米线', '官渡粑粑', '鲜花饼'], + hotel: '昆明市区酒店(翠湖或南屏街附近)', + tips: '取车拍照记录车况,下载离线地图。' + }, + { + id: 'p1', name: '普者黑', lat: 24.12, lng: 104.12, + day: 'Day 1', badge: 'D1', km: '280km', driveTime: '3.5h', + desc: '昆明 → 普者黑,喀斯特地貌、荷花世界', icon: '🌿', + images: [ + 'https://picsum.photos/400/300?random=4', + 'https://picsum.photos/400/300?random=5' + ], + heroImage: 'https://picsum.photos/800/400?random=4', + schedule: [ + { time: '上午', content: '昆明出发,沿广昆高速前往普者黑' }, + { time: '中午', content: '抵达丘北县,品尝当地特色午餐' }, + { time: '下午', content: '游普者黑景区:青龙山、仙人洞、荷花世界' }, + { time: '晚上', content: '入住景区民宿,观星空' } + ], + foods: ['荷花宴', '丘北辣椒鸡', '酸汤鱼', '紫洋芋'], + hotel: '普者黑景区内民宿(仙人洞村)', + tips: '9月荷花正盛,建议穿轻便鞋,带防晒霜。' + }, + { + id: 'p2', name: '坝美', lat: 24.08, lng: 104.65, + day: 'Day 2', badge: 'D2', km: '60km', driveTime: '1.5h', + desc: '普者黑 → 坝美,世外桃源般的壮族村落', icon: '🏘️', + images: [ + 'https://picsum.photos/400/300?random=6', + 'https://picsum.photos/400/300?random=7' + ], + heroImage: 'https://picsum.photos/800/400?random=6', + schedule: [ + { time: '上午', content: '普者黑出发,前往坝美村' }, + { time: '中午', content: '乘船穿过水洞,进入坝美村' }, + { time: '下午', content: '漫步古村、田园风光、体验壮族文化' }, + { time: '晚上', content: '返回丘北县城住宿' } + ], + foods: ['壮族五色糯米饭', '河鲜', '农家土鸡'], + hotel: '丘北县城酒店', + tips: '坝美需乘船进入,轻装出行,贵重物品防水。' + }, + { + id: 'p3', name: '大理', lat: 25.69, lng: 100.26, + day: 'Day 3', badge: 'D3', km: '520km', driveTime: '6.5h', + desc: '丘北 → 大理,苍山洱海、古城漫步', icon: '🏔️', + images: [ + 'https://picsum.photos/400/300?random=8', + 'https://picsum.photos/400/300?random=9', + 'https://picsum.photos/400/300?random=10' + ], + heroImage: 'https://picsum.photos/800/400?random=8', + schedule: [ + { time: '上午', content: '丘北出发,沿高速前往大理(车程较长)' }, + { time: '中午', content: '途经石林,可短暂停留午餐' }, + { time: '下午', content: '抵达大理,入住酒店,逛大理古城' }, + { time: '晚上', content: '古城酒吧街或人民路夜市' } + ], + foods: ['乳扇', '喜洲粑粑', '酸辣鱼', '生皮'], + hotel: '大理古城周边酒店', + tips: '车程较长建议中途休息,注意高反。' + }, + { + id: 'p4', name: '丽江', lat: 26.87, lng: 100.23, + day: 'Day 4', badge: 'D4', km: '180km', driveTime: '2.5h', + desc: '大理 → 丽江,丽江古城、玉龙雪山', icon: '🏯', + images: [ + 'https://picsum.photos/400/300?random=11', + 'https://picsum.photos/400/300?random=12', + 'https://picsum.photos/400/300?random=13' + ], + heroImage: 'https://picsum.photos/800/400?random=11', + schedule: [ + { time: '上午', content: '大理出发,沿大丽高速前往丽江' }, + { time: '中午', content: '抵达丽江,入住古城客栈' }, + { time: '下午', content: '游丽江古城、木府、黑龙潭' }, + { time: '晚上', content: '古城夜景,酒吧街体验纳西文化' } + ], + foods: ['腊排骨火锅', '鸡豆凉粉', '纳西烤鱼', '酥油茶'], + hotel: '丽江古城内客栈', + tips: '丽江古城维护费已取消,玉龙雪山需提前购票。' + }, + { + id: 'p5', name: '返程', lat: 25.04, lng: 102.71, + day: '返程日', badge: 'END', km: '返程', driveTime: '—', + desc: '丽江机场还车,飞返北京', icon: '✈️', + images: [], + heroImage: '', + schedule: [ + { time: '上午', content: '丽江机场还车,办理登机' }, + { time: '全天', content: '飞返北京,结束愉快旅程' } + ], + foods: [], + hotel: '', + tips: '提前2小时到达机场,预留还车时间。' + } + ] +} + +export const replacementPool = { + '普者黑': [ + { id: 'r1', name: '弥勒', lat: 24.41, lng: 103.41, icon: '♨️', desc: '温泉小镇,东风韵艺术小镇' }, + { id: 'r2', name: '抚仙湖', lat: 24.55, lng: 102.93, icon: '🏖️', desc: '云南最深湖泊,水质清澈' }, + { id: 'r3', name: '建水古城', lat: 23.62, lng: 102.82, icon: '🏛️', desc: '千年古城,建水豆腐' } + ], + '坝美': [ + { id: 'r4', name: '元阳梯田', lat: 23.22, lng: 102.83, icon: '🌾', desc: '世界遗产,哈尼梯田' }, + { id: 'r5', name: '蒙自', lat: 23.37, lng: 103.38, icon: '🍜', desc: '过桥米线发源地' } + ], + '大理': [ + { id: 'r6', name: '沙溪古镇', lat: 26.32, lng: 99.85, icon: '🏘️', desc: '茶马古道重镇,宁静古朴' }, + { id: 'r7', name: '巍山古城', lat: 25.23, lng: 100.30, icon: '🏛️', desc: '南诏故都,小吃天堂' } + ], + '丽江': [ + { id: 'r8', name: '香格里拉', lat: 27.83, lng: 99.70, icon: '🏔️', desc: '高原秘境,松赞林寺' }, + { id: 'r9', name: '泸沽湖', lat: 27.72, lng: 100.82, icon: '💧', desc: '摩梭人走婚,湖光山色' } + ] +} + +// AI 对话方案模板 +export const aiTemplates = [ + { + badge: '方案A', + name: '经典环线:昆明-普者黑-大理-丽江', + km: '~1450km', + driveTime: '14h', + budget: '¥4000', + highlights: ['喀斯特地貌', '苍山洱海', '古城文化', '玉龙雪山'], + route: '昆明 → 普者黑 → 坝美 → 大理 → 丽江' + }, + { + badge: '方案B', + name: '南线深度:昆明-建水-元阳-普者黑', + km: '~980km', + driveTime: '11h', + budget: '¥3200', + highlights: ['元阳梯田', '建水古城', '抚仙湖', '少打卡多体验'], + route: '昆明 → 抚仙湖 → 建水 → 元阳 → 普者黑' + }, + { + badge: '方案C', + name: '西线探险:昆明-大理-沙溪-香格里拉', + km: '~1200km', + driveTime: '16h', + budget: '¥4800', + highlights: ['茶马古道', '高原秘境', '松赞林寺', '纳帕海'], + route: '昆明 → 大理 → 沙溪 → 香格里拉 → 返程' + } +] diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..92378b9 --- /dev/null +++ b/src/main.js @@ -0,0 +1,9 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import router from './router' +import App from './App.vue' + +const app = createApp(App) +app.use(createPinia()) +app.use(router) +app.mount('#app') diff --git a/src/router.js b/src/router.js new file mode 100644 index 0000000..0cb27e4 --- /dev/null +++ b/src/router.js @@ -0,0 +1,15 @@ +import { createRouter, createWebHistory } from 'vue-router' +import AppView from './App.vue' +import SettingsPage from './views/SettingsPage.vue' + +const routes = [ + { path: '/', component: AppView }, + { path: '/settings', component: SettingsPage } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router diff --git a/src/services/routeService.js b/src/services/routeService.js new file mode 100644 index 0000000..4e2c637 --- /dev/null +++ b/src/services/routeService.js @@ -0,0 +1,54 @@ +const OSRM_BASE = 'https://router.project-osrm.org/route/v1/driving' + +export async function getRoute(fromPoint, toPoint) { + const url = `${OSRM_BASE}/${fromPoint[1]},${fromPoint[0]};${toPoint[1]},${toPoint[0]}?overview=full&geometries=geojson` + try { + const response = await fetch(url) + const data = await response.json() + if (data.code !== 'Ok' || !data.routes || data.routes.length === 0) return null + const route = data.routes[0] + const coords = route.geometry.coordinates.map(c => [c[1], c[0]]) + return { coords, distance: Math.round(route.distance / 1000), duration: Math.round(route.duration / 60) } + } catch (e) { + console.warn('OSRM routing failed, fallback to straight line:', e) + return null + } +} + +export async function getMultiRoute(points) { + if (points.length < 2) return [] + const segments = [] + for (let i = 0; i < points.length - 1; i++) { + const from = [points[i].lat, points[i].lng] + const to = [points[i + 1].lat, points[i + 1].lng] + const route = await getRoute(from, to) + if (route) { + segments.push(route) + } else { + segments.push({ coords: [from, to], distance: calcDist(from, to), duration: Math.round(calcDist(from, to) / 60 * 2) }) + } + } + return segments +} + +function calcDist(from, to) { + const R = 6371 + const dLat = (to[0] - from[0]) * Math.PI / 180 + const dLng = (to[1] - from[1]) * Math.PI / 180 + const a = Math.sin(dLat/2)**2 + Math.cos(from[0]*Math.PI/180)*Math.cos(to[0]*Math.PI/180)*Math.sin(dLng/2)**2 + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) +} + +export function detectDetour(segments) { + const detours = [] + for (let i = 0; i < segments.length; i++) { + const seg = segments[i] + if (!seg || !seg.coords || seg.coords.length < 2) continue + const straightDist = calcDist([seg.coords[0][0], seg.coords[0][1]], [seg.coords[seg.coords.length-1][0], seg.coords[seg.coords.length-1][1]]) + if (straightDist > 0 && seg.distance / straightDist > 1.5 && seg.distance > 100) { + const ratio = seg.distance / straightDist + detours.push({ index: i, ratio: Math.round(ratio * 100), message: `第${i + 1}段路线可能绕路,实际距离是直线距离的${Math.round((ratio - 1) * 100)}%` }) + } + } + return detours +} diff --git a/src/stores/itinerary.js b/src/stores/itinerary.js new file mode 100644 index 0000000..785bc7c --- /dev/null +++ b/src/stores/itinerary.js @@ -0,0 +1,196 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { sampleItinerary, replacementPool } from '../data/sampleData' +import { getMultiRoute, detectDetour } from '../services/routeService' + +export const useItineraryStore = defineStore('itinerary', () => { + const phase = ref('workbench') + const mode = ref('preview') + const points = ref(JSON.parse(JSON.stringify(sampleItinerary.points))) + const currentStep = ref(0) + const selectedForReplace = ref(null) + const showReplacementPanel = ref(false) + const showOptimizationAlert = ref(false) + const optimizationMessage = ref('') + const isFetchingRoutes = ref(false) + const routeSegments = ref([]) + const detourWarnings = ref([]) + const hoveredReplacement = ref(null) + + const startPoint = computed(() => points.value.find(p => p.badge === 'START')) + const endPoint = computed(() => points.value.find(p => p.badge === 'END')) + + const totalKm = computed(() => { + if (routeSegments.value.length > 0) { + return routeSegments.value.reduce((sum, s) => sum + (s ? s.distance : 0), 0) + } + let km = 0 + points.value.forEach(p => { const n = parseInt(p.km); if (!isNaN(n)) km += n }) + return km + }) + + const totalDriveTime = computed(() => { + if (routeSegments.value.length > 0) { + const totalMin = routeSegments.value.reduce((sum, s) => sum + (s ? s.duration : 0), 0) + return Math.round(totalMin / 60 * 10) / 10 + } + let hours = 0 + points.value.forEach(p => { + if (p.driveTime && p.driveTime !== '—') { + const parts = p.driveTime.replace('h', '').split('.') + hours += parseInt(parts[0]) || 0 + if (parts[1]) hours += parseInt(parts[1]) * 0.1 + } + }) + return Math.round(hours * 10) / 10 + }) + + const travelDays = computed(() => { + return points.value.filter(p => p.day && !['START', 'END'].includes(p.badge)).length + }) + + const currentPoint = computed(() => points.value[currentStep.value] || null) + + const replacementOptions = computed(() => { + if (selectedForReplace.value === null) return [] + const point = points.value[selectedForReplace.value] + if (!point) return [] + return replacementPool[point.name] || [] + }) + + function setPhase(p) { phase.value = p } + function setMode(m) { mode.value = m } + function setCurrentStep(idx) { if (idx >= 0 && idx < points.value.length) currentStep.value = idx } + function nextStep() { if (currentStep.value < points.value.length - 1) currentStep.value++ } + function prevStep() { if (currentStep.value > 0) currentStep.value-- } + + function deletePoint(index) { + if (index <= 0 || index >= points.value.length - 1) return + points.value.splice(index, 1) + routeSegments.value = [] + if (currentStep.value >= points.value.length) currentStep.value = points.value.length - 1 + checkRouteOptimization() + } + + function insertPoint(beforeIndex, newPoint) { + points.value.splice(beforeIndex, 0, { + id: 'p_' + Date.now(), name: newPoint.name, lat: newPoint.lat, lng: newPoint.lng, + day: '', badge: '', km: '', driveTime: '', desc: newPoint.desc, + icon: newPoint.icon || '📍', images: [], heroImage: '', + schedule: [], foods: [], hotel: '', tips: '' + }) + routeSegments.value = [] + checkRouteOptimization() + } + + function replacePoint(index, newPoint) { + if (index < 0 || index >= points.value.length) return + const old = points.value[index] + points.value[index] = { ...old, name: newPoint.name, lat: newPoint.lat, lng: newPoint.lng, desc: newPoint.desc, icon: newPoint.icon || '📍' } + selectedForReplace.value = null + showReplacementPanel.value = false + routeSegments.value = [] + checkRouteOptimization() + } + + function reorderPoints(fromIndex, toIndex) { + if (fromIndex === toIndex) return + if (fromIndex <= 0 || fromIndex >= points.value.length - 1) return + const item = points.value.splice(fromIndex, 1)[0] + points.value.splice(toIndex, 0, item) + currentStep.value = toIndex + routeSegments.value = [] + checkRouteOptimization() + } + + async function fetchRealRoutes() { + if (isFetchingRoutes.value) return + isFetchingRoutes.value = true + try { + routeSegments.value = await getMultiRoute(points.value) + detourWarnings.value = detectDetour(routeSegments.value) + if (detourWarnings.value.length > 0) { + showOptimizationAlert.value = true + optimizationMessage.value = detourWarnings.value[0].message + } else { + showOptimizationAlert.value = false + } + } catch (e) { + console.error('Failed to fetch routes:', e) + routeSegments.value = [] + } finally { + isFetchingRoutes.value = false + } + } + + function checkRouteOptimization() { + if (points.value.length < 3) { + showOptimizationAlert.value = false + return + } + for (let i = 1; i < points.value.length - 1; i++) { + const prev = points.value[i - 1], curr = points.value[i], next = points.value[i + 1] + const distPrev = calcDist(prev.lat, prev.lng, curr.lat, curr.lng) + const distNext = calcDist(curr.lat, curr.lng, next.lat, next.lng) + const distDirect = calcDist(prev.lat, prev.lng, next.lat, next.lng) + if (distPrev + distNext > distDirect * 1.8 && distDirect > 50) { + showOptimizationAlert.value = true + optimizationMessage.value = `检测到「${curr.name}」可能使路线绕路,是否优化?` + return + } + } + showOptimizationAlert.value = false + } + + function autoOptimizeRoute() { + if (points.value.length < 3) return + const middle = points.value.slice(1, -1) + const start = points.value[0] + const end = points.value[points.value.length - 1] + middle.sort((a, b) => { + const da = Math.sqrt((a.lat - start.lat)**2 + (a.lng - start.lng)**2) + const db = Math.sqrt((b.lat - start.lat)**2 + (b.lng - start.lng)**2) + return da - db + }) + points.value = [start, ...middle, end] + currentStep.value = 0 + routeSegments.value = [] + showOptimizationAlert.value = false + } + + function dismissOptimization() { + showOptimizationAlert.value = false + } + + function resetToSample() { + points.value = JSON.parse(JSON.stringify(sampleItinerary.points)) + currentStep.value = 0 + routeSegments.value = [] + showOptimizationAlert.value = false + showReplacementPanel.value = false + selectedForReplace.value = null + hoveredReplacement.value = null + } + + function setHoveredReplacement(opt) { + hoveredReplacement.value = opt + } + + return { + phase, mode, points, currentStep, selectedForReplace, showReplacementPanel, + showOptimizationAlert, optimizationMessage, isFetchingRoutes, routeSegments, detourWarnings, + hoveredReplacement, startPoint, endPoint, totalKm, totalDriveTime, travelDays, currentPoint, replacementOptions, + setPhase, setMode, setCurrentStep, nextStep, prevStep, + deletePoint, insertPoint, replacePoint, reorderPoints, resetToSample, + fetchRealRoutes, checkRouteOptimization, autoOptimizeRoute, dismissOptimization, + setHoveredReplacement + } +}) + +function calcDist(lat1, lng1, lat2, lng2) { + const R = 6371 + const dLat = (lat2 - lat1) * Math.PI / 180 + const dLng = (lng2 - lng1) * Math.PI / 180 + const a = Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLng/2)**2 + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) +} diff --git a/src/stores/settings.js b/src/stores/settings.js new file mode 100644 index 0000000..6a88d52 --- /dev/null +++ b/src/stores/settings.js @@ -0,0 +1,104 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +const STORAGE_KEY = 'trip_planner_ai_config' + +const DEFAULT_CONFIG = { + provider: 'aliyun', + apiKey: '', + model: 'qwen-plus', + baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', + temperature: 0.7, + maxTokens: 2000 +} + +const ALIYUN_MODELS = [ + { value: 'qwen-turbo', label: 'Qwen-Turbo(快速)', desc: '速度快,成本低,适合简单对话' }, + { value: 'qwen-plus', label: 'Qwen-Plus(均衡)', desc: '速度与质量平衡,推荐使用' }, + { value: 'qwen-max', label: 'Qwen-Max(高质量)', desc: '复杂推理,质量最高' }, + { value: 'qwen-max-latest', label: 'Qwen-Max-Latest', desc: '最新版本,能力最强' }, + { value: 'qwen-long', label: 'Qwen-Long(长文本)', desc: '支持超长上下文(100万token)' } +] + +export const useSettingsStore = defineStore('settings', () => { + const config = ref(loadConfig()) + const isConfigured = computed(() => !!config.value.apiKey) + const isTesting = ref(false) + const testResult = ref(null) + const showConfigPage = ref(false) + + function loadConfig() { + try { + const saved = localStorage.getItem(STORAGE_KEY) + if (saved) return { ...DEFAULT_CONFIG, ...JSON.parse(saved) } + } catch (e) { + console.warn('Failed to load config:', e) + } + return { ...DEFAULT_CONFIG } + } + + function saveConfig() { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(config.value)) + } catch (e) { + console.warn('Failed to save config:', e) + } + } + + function updateConfig(updates) { + config.value = { ...config.value, ...updates } + saveConfig() + } + + function resetConfig() { + config.value = { ...DEFAULT_CONFIG, apiKey: '' } + saveConfig() + } + + async function testConnection() { + isTesting.value = true + testResult.value = null + + if (!config.value.apiKey) { + testResult.value = { success: false, message: '请先输入 API Key' } + isTesting.value = false + return false + } + + try { + const response = await fetch(`${config.value.baseUrl}/chat/completions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${config.value.apiKey}` + }, + body: JSON.stringify({ + model: config.value.model, + messages: [{ role: 'user', content: '你好,请回复OK' }], + max_tokens: 10 + }) + }) + + const data = await response.json() + + if (response.ok && data.choices) { + testResult.value = { success: true, message: `连接成功!模型: ${data.model || config.value.model}` } + saveConfig() + return true + } else { + testResult.value = { success: false, message: data.error?.message || '连接失败' } + return false + } + } catch (error) { + testResult.value = { success: false, message: `网络错误: ${error.message}` } + return false + } finally { + isTesting.value = false + } + } + + return { + config, isConfigured, isTesting, testResult, showConfigPage, ALIYUN_MODELS, + updateConfig, resetConfig, testConnection + } +}) diff --git a/src/utils/exporter.js b/src/utils/exporter.js new file mode 100644 index 0000000..2866995 --- /dev/null +++ b/src/utils/exporter.js @@ -0,0 +1,267 @@ +export function generateStaticHTML(itineraryData) { + const points = itineraryData.points + + const pointsJSON = JSON.stringify(points) + + return ` + + + + +${itineraryData.title || '行程规划'} + + + + + +
+
+

${itineraryData.icon || '🚗'} ${itineraryData.title || '行程规划'}

+
${itineraryData.subtitle || ''}
+
+
+
+
+
+ +
+ + +
+
+
+
+ + +` +} diff --git a/src/views/SettingsPage.vue b/src/views/SettingsPage.vue new file mode 100644 index 0000000..98791d1 --- /dev/null +++ b/src/views/SettingsPage.vue @@ -0,0 +1,500 @@ + + + + + diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..0532775 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + base: './', + server: { + historyApiFallback: true + } +})