<template>
|
<div class="notice">
|
<el-dialog
|
v-model="dialogVisible"
|
:title="title == 'pro' ? '成品二维码打印' : '危化品二维码打印'"
|
width="800px"
|
:before-close="handleClose"
|
:close-on-press-escape="false"
|
:close-on-click-modal="false"
|
>
|
<div style="display: flex;justify-content: space-between">
|
<el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
|
<el-form-item label="条码编号:" >
|
<el-input v-model="state.queryParams.code" placeholder="请输入条码编号" ></el-input>
|
</el-form-item>
|
<el-form-item >
|
<el-button
|
type="primary"
|
@click="getList"
|
>查询</el-button>
|
<el-button
|
type="primary"
|
plain
|
@click="reset"
|
>重置</el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
<div style="display: flex;flex-wrap: wrap">
|
<div v-for="item in state.dataList">
|
<div :id="item.code" style="width: 50mm;height: 40mm;">
|
<div style="display: flex;flex-direction: column;align-items: center;margin-bottom: 5px">
|
<div style="font-size:14px;margin-bottom: 2px" id="codeTitle">{{item.name}}—{{item.productSn}}</div>
|
<vue-qr :size="100" :correctLevel="3" colorDark="black" :margin="0" :auto-color="true" :text="item.code"></vue-qr>
|
<div style="font-size:10px;margin-top: 2px">{{item.code}}</div>
|
<div class="page-break"></div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 打印模板-->
|
<!-- <div style="display: none">-->
|
<!-- <div ref="printTemplate" class="print-template">-->
|
<!-- <div v-for="(item, index) in state.dataList" :key="'print-'+index" class="print-label">-->
|
<!-- <div class="label-content">-->
|
<!-- <div class="label-title">{{ item.name }}—{{ item.productSn }}</div>-->
|
<!-- <!– 确保二维码使用原始数据而不是DOM引用 –>-->
|
<!-- <vue-qr-->
|
<!-- :size="100"-->
|
<!-- :correctLevel="3"-->
|
<!-- colorDark="black"-->
|
<!-- :margin="2"-->
|
<!-- :auto-color="true"-->
|
<!-- :text="item.code">-->
|
<!-- </vue-qr>-->
|
<!-- <div class="label-code">{{ item.code }}</div>-->
|
<!-- </div>-->
|
<!-- </div>-->
|
<!-- </div>-->
|
<!-- </div>-->
|
|
|
<!-- <el-table v-loading="state.loading" :data="state.dataList" :border="true" :show-header="false" height="550" @selection-change="handleSelectionChange">-->
|
<!-- <el-table-column type="selection" width="55" align="center" />-->
|
<!-- <el-table-column >-->
|
<!-- <template #default="scope">-->
|
<!-- <div :id="scope.row.code">-->
|
<!-- <div style="width: 50mm;height: 40mm; ">-->
|
<!-- <div style="font-size:14px;margin-bottom: 2px" id="codeTitle">{{scope.row.name}}—{{scope.row.productSn}}</div>-->
|
<!-- <vue-qr :size="100" :correctLevel="3" colorDark="black" :margin="0" :auto-color="true" :text="scope.row.code"></vue-qr>-->
|
<!-- <div style="font-size:10px;margin-top: 2px">{{scope.row.code}}</div>-->
|
<!-- <div class="page-break"></div>-->
|
<!-- </div>-->
|
<!-- </div>-->
|
<!-- </template>-->
|
<!-- </el-table-column>-->
|
<!-- </el-table>-->
|
<pagination
|
v-model:pager-count="pageCount"
|
v-show="state.total > 0"
|
:total="state.total"
|
:page-sizes="[28,56,84,100]"
|
v-model:page="state.queryParams.pageNum"
|
v-model:limit="state.queryParams.pageSize"
|
@pagination="getList"
|
/>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="handleClose" size="default">取 消</el-button>
|
<el-button type="primary" @click="printLabels" size="default" v-preReClick>打印</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
<script setup>
|
import {reactive, ref, toRefs} from 'vue'
|
import VueQr from 'vue-qr/src/packages/vue-qr.vue'
|
import {ElMessage} from "element-plus";
|
import printJS from 'print-js';
|
import QRCode from 'qrcode';
|
import {addWarehouse, checkName, editWarehouse} from "@/api/hazardousChemicals/warehouse";
|
import {verifyPhone} from "@/utils/validate";
|
import {checkBasicName} from "@/api/hazardousChemicals/basicInfo";
|
import {getProDetail, getProductRecord, getWhProDetail} from "@/api/hazardousChemicals/productRecord";
|
import {getRawDetail, getWhRawDetail} from "@/api/hazardousChemicals/rawRecord";
|
|
const dialogVisible = ref(false);
|
const title = ref("");
|
const busRef = ref();
|
const length = ref()
|
const emit = defineEmits(["getList"]);
|
|
const state = reactive({
|
loading: false,
|
dataList: [],
|
total: 0,
|
queryParams:{
|
pageNum: 1,
|
pageSize: 28,
|
// warehouseId: null,
|
// basicId: null,
|
entryId: null,
|
code: ''
|
},
|
chooseList: []
|
|
})
|
const pageCount = ref(3)
|
const originalList = ref([])
|
const openDialog = async (type,value) => {
|
// state.queryParams.warehouseId =value.warehouseId
|
// state.queryParams.basicId =value.basicId
|
state.queryParams.entryId = value.id
|
title.value = type;
|
await reset()
|
|
console.log('state.dataList',state.dataList)
|
dialogVisible.value = true;
|
}
|
const getRowKey = (row) => {
|
return row.id
|
}
|
|
const onSubmit = async () => {
|
|
}
|
const handleSelectionChange = (val) => {
|
// state.form.studentIds = val.map(item => item.id)
|
state.chooseList = val
|
console.log("选中的行", val)
|
}
|
|
const handleClose = () => {
|
dialogVisible.value = false;
|
emit("getList")
|
}
|
const reset = async () => {
|
state.dataList = [];
|
state.queryParams = {
|
pageNum: 1,
|
pageSize: 28,
|
entryId: state.queryParams.entryId,
|
code: ''
|
}
|
state.total = 0
|
state.chooseList = []
|
await getList()
|
}
|
const getList = async () => {
|
if(title.value == 'pro'){
|
const res = await getWhProDetail(state.queryParams)
|
if(res.code == 200){
|
state.dataList = res.data.list.map(item => {
|
return{
|
...item,
|
name: item.productBasic.name,
|
productSn: item.productBasic.productSn
|
}
|
})
|
state.total = res.data.total
|
originalList.value = state.dataList
|
}else{
|
ElMessage.warning(res.message)
|
}
|
}else {
|
const res = await getWhRawDetail(state.queryParams)
|
if(res.code == 200){
|
state.dataList = res.data.list.map(item => {
|
return{
|
...item,
|
name: item.hazmatBasic.name,
|
productSn: item.hazmatBasic.productSn
|
}
|
})
|
state.total = res.data.total
|
originalList.value = state.dataList
|
}else{
|
ElMessage.warning(res.message)
|
}
|
}
|
|
|
|
}
|
const printLabels = async () => {
|
const existingContainer = document.getElementById('print-container');
|
if (existingContainer) {
|
existingContainer.remove();
|
}
|
// 创建打印容器
|
const printContainer = document.createElement('div');
|
printContainer.id = 'print-container';
|
// 添加内联样式
|
const style = document.createElement('style');
|
style.textContent = `
|
.print-container {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
z-index: 9999;
|
background: white;
|
}
|
.print-page {
|
width: 210mm;
|
height: 297mm;
|
display: flex;
|
flex-wrap: wrap;
|
justify-content: flex-start;
|
align-content: flex-start;
|
padding: 3mm 5mm; /* 考虑打印机物理边距 */
|
box-sizing: border-box;
|
page-break-after: always;
|
}
|
/* 标签容器 - 保证每个标签独立且尺寸固定 */
|
.print-label {
|
width: 50mm; /* 4列布局 (210mm / 4) */
|
max-height: 40mm;
|
height: 40mm; /* 7行布局 (297mm / 7) */
|
box-sizing: border-box;
|
padding: 1mm;
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between; /* 上方空间给标题,中间二维码,底部给编码 */
|
align-items: center;
|
page-break-inside: avoid; /* 防止分页中断标签 */
|
break-inside: avoid;
|
margin-bottom:7px;
|
}
|
.label-title {
|
font-size: 3.5mm;
|
font-weight: bold;
|
color: black;
|
text-align:center;
|
/* 强制单行 + 截断 */
|
// white-space: nowrap;
|
// overflow: hidden;
|
// text-overflow: ellipsis;
|
/* 行高控制 */
|
line-height: 1;
|
min-height: 2.2mm;
|
/* 宽度控制 */
|
width: 100%;
|
max-width: 100%;
|
/* 防止 flex/grid 父级影响 */
|
flex: none;
|
}
|
/* 二维码容器 - 增大二维码尺寸 */
|
.qr-container {
|
flex-grow: 1; /* 占据可用空间 */
|
width: 100%;
|
max-height: 35mm; /* 增大二维码区高度 */
|
min-height: 35mm;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
overflow: hidden;
|
margin-top: -5px
|
}
|
.qrcode-img {
|
max-width: 25mm !important; /* 增大二维码尺寸 */
|
max-height: 25mm !important;
|
object-fit: contain;
|
margin-top: 0px
|
}
|
|
.label-code {
|
margin-top: -15px;
|
|
font-size: 3mm; /* ≈8.5px */
|
text-align: center;
|
width: 100%;
|
box-sizing: border-box;
|
|
|
/* 超长编码处理 */
|
-webkit-line-clamp: 0 !important; /* 解除2行限制 */
|
max-height: none !important;
|
white-space: normal !important;
|
padding: 0.5mm 0; /* 增加纵向空间 */
|
line-height: 0.5; /* 增大行高 */
|
min-height: 1.5mm; /* 确保最小显示区域 */
|
}
|
@media print {
|
body { margin: 0 !important; }
|
@page { margin: 0; size: auto; }
|
}
|
`;
|
|
printContainer.appendChild(style);
|
|
// 添加打印容器到DOM
|
document.body.appendChild(printContainer);
|
|
// 分页处理并生成二维码
|
const pageCount = Math.ceil(state.dataList.length / 28);
|
const qrPromises = []; // 存储所有二维码生成Promise
|
|
for (let pageIndex = 0; pageIndex < pageCount; pageIndex++) {
|
const pageDiv = document.createElement('div');
|
pageDiv.className = 'print-page';
|
|
const startIndex = pageIndex * 28;
|
const endIndex = Math.min(startIndex + 28, state.dataList.length);
|
|
for (let i = startIndex; i < endIndex; i++) {
|
const item = state.dataList[i];
|
const labelDiv = document.createElement('div');
|
labelDiv.className = 'print-label';
|
|
// 二维码容器
|
const qrContainer = document.createElement('div');
|
qrContainer.className = 'qr-container';
|
|
// 创建二维码Canvas
|
const canvasId = `canvas-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
const qrCanvas = document.createElement('canvas');
|
qrCanvas.id = canvasId;
|
qrCanvas.className = 'qrcode-img';
|
qrContainer.appendChild(qrCanvas);
|
|
// 文本内容
|
const titleDiv = document.createElement('div');
|
titleDiv.className = 'label-title';
|
titleDiv.textContent = `${item.name}—${item.productSn}`;
|
|
const codeDiv = document.createElement('div');
|
codeDiv.className = 'label-code';
|
codeDiv.textContent = item.code;
|
|
// 组装标签
|
labelDiv.appendChild(titleDiv);
|
labelDiv.appendChild(qrContainer);
|
labelDiv.appendChild(codeDiv);
|
pageDiv.appendChild(labelDiv);
|
|
// 添加到打印容器
|
labelDiv.style.opacity = '0'; // 初始隐藏防止闪烁
|
|
// 创建二维码生成Promise
|
qrPromises.push(new Promise(resolve => {
|
// 使用requestAnimationFrame处理渲染队列
|
requestAnimationFrame(() => {
|
generateDynamicQRCode(item.code, canvasId).then(resolve);
|
});
|
}));
|
}
|
printContainer.appendChild(pageDiv);
|
}
|
|
try {
|
// 关键修改1:隔离打印环境
|
const printSandbox = document.createElement('div');
|
printSandbox.style.position = 'fixed';
|
printSandbox.style.left = '-9999px';
|
document.body.appendChild(printSandbox);
|
printSandbox.appendChild(printContainer); // 移入沙箱
|
// 显示加载提示
|
const loadingIndicator = document.createElement('div');
|
loadingIndicator.style = `
|
position: fixed;
|
top: 50%;
|
left: 50%;
|
transform: translate(-50%, -50%);
|
background: rgba(0,0,0,0.7);
|
color: white;
|
padding: 20px;
|
border-radius: 5px;
|
z-index: 10000;
|
`;
|
loadingIndicator.textContent = '正在生成二维码,请稍候...';
|
document.body.appendChild(loadingIndicator);
|
|
// 等待所有二维码生成完成
|
await Promise.all(qrPromises);
|
|
// 二维码生成完成后显示所有标签
|
printContainer.querySelectorAll('.print-label').forEach(el => {
|
el.style.opacity = '1';
|
});
|
|
// 添加0.5s延迟确保DOM更新
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
// 移除加载提示
|
loadingIndicator.remove();
|
|
// 执行打印
|
printJS({
|
printable: 'print-container',
|
type: 'html',
|
scanStyles: true,
|
style: '@page { margin: 0; } body { margin: 0; }',
|
onPrintDialogClose: () => {
|
printSandbox.remove();
|
}
|
});
|
} catch (error) {
|
[loadingIndicator, printSandbox].forEach(el => {
|
el?.parentNode?.removeChild(el);
|
});
|
}
|
};
|
// 在生成二维码时根据文本长度自适应
|
function generateDynamicQRCode(text, canvasId) {
|
return new Promise((resolve) => {
|
const canvas = document.getElementById(canvasId);
|
const container = canvas.closest('.qr-container');
|
|
// 根据文本长度确定最佳尺寸
|
const sizeBase = text.length > 40 ? 160 : 200; /* 短文本更大尺寸 */
|
const maxSize = Math.min(
|
container.clientWidth * 0.9,
|
container.clientHeight * 0.9
|
);
|
|
// 最终尺寸
|
const finalSize = Math.min(sizeBase, maxSize);
|
|
QRCode.toCanvas(canvas, text, {
|
width: finalSize,
|
margin: 0,
|
color: {
|
dark: '#000000',
|
light: '#ffffff'
|
}
|
}, () => {
|
// 生成后重新居中
|
canvas.style.margin = `${(container.clientHeight - canvas.height) / 2}px auto`;
|
resolve();
|
});
|
});
|
}
|
|
defineExpose({
|
openDialog
|
});
|
|
</script>
|
|
<style scoped lang="scss">
|
.notice{
|
:deep(.el-form .el-form-item__label) {
|
font-size: 15px;
|
}
|
:deep(.el-dialog__body) {
|
padding: 10px 20px 0 20px;
|
}
|
|
.file {
|
display: flex;
|
flex-direction: column;
|
align-items: flex-start;
|
}
|
|
|
}
|
</style>
|