初始化仓库

This commit is contained in:
tao
2025-12-18 14:11:48 +08:00
parent e96f277a68
commit 54ec472bd4
1107 changed files with 158756 additions and 0 deletions

View File

@@ -0,0 +1,565 @@
<template>
<view>
<uni-collapse>
<uni-forms ref="form" :modelValue="formData" :rules="rules" label-align="right">
<uni-collapse-item title="直接入库单" :open="true">
<uni-forms-item label="工单" :labelWidth='80' name="workOrderCode">
<uni-easyinput suffixIcon="scan" @iconClick="scanWorkOrderCode" @change="handleChangeWorkOrderCode"
v-model="workOrderCode" type="text" />
</uni-forms-item>
<uni-forms-item label="产品入库任务单" :labelWidth='80' name="productInTaskCode">
<uni-combox :candidates="productInTaskCodeList" emptyTips="无" @input="fetchTaskInfo"
v-model="formData.productInTaskCode"></uni-combox>
</uni-forms-item>
<uni-forms-item label="上架员" :labelWidth='80' name="shelfPutBy">
<uni-easyinput suffixIcon="scan" @iconClick="scanPutBy" v-model="formData.shelfPutBy" type="text" />
<uni-data-picker popup-title="选择上架员" :localdata="dataTree" v-model="pickerData" @change="onchange"
@nodeclick="onnodeclick" @popupopened="onpopupopened" @popupclosed="onpopupclosed" placeholder="选择上架员"
class="putByPicker" :preload="true">
</uni-data-picker>
</uni-forms-item>
<uni-forms-item label="入库方式" :labelWidth='80'>
<uni-data-checkbox v-model="value" :localdata="pdcInTypeOptions" class="pdcInType" />
</uni-forms-item>
<span v-if="value=='扫物料标签' && formData.wmsProductInDetailList.length == 0" class="scanMatLabel">
<button size="mini" type="primary" class="scanMatLabelBtn" @click="show=!show">
添加物料标签
</button>
</span>
<u-modal :show="show" title="扫描物料标签编码" showCancelButton closeOnClickOverlay @cancel="cancelMaterialLabel"
@close="cancelMaterialLabel" :showConfirmButton="false">
<uni-easyinput suffixIcon="scan" @iconClick="scanBarMaterialLabel" v-model="materialLabel" type="text"
@confirm="confirmMaterialLabel" maxlength="-1" focus="true" />
</u-modal>
</uni-collapse-item>
<uni-collapse-item title="直接入库单明细" :open="true">
<uni-swipe-action>
<uni-swipe-action-item :rightOptions="rightOptions" :key="index"
v-for="(item, index) in formData.wmsProductInDetailList" @click="(data) => clickDetail(index,data)"
@change="swipChange">
<uni-badge :text="index+1" type="primary"></uni-badge>
<uni-forms-item label="物料编码" :labelWidth='90' :name="'wmsProductInDetailList.'+ index +'.materialCode'">
<uni-easyinput type="text" disabled v-model="item.materialCode"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="物料名称" :labelWidth='90' :name="'wmsProductInDetailList.'+ index +'.materialName'">
<uni-easyinput type="text" disabled v-model="item.materialName"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="物料批号" :labelWidth='90' name="'wmsProductInDetailList.'+ index +'.materialBatchNo'">
<uni-easyinput type="text" v-model="item.materialBatchNo" />
</uni-forms-item>
<uni-forms-item label="物料箱号" :labelWidth='90' name="'wmsProductInDetailList.'+ index +'.materialLotNo'">
<uni-easyinput disabled type="text" v-model="item.materialLotNo" />
</uni-forms-item>
<uni-forms-item label="类型" :labelWidth='90' name="'wmsProductInDetailList.'+ index +'.type'">
<uni-tag v-if="item.type == 1" text="合格" type="success"></uni-tag>
<uni-tag v-else-if="item.type == 2" text="不良" type="warning"></uni-tag>
<uni-tag v-else-if="item.type == 3" text="报废" type="error"></uni-tag>
</uni-forms-item>
<uni-forms-item label="推荐库位" :label-width="90" ref="myInput">
<uni-easyinput type="text" class="uni-mt-5" v-model="item.recommendLocation" disabled>
<template #right>
<uni-icons custom-prefix="iconfont" type="icon-fuzhi" size="40"
@click="clickCopy(index)"></uni-icons>
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item label="库位条码" :labelWidth='90'
name="'wmsProductInDetailList.'+ index +'.storageLocationBarcode'">
<uni-easyinput suffixIcon="scan" @iconClick="scanBarstorageLocationBarcode(index)" type="text"
v-model="item.storageLocationBarcode" />
</uni-forms-item>
<uni-forms-item label="上架数量" :labelWidth='90' name="'wmsProductInDetailList.'+ index +'number'">
<uni-easyinput disabled type="text" v-model="item.number" v-if="value == '扫物料标签'" />
<u-number-box inputWidth="120" button-size="36" v-model="item.number" :min="0" :max="item.notInNumber" v-else></u-number-box>
</uni-forms-item>
</uni-swipe-action-item>
</uni-swipe-action>
</uni-collapse-item>
</uni-forms>
</uni-collapse>
<view class="opt">
<button @click="clickCopy">一键复制</button>
<button type="primary" @click="submit">提交</button>
</view>
</view>
</template>
<script>
import {
addIn,
listTask,
getTask,
getReveive,
getDetails,
listReceiveDetail,
getDetail,
directProductInByTaskDetail,
getConnectLoc
} from "@/api/wms/pdcIn.js";
import { listDepartment } from "@/api/basic/department";
import { listEmployee } from "@/api/mes/jobIn.js";
import { listWarehouse } from "@/api/wms/warehouse";
import { listStock } from "@/api/wms/stock.js";
import { getConfigKey } from "@/api/system/config.js"
import { getMaterial_code } from "@/api/wms/purchase.js";
import { listLocation } from '@/api/basic/location';
export default {
mounted() {
// 获取任务单编码列表
listTask({
pageNum: 1,
pageSize: 25
}).then(res => {
this.productInTaskCodeList = res.rows.map(item => item.productInTaskCode);
});
// 获取部门列表
listDepartment().then((res) => {
this.dptList = res.rows
})
// 获取员工列表
listEmployee().then((res) => {
this.empList = res.rows
})
// 获取参数: 库位显示级数
getConfigKey('wms.location.size').then(res => {
this.locationSize = res.msg
})
},
data() {
return {
locationSize: null,
value: '正常',
show: false,
materialLabel: null,
workOrderCode: '',
productInTaskCodeList: [],
legalLocation: true,
dptList: [],
empList: [],
item: '',
dataTree: [],
pickerData: '',
formData: {
billType: '2',
status: '3',
productInTaskCode: '',
shelfPutBy: null,
wmsProductInDetailList: [],
},
//类型
pdcInTypeOptions: [
{ text: '正常', value: '正常' },
{ text: '扫物料标签', value: '扫物料标签' },
],
typeOptions: [{
value: 1,
label: "合格",
},
{
value: 2,
label: "不良",
},
{
value: 3,
label: "报废",
},
],
rightOptions: [{
text: '删除',
style: {
backgroundColor: '#ff2a17'
}
}, ],
rules: {
productInTaskCode: {
rules: [{
required: true,
errorMessage: '请输入产品入库任务单!'
}]
},
shelfPutBy: {
rules: [{
required: false,
errorMessage: '请输入上架员编码!'
}]
}
}
}
},
methods: {
// 工单改变
handleChangeWorkOrderCode() {
// 重置任务单列表
this.productInTaskCodeList = [];
// 重置任务单编码
this.formData.productInTaskCode = '';
// 重置明细
this.formData.wmsProductInDetailList = [];
// 获取任务单列表
this.fetchTaskList();
},
// 获取任务单列表
fetchTaskList() {
if (!this.workOrderCode) return;
listTask({
workOrderCode: this.workOrderCode
}).then(async res => {
this.productInTaskCodeList = res.rows.map(item => item.productInTaskCode);
}).catch(err => {
console.error(`获取工单号为${this.workOrderCode}的产品入库任务单列表失败,详情:${err}`);
});
},
// 扫描工单号
scanWorkOrderCode() {
const _this = this;
uni.scanCode({
scanType: ['barCode', 'qrCode'],
success: function(res) {
_this.workOrderCode = res.result;
_this.handleChangeWorkOrderCode();
}
});
},
onnodeclick(e) {
this.item = e
this.onchange(this.item)
},
onpopupopened(e) {
this.dataTree = []
this.empList.filter(item => item.deptId).forEach(item => {
item.departmentTitle = this.dptList.find(item2 => item2.id == item.deptId).departmentTitle
// 检查dataTree中是否已存在相同部门
let existingDept = this.dataTree.find(dept => dept.value === item.deptId);
if (existingDept) {
// 如果已存在相同部门则将员工信息push进该部门的children数组
existingDept.children.push({
text: item.name,
value: item.empCode
});
} else {
// 如果不存在相同部门则创建一个新部门对象包括children数组并将员工信息push进去
let newDept = {
text: item.departmentTitle,
value: item.deptId,
children: [{
text: item.name,
value: item.empCode
}]
};
this.dataTree.push(newDept);
}
})
},
onchange(e) {
this.formData.shelfPutBy = null
this.$set(this.formData, 'shelfPutBy', e.value.split('/')[0])
},
cancelMaterialLabel() {
this.materialLabel = null;
this.show = false;
},
confirmMaterialLabel(data) {
data = JSON.parse(data)
if (data) {
this.id = data.id
getDetail(data.id).then(res => {
if (res.data) {
this.formData.productInTaskCode = res.data.productInTaskCode;
let obj = {
materialCode: res.data.materialCode,
materialName: res.data.materialName,
materialBatchNo: res.data.materialBatchNo,
materialLotNo: res.data.materialLotNo,
type: res.data.type,
storageLocationBarcode: res.data.storageLocationBarcode,
number: res.data.number
}
this.formData.wmsProductInDetailList.push(obj)
this.materialLabel = null;
this.show = false;
} else {
this.$modal.msg("未查询到该条物料明细!")
}
})
}
},
scanBarMaterialLabel() {
const _this = this;
uni.scanCode({
scanType: ['barCode', 'qrCode'],
success: function(res) {
_this.materialLabel = res.result;
// console.log(materialLabel)
_this.confirmMaterialLabel(_this.materialLabel);
}
});
},
// 验证库位合法性
async validateLocation(item) {
const code = item.storageLocationBarcode
if (!code) return false
if (['2', '4'].includes(this.locationSize)) return true
let fullLocation = null
if (this.locationSize == '0') {
fullLocation = code
} else if (this.locationSize == '5') {
// 用两级库位查完整库位
const res = await listLocation({
storageShelvesCode: code.split('-')[0],
storageLocationCode: code.split('-').slice(1).join('-')
})
fullLocation = res.rows[0]?.storageLocationBarcode || null
}
const data = await getConnectLoc({ connectLoc: fullLocation })
if (data.data) {
item.whCode = fullLocation.split('-')[0] || null
item.areaCode = fullLocation.split('-')[1] || null
item.shelvesCode = fullLocation.split('-')[2] || null
item.storageLocationCode = fullLocation.split('-').slice(3).join('-') || null
} else {
this.$modal.msg("库位条码校验错误,请重新输入!")
}
return data.data
},
// 扫描库位编码
scanBarstorageLocationBarcode(i) {
const _this = this;
uni.scanCode({
scanType: ['barCode', 'qrCode'],
success: function(res) {
_this.$set(_this.formData.wmsProductInDetailList[i], "storageLocationBarcode", res.result);
}
});
},
selectTypeList() {
listTask({
pageNum: 1,
pageSize: 25
}).then(async res => {
for (var i in res.rows) {
this.productInTaskCodeList.push(res.rows[i].productInTaskCode);
}
});
},
// 依次获取推荐库位
async fetchLocation(taskDetail) {
const promises = taskDetail.map(async (item) => {
if (!item.materialCode) {
return {
...item,
recommend: null,
recommendLocation: null
}
}
const res = await getMaterial_code(item.materialCode);
// 提供默认值以防 res 为 null 或 undefined
const { data } = res || {};
const recommend = data || null;
const recommendLocation = data ? this.getRecommend(data) : null;
return { ...item, recommend, recommendLocation };
});
// 等待所有异步操作完成
const updatedList = await Promise.all(promises);
return updatedList;
},
async fetchTaskInfo() {
// 清空明细单列表
this.formData.wmsProductInDetailList = [];
if (!this.formData.productInTaskCode) return;
let taskId = null;
let taskDetail = [];
// 获取任务单 id
await listTask({
productInTaskCode: this.formData.productInTaskCode
}).then(res => {
taskId = res.rows[0].id
}).catch(err => {
console.error(`获取入库任务单号为${this.formData.productInTaskCode}的任务单 id 失败,详情:${err}`);
})
// 根据任务单 id 获取任务单明细
await getTask(taskId).then(res => {
taskDetail = res.data.wmsProductInTaskDetailList
}).catch(err => {
console.error(`获取入库任务单 id 为${taskId}的任务单明细失败,详情:${err}`);
})
if (taskDetail.length === 0) return
// 获取推荐库位
this.formData.wmsProductInDetailList = await this.fetchLocation(taskDetail)
console.log("明细单列表: ", this.formData.wmsProductInDetailList)
},
// 上架员
scanPutBy() {
const _this = this;
uni.scanCode({
scanType: ['qrCode', 'barCode'],
success: function(res) {
_this.formData.shelfPutBy = res.result;
}
});
},
// 拆解推荐库位
getRecommend(recommend) {
const { whCode, areaCode, shelvesCode, storageLocationCode } = recommend
if (!(whCode || areaCode || shelvesCode || storageLocationCode)) return null
switch (this.locationSize) {
case '0':
return [whCode, areaCode, shelvesCode, storageLocationCode].join('-')
case '2':
return areaCode
case '4':
return storageLocationCode
case '5':
return [shelvesCode, storageLocationCode].join('-')
}
},
// 校验明细单各项库位是否为空
checkLocation() {
this.formData.wmsProductInDetailList.forEach(item => {
if (!item.storageLocationBarcode) {
this.$modal.msg("库位条码不允许为空")
return false
}
})
return true
},
// 提交
async submit() {
const _this = this;
if (!this.checkLocation()) return
_this.$modal.loading('校验库位中')
await _this.formData.wmsProductInDetailList.forEach(async item => {
// 避免明细传递失败,删除明细 id
delete item.id;
if (!item.storageLocationBarcode) {
this.$modal.msg("库位条码不允许为空");
return;
}
const res = await _this.validateLocation(item)
if (!res) {
item.storageLocationBarcode = null
this.$modal.msg("库位条码校验错误,请重新输入!")
return
}
})
_this.$modal.closeLoading()
this.$refs.form.validate().then(res => {
uni.showModal({
title: '提示',
content: '您确定完成直接入库吗?',
success: function(res) {
if (res.confirm) {
if (_this.value == '扫物料标签') {
let obj = {
id: _this.id,
whCode: _this.formData.wmsProductInDetailList[0].whCode,
areaCode: _this.formData.wmsProductInDetailList[0]
.areaCode,
shelvesCode: _this.formData.wmsProductInDetailList[0]
.shelvesCode,
storageLocationCode: _this.formData.wmsProductInDetailList[
0].storageLocationCode
}
_this.$modal.loading('提交中')
directProductInByTaskDetail(obj).then(async res => {
_this.$modal.closeLoading();
_this.$modal.msgSuccess("入库成功!");
_this.$tab.switchTab('/pages/work/index');
});
} else {
if (_this.formData.productInTaskCode) {
_this.$modal.loading('提交中')
addIn(_this.formData).then(async res => {
_this.$modal.closeLoading();
_this.$modal.msgSuccess("入库成功!");
_this.$tab.switchTab('/pages/work/index');
});
}
}
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
});
},
// 复制库位
clickCopy(index) {
// 创建新数组避免引用问题
const newList = this.formData.wmsProductInDetailList.map(item => ({ ...item }));
if (typeof index === 'number') {
// 复制单个项
newList[index] = {
...newList[index],
storageLocationBarcode: newList[index].recommendLocation,
whCode: newList[index].recommend.whCode,
areaCode: newList[index].recommend.areaCode,
shelvesCode: newList[index].recommend.shelvesCode,
storageLocationCode: newList[index].recommend.storageLocationCode
};
} else {
// 复制所有项
newList.forEach(item => {
item.storageLocationBarcode = item.recommendLocation;
item.whCode = item.recommend.whCode;
item.areaCode = item.recommend.areaCode;
item.shelvesCode = item.recommend.shelvesCode;
item.storageLocationCode = item.recommend.storageLocationCode;
});
}
// 创建新对象确保响应式更新
this.formData = {
...this.formData,
wmsProductInDetailList: newList
};
}
}
}
</script>
<style lang="scss" scoped>
.putByPicker {
margin-top: 5px;
}
.pdcInType {
margin-top: 6px;
}
.scanMatLabel {
display: flex;
}
.scanMatLabel .scanMatLabelBtn {
justify-content: center;
font-size: 1.1rem;
}
.opt {
// position: fixed;
// bottom: 0;
width: 100%;
padding: 2vw 0;
display: flex;
justify-content: space-evenly;
}
.opt button {
flex: 1;
margin: 0 5px;
}
</style>