From 9d25aac5f499aa352447edefa2044482144fc48d Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Thu, 15 Jan 2026 17:41:00 +0800
Subject: [PATCH] feat: 图表

---
 applications/drone-command/package.json                                       |   11 
 applications/drone-command/src/views/basicManage/deviceStock/DeviceChart1.vue |  263 ++++++++++++++++
 applications/drone-command/src/assets/images/basicManage/chartBg2.svg         |  132 ++++++++
 applications/drone-command/src/views/basicManage/deviceStock/index.vue        |   46 ++
 applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue |  156 +++++++++
 applications/drone-command/src/views/basicManage/deviceStock/DeviceChart3.vue |  145 +++++++++
 applications/drone-command/src/assets/images/basicManage/chartBg.svg          |  132 ++++++++
 applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js      |   25 +
 pnpm-lock.yaml                                                                |   19 +
 9 files changed, 923 insertions(+), 6 deletions(-)

diff --git a/applications/drone-command/package.json b/applications/drone-command/package.json
index 0d2c512..287ccae 100644
--- a/applications/drone-command/package.json
+++ b/applications/drone-command/package.json
@@ -11,11 +11,6 @@
   },
   "dependencies": {
     "@amap/amap-jsapi-loader": "catalog:",
-    "@ztzf/apis": "workspace:*",
-    "@ztzf/components": "workspace:*",
-    "@ztzf/constants": "workspace:*",
-    "@ztzf/hooks": "workspace:*",
-    "@ztzf/utils": "workspace:*",
     "@dvgis/dc-sdk": "catalog:",
     "@element-plus/icons-vue": "catalog:",
     "@saber/nf-design-base-elp": "catalog:",
@@ -23,6 +18,11 @@
     "@saber/nf-form-elp": "catalog:",
     "@smallwei/avue": "catalog:",
     "@turf/turf": "catalog:",
+    "@ztzf/apis": "workspace:*",
+    "@ztzf/components": "workspace:*",
+    "@ztzf/constants": "workspace:*",
+    "@ztzf/hooks": "workspace:*",
+    "@ztzf/utils": "workspace:*",
     "animate.css": "catalog:",
     "avue-plugin-ueditor": "catalog:",
     "axios": "catalog:",
@@ -33,6 +33,7 @@
     "decimal.js": "catalog:",
     "disable-devtool": "catalog:",
     "echarts": "catalog:",
+    "echarts-gl": "^2.0.9",
     "element-plus": "catalog:",
     "eventemitter3": "catalog:",
     "highlight.js": "catalog:",
diff --git a/applications/drone-command/src/assets/images/basicManage/chartBg.svg b/applications/drone-command/src/assets/images/basicManage/chartBg.svg
new file mode 100644
index 0000000..583b831
--- /dev/null
+++ b/applications/drone-command/src/assets/images/basicManage/chartBg.svg
@@ -0,0 +1,132 @@
+<svg width="365" height="34" viewBox="0 0 365 34" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Group 2136636843">
+<g id="Group 1321318807">
+<g id="Vector 482" filter="url(#filter0_f_62_4305)">
+<path d="M365 15.5H167.292L150.51 31.5H12.5863L7.34204 26.5002" stroke="url(#paint0_linear_62_4305)" stroke-width="1.6"/>
+</g>
+<g id="Rectangle 346241148" filter="url(#filter1_f_62_4305)">
+<path d="M24.1237 14.5H145.79L139.497 23.5H17.8306L24.1237 14.5Z" fill="url(#paint1_linear_62_4305)"/>
+</g>
+<g id="Vector 485" filter="url(#filter2_f_62_4305)">
+<path d="M6.29321 8H8.39091V27H6.4756H6.29321V8Z" fill="url(#paint2_linear_62_4305)"/>
+</g>
+<g id="Vector 483" filter="url(#filter3_ddf_62_4305)">
+<path d="M8.39078 34L1.04883 27V30.5L4.71981 34H8.39078Z" fill="white"/>
+</g>
+<g id="Vector 484" opacity="0.3" filter="url(#filter4_f_62_4305)">
+<path d="M0 0H31.4655V2H2.24754H0V0Z" fill="url(#paint3_linear_62_4305)"/>
+</g>
+<g id="Union" filter="url(#filter5_dd_62_4305)">
+<path d="M365 16H348.218V14H365V16Z" fill="white"/>
+</g>
+<path id="Union_2" d="M296.825 24H293.678V21H296.825V24Z" fill="#686B8D"/>
+<path id="Union_3" d="M316.753 24H313.606V21H316.753V24Z" fill="#686B8D"/>
+</g>
+<g id="Group 1321318808">
+<g id="Rectangle 346241141" filter="url(#filter6_f_62_4305)">
+<path d="M174.109 20H182.5L173.06 30H164.669L174.109 20Z" fill="#0041FF"/>
+</g>
+<g id="Rectangle 346241142" filter="url(#filter7_f_62_4305)">
+<path d="M188.793 20H197.184L187.744 30H179.353L188.793 20Z" fill="#0041FF" fill-opacity="0.7"/>
+</g>
+<g id="Rectangle 346241143" filter="url(#filter8_f_62_4305)">
+<path d="M203.477 20H211.868L202.428 30H194.037L203.477 20Z" fill="#0041FF" fill-opacity="0.3"/>
+</g>
+<g id="Rectangle 346241144" filter="url(#filter9_f_62_4305)">
+<path d="M218.161 20H226.552L217.112 30H208.721L218.161 20Z" fill="#0041FF" fill-opacity="0.16"/>
+</g>
+</g>
+</g>
+<defs>
+<filter id="filter0_f_62_4305" x="5.79004" y="13.7" width="360.21" height="19.6001" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter1_f_62_4305" x="16.8306" y="13.5" width="129.96" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter2_f_62_4305" x="5.29321" y="7" width="4.09766" height="21" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter3_ddf_62_4305" x="-0.951172" y="25" width="11.342" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="1"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_62_4305"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="1"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="effect1_dropShadow_62_4305" result="effect2_dropShadow_62_4305"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_62_4305" result="shape"/>
+<feGaussianBlur stdDeviation="0.05" result="effect3_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter4_f_62_4305" x="-0.1" y="-0.1" width="31.6656" height="2.2" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.05" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter5_dd_62_4305" x="342.218" y="8" width="28.7815" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_62_4305"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="effect1_dropShadow_62_4305" result="effect2_dropShadow_62_4305"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_62_4305" result="shape"/>
+</filter>
+<filter id="filter6_f_62_4305" x="163.669" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter7_f_62_4305" x="178.353" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter8_f_62_4305" x="193.037" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<filter id="filter9_f_62_4305" x="207.721" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
+</filter>
+<linearGradient id="paint0_linear_62_4305" x1="17.8305" y1="23.5" x2="428.456" y2="23.5" gradientUnits="userSpaceOnUse">
+<stop stop-color="#89A1FF"/>
+<stop offset="0.159605" stop-color="#D9DFFF"/>
+<stop offset="1" stop-color="#3C4986" stop-opacity="0"/>
+</linearGradient>
+<linearGradient id="paint1_linear_62_4305" x1="17.8306" y1="18.5" x2="139.497" y2="18.5" gradientUnits="userSpaceOnUse">
+<stop stop-color="#002AA7"/>
+<stop offset="1" stop-color="#0041FF" stop-opacity="0"/>
+</linearGradient>
+<linearGradient id="paint2_linear_62_4305" x1="7.34206" y1="8" x2="7.34206" y2="27" gradientUnits="userSpaceOnUse">
+<stop stop-color="#4B598C" stop-opacity="0"/>
+<stop offset="1" stop-color="#8299F2"/>
+</linearGradient>
+<linearGradient id="paint3_linear_62_4305" x1="31.4655" y1="1" x2="0" y2="1" gradientUnits="userSpaceOnUse">
+<stop stop-color="#999999" stop-opacity="0"/>
+<stop offset="1" stop-color="white"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/applications/drone-command/src/assets/images/basicManage/chartBg2.svg b/applications/drone-command/src/assets/images/basicManage/chartBg2.svg
new file mode 100644
index 0000000..8be08b5
--- /dev/null
+++ b/applications/drone-command/src/assets/images/basicManage/chartBg2.svg
@@ -0,0 +1,132 @@
+<svg width="618" height="34" viewBox="0 0 618 34" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Group 2136636843">
+<g id="Group 1321318807">
+<g id="Vector 482" filter="url(#filter0_f_63_4945)">
+<path d="M617.49 15.5H162.658L146.342 31.5H12.2377L7.13867 26.5002" stroke="url(#paint0_linear_63_4945)" stroke-width="1.6"/>
+</g>
+<g id="Rectangle 346241148" filter="url(#filter1_f_63_4945)">
+<path d="M23.4555 14.5H141.753L135.634 23.5H17.3367L23.4555 14.5Z" fill="url(#paint1_linear_63_4945)"/>
+</g>
+<g id="Vector 485" filter="url(#filter2_f_63_4945)">
+<path d="M6.1189 8H8.1585V27H6.29623H6.1189V8Z" fill="url(#paint2_linear_63_4945)"/>
+</g>
+<g id="Vector 483" filter="url(#filter3_ddf_63_4945)">
+<path d="M8.15839 34L1.01978 27V30.5L4.58908 34H8.15839Z" fill="white"/>
+</g>
+<g id="Vector 484" opacity="0.3" filter="url(#filter4_f_63_4945)">
+<path d="M0 0H30.5941V2H2.18529H0V0Z" fill="url(#paint3_linear_63_4945)"/>
+</g>
+<g id="Union" filter="url(#filter5_dd_63_4945)">
+<path d="M618 16H601.683V14H618V16Z" fill="white"/>
+</g>
+<path id="Union_2" d="M543.555 24H540.495V21H543.555V24Z" fill="#686B8D"/>
+<path id="Union_3" d="M562.931 24H559.871V21H562.931V24Z" fill="#686B8D"/>
+</g>
+<g id="Group 1321318808">
+<g id="Rectangle 346241141" filter="url(#filter6_f_63_4945)">
+<path d="M169.287 20H177.446L168.267 30H160.109L169.287 20Z" fill="#0041FF"/>
+</g>
+<g id="Rectangle 346241142" filter="url(#filter7_f_63_4945)">
+<path d="M183.564 20H191.723L182.545 30H174.386L183.564 20Z" fill="#0041FF" fill-opacity="0.7"/>
+</g>
+<g id="Rectangle 346241143" filter="url(#filter8_f_63_4945)">
+<path d="M197.842 20H206L196.822 30H188.663L197.842 20Z" fill="#0041FF" fill-opacity="0.3"/>
+</g>
+<g id="Rectangle 346241144" filter="url(#filter9_f_63_4945)">
+<path d="M212.119 20H220.277L211.099 30H202.941L212.119 20Z" fill="#0041FF" fill-opacity="0.16"/>
+</g>
+</g>
+</g>
+<defs>
+<filter id="filter0_f_63_4945" x="5.57861" y="13.7" width="612.912" height="19.6001" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter1_f_63_4945" x="16.3367" y="13.5" width="126.416" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter2_f_63_4945" x="5.1189" y="7" width="4.03955" height="21" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter3_ddf_63_4945" x="-0.980225" y="25" width="11.1387" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="1"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_63_4945"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="1"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="effect1_dropShadow_63_4945" result="effect2_dropShadow_63_4945"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_63_4945" result="shape"/>
+<feGaussianBlur stdDeviation="0.05" result="effect3_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter4_f_63_4945" x="-0.1" y="-0.1" width="30.794" height="2.2" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.05" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter5_dd_63_4945" x="595.683" y="8" width="28.3169" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_63_4945"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="out"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
+<feBlend mode="normal" in2="effect1_dropShadow_63_4945" result="effect2_dropShadow_63_4945"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_63_4945" result="shape"/>
+</filter>
+<filter id="filter6_f_63_4945" x="159.109" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter7_f_63_4945" x="173.386" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter8_f_63_4945" x="187.663" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<filter id="filter9_f_63_4945" x="201.941" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
+</filter>
+<linearGradient id="paint0_linear_63_4945" x1="17.3367" y1="23.5" x2="618" y2="24.02" gradientUnits="userSpaceOnUse">
+<stop stop-color="#89A1FF"/>
+<stop offset="0.716346" stop-color="#D9DFFF"/>
+<stop offset="1" stop-color="#3C4986" stop-opacity="0"/>
+</linearGradient>
+<linearGradient id="paint1_linear_63_4945" x1="17.3367" y1="18.5" x2="135.634" y2="18.5" gradientUnits="userSpaceOnUse">
+<stop stop-color="#002AA7"/>
+<stop offset="1" stop-color="#0041FF" stop-opacity="0"/>
+</linearGradient>
+<linearGradient id="paint2_linear_63_4945" x1="7.1387" y1="8" x2="7.1387" y2="27" gradientUnits="userSpaceOnUse">
+<stop stop-color="#4B598C" stop-opacity="0"/>
+<stop offset="1" stop-color="#8299F2"/>
+</linearGradient>
+<linearGradient id="paint3_linear_63_4945" x1="30.5941" y1="1" x2="0" y2="1" gradientUnits="userSpaceOnUse">
+<stop stop-color="#999999" stop-opacity="0"/>
+<stop offset="1" stop-color="white"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart1.vue b/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart1.vue
new file mode 100644
index 0000000..adf02ec
--- /dev/null
+++ b/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart1.vue
@@ -0,0 +1,263 @@
+<template>
+	<div class="deviceChart">
+		<div class="titleBg">
+			<span>设备类型分析</span>
+		</div>
+		<div class="chartBox" ref="chartRef"></div>
+	</div>
+</template>
+<script setup>
+import 'echarts-gl'
+import { statisticalDeviceTypeApi } from '@/views/basicManage/deviceStock/fwDevice'
+import useEchartsResize from '@/hooks/useEchartsResize'
+
+const chartRef = ref(null)
+let { chart } = useEchartsResize(chartRef)
+
+const list = ref([])
+
+const COLORS = ['#2D74EA', '#1BBDCA', '#E3A023', '#FFD900', '#AA4CD3', '#00ADEF']
+
+const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h) => {
+	const midRatio = (startRatio + endRatio) / 2
+	const startRadian = startRatio * Math.PI * 2
+	const endRadian = endRatio * Math.PI * 2
+	const midRadian = midRatio * Math.PI * 2
+	const kValue = typeof k !== 'undefined' ? k : 1 / 3
+	const offsetX = isSelected ? Math.sin(midRadian) * 0.1 : 0
+	const offsetY = isSelected ? Math.cos(midRadian) * 0.1 : 0
+	const hoverRate = isHovered ? 1.05 : 1
+	return {
+		u: {
+			min: -Math.PI,
+			max: Math.PI * 3,
+			step: Math.PI / 32,
+		},
+		v: {
+			min: 0,
+			max: Math.PI * 2,
+			step: Math.PI / 20,
+		},
+		x: (u, v) => {
+			if (u < startRadian) return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * kValue) * hoverRate
+			if (u > endRadian) return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * kValue) * hoverRate
+			return offsetX + Math.cos(u) * (1 + Math.cos(v) * kValue) * hoverRate
+		},
+		y: (u, v) => {
+			if (u < startRadian) return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * kValue) * hoverRate
+			if (u > endRadian) return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * kValue) * hoverRate
+			return offsetY + Math.sin(u) * (1 + Math.cos(v) * kValue) * hoverRate
+		},
+		z: (u, v) => {
+			if (u < -Math.PI * 0.5) return Math.sin(u)
+			if (u > Math.PI * 2.5) return Math.sin(u) * h * 0.1
+			return Math.sin(v) > 0 ? 1 * h * 0.1 : -1
+		},
+	}
+}
+
+const getPie3D = (pieData, internalDiameterRatio) => {
+	const series = []
+	let sumValue = 0
+	let startValue = 0
+	let endValue = 0
+	const k =
+		typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3
+
+	for (let i = 0; i < pieData.length; i++) {
+		sumValue += pieData[i].value
+		const seriesItem = {
+			name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
+			type: 'surface',
+			parametric: true,
+			wireframe: {
+				show: false,
+			},
+			pieData: pieData[i],
+			pieStatus: {
+				selected: false,
+				hovered: false,
+				k: 1 / 10,
+			},
+		}
+		if (typeof pieData[i].itemStyle !== 'undefined') {
+			const itemStyle = {}
+			if (typeof pieData[i].itemStyle.color !== 'undefined') itemStyle.color = pieData[i].itemStyle.color
+			if (typeof pieData[i].itemStyle.opacity !== 'undefined') itemStyle.opacity = pieData[i].itemStyle.opacity
+			seriesItem.itemStyle = itemStyle
+		}
+		series.push(seriesItem)
+	}
+
+	for (let i = 0; i < series.length; i++) {
+		endValue = startValue + series[i].pieData.value
+		series[i].pieData.startRatio = startValue / sumValue
+		series[i].pieData.endRatio = endValue / sumValue
+		series[i].parametricEquation = getParametricEquation(
+			series[i].pieData.startRatio,
+			series[i].pieData.endRatio,
+			false,
+			false,
+			k,
+			series[i].pieData.value
+		)
+		startValue = endValue
+	}
+
+	return series
+}
+
+const buildOption = () => {
+	const legendRich = {
+		percent: {
+			fontWeight: 700,
+		},
+	}
+	const optionsData = list.value.map((item, index) => ({
+		name: item.deviceTypeValue,
+		value: item.deviceCount,
+		itemStyle: {
+			color: COLORS[index % COLORS.length],
+		},
+	}))
+	list.value.forEach((item, index) => {
+		legendRich[`count${index}`] = {
+			color: COLORS[index % COLORS.length],
+		}
+	})
+	const series = getPie3D(optionsData, 0.6)
+	series.push({
+		name: 'pie2d',
+		type: 'pie',
+		label: {
+			show: false,
+		},
+		labelLine: {
+			show: false,
+		},
+		startAngle: 300,
+		clockwise: false,
+		radius: ['42%', '54%'],
+		center: ['30%', '50%'],
+		data: optionsData,
+		itemStyle: {
+			opacity: 0,
+		},
+	})
+
+	return {
+		graphic: {
+			type: 'text',
+			left: '22%',
+			top: '35%',
+			style: {
+				text: '设备类型',
+				fill: '#FFFFFF',
+				font: '600 14px Arial',
+				textAlign: 'center',
+				textVerticalAlign: 'middle',
+			},
+		},
+		legend: {
+			right: 24,
+			top: 'center',
+			orient: 'vertical',
+			textStyle: {
+				color: '#FFFFFF',
+				fontSize: 12,
+				rich: legendRich,
+			},
+			itemWidth: 8,
+			itemHeight: 8,
+			icon: 'rect',
+			formatter: name => {
+				const itemIndex = list.value.findIndex(v => v.deviceTypeValue === name)
+				const item = list.value[itemIndex]
+				if (!item) return name
+				const percent = item.totalDevices ? Math.round((item.deviceCount / item.totalDevices) * 100) : 0
+				return `${name}   {count${itemIndex}|${item.deviceCount}}   {percent|${percent}%}`
+			},
+		},
+		animation: true,
+		tooltip: {
+			show: true,
+			trigger: 'item',
+			confine: true,
+			backgroundColor: 'rgba(0,0,0,0.7)',
+			borderColor: '#0070FF',
+			borderWidth: 1,
+			textStyle: {
+				color: '#FFFFFF',
+				fontSize: 14, // 也可以顺便调整字号
+			},
+			formatter: params => {
+				if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
+					return `${params.seriesName}:${series[params.seriesIndex].pieData.value}`
+				}
+			},
+		},
+		title: {
+			x: 'center',
+			top: '20',
+			textStyle: {
+				color: '#fff',
+				fontSize: 22,
+			},
+		},
+		backgroundColor: 'transparent',
+		labelLine: {
+			show: false,
+		},
+		label: {
+			show: false,
+		},
+		xAxis3D: {
+			min: -1,
+			max: 1,
+		},
+		yAxis3D: {
+			min: -1,
+			max: 1,
+		},
+		zAxis3D: {
+			min: -1,
+			max: 1,
+		},
+		grid3D: {
+			show: false,
+			boxHeight: 26,
+			left: '0%',
+			top: -20,
+			width: '60%',
+			viewControl: {
+				distance: 210,
+				alpha: 55,
+				beta: 70,
+				autoRotate: false,
+				rotateSensitivity: 1,
+				zoomSensitivity: 0,
+				panSensitivity: 0,
+			},
+		},
+		series,
+	}
+}
+
+onMounted(() => {
+	statisticalDeviceTypeApi().then(res => {
+		list.value = Array.isArray(res?.data?.data) ? res.data.data : []
+		chart.value.setOption(buildOption())
+	})
+})
+</script>
+
+<style scoped lang="scss">
+.deviceChart {
+	width: 385px;
+}
+.titleBg {
+	width: 365px;
+	height: 34px;
+	background: url('@/assets/images/basicManage/chartBg.svg') no-repeat center / 100% 100%;
+}
+</style>
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue b/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue
new file mode 100644
index 0000000..1fd3b08
--- /dev/null
+++ b/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue
@@ -0,0 +1,156 @@
+<template>
+	<div class="deviceChart">
+		<div class="titleBg">
+			<span>设备类型分析</span>
+		</div>
+		<div class="chartBox" ref="chartRef"></div>
+	</div>
+</template>
+
+<script setup>
+import * as echarts from 'echarts'
+import useEchartsResize from '@/hooks/useEchartsResize'
+import { statisticalDeviceOutApi } from '@/views/basicManage/deviceStock/fwDevice'
+
+const chartRef = ref(null)
+let { chart } = useEchartsResize(chartRef)
+
+const list = ref([])
+function buildOption() {
+	const names = list.value.map(item => item.type)
+	const values = list.value.map(item => item.count)
+	const hasData = values.length > 0
+	const maxValue = values.length ? Math.max(...values) : 0
+	const stepMax = maxValue === 0 ? 10 : Math.ceil(maxValue / 5) * 5
+	const shadowData = values.map(() => stepMax)
+
+	return {
+		graphic: hasData
+			? []
+			: [
+					{
+						type: 'text',
+						left: 'center',
+						top: 'middle',
+						style: {
+							text: '暂无数据',
+							fill: '#FFFFFF',
+							font: '14px Arial',
+							textAlign: 'center',
+							textVerticalAlign: 'middle',
+						},
+					},
+			  ],
+		grid: {
+			top: 15,
+			left: 10,
+			right: 10,
+			bottom: 10,
+			containLabel: true,
+		},
+		tooltip: {
+			trigger: 'axis',
+			backgroundColor: 'rgba(0,0,0,0.7)',
+			borderColor: '#0070FF',
+			borderWidth: 1,
+			textStyle: {
+				color: '#FFFFFF',
+				fontSize: 14, // 也可以顺便调整字号
+			},
+			axisPointer: {
+				type: 'shadow',
+			},
+			formatter: params => {
+				const target = Array.isArray(params) ? params.find(item => item.seriesIndex === 1) : null
+				return `${target?.name ?? ''}:${target?.value ?? 0}`
+			},
+		},
+		xAxis: {
+			type: 'category',
+			data: names,
+			axisLine: {
+				show: false,
+			},
+			axisTick: {
+				show: false,
+			},
+			axisLabel: {
+				color: '#FFFFFF',
+				fontSize: 12,
+				interval: 0,
+			},
+		},
+		yAxis: {
+			type: 'value',
+			axisLabel: {
+				color: '#FFFFFF',
+				fontSize: 12,
+			},
+			axisLine: {
+				show: false,
+			},
+			axisTick: {
+				show: false,
+			},
+			splitLine: {
+				show: false,
+			},
+		},
+		series: [
+			{
+				type: 'bar',
+				data: shadowData,
+				barWidth: 20,
+				itemStyle: {
+					color: 'rgba(255,255,255,0.08)',
+				},
+				z: 0,
+			},
+			{
+				type: 'bar',
+				data: values,
+				barWidth: 10,
+				barGap: '-75%',
+				showBackground: false,
+				itemStyle: {
+					color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
+						{ offset: 1, color: '#0783FA' },
+						{ offset: 0, color: 'rgba(0,85,255,0)' },
+					]),
+				},
+				z: 1,
+			},
+			{
+				type: 'pictorialBar',
+				data: values,
+				symbol: 'rect',
+				symbolPosition: 'end',
+				symbolSize: [10, 3],
+				symbolOffset: [2.5, -2],
+				itemStyle: {
+					color: '#6BB7FF',
+				},
+				z: 3,
+			},
+		],
+	}
+}
+
+onMounted(() => {
+	statisticalDeviceOutApi().then(res => {
+		list.value = Array.isArray(res?.data?.data) ? res.data.data : []
+		chart.value.setOption(buildOption())
+	})
+})
+</script>
+
+<style scoped lang="scss">
+.deviceChart {
+	width: 638px;
+}
+.titleBg {
+	width: 618px;
+	height: 34px;
+	background: url('@/assets/images/basicManage/chartBg2.svg') no-repeat center / 100% 100%;
+}
+</style>
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart3.vue b/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart3.vue
new file mode 100644
index 0000000..8821d74
--- /dev/null
+++ b/applications/drone-command/src/views/basicManage/deviceStock/DeviceChart3.vue
@@ -0,0 +1,145 @@
+<template>
+	<div class="deviceChart">
+		<div class="titleBg">
+			<span>设备生产厂商统计</span>
+		</div>
+		<div class="chartBox" ref="chartRef"></div>
+	</div>
+</template>
+
+<script setup>
+import * as echarts from 'echarts'
+import useEchartsResize from '@/hooks/useEchartsResize'
+import { statisticalDeviceManufacturerApi, statisticalDeviceOutApi } from '@/views/basicManage/deviceStock/fwDevice'
+
+const chartRef = ref(null)
+let { chart } = useEchartsResize(chartRef)
+
+const list = ref([])
+function buildOption() {
+	const names = list.value.map(item => item.type)
+	const values = list.value.map(item => item.count)
+	const hasData = values.length > 0
+
+	return {
+		graphic: hasData
+			? []
+			: [
+					{
+						type: 'text',
+						left: 'center',
+						top: 'middle',
+						style: {
+							text: '暂无数据',
+							fill: '#FFFFFF',
+							font: '14px Arial',
+							textAlign: 'center',
+							textVerticalAlign: 'middle',
+						},
+					},
+			  ],
+		grid: {
+			top: 15,
+			left: 10,
+			right: 10,
+			bottom: 10,
+			containLabel: true,
+		},
+		tooltip: {
+			trigger: 'axis',
+			backgroundColor: 'rgba(0,0,0,0.7)',
+			borderColor: '#0070FF',
+			borderWidth: 1,
+			textStyle: {
+				color: '#FFFFFF',
+				fontSize: 14, // 也可以顺便调整字号
+			},
+			axisPointer: {
+				type: 'shadow',
+			},
+			formatter: params => {
+				const target = Array.isArray(params) ? params.find(item => item.seriesIndex === 1) : null
+				return `${target?.name ?? ''}:${target?.value ?? 0}`
+			},
+		},
+
+		xAxis: {
+			type: 'category',
+			data: names,
+			axisLine: {
+				show: false,
+			},
+			axisTick: {
+				show: false,
+			},
+			axisLabel: {
+				color: '#FFFFFF',
+				fontSize: 12,
+				interval: 0,
+			},
+		},
+		yAxis: {
+			type: 'value',
+			axisLabel: {
+				color: '#FFFFFF',
+				fontSize: 12,
+			},
+			axisLine: {
+				show: false,
+			},
+			axisTick: {
+				show: false,
+			},
+			splitLine: {
+				show: false,
+			},
+		},
+		series: [
+			{
+				type: 'bar',
+				data: values,
+				barWidth: 10,
+				itemStyle: {
+					color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
+						{ offset: 1, color: '#0783FA' },
+						{ offset: 0, color: 'rgba(0,85,255,0)' },
+					]),
+				},
+				z: 1,
+			},
+			{
+				type: 'pictorialBar',
+				itemStyle: {
+					color: '#0B0B17',
+				},
+				symbolRepeat: 'fixed',
+				symbolMargin: 3,
+				symbol: 'rect',
+				symbolClip: false,
+				symbolPosition: 'end',
+				symbolSize: [10, 3],
+				data: values,
+				z: 2,
+			},
+		],
+	}
+}
+
+onMounted(() => {
+	statisticalDeviceManufacturerApi().then(res => {
+		list.value = Array.isArray(res?.data?.data) ? res.data.data : []
+		chart.value.setOption(buildOption())
+	})
+})
+</script>
+
+<style scoped lang="scss">
+.deviceChart {
+	width: 638px;
+}
+.titleBg {
+	width: 618px;
+	height: 34px;
+	background: url('@/assets/images/basicManage/chartBg2.svg') no-repeat center / 100% 100%;
+}
+</style>
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js b/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
index 1e3cbf8..4007cfd 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
+++ b/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
@@ -53,3 +53,28 @@
 		responseType: 'blob',
 	})
 }
