feat: 多个页面功能优化与新增
1. 异议页面:新增状态为4时的导出PDF按钮,替换原有注释的打印按钮 2. 流程图页面:重构流程图组件,支持多流程切换、节点点击事件,优化主题配置和渲染逻辑 3. 钢卷待领页面:新增钢卷数据修正功能,新增操作列按钮和对应弹窗表单,注释绑定组件 4. 导出PDF弹窗:优化多钢卷数据展示,拆分合并附件排版,优化导出样式和分页逻辑
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
:name="tab.value"></el-tab-pane>
|
:name="tab.value"></el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<h3 class="section-title" v-else>待领物料列表</h3>
|
<h3 class="section-title" v-else>待领物料列表</h3>
|
||||||
<schedule-detail-coil-bind />
|
<!-- <schedule-detail-coil-bind /> -->
|
||||||
<el-button size="mini" icon="el-icon-refresh" @click="getMaterialCoil">刷新</el-button>
|
<el-button size="mini" icon="el-icon-refresh" @click="getMaterialCoil">刷新</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -44,12 +44,13 @@
|
|||||||
|
|
||||||
<el-table-column prop="specification" label="规格" width="100" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="specification" label="规格" width="100" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column prop="netWeight" label="净重" width="100" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="netWeight" label="净重" width="100" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column prop="action" label="操作" width="320">
|
<el-table-column prop="action" label="操作" width="360">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button v-loading="buttonLoading" style="margin-left: 0px; padding: 4px !important;" type="default"
|
<el-button v-loading="buttonLoading" style="margin-left: 0px; padding: 4px !important;" type="default"
|
||||||
size="mini" icon="el-icon-view" @click="handlePreviewLabel(scope.row)" title="预览标签">预览</el-button>
|
size="mini" icon="el-icon-view" @click="handlePreviewLabel(scope.row)" title="预览标签">预览</el-button>
|
||||||
<el-button v-loading="buttonLoading" style="margin-left: 0px; padding: 4px !important;" type="default"
|
<el-button v-loading="buttonLoading" style="margin-left: 0px; padding: 4px !important;" type="default"
|
||||||
size="mini" icon="el-icon-printer" @click="handlePrintLabel(scope.row)" title="打印标签">打印</el-button>
|
size="mini" icon="el-icon-printer" @click="handlePrintLabel(scope.row)" title="打印标签">打印</el-button>
|
||||||
|
<el-button size="mini" type="default" icon="el-icon-edit" @click="handleCheck(scope.row)" style="margin-left: 0;">修正</el-button>
|
||||||
<el-button v-if="useSpecialSplit" :style="splitButtonStyle" icon="el-icon-scissors" size="mini"
|
<el-button v-if="useSpecialSplit" :style="splitButtonStyle" icon="el-icon-scissors" size="mini"
|
||||||
@click="handleStartSplit(scope.row)" :loading="buttonLoading" class="action-btn">加工</el-button>
|
@click="handleStartSplit(scope.row)" :loading="buttonLoading" class="action-btn">加工</el-button>
|
||||||
<el-button v-else type="primary" icon="el-icon-check" size="mini" @click="handlePickMaterial(scope.row)"
|
<el-button v-else type="primary" icon="el-icon-check" size="mini" @click="handlePickMaterial(scope.row)"
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
缺陷明细
|
缺陷明细
|
||||||
<span v-if="scope.row.abnormalCount > 0">({{ scope.row.abnormalCount }})</span>
|
<span v-if="scope.row.abnormalCount > 0">({{ scope.row.abnormalCount }})</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</KLPTable>
|
</KLPTable>
|
||||||
@@ -297,15 +299,206 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<label-render ref="labelRender" v-show="false" :content="labelRender.data" :labelType="labelRender.type" />
|
<label-render ref="labelRender" v-show="false" :content="labelRender.data" :labelType="labelRender.type" />
|
||||||
|
|
||||||
|
<!-- 数据修正对话框 -->
|
||||||
|
<el-dialog :title="title" :visible.sync="open" width="1200px" append-to-body>
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="入场钢卷号" prop="enterCoilNo">
|
||||||
|
<el-input v-model="form.enterCoilNo" placeholder="请输入入场钢卷号" :disabled="!!form.coilId" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="当前钢卷号" prop="currentCoilNo">
|
||||||
|
<el-input v-model.trim="form.currentCoilNo" placeholder="请输入当前钢卷号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="厂家原料卷号" prop="supplierCoilNo">
|
||||||
|
<el-input v-model.trim="form.supplierCoilNo" placeholder="请输入厂家原料卷号" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="所在库位" prop="warehouseId">
|
||||||
|
<warehouse-select v-model="form.warehouseId" placeholder="请选择仓库/库区/库位" style="width: 100%;" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="实际库区" prop="actualWarehouseId">
|
||||||
|
<actual-warehouse-select v-model="form.actualWarehouseId" placeholder="请选择实际库区" style="width: 100%;" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="毛重" prop="grossWeight">
|
||||||
|
<el-input v-model="form.grossWeight" placeholder="请输入毛重" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="净重" prop="netWeight">
|
||||||
|
<el-input v-model="form.netWeight" placeholder="请输入净重" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="实测长度(mm)" prop="actualLength">
|
||||||
|
<el-input-number :controls="false" v-model="form.actualLength" placeholder="请输入实测长度" type="number" :step="0.01" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<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-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="实测厚度(mm)" prop="actualThickness">
|
||||||
|
<el-input-number :controls="false" v-model="form.actualThickness" placeholder="请输入实测厚度" type="number" :step="0.01" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="业务目的" prop="businessPurpose">
|
||||||
|
<el-select v-model="form.businessPurpose" placeholder="请选择业务目的" style="width: 100%">
|
||||||
|
<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-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="调制度" prop="temperGrade">
|
||||||
|
<el-input v-model="form.temperGrade" placeholder="请输入调制度" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="镀层种类" prop="coatingType">
|
||||||
|
<memo-input storageKey="coatingType" v-model="form.coatingType" placeholder="请输入镀层种类" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="钢卷表面处理" prop="coilSurfaceTreatment">
|
||||||
|
<memo-input storageKey="surfaceTreatmentDesc" v-model="form.coilSurfaceTreatment" placeholder="请输入钢卷表面处理" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="排产厚度(mm)" prop="scheduleThickness">
|
||||||
|
<el-input-number :controls="false" v-model="form.scheduleThickness" placeholder="请输入排产厚度" type="number" :step="0.001" style="width: 100%;" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="生产开始时间" prop="productionStartTime">
|
||||||
|
<time-input v-model="form.productionStartTime" @input="calculateFormProductionDuration" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="生产结束时间" prop="productionEndTime">
|
||||||
|
<time-input v-model="form.productionEndTime" @input="calculateFormProductionDuration" :show-now-button="true" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="生产耗时" prop="productionDuration">
|
||||||
|
<el-input v-model="form.formattedDuration" placeholder="自动计算" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20" v-if="form.coilId">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="绑定合同" prop="contractId">
|
||||||
|
<div style="display: flex; gap: 10px; width: 100%;">
|
||||||
|
<contract-select v-model="form.contractId" placeholder="请选择合同" style="flex: 1;" clearable mode="all" />
|
||||||
|
<el-button type="success" :loading="contractLoading" @click="saveContractRel">保存合同</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listMaterialCoil, startSpecialSplit, cancelSpecialSplit } from '@/api/wms/coil'
|
import { listMaterialCoil, startSpecialSplit, cancelSpecialSplit, getMaterialCoil, updateMaterialCoilSimple } from '@/api/wms/coil'
|
||||||
import { listPendingAction, startProcess, cancelAction, delPendingAction, addPendingAction } from '@/api/wms/pendingAction'
|
import { listPendingAction, startProcess, cancelAction, delPendingAction, addPendingAction } from '@/api/wms/pendingAction'
|
||||||
|
import { listUser } from "@/api/system/user"
|
||||||
|
import { listCoilContractRel, addCoilContractRel, updateCoilContractRel } from "@/api/wms/coilContractRel"
|
||||||
import { parseTime } from '@/utils/klp'
|
import { parseTime } from '@/utils/klp'
|
||||||
import ProductInfo from '@/components/KLPService/Renderer/ProductInfo'
|
import ProductInfo from '@/components/KLPService/Renderer/ProductInfo'
|
||||||
import RawMaterialInfo from '@/components/KLPService/Renderer/RawMaterialInfo'
|
import RawMaterialInfo from '@/components/KLPService/Renderer/RawMaterialInfo'
|
||||||
|
import WarehouseSelect from "@/components/KLPService/WarehouseSelect"
|
||||||
|
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect"
|
||||||
|
import ProductSelect from "@/components/KLPService/ProductSelect"
|
||||||
|
import RawMaterialSelect from "@/components/KLPService/RawMaterialSelect"
|
||||||
|
import MemoInput from "@/components/MemoInput"
|
||||||
|
import TimeInput from "@/components/TimeInput"
|
||||||
|
import ContractSelect from "@/components/KLPService/ContractSelect"
|
||||||
import CoilCard from '@/components/KLPService/Renderer/CoilCard.vue'
|
import CoilCard from '@/components/KLPService/Renderer/CoilCard.vue'
|
||||||
import LabelRender from './LabelRender/index.vue'
|
import LabelRender from './LabelRender/index.vue'
|
||||||
import StepSplit from './stepSplit.vue'
|
import StepSplit from './stepSplit.vue'
|
||||||
@@ -315,7 +508,7 @@ import { getCoilTagPrintType } from '@/views/wms/coil/js/coilPrint'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DoPage',
|
name: 'DoPage',
|
||||||
dicts: ['action_type', 'coil_abnormal_code', 'coil_abnormal_position', 'coil_abnormal_degree'],
|
dicts: ['action_type', 'coil_abnormal_code', 'coil_abnormal_position', 'coil_abnormal_degree', 'coil_business_purpose'],
|
||||||
props: {
|
props: {
|
||||||
label: {
|
label: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -337,7 +530,14 @@ export default {
|
|||||||
LabelRender,
|
LabelRender,
|
||||||
StepSplit,
|
StepSplit,
|
||||||
ExceptionManager,
|
ExceptionManager,
|
||||||
ScheduleDetailCoilBind
|
ScheduleDetailCoilBind,
|
||||||
|
WarehouseSelect,
|
||||||
|
ActualWarehouseSelect,
|
||||||
|
ProductSelect,
|
||||||
|
RawMaterialSelect,
|
||||||
|
MemoInput,
|
||||||
|
TimeInput,
|
||||||
|
ContractSelect
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -396,6 +596,27 @@ export default {
|
|||||||
title: '详细信息'
|
title: '详细信息'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
enterCoilNo: [
|
||||||
|
{ required: true, message: "入场钢卷号不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
currentCoilNo: [
|
||||||
|
{ required: true, message: "当前钢卷号不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
itemId: [
|
||||||
|
{ required: true, message: "物品ID不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
itemType: [
|
||||||
|
{ required: true, message: "物品类型不能为空", trigger: "change" }
|
||||||
|
],
|
||||||
|
netWeight: [
|
||||||
|
{ required: true, message: "净重不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
grossWeight: [
|
||||||
|
{ required: true, message: "毛重不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
// 待操作列表相关
|
// 待操作列表相关
|
||||||
actionLoading: false,
|
actionLoading: false,
|
||||||
pendingActionList: [],
|
pendingActionList: [],
|
||||||
@@ -410,6 +631,14 @@ export default {
|
|||||||
buttonLoading: false,
|
buttonLoading: false,
|
||||||
exceptionDialogVisible: false,
|
exceptionDialogVisible: false,
|
||||||
currentCoilId: null,
|
currentCoilId: null,
|
||||||
|
// 数据修正相关
|
||||||
|
open: false,
|
||||||
|
title: "",
|
||||||
|
isCheck: false,
|
||||||
|
form: {},
|
||||||
|
userList: [],
|
||||||
|
contractLoading: false,
|
||||||
|
contractRelId: null,
|
||||||
stepSpilt: {
|
stepSpilt: {
|
||||||
list: [],
|
list: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -422,6 +651,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// 动态显示标签
|
||||||
|
getItemLabel() {
|
||||||
|
if (this.form.materialType === '成品') {
|
||||||
|
return '产品类型'
|
||||||
|
} else if (this.form.materialType === '原料') {
|
||||||
|
return '原料类型'
|
||||||
|
}
|
||||||
|
return '物品类型'
|
||||||
|
},
|
||||||
// 获取酸连轧工序的actionType值
|
// 获取酸连轧工序的actionType值
|
||||||
acidRollingActionType() {
|
acidRollingActionType() {
|
||||||
if (!this.dict.type.action_type) return null
|
if (!this.dict.type.action_type) return null
|
||||||
@@ -558,6 +796,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this.getUserList()
|
||||||
// 立即加载物料列表(不依赖字典)
|
// 立即加载物料列表(不依赖字典)
|
||||||
// this.getMaterialCoil()
|
// this.getMaterialCoil()
|
||||||
// 尝试加载待操作列表(如果字典已加载)
|
// 尝试加载待操作列表(如果字典已加载)
|
||||||
@@ -916,6 +1155,172 @@ export default {
|
|||||||
}
|
}
|
||||||
this.exceptionDialogVisible = false
|
this.exceptionDialogVisible = false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ========== 数据修正相关方法 ==========
|
||||||
|
getUserList() {
|
||||||
|
listUser({ pageNum: 1, pageSize: 1000 }).then(res => {
|
||||||
|
this.userList = res.rows || []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
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分钟'
|
||||||
|
},
|
||||||
|
calculateFormProductionDuration() {
|
||||||
|
const { productionStartTime, productionEndTime } = this.form
|
||||||
|
if (productionStartTime && productionEndTime) {
|
||||||
|
const start = new Date(productionStartTime).getTime()
|
||||||
|
const end = new Date(productionEndTime).getTime()
|
||||||
|
if (end < start) {
|
||||||
|
this.form.productionDuration = ''
|
||||||
|
this.form.formattedDuration = ''
|
||||||
|
} else {
|
||||||
|
const durationMs = end - start
|
||||||
|
this.form.productionDuration = Math.round(durationMs / (1000 * 60))
|
||||||
|
this.form.formattedDuration = this.formatDuration(durationMs)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.form.productionDuration = ''
|
||||||
|
this.form.formattedDuration = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleMaterialTypeChange(value) {
|
||||||
|
this.form.itemId = null
|
||||||
|
if (value === '成品') {
|
||||||
|
this.form.itemType = 'product'
|
||||||
|
} else if (value === '原料') {
|
||||||
|
this.form.itemType = 'raw_material'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleCheck(row) {
|
||||||
|
this.isCheck = true
|
||||||
|
this.reset()
|
||||||
|
const coilId = row.coilId
|
||||||
|
getMaterialCoil(coilId).then(response => {
|
||||||
|
this.form = response.data
|
||||||
|
if (!this.form.materialType) {
|
||||||
|
if (this.form.itemType) {
|
||||||
|
if (this.form.itemType === 'product') {
|
||||||
|
this.form.materialType = '成品'
|
||||||
|
} else if (this.form.itemType === 'raw_material') {
|
||||||
|
this.form.materialType = '原料'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.form.productionDuration) {
|
||||||
|
this.form.formattedDuration = this.formatDuration(this.form.productionDuration * 60 * 1000)
|
||||||
|
}
|
||||||
|
this.loadContractRel(coilId)
|
||||||
|
this.open = true
|
||||||
|
this.title = "修改钢卷物料"
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadContractRel(coilId) {
|
||||||
|
if (!coilId) {
|
||||||
|
this.contractRelId = null
|
||||||
|
this.$set(this.form, 'contractId', null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
listCoilContractRel({ coilId }).then(res => {
|
||||||
|
const rows = res.rows || []
|
||||||
|
if (rows.length > 0) {
|
||||||
|
this.contractRelId = rows[0].relId
|
||||||
|
this.$set(this.form, 'contractId', rows[0].contractId)
|
||||||
|
} else {
|
||||||
|
this.contractRelId = null
|
||||||
|
this.$set(this.form, 'contractId', null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveContractRel() {
|
||||||
|
this.contractLoading = true
|
||||||
|
listCoilContractRel({ coilId: this.form.coilId }).then(res => {
|
||||||
|
const rows = res.rows || []
|
||||||
|
if (rows.length > 0) {
|
||||||
|
return updateCoilContractRel({
|
||||||
|
relId: rows[0].relId,
|
||||||
|
coilId: this.form.coilId,
|
||||||
|
contractId: this.form.contractId
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return addCoilContractRel({
|
||||||
|
coilId: this.form.coilId,
|
||||||
|
contractId: this.form.contractId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
this.$message.success('合同绑定保存成功')
|
||||||
|
}).finally(() => {
|
||||||
|
this.contractLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
qualityStatus: undefined,
|
||||||
|
actualLength: undefined,
|
||||||
|
actualWidth: undefined,
|
||||||
|
actualThickness: undefined,
|
||||||
|
scheduleThickness: undefined,
|
||||||
|
productionStartTime: undefined,
|
||||||
|
productionEndTime: undefined,
|
||||||
|
productionDuration: undefined,
|
||||||
|
formattedDuration: undefined,
|
||||||
|
contractId: undefined,
|
||||||
|
}
|
||||||
|
this.resetForm("form")
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
this.open = false
|
||||||
|
this.reset()
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
this.$refs["form"].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
const { status, exclusiveStatus, dataType, ...payload } = {
|
||||||
|
...this.form,
|
||||||
|
}
|
||||||
|
updateMaterialCoilSimple(payload).then(_ => {
|
||||||
|
this.$modal.msgSuccess("修改成功")
|
||||||
|
this.open = false
|
||||||
|
this.getMaterialCoil()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-loading="loading" class="flow-content">
|
<div v-loading="loading" class="flow-content">
|
||||||
<div ref="diagram" class="flow-diagram" v-html="currentSvg" @click="onNodeClick"></div>
|
<div ref="diagram" class="flow-diagram" v-html="currentSvg"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { renderMermaidSVG, THEMES } from 'beautiful-mermaid'
|
import { renderMermaidSVG, THEMES } from 'beautiful-mermaid'
|
||||||
|
|
||||||
const theme = {
|
const MERMAID_THEME = {
|
||||||
...THEMES['zinc-light'],
|
...THEMES['zinc-light'],
|
||||||
padding: 24,
|
padding: 24,
|
||||||
nodeSpacing: 28,
|
nodeSpacing: 28,
|
||||||
@@ -43,35 +43,75 @@ const theme = {
|
|||||||
font: 'Inter, "Microsoft YaHei", sans-serif',
|
font: 'Inter, "Microsoft YaHei", sans-serif',
|
||||||
}
|
}
|
||||||
|
|
||||||
const diagrams = {
|
const TAB_LIST = [
|
||||||
afterSales: `
|
{ key: 'steelFullChain', label: '生产全链路流程', icon: 'el-icon-s-operation' },
|
||||||
graph TD
|
{ key: 'afterSales', label: '售后处理流程', icon: 'el-icon-s-claim' },
|
||||||
A["<b>创建售后单</b><br/>填写基本信息<br/>选择需售后处理的钢卷"]:::step
|
{ key: 'inventoryCheck', label: '盘库流程', icon: 'el-icon-s-check' },
|
||||||
A --> B["<b>多部门并行处理</b>"]:::fork
|
{ key: 'productionSchedule', label: '排产流程', icon: 'el-icon-s-order' },
|
||||||
B --> C["<b>生产部</b><br/>出具处理意见"]:::dept1
|
{ key: 'equipmentRepair', label: '设备维修流程', icon: 'el-icon-s-tools' },
|
||||||
B --> D["<b>质量部</b><br/>出具处理意见"]:::dept2
|
]
|
||||||
B --> E["<b>销售部</b><br/>出具处理意见"]:::dept3
|
|
||||||
C --> F{"三个部门<br/>全部提交?"}:::decision
|
|
||||||
D --> F
|
|
||||||
E --> F
|
|
||||||
F -->|已全部提交| G["<b>售后负责人</b><br/>汇总各部门意见<br/>形成最终处理方案<br/>指定执行部门"]:::approve
|
|
||||||
G --> H["<b>部门执行</b><br/>执行处理方案"]:::execute
|
|
||||||
H --> I["<b>返回执行结果</b><br/>提交执行报告"]:::result
|
|
||||||
I --> J(["<b>售后单封存</b><br/>流程结束"]):::end
|
|
||||||
|
|
||||||
classDef step fill:#409eff,stroke:#337ecc,color:#fff,stroke-width:2px
|
const NODE_EVENT_CONFIG = {
|
||||||
classDef fork fill:#f0f5ff,stroke:#409eff,color:#303133,stroke-width:2px
|
steelFullChain: [
|
||||||
classDef dept1 fill:#e6fffa,stroke:#00b4a0,color:#303133,stroke-width:2px
|
{ id: 'A', label: '销售部创建合同', handler: 'handleClick', params: { action: 'openContract' } },
|
||||||
classDef dept2 fill:#fff7e6,stroke:#fa8c16,color:#303133,stroke-width:2px
|
{ id: 'B', label: '原料卷到货', handler: 'handleClick', params: { action: 'openRawMaterial' } },
|
||||||
classDef dept3 fill:#fff0f6,stroke:#eb2f96,color:#303133,stroke-width:2px
|
{ id: 'C', label: '入库验收', handler: 'handleClick', params: { action: 'openWarehouse' } },
|
||||||
classDef decision fill:#f9f0ff,stroke:#722ed1,color:#303133,stroke-width:2px
|
{ id: 'D', label: '登记原料库存', handler: 'handleClick', params: { action: 'openWarehouse' } },
|
||||||
classDef approve fill:#e6f7ff,stroke:#1890ff,color:#303133,stroke-width:2px
|
{ id: 'E', label: '成品钢卷加工', handler: 'handleClick', params: { action: 'openProduction' } },
|
||||||
classDef execute fill:#fffbe6,stroke:#fadb14,color:#303133,stroke-width:2px
|
{ id: 'I', label: '登记质量缺陷', handler: 'handleClick', params: { action: 'openQuality' } },
|
||||||
classDef result fill:#f6ffed,stroke:#52c41a,color:#303133,stroke-width:2px
|
{ id: 'J', label: '质量等级判定', handler: 'handleClick', params: { action: 'openQuality' } },
|
||||||
classDef end fill:#dcf7e8,stroke:#52c41a,color:#303133,stroke-width:2px,rx:10,ry:10
|
{ id: 'K', label: '单卷档案', handler: 'handleClick', params: { action: 'openArchive' } },
|
||||||
linkStyle default stroke:#bfbfbf,stroke-width:2px
|
{ id: 'L', label: '编排发货计划', handler: 'handleClick', params: { action: 'openShipping' } },
|
||||||
`,
|
{ id: 'M', label: '生成发货单', handler: 'handleClick', params: { action: 'openShipping' } },
|
||||||
|
{ id: 'N', label: '发货质量校验', handler: 'handleClick', params: { action: 'openQuality' } },
|
||||||
|
{ id: 'O', label: '出库发货', handler: 'handleClick', params: { action: 'openShipping' } },
|
||||||
|
{ id: 'P', label: '禁止出库', handler: 'handleClick', params: { action: 'openShipping' } },
|
||||||
|
{ id: 'Q', label: '钢卷库存管理', handler: 'handleClick', params: { action: 'openInventory' } },
|
||||||
|
{ id: 'V', label: '生产过程数据异常检测', handler: 'handleClick', params: { action: 'openAlert' } },
|
||||||
|
{ id: 'W', label: '自动触发告警', handler: 'handleClick', params: { action: 'openAlert' } },
|
||||||
|
{ id: 'X', label: '生产数据报表统计', handler: 'handleClick', params: { action: 'openReport' } },
|
||||||
|
],
|
||||||
|
afterSales: [
|
||||||
|
{ id: 'A', label: '创建售后单', handler: 'handleClick', params: { action: 'openAfterSalesCreate' } },
|
||||||
|
{ id: 'C', label: '生产部出具处理意见', handler: 'handleClick', params: { action: 'openAfterSalesDept', dept: 'production' } },
|
||||||
|
{ id: 'D', label: '质量部出具处理意见', handler: 'handleClick', params: { action: 'openAfterSalesDept', dept: 'quality' } },
|
||||||
|
{ id: 'E', label: '销售部出具处理意见', handler: 'handleClick', params: { action: 'openAfterSalesDept', dept: 'sales' } },
|
||||||
|
{ id: 'G', label: '售后负责人汇总', handler: 'handleClick', params: { action: 'openAfterSalesSummary' } },
|
||||||
|
],
|
||||||
|
inventoryCheck: [
|
||||||
|
{ id: 'A', label: '创建盘库计划', handler: 'handleClick', params: { action: 'openInventoryPlan' } },
|
||||||
|
{ id: 'B', label: '创建计划明细', handler: 'handleClick', params: { action: 'openInventoryPlanDetail' } },
|
||||||
|
{ id: 'C', label: '选择库区', handler: 'handleClick', params: { action: 'openInventoryPlanDetail' } },
|
||||||
|
{ id: 'D', label: '提交审批', handler: 'handleClick', params: { action: 'submitApproval' } },
|
||||||
|
{ id: 'G', label: '生成系统库存快照', handler: 'handleClick', params: { action: 'openInventorySnapshot' } },
|
||||||
|
{ id: 'H', label: '上传实盘库存Excel', handler: 'handleClick', params: { action: 'openInventoryUpload' } },
|
||||||
|
{ id: 'I', label: '执行对比', handler: 'handleClick', params: { action: 'openInventoryCompare' } },
|
||||||
|
{ id: 'J', label: '查看差异明细', handler: 'handleClick', params: { action: 'openInventoryDiff' } },
|
||||||
|
{ id: 'K', label: '再次提交审批', handler: 'handleClick', params: { action: 'submitApproval' } },
|
||||||
|
{ id: 'N', label: '执行处理差异', handler: 'handleClick', params: { action: 'openInventoryExecute' } },
|
||||||
|
],
|
||||||
|
productionSchedule: [
|
||||||
|
{ id: 'A', label: '创建需求单', handler: 'handleClick', params: { action: 'openScheduleCreate' } },
|
||||||
|
{ id: 'B', label: '选择合同', handler: 'handleClick', params: { action: 'openScheduleContract' } },
|
||||||
|
{ id: 'C', label: '自动获取需求明细', handler: 'handleClick', params: { action: 'openScheduleDetail' } },
|
||||||
|
{ id: 'D', label: '调整需求明细', handler: 'handleClick', params: { action: 'openScheduleDetail' } },
|
||||||
|
{ id: 'E', label: '提交审批', handler: 'handleClick', params: { action: 'submitApproval' } },
|
||||||
|
{ id: 'H', label: '转化为排产单', handler: 'handleClick', params: { action: 'openScheduleConvert' } },
|
||||||
|
{ id: 'I', label: '排产单', handler: 'handleClick', params: { action: 'openScheduleEdit' } },
|
||||||
|
{ id: 'J', label: '再次提交审批', handler: 'handleClick', params: { action: 'submitApproval' } },
|
||||||
|
{ id: 'M', label: '提交给车间', handler: 'handleClick', params: { action: 'submitApproval' } },
|
||||||
|
{ id: 'N', label: '车间绑定钢卷', handler: 'handleClick', params: { action: 'openScheduleBind' } },
|
||||||
|
{ id: 'O', label: '执行生产', handler: 'handleClick', params: { action: 'openScheduleExecute' } },
|
||||||
|
],
|
||||||
|
equipmentRepair: [
|
||||||
|
{ id: 'A', label: '创建维修计划', handler: 'handleClick', params: { action: 'openRepairCreate' } },
|
||||||
|
{ id: 'A1', label: '点选异常巡检记录', handler: 'handleClick', params: { action: 'openRepairSelect' } },
|
||||||
|
{ id: 'B', label: '提交审批', handler: 'handleClick', params: { action: 'submitApproval' } },
|
||||||
|
{ id: 'E', label: '逐设备维修记录', handler: 'handleClick', params: { action: 'openRepairExecute' } },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIAGRAMS = {
|
||||||
steelFullChain: `
|
steelFullChain: `
|
||||||
graph TD
|
graph TD
|
||||||
A["<b>销售部创建合同</b><br/>录入产品所需内容<br/>规格/数量/技术标准"]:::s1
|
A["<b>销售部创建合同</b><br/>录入产品所需内容<br/>规格/数量/技术标准"]:::s1
|
||||||
@@ -121,38 +161,66 @@ graph TD
|
|||||||
linkStyle default stroke:#bfbfbf,stroke-width:2px
|
linkStyle default stroke:#bfbfbf,stroke-width:2px
|
||||||
`,
|
`,
|
||||||
|
|
||||||
|
afterSales: `
|
||||||
|
graph TD
|
||||||
|
A["<b>创建售后单</b><br/>填写基本信息<br/>选择需售后处理的钢卷"]:::step
|
||||||
|
A --> B["<b>多部门并行处理</b>"]:::fork
|
||||||
|
B --> C["<b>生产部</b><br/>出具处理意见"]:::dept1
|
||||||
|
B --> D["<b>质量部</b><br/>出具处理意见"]:::dept2
|
||||||
|
B --> E["<b>销售部</b><br/>出具处理意见"]:::dept3
|
||||||
|
C --> F{"三个部门<br/>全部提交?"}:::decision
|
||||||
|
D --> F
|
||||||
|
E --> F
|
||||||
|
F -->|已全部提交| G["<b>售后负责人</b><br/>汇总各部门意见<br/>直接办结归档"]:::approve
|
||||||
|
G --> J(["<b>售后单封存</b><br/>流程结束"]):::end
|
||||||
|
|
||||||
|
classDef step fill:#409eff,stroke:#337ecc,color:#fff,stroke-width:2px
|
||||||
|
classDef fork fill:#f0f5ff,stroke:#409eff,color:#303133,stroke-width:2px
|
||||||
|
classDef dept1 fill:#e6fffa,stroke:#00b4a0,color:#303133,stroke-width:2px
|
||||||
|
classDef dept2 fill:#fff7e6,stroke:#fa8c16,color:#303133,stroke-width:2px
|
||||||
|
classDef dept3 fill:#fff0f6,stroke:#eb2f96,color:#303133,stroke-width:2px
|
||||||
|
classDef decision fill:#f9f0ff,stroke:#722ed1,color:#303133,stroke-width:2px
|
||||||
|
classDef approve fill:#e6f7ff,stroke:#1890ff,color:#303133,stroke-width:2px
|
||||||
|
classDef end fill:#dcf7e8,stroke:#52c41a,color:#303133,stroke-width:2px,rx:10,ry:10
|
||||||
|
linkStyle default stroke:#bfbfbf,stroke-width:2px
|
||||||
|
`,
|
||||||
|
|
||||||
inventoryCheck: `
|
inventoryCheck: `
|
||||||
graph TD
|
graph TD
|
||||||
A["<b>创建盘库计划</b><br/>填写基本信息"]:::c1
|
A["<b>创建盘库计划</b><br/>填写基本信息"]:::c1
|
||||||
|
|
||||||
A --> B["<b>创建计划明细</b><br/>可创建多个明细"]:::c2
|
A --> B["<b>创建计划明细</b><br/>可创建多个明细"]:::c2
|
||||||
|
|
||||||
B --> C["<b>选择库区</b><br/>逻辑库 / 实际库<br/>至少选一个"]:::c3
|
B --> C["<b>选择库区</b><br/>逻辑库 / 实际库<br/>至少选一个"]:::c3
|
||||||
C --> D["<b>生成系统库存快照</b>"]:::c4
|
C --> D["<b>提交审批</b>"]:::c4
|
||||||
D --> E["<b>上传实盘库存Excel</b>"]:::c5
|
|
||||||
E --> F["<b>执行对比</b><br/>快照 vs 实盘<br/>自动计算差异"]:::c6
|
|
||||||
|
|
||||||
F --> G["<b>查看差异明细</b><br/>保存差异并填写处理方式"]:::c7
|
D --> E{"审批"}:::dec
|
||||||
G --> H["<b>提交送审</b>"]:::c8
|
E -->|不通过| F["<b>退回修改</b>"]:::c5
|
||||||
|
F --> B
|
||||||
|
E -->|通过| G["<b>生成系统库存快照</b>"]:::c6
|
||||||
|
|
||||||
H --> I{"审批"}:::dec
|
G --> H["<b>上传实盘库存Excel</b>"]:::c7
|
||||||
I -->|不通过| J["<b>退回修改</b>"]:::c7
|
H --> I["<b>执行对比</b><br/>快照 vs 实盘<br/>自动计算差异"]:::c8
|
||||||
J --> G
|
I --> J["<b>查看差异明细</b><br/>保存差异并填写处理方式"]:::c9
|
||||||
I -->|通过| K["<b>开始处理差异</b><br/>逐项执行处理方式"]:::c9
|
J --> K["<b>再次提交审批</b>"]:::c10
|
||||||
|
|
||||||
K --> L{"所有差异<br/>处理完成?"}:::dec
|
K --> L{"审批"}:::dec
|
||||||
L -->|否| K
|
L -->|不通过| M["<b>退回修改</b>"]:::c11
|
||||||
L -->|是| M(["<b>完结流程</b><br/>盘库结束"]):::cend
|
M --> J
|
||||||
|
L -->|通过| N["<b>执行处理差异</b><br/>逐项执行并填写执行结果"]:::c12
|
||||||
|
|
||||||
|
N --> O(["<b>归档办结</b><br/>盘库结束"]):::cend
|
||||||
|
|
||||||
classDef c1 fill:#409eff,stroke:#337ecc,color:#fff,stroke-width:2px
|
classDef c1 fill:#409eff,stroke:#337ecc,color:#fff,stroke-width:2px
|
||||||
classDef c2 fill:#e6fffa,stroke:#13c2c2,color:#303133,stroke-width:2px
|
classDef c2 fill:#e6fffa,stroke:#13c2c2,color:#303133,stroke-width:2px
|
||||||
classDef c3 fill:#fff7e6,stroke:#fa8c16,color:#303133,stroke-width:2px
|
classDef c3 fill:#fff7e6,stroke:#fa8c16,color:#303133,stroke-width:2px
|
||||||
classDef c4 fill:#f0f5ff,stroke:#597ef7,color:#303133,stroke-width:2px
|
classDef c4 fill:#f0f5ff,stroke:#597ef7,color:#303133,stroke-width:2px
|
||||||
classDef c5 fill:#e6f7ff,stroke:#1890ff,color:#303133,stroke-width:2px
|
classDef c5 fill:#fff1f0,stroke:#f5222d,color:#303133,stroke-width:2px
|
||||||
classDef c6 fill:#fffbe6,stroke:#fadb14,color:#606266,stroke-width:2px
|
classDef c6 fill:#e6f7ff,stroke:#1890ff,color:#303133,stroke-width:2px
|
||||||
classDef c7 fill:#f0f5ff,stroke:#597ef7,color:#303133,stroke-width:2px
|
classDef c7 fill:#f0f5ff,stroke:#597ef7,color:#303133,stroke-width:2px
|
||||||
classDef c8 fill:#fff0f6,stroke:#eb2f96,color:#303133,stroke-width:2px
|
classDef c8 fill:#fffbe6,stroke:#fadb14,color:#606266,stroke-width:2px
|
||||||
classDef c9 fill:#f6ffed,stroke:#52c41a,color:#303133,stroke-width:2px
|
classDef c9 fill:#fff0f6,stroke:#eb2f96,color:#303133,stroke-width:2px
|
||||||
|
classDef c10 fill:#f0f5ff,stroke:#597ef7,color:#303133,stroke-width:2px
|
||||||
|
classDef c11 fill:#fff1f0,stroke:#f5222d,color:#303133,stroke-width:2px
|
||||||
|
classDef c12 fill:#f6ffed,stroke:#52c41a,color:#303133,stroke-width:2px
|
||||||
classDef dec fill:#f9f0ff,stroke:#722ed1,color:#303133,stroke-width:2px
|
classDef dec fill:#f9f0ff,stroke:#722ed1,color:#303133,stroke-width:2px
|
||||||
classDef cend fill:#dcf7e8,stroke:#52c41a,color:#303133,stroke-width:2px,rx:10,ry:10
|
classDef cend fill:#dcf7e8,stroke:#52c41a,color:#303133,stroke-width:2px,rx:10,ry:10
|
||||||
linkStyle default stroke:#bfbfbf,stroke-width:2px
|
linkStyle default stroke:#bfbfbf,stroke-width:2px
|
||||||
@@ -226,22 +294,42 @@ graph TD
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FlowChart',
|
name: 'FlowChart',
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
activeTab: 'steelFullChain',
|
activeTab: 'steelFullChain',
|
||||||
tabs: [
|
tabs: TAB_LIST,
|
||||||
{ key: 'steelFullChain', label: '生产全链路流程', icon: 'el-icon-s-operation' },
|
|
||||||
{ key: 'afterSales', label: '售后处理流程', icon: 'el-icon-s-claim' },
|
|
||||||
{ key: 'inventoryCheck', label: '盘库流程', icon: 'el-icon-s-check' },
|
|
||||||
{ key: 'productionSchedule', label: '排产流程', icon: 'el-icon-s-order' },
|
|
||||||
{ key: 'equipmentRepair', label: '设备维修流程', icon: 'el-icon-s-tools' },
|
|
||||||
],
|
|
||||||
svgCache: {},
|
svgCache: {},
|
||||||
selectedNode: null,
|
selectedNode: null,
|
||||||
downloadLoading: false,
|
downloadLoading: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
currentSvg() {
|
||||||
|
const code = DIAGRAMS[this.activeTab]
|
||||||
|
if (!code) return ''
|
||||||
|
|
||||||
|
if (!this.svgCache[this.activeTab]) {
|
||||||
|
try {
|
||||||
|
this.svgCache[this.activeTab] = renderMermaidSVG(code, MERMAID_THEME)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Mermaid render error:', e)
|
||||||
|
return '<p style="color:#f5222d;text-align:center;">流程图渲染失败: ' + e.message + '</p>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.svgCache[this.activeTab]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.bindNodeEvents()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
currentSvg() {
|
currentSvg() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@@ -249,22 +337,41 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
currentSvg() {
|
|
||||||
const code = diagrams[this.activeTab]
|
|
||||||
if (!code) return ''
|
|
||||||
if (!this.svgCache[this.activeTab]) {
|
|
||||||
try {
|
|
||||||
this.svgCache[this.activeTab] = renderMermaidSVG(code, theme)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Mermaid render error:', e)
|
|
||||||
return '<p style="color:#f5222d;text-align:center;">流程图渲染失败: ' + e.message + '</p>'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.svgCache[this.activeTab]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
|
switchTab(key) {
|
||||||
|
this.activeTab = key
|
||||||
|
this.selectedNode = null
|
||||||
|
},
|
||||||
|
|
||||||
|
bindNodeEvents() {
|
||||||
|
const el = this.$refs.diagram
|
||||||
|
if (!el) return
|
||||||
|
|
||||||
|
const svg = el.querySelector('svg')
|
||||||
|
if (!svg) return
|
||||||
|
|
||||||
|
const nodeConfigs = NODE_EVENT_CONFIG[this.activeTab] || []
|
||||||
|
nodeConfigs.forEach(config => {
|
||||||
|
const node = svg.querySelector(`g[data-id="${config.id}"]`)
|
||||||
|
if (!node) return
|
||||||
|
|
||||||
|
node.style.cursor = 'pointer'
|
||||||
|
node.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
const textEl = node.querySelector('text, span')
|
||||||
|
const label = textEl ? textEl.textContent.replace(/\s+/g, ' ').trim() : config.label
|
||||||
|
this.selectedNode = { dataId: config.id, label, handler: config.handler, params: config.params }
|
||||||
|
|
||||||
|
if (typeof this[config.handler] === 'function') {
|
||||||
|
this[config.handler]({ id: config.id, label, node, params: config.params })
|
||||||
|
} else {
|
||||||
|
console.warn(`[FlowChart] Handler "${config.handler}" not found`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
handleDownload(format) {
|
handleDownload(format) {
|
||||||
if (format === 'svg') {
|
if (format === 'svg') {
|
||||||
this.downloadSvg()
|
this.downloadSvg()
|
||||||
@@ -272,25 +379,30 @@ export default {
|
|||||||
this.downloadPng()
|
this.downloadPng()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadSvg() {
|
downloadSvg() {
|
||||||
const svgEl = this.$refs.diagram?.querySelector('svg')
|
const svgEl = this.$refs.diagram?.querySelector('svg')
|
||||||
if (!svgEl) {
|
if (!svgEl) {
|
||||||
this.$message.warning('流程图尚未渲染完成')
|
this.$message.warning('流程图尚未渲染完成')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const clone = svgEl.cloneNode(true)
|
const clone = svgEl.cloneNode(true)
|
||||||
const serializer = new XMLSerializer()
|
const serializer = new XMLSerializer()
|
||||||
const source = serializer.serializeToString(clone)
|
const source = serializer.serializeToString(clone)
|
||||||
const blob = new Blob([source], { type: 'image/svg+xml;charset=utf-8' })
|
const blob = new Blob([source], { type: 'image/svg+xml;charset=utf-8' })
|
||||||
|
|
||||||
this.triggerDownload(URL.createObjectURL(blob), `${this.activeTab}.svg`)
|
this.triggerDownload(URL.createObjectURL(blob), `${this.activeTab}.svg`)
|
||||||
this.$message.success('SVG 已下载')
|
this.$message.success('SVG 已下载')
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadPng() {
|
downloadPng() {
|
||||||
const svgEl = this.$refs.diagram?.querySelector('svg')
|
const svgEl = this.$refs.diagram?.querySelector('svg')
|
||||||
if (!svgEl) {
|
if (!svgEl) {
|
||||||
this.$message.warning('流程图尚未渲染完成')
|
this.$message.warning('流程图尚未渲染完成')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.downloadLoading = true
|
this.downloadLoading = true
|
||||||
|
|
||||||
const clone = svgEl.cloneNode(true)
|
const clone = svgEl.cloneNode(true)
|
||||||
@@ -308,10 +420,13 @@ export default {
|
|||||||
const scale = 2
|
const scale = 2
|
||||||
canvas.width = rect.width * scale
|
canvas.width = rect.width * scale
|
||||||
canvas.height = rect.height * scale
|
canvas.height = rect.height * scale
|
||||||
|
|
||||||
const ctx = canvas.getContext('2d')
|
const ctx = canvas.getContext('2d')
|
||||||
ctx.scale(scale, scale)
|
ctx.scale(scale, scale)
|
||||||
ctx.drawImage(img, 0, 0, rect.width, rect.height)
|
ctx.drawImage(img, 0, 0, rect.width, rect.height)
|
||||||
|
|
||||||
URL.revokeObjectURL(url)
|
URL.revokeObjectURL(url)
|
||||||
|
|
||||||
canvas.toBlob(blob => {
|
canvas.toBlob(blob => {
|
||||||
this.downloadLoading = false
|
this.downloadLoading = false
|
||||||
if (blob) {
|
if (blob) {
|
||||||
@@ -320,12 +435,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}, 'image/png')
|
}, 'image/png')
|
||||||
}
|
}
|
||||||
|
|
||||||
img.onerror = () => {
|
img.onerror = () => {
|
||||||
this.downloadLoading = false
|
this.downloadLoading = false
|
||||||
this.$message.error('PNG 导出失败')
|
this.$message.error('PNG 导出失败')
|
||||||
}
|
}
|
||||||
|
|
||||||
img.src = url
|
img.src = url
|
||||||
},
|
},
|
||||||
|
|
||||||
triggerDownload(url, filename) {
|
triggerDownload(url, filename) {
|
||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = url
|
a.href = url
|
||||||
@@ -335,40 +453,10 @@ export default {
|
|||||||
document.body.removeChild(a)
|
document.body.removeChild(a)
|
||||||
URL.revokeObjectURL(url)
|
URL.revokeObjectURL(url)
|
||||||
},
|
},
|
||||||
switchTab(key) {
|
|
||||||
this.activeTab = key
|
handleClick({ id, label, params }) {
|
||||||
this.selectedNode = null
|
console.log('[FlowChart] Node clicked:', { id, label, params })
|
||||||
},
|
this.$message({ message: `${label}`, type: 'info', duration: 1500 })
|
||||||
bindNodeEvents() {
|
|
||||||
const el = this.$refs.diagram
|
|
||||||
if (!el) return
|
|
||||||
const svg = el.querySelector('svg')
|
|
||||||
if (!svg) return
|
|
||||||
const groups = svg.querySelectorAll('g:not([class*="edge"])')
|
|
||||||
groups.forEach(g => {
|
|
||||||
const hasRect = g.querySelector('rect, path, ellipse, polygon')
|
|
||||||
const text = g.querySelector('text, span')
|
|
||||||
if (hasRect && text) {
|
|
||||||
g.style.cursor = 'pointer'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onNodeClick(e) {
|
|
||||||
let target = e.target
|
|
||||||
while (target && target !== e.currentTarget) {
|
|
||||||
if (target.tagName === 'g') {
|
|
||||||
const textEl = target.querySelector('text, span')
|
|
||||||
if (textEl) {
|
|
||||||
const label = textEl.textContent.replace(/\s+/g, ' ').trim()
|
|
||||||
if (label) {
|
|
||||||
this.selectedNode = label
|
|
||||||
this.$message({ message: label, type: 'info', duration: 1500 })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target = target.parentElement
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div v-loading="loading" class="export-preview">
|
<div v-loading="loading" class="export-preview">
|
||||||
<div ref="pdfContent" class="pdf-content">
|
<div ref="pdfContent" class="pdf-content">
|
||||||
<!-- ========== 附件一:质量异议反馈单 ========== -->
|
<!-- ========== 附件一:质量异议反馈单 ========== -->
|
||||||
<div class="attachment-section">
|
<div ref="feedbackFormSection" class="attachment-section">
|
||||||
<h2 class="doc-title">质量异议反馈单</h2>
|
<h2 class="doc-title">质量异议反馈单</h2>
|
||||||
|
|
||||||
<table class="form-table">
|
<table class="form-table">
|
||||||
@@ -24,19 +24,19 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="label" style="width:60px;vertical-align:middle" :rowspan="8">情况概述<br>(用户提供)</td>
|
<td class="label" style="width:60px;vertical-align:middle" :rowspan="8">情况概述<br>(用户提供)</td>
|
||||||
<td class="label" style="width:120px">产品名称</td>
|
<td class="label" style="width:120px">产品名称</td>
|
||||||
<td style="width:180px">{{ firstCoil.itemName || '' }}</td>
|
<td style="width:180px">{{ allItemNames }}</td>
|
||||||
<td class="label" style="width:110px">异议合同号</td>
|
<td class="label" style="width:110px">异议合同号</td>
|
||||||
<td>{{ firstContract.contractCode || '' }}</td>
|
<td>{{ allContractCodes }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">合同交货量</td>
|
<td class="label">合同交货量</td>
|
||||||
<td>{{ firstContract.orderAmount || '' }}</td>
|
<td>{{ allOrderAmounts }}</td>
|
||||||
<td class="label">牌号/钢种</td>
|
<td class="label">牌号/钢种</td>
|
||||||
<td>{{ firstCoil.material || '' }}</td>
|
<td>{{ allMaterials }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">规格</td>
|
<td class="label">规格</td>
|
||||||
<td>{{ firstCoil.specification || '' }}</td>
|
<td>{{ allSpecifications }}</td>
|
||||||
<td class="label">异议量</td>
|
<td class="label">异议量</td>
|
||||||
<td>{{ coilWeightSummary }}</td>
|
<td>{{ coilWeightSummary }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<td class="label">采购日期</td>
|
<td class="label">采购日期</td>
|
||||||
<td>{{ detail.complaintDate | formatDate }}</td>
|
<td>{{ detail.complaintDate | formatDate }}</td>
|
||||||
<td class="label">使用日期</td>
|
<td class="label">使用日期</td>
|
||||||
<td>{{ firstCoil.exportTime | formatDate }}</td>
|
<td>{{ allExportTimes }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">产品卷号</td>
|
<td class="label">产品卷号</td>
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">产品生产日期</td>
|
<td class="label">产品生产日期</td>
|
||||||
<td colspan="3">{{ firstCoil.createTime | formatDate }}</td>
|
<td colspan="3">{{ allCreateTimes }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">下游使用用户名称</td>
|
<td class="label">下游使用用户名称</td>
|
||||||
@@ -95,7 +95,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ========== 附件二:质量投诉立案、确认及处置单 ========== -->
|
<!-- ========== 附件二 + 附件三 ========== -->
|
||||||
|
<div ref="restContentSection">
|
||||||
<div class="page-break"></div>
|
<div class="page-break"></div>
|
||||||
<div class="attachment-section">
|
<div class="attachment-section">
|
||||||
<h2 class="doc-title">附件 2</h2>
|
<h2 class="doc-title">附件 2</h2>
|
||||||
@@ -126,21 +127,21 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">产品名称</td>
|
<td class="label">产品名称</td>
|
||||||
<td>{{ firstCoil.itemName || '' }}</td>
|
<td>{{ allItemNames }}</td>
|
||||||
<td class="label">合同号</td>
|
<td class="label">合同号</td>
|
||||||
<td>{{ firstContract.contractCode || '' }}</td>
|
<td>{{ allContractCodes }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">合同交货量</td>
|
<td class="label">合同交货量</td>
|
||||||
<td>{{ firstContract.orderAmount || '' }}</td>
|
<td>{{ allOrderAmounts }}</td>
|
||||||
<td class="label">未出库量</td>
|
<td class="label">未出库量</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">规格(mm)</td>
|
<td class="label">规格(mm)</td>
|
||||||
<td>{{ firstCoil.specification || '' }}</td>
|
<td>{{ allSpecifications }}</td>
|
||||||
<td class="label">产品钢种/牌号</td>
|
<td class="label">产品钢种/牌号</td>
|
||||||
<td>{{ firstCoil.material || '' }}</td>
|
<td>{{ allMaterials }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label">异议量</td>
|
<td class="label">异议量</td>
|
||||||
@@ -266,6 +267,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="visible = false">取 消</el-button>
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
@@ -322,16 +324,42 @@ export default {
|
|||||||
const day = String(d.getDate()).padStart(2, '0');
|
const day = String(d.getDate()).padStart(2, '0');
|
||||||
return `${y}.${m}.${day}`;
|
return `${y}.${m}.${day}`;
|
||||||
},
|
},
|
||||||
firstCoil() {
|
allItemNames() {
|
||||||
if (this.coilList.length === 0) return {};
|
return [...new Set(this.coilList.map(rel => (rel.coilInfo && rel.coilInfo.itemName) || '').filter(Boolean))].join('、');
|
||||||
const rel = this.coilList[0];
|
|
||||||
return rel.coilInfo || {};
|
|
||||||
},
|
},
|
||||||
firstContract() {
|
allMaterials() {
|
||||||
if (this.coilList.length === 0) return {};
|
return [...new Set(this.coilList.map(rel => (rel.coilInfo && rel.coilInfo.material) || '').filter(Boolean))].join('、');
|
||||||
const rel = this.coilList[0];
|
},
|
||||||
|
allSpecifications() {
|
||||||
|
return [...new Set(this.coilList.map(rel => (rel.coilInfo && rel.coilInfo.specification) || '').filter(Boolean))].join('、');
|
||||||
|
},
|
||||||
|
allContractCodes() {
|
||||||
|
const codes = [];
|
||||||
|
this.coilList.forEach(rel => {
|
||||||
const orders = (rel.coilInfo && rel.coilInfo.orderList) || [];
|
const orders = (rel.coilInfo && rel.coilInfo.orderList) || [];
|
||||||
return orders[0] || {};
|
orders.forEach(o => { if (o.contractCode) codes.push(o.contractCode); });
|
||||||
|
});
|
||||||
|
return [...new Set(codes)].join('、');
|
||||||
|
},
|
||||||
|
allOrderAmounts() {
|
||||||
|
const amounts = [];
|
||||||
|
this.coilList.forEach(rel => {
|
||||||
|
const orders = (rel.coilInfo && rel.coilInfo.orderList) || [];
|
||||||
|
orders.forEach(o => { if (o.orderAmount != null) amounts.push(o.orderAmount); });
|
||||||
|
});
|
||||||
|
return [...new Set(amounts)].join('、');
|
||||||
|
},
|
||||||
|
allExportTimes() {
|
||||||
|
return this.coilList
|
||||||
|
.map(rel => this.fmtDate((rel.coilInfo && rel.coilInfo.exportTime) || ''))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('、');
|
||||||
|
},
|
||||||
|
allCreateTimes() {
|
||||||
|
return this.coilList
|
||||||
|
.map(rel => this.fmtDate((rel.coilInfo && rel.coilInfo.createTime) || ''))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('、');
|
||||||
},
|
},
|
||||||
coilNoSummary() {
|
coilNoSummary() {
|
||||||
return this.coilList
|
return this.coilList
|
||||||
@@ -417,43 +445,35 @@ export default {
|
|||||||
this.taskList = [];
|
this.taskList = [];
|
||||||
this.defectImages = [];
|
this.defectImages = [];
|
||||||
},
|
},
|
||||||
|
fmtDate(val) {
|
||||||
|
if (!val) return '';
|
||||||
|
const d = new Date(val);
|
||||||
|
if (isNaN(d.getTime())) return val;
|
||||||
|
const y = d.getFullYear();
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(d.getDate()).padStart(2, '0');
|
||||||
|
return `${y}.${m}.${day}`;
|
||||||
|
},
|
||||||
async exportPdf() {
|
async exportPdf() {
|
||||||
const element = this.$refs.pdfContent;
|
const feedbackEl = this.$refs.feedbackFormSection;
|
||||||
if (!element) return;
|
const restEl = this.$refs.restContentSection;
|
||||||
|
if (!feedbackEl || !restEl) return;
|
||||||
this.exporting = true;
|
this.exporting = true;
|
||||||
try {
|
try {
|
||||||
const canvas = await html2canvas(element, {
|
|
||||||
scale: 2,
|
|
||||||
useCORS: true,
|
|
||||||
logging: false,
|
|
||||||
backgroundColor: '#ffffff',
|
|
||||||
windowWidth: 794
|
|
||||||
});
|
|
||||||
const imgData = canvas.toDataURL('image/png');
|
|
||||||
const pdf = new jsPDF('p', 'mm', 'a4');
|
const pdf = new jsPDF('p', 'mm', 'a4');
|
||||||
const pdfWidth = pdf.internal.pageSize.getWidth();
|
const pageWidth = pdf.internal.pageSize.getWidth(); // 210
|
||||||
const pageHeight = pdf.internal.pageSize.getHeight();
|
const pageHeight = pdf.internal.pageSize.getHeight(); // 297
|
||||||
const imgWidth = pdfWidth;
|
const margin = 10; // mm
|
||||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
const contentWidth = pageWidth - margin * 2; // 190
|
||||||
|
const contentHeight = pageHeight - margin * 2; // 277
|
||||||
|
|
||||||
|
// 附件一:质量异议反馈单 — 独立起始
|
||||||
|
await this._renderSection(pdf, feedbackEl, pageWidth, contentWidth, contentHeight, margin);
|
||||||
|
|
||||||
|
// 附件二 + 附件三 — 从新页开始
|
||||||
|
pdf.addPage();
|
||||||
|
await this._renderSection(pdf, restEl, pageWidth, contentWidth, contentHeight, margin);
|
||||||
|
|
||||||
if (imgHeight <= pageHeight) {
|
|
||||||
pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight);
|
|
||||||
} else {
|
|
||||||
let posY = 0;
|
|
||||||
const ratio = pdfWidth / canvas.width;
|
|
||||||
const pageCanvasHeight = pageHeight / ratio;
|
|
||||||
while (posY < canvas.height) {
|
|
||||||
if (posY > 0) pdf.addPage();
|
|
||||||
const pieceCanvas = document.createElement('canvas');
|
|
||||||
pieceCanvas.width = canvas.width;
|
|
||||||
pieceCanvas.height = Math.min(pageCanvasHeight, canvas.height - posY);
|
|
||||||
const ctx = pieceCanvas.getContext('2d');
|
|
||||||
ctx.drawImage(canvas, 0, posY, canvas.width, pieceCanvas.height, 0, 0, canvas.width, pieceCanvas.height);
|
|
||||||
const pieceData = pieceCanvas.toDataURL('image/png');
|
|
||||||
pdf.addImage(pieceData, 'PNG', 0, 0, imgWidth, pieceCanvas.height * ratio);
|
|
||||||
posY += pageCanvasHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pdf.save(`${this.detail.complaintNo || '售后单'}_质量异议全套资料.pdf`);
|
pdf.save(`${this.detail.complaintNo || '售后单'}_质量异议全套资料.pdf`);
|
||||||
this.$modal.msgSuccess('PDF导出成功');
|
this.$modal.msgSuccess('PDF导出成功');
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
@@ -463,6 +483,38 @@ export default {
|
|||||||
} finally {
|
} finally {
|
||||||
this.exporting = false;
|
this.exporting = false;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async _renderSection(pdf, el, pageWidth, contentWidth, contentHeight, margin) {
|
||||||
|
const canvas = await html2canvas(el, {
|
||||||
|
scale: 2,
|
||||||
|
useCORS: true,
|
||||||
|
logging: false,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
windowWidth: 794
|
||||||
|
});
|
||||||
|
const ratio = contentWidth / canvas.width;
|
||||||
|
const imgFullHeight = canvas.height * ratio;
|
||||||
|
|
||||||
|
if (imgFullHeight <= contentHeight) {
|
||||||
|
// 单页:居中放置
|
||||||
|
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', margin, margin, contentWidth, imgFullHeight);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多页分割
|
||||||
|
let posY = 0;
|
||||||
|
const pageCanvasHeight = contentHeight / ratio;
|
||||||
|
while (posY < canvas.height) {
|
||||||
|
if (posY > 0) pdf.addPage();
|
||||||
|
const pieceHeight = Math.min(pageCanvasHeight, canvas.height - posY);
|
||||||
|
const pieceCanvas = document.createElement('canvas');
|
||||||
|
pieceCanvas.width = canvas.width;
|
||||||
|
pieceCanvas.height = pieceHeight;
|
||||||
|
const ctx = pieceCanvas.getContext('2d');
|
||||||
|
ctx.drawImage(canvas, 0, posY, canvas.width, pieceHeight, 0, 0, canvas.width, pieceHeight);
|
||||||
|
pdf.addImage(pieceCanvas.toDataURL('image/png'), 'PNG', margin, margin, contentWidth, pieceHeight * ratio);
|
||||||
|
posY += pageCanvasHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -472,7 +524,7 @@ export default {
|
|||||||
.export-preview {
|
.export-preview {
|
||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: #f5f5f5;
|
background: #ffffff;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +545,7 @@ export default {
|
|||||||
break-after: page;
|
break-after: page;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
border-top: 1px dashed #ccc;
|
border-top: 1px dashed #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.doc-title {
|
.doc-title {
|
||||||
@@ -530,7 +582,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-table .label {
|
.form-table .label {
|
||||||
background-color: #f5f5f5;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@@ -558,7 +609,7 @@ export default {
|
|||||||
|
|
||||||
.desc-label {
|
.desc-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #333;
|
color: #000;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,7 +632,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.company-header {
|
.company-header {
|
||||||
background-color: #f5f5f5;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -597,7 +647,7 @@ export default {
|
|||||||
|
|
||||||
.defect-item {
|
.defect-item {
|
||||||
flex: 0 0 calc(50% - 6px);
|
flex: 0 0 calc(50% - 6px);
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #000;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,7 +660,7 @@ export default {
|
|||||||
|
|
||||||
.empty-tip {
|
.empty-tip {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #999;
|
color: #000;
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,8 +70,8 @@
|
|||||||
<HeaderControlSection :complaintNo="currentRow.complaintNo" :flowStatus="currentRow.flowStatus"
|
<HeaderControlSection :complaintNo="currentRow.complaintNo" :flowStatus="currentRow.flowStatus"
|
||||||
:meta="currentRow">
|
:meta="currentRow">
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<!-- <el-button :loading="pdfLoading" size="mini" type="text" icon="el-icon-printer" @click="handlePrint"
|
<el-button v-if="currentRow.flowStatus === 4" size="mini" type="text" icon="el-icon-download"
|
||||||
:disabled="pdfLoading" title="导出PDF">导出PDF</el-button> -->
|
@click="handleExportPdf(currentRow)" title="导出PDF">导出</el-button>
|
||||||
<el-button v-if="currentRow.flowStatus === 1" size="mini" type="primary" plain
|
<el-button v-if="currentRow.flowStatus === 1" size="mini" type="primary" plain
|
||||||
icon="el-icon-s-promotion" @click="handleOpinionDispatch">意见下发</el-button>
|
icon="el-icon-s-promotion" @click="handleOpinionDispatch">意见下发</el-button>
|
||||||
<el-button v-if="false" size="mini" type="warning" plain
|
<el-button v-if="false" size="mini" type="warning" plain
|
||||||
|
|||||||
Reference in New Issue
Block a user