From 5667f2b4fc7555ba678d5aef13b096cd38da64db Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Mon, 12 Jan 2026 15:45:34 +0800
Subject: [PATCH] feat:数据驾驶舱相关处理

---
 applications/drone-command/src/styles/common.scss                                   |    2 
 applications/drone-command/src/page/index/top/index.vue                             |   35 
 applications/drone-command/src/styles/element-ui.scss                               |  261 -------
 applications/drone-command/src/styles/common/cockpit.scss                           |  660 +++++++++++++++++++
 applications/drone-command/src/styles/top.scss                                      |    7 
 applications/drone-command/src/assets/images/dataCockpit/map/equipment.png          |    0 
 applications/drone-command/src/views/dataCockpit/index.vue                          |   10 
 applications/drone-command/src/assets/images/defaultava.png                         |    0 
 applications/drone-command/src/assets/images/dataCockpit/layerControl.png           |    0 
 applications/drone-command/src/views/dataCockpit/components/MapContainer.vue        |  400 +++++++++++
 applications/drone-command/src/views/dataCockpit/components/RightContainer.vue      |   35 
 applications/drone-command/src/views/api.txt                                        |  142 ++-
 applications/drone-command/src/utils/mapUtils.js                                    |    7 
 applications/drone-command/src/assets/images/mh.png                                 |    0 
 applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue |  395 ++++-------
 applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue       |   20 
 applications/drone-command/src/api/dataCockpit/index.js                             |    8 
 17 files changed, 1,372 insertions(+), 610 deletions(-)

diff --git a/applications/drone-command/src/api/dataCockpit/index.js b/applications/drone-command/src/api/dataCockpit/index.js
index f8b68b6..553d692 100644
--- a/applications/drone-command/src/api/dataCockpit/index.js
+++ b/applications/drone-command/src/api/dataCockpit/index.js
@@ -44,3 +44,11 @@
 		data
 	})
 }
+
+// 设备统计
+export const deviceStatisticsApi = () => {
+	return request({
+		url: '/drone-fw/cockpit/dataCockpit/deviceStatistics',
+		method: 'get'
+	})
+}
diff --git a/applications/drone-command/src/assets/images/dataCockpit/layerControl.png b/applications/drone-command/src/assets/images/dataCockpit/layerControl.png
new file mode 100644
index 0000000..b6ea5b9
--- /dev/null
+++ b/applications/drone-command/src/assets/images/dataCockpit/layerControl.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/dataCockpit/map/equipment.png b/applications/drone-command/src/assets/images/dataCockpit/map/equipment.png
new file mode 100644
index 0000000..f07291b
--- /dev/null
+++ b/applications/drone-command/src/assets/images/dataCockpit/map/equipment.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/defaultava.png b/applications/drone-command/src/assets/images/defaultava.png
index cc3462f..0747af3 100644
--- a/applications/drone-command/src/assets/images/defaultava.png
+++ b/applications/drone-command/src/assets/images/defaultava.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/mh.png b/applications/drone-command/src/assets/images/mh.png
index cc0d23b..fe0b5d9 100644
--- a/applications/drone-command/src/assets/images/mh.png
+++ b/applications/drone-command/src/assets/images/mh.png
Binary files differ
diff --git a/applications/drone-command/src/page/index/top/index.vue b/applications/drone-command/src/page/index/top/index.vue
index 8554741..84906ca 100644
--- a/applications/drone-command/src/page/index/top/index.vue
+++ b/applications/drone-command/src/page/index/top/index.vue
@@ -6,23 +6,16 @@
     </div>
 
     <div class="top-bar__right">
-      <div class="icon-box">
-        <top-lock v-if="setting.lock" />
-        <top-qna />
-        <top-full v-if="setting.fullscreen" title="全屏" />
-        <img class="gateway" @click="jumpMH" src="@/assets/images/mh.svg" alt="进入门户" title="进入门户" width="20"
-          height="20">
-      </div>
       <div class="top-user">
-        <img class="top-bar__img" :src="userInfo.avatar" alt="" />
+        <div class="icon-box">
+         <img class="gateway" @click="jumpMH" src="@/assets/images/mh.png" alt="进入门户" title="进入门户">
+        </div>
 
         <el-dropdown>
           <span class="el-dropdown-link">
-            {{ userInfo.real_name }}
-            <el-icon class="el-icon--right">
-              <arrow-down />
-            </el-icon>
+            <img class="top-bar__img" :src="userInfo.avatar" alt="" />
           </span>
+
           <template #dropdown>
             <el-dropdown-menu>
               <!--              <el-dropdown-item>
