refactor(wms): 重构钢卷管理模块,优化代码结构

- 将钢卷管理页面拆分为基础组件和多个视图组件
- 新增历史数据和产品数据专用视图
- 重构钢卷追溯功能,支持更详细的操作记录展示
- 移除首页和订单看板中不再使用的组件
- 优化产品选择组件,默认类型改为undefined
- 修改钢卷溯源API,支持更灵活的查询参数
This commit is contained in:
砂糖
2025-10-29 14:33:26 +08:00
parent 128700da0f
commit 2c49f4d600
8 changed files with 878 additions and 404 deletions

View File

@@ -58,9 +58,10 @@ export function delMaterialCoil(CoilMaterialId) {
}
// 钢卷溯源查询
export function getMaterialCoilTrace(CoilMaterialId) {
export function getMaterialCoilTrace(query) {
return request({
url: '/wms/materialCoil/trace/' + CoilMaterialId,
method: 'get'
url: '/wms/materialCoil/trace',
method: 'get',
params: query
})
}

View File

@@ -87,7 +87,7 @@ export default {
productName: undefined,
owner: undefined,
unit: undefined,
type: 'product'
type: undefined,
},
addDialogVisible: false,
rules: {

View File

@@ -8,12 +8,6 @@
<el-tab-pane label="业绩总览" name="performance">
<PerformanceArea mode="mini" :performance-area="dashboardData.performanceArea" />
</el-tab-pane>
<el-tab-pane label="订单统计" name="currentSituation">
<CurrentSituationArea mode="mini" :current-situation-area="dashboardData.currentSituationArea" />
</el-tab-pane>
<el-tab-pane label="采购推荐" name="recommendation">
<RecommendationArea mode="mini" :recommendation-area="dashboardData.recommendationArea" />
</el-tab-pane>
</el-tabs>
</div>
</template>

View File

@@ -16,12 +16,6 @@
<FlowTable />
</el-card>
</el-col>
<el-col :span="12">
<el-card style="height: 500px;">
<OrderDashboard />
</el-card>
</el-col>
</el-row>
</div>
</template>

View File

@@ -0,0 +1,21 @@
<template>
<BasePage :qrcode="qrcode" :querys="querys"/>
</template>
<script>
import BasePage from './panels/base.vue';
export default {
components: {
BasePage
},
data() {
return {
qrcode: false,
querys: {
dataType: 0
}
}
}
}
</script>

View File

@@ -1,401 +1,21 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input
v-model="queryParams.enterCoilNo"
placeholder="请输入入场钢卷号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="当前钢卷号" prop="currentCoilNo">
<el-input
v-model="queryParams.currentCoilNo"
placeholder="请输入当前钢卷号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="厂家卷号" prop="supplierCoilNo">
<el-input
v-model="queryParams.supplierCoilNo"
placeholder="请输入厂家原料卷号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="班组" prop="team">
<el-input
v-model="queryParams.team"
placeholder="请输入班组"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
>转移</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="materialCoilList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo" />
<el-table-column label="当前钢卷号" align="center" prop="currentCoilNo" />
<el-table-column label="厂家原料卷号" align="center" prop="supplierCoilNo" />
<el-table-column label="数据类型" align="center" prop="dataType">
<template slot-scope="scope">
{{ scope.row.dataType == 0 ? '历史数据' : '当前数据' }}
</template>
</el-table-column>
<!-- <el-table-column label="所在库区ID" align="center" prop="warehouseId" /> -->
<!-- <el-table-column label="下一库区ID" align="center" prop="nextWarehouseId" /> -->
<!-- <el-table-column label="关联二维码ID" align="center" prop="qrcodeRecordId" /> -->
<el-table-column label="二维码">
<template slot-scope="scope">
<QRCode
v-if="scope.row.dataType == 1"
:content="scope.row.qrcodeRecordId"
:size="50"
/>
<!-- <el-image
:src="scope.row.qrcodeUrl"
:preview-src-list="[scope.row.qrcodeUrl]"
fit="contain"
style="width: 50px; height: 50px;"
/> -->
</template>
</el-table-column>
<el-table-column label="班组" align="center" prop="team" />
<!-- <el-table-column label="是否合卷/分卷" align="center" prop="hasMergeSplit" /> -->
<!-- <el-table-column label="父卷号" align="center" prop="parentCoilNos" /> -->
<!-- <el-table-column label="物品ID" align="center" prop="itemId" /> -->
<!-- <el-table-column label="物品类型" align="center" prop="itemType" /> -->
<!-- <el-table-column label="状态" align="center" prop="status" /> -->
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
>转移</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改钢卷物料对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input v-model="form.enterCoilNo" placeholder="请输入入场钢卷号" />
</el-form-item>
<el-form-item label="当前钢卷号" prop="currentCoilNo">
<el-input v-model="form.currentCoilNo" placeholder="请输入当前钢卷号" />
</el-form-item>
<el-form-item label="厂家原料卷号" prop="supplierCoilNo">
<el-input v-model="form.supplierCoilNo" placeholder="请输入厂家原料卷号" />
</el-form-item>
<el-form-item label="所在库区ID" prop="warehouseId">
<warehouse-select v-model="form.warehouseId" placeholder="请选择仓库/库区/库位" style="width: 100%;" clearable />
</el-form-item>
<el-form-item label="下一库区ID" prop="nextWarehouseId">
<el-input v-model="form.nextWarehouseId" placeholder="请输入下一库区ID" />
</el-form-item>
<el-form-item label="关联二维码ID" prop="qrcodeRecordId">
<el-input v-model="form.qrcodeRecordId" placeholder="请输入关联二维码ID" />
</el-form-item>
<el-form-item label="班组" prop="team">
<el-input v-model="form.team" placeholder="请输入班组" />
</el-form-item>
<el-form-item label="是否合卷/分卷" prop="hasMergeSplit">
<el-input v-model="form.hasMergeSplit" placeholder="请输入是否合卷/分卷" />
</el-form-item>
<el-form-item label="父卷号" prop="parentCoilNos">
<el-input v-model="form.parentCoilNos" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="物品类型" prop="itemType">
<el-input v-model="form.itemType" placeholder="请输入物品类型" />
</el-form-item>
<el-form-item label="物品ID" prop="itemId">
<el-input v-model="form.itemId" placeholder="请输入物品ID" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
<BasePage :qrcode="qrcode" :querys="querys"/>
</template>
<script>
import { listMaterialCoil, getMaterialCoil, delMaterialCoil, addMaterialCoil, updateMaterialCoil } from "@/api/wms/coil";
import WarehouseSelect from "@/components/WarehouseSelect";
import QRCode from "../print/components/QRCode.vue";
import BasePage from './panels/base.vue';
export default {
name: "MaterialCoil",
components: {
WarehouseSelect,
QRCode
BasePage
},
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 钢卷物料表格数据
materialCoilList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
enterCoilNo: undefined,
currentCoilNo: undefined,
supplierCoilNo: undefined,
dataType: undefined,
warehouseId: undefined,
nextWarehouseId: undefined,
qrcodeRecordId: undefined,
team: undefined,
hasMergeSplit: undefined,
parentCoilNos: undefined,
itemId: undefined,
itemType: undefined,
status: undefined,
},
// 表单参数
form: {},
// 表单校验
rules: {
enterCoilNo: [
{ required: true, message: "入场钢卷号不能为空", trigger: "blur" }
],
currentCoilNo: [
{ required: true, message: "当前钢卷号不能为空", trigger: "blur" }
],
supplierCoilNo: [
{ required: true, message: "厂家原料卷号不能为空", trigger: "blur" }
],
warehouseId: [
{ required: true, message: "所在库区ID不能为空", trigger: "blur" }
],
team: [
{ required: true, message: "班组不能为空", trigger: "blur" }
],
itemId: [
{ required: true, message: "物品ID不能为空", trigger: "blur" }
],
itemType: [
{ required: true, message: "物品类型不能为空", trigger: "change" }
],
remark: [
{ required: true, message: "备注不能为空", trigger: "blur" }
],
qrcode: true,
querys: {
dataType: 1
}
};
},
created() {
this.getList();
},
methods: {
/** 查询钢卷物料列表 */
getList() {
this.loading = true;
listMaterialCoil(this.queryParams).then(response => {
this.materialCoilList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
coilId: undefined,
enterCoilNo: undefined,
currentCoilNo: undefined,
supplierCoilNo: undefined,
dataType: undefined,
warehouseId: undefined,
nextWarehouseId: undefined,
qrcodeRecordId: undefined,
team: undefined,
hasMergeSplit: undefined,
parentCoilNos: undefined,
itemId: undefined,
itemType: undefined,
status: undefined,
remark: undefined,
delFlag: undefined,
createTime: undefined,
createBy: undefined,
updateTime: undefined,
updateBy: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.coilId)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加钢卷物料";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.loading = true;
this.reset();
const coilId = row.coilId || this.ids
getMaterialCoil(coilId).then(response => {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改钢卷物料";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.coilId != null) {
updateMaterialCoil(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
} else {
addMaterialCoil(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const coilIds = row.coilId || this.ids;
this.$modal.confirm('是否确认删除钢卷物料编号为"' + coilIds + '"的数据项?').then(() => {
this.loading = true;
return delMaterialCoil(coilIds);
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
/** 导出按钮操作 */
handleExport() {
this.download('system/materialCoil/export', {
...this.queryParams
}, `materialCoil_${new Date().getTime()}.xlsx`)
}
}
};
</script>
}
</script>

View File

@@ -0,0 +1,823 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input v-model="queryParams.enterCoilNo" placeholder="请输入入场钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="当前钢卷号" prop="currentCoilNo">
<el-input v-model="queryParams.currentCoilNo" placeholder="请输入当前钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="数据状态">
<el-select v-model="queryParams.dataType" placeholder="请选择数据状态" clearable>
<el-option :value="0" label="历史数据">
历史数据
</el-option>
<el-option :value="1" label="当前数据">
当前数据
</el-option>
</el-select>
</el-form-item>
<el-form-item label="厂家卷号" prop="supplierCoilNo">
<el-input v-model="queryParams.supplierCoilNo" placeholder="请输入厂家原料卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="班组" prop="team">
<el-input v-model="queryParams.team" placeholder="请输入班组" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single"
@click="handleCheck">修正</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple"
@click="handleDelete">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="materialCoilList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo" />
<el-table-column label="当前钢卷号" align="center" prop="currentCoilNo" />
<el-table-column label="厂家原料卷号" align="center" prop="supplierCoilNo" />
<el-table-column label="数据类型" align="center" prop="dataType">
<template slot-scope="scope">
{{ scope.row.dataType == 0 ? '历史数据' : '当前数据' }}
</template>
</el-table-column>
<el-table-column label="二维码" v-if="qrcode">
<template slot-scope="scope">
<QRCode v-if="scope.row.dataType == 1" :content="scope.row.qrcodeRecordId" :size="50" />
</template>
</el-table-column>
<el-table-column label="班组" align="center" prop="team" />
<el-table-column label="毛重" align="center" prop="grossWeight" />
<el-table-column label="净重" align="center" prop="netWeight" />
<el-table-column label="物品" align="center" prop="itemName">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" productId=" scope.row.itemId">
<template slot-scope="{ product }">
{{ product.productName }}({{ product.productCode }})
</template>
</ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :materialId="scope.row.itemId">
<template slot-scope="{ material }">
{{ material.rawMaterialName }}({{ material.rawMaterialCode }})
</template>
</RawMaterialInfo>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownloadQRCode(scope.row)"
v-if="qrcode">
下载二维码
</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleCheck(scope.row)">修正</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
<el-button size="mini" type="text" icon="el-icon-search" @click="handleTrace(scope.row)">追溯</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<!-- 添加或修改钢卷物料对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input v-model="form.enterCoilNo" placeholder="请输入入场钢卷号" />
</el-form-item>
<el-form-item label="当前钢卷号" prop="currentCoilNo">
<el-input v-model="form.currentCoilNo" placeholder="请输入当前钢卷号" />
</el-form-item>
<el-form-item v-if="!form.coilId" label="厂家原料卷号" prop="supplierCoilNo">
<el-input v-model="form.supplierCoilNo" placeholder="请输入厂家原料卷号" />
</el-form-item>
<el-form-item label="所在库区" prop="warehouseId">
<warehouse-select v-model="form.warehouseId" placeholder="请选择仓库/库区/库位" style="width: 100%;" clearable />
</el-form-item>
<el-form-item label="班组" prop="team">
<el-input v-model="form.team" placeholder="请输入班组" />
</el-form-item>
<el-form-item label="物品类型" prop="itemType">
<el-select v-model="form.itemType" placeholder="请选择物品类型">
<el-option label="成品" value="product" />
<el-option label="原料" value="raw_material" />
</el-select>
</el-form-item>
<el-form-item label="物品ID" prop="itemId">
<MaterialSelect v-model="form.itemId" :itemType="form.itemType" placeholder="请选择物品ID" style="width: 100%;"
clearable />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="毛重" prop="grossWeight">
<el-input v-model="form.grossWeight" placeholder="请输入毛重" />
</el-form-item>
<el-form-item label="净重" prop="NetWeight">
<el-input v-model="form.netWeight" placeholder="请输入净重" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 钢卷追溯对话框 -->
<el-dialog title="钢卷追溯" :visible.sync="traceOpen" width="90%" append-to-body>
<div class="trace-result-container" v-loading="loading">
<!-- 二维码信息 -->
<el-card shadow="hover" class="mb20" v-if="traceResult && traceResult.qrcode">
<div slot="header" class="card-header">
<span class="title-dot"></span>
<span class="title-text">二维码信息</span>
</div>
<div class="qrcode-info">
<el-row class="info-row" :gutter="10">
<el-col :span="6" class="info-label">主序列号</el-col>
<el-col :span="18" class="info-value">{{ traceResult.qrcode.serialNumber || '-' }}</el-col>
</el-row>
<el-row class="info-row" :gutter="10">
<el-col :span="6" class="info-label">创建时间</el-col>
<el-col :span="18" class="info-value">{{ traceResult.qrcode.createTime || '-' }}</el-col>
</el-row>
<el-row class="info-row" :gutter="10" v-if="traceResult.all_qrcodes && traceResult.all_qrcodes.length > 1">
<el-col :span="6" class="info-label">相关二维码</el-col>
<el-col :span="18" class="info-value">{{ traceResult.all_qrcodes.length }} </el-col>
</el-row>
</div>
</el-card>
<!-- 操作步骤 -->
<el-card shadow="hover" class="mb20" v-if="traceResult && traceResult.steps && traceResult.steps.length > 0">
<div slot="header" class="card-header">
<span class="title-dot"></span>
<span class="title-text">操作步骤</span>
</div>
<el-timeline>
<el-timeline-item
v-for="(step, index) in traceResult.steps"
:key="index"
:timestamp="step.display_step || step.step"
placement="top"
>
<el-card shadow="hover">
<div class="step-header">
<span class="step-action">{{ step.action }}</span>
<el-tag size="mini" type="primary" v-if="step.operation">{{ step.operation }}</el-tag>
</div>
<div class="step-details">
<el-row class="detail-row" v-if="step.current_coil_no">
<el-col :span="8" class="detail-label">当前钢卷号</el-col>
<el-col :span="16" class="detail-value">{{ step.current_coil_no }}</el-col>
</el-row>
<el-row class="detail-row" v-if="step.old_current_coil_no">
<el-col :span="8" class="detail-label">原钢卷号</el-col>
<el-col :span="16" class="detail-value">{{ step.old_current_coil_no }}</el-col>
</el-row>
<el-row class="detail-row" v-if="step.new_current_coil_no">
<el-col :span="8" class="detail-label">新钢卷号</el-col>
<el-col :span="16" class="detail-value">{{ step.new_current_coil_no }}</el-col>
</el-row>
<el-row class="detail-row" v-if="step.new_current_coil_nos">
<el-col :span="8" class="detail-label">分卷列表</el-col>
<el-col :span="16" class="detail-value">
<el-tag
v-for="coilNo in step.new_current_coil_nos.split(',')"
:key="coilNo"
type="warning"
size="mini"
class="mr8"
>
{{ coilNo.trim() }}
</el-tag>
</el-col>
</el-row>
<el-row class="detail-row" v-if="step.parent_coil_nos">
<el-col :span="8" class="detail-label">合卷来源</el-col>
<el-col :span="16" class="detail-value">
<el-tag
v-for="coilNo in step.parent_coil_nos.split(',')"
:key="coilNo"
type="success"
size="mini"
class="mr8"
>
{{ coilNo.trim() }}
</el-tag>
</el-col>
</el-row>
<el-row class="detail-row" v-if="step.child_coils && step.child_coils.length > 0">
<el-col :span="8" class="detail-label">子钢卷</el-col>
<el-col :span="16" class="detail-value">
<el-tag
v-for="coilNo in step.child_coils"
:key="coilNo"
type="info"
size="mini"
class="mr8"
>
{{ coilNo }}
</el-tag>
</el-col>
</el-row>
<el-row class="detail-row" v-if="step.qrcode_serial">
<el-col :span="8" class="detail-label">二维码序列</el-col>
<el-col :span="16" class="detail-value">{{ step.qrcode_serial }}</el-col>
</el-row>
<el-row class="detail-row" v-if="step.operator">
<el-col :span="8" class="detail-label">操作者</el-col>
<el-col :span="16" class="detail-value">{{ step.operator }}</el-col>
</el-row>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</el-card>
<!-- 钢卷分支图 -->
<el-card shadow="hover" class="mb20" v-if="traceResult && traceResult.records && traceResult.records.length > 0">
<div slot="header" class="card-header">
<span class="title-dot"></span>
<span class="title-text">钢卷分支图</span>
</div>
<div class="branch-container">
<div
class="branch-group"
v-for="(group, index) in groupedRecords"
:key="index"
>
<div class="branch-group-header">
<el-tag :type="getGroupTypeTag(group.groupColor)" size="medium">{{ group.title }}</el-tag>
<span class="branch-count">{{ group.coils.length }} 个钢卷</span>
</div>
<el-row :gutter="20" class="branch-coils">
<el-col
:span="8"
v-for="coil in group.coils"
:key="coil.coilId"
>
<el-card shadow="hover" class="coil-card">
<div class="coil-header">
<span class="coil-no">{{ coil.currentCoilNo }}</span>
<el-tag :type="coil.dataType === 1 ? 'success' : 'danger'" size="mini">
{{ coil.dataType === 1 ? '当前' : '历史' }}
</el-tag>
</div>
<div class="coil-info">
<p class="coil-detail">入场号{{ coil.enterCoilNo }}</p>
<p class="coil-detail" v-if="coil.team">班组{{ coil.team }}</p>
<p class="coil-detail" v-if="coil.warehouse && coil.warehouse.warehouseName">
库区{{ coil.warehouse.warehouseName }}
</p>
<p class="coil-detail">{{ coil.createTime }}</p>
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</el-card>
<!-- 无记录提示 -->
<div class="empty-tip" v-if="!traceResult || (traceResult && !traceResult.records && !traceResult.steps)">
<el-empty description="未找到相关钢卷记录"></el-empty>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { listMaterialCoil, getMaterialCoil, delMaterialCoil, addMaterialCoil, updateMaterialCoilSimple, getMaterialCoilTrace } from "@/api/wms/coil";
import WarehouseSelect from "@/components/WarehouseSelect";
import QRCode from "../../print/components/QRCode.vue";
import { saveAsImage } from '@/utils/klp';
import MaterialSelect from "@/components/KLPService/ProductSelect";
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import BomInfoMini from "@/components/KLPService/Renderer/BomInfoMini";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
import RightToolbar from "@/components/RightToolbar";
import Pagination from "@/components/Pagination";
export default {
name: "MaterialCoil",
components: {
WarehouseSelect,
QRCode,
MaterialSelect,
ProductInfo,
RawMaterialInfo,
BomInfoMini,
RightToolbar,
Pagination
},
props: {
qrcode: {
type: Boolean,
default: false,
},
querys: {
type: Object,
default: () => {},
}
},
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 钢卷物料表格数据
materialCoilList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 追溯对话框显示
traceOpen: false,
// 追溯结果数据
traceResult: null,
// 查询参数
queryParams: {
...this.querys,
pageNum: 1,
pageSize: 10,
enterCoilNo: undefined,
currentCoilNo: undefined,
supplierCoilNo: undefined,
dataType: 1,
warehouseId: undefined,
nextWarehouseId: undefined,
qrcodeRecordId: undefined,
team: undefined,
hasMergeSplit: undefined,
parentCoilNos: undefined,
itemId: undefined,
itemType: undefined,
status: undefined,
},
// 表单参数
form: {},
transferCoilForm: {},
// 表单校验
rules: {
enterCoilNo: [
{ required: true, message: "入场钢卷号不能为空", trigger: "blur" }
],
currentCoilNo: [
{ required: true, message: "当前钢卷号不能为空", trigger: "blur" }
],
itemId: [
{ required: true, message: "物品ID不能为空", trigger: "blur" }
],
itemType: [
{ required: true, message: "物品类型不能为空", trigger: "change" }
]
}
};
},
computed: {
// 按时间线和分支关系分组钢卷记录
groupedRecords() {
if (!this.traceResult || !this.traceResult.records) {
return [];
}
const groups = [];
// 按创建时间排序所有记录
const sortedRecords = [...this.traceResult.records].sort((a, b) =>
new Date(a.createTime) - new Date(b.createTime)
);
// 按数据类型分组,但保持时间顺序
const currentCoils = sortedRecords.filter(record => record.dataType === 1);
const historyCoils = sortedRecords.filter(record => record.dataType === 0);
// 当前数据分组
if (currentCoils.length > 0) {
const currentByOperation = this.groupByOperation(currentCoils);
groups.push(...currentByOperation);
}
// 历史数据分组
if (historyCoils.length > 0) {
const historyByOperation = this.groupByOperation(historyCoils, true);
groups.push(...historyByOperation);
}
return groups;
}
},
created() {
this.getList();
},
methods: {
/** 查询钢卷物料列表 */
getList() {
this.loading = true;
listMaterialCoil(this.queryParams).then(response => {
this.materialCoilList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 追溯按钮操作 */
handleTrace(row) {
this.traceOpen = true;
this.traceResult = null;
this.loading = true;
getMaterialCoilTrace({
enterCoilNo: row.enterCoilNo,
currentCoilNo: row.currentCoilNo,
}).then(res => {
this.traceResult = res.data;
console.log('溯源结果:', this.traceResult);
this.loading = false;
}).catch(err => {
console.error('溯源查询失败:', err);
this.$message.error('溯源查询失败,请重试');
this.loading = false;
});
},
// 按操作类型分组
groupByOperation(records, isHistory = false) {
const operationGroups = {};
for (const record of records) {
let operationType = '原始数据';
let groupColor = 'default';
if (record.hasMergeSplit === 1) {
operationType = '分卷结果';
groupColor = 'split';
} else if (record.hasMergeSplit === 2) {
operationType = '合卷结果';
groupColor = 'merge';
}
const groupKey = operationType;
if (!operationGroups[groupKey]) {
operationGroups[groupKey] = {
operationType: operationType,
groupColor: groupColor,
coils: []
};
}
operationGroups[groupKey].coils.push(record);
}
// 转换为数组
const groups = [];
for (const key in operationGroups) {
const group = operationGroups[key];
groups.push({
type: isHistory ? 'history' : 'current',
operationType: group.operationType,
groupColor: group.groupColor,
title: `${group.operationType}${isHistory ? '(历史)' : '(当前)'}`,
coils: group.coils
});
}
return groups;
},
// 获取分组标签类型
getGroupTypeTag(color) {
switch(color) {
case 'split': return 'warning';
case 'merge': return 'success';
default: return 'primary';
}
},
handleDownloadQRCode(row) {
try {
saveAsImage(
row.qrcodeRecordId,
'',
1,
{
barcodeWidth: 200,
barcodeHeight: 200
}
);
this.$message.success('图片保存成功');
} catch (error) {
console.error('保存图片失败', error);
this.$message.error('保存图片失败,请稍后重试');
}
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
coilId: undefined,
enterCoilNo: undefined,
currentCoilNo: undefined,
supplierCoilNo: undefined,
dataType: undefined,
warehouseId: undefined,
nextWarehouseId: undefined,
qrcodeRecordId: undefined,
team: undefined,
hasMergeSplit: undefined,
parentCoilNos: undefined,
itemId: undefined,
itemType: undefined,
status: undefined,
remark: undefined,
delFlag: undefined,
createTime: undefined,
createBy: undefined,
updateTime: undefined,
updateBy: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.coilId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.isCheck = false;
this.reset();
this.open = true;
this.title = "添加钢卷物料";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.isCheck = false;
this.loading = true;
this.reset();
const coilId = row.coilId || this.ids
getMaterialCoil(coilId).then(response => {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改钢卷物料";
});
},
handleCheck(row) {
this.isCheck = true;
this.loading = true;
this.reset();
const coilId = row.coilId || this.ids
getMaterialCoil(coilId).then(response => {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改钢卷物料";
});
},
transferCoil() {
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.coilId != null) {
updateMaterialCoilSimple(this.form).then(_ => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
} else {
addMaterialCoil(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const coilIds = row.coilId || this.ids;
this.$modal.confirm('是否确认删除钢卷物料编号为"' + coilIds + '"的数据项?').then(() => {
this.loading = true;
return delMaterialCoil(coilIds);
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
/** 导出按钮操作 */
handleExport() {
this.download('system/materialCoil/export', {
...this.queryParams
}, `materialCoil_${new Date().getTime()}.xlsx`)
}
}
};
</script>
<style scoped>
/* 溯源结果样式 */
.trace-result-container {
padding: 10px 0;
}
.card-header {
display: flex;
align-items: center;
font-size: 16px;
font-weight: 600;
}
.title-dot {
display: inline-block;
width: 4px;
height: 16px;
background: #409EFF;
border-radius: 2px;
margin-right: 8px;
}
.title-text {
color: #333;
}
.mb20 {
margin-bottom: 20px;
}
/* 二维码信息样式 */
.qrcode-info {
padding: 10px 0;
}
.info-row {
margin-bottom: 12px;
align-items: center;
}
.info-label {
color: #666;
font-weight: 500;
}
.info-value {
color: #333;
font-weight: 500;
}
/* 操作步骤样式 */
.step-header {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.step-action {
font-size: 15px;
font-weight: 600;
color: #333;
margin-right: 8px;
}
.step-details {
padding: 10px 0;
}
.detail-row {
margin-bottom: 8px;
align-items: center;
}
.detail-label {
color: #666;
font-size: 14px;
}
.detail-value {
color: #333;
font-size: 14px;
}
.mr8 {
margin-right: 8px;
margin-bottom: 8px;
}
/* 钢卷分支图样式 */
.branch-container {
padding: 10px 0;
}
.branch-group {
margin-bottom: 20px;
}
.branch-group-header {
display: flex;
align-items: center;
margin-bottom: 12px;
justify-content: space-between;
}
.branch-count {
color: #666;
font-size: 14px;
}
.branch-coils {
margin-bottom: 10px;
}
.coil-card {
height: 100%;
display: flex;
flex-direction: column;
}
.coil-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.coil-no {
font-size: 15px;
font-weight: 600;
color: #333;
}
.coil-info {
flex: 1;
}
.coil-detail {
font-size: 13px;
color: #666;
margin-bottom: 4px;
margin: 0;
line-height: 1.5;
}
/* 空提示样式 */
.empty-tip {
text-align: center;
padding: 40px 0;
}
</style>

View File

@@ -0,0 +1,21 @@
<template>
<BasePage :qrcode="qrcode" :querys="querys"/>
</template>
<script>
import BasePage from './panels/base.vue';
export default {
components: {
BasePage
},
data() {
return {
qrcode: false,
querys: {
itemType: 'product'
}
}
}
}
</script>