This commit is contained in:
2026-03-06 17:06:40 +07:00
commit 1d7781ab4d
24 changed files with 1961 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -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?

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>im-app-store</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

23
package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "im-app-store",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.25",
"vue-router": "^4.6.4"
},
"devDependencies": {
"@types/node": "^24.10.1",
"@vitejs/plugin-vue": "^6.0.2",
"@vue/tsconfig": "^0.8.1",
"typescript": "~5.9.3",
"vite": "^7.3.1",
"vue-tsc": "^3.1.5"
}
}

957
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,957 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
vue:
specifier: ^3.5.25
version: 3.5.29(typescript@5.9.3)
vue-router:
specifier: ^4.6.4
version: 4.6.4(vue@3.5.29(typescript@5.9.3))
devDependencies:
'@types/node':
specifier: ^24.10.1
version: 24.12.0
'@vitejs/plugin-vue':
specifier: ^6.0.2
version: 6.0.4(vite@7.3.1(@types/node@24.12.0))(vue@3.5.29(typescript@5.9.3))
'@vue/tsconfig':
specifier: ^0.8.1
version: 0.8.1(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3))
typescript:
specifier: ~5.9.3
version: 5.9.3
vite:
specifier: ^7.3.1
version: 7.3.1(@types/node@24.12.0)
vue-tsc:
specifier: ^3.1.5
version: 3.2.5(typescript@5.9.3)
packages:
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.28.5':
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.29.0':
resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/types@7.29.0':
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
'@esbuild/aix-ppc64@0.27.3':
resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.27.3':
resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.27.3':
resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.27.3':
resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.27.3':
resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.27.3':
resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.27.3':
resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.3':
resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.27.3':
resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.27.3':
resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.27.3':
resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.27.3':
resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.27.3':
resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.27.3':
resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.27.3':
resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.27.3':
resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.27.3':
resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.27.3':
resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.3':
resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.27.3':
resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.3':
resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.27.3':
resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.27.3':
resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.27.3':
resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.27.3':
resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.27.3':
resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@rolldown/pluginutils@1.0.0-rc.2':
resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==}
'@rollup/rollup-android-arm-eabi@4.59.0':
resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.59.0':
resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.59.0':
resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.59.0':
resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.59.0':
resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.59.0':
resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.59.0':
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.59.0':
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loong64-gnu@4.59.0':
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-loong64-musl@4.59.0':
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-ppc64-musl@4.59.0':
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.59.0':
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.59.0':
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.59.0':
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.59.0':
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-openbsd-x64@4.59.0':
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.59.0':
resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.59.0':
resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.59.0':
resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.59.0':
resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.59.0':
resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
cpu: [x64]
os: [win32]
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/node@24.12.0':
resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==}
'@vitejs/plugin-vue@6.0.4':
resolution: {integrity: sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==}
engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies:
vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.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.29':
resolution: {integrity: sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==}
'@vue/compiler-dom@3.5.29':
resolution: {integrity: sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==}
'@vue/compiler-sfc@3.5.29':
resolution: {integrity: sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA==}
'@vue/compiler-ssr@3.5.29':
resolution: {integrity: sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==}
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
'@vue/language-core@3.2.5':
resolution: {integrity: sha512-d3OIxN/+KRedeM5wQ6H6NIpwS3P5gC9nmyaHgBk+rO6dIsjY+tOh4UlPpiZbAh3YtLdCGEX4M16RmsBqPmJV+g==}
'@vue/reactivity@3.5.29':
resolution: {integrity: sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA==}
'@vue/runtime-core@3.5.29':
resolution: {integrity: sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg==}
'@vue/runtime-dom@3.5.29':
resolution: {integrity: sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg==}
'@vue/server-renderer@3.5.29':
resolution: {integrity: sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g==}
peerDependencies:
vue: 3.5.29
'@vue/shared@3.5.29':
resolution: {integrity: sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==}
'@vue/tsconfig@0.8.1':
resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==}
peerDependencies:
typescript: 5.x
vue: ^3.4.0
peerDependenciesMeta:
typescript:
optional: true
vue:
optional: true
alien-signals@3.1.2:
resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
esbuild@0.27.3:
resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
engines: {node: '>=18'}
hasBin: true
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
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==}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
rollup@4.59.0:
resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
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'}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
jiti: '>=1.21.0'
less: ^4.0.0
lightningcss: ^1.21.0
sass: ^1.70.0
sass-embedded: ^1.70.0
stylus: '>=0.54.8'
sugarss: ^5.0.0
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
vue-router@4.6.4:
resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==}
peerDependencies:
vue: ^3.5.0
vue-tsc@3.2.5:
resolution: {integrity: sha512-/htfTCMluQ+P2FISGAooul8kO4JMheOTCbCy4M6dYnYYjqLe3BExZudAua6MSIKSFYQtFOYAll7XobYwcpokGA==}
hasBin: true
peerDependencies:
typescript: '>=5.0.0'
vue@3.5.29:
resolution: {integrity: sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
snapshots:
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.28.5': {}
'@babel/parser@7.29.0':
dependencies:
'@babel/types': 7.29.0
'@babel/types@7.29.0':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
'@esbuild/aix-ppc64@0.27.3':
optional: true
'@esbuild/android-arm64@0.27.3':
optional: true
'@esbuild/android-arm@0.27.3':
optional: true
'@esbuild/android-x64@0.27.3':
optional: true
'@esbuild/darwin-arm64@0.27.3':
optional: true
'@esbuild/darwin-x64@0.27.3':
optional: true
'@esbuild/freebsd-arm64@0.27.3':
optional: true
'@esbuild/freebsd-x64@0.27.3':
optional: true
'@esbuild/linux-arm64@0.27.3':
optional: true
'@esbuild/linux-arm@0.27.3':
optional: true
'@esbuild/linux-ia32@0.27.3':
optional: true
'@esbuild/linux-loong64@0.27.3':
optional: true
'@esbuild/linux-mips64el@0.27.3':
optional: true
'@esbuild/linux-ppc64@0.27.3':
optional: true
'@esbuild/linux-riscv64@0.27.3':
optional: true
'@esbuild/linux-s390x@0.27.3':
optional: true
'@esbuild/linux-x64@0.27.3':
optional: true
'@esbuild/netbsd-arm64@0.27.3':
optional: true
'@esbuild/netbsd-x64@0.27.3':
optional: true
'@esbuild/openbsd-arm64@0.27.3':
optional: true
'@esbuild/openbsd-x64@0.27.3':
optional: true
'@esbuild/openharmony-arm64@0.27.3':
optional: true
'@esbuild/sunos-x64@0.27.3':
optional: true
'@esbuild/win32-arm64@0.27.3':
optional: true
'@esbuild/win32-ia32@0.27.3':
optional: true
'@esbuild/win32-x64@0.27.3':
optional: true
'@jridgewell/sourcemap-codec@1.5.5': {}
'@rolldown/pluginutils@1.0.0-rc.2': {}
'@rollup/rollup-android-arm-eabi@4.59.0':
optional: true
'@rollup/rollup-android-arm64@4.59.0':
optional: true
'@rollup/rollup-darwin-arm64@4.59.0':
optional: true
'@rollup/rollup-darwin-x64@4.59.0':
optional: true
'@rollup/rollup-freebsd-arm64@4.59.0':
optional: true
'@rollup/rollup-freebsd-x64@4.59.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-loong64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.59.0':
optional: true
'@rollup/rollup-openbsd-x64@4.59.0':
optional: true
'@rollup/rollup-openharmony-arm64@4.59.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.59.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.59.0':
optional: true
'@rollup/rollup-win32-x64-gnu@4.59.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.59.0':
optional: true
'@types/estree@1.0.8': {}
'@types/node@24.12.0':
dependencies:
undici-types: 7.16.0
'@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@24.12.0))(vue@3.5.29(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2
vite: 7.3.1(@types/node@24.12.0)
vue: 3.5.29(typescript@5.9.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.1.0
'@vue/compiler-core@3.5.29':
dependencies:
'@babel/parser': 7.29.0
'@vue/shared': 3.5.29
entities: 7.0.1
estree-walker: 2.0.2
source-map-js: 1.2.1
'@vue/compiler-dom@3.5.29':
dependencies:
'@vue/compiler-core': 3.5.29
'@vue/shared': 3.5.29
'@vue/compiler-sfc@3.5.29':
dependencies:
'@babel/parser': 7.29.0
'@vue/compiler-core': 3.5.29
'@vue/compiler-dom': 3.5.29
'@vue/compiler-ssr': 3.5.29
'@vue/shared': 3.5.29
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.8
source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.29':
dependencies:
'@vue/compiler-dom': 3.5.29
'@vue/shared': 3.5.29
'@vue/devtools-api@6.6.4': {}
'@vue/language-core@3.2.5':
dependencies:
'@volar/language-core': 2.4.28
'@vue/compiler-dom': 3.5.29
'@vue/shared': 3.5.29
alien-signals: 3.1.2
muggle-string: 0.4.1
path-browserify: 1.0.1
picomatch: 4.0.3
'@vue/reactivity@3.5.29':
dependencies:
'@vue/shared': 3.5.29
'@vue/runtime-core@3.5.29':
dependencies:
'@vue/reactivity': 3.5.29
'@vue/shared': 3.5.29
'@vue/runtime-dom@3.5.29':
dependencies:
'@vue/reactivity': 3.5.29
'@vue/runtime-core': 3.5.29
'@vue/shared': 3.5.29
csstype: 3.2.3
'@vue/server-renderer@3.5.29(vue@3.5.29(typescript@5.9.3))':
dependencies:
'@vue/compiler-ssr': 3.5.29
'@vue/shared': 3.5.29
vue: 3.5.29(typescript@5.9.3)
'@vue/shared@3.5.29': {}
'@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3))':
optionalDependencies:
typescript: 5.9.3
vue: 3.5.29(typescript@5.9.3)
alien-signals@3.1.2: {}
csstype@3.2.3: {}
entities@7.0.1: {}
esbuild@0.27.3:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.3
'@esbuild/android-arm': 0.27.3
'@esbuild/android-arm64': 0.27.3
'@esbuild/android-x64': 0.27.3
'@esbuild/darwin-arm64': 0.27.3
'@esbuild/darwin-x64': 0.27.3
'@esbuild/freebsd-arm64': 0.27.3
'@esbuild/freebsd-x64': 0.27.3
'@esbuild/linux-arm': 0.27.3
'@esbuild/linux-arm64': 0.27.3
'@esbuild/linux-ia32': 0.27.3
'@esbuild/linux-loong64': 0.27.3
'@esbuild/linux-mips64el': 0.27.3
'@esbuild/linux-ppc64': 0.27.3
'@esbuild/linux-riscv64': 0.27.3
'@esbuild/linux-s390x': 0.27.3
'@esbuild/linux-x64': 0.27.3
'@esbuild/netbsd-arm64': 0.27.3
'@esbuild/netbsd-x64': 0.27.3
'@esbuild/openbsd-arm64': 0.27.3
'@esbuild/openbsd-x64': 0.27.3
'@esbuild/openharmony-arm64': 0.27.3
'@esbuild/sunos-x64': 0.27.3
'@esbuild/win32-arm64': 0.27.3
'@esbuild/win32-ia32': 0.27.3
'@esbuild/win32-x64': 0.27.3
estree-walker@2.0.2: {}
fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
fsevents@2.3.3:
optional: true
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
muggle-string@0.4.1: {}
nanoid@3.3.11: {}
path-browserify@1.0.1: {}
picocolors@1.1.1: {}
picomatch@4.0.3: {}
postcss@8.5.8:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
rollup@4.59.0:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.59.0
'@rollup/rollup-android-arm64': 4.59.0
'@rollup/rollup-darwin-arm64': 4.59.0
'@rollup/rollup-darwin-x64': 4.59.0
'@rollup/rollup-freebsd-arm64': 4.59.0
'@rollup/rollup-freebsd-x64': 4.59.0
'@rollup/rollup-linux-arm-gnueabihf': 4.59.0
'@rollup/rollup-linux-arm-musleabihf': 4.59.0
'@rollup/rollup-linux-arm64-gnu': 4.59.0
'@rollup/rollup-linux-arm64-musl': 4.59.0
'@rollup/rollup-linux-loong64-gnu': 4.59.0
'@rollup/rollup-linux-loong64-musl': 4.59.0
'@rollup/rollup-linux-ppc64-gnu': 4.59.0
'@rollup/rollup-linux-ppc64-musl': 4.59.0
'@rollup/rollup-linux-riscv64-gnu': 4.59.0
'@rollup/rollup-linux-riscv64-musl': 4.59.0
'@rollup/rollup-linux-s390x-gnu': 4.59.0
'@rollup/rollup-linux-x64-gnu': 4.59.0
'@rollup/rollup-linux-x64-musl': 4.59.0
'@rollup/rollup-openbsd-x64': 4.59.0
'@rollup/rollup-openharmony-arm64': 4.59.0
'@rollup/rollup-win32-arm64-msvc': 4.59.0
'@rollup/rollup-win32-ia32-msvc': 4.59.0
'@rollup/rollup-win32-x64-gnu': 4.59.0
'@rollup/rollup-win32-x64-msvc': 4.59.0
fsevents: 2.3.3
source-map-js@1.2.1: {}
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
typescript@5.9.3: {}
undici-types@7.16.0: {}
vite@7.3.1(@types/node@24.12.0):
dependencies:
esbuild: 0.27.3
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.8
rollup: 4.59.0
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.12.0
fsevents: 2.3.3
vscode-uri@3.1.0: {}
vue-router@4.6.4(vue@3.5.29(typescript@5.9.3)):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.29(typescript@5.9.3)
vue-tsc@3.2.5(typescript@5.9.3):
dependencies:
'@volar/typescript': 2.4.28
'@vue/language-core': 3.2.5
typescript: 5.9.3
vue@3.5.29(typescript@5.9.3):
dependencies:
'@vue/compiler-dom': 3.5.29
'@vue/compiler-sfc': 3.5.29
'@vue/runtime-dom': 3.5.29
'@vue/server-renderer': 3.5.29(vue@3.5.29(typescript@5.9.3))
'@vue/shared': 3.5.29
optionalDependencies:
typescript: 5.9.3

1
public/vite.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

41
src/App.vue Normal file
View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { RouterView } from "vue-router";
import { provideLocaleStore } from "./composables/useLocaleStore";
const { locale, switchLocale, t } = provideLocaleStore();
</script>
<template>
<div class="app-shell">
<header class="topbar">
<div class="brand">
<span class="brand-dot" />
<span>{{ t("应用商店", "App Store") }}</span>
</div>
<div class="lang-switch">
<button
:class="{ active: locale === 'zh-CN' }"
@click="switchLocale('zh-CN')"
>
中文
</button>
<button
:class="{ active: locale === 'en-US' }"
@click="switchLocale('en-US')"
>
EN
</button>
</div>
</header>
<main class="page-wrap">
<RouterView />
</main>
<footer class="footer">
{{ t("应用数据演示版本", "Demo app catalog data") }}
</footer>
</div>
</template>

1
src/assets/vue.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@@ -0,0 +1,48 @@
import { computed, inject, provide, ref, type Ref } from 'vue'
import type { Locale } from '../types/app'
const LOCALE_KEY = Symbol('locale-store')
interface LocaleStore {
locale: Ref<Locale>
switchLocale: (next: Locale) => void
t: (zh: string, en: string) => string
}
const savedLocale = localStorage.getItem('im-app-store-locale')
const defaultLocale: Locale = savedLocale === 'en-US' ? 'en-US' : 'zh-CN'
export function provideLocaleStore() {
const locale = ref<Locale>(defaultLocale)
const switchLocale = (next: Locale) => {
locale.value = next
localStorage.setItem('im-app-store-locale', next)
}
const t = (zh: string, en: string) => (locale.value === 'zh-CN' ? zh : en)
const store: LocaleStore = {
locale,
switchLocale,
t,
}
provide(LOCALE_KEY, store)
return {
...store,
isZh: computed(() => locale.value === 'zh-CN'),
}
}
export function useLocaleStore() {
const store = inject<LocaleStore>(LOCALE_KEY)
if (!store) {
throw new Error('Locale store is not provided')
}
return store
}

42
src/data/apps.json Normal file
View File

@@ -0,0 +1,42 @@
[
{
"id": "amap",
"name": "高德地图",
"icon": "https://pp.myapp.com/ma_icon/0/icon_7678_1765965797/256",
"shortDescription": {
"zh-CN": "专业地图导航服务",
"en-US": "Professional Map & Navigation"
},
"description": {
"zh-CN": "高德地图是中国领先的数字地图内容、导航和位置服务解决方案提供商,拥有导航电子地图甲级测绘资质。",
"en-US": "AutoNavi is China's leading provider of digital map content, navigation and location services."
},
"category": "tools",
"version": "13.19.0",
"buildNumber": "1319",
"releaseDate": "2025-12-30",
"releaseNotes": {
"zh-CN": ["地图数据更新", "导航功能优化", "定位精度提升", "性能优化"],
"en-US": [
"Map data updated",
"Navigation improved",
"Enhanced location accuracy",
"Performance optimization"
]
},
"downloads": {
"ios": "https://mobile.amap.com/",
"android": "https://mobile.amap.com/"
},
"size": {
"ios": "298 MB",
"android": "265 MB"
},
"stats": {
"total": 720000,
"today": 1850,
"ios": 420000,
"android": 300000
}
}
]

15
src/data/apps.ts Normal file
View File

@@ -0,0 +1,15 @@
import type { AppItem, Locale } from '../types/app'
import appsJson from './apps.json'
export const apps: AppItem[] = appsJson as AppItem[]
export const categoryLabel: Record<string, Record<Locale, string>> = {
all: {
'zh-CN': '全部',
'en-US': 'All',
},
tools: {
'zh-CN': '工具',
'en-US': 'Tools',
},
}

6
src/main.ts Normal file
View File

@@ -0,0 +1,6 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { router } from './router'
createApp(App).use(router).mount('#app')

20
src/router/index.ts Normal file
View File

@@ -0,0 +1,20 @@
import { createRouter, createWebHistory } from 'vue-router'
import AppDetail from '../views/AppDetail.vue'
import StoreHome from '../views/StoreHome.vue'
export const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: StoreHome,
},
{
path: '/app/:id',
name: 'app-detail',
component: AppDetail,
},
],
})

