feat(仓库管理): 添加库位二维码导出功能并优化查询参数
新增jsPDF依赖用于生成PDF格式的库位二维码 在仓库概览页面添加导出所有二维码按钮及实现逻辑 统一钢卷查询参数命名(itemName/itemMaterial/itemManufacturer) 修复审批状态同步问题并优化按钮点击事件处理
This commit is contained in:
@@ -56,6 +56,7 @@
|
||||
"js-cookie": "3.0.1",
|
||||
"jsbarcode": "^3.12.1",
|
||||
"jsencrypt": "3.0.0-rc.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"konva": "^10.0.2",
|
||||
"mqtt": "^5.13.3",
|
||||
"nprogress": "0.2.0",
|
||||
|
||||
@@ -24,9 +24,18 @@
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="钢种">
|
||||
<el-input v-model="queryParams.grade" placeholder="请输入钢种" clearable size="small"
|
||||
<el-input v-model="queryParams.itemName" placeholder="请输入钢种" clearable size="small"
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="材质">
|
||||
<el-input v-model="queryParams.itemMaterial" placeholder="请输入材质" clearable size="small"
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="厂家">
|
||||
<el-input v-model="queryParams.itemManufacturer" placeholder="请输入厂家" clearable size="small"
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
|
||||
@@ -115,6 +124,7 @@ export default {
|
||||
pageSize: 10,
|
||||
currentCoilNo: null,
|
||||
grade: null,
|
||||
itemType: 'raw_material',
|
||||
dataType: 1 // 只查询当前数据,不查询历史数据
|
||||
}
|
||||
};
|
||||
@@ -241,7 +251,8 @@ export default {
|
||||
pageSize: 10,
|
||||
currentCoilNo: null,
|
||||
grade: null,
|
||||
dataType: 1
|
||||
dataType: 1,
|
||||
itemType: 'raw_material',
|
||||
};
|
||||
this.getList();
|
||||
},
|
||||
|
||||
@@ -20,23 +20,18 @@
|
||||
style="width: 100%; display: inline-block;" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="产品名称" prop="productName">
|
||||
<el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable
|
||||
<el-form-item label="产品名称" prop="itemName">
|
||||
<el-input v-model="queryParams.itemName" placeholder="请输入产品名称" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="queryParams.spec" placeholder="请输入规格" clearable
|
||||
<el-form-item label="材质" prop="itemMaterial">
|
||||
<el-input v-model="queryParams.itemMaterial" placeholder="请输入材质" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="材质" prop="material">
|
||||
<el-input v-model="queryParams.material" placeholder="请输入材质" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="厂家" prop="factory">
|
||||
<el-input v-model="queryParams.factory" placeholder="请输入厂家" clearable
|
||||
<el-form-item label="厂家" prop="itemManufacturer">
|
||||
<el-input v-model="queryParams.itemManufacturer" placeholder="请输入厂家" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
@@ -445,6 +440,10 @@ export default {
|
||||
startTime: this.queryParams.updateTime?.[0],
|
||||
endTime: this.queryParams.updateTime?.[1],
|
||||
}
|
||||
// 如果没有设置itemType,则设置为raw_material
|
||||
if (!query.itemType) {
|
||||
query.itemType = 'raw_material';
|
||||
}
|
||||
listMaterialCoil(query).then(response => {
|
||||
if (this.querys.warehouseId != 111) {
|
||||
// 排除掉111仓库的
|
||||
|
||||
@@ -59,9 +59,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(row)" v-if="row.auditStatus != 1">审批</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(row)">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(row)">删除</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleApprove(row)" v-if="row.auditStatus != 1">审批</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(row)">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(row)">删除</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
@@ -92,7 +92,7 @@
|
||||
<el-table-column label="厂家" align="center" prop="manufacturer" />
|
||||
<el-table-column label="重量(t)" align="center" prop="netWeight" width="100" />
|
||||
<el-table-column label="库区" align="center" prop="warehouseName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" align="center" width="100" fixed="right" v-if="currentPlan.auditStatus != 1">
|
||||
<el-table-column label="操作" align="center" width="100" v-if="currentPlan.auditStatus != 1">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="small" @click.stop="handleDeleteCoil(scope.row)">删除</el-button>
|
||||
</template>
|
||||
@@ -228,6 +228,11 @@ export default {
|
||||
}).then(res => {
|
||||
this.loading = false;
|
||||
this.buttonLoading = false;
|
||||
|
||||
// 如何当前选中的计划是被审批的计划,需要同步修改数据
|
||||
if (row.planId == this.currentPlan.planId) {
|
||||
this.currentPlan.auditStatus = 1;
|
||||
}
|
||||
this.$message({
|
||||
message: '审批成功',
|
||||
type: 'success'
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
<!-- 右侧仓库信息区域 - 替换为 Bird 组件 -->
|
||||
<div class="warehouse-container" v-if="selectedNodeId" v-loading="rightLoading" element-loading-text="加载中..."
|
||||
element-loading-spinner="el-icon-loading">
|
||||
<!-- 导出所有二维码 -->
|
||||
<button buttonLoading type="primary" @click="exportAllQrcodes">导出二维码</button>
|
||||
<WarehouseBird :warehouse-list="warehouseList" @open-init-dialog="openInitDialog" />
|
||||
</div>
|
||||
|
||||
@@ -35,9 +37,9 @@
|
||||
@mouseleave="resetGridHover">
|
||||
<div v-for="row in 40" :key="`grid-row-${row}`" class="grid-selector-row">
|
||||
<div v-for="col in 10" :key="`grid-col-${col}`" class="grid-selector-cell" :class="{
|
||||
hovered: row <= hoverRow && col <= hoverCol,
|
||||
selected: row <= initForm.rowCount && col <= initForm.columnCount
|
||||
}">
|
||||
hovered: row <= hoverRow && col <= hoverCol,
|
||||
selected: row <= initForm.rowCount && col <= initForm.columnCount
|
||||
}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -58,6 +60,8 @@
|
||||
<script>
|
||||
import { listActualWarehouse, treeActualWarehouseTwoLevel, getActualWarehouse, generateLocations } from "@/api/wms/actualWarehouse";
|
||||
import WarehouseBird from './components/WarehouseBird.vue';
|
||||
import jsPDF from 'jspdf';
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
export default {
|
||||
name: "Overview",
|
||||
@@ -125,6 +129,111 @@ export default {
|
||||
.finally(() => { this.treeLoading = false; });
|
||||
},
|
||||
|
||||
async exportAllQrcodes() {
|
||||
this.buttonLoading = true;
|
||||
if (!this.warehouseList || this.warehouseList.length === 0) {
|
||||
this.$message.warning('暂无库位数据可导出');
|
||||
return;
|
||||
}
|
||||
|
||||
const loadingInstance = this.$loading({
|
||||
lock: true,
|
||||
text: '正在生成二维码PDF,请稍候...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
});
|
||||
|
||||
try {
|
||||
// 初始化PDF (A4纸尺寸: 210mm × 297mm,换算成px约595 × 842)
|
||||
const pdf = new jsPDF({
|
||||
orientation: 'portrait', // 纵向
|
||||
unit: 'mm', // 单位:毫米
|
||||
format: 'a4' // A4格式
|
||||
});
|
||||
|
||||
// 配置项
|
||||
const qrCodeSize = 30; // 二维码尺寸(mm)
|
||||
const margin = 10; // 页面边距(mm)
|
||||
const gap = 10; // 二维码间距(mm)
|
||||
const textOffset = 5; // 文字与二维码间距(mm)
|
||||
const maxPerRow = Math.floor((210 - 2 * margin) / (qrCodeSize + gap)); // 每行最多二维码数
|
||||
const maxPerPage = Math.floor((297 - 2 * margin) / (qrCodeSize + gap + textOffset + 5)) * maxPerRow; // 每页最多二维码数
|
||||
|
||||
let currentX = margin; // 当前X坐标
|
||||
let currentY = margin; // 当前Y坐标
|
||||
let currentIndex = 0; // 当前处理的库位索引
|
||||
const totalCount = this.warehouseList.length;
|
||||
|
||||
// 遍历所有库位生成二维码并添加到PDF
|
||||
for (const [index, item] of this.warehouseList.entries()) {
|
||||
currentIndex = index;
|
||||
|
||||
// 跳过无ID的项
|
||||
if (!item.actualWarehouseId) continue;
|
||||
|
||||
// 生成二维码 (返回base64格式)
|
||||
const qrCodeDataUrl = await QRCode.toDataURL(item.actualWarehouseId, {
|
||||
width: qrCodeSize * 3.78, // 转换mm到px (1mm ≈ 3.78px)
|
||||
margin: 1,
|
||||
errorCorrectionLevel: 'H' // 高容错率
|
||||
});
|
||||
|
||||
// 检查是否需要换行
|
||||
if (currentX + qrCodeSize > 210 - margin) {
|
||||
currentX = margin;
|
||||
currentY += qrCodeSize + gap + textOffset + 5;
|
||||
}
|
||||
|
||||
// 检查是否需要分页
|
||||
if (currentY + qrCodeSize > 297 - margin || (index > 0 && index % maxPerPage === 0)) {
|
||||
pdf.addPage();
|
||||
currentX = margin;
|
||||
currentY = margin;
|
||||
}
|
||||
|
||||
// 添加二维码到PDF
|
||||
pdf.addImage(qrCodeDataUrl, 'PNG', currentX, currentY, qrCodeSize, qrCodeSize);
|
||||
|
||||
// 添加库位名称(自动换行处理)
|
||||
const name = item.actualWarehouseName || '未知库位';
|
||||
const fontSize = 8; // 字体大小
|
||||
pdf.setFontSize(fontSize);
|
||||
// 计算文字宽度,超出则截断
|
||||
const textWidth = pdf.getTextWidth(name);
|
||||
let displayName = name;
|
||||
if (textWidth > qrCodeSize) {
|
||||
const maxTextWidth = qrCodeSize;
|
||||
let tempText = '';
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
if (pdf.getTextWidth(tempText + name[i]) > maxTextWidth) {
|
||||
displayName = tempText + '...';
|
||||
break;
|
||||
}
|
||||
tempText += name[i];
|
||||
}
|
||||
}
|
||||
// 文字居中显示在二维码下方
|
||||
const textX = currentX + (qrCodeSize - pdf.getTextWidth(displayName)) / 2;
|
||||
pdf.text(displayName, textX, currentY + qrCodeSize + textOffset);
|
||||
|
||||
// 更新X坐标
|
||||
currentX += qrCodeSize + gap;
|
||||
}
|
||||
|
||||
// 保存PDF
|
||||
const fileName = `库位二维码_${this.selectedNode.actualWarehouseName}.pdf`;
|
||||
pdf.save(fileName);
|
||||
|
||||
this.$message.success(`成功导出 ${totalCount} 个库位二维码`);
|
||||
} catch (error) {
|
||||
console.error('二维码导出失败:', error);
|
||||
this.$message.error(`二维码导出失败:${error.message}`);
|
||||
} finally {
|
||||
this.buttonLoading = false;
|
||||
loadingInstance.close();
|
||||
}
|
||||
},
|
||||
|
||||
// 树节点点击
|
||||
handleNodeClick(node) {
|
||||
if (this.isSwitching) return;
|
||||
|
||||
Reference in New Issue
Block a user