Files
klp-oa/klp-ui/src/views/wms/coil/panels/base.vue

2411 lines
94 KiB
Vue
Raw Normal View History

<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="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 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-select v-model="queryParams.status" placeholder="请选择发货状态" clearable>
<el-option :value="0" label="未发货">未发货</el-option>
<el-option :value="1" label="已发货">已发货</el-option>
</el-select> -->
</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-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>
2026-01-09 16:31:59 +08:00
</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 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="逻辑库位" align="center" prop="warehouseName" v-if="!hideWarehouseQuery" />
<el-table-column label="实际库区" 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 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="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 label="备注" align="center" prop="remark" show-overflow-tooltip />
<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" width="150">
<template slot-scope="scope">
{{ scope.row.bindPlanName || '-' }}
</template>
</el-table-column>
<el-table-column label="发货单据" align="center" prop="nextWarehouseId" v-if="showWaybill" 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>
2026-01-09 16:31:59 +08:00
<label-render :forceSpecialTag="forceSpecialTag" ref="labelRender" v-show="false" :content="labelRender.data"
:labelType="labelRender.type" />
2026-01-09 16:31:59 +08:00
<!-- 批量导出标签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>
2026-01-09 16:31:59 +08:00
</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" />
2026-01-09 16:31:59 +08:00
</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 } 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'
2026-01-09 16:31:59 +08:00
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";
2026-01-09 16:31:59 +08:00
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 { addCoilQualityRejudge } from "@/api/wms/coilQualityRejudge";
export default {
name: "MaterialCoil",
components: {
WarehouseSelect,
QRCode,
MaterialSelect,
ProductSelect,
RawMaterialSelect,
ProductInfo,
RawMaterialInfo,
CoilTraceResult,
LabelRender,
ActualWarehouseSelect,
CoilNo,
MemoInput,
MutiSelect,
2026-01-09 16:31:59 +08:00
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,
},
},
data() {
return {
// 按钮loading
buttonLoading: false,
showProcessFlow: false,
// 遮罩层
loading: true,
// 追溯加载中
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,
...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'
},
2026-01-09 16:31:59 +08:00
batchPrint: {
visible: false,
loading: false,
list: [],
},
warehouseIds: '',
2026-01-09 16:31:59 +08:00
__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(',');
}
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();
});
})
// 打开一个弹窗列出查询到的所有单据明细
},
// 处理重贴标签
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] + ' 00:00:00',
endTime: 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 }).then(res => {
this.unshippedCount = res.total;
})
// 获取统计数据:已发货的数量和未发货的数量
listBoundCoil({ ...query, status: 1 }).then(res => {
this.shippedCount = res.total;
})
getCoilStatisticsList(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;
}
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");
},
/** 搜索按钮操作 */
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`)
},
2026-01-09 16:31:59 +08:00
/** 批量打印标签按钮 */
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 || '';
2026-01-09 16:31:59 +08:00
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;
2026-01-09 19:04:03 +08:00
// 边距:左右 4mm上下对称 2mm确保垂直居中
2026-01-09 16:31:59 +08:00
const marginXmm = 4;
2026-01-09 19:04:03 +08:00
const marginYmm = 0.5; // 上下对称边距(不裁切前提下尽量贴边)
2026-01-09 16:31:59 +08:00
const pageWidthPt = paperWidthMm * mmToPt;
const pageHeightPt = paperHeightMm * mmToPt;
const marginXPt = marginXmm * mmToPt;
2026-01-09 19:04:03 +08:00
const marginYPt = marginYmm * mmToPt;
2026-01-09 16:31:59 +08:00
const contentWidthPt = pageWidthPt - marginXPt * 2;
2026-01-09 19:04:03 +08:00
const contentHeightPt = pageHeightPt - marginYPt * 2;
2026-01-09 16:31:59 +08:00
const pdfDoc = await PDFDocument.create();
2026-01-09 19:04:03 +08:00
// 关键:只截取标签本身(.label-container不要把外层容器/按钮高度算进去
2026-01-09 16:31:59 +08:00
const pageEls = container.querySelectorAll('.batch-pdf-page');
for (let i = 0; i < pageEls.length; i++) {
const el = pageEls[i];
2026-01-09 19:04:03 +08:00
// 在每一页内部优先查找标签根节点
const labelEl =
el.querySelector('.label-container') ||
el.querySelector('.material-label-container') ||
el; // 兜底:找不到时退回整页
// 强制用标签的实际尺寸作为截图基准,避免被外层布局影响
const canvas = await html2canvas(labelEl, {
2026-01-09 16:31:59 +08:00
backgroundColor: '#ffffff',
2026-01-09 19:04:03 +08:00
scale: 3,
2026-01-09 16:31:59 +08:00
useCORS: true,
// 让 html2canvas 为频繁读回优化 Canvas浏览器会提示 willReadFrequently
willReadFrequently: true,
// 确保按元素尺寸截图
2026-01-09 19:04:03 +08:00
width: labelEl.offsetWidth,
height: labelEl.offsetHeight,
windowWidth: labelEl.scrollWidth,
windowHeight: labelEl.scrollHeight,
2026-01-09 16:31:59 +08:00
});
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;
2026-01-09 19:04:03 +08:00
// 标准适配:确保完整显示且不变形(在内容区域内等比缩放)
2026-01-09 16:31:59 +08:00
const scale = Math.min(contentWidthPt / imgW, contentHeightPt / imgH);
const drawW = imgW * scale;
const drawH = imgH * scale;
2026-01-09 19:04:03 +08:00
// 内容区域内居中 + 外层边距(上下对称)
2026-01-09 16:31:59 +08:00
const x = marginXPt + (contentWidthPt - drawW) / 2;
2026-01-09 19:04:03 +08:00
const y = marginYPt + (contentHeightPt - drawH) / 2;
2026-01-09 19:04:03 +08:00
page.drawImage(pngImage, { x, y, width: drawW, height: drawH });
2026-01-09 16:31:59 +08:00
}
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对象
}
}
};
2026-01-09 16:31:59 +08:00
</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;
}
::v-deep .el-table {
margin-top: 0;
}
2026-01-09 16:31:59 +08:00
</style>