412
src/style.css Normal file
View File

@@ -0,0 +1,412 @@
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;700;800&display=swap');
:root {
--bg: #f5f7f4;
--bg-accent: #eaf1eb;
--panel: #ffffff;
--text: #102a1c;
--muted: #5d7468;
--line: #d8e3dc;
--brand: #0f8a5f;
--brand-deep: #0d6f4d;
--tag: #e6f4ec;
--shadow: 0 18px 40px rgba(13, 79, 57, 0.1);
--radius-lg: 20px;
--radius-md: 14px;
--radius-sm: 10px;
}
* {
box-sizing: border-box;
}
html,
body,
#app {
margin: 0;
min-height: 100%;
}
body {
min-width: 320px;
font-family: 'Manrope', sans-serif;
color: var(--text);
background:
radial-gradient(circle at 10% 10%, #d9f2e3 0%, transparent 30%),
radial-gradient(circle at 90% 20%, #e7f3ff 0%, transparent 25%),
linear-gradient(160deg, var(--bg) 0%, var(--bg-accent) 100%);
}
a {
color: inherit;
text-decoration: none;
}
.app-shell {
max-width: 1080px;
margin: 0 auto;
padding: 28px 20px 34px;
animation: fade-in 0.55s ease;
}
.topbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-weight: 800;
letter-spacing: 0.2px;
}
.brand-dot {
width: 12px;
height: 12px;
border-radius: 999px;
background: linear-gradient(120deg, #0c8e65, #3fcb99);
}
.lang-switch {
display: flex;
gap: 8px;
padding: 6px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.7);
border: 1px solid var(--line);
}
.lang-switch button {
border: 0;
border-radius: 999px;
padding: 8px 14px;
background: transparent;
color: var(--muted);
font-weight: 700;
cursor: pointer;
}
.lang-switch button.active {
background: var(--brand);
color: #ffffff;
}
.page-wrap {
display: block;
}
.footer {
margin-top: 26px;
color: var(--muted);
font-size: 13px;
text-align: center;
}
.hero-card,
.control-bar,
.app-card,
.detail-hero,
.detail-grid article,
.notes,
.not-found,
.empty {
background: var(--panel);
border: 1px solid rgba(16, 42, 28, 0.08);
box-shadow: var(--shadow);
border-radius: var(--radius-lg);
}
.hero-card {
padding: 28px;
margin-bottom: 16px;
}
.eyebrow {
margin: 0;
color: var(--brand);
font-size: 13px;
font-weight: 700;
letter-spacing: 0.06em;
}
.hero-card h1 {
margin: 8px 0;
font-size: clamp(28px, 4vw, 40px);
line-height: 1.08;
}
.hero-desc {
margin: 0;
color: var(--muted);
max-width: 620px;
}
.control-bar {
padding: 18px;
margin-bottom: 16px;
}
.search-box {
display: block;
margin-bottom: 12px;
}
.search-box span {
display: inline-block;
margin-bottom: 8px;
font-size: 13px;
color: var(--muted);
}
.search-box input {
width: 100%;
padding: 12px 14px;
border-radius: var(--radius-sm);
border: 1px solid var(--line);
outline: none;
font: inherit;
transition: border-color 0.2s ease;
}
.search-box input:focus {
border-color: var(--brand);
}
.chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip {
border: 1px solid var(--line);
border-radius: 999px;
background: #ffffff;
color: var(--muted);
padding: 8px 12px;
font-size: 13px;
font-weight: 700;
cursor: pointer;
}
.chip.active {
background: var(--tag);
color: var(--brand-deep);
border-color: #b7d4c5;
}
.app-grid {
display: grid;
gap: 12px;
}
.app-card {
display: grid;
grid-template-columns: 68px minmax(0, 1fr);
gap: 16px;
padding: 16px;
transition: transform 0.22s ease, box-shadow 0.22s ease;
}
.app-card:hover {
transform: translateY(-2px);
box-shadow: 0 22px 36px rgba(13, 79, 57, 0.14);
}
.app-icon,
.detail-icon {
width: 68px;
height: 68px;
border-radius: 16px;
object-fit: cover;
border: 1px solid var(--line);
}
.app-main h2 {
margin: 0 0 6px;
font-size: 20px;
}
.app-main p {
margin: 0;
color: var(--muted);
}
.app-meta {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
margin: 2px 0 0;
}
.app-meta div {
border: 1px solid var(--line);
border-radius: var(--radius-md);
padding: 10px;
background: #fbfdfb;
}
.app-meta dt {
margin: 0 0 3px;
color: var(--muted);
font-size: 12px;
}
.app-meta dd {
margin: 0;
font-weight: 700;
}
.empty,
.not-found {
padding: 22px;
text-align: center;
}
.detail-page {
display: grid;
gap: 14px;
}
.back-link {
width: fit-content;
color: var(--brand-deep);
font-weight: 700;
}
.detail-hero {
display: grid;
grid-template-columns: 92px minmax(0, 1fr);
gap: 18px;
padding: 20px;
}
.detail-icon {
width: 92px;
height: 92px;
border-radius: 20px;
}
.badge {
width: fit-content;
margin: 0;
padding: 5px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 800;
color: var(--brand-deep);
background: var(--tag);
}
.detail-hero h1 {
margin: 8px 0 6px;
font-size: clamp(25px, 3vw, 34px);
}
.subtitle {
margin: 0;
font-size: 16px;
font-weight: 700;
}
.description {
margin: 10px 0 0;
color: var(--muted);
}
.actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 14px;
}
.actions a {
border: 1px solid var(--line);
border-radius: 10px;
padding: 9px 12px;
background: #ffffff;
font-weight: 700;
}
.actions a:first-child {
color: #ffffff;
background: linear-gradient(120deg, #0f8a5f, #17ab78);
border-color: transparent;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.detail-grid article,
.notes {
padding: 18px;
}
.detail-grid h2,
.notes h2 {
margin: 0 0 8px;
font-size: 18px;
}
.detail-grid ul,
.notes ul {
margin: 0;
padding-left: 18px;
color: #284438;
}
.detail-grid li,
.notes li {
margin: 7px 0;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 780px) {
.app-shell {
padding: 16px;
}
.topbar {
margin-bottom: 14px;
}
.hero-card,
.control-bar,
.app-card,
.detail-hero,
.detail-grid article,
.notes {
border-radius: 16px;
}
.detail-grid {
grid-template-columns: 1fr;
}
.app-meta {
grid-template-columns: 1fr;
}
.detail-hero {
grid-template-columns: 1fr;
}
}

38
src/types/app.ts Normal file
View File

@@ -0,0 +1,38 @@
export type Locale = 'zh-CN' | 'en-US'
export interface AppText {
'zh-CN': string
'en-US': string
}
export interface AppNotes {
'zh-CN': string[]
'en-US': string[]
}
export interface AppItem {
id: string
name: string
icon: string
shortDescription: AppText
description: AppText
category: string
version: string
buildNumber: string
releaseDate: string
releaseNotes: AppNotes
downloads: {
ios: string
android: string
}
size: {
ios: string
android: string
}
stats: {
total: number
today: number
ios: number
android: number
}
}

13
src/utils/format.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { Locale } from '../types/app'
export const formatNumber = (value: number, locale: Locale) =>
new Intl.NumberFormat(locale).format(value)
export const formatDate = (value: string, locale: Locale) => {
const date = new Date(value)
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
}).format(date)
}

