Quellcode durchsuchen

Merge branch 'master' of http://39.97.59.228:8013/hrx/hsq-pro

bobo04052021@163.com vor 18 Stunden
Ursprung
Commit
c24c41d9d3
20 geänderte Dateien mit 618 neuen und 1052 gelöschten Zeilen
  1. 118 3
      src/layouts/default/sider/bottomSideder.vue
  2. 2 1
      src/views/vent/monitorManager/hsqHome/components/AlarmDay.vue
  3. 2 1
      src/views/vent/monitorManager/hsqHome/components/AlarmMonth.vue
  4. 2 1
      src/views/vent/monitorManager/hsqHome/components/AlarmPoint.vue
  5. 1 46
      src/views/vent/monitorManager/hsqHome/components/DeviceCenter.vue
  6. 12 345
      src/views/vent/monitorManager/hsqHome/components/DeviceControl.vue
  7. 40 10
      src/views/vent/monitorManager/hsqHome/components/DeviceLeft.vue
  8. 2 68
      src/views/vent/monitorManager/hsqHome/components/DeviceRight.vue
  9. 13 318
      src/views/vent/monitorManager/hsqHome/components/DeviceTree.vue
  10. 8 59
      src/views/vent/monitorManager/hsqHome/components/DeviceView.vue
  11. 3 45
      src/views/vent/monitorManager/hsqHome/components/SystemLeftBottom.vue
  12. 0 38
      src/views/vent/monitorManager/hsqHome/components/SystemLeftTop.vue
  13. 1 5
      src/views/vent/monitorManager/hsqHome/components/SystemRightTop.vue
  14. 3 13
      src/views/vent/monitorManager/hsqHome/components/WarnCenTop.vue
  15. 1 9
      src/views/vent/monitorManager/hsqHome/components/common/commonEchart.vue
  16. 234 26
      src/views/vent/monitorManager/hsqHome/components/type.ts
  17. 3 28
      src/views/vent/monitorManager/hsqHome/hsqHome.data.ts
  18. 163 0
      src/views/vent/monitorManager/hsqHome/hsqPagination.less
  19. 8 19
      src/views/vent/monitorManager/hsqHome/index.vue
  20. 2 17
      src/views/vent/monitorManager/hsqHome/videoPlayer.vue

+ 118 - 3
src/layouts/default/sider/bottomSideder.vue

@@ -51,13 +51,23 @@
       </div>
     </div>
   </div>
-  <div v-else-if="isShowMenu == 0" class="menu-show-icon">
-    <div class="icon" :class="themeIcon == 'styleTwo' ? 'icon-2' : 'icon-1'" @click="openMenu"></div>
+  <div
+    v-else-if="isShowMenu == 0"
+    class="menu-show-icon"
+    :class="{ dragging: iconDragging }"
+    :style="menuIconStyle"
+    @mousedown="onIconMouseDown"
+  >
+    <div
+      class="icon"
+      :class="themeIcon == 'styleTwo' ? 'icon-2' : 'icon-1'"
+      @click="onIconClick"
+    ></div>
   </div>
 </template>
 
 <script lang="ts">
-  import { defineComponent, nextTick, onMounted, ref, unref } from 'vue';
+  import { computed, defineComponent, nextTick, onMounted, onUnmounted, ref, unref } from 'vue';
   import type { Menu } from '/@/router/types';
   import FourBorderBg from '/@/components/vent/fourBorderBg.vue';
   import { SvgIcon } from '/@/components/Icon';
@@ -72,6 +82,11 @@
   import { useAppStore } from '/@/store/modules/app';
   import { router } from '/@/router';
 
