commit 01bdf0eb17de11b56a38aade7edce3a3159b37a7 Author: liuhaodong Date: Wed Aug 19 14:07:49 2026 +0800 Initial commit: laic frontend Vue project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7535211 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +*.log +.DS_Store diff --git a/index.html b/index.html new file mode 100644 index 0000000..a12b994 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + 嘉谷低空智控平台 + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a3fa189 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1690 @@ +{ + "name": "laic-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "laic-frontend", + "version": "0.1.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "axios": "^1.6.8", + "element-plus": "^2.7.3", + "pinia": "^2.1.7", + "vue": "^3.4.27", + "vue-router": "^4.3.2" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "vite": "^5.2.11" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz", + "integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz", + "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz", + "integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.41", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz", + "integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==", + "dependencies": { + "@vue/compiler-core": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz", + "integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.41", + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-ssr": "3.5.41", + "@vue/shared": "3.5.41", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz", + "integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.41.tgz", + "integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==", + "dependencies": { + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.41.tgz", + "integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz", + "integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/runtime-core": "3.5.41", + "@vue/shared": "3.5.41", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.41.tgz", + "integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==", + "dependencies": { + "@vue/compiler-ssr": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz", + "integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==" + }, + "node_modules/@vueuse/core": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz", + "integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.3.0", + "@vueuse/shared": "14.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.3.0.tgz", + "integrity": "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.3.0.tgz", + "integrity": "sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/element-plus": { + "version": "2.14.4", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.14.4.tgz", + "integrity": "sha512-vMKR9tFcLeNrJgFXA3zhUn6YuRKUQW9d0btakBR8U1Iq8MfzkjMOGcgs5de2VgiLldHt69brmuBHpxc3bK4ZgQ==", + "dependencies": { + "@ctrl/tinycolor": "^4.2.0", + "@element-plus/icons-vue": "^2.3.2", + "@floating-ui/dom": "^1.8.0", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.8", + "@types/lodash": "^4.17.24", + "@types/lodash-es": "^4.17.12", + "@vueuse/core": "14.3.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.20", + "lodash": "^4.18.1", + "lodash-es": "^4.18.1", + "lodash-unified": "^1.0.3", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0", + "vue-component-type-helpers": "^3.3.9" + }, + "peerDependencies": { + "vue": "^3.3.7" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.41.tgz", + "integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-sfc": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/server-renderer": "3.5.41", + "@vue/shared": "3.5.41" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-3.3.9.tgz", + "integrity": "sha512-3c/UfMe0SqyEfcGTyH7mfshHagJ9QTCbppCb0/uGpHZpFug7+If3GeGZN7I0YheKEExemx3xldQPoO7PQSOLQg==" + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1bcdab5 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "laic-frontend", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "axios": "^1.6.8", + "element-plus": "^2.7.3", + "pinia": "^2.1.7", + "vue": "^3.4.27", + "vue-router": "^4.3.2" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "vite": "^5.2.11" + } +} diff --git a/public/assets/login-hero-dock-drone.png b/public/assets/login-hero-dock-drone.png new file mode 100644 index 0000000..050ea69 Binary files /dev/null and b/public/assets/login-hero-dock-drone.png differ diff --git a/public/assets/map-base-clean.png b/public/assets/map-base-clean.png new file mode 100644 index 0000000..cef19db Binary files /dev/null and b/public/assets/map-base-clean.png differ diff --git a/public/assets/map-base.png b/public/assets/map-base.png new file mode 100644 index 0000000..abdbb58 Binary files /dev/null and b/public/assets/map-base.png differ diff --git a/public/assets/reference-map-source.png b/public/assets/reference-map-source.png new file mode 100644 index 0000000..7b8a089 Binary files /dev/null and b/public/assets/reference-map-source.png differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..de2acf6 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,65 @@ + + + diff --git a/src/api/request.js b/src/api/request.js new file mode 100644 index 0000000..79f6090 --- /dev/null +++ b/src/api/request.js @@ -0,0 +1,45 @@ +import axios from 'axios' +import { useUserStore } from '../store/user' +import { useUiStore } from '../store/ui' +import router from '../router' + +const request = axios.create({ + baseURL: '/api', + timeout: 15000 +}) + +request.interceptors.request.use((config) => { + const userStore = useUserStore() + if (userStore.token) { + config.headers.Authorization = `Bearer ${userStore.token}` + } + return config +}) + +request.interceptors.response.use( + (response) => { + const res = response.data + if (res && res.code === 200) { + return res.data + } + useUiStore().toast(res?.msg || '请求失败') + return Promise.reject(new Error(res?.msg || '请求失败')) + }, + (error) => { + const status = error.response?.status + const data = error.response?.data + const userStore = useUserStore() + if (status === 401) { + userStore.logout() + router.push('/login') + useUiStore().toast(data?.msg || '未登录,请重新登录') + } else if (status === 403) { + useUiStore().toast(data?.msg || '无权限访问') + } else { + useUiStore().toast(data?.msg || error.message || '网络错误') + } + return Promise.reject(error) + } +) + +export default request diff --git a/src/components/DockEditModal.vue b/src/components/DockEditModal.vue new file mode 100644 index 0000000..f8ee47c --- /dev/null +++ b/src/components/DockEditModal.vue @@ -0,0 +1,91 @@ + + + diff --git a/src/layout/AppShell.vue b/src/layout/AppShell.vue new file mode 100644 index 0000000..4e98f1e --- /dev/null +++ b/src/layout/AppShell.vue @@ -0,0 +1,130 @@ + + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..6f3dc12 --- /dev/null +++ b/src/main.js @@ -0,0 +1,10 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import App from './App.vue' +import router from './router' +import './styles/prototype.css' + +const app = createApp(App) +app.use(createPinia()) +app.use(router) +app.mount('#app') diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..7725f81 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,56 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { useUserStore } from '../store/user' +import AppShell from '../layout/AppShell.vue' + +const routes = [ + { path: '/login', name: 'login', component: () => import('../views/Login.vue') }, + { + path: '/', + component: AppShell, + redirect: '/monitor', + children: [ + { path: 'monitor', name: 'monitor', component: () => import('../views/Monitor.vue') }, + { path: 'devices', name: 'devices', component: () => import('../views/Devices.vue') }, + { path: 'tasks', name: 'tasks', component: () => import('../views/Tasks.vue') }, + { path: 'replay/:key', name: 'replay', component: () => import('../views/Replay.vue') }, + { path: 'media', name: 'media', component: () => import('../views/Media.vue') }, + { path: 'account', name: 'account', component: () => import('../views/Account.vue') }, + { path: 'alarms', name: 'alarms', component: () => import('../views/Alarms.vue') }, + { path: 'system', name: 'system', component: () => import('../views/System.vue'), meta: { admin: true } }, + { path: 'users', name: 'users', component: () => import('../views/Users.vue'), meta: { admin: true } }, + { path: 'firmwares', name: 'firmwares', component: () => import('../views/Firmwares.vue'), meta: { admin: true } }, + { path: 'control/:id?', name: 'control', component: () => import('../views/Control.vue'), meta: { userOnly: true } }, + { path: 'logs', name: 'logs', component: () => import('../views/OperationLogs.vue'), meta: { userOnly: true } }, + { path: 'device/:id', name: 'device-detail', component: () => import('../views/DeviceDetail.vue') } + ] + }, + { path: '/:pathMatch(.*)*', redirect: '/monitor' } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +router.beforeEach((to, from, next) => { + const userStore = useUserStore() + if (to.path === '/login') { + next() + return + } + if (!userStore.token) { + next('/login') + return + } + if (to.meta.admin && !userStore.isAdmin()) { + next('/monitor') + return + } + if (to.meta.userOnly && userStore.isAdmin()) { + next('/monitor') + return + } + next() +}) + +export default router diff --git a/src/store/devices.js b/src/store/devices.js new file mode 100644 index 0000000..b0f7c1d --- /dev/null +++ b/src/store/devices.js @@ -0,0 +1,313 @@ +import { defineStore } from 'pinia' +import { reactive, ref } from 'vue' +import request from '../api/request' + +// 后端 status 值 → 展示文案 + 状态样式 +const DOCK_STATUS = { + online: { status: '在线', statusClass: 'online' }, + offline: { status: '离线', statusClass: 'offline' } +} + +function fmtTime(t) { + if (!t) return '--' + const d = new Date(t) + if (Number.isNaN(d.getTime())) return '--' + return d.toLocaleString('zh-CN', { hour12: false }) +} + +function gpsText(q) { + const key = String(q || '').toUpperCase().replace(/-/g, '_') + switch (key) { + case 'RTK_FIX': + case 'RTK_FIXED': return 'RTK 固定解' + case 'RTK_FLOAT': return 'RTK 浮点解' + case '3D_FIX': return '3D 定位' + case '2D_FIX': return '2D 定位' + default: return q ? String(q) : '无定位' + } +} + +function modeText(m) { + const key = String(m || '').toUpperCase() + switch (key) { + case 'STANDBY': return '待命' + case 'AUTO': return 'AUTO' + case 'GUIDED': return 'GUIDED' + case 'LOITER': return '悬停' + case 'RTL': return '返航' + default: return m || '--' + } +} + +function controlModeText(m) { + const key = String(m || '').toLowerCase() + if (key === 'auto' || key === 'automatic') return '自动' + if (key === 'manual') return '手动' + return m || '--' +} + +function parseJson(value, fallback = null) { + if (!value) return fallback + if (typeof value === 'object') return value + try { return JSON.parse(value) } catch { return fallback } +} + +function environmentValues(realtime) { + const nested = parseJson(realtime?.environment, {}) || {} + const value = (key, ...aliases) => { + for (const name of [key, ...aliases]) { + if (nested[name] != null && nested[name] !== '') return nested[name] + if (realtime?.[name] != null && realtime[name] !== '') return realtime[name] + } + return null + } + return { + rain: value('rain'), + windSpeed: value('windSpeed', 'wind_speed'), + outsideTemperature: value('outsideTemperature', 'outside_temperature'), + outsideHumidity: value('outsideHumidity', 'outside_humidity'), + insideTemperature: value('insideTemperature', 'inside_temperature', 'temperature'), + insideHumidity: value('insideHumidity', 'inside_humidity', 'humidity') + } +} + +function alarmCodes(realtime) { + const codes = parseJson(realtime?.alarmCodes, []) + return Array.isArray(codes) ? codes : [] +} + +function displayValue(value) { + return value == null || value === '' ? '--' : String(value) +} + +function coordinate(value, min, max) { + const number = Number(value) + return Number.isFinite(number) && number >= min && number <= max ? number : null +} + +// 指令标题 → 后端指令 type +const COMMANDS = { + '一键起飞': 'workflow.one_key_takeoff', + '一键降落': 'workflow.one_key_land', + '起飞准备': 'dock.prepare_takeoff', + '起飞准备完成': 'dock.complete_takeoff', + '降落准备': 'dock.prepare_landing', + '降落准备完成': 'dock.complete_landing', + '打开舱门': 'dock.open', + '关闭舱门': 'dock.close', + '开始充电': 'dock.charge_start', + '整机复位': 'dock.reset', + '机巢复位': 'dock.reset', + '无人机开机': 'dock.drone_power_on', + '无人机关机': 'dock.drone_power_off', + '远程重启': 'dock.reboot', + '清除设备告警': 'dock.clear_alarm', + '设备自检': 'dock.self_check', + '起飞': 'drone.takeoff', + '降落': 'drone.land', + '一键返航': 'drone.return', + '悬停': 'drone.hover', + '紧急停止': 'drone.emergency_stop', + '开始任务': 'drone.mission_start', + '暂停任务': 'drone.mission_pause', + '继续任务': 'drone.mission_resume', + '取消任务': 'drone.mission_cancel' +} + +// 经纬度 → 地图百分比坐标(按设备经纬范围动态投影,北向上) +function project(lon, lat, bounds) { + const lonSpan = bounds.maxLon - bounds.minLon || 1 + const latSpan = bounds.maxLat - bounds.minLat || 1 + const x = ((lon - bounds.minLon) / lonSpan) * 80 + 10 + const y = ((bounds.maxLat - lat) / latSpan) * 70 + 15 + return { x: Math.round(x * 10) / 10, y: Math.round(y * 10) / 10 } +} + +export const useDeviceStore = defineStore('devices', () => { + const assets = reactive({}) + const docks = ref([]) + const drones = ref([]) + const loaded = ref(false) + + function asset(id) { + return assets[id] || null + } + + function record(id) { + const key = String(id) + return docks.value.find((d) => d.id === key) || drones.value.find((d) => d.id === key) || null + } + + function commandType(title) { + return COMMANDS[title] || '' + } + + async function sendCommand(rec, title, params = {}) { + const type = COMMANDS[title] + if (!type) throw new Error('未知指令') + if (rec.kind === 'dock') { + return request.post(`/v1/docks/${rec.id}/command`, { type, params }) + } + const dock = docks.value.find((d) => d.dockId === rec.dockId) + if (!dock) throw new Error('未找到绑定机巢') + return request.post(`/v1/docks/${dock.id}/command`, { type, params, droneSn: rec.droneSn }) + } + + async function load() { + const [dockPage, dronePage] = await Promise.all([ + request.get('/v1/docks', { params: { pageNum: 1, pageSize: 100 } }), + request.get('/v1/drones', { params: { pageNum: 1, pageSize: 100 } }) + ]) + + const dockList = dockPage?.records || [] + const droneList = dronePage?.records || [] + const [dockDetails, droneDetails] = await Promise.all([ + Promise.all(dockList.map((d) => request.get(`/v1/docks/${d.id}`).then((r) => r || {}).catch(() => ({})))), + Promise.all(droneList.map((d) => request.get(`/v1/drones/${d.id}`).then((r) => r || {}).catch(() => ({})))) + ]) + const coords = dockList + .map((d) => ({ longitude: coordinate(d.longitude, -180, 180), latitude: coordinate(d.latitude, -90, 90) })) + .filter((d) => d.longitude != null && d.latitude != null) + const defaultLon = coords[0]?.longitude ?? 0 + const defaultLat = coords[0]?.latitude ?? 0 + const bounds = { + minLon: Math.min(...coords.map((d) => Number(d.longitude)), defaultLon), + maxLon: Math.max(...coords.map((d) => Number(d.longitude)), defaultLon), + minLat: Math.min(...coords.map((d) => Number(d.latitude)), defaultLat), + maxLat: Math.max(...coords.map((d) => Number(d.latitude)), defaultLat) + } + if (bounds.minLon === bounds.maxLon) { bounds.minLon -= 0.01; bounds.maxLon += 0.01 } + if (bounds.minLat === bounds.maxLat) { bounds.minLat -= 0.01; bounds.maxLat += 0.01 } + + const newDocks = dockList.map((d, i) => { + const id = String(d.id) + const detail = dockDetails[i] || {} + const rt = detail.realtime || {} + const online = detail.online ?? d.status === 'online' + const registerStatus = d.registerStatus || 'registered' + const pending = registerStatus === 'pending' && !online + const st = pending + ? { status: '待连接', statusClass: 'pending' } + : online + ? alarmCodes(rt).length ? { status: '告警', statusClass: 'alarm' } : DOCK_STATUS.online + : DOCK_STATUS.offline + const name = d.name || d.dockId + const environment = environmentValues(rt) + return { + id, + kind: 'dock', + dockId: d.dockId, + name, + code: d.code || d.sn || d.dockId, + sn: d.sn || d.dockId || '--', + iccid: d.iccid || '--', + longitude: coordinate(d.longitude, -180, 180), + latitude: coordinate(d.latitude, -90, 90), + location: d.location || '--', + status: st.status, + statusClass: st.statusClass, + binding: '--', + bindingState: 'unbound', + mode: controlModeText(rt.controlMode), + version: d.softwareVer ? `v${d.softwareVer}` : '--', + updated: fmtTime(d.updatedAt), + protocol: d.protocolVer ? `MQTT v${d.protocolVer}` : '--', + registerStatus, + realtime: rt, + environment, + alarms: alarmCodes(rt), + online + } + }) + + const newDrones = droneList.map((d, i) => { + const id = String(d.id) + const detail = droneDetails[i] || {} + const rt = detail.realtime || {} + const armed = ['1', 'true', 1, true].includes(rt.armed) + const altitude = coordinate(rt.altitude, -1000, 100000) + const flying = armed || (altitude != null && altitude > 0) + const online = detail.online ?? d.status !== 'offline' + const st = !online + ? { status: '离线', statusClass: 'offline' } + : flying + ? { status: '飞行中', statusClass: 'mission' } + : { status: '在巢', statusClass: 'online' } + const dock = newDocks.find((dk) => dk.dockId === d.dockId) + const battery = rt.batteryPercent ?? rt.batteryPct ?? d.battery + return { + id, + kind: 'drone', + droneSn: d.droneSn, + dockId: d.dockId, + name: d.name || d.droneSn, + code: d.code || d.droneSn, + status: st.status, + statusClass: st.statusClass, + binding: dock?.name || '--', + bindingState: dock ? 'bound' : 'unbound', + battery: battery != null && battery !== '' ? `${battery}%` : '--', + mode: modeText(rt.flightMode), + mission: displayValue(rt.missionName || rt.mission), + updated: fmtTime(d.updatedAt), + sysid: displayValue(rt.currentSysId), + gps: gpsText(rt.gpsQuality), + sn: d.droneSn || d.code || '--', + model: d.model || '--', + firmwareVer: d.firmwareVer || '--', + localRoutes: [], + realtime: rt, + alarms: alarmCodes(rt), + online, + _lat: coordinate(rt.latitude, -90, 90), + _lon: coordinate(rt.longitude, -180, 180), + _alt: altitude, + _speed: coordinate(rt.groundSpeed, 0, 1000), + _sats: coordinate(rt.satellites, 0, 1000) + } + }) + + for (const dk of newDocks) { + const dr = newDrones.find((d) => d.dockId === dk.dockId) + if (dr) { + dk.binding = dr.name + dk.bindingState = 'bound' + } + } + + docks.value = newDocks + drones.value = newDrones + + // 重建地图资产 + Object.keys(assets).forEach((k) => delete assets[k]) + for (const dk of newDocks) { + const hasCoordinates = dk.longitude != null && dk.latitude != null + const p = hasCoordinates ? project(dk.longitude, dk.latitude, bounds) : { x: null, y: null } + assets[dk.id] = { + id: dk.id, type: 'dock', name: dk.name, location: dk.location, + status: dk.status, statusClass: dk.statusClass, x: p.x, y: p.y, hasCoordinates, + realtime: dk.realtime, environment: dk.environment, alarms: dk.alarms, online: dk.online + } + } + for (const dr of newDrones) { + const dock = newDocks.find((d) => d.dockId === dr.dockId) + const lon = dr._lon != null ? dr._lon : dock?.longitude + const lat = dr._lat != null ? dr._lat : dock?.latitude + const hasCoordinates = lon != null && lat != null + const p = hasCoordinates ? project(lon, lat, bounds) : { x: null, y: null } + assets[dr.id] = { + id: dr.id, type: 'drone', name: dr.name, + location: dr.statusClass === 'mission' ? `${dock?.location || '--'} · 飞行中` : `停放于${dock?.name || '--'}`, + status: dr.status, statusClass: dr.statusClass, x: p.x, y: p.y, hasCoordinates, + parent: dock?.id || '', parentName: dock?.name || '--', + inDock: dr.statusClass !== 'mission', altitude: dr._alt, speed: dr._speed, satellites: dr._sats, + realtime: dr.realtime, alarms: dr.alarms, online: dr.online + } + } + + loaded.value = true + return { docks: newDocks, drones: newDrones } + } + + return { assets, docks, drones, loaded, asset, record, commandType, sendCommand, load } +}) diff --git a/src/store/ui.js b/src/store/ui.js new file mode 100644 index 0000000..3331569 --- /dev/null +++ b/src/store/ui.js @@ -0,0 +1,33 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useUiStore = defineStore('ui', () => { + const toastMsg = ref('') + const toastVisible = ref(false) + let toastTimer = null + function toast(message) { + toastMsg.value = message + toastVisible.value = true + clearTimeout(toastTimer) + toastTimer = setTimeout(() => { toastVisible.value = false }, 2200) + } + + const confirmVisible = ref(false) + const confirmTitle = ref('') + const confirmDesc = ref('') + const confirmChecks = ref([]) + let confirmResolve = null + function confirm(title, desc = '系统将校验设备状态和流程互斥条件,确认通过后提交本次操作。', checks = ['设备在线', '无活动告警', '指令互斥检查']) { + confirmTitle.value = title + confirmDesc.value = desc + confirmChecks.value = checks + confirmVisible.value = true + return new Promise((resolve) => { confirmResolve = resolve }) + } + function resolveConfirm(ok) { + confirmVisible.value = false + if (confirmResolve) { confirmResolve(ok); confirmResolve = null } + } + + return { toastMsg, toastVisible, toast, confirmVisible, confirmTitle, confirmDesc, confirmChecks, confirm, resolveConfirm } +}) diff --git a/src/store/user.js b/src/store/user.js new file mode 100644 index 0000000..ffa71b3 --- /dev/null +++ b/src/store/user.js @@ -0,0 +1,27 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useUserStore = defineStore('user', () => { + const token = ref(localStorage.getItem('token') || '') + const user = ref(JSON.parse(localStorage.getItem('user') || 'null')) + + function setLogin(tokenVal, userVal) { + token.value = tokenVal + user.value = userVal + localStorage.setItem('token', tokenVal) + localStorage.setItem('user', JSON.stringify(userVal)) + } + + function logout() { + token.value = '' + user.value = null + localStorage.removeItem('token') + localStorage.removeItem('user') + } + + function isAdmin() { + return !!user.value && user.value.role === 'admin' + } + + return { token, user, setLogin, logout, isAdmin } +}) diff --git a/src/styles/prototype.css b/src/styles/prototype.css new file mode 100644 index 0000000..34c36f7 --- /dev/null +++ b/src/styles/prototype.css @@ -0,0 +1,893 @@ +:root { + color-scheme: light; + --ink: #17202b; + --muted: #697586; + --line: #e2e7ec; + --soft: #f5f7f9; + --sidebar: #18232d; + --sidebar-hover: #24323e; + --blue: #1f6feb; + --blue-dark: #1556ba; + --green: #168a61; + --orange: #c66a18; + --red: #cc3d43; + --shadow: 0 10px 30px rgba(19, 33, 46, .12); +} + +#appShell[hidden], #loginPage[hidden] { display: none !important; } +body:has(.login-page:not([hidden])) { background: #edf2f5; } +.login-page { min-height: 100vh; display: grid; grid-template-columns: minmax(430px, .82fr) minmax(0, 1.18fr); background: #fff; }.login-form-side { grid-column: 1; grid-row: 1; }.login-visual { grid-column: 2; grid-row: 1; } +.login-visual { position: relative; min-height: 100vh; overflow: hidden; color: #fff; background: #1b323c; }.login-visual-map, .login-visual-shade { position: absolute; inset: 0; }.login-visual-map { background-image: url("/assets/login-hero-dock-drone.png"); background-size: cover; background-position: center; filter: saturate(.82) contrast(1.04); }.login-visual-shade { background: linear-gradient(90deg, rgba(10,31,40,.78), rgba(12,38,48,.18) 62%, rgba(9,26,34,.34)), linear-gradient(0deg, rgba(9,26,34,.56), transparent 40%); }.login-brand, .login-visual-copy, .login-visual-footer { position: absolute; z-index: 1; }.login-brand { top: 38px; left: 48px; display: flex; align-items: center; gap: 10px; }.login-brand .brand-mark { width: 28px; height: 28px; }.login-brand .brand-mark span { width: 14px; height: 8px; }.login-brand strong { display: block; font-size: 19px; }.login-brand small { display: block; margin-top: 3px; color: #9db1bc; font-size: 11px; }.login-visual-copy { left: 48px; bottom: 112px; }.login-visual-copy > span { color: #b5d9e7; font-size: 11px; letter-spacing: 1.5px; }.login-visual-copy h1 { margin: 14px 0 13px; color: #fff; font-size: clamp(27px, 3vw, 42px); font-weight: 500; line-height: 1.25; letter-spacing: 0; text-shadow: 0 2px 18px rgba(0,0,0,.25); }.login-visual-copy p { margin: 0; color: #d0e0e5; font-size: 13px; }.login-visual-footer { right: 38px; bottom: 28px; left: 48px; display: flex; justify-content: space-between; color: #b5c7ce; font-size: 10px; } +.login-device-scene { position: absolute; z-index: 1; inset: 0; }.login-scene-line { position: absolute; height: 1px; background: linear-gradient(90deg, transparent, rgba(128,194,224,.8), transparent); transform-origin: left center; }.line-one { width: 34%; top: 38%; left: 38%; transform: rotate(-22deg); }.line-two { width: 30%; top: 54%; left: 49%; transform: rotate(20deg); }.login-scene-card { position: absolute; min-width: 174px; padding: 9px 11px; display: flex; align-items: center; gap: 9px; border: 1px solid rgba(170,220,238,.32); border-radius: 5px; color: #e8f5fa; background: rgba(13,35,45,.68); box-shadow: 0 10px 22px rgba(5,20,28,.2); backdrop-filter: blur(8px); }.login-scene-card > span { width: 25px; height: 25px; display: grid; place-items: center; border-radius: 4px; }.login-scene-card svg { width: 14px; }.login-scene-card strong, .login-scene-card small { display: block; }.login-scene-card strong { font-size: 11px; }.login-scene-card small { margin-top: 3px; color: #8fb4c3; font-size: 8px; }.login-scene-card.dock { top: 35%; left: 30%; }.login-scene-card.dock > span { color: #78d1aa; background: rgba(45,137,103,.25); }.login-scene-card.drone { top: 51%; left: 57%; }.login-scene-card.drone > span { color: #83bdf1; background: rgba(46,111,181,.28); }.login-scene-status { position: absolute; top: 19%; right: 8%; padding: 8px 10px; display: flex; align-items: center; gap: 7px; border: 1px solid rgba(170,220,238,.2); border-radius: 4px; color: #b4d3de; background: rgba(13,35,45,.55); font-size: 10px; }.login-scene-status i { width: 6px; height: 6px; border-radius: 50%; background: #50c995; box-shadow: 0 0 0 4px rgba(80,201,149,.12); }.login-scene-status b { color: #e4f4f7; font-size: 11px; font-weight: 500; } +.login-form-side { display: flex; align-items: center; justify-content: center; padding: 40px 48px; background: #f3f6f9; }.login-form-wrap { width: min(360px, 100%); padding: 42px 40px 36px; border: 1px solid #e1e7ec; border-radius: 8px; background: #fff; box-shadow: 0 16px 40px rgba(28,53,70,.08); }.login-form-heading > span { color: var(--blue); font-size: 12px; }.login-form-heading h2 { margin: 10px 0 7px; color: #263945; font-size: 30px; font-weight: 600; }.login-form-heading p { margin: 0 0 33px; color: var(--muted); font-size: 12px; }.login-form-wrap form > label { display: block; margin-bottom: 18px; }.login-form-wrap form > label > span { display: block; margin-bottom: 7px; color: #495b67; font-size: 12px; }.login-input { height: 44px; padding: 0 12px; display: flex; align-items: center; gap: 9px; border: 1px solid #d7e0e5; border-radius: 4px; background: #fff; transition: border-color .15s ease, box-shadow .15s ease; }.login-input:focus-within { border-color: #6f9ec8; box-shadow: 0 0 0 3px rgba(47,116,189,.1); }.login-input > svg { width: 15px; color: #8a99a3; }.login-input input { width: 100%; min-width: 0; border: 0; outline: 0; color: var(--ink); background: transparent; font-size: 13px; }.login-input button { width: 24px; height: 24px; display: grid; place-items: center; color: #8a99a3; background: transparent; }.login-input button svg { width: 15px; }.login-options { margin: -1px 0 20px; display: flex; align-items: center; justify-content: space-between; }.login-options label { display: flex; align-items: center; gap: 7px; color: #74838d; font-size: 11px; }.login-options label input { position: absolute; opacity: 0; }.login-options label > span { width: 14px; height: 14px; display: block; border: 1px solid #c4d0d7; border-radius: 3px; background: #fff; }.login-options label input:checked + span { border-color: var(--blue); background: var(--blue); box-shadow: inset 0 0 0 3px #fff; }.login-options button { color: var(--blue); background: transparent; font-size: 11px; }.login-error { min-height: 17px; color: var(--red); font-size: 11px; }.login-submit { width: 100%; height: 44px; display: flex; align-items: center; justify-content: center; gap: 8px; border-radius: 4px; color: #fff; background: var(--blue); font-size: 13px; box-shadow: 0 5px 12px rgba(47,116,189,.2); }.login-submit:hover { background: #2766a5; }.login-submit svg { width: 13px; }.login-help { display: block; margin-top: 20px; color: #9aa7ae; font-size: 10px; text-align: center; } +.login-device-scene { display: none; } +.login-panel#loginPanel > .login-form-heading { display: none; }.login-panel#loginPanel .login-method-tabs { margin-top: 0; } +.login-panel { display: none; }.login-panel.active { display: block; }.login-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }.login-form-row > label { min-width: 0; }.login-form-row > label > span { display: block; margin-bottom: 7px; color: #495b67; font-size: 12px; }.login-form-row > label > input { width: 100%; height: 44px; padding: 0 12px; border: 1px solid #d7e0e5; border-radius: 4px; outline: 0; color: var(--ink); background: #fff; font-size: 13px; }.login-form-row > label > input:focus { border-color: #6f9ec8; box-shadow: 0 0 0 3px rgba(47,116,189,.1); }.register-agreement { margin: 0 0 17px; display: flex; align-items: flex-start; gap: 7px; color: #74838d; font-size: 10px; line-height: 1.5; }.register-agreement input { position: absolute; opacity: 0; }.register-agreement > span { width: 14px; height: 14px; min-width: 14px; display: block; border: 1px solid #c4d0d7; border-radius: 3px; background: #fff; }.register-agreement input:checked + span { border-color: var(--blue); background: var(--blue); box-shadow: inset 0 0 0 3px #fff; }.login-switch { color: var(--blue); background: transparent; font-size: inherit; } +.login-method-tabs { height: 38px; margin: -4px 0 21px; display: grid; grid-template-columns: 1fr 1fr; border-bottom: 1px solid #dce4e9; }.login-method-tabs button { position: relative; color: #7d8b94; background: transparent; font-size: 12px; }.login-method-tabs button.active { color: var(--blue); font-weight: 600; }.login-method-tabs button.active::after { content: ""; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; background: var(--blue); }.login-method-fields[hidden] { display: none; }.captcha-input { padding-right: 4px; }.captcha-image { width: 72px !important; height: 34px !important; margin-left: auto; border-left: 1px solid #dce4e9; color: #2f4553; background: #f1f4f5 !important; font-family: Georgia, serif; font-size: 18px !important; font-style: italic; letter-spacing: 3px; }.send-code-button { width: auto !important; min-width: 76px; height: 30px !important; margin-left: auto; padding: 0 8px; border-left: 1px solid #dce4e9; color: var(--blue) !important; background: transparent !important; font-size: 10px !important; white-space: nowrap; } +.login-page { position: relative; display: block; overflow: hidden; background: #102a35; }.login-visual { position: absolute; z-index: 0; inset: 0; width: 100%; min-height: 100vh; }.login-form-side { position: relative; z-index: 2; width: 100%; min-height: 100vh; padding: 40px 24px; display: flex; align-items: center; justify-content: center; background: transparent; }.login-form-wrap { padding-top: 32px; padding-bottom: 30px; box-shadow: 0 20px 55px rgba(10,31,43,.25); background: rgba(255,255,255,.96); backdrop-filter: blur(8px); } + +* { box-sizing: border-box; } +html, body { margin: 0; height: 100%; overflow: hidden; font-family: Inter, "PingFang SC", "Microsoft YaHei", Arial, sans-serif; color: var(--ink); background: #edf1f4; } +button, input { font: inherit; } +button { border: 0; cursor: pointer; } +svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } +.icon-library { position: absolute; width: 0; height: 0; overflow: hidden; } +.app-shell { min-height: 100vh; display: flex; overflow: hidden; } + +.sidebar { width: 208px; min-width: 208px; height: 100vh; background: var(--sidebar); color: #d7e0e8; display: flex; flex-direction: column; z-index: 30; } +.brand { height: 64px; padding: 0 20px; display: flex; align-items: center; gap: 11px; color: #fff; border-bottom: 1px solid rgba(255,255,255,.08); } +.brand > div:last-child { display: flex; flex-direction: column; min-width: 0; } +.brand strong { font-size: 19px; line-height: 20px; letter-spacing: 0; } +.brand small { color: #9fb0be; font-size: 13px; } +.brand-mark { position: relative; width: 28px; height: 28px; transform: rotate(45deg); } +.brand-mark span { position: absolute; width: 12px; height: 12px; border-radius: 8px 2px 8px 2px; } +.brand-mark span:nth-child(1) { left: 1px; top: 1px; background: #3f86ed; } +.brand-mark span:nth-child(2) { right: 1px; bottom: 1px; background: #42b883; } +.brand-mark span:nth-child(3) { right: 1px; top: 1px; background: #f0b44d; } +.main-nav { flex: 1; padding: 18px 10px; } +.nav-item { width: 100%; min-height: 42px; padding: 0 12px; display: flex; align-items: center; gap: 12px; border-radius: 5px; background: transparent; color: #aebbc6; text-align: left; margin-bottom: 5px; transition: .18s ease; } +.nav-item:hover { background: var(--sidebar-hover); color: #fff; } +.nav-item.active { background: #2d6fba; color: #fff; box-shadow: inset 3px 0 #7db5f5; } +.nav-item span { flex: 1; font-size: 17px; } +.nav-item svg { width: 17px; } +.nav-arrow { width: 14px !important; transform: rotate(0); opacity: .6; transition: transform .18s ease; } +.nav-item.expanded .nav-arrow { transform: rotate(90deg); } +.nav-group { margin-bottom: 5px; }.nav-group > .nav-item { margin-bottom: 0; } +.subnav { max-height: 0; margin: 0 0 0 21px; padding-left: 18px; overflow: hidden; border-left: 1px solid rgba(255,255,255,.11); transition: max-height .22s ease, padding .22s ease, margin .22s ease; } +.nav-group.open .subnav { max-height: 128px; margin-top: 5px; padding-top: 3px; padding-bottom: 3px; } +.subnav-item { position: relative; width: 100%; height: 34px; padding: 0 8px 0 3px; display: flex; align-items: center; gap: 9px; border-radius: 4px; color: #8fa0ad; background: transparent; text-align: left; font-size: 14px; } +.subnav-item:hover { color: #e8eef3; background: rgba(255,255,255,.045); }.subnav-item.active { color: #fff; }.subnav-item > span { width: 5px; height: 5px; border: 1px solid #708390; border-radius: 50%; }.subnav-item.active > span { border-color: #71acf0; background: #71acf0; box-shadow: 0 0 0 3px rgba(113,172,240,.12); }.subnav-item b { margin-left: auto; font-size: 11px; font-weight: 500; color: #6f8392; }.subnav-item .pending-count { min-width: 16px; padding: 1px 5px; border-radius: 8px; color: #f4cf98; background: rgba(205,129,34,.2); text-align: center; } +.nav-count { padding: 1px 6px; border-radius: 8px; background: #c8454b; color: #fff; font-size: 13px; font-weight: 600; } +.sidebar-bottom { padding: 0 10px 14px; }.sidebar-bottom .profile-wrap { width: 100%; }.sidebar-profile { width: 100%; min-height: 44px; padding: 0 10px; border-radius: 5px; color: #dbe4eb; text-align: left; }.sidebar-profile:hover { color: #fff; background: var(--sidebar-hover); }.sidebar-profile > span { width: 30px; height: 30px; font-size: 13px; }.sidebar-profile strong { font-size: 12px; }.sidebar-profile small { color: #8fa0ad; font-size: 10px; }.sidebar-profile svg { margin-left: auto; width: 12px; transform: rotate(-90deg); color: #8fa0ad; }.sidebar-bottom .profile-menu { top: auto; right: 0; bottom: calc(100% + 8px); } + +.workspace { position: relative; flex: 1; min-width: 0; height: 100vh; display: flex; flex-direction: column; } +.topbar { height: 64px; min-height: 64px; display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 0 20px; background: #fff; border-bottom: 1px solid var(--line); z-index: 20; } +.breadcrumb { display: flex; align-items: center; gap: 7px; font-size: 16px; white-space: nowrap; } +.breadcrumb span { color: var(--muted); } +.breadcrumb svg { width: 13px; color: #9aa5af; } +.breadcrumb strong { font-weight: 600; } +.profile-wrap { position: relative; } +.profile-menu { position: absolute; z-index: 30; top: calc(100% + 9px); right: 0; width: 190px; padding: 6px; border: 1px solid var(--line); border-radius: 5px; background: #fff; box-shadow: 0 12px 28px rgba(25,42,54,.16); } +.profile-menu-heading { padding: 8px 9px 10px; display: flex; flex-direction: column; gap: 4px; border-bottom: 1px solid var(--line); }.profile-menu-heading strong { color: var(--ink); font-size: 12px; }.profile-menu-heading small { color: var(--muted); font-size: 10px; } +.profile-menu button { width: 100%; height: 30px; padding: 0 9px; border-radius: 3px; color: #53626e; background: transparent; font-size: 11px; text-align: left; }.profile-menu button:hover { color: var(--blue); background: #f1f6fb; }.profile-menu .logout-action { color: #b04b4f; }.profile-menu .logout-action:hover { color: #a53a40; background: #fff3f3; } +.device-search { display: flex; align-items: center; gap: 8px; border: 1px solid var(--line); background: var(--soft); border-radius: 5px; color: #7f8b97; } +.device-search input { width: 100%; min-width: 0; outline: 0; border: 0; background: transparent; font-size: 14px; color: var(--ink); } +.icon-button { width: 34px; height: 34px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; background: transparent; color: #607080; } +.icon-button:hover { background: #eef2f5; color: var(--ink); } +.profile { display: flex; align-items: center; gap: 8px; padding: 0; background: transparent; color: var(--ink); } +.profile > span { width: 32px; height: 32px; display: grid; place-items: center; background: #dfeaf7; color: #225d99; border-radius: 4px; font-size: 14px; font-weight: 700; } +.profile div { display: flex; flex-direction: column; align-items: flex-start; } +.profile strong { font-size: 14px; } +.profile small { color: var(--muted); font-size: 12px; } +.profile svg { width: 13px; transform: rotate(90deg); color: #8a96a1; } +.sidebar-bottom .sidebar-profile { color: #dbe4eb; } +.sidebar-bottom .sidebar-profile strong { color: #f2f6f9; } +.sidebar-bottom .sidebar-profile small { color: #8fa0ad; } +.sidebar-bottom .sidebar-profile > svg { transform: rotate(-90deg); color: #8fa0ad; } +.mobile-menu { display: none; } + +.monitor-layout { position: relative; flex: 1; min-height: 0; display: grid; grid-template-columns: 0 minmax(0, 1fr) 0; overflow: hidden; transition: grid-template-columns .38s cubic-bezier(.22,.8,.25,1); } +.monitor-layout.focused { grid-template-columns: 276px minmax(360px, 1fr) 348px; } +.monitor-layout.focused.control-expanded { grid-template-columns: 276px minmax(360px, 1fr) 560px; } +.device-panel, .detail-panel { background: #fff; min-width: 0; min-height: 0; overflow: auto; opacity: 0; visibility: hidden; z-index: 8; transition: transform .38s cubic-bezier(.22,.8,.25,1), opacity .2s ease, visibility 0s linear .38s; } +.device-panel { width: 276px; border-right: 1px solid var(--line); padding: 18px 14px; transform: translateX(-100%); } +.detail-panel { width: 348px; transform: translateX(100%); } +.monitor-layout.control-expanded .detail-panel { width: 560px; } +.monitor-layout.focused .device-panel, .monitor-layout.focused .detail-panel { opacity: 1; visibility: visible; transform: translateX(0); transition-delay: 0s; } +.panel-heading { display: flex; justify-content: space-between; align-items: center; } +.panel-heading > div { display: flex; align-items: baseline; gap: 8px; } +.panel-heading h1 { margin: 0; font-size: 20px; } +.panel-heading span { color: var(--muted); font-size: 13px; } +.device-search { height: 36px; margin-top: 16px; padding: 0 10px; background: #fff; } +.device-search svg { width: 15px; } +.filter-row { display: flex; border-bottom: 1px solid var(--line); margin-top: 14px; } +.filter { position: relative; flex: 1; height: 36px; background: transparent; color: var(--muted); font-size: 14px; } +.filter b { font-weight: 500; } +.filter.active { color: var(--blue); font-weight: 600; } +.filter.active::after { content: ""; position: absolute; left: 20%; right: 20%; bottom: -1px; height: 2px; background: var(--blue); } +.multi-select-actions { display: flex; align-items: center; gap: 5px; padding: 0 2px 0 7px; white-space: nowrap; } +.multi-select-actions button { padding: 0; color: #627481; background: transparent; font-size: 10px; } +.multi-select-actions button:hover { color: var(--blue); } +.panel-heading .multi-select-actions { margin-left: auto; padding: 0 4px 0 0; } +.device-list { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; } +.multi-dock-bar { min-height: 34px; margin-top: 10px; padding: 0 9px; display: flex; align-items: center; justify-content: space-between; border: 1px solid #c9dff2; border-radius: 4px; color: #486575; background: #f3f8fc; font-size: 11px; } +.multi-dock-bar strong { color: var(--blue); font-size: 12px; } +.multi-dock-bar select { height: 25px; max-width: 90px; padding: 0 4px; border: 1px solid #cbd9e4; border-radius: 3px; color: #526574; background: #fff; font-size: 10px; } +.multi-dock-bar .primary-command { min-height: 25px; padding: 0 8px; font-size: 10px; } +.multi-select-bar { min-height: 34px; margin-top: 10px; padding: 0 9px; display: flex; align-items: center; gap: 7px; border: 1px solid var(--line); border-radius: 4px; color: var(--muted); background: #fafcfd; font-size: 10px; } +.multi-select-bar span { margin-right: auto; color: #607481; } +.multi-select-bar button { min-height: 24px; padding: 0 8px; border: 1px solid #d7e2e9; border-radius: 3px; color: #526875; background: #fff; font-size: 10px; } +.multi-select-bar button:hover, .multi-select-bar.has-selection button { color: var(--blue); border-color: #a6c5df; background: #f3f8fc; } +.device-card { position: relative; width: 100%; min-height: 70px; text-align: left; border: 1px solid var(--line); border-radius: 6px; background: #fff; color: var(--ink); overflow: hidden; transition: .16s ease; } +.device-card:hover { border-color: #aab9c8; transform: translateY(-1px); } +.device-card.active { border-color: #5a93d4; background: #f4f8fd; box-shadow: inset 3px 0 var(--blue); } +.device-card.multi-selected { border-color: var(--blue); box-shadow: inset 3px 0 var(--blue); background: #f4f8fd; } +.device-select-checkbox { position: absolute; z-index: 2; top: 10px; left: 8px; width: 16px; height: 16px; display: block; cursor: pointer; } +.device-select-checkbox input { position: absolute; opacity: 0; } +.device-select-checkbox > span { width: 15px; height: 15px; display: block; border: 1px solid #b8c7d2; border-radius: 3px; background: #fff; } +.device-select-checkbox input:checked + span { border-color: var(--blue); background: var(--blue); box-shadow: inset 0 0 0 3px #fff; } +.device-card-main { width: 100%; min-height: 68px; padding: 11px 11px 11px 34px; display: grid; grid-template-columns: 34px 1fr auto; align-items: center; gap: 9px; color: inherit; background: transparent; text-align: left; } +.device-copy { min-width: 0; display: flex; flex-direction: column; gap: 3px; } +.device-copy strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; } +.device-copy small { color: var(--muted); font-size: 12px; } +.dock-glyph { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 5px; color: #1e7658; background: #e3f3ec; } +.dock-glyph.alarm { color: #a54526; background: #fff0e9; } +.dock-glyph.offline { color: #7c8791; background: #edf0f2; } +.status { font-style: normal; font-size: 12px; border-radius: 3px; padding: 3px 6px; white-space: nowrap; } +.status.online { background: #e6f5ee; color: #167955; } +.status.mission { background: #e7f0fc; color: #2568b5; } +.status.alarm { background: #fff0e7; color: #b75225; } +.status.offline { background: #edf0f2; color: #737e88; } +.status.pending { background: #fff1dc; color: #95631d; } +.bound-drone { position: relative; width: calc(100% - 18px); min-height: 45px; margin: 0 9px 9px; padding: 7px 7px 7px 28px; display: grid; grid-template-columns: 18px 1fr auto; align-items: center; gap: 6px; border-radius: 4px; background: #f1f4f6; color: #42515e; text-align: left; } +.bound-drone:hover, .bound-drone.active { background: #e7eff9; color: #1f5f9f; } +.bound-drone > svg { width: 15px; }.bound-drone > span:nth-of-type(2) { min-width: 0; display: flex; flex-direction: column; gap: 2px; }.bound-drone strong { font-size: 12px; }.bound-drone small { color: var(--muted); font-size: 10px; }.bound-drone > i { font-style: normal; color: var(--green); font-size: 10px; } +.bound-drone.flying > i { color: var(--blue); }.bound-drone.offline > i { color: #8b969f; }.tree-line { position: absolute; left: 13px; top: -9px; width: 10px; height: 29px; border-left: 1px solid #c4ced6; border-bottom: 1px solid #c4ced6; border-radius: 0 0 0 5px; } +.device-alert { margin: 0 10px 10px; padding-top: 8px; border-top: 1px solid var(--line); display: flex; align-items: center; gap: 5px; color: var(--red); font-size: 11px; }.device-alert svg { width: 12px; }.device-alert span { margin-left: auto; color: var(--muted); }.device-alert.muted { color: var(--muted); } + +.map-panel { position: relative; min-width: 0; overflow: hidden; background: #71826f; isolation: isolate; } +.map-world { position: absolute; inset: 0; transform-origin: center; transition: transform .72s cubic-bezier(.2,.75,.25,1); will-change: transform; } +.map-surface { position: absolute; inset: 0; background-image: linear-gradient(rgba(16,40,38,.12), rgba(16,40,38,.22)), url("/assets/map-base-clean.png"); background-size: cover; background-position: center; filter: saturate(.42) contrast(.92); transform: scale(1.02); transition: filter .2s ease; } +.map-panel::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(90deg, rgba(18,28,32,.10), transparent 30%, rgba(10,20,26,.06)); z-index: 1; } +.map-toolbar { position: absolute; z-index: 4; top: 14px; left: 14px; height: 34px; display: flex; background: #fff; border-radius: 5px; box-shadow: 0 4px 14px rgba(19,31,39,.17); overflow: hidden; } +.map-type { padding: 0 13px; background: #fff; color: var(--muted); border-right: 1px solid var(--line); font-size: 13px; } +.map-type.active { background: #263846; color: #fff; } +.map-toolbar .icon-button { border-radius: 0; } +.map-legend { position: absolute; z-index: 4; left: 14px; bottom: 14px; height: 32px; padding: 0 11px; display: flex; align-items: center; gap: 12px; border-radius: 4px; background: rgba(255,255,255,.94); box-shadow: 0 4px 14px rgba(19,31,39,.13); font-size: 12px; color: #52616e; } +.map-legend span { display: flex; align-items: center; gap: 4px; white-space: nowrap; } +.map-legend i { width: 7px; height: 7px; border-radius: 50%; } +.green { background: var(--green); } .blue { background: var(--blue); } .red { background: var(--red); } .gray { background: #84909b; } +.map-marker { position: absolute; z-index: 5; display: flex; flex-direction: column; align-items: center; transform: translate(-50%, -50%); color: var(--green); background: transparent; transition: opacity .25s ease, filter .25s ease; } +.monitor-layout.focused .map-marker { transform: translate(-50%, -50%) scale(.746); } +.map-marker > span { width: 36px; height: 36px; display: grid; place-items: center; border: 3px solid #fff; border-radius: 50% 50% 50% 6px; background: currentColor; box-shadow: 0 5px 14px rgba(15,28,35,.28); transform: rotate(-45deg); transition: .18s ease; } +.map-marker svg { width: 16px; color: #fff; transform: rotate(45deg); } +.map-marker b { margin-top: 5px; padding: 3px 7px; border-radius: 3px; background: rgba(24,35,45,.86); color: #fff; font-size: 12px; font-weight: 500; white-space: nowrap; } +.map-marker > small { margin-top: 3px; padding: 2px 6px; border-radius: 3px; color: #dcecff; background: rgba(27,69,109,.9); font-size: 10px; white-space: nowrap; } +.map-marker em { position: absolute; right: -8px; top: -7px; width: 17px; height: 17px; display: grid; place-items: center; border: 2px solid #fff; border-radius: 50%; background: #246ebc; transform: rotate(45deg); }.map-marker em svg { width: 9px; transform: none; } +.map-marker:hover > span, .map-marker.selected > span { transform: rotate(-45deg) scale(1.15); } +.map-marker.selected > span { box-shadow: 0 0 0 6px rgba(33,130,91,.2), 0 5px 14px rgba(15,28,35,.3); } +.map-marker.mission { color: var(--blue); } +.map-marker.alarm { color: var(--red); animation: marker-pulse 1.8s infinite; } +.map-marker.offline { color: #78838c; } +.monitor-layout.focused .map-marker:not(.selected):not(.related) { opacity: .38; filter: grayscale(.55); } +.map-marker.selected > span { box-shadow: 0 0 0 7px rgba(31,111,235,.22), 0 6px 18px rgba(15,28,35,.34); } +.binding-line { position: absolute; inset: 0; z-index: 4; width: 100%; height: 100%; overflow: visible; pointer-events: none; opacity: 1; } +.binding-line line { stroke: rgba(117,191,246,.95); stroke-width: .35; stroke-dasharray: 1.5 1; } +.binding-line text { fill: #fff; font-size: 2.4px; text-anchor: middle; paint-order: stroke; stroke: rgba(34,77,111,.88); stroke-width: .8px; } +.map-view-all { height: 34px; padding: 0 10px; border-left: 1px solid var(--line); color: var(--blue); background: #fff; font-size: 11px; } +.map-view-all:hover { background: #f3f8fc; } +@keyframes marker-pulse { 50% { filter: drop-shadow(0 0 6px rgba(204,61,67,.7)); } } + +.video-unavailable { color: var(--muted); font-size: 10px; } +.detail-header { min-height: 90px; padding: 17px 18px 14px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-bottom: 1px solid var(--line); } +.detail-header > div { min-width: 0; } +.eyebrow { color: #73808d; font-size: 12px; } +.detail-header h2 { margin: 4px 0 5px; font-size: 18px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.detail-header p { margin: 0; display: flex; align-items: center; gap: 4px; color: var(--muted); font-size: 12px; } +.detail-header p svg { width: 12px; height: 12px; } +.detail-head-actions { display: flex; align-items: center; gap: 4px; }.detail-head-actions .icon-button { width: 28px; height: 28px; } +.asset-relation { min-height: 44px; padding: 0 18px; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); background: #f6f8fa; }.asset-relation span { display: flex; align-items: center; gap: 5px; color: var(--muted); font-size: 11px; }.asset-relation svg { width: 13px; }.asset-relation button { display: flex; align-items: center; gap: 3px; color: var(--blue); background: transparent; font-size: 12px; }.asset-relation button svg { width: 12px; } +.drone-only { display: none; }.detail-panel.drone-selected .dock-only { display: none; }.detail-panel.drone-selected .drone-only { display: block; }.detail-panel.drone-selected .asset-relation { display: flex; } +.detail-panel section { padding: 14px 18px; border-bottom: 1px solid var(--line); } +.section-title { min-height: 22px; display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; } +.section-title h3 { margin: 0; font-size: 14px; } +.section-title > span, .section-title > div > b { font-size: 11px; color: var(--muted); font-weight: 500; } +.section-title > div { display: flex; align-items: center; gap: 5px; } +.section-title .icon-button { width: 26px; height: 26px; } +.live-dot { width: 6px; height: 6px; border-radius: 50%; background: #929ca4; } +.live-dot.active { background: var(--red); box-shadow: 0 0 0 3px rgba(204,61,67,.14); } +.video-frame { position: relative; width: 100%; aspect-ratio: 16 / 8.6; overflow: hidden; border-radius: 5px; background: #1e2d34; } +.camera-scene { position: absolute; inset: 0; overflow: hidden; background: linear-gradient(#718fa0 0 45%, #687765 45% 66%, #40514c 66%); filter: saturate(.75); } +.camera-scene::after { content: ""; position: absolute; inset: 0; background: linear-gradient(115deg, rgba(255,255,255,.22), transparent 35%, rgba(4,18,26,.2)); } +.horizon { position: absolute; left: -10%; right: -10%; bottom: 31%; height: 16%; background: #49655c; clip-path: polygon(0 70%, 12% 25%, 22% 60%, 36% 5%, 48% 55%, 62% 20%, 73% 58%, 88% 7%, 100% 58%, 100% 100%, 0 100%); } +.dock-model { position: absolute; z-index: 2; width: 80px; height: 38px; left: 50%; bottom: 19%; transform: translateX(-50%); background: #d8dde0; border: 3px solid #87949b; box-shadow: 0 12px 18px rgba(0,0,0,.24); } +.dock-model::before, .dock-model::after { content: ""; position: absolute; bottom: 31px; width: 54px; height: 13px; background: #c2cbd0; border: 3px solid #7b898f; } +.dock-model::before { right: 50%; transform: skewY(14deg); transform-origin: right bottom; }.dock-model::after { left: 50%; transform: skewY(-14deg); transform-origin: left bottom; } +.dock-model span { position: absolute; top: 7px; width: 9px; height: 22px; background: #263a42; }.dock-model span:first-child { left: 13px; }.dock-model span:nth-child(2) { right: 13px; } +.dock-model i { position: absolute; width: 34px; height: 3px; left: 21px; top: -10px; background: #222; box-shadow: 0 -6px #222; } +.video-overlay { position: absolute; z-index: 3; left: 9px; right: 9px; top: 8px; display: flex; justify-content: space-between; color: #fff; font-size: 11px; text-shadow: 0 1px 3px #000; } +.video-overlay small { font-size: 10px; } +.play-control { position: absolute; z-index: 4; left: 50%; top: 50%; width: 42px; height: 42px; display: grid; place-items: center; transform: translate(-50%, -50%); border: 1px solid rgba(255,255,255,.55); border-radius: 50%; color: #fff; background: rgba(13,28,35,.64); backdrop-filter: blur(4px); } +.play-control:hover { transform: translate(-50%, -50%) scale(1.06); } +.play-control svg { width: 17px; } +.video-frame.streaming .camera-scene { animation: camera-live 4s ease-in-out infinite alternate; } +@keyframes camera-live { to { transform: scale(1.035) translateX(-2px); } } +.text-button { color: var(--blue); background: transparent; font-size: 12px; padding: 0; } +.metric-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px; } +.metric-grid > div { position: relative; min-height: 61px; padding: 9px 9px 8px 31px; border-radius: 5px; background: var(--soft); display: flex; flex-direction: column; gap: 3px; } +.metric-grid svg { position: absolute; left: 9px; top: 10px; width: 15px; color: #607b92; } +.metric-grid span { color: var(--muted); font-size: 11px; } +.metric-grid strong { font-size: 18px; font-weight: 650; } +.metric-grid strong small { font-size: 11px; margin-left: 2px; color: var(--muted); } +.metric-grid .success { color: var(--green); font-size: 14px; } +.mission-state { padding: 3px 6px; border-radius: 3px; background: #e7f0fc; color: #2568b5 !important; } +.mission-name { display: flex; align-items: center; justify-content: space-between; }.mission-name > span { display: flex; flex-direction: column; gap: 3px; }.mission-name strong { font-size: 13px; }.mission-name small { color: var(--muted); font-size: 10px; }.mission-name > b { color: var(--blue); font-size: 16px; } +.progress-track { height: 5px; margin-top: 10px; overflow: hidden; border-radius: 3px; background: #e7ebef; }.progress-track i { display: block; width: 50%; height: 100%; background: var(--blue); }.progress-meta { margin-top: 8px; display: flex; justify-content: space-between; color: var(--muted); font-size: 10px; } +.return-action { background: #176b61; }.return-action:hover { background: #10584f; } +.environment-row { display: grid; grid-template-columns: repeat(3, 1fr); } +.environment-row > div { min-width: 0; padding: 0 8px; border-right: 1px solid var(--line); display: grid; grid-template-columns: 18px 1fr; gap: 2px 3px; } +.environment-row > div:first-child { padding-left: 0; }.environment-row > div:last-child { border-right: 0; padding-right: 0; } +.environment-row svg { grid-row: 1 / 3; width: 15px; color: #607b92; } +.environment-row span { font-size: 10px; color: var(--muted); white-space: nowrap; } +.environment-row strong { font-size: 12px; white-space: nowrap; } +.quick-actions { border-bottom: 0 !important; } +.workflow-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; } +.workflow-action { min-width: 0; min-height: 54px; padding: 0 10px; display: grid; grid-template-columns: 23px 1fr; align-items: center; gap: 7px; border-radius: 5px; color: #fff; text-align: left; } +.workflow-action.takeoff { background: var(--blue); }.workflow-action.takeoff:hover { background: var(--blue-dark); } +.workflow-action.landing { background: #176b61; }.workflow-action.landing:hover { background: #10584f; } +.workflow-action > svg { width: 19px; }.workflow-action > span { min-width: 0; display: flex; flex-direction: column; gap: 2px; }.workflow-action strong { font-size: 12px; }.workflow-action small { overflow: hidden; color: rgba(255,255,255,.76); font-size: 8px; white-space: nowrap; text-overflow: ellipsis; } +.stage-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-top: 8px; } +.stage-actions button { min-width: 0; min-height: 48px; padding: 6px 7px; display: grid; grid-template-columns: 22px 1fr; align-items: center; gap: 5px; border: 1px solid var(--line); border-radius: 5px; color: #42515e; background: #fff; text-align: left; } +.stage-actions button:hover { border-color: #8fa7bc; background: #f7f9fb; }.stage-actions button > b { width: 21px; height: 21px; display: grid; place-items: center; border-radius: 3px; color: #376b98; background: #e8f0f7; font-size: 10px; }.stage-actions button > span { min-width: 0; display: flex; flex-direction: column; gap: 2px; font-size: 11px; }.stage-actions small { overflow: hidden; color: var(--muted); font-size: 8px; white-space: nowrap; text-overflow: ellipsis; }.stage-actions svg { width: 11px; color: #8b98a3; } +.reset-action { width: 100%; height: 38px; margin-top: 8px; padding: 0 10px; display: grid; grid-template-columns: 16px 1fr; align-items: center; gap: 6px; border: 1px solid var(--line); border-radius: 5px; color: #566574; background: #f7f8fa; text-align: left; font-size: 11px; }.reset-action:hover { color: var(--ink); border-color: #aebbc6; }.reset-action svg { width: 13px; }.reset-action span { display: flex; flex-direction: column; gap: 2px; }.reset-action small { color: var(--muted); font-size: 8px; } +.primary-action { width: 100%; min-height: 51px; padding: 0 11px; display: grid; grid-template-columns: 26px 1fr; align-items: center; gap: 9px; border-radius: 5px; background: var(--blue); color: #fff; text-align: left; } +.primary-action:hover { background: var(--blue-dark); } +.primary-action > span { display: flex; flex-direction: column; gap: 2px; } +.primary-action strong { font-size: 13px; }.primary-action small { font-size: 10px; color: #d9eaff; } +.secondary-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-top: 7px; } +.secondary-actions button { height: 36px; display: flex; align-items: center; justify-content: center; gap: 5px; border: 1px solid var(--line); border-radius: 5px; background: #fff; color: #526271; font-size: 12px; } +.secondary-actions button:hover { border-color: #92a5b6; color: var(--ink); } +.secondary-actions svg { width: 14px; } + +.page-view[hidden] { display: none !important; } +.monitor-layout[hidden] { display: none !important; } +.device-page { position: relative; flex: 1; min-height: 0; padding: 22px 24px 24px; display: flex; flex-direction: column; gap: 16px; overflow: auto; background: #f2f5f7; } +.device-page-header { min-height: 42px; display: flex; align-items: center; justify-content: space-between; gap: 16px; } +.device-page-header > div:first-child { display: flex; align-items: baseline; gap: 12px; }.device-page-header h1 { margin: 0; font-size: 24px; }.device-page-header span { color: var(--muted); font-size: 12px; } +.page-actions { display: flex; align-items: center; gap: 8px; }.icon-command, .primary-command, .plain-command { height: 34px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; font-size: 13px; } +.icon-command { width: 34px; border: 1px solid var(--line); color: #5d6c79; background: #fff; }.icon-command:hover { color: var(--ink); border-color: #aebbc6; }.primary-command { padding: 0 13px; color: #fff; background: var(--blue); }.primary-command:hover { background: var(--blue-dark); }.plain-command { padding: 0 13px; border: 1px solid var(--line); color: #536270; background: #fff; }.plain-command:hover { border-color: #aebbc6; color: var(--ink); }.page-actions svg, .primary-command svg { width: 14px; } +.asset-overview { min-height: 74px; padding: 0 4px; display: grid; grid-template-columns: repeat(5, 1fr); align-items: center; border: 1px solid var(--line); border-radius: 6px; background: #fff; } +.asset-overview > div { min-width: 0; height: 42px; padding: 0 20px; display: grid; grid-template-columns: auto 1fr; grid-template-rows: 18px 24px; align-items: end; border-right: 1px solid var(--line); }.asset-overview > div:last-child { border-right: 0; }.asset-overview span { grid-column: 1 / 3; color: var(--muted); font-size: 11px; }.asset-overview strong { font-size: 26px; line-height: 24px; font-weight: 650; }.asset-overview small { align-self: end; padding: 0 0 2px 4px; color: var(--muted); font-size: 11px; }.green-text { color: var(--green); }.blue-text { color: var(--blue); }.red-text { color: var(--red); }.orange-text { color: var(--orange); } +.asset-workspace { min-height: 500px; flex: 1; display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 6px; background: #fff; overflow: hidden; } +.asset-tabs { height: 50px; min-height: 50px; padding: 0 18px; display: flex; align-items: end; gap: 24px; border-bottom: 1px solid var(--line); } +.asset-tab { position: relative; height: 49px; padding: 0 2px; color: var(--muted); background: transparent; font-size: 14px; }.asset-tab b { margin-left: 3px; font-size: 11px; font-weight: 500; }.asset-tab.active { color: var(--blue); font-weight: 600; }.asset-tab.active::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--blue); } +.asset-toolbar { min-height: 58px; padding: 11px 18px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--line); } +.asset-search { width: min(320px, 34%); height: 34px; padding: 0 10px; display: flex; align-items: center; gap: 7px; border: 1px solid var(--line); border-radius: 5px; color: #7c8995; }.asset-search svg { width: 14px; }.asset-search input { width: 100%; min-width: 0; border: 0; outline: 0; color: var(--ink); background: transparent; font-size: 12px; } +.toolbar-group { display: flex; align-items: center; gap: 7px; }.toolbar-group select { width: 126px; height: 34px; padding: 0 26px 0 10px; border: 1px solid var(--line); border-radius: 5px; outline: 0; color: #536270; background: #fff; font-size: 12px; } +.asset-table-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: auto; }.asset-table { width: 100%; border-collapse: collapse; table-layout: fixed; }.asset-table th { height: 40px; padding: 0 14px; color: #7b8792; background: #f7f8fa; font-size: 11px; font-weight: 500; text-align: left; }.asset-table td { height: 62px; padding: 8px 14px; border-bottom: 1px solid #edf0f2; color: #455461; font-size: 12px; vertical-align: middle; }.asset-table tbody tr { cursor: pointer; transition: background .14s ease; }.asset-table tbody tr:hover { background: #f7faff; }.asset-table .asset-name-cell { display: flex; align-items: center; gap: 9px; }.table-asset-icon { width: 30px; height: 30px; min-width: 30px; display: grid; place-items: center; border-radius: 4px; color: #256e56; background: #e6f3ee; }.table-asset-icon.drone { color: #286aa9; background: #e7f0fa; }.table-asset-icon.pending { color: #93611d; background: #fff2dc; }.table-asset-icon svg { width: 15px; }.asset-name-cell > span { min-width: 0; display: flex; flex-direction: column; gap: 3px; }.asset-name-cell strong { overflow: hidden; color: var(--ink); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }.asset-name-cell small { color: var(--muted); font-size: 10px; }.table-status { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }.table-status i { width: 6px; height: 6px; border-radius: 50%; }.table-status.online i { background: var(--green); }.table-status.mission i { background: var(--blue); }.table-status.alarm i { background: var(--red); }.table-status.offline i { background: #8b969f; }.table-status.pending i { background: var(--orange); }.binding-cell { display: flex; align-items: center; gap: 5px; }.binding-cell svg { width: 12px; color: #72808d; }.binding-cell.unbound { color: #8a959f; }.row-actions { display: flex; align-items: center; gap: 2px; }.row-actions button { height: 28px; padding: 0 7px; border-radius: 4px; color: var(--blue); background: transparent; font-size: 11px; }.row-actions button:hover { background: #eaf2fc; }.row-actions .more-action { width: 28px; padding: 0; color: #687684; }.row-actions svg { width: 14px; } +.table-empty { height: 240px !important; color: var(--muted) !important; text-align: center; }.table-footer { min-height: 48px; margin-top: auto; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; color: var(--muted); font-size: 11px; }.table-footer > div { display: flex; align-items: center; gap: 6px; }.table-footer button, .table-footer b { width: 26px; height: 26px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 4px; color: #84909b; background: #fff; }.table-footer button:first-child svg { transform: rotate(180deg); }.table-footer button svg { width: 12px; }.table-footer b { border-color: var(--blue); color: #fff; background: var(--blue); font-size: 11px; font-weight: 500; } +.drawer-scrim { position: absolute; z-index: 39; inset: 0; background: rgba(22,34,43,.18); opacity: 0; visibility: hidden; transition: opacity .2s ease, visibility 0s linear .25s; }.drawer-scrim.open { opacity: 1; visibility: visible; transition-delay: 0s; } +.device-drawer { position: absolute; z-index: 40; top: 0; right: 0; bottom: 0; width: 408px; display: flex; flex-direction: column; overflow: auto; color: var(--ink); background: #fff; border-left: 1px solid var(--line); box-shadow: -12px 0 32px rgba(19,33,45,.14); transform: translateX(102%); visibility: hidden; transition: transform .3s cubic-bezier(.22,.8,.25,1), visibility 0s linear .3s; }.device-drawer.open { transform: translateX(0); visibility: visible; transition-delay: 0s; } +.device-drawer > header { min-height: 84px; padding: 16px 18px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--line); }.drawer-identity { min-width: 0; display: flex; align-items: center; gap: 11px; }.drawer-identity > span { width: 38px; height: 38px; min-width: 38px; display: grid; place-items: center; border-radius: 5px; color: #226e55; background: #e5f3ed; }.drawer-identity > span.drone { color: #246aa9; background: #e6eff9; }.drawer-identity > span.pending { color: #94631d; background: #fff2dc; }.drawer-identity > div { min-width: 0; }.drawer-identity small { color: var(--muted); font-size: 10px; }.drawer-identity h2 { margin: 3px 0; overflow: hidden; font-size: 17px; white-space: nowrap; text-overflow: ellipsis; }.drawer-identity p { margin: 0; color: var(--muted); font-size: 11px; } +.drawer-status { min-height: 42px; padding: 0 18px; display: flex; align-items: center; gap: 8px; background: #f7f9fa; border-bottom: 1px solid var(--line); }.drawer-status > span { color: var(--muted); font-size: 11px; } +.drawer-section { padding: 17px 18px; border-bottom: 1px solid var(--line); }.drawer-section[hidden] { display: none; }.drawer-section h3 { margin: 0 0 13px; font-size: 13px; }.drawer-section-title { display: flex; align-items: center; justify-content: space-between; }.drawer-section-title button { color: var(--blue); background: transparent; font-size: 11px; }.property-list { margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 15px 12px; }.property-list div { min-width: 0; }.property-list dt { margin-bottom: 4px; color: var(--muted); font-size: 10px; }.property-list dd { margin: 0; overflow: hidden; color: #34434f; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } +.binding-device { width: 100%; min-height: 50px; padding: 8px 10px; display: grid; grid-template-columns: 30px 1fr 14px; align-items: center; gap: 8px; border: 1px solid var(--line); border-radius: 5px; color: #43525f; background: #fff; text-align: left; }.binding-device:hover { border-color: #9eb2c3; }.binding-device > span { width: 28px; height: 28px; display: grid; place-items: center; border-radius: 4px; color: #286aa9; background: #e7f0fa; }.binding-device > span svg { width: 14px; }.binding-device div { display: flex; flex-direction: column; gap: 3px; }.binding-device strong { font-size: 12px; }.binding-device small { color: var(--muted); font-size: 10px; }.binding-device > svg { width: 12px; color: #8a96a0; } +.device-drawer > footer { position: sticky; bottom: 0; min-height: 62px; margin-top: auto; padding: 12px 18px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--line); background: #fff; } +.firmware-current { color: var(--muted); font-size: 11px; }.firmware-upgrade { display: flex; align-items: center; gap: 8px; }.firmware-upgrade select { flex: 1; min-width: 0; height: 34px; padding: 0 26px 0 10px; border: 1px solid var(--line); border-radius: 5px; outline: 0; color: #536270; background: #fff; font-size: 12px; }.firmware-upgrade .primary-command { height: 34px; padding: 0 16px; white-space: nowrap; }.firmware-upgrade .primary-command:disabled { opacity: .5; cursor: not-allowed; } + +.operation-page { flex: 1; min-height: 0; padding: 22px 24px 24px; display: flex; flex-direction: column; gap: 16px; overflow: auto; background: #f2f5f7; } +.operation-page-header { min-height: 45px; display: flex; align-items: center; justify-content: space-between; gap: 18px; } +.operation-page-header > div { min-width: 0; } +.operation-page-header h1 { margin: 0 0 5px; font-size: 23px; } +.operation-page-header span { color: var(--muted); font-size: 11px; } +.operation-page-header .primary-command svg { width: 14px; } +.business-metric-band { min-height: 82px; display: grid; grid-template-columns: repeat(5, 1fr); border: 1px solid var(--line); border-radius: 6px; background: #fff; } +.task-metrics { grid-template-columns: repeat(4, 1fr); } +.business-metric-band > div { min-width: 0; padding: 15px 18px; display: grid; grid-template-columns: 1fr auto; align-content: center; gap: 5px 8px; border-right: 1px solid var(--line); } +.business-metric-band > div:last-child { border-right: 0; } +.business-metric-band span { color: var(--muted); font-size: 11px; } +.business-metric-band strong { grid-row: 1 / 3; grid-column: 2; align-self: center; color: #30414e; font-size: 25px; } +.business-metric-band small { overflow: hidden; color: #83909a; font-size: 10px; text-overflow: ellipsis; white-space: nowrap; } +.business-workspace { flex: 1; min-height: 440px; display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--line); border-radius: 6px; background: #fff; } +.business-tabs { height: 50px; min-height: 50px; padding: 0 18px; display: flex; align-items: end; gap: 27px; border-bottom: 1px solid var(--line); } +.business-tabs button { position: relative; height: 49px; padding: 0 2px; color: var(--muted); background: transparent; font-size: 12px; } +.business-tabs button b { margin-left: 3px; color: #81909b; font-size: 10px; font-weight: 500; } +.business-tabs button.active { color: var(--blue); font-weight: 600; } +.business-tabs button.active::after { content: ""; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; background: var(--blue); } +.business-panel { display: none; flex: 1; min-height: 0; overflow: auto; } +.business-panel.active { display: block; } +.business-toolbar { min-height: 58px; padding: 11px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--line); } +.business-toolbar > div { display: flex; align-items: center; gap: 8px; } +.business-toolbar select, .task-dialog select { height: 34px; padding: 0 28px 0 9px; border: 1px solid var(--line); border-radius: 4px; color: #536270; background: #fff; font-size: 11px; outline: 0; } +.business-search { width: 290px; height: 34px; padding: 0 10px; display: flex; align-items: center; gap: 7px; border: 1px solid var(--line); border-radius: 4px; background: #f8fafb; color: #81909b; } +.business-search svg { width: 14px; } +.business-search input { width: 100%; min-width: 0; border: 0; outline: 0; color: var(--ink); background: transparent; font-size: 11px; } +.business-table-wrap { min-width: 900px; overflow: auto; } +.business-table { width: 100%; border-collapse: collapse; table-layout: fixed; } +.business-table th { height: 40px; padding: 0 14px; color: #788691; background: #f7f9fa; font-size: 10px; font-weight: 500; text-align: left; } +.business-table th:first-child { width: 23%; } +.business-table th:last-child { width: 10%; } +.business-table td { height: 62px; padding: 8px 14px; border-bottom: 1px solid #edf0f2; color: #465561; font-size: 11px; } +.business-table tbody tr { transition: background .14s ease; } +.business-table tbody tr:hover { background: #f8faff; } +.task-plan-table { min-width: 1120px; }.task-plan-table th:first-child { width: 20%; }.task-plan-table th:last-child { width: 16%; } +.media-policy { display: inline-flex; align-items: center; white-space: nowrap; font-size: 10px; }.media-policy::before { content: ""; width: 6px; height: 6px; margin-right: 5px; border-radius: 50%; background: #8b969f; }.media-policy.live::before { background: #cc3d43; }.media-policy.record::before { background: #2b72bd; }.media-policy.live-record::before { background: linear-gradient(90deg, #cc3d43 0 50%, #2b72bd 50%); }.media-policy.none { color: #8a959e; } +.primary-table-cell { min-width: 0; display: flex; align-items: center; gap: 9px; } +.primary-table-cell > span:last-child { min-width: 0; display: flex; flex-direction: column; gap: 3px; } +.primary-table-cell strong, .media-card-copy strong { overflow: hidden; color: #283946; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } +.primary-table-cell small, .media-card-copy small { overflow: hidden; color: var(--muted); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; } +.task-table-icon { width: 29px; height: 29px; min-width: 29px; display: grid; place-items: center; border-radius: 4px; color: #286aa9; background: #e8f0f9; } +.task-table-icon.muted { color: #788691; background: #eef1f3; } +.task-table-icon svg { width: 14px; } +.table-footer { height: 48px; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; color: var(--muted); font-size: 10px; } +.execution-record-table { min-width: 1050px; }.execution-record-table th:first-child { width: 15%; }.execution-record-table th:last-child { width: 8%; } +.table-text-action { height: 27px; padding: 0 6px; border-radius: 3px; color: var(--blue); background: transparent; font-size: 11px; }.table-text-action:hover { background: #eaf2fc; }.table-text-action:disabled { color: #9aa4ac; background: transparent; cursor: not-allowed; } +.route-library .business-toolbar .plain-command { display: flex; align-items: center; gap: 5px; } +.route-library .business-toolbar svg { width: 13px; } +.route-grid { padding: 16px; display: grid; grid-template-columns: repeat(3, minmax(220px, 1fr)); gap: 12px; } +.route-grid article { overflow: hidden; border: 1px solid var(--line); border-radius: 5px; background: #fff; } +.route-map { position: relative; height: 130px; display: grid; place-items: center; overflow: hidden; color: #fff; background-image: linear-gradient(rgba(24,48,56,.1), rgba(24,48,56,.35)), url("/assets/map-base-clean.png"); background-size: cover; } +.route-b { background-position: 65% 45%; }.route-c { background-position: 28% 68%; } +.route-map::after { content: ""; position: absolute; width: 58%; height: 46%; border: 2px dashed rgba(108,188,255,.95); border-radius: 48% 35% 52% 30%; transform: rotate(-8deg); } +.route-map svg { z-index: 1; width: 25px; filter: drop-shadow(0 2px 5px rgba(0,0,0,.4)); } +.route-map span { position: absolute; z-index: 2; right: 8px; bottom: 8px; padding: 4px 7px; border-radius: 3px; background: rgba(29,45,57,.82); font-size: 10px; } +.route-grid article > div:last-child { padding: 11px 12px; display: flex; flex-direction: column; gap: 4px; } +.route-grid strong { font-size: 12px; }.route-grid small { color: var(--muted); font-size: 10px; } +.route-library-view[hidden], .route-editor[hidden] { display: none !important; } +.route-editor { height: 100%; min-height: 520px; display: flex; flex-direction: column; background: #fff; } +.route-editor-header { height: 58px; min-height: 58px; padding: 0 14px; display: grid; grid-template-columns: 150px 1fr auto; align-items: center; gap: 12px; border-bottom: 1px solid var(--line); } +.route-editor-header .back-command { grid-column: auto; } +.route-editor-header > div:nth-child(2) { min-width: 0; display: flex; align-items: baseline; gap: 9px; } +.route-editor-header > div:nth-child(2) strong { font-size: 14px; }.route-editor-header > div:nth-child(2) span { color: var(--muted); font-size: 10px; } +.route-editor-header > div:last-child { display: flex; align-items: center; gap: 7px; } +.route-editor-header button { display: inline-flex; align-items: center; gap: 5px; }.route-editor-header button svg { width: 12px; } +.route-editor-header button:disabled { color: #a6afb6; background: #f1f3f4; cursor: not-allowed; } +.route-editor-body { flex: 1; min-height: 0; display: grid; grid-template-columns: 290px minmax(480px, 1fr); } +.route-config-panel { min-height: 0; padding: 15px; display: flex; flex-direction: column; border-right: 1px solid var(--line); background: #fff; } +.route-config-panel label { margin-bottom: 13px; display: flex; flex-direction: column; gap: 6px; } +.route-config-panel label > span { color: #4d5d69; font-size: 11px; }.route-config-panel label > span b { color: var(--red); } +.route-config-panel input, .route-config-panel select { width: 100%; height: 35px; padding: 0 9px; border: 1px solid var(--line); border-radius: 4px; outline: 0; color: #354550; background: #fff; font-size: 11px; } +.route-config-panel input:focus, .route-config-panel select:focus { border-color: #6799d1; box-shadow: 0 0 0 3px rgba(31,111,235,.07); } +.number-input { position: relative; }.number-input input { padding-right: 30px; }.number-input em { position: absolute; top: 50%; right: 10px; color: var(--muted); font-size: 11px; font-style: normal; transform: translateY(-50%); } +.route-default-title { min-height: 31px; padding-top: 3px; display: flex; align-items: center; border-top: 1px solid var(--line); }.route-default-title strong { font-size: 11px; } +.route-default-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }.route-default-row label { min-width: 0; }.route-default-row .number-input input { padding-right: 34px; } +.route-turn-segment { height: 34px; grid-template-columns: repeat(2, minmax(0, 1fr)); } +.apply-all-waypoints { height: 33px; margin: -3px 0 12px; display: flex; align-items: center; justify-content: center; gap: 6px; border: 1px solid #b8c7d4; border-radius: 4px; color: #2d669f; background: #f6f9fc; font-size: 11px; }.apply-all-waypoints:hover { border-color: #78a1c9; background: #edf4fb; }.apply-all-waypoints svg { width: 12px; } +.waypoint-heading { height: 34px; display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--line); } +.waypoint-heading strong { font-size: 12px; }.waypoint-heading span { min-width: 20px; padding: 2px 6px; border-radius: 8px; color: #346caa; background: #e8f1fb; font-size: 10px; text-align: center; } +.waypoint-list { flex: 1; min-height: 110px; overflow: auto; border-top: 1px solid var(--line); } +.waypoint-empty { height: 70px; display: grid; place-items: center; color: #929da6; font-size: 11px; } +.waypoint-item { min-height: 55px; padding: 7px 2px; display: grid; grid-template-columns: 24px 1fr 50px; align-items: center; gap: 7px; border-bottom: 1px solid #edf0f2; cursor: pointer; }.waypoint-item:hover { background: #f7faff; } +.waypoint-item > b { width: 22px; height: 22px; display: grid; place-items: center; border-radius: 50%; color: #fff; background: var(--blue); font-size: 10px; } +.waypoint-item:first-child > b { background: var(--green); }.waypoint-item:last-child > b { background: #c56c28; }.waypoint-item:only-child > b { background: var(--green); } +.waypoint-item > span { min-width: 0; display: flex; flex-direction: column; gap: 3px; }.waypoint-item strong { font-size: 11px; }.waypoint-item small { overflow: hidden; color: var(--muted); font-size: 8px; text-overflow: ellipsis; white-space: nowrap; }.waypoint-item small:last-child { color: #536a7c; } +.waypoint-item-actions { display: flex; align-items: center; }.waypoint-item-actions button { width: 23px; height: 23px; display: grid; place-items: center; border-radius: 3px; color: #788792; background: transparent; }.waypoint-item-actions button:hover { color: var(--blue); background: #eaf2fc; }.waypoint-item-actions button:last-child:hover { color: var(--red); background: #fff0f0; }.waypoint-item-actions button svg { width: 11px; } +.route-form-error { min-height: 25px; padding-top: 8px; color: var(--red); font-size: 10px; } +.route-planner-map { position: relative; min-width: 0; min-height: 430px; overflow: hidden; background: #75887a; cursor: crosshair; isolation: isolate; } +.route-map-base { position: absolute; inset: 0; background-image: linear-gradient(rgba(20,45,42,.06), rgba(20,45,42,.16)), url("/assets/map-base-clean.png"); background-size: cover; background-position: center; filter: saturate(.58) contrast(.94); transform: scale(1.01); } +.route-drawing { position: absolute; z-index: 2; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; } +.route-drawing polyline { fill: none; stroke: #2d82da; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; filter: drop-shadow(0 1px 2px rgba(12,37,58,.35)); } +.route-drawing circle { stroke: #fff; stroke-width: 3; vector-effect: non-scaling-stroke; filter: drop-shadow(0 2px 3px rgba(12,31,43,.4)); } +.route-drawing text { fill: #fff; font-size: 16px; font-weight: 700; text-anchor: middle; dominant-baseline: central; pointer-events: none; } +.route-map-status, .route-map-legend { position: absolute; z-index: 3; display: flex; align-items: center; border-radius: 4px; background: rgba(255,255,255,.94); box-shadow: 0 3px 12px rgba(20,36,45,.13); color: #52616d; font-size: 10px; } +.route-map-status { top: 12px; right: 12px; height: 30px; padding: 0 9px; gap: 13px; }.route-map-status span { display: flex; align-items: center; gap: 5px; }.route-map-status i { width: 6px; height: 6px; border-radius: 50%; background: var(--green); } +.route-map-legend { right: 12px; bottom: 12px; height: 31px; padding: 0 10px; gap: 11px; }.route-map-legend span { display: flex; align-items: center; gap: 4px; }.route-map-legend i { width: 7px; height: 7px; border: 2px solid #fff; border-radius: 50%; background: var(--blue); box-shadow: 0 0 0 1px #7e8c97; }.route-map-legend i.start { background: var(--green); }.route-map-legend i.end { background: #c56c28; } +.waypoint-dialog { width: min(540px, 100%); }.waypoint-id-row { min-height: 42px; margin-bottom: 14px; padding: 0 12px; display: flex; align-items: center; justify-content: space-between; border-radius: 4px; background: #f4f6f8; }.waypoint-id-row span { color: var(--muted); font-size: 11px; }.waypoint-id-row strong { font-size: 14px; }.waypoint-dialog .number-input input { padding-right: 38px; }.waypoint-turn-segment { height: 38px; grid-template-columns: repeat(2, minmax(0, 1fr)); } + +.task-replay-page { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: #eef2f4; } +.replay-header { min-height: 88px; padding: 10px 20px 13px; display: grid; grid-template-columns: 155px minmax(260px, 1fr) auto; align-items: end; gap: 8px 15px; border-bottom: 1px solid var(--line); background: #fff; } +.replay-header .back-command { grid-column: 1 / 4; } +.replay-header > div:nth-child(2) { grid-column: 1 / 3; min-width: 0; }.replay-header small { color: var(--muted); font-size: 10px; }.replay-header h1 { margin: 3px 0; overflow: hidden; font-size: 19px; text-overflow: ellipsis; white-space: nowrap; }.replay-header > div:nth-child(2) > span { color: var(--muted); font-size: 10px; }.replay-header > div:nth-child(2) b { color: #5c6a75; font-weight: 500; } +.replay-header-summary { display: flex; align-items: center; }.replay-header-summary > span { min-width: 86px; padding: 0 14px; display: flex; flex-direction: column; gap: 4px; border-left: 1px solid var(--line); color: var(--muted); font-size: 10px; }.replay-header-summary strong { color: #344550; font-size: 12px; } +.replay-map { position: relative; flex: 1; min-height: 430px; overflow: hidden; isolation: isolate; background: #718276; } +.replay-map-base { position: absolute; inset: 0; background-image: linear-gradient(rgba(17,39,37,.07), rgba(17,39,37,.19)), url("/assets/map-base-clean.png"); background-position: center; background-size: cover; filter: saturate(.55) contrast(.94); transform: scale(1.01); } +.replay-route-layer { position: absolute; z-index: 2; inset: 0; width: 100%; height: 100%; overflow: visible; } +.replay-route-shadow, .replay-route-line { fill: none; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }.replay-route-shadow { stroke: rgba(255,255,255,.9); stroke-width: 7; }.replay-route-line { stroke: #2b79c8; stroke-width: 3; stroke-dasharray: 7 5; } +.replay-waypoints circle { fill: #fff; stroke: #2b79c8; stroke-width: 4; vector-effect: non-scaling-stroke; }.replay-waypoints circle:first-child { stroke: var(--green); }.replay-waypoints circle:last-child { stroke: #c56c28; } +.replay-drone-marker { color: #fff; filter: drop-shadow(0 4px 6px rgba(13,33,45,.4)); }.replay-drone-marker circle { fill: #216fbd; stroke: #fff; stroke-width: 3; vector-effect: non-scaling-stroke; }.replay-drone-marker use { color: #fff; } +.replay-map-legend { position: absolute; z-index: 4; height: 31px; padding: 0 10px; display: flex; align-items: center; gap: 12px; border-radius: 4px; color: #53626e; background: rgba(255,255,255,.94); box-shadow: 0 3px 12px rgba(19,36,46,.13); font-size: 10px; } +.replay-map-back { position: absolute; z-index: 5; top: 13px; left: 13px; height: 32px; padding: 0 11px; display: flex; align-items: center; gap: 5px; border-radius: 4px; color: #40525f; background: rgba(255,255,255,.96); box-shadow: 0 3px 12px rgba(19,36,46,.15); font-size: 11px; }.replay-map-back:hover { color: var(--blue); }.replay-map-back svg { width: 12px; transform: rotate(180deg); } +.replay-map-legend { right: 13px; bottom: 78px; }.replay-map-legend span { display: flex; align-items: center; gap: 5px; }.replay-map-legend i { width: 18px; border-top: 2px dashed #2b79c8; }.replay-map-legend i.route-start, .replay-map-legend i.route-end { width: 7px; height: 7px; border: 2px solid #fff; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 1px #72818c; }.replay-map-legend i.route-end { background: #c56c28; } +.replay-telemetry { position: absolute; z-index: 4; top: 13px; right: 13px; width: 248px; overflow: hidden; border: 1px solid rgba(210,218,223,.9); border-radius: 5px; background: rgba(255,255,255,.96); box-shadow: 0 5px 18px rgba(18,34,43,.14); } +.replay-telemetry header { height: 39px; padding: 0 12px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); }.replay-telemetry header span { font-size: 12px; font-weight: 600; }.replay-telemetry header b { color: var(--muted); font-size: 10px; font-weight: 500; } +.telemetry-grid { display: grid; grid-template-columns: 1fr 1fr; }.telemetry-grid > div { min-height: 50px; padding: 9px 11px; display: flex; flex-direction: column; gap: 5px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }.telemetry-grid > div:nth-child(even) { border-right: 0; }.telemetry-grid span { color: var(--muted); font-size: 10px; }.telemetry-grid strong { color: #354651; font-size: 12px; } +.replay-telemetry dl { margin: 0; padding: 7px 11px 9px; }.replay-telemetry dl > div { min-height: 26px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #edf0f2; }.replay-telemetry dl > div:last-child { border-bottom: 0; }.replay-telemetry dt { color: var(--muted); font-size: 10px; }.replay-telemetry dd { margin: 0; color: #455560; font-size: 10px; } +.replay-controller { position: absolute; z-index: 5; right: 13px; bottom: 13px; left: 13px; height: 52px; padding: 0 13px; display: grid; grid-template-columns: 34px 42px minmax(160px, 1fr) 42px 60px; align-items: center; gap: 9px; border-radius: 5px; background: rgba(255,255,255,.97); box-shadow: 0 5px 20px rgba(17,34,44,.18); } +.replay-play { width: 32px; height: 32px; display: grid; place-items: center; border-radius: 50%; color: #fff; background: var(--blue); }.replay-play svg { width: 13px; }.replay-controller > span { color: #53626e; font-size: 10px; text-align: center; }.replay-controller input { width: 100%; accent-color: var(--blue); }.replay-controller select { width: 58px; height: 30px; padding: 0 6px; border: 1px solid var(--line); border-radius: 4px; color: #455560; background: #fff; font-size: 10px; } + +.media-panel-heading { min-height: 63px; padding: 12px 17px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); } +.media-panel-heading > div { display: flex; flex-direction: column; gap: 4px; }.media-panel-heading h2 { margin: 0; font-size: 14px; }.media-panel-heading span { color: var(--muted); font-size: 10px; } +.media-live-count { display: flex; align-items: center; gap: 6px; }.media-live-count i, .live-corner i { width: 6px; height: 6px; border-radius: 50%; background: var(--red); } +.live-media-grid, .media-grid { padding: 16px; display: grid; grid-template-columns: repeat(2, minmax(260px, 1fr)); gap: 13px; } +.live-media-grid article, .media-grid article { overflow: hidden; border: 1px solid var(--line); border-radius: 5px; background: #fff; } +.media-card-actions { display: flex; align-items: center; gap: 8px; } +.media-visual { position: relative; aspect-ratio: 16 / 8.5; overflow: hidden; background-image: linear-gradient(rgba(15,30,38,.06), rgba(15,30,38,.3)), url("/assets/reference-map-source.png"); background-size: cover; background-position: center; } +.media-visual::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 0 35px rgba(12,25,32,.2); pointer-events: none; } +.dock-camera, .dock-record { background-image: linear-gradient(rgba(18,35,40,.1), rgba(18,35,40,.32)), url("/assets/map-base-clean.png"); background-position: 45% 58%; } +.aerial-camera, .aerial-1 { background-position: 68% 35%; }.aerial-2 { background-position: 34% 48%; }.aerial-3 { background-position: 78% 67%; } +.media-visual > button { position: absolute; z-index: 2; top: 50%; left: 50%; width: 40px; height: 40px; display: grid; place-items: center; border: 1px solid rgba(255,255,255,.7); border-radius: 50%; color: #fff; background: rgba(23,42,54,.72); transform: translate(-50%, -50%); transition: .15s ease; } +.media-visual > button:hover { background: rgba(31,111,235,.9); transform: translate(-50%, -50%) scale(1.06); } +.media-visual > button svg { width: 16px; margin-left: 2px; } +.media-visual > small, .media-duration, .live-corner { position: absolute; z-index: 2; padding: 4px 7px; border-radius: 3px; color: #fff; background: rgba(23,38,48,.78); font-size: 10px; } +.media-visual > small { right: 8px; bottom: 8px; }.media-duration { right: 8px; bottom: 8px; }.live-corner { top: 8px; left: 8px; display: flex; align-items: center; gap: 5px; } +.media-card-copy { min-height: 57px; padding: 9px 11px; display: flex; align-items: center; justify-content: space-between; gap: 10px; } +.media-card-copy > span { min-width: 0; display: flex; flex-direction: column; gap: 4px; } +.media-card-copy .icon-button { width: 28px; height: 28px; min-width: 28px; }.media-card-copy .icon-button svg { width: 13px; } +.original-video-toolbar { gap: 10px; }.original-video-toolbar > div:last-child { flex-shrink: 0; } +.download-quota { min-width: 190px; height: 34px; padding: 0 8px 0 10px; display: grid !important; grid-template-columns: 1fr auto; grid-template-rows: 1fr 1fr; align-items: center; column-gap: 8px !important; border-left: 1px solid var(--line); border-right: 1px solid var(--line); } +.download-quota span { align-self: end; color: #53626e; font-size: 10px; }.download-quota span b { color: #344651; font-weight: 600; }.download-quota small { align-self: start; color: #a05e24; font-size: 8px; }.download-quota button { grid-row: 1 / 3; grid-column: 2; height: 26px; padding: 0 7px; border-radius: 3px; color: var(--blue); background: #eaf2fc; font-size: 10px; } +.task-dialog select { width: 100%; height: 38px; } +.mode-segment { height: 36px; padding: 3px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px; border-radius: 4px; background: #edf1f4; } +.mode-segment button { border-radius: 3px; color: #64727e; background: transparent; font-size: 11px; }.mode-segment button.active { color: #245f9e; background: #fff; box-shadow: 0 1px 4px rgba(23,39,50,.12); } +.task-schedule-fields[hidden] { display: none; }.task-schedule-fields { margin-top: 2px; } +.weekday-picker { height: 38px; padding: 3px; display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 3px; border-radius: 4px; background: #edf1f4; } +.weekday-picker button { min-width: 0; border-radius: 3px; color: #65737f; background: transparent; font-size: 11px; }.weekday-picker button:hover { color: #2869aa; }.weekday-picker button.active { color: #fff; background: #2c72bd; box-shadow: 0 1px 4px rgba(23,66,108,.18); } +.task-video-settings { margin: 3px 0 12px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } +.task-video-title { height: 35px; display: flex; align-items: center; justify-content: space-between; }.task-video-title strong { font-size: 12px; }.task-video-title span { color: var(--muted); font-size: 10px; } +.add-dock-dialog .task-video-option { min-height: 48px; margin: 0; padding: 7px 2px; display: grid; grid-template-columns: 1fr 34px; align-items: center; gap: 10px; border-top: 1px solid #edf0f2; cursor: pointer; } +.task-video-option > span { min-width: 0; display: flex; flex-direction: column; gap: 3px; }.task-video-option strong { color: #3c4d59; font-size: 11px; }.add-dock-dialog .task-video-option small { color: var(--muted); font-size: 10px; } +.task-video-option input { position: absolute; opacity: 0; pointer-events: none; }.task-video-option > i { position: relative; width: 32px; height: 18px; border-radius: 9px; background: #c9d0d5; transition: background .16s ease; }.task-video-option > i::after { content: ""; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(20,36,45,.25); transition: transform .16s ease; }.task-video-option input:checked + i { background: #2d76c2; }.task-video-option input:checked + i::after { transform: translateX(14px); } +.task-video-option.disabled { cursor: not-allowed; opacity: .55; }.task-video-option.disabled > i { background: #d8dde1; } +.add-dock-dialog .task-video-note { margin: 7px 2px 10px; color: #a87527; font-size: 10px; line-height: 1.5; } +.raw-download-dialog { width: min(500px, 100%); }.raw-download-content { padding: 18px 20px 4px; }.raw-download-file { min-height: 58px; padding: 9px 11px; display: flex; align-items: center; gap: 10px; border: 1px solid var(--line); border-radius: 4px; }.raw-download-file > span { width: 32px; height: 32px; display: grid; place-items: center; border-radius: 4px; color: #286aa9; background: #e7f0fa; }.raw-download-file svg { width: 15px; }.raw-download-file > div { min-width: 0; display: flex; flex-direction: column; gap: 4px; }.raw-download-file strong { overflow: hidden; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }.raw-download-file small { color: var(--muted); font-size: 10px; } +.raw-download-quota { margin-top: 14px; display: grid; grid-template-columns: repeat(3, 1fr); border: 1px solid var(--line); border-radius: 4px; }.raw-download-quota > div { min-height: 55px; padding: 9px 11px; display: flex; flex-direction: column; justify-content: center; gap: 5px; border-right: 1px solid var(--line); }.raw-download-quota > div:last-child { border-right: 0; }.raw-download-quota span { color: var(--muted); font-size: 10px; }.raw-download-quota strong { font-size: 12px; }.raw-download-content p { margin: 12px 0 0; color: #9a5d25; font-size: 10px; line-height: 1.6; } +.raw-download-dialog > footer { height: 62px; margin-top: 14px; padding: 0 20px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--line); }.raw-download-dialog > footer button { min-width: 88px; }.raw-download-dialog > footer .primary-command { display: flex; align-items: center; justify-content: center; gap: 5px; }.raw-download-dialog > footer svg { width: 13px; } + +.device-detail-page { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: #f1f4f6; } +.full-detail-header { min-height: 108px; padding: 12px 24px 16px; display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 7px 16px; background: #fff; border-bottom: 1px solid var(--line); }.back-command { grid-column: 1 / 3; width: fit-content; height: 24px; padding: 0; display: flex; align-items: center; gap: 4px; color: var(--muted); background: transparent; font-size: 11px; }.back-command:hover { color: var(--blue); }.back-command svg { width: 12px; transform: rotate(180deg); }.full-detail-identity { min-width: 0; display: flex; align-items: center; gap: 11px; }.full-detail-identity > span { width: 40px; height: 40px; min-width: 40px; display: grid; place-items: center; border-radius: 5px; color: #226e55; background: #e5f3ed; }.full-detail-identity > span.drone { color: #246aa9; background: #e6eff9; }.full-detail-identity > span svg { width: 19px; }.full-detail-identity > div { min-width: 0; }.full-detail-identity small { color: var(--muted); font-size: 10px; }.full-detail-identity h1 { margin: 2px 0; overflow: hidden; font-size: 20px; text-overflow: ellipsis; white-space: nowrap; }.full-detail-identity p { margin: 0; color: var(--muted); font-size: 11px; }.full-detail-identity > i { margin-left: 4px; }.full-detail-actions { display: flex; align-items: center; gap: 8px; }.full-detail-actions svg { width: 14px; } +.full-detail-actions [hidden] { display: none; } +.full-detail-tabs { height: 48px; min-height: 48px; padding: 0 24px; display: flex; align-items: end; gap: 28px; background: #fff; border-bottom: 1px solid var(--line); }.full-detail-tabs button { position: relative; height: 47px; padding: 0 2px; color: var(--muted); background: transparent; font-size: 13px; }.full-detail-tabs button b { margin-left: 3px; color: var(--red); font-size: 10px; font-weight: 500; }.full-detail-tabs button.active { color: var(--blue); font-weight: 600; }.full-detail-tabs button.active::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--blue); } +.full-detail-content { flex: 1; min-height: 0; margin: 16px 24px 24px; overflow: auto; border: 1px solid var(--line); border-radius: 6px; background: #fff; }.full-tab-panel { display: none; min-height: 100%; }.full-tab-panel.active { display: block; }.device-detail-page:not(.drone-detail) .drone-full-only { display: none !important; }.device-detail-page.drone-detail .dock-full-only { display: none !important; } +.detail-metric-band { min-height: 76px; padding: 0 8px; display: grid; grid-template-columns: repeat(6, 1fr); align-items: center; border-bottom: 1px solid var(--line); background: #fafbfc; }.detail-metric-band > div { height: 40px; padding: 0 16px; display: flex; flex-direction: column; justify-content: center; gap: 5px; border-right: 1px solid var(--line); }.detail-metric-band > div:last-child { border-right: 0; }.detail-metric-band span { color: var(--muted); font-size: 10px; }.detail-metric-band strong { color: #34434f; font-size: 16px; font-weight: 600; } +.detail-columns { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(270px, .8fr); }.detail-main-column, .detail-side-column { min-width: 0; }.detail-side-column { border-left: 1px solid var(--line); }.data-section { padding: 18px 20px; border-bottom: 1px solid var(--line); }.data-section:last-child { border-bottom: 0; }.data-section h2 { margin: 0 0 14px; font-size: 13px; }.data-section-title { display: flex; align-items: center; justify-content: space-between; gap: 10px; }.data-section-title button { color: var(--blue); background: transparent; font-size: 11px; } +.status-matrix { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }.status-matrix > div { position: relative; min-height: 54px; padding: 9px 24px 9px 32px; display: flex; flex-direction: column; justify-content: center; gap: 4px; border-radius: 4px; background: #f5f7f9; }.status-matrix svg { position: absolute; left: 9px; top: 10px; width: 15px; color: #657b8e; }.status-matrix span { color: var(--muted); font-size: 10px; }.status-matrix strong { color: #354550; font-size: 12px; }.status-matrix > div > i { position: absolute; right: 9px; top: 11px; width: 6px; height: 6px; border-radius: 50%; }.ok { background: var(--green); } +.value-table { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--line); border-left: 1px solid var(--line); }.value-table > div { min-height: 54px; padding: 9px 12px; display: flex; flex-direction: column; justify-content: center; gap: 5px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }.value-table span { color: var(--muted); font-size: 10px; }.value-table strong { color: #34434f; font-size: 12px; font-weight: 600; } +.environment-values { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }.environment-values > div { min-height: 52px; padding: 9px 10px 9px 34px; position: relative; display: flex; flex-direction: column; justify-content: center; gap: 4px; border-radius: 4px; background: #f5f7f9; }.environment-values svg { position: absolute; left: 10px; width: 15px; color: #657b8e; }.environment-values span { color: var(--muted); font-size: 10px; }.environment-values strong { font-size: 12px; } +.bound-asset-large { min-height: 52px; padding: 8px 9px; display: grid; grid-template-columns: 32px 1fr auto; align-items: center; gap: 8px; border: 1px solid var(--line); border-radius: 5px; }.bound-asset-large > span { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 4px; color: #286aa9; background: #e7f0fa; }.bound-asset-large.dock > span { color: #226e55; background: #e5f3ed; }.bound-asset-large svg { width: 15px; }.bound-asset-large > div { min-width: 0; display: flex; flex-direction: column; gap: 3px; }.bound-asset-large strong { overflow: hidden; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }.bound-asset-large small { color: var(--muted); font-size: 10px; }.detail-side-column dl { margin: 13px 0 0; }.detail-side-column dl > div, .compact-properties > div { min-height: 29px; display: grid; grid-template-columns: 88px 1fr; align-items: center; border-bottom: 1px solid #edf0f2; }.detail-side-column dt, .compact-properties dt { color: var(--muted); font-size: 10px; }.detail-side-column dd, .compact-properties dd { margin: 0; overflow: hidden; color: #3c4b57; font-size: 11px; text-align: right; text-overflow: ellipsis; white-space: nowrap; } +.workflow-summary { display: grid; grid-template-columns: 30px 1fr auto; align-items: center; gap: 8px; }.workflow-icon { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 4px; color: #226e55; background: #e5f3ed; }.workflow-icon svg { width: 14px; }.workflow-summary > div { display: flex; flex-direction: column; gap: 3px; }.workflow-summary strong { font-size: 12px; }.workflow-summary small { color: var(--muted); font-size: 10px; }.workflow-summary > i { color: var(--green); font-size: 10px; font-style: normal; }.workflow-steps { margin: 13px 0 0 10px; padding: 0 0 0 15px; border-left: 1px solid var(--line); list-style: none; }.workflow-steps li { position: relative; min-height: 25px; color: var(--muted); font-size: 10px; }.workflow-steps li::before { content: ""; position: absolute; left: -19px; top: 1px; width: 6px; height: 6px; border: 2px solid #fff; border-radius: 50%; background: #a1abb3; }.workflow-steps li.done::before { background: var(--green); } +.mission-detail-row { display: flex; align-items: center; justify-content: space-between; }.mission-detail-row > div { display: flex; flex-direction: column; gap: 4px; }.mission-detail-row strong { font-size: 13px; }.mission-detail-row small { color: var(--muted); font-size: 10px; }.mission-detail-row > b { color: var(--blue); font-size: 12px; }.progress-track.wide { margin-top: 14px; } +.state-groups, .diagnostics-layout { display: grid; grid-template-columns: 1fr 1fr; }.state-groups .data-section:nth-child(odd), .diagnostics-layout .data-section:nth-child(odd) { border-right: 1px solid var(--line); }.state-detail-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }.state-detail-grid > div { min-height: 68px; padding: 11px; display: flex; flex-direction: column; justify-content: center; gap: 5px; border: 1px solid var(--line); border-radius: 4px; }.state-detail-grid span { color: var(--muted); font-size: 10px; }.state-detail-grid strong { font-size: 12px; }.state-detail-grid small { color: #8a96a0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 8px; } +.record-header { height: 62px; padding: 0 18px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); }.record-header > div { display: flex; align-items: baseline; gap: 9px; }.record-header h2 { margin: 0; font-size: 14px; }.record-header span { color: var(--muted); font-size: 10px; }.record-header select { width: 132px; height: 32px; padding: 0 8px; border: 1px solid var(--line); border-radius: 4px; color: #536270; background: #fff; font-size: 11px; }.record-table { width: 100%; border-collapse: collapse; table-layout: fixed; }.record-table th { height: 40px; padding: 0 14px; color: var(--muted); background: #f7f8fa; font-size: 10px; font-weight: 500; text-align: left; }.record-table td { height: 54px; padding: 8px 14px; border-bottom: 1px solid var(--line); color: #455461; font-size: 11px; }.severity { padding: 3px 6px; border-radius: 3px; font-size: 10px; font-style: normal; }.severity.medium { color: #a0591e; background: #fff0e3; }.severity.low { color: #426785; background: #eaf1f6; }.result-success { color: var(--green); } +.diagnostic-list { display: flex; flex-direction: column; border-top: 1px solid var(--line); }.diagnostic-list > div { min-height: 46px; display: grid; grid-template-columns: 1fr 70px 90px; align-items: center; gap: 8px; border-bottom: 1px solid var(--line); }.diagnostic-list span { display: flex; align-items: center; gap: 7px; color: #455461; font-size: 11px; }.diagnostic-list span i { width: 6px; height: 6px; border-radius: 50%; }.diagnostic-list span i.standby { background: #8d98a1; }.diagnostic-list strong { color: var(--green); font-size: 11px; font-weight: 500; }.diagnostic-list small { color: var(--muted); font-size: 10px; text-align: right; } +.alarm-metrics { grid-template-columns: repeat(4, 1fr); }.alarm-workspace, .system-workspace { min-height: 0; overflow: hidden; }.alarm-layout { min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 285px; }.alarm-list { min-width: 0; padding: 12px 16px; }.alarm-item { min-height: 76px; padding: 12px 10px; display: grid; grid-template-columns: 42px minmax(0, 1fr) 66px 48px; align-items: center; gap: 10px; border-bottom: 1px solid var(--line); }.alarm-item > div { min-width: 0; display: flex; flex-direction: column; gap: 5px; }.alarm-item strong { overflow: hidden; color: #344550; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }.alarm-item small { overflow: hidden; color: var(--muted); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }.alarm-severity { width: fit-content; padding: 4px 6px; border-radius: 3px; font-size: 10px; }.alarm-item.critical .alarm-severity { color: #b33f43; background: #fdebec; }.alarm-item.medium .alarm-severity { color: #a25f21; background: #fff0df; }.alarm-item.low .alarm-severity { color: #496d88; background: #eaf1f6; }.alarm-handle { height: 26px; border: 1px solid #c6d9eb; border-radius: 3px; color: var(--blue); background: #f4f8fc; font-size: 10px; }.alarm-side-panel { border-left: 1px solid var(--line); background: #fafbfc; }.alarm-side-panel section { padding: 17px 16px; border-bottom: 1px solid var(--line); }.alarm-side-panel h2 { margin: 0 0 15px; font-size: 13px; }.alarm-bars { display: flex; flex-direction: column; gap: 13px; }.alarm-bars > div { display: grid; grid-template-columns: 62px 1fr 16px; align-items: center; gap: 8px; }.alarm-bars span, .alarm-bars strong { color: var(--muted); font-size: 10px; }.alarm-bars strong { color: #41515d; text-align: right; }.alarm-bars b { height: 6px; overflow: hidden; border-radius: 5px; background: #e8edf0; }.alarm-bars b i { display: block; width: var(--value); height: 100%; border-radius: inherit; background: #d46a6d; }.alarm-tip { margin: 0 0 14px; display: flex; align-items: flex-start; gap: 7px; color: #8b6329; font-size: 10px; line-height: 1.6; }.alarm-tip svg { width: 14px; min-width: 14px; color: #b17b2e; }.system-panel { display: none; min-height: 0; overflow: auto; }.system-panel.active { display: block; }.system-grid { display: grid; grid-template-columns: 1fr 1fr; }.settings-section { padding: 19px 20px; border-bottom: 1px solid var(--line); }.system-grid .settings-section:nth-child(odd) { border-right: 1px solid var(--line); }.settings-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; margin-bottom: 16px; }.settings-heading h2 { margin: 0 0 5px; font-size: 14px; }.settings-heading span { color: var(--muted); font-size: 10px; }.account-profile { min-height: 59px; padding: 10px; display: grid; grid-template-columns: 34px 1fr auto; align-items: center; gap: 9px; border: 1px solid var(--line); border-radius: 4px; }.account-profile > span, .member-avatar { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 4px; color: #fff; background: #347fc0; font-size: 13px; }.account-profile > div, .member-list > div > div { min-width: 0; display: flex; flex-direction: column; gap: 4px; }.account-profile strong, .member-list strong { font-size: 12px; }.account-profile small, .member-list small { color: var(--muted); font-size: 10px; }.settings-list { margin-top: 12px; border-top: 1px solid var(--line); }.settings-list > div { min-height: 42px; display: grid; grid-template-columns: 82px 1fr auto; align-items: center; gap: 8px; border-bottom: 1px solid var(--line); }.settings-list span, .settings-list em { color: var(--muted); font-size: 10px; font-style: normal; }.settings-list strong { color: #40505c; font-size: 11px; font-weight: 500; }.settings-list button { color: var(--blue); background: transparent; font-size: 10px; }.member-list { display: flex; flex-direction: column; border-top: 1px solid var(--line); }.member-list > div { min-height: 54px; display: grid; grid-template-columns: 30px 1fr auto; align-items: center; gap: 9px; border-bottom: 1px solid var(--line); }.member-avatar.blue { background: #6d7dba; }.member-list > div > i { color: var(--muted); font-size: 10px; font-style: normal; }.notification-options { border-top: 1px solid var(--line); }.notification-options label { min-height: 65px; display: grid; grid-template-columns: 1fr 32px 100px; align-items: center; gap: 10px; border-bottom: 1px solid var(--line); }.notification-options label > span { display: flex; flex-direction: column; gap: 4px; }.notification-options strong { font-size: 12px; }.notification-options small, .notification-options em { color: var(--muted); font-size: 10px; font-style: normal; }.check-toggle { width: 30px; height: 17px; position: relative; border-radius: 9px; background: #c9d0d5; }.check-toggle::after { content: ""; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: #fff; }.check-toggle.active { background: #347fc0; }.check-toggle.active::after { transform: translateX(13px); }.service-status-list { border-top: 1px solid var(--line); }.service-status-list > div { min-height: 46px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); }.service-status-list span { display: flex; align-items: center; gap: 7px; color: #465762; font-size: 11px; }.service-status-list span i { width: 6px; height: 6px; border-radius: 50%; background: var(--green); }.service-status-list strong, .audit-table i { color: var(--green); font-size: 11px; font-weight: 500; }.audit-table { border-top: 1px solid var(--line); }.audit-table > div { min-height: 48px; display: grid; grid-template-columns: 130px 120px 1fr 45px; align-items: center; gap: 8px; border-bottom: 1px solid var(--line); }.audit-table span, .audit-table strong, .audit-table em, .audit-table i { font-size: 10px; font-style: normal; }.audit-table span, .audit-table em { color: var(--muted); } +.account-metrics { grid-template-columns: repeat(5, 1fr); }.account-metrics > div:first-child strong { font-size: 16px; }.account-workspace { min-height: 0; overflow: auto; }.account-panel { display: none; }.account-panel.active { display: block; }.account-overview-grid { display: grid; grid-template-columns: 1fr 1fr; }.account-info-block { min-width: 0; padding: 20px; border-bottom: 1px solid var(--line); }.account-overview-grid .account-info-block:first-child { border-right: 1px solid var(--line); }.account-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 17px; }.account-section-heading h2 { margin: 0 0 5px; font-size: 14px; }.account-section-heading span { color: var(--muted); font-size: 10px; }.tenant-identity { min-height: 66px; padding: 11px; display: grid; grid-template-columns: 36px 1fr auto; align-items: center; gap: 10px; border: 1px solid var(--line); border-radius: 4px; }.tenant-identity > span { width: 32px; height: 32px; display: grid; place-items: center; border-radius: 4px; color: #fff; background: #2f78bb; font-size: 16px; font-weight: 600; }.tenant-identity > div { min-width: 0; display: flex; flex-direction: column; gap: 4px; }.tenant-identity strong { font-size: 12px; }.tenant-identity small { color: var(--muted); font-size: 10px; }.account-details { margin: 14px 0 0; border-top: 1px solid var(--line); }.account-details > div { min-height: 38px; display: grid; grid-template-columns: 82px 1fr; align-items: center; border-bottom: 1px solid var(--line); }.account-details dt { color: var(--muted); font-size: 10px; }.account-details dd { margin: 0; color: #3f505b; font-size: 11px; }.resource-progress { display: flex; flex-direction: column; gap: 17px; }.resource-progress > div > span { display: flex; justify-content: space-between; color: #52616d; font-size: 11px; }.resource-progress b { color: #344651; font-weight: 500; }.resource-progress i { display: block; height: 6px; margin-top: 7px; overflow: hidden; border-radius: 4px; background: #e7edf1; }.resource-progress em { display: block; height: 100%; border-radius: inherit; background: #3d82bd; }.account-buy-button { margin-top: 22px; }.resource-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }.resource-cards button { min-height: 88px; padding: 12px; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; border: 1px solid var(--line); border-radius: 4px; color: #334651; background: #fff; text-align: left; }.resource-cards button:hover { border-color: #91b5d6; background: #f7fbff; }.resource-cards strong { font-size: 11px; }.resource-cards small { color: var(--muted); font-size: 10px; line-height: 1.4; }.resource-cards b { margin-top: auto; color: var(--blue); font-size: 10px; font-weight: 500; }.usage-table { border-top: 1px solid var(--line); }.usage-table > div { min-height: 48px; display: grid; grid-template-columns: 1fr 1.3fr 100px; align-items: center; gap: 10px; border-bottom: 1px solid var(--line); }.usage-table span, .usage-table em { color: var(--muted); font-size: 10px; font-style: normal; }.usage-table strong { color: #3f505b; font-size: 11px; font-weight: 500; }.usage-table em { text-align: right; }.billing-total { margin-top: 17px; padding: 13px 15px; display: flex; align-items: center; gap: 15px; border-radius: 4px; background: #f5f8fa; }.billing-total span, .billing-total small { color: var(--muted); font-size: 10px; }.billing-total strong { color: #2f6fa8; font-size: 20px; }.security-list { border-top: 1px solid var(--line); }.security-list button { width: 100%; min-height: 58px; padding: 0 4px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); text-align: left; }.security-list button span { display: flex; flex-direction: column; gap: 4px; }.security-list strong { font-size: 12px; }.security-list small { color: var(--muted); font-size: 10px; }.security-list b { color: var(--blue); font-size: 10px; font-weight: 500; } + +.modal-backdrop { position: fixed; z-index: 80; inset: 0; display: grid; place-items: center; padding: 20px; background: rgba(13,23,31,.48); opacity: 0; pointer-events: none; transition: opacity .18s ease; } +.modal-backdrop.open { opacity: 1; pointer-events: auto; } +.modal { width: min(390px, 100%); padding: 24px; border-radius: 7px; background: #fff; box-shadow: 0 22px 60px rgba(0,0,0,.23); transform: translateY(8px); transition: transform .18s ease; text-align: center; } +.modal-backdrop.open .modal { transform: translateY(0); } +.modal-icon { margin: 0 auto 12px; width: 42px; height: 42px; display: grid; place-items: center; border-radius: 50%; background: #fff2e3; color: #b6631b; } +.modal h3 { margin: 0 0 8px; font-size: 19px; }.modal p { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.7; } +.safety-checks { margin-top: 16px; padding: 11px; display: flex; justify-content: space-between; gap: 6px; border-radius: 5px; background: var(--soft); } +.safety-checks span { display: flex; align-items: center; gap: 4px; color: #52616e; font-size: 11px; }.safety-checks i { width: 6px; height: 6px; border-radius: 50%; background: var(--green); } +.modal-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 18px; } +.modal-actions button { height: 38px; border-radius: 5px; font-size: 13px; }.modal-actions .cancel { background: #fff; border: 1px solid var(--line); }.modal-actions .confirm { color: #fff; background: var(--blue); } +.dock-status-dialog { width: min(700px, calc(100vw - 32px)); padding: 0; overflow: hidden; text-align: left; } +.dock-status-header { min-height: 58px; padding: 0 18px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); cursor: grab; } +.dock-status-header:active { cursor: grabbing; } +.dock-status-header > div { display: flex; flex-direction: column; gap: 4px; }.dock-status-header span { color: var(--muted); font-size: 10px; }.dock-status-header h3 { margin: 0; font-size: 18px; } +.dock-status-body { padding: 13px 18px 15px; max-height: none; overflow: visible; } +.dock-status-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }.dock-status-grid > div { min-height: 58px; padding: 8px 9px; display: flex; flex-direction: column; gap: 4px; border: 1px solid var(--line); border-radius: 4px; background: #f7f9fb; }.dock-status-grid span, .dock-status-grid small, .dock-status-list span { color: var(--muted); font-size: 10px; }.dock-status-grid strong { color: #344651; font-size: 12px; }.dock-status-grid small { margin-top: auto; }.dock-status-section { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--line); }.dock-status-section h4 { margin: 0 0 8px; font-size: 12px; }.dock-status-environment { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }.dock-status-environment span { color: var(--muted); font-size: 10px; }.dock-status-environment b { display: block; margin-top: 3px; color: #3f505b; font-size: 11px; font-weight: 500; }.dock-status-list { border-top: 1px solid var(--line); }.dock-status-list > div { min-height: 34px; display: grid; grid-template-columns: 100px 1fr; align-items: center; border-bottom: 1px solid var(--line); }.dock-status-list b { color: #3f505b; font-size: 11px; font-weight: 500; } +.dock-process-track { display: grid; grid-template-columns: repeat(8, 1fr); gap: 0; padding: 4px 0 1px; }.dock-process-track > div { position: relative; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 3px; color: var(--muted); text-align: center; }.dock-process-track > div:not(:last-child)::after { content: ""; position: absolute; top: 5px; left: 50%; width: 100%; height: 1px; background: #d7e0e6; }.dock-process-track i { position: relative; z-index: 1; width: 9px; height: 9px; border: 2px solid #c4d0d8; border-radius: 50%; background: #fff; }.dock-process-track span { font-size: 8px; line-height: 1.2; white-space: nowrap; }.dock-process-track small { font-size: 7px; }.dock-process-track .active { color: var(--blue); }.dock-process-track .active i { border-color: var(--blue); background: var(--blue); box-shadow: 0 0 0 2px #e8f1fb; }.dock-process-track .active::after { background: #a9c7e3; } +.dock-status-grid.compact > div { min-height: 50px; } +.dock-state-section { margin: 0; }.dock-state-heading { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }.dock-state-heading h4 { margin: 0; font-size: 12px; }.dock-state-heading span { color: var(--muted); font-size: 8px; }.dock-state-board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; }.dock-state-board article { min-height: 112px; padding: 10px 11px; border: 1px solid #e4eaf0; border-radius: 8px; background: #f7f9fb; }.dock-state-board h5 { margin: 0 0 8px; color: #2f7fb9; font-size: 12px; font-weight: 600; }.dock-state-board ul { margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; list-style: none; }.dock-state-board li { display: flex; align-items: center; gap: 6px; color: #82909b; font-size: 10px; line-height: 1.2; }.dock-state-board li i { width: 7px; height: 7px; flex: 0 0 7px; border-radius: 50%; background: #cbd5dd; }.dock-state-board li.active { color: #486271; }.dock-state-board li.active i { background: #36a477; box-shadow: 0 0 0 2px #e3f3ed; }.dock-state-board li i.warning { background: #dda44a; box-shadow: 0 0 0 2px #fff1dc; }.dock-status-summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }.dock-status-summary span { display: flex; flex-direction: column; gap: 4px; color: var(--muted); font-size: 10px; }.dock-status-summary b { color: #405460; font-size: 11px; font-weight: 500; } +.dock-state-board .flow-status-list { display: grid; grid-template-columns: 1fr 1fr; column-gap: 10px; row-gap: 6px; }.dock-state-board .flow-status-list li { white-space: nowrap; } +.device-detail-state-board { margin-top: 12px; }.device-detail-state-board article { background: #f5f7f9; }.detail-status-note { color: var(--muted); font-size: 10px; } +@media (max-width: 560px) { + .dock-state-board { grid-template-columns: repeat(2, 1fr); } + .dock-status-summary { grid-template-columns: repeat(2, 1fr); row-gap: 9px; } + .dock-status-dialog { width: min(520px, calc(100vw - 20px)); } + .dock-status-body { max-height: calc(100vh - 96px); overflow: auto; } + .dock-process-track { grid-template-columns: repeat(2, 1fr); row-gap: 12px; } + .dock-process-track > div:nth-child(2n)::after { display: none; } + .dock-status-grid { grid-template-columns: repeat(2, 1fr); } + .dock-status-environment { grid-template-columns: repeat(2, 1fr); } +} +.add-dock-dialog { width: min(520px, 100%); padding: 0; overflow: hidden; text-align: left; }.add-dock-dialog > header { height: 68px; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); }.add-dock-dialog > header > div { display: flex; flex-direction: column; gap: 3px; }.add-dock-dialog > header span { color: var(--muted); font-size: 10px; }.add-dock-dialog > header h3 { margin: 0; font-size: 18px; }.add-dock-dialog form { padding: 18px 20px 0; }.add-dock-dialog label { display: flex; flex-direction: column; gap: 6px; margin-bottom: 15px; }.add-dock-dialog label > span { color: #485764; font-size: 12px; }.add-dock-dialog label b { color: var(--red); font-weight: 500; }.add-dock-dialog label em { margin-left: 4px; color: #8a96a0; font-size: 10px; font-style: normal; }.add-dock-dialog input { width: 100%; height: 38px; padding: 0 10px; border: 1px solid var(--line); border-radius: 5px; outline: 0; color: var(--ink); background: #fff; font-size: 12px; }.add-dock-dialog input:focus { border-color: #6799d1; box-shadow: 0 0 0 3px rgba(31,111,235,.08); }.add-dock-dialog input[readonly] { color: #77838d; background: #f2f4f6; cursor: not-allowed; }.add-dock-dialog input.invalid { border-color: var(--red); }.add-dock-dialog label small { min-height: 0; color: var(--red); font-size: 10px; }.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }.form-error { min-height: 28px; padding-top: 7px; color: var(--red); font-size: 11px; }.add-dock-dialog form > footer { height: 62px; margin: 0 -20px; padding: 0 20px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--line); }.add-dock-dialog form > footer button { min-width: 82px; } +.route-upload-dialog select { width: 100%; height: 38px; padding: 0 10px; border: 1px solid var(--line); border-radius: 5px; outline: 0; color: var(--ink); background: #fff; font-size: 12px; }.add-dock-dialog .route-upload-note { margin: -4px 0 0; color: #778590; font-size: 10px; line-height: 1.6; } +.firmware-dialog { width: min(500px, calc(100vw - 32px)); padding: 0; overflow: hidden; text-align: left; }.firmware-content { padding: 18px 20px 8px; }.firmware-device { min-height: 54px; padding: 9px 10px; display: grid; grid-template-columns: 34px 1fr auto; align-items: center; gap: 9px; border: 1px solid var(--line); border-radius: 5px; background: #f7f9fb; }.firmware-device > div { min-width: 0; display: flex; flex-direction: column; gap: 4px; }.firmware-device strong { font-size: 12px; }.firmware-device small { color: var(--muted); font-size: 10px; }.firmware-content label { display: flex; flex-direction: column; gap: 6px; margin-top: 16px; }.firmware-content label > span { color: #485764; font-size: 12px; }.firmware-content select { width: 100%; height: 38px; padding: 0 10px; border: 1px solid var(--line); border-radius: 5px; color: var(--ink); background: #fff; font-size: 12px; }.firmware-checks { margin-top: 15px; padding: 10px; display: flex; justify-content: space-between; gap: 8px; border-radius: 4px; background: #f5f8fa; }.firmware-checks span { display: flex; align-items: center; gap: 5px; color: #52616e; font-size: 10px; }.firmware-checks i { width: 6px; height: 6px; border-radius: 50%; background: var(--green); }.firmware-note { margin: 13px 0 0; color: #7b8791; font-size: 10px; line-height: 1.6; }.firmware-footer { height: 62px; padding: 0 20px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--line); }.firmware-footer button { min-width: 82px; } +.toast { position: fixed; z-index: 100; top: 78px; left: 50%; min-width: 180px; height: 42px; padding: 0 18px; display: flex; align-items: center; justify-content: center; gap: 9px; border-radius: 5px; background: #fff; box-shadow: var(--shadow); transform: translate(-50%, -18px); opacity: 0; pointer-events: none; transition: .22s ease; } +.toast.show { transform: translate(-50%, 0); opacity: 1; }.toast span { width: 8px; height: 8px; border-radius: 50%; background: var(--green); }.toast strong { font-size: 13px; } + +@media (max-width: 1260px) { + .monitor-layout { grid-template-columns: 0 minmax(0, 1fr) 0; } + .monitor-layout.focused { grid-template-columns: 248px minmax(330px, 1fr) 320px; } + .device-panel { width: 248px; } + .detail-panel { width: 320px; } + .asset-overview { grid-template-columns: repeat(5, 1fr); min-height: 74px; } +} +@media (max-width: 820px) { + .login-page { display: block; }.login-visual { display: none; }.login-form-side { width: 100%; min-height: 100vh; padding: 28px 22px; background: #f3f6f9; }.login-form-wrap { width: min(360px, 100%); padding: 32px 24px 28px; } +} +@media (max-width: 1020px) { + .sidebar { position: fixed; left: -208px; transition: left .2s ease; box-shadow: var(--shadow); } + .sidebar.open { left: 0; } + .mobile-menu { position: fixed; z-index: 25; top: 12px; left: 12px; display: inline-flex; color: #fff; background: rgba(26,43,54,.76); } + .monitor-layout.focused { grid-template-columns: 225px minmax(280px, 1fr) 300px; } + .device-panel { width: 225px; } + .detail-panel { width: 300px; } + .device-page { padding: 18px; } + .asset-toolbar { align-items: stretch; flex-direction: column; } + .asset-search { width: 100%; } + .toolbar-group { justify-content: flex-end; } + .asset-table { min-width: 920px; } + .operation-page { padding: 18px; } + .business-metric-band { min-width: 760px; } + .task-metrics { overflow-x: auto; } + .route-grid { grid-template-columns: repeat(2, minmax(220px, 1fr)); } + .media-workspace { min-width: 680px; } + .operation-page { overflow-x: auto; } + .detail-metric-band { grid-template-columns: repeat(3, 1fr); padding: 8px; } + .detail-metric-band > div:nth-child(3) { border-right: 0; } + .detail-columns { grid-template-columns: 1fr; } + .detail-side-column { border-top: 1px solid var(--line); border-left: 0; } + .record-table { min-width: 760px; } +} +@media (max-width: 760px) { + .topbar { height: 56px; min-height: 56px; padding: 0 10px; } + .breadcrumb span, .breadcrumb svg, .profile div, .profile > svg { display: none; } + .sidebar-profile div { display: flex; } + .sidebar-profile > svg { display: block; } + .monitor-layout, .monitor-layout.focused { display: grid; grid-template-columns: 0 minmax(0, 1fr) 0; overflow: hidden; } + .device-panel { display: none; } + .map-panel { min-height: 390px; } + .detail-panel { position: absolute; right: 0; top: 0; bottom: 0; width: min(360px, 94vw); box-shadow: -8px 0 24px rgba(21,33,43,.16); } + .detail-header { min-height: 78px; } + .map-legend { max-width: calc(100% - 28px); overflow-x: auto; } + .device-page { padding: 12px; gap: 12px; } + .device-page-header h1 { font-size: 20px; } + .device-page-header > div:first-child span { display: none; } + .asset-overview { min-width: 620px; } + .device-page { overflow-x: auto; } + .asset-workspace { min-width: 680px; } + .device-drawer { position: fixed; top: 56px; width: min(408px, 94vw); } + .drawer-scrim { position: fixed; top: 56px; } + .form-row { grid-template-columns: 1fr; gap: 0; } + .operation-page { padding: 12px; gap: 12px; } + .operation-page-header h1 { font-size: 20px; } + .operation-page-header > div span { display: none; } + .business-workspace { min-width: 680px; } + .business-tabs { gap: 22px; } + .route-grid { grid-template-columns: repeat(2, minmax(240px, 1fr)); } + .live-media-grid, .media-grid { grid-template-columns: repeat(2, minmax(260px, 1fr)); } + .full-detail-header { padding: 10px 14px 14px; grid-template-columns: minmax(0, 1fr) auto; } + .full-detail-actions .plain-command { display: none; } + .full-detail-tabs { padding: 0 14px; gap: 22px; overflow-x: auto; align-items: end; } + .full-detail-tabs button { flex: 0 0 auto; } + .full-detail-content { margin: 10px; } + .detail-metric-band > div { padding: 0 10px; } + .state-groups, .diagnostics-layout { grid-template-columns: 1fr; } + .state-groups .data-section:nth-child(odd), .diagnostics-layout .data-section:nth-child(odd) { border-right: 0; } + .status-matrix, .environment-values { grid-template-columns: repeat(2, 1fr); } + .record-header { min-width: 680px; } +} + +/* Keep login field labels readable against the compact form controls. */ +.login-form-wrap form > label > span, +.login-form-row > label > span { + margin-bottom: 10px; + color: #3f515d; + font-size: 13px; + font-weight: 500; + line-height: 1.35; +} + +#loginForm .login-method-fields > label, +#loginForm > label { + margin-bottom: 26px; +} + +#loginForm > label { + margin-bottom: 10px; +} + +#loginForm .login-method-fields { + display: flex; + flex-direction: column; + gap: 16px; + margin-bottom: 14px; +} + +#loginForm .login-method-fields[hidden] { + display: none; +} + +#loginForm .login-method-fields > label { + margin-bottom: 0; +} + +/* Registration keeps the form focused and aligns the agreement control inline. */ +#registerPanel .login-form-heading > span, +#registerPanel .login-form-heading > p { + display: none; +} + +#registerPanel .login-form-heading h2 { + margin: 0 0 24px; +} + +#registerPanel #registerForm .register-agreement { + display: flex; + align-items: center; + gap: 7px; + margin-bottom: 17px; + font-size: 11px; + line-height: 1.5; +} + +#registerPanel #registerForm .register-agreement > span { + width: 14px; + height: 14px; + min-width: 14px; + margin: 0; + font-size: 0; + line-height: 1; +} + +#registerPanel #registerForm .login-form-row { + margin-bottom: 17px; +} + +.control-page { min-height: 100vh; overflow: auto; background: #f3f6f8; } +.control-page-header { padding: 18px 24px; background: #fff; border-bottom: 1px solid var(--line); } +.control-page-header h1 { margin: 0 0 5px; font-size: 22px; } +.control-workspace { min-height: calc(100vh - 76px); display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 14px; padding: 14px; } +.control-device-list, .control-main { min-width: 0; border: 1px solid var(--line); border-radius: 6px; background: #fff; } +.control-list-heading { min-height: 62px; padding: 0 15px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); } +.control-list-heading h2, .control-selected-header h2 { margin: 0 0 4px; font-size: 17px; } +.control-list-heading span, .control-selected-header p { color: var(--muted); font-size: 11px; } +.control-device-items { padding: 8px; } +.control-device-item { width: 100%; min-height: 70px; padding: 10px 8px; display: grid; grid-template-columns: 30px minmax(0, 1fr) auto; align-items: center; gap: 8px; border-bottom: 1px solid #edf0f2; color: var(--ink); background: transparent; text-align: left; } +.control-device-item:last-child { border-bottom: 0; } +.control-device-item:hover, .control-device-item.active { background: #f2f7fc; } +.control-device-item.active { box-shadow: inset 3px 0 0 var(--blue); } +.control-device-icon { width: 28px; height: 28px; display: grid; place-items: center; border-radius: 4px; color: #3478b8; background: #e8f1f8; } +.control-device-icon svg { width: 15px; } +.control-device-item strong, .control-device-item small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.control-device-item strong { font-size: 12px; } +.control-device-item small { margin-top: 4px; color: var(--muted); font-size: 10px; } +.control-device-item .status { font-size: 10px; } +.control-main { overflow: hidden; } +.control-selected-header { min-height: 86px; padding: 17px 20px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); } +.control-status-band { min-height: 70px; display: grid; grid-template-columns: repeat(5, 1fr); border-bottom: 1px solid var(--line); background: #f7f9fa; } +.control-status-band > div { padding: 14px 16px; border-right: 1px solid var(--line); } +.control-status-band > div:last-child { border-right: 0; } +.control-status-band span, .control-status-band strong { display: block; } +.control-status-band span { color: var(--muted); font-size: 10px; } +.control-status-band strong { margin-top: 6px; color: #344651; font-size: 12px; font-weight: 500; } +.control-alert-section { padding: 15px 20px; border-bottom: 1px solid var(--line); } +.control-alert-content { min-height: 52px; padding: 10px 12px; display: flex; align-items: center; gap: 10px; border: 1px solid #d9ebe3; border-radius: 5px; background: #f5fbf8; } +.control-alert-content.alarm { border-color: #f0d6d7; background: #fff7f7; } +.control-alert-content.offline { border-color: #e2e5e7; background: #f7f8f9; } +.control-alert-icon { width: 25px; height: 25px; min-width: 25px; display: grid; place-items: center; border-radius: 50%; color: var(--red); background: #fbe3e4; } +.control-alert-icon.ok { color: var(--green); background: #dff3e9; } +.control-alert-icon svg { width: 13px; } +.control-alert-content > div { min-width: 0; display: flex; flex-direction: column; gap: 3px; } +.control-alert-content strong { color: #49545d; font-size: 12px; } +.control-alert-content small { color: var(--muted); font-size: 10px; } +.control-alert-content.alarm strong { color: #a53e45; } +.control-alert-content .plain-command { margin-left: auto; white-space: nowrap; } +.control-section { padding: 18px 20px; border-bottom: 1px solid var(--line); } +.control-section .data-section-title { margin-bottom: 14px; } +.control-section .data-section-title span { color: var(--muted); font-size: 10px; } +.control-command-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px; } +.control-command { min-height: 58px; padding: 10px 12px; display: flex; align-items: center; gap: 10px; border: 1px solid var(--line); border-radius: 5px; color: #344651; background: #fff; text-align: left; } +.control-command:hover { border-color: #8fb7d6; background: #f5faff; } +.control-command.primary, .control-command.landing { color: #fff; border-color: transparent; background: var(--blue); } +.control-command.landing { background: #1d7468; } +.control-command.danger { color: #a53e45; } +.control-command > svg { width: 18px; } +.control-command > b { width: 23px; height: 23px; display: grid; place-items: center; border-radius: 3px; color: #3478b8; background: #eaf2f8; font-size: 10px; font-weight: 500; } +.control-command span { min-width: 0; display: flex; flex-direction: column; gap: 3px; } +.control-command strong { font-size: 12px; } +.control-command small { color: #7b8992; font-size: 10px; } +.control-command.primary small, .control-command.landing small { color: rgba(255,255,255,.78); } +.control-command-grid.compact { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.control-note { display: flex; align-items: flex-start; gap: 10px; border-bottom: 0; background: #fbfcfd; } +.control-note > svg { width: 17px; color: #5e88ab; } +.control-note span { display: flex; flex-direction: column; gap: 4px; } +.control-note strong { font-size: 11px; } +.control-note small { color: var(--muted); font-size: 10px; line-height: 1.5; } +.more-control-action { width: 100%; min-height: 36px; margin-top: 8px; padding: 0 10px; display: flex; align-items: center; gap: 7px; border: 1px solid #dbe4ea; border-radius: 4px; color: #4f6471; background: #fff; font-size: 11px; } +.more-control-action:hover { color: var(--blue); border-color: #9ebed8; background: #f7fbff; } +.more-control-action.expanded { color: var(--blue); border-color: #9ebed8; background: #f7fbff; } +.more-control-action.expanded svg:last-child { transform: rotate(90deg); } +.control-mode-toggle { display: inline-flex; align-items: center; gap: 3px; padding: 0; color: var(--blue); background: transparent; font-size: 10px; } +.control-mode-toggle:hover { color: var(--blue-dark); } +.control-mode-toggle svg { width: 12px; } +.control-mode-toggle.expanded svg { transform: rotate(180deg); } +.more-control-action svg:last-child { margin-left: auto; width: 13px; } +.drone-advanced-actions { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px; margin-top: 7px; } +.drone-advanced-actions button { min-height: 34px; padding: 0 9px; display: flex; align-items: center; gap: 6px; border: 1px solid var(--line); border-radius: 4px; color: #50616d; background: #fff; font-size: 11px; text-align: left; } +.drone-advanced-actions button:hover { color: var(--blue); border-color: #9ebed8; background: #f7fbff; } +.drone-advanced-actions svg { width: 13px; } +.dock-control-extra { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); } +.dock-control-info { display: grid; grid-template-columns: repeat(4, 1fr); margin-bottom: 10px; border: 1px solid var(--line); border-radius: 5px; background: #f7f9fa; } +.dock-control-info > div { min-width: 0; padding: 10px; border-right: 1px solid var(--line); } +.dock-control-info > div:last-child { border-right: 0; } +.dock-control-info span, .dock-control-info strong { display: block; } +.dock-control-info span { color: var(--muted); font-size: 10px; } +.dock-control-info strong { margin-top: 5px; overflow: hidden; color: #40515d; font-size: 11px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; } +.dock-control-extra-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; } +.quick-actions.all-controls .workflow-actions, +.quick-actions.all-controls .stage-actions, +.quick-actions.all-controls .reset-action { display: none; } +.quick-actions.all-controls .dock-control-extra-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } +.quick-actions.all-controls .dock-control-extra-grid button { min-height: 42px; padding: 6px 8px; } +.drone-quick-actions .secondary-actions { grid-template-columns: repeat(3, 1fr); } +.drone-quick-actions.all-controls .primary-action, +.drone-quick-actions.all-controls .secondary-actions { display: none; } +.drone-control-all button.danger { color: #a53e45; border-color: #e3b4b7; } +.drone-control-all button.danger:hover { color: #8f2f36; border-color: #d18a8e; background: #fff5f5; } +.drone-control-all { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; margin-top: 8px; } +.drone-control-all[hidden] { display: none !important; } +.drone-control-all button { min-height: 42px; padding: 6px 8px; display: flex; align-items: center; gap: 6px; border: 1px solid var(--line); border-radius: 4px; color: #4c5f6b; background: #fff; font-size: 10px; } +.drone-control-all button:hover { color: var(--blue); border-color: #91b5d6; background: #f7fbff; } +.drone-control-all button > span { display: flex; flex-direction: column; gap: 3px; } +.drone-control-all button strong { font-size: 11px; font-weight: 500; } +.drone-control-all button small { color: var(--muted); font-size: 8px; } +.dock-control-extra-grid button { min-height: 45px; padding: 7px 10px; display: flex; align-items: center; gap: 7px; border: 1px solid var(--line); border-radius: 4px; color: #4c5f6b; background: #fff; font-size: 11px; } +.dock-control-extra-grid button:hover { color: var(--blue); border-color: #91b5d6; background: #f7fbff; } +.dock-control-extra-grid svg { width: 14px; } +.dock-control-extra-grid button > span { display: flex; flex-direction: column; gap: 3px; } +.dock-control-extra-grid button strong { font-size: 11px; font-weight: 500; } +.dock-control-extra-grid button small { color: var(--muted); font-size: 8px; } +@media (max-width: 1180px) { + .monitor-layout.focused.control-expanded { grid-template-columns: 225px minmax(280px, 1fr) 460px; } + .monitor-layout.control-expanded .detail-panel { width: 460px; } +} +@media (max-width: 760px) { + .monitor-layout.focused.control-expanded { grid-template-columns: 0 minmax(0, 1fr) 0; } + .monitor-layout.control-expanded .detail-panel { width: min(560px, 94vw); } + .dock-control-info { grid-template-columns: repeat(2, 1fr); } + .dock-control-info > div:nth-child(2) { border-right: 0; } + .dock-control-extra-grid { grid-template-columns: repeat(2, 1fr); } + .quick-actions.all-controls .dock-control-extra-grid { grid-template-columns: repeat(2, 1fr); } + .drone-control-all { grid-template-columns: repeat(2, 1fr); } + .drone-control-all { grid-template-columns: repeat(2, 1fr); } +} +.forgot-password-dialog { width: min(400px, 100%); padding: 0; overflow: hidden; text-align: left; } +.forgot-password-dialog > header { min-height: 68px; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); } +.forgot-password-dialog > header > div { display: flex; flex-direction: column; gap: 4px; } +.forgot-password-dialog > header span { color: var(--muted); font-size: 10px; } +.forgot-password-dialog > header h3 { margin: 0; font-size: 18px; } +.forgot-password-dialog form { padding: 18px 20px 0; } +.forgot-password-dialog label { display: flex; flex-direction: column; gap: 6px; margin-bottom: 15px; } +.forgot-password-dialog label > span { color: #485764; font-size: 12px; } +.forgot-password-dialog input { width: 100%; height: 38px; padding: 0 10px; border: 1px solid var(--line); border-radius: 5px; outline: 0; color: var(--ink); background: #fff; font-size: 12px; } +.forgot-password-dialog input:focus { border-color: #6799d1; box-shadow: 0 0 0 3px rgba(31,111,235,.08); } +.forgot-password-dialog .login-input { height: 38px; } +.forgot-password-dialog form > footer { height: 62px; margin: 3px -20px 0; padding: 0 20px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--line); } +.full-detail-tabs button[data-detail-tab="state"], +.full-detail-tabs button[data-detail-tab="alarms"], +.full-detail-tabs button[data-detail-tab="diagnostics"], +.full-tab-panel[data-detail-panel="state"], +.full-tab-panel[data-detail-panel="alarms"], +.full-tab-panel[data-detail-panel="diagnostics"], +[data-detail-jump="state"] { display: none !important; } +.traffic-balance-card { min-height: 112px; padding: 17px; border: 1px solid #d7e4ee; border-radius: 5px; background: #f5f9fc; } +.traffic-balance-card > span { color: var(--muted); font-size: 10px; } +.traffic-balance-card > strong { display: block; margin-top: 8px; color: #246fae; font-size: 32px; font-weight: 600; } +.traffic-balance-card > strong small { font-size: 12px; font-weight: 500; } +.traffic-balance-card p { margin: 7px 0 0; color: #74838d; font-size: 10px; } +.traffic-unit-price { color: var(--blue); font-size: 17px; } +.traffic-purchase-panel { min-height: 390px; } +.traffic-purchase-layout { display: grid; grid-template-columns: minmax(0, 1fr) 250px; gap: 18px; } +.traffic-amount-block { padding: 18px; border: 1px solid var(--line); border-radius: 5px; } +.traffic-amount-block label > span { display: block; margin-bottom: 8px; color: #4e5f6b; font-size: 11px; } +.traffic-amount-block label > div { height: 46px; display: flex; align-items: center; border: 1px solid #cfdae2; border-radius: 4px; overflow: hidden; } +.traffic-amount-block input { width: 100%; height: 100%; padding: 0 12px; border: 0; outline: 0; color: #304552; background: #fff; font-size: 19px; } +.traffic-amount-block label b { padding: 0 13px; color: #6c7d88; font-size: 12px; font-weight: 500; } +.traffic-quick-amounts { margin-top: 13px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; } +.traffic-quick-amounts button { min-height: 34px; border: 1px solid var(--line); border-radius: 4px; color: #526570; background: #fff; font-size: 11px; } +.traffic-quick-amounts button:hover, .traffic-quick-amounts button.active { color: var(--blue); border-color: #8fb8da; background: #f3f8fc; } +.traffic-order-summary { padding: 18px; display: flex; flex-direction: column; border: 1px solid #d7e4ee; border-radius: 5px; background: #f5f9fc; } +.traffic-order-summary > span { color: var(--muted); font-size: 10px; } +.traffic-order-summary > strong { margin: 5px 0 14px; font-size: 13px; } +.traffic-order-summary > b { margin: 6px 0; color: #246fae; font-size: 29px; } +.traffic-order-summary > small { color: var(--muted); font-size: 10px; } +.traffic-order-summary .primary-command { margin-top: auto; justify-content: center; } +.traffic-scope { margin-top: 14px; display: grid; grid-template-columns: repeat(3, 1fr); border: 1px solid var(--line); border-radius: 5px; } +.traffic-scope span { min-height: 48px; display: flex; align-items: center; justify-content: center; gap: 7px; color: #526570; border-right: 1px solid var(--line); font-size: 11px; } +.traffic-scope span:last-child { border-right: 0; } +.traffic-scope svg { width: 14px; color: #347cb9; } +@media (max-width: 760px) { .traffic-purchase-layout { grid-template-columns: 1fr; }.traffic-quick-amounts { grid-template-columns: repeat(2, 1fr); }.traffic-scope { grid-template-columns: 1fr; }.traffic-scope span { border-right: 0; border-bottom: 1px solid var(--line); }.traffic-scope span:last-child { border-bottom: 0; } } +@media (max-width: 900px) { .control-workspace { grid-template-columns: 240px minmax(0, 1fr); }.control-status-band { grid-template-columns: repeat(3, 1fr); }.control-status-band > div:nth-child(3) { border-right: 0; }.control-command-grid.compact { grid-template-columns: repeat(2, minmax(0, 1fr)); } } +@media (max-width: 700px) { .control-workspace { grid-template-columns: 1fr; }.control-device-list { max-height: 270px; overflow: auto; }.control-status-band { grid-template-columns: repeat(2, 1fr); }.control-status-band > div:nth-child(2n) { border-right: 0; }.control-page-header { padding: 14px; } } + +.account-metrics > div:first-child strong { font-size: 25px; } +.communication-warning-metric strong { color: #a96621; } +.communication-danger-metric strong { color: #bd4046; } +.account-identity-strip { min-height: 78px; padding: 10px 20px; display: grid; grid-template-columns: minmax(260px, 1.5fr) repeat(2, 1fr); align-items: center; gap: 18px; border-bottom: 1px solid var(--line); background: #fafbfc; } +.account-identity-strip .tenant-identity { min-height: 54px; padding: 0; border: 0; background: transparent; } +.account-identity-strip > div:not(.tenant-identity) { min-width: 0; display: flex; flex-direction: column; gap: 5px; } +.account-identity-strip > div:not(.tenant-identity) span { color: var(--muted); font-size: 10px; } +.account-identity-strip > div:not(.tenant-identity) strong { overflow: hidden; color: #3d4f5b; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; } +.account-resource-grid { padding: 18px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; } +.account-resource-card { min-width: 0; min-height: 300px; padding: 18px; border: 1px solid var(--line); border-radius: 6px; background: #fff; } +.resource-type-tag { padding: 4px 7px; border-radius: 3px; color: #276da9; background: #edf5fb; font-size: 10px; font-style: normal; white-space: nowrap; } +.resource-type-tag.carrier { color: #55705f; background: #edf4ef; } +.account-resource-value { padding: 16px 0 14px; border-bottom: 1px solid var(--line); } +.account-resource-value > span { display: block; color: var(--muted); font-size: 10px; } +.account-resource-value > strong { display: block; margin-top: 7px; color: #276da9; font-size: 34px; } +.account-resource-value small { font-size: 12px; font-weight: 500; } +.account-resource-meta { min-height: 57px; display: flex; align-items: center; gap: 30px; } +.account-resource-meta span { color: var(--muted); font-size: 10px; } +.account-resource-meta b { margin-left: 5px; color: #3c4e5a; font-size: 11px; font-weight: 500; } +.account-card-actions { margin-top: 10px; display: flex; align-items: center; gap: 8px; } +.communication-status-grid { margin: 2px 0 14px; display: grid; grid-template-columns: repeat(3, 1fr); border: 1px solid var(--line); border-radius: 5px; } +.communication-status-grid > div { min-height: 66px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; border-right: 1px solid var(--line); } +.communication-status-grid > div:last-child { border-right: 0; } +.communication-status-grid strong { font-size: 22px; } +.communication-status-grid span { color: var(--muted); font-size: 10px; } +.communication-alert { min-height: 48px; padding: 0 11px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid #eccacc; border-radius: 4px; background: #fff7f7; } +.communication-alert > span { display: flex; align-items: center; gap: 6px; color: #7f5154; font-size: 10px; } +.communication-alert > span i { width: 6px; height: 6px; border-radius: 50%; background: #c4474c; } +.communication-alert > span strong { color: #a43d42; font-size: 11px; } +.communication-alert button { color: #b23e43; background: transparent; font-size: 10px; white-space: nowrap; } +.cloud-media-panel { padding: 18px 20px 22px; } +.cloud-media-stats { margin-bottom: 16px; display: grid; grid-template-columns: repeat(4, 1fr); border: 1px solid var(--line); border-radius: 5px; } +.cloud-media-stats > div { min-height: 72px; padding: 12px 14px; display: flex; flex-direction: column; justify-content: center; gap: 5px; border-right: 1px solid var(--line); } +.cloud-media-stats > div:last-child { border-right: 0; } +.cloud-media-stats span, .cloud-media-stats small { color: var(--muted); font-size: 10px; } +.cloud-media-stats strong { color: #344955; font-size: 18px; } +.cloud-purchase-layout { margin-bottom: 20px; } +.account-subsection-title { padding: 15px 0 10px; display: flex; align-items: flex-end; justify-content: space-between; border-top: 1px solid var(--line); } +.account-subsection-title h3 { margin: 0 0 4px; font-size: 13px; } +.account-subsection-title span { color: var(--muted); font-size: 10px; } +.communication-panel { min-width: 820px; } +.communication-panel-head { min-height: 70px; padding: 0 18px; display: flex; align-items: center; justify-content: space-between; gap: 18px; border-bottom: 1px solid var(--line); } +.communication-panel-head h2 { margin: 0 0 5px; font-size: 14px; } +.communication-panel-head > div > span { color: var(--muted); font-size: 10px; } +.communication-head-stats { display: flex; align-items: center; gap: 7px; } +.communication-head-stats span { padding: 5px 8px; border-radius: 3px; color: #61717c; background: #f1f4f6; font-size: 10px; } +.communication-head-stats span.normal { color: #34705b; background: #edf5f0; } +.communication-head-stats span.warning { color: #996020; background: #fff4e5; } +.communication-head-stats span.danger { color: #ad3d42; background: #fff0f0; } +.communication-head-stats b { margin-left: 3px; font-size: 11px; } +.communication-toolbar { min-height: 58px; } +.communication-table-wrap { overflow: auto; } +.communication-table { width: 100%; min-width: 1050px; border-collapse: collapse; table-layout: fixed; } +.communication-table th { height: 39px; padding: 0 12px; color: var(--muted); background: #f7f8fa; font-size: 10px; font-weight: 500; text-align: left; } +.communication-table th:first-child { width: 15%; }.communication-table th:nth-child(2) { width: 14%; }.communication-table th:nth-child(5) { width: 16%; }.communication-table th:last-child { width: 9%; } +.communication-table td { height: 62px; padding: 9px 12px; border-bottom: 1px solid var(--line); color: #475965; font-size: 11px; vertical-align: middle; } +.communication-table td > strong, .communication-table td > small { display: block; } +.communication-table td > strong { color: #344752; font-size: 11px; }.communication-table td > small { margin-top: 4px; color: var(--muted); font-size: 10px; } +.communication-table td:last-child button { color: var(--blue); background: transparent; font-size: 10px; white-space: nowrap; } +.communication-table td:last-child button.urgent-action { color: #ba3f44; } +.communication-usage { display: flex; flex-direction: column; gap: 6px; } +.communication-usage span { color: #4b5d68; font-size: 10px; } +.communication-usage i { height: 5px; overflow: hidden; border-radius: 4px; background: #e7ecef; } +.communication-usage em { display: block; height: 100%; border-radius: inherit; background: #4384ba; } +.communication-usage.warning em { background: #d58a31; }.communication-usage.danger em { background: #c4484d; } +.carrier-status { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; } +.carrier-status i { width: 6px; height: 6px; border-radius: 50%; }.carrier-status.normal i { background: var(--green); }.carrier-status.warning i { background: var(--orange); }.carrier-status.danger i { background: var(--red); }.carrier-status.owned i { background: #88959e; } +.muted-cell { color: var(--muted); font-size: 10px; } +.account-order-table { border-top: 1px solid var(--line); } +.account-order-table > div { min-height: 52px; display: grid; grid-template-columns: 140px 125px minmax(210px, 1fr) 75px 80px 105px; align-items: center; gap: 10px; border-bottom: 1px solid var(--line); } +.account-order-table .order-head { min-height: 38px; color: var(--muted); background: #f7f8fa; } +.account-order-table span, .account-order-table strong, .account-order-table em { padding: 0 8px; color: #4a5b66; font-size: 10px; font-style: normal; font-weight: 500; } +.account-order-table em { color: var(--green); }.account-order-table em.processing { color: #a46924; } +.carrier-current-plan { min-height: 51px; margin-bottom: 15px; padding: 9px 11px; display: flex; flex-direction: column; justify-content: center; gap: 5px; border: 1px solid var(--line); border-radius: 5px; background: #f7f9fa; } +.carrier-current-plan span { color: var(--muted); font-size: 10px; }.carrier-current-plan strong { color: #3b4d58; font-size: 12px; } +.carrier-package-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; } +.carrier-package-grid button { min-height: 54px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; border: 1px solid var(--line); border-radius: 4px; color: #435560; background: #fff; } +.carrier-package-grid button.active { color: #276ea9; border-color: #79a9d2; background: #f2f8fc; box-shadow: inset 0 0 0 1px #c4dced; } +.carrier-package-grid strong { font-size: 12px; }.carrier-package-grid small { color: var(--muted); font-size: 10px; } +.carrier-effective-row { min-height: 47px; display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--line); } +.carrier-effective-row > span { color: #485a65; font-size: 11px; }.carrier-effective-row > div { display: flex; gap: 18px; } +.carrier-effective-row label { margin: 0; display: flex; flex-direction: row; align-items: center; gap: 6px; color: #52636e; font-size: 10px; cursor: pointer; } +.carrier-effective-row input { position: absolute; opacity: 0; }.carrier-effective-row i { width: 13px; height: 13px; border: 1px solid #b8c4cc; border-radius: 50%; background: #fff; }.carrier-effective-row input:checked + i { border-color: var(--blue); box-shadow: inset 0 0 0 3px #fff; background: var(--blue); } +.carrier-recharge-total { min-height: 54px; padding: 0 2px; display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--line); } +.carrier-recharge-total span { color: var(--muted); font-size: 11px; }.carrier-recharge-total strong { color: #276ea9; font-size: 24px; } +.recharge-modal { width: min(440px, 100%); padding: 20px; border-radius: 7px; background: #fff; box-shadow: 0 22px 60px rgba(0,0,0,.23); text-align: left; transform: translateY(8px); transition: transform .18s ease; } +.modal-backdrop.open .recharge-modal { transform: translateY(0); } +.recharge-modal h3 { margin: 0 0 5px; font-size: 16px; } +.recharge-sub { margin: 0 0 16px; color: var(--muted); font-size: 11px; } +.recharge-modal-footer { margin-top: 14px; display: flex; justify-content: flex-end; gap: 8px; } +.recharge-modal-footer button { height: 36px; padding: 0 16px; border-radius: 5px; font-size: 12px; } +.recharge-modal-footer .cancel { background: #fff; border: 1px solid var(--line); } +.recharge-modal-footer .confirm { color: #fff; background: var(--blue); } +.recharge-modal-footer .confirm:disabled { opacity: .6; cursor: not-allowed; } +@media (max-width: 900px) { .account-identity-strip { grid-template-columns: 1fr 1fr; }.account-resource-grid { grid-template-columns: 1fr; }.cloud-media-stats { grid-template-columns: repeat(2, 1fr); }.cloud-media-stats > div:nth-child(2) { border-right: 0; }.cloud-media-stats > div:nth-child(-n+2) { border-bottom: 1px solid var(--line); } } +@media (max-width: 620px) { .account-identity-strip { grid-template-columns: 1fr; }.communication-status-grid { grid-template-columns: repeat(3, 1fr); }.carrier-package-grid { grid-template-columns: repeat(2, 1fr); } } diff --git a/src/views/Account.vue b/src/views/Account.vue new file mode 100644 index 0000000..49f1130 --- /dev/null +++ b/src/views/Account.vue @@ -0,0 +1,413 @@ + + + diff --git a/src/views/Alarms.vue b/src/views/Alarms.vue new file mode 100644 index 0000000..bcdb6ba --- /dev/null +++ b/src/views/Alarms.vue @@ -0,0 +1,136 @@ + + + diff --git a/src/views/Control.vue b/src/views/Control.vue new file mode 100644 index 0000000..9156350 --- /dev/null +++ b/src/views/Control.vue @@ -0,0 +1,152 @@ + + + diff --git a/src/views/DeviceDetail.vue b/src/views/DeviceDetail.vue new file mode 100644 index 0000000..4904c43 --- /dev/null +++ b/src/views/DeviceDetail.vue @@ -0,0 +1,593 @@ + + + diff --git a/src/views/Devices.vue b/src/views/Devices.vue new file mode 100644 index 0000000..2d4503a --- /dev/null +++ b/src/views/Devices.vue @@ -0,0 +1,512 @@ + + + diff --git a/src/views/Firmwares.vue b/src/views/Firmwares.vue new file mode 100644 index 0000000..5aa4611 --- /dev/null +++ b/src/views/Firmwares.vue @@ -0,0 +1,266 @@ + + + diff --git a/src/views/Login.vue b/src/views/Login.vue new file mode 100644 index 0000000..972b480 --- /dev/null +++ b/src/views/Login.vue @@ -0,0 +1,197 @@ + + + diff --git a/src/views/Media.vue b/src/views/Media.vue new file mode 100644 index 0000000..d30dda3 --- /dev/null +++ b/src/views/Media.vue @@ -0,0 +1,145 @@ + + + diff --git a/src/views/Monitor.vue b/src/views/Monitor.vue new file mode 100644 index 0000000..f7ec6a9 --- /dev/null +++ b/src/views/Monitor.vue @@ -0,0 +1,479 @@ + + + diff --git a/src/views/OperationLogs.vue b/src/views/OperationLogs.vue new file mode 100644 index 0000000..355bd0f --- /dev/null +++ b/src/views/OperationLogs.vue @@ -0,0 +1,159 @@ + + + diff --git a/src/views/Replay.vue b/src/views/Replay.vue new file mode 100644 index 0000000..37e3cf0 --- /dev/null +++ b/src/views/Replay.vue @@ -0,0 +1,127 @@ + + + diff --git a/src/views/System.vue b/src/views/System.vue new file mode 100644 index 0000000..e1a8bfb --- /dev/null +++ b/src/views/System.vue @@ -0,0 +1,88 @@ + + + diff --git a/src/views/Tasks.vue b/src/views/Tasks.vue new file mode 100644 index 0000000..630e487 --- /dev/null +++ b/src/views/Tasks.vue @@ -0,0 +1,582 @@ + + + diff --git a/src/views/Users.vue b/src/views/Users.vue new file mode 100644 index 0000000..54566dc --- /dev/null +++ b/src/views/Users.vue @@ -0,0 +1,126 @@ + + + diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..fcfb10b --- /dev/null +++ b/vite.config.js @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + server: { + host: '0.0.0.0', + port: 5173, + proxy: { + '/api': { + target: 'http://127.0.0.1:8080', + changeOrigin: true, + rewrite: (p) => p.replace(/^\/api/, '') + } + } + } +})