88
src/views/AppDetail.vue Normal file
View File

@@ -0,0 +1,88 @@
<script setup lang="ts">
import { computed } from "vue";
import { RouterLink, useRoute } from "vue-router";
import { apps, categoryLabel } from "../data/apps";
import { useLocaleStore } from "../composables/useLocaleStore";
import { formatDate, formatNumber } from "../utils/format";
const route = useRoute();
const { locale, t } = useLocaleStore();
const app = computed(() => apps.find((item) => item.id === route.params.id));
</script>
<template>
<section v-if="app" class="detail-page">
<RouterLink class="back-link" to="/">{{
t("返回应用列表", "Back to apps")
}}</RouterLink>
<header class="detail-hero">
<img class="detail-icon" :src="app.icon" :alt="app.name" />
<div>
<p class="badge">
{{ categoryLabel[app.category]?.[locale] ?? app.category }}
</p>
<h1>{{ app.name }}</h1>
<p class="subtitle">{{ app.shortDescription[locale] }}</p>
<p class="description">{{ app.description[locale] }}</p>
<div class="actions">
<a :href="app.downloads.ios" target="_blank" rel="noreferrer">{{
t("iOS 下载", "Download for iOS")
}}</a>
<a :href="app.downloads.android" target="_blank" rel="noreferrer">{{
t("Android 下载", "Download for Android")
}}</a>
</div>
</div>
</header>
<section class="detail-grid">
<article>
<h2>{{ t("版本信息", "Version") }}</h2>
<ul>
<li>{{ t("版本号", "Version") }}: {{ app.version }}</li>
<li>{{ t("构建号", "Build") }}: {{ app.buildNumber }}</li>
<li>
{{ t("发布日期", "Release Date") }}:
{{ formatDate(app.releaseDate, locale) }}
</li>
<li>
{{ t("安装包大小", "Package Size") }}: iOS {{ app.size.ios }} /
Android {{ app.size.android }}
</li>
</ul>
</article>
<article>
<h2>{{ t("下载统计", "Download Stats") }}</h2>
<ul>
<li>
{{ t("总下载量", "Total") }}:
{{ formatNumber(app.stats.total, locale) }}
</li>
<li>
{{ t("今日下载", "Today") }}:
{{ formatNumber(app.stats.today, locale) }}
</li>
<li>iOS: {{ formatNumber(app.stats.ios, locale) }}</li>
<li>Android: {{ formatNumber(app.stats.android, locale) }}</li>
</ul>
</article>
</section>
<section class="notes">
<h2>{{ t("更新说明", "Release Notes") }}</h2>
<ul>
<li v-for="note in app.releaseNotes[locale]" :key="note">{{ note }}</li>
</ul>
</section>
</section>
<section v-else class="not-found">
<h1>{{ t("应用不存在", "App not found") }}</h1>
<RouterLink to="/">{{ t("返回首页", "Back Home") }}</RouterLink>
</section>
</template>

