浏览代码

PDF预览测试

songxy 2 月之前
父节点
当前提交
36fb7c8960

+ 1 - 1
ais_search_zj/web/src/components/pdf/PDFViewerSearch.vue

@@ -174,7 +174,7 @@ const onLoad = () => {
 };
 const src = computed(() => {
   // return `/lib/pdfjs/web/viewer.html?file=${props.src}&t=` + new Date().getTime();
-  return `/aisearch/lib/pdfjs/web/viewer.html?file=${props.src}`;
+  return `/lib/pdfjs/web/viewer.html?file=${props.src}`;
   // return `/aisearch/lib/pdfjs/web/viewer.html?file=${props.src}&t=` + new Date().getTime();
 
   // return `/lib/pdfjs/web/viewer.html?file=http://121.40.148.47:8530/doc/knowledge_base/download_doc/国土资源部 国家发展和改革委员会+财政部+住房和城乡建设部农业部+中国人民银行+国家林业局+中国银行业监督管理委员会关于扩大国有土地有偿使用范围的意见%28279-283%29.pdf`

+ 0 - 1144
ais_search_zj/web/src/views/ai-home/index copy.vue

@@ -1,1144 +0,0 @@
-<template>
-  <div class="home_box">
-    <div class="left_box">
-      <button @click="startNewSessionHandle">
-        <span class="icon"></span>
-        <span>开启新对话</span>
-      </button>
-      <div class="history">
-        <div class="menu_down">
-          <span class="title">历史问答</span>
-          <span class="icon" @click="toggleCardHandle('history')">
-            <UpOutlined v-if="visibleMap['history']" />
-            <DownOutlined v-else />
-          </span>
-        </div>
-        <div class="history_box" v-show="visibleMap['history']">
-          <ul>
-            <li>
-              <span class="title">今天</span>
-              <ul>
-                <li>什么是闲置土地</li>
-                <li>什么是闲置土地</li>
-                <li>什么是闲置土地</li>
-              </ul>
-            </li>
-            <li>
-              <span class="title">昨天</span>
-              <ul>
-                <li>什么是闲置土地</li>
-                <li>什么是闲置土地</li>
-                <li>什么是闲置土地</li>
-              </ul>
-            </li>
-            <li>
-              <span class="title">30天内</span>
-              <ul>
-                <li>什么是闲置土地</li>
-                <li>什么是闲置土地</li>
-                <li>什么是闲置土地</li>
-              </ul>
-            </li>
-          </ul>
-        </div>
-      </div>
-      <div class="ai_tools">
-        <div class="menu_down">
-          <span class="title">AI工具</span>
-          <span class="icon" @click="toggleCardHandle('tool')">
-            <UpOutlined v-if="visibleMap['tool']" />
-            <DownOutlined v-else />
-          </span>
-        </div>
-        <ul v-show="visibleMap['tool']">
-          <li @click="toToolPage('./#/policy/interpret')">
-            <span class="icon">
-              <span class="iconfont icon-a-lujing8796"></span>
-            </span>
-            <span class="txt">政策解读</span>
-          </li>
-          <li @click="toToolPage('./#/policy/smart')">
-            <span class="icon">
-              <span class="iconfont icon-a-lujing8794"></span>
-            </span>
-            <span class="txt">政策比对</span>
-          </li>
-        </ul>
-      </div>
-    </div>
-    <div class="right_box">
-      <div class="chat-container">
-        <div v-if="historys.length === 0" class="messages-container">
-          <p class="n_tips">Hi~,我是自然资源大模型,您身边的智能助手!</p>
-        </div>
-        <div v-else class="messages-container" ref="msgContainer">
-          <template v-for="(history,index) in historys" :key="index">
-            <div class="message user">
-              {{ history.question }}
-            </div>
-            <div class="message assistant">
-              <div class="ai-search-detail">
-                <div class="search-panel" id="pageContainer">
-                  <div :class="`search-detail search-detail-${askType}`">
-                    <div
-                      class="search-result"
-                      id="searchResult"
-                      style="overflow-x: hidden; height: auto"
-                      ref="messageContainer"
-                    >
-
-                      <div :class="`result-panel result-panel-${askType}`">
-
-                        <div class="result">
-                          <template v-if="askType === 'zcfg'">
-                            <div class="result-view">
-                              <div class="q-r">
-                                <div class="ds-content-box">
-                                  <div class="icon">
-                                    <img src="/images/icon-ds.png" />
-                                  </div>
-                                  <div class="ds-panel">
-                                    <div class="ds-loading" v-if="history.currentResponse.hintTxt" @click="dsUp = !dsUp">
-                                      {{ history.currentResponse.hintTxt }}
-                                      <DownOutlined class="icon-arrow" :class="{ rotate: dsUp }" />
-                                    </div>
-                                    <div class="ds-con" v-if="!dsUp">
-                                      <vue-markdown-it
-                                        id="dsMarkdown"
-                                        :source="
-                                          history.currentResponse.streamMock
-                                            ? history.currentResponse.streamMsg.indexOf('###') > -1
-                                              ? history.currentResponse.streamMsg.substring(
-                                                  0,
-                                                  history.currentResponse.streamMsg.indexOf('###')
-                                                )
-                                              : history.currentResponse.streamMsg
-                                            : history.currentResponse.msg.indexOf('###') > -1
-                                            ? history.currentResponse.msg.substring(
-                                                0,
-                                                history.currentResponse.msg.indexOf('###')
-                                              )
-                                            : history.currentResponse.msg
-                                        "
-                                        :options="{
-                                          html: true,
-                                          linkify: true
-                                        }"
-                                      />
-                                    </div>
-                                    <a-spin :indicator="indicator" v-if="history.currentResponse.loading" />
-                                  </div>
-                                </div>
-                                <vue-markdown-it
-                                  id="resMarkdown"
-                                  :source="
-                                    history.currentResponse.streamMock
-                                      ? history.currentResponse.streamMsg.indexOf('###') > -1
-                                        ? history.currentResponse.streamMsg.substring(
-                                            history.currentResponse.streamMsg.indexOf('###')
-                                          )
-                                        : ''
-                                      : history.dsChecked
-                                      ? history.currentResponse.msg.indexOf('###') > -1
-                                        ? history.currentResponse.msg.substring(history.currentResponse.msg.indexOf('###'))
-                                        : ''
-                                      : history.currentResponse.msg
-                                  "
-                                  :options="{
-                                    html: true,
-                                    linkify: true
-                                  }"
-                                />
-                              </div>
-                              <div class="source" v-if="activeTab !== 'original'" v-show="activeIndex === 5">
-                                <div class="title">
-                                  <span>基于{{ history.currentResponse.docs.length }}个参考来源</span>
-                                  <span class="icon" @click="history.sourceVisible = !history.sourceVisible">
-                                    <UpOutlined v-if="history.sourceVisible" />
-                                    <DownOutlined v-else />
-                                  </span>
-                                </div>
-                                <div class="items" v-show="history.sourceVisible">
-                                  <div
-                                    class="item"
-                                    v-if="activeTab !== 'net'"
-                                    v-for="(doc, i) in history.currentResponse.docs"
-                                    :key="'doc-' + i"
-                                  >
-                                    <div class="doc">
-                                      <p>
-                                        <span
-                                          class="ma"
-                                          style="font-weight: bolder; color: #000000; margin-right: 10px"
-                                          >出处 [{{ i + 1 }}] </span
-                                        ><span class="doc-link" @click="openDoc(doc, i)">{{ doc.doc }}</span>
-                                      </p>
-                                      <div
-                                        :class="`doc-icon${
-                                          !doc.showContent ? ' doc-icon-show' : ' doc-icon-hide'
-                                        }`"
-                                        @click="doc.showContent = !doc.showContent"
-                                      ></div>
-                                    </div>
-
-                                    <div :class="`content${doc.showContent ? '' : ' content-hide'}`">
-                                      <p>{{ doc.content }}</p>
-                                    </div>
-                                  </div>
-                                  <div
-                                    class="item item-url"
-                                    v-if="activeTab === 'net'"
-                                    v-for="(doc, i) in history.currentResponse.docs"
-                                    :key="'doc-' + i"
-                                  >
-                                    <div class="doc">
-                                      <p>
-                                        <span class="doc-link" @click="openUrl(doc.link)">{{ doc.title }}</span>
-                                      </p>
-                                    </div>
-                                    <div class="bottom">
-                                      <div class="title-icon">
-                                        <div class="icon"></div>
-                                        <div class="title">{{ doc.doc }}</div>
-                                      </div>
-                                      <div class="index">{{ i + 1 }}</div>
-                                    </div>
-                                  </div>
-                                </div>
-                              </div>
-                            </div>
-                          </template>
-                          <template v-else>
-                            <div v-if="activeIndex >= 1" class="map-answer">
-                              <div class="left-panel">
-                                <div class="content">
-                                  <div class="summary-card">
-                                    <div class="summary-title" id="tdscSummaryTitle">总结</div>
-                                    <div class="summary-content">
-                                      <a-skeleton active v-if="!history.currentResponse.msg" />
-                                      <vue-markdown-it
-                                        :source="
-                                          history.currentResponse.streamMock
-                                            ? history.currentResponse.streamMsg
-                                            : history.currentResponse.msg
-                                        "
-                                        :options="{
-                                          html: true,
-                                          linkify: true
-                                        }"
-                                      />
-                                    </div>
-                                  </div>
-                                  <div class="chart-card" v-if="history.currentResponse.hasChart" id="tdscChartCard">
-                                    <div class="chart-title">生成图表</div>
-                                    <a-skeleton
-                                      active
-                                      style="height: 100%"
-                                      v-if="!history.currentResponse.chartOption"
-                                    />
-                                    <div v-else class="chart" id="summaryChart"></div>
-                                  </div>
-                                </div>
-                              </div>
-                            </div>
-                          </template>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </template>
-        </div>
-        
-        <div class="input-container">
-            <textarea 
-              v-model="cQuestion"
-              class="input-box" 
-              ref="msgInput" 
-              placeholder="请输入对话内容,换行请使用Shift+Enter" 
-              rows="5"
-              @keydown="onKeydownHandle"
-            ></textarea>
-            <div class="bottom_box">
-              <div class="tools_box">
-                <div class="tool_box_1">
-                  <a-dropdown>
-                    <template #overlay>
-                      <a-menu @click="({key})=>onChange(key)">
-                        <a-menu-item key="1">
-                          <div :class="{tool_1: true, active: modelType === '1'}">
-                            DeepSeek
-                          </div>
-                        </a-menu-item>
-                        <a-menu-item key="0">
-                          <div :class="{tool_1: true, active: modelType === '0'}">
-                            通义千问
-                          </div>
-                        </a-menu-item>
-                      </a-menu>
-                    </template>
-                    <a-button style="width: 120px">
-                      {{ modelType === '1' ? 'DeepSeek' : '通义千问'}}
-                      <DownOutlined />
-                    </a-button>
-                  </a-dropdown>
-                </div>
-                <div class="tool_box_2">
-                  <a-dropdown>
-                    <template #overlay>
-                      <a-menu @click="changeAnswerType">
-                        <a-menu-item key="0">
-                          <div :class="{tool_1: true, active: answerType === '0'}">
-                            简洁
-                          </div>
-                        </a-menu-item>
-                        <a-menu-item key="1">
-                          <div :class="{tool_1: true, active: answerType === '1'}">
-                            深入
-                          </div>
-                        </a-menu-item>
-                        <a-menu-item key="2">
-                          <div :class="{tool_1: true, active: answerType === '2'}">
-                            研究
-                          </div>
-                        </a-menu-item>
-                      </a-menu>
-                    </template>
-                    <a-button style="width: 80px">
-                      {{ answerType === '0' ? '简洁' : answerType === '1' ? '深入' : '研究'}}
-                      <DownOutlined />
-                    </a-button>
-                  </a-dropdown>
-                </div>
-                <div class="tool_box_3">
-                  <a-dropdown>
-                    <template #overlay>
-                      <a-menu @click="({key})=>changeTab(key)">
-                        <a-menu-item 
-                          v-for="t in tabs"
-                          :key="t.key"
-                        >
-                          <div :class="{tool_1: true, active: activeTab === t.key}">
-                            {{t.name}}
-                          </div>
-                        </a-menu-item>
-                      </a-menu>
-                    </template>
-                    <a-button style="width: 90px">
-                      {{ activeTab === 'knowledge' ? '知识库' : activeTab === 'net' ? '全网' : '原生'}}
-                      <DownOutlined />
-                    </a-button>
-                  </a-dropdown>
-                </div>
-              </div>
-              <div class="send_btn">
-                <div v-if="historys.length > 0 && historys[historyIndex].currentResponse.loading" @click="onSendHandle(false)">
-                  <i class="stop"></i>
-                </div>
-                <div v-else @click="onSendHandle">
-                  <i class="iconfont icon-a-lujing9250"></i>
-                </div>
-              </div>
-            </div>
-        </div>
-    </div>
-    </div>
-    <div
-      v-if="showDoc"
-      class="docs_box"
-      style="background-color: white;overflow: auto"
-    >
-      <p-d-f-viewer
-        v-if="fileType === 'pdf'"
-        :src="pdfSrc"
-        @close="closeDoc"
-        :content="pdfContent"
-        :num="pdfNum"
-      />
-      <word-viewer
-        v-if="fileType === 'docx'"
-        :src="pdfSrc"
-        @close="closeDoc"
-        :content="pdfContent"
-        :num="pdfNum"
-      >
-      </word-viewer>
-      <txt-viewer v-if="fileType === 'txt'" :src="pdfSrc" @close="closeDoc" :txt="pdfContent" />
-    </div>
-  </div>
-</template>
-
-<script setup>
-import {
-  LoadingOutlined,
-  UpOutlined,
-  DownOutlined
-} from "@ant-design/icons-vue";
-import { fetchEventSource } from '@microsoft/fetch-event-source';
-import PDFViewer from '@/components/pdf/PdfCanvas.vue';
-import WordViewer from '@/components/pdf/WordViewer.vue';
-import { VueMarkdownIt } from '@f3ve/vue-markdown-it';
-import TxtViewer from '@/components/pdf/TxtViewer.vue';
-import { message } from 'ant-design-vue';
-import { h, ref, reactive, watch } from 'vue';
-import ManagerAPI from '@/api/manager';
-import PubsubService from '@/utils/PubsubService';
-import { useUserStore } from '@/stores';
-
-let ctr = null;
-
-const visibleMap = reactive({
-  history: true,
-  tool: true
-})
-const toggleCardHandle = (type) => {
-  visibleMap[type] = !visibleMap[type]
-}
-const msgInput = ref(null)
-const msgContainer = ref(null)
-
-function scrollToBottom() {
-  msgContainer.value.scrollTop = msgContainer.value.scrollHeight;
-}
-
-const historys = ref([])
-let historyIndex = -1;
-const cQuestion = ref('')
-
-const startNewSessionHandle = () => {
-  cQuestion.value = ''
-  historys.value = []
-  historyIndex = -1
-}
-
-const onKeydownHandle = (e) => {
-    if (e.key === 'Enter' && !e.shiftKey) {
-      e.preventDefault();
-      if (historys.value.length > 0 && historys.value[historyIndex].currentResponse.loading) {
-        message.error('回答输出中,请稍后操作或点击停止回答');
-        return;
-      }
-      onSendHandle();
-    }
-}
-
-const onSendHandle = (status = true) => {
-  if (!cQuestion.value) {
-    message.error('请输入问题');
-    return;
-  }
-  if (!status) {
-    historys.value[historyIndex].currentResponse.loading = false;
-    stopAI();
-    return;
-  }
-  historyIndex++;
-  historys.value.push({
-    question: cQuestion.value,
-    dsChecked: true,
-    sourceVisible: false,
-    currentResponse: {
-      loading: true,
-      hintTxt: '',
-      streamMsg: '',
-      streamMock: false,
-      msg: '',
-      originAnswer: '',
-      docs: []
-    }
-  })
-  ask(decodeURIComponent(cQuestion.value));
-  cQuestion.value = '';
-}
-
-const toToolPage = (path) => {
-  window.open(path, '_blank')
-}
-
-const aiLoading = ref(false);
-const indicator = h(LoadingOutlined, {
-  style: {
-    fontSize: '24px'
-  },
-  spin: true
-});
-
-const modelType = ref('1')
-const answerType = ref('0');
-
-const question = ref('国有土地的使用方式有哪些?');
-const statusText = ref('检索中');
-const activeIndex = ref(0);
-const activeTab = ref('knowledge');
-
-const evaluate = ref(null);
-
-const open = ref(true);
-const showDoc = ref(false);
-const pdfSrc = ref('');
-const pdfContent = ref('');
-const pdfNum = ref(1);
-const fileType = ref('pdf');
-const startTime = ref(0);
-const endTime = ref(0);
-const dsUp = ref(false);
-const times = ref(0);
-const timers = ref([]);
-let streamMockInterval = null;
-const streamToAnswer = () => {
-  historys.value[historyIndex].currentResponse.index = 0;
-  streamMockInterval = setInterval(() => {
-    const { originAnswer = '', msg, streamMsg = '', id, index = 0 } = historys.value[historyIndex].currentResponse;
-
-    if (historys.value[historyIndex].currentResponse.mockStart || index <= originAnswer.length) {
-      historys.value[historyIndex].currentResponse.streamMsg = originAnswer.substr(0, index + 2).replaceAll('\n', '  \n');
-      if (originAnswer) {
-        historys.value[historyIndex].currentResponse.index += 2;
-      }
-      let num = getNum(historys.value[historyIndex].currentResponse.streamMsg);
-
-      while (num) {
-        const docsNum = historys.value[historyIndex].currentResponse.docs.length;
-        historys.value[historyIndex].currentResponse.streamMsg = historys.value[historyIndex].currentResponse.streamMsg.replace(
-          `[[${num}]]`,
-          `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style="    cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd;    width: 20px;
-    height: 20px;
-    background: #FFFFFF;
-    border-radius: 4px 4px 4px 4px;
-    border: 1px solid #BACAE3;">${num}</span>`
-        );
-
-        num = getNum(historys.value[historyIndex].currentResponse.streamMsg);
-      }
-    } else {
-      if (streamMockInterval) {
-        clearInterval(streamMockInterval);
-        streamMockInterval = null;
-      }
-
-      activeIndex.value = 5;
-
-      console.log('mock 结束');
-    }
-  }, 50);
-};
-
-const tabs = [
-  { key: 'knowledge', name: '知识库' },
-  // { key: 'net', name: '全网' },
-  { key: 'original', name: '原生' },
-];
-
-
-const changeStatusText = () => {
-  let i = 0;
-  setInterval(() => {
-    statusText.value = '检索中' + '.'.repeat(i);
-
-    if (i === 3) {
-      i = 0;
-    }
-    i++;
-  }, 500);
-};
-changeStatusText();
-
-const askType = ref('zcfg');
-const ask = async (q, isFllow) => {
-  if (ctr) {
-    if (streamMockInterval) {
-      clearInterval(streamMockInterval);
-      streamMockInterval = null;
-    }
-    ctr.abort();
-  }
-  times.value = 0;
-  if (timers.value) {
-    timers.value.forEach((t) => {
-      clearTimeout(t);
-      t = null;
-    });
-  }
-  timers.value = [];
-  if (historys.value[historyIndex].dsChecked) {
-    historys.value[historyIndex].currentResponse.hintTxt = '';
-    historys.value[historyIndex].currentResponse.loading = true;
-  }
-  question.value = q;
-  open.value = false;
-  showDoc.value = false;
-  askType.value = 'zcfg';
-  quest(isFllow);
-};
-
-let questionUrl = '/chat/kb_chat';
-
-const changeTab = (tab) => {
-  times.value = 0;
-  if (timers.value) {
-    timers.value.forEach((t) => {
-      clearTimeout(t);
-      t = null;
-    });
-  }
-  timers.value = [];
-  if (tab === 'net') {
-    questionUrl = '/chat/bing_chat';
-  } else if(tab === 'knowledge'){
-    questionUrl = '/chat/kb_chat';
-  }else if(tab === 'original') {
-    questionUrl = '/chat/chat';
-  }
-
-  activeTab.value = tab;
-};
-const onChange = (type) => {
-  modelType.value = type;
-  dsChange(type);
-  changeAnswerType({key: '0'});
-};
-//ds绑定用户改变
-const dsChange = (type) => {
-  localStorage.setItem("_isDeepSeek", type);
-  if (timers.value) {
-    timers.value.forEach((t) => {
-      clearTimeout(t);
-      t = null;
-    });
-    timers.value = [];
-  }
-  times.value = 0;
-  //打字机效果 切换会打印
-  aiLoading.value = true;
-};
-const changeAnswerType = ({ key }) => {
-  answerType.value = key
-};
-
-const questHistories = ref([]);
-
-let scb = null;
-const quest = async (isFllow) => {
-  startTime.value = Date.now();
-  window.scroll({ top: 0, behavior: 'smooth' });
-  question.value = (isFllow ? '追问: ' : '') + question.value;
-  evaluate.value = null;
-  activeIndex.value = 0;
-
-  if (!isFllow) {
-    showDoc.value = false;
-  }
-  if (isFllow) {
-    const { id, question, msg, docs, originAnswer, keywords = [] } = historys.value[historyIndex].currentResponse;
-    questHistories.value.push({ id, question, msg, docs: docs, originAnswer, keywords });
-  } else {
-    questHistories.value = [];
-  }
-  aiLoading.value = true;
-  if (scb !== null) {
-    clearInterval(scb);
-    scb = null;
-  } else {
-    scb = setInterval(() => {
-    }, 500);
-  }
-  ctr = new AbortController();
-
-  const id = questHistories.value.length;
-  historys.value[historyIndex].currentResponse = {
-    question: question.value,
-    id,
-    msg: '',
-    originAnswer: '',
-    streamMock: false,
-    streamMsg: '',
-    docs: []
-  };
-  activeIndex.value = 0;
-  getQuestionKeyWords();
-  if (activeTab.value === 'net') {
-    questionUrl = '/chat/bing_chat';
-  } else if(activeTab.value === 'knowledge'){
-    questionUrl = '/chat/kb_chat';
-  }else if(activeTab.value === 'original') {
-    questionUrl = '/chat/chat';
-  }
-  const topKs = window?.AppGlobalConfig?.topKs || {
-    0: 5,
-    1: 10,
-    2: 15
-  };
-  let body = null
-  if (activeTab.value === 'net') {
-    body = {
-      query: question.value,
-      stream: true,
-      model: modelType.value === '1' ? 'deepseek-r1' : '',
-      search_type: answerType.value
-    }
-  } else if(activeTab.value === 'knowledge'){
-    body = {
-      query: question.value,
-      mode: 'local_kb',
-      kb_name: activeTab.value === 'paper' ? 'compose_paper_material_total' : modelType.value === '1' ? window?.AppGlobalConfig?.llm?.kb_name : 'policy',
-      top_k: topKs[answerType.value],
-      search_type: answerType.value,
-      score_threshold: 0.5,
-      model: historys.value[historyIndex].dsChecked ? 'deepseek-r1' : '',
-      history: isFllow ? getFlowHistory() : [],
-      stream: true,
-      prompt_name: 'rag_context_qa.md',
-      return_direct: false
-    }
-  } else if (activeTab.value === 'original') {
-    body = {
-      query: question.value,
-      stream: true
-    }
-  }
-  if (activeTab.value === 'knowledge' && isFllow) {
-    if (questHistories.value.length > 0) {
-      body.history_keyword = questHistories.value[questHistories.value.length - 1].keywords;
-    }
-  }
-  if (activeTab.value !== 'net' && answerType.value !== '0') {
-    historys.value[historyIndex].currentResponse.streamMock = true;
-    historys.value[historyIndex].currentResponse.mockStart = true;
-    streamToAnswer();
-  }
-  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
-  await fetchEventSource(rootUrl + questionUrl, {
-    method: 'POST',
-    openWhenHidden: true,
-    timeout: 300000,
-    headers: {
-      'Content-Type': 'application/json'
-    },
-
-    body: JSON.stringify(body),
-    signal: ctr.signal,
-    async onmessage(msg) {
-      if (activeIndex.value !== 3) {
-        activeIndex.value = 3;
-      }
-
-      activeTab.value === 'net' ? handleNetResponse(msg, id) : handleKnowledgeResponse(msg, id);
-    },
-    onclose () {
-      if (scb !== null) {
-        clearInterval(scb);
-        scb = null;
-        collectQuestion();
-      }
-
-      if (historys.value[historyIndex].currentResponse.streamMock) {
-        historys.value[historyIndex].currentResponse.mockStart = false;
-      } else {
-        activeIndex.value = 5;
-      }
-    },
-    onerror (err) {
-      historys.value[historyIndex].currentResponse.loading = false
-      throw err;
-    }
-  });
-};
-
-const getFlowHistory = () => {
-  const lastHistory = [...questHistories.value].splice(-1);
-  const parm = [];
-  lastHistory.forEach((item) => {
-    parm.push({ role: 'user', content: item.question });
-  });
-
-  return parm;
-};
-
-const handleKnowledgeResponse = (msg, id) => {
-  if (!msg || !msg.data) {
-    return;
-  }
-  const rData = JSON.parse(msg.data);
-  if (rData?.choices && rData.choices.length > 0) {
-    if (activeTab.value === 'net' && rData.status !== 2) {
-      return;
-    }
-    if (rData.status == 3) {
-      if (timers.value) {
-        timers.value.forEach((t) => {
-          clearTimeout(t);
-          t = null;
-        });
-        timers.value = [];
-      }
-        endTime.value = Date.now();
-        var time = ((endTime.value - startTime.value) / 1000).toFixed(0);
-        historys.value[historyIndex].currentResponse.hintTxt = `已深度思考(用时 ${time} 秒)`;
-        historys.value[historyIndex].currentResponse.loading = false;
-        aiLoading.value=true;
-        historys.value[historyIndex].currentResponse.originAnswer = rData.choices[0]?.delta?.content.replaceAll(
-          '\n',
-          `  \n`
-        );
-        historys.value[historyIndex].currentResponse.msg = rData.choices[0]?.delta?.content.replaceAll('\n', `  \n`);
-        let num = getNum(historys.value[historyIndex].currentResponse.msg);
-        while (num) {
-          const docsNum = historys.value[historyIndex].currentResponse.docs.length;
-          historys.value[historyIndex].currentResponse.msg = historys.value[historyIndex].currentResponse.msg.replace(
-            `[[${num}]]`,
-            `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style="    cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd;    width: 20px;
-    height: 20px;
-    background: #FFFFFF;
-    border-radius: 4px 4px 4px 4px;
-    border: 1px solid #BACAE3;">${num}</span>`
-          );
-
-          num = getNum(historys.value[historyIndex].currentResponse.msg);
-        }
-    } else {
-      
-      aiLoading.value = false;
-      historys.value[historyIndex].currentResponse.hintTxt = '思考中...';
-      //ds模式打字机效果输出
-      const timer = setTimeout(() => {
-        if (!aiLoading.value) {
-          historys.value[historyIndex].currentResponse.originAnswer += rData.choices[0]?.delta?.content.replaceAll(
-            '\n',
-            `  \n`
-          );
-          historys.value[historyIndex].currentResponse.msg += rData.choices[0]?.delta?.content.replaceAll('\n', `  \n`);
-          let num = getNum(historys.value[historyIndex].currentResponse.msg);
-          while (num) {
-            const docsNum = historys.value[historyIndex].currentResponse.docs.length;
-            historys.value[historyIndex].currentResponse.msg = historys.value[historyIndex].currentResponse.msg.replace(
-              `[[${num}]]`,
-              `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style="    cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd;    width: 20px;
-  height: 20px;
-  background: #FFFFFF;
-  border-radius: 4px 4px 4px 4px;
-  border: 1px solid #BACAE3;">${num}</span>`
-            );
-
-            num = getNum(historys.value[historyIndex].currentResponse.msg);
-          }
-        }
-        clearTimeout(timer);
-        timers.value.push(timer);
-      }, times.value * 15);
-    }
-  }
-
-  if (!historys.value[historyIndex].currentResponse.docs.length) {
-    if (rData.docs && rData.docs.length) {
-      handleDocs(rData.docs);
-    }
-  }
-  times.value++;
-  setTimeout(() => {
-    scrollToBottom()
-  }, 50)
-};
-
-const handleNetResponse = (msg, id) => {
-  const rData = msg.data;
-  if (!!rData && rData !== '[DONE]') {
-    try {
-      const res = JSON.parse(rData);
-      if (res.error) {
-        activeIndex.value = 4;
-        message.error(res.error);
-        historys.value[historyIndex].currentResponse.msg = res.error;
-
-        return;
-      }
-      if (res.rag_finish) {
-        if (activeIndex.value < 4) {
-          activeIndex.value = 4;
-        }
-        if (historys.value[historyIndex].dsChecked) {
-          endTime.value = Date.now();
-          var time = ((endTime.value - startTime.value) / 1000).toFixed(0);
-          historys.value[historyIndex].currentResponse.hintTxt = `已深度思考(用时 ${time} 秒)`;
-          historys.value[historyIndex].currentResponse.loading = false;
-        }
-      } else {
-        if (historys.value[historyIndex].dsChecked && historys.value[historyIndex].currentResponse.hintTxt != '思考中...') {
-          historys.value[historyIndex].currentResponse.hintTxt = '思考中...';
-        }
-      }
-      if (res.result) {
-        historys.value[historyIndex].currentResponse.originAnswer = res.result;
-        historys.value[historyIndex].currentResponse.msg = res.result.replaceAll('\n', `  \n`);
-
-        let num = getNum(historys.value[historyIndex].currentResponse.msg);
-
-        while (num) {
-          const docsNum = historys.value[historyIndex].currentResponse.docs.length;
-          if (docsNum && num > docsNum + 1) {
-            historys.value[historyIndex].currentResponse.msg = historys.value[historyIndex].currentResponse.msg.replace(`[[${num}]]`, ``);
-          }
-          historys.value[historyIndex].currentResponse.msg = historys.value[historyIndex].currentResponse.msg.replace(
-            `[[${num}]]`,
-            `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style="    cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd">${num}</span>`
-          );
-
-          num = getNum(historys.value[historyIndex].currentResponse.msg);
-        }
-      }
-      if (res.source_list && res.source_list.length && !historys.value[historyIndex].currentResponse.docs.length) {
-        handleDocs(res.source_list);
-      }
-    } catch (e) {}
-  }
-
-  if (rData === '[DONE]') {
-    console.log(historys.value[historyIndex].currentResponse.msg);
-  }
-};
-
-const handleDocs = (docs) => {
-  if (
-    docs.length === 1 &&
-    "<span style='color:red'>未找到相关文档,该回答为大模型自身能力解答!</span>" === docs[0]
-  ) {
-    return;
-  }
-
-  historys.value[historyIndex].currentResponse.docs = docs.map((v, i) => {
-    if (activeTab.value === 'net') {
-      return {
-        index: v.num,
-        doc: v.name,
-        link: v.url,
-        title: v.title,
-        summary: v.summary,
-        content: '',
-        showContent: false,
-        type: 'url'
-      };
-    }
-
-    if (v.toLowerCase().indexOf('.pdf') > 0) {
-      return {
-        index: i++,
-        doc: v.substring(v.toLowerCase().indexOf('] [') + 3, v.toLowerCase().indexOf('.pdf]') + 4),
-        link: v.substring(v.toLowerCase().indexOf('.pdf]') + 6, v.toLowerCase().indexOf('.pdf)') + 4),
-        content: v.substring(v.toLowerCase().indexOf('.pdf)') + 5),
-        showContent: false,
-        type: 'pdf'
-      };
-    } else if (v.toLowerCase().indexOf('.txt') > 0) {
-      return {
-        index: i++,
-        doc: v.toLowerCase().substring(v.indexOf('] [') + 3, v.toLowerCase().indexOf('.txt]') + 4),
-        link: v.toLowerCase().substring(v.indexOf('.txt]') + 6, v.toLowerCase().indexOf('.txt)') + 4),
-        content: v.toLowerCase().substring(v.indexOf('.txt)') + 5),
-        showContent: false,
-        type: 'txt'
-      };
-    } else if (v.toLowerCase().indexOf('.docx') > 0) {
-      return {
-        index: i++,
-        doc: v.substring(v.toLowerCase().indexOf('] [') + 3, v.toLowerCase().indexOf('.docx]') + 5),
-        link: v.substring(v.toLowerCase().indexOf('.docx]') + 7, v.toLowerCase().indexOf('.docx)') + 5),
-        content: v.substring(v.toLowerCase().indexOf('.docx)') + 6),
-        showContent: false,
-        type: 'docx'
-      };
-    }
-  });
-};
-
-const openDoc = (doc, i) => {
-  var link = doc.link;
-  var type = doc.type;
-  pdfSrc.value = link.replace(
-    window.AppGlobalConfig.knowledgeDocUrl.replace(
-      '=policy&',
-      activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
-    ),
-    window.AppGlobalConfig.knowledgeDocUrlProxy.replace(
-      '=policy&',
-      activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
-    )
-  );
-  showDoc.value = true;
-  fileType.value = type;
-  pdfContent.value = doc.content;
-  pdfNum.value = i;
-};
-
-const closeDoc = () => {
-  showDoc.value = false;
-};
-
-watch(
-  () => showDoc.value,
-  (newVal) => {
-    // 发布打开关闭,在关闭文档的时候打开相关案例
-    PubsubService.publish('switch-relevant-cases-box', newVal);
-  }
-);
-
-const openUrl = (url) => {
-  window.open(url, '_blank');
-};
-
-const openDocByIndex = (ind, id) => {
-  showDoc.value = false;
-  let link = null;
-  if (id !== historys.value[historyIndex].currentResponse.id) {
-    if (questHistories.value[id].docs[ind - 1].type === 'url') {
-      openUrl(questHistories.value[id].docs[ind - 1].link);
-      return;
-    }
-
-    link = questHistories.value[id].docs[ind - 1].link;
-    fileType.value = questHistories.value[id].docs[ind - 1].type;
-  } else {
-    if (historys.value[historyIndex].currentResponse.docs[ind - 1].type === 'url') {
-      openUrl(historys.value[historyIndex].currentResponse.docs[ind - 1].link);
-      return;
-    }
-    link = historys.value[historyIndex].currentResponse.docs[ind - 1].link;
-    fileType.value = historys.value[historyIndex].currentResponse.docs[ind - 1].type;
-    pdfContent.value = historys.value[historyIndex].currentResponse.docs[ind - 1].content;
-    pdfNum.value = historys.value[historyIndex].currentResponse.docs[ind - 1].num;
-  }
-  pdfNum.value = ind;
-  pdfSrc.value = link.replace(
-    window.AppGlobalConfig.knowledgeDocUrl.replace(
-      '=policy&',
-      activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
-    ),
-    window.AppGlobalConfig.knowledgeDocUrlProxy.replace(
-      '=policy&',
-      activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
-    )
-  );
-  showDoc.value = true;
-};
-window.openDocByIndex = openDocByIndex;
-const getNum = (str) => {
-  const matches = str.match(/\[\[(\d+)\]\]/);
-
-  if (matches) {
-    return matches[1]; // 输出: 2
-  } else {
-    return null;
-  }
-};
-
-// 埋点采集数据
-const collectQuestion = () => {
-  const { question, originAnswer, keywords = [] } = historys.value[historyIndex].currentResponse;
-  const userStore = useUserStore();
-  const param = {
-    question,
-    answer: originAnswer,
-    questionType: askType.value === 'zcfg' ? '政策法规' : '土地市场',
-    keywords: Array.isArray(keywords) ? keywords.join(',') : keywords,
-    user: '游客',
-    userId: '-1'
-  };
-  if (userStore.isLogin) {
-    const { id = '-1', displayName = '游客' } = userStore?.user?.user || {};
-    param.user = displayName;
-    param.userId = id;
-  }
-
-  ManagerAPI.collect(param).then((res) => {
-    if (res.data) {
-      // 记录日志,用来反馈
-      historys.value[historyIndex].currentResponse.logId = res.data;
-    }
-  });
-};
-
-const questions = ref([]);
-
-const getQuestionKeyWords = async () => {
-  const { question } = historys.value[historyIndex].currentResponse;
-  const myHeaders = new Headers();
-  myHeaders.append('Content-Type', 'application/json');
-  questions.value = [];
-  const raw = JSON.stringify({
-    query: `请从以下文本中提取核心关键词,确保关键词简洁明了且准确反映文本的主要内容。 请按照以下格式输出:关键词:关键词1,关键词2。文本如下:“${question}”`,
-    stream: false
-  });
-
-  const requestOptions = {
-    method: 'POST',
-    headers: myHeaders,
-    body: raw,
-    redirect: 'follow'
-  };
-
-  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
-  return fetch(rootUrl + '/chat/chat', requestOptions)
-    .then((response) => response.json())
-    .then((msgStr) => {
-      const msg = JSON.parse(msgStr)
-      activeIndex.value = 1;
-      const str = msg.choices[0]?.message?.content;
-      if (str) {
-        const str1 = str.slice(str.indexOf("</think>")+7)
-        const keywords = splitWords(str1).slice(0, 3);
-        historys.value[historyIndex].currentResponse.keywords = keywords;
-      }
-    })
-    .catch((error) => console.error(error));
-};
-
-const splitWords = (word) => {
-  if (word) {
-    word = word
-      .replaceAll('核心词为:', '')
-      .replaceAll('问题核心词:', '')
-      .replaceAll('>', '')
-      .replaceAll('。', '')
-      .trim();
-
-    if (word.indexOf('\n\n') !== -1) {
-      return word.split('\n\n');
-    }
-
-    if (word.indexOf(',') !== -1) {
-      return word.split(',');
-    }
-
-    if (word.indexOf('、') !== -1) {
-      return word.split('、');
-    }
-
-    if (word.indexOf(',') !== -1) {
-      return word.split(',');
-    }
-
-    return [word];
-  }
-
-  return [];
-};
-
-
-const changeActiveTab = (tab) => {
-  activeTab.value = tab;
-};
-
-const stopAI = () => {
-  if (ctr) {
-    ctr.abort();
-  }
-};
-
-
-defineExpose({ changeActiveTab, stopAI });
-
-</script>
-
-<style lang="scss" scoped>
-@import './index.scss';
-</style>

+ 1 - 1
ais_search_zj/web/src/views/zjjd/index.vue

@@ -143,7 +143,7 @@ const chatDesc = ref([]);
 const keyword = ref('');
 const ctr = new AbortController();
 const fileDetail = ref(null);
-const fileUrl = ref('');
+const fileUrl = ref('https://ai.zrzyt.zj.gov.cn/aisKnowledge/infra/file/0/get/4300d7c65567a57cb5de4222b2f4964bbe0df7678711154bb44f7c1282f840d0_20250423_094824.pdf');
 const aiLoading = ref(false);
 const queryText = ref('分别给出该文章的全文综述和要点总结');
 const content = ref('');