<template>
|
<div>
|
<el-button id="button" type="primary" @click="previewReport" style="position: fixed; top: 90px; right: 40px; padding: 20px; background: #409eff; color: white; border: none; border-radius: 5px; cursor: pointer;">
|
预览
|
</el-button>
|
<!-- <el-button id="button" type="primary" @click="printReport" style="position: fixed; top: 90px; right: 20px; padding: 20px; background: #409eff; color: white; border: none; border-radius: 5px; cursor: pointer;">-->
|
<!-- 打印-->
|
<!-- </el-button>-->
|
|
<div style="display: flex; align-items: center; gap: 20px; margin-bottom: 20px;">
|
<span>工程编号:</span>
|
<el-input readonly placeholder="" style="width: 150px" v-model="processId"></el-input>
|
<el-checkbox v-model="config.printLayouts">
|
打印版图
|
</el-checkbox>
|
<el-checkbox v-model="config.printReport">
|
打印报告
|
</el-checkbox>
|
<div style="margin-right: 30px;"></div>
|
<span>布局选择:</span>
|
<el-select v-model="config.columnTypes" style="width: 120px;">
|
<el-option v-for="type in columnTypes" :key="type" :value="type" />
|
</el-select>
|
<el-select v-model="config.rowTypes" style="width: 120px;">
|
<el-option v-for="type in rowTypes" :key="type" :value="type" />
|
</el-select>
|
</div>
|
<div style="display: flex; align-items: center; gap: 20px; margin-bottom: 20px;">
|
<span>显示小片信息:</span>
|
<el-select v-model="config.glassInfo" style="width: 120px;">
|
<el-option v-for="type in glassInfo" :key="type" :value="type" />
|
</el-select>
|
<span>显示切割信息:</span>
|
<el-select v-model="config.cutInfo" style="width: 120px;">
|
<el-option v-for="type in cutInfo" :key="type" :value="type" />
|
</el-select>
|
</div>
|
|
<div v-if="pdfUrl" style="margin-top: 20px;">
|
<iframe
|
:src="pdfUrl"
|
style="width: 100%; height: 800px; border: 1px solid #ddd;"
|
title="PDF预览">
|
</iframe>
|
</div>
|
|
<div ref="printContainer" style="position: relative;">
|
<RectRenderer
|
ref="rectRenderer"
|
:layoutData="layoutData"
|
:gw="currentGw"
|
:gh="currentGh"
|
:printLayout="printLayout"
|
:printWidth="currentPrintWidth"
|
:printHeight="currentPrintHeight"
|
:materialDetails="materialDetails"
|
:state="state"
|
:projectNo="processId"
|
style=""
|
v-if="dataLoaded"
|
/>
|
</div>
|
</div>
|
|
</template>
|
|
<script setup>
|
import { ref, onMounted, watch, reactive, inject } from 'vue';
|
import RectRenderer from './page/RectRenderer.vue';
|
import request from "@/utils/request";
|
import {ElMessage, ElMessageBox} from "element-plus";
|
import requestOptimize from "@/utils/requestOptimize";
|
|
const props = defineProps({
|
project : null,
|
state : null
|
});
|
const printLayout = ref('2rows-2cols');
|
const rectRenderer = ref(null);
|
const savedProjectNo = localStorage.getItem('projectNo');
|
const processId = ref('');
|
const layoutData = ref();
|
const optimizeUse = ref();
|
const reportData = ref();
|
const layoutSet = ref();
|
const materialList = ref();
|
const productList = ref();
|
const dataLoaded = ref(false);
|
const materialDetails = ref();
|
const injectedProjectNo = inject('projectNo', null);
|
const state = ref();
|
const columnTypes = ['一列', '两列']
|
const rowTypes = ['一行', '两行', '三行', '四行']
|
const glassInfo = ['不显示', '显示在右侧', '显示在下侧']
|
const cutInfo = ['不显示', '显示']
|
const filePath = ref('');
|
|
const pdfUrl = ref('');
|
|
// 定义不同布局对应的尺寸
|
const layoutDimensions = {
|
'4rows-2cols': { width: 1000, height: 1000 },
|
'3rows-2cols': { width: 1000, height: 1000 },
|
'3rows-1col': { width: 1000, height: 1000 },
|
'2rows-2cols': { width: 1400, height: 800 }
|
};
|
|
// 当前布局的尺寸
|
const currentGw = ref(layoutDimensions[printLayout.value].width);
|
const currentGh = ref(layoutDimensions[printLayout.value].height);
|
const currentPrintWidth = ref(layoutDimensions[printLayout.value].width);
|
const currentPrintHeight = ref(layoutDimensions[printLayout.value].height);
|
|
const selectLayout = () => {
|
request.post(`/glassOptimize/getOptimizeInfo/${processId.value}`)
|
.then((res) => {
|
if (res.code == 200) {
|
try {
|
layoutData.value = res.data.layouts;
|
optimizeUse.value=res.data.optimizeUse[0];
|
// 添加控制台输出
|
console.log('layoutData:', layoutData.value);
|
console.log('optimizeUse:', optimizeUse.value);
|
} catch (error) {
|
console.error("解析布局数据失败:", error);
|
}
|
} else {
|
console.error("请求失败,状态码:", res.code);
|
}
|
})
|
.catch((error) => {
|
console.error("请求失败:", error);
|
});
|
};
|
|
const selectReportData= () => {
|
request.post(`/glassOptimize/getReportData/${processId.value}`)
|
.then((res) => {
|
if (res.code == 200) {
|
try {
|
reportData.value = res.data.reportData[0];
|
console.log('reportData:', reportData.value);
|
} catch (error) {
|
console.error("解析布局数据失败:", error);
|
}
|
} else {
|
console.error("请求失败,状态码:", res.code);
|
}
|
})
|
.catch((error) => {
|
console.error("请求失败:", error);
|
});
|
};
|
|
|
const selectMaterialData= () => {
|
request.post(`/glassOptimize/materialInfo/${processId.value}`)
|
.then((res) => {
|
if (res.code == 200) {
|
try {
|
materialList.value = res.data.materialList;
|
console.log('materialList:', materialList.value);
|
} catch (error) {
|
console.error("解析布局数据失败:", error);
|
}
|
} else {
|
console.error("请求失败,状态码:", res.code);
|
}
|
})
|
.catch((error) => {
|
console.error("请求失败:", error);
|
});
|
};
|
|
const selectProductData= () => {
|
request.post(`/glassOptimize/getProductList/${processId.value}`)
|
.then((res) => {
|
if (res.code == 200) {
|
try {
|
productList.value = res.data.productList;
|
console.log('productList:', productList.value);
|
} catch (error) {
|
console.error("解析布局数据失败:", error);
|
}
|
} else {
|
console.error("请求失败,状态码:", res.code);
|
}
|
})
|
.catch((error) => {
|
console.error("请求失败:", error);
|
});
|
};
|
|
const generateReport = async() => {
|
try {
|
// 确保有数据可以提交
|
if (!processId) {
|
ElMessage.warning('没有可打印的数据');
|
return;
|
}
|
|
// 映射 rowTypes 到 layoutRows
|
const layoutRowsMap = {
|
'一行': 1,
|
'两行': 2,
|
'三行': 3,
|
'四行': 4
|
};
|
|
// 映射 columnTypes 到 layoutColumns
|
const layoutColumnsMap = {
|
'一列': 1,
|
'两列': 2,
|
};
|
|
// 映射 glassInfo 到 glassInfoShow
|
const glassInfoMap = {
|
'不显示': 0,
|
'显示在右侧': 1,
|
'显示在下侧': 2
|
};
|
|
// 映射 cutInfo 到 cutInfoShow
|
const cutInfoMap = {
|
'不显示': 0,
|
'显示': 1
|
};
|
|
const response = await requestOptimize.post('/api/reports', {
|
fileName: processId.value,
|
projectNo: processId.value,
|
companyName : '1',
|
glassThickness : optimizeUse.value.thickness,
|
glassType : optimizeUse.value.model,
|
quantity : String(optimizeUse.value.processingQuantity),
|
printLayouts : config.printLayouts ? '1' : '0',
|
printReport : config.printReport ? '1' : '0',
|
layouts : layoutData.value,
|
reportData:{
|
rectangleQuantity: reportData.value.rectangleQuantity,
|
otherShapeQuantity: reportData.value.otherShapeQuantity,
|
rectangleArea: reportData.value.rectangleArea,
|
otherShapeArea: reportData.value.otherShapeArea,
|
rectanglePerimeter: reportData.value.rectanglePerimeter,
|
otherShapePerimeter: reportData.value.otherShapePerimeter,
|
materialList: materialList.value,
|
productList: productList.value,
|
},
|
layoutSet: {
|
layoutRows: layoutRowsMap[config.rowTypes] || 1,
|
layoutColumns: layoutColumnsMap[config.columnTypes] || 1,
|
glassInfoShow: glassInfoMap[config.glassInfo] || 0,
|
cutInfoShow: cutInfoMap[config.cutInfo] || 0
|
}
|
|
}, {
|
headers: {
|
'Content-Type': 'application/json'
|
}
|
});
|
|
if (response.code == 200) {
|
ElMessage.success('保存成功');
|
filePath.value = response.data[0];
|
console.log('filePath:', filePath.value);
|
} else {
|
ElMessage.error('保存失败,请稍后再试');
|
}
|
|
} catch (error) {
|
console.error('保存失败:', error);
|
ElMessage.error('保存失败,请稍后再试');
|
}
|
};
|
|
const printReport = async () => {
|
try {
|
await generateReport();
|
if (!filePath.value) {
|
ElMessage.error('未收到有效的PDF文件路径');
|
return;
|
}
|
const encodedFilePath = encodeURIComponent(filePath.value);
|
|
const response = await request.get('/glassOptimize/reports/pdf', {
|
params: { filePath: encodedFilePath },
|
responseType: 'blob',
|
headers: {
|
'Accept': 'application/pdf'
|
}
|
});
|
|
// 检查响应数据是否存在且有效
|
if (!response) {
|
ElMessage.error('未能获取到PDF数据');
|
return;
|
}
|
|
const blob = new Blob([response], { type: 'application/pdf' });
|
|
// 检查 blob 是否有效
|
if (blob.size === 0) {
|
ElMessage.error('接收到空的PDF文件');
|
return;
|
}
|
|
const url = URL.createObjectURL(blob);
|
|
// 创建隐藏的 iframe
|
const iframe = document.createElement('iframe');
|
iframe.style.position = 'fixed';
|
iframe.style.left = '0';
|
iframe.style.top = '0';
|
iframe.style.width = '0';
|
iframe.style.height = '0';
|
iframe.style.border = 'none';
|
iframe.src = url;
|
|
// 标记是否已经清理过资源
|
let isCleanedUp = false;
|
// 清理资源函数
|
const cleanup = () => {
|
if (isCleanedUp) return;
|
isCleanedUp = true;
|
URL.revokeObjectURL(url);
|
if (iframe.parentNode) {
|
iframe.parentNode.removeChild(iframe);
|
}
|
};
|
|
iframe.onload = () => {
|
setTimeout(() => {
|
try {
|
// 确保 iframe 内容已加载完成
|
if (iframe.contentWindow) {
|
iframe.contentWindow.focus();
|
|
// 监听打印事件(某些浏览器支持)
|
const handleAfterPrint = () => {
|
window.removeEventListener('afterprint', handleAfterPrint);
|
// 延迟清理,确保打印完成
|
setTimeout(cleanup, 3000);
|
};
|
|
window.addEventListener('afterprint', handleAfterPrint);
|
|
// 执行打印
|
iframe.contentWindow.print();
|
ElMessage.success('报告生成成功,已启动打印...');
|
|
// 如果浏览器不支持 afterprint 事件,设置超时清理
|
setTimeout(cleanup, 10000);
|
}
|
} catch (printError) {
|
console.error('打印过程中出错:', printError);
|
// 如果程序化打印失败,至少打开 PDF 供用户手动打印
|
window.open(url, '_blank');
|
ElMessage.info('已打开PDF文件,请手动打印');
|
cleanup();
|
}
|
}, 1000); // 给更多时间确保PDF完全渲染
|
};
|
|
iframe.onerror = () => {
|
console.error('PDF 加载失败');
|
ElMessage.error('PDF 文件加载失败,请重试');
|
cleanup();
|
};
|
|
document.body.appendChild(iframe);
|
|
} catch (error) {
|
console.error('打印流程异常:', error);
|
|
if (error.response) {
|
const status = error.response.status;
|
if (status === 400) {
|
ElMessage.error('文件路径无效或不是PDF');
|
} else if (status === 404) {
|
ElMessage.error('PDF 文件不存在');
|
} else {
|
ElMessage.error(`服务器错误 (${status}),请稍后再试`);
|
}
|
} else if (error.request) {
|
ElMessage.error('网络错误,请检查连接');
|
} else {
|
ElMessage.error('打印失败,请稍后再试');
|
}
|
}
|
};
|
|
|
|
const previewReport = async () => {
|
try {
|
await generateReport();
|
if (!filePath.value) {
|
ElMessage.error('未收到有效的PDF文件路径');
|
return;
|
}
|
const encodedFilePath = encodeURIComponent(filePath.value);
|
|
const response = await request.get('/glassOptimize/reports/pdf', {
|
params: { filePath: encodedFilePath },
|
responseType: 'blob',
|
headers: {
|
'Accept': 'application/pdf'
|
}
|
});
|
|
// 检查响应数据是否存在且有效
|
if (!response) {
|
ElMessage.error('未能获取到PDF数据');
|
return;
|
}
|
|
const blob = new Blob([response], { type: 'application/pdf' });
|
|
// 检查 blob 是否有效
|
if (blob.size === 0) {
|
ElMessage.error('接收到空的PDF文件');
|
return;
|
}
|
|
// 创建PDF URL并赋值给pdfUrl用于界面显示
|
const url = URL.createObjectURL(blob);
|
pdfUrl.value = url;
|
|
ElMessage.success('报告生成成功,正在预览...');
|
|
// 可选:添加清理机制,当组件卸载时清理URL
|
// 这里可以使用 onUnmounted 钩子进行清理
|
} catch (error) {
|
console.error('预览流程异常:', error);
|
|
if (error.response) {
|
const status = error.response.status;
|
if (status === 400) {
|
ElMessage.error('文件路径无效或不是PDF');
|
} else if (status === 404) {
|
ElMessage.error('PDF 文件不存在');
|
} else {
|
ElMessage.error(`服务器错误 (${status}),请稍后再试`);
|
}
|
} else if (error.request) {
|
ElMessage.error('网络错误,请检查连接');
|
} else {
|
ElMessage.error('预览失败,请稍后再试');
|
}
|
}
|
};
|
|
const autoPreviewReport = async () => {
|
try {
|
await generateReport();
|
if (!filePath.value) {
|
// 如果没有文件路径,不执行预览
|
return;
|
}
|
const encodedFilePath = encodeURIComponent(filePath.value);
|
|
const response = await request.get('/glassOptimize/reports/pdf', {
|
params: { filePath: encodedFilePath },
|
responseType: 'blob',
|
headers: {
|
'Accept': 'application/pdf'
|
}
|
});
|
|
// 检查响应数据是否存在且有效
|
if (!response) {
|
ElMessage.error('未能获取到PDF数据');
|
return;
|
}
|
|
const blob = new Blob([response], { type: 'application/pdf' });
|
|
// 检查 blob 是否有效
|
if (blob.size === 0) {
|
ElMessage.error('接收到空的PDF文件');
|
return;
|
}
|
|
// 创建PDF URL并赋值给 pdfUrl 用于界面显示
|
const url = URL.createObjectURL(blob);
|
pdfUrl.value = url;
|
|
} catch (error) {
|
console.error('自动预览流程异常:', error);
|
}
|
};
|
|
onMounted(() => {
|
// 优先使用注入的 projectNo,其次使用 props,最后使用 localStorage
|
if (injectedProjectNo) {
|
processId.value = injectedProjectNo.value || injectedProjectNo;
|
} else if (props.project) {
|
processId.value = props.project.projectNumber || '';
|
state.value = props.state;
|
} else if (savedProjectNo) {
|
processId.value = savedProjectNo;
|
}
|
|
if (processId.value) {
|
selectLayout();
|
selectReportData();
|
selectMaterialData();
|
selectProductData();
|
|
// 数据加载完成后自动预览
|
setTimeout(() => {
|
autoPreviewReport();
|
}, 1000); // 延迟1秒确保所有数据加载完成
|
}
|
});
|
|
const config = reactive({
|
columnTypes: '两列',
|
rowTypes: '两行',
|
plain: true,
|
printLayouts: true,
|
printReport: true,
|
glassInfo: '不显示',
|
cutInfo: '显示'
|
|
})
|
|
onMounted(() => {
|
// 优先使用注入的 projectNo,其次使用 props,最后使用 localStorage
|
if (injectedProjectNo) {
|
processId.value = injectedProjectNo.value || injectedProjectNo;
|
} else if (props.project) {
|
processId.value = props.project.projectNumber || '';
|
state.value = props.state;
|
} else if (savedProjectNo) {
|
processId.value = savedProjectNo;
|
}
|
|
if (processId.value) {
|
selectLayout();
|
selectReportData();
|
selectMaterialData();
|
selectProductData();
|
}
|
});
|
|
const handlePrint = () => {
|
if (rectRenderer.value) {
|
rectRenderer.value.print();
|
}
|
};
|
|
const handleLayoutChange = () => {
|
// 更新布局尺寸
|
const dimensions = layoutDimensions[printLayout.value];
|
currentGw.value = dimensions.width;
|
currentGh.value = dimensions.height;
|
currentPrintWidth.value = dimensions.width;
|
currentPrintHeight.value = dimensions.height;
|
|
if (rectRenderer.value) {
|
rectRenderer.value.updateLayout();
|
}
|
};
|
|
// 监听布局变化
|
watch(printLayout, (newVal) => {
|
handleLayoutChange();
|
});
|
</script>
|