+
+//设备类型统计
+export const statisticalDeviceTypeApi = params => {
+	return request({
+		url: `/drone-fw/device/fwDevice/statisticalDeviceType`,
+		method: 'get',
+		params,
+	})
+}
+//设备出库去向统计
+export const statisticalDeviceOutApi = params => {
+	return request({
+		url: `/drone-fw/device/fwDevice/statisticalDeviceOut`,
+		method: 'get',
+		params,
+	})
+}
+//设备生产厂商统计
+export const statisticalDeviceManufacturerApi = params => {
+	return request({
+		url: `/drone-fw/device/fwDevice/statisticalDeviceManufacturer`,
+		method: 'get',
+		params,
+	})
+}
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/index.vue b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
index 0138975..b598892 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/index.vue
+++ b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
@@ -1,5 +1,10 @@
 <template>
 	<basic-container>
+		<div class="chart">
+			<DeviceChart1 />
+			<DeviceChart2 />
+			<DeviceChart3 />
+		</div>
 		<el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
 			<el-form-item label="名称" prop="deviceName">
 				<el-input
@@ -138,6 +143,9 @@
 import { blobDownload, dateRangeFormat, getDictLabel, formatDateToSlash } from '@ztzf/utils'
 import DeviceTrackDiaLog from '@/views/basicManage/deviceStock/DeviceTrackDiaLog.vue'
 import DeviceScrapDiaLog from '@/views/basicManage/deviceStock/DeviceScrapDiaLog.vue'
