Merge remote-tracking branch 'origin/0.8.X' into 0.8.X

This commit is contained in:
2026-01-08 14:01:39 +08:00
3 changed files with 240 additions and 2 deletions

View File

@@ -88,7 +88,7 @@
<!-- 第八行生产日期跨3列 -->
<div class="info-grid-item label-cell">生产日期</div>
<div class="info-grid-item value-cell">
<input type="text" class="nob" :value="content.updateTime || ''" />
<input type="text" class="nob" :value="parseTime(content.updateTime, '{y}-{m}-{d}')" />
</div>
</div>
@@ -152,6 +152,8 @@ export default {
return {
logo,
}
},
methods: {
}
}
</script>

View File

@@ -557,7 +557,7 @@ export default {
enterCoilNo: undefined,
currentCoilNo: undefined,
supplierCoilNo: undefined,
dataType: undefined,
dataType: 1,
warehouseId: undefined,
nextWarehouseId: undefined,
qrcodeRecordId: undefined,

View File

@@ -0,0 +1,236 @@
<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>