<template>
|
<div>
|
<vol-box
|
v-model="showDetialBox"
|
:lazy="true"
|
width="60%"
|
:padding="15"
|
title="指定库存"
|
>
|
<div class="box-head">
|
<el-alert :closable="false" style="width: 100%">
|
<el-row>
|
<el-col :span="16">
|
<span class="less-style">物料名称: {{ row.materielName }} </span>
|
<el-divider direction="vertical"></el-divider>
|
<span class="less-style">物料编号: {{ row.materielCode }} </span>
|
<el-divider direction="vertical"></el-divider>
|
<span class="less-style">需求数量: {{ row.qty }} </span>
|
<el-divider direction="vertical"></el-divider>
|
<span :class="selectionClass">已选数量: {{ selectionSum }} </span>
|
</el-col>
|
<el-col :span="8">
|
<el-link
|
type="primary"
|
size="small"
|
style="float: right; height: 20px; margin-right: 10px"
|
@click="getData"
|
>刷新</el-link
|
>
|
<el-link
|
type="primary"
|
size="small"
|
style="float: right; height: 20px; margin-right: 10px"
|
@click="openOutboundDialog"
|
>直接出库</el-link
|
>
|
</el-col>
|
</el-row>
|
</el-alert>
|
</div>
|
|
<!-- 新增筛选区域 -->
|
<div class="filter-area" style="margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 4px;">
|
<el-form :model="filterForm" inline @submit.prevent>
|
<el-form-item label="物料编号:">
|
<el-input
|
v-model="filterForm.materielCode"
|
placeholder="模糊筛选物料编号"
|
clearable
|
style="width: 180px"
|
@input="filterTable"
|
></el-input>
|
</el-form-item>
|
<el-form-item label="物料条码:">
|
<el-input
|
v-model="filterForm.barcode"
|
placeholder="模糊筛选物料条码"
|
clearable
|
style="width: 180px"
|
@input="filterTable"
|
></el-input>
|
</el-form-item>
|
<el-form-item label="托盘编号:">
|
<el-input
|
v-model="filterForm.palletCode"
|
placeholder="模糊筛选托盘编号"
|
clearable
|
style="width: 180px"
|
@input="filterTable"
|
></el-input>
|
</el-form-item>
|
<el-form-item label="货位编号:">
|
<el-input
|
v-model="filterForm.locationCode"
|
placeholder="模糊筛选货位编号"
|
clearable
|
style="width: 180px"
|
@input="filterTable"
|
></el-input>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="filterTable">搜索</el-button>
|
<el-button @click="resetFilter">重置</el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
|
<div class="box-table" style="margin-top: 1%">
|
<el-table
|
ref="singleTable"
|
:data="tableData"
|
style="width: 100%; height: 100%"
|
highlight-current-row
|
@row-click="handleRowClick"
|
height="500px"
|
@selection-change="handleSelectionChange"
|
>
|
<el-table-column type="selection" width="55"> </el-table-column>
|
<el-table-column
|
label="序号"
|
type="index"
|
fixed="left"
|
width="55"
|
align="center"
|
></el-table-column>
|
<el-table-column
|
v-for="(item, index) in tableColumns.filter((x) => !x.hidden)"
|
:key="index"
|
:prop="item.prop"
|
:label="item.title"
|
:width="item.width"
|
align="center"
|
>
|
<template #default="scoped" v-if="item.type == 'icon'">
|
<el-tooltip
|
class="item"
|
effect="dark"
|
:content="item.title"
|
placement="bottom"
|
><el-button
|
type="text"
|
@click="tableButtonClick(scoped.row, item)"
|
><i :class="item.icon" style="font-size: 22px"></i></el-button
|
></el-tooltip>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
<template #footer>
|
<!-- 去掉锁定库存按钮,只保留关闭按钮 -->
|
<el-button type="danger" size="small" @click="showDetialBox = false"
|
>关闭</el-button
|
>
|
</template>
|
</vol-box>
|
|
<!-- 出库站台选择弹窗(静态模板实现) -->
|
<el-dialog
|
v-model="showOutboundDialog"
|
title="出库操作 - 选择出库站台"
|
width="500px"
|
:append-to-body="true"
|
>
|
<el-form
|
:model="outboundForm"
|
:rules="outboundRules"
|
ref="outboundFormRef"
|
label-width="100px"
|
style="padding: 0 20px"
|
>
|
<el-form-item label="出库站台" prop="selectedPlatform" style="margin-bottom: 24px">
|
<el-select
|
v-model="outboundForm.selectedPlatform"
|
placeholder="请选择出库站台(3-12)"
|
style="width: 100%; height: 40px"
|
>
|
<el-option
|
v-for="platform in platformOptions"
|
:key="platform.value"
|
:label="platform.label"
|
:value="platform.value"
|
></el-option>
|
</el-select>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<el-button @click="showOutboundDialog = false" style="margin-right: 8px">取消</el-button>
|
<el-button type="primary" @click="confirmOutbound">确定出库</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import VolBox from "@/components/basic/VolBox.vue";
|
import { ElMessage } from "element-plus";
|
|
export default {
|
components: { VolBox },
|
data() {
|
return {
|
row: null, // 接收父组件传递的完整数据(包含明细ID集合)
|
showDetialBox: false,
|
tableData: [],
|
originalTableData: [], // 存储原始数据,用于筛选
|
tableColumns: [
|
{ prop: "materielCode", title: "物料编号", type: "string", width: 150 },
|
{ prop: "barcode", title: "物料条码", type: "string", width: 150 },
|
{ prop: "palletCode", title: "托盘编号", type: "string", width: 150 },
|
{ prop: "locationCode", title: "货位编号", type: "string", width: 180 },
|
{ prop: "useableQuantity", title: "可用数量", type: "string" },
|
],
|
selection: [], // 选中的库存数据
|
selectionSum: 0, // 已选数量总和
|
selectionClass: "less-style",
|
originalQuantity: 0,
|
|
// 筛选表单数据
|
filterForm: {
|
materielCode: "",
|
barcode: "",
|
palletCode: "",
|
locationCode: ""
|
},
|
|
// 出库弹窗相关数据
|
showOutboundDialog: false,
|
outboundForm: { selectedPlatform: "" }, // 表单绑定数据
|
outboundRules: {
|
selectedPlatform: [
|
{ required: true, message: "请选择出库站台", trigger: "change" },
|
],
|
},
|
platformOptions: [
|
{ label: "站台2", value: "2-1" },
|
{ label: "站台3", value: "3-1" },
|
],
|
pkcx: false, // 新增:默认false(如果需要可从父组件传递)
|
};
|
},
|
methods: {
|
// 接收父组件传递的数据(包含明细ID集合和物料信息)
|
open(data) {
|
this.row = data; // data结构:{materielCode, materielName, qty, orderNo, detailIds, mainOrderId, groupRow}
|
this.showDetialBox = true;
|
this.getData(); // 加载库存数据
|
this.updateSelectionClass(); // 初始化已选数量样式
|
console.log("接收的分组明细ID集合:", this.row.detailIds);
|
console.log("查询库存的物料编码:", this.row.materielCode);
|
},
|
|
// 打开出库弹窗
|
openOutboundDialog() {
|
if (this.selection.length === 0) {
|
return ElMessage.error("请选择库存数据");
|
}
|
// 校验明细ID集合
|
if (!this.row?.detailIds || this.row.detailIds.length === 0) {
|
return ElMessage.error("没有获取到单据明细ID,无法出库");
|
}
|
// 重置表单避免残留值
|
this.outboundForm.selectedPlatform = "";
|
this.showOutboundDialog = true;
|
},
|
|
// 核心修改:URL拼接传递int[] orderDetailId(重复参数名)和station,请求体传库存数据
|
confirmOutbound() {
|
this.$refs.outboundFormRef.validate((valid) => {
|
if (!valid) return;
|
|
// 校验选中库存和明细ID
|
if (this.selection.length <= 0) {
|
return this.$message.error("请勾选库存数据");
|
}
|
if (!this.row?.detailIds || this.row.detailIds.length === 0) {
|
return this.$message.error("没有获取到单据明细ID,无法出库");
|
}
|
|
try {
|
// 1. 转换ID为整数数组(确保后端能识别为int[])
|
const orderDetailId = this.row.detailIds.map(id => {
|
const num = Number(id);
|
if (isNaN(num) || !Number.isInteger(num)) {
|
throw new Error(`ID ${id} 不是有效的整数`);
|
}
|
return num;
|
});
|
|
// 2. 拼接URL:int[] 用重复参数名格式(?orderDetailId=1&orderDetailId=2&...)
|
let url = "api/Task/GenerateOutboundTask";
|
// 拼接ID数组参数
|
const idParams = orderDetailId.map(id => `orderDetailId=${id}`).join("&");
|
// 拼接站台参数
|
const stationParam = `station=${encodeURIComponent(this.outboundForm.selectedPlatform)}`;
|
// 完整URL(处理参数拼接逻辑)
|
const fullUrl = idParams
|
? `${url}?${idParams}&${stationParam}`
|
: `${url}?${stationParam}`;
|
|
console.log("出库请求URL:", fullUrl);
|
|
// 3. 发送请求:URL拼接ID和站台,请求体传库存数据(适配FromBody)
|
this.http
|
.post(fullUrl, this.selection, "数据处理中")
|
.then((x) => {
|
if (!x.status) return this.$message.error(x.message);
|
this.$message.success("操作成功");
|
this.showDetialBox = false;
|
this.$emit("parentCall", ($vue) => {
|
$vue.getData(); // 刷新父组件数据
|
});
|
})
|
.catch((err) => {
|
console.error("出库失败:", err);
|
this.$message.error(`出库失败:${err.message || '请稍后重试'}`);
|
});
|
} catch (err) {
|
this.$message.error(err.message);
|
}
|
});
|
},
|
|
// 按第一个物料编码查询库存数据
|
getData() {
|
const url = "api/StockInfo/GetSelectViewDTOs?materielCode=";
|
// 使用父组件传递的物料编码(第一个明细的编码)
|
this.http
|
.post(
|
url + this.row.materielCode + "&orderNo=" + (this.row.upperOrderNo || this.row.orderNo),
|
null,
|
"查询中"
|
)
|
.then((x) => {
|
this.tableData = x || [];
|
this.originalTableData = [...this.tableData]; // 保存原始数据
|
// 刷新后清空之前的选择和计数
|
this.clearSelection();
|
this.selectionSum = 0;
|
this.originalQuantity = 0;
|
this.updateSelectionClass();
|
// 刷新后重置筛选条件
|
this.resetFilter();
|
})
|
.catch((err) => {
|
console.error("库存查询失败:", err);
|
ElMessage.error("库存查询失败,请稍后重试");
|
this.tableData = [];
|
this.originalTableData = [];
|
});
|
},
|
|
// 撤销指定库存(如果需要)
|
revokeAssign() {
|
if (!this.row?.detailIds || this.row.detailIds.length === 0) {
|
return ElMessage.error("没有获取到单据明细ID,无法撤销");
|
}
|
|
try {
|
// ID转为整数数组,拼接URL
|
const detailIds = this.row.detailIds.map(id => Number(id));
|
const idParams = detailIds.map(id => `detailIds=${id}`).join("&");
|
const url = `api/OutboundOrderDetail/RevokeLockOutboundStock?id=${this.row.mainOrderId}&${idParams}`;
|
|
this.http
|
.post(url, null, "数据处理中")
|
.then((x) => {
|
if (!x.status) return ElMessage.error(x.message);
|
ElMessage.success("撤销成功");
|
this.showDetialBox = false;
|
this.$emit("parentCall", ($vue) => {
|
$vue.getData();
|
});
|
});
|
} catch (err) {
|
this.$message.error(`撤销失败:${err.message}`);
|
}
|
},
|
|
// 处理表格选择变化(计算已选数量)
|
handleSelectionChange(val) {
|
this.selection = val;
|
// 计算已选数量(转数字避免字符串拼接)
|
this.selectionSum = val.reduce(
|
(acc, curr) => acc + Number(curr.useableQuantity || 0),
|
0
|
) + this.originalQuantity;
|
this.updateSelectionClass();
|
},
|
|
// 更新已选数量样式(对比分组总需求数量)
|
updateSelectionClass() {
|
if (!this.row) return;
|
if (this.selectionSum === this.row.qty) {
|
this.selectionClass = "equle-style";
|
} else if (this.selectionSum < this.row.qty) {
|
this.selectionClass = "less-style";
|
} else {
|
this.selectionClass = "more-style";
|
}
|
},
|
|
// 切换表格选择
|
toggleSelection(rows) {
|
rows
|
? rows.forEach((row) => this.$refs.singleTable.toggleRowSelection(row))
|
: this.clearSelection();
|
},
|
|
// 清空选择
|
clearSelection() {
|
if (this.$refs.singleTable) {
|
this.$refs.singleTable.clearSelection();
|
}
|
},
|
|
// 行点击事件
|
handleRowClick(row) {
|
this.$refs.singleTable.toggleRowSelection(row);
|
},
|
|
// 图标按钮点击占位方法(可根据需求扩展)
|
tableButtonClick(row, item) {
|
console.log("图标按钮点击:", item.title, row);
|
},
|
|
// 筛选表格数据
|
filterTable() {
|
if (!this.originalTableData.length) return;
|
|
// 解构筛选条件并转为小写(忽略大小写)
|
const { materielCode, barcode, palletCode, locationCode } = this.filterForm;
|
const mc = materielCode.toLowerCase().trim();
|
const bc = barcode.toLowerCase().trim();
|
const pc = palletCode.toLowerCase().trim();
|
const lc = locationCode.toLowerCase().trim();
|
|
// 模糊筛选逻辑
|
this.tableData = this.originalTableData.filter(item => {
|
// 每个字段都做空值处理和小写转换,支持模糊匹配
|
const itemMc = (item.materielCode || "").toLowerCase();
|
const itemBc = (item.barcode || "").toLowerCase();
|
const itemPc = (item.palletCode || "").toLowerCase();
|
const itemLc = (item.locationCode || "").toLowerCase();
|
|
return (
|
itemMc.includes(mc) &&
|
itemBc.includes(bc) &&
|
itemPc.includes(pc) &&
|
itemLc.includes(lc)
|
);
|
});
|
|
// 筛选后清空选择状态
|
this.clearSelection();
|
this.selectionSum = 0;
|
this.updateSelectionClass();
|
},
|
|
// 重置筛选条件
|
resetFilter() {
|
this.filterForm = {
|
materielCode: "",
|
barcode: "",
|
palletCode: "",
|
locationCode: ""
|
};
|
// 恢复原始数据
|
this.tableData = [...this.originalTableData];
|
// 重置选择状态
|
this.clearSelection();
|
this.selectionSum = 0;
|
this.updateSelectionClass();
|
}
|
},
|
};
|
</script>
|
|
<style scoped>
|
.less-style {
|
color: black;
|
}
|
|
.equle-style {
|
color: green;
|
}
|
|
.more-style {
|
color: red;
|
}
|
|
/* 筛选区域样式优化 */
|
.filter-area :deep(.el-form-item) {
|
margin-bottom: 0;
|
margin-right: 10px;
|
}
|
</style>
|
|
<style>
|
.text-button:hover {
|
background-color: #f0f9eb !important;
|
}
|
|
.el-table .warning-row {
|
background: oldlace;
|
}
|
|
.box-table .el-table tbody tr:hover > td {
|
background-color: #d8e0d4 !important;
|
}
|
|
.box-table .el-table tbody tr.current-row > td {
|
background-color: #f0f9eb !important;
|
}
|
|
.el-table .success-row {
|
background: #f0f9eb;
|
}
|
|
.box-table .el-table {
|
border: 1px solid #ebeef5;
|
}
|
|
.box-head .el-alert__content {
|
width: 100%;
|
}
|
</style>
|