<template>
|
<div class="scan-container">
|
<el-card class="scan-card no-print">
|
<div slot="header" class="clearfix">
|
<span>灯光校验扫描出库</span>
|
</div>
|
<el-row>
|
<el-col :span="12">
|
<el-form ref="form" :model="formData" label-width="120px" class="scan-form">
|
<el-form-item label="快递号/出库单号">
|
<el-input ref="expressCode" v-model="formData.expressCode" class="input-300" autofocus @keyup.native.enter.stop="getOrderData"></el-input>
|
<span class="sub-item">
|
<span class="sub-label">物料校验:</span>
|
<el-switch v-model="formData.isValidateProductCode" @change="onIsValidateProductCodeChange"></el-switch>
|
</span>
|
</el-form-item>
|
<el-form-item label="物料条码">
|
<el-input ref="productModel" v-model="formData.productModel" :disabled="!formData.isValidateProductCode" class="input-300" @keyup.native.enter.stop="checkPackingBarcode"></el-input>
|
</el-form-item>
|
<el-form-item label="扫描数量">
|
<el-input-number ref="scanQty" v-model="formData.scanQty" :min="0" :disabled="!formData.isValidateProductCode" controls-position="right" class="input-120" @change="setScanQty"></el-input-number>
|
<span class="sub-item hidden">
|
<span class="sub-label">称重:</span>
|
<el-input-number v-model="formData.weight" :min="0" controls-position="right" class="input-105"></el-input-number>
|
</span>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="lightLabel">点亮标签</el-button>
|
<el-button type="success" @click="partialSave">复核提交</el-button>
|
<el-button @click="onReset">重置</el-button>
|
</el-form-item>
|
</el-form>
|
</el-col>
|
<el-col :span="12">
|
<el-form ref="form" :model="formData" label-width="120px" class="scan-form">
|
<el-form-item label="灯光服务器地址">
|
<el-input v-model="serverIP" class="w-300" @keyup.native.enter.stop="getWayBillPrinter"></el-input>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="connectSocketServer('showMsg')">连接服务器</el-button>
|
<el-tag :type="serverStatus==='已连接服务器'?'success':'danger'">{{ serverStatus }}</el-tag>
|
</el-form-item>
|
</el-form>
|
</el-col>
|
</el-row>
|
</el-card>
|
<el-card class="scan-card body-no-padding no-print">
|
<div slot="header" class="clearfix">
|
<span class="padding-top-10">扫描结果</span>
|
<el-button type="text" class="floatRight" @click="setting.visible=true">字段设置</el-button>
|
</div>
|
<el-table ref="scan-table" :data="tableData" :row-class-name="rowClass" stripe style="width: 100%" class="scan-table" @row-dblclick="setCurrent">
|
<template v-for="(item, index) in setting.fields">
|
<template v-if="['unFinishedQuantity','finishedQuantity'].indexOf(item.prop)>=0">
|
<el-table-column v-if="item.visible" :key="index" :prop="item.prop" :label="item.label" :width="item.width">
|
<template slot-scope="scope">
|
<template v-if="!formData.isValidateProductCode">
|
<el-input-number v-model="scope.row[item.prop]" :min="0" :max="scope.row['Quantity']" size="mini" class="w-100pc" controls-position="right" @change="rowChangeQty(item.prop, scope.row)"></el-input-number>
|
</template>
|
<template v-else>
|
{{ scope.row[item.prop] }}
|
</template>
|
</template>
|
</el-table-column>
|
</template>
|
<template v-else-if="'rowStatus'.indexOf(item.prop)>=0">
|
<el-table-column v-if="item.visible" :key="index" :prop="item.prop" :label="item.label" :width="item.width">
|
<template slot-scope="scope">
|
<template>
|
<el-tag v-if="scope.row.rowStatus==='已确认'" type="success" effect="dark">
|
{{ scope.row.rowStatus }}
|
</el-tag>
|
<el-tag v-else type="warning">
|
{{ scope.row.rowStatus }}
|
</el-tag>
|
</template>
|
</template>
|
</el-table-column>
|
</template>
|
<template v-else-if="'scanWeight'.indexOf(item.prop)>=0">
|
<el-table-column v-if="item.visible" :key="index" :prop="item.prop" :label="item.label" :width="item.width">
|
<template slot-scope="scope">
|
<template>
|
<el-input v-model="scope.row[item.prop]" size="mini" class="w-100pc"></el-input>
|
</template>
|
</template>
|
</el-table-column>
|
</template>
|
<template v-else>
|
<el-table-column v-if="item.visible" :key="index" :prop="item.prop" :label="item.label" :width="item.width">
|
</el-table-column>
|
</template>
|
</template>
|
</el-table>
|
</el-card>
|
<scan-setting-dialog ref="setting-dialog" :visible.sync="setting.visible" :fields="setting.fields" :name="setting.name">
|
</scan-setting-dialog>
|
|
<!-- 显示打印模板 -->
|
<print v-if="formData.isOuter_PrintBill" ref="printRef" :ids="''+formData.order_Id" type="express"></print>
|
<!-- 显示打印装箱清单模板 -->
|
<PrintPacking v-if="formData.isOuter_Packinglist" ref="printRef" :ids="''+formData.order_Id" :code="''+formData.caseNumber" type="express"></PrintPacking>
|
|
<!--声音文件-->
|
<audio ref="sound_error" controls="controls" style="display:none;">
|
<source src="@/assets/sounds/error2.mp3" type="audio/mpeg" />
|
</audio>
|
<audio ref="sound_correct" controls="controls" style="display:none;">
|
<source src="@/assets/sounds/feixin.mp3" type="audio/mpeg" />
|
</audio>
|
<audio controls="controls" style="display:none;">
|
<source ref="sound_scan" src="@/assets/sounds/saomiao.wav" type="audio/mpeg" />
|
</audio>
|
|
</div>
|
</template>
|
|
<script>
|
import print from "@/views/sys/print/base2";
|
import PrintPacking from "@/views/sys/print/base3";
|
import YrtScanMixins from "@/components/common/yrtScanMixins.vue";
|
import ScanSettingDialog from "@/components/common/components/scanSettingDialog.vue";
|
|
export default {
|
name: "outbound-scan-out-light",
|
components: {
|
print,
|
PrintPacking,
|
ScanSettingDialog
|
},
|
mixins: [YrtScanMixins],
|
data() {
|
return {
|
formData: {
|
order_Id: 0,
|
orderCode: null,
|
expressCode: null,
|
caseNumber: null,
|
productModel: null, // 包装条码
|
scanQty: 0, // 扫描数量
|
quantityOuter: null,
|
weight: 0, // 称重
|
isValidateProductCode: false, // 是否校验物料
|
isOuter_PrintBill: false,
|
isOuter_Packinglist: false,
|
wrapperBarcode: ""
|
},
|
// 扫描数据
|
tableData: [],
|
// radio2: 1,
|
// 当前正在扫描的数据
|
currentRow: {},
|
// 已经找到的数据
|
existRows: [],
|
// 装箱方式:0:常规扫描,1:一品一箱,2:多品一箱
|
caseMode: 0,
|
// 一次扫描的数量
|
scanCount: 1,
|
// 装箱新增行
|
caseNewRows: [],
|
// 配置参数
|
config: {
|
// 自动生成上架单
|
in_generateShelve: true,
|
// 是否启用装箱操作
|
in_caseNumber: false,
|
// 支持一品多码
|
sku_productToMultiBarcode: true,
|
// 打印面单
|
outer_printBill: true,
|
// 打印装箱清单
|
Outer_Packinglist: true
|
},
|
/** ***********************
|
* websockect
|
* *********************** */
|
// 服务器IP
|
serverIP: null,
|
ws: null,
|
// 服务器状态
|
serverStatus: "未连接",
|
// 扫描列设置对话框参数
|
setting: {
|
visible: false,
|
name: "outbound-scan-out-light",
|
fields: [
|
{
|
prop: "productModel",
|
label: "条形码",
|
visible: true,
|
width: 130,
|
order: 1
|
},
|
{
|
prop: "quantityOrder",
|
label: "物料数量",
|
visible: true,
|
width: 80,
|
order: 2
|
},
|
{
|
prop: "quantityOuter",
|
label: "已出库数量",
|
visible: true,
|
width: 90,
|
order: 3
|
},
|
{
|
prop: "finishedQuantity",
|
label: "已扫描数量",
|
visible: true,
|
width: 90,
|
order: 4
|
},
|
{
|
prop: "unFinishedQuantity",
|
label: "未扫描数量",
|
visible: true,
|
width: 90,
|
order: 5
|
},
|
{
|
prop: "productCode",
|
label: "物料编号",
|
visible: false,
|
width: 130,
|
order: 6
|
},
|
{
|
prop: "purchasePrice",
|
label: "单价",
|
visible: false,
|
width: 80,
|
order: 11
|
},
|
{
|
prop: "positionName",
|
label: "货位",
|
visible: true,
|
width: 120,
|
order: 8
|
},
|
{
|
prop: "rowStatus",
|
label: "出库状态",
|
visible: true,
|
width: 80,
|
order: 8
|
},
|
{
|
prop: "productSpec",
|
label: "物料规格",
|
visible: true,
|
width: 80,
|
order: 12
|
},
|
{
|
prop: "weight",
|
label: "单位毛重",
|
visible: false,
|
width: 80,
|
order: 13
|
},
|
{
|
prop: "sumTotalWeight",
|
label: "合计重量",
|
visible: false,
|
width: 80,
|
order: 14
|
},
|
{
|
prop: "scanWeight",
|
label: "已扫重量",
|
visible: false,
|
width: 80,
|
order: 15
|
},
|
{
|
prop: "productName",
|
label: "物料名称",
|
visible: true,
|
width: null,
|
order: 16
|
}
|
]
|
}
|
};
|
},
|
computed: {
|
// 服务器地址
|
serverDomain: function() {
|
return "ws://" + this.serverIP + ":2888/";
|
}
|
},
|
activated() {
|
const isValidateProductCode = localStorage["out_isValidateProductCode"];
|
if (isValidateProductCode) {
|
this.formData.isValidateProductCode = isValidateProductCode === "true";
|
}
|
},
|
mounted() {
|
this.getConfig();
|
|
this.serverIP = localStorage.getItem("serverIP");
|
// 连接灯光系统
|
this.connectSocketServer();
|
setInterval(() => {
|
if (!this.ws || this.ws.readyState === 2 || this.ws.readyState === 3) {
|
this.connectSocketServer();
|
}
|
}, 20 * 1000);
|
|
// 字段设置
|
const setting = localStorage[this.setting.name + "-setting"];
|
if (setting) {
|
this.setting.fields = JSON.parse(setting);
|
}
|
},
|
methods: {
|
// 连接服务器
|
connectSocketServer(type) {
|
const userInfo = this.common.getUserInfo();
|
const support = "MozWebSocket" in window ? "MozWebSocket" : "WebSocket" in window ? "WebSocket" : null;
|
localStorage.setItem("serverIP", this.serverIP);
|
|
if (support == null) {
|
alert("Your browser cannot support WebSocket!");
|
return;
|
}
|
|
if (this.ws) {
|
this.ws.close();
|
}
|
|
// 0 (CONNECTING), 1 (OPEN), 2 (CLOSING),3 (CLOSED)
|
if (!this.ws || this.ws.readyState === 2 || this.ws.readyState === 3) {
|
this.ws = new window[support](this.serverDomain);
|
} else {
|
this.$message.warning("已连接,无需重复连接");
|
}
|
|
this.ws.onerror = function(e) {
|
// console.log(e);
|
};
|
|
this.ws.onmessage = evt => {
|
var data = JSON.parse(evt.data);
|
if (data.action === "closeLabelLight") {
|
this.tableData.forEach(item => {
|
if (item.orderList_Id === data.rowId) {
|
item.rowStatus = "已确认";
|
}
|
});
|
}
|
};
|
|
// when the connection is established, this method is called
|
this.ws.onopen = () => {
|
var msg = {};
|
msg.action = "setOnline";
|
msg.fromMobile = userInfo.mobile;
|
this.ws.send(JSON.stringify(msg));
|
this.serverStatus = "已连接服务器";
|
if (type === "showMsg") {
|
if (this.serverStatus === "已连接服务器") {
|
this.$message.success(this.serverStatus);
|
} else {
|
this.$message.error(this.serverStatus);
|
}
|
}
|
};
|
|
// when the connection is closed, this method is called
|
this.ws.onclose = () => {
|
this.serverStatus = "未连接服务器";
|
};
|
},
|
// 点亮标签
|
lightLabel() {
|
const positionNames = [];
|
this.tableData
|
.filter(item => {
|
return item.finishedQuantity > 0 && item.rowStatus !== "已确认";
|
})
|
.forEach(item => {
|
const _positionNames = item.positionName.split(",");
|
|
for (const pn of _positionNames) {
|
positionNames.push({
|
rowId: item.orderList_Id,
|
positionName: pn,
|
qty: item.finishedQuantity,
|
status: "未点亮"
|
});
|
}
|
});
|
if (!positionNames.length) {
|
this.$message.error("没有可提交的数据");
|
return;
|
}
|
if (!this.ws || this.ws.readyState === 2 || this.ws.readyState === 3) {
|
this.$message.warning("未连接灯光系统,无法点亮");
|
return;
|
}
|
|
const dataInfo = {
|
action: "SendCode",
|
dataList: positionNames
|
};
|
this.ws.send(JSON.stringify(dataInfo));
|
},
|
|
// 是否校验物料切换
|
onIsValidateProductCodeChange() {
|
localStorage["out_isValidateProductCode"] = this.formData.isValidateProductCode;
|
this.tableData.forEach(item => {
|
if (this.formData.isValidateProductCode) {
|
item.finishedQuantity = 0;
|
item.unFinishedQuantity = item.quantityOrder;
|
} else {
|
item.finishedQuantity = item.unFinishedQuantity;
|
item.unFinishedQuantity = 0;
|
}
|
});
|
},
|
// 获得配置参数
|
getConfig() {
|
var keys = Object.keys(this.config).join(",");
|
var url = "/api/sys/param/getConfig";
|
var params = {
|
openNodeApi: true,
|
keys: keys
|
};
|
var callback = res => {
|
this.common.showMsg(res);
|
this.valueList = res.data;
|
// 获得参数值列表,将数字转换为对象
|
res.data.forEach(item => {
|
var value03 = item.value03;
|
if (this.common.isNumber(item.value03)) {
|
value03 = parseInt(item.value03);
|
}
|
this.$set(this.config, item.Value02, !!value03);
|
});
|
};
|
var target = this.$refs["settings"];
|
this.common.ajax(url, params, callback, target);
|
},
|
// 获取扫描数据
|
getOrderData() {
|
var expressCode = this.formData.expressCode;
|
if (!expressCode) {
|
this.$message.error("快递单号不能为空!");
|
return;
|
}
|
var url = "/api/outbound/outScan/getOrderData";
|
var params = {
|
openNodeApi: true,
|
expressCode: expressCode
|
};
|
var callBack = res => {
|
this.common.showMsg(res);
|
if (res.result) {
|
this.tableData = res.data.map(row => {
|
row.unFinishedQuantity = row.quantityOrder;
|
row.finishedQuantity = 0;
|
row.sortIndex = 0;
|
row.rowStatus = "未确认";
|
if (this.formData.isValidateProductCode) {
|
row.finishedQuantity = 0;
|
} else {
|
row.finishedQuantity = row.unFinishedQuantity;
|
row.unFinishedQuantity = 0;
|
}
|
|
return row;
|
});
|
if (this.config.outer_printBill === true) {
|
this.formData.order_Id = this.tableData[0].order_Id;
|
}
|
this.formData.caseNumber = res.Dynamic;
|
// 条码框获得焦点
|
if (this.formData.isValidateProductCode) {
|
this.$refs.productModel.focus();
|
this.$refs.productModel.select();
|
} else {
|
this.$refs.expressCode.focus();
|
this.$refs.expressCode.select();
|
}
|
} else {
|
this.onReset();
|
}
|
};
|
this.common.ajax(url, params, callBack, true);
|
},
|
// 判断扫描包装条码
|
checkPackingBarcode() {
|
this.checkPackingProductModel();
|
// var code = this.formData.productModel;
|
// if (!code) {
|
// this.$refs.productModel.focus();
|
// this.$refs.productModel.select();
|
// this.$message.error("物料条码不能为空!");
|
// return;
|
// }
|
// var isExistsProduct = false; // 物料是否在明细存在
|
// this.tableData.forEach(rowData => {
|
// if (!rowData) return true;
|
// if (this.checkBarcodeExist(code, rowData)) {
|
// this.checkProductCode(this);
|
// this.$refs.productModel.focus();
|
// this.$refs.productModel.select();
|
// isExistsProduct = true;
|
// return false;
|
// }
|
// });
|
// if (!isExistsProduct) {
|
// this.$messager.error("物料条码[" + code + "]不存在!", "red");
|
// return;
|
// }
|
},
|
// 校验SKU条码
|
checkProductCode() {
|
var code = this.formData.productModel;
|
code = code.trim();
|
if (!code) return;
|
var isItemFinished = true; // 当前物料是否全部扫描完成
|
var isExistsProduct = false; // 物料是否在明细存在
|
this.tableData.forEach(rowData => {
|
if (this.checkBarcodeExist(code, rowData)) {
|
isExistsProduct = true;
|
var finishedQuantity = rowData.finishedQuantity;
|
var unFinishedQuantity = rowData.unFinishedQuantity;
|
if (unFinishedQuantity > 0) {
|
isItemFinished = false;
|
if (this.caseMode === 1) {
|
// 一品一箱
|
this.addRow_1(rowData);
|
} else if (this.caseMode === 2) {
|
// 多品一箱
|
this.addRow_2(rowData);
|
} else {
|
rowData.finishedQuantity = finishedQuantity + 1;
|
rowData.unFinishedQuantity = unFinishedQuantity - 1;
|
rowData.caseNumber = this.formData.caseNumber;
|
this.currentRow = rowData;
|
// 设置排序,进行置顶
|
this.currentRow.sortIndex = 1;
|
}
|
return false;
|
}
|
return true;
|
}
|
});
|
if (!isExistsProduct) {
|
this.$message({
|
showClose: true,
|
message: "物料[" + code + "]在该订单不存在!",
|
type: "warning"
|
});
|
return;
|
}
|
if (isItemFinished) {
|
this.$message({
|
showClose: true,
|
message: "物料[" + code + "]已经扫描完成,不需要再扫描!",
|
type: "warning"
|
});
|
return;
|
}
|
// 扫描置顶
|
this.tableData = this.tableData.sort(function(a, b) {
|
return b.sortIndex - a.sortIndex;
|
});
|
|
this.tableData.forEach(element => {
|
element.sortIndex = 0;
|
});
|
},
|
// 判断扫描是否完成
|
checkFinished() {
|
var isFinished = true;
|
this.tableData.forEach(rowData => {
|
var unFinishedQuantity = rowData.unFinishedQuantity;
|
if (unFinishedQuantity > 0) {
|
isFinished = false;
|
}
|
});
|
return isFinished;
|
},
|
// 判断条码是否存在
|
checkBarcodeExist(barcode, rowData) {
|
var isExists = false;
|
if (!rowData) return false;
|
|
if (this.config.sku_productToMultiBarcode) {
|
// 支持一品多码
|
isExists =
|
barcode === rowData.productModel ||
|
barcode === rowData.RelationCode ||
|
barcode === rowData.RelationCode2 ||
|
barcode === rowData.RelationCode3 ||
|
barcode === rowData.RelationCode4 ||
|
barcode === rowData.RelationCode5;
|
} else {
|
isExists = barcode === rowData.productModel;
|
}
|
|
return isExists;
|
},
|
// 改变明细扫描数量
|
rowFinishedQtyChange(row) {
|
row.unFinishedQuantity = Math.Round(row.quantityOrder - row.quantityOuter - row.finishedQuantity, 4);
|
},
|
// 设置扫描数量
|
setScanCount(barcode, count, isAdd) {
|
if (!count || count < 0) {
|
this.$message({
|
showClose: true,
|
message: '"数量不正确',
|
type: "warning"
|
});
|
return;
|
}
|
if (!barcode) {
|
this.$message({
|
showClose: true,
|
message: '"条码不能为空',
|
type: "warning"
|
});
|
return;
|
}
|
},
|
// 行样式
|
rowClass({ row, rowIndex }) {
|
if (this.currentRow === row) {
|
return "row-active";
|
}
|
},
|
// 复核提交,封箱(部分打包),然后计算已打包数量,设置 新快递单号为 “可用”
|
partialSave() {
|
var expressCode = this.formData.expressCode; // 快递单号
|
var wrapperBarcode = this.formData.wrapperBarcode; // 包材条码
|
var newExpressCode = ""; // 新快递单号
|
var weight = this.formData.weight; // 重量
|
if (!expressCode) {
|
this.$message({
|
showClose: true,
|
message: "快递单号不能为空!",
|
type: "warning"
|
});
|
return;
|
}
|
|
var dataArray = this.tableData
|
.filter(item => {
|
return item.finishedQuantity > 0;
|
})
|
.map(rowData => {
|
return {
|
caseNumber: rowData.caseNumber,
|
order_Id: rowData.order_Id,
|
orderCode: rowData.orderCode,
|
orderList_Id: rowData.orderList_Id,
|
scanCount: rowData.finishedQuantity,
|
totalWeight: rowData.scanWeight,
|
weight: rowData.weight
|
};
|
});
|
if (!dataArray.length) {
|
this.$message({
|
showClose: true,
|
message: "请先扫描条码!",
|
type: "error"
|
});
|
return;
|
}
|
|
var existRows = this.tableData.some(item => {
|
return item.rowStatus === "未确认" && item.finishedQuantity > 0;
|
});
|
|
const doWork = noTip => {
|
const url = "/api/outbound/outScan/orderPartialSaveScan";
|
const params = {
|
openNodeApi: true,
|
expressCode: expressCode,
|
wrapperBarcode: wrapperBarcode,
|
newExpressCode: newExpressCode,
|
data: dataArray,
|
weight: weight
|
};
|
const callBack = res => {
|
this.common.showMsg(res);
|
if (res.result) {
|
this.onReset();
|
// 自动打印,系统配置中心开启,且勾选打印复选框
|
if (this.config.outer_printBill === true && this.formData.isOuter_PrintBill) {
|
this.$refs.printRef.lodopPrint();
|
}
|
}
|
};
|
this.common.ajax(url, params, callBack, true);
|
};
|
|
if (existRows) {
|
this.$confirm("已扫描出库数量未确认,是否强制出库?", "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning"
|
})
|
.then(() => {
|
doWork(true);
|
})
|
.catch(() => {
|
this.$message({
|
type: "info",
|
message: "已取消"
|
});
|
});
|
} else {
|
doWork();
|
}
|
},
|
// 重置
|
onReset() {
|
const isValidateProductCode = localStorage["out_isValidateProductCode"];
|
|
this.formData = {
|
order_Id: 0,
|
orderCode: null,
|
expressCode: null,
|
caseNumber: null,
|
productModel: null, // 包装条码
|
scanQty: 0, // 扫描数量
|
quantityOuter: null,
|
weight: 0, // 称重
|
isValidateProductCode: isValidateProductCode === "true", // 是否校验物料
|
isOuter_PrintBill: false,
|
isOuter_Packinglist: false,
|
wrapperBarcode: ""
|
};
|
// 扫描数据
|
this.tableData = [];
|
// radio2: 1,
|
// 当前正在扫描的数据
|
this.currentRow = {};
|
// 已经找到的数据
|
this.existRows = [];
|
// 装箱方式:0:常规扫描,1:一品一箱,2:多品一箱
|
this.caseMode = 0;
|
// 一次扫描的数量
|
this.scanCount = 1;
|
// 装箱新增行
|
this.caseNewRows = [];
|
// 配置参数
|
this.config = {
|
// 自动生成上架单
|
in_generateShelve: true,
|
// 是否启用装箱操作
|
in_caseNumber: false,
|
// 支持一品多码
|
sku_productToMultiBarcode: true,
|
// 打印面单
|
outer_printBill: true,
|
// 打印装箱清单
|
Outer_Packinglist: true
|
};
|
this.$refs.expressCode.focus();
|
}
|
}
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
@import "../../../styles/scan.scss";
|
</style>
|