feat(property-marketing): 优化投放指南下载功能并重构PDF生成逻辑
This commit is contained in:
@ -5,7 +5,7 @@
|
||||
<a-tabs
|
||||
v-model:activeKey="tabData"
|
||||
@tab-click="onSearch"
|
||||
class="a-tab-class"
|
||||
class="a-tab-class ignore-export"
|
||||
default-active-key="placement_guide"
|
||||
>
|
||||
<a-tab-pane key="placement_guide" title="投放指南"></a-tab-pane>
|
||||
@ -16,6 +16,7 @@
|
||||
</div>
|
||||
<!--表单组件搜索-->
|
||||
<listSearchForm
|
||||
class="ignore-export"
|
||||
@onReset="handleReset"
|
||||
v-model:query="query"
|
||||
@onSearch="onSearch"
|
||||
@ -43,18 +44,21 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<a-spin v-if="tabData === 'placement_guide'" :loading="loading" tip="AI分析中...." wrapperClassName="custom-spin-wrapper">
|
||||
<div >
|
||||
|
||||
<a-spin
|
||||
v-show="tabData === 'placement_guide'"
|
||||
:loading="loading"
|
||||
tip="AI分析中...."
|
||||
wrapperClassName="custom-spin-wrapper"
|
||||
>
|
||||
<div>
|
||||
<MonthData :overview="aiResult.overview"></MonthData>
|
||||
<!-- 投放建议-->
|
||||
<PlacementSuggestions :optimization="aiResult.optimization"></PlacementSuggestions>
|
||||
<!-- 投放行动指南-->
|
||||
<ActionGuideDistribution :action_guide="aiResult.action_guide"></ActionGuideDistribution>
|
||||
<!-- <ActionGuideDistribution :action_guide="aiResult.action_guide"></ActionGuideDistribution>-->
|
||||
</div>
|
||||
|
||||
</a-spin>
|
||||
<div v-if="tabData == 'placement_guide'">
|
||||
<div v-if="tabData == 'placement_guide'" class="ignore-export">
|
||||
<a-space class="down-btn">
|
||||
<a-button type="outline" @click="downPage">
|
||||
<template #icon>
|
||||
@ -89,7 +93,10 @@ import {
|
||||
} from '@/api/all/propertyMarketing';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { jsPDF } from 'jspdf';
|
||||
import { AiResultStatus } from '@/views/property-marketing/put-account/investment-guidelines/constants';
|
||||
import { fetchUploadFile } from '@/api/all';
|
||||
import axios from 'axios';
|
||||
|
||||
const tabData = ref('placement_guide');
|
||||
|
||||
@ -121,6 +128,7 @@ const handleUpdateQuery = (payload) => {
|
||||
query.sort_column = payload.column;
|
||||
query.sort_order = payload.order;
|
||||
isGetAi.value = false;
|
||||
loading.value = false;
|
||||
onSearch();
|
||||
};
|
||||
|
||||
@ -143,11 +151,10 @@ const onSearch = async () => {
|
||||
guideHistoryList.value = result?.data?.data || [];
|
||||
}
|
||||
listData.total = result.data.total;
|
||||
if (placementGuideList.value.length > 0) {
|
||||
if (placementGuideList.value.length > 0 && isGetAi.value) {
|
||||
loading.value = true;
|
||||
if (isGetAi.value) {
|
||||
startTask();
|
||||
}
|
||||
syncGetAiResult();
|
||||
startTask();
|
||||
}
|
||||
isGetAi.value = true;
|
||||
};
|
||||
@ -159,15 +166,14 @@ const aiResult = reactive({
|
||||
|
||||
// 下载当前页面
|
||||
const downPage = async () => {
|
||||
await nextTick(); // 确保 DOM 更新完成
|
||||
html2canvas(document.querySelector('.guidelines-data-wrap')).then((canvas) => {
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
const link = document.createElement('a');
|
||||
link.href = imgData;
|
||||
const timestamp = new Date().getTime();
|
||||
link.download = `投放指南-${timestamp}.png`;
|
||||
link.click();
|
||||
});
|
||||
await nextTick();
|
||||
const fileUrl = await uploadPdf();
|
||||
const link = document.createElement('a');
|
||||
link.href = fileUrl;
|
||||
link.download = '投放指南.pdf';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
const saveForm = reactive({
|
||||
account: [],
|
||||
@ -175,33 +181,48 @@ const saveForm = reactive({
|
||||
platform: [],
|
||||
aiResult: [],
|
||||
code: '',
|
||||
file_url: '',
|
||||
});
|
||||
const timerRef = ref<number | null>(null);
|
||||
const startTask = () => {
|
||||
if (timerRef.value !== null) return;
|
||||
timerRef.value = setInterval(async () => {
|
||||
try {
|
||||
const { code, data } = await getAiResult(query);
|
||||
console.log('定时任务执行结果:', data);
|
||||
if (data.ai_result_status === AiResultStatus.SUCCESS || data.ai_result_status === AiResultStatus.FAILED) {
|
||||
stopTask();
|
||||
console.log('任务已完成,定时器已关闭');
|
||||
}
|
||||
if (data.ai_result_status === AiResultStatus.SUCCESS) {
|
||||
loading.value = false;
|
||||
aiResult.optimization = data.result?.optimization?.modules || [];
|
||||
aiResult.action_guide = data.result?.action_guide?.modules || [];
|
||||
aiResult.overview = data.result?.overview?.content_blocks[0] || [];
|
||||
}
|
||||
saveForm.code = data?.code;
|
||||
console.log(aiResult, 'aiResult');
|
||||
} catch (error) {
|
||||
console.error('定时任务执行出错:', error);
|
||||
stopTask();
|
||||
}
|
||||
syncGetAiResult();
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
const syncGetAiResult = async () => {
|
||||
try {
|
||||
const { code, data } = await getAiResult(query);
|
||||
saveForm.file_url = '';
|
||||
console.log('定时任务执行结果:', data);
|
||||
if (data.ai_result_status === AiResultStatus.SUCCESS || data.ai_result_status === AiResultStatus.FAILED) {
|
||||
stopTask();
|
||||
console.log('任务已完成,定时器已关闭');
|
||||
}
|
||||
if (data.ai_result_status === AiResultStatus.SUCCESS) {
|
||||
loading.value = false;
|
||||
aiResult.optimization = data.result?.optimization?.modules || [];
|
||||
aiResult.action_guide = data.result?.action_guide?.modules || [];
|
||||
aiResult.overview = data.result?.overview?.content_blocks[0] || [];
|
||||
} else if (data.ai_result_status === AiResultStatus.FAILED) {
|
||||
Message.error('AI分析失败');
|
||||
aiError();
|
||||
}
|
||||
saveForm.code = data?.code;
|
||||
} catch (error) {
|
||||
aiError();
|
||||
}
|
||||
};
|
||||
|
||||
// ai 检测失败执行方法
|
||||
const aiError = () => {
|
||||
aiResult.optimization = [];
|
||||
aiResult.action_guide = [];
|
||||
aiResult.overview = [];
|
||||
stopTask();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
console.log('handleReset');
|
||||
query.page = 1;
|
||||
@ -226,6 +247,7 @@ onUnmounted(() => {
|
||||
stopTask();
|
||||
});
|
||||
const handleSave = async () => {
|
||||
await uploadPdf();
|
||||
const updatedSaveForm = {
|
||||
...saveForm,
|
||||
ai_result: aiResult,
|
||||
@ -235,6 +257,52 @@ const handleSave = async () => {
|
||||
Message.success(message);
|
||||
}
|
||||
};
|
||||
|
||||
// 上传pdf文件
|
||||
const uploadPdf = async () => {
|
||||
if (saveForm.file_url !== '') {
|
||||
return saveForm.file_url;
|
||||
}
|
||||
const response = await fetchUploadFile({ suffix: 'pdf' });
|
||||
const preSignedUrl = response?.data?.upload_url;
|
||||
const fileUrl = response?.data?.file_url;
|
||||
if (!preSignedUrl) {
|
||||
throw new Error('未能获取有效的预签名上传地址');
|
||||
}
|
||||
saveForm.file_url = fileUrl;
|
||||
const pdfBlob = await generatePDF(); // 生成 PDF Blob
|
||||
console.log('pdfBlob', pdfBlob);
|
||||
await axios.put(preSignedUrl, pdfBlob, {
|
||||
headers: {
|
||||
'Content-Type': pdfBlob.type,
|
||||
},
|
||||
});
|
||||
return fileUrl;
|
||||
};
|
||||
|
||||
const generatePDF = async (): Promise<Blob> => {
|
||||
await nextTick();
|
||||
const sections = document.querySelectorAll('.guidelines-data-wrap');
|
||||
const pdf = new jsPDF('p', 'mm', 'a4');
|
||||
let position = 0;
|
||||
for (const section of sections) {
|
||||
if (section.children.length === 0) continue; // 跳过空内容区域
|
||||
const canvas = await html2canvas(section as HTMLElement, {
|
||||
ignoreElements: (element) => {
|
||||
return element.classList.contains('ignore-export'); // 例如:忽略 class 为 ignore-export 的元素
|
||||
},
|
||||
});
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
const imgProps = pdf.getImageProperties(imgData);
|
||||
const pdfWidth = pdf.internal.pageSize.getWidth();
|
||||
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
|
||||
pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, pdfHeight);
|
||||
position += pdfHeight + 10;
|
||||
}
|
||||
|
||||
return pdf.output('blob');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
@ -242,6 +310,7 @@ onMounted(() => {
|
||||
|
||||
<style lang="scss">
|
||||
@import './style.scss';
|
||||
|
||||
.custom-spin-wrapper {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user