<!DOCTYPE html>
|
<html lang="en">
|
<head>
|
<meta charset="UTF-8">
|
<title>WCS控制中心</title>
|
<link rel="stylesheet" href="../../static/css/animate.min.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>
|
<script type="text/javascript" src="../../static/layui/layui.js"></script>
|
<script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
|
<script type="text/javascript" src="../../static/js/common.js"></script>
|
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
|
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
|
<script src="../../static/js/gsap.min.js"></script>
|
<script src="../../static/js/pixi-legacy.min.js"></script>
|
</head>
|
<body>
|
<div id="app">
|
<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>
|
|
<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>
|
<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/WatchRgvCard.js"></script>
|
<script src="../../components/MapCanvas.js"></script>
|
<script>
|
let ws;
|
var app = new Vue({
|
el: '#app',
|
data: {
|
map: [],//地图数据
|
levList: [],
|
currentLev: 1,
|
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
|
},
|
dualCrnParam: {
|
crnNo: 0
|
},
|
devpParam: {
|
stationId: 0
|
},
|
rgvParam: {
|
rgvNo: 0
|
},
|
crnStateList: [],
|
dualCrnStateList: [],
|
stationStateList: [],
|
rgvStateList: [],
|
locMastData: [],//库位数据
|
wsReconnectTimer: null,
|
wsReconnectAttempts: 0,
|
wsReconnectBaseDelay: 1000,
|
wsReconnectMaxDelay: 15000
|
},
|
created() {
|
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: {
|
|
},
|
methods: {
|
sendWs(data) {
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
ws.send(data);
|
}
|
},
|
webSocketOnOpen() {
|
console.log("WebSocket连接成功");
|
if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
|
this.wsReconnectAttempts = 0;
|
this.getMap();
|
this.refreshWorkbench(this.activateCard);
|
},
|
webSocketOnError() {
|
console.log("WebSocket连接发生错误");
|
this.scheduleWsReconnect();
|
},
|
webSocketClose() {
|
console.log("WebSocket连接关闭");
|
this.scheduleWsReconnect();
|
},
|
webSocketOnMessage(e) {
|
const result = JSON.parse(e.data);
|
if (result.url == "/crn/table/crn/state") {
|
const res = JSON.parse(result.data);
|
this.crnStateList = res && res.code === 200 ? (res.data || []) : [];
|
} else if (result.url == "/dualcrn/table/crn/state") {
|
const res = JSON.parse(result.data);
|
this.dualCrnStateList = res && res.code === 200 ? (res.data || []) : [];
|
} else if (result.url == "/console/latest/data/station") {
|
const res = JSON.parse(result.data);
|
this.stationStateList = res && res.code === 200 ? (res.data || []) : [];
|
} else if (result.url == "/rgv/table/rgv/state") {
|
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);
|
if (res.code === 200) {
|
this.map = res.data;
|
}
|
}
|
},
|
getMap() {
|
this.sendWs(JSON.stringify({
|
"url": "/basMap/lev/" + this.currentLev + "/auth",
|
"data": {}
|
}))
|
},
|
init() {
|
this.connectWs();
|
|
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; }
|
ws = new WebSocket("ws://" + window.location.host + baseUrl + "/console/websocket");
|
ws.onopen = this.webSocketOnOpen;
|
ws.onerror = this.webSocketOnError;
|
ws.onmessage = this.webSocketOnMessage;
|
ws.onclose = this.webSocketClose;
|
},
|
scheduleWsReconnect() {
|
if (this.wsReconnectTimer) { return; }
|
const attempt = this.wsReconnectAttempts + 1;
|
const jitter = Math.floor(Math.random() * 300);
|
const delay = Math.min(this.wsReconnectMaxDelay, this.wsReconnectBaseDelay * Math.pow(2, this.wsReconnectAttempts)) + jitter;
|
this.wsReconnectTimer = setTimeout(() => {
|
this.wsReconnectTimer = null;
|
this.wsReconnectAttempts = attempt;
|
this.connectWs();
|
}, delay);
|
},
|
getLevList() {
|
let that = this;
|
$.ajax({
|
url: baseUrl + "/basMap/getLevList",
|
headers: {
|
'token': localStorage.getItem('token')
|
},
|
method: "get",
|
success: (res) => {
|
let data = res.data;
|
that.levList = data;
|
}
|
})
|
},
|
switchLev(lev) {
|
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() {
|
// 系统开关
|
if (this.systemStatus) {
|
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
|
$.ajax({
|
url: baseUrl + "/console/system/switch",
|
headers: {
|
'token': localStorage.getItem('token')
|
},
|
data: {
|
operatorType: operatorType,
|
password: password
|
},
|
method: 'POST',
|
success: function(res) {
|
if (res.code === 200) {
|
if (res.data.status) {
|
$('#system-toggle-checked').attr("checked", true);
|
$('#system-run-desc').html("系统运行中...");
|
that.systemStatus = true;
|
parent.systemRunning = true;
|
} else {
|
$('#system-toggle-checked').attr("checked", false);
|
$('#system-run-desc').html("系统已停止!");
|
that.systemStatus = false;
|
parent.systemRunning = false;
|
}
|
} else if (res.code === 403) {
|
parent.location.href = baseUrl + "/login";
|
} else {
|
that.showPageMessage(res.msg, 'error');
|
}
|
}
|
});
|
},
|
getSystemRunningStatus() {
|
// 获取wcs系统运行状态
|
let that = this
|
$.ajax({
|
url: baseUrl + "/console/system/running/status",
|
headers: {
|
'token': localStorage.getItem('token')
|
},
|
method: 'POST',
|
success: function(res) {
|
if (res.code === 200) {
|
if (res.data.status) {
|
$('#system-toggle-checked').attr("checked", true);
|
$('#system-run-desc').html("系统运行中...");
|
that.systemStatus = true;
|
parent.systemRunning = true;
|
} else {
|
$('#system-toggle-checked').attr("checked", false);
|
$('#system-run-desc').html("系统已停止!");
|
that.systemStatus = false;
|
parent.systemRunning = false;
|
}
|
} else if (res.code === 403) {
|
parent.location.href = baseUrl + "/login";
|
} else {
|
that.showPageMessage(res.msg, 'error');
|
}
|
}
|
});
|
},
|
getCrnTargetCell(crnEl, bay) {
|
if (!crnEl || bay == null) {
|
return null;
|
}
|
let rowEl = crnEl.closest('tr');
|
if (!rowEl || rowEl.length === 0) {
|
return null;
|
}
|
let targetCell = null;
|
let colCounter = 0;
|
let startCount = false;
|
rowEl.find('td').each(function () {
|
// 以当前行中首次出现的track-item为起点进行列计数
|
const isTrackCrn = $(this).find('.track-crn').length > 0;
|
if (!startCount && !isTrackCrn) {
|
return;
|
}
|
let span = parseInt($(this).attr('colspan'), 10);
|
if (isNaN(span) || span < 1) {
|
span = 1;
|
}
|
if (!startCount && isTrackCrn) {
|
startCount = true;
|
}
|
colCounter += span;
|
if (targetCell == null && colCounter >= bay) {
|
targetCell = $(this);
|
return false;
|
}
|
});
|
return targetCell;
|
},
|
getDeviceNo(obj) {
|
if (this.isJson(obj)) {
|
let data = JSON.parse(obj)
|
if (data.deviceNo == null || data.deviceNo == undefined) {
|
return -1;
|
}
|
return data.deviceNo;
|
}else {
|
return -1;
|
}
|
},
|
getStationId(obj) {
|
if (this.isJson(obj)) {
|
let data = JSON.parse(obj)
|
if (data.stationId == null || data.stationId == undefined) {
|
return -1;
|
}
|
return data.stationId;
|
}else {
|
return -1;
|
}
|
},
|
getTrackSiteNo(obj) {
|
if (this.isJson(obj)) {
|
let data = JSON.parse(obj)
|
if (data.trackSiteNo == null || data.trackSiteNo == undefined) {
|
return -1;
|
}
|
return data.trackSiteNo;
|
}else {
|
return -1;
|
}
|
},
|
isJson(str) {
|
try {
|
JSON.parse(str);
|
return true;
|
} catch (e) {
|
return false;
|
}
|
},
|
//获取库位数据
|
getLocMastData() {
|
let that = this;
|
$.ajax({
|
url: baseUrl + "/console/map/locList",
|
headers: {
|
'token': localStorage.getItem('token')
|
},
|
method: "get",
|
data: {},
|
success: (res) => {
|
if (res.code === 200) {
|
that.locMastData = res.data;
|
}
|
}
|
})
|
},
|
//根据地图坐标获取库位的排列信息
|
getShelfLocInfo(rowIdx, colIdx) {
|
if (!this.locMastData || this.locMastData.length === 0) {
|
return '';
|
}
|
// 在locMastData中查找匹配的库位
|
// locType字段存储的是地图坐标信息
|
let locInfo = this.locMastData.find(loc => {
|
if (!loc.locType) return false;
|
// locType格式类似 "0-1-1" (mapX-mapY-lev)
|
let parts = loc.locType.split('-');
|
if (parts.length >= 2) {
|
return parseInt(parts[0]) === rowIdx && parseInt(parts[1]) === colIdx;
|
}
|
return false;
|
});
|
|
if (locInfo && locInfo.row1 && locInfo.bay1) {
|
return locInfo.row1 + '-' + locInfo.bay1;
|
}
|
return '';
|
},
|
}
|
})
|
</script>
|
</body>
|
</html>
|