Files
xgy-oa/klp-ui/src/views/wms/coil/split.vue
砂糖 9b2de46977 refactor(wms): 使用MaterialSelect组件替换物品类型和物品选择逻辑
将typing.vue、merge.vue和split.vue中的物品类型和物品选择逻辑替换为MaterialSelect组件,简化代码并提高复用性
同时为ProductSelect组件添加width: 100%样式
2025-11-04 16:31:51 +08:00

939 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="split-coil-container">
<!-- 顶部操作栏 -->
<div class="header-bar">
<div class="header-title">
<i class="el-icon-s-operation"></i>
<span>钢卷分卷</span>
</div>
<div class="header-actions">
<el-button v-if="!readonly" type="primary" size="small" @click="handleSave" :loading="loading">保存分卷</el-button>
<el-button size="small" @click="handleCancel" :disabled="loading">{{ readonly ? '返回' : '取消' }}</el-button>
</div>
</div>
<!-- 流程图区域 -->
<div class="flow-container">
<!-- 左侧母卷信息 -->
<div class="flow-left">
<div class="flow-section-title">
<span>母卷信息</span>
</div>
<div class="coil-card mother-coil">
<div class="coil-header">
<i class="el-icon-s-grid"></i>
<span class="coil-title">钢卷信息</span>
</div>
<div class="coil-body">
<div class="coil-info-row">
<span class="label">入场钢卷号</span>
<span class="value">{{ motherCoil.enterCoilNo || '—' }}</span>
</div>
<div class="coil-info-row">
<span class="label">当前钢卷号</span>
<span class="value">{{ motherCoil.currentCoilNo || '—' }}</span>
</div>
<div class="coil-info-row">
<span class="label">当前库区</span>
<span class="value">{{ motherCoil.warehouseName || '未分配' }}</span>
</div>
<div class="coil-info-row">
<span class="label">班组</span>
<span class="value">{{ motherCoil.team || '—' }}</span>
</div>
<div class="coil-info-row" v-if="motherCoil.materialName || motherCoil.productName">
<span class="label">物料名称</span>
<span class="value">{{ motherCoil.materialName || motherCoil.productName || '—' }}</span>
</div>
<div class="coil-info-row">
<span class="label">毛重</span>
<span class="value">{{ motherCoil.grossWeight ? motherCoil.grossWeight + ' t' : '—' }}</span>
</div>
<div class="coil-info-row">
<span class="label">净重</span>
<span class="value">{{ motherCoil.netWeight ? motherCoil.netWeight + ' t' : '—' }}</span>
</div>
<!-- BOM参数展示 -->
<template v-if="motherCoil.bomItems && motherCoil.bomItems.length > 0">
<el-divider content-position="left" style="margin: 15px 0 10px;">参数信息</el-divider>
<div class="bom-params">
<div class="param-item" v-for="(param, index) in motherCoil.bomItems" :key="index">
<span class="param-name">{{ param.attrKey }}</span>
<span class="param-value">{{ param.attrValue }}</span>
</div>
</div>
</template>
</div>
</div>
</div>
<!-- 中间流程箭头 -->
<div class="flow-middle">
<div class="flow-arrow-container">
<div class="arrow-line" v-for="(item, index) in splitList" :key="index">
<div class="arrow-start"></div>
<div class="arrow-body"></div>
<div class="arrow-end"></div>
</div>
</div>
<div class="flow-label">
<i class="el-icon-d-arrow-right"></i>
<span>分割</span>
</div>
</div>
<!-- 右侧子卷列表 -->
<div class="flow-right">
<div class="flow-section-title">
<span>子卷列表</span>
<div>
<el-button v-if="!readonly" type="text" size="mini" @click="copyToAllSubCoils" icon="el-icon-document-copy">复制到全部</el-button>
<el-button v-if="!readonly" type="text" size="mini" @click="addSplitItem" icon="el-icon-plus">添加子卷</el-button>
</div>
</div>
<div class="split-list">
<div class="sub-coil-card" v-for="(item, index) in splitList" :key="index">
<div class="sub-coil-header">
<span class="sub-coil-number">{{ index + 1 }}</span>
<el-button
v-if="!readonly"
type="text"
size="mini"
icon="el-icon-delete"
@click="removeSplitItem(index)"
class="btn-remove"
></el-button>
</div>
<div class="sub-coil-body">
<el-form size="small" label-width="90px">
<el-form-item label="卷号" required>
<el-input v-model="item.currentCoilNo" placeholder="输入子卷卷号" :disabled="readonly"></el-input>
</el-form-item>
<el-form-item label="班组" required>
<el-input v-model="item.team" placeholder="输入班组名称" :disabled="readonly"></el-input>
</el-form-item>
<MaterialSelect :hideType="hideType" :itemId.sync="item.itemId" :itemType.sync="item.itemType" />
<el-form-item label="毛重(t)" required>
<el-input
v-model.number="item.grossWeight"
placeholder="请输入毛重"
type="number"
step="0.01"
:disabled="readonly"
>
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="净重(t)" required>
<el-input
v-model.number="item.netWeight"
placeholder="请输入净重"
type="number"
step="0.01"
:disabled="readonly"
>
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="目标库区" required>
<el-select
v-model="item.warehouseId"
placeholder="请选择目标库位"
style="width: 100%"
filterable
:disabled="readonly"
>
<el-option
v-for="warehouse in warehouseList"
:key="warehouse.warehouseId"
:label="warehouse.warehouseName"
:value="warehouse.warehouseId"
/>
</el-select>
</el-form-item>
<el-form-item label="展示库区" required>
<ActualWarehouseSelect
v-model="item.actualWarehouseId"
placeholder="请选择真实库区"
style="width: 100%"
filterable
:disabled="readonly"
/>
</el-form-item>
</el-form>
</div>
</div>
</div>
<!-- 汇总信息 -->
<div class="summary-card">
<div class="summary-item">
<span class="summary-label">子卷数量</span>
<span class="summary-value">{{ splitList.length }}</span>
</div>
</div>
</div>
</div>
<!-- 钢卷选择器 -->
<coil-selector
:visible.sync="coilSelectorVisible"
@select="handleCoilSelect"
/>
</div>
</template>
<script>
import { getMaterialCoil, splitMaterialCoil } from '@/api/wms/coil';
import { listWarehouse } from '@/api/wms/warehouse';
import { listRawMaterial } from '@/api/wms/rawMaterial';
import { listProduct } from '@/api/wms/product';
import CoilSelector from '@/components/CoilSelector';
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect";
import MaterialSelect from "@/components/KLPService/MaterialSelect";
export default {
name: 'SplitCoil',
components: {
CoilSelector,
ActualWarehouseSelect,
MaterialSelect
},
data() {
return {
// 母卷信息
motherCoil: {
coilId: null,
enterCoilNo: '',
currentCoilNo: '',
team: '',
warehouseId: null,
warehouseName: '',
itemType: null,
itemId: null,
materialName: '',
productName: '',
grossWeight: null,
netWeight: null,
bomItems: []
},
// 子卷列表
splitList: [
{
currentCoilNo: '',
team: '',
itemType: null,
itemId: null,
grossWeight: null,
netWeight: null,
warehouseId: null,
actualWarehouseId: null
}
],
loading: false,
// 钢卷选择器可见性
coilSelectorVisible: false,
// 库区列表
warehouseList: [],
// 原材料和产品列表
rawMaterialList: [],
productList: [],
itemSearchLoading: false,
// 只读模式
readonly: false
};
},
computed: {
},
async created() {
// 先加载库区列表
await this.loadWarehouses();
// 从路由参数获取coilId和readonly
const coilId = this.$route.query.coilId;
const readonly = this.$route.query.readonly;
if (coilId) {
await this.loadMotherCoil(coilId);
}
// 设置只读模式
if (readonly === 'true' || readonly === true) {
this.readonly = true;
}
},
methods: {
// 获取子卷的物品列表
getItemListForSplit(itemType) {
if (itemType === 'raw_material') {
return this.rawMaterialList.map(item => ({
id: item.rawMaterialId,
name: item.rawMaterialName
}));
} else if (itemType === 'product') {
return this.productList.map(item => ({
id: item.productId,
name: item.productName
}));
}
return [];
},
// 物品类型变化
handleItemTypeChange(index) {
this.splitList[index].itemId = null;
this.loadItemListForSplit(this.splitList[index].itemType);
},
// 搜索子卷物品
async searchItemsForSplit(query, index) {
const itemType = this.splitList[index].itemType;
if (!itemType) {
this.$message.warning('请先选择物品类型');
return;
}
try {
this.itemSearchLoading = true;
if (itemType === 'raw_material') {
const response = await listRawMaterial({
rawMaterialName: query,
pageNum: 1,
pageSize: 50
});
if (response.code === 200) {
this.rawMaterialList = response.rows || [];
}
} else if (itemType === 'product') {
const response = await listProduct({
productName: query,
pageNum: 1,
pageSize: 50
});
if (response.code === 200) {
this.productList = response.rows || [];
}
}
} catch (error) {
console.error('搜索物品失败', error);
} finally {
this.itemSearchLoading = false;
}
},
// 加载子卷物品列表
async loadItemListForSplit(itemType) {
if (!itemType) return;
try {
this.itemSearchLoading = true;
if (itemType === 'raw_material') {
const response = await listRawMaterial({ pageNum: 1, pageSize: 100 });
if (response.code === 200) {
this.rawMaterialList = response.rows || [];
}
} else if (itemType === 'product') {
const response = await listProduct({ pageNum: 1, pageSize: 100 });
if (response.code === 200) {
this.productList = response.rows || [];
}
}
} catch (error) {
console.error('加载物品列表失败', error);
} finally {
this.itemSearchLoading = false;
}
},
// 显示钢卷选择器
showCoilSelector() {
this.coilSelectorVisible = true;
},
// 钢卷选择回调
handleCoilSelect(coil) {
this.motherCoil = {
coilId: coil.coilId,
enterCoilNo: coil.enterCoilNo || '',
currentCoilNo: coil.currentCoilNo || '',
team: coil.team || '',
warehouseId: coil.warehouseId,
warehouseName: coil.warehouseName || '',
itemType: coil.itemType,
itemId: coil.itemId,
materialName: coil.materialName || '',
productName: coil.productName || '',
grossWeight: coil.grossWeight,
netWeight: coil.netWeight,
bomItems: coil.bomItemList || coil.bomItems || []
};
this.$message.success('母卷选择成功');
},
// 加载母卷信息
async loadMotherCoil(coilId) {
try {
this.loading = true;
const response = await getMaterialCoil(coilId);
if (response.code === 200 && response.data) {
const data = response.data;
this.motherCoil = {
coilId: data.coilId,
enterCoilNo: data.enterCoilNo || '',
currentCoilNo: data.currentCoilNo || '',
team: data.team || '',
warehouseId: data.warehouseId,
warehouseName: data.warehouseName || (data.warehouse ? data.warehouse.warehouseName : ''),
itemType: data.itemType,
itemId: data.itemId,
materialName: data.materialName || (data.rawMaterial ? data.rawMaterial.rawMaterialName : ''),
productName: data.productName || (data.product ? data.product.productName : ''),
grossWeight: data.grossWeight,
netWeight: data.netWeight,
bomItems: data.bomItemList || data.bomItems || []
};
}
} catch (error) {
this.$message.error('加载母卷信息失败');
console.error(error);
} finally {
this.loading = false;
}
},
// 加载库区列表
async loadWarehouses() {
try {
const response = await listWarehouse({ pageNum: 1, pageSize: 1000 });
if (response.code === 200) {
this.warehouseList = response.rows || response.data || [];
console.log('库区列表加载成功,数量:', this.warehouseList.length);
}
} catch (error) {
console.error('加载库区列表失败', error);
}
},
// 添加子卷
addSplitItem() {
this.splitList.push({
currentCoilNo: '',
team: '',
itemType: null,
itemId: null,
grossWeight: null,
netWeight: null,
warehouseId: null,
actualWarehouseId: null
});
},
// 删除子卷
removeSplitItem(index) {
if (this.splitList.length > 1) {
this.splitList.splice(index, 1);
} else {
this.$message.warning('至少保留一个子卷');
}
},
// 保存分卷
async handleSave() {
// 验证母卷信息
if (!this.motherCoil.coilId) {
this.$message.error('请先选择母卷');
return;
}
// 验证子卷数量
if (this.splitList.length < 1) {
this.$message.error('至少需要一个子卷');
return;
}
// 验证子卷信息
for (let i = 0; i < this.splitList.length; i++) {
const item = this.splitList[i];
if (!item.currentCoilNo || item.currentCoilNo.trim() === '') {
this.$message.error(`${i + 1}个子卷的卷号不能为空`);
return;
}
if (!item.team || item.team.trim() === '') {
this.$message.error(`${i + 1}个子卷的班组不能为空`);
return;
}
if (!item.itemType) {
this.$message.error(`${i + 1}个子卷的物品类型不能为空`);
return;
}
if (!item.itemId) {
this.$message.error(`${i + 1}个子卷的物品不能为空`);
return;
}
if (item.grossWeight === null || item.grossWeight === undefined || item.grossWeight === '') {
this.$message.error(`${i + 1}个子卷的毛重不能为空`);
return;
}
if (item.netWeight === null || item.netWeight === undefined || item.netWeight === '') {
this.$message.error(`${i + 1}个子卷的净重不能为空`);
return;
}
if (!item.warehouseId) {
this.$message.error(`${i + 1}个子卷的目标库区不能为空`);
return;
}
}
const loadingInstance = this.$loading({
lock: true,
text: '正在分卷,请稍后...',
background: 'rgba(0, 0, 0, 0.7)'
})
try {
this.loading = true;
// 构造分卷数据
const splitData = {
coilId: this.motherCoil.coilId,
enterCoilNo: this.motherCoil.enterCoilNo, // 入场钢卷号(必填)
currentCoilNo: this.motherCoil.currentCoilNo,
hasMergeSplit: 1, // 1表示分卷
newCoils: this.splitList.map(item => ({
enterCoilNo: this.motherCoil.enterCoilNo, // 子卷继承母卷的入场钢卷号
currentCoilNo: item.currentCoilNo,
team: item.team,
itemType: item.itemType || this.motherCoil.itemType,
itemId: item.itemId || this.motherCoil.itemId,
grossWeight: item.grossWeight,
netWeight: item.netWeight,
warehouseId: item.warehouseId,
actualWarehouseId: item.actualWarehouseId,
hasMergeSplit: 1
}))
};
console.log('提交的分卷数据:', splitData);
const response = await splitMaterialCoil(splitData);
if (response.code === 200) {
this.$message.success('分卷保存成功');
// 延迟返回,让用户看到成功提示
setTimeout(() => {
this.$router.back();
}, 1000);
} else {
this.$message.error(response.msg || '分卷保存失败');
}
} catch (error) {
this.$message.error('分卷保存失败');
console.error(error);
} finally {
this.loading = false;
loadingInstance.close();
}
},
// 取消操作
handleCancel() {
this.$router.back();
},
// 复制到全部子卷
copyToAllSubCoils() {
if (!this.motherCoil.coilId) {
this.$message.warning('请先加载母卷信息');
return;
}
// 复制到所有子卷
this.splitList.forEach((item, index) => {
// 自动生成卷号:母卷号-1, 母卷号-2, 母卷号-3...
if (!item.currentCoilNo) {
item.currentCoilNo = `${this.motherCoil.currentCoilNo}-${index + 1}`;
}
// 复制班组
if (!item.team) {
item.team = this.motherCoil.team;
}
// 复制物品类型和物品ID
if (!item.itemType) {
item.itemType = this.motherCoil.itemType;
}
if (!item.itemId) {
item.itemId = this.motherCoil.itemId;
}
});
// 加载物品列表
if (this.motherCoil.itemType) {
this.loadItemListForSplit(this.motherCoil.itemType);
}
this.$message.success('已复制到所有子卷');
}
}
};
</script>
<style scoped lang="scss">
.split-coil-container {
padding: 20px;
background: #f5f7fa;
min-height: calc(100vh - 84px);
}
/* 顶部操作栏 */
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 16px 20px;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.header-title {
font-size: 18px;
font-weight: 500;
color: #303133;
display: flex;
align-items: center;
gap: 8px;
i {
color: #0066cc;
font-size: 20px;
}
}
/* 流程图容器 */
.flow-container {
display: flex;
gap: 40px;
background: #fff;
padding: 30px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
min-height: 600px;
}
.flow-left {
flex: 0 0 300px;
}
.flow-middle {
flex: 0 0 120px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.flow-right {
flex: 1;
min-width: 0;
}
.flow-section-title {
font-size: 16px;
font-weight: 500;
color: #303133;
margin-bottom: 16px;
padding-left: 12px;
border-left: 4px solid #0066cc;
display: flex;
justify-content: space-between;
align-items: center;
.el-button--text {
font-size: 12px;
}
}
/* 母卷卡片 */
.mother-coil {
border: 2px solid #0066cc;
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.15);
}
.coil-card {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 8px;
overflow: hidden;
}
.coil-header {
background: #0066cc;
color: #fff;
padding: 16px 20px;
display: flex;
align-items: center;
gap: 10px;
i {
font-size: 20px;
}
.coil-title {
flex: 1;
font-size: 16px;
font-weight: 600;
}
}
.coil-body {
padding: 20px;
}
.coil-info-row {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
.label {
color: #909399;
font-size: 14px;
min-width: 100px;
flex-shrink: 0;
}
.value {
color: #303133;
font-size: 14px;
font-weight: 500;
flex: 1;
word-break: break-all;
&.highlight {
color: #0066cc;
font-size: 16px;
font-weight: 600;
}
}
}
/* BOM参数展示 */
.bom-params {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-top: 10px;
}
.param-item {
display: flex;
align-items: center;
font-size: 13px;
.param-name {
color: #909399;
min-width: 80px;
}
.param-value {
color: #303133;
font-weight: 500;
}
}
/* 流程箭头 */
.flow-arrow-container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
}
.arrow-line {
position: relative;
height: 3px;
margin: 20px 0;
display: flex;
align-items: center;
}
.arrow-start {
width: 8px;
height: 8px;
background: #0066cc;
border-radius: 50%;
}
.arrow-body {
flex: 1;
height: 3px;
background: #0066cc;
}
.arrow-end {
width: 0;
height: 0;
border-left: 12px solid #0066cc;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
}
.flow-label {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: #ecf5ff;
color: #0066cc;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
display: flex;
align-items: center;
gap: 6px;
border: 1px solid #d9ecff;
}
/* 子卷列表 */
.split-list {
max-height: 500px;
overflow-y: auto;
margin-bottom: 20px;
padding-right: 10px;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #dcdfe6;
border-radius: 3px;
}
}
.sub-coil-card {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 8px;
margin-bottom: 16px;
transition: all 0.3s;
&:hover {
border-color: #0066cc;
box-shadow: 0 2px 8px rgba(0, 102, 204, 0.15);
}
}
.sub-coil-header {
background: #f5f7fa;
padding: 12px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #e4e7ed;
}
.sub-coil-number {
font-size: 16px;
font-weight: 600;
color: #0066cc;
width: 32px;
height: 32px;
border-radius: 50%;
background: #ecf5ff;
display: flex;
align-items: center;
justify-content: center;
}
.btn-remove {
color: #f56c6c;
padding: 0;
&:hover {
color: #f56c6c;
}
}
.sub-coil-body {
padding: 16px;
}
/* 汇总卡片 */
.summary-card {
background: #ecf5ff;
border: 1px solid #d9ecff;
border-radius: 8px;
padding: 16px 20px;
display: flex;
justify-content: space-around;
}
.summary-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.summary-label {
font-size: 13px;
color: #606266;
}
.summary-value {
font-size: 20px;
font-weight: 600;
color: #0066cc;
&.error {
color: #f56c6c;
}
}
// 优化按钮文字颜色
// 实心primary按钮白色文字
::v-deep .el-button--primary.el-button--small:not(.is-plain) {
color: #fff;
}
// plain按钮和text按钮蓝色文字
::v-deep .el-button--primary.el-button--small.is-plain,
::v-deep .el-button--text {
color: #409eff;
&:hover {
color: #66b1ff;
}
}
// 修复数字输入框的上下箭头溢出
.sub-coil-body {
::v-deep input[type="number"] {
appearance: textfield;
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
appearance: none;
margin: 0;
}
}
}
</style>