feat(wms): 新增钢卷去向标签和异常信息查看功能
- 新增钢卷去向标签(WhereTag)及相关配置 - 在base.vue中添加钢卷去向选择功能 - 新增异常信息查看弹窗组件(abnormal.vue) - 新增多个仓库视图页面(mini.vue, scrap.vue等) - 在发货单组件中新增Excel导出功能 - 优化标签打印功能,支持多种标签类型 - 修复报表查询时间参数问题
This commit is contained in:
@@ -48,6 +48,7 @@
|
||||
"dom-to-image": "^2.6.0",
|
||||
"echarts": "5.4.0",
|
||||
"element-ui": "2.15.12",
|
||||
"exceljs": "^4.4.0",
|
||||
"file-saver": "2.0.5",
|
||||
"flv.js": "^1.6.2",
|
||||
"fuse.js": "6.4.3",
|
||||
|
||||
44
klp-ui/src/views/wms/coil/allocation.vue
Normal file
44
klp-ui/src/views/wms/coil/allocation.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<el-tabs class="app-container" v-model="activeTab">
|
||||
<el-tab-pane label="待收卷" name="second">
|
||||
<BasePage :qrcode="qrcode" :querys="querys2" :labelType="labelType" :hideWarehouseQuery="hideWarehouseQuery"
|
||||
:hideType="hideType" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="已收卷" name="first">
|
||||
<BasePage :qrcode="qrcode" :querys="querys" :labelType="labelType" :hideWarehouseQuery="hideWarehouseQuery"
|
||||
:hideType="hideType" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasePage from './panels/base.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BasePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrcode: false,
|
||||
activeTab: 'second',
|
||||
querys: {
|
||||
dataType: 1,
|
||||
status: 0,
|
||||
warehouseIds: '2019583656787259393,2019583325311414274,2019583137616310273,2019583429955104769', // 技术部逻辑库
|
||||
// materialType: '废品'
|
||||
},
|
||||
querys2: {
|
||||
dataType: 1,
|
||||
status: 0,
|
||||
nextWarehouseIds: '2019583656787259393,2019583325311414274,2019583137616310273,2019583429955104769', // 技术部逻辑库
|
||||
// materialType: '废品'
|
||||
},
|
||||
hideWarehouseQuery: true,
|
||||
showAbnormal: true,
|
||||
labelType: '2',
|
||||
hideType: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
297
klp-ui/src/views/wms/coil/panels/LabelRender/WhereTag.vue
Normal file
297
klp-ui/src/views/wms/coil/panels/LabelRender/WhereTag.vue
Normal file
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<div class="label-container" :style="{ '--print-scale': printScale }">
|
||||
<table class="material-label-table">
|
||||
<tr>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">卷号</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.currentCoilNo || '' }}</div>
|
||||
</td>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">来源</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.warehouseName || '' }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">班组</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.team || '' }}</div>
|
||||
</td>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">净重</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.netWeight || '' }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">规格</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.specification || '' }}</div>
|
||||
</td>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">材质</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.material || '' }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">卷名</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.itemName || '' }}</div>
|
||||
</td>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">厂家</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.manufacturer || '' }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">时间</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.updateTime || '' }}</div>
|
||||
</td>
|
||||
<td class="label-cell" style="width: 16.67%; padding: 4px;">去向</td>
|
||||
<td class="value-cell" colspan="2" style="width: 33.33%; padding: 4px;">
|
||||
<div class="nob" contenteditable>{{ content.nextWarehouseName || '' }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'WhereTag',
|
||||
props: {
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
coilNumber: '',
|
||||
specification: '',
|
||||
netWeight: '',
|
||||
material: '',
|
||||
nextProcess: '',
|
||||
productionTeam: '',
|
||||
productionDate: '',
|
||||
qrcodeRecordId: '',
|
||||
})
|
||||
},
|
||||
paperWidthMm: {
|
||||
type: Number,
|
||||
default: 100 // 原料码:100mm x 80mm(宽100,高80)
|
||||
},
|
||||
paperHeightMm: {
|
||||
type: Number,
|
||||
default: 80
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
printScale: 1,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 使用 matchMedia 监听打印状态(更可靠)
|
||||
this.printMediaQuery = window.matchMedia('print');
|
||||
this.printMediaQuery.addListener(this.handlePrintMediaChange);
|
||||
|
||||
// 监听打印事件作为备用
|
||||
window.addEventListener('beforeprint', this.handleBeforePrint);
|
||||
window.addEventListener('afterprint', this.handleAfterPrint);
|
||||
|
||||
// 计算初始缩放比例
|
||||
this.$nextTick(() => {
|
||||
this.calculatePrintScale();
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.printMediaQuery) {
|
||||
this.printMediaQuery.removeListener(this.handlePrintMediaChange);
|
||||
}
|
||||
window.removeEventListener('beforeprint', this.handleBeforePrint);
|
||||
window.removeEventListener('afterprint', this.handleAfterPrint);
|
||||
},
|
||||
methods: {
|
||||
handlePrintMediaChange(mq) {
|
||||
if (mq.matches) {
|
||||
// 进入打印模式
|
||||
this.calculatePrintScale();
|
||||
} else {
|
||||
// 退出打印模式
|
||||
this.resetPrintScale();
|
||||
}
|
||||
},
|
||||
handleBeforePrint() {
|
||||
// 延迟计算,确保DOM已更新
|
||||
setTimeout(() => {
|
||||
this.calculatePrintScale();
|
||||
}, 100);
|
||||
},
|
||||
handleAfterPrint() {
|
||||
this.resetPrintScale();
|
||||
},
|
||||
calculatePrintScale() {
|
||||
this.$nextTick(() => {
|
||||
const container = this.$el;
|
||||
if (!container) return;
|
||||
|
||||
// 纸张尺寸:180mm x 100mm
|
||||
const dpi = 96; // 标准 DPI
|
||||
const mmToPx = dpi / 25.4; // 1mm = 3.779527559px
|
||||
|
||||
const paperWidthMm = this.paperWidthMm || 100;
|
||||
const paperHeightMm = this.paperHeightMm || 80;
|
||||
const marginMm = 2;
|
||||
|
||||
const paperWidthPx = paperWidthMm * mmToPx;
|
||||
const paperHeightPx = paperHeightMm * mmToPx;
|
||||
const marginPx = marginMm * mmToPx;
|
||||
|
||||
// 获取内容实际尺寸
|
||||
const rect = container.getBoundingClientRect();
|
||||
let contentWidth = rect.width;
|
||||
let contentHeight = rect.height;
|
||||
|
||||
// 如果尺寸为0或无效,尝试使用 scrollWidth/scrollHeight
|
||||
if (contentWidth === 0 || contentHeight === 0) {
|
||||
contentWidth = container.scrollWidth || contentWidth;
|
||||
contentHeight = container.scrollHeight || contentHeight;
|
||||
}
|
||||
|
||||
// 计算可用空间(减去边距)
|
||||
const availableWidth = paperWidthPx - marginPx * 2;
|
||||
const availableHeight = paperHeightPx - marginPx * 2;
|
||||
|
||||
// 计算缩放比例
|
||||
const scaleX = contentWidth > 0 ? availableWidth / contentWidth : 1;
|
||||
const scaleY = contentHeight > 0 ? availableHeight / contentHeight : 1;
|
||||
|
||||
// 取较小的缩放比例,确保内容完全适配(不超过1,不放大)
|
||||
this.printScale = Math.min(scaleX, scaleY, 1);
|
||||
|
||||
// 设置CSS变量和内联样式
|
||||
container.style.setProperty('--print-scale', this.printScale);
|
||||
container.style.setProperty('--paper-width', `${paperWidthMm}mm`);
|
||||
container.style.setProperty('--paper-height', `${paperHeightMm}mm`);
|
||||
});
|
||||
},
|
||||
resetPrintScale() {
|
||||
this.printScale = 1;
|
||||
if (this.$el) {
|
||||
this.$el.style.setProperty('--print-scale', 1);
|
||||
this.$el.style.removeProperty('--paper-width');
|
||||
this.$el.style.removeProperty('--paper-height');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.label-container {
|
||||
width: 25em;
|
||||
height: 20em;
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
/* 启用Flex布局 */
|
||||
flex-direction: column;
|
||||
/* 子元素垂直排列 */
|
||||
font-family: "SimSun", serif;
|
||||
box-sizing: border-box;
|
||||
/* 确保内边距/边框不影响总尺寸 */
|
||||
}
|
||||
|
||||
.material-label-table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
flex-grow: 1;
|
||||
/* 让表格拉伸,占满剩余垂直空间 */
|
||||
height: 0;
|
||||
/* 配合flex-grow,自动计算高度 */
|
||||
border: 1px solid #333;
|
||||
/* 确保表格有最外层边框 */
|
||||
}
|
||||
|
||||
/* 四列均分宽度,每列占25% */
|
||||
.material-label-table td {
|
||||
border: 1px solid #333;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
/* width: 25%; */
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.label-cell {
|
||||
background-color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.value-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nob {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
font-size: inherit;
|
||||
word-break: break-all;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* 打印样式 - 强制单页,适配100mm x 80mm纸张,保持原有样式不变 */
|
||||
@media print {
|
||||
@page {
|
||||
size: 100mm 80mm;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* 隐藏所有其他内容,只显示标签容器 */
|
||||
body>*:not(.material-label-container) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.material-label-container {
|
||||
/* 防止分页 - 多重保护 */
|
||||
page-break-inside: avoid !important;
|
||||
break-inside: avoid !important;
|
||||
page-break-after: avoid !important;
|
||||
break-after: avoid !important;
|
||||
page-break-before: avoid !important;
|
||||
break-before: avoid !important;
|
||||
orphans: 999 !important;
|
||||
widows: 999 !important;
|
||||
|
||||
/* 应用动态缩放,保持原有样式 */
|
||||
transform: scale(var(--print-scale, 1)) !important;
|
||||
transform-origin: top left !important;
|
||||
|
||||
/* 确保缩放后不超出纸张 */
|
||||
max-width: var(--paper-width, 100mm) !important;
|
||||
max-height: var(--paper-height, 80mm) !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,23 +3,29 @@
|
||||
<!-- 标签预览容器 -->
|
||||
<div class="preview-container" :id="hideActions ? undefined : 'label-preview-container'" ref="labelRef">
|
||||
<MaterialTag
|
||||
v-if="labelType === '2'"
|
||||
v-if="tagType === '2'"
|
||||
:content="content"
|
||||
:paperWidthMm="100"
|
||||
:paperHeightMm="80"
|
||||
/>
|
||||
<OuterTagPreview
|
||||
v-if="labelType === '3'"
|
||||
v-if="tagType === '3'"
|
||||
:content="content"
|
||||
:paperWidthMm="180"
|
||||
:paperHeightMm="100"
|
||||
/>
|
||||
<GalvanizedTag
|
||||
v-if="labelType === '4'"
|
||||
v-if="tagType === '4'"
|
||||
:content="content"
|
||||
:paperWidthMm="180"
|
||||
:paperHeightMm="100"
|
||||
/>
|
||||
<WhereTag
|
||||
v-if="tagType === 'where'"
|
||||
:content="content"
|
||||
:paperWidthMm="100"
|
||||
:paperHeightMm="80"
|
||||
/>
|
||||
<!-- <SampleTagPreview v-if="labelType === '4'" :content="content" />
|
||||
<ForgeTagPreview v-if="labelType === '5'" :content="content" />
|
||||
<SaltSprayTagPreview v-if="labelType === '6'" :content="content" /> -->
|
||||
@@ -40,6 +46,7 @@ import { PDFDocument } from 'pdf-lib';
|
||||
import MaterialTag from './MaterialTag.vue';
|
||||
import OuterTagPreview from './OuterTagPreview.vue';
|
||||
import GalvanizedTag from './GalvanizedTag.vue';
|
||||
import WhereTag from './WhereTag.vue';
|
||||
// import SampleTagPreview from './SampleTagPreview.vue';
|
||||
// import ForgeTagPreview from './ForgeTagPreview.vue';
|
||||
// import SaltSprayTagPreview from './SaltSprayTagPreview.vue';
|
||||
@@ -50,6 +57,7 @@ export default {
|
||||
MaterialTag,
|
||||
OuterTagPreview,
|
||||
GalvanizedTag,
|
||||
WhereTag,
|
||||
// SampleTagPreview,
|
||||
// ForgeTagPreview,
|
||||
// SaltSprayTagPreview,
|
||||
@@ -57,6 +65,32 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
labelType: '2',
|
||||
tagSizeMap: {
|
||||
'2': {
|
||||
width: 100,
|
||||
height: 80,
|
||||
},
|
||||
'3': {
|
||||
width: 180,
|
||||
height: 100,
|
||||
},
|
||||
'4': {
|
||||
width: 180,
|
||||
height: 100,
|
||||
},
|
||||
'where': {
|
||||
width: 100,
|
||||
height: 80,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tagType() {
|
||||
if (this.forceSpecialTag) {
|
||||
return this.forceSpecialTag;
|
||||
}
|
||||
return this.labelType;
|
||||
}
|
||||
},
|
||||
props: {
|
||||
@@ -64,6 +98,10 @@ export default {
|
||||
// type: String,
|
||||
// required: true,
|
||||
// },
|
||||
forceSpecialTag: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
content: {
|
||||
type: Object,
|
||||
required: true,
|
||||
@@ -139,8 +177,8 @@ export default {
|
||||
// 2. 计算纸张尺寸(与批量导出保持一致)
|
||||
// 根据 labelType 判断:'2' 是材料标签(100x80,宽100高80),'3' 是外标(180x100)
|
||||
const isMaterial = this.labelType === '2';
|
||||
const paperWidthMm = isMaterial ? 100 : 180;
|
||||
const paperHeightMm = isMaterial ? 80 : 100;
|
||||
const paperWidthMm = this.tagSizeMap[this.tagType].width || 100;
|
||||
const paperHeightMm = this.tagSizeMap[this.tagType].height || 80;
|
||||
|
||||
// 使用合适的scale值生成高清Canvas(但不超过纸张尺寸)
|
||||
const canvasScale = 3; // 提高清晰度(单张打印)
|
||||
|
||||
267
klp-ui/src/views/wms/coil/panels/abnormal.vue
Normal file
267
klp-ui/src/views/wms/coil/panels/abnormal.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<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="position">
|
||||
<el-select v-model="queryParams.position" placeholder="请选择位置" clearable>
|
||||
<el-option v-for="dict in dict.type.coil_abnormal_position" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="缺陷代码" prop="defectCode">
|
||||
<el-select v-model="queryParams.defectCode" placeholder="请选择缺陷代码" clearable>
|
||||
<el-option v-for="dict in dict.type.coil_abnormal_code" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="程度" prop="degree">
|
||||
<el-select v-model="queryParams.degree" placeholder="请选择程度" clearable>
|
||||
<el-option v-for="dict in dict.type.coil_abnormal_degree" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="coilAbnormalList">
|
||||
<el-table-column label="位置" align="center" prop="position">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.coil_abnormal_position" :value="scope.row.position" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="长度坐标" align="center" prop="lengthCoord" />
|
||||
<el-table-column label="缺陷代码" align="center" prop="defectCode">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.coil_abnormal_code" :value="scope.row.defectCode" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="程度" align="center" prop="degree">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.coil_abnormal_degree" :value="scope.row.degree" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
|
||||
<!-- 添加或修改钢卷异常信息对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="位置" prop="position">
|
||||
<el-radio-group v-model="form.position">
|
||||
<el-radio-button v-for="dict in dict.type.coil_abnormal_position" :key="dict.value" :label="dict.value">{{
|
||||
dict.label }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="长度坐标" prop="lengthCoord">
|
||||
<el-input v-model="form.lengthCoord" placeholder="请输入长度坐标" />
|
||||
</el-form-item>
|
||||
<el-form-item label="缺陷代码" prop="defectCode">
|
||||
<el-radio-group v-model="form.defectCode">
|
||||
<el-radio-button v-for="dict in dict.type.coil_abnormal_code" :key="dict.value" :label="dict.value">{{
|
||||
dict.label }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="程度" prop="degree">
|
||||
<el-radio-group v-model="form.degree">
|
||||
<el-radio-button v-for="dict in dict.type.coil_abnormal_degree" :key="dict.value" :label="dict.value">{{
|
||||
dict.label }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCoilAbnormal, getCoilAbnormal, delCoilAbnormal, addCoilAbnormal, updateCoilAbnormal } from "@/api/wms/coilAbnormal";
|
||||
|
||||
export default {
|
||||
name: "CoilAbnormal",
|
||||
dicts: ['coil_abnormal_code', 'coil_abnormal_position', 'coil_abnormal_degree'],
|
||||
data() {
|
||||
return {
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 钢卷异常信息表格数据
|
||||
coilAbnormalList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
coilId: undefined,
|
||||
position: undefined,
|
||||
lengthCoord: undefined,
|
||||
defectCode: undefined,
|
||||
degree: undefined,
|
||||
judgeLevel: undefined,
|
||||
judgeBy: undefined,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
},
|
||||
judgeOpen: false,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
coilId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
coilId: {
|
||||
handler(newVal, oldVal) {
|
||||
if (!newVal) {
|
||||
this.coilAbnormalList = [];
|
||||
this.total = 0;
|
||||
return;
|
||||
}
|
||||
this.queryParams.coilId = newVal
|
||||
this.handleQuery()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 查询钢卷异常信息列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listCoilAbnormal(this.queryParams).then(response => {
|
||||
this.coilAbnormalList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
abnormalId: undefined,
|
||||
coilId: this.coilId,
|
||||
position: undefined,
|
||||
lengthCoord: undefined,
|
||||
defectCode: undefined,
|
||||
degree: undefined,
|
||||
judgeLevel: undefined,
|
||||
judgeBy: undefined,
|
||||
judgeTime: undefined,
|
||||
remark: undefined,
|
||||
delFlag: undefined,
|
||||
createTime: undefined,
|
||||
createBy: undefined,
|
||||
updateTime: undefined,
|
||||
updateBy: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加钢卷异常信息";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.loading = true;
|
||||
this.reset();
|
||||
const abnormalId = row.abnormalId || this.ids
|
||||
getCoilAbnormal(abnormalId).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.abnormalId != null) {
|
||||
updateCoilAbnormal(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addCoilAbnormal(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const abnormalIds = row.abnormalId || this.ids;
|
||||
this.$modal.confirm('是否确认删除钢卷异常信息编号为"' + abnormalIds + '"的数据项?').then(() => {
|
||||
this.loading = true;
|
||||
return delCoilAbnormal(abnormalIds);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -146,7 +146,17 @@
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
|
||||
<el-table-column label="钢卷去向" align="center" prop="nextWarehouseId" v-if="editNext" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.nextWarehouseId" placeholder="钢卷去向" filterable
|
||||
@change="handleNextWarehouseChange(scope.row)">
|
||||
<el-option v-for="item in dict.type.warehouse_sync" :key="item.value" :value="item.value"
|
||||
:label="item.label" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip/> -->
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
@@ -274,10 +284,10 @@
|
||||
|
||||
<!-- 标签预览弹窗 -->
|
||||
<el-dialog title="标签预览" :visible.sync="labelRender.visible" append-to-body>
|
||||
<label-render :content="labelRender.data" :labelType="labelRender.type" />
|
||||
<label-render :forceSpecialTag="forceSpecialTag" :content="labelRender.data" :labelType="labelRender.type" />
|
||||
</el-dialog>
|
||||
|
||||
<label-render ref="labelRender" v-show="false" :content="labelRender.data" :labelType="labelRender.type" />
|
||||
<label-render :forceSpecialTag="forceSpecialTag" ref="labelRender" v-show="false" :content="labelRender.data" :labelType="labelRender.type" />
|
||||
|
||||
<!-- 批量导出标签PDF弹窗 -->
|
||||
<el-dialog title="批量导出标签PDF" :visible.sync="batchPrint.visible" width="520px" append-to-body>
|
||||
@@ -293,10 +303,14 @@
|
||||
<!-- 渲染容器:屏幕隐藏,仅用于截图生成PDF -->
|
||||
<div ref="batchPdfContainer" class="batch-pdf-root" aria-hidden="true">
|
||||
<div v-for="(item, idx) in batchPrint.list" :key="item.coilId || idx" class="batch-pdf-page">
|
||||
<label-render :content="item" :hideActions="true" />
|
||||
<label-render :content="item" :hideActions="true" :forceSpecialTag="forceSpecialTag" />
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="异常信息" :visible.sync="abnormalOpen" width="90%" append-to-body>
|
||||
<abnormal-list :coil-id="currentCoilId"></abnormal-list>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -335,6 +349,7 @@ import MutiSelect from "@/components/MutiSelect";
|
||||
import html2canvas from 'html2canvas';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { listUser } from "@/api/system/user";
|
||||
import AbnormalList from "./abnormal.vue";
|
||||
|
||||
export default {
|
||||
name: "MaterialCoil",
|
||||
@@ -354,8 +369,9 @@ export default {
|
||||
MemoInput,
|
||||
MutiSelect,
|
||||
OuterTagPreview,
|
||||
AbnormalList,
|
||||
},
|
||||
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status'],
|
||||
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status', 'warehouse_sync'],
|
||||
props: {
|
||||
qrcode: {
|
||||
type: Boolean,
|
||||
@@ -405,6 +421,14 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
editNext: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
forceSpecialTag: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -568,6 +592,8 @@ export default {
|
||||
],
|
||||
title: '详细信息'
|
||||
},
|
||||
abnormalOpen: false,
|
||||
currentCoilId: '',
|
||||
userList: [],
|
||||
};
|
||||
},
|
||||
@@ -598,6 +624,24 @@ export default {
|
||||
this.userList = res.rows || [];
|
||||
});
|
||||
},
|
||||
handleNextWarehouseChange(row) {
|
||||
if (!this.editNext) {
|
||||
return;
|
||||
}
|
||||
updateMaterialCoilSimple(row).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message({
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
});
|
||||
} else {
|
||||
this.$message({
|
||||
message: res.msg || '更新失败',
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
// 打印标签
|
||||
handlePrintLabel(row) {
|
||||
const item = row.itemType === 'product' ? row.product : row.rawMaterial;
|
||||
@@ -701,12 +745,14 @@ export default {
|
||||
}
|
||||
},
|
||||
handleAbnormal(row) {
|
||||
this.$router.push({
|
||||
path: '/quality/detail',
|
||||
query: {
|
||||
coilId: row.coilId,
|
||||
}
|
||||
})
|
||||
// this.$router.push({
|
||||
// path: '/quality/detail',
|
||||
// query: {
|
||||
// coilId: row.coilId,
|
||||
// }
|
||||
// })
|
||||
this.currentCoilId = row.coilId;
|
||||
this.abnormalOpen = true;
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
|
||||
28
klp-ui/src/views/wms/coil/views/jishu.vue
Normal file
28
klp-ui/src/views/wms/coil/views/jishu.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<BasePage :qrcode="qrcode" :querys="querys" :labelType="labelType" :hideWarehouseQuery="hideWarehouseQuery" :hideType="hideType" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasePage from '../panels/base.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BasePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrcode: false,
|
||||
querys: {
|
||||
dataType: 1,
|
||||
status: 0,
|
||||
warehouseId: '2019583656787259393', // 技术部逻辑库
|
||||
// materialType: '废品'
|
||||
},
|
||||
hideWarehouseQuery: true,
|
||||
showAbnormal: true,
|
||||
labelType: '2',
|
||||
hideType: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
28
klp-ui/src/views/wms/coil/views/mini.vue
Normal file
28
klp-ui/src/views/wms/coil/views/mini.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<BasePage :qrcode="qrcode" :querys="querys" :labelType="labelType" :hideWarehouseQuery="hideWarehouseQuery" :hideType="hideType" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasePage from '../panels/base.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BasePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrcode: false,
|
||||
querys: {
|
||||
dataType: 1,
|
||||
status: 0,
|
||||
warehouseId: '2019583325311414274', // 小钢卷仓
|
||||
// materialType: '废品'
|
||||
},
|
||||
hideWarehouseQuery: true,
|
||||
showAbnormal: true,
|
||||
labelType: '2',
|
||||
hideType: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
28
klp-ui/src/views/wms/coil/views/return.vue
Normal file
28
klp-ui/src/views/wms/coil/views/return.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<BasePage :qrcode="qrcode" :querys="querys" :labelType="labelType" :hideWarehouseQuery="hideWarehouseQuery" :hideType="hideType" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasePage from '../panels/base.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BasePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrcode: false,
|
||||
querys: {
|
||||
dataType: 1,
|
||||
status: 0,
|
||||
warehouseId: '2019583137616310273', // 退货仓
|
||||
// materialType: '废品'
|
||||
},
|
||||
hideWarehouseQuery: true,
|
||||
showAbnormal: true,
|
||||
labelType: '2',
|
||||
hideType: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
28
klp-ui/src/views/wms/coil/views/scrap.vue
Normal file
28
klp-ui/src/views/wms/coil/views/scrap.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<BasePage :qrcode="qrcode" :querys="querys" :labelType="labelType" :hideWarehouseQuery="hideWarehouseQuery" :hideType="hideType" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasePage from '../panels/base.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BasePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrcode: false,
|
||||
querys: {
|
||||
dataType: 1,
|
||||
status: 0,
|
||||
warehouseId: '2019583429955104769', // 废品仓
|
||||
// materialType: '废品'
|
||||
},
|
||||
hideWarehouseQuery: true,
|
||||
showAbnormal: true,
|
||||
labelType: '2',
|
||||
hideType: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
41
klp-ui/src/views/wms/coil/where.vue
Normal file
41
klp-ui/src/views/wms/coil/where.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<BasePage
|
||||
:editNext="editNext"
|
||||
:qrcode="qrcode"
|
||||
:querys="querys"
|
||||
:labelType="labelType"
|
||||
:hideWarehouseQuery="hideWarehouseQuery"
|
||||
:forceSpecialTag="forceSpecialTag"
|
||||
:showControl="showControl"
|
||||
:showAbnormal="showAbnormal"
|
||||
:hideType="hideType"></BasePage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasePage from './panels/base.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BasePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrcode: false,
|
||||
querys: {
|
||||
dataType: 1,
|
||||
status: 0,
|
||||
// OnlyScrap: true,
|
||||
// materialType: '废品'
|
||||
},
|
||||
// 强制使用特殊标签
|
||||
forceSpecialTag: 'where',
|
||||
editNext: true,
|
||||
hideWarehouseQuery: true,
|
||||
labelType: '2',
|
||||
hideType: false,
|
||||
showControl: false,
|
||||
showAbnormal: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -186,6 +186,7 @@
|
||||
</div>
|
||||
<el-button type="primary" @click="saveAsImage">保存为图片</el-button>
|
||||
<el-button type="success" @click="printWaybill">打印</el-button>
|
||||
<el-button type="warning" @click="exportExcel">导出Excel</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -194,6 +195,8 @@
|
||||
import domtoimage from 'dom-to-image';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import html2canvas from 'html2canvas';
|
||||
import * as XLSX from 'xlsx';
|
||||
import ExcelJS from 'exceljs';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -289,6 +292,26 @@ export default {
|
||||
}
|
||||
return pageRows;
|
||||
},
|
||||
readWaybill() {
|
||||
const file = this.$refs.file.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.readAsArrayBuffer(file);
|
||||
reader.onload = (e) => {
|
||||
const data = e.target.result;
|
||||
const wb = XLSX.read(data, { type: 'array' });
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
const json = XLSX.utils.sheet_to_eth(ws);
|
||||
console.log(json);
|
||||
// this.localWaybillDetails = json;
|
||||
// this.refreshPagination();
|
||||
};
|
||||
reader.onerror = (e) => {
|
||||
console.error('读取文件失败:', e);
|
||||
};
|
||||
},
|
||||
refreshPagination() {
|
||||
const src = Array.isArray(this.localWaybillDetails) ? this.localWaybillDetails : [];
|
||||
this.totalPages = Math.max(1, Math.ceil(src.length / this.perPage));
|
||||
@@ -460,6 +483,449 @@ export default {
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
},
|
||||
async exportExcelByXLSX() {
|
||||
try {
|
||||
// ===== 1. 构建真实数据(替换占位符,使用实际明细)=====
|
||||
const headerData = [
|
||||
['科伦普发货单'], // 标题行(r=0)
|
||||
[
|
||||
`收货单位:${this.localWaybill.consigneeUnit || ''}`,
|
||||
undefined, undefined, undefined,
|
||||
`${this.localWaybill.deliveryYear || ''}年${this.localWaybill.deliveryMonth || ''}月${this.localWaybill.deliveryDay || ''}日`,
|
||||
undefined, undefined, undefined,
|
||||
`发货单位:${this.localWaybill.senderUnit || ''}`
|
||||
], // 收货/日期/发货单位行(r=1)
|
||||
[
|
||||
`负责人:${this.localWaybill.principal || ''}`,
|
||||
undefined, undefined,
|
||||
`电话:${this.localWaybill.principalPhone || ''}`,
|
||||
undefined, undefined,
|
||||
`合同号:${this.localWaybill.contractCode || ''}`,
|
||||
undefined, undefined,
|
||||
`车牌:${this.localWaybill.licensePlate || ''}`
|
||||
], // 负责人/电话/合同号/车牌行(r=2)
|
||||
["品名", '切边', '包装', '仓库位置', '结算', '原料厂家', '卷号', '规格', '材质', '重量(t)', '单价', '备注'], // 表格表头(r=3)
|
||||
];
|
||||
|
||||
// 表格明细行(替换原有E1/F1等占位符,使用真实明细)
|
||||
const detailRows = this.displayWaybillDetails.map(item => [
|
||||
item.productName || '',
|
||||
item.edgeType || '',
|
||||
item.packageType || '',
|
||||
item.actualWarehouseName || '',
|
||||
item.settlementType || '',
|
||||
item.rawMaterialFactory || '',
|
||||
item.coilNumber || '',
|
||||
item.specification || '',
|
||||
item.material || '',
|
||||
item.weight || '',
|
||||
item.unitPrice || '',
|
||||
item.remark || ''
|
||||
]);
|
||||
|
||||
// 底部备注/取货地点/签名栏
|
||||
const footerData = [
|
||||
[
|
||||
'1、品名:冷硬钢卷(酸连轧)、冷轧钢卷(脱脂退火火拉矫)、镀锌卷板,镀锌管料(镀锌分剪料);2、切边:净边/毛边;3、包装:裸包:周三径四;简包1:周三径四内外护角;简包2:周三径四+防锈纸;普包:周三径四+内外护角+防锈纸+端护板;精包1:周三径四+内外护角+防锈纸+薄膜+端护板+内外护板;精包2:周三径四+内外护角+防锈纸+薄膜+端护板+内外护板+木托。'
|
||||
], // 备注行(r=3+明细行数)
|
||||
[`取货地点:${this.localWaybill.pickupLocation || ''}`], // 取货地点行
|
||||
[
|
||||
`发货:${this.localWaybill.deliveryman || ''}`,
|
||||
undefined, undefined, undefined,
|
||||
`司机:${this.localWaybill.driver || ''}`,
|
||||
undefined, undefined, undefined,
|
||||
`磅房:${this.localWaybill.weightRoom || ''}`
|
||||
] // 签名栏行
|
||||
];
|
||||
|
||||
// 合并所有数据行
|
||||
const data = [...headerData, ...detailRows, ...footerData];
|
||||
const titleRowIdx = 0; // 标题行索引
|
||||
const headerRow1Idx = 1; // 收货单位行索引
|
||||
const headerRow2Idx = 2; // 负责人行索引
|
||||
const tableHeaderIdx = 3; // 表格表头行索引
|
||||
const remarkRowIdx = 3 + detailRows.length; // 备注行索引
|
||||
const pickupRowIdx = remarkRowIdx + 1; // 取货地点行索引
|
||||
const footerRowIdx = pickupRowIdx + 1; // 签名栏行索引
|
||||
|
||||
// ===== 2. 创建工作表并配置基础合并 =====
|
||||
const ws = XLSX.utils.aoa_to_sheet(data);
|
||||
ws["!cellStyles"] = true; // 必须开启才能设置单元格样式
|
||||
ws["!merges"] = [
|
||||
{ s: { c: 0, r: titleRowIdx }, e: { c: 11, r: titleRowIdx } }, // 标题跨列合并(A1-L1)
|
||||
{ s: { c: 0, r: headerRow1Idx }, e: { c: 3, r: headerRow1Idx } }, // 收货单位(A2-D2)
|
||||
{ s: { c: 4, r: headerRow1Idx }, e: { c: 7, r: headerRow1Idx } }, // 日期(E2-H2)
|
||||
{ s: { c: 8, r: headerRow1Idx }, e: { c: 11, r: headerRow1Idx } }, // 发货单位(I2-L2)
|
||||
{ s: { c: 0, r: headerRow2Idx }, e: { c: 2, r: headerRow2Idx } }, // 负责人(A3-C3)
|
||||
{ s: { c: 3, r: headerRow2Idx }, e: { c: 5, r: headerRow2Idx } }, // 电话(D3-F3)
|
||||
{ s: { c: 6, r: headerRow2Idx }, e: { c: 8, r: headerRow2Idx } }, // 合同号(G3-I3)
|
||||
{ s: { c: 9, r: headerRow2Idx }, e: { c: 11, r: headerRow2Idx } }, // 车牌(J3-L3)
|
||||
{ s: { c: 0, r: remarkRowIdx }, e: { c: 11, r: remarkRowIdx } }, // 备注跨列合并(A*_L*)
|
||||
{ s: { c: 0, r: pickupRowIdx }, e: { c: 11, r: pickupRowIdx } }, // 取货地点跨列合并
|
||||
{ s: { c: 0, r: footerRowIdx }, e: { c: 3, r: footerRowIdx } }, // 发货(A*_D*)
|
||||
{ s: { c: 4, r: footerRowIdx }, e: { c: 7, r: footerRowIdx } }, // 司机(E*_H*)
|
||||
{ s: { c: 8, r: footerRowIdx }, e: { c: 11, r: footerRowIdx } }, // 磅房(I*_L*)
|
||||
];
|
||||
|
||||
// ===== 3. 配置列宽(完全匹配Web端表格列宽)=====
|
||||
ws['!cols'] = [
|
||||
{ wpx: 70 }, // 品名
|
||||
{ wpx: 40 }, // 切边
|
||||
{ wpx: 50 }, // 包装
|
||||
{ wpx: 90 }, // 仓库位置
|
||||
{ wpx: 60 }, // 结算
|
||||
{ wpx: 70 }, // 原料厂家
|
||||
{ wpx: 110 }, // 卷号
|
||||
{ wpx: 90 }, // 规格
|
||||
{ wpx: 80 }, // 材质
|
||||
{ wpx: 70 }, // 重量(t)
|
||||
{ wpx: 50 }, // 单价
|
||||
{ wpx: 130 }, // 备注
|
||||
];
|
||||
|
||||
// ===== 4. 逐个配置单元格样式(核心:匹配Web端)=====
|
||||
// 工具函数:生成单元格样式
|
||||
const createCellStyle = (options = {}) => {
|
||||
const defaultStyle = {
|
||||
font: {
|
||||
name: "SimSun", // 宋体(匹配Web端SimSun)
|
||||
sz: options.sz || 16, // 默认字号16px(表格单元格)
|
||||
bold: options.bold ?? true, // 默认加粗(匹配Web端font-weight:900)
|
||||
},
|
||||
alignment: {
|
||||
horizontal: options.horizontal || "center", // 默认水平居中
|
||||
vertical: options.vertical || "center", // 默认垂直居中
|
||||
wrapText: options.wrapText || false, // 默认不换行
|
||||
},
|
||||
border: options.border || {
|
||||
top: { style: "thin", color: { rgb: "000000" } },
|
||||
bottom: { style: "thin", color: { rgb: "000000" } },
|
||||
left: { style: "thin", color: { rgb: "000000" } },
|
||||
right: { style: "thin", color: { rgb: "000000" } },
|
||||
},
|
||||
};
|
||||
return defaultStyle;
|
||||
};
|
||||
|
||||
// 4.1 标题单元格(A1):24px、宋体、加粗、居中、无边框
|
||||
const titleCell = XLSX.utils.encode_cell({ r: titleRowIdx, c: 0 });
|
||||
ws[titleCell].s = {
|
||||
...createCellStyle({
|
||||
sz: 24, // Web端title的24px
|
||||
border: null, // 标题无边框
|
||||
}),
|
||||
};
|
||||
|
||||
// 4.2 头部信息行(收货单位/负责人行):18px、宋体、加粗、居中、无边框
|
||||
const headerCells = [
|
||||
// 收货单位行(A2/E2/I2)
|
||||
XLSX.utils.encode_cell({ r: headerRow1Idx, c: 0 }),
|
||||
XLSX.utils.encode_cell({ r: headerRow1Idx, c: 4 }),
|
||||
XLSX.utils.encode_cell({ r: headerRow1Idx, c: 8 }),
|
||||
// 负责人行(A3/D3/G3/J3)
|
||||
XLSX.utils.encode_cell({ r: headerRow2Idx, c: 0 }),
|
||||
XLSX.utils.encode_cell({ r: headerRow2Idx, c: 3 }),
|
||||
XLSX.utils.encode_cell({ r: headerRow2Idx, c: 6 }),
|
||||
XLSX.utils.encode_cell({ r: headerRow2Idx, c: 9 }),
|
||||
];
|
||||
headerCells.forEach(cellAddr => {
|
||||
if (ws[cellAddr]) {
|
||||
ws[cellAddr].s = {
|
||||
...createCellStyle({
|
||||
sz: 18, // Web端label的18px
|
||||
border: null, // 头部信息无边框
|
||||
}),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 4.3 表格表头(A4-L4):16px、宋体、加粗、居中、带边框
|
||||
for (let c = 0; c < 12; c++) {
|
||||
const cellAddr = XLSX.utils.encode_cell({ r: tableHeaderIdx, c });
|
||||
if (ws[cellAddr]) {
|
||||
ws[cellAddr].s = createCellStyle({ sz: 16 }); // 匹配Web表格16px
|
||||
}
|
||||
}
|
||||
|
||||
// 4.4 表格明细行:16px、宋体、加粗、居中、带边框
|
||||
for (let r = tableHeaderIdx + 1; r < remarkRowIdx; r++) {
|
||||
for (let c = 0; c < 12; c++) {
|
||||
const cellAddr = XLSX.utils.encode_cell({ r, c });
|
||||
if (ws[cellAddr]) {
|
||||
ws[cellAddr].s = createCellStyle({ sz: 16 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.5 备注行:18px、宋体、加粗、居中、自动换行、无边框
|
||||
const remarkCell = XLSX.utils.encode_cell({ r: remarkRowIdx, c: 0 });
|
||||
if (ws[remarkCell]) {
|
||||
ws[remarkCell].s = {
|
||||
...createCellStyle({
|
||||
sz: 18, // Web端备注18px
|
||||
wrapText: true, // 自动换行(匹配Web端text-align: justify)
|
||||
border: null, // 备注无边框
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// 4.6 取货地点行:18px、宋体、加粗、居中、无边框
|
||||
const pickupCell = XLSX.utils.encode_cell({ r: pickupRowIdx, c: 0 });
|
||||
if (ws[pickupCell]) {
|
||||
ws[pickupCell].s = {
|
||||
...createCellStyle({
|
||||
sz: 18, // Web端取货地点18px
|
||||
border: null, // 无边框
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// 4.7 签名栏行:18px、宋体、加粗、居中、无边框
|
||||
const footerCells = [
|
||||
XLSX.utils.encode_cell({ r: footerRowIdx, c: 0 }), // 发货
|
||||
XLSX.utils.encode_cell({ r: footerRowIdx, c: 4 }), // 司机
|
||||
XLSX.utils.encode_cell({ r: footerRowIdx, c: 8 }), // 磅房
|
||||
];
|
||||
footerCells.forEach(cellAddr => {
|
||||
if (ws[cellAddr]) {
|
||||
ws[cellAddr].s = {
|
||||
...createCellStyle({
|
||||
sz: 18, // Web端签名栏18px
|
||||
border: null, // 无边框
|
||||
}),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
console.log(ws);
|
||||
|
||||
// ===== 5. 生成Excel并下载 =====
|
||||
const wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, "科伦普发货单"); // 工作表名称更贴合业务
|
||||
|
||||
// 生成Excel文件(启用样式配置)
|
||||
const excelBuffer = XLSX.write(wb, {
|
||||
bookType: "xlsx",
|
||||
type: "array",
|
||||
cellStyles: true, // 必须开启才能导出样式
|
||||
sheetStubs: true
|
||||
});
|
||||
const blob = new Blob([excelBuffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
|
||||
});
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `科伦普发货单_${this.waybill.waybillNo || Date.now()}.xlsx`; // 文件名更规范
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
// 清理资源
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Excel导出失败:", error);
|
||||
this.$message.error("导出失败,请重试"); // 替换为Vue的message提示
|
||||
}
|
||||
},
|
||||
async exportExcel() {
|
||||
try {
|
||||
// 1. 创建工作簿和工作表
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet('科伦普发货单');
|
||||
worksheet.properties.defaultRowHeight = 25; // 设置默认行高(匹配Web端行高)
|
||||
|
||||
// 2. 构建数据(复用原有业务数据)
|
||||
const title = `科伦普发货单`; // 标题
|
||||
const header1 = { // 收货单位+日期+发货单位
|
||||
consignee: `收货单位:${this.localWaybill.consigneeUnit || ''}`,
|
||||
date: `${this.localWaybill.deliveryYear || ''}年${this.localWaybill.deliveryMonth || ''}月${this.localWaybill.deliveryDay || ''}日`,
|
||||
sender: `发货单位:${this.localWaybill.senderUnit || ''}`
|
||||
};
|
||||
const header2 = { // 负责人+电话+合同号+车牌
|
||||
principal: `负责人:${this.localWaybill.principal || ''}`,
|
||||
phone: `电话:${this.localWaybill.principalPhone || ''}`,
|
||||
contract: `合同号:${this.localWaybill.contractCode || ''}`,
|
||||
license: `车牌:${this.localWaybill.licensePlate || ''}`
|
||||
};
|
||||
const tableHeader = ["品名", '切边', '包装', '仓库位置', '结算', '原料厂家', '卷号', '规格', '材质', '重量(t)', '单价', '备注']; // 表格表头
|
||||
const tableData = this.displayWaybillDetails.map(item => [ // 表格明细
|
||||
item.productName || '',
|
||||
item.edgeType || '',
|
||||
item.packageType || '',
|
||||
item.actualWarehouseName || '',
|
||||
item.settlementType || '',
|
||||
item.rawMaterialFactory || '',
|
||||
item.coilNumber || '',
|
||||
item.specification || '',
|
||||
item.material || '',
|
||||
item.weight || '',
|
||||
item.unitPrice || '',
|
||||
item.remark || ''
|
||||
]);
|
||||
const remark = '1、品名:冷硬钢卷(酸连轧)、冷轧钢卷(脱脂退火火拉矫)、镀锌卷板,镀锌管料(镀锌分剪料);2、切边:净边/毛边;3、包装:裸包:周三径四;简包1:周三径四内外护角;简包2:周三径四+防锈纸;普包:周三径四+内外护角+防锈纸+端护板;精包1:周三径四+内外护角+防锈纸+薄膜+端护板+内外护板;精包2:周三径四+内外护角+防锈纸+薄膜+端护板+内外护板+木托。';
|
||||
const pickupLocation = `取货地点:${this.localWaybill.pickupLocation || ''}`;
|
||||
const footer = { // 签名栏
|
||||
deliveryman: `发货:${this.localWaybill.deliveryman || ''}`,
|
||||
driver: `司机:${this.localWaybill.driver || ''}`,
|
||||
weightRoom: `磅房:${this.localWaybill.weightRoom || ''}`
|
||||
};
|
||||
|
||||
// 3. 写入数据到工作表(按行写入)
|
||||
let rowIdx = 1; // exceljs行号从1开始
|
||||
// 3.1 标题行(第1行)
|
||||
const titleCell = worksheet.getCell(`A${rowIdx}`);
|
||||
titleCell.value = title;
|
||||
worksheet.mergeCells(`A${rowIdx}:L${rowIdx}`); // 合并A1-L1
|
||||
// 3.2 收货单位行(第2行)
|
||||
rowIdx++;
|
||||
worksheet.getCell(`A${rowIdx}`).value = header1.consignee;
|
||||
worksheet.getCell(`E${rowIdx}`).value = header1.date;
|
||||
worksheet.getCell(`I${rowIdx}`).value = header1.sender;
|
||||
worksheet.mergeCells(`A${rowIdx}:D${rowIdx}`); // 收货单位:A2-D2
|
||||
worksheet.mergeCells(`E${rowIdx}:H${rowIdx}`); // 日期:E2-H2
|
||||
worksheet.mergeCells(`I${rowIdx}:L${rowIdx}`); // 发货单位:I2-L2
|
||||
// 3.3 负责人行(第3行)
|
||||
rowIdx++;
|
||||
worksheet.getCell(`A${rowIdx}`).value = header2.principal;
|
||||
worksheet.getCell(`D${rowIdx}`).value = header2.phone;
|
||||
worksheet.getCell(`G${rowIdx}`).value = header2.contract;
|
||||
worksheet.getCell(`J${rowIdx}`).value = header2.license;
|
||||
worksheet.mergeCells(`A${rowIdx}:C${rowIdx}`); // 负责人:A3-C3
|
||||
worksheet.mergeCells(`D${rowIdx}:F${rowIdx}`); // 电话:D3-F3
|
||||
worksheet.mergeCells(`G${rowIdx}:I${rowIdx}`); // 合同号:G3-I3
|
||||
worksheet.mergeCells(`J${rowIdx}:L${rowIdx}`); // 车牌:J3-L3
|
||||
// 3.4 表格表头(第4行)
|
||||
rowIdx++;
|
||||
tableHeader.forEach((text, colIdx) => {
|
||||
const colLetter = String.fromCharCode(65 + colIdx); // A=65, B=66...
|
||||
worksheet.getCell(`${colLetter}${rowIdx}`).value = text;
|
||||
});
|
||||
// 3.5 表格明细行
|
||||
tableData.forEach(item => {
|
||||
rowIdx++;
|
||||
item.forEach((text, colIdx) => {
|
||||
const colLetter = String.fromCharCode(65 + colIdx);
|
||||
worksheet.getCell(`${colLetter}${rowIdx}`).value = text;
|
||||
});
|
||||
});
|
||||
// 3.6 备注行
|
||||
rowIdx++;
|
||||
const remarkCell = worksheet.getCell(`A${rowIdx}`);
|
||||
remarkCell.value = remark;
|
||||
worksheet.mergeCells(`A${rowIdx}:L${rowIdx}`); // 合并A*_L*
|
||||
// 3.7 取货地点行
|
||||
rowIdx++;
|
||||
const pickupCell = worksheet.getCell(`A${rowIdx}`);
|
||||
pickupCell.value = pickupLocation;
|
||||
worksheet.mergeCells(`A${rowIdx}:L${rowIdx}`); // 合并A*_L*
|
||||
// 3.8 签名栏行
|
||||
rowIdx++;
|
||||
worksheet.getCell(`A${rowIdx}`).value = footer.deliveryman;
|
||||
worksheet.getCell(`E${rowIdx}`).value = footer.driver;
|
||||
worksheet.getCell(`I${rowIdx}`).value = footer.weightRoom;
|
||||
worksheet.mergeCells(`A${rowIdx}:D${rowIdx}`); // 发货:A*_D*
|
||||
worksheet.mergeCells(`E${rowIdx}:H${rowIdx}`); // 司机:E*_H*
|
||||
worksheet.mergeCells(`I${rowIdx}:L${rowIdx}`); // 磅房:I*_L*
|
||||
|
||||
// 4. 配置列宽(完全匹配Web端)
|
||||
const columnWidths = [70, 40, 50, 90, 60, 70, 110, 90, 80, 70, 50, 130];
|
||||
worksheet.columns = columnWidths.map((width, idx) => ({
|
||||
key: `col${idx + 1}`,
|
||||
width: width / 5 // exceljs的width单位是字符宽度,转换为px(约1px=0.072字符)
|
||||
}));
|
||||
|
||||
// 5. 配置样式(核心:匹配Web端)
|
||||
// 工具函数:设置单元格样式
|
||||
const setCellStyle = (cell, options = {}) => {
|
||||
// 默认样式:宋体、加粗、居中
|
||||
cell.font = {
|
||||
name: 'SimSun', // 宋体(匹配Web端)
|
||||
size: options.size || 16, // 默认16px(表格单元格)
|
||||
bold: options.bold ?? true // 默认加粗
|
||||
};
|
||||
cell.alignment = {
|
||||
horizontal: options.horizontal || 'left', // 水平居中
|
||||
vertical: options.vertical || 'middle', // 垂直居中
|
||||
wrapText: options.wrapText || false // 自动换行
|
||||
};
|
||||
// 边框:表格单元格带边框,其他区域无边框
|
||||
if (options.border) {
|
||||
cell.border = {
|
||||
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
left: { style: 'thin', color: { argb: 'FF000000' } },
|
||||
right: { style: 'thin', color: { argb: 'FF000000' } }
|
||||
};
|
||||
} else {
|
||||
cell.border = null; // 无边框
|
||||
}
|
||||
};
|
||||
|
||||
// 5.1 标题样式(24px、宋体、加粗、居中、无边框)
|
||||
setCellStyle(worksheet.getCell('A1'), { size: 24, border: false, horizontal: 'center' });
|
||||
// 5.2 头部信息样式(18px、宋体、加粗、居中、无边框)
|
||||
setCellStyle(worksheet.getCell(`A2`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`E2`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`I2`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`A3`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`D3`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`G3`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`J3`), { size: 18, border: false });
|
||||
// 5.3 表格表头样式(16px、宋体、加粗、居中、带边框)
|
||||
for (let col = 0; col < 12; col++) {
|
||||
const colLetter = String.fromCharCode(65 + col);
|
||||
setCellStyle(worksheet.getCell(`${colLetter}4`), { size: 16, border: true, horizontal: 'center' });
|
||||
}
|
||||
// 5.4 表格明细样式(16px、宋体、加粗、居中、带边框)
|
||||
for (let r = 5; r < rowIdx - 2; r++) { // 明细行范围:第5行到备注行前一行
|
||||
for (let col = 0; col < 12; col++) {
|
||||
const colLetter = String.fromCharCode(65 + col);
|
||||
setCellStyle(worksheet.getCell(`${colLetter}${r}`), { size: 16, border: true, horizontal: 'center' });
|
||||
}
|
||||
}
|
||||
// 5.5 备注样式(18px、宋体、加粗、居中、自动换行、无边框)
|
||||
setCellStyle(remarkCell, { size: 18, border: false, wrapText: true });
|
||||
// 5.6 取货地点样式(18px、宋体、加粗、居中、无边框)
|
||||
setCellStyle(pickupCell, { size: 18, border: false });
|
||||
// 5.7 签名栏样式(18px、宋体、加粗、居中、无边框)
|
||||
setCellStyle(worksheet.getCell(`A${rowIdx}`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`E${rowIdx}`), { size: 18, border: false });
|
||||
setCellStyle(worksheet.getCell(`I${rowIdx}`), { size: 18, border: false });
|
||||
|
||||
// 第一行的行高
|
||||
const row1 = worksheet.getRow(1);
|
||||
row1.height = 30;
|
||||
// 备注行的行高
|
||||
const remarkRow = worksheet.getRow(12);
|
||||
remarkRow.height = 100;
|
||||
|
||||
// 6. 生成Excel文件并下载
|
||||
const buffer = await workbook.xlsx.writeBuffer(); // 生成二进制buffer
|
||||
const blob = new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `科伦普发货单_${this.waybill.waybillNo || Date.now()}.xlsx`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
// 清理资源
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Excel导出失败:', error);
|
||||
this.$message.error('导出失败,请重试');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +205,8 @@ export default {
|
||||
}
|
||||
listCoilWithIds({
|
||||
...this.queryParams,
|
||||
byCreateTimeStart: undefined,
|
||||
byCreateTimeEnd: undefined,
|
||||
coilIds: coilIds,
|
||||
}).then(res => {
|
||||
this.list = res.rows
|
||||
|
||||
@@ -205,6 +205,8 @@ export default {
|
||||
}
|
||||
listCoilWithIds({
|
||||
...this.queryParams,
|
||||
byCreateTimeStart: undefined,
|
||||
byCreateTimeEnd: undefined,
|
||||
coilIds: coilIds,
|
||||
}).then(res => {
|
||||
this.list = res.rows
|
||||
|
||||
Reference in New Issue
Block a user