|
@@ -1,111 +1,700 @@
|
|
|
+<!-- Chat.vue -->
|
|
|
<template>
|
|
|
<div class="chat-container">
|
|
|
- <div class="advanced-header">
|
|
|
- <h2>高级模式</h2>
|
|
|
- <p>在这里您可以使用更多高级功能</p>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="advanced-content">
|
|
|
- <!-- 这里可以添加高级模式的具体内容 -->
|
|
|
- <el-empty description="高级功能正在开发中..." />
|
|
|
-
|
|
|
- <!-- 示例功能区域 -->
|
|
|
- <div class="feature-cards">
|
|
|
- <el-card class="feature-card">
|
|
|
- <template #header>
|
|
|
- <div class="card-header">
|
|
|
- <el-icon><Document /></el-icon>
|
|
|
- <span>文档分析</span>
|
|
|
+ <!-- <div v-if="!messages.length && !msgLoading" class="message-list">
|
|
|
+ <pageMask />
|
|
|
+ </div> -->
|
|
|
+ <!-- 消息列表 -->
|
|
|
+ <div class="message-list">
|
|
|
+ <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>
|
|
|
+ <div class="messages" ref="messagesContainer" >
|
|
|
+ <div v-for="(message, index) in messages" :key="index"
|
|
|
+ :class="['message-item', message.role === 'user' ? 'self' : 'other']">
|
|
|
+ <el-avatar :size="32" :src="message.role === 'user' ? userAvatar : avatar" />
|
|
|
+ <div class="message-content">
|
|
|
+ <div class="content" v-if="message.role ==='system'"
|
|
|
+ :class="{ 'loading-content': message.content === '' }">
|
|
|
+ <StepsDisplay
|
|
|
+ v-if="message.type === 'plan'"
|
|
|
+ :content="message.rawContent"
|
|
|
+ :initialStep="0"
|
|
|
+ @step-change="handleStepChange" @complete="handleComplete"
|
|
|
+ />
|
|
|
+ <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.role === 'user'" :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.sortKey).format('MM-DD HH:mm') }}
|
|
|
+ <!-- <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span> -->
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- <div class="card-content">
|
|
|
- 上传文档进行智能分析和处理
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <el-card class="feature-card">
|
|
|
- <template #header>
|
|
|
- <div class="card-header">
|
|
|
- <el-icon><DataAnalysis /></el-icon>
|
|
|
- <span>数据可视化</span>
|
|
|
+ </div>
|
|
|
+ </el-scrollbar>
|
|
|
+ <ScrollToBottom :target="scrollbar" ref="scrollToBottomRef" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- <Tools v-if="selectModal" :disHistory="sendLoading" :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>
|
|
|
+ <!-- 输入区域 -->
|
|
|
+ <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">
|
|
|
+ <div v-for="(v, i) in pageInfoList" :key="i"
|
|
|
+ :class="`card-content ${pageInfoList.length > 1 ? 'card_width' : ''}`">
|
|
|
+ <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.loading ? v?.state : v?.url }}</span>
|
|
|
+ </div>
|
|
|
+ <el-icon class="closeIcon" size="16px" color="#909399" @click="deletePageInfo(i)">
|
|
|
+ <CircleClose />
|
|
|
+ </el-icon>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- <div class="card-content">
|
|
|
- 将数据转化为直观的图表展示
|
|
|
+ <div v-show="!pageInfoList.length && type === FunctionList.Intelligent_Form_filling">智能填表</div>
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
+
|
|
|
+ <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()" />
|
|
|
+ <div class="chat_area_op">
|
|
|
+
|
|
|
+ <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>
|
|
|
+
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- <historyComponent :msgUuid="msgUuid" ref="historyComponentRef" @currentData="(e) => handleCurrentData(e)" /> -->
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-<script lang="ts" setup>
|
|
|
-import { Document, DataAnalysis } from '@element-plus/icons-vue'
|
|
|
-</script>
|
|
|
+<script setup>
|
|
|
+import { ref, onMounted, nextTick, inject, useTemplateRef, reactive,onBeforeMount } from 'vue'
|
|
|
+import { ElScrollbar, ElAvatar, ElInput, ElButton } from 'element-plus'
|
|
|
+import moment from 'moment'
|
|
|
+import fileLogo from '@/assets/svg/file.svg'
|
|
|
+import {
|
|
|
+ getFileContent,
|
|
|
+ buildObjPrompt,
|
|
|
+ controllerList,
|
|
|
+ formatMessage
|
|
|
+} from '@/entrypoints/sidepanel/utils/ai-service.js'
|
|
|
+import { storeToRefs } from 'pinia'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import { useMsg } from '@/entrypoints/sidepanel/hook/useMsg.ts'
|
|
|
+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 StepsDisplay from './component/StepsDisplay.vue'
|
|
|
+import userAvatar from '@/assets/images/user.png'
|
|
|
+import avatar from '@/public/icon/icon.png'
|
|
|
+import { options, FunctionList } from '@/entrypoints/sidepanel/mock'
|
|
|
+import { useAutoResizeTextarea } from '@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts'
|
|
|
+import { getPageInfo, getPageInfoClean, handleInput,downloadFile } from './utils/index.js'
|
|
|
+import { useMsgStore } from '@/store/modules/msg.ts'
|
|
|
+import { useUserStore } from '@/store/modules/user'
|
|
|
+// import { putChat, uploadFile } from "@/api/index.js";
|
|
|
+// import {askQues,getFormKey} from '@/api/modal.js'
|
|
|
+import { debounce } from 'lodash'
|
|
|
+const userStore = useUserStore()
|
|
|
+// 在其他状态变量附近添加
|
|
|
+const isLoadingMore = ref(false)
|
|
|
+import { v4 as uuidv4 } from 'uuid';
|
|
|
+import { getPlan } from '@/api/advance.js'
|
|
|
+// 滚动条引用
|
|
|
+const scrollbar = ref(null)
|
|
|
+const scrollToBottomRef = ref(null)
|
|
|
+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,msgLoading,selectModal } = storeToRefs(useMsgStore())
|
|
|
+const formInfo = ref('')
|
|
|
+const {
|
|
|
+ taklToHtml,
|
|
|
+ sendLoading,
|
|
|
+ type,
|
|
|
+ fetchRes
|
|
|
|
|
|
-<style scoped>
|
|
|
-.chat-container {
|
|
|
- border-radius: 14px;
|
|
|
- height: 100%;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- border: 1px solid #dcdfe6;
|
|
|
- background-color: #ffffff;
|
|
|
+} = useMsg(scrollbar)
|
|
|
+const inputMessage = ref('')
|
|
|
+const pageInfo = ref('')
|
|
|
+const summaryHtml = ref(false)
|
|
|
+function handleStopAsk() {
|
|
|
+ inputMessage.value = ''
|
|
|
+ pageInfoList.value = []
|
|
|
+ isShowPage.value = false
|
|
|
+ taklToHtml.value = false
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ controllerList.value[0]?.abort()
|
|
|
+ controllerList.value = []
|
|
|
+ sendLoading.value = false
|
|
|
+ formInfo.value = null
|
|
|
}
|
|
|
-.advanced-mode-container {
|
|
|
- height: 100%;
|
|
|
- padding: 20px;
|
|
|
- overflow-y: auto;
|
|
|
+
|
|
|
+function handleScroll(a) {
|
|
|
+ const { wrapRef } = scrollbar.value
|
|
|
+ const { scrollHeight, clientHeight } = wrapRef
|
|
|
+ scrollToBottomRef.value.showButton = scrollHeight - a.scrollTop - clientHeight >= 350
|
|
|
+
|
|
|
+ // 检测是否滚动到顶部
|
|
|
+ if (a.scrollTop <= 10 && hasNext.value) {
|
|
|
+ handleScrollToTop()
|
|
|
+ }
|
|
|
}
|
|
|
+const messagesContainer = ref(null)
|
|
|
+let innerText = ''
|
|
|
+// 处理滚动到顶部的事件
|
|
|
+const handleScrollToTop = debounce(async function () {
|
|
|
+ console.log('滚动到顶部,可以加载更多历史消息')
|
|
|
+ if (!sendLoading.value && !isLoadingMore.value) {
|
|
|
+ isLoadingMore.value = true
|
|
|
+ try {
|
|
|
+ // 记录当前第一条消息的位置
|
|
|
+ const firstMessage = messagesContainer.value?.firstElementChild.querySelector('.timestamp')
|
|
|
+ innerText = firstMessage.innerText
|
|
|
+ const oldHeight = firstMessage?.offsetTop || 0
|
|
|
+ await msgStore.changePage()
|
|
|
+ setTimeout(() => {
|
|
|
+ if (firstMessage) {
|
|
|
+ console.log([...messagesContainer.value.querySelectorAll('.timestamp')].find(_ => _.innerText === innerText).offsetTop, innerText, 778);
|
|
|
+ const newHeight = [...messagesContainer.value.querySelectorAll('.timestamp')].find(_ => _.innerText === innerText).offsetTop
|
|
|
+ const scrollOffset = newHeight - oldHeight
|
|
|
+ scrollbar.value?.setScrollTop(scrollOffset)
|
|
|
+ }
|
|
|
+ }, 400);
|
|
|
+ // 恢复滚动位置
|
|
|
+
|
|
|
+ } finally {
|
|
|
+ isLoadingMore.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+}, 500, { leading: false, trailing: true })
|
|
|
|
|
|
-.advanced-header {
|
|
|
- margin-bottom: 24px;
|
|
|
- text-align: center;
|
|
|
+const handleStepChange = (step) => {
|
|
|
+ console.log('当前步骤:', step)
|
|
|
}
|
|
|
|
|
|
-.advanced-header h2 {
|
|
|
- font-size: 20px;
|
|
|
- color: #303133;
|
|
|
- margin-bottom: 8px;
|
|
|
+const handleComplete = () => {
|
|
|
+ console.log('所有步骤已完成')
|
|
|
+}
|
|
|
+/**
|
|
|
+ * @param {string} msg
|
|
|
+ * @param {string} raw
|
|
|
+ * @param {string} type 发送的类型 document 、text
|
|
|
+ * **/
|
|
|
+async function addMessage(msg, raw, type) {
|
|
|
+ const newMessage = reactive({
|
|
|
+ id:+new Date(),
|
|
|
+ type: type || '',
|
|
|
+ rawContent: raw ?? msg,
|
|
|
+ senderId: userStore.userInfo.id,
|
|
|
+ receiverId:-1, //大模型
|
|
|
+ content: msg,
|
|
|
+ sortKey: moment().valueOf(),
|
|
|
+ role: 'user',
|
|
|
+ conversationId:msgUuid.value,
|
|
|
+ addToHistory: `${!taklToHtml.value}`,
|
|
|
+ redisKey: undefined,
|
|
|
+ fileId:undefined
|
|
|
+ })
|
|
|
+ if (type === 'document') {
|
|
|
+ newMessage.content = msg
|
|
|
+ newMessage.rawContent = raw
|
|
|
+ }
|
|
|
+ if (!msg) return
|
|
|
+ messages.value.push(newMessage)
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return newMessage
|
|
|
}
|
|
|
|
|
|
-.advanced-header p {
|
|
|
- font-size: 14px;
|
|
|
- color: #909399;
|
|
|
+const handleSummary = async () => {
|
|
|
+ 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 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() {
|
|
|
+ handleInput()
|
|
|
+ isShowPage.value = true
|
|
|
+ // taklToHtml.value = true
|
|
|
+ // const tempPageInfo = await getPageInfo()
|
|
|
+ // pageInfo.value = tempPageInfo
|
|
|
+ // pageInfoList.value = [tempPageInfo]
|
|
|
+ inputMessage.value = '/智能填表 '
|
|
|
+ type.value = FunctionList.Intelligent_Form_filling
|
|
|
}
|
|
|
|
|
|
-.feature-cards {
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 16px;
|
|
|
- margin-top: 24px;
|
|
|
+function handleCurrentChange(e) {
|
|
|
+ msgStore.updateAIModel(e)
|
|
|
+ // if (AIModel.value.file === true) {
|
|
|
+ // isShowPage.value = false
|
|
|
+ // taklToHtml.value = false
|
|
|
+ // pageInfoList.value = []
|
|
|
+ // }
|
|
|
}
|
|
|
|
|
|
-.feature-card {
|
|
|
- width: calc(50% - 8px);
|
|
|
- cursor: pointer;
|
|
|
- transition: transform 0.3s;
|
|
|
+async function handleCurrentData(e) {
|
|
|
+ drawerRef.value.drawer = false
|
|
|
+ isShowPage.value = false
|
|
|
+ if (!e) {
|
|
|
+ addNewDialogue()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ messages.value = []
|
|
|
+ page.value = 1
|
|
|
+ msgUuid.value = e
|
|
|
+ chrome.storage.local.set({ msgUuid: msgUuid.value })
|
|
|
+ await msgStore.initMsg()
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+})
|
|
|
}
|
|
|
+let SystemMsg = null
|
|
|
|
|
|
-.feature-card:hover {
|
|
|
- transform: translateY(-5px);
|
|
|
+watchEffect(() => {
|
|
|
+ console.log(isShowPage.value);
|
|
|
+
|
|
|
+ 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)) {
|
|
|
+ ElMessage.warning(`最多添加${import.meta.env.VITE_MAX_FILE_NUMBER}个文件`)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const tempPageInfo = await getPageInfo()
|
|
|
+ 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')
|
|
|
}
|
|
|
|
|
|
-.card-header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
+// 添加下载文件的函数
|
|
|
+
|
|
|
+function deletePageInfo(i) {
|
|
|
+ pageInfoList.value.splice(i, 1)
|
|
|
+ if (pageInfoList.value.length === 0) {
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ inputMessage.value = ''
|
|
|
+ SystemMsg.content = '智能填表流程中断'
|
|
|
+ SystemMsg.rawContent = '智能填表流程中断'
|
|
|
+ putChat({
|
|
|
+ ...SystemMsg,
|
|
|
+ content: '',
|
|
|
+ })
|
|
|
+ }
|
|
|
+ handleStopAsk()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.card-content {
|
|
|
+function addNewDialogue() {
|
|
|
+ if (messages.value.length === 0) {
|
|
|
+ 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 })
|
|
|
+}
|
|
|
+const disabledBtn = computed(() => {
|
|
|
+ return !!(pageInfoList.value.find(_ => _.loading ))
|
|
|
+})
|
|
|
+function closePageInfo() {
|
|
|
+ pageInfoList.value = []
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling && SystemMsg ) {
|
|
|
+ SystemMsg.content = '智能填表流程中断'
|
|
|
+ SystemMsg.rawContent = '智能填表流程中断'
|
|
|
+ putChat({
|
|
|
+ ...SystemMsg,
|
|
|
+ content: '',
|
|
|
+ })
|
|
|
+ }
|
|
|
+ handleStopAsk()
|
|
|
+
|
|
|
+}
|
|
|
+async function handleAsk(value) {
|
|
|
+ if (sendLoading.value) return
|
|
|
+ const str = value ?? inputMessage.value.trim()
|
|
|
+ inputMessage.value = ''
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ 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
|
|
|
+ SystemMsg = obj
|
|
|
+ } else {
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ isShowPage.value = false
|
|
|
+ }
|
|
|
+ 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: '',
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ getPlan({
|
|
|
+ query: msg.rawContent,
|
|
|
+ }).then(res => {
|
|
|
+ sendLoading.value = false
|
|
|
+ obj.type = 'plan'
|
|
|
+ console.log(res);
|
|
|
+ console.log(JSON.parse(res.planJson));
|
|
|
+
|
|
|
+ obj.rawContent = res.planJson
|
|
|
+ // handlePlan(res)
|
|
|
+ }).catch(res => {
|
|
|
+ obj.rawContent = '接口出错,请重试。'
|
|
|
+ obj.content = '接口出错,请重试。'
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ }
|
|
|
+}
|
|
|
+function handlePlan(res) {
|
|
|
+ const obj = reactive({
|
|
|
+ type: 'plan',
|
|
|
+ rawContent: res,
|
|
|
+ senderId: -1,
|
|
|
+ receiverId: userStore.userInfo.id, //大模型
|
|
|
+ content: '',
|
|
|
+ sortKey: moment().valueOf(),
|
|
|
+ role: 'system',
|
|
|
+ conversationId: msgUuid.value,
|
|
|
+ })
|
|
|
+ messages.value.push(obj)
|
|
|
+}
|
|
|
+function handleCapture() {
|
|
|
+ ElMessage({
|
|
|
+ message: '开发中...',
|
|
|
+ grouping: true,
|
|
|
+ showClose: true
|
|
|
+ })
|
|
|
+}
|
|
|
+function hisRecords() {
|
|
|
+ drawerRef.value.drawer = true
|
|
|
+}
|
|
|
+const createFileObj = async (file) => {
|
|
|
+ handleUpload(file)
|
|
|
+}
|
|
|
+const handleUpload = async (file) => {
|
|
|
+ const obj = reactive({
|
|
|
+ title: summaryHtml.value ? file.name.split('.')[0] : file.name,
|
|
|
+ state: '上传中...',
|
|
|
+ favIconUrl: fileLogo,
|
|
|
+ loading: true,
|
|
|
+ url:'',
|
|
|
+ fileId: '',
|
|
|
+ redisKey:'',
|
|
|
+ content: {
|
|
|
+ mainContent: ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ pageInfoList.value = []
|
|
|
+ }
|
|
|
+ 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('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)
|
|
|
+ }
|
|
|
+})
|
|
|
+ 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) {
|
|
|
+ let formData = new FormData()
|
|
|
+ formData.append('file', file)
|
|
|
+ const res = await getFileContent(formData)
|
|
|
+ return res.data
|
|
|
+}
|
|
|
+let a = null
|
|
|
+// 组件挂载时滚动到底部
|
|
|
+onBeforeMount(async () => {
|
|
|
+ await msgStore.initMsg()
|
|
|
+ await msgStore.initModal()
|
|
|
+})
|
|
|
+onMounted(async () => {
|
|
|
+ // msgStore.updateAIModel(options[0].options[0])
|
|
|
+ useAutoResizeTextarea(tareRef, inputMessage)
|
|
|
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
+ if (message.type === 'TO_SIDE_PANEL_PAGE_INFO') {
|
|
|
+ pageInfo.value = message.data
|
|
|
+ // 转发到 content.js
|
|
|
+ chrome.tabs.sendMessage(sender.tab.id, {
|
|
|
+ type: 'TO_CONTENT_SCRIPT',
|
|
|
+ data: message.data
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if (message.type === 'TO_SIDE_PANEL_PAGE_CHANGE') {
|
|
|
+ pageInfo.value = message.data
|
|
|
+ }
|
|
|
+ })
|
|
|
+ nextTick(() => {
|
|
|
+ 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;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 10px 0;
|
|
|
+ color: #909399;
|
|
|
font-size: 14px;
|
|
|
- color: #606266;
|
|
|
- line-height: 1.5;
|
|
|
+
|
|
|
+ .loading-spinner {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ margin-right: 8px;
|
|
|
+ border: 2px solid #e6e6e6;
|
|
|
+ border-top-color: #4d6bfe;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-@media (max-width: 768px) {
|
|
|
- .feature-card {
|
|
|
- width: 100%;
|
|
|
+@keyframes spin {
|
|
|
+ to {
|
|
|
+ transform: rotate(360deg);
|
|
|
}
|
|
|
}
|
|
|
</style>
|