|
@@ -5,42 +5,49 @@
|
|
|
<pageMask />
|
|
|
</div>
|
|
|
<!-- 消息列表 -->
|
|
|
- <el-scrollbar v-else class="message-list" ref="scrollbar">
|
|
|
- <div class="messages">
|
|
|
- <div v-for="(message, index) in messages" :key="index"
|
|
|
- :class="['message-item', message.isSelf ? 'self' : 'other']">
|
|
|
- <el-avatar :size="32" :src="message.avatar" />
|
|
|
- <div class="message-content">
|
|
|
- <div class="content" v-if="!message.isSelf" :class="{ 'loading-content': message.content === '' }">
|
|
|
- <formTable v-if="message.type === 'form'" :content="message.rawContent" />
|
|
|
- <span v-else v-html="message.content"></span>
|
|
|
- <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
|
|
|
- <span class="dot"></span>
|
|
|
- <span class="dot"></span>
|
|
|
- <span class="dot"></span>
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- <document v-else-if="message.type === 'document' && message.isSelf" :content="message.content"
|
|
|
- :rawContent="message.rawContent" />
|
|
|
- <div v-else class="content">
|
|
|
- <span v-if="message.type === ''">{{ message.content }}</span>
|
|
|
- <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
|
|
|
+ <div class="message-list" v-else>
|
|
|
+ <el-scrollbar ref="scrollbar" @scroll="handleScroll">
|
|
|
+ <div class="messages">
|
|
|
+ <div v-for="(message, index) in messages" :key="index"
|
|
|
+ :class="['message-item', message.isSelf ? 'self' : 'other']">
|
|
|
+ <el-avatar :size="32" :src="message.avatar" />
|
|
|
+ <div class="message-content">
|
|
|
+ <div class="content" v-if="!message.isSelf" :class="{ 'loading-content': message.content === '' }">
|
|
|
+ <formTable v-if="message.type === 'form'" :content="message.rawContent" />
|
|
|
+ <span v-else v-html="message.content"></span>
|
|
|
+ <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
|
|
|
+ <span class="dot"></span>
|
|
|
+ <span class="dot"></span>
|
|
|
+ <span class="dot"></span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <document v-else-if="message.type === 'document' && message.isSelf" :content="message.content"
|
|
|
+ :rawContent="message.rawContent" />
|
|
|
+ <div v-else class="content">
|
|
|
+ <span v-if="message.type === ''">{{ message.content }}</span>
|
|
|
+ <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
|
|
|
<span class="dot"></span>
|
|
|
<span class="dot"></span>
|
|
|
<span class="dot"></span>
|
|
|
</span>
|
|
|
- </div>
|
|
|
- <div class="timestamp ">{{ moment(message.timestamp).format('MM-DD HH:mm') }}
|
|
|
- <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span>
|
|
|
+ </div>
|
|
|
+ <div class="timestamp ">{{ moment(message.timestamp).format('MM-DD HH:mm') }}
|
|
|
+ <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </el-scrollbar>
|
|
|
+ </el-scrollbar>
|
|
|
+ <ScrollToBottom :target="scrollbar" ref="scrollToBottomRef" />
|
|
|
+ </div>
|
|
|
|
|
|
- <Tools @read-click="readClick" @upload-file="handleUpload" @handle-capture="handleCapture" @his-records="hisRecords"
|
|
|
- @add-new-dialogue="addNewDialogue" @handle-current-change="handleCurrentChange"
|
|
|
- @handel-intelligent-filling-click="handelIntelligentFillingClick" />
|
|
|
+ <Tools @read-click="readClick"
|
|
|
+ @upload-file="handleUpload"
|
|
|
+ @handle-capture="handleCapture"
|
|
|
+ @his-records="hisRecords"
|
|
|
+ @add-new-dialogue="addNewDialogue"
|
|
|
+ @handle-current-change="handleCurrentChange"
|
|
|
+ @handel-intelligent-filling-click="handelIntelligentFillingClick" />
|
|
|
|
|
|
<div>
|
|
|
<!-- 输入区域 -->
|
|
@@ -48,7 +55,7 @@
|
|
|
<div v-show="isShowPage" style="border-bottom: 1px solid #F0F0F0;">
|
|
|
<div class="card_list">
|
|
|
<div v-for="(v,i) in pageInfoList" :key="i"
|
|
|
- :class="`card-content ${pageInfoList.length > 1 ? 'card_width' : ''}`">
|
|
|
+ :class="`card-content ${pageInfoList.length > 1 ? 'card_width' : ''}`">
|
|
|
<img :src="v?.favIconUrl" style="width: 24px;display: block" />
|
|
|
<div class="title-wrapper">
|
|
|
<span class="els title-scroller">{{ v?.title }}</span>
|
|
@@ -60,27 +67,27 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="card-btn">
|
|
|
+ <div v-show="type !== FunctionList.Intelligent_Form_filling" class="card-btn">
|
|
|
<el-tooltip content="总结当前页面" placement="top">
|
|
|
- <el-button round @click="handleSummary" :disabled="!taklToHtml">总结</el-button>
|
|
|
+ <el-button round @click="handleSummary">总结</el-button>
|
|
|
</el-tooltip>
|
|
|
-
|
|
|
- <!-- <el-tooltip content="选择后,在输入框描述填表流程" placement="top">-->
|
|
|
- <!-- <el-button :class="type === '2' ? 'buttom-clicked' : ''" round @click="handelIntelligentFillingClick"-->
|
|
|
- <!-- :disabled="!taklToHtml">智能填表-->
|
|
|
- <!-- </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 type="primary" link @click="handleAsk" :disabled="!inputMessage.trim() || sendLoading">
|
|
|
- <el-icon size="18" :color="inputMessage.trim() ? 'black' : 'gray'">
|
|
|
- <Promotion />
|
|
|
- </el-icon>
|
|
|
+ <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
|
|
|
+ @click="handleStopAsk">
|
|
|
+ <svg-icon icon-class="stop" color="red" />
|
|
|
</el-button>
|
|
|
+
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
@@ -96,13 +103,15 @@
|
|
|
import { ref, onMounted, nextTick, inject, useTemplateRef, reactive } from 'vue'
|
|
|
import { ElScrollbar, ElAvatar, ElInput, ElButton } from 'element-plus'
|
|
|
import moment from 'moment'
|
|
|
+import { cloneDeep } from 'lodash'
|
|
|
import fileLogo from '@/assets/svg/file.svg'
|
|
|
import {
|
|
|
buildExcelUnderstandingPrompt,
|
|
|
getSummaryPrompt,
|
|
|
getFileContent,
|
|
|
buildObjPrompt,
|
|
|
- modelFileUpload
|
|
|
+ modelFileUpload,
|
|
|
+ controllerList
|
|
|
} from '@/entrypoints/sidepanel/utils/ai-service.js'
|
|
|
import { storeToRefs } from 'pinia'
|
|
|
import { ElMessage } from 'element-plus'
|
|
@@ -111,6 +120,7 @@ import Tools from '@/entrypoints/sidepanel/component/tools.vue'
|
|
|
import historyComponent from '@/entrypoints/sidepanel/component/historyComponent.vue'
|
|
|
import document from '@/entrypoints/sidepanel/component/document.vue'
|
|
|
import pageMask from '@/entrypoints/sidepanel/component/pageMask.vue'
|
|
|
+import ScrollToBottom from '@/entrypoints/sidepanel/component/ScrollToBottom.vue'
|
|
|
import formTable from '@/entrypoints/sidepanel/component/formTable.vue'
|
|
|
|
|
|
import { mockData, startMsg, mockData2, options, FunctionList } from '@/entrypoints/sidepanel/mock'
|
|
@@ -120,6 +130,7 @@ import { useMsgStore } from '@/store/modules/msg.ts'
|
|
|
|
|
|
// 滚动条引用
|
|
|
const scrollbar = ref(null)
|
|
|
+const scrollToBottomRef = ref(null)
|
|
|
const xlsxData = ref({})
|
|
|
const isShowPage = ref(false)
|
|
|
const tareRef = useTemplateRef('textareaRef')
|
|
@@ -142,12 +153,41 @@ const {
|
|
|
const inputMessage = ref('')
|
|
|
const pageInfo = ref('')
|
|
|
|
|
|
+
|
|
|
+function handleStopAsk() {
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ inputMessage.value = ''
|
|
|
+ pageInfoList.value = []
|
|
|
+ isShowPage.value = false
|
|
|
+ taklToHtml.value = false
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ }
|
|
|
+ controllerList.value[0].abort()
|
|
|
+ controllerList.value = []
|
|
|
+ sendLoading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+function handleScroll({ scrollTop }) {
|
|
|
+ // scrollTop 滚动条的位置
|
|
|
+ const { wrapRef } = scrollbar.value
|
|
|
+ const { scrollHeight, clientHeight } = wrapRef
|
|
|
+ scrollToBottomRef.value.showButton = scrollHeight - scrollTop - clientHeight >= 350
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* @param {string} msg
|
|
|
* @param {string} raw
|
|
|
* @param {string} type 发送的类型 document 、text
|
|
|
* **/
|
|
|
-const addMessage = (msg, raw, type) => {
|
|
|
+async function addMessage(msg, raw, type) {
|
|
|
+ // 添加indexDB Store配置
|
|
|
+ if (msgUuid.value === '') {
|
|
|
+ msgUuid.value = 'D' + Date.now().toString()
|
|
|
+ await registerStore({
|
|
|
+ name: msgUuid.value,
|
|
|
+ keyPath: 'id'
|
|
|
+ })
|
|
|
+ }
|
|
|
const newMessage = reactive({
|
|
|
id: messages.value.length + 1,
|
|
|
type: type || '',
|
|
@@ -160,14 +200,14 @@ const addMessage = (msg, raw, type) => {
|
|
|
addToHistory: !taklToHtml.value
|
|
|
})
|
|
|
if (type === 'document') {
|
|
|
- newMessage.content = JSON.stringify(msg)
|
|
|
+ newMessage.content = msg
|
|
|
newMessage.rawContent = raw
|
|
|
}
|
|
|
if (!msg) return
|
|
|
|
|
|
messages.value.push(newMessage)
|
|
|
- useStore(msgUuid.value).add({ ...newMessage })
|
|
|
- nextTick(() => {
|
|
|
+ useStore(msgUuid.value).add(cloneDeep(newMessage))
|
|
|
+ await nextTick(() => {
|
|
|
scrollbar.value?.setScrollTop(99999)
|
|
|
})
|
|
|
return newMessage
|
|
@@ -181,11 +221,11 @@ const handleSummary = async () => {
|
|
|
})
|
|
|
params.push({
|
|
|
role: 'user', content: `
|
|
|
- 请根据这几个文件综合分析总结
|
|
|
+ 请根据这几个文件的文本内容综合分析总结
|
|
|
要求:
|
|
|
- 1.抽取各文件的内容
|
|
|
- 2.每个文件的内容进行总结
|
|
|
- 3.所有文件进行综合的总结`
|
|
|
+ 1.抽取文件的文本内容
|
|
|
+ 2.对每个文件中的文本内容进行分别总结
|
|
|
+ 3.对步骤二总结的内容进行综合总结`
|
|
|
})
|
|
|
} else {
|
|
|
let tempStr = ''
|
|
@@ -205,12 +245,12 @@ const handleSummary = async () => {
|
|
|
6. 对这几段内容进行综合分析及联想`
|
|
|
}]
|
|
|
}
|
|
|
- addMessage(pageInfoList.value, '总结', 'document')
|
|
|
+ await addMessage(pageInfoList.value, '总结', 'document')
|
|
|
if (requestFlowFn) {
|
|
|
- await requestFlowFn(params)
|
|
|
- taklToHtml.value = true
|
|
|
- isShowPage.value = true
|
|
|
+ isShowPage.value = false
|
|
|
+ taklToHtml.value = false
|
|
|
pageInfoList.value = []
|
|
|
+ const res = await requestFlowFn(params)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -232,6 +272,11 @@ function handleCurrentChange(e) {
|
|
|
}
|
|
|
})
|
|
|
})
|
|
|
+ if (AIModel.value.file === true) {
|
|
|
+ isShowPage.value = false
|
|
|
+ taklToHtml.value = false
|
|
|
+ pageInfoList.value = []
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function handleCurrentData(e) {
|
|
@@ -251,7 +296,10 @@ function handleCurrentData(e) {
|
|
|
}
|
|
|
|
|
|
async function readClick() {
|
|
|
- type.value = FunctionList.File_Operation
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ pageInfoList.value = []
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ }
|
|
|
isShowPage.value = true
|
|
|
taklToHtml.value = true
|
|
|
if (pageInfoList.value.length >= Number(import.meta.env.VITE_MAX_FILE_NUMBER)) {
|
|
@@ -273,6 +321,7 @@ function deletePageInfo(i) {
|
|
|
isShowPage.value = false
|
|
|
taklToHtml.value = false
|
|
|
if (type.value === FunctionList.Intelligent_Form_filling) inputMessage.value = ''
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -281,7 +330,10 @@ function addNewDialogue() {
|
|
|
ElMessage.warning('已经是新对话')
|
|
|
return
|
|
|
}
|
|
|
+ isShowPage.value = false
|
|
|
+ taklToHtml.value = false
|
|
|
messages.value = []
|
|
|
+ pageInfoList.value = []
|
|
|
msgUuid.value = ''
|
|
|
}
|
|
|
|
|
@@ -289,19 +341,11 @@ async function handleAsk() {
|
|
|
const str = inputMessage.value.trim()
|
|
|
inputMessage.value = ''
|
|
|
if (sendLoading.value) return
|
|
|
- // 添加indexDB Store配置
|
|
|
- if (msgUuid.value === '') {
|
|
|
- msgUuid.value = 'D' + Date.now().toString()
|
|
|
- await registerStore({
|
|
|
- name: msgUuid.value,
|
|
|
- keyPath: 'id'
|
|
|
- })
|
|
|
- }
|
|
|
- addMessage(str)
|
|
|
+ addMessage(inputMessage.value.trim())
|
|
|
if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
const res = await fetchRes(str)
|
|
|
console.log(res);
|
|
|
-
|
|
|
+
|
|
|
if (res.status === 'ok') {
|
|
|
formInfo.value = res.data
|
|
|
console.log(res)
|
|
@@ -355,7 +399,7 @@ const handleUpload = async (file) => {
|
|
|
if (rawContent.includes('json')) form = JSON.parse(rawContent.split('json')[1].split('```')[0])
|
|
|
else form = JSON.parse(rawContent)
|
|
|
handleInput(xlsxData.value, form)
|
|
|
- }
|
|
|
+ }
|
|
|
} else {
|
|
|
try {
|
|
|
const { data, msg } = await getFileValue(file)
|
|
@@ -368,7 +412,7 @@ const handleUpload = async (file) => {
|
|
|
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 {
|
|
@@ -382,11 +426,20 @@ const handleUpload = async (file) => {
|
|
|
return
|
|
|
}
|
|
|
if (type.value === FunctionList.File_Operation) {
|
|
|
- const fileVaue = await getFileValue(file)
|
|
|
- // streamRes()
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ })
|
|
|
+ isShowPage.value = true
|
|
|
}
|
|
|
}
|
|
|
-let str = ''
|
|
|
|
|
|
async function getFileValue(file) {
|
|
|
const msg = addMessage(`文件上传中`)
|