@@ -154,20 +147,25 @@
 }
 
 .top-bar__right {
+  margin-right: 28px;
+  padding-top: 16px;
   flex: 0 0 auto;
   display: flex !important;
-  align-items: center;
+  align-items: flex-start;
   height: 100%;
   pointer-events: auto;
+  box-sizing: border-box;
 
   .icon-box {
+    margin-top: 6px;
+    margin-right: 30px;
     display: flex;
     align-items: center;
     gap: 0 20px;
 
     .gateway {
-      width: 25px;
-      height: 25px;
+      width: 21px;
+      height: 18px;
     }
 
     >* {
@@ -187,10 +185,9 @@
 }
 
 .top-bar__img {
-  width: 32px;
-  height: 32px;
-  border-radius: 50%;
-  margin-right: 10px;
+  margin-top: 5px;
+  width: 16px;
+  height: 21px;
 }
 
 .el-dropdown-link {
diff --git a/applications/drone-command/src/styles/common.scss b/applications/drone-command/src/styles/common.scss
index 7172f8e..d8a1616 100644
--- a/applications/drone-command/src/styles/common.scss
+++ b/applications/drone-command/src/styles/common.scss
@@ -8,6 +8,8 @@
 // 地图编辑样式
 @import './mapEdit.scss';
 
+@import './common/cockpit.scss';
+
 a {
   text-decoration: none;
   color: #333;
diff --git a/applications/drone-command/src/styles/common/cockpit.scss b/applications/drone-command/src/styles/common/cockpit.scss
new file mode 100644
index 0000000..9bf2d0c
--- /dev/null
+++ b/applications/drone-command/src/styles/common/cockpit.scss
@@ -0,0 +1,660 @@
+// 数据驾驶舱专属
+.ztzf-data-cockpit-search-input {
+    .el-input__wrapper {
+        padding: 0 8px !important;
+        background: #161B2C;
+        box-shadow: none !important;
+        border: none;
+
+        .el-input__inner {
+            color: #ffffff;
+
+            &::placeholder {
+                color: #86909C;
+            }
+        }
+    }
+
+    &.is-disabled {
+        .el-input__wrapper {
+            background-color: #161B2C;
+        }
+    }
+}
+
+.ztzf-data-cockpit-date-picker {
+    box-shadow: none !important;
+    background: #161B2C;
+    border: none;
+    border-radius: 5px;
+    box-sizing: border-box !important;
+
+    .el-input__wrapper {
+        box-shadow: none !important;
+        background-color: transparent !important;
+
+        &.is-focus {
+            box-shadow: none !important;
+        }
+    }
+
+    .el-range-input {
+        color: #fff;
+
+        &::placeholder {
+            color: #86909C;
+        }
+    }
+
+    .el-range-separator {
+        color: #ffffff;
+    }
+}
+
+.ztzf-data-cockpit-date-picker-popper {
+    background: #161B2C !important;
+    border: 1px solid !important;
+    border-radius: 0px 0px 8px 8px;
+    border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
+
+    .el-date-picker__prev-btn,
+    .el-date-picker__next-btn {
+        .el-icon {
+            color: #fff !important;
+        }
+    }
+
+    .el-date-picker__header-label {
+        color: #fff !important;
+    }
+
+    .el-picker-panel {
+        .el-picker-panel__body-wrapper {
+            .el-picker-panel__body {
+                background: #161B2C !important;
+                color: #fff !important;
+
+                .el-date-picker__time-header {
+                    border: none;
+
+                    .el-date-picker__editor-wrap {
+                        .el-time-panel {
+                            background: #161B2C !important;
+                            border-radius: 0px 0px 8px 8px;
+                            border: 1px solid !important;
+                            border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
+
+                            // 将 time-picker-popper 的样式移到这里
+                            .el-time-panel__header {
+                                background: #161B2C !important;
+                                color: #ffffff !important;
+                                border-bottom: 1px solid rgba(71, 157, 255, 0.3) !important;
+                            }
+
+                            .el-time-panel__content {
+                                .el-time-spinner__item {
+                                    color: #e6e6e6 !important;
+
+                                    &:hover {
+                                        background: rgba(71, 157, 255, 0.3) !important;
+                                    }
+
+                                    &.active {
+                                        color: #479dff !important;
+                                        font-weight: bold;
+                                    }
+                                }
+                            }
+
+                            .el-time-panel__footer {
+                                background: #012350 !important;
+                                border-top: 1px solid rgba(71, 157, 255, 0.3) !important;
+
+                                .el-button {
+                                    color: #fff !important;
+
+                                    &:hover {
+                                        color: #479dff !important;
+                                    }
+                                }
+
+                                .el-time-panel__btn.cancel {
+                                    color: #fff !important;
+
+                                    &:hover {
+                                        color: #fff !important;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                .el-picker-panel__content {
+                    .el-date-range-picker__header {
+                        .el-picker-panel__icon-btn {
+                            color: #fff !important;
+
+                            &:hover {
+                                color: var(--el-color-primary) !important;
+                            }
+
+                            .el-icon {
+                                &::before {
+                                    color: inherit !important;
+                                }
+                            }
+                        }
+
+                        &>div {
+                            span {
+                                color: #fff !important;
+                            }
+                        }
+                    }
+
+                    .el-date-table {
+                        th {
+                            color: #fff !important;
+                        }
+
+                        td.disabled div {
+                            background-color: #00193b !important;
+                            color: #505050 !important;
+                            cursor: not-allowed;
+                        }
+
+                        // td:hover,
+                        // td.current:not(.disabled) {
+                        //     background: rgba(60, 121, 202) !important;
+                        //     color: white !important;
+                        // }
+
+                        td.start-date span,
+                        td.start-date span,
+                        td.end-date span {
+                            background-color: #012350 !important;
+                        }
+
+                        td.in-range div,
+                        td.in-range div:hover,
+                        &.is-week-mode .el-date-table__row.current div,
+                        &.is-week-mode .el-date-table__row:hover div {
+                            background-color: rgba(60, 121, 202) !important;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    .el-popper__arrow {
+        &::before {
+            background: #161B2C !important;
+            border: 1px solid #161B2C !important;
+            box-sizing: border-box;
+        }
+    }
+
+    .el-picker-panel__sidebar {
+        background: #012350;
+        color: #fff;
+
+        .el-picker-panel__shortcut {
+            color: #ffffff;
+        }
+    }
+
+    .el-picker-panel__body {
+        .el-input.is-disabled .el-input__wrapper {
+            background: #012350;
+            box-shadow: 0 0 0 0.1rem#409eff;
+            color: #fff;
+
+            .el-input__inner {
+                color: #fff !important;
+            }
+        }
+
+        .el-button.is-disabled,
+        .el-button.is-disabled:hover {
+            background: #012350;
+        }
+
+        .el-input__wrapper {
+            background: #012350;
+            box-shadow: 0 0 0 0.1rem#409eff;
+            color: #fff;
+
+            .el-input__inner {
+                color: #fff !important;
+            }
+        }
+    }
+
+    .el-picker-panel__footer {
+        background: #012350;
+        color: #fff;
+        border-top: none;
+
+        .el-button--small {
+            box-shadow: 0 0 0 0.1rem#409eff;
+            background-color: #012350;
+            color: #fff;
+            border: none;
+
+            &:hover {
+                background-color: #012350;
+                color: #fff;
+                border-color: #409eff;
+            }
+        }
+    }
+}
+
+.ztzf-data-cockpit-select {
+    .el-select__input {
+        color: #8ac3fd !important;
+    }
+
+    .el-select__wrapper {
+        background: #2E2E48;
+        box-shadow: none !important;
+        border: none;
+        width: 100%;
+    }
+
+    .el-select__placeholder {
+        &.is-transparent {
+            color: #86909C !important;
+        }
+
+        &.el-select__selected-item {
+            color: #ffffff;
+        }
+    }
+
+    .el-select {
+        --el-select-border-color-hover: rgb(0, 162, 255) !important;
+    }
+
+    .el-input {
+        --el-border-color: rgb(0, 162, 255);
+    }
+
+    .el-select .el-input__wrapper {
+        background-color: #012350 !important;
+    }
+
+    .el-input__wrapper {
+        --el-input-text-color: #012350 !important;
+    }
+}
+
+.ztzf-data-cockpit-select-popper.el-select__popper {
+    border: none;
+    background: #2E2E48 !important;
+
+    .el-select-dropdown {
+        overflow-x: hidden !important;
+        border: none;
+
+        .el-scrollbar {
+            .el-select-dropdown__wrap {
+                overflow-x: hidden !important;
+                border: none;
+
+                .el-select-dropdown__list {
+                    padding: 0 !important;
+
+                    .el-select-dropdown__item {
+                        background: transparent !important;
+                        color: white;
+
+                        &.hover {
+                            background-color: rgba(0, 120, 233, 0.63) !important;
+                            color: white;
+                        }
+
+                        &.selected {
+                            color: #8ac3fd !important;
+                            font-weight: 700;
+                        }
+                    }
+                }
+            }
+        }
+
+        .el-select-dropdown__empty {
+            background: #2E2E48;
+            border-radius: 0px 0px 8px 8px;
+        }
+    }
+
+    .el-popper__arrow::before {
+        background: #2E2E48 !important;
+        border: 1px solid #2E2E48 !important;
+    }
+}
+
+// 树形选择
+.ztzf-data-cockpit-tree-select-popper {
+    background: #2E2E48 !important;
+    border: none !important;
+
+    .el-popper__arrow::before {
+        background: #2E2E48 !important;
+        border: 1px solid #2E2E48 !important;
+    }
+
+    .el-tree {
+        background: transparent !important;
+        color: #fff !important;
+
+        .el-tree-node__content:hover {
+            background: none !important;
+        }
+
+        .el-tree-node__content:hover {
+            background: none !important;
+        }
+
+        .el-tree-node.is-current>.el-tree-node__content {
+            color: #479dff !important;
+        }
+    }
+
+    .el-select-dropdown__item {
+        background: none !important;
+        color: #fff !important;
+
+        input {
+            color: #fff !important;
+
+            &::placeholder {
+                color: #fff !important;
+            }
+        }
+    }
+
+    .el-tree {
+        --el-tree-node-hover-bg-color: none !important;
+        color: #fff !important;
+
+        .el-checkbox__inner {
+            background: none !important;
+            border: 1px solid #3194ef !important;
+        }
+    }
+
+    .el-select-dropdown__item.is-selected {
+        color: #fff !important;
+    }
+
+    .el-popper.is-light {
+        background: #012a50 !important;
+        box-shadow: none !important;
+        border: none !important;
+        border-radius: 4px !important;
+        margin-top: 2px;
+
+        .el-select__selection {
+            max-width: 240px !important;
+
+            .el-select__selected-item {
+                .el-tag {
+                    background: #0b1d38 !important;
+                    color: white;
+                }
+            }
+        }
+
+        .el-popper__arrow::before {
+            background: #012a50 !important;
+            border: 1px solid #012a50 !important;
+        }
+    }
+}
+
+.ztzf-data-cockpit-dialog {
+    padding: 20px;
+    display: flex;
+    flex-direction: column;
+    width: 1222px;
+    height: 835px;
+    background: #1A1A2A;
+    border-radius: 6px 6px 6px 6px;
+    border: 1px solid #2E2E46;
+    box-sizing: border-box;
+
+    .el-dialog__header {
+        padding-bottom: 30px;
+        height: auto;
+
+        .el-dialog__title {
+            font-family: Source Han Sans CN, Source Han Sans CN;
+            font-weight: bold;
+            font-size: 18px;
+            color: #FFFFFF;
+            line-height: 22px;
+            text-align: left;
+            font-style: normal;
+            text-transform: none;
+        }
+
+        .el-dialog__headerbtn {
+            .el-icon.el-dialog__close {
+                color: #fff;
+            }
+        }
+    }
+
+    .history-search {
+        .el-form-item__label {
+            font-family: Source Han Sans CN, Source Han Sans CN;
+            font-weight: 500;
+            font-size: 14px;
+            color: #C5C9D6;
+            font-style: normal;
+            text-transform: none;
+        }
+
+        .ztzf-data-cockpit-search-input {
+            .el-input__wrapper {
+                background: #2E2E48;
+            }
+
+            &.is-disabled {
+                .el-input__wrapper {
+                    background-color: #2E2E48;
+                }
+            }
+        }
+
+        .ztzf-data-cockpit-date-picker {
+            background: #2E2E48;
+        }
+
+        .ztzf-data-cockpit-date-picker-popper {
+            background: #2E2E48 !important;
+
+            .el-picker-panel {
+                .el-picker-panel__body-wrapper {
+                    .el-picker-panel__body {
+                        background: #2E2E48 !important;
+
+                        .el-date-picker__time-header {
+
+                            .el-date-picker__editor-wrap {
+                                .el-time-panel {
+                                    background: #2E2E48 !important;
+
+                                    // 将 time-picker-popper 的样式移到这里
+                                    .el-time-panel__header {
+                                        background: #2E2E48 !important;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            .el-popper__arrow {
+                &::before {
+                    background: #2E2E48 !important;
+                    border: 1px solid #2E2E48 !important;
+                    box-sizing: border-box;
+                }
+            }
+        }
+
+        .history-search-actions {
+            .el-button {
+                width: 96px;
+                background: transparent !important;
+                border-radius: 4px 4px 4px 4px;
+                border: 1px solid rgba(255, 255, 255, 0.3);
+
+                .el-icon {
+                    font-size: 16px;
+                    color: #fff;
+                }
+            }
+        }
+    }
+
+    .el-dialog__body {
+        height: 0;
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+
+        .table-container {
+            height: 0;
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+
+            .table-content {
+                height: 0;
+                flex: 1;
+                display: flex;
+                flex-direction: column;
+
+                .ztzf-data-cockpit-table {
+                    height: 0;
+                    flex: 1;
+                    background: transparent !important;
+
+					.el-table--border .el-table__inner-wrapper:after, .el-table--border:after, .el-table--border:before, .el-table__inner-wrapper:before {
+						height: 0;
+					}
+
+                    tr {
+                        background: transparent !important;
+                    }
+
+                    th.el-table__cell {
+						border-bottom: 1px solid #323241;
+                        background: transparent !important;
+
+                        .cell {
+                            font-family: Source Han Sans CN, Source Han Sans CN;
+                            font-weight: 500;
+                            font-size: 14px;
+                            color: #FFFFFF;
+                            line-height: 22px;
+                            font-style: normal;
+                            text-transform: none;
+                        }
+                    }
+
+                    td.el-table__cell {
+						border-bottom: 1px solid #323241;
+                        background: transparent !important;
+
+                        .cell {
+                            font-family: Source Han Sans CN, Source Han Sans CN;
+                            font-weight: 400;
+                            font-size: 14px;
+                            color: #9E9EBA;
+                            line-height: 18px;
+                            font-style: normal;
+                            text-transform: none;
+                        }
+                    }
+                }
+            }
+
+            .table-pagination {
+                padding-top: 30px;
+                padding-bottom: 10px;
+                display: flex;
+                justify-content: flex-end;
+
+                .el-pagination__total {
+                    font-family: Source Han Sans CN, Source Han Sans CN;
+                    font-weight: 400;
+                    font-size: 14px;
+                    color: #9E9EBA;
+                    font-style: normal;
+                    text-transform: none;
+                }
+
+                .btn-prev,
+                .btn-next {
+                    background: transparent !important;
+
+                    .el-icon {
+                        color: #C9CDD4;
+                    }
+                }
+
+                .el-pager {
+                    li {
+                        margin-left: 8px;
+                        font-family: Nunito Sans, Nunito Sans;
+                        font-weight: 600;
+                        font-size: 14px;
+                        color: #9E9EBA;
+                        font-style: normal;
+                        text-transform: none;
+                        background: transparent;
+
+                        &:first-child {
+                            margin-left: 0;
+                        }
+
+                        &.is-active {
+                            font-family: Nunito Sans, Nunito Sans;
+                            font-weight: 600;
+                            font-size: 14px;
+                            color: #477EFF;
+                            font-style: normal;
+                            text-transform: none;
+                            background: rgba(71, 126, 255, 0.16);
+                        }
+                    }
+                }
+
+                .el-select {
+                    .el-select__wrapper {
+						.el-select__placeholder {
+							font-family: Source Han Sans CN, Source Han Sans CN;
+							font-weight: 400;
+							font-size: 14px;
+							color: #9E9EBA;
+							font-style: normal;
+							text-transform: none;
+						}
+
+                        background: #1C1C31;
+                        border: none;
+                        box-shadow: none;
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/applications/drone-command/src/styles/element-ui.scss b/applications/drone-command/src/styles/element-ui.scss
index 8dc06ed..46dfc8f 100644
--- a/applications/drone-command/src/styles/element-ui.scss
+++ b/applications/drone-command/src/styles/element-ui.scss
@@ -766,265 +766,4 @@
         box-sizing: border-box;
         background: #fafafa;
     }
-}
-
-.ztzf-data-cockpit-search-input {
-    .el-input__wrapper {
-        padding: 0 8px !important;
-        background: #161B2C;
-        box-shadow: none !important;
-        border: none;
-
-        .el-input__inner {
-            color: #ffffff;
-
-            &::placeholder {
-                color: #86909C;
-            }
-        }
-    }
-
-    &.is-disabled {
-        .el-input__wrapper {
-            background-color: #161B2C;
-        }
-    }
-}
-
-.ztzf-data-cockpit-date-picker {
-    box-shadow: none !important;
-	background: #161B2C;
-    border: none;
-    border-radius: 5px;
-    box-sizing: border-box !important;
-
-    .el-input__wrapper {
-        box-shadow: none !important;
-        background-color: transparent !important;
-
-        &.is-focus {
-            box-shadow: none !important;
-        }
-    }
-
-    .el-range-input {
-        color: #fff;
-
-        &::placeholder {
-            color: #86909C;
-        }
-    }
-
-    .el-range-separator {
-        color: #ffffff;
-    }
-}
-
-.ztzf-data-cockpit-date-picker-popper {
-    background: #161B2C !important;
-    border: 1px solid !important;
-    border-radius: 0px 0px 8px 8px;
-    border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
-
-    .el-date-picker__prev-btn,
-    .el-date-picker__next-btn {
-        .el-icon {
-            color: #fff !important;
-        }
-    }
-
-    .el-date-picker__header-label {
-        color: #fff !important;
-    }
-
-    .el-picker-panel {
-        .el-picker-panel__body-wrapper {
-            .el-picker-panel__body {
-                background: #161B2C !important;
-                color: #fff !important;
-
-                .el-date-picker__time-header {
-                    border: none;
-
-                    .el-date-picker__editor-wrap {
-                        .el-time-panel {
-                            background: #161B2C !important;
-                            border-radius: 0px 0px 8px 8px;
-                            border: 1px solid !important;
-                            border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
-
-                            // 将 time-picker-popper 的样式移到这里
-                            .el-time-panel__header {
-                                background: #161B2C !important;
-                                color: #ffffff !important;
-                                border-bottom: 1px solid rgba(71, 157, 255, 0.3) !important;
-                            }
-
-                            .el-time-panel__content {
-                                .el-time-spinner__item {
-                                    color: #e6e6e6 !important;
-
-                                    &:hover {
-                                        background: rgba(71, 157, 255, 0.3) !important;
-                                    }
-
-                                    &.active {
-                                        color: #479dff !important;
-                                        font-weight: bold;
-                                    }
-                                }
-                            }
-
-                            .el-time-panel__footer {
-                                background: #012350 !important;
-                                border-top: 1px solid rgba(71, 157, 255, 0.3) !important;
-
-                                .el-button {
-                                    color: #fff !important;
-
-                                    &:hover {
-                                        color: #479dff !important;
-                                    }
-                                }
-
-                                .el-time-panel__btn.cancel {
-                                    color: #fff !important;
-
-                                    &:hover {
-                                        color: #fff !important;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-
-                .el-picker-panel__content {
-                    .el-date-range-picker__header {
-                        .el-picker-panel__icon-btn {
-                            color: #fff !important;
-
-                            &:hover {
-                                color: var(--el-color-primary) !important;
-                            }
-
-                            .el-icon {
-                                &::before {
-                                    color: inherit !important;
-                                }
-                            }
-                        }
-
-                        &>div {
-                            span {
-                                color: #fff !important;
-                            }
-                        }
-                    }
-
-                    .el-date-table {
-                        th {
-                            color: #fff !important;
-                        }
-
-                        td.disabled div {
-                            background-color: #00193b !important;
-                            color: #505050 !important;
-                            cursor: not-allowed;
-                        }
-
-                        // td:hover,
-                        // td.current:not(.disabled) {
-                        //     background: rgba(60, 121, 202) !important;
-                        //     color: white !important;
-                        // }
-
-                        td.start-date span,
-                        td.start-date span,
-                        td.end-date span {
-                            background-color: #012350 !important;
-                        }
-
-                        td.in-range div,
-                        td.in-range div:hover,
-                        &.is-week-mode .el-date-table__row.current div,
-                        &.is-week-mode .el-date-table__row:hover div {
-                            background-color: rgba(60, 121, 202) !important;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    .el-popper__arrow {
-        &::before {
-            background: #161B2C !important;
-            border: 1px solid #161B2C !important;
-            box-sizing: border-box;
-        }
-    }
-
-    .el-picker-panel__sidebar {
-        background: #012350;
-        color: #fff;
-
-        .el-picker-panel__shortcut {
-            color: #ffffff;
-        }
-    }
-
-    .el-picker-panel__body {
-        .el-input.is-disabled .el-input__wrapper {
-            background: #012350;
-            box-shadow: 0 0 0 0.1rem#409eff;
-            color: #fff;
-
-            .el-input__inner {
-                color: #fff !important;
-            }
-        }
-
-        .el-button.is-disabled,
-        .el-button.is-disabled:hover {
-            background: #012350;
-        }
-
-        .el-input__wrapper {
-            background: #012350;
-            box-shadow: 0 0 0 0.1rem#409eff;
-            color: #fff;
-
-            .el-input__inner {
-                color: #fff !important;
-            }
-        }
-    }
-
-    .el-picker-panel__footer {
-        background: #012350;
-        color: #fff;
-        border-top: none;
-
-        .el-button--small {
-            box-shadow: 0 0 0 0.1rem#409eff;
-            background-color: #012350;
-            color: #fff;
-            border: none;
-
-            &:hover {
-                background-color: #012350;
-                color: #fff;
-                border-color: #409eff;
-            }
-        }
-    }
-}
-
-.ztzf-data-cockpit-dialog {
-    width: 1222px;
-    height: 835px;
-    background: #1A1A2A;
-    border-radius: 6px 6px 6px 6px;
-    border: 1px solid #2E2E46;
 }
\ No newline at end of file
diff --git a/applications/drone-command/src/styles/top.scss b/applications/drone-command/src/styles/top.scss
index dd2148e..1a8cc46 100644
--- a/applications/drone-command/src/styles/top.scss
+++ b/applications/drone-command/src/styles/top.scss
@@ -56,13 +56,6 @@
 }
 
 .top-bar__img {
-  margin: 0 5px;
-  padding: 2px;
-  width: 30px;
-  height: 30px;
-  border-radius: 100%;
-  box-sizing: border-box;
-  border: 1px solid #eee;
   vertical-align: middle;
 }
 
diff --git a/applications/drone-command/src/utils/mapUtils.js b/applications/drone-command/src/utils/mapUtils.js
index a91d35f..028563a 100644
--- a/applications/drone-command/src/utils/mapUtils.js
+++ b/applications/drone-command/src/utils/mapUtils.js
@@ -315,6 +315,13 @@
 
 	// 销毁处理
 	destroy () {
+		if (!this._viewer || this._viewer.isDestroyed?.()) {
+			this._primitiveCollection = null
+			this._entityCircleDataSource = null
+			this._viewer = null
+			return
+		}
+
 		if (this._primitiveCollection) {
 			this._primitiveCollection.removeAll()
 			this._viewer.scene.primitives.remove(this._primitiveCollection)
diff --git a/applications/drone-command/src/views/api.txt b/applications/drone-command/src/views/api.txt
index 86dc87e..1decdbe 100644
--- a/applications/drone-command/src/views/api.txt
+++ b/applications/drone-command/src/views/api.txt
@@ -1,48 +1,22 @@
 
 
-## 信号干扰,诱导驱离
+## 历史告警-实时告警
 
 
-**接口地址**:`/cockpit/dataCockpit/interferenceAndExpulsion`
+**接口地址**:`/cockpit/dataCockpit/alarmLog`
 
 
-**请求方式**:`POST`
+**请求方式**:`GET`
 
 
-**请求数据类型**:`application/json`
+**请求数据类型**:`application/x-www-form-urlencoded`
 
 
 **响应数据类型**:`*/*`
 
 
-**接口描述**:<p>传入fwEffectEval</p>
+**接口描述**:<p>传入fwDroneAlarmRecord</p>
 
-
-
-**请求示例**:
-
-
-```javascript
-{
-  "alarmRecordId": 0,
-  "areaCode": "",
-  "counterEffect": "",
-  "coverRadiusM": 0,
-  "deployLatitude": 0,
-  "deployLongitude": 0,
-  "deviceCode": "",
-  "deviceId": 0,
-  "deviceModel": "",
-  "deviceName": "",
-  "deviceType": "",
-  "droneDeviceCode": "",
-  "droneName": "",
-  "droneType": "",
-  "findTime": "",
-  "id": 0,
-  "workMode": ""
-}
-```
 
 
 **请求参数**:
@@ -53,24 +27,31 @@
 
 | 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
 | -------- | -------- | ----- | -------- | -------- | ------ |
-|fwEffectEval|fwEffectEval|body|true|FwEffectEvalDTO|FwEffectEvalDTO|
-|&emsp;&emsp;alarmRecordId|告警记录ID(ja_fw_drone_alarm_record.id)||false|integer(int64)||
-|&emsp;&emsp;areaCode|区域编码||false|string||
-|&emsp;&emsp;counterEffect|反制效果(success/fail)||false|string||
-|&emsp;&emsp;coverRadiusM|覆盖范围(米)||false|integer(int32)||
-|&emsp;&emsp;deployLatitude|部署纬度(冗余)||false|number(double)||
-|&emsp;&emsp;deployLongitude|部署经度(冗余)||false|number(double)||
-|&emsp;&emsp;deviceCode|反制设备编码(快照)||false|string||
-|&emsp;&emsp;deviceId|反制设备ID(ja_fw_device.id)||false|integer(int64)||
-|&emsp;&emsp;deviceModel|反制设备型号(快照)||false|string||
-|&emsp;&emsp;deviceName|反制设备名称(快照)||false|string||
-|&emsp;&emsp;deviceType|反制设备类型(快照)||false|string||
-|&emsp;&emsp;droneDeviceCode|无人机设备编码||false|string||
-|&emsp;&emsp;droneName|无人机名称||false|string||
-|&emsp;&emsp;droneType|无人机类型||false|string||
-|&emsp;&emsp;findTime|发现时间||false|string(date-time)||
-|&emsp;&emsp;id|主键id||false|integer(int64)||
-|&emsp;&emsp;workMode|工作模式(机动/固定)||false|string||
+|alarmTime|告警时间|query|false|string(date-time)||
+|alarmType|告警类型:1.实时告警/2.历史告警|query|false|string||
+|areaCode|区域编码|query|false|string||
+|areaDivideId|区域id ja_fw_area_divide|query|false|string||
+|areaName|区域名称|query|false|string||
+|counterWay|反制方式:1.信号干扰/2.诱导驱离/无|query|false|string||
+|current|当前页|query|false|integer(int32)||
+|defenseSceneId|场景id ja_fw_defense_scene.id|query|false|string||
+|deviceId|反无设备ID(关联ja_fw_device.id)|query|false|integer(int64)||
+|deviceType|设备类型|query|false|string||
+|droneName|无人机名称|query|false|string||
+|droneSerialNo|无人机序列号|query|false|string||
+|droneType|无人机类型(1.微型机/2.植保机...)|query|false|string||
+|endTime|结束时间|query|false|string||
+|flightHeightM|飞行高度(m)|query|false|integer(int32)||
+|flightSpeedMs|飞行速度(m/s)|query|false|number||
+|flightStatus|飞行状态:1.侦测中/2.反制中|query|false|string||
+|id|主键id|query|false|integer(int64)||
+|latitude|当前纬度|query|false|number(double)||
+|longitude|当前经度|query|false|number(double)||
+|signalFreqMhz|信号频段(MHz)|query|false|integer(int32)||
+|size|每页的数量|query|false|integer(int32)||
+|startTime|开始时间|query|false|string||
+|stayDuration|停留时长 (秒) |query|false|string||
+|triggerReason|触发原因|query|false|string||
 
 
 **响应状态**:
@@ -78,8 +59,7 @@
 
 | 状态码 | 说明 | schema |
 | -------- | -------- | ----- | 
-|200|OK|R|
-|201|Created||
+|200|OK|R«IPage«FwDroneAlarmRecordVO»»|
 |401|Unauthorized||
 |403|Forbidden||
 |404|Not Found||
@@ -91,7 +71,33 @@
 | 参数名称 | 参数说明 | 类型 | schema |
 | -------- | -------- | ----- |----- | 
 |code|状态码|integer(int32)|integer(int32)|
-|data|承载数据|object||
+|data|承载数据|IPage«FwDroneAlarmRecordVO»|IPage«FwDroneAlarmRecordVO»|
+|&emsp;&emsp;current||integer(int64)||
+|&emsp;&emsp;pages||integer(int64)||
+|&emsp;&emsp;records||array|FwDroneAlarmRecordVO|
+|&emsp;&emsp;&emsp;&emsp;alarmTime|告警时间|string||
+|&emsp;&emsp;&emsp;&emsp;alarmType|告警类型:1.实时告警/2.历史告警|string||
+|&emsp;&emsp;&emsp;&emsp;areaCode|区域编码|string||
+|&emsp;&emsp;&emsp;&emsp;areaName|区域名称|string||
+|&emsp;&emsp;&emsp;&emsp;counterWay|反制方式:1.信号干扰/2.诱导驱离/3.无|string||
+|&emsp;&emsp;&emsp;&emsp;deviceId|反无设备ID(关联ja_fw_device.id)|integer||
+|&emsp;&emsp;&emsp;&emsp;deviceModel|设备型号|string||
+|&emsp;&emsp;&emsp;&emsp;deviceName|设备名称|string||
+|&emsp;&emsp;&emsp;&emsp;deviceType|设备类型(1.便捷侦测箱/2.反制枪/3.察打一体)|string||
+|&emsp;&emsp;&emsp;&emsp;droneName|无人机名称|string||
+|&emsp;&emsp;&emsp;&emsp;droneSerialNo|无人机序列号|string||
+|&emsp;&emsp;&emsp;&emsp;droneType|无人机类型(1.微型机/2.植保机...)|string||
+|&emsp;&emsp;&emsp;&emsp;flightHeightM|飞行高度(m)|integer||
+|&emsp;&emsp;&emsp;&emsp;flightSpeedMs|飞行速度(m/s)|number||
+|&emsp;&emsp;&emsp;&emsp;flightStatus|飞行状态:1.侦测中/2.反制中|string||
+|&emsp;&emsp;&emsp;&emsp;id|主键id|integer||
+|&emsp;&emsp;&emsp;&emsp;latitude|当前纬度|number||
+|&emsp;&emsp;&emsp;&emsp;longitude|当前经度|number||
+|&emsp;&emsp;&emsp;&emsp;signalFreqMhz|信号频段(MHz)|integer||
+|&emsp;&emsp;&emsp;&emsp;stayDuration|停留时长(秒)|string||
+|&emsp;&emsp;&emsp;&emsp;triggerReason|触发原因|string||
+|&emsp;&emsp;size||integer(int64)||
+|&emsp;&emsp;total||integer(int64)||
 |msg|返回消息|string||
 |success|是否成功|boolean||
 
@@ -100,7 +106,37 @@
 ```javascript
 {
 	"code": 0,
-	"data": {},
+	"data": {
+		"current": 0,
+		"pages": 0,
+		"records": [
+			{
+				"alarmTime": "",
+				"alarmType": "",
+				"areaCode": "",
+				"areaName": "",
+				"counterWay": "",
+				"deviceId": 0,
+				"deviceModel": "",
+				"deviceName": "",
+				"deviceType": "",
+				"droneName": "",
+				"droneSerialNo": "",
+				"droneType": "",
+				"flightHeightM": 0,
+				"flightSpeedMs": 0,
+				"flightStatus": "",
+				"id": 0,
+				"latitude": 0,
+				"longitude": 0,
+				"signalFreqMhz": 0,
+				"stayDuration": "",
+				"triggerReason": ""
+			}
+		],
+		"size": 0,
+		"total": 0
+	},
 	"msg": "",
 	"success": true
 }
diff --git a/applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue b/applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
index c9b0954..1ac9a90 100644
--- a/applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
@@ -1,94 +1,87 @@
 <template>
-	<el-dialog
-		class="ztzf-data-cockpit-dialog"
-		append-to-body
-		v-model="visibleModel"
-		:title="dialogTitle"
-		:close-on-click-modal="false"
-		:destroy-on-close="true"
-	>
-		<div class="dialog-body">
-			<el-form ref="queryParamsRef" :model="searchParams" class="history-search">
-				<el-row :gutter="12">
-					<el-col :span="5">
-						<el-form-item label="关键字" prop="keyword">
-							<el-input v-model="searchParams.keyword" placeholder="名称/序列号" clearable @clear="handleSearch" />
-						</el-form-item>
-					</el-col>
-					<el-col :span="5">
-						<el-form-item label="日期" prop="dateRange">
-							<el-date-picker
-								v-model="searchParams.dateRange"
-								type="daterange"
-								range-separator="至"
-								start-placeholder="开始日期"
-								end-placeholder="结束日期"
-								value-format="YYYY/MM/DD"
-								@change="handleSearch"
-							/>
-						</el-form-item>
-					</el-col>
-					<el-col :span="5">
-						<el-form-item label="区域" prop="area">
-							<el-tree-select
-								v-model="searchParams.area"
-								:data="areaTree"
-								:props="areaTreeProps"
-								node-key="value"
-								check-strictly
-								clearable
-								placeholder="请选择"
-								@change="handleSearch"
-							/>
-						</el-form-item>
-					</el-col>
-					<el-col :span="5">
-						<el-form-item label="无人机类型" prop="droneType">
-							<el-select
-								v-model="searchParams.droneType"
-								placeholder="请选择"
-								clearable
-								@change="handleSearch"
-							>
-								<el-option v-for="item in droneTypeOptions" :key="item" :label="item" :value="item" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-					<el-col :span="4" class="history-search-actions">
-						<el-form-item>
-							<el-button @click="resetForm">重置</el-button>
-							<el-button type="primary" @click="handleSearch">查询</el-button>
-						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
+	<el-dialog class="ztzf-data-cockpit-dialog" append-to-body v-model="visibleModel" :title="dialogTitle"
+		:close-on-click-modal="false" :destroy-on-close="true">
+		<el-form ref="queryParamsRef" :model="searchParams" class="history-search">
+			<el-row :gutter="12">
+				<el-col :span="4">
+					<el-form-item label="关键字" prop="keyword">
+						<el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.keyword"
+							placeholder="名称/序列号" clearable @clear="handleSearch" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="5">
+					<el-form-item prop="dateRange">
+						<el-date-picker class="ztzf-data-cockpit-date-picker"
+							popper-class="ztzf-data-cockpit-date-picker-popper" v-model="searchParams.dateRange"
+							type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
+							value-format="YYYY/MM/DD" @change="handleSearch" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="4">
+					<el-form-item label="区域" prop="area">
+						<el-tree-select  class="ztzf-data-cockpit-select" popper-class="ztzf-tree-cockpit-select-popper" v-model="searchParams.area" :data="areaTree" :props="areaTreeProps"
+							node-key="value" check-strictly clearable placeholder="请选择" @change="handleSearch" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="5">
+					<el-form-item label="无人机类型" prop="droneType">
+						<el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
+							v-model="searchParams.droneType" placeholder="请选择" clearable @change="handleSearch">
+							<el-option v-for="item in droneTypeOptions" :key="item.value" :label="item.label"
+								:value="item.value" />
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="6" class="history-search-actions">
+					<el-form-item>
+						<el-button :icon="RefreshRight" @click="resetForm"></el-button>
+						<el-button :icon="Search" @click="handleSearch"></el-button>
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</el-form>
 
-			<el-table v-loading="loading" :data="pagedList" height="360" class="history-table">
-				<el-table-column type="index" width="60" label="序号" />
-				<el-table-column prop="droneName" label="无人机名称" min-width="120" />
-				<el-table-column prop="serialNumber" label="无人机序列号" min-width="140" />
-				<el-table-column prop="droneType" label="无人机类型" min-width="110" />
-				<el-table-column prop="area" label="区域" min-width="90" />
-				<el-table-column prop="dataSource" label="数据来源" min-width="110" />
-				<el-table-column prop="frequency" label="信号频段" min-width="100" />
-				<el-table-column prop="discoveredAt" label="发现时间" min-width="150" />
-				<el-table-column prop="stayTime" label="停留时间" min-width="110" />
-				<el-table-column prop="counterMethod" label="反制方式" min-width="110" />
-			</el-table>
-
-			<div class="history-pagination">
-				<el-pagination
-					v-model:current-page="searchParams.current"
-					v-model:page-size="searchParams.size"
-					:total="total"
-				/>
+		<div class="table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
+			<div class="table-content">
+				<el-table class="ztzf-data-cockpit-table" :data="list">
+					<el-table-column type="index" width="60" label="序号" />
+					<el-table-column prop="droneName" label="无人机名称" min-width="120" />
+					<el-table-column prop="droneSerialNo" label="无人机序列号" min-width="140" />
+					<el-table-column label="无人机类型" min-width="110">
+						<template #default="{ row }">
+							{{ getDroneTypeText(row.droneType) }}
+						</template>
+					</el-table-column>
+					<el-table-column prop="areaName" label="区域" min-width="90" />
+					<el-table-column prop="deviceName" label="数据来源" min-width="110" />
+					<el-table-column label="信号频段" min-width="100">
+						<template #default="{ row }">
+							{{ formatFrequency(row.signalFreqMhz) }}
+						</template>
+					</el-table-column>
+					<el-table-column prop="alarmTime" label="发现时间" min-width="150" />
+					<el-table-column prop="stayDuration" label="停留时间" min-width="110" />
+					<el-table-column label="反制方式" min-width="110">
+						<template #default="{ row }">
+							{{ getCounterWayText(row.counterWay) }}
+						</template>
+					</el-table-column>
+				</el-table>
+			</div>
+			
+			<div class="table-pagination">
+				<el-pagination popper-class="ztzf-data-cockpit-select-popper" v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
+					layout="total, prev, pager, next, sizes"
+					:total="total" @change="getList" />
 			</div>
 		</div>
 	</el-dialog>
 </template>
 
 <script setup>
-import { computed, ref } from 'vue'
+import { Search, RefreshRight } from '@element-plus/icons-vue'
+import { computed, ref, watch } from 'vue'
+import { alarmLogApi } from '@/api/dataCockpit'
 
 const props = defineProps({
 	modelValue: {
@@ -118,11 +111,13 @@
 	area: '',
 	droneType: '',
 	current: 1,
-	size: 6
+	size: 10
 })
 const searchParams = ref(initSearchParams())
 const queryParamsRef = ref(null)
 const loading = ref(false)
+const list = ref([])
+const total = ref(0)
 const areaTree = ref([
 	{
 		label: '古村区',
@@ -142,167 +137,79 @@
 	children: 'children'
 }
 
-const historyList = computed(() => {
-	const dataSource = props.device?.name || '侦测反制设备名称'
-	return [
-		{
-			id: 'his_001',
-			droneName: '无人机名称',
-			serialNumber: 'XLH789456',
-			droneType: '微型机',
-			area: '古村区',
-			dataSource,
-			frequency: '5800MHZ',
-			discoveredAt: '2025/12/26 12:15:26',
-			stayTime: '00:10:50',
-			counterMethod: '信号干扰'
-		},
-		{
-			id: 'his_002',
-			droneName: '无人机名称',
-			serialNumber: 'XLH789456',
-			droneType: '微型机',
-			area: '古村区',
-			dataSource,
-			frequency: '5800MHZ',
-			discoveredAt: '2025/12/26 12:18:06',
-			stayTime: '00:08:12',
-			counterMethod: '信号干扰'
-		},
-		{
-			id: 'his_003',
-			droneName: '无人机名称',
-			serialNumber: 'XLH789456',
-			droneType: '微型机',
-			area: '古村区',
-			dataSource,
-			frequency: '2400MHZ',
-			discoveredAt: '2025/12/26 12:22:31',
-			stayTime: '00:06:09',
-			counterMethod: '诱导驱离'
-		},
-		{
-			id: 'his_004',
-			droneName: '无人机名称',
-			serialNumber: 'XLH789456',
-			droneType: '微型机',
-			area: '古村区',
-			dataSource,
-			frequency: '2400MHZ',
-			discoveredAt: '2025/12/26 12:28:44',
-			stayTime: '00:12:03',
-			counterMethod: '诱导驱离'
-		},
-		{
-			id: 'his_005',
-			droneName: '无人机名称',
-			serialNumber: 'XLH789456',
-			droneType: '微型机',
-			area: '古村区',
-			dataSource,
-			frequency: '5800MHZ',
-			discoveredAt: '2025/12/26 12:32:10',
-			stayTime: '00:04:39',
-			counterMethod: '信号干扰'
-		},
-		{
-			id: 'his_006',
-			droneName: '无人机名称',
-			serialNumber: 'XLH789456',
-			droneType: '微型机',
-			area: '古村区',
-			dataSource,
-			frequency: '5800MHZ',
-			discoveredAt: '2025/12/26 12:36:42',
-			stayTime: '00:07:21',
-			counterMethod: '信号干扰'
+const droneTypeOptions = [
+	{ label: '微型机', value: '1' },
+	{ label: '植保机', value: '2' }
+]
+
+const getList = async () => {
+	if (!props.device?.id) {
+		list.value = []
+		total.value = 0
+		return
+	}
+	const [startTime, endTime] = searchParams.value.dateRange || []
+	const keyword = (searchParams.value.keyword || '').trim()
+	const params = {
+		alarmType: '2',
+		deviceId: props.device.id,
+		current: searchParams.value.current,
+		size: searchParams.value.size,
+		startTime,
+		endTime,
+		areaName: searchParams.value.area || undefined,
+		droneType: searchParams.value.droneType || undefined,
+		droneName: keyword || undefined,
+	}
+	loading.value = true
+	try {
+		const res = await alarmLogApi(params)
+		list.value = res?.data?.data?.records ?? []
+		total.value = res?.data?.data?.total ?? 0
+	} finally {
+		loading.value = false
+	}
+}
+
+function handleSearch () {
+	searchParams.value.current = 1
+	getList()
+}
+
+function resetForm () {
+	searchParams.value = initSearchParams()
+
+	getList()
+}
+
+const formatFrequency = (value) => {
+	if (value == null || value === '') return '-'
+	return `${value}MHZ`
+}
+
+const getCounterWayText = (value) => {
+	if (value === 1 || value === '1') return '信号干扰'
+	if (value === 2 || value === '2') return '诱导驱离'
+	if (value === 3 || value === '3') return '无'
+	return value || '-'
+}
+
+const getDroneTypeText = (value) => {
+	if (value === 1 || value === '1') return '微型机'
+	if (value === 2 || value === '2') return '植保机'
+	return value || '-'
+}
+
+watch(
+	() => visibleModel.value,
+	(visible) => {
+		if (visible) {
+			searchParams.value.current = 1
+			getList()
 		}
-	]
-})
-
-const droneTypeOptions = computed(() => {
-	const types = historyList.value.map((item) => item.droneType).filter(Boolean)
-	return Array.from(new Set(types))
-})
-
-const filteredList = computed(() => {
-	const { keyword, dateRange, area, droneType } = searchParams.value
-	const [startDate, endDate] = dateRange || []
-	return historyList.value.filter((item) => {
-		const keywordMatch =
-			!keyword || item.droneName.includes(keyword) || item.serialNumber.includes(keyword)
-		const typeMatch = !droneType || item.droneType === droneType
-		const areaMatch = !area || item.area.includes(area)
-		const dateMatch =
-			!startDate || !endDate || isWithinDateRange(item.discoveredAt, startDate, endDate)
-		return keywordMatch && typeMatch && areaMatch && dateMatch
-	})
-})
-
-const total = computed(() => filteredList.value.length)
-
-const pagedList = computed(() => {
-	const { current, size } = searchParams.value
-	const start = (current - 1) * size
-	return filteredList.value.slice(start, start + size)
-})
-
-function handleSearch() {
-	searchParams.value.current = 1
-}
-
-function resetForm() {
-	queryParamsRef.value?.resetFields()
-	searchParams.value.current = 1
-}
-
-function isWithinDateRange(value, start, end) {
-	const dateValue = new Date(value)
-	const startDate = new Date(`${start} 00:00:00`)
-	const endDate = new Date(`${end} 23:59:59`)
-	return dateValue >= startDate && dateValue <= endDate
-}
+	}
+)
 
 </script>
 
-<style lang="scss" scoped>
-.dialog-body {
-	display: flex;
-	flex-direction: column;
-	gap: 16px;
-	color: #C3C3DD;
-}
-
-.history-search {
-	padding: 8px 12px 4px;
-	background: rgba(10, 14, 34, 0.6);
-	border: 1px solid rgba(255, 255, 255, 0.08);
-	border-radius: 6px;
-}
-
-.history-search :deep(.el-date-editor),
-.history-search :deep(.el-select),
-.history-search :deep(.el-tree-select) {
-	width: 100%;
-}
-
-.history-search-actions :deep(.el-form-item__content) {
-	justify-content: flex-end;
-	gap: 8px;
-}
-
-.history-table {
-	margin-top: 8px;
-	--el-table-border-color: rgba(255, 255, 255, 0.08);
-	--el-table-header-bg-color: rgba(20, 24, 48, 0.9);
-	--el-table-row-hover-bg-color: rgba(255, 255, 255, 0.03);
-	--el-table-text-color: #C3C3DD;
-	--el-table-header-text-color: #FFFFFF;
-}
-
-.history-pagination {
-	display: flex;
-	justify-content: flex-end;
-	padding-top: 8px;
-}
-</style>
+<style lang="scss" scoped></style>
diff --git a/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue b/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
index 07b66eb..7b54029 100644
--- a/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
@@ -115,17 +115,12 @@
 	position: absolute;
 	top: 50%;
 	left: 357px;
-	background: rgba(17, 23, 34, 0.8);
-	border: 1px solid #333355;
-	border-left: none;
-	border-radius: 0 4px 4px 0;
 	cursor: pointer;
 	display: flex;
 	align-items: center;
 	justify-content: center;
 	color: #fff;
 	z-index: 10;
-	backdrop-filter: blur(5px);
 	transform: translateY(-50%);
 
 	.arrow {
@@ -140,8 +135,19 @@
 }
 
 .category-container {
+	position: relative;
 	display: flex;
 	justify-content: space-between;
+
+	&::after {
+		content: '';
+		position: absolute;
+		left: 0;
+		bottom: 0;
+		width: 100%;
+		height: 1px;
+		background: #333355;
+	}
 
 	.category-item {
 		position: relative;
@@ -175,8 +181,7 @@
 			left: 0;
 			bottom: 0;
 			width: 100%;
-			height: 1px;
-			background: #333355;
+			height: 0;
 		}
 
 		&.active::after {
@@ -184,6 +189,7 @@
 			background: #284FE3;
 			box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
 			border-radius: 5px 5px 0px 0px;
+			z-index: 9;
 		}
 
 		.badge {
diff --git a/applications/drone-command/src/views/dataCockpit/components/MapContainer.vue b/applications/drone-command/src/views/dataCockpit/components/MapContainer.vue
index aa94178..4576555 100644
--- a/applications/drone-command/src/views/dataCockpit/components/MapContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/MapContainer.vue
@@ -1,28 +1,274 @@
 <template>
-	<div class="ztzf-cesium map-container" id="cesium">
+	<div class="ztzf-cesium map-container" id="cesium"></div>
+	<div class="layer-control-root" :class="{ collapsed: props.leftCollapsed }">
+		<div class="layer-control-wrap" ref="layerWrapRef">
+			<div class="layer-control" @click="toggleLayerPanel">
+				<img :src="layerControlIcon" alt="图层控制">
+			</div>
+			<div v-if="showLayerPanel" class="layer-panel">
+				<div class="panel-title">图层管理</div>
 
+				<div class="panel-content">
+					<el-tree :data="layerTree" show-checkbox default-expand-all node-key="key" :props="layerTreeProps"
+						:default-checked-keys="defaultCheckedKeys" />
+				</div>
+			</div>
+		</div>
 	</div>
 </template>
 
 <script setup>
+import * as Cesium from 'cesium'
 import { PublicCesium } from '@/utils/cesium/publicCesium'
+import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
+import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
+
+const props = defineProps({
+	onlineDevices: {
+		type: Array,
+		default: () => []
+	},
+	leftCollapsed: {
+		type: Boolean,
+		default: false
+	}
+})
+
 let viewInstance = null
 let viewer = null
+const deviceEntityIds = new Set()
+const showLayerPanel = ref(false)
+const layerWrapRef = ref(null)
+const layerTreeProps = {
+	label: 'label',
+	children: 'children'
+}
+const defaultCheckedKeys = ['global', 'city-base']
+const layerTree = ref([
+	{
+		key: 'base',
+		label: '地理信息图层',
+		children: [
+			{ key: 'global', label: '全球地形' },
+			{ key: 'admin', label: '行政区划' }
+		]
+	},
+	{
+		key: 'city',
+		label: '城市CIM图层',
+		children: [
+			{ key: 'city-base', label: '皖山白模' },
+			{ key: 'city-grid', label: '皖山白模光栅网格' },
+			{ key: 'city-tilt', label: '皖山倾斜摄影' },
+			{ key: 'city-tilt-grid', label: '皖山倾斜摄影网格' }
+		]
+	},
+	{
+		key: 'sky',
+		label: '空域要素图层',
+		children: [
+			{ key: 'airspace', label: '空域边界' },
+			{ key: 'route', label: '飞行航路' }
+		]
+	}
+])
+
+const getDevicePosition = (item) => {
+	const longitudeRaw = item.longitude ?? item.lng ?? item.lon
+	const latitudeRaw = item.latitude ?? item.lat
+	if (longitudeRaw == null || latitudeRaw == null) return null
+	const longitude = Number(longitudeRaw)
+	const latitude = Number(latitudeRaw)
+	if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
+	return { longitude, latitude }
+}
+
+const clearDeviceEntities = () => {
+	if (!viewer) return
+	deviceEntityIds.forEach((id) => {
+		viewer.entities.removeById(id)
+	})
+	deviceEntityIds.clear()
+}
+
+const RING_STYLES = [
+	{ inner: 0, outer: 5000, gradient: ['#FF361C', '#360B00'] },
+	{ inner: 5000, outer: 8000, gradient: ['#FFC609', '#583300'] },
+	{ inner: 8000, outer: 10000, gradient: ['#2AEDBF', '#012B11'] }
+]
+
+const MATERIAL_TYPE = 'RadialGradientMaterial'
+let materialRegistered = false
+
+const registerRadialGradientMaterial = () => {
+	if (materialRegistered || !Cesium?.Material) return
+	materialRegistered = true
+	Cesium.Material._materialCache.addMaterial(MATERIAL_TYPE, {
+		fabric: {
+			type: MATERIAL_TYPE,
+			uniforms: {
+				color1: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
+				color2: new Cesium.Color(0.0, 0.0, 0.0, 1.0)
+			},
+			source: `
+				czm_material czm_getMaterial(czm_materialInput materialInput) {
+					czm_material material = czm_getDefaultMaterial(materialInput);
+					vec2 st = materialInput.st - vec2(0.5);
+					float t = clamp(length(st) * 2.0, 0.0, 1.0);
+					vec4 color = mix(color1, color2, t);
+					material.diffuse = color.rgb;
+					material.alpha = color.a;
+					return material;
+				}
+			`
+		},
+		translucent: () => true
+	})
+}
+
+class RadialGradientMaterialProperty {
+	constructor(color1, color2) {
+		this._definitionChanged = new Cesium.Event()
+		this.color1 = color1
+		this.color2 = color2
+	}
+
+	get isConstant () {
+		return true
+	}
+
+	get definitionChanged () {
+		return this._definitionChanged
+	}
+
+	getType () {
+		return MATERIAL_TYPE
+	}
+
+	getValue (time, result) {
+		const target = result || {}
+		target.color1 = this.color1
+		target.color2 = this.color2
+		return target
+	}
+
+	equals (other) {
+		return (
+			other instanceof RadialGradientMaterialProperty &&
+			Cesium.Color.equals(this.color1, other.color1) &&
+			Cesium.Color.equals(this.color2, other.color2)
+		)
+	}
+}
+
+const buildCirclePositions = (center, radiusMeters, steps = 64) => {
+	const positions = []
+	const latRad = Cesium.Math.toRadians(center.latitude)
+	const metersToLat = 1 / 111320
+	const metersToLon = 1 / (111320 * Math.cos(latRad))
+	for (let i = 0; i <= steps; i += 1) {
+		const angle = Cesium.Math.toRadians((i / steps) * 360)
+		const dx = radiusMeters * Math.cos(angle)
+		const dy = radiusMeters * Math.sin(angle)
+		const lon = center.longitude + dx * metersToLon
+		const lat = center.latitude + dy * metersToLat
+		positions.push(Cesium.Cartesian3.fromDegrees(lon, lat))
+	}
+	return positions
+}
+
+const addDeviceRings = (center, baseId) => {
+	RING_STYLES.forEach((ring, index) => {
+		const outerPositions = buildCirclePositions(center, ring.outer)
+		const holes = ring.inner
+			? [new Cesium.PolygonHierarchy(buildCirclePositions(center, ring.inner))]
+			: []
+		const entityId = `${baseId}-ring-${index}`
+		deviceEntityIds.add(entityId)
+		registerRadialGradientMaterial()
+		const [startColor, endColor] = ring.gradient
+		const color1 = Cesium.Color.fromCssColorString(startColor).withAlpha(0.34)
+		const color2 = Cesium.Color.fromCssColorString(endColor).withAlpha(0.34)
+		const material = new RadialGradientMaterialProperty(color1, color2)
+		viewer.entities.add({
+			id: entityId,
+			polygon: {
+				hierarchy: new Cesium.PolygonHierarchy(outerPositions, holes),
+				material
+			}
+		})
+	})
+}
+
+const renderDeviceEntities = (devices) => {
+	console.log(devices, 'devices')
+
+	if (!viewer) return
+
+	clearDeviceEntities()
+	devices.forEach((item, index) => {
+		const position = getDevicePosition(item)
+		if (!position) return
+		const entityId = `online-device-${item.id ?? index}`
+		deviceEntityIds.add(entityId)
+		addDeviceRings(position, entityId)
+		viewer.entities.add({
+			id: entityId,
+			position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
+			billboard: {
+				image: equipmentIcon,
+				width: 40.34,
+				height: 40.34
+			}
+		})
+	})
+}
+
+watch(
+	() => props.onlineDevices,
+	(devices) => {
+		renderDeviceEntities(devices || [])
+	},
+	{ deep: true }
+)
+
+watch(
+	() => props.leftCollapsed,
+	(isCollapsed) => {
+		if (isCollapsed) showLayerPanel.value = false
+	}
+)
+
+const toggleLayerPanel = () => {
+	showLayerPanel.value = !showLayerPanel.value
+}
+
+const handleClickOutside = (event) => {
+	if (!showLayerPanel.value) return
+	const target = event.target
+	if (layerWrapRef.value?.contains(target)) return
+	showLayerPanel.value = false
+}
+
 onMounted(() => {
+	document.addEventListener('click', handleClickOutside)
 	viewInstance = new PublicCesium({
 		dom: 'cesium',
 		flatMode: false,
 		terrain: false,
 		layerMode: 4,
-		contour: false,
+		contour: false
 	})
 
 	viewer = viewInstance.getViewer()
-}) 
+	renderDeviceEntities(props.onlineDevices)
+})
 
 onBeforeUnmount(() => {
+	document.removeEventListener('click', handleClickOutside)
+	clearDeviceEntities()
 	if (viewInstance) {
-		viewInstance?.destroy()
+		viewInstance?.viewerDestroy()
 		viewInstance = null
 	}
 
@@ -38,4 +284,148 @@
 	width: 100%;
 	height: 100%;
 }
-</style>
\ No newline at end of file
+
+.layer-control-root {
+	position: absolute;
+	left: 411px;
+	bottom: 22px;
+	z-index: 9;
+	transition: transform 0.3s ease-in-out;
+	pointer-events: none;
+
+	&.collapsed {
+		transform: translateX(-401px);
+	}
+}
+
+.layer-control-wrap {
+	position: relative;
+	display: flex;
+	align-items: flex-end;
+	pointer-events: auto;
+}
+
+.layer-control {
+	width: 34px;
+	height: 34px;
+	cursor: pointer;
+
+	img {
+		width: 100%;
+		height: 100%;
+		display: block;
+	}
+}
+
+.layer-panel {
+	display: flex;
+	flex-direction: column;
+	position: absolute;
+	left: 46px;
+	bottom: 0;
+	width: 160px;
+	max-height: 442px;
+	background: #191932;
+	border-radius: 8px 8px 8px 8px;
+
+	.panel-title {
+		padding: 0 16px;
+		line-height: 42px;
+
+		font-family: Open Sans, Open Sans;
+		font-weight: 400;
+		font-size: 12px;
+		color: #FFFFFF;
+		text-align: left;
+		font-style: normal;
+		text-transform: none;
+
+		border-bottom: 1px solid rgba(70,70,100,0.5);
+
+		box-sizing: border-box;
+	}
+
+	.panel-content {
+		padding: 0 16px;
+		height: 0;
+		flex: 1;
+		overflow: auto;
+	}
+
+	::v-deep(.el-tree) {
+		background: transparent;
+		color: #C3C3DD;
+		font-size: 12px;
+
+		.el-tree-node {
+			line-height: 30px !important;
+
+			.el-tree-node__content {
+				display: flex;
+				align-items: center;
+				height: 40px !important;
+				line-height: 40px !important;
+				border-bottom: 1px solid rgba(70,70,100,0.5);
+				box-sizing: border-box;
+			}
+
+			.el-tree-node__children {
+				.el-tree-node__content {
+					border: none;
+				}
+			}
+		}
+
+	}
+}
+
+
+
+.layer-panel :deep(.el-tree-node__label) {
+	color: #C3C3DD;
+}
+
+.layer-panel :deep(.el-tree-node__expand-icon) {
+	order: 3;
+	margin-left: auto;
+}
+
+.layer-panel :deep(.el-tree-node__expand-icon.is-leaf) {
+	visibility: hidden;
+}
+
+.layer-panel :deep(.el-checkbox) {
+	order: 1;
+}
+
+.layer-panel :deep(.el-tree-node__label) {
+	order: 2;
+}
+
+.layer-panel :deep(.el-tree-node__expand-icon:not(.is-leaf) ~ .el-checkbox) {
+	display: none;
+}
+
+.layer-panel :deep(.el-tree-node__content:hover) {
+	background: transparent !important;
+}
+
+.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content) {
+	background: transparent !important;
+	color: #FFFFFF;
+}
+
+.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content .el-tree-node__label) {
+	color: #FFFFFF;
+}
+
+.layer-panel :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
+	background-color: #023AFF;
+	border-color: #023AFF;
+}
+
+.layer-panel :deep(.el-checkbox__inner) {
+	background-color: transparent;
+	border-color: #A1A3D4;
+}
+</style>
diff --git a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
index 33235ae..7a4cfcb 100644
--- a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -2,7 +2,7 @@
  * @Author       : yuan
  * @Date         : 2026-01-07 15:17:54
  * @LastEditors  : yuan
- * @LastEditTime : 2026-01-08 17:04:11
+ * @LastEditTime : 2026-01-09 16:54:31
  * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
  * @Description  : 
  * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -59,7 +59,7 @@
 import EmptyState from './EmptyState.vue'
 import zcsbLogo from '@/assets/images/dataCockpit/zcsb.png'
 import fzsbLogo from '@/assets/images/dataCockpit/fzsb.png'
-import { deviceSearchApi } from '@/api/dataCockpit'
+import { deviceSearchApi, deviceStatisticsApi } from '@/api/dataCockpit'
 
 const props = defineProps({
 	collapsed: {
@@ -68,7 +68,7 @@
 	}
 })
 
-const emit = defineEmits(['update:collapsed'])
+const emit = defineEmits(['update:collapsed', 'update:onlineDevices'])
 
 const collapsedModel = computed({
 	get: () => props.collapsed,
@@ -79,13 +79,13 @@
 	{
 		id: 1,
 		name: '侦测设备',
-		val: 169,
+		val: 0,
 		logo: zcsbLogo
 	},
 	{
 		id: 2,
 		name: '反制设备',
-		val: 169,
+		val: 0,
 		logo: fzsbLogo
 	}
 ])
@@ -148,6 +148,7 @@
 		const res = await deviceSearchApi(params)
 		const records = res?.data?.data?.records ?? []
 		counterDeviceStatusList.value = records.map(formatDeviceItem)
+		emit('update:onlineDevices', records)
 	} finally {
 		const elapsed = Date.now() - startAt
 		if (elapsed < minLoadingMs) {
@@ -159,7 +160,26 @@
 
 onMounted(() => {
 	fetchDeviceList()
+	fetchDeviceStatistics()
 })
+
+const normalizeType = (value) => {
+	if (value === 1 || value === '1') return '侦测设备'
+	if (value === 2 || value === '2') return '反制设备'
+	if (value === 'detect') return '侦测设备'
+	if (value === 'counter') return '反制设备'
+	return value
+}
+
+const fetchDeviceStatistics = async () => {
+	const res = await deviceStatisticsApi()
+	const list = res?.data?.data ?? []
+	const map = new Map(list.map((item) => [normalizeType(item.type), item.count]))
+	categoryList.value = categoryList.value.map((item) => ({
+		...item,
+		val: map.has(item.name) ? map.get(item.name) : item.val
+	}))
+}
 </script>
 
 <style lang="scss" scoped>
@@ -247,17 +267,12 @@
 	position: absolute;
 	top: 50%;
 	right: 357px;
-	background: rgba(17, 23, 34, 0.8);
-	border: 1px solid #333355;
-	border-right: none;
-	border-radius: 4px 0 0 4px;
 	cursor: pointer;
 	display: flex;
 	align-items: center;
 	justify-content: center;
 	color: #fff;
 	z-index: 10;
-	backdrop-filter: blur(5px);
 	transform: translateY(-50%);
 
 	.arrow {
diff --git a/applications/drone-command/src/views/dataCockpit/index.vue b/applications/drone-command/src/views/dataCockpit/index.vue
index f2abbe5..5226ea0 100644
--- a/applications/drone-command/src/views/dataCockpit/index.vue
+++ b/applications/drone-command/src/views/dataCockpit/index.vue
@@ -2,7 +2,7 @@
  * @Author       : yuan
  * @Date         : 2026-01-06 16:35:50
  * @LastEditors  : yuan
- * @LastEditTime : 2026-01-08 09:45:25
+ * @LastEditTime : 2026-01-09 16:34:39
  * @FilePath     : \applications\drone-command\src\views\dataCockpit\index.vue
  * @Description  : 
  * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -10,9 +10,9 @@
 -->
 <template>
   <div class="page-container">
-    <MapContainer />  
+    <MapContainer :online-devices="onlineDevices" :left-collapsed="leftCollapsed" />  
     <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" />  
-    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" />  
+    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" v-model:onlineDevices="onlineDevices" />  
     <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />  
   </div>
 </template>
@@ -27,6 +27,7 @@
   const leftCollapsed = ref(false);
 
   const rightCollapsed = ref(false);
+  const onlineDevices = ref([]);
 
   const onSelectWarning = (type) => {
     leftActiveId.value = type;
@@ -52,7 +53,7 @@
     bottom: 0;
     width: 401px;
     z-index: 9;
-    backdrop-filter: blur(5px);
+    backdrop-filter: blur(2px);
 
     ::v-deep(.wrapper) {
       display: flex;
@@ -63,6 +64,7 @@
       padding: 20px;
       width: 300px;
       background: rgba(0,0,0,0.3);
+      backdrop-filter: blur(16px);
       border-radius: 10px 10px 10px 10px;
     }
   }

--
Gitblit v1.9.3