init
This commit is contained in:
commit
51b58fdf7d
|
|
@ -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?
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>古建筑保护信息化平台</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "heritage-admin",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.25",
|
||||
"vue-router": "^4.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"vite": "^7.3.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,951 @@
|
|||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
pinia:
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4(vue@3.5.30)
|
||||
vue:
|
||||
specifier: ^3.5.25
|
||||
version: 3.5.30
|
||||
vue-router:
|
||||
specifier: ^4.6.4
|
||||
version: 4.6.4(vue@3.5.30)
|
||||
devDependencies:
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^6.0.2
|
||||
version: 6.0.4(vite@7.3.1)(vue@3.5.30)
|
||||
vite:
|
||||
specifier: ^7.3.1
|
||||
version: 7.3.1
|
||||
|
||||
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]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
|
||||
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@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==}
|
||||
|
||||
'@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
|
||||
|
||||
'@vue/compiler-core@3.5.30':
|
||||
resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==}
|
||||
|
||||
'@vue/compiler-dom@3.5.30':
|
||||
resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.30':
|
||||
resolution: {integrity: sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.30':
|
||||
resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==}
|
||||
|
||||
'@vue/devtools-api@6.6.4':
|
||||
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
|
||||
|
||||
'@vue/devtools-api@7.7.9':
|
||||
resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==}
|
||||
|
||||
'@vue/devtools-kit@7.7.9':
|
||||
resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==}
|
||||
|
||||
'@vue/devtools-shared@7.7.9':
|
||||
resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==}
|
||||
|
||||
'@vue/reactivity@3.5.30':
|
||||
resolution: {integrity: sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==}
|
||||
|
||||
'@vue/runtime-core@3.5.30':
|
||||
resolution: {integrity: sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==}
|
||||
|
||||
'@vue/runtime-dom@3.5.30':
|
||||
resolution: {integrity: sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==}
|
||||
|
||||
'@vue/server-renderer@3.5.30':
|
||||
resolution: {integrity: sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==}
|
||||
peerDependencies:
|
||||
vue: 3.5.30
|
||||
|
||||
'@vue/shared@3.5.30':
|
||||
resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==}
|
||||
|
||||
birpc@2.9.0:
|
||||
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
|
||||
|
||||
copy-anything@4.0.5:
|
||||
resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
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]
|
||||
|
||||
hookable@5.5.3:
|
||||
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
|
||||
|
||||
is-what@5.5.0:
|
||||
resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
nanoid@3.3.11:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
perfect-debounce@1.0.0:
|
||||
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@4.0.3:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pinia@3.0.4:
|
||||
resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==}
|
||||
peerDependencies:
|
||||
typescript: '>=4.5.0'
|
||||
vue: ^3.5.11
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
postcss@8.5.8:
|
||||
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
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'}
|
||||
|
||||
speakingurl@14.0.1:
|
||||
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
superjson@2.2.6:
|
||||
resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
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
|
||||
|
||||
vue-router@4.6.4:
|
||||
resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
vue@3.5.30:
|
||||
resolution: {integrity: sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==}
|
||||
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': {}
|
||||
|
||||
'@vitejs/plugin-vue@6.0.4(vite@7.3.1)(vue@3.5.30)':
|
||||
dependencies:
|
||||
'@rolldown/pluginutils': 1.0.0-rc.2
|
||||
vite: 7.3.1
|
||||
vue: 3.5.30
|
||||
|
||||
'@vue/compiler-core@3.5.30':
|
||||
dependencies:
|
||||
'@babel/parser': 7.29.0
|
||||
'@vue/shared': 3.5.30
|
||||
entities: 7.0.1
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.5.30':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.30
|
||||
'@vue/shared': 3.5.30
|
||||
|
||||
'@vue/compiler-sfc@3.5.30':
|
||||
dependencies:
|
||||
'@babel/parser': 7.29.0
|
||||
'@vue/compiler-core': 3.5.30
|
||||
'@vue/compiler-dom': 3.5.30
|
||||
'@vue/compiler-ssr': 3.5.30
|
||||
'@vue/shared': 3.5.30
|
||||
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.30':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.30
|
||||
'@vue/shared': 3.5.30
|
||||
|
||||
'@vue/devtools-api@6.6.4': {}
|
||||
|
||||
'@vue/devtools-api@7.7.9':
|
||||
dependencies:
|
||||
'@vue/devtools-kit': 7.7.9
|
||||
|
||||
'@vue/devtools-kit@7.7.9':
|
||||
dependencies:
|
||||
'@vue/devtools-shared': 7.7.9
|
||||
birpc: 2.9.0
|
||||
hookable: 5.5.3
|
||||
mitt: 3.0.1
|
||||
perfect-debounce: 1.0.0
|
||||
speakingurl: 14.0.1
|
||||
superjson: 2.2.6
|
||||
|
||||
'@vue/devtools-shared@7.7.9':
|
||||
dependencies:
|
||||
rfdc: 1.4.1
|
||||
|
||||
'@vue/reactivity@3.5.30':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.30
|
||||
|
||||
'@vue/runtime-core@3.5.30':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.30
|
||||
'@vue/shared': 3.5.30
|
||||
|
||||
'@vue/runtime-dom@3.5.30':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.30
|
||||
'@vue/runtime-core': 3.5.30
|
||||
'@vue/shared': 3.5.30
|
||||
csstype: 3.2.3
|
||||
|
||||
'@vue/server-renderer@3.5.30(vue@3.5.30)':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.30
|
||||
'@vue/shared': 3.5.30
|
||||
vue: 3.5.30
|
||||
|
||||
'@vue/shared@3.5.30': {}
|
||||
|
||||
birpc@2.9.0: {}
|
||||
|
||||
copy-anything@4.0.5:
|
||||
dependencies:
|
||||
is-what: 5.5.0
|
||||
|
||||
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
|
||||
|
||||
hookable@5.5.3: {}
|
||||
|
||||
is-what@5.5.0: {}
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
perfect-debounce@1.0.0: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
pinia@3.0.4(vue@3.5.30):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 7.7.9
|
||||
vue: 3.5.30
|
||||
|
||||
postcss@8.5.8:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
rfdc@1.4.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: {}
|
||||
|
||||
speakingurl@14.0.1: {}
|
||||
|
||||
superjson@2.2.6:
|
||||
dependencies:
|
||||
copy-anything: 4.0.5
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
|
||||
vite@7.3.1:
|
||||
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:
|
||||
fsevents: 2.3.3
|
||||
|
||||
vue-router@4.6.4(vue@3.5.30):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.30
|
||||
|
||||
vue@3.5.30:
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.30
|
||||
'@vue/compiler-sfc': 3.5.30
|
||||
'@vue/runtime-dom': 3.5.30
|
||||
'@vue/server-renderer': 3.5.30(vue@3.5.30)
|
||||
'@vue/shared': 3.5.30
|
||||
|
|
@ -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 |
|
|
@ -0,0 +1,197 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { RouterLink, RouterView, useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const isAdmin = computed(() => route.path.startsWith('/admin'))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app">
|
||||
<RouterView v-if="isAdmin" />
|
||||
<template v-else>
|
||||
<header class="topbar">
|
||||
<div class="topbar__left">
|
||||
<div class="brand">
|
||||
<div class="brand__mark">遗</div>
|
||||
<div class="brand__text">
|
||||
<div class="brand__title">古建筑保护信息化平台</div>
|
||||
<div class="brand__subtitle">Heritage Admin</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="topnav">
|
||||
<RouterLink class="topnav__link" exact-active-class="is-active" to="/">首页</RouterLink>
|
||||
<RouterLink class="topnav__link" active-class="is-active" to="/archives">资源档案</RouterLink>
|
||||
<RouterLink class="topnav__link" active-class="is-active" to="/inspections">巡查监测</RouterLink>
|
||||
<RouterLink class="topnav__link" active-class="is-active" to="/projects">修缮项目</RouterLink>
|
||||
<RouterLink class="topnav__link" active-class="is-active" to="/analytics">统计分析</RouterLink>
|
||||
</nav>
|
||||
|
||||
<div class="topbar__right">
|
||||
<RouterLink class="login" active-class="is-active" to="/login">用户登录</RouterLink>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
<RouterView />
|
||||
</main>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.app {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 12px 18px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: rgba(255, 255, 255, 0.82);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.topbar__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.topbar__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
.brand__mark {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: linear-gradient(135deg, #b48040, #205c40);
|
||||
color: #fff;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.brand__title {
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.brand__subtitle {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.topnav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.topnav__link {
|
||||
padding: 7px 9px;
|
||||
border-radius: 10px;
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.topnav__link:hover {
|
||||
color: var(--text);
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.topnav__link.is-active {
|
||||
color: var(--text);
|
||||
border-color: rgba(180, 128, 64, 0.45);
|
||||
background: rgba(180, 128, 64, 0.08);
|
||||
}
|
||||
|
||||
.login {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
border-radius: 10px;
|
||||
text-decoration: none;
|
||||
border-color: rgba(32, 92, 64, 0.25);
|
||||
background: rgba(32, 92, 64, 0.12);
|
||||
color: var(--text);
|
||||
font-weight: 800;
|
||||
border: 1px solid rgba(32, 92, 64, 0.25);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.login:hover {
|
||||
border-color: rgba(32, 92, 64, 0.35);
|
||||
background: rgba(32, 92, 64, 0.18);
|
||||
}
|
||||
|
||||
.login.is-active {
|
||||
border-color: rgba(32, 92, 64, 0.35);
|
||||
background: rgba(32, 92, 64, 0.18);
|
||||
}
|
||||
|
||||
.main {
|
||||
width: min(1200px, 100%);
|
||||
margin: 0 auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.topbar {
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-areas:
|
||||
'left right'
|
||||
'nav nav';
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.topbar__left {
|
||||
grid-area: left;
|
||||
}
|
||||
.topbar__right {
|
||||
grid-area: right;
|
||||
}
|
||||
.topnav {
|
||||
grid-area: nav;
|
||||
justify-content: center;
|
||||
}
|
||||
.brand {
|
||||
min-width: 0;
|
||||
}
|
||||
.brand__title {
|
||||
font-size: 14px;
|
||||
}
|
||||
.brand__subtitle {
|
||||
display: none;
|
||||
}
|
||||
.topnav__link {
|
||||
padding: 7px 9px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -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 |
|
|
@ -0,0 +1,34 @@
|
|||
import { getToken } from './auth'
|
||||
|
||||
export async function apiFetch(path, options = {}) {
|
||||
const headers = new Headers(options.headers || {})
|
||||
if (!headers.has('Content-Type') && options.body != null) {
|
||||
headers.set('Content-Type', 'application/json')
|
||||
}
|
||||
|
||||
const token = getToken()
|
||||
if (token && !headers.has('Authorization')) {
|
||||
headers.set('Authorization', `Bearer ${token}`)
|
||||
}
|
||||
|
||||
const res = await fetch(path, {
|
||||
...options,
|
||||
headers,
|
||||
})
|
||||
|
||||
const text = await res.text()
|
||||
let data = null
|
||||
try {
|
||||
data = text ? JSON.parse(text) : null
|
||||
} catch {
|
||||
data = text
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const message = (data && data.data) || (data && data.message) || '请求失败'
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
const TOKEN_KEY = 'heritage.token'
|
||||
const USER_KEY = 'heritage.user'
|
||||
|
||||
export function getToken() {
|
||||
return localStorage.getItem(TOKEN_KEY) || ''
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
if (!token) return
|
||||
localStorage.setItem(TOKEN_KEY, token)
|
||||
}
|
||||
|
||||
export function clearToken() {
|
||||
localStorage.removeItem(TOKEN_KEY)
|
||||
}
|
||||
|
||||
export function getUser() {
|
||||
const raw = localStorage.getItem(USER_KEY)
|
||||
if (!raw) return null
|
||||
try {
|
||||
return JSON.parse(raw)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function setUser(user) {
|
||||
if (!user) return
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(user))
|
||||
}
|
||||
|
||||
export function clearUser() {
|
||||
localStorage.removeItem(USER_KEY)
|
||||
}
|
||||
|
||||
export function clearAuth() {
|
||||
clearToken()
|
||||
clearUser()
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import router from './router'
|
||||
|
||||
createApp(App).use(createPinia()).use(router).mount('#app')
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const months = ref([
|
||||
{ month: '10月', hazards: 2, repairs: 1 },
|
||||
{ month: '11月', hazards: 3, repairs: 2 },
|
||||
{ month: '12月', hazards: 1, repairs: 1 },
|
||||
{ month: '1月', hazards: 4, repairs: 2 },
|
||||
{ month: '2月', hazards: 2, repairs: 3 },
|
||||
{ month: '3月', hazards: 3, repairs: 2 },
|
||||
])
|
||||
|
||||
const maxHazards = computed(() => Math.max(...months.value.map((m) => m.hazards), 1))
|
||||
const maxRepairs = computed(() => Math.max(...months.value.map((m) => m.repairs), 1))
|
||||
|
||||
const summary = computed(() => {
|
||||
const hazards = months.value.reduce((acc, m) => acc + m.hazards, 0)
|
||||
const repairs = months.value.reduce((acc, m) => acc + m.repairs, 0)
|
||||
return {
|
||||
buildings: 128,
|
||||
inspections: 356,
|
||||
hazards,
|
||||
repairs,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page__header">
|
||||
<div>
|
||||
<div class="title">统计分析</div>
|
||||
<div class="subtitle">区域态势、巡查隐患与修缮项目统计</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="cards">
|
||||
<div class="metric">
|
||||
<div class="metric__label">在册古建筑</div>
|
||||
<div class="metric__value">{{ summary.buildings }}</div>
|
||||
<div class="metric__hint">覆盖建档与监管对象</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric__label">巡查次数</div>
|
||||
<div class="metric__value">{{ summary.inspections }}</div>
|
||||
<div class="metric__hint">近 6 个月累计</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric__label">隐患数量</div>
|
||||
<div class="metric__value">{{ summary.hazards }}</div>
|
||||
<div class="metric__hint">已纳入处置流程</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric__label">修缮项目</div>
|
||||
<div class="metric__value">{{ summary.repairs }}</div>
|
||||
<div class="metric__hint">进行中与已完成</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel__title">近 6 个月趋势</div>
|
||||
<div class="chart">
|
||||
<div v-for="m in months" :key="m.month" class="chart__col">
|
||||
<div class="bars">
|
||||
<div
|
||||
class="bar is-hazard"
|
||||
:style="{ height: `${Math.round((m.hazards / maxHazards) * 100)}%` }"
|
||||
:title="`隐患:${m.hazards}`"
|
||||
/>
|
||||
<div
|
||||
class="bar is-repair"
|
||||
:style="{ height: `${Math.round((m.repairs / maxRepairs) * 100)}%` }"
|
||||
:title="`修缮:${m.repairs}`"
|
||||
/>
|
||||
</div>
|
||||
<div class="label">{{ m.month }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="legend">
|
||||
<span class="legend__item"><i class="dot is-hazard" />隐患</span>
|
||||
<span class="legend__item"><i class="dot is-repair" />修缮</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page__header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.metric {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.metric__label {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.metric__value {
|
||||
margin-top: 6px;
|
||||
font-size: 26px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
.metric__hint {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-top: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.panel__title {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.chart {
|
||||
margin-top: 12px;
|
||||
height: 220px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.chart__col {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.bars {
|
||||
width: 100%;
|
||||
height: 170px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 6px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.bar {
|
||||
width: 100%;
|
||||
border-radius: 10px 10px 6px 6px;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.bar.is-hazard {
|
||||
background: linear-gradient(180deg, rgba(180, 44, 44, 0.9), rgba(180, 44, 44, 0.45));
|
||||
}
|
||||
|
||||
.bar.is-repair {
|
||||
background: linear-gradient(180deg, rgba(32, 92, 64, 0.9), rgba(32, 92, 64, 0.45));
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.legend__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dot.is-hazard {
|
||||
background: rgba(180, 44, 44, 0.9);
|
||||
}
|
||||
|
||||
.dot.is-repair {
|
||||
background: rgba(32, 92, 64, 0.9);
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.cards {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.page {
|
||||
padding: 16px;
|
||||
}
|
||||
.cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.chart {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const keyword = ref('')
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
id: 'HZ-001',
|
||||
name: '永宁寺大殿',
|
||||
level: '省级文保',
|
||||
location: '某省·某市',
|
||||
status: '在册',
|
||||
},
|
||||
{
|
||||
id: 'HZ-002',
|
||||
name: '文庙大成殿',
|
||||
level: '市级文保',
|
||||
location: '某省·某县',
|
||||
status: '在册',
|
||||
},
|
||||
{
|
||||
id: 'HZ-003',
|
||||
name: '城隍庙戏台',
|
||||
level: '一般不可移动文物',
|
||||
location: '某省·某区',
|
||||
status: '在册',
|
||||
},
|
||||
])
|
||||
|
||||
const filtered = computed(() => {
|
||||
const q = keyword.value.trim()
|
||||
if (!q) return items.value
|
||||
return items.value.filter((it) => {
|
||||
const haystack = `${it.id} ${it.name} ${it.level} ${it.location} ${it.status}`
|
||||
return haystack.includes(q)
|
||||
})
|
||||
})
|
||||
|
||||
function addSample() {
|
||||
const nextId = String(items.value.length + 1).padStart(3, '0')
|
||||
items.value.unshift({
|
||||
id: `HZ-${nextId}`,
|
||||
name: '新增古建筑(示例)',
|
||||
level: '未定级',
|
||||
location: '待补充',
|
||||
status: '草稿',
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page__header">
|
||||
<div>
|
||||
<div class="title">资源档案</div>
|
||||
<div class="subtitle">管理古建筑基础档案与关联资料</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<input v-model="keyword" class="input" placeholder="搜索:编号 / 名称 / 等级 / 地点" />
|
||||
<button class="btn" type="button" @click="addSample">新增档案</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="table">
|
||||
<div class="row head">
|
||||
<div>编号</div>
|
||||
<div>名称</div>
|
||||
<div>等级</div>
|
||||
<div>地点</div>
|
||||
<div>状态</div>
|
||||
</div>
|
||||
<div v-for="it in filtered" :key="it.id" class="row">
|
||||
<div class="mono">{{ it.id }}</div>
|
||||
<div class="strong">{{ it.name }}</div>
|
||||
<div>{{ it.level }}</div>
|
||||
<div>{{ it.location }}</div>
|
||||
<div>
|
||||
<span class="pill">{{ it.status }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="filtered.length === 0" class="empty">暂无匹配数据</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page__header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(180, 128, 64, 0.35);
|
||||
background: rgba(180, 128, 64, 0.12);
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: rgba(180, 128, 64, 0.18);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 140px 1.2fr 180px 220px 120px;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-top: 1px solid rgba(31, 35, 40, 0.06);
|
||||
align-items: center;
|
||||
min-width: 820px;
|
||||
}
|
||||
|
||||
.row.head {
|
||||
border-top: 0;
|
||||
background: rgba(31, 35, 40, 0.03);
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(32, 92, 64, 0.25);
|
||||
background: rgba(32, 92, 64, 0.08);
|
||||
color: rgba(32, 92, 64, 0.9);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 24px 14px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.row {
|
||||
grid-template-columns: 120px 1fr 160px 1fr 96px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.page {
|
||||
padding: 16px;
|
||||
}
|
||||
.input {
|
||||
min-width: 0;
|
||||
width: min(360px, 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<script setup>
|
||||
import { RouterLink } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="home">
|
||||
<section class="hero">
|
||||
<div class="hero__badge">古建筑保护信息化平台</div>
|
||||
<h1 class="hero__title">古建筑保护信息化平台</h1>
|
||||
<p class="hero__subtitle">
|
||||
汇聚基础档案、巡查监测、修缮项目与展示传播,支撑古建筑全生命周期保护管理。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="grid">
|
||||
<RouterLink class="card" to="/archives">
|
||||
<div class="card__title">资源档案</div>
|
||||
<div class="card__desc">建筑本体、构件、图纸与影像资料统一管理</div>
|
||||
</RouterLink>
|
||||
<RouterLink class="card" to="/inspections">
|
||||
<div class="card__title">巡查监测</div>
|
||||
<div class="card__desc">日常巡查、隐患上报与监测数据可视化</div>
|
||||
</RouterLink>
|
||||
<RouterLink class="card" to="/projects">
|
||||
<div class="card__title">修缮项目</div>
|
||||
<div class="card__desc">立项、审批、施工与竣工全过程留痕</div>
|
||||
</RouterLink>
|
||||
<RouterLink class="card" to="/analytics">
|
||||
<div class="card__title">统计分析</div>
|
||||
<div class="card__desc">多维统计报表与区域态势分析</div>
|
||||
</RouterLink>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.home {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
border: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, rgba(180, 128, 64, 0.12), rgba(32, 92, 64, 0.08));
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.hero__badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(180, 128, 64, 0.35);
|
||||
color: var(--muted);
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
.hero__title {
|
||||
margin: 12px 0 8px;
|
||||
font-size: 28px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero__subtitle {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
max-width: 60ch;
|
||||
}
|
||||
|
||||
.grid {
|
||||
margin-top: 18px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: transform 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(180, 128, 64, 0.45);
|
||||
box-shadow: 0 12px 32px rgba(16, 24, 40, 0.08);
|
||||
}
|
||||
|
||||
.card__title {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.card__desc {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.home {
|
||||
padding: 16px;
|
||||
}
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const status = ref('全部')
|
||||
|
||||
const records = ref([
|
||||
{
|
||||
id: 'XC-202603-001',
|
||||
building: '永宁寺大殿',
|
||||
inspector: '张三',
|
||||
date: '2026-03-08',
|
||||
result: '正常',
|
||||
level: '低',
|
||||
},
|
||||
{
|
||||
id: 'XC-202603-002',
|
||||
building: '文庙大成殿',
|
||||
inspector: '李四',
|
||||
date: '2026-03-10',
|
||||
result: '隐患',
|
||||
level: '中',
|
||||
},
|
||||
{
|
||||
id: 'XC-202603-003',
|
||||
building: '城隍庙戏台',
|
||||
inspector: '王五',
|
||||
date: '2026-03-11',
|
||||
result: '整改中',
|
||||
level: '高',
|
||||
},
|
||||
])
|
||||
|
||||
const filtered = computed(() => {
|
||||
if (status.value === '全部') return records.value
|
||||
return records.value.filter((r) => r.result === status.value)
|
||||
})
|
||||
|
||||
function createRecord() {
|
||||
const next = String(records.value.length + 1).padStart(3, '0')
|
||||
records.value.unshift({
|
||||
id: `XC-202603-${next}`,
|
||||
building: '(待选择)',
|
||||
inspector: '(待填写)',
|
||||
date: new Date().toISOString().slice(0, 10),
|
||||
result: '正常',
|
||||
level: '低',
|
||||
})
|
||||
}
|
||||
|
||||
function pillVariant(result) {
|
||||
if (result === '隐患') return 'danger'
|
||||
if (result === '整改中') return 'warn'
|
||||
return 'ok'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page__header">
|
||||
<div>
|
||||
<div class="title">巡查监测</div>
|
||||
<div class="subtitle">巡查记录、隐患处置与整改跟踪</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<select v-model="status" class="select">
|
||||
<option>全部</option>
|
||||
<option>正常</option>
|
||||
<option>隐患</option>
|
||||
<option>整改中</option>
|
||||
</select>
|
||||
<button class="btn" type="button" @click="createRecord">新建巡查</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="table">
|
||||
<div class="row head">
|
||||
<div>巡查单号</div>
|
||||
<div>巡查对象</div>
|
||||
<div>巡查人</div>
|
||||
<div>日期</div>
|
||||
<div>结果</div>
|
||||
<div>风险</div>
|
||||
</div>
|
||||
<div v-for="r in filtered" :key="r.id" class="row">
|
||||
<div class="mono">{{ r.id }}</div>
|
||||
<div class="strong">{{ r.building }}</div>
|
||||
<div>{{ r.inspector }}</div>
|
||||
<div class="mono">{{ r.date }}</div>
|
||||
<div>
|
||||
<span class="pill" :class="`is-${pillVariant(r.result)}`">{{ r.result }}</span>
|
||||
</div>
|
||||
<div>{{ r.level }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page__header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.select {
|
||||
height: 34px;
|
||||
padding: 0 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(32, 92, 64, 0.25);
|
||||
background: rgba(32, 92, 64, 0.12);
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: rgba(32, 92, 64, 0.18);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 170px 1.2fr 120px 120px 96px 72px;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-top: 1px solid rgba(31, 35, 40, 0.06);
|
||||
align-items: center;
|
||||
min-width: 760px;
|
||||
}
|
||||
|
||||
.row.head {
|
||||
border-top: 0;
|
||||
background: rgba(31, 35, 40, 0.03);
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.18);
|
||||
background: rgba(31, 35, 40, 0.06);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pill.is-ok {
|
||||
border-color: rgba(32, 92, 64, 0.25);
|
||||
background: rgba(32, 92, 64, 0.08);
|
||||
color: rgba(32, 92, 64, 0.9);
|
||||
}
|
||||
|
||||
.pill.is-warn {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
background: rgba(180, 128, 64, 0.12);
|
||||
color: rgba(120, 74, 20, 0.95);
|
||||
}
|
||||
|
||||
.pill.is-danger {
|
||||
border-color: rgba(180, 44, 44, 0.35);
|
||||
background: rgba(180, 44, 44, 0.1);
|
||||
color: rgba(140, 18, 18, 0.95);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.page {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const phase = ref('全部')
|
||||
|
||||
const projects = ref([
|
||||
{
|
||||
id: 'XS-2026-001',
|
||||
name: '永宁寺大殿屋面修缮',
|
||||
location: '某省·某市',
|
||||
phase: '施工',
|
||||
progress: 62,
|
||||
updatedAt: '2026-03-09',
|
||||
},
|
||||
{
|
||||
id: 'XS-2026-002',
|
||||
name: '文庙大成殿结构加固',
|
||||
location: '某省·某县',
|
||||
phase: '审批',
|
||||
progress: 28,
|
||||
updatedAt: '2026-03-06',
|
||||
},
|
||||
{
|
||||
id: 'XS-2026-003',
|
||||
name: '城隍庙戏台彩绘修复',
|
||||
location: '某省·某区',
|
||||
phase: '验收',
|
||||
progress: 92,
|
||||
updatedAt: '2026-03-11',
|
||||
},
|
||||
])
|
||||
|
||||
const filtered = computed(() => {
|
||||
if (phase.value === '全部') return projects.value
|
||||
return projects.value.filter((p) => p.phase === phase.value)
|
||||
})
|
||||
|
||||
function createProject() {
|
||||
const next = String(projects.value.length + 1).padStart(3, '0')
|
||||
projects.value.unshift({
|
||||
id: `XS-2026-${next}`,
|
||||
name: '新增修缮项目(示例)',
|
||||
location: '待补充',
|
||||
phase: '立项',
|
||||
progress: 0,
|
||||
updatedAt: new Date().toISOString().slice(0, 10),
|
||||
})
|
||||
}
|
||||
|
||||
function phaseClass(value) {
|
||||
if (value === '施工') return 'is-doing'
|
||||
if (value === '审批') return 'is-warn'
|
||||
if (value === '验收') return 'is-ok'
|
||||
return 'is-default'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page__header">
|
||||
<div>
|
||||
<div class="title">修缮项目</div>
|
||||
<div class="subtitle">立项、审批、施工、验收全流程管理</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<select v-model="phase" class="select">
|
||||
<option>全部</option>
|
||||
<option>立项</option>
|
||||
<option>审批</option>
|
||||
<option>施工</option>
|
||||
<option>验收</option>
|
||||
</select>
|
||||
<button class="btn" type="button" @click="createProject">新建项目</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div v-for="p in filtered" :key="p.id" class="card">
|
||||
<div class="card__top">
|
||||
<div>
|
||||
<div class="name">{{ p.name }}</div>
|
||||
<div class="meta">
|
||||
<span class="mono">{{ p.id }}</span>
|
||||
<span class="dot">·</span>
|
||||
<span>{{ p.location }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="tag" :class="phaseClass(p.phase)">{{ p.phase }}</span>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<div class="progress__bar" :style="{ width: `${p.progress}%` }" />
|
||||
</div>
|
||||
<div class="progress__meta">
|
||||
<span class="mono">{{ p.progress }}%</span>
|
||||
<span class="muted">更新于 {{ p.updatedAt }}</span>
|
||||
</div>
|
||||
|
||||
<div class="actions2">
|
||||
<button class="ghost" type="button">项目详情</button>
|
||||
<button class="ghost" type="button">过程资料</button>
|
||||
<button class="ghost" type="button">进度记录</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.page__header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.select {
|
||||
height: 34px;
|
||||
padding: 0 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(180, 128, 64, 0.35);
|
||||
background: rgba(180, 128, 64, 0.12);
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: rgba(180, 128, 64, 0.18);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.card__top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.dot {
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.18);
|
||||
background: rgba(31, 35, 40, 0.06);
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tag.is-doing {
|
||||
border-color: rgba(32, 92, 64, 0.25);
|
||||
background: rgba(32, 92, 64, 0.08);
|
||||
color: rgba(32, 92, 64, 0.9);
|
||||
}
|
||||
|
||||
.tag.is-warn {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
background: rgba(180, 128, 64, 0.12);
|
||||
color: rgba(120, 74, 20, 0.95);
|
||||
}
|
||||
|
||||
.tag.is-ok {
|
||||
border-color: rgba(72, 96, 164, 0.35);
|
||||
background: rgba(72, 96, 164, 0.1);
|
||||
color: rgba(35, 56, 120, 0.95);
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-top: 12px;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
background: rgba(31, 35, 40, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress__bar {
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, #b48040, #205c40);
|
||||
}
|
||||
|
||||
.progress__meta {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.actions2 {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.ghost {
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.ghost:hover {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.page {
|
||||
padding: 16px;
|
||||
}
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { RouterLink, useRouter } from 'vue-router'
|
||||
import { apiFetch } from '../lib/api'
|
||||
import { setToken, setUser } from '../lib/auth'
|
||||
|
||||
const appName = '古建筑保护信息化平台'
|
||||
const token = ref(Math.random().toString(36).slice(2, 10).toUpperCase())
|
||||
const router = useRouter()
|
||||
|
||||
const userName = ref('')
|
||||
const password = ref('')
|
||||
const errorText = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
async function accountLogin() {
|
||||
if (loading.value) return
|
||||
errorText.value = ''
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await apiFetch('/api/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
userName: userName.value,
|
||||
password: password.value,
|
||||
}),
|
||||
})
|
||||
setToken(res?.token || '')
|
||||
setUser(res?.user || null)
|
||||
router.push('/admin')
|
||||
} catch (e) {
|
||||
errorText.value = e?.message || '登录失败'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="panel">
|
||||
<div class="panel__header">
|
||||
<div class="title">用户登录</div>
|
||||
<div class="subtitle">支持账号登录与扫码登录</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<section class="card card--form">
|
||||
<div class="card__head">
|
||||
<div class="card__title">账号登录</div>
|
||||
<RouterLink class="ghost" to="/">返回首页</RouterLink>
|
||||
</div>
|
||||
<div class="card__body">
|
||||
<div class="form">
|
||||
<label class="field">
|
||||
<div class="label">账号</div>
|
||||
<input v-model.trim="userName" class="input" placeholder="例如:admin" autocomplete="username" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<div class="label">密码</div>
|
||||
<input
|
||||
v-model="password"
|
||||
class="input"
|
||||
placeholder="请输入密码"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
@keyup.enter="accountLogin"
|
||||
/>
|
||||
</label>
|
||||
<div v-if="errorText" class="error">{{ errorText }}</div>
|
||||
<button class="btn btn--primary btn--wide" type="button" :disabled="loading" @click="accountLogin">
|
||||
{{ loading ? '登录中...' : '登录' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card card--qr">
|
||||
<div class="card__head">
|
||||
<div class="card__title">扫码登录</div>
|
||||
<button class="btn" type="button" @click="token = Math.random().toString(36).slice(2, 10).toUpperCase()">
|
||||
刷新
|
||||
</button>
|
||||
</div>
|
||||
<div class="card__body">
|
||||
<div class="qr">
|
||||
<div class="qr__inner">
|
||||
<div class="qr__brand">{{ appName }}</div>
|
||||
<div class="qr__token">TOKEN {{ token }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="tips__list">
|
||||
<li>打开移动端应用/小程序,进入“扫码登录”。</li>
|
||||
<li>对准二维码区域完成扫描。</li>
|
||||
<li>在移动端确认授权后自动登录。</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
min-height: calc(100vh - 60px);
|
||||
padding: 28px 16px;
|
||||
display: grid;
|
||||
place-items: start center;
|
||||
background:
|
||||
radial-gradient(1200px 500px at 20% 0%, rgba(180, 128, 64, 0.15), transparent 60%),
|
||||
radial-gradient(900px 500px at 90% 10%, rgba(32, 92, 64, 0.16), transparent 55%);
|
||||
}
|
||||
|
||||
.panel {
|
||||
width: min(920px, 100%);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
padding: 18px;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 18px 40px rgba(31, 35, 40, 0.08);
|
||||
}
|
||||
|
||||
.panel__header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(31, 35, 40, 0.06);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 16px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 340px;
|
||||
gap: 14px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid rgba(31, 35, 40, 0.08);
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
.card__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 12px 12px;
|
||||
border-bottom: 1px solid rgba(31, 35, 40, 0.06);
|
||||
background: rgba(31, 35, 40, 0.02);
|
||||
}
|
||||
|
||||
.card__title {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.card__body {
|
||||
padding: 14px 12px;
|
||||
}
|
||||
|
||||
.qr__inner {
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 12px;
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
rgba(31, 35, 40, 0.06),
|
||||
rgba(31, 35, 40, 0.06) 10px,
|
||||
rgba(31, 35, 40, 0.02) 10px,
|
||||
rgba(31, 35, 40, 0.02) 20px
|
||||
);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.qr__brand {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.qr__token {
|
||||
margin-top: 8px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tips__list {
|
||||
margin: 12px 0 0;
|
||||
padding-left: 18px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.qr {
|
||||
border: 1px dashed rgba(31, 35, 40, 0.18);
|
||||
border-radius: 14px;
|
||||
padding: 12px;
|
||||
background: rgba(31, 35, 40, 0.02);
|
||||
}
|
||||
|
||||
.form {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border-color: rgba(32, 92, 64, 0.35);
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: 12px;
|
||||
color: rgba(180, 44, 44, 0.95);
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 14px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(32, 92, 64, 0.25);
|
||||
background: rgba(32, 92, 64, 0.12);
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: rgba(32, 92, 64, 0.18);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.btn--primary {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
background: rgba(180, 128, 64, 0.16);
|
||||
}
|
||||
|
||||
.btn--primary:hover {
|
||||
background: rgba(180, 128, 64, 0.22);
|
||||
}
|
||||
|
||||
.btn--wide {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ghost {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ghost:hover {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.page {
|
||||
padding: 18px 14px;
|
||||
}
|
||||
.content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { RouterLink, RouterView, useRouter } from 'vue-router'
|
||||
import { apiFetch } from '../../lib/api'
|
||||
import { clearAuth, getToken, getUser, setUser } from '../../lib/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const user = ref(getUser())
|
||||
const menuOpen = ref(false)
|
||||
const showUserModal = ref(false)
|
||||
const showPasswordModal = ref(false)
|
||||
const oldPassword = ref('')
|
||||
const newPassword = ref('')
|
||||
const newPassword2 = ref('')
|
||||
const passwordError = ref('')
|
||||
const passwordLoading = ref(false)
|
||||
const menuRoot = ref(null)
|
||||
|
||||
const avatarText = computed(() => {
|
||||
const name = user.value?.userName || '用户'
|
||||
const t = String(name).trim()
|
||||
return t ? t.slice(0, 1).toUpperCase() : 'U'
|
||||
})
|
||||
|
||||
async function logout() {
|
||||
try {
|
||||
await apiFetch('/api/logout', { method: 'POST' })
|
||||
} catch {
|
||||
} finally {
|
||||
clearAuth()
|
||||
}
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
async function loadMe() {
|
||||
if (!getToken()) return
|
||||
try {
|
||||
const res = await apiFetch('/api/me')
|
||||
if (res?.user) {
|
||||
user.value = res.user
|
||||
setUser(res.user)
|
||||
}
|
||||
} catch {
|
||||
clearAuth()
|
||||
router.push('/login')
|
||||
}
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
menuOpen.value = false
|
||||
}
|
||||
|
||||
function onDocumentClick(e) {
|
||||
const root = menuRoot.value
|
||||
if (!root) return
|
||||
if (root.contains(e.target)) return
|
||||
closeMenu()
|
||||
}
|
||||
|
||||
function openUser() {
|
||||
closeMenu()
|
||||
showUserModal.value = true
|
||||
}
|
||||
|
||||
function openPassword() {
|
||||
closeMenu()
|
||||
passwordError.value = ''
|
||||
oldPassword.value = ''
|
||||
newPassword.value = ''
|
||||
newPassword2.value = ''
|
||||
showPasswordModal.value = true
|
||||
}
|
||||
|
||||
async function submitPassword() {
|
||||
if (passwordLoading.value) return
|
||||
passwordError.value = ''
|
||||
const oldPwd = oldPassword.value.trim()
|
||||
const newPwd = newPassword.value.trim()
|
||||
const newPwd2 = newPassword2.value.trim()
|
||||
if (!oldPwd || !newPwd || !newPwd2) {
|
||||
passwordError.value = '请填写完整'
|
||||
return
|
||||
}
|
||||
if (newPwd.length < 6) {
|
||||
passwordError.value = '新密码至少6位'
|
||||
return
|
||||
}
|
||||
if (newPwd !== newPwd2) {
|
||||
passwordError.value = '两次输入的新密码不一致'
|
||||
return
|
||||
}
|
||||
passwordLoading.value = true
|
||||
try {
|
||||
await apiFetch('/api/password', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
oldPassword: oldPwd,
|
||||
newPassword: newPwd,
|
||||
}),
|
||||
})
|
||||
showPasswordModal.value = false
|
||||
} catch (e) {
|
||||
passwordError.value = e?.message || '修改失败'
|
||||
} finally {
|
||||
passwordLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadMe()
|
||||
document.addEventListener('click', onDocumentClick, true)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', onDocumentClick, true)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="admin">
|
||||
<aside class="sider">
|
||||
<RouterLink class="sider__brand" to="/admin">
|
||||
<div class="mark">遗</div>
|
||||
<div>
|
||||
<div class="title">管理后台</div>
|
||||
<div class="sub">古建筑保护信息化平台</div>
|
||||
</div>
|
||||
</RouterLink>
|
||||
|
||||
<nav class="menu">
|
||||
<RouterLink class="item" active-class="is-active" to="/admin/archives">资源档案</RouterLink>
|
||||
<RouterLink class="item" active-class="is-active" to="/admin/inspections">巡查监测</RouterLink>
|
||||
<RouterLink class="item" active-class="is-active" to="/admin/projects">修缮项目</RouterLink>
|
||||
<RouterLink class="item" active-class="is-active" to="/admin/analytics">统计分析</RouterLink>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="main">
|
||||
<header class="header">
|
||||
<div class="header__left">古建筑保护信息化平台 · 管理后台</div>
|
||||
<div class="header__right">
|
||||
<div ref="menuRoot" class="userbox">
|
||||
<button class="userbtn" type="button" @click.stop="menuOpen = !menuOpen">
|
||||
<span class="avatar">{{ avatarText }}</span>
|
||||
<span class="username">{{ user?.userName || '管理员' }}</span>
|
||||
<span class="chev" :class="{ 'is-open': menuOpen }">▾</span>
|
||||
</button>
|
||||
<div v-if="menuOpen" class="menu2">
|
||||
<button class="menu2__item" type="button" @click="openUser">当前用户</button>
|
||||
<button class="menu2__item" type="button" @click="openPassword">修改密码</button>
|
||||
<div class="menu2__divider" />
|
||||
<button class="menu2__item is-danger" type="button" @click="logout">退出登录</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<RouterView />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showUserModal" class="modal" @click.self="showUserModal = false">
|
||||
<div class="dialog">
|
||||
<div class="dialog__head">
|
||||
<div class="dialog__title">当前用户</div>
|
||||
<button class="iconbtn" type="button" @click="showUserModal = false">×</button>
|
||||
</div>
|
||||
<div class="dialog__body">
|
||||
<div class="kv">
|
||||
<div class="k">用户名</div>
|
||||
<div class="v">{{ user?.userName || '-' }}</div>
|
||||
</div>
|
||||
<div class="kv">
|
||||
<div class="k">用户ID</div>
|
||||
<div class="v mono">{{ user?.id || '-' }}</div>
|
||||
</div>
|
||||
<div class="kv">
|
||||
<div class="k">角色ID</div>
|
||||
<div class="v mono">{{ user?.roleId || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog__foot">
|
||||
<button class="btn2" type="button" @click="showUserModal = false">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showPasswordModal" class="modal" @click.self="showPasswordModal = false">
|
||||
<div class="dialog">
|
||||
<div class="dialog__head">
|
||||
<div class="dialog__title">修改密码</div>
|
||||
<button class="iconbtn" type="button" @click="showPasswordModal = false">×</button>
|
||||
</div>
|
||||
<div class="dialog__body">
|
||||
<label class="field">
|
||||
<div class="label">旧密码</div>
|
||||
<input v-model="oldPassword" class="input" type="password" autocomplete="current-password" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<div class="label">新密码</div>
|
||||
<input v-model="newPassword" class="input" type="password" autocomplete="new-password" />
|
||||
</label>
|
||||
<label class="field">
|
||||
<div class="label">确认新密码</div>
|
||||
<input v-model="newPassword2" class="input" type="password" autocomplete="new-password" @keyup.enter="submitPassword" />
|
||||
</label>
|
||||
<div v-if="passwordError" class="error">{{ passwordError }}</div>
|
||||
</div>
|
||||
<div class="dialog__foot">
|
||||
<button class="btn2" type="button" @click="showPasswordModal = false">取消</button>
|
||||
<button class="btn2 btn2--primary" type="button" :disabled="passwordLoading" @click="submitPassword">
|
||||
{{ passwordLoading ? '提交中...' : '确认修改' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.admin {
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: 240px 1fr;
|
||||
}
|
||||
|
||||
.sider {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
border-right: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.sider__brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 10px 14px;
|
||||
border-bottom: 1px solid rgba(31, 35, 40, 0.06);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sider__brand:hover .title {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.sider__brand:hover .sub {
|
||||
color: rgba(31, 35, 40, 0.72);
|
||||
}
|
||||
|
||||
.mark {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: linear-gradient(135deg, #b48040, #205c40);
|
||||
color: #fff;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 900;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.sub {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.menu {
|
||||
margin-top: 12px;
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.item {
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--muted);
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
color: var(--text);
|
||||
border-color: rgba(180, 128, 64, 0.25);
|
||||
background: rgba(180, 128, 64, 0.08);
|
||||
}
|
||||
|
||||
.item.is-active {
|
||||
color: var(--text);
|
||||
border-color: rgba(32, 92, 64, 0.28);
|
||||
background: rgba(32, 92, 64, 0.1);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 5;
|
||||
height: 52px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 18px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.header__left {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.header__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.userbox {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.userbtn {
|
||||
height: 34px;
|
||||
padding: 0 10px 0 6px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--text);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.userbtn:hover {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 999px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: linear-gradient(135deg, #b48040, #205c40);
|
||||
color: #fff;
|
||||
font-weight: 900;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 13px;
|
||||
color: rgba(31, 35, 40, 0.8);
|
||||
}
|
||||
|
||||
.chev {
|
||||
font-size: 12px;
|
||||
color: rgba(31, 35, 40, 0.55);
|
||||
transition: transform 0.15s ease;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.chev.is-open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.menu2 {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 8px);
|
||||
min-width: 180px;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 12px 30px rgba(31, 35, 40, 0.12);
|
||||
overflow: hidden;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.menu2__item {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 10px 12px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
font-weight: 800;
|
||||
color: rgba(31, 35, 40, 0.85);
|
||||
}
|
||||
|
||||
.menu2__item:hover {
|
||||
background: rgba(31, 35, 40, 0.04);
|
||||
}
|
||||
|
||||
.menu2__item.is-danger {
|
||||
color: rgba(180, 44, 44, 0.95);
|
||||
}
|
||||
|
||||
.menu2__divider {
|
||||
height: 1px;
|
||||
background: rgba(31, 35, 40, 0.08);
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(31, 35, 40, 0.35);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 18px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
width: min(520px, 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 18px 40px rgba(31, 35, 40, 0.18);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dialog__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid rgba(31, 35, 40, 0.08);
|
||||
}
|
||||
|
||||
.dialog__title {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.iconbtn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.iconbtn:hover {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
}
|
||||
|
||||
.dialog__body {
|
||||
padding: 14px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dialog__foot {
|
||||
padding: 12px 14px;
|
||||
border-top: 1px solid rgba(31, 35, 40, 0.08);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.kv {
|
||||
display: grid;
|
||||
grid-template-columns: 88px 1fr;
|
||||
gap: 10px;
|
||||
align-items: baseline;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.08);
|
||||
border-radius: 12px;
|
||||
background: rgba(31, 35, 40, 0.02);
|
||||
}
|
||||
|
||||
.k {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.v {
|
||||
font-weight: 800;
|
||||
color: rgba(31, 35, 40, 0.86);
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border-color: rgba(32, 92, 64, 0.35);
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: 12px;
|
||||
color: rgba(180, 44, 44, 0.95);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn2 {
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
font-weight: 800;
|
||||
color: rgba(31, 35, 40, 0.85);
|
||||
}
|
||||
|
||||
.btn2:hover {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
}
|
||||
|
||||
.btn2:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.btn2--primary {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
background: rgba(180, 128, 64, 0.16);
|
||||
}
|
||||
|
||||
.btn2--primary:hover {
|
||||
background: rgba(180, 128, 64, 0.22);
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.admin {
|
||||
grid-template-columns: 88px 1fr;
|
||||
}
|
||||
.sider__brand .title,
|
||||
.sider__brand .sub {
|
||||
display: none;
|
||||
}
|
||||
.item {
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.admin {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.sider {
|
||||
position: static;
|
||||
height: auto;
|
||||
}
|
||||
.menu {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
.item {
|
||||
justify-content: center;
|
||||
padding: 0 8px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const now = new Date()
|
||||
const dateText = computed(() => {
|
||||
const y = now.getFullYear()
|
||||
const m = String(now.getMonth() + 1).padStart(2, '0')
|
||||
const d = String(now.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${d}`
|
||||
})
|
||||
|
||||
const metrics = [
|
||||
{ label: '在册古建筑', value: 128, hint: '建档对象' },
|
||||
{ label: '本月巡查', value: 42, hint: '含日常与专项' },
|
||||
{ label: '隐患处置', value: 8, hint: '整改跟踪中' },
|
||||
{ label: '在建项目', value: 5, hint: '施工阶段' },
|
||||
]
|
||||
|
||||
const tasks = [
|
||||
{ title: '隐患复核:文庙大成殿', meta: '优先级:中 · 截止:本周' },
|
||||
{ title: '项目进度:永宁寺大殿屋面修缮', meta: '阶段:施工 · 更新:昨日' },
|
||||
{ title: '巡查计划:重点片区专项巡查', meta: '执行人:张三 · 时间:明日' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="head">
|
||||
<div>
|
||||
<div class="title">仪表盘</div>
|
||||
<div class="sub">欢迎回来 · {{ dateText }}</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<RouterLink class="action" to="/admin/archives">新建档案</RouterLink>
|
||||
<RouterLink class="action" to="/admin/inspections">新建巡查</RouterLink>
|
||||
<RouterLink class="action" to="/admin/projects">新建项目</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="grid">
|
||||
<div v-for="m in metrics" :key="m.label" class="card">
|
||||
<div class="label">{{ m.label }}</div>
|
||||
<div class="value">{{ m.value }}</div>
|
||||
<div class="hint">{{ m.hint }}</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel__title">待办事项</div>
|
||||
<div class="list">
|
||||
<div v-for="t in tasks" :key="t.title" class="item">
|
||||
<div class="item__title">{{ t.title }}</div>
|
||||
<div class="item__meta">{{ t.meta }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.sub {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.action {
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.12);
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-weight: 800;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.action:hover {
|
||||
border-color: rgba(180, 128, 64, 0.35);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.value {
|
||||
margin-top: 8px;
|
||||
font-size: 28px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-top: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.panel__title {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-top: 10px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 12px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(31, 35, 40, 0.08);
|
||||
background: rgba(31, 35, 40, 0.02);
|
||||
}
|
||||
|
||||
.item__title {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.item__meta {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.page {
|
||||
padding: 16px;
|
||||
}
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '../pages/Home.vue'
|
||||
import Archives from '../pages/Archives.vue'
|
||||
import Inspections from '../pages/Inspections.vue'
|
||||
import Projects from '../pages/Projects.vue'
|
||||
import Analytics from '../pages/Analytics.vue'
|
||||
import QrLogin from '../pages/QrLogin.vue'
|
||||
import AdminLayout from '../pages/admin/AdminLayout.vue'
|
||||
import AdminDashboard from '../pages/admin/Dashboard.vue'
|
||||
import { getToken } from '../lib/auth'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: Home,
|
||||
},
|
||||
{
|
||||
path: '/archives',
|
||||
name: 'archives',
|
||||
component: Archives,
|
||||
},
|
||||
{
|
||||
path: '/inspections',
|
||||
name: 'inspections',
|
||||
component: Inspections,
|
||||
},
|
||||
{
|
||||
path: '/projects',
|
||||
name: 'projects',
|
||||
component: Projects,
|
||||
},
|
||||
{
|
||||
path: '/analytics',
|
||||
name: 'analytics',
|
||||
component: Analytics,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: QrLogin,
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
component: AdminLayout,
|
||||
children: [
|
||||
{ path: '', name: 'admin-home', component: AdminDashboard },
|
||||
{ path: 'dashboard', redirect: '/admin' },
|
||||
{ path: 'archives', name: 'admin-archives', component: Archives },
|
||||
{ path: 'inspections', name: 'admin-inspections', component: Inspections },
|
||||
{ path: 'projects', name: 'admin-projects', component: Projects },
|
||||
{ path: 'analytics', name: 'admin-analytics', component: Analytics },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
router.beforeEach((to) => {
|
||||
if (to.path.startsWith('/admin') && !getToken()) {
|
||||
return { path: '/login' }
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
:root {
|
||||
--bg: #f6f7f9;
|
||||
--surface: #ffffff;
|
||||
--text: #1f2328;
|
||||
--muted: rgba(31, 35, 40, 0.68);
|
||||
--border: rgba(31, 35, 40, 0.12);
|
||||
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue',
|
||||
Arial, 'Noto Sans', 'Liberation Sans', sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
Loading…
Reference in New Issue