|
@@ -1,21 +1,17 @@
|
|
|
<!-- Chat.vue -->
|
|
|
<template>
|
|
|
- <div class="chat-container items-center">
|
|
|
- <div v-if="!messages.length" class="message-list">
|
|
|
+ <div class="chat-container">
|
|
|
+ <div v-if="!messages.length && !msgLoading" class="message-list">
|
|
|
<pageMask />
|
|
|
</div>
|
|
|
<!-- 消息列表 -->
|
|
|
- <div class="message-list w-full" v-else>
|
|
|
- <el-scrollbar ref="scrollbar" @scroll="handleScroll">
|
|
|
+ <div class="message-list" v-else>
|
|
|
+ <el-scrollbar ref="scrollbar" @scroll="handleScroll" v-loading="msgLoading">
|
|
|
<!-- 加载更多指示器 -->
|
|
|
<div v-if="isLoadingMore" class="loading-more-indicator">
|
|
|
<div class="loading-spinner"></div>
|
|
|
<span>加载更多消息...</span>
|
|
|
</div>
|
|
|
- // 添加一个 ref 用于获取消息列表容器
|
|
|
- const messagesContainer = ref(null)
|
|
|
-
|
|
|
- // 修改模板中的消息列表 div,添加 ref
|
|
|
<div class="messages" ref="messagesContainer">
|
|
|
<div v-for="(message, index) in messages" :key="index"
|
|
|
:class="['message-item', message.role === 'user' ? 'self' : 'other']">
|
|
@@ -42,7 +38,7 @@
|
|
|
</span>
|
|
|
</div>
|
|
|
<div class="timestamp ">{{ moment(message.sortKey).format('MM-DD HH:mm') }}
|
|
|
- <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span>
|
|
|
+ <!-- <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -51,16 +47,23 @@
|
|
|
<ScrollToBottom :target="scrollbar" ref="scrollToBottomRef" />
|
|
|
</div>
|
|
|
|
|
|
- <Tools @read-click="readClick" @upload-file="handleUpload" @handle-capture="handleCapture" @his-records="hisRecords"
|
|
|
+ <Tools :upload="type === FunctionList.File_Operation || !!formInfo" @read-click="readClick"
|
|
|
+ @upload-file="(file) => createFileObj(file)" @handle-capture="handleCapture" @his-records="hisRecords"
|
|
|
@add-new-dialogue="addNewDialogue" @handle-current-change="handleCurrentChange"
|
|
|
@handel-intelligent-filling-click="handelIntelligentFillingClick" />
|
|
|
|
|
|
<div class="w-full max-w-[720px] p-[0_12px_12px]">
|
|
|
<!-- 输入区域 -->
|
|
|
- <div class="input-area w-full">
|
|
|
+ <div class="input-area">
|
|
|
+ <el-icon class="closeShow" :style="{ display: isShowPage ? 'block' : 'none' }" size="16px" color="#909399"
|
|
|
+ @click="closePageInfo">
|
|
|
+ <CircleClose />
|
|
|
+ </el-icon>
|
|
|
<div v-show="isShowPage" style="border-bottom: 1px solid #F0F0F0;">
|
|
|
<div class="card_list">
|
|
|
<template v-for="(v, i) in pageInfoList" :key="i">
|
|
|
+
|
|
|
+
|
|
|
<div class="card_image " v-if="v.type === 'image'">
|
|
|
<el-image v-loading="v.isUpload" class="w-full h-full p-0.5 object-cover" :src="v.url" :zoom-rate="1.2"
|
|
|
:max-scale="7" :min-scale="0.2" :preview-src-list="[v.url]" :initial-index="4" fit="cover" />
|
|
@@ -69,10 +72,14 @@
|
|
|
</el-icon>
|
|
|
</div>
|
|
|
<div v-else :class="`card-content ${pageInfoList.length > 1 ? 'card_width' : ''}`">
|
|
|
- <img :src="v?.favIconUrl" style="width: 24px;display: block" />
|
|
|
+ <div class="loading-more-indicator" v-if="v.loading">
|
|
|
+ <div class="loading-spinner"></div>
|
|
|
+ </div>
|
|
|
+ <img v-else :src="v?.favIconUrl" style="width: 24px;display: block" />
|
|
|
+
|
|
|
<div class="title-wrapper">
|
|
|
<span class="els title-scroller">{{ v?.title }}</span>
|
|
|
- <span class="els url-scroller">{{ v?.url }}</span>
|
|
|
+ <span class="els url-scroller">{{ v.loading ? v?.state : v?.url }}</span>
|
|
|
</div>
|
|
|
<el-icon class="closeIcon" size="16px" color="#909399" @click="deletePageInfo(i)">
|
|
|
<CircleClose />
|
|
@@ -80,29 +87,35 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
+ <div v-show="!pageInfoList.length && type === FunctionList.Intelligent_Form_filling">智能填表</div>
|
|
|
</div>
|
|
|
|
|
|
- <div v-show="type !== FunctionList.Intelligent_Form_filling" class="card-btn">
|
|
|
- <el-tooltip content="总结当前页面" placement="top">
|
|
|
- <el-button round @click="handleSummary">总结</el-button>
|
|
|
+ <div class="card-btn">
|
|
|
+ <el-tooltip content="总结" placement="top">
|
|
|
+ <el-button :disabled="disabledBtn" v-if="type !== FunctionList.Intelligent_Form_filling" round
|
|
|
+ @click="handleSummary">总结</el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="抽取表单信息" placement="top">
|
|
|
+ <el-button :disabled="disabledBtn"
|
|
|
+ v-if="type === FunctionList.Intelligent_Form_filling && pageInfoList.length" round
|
|
|
+ @click="handleSummaryFile">抽取</el-button>
|
|
|
</el-tooltip>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<el-input ref="textareaRef" v-model="inputMessage" type="textarea" :rows="3" placeholder="输入消息..."
|
|
|
- @keyup.enter="handleAsk" />
|
|
|
+ @keyup.enter="() => handleAsk()" />
|
|
|
<div class="chat_area_op">
|
|
|
- <el-button
|
|
|
- :style="`background-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'};border-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'}`"
|
|
|
- v-if="inputMessage.trim() || !sendLoading" type="primary" circle @click="handleAsk"
|
|
|
- :disabled="!inputMessage.trim() || sendLoading">
|
|
|
- <svg-icon icon-class="send" color="#000000" />
|
|
|
- </el-button>
|
|
|
- <el-button style="background-color:#ffffff;border:2px solid rgb(134 143 153);" v-else circle
|
|
|
+
|
|
|
+ <el-button v-if="sendLoading" style="background-color:#ffffff;border:2px solid rgb(134 143 153);" circle
|
|
|
@click="handleStopAsk">
|
|
|
<svg-icon icon-class="stop" color="red" />
|
|
|
</el-button>
|
|
|
-
|
|
|
+ <el-button v-else
|
|
|
+ :style="`background-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'};border-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'}`"
|
|
|
+ type="primary" circle @click="() => handleAsk()" :disabled="disSend">
|
|
|
+ <svg-icon icon-class="send" color="#000000" />
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
@@ -120,12 +133,10 @@ import { ElScrollbar, ElAvatar, ElInput, ElButton } from 'element-plus'
|
|
|
import moment from 'moment'
|
|
|
import fileLogo from '@/assets/svg/file.svg'
|
|
|
import {
|
|
|
- buildExcelUnderstandingPrompt,
|
|
|
- getSummaryPrompt,
|
|
|
getFileContent,
|
|
|
buildObjPrompt,
|
|
|
- modelFileUpload,
|
|
|
- controllerList
|
|
|
+ controllerList,
|
|
|
+ formatMessage
|
|
|
} from '@/entrypoints/sidepanel/utils/ai-service.js'
|
|
|
import { storeToRefs } from 'pinia'
|
|
|
import { ElMessage } from 'element-plus'
|
|
@@ -138,15 +149,15 @@ import ScrollToBottom from '@/entrypoints/sidepanel/component/ScrollToBottom.vue
|
|
|
import formTable from '@/entrypoints/sidepanel/component/formTable.vue'
|
|
|
import userAvatar from '@/assets/images/user.png'
|
|
|
import avatar from '@/public/icon/32.png'
|
|
|
-import { mockData, startMsg, mockData2, options, FunctionList } from '@/entrypoints/sidepanel/mock'
|
|
|
+import { options, FunctionList } from '@/entrypoints/sidepanel/mock'
|
|
|
import { useAutoResizeTextarea } from '@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts'
|
|
|
-import { getPageInfo, getXlsxValue, handleInput } from './utils/index.js'
|
|
|
+import { getPageInfo, getPageInfoClean, handleInput, downloadFile } from './utils/index.js'
|
|
|
import { useMsgStore } from '@/store/modules/msg.ts'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
-import { putChat } from "@/api/index.js";
|
|
|
+import { putChat, uploadFile } from "@/api/index.js";
|
|
|
+import { askQues, getFormKey } from '@/api/modal.js'
|
|
|
import { debounce } from 'lodash'
|
|
|
const userStore = useUserStore()
|
|
|
-import { getChatDetail } from '@/api/index.js'
|
|
|
// 在其他状态变量附近添加
|
|
|
const isLoadingMore = ref(false)
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
@@ -157,30 +168,32 @@ const xlsxData = ref({})
|
|
|
const isShowPage = ref(false)
|
|
|
const tareRef = useTemplateRef('textareaRef')
|
|
|
const drawerRef = useTemplateRef('historyComponentRef')
|
|
|
+const disSend = computed(() => {
|
|
|
+ if (pageInfoList.value.length && type.value === FunctionList.Intelligent_Form_filling) return true
|
|
|
+ if (sendLoading.value) return true
|
|
|
+ if (inputMessage.value.trim()) return false
|
|
|
+ return true
|
|
|
+})
|
|
|
// 获取父组件提供的 Hook 实例
|
|
|
const msgStore = useMsgStore()
|
|
|
-const { pageInfoList, messages, msgUuid, AIModel, page, hasNext } = storeToRefs(useMsgStore())
|
|
|
+const { pageInfoList, messages, msgUuid, AIModel, page, hasNext, msgLoading } = storeToRefs(useMsgStore())
|
|
|
const formInfo = ref('')
|
|
|
-
|
|
|
const {
|
|
|
taklToHtml,
|
|
|
sendLoading,
|
|
|
type,
|
|
|
- streamRes,
|
|
|
- getFormKeyAndValue,
|
|
|
- requestFlowFn,
|
|
|
fetchRes
|
|
|
+
|
|
|
} = useMsg(scrollbar)
|
|
|
const inputMessage = ref('')
|
|
|
const pageInfo = ref('')
|
|
|
+const summaryHtml = ref(false)
|
|
|
function handleStopAsk() {
|
|
|
- if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
- inputMessage.value = ''
|
|
|
- pageInfoList.value = []
|
|
|
- isShowPage.value = false
|
|
|
- taklToHtml.value = false
|
|
|
- type.value = FunctionList.File_Operation
|
|
|
- }
|
|
|
+ inputMessage.value = ''
|
|
|
+ pageInfoList.value = []
|
|
|
+ isShowPage.value = false
|
|
|
+ taklToHtml.value = false
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
controllerList.value[0].abort()
|
|
|
controllerList.value = []
|
|
|
sendLoading.value = false
|
|
@@ -230,7 +243,6 @@ const handleScrollToTop = debounce(async function () {
|
|
|
* @param {string} type 发送的类型 document 、text
|
|
|
* **/
|
|
|
async function addMessage(msg, raw, type) {
|
|
|
- console.log(msg);
|
|
|
const newMessage = reactive({
|
|
|
id: +new Date(),
|
|
|
type: type || '',
|
|
@@ -241,7 +253,9 @@ async function addMessage(msg, raw, type) {
|
|
|
sortKey: moment().valueOf(),
|
|
|
role: 'user',
|
|
|
conversationId: msgUuid.value,
|
|
|
- addToHistory: `${!taklToHtml.value}`
|
|
|
+ addToHistory: `${!taklToHtml.value}`,
|
|
|
+ redisKey: undefined,
|
|
|
+ fileId: undefined
|
|
|
})
|
|
|
if (type === 'document') {
|
|
|
newMessage.content = msg
|
|
@@ -249,60 +263,66 @@ async function addMessage(msg, raw, type) {
|
|
|
}
|
|
|
if (!msg) return
|
|
|
messages.value.push(newMessage)
|
|
|
- await nextTick(() => {
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
})
|
|
|
return newMessage
|
|
|
}
|
|
|
|
|
|
const handleSummary = async () => {
|
|
|
- let params = []
|
|
|
- if (AIModel.value.file === true) {
|
|
|
- pageInfoList.value.forEach((pageInfo) => {
|
|
|
- params.push({ role: 'system', content: `fileid://${pageInfo.fileId}` })
|
|
|
- })
|
|
|
- params.push({
|
|
|
- role: 'user', content: `
|
|
|
- 请根据这几个文件的文本内容综合分析总结
|
|
|
- 要求:
|
|
|
- 1.抽取文件的文本内容
|
|
|
- 2.对每个文件中的文本内容进行分别总结
|
|
|
- 3.对步骤二总结的内容进行综合总结`
|
|
|
- })
|
|
|
- } else {
|
|
|
- let tempStr = ''
|
|
|
- pageInfoList.value.forEach((pageInfo, index) => {
|
|
|
- pageInfo.handleText = getSummaryPrompt(pageInfo.content)
|
|
|
- tempStr += `第${index + 1}段:${pageInfo.handleText}\n`
|
|
|
- })
|
|
|
- params = [{
|
|
|
- role: 'user',
|
|
|
- content: `请根据以下这几个内容综合总结出结果:
|
|
|
- ${tempStr}要求:
|
|
|
- 1. 用简洁清晰的语言提取所有的核心要点
|
|
|
- 2. 保持客观中立的语气
|
|
|
- 3. 按重要性排序
|
|
|
- 4. 返回内容做好换行,以及展示样式
|
|
|
- 5. 请以"以下是对该文件内容的总结:"开头,然后用要点的形式列出主要内容。
|
|
|
- 6. 对这几个内容进行综合分析及联想`
|
|
|
- }]
|
|
|
- }
|
|
|
- const msg = await addMessage(JSON.stringify(pageInfoList.value), JSON.stringify(params.map(_ => _.content)), 'document')
|
|
|
- putChat(msg)
|
|
|
- if (requestFlowFn) {
|
|
|
- isShowPage.value = false
|
|
|
- taklToHtml.value = false
|
|
|
+ if (sendLoading.value) return
|
|
|
+ handleAsk(`请帮我总结当前${summaryHtml.value ? '页面' : '文件'}`)
|
|
|
+ summaryHtml.value = false
|
|
|
+}
|
|
|
+const handleSummaryFile = async () => {
|
|
|
+ if (sendLoading.value) return
|
|
|
+ try {
|
|
|
+ sendLoading.value = true
|
|
|
+ const userMsg = await addMessage(JSON.stringify([...pageInfoList.value, { type: 'text', value: '抽取表单' }]), '抽取表单', 'document')
|
|
|
+ userMsg.fileId = pageInfoList.value[0].fileId
|
|
|
+ userMsg.redisKey = pageInfoList.value[0].redisKey
|
|
|
+ await putChat(userMsg)
|
|
|
+ createWS(userMsg)
|
|
|
+ formInfo.value = ''
|
|
|
+ // const msg = reactive({
|
|
|
+ // type: '',
|
|
|
+ // rawContent: '',
|
|
|
+ // senderId: -1,
|
|
|
+ // receiverId: userStore.userInfo.id, //大模型
|
|
|
+ // content: '',
|
|
|
+ // sortKey: moment().valueOf(),
|
|
|
+ // role: 'system',
|
|
|
+ // conversationId: msgUuid.value,
|
|
|
+ // })
|
|
|
+ // messages.value.push(msg)
|
|
|
+ // isShowPage.value = false
|
|
|
pageInfoList.value = []
|
|
|
- const res = await requestFlowFn(params)
|
|
|
+ // const resForm = await askQues({
|
|
|
+ // conversationId: msgUuid.value,
|
|
|
+ // modelName: '通义千问-Max',
|
|
|
+ // question: buildObjPrompt(xlsxData.value,formInfo.value),
|
|
|
+ // id: '699637194561691650',
|
|
|
+ // // redisKey:msg.redisKey
|
|
|
+ // })
|
|
|
+ // return console.log(resForm);
|
|
|
+
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+
|
|
|
+ } finally {
|
|
|
+ // sendLoading.value = false
|
|
|
+ // type.value = FunctionList.File_Operation
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
async function handelIntelligentFillingClick() {
|
|
|
isShowPage.value = true
|
|
|
- taklToHtml.value = true
|
|
|
- const tempPageInfo = await getPageInfo()
|
|
|
- pageInfo.value = tempPageInfo
|
|
|
- pageInfoList.value = [tempPageInfo]
|
|
|
+ // taklToHtml.value = true
|
|
|
+ // const tempPageInfo = await getPageInfo()
|
|
|
+ // pageInfo.value = tempPageInfo
|
|
|
+ // pageInfoList.value = [tempPageInfo]
|
|
|
inputMessage.value = '/智能填表 '
|
|
|
type.value = FunctionList.Intelligent_Form_filling
|
|
|
}
|
|
@@ -324,6 +344,7 @@ function handleCurrentChange(e) {
|
|
|
|
|
|
async function handleCurrentData(e) {
|
|
|
drawerRef.value.drawer = false
|
|
|
+ isShowPage.value = false
|
|
|
if (!e) {
|
|
|
addNewDialogue()
|
|
|
return
|
|
@@ -333,14 +354,29 @@ async function handleCurrentData(e) {
|
|
|
msgUuid.value = e
|
|
|
chrome.storage.local.set({ msgUuid: msgUuid.value })
|
|
|
await msgStore.initMsg()
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
+let SystemMsg = null
|
|
|
|
|
|
+watchEffect(() => {
|
|
|
+ if (!isShowPage.value) {
|
|
|
+ summaryHtml.value = false
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ SystemMsg = null
|
|
|
+ inputMessage.value = ''
|
|
|
+ }
|
|
|
+})
|
|
|
+const htmlIcon = ref('')
|
|
|
async function readClick() {
|
|
|
if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
pageInfoList.value = []
|
|
|
type.value = FunctionList.File_Operation
|
|
|
}
|
|
|
+ summaryHtml.value = true
|
|
|
isShowPage.value = true
|
|
|
taklToHtml.value = true
|
|
|
if (pageInfoList.value.length >= Number(import.meta.env.VITE_MAX_FILE_NUMBER)) {
|
|
@@ -348,14 +384,20 @@ async function readClick() {
|
|
|
return
|
|
|
}
|
|
|
const tempPageInfo = await getPageInfo()
|
|
|
- if (AIModel.value.file === true) {
|
|
|
- const blob = new Blob([tempPageInfo.content.mainContent], { type: 'text/plain' })
|
|
|
- const file = new File([blob], tempPageInfo.title + '.txt', { type: 'text/plain' })
|
|
|
- tempPageInfo.fileId = await modelFileUpload(file)
|
|
|
- }
|
|
|
- pageInfoList.value.unshift(tempPageInfo)
|
|
|
+ htmlIcon.value = tempPageInfo.favIconUrl
|
|
|
+ const blob = new Blob([tempPageInfo.content.mainContent], { type: 'text/plain' })
|
|
|
+ const file = new File([blob], '页面' + '.txt', { type: 'text/plain' })
|
|
|
+ // console.log(file);
|
|
|
+ // 下载文件到本地
|
|
|
+ // downloadFile(file);
|
|
|
+ console.log(tempPageInfo);
|
|
|
+
|
|
|
+ await createFileObj(file)
|
|
|
+ // await handleUpload(file,tempPageInfo.title + '.txt')
|
|
|
}
|
|
|
|
|
|
+// 添加下载文件的函数
|
|
|
+
|
|
|
function deletePageInfo(i) {
|
|
|
pageInfoList.value.splice(i, 1)
|
|
|
if (pageInfoList.value.length === 0) {
|
|
@@ -371,35 +413,157 @@ function addNewDialogue() {
|
|
|
ElMessage.warning('已经是新对话')
|
|
|
return
|
|
|
}
|
|
|
+ summaryHtml.value = false
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
isShowPage.value = false
|
|
|
taklToHtml.value = false
|
|
|
messages.value = []
|
|
|
page.value = 1
|
|
|
pageInfoList.value = []
|
|
|
msgUuid.value = uuidv4()
|
|
|
+ hasNext.value = false
|
|
|
chrome.storage.local.set({ msgUuid: msgUuid.value })
|
|
|
}
|
|
|
-
|
|
|
-async function handleAsk() {
|
|
|
- const str = inputMessage.value.trim()
|
|
|
- inputMessage.value = ''
|
|
|
+const disabledBtn = computed(() => {
|
|
|
+ return !!(pageInfoList.value.find(_ => _.loading))
|
|
|
+})
|
|
|
+function closePageInfo() {
|
|
|
+ pageInfoList.value = []
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ isShowPage.value = false
|
|
|
+ formInfo.value = null
|
|
|
+}
|
|
|
+async function handleAsk(value) {
|
|
|
if (sendLoading.value) return
|
|
|
- const msg = await addMessage(str)
|
|
|
- putChat(msg)
|
|
|
+ const str = value ?? inputMessage.value.trim()
|
|
|
+ inputMessage.value = ''
|
|
|
if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
- const res = await fetchRes(str)
|
|
|
+ const msg = await addMessage(str)
|
|
|
+ await putChat(msg)
|
|
|
+ const [res, obj] = await fetchRes(str)
|
|
|
if (res.status === 'ok') {
|
|
|
+ console.log(obj);
|
|
|
+
|
|
|
formInfo.value = res.data
|
|
|
- console.log(res, 55558)
|
|
|
+ SystemMsg = obj
|
|
|
} else {
|
|
|
type.value = FunctionList.File_Operation
|
|
|
- isShowPage.value = false
|
|
|
- taklToHtml.value = false
|
|
|
- pageInfoList.value = []
|
|
|
}
|
|
|
- } else streamRes(taklToHtml.value)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (sendLoading.value) return
|
|
|
+ let msg = null
|
|
|
+ if (pageInfoList.value.length) {
|
|
|
+ msg = await addMessage(JSON.stringify([...pageInfoList.value, { type: 'text', value: str }]), str, 'document')
|
|
|
+ msg.fileId = pageInfoList.value[0].fileId
|
|
|
+ msg.redisKey = pageInfoList.value[0].redisKey
|
|
|
+ } else msg = await addMessage(str)
|
|
|
+ pageInfoList.value = []
|
|
|
+ isShowPage.value = false
|
|
|
+ await putChat(msg)
|
|
|
+ createWS(msg)
|
|
|
}
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @param msg 用户消息对象,调用askQues时需要
|
|
|
+ */
|
|
|
+async function createWS(msg) {
|
|
|
+ sendLoading.value = true
|
|
|
+ const obj = reactive({
|
|
|
+ type: type.value === FunctionList.Intelligent_Form_filling ? 'form' : '',
|
|
|
+ rawContent: '',
|
|
|
+ senderId: -1,
|
|
|
+ receiverId: userStore.userInfo.id, //大模型
|
|
|
+ content: '',
|
|
|
+ sortKey: moment().valueOf(),
|
|
|
+ role: 'system',
|
|
|
+ conversationId: msgUuid.value,
|
|
|
+ })
|
|
|
+ try {
|
|
|
+ messages.value.push(obj)
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const wsUrl = `${import.meta.env.VITE_API_WS_URL}/webSocket/clue/${msgUuid.value}`;
|
|
|
+ const socket = new WebSocket(wsUrl);
|
|
|
+ askQues({
|
|
|
+ conversationId: msgUuid.value,
|
|
|
+ modelName: '通义千问-Max',
|
|
|
+ question: type.value === FunctionList.File_Operation ? msg.rawContent : buildObjPrompt(xlsxData.value, formInfo.value),
|
|
|
+ id: '699637194561691650',
|
|
|
+ redisKey: msg.redisKey
|
|
|
+ }).catch(res => {
|
|
|
+ obj.rawContent = '接口出错,请重试。'
|
|
|
+ obj.content = '接口出错,请重试。'
|
|
|
+ socket.close()
|
|
|
+ })
|
|
|
+ socket.onmessage = (event) => {
|
|
|
+ try {
|
|
|
+ if (event.data === '') {
|
|
|
+ console.log(23223);
|
|
|
|
|
|
+ socket.close()
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ const formResult = obj.rawContent
|
|
|
+ let form = null
|
|
|
+ if (formResult.includes('json')) form = JSON.parse(formResult.split('json')[1].split('```')[0])
|
|
|
+ else form = JSON.parse(formResult)
|
|
|
+ sendLoading.value = false
|
|
|
+ console.log(xlsxData.value, form);
|
|
|
+
|
|
|
+ handleInput(xlsxData.value, form)
|
|
|
+ }
|
|
|
+ isShowPage.value = false
|
|
|
+ } else {
|
|
|
+ obj.rawContent += event.data;
|
|
|
+ obj.content = obj.type === 'form' ? obj.rawContent : formatMessage(obj.rawContent);
|
|
|
+ // 滚动到底部
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('解析消息出错:', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ socket.onerror = (error) => {
|
|
|
+ console.error('WebSocket 错误:', error);
|
|
|
+ socket.close();
|
|
|
+ };
|
|
|
+ socket.onclose = () => {
|
|
|
+ console.log('WebSocket 连接已关闭');
|
|
|
+ sendLoading.value = false;
|
|
|
+ putChat({
|
|
|
+ ...obj,
|
|
|
+ content: ''
|
|
|
+ });
|
|
|
+ sendLoading.value = false
|
|
|
+ controllerList.value = []
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ };
|
|
|
+ // 添加到控制器列表,以便可以在需要时中断连接
|
|
|
+ controllerList.value.push({
|
|
|
+ abort: () => {
|
|
|
+ socket.close();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ sendLoading.value = false;
|
|
|
+ // 显示错误消息
|
|
|
+ obj.content = '连接失败,请重试';
|
|
|
+ putChat({
|
|
|
+ ...obj,
|
|
|
+ content: ''
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
function handleCapture() {
|
|
|
chrome.runtime.sendMessage({
|
|
|
type: 'SCREENSHOT',
|
|
@@ -417,87 +581,103 @@ function handleCapture() {
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
function hisRecords() {
|
|
|
drawerRef.value.drawer = true
|
|
|
}
|
|
|
+const createFileObj = async (file) => {
|
|
|
+ handleUpload(file)
|
|
|
+}
|
|
|
const handleUpload = async (file) => {
|
|
|
- if (AIModel.value.file === true) {
|
|
|
- const id = await modelFileUpload(file)
|
|
|
- pageInfoList.value.unshift({
|
|
|
- fileId: id,
|
|
|
- title: file.name,
|
|
|
- url: 'File',
|
|
|
- favIconUrl: fileLogo
|
|
|
- })
|
|
|
- isShowPage.value = true
|
|
|
- taklToHtml.value = true
|
|
|
- return
|
|
|
- }
|
|
|
- if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
- const fileExtension = file.name.split('.').pop().toLowerCase()
|
|
|
- if (fileExtension === 'xlsx') {
|
|
|
- const readData = await getXlsxValue(file)
|
|
|
- readData[0].forEach((header, i) => {
|
|
|
- // if (!xlsxData.value[header]) xlsxData.value[header] = []
|
|
|
- xlsxData.value[header] = readData[1][i]
|
|
|
- })
|
|
|
- const msg = addMessage(`已上传文件:${file.name}`, buildExcelUnderstandingPrompt(readData[0], file?.name, formInfo.value))
|
|
|
- putChat(msg)
|
|
|
- const { rawContent, status } = await streamRes()
|
|
|
- if (status === 'ok') {
|
|
|
- let form = []
|
|
|
- if (rawContent.includes('json')) form = JSON.parse(rawContent.split('json')[1].split('```')[0])
|
|
|
- else form = JSON.parse(rawContent)
|
|
|
- handleInput(xlsxData.value, form)
|
|
|
- }
|
|
|
- } else {
|
|
|
- const msg = await addMessage(`文件上传中`)
|
|
|
- try {
|
|
|
- sendLoading.value = true
|
|
|
- putChat(msg)
|
|
|
- const data = await getFileValue(file)
|
|
|
- msg.content = `已上传文件:${file.name}`
|
|
|
- msg.rawContent = data
|
|
|
- const res2 = await getFormKeyAndValue(data, formInfo.value)
|
|
|
- xlsxData.value = res2.data
|
|
|
- msg.rawContent = buildObjPrompt(res2.data, formInfo.value)
|
|
|
- const { rawContent, status } = await streamRes()
|
|
|
- if (status === 'ok') {
|
|
|
- let form = []
|
|
|
- if (rawContent.includes('json')) form = JSON.parse(rawContent.split('json')[1].split('```')[0])
|
|
|
- else form = JSON.parse(rawContent)
|
|
|
- handleInput(xlsxData.value, form)
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- msg.content = '文件解析出错'
|
|
|
- } finally {
|
|
|
- sendLoading.value = false
|
|
|
- // putChat({
|
|
|
- // id: msgUuid.value,
|
|
|
- // msg: msg
|
|
|
- // })
|
|
|
- }
|
|
|
+ const obj = reactive({
|
|
|
+ title: file.name,
|
|
|
+ state: '上传中...',
|
|
|
+ favIconUrl: fileLogo,
|
|
|
+ loading: true,
|
|
|
+ url: '',
|
|
|
+ fileId: '',
|
|
|
+ redisKey: '',
|
|
|
+ content: {
|
|
|
+ mainContent: ''
|
|
|
}
|
|
|
- type.value = FunctionList.File_Operation
|
|
|
- isShowPage.value = false
|
|
|
- taklToHtml.value = false
|
|
|
+ })
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
pageInfoList.value = []
|
|
|
- return
|
|
|
}
|
|
|
- if (type.value === FunctionList.File_Operation) {
|
|
|
+ if (summaryHtml.value) obj.favIconUrl = htmlIcon.value
|
|
|
+ isShowPage.value = true
|
|
|
+ // const obj = reactive({
|
|
|
+ // title: file.name,
|
|
|
+ // state: '上传中...',
|
|
|
+ // favIconUrl: fileLogo,
|
|
|
+ // loading: true,
|
|
|
+ // url:'',
|
|
|
+ // fileId: '',
|
|
|
+ // redisKey:'',
|
|
|
+ // content: {
|
|
|
+ // mainContent: ''
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ pageInfoList.value = [obj]
|
|
|
+ try {
|
|
|
let formData = new FormData()
|
|
|
- formData.append('file', file)
|
|
|
- const res = await getFileContent(formData)
|
|
|
- pageInfoList.value.unshift({
|
|
|
- title: file.name,
|
|
|
- url: 'File',
|
|
|
- favIconUrl: fileLogo,
|
|
|
- content: {
|
|
|
- mainContent: res.data
|
|
|
+ formData.append('avatarFile', file)
|
|
|
+ const res = await uploadFile(formData)
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ obj.state = '解析中...'
|
|
|
+ obj.redisKey = res.data.redisKey
|
|
|
+ obj.id = res.data.file.id
|
|
|
+ obj.url = res.data.file.url
|
|
|
+ // const fileExtension = file.name.split('.').pop().toLowerCase()
|
|
|
+ const result = await getFormKey({
|
|
|
+ body: formInfo.value,
|
|
|
+ input_data: res.data.data
|
|
|
+ })
|
|
|
+ xlsxData.value = JSON.parse(result.data)
|
|
|
+ // if (fileExtension === 'xlsx') {
|
|
|
+ // const readData = await getXlsxValue(file)
|
|
|
+ // readData[0].forEach((header, i) => {
|
|
|
+ // // if (!xlsxData.value[header]) xlsxData.value[header] = []
|
|
|
+ // xlsxData.value[header] = readData[1][i]
|
|
|
+ // })
|
|
|
+ // } else {
|
|
|
+ // const result = await getFormKey({
|
|
|
+ // body: formInfo.value,
|
|
|
+ // input_data:res.data.data
|
|
|
+ // })
|
|
|
+ // xlsxData.value = JSON.parse(result.data)
|
|
|
+ // }
|
|
|
+ }
|
|
|
+
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
}
|
|
|
})
|
|
|
- isShowPage.value = true
|
|
|
+ if (type.value === FunctionList.File_Operation) {
|
|
|
+ obj.loading = false
|
|
|
+ obj.url = res.data.url
|
|
|
+ obj.redisKey = res.data.redisKey
|
|
|
+ obj.fileId = res.data.file.id
|
|
|
+ obj.url = res.data.file.url
|
|
|
+ }
|
|
|
+ obj.loading = false
|
|
|
+ console.log(SystemMsg);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ if (SystemMsg) {
|
|
|
+ SystemMsg.content = '数据上传出错,请重试'
|
|
|
+ SystemMsg.rawContent = '数据上传出错,请重试'
|
|
|
+ }
|
|
|
+ console.log(error);
|
|
|
+ ElMessage.error('上传出错')
|
|
|
+ pageInfoList.value = []
|
|
|
+ isShowPage.value = false
|
|
|
+ } finally {
|
|
|
+ SystemMsg && putChat({
|
|
|
+ ...SystemMsg,
|
|
|
+ content: '',
|
|
|
+ })
|
|
|
+ SystemMsg = null
|
|
|
}
|
|
|
}
|
|
|
async function getFileValue(file) {
|
|
@@ -526,13 +706,16 @@ onMounted(async () => {
|
|
|
}
|
|
|
})
|
|
|
nextTick(() => {
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
})
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@use '@/entrypoints/sidepanel/css/chat.scss';
|
|
|
+@use '@/entrypoints/sidepanel/css/markdown.scss';
|
|
|
|
|
|
.loading-more-indicator {
|
|
|
display: flex;
|