Vue.component("devp-card", {
|
template: `
|
<div>
|
<div style="display: flex;margin-bottom: 10px;">
|
<div style="width: 100%;">输送监控</div>
|
<div style="width: 100%;text-align: right;display: flex;"><el-input size="mini" v-model="searchStationId" placeholder="请输入站号"></el-input><el-button @click="getDevpStateInfo" size="mini">查询</el-button></div>
|
</div>
|
<div style="margin-bottom: 10px;" v-if="!readOnly">
|
<div style="margin-bottom: 5px;">
|
<el-button v-if="showControl" @click="openControl" size="mini">关闭控制中心</el-button>
|
<el-button v-else @click="openControl" size="mini">打开控制中心</el-button>
|
</div>
|
<div v-if="showControl" style="display: flex;justify-content: space-between;flex-wrap: wrap;">
|
<div style="margin-bottom: 10px;width: 33%;"><el-input size="mini" v-model="controlParam.stationId" placeholder="站号"></el-input></div>
|
<div style="margin-bottom: 10px;width: 33%;"><el-input size="mini" v-model="controlParam.taskNo" placeholder="工作号"></el-input></div>
|
<div style="margin-bottom: 10px;width: 33%;"><el-input size="mini" v-model="controlParam.targetStationId" placeholder="目标站"></el-input></div>
|
<div style="margin-bottom: 10px;"><el-button @click="controlCommand()" size="mini">下发</el-button></div>
|
<div style="margin-bottom: 10px;"><el-button @click="resetCommand()" size="mini">复位</el-button></div>
|
</div>
|
</div>
|
<div style="max-height: 55vh; overflow:auto;">
|
<el-collapse v-model="activeNames" accordion>
|
<el-collapse-item v-for="(item) in displayStationList" :name="item.stationId">
|
<template slot="title">
|
<div style="width: 100%;display: flex;">
|
<div style="width: 50%;">{{ item.stationId }}站</div>
|
<div style="width: 50%;text-align: right;">
|
<el-tag v-if="item.autoing" type="success" size="small">自动</el-tag>
|
<el-tag v-else type="warning" size="small">手动</el-tag>
|
</div>
|
</div>
|
</template>
|
<el-descriptions border direction="vertical">
|
<el-descriptions-item label="编号">{{ item.stationId }}</el-descriptions-item>
|
<el-descriptions-item label="工作号">{{ item.taskNo }}</el-descriptions-item>
|
<el-descriptions-item label="目标站">{{ item.targetStaNo }}</el-descriptions-item>
|
<el-descriptions-item label="模式">{{ item.autoing ? '自动' : '手动' }}</el-descriptions-item>
|
<el-descriptions-item label="有物">{{ item.loading ? '有' : '无' }}</el-descriptions-item>
|
<el-descriptions-item label="可入">{{ item.inEnable ? 'Y' : 'N' }}</el-descriptions-item>
|
<el-descriptions-item label="可出">{{ item.outEnable ? 'Y' : 'N' }}</el-descriptions-item>
|
<el-descriptions-item label="空板信号">{{ item.emptyMk ? 'Y' : 'N' }}</el-descriptions-item>
|
<el-descriptions-item label="满板信号">{{ item.fullPlt ? 'Y' : 'N' }}</el-descriptions-item>
|
<el-descriptions-item label="运行阻塞">{{ item.runBlock ? 'Y' : 'N' }}</el-descriptions-item>
|
<el-descriptions-item label="启动入库">{{ item.enableIn ? 'Y' : 'N' }}</el-descriptions-item>
|
<el-descriptions-item label="托盘高度">{{ item.palletHeight }}</el-descriptions-item>
|
<el-descriptions-item label="条码">
|
<el-popover v-if="item.barcode" placement="top" width="460" trigger="hover">
|
<div style="text-align: center;">
|
<img
|
:src="getBarcodePreview(item.barcode)"
|
:alt="'barcode-' + item.barcode"
|
style="display: block; max-width: 100%; height: auto; margin: 0 auto; image-rendering: pixelated; background: #fff;"
|
/>
|
<div style="margin-top: 4px; font-size: 12px; word-break: break-all;">{{ item.barcode }}</div>
|
</div>
|
<span slot="reference" style="cursor: pointer; color: #409EFF;">{{ item.barcode }}</span>
|
</el-popover>
|
<span v-else>-</span>
|
</el-descriptions-item>
|
<el-descriptions-item label="重量">{{ item.weight }}</el-descriptions-item>
|
<el-descriptions-item label="故障代码">{{ item.error }}</el-descriptions-item>
|
<el-descriptions-item label="故障信息">{{ item.errorMsg }}</el-descriptions-item>
|
<el-descriptions-item label="扩展数据">{{ item.extend }}</el-descriptions-item>
|
</el-descriptions>
|
</el-collapse-item>
|
</el-collapse>
|
</div>
|
<div style="display:flex; justify-content:flex-end; margin-top:8px;">
|
<el-pagination
|
small
|
@current-change="handlePageChange"
|
@size-change="handleSizeChange"
|
:current-page="currentPage"
|
:page-size="pageSize"
|
:page-sizes="[10,20,50,100]"
|
layout="total, prev, pager, next"
|
:total="stationList.length">
|
</el-pagination>
|
</div>
|
</div>
|
`,
|
props: {
|
param: {
|
type: Object,
|
default: () => ({})
|
},
|
autoRefresh: {
|
type: Boolean,
|
default: true
|
},
|
readOnly: {
|
type: Boolean,
|
default: false
|
}
|
},
|
data() {
|
return {
|
stationList: [],
|
fullStationList: [],
|
activeNames: "",
|
searchStationId: "",
|
showControl: false,
|
controlParam: {
|
stationId: "",
|
taskNo: "",
|
targetStationId: "",
|
},
|
barcodePreviewCache: {},
|
pageSize: 25,
|
currentPage: 1,
|
timer: null
|
};
|
},
|
created() {
|
if (this.autoRefresh) {
|
this.timer = setInterval(() => {
|
this.getDevpStateInfo();
|
}, 1000);
|
}
|
},
|
beforeDestroy() {
|
if (this.timer) {
|
clearInterval(this.timer);
|
}
|
},
|
computed: {
|
displayStationList() {
|
const start = (this.currentPage - 1) * this.pageSize;
|
const end = start + this.pageSize;
|
return this.stationList.slice(start, end);
|
}
|
},
|
watch: {
|
param: {
|
handler(newVal, oldVal) {
|
if (newVal && newVal.stationId && newVal.stationId != 0) {
|
this.activeNames = newVal.stationId;
|
this.searchStationId = newVal.stationId;
|
}
|
},
|
deep: true, // 深度监听嵌套属性
|
immediate: true, // 立即触发一次(可选)
|
},
|
},
|
methods: {
|
handlePageChange(page) {
|
this.currentPage = page;
|
},
|
handleSizeChange(size) {
|
this.pageSize = size;
|
this.currentPage = 1;
|
},
|
getBarcodePreview(barcode) {
|
const value = String(barcode || "").trim();
|
if (!value) {
|
return "";
|
}
|
if (this.barcodePreviewCache[value]) {
|
return this.barcodePreviewCache[value];
|
}
|
const encodeResult = this.encodeCode128(value);
|
if (!encodeResult) {
|
return "";
|
}
|
const svg = this.buildCode128Svg(encodeResult, value);
|
const dataUrl = "data:image/svg+xml;charset=UTF-8," + encodeURIComponent(svg);
|
this.$set(this.barcodePreviewCache, value, dataUrl);
|
return dataUrl;
|
},
|
encodeCode128(value) {
|
if (!value) {
|
return null;
|
}
|
const isNumeric = /^\d+$/.test(value);
|
if (isNumeric && value.length % 2 === 0) {
|
return this.encodeCode128C(value);
|
}
|
return this.encodeCode128B(value);
|
},
|
encodeCode128B(value) {
|
const codes = [104];
|
for (let i = 0; i < value.length; i++) {
|
const code = value.charCodeAt(i) - 32;
|
if (code < 0 || code > 94) {
|
return null;
|
}
|
codes.push(code);
|
}
|
return this.buildCode128Pattern(codes);
|
},
|
encodeCode128C(value) {
|
if (value.length % 2 !== 0) {
|
return null;
|
}
|
const codes = [105];
|
for (let i = 0; i < value.length; i += 2) {
|
codes.push(parseInt(value.substring(i, i + 2), 10));
|
}
|
return this.buildCode128Pattern(codes);
|
},
|
buildCode128Pattern(codes) {
|
const patterns = this.getCode128Patterns();
|
let checksum = codes[0];
|
for (let i = 1; i < codes.length; i++) {
|
checksum += codes[i] * i;
|
}
|
const checkCode = checksum % 103;
|
const fullCodes = codes.concat([checkCode, 106]);
|
let bars = "";
|
for (let i = 0; i < fullCodes.length; i++) {
|
const code = fullCodes[i];
|
if (patterns[code] == null) {
|
return null;
|
}
|
bars += patterns[code];
|
}
|
bars += "11";
|
return bars;
|
},
|
buildCode128Svg(bars, text) {
|
const quietModules = 20;
|
const modules = quietModules * 2 + bars.split("").reduce((sum, n) => sum + parseInt(n, 10), 0);
|
const moduleWidth = modules > 300 ? 1 : 2;
|
const width = modules * moduleWidth;
|
const barTop = 10;
|
const barHeight = 110;
|
let x = quietModules * moduleWidth;
|
let black = true;
|
let rects = "";
|
for (let i = 0; i < bars.length; i++) {
|
const w = parseInt(bars[i], 10) * moduleWidth;
|
if (black) {
|
rects += '<rect x="' + x + '" y="' + barTop + '" width="' + w + '" height="' + barHeight + '" fill="#000" shape-rendering="crispEdges" />';
|
}
|
x += w;
|
black = !black;
|
}
|
return (
|
'<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="145" viewBox="0 0 ' + width + ' 145">' +
|
'<rect width="100%" height="100%" fill="#fff" />' +
|
rects +
|
'<text x="' + (width / 2) + '" y="136" text-anchor="middle" font-family="monospace" font-size="14" fill="#111">' +
|
this.escapeXml(text) +
|
"</text>" +
|
"</svg>"
|
);
|
},
|
getCode128Patterns() {
|
return [
|
"212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213",
|
"221312", "231212", "112232", "122132", "122231", "113222", "123122", "123221", "223211", "221132",
|
"221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211",
|
"212123", "212321", "232121", "111323", "131123", "131321", "112313", "132113", "132311", "211313",
|
"231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331",
|
"231131", "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111",
|
"314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214",
|
"112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111",
|
"111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", "421211", "212141",
|
"214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141",
|
"114131", "311141", "411131", "211412", "211214", "211232", "2331112"
|
];
|
},
|
escapeXml(text) {
|
return String(text)
|
.replace(/&/g, "&")
|
.replace(/</g, "<")
|
.replace(/>/g, ">")
|
.replace(/"/g, """)
|
.replace(/'/g, "'");
|
},
|
getDevpStateInfo() {
|
if (this.readOnly) {
|
// Frontend filtering for readOnly mode
|
if (this.searchStationId == "") {
|
this.stationList = this.fullStationList;
|
} else {
|
this.stationList = this.fullStationList.filter(item => item.stationId == this.searchStationId);
|
this.currentPage = 1;
|
}
|
} else if (this.$root.sendWs) {
|
this.$root.sendWs(JSON.stringify({
|
"url": "/console/latest/data/station",
|
"data": {}
|
}));
|
}
|
},
|
setStationList(res) {
|
let that = this;
|
if (res.code == 200) {
|
let list = res.data;
|
that.fullStationList = list;
|
if (that.searchStationId == "") {
|
that.stationList = list;
|
} else {
|
let tmp = [];
|
list.forEach((item) => {
|
if (item.stationId == that.searchStationId) {
|
tmp.push(item);
|
}
|
});
|
that.stationList = tmp;
|
that.currentPage = 1;
|
}
|
}
|
},
|
openControl() {
|
this.showControl = !this.showControl;
|
},
|
controlCommand() {
|
let that = this;
|
//下发命令
|
$.ajax({
|
url: baseUrl + "/station/command/move",
|
headers: {
|
token: localStorage.getItem("token"),
|
},
|
contentType: "application/json",
|
method: "post",
|
data: JSON.stringify(that.controlParam),
|
success: (res) => {
|
if (res.code == 200) {
|
that.$message({
|
message: res.msg,
|
type: "success",
|
});
|
} else {
|
that.$message({
|
message: res.msg,
|
type: "warning",
|
});
|
}
|
},
|
});
|
},
|
resetCommand() {
|
let that = this;
|
//下发命令
|
$.ajax({
|
url: baseUrl + "/station/command/reset",
|
headers: {
|
token: localStorage.getItem("token"),
|
},
|
contentType: "application/json",
|
method: "post",
|
data: JSON.stringify(that.controlParam),
|
success: (res) => {
|
if (res.code == 200) {
|
that.$message({
|
message: res.msg,
|
type: "success",
|
});
|
} else {
|
that.$message({
|
message: res.msg,
|
type: "warning",
|
});
|
}
|
},
|
});
|
},
|
},
|
});
|