+import DeviceChart1 from '@/views/basicManage/deviceStock/DeviceChart1.vue'
+import DeviceChart2 from '@/views/basicManage/deviceStock/DeviceChart2.vue'
+import DeviceChart3 from '@/views/basicManage/deviceStock/DeviceChart3.vue'
 
 const initSearchParams = () => ({
 	deviceName: '', // 设备名称
@@ -267,4 +275,40 @@
 	getDeptTreeFun()
 })
 </script>
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+.chart {
+	width: 100%;
+	height: 234px;
+	display: flex;
+	:deep() {
+		* {
+			box-sizing: border-box;
+		}
+		.deviceChart {
+
+			height: 100%;
+			background: rgba(26, 26, 42, 0.3);
+			border-radius: 10px 10px 10px 10px;
+			position: relative;
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			align-items: center;
+			padding: 10px;
+
+			.titleBg {
+				font-weight: 400;
+				font-size: 18px;
+				color: #ffffff;
+				font-family: PangMen, serif;
+				padding-left: 22px;
+			}
+			.chartBox {
+				width: 100%;
+				height: 0;
+				flex-grow: 1;
+			}
+		}
+	}
+}
+</style>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7234a4a..c877e2a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -281,6 +281,9 @@
       echarts:
         specifier: 'catalog:'
         version: 5.6.0
