|
@@ -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>
|