Browse Source

功能优化

songxy 2 months ago
parent
commit
dccd27eab2

+ 0 - 1
ais_search_zj/web/components.d.ts

@@ -23,7 +23,6 @@ declare module 'vue' {
     PDFReader: typeof import('./src/components/pdf/PDFReader.vue')['default']
     PDFReader: typeof import('./src/components/pdf/PDFReader.vue')['default']
     PDFViewer: typeof import('./src/components/pdf/PDFViewer.vue')['default']
     PDFViewer: typeof import('./src/components/pdf/PDFViewer.vue')['default']
     PDFViewerSearch: typeof import('./src/components/pdf/PDFViewerSearch.vue')['default']
     PDFViewerSearch: typeof import('./src/components/pdf/PDFViewerSearch.vue')['default']
-    Progress: typeof import('./src/components/Progress.vue')['default']
     ProgressBar: typeof import('./src/components/ProgressBar.vue')['default']
     ProgressBar: typeof import('./src/components/ProgressBar.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     RouterView: typeof import('vue-router')['RouterView']

+ 1 - 2
ais_search_zj/web/public/config.js

@@ -55,9 +55,8 @@
     server: '/server',
     server: '/server',
     authServer: '/auth',
     authServer: '/auth',
     policyServer: 'https://zjugpt.com/server',
     policyServer: 'https://zjugpt.com/server',
-    chatServer: '/chat',
     aiServer: '/aiServer',  //省厅
     aiServer: '/aiServer',  //省厅
-    knowledgeServer: '/aisChat',  //温州
+    aisChat: '/aisChat',  //温州
     knowledgeServer2: 'https://natureai.zjugis.com/liqchat',
     knowledgeServer2: 'https://natureai.zjugis.com/liqchat',
     knowledgeDocUrl: 'http://127.0.0.1:20331/',
     knowledgeDocUrl: 'http://127.0.0.1:20331/',
     knowledgeDocUrl2: 'http://127.0.0.1:20339/',
     knowledgeDocUrl2: 'http://127.0.0.1:20339/',

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

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

+ 0 - 10
ais_search_zj/web/src/permission/router-permission.js

@@ -1,10 +0,0 @@
-import { useUserStore } from '@/stores';
-
-export const hasPermission = (menu, userMenus = []) => {
-  return userMenus.findIndex(v => v.pmsCode === menu?.meta?.permission) !== -1;
-}
-
-export const filterRouters = (menus = []) => {
-  const { user } = useUserStore();
-  return menus.filter(menu => hasPermission(menu, user.menus));
-}

+ 6 - 6
ais_search_zj/web/src/router/routes.js

@@ -11,15 +11,15 @@ export default [
         meta: { title: '首页' },
         meta: { title: '首页' },
         component: () => import('@/views/home/index.vue'),
         component: () => import('@/views/home/index.vue'),
         children: [
         children: [
+          // {
+          //   path: 'home',
+          //   name: 'Home',
+          //   meta: { title: 'AI搜索' },
+          //   component: () => import('@/views/home/home.vue'),
+          // },
           {
           {
             path: 'home',
             path: 'home',
             name: 'Home',
             name: 'Home',
-            meta: { title: 'AI搜索' },
-            component: () => import('@/views/home/home.vue'),
-          },
-          {
-            path: 'home2',
-            name: 'Home2',
             meta: { title: 'AI搜索2' },
             meta: { title: 'AI搜索2' },
             component: () => import('@/views/ai-home/index.vue'),
             component: () => import('@/views/ai-home/index.vue'),
           },
           },

+ 3 - 3
ais_search_zj/web/src/views/ai-home/ai-search.vue

@@ -1149,7 +1149,7 @@ const quest = async (isFllow) => {
     currentResponse.value.mockStart = true;
     currentResponse.value.mockStart = true;
     streamToAnswer();
     streamToAnswer();
   }
   }
-  const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = dsChecked.value ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   await fetchEventSource(rootUrl + questionUrl, {
   await fetchEventSource(rootUrl + questionUrl, {
     method: 'POST',
     method: 'POST',
     openWhenHidden: true,
     openWhenHidden: true,
@@ -1576,7 +1576,7 @@ for example:
     redirect: 'follow'
     redirect: 'follow'
   };
   };
 
 
-  const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = dsChecked.value ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   fetch(rootUrl + '/chat/chat', requestOptions)
   fetch(rootUrl + '/chat/chat', requestOptions)
     .then((response) => {
     .then((response) => {
       return response.json()
       return response.json()
@@ -1637,7 +1637,7 @@ const getQuestionKeyWords = async () => {
     redirect: 'follow'
     redirect: 'follow'
   };
   };
 
 
-  const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = dsChecked.value ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   return fetch(rootUrl + '/chat/chat', requestOptions)
   return fetch(rootUrl + '/chat/chat', requestOptions)
     .then((response) => response.json())
     .then((response) => response.json())
     .then((msgStr) => {
     .then((msgStr) => {

+ 1 - 1
ais_search_zj/web/src/views/ai-home/index.scss

@@ -387,7 +387,7 @@
                     color: #888;
                     color: #888;
                     display: flex;
                     display: flex;
                     border-radius: 8px 8px 8px 8px;
                     border-radius: 8px 8px 8px 8px;
-                    padding: 8px 0px;
+                    padding: 0px;
                     .doc {
                     .doc {
                       display: flex;
                       display: flex;
                       width: 100%;
                       width: 100%;

+ 7 - 10
ais_search_zj/web/src/views/ai-home/index.vue

@@ -147,7 +147,7 @@
                                         }"
                                         }"
                                       />
                                       />
                                     </div>
                                     </div>
-                                    <div class="source" v-if="activeTab !== 'original'" v-show="activeIndex === 5">
+                                    <div class="source" v-if="activeTab !== 'original' && history.currentResponse.docs.length > 0" v-show="activeIndex === 5">
                                       <div class="title">
                                       <div class="title">
                                         <span>基于{{ history.currentResponse.docs.length }}个参考来源</span>
                                         <span>基于{{ history.currentResponse.docs.length }}个参考来源</span>
                                         <span class="icon" @click="history.sourceVisible = !history.sourceVisible">
                                         <span class="icon" @click="history.sourceVisible = !history.sourceVisible">
@@ -728,7 +728,7 @@ const quest = async (isFllow) => {
     historys.value[historyIndex].currentResponse.mockStart = true;
     historys.value[historyIndex].currentResponse.mockStart = true;
     streamToAnswer();
     streamToAnswer();
   }
   }
-  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   await fetchEventSource(rootUrl + questionUrl, {
   await fetchEventSource(rootUrl + questionUrl, {
     method: 'POST',
     method: 'POST',
     openWhenHidden: true,
     openWhenHidden: true,
@@ -1134,11 +1134,7 @@ const switchSession = async (item) => {
     nums.forEach(num => {
     nums.forEach(num => {
       msg = msg.replace(
       msg = msg.replace(
             `[[${num}]]`,
             `[[${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>`
+            ''
         )
         )
     })
     })
     const docs = JSON.parse(item['answerSources']) || []
     const docs = JSON.parse(item['answerSources']) || []
@@ -1151,12 +1147,13 @@ const switchSession = async (item) => {
         loading: false,
         loading: false,
         hintTxt: `已深度思考(用时 ${item['thinkTime']} 秒)`,
         hintTxt: `已深度思考(用时 ${item['thinkTime']} 秒)`,
         msg: msg,
         msg: msg,
-        docs: docs
+        sourceVisible: false,
+        docs: []
       }
       }
     })
     })
     activeIndex.value = 5
     activeIndex.value = 5
     historyIndex = index;
     historyIndex = index;
-    handleDocs(docs)
+    // handleDocs(docs)
   })
   })
 }
 }
 const onSessionDeleteHandle = (id) => {
 const onSessionDeleteHandle = (id) => {
@@ -1217,7 +1214,7 @@ const getQuestionKeyWords = async () => {
     redirect: 'follow'
     redirect: 'follow'
   };
   };
 
 
-  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   return fetch(rootUrl + '/chat/chat', requestOptions)
   return fetch(rootUrl + '/chat/chat', requestOptions)
     .then((response) => response.json())
     .then((response) => response.json())
     .then((msgStr) => {
     .then((msgStr) => {

+ 3 - 3
ais_search_zj/web/src/views/ai-search/ai-search.vue

@@ -1149,7 +1149,7 @@ const quest = async (isFllow) => {
     currentResponse.value.mockStart = true;
     currentResponse.value.mockStart = true;
     streamToAnswer();
     streamToAnswer();
   }
   }
-  const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = dsChecked.value ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   await fetchEventSource(rootUrl + questionUrl, {
   await fetchEventSource(rootUrl + questionUrl, {
     method: 'POST',
     method: 'POST',
     openWhenHidden: true,
     openWhenHidden: true,
@@ -1576,7 +1576,7 @@ for example:
     redirect: 'follow'
     redirect: 'follow'
   };
   };
 
 
-  const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = dsChecked.value ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   fetch(rootUrl + '/chat/chat', requestOptions)
   fetch(rootUrl + '/chat/chat', requestOptions)
     .then((response) => {
     .then((response) => {
       return response.json()
       return response.json()
@@ -1637,7 +1637,7 @@ const getQuestionKeyWords = async () => {
     redirect: 'follow'
     redirect: 'follow'
   };
   };
 
 
-  const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = dsChecked.value ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   return fetch(rootUrl + '/chat/chat', requestOptions)
   return fetch(rootUrl + '/chat/chat', requestOptions)
     .then((response) => response.json())
     .then((response) => response.json())
     .then((msgStr) => {
     .then((msgStr) => {

+ 3 - 3
ais_search_zj/web/src/views/ai-search/index.vue

@@ -736,7 +736,7 @@ const quest = async (isFllow) => {
     historys.value[historyIndex].currentResponse.mockStart = true;
     historys.value[historyIndex].currentResponse.mockStart = true;
     streamToAnswer();
     streamToAnswer();
   }
   }
-  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   await fetchEventSource(rootUrl + questionUrl, {
   await fetchEventSource(rootUrl + questionUrl, {
     method: 'POST',
     method: 'POST',
     openWhenHidden: true,
     openWhenHidden: true,
@@ -1123,7 +1123,7 @@ for example:
     redirect: 'follow'
     redirect: 'follow'
   };
   };
 
 
-  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   fetch(rootUrl + '/chat/chat', requestOptions)
   fetch(rootUrl + '/chat/chat', requestOptions)
     .then((response) => {
     .then((response) => {
       return response.json()
       return response.json()
@@ -1184,7 +1184,7 @@ const getQuestionKeyWords = async () => {
     redirect: 'follow'
     redirect: 'follow'
   };
   };
 
 
-  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
+  const rootUrl = modelType.value === '1' ? window.AppGlobalConfig.aisChat : window.AppGlobalConfig.aiServer
   return fetch(rootUrl + '/chat/chat', requestOptions)
   return fetch(rootUrl + '/chat/chat', requestOptions)
     .then((response) => response.json())
     .then((response) => response.json())
     .then((msgStr) => {
     .then((msgStr) => {

+ 0 - 1810
ais_search_zj/web/src/views/ai-search/index3.vue

@@ -1,1810 +0,0 @@
-<template>
-  <div class="ai-search-detail">
-    <div class="search-panel" id="pageContainer">
-      <div :class="`search-detail search-detail-${askType}`" ref="searchRef">
-        <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="tabs" style="position: relative">
-                  <div
-                    :class="`tab${activeTab === t.key ? ' tab-active' : ''}`"
-                    v-for="t in tabs"
-                    :key="t.key"
-                    @click="changeTab(t.key)"
-                  >
-                    <div class="title">{{ t.name }}</div>
-                    <div class="bottom"></div>
-                  </div>
-                  <div
-                    class="scope-type"
-                    style="
-                      display: flex;
-                      align-items: center;
-                      position: absolute;
-                      right: 5px;
-                      top: 10px;
-                    "
-                  >
-                    <div class="label">搜索模式:</div>
-                    <a-radio-group
-                      v-model:value="answerType"
-                      @change="changeAnswerType"
-                      name="radioGroup"
-                    >
-                      <a-radio value="0">简洁</a-radio>
-                      <a-radio value="1">深入</a-radio>
-                      <a-radio value="2">研究</a-radio>
-                    </a-radio-group>
-                    <div class="ds-box">
-                      <div @click="onChange('1')" :class="{active: dsChecked === true}">
-                        <i class="iconfont icon-deepsee"></i>
-                      </div>
-                      <div @click="onChange('0')" :class="{active: dsChecked === false}">
-                        <i class="iconfont icon-tongyi"></i>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-                <div class="result-view">
-                  <div class="q-r">
-                    <div class="ds-content-box" v-if="dsChecked">
-                      <div class="icon">
-                        <img src="/images/icon-ds.png" />
-                      </div>
-                      <div class="ds-panel">
-                        <div class="ds-loading" v-if="dsHintTxt" @click="dsUp = !dsUp">
-                          {{ dsHintTxt }}
-                          <DownOutlined class="icon-arrow" :class="{ rotate: dsUp }" />
-                        </div>
-                        <div class="ds-con" v-if="!dsUp">
-                          <vue-markdown-it
-                            id="dsMarkdown"
-                            :source="
-                              currentResponse.streamMock
-                                ? currentResponse.streamMsg.indexOf('###') > -1
-                                  ? currentResponse.streamMsg.substring(
-                                      0,
-                                      currentResponse.streamMsg.indexOf('###')
-                                    )
-                                  : currentResponse.streamMsg
-                                : currentResponse.msg.indexOf('###') > -1
-                                ? currentResponse.msg.substring(
-                                    0,
-                                    currentResponse.msg.indexOf('###')
-                                  )
-                                : currentResponse.msg
-                            "
-                            :options="{
-                              html: true,
-                              linkify: true
-                            }"
-                          />
-                        </div>
-                        <a-spin :indicator="indicator" v-if="dsLoading" />
-                      </div>
-                    </div>
-                    <vue-markdown-it
-                      id="resMarkdown"
-                      :source="
-                        currentResponse.streamMock
-                          ? currentResponse.streamMsg.indexOf('###') > -1
-                            ? currentResponse.streamMsg.substring(
-                                currentResponse.streamMsg.indexOf('###')
-                              )
-                            : ''
-                          : dsChecked
-                          ? currentResponse.msg.indexOf('###') > -1
-                            ? currentResponse.msg.substring(currentResponse.msg.indexOf('###'))
-                            : ''
-                          : currentResponse.msg
-                      "
-                      :options="{
-                        html: true,
-                        linkify: true
-                      }"
-                    />
-                  </div>
-                  <a-skeleton
-                    active
-                    v-if="!dsLoading && (activeIndex < 2 || !currentResponse.msg)"
-                  />
-                  <div class="more-questions" v-if="activeIndex >= 4">
-                    <div class="title" style="margin-bottom: 14px">
-                      你可以继续问我:
-                      <div class="change-title" @click="changeRecommendedQuestions">
-                        <div class="change-icon"></div>
-                        换一批
-                      </div>
-                    </div>
-                    <div class="questions" style="cursor: pointer">
-                      <div
-                        class="question"
-                        @click="openRecommendedQuestion(q)"
-                        v-for="(q, i) in questions"
-                        :key="i"
-                        style="
-                          line-height: 14px;
-                          text-align: left;
-                          font-style: normal;
-                          text-transform: none;
-                          width: fit-content;
-                          margin-bottom: 12px;
-                          background: #ffffff;
-                          box-shadow: 0px 1px 8px 0px #e4e4e4;
-                          border-radius: 10px;
-                          border: 1px solid #e9e9e9;
-                          padding: 16px;
-                          font-family: PingFang SC;
-                          font-weight: 500;
-                          font-size: 14px;
-                          color: #2185f2;
-                        "
-                      >
-                        {{ q }}
-                      </div>
-                    </div>
-                  </div>
-                  <div class="source" v-if="activeTab !== 'original'" v-show="activeIndex === 5">
-                    <div class="title">
-                      <div class="icon"></div>
-                      <div class="text">来源</div>
-                      <div class="num">({{ currentResponse.docs.length }})</div>
-                    </div>
-                    <div class="items">
-                      <div
-                        class="item"
-                        v-if="activeTab !== 'net'"
-                        v-for="(doc, i) in 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 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="!currentResponse.msg" />
-                          <vue-markdown-it
-                            :source="
-                              currentResponse.streamMock
-                                ? currentResponse.streamMsg
-                                : currentResponse.msg
-                            "
-                            :options="{
-                              html: true,
-                              linkify: true
-                            }"
-                          />
-                        </div>
-                      </div>
-                      <div class="chart-card" v-if="currentResponse.hasChart" id="tdscChartCard">
-                        <div class="chart-title">生成图表</div>
-                        <a-skeleton
-                          active
-                          style="height: 100%"
-                          v-if="!currentResponse.chartOption"
-                        />
-                        <div v-else class="chart" id="summaryChart"></div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </template>
-            </div>
-          </div>
-        </div>
-        <!--   历史回答     -->
-        <div
-          v-show="askType === 'zcfg'"
-          class="search-result result-history"
-          style="overflow-y: hidden; height: auto"
-          v-if="questHistories.length > 0"
-          id="messageContainer"
-        >
-          <template v-for="(qh, i) in [...questHistories].reverse()" :key="`qh-${i}`">
-            <div class="top">
-              <div class="title">
-                <div class="icon"></div>
-                <div class="question" style="cursor: pointer">{{ qh.question }}</div>
-              </div>
-            </div>
-
-            <div class="result-panel">
-              <div class="result">
-                <div class="tabs">
-                  <div :class="`tab tab-active`">
-                    <div class="title">回答</div>
-                    <div class="bottom"></div>
-                  </div>
-                </div>
-              </div>
-              <div class="result-view">
-                <div class="q-r">
-                  <vue-markdown-it
-                    :source="qh.msg"
-                    :toc="true"
-                    :options="{
-                      html: true,
-                      linkify: true
-                    }"
-                  />
-                </div>
-              </div>
-            </div>
-          </template>
-        </div>
-      </div>
-      <a-affix v-if="showDoc" style="width: 52%; font-size: 16px" :offset-top="210">
-        <div
-          class="doc"
-          style="width: 100%; background-color: white; height: calc(100vh - 190px); 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>
-      </a-affix>
-    </div>
-
-    <a-modal force-render class="input-modal" v-model:open="open" :closable="false" width="700px">
-      <template #footer>
-        <div class="footer" style="height: 26px; margin: 0 auto">
-          <div class="left" style="float: left; cursor: pointer" @click="clearModalTextArea">
-            清空
-          </div>
-          <a-button style="float: right" type="primary" size="small" @click="sendModalTextArea">
-            发送
-          </a-button>
-        </div>
-      </template>
-      <div class="modal-que">
-        <ai-textarea :q="question" ref="aiTextAreaRef" @enter="ask" />
-      </div>
-    </a-modal>
-  </div>
-</template>
-<script setup>
-import { fetchEventSource } from '@microsoft/fetch-event-source';
-import PDFViewer from '@/components/pdf/PdfCanvas.vue';
-import WordViewer from '@/components/pdf/WordViewer.vue';
-import AiTextarea from '@/components/TextArea/AiTextarea.vue';
-import { VueMarkdownIt } from '@f3ve/vue-markdown-it';
-import TxtViewer from '@/components/pdf/TxtViewer.vue';
-import { message } from 'ant-design-vue';
-import { ref, defineProps, watch } from 'vue';
-import CommonAPI from '@/api/common';
-import ManagerAPI from '@/api/manager';
-import PubsubService from '@/utils/PubsubService';
-import { useUserStore } from '@/stores';
-const aiLoading = ref(false);
-const router = useRouter();
-import { LoadingOutlined, DownOutlined } from '@ant-design/icons-vue';
-import { h } from 'vue';
-const indicator = h(LoadingOutlined, {
-  style: {
-    fontSize: '24px'
-  },
-  spin: true
-});
-const props = defineProps({
-  searchType: '',
-  askType: {
-    type: String,
-    default: 'zcfg'
-  }
-});
-
-const answerType = ref('0');
-const dsChecked = ref(true);
-const scope = ref('net');
-watch(
-  () => router.currentRoute.value,
-  (value) => {
-    const { query } = value;
-    if (query.q) {
-      ask(decodeURIComponent(query.q));
-      answerType.value = query.type || '0';
-      scope.value = query.scope || 'net';
-      dsHintTxt.value = '';
-    }
-  }
-);
-
-watch(
-  () => router.currentRoute.value,
-  (value) => {
-    const { query } = value;
-    if (query.q) {
-      var ds = query.ds;
-      if (ds && ds == '1') {
-        ds = true;
-      } else {
-        ds = false;
-      }
-      dsChecked.value = ds;
-    }
-  },
-  {
-    immediate: true
-  }
-);
-
-watch(props.searchType, (val) => {
-  activeTab.value = val;
-});
-
-const question = ref('国有土地的使用方式有哪些?');
-const statusText = ref('检索中');
-const activeIndex = ref(0);
-const activeTab = ref(props.searchType);
-let ctr = null;
-const aiTextAreaRef = ref(null);
-const searchRef = ref(null);
-
-const evaluate = ref(null);
-
-const clearModalTextArea = () => {
-  aiTextAreaRef.value.clear();
-};
-
-const sendModalTextArea = () => {
-  aiTextAreaRef.value.send();
-};
-
-const open = ref(false);
-const showDoc = ref(false);
-const pdfSrc = ref('');
-const pdfContent = ref('');
-const pdfNum = ref(1);
-const fileType = ref('pdf');
-const dsLoading = ref(true);
-const startTime = ref(0);
-const endTime = ref(0);
-const dsHintTxt = ref('');
-const dsUp = ref(false);
-const times = ref(0);
-const timers = ref([]);
-const currentResponse = ref({
-  streamMsg: '',
-  streamMock: false,
-  msg: '',
-  originAnswer: '',
-  docs: []
-});
-let streamMockInterval = null;
-const streamToAnswer = () => {
-  console.log('streamToAnswer');
-  currentResponse.value.index = 0;
-  streamMockInterval = setInterval(() => {
-    const { originAnswer = '', msg, streamMsg = '', id, index = 0 } = currentResponse.value;
-
-    if (currentResponse.value.mockStart || index <= originAnswer.length) {
-      currentResponse.value.streamMsg = originAnswer.substr(0, index + 2).replaceAll('\n', '  \n');
-      if (originAnswer) {
-        currentResponse.value.index += 2;
-      }
-      let num = getNum(currentResponse.value.streamMsg);
-
-      while (num) {
-        const docsNum = currentResponse.value.docs.length;
-        currentResponse.value.streamMsg = currentResponse.value.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(currentResponse.value.streamMsg);
-      }
-    } else {
-      if (streamMockInterval) {
-        clearInterval(streamMockInterval);
-        streamMockInterval = null;
-      }
-
-      activeIndex.value = 5;
-
-      searchRef.value.scrollTop = 0;
-      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 search = (q, type = 'zcfg') => {
-  question.value = q;
-  askType.value = type;
-  if (ctr) {
-    ctr.abort();
-    ctr = null;
-    if (streamMockInterval) {
-      clearInterval(streamMockInterval);
-      streamMockInterval = null;
-    }
-  }
-  if (type === 'zcfg') {
-    quest();
-  } else {
-  }
-};
-
-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 (dsChecked.value) {
-    dsHintTxt.value = '';
-    dsLoading.value = true;
-  }
-  question.value = q;
-  open.value = false;
-  showDoc.value = false;
-  askType.value = 'zcfg';
-  // activeTab.value = 'knowledge';
-  quest(isFllow);
-};
-
-let questionUrl = '/chat/kb_chat';
-
-const changeTab = (tab) => {
-  dsLoading.value = dsChecked.value;
-  dsHintTxt.value = '';
-  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;
-  if (ctr) {
-    if (streamMockInterval) {
-      clearInterval(streamMockInterval);
-      streamMockInterval = null;
-    }
-    ctr.abort();
-  }
-  quest();
-};
-const onChange = (type) => {
-  dsChange(type);
-  changeAnswerType();
-};
-//ds绑定用户改变
-const dsChange = (type) => {
-  dsChecked.value = type === '0' ? false : true;
-  localStorage.setItem("_isDeepSeek", type);
-  if (timers.value) {
-    timers.value.forEach((t) => {
-      clearTimeout(t);
-      t = null;
-    });
-    timers.value = [];
-  }
-  times.value = 0;
-  dsHintTxt.value = '';
-  //打字机效果 切换会打印
-  aiLoading.value = true;
-};
-const changeAnswerType = () => {
-  if (!dsChecked.value) {
-    dsLoading.value = false;
-    dsHintTxt.value = '';
-  } else {
-    dsLoading.value = true;
-    dsHintTxt.value = '';
-  }
-  if (ctr) {
-    if (streamMockInterval) {
-      clearInterval(streamMockInterval);
-      streamMockInterval = null;
-    }
-    ctr.abort();
-  }
-  quest();
-};
-
-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;
-  }
-  searchRef.value.scrollTop = 0;
-  if (isFllow) {
-    const { id, question, msg, docs, originAnswer, keywords = [] } = currentResponse.value;
-    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;
-  currentResponse.value = {
-    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: dsChecked.value ? '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' : dsChecked.value ? window?.AppGlobalConfig?.llm?.kb_name : 'policy',
-      top_k: topKs[answerType.value],
-      search_type: answerType.value,
-      score_threshold: 0.5,
-      model: dsChecked.value ? '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') {
-    currentResponse.value.streamMock = true;
-    currentResponse.value.mockStart = true;
-    streamToAnswer();
-  }
-  const rootUrl = dsChecked.value ? 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 (currentResponse.value.streamMock) {
-        currentResponse.value.mockStart = false;
-      } else {
-        activeIndex.value = 5;
-
-        searchRef.value.scrollTop = 0;
-      }
-    },
-    onerror(err) {
-      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 = [];
-      }
-      if (dsChecked.value) {
-        endTime.value = Date.now();
-        var time = ((endTime.value - startTime.value) / 1000).toFixed(0);
-        dsHintTxt.value = `已深度思考(用时 ${time} 秒)`;
-        dsLoading.value = false;
-        aiLoading.value=true;
-      }
-        currentResponse.value.originAnswer = rData.choices[0]?.delta?.content.replaceAll(
-          '\n',
-          `  \n`
-        );
-        currentResponse.value.msg = rData.choices[0]?.delta?.content.replaceAll('\n', `  \n`);
-        let num = getNum(currentResponse.value.msg);
-        while (num) {
-          const docsNum = currentResponse.value.docs.length;
-          if (docsNum && num > docsNum + 1) {
-          }
-
-          currentResponse.value.msg = currentResponse.value.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(currentResponse.value.msg);
-        }
-    } else {
-      aiLoading.value = false;
-      if (dsChecked.value) {
-        dsHintTxt.value = '思考中...';
-        //ds模式打字机效果输出
-        const timer = setTimeout(() => {
-          if (!aiLoading.value) {
-            currentResponse.value.originAnswer += rData.choices[0]?.delta?.content.replaceAll(
-              '\n',
-              `  \n`
-            );
-            currentResponse.value.msg += rData.choices[0]?.delta?.content.replaceAll('\n', `  \n`);
-            let num = getNum(currentResponse.value.msg);
-            while (num) {
-              const docsNum = currentResponse.value.docs.length;
-              if (docsNum && num > docsNum + 1) {
-              }
-
-              currentResponse.value.msg = currentResponse.value.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(currentResponse.value.msg);
-            }
-          }
-          clearTimeout(timer);
-          timers.value.push(timer);
-        }, times.value * 15);
-      } else {
-        currentResponse.value.originAnswer += rData.choices[0]?.delta?.content.replaceAll(
-          '\n',
-          `  \n`
-        );
-        currentResponse.value.msg += rData.choices[0]?.delta?.content.replaceAll('\n', `  \n`);
-        let num = getNum(currentResponse.value.msg);
-        while (num) {
-          const docsNum = currentResponse.value.docs.length;
-          if (docsNum && num > docsNum + 1) {
-          }
-
-          currentResponse.value.msg = currentResponse.value.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(currentResponse.value.msg);
-        }
-      }
-    }
-  }
-
-  if (!currentResponse.value.docs.length) {
-    if (rData.docs && rData.docs.length) {
-      handleDocs(rData.docs);
-    }
-  }
-  times.value++;
-};
-
-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);
-        currentResponse.value.msg = res.error;
-
-        return;
-      }
-      if (res.rag_finish) {
-        if (activeIndex.value < 4) {
-          activeIndex.value = 4;
-        }
-        if (dsChecked.value) {
-          endTime.value = Date.now();
-          var time = ((endTime.value - startTime.value) / 1000).toFixed(0);
-          dsHintTxt.value = `已深度思考(用时 ${time} 秒)`;
-          dsLoading.value = false;
-        }
-      } else {
-        if (dsChecked.value && dsHintTxt.value != '思考中...') {
-          dsHintTxt.value = '思考中...';
-        }
-      }
-      if (res.result) {
-        currentResponse.value.originAnswer = res.result;
-        currentResponse.value.msg = res.result.replaceAll('\n', `  \n`);
-
-        let num = getNum(currentResponse.value.msg);
-
-        while (num) {
-          const docsNum = currentResponse.value.docs.length;
-          if (docsNum && num > docsNum + 1) {
-            currentResponse.value.msg = currentResponse.value.msg.replace(`[[${num}]]`, ``);
-          }
-          currentResponse.value.msg = currentResponse.value.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(currentResponse.value.msg);
-        }
-      }
-      if (res.source_list && res.source_list.length && !currentResponse.value.docs.length) {
-        handleDocs(res.source_list);
-      }
-    } catch (e) {}
-  }
-
-  if (rData === '[DONE]') {
-    console.log(currentResponse.value.msg);
-  }
-};
-
-const handleDocs = (docs) => {
-  if (
-    docs.length === 1 &&
-    "<span style='color:red'>未找到相关文档,该回答为大模型自身能力解答!</span>" === docs[0]
-  ) {
-    return;
-  }
-
-  currentResponse.value.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) => {
-  if (window.AppGlobalConfig.isDisabledSource) return;
-  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 !== currentResponse.value.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 (currentResponse.value.docs[ind - 1].type === 'url') {
-      openUrl(currentResponse.value.docs[ind - 1].link);
-      return;
-    }
-    link = currentResponse.value.docs[ind - 1].link;
-    fileType.value = currentResponse.value.docs[ind - 1].type;
-    pdfContent.value = currentResponse.value.docs[ind - 1].content;
-    pdfNum.value = currentResponse.value.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 = [] } = currentResponse.value;
-  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) {
-      // 记录日志,用来反馈
-      currentResponse.value.logId = res.data;
-    }
-  });
-};
-
-const questions = ref([]);
-let recommendedQuestions = [];
-let recommendedQuestionsIndex = 0;
-const getRecommendedQuestion = async (b = false) => {
-  const { question } = currentResponse.value;
-  const myHeaders = new Headers();
-  myHeaders.append('Content-Type', 'application/json');
-  questions.value = [];
-  if (!b) {
-    recommendedQuestions = [];
-  }
-  recommendedQuestionsIndex = 0;
-  const raw = JSON.stringify({
-    query: `你会收到一个用户提问,请根据问题延伸出10个子问题。
-
-在你回答问题的时候,还需要注意推荐给用户的问题必须以列表的形式返回。
-for example:
-[
-    "1、南京市在推进产业用地高质量利用方面采取了哪些具体措施?这些措施的效果如何?",
-    "2、杭州市创新型产业用地管理的具体实施细则是什么?这些细则如何促进传统产业转型升级?",
-    "3、南京市和杭州市在土地供应方式上有哪些不同?这些不同如何影响各自的产业发展?",
-    "4、南京市如何通过政策支持和激励措施吸引重大投资项目?这些措施的实际效果如何?",
-    "5、杭州市“工业上楼”项目的实施情况如何?这一政策对提升土地利用效率有何影响?",
-]
-
-以下是用户提问:${question}`,
-    // model: 'qwen1.5-chat',
-    stream: false
-  });
-
-  const requestOptions = {
-    method: 'POST',
-    headers: myHeaders,
-    body: raw,
-    redirect: 'follow'
-  };
-
-  const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
-  fetch(rootUrl + '/chat/chat', requestOptions)
-    .then((response) => {
-      return response.json()
-    })
-    .then((msgStr) => {
-      const msg = JSON.parse(msgStr)
-      const str = msg.choices[0]?.message?.content;
-      if (str) {
-        const str1 = str.slice(str.indexOf("</think>") + 7)
-        recommendedQuestions = formatRecommendedQuestions(str1);
-        questions.value = recommendedQuestions.slice(0, 5);
-      }
-    })
-    .catch((error) => console.error(error));
-};
-const formatRecommendedQuestions = (str) => {
-  return str
-    .replace(/`|$$|$$|"|,/g, '')  // 清除引号、反引号、方括号和逗号
-    .split('\n')                  // 按换行分割
-    .map(line => line.trim())     // 去除首尾空格
-    .filter(line => {             // 双条件过滤
-      return (
-        line.length > 0 &&         // 过滤空行
-        /^\d+[、.]/.test(line)     // 匹配"1、"或"1."开头
-      )
-    })
-    .map(line => {
-      // 统一标点为中文顿号并保留末尾空格
-      return line
-        .replace(/^\d+\./, m => m.replace('.', '、')) // 英文标点转中文
-        .replace(/\s*$/, '   ')                       // 强制保留末尾3空格
-    });
-}
-const changeRecommendedQuestions = () => {
-  recommendedQuestionsIndex++;
-  if (recommendedQuestionsIndex % 2 === 0) {
-    questions.value = recommendedQuestions.slice(0, 5);
-  } else {
-    questions.value = recommendedQuestions.slice(5);
-    getRecommendedQuestion(true)
-  }
-};
-
-const getQuestionKeyWords = async () => {
-  const { question } = currentResponse.value;
-  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 = dsChecked.value ? 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);
-        currentResponse.value.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 exportAnswer = (type) => {
-  const { question, originAnswer } = currentResponse.value;
-  CommonAPI.answerExport(type, { question, answer: originAnswer });
-};
-
-const copy = async () => {
-  try {
-    const input = document.createElement('input');
-    input.value = currentResponse.value.msg;
-    document.body.appendChild(input);
-    input.select();
-    document.execCommand('copy');
-    document.body.removeChild(input);
-
-    message.info('内容已复制到剪贴板');
-  } catch (err) {
-    console.error(err);
-    // console.error('复制到剪贴板失败', err);
-  }
-};
-
-const changeActiveTab = (tab) => {
-  activeTab.value = tab;
-};
-
-const stopAI = () => {
-  if (ctr) {
-    ctr.abort();
-  }
-};
-
-const openRecommendedQuestion = (q) => {
-  if (q) {
-    if (q.charAt(1) === '、') {
-      q = q.substring(2);
-    }
-    window.open(
-      `/aisearch/#/ai-search?q=${encodeURI(q.trim())}&scope=${activeTab.value}&type=${answerType.value}`,
-      '_blank'
-    );
-  }
-};
-onMounted(() => {
-  const { query } = router.currentRoute.value;
-  if (query.q) {
-    answerType.value = query.type || '0';
-    scope.value = query.scope || 'net';
-    var ds = '0';
-    if (!useUserStore().isLogin) {
-      ds = query.ds;
-    } else {
-      ds = useUserStore().user.user.enableDeepseek
-        ? useUserStore().user.user.enableDeepseek + ''
-        : query.ds;
-    }
-    dsHintTxt.value = '';
-    if (ds && ds == '1') {
-      ds = true;
-      dsLoading.value = true;
-    } else {
-      ds = false;
-      dsLoading.value = false;
-    }
-    dsChecked.value = ds;
-    activeTab.value = scope.value;
-    ask(decodeURIComponent(query.q));
-  }
-
-});
-
-defineExpose({ search, changeActiveTab, stopAI });
-</script>
-<style scoped lang="scss">
-@import 'src/assets/scss/variables';
-.ai-search-detail {
-  width: 100%;
-  background: $background_color;
-  height: 100%;
-
-
-  .search-panel {
-    width: 100%;
-    height: calc(100% - 60px - 50px);
-    display: flex;
-    margin-top: 50px;
-    padding-bottom: 50px;
-    .search-detail {
-      width: calc(52%);
-      background: #ffffff;
-      margin-left: 52px;
-
-      &-tdsc {
-        width: calc(52%);
-      }
-
-      .search-result {
-        height: calc(100% - 40px);
-
-
-        .result-panel {
-          width: 100%;
-          background: #fff;
-          padding: 29px 0;
-
-
-          .result {
-            width: 100%;
-
-            .tabs {
-              display: flex;
-              border-bottom: 1px solid #ccc;
-              .ds-box {
-                display: flex;
-                height: 36px;
-                align-items: center;
-                border: 1px solid #E3E4E4;
-                background: #f0f0f0;
-                border-radius: 5px;
-                overflow: hidden;
-                >div {
-                  height: 100%;
-                  display: flex;
-                  align-items: center;
-                  justify-content: center;
-                  width: 108px;
-                  cursor: pointer;
-                  &.active {
-                    background: #fff;
-                    &:nth-child(1) {
-                      .iconfont {
-                        color: #4f6bfe;
-                      }
-                    }
-                    &:nth-child(2) {
-                      .iconfont {
-                        color: #605BEC;
-                      }
-                    }
-                  }
-                  .iconfont {
-                    color: #898D93;
-                    margin-top: 2px;
-                  }
-                  &:nth-child(1) {
-                    .iconfont {
-                      font-size: 16px;
-                    }
-                  }
-                  &:nth-child(2) {
-                    .iconfont {
-                      font-size: 22px;
-                    }
-                  }
-                }
-                .sw {
-                  :deep(.ant-switch) {
-                    background-color: #eaeaea;
-                  }
-                  :deep(.ant-switch-handle::before) {
-                    background-color: #fff;
-                  }
-                }
-                .sw-checked {
-                  :deep(.ant-switch-checked) {
-                    background-color: #fff;
-                  }
-                  :deep(.ant-switch-handle::before) {
-                    background-color: #1890ff;
-                  }
-                }
-              }
-              .tab {
-                font-family: PingFang SC, PingFang SC;
-                font-weight: 400;
-                font-size: 24px;
-                color: #212121;
-                text-align: left;
-                font-style: normal;
-                text-transform: none;
-                margin-right: 28px;
-                display: flex;
-                justify-content: center;
-                flex-direction: column;
-                align-items: center;
-                cursor: pointer;
-
-                .title {
-                  margin-bottom: 9px;
-                }
-
-                .bottom {
-                  height: 0;
-                  margin-top: 10px;
-                }
-
-                &-active,
-                &:hover {
-                  font-family: PingFang SC, PingFang SC;
-                  font-weight: 400;
-                  font-size: 24px;
-                  color: #1586fa;
-                  text-align: left;
-                  font-style: normal;
-                  text-transform: none;
-
-                  .bottom {
-                    width: 100%;
-                    height: 4px;
-                    background: #3987F4;
-                    border-radius: 2px;
-                  }
-                }
-              }
-            }
-
-            .result-view {
-              width: 100%;
-              height: 50%;
-              overflow-y: auto;
-
-              .q-r {
-                padding: 20px;
-                line-height: 30px;
-                background: #f5f8fa;
-                border-radius: 4px 4px 4px 4px;
-                margin: 20px 0;
-                .ds-content-box {
-                  display: flex;
-                  .icon {
-                    width: 26px;
-                    height: 26px;
-                    display: flex;
-                    align-items: center;
-                    justify-content: center;
-                    border-radius: 50%;
-                    background: white;
-                    border: 1px solid #117ff9;
-                    img {
-                      width: 18px;
-                      height: 18px;
-                    }
-                  }
-                  .ds-panel {
-                    margin-top: -5px;
-                    margin-left: 10px;
-                    width: calc(100% - 40px);
-                    .ds-loading {
-                      display: flex;
-                      align-items: center;
-                      .icon-arrow {
-                        padding-left: 5px;
-                        cursor: pointer;
-                      }
-                    }
-                    .rotate {
-                      transform: rotate(180deg);
-                    }
-                  }
-                  .ds-con {
-                    border-left: 1px solid #ccc;
-                    padding-left: 10px;
-                  }
-                }
-              }
-
-              .more-questions {
-                .title {
-                  display: flex;
-                  margin-bottom: 14px;
-
-                  .change-title {
-                    margin-left: 20px;
-                    display: flex;
-                    cursor: pointer;
-                    font-size: 13px;
-                    color: #3c3c3c;
-
-                    .change-icon {
-                      width: 16px;
-                      height: 16px;
-                      margin-right: 4px;
-                      background: url('@/assets/images/ai-search/icon-chang-question.png') no-repeat;
-                      background-size: 100% 100%;
-                    }
-                  }
-                }
-              }
-
-              .source {
-                width: 100%;
-
-                .title {
-                  display: flex;
-                  align-items: center;
-                  font-family: PingFang SC;
-                  font-weight: bold;
-                  font-size: 20px;
-                  color: #333333;
-                  line-height: 30px;
-
-                  .icon {
-                    width: 20px;
-                    height: 20px;
-                    margin-right: 8px;
-                    background: url('@/assets/images/ai-search/source-icon.png') no-repeat;
-                    background-size: 100% 100%;
-                  }
-
-                  .text {
-                    font-family: Alibaba PuHuiTi 3, Alibaba PuHuiTi 30;
-                    font-weight: normal;
-                    //font-size: 16px;
-                    color: #404557;
-                    line-height: 30px;
-                    text-align: left;
-                    font-style: normal;
-                    text-transform: none;
-                    margin-right: 5px;
-                  }
-                }
-
-                .items {
-                  .item {
-                    width: calc(100% - 10px);
-                    min-height: 82px;
-                    background: rgba(255, 255, 255, 0.5);
-                    border-radius: 10px;
-                    display: flex;
-                    flex-direction: column;
-                    align-items: center;
-                    padding: 20px;
-                    color: #888;
-                    display: flex;
-                    line-height: 25px;
-                    margin-bottom: 20px;
-                    margin: 12px 0;
-                    background: #f1f9ff;
-                    border-radius: 8px 8px 8px 8px;
-
-                    .doc {
-                      display: flex;
-                      width: 100%;
-
-                      p {
-                        width: calc(100% - 25px);
-
-                        .doc-link {
-                          cursor: pointer;
-                          color: #3987F4;
-
-                          &:hover {
-                            font-weight: bold;
-                          }
-                        }
-                      }
-
-                      .doc-icon {
-                        width: 20px;
-                        height: 20px;
-                        margin-left: 5px;
-                        cursor: pointer;
-
-                        &-show {
-                          background: url('/images/zcbd/ai-search/icon-doc-show.png') no-repeat;
-                          background-size: 100% 100%;
-                        }
-
-                        &-hide {
-                          background: url('/images/zcbd/ai-search/icon-doc-hide.png') no-repeat;
-                          background-size: 100% 100%;
-                        }
-                      }
-                    }
-
-                    .content {
-                      //white-space: pre-line;
-                      width: 100%;
-                      font-family: Alibaba PuHuiTi 3, Alibaba PuHuiTi 30;
-                      font-weight: normal;
-                      font-size: 14px;
-                      color: #757575;
-                      line-height: 28px;
-                      text-align: left;
-                      font-style: normal;
-                      text-transform: none;
-
-                      &-hide {
-                        display: none;
-                      }
-                    }
-                  }
-
-                  .item-url {
-                    margin: 12px 0;
-                    background: #f1f9ff;
-                    border-radius: 8px 8px 8px 8px;
-
-                    .bottom {
-                      width: 100%;
-
-                      .title-icon {
-                        display: flex;
-                        justify-content: left;
-                        align-items: center;
-                        width: 90%;
-                        float: left;
-
-                        .icon {
-                          width: 23px;
-                          height: 23px;
-                          background: url('/images/zcbd/ai-search/icon-net-url.png') no-repeat;
-                          background-size: 100% 100%;
-                        }
-
-                        .title {
-                          color: #a9a3a3cc;
-                          font-size: 16px;
-                          margin-left: 10px;
-                        }
-                      }
-
-                      .index {
-                        float: right;
-                        width: 20px;
-                        height: 20px;
-                        font-size: 12px;
-                        line-height: 20px;
-                        text-align: center;
-                        margin: 0 5px;
-                        border-radius: 10px;
-                        background: #d0d5dd;
-                        color: #000;
-                      }
-                    }
-                  }
-                }
-              }
-            }
-
-            // 土地市场
-            .map-answer {
-              position: relative;
-              width: 100%;
-              min-height: 600px;
-
-              .left-panel {
-                width: 100%;
-                background: #ffffff;
-                z-index: 1000;
-                //position: relative;
-                left: 12px;
-                top: 15px;
-                border-radius: 10px;
-
-                .title {
-                  width: 100%;
-                  height: 30px;
-                  text-align: left;
-                  line-height: 30px;
-                  font-size: 18px;
-                  font-weight: bolder;
-                }
-
-                .content {
-                  width: 100%;
-                  height: auto;
-
-                  .chart-card {
-                    width: 100%;
-                    height: 330px;
-
-                    .chart-title {
-                      width: 100%;
-                      height: 30px;
-                      font-size: 18px;
-                      font-weight: bolder;
-                      padding: 0 5px;
-                      line-height: 30px;
-                      margin: 10px 0;
-                    }
-
-                    .chart {
-                      width: 100%;
-                      height: 300px;
-                    }
-
-                    //background: #0a84ff;
-                  }
-
-                  .summary-card {
-                    width: 100%;
-                    height: 50%;
-
-                    .summary-title {
-                      width: 100%;
-                      height: 30px;
-                      font-size: 18px;
-                      font-weight: bolder;
-                      padding: 0 5px;
-                      line-height: 30px;
-                      margin: 10px 0;
-                    }
-
-                    .summary-content {
-                      width: calc(100% - 20px);
-                      height: calc(100% - 30px);
-                      padding: 0;
-                      white-space: pre-wrap;
-                      overflow-y: auto;
-                      border-radius: 10px;
-                      margin: 10px;
-                      font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Noto Sans, Helvetica,
-                        Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
-                      font-size: 16px;
-                      line-height: 1.5;
-                    }
-                  }
-                }
-              }
-
-              .map-panel {
-                width: 100%;
-                height: 630px;
-
-                .map-title {
-                  width: 100%;
-                  height: 30px;
-                  font-size: 18px;
-                  font-weight: bolder;
-                  padding: 0 5px;
-                  line-height: 30px;
-                  margin: 10px 0;
-                }
-
-                .map {
-                  width: 100%;
-                  height: 600px;
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  ::-webkit-scrollbar {
-    width: 8px;
-  }
-
-  ::-webkit-scrollbar-thumb {
-    background-color: rgb(0 0 0 / 10%);
-    border-radius: 10px;
-  }
-}
-
-::v-deep .input-modal {
-  .modal-que {
-    background: #0e91fd;
-
-    textarea {
-      width: 100%;
-      height: 257px !important;
-      border: none;
-      border-radius: 10px;
-      resize: none;
-      padding: 10px;
-      background: transparent;
-      font-size: 19px;
-
-      &:focus-visible {
-        border: none !important;
-        outline: none;
-      }
-    }
-  }
-}
-</style>
-

+ 1 - 1
ais_search_zj/web/src/views/zcdb/components/aiAssistant.vue

@@ -136,7 +136,7 @@ const query = async () => {
   timers.value = []
   timers.value = []
   timerIndex.value = 0
   timerIndex.value = 0
   await fetchEventSource(
   await fetchEventSource(
-    (window)?.AppGlobalConfig?.knowledgeServer + '/chat/complete_file_chat',
+    (window)?.AppGlobalConfig?.aiServer + '/chat/complete_file_chat',
     {
     {
       method: 'POST',
       method: 'POST',
       openWhenHidden: true,
       openWhenHidden: true,