Vue.component("watch-crn-card", {
|
template: `
|
<div class="mc-root">
|
<div class="mc-toolbar">
|
<div class="mc-title">堆垛机监控</div>
|
<div class="mc-search">
|
<input class="mc-input" v-model="searchCrnNo" placeholder="请输入堆垛机号" />
|
<button type="button" class="mc-btn mc-btn-ghost" @click="getCrnStateInfo">查询</button>
|
</div>
|
</div>
|
|
<div v-if="!readOnly" class="mc-control-toggle">
|
<button type="button" class="mc-btn mc-btn-ghost" @click="openControl">
|
{{ showControl ? '收起控制中心' : '打开控制中心' }}
|
</button>
|
</div>
|
|
<div v-if="showControl" class="mc-control">
|
<div class="mc-control-grid">
|
<label class="mc-field">
|
<span class="mc-field-label">堆垛机号</span>
|
<input class="mc-input" v-model="controlParam.crnNo" placeholder="例如 1" />
|
</label>
|
<label class="mc-field">
|
<span class="mc-field-label">源库位</span>
|
<input class="mc-input" v-model="controlParam.sourceLocNo" placeholder="输入源点" />
|
</label>
|
<label class="mc-field mc-span-2">
|
<span class="mc-field-label">目标库位</span>
|
<input class="mc-input" v-model="controlParam.targetLocNo" placeholder="输入目标点" />
|
</label>
|
<div class="mc-action-row">
|
<button type="button" class="mc-btn" @click="controlCommandTransport">取放货</button>
|
<button type="button" class="mc-btn mc-btn-ghost" @click="controlCommandMove">移动</button>
|
<button type="button" class="mc-btn mc-btn-soft" @click="controlCommandTaskComplete">任务完成</button>
|
</div>
|
</div>
|
</div>
|
|
<div class="mc-collapse">
|
<div
|
v-for="item in displayCrnList"
|
:key="item.crnNo"
|
:class="['mc-item', { 'is-open': isActive(item.crnNo) }]"
|
>
|
<button type="button" class="mc-head" @click="toggleItem(item)">
|
<div class="mc-head-main">
|
<div class="mc-head-title">{{ item.crnNo }}号堆垛机</div>
|
<div class="mc-head-subtitle">工作号 {{ orDash(item.workNo) }} | 目标 {{ orDash(item.locNo) }}</div>
|
</div>
|
<div class="mc-head-right">
|
<span :class="['mc-badge', 'is-' + getStatusTone(item)]">{{ getStatusLabel(item) }}</span>
|
<span class="mc-chevron">{{ isActive(item.crnNo) ? '▾' : '▸' }}</span>
|
</div>
|
</button>
|
|
<div v-if="isActive(item.crnNo)" class="mc-body">
|
<div class="mc-detail-grid">
|
<div v-for="entry in buildDetailEntries(item)" :key="entry.label" class="mc-detail-cell">
|
<div class="mc-detail-label">{{ entry.label }}</div>
|
<div class="mc-detail-value">{{ entry.value }}</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<div v-if="displayCrnList.length === 0" class="mc-empty">当前没有可展示的堆垛机数据</div>
|
</div>
|
|
<div class="mc-footer">
|
<button type="button" class="mc-page-btn" :disabled="currentPage <= 1" @click="handlePageChange(currentPage - 1)">上一页</button>
|
<span>{{ currentPage }} / {{ totalPages }}</span>
|
<button type="button" class="mc-page-btn" :disabled="currentPage >= totalPages" @click="handlePageChange(currentPage + 1)">下一页</button>
|
</div>
|
</div>
|
`,
|
props: {
|
param: { type: Object, default: function () { return {}; } },
|
items: { type: Array, default: null },
|
autoRefresh: { type: Boolean, default: true },
|
readOnly: { type: Boolean, default: false }
|
},
|
data: function () {
|
return {
|
crnList: [],
|
activeNames: "",
|
searchCrnNo: "",
|
showControl: false,
|
controlParam: {
|
crnNo: "",
|
sourceLocNo: "",
|
targetLocNo: ""
|
},
|
pageSize: 12,
|
currentPage: 1,
|
timer: null
|
};
|
},
|
computed: {
|
sourceList: function () {
|
return Array.isArray(this.items) ? this.items : this.crnList;
|
},
|
filteredCrnList: function () {
|
var keyword = String(this.searchCrnNo || "").trim();
|
if (!keyword) {
|
return this.sourceList;
|
}
|
return this.sourceList.filter(function (item) {
|
return String(item.crnNo) === keyword;
|
});
|
},
|
displayCrnList: function () {
|
var start = (this.currentPage - 1) * this.pageSize;
|
return this.filteredCrnList.slice(start, start + this.pageSize);
|
},
|
totalPages: function () {
|
return Math.max(1, Math.ceil(this.filteredCrnList.length / this.pageSize) || 1);
|
}
|
},
|
watch: {
|
items: function () {
|
this.afterDataRefresh();
|
},
|
param: {
|
deep: true,
|
immediate: true,
|
handler: function (newVal) {
|
if (newVal && newVal.crnNo && newVal.crnNo !== 0) {
|
this.focusCrn(newVal.crnNo);
|
}
|
}
|
}
|
},
|
created: function () {
|
MonitorCardKit.ensureStyles();
|
if (this.autoRefresh) {
|
this.timer = setInterval(this.getCrnStateInfo, 1000);
|
}
|
},
|
beforeDestroy: function () {
|
if (this.timer) {
|
clearInterval(this.timer);
|
this.timer = null;
|
}
|
},
|
methods: {
|
orDash: function (value) {
|
return MonitorCardKit.orDash(value);
|
},
|
getStatusLabel: function (item) {
|
return MonitorCardKit.deviceStatusLabel(item && item.deviceStatus);
|
},
|
getStatusTone: function (item) {
|
return MonitorCardKit.statusTone(this.getStatusLabel(item));
|
},
|
isActive: function (crnNo) {
|
return String(this.activeNames) === String(crnNo);
|
},
|
toggleItem: function (item) {
|
var next = String(item.crnNo);
|
this.activeNames = this.activeNames === next ? "" : next;
|
},
|
focusCrn: function (crnNo) {
|
this.searchCrnNo = String(crnNo);
|
var index = this.filteredCrnList.findIndex(function (item) {
|
return String(item.crnNo) === String(crnNo);
|
});
|
if (index >= 0) {
|
this.currentPage = Math.floor(index / this.pageSize) + 1;
|
} else {
|
this.currentPage = 1;
|
}
|
this.activeNames = String(crnNo);
|
},
|
afterDataRefresh: function () {
|
if (this.currentPage > this.totalPages) {
|
this.currentPage = this.totalPages;
|
}
|
if (this.activeNames) {
|
var exists = this.filteredCrnList.some(function (item) {
|
return String(item.crnNo) === String(this.activeNames);
|
}, this);
|
if (!exists) {
|
this.activeNames = "";
|
}
|
}
|
},
|
handlePageChange: function (page) {
|
if (page < 1 || page > this.totalPages) {
|
return;
|
}
|
this.currentPage = page;
|
},
|
getCrnStateInfo: function () {
|
if (this.$root && this.$root.sendWs) {
|
this.$root.sendWs(JSON.stringify({
|
url: "/crn/table/crn/state",
|
data: {}
|
}));
|
}
|
},
|
setCrnList: function (res) {
|
if (res && res.code === 200) {
|
this.crnList = res.data || [];
|
this.afterDataRefresh();
|
}
|
},
|
openControl: function () {
|
this.showControl = !this.showControl;
|
},
|
buildDetailEntries: function (item) {
|
return [
|
{ label: "编号", value: this.orDash(item.crnNo) },
|
{ label: "工作号", value: this.orDash(item.workNo) },
|
{ label: "模式", value: this.orDash(item.mode) },
|
{ label: "状态", value: this.orDash(item.status) },
|
{ label: "源库位", value: this.orDash(item.sourceLocNo) },
|
{ label: "目标库位", value: this.orDash(item.locNo) },
|
{ label: "是否有物", value: MonitorCardKit.yesNo(item.loading) },
|
{ label: "任务接收", value: this.orDash(item.taskReceive) },
|
{ label: "列", value: this.orDash(item.bay) },
|
{ label: "层", value: this.orDash(item.lev) },
|
{ label: "货叉定位", value: this.orDash(item.forkOffset) },
|
{ label: "载货台定位", value: this.orDash(item.liftPos) },
|
{ label: "走行定位", value: this.orDash(item.walkPos) },
|
{ label: "走行速度", value: this.orDash(item.xspeed) },
|
{ label: "升降速度", value: this.orDash(item.yspeed) },
|
{ label: "叉牙速度", value: this.orDash(item.zspeed) },
|
{ label: "称重数据", value: this.orDash(item.weight) },
|
{ label: "条码数据", value: this.orDash(item.barcode) },
|
{ label: "故障代码", value: this.orDash(item.warnCode) },
|
{ label: "故障描述", value: this.orDash(item.alarm) },
|
{ label: "扩展数据", value: this.orDash(item.extend) }
|
];
|
},
|
postControl: function (url, payload) {
|
$.ajax({
|
url: baseUrl + url,
|
headers: {
|
token: localStorage.getItem("token")
|
},
|
contentType: "application/json",
|
method: "post",
|
data: JSON.stringify(payload),
|
success: function (res) {
|
if (res && res.code === 200) {
|
MonitorCardKit.showMessage(this, res.msg || "操作成功", "success");
|
} else {
|
MonitorCardKit.showMessage(this, (res && res.msg) || "操作失败", "warning");
|
}
|
}.bind(this)
|
});
|
},
|
controlCommandTransport: function () {
|
this.postControl("/crn/command/take", this.controlParam);
|
},
|
controlCommandMove: function () {
|
this.postControl("/crn/command/move", this.controlParam);
|
},
|
controlCommandTaskComplete: function () {
|
this.postControl("/crn/command/taskComplete", this.controlParam);
|
}
|
}
|
});
|