- 在base.vue中设置dataType默认值为1 - 在OuterTagPreview.vue中优化生产日期显示格式 - 新增tool.vue组件实现批量导出外标签为PDF功能,包含进度条显示
236 lines
7.2 KiB
Vue
236 lines
7.2 KiB
Vue
<template>
|
||
<div>
|
||
<button
|
||
@click="handleExportAll"
|
||
:disabled="exportLoading"
|
||
style="padding: 8px 16px; cursor: pointer; margin-bottom: 20px;"
|
||
>
|
||
{{ exportLoading ? '导出中...' : '导出所有外标签' }}
|
||
</button>
|
||
|
||
<!-- 进度条容器:导出时显示 -->
|
||
<div v-if="showProgress" class="progress-container" style="margin-bottom: 20px; width: 100%; max-width: 600px;">
|
||
<!-- 进度文本:显示当前进度/总数量 -->
|
||
<div class="progress-text" style="margin-bottom: 8px; font-size: 14px; color: #666;">
|
||
导出进度:{{ currentIndex }}/{{ totalCount }} ({{ progress }}%)
|
||
</div>
|
||
<!-- 进度条背景 -->
|
||
<div class="progress-bar-bg" style="width: 100%; height: 8px; background: #f5f5f5; border-radius: 4px; overflow: hidden;">
|
||
<!-- 进度条进度 -->
|
||
<div
|
||
class="progress-bar"
|
||
style="height: 100%; background: #409eff; transition: width 0.3s ease;"
|
||
:style="{ width: `${progress}%` }"
|
||
></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 预览组件:保留预览,同时用于复用渲染逻辑 -->
|
||
<OuterTagPreview ref="outerTagPreview" :content="current" />
|
||
<!-- 临时渲染容器:用于逐个渲染标签并转换图片 -->
|
||
<div ref="tempTagContainer" style="position: fixed; top: -9999px; left: -9999px; z-index: -9999;"></div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import OuterTagPreview from './LabelRender/OuterTagPreview.vue';
|
||
import { listMaterialCoil } from "@/api/wms/coil";
|
||
import jsPDF from 'jspdf';
|
||
import domtoimage from 'dom-to-image';
|
||
|
||
export default {
|
||
name: 'ToolPanel',
|
||
components: {
|
||
OuterTagPreview,
|
||
},
|
||
data() {
|
||
return {
|
||
list: [],
|
||
pos: 0,
|
||
current: {},
|
||
exportLoading: false,
|
||
// 进度条相关变量
|
||
showProgress: false, // 是否显示进度条
|
||
progress: 0, // 进度值(0-100)
|
||
currentIndex: 0, // 当前处理的标签索引
|
||
totalCount: 0, // 总标签数量
|
||
}
|
||
},
|
||
mounted() {
|
||
this.fetchData();
|
||
},
|
||
methods: {
|
||
// 获取标签数据
|
||
fetchData() {
|
||
listMaterialCoil({
|
||
status: 0,
|
||
dataType: 1,
|
||
materialType: '成品',
|
||
itemType: 'product',
|
||
selectType: 'raw_material',
|
||
pageSize: 100,
|
||
pageNum: 10
|
||
}).then(res => {
|
||
this.list = res.rows || [];
|
||
if (this.list.length > 0) {
|
||
this.current = this.list[0];
|
||
}
|
||
}).catch(err => {
|
||
console.error('获取标签数据失败:', err);
|
||
this.$message.error('获取标签数据失败,请刷新重试');
|
||
})
|
||
},
|
||
|
||
// 核心:复用OuterTagPreview组件渲染标签,再转图片导出PDF
|
||
async handleExportAll() {
|
||
if (this.exportLoading) return;
|
||
if (this.list.length === 0) {
|
||
this.$message.warning('暂无外标签数据可导出');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 初始化进度条
|
||
this.exportLoading = true;
|
||
this.showProgress = true; // 显示进度条
|
||
this.totalCount = this.list.length; // 总数量
|
||
this.currentIndex = 0; // 重置当前索引
|
||
this.progress = 0; // 重置进度
|
||
|
||
const doc = new jsPDF({
|
||
unit: 'mm',
|
||
format: 'a4',
|
||
orientation: 'portrait'
|
||
});
|
||
const tempContainer = this.$refs.tempTagContainer;
|
||
const previewComponent = this.$refs.outerTagPreview;
|
||
|
||
// 循环处理每个标签
|
||
for (let index = 0; index < this.list.length; index++) {
|
||
const item = this.list[index];
|
||
this.current = item; // 更新预览组件的content,触发重新渲染
|
||
|
||
// 等待组件渲染完成
|
||
await this.waitForDOMRender();
|
||
|
||
// 复制预览组件的DOM到临时容器
|
||
const renderedDOM = previewComponent.$el.cloneNode(true);
|
||
tempContainer.innerHTML = '';
|
||
tempContainer.appendChild(renderedDOM);
|
||
|
||
// 再次等待临时容器DOM渲染
|
||
await this.waitForDOMRender();
|
||
|
||
// 检查临时容器是否有内容
|
||
if (!tempContainer.firstChild) {
|
||
console.warn(`第${index+1}个标签DOM为空`, item);
|
||
// 即使当前标签为空,也更新进度
|
||
this.updateProgress(index + 1);
|
||
continue;
|
||
}
|
||
|
||
// 将临时容器的DOM转为图片
|
||
const imgData = await domtoimage.toPng(previewComponent.$el);
|
||
|
||
// 分页:非第一个标签新增页面
|
||
if (index > 0) {
|
||
doc.addPage();
|
||
}
|
||
|
||
// 计算图片在PDF中的尺寸(适配A4,保持原比例)
|
||
const a4Width = 210;
|
||
const a4Height = 297;
|
||
const imgRatio = (previewComponent.$el.offsetWidth || 800) / (previewComponent.$el.offsetHeight || 1100);
|
||
const imgWidth = a4Width - 20;
|
||
const imgHeight = imgWidth / imgRatio;
|
||
|
||
// 确保图片高度不超过A4高度
|
||
const finalImgHeight = imgHeight > a4Height - 20 ? (a4Height - 20) : imgHeight;
|
||
const finalImgWidth = finalImgHeight * imgRatio;
|
||
// 居中显示
|
||
const xPos = (a4Width - finalImgWidth) / 2;
|
||
const yPos = (a4Height - finalImgHeight) / 2;
|
||
|
||
// 插入图片到PDF
|
||
doc.addImage(imgData, 'PNG', xPos, yPos, finalImgWidth, finalImgHeight);
|
||
|
||
// 更新进度条(关键:处理完一个标签就更新)
|
||
this.updateProgress(index + 1);
|
||
}
|
||
|
||
// 保存PDF
|
||
const fileName = `外标签导出_${new Date().toLocaleString().replace(/[/: ]/g, '-')}.pdf`;
|
||
doc.save(fileName);
|
||
this.$message.success('PDF导出成功!');
|
||
|
||
} catch (err) {
|
||
console.error('PDF导出失败详情:', err);
|
||
this.$message.error('PDF导出失败,详情请查看控制台');
|
||
} finally {
|
||
// 重置进度条和加载状态
|
||
this.$refs.tempTagContainer.innerHTML = '';
|
||
this.exportLoading = false;
|
||
this.showProgress = false; // 隐藏进度条
|
||
this.progress = 0;
|
||
this.currentIndex = 0;
|
||
this.totalCount = 0;
|
||
}
|
||
},
|
||
|
||
// 辅助方法:更新进度条
|
||
updateProgress(current) {
|
||
this.currentIndex = current;
|
||
// 计算进度百分比(保留整数)
|
||
this.progress = Math.round((current / this.totalCount) * 100);
|
||
},
|
||
|
||
// 辅助方法:等待DOM渲染完成
|
||
waitForDOMRender(time = 300) {
|
||
return new Promise(resolve => setTimeout(resolve, time));
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
button {
|
||
border: 1px solid #409eff;
|
||
background: #409eff;
|
||
color: white;
|
||
border-radius: 4px;
|
||
transition: all 0.3s;
|
||
}
|
||
button:disabled {
|
||
background: #a0cfff;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* 进度条样式优化(可选:替换内联样式,统一管理) */
|
||
.progress-container {
|
||
:deep(.progress-text) {
|
||
font-size: 14px;
|
||
color: #666;
|
||
margin-bottom: 8px;
|
||
}
|
||
:deep(.progress-bar-bg) {
|
||
width: 100%;
|
||
height: 8px;
|
||
background: #f5f5f5;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
:deep(.progress-bar) {
|
||
height: 100%;
|
||
background: #409eff;
|
||
transition: width 0.3s ease;
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
|
||
/* 确保预览组件的DOM能被正确复制 */
|
||
:deep(.label-container) {
|
||
width: 100%;
|
||
height: auto;
|
||
box-sizing: border-box;
|
||
}
|
||
</style> |