feat(mes/qc): add quality certificate management function
- 新增质量证明书主、明细的CRUD接口 - 新增质量证明书列表页、明细编辑页 - 新增打印预览组件和PDF导出打印功能 - 添加配套的静态资源和路由依赖 - 优化路由菜单处理逻辑
This commit is contained in:
1
klp-ui/.gitignore
vendored
1
klp-ui/.gitignore
vendored
@@ -12,6 +12,7 @@ selenium-debug.log
|
|||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
|
.trae
|
||||||
.vscode
|
.vscode
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
|
|||||||
@@ -1,9 +1,29 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 获取路由
|
const addParentPrefix = (menuList, parentName = '') => {
|
||||||
|
return menuList.map(menu => {
|
||||||
|
const newName = parentName ? `${parentName}-${menu.name}` : menu.name
|
||||||
|
const newMenu = { ...menu, name: newName }
|
||||||
|
if (menu.children && menu.children.length > 0) {
|
||||||
|
newMenu.children = addParentPrefix(menu.children, newName)
|
||||||
|
}
|
||||||
|
return newMenu
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const getRouters = () => {
|
export const getRouters = () => {
|
||||||
return request({
|
return request({
|
||||||
url: '/getRouters',
|
url: '/getRouters',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
}).then(res => {
|
||||||
|
if (res && Array.isArray(res.data)) {
|
||||||
|
return {
|
||||||
|
data: addParentPrefix(res.data),
|
||||||
|
code: 200,
|
||||||
|
msg: 'success'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(res, 'getRouters res')
|
||||||
|
return res
|
||||||
|
})
|
||||||
}
|
}
|
||||||
44
klp-ui/src/api/mes/qc/certificate.js
Normal file
44
klp-ui/src/api/mes/qc/certificate.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询质量证明书主列表
|
||||||
|
export function listCertificate(query) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificate/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询质量证明书主详细
|
||||||
|
export function getCertificate(certificateId) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificate/' + certificateId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增质量证明书主
|
||||||
|
export function addCertificate(data) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificate',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改质量证明书主
|
||||||
|
export function updateCertificate(data) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificate',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除质量证明书主
|
||||||
|
export function delCertificate(certificateId) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificate/' + certificateId,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
44
klp-ui/src/api/mes/qc/certificateItem.js
Normal file
44
klp-ui/src/api/mes/qc/certificateItem.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询质量证明书明细列表
|
||||||
|
export function listCertificateItem(query) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificateItem/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询质量证明书明细详细
|
||||||
|
export function getCertificateItem(itemId) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificateItem/' + itemId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增质量证明书明细
|
||||||
|
export function addCertificateItem(data) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificateItem',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改质量证明书明细
|
||||||
|
export function updateCertificateItem(data) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificateItem',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除质量证明书明细
|
||||||
|
export function delCertificateItem(itemId) {
|
||||||
|
return request({
|
||||||
|
url: '/qc/certificateItem/' + itemId,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
BIN
klp-ui/src/assets/images/yanghongyan.png
Normal file
BIN
klp-ui/src/assets/images/yanghongyan.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 464 KiB |
BIN
klp-ui/src/assets/images/zhijian.png
Normal file
BIN
klp-ui/src/assets/images/zhijian.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -1,4 +1,5 @@
|
|||||||
export { default as CheckItemSelect } from './CheckItemSelect/index.vue';
|
export { default as CheckItemSelect } from './CheckItemSelect/index.vue';
|
||||||
|
export { default as SchemeSelect } from './SchemeSelect/index.vue';
|
||||||
export { default as ProductSelect } from './ProductSelect/index.vue';
|
export { default as ProductSelect } from './ProductSelect/index.vue';
|
||||||
export { default as RawMaterialSelect } from './RawMaterialSelect/index.vue';
|
export { default as RawMaterialSelect } from './RawMaterialSelect/index.vue';
|
||||||
export { default as UserSelect } from './UserSelect/index.vue';
|
export { default as UserSelect } from './UserSelect/index.vue';
|
||||||
|
|||||||
657
klp-ui/src/views/mes/qc/certificate/book.vue
Normal file
657
klp-ui/src/views/mes/qc/certificate/book.vue
Normal file
@@ -0,0 +1,657 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
|
<el-form-item label="证明书号" prop="certificateNo">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.certificateNo"
|
||||||
|
placeholder="请输入证明书号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="合同号" prop="contractNo">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.contractNo"
|
||||||
|
placeholder="请输入合同号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品名称" prop="productName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.productName"
|
||||||
|
placeholder="请输入产品名称"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="执行标准" prop="standard">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.standard"
|
||||||
|
placeholder="请输入执行标准"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="收货单位" prop="consignee">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.consignee"
|
||||||
|
placeholder="请输入收货单位"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="生产厂家" prop="manufacturer">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.manufacturer"
|
||||||
|
placeholder="请输入生产厂家"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
icon="el-icon-plus"
|
||||||
|
size="mini"
|
||||||
|
@click="handleAdd"
|
||||||
|
>新增</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
plain
|
||||||
|
icon="el-icon-edit"
|
||||||
|
size="mini"
|
||||||
|
:disabled="single"
|
||||||
|
@click="handleUpdate"
|
||||||
|
>修改</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
plain
|
||||||
|
icon="el-icon-delete"
|
||||||
|
size="mini"
|
||||||
|
:disabled="multiple"
|
||||||
|
@click="handleDelete"
|
||||||
|
>删除</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div v-loading="loading" class="certificate-list">
|
||||||
|
<div
|
||||||
|
v-for="item in certificateList"
|
||||||
|
:key="item.certificateId"
|
||||||
|
class="certificate-card"
|
||||||
|
:class="{ 'certificate-card-selected': selectedIds.includes(item.certificateId) }"
|
||||||
|
@click="handleCardClick(item)"
|
||||||
|
>
|
||||||
|
<div class="card-top-bar"></div>
|
||||||
|
|
||||||
|
<div class="card-title-row">
|
||||||
|
<span class="card-doc-type">质量保证书</span>
|
||||||
|
<span class="card-sep">|</span>
|
||||||
|
<span class="card-env">MES-QC-{{ item.certificateNo || '' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-cert-no">
|
||||||
|
<span class="card-cert-no-label">No.</span>
|
||||||
|
<span class="card-cert-no-value">{{ item.certificateNo }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-info-grid">
|
||||||
|
<div class="card-info-item">
|
||||||
|
<span class="card-info-label">合同号</span>
|
||||||
|
<span class="card-info-value">{{ item.contractNo || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-info-item">
|
||||||
|
<span class="card-info-label">产品名称</span>
|
||||||
|
<span class="card-info-value">{{ item.productName || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-info-item">
|
||||||
|
<span class="card-info-label">执行标准</span>
|
||||||
|
<span class="card-info-value">{{ item.standard || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-info-item">
|
||||||
|
<span class="card-info-label">收货单位</span>
|
||||||
|
<span class="card-info-value">{{ item.consignee || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-info-item">
|
||||||
|
<span class="card-info-label">生产厂家</span>
|
||||||
|
<span class="card-info-value">{{ item.manufacturer || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-info-item">
|
||||||
|
<span class="card-info-label">签发日期</span>
|
||||||
|
<span class="card-info-value">{{ parseTime(item.issueDate, '{y}-{m}-{d}') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer">
|
||||||
|
<div class="card-stamp-placeholder">质检专用</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(item)">修改</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)">删除</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handlePreview(item)">预览</el-button>
|
||||||
|
<el-button size="mini" type="text" icon="el-icon-printer" @click.stop="handlePrint(item)">打印</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pagination
|
||||||
|
v-show="total>0"
|
||||||
|
:total="total"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 添加或修改质量证明书主对话框 -->
|
||||||
|
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||||
|
<el-form-item label="证明书号" prop="certificateNo">
|
||||||
|
<el-input v-model="form.certificateNo" placeholder="请输入证明书号" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="合同号" prop="contractNo">
|
||||||
|
<el-input v-model="form.contractNo" placeholder="请输入合同号" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品名称" prop="productName">
|
||||||
|
<el-input v-model="form.productName" placeholder="请输入产品名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="执行标准" prop="standard">
|
||||||
|
<el-input v-model="form.standard" placeholder="请输入执行标准" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="收货单位" prop="consignee">
|
||||||
|
<el-input v-model="form.consignee" placeholder="请输入收货单位" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="生产厂家" prop="manufacturer">
|
||||||
|
<el-input v-model="form.manufacturer" placeholder="请输入生产厂家" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="签发日期" prop="issueDate">
|
||||||
|
<el-date-picker clearable
|
||||||
|
v-model="form.issueDate"
|
||||||
|
type="datetime"
|
||||||
|
value-format="yyyy-MM-dd HH:mm:ss"
|
||||||
|
placeholder="请选择签发日期">
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="质保证明说明" prop="note">
|
||||||
|
<el-input v-model="form.note" type="textarea" placeholder="请输入内容" auto-height />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" auto-height />
|
||||||
|
</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="previewTitle" :visible.sync="previewVisible" width="1200px" append-to-body fullscreen>
|
||||||
|
<div class="preview-content-wrapper">
|
||||||
|
<CertificatePrintPreview
|
||||||
|
ref="printComponent"
|
||||||
|
:visible="true"
|
||||||
|
:preview="true"
|
||||||
|
:certificate="previewData"
|
||||||
|
:items="previewItems"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="handleExportPdfFromPreview">导出PDF</el-button>
|
||||||
|
<el-button type="success" @click="handlePrintFromPreview">打印</el-button>
|
||||||
|
<el-button @click="previewVisible = false">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 隐藏的打印组件 -->
|
||||||
|
<CertificatePrintPreview
|
||||||
|
v-show="false"
|
||||||
|
ref="hiddenPrintComponent"
|
||||||
|
:certificate="printCertificateData"
|
||||||
|
:items="printItemsData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listCertificate, getCertificate, delCertificate, addCertificate, updateCertificate } from "@/api/mes/qc/certificate";
|
||||||
|
import { listCertificateItem } from "@/api/mes/qc/certificateItem";
|
||||||
|
import CertificatePrintPreview from "./components/CertificatePrintPreview.vue";
|
||||||
|
import { print as printPdf, downloadPdf } from "./lib/printUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Certificate",
|
||||||
|
components: {
|
||||||
|
CertificatePrintPreview
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
buttonLoading: false,
|
||||||
|
loading: true,
|
||||||
|
ids: [],
|
||||||
|
selectedIds: [],
|
||||||
|
single: true,
|
||||||
|
multiple: true,
|
||||||
|
showSearch: true,
|
||||||
|
total: 0,
|
||||||
|
certificateList: [],
|
||||||
|
title: "",
|
||||||
|
open: false,
|
||||||
|
previewVisible: false,
|
||||||
|
previewTitle: "",
|
||||||
|
previewData: {},
|
||||||
|
previewItems: [],
|
||||||
|
printComponentVisible: false,
|
||||||
|
printCertificateData: {},
|
||||||
|
printItemsData: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
certificateNo: undefined,
|
||||||
|
contractNo: undefined,
|
||||||
|
productName: undefined,
|
||||||
|
standard: undefined,
|
||||||
|
consignee: undefined,
|
||||||
|
manufacturer: undefined,
|
||||||
|
},
|
||||||
|
form: {},
|
||||||
|
rules: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 查询质量证明书主列表 */
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listCertificate(this.queryParams).then(response => {
|
||||||
|
this.certificateList = response.rows;
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 取消按钮
|
||||||
|
cancel() {
|
||||||
|
this.open = false;
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
// 表单重置
|
||||||
|
reset() {
|
||||||
|
this.form = {
|
||||||
|
certificateId: undefined,
|
||||||
|
certificateNo: undefined,
|
||||||
|
contractNo: undefined,
|
||||||
|
productName: undefined,
|
||||||
|
standard: undefined,
|
||||||
|
consignee: undefined,
|
||||||
|
manufacturer: undefined,
|
||||||
|
issueDate: undefined,
|
||||||
|
note: 'D.T=Denu_Test \t T.S=Tensile Strength \t D=弯心直径MandrelDiameter \n Strength \t G.L=拉伸标距GaugeLength \t EL=Percentage Elongation After Fracture',
|
||||||
|
remark: '1.本产品经检验满足订货标准要求。The material has been tested with satisfactory resultsin accordance with the speciicatication \n 2.本质量证明书中空白项目均不作为交货条件。The blank items shouldn’tbe regarded as delivery conditions. \n 3.盖章后生效。The quality certificate willcome into force with a valid stamp.',
|
||||||
|
delFlag: undefined,
|
||||||
|
createTime: undefined,
|
||||||
|
createBy: undefined,
|
||||||
|
updateTime: undefined,
|
||||||
|
updateBy: undefined
|
||||||
|
};
|
||||||
|
this.resetForm("form");
|
||||||
|
},
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
resetQuery() {
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
handleCardClick(item) {
|
||||||
|
const index = this.selectedIds.indexOf(item.certificateId);
|
||||||
|
if (index > -1) {
|
||||||
|
this.selectedIds.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
this.selectedIds.push(item.certificateId);
|
||||||
|
}
|
||||||
|
this.updateSelectionStatus();
|
||||||
|
},
|
||||||
|
handleCheckboxChange(certificateId, event) {
|
||||||
|
if (event.target.checked) {
|
||||||
|
if (!this.selectedIds.includes(certificateId)) {
|
||||||
|
this.selectedIds.push(certificateId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const index = this.selectedIds.indexOf(certificateId);
|
||||||
|
if (index > -1) {
|
||||||
|
this.selectedIds.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.updateSelectionStatus();
|
||||||
|
},
|
||||||
|
updateSelectionStatus() {
|
||||||
|
this.ids = [...this.selectedIds];
|
||||||
|
this.single = this.selectedIds.length !== 1;
|
||||||
|
this.multiple = this.selectedIds.length === 0;
|
||||||
|
},
|
||||||
|
/** 新增按钮操作 */
|
||||||
|
handleAdd() {
|
||||||
|
this.reset();
|
||||||
|
this.open = true;
|
||||||
|
this.title = "添加质量证明书主";
|
||||||
|
},
|
||||||
|
/** 修改按钮操作 */
|
||||||
|
handleUpdate(row) {
|
||||||
|
this.loading = true;
|
||||||
|
this.reset();
|
||||||
|
const certificateId = row.certificateId || this.ids
|
||||||
|
getCertificate(certificateId).then(response => {
|
||||||
|
this.loading = false;
|
||||||
|
this.form = response.data;
|
||||||
|
this.open = true;
|
||||||
|
this.title = "修改质量证明书主";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 提交按钮 */
|
||||||
|
submitForm() {
|
||||||
|
this.$refs["form"].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.buttonLoading = true;
|
||||||
|
if (this.form.certificateId != null) {
|
||||||
|
updateCertificate(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
}).finally(() => {
|
||||||
|
this.buttonLoading = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
addCertificate(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("新增成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
}).finally(() => {
|
||||||
|
this.buttonLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 删除按钮操作 */
|
||||||
|
handleDelete(row) {
|
||||||
|
const certificateIds = row.certificateId || this.ids;
|
||||||
|
this.$modal.confirm('是否确认删除质量证明书主编号为"' + certificateIds + '"的数据项?').then(() => {
|
||||||
|
this.loading = true;
|
||||||
|
return delCertificate(certificateIds);
|
||||||
|
}).then(() => {
|
||||||
|
this.loading = false;
|
||||||
|
this.getList();
|
||||||
|
this.$modal.msgSuccess("删除成功");
|
||||||
|
}).catch(() => {
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 预览按钮操作 */
|
||||||
|
async handlePreview(row) {
|
||||||
|
const certificateId = row.certificateId;
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const [certRes, itemsRes] = await Promise.all([
|
||||||
|
getCertificate(certificateId),
|
||||||
|
listCertificateItem({ certificateId, pageSize: 100 })
|
||||||
|
]);
|
||||||
|
this.previewData = certRes.data;
|
||||||
|
this.previewItems = itemsRes.rows || [];
|
||||||
|
this.previewTitle = `预览 - ${certRes.data.certificateNo}`;
|
||||||
|
this.previewVisible = true;
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('获取数据失败');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 打印按钮操作 */
|
||||||
|
async handlePrint(row) {
|
||||||
|
const certificateId = row.certificateId;
|
||||||
|
await this.preparePrintComponent(certificateId);
|
||||||
|
await this.$nextTick();
|
||||||
|
const el = this.$refs.hiddenPrintComponent.$refs.certificateContent;
|
||||||
|
await printPdf(el);
|
||||||
|
},
|
||||||
|
/** 从预览打印 */
|
||||||
|
async handlePrintFromPreview() {
|
||||||
|
await this.$nextTick();
|
||||||
|
const el = this.$refs.printComponent.$refs.certificateContent;
|
||||||
|
await printPdf(el);
|
||||||
|
},
|
||||||
|
/** 导出PDF按钮操作 */
|
||||||
|
async handleExportPdf() {
|
||||||
|
if (this.ids.length !== 1) {
|
||||||
|
this.$message.warning('请选择一条质保书进行导出');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.preparePrintComponent(this.ids[0]);
|
||||||
|
await this.$nextTick();
|
||||||
|
const fileName = `${this.printCertificateData.certificateNo || '质量证明书'}_${new Date().getTime()}.pdf`;
|
||||||
|
const el = this.$refs.hiddenPrintComponent.$refs.certificateContent;
|
||||||
|
await downloadPdf(el, fileName);
|
||||||
|
this.$message.success('导出成功');
|
||||||
|
},
|
||||||
|
/** 从预览导出PDF */
|
||||||
|
async handleExportPdfFromPreview() {
|
||||||
|
await this.$nextTick();
|
||||||
|
const fileName = `${this.previewData.certificateNo || '质量证明书'}_${new Date().getTime()}.pdf`;
|
||||||
|
const el = this.$refs.printComponent.$refs.certificateContent;
|
||||||
|
await downloadPdf(el, fileName);
|
||||||
|
this.$message.success('导出成功');
|
||||||
|
},
|
||||||
|
async preparePrintComponent(certificateId) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const [certRes, itemsRes] = await Promise.all([
|
||||||
|
getCertificate(certificateId),
|
||||||
|
listCertificateItem({ certificateId, pageSize: 100 })
|
||||||
|
]);
|
||||||
|
this.printCertificateData = certRes.data;
|
||||||
|
this.printItemsData = itemsRes.rows || [];
|
||||||
|
this.printComponentVisible = true;
|
||||||
|
await this.$nextTick();
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('获取数据失败');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.certificate-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
|
||||||
|
gap: 18px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-card {
|
||||||
|
background: #fcf9f2;
|
||||||
|
border: 1px solid #d6cfc0;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 18px 20px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
box-shadow:
|
||||||
|
0 1px 3px rgba(0, 0, 0, 0.06),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-top-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: linear-gradient(90deg, #3a3a3a 0%, #7a7a7a 50%, #3a3a3a 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-card:hover {
|
||||||
|
border-color: #b8ad98;
|
||||||
|
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-card-selected {
|
||||||
|
border-color: #7a6f5e;
|
||||||
|
border-width: 2px;
|
||||||
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-checkbox {
|
||||||
|
position: absolute;
|
||||||
|
top: 14px;
|
||||||
|
right: 14px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-doc-type {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2a2a2a;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
font-family: "SimSun", "Songti SC", "Noto Serif SC", serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-sep {
|
||||||
|
color: #c0b8a8;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-env {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #9a9080;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
font-family: "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-cert-no {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #ddd6c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-cert-no-label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #8a8070;
|
||||||
|
font-style: italic;
|
||||||
|
font-family: "Times New Roman", serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-cert-no-value {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a1a1a;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-family: "Courier New", "Times New Roman", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
padding: 5px 0;
|
||||||
|
border-bottom: 1px dotted #e6dfd0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info-item:nth-last-child(-n+2) {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info-label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #8a8270;
|
||||||
|
min-width: 58px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info-value {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #222;
|
||||||
|
font-weight: 500;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid #ddd6c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-stamp-placeholder {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border: 1.5px solid #9a8a7a;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #9a8a7a;
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
font-family: "SimSun", serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-actions .el-button {
|
||||||
|
padding: 3px 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,384 @@
|
|||||||
|
<template>
|
||||||
|
<div class="certificate-print-container" :class="{ 'is-hidden': !preview }">
|
||||||
|
<div ref="certificateContent" class="certificate-pdf-content" :class="{ 'is-preview': preview }">
|
||||||
|
<div class="pdf-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<img src="@/assets/logo/logo.png" class="company-logo" alt="logo" />
|
||||||
|
<div class="company-name-cn">科伦普</div>
|
||||||
|
<div class="company-name-en">KE LUN PU</div>
|
||||||
|
</div>
|
||||||
|
<div class="header-center">
|
||||||
|
<div class="main-title">产品质量证明书</div>
|
||||||
|
<div class="main-title-en">PRODUCT QUALITY CERTIFICATE</div>
|
||||||
|
</div>
|
||||||
|
<div class="header-right">
|
||||||
|
<div class="company-full-name">嘉祥科伦普重工有限公司</div>
|
||||||
|
<div class="company-address">山东省济宁市嘉祥县化工园区</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pdf-body">
|
||||||
|
<table class="info-table">
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">执行标准(SPECIFICATION):</td>
|
||||||
|
<td class="value-cell">{{ certificate.standard || '/' }}</td>
|
||||||
|
<td class="label-cell">证明书号(CERTIFICATE NO.):</td>
|
||||||
|
<td class="value-cell">{{ certificate.certificateNo || '' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">收货单位(CONSIGNEE):</td>
|
||||||
|
<td class="value-cell">{{ certificate.consignee || '-' }}</td>
|
||||||
|
<td class="label-cell">合同号(CONTRACT NO.):</td>
|
||||||
|
<td class="value-cell">{{ certificate.contractNo || '' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">产品名称(PRODUCT):</td>
|
||||||
|
<td class="value-cell">{{ certificate.productName || '' }}</td>
|
||||||
|
<td class="label-cell">签发日期(DATE OF ISSUE):</td>
|
||||||
|
<td class="value-cell">{{ formatDate(certificate.issueDate) || '' }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th rowspan="2">序号</th>
|
||||||
|
<th rowspan="2">钢卷号<br>Coil No.</th>
|
||||||
|
<th rowspan="2">炉号<br>Heat No.</th>
|
||||||
|
<th rowspan="2">材质<br>Type</th>
|
||||||
|
<th rowspan="2">规格 Size<br>(mm)</th>
|
||||||
|
<th rowspan="2">件数<br>Pieces</th>
|
||||||
|
<th rowspan="2">重量<br>Weight(t)</th>
|
||||||
|
<th colspan="6">化学成分(%)<br>Chemical Composition</th>
|
||||||
|
<th colspan="3">拉伸试验 Tensile Test<br>(G.L=80mm)</th>
|
||||||
|
<th rowspan="2">硬度试验<br>Hardness Test</th>
|
||||||
|
<th rowspan="2">弯曲试验<br>180° B.T<br>D=0a</th>
|
||||||
|
<th rowspan="2">表面质量</th>
|
||||||
|
<th rowspan="2">表面结构</th>
|
||||||
|
<th rowspan="2">边缘状态</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>C</th>
|
||||||
|
<th>Si</th>
|
||||||
|
<th>Mn</th>
|
||||||
|
<th>P</th>
|
||||||
|
<th>S</th>
|
||||||
|
<th>Als</th>
|
||||||
|
<th>屈服 RP0.2<br>Y.S(MPa)</th>
|
||||||
|
<th>抗拉 T.S<br>(MPa)</th>
|
||||||
|
<th>伸长率 EL<br>(%)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(item, index) in items" :key="index">
|
||||||
|
<td>{{ index + 1 }}</td>
|
||||||
|
<td>{{ item.coilNo || '' }}</td>
|
||||||
|
<td>{{ item.heatNo || '' }}</td>
|
||||||
|
<td>{{ item.materialType || '' }}</td>
|
||||||
|
<td>{{ item.size || '' }}</td>
|
||||||
|
<td>{{ item.pieces || '' }}</td>
|
||||||
|
<td>{{ item.weight || '' }}</td>
|
||||||
|
<td>{{ item.c || '/' }}</td>
|
||||||
|
<td>{{ item.si || '/' }}</td>
|
||||||
|
<td>{{ item.mn || '/' }}</td>
|
||||||
|
<td>{{ item.p || '/' }}</td>
|
||||||
|
<td>{{ item.s || '/' }}</td>
|
||||||
|
<td>{{ item.als || '/' }}</td>
|
||||||
|
<td>{{ item.yieldStrength || '/' }}</td>
|
||||||
|
<td>{{ item.tensileStrength || '/' }}</td>
|
||||||
|
<td>{{ item.elongation || '/' }}</td>
|
||||||
|
<td>{{ item.hardness || '/' }}</td>
|
||||||
|
<td>{{ item.bendingTest || '/' }}</td>
|
||||||
|
<td>{{ item.surfaceQuality || '/' }}</td>
|
||||||
|
<td>{{ item.surfaceStructure || '/' }}</td>
|
||||||
|
<td>{{ item.edgeStatus || '/' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="items.length === 0">
|
||||||
|
<td colspan="21" class="empty-cell">暂无数据</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table class="footer-table">
|
||||||
|
<tr>
|
||||||
|
<td class="footer-label">注释<br>NOTE</td>
|
||||||
|
<td class="footer-content">
|
||||||
|
<pre>{{ certificate.note || '/' }}</pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="footer-label">备注<br>REMARK</td>
|
||||||
|
<td class="footer-content footer-remark-cell">
|
||||||
|
<pre class="remark-text">{{ certificate.remark || '/' }}</pre>
|
||||||
|
<div class="signature-area">
|
||||||
|
<img src="@/assets/images/zhijian.png" class="stamp-image" alt="质检专用章" />
|
||||||
|
<div class="signature-block">
|
||||||
|
<div class="signature-label">质量负责人:</div>
|
||||||
|
<img src="@/assets/images/yanghongyan.png" class="signature-image" alt="签名" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CertificatePrintPreview',
|
||||||
|
props: {
|
||||||
|
certificate: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatDate(dateStr) {
|
||||||
|
if (!dateStr) return '';
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.certificate-print-container.is-hidden {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-print-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-pdf-content {
|
||||||
|
width: 297mm;
|
||||||
|
min-height: 210mm;
|
||||||
|
background: white;
|
||||||
|
padding: 8mm 10mm;
|
||||||
|
font-family: "SimSun", "宋体", Arial, sans-serif;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.pdf-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
text-align: center;
|
||||||
|
width: 90px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-logo {
|
||||||
|
width: 70px;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-name-cn {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-name-en {
|
||||||
|
font-size: 9px;
|
||||||
|
color: #333;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-center {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title-en {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #000;
|
||||||
|
font-family: "Times New Roman", serif;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-right {
|
||||||
|
text-align: right;
|
||||||
|
width: 160px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-full-name {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-address {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #333;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-body {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-table td {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 3px 6px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-table .label-cell {
|
||||||
|
font-weight: bold;
|
||||||
|
width: 22%;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-table .value-cell {
|
||||||
|
width: 28%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th,
|
||||||
|
.data-table td {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 2px 3px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table td {
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-cell {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-table td {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 4px 6px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-label {
|
||||||
|
font-weight: bold;
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 9px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
font-size: 9px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-remark-cell {
|
||||||
|
position: relative;
|
||||||
|
padding-right: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remark-text {
|
||||||
|
font-size: 9px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-area {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
bottom: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stamp-image {
|
||||||
|
width: 80px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-block {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-label {
|
||||||
|
font-size: 9px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-image {
|
||||||
|
height: 35px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
888
klp-ui/src/views/mes/qc/certificate/item.vue
Normal file
888
klp-ui/src/views/mes/qc/certificate/item.vue
Normal file
@@ -0,0 +1,888 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<div class="tab-container">
|
||||||
|
<div class="select-button" @click="openCertificateDialog">
|
||||||
|
<i class="el-icon-setting"></i>
|
||||||
|
</div>
|
||||||
|
<div class="custom-tabs">
|
||||||
|
<div class="tab-nav">
|
||||||
|
<button v-if="certificateList.length > 0 && queryParams.pageNum > 1" class="nav-btn prev" @click="prevPage">
|
||||||
|
<i class="el-icon-arrow-left"></i>
|
||||||
|
</button>
|
||||||
|
<div class="tab-header">
|
||||||
|
<div v-for="cert in certificateList" :key="cert.certificateId" class="tab-item"
|
||||||
|
:class="{ active: activeTab === cert.certificateId.toString() }" @click="handleTabClick(cert)">
|
||||||
|
<div class="tab-title">{{ cert.certificateNo }}</div>
|
||||||
|
<div class="tab-info">
|
||||||
|
<span>{{ cert.productName }}</span>
|
||||||
|
<span class="date">{{ parseTime(cert.issueDate, '{y}-{m}-{d}') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="certificateList.length === 0" class="tab-item disabled">
|
||||||
|
<div class="tab-title">暂无质保书</div>
|
||||||
|
<div class="tab-info">请点击齿轮图标选择质保书</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button v-if="certificateList.length > 0 && queryParams.pageNum < totalPage" class="nav-btn next" @click="nextPage">
|
||||||
|
<i class="el-icon-arrow-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="currentCertificateId" class="certificate-detail-container">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>{{ currentCertificateInfo.certificateNo || '质保书明细' }}</h3>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" plain @click="handleAdd">新增明细</el-button>
|
||||||
|
<el-button type="success" plain @click="openBatchAddDialog">批量新增</el-button>
|
||||||
|
<el-button type="danger" plain @click="handleBatchDelete"
|
||||||
|
:disabled="selectedRows.length === 0">批量删除</el-button>
|
||||||
|
<el-button type="warning" plain icon="el-icon-printer" @click="handlePrint">打印</el-button>
|
||||||
|
<el-button type="info" plain @click="getList">刷新</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-form class="certificate-info" :model="currentCertificateInfo" label-width="80px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="证明书号">
|
||||||
|
<el-input v-model="currentCertificateInfo.certificateNo" @change="handleCertificateInfoChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="合同号">
|
||||||
|
<el-input v-model="currentCertificateInfo.contractNo" @change="handleCertificateInfoChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="产品名称">
|
||||||
|
<el-input v-model="currentCertificateInfo.productName" @change="handleCertificateInfoChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="签发日期">
|
||||||
|
<el-date-picker v-model="currentCertificateInfo.issueDate" type="date" @change="handleCertificateInfoChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="执行标准">
|
||||||
|
<el-input v-model="currentCertificateInfo.standard" @change="handleCertificateInfoChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="收货单位">
|
||||||
|
<el-input v-model="currentCertificateInfo.consignee" @change="handleCertificateInfoChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="生产厂家">
|
||||||
|
<el-input v-model="currentCertificateInfo.manufacturer" @change="handleCertificateInfoChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="certificateItemList" style="width: 100%" border
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" fixed="left" />
|
||||||
|
<el-table-column label="操作" align="center" width="140" fixed="left">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="primary" size="mini" @click="handleSave(scope.row)">保存</el-button>
|
||||||
|
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="钢卷号" align="center" prop="coilNo" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.coilNo" style="background-color: #fff3e6;" placeholder="点击选择钢卷">
|
||||||
|
<template slot="append">
|
||||||
|
<el-button size="mini" icon="el-icon-search" @click="openCoilSelectDialog(scope.row)"></el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="炉号" align="center" prop="heatNo" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.heatNo" style="background-color: #e6f7ff;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="材质" align="center" prop="materialType" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.materialType" style="background-color: #fff3e6;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="规格(mm)" align="center" prop="size" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.size" style="background-color: #fff3e6;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="件数" align="center" prop="pieces" width="80">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model.number="scope.row.pieces" style="background-color: #fff3e6;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="重量(t)" align="center" prop="weight" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model.number="scope.row.weight" style="background-color: #fff3e6;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="C(%)" align="center" prop="c" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.c" style="background-color: #e6f7ff;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="Si(%)" align="center" prop="si" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.si" style="background-color: #e6f7ff;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="Mn(%)" align="center" prop="mn" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.mn" style="background-color: #e6f7ff;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="P(%)" align="center" prop="p" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.p" style="background-color: #e6f7ff;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="S(%)" align="center" prop="s" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.s" style="background-color: #e6f7ff;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="Als(%)" align="center" prop="als" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.als" style="background-color: #e6f7ff;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="屈服强度(MPa)" align="center" prop="yieldStrength" width="130">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model.number="scope.row.yieldStrength" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="抗拉强度(MPa)" align="center" prop="tensileStrength" width="130">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model.number="scope.row.tensileStrength" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="伸长率(%)" align="center" prop="elongation" width="110">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.elongation" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="硬度(HRB)" align="center" prop="hardness" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model.number="scope.row.hardness" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="弯曲试验" align="center" prop="bendingTest" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.bendingTest" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="表面质量" align="center" prop="surfaceQuality" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.surfaceQuality" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="表面结构" align="center" prop="surfaceStructure" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.surfaceStructure" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="边缘状态" align="center" prop="edgeStatus" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.edgeStatus" style="background-color: #f6ffed;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column label="备注" align="center" prop="remark">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.remark" style="background-color: #fff0f6;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column> -->
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||||
|
@pagination="getList" />
|
||||||
|
</div>
|
||||||
|
<el-empty v-else class="appempty" description="选择质保书查看明细" />
|
||||||
|
|
||||||
|
<el-dialog :title="'选择钢卷'" :visible.sync="coilDialogVisible" width="900px" append-to-body>
|
||||||
|
<div class="dialog-toolbar">
|
||||||
|
<el-form :model="coilQueryParams" ref="coilQueryForm" size="small" :inline="true" label-width="68px">
|
||||||
|
<el-form-item label="钢卷号" prop="coilNo">
|
||||||
|
<el-input v-model="coilQueryParams.coilNo" placeholder="请输入钢卷号" clearable
|
||||||
|
@keyup.enter.native="handleCoilQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="材质" prop="material">
|
||||||
|
<el-input v-model="coilQueryParams.material" placeholder="请输入材质" clearable
|
||||||
|
@keyup.enter.native="handleCoilQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规格" prop="specification">
|
||||||
|
<el-input v-model="coilQueryParams.specification" placeholder="请输入规格" clearable
|
||||||
|
@keyup.enter.native="handleCoilQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleCoilQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetCoilQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="coilLoading" :data="coilList" height="350px" @row-click="handleCoilSelect">
|
||||||
|
<el-table-column label="钢卷号" align="center" prop="currentCoilNo" />
|
||||||
|
<el-table-column label="材质" align="center" prop="material" />
|
||||||
|
<el-table-column label="规格" align="center" prop="specification" />
|
||||||
|
<el-table-column label="重量(t)" align="center" prop="netWeight" />
|
||||||
|
</el-table>
|
||||||
|
<pagination v-show="coilTotal > 0" :total="coilTotal" :page.sync="coilQueryParams.pageNum"
|
||||||
|
:limit.sync="coilQueryParams.pageSize" @pagination="getCoilList" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :title="'选择质保书'" :visible.sync="certificateDialogVisible" width="900px" append-to-body>
|
||||||
|
<div class="dialog-toolbar">
|
||||||
|
<el-form :model="certificateQueryParams" ref="certificateQueryForm" size="small" :inline="true" label-width="68px">
|
||||||
|
<el-form-item label="证明书号" prop="certificateNo">
|
||||||
|
<el-input v-model="certificateQueryParams.certificateNo" placeholder="请输入证明书号" clearable
|
||||||
|
@keyup.enter.native="handleCertificateQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="合同号" prop="contractNo">
|
||||||
|
<el-input v-model="certificateQueryParams.contractNo" placeholder="请输入合同号" clearable
|
||||||
|
@keyup.enter.native="handleCertificateQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品名称" prop="productName">
|
||||||
|
<el-input v-model="certificateQueryParams.productName" placeholder="请输入产品名称" clearable
|
||||||
|
@keyup.enter.native="handleCertificateQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleCertificateQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetCertificateQuery">重置</el-button>
|
||||||
|
<el-button type="success" icon="el-icon-plus" size="mini" @click="handleAddCertificate">新增质保书</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="certificateLoading" :data="allCertificateList" height="350px" @row-click="handleTabClick">
|
||||||
|
<el-table-column label="证明书号" align="center" prop="certificateNo" />
|
||||||
|
<el-table-column label="合同号" align="center" prop="contractNo" />
|
||||||
|
<el-table-column label="产品名称" align="center" prop="productName" />
|
||||||
|
<el-table-column label="执行标准" align="center" prop="standard" />
|
||||||
|
<el-table-column label="收货单位" align="center" prop="consignee" />
|
||||||
|
<el-table-column label="签发日期" align="center" prop="issueDate" width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.issueDate, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
|
</el-table>
|
||||||
|
<pagination v-show="certificateTotal > 0" :total="certificateTotal" :page.sync="certificateQueryParams.pageNum"
|
||||||
|
:limit.sync="certificateQueryParams.pageSize" @pagination="getCertificateList" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog :title="'新增质保书'" :visible.sync="addCertificateDialogVisible" width="800px" append-to-body>
|
||||||
|
<el-form :model="certificateForm" ref="certificateForm" label-width="100px">
|
||||||
|
<el-form-item label="证明书号" prop="certificateNo">
|
||||||
|
<el-input v-model="certificateForm.certificateNo" placeholder="请输入证明书号" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="合同号" prop="contractNo">
|
||||||
|
<el-input v-model="certificateForm.contractNo" placeholder="请输入合同号" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品名称" prop="productName">
|
||||||
|
<el-input v-model="certificateForm.productName" placeholder="请输入产品名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="执行标准" prop="standard">
|
||||||
|
<el-input v-model="certificateForm.standard" placeholder="请输入执行标准" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="收货单位" prop="consignee">
|
||||||
|
<el-input v-model="certificateForm.consignee" placeholder="请输入收货单位" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="生产厂家" prop="manufacturer">
|
||||||
|
<el-input v-model="certificateForm.manufacturer" placeholder="请输入生产厂家" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="签发日期" prop="issueDate">
|
||||||
|
<el-date-picker v-model="certificateForm.issueDate" value-format="yyyy-MM-dd HH:mm:ss" type="date" placeholder="请选择签发日期" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="质保证明说明" prop="note">
|
||||||
|
<el-input v-model="certificateForm.note" type="textarea" placeholder="请输入内容" auto-height />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="certificateForm.remark" type="textarea" placeholder="请输入内容" auto-height />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="addCertificateDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitCertificateForm">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<CoilSelector
|
||||||
|
:visible.sync="batchAddDialogVisible"
|
||||||
|
:use-trigger="false"
|
||||||
|
:multiple="true"
|
||||||
|
@confirm="handleBatchAddConfirm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 隐藏的打印组件 -->
|
||||||
|
<CertificatePrintPreview
|
||||||
|
ref="certificatePrint"
|
||||||
|
v-show="false"
|
||||||
|
:certificate="printCertificateData"
|
||||||
|
:items="printItemsData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listCertificateItem, getCertificateItem, delCertificateItem, addCertificateItem, updateCertificateItem } from "@/api/mes/qc/certificateItem";
|
||||||
|
import { listCertificate, getCertificate, updateCertificate, addCertificate } from "@/api/mes/qc/certificate";
|
||||||
|
import { listMaterialCoil } from "@/api/wms/coil";
|
||||||
|
import CoilSelector from "@/components/CoilSelector/index.vue";
|
||||||
|
import CertificatePrintPreview from "./components/CertificatePrintPreview.vue";
|
||||||
|
import { print as printPdf } from "./lib/printUtils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CoilSelector,
|
||||||
|
CertificatePrintPreview
|
||||||
|
},
|
||||||
|
name: "CertificateItem",
|
||||||
|
computed: {
|
||||||
|
totalPage() {
|
||||||
|
return Math.ceil(this.certificateTotal / this.certificateQueryParams.pageSize)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
buttonLoading: false,
|
||||||
|
loading: false,
|
||||||
|
certificateLoading: false,
|
||||||
|
total: 0,
|
||||||
|
certificateTotal: 0,
|
||||||
|
certificateList: [],
|
||||||
|
allCertificateList: [],
|
||||||
|
certificateItemList: [],
|
||||||
|
currentCertificateId: undefined,
|
||||||
|
currentCertificateInfo: {},
|
||||||
|
activeTab: '',
|
||||||
|
certificateDialogVisible: false,
|
||||||
|
coilDialogVisible: false,
|
||||||
|
batchAddDialogVisible: false,
|
||||||
|
addCertificateDialogVisible: false,
|
||||||
|
printComponentVisible: false,
|
||||||
|
printCertificateData: {},
|
||||||
|
printItemsData: [],
|
||||||
|
certificateForm: {
|
||||||
|
certificateNo: '',
|
||||||
|
contractNo: '',
|
||||||
|
productName: '',
|
||||||
|
standard: '',
|
||||||
|
consignee: '',
|
||||||
|
manufacturer: '',
|
||||||
|
issueDate: '',
|
||||||
|
remark: ''
|
||||||
|
},
|
||||||
|
coilLoading: false,
|
||||||
|
coilTotal: 0,
|
||||||
|
coilList: [],
|
||||||
|
currentEditRow: null,
|
||||||
|
selectedRows: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 50,
|
||||||
|
certificateId: undefined
|
||||||
|
},
|
||||||
|
certificateQueryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
certificateNo: undefined,
|
||||||
|
contractNo: undefined,
|
||||||
|
productName: undefined
|
||||||
|
},
|
||||||
|
coilQueryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
coilNo: undefined,
|
||||||
|
material: undefined,
|
||||||
|
specification: undefined
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getCertificateList();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
parseTime(time, pattern) {
|
||||||
|
if (!time) return '';
|
||||||
|
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
|
||||||
|
const date = new Date(time);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
const day = date.getDate();
|
||||||
|
const hour = date.getHours();
|
||||||
|
const minute = date.getMinutes();
|
||||||
|
const second = date.getSeconds();
|
||||||
|
return format.replace('{y}', year)
|
||||||
|
.replace('{m}', month.toString().padStart(2, '0'))
|
||||||
|
.replace('{d}', day.toString().padStart(2, '0'))
|
||||||
|
.replace('{h}', hour.toString().padStart(2, '0'))
|
||||||
|
.replace('{i}', minute.toString().padStart(2, '0'))
|
||||||
|
.replace('{s}', second.toString().padStart(2, '0'));
|
||||||
|
},
|
||||||
|
getCertificateList() {
|
||||||
|
this.certificateLoading = true;
|
||||||
|
listCertificate(this.certificateQueryParams).then(response => {
|
||||||
|
this.allCertificateList = response.rows;
|
||||||
|
this.certificateTotal = response.total;
|
||||||
|
this.certificateList = response.rows;
|
||||||
|
this.certificateLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleCertificateQuery() {
|
||||||
|
this.certificateQueryParams.pageNum = 1;
|
||||||
|
this.getCertificateList();
|
||||||
|
},
|
||||||
|
resetCertificateQuery() {
|
||||||
|
this.certificateQueryParams = {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
certificateNo: undefined,
|
||||||
|
contractNo: undefined,
|
||||||
|
productName: undefined
|
||||||
|
};
|
||||||
|
this.getCertificateList();
|
||||||
|
},
|
||||||
|
handleTabClick(certificate) {
|
||||||
|
this.activeTab = certificate.certificateId.toString();
|
||||||
|
this.certificateDialogVisible = false;
|
||||||
|
this.handleRowClick(certificate);
|
||||||
|
},
|
||||||
|
handleRowClick(row) {
|
||||||
|
this.currentCertificateId = row.certificateId;
|
||||||
|
this.currentCertificateInfo = row;
|
||||||
|
this.queryParams.certificateId = row.certificateId;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
getCertificateDetail(certificateId) {
|
||||||
|
getCertificate(certificateId).then(response => {
|
||||||
|
this.currentCertificateInfo = response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
prevPage() {
|
||||||
|
if (this.certificateQueryParams.pageNum > 1) {
|
||||||
|
this.certificateQueryParams.pageNum--;
|
||||||
|
this.getCertificateList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextPage() {
|
||||||
|
if (this.certificateQueryParams.pageNum < this.totalPage) {
|
||||||
|
this.certificateQueryParams.pageNum++;
|
||||||
|
this.getCertificateList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openCertificateDialog() {
|
||||||
|
this.getCertificateList();
|
||||||
|
this.certificateDialogVisible = true;
|
||||||
|
},
|
||||||
|
openCoilSelectDialog(row) {
|
||||||
|
this.currentEditRow = row;
|
||||||
|
this.coilDialogVisible = true;
|
||||||
|
this.getCoilList();
|
||||||
|
},
|
||||||
|
getCoilList() {
|
||||||
|
this.coilLoading = true;
|
||||||
|
listMaterialCoil(this.coilQueryParams).then(response => {
|
||||||
|
this.coilList = response.rows;
|
||||||
|
this.coilTotal = response.total;
|
||||||
|
this.coilLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleCoilQuery() {
|
||||||
|
this.coilQueryParams.pageNum = 1;
|
||||||
|
this.getCoilList();
|
||||||
|
},
|
||||||
|
resetCoilQuery() {
|
||||||
|
this.coilQueryParams = {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
coilNo: undefined,
|
||||||
|
material: undefined,
|
||||||
|
specification: undefined
|
||||||
|
};
|
||||||
|
this.getCoilList();
|
||||||
|
},
|
||||||
|
handleCoilSelect(row) {
|
||||||
|
if (this.currentEditRow) {
|
||||||
|
this.currentEditRow.coilNo = row.currentCoilNo;
|
||||||
|
this.currentEditRow.materialType = row.material;
|
||||||
|
this.currentEditRow.size = row.specification;
|
||||||
|
this.currentEditRow.weight = row.netWeight;
|
||||||
|
this.currentEditRow.pieces = 1;
|
||||||
|
}
|
||||||
|
this.coilDialogVisible = false;
|
||||||
|
this.currentEditRow = null;
|
||||||
|
},
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listCertificateItem({
|
||||||
|
...this.queryParams,
|
||||||
|
certificateId: this.currentCertificateId,
|
||||||
|
}).then(response => {
|
||||||
|
this.certificateItemList = response.rows.sort((a, b) => {
|
||||||
|
return parseInt(a.itemSeqNo) - parseInt(b.itemSeqNo);
|
||||||
|
});
|
||||||
|
this.total = response.total;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleAdd() {
|
||||||
|
const newRow = {
|
||||||
|
certificateId: this.currentCertificateId,
|
||||||
|
itemSeqNo: this.certificateItemList.length + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
addCertificateItem(newRow).then(response => {
|
||||||
|
this.$message({
|
||||||
|
message: "新增成功",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openBatchAddDialog() {
|
||||||
|
this.batchAddDialogVisible = true;
|
||||||
|
},
|
||||||
|
handleBatchAddConfirm(coils) {
|
||||||
|
if (!coils || coils.length === 0) {
|
||||||
|
this.$message.warning('请选择钢卷');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalCount = coils.length;
|
||||||
|
let successCount = 0;
|
||||||
|
let failCount = 0;
|
||||||
|
|
||||||
|
coils.forEach((coil, index) => {
|
||||||
|
const newRow = {
|
||||||
|
certificateId: this.currentCertificateId,
|
||||||
|
itemSeqNo: this.certificateItemList.length + index + 1,
|
||||||
|
coilNo: coil.currentCoilNo,
|
||||||
|
materialType: coil.material,
|
||||||
|
size: coil.specification,
|
||||||
|
weight: coil.netWeight,
|
||||||
|
pieces: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
addCertificateItem(newRow).then(() => {
|
||||||
|
successCount++;
|
||||||
|
if (successCount + failCount === totalCount) {
|
||||||
|
this.batchAddDialogVisible = false;
|
||||||
|
if (successCount === totalCount) {
|
||||||
|
this.$message.success(`批量新增成功,共新增 ${successCount} 条明细`);
|
||||||
|
} else {
|
||||||
|
this.$message.warning(`批量新增完成,成功 ${successCount} 条,失败 ${failCount} 条`);
|
||||||
|
}
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
failCount++;
|
||||||
|
if (successCount + failCount === totalCount) {
|
||||||
|
this.batchAddDialogVisible = false;
|
||||||
|
if (failCount === totalCount) {
|
||||||
|
this.$message.error('批量新增失败');
|
||||||
|
} else {
|
||||||
|
this.$message.warning(`批量新增完成,成功 ${successCount} 条,失败 ${failCount} 条`);
|
||||||
|
}
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleAddCertificate() {
|
||||||
|
this.certificateForm = {
|
||||||
|
certificateNo: '',
|
||||||
|
contractNo: '',
|
||||||
|
productName: '',
|
||||||
|
standard: '',
|
||||||
|
consignee: '',
|
||||||
|
manufacturer: '',
|
||||||
|
issueDate: '',
|
||||||
|
note: 'D.T=Denu_Test \t T.S=Tensile Strength \t D=弯心直径MandrelDiameter \n Strength \t G.L=拉伸标距GaugeLength \t EL=Percentage Elongation After Fracture',
|
||||||
|
remark: '1.本产品经检验满足订货标准要求。The material has been tested with satisfactory resultsin accordance with the speciicatication \n 2.本质量证明书中空白项目均不作为交货条件。The blank items shouldn’tbe regarded as delivery conditions. \n 3.盖章后生效。The quality certificate willcome into force with a valid stamp.',
|
||||||
|
};
|
||||||
|
this.addCertificateDialogVisible = true;
|
||||||
|
},
|
||||||
|
submitCertificateForm() {
|
||||||
|
addCertificate(this.certificateForm).then(response => {
|
||||||
|
this.$message({
|
||||||
|
message: "新增成功",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
this.addCertificateDialogVisible = false;
|
||||||
|
this.getCertificateList();
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message({
|
||||||
|
message: "新增失败",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleCertificateInfoChange() {
|
||||||
|
if (!this.currentCertificateId) return;
|
||||||
|
updateCertificate(this.currentCertificateInfo).then(response => {
|
||||||
|
this.$message({
|
||||||
|
message: "保存成功",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message({
|
||||||
|
message: "保存失败",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
this.getCertificateDetail(this.currentCertificateId);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSave(row) {
|
||||||
|
updateCertificateItem(row).then(response => {
|
||||||
|
this.$message({
|
||||||
|
message: "保存成功",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message({
|
||||||
|
message: "保存失败,正在重新获取数据",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('是否确认删除该质保书明细?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.buttonLoading = true;
|
||||||
|
delCertificateItem(row.itemId).then(response => {
|
||||||
|
this.$message({
|
||||||
|
message: "删除成功",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
this.buttonLoading = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.selectedRows = selection;
|
||||||
|
},
|
||||||
|
handleBatchDelete() {
|
||||||
|
if (this.selectedRows.length === 0) {
|
||||||
|
this.$message.warning('请至少选择一条明细');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$confirm(`确认删除选中的 ${this.selectedRows.length} 条明细?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const ids = this.selectedRows.map(row => row.itemId).join(',');
|
||||||
|
this.buttonLoading = true;
|
||||||
|
delCertificateItem(ids).then(() => {
|
||||||
|
this.$message.success('批量删除成功');
|
||||||
|
this.getList();
|
||||||
|
this.buttonLoading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error('批量删除失败');
|
||||||
|
this.buttonLoading = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async handlePrint() {
|
||||||
|
this.printCertificateData = this.currentCertificateInfo;
|
||||||
|
this.printItemsData = this.certificateItemList || [];
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const el = this.$refs.certificatePrint.$refs.certificateContent;
|
||||||
|
console.log(el);
|
||||||
|
printPdf(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tab-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
user-select: none;
|
||||||
|
margin-right: 10px;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-button:hover {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs {
|
||||||
|
flex: 1;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 52px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:hover {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-header {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
padding: 4px 4px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item:hover {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
border-color: #c6e2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-color: #409eff;
|
||||||
|
border-bottom-color: #ffffff;
|
||||||
|
color: #409eff;
|
||||||
|
box-shadow: 0 -2px 0 0 #409eff inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
color: #c0c4cc;
|
||||||
|
border-color: #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-info {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-info .date {
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-info {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-detail-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table .cell {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appempty {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-table .cell {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
95
klp-ui/src/views/mes/qc/certificate/lib/printUtils.js
Normal file
95
klp-ui/src/views/mes/qc/certificate/lib/printUtils.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import html2canvas from 'html2canvas';
|
||||||
|
import { PDFDocument } from 'pdf-lib';
|
||||||
|
|
||||||
|
async function generatePdf(domElement) {
|
||||||
|
const originalParent = domElement.parentNode;
|
||||||
|
const originalNext = domElement.nextSibling;
|
||||||
|
|
||||||
|
const paperWidthMm = 297;
|
||||||
|
const paperHeightMm = 210;
|
||||||
|
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.style.cssText = `position:fixed;left:-100000px;top:0;width:${paperWidthMm}mm;height:${paperHeightMm}mm;box-sizing:border-box;background-color:#ffffff;overflow:hidden;`;
|
||||||
|
wrapper.appendChild(domElement);
|
||||||
|
document.body.appendChild(wrapper);
|
||||||
|
|
||||||
|
await document.fonts.ready;
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const containerWidth = domElement.offsetWidth || domElement.scrollWidth;
|
||||||
|
const containerHeight = domElement.offsetHeight || domElement.scrollHeight;
|
||||||
|
|
||||||
|
const canvas = await html2canvas(domElement, {
|
||||||
|
scale: 2,
|
||||||
|
useCORS: true,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
logging: false,
|
||||||
|
width: containerWidth,
|
||||||
|
height: containerHeight,
|
||||||
|
windowWidth: containerWidth,
|
||||||
|
windowHeight: containerHeight
|
||||||
|
});
|
||||||
|
|
||||||
|
const pdfDoc = await PDFDocument.create();
|
||||||
|
const mmToPt = 72 / 25.4;
|
||||||
|
const pageWidthPt = paperWidthMm * mmToPt;
|
||||||
|
const pageHeightPt = paperHeightMm * mmToPt;
|
||||||
|
const page = pdfDoc.addPage([pageWidthPt, pageHeightPt]);
|
||||||
|
|
||||||
|
const pngImage = await pdfDoc.embedPng(canvas.toDataURL('image/png'));
|
||||||
|
const imgWidth = page.getWidth();
|
||||||
|
const imgHeight = (pngImage.height * imgWidth) / pngImage.width;
|
||||||
|
|
||||||
|
if (imgHeight > page.getHeight()) {
|
||||||
|
const scale = page.getHeight() / imgHeight;
|
||||||
|
page.drawImage(pngImage, {
|
||||||
|
x: (page.getWidth() - imgWidth * scale) / 2,
|
||||||
|
y: 0,
|
||||||
|
width: imgWidth * scale,
|
||||||
|
height: page.getHeight()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
page.drawImage(pngImage, {
|
||||||
|
x: 0,
|
||||||
|
y: (page.getHeight() - imgHeight) / 2,
|
||||||
|
width: imgWidth,
|
||||||
|
height: imgHeight
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const pdfBytes = await pdfDoc.save();
|
||||||
|
return pdfBytes;
|
||||||
|
} finally {
|
||||||
|
if (originalParent) {
|
||||||
|
if (originalNext) {
|
||||||
|
originalParent.insertBefore(domElement, originalNext);
|
||||||
|
} else {
|
||||||
|
originalParent.appendChild(domElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wrapper.parentNode) {
|
||||||
|
document.body.removeChild(wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downloadPdf(domElement, fileName) {
|
||||||
|
const pdfBytes = await generatePdf(domElement);
|
||||||
|
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName || `certificate_${new Date().getTime()}.pdf`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function print(domElement) {
|
||||||
|
const pdfBytes = await generatePdf(domElement);
|
||||||
|
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const printWindow = window.open(url, '_blank');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user