HuBingJie
10 天以前 21078521d79f0eb5535cc34be7917a3fd1123b8f
´úÂë¹ÜÀí/WCS/WCS/WIDESEAWCS_Client/src/views/Home.vue
@@ -1,24 +1,1459 @@
<template>
  <div class="title"></div>
  <div class="rgv-monitor">
    <div class="monitor-header">
      <div class="title">设备状态监控</div>
      <div class="control-panel">
        <!-- ç›‘控类型切换 -->
        <div class="monitor-type-switch">
          <el-button-group>
            <el-button
              :type="currentMonitorType === 'inbound' ? 'primary' : ''"
              @click="switchMonitorType('inbound')"
            >
              å…¥åº“监控
            </el-button>
            <el-button
              :type="currentMonitorType === 'outbound' ? 'primary' : ''"
              @click="switchMonitorType('outbound')"
            >
              å‡ºåº“监控
            </el-button>
            <el-button
              :type="currentMonitorType === 'safetydoor' ? 'primary' : ''"
              @click="switchMonitorType('safetydoor')"
            >
              å®‰å…¨é—¨ç›‘控
            </el-button>
          </el-button-group>
        </div>
        <!-- ä¸€é”®æ“ä½œæŒ‰é’®ï¼ˆå®‰å…¨é—¨ç›‘控时不显示) -->
        <div class="one-click-operations" v-if="currentMonitorType !== 'safetydoor'">
          <el-button-group>
            <el-button
              type="primary"
              @click="handleOneClickOperation('init')"
              :loading="oneClickLoading.init"
            >
              ä¸€é”®åˆå§‹åŒ–
            </el-button>
            <el-button
              type="warning"
              @click="handleOneClickOperation('reset')"
              :loading="oneClickLoading.reset"
            >
              ä¸€é”®å¤ä½
            </el-button>
            <el-button
              type="success"
              @click="handleOneClickOperation('start')"
              :loading="oneClickLoading.start"
            >
              ä¸€é”®å¯åЍ
            </el-button>
            <el-button
              type="danger"
              @click="handleOneClickOperation('stop')"
              :loading="oneClickLoading.stop"
            >
              ä¸€é”®æš‚停
            </el-button>
          </el-button-group>
        </div>
        <el-button
          :type="isMonitoring ? 'danger' : 'primary'"
          @click="toggleMonitoring"
        >
          {{ isMonitoring ? '停止监控' : '启动监控' }}
        </el-button>
        <el-button type="warning" @click="refreshData">刷新数据</el-button>
      </div>
    </div>
    <!-- ç›‘控状态显示 -->
    <div class="monitor-status">
      <el-alert
        :title="getMonitorStatusTitle()"
        :type="isMonitoring ? 'success' : 'info'"
        :closable="false"
        show-icon
      />
    </div>
    <div class="devices-container">
      <!-- åŠ¨æ€æ¸²æŸ“è®¾å¤‡ -->
      <div
        class="device-card"
        v-for="(deviceData, deviceKey) in currentDeviceData"
        :key="deviceKey"
      >
        <div class="device-header">
          <h3>{{ getDeviceDisplayName(deviceKey) }}</h3>
          <div class="status-indicator" :class="getStatusClass(deviceData)"></div>
        </div>
        <div class="device-content">
          <template v-if="deviceData && Object.keys(deviceData).length > 0">
            <!-- RGV设备显示 -->
            <template v-if="currentMonitorType !== 'safetydoor'">
              <!-- åˆå§‹åŒ–状态显示 -->
              <div
                class="data-row"
                v-if="deviceData['初始化未完成标志位'] !== undefined"
              >
                <span class="label">初始化状态:</span>
                <span class="value" :class="deviceData['初始化未完成标志位'] === 1 ? 'fault-text' : ''">
                  {{ deviceData['初始化未完成标志位'] === 0 ? '已完成' : '未完成' }}
                </span>
              </div>
              <div
                class="data-row"
                v-for="(value, key) in deviceData"
                :key="key"
                v-if="key !== '初始化未完成标志位'"
              >
                <span class="label">{{ getFieldDisplayName(key) }}:</span>
                <span class="value" :class="getValueClass(key, value, deviceKey)">
                  {{ getFormattedValue(key, value, deviceKey) }}
                  <!-- ä¸ºä¸Šå‡ä¿¡å·å’Œä¸‹é™ä¿¡å·æ·»åŠ æŒ‡ç¤ºç¯ -->
                  <span v-if="key === '上升信号到位'" class="signal-indicator" :class="getSignalClass(value)"></span>
                  <span v-if="key === '下降信号到位'" class="signal-indicator" :class="getSignalClass(value)"></span>
                </span>
              </div>
            </template>
            <!-- å®‰å…¨é—¨è®¾å¤‡æ˜¾ç¤º -->
            <template v-else>
              <div
                class="data-row"
                v-for="(value, key) in deviceData"
                :key="key"
              >
                <span class="label">{{ getFieldDisplayName(key) }}:</span>
                <span class="value" :class="getSafetyDoorValueClass(key, value)">
                  {{ getSafetyDoorFormattedValue(key, value) }}
                  <!-- å®‰å…¨é—¨çŠ¶æ€æŒ‡ç¤ºç¯ -->
                  <span v-if="key === '安全门指示灯状态'" class="safety-light" :class="getSafetyLightClass(value)"></span>
                  <span v-if="key === '安全门急停状态'" class="emergency-stop" :class="getEmergencyStopClass(value)"></span>
                </span>
              </div>
            </template>
          </template>
          <div v-else class="no-data">
            è®¾å¤‡æœªè¿žæŽ¥æˆ–暂无数据
          </div>
          <!-- æ“ä½œæŒ‰é’®åŒºåŸŸï¼ˆå®‰å…¨é—¨ç›‘控时不显示) -->
          <div class="operation-buttons" v-if="currentMonitorType !== 'safetydoor'">
            <!-- å­è½¦æ“ä½œæŒ‰é’® -->
            <template v-if="getDeviceType(deviceKey) === 'child'">
              <el-button
                type="primary"
                size="small"
                @click="handleOperation(deviceKey, 'cs')"
                :loading="loadingStates[deviceKey]?.cs"
                :disabled="deviceData && deviceData['初始化未完成标志位'] === 0"
              >
                {{ getInitializationButtonText(deviceData) }}
              </el-button>
              <el-button
                :type="getModeButtonType(deviceData)"
                size="small"
                @click="handleModeToggle(deviceKey)"
                :loading="loadingStates[deviceKey]?.modeToggle"
              >
                {{ getCurrentModeText(deviceData) }}
              </el-button>
              <el-button
                type="warning"
                size="small"
                @click="handleOperation(deviceKey, 'fw')"
                :loading="loadingStates[deviceKey]?.fw"
              >
                å¤ä½
              </el-button>
            </template>
            <!-- æ¯è½¦æ“ä½œæŒ‰é’® -->
            <template v-else-if="getDeviceType(deviceKey) === 'mother'">
              <el-button
                type="primary"
                size="small"
                @click="handleOperation(deviceKey, 'cs')"
                :loading="loadingStates[deviceKey]?.cs"
                :disabled="deviceData && deviceData['初始化未完成标志位'] === 0"
              >
                {{ getInitializationButtonText(deviceData) }}
              </el-button>
              <el-button
                :type="getModeButtonType(deviceData)"
                size="small"
                @click="handleModeToggle(deviceKey)"
                :loading="loadingStates[deviceKey]?.modeToggle"
              >
                {{ getCurrentModeText(deviceData) }}
              </el-button>
              <el-button
                type="warning"
                size="small"
                @click="handleOperation(deviceKey, 'fw')"
                :loading="loadingStates[deviceKey]?.fw"
              >
                å¤ä½
              </el-button>
            </template>
            <!-- åŽŸæ–™è½¦æ“ä½œæŒ‰é’® -->
            <template v-else-if="getDeviceType(deviceKey) === 'material'">
              <el-button
                type="primary"
                size="small"
                @click="handleOperation(deviceKey, 'cs')"
                :loading="loadingStates[deviceKey]?.cs"
                :disabled="deviceData && deviceData['初始化未完成标志位'] === 0"
              >
                {{ getInitializationButtonText(deviceData) }}
              </el-button>
              <el-button
                :type="getModeButtonType(deviceData)"
                size="small"
                @click="handleModeToggle(deviceKey)"
                :loading="loadingStates[deviceKey]?.modeToggle"
              >
                {{ getCurrentModeText(deviceData) }}
              </el-button>
              <el-button
                type="warning"
                size="small"
                @click="handleOperation(deviceKey, 'fw')"
                :loading="loadingStates[deviceKey]?.fw"
              >
                å¤ä½
              </el-button>
              <!-- é›·è¾¾å¼€å…³æŒ‰é’® -->
              <el-button
                :type="getRadarButtonType(deviceData)"
                size="small"
                @click="handleRadarToggle(deviceKey)"
                :loading="loadingStates[deviceKey]?.radarToggle"
                :disabled="!isManualMode(deviceData)"
              >
                {{ getRadarButtonText(deviceData) }}
              </el-button>
              <!-- å…¥åº“继续任务按钮(仅对RGV101显示) -->
              <el-button
                v-if="deviceKey === 'rgV101'"
                type="success"
                size="small"
                @click="handleInNormal(deviceKey)"
                :loading="loadingStates[deviceKey]?.inNormal"
              >
                å…¥åº“继续任务
              </el-button>
              <!-- å…¥åº“异常排除按钮(仅对RGV101显示) -->
              <el-button
                v-if="deviceKey === 'rgV101'"
                type="danger"
                size="small"
                @click="handleInAbnormal(deviceKey)"
                :loading="loadingStates[deviceKey]?.inAbnormal"
              >
                å…¥åº“异常排除
              </el-button>
            </template>
          </div>
          <!-- åœ°å€æ“ä½œåŒºåŸŸï¼ˆå®‰å…¨é—¨ç›‘控时不显示) -->
          <div class="address-operation" v-if="currentMonitorType !== 'safetydoor'">
            <div class="address-title">地址操作</div>
            <div class="address-input-group">
              <el-select
                v-model="addressValues[deviceKey]"
                placeholder="选择目标地址"
                size="small"
                style="width: 200px; margin-right: 10px;"
              >
                <el-option
                  v-for="address in getDeviceAddresses(deviceKey)"
                  :key="address.value"
                  :label="address.label"
                  :value="address.value"
                />
              </el-select>
              <el-button
                type="info"
                size="small"
                @click="handleAddressOperation(deviceKey)"
                :loading="loadingStates[deviceKey]?.dz"
                :disabled="!addressValues[deviceKey]"
              >
                å‰å¾€åœ°å€
              </el-button>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- å…¨å±€æ“ä½œé¢æ¿ï¼ˆå®‰å…¨é—¨ç›‘控时不显示) -->
    <div class="operation-panel" v-if="currentMonitorType !== 'safetydoor'">
      <!-- <el-button type="primary" @click="showOperationDialog = true">高级操作</el-button> -->
    </div>
    <!-- æ“ä½œå¯¹è¯æ¡† -->
    <el-dialog v-model="showOperationDialog" title="设备高级操作" width="600px">
      <div>设备初始化、写入参数等高级操作界面</div>
    </el-dialog>
  </div>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
  name: 'RgvMonitor',
  data() {
    return {
      currentMonitorType: 'inbound', // 'inbound' æˆ– 'outbound' æˆ– 'safetydoor'
      isMonitoring: false,
      showOperationDialog: false,
      pollingTimer: null,
      loadingStates: {},
      addressValues: {},
      // ä¸€é”®æ“ä½œåŠ è½½çŠ¶æ€
      oneClickLoading: {
        init: false,
        reset: false,
        start: false,
        stop: false
      },
      // è®¾å¤‡æ•°æ®
      inboundDeviceData: {
        rgV101: null,
        rgV103: null,
        rgV104: null,
        rgV105: null,
        rgV107: null,
        rgV108: null,
        rgV109: null,
        // rgV202: null
      },
      outboundDeviceData: {
        rgV116: null,
        rgV115: null,
        rgV111: null,
        rgV112: null,
        rgV110: null,
        rgV114: null,
        rgV118: null
      },
      safetyDoorDeviceData: {
        aqm001: null,
        aqm002: null,
        aqm003: null
      },
      // å­—段显示名称映射
      fieldDisplayNames: {
        '工作模式': '工作模式',
        '当前位置': '当前位置',
        '有货状态': '有货状态',
        '目标地址': '目标地址',
        '任务状态': '任务状态',
        'rgV任务编号': '任务编号',
        '故障代码': '故障代码',
        '上升信号到位': '上升信号',
        '下降信号到位': '下降信号',
        '初始化未完成标志位': '初始化状态',
        '雷达状态': '雷达状态',
        '货叉状态': '货叉状态',
        // å®‰å…¨é—¨å­—段
        '安全门指示灯状态': '指示灯状态',
        '安全门请求开门': '请求开门',
        '安全门断电状态': '断电状态',
        '安全门急停状态': '急停状态',
        '安全门锁状态': '门锁状态',
        '安全门复位状态': '复位状态',
        '报警信息': '报警信息',
        '开门信息': '开门信息'
      },
      // è®¾å¤‡æ˜¾ç¤ºåç§°æ˜ å°„
      deviceDisplayNames: {
        // å…¥åº“设备
        'rgV101': 'RGV101 - åŽŸæ–™å…¥åº“è½¦',
        'rgV103': 'RGV103 - æ¯è½¦',
        'rgV104': 'RGV104 - å­è½¦',
        'rgV105': 'RGV105 - æ¯è½¦',
        'rgV107': 'RGV107 - å­è½¦',
        'rgV108': 'RGV108 - æ¯è½¦',
        'rgV109': 'RGV109 - æ¯è½¦',
        // å‡ºåº“设备
        'rgV110': 'RGV110 - æ¯è½¦',
        'rgV111': 'RGV111 - å­è½¦',
        'rgV112': 'RGV112 - æ¯è½¦',
        'rgV114': 'RGV114 - æ¯è½¦',
        'rgV115': 'RGV115 - æ¯è½¦',
        'rgV116': 'RGV116 - å­è½¦',
        'rgV118': 'RGV118 - åŽŸæ–™å‡ºåº“è½¦',
        // å®‰å…¨é—¨è®¾å¤‡
        'aqm001': 'AQM001 - 1#安全门',
        'aqm002': 'AQM002 - 2#安全门',
        'aqm003': 'AQM003 - 3#安全门'
      },
      // æ“ä½œç±»åž‹æ˜ å°„
      operationTypes: {
        'cs': '初始化',
        'sd': '切换到手动模式',
        'zd': '切换到自动模式',
        'dz': '前往地址',
        'kld': '开雷达',
        'gld': '关雷达',
        'fw': '复位',
      },
      // æŠ¥è­¦ä»£ç æ˜ å°„
      motherCarAlarmCodes: {
        0: '无报警',
        1: 'RGV小车急停被按下',
        2: '前进限位报警',
        3: '后退限位报警',
        4: 'PLC模块故障',
        5: 'PLC扩展模块故障',
        6: 'RGV长时间空转故障',
        7: '目的地不等于实际位置故障',
        8: '与总控通讯故障',
        9: '行走变频器故障',
        10: '取货时自身有货物报警',
        11: '放货时自身无货物报警',
        12: '停止时位置过冲报警'
      },
      childCarAlarmCodes: {
        0: '无报警',
        1: 'RGV小车急停被按下',
        2: '前进限位报警',
        3: '后退限位报警',
        4: 'PLC模块故障',
        5: 'PLC扩展模块故障',
        6: '扫码定位故障',
        7: 'RGV长时间空转故障',
        8: '目的地不等于实际位置故障',
        9: '与总控通讯故障',
        10: '行走变频器故障',
        11: '液压单元过载保护故障',
        12: '液压上升超时报警',
        13: '液压下降超时报警',
        14: '取货时自身有货物报警',
        15: '放货时自身无货物报警',
        16: '取货检测不到货物报警'
      },
      materialCarAlarmCodes: {
        0: '无报警',
        1: 'RGV小车急停被按下',
        2: '正转雷达报警',
        3: '反转雷达报警',
        4: '前进限位报警',
        5: '后退限位报警',
        6: '预留',
        7: 'PLC模块故障',
        8: 'PLC扩展模块故障',
        9: '称重模块故障',
        10: '扫码定位故障',
        11: 'RGV长时间空转故障',
        12: '目的地不等于实际位置故障',
        13: '与总控通讯故障',
        14: '前雷达屏蔽警告',
        15: '后雷达屏蔽警告',
        16: '行走变频器故障',
        17: '伸缩叉变频器故障',
        18: '液压单元过载保护故障',
        19: '液压上升超时报警',
        20: '液压下降超时报警',
        21: '伸缩叉伸出超时报警',
        22: '伸缩叉缩回超时报警',
        23: '外形检测报警',
        24: '称重超重报警',
        25: '货叉伸出极限限位报警',
        26: '货叉缩回极限限位报警',
        27: '取货时自身有货物报警',
        28: '放货时自身无货物报警',
        29: '货叉未回到初始位报警',
        30: '触发仅移动命令时货叉不在初始位报警',
        31: '货叉到达初始位但中位传感器未检测到报警'
      },
      // å®‰å…¨é—¨æŒ‡ç¤ºç¯çŠ¶æ€æ˜ å°„
      safetyDoorLightCodes: {
        0: '无输出',
        1: '红灯常亮+蜂鸣',
        2: '绿灯常亮',
        3: '黄灯闪烁(2HZ)',
        4: '黄灯常亮'
      },
      // è®¾å¤‡ç±»åž‹åˆ†ç±»
      deviceTypes: {
        // å…¥åº“设备
        motherCars: ['rgV103', 'rgV105', 'rgV108', 'rgV109'],
        childCars: ['rgV104', 'rgV107'],
        materialCars: ['rgV101'],
        // å‡ºåº“设备
        outboundMotherCars: ['rgV110', 'rgV112', 'rgV114', 'rgV115'],
        outboundChildCars: ['rgV111', 'rgV116'],
        outboundMaterialCars: ['rgV118'],
        // å®‰å…¨é—¨è®¾å¤‡
        safetyDoors: ['aqm001', 'aqm002', 'aqm003']
      },
      // è®¾å¤‡åœ°å€æ˜ å°„
      deviceAddresses: {
        // å…¥åº“设备地址
        'rgV101': [
          { value: '1001', label: '1001 - åŽŸæ–™å…¥åº“ä½' },
          { value: '1002', label: '1002 - åŽŸæ–™å‡ºåº“ä½' },
          { value: '1021', label: '1021 - ä¸­è½¬ä½' }
        ],
        'rgV103': [
          { value: '1031', label: '1031 - æ¯è½¦åœè½¦ä½1' },
          { value: '1032', label: '1032 - æ¯è½¦åœè½¦ä½2' }
        ],
        'rgV104': [
          { value: '3', label: '停靠点1' },
          { value: '5', label: '停靠点2' },
        ],
        'rgV105': [
          { value: '1051', label: '1051 - æ¯è½¦åœè½¦ä½1' },
          { value: '1052', label: '1052 - æ¯è½¦åœè½¦ä½2' }
        ],
        'rgV107': [
          { value: '3', label: '停靠点1' },
          { value: '5', label: '停靠点2' },
        ],
        'rgV108': [
          { value: '1081', label: '1081 - æ¯è½¦åœè½¦ä½1' },
          { value: '1082', label: '1082 - æ¯è½¦åœè½¦ä½2' }
        ],
        'rgV109': [
          { value: '1091', label: '1091 - æ¯è½¦åœè½¦ä½1' },
          { value: '1092', label: '1092 - æ¯è½¦åœè½¦ä½2' }
        ],
        // 'rgV202': [
        //   { value: '2001', label: '2001 - å·¥ä½1' },
        //   { value: '2002', label: '2002 - å·¥ä½2' },
        //   { value: '2003', label: '2003 - å·¥ä½3' },
        //   { value: '2004', label: '2004 - å·¥ä½4' },
        //   { value: '2005', label: '2005 - å·¥ä½5' },
        //   { value: '2006', label: '2006 - å·¥ä½6' },
        //   { value: '2007', label: '2007 - å·¥ä½7' },
        //   { value: '2008', label: '2008 - å·¥ä½8' },
        //   { value: '2009', label: '2009 - å·¥ä½9' }
        // ],
        // å‡ºåº“设备地址
        'rgV110': [
          { value: '1101', label: '1101 - æ¯è½¦åœè½¦ä½1' },
          { value: '1102', label: '1102 - æ¯è½¦åœè½¦ä½2' }
        ],
        'rgV111': [
          { value: '3', label: '停靠点1' },
          { value: '5', label: '停靠点2' },
        ],
        'rgV112': [
          { value: '1121', label: '1121 - æ¯è½¦åœè½¦ä½1' },
          { value: '1122', label: '1122 - æ¯è½¦åœè½¦ä½2' }
        ],
        'rgV114': [
          { value: '1141', label: '1141 - æ¯è½¦åœè½¦ä½1' },
          { value: '1142', label: '1142 - æ¯è½¦åœè½¦ä½2' }
        ],
        'rgV115': [
          { value: '1151', label: '1151 - æ¯è½¦åœè½¦ä½1' },
          { value: '1152', label: '1152 - æ¯è½¦åœè½¦ä½2' }
        ],
        'rgV116': [
          { value: '3', label: '停靠点1' },
          { value: '5', label: '停靠点2' },
        ],
        'rgV118': [
          { value: '2016', label: '2016 - åŽŸæ–™å¤„ç†ä½1' },
          { value: '2017', label: '2017 - åŽŸæ–™å¤„ç†ä½2' },
          { value: '2018', label: '2018 - åŽŸæ–™å¤„ç†ä½3' },
          { value: '2019', label: '2019 - åŽŸæ–™å¤„ç†ä½4' }
        ]
      },
      // ç‰¹æ®Šå­—段的值格式化
      valueFormatters: {
        '工作模式': (value) => {
          const modes = { 0: '手动模式', 1: '自动模式' }
          return modes[value] || `未知模式(${value})`
        },
        '有货状态': (value) => value === 0 ? '无货' : '有货',
        '任务状态': (value) => {
          const statusMap = { 0: '空闲', 1: '执行中', 2: '完成' }
          return statusMap[value] || `未知状态(${value})`
        },
        '故障代码': (value, deviceKey) => {
          return this.getAlarmText(value, deviceKey)
        },
        '上升信号到位': (value) => value === 0 ? '未到位' : '已到位',
        '下降信号到位': (value) => value === 0 ? '未到位' : '已到位',
        '初始化未完成标志位': (value) => value === 0 ? '已完成' : '未完成',
        '雷达状态': (value) => value === 0 ? '关闭' : '开启',
        '货叉状态': (value) => value === 0 ? '缩回' : '伸出'
      },
      // å®‰å…¨é—¨å­—段值格式化
      safetyDoorValueFormatters: {
        '安全门指示灯状态': (value) => {
          return this.safetyDoorLightCodes[value] || `未知状态(${value})`
        },
        '安全门请求开门': (value) => value === 0 ? '正常' : '请求开门',
        '安全门断电状态': (value) => value === 0 ? '断电' : '上电',
        '安全门急停状态': (value) => value === 0 ? '急停' : '正常',
        '安全门锁状态': (value) => value === 0 ? '开门' : '关门',
        '安全门复位状态': (value) => value === 0 ? '正常' : '复位中',
        '报警信息': (value) => value === 0 ? '正常' : '报警',
        '开门信息': (value) => value === 0 ? '关门' : '开门'
      }
    }
  },
  computed: {
    // å½“前显示的设备数据
    currentDeviceData() {
      switch (this.currentMonitorType) {
        case 'inbound':
          return this.inboundDeviceData
        case 'outbound':
          return this.outboundDeviceData
        case 'safetydoor':
          return this.safetyDoorDeviceData
        default:
          return this.inboundDeviceData
      }
    }
  },
  methods: {
    // å…¥åº“继续任务
    async handleInNormal(deviceKey) {
      this.setLoadingState(deviceKey, 'inNormal', true);
      try {
        const response = await this.http.post("api/RgvOperation/WriteInNormal", {}, "数据处理中...");
        if (response.status) {
          this.$message.success('入库继续任务成功');
          this.refreshData();
        } else {
          this.$message.error(response.message || '入库继续任务失败');
        }
      } catch (error) {
        this.$message.error('入库继续任务请求失败: ' + error.message);
      } finally {
        this.setLoadingState(deviceKey, 'inNormal', false);
      }
    },
    // å…¥åº“异常排除
    async handleInAbnormal(deviceKey) {
      this.setLoadingState(deviceKey, 'inAbnormal', true);
      try {
        const response = await this.http.post("api/RgvOperation/WriteInAbnormal", {}, "数据处理中...");
        if (response.status) {
          this.$message.success('入库异常排除成功');
          this.refreshData();
        } else {
          this.$message.error(response.message || '入库异常排除失败');
        }
      } catch (error) {
        this.$message.error('入库异常排除请求失败: ' + error.message);
      } finally {
        this.setLoadingState(deviceKey, 'inAbnormal', false);
      }
    },
    // èŽ·å–ç›‘æŽ§çŠ¶æ€æ ‡é¢˜
    getMonitorStatusTitle() {
      const typeNames = {
        'inbound': '入库',
        'outbound': '出库',
        'safetydoor': '安全门'
      }
      const status = this.isMonitoring ? '运行中' : '已停止'
      return `${typeNames[this.currentMonitorType]}监控状态: ${status}`
    },
    // å®‰å…¨é—¨å­—段值格式化
    getSafetyDoorFormattedValue(fieldKey, value) {
      const formatter = this.safetyDoorValueFormatters[fieldKey]
      return formatter ? formatter(value) : value
    },
    // å®‰å…¨é—¨å­—段值样式
    getSafetyDoorValueClass(fieldKey, value) {
      if (fieldKey === '安全门急停状态' && value === 0) {
        return 'fault-text'
      }
      if (fieldKey === '安全门断电状态' && value === 0) {
        return 'fault-text'
      }
      if (fieldKey === '报警信息' && value === 1) {
        return 'fault-text'
      }
      return ''
    },
    // å®‰å…¨é—¨æŒ‡ç¤ºç¯æ ·å¼
    getSafetyLightClass(value) {
      const classMap = {
        0: 'light-off',
        1: 'light-red',
        2: 'light-green',
        3: 'light-yellow-blink',
        4: 'light-yellow'
      }
      return classMap[value] || 'light-off'
    },
    // æ€¥åœçŠ¶æ€æ ·å¼
    getEmergencyStopClass(value) {
      return value === 0 ? 'emergency-stop-active' : 'emergency-stop-normal'
    },
    // ä¸€é”®æ“ä½œå¤„理
    async handleOneClickOperation(operationType) {
      // æ ¹æ®å½“前监控类型设置参数
      const monitorType = this.currentMonitorType === 'inbound' ? 'Inbound' : 'Outbound'
      this.oneClickLoading[operationType] = true
      try {
        const operationNames = {
          init: '一键初始化',
          reset: '一键复位',
          start: '一键启动',
          stop: '一键暂停'
        }
        const response = await this.http.post('api/Rgvoperainform/OneClickOperation', {
          operationType: operationType,
          monitorType: monitorType
        }, `${operationNames[operationType]}中...`)
        if (response.status) {
          this.$message.success(`${operationNames[operationType]} ${monitorType}端设备成功`)
          this.refreshData()
        } else {
          this.$message.error(response.message || `${operationNames[operationType]}失败`)
        }
      } catch (error) {
        this.$message.error(`${operationNames[operationType]}请求失败: ` + error.message)
      } finally {
        this.oneClickLoading[operationType] = false
      }
    },
    // èŽ·å–åˆå§‹åŒ–æŒ‰é’®æ˜¾ç¤ºæ–‡æœ¬
    getInitializationButtonText(deviceData) {
      if (!deviceData) return '初始化'
      return deviceData['初始化未完成标志位'] === 0 ? '已初始化' : '初始化'
    },
    // åˆ‡æ¢ç›‘控类型
    switchMonitorType(type) {
      if (this.currentMonitorType !== type) {
        // åœæ­¢å½“前监控
        if (this.isMonitoring) {
          this.stopMonitoring()
        }
        this.currentMonitorType = type
        // å¦‚果之前是监控状态,自动启动新类型的监控
        if (this.isMonitoring) {
          this.startMonitoring()
        }
      }
    },
    // èŽ·å–ä¿¡å·æŒ‡ç¤ºç¯ç±»å
    getSignalClass(value) {
      return value === 1 ? 'signal-active' : 'signal-inactive'
    },
    // ç›‘控控制方法
    async toggleMonitoring() {
      if (this.isMonitoring) {
        this.stopMonitoring()
      } else {
        await this.startMonitoring()
      }
    },
    async startMonitoring() {
      try {
        const param = {
          off: 1,
          monitorType: this.currentMonitorType
        }
        const response = await this.http.post('api/Rgvoperainform/GetDeviceStatusDto', param)
        if (response.status) {
          this.isMonitoring = true
          // æ ¹æ®ç›‘控类型更新对应的设备数据
          switch (this.currentMonitorType) {
            case 'inbound':
              this.inboundDeviceData = response.data
              break
            case 'outbound':
              this.outboundDeviceData = response.data
              break
            case 'safetydoor':
              this.safetyDoorDeviceData = response.data
              break
          }
          this.startPolling()
          this.$message.success(`${this.getMonitorStatusTitle()}已启动`)
        } else {
          this.$message.error(response.message || `启动${this.currentMonitorType}监控失败`)
        }
      } catch (error) {
        this.$message.error('请求失败: ' + error.message)
      }
    },
    stopMonitoring() {
      this.isMonitoring = false
      if (this.pollingTimer) {
        clearTimeout(this.pollingTimer)
        this.pollingTimer = null
      }
      this.http.post('api/Rgvoperainform/GetDeviceStatusDto', {
        off: 0,
        monitorType: this.currentMonitorType
      })
      this.$message.info(`${this.getMonitorStatusTitle()}已停止`)
    },
    startPolling() {
      this.pollingTimer = setTimeout(() => {
        if (this.isMonitoring) {
          this.refreshData()
          this.startPolling()
        }
      }, 2000)
    },
    async refreshData() {
      if (!this.isMonitoring) return
      try {
        const response = await this.http.post('api/Rgvoperainform/GetDeviceStatusDto', {
          off: 1,
          monitorType: this.currentMonitorType
        })
        if (response.status) {
          // æ ¹æ®ç›‘控类型更新对应的设备数据
          switch (this.currentMonitorType) {
            case 'inbound':
              this.inboundDeviceData = response.data
              break
            case 'outbound':
              this.outboundDeviceData = response.data
              break
            case 'safetydoor':
              this.safetyDoorDeviceData = response.data
              break
          }
        }
      } catch (error) {
        console.error('刷新数据失败:', error)
      }
    },
    // å…¶ä»–原有方法保持不变
    getStatusClass(device) {
      if (!device || Object.keys(device).length === 0) return 'offline'
      // å®‰å…¨é—¨çŠ¶æ€åˆ¤æ–­
      if (this.currentMonitorType === 'safetydoor') {
        if (device['安全门急停状态'] === 0) return 'fault'
        if (device['安全门断电状态'] === 0) return 'offline'
        if (device['报警信息'] === 1) return 'fault'
        return 'normal'
      }
      // RGV设备状态判断
      if (device['故障代码'] !== 0) return 'fault'
      if (device['任务状态'] === 1) return 'running'
      if (device['工作模式'] === 0) return 'manual'
      return 'normal'
    },
    getDeviceDisplayName(deviceKey) {
      return this.deviceDisplayNames[deviceKey] || deviceKey.toUpperCase()
    },
    getFieldDisplayName(fieldKey) {
      return this.fieldDisplayNames[fieldKey] || fieldKey
    },
    getFormattedValue(fieldKey, value, deviceKey) {
      const formatter = this.valueFormatters[fieldKey]
      return formatter ? formatter(value, deviceKey) : value
    },
    getValueClass(fieldKey, value, deviceKey) {
      if (fieldKey === '故障代码' && value !== 0) {
        return 'fault-text'
      }
      return ''
    },
    // èŽ·å–è®¾å¤‡ç±»åž‹
    getDeviceType(deviceKey) {
      if (this.deviceTypes.motherCars.includes(deviceKey) ||
          this.deviceTypes.outboundMotherCars.includes(deviceKey)) {
        return 'mother'
      } else if (this.deviceTypes.childCars.includes(deviceKey) ||
                this.deviceTypes.outboundChildCars.includes(deviceKey)) {
        return 'child'
      } else if (this.deviceTypes.materialCars.includes(deviceKey) ||
                this.deviceTypes.outboundMaterialCars.includes(deviceKey)) {
        return 'material'
      } else if (this.deviceTypes.safetyDoors.includes(deviceKey)) {
        return 'safetydoor'
      }
      return 'unknown'
    },
    // èŽ·å–è®¾å¤‡å¯ç”¨çš„ç›®æ ‡åœ°å€
    getDeviceAddresses(deviceKey) {
      return this.deviceAddresses[deviceKey] || []
    },
    // èŽ·å–æŠ¥è­¦æ–‡æœ¬
    getAlarmText(alarmCode, deviceKey) {
      if (alarmCode === 0) return '无报警'
      const deviceType = this.getDeviceType(deviceKey)
      let alarmMap = {}
      switch (deviceType) {
        case 'mother':
          alarmMap = this.motherCarAlarmCodes
          break
        case 'child':
          alarmMap = this.childCarAlarmCodes
          break
        case 'material':
          alarmMap = this.materialCarAlarmCodes
          break
        default:
          alarmMap = this.motherCarAlarmCodes
      }
      return alarmMap[alarmCode] || `未连接(${alarmCode})`
    },
    // èŽ·å–å½“å‰æ¨¡å¼æ˜¾ç¤ºæ–‡æœ¬
    getCurrentModeText(deviceData) {
      if (!deviceData || !deviceData['工作模式']) return '点击切换自动'
      return deviceData['工作模式'] === 1 ? '点击切换手动' : '点击切换自动'
    },
    // èŽ·å–æ¨¡å¼æŒ‰é’®ç±»åž‹ï¼ˆé¢œè‰²ï¼‰
    getModeButtonType(deviceData) {
      if (!deviceData || !deviceData['工作模式']) return 'success'
      return deviceData['工作模式'] === 1 ? 'warning' : 'success'
    },
    // åˆ¤æ–­æ˜¯å¦ä¸ºæ‰‹åŠ¨æ¨¡å¼
    isManualMode(deviceData) {
      return deviceData && deviceData['工作模式'] === 0
    },
    // èŽ·å–é›·è¾¾æŒ‰é’®æ–‡æœ¬
    getRadarButtonText(deviceData) {
      if (!deviceData) return '开启雷达'
      return deviceData['雷达状态'] === 1 ? '关闭雷达' : '开启雷达'
    },
    // èŽ·å–é›·è¾¾æŒ‰é’®ç±»åž‹
    getRadarButtonType(deviceData) {
      if (!deviceData) return 'info'
      return deviceData['雷达状态'] === 1 ? 'warning' : 'info'
    },
    // è®¾ç½®åŠ è½½çŠ¶æ€
    setLoadingState(deviceKey, operationType, loading) {
      if (!this.loadingStates[deviceKey]) {
        this.loadingStates[deviceKey] = {}
      }
      this.loadingStates[deviceKey][operationType] = loading
      this.loadingStates = { ...this.loadingStates }
    },
    // æ‰‹åЍ/自动模式切换
    async handleModeToggle(deviceKey) {
      this.setLoadingState(deviceKey, 'modeToggle', true)
      try {
        const deviceData = this.currentDeviceData[deviceKey]
        const currentMode = deviceData?.['工作模式'] || 0
        const operationType = currentMode === 0 ? 'zd' : 'sd'
        const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
          DelKeys: [deviceKey, operationType],
          Extra: true
        }, `${this.operationTypes[operationType]}中...`)
        if (response.status) {
          this.$message.success(`${this.getDeviceDisplayName(deviceKey)} ${this.operationTypes[operationType]}成功`)
          this.refreshData()
        } else {
          this.$message.error(response.message || `${this.operationTypes[operationType]}失败`)
        }
      } catch (error) {
        this.$message.error(`模式切换请求失败: ` + error.message)
      } finally {
        this.setLoadingState(deviceKey, 'modeToggle', false)
      }
    },
    // é›·è¾¾å¼€å…³åˆ‡æ¢
    async handleRadarToggle(deviceKey) {
      this.setLoadingState(deviceKey, 'radarToggle', true)
      try {
        const deviceData = this.currentDeviceData[deviceKey]
        const currentRadarState = deviceData?.['雷达状态'] || 0
        const operationType = currentRadarState === 0 ? 'kld' : 'gld'
        const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
          DelKeys: [deviceKey, operationType],
          Extra: true
        }, `${this.operationTypes[operationType]}中...`)
        if (response.status) {
          this.$message.success(`${this.getDeviceDisplayName(deviceKey)} ${this.operationTypes[operationType]}成功`)
          this.refreshData()
        } else {
          this.$message.error(response.message || `${this.operationTypes[operationType]}失败`)
        }
      } catch (error) {
        this.$message.error(`雷达操作请求失败: ` + error.message)
      } finally {
        this.setLoadingState(deviceKey, 'radarToggle', false)
      }
    },
    // åœ°å€æ“ä½œ
    async handleAddressOperation(deviceKey) {
      const address = this.addressValues[deviceKey]
      if (!address) {
        this.$message.warning('请选择目标地址')
        return
      }
      this.setLoadingState(deviceKey, 'dz', true)
      try {
        const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
          DelKeys: [deviceKey, 'dz', address],
          Extra: true
        }, '前往地址中...')
        if (response.status) {
          this.$message.success(`${this.getDeviceDisplayName(deviceKey)} å‰å¾€åœ°å€ ${address} æˆåŠŸ`)
          this.refreshData()
        } else {
          this.$message.error(response.message || '前往地址失败')
        }
      } catch (error) {
        this.$message.error('前往地址请求失败: ' + error.message)
      } finally {
        this.setLoadingState(deviceKey, 'dz', false)
      }
    },
    // ç»Ÿä¸€æ“ä½œå¤„理函数
    async handleOperation(deviceKey, operationType) {
      this.setLoadingState(deviceKey, operationType, true)
      try {
        const response = await this.http.post('api/Rgvoperainform/DeviceOperation', {
          DelKeys: [deviceKey, operationType],
          Extra: true
        }, `${this.operationTypes[operationType]}中...`)
        if (response.status) {
          const operationName = this.operationTypes[operationType]
          this.$message.success(`${this.getDeviceDisplayName(deviceKey)} ${operationName}操作成功`)
          // å¦‚果是初始化操作,添加额外提示
          if (operationType === 'cs') {
            this.$notify({
              title: '初始化成功',
              message: `${this.getDeviceDisplayName(deviceKey)} è®¾å¤‡åˆå§‹åŒ–完成`,
              type: 'success',
              duration: 3000
            })
          }
          this.refreshData()
        } else {
          this.$message.error(response.message || `${this.operationTypes[operationType]}操作失败`)
        }
      } catch (error) {
        this.$message.error(`${this.operationTypes[operationType]}请求失败: ` + error.message)
      } finally {
        this.setLoadingState(deviceKey, operationType, false)
      }
    }
  },
  beforeUnmount() {
    this.stopMonitoring()
  }
}
</script>
<style scoped>
/* åŽŸæœ‰çš„æ ·å¼ä¿æŒä¸å˜ï¼Œæ·»åŠ å®‰å…¨é—¨ç›¸å…³æ ·å¼ */
/* å®‰å…¨é—¨æŒ‡ç¤ºç¯æ ·å¼ */
.safety-light {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: inline-block;
  margin-left: 8px;
  border: 1px solid #ccc;
}
.safety-light.light-off {
  background-color: #909399;
}
.safety-light.light-red {
  background-color: #F56C6C;
  box-shadow: 0 0 6px #F56C6C;
  animation: blink 1s infinite;
}
.safety-light.light-green {
  background-color: #67C23A;
  box-shadow: 0 0 6px #67C23A;
}
.safety-light.light-yellow {
  background-color: #E6A23C;
  box-shadow: 0 0 6px #E6A23C;
}
.safety-light.light-yellow-blink {
  background-color: #E6A23C;
  box-shadow: 0 0 6px #E6A23C;
  animation: blink 0.5s infinite;
}
/* æ€¥åœçŠ¶æ€æŒ‡ç¤º */
.emergency-stop {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  margin-left: 8px;
}
.emergency-stop.emergency-stop-normal {
  background-color: #67C23A;
  box-shadow: 0 0 4px #67C23A;
}
.emergency-stop.emergency-stop-active {
  background-color: #F56C6C;
  box-shadow: 0 0 4px #F56C6C;
  animation: blink 0.8s infinite;
}
/* ç›‘控类型切换样式 */
.monitor-type-switch {
  margin-right: 20px;
}
.one-click-operations {
  margin-right: 20px;
}
.control-panel {
  display: flex;
  gap: 10px;
  align-items: center;
}
/* ä¿¡å·æŒ‡ç¤ºç¯æ ·å¼ */
.signal-indicator {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  margin-left: 8px;
}
.signal-indicator.signal-active {
  background-color: #67C23A;
  box-shadow: 0 0 4px #67C23A;
}
.signal-indicator.signal-inactive {
  background-color: #DCDFE6;
  border: 1px solid #C0C4CC;
}
/* ä¿®æ”¹value样式以容纳指示灯 */
.value {
  color: #303133;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* å…¶ä»–原有样式保持不变 */
.rgv-monitor {
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
}
.monitor-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding: 0 10px;
}
.title {
  line-height: 70vh;
  font-size: 24px;
  font-weight: bold;
  color: #409EFF;
}
.monitor-status {
  margin-bottom: 20px;
}
.devices-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  gap: 20px;
  margin-bottom: 20px;
}
.device-card {
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  overflow: hidden;
  transition: all 0.3s ease;
}
.device-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
}
.device-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}
.device-header h3 {
  margin: 0;
  font-size: 16px;
}
.status-indicator {
  width: 12px;
  height: 12px;
  border-radius: 50%;
}
.status-indicator.normal {
  background-color: #67C23A;
  box-shadow: 0 0 8px #67C23A;
}
.status-indicator.running {
  background-color: #409EFF;
  box-shadow: 0 0 8px #409EFF;
  animation: pulse 1.5s infinite;
}
.status-indicator.fault {
  background-color: #F56C6C;
  box-shadow: 0 0 8px #F56C6C;
  animation: blink 1s infinite;
}
.status-indicator.manual {
  background-color: #E6A23C;
  box-shadow: 0 0 8px #E6A23C;
  animation: pulse 1.5s infinite;
}
.status-indicator.offline {
  background-color: #909399;
}
.device-content {
  padding: 20px;
}
.data-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid #ebeef5;
}
.data-row:last-child {
  border-bottom: none;
}
.label {
  font-weight: 500;
  color: #606266;
}
.fault-text {
  color: #F56C6C;
  font-weight: bold;
}
.no-data {
  text-align: center;
  font-size: 28px;
  color: orange;
  color: #909399;
  padding: 40px 0;
  font-style: italic;
}
.operation-buttons {
  margin-top: 15px;
  padding-top: 15px;
  border-top: 1px dashed #ebeef5;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}
.address-operation {
  margin-top: 15px;
  padding-top: 15px;
  border-top: 1px dashed #ebeef5;
}
.address-title {
  font-weight: 500;
  color: #606266;
  margin-bottom: 10px;
  text-align: center;
}
.address-input-group {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
}
.operation-panel {
  text-align: center;
  padding: 20px;
}
@keyframes pulse {
  0% { opacity: 1; }
  50% { opacity: 0.5; }
  100% { opacity: 1; }
}
@keyframes blink {
  0%, 50% { opacity: 1; }
  51%, 100% { opacity: 0.3; }
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .devices-container {
    grid-template-columns: 1fr;
  }
  .monitor-header {
    flex-direction: column;
    gap: 15px;
    align-items: flex-start;
  }
  .control-panel {
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
  }
  .monitor-type-switch,
  .one-click-operations {
    margin-right: 0;
    margin-bottom: 10px;
    width: 100%;
  }
  .monitor-type-switch .el-button-group,
  .one-click-operations .el-button-group {
    width: 100%;
  }
  .monitor-type-switch .el-button,
  .one-click-operations .el-button {
    flex: 1;
  }
  .operation-buttons {
    flex-direction: column;
    align-items: stretch;
  }
  .operation-buttons .el-button {
    width: 100%;
  }
  .address-input-group {
    flex-direction: column;
    align-items: stretch;
  }
  .address-input-group .el-select,
  .address-input-group .el-button {
    width: 100%;
  }
}
</style>