|
|
@@ -0,0 +1,349 @@
|
|
|
+<template>
|
|
|
+ <ele-modal
|
|
|
+ title="库存解锁申请"
|
|
|
+ :visible.sync="visible"
|
|
|
+ :before-close="handleClose"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :close-on-press-escape="false"
|
|
|
+ append-to-body
|
|
|
+ width="75%"
|
|
|
+ :maxable="true"
|
|
|
+ destroy-on-close
|
|
|
+ @closed="handleClosed"
|
|
|
+ @opened="handleOpened"
|
|
|
+ >
|
|
|
+ <el-card shadow="never">
|
|
|
+ <el-descriptions :column="3" border size="small">
|
|
|
+ <el-descriptions-item label="创建时间">{{
|
|
|
+ formData.createTime || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="物品编码">{{
|
|
|
+ formData.productCode || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="名称">{{
|
|
|
+ formData.productName || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="批次号">{{
|
|
|
+ formData.batchNo || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="系统内部批次号">{{
|
|
|
+ formData.systemBatchNo || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="牌号">{{
|
|
|
+ formData.brandNum || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="型号">{{
|
|
|
+ formData.categoryModel || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="规格">{{
|
|
|
+ formData.specification || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="机型">{{
|
|
|
+ formData.modelKey || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="颜色">{{
|
|
|
+ formData.colorKey || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="计量单位">{{
|
|
|
+ formData.measureUnit || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="锁定数量">{{
|
|
|
+ formData.lockQuantity != null ? formData.lockQuantity : '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="仓库">{{
|
|
|
+ formData.warehouseName || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ <div style="margin-top: 12px">
|
|
|
+ <ele-pro-table
|
|
|
+ ref="eleTable"
|
|
|
+ :columns="columns"
|
|
|
+ :datasource="tableData"
|
|
|
+ height="calc(100vh - 405px)"
|
|
|
+ full-height="calc(100vh - 116px)"
|
|
|
+ tool-class="ele-toolbar-form"
|
|
|
+ :pageSize="20"
|
|
|
+ :initLoad="false"
|
|
|
+ :needPage="false"
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ >
|
|
|
+ <template v-slot:unionQuantity="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-model="row.unionQuantity"
|
|
|
+ placeholder="请输入"
|
|
|
+ @input="(value) => inputNum(value, row)"
|
|
|
+ ></el-input>
|
|
|
+ </template>
|
|
|
+ </ele-pro-table>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button
|
|
|
+ :loading="loading"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ @click="handleSave(0)"
|
|
|
+ >保存</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ :loading="loading"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ @click="handleSave(1)"
|
|
|
+ >提交</el-button
|
|
|
+ >
|
|
|
+ <el-button size="small" :disabled="loading" @click="handleClose"
|
|
|
+ >取消</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </ele-modal>
|
|
|
+</template>
|
|
|
+<script>
|
|
|
+ import { inboundLockPage, saveUnlock, submitUnlock } from '@/api/wms';
|
|
|
+
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ visible: false,
|
|
|
+ loading: false,
|
|
|
+ // 主表数据:展示字段 + 提交时使用白名单字段
|
|
|
+ formData: {},
|
|
|
+ // 该库存维度下的有效锁定明细
|
|
|
+ tableData: [],
|
|
|
+ // 已勾选的解锁明细
|
|
|
+ selectionList: [],
|
|
|
+ columns: [
|
|
|
+ {
|
|
|
+ columnKey: 'selection',
|
|
|
+ type: 'selection',
|
|
|
+ width: 45,
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ columnKey: 'index',
|
|
|
+ type: 'index',
|
|
|
+ width: 50,
|
|
|
+ align: 'center',
|
|
|
+ label: '序号',
|
|
|
+ fixed: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'quantity',
|
|
|
+ label: '锁定数量',
|
|
|
+ showOverflowTooltip: true,
|
|
|
+ width: 130,
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'unionQuantity',
|
|
|
+ label: '解锁数量',
|
|
|
+ slot: 'unionQuantity',
|
|
|
+ showOverflowTooltip: true,
|
|
|
+ width: 130,
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'sourceOrderNo',
|
|
|
+ label: '关联单据编码',
|
|
|
+ showOverflowTooltip: true,
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'sourceType',
|
|
|
+ label: '关联单据类型',
|
|
|
+ align: 'center',
|
|
|
+ formatter: (row) =>
|
|
|
+ this.LockOrderSourceTypeEnum[row.sourceType] || '-'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'orderDate',
|
|
|
+ label: '锁定时间',
|
|
|
+ align: 'center'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ LockOrderSourceTypeEnum: {
|
|
|
+ 0: '无定义',
|
|
|
+ 1: '销售发货单',
|
|
|
+ 2: '领料出库单',
|
|
|
+ 3: '调拨出库单',
|
|
|
+ 4: '销售订单'
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /**
|
|
|
+ * 打开解锁弹窗:row 为库存台账行(批次维度)
|
|
|
+ */
|
|
|
+ async open(row) {
|
|
|
+ const productId = row?.categoryId || row?.productId;
|
|
|
+ const productCode = row?.categoryCode || row?.productCode;
|
|
|
+ if (!row?.systemBatchNo && !row?.batchNo) {
|
|
|
+ return this.$message.warning('缺少批次信息,无法发起库存解锁申请');
|
|
|
+ }
|
|
|
+ if (!productId || !productCode || !row?.warehouseId) {
|
|
|
+ return this.$message.warning(
|
|
|
+ '缺少物品或仓库信息,无法发起库存解锁申请'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (!(Number(row.lockQuantity) > 0)) {
|
|
|
+ return this.$message.warning('该库存批次没有锁库数量,无需解锁');
|
|
|
+ }
|
|
|
+ this.loading = true;
|
|
|
+ try {
|
|
|
+ // 系统内部批次号为空时,后端要求同时携带批次号、物品ID、物品编码和仓库ID
|
|
|
+ const res = await inboundLockPage({
|
|
|
+ pageNum: 1,
|
|
|
+ size: 500,
|
|
|
+ systemBatchNo: row.systemBatchNo || '',
|
|
|
+ batchNo: row.batchNo || '',
|
|
|
+ productId,
|
|
|
+ productCode,
|
|
|
+ warehouseId: row.warehouseId
|
|
|
+ });
|
|
|
+ const list = res?.list || [];
|
|
|
+ if (!list.length) {
|
|
|
+ return this.$message.warning(
|
|
|
+ '未查询到可解锁的锁库明细,请刷新库存台账后重试'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ // 仅保留后端需要的字段,避免把台账行主键等无关数据回传
|
|
|
+ this.formData = {
|
|
|
+ systemBatchNo: row.systemBatchNo || '',
|
|
|
+ batchNo: row.batchNo || '',
|
|
|
+ productId,
|
|
|
+ productCode,
|
|
|
+ productName: row.categoryName || row.productName || '',
|
|
|
+ warehouseId: row.warehouseId,
|
|
|
+ warehouseName: row.warehouseName || '',
|
|
|
+ createTime: row.createTime,
|
|
|
+ brandNum: row.brandNum,
|
|
|
+ categoryModel: row.categoryModel,
|
|
|
+ specification: row.specification,
|
|
|
+ modelKey: row.modelKey,
|
|
|
+ colorKey: row.colorKey,
|
|
|
+ measureUnit: row.measureUnit,
|
|
|
+ lockQuantity: row.lockQuantity
|
|
|
+ };
|
|
|
+ this.selectionList = [];
|
|
|
+ // 先显示弹窗,再灌入锁定明细:
|
|
|
+ // el-table 在 display:none 的祖先容器内渲染时列宽会算成 0,会出现“有数据但表格看不到行”,
|
|
|
+ // 必须等弹窗显示后再赋值,并在渲染后主动重新布局一次。
|
|
|
+ this.visible = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.tableData = list.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ unionQuantity: ''
|
|
|
+ }));
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const table = this.$refs.eleTable;
|
|
|
+ if (table && table.doLayout) {
|
|
|
+ table.doLayout();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ } catch (e) {
|
|
|
+ // 请求异常拦截器已提示 500/-1,这里兜底业务异常并避免未处理的 Promise 拒绝
|
|
|
+ this.$message.error(e?.message || '查询锁库明细失败');
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 解锁数量仅允许数字,且不能超过锁定数量
|
|
|
+ inputNum(value, row) {
|
|
|
+ let numStr = String(value ?? '').replace(/[^\d.]/g, '');
|
|
|
+ // 仅保留一个小数点,避免出现 1.2.3 这类后端无法解析的非法数字
|
|
|
+ const firstDot = numStr.indexOf('.');
|
|
|
+ if (firstDot > -1) {
|
|
|
+ numStr =
|
|
|
+ numStr.slice(0, firstDot + 1) +
|
|
|
+ numStr.slice(firstDot + 1).replace(/\./g, '');
|
|
|
+ }
|
|
|
+ const num = parseFloat(numStr);
|
|
|
+ const max = Number(row.quantity) || 0;
|
|
|
+ if (!isNaN(num) && num > max) {
|
|
|
+ this.$message.warning(`解锁数量不能超过锁定数量(${max})`);
|
|
|
+ this.$set(row, 'unionQuantity', max);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$set(row, 'unionQuantity', numStr);
|
|
|
+ },
|
|
|
+ handleSelectionChange(selection) {
|
|
|
+ this.selectionList = selection;
|
|
|
+ },
|
|
|
+ handleClose() {
|
|
|
+ this.visible = false;
|
|
|
+ },
|
|
|
+ handleClosed() {
|
|
|
+ this.formData = {};
|
|
|
+ this.tableData = [];
|
|
|
+ this.selectionList = [];
|
|
|
+ this.loading = false;
|
|
|
+ },
|
|
|
+ // 弹窗动画结束后兜底重算列宽,避免表格在隐藏容器内完成布局导致有数据却不显示
|
|
|
+ handleOpened() {
|
|
|
+ const table = this.$refs.eleTable;
|
|
|
+ if (table && table.doLayout) {
|
|
|
+ table.doLayout();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async handleSave(status) {
|
|
|
+ if (this.loading) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!this.selectionList.length) {
|
|
|
+ return this.$message.warning('请选择解锁数据');
|
|
|
+ }
|
|
|
+ const details = [];
|
|
|
+ for (const item of this.selectionList) {
|
|
|
+ const unionQuantity = Number(item.unionQuantity);
|
|
|
+ if (!unionQuantity || unionQuantity <= 0) {
|
|
|
+ return this.$message.error('请填写解锁数量');
|
|
|
+ }
|
|
|
+ if (unionQuantity > Number(item.quantity || 0)) {
|
|
|
+ return this.$message.error('解锁数量不能超过锁定数量');
|
|
|
+ }
|
|
|
+ details.push({
|
|
|
+ lockOrderId: item.lockOrderId,
|
|
|
+ lockOrderDetailId: item.lockOrderDetailId,
|
|
|
+ unionQuantity
|
|
|
+ });
|
|
|
+ }
|
|
|
+ const postData = {
|
|
|
+ systemBatchNo: this.formData.systemBatchNo,
|
|
|
+ batchNo: this.formData.batchNo,
|
|
|
+ productId: this.formData.productId,
|
|
|
+ productCode: this.formData.productCode,
|
|
|
+ productName: this.formData.productName,
|
|
|
+ warehouseId: this.formData.warehouseId,
|
|
|
+ warehouseName: this.formData.warehouseName,
|
|
|
+ details
|
|
|
+ };
|
|
|
+ this.loading = true;
|
|
|
+ let savedId = null;
|
|
|
+ try {
|
|
|
+ savedId = await saveUnlock(postData);
|
|
|
+ if (status === 1) {
|
|
|
+ await submitUnlock({ businessId: savedId });
|
|
|
+ this.$message.success('提交成功');
|
|
|
+ } else {
|
|
|
+ this.$message.success('保存成功');
|
|
|
+ }
|
|
|
+ this.visible = false;
|
|
|
+ this.$emit('success');
|
|
|
+ } catch (e) {
|
|
|
+ if (savedId) {
|
|
|
+ // 草稿已落库,重复保存会产生多条申请,改为提示用户到解锁申请列表重新提交
|
|
|
+ this.visible = false;
|
|
|
+ this.$message.error(
|
|
|
+ `解锁申请已保存为草稿,提交失败:${e?.message || '请稍后重试'}`
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ this.$message.error(e?.message || '保存失败');
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|