Browse Source

add:知识库对话组件

master
tiantian 5 months ago
parent
commit
96d2591780
  1. 262
      frontend/src/components/builtin/FileList.vue
  2. 325
      frontend/src/components/localchat/KnowledgeChat.vue

262
frontend/src/components/builtin/FileList.vue

@ -17,10 +17,7 @@
</div> </div>
</div> </div>
</template> </template>
<div <div draggable="true" class="file-item" :class="{
draggable="true"
class="file-item"
:class="{
chosen: chosenIndexs.includes(index), chosen: chosenIndexs.includes(index),
'no-chosen': !chosenIndexs.includes(index), 'no-chosen': !chosenIndexs.includes(index),
'mode-icon': mode === 'icon', 'mode-icon': mode === 'icon',
@ -29,53 +26,28 @@
'mode-middle': mode === 'middle', 'mode-middle': mode === 'middle',
'mode-detail': mode === 'detail', 'mode-detail': mode === 'detail',
'drag-over': hoverIndex === index, 'drag-over': hoverIndex === index,
}" }" :style="{
:style="{
'--theme-color': theme === 'light' ? '#ffffff6b' : '#3bdbff3d', '--theme-color': theme === 'light' ? '#ffffff6b' : '#3bdbff3d',
}" }" v-for="(item, index) in fileList" :key="item.path" @dblclick="handleOnOpen(item)"
v-for="(item, index) in fileList"
:key="item.path"
@dblclick="handleOnOpen(item)"
@touchstart.passive="doubleTouch($event, item)" @touchstart.passive="doubleTouch($event, item)"
@contextmenu.stop.prevent="handleRightClick($event, item, index)" @contextmenu.stop.prevent="handleRightClick($event, item, index)" @drop="hadnleDrop($event, item.path)"
@drop="hadnleDrop($event, item.path)" @dragenter.prevent="handleDragEnter(index)" @dragover.prevent @dragleave="handleDragLeave()"
@dragenter.prevent="handleDragEnter(index)" @dragstart.stop="startDragApp($event, item)" @click="handleClick(index, item)" @mousedown.stop :ref="(ref: any) => {
@dragover.prevent
@dragleave="handleDragLeave()"
@dragstart.stop="startDragApp($event, item)"
@click="handleClick(index, item)"
@mousedown.stop
:ref="(ref: any) => {
if (ref) { if (ref) {
appPositions[index] = markRaw(ref as Element); appPositions[index] = markRaw(ref as Element);
} }
} }
" ">
>
<!-- {{item.name}} --> <!-- {{item.name}} -->
<div class="file-item_img"> <div class="file-item_img">
<FileIcon :file="item" /> <FileIcon :file="item" />
</div> </div>
<span <span v-if="editIndex !== index" class="file-item_title">
v-if="editIndex !== index"
class="file-item_title"
>
{{ getName(item) }} {{ getName(item) }}
</span> </span>
<textarea <textarea autofocus draggable="false" @dragover.stop @dragstart.stop @dragenter.stop @mousedown.stop
autofocus @dblclick.stop @click.stop @keydown.enter.prevent="onEditNameEnd" v-if="editIndex === index"
draggable="false" class="file-item_title file-item_editing" v-model="editName"></textarea>
@dragover.stop
@dragstart.stop
@dragenter.stop
@mousedown.stop
@dblclick.stop
@click.stop
@keydown.enter.prevent="onEditNameEnd"
v-if="editIndex === index"
class="file-item_title file-item_editing"
v-model="editName"
></textarea>
<template v-if="mode === 'detail'"> <template v-if="mode === 'detail'">
<div class="file-item_type"> <div class="file-item_type">
<span>{{ item.isDirectory ? "-" : dealSize(item.size) }}</span> <span>{{ item.isDirectory ? "-" : dealSize(item.size) }}</span>
@ -93,33 +65,33 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useAppMenu } from "@/hook/useAppMenu"; import { useAppMenu } from "@/hook/useAppMenu";
import { useContextMenu } from "@/hook/useContextMenu.ts"; import { useContextMenu } from "@/hook/useContextMenu.ts";
import { useFileDrag } from "@/hook/useFileDrag"; import { useFileDrag } from "@/hook/useFileDrag";
import { Rect } from "@/hook/useRectChosen"; import { Rect } from "@/hook/useRectChosen";
import { dealSystemName, t } from "@/i18n"; import { dealSystemName, t } from "@/i18n";
import { useChooseStore } from "@/stores/choose"; import { useChooseStore } from "@/stores/choose";
import { getSystemKey } from "@/system/config"; import { getSystemKey } from "@/system/config";
import { emitEvent, mountEvent } from "@/system/event"; import { emitEvent, mountEvent } from "@/system/event";
import { addKnowledge } from "@/hook/useAi"; import { addKnowledge } from "@/hook/useAi";
import { import {
basename, basename,
BrowserWindow, BrowserWindow,
Notify, Notify,
OsFileWithoutContent, OsFileWithoutContent,
useSystem, useSystem,
} from "@/system/index.ts"; } from "@/system/index.ts";
import { Menu } from "@/system/menu/Menu"; import { Menu } from "@/system/menu/Menu";
import { throttle } from "@/util/debounce"; import { throttle } from "@/util/debounce";
import { dealSize } from "@/util/file"; import { dealSize } from "@/util/file";
import { markRaw, onMounted, ref } from "vue"; import { markRaw, onMounted, ref } from "vue";
import { notifyError, notifySuccess } from "@/util/msg"; import { notifyError, notifySuccess } from "@/util/msg";
const { openPropsWindow, copyFile, createLink, deleteFile } = const { openPropsWindow, copyFile, createLink, deleteFile } =
useContextMenu(); useContextMenu();
const sys = useSystem(); const sys = useSystem();
const { startDrag, folderDrop } = useFileDrag(sys); const { startDrag, folderDrop } = useFileDrag(sys);
const choose = useChooseStore(); const choose = useChooseStore();
const props = defineProps({ const props = defineProps({
onChosen: { onChosen: {
type: Function, type: Function,
required: true, required: true,
@ -148,9 +120,9 @@
type: String, type: String,
default: "icon", default: "icon",
}, },
}); });
function getName(item: any) { function getName(item: any) {
const name = dealSystemName(basename(item.path)); const name = dealSystemName(basename(item.path));
// console.log(name) // console.log(name)
// console.log(item.path) // console.log(item.path)
@ -159,9 +131,9 @@
} else { } else {
return name; return name;
} }
} }
function handleOnOpen(item: OsFileWithoutContent) { function handleOnOpen(item: OsFileWithoutContent) {
// props.onOpen(item); // props.onOpen(item);
// emitEvent('desktop.app.open'); // emitEvent('desktop.app.open');
chosenIndexs.value = []; chosenIndexs.value = [];
@ -174,14 +146,14 @@
props.onOpen(item); props.onOpen(item);
emitEvent("desktop.app.open"); emitEvent("desktop.app.open");
} }
} }
function hadnleDrop(mouse: DragEvent, path: string) { function hadnleDrop(mouse: DragEvent, path: string) {
hoverIndex.value = -1; hoverIndex.value = -1;
folderDrop(mouse, path); folderDrop(mouse, path);
chosenIndexs.value = []; chosenIndexs.value = [];
} }
let expired: number | null = null; let expired: number | null = null;
function doubleTouch(e: TouchEvent, item: OsFileWithoutContent) { function doubleTouch(e: TouchEvent, item: OsFileWithoutContent) {
if (e.touches.length === 1) { if (e.touches.length === 1) {
if (!expired) { if (!expired) {
expired = e.timeStamp + 400; expired = e.timeStamp + 400;
@ -196,12 +168,12 @@
expired = e.timeStamp + 400; expired = e.timeStamp + 400;
} }
} }
} }
const editIndex = ref<number>(-1); const editIndex = ref<number>(-1);
const editName = ref<string>(""); const editName = ref<string>("");
async function onEditNameEnd() { async function onEditNameEnd() {
const editEndName = editName.value.trim(); const editEndName = editName.value.trim();
if (editIndex.value >= 0) { if (editIndex.value >= 0) {
const currentItem = props.fileList[editIndex.value]; const currentItem = props.fileList[editIndex.value];
@ -211,25 +183,25 @@
} }
} }
editIndex.value = -1; editIndex.value = -1;
} }
mountEvent("edit.end", () => { mountEvent("edit.end", () => {
onEditNameEnd(); onEditNameEnd();
}); });
const hoverIndex = ref<number>(-1); const hoverIndex = ref<number>(-1);
const appPositions = ref<Array<Element>>([]); const appPositions = ref<Array<Element>>([]);
const chosenIndexs = ref<Array<number>>([]); const chosenIndexs = ref<Array<number>>([]);
import { isMobileDevice } from "@/util/device"; import { isMobileDevice } from "@/util/device";
const isMobile = ref<boolean>(false); const isMobile = ref<boolean>(false);
function handleClick(index: number, item: OsFileWithoutContent) { function handleClick(index: number, item: OsFileWithoutContent) {
chosenIndexs.value = [index]; chosenIndexs.value = [index];
if (isMobile.value) { if (isMobile.value) {
handleOnOpen(item); handleOnOpen(item);
} }
} }
onMounted(() => { onMounted(() => {
isMobile.value = isMobileDevice(); isMobile.value = isMobileDevice();
chosenIndexs.value = []; chosenIndexs.value = [];
props.onChosen( props.onChosen(
@ -253,9 +225,9 @@
chosenIndexs.value = tempChosen; chosenIndexs.value = tempChosen;
}, 100) }, 100)
); );
}); });
function startDragApp(mouse: DragEvent, item: OsFileWithoutContent) { function startDragApp(mouse: DragEvent, item: OsFileWithoutContent) {
if (chosenIndexs.value.length) { if (chosenIndexs.value.length) {
startDrag( startDrag(
mouse, mouse,
@ -271,31 +243,31 @@
chosenIndexs.value = []; chosenIndexs.value = [];
}); });
} }
} }
document.addEventListener("copy", function () { document.addEventListener("copy", function () {
const files = chosenIndexs.value.map((index) => props.fileList[index]); const files = chosenIndexs.value.map((index) => props.fileList[index]);
if (files.length > 0) { if (files.length > 0) {
copyFile(files); copyFile(files);
chosenIndexs.value = []; chosenIndexs.value = [];
} }
}); });
// document.addEventListener('keydown', function (event) { // document.addEventListener('keydown', function (event) {
// // Control + C // // Control + C
// if (event.ctrlKey && event.key === 'c') { // if (event.ctrlKey && event.key === 'c') {
// //console.log('Control + C '); // //console.log('Control + C ');
// copyFile( // copyFile(
// chosenIndexs.value.map( // chosenIndexs.value.map(
// (index) => props.fileList[index] // (index) => props.fileList[index]
// ) // )
// ); // );
// chosenIndexs.value = []; // chosenIndexs.value = [];
// } // }
// }); // });
function handleRightClick( function handleRightClick(
mouse: MouseEvent, mouse: MouseEvent,
item: OsFileWithoutContent, item: OsFileWithoutContent,
index: number index: number
) { ) {
if (chosenIndexs.value.length <= 1) { if (chosenIndexs.value.length <= 1) {
chosenIndexs.value = [ chosenIndexs.value = [
props.fileList.findIndex((app) => app.path === item.path), props.fileList.findIndex((app) => app.path === item.path),
@ -410,7 +382,7 @@
click: () => { click: () => {
const win = new BrowserWindow({ const win = new BrowserWindow({
title: "对话知识库", title: "对话知识库",
content: "AiChatMain", content: "KnowledgeChat",
config: { config: {
path: item.path, path: item.path,
knowledgeId: item.knowledgeId, knowledgeId: item.knowledgeId,
@ -554,36 +526,36 @@
//console.log(ext) //console.log(ext)
Menu.buildFromTemplate(menuArr).popup(mouse); Menu.buildFromTemplate(menuArr).popup(mouse);
} }
function handleDragEnter(index: number) { function handleDragEnter(index: number) {
hoverIndex.value = index; hoverIndex.value = index;
} }
function handleDragLeave() { function handleDragLeave() {
hoverIndex.value = -1; hoverIndex.value = -1;
} }
// function dealtName(name: string) { // function dealtName(name: string) {
// return name; // return name;
// } // }
function getPathSeparator(path: string): string { function getPathSeparator(path: string): string {
return path.indexOf("/") === -1 ? "\\" : "/"; return path.indexOf("/") === -1 ? "\\" : "/";
} }
function buildNewPath(editpath: string, editEndName: string): string { function buildNewPath(editpath: string, editEndName: string): string {
const sp = getPathSeparator(editpath); const sp = getPathSeparator(editpath);
const pathParts = editpath.split(sp); const pathParts = editpath.split(sp);
pathParts.pop(); pathParts.pop();
pathParts.push(editEndName); pathParts.push(editEndName);
return pathParts.join(sp); return pathParts.join(sp);
} }
async function renameFile( async function renameFile(
currentItem: OsFileWithoutContent, currentItem: OsFileWithoutContent,
newName: string newName: string
) { ) {
const currentName = basename(currentItem.path); const currentName = basename(currentItem.path);
if (newName && newName !== currentName) { if (newName && newName !== currentName) {
const editpath = currentItem.path.toString(); const editpath = currentItem.path.toString();
@ -609,10 +581,10 @@
} }
} }
return false; return false;
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.file-item { .file-item {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -667,13 +639,13 @@
box-shadow: 0 3px 8px rgba(0, 86, 179, 0.3); box-shadow: 0 3px 8px rgba(0, 86, 179, 0.3);
background-color: var(--focus-bg-color, #e6f7ff); background-color: var(--focus-bg-color, #e6f7ff);
} }
} }
.file-item:hover { .file-item:hover {
background-color: #b1f1ff4c; background-color: #b1f1ff4c;
} }
.chosen { .chosen {
border: 1px dashed #3bdbff3d; border: 1px dashed #3bdbff3d;
// background-color: #ffffff6b; // background-color: #ffffff6b;
background-color: var(--theme-color); background-color: var(--theme-color);
@ -686,9 +658,9 @@
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
} }
} }
.no-chosen { .no-chosen {
.file-item_title { .file-item_title {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -697,15 +669,15 @@
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
} }
} }
.drag-over { .drag-over {
border: 1px dashed #3bdbff3d; border: 1px dashed #3bdbff3d;
// background-color: #ffffff6b; // background-color: #ffffff6b;
background-color: var(--theme-color); background-color: var(--theme-color);
} }
.mode-icon { .mode-icon {
.file-item_img { .file-item_img {
width: 60%; width: 60%;
height: calc(0.6 * var(--desk-item-size)); height: calc(0.6 * var(--desk-item-size));
@ -722,9 +694,9 @@
word-break: break-all; word-break: break-all;
flex-grow: 0; flex-grow: 0;
} }
} }
.mode-list { .mode-list {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
@ -743,24 +715,24 @@
height: min-content; height: min-content;
word-break: break-all; word-break: break-all;
} }
} }
.mode-icon { .mode-icon {
width: var(--desk-item-size); width: var(--desk-item-size);
height: var(--desk-item-size); height: var(--desk-item-size);
} }
.mode-big { .mode-big {
width: calc(var(--desk-item-size) * 2.5); width: calc(var(--desk-item-size) * 2.5);
height: calc(var(--desk-item-size) * 2.5); height: calc(var(--desk-item-size) * 2.5);
} }
.mode-middle { .mode-middle {
width: calc(var(--desk-item-size) * 1.5); width: calc(var(--desk-item-size) * 1.5);
height: calc(var(--desk-item-size) * 1.5); height: calc(var(--desk-item-size) * 1.5);
} }
.mode-detail { .mode-detail {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
@ -790,14 +762,14 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
} }
.file-bar:hover { .file-bar:hover {
background-color: unset; background-color: unset;
user-select: none; user-select: none;
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.file-item { .file-item {
height: vh(70); height: vh(70);
display: flex; display: flex;
@ -825,5 +797,5 @@
background-color: transparent; background-color: transparent;
border: 1px solid transparent; border: 1px solid transparent;
} }
} }
</style> </style>

325
frontend/src/components/localchat/KnowledgeChat.vue

@ -0,0 +1,325 @@
<script setup lang="ts">
import { useAiChatStore } from "@/stores/aichat";
import { useModelStore } from "@/stores/model";
import { t } from "@/i18n";
import { notifyError } from "@/util/msg.ts";
import { ElScrollbar } from "element-plus";
import { getSystemConfig } from "@/system/config";
import { Vue3Lottie } from "vue3-lottie";
import { BrowserWindow, useSystem } from "@/system";
import { isMobileDevice } from "@/util/device";
const chatStore = useAiChatStore();
const modelStore = useModelStore();
const isPadding = ref(false); //
const webSearch = ref(false);
const imageInput: any = ref(null);
const win: any = inject<BrowserWindow>("browserWindow");
let imageData = ref("");
let fileContent = ref("");
let fileName = ref("");
const messageContainerRef = ref<InstanceType<typeof ElScrollbar>>();
const messageInnerRef = ref<HTMLDivElement>();
// User Input Message
const userMessage = ref("");
const knowledgeId = ref(0)
const system = useSystem()
const messageList: any = ref([])
const chatInfo: any = ref({})
const activeId: any = ref(0)
const promptMessage = computed(() => {
return [
{
content: chatInfo.value.prompt || "",
chatType: "chat",
chatId: activeId.value,
role: "system",
id: Date.now(),
createAt: Date.now(),
},
];
});
onMounted(async () => {
knowledgeId.value = win?.config?.knowledgeId || 0
await chatStore.initChat(knowledgeId.value)
const res = await chatStore.getActiveChat()
messageList.value = res.messageList.value
chatInfo.value = res.chatInfo.value
activeId.value = res?.chatInfo?.value?.id || 0
console.log(messageList.value, chatInfo.value, activeId.value)
//await aiStore.initChat()
});
const requestMessages = computed(() => {
const contextLen = modelStore.chatConfig.chat.contextLength;
//console.log(contextLen)
if (messageList.value.length <= contextLen) {
return [...promptMessage.value, ...messageList.value];
} else {
// 10
const slicedMessages = messageList.value.slice(-contextLen);
return [...promptMessage.value, ...slicedMessages];
}
});
// async function addKnowledge(){
// const knowledgeId = win?.config?.knowledgeId || 0;
// let msg = userMessage.value
// if (knowledgeId > 0) {
// const askData: any = {
// id: knowledgeId,
// input: userMessage.value,
// }
// const config = getSystemConfig()
// const postData: any = {
// method: "POST",
// body: JSON.stringify(askData),
// };
// const completion = await fetch(config.apiUrl + '/ai/askknowledge', postData);
// if (!completion.ok) {
// const errorData = await completion.json();
// //console.log(errorData)
// notifyError(errorData.message);
// isPadding.value = false;
// return;
// }
// const res = await completion.json();
// console.log(res)
// // let prompt = await chatStore.getPrompt("knowledge")
// // if (prompt == '') {
// // notifyError("prompt")
// // return
// // }
// if (res && res.data.length > 0) {
// let context: string = "";
// res.data.forEach((item: any) => {
// context += "- " + item.content + "\n";
// })
// //prompt = prompt.replace("{content}", context)
// msg = `\n${context}\n:${userMessage.value} `
// }
// }
// return msg
// }
const sendMessage = async () => {
if (chatStore.activeId < 1) {
notifyError(t("aichat.selectModel"));
return;
}
if (userMessage.value) {
// Add the message to the list
if (isPadding.value === true) return;
let saveMessage: any = {
content: userMessage.value,
chatId: activeId.value,
role: "user",
id: Date.now(),
createdAt: Date.now(),
};
messageList.value.push(saveMessage);
await chatStore.addMessages(activeId.value, saveMessage);
// Clear the input
userMessage.value = "";
// Create a completion
isPadding.value = true;
await createCompletion();
}
};
const createCompletion = async () => {
try {
const config = getSystemConfig()
const messageId = Date.now();
const saveMessage: any = {
content: "",
role: "assistant",
chatType: "chat",
chatId: activeId.value,
id: messageId,
createdAt: messageId,
};
const chatConfig = modelStore.chatConfig.chat;
const knowledgeId = chatInfo.value.knowledgeId * 1;
let postMsg: any = {
messages: requestMessages.value,
model: chatInfo.value.model,
engine: chatInfo.value.engine,
stream: false,
webSearch: webSearch.value,
fileContent: fileContent.value,
fileName: fileName.value,
options: chatConfig,
knowledgeId: knowledgeId,
};
if (imageData.value != "") {
const img2txtModel = await modelStore.getModel("img2txt");
const usermsg = messageList.value[messageList.value.length - 1];
postMsg = {
model: img2txtModel.model,
//"prompt":userMessage.value,
engine: img2txtModel.info.engine,
stream: false,
webSearch: false,
options: chatConfig,
messages: [
{
role: "user",
content: usermsg.content,
images: [imageData.value],
},
],
knowledgeId: knowledgeId,
};
}
const postData: any = {
method: "POST",
body: JSON.stringify(postMsg),
};
//console.log(postData)
const completion = await fetch(config.apiUrl + '/ai/chat', postData);
//const completion:any = await modelStore.getModel(postData)
imageData.value = "";
fileContent.value = "";
fileName.value = "";
if (!completion.ok) {
const errorData = await completion.json();
notifyError(errorData.error.message);
isPadding.value = false;
return;
}
const res = await completion.json();
console.log(res)
if (res && res.choices && res.choices.length > 0) {
if (res.choices[0].message.content) {
const msg = res.choices[0].message.content;
saveMessage.content = msg;
if (res.documents && res.documents.length > 0) {
saveMessage.doc = res.documents;
}
if (res.web_search && res.web_search.length > 0) {
saveMessage.web_search = res.web_search;
}
messageList.value.push(saveMessage);
await chatStore.addMessages(activeId.value, saveMessage);
}
}
isPadding.value = false;
} catch (error: any) {
isPadding.value = false;
notifyError(error.message);
}
};
const scrollToBottom = () => {
nextTick(() => {
if (messageContainerRef && messageContainerRef.value) {
messageContainerRef.value!.setScrollTop(
messageInnerRef.value!.clientHeight
);
}
});
};
watch(
() => messageList,
() => {
scrollToBottom();
},
{
deep: true,
}
);
const handleKeydown = (e: any) => {
if (e.key === "Enter" && (e.altKey || e.shiftKey)) {
// altshift enter
e.preventDefault();
userMessage.value += "\n";
} else if (e.key === "Enter") {
// enter
e.preventDefault();
sendMessage();
}
};
const selectImage = async () => {
imageInput.value.click();
};
const uploadImage = async (event: any) => {
const file = event.target.files[0];
if (!file) {
return;
}
//console.log(file)
if (file.type.startsWith('image/')) {
const img2txtModel = await modelStore.getModel("img2txt");
if (!img2txtModel) {
notifyError(t("aichat.notEyeModel"));
return;
}
}
const reader = new FileReader();
reader.onload = (e: any) => {
const fileData = e.target.result.split(",")[1];
if (file.type.startsWith('image/')) {
imageData.value = fileData;
} else {
fileContent.value = fileData;
fileName.value = file.name;
}
//console.log(fileContent.value)
};
reader.readAsDataURL(file);
};
</script>
<template>
<el-dialog v-model="chatStore.showInfo" width="600" append-to-body :fullscreen="isMobileDevice() ? true : false">
<ai-chat-info />
</el-dialog>
<div class="chat-bot">
<div class="messsage-area">
<el-scrollbar v-if="messageList.length > 0" class="message-container" ref="messageContainerRef">
<div ref="messageInnerRef">
<ai-chat-message v-for="message in messageList" :key="message.messageId" :content="message.content"
:link="message.link" :role="message.role" :createdAt="message.createdAt"
:doc="message.doc || []" :web_search="message.web_search || []" :system="system" />
</div>
</el-scrollbar>
<div class="no-message-container" v-else>
<Vue3Lottie animationLink="/bot/chat.json" :height="420" :width="420" />
</div>
</div>
<div class="input-area">
<div class="input-panel">
<el-row :gutter="24" style="border-bottom: none;">
<el-col :span="2">
<el-button class="file-btn" @click="selectImage" size="large" icon="Paperclip" circle
:class="{ 'selected-image': imageData != '' || fileContent != '' }" />
<input type="file" ref="imageInput"
accept="image/*,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/pdf"
style="display: none" @change="uploadImage" />
</el-col>
<el-col :span="2">
<el-button class="websearch-btn" @click="webSearch = !webSearch" size="large"
icon="ChromeFilled" circle :type="webSearch ? 'primary' : 'default'" />
</el-col>
<el-col :span="17">
<el-input v-model="userMessage" :placeholder="t('aichat.askme')" size="large" clearable
@keydown="handleKeydown" :autofocus="isMobileDevice() ? false : true"
class="ai-input-area" />
</el-col>
<el-col :span="2">
<el-button v-if="!isPadding" @click="sendMessage" icon="Promotion" type="info" size="large"
circle />
<el-button type="primary" size="large" v-else loading-icon="Eleme" icon="Loading" loading
circle />
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/chat.scss";
</style>
Loading…
Cancel
Save