113
src/views/StoreHome.vue Normal file
View File

@@ -0,0 +1,113 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { RouterLink } from "vue-router";
import { apps, categoryLabel } from "../data/apps";
import { useLocaleStore } from "../composables/useLocaleStore";
import { formatDate, formatNumber } from "../utils/format";
const { locale, t } = useLocaleStore();
const selectedCategory = ref("all");
const keyword = ref("");
const categories = computed(() => [
"all",
...new Set(apps.map((item) => item.category)),
]);
const filteredApps = computed(() => {
const searchWord = keyword.value.trim().toLowerCase();
return apps.filter((item) => {
const categoryMatched =
selectedCategory.value === "all" ||
item.category === selectedCategory.value;
const localizedText =
`${item.name} ${item.shortDescription[locale.value]}`.toLowerCase();
const searchMatched = searchWord
? localizedText.includes(searchWord)
: true;
return categoryMatched && searchMatched;
});
});
</script>
<template>
<section class="home-page">
<header class="hero-card">
<div>
<p class="eyebrow">{{ t("IM 应用商店", "IM App Store") }}</p>
<h2>{{ t("发现优质应用", "Discover Quality Apps") }}</h2>
<p class="hero-desc">
{{
t(
"统一管理企业应用,支持下载、版本查看与更新追踪。",
"Manage enterprise apps with downloads, versions, and release tracking.",
)
}}
</p>
</div>
</header>
<section class="control-bar">
<label class="search-box" for="search-input">
<span>{{ t("搜索", "Search") }}</span>
<input
id="search-input"
v-model="keyword"
type="search"
:placeholder="
t('输入应用名称或描述', 'Search by app name or description')
"
/>
</label>
<div class="chips">
<button
v-for="category in categories"
:key="category"
class="chip"
:class="{ active: category === selectedCategory }"
@click="selectedCategory = category"
>
{{ categoryLabel[category]?.[locale] ?? category }}
</button>
</div>
</section>
<section class="app-grid">
<RouterLink
v-for="item in filteredApps"
:key="item.id"
class="app-card"
:to="`/app/${item.id}`"
>
<img class="app-icon" :src="item.icon" :alt="item.name" />
<div class="app-main">
<h2>{{ item.name }}</h2>
<p>{{ item.shortDescription[locale] }}</p>
</div>
<dl class="app-meta">
<div>
<dt>{{ t("版本", "Version") }}</dt>
<dd>{{ item.version }}</dd>
</div>
<div>
<dt>{{ t("发布日期", "Release") }}</dt>
<dd>{{ formatDate(item.releaseDate, locale) }}</dd>
</div>
<div>
<dt>{{ t("总下载", "Downloads") }}</dt>
<dd>{{ formatNumber(item.stats.total, locale) }}</dd>
</div>
</dl>
</RouterLink>
<article v-if="filteredApps.length === 0" class="empty">
{{ t("未找到匹配应用", "No matching apps found") }}
</article>
</section>
</section>
</template>

17
tsconfig.app.json Normal file
View File

@@ -0,0 +1,17 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"types": ["vite/client"],
"resolveJsonModule": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

26
tsconfig.node.json Normal file
View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

7
vite.config.ts Normal file
View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
})