Files
klp-oa/klp-ui/src/views/wms/coil/panels/base.vue
砂糖 0c05455ae3 feat: 新增业务员列并调整部分表格列宽,注释操作记录标签页
1. 在CoilSelector相关数据和报表设置列配置中新增业务员字段
2. 调整逻辑库位、实际库区的表格列宽
3. 注释crm合同页的操作记录标签页
2026-06-21 11:45:56 +08:00

2547 lines
99 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="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="发货计划" prop="planId" v-if="showWaybill">
<el-select v-model="queryParams.planId" placeholder="请输入发货计划名称搜索" filterable remote clearable
:remote-method="remoteSearchWaybill" :loading="waybillLoading" style="width: 220px" @change="handleQuery">
<el-option v-for="item in waybillOptions" :key="item.planId" :label="item.planName" :value="item.planId" />
</el-select>
</el-form-item>
<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="warehouseId" v-if="useWarehouseIds">
<muti-select v-model="warehouseIds" placeholder="请选择逻辑库位" style="width: 100%; display: inline-block;" clearable
:options="warehouseOptions">
</muti-select>
</el-form-item>
<el-form-item label="逻辑库位" prop="warehouseId" v-else-if="!hideWarehouseQuery && !leftWarehouseQuery">
<warehouse-select v-model="queryParams.warehouseId" placeholder="请选择逻辑库位"
style="width: 100%; display: inline-block;" clearable />
</el-form-item>
<el-form-item label="实际库区" prop="actualWarehouseId" v-if="!hideWarehouseQuery">
<actual-warehouse-select v-model="queryParams.actualWarehouseId" placeholder="请选择仓库/库区/库位"
style="width: 100%; display: inline-block;" clearable :canSelectDisabled="true" :canSelectLevel2="true"
:clearInput="false" :showEmpty="true" />
</el-form-item>
<el-form-item v-if="showMaterialType" label="物料类型" prop="materialType">
<el-select v-model="queryParams.materialType" placeholder="请选择物料类型" clearable>
<el-option label="原料" value="原料"></el-option>
<el-option label="成品" value="成品"></el-option>
</el-select>
</el-form-item>
<el-form-item label="产品名称" prop="itemName">
<muti-select v-model="queryParams.itemName" :options="dict.type.coil_itemname" placeholder="请选择物料" clearable />
</el-form-item>
<el-form-item label="规格" prop="itemSpecification">
<memo-input v-model="queryParams.itemSpecification" storageKey="coilSpec" placeholder="请选择规格" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质" prop="itemMaterial">
<muti-select v-model="queryParams.itemMaterial" :options="dict.type.coil_material" placeholder="请选择材质" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="厂家" prop="itemManufacturer">
<muti-select v-model="queryParams.itemManufacturer" :options="dict.type.coil_manufacturer" placeholder="请选择厂家"
clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="表面处理">
<el-input v-model="queryParams.itemSurfaceTreatmentDesc" placeholder="请输入表面处理" clearable size="small" />
</el-form-item>
<el-form-item label="切边" prop="trimmingRequirement">
<el-select v-model="queryParams.trimmingRequirement" placeholder="请选择切边" clearable style="width: 100%">
<el-option label="净边" value="净边" />
<el-option label="毛边" value="毛边" />
</el-select>
</el-form-item>
<el-form-item label="包装" prop="packagingRequirement">
<el-select v-model="queryParams.packagingRequirement" placeholder="请选择包装" clearable style="width: 100%">
<el-option label="裸包" value="裸包" />
<el-option label="普包" value="普包" />
<el-option label="简包" value="简包" />
</el-select>
</el-form-item>
<el-form-item label="品质">
<muti-select v-model="queryParams.qualityStatusCsv" :options="dict.type.coil_quality_status" placeholder="请选择品质"
clearable />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="queryParams.remark" placeholder="请输入备注" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item v-if="showWaybill" label="发货状态">
<el-radio-group v-model="queryParams.status" @change="handleQuery">
<el-radio-button label="">全部</el-radio-button>
<el-radio-button label="0">未发货{{ unshippedCount }}</el-radio-button>
<el-radio-button label="1">已发货{{ shippedCount }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="isShipView" label="货单状态">
<el-radio-group v-model="shipViewModel.status" @change="handleQuery">
<el-radio-button label="all">全部</el-radio-button>
<el-radio-button label="has">有货单{{ shipViewModel.hasCount }}</el-radio-button>
<el-radio-button label="none">无货单{{ shipViewModel.noneCount }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="showWaybill" label="发货单时间">
<el-date-picker v-model="queryParams.shipmentTime" type="daterange" value-format="yyyy-MM-dd"
range-separator="" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item v-if="editNext" label="显示流程图" prop="showProcessFlow">
<el-checkbox v-model="showProcessFlow" @change="handleShowProcessFlowChange"></el-checkbox>
</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-button icon="el-icon-download" size="mini" @click="handleNewExport" v-if="showNewExport">导出</el-button>
<el-button type="danger" v-if="showWaybill" plain icon="el-icon-close" size="mini" :disabled="multiple"
:loading="buttonLoading" @click="handleBatchRemoveFromWaybill">批量移出发货单</el-button>
</el-form-item>
<!-- <el-form-item style="float: right;" v-if="showWaybill" v-loading="loading">
<el-descriptions :column="2" border>
<el-descriptions-item label="已发货数量">{{ shippedCount }}</el-descriptions-item>
<el-descriptions-item label="未发货数量">{{ unshippedCount }}</el-descriptions-item>
</el-descriptions>
</el-form-item> -->
</el-form>
<el-row :gutter="10" class="mb8" v-if="showControl">
<!-- <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="warning" plain icon="el-icon-download" size="mini" @click="handleExportAll">导出当前</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-download" size="mini" @click="handleExportAllProps">导出全部</el-button>
</el-col>
<el-col :span="2" v-if="canExportAll">
<el-button type="info" plain icon="el-icon-printer" size="mini" :disabled="multiple"
@click="handleBatchPrintLabel">批量打印标签</el-button>
</el-col>
<!-- <el-col :span="2" v-if="showWaybill">
</el-col> -->
<el-col :span="1.5" v-if="showOrderBy">
<el-checkbox v-model="queryParams.orderBy" v-loading="loading" @change="getList"
label="orderBy">按实际库区排序</el-checkbox>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<div style="display: flex; align-items: flex-start;">
<div v-if="leftWarehouseQuery" :style="{
width: '220px',
height: showAbnormal ? 'calc(100vh - 400px)' : 'calc(100vh - 300px)',
backgroundColor: '#ffffff',
overflowY: 'auto',
overflowX: 'hidden'
}">
<warehouse-tree warehouseType="logic" @node-click="handleWarehouseNodeClick" />
</div>
<div style="flex: 1; width: 100%; overflow: hidden;">
<KLPTable v-loading="loading" :data="materialCoilList" @selection-change="handleSelectionChange"
:floatLayer="true" :floatLayerConfig="floatLayerConfig" @row-click="handleRowClick"
:height="showAbnormal ? 'calc(100vh - 400px)' : 'calc(100vh - 300px)'" border>
<el-table-column v-if="showWaybill" type="selection" width="55" align="center" />
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.enterCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="当前钢卷号" align="center" prop="currentCoilNo">
<template slot-scope="scope">
<current-coil-no :current-coil-no="scope.row.currentCoilNo"></current-coil-no>
</template>
</el-table-column>
<el-table-column label="净重" align="center" prop="netWeight" v-if="!hideWarehouseQuery" />
<el-table-column label="逻辑库位" width="120" align="center" prop="warehouseName" v-if="!hideWarehouseQuery" />
<el-table-column label="实际库区" width="100" align="center" prop="actualWarehouseName"
v-if="!hideWarehouseQuery && !showExportTime" />
<el-table-column label="产品类型" align="center" width="180" v-if="!moreColumn">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row" />
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row" />
</template>
</el-table-column>
<el-table-column label="业务员" align="center" prop="saleName" width="60" />
<el-table-column v-if="showAbnormal" label="异常数量" align="center" prop="abnormalCount"></el-table-column>
<el-table-column label="长度 (米)" align="center" prop="length" v-if="showLength" />
<el-table-column label="发货时间" v-if="showExportTime" align="center" prop="exportTime" width="205">
<template slot-scope="scope">
<el-date-picker @change="handleExportTimeChange(scope.row)" v-if="canEditExportTime" style="width: 100%"
v-model="scope.row.exportTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择发货时间" />
<div v-else>{{ scope.row.exportTime }}</div>
</template>
</el-table-column>
<el-table-column label="发货人" v-if="showExportTime" align="center" prop="exportByName" width="150">
<template slot-scope="scope">
<el-select v-model="scope.row.exportBy" placeholder="请选择发货人" filterable
@change="handleExportByNameChange(scope.row)">
<el-option v-for="item in userList" :key="item.userName" :value="item.userName"
:label="item.nickName" />
</el-select>
</template>
</el-table-column>
<el-table-column v-if="showGrade" label="质量状态" align="center" prop="qualityStatus">
</el-table-column>
<el-table-column label="逻辑库位" align="center" prop="warehouseId" v-if="editWarehouse">
<template slot-scope="scope">
<warehouse-select @change="handleWarehouseChange(scope.row)" v-model="scope.row.warehouseId"
placeholder="请选择仓库/库区/库位" style="width: 100%;" clearable />
</template>
</el-table-column>
<el-table-column v-if="hasTransferType" label="实际库区" align="center" prop="actualWarehouseName" />
<el-table-column v-if="moreColumn" label="规格" prop="specification"></el-table-column>
<el-table-column v-if="moreColumn" label="物料" prop="itemName"></el-table-column>
<el-table-column v-if="moreColumn" label="材质" prop="material"></el-table-column>
<el-table-column v-if="moreColumn" label="厂家" prop="manufacturer"></el-table-column>
<el-table-column v-if="moreColumn" label="表面处理" prop="surfaceTreatmentDesc"></el-table-column>
<el-table-column v-if="moreColumn" label="品质" prop="qualityStatus"></el-table-column>
<el-table-column v-if="moreColumn" label="备注" prop="remark" show-overflow-tooltip></el-table-column>
<el-table-column v-if="moreColumn" label="切边" prop="trimmingRequirement"></el-table-column>
<el-table-column v-if="moreColumn" label="包装" prop="packagingRequirement"></el-table-column>
<el-table-column v-if="moreColumn" label="镀层质量" prop="zincLayer"></el-table-column>
<el-table-column v-if="hasTransferType" label="调拨类型" align="center" prop="transferType" />
<el-table-column v-if="hasTransferType" label="改判原因" align="center" prop="rejudgeInfo.rejudgeReason" />
<el-table-column label="钢卷去向" align="center" prop="nextWarehouseId" v-if="editNext" width="150">
<template slot-scope="scope">
<el-select v-model="scope.row.nextWarehouseId" placeholder="钢卷去向" filterable
@change="handleNextWarehouseChange(scope.row)">
<el-option v-for="item in dict.type.wms_next_warehouse" :key="item.value" :value="item.value"
:label="item.label" />
</el-select>
</template>
</el-table-column>
<el-table-column label="业务目的" align="center" prop="businessPurpose" v-if="showBusinessPurpose" width="150">
<template slot-scope="scope">
<el-select v-model="scope.row.businessPurpose" placeholder="业务目的" filterable
@change="handleRowChange(scope.row)">
<el-option v-for="item in dict.type.coil_business_purpose" :key="item.value" :value="item.value"
:label="item.label" />
</el-select>
</template>
</el-table-column>
<el-table-column label="关联订单" align="center" prop="relatedToOrder" v-if="showRelatedToOrder" width="150">
<template slot-scope="scope">
<el-switch @change="handleRowChange(scope.row)" v-model="scope.row.isRelatedToOrder" :active-value="1"
:inactive-value="0" />
</template>
</el-table-column>
<el-table-column label="发货计划" align="center" prop="nextWarehouseId" v-if="showWaybill || (isShipView && shipViewModel.status == 'has')" width="150">
<template slot-scope="scope">
{{ scope.row.bindPlanName || '-' }}
</template>
</el-table-column>
<el-table-column label="发货单据" align="center" prop="nextWarehouseId" v-if="showWaybill || (isShipView && shipViewModel.status == 'has')" width="150">
<template slot-scope="scope">
<el-popover placement="top" width="400" trigger="hover">
<div>
<el-descriptions :column="2" :border="false">
<el-descriptions-item label="单据名称">
{{ scope.row.bindWaybillName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="车牌号">
{{ scope.row.bindLicensePlate || '-' }}
</el-descriptions-item>
<el-descriptions-item label="收货单位">
{{ scope.row.bindConsigneeUnit || '-' }}
</el-descriptions-item>
<el-descriptions-item label="负责人">
{{ scope.row.bindPrincipal || '-' }}
</el-descriptions-item>
<el-descriptions-item label="单据状态">
<el-tag v-if="scope.row.bindWaybillStatus === 0" type="info" size="mini">未发货</el-tag>
<el-tag v-else-if="scope.row.bindWaybillStatus === 1" type="info" size="mini">已发货</el-tag>
<el-tag v-else-if="scope.row.bindWaybillStatus === 2" type="info" size="mini">未打印</el-tag>
<el-tag v-else-if="scope.row.bindWaybillStatus === 3" type="info" size="mini">已打印</el-tag>
<el-tag v-else type="danger" size="mini">未知状态</el-tag>
</el-descriptions-item>
</el-descriptions>
</div>
<div slot="reference" class="text-ellipsis" v-text>{{ scope.row.bindLicensePlate || '-' }}</div>
</el-popover>
</template>
</el-table-column>
<el-table-column label="发货状态" align="center" prop="status" v-if="showWaybill" width="150">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 1" type="success" size="mini">已发货</el-tag>
<el-tag v-else type="info" size="mini">未发货</el-tag>
</template>
</el-table-column>
<el-table-column label="实测宽度(mm)" align="center" prop="width" v-if="showWidthEdit" width="150">
<template slot-scope="scope">
<el-input v-model="scope.row.actualWidth" placeholder="请输入实测宽度"
@change="handleRowChange(scope.row)"></el-input>
</template>
</el-table-column>
<el-table-column label="实测厚度(mm)" align="center" prop="actualThickness" v-if="showWidthEdit" width="150">
<template slot-scope="scope">
<el-input v-model="scope.row.actualThickness" placeholder="请输入实测厚度"
@change="handleRowChange(scope.row)"></el-input>
</template>
</el-table-column>
<el-table-column label="预留宽度(mm)" align="center" prop="width" v-if="showWidthEdit" width="150">
<template slot-scope="scope">
<el-input v-model="scope.row.reservedWidth" placeholder="请输入预留宽度"
@change="handleRowChange(scope.row)"></el-input>
</template>
</el-table-column>
<el-table-column label="生产开始" align="center" prop="productionStartTime" v-if="showProductionTimeEdit"
width="150">
</el-table-column>
<el-table-column label="生产结束" align="center" prop="productionEndTime" v-if="showProductionTimeEdit"
width="150">
</el-table-column>
<el-table-column label="生产耗时" align="center" prop="productionDuration" v-if="showProductionTimeEdit"
width="150">
<template slot-scope="scope">
{{ formatDuration(scope.row.productionDuration * 60 * 1000) }}
</template>
</el-table-column>
<el-table-column prop="action" label="操作" align="center" class-name="small-padding fixed-width"
v-if="!moreColumn">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreviewLabel(scope.row)">
预览标签
</el-button>
<el-button size="mini" type="text" icon="el-icon-printer" @click="handlePrintLabel(scope.row)">
打印标签
</el-button>
<el-button size="mini" type="text" icon="el-icon-view" @click="handleNumberCoilClick(scope.row)">
数字钢卷
</el-button>
<el-button size="mini" v-if="showStatus" type="text" icon="el-icon-upload"
@click="handleExportCoil(scope.row)">
发货
</el-button>
<el-button size="mini" v-if="showExportTime" type="text" icon="el-icon-close"
@click="handleCancelExport(scope.row)">
撤回发货
</el-button>
<el-button size="mini" v-if="showProductionTimeEdit" type="text" icon="el-icon-close"
@click="handleProductionTimeEdit(scope.row)">
加工修正
</el-button>
<el-button size="mini" v-if="showExportTime" type="text" icon="el-icon-sold-out"
@click="handleReturnCoil(scope.row)">
退货钢卷
</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleCheck(scope.row)"
v-if="showControl">修正</el-button>
<el-button size="mini" type="text" icon="el-icon-search" @click="handleTrace(scope.row)">追溯</el-button>
<el-button size="mini" v-if="showWaybill" type="text" icon="el-icon-close"
@click="handleRemoveFromWaybill(scope.row)">
移出发货单
</el-button>
<el-button v-if="showGrade" size="mini" type="text" icon="el-icon-edit" @click="handleJudge(scope.row)">
改判
</el-button>
<el-button size="mini" v-if="hasTransferType" type="text" icon="el-icon-close"
@click="handleReplaceLabel(scope.row)">
重贴标签
</el-button>
<el-button size="mini" v-if="hasTransferType" type="text" icon="el-icon-document"
@click="handleViewTransferRecord(scope.row)">
查看记录
</el-button>
</template>
</el-table-column>
</KLPTable>
</div>
</div>
<div v-show="total > 0" style="display: flex; justify-content: flex-end; align-items: flex-end; gap: 10px;">
<span>
总净重{{ statistics.total_net_weight || 0 }}t
</span>
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
</div>
<!-- 添加或修改钢卷物料对话框 -->
<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="请输入入场钢卷号" :disabled="form.coilId" />
</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="所在库位" prop="warehouseId">
<warehouse-select v-model="form.warehouseId" placeholder="请选择仓库/库区/库位" style="width: 100%;" clearable />
</el-form-item>
<el-form-item label="实际库区" prop="actualWarehouseId">
<actual-warehouse-select v-model="form.actualWarehouseId" :clearInput="form.coilId != null"
placeholder="请选择实际库区" style="width: 100%;" clearable />
</el-form-item>
<el-form-item label="班组" prop="team">
<el-select v-model="form.team" placeholder="请选择班组" style="width: 100%">
<el-option key="甲" label="甲" value="甲" />
<el-option key="乙" label="乙" value="乙" />
</el-select>
</el-form-item>
<el-form-item label="材料类型" prop="materialType">
<el-select v-model="form.materialType" placeholder="请选择材料类型" @change="handleMaterialTypeChange">
<el-option label="成品" value="成品" />
<el-option label="原料" value="原料" />
</el-select>
</el-form-item>
<el-form-item :label="getItemLabel" prop="itemId">
<product-select v-if="form.itemType == 'product'" v-model="form.itemId" placeholder="请选择成品"
style="width: 100%;" clearable />
<raw-material-select v-else-if="form.itemType == 'raw_material'" v-model="form.itemId" placeholder="请选择原料"
style="width: 100%;" clearable />
<div v-else>请先选择材料类型</div>
</el-form-item>
<!-- <el-form-item label="质量状态" prop="qualityStatus">
<el-select v-model="form.qualityStatus" placeholder="请选择质量状态" style="width: 100%">
<el-option v-for="item in dict.type.coil_quality_status" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item> -->
<el-form-item label="切边要求" prop="trimmingRequirement">
<el-select v-model="form.trimmingRequirement" placeholder="请选择切边要求" style="width: 100%">
<el-option label="净边料" value="净边料" />
<el-option label="毛边料" value="毛边料" />
</el-select>
</el-form-item>
<el-form-item label="原料材质" prop="packingStatus">
<el-input v-model="form.packingStatus" placeholder="请输入原料材质">
</el-input>
</el-form-item>
<el-form-item label="包装要求" prop="packagingRequirement">
<el-select v-model="form.packagingRequirement" placeholder="请选择包装要求" style="width: 100%">
<el-option label="裸包" value="裸包" />
<el-option label="普包" value="普包" />
<el-option label="简包" value="简包" />
<el-option label="精包" value="精包" />
</el-select>
</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-item label="长度" prop="length" v-if="showLength">
<el-input v-model="form.length" placeholder="请输入长度" />
</el-form-item>
<el-form-item label="实测长度(m)" prop="actualLength">
<el-input-number :controls="false" v-model="form.actualLength" placeholder="请输入实测长度" type="number"
:step="0.01" />
</el-form-item>
<el-form-item label="实测厚度(mm)" prop="actualThickness" class="form-item-half">
<el-input-number :controls="false" v-model="form.actualThickness" placeholder="请输入实测厚度" type="number"
:step="0.01" />
</el-form-item>
<el-form-item label="实测宽度(mm)" prop="actualWidth">
<el-input-number :controls="false" v-model="form.actualWidth" placeholder="请输入实测宽度" type="number"
:step="0.01" />
</el-form-item>
<el-form-item label="业务目的" prop="businessPurpose">
<el-select v-model="form.businessPurpose" placeholder="业务目的" filterable>
<el-option v-for="item in dict.type.coil_business_purpose" :key="item.value" :value="item.value"
:label="item.label" />
</el-select>
</el-form-item>
<el-form-item label="调制度" prop="temperGrade">
<el-input v-model="form.temperGrade" placeholder="请输入调制度" />
</el-form-item>
<el-form-item label="镀层种类" prop="coatingType">
<MemoInput storageKey="coatingType" v-model="form.coatingType" placeholder="请输入镀层种类" />
</el-form-item>
<el-form-item label="钢卷表面处理" prop="coilSurfaceTreatment">
<MemoInput storageKey="surfaceTreatmentDesc" v-model="form.coilSurfaceTreatment" placeholder="请输入钢卷表面处理" />
</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>
<!-- 钢卷追溯对话框使用封装的组件 -->
<el-dialog title="钢卷追溯" :visible.sync="traceOpen" width="90%" append-to-body>
<coil-trace-result v-loading="traceLoading" :trace-result="traceResult"></coil-trace-result>
</el-dialog>
<!-- 标签预览弹窗 -->
<el-dialog title="标签预览" :visible.sync="labelRender.visible" append-to-body>
<label-render :forceSpecialTag="forceSpecialTag" :content="labelRender.data" :labelType="labelRender.type" />
</el-dialog>
<label-render :forceSpecialTag="forceSpecialTag" ref="labelRender" v-show="false" :content="labelRender.data"
:labelType="labelRender.type" />
<!-- 批量导出标签PDF弹窗 -->
<el-dialog title="批量导出标签PDF" :visible.sync="batchPrint.visible" width="520px" append-to-body>
<div style="line-height: 22px; font-size: 12px; color: #909399; margin-bottom: 10px;">
已选择 <b>{{ batchPrint.list.length }}</b> 个钢卷点击生成PDF并打开将每个标签作为一页180mm × 100mm
</div>
<div style="text-align:right;">
<el-button size="mini" @click="batchPrint.visible = false">取消</el-button>
<el-button type="primary" size="mini" :loading="batchPrint.loading"
@click="handleBatchExportLabelPdf">生成PDF并打开</el-button>
</div>
<!-- 渲染容器屏幕隐藏仅用于截图生成PDF -->
<div ref="batchPdfContainer" class="batch-pdf-root" aria-hidden="true">
<div v-for="(item, idx) in batchPrint.list" :key="item.coilId || idx" class="batch-pdf-page">
<label-render :content="item" :hideActions="true" :forceSpecialTag="forceSpecialTag" />
</div>
</div>
</el-dialog>
<el-dialog title="生产时间修正" :visible.sync="productionTimeFormVisible" width="500px" append-to-body>
<el-form ref="productionTimeForm" :model="productionTimeForm" :rules="productionTimeFormRules"
label-width="100px">
<el-form-item label="生产开始时间" prop="productionStartTime">
<el-date-picker v-model="productionTimeForm.productionStartTime" type="datetime"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择生产时间"
@change="(value) => { productionTimeForm.productionStartTime = value; calculateProductionDuration(); }" />
</el-form-item>
<el-form-item label="生产结束时间" prop="productionEndTime">
<el-date-picker v-model="productionTimeForm.productionEndTime" type="datetime"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择生产时间"
@change="(value) => { productionTimeForm.productionEndTime = value; calculateProductionDuration(); }" />
</el-form-item>
<el-form-item label="生产耗时" prop="productionDuration">
<el-input v-model="productionTimeForm.formattedDuration" placeholder="自动计算" disabled />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitProductionTimeForm"> </el-button>
<el-button @click="productionTimeFormVisible = false"> </el-button>
</div>
</el-dialog>
<abnormal-list v-if="showAbnormal && currentCoilId" :coil-id="currentCoilId"></abnormal-list>
<log-table v-if="showWareLog && currentCoilId" :coil-id="currentCoilId"></log-table>
<DragResizeBox v-if="editNext && showProcessFlow" storageKey="coil-process-flow" @size-change="resizeChart">
<div style="height: 100%; width: 100%; overflow-y: scroll; display: flex; background-color: #fff;">
<process-flow v-if="showProcessFlow" ref="processFlow"></process-flow>
</div>
</DragResizeBox>
<!-- 钢卷改判弹窗 -->
<el-dialog title="钢卷改判" :visible.sync="judgeDialogVisible" width="800px" append-to-body>
<CoilInfoRender :column="3" :coilInfo="judgeForm" border />
<div style="margin-top: 20px; padding: 15px; background-color: #f5f7fa; border-radius: 4px;">
<el-form :model="judgeForm" label-width="120px">
<el-form-item label="修改质量状态">
<el-select v-model="judgeForm.qualityStatus" placeholder="请选择质量状态" style="width: 200px">
<el-option v-for="item in dict.type.coil_quality_status" :key="item.value" :value="item.value"
:label="item.label" />
</el-select>
</el-form-item>
<el-form-item label="改判原因">
<el-input type="textarea" v-model="judgeForm.rejudgeReason" placeholder="请输入改判原因" />
</el-form-item>
<el-form-item label="通知重贴标签">
<el-checkbox v-model="judgeForm.notifyReLabel">是否通知重贴标签</el-checkbox>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="judgeDialogVisible = false">取消</el-button>
<el-button :loading="buttonLoading" type="primary" @click="submitJudgeForm">确认</el-button>
</div>
</el-dialog>
<!-- 调拨记录弹窗 -->
<el-dialog title="调拨记录" :visible.sync="transferRecordVisible" width="900px" append-to-body>
<div class="table-wrapper transfer-table" v-loading="loading">
<div class="table-container">
<el-table :data="transferRecordList" size="small" border stripe style="width: 100%">
<el-table-column prop="type" label="类型" min-width="100"></el-table-column>
<el-table-column label="变更前">
<template slot-scope="scope">
{{ scope.row.before }}
</template>
</el-table-column>
<el-table-column label="变更后">
<template slot-scope="scope">
{{ scope.row.after }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="时间"></el-table-column>
<el-table-column prop="remark" label="备注" show-overflow-tooltip></el-table-column>
</el-table>
</div>
</div>
</el-dialog>
<!-- 钢卷暂存单据管理 -->
<el-card v-if="moreColumn" class="box-card" style="margin-top: 20px;" :body-style="{ padding: '20px' }">
<div slot="header" class="clearfix">
<span>钢卷暂存单据管理</span>
<el-button style="float: right; padding: 3px 0" type="text" @click="createTempOrder">创建暂存单据</el-button>
</div>
<!-- 暂存单据列表 -->
<el-table :data="tempOrderList" style="width: 100%" :height="'calc(100vh - 500px)'">
<el-table-column prop="orderName" label="单据名称">
<template slot-scope="scope">
<div style="display: flex; align-items: center; gap: 5px;">
<el-link type="primary" @click="viewSelectedCoils(scope.row)">{{ scope.row.orderName }}</el-link>
<el-button size="mini" type="text" icon="el-icon-edit" @click="editOrderName(scope.row)"></el-button>
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column prop="coilCount" label="钢卷数量">
<template slot-scope="scope">
<el-tag size="small">{{ (scope.row.coils && scope.row.coils.length) ? scope.row.coils.length : 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="totalWeight" label="总重量(t)">
<template slot-scope="scope">
{{ calculateTotalWeight(scope.row.coils || []).toFixed(3) }}
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="openCoilSelection(scope.row)">选择钢卷</el-button>
<el-button size="mini" type="text" style="color: #f56c6c;"
@click="deleteTempOrder(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 当没有数据时显示提示 -->
<div v-if="!tempOrderList || tempOrderList.length === 0"
style="text-align: center; padding: 50px; color: #909399;">
<i class="el-icon-document" style="font-size: 48px; margin-bottom: 10px;"></i>
<div>暂无暂存单据</div>
<div style="font-size: 12px; margin-top: 5px;">点击右上角"创建暂存单据"开始使用</div>
</div>
</el-card>
<!-- 钢卷选择对话框 -->
<el-dialog title="选择钢卷" :visible.sync="coilSelectionVisible" width="95%" append-to-body>
<!-- 上方查询条件 -->
<el-card class="query-card" style="margin-bottom: 15px;">
<el-form :model="coilQueryParams" size="small" :inline="true">
<el-form-item label="入场卷号">
<el-input v-model="coilQueryParams.enterCoilNo" placeholder="请输入入场钢卷号" clearable style="width: 200px;" />
</el-form-item>
<el-form-item label="当前卷号">
<el-input v-model="coilQueryParams.currentCoilNo" placeholder="请输入当前钢卷号" clearable style="width: 200px;" />
</el-form-item>
<el-form-item label="产品名称">
<el-input v-model="coilQueryParams.itemName" placeholder="请选择物料" clearable style="width: 200px;" />
</el-form-item>
<el-form-item label="规格">
<el-input v-model="coilQueryParams.itemSpecification" placeholder="请选择规格" clearable style="width: 200px;" />
</el-form-item>
<el-form-item label="材质">
<el-input v-model="coilQueryParams.itemMaterial" placeholder="请选择材质" clearable style="width: 200px;" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchCoils">查询</el-button>
<el-button @click="resetCoilQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 下方钢卷列表 -->
<div style="height: 500px;">
<el-table v-loading="coilLoading" :data="availableCoils" @selection-change="handleCoilSelection" border
height="450">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.enterCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="当前钢卷号" align="center" prop="currentCoilNo">
<template slot-scope="scope">
<current-coil-no :current-coil-no="scope.row.currentCoilNo"></current-coil-no>
</template>
</el-table-column>
<el-table-column label="净重" align="center" prop="netWeight" />
<el-table-column label="逻辑库位" align="center" prop="warehouseName" />
<el-table-column label="实际库区" align="center" prop="actualWarehouseName" />
<el-table-column label="产品类型" align="center" width="180">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row" />
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row" />
</template>
</el-table-column>
<el-table-column label="规格" prop="specification"></el-table-column>
<el-table-column label="物料" prop="itemName"></el-table-column>
<el-table-column label="材质" prop="material"></el-table-column>
<el-table-column label="厂家" prop="manufacturer"></el-table-column>
<el-table-column label="表面处理" prop="surfaceTreatmentDesc"></el-table-column>
<el-table-column label="品质" prop="qualityStatus"></el-table-column>
<el-table-column label="切边" prop="trimmingRequirement"></el-table-column>
<el-table-column label="包装" prop="packagingRequirement"></el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 0" type="info" size="mini">未发货</el-tag>
<el-tag v-else type="success" size="mini">已发货</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
</el-table>
<div style="margin-top: 10px; text-align: center;">
<el-pagination @size-change="handleCoilSizeChange" @current-change="handleCoilCurrentChange"
:current-page="coilQueryParams.pageNum" :page-sizes="[10, 20, 50, 100]"
:page-size="coilQueryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="coilTotal">
</el-pagination>
</div>
</div>
<div slot="footer" class="dialog-footer">
<div style="margin-bottom: 10px;">
<span>已选择 {{ selectedCoils.length }} 个钢卷总重量{{ calculateSelectedWeight().toFixed(3) }}t</span>
</div>
<el-button @click="coilSelectionVisible = false">取消</el-button>
<el-button type="primary" @click="saveSelectedCoils">确认选择</el-button>
</div>
</el-dialog>
<!-- 已选钢卷列表对话框 -->
<el-dialog title="已选钢卷列表" :visible.sync="selectedCoilsVisible" width="90%" append-to-body>
<el-table :data="currentTempOrder ? (currentTempOrder.coils || []) : []" border>
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.enterCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="当前钢卷号" align="center" prop="currentCoilNo">
<template slot-scope="scope">
<current-coil-no :current-coil-no="scope.row.currentCoilNo"></current-coil-no>
</template>
</el-table-column>
<el-table-column label="净重" align="center" prop="netWeight" />
<el-table-column label="逻辑库位" align="center" prop="warehouseName" />
<el-table-column label="实际库区" align="center" prop="actualWarehouseName" />
<el-table-column label="产品类型" align="center" width="180">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row" />
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row" />
</template>
</el-table-column>
<el-table-column label="规格" prop="specification"></el-table-column>
<el-table-column label="物料" prop="itemName"></el-table-column>
<el-table-column label="材质" prop="material"></el-table-column>
<el-table-column label="厂家" prop="manufacturer"></el-table-column>
<el-table-column label="表面处理" prop="surfaceTreatmentDesc"></el-table-column>
<el-table-column label="品质" prop="qualityStatus"></el-table-column>
<el-table-column label="切边" prop="trimmingRequirement"></el-table-column>
<el-table-column label="包装" prop="packagingRequirement"></el-table-column>
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<el-button size="mini" type="text" style="color: #f56c6c;"
@click="removeCoilFromOrder(scope.$index)">移除</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
import {
listMaterialCoil,
getMaterialCoil,
delMaterialCoil,
addMaterialCoil,
updateMaterialCoilSimple,
getMaterialCoilTrace,
exportCoil,
cancelExportCoil,
checkCoilNo,
returnCoil,
getCoilStatisticsList,
listWithAdjustRecordCoil
} from "@/api/wms/coil";
import { listBoundCoil, getBoundCoilStatisticsList } from "@/api/wms/deliveryWaybillDetail";
import { addPendingAction } from "@/api/wms/pendingAction";
import { listTransferOrderItem } from "@/api/wms/transferOrderItem";
import { listCoilQualityRejudge } from "@/api/wms/coilQualityRejudge";
import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
import QRCode from "@/components/QRCode";
import * as XLSX from 'xlsx'
import { saveAsImage } from '@/utils/klp';
import ProductSelect from "@/components/KLPService/ProductSelect";
import RawMaterialSelect from "@/components/KLPService/RawMaterialSelect";
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
// 引入封装的追溯结果组件
import CoilTraceResult from "./CoilTraceResult.vue";
import LabelRender from './LabelRender/index.vue'
import OuterTagPreview from './LabelRender/OuterTagPreview.vue'
import MaterialSelect from "@/components/KLPService/MaterialSelect";
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect";
import { findItemWithBom } from "@/store/modules/category";
import CoilNo from "@/components/KLPService/Renderer/CoilNo.vue";
import MemoInput from "@/components/MemoInput";
import MutiSelect from "@/components/MutiSelect";
import html2canvas from 'html2canvas';
import { PDFDocument } from 'pdf-lib';
import { listUser } from "@/api/system/user";
import AbnormalList from "./abnormal.vue";
import LogTable from "@/views/wms/warehouse/components/LogTable.vue";
import { getCoilTagPrintType } from '@/views/wms/coil/js/coilPrint';
import DragResizeBox from '@/components/DragResizeBox/index.vue';
import ProcessFlow from '../components/ProcessFlow.vue';
import WarehouseTree from '@/components/KLPService/WarehouseTree/index.vue';
import { listDeliveryWaybillDetail, delDeliveryWaybillDetail } from "@/api/wms/deliveryWaybillDetail";
import { listDeliveryPlan } from "@/api/wms/deliveryPlan";
import { addCoilQualityRejudge } from "@/api/wms/coilQualityRejudge";
export default {
name: "MaterialCoil",
components: {
WarehouseSelect,
QRCode,
MaterialSelect,
ProductSelect,
RawMaterialSelect,
ProductInfo,
RawMaterialInfo,
CoilTraceResult,
LabelRender,
ActualWarehouseSelect,
CoilNo,
MemoInput,
MutiSelect,
OuterTagPreview,
AbnormalList,
LogTable,
ProcessFlow,
DragResizeBox,
WarehouseTree,
},
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status', 'wms_next_warehouse', 'coil_business_purpose'],
props: {
qrcode: {
type: Boolean,
default: false,
},
querys: {
type: Object,
default: () => { },
},
labelType: {
type: String,
default: '2'
},
hideWarehouseQuery: {
type: Boolean,
default: false,
},
showStatus: {
type: Boolean,
default: false,
},
showMaterialType: {
type: Boolean,
default: false,
},
showControl: {
type: Boolean,
default: true,
},
showExportTime: {
type: Boolean,
default: false,
},
showGrade: {
type: Boolean,
default: false,
},
showAbnormal: {
type: Boolean,
default: false,
},
showLength: {
type: Boolean,
default: false,
},
canExportAll: {
type: Boolean,
default: false,
},
editNext: {
type: Boolean,
default: false,
},
forceSpecialTag: {
type: String,
required: false,
},
editWarehouse: {
type: Boolean,
default: false,
},
showWaybill: {
type: Boolean,
default: false,
},
showWareLog: {
type: Boolean,
default: false,
},
showBusinessPurpose: {
type: Boolean,
default: false,
},
showRelatedToOrder: {
type: Boolean,
default: false,
},
showOrderBy: {
type: Boolean,
default: false,
},
showNewExport: {
type: Boolean,
default: false,
},
// 展示宽度快捷编辑
showWidthEdit: {
type: Boolean,
default: false,
},
// 展示生产时间快捷编辑
showProductionTimeEdit: {
type: Boolean,
default: false,
},
leftWarehouseQuery: {
type: Boolean,
default: false,
},
hasTransferType: {
type: Boolean,
default: false,
},
useWarehouseIds: {
type: Boolean,
default: false,
},
warehouseOptions: {
type: Array,
default: () => [],
},
moreColumn: {
type: Boolean,
default: false,
},
isShipView: {
type: Boolean,
default: false,
},
},
data() {
return {
shipViewModel: {
status: 'all', // all, has, none
allCount: 0,
hasCount: 0,
noneCount: 0,
},
// 按钮loading
buttonLoading: false,
showProcessFlow: false,
// 遮罩层
loading: true,
waybillLoading: false,
waybillOptions: [],
// 追溯加载中
traceLoading: false,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 钢卷物料表格数据
materialCoilList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 追溯对话框显示
traceOpen: false,
// 追溯结果数据(传递给组件)
traceResult: null,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 50,
enterCoilNo: undefined,
currentCoilNo: undefined,
supplierCoilNo: undefined,
warehouseId: undefined,
nextWarehouseId: undefined,
actualWarehouseId: undefined,
qrcodeRecordId: undefined,
team: undefined,
hasMergeSplit: undefined,
parentCoilNos: undefined,
itemId: undefined,
itemIds: undefined,
status: '',
updateTime: undefined,
orderBy: false,
planId: undefined,
...this.querys,
},
// 表单参数
form: {},
transferCoilForm: {},
// 表单校验
rules: {
enterCoilNo: [
{ required: true, message: "入场钢卷号不能为空", trigger: "blur" },
// 自定义校验必须是8位的阿拉伯数字
{
validator: (rule, value, callback) => {
if (this.form.coilId) {
// 新增时触发校验
callback();
} else {
if (!/^\d{8}$/.test(value)) {
callback(new Error('入场钢卷号必须是8位的阿拉伯数字'));
} else {
callback();
}
}
}, trigger: 'blur'
},
// 远程校验,检查钢卷号是否存在
{
validator: (rule, value, callback) => {
if (this.form.coilId) {
// 新增时触发校验
console.log('新增时触发校验');
callback();
} else {
checkCoilNo({ enterCoilNo: value }).then(res => {
const { duplicateType } = res.data;
if (duplicateType === 'enter' || duplicateType === 'both') {
// alert('入场钢卷号重复,请重新输入');
callback(new Error('入场钢卷号重复,请重新输入'));
} else {
callback();
}
})
}
}, trigger: 'blur'
},
],
currentCoilNo: [
{ required: true, message: "当前钢卷号不能为空", trigger: "blur" },
],
itemId: [
{ required: true, message: "物品ID不能为空", trigger: "blur" }
],
itemType: [
{ required: true, message: "物品类型不能为空", trigger: "change" }
],
// 净重和毛重
netWeight: [
{ required: true, message: "净重不能为空", trigger: "blur" }
],
grossWeight: [
{ required: true, message: "毛重不能为空", trigger: "blur" }
],
},
labelRender: {
visible: false,
data: {},
type: '2'
},
batchPrint: {
visible: false,
loading: false,
list: [],
},
warehouseIds: '',
__printOldTitle: document.title,
floatLayerConfig: {
columns: [
{ label: '入场钢卷号', prop: 'enterCoilNo' },
{ label: '当前钢卷号', prop: 'currentCoilNo' },
{ label: '厂家卷号', prop: 'supplierCoilNo' },
{ label: '逻辑库位', prop: 'warehouseName' },
{ label: '实际库位', prop: 'actualWarehouseName' },
{ label: '物料类型', prop: 'materialType' },
{ label: '班组', prop: 'team' },
{ label: '净重', prop: 'netWeight' },
{ label: '毛重', prop: 'grossWeight' },
{ label: '备注', prop: 'remark' },
{ label: '创建人', prop: 'createBy' },
{ label: '创建时间', prop: 'createTime' },
{ label: '更新人', prop: 'updateBy' },
{ label: '更新时间', prop: 'updateTime' },
{ label: '质量状态', prop: 'qualityStatus' },
{ label: '原料材质', prop: 'packingStatus' },
{ label: '切边要求', prop: 'edgeRequirement' },
{ label: '包装要求', prop: 'packagingRequirement' },
{ label: '物料名称', prop: 'itemName' },
{ label: '材质', prop: 'material' },
{ label: '规格', prop: 'specification' },
{ label: '镀层质量', prop: 'zincLayer' },
{ label: '厂家', prop: 'manufacturer' },
{ label: '调制度', prop: 'temperGrade' },
{ label: '镀层种类', prop: 'coatingType' },
{ label: '实测长度(mm)', prop: 'actualLength' },
{ label: '实测宽度(mm)', prop: 'actualWidth' },
{ label: '实测厚度(mm)', prop: 'actualThickness' },
{ label: '钢卷表面处理', prop: 'coilSurfaceTreatment' },
],
title: '详细信息'
},
abnormalOpen: false,
currentCoilId: '',
userList: [],
logOpen: false,
productionTimeForm: {
productionStartTime: '',
productionEndTime: '',
formattedDuration: '',
productionDuration: 0,
},
productionTimeFormRules: {
productionTime: [
{ required: true, message: "生产时间不能为空", trigger: "blur" }
],
},
productionTimeFormVisible: false,
// 统计数据:已发货的数量和未发货的数量
shippedCount: 0,
unshippedCount: 0,
statistics: {},
// 钢卷改判弹窗
judgeDialogVisible: false,
judgeForm: {
coilId: undefined,
enterCoilNo: undefined,
currentCoilNo: undefined,
netWeight: undefined,
warehouseName: undefined,
actualWarehouseName: undefined,
qualityStatus: undefined,
itemName: undefined,
itemMaterial: undefined,
itemSpecification: undefined,
itemManufacturer: undefined,
notifyReLabel: false
},
// 调拨记录弹窗
transferRecordVisible: false,
transferRecordList: [],
// 暂存单据管理相关数据
tempOrderList: [],
currentTempOrder: null,
coilSelectionVisible: false,
selectedCoilsVisible: false,
coilLoading: false,
availableCoils: [],
selectedCoils: [],
coilTotal: 0,
coilQueryParams: {
pageNum: 1,
pageSize: 20,
enterCoilNo: '',
currentCoilNo: '',
itemName: '',
itemSpecification: '',
itemMaterial: '',
status: 0,
orderBy: true,
dataType: 1,
materialType: '成品',
itemType: 'product',
excludeBound: true,
selectType: 'product'
},
};
},
computed: {
// 动态显示标签
getItemLabel() {
if (this.form.materialType === '成品') {
return '产品类型';
} else if (this.form.materialType === '原料') {
return '原料类型';
}
return '物品类型';
},
canEditExportTime() {
// 徐梦琪和若依管理员
const canEdit = ['1988841895986642945', 1];
const currentUserId = this.$store.getters.id;
return canEdit.includes(currentUserId);
},
},
created() {
if (this.showExportTime) {
this.getUserList();
}
if (this.useWarehouseIds) {
this.warehouseIds = this.warehouseOptions.map(item => item.value).join(',');
}
if (this.showWaybill) {
this.remoteSearchWaybill();
}
this.getList();
// 初始化暂存单据列表
this.loadTempOrderList();
},
methods: {
// === 暂存单据管理相关方法 ===
// 加载暂存单据列表
loadTempOrderList() {
const savedOrders = localStorage.getItem('tempCoilOrders');
if (savedOrders) {
this.tempOrderList = JSON.parse(savedOrders);
} else {
this.tempOrderList = [];
}
},
// 保存暂存单据列表到本地存储
saveTempOrderList() {
localStorage.setItem('tempCoilOrders', JSON.stringify(this.tempOrderList));
},
// 创建暂存单据
createTempOrder() {
const orderName = `暂存单据_${new Date().toLocaleString().replace(/[/:]/g, '-')}`;
const newOrder = {
orderId: Date.now().toString(),
orderName: orderName,
createTime: new Date().toLocaleString(),
coils: []
};
this.tempOrderList.unshift(newOrder);
this.saveTempOrderList();
this.$message.success('暂存单据创建成功');
},
// 删除暂存单据
deleteTempOrder(order) {
this.$confirm(`确认删除暂存单据"${order.orderName}"吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const index = this.tempOrderList.findIndex(item => item.orderId === order.orderId);
if (index > -1) {
this.tempOrderList.splice(index, 1);
this.saveTempOrderList();
this.$message.success('删除成功');
}
}).catch(() => {
this.$message.info('已取消删除');
});
},
// 查看已选钢卷列表
viewSelectedCoils(order) {
this.currentTempOrder = order;
this.selectedCoilsVisible = true;
},
// 编辑单据名称
editOrderName(order) {
this.$prompt('请输入新的单据名称', '编辑单据名称', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputValue: order.orderName,
inputValidator: (value) => {
if (!value || value.trim() === '') {
return '单据名称不能为空';
}
return true;
}
}).then(({ value }) => {
const newName = value.trim();
// 检查名称是否重复
const duplicateOrder = this.tempOrderList.find(item =>
item.orderId !== order.orderId && item.orderName === newName
);
if (duplicateOrder) {
this.$message.warning('单据名称已存在,请使用其他名称');
return;
}
// 更新单据名称
order.orderName = newName;
this.saveTempOrderList();
this.$message.success('单据名称修改成功');
}).catch(() => {
this.$message.info('已取消编辑');
});
},
// 打开钢卷选择对话框
openCoilSelection(order) {
this.currentTempOrder = order;
this.coilSelectionVisible = true;
this.searchCoils();
},
// 计算总重量
calculateTotalWeight(coils) {
if (!coils || coils.length === 0) return 0;
return coils.reduce((total, coil) => {
return total + (parseFloat(coil.netWeight) || 0);
}, 0);
},
// 计算选中钢卷的总重量
calculateSelectedWeight() {
return this.calculateTotalWeight(this.selectedCoils);
},
// 查询钢卷
async searchCoils() {
this.coilLoading = true;
try {
// 构建查询参数
const params = { ...this.coilQueryParams };
// 移除空值参数
Object.keys(params).forEach(key => {
if (params[key] === '' || params[key] === null || params[key] === undefined) {
delete params[key];
}
});
// 调用接口查询钢卷
const response = await listMaterialCoil(params);
this.availableCoils = response.rows || [];
this.coilTotal = response.total || 0;
// 过滤掉所有单据中已选择的钢卷,确保钢卷在所有单据中唯一
const allExistingCoilIds = [];
this.tempOrderList.forEach(order => {
if (order.coils && Array.isArray(order.coils)) {
order.coils.forEach(coil => {
if (coil.coilId) {
allExistingCoilIds.push(coil.coilId);
}
});
}
});
// 如果是当前正在编辑的单据排除当前单据中的钢卷因为它们已经在allExistingCoilIds中了
// 这样可以确保其他单据不能选择当前单据已选的钢卷
this.availableCoils = this.availableCoils.filter(coil => !allExistingCoilIds.includes(coil.coilId));
} catch (error) {
console.error('查询钢卷失败:', error);
this.$message.error('查询钢卷失败');
} finally {
this.coilLoading = false;
}
},
// 重置钢卷查询条件
resetCoilQuery() {
this.coilQueryParams = {
pageNum: 1,
pageSize: 20,
enterCoilNo: '',
currentCoilNo: '',
itemName: '',
itemSpecification: '',
itemMaterial: '',
status: 0,
orderBy: true,
dataType: 1,
materialType: '成品',
itemType: 'product',
excludeBound: true,
selectType: 'product'
};
this.searchCoils();
},
// 处理钢卷选择变化
handleCoilSelection(selection) {
this.selectedCoils = selection;
},
// 保存选中的钢卷
saveSelectedCoils() {
if (!this.currentTempOrder) {
this.$message.error('请先选择暂存单据');
return;
}
if (this.selectedCoils.length === 0) {
this.$message.warning('请选择要添加的钢卷');
return;
}
// 确保coils数组存在
if (!this.currentTempOrder.coils) {
this.currentTempOrder.coils = [];
}
// 由于在查询阶段已经过滤了所有重复的钢卷,这里不需要再次检查
// 但为了安全起见,保留基本的检查逻辑
if (this.currentTempOrder.coils && this.currentTempOrder.coils.length > 0) {
const existingCoilIds = this.currentTempOrder.coils.map(coil => coil.coilId);
const duplicateCoils = this.selectedCoils.filter(coil => existingCoilIds.includes(coil.coilId));
if (duplicateCoils.length > 0) {
this.$message.warning(`钢卷 ${duplicateCoils.map(coil => coil.enterCoilNo).join(', ')} 已存在,不能重复添加`);
return;
}
}
// 添加钢卷到当前单据
this.currentTempOrder.coils.push(...this.selectedCoils);
this.saveTempOrderList();
this.$message.success(`成功添加 ${this.selectedCoils.length} 个钢卷`);
// 关闭对话框并重置选择
this.coilSelectionVisible = false;
this.selectedCoils = [];
},
// 从单据中移除钢卷
removeCoilFromOrder(index) {
if (!this.currentTempOrder || !this.currentTempOrder.coils || !this.currentTempOrder.coils[index]) return;
this.$confirm('确认移除该钢卷吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.currentTempOrder.coils.splice(index, 1);
this.saveTempOrderList();
this.$message.success('移除成功');
}).catch(() => {
this.$message.info('已取消移除');
});
},
// 钢卷分页相关方法
handleCoilSizeChange(val) {
this.coilQueryParams.pageSize = val;
this.coilQueryParams.pageNum = 1;
this.searchCoils();
},
handleCoilCurrentChange(val) {
this.coilQueryParams.pageNum = val;
this.searchCoils();
},
// 进入数字钢卷页面
handleNumberCoilClick(row) {
this.$router.push({
path: '/wms/coil/' + row.coilId,
});
},
// 处理行点击事件
handleRowClick(row) {
this.currentCoilId = row.coilId;
this.logOpen = true;
},
// 处理大小变化事件
resizeChart() {
this.$refs.processFlow.resizeChart();
},
// 获取用户列表
getUserList() {
listUser({ pageNum: 1, pageSize: 1000 }).then(res => {
this.userList = res.rows || [];
});
},
handleProductionTimeEdit(row) {
// 创建一个新对象避免直接引用row
this.productionTimeForm = { ...row };
this.productionTimeFormVisible = true;
// 初始化时计算一次
this.calculateProductionDuration();
},
// 处理逻辑库位点击事件
handleWarehouseNodeClick(node) {
this.queryParams.warehouseId = node.warehouseId;
this.getList();
},
async handleRemoveFromWaybill(row) {
const coilId = row.coilId;
// 根据id查询所在的单据明细
const res = await listDeliveryWaybillDetail({ coilId });
if (res.rows.length != 1) {
this.$message({
message: '发货单查找失败',
type: 'error',
});
return;
}
console.log(res.rows)
const detailId = res.rows[0].detailId;
// 二次确认是否移除
this.$modal.confirm('确认要将该钢卷从发货单中移除吗?', {
title: '确认移除',
type: 'warning',
}).then(() => {
delDeliveryWaybillDetail(detailId).then(res => {
this.$message({
message: '移除成功',
type: 'success',
});
this.getList();
});
})
// 打开一个弹窗列出查询到的所有单据明细
},
async handleBatchRemoveFromWaybill() {
const selectedRows = this.materialCoilList.filter(item => this.ids.includes(item.coilId));
if (selectedRows.length === 0) {
this.$message.warning('请先勾选要移除的钢卷');
return;
}
this.$modal.confirm(`确认要将选中的 ${selectedRows.length} 个钢卷从发货单中移除吗?`, {
title: '批量移除',
type: 'warning',
}).then(async () => {
this.buttonLoading = true;
const detailIds = [];
const failCoils = [];
for (const row of selectedRows) {
try {
const res = await listDeliveryWaybillDetail({ coilId: row.coilId });
if (res.rows.length === 1) {
detailIds.push(res.rows[0].detailId);
} else {
failCoils.push(row.currentCoilNo);
}
} catch (e) {
failCoils.push(row.currentCoilNo);
}
}
if (detailIds.length === 0) {
this.$message.error('未找到任何可移除的发货单明细');
this.buttonLoading = false;
return;
}
try {
await delDeliveryWaybillDetail(detailIds.join(','));
if (failCoils.length > 0) {
this.$message.warning(`移除完成:成功 ${detailIds.length} 个,失败 ${failCoils.length} 个(${failCoils.join('、')}`);
} else {
this.$message.success(`成功移除 ${detailIds.length} 个钢卷`);
}
} catch (e) {
this.$message.error('批量移除失败');
}
this.buttonLoading = false;
this.getList();
});
},
// 处理重贴标签
handleReplaceLabel(row) {
updateMaterialCoilSimple({
...row,
transferType: '',
}).then(res => {
this.$message({
message: '标签已重贴,记录移除',
type: 'success',
});
this.getList();
})
},
// 查看调拨记录
async handleViewTransferRecord(row) {
this.transferRecordVisible = true;
this.transferRecordList = [];
try {
// 获取批量调拨记录
this.loading = true;
const transferOrderRes = await listTransferOrderItem({
coilId: row.coilId,
pageNum: 1,
pageSize: 100
});
// 获取技术部改判记录
const rejudgeRes = await listCoilQualityRejudge({
coilId: row.coilId,
pageNum: 1,
pageSize: 100
});
// 合并记录
const list = [];
// 添加批量调拨记录
(transferOrderRes.rows || []).forEach(item => {
list.push({
type: '批量调拨',
before: '逻辑库:' + (item.warehouseNameBefore || '-'),
after: '逻辑库:' + (item.warehouseNameAfter || '-'),
createTime: item.createTime || '-',
remark: item.remark || '-',
...item
});
});
// 添加技术部改判记录
(rejudgeRes.rows || []).forEach(item => {
list.push({
...item,
type: '技术部改判',
before: '质量状态:' + (item.beforeQuality || '-'),
after: '质量状态:' + (item.afterQuality || '-'),
createTime: item.createTime || '-',
remark: item.rejudgeReason,
});
});
// 按时间排序
list.sort((a, b) => {
const timeA = new Date(a.createTime || 0).getTime();
const timeB = new Date(b.createTime || 0).getTime();
return timeB - timeA;
});
this.transferRecordList = list;
} catch (error) {
console.error('获取调拨记录失败:', error);
this.$message.error('获取调拨记录失败');
} finally {
this.loading = false;
}
},
// 格式化毫秒值为xx天xx小时xx分钟
formatDuration(milliseconds) {
if (!milliseconds || milliseconds < 0) return '';
const seconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const remainingHours = hours % 24;
const remainingMinutes = minutes % 60;
let result = '';
if (days > 0) result += `${days}`;
if (remainingHours > 0) result += `${remainingHours}小时`;
if (remainingMinutes > 0) result += `${remainingMinutes}分钟`;
return result || '0分钟';
},
// 计算生产耗时
calculateProductionDuration() {
const { productionStartTime, productionEndTime } = this.productionTimeForm;
if (productionStartTime && productionEndTime) {
const start = new Date(productionStartTime).getTime();
const end = new Date(productionEndTime).getTime();
if (end < start) {
this.$message({
message: '结束时间不能早于开始时间',
type: 'error',
});
this.$set(this.productionTimeForm, 'productionDuration', '');
this.$set(this.productionTimeForm, 'formattedDuration', '');
} else {
const durationMs = end - start;
const durationMinutes = Math.round(durationMs / (1000 * 60));
this.$set(this.productionTimeForm, 'productionDuration', durationMinutes);
// 同时保存格式化后的显示值
this.$set(this.productionTimeForm, 'formattedDuration', this.formatDuration(durationMinutes * 60 * 1000));
}
} else {
this.$set(this.productionTimeForm, 'productionDuration', '');
this.$set(this.productionTimeForm, 'formattedDuration', '');
}
},
// 处理生产时间提交
submitProductionTimeForm() {
this.$refs.productionTimeForm.validate((valid) => {
if (valid) {
// 再次验证时间逻辑
const { productionStartTime, productionEndTime } = this.productionTimeForm;
if (productionStartTime && productionEndTime) {
const start = new Date(productionStartTime).getTime();
const end = new Date(productionEndTime).getTime();
if (end < start) {
this.$message({
message: '结束时间不能早于开始时间',
type: 'error',
});
return false;
}
}
this.buttonLoading = true;
updateMaterialCoilSimple(this.productionTimeForm).then(res => {
this.buttonLoading = false;
this.$message({
message: '更新成功',
type: 'success',
});
this.productionTimeFormVisible = false;
})
} else {
return false;
}
})
},
handleNextWarehouseChange(row) {
if (!this.editNext) {
return;
}
updateMaterialCoilSimple(row).then(res => {
if (res.code === 200) {
this.$message({
message: '更新成功',
type: 'success',
});
} else {
this.$message({
message: res.msg || '更新失败',
type: 'error',
});
}
})
},
// 处理行数据变化
handleRowChange(row) {
updateMaterialCoilSimple(row).then(res => {
this.$message({
message: '更新成功',
type: 'success',
});
})
},
// 打印标签
handlePrintLabel(row) {
const type = getCoilTagPrintType(row);
this.labelRender.type = type;
this.labelRender.data = {
...row,
updateTime: row.updateTime?.split(' ')[0] || '',
};
this.$nextTick(() => {
this.$refs.labelRender.printLabel();
})
},
// 处理材料类型变化
handleMaterialTypeChange(value) {
// 清空物品选择
this.form.itemId = null;
// 根据材料类型设置物品类型
if (value === '成品') {
this.form.itemType = 'product';
} else if (value === '原料') {
this.form.itemType = 'raw_material';
}
},
/** 查询钢卷物料列表 */
async getList() {
this.loading = true;
const { updateTime, ...query } = {
...this.queryParams,
startTime: this.queryParams.updateTime?.[0],
endTime: this.queryParams.updateTime?.[1],
}
// 如果没有设置itemType则设置为raw_material
query.selectType = this.querys.materialType === '原料' ? 'raw_material' : 'product';
if (this.useWarehouseIds) {
query.warehouseIds = this.warehouseIds;
}
const { orderBy, ...statisticQuery } = query;
if (this.showWaybill) {
const { shipmentTime, ...query } = {
...this.queryParams,
exportTimeBy: true,
selectType: this.querys.materialType === '原料' ? 'raw_material' : 'product',
startTime: this.queryParams.shipmentTime?.[0] && this.queryParams.shipmentTime?.[0] + ' 00:00:00',
endTime: this.queryParams.shipmentTime?.[1] && this.queryParams.shipmentTime?.[1] + ' 23:59:59',
}
listBoundCoil(query).then(res => {
this.materialCoilList = res.rows || [];
this.total = res.total;
this.loading = false;
})
// 获取统计数据:已发货的数量和未发货的数量
listBoundCoil({ ...query, status: 0, pageNum: 1, pageSize: 1 }).then(res => {
this.unshippedCount = res.total;
})
// 获取统计数据:已发货的数量和未发货的数量
listBoundCoil({ ...query, status: 1, pageNum: 1, pageSize: 1 }).then(res => {
this.shippedCount = res.total;
})
getBoundCoilStatisticsList(query).then(res => {
this.statistics = res.data || [];
})
return;
}
if (this.hasTransferType) {
listWithAdjustRecordCoil(query).then(response => {
this.materialCoilList = response.rows
this.total = response.total;
this.loading = false;
});
getCoilStatisticsList(statisticQuery).then(res => {
this.statistics = res.data || [];
})
return;
}
if (this.isShipView) {
// query.status = this.shipViewModel.status;
query.status = 1;
if (this.shipViewModel.status === 'all') {
listMaterialCoil(query).then(response => {
this.materialCoilList = response.rows
this.total = response.total;
this.loading = false;
});
getCoilStatisticsList(statisticQuery).then(res => {
this.statistics = res.data || [];
})
} else if (this.shipViewModel.status === 'has') {
listBoundCoil(query).then(res => {
this.materialCoilList = res.rows || [];
this.total = res.total;
this.loading = false;
})
getBoundCoilStatisticsList(query).then(res => {
this.statistics = res.data || [];
})
} else if (this.shipViewModel.status === 'none') {
query.excludeBound = true;
listMaterialCoil(query).then(response => {
this.materialCoilList = response.rows
this.total = response.total;
this.loading = false;
});
getCoilStatisticsList(statisticQuery).then(res => {
this.statistics = res.data || [];
})
}
listBoundCoil({ ...query, pageNum: 1, pageSize: 1 }).then(res => {
this.shipViewModel.hasCount = res.total;
})
listMaterialCoil({ ...query, pageNum: 1, pageSize: 1, excludeBound: true }).then(response => {
this.shipViewModel.noneCount = response.total;
});
return;
}
listMaterialCoil(query).then(response => {
this.materialCoilList = response.rows
this.total = response.total;
this.loading = false;
});
getCoilStatisticsList(statisticQuery).then(res => {
this.statistics = res.data || [];
})
},
/** 追溯按钮操作 */
handleTrace(row) {
this.traceOpen = true;
this.traceLoading = true;
this.traceResult = null; // 清空历史数据
getMaterialCoilTrace({
coilId: row.coilId,
currentCoilNo: row.currentCoilNo,
}).then(res => {
this.traceResult = res.data; // 将结果传递给组件
}).catch(err => {
console.error('溯源查询失败:', err);
this.$message.error('溯源查询失败,请重试');
}).finally(() => {
this.traceLoading = false;
});
},
handleGradeChange(row) {
updateMaterialCoilSimple(row).then(res => {
this.$message.success('质量状态更新成功');
this.getList(); // 刷新列表
})
},
handleWarehouseChange(row) {
if (!this.editWarehouse) {
return;
}
updateMaterialCoilSimple(row).then(res => {
this.$message.success('库位更新成功');
this.getList(); // 刷新列表
})
},
/** 预览标签 */
handlePreviewLabel(row) {
this.labelRender.visible = true;
const itemName = row.itemName || '';
this.labelRender.type = row.itemType === 'product' ? '3' : '2';
this.labelRender.data = {
...row,
itemName: itemName,
updateTime: row.updateTime?.split(' ')[0] || '',
};
},
/** 下载二维码 */
handleDownloadQRCode(row) {
try {
saveAsImage(
row.qrcodeRecordId,
'',
1,
{
barcodeWidth: 200,
barcodeHeight: 200
}
);
this.$message.success('图片保存成功');
} catch (error) {
console.error('保存图片失败', error);
this.$message.error('保存图片失败,请稍后重试');
}
},
handleAbnormal(row) {
this.currentCoilId = row.coilId;
this.abnormalOpen = true;
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
coilId: undefined,
enterCoilNo: undefined,
currentCoilNo: undefined,
supplierCoilNo: undefined,
dataType: 1,
warehouseId: undefined,
nextWarehouseId: undefined,
qrcodeRecordId: undefined,
actualWarehouseId: 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,
materialType: '原料',
temperGrade: undefined,
coatingType: undefined,
};
this.resetForm("form");
},
remoteSearchWaybill(query) {
this.waybillLoading = true;
listDeliveryPlan({ planName: query, pageNum: 1, pageSize: 20, planType: 0 }).then(res => {
this.waybillOptions = res.rows || [];
}).finally(() => {
this.waybillLoading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.warehouseId = '';
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.coilId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 修改按钮操作 */
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;
// 设置 materialType优先级后端返回 > itemType推断 > 父组件传入)
if (!this.form.materialType) {
if (this.form.itemType) {
// 根据 itemType 推断
if (this.form.itemType === 'product') {
this.form.materialType = '成品';
} else if (this.form.itemType === 'raw_material') {
this.form.materialType = '原料';
}
} else if (this.querys.materialType) {
// 使用父组件传入的默认值
this.form.materialType = this.querys.materialType;
// 同时设置对应的 itemType
if (this.querys.materialType === '成品') {
this.form.itemType = 'product';
} else if (this.querys.materialType === '原料') {
this.form.itemType = 'raw_material';
}
}
}
this.open = true;
this.title = "修改钢卷物料";
});
},
handleExportCoil(row) {
exportCoil(row.coilId).then(response => {
this.$modal.msgSuccess("发货成功");
// 2. 插入一条已完成的待操作记录
addPendingAction({
coilId: row.coilId,
currentCoilNo: row.currentCoilNo,
actionType: 402, // 402=发货
actionStatus: 2, // 直接标记为完成状态
scanTime: new Date(),
// scanDevice: ,
priority: 0, // 0=普通
sourceType: 'scan',
warehouseId: row.warehouseId,
processTime: new Date(),
completeTime: new Date()
});
this.getList();
}).catch(error => {
this.$modal.msgError("发货失败");
});
},
async handleNewExport(row) {
this.loading = true
let coilIds = ''
const query = {
...this.queryParams,
selectType: 'product',
pageSize: 9999,
pageNum: 1,
}
if (this.showWaybill) {
const res = await listBoundCoil(query)
coilIds = res.rows.map(item => item.coilId).join(',')
this.loading = false
this.download('/wms/materialCoil/exportDelivery', {
coilIds,
}, 'coil.xlsx')
} else {
const { rows: coils } = await listMaterialCoil(query)
coilIds = coils.map(item => item.coilId).join(',')
this.loading = false
this.download('wms/materialCoil/exportAll', {
coilIds,
}, 'coil.xlsx')
}
},
async handleNewExportProps(row) {
this.loading = true
let coilIds = ''
const query = {
...this.querys,
selectType: 'product',
pageSize: 9999,
pageNum: 1,
}
if (this.showWaybill) {
const res = await listBoundCoil(query)
coilIds = res.rows.map(item => item.coilId).join(',')
this.loading = false
this.download('/wms/materialCoil/exportDelivery', {
coilIds,
}, 'coil.xlsx')
} else {
const { rows: coils } = await listMaterialCoil(query)
coilIds = coils.map(item => item.coilId).join(',')
this.loading = false
this.download('wms/materialCoil/exportAll', {
coilIds,
}, 'coil.xlsx')
}
},
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;
// 设置 materialType优先级后端返回 > itemType推断 > 父组件传入)
if (!this.form.materialType) {
if (this.form.itemType) {
// 根据 itemType 推断
if (this.form.itemType === 'product') {
this.form.materialType = '成品';
} else if (this.form.itemType === 'raw_material') {
this.form.materialType = '原料';
}
} else if (this.querys.materialType) {
// 使用父组件传入的默认值
this.form.materialType = this.querys.materialType;
// 同时设置对应的 itemType
if (this.querys.materialType === '成品') {
this.form.itemType = 'product';
} else if (this.querys.materialType === '原料') {
this.form.itemType = 'raw_material';
}
}
}
this.open = true;
this.title = "修改钢卷物料";
});
},
handleLog(row) {
this.logOpen = true;
this.currentCoilId = row.coilId;
},
transferCoil() { },
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.coilId != null) {
const { status, exclusiveStatus, dataType, ...payload } = {
...this.form,
}
updateMaterialCoilSimple(payload).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;
});
}
}
});
},
handleCancelExport(row) {
// 将相关的action改为已取消然后将钢卷的已发货状态撤回
this.$modal.confirm('是否确认撤回钢卷编号为"' + row.currentCoilNo + '"的发货操作?').then(() => {
this.buttonLoading = true;
cancelExportCoil(row.coilId).then(response => {
this.$modal.msgSuccess("撤回发货成功");
this.getList();
}).catch(error => {
this.$modal.msgError("撤回发货失败");
}).finally(() => {
this.buttonLoading = false;
});
}).catch(() => {
});
},
handleReturnCoil(row) {
this.$modal.confirm('是否确认退货钢卷编号为"' + row.currentCoilNo + '"的退货操作?').then(() => {
this.buttonLoading = true;
returnCoil(row.coilId).then(response => {
this.$modal.msgSuccess("退货成功");
this.getList();
}).catch(error => {
this.$modal.msgError("退货失败");
}).finally(() => {
this.buttonLoading = false;
});
})
},
handleExportTimeChange(row) {
if (row.exportTime) {
row.exportTime = row.exportTime.replace('T', ' ');
}
this.buttonLoading = true;
updateMaterialCoilSimple(row).then(_ => {
this.$modal.msgSuccess("发货时间修改成功");
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
},
handleExportByNameChange(row) {
this.buttonLoading = true;
updateMaterialCoilSimple(row).then(_ => {
this.$modal.msgSuccess("发货人修改成功");
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
},
/** 改判按钮操作 */
handleJudge(row) {
// 填充改判表单数据
this.judgeForm = {
...row,
oldQuality: row.qualityStatus,
notifyReLabel: true
};
this.judgeDialogVisible = true;
},
/** 提交改判表单 */
submitJudgeForm() {
this.buttonLoading = true;
const submitData = {
...this.judgeForm,
// 如果通知重贴标签,则设置 transferType 为技术部改判
transferType: this.judgeForm.notifyReLabel ? '技术部改判' : null
};
updateMaterialCoilSimple(submitData).then(res => {
this.buttonLoading = false;
this.$message({
message: '改判成功',
type: 'success'
});
this.judgeDialogVisible = false;
// 记录改判前和改判后的钢卷质量状态
addCoilQualityRejudge({
coilId: this.judgeForm.coilId,
beforeQuality: this.judgeForm.oldQuality,
afterQuality: this.judgeForm.qualityStatus,
rejudgeReason: this.judgeForm.rejudgeReason,
})
this.getList();
}).catch(err => {
this.buttonLoading = false;
this.$message({
message: '改判失败',
type: 'error'
});
});
},
/** 删除按钮操作 */
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;
});
},
handleExportAll() {
const { orderBy, ...query } = this.queryParams;
query.selectType = query.itemType;
this.download('wms/materialCoil/export', query, `materialCoil_${new Date().getTime()}.xlsx`)
},
handleExportAllProps() {
const query = this.querys;
this.download('wms/materialCoil/export', query, `materialCoil_${new Date().getTime()}.xlsx`)
},
/** 批量打印标签按钮 */
handleBatchPrintLabel() {
if (!this.ids || this.ids.length === 0) {
this.$message.warning('请先勾选要打印标签的钢卷');
return;
}
// 取出选中行数据,并补齐标签渲染需要的字段
const selectedData = this.materialCoilList
.filter(item => this.ids.includes(item.coilId))
.map(row => {
const itemName = row.itemName || '';
return {
...row,
itemName,
// OuterTagPreview.vue 里字段名使用了 specification/material而列表里是 itemSpecification/itemMaterial
specification: row.itemSpecification || row.specification || '',
material: row.itemMaterial || row.material || '',
updateTime: row.updateTime?.split(' ')[0] || row.updateTime || '',
}
});
this.batchPrint.list = selectedData;
this.batchPrint.visible = true;
},
/** 批量导出标签PDF每个标签一页180mm × 100mm */
async handleBatchExportLabelPdf() {
if (!this.batchPrint.list || this.batchPrint.list.length === 0) {
this.$message.warning('没有可导出的数据');
return;
}
const container = this.$refs.batchPdfContainer;
if (!container) {
this.$message.error('PDF渲染容器未初始化');
return;
}
try {
this.batchPrint.loading = true;
await this.$nextTick();
await new Promise(resolve => setTimeout(resolve, 100));
const mmToPt = 72 / 25.4;
// 纸张尺寸
const paperWidthMm = 180;
const paperHeightMm = 100;
// 边距:左右 4mm上下对称 2mm确保垂直居中
const marginXmm = 4;
const marginYmm = 0.5; // 上下对称边距(不裁切前提下尽量贴边)
const pageWidthPt = paperWidthMm * mmToPt;
const pageHeightPt = paperHeightMm * mmToPt;
const marginXPt = marginXmm * mmToPt;
const marginYPt = marginYmm * mmToPt;
const contentWidthPt = pageWidthPt - marginXPt * 2;
const contentHeightPt = pageHeightPt - marginYPt * 2;
const pdfDoc = await PDFDocument.create();
// 关键:只截取标签本身(.label-container不要把外层容器/按钮高度算进去
const pageEls = container.querySelectorAll('.batch-pdf-page');
for (let i = 0; i < pageEls.length; i++) {
const el = pageEls[i];
// 在每一页内部优先查找标签根节点
const labelEl =
el.querySelector('.label-container') ||
el.querySelector('.material-label-container') ||
el; // 兜底:找不到时退回整页
// 强制用标签的实际尺寸作为截图基准,避免被外层布局影响
const canvas = await html2canvas(labelEl, {
backgroundColor: '#ffffff',
scale: 3,
useCORS: true,
// 让 html2canvas 为频繁读回优化 Canvas浏览器会提示 willReadFrequently
willReadFrequently: true,
// 确保按元素尺寸截图
width: labelEl.offsetWidth,
height: labelEl.offsetHeight,
windowWidth: labelEl.scrollWidth,
windowHeight: labelEl.scrollHeight,
});
const imgDataUrl = canvas.toDataURL('image/png');
const pngImage = await pdfDoc.embedPng(imgDataUrl);
const page = pdfDoc.addPage([pageWidthPt, pageHeightPt]);
// 图片铺满页面(保持比例,居中)
const imgW = pngImage.width;
const imgH = pngImage.height;
// 标准适配:确保完整显示且不变形(在内容区域内等比缩放)
const scale = Math.min(contentWidthPt / imgW, contentHeightPt / imgH);
const drawW = imgW * scale;
const drawH = imgH * scale;
// 内容区域内居中 + 外层边距(上下对称)
const x = marginXPt + (contentWidthPt - drawW) / 2;
const y = marginYPt + (contentHeightPt - drawH) / 2;
page.drawImage(pngImage, { x, y, width: drawW, height: drawH });
}
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
// 尽量避免被浏览器拦截弹窗优先在当前tab打开如仍被策略限制可改为下载
const win = window.open(url, '_blank');
if (!win) {
// fallback触发下载
const a = document.createElement('a');
a.href = url;
a.download = `钢卷标签_${new Date().getTime()}.pdf`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
} catch (e) {
console.error(e);
this.$message.error('生成PDF失败请重试');
} finally {
this.batchPrint.loading = false;
}
},
/** 导出选中数据操作 */
handleExport() {
// 1. 判断是否有选中数据
if (this.ids.length === 0) {
this.$message.warning('请先选中要导出的数据');
return;
}
// 2. 筛选选中的数据通过ids匹配表格数据
const selectedData = this.materialCoilList.filter(item =>
this.ids.includes(item.coilId) // 用选中的coilId匹配表格数据
);
// 3. 处理导出数据格式(和之前一致,转换枚举值)
const exportData = selectedData.map(item => {
return {
'入场钢卷号': item.enterCoilNo || '',
'当前钢卷号': item.currentCoilNo || '',
'厂家原料卷号': item.supplierCoilNo || '',
'物料类型': item.itemType === 'product' ? '成品' : '原料',
'仓区': item.warehouseName || '',
'实际库区': item.actualWarehouseName || '',
'物品': findItemWithBom(item.itemType, item.itemId)?.itemName || '',
'数据类型': item.dataType === 0 ? '历史数据' : '当前数据',
'班组': item.team || '',
'毛重': item.grossWeight || '',
'净重': item.netWeight || '',
'备注': item.remark || ''
};
});
// 4. 生成Excel并下载复用之前的逻辑
const worksheet = XLSX.utils.json_to_sheet(exportData);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '选中钢卷物料');
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
this.saveExcelFile(excelBuffer, '选中钢卷物料数据');
},
/** 保存Excel文件到本地 */
saveExcelFile(buffer, fileName) {
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8'
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${fileName}_${new Date().getTime()}.xlsx`; // 文件名带时间戳
document.body.appendChild(a);
a.click(); // 触发下载
document.body.removeChild(a); // 清理DOM
URL.revokeObjectURL(url); // 释放URL对象
}
}
};
</script>
<style scoped>
/* 批量导出PDF渲染容器屏幕隐藏但保留真实布局尺寸给 html2canvas 截图 */
.batch-pdf-root {
position: fixed;
left: -100000px;
top: 0;
width: 180mm;
background: #fff;
}
.batch-pdf-page {
width: 180mm;
height: 100mm;
box-sizing: border-box;
overflow: hidden;
}
.el-table .el-button+.el-button {
margin-left: 0;
}
::v-deep .el-table {
margin-top: 0;
}
</style>