#
Junjie
14 小时以前 bf64e8016283b18c04d5392dd9c002b921021af2
src/main/webapp/views/watch/console.html
@@ -4,14 +4,506 @@
            <meta charset="UTF-8">
            <title>WCS控制中心</title>
            <link rel="stylesheet" href="../../static/css/animate.min.css">
            <link rel="stylesheet" href="../../static/vue/element/element.css">
            <link rel="stylesheet" href="../../static/css/watch/console_vue.css">
            <link rel="stylesheet" href="../../static/vue/element/element.css">
            <style>
               html, body, #app {
                  width: 100%;
                  height: 100%;
                  margin: 0;
                  overflow: hidden;
               }
               body {
                  background: linear-gradient(180deg, #eef4f8 0%, #e7edf4 100%);
               }
               #app {
                  position: relative;
               }
               .monitor-shell {
                  position: relative;
                  width: 100%;
                  height: 100%;
                  overflow: hidden;
               }
               .monitor-map {
                  width: 100%;
                  height: 100%;
               }
               .monitor-panel-wrap {
                  position: absolute;
                  top: 18px;
                  left: 18px;
                  bottom: 18px;
                  z-index: 40;
                  pointer-events: none;
               }
               .monitor-panel {
                  width: min(max(360px, 30vw), calc(100vw - 92px));
                  max-width: calc(100vw - 92px);
                  height: calc(100vh - 36px);
                  display: flex;
                  flex-direction: column;
                  border-radius: 20px;
                  border: 1px solid rgba(255, 255, 255, 0.42);
                  background: rgba(248, 251, 253, 0.94);
                  box-shadow: 0 10px 24px rgba(88, 110, 136, 0.08);
                  overflow: hidden;
                  pointer-events: auto;
                  transform-origin: left center;
                  will-change: transform, opacity;
                  backface-visibility: hidden;
                  contain: layout paint style;
                  transition: transform .26s cubic-bezier(0.22, 1, 0.36, 1), opacity .2s ease, box-shadow .2s ease, border-color .2s ease;
               }
               .monitor-panel.is-collapsed {
                  opacity: 0;
                  transform: translate3d(calc(-100% - 14px), 0, 0);
                  border-color: transparent;
                  box-shadow: 0 6px 16px rgba(88, 110, 136, 0.02);
                  pointer-events: none;
               }
               .monitor-panel-header {
                  padding: 14px 16px 10px;
                  border-bottom: 1px solid rgba(226, 232, 240, 0.72);
                  background: rgba(255, 255, 255, 0.24);
               }
               .monitor-panel-title {
                  font-size: 15px;
                  font-weight: 600;
                  color: #243447;
                  line-height: 1.2;
               }
               .monitor-panel-desc {
                  margin-top: 4px;
                  font-size: 12px;
                  color: #6b7b8d;
               }
               .monitor-panel-body {
                  flex: 1;
                  min-height: 0;
                  padding: 12px;
                  overflow: hidden;
               }
               .monitor-panel-body {
                  flex: 1;
                  display: flex;
                  flex-direction: column;
                  gap: 10px;
               }
               .monitor-card-host {
                  flex: 1;
                  min-height: 0;
                  display: flex;
                  align-items: stretch;
                  width: 100%;
               }
               .wb-root {
                  position: relative;
                  flex: 1;
                  min-width: 0;
                  display: flex;
                  flex-direction: column;
                  height: 100%;
                  gap: 10px;
                  color: #395066;
               }
               .wb-main {
                  flex: 1;
                  min-height: 0;
                  display: flex;
                  gap: 10px;
               }
               .wb-side {
                  flex: 0 0 38%;
                  min-width: 220px;
                  max-width: 44%;
                  display: flex;
                  flex-direction: column;
                  gap: 10px;
                  min-height: 0;
               }
               .wb-side-title {
                  font-size: 11px;
                  font-weight: 700;
                  letter-spacing: 0.06em;
                  text-transform: uppercase;
                  color: #7d8fa2;
                  margin-bottom: 8px;
               }
               .wb-list-card,
               .wb-detail-panel {
                  min-height: 0;
                  display: flex;
                  flex-direction: column;
               }
               .wb-detail-panel {
                  flex: 1 1 62%;
                  min-width: 0;
               }
               .wb-tabs {
                  display: grid;
                  grid-template-columns: repeat(4, 1fr);
                  gap: 6px;
                  padding: 6px;
                  border-radius: 14px;
                  background: rgba(242, 246, 250, 0.78);
                  border: 1px solid rgba(224, 232, 239, 0.9);
               }
               .wb-tab {
                  height: 34px;
                  border: none;
                  border-radius: 10px;
                  background: transparent;
                  color: #73869b;
                  font-size: 12px;
                  font-weight: 600;
                  cursor: pointer;
                  transition: all .16s ease;
               }
               .wb-tab.is-active {
                  background: rgba(255, 255, 255, 0.92);
                  color: #24405c;
                  box-shadow: 0 8px 16px rgba(148, 163, 184, 0.08);
               }
               .wb-toolbar {
                  display: flex;
                  align-items: center;
                  gap: 8px;
               }
               .wb-toolbar-actions {
                  display: flex;
                  gap: 8px;
                  flex-shrink: 0;
               }
               .wb-input,
               .wb-select {
                  width: 100%;
                  height: 36px;
                  padding: 0 12px;
                  border-radius: 10px;
                  border: 1px solid rgba(224, 232, 239, 0.96);
                  background: rgba(255, 255, 255, 0.76);
                  color: #334155;
                  box-sizing: border-box;
                  outline: none;
                  transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;
               }
               .wb-input:focus,
               .wb-select:focus {
                  border-color: rgba(128, 168, 208, 0.66);
                  box-shadow: 0 0 0 3px rgba(128, 168, 208, 0.1);
                  background: rgba(255, 255, 255, 0.92);
               }
               .wb-btn {
                  height: 36px;
                  padding: 0 14px;
                  border: none;
                  border-radius: 10px;
                  background: #6f95bd;
                  color: #fff;
                  font-size: 12px;
                  font-weight: 600;
                  cursor: pointer;
                  box-shadow: 0 6px 14px rgba(111, 149, 189, 0.18);
                  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease, background .16s ease;
               }
               .wb-btn:hover {
                  transform: translateY(-1px);
               }
               .wb-btn-primary {
                  background: linear-gradient(135deg, #5e89b4 0%, #6f95bd 100%);
                  box-shadow: 0 10px 20px rgba(111, 149, 189, 0.22);
               }
               .wb-btn.wb-btn-ghost {
                  background: rgba(255, 255, 255, 0.76);
                  color: #4c6177;
                  border: 1px solid rgba(224, 232, 239, 0.96);
                  box-shadow: none;
               }
               .wb-btn.wb-btn-soft {
                  background: rgba(230, 237, 244, 0.92);
                  color: #4c6177;
                  border: 1px solid rgba(210, 221, 232, 0.98);
                  box-shadow: none;
               }
               .wb-control-card,
               .wb-list-card,
               .wb-detail {
                  border-radius: 16px;
                  border: 1px solid rgba(224, 232, 239, 0.92);
                  background: rgba(255, 255, 255, 0.62);
                  box-shadow: 0 8px 18px rgba(148, 163, 184, 0.06);
               }
               .wb-list-card {
                  flex: 1;
                  padding: 10px 8px 8px;
               }
               .wb-control-card {
                  padding: 14px;
                  background: linear-gradient(180deg, rgba(255, 255, 255, 0.82) 0%, rgba(246, 250, 253, 0.78) 100%);
                  overflow: auto;
               }
               .wb-control-subtitle {
                  margin-top: 8px;
                  margin-bottom: 10px;
                  font-size: 11px;
                  line-height: 1.45;
                  color: #6f8194;
               }
               .wb-control-target {
                  padding: 7px 9px;
                  border-radius: 12px;
                  background: rgba(234, 241, 247, 0.96);
                  border: 1px solid rgba(214, 224, 234, 0.96);
                  color: #43607c;
                  font-size: 11px;
                  font-weight: 700;
                  line-height: 1.4;
               }
               .wb-form-grid {
                  display: grid;
                  grid-template-columns: 1fr;
                  gap: 8px;
               }
               .wb-field {
                  display: flex;
                  flex-direction: column;
                  gap: 5px;
               }
               .wb-field-label {
                  font-size: 11px;
                  font-weight: 700;
                  letter-spacing: 0.02em;
                  color: #6d8197;
               }
               .wb-action-row {
                  display: flex;
                  flex-wrap: wrap;
                  gap: 8px;
                  margin-top: 2px;
                  padding-top: 8px;
                  border-top: 1px dashed rgba(216, 226, 235, 0.92);
               }
               .wb-section-title {
                  font-size: 13px;
                  font-weight: 700;
                  color: #28425d;
                  margin-bottom: 10px;
               }
               .wb-list {
                  flex: 1;
                  min-height: 0;
                  padding: 6px;
                  overflow: auto;
               }
               .wb-list-item {
                  width: 100%;
                  display: flex;
                  flex-direction: column;
                  align-items: flex-start;
                  justify-content: flex-start;
                  gap: 7px;
                  padding: 10px;
                  margin-bottom: 8px;
                  border: 1px solid transparent;
                  border-radius: 12px;
                  background: rgba(248, 250, 252, 0.72);
                  cursor: pointer;
                  text-align: left;
                  color: inherit;
               }
               .wb-list-item:last-child {
                  margin-bottom: 0;
               }
               .wb-list-item.is-active {
                  border-color: rgba(135, 166, 198, 0.38);
                  background: rgba(236, 243, 249, 0.94);
               }
               .wb-list-main {
                  min-width: 0;
               }
               .wb-list-title {
                  font-size: 12px;
                  font-weight: 700;
                  color: #27425c;
                  line-height: 1.35;
               }
               .wb-list-meta {
                  margin-top: 4px;
                  font-size: 11px;
                  color: #7b8b9c;
                  line-height: 1.4;
                  display: -webkit-box;
                  -webkit-line-clamp: 2;
                  -webkit-box-orient: vertical;
                  overflow: hidden;
               }
               .wb-badge {
                  flex-shrink: 0;
                  padding: 3px 8px;
                  border-radius: 999px;
                  font-size: 10px;
                  font-weight: 700;
               }
               .wb-badge.is-success {
                  background: rgba(82, 177, 126, 0.12);
                  color: #2d7650;
               }
               .wb-badge.is-working {
                  background: rgba(111, 149, 189, 0.12);
                  color: #3f6286;
               }
               .wb-badge.is-warning {
                  background: rgba(214, 162, 94, 0.14);
                  color: #9b6a24;
               }
               .wb-badge.is-danger {
                  background: rgba(207, 126, 120, 0.14);
                  color: #a14e4a;
               }
               .wb-badge.is-muted {
                  background: rgba(148, 163, 184, 0.14);
                  color: #748397;
               }
               .wb-empty {
                  padding: 28px 12px;
                  text-align: center;
                  color: #8b9aad;
                  font-size: 12px;
               }
               .wb-detail {
                  flex: 1;
                  min-height: 0;
                  padding: 12px;
                  overflow: auto;
               }
               .wb-detail-empty {
                  display: flex;
                  align-items: center;
                  justify-content: center;
               }
               .wb-detail-header {
                  display: flex;
                  align-items: flex-start;
                  justify-content: space-between;
                  gap: 10px;
                  margin-bottom: 12px;
               }
               .wb-detail-subtitle {
                  margin-top: 4px;
                  font-size: 12px;
                  color: #7c8c9d;
               }
               .wb-detail-actions {
                  display: flex;
                  flex-wrap: wrap;
                  gap: 8px;
               }
               .wb-link {
                  padding: 0;
                  border: none;
                  background: transparent;
                  color: #4677a4;
                  font-size: 12px;
                  cursor: pointer;
               }
               .wb-detail-grid {
                  display: grid;
                  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
                  gap: 8px;
               }
               .wb-detail-cell {
                  padding: 10px 12px;
                  border-radius: 12px;
                  background: rgba(247, 250, 252, 0.86);
                  border: 1px solid rgba(233, 239, 244, 0.96);
               }
               .wb-detail-label {
                  font-size: 11px;
                  color: #8090a2;
               }
               .wb-detail-value {
                  margin-top: 5px;
                  font-size: 13px;
                  color: #31485f;
                  word-break: break-all;
               }
               .wb-notice {
                  position: absolute;
                  right: 18px;
                  bottom: 18px;
                  padding: 10px 14px;
                  border-radius: 12px;
                  font-size: 12px;
                  font-weight: 600;
                  color: #fff;
                  box-shadow: 0 12px 24px rgba(15, 23, 42, 0.12);
               }
               .wb-notice.is-success {
                  background: rgba(82, 177, 126, 0.92);
               }
               .wb-notice.is-warning {
                  background: rgba(214, 162, 94, 0.92);
               }
               .wb-notice.is-danger {
                  background: rgba(207, 126, 120, 0.92);
               }
               .floor-switch-button {
                  min-width: 44px;
                  height: 30px;
                  padding: 0 12px;
                  border: 1px solid rgba(185, 197, 210, 0.84);
                  border-radius: 999px;
                  background: rgba(255, 255, 255, 0.82);
                  color: #4b6177;
                  font-size: 12px;
                  font-weight: 700;
                  cursor: pointer;
               }
               .floor-switch-button.is-active {
                  border-color: rgba(111, 149, 189, 0.4);
                  background: rgba(236, 243, 249, 0.94);
                  color: #27425c;
               }
               .monitor-panel-toggle {
                  position: absolute;
                  left: 0;
                  top: 50%;
                  margin-left: 0;
                  transform: translateY(-50%);
                  width: 30px;
                  min-height: 108px;
                  padding: 10px 4px;
                  border: 1px solid rgba(148, 163, 184, 0.22);
                  border-left: none;
                  border-radius: 0 14px 14px 0;
                  background: rgba(255, 255, 255, 0.96);
                  color: #3e5974;
                  box-shadow: 0 8px 18px rgba(15, 23, 42, 0.08);
                  cursor: pointer;
                  pointer-events: auto;
                  display: inline-flex;
                  flex-direction: column;
                  align-items: center;
                  justify-content: center;
                  gap: 10px;
                  font-size: 12px;
                  line-height: 1;
                  white-space: nowrap;
                  backface-visibility: hidden;
                  transition: left .26s cubic-bezier(0.22, 1, 0.36, 1), transform .26s cubic-bezier(0.22, 1, 0.36, 1), box-shadow .18s ease, background .18s ease;
               }
               .monitor-panel-toggle.is-panel-open {
                  left: calc(100% + 10px);
               }
               .monitor-panel-toggle i {
                  font-size: 14px;
               }
               .monitor-panel-toggle span {
                  writing-mode: vertical-rl;
                  text-orientation: mixed;
                  letter-spacing: 0.08em;
                  user-select: none;
               }
            </style>
            <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
@@ -25,42 +517,43 @@
      </head>
   <body>
      <div id="app">
         <div style="display: flex;margin-left: 20px;">
            <div style="width: 20%;height: 60vh;margin-right: 20px;margin-top: 30px;">
               <el-tabs type="border-card" v-model="activateCard" @tab-click="handleCardClick">
                  <el-tab-pane label="堆垛机" name="crn">
                     <watch-crn-card ref="watchCrnCard" :param="crnParam"></watch-crn-card>
                  </el-tab-pane>
                  <el-tab-pane label="双工位堆垛机" name="dualCrn">
                     <watch-dual-crn-card ref="watchDualCrnCard" :param="dualCrnParam"></watch-dual-crn-card>
                  </el-tab-pane>
                  <el-tab-pane label="输送站" name="devp">
                     <devp-card ref="devpCard" :param="devpParam"></devp-card>
                  </el-tab-pane>
                  <el-tab-pane label="RGV" name="rgv">
                     <watch-rgv-card ref="watchRgvCard" :param="rgvParam"></watch-rgv-card>
                  </el-tab-pane>
                  <!-- <el-tab-pane label="地图配置" name="mapSetting">
                     <map-setting-card :param="mapSettingParam"></map-setting-card>
                  </el-tab-pane> -->
               </el-tabs>
            </div>
         <div class="monitor-shell" ref="monitorShell">
            <map-canvas :lev="currentLev" :lev-list="levList" :crn-param="crnParam" :rgv-param="rgvParam" :devp-param="devpParam" :station-task-range="stationTaskRange" :viewport-padding="mapViewportPadding" :hud-padding="mapHudPadding" @switch-lev="switchLev" @crn-click="openCrn" @dual-crn-click="openDualCrn" @station-click="openSite" @rgv-click="openRgv" class="monitor-map"></map-canvas>
            <map-canvas :lev="currentLev" :crn-param="crnParam" :rgv-param="rgvParam" :devp-param="devpParam" @crn-click="openCrn" @dual-crn-click="openDualCrn" @station-click="openSite" @rgv-click="openRgv" style="width: 80%; height: 100vh;"></map-canvas>
            <div style="position: absolute;top: 15px;left: 50%;display: flex;">
               <div v-if="levList.length > 1" v-for="(lev,index) in levList" :key="index" style="margin-right: 10px;">
                  <el-button :type="currentLev == lev ? 'primary' : ''" @click="switchLev(lev)" size="mini">{{ lev }}F</el-button>
            <div class="monitor-panel-wrap" ref="monitorPanelWrap">
               <div class="monitor-panel" ref="monitorPanel" :class="{ 'is-collapsed': panelCollapsed }">
                  <div class="monitor-panel-header">
                     <div class="monitor-panel-title">监控工作台</div>
                     <div class="monitor-panel-desc">围绕地图做操作,设备点击后自动切换到对应面板</div>
               </div>
                  <div class="monitor-panel-body">
                     <div class="wb-tabs" role="tablist">
                        <button type="button" :class="['wb-tab', { 'is-active': activateCard === 'crn' }]" @click="handleWorkbenchTabChange('crn')">堆垛机</button>
                        <button type="button" :class="['wb-tab', { 'is-active': activateCard === 'dualCrn' }]" @click="handleWorkbenchTabChange('dualCrn')">双工位</button>
                        <button type="button" :class="['wb-tab', { 'is-active': activateCard === 'devp' }]" @click="handleWorkbenchTabChange('devp')">输送站</button>
                        <button type="button" :class="['wb-tab', { 'is-active': activateCard === 'rgv' }]" @click="handleWorkbenchTabChange('rgv')">RGV</button>
                     </div>
                     <div class="monitor-card-host">
                        <watch-crn-card v-if="activateCard === 'crn'" ref="watchCrnCard" :param="crnParam" :items="crnStateList" :auto-refresh="false"></watch-crn-card>
                        <watch-dual-crn-card v-else-if="activateCard === 'dualCrn'" ref="watchDualCrnCard" :param="dualCrnParam" :items="dualCrnStateList" :auto-refresh="false"></watch-dual-crn-card>
                        <devp-card v-else-if="activateCard === 'devp'" ref="devpCard" :param="devpParam" :items="stationStateList" :auto-refresh="false"></devp-card>
                        <watch-rgv-card v-else ref="watchRgvCard" :param="rgvParam" :items="rgvStateList" :auto-refresh="false"></watch-rgv-card>
                     </div>
                  </div>
               </div>
               <button class="monitor-panel-toggle" :class="{ 'is-panel-open': !panelCollapsed }" ref="monitorToggle" @click="toggleMonitorPanel">
                  <i>{{ panelCollapsed ? '>' : '<' }}</i>
                  <span>{{ panelCollapsed ? '展开面板' : '收起面板' }}</span>
               </button>
            </div>
         </div>
      </div>
      <script src="../../components/MonitorCardKit.js"></script>
      <script src="../../components/WatchCrnCard.js"></script>
      <script src="../../components/WatchDualCrnCard.js"></script>
      <script src="../../components/DevpCard.js"></script>
      <script src="../../components/MapSettingCard.js"></script>
      <script src="../../components/WatchRgvCard.js"></script>
      <script src="../../components/MapCanvas.js"></script>
      <script>
@@ -74,6 +567,22 @@
               systemStatus: true,//系统运行状态
               consoleInterval: null,//定时器存储变量
               rgvPosition: [],
               panelCollapsed: false,
               mapViewportPadding: {
                  top: 0,
                  right: 0,
                  bottom: 0,
                  left: 0
               },
               mapHudPadding: {
                  left: 14
               },
               stationTaskRange: {
                  inbound: null,
                  outbound: null
               },
               panelTransitionTimer: null,
               panelPollTimer: null,
               activateCard: 'crn',
               crnParam: {
                  crnNo: 0
@@ -81,15 +590,16 @@
               dualCrnParam: {
                  crnNo: 0
               },
               mapSettingParam: {
                  zoom: 70
               },
               devpParam: {
                  stationId: 0
               },
               rgvParam: {
                  rgvNo: 0
               },
               crnStateList: [],
               dualCrnStateList: [],
               stationStateList: [],
               rgvStateList: [],
               locMastData: [],//库位数据
               wsReconnectTimer: null,
               wsReconnectAttempts: 0,
@@ -100,10 +610,20 @@
               this.init()
            },
            mounted() {
               this.$nextTick(() => {
                  this.updateMapViewportPadding();
               });
               window.addEventListener('resize', this.updateMapViewportPadding);
               this.panelPollTimer = setInterval(() => {
                  this.refreshWorkbench(this.activateCard);
               }, 1000);
            },
            beforeDestroy() {
               if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
               if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) { try { ws.close(); } catch (e) {} }
               window.removeEventListener('resize', this.updateMapViewportPadding);
               if (this.panelTransitionTimer) { clearTimeout(this.panelTransitionTimer); this.panelTransitionTimer = null; }
               if (this.panelPollTimer) { clearInterval(this.panelPollTimer); this.panelPollTimer = null; }
            },
            watch: {
@@ -119,6 +639,7 @@
                        if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
                        this.wsReconnectAttempts = 0;
                        this.getMap();
                           this.refreshWorkbench(this.activateCard);
                    },
                    webSocketOnError() {
                        console.log("WebSocket连接发生错误");
@@ -131,21 +652,17 @@
                    webSocketOnMessage(e) {
                        const result = JSON.parse(e.data);
                        if (result.url == "/crn/table/crn/state") {
                             if(this.$refs.watchCrnCard) {
                                 this.$refs.watchCrnCard.setCrnList(JSON.parse(result.data));
                             }
                                const res = JSON.parse(result.data);
                                this.crnStateList = res && res.code === 200 ? (res.data || []) : [];
                        } else if (result.url == "/dualcrn/table/crn/state") {
                             if(this.$refs.watchDualCrnCard) {
                                 this.$refs.watchDualCrnCard.setDualCrnList(JSON.parse(result.data));
                             }
                                const res = JSON.parse(result.data);
                                this.dualCrnStateList = res && res.code === 200 ? (res.data || []) : [];
                        } else if (result.url == "/console/latest/data/station") {
                             if(this.$refs.devpCard) {
                                 this.$refs.devpCard.setStationList(JSON.parse(result.data));
                             }
                                const res = JSON.parse(result.data);
                                this.stationStateList = res && res.code === 200 ? (res.data || []) : [];
                        } else if (result.url == "/rgv/table/rgv/state") {
                             if(this.$refs.watchRgvCard) {
                                 this.$refs.watchRgvCard.setRgvList(JSON.parse(result.data));
                             }
                                const res = JSON.parse(result.data);
                                this.rgvStateList = res && res.code === 200 ? (res.data || []) : [];
                        } else if (result.url == "/basMap/lev/" + this.currentLev + "/auth") {
                            // 地图数据
                             let res = JSON.parse(result.data);
@@ -165,7 +682,31 @@
                  this.getSystemRunningStatus() //获取系统运行状态
                  this.getLevList() //获取地图层级列表
                  this.getStationTaskRange() // 获取入库/出库工作号范围
                  this.getLocMastData() //获取库位数据
               },
               getStationTaskRange() {
                  this.fetchWrkLastnoRange(1, 'inbound');
                  this.fetchWrkLastnoRange(101, 'outbound');
               },
               fetchWrkLastnoRange(id, key) {
                  $.ajax({
                     url: baseUrl + "/wrkLastno/" + id + "/auth",
                     headers: {
                        'token': localStorage.getItem('token')
                     },
                     method: "get",
                     success: (res) => {
                        if (!res || res.code !== 200 || !res.data) { return; }
                        const data = res.data;
                        this.stationTaskRange = Object.assign({}, this.stationTaskRange, {
                           [key]: {
                              start: data.sNo,
                              end: data.eNo
                           }
                        });
                     }
                  });
               },
               connectWs() {
                  if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) { return; }
@@ -204,40 +745,138 @@
                  this.currentLev = lev;
                  this.getMap()
                  this.getLocMastData()
                  this.refreshWorkbench(this.activateCard)
               },
               handleWorkbenchTabChange(type) {
                  this.activateCard = type;
                  this.refreshWorkbench(type);
               },
               refreshWorkbench(type) {
                  if (!type) { return; }
                  if (type === 'crn') {
                     this.sendWs(JSON.stringify({ url: "/crn/table/crn/state", data: {} }));
                  } else if (type === 'dualCrn') {
                     this.sendWs(JSON.stringify({ url: "/dualcrn/table/crn/state", data: {} }));
                  } else if (type === 'devp') {
                     this.sendWs(JSON.stringify({ url: "/console/latest/data/station", data: {} }));
                  } else if (type === 'rgv') {
                     this.sendWs(JSON.stringify({ url: "/rgv/table/rgv/state", data: {} }));
                  }
               },
               updateMapViewportPadding() {
                  const shell = this.$refs.monitorShell;
                  const panelWrap = this.$refs.monitorPanelWrap;
                  if (!shell) { return; }
                  const shellRect = shell.getBoundingClientRect();
                  let leftPadding = 0;
                  let hudLeft = 14;
                  if (!this.panelCollapsed && this.$refs.monitorPanel && panelWrap) {
                     const wrapRect = panelWrap.getBoundingClientRect();
                     const panelWidth = this.$refs.monitorPanel.offsetWidth || this.$refs.monitorPanel.getBoundingClientRect().width || 0;
                     const panelLeft = Math.max(0, Math.ceil(wrapRect.left - shellRect.left));
                     const panelBaseRight = panelLeft + Math.ceil(panelWidth);
                     const overlapCompensation = Math.min(56, panelWidth * 0.18);
                     leftPadding = Math.max(0, Math.ceil(panelBaseRight - overlapCompensation));
                     hudLeft = Math.max(14, Math.ceil(panelBaseRight + 34));
                  } else {
                     leftPadding = 0;
                     hudLeft = 14;
                  }
                  this.mapViewportPadding = {
                     top: 0,
                     right: 0,
                     bottom: 0,
                     left: leftPadding
                  };
                  this.mapHudPadding = {
                     left: hudLeft
                  };
               },
               scheduleMapViewportPaddingUpdate(delay) {
                  if (this.panelTransitionTimer) {
                     clearTimeout(this.panelTransitionTimer);
                     this.panelTransitionTimer = null;
                  }
                  this.panelTransitionTimer = setTimeout(() => {
                     this.panelTransitionTimer = null;
                     this.updateMapViewportPadding();
                  }, delay == null ? 0 : delay);
               },
               toggleMonitorPanel() {
                  this.panelCollapsed = !this.panelCollapsed;
                  this.scheduleMapViewportPaddingUpdate(0);
               },
               openCrn(id) {
                  this.panelCollapsed = false;
                  this.crnParam.crnNo = id;
                  this.activateCard = 'crn';
                  this.scheduleMapViewportPaddingUpdate(0);
                  this.refreshWorkbench('crn');
               },
               openDualCrn(id) {
                  this.panelCollapsed = false;
                  this.dualCrnParam.crnNo = id;
                  this.activateCard = 'dualCrn';
                  this.scheduleMapViewportPaddingUpdate(0);
                  this.refreshWorkbench('dualCrn');
               },
               openRgv(id) {
                  this.panelCollapsed = false;
                  this.rgvParam.rgvNo = id;
                  this.activateCard = 'rgv';
                  this.scheduleMapViewportPaddingUpdate(0);
                  this.refreshWorkbench('rgv');
               },
               openSite(id) {
                  this.panelCollapsed = false;
                  this.devpParam.stationId = id;
                  this.activateCard = 'devp';
                  this.scheduleMapViewportPaddingUpdate(0);
                  this.refreshWorkbench('devp');
               },
               systemSwitch() {
                  // 系统开关
                  let that = this
                  if (this.systemStatus) {
                     this.$prompt('请输入口令,并停止WCS系统', '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                     }).then(({
                        value
                     }) => {
                        that.doSwitch(0, value)
                     }).catch(() => {
                     });
                     const password = window.prompt('请输入口令,并停止WCS系统', '');
                     if (password === null) {
                        return;
                     }
                     this.doSwitch(0, password);
                  } else {
                     this.doSwitch(1)
                  }
               },
               showPageMessage(message, type) {
                  if (!message) {
                     return;
                  }
                  if (typeof this.$message === 'function') {
                     this.$message({
                        message: message,
                        type: type || 'info'
                     });
                     return;
                  }
                  if (window.ELEMENT && typeof window.ELEMENT.Message === 'function') {
                     window.ELEMENT.Message({
                        message: message,
                        type: type || 'info'
                     });
                     return;
                  }
                  if (window.layer && typeof window.layer.msg === 'function') {
                     const iconMap = {
                        success: 1,
                        error: 2,
                        warning: 0
                     };
                     window.layer.msg(message, {
                        icon: iconMap[type] != null ? iconMap[type] : 0,
                        time: 1800
                     });
                     return;
                  }
                  console[type === 'error' ? 'error' : 'log'](message);
               },
               doSwitch(operatorType, password) {
                  let that = this
@@ -267,10 +906,7 @@
                        } else if (res.code === 403) {
                           parent.location.href = baseUrl + "/login";
                        } else {
                           that.$message({
                              message: res.msg,
                              type: 'error'
                           });
                           that.showPageMessage(res.msg, 'error');
                        }
                     }
                  });
@@ -300,10 +936,7 @@
                        } else if (res.code === 403) {
                           parent.location.href = baseUrl + "/login";
                        } else {
                           that.$message({
                              message: res.msg,
                              type: 'error'
                           });
                           that.showPageMessage(res.msg, 'error');
                        }
                     }
                  });
@@ -380,9 +1013,6 @@
                  } catch (e) {
                     return false;
                  }
               },
               handleCardClick(tab, event) {
               },
               //获取库位数据
               getLocMastData() {