A:增加真实网速展示

This commit is contained in:
jimmy committed 2025-08-08 13:35:47 +08:00
1 parent bc2a826008
commit 2b2a5eca98
10 files changed
+884 -206

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
# 项目介绍
这是一个网络管理项目,它用的是Vue进行展示的,集成防火墙API,提供HTTP和WebSocket接口来实现数据的传递和接收。使用Vue作为前端并通过WebView2套壳实现主页面渲染
我所有的环境并非全部在默认位置安装,比如部分环境如 Git 之类的在 D:\Env 下
你可以使用TODO.md 文档来管理你的任务,或者记录任何东西,里面的内容完全由你管理,我不会干预,如果有内容你可以直接清空,它就像你的草稿纸。
-1
View File
@@ -84,7 +84,6 @@ npm run lint
对于单个软件网速的记录采用 可执行文件的路径作为ID进行数据存储,第二个字段为时间,其他的分别为:上传总量、下载总量、网卡、时间戳、
于此同时也可以记录网络连接的信息:时间戳 本地IP 本地端口 远程IP 远程端口 协议 上传字节 下载字节 可执行文件路径。这些连接信息每分钟只保存流量最多的100条(后期开放用户可定义数量,
但是不能超过100)。该信息最多只保存1天,超过1天就合并删除。超过1周的数据再次聚会删除。超过1个月再次处理。
你帮我估算一个,如果按照这样的策略,数据库需要考虑分库分表嘛?它的健壮性如何?还有什么问题需要考虑。
软件信息的记录:
名字、路径、版本、公司、ICON等
View File
Whitespace-only changes.
+2
View File
@@ -16,6 +16,7 @@
"dependencies": {
"@vicons/ionicons5": "^0.13.0",
"axios": "^1.10.0",
"echarts": "^6.0.0",
"gsap": "^3.13.0",
"lodash": "^4.17.21",
"lucide": "^0.525.0",
@@ -26,6 +27,7 @@
"unplugin-vue-components": "^28.7.0",
"vicons": "^0.0.1",
"vue": "^3.5.13",
"vue-echarts": "^7.0.3",
"vue-router": "^4.5.0",
"vue-virtual-scroller": "2.0.0-beta.8"
},
+114 -64
View File
@@ -14,6 +14,9 @@ importers:
axios:
specifier: ^1.10.0
version: 1.10.0
echarts:
specifier: ^6.0.0
version: 6.0.0
gsap:
specifier: ^3.13.0
version: 3.13.0
@@ -44,6 +47,9 @@ importers:
vue:
specifier: ^3.5.13
version: 3.5.14(typescript@5.8.3)
vue-echarts:
specifier: ^7.0.3
version: 7.0.3(@vue/runtime-core@3.5.14)(echarts@6.0.0)(vue@3.5.14(typescript@5.8.3))
vue-router:
specifier: ^4.5.0
version: 4.5.1(vue@3.5.14(typescript@5.8.3))
@@ -323,151 +329,151 @@ packages:
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
'@esbuild/aix-ppc64@0.25.4':
resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==, tarball: https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz}
resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.25.4':
resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==, tarball: https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz}
resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.25.4':
resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==, tarball: https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.4.tgz}
resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.25.4':
resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==, tarball: https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.4.tgz}
resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.25.4':
resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==, tarball: https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz}
resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.25.4':
resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==, tarball: https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz}
resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.25.4':
resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz}
resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.4':
resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz}
resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.25.4':
resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz}
resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.25.4':
resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz}
resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.25.4':
resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==, tarball: https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz}
resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.25.4':
resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==, tarball: https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz}
resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.25.4':
resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==, tarball: https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz}
resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.25.4':
resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==, tarball: https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz}
resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.25.4':
resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==, tarball: https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz}
resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.25.4':
resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==, tarball: https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz}
resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.25.4':
resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==, tarball: https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz}
resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.25.4':
resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==, tarball: https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz}
resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.4':
resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==, tarball: https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz}
resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.25.4':
resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==, tarball: https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz}
resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.4':
resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==, tarball: https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz}
resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/sunos-x64@0.25.4':
resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==, tarball: https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz}
resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.25.4':
resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==, tarball: https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz}
resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.25.4':
resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==, tarball: https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz}
resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.25.4':
resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==, tarball: https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz}
resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
@@ -571,7 +577,7 @@ packages:
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==, tarball: https://registry.npmmirror.com/@one-ini/wasm/-/wasm-0.1.1.tgz}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, tarball: https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz}
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@pkgr/core@0.2.4':
@@ -591,113 +597,102 @@ packages:
optional: true
'@rollup/rollup-android-arm-eabi@4.40.2':
resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==, tarball: https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz}
resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.40.2':
resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==, tarball: https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz}
resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.40.2':
resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz}
resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.40.2':
resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz}
resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.40.2':
resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz}
resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.40.2':
resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==, tarball: https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz}
resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.40.2':
resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz}
resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.40.2':
resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz}
resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.40.2':
resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz}
resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.40.2':
resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz}
resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loongarch64-gnu@4.40.2':
resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz}
resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-powerpc64le-gnu@4.40.2':
resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz}
resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.40.2':
resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz}
resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.40.2':
resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz}
resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.40.2':
resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz}
resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.40.2':
resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz}
resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.40.2':
resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz}
resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.40.2':
resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz}
resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.40.2':
resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz}
resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.40.2':
resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz}
resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==}
cpu: [x64]
os: [win32]
@@ -991,7 +986,7 @@ packages:
resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==, tarball: https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.13.tgz}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, tarball: https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz}
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-regex@6.1.0:
@@ -1202,6 +1197,9 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, tarball: https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz}
echarts@6.0.0:
resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==}
editorconfig@1.0.4:
resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==, tarball: https://registry.npmmirror.com/editorconfig/-/editorconfig-1.0.4.tgz}
engines: {node: '>=14'}
@@ -1211,7 +1209,7 @@ packages:
resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==, tarball: https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, tarball: https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz}
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, tarball: https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz}
@@ -1424,7 +1422,7 @@ packages:
engines: {node: '>=14.14'}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz}
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
@@ -1560,7 +1558,7 @@ packages:
engines: {node: '>=0.10.0'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, tarball: https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz}
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-glob@4.0.3:
@@ -2049,7 +2047,7 @@ packages:
resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==, tarball: https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, tarball: https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz}
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
string-width@5.1.2:
@@ -2057,7 +2055,7 @@ packages:
engines: {node: '>=12'}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, tarball: https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz}
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-ansi@7.1.0:
@@ -2148,6 +2146,9 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
tslib@2.3.0:
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, tarball: https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz}
engines: {node: '>= 0.8.0'}
@@ -2345,6 +2346,27 @@ packages:
vue-component-type-helpers@2.2.10:
resolution: {integrity: sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==, tarball: https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.10.tgz}
vue-demi@0.13.11:
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
engines: {node: '>=12'}
hasBin: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
vue-echarts@7.0.3:
resolution: {integrity: sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==}
peerDependencies:
'@vue/runtime-core': ^3.0.0
echarts: ^5.5.1
vue: ^2.7.0 || ^3.1.1
peerDependenciesMeta:
'@vue/runtime-core':
optional: true
vue-eslint-parser@10.1.3:
resolution: {integrity: sha512-dbCBnd2e02dYWsXoqX5yKUZlOt+ExIpq7hmHKPb5ZqKcjf++Eo0hMseFTZMLKThrUk61m+Uv6A2YSBve6ZvuDQ==, tarball: https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.1.3.tgz}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -2433,7 +2455,7 @@ packages:
engines: {node: '>=0.10.0'}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, tarball: https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz}
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrap-ansi@8.1.0:
@@ -2474,6 +2496,9 @@ packages:
resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==, tarball: https://registry.npmmirror.com/yoctocolors/-/yoctocolors-2.1.1.tgz}
engines: {node: '>=18'}
zrender@6.0.0:
resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==}
snapshots:
'@ampproject/remapping@2.3.0':
@@ -3540,6 +3565,11 @@ snapshots:
eastasianwidth@0.2.0: {}
echarts@6.0.0:
dependencies:
tslib: 2.3.0
zrender: 6.0.0
editorconfig@1.0.4:
dependencies:
'@one-ini/wasm': 0.1.1
@@ -4470,6 +4500,8 @@ snapshots:
dependencies:
typescript: 5.8.3
tslib@2.3.0: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@@ -4704,6 +4736,20 @@ snapshots:
vue-component-type-helpers@2.2.10: {}
vue-demi@0.13.11(vue@3.5.14(typescript@5.8.3)):
dependencies:
vue: 3.5.14(typescript@5.8.3)
vue-echarts@7.0.3(@vue/runtime-core@3.5.14)(echarts@6.0.0)(vue@3.5.14(typescript@5.8.3)):
dependencies:
echarts: 6.0.0
vue: 3.5.14(typescript@5.8.3)
vue-demi: 0.13.11(vue@3.5.14(typescript@5.8.3))
optionalDependencies:
'@vue/runtime-core': 3.5.14
transitivePeerDependencies:
- '@vue/composition-api'
vue-eslint-parser@10.1.3(eslint@9.27.0(jiti@2.4.2)):
dependencies:
debug: 4.4.1
@@ -4823,3 +4869,7 @@ snapshots:
yocto-queue@0.1.0: {}
yoctocolors@2.1.1: {}
zrender@6.0.0:
dependencies:
tslib: 2.3.0
@@ -0,0 +1,140 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export interface TrafficDataPoint {
timestamp: number
uploadSpeed: number
downloadSpeed: number
}
export const useTrafficStore = defineStore('traffic', () => {
// 存储实时网络流量数据
const trafficHistory = ref<TrafficDataPoint[]>([])
// 最大数据点数量(默认60个点,约20分钟的数据)
const maxDataPoints = ref(60)
// 数据收集间隔(毫秒)
const dataInterval = ref(1000) // 20秒
// 添加新的流量数据点
const addTrafficData = (uploadSpeed: number, downloadSpeed: number) => {
const now = Date.now()
const newDataPoint: TrafficDataPoint = {
timestamp: now,
uploadSpeed,
downloadSpeed
}
trafficHistory.value.push(newDataPoint)
// 限制数据点数量,删除过期数据
if (trafficHistory.value.length > maxDataPoints.value) {
trafficHistory.value.shift()
}
// 可选:基于时间限制删除老数据(20-30分钟)
const timeLimit = 30 * 60 * 1000 // 30分钟
const cutoffTime = now - timeLimit
trafficHistory.value = trafficHistory.value.filter(
point => point.timestamp > cutoffTime
)
}
// 获取当前上传速度
const currentUploadSpeed = computed(() => {
const latest = trafficHistory.value[trafficHistory.value.length - 1]
return latest?.uploadSpeed || 0
})
// 获取当前下载速度
const currentDownloadSpeed = computed(() => {
const latest = trafficHistory.value[trafficHistory.value.length - 1]
return latest?.downloadSpeed || 0
})
// 获取总的当前速度
const currentTotalSpeed = computed(() => {
return currentUploadSpeed.value + currentDownloadSpeed.value
})
// 清空历史数据
const clearHistory = () => {
trafficHistory.value = []
}
// 设置最大数据点数量
const setMaxDataPoints = (max: number) => {
maxDataPoints.value = max
// 如果当前数据超过新的最大值,删除老数据
if (trafficHistory.value.length > max) {
trafficHistory.value = trafficHistory.value.slice(-max)
}
}
// 获取指定时间范围内的数据
const getDataInRange = (minutes: number): TrafficDataPoint[] => {
const now = Date.now()
const cutoffTime = now - (minutes * 60 * 1000)
return trafficHistory.value.filter(point => point.timestamp > cutoffTime)
}
// 获取平均速度(最近N个数据点)
const getAverageSpeed = (lastNPoints?: number): { upload: number; download: number } => {
const dataToAnalyze = lastNPoints
? trafficHistory.value.slice(-lastNPoints)
: trafficHistory.value
if (dataToAnalyze.length === 0) {
return { upload: 0, download: 0 }
}
const totalUpload = dataToAnalyze.reduce((sum, point) => sum + point.uploadSpeed, 0)
const totalDownload = dataToAnalyze.reduce((sum, point) => sum + point.downloadSpeed, 0)
return {
upload: totalUpload / dataToAnalyze.length,
download: totalDownload / dataToAnalyze.length
}
}
// 获取峰值速度
const getPeakSpeed = (): { upload: number; download: number; total: number } => {
if (trafficHistory.value.length === 0) {
return { upload: 0, download: 0, total: 0 }
}
const maxUpload = Math.max(...trafficHistory.value.map(p => p.uploadSpeed))
const maxDownload = Math.max(...trafficHistory.value.map(p => p.downloadSpeed))
const maxTotal = Math.max(...trafficHistory.value.map(p => p.uploadSpeed + p.downloadSpeed))
return {
upload: maxUpload,
download: maxDownload,
total: maxTotal
}
}
return {
// 状态
trafficHistory,
maxDataPoints,
dataInterval,
// 计算属性
currentUploadSpeed,
currentDownloadSpeed,
currentTotalSpeed,
// 方法
addTrafficData,
clearHistory,
setMaxDataPoints,
getDataInRange,
getAverageSpeed,
getPeakSpeed
}
})
@@ -74,18 +74,8 @@
<!-- 流量统计 -->
<div class="detail-section">
<h4 class="detail-section-title">流量概览 (模拟)</h4>
<div class="traffic-stats">
<div class="traffic-item">
<span class="traffic-label">上传</span>
<span class="traffic-value upload">{{ trafficData.upload }}</span>
</div>
<div class="traffic-item">
<span class="traffic-label">下载</span>
<span class="traffic-value download">{{ trafficData.download }}</span>
</div>
</div>
<TrafficChart :data="chartData" />
<h4 class="detail-section-title">流量概览</h4>
<TrafficChart :data="trafficHistory" :max-data-points="60" />
</div>
</div>
@@ -107,6 +97,7 @@ import { storeToRefs } from 'pinia'
import TrafficChart from './components/TrafficChart.vue'
import { useApplicationStore } from '@/stores/application'
import { useWebSocketStore } from '@/stores/websocketStore'
import { useTrafficStore } from '@/stores/trafficStore'
import { httpClient } from '@/utils/http'
import { SubscriptionInfo } from '@/types/response'
import { convertFileSize } from '@/utils/fileUtil'
@@ -120,8 +111,10 @@ defineProps<{
// Store
const applicationStore = useApplicationStore()
const webSocketStore = useWebSocketStore()
const trafficStore = useTrafficStore()
const { selectedApp, inspectingAppDetails, isInspecting } = storeToRefs(applicationStore)
const { isOpen } = storeToRefs(webSocketStore)
const { trafficHistory } = storeToRefs(trafficStore)
// 订阅应用详情
const subscribeToAppDetails = (appPath: string) => {
@@ -190,23 +183,6 @@ const formatMemory = (memoryInBytes: number): string => {
const result = convertFileSize(memoryInBytes, FILE_SIZE_UNIT_ENUM.B)
return result.size + result.unit
}
// 模拟流量数据
const trafficData = computed(() => {
if (!isInspecting.value) {
return { upload: '0 B', download: '0 B' }
}
// 将来可以从API获取真实数据
return {
upload: '1.2 MB',
download: '5.8 MB',
}
})
// 模拟图表数据
const chartData = computed(() => {
return Array.from({ length: 20 }, () => Math.random() * 100)
})
</script>
<style scoped>
@@ -363,6 +363,7 @@ import { httpClient } from '@/utils/http'
import { useWebSocketStore } from '@/stores/websocketStore'
import { useProcessStore } from '@/stores/processInfo'
import { useFilterStore } from '@/stores/filterStore'
import { useTrafficStore } from '@/stores/trafficStore'
import type { ProcessType, ConnectionInfo, IPEndPoint } from '@/types/process'
import { ConnectionState, FILE_SIZE_UNIT_ENUM } from '@/constants/enums'
import { convertFileSize } from '@/utils/fileUtil'
@@ -380,6 +381,8 @@ const { processInfos } = storeToRefs(processStore)
const filterStore = useFilterStore()
const { filterText, isFiltering, filteredProcesses } = storeToRefs(filterStore)
const trafficStore = useTrafficStore()
// 订阅进程信息
processStore.subscribe()
@@ -439,6 +442,37 @@ const stats = computed(() => {
}
})
// 数据收集定时器
let dataCollectionTimer: number | null = null
// 收集网络流量数据
const collectTrafficData = () => {
const currentStats = stats.value
trafficStore.addTrafficData(
currentStats.totalUploadSpeed,
currentStats.totalDownloadSpeed
)
}
// 启动数据收集
const startDataCollection = () => {
// 立即收集一次数据
collectTrafficData()
// 每20秒收集一次数据
dataCollectionTimer = window.setInterval(() => {
collectTrafficData()
}, trafficStore.dataInterval)
}
// 停止数据收集
const stopDataCollection = () => {
if (dataCollectionTimer) {
clearInterval(dataCollectionTimer)
dataCollectionTimer = null
}
}
// 获取所有连接列表(统一显示)
const allConnections = computed(() => {
const connections: unknown[] = []
@@ -796,6 +830,9 @@ onMounted(() => {
window.addEventListener('resize', updateMainViewHeight)
// 启动数据收集
startDataCollection()
watch(
[isOpen, selectedApp],
([newValue, newValue1]) => {
@@ -824,6 +861,9 @@ onMounted(() => {
onUnmounted(() => {
window.removeEventListener('resize', updateMainViewHeight)
// 停止数据收集
stopDataCollection()
const subAppInfo: SubscriptionInfo = {
subscriptionType: 'ProcessInfo',
interval: 1,
@@ -5,31 +5,24 @@
<span class="chart-value">{{ currentSpeed }}</span>
</div>
<div class="chart-container">
<div class="chart-bars">
<div
v-for="(value, index) in data"
:key="index"
class="chart-bar"
:style="{ height: value + '%' }"
:class="{ 'bar-active': index === data.length - 1 }"
>
<div class="bar-tooltip">{{ formatSpeed(value) }}</div>
</div>
</div>
<div class="chart-grid">
<div class="grid-line" v-for="i in 4" :key="i"></div>
</div>
<v-chart
ref="chartRef"
class="echarts-container"
:option="chartOption"
:autoresize="true"
:theme="chartTheme"
/>
</div>
<div class="chart-footer">
<span class="chart-label">{{ timeRange }}</span>
<div class="chart-legend">
<span class="legend-item upload">
<span class="legend-dot"></span>
上传
<span class="legend-dot upload"></span>
上传: {{ formatSpeed(currentUploadSpeed) }}
</span>
<span class="legend-item download">
<span class="legend-dot"></span>
下载
<span class="legend-dot download"></span>
下载: {{ formatSpeed(currentDownloadSpeed) }}
</span>
</div>
</div>
@@ -37,47 +30,318 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import VChart from 'vue-echarts'
import { use, getInstanceByDom } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { BarChart } from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
LegendComponent,
GridComponent,
DataZoomComponent,
ToolboxComponent
} from 'echarts/components'
import type { EChartsOption } from 'echarts'
import { convertFileSize } from '@/utils/fileUtil'
import { FILE_SIZE_UNIT_ENUM } from '@/constants/enums'
// 注册必需的组件
use([
CanvasRenderer,
BarChart,
TitleComponent,
TooltipComponent,
LegendComponent,
GridComponent,
DataZoomComponent,
ToolboxComponent
])
// 数据点管理常量
const MAX_POINTS = 300 // 最大数据点数(5分钟)
const DEFAULT_SHOW = 30 // 默认显示数量
const MAX_SHOW = 60 // 最大显示数量
const MIN_SHOW = 30 // 最小显示数量
// Props接口定义
interface TrafficDataPoint {
timestamp: number
uploadSpeed: number
downloadSpeed: number
}
// Props
const props = defineProps<{
data: number[]
data?: TrafficDataPoint[]
maxDataPoints?: number
}>()
// 当前速度
// 默认props值
const maxDataPoints = computed(() => props.maxDataPoints || 60) // 默认60个数据点(约20分钟,每20秒一个点)
// 组件引用
const chartRef = ref()
// 图表主题(深色模式适配)
const chartTheme = ref('light')
// 当前网速数据
const currentUploadSpeed = computed(() => {
const data = props.data || []
return data.length > 0 ? data[data.length - 1].uploadSpeed : 0
})
const currentDownloadSpeed = computed(() => {
const data = props.data || []
return data.length > 0 ? data[data.length - 1].downloadSpeed : 0
})
const currentSpeed = computed(() => {
const lastValue = props.data[props.data.length - 1] || 0
return formatSpeed(lastValue)
const total = currentUploadSpeed.value + currentDownloadSpeed.value
return formatSpeed(total)
})
// 时间范围
const timeRange = ref('最近 60 秒')
const timeRange = computed(() => {
const minutes = Math.ceil((maxDataPoints.value * 20) / 60) // 20秒一个点
return `最近 ${minutes} 分钟`
})
// 格式化速度
const formatSpeed = (value: number) => {
const speed = (value / 100) * 10 // 假设最大10MB/s
if (speed < 1) {
return `${(speed * 1024).toFixed(0)} KB/s`
}
return `${speed.toFixed(1)} MB/s`
const formatSpeed = (bytesPerSecond: number): string => {
if (bytesPerSecond === 0) return '0 B/s'
const result = convertFileSize(bytesPerSecond, FILE_SIZE_UNIT_ENUM.B)
return `${result.size}${result.unit}/s`
}
// 动画定时器
let animationTimer: number | null = null
// 计算缩放范围
const getZoomRange = (currentPoints: number) => {
if (currentPoints <= DEFAULT_SHOW) {
return { start: 0, end: 100 }
}
const spanPercent = (DEFAULT_SHOW / currentPoints) * 100
return { start: 100 - spanPercent, end: 100 }
}
// 生成时间标签
const generateTimeLabels = (dataLength: number): string[] => {
const labels: string[] = []
const now = Date.now()
for (let i = dataLength - 1; i >= 0; i--) {
const time = new Date(now - i * 1000) // 每秒一个点
const minutes = time.getMinutes().toString().padStart(2, '0')
const seconds = time.getSeconds().toString().padStart(2, '0')
labels.push(`${minutes}:${seconds}`)
}
return labels
}
// ECharts配置
const chartOption = computed<EChartsOption>(() => {
const data = props.data || []
const limitedData = data.slice(-maxDataPoints.value) // 只取最近的数据点
// 准备数据
const uploadData = limitedData.map(d => +(d.uploadSpeed / 1024 / 1024).toFixed(2))
const downloadData = limitedData.map(d => +(d.downloadSpeed / 1024 / 1024).toFixed(2))
const timeLabels = generateTimeLabels(limitedData.length)
return {
animation: true,
animationDuration: 1000,
animationEasing: 'elasticOut',
animationDelayUpdate: (idx: number) => idx * 25,
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params: unknown[]) => {
const time = params[0]?.axisValue || ''
let tooltip = `<div style="margin-bottom: 4px; font-weight: bold;">${time}</div>`
params.forEach((param) => {
const color = param.color
const seriesName = param.seriesName
const value = param.value
tooltip += `
<div style="margin-bottom: 2px;">
<span style="display:inline-block;margin-right:4px;border-radius:50%;width:10px;height:10px;background-color:${color};"></span>
${seriesName}: ${value} MB/s
</div>
`
})
return tooltip
},
backgroundColor: 'rgba(0, 0, 0, 0.8)',
borderColor: 'rgba(255, 255, 255, 0.2)',
textStyle: {
color: '#ffffff',
fontSize: 12
}
},
legend: {
show: false // 我们使用自定义图例
},
grid: {
top: 10,
left: 10,
right: 10,
bottom: 20,
containLabel: false
},
xAxis: {
type: 'category',
data: timeLabels,
show: false // 隐藏x轴,节省空间
},
yAxis: {
type: 'value',
show: false, // 隐藏y轴,节省空间
min: 0
},
series: [
{
name: '上传',
type: 'bar',
data: uploadData,
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#10b981' // 绿色 - 顶部
},
{
offset: 1,
color: '#059669' // 深绿色 - 底部
}
]
},
borderRadius: [2, 2, 0, 0]
},
emphasis: {
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#34d399'
},
{
offset: 1,
color: '#10b981'
}
]
}
}
},
animationDelay: (idx: number) => idx * 10, // bar-animation-delay效果
},
{
name: '下载',
type: 'bar',
data: downloadData,
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#3b82f6' // 蓝色 - 顶部
},
{
offset: 1,
color: '#1d4ed8' // 深蓝色 - 底部
}
]
},
borderRadius: [2, 2, 0, 0]
},
emphasis: {
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: '#60a5fa'
},
{
offset: 1,
color: '#3b82f6'
}
]
}
}
},
animationDelay: (idx: number) => idx * 10 + 100 ,// bar-animation-delay效果,稍微延迟
}
]
}
})
// 监听数据变化,更新图表
watch(
() => props.data,
() => {
nextTick(() => {
if (chartRef.value) {
chartRef.value.resize()
}
})
},
{ deep: true }
)
// 主题切换检测
const detectTheme = () => {
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches
chartTheme.value = isDark ? 'dark' : 'light'
}
onMounted(() => {
// 添加入场动画
animationTimer = window.setTimeout(() => {
document.querySelectorAll('.chart-bar').forEach((el, index) => {
;(el as HTMLElement).style.transitionDelay = `${index * 20}ms`
})
}, 100)
detectTheme()
// 监听主题变化
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
mediaQuery.addEventListener('change', detectTheme)
// 确保图表正确初始化
nextTick(() => {
if (chartRef.value) {
chartRef.value.resize()
}
})
})
onUnmounted(() => {
if (animationTimer) {
clearTimeout(animationTimer)
}
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
mediaQuery.removeEventListener('change', detectTheme)
})
</script>
@@ -88,6 +352,9 @@ onUnmounted(() => {
border-radius: 8px;
padding: 16px;
border: 1px solid var(--border-tertiary);
height: 100%;
display: flex;
flex-direction: column;
}
/* 图表头部 */
@@ -96,6 +363,7 @@ onUnmounted(() => {
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
flex-shrink: 0;
}
.chart-title {
@@ -115,74 +383,16 @@ onUnmounted(() => {
/* 图表主体 */
.chart-container {
position: relative;
height: 80px;
margin-bottom: 8px;
}
.chart-bars {
display: flex;
align-items: flex-end;
gap: 2px;
height: 100%;
position: relative;
z-index: 2;
}
.chart-bar {
flex: 1;
background: linear-gradient(to top, var(--accent-primary), var(--accent-secondary));
border-radius: 2px 2px 0 0;
min-height: 2px;
transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
min-height: 120px;
margin-bottom: 12px;
position: relative;
cursor: pointer;
}
.chart-bar:hover {
background: linear-gradient(to top, #1d4ed8, #0891b2);
}
.chart-bar.bar-active {
background: linear-gradient(to top, var(--accent-warning), var(--accent-purple));
}
/* 工具提示 */
.bar-tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(-4px);
background: var(--bg-tooltip);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s;
}
.chart-bar:hover .bar-tooltip {
opacity: 1;
}
/* 网格线 */
.chart-grid {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
pointer-events: none;
}
.grid-line {
height: 1px;
background: var(--border-secondary);
opacity: 0.5;
.echarts-container {
width: 100% !important;
height: 100% !important;
min-height: 120px;
}
/* 图表底部 */
@@ -190,6 +400,7 @@ onUnmounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.chart-label {
@@ -201,6 +412,7 @@ onUnmounted(() => {
.chart-legend {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.legend-item {
@@ -209,27 +421,100 @@ onUnmounted(() => {
gap: 4px;
font-size: 11px;
color: var(--text-muted);
font-weight: 500;
}
.legend-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent-primary);
flex-shrink: 0;
}
.legend-item.upload .legend-dot {
background: var(--accent-warning);
.legend-dot.upload {
background: linear-gradient(135deg, #10b981, #059669);
box-shadow: 0 0 4px rgba(16, 185, 129, 0.3);
}
.legend-item.download .legend-dot {
background: var(--accent-secondary);
.legend-dot.download {
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
box-shadow: 0 0 4px rgba(59, 130, 246, 0.3);
}
/* 响应式 */
/* 上传和下载颜色区分 */
.legend-item.upload {
color: #10b981;
}
.legend-item.download {
color: #3b82f6;
}
/* 响应式设计 */
@media (max-width: 1400px) {
.chart-container {
height: 60px;
min-height: 100px;
}
.echarts-container {
min-height: 100px;
}
}
@media (max-width: 1200px) {
.traffic-chart {
padding: 12px;
}
.chart-legend {
gap: 8px;
}
.legend-item {
font-size: 10px;
}
.chart-header {
margin-bottom: 8px;
}
.chart-container {
margin-bottom: 8px;
min-height: 80px;
}
.echarts-container {
min-height: 80px;
}
}
/* 暗色主题适配 */
@media (prefers-color-scheme: dark) {
.chart-value {
color: #60a5fa;
}
.legend-item.upload {
color: #34d399;
}
.legend-item.download {
color: #60a5fa;
}
.legend-dot.upload {
background: linear-gradient(135deg, #34d399, #10b981);
box-shadow: 0 0 4px rgba(52, 211, 153, 0.4);
}
.legend-dot.download {
background: linear-gradient(135deg, #60a5fa, #3b82f6);
box-shadow: 0 0 4px rgba(96, 165, 250, 0.4);
}
}
/* ECharts自定义样式覆盖 */
:deep(.echarts-tooltip) {
font-family: 'JetBrains Mono', 'Fira Code', monospace !important;
}
</style>
@@ -0,0 +1,169 @@
<template>
<div class="chart-container">
<v-chart :option="option" autoresize class="chart" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { use, getInstanceByDom } from 'echarts/core'
import VChart from 'vue-echarts'
import {
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
} from 'echarts/components'
import { BarChart } from 'echarts/charts'
import { CanvasRenderer } from 'echarts/renderers'
use([
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
BarChart,
CanvasRenderer,
])
// 5分钟数据,假设1秒1条
const MAX_POINTS = 300 // 总数据点(5分钟)
const DEFAULT_SHOW = 30 // 默认显示数量
const MAX_SHOW = 60 // 最大显示数量
const MIN_SHOW = 30 // 最小显示数量
const xAxisData = []
const uploadData = []
const downloadData = []
let userZoomLocked = false // 用户是否手动调整过滑轨
const option = ref({
title: { text: '网络速度(MB/s)' },
legend: { data: ['上传', '下载'] },
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: 40, right: 20, top: 40, bottom: 60 },
xAxis: { type: 'category', data: [] },
yAxis: { type: 'value' },
animationEasingUpdate: 'linear',
animationDurationUpdate: 200,
dataZoom: [
{
type: 'slider',
start: 100 - (DEFAULT_SHOW / MAX_POINTS) * 100,
end: 100,
minSpan: (MIN_SHOW / MAX_POINTS) * 100, // 最小 30 个
maxSpan: (MAX_SHOW / MAX_POINTS) * 100, // 最大 60 个
handleSize: '80%',
},
{ type: 'inside' },
],
series: [
{
name: '上传',
type: 'bar',
data: [],
itemStyle: { color: '#3b82f6' },
emphasis: { focus: 'series' },
},
{
name: '下载',
type: 'bar',
data: [],
itemStyle: { color: '#10b981' },
emphasis: { focus: 'series' },
},
],
})
function addData() {
const chartInstance = getInstanceByDom(document.querySelector('.chart'))
const selectedState = chartInstance?.getOption()?.legend?.[0]?.selected || {}
const zoomState = chartInstance?.getOption()?.dataZoom?.[0] || {}
const upload = +(Math.random() * 10).toFixed(2)
const download = +(Math.random() * 10).toFixed(2)
const label = new Date().toLocaleTimeString()
if (uploadData.length >= MAX_POINTS) {
uploadData.shift()
downloadData.shift()
xAxisData.shift()
}
uploadData.push(upload)
downloadData.push(download)
xAxisData.push(label)
const newOption = {
...option.value,
legend: { ...option.value.legend, selected: selectedState },
series: [
{ ...option.value.series[0], data: [...uploadData] },
{ ...option.value.series[1], data: [...downloadData] },
],
xAxis: { ...option.value.xAxis, data: [...xAxisData] },
}
const range = getZoomRange(xAxisData.length)
if (userZoomLocked) {
newOption.dataZoom = [
{
...option.value.dataZoom[0],
start: zoomState.start,
end: zoomState.end,
minSpan: (MIN_SHOW / Math.max(xAxisData.length, MIN_SHOW)) * 100,
maxSpan: (MAX_SHOW / Math.max(xAxisData.length, MAX_SHOW)) * 100
},
option.value.dataZoom[1]
]
} else {
newOption.dataZoom = [
{
...option.value.dataZoom[0],
start: range.start,
end: range.end,
minSpan: (MIN_SHOW / Math.max(xAxisData.length, MIN_SHOW)) * 100,
maxSpan: (MAX_SHOW / Math.max(xAxisData.length, MAX_SHOW)) * 100
},
option.value.dataZoom[1]
]
}
option.value = newOption
}
function getZoomRange(currentPoints) {
if (currentPoints <= DEFAULT_SHOW) {
return { start: 0, end: 100 }
}
const spanPercent = (DEFAULT_SHOW / currentPoints) * 100
return { start: 100 - spanPercent, end: 100 }
}
onMounted(() => {
const chartInstance = getInstanceByDom(document.querySelector('.chart'))
// 监听滑轨变化,判断是否锁定用户设置
chartInstance?.on('dataZoom', () => {
userZoomLocked = true
})
setInterval(addData, 1000)
})
</script>
<style scoped>
.chart-container {
width: 100%;
height: 400px;
}
.chart {
width: 100%;
height: 100%;
}
</style>