Przeglądaj źródła

feat:新增灵活模板报告pdf下载,修复修改special中内容修改后保存不成功bug,修改指标和special相邻时,内如回填时,正则解析失效的bug

韩洋 3 miesięcy temu
rodzic
commit
52c88d935b

BIN
src.zip


+ 44 - 4
src/modules/chat-item/Answer.vue

@@ -178,7 +178,7 @@ import Reference from './Reference.vue';
 import { useThemeStore } from '../../base/store/use-theme';
 import { THEME_LIGHT, THEME_DARK, THEME_GREEN, THEME_PARTY } from '../../base/variables';
 import { getIcon } from '../common/use-icon-map';
-import { downloadReport, downloadExcel, toEvaluation } from '../conversation-dialog/api-dialog';
+import { downloadReport, downloadReportPDF, downloadExcel, toEvaluation } from '../conversation-dialog/api-dialog';
 import { getMergeExcel, getCheckedErrorList } from '../table-dialog/api-dialog';
 import { mergeTableData } from '../../modules/table-dialog/api-dialog';
 import { formatMdToHtmlForView } from '../conversation-dialog/format-html-md-switch-for-report';
@@ -491,10 +491,15 @@ const handleReportEdit = () => {
   }
 };
 
-const handleDwonload = () => {
-  if (props.dialog.record_id) {
+const handleDwonload = (type = 'word') => {
+  if (props.dialog.record_id || report_file.value?.record_id) {
     // downloadReport({ record_id: props.dialog.record_id });
-    toDownload();
+    if (type === 'word') {
+      toDownload();
+    }
+    if (type === 'pdf') {
+      toDownloadPDF();
+    }
   }
 };
 const handleSelect = (v) => {
@@ -550,6 +555,9 @@ const toDownload = () => {
   if (props.dialog.report_files && props.dialog.report_files.record_id) {
     record_id = props.dialog.report_files.record_id;
   }
+  if (report_file.value && report_file.value.record_id) {
+    record_id = report_file.value.record_id;
+  }
   if (props.dialog?.record_id) {
     record_id = props.dialog.record_id;
   }
@@ -574,6 +582,38 @@ const toDownload = () => {
       });
   }
 };
+const toDownloadPDF = () => {
+  let record_id;
+  if (props.dialog.report_files && props.dialog.report_files.record_id) {
+    record_id = props.dialog.report_files.record_id;
+  }
+  if (report_file.value && report_file.value.record_id) {
+    record_id = report_file.value.record_id;
+  }
+  if (props.dialog?.record_id) {
+    record_id = props.dialog.record_id;
+  }
+
+  if (record_id) {
+    is_download_loading.value = true;
+    downloadReportPDF({ record_id })
+      .then((res) => {
+        const url = URL.createObjectURL(res.data);
+        const link = document.createElement('a');
+        link.href = url;
+        // console.log('下载报告url', url);
+        link.download = props.dialog?.report_files?.file_name || `报告_${record_id}`;
+        // console.log('下载报告名称', link.download);
+        link.click();
+      })
+      .catch((e) => {
+        console.log(e);
+      })
+      .finally(() => {
+        is_download_loading.value = false;
+      });
+  }
+};
 const toDownloadExcel = () => {
   const record_id = props.dialog.report_excels.record_id;
   if (record_id) {

+ 19 - 4
src/modules/chat-item/DialogActions.vue

@@ -13,7 +13,14 @@
       </div>
       <div class="action-btn-item" v-if="show_download">
         <a-tooltip content="下载" position="top">
-          <icon-download size="18" @click="handleDwonload" />
+          <!-- <icon-download size="18" @click="handleDwonload" /> -->
+          <a-dropdown trigger="hover" @select="toDownload">
+            <icon-download style="cursor: pointer" size="18" />
+            <template #content>
+              <a-doption value="word">word</a-doption>
+              <a-doption value="pdf">pdf</a-doption>
+            </template>
+          </a-dropdown>
         </a-tooltip>
       </div>
       <div class="action-btn-item" v-if="show_like">
@@ -109,7 +116,7 @@ const show_edit = computed(() => {
  * 下载按钮只有报告生成中,灵活模板有,因为没有名称
  * */
 const show_download = computed(() => {
-  if (props.type === 'bgsc' && props.dialog?.record_id && !props.dialog?.report_files?.file_name) {
+  if (props.type === 'bgsc' && props.dialog?.client_request_id / 1 == 5) {
     return true;
   } else {
     return false;
@@ -144,8 +151,16 @@ const handleEdit = () => {
 /**
  * 下载
  */
-const handleDwonload = () => {
-  emit('download');
+// const handleDwonload = () => {
+//   emit('download');
+// };
+
+const toDownload = (value) => {
+  if (value === 'word') {
+    emit('download', 'word');
+  } else if (value === 'pdf') {
+    emit('download', 'pdf');
+  }
 };
 /**
  * 重试

+ 23 - 0
src/modules/conversation-dialog/api-dialog.js

@@ -274,6 +274,29 @@ export const downloadReport = ({ record_id }) => {
       });
   });
 }
+/**
+ * 报告下载
+ * */
+export const downloadReportPDF = ({ record_id }) => {
+  return new Promise((resolve, reject) => {
+    ajax
+      .get(
+        `/iaserverapi/v1/report/markdown-to-pdf/?record_id=${record_id}`,
+        {
+          headers: {
+            'Content-Type': 'application/json',
+          },
+          responseType: 'blob'
+        }
+      )
+      .then((res) => {
+        resolve({ code: 200, data: res });
+      })
+      .catch((err) => {
+        reject(err);
+      });
+  });
+}
 /**
  * 报告下载
  * */

+ 2 - 0
src/modules/conversation-dialog/format-dialog-answer-from-history.js

@@ -263,6 +263,8 @@ export default function formatDialogAnswerFromHistory(item, type, fileLength = 0
           // file_size: '12.2KB',
           record_id: item.id
         }
+      } else if (item.request_id / 1 === 5 || item.request_id === "create_report_by_template_id") {
+        history_item.client_request_id = 5
       }
 
       if (item.references && item.references.length && item.request_id !== "create_outline_by_word") {

+ 17 - 8
src/modules/conversation-dialog/format-html-md-switch-for-report.js

@@ -53,18 +53,28 @@ export const formatMdToHtml = (exist_metrics, md) => {
   if (!md) return '';
   // 创建存在的指标名称集合,用于快速查找
   const existMetricNames = new Set(exist_metrics.map(metric => metric.field_name));
-  console.log(existMetricNames, 999)
 
+  const placeholderText = '请输入特别提示词,以此统一文本结构、规范输出样式';
 
-  // 先解析markdown为html
-  let html = marked.parse(md);
+  // 直接在 markdown 解析前处理特殊标签,使用 HTML 注释作为占位符
+  // 这样 markdown 解析器会保留注释,不会把 (special) 当成链接
+  let specialCounter = 0;
+  const specialInputs = [];
 
-  // 处理 (special) 标签,转换为 input 元素
-  html = html.replace(/\(special\)([\s\S]*?)\(\/special\)/gi, (match, content) => {
-    const placeholderText = '请输入特别提示词,以此统一文本结构、规范输出样式';
-    return `<input type="text" data-special="true" value="${content.trim()}" placeholder="${placeholderText}" contenteditable="true" style="background-color: #fff3cd; border: 1px solid #ffeeba; border-radius: 4px; padding: 2px 8px; color: #856404; font-weight: 500; min-width: 360px; display: inline-block; outline: none; font-size: inherit; font-family: inherit; vertical-align: middle; box-sizing: border-box; margin-left:5px;"/>`;
+  // 使用 HTML 注释作为占位符,避免被 markdown 解析器处理
+  md = md.replace(/\(special\)([\s\S]*?)\(\/special\)/gi, (match, content) => {
+    const inputHtml = `<input type="text" data-special="true" autocomplete="off" value="${content.trim()}" placeholder="${placeholderText}" contenteditable="false" style="background-color: #fff3cd; border: 1px solid #ffeeba; border-radius: 4px; padding: 2px 8px; color: #856404; font-weight: 500; min-width: 360px; display: inline-block; outline: none; font-size: inherit; font-family: inherit; vertical-align: middle; box-sizing: border-box; margin-left:5px;"/>`;
+    specialInputs.push(inputHtml);
+    return `<!--SPECIAL_INPUT_${specialCounter}-->`;
   });
 
+  // 再解析 markdown 为 html
+  let html = marked.parse(md);
+
+  // 将 HTML 注释替换为实际的 input 元素
+  for (let i = 0; i < specialInputs.length; i++) {
+    html = html.replace(`<!--SPECIAL_INPUT_${i}-->`, specialInputs[i]);
+  }
 
   // 只转换存在的指标
   html = html.replace(/\[\[([^_]+?)_([^\]]+?)\]\]/g, (match, text, uid) => {
@@ -84,7 +94,6 @@ export const formatMdToHtml = (exist_metrics, md) => {
     // 不存在则返回原始文本
     return match;
   });
-  console.log(html, 99999)
 
   return html;
 };

+ 46 - 1
src/modules/report-template/editor-model/EditorDetail.vue

@@ -191,6 +191,17 @@ const emit = defineEmits([
 
 // ============ Refs ============
 const editorRef = ref(null);
+
+watch(
+  () => props.modelValue,
+  (newValue) => {
+    if (newValue && editorRef.value) {
+      nextTick(() => {
+        bindEventsForSpecialInputs();
+      });
+    }
+  }
+);
 const containerRef = ref(null); // 👈 新增:用于定位浮层
 const mentionDropdownRef = ref(null);
 // 记录编辑器内“最后一次有效光标”,用于复制/插入等操作(避免点击菜单导致 selection 丢失)
@@ -468,6 +479,7 @@ function undo() {
     } else {
       syncMentionsData();
     }
+    bindEventsForSpecialInputs();
     isRestoring = false;
   });
 }
@@ -488,6 +500,7 @@ function redo() {
     } else {
       syncMentionsData();
     }
+    bindEventsForSpecialInputs();
     isRestoring = false;
   });
 }
@@ -507,9 +520,10 @@ onMounted(() => {
   if (editorRef.value) {
     const initialContent = props.modelValue || '';
     editorRef.value.innerHTML = initialContent;
+    bindEventsForSpecialInputs();
     saveToHistory(initialContent, null);
     updateToolbar();
-    syncMentionsData(); // 👈
+    syncMentionsData();
   }
 
   document.addEventListener('selectionchange', handleSelectionChange);
@@ -1090,6 +1104,37 @@ function syncMentionsData() {
   // 过滤 mentionsData,只保留还在 DOM 中的
   mentionsData.value = mentionsData.value.filter((item) => currentUIDs.has(item.uid));
 }
+
+function bindEventsForSpecialInputs() {
+  console.log(111111111111111111111111);
+  if (!editorRef.value) return;
+  console.log(2222222222222222222);
+  const specialInputs = editorRef.value.querySelectorAll('input[data-special="true"]');
+  for (let i = 0, len = specialInputs.length; i < len; i++) {
+    const inputEl = specialInputs[i];
+    console.log(inputEl, '打印');
+    if (inputEl.dataset.eventsBound === 'true') continue;
+
+    inputEl.dataset.eventsBound = 'true';
+
+    function handleInputWidth(el) {
+      const valLength = el.value.length;
+      if (valLength > 10) {
+        el.style.width = valLength * 14 + 20 + 'px';
+      } else {
+        el.style.width = '200px';
+      }
+    }
+
+    inputEl.oninput = function () {
+      handleInputWidth(this);
+      this.setAttribute('value', this.value);
+      emit('content-changed', editorRef.value.innerHTML);
+    };
+
+    handleInputWidth(inputEl);
+  }
+}
 function handleInputForMention(e) {
   if (isRestoring) return;
 

+ 14 - 0
src/views/About.vue

@@ -26,6 +26,16 @@
     expand-trigger="hover"
     :style="{ width: '320px' }"
   />
+  <a-tooltip content="下载" position="top">
+    <!-- <icon-download size="18" @click="handleDwonload" /> -->
+    <a-dropdown trigger="hover" @select="toDownload">
+      <icon-download style="cursor: pointer" size="18" />
+      <template #content>
+        <a-doption value="word">word</a-doption>
+        <a-doption value="pdf">pdf</a-doption>
+      </template>
+    </a-dropdown>
+  </a-tooltip>
 </template>
 
 <script setup >
@@ -108,6 +118,10 @@ const md = ref(`
 `);
 const new_md = ref('');
 const exist_metrics = [{ metric_name: '总公变停电次数' }];
+
+const toDownload = (type) => {
+  console.log(type);
+};
 onMounted(() => {
   // md.value = formatHtmlToMd(html.value);
   console.log(md.value, '2222222222222');