+  /** 菜单图标默认尺寸(与样式一致) */
+  const MENU_ICON_SIZE = 60;
+  /** 超过该位移视为拖动,不触发点击打开菜单 */
+  const DRAG_CLICK_THRESHOLD = 5;
+
   export default defineComponent({
     name: 'BottomSider',
     components: { FourBorderBg, SvgIcon },
@@ -89,6 +104,87 @@
       // const themeIcon = appStore.getDarkMode;
       const themeIcon = ref('styleTwo');
 
+      /** 菜单图标位置(拖动后改为 left/top) */
+      const iconPos = ref<{ left: number; top: number | null; bottom: number }>({
+        left: 5,
+        top: null,
+        bottom: 5,
+      });
+      const iconDragging = ref(false);
+      let iconDragMoved = false;
+      let iconDragStartX = 0;
+      let iconDragStartY = 0;
+      let iconDragOriginLeft = 0;
+      let iconDragOriginTop = 0;
+
+      const menuIconStyle = computed(() => {
+        if (iconPos.value.top != null) {
+          return {
+            left: `${iconPos.value.left}px`,
+            top: `${iconPos.value.top}px`,
+            bottom: 'auto',
+          };
+        }
+        return {
+          left: `${iconPos.value.left}px`,
+          bottom: `${iconPos.value.bottom}px`,
+        };
+      });
+
+      function clampIconPos(left: number, top: number) {
+        const maxLeft = Math.max(0, window.innerWidth - MENU_ICON_SIZE);
+        const maxTop = Math.max(0, window.innerHeight - MENU_ICON_SIZE);
+        return {
+          left: Math.min(Math.max(0, left), maxLeft),
+          top: Math.min(Math.max(0, top), maxTop),
+        };
+      }
+
+      function onIconMouseMove(e: MouseEvent) {
+        if (!iconDragging.value) return;
+        const dx = e.clientX - iconDragStartX;
+        const dy = e.clientY - iconDragStartY;
+        if (Math.abs(dx) > DRAG_CLICK_THRESHOLD || Math.abs(dy) > DRAG_CLICK_THRESHOLD) {
+          iconDragMoved = true;
+        }
+        const next = clampIconPos(iconDragOriginLeft + dx, iconDragOriginTop + dy);
+        iconPos.value = { left: next.left, top: next.top, bottom: 0 };
+      }
+
+      function onIconMouseUp() {
+        iconDragging.value = false;
+        document.removeEventListener('mousemove', onIconMouseMove);
+        document.removeEventListener('mouseup', onIconMouseUp);
+      }
+
+      /** 开始拖动菜单图标(与点击打开菜单区分) */
+      function onIconMouseDown(e: MouseEvent) {
+        if (e.button !== 0) return;
+        e.preventDefault();
+        e.stopPropagation();
+        const target = e.currentTarget as HTMLElement | null;
+        if (!target) return;
+        const rect = target.getBoundingClientRect();
+        iconDragMoved = false;
+        iconDragging.value = true;
+        iconDragStartX = e.clientX;
+        iconDragStartY = e.clientY;
+        iconDragOriginLeft = rect.left;
+        iconDragOriginTop = rect.top;
+        iconPos.value = { left: rect.left, top: rect.top, bottom: 0 };
+        document.addEventListener('mousemove', onIconMouseMove);
+        document.addEventListener('mouseup', onIconMouseUp);
+      }
+
+      function onIconClick(e: Event) {
+        if (iconDragMoved) {
+          e.stopPropagation();
+          iconDragMoved = false;
+          return;
+        }
+        openMenu(e);
+      }
+
       function selectMenu(e: Event, programMenu) {
         e.stopPropagation();
         currentParentRoute.value = programMenu;
@@ -153,14 +249,25 @@
         const index = menuModules.value.findIndex((menu) => menu.children && menu.children.length > 0);
         currentParentRoute.value = menuModules.value[index];
       });
+
+      onUnmounted(() => {
+        document.removeEventListener('mousemove', onIconMouseMove);
+        document.removeEventListener('mouseup', onIconMouseUp);
+        document.removeEventListener('click', closeMenu);
+      });
+
       return {
         themeIcon,
         menuModules,
         isShowMenu,
+        menuIconStyle,
+        iconDragging,
         handleMenuClick,
         openMenu,
         closeMenu,
         selectMenu,
+        onIconMouseDown,
+        onIconClick,
         go,
         geHome,
         currentParentRoute,
@@ -308,11 +415,19 @@
     bottom: 5px;
     left: 5px;
     z-index: 1000000;
+    cursor: grab;
+    user-select: none;
+    touch-action: none;
+
+    &.dragging {
+      cursor: grabbing;
+    }
 
     .icon {
       width: 60px;
       height: 60px;
       position: relative;
+      pointer-events: auto;
 
       &:before {
         content: '';

+ 2 - 1
src/views/vent/monitorManager/hsqHome/components/AlarmDay.vue

@@ -18,7 +18,8 @@
 <script setup lang="ts">
 import { computed, type PropType } from 'vue'
 import dayjs from 'dayjs'
-import commonEchart, { type CommonBarSeriesItem } from './common/commonEchart.vue'
+import commonEchart from './common/commonEchart.vue'
+import type { CommonBarSeriesItem } from './type'
 import type { AlarmDayCountItem } from './type'
 
 const props = defineProps({

+ 2 - 1
src/views/vent/monitorManager/hsqHome/components/AlarmMonth.vue

@@ -18,7 +18,8 @@
 <script setup lang="ts">
 import { computed, type PropType } from 'vue'
 import dayjs from 'dayjs'
-import commonEchart, { type CommonBarSeriesItem } from './common/commonEchart.vue'
+import commonEchart from './common/commonEchart.vue'
+import type { CommonBarSeriesItem } from './type'
 import type { AlarmMonCountItem } from './type'
 
 const props = defineProps({

+ 2 - 1
src/views/vent/monitorManager/hsqHome/components/AlarmPoint.vue

@@ -17,7 +17,8 @@
 
 <script setup lang="ts">
 import { computed, type PropType } from 'vue'
-import commonEchart, { type CommonBarSeriesItem } from './common/commonEchart.vue'
+import commonEchart from './common/commonEchart.vue'
+import type { CommonBarSeriesItem } from './type'
 import type { AlarmDevCountItem } from './type'
 
 const props = defineProps({

+ 1 - 46
src/views/vent/monitorManager/hsqHome/components/DeviceCenter.vue

@@ -366,52 +366,7 @@ import { reactive, ref, watch } from 'vue'
 import basicBorder from './basicBorder.vue'
 import {hsqComControl, getSysCfg, setSysCfg, managesysList, monitorSystem } from '../hsqHome.api'
 import { useMessage } from '/@/hooks/web/useMessage'
-
-/** 前端配置表单(与 ZC_SYSTEM_CFG_S 字段对应) */
-interface SysCfgForm {
-  findFire: boolean
-  findMove: boolean
-  fireTrace: boolean
-  findDxm: boolean
-  camZoomCapture: boolean
-  fireMeasureTemp: boolean
-  fireMinThreshold: string | number
-  fireMaxThreshold: string | number
-  fireMinArea: string | number
-  fireMaxArea: string | number
-  radiation: string | number
-  emissivity: string | number
-  environmentTemp: string | number
-  grayOffset: string | number
-  fireRadius: string | number
-  shakeRadius: string | number
-  stillDuration: string | number
-  ptzSemiCircleDuration: string | number
-  stayDuration: string | number
-  dxmLength: string | number
-  dxmRadius: string | number
-  deviceId: string | number
-  deviceName: string
-  ip: string
-  subnetMask: string
-  gateway: string
-  cmdPort: string | number
-  alarmPort: string | number
-  horFov: string | number
-  verFov: string | number
-  majorStreamReso: string | number
-  minorStreamReso: string | number
-  srvCenterIp: string
-  srvCenterPort: string | number
-  showMaxTemp: boolean
-  showMinTemp: boolean
-  showAvgTemp: boolean
-  showRunMode: boolean
-  showRunStatus: boolean
-  enableCenterService: boolean
-  /** 版本区占位字段(未改版本获取逻辑) */
-  ld: string
-}
+import type { SysCfgForm } from './type'
 
 const BOOL_KEYS = [
   'findFire',

+ 12 - 345
src/views/vent/monitorManager/hsqHome/components/DeviceControl.vue

@@ -1,5 +1,5 @@
 <template>
-  <!-- 设备控制组件:左侧面板下方,包含设备选择、云台控制、扫描/调色模式、红外选项等 -->
+  <!-- 设备控制组件:左侧面板下方,包含设备选择、云台控制、扫描/调色模式等 -->
   <div class="device-control">
     <!-- 控制设备下拉选择 -->
     <div class="device-select-row">
@@ -79,71 +79,8 @@
           </ul>
         </div>
       </div>
-
-      <!-- 底部勾选 -->
-      <div class="footer-checks">
-        <label v-for="item in footerCheckItems" :key="item.key" class="check-item">
-          <input
-            type="checkbox"
-            :checked="getFooterChecked(item.key)"
-            @change="onFooterCheckChange(item.key, ($event.target as HTMLInputElement).checked)"
-          />
-          <span class="check-box">
-            <svg v-if="getFooterChecked(item.key)" viewBox="0 0 12 12" width="10" height="10">
-              <path
-                d="M2 6.2 L4.8 9 L10 3.2"
-                fill="none"
-                stroke="#fff"
-                stroke-width="1.8"
-                stroke-linecap="round"
-                stroke-linejoin="round"
-              />
-            </svg>
-          </span>
-          <span class="check-label">{{ item.label }}</span>
-        </label>
-      </div>
     </template>
 
-    <!-- 红外控制 -->
-    <div v-else-if="activeTab === 'ir'" class="ir-panel">
-      <div class="ir-body">
-        <div class="ir-controls">
-          <div v-for="(row, rowIndex) in irControlRows" :key="rowIndex" class="ir-row">
-            <div v-for="item in row" :key="`${rowIndex}-${item.key}`" class="ir-group">
-              <a-tooltip :title="item.plusTip" placement="top" overlay-class-name="lens-btn-tip">
-                <a-button
-                  class="ir-btn"
-                  type="text"
-                  @mousedown="emitAction(item.key, 'plus')"
-                  @mouseup="emitAction(item.key, 'stop')"
-                  @mouseleave="emitAction(item.key, 'stop')"
-                >+</a-button>
-              </a-tooltip>
-              <span class="ir-icon" v-html="item.icon"></span>
-              <a-tooltip :title="item.minusTip" placement="top" overlay-class-name="lens-btn-tip">
-                <a-button
-                  class="ir-btn"
-                  type="text"
-                  @mousedown="emitAction(item.key, 'minus')"
-                  @mouseup="emitAction(item.key, 'stop')"
-                  @mouseleave="emitAction(item.key, 'stop')"
-                >−</a-button>
-              </a-tooltip>
-            </div>
-          </div>
-        </div>
-        <a-tooltip title="自动聚焦" placement="top" overlay-class-name="lens-btn-tip">
-          <a-button class="ir-aim-btn" type="text" aria-label="自动聚焦" @click="emitAction('irAim', 'click')">
-            <span class="ir-aim-wrap">
-              <SvgIcon class="ir-aim-bg" name="yuandi" :size="58" />
-              <SvgIcon class="ir-aim-fg" name="dingwei" :size="34" />
-            </span>
-          </a-button>
-        </a-tooltip>
-      </div>
-    </div>
-
     <!-- 激光控制 -->
     <div v-else-if="activeTab === 'laser'" class="laser-panel">
       <div class="laser-body">
@@ -220,54 +157,18 @@
 </template>
 
 <script setup lang="ts">
-import { computed, onMounted, onUnmounted, ref, watch, type PropType, type Ref } from 'vue'
-import { SvgIcon } from '/@/components/Icon'
+import { computed, onMounted, onUnmounted, ref, watch, type PropType } from 'vue'
 import { tabsPtz, lensControls, laserActions } from '../hsqHome.data'
 import { hsqComControl, setRunModeCfg, setPesudoColor } from '../hsqHome.api'
 import { useMessage } from '/@/hooks/web/useMessage'
-import { IrControlItem } from './type'
-
-/** 方向轮盘按钮 */
-interface DirectionButtonItem {
-  value: 'up' | 'down' | 'left' | 'right'
-  label: string
-}
-
-/** 模式选择器配置 */
-interface ModeSelectorItem {
-  key: 'scan' | 'color'
-  label: string
-  current: string
-  options: string[]
-}
-
-/** 底部勾选项 */
-interface FooterCheckItem {
-  key: 'irZoom' | 'irControl'
-  label: string
-}
-
-/** 配置类模式切换参数 */
-interface CfgModeSwitchOption {
-  value: string
-  current: Ref<string>
-  emitChange: (value: string) => void
-  request: (deviceid: string, code: number) => Promise<unknown>
-  codeMap: Record<string, number>
-  successText: string
-  failText: string
-  logLabel: string
-}
-
-/** 云台方向指令 */
-interface PtzControlParam {
-  deviceid: string
-  paramcode: string
-  noCheckPassword: boolean
-  cmd?: string
-  stop: number
-  speed: number
-}
+import type {
+  IrControlItem,
+  DirectionButtonItem,
+  ModeSelectorItem,
+  CfgModeSwitchOption,
+  PtzControlParam,
+  LensPtzCmdOption,
+} from './type'
 
 const props = defineProps({
   deviceOptions: {
@@ -289,7 +190,6 @@ const emit = defineEmits<{
   (e: 'speedChange', value: number): void
   (e: 'scanModeChange', value: string): void
   (e: 'colorModeChange', value: string): void
-  (e: 'optionChange', payload: { irZoom: boolean; irControl: boolean }): void
   (e: 'defogChange', payload: {
     electronicDefog: boolean
     opticalDefog: boolean
@@ -314,10 +214,6 @@ const isDirectionPadDisabled = computed(() => scanMode.value !== '手动')
 const colorMode = ref('白热')
 /** 当前展开的下拉菜单(扫描/调色/无) */
 const openMenu = ref<'scan' | 'color' | ''>('')
-/** 红外变倍开关 */
-const irZoom = ref(true)
-/** 红外控制开关 */
-const irControl = ref(false)
 /** 未选摄像头提示 */
 const deviceTipVisible = ref(false)
 
@@ -366,11 +262,6 @@ const directionButtons: DirectionButtonItem[] = [
   { value: 'right', label: '右' },
   { value: 'down', label: '下' },
 ]
-/** 底部勾选项 */
-const footerCheckItems: FooterCheckItem[] = [
-  { key: 'irZoom', label: '红外变倍' },
-  { key: 'irControl', label: '红外控制' },
-]
 /** 焦距 / 焦点 / 光圈 / 方向按住方向,用于松开时区分对应停止指令 */
 const lastZoomAction = ref<'plus' | 'minus' | null>(null)
 const lastFocusAction = ref<'plus' | 'minus' | null>(null)
@@ -379,21 +270,9 @@ const lastPanAction = ref<'up' | 'down' | 'left' | 'right' | null>(null)
 
 const { createMessage } = useMessage()
 
-/** 复用镜头图标:聚焦=摄像机,变焦=放大镜 */
-const focusIcon = lensControls.find((item) => item.key === 'focus')?.icon || ''
+/** 复用镜头图标:变焦=放大镜(激光变倍) */
 const zoomIcon = lensControls.find((item) => item.key === 'zoom')?.icon || ''
 
-/** 红外控制两行:每行「聚焦 + 变焦」 */
-const irControlRows: IrControlItem[][] = [
-  [
-    { key: 'focus', icon: focusIcon, plusTip: '近景微调', minusTip: '远景微调' },
-    { key: 'zoom', icon: zoomIcon, plusTip: '长焦微调', minusTip: '短焦微调' },
-  ],
-  [
-    { key: 'focus', icon: focusIcon, plusTip: '近景', minusTip: '远景' },
-    { key: 'zoom', icon: zoomIcon, plusTip: '长焦', minusTip: '短焦' },
-  ],
-]
 /** 激光变倍:两组放大镜加减 */
 const laserZoomControls: IrControlItem[] = [
   { key: 'laserZoom1', icon: zoomIcon },
@@ -492,16 +371,6 @@ function resolveChannelType() {
   return 2
 }
 
-interface LensPtzCmdOption {
-  /** 按下 plus 时的 cmd */
-  plusCmd: number
-  /** 按下 minus 时的 cmd */
-  minusCmd: number
-  /** 记录按住方向的 ref */
-  lastAction: typeof lastZoomAction
-  logLabel: string
-}
-
 /**
  * 镜头 PTZ 控制(焦距 / 焦点):调用 hsqComControl
  * 按下:cmd=对应指令, stop=0, speed=界面速度, type=可见光2/热成像1
@@ -671,15 +540,6 @@ function getControlDeviceId() {
   return String(currentDevice.value || '')
 }
 
-function getFooterChecked(key: FooterCheckItem['key']) {
-  return key === 'irZoom' ? irZoom.value : irControl.value
-}
-
-function onFooterCheckChange(key: FooterCheckItem['key'], checked: boolean) {
-  if (key === 'irZoom') irZoom.value = checked
-  else irControl.value = checked
-}
-
 /** 配置类模式切换(扫描 / 调色)共用逻辑 */
 async function switchCfgMode(option: CfgModeSwitchOption) {
   const { value, current, emitChange, request, codeMap, successText, failText, logLabel } = option
@@ -807,10 +667,6 @@ function onDocClick() {
 watch(activeTab, (key) => emit('tabChange', key))
 /** 监听速度变化 */
 watch(speed, (val) => emit('speedChange', val))
-/** 监听红外选项变化 */
-watch([irZoom, irControl], () => {
-  emit('optionChange', { irZoom: irZoom.value, irControl: irControl.value })
-})
 /** 监听透雾选项变化 */
 watch([electronicDefog, opticalDefog, fogIntensity, lightIntensity], () => {
   emit('defogChange', {
@@ -954,7 +810,7 @@ onUnmounted(() => {
   }
 }
 
-// 功能 Tab 切换栏(云台控制/红外控制/激光/透雾)
+// 功能 Tab 切换栏(云台控制/激光/透雾)
 .control-tabs {
   display: flex;
   align-items: stretch;
@@ -1352,149 +1208,6 @@ onUnmounted(() => {
   }
 }
 
-// 红外控制面板:--image-bg1 底图框 + 2×2 调节条 + 右侧一键聚焦
-.ir-panel {
-  min-height: 0;
-  width: 100%;
-  overflow: hidden;
-  box-sizing: border-box;
-  // padding: 5px;
-  display: flex;
-  flex-direction: column;
-}
-
-.ir-body {
-  flex: 1;
-  min-height: 0;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: space-between;
-  gap: clamp(16px, 3vw, 28px);
-  padding: clamp(14px, 2.5vh, 22px) clamp(14px, 2.5vw, 24px);
-  box-sizing: border-box;
-  background: var(--image-bg1) no-repeat;
-  background-size: 100% 100%;
-}
-
-.ir-controls {
-  flex: 1;
-  min-width: 0;
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: clamp(10px, 2vh, 16px) clamp(12px, 2.5vw, 20px);
-}
-
-.ir-row {
-  display: contents;
-}
-
-.ir-group {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  min-width: 0;
-  height: 30px;
-  padding: 18px;
-  box-sizing: border-box;
-  background: var(--image-bg2) no-repeat;
-  background-size: 100% 100%;
-  // background: rgba(10, 40, 72, 0.72);
-  // border-radius: 6px;
-  // border: 1px solid rgba(40, 100, 150, 0.28);
-}
-
-.ir-btn {
-  width: 22px !important;
-  height: 22px !important;
-  min-width: 22px !important;
-  padding: 0 !important;
-  border: none !important;
-  background: transparent !important;
-  color: #fff !important;
-  font-size: 18px !important;
-  font-weight: 400 !important;
-  line-height: 1 !important;
-  box-shadow: none !important;
-
-  &:hover,
-  &:focus {
-    color: #cfe9ff !important;
-    background: transparent !important;
-  }
-
-  &:active {
-    transform: scale(0.92);
-    color: var(--dc-accent) !important;
-  }
-}
-
-.ir-icon {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 22px;
-  height: 22px;
-  flex-shrink: 0;
-  color: #fff;
-
-  :deep(svg) {
-    display: block;
-    width: 18px;
-    height: 18px;
-  }
-}
-
-.ir-aim-btn {
-  width: 64px !important;
-  height: 64px !important;
-  min-width: 64px !important;
-  padding: 0 !important;
-  margin: 0 !important;
-  flex-shrink: 0;
-  border: none !important;
-  border-radius: 50% !important;
-  background: transparent !important;
-  box-shadow: none !important;
-  display: inline-flex !important;
-  align-items: center;
-  justify-content: center;
-
-  &:hover,
-  &:focus {
-    background: transparent !important;
-    filter: brightness(1.08);
-  }
-
-  &:active {
-    transform: scale(0.96);
-  }
-}
-
-.ir-aim-wrap {
-  position: relative;
-  width: 58px;
-  height: 58px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: 50%;
-  box-shadow: 0 0 10px rgba(0, 183, 255, 0.45);
-}
-
-.ir-aim-bg {
-  display: block;
-}
-
-.ir-aim-fg {
-  position: absolute;
-  left: 50%;
-  top: 50%;
-  transform: translate(-50%, -50%);
-  color: #fff;
-  fill: #fff;
-}
-
 // 激光控制面板:上排四按钮 + 下排两组变倍,按钮样式对齐高级看板
 .laser-panel {
   flex: 1;
@@ -1823,52 +1536,6 @@ onUnmounted(() => {
   white-space: nowrap;
 }
 
-// 底部勾选框区域(红外变倍/红外控制)
-.footer-checks {
-  display: flex;
-  align-items: center;
-  gap: 28px;
-  margin-top: auto;
-  padding-top: 4px;
-  flex-shrink: 0;
-
-  .check-item {
-    display: inline-flex;
-    align-items: center;
-    gap: 6px;
-    cursor: pointer;
-    margin: 0;
-
-    input {
-      display: none;
-    }
-
-    // 自定义复选框
-    .check-box {
-      width: 14px;
-      height: 14px;
-      box-sizing: border-box;
-      border: 1px solid rgba(0, 183, 255, 0.85);
-      border-radius: 2px;
-      background: rgba(0, 30, 55, 0.85);
-      display: inline-flex;
-      align-items: center;
-      justify-content: center;
-    }
-
-    input:checked+.check-box {
-      background: #1aa0e0;
-      border-color: #4ec8ff;
-      box-shadow: 0 0 6px rgba(0, 183, 255, 0.55);
-    }
-
-    .check-label {
-      color: #fff;
-      font-size: 12px;
-    }
-  }
-}
-
 // 隐藏滚动条
 ::-webkit-scrollbar {
   display: none;

+ 40 - 10
src/views/vent/monitorManager/hsqHome/components/DeviceLeft.vue

@@ -16,11 +16,8 @@
           </div>
         </div>
         <div class="content-box">
-          <div
-            :class="['basic-device', { 'basic-device-active': active == index }]"
-            v-for="(item, index) in filteredListData"
-            :key="index"
-          >
+          <div :class="['basic-device', { 'basic-device-active': active == index }]"
+            v-for="(item, index) in filteredListData" :key="index">
             <div class="icon-left">
               <!-- 状态指示圆点:根据报警级别显示不同颜色(绿色正常/红色报警) -->
               <div :class="item.netStatus ? 'icon-item' : 'icon-item-warn'"></div>
@@ -31,7 +28,8 @@
               <div class="title-text">{{ item.strserno || '-' }}</div>
               <!-- 操作按钮:详情 / 恢复默认配置 -->
               <div class="title-btn">
-                <div :class="active == index ? 'btn-active' : 'btn'" @click="handlerClick('config', item, index)">详情</div>
+                <div :class="active == index ? 'btn-active' : 'btn'" @click="handlerClick('config', item, index)">详情
+                </div>
                 <div class="btn" @click="handlerClick('restoreDefault', item, index)">恢复默认配置</div>
               </div>
             </div>
@@ -151,6 +149,18 @@ function handlerClick(type: string, item: any, index: number) {
   active.value = index
 }
 
+/** 是否已完成初始化默认激活详情(避免轮询重复触发) */
+let hasAutoActivatedDetail = false
+
+/** 激活列表第一项详情(与手动点击「详情」一致) */
+function activateFirstDetail() {
+  if (hasAutoActivatedDetail) return
+  const list = filteredListData.value
+  if (!list.length) return
+  hasAutoActivatedDetail = true
+  handlerClick('config', list[0], 0)
+}
+
 /**
  * 异步获取设备监控数据的API调用函数
  * 向后端请求指定系统的所有光谱摄像机设备信息
@@ -171,6 +181,8 @@ async function getData() {
 
   // 统计并记录设备总数量,用于标题栏显示
   count.value = sourceList.value.length;
+  // 初始化时若列表已有数据,默认激活第一项详情(仅一次)
+  activateFirstDetail()
 }
 
 /** 搜索条件变化时重置选中项,避免索引越界 */
@@ -203,13 +215,16 @@ async function getManagesysList() {
  * 组件挂载完成后的生命周期钩子
  * 执行组件初始化流程:
  * 1. 先获取系统ID(必须先于数据获取,因为systemId是getData的必要参数)
- * 2. 然后启动定时数据刷新机制(传true表示首次立即执行)
+ * 2. 拉取设备列表并默认激活第一项详情
+ * 3. 启动定时数据刷新机制
  */
 onMounted(async () => {
   // 等待系统ID获取完成,确保后续API调用有正确的参数
   await getManagesysList()
-  // 启动定时监控,true表示首次立即执行一次
-  getMonitor(true)
+  // 首次立即拉取列表(getData 内会默认激活第一项详情)
+  await getData()
+  // 启动后续定时轮询
+  getMonitor()
 })
 
 /**
@@ -383,7 +398,22 @@ onUnmounted(() => {
 
       /* 背景图已含顶部装饰线与切角,取消 title 额外装饰,避免叠线 */
       .basic-title {
-        background: none;
+        // background: none;
+        height: 30px;
+        /* 固定高度 */
+        margin: 0px 20px 5px 32px;
+        /* 外边距:避开左侧图标区域 */
+        padding-left: 10px;
+        /* 左内边距:文字缩进 */
+        display: flex;
+        /* 弹性布局:序列号和按钮组两端对齐 */
+        justify-content: space-between;
+        /* 两端对齐:序列号靠左,按钮靠右 */
+        align-items: center;
+        /* 垂直居中 */
+        background: var(--image-box-bg5) no-repeat bottom;
+        /* 标题行底部装饰线 */
+        background-size: 100% auto;
       }
 
       /* 背景图左上已有圆环,取消原状态外框,仅保留状态圆点 */

+ 2 - 68
src/views/vent/monitorManager/hsqHome/components/DeviceRight.vue

@@ -131,12 +131,6 @@
               <div class="item-label">纬度 : </div>
               <a-input class="item-input" v-model:value="formData.latitude" placeholder="请输入" size="small" />
             </div>
-            <div class="btn-box">
-              <div class="baisc-btn">恢复默认</div>
-              <div class="baisc-btn">
-                <div class="btn-item">保存设置</div>
-              </div>
-            </div>
           </div>
         </basicBorder>
       </div>
@@ -147,38 +141,7 @@
 <script setup lang="ts">
 import { reactive, ref, watch } from 'vue'
 import basicBorder from './basicBorder.vue'
-
-/** 设备配置表单字段 */
-interface DeviceConfigForm {
-  deviceNo: string
-  deviceName: string
-  groupName: string
-  ip: string
-  cmdPort: string
-  hasInfrared: boolean
-  hasHd: boolean
-  hasPtz: boolean
-  irIp: string
-  irPort: string
-  irType: string
-  irUser: string
-  irPassword: string
-  irStreamUrl: string
-  irSubStreamUrl: string
-  horFov: string
-  verFov: string
-  hdIp: string
-  hdPort: string
-  hdType: string
-  hdUser: string
-  hdPassword: string
-  hdStreamUrl: string
-  hdSubStreamUrl: string
-  installHeight: string
-  northDeclination: string
-  longitude: string
-  latitude: string
-}
+import type { DeviceConfigForm } from './type'
 
 const props = defineProps({
   detailData: {
@@ -404,7 +367,7 @@ watch(
   }
 
   .item-label {
-    width: 130px;
+    width: 160px;
     text-align: right;
     margin-right: 5px;
   }
@@ -426,35 +389,6 @@ watch(
     color: #fff;
   }
 
-  .btn-box {
-    width: 100%;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    gap: 10px;
-  }
-
-  .baisc-btn {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    width: 85px;
-    height: 30px;
-    border: 1px solid #01fefc;
-    border-radius: 4px;
-    padding: 3px;
-    cursor: pointer;
-  }
-
-  .btn-item {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    width: 100%;
-    height: 100%;
-    background-color: rgba(32, 166, 169);
-  }
-
   .zxm-checkbox-wrapper {
     margin: 0px 10px 0px 15px;
   }

+ 13 - 318
src/views/vent/monitorManager/hsqHome/components/DeviceTree.vue

@@ -70,11 +70,6 @@
 
     <!-- 高级功能面板 -->
     <div v-show="activeTab === 'advanced'" class="advanced-panel">
-      <div class="adv-section adv-section-between">
-        <a-checkbox v-model:checked="advancedState.videoFrame" class="adv-check">视频框图</a-checkbox>
-        <a-button class="adv-btn" @click="onAdvancedAction('alarmReport')">告警上报</a-button>
-      </div>
-
       <div class="adv-section">
         <a-button class="adv-btn" @click="onAdvancedAction('snapshot')">一键抓拍</a-button>
         <a-button class="adv-btn" @click="onAdvancedAction('viewImages')">查看图片</a-button>
@@ -180,41 +175,6 @@
       {{ placeholderText }}
     </div>
 
-    <!-- 告警上报弹窗 -->
-    <Teleport to="body">
-      <div v-if="alarmReportVisible" :class="['alarm-report-mask', { 'is-minimized': alarmReportMinimized }]" @click.self="closeAlarmReport">
-        <div :class="['alarm-report-panel', { minimized: alarmReportMinimized }]">
-          <div class="alarm-report-header">
-            <span class="alarm-report-title">告警上报</span>
-            <div class="alarm-report-actions">
-              <button type="button" class="alarm-icon-btn" title="最小化" @click="toggleAlarmMinimize">
-                <svg viewBox="0 0 16 16" width="14" height="14">
-                  <rect x="3" y="3" width="10" height="10" fill="none" stroke="currentColor" stroke-width="1.4" />
-                  <path d="M5 11 H11 M5 11 V8" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" />
-                </svg>
-              </button>
-              <button type="button" class="alarm-icon-btn" title="关闭" @click="closeAlarmReport">
-                <svg viewBox="0 0 16 16" width="14" height="14">
-                  <path d="M4 4 L12 12 M12 4 L4 12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
-                </svg>
-              </button>
-            </div>
-          </div>
-
-          <div v-show="!alarmReportMinimized" class="alarm-report-body">
-            <div v-for="item in alarmReportFieldList" :key="item.key" class="alarm-field">
-              <span class="alarm-field-label">{{ item.label }}</span>
-              <div class="alarm-field-value editable">
-                <a-input v-model:value="alarmForm[item.key]" class="alarm-field-input" :bordered="false" :placeholder="item.placeholder" />
-              </div>
-            </div>
-
-            <a-button class="alarm-submit-btn" @click="submitAlarmReport">上报</a-button>
-          </div>
-        </div>
-      </div>
-    </Teleport>
-
     <!-- 未选摄像头提示弹窗(风格对齐报警弹窗) -->
     <Teleport to="body">
       <div v-if="deviceTipVisible" class="device-tip-mask" @click.self="closeDeviceTip">
@@ -569,10 +529,21 @@ import {
   presetSet,
   presetDelete,
 } from '../hsqHome.api';
-import type { DeviceNode, DeviceGroup, AlarmFormState, StatsCount, PresetItem, RecordFileItem, AutoAlarmPopupData, PicVideoRecord } from './type';
+import type {
+  DeviceNode,
+  DeviceGroup,
+  AlarmFormState,
+  AngleState,
+  StatsCount,
+  PresetItem,
+  RecordFileItem,
+  AutoAlarmPopupData,
+  PicVideoRecord,
+  PicViewItem,
+  VideoViewItem,
+} from './type';
 import { tabs, ANGLE_DEFAULT, angleFields, checkTips, alarmReportFieldList, presetToolbarItems, PRESET_EMPTY_ROW_COUNT } from '../hsqHome.data';
 import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
-
 const props = defineProps<{
   treeData: DeviceGroup[];
   selectedCamera: Ref<DeviceNode | null>;
@@ -583,17 +554,6 @@ const emit = defineEmits<{
   (e: 'tabChange', key: string): void;
   (e: 'advancedAction', payload: { action: string; state: Record<string, unknown> }): void;
   (e: 'angleAction', payload: { action: string; state: AngleState }): void;
-  (
-    e: 'alarmReport',
-    payload: {
-      deviceId: string;
-      deviceName: string;
-      longitude: string;
-      latitude: string;
-      operator: string;
-      remark: string;
-    }
-  ): void;
   (e: 'presetAction', key: string): void;
   /** 预置位操作列点击放大:放大当前选中摄像头并显示左侧区域选择,preset 为选中预置位编号 */
   (e: 'regionPreview', preset: number): void;
@@ -615,9 +575,6 @@ const angleInputProps = {
   controls: false,
   bordered: false,
 } as const;
-/** 告警上报弹窗 */
-const alarmReportVisible = ref(false);
-const alarmReportMinimized = ref(false);
 /** 未选摄像头提示弹窗 */
 const deviceTipVisible = ref(false);
 /** 查看图片弹窗 */
@@ -724,7 +681,6 @@ const alarmForm = reactive<AlarmFormState>({
   remark: '',
 });
 const advancedState = reactive({
-  videoFrame: false,
   alarmPopup: false,
   fillLight1: false,
   fillLight2: false,
@@ -764,21 +720,6 @@ const currentRecordMime = computed(() => {
   return item.infraredMime || 'video/mp4';
 });
 
-/** 当前选中设备 */
-const selectedDevice = computed(() => {
-  for (const group of props.treeData || []) {
-    const found = (group.children || []).find((d) => d.id === selectedId.value);
-    if (found) return found;
-  }
-  return null;
-});
-
-/** 操作员:优先登录用户名 */
-const operatorName = computed(() => {
-  const info = userStore.getUserInfo as any;
-  return info?.username || info?.realname || 'admin';
-});
-
 /** 统计在线/离线:优先 netStatus,否则回退示意数据 */
 const statsCount = computed<StatsCount>(() => {
   let online = 0;
@@ -877,15 +818,6 @@ function resolveCameraDeviceId(camera: Record<string, any> | null | undefined) {
   return String(raw).trim();
 }
 
-/** 优先取 camera 字段,其次 device 字段,统一转字符串 */
-function pickCameraField(camera: Record<string, any>, device: DeviceNode | null, key: 'id' | 'name' | 'longitude' | 'latitude') {
-  const fromCamera = camera[key];
-  if (fromCamera != null && fromCamera !== '') return String(fromCamera);
-  const fromDevice = device?.[key];
-  if (fromDevice != null && fromDevice !== '') return String(fromDevice);
-  return '';
-}
-
 /** 解析数值,非法时回退默认值 */
 function toFiniteNumber(value: unknown, fallback: number) {
   const num = Number(value);
@@ -1034,7 +966,6 @@ function toggleSelectAll(checked: boolean) {
 function onAdvancedAction(action: string) {
   emit('advancedAction', { action, state: { ...advancedState } });
   const actionMap: Record<string, () => void> = {
-    alarmReport: tryOpenAlarmReport,
     snapshot: trySnapshot,
     viewImages: tryViewImages,
     viewRecords: tryViewRecords,
@@ -1042,12 +973,6 @@ function onAdvancedAction(action: string) {
   actionMap[action]?.();
 }
 
-/** 有选中摄像头则打开上报弹窗,否则提示选择设备 */
-function tryOpenAlarmReport() {
-  if (!ensureSelectedCamera()) return;
-  openAlarmReport();
-}
-
 /** 关闭未选设备提示 */
 function closeDeviceTip() {
   deviceTipVisible.value = false;
@@ -1109,47 +1034,6 @@ function closeSnapshotView() {
   closePicPreview();
 }
 
-/** 打开告警上报弹窗(预填选中摄像头/设备信息,字段可再手动修改) */
-function openAlarmReport() {
-  const camera = props.selectedCamera || {};
-  const device = selectedDevice.value;
-  alarmForm.deviceId = pickCameraField(camera, device, 'id');
-  alarmForm.deviceName = pickCameraField(camera, device, 'name');
-  alarmForm.longitude = pickCameraField(camera, device, 'longitude');
-  alarmForm.latitude = pickCameraField(camera, device, 'latitude');
-  alarmForm.operator = operatorName.value;
-  alarmForm.remark = '';
-  alarmReportMinimized.value = false;
-  alarmReportVisible.value = true;
-}
-
-/** 关闭告警上报弹窗 */
-function closeAlarmReport() {
-  alarmReportVisible.value = false;
-  alarmReportMinimized.value = false;
-}
-
-/** 最小化 / 还原告警上报弹窗 */
-function toggleAlarmMinimize() {
-  alarmReportMinimized.value = !alarmReportMinimized.value;
-}
-
-/** 提交告警上报 */
-function submitAlarmReport() {
-  const payload = {
-    deviceId: alarmForm.deviceId.trim(),
-    deviceName: alarmForm.deviceName.trim(),
-    longitude: alarmForm.longitude.trim(),
-    latitude: alarmForm.latitude.trim(),
-    operator: alarmForm.operator.trim(),
-    remark: alarmForm.remark.trim(),
-  };
-  emit('alarmReport', payload);
-  emit('advancedAction', { action: 'alarmReportSubmit', state: { ...payload } });
-  createMessage.success('告警上报已提交');
-  closeAlarmReport();
-}
-
 /** 拼接录像完整路径(兼容 Windows / Unix 分隔符) */
 function buildRecordFullPath(filePath: string, fileName: string) {
   if (!filePath) return fileName;
@@ -2366,10 +2250,6 @@ onUnmounted(() => {
   // border: 1px solid rgba(58, 225, 255, 0.12);
   border-radius: 2px;
 
-  &.adv-section-between {
-    justify-content: space-between;
-  }
-
   &.adv-section-last {
     border-bottom-color: rgba(58, 225, 255, 0.12);
   }
@@ -2966,158 +2846,6 @@ onUnmounted(() => {
 
 <!-- 弹窗等挂载到 body,需非 scoped 样式 -->
 <style lang="less">
-/* 告警上报弹窗(Teleport 到 body) */
-.alarm-report-mask {
-  position: fixed;
-  inset: 0;
-  z-index: 1100;
-  background: rgba(0, 10, 24, 0.55);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  &.is-minimized {
-    background: transparent;
-    pointer-events: none;
-    align-items: flex-end;
-    justify-content: flex-end;
-    padding: 24px;
-  }
-}
-
-.alarm-report-panel {
-  width: min(420px, calc(100vw - 40px));
-  background: linear-gradient(180deg, rgba(18, 48, 78, 0.98) 0%, rgba(8, 28, 50, 0.98) 100%);
-  border: 1px solid rgba(0, 183, 255, 0.45);
-  border-radius: 4px;
-  box-shadow: 0 0 24px rgba(0, 183, 255, 0.25);
-  overflow: hidden;
-  pointer-events: auto;
-
-  &.minimized {
-    width: 220px;
-  }
-}
-
-.alarm-report-header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 12px 16px 8px;
-  border-bottom: 1px solid rgba(0, 183, 255, 0.2);
-}
-
-.alarm-report-title {
-  color: #ff7a45;
-  font-size: 16px;
-  font-weight: 600;
-  letter-spacing: 1px;
-}
-
-.alarm-report-actions {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-}
-
-.alarm-icon-btn {
-  width: 24px;
-  height: 24px;
-  padding: 0;
-  border: none;
-  background: transparent;
-  color: #e8f4ff;
-  cursor: pointer;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-
-  &:hover {
-    color: #01fefc;
-  }
-}
-
-.alarm-report-body {
-  padding: 18px 22px 22px;
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-}
-
-.alarm-field {
-  display: flex;
-  align-items: flex-end;
-  gap: 12px;
-}
-
-.alarm-field-label {
-  flex-shrink: 0;
-  width: 72px;
-  color: #fff;
-  font-size: 13px;
-  line-height: 28px;
-}
-
-.alarm-field-value {
-  flex: 1;
-  min-width: 0;
-  text-align: left;
-  color: rgba(232, 244, 255, 0.92);
-  font-size: 13px;
-  line-height: 28px;
-  border-bottom: 1px solid rgba(232, 244, 255, 0.85);
-  padding-bottom: 2px;
-
-  &.editable {
-    border-bottom: 1px solid rgba(232, 244, 255, 0.85);
-  }
-}
-
-.alarm-field-input {
-  width: 100%;
-  background: transparent !important;
-  box-shadow: none !important;
-  border: none !important;
-  padding: 0 !important;
-
-  .ant-input,
-  .zxm-input {
-    background: transparent !important;
-    color: #fff !important;
-    text-align: center !important;
-    padding: 0 2px !important;
-    height: 28px !important;
-    box-shadow: none !important;
-    border: none !important;
-
-    &::placeholder {
-      color: rgba(200, 220, 235, 0.45);
-    }
-  }
-}
-
-.alarm-submit-btn {
-  align-self: center;
-  margin-top: 8px;
-  min-width: 120px !important;
-  height: 32px !important;
-  padding: 0 28px !important;
-  border-radius: 16px !important;
-  border: 1px solid rgba(232, 244, 255, 0.85) !important;
-  background: transparent !important;
-  color: #fff !important;
-  font-size: 14px !important;
-  letter-spacing: 2px;
-  box-shadow: none !important;
-
-  &:hover,
-  &:focus {
-    border-color: #01fefc !important;
-    color: #01fefc !important;
-    background: rgba(1, 254, 252, 0.08) !important;
-  }
-}
-
 /* 未选设备提示弹窗(风格对齐报警自动弹窗) */
 .device-tip-mask {
   position: fixed;
@@ -3677,39 +3405,6 @@ onUnmounted(() => {
   flex-shrink: 0;
 }
 
-.pic-view-footer :deep(.zxm-pagination),
-.pic-view-footer :deep(.ant-pagination) {
-  color: #cfe9ff;
-}
-
-.pic-view-footer :deep(.zxm-pagination-item),
-.pic-view-footer :deep(.zxm-pagination-prev),
-.pic-view-footer :deep(.zxm-pagination-next),
-.pic-view-footer :deep(.ant-pagination-item),
-.pic-view-footer :deep(.ant-pagination-prev),
-.pic-view-footer :deep(.ant-pagination-next) {
-  min-width: 26px;
-  height: 26px;
-  line-height: 24px;
-  border: 1px solid rgba(58, 225, 255, 0.45);
-  background: rgba(16, 64, 100, 0.85);
-}
-
-.pic-view-footer :deep(.zxm-pagination-item a),
-.pic-view-footer :deep(.zxm-pagination-prev button),
-.pic-view-footer :deep(.zxm-pagination-next button),
-.pic-view-footer :deep(.ant-pagination-item a),
-.pic-view-footer :deep(.ant-pagination-prev button),
-.pic-view-footer :deep(.ant-pagination-next button) {
-  color: #cfe9ff;
-}
-
-.pic-view-footer :deep(.zxm-pagination-item-active),
-.pic-view-footer :deep(.ant-pagination-item-active) {
-  border-color: #01fefc;
-  background: rgba(32, 166, 169, 0.85);
-}
-
 /* 查看录像弹窗 */
 .record-view-mask {
   position: fixed;

+ 8 - 59
src/views/vent/monitorManager/hsqHome/components/DeviceView.vue

@@ -2,7 +2,7 @@
   <!-- 设备画面组件:右侧面板上方,包含操作按钮和 2×4 监控画面网格 -->
   <div class="device-view">
     <div class="device-view-content">
-      <!-- 顶部操作按钮:最新热源 / 最新闯入 / 最新烟雾 / 关闭声光报警 -->
+      <!-- 顶部操作按钮:最新热源 / 最新闯入 / 最新烟雾 -->
       <div class="view-actions">
         <button
           v-for="btn in actions"
@@ -100,25 +100,13 @@ import { useCamera } from '/@/hooks/system/useCameraPianation';
 import { useMessage } from '/@/hooks/web/useMessage';
 import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
 import { dscAlarmLogList } from '../hsqHome.api';
+import type { ViewActionItem, LatestAlarmPopupData } from './type';
 
-/** 操作按钮数据结构 */
-interface ActionItem {
-  key: string;
-  label: string;
-}
-
-/** 最新报警弹窗展示数据 */
-interface LatestAlarmPopupData {
-  imageUrl: string;
-  alarmTime: string;
-  deviceName: string;
-  alarmType: string;
-}
-
-/** 事件类型编码:热源 / 闯入 */
+/** 事件类型编码:热源 / 闯入 / 烟雾 */
 const EVENT_TYPE_MAP: Record<string, { code: string; title: string; typeLabel: string }> = {
   heat: { code: '10001', title: '最新热源告警图', typeLabel: '热源报警' },
   intrusion: { code: '10002', title: '最新闯入告警图', typeLabel: '闯入报警' },
+  smoke: { code: '10003', title: '最新烟雾告警图', typeLabel: '烟雾报警' },
 };
 
 let props = defineProps({
@@ -150,11 +138,10 @@ const pagination = reactive<{
   onCameraSelect: handleCameraSelect,
 });
 /** 顶部操作按钮列表 */
-const actions: ActionItem[] = [
+const actions: ViewActionItem[] = [
   { key: 'heat', label: '最新热源' },
   { key: 'intrusion', label: '最新闯入' },
   { key: 'smoke', label: '最新烟雾' },
-  { key: 'alarmOff', label: '关闭声光报警' },
 ];
 
 /** 当前激活的操作按钮 */
@@ -213,8 +200,8 @@ function pickLatestAlarmRecord(records: any[], eventCode: string) {
   return matched.slice().sort((a, b) => dayjs(b.createTime || b.alarmTime || 0).valueOf() - dayjs(a.createTime || a.alarmTime || 0).valueOf())[0];
 }
 
-/** 点击最新热源 / 最新闯入:请求列表后展示对应最新报警图 */
-async function openLatestAlarmByType(key: 'heat' | 'intrusion') {
+/** 点击最新热源 / 最新闯入 / 最新烟雾:请求列表后展示对应最新报警图 */
+async function openLatestAlarmByType(key: 'heat' | 'intrusion' | 'smoke') {
   if (latestAlarmLoading.value) return;
   const meta = EVENT_TYPE_MAP[key];
   if (!meta) return;
@@ -257,7 +244,7 @@ function closeLatestAlarmPopup() {
 /** 点击操作按钮,触发 action 事件 */
 function onAction(key: string) {
   activeAction.value = key;
-  if (key === 'heat' || key === 'intrusion') {
+  if (key === 'heat' || key === 'intrusion' || key === 'smoke') {
     openLatestAlarmByType(key);
   }
   emit('action', key);
@@ -565,44 +552,6 @@ defineExpose({
   padding: 0 4px;
 }
 
-:deep(.zxm-pagination) {
-  color: #cfe9ff;
-
-  .zxm-pagination-item,
-  .zxm-pagination-prev,
-  .zxm-pagination-next,
-  .zxm-pagination-jump-prev,
-  .zxm-pagination-jump-next {
-    min-width: 26px;
-    height: 26px;
-    line-height: 24px;
-    border: 1px solid rgba(58, 225, 255, 0.45);
-    background: rgba(16, 64, 100, 0.85);
-    color: #cfe9ff;
-  }
-
-  .zxm-pagination-item a,
-  .zxm-pagination-prev button,
-  .zxm-pagination-next button {
-    color: #cfe9ff;
-  }
-
-  .zxm-pagination-item-active {
-    border-color: #01fefc;
-    background: rgba(32, 166, 169, 0.85);
-
-    a {
-      color: #fff;
-    }
-  }
-
-  .zxm-pagination-disabled .zxm-pagination-item-link,
-  .zxm-pagination-disabled button {
-    color: rgba(207, 233, 255, 0.35);
-    border-color: rgba(58, 225, 255, 0.2);
-  }
-}
-
 // 无画面占位提示
 .media-empty {
   height: 100%;

+ 3 - 45
src/views/vent/monitorManager/hsqHome/components/SystemLeftBottom.vue

@@ -39,11 +39,7 @@
 
 <script setup lang="ts">
 import { ref, type PropType } from 'vue'
-
-interface BtnOption {
-  label: string
-  value?: string | number
-}
+import type { SystemTableBtnOption } from './type'
 
 let props = defineProps({
   title: {
@@ -55,11 +51,11 @@ let props = defineProps({
     default: () => [],
   },
   btnOption: {
-    type: Array as PropType<BtnOption[]>,
+    type: Array as PropType<SystemTableBtnOption[]>,
     default: () => [],
   },
   titleOption: {
-    type: Array as PropType<BtnOption[]>,
+    type: Array as PropType<SystemTableBtnOption[]>,
     default: () => [],
   },
   showPagination: {
@@ -228,44 +224,6 @@ function onPageChange(page: number, size: number) {
     padding: 0 4px;
   }
 
-  :deep(.zxm-pagination) {
-    color: #cfe9ff;
-
-    .zxm-pagination-item,
-    .zxm-pagination-prev,
-    .zxm-pagination-next,
-    .zxm-pagination-jump-prev,
-    .zxm-pagination-jump-next {
-      min-width: 26px;
-      height: 26px;
-      line-height: 24px;
-      border: 1px solid rgba(58, 225, 255, 0.45);
-      background: rgba(16, 64, 100, 0.85);
-      color: #cfe9ff;
-    }
-
-    .zxm-pagination-item a,
-    .zxm-pagination-prev button,
-    .zxm-pagination-next button {
-      color: #cfe9ff;
-    }
-
-    .zxm-pagination-item-active {
-      border-color: #01fefc;
-      background: rgba(32, 166, 169, 0.85);
-
-      a {
-        color: #fff;
-      }
-    }
-
-    .zxm-pagination-disabled .zxm-pagination-item-link,
-    .zxm-pagination-disabled button {
-      color: rgba(207, 233, 255, 0.35);
-      border-color: rgba(58, 225, 255, 0.2);
-    }
-  }
-
   .content-item {
     display: flex;
     justify-content: space-between;

+ 0 - 38
src/views/vent/monitorManager/hsqHome/components/SystemLeftTop.vue

@@ -240,44 +240,6 @@ function onPageChange(page: number, size: number) {
     padding: 0 4px;
   }
 
-  :deep(.zxm-pagination) {
-    color: #cfe9ff;
-
-    .zxm-pagination-item,
-    .zxm-pagination-prev,
-    .zxm-pagination-next,
-    .zxm-pagination-jump-prev,
-    .zxm-pagination-jump-next {
-      min-width: 26px;
-      height: 26px;
-      line-height: 24px;
-      border: 1px solid rgba(58, 225, 255, 0.45);
-      background: rgba(16, 64, 100, 0.85);
-      color: #cfe9ff;
-    }
-
-    .zxm-pagination-item a,
-    .zxm-pagination-prev button,
-    .zxm-pagination-next button {
-      color: #cfe9ff;
-    }
-
-    .zxm-pagination-item-active {
-      border-color: #01fefc;
-      background: rgba(32, 166, 169, 0.85);
-
-      a {
-        color: #fff;
-      }
-    }
-
-    .zxm-pagination-disabled .zxm-pagination-item-link,
-    .zxm-pagination-disabled button {
-      color: rgba(207, 233, 255, 0.35);
-      border-color: rgba(58, 225, 255, 0.2);
-    }
-  }
-
   .content-item {
     display: flex;
     justify-content: space-between;

+ 1 - 5
src/views/vent/monitorManager/hsqHome/components/SystemRightTop.vue

@@ -18,11 +18,7 @@
 
 <script setup lang="ts">
 import { useRouter } from 'vue-router'
-
-interface UserInfoItem {
-  label: string
-  value: string
-}
+import type { UserInfoItem } from './type'
 
 const props = defineProps<{
   userInfo?: UserInfoItem[]

+ 3 - 13
src/views/vent/monitorManager/hsqHome/components/WarnCenTop.vue

@@ -83,17 +83,7 @@ import { picList, actionBtns } from '../hsqHome.data'
 import { useGlobSetting } from '/@/hooks/setting';
 import { updateProcessState } from '../hsqHome.api'
 import { useMessage } from '/@/hooks/web/useMessage';
-
-interface DetailItem {
-  label: string
-  value: string | number
-  valueClass: 'text-val' | 'text-val1'
-}
-
-interface ActionBtn {
-  key: number
-  label: string
-}
+import type { WarnDetailItem, WarnActionBtn } from './type'
 
 const props = defineProps({
   warnData: {
@@ -159,7 +149,7 @@ const modeText = computed(() => {
 })
 
 /** 报警详情字段列表 */
-const detailItems = computed<DetailItem[]>(() => {
+const detailItems = computed<WarnDetailItem[]>(() => {
   const data = props.warnData || {}
   return [
     { label: '设备编号', value: data.devId || '-', valueClass: 'text-val1' },
@@ -188,7 +178,7 @@ function closePreview() {
 }
 
 /** 切换步骤并更新服务端状态 */
-function changeStep(btn: ActionBtn) {
+function changeStep(btn: WarnActionBtn) {
   syncProcessFlow(btn.key - 1)
   getUpdateWarnStatus()
 }

+ 1 - 9
src/views/vent/monitorManager/hsqHome/components/common/commonEchart.vue

@@ -9,15 +9,7 @@ import { ref, watch, onMounted, type PropType, type Ref } from 'vue'
 import { merge } from 'lodash-es'
 import type { EChartsOption } from 'echarts'
 import { useECharts } from '/@/hooks/web/useECharts'
-
-/** 柱状图单系列数据 */
-export interface CommonBarSeriesItem {
-  name: string
-  data: number[]
-  /** 柱条颜色;不传则使用主题渐变色板 */
-  color?: string
-  barWidth?: number | string
-}
+import type { CommonBarSeriesItem } from '../type'
 
 /** 主题色板(青蓝监控风格) */
 const THEME_COLORS = ['#01fefc', '#3ae1ff', '#20a6a9', '#2f6fb8', '#7ec7ff']

+ 234 - 26
src/views/vent/monitorManager/hsqHome/components/type.ts

@@ -13,23 +13,6 @@ export interface DeviceNode {
   latitude?: string | number;
 }
 
-/** 告警上报表单字段 */
-export interface AlarmFormState {
-  deviceId: string;
-  deviceName: string;
-  longitude: string;
-  latitude: string;
-  operator: string;
-  remark: string;
-}
-
-/** 告警上报字段配置 */
-export interface AlarmReportFieldItem {
-  key: keyof AlarmFormState;
-  label: string;
-  placeholder?: string;
-}
-
 /** 设备分组 */
 export interface DeviceGroup {
   id: string;
@@ -75,7 +58,7 @@ export interface PresetItem {
   name: string;
 }
 
-/** 红外控制单项 */
+/** 调节控件单项(激光变倍等) */
 export interface IrControlItem {
   key: string;
   icon: string;
@@ -173,14 +156,239 @@ export interface AlarmStatResult {
 
 /** 抓拍图片记录(picVideoList.records) */
 export interface PicVideoRecord {
-  id: string
-  deviceId: string
-  deviceName: string
-  devicePos: string
+  id: string;
+  deviceId: string;
+  deviceName: string;
+  devicePos: string;
   /** 可见光路径 */
-  path1: string
+  path1: string;
   /** 红外路径 */
-  path2: string
-  createTime: string
-  type: number
+  path2: string;
+  createTime: string;
+  type: number;
+}
+
+/** Tab 标签项 */
+export interface TabItem {
+  key: string;
+  label: string;
+}
+
+/** 镜头控制项(变焦/聚焦/光圈) */
+export interface LensControl {
+  key: string;
+  icon: string;
+  /** + 按钮提示 */
+  plusTip: string;
+  /** − 按钮提示 */
+  minusTip: string;
+}
+
+/** 导航菜单键值映射 */
+export interface IMenuKeyMap {
+  yzt: 'yzt';
+  jcyj: 'jcyj';
+  sbgl: 'sbgl';
+  ldpz: 'ldpz';
+  xtgl: 'xtgl';
+  dpzs: 'dpzs';
+  spyl: 'spyl';
+  lspb: 'lspb';
+}
+
+/** 导航菜单 key */
+export type MenuKey = IMenuKeyMap[keyof IMenuKeyMap];
+
+/** 云台方向轮盘按钮 */
+export interface DirectionButtonItem {
+  value: 'up' | 'down' | 'left' | 'right';
+  label: string;
+}
+
+/** 云台扫描/调色模式选择器 */
+export interface ModeSelectorItem {
+  key: 'scan' | 'color';
+  label: string;
+  current: string;
+  options: string[];
+}
+
+/** 配置类模式切换参数(扫描 / 调色) */
+export interface CfgModeSwitchOption {
+  value: string;
+  current: { value: string };
+  emitChange: (value: string) => void;
+  request: (deviceid: string, code: number) => any;
+  codeMap: { [key: string]: number };
+  successText: string;
+  failText: string;
+  logLabel: string;
+}
+
+/** 云台方向控制指令参数 */
+export interface PtzControlParam {
+  deviceid: string;
+  paramcode: string;
+  noCheckPassword: boolean;
+  cmd?: string;
+  stop: number;
+  speed: number;
+}
+
+/** 镜头 PTZ 控制参数(焦距 / 焦点 / 光圈) */
+export interface LensPtzCmdOption {
+  /** 按下 plus 时的 cmd */
+  plusCmd: number;
+  /** 按下 minus 时的 cmd */
+  minusCmd: number;
+  /** 记录按住方向的 ref */
+  lastAction: { value: 'plus' | 'minus' | null };
+  logLabel: string;
+}
+
+/** 查看图片 / 抓拍结果项 */
+export interface PicViewItem {
+  id: string;
+  deviceName: string;
+  createTime: string;
+  visibleUrl: string;
+  infraredUrl: string;
+}
+
+/** 查看录像弹窗列表项 */
+export interface VideoViewItem {
+  id: string;
+  fileName: string;
+  deviceName: string;
+  recordTime: string;
+  visibleUrl: string;
+  infraredUrl: string;
+  visibleMime: string;
+  infraredMime: string;
+}
+
+/** 视频画面区顶部操作按钮 */
+export interface ViewActionItem {
+  key: string;
+  label: string;
+}
+
+/** 最新报警弹窗展示数据 */
+export interface LatestAlarmPopupData {
+  imageUrl: string;
+  alarmTime: string;
+  deviceName: string;
+  alarmType: string;
+}
+
+/** 前端系统配置表单(ZC_SYSTEM_CFG_S) */
+export interface SysCfgForm {
+  findFire: boolean;
+  findMove: boolean;
+  fireTrace: boolean;
+  findDxm: boolean;
+  camZoomCapture: boolean;
+  fireMeasureTemp: boolean;
+  fireMinThreshold: string | number;
+  fireMaxThreshold: string | number;
+  fireMinArea: string | number;
+  fireMaxArea: string | number;
+  radiation: string | number;
+  emissivity: string | number;
+  environmentTemp: string | number;
+  grayOffset: string | number;
+  fireRadius: string | number;
+  shakeRadius: string | number;
+  stillDuration: string | number;
+  ptzSemiCircleDuration: string | number;
+  stayDuration: string | number;
+  dxmLength: string | number;
+  dxmRadius: string | number;
+  deviceId: string | number;
+  deviceName: string;
+  ip: string;
+  subnetMask: string;
+  gateway: string;
+  cmdPort: string | number;
+  alarmPort: string | number;
+  horFov: string | number;
+  verFov: string | number;
+  majorStreamReso: string | number;
+  minorStreamReso: string | number;
+  srvCenterIp: string;
+  srvCenterPort: string | number;
+  showMaxTemp: boolean;
+  showMinTemp: boolean;
+  showAvgTemp: boolean;
+  showRunMode: boolean;
+  showRunStatus: boolean;
+  enableCenterService: boolean;
+  /** 版本区占位字段 */
+  ld: string;
+}
+
+/** 设备配置表单字段 */
+export interface DeviceConfigForm {
+  deviceNo: string;
+  deviceName: string;
+  groupName: string;
+  ip: string;
+  cmdPort: string;
+  hasInfrared: boolean;
+  hasHd: boolean;
+  hasPtz: boolean;
+  irIp: string;
+  irPort: string;
+  irType: string;
+  irUser: string;
+  irPassword: string;
+  irStreamUrl: string;
+  irSubStreamUrl: string;
+  horFov: string;
+  verFov: string;
+  hdIp: string;
+  hdPort: string;
+  hdType: string;
+  hdUser: string;
+  hdPassword: string;
+  hdStreamUrl: string;
+  hdSubStreamUrl: string;
+  installHeight: string;
+  northDeclination: string;
+  longitude: string;
+  latitude: string;
+}
+
+/** 报警详情字段项 */
+export interface WarnDetailItem {
+  label: string;
+  value: string | number;
+  valueClass: 'text-val' | 'text-val1';
+}
+
+/** 报警处置流程按钮 */
+export interface WarnActionBtn {
+  key: number;
+  label: string;
+}
+
+/** 系统管理表格按钮 / 表头选项 */
+export interface SystemTableBtnOption {
+  label: string;
+  value?: string | number;
+}
+
+/** 系统管理用户信息展示项 */
+export interface UserInfoItem {
+  label: string;
+  value: string;
+}
+
+/** 通用柱状图系列数据 */
+export interface CommonBarSeriesItem {
+  name: string;
+  data: number[];
+  /** 柱条颜色;不传则使用主题渐变色板 */
+  color?: string;
+  barWidth?: number | string;
 }

+ 3 - 28
src/views/vent/monitorManager/hsqHome/hsqHome.data.ts

@@ -3,27 +3,13 @@ import type {
   LaserActionItem,
   DeviceNode,
   DeviceGroup,
-  AlarmFormState,
-  AlarmReportFieldItem,
   AngleState,
   StatsCount,
   AngleField,
   PresetToolbarItem,
+  TabItem,
+  LensControl,
 } from './components/type';
-/** Tab 标签项数据结构 */
-interface TabItem {
-  key: string;
-  label: string;
-}
-/** 镜头控制项(变焦/聚焦/光圈)数据结构 */
-interface LensControl {
-  key: string;
-  icon: string;
-  /** + 按钮提示 */
-  plusTip: string;
-  /** − 按钮提示 */
-  minusTip: string;
-}
 
 //设备状态总览
 export let option = [
@@ -350,10 +336,9 @@ export const tabs: TabItem[] = [
   { key: 'preset', label: '预置位' },
 ];
 
-/**云台控制-功能 Tab 列表:云台控制 / 红外控制 / 激光 / 透雾 */
+/**云台控制-功能 Tab 列表:云台控制 / 激光 / 透雾 */
 export const tabsPtz: TabItem[] = [
   { key: 'ptz', label: '云台控制' },
-  { key: 'ir', label: '红外控制' },
   { key: 'laser', label: '激光' },
   { key: 'defog', label: '透雾' },
 ];
@@ -425,16 +410,6 @@ export const angleFields: AngleField[] = [
 /** 多选框提示:第一个可见光取流,第二个红外取流 */
 export const checkTips = ['可见光取流', '红外取流'] as const;
 
-/** 告警上报字段(全部可手动输入修改) */
-export const alarmReportFieldList: AlarmReportFieldItem[] = [
-  { key: 'deviceId', label: '设备号', placeholder: '请输入设备号' },
-  { key: 'deviceName', label: '设备名', placeholder: '请输入设备名' },
-  { key: 'longitude', label: '设备经度', placeholder: '请输入设备经度' },
-  { key: 'latitude', label: '设备纬度', placeholder: '请输入设备纬度' },
-  { key: 'operator', label: '操作员', placeholder: '请输入操作员' },
-  { key: 'remark', label: '备注', placeholder: '请输入备注' },
-];
-
 /** 预置位工具栏:前 5 个图标对应 assets/icons 下 1.svg ~ 5.svg,添加为 add.svg */
 export const presetToolbarItems: PresetToolbarItem[] = [
   { key: 'refresh', icon: '1', title: '刷新' },

+ 163 - 0
src/views/vent/monitorManager/hsqHome/hsqPagination.less

@@ -0,0 +1,163 @@
+/**
+ * 红沙泉监测预警系统统一分页样式
+ * 对齐「视频预览 - 摄像头列表」分页(DeviceView .view-pagination)
+ * 覆盖:一张图 / 监测预警 / 视频预览 / 设备管理 / 系统管理 / 历史报表
+ * 以及 Teleport 到 body 的查看图片、查看录像弹窗底部分页
+ */
+
+.hsq-tech-pagination-style() {
+  color: #cfe9ff !important;
+
+  .zxm-pagination-item,
+  .zxm-pagination-prev,
+  .zxm-pagination-next,
+  .zxm-pagination-jump-prev,
+  .zxm-pagination-jump-next,
+  .ant-pagination-item,
+  .ant-pagination-prev,
+  .ant-pagination-next,
+  .ant-pagination-jump-prev,
+  .ant-pagination-jump-next {
+    min-width: 26px !important;
+    height: 26px !important;
+    line-height: 24px !important;
+    margin: 0 4px !important;
+    border: 1px solid rgba(58, 225, 255, 0.45) !important;
+    border-radius: 0 !important;
+    background: rgba(16, 64, 100, 0.85) !important;
+    color: #cfe9ff !important;
+  }
+
+  .zxm-pagination-item a,
+  .zxm-pagination-prev .zxm-pagination-item-link,
+  .zxm-pagination-next .zxm-pagination-item-link,
+  .zxm-pagination-prev button,
+  .zxm-pagination-next button,
+  .ant-pagination-item a,
+  .ant-pagination-prev .ant-pagination-item-link,
+  .ant-pagination-next .ant-pagination-item-link,
+  .ant-pagination-prev button,
+  .ant-pagination-next button {
+    color: #cfe9ff !important;
+  }
+
+  .zxm-pagination-item:hover,
+  .zxm-pagination-prev:hover,
+  .zxm-pagination-next:hover,
+  .ant-pagination-item:hover,
+  .ant-pagination-prev:hover,
+  .ant-pagination-next:hover {
+    border-color: #01fefc !important;
+
+    a,
+    button,
+    .zxm-pagination-item-link,
+    .ant-pagination-item-link {
+      color: #fff !important;
+    }
+  }
+
+  .zxm-pagination-item-active,
+  .ant-pagination-item-active {
+    border-color: #01fefc !important;
+    background: rgba(32, 166, 169, 0.85) !important;
+
+    a {
+      color: #fff !important;
+    }
+  }
+
+  /* 覆盖全局 pagination.less 中 disabled 被 display:none 的规则 */
+  .zxm-pagination-disabled,
+  .ant-pagination-disabled {
+    display: inline-block !important;
+    opacity: 1 !important;
+
+    .zxm-pagination-item-link,
+    .ant-pagination-item-link,
+    button,
+    a {
+      color: rgba(207, 233, 255, 0.35) !important;
+      border-color: rgba(58, 225, 255, 0.2) !important;
+      cursor: not-allowed;
+    }
+  }
+
+  .zxm-pagination-disabled.zxm-pagination-item,
+  .zxm-pagination-disabled.zxm-pagination-prev,
+  .zxm-pagination-disabled.zxm-pagination-next,
+  .ant-pagination-disabled.ant-pagination-item,
+  .ant-pagination-disabled.ant-pagination-prev,
+  .ant-pagination-disabled.ant-pagination-next {
+    border-color: rgba(58, 225, 255, 0.2) !important;
+    background: rgba(16, 64, 100, 0.45) !important;
+  }
+
+  .zxm-pagination-jump-prev .zxm-pagination-item-container .zxm-pagination-item-ellipsis,
+  .zxm-pagination-jump-next .zxm-pagination-item-container .zxm-pagination-item-ellipsis,
+  .ant-pagination-jump-prev .ant-pagination-item-container .ant-pagination-item-ellipsis,
+  .ant-pagination-jump-next .ant-pagination-item-container .ant-pagination-item-ellipsis {
+    color: #cfe9ff !important;
+  }
+
+  .zxm-pagination-total-text,
+  .ant-pagination-total-text {
+    color: #cfe9ff !important;
+  }
+
+  .zxm-pagination-options,
+  .ant-pagination-options {
+    .zxm-select-selector,
+    .ant-select-selector {
+      height: 26px !important;
+      border: 1px solid rgba(58, 225, 255, 0.45) !important;
+      background: rgba(16, 64, 100, 0.85) !important;
+      color: #cfe9ff !important;
+    }
+
+    .zxm-select-selection-item,
+    .ant-select-selection-item,
+    .zxm-select-arrow,
+    .ant-select-arrow {
+      color: #cfe9ff !important;
+      line-height: 24px !important;
+    }
+
+    .zxm-pagination-options-quick-jumper,
+    .ant-pagination-options-quick-jumper {
+      color: #cfe9ff !important;
+
+      input {
+        height: 26px !important;
+        border: 1px solid rgba(58, 225, 255, 0.45) !important;
+        background: rgba(16, 64, 100, 0.85) !important;
+        color: #cfe9ff !important;
+      }
+    }
+  }
+}
+
+/* 主界面内分页(一张图 / 监测预警 / 视频预览 / 设备管理 / 系统管理 / 历史报表) */
+.hsq-home {
+  .zxm-pagination,
+  .ant-pagination {
+    .hsq-tech-pagination-style();
+  }
+
+  /* a-table / BasicTable 内置分页区域 */
+  .zxm-table-pagination,
+  .ant-table-pagination {
+    .zxm-pagination,
+    .ant-pagination {
+      .hsq-tech-pagination-style();
+    }
+  }
+}
+
+/* Teleport 到 body 的查看图片 / 查看录像弹窗底部分页 */
+.pic-view-footer {
+  .zxm-pagination,
+  .ant-pagination {
+    .hsq-tech-pagination-style();
+  }
+}

+ 8 - 19
src/views/vent/monitorManager/hsqHome/index.vue

@@ -13,7 +13,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, type Component, type Ref, watch, onMounted, provide } from 'vue';
+import { ref, type Component, watch, onMounted, provide } from 'vue';
 import navMenu from './components/navMenu.vue'
 import fireMonitor from './fireMonitor.vue'
 import deviceManger from './deviceManger.vue'
@@ -23,30 +23,14 @@ import largeScreen from './largeScreen.vue'
 import linkConfiguration from './linkConfiguration.vue'
 import videoPlayer from './videoPlayer.vue'
 import historyReport from './historyReport.vue'
-
-
-/** 菜单键值映射接口,定义各导航菜单项对应的 key 标识 */
-interface IMenuKeyMap {
-  yzt: 'yzt'
-  jcyj: 'jcyj'
-  sbgl: 'sbgl'
-  ldpz: 'ldpz'
-  xtgl: 'xtgl'
-  dpzs: 'dpzs'
-  spyl: 'spyl',
-  lspb: 'lspb',
-}
-/** 菜单 key 类型,从 IMenuKeyMap 中提取所有键的联合类型 */
-type MenuKey = IMenuKeyMap[keyof IMenuKeyMap]
-/** 组件引用类型,继承 Vue 的 Ref<Component>,用于动态组件切换 */
-interface IComponentReference extends Ref<Component> { }
+import type { MenuKey } from './components/type'
 
 // 当前激活的菜单项
 const activeIndex = ref<number>(0);
 // 主标题
 const mainTitle = ref<string>('红沙泉二矿火区监测预警系统');
 //当前激活界面
-const activeComponente = ref<Component>(fireMonitor) as IComponentReference;
+const activeComponente = ref<Component>(fireMonitor);
 /** 菜单 key 与组件的映射表,用于根据导航菜单项动态切换显示组件 */
 const menuComponentMap: Record<MenuKey, Component> = {
   yzt: fireMonitor,
@@ -119,3 +103,8 @@ function changeMenu(param: MenuKey) {
   }
 }
 </style>
+
+<!-- 统一分页样式:对齐视频预览摄像头列表分页 -->
+<style lang="less">
+@import './hsqPagination.less';
+</style>

+ 2 - 17
src/views/vent/monitorManager/hsqHome/videoPlayer.vue

@@ -41,27 +41,12 @@ import DeviceControl from './components/DeviceControl.vue'; // 设备控制
 import DeviceView from './components/DeviceView.vue'; // 设备画面
 import DeviceWarnList from './components/DeviceWarnList.vue'; // 设备告警列表
 import { managesysList, monitorSystem, dscAlarmLogList } from './hsqHome.api';
-
-/** 设备分组节点数据结构 */
-interface GroupNode {
-  id: string;
-  title: string;
-  children: DeviceNode[];
-}
-/** 设备节点数据结构 */
-interface DeviceNode {
-  id: string;
-  name: string;
-  /** 勾选:可见光取流 / 红外取流 */
-  checks: [boolean, boolean];
-  /** 设备 IP */
-  ip?: string;
-}
+import type { DeviceGroup, DeviceNode } from './components/type';
 
 //系统id
 const systemId = ref('');
 /** 设备树数据(包含分组和设备列表) */
-const treeData = ref<GroupNode[]>([]);
+const treeData = ref<DeviceGroup[]>([]);
 // 设备选项
 const deviceOptions = ref<any[]>([]);
 // 双光谱摄像机报警记录-分页列表查询