index.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <template>
  2. <div class="page adffcacjc">
  3. <div class="content">
  4. <div class="c_pdf">
  5. <div id="pdf-content" style="width: 630px;" ref="pdfRef">
  6. <pdf :reportData="reportData" @optionsMaps="optionsMaps" v-if="isTeam"></pdf>
  7. <pdf-user :reportData="reportData" @optionsMaps="optionsMaps" v-else></pdf-user>
  8. </div>
  9. </div>
  10. <div class="c_footer adfac">
  11. <el-button type="defalut" @click="cancel">取消</el-button>
  12. <el-button type="primary" style="margin-left: 20px;" @click="exportToPDF">生成PDF</el-button>
  13. </div>
  14. </div>
  15. <div class="cus_dialog adffcacjc" v-if="reportShow" @click.prevent="closeReportAlert">
  16. <div class="cus_dialog_content adffcac">
  17. <div class="cdc_title">生成PDF</div>
  18. <i class="el-icon-close cdc_close" style="font-size: 20px;color: #393939;" @click.stop="closeReportAlert"></i>
  19. <img src="@/assets/images/agent/report.gif">
  20. <p>{{ reportData.enterpriseName }} - {{ reportData.teamName }}</p>
  21. <p>pdf正在后台生成中,预计需要时间<span>3-5分钟</span>,在此期间可进行其他操作,成功后将在上方出现提示。</p>
  22. <p>生成后可点击对应报告操作中的<span>导出报告</span>进行查看</p>
  23. <div class="zt_btn" @click.stop="closeReportAlert">我知道了</div>
  24. </div>
  25. </div>
  26. </div>
  27. </template>
  28. <script lang="ts" setup name="">
  29. import * as echarts from "echarts";
  30. import { exportPDF } from './exportPDF';
  31. import pdf from './pdf.vue'
  32. import pdfUser from './pdfUser.vue'
  33. const props = defineProps({
  34. isTeam: {
  35. type: Boolean,
  36. default: true
  37. },
  38. reportData:{
  39. type: Object,
  40. default: () => {}
  41. },
  42. reportId:{
  43. type: String,
  44. default: ''
  45. },
  46. reportName:{
  47. type: String,
  48. default: ''
  49. }
  50. })
  51. import _this from '@/main.js'
  52. import { ref, getCurrentInstance, onMounted, nextTick, inject } from 'vue'
  53. const { proxy } = getCurrentInstance();
  54. const reportData = ref(props.reportData);
  55. const reportId = ref(props.reportId);
  56. const reportName = ref(props.reportName);
  57. const reportShow = ref(false);
  58. const pdfRef = ref(null);
  59. const echartsOptions = ref(new Map())
  60. const isTeam = ref(props.isTeam)
  61. import useCommonStore from "@/store_v3/modules/common";
  62. const emit = defineEmits(['cancel','refreshReportList']);
  63. import { updateReportPdfUrl } from '@/api/agent/index.js';
  64. const refreshReportTeamList = inject('refreshTeamList')
  65. const refreshReportPersonList = inject('refreshPersonList')
  66. const cancel = () => {
  67. emit('cancel');
  68. }
  69. const exportToPDF = async () => {
  70. const clonedEl = cloneEl();
  71. await reinitClonedCharts(clonedEl);
  72. try {
  73. reportShow.value = true;
  74. // 滚动到顶部确保完整渲染
  75. window.scrollTo(0, 0);
  76. // 避免异步渲染问题
  77. await new Promise(resolve => setTimeout(resolve, 500));
  78. // const res = await exportPDF('pdf-content', reportName.value+'.pdf');
  79. const res = await exportPDF(clonedEl, reportName.value+'.pdf');
  80. if(res.data && res.data.code === 0) {
  81. updateReportPdfUrl({id:reportId.value, fileUrl:res.data.data}).then(async resu => {
  82. if(resu.code!==0) return proxy.$message.error(res.msg)
  83. proxy.$message.success('生成成功!');
  84. reportShow.value = false;
  85. if(isTeam.value){
  86. refreshReportTeamList(reportData.value.relationId,useCommonStore().$state.teamIndex)
  87. }else{
  88. refreshReportPersonList(reportData.value.relationId,useCommonStore().$state.personIndex)
  89. }
  90. nextTick(()=>{
  91. cancel();
  92. })
  93. })
  94. }
  95. } catch (error) {
  96. proxy.$message.error('报告生成失败!请稍后再试!');
  97. reportShow.value = false;
  98. cancel();
  99. }
  100. };
  101. const cloneEl = () => {
  102. const targetEl = proxy.$refs.pdfRef; // 通过 ref 获取
  103. // 2. 克隆元素并插入到 body 末尾(不触发 Vue 更新)
  104. const clonedEl = targetEl.cloneNode(true);
  105. clonedEl.style.position = "fixed";
  106. clonedEl.style.left = "-9999px"; // 移出屏幕外
  107. clonedEl.style.visibility = "visible"; // 强制显示
  108. clonedEl.style.display = "block"; // 覆盖原隐藏样式
  109. document.body.appendChild(clonedEl);
  110. return clonedEl
  111. }
  112. const optionsMaps = (optionsMap) => {
  113. echartsOptions.value = optionsMap;
  114. }
  115. const reinitClonedCharts = async (clonedEl) => {
  116. // 1. 查找所有图表容器(通过 class 或 data-* 标识)
  117. const chartContainers = clonedEl.querySelectorAll('.pdfEchart');
  118. // 2. 并行重新渲染
  119. await Promise.all(
  120. Array.from(chartContainers).map(async container => {
  121. container.id = container.id+'_copy';
  122. const chartId = container.id;
  123. const originalOptions = echartsOptions.value.get(chartId);
  124. const existingChart = echarts.getInstanceByDom(container);
  125. if (existingChart) existingChart.dispose();
  126. // 5. 重新初始化 ECharts
  127. const chart = echarts.init(container);
  128. chart.setOption(originalOptions);
  129. if(container.id=='zttdznRef_copy'&&echartsOptions.value.get('zttdznRef_copy2')){
  130. chart.setOption(echartsOptions.value.get('zttdznRef_copy2'));
  131. }
  132. await new Promise(resolve => {
  133. chart.on('finished', resolve); // ECharts 3.0+ 支持
  134. setTimeout(resolve, 500); // 双保险
  135. });
  136. })
  137. );
  138. }
  139. const closeReportAlert = () => {
  140. reportShow.value = false;
  141. nextTick(()=>{
  142. cancel();
  143. })
  144. }
  145. onMounted(() => {
  146. });
  147. </script>
  148. <style scoped lang="scss">
  149. .page{
  150. width: 100%;
  151. height: 100vh;
  152. position: fixed;
  153. left: 0;
  154. right: 0;
  155. top: 0;
  156. bottom: 0;
  157. z-index: 99999;
  158. background: rgba($color: #000000, $alpha: .4);
  159. .content{
  160. width: 685px;
  161. height: calc(100% - 100px);
  162. background: #FFFFFF;
  163. padding: 0 20px;
  164. box-sizing: border-box;
  165. display: flex;
  166. flex-direction: column;
  167. .c_pdf{
  168. width: 645px;
  169. flex: 1;
  170. padding-bottom: 20px;
  171. box-sizing: border-box;
  172. overflow-y: auto;
  173. #pdf-content {
  174. width: 100%;
  175. background-color: white;
  176. word-break: break-all;
  177. .cd_box{
  178. width: 100%;
  179. height: 891px;
  180. font-size: 30px;
  181. font-weight: bold;
  182. }
  183. }
  184. }
  185. .c_footer{
  186. justify-content: flex-end;
  187. padding: 20px 0;
  188. border-top: 1px solid #E5E7EB;
  189. }
  190. }
  191. }
  192. .cus_dialog_content{
  193. width: 500px;
  194. background: linear-gradient( 180deg, #FDF2FB 0%, #FFFFFF 100%);
  195. border-radius: 6px;
  196. padding: 36px 68px;
  197. box-sizing: border-box;
  198. position: relative;
  199. .cdc_title{
  200. font-family: PingFang-SC, PingFang-SC;
  201. font-weight: bold;
  202. font-size: 16px;
  203. color: #252525;
  204. line-height: 16px;
  205. text-align: center;
  206. }
  207. .cdc_close{
  208. position: absolute;
  209. right: 30px;
  210. top: 36px;
  211. cursor: pointer;
  212. }
  213. img{
  214. width: 100%;
  215. }
  216. p{
  217. font-family: PingFangSC, PingFang SC;
  218. font-weight: 400;
  219. font-size: 14px;
  220. color: #393939;
  221. line-height: 24px;
  222. text-align: center;
  223. span{
  224. color: #F31616;
  225. }
  226. }
  227. .zt_btn{
  228. margin-top: 40px;
  229. }
  230. }
  231. </style>