This commit is contained in:
feie9456 2025-08-17 16:43:13 +08:00
commit db749b97e3
17 changed files with 1076 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

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

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

5
README.md Normal file
View File

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

232
bun.lock Normal file
View File

@ -0,0 +1,232 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "mag-rl-optimization",
"dependencies": {
"@types/three": "^0.179.0",
"echarts": "^6.0.0",
"three": "^0.179.1",
"vue": "^3.5.18",
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.7.0",
"typescript": "~5.8.3",
"vite": "^7.1.2",
"vue-tsc": "^3.0.5",
},
},
},
"packages": {
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
"@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="],
"@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
"@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.8", "", { "os": "none", "cpu": "arm64" }, "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.8", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="],
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.8", "", { "os": "none", "cpu": "arm64" }, "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.29", "", {}, "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.46.2", "", { "os": "android", "cpu": "arm" }, "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.46.2", "", { "os": "android", "cpu": "arm64" }, "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.46.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.46.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.46.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.46.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.46.2", "", { "os": "linux", "cpu": "arm" }, "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.46.2", "", { "os": "linux", "cpu": "arm" }, "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.46.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.46.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.46.2", "", { "os": "linux", "cpu": "none" }, "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA=="],
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.46.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.46.2", "", { "os": "linux", "cpu": "none" }, "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.46.2", "", { "os": "linux", "cpu": "none" }, "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.46.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.46.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.46.2", "", { "os": "linux", "cpu": "x64" }, "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.46.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.46.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.46.2", "", { "os": "win32", "cpu": "x64" }, "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg=="],
"@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/stats.js": ["@types/stats.js@0.17.4", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
"@types/three": ["@types/three@0.179.0", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": "*", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" } }, "sha512-VgbFG2Pgsm84BqdegZzr7w2aKbQxmgzIu4Dy7/75ygiD/0P68LKmp5ie08KMPNqGTQwIge8s6D1guZf1RnZE0A=="],
"@types/webxr": ["@types/webxr@0.5.22", "", {}, "sha512-Vr6Stjv5jPRqH690f5I5GLjVk8GSsoQSYJ2FVd/3jJF7KaqfwPi3ehfBS96mlQ2kPCwZaX6U0rG2+NGHBKkA/A=="],
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.29" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue": "^3.2.25" } }, "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw=="],
"@volar/language-core": ["@volar/language-core@2.4.22", "", { "dependencies": { "@volar/source-map": "2.4.22" } }, "sha512-gp4M7Di5KgNyIyO903wTClYBavRt6UyFNpc5LWfyZr1lBsTUY+QrVZfmbNF2aCyfklBOVk9YC4p+zkwoyT7ECg=="],
"@volar/source-map": ["@volar/source-map@2.4.22", "", {}, "sha512-L2nVr/1vei0xKRgO2tYVXtJYd09HTRjaZi418e85Q+QdbbqA8h7bBjfNyPPSsjnrOO4l4kaAo78c8SQUAdHvgA=="],
"@volar/typescript": ["@volar/typescript@2.4.22", "", { "dependencies": { "@volar/language-core": "2.4.22", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-6ZczlJW1/GWTrNnkmZxJp4qyBt/SGVlcTuCWpI5zLrdPdCZsj66Aff9ZsfFaT3TyjG8zVYgBMYPuCm/eRkpcpQ=="],
"@vue/compiler-core": ["@vue/compiler-core@3.5.18", "", { "dependencies": { "@babel/parser": "^7.28.0", "@vue/shared": "3.5.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw=="],
"@vue/compiler-dom": ["@vue/compiler-dom@3.5.18", "", { "dependencies": { "@vue/compiler-core": "3.5.18", "@vue/shared": "3.5.18" } }, "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A=="],
"@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.18", "", { "dependencies": { "@babel/parser": "^7.28.0", "@vue/compiler-core": "3.5.18", "@vue/compiler-dom": "3.5.18", "@vue/compiler-ssr": "3.5.18", "@vue/shared": "3.5.18", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA=="],
"@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.18", "", { "dependencies": { "@vue/compiler-dom": "3.5.18", "@vue/shared": "3.5.18" } }, "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g=="],
"@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="],
"@vue/language-core": ["@vue/language-core@3.0.5", "", { "dependencies": { "@volar/language-core": "2.4.22", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^2.0.5", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-gCEjn9Ik7I/seHVNIEipOm8W+f3/kg60e8s1IgIkMYma2wu9ZGUTMv3mSL2bX+Md2L8fslceJ4SU8j1fgSRoiw=="],
"@vue/reactivity": ["@vue/reactivity@3.5.18", "", { "dependencies": { "@vue/shared": "3.5.18" } }, "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg=="],
"@vue/runtime-core": ["@vue/runtime-core@3.5.18", "", { "dependencies": { "@vue/reactivity": "3.5.18", "@vue/shared": "3.5.18" } }, "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w=="],
"@vue/runtime-dom": ["@vue/runtime-dom@3.5.18", "", { "dependencies": { "@vue/reactivity": "3.5.18", "@vue/runtime-core": "3.5.18", "@vue/shared": "3.5.18", "csstype": "^3.1.3" } }, "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw=="],
"@vue/server-renderer": ["@vue/server-renderer@3.5.18", "", { "dependencies": { "@vue/compiler-ssr": "3.5.18", "@vue/shared": "3.5.18" }, "peerDependencies": { "vue": "3.5.18" } }, "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA=="],
"@vue/shared": ["@vue/shared@3.5.18", "", {}, "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA=="],
"@vue/tsconfig": ["@vue/tsconfig@0.7.0", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg=="],
"@webgpu/types": ["@webgpu/types@0.1.64", "", {}, "sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A=="],
"alien-signals": ["alien-signals@2.0.6", "", {}, "sha512-P3TxJSe31bUHBiblg59oU1PpaWPtmxF9GhJ/cB7OkgJ0qN/ifFSKUI25/v8ZhsT+lIG6ac8DpTOplXxORX6F3Q=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="],
"echarts": ["echarts@6.0.0", "", { "dependencies": { "tslib": "2.3.0", "zrender": "6.0.0" } }, "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="],
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"meshoptimizer": ["meshoptimizer@0.22.0", "", {}, "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg=="],
"muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"rollup": ["rollup@4.46.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.46.2", "@rollup/rollup-android-arm64": "4.46.2", "@rollup/rollup-darwin-arm64": "4.46.2", "@rollup/rollup-darwin-x64": "4.46.2", "@rollup/rollup-freebsd-arm64": "4.46.2", "@rollup/rollup-freebsd-x64": "4.46.2", "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", "@rollup/rollup-linux-arm-musleabihf": "4.46.2", "@rollup/rollup-linux-arm64-gnu": "4.46.2", "@rollup/rollup-linux-arm64-musl": "4.46.2", "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", "@rollup/rollup-linux-ppc64-gnu": "4.46.2", "@rollup/rollup-linux-riscv64-gnu": "4.46.2", "@rollup/rollup-linux-riscv64-musl": "4.46.2", "@rollup/rollup-linux-s390x-gnu": "4.46.2", "@rollup/rollup-linux-x64-gnu": "4.46.2", "@rollup/rollup-linux-x64-musl": "4.46.2", "@rollup/rollup-win32-arm64-msvc": "4.46.2", "@rollup/rollup-win32-ia32-msvc": "4.46.2", "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"three": ["three@0.179.1", "", {}, "sha512-5y/elSIQbrvKOISxpwXCR4sQqHtGiOI+MKLc3SsBdDXA2hz3Mdp3X59aUp8DyybMa34aeBwbFTpdoLJaUDEWSw=="],
"tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"tslib": ["tslib@2.3.0", "", {}, "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"vite": ["vite@7.1.2", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ=="],
"vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
"vue": ["vue@3.5.18", "", { "dependencies": { "@vue/compiler-dom": "3.5.18", "@vue/compiler-sfc": "3.5.18", "@vue/runtime-dom": "3.5.18", "@vue/server-renderer": "3.5.18", "@vue/shared": "3.5.18" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA=="],
"vue-tsc": ["vue-tsc@3.0.5", "", { "dependencies": { "@volar/typescript": "2.4.22", "@vue/language-core": "3.0.5" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-PsTFN9lo1HJCrZw9NoqjYcAbYDXY0cOKyuW2E7naX5jcaVyWpqEsZOHN9Dws5890E8e5SDAD4L4Zam3dxG3/Cw=="],
"zrender": ["zrender@6.0.0", "", { "dependencies": { "tslib": "2.3.0" } }, "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg=="],
}
}

13
index.html Normal file
View File

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

24
package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "mag-rl-optimization",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@types/three": "^0.179.0",
"echarts": "^6.0.0",
"three": "^0.179.1",
"vue": "^3.5.18"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.7.0",
"typescript": "~5.8.3",
"vite": "^7.1.2",
"vue-tsc": "^3.0.5"
}
}

1
public/vite.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 1.5 KiB

26
src/App.vue Normal file
View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import ExperimentLiveDashboard from './components/ExperimentLiveDashboard.vue';
</script>
<template>
<div>
<ExperimentLiveDashboard />
</div>
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

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

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

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,671 @@
<template>
<div class="page">
<header class="header glass">
<div class="title">
<h1>EM Induction Lab Live Monitor</h1>
<p>气垫导轨 · 单线圈 · 可编程电源 · 实验状态实时演示</p>
</div>
<div class="badges">
<span class="pill" :class="statusClass">{{ statusText }}</span>
<span class="pill alt">Mode: {{ mode }}</span>
</div>
</header>
<section class="metrics-grid">
<div class="card glass metric">
<div class="label">当前回合</div>
<div class="value xl">{{ state.round }}</div>
</div>
<div class="card glass metric">
<div class="label">回合时间</div>
<div class="value">{{ formattedTime }}</div>
</div>
<div class="card glass metric">
<div class="label">末速度 v_out</div>
<div class="value">{{ fmt(state.metrics.v_out) }} m/s</div>
</div>
<div class="card glass metric">
<div class="label">能耗 E</div>
<div class="value">{{ fmt(state.metrics.energy) }} J</div>
</div>
<div class="card glass metric">
<div class="label">峰值电流</div>
<div class="value">{{ fmt(state.metrics.I_pk) }} A</div>
</div>
<div class="card glass metric">
<div class="label">线圈温度</div>
<div class="value">{{ state.metrics.temp.toFixed(1) }} </div>
</div>
<div class="card glass metric">
<div class="label">本回合得分</div>
<div class="value">{{ state.metrics.score.toFixed(2) }}</div>
</div>
<div class="card glass metric">
<div class="label">脉冲参数</div>
<div class="value">
δ={{ (state.params.delta*1000).toFixed(1) }}ms ·
τ={{ (state.params.tau*1000).toFixed(1) }}ms ·
A={{ (state.params.amp*100).toFixed(0) }}%
</div>
</div>
</section>
<section class="main-grid">
<div class="card glass three-wrap">
<div class="card-title">装置实时 3D 状态</div>
<canvas ref="threeCanvas" class="three"></canvas>
<div class="legend">
<span class="dot coil"></span>线圈
<span class="dot cart"></span>滑块
<span class="dot track"></span>导轨
</div>
</div>
<div class="card glass chart" ref="chartVIP">
<div class="card-title">实时电压 / 电流 / 功率</div>
<div class="sub">{{ nowStr }}</div>
<div class="chart-host"></div>
</div>
<div class="card glass chart" ref="chartEnergy">
<div class="card-title">累计能量积分</div>
<div class="chart-host"></div>
</div>
<div class="card glass chart" ref="chartXV">
<div class="card-title">实时位置 / 速度</div>
<div class="chart-host"></div>
</div>
<div class="card glass chart" ref="chartScores">
<div class="card-title">回合表现得分v_outI_pk</div>
<div class="chart-host"></div>
</div>
<div class="card glass chart" ref="chartParams">
<div class="card-title">参数历史δ/τ/A</div>
<div class="chart-host"></div>
</div>
<div class="card glass side">
<div class="card-title">控制</div>
<div class="controls">
<button class="btn" @click="toggleRun">
{{ running ? '终止实验' : '继续实验' }}
</button>
<button class="btn alt" @click="nextRound">进入下一回合</button>
</div>
<div class="divider"></div>
<div class="card-title">阈值 / 设定</div>
<ul class="kv">
<li><span>I 限流</span><b>{{ limits.I_max }} A</b></li>
<li><span>温度上限</span><b>{{ limits.T_max }} </b></li>
<li><span>脉冲最宽</span><b>{{ (limits.tau_max*1000).toFixed(0) }} ms</b></li>
<li><span>采样周期</span><b>{{ (dt*1000).toFixed(0) }} ms</b></li>
</ul>
<div class="divider"></div>
<div class="card-title">最近事件</div>
<ul class="log">
<li v-for="(e,i) in events.slice(-6).reverse()" :key="i">
<span class="ts">{{ e.t }}</span>
<span :class="['tag', e.type]">{{ e.type }}</span>
<span class="msg">{{ e.msg }}</span>
</li>
</ul>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, reactive, computed } from 'vue'
import * as echarts from 'echarts'
import * as THREE from 'three'
// ---------- ----------
const running = ref(true)
const mode = ref<'ACCEL' | 'BRAKE'>('ACCEL')
const dt = 0.02 //
const state = reactive({
round: 1,
t: 0, //
params: {
delta: 0.010, // s
tau: 0.018, // s
amp: 0.7 // 0..1
},
metrics: {
v_out: 0,
energy: 0,
I_pk: 0,
temp: 31.2,
score: 0
}
})
const limits = reactive({
I_max: 4.0,
T_max: 70,
tau_max: 0.050
})
const events = reactive<{t:string,type:string,msg:string}[]>([])
const statusText = computed(()=> running.value ? 'RUNNING' : 'PAUSED')
const statusClass = computed(()=> running.value ? 'ok' : 'warn')
const formattedTime = computed(()=>{
const s = Math.floor(state.t)
const ms = Math.floor((state.t - s)*1000)
return `${String(s).padStart(2,'0')}.${String(ms).padStart(3,'0')} s`
})
const nowStr = computed(()=>{
const d = new Date()
const pad = (n:number,l=2)=>String(n).padStart(l,'0')
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
})
const fmt = (v:number)=> v>=10? v.toFixed(2) : v>=1? v.toFixed(3) : v.toFixed(4)
// ---------- three.js ----------
const threeCanvas = ref<HTMLCanvasElement|null>(null)
let renderer: THREE.WebGLRenderer | null = null
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let cart: THREE.Mesh
let coil: THREE.Mesh
let rafId = 0
function initThree() {
if (!threeCanvas.value) return
// DOM
setTimeout(() => {
if (!threeCanvas.value) return
const rect = threeCanvas.value.getBoundingClientRect()
const w = rect.width
const h = rect.height
scene = new THREE.Scene()
scene.background = new THREE.Color('#0a0f1b')
camera = new THREE.PerspectiveCamera(45, w/h, 0.1, 100)
camera.position.set(2.8, 1.6, 3.2)
camera.lookAt(0,0,0)
renderer = new THREE.WebGLRenderer({ canvas: threeCanvas.value, antialias: true })
renderer.setSize(w, h, false) // false canvas
renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1))
const light = new THREE.DirectionalLight('#ffffff', 1.0)
light.position.set(2,3,4)
scene.add(light)
scene.add(new THREE.AmbientLight('#88a', 0.4))
//
const trackGeo = new THREE.BoxGeometry(3.0, 0.05, 0.25)
const trackMat = new THREE.MeshStandardMaterial({ color: '#4a6fa5', metalness: 0.5, roughness: 0.4 })
const track = new THREE.Mesh(trackGeo, trackMat)
track.position.set(0, -0.025, 0)
scene.add(track)
// 线
const coilGeo = new THREE.TorusGeometry(0.18, 0.04, 16, 64)
const coilMat = new THREE.MeshStandardMaterial({ color: '#00eaff', emissive: '#003344', emissiveIntensity: 0.8, metalness: 0.6, roughness: 0.3 })
coil = new THREE.Mesh(coilGeo, coilMat)
coil.rotation.x = 0 //
coil.rotation.z = Math.PI / 2 //
coil.rotation.y = Math.PI / 2 //
scene.add(coil)
//
const cartGeo = new THREE.BoxGeometry(0.18, 0.08, 0.18)
const cartMat = new THREE.MeshStandardMaterial({ color: '#ffd166', metalness: 0.4, roughness: 0.5, emissive: '#664400', emissiveIntensity: 0.3 })
cart = new THREE.Mesh(cartGeo, cartMat)
cart.position.set(-1.2, 0.04, 0)
scene.add(cart)
//
const grid = new THREE.GridHelper(10, 20, '#224466', '#112233')
grid.position.y = -0.025
scene.add(grid)
const animate = ()=>{
// -1.2 -> 1.2
const x = -1.2 + 2.4 * normPos // normPos in [0..1]
cart.position.x = x
// 线
const em = 0.3 + 0.7*Math.min(1, lastI/limits.I_max)
;(coil.material as THREE.MeshStandardMaterial).emissiveIntensity = em
renderer!.render(scene, camera)
rafId = requestAnimationFrame(animate)
}
animate()
window.addEventListener('resize', onResize)
function onResize() {
if (!renderer || !threeCanvas.value) return
const rect = threeCanvas.value.getBoundingClientRect()
const w = rect.width
const h = rect.height
camera.aspect = w/h
camera.updateProjectionMatrix()
renderer.setSize(w, h, false) // false
}
}, 100) // 100ms DOM
}
// ---------- ----------
const chartVIP = ref<HTMLDivElement|null>(null)
const chartEnergy = ref<HTMLDivElement|null>(null)
const chartXV = ref<HTMLDivElement|null>(null)
const chartScores = ref<HTMLDivElement|null>(null)
const chartParams = ref<HTMLDivElement|null>(null)
let vipChart: echarts.ECharts
let energyChart: echarts.ECharts
let xvChart: echarts.ECharts
let scoresChart: echarts.ECharts
let paramsChart: echarts.ECharts
//
const maxPts = 120
const series = reactive({
t: [] as number[],
U: [] as number[],
I: [] as number[],
P: [] as number[],
E: [] as number[],
x: [] as number[],
v: [] as number[],
})
const history = reactive({
rounds: [] as number[],
score: [] as number[],
vout: [] as number[],
Ipk: [] as number[],
delta: [] as number[],
tau: [] as number[],
amp: [] as number[],
})
function initCharts(){
//
const base = {
backgroundColor: 'transparent',
textStyle: { color: '#cfe5ff' },
grid: { left: 48, right: 20, top: 40, bottom: 40 },
tooltip: { trigger: 'axis' },
animation: false
}
// VIP
vipChart = echarts.init(chartVIP.value!.querySelector('.chart-host') as HTMLDivElement)
vipChart.setOption({
...base,
legend: { data: ['U (V)','I (A)','P (W)'], top: 4, textStyle: { color: '#a9c3ff' } },
xAxis: { type:'category', boundaryGap:false, axisLine:{lineStyle:{color:'#335'}}, axisLabel:{color:'#89a'}},
yAxis: [
{ type:'value', name:'U/I', position:'left', axisLabel:{color:'#89a'} },
{ type:'value', name:'P', position:'right', axisLabel:{color:'#89a'} }
],
series: [
{ name:'U (V)', type:'line', smooth:true, showSymbol:false, data:[] },
{ name:'I (A)', type:'line', smooth:true, showSymbol:false, data:[] },
{ name:'P (W)', type:'line', smooth:true, yAxisIndex:1, showSymbol:false, areaStyle:{}, data:[] },
]
})
// Energy
energyChart = echarts.init(chartEnergy.value!.querySelector('.chart-host') as HTMLDivElement)
energyChart.setOption({
...base,
xAxis: { type:'category', boundaryGap:false, axisLabel:{color:'#89a'} },
yAxis: { type:'value', name:'E (J)', axisLabel:{color:'#89a'} },
series: [{ type:'line', smooth:true, showSymbol:false, areaStyle:{}, data:[] }]
})
// XV
xvChart = echarts.init(chartXV.value!.querySelector('.chart-host') as HTMLDivElement)
xvChart.setOption({
...base,
legend: { data:['x (m)','v (m/s)'], top: 4, textStyle: { color:'#a9c3ff' } },
xAxis: { type:'category', boundaryGap:false, axisLabel:{color:'#89a'} },
yAxis: [
{ type:'value', name:'x', position:'left', axisLabel:{color:'#89a'} },
{ type:'value', name:'v', position:'right', axisLabel:{color:'#89a'} }
],
series: [
{ name:'x (m)', type:'line', smooth:true, showSymbol:false, data:[] },
{ name:'v (m/s)', type:'line', smooth:true, showSymbol:false, yAxisIndex:1, data:[] },
]
})
// Scores
scoresChart = echarts.init(chartScores.value!.querySelector('.chart-host') as HTMLDivElement)
scoresChart.setOption({
...base,
xAxis: { type:'category', axisLabel:{color:'#89a'}, data:[] },
yAxis: [{ type:'value', axisLabel:{color:'#89a'}, name:'Score / v_out / I_pk'}],
legend: { data:['Score','v_out','I_pk'], top: 4, textStyle: { color:'#a9c3ff' } },
series: [
{ name:'Score', type:'bar', data:[], itemStyle:{opacity:0.9}},
{ name:'v_out', type:'line', smooth:true, showSymbol:true, data:[] },
{ name:'I_pk', type:'line', smooth:true, showSymbol:true, data:[] },
]
})
// Params
paramsChart = echarts.init(chartParams.value!.querySelector('.chart-host') as HTMLDivElement)
paramsChart.setOption({
...base,
legend: { data:['δ(ms)','τ(ms)','A(%)'], top: 4, textStyle: { color:'#a9c3ff' } },
xAxis: { type:'category', axisLabel:{color:'#89a'}, data:[] },
yAxis: { type:'value', axisLabel:{color:'#89a'} },
series: [
{ name:'δ(ms)', type:'line', smooth:true, showSymbol:true, data:[] },
{ name:'τ(ms)', type:'line', smooth:true, showSymbol:true, data:[] },
{ name:'A(%)', type:'line', smooth:true, showSymbol:true, data:[] },
]
})
}
function updateCharts(){
// 使 X
const timeLabels = series.t.map(t => t.toFixed(1))
vipChart.setOption({
xAxis: { data: timeLabels },
series: [
{ data: series.U },
{ data: series.I },
{ data: series.P },
]
})
energyChart.setOption({
xAxis: { data: timeLabels },
series: [{ data: series.E }]
})
xvChart.setOption({
xAxis: { data: timeLabels },
series: [
{ data: series.x },
{ data: series.v }
]
})
// 使 X
scoresChart.setOption({
xAxis: { data: history.rounds.map(r=>`#${r}`) },
series: [
{ data: history.score },
{ data: history.vout },
{ data: history.Ipk },
]
})
paramsChart.setOption({
xAxis: { data: history.rounds.map(r=>`#${r}`) },
series: [
{ data: history.delta.map(d=> (d*1000).toFixed(1)) },
{ data: history.tau.map(d=> (d*1000).toFixed(1)) },
{ data: history.amp.map(a=> (a*100).toFixed(0)) },
]
})
}
// ---------- ----------
let ticker: number | null = null
let roundTimer: number | null = null
let lastI = 0
let normPos = 0 // 0..1 for three.js
function resetSeries(){
series.t.length = 0
series.U.length = 0
series.I.length = 0
series.P.length = 0
series.E.length = 0
series.x.length = 0
series.v.length = 0
state.t = 0
normPos = 0
}
function clamp(n:number,min:number,max:number){ return Math.max(min, Math.min(max, n)) }
function pushDataPoint(){
// 穿线
state.t += dt
const t = state.t
const { delta, tau, amp } = state.params
// /
const total = 1.6 // s
const progress = clamp(t/total, 0, 1)
const x = 1.2*progress // 0..1.2m
const v = 1.2/total + 0.4*Math.sin(progress*Math.PI) // m/s
normPos = progress
// /
const pulseCenter = 0.8 // s
const t0 = pulseCenter - delta
const inPulse = t>=t0 && t<=t0+tau
const U = inPulse ? 8*amp + 0.2*Math.sin(t*20) : 0.1+0.05*Math.sin(t*6)
const I = inPulse ? clamp(2.5*amp + 0.5*Math.sin(t*30), 0, limits.I_max) : 0.05+0.02*Math.sin(t*10)
const P = U*I
lastI = I
const E = ((series.E.length ? series.E[series.E.length - 1] : 0) || 0) + P*dt
series.t.push(t)
series.U.push(Number(U.toFixed(3)))
series.I.push(Number(I.toFixed(3)))
series.P.push(Number(P.toFixed(3)))
series.E.push(Number(E.toFixed(3)))
series.x.push(Number((x).toFixed(3)))
series.v.push(Number((v).toFixed(3)))
if (series.t.length>maxPts){
Object.values(series).forEach(arr => (arr as number[]).shift())
}
// metrics
state.metrics.energy = E
state.metrics.I_pk = Math.max(state.metrics.I_pk, I)
state.metrics.v_out = v
state.metrics.temp = clamp(state.metrics.temp + (inPulse? 0.05: -0.02), 25, 80)
// /
if (I > limits.I_max*0.95){
events.push({t: nowStr.value, type:'warn', msg:`接近限流:${I.toFixed(2)} A`})
}
}
function finishRound(){
//
const score = state.metrics.v_out - 0.4*state.metrics.energy - 0.2*state.metrics.I_pk - 0.02*(state.metrics.temp-25)
state.metrics.score = Number(score.toFixed(3))
history.rounds.push(state.round)
history.score.push(Number(state.metrics.score.toFixed(3)))
history.vout.push(Number(state.metrics.v_out.toFixed(3)))
history.Ipk.push(Number(state.metrics.I_pk.toFixed(3)))
history.delta.push(state.params.delta)
history.tau.push(state.params.tau)
history.amp.push(state.params.amp)
//
state.round += 1
state.params.delta = clamp(state.params.delta + (Math.random()-0.5)*0.004, 0.004, 0.020)
state.params.tau = clamp(state.params.tau + (Math.random()-0.5)*0.006, 0.008, limits.tau_max)
state.params.amp = clamp(state.params.amp + (Math.random()-0.5)*0.08, 0.25, 1.0)
events.push({t: nowStr.value, type:'info', msg:`回合结束Score=${state.metrics.score.toFixed(2)} v_out=${state.metrics.v_out.toFixed(2)} Ipk=${state.metrics.I_pk.toFixed(2)}`})
//
resetSeries()
state.metrics.I_pk = 0
state.metrics.energy = 0
}
function startDemo(){
if (ticker) return
resetSeries()
ticker = window.setInterval(()=>{
if (!running.value) return
pushDataPoint()
updateCharts()
// 1.6s
if (state.t >= 1.6){
finishRound()
updateCharts()
}
}, dt*1000)
// 5
if (!roundTimer){
roundTimer = window.setInterval(()=>{
events.push({t: nowStr.value, type:'sys', msg:'系统检查通过'})
if (events.length>100) events.splice(0, events.length-100)
}, 5000)
}
}
function stopDemo(){
if (ticker){ clearInterval(ticker); ticker = null }
if (roundTimer){ clearInterval(roundTimer); roundTimer = null }
}
function toggleRun(){ running.value = !running.value }
function nextRound(){ if (running.value){ finishRound(); updateCharts() } }
onMounted(()=>{
initThree()
initCharts()
startDemo()
})
onBeforeUnmount(()=>{
stopDemo()
if (renderer){ renderer.dispose(); renderer = null }
cancelAnimationFrame(rafId)
})
</script>
<style scoped>
* { box-sizing: border-box; }
.page{
min-height: 100vh;
background:
radial-gradient(1200px 600px at 10% -10%, rgba(0,234,255,0.1), transparent),
radial-gradient(1200px 600px at 90% 110%, rgba(255,209,102,0.08), transparent),
linear-gradient(180deg, #050914 0%, #090f1e 100%);
color: var(--text);
padding: 24px;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial;
}
.header{
display:flex; align-items:center; justify-content:space-between;
padding:16px 20px; border:1px solid var(--border); border-radius:16px; margin-bottom: 18px;
}
.header .title h1{ margin:0; letter-spacing:0.4px; font-weight:700; }
.header .title p{ margin:4px 0 0; color:var(--muted); font-size:13px; }
.badges{ display:flex; gap:10px; }
.pill{
padding:6px 12px; border-radius:999px; font-size:12px;
background: linear-gradient(180deg, rgba(0,234,255,0.2), rgba(0,234,255,0.06));
color:#baf6ff; border:1px solid rgba(0,234,255,0.25);
text-shadow: 0 0 8px rgba(0,234,255,0.4);
}
.pill.ok{ background: linear-gradient(180deg, rgba(16,212,138,0.25), rgba(16,212,138,0.07));
color:#c9ffe9; border-color: rgba(16,212,138,0.35); text-shadow:0 0 8px rgba(16,212,138,0.4);}
.pill.warn{ background: linear-gradient(180deg, rgba(255,157,87,0.25), rgba(255,157,87,0.07));
color:#ffe6d2; border-color: rgba(255,157,87,0.35); text-shadow:0 0 8px rgba(255,157,87,0.4);}
.pill.alt{ background: linear-gradient(180deg, rgba(140,160,255,0.18), rgba(140,160,255,0.06)); color:#e0e6ff; border-color:rgba(140,160,255,0.3); }
.metrics-grid{
display:grid; gap:12px; grid-template-columns: repeat(8, minmax(0,1fr)); margin-bottom: 14px;
}
.metric .label{ color: var(--muted); font-size:12px; }
.metric .value{ font-size:18px; font-weight:700; margin-top:6px; }
.metric .value.xl{ font-size:28px; letter-spacing: 0.5px; }
.main-grid{
display:grid; grid-template-columns: 1.6fr 1.6fr 1.6fr 1fr; grid-auto-rows: 320px; gap:14px;
}
.card{
position:relative; border:1px solid var(--border); border-radius:16px; padding:12px;
}
.glass{
background: var(--card);
backdrop-filter: blur(8px) saturate(120%);
box-shadow: 0 12px 40px rgba(0,0,0,0.25), inset 0 0 0 1px rgba(255,255,255,0.02);
}
.card-title{
font-weight:700; color:#e6f2ff; margin:4px 2px 10px; letter-spacing:0.3px;
}
.chart .chart-host{ position:absolute; inset: 48px 8px 8px 8px; }
.sub{ position:absolute; right:14px; top:10px; color:#8fb3ff; font-size:12px; }
.three-wrap{
grid-column: span 2;
position: relative;
overflow: hidden;
}
.three{
width: 100%;
height: calc(100% - 40px); /* 减去卡片标题和内边距 */
max-width: 100%;
max-height: calc(100% - 40px);
border-radius: 12px;
outline: 1px solid rgba(0,234,255,0.08);
display: block;
}
.legend{
position:absolute; left:12px; bottom:10px; display:flex; gap:14px; align-items:center; font-size:12px; color:#a9c3ff;
}
.legend .dot{ width:10px; height:10px; border-radius:50%; display:inline-block; margin-right:6px; }
.legend .dot.coil{ background:#00eaff; box-shadow:0 0 10px #00eaff; }
.legend .dot.cart{ background:#ffd166; box-shadow:0 0 10px #ffd166; }
.legend .dot.track{ background:#1b2a4a; }
.side{ grid-row: span 2; display:flex; flex-direction:column; }
.controls{ display:flex; gap:8px; }
.btn{
background: linear-gradient(180deg, rgba(0,234,255,0.2), rgba(0,109,169,0.2));
color:#dff8ff; border:1px solid rgba(0,234,255,0.35); padding:8px 12px; border-radius:10px;
cursor:pointer; font-weight:700; letter-spacing:0.2px;
}
.btn:hover{ filter: brightness(1.1); }
.btn.alt{ background: linear-gradient(180deg, rgba(255,209,102,0.25), rgba(255,145,0,0.18)); border-color: rgba(255,209,102,0.45); color:#1b1200; }
.divider{ height:1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.12), transparent); margin:12px 0; }
.kv{ list-style:none; padding:0; margin:6px 0 0; }
.kv li{ display:flex; justify-content:space-between; padding:6px 2px; color:var(--muted); }
.kv b{ color:var(--text); }
.log{ list-style:none; padding:0; margin:8px 0 0; display:flex; flex-direction:column; gap:8px; max-height: 180px; overflow:auto; }
.log .ts{ color:#89a; font-size:12px; margin-right:6px; }
.log .msg{ color:#ddecff; }
.tag{
font-size:11px; padding:2px 8px; border-radius:999px; margin-right:6px;
background: rgba(140,160,255,0.18); border:1px solid rgba(140,160,255,0.3); color:#dee5ff;
}
.tag.warn{ background: rgba(255,157,87,0.2); border-color: rgba(255,157,87,0.35); color:#fff0e5;}
.tag.info{ background: rgba(0,234,255,0.18); border-color: rgba(0,234,255,0.35); color:#e2fbff;}
.tag.sys{ background: rgba(16,212,138,0.2); border-color: rgba(16,212,138,0.35); color:#e5fff6;}
.footer{ text-align:center; color:#7fa5ff; margin-top: 16px; font-size:12px; }
/* 响应式 */
@media (max-width: 1200px){
.main-grid{ grid-template-columns: 1fr 1fr; grid-auto-rows: 300px; }
.three-wrap{ grid-column: span 2; }
.side{ grid-row: auto; }
.metrics-grid{ grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 700px){
.metrics-grid{ grid-template-columns: repeat(2, 1fr); }
.main-grid{ grid-template-columns: 1fr; }
.three-wrap{ grid-column: span 1; }
}
</style>

5
src/main.ts Normal file
View File

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

16
src/style.css Normal file
View File

@ -0,0 +1,16 @@
:root{
--bg: #060a14;
--card: rgba(16,24,44,0.6);
--border: rgba(255,255,255,0.08);
--text: #dce9ff;
--muted: #9ab0d6;
--accent: #00eaff;
--accent2: #ffd166;
--ok: #10d48a;
--warn: #ff9d57;
}
body{
margin: 0;
}

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

15
tsconfig.app.json Normal file
View File

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

7
tsconfig.json Normal file
View File

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

25
tsconfig.node.json Normal file
View File

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

7
vite.config.ts Normal file
View File

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