+      echarts-gl:
+        specifier: ^2.0.9
+        version: 2.0.9(echarts@5.6.0)
       element-plus:
         specifier: 'catalog:'
         version: 2.9.11(vue@3.5.26(typescript@5.9.3))
@@ -4549,6 +4552,9 @@
     resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
     engines: {node: '>=0.10.0'}
 
+  claygl@1.3.0:
+    resolution: {integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==}
+
   clean-regexp@1.0.0:
     resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
     engines: {node: '>=4'}
@@ -5098,6 +5104,11 @@
 
   eastasianwidth@0.2.0:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+  echarts-gl@2.0.9:
+    resolution: {integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==}
+    peerDependencies:
+      echarts: ^5.1.2
 
   echarts@5.6.0:
     resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
@@ -14382,6 +14393,8 @@
       isobject: 3.0.1
       static-extend: 0.1.2
 
+  claygl@1.3.0: {}
+
   clean-regexp@1.0.0:
     dependencies:
       escape-string-regexp: 1.0.5
@@ -14887,6 +14900,12 @@
 
   eastasianwidth@0.2.0: {}
 
+  echarts-gl@2.0.9(echarts@5.6.0):
+    dependencies:
+      claygl: 1.3.0
+      echarts: 5.6.0
+      zrender: 5.6.1
+
   echarts@5.6.0:
     dependencies:
       tslib: 2.3.0

--
Gitblit v1.9.3