Forráskód Böngészése

feat:修改问数中表格逻辑,问答完成后将表格替换为下载按钮,隐藏表格

韩洋 9 hónapja
szülő
commit
7cddcb882c

+ 284 - 0
src/modules/chat-item/DialogReportContent copy.vue

@@ -0,0 +1,284 @@
+<template>
+  <div class="dialog-chat-answer-content">
+    <div v-if="is_loading && type == 'bgsc'" class="loading"><a-spin></a-spin> <span>报告生成中...</span></div>
+    <div v-if="is_loading && type == 'znws'" class="loading"><a-spin></a-spin> <span>正在思考中...</span></div>
+    <div class="answer-content-text answer-content-report" v-for="(item, index) in result_list" :key="index">
+      <div ref="answerRef" class="answer-content" v-if="typeof item === 'string'" v-html="renderHtml(item)"></div>
+      <HomeChart v-if="typeof item === 'object' && item.type != 'excel'" :origin="'home'" :data="item"></HomeChart>
+    </div>
+    <slot></slot>
+    <div class="report-loading" v-if="answer_status === 1">
+      <img src="../../assets/chat-report/report-ing.png" alt="" />
+      <!-- <span>报告生成中...</span> -->
+    </div>
+  </div>
+  <!-- <div v-html="html_1"></div> -->
+</template>
+
+<script setup>
+import { ref, computed, watch, onMounted, render, h } from 'vue';
+import HomeChart from '../chat_echart/HomeChart.vue';
+import marked from '../../3rd-libs/marked';
+import { Tooltip } from '@arco-design/web-vue';
+import { IconDownload } from '@arco-design/web-vue/es/icon';
+import * as XLSX from 'xlsx';
+// import TableHasDownload from '../chat_files/TableHasDownload.vue';
+// 接收markdown数据,进行一些转换
+const props = defineProps({
+  /**
+   * 内容类型
+   */
+  type: String,
+  /**
+   * 文本内容数组
+   */
+  result_list: Array,
+  /**
+   * 状态值
+   */
+  is_loading: Boolean,
+  /**
+   * 会话状态
+   * */
+  answer_status: Boolean
+});
+const answerRef = ref(null);
+const exportButtonList = ref([]);
+
+const content_style = computed(() => {
+  if (props.type === 'q') {
+    return {
+      background: 'var(--primary-default)',
+      color: '#fff'
+    };
+  }
+});
+
+const renderHtml = (item) => {
+  let renderContent = marked.parse(item);
+  renderContent = renderContent.replace(/^ {2}/gm, '&nbsp;&nbsp;&nbsp;&nbsp;');
+  renderContent = renderContent
+    .replace(/<p>(.*?)<\/p>/gs, (match, content) => {
+      return `<p class="indented" style="text-indent: 2em;">${content}</p>`;
+      // return `<p>${content}</p>`
+    })
+    .replace(/<br\s*\/?>([^<]*)/g, (match, content) => {
+      if (content.trim() === '') return match;
+      return `<br/><span style="display:inline-block;text-indent:2em;">${content}</span>`;
+    });
+  renderContent = renderContent
+    .replace(/<pre><code>/gs, '<p style="text-indent: 2em;">')
+    .replace(/<\/code><\/pre>/gs, '</p>');
+  return renderContent;
+};
+
+const createSingTableExportBtn = (table, index) => {
+  // table.style.display = 'none';
+  // 创建按钮容器
+  const btnContainer = document.createElement('div');
+  btnContainer.className = 'arco-export-btn';
+  btnContainer.dataset.tableIndex = index; //标记表格索引
+  console.log(btnContainer, 112121);
+  // 渲染组件
+  render(
+    h(
+      Tooltip,
+      { content: `导出表格${index + 1}` },
+      {
+        default: () =>
+          h(IconDownload, {
+            size: 18,
+            style: { cursor: 'pointer', color: 'var(--color-primary)' },
+            onClick: () => handleSingleTableExport(table, index)
+          })
+      }
+    ),
+    btnContainer
+  );
+  // setTimeout(() => {
+  // console.log(table.parentNode, 99);
+  if (table.nextSibling) {
+    table.parentNode.insertBefore(btnContainer, table.nextSibling);
+  } else {
+    table.parentNode.appendChild(btnContainer);
+  }
+
+  // }, 30);
+
+  exportButtonList.value.push(btnContainer);
+};
+const handleSingleTableExport = (table, tableIndex) => {
+  const workbook = XLSX.utils.book_new();
+  const worksheet = XLSX.utils.table_to_sheet(table);
+  // 添加工作表到工作簿
+  XLSX.utils.book_append_sheet(workbook, worksheet, `表格${tableIndex + 1}数据`);
+
+  // 生成并下载Excel文件
+  XLSX.writeFile(workbook, `表格${tableIndex + 1}_数据导出.xlsx`);
+};
+watch(
+  () => props.answer_status,
+  (new_value) => {
+    // console.log('变化', props.answer_status);
+    if (props.type == 'znws' && new_value / 1 == 2) {
+      setTimeout(() => {
+        const tableContainers = document.querySelectorAll('.answer-content');
+        console.log(tableContainers);
+        const tableContainers_filter = Array.from(tableContainers).filter((item) => !item.getAttribute('data-table'));
+        const allTables = [];
+        tableContainers_filter.forEach((item) => {
+          item.setAttribute('data-table', '1');
+          if (item.querySelectorAll('table') && item.querySelectorAll('table').length) {
+            allTables.push(...Array.from(item.querySelectorAll('table')));
+          }
+        });
+        if (!allTables || !allTables.length) return;
+        Array.from(allTables).forEach((table, index) => {
+          createSingTableExportBtn(table, index);
+        });
+        // const tableContainer = answerRef.value;
+        // tableContainer.setAttribute('data-table', '1');
+        // const allTables = tableContainer?.querySelectorAll('table');
+        // console.log(allTables, 111);
+        // if (!allTables || !allTables.length) return;
+        // Array.from(allTables).forEach((table, index) => {
+        //   createSingTableExportBtn(table, index);
+        // });
+      }, 1000);
+    }
+  }
+);
+onMounted(() => {
+  if (props.result_list && props.result_list.length) {
+    if (props.type == 'znws') {
+      const tableContainers = document.querySelectorAll('.answer-content');
+      console.log(tableContainers);
+      const tableContainers_filter = Array.from(tableContainers).filter((item) => !item.getAttribute('data-table'));
+      const allTables = [];
+      tableContainers_filter.forEach((item) => {
+        item.setAttribute('data-table', '1');
+        if (item.querySelectorAll('table') && item.querySelectorAll('table').length) {
+          allTables.push(...Array.from(item.querySelectorAll('table')));
+        }
+      });
+      // const allTables = tableContainer?.querySelectorAll('table');
+      if (!allTables || !allTables.length) return;
+      Array.from(allTables).forEach((table, index) => {
+        createSingTableExportBtn(table, index);
+      });
+    }
+  }
+});
+</script>
+
+<style lang="css" scoped>
+.dialog-chat-answer-content {
+  position: relative;
+  background-color: var(--color-bg-1);
+  color: var(--color-text-1);
+  border-radius: 12px;
+
+  padding-top: 15px;
+  padding-bottom: 15px;
+  padding-left: 30px;
+  padding-right: 30px;
+}
+
+.answer-content-report .answer-content * p {
+  text-indent: 2em !important;
+}
+
+.answer-content * p {
+  text-indent: 2em !important;
+}
+
+p {
+  text-indent: 2em !important;
+}
+
+.dialog-chat-answer-content code {
+  overflow-x: auto;
+}
+
+.answer-content-text {
+  line-height: 24px;
+}
+
+.loading {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+</style>
+<style>
+.refferences {
+  background: rgb(238, 243, 254);
+  border: 1px solid rgb(255, 255, 255);
+  box-shadow: rgba(29, 48, 91, 0.2) 0px 1px 1px 0px;
+
+  padding: 2px 6px;
+  cursor: pointer;
+}
+
+.refferences:hover {
+  background-color: var(--primary-default);
+  color: #fff;
+}
+
+.indented br + * {
+  display: inline-block;
+  text-indent: 2em;
+}
+.report-loading {
+  margin-top: 10px;
+}
+.report-loading img {
+  width: 32px;
+}
+</style>
+<style>
+/* 表格基础样式 */
+.answer-content-text table {
+  width: 100%;
+  border-collapse: collapse; /* 合并边框 */
+  border-spacing: 0;
+  font-size: 14px;
+  line-height: 1.5;
+}
+
+/* 表头样式 */
+.answer-content-text th {
+  padding: 12px 16px;
+  background-color: #f5f5f5; /* 浅灰色背景 */
+  border: 1px solid #e8e8e8; /* 边框颜色 */
+  font-weight: 500;
+  text-align: center; /* 表头文字居中 */
+}
+
+/* 单元格样式 */
+.answer-content-text td {
+  padding: 12px 16px;
+  border: 1px solid #e8e8e8;
+  text-align: left; /* 默认左对齐,会被Markdown的对齐方式覆盖 */
+}
+
+/* 隔行变色(可选) */
+.answer-content-text tr:nth-child(even) {
+  background-color: #fafafa;
+}
+
+/* 鼠标悬停效果(可选) */
+.answer-content-text tr:hover {
+  background-color: #f0f7ff;
+}
+.arco-export-btn {
+  margin: 8px 0 24px 0;
+  display: inline-block;
+}
+.arco-export-btn :deep(.arco-icon) {
+  transition: color 0.2s ease;
+}
+.arco-export-btn :deep(.arco-icon:hover) {
+  color: var(--primary-default);
+}
+</style>

+ 34 - 24
src/modules/chat-item/DialogReportContent.vue

@@ -53,13 +53,7 @@ const content_style = computed(() => {
     };
   }
 });
-// const html_1 = computed(()=>{
-//   const demo =
-// "\n  \n  ## 五、配电自动化应用建设情况\n  上周全省FA有效覆盖率60.04%,其中较高的单位有:攀枝花(77.17%),遂宁(76.22%),自贡(75.79%);较低的单位有:内江(45.78%),甘孜(32.47%),岷江(0.0%)。\n\n 上周全省全自动FA覆盖率,其中较高的单位有:雅安(51.42%),成都(29.32%),眉山(17.18%);较低的单位有:遂宁(0.0%),天府(0.0%),岷江(0.0%)。\n上周全省遥控使用率31.50%,其中较高的单位有:遂宁(95.65%),广安(92.86%),眉山(86.52%);较低的单位有:德阳(0%),甘孜(0.0%),岷江(0%)。 \n上周全省FA动作正确率51.86%,其中较高的单位有:攀枝花(76.56%),自贡(70.0%),泸州(69.33%);较低的单位有:乐山(20.83%),德阳(0%),岷江(0%)。\n  \n "
-//   console.log(marked.parse(demo),5555555555)
-// return marked.parse(demo)
 
-// })
 const renderHtml = (item) => {
   let renderContent = marked.parse(item);
   renderContent = renderContent.replace(/^ {2}/gm, '&nbsp;&nbsp;&nbsp;&nbsp;');
@@ -79,6 +73,16 @@ const renderHtml = (item) => {
 };
 
 const createSingTableExportBtn = (table, index) => {
+  table.style.display = 'none';
+  // 创建包含标题和导出按钮的容器
+  const tableContainer = document.createElement('div');
+  tableContainer.className = 'custom-table-container';
+
+  // 创建标题区域
+  const titleDiv = document.createElement('div');
+  titleDiv.className = 'table-title-container';
+  titleDiv.innerHTML = '<span class="table-title">表格数据</span>';
+
   // 创建按钮容器
   const btnContainer = document.createElement('div');
   btnContainer.className = 'arco-export-btn';
@@ -100,16 +104,12 @@ const createSingTableExportBtn = (table, index) => {
     ),
     btnContainer
   );
-  // setTimeout(() => {
-  // console.log(table.parentNode, 99);
-  if (table.nextSibling) {
-    table.parentNode.insertBefore(btnContainer, table.nextSibling);
-  } else {
-    table.parentNode.appendChild(btnContainer);
-  }
-
-  // }, 30);
 
+  // 将按钮添加到标题区域
+  titleDiv.appendChild(btnContainer);
+  tableContainer.appendChild(titleDiv);
+  // 将容器插入到表格前面
+  table.parentNode.insertBefore(tableContainer, table);
   exportButtonList.value.push(btnContainer);
 };
 const handleSingleTableExport = (table, tableIndex) => {
@@ -141,14 +141,6 @@ watch(
         Array.from(allTables).forEach((table, index) => {
           createSingTableExportBtn(table, index);
         });
-        // const tableContainer = answerRef.value;
-        // tableContainer.setAttribute('data-table', '1');
-        // const allTables = tableContainer?.querySelectorAll('table');
-        // console.log(allTables, 111);
-        // if (!allTables || !allTables.length) return;
-        // Array.from(allTables).forEach((table, index) => {
-        //   createSingTableExportBtn(table, index);
-        // });
       }, 1000);
     }
   }
@@ -277,7 +269,7 @@ p {
   background-color: #f0f7ff;
 }
 .arco-export-btn {
-  margin: 8px 0 24px 0;
+  margin: 3px 0 0 0;
   display: inline-block;
 }
 .arco-export-btn :deep(.arco-icon) {
@@ -286,4 +278,22 @@ p {
 .arco-export-btn :deep(.arco-icon:hover) {
   color: var(--primary-default);
 }
+
+.custom-table-container {
+  /* margin: 3px 0; */
+}
+
+.table-title-container {
+  background-color: #f5f5f5;
+  padding: 8px 16px;
+  border-radius: 4px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.table-title {
+  font-weight: 600;
+  color: #333;
+}
 </style>

+ 21 - 1
src/views/Conversation.vue

@@ -22,7 +22,13 @@
       <!-- <Test></Test> -->
 
       <router-view ref="conversationContainer" :style="conversationChatStyle" @updateHistory="updateHistory" />
-      <!-- <TableHasDownload></TableHasDownload> -->
+
+      <!-- <DialogReportContent
+        type="znws"
+        :is_loading="false"
+        :result_list="result_list"
+        :answer_status="2"
+      ></DialogReportContent> -->
       <!-- <iframe src="../assets/1000548/index.html" frameborder="0"></iframe> -->
       <!-- <iframe src="http://localhost:5174/" frameborder="0"></iframe> -->
     </div>
@@ -38,6 +44,7 @@ import ChatAsideMenu from './conversation/ChatAsideMenu.vue';
 import chatUserMenu from './conversation/chatUserMenu.vue';
 
 import chat_history_unfold from '../assets/chat-aside/chat-history-unfold.svg';
+// import DialogReportContent from '../modules/chat-item/DialogReportContent.vue';
 import marked from '../3rd-libs/marked.js';
 
 import HomeChart from '../modules/chat_echart/HomeChart_cs.vue';
@@ -58,6 +65,19 @@ const show_aside_control = ref(false);
 const conversationContainer = ref(null);
 const chatMenu = ref(null);
 
+// const result_list = ref([
+//   `## 表格测试 \n| 学科   | 满分 | 及格线 | 难度系数 |
+// | :----- | ---: | :----: | :------- |
+// | 数学   | 150  |   90   | 高       |
+// | 语文   | 150  |   90   | 中       |
+// | 英语   | 120  |   72   | 中       |
+// | 物理   | 100  |   60   | 高       | \n
+// - asdasdasdaldas;jaf
+// - asdasdasdaldas;jaf
+// - asdasdasdaldas;jaf
+// - asdasdasdaldas;jaf`
+// ]);
+
 const chatAsideStyle = computed(() => {
   if (show_aside_menu.value) {
     return {