Browse Source

change chat ai

master
godo 6 months ago
parent
commit
214ff84f71
  1. 2
      README.md
  2. 1
      frontend/components.d.ts
  3. 2
      frontend/src/assets/chat.scss
  4. 93
      frontend/src/components/localchat/AiChatInfo.vue
  5. 135
      frontend/src/components/localchat/AiChatLeft.vue
  6. 26
      frontend/src/components/localchat/AiChatMain.vue
  7. 39
      frontend/src/stores/aichat.ts
  8. 2
      packages/kanban/package.json

2
README.md

@ -24,6 +24,8 @@
- 新增文件密码箱(系统设置里),可根据不同文件进行加密存储 - 新增文件密码箱(系统设置里),可根据不同文件进行加密存储
- 美化日程提醒弹窗 - 美化日程提醒弹窗
- 修复word格式问题以及导出名字不对 - 修复word格式问题以及导出名字不对
- markdown新增ai优化/续写/纠错/翻译/总结,生成大纲,根据大纲一键创建文章
- 更改文档存储方式,支持选择文件夹
## 🏭 第三阶段目标(十二月底发布) ## 🏭 第三阶段目标(十二月底发布)
1. **文档处理与Markdown智能升级** 1. **文档处理与Markdown智能升级**

1
frontend/components.d.ts

@ -76,6 +76,7 @@ declare module 'vue' {
ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage'] ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']

2
frontend/src/assets/chat.scss

@ -4,7 +4,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
background: #fff;
.messsage-area { .messsage-area {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

93
frontend/src/components/localchat/AiChatInfo.vue

@ -1,58 +1,75 @@
<script setup lang="ts"> <script setup lang="ts">
// import { useKnowStore } from "@/stores/know.ts"; import { useAiChatStore } from "@/stores/aichat";
// import {getModelList,getPromptList} from "@/stores/config";
import { onMounted,ref } from "vue";
import { t } from "@/i18n/index"; import { t } from "@/i18n/index";
// const knowStore = useKnowStore(); import { notifyInfo,notifySuccess } from "@/util/msg.ts";
const props = defineProps<{ const chatStore = useAiChatStore();
dataInfo: any function changPrompt(id:number) {
}>() const promptData:any = chatStore.promptList.find((item:any) => {
const modelList:any = ref([]) return item.id == id;
const knowList:any = ref([])
const promptList:any = ref([])
const emit = defineEmits(['saveFn'])
function changPrompt(promptName:string) {
const promptData:any = promptList.value.find((item:any) => {
return item.name == promptName;
}); });
console.log(promptData)
if(promptData) { if(promptData) {
props.dataInfo.prompt = promptData.prompt; chatStore.editInfo.prompt = promptData.prompt;
} }
}
function saveData(){
emit('saveFn', props.dataInfo)
} }
onMounted(async () => { const changeInfo = async () => {
// modelList.value = await getModelList('chat') const info = chatStore.editInfo;
//knowList.value = await knowStore.getKnowAll()
// promptList.value = await getPromptList('chat') if (!info.title) {
}) notifyInfo(t("chat.inputTitle"));
return;
}
if (!info.model) {
notifyInfo(t("chat.selectModel"));
return;
}
if (!info.prompt) {
info.prompt = "";
}
delete info.id;
if (chatStore.isEditor) {
//console.log(info);
await chatStore.updateChat(info, chatStore.activeId);
chatStore.chatInfo = info;
notifySuccess(t("chat.editsuccess"));
} else {
const promptData = {
prompt: info.prompt,
promptName: info.promptName,
};
await chatStore.addChat(info.title, info.model, promptData, info.kid);
notifySuccess(t("chat.addsuccess"));
}
await chatStore.getActiveChat();
chatStore.showInfo = false;
};
</script> </script>
<template> <template>
<el-form label-width="150px" style="margin-top: 12px"> <el-form label-width="150px" style="margin-top: 12px">
<el-form-item :label="t('common.title')"> <el-form-item :label="t('common.title')">
<el-input <el-input
v-model="props.dataInfo.title" v-model="chatStore.editInfo.title"
:placeholder="t('chat.inputTitle')" :placeholder="t('aichat.inputTitle')"
prefix-icon="Notification" prefix-icon="Notification"
clearable clearable
:autofocus="true" :autofocus="true"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item :label="t('chat.model')"> <el-form-item :label="t('aichat.model')">
<el-select v-model="props.dataInfo.model"> <el-select v-model="chatStore.editInfo.model">
<el-option <el-option
v-for="(item, key) in modelList" v-for="(item, key) in chatStore.modelList"
:key="key" :key="key"
:label="item.model" :label="item.model"
:value="item.model" :value="item.model"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('chat.refknow')"> <!-- <el-form-item :label="t('chat.refknow')">
<el-select <el-select
v-model="props.dataInfo.kid" v-model="chatStore.editInfo.kid"
:clearable="true" :clearable="true"
:filterable="true" :filterable="true"
> >
@ -63,17 +80,17 @@ onMounted(async () => {
:value="item.uuid" :value="item.uuid"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item> -->
<el-form-item :label="t('chat.role')"> <el-form-item :label="t('aichat.role')">
<el-select <el-select
v-model="props.dataInfo.promptName" v-model="chatStore.editInfo.promptId"
@change="changPrompt" @change="changPrompt"
> >
<el-option <el-option
v-for="(item, key) in promptList" v-for="(item, key) in chatStore.promptList"
:key="key" :key="key"
:label="item.name" :label="item.name"
:value="item.name" :value="item.id"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -81,12 +98,12 @@ onMounted(async () => {
<el-form-item> <el-form-item>
<el-input <el-input
type="textarea" type="textarea"
v-model="props.dataInfo.prompt" v-model="chatStore.editInfo.prompt"
:rows="6" :rows="6"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="saveData">{{ <el-button type="primary" @click="changeInfo">{{
t("common.confim") t("common.confim")
}}</el-button> }}</el-button>
</el-form-item> </el-form-item>

135
frontend/src/components/localchat/AiChatLeft.vue

@ -1,34 +1,121 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAiChatStore } from "@/stores/aichat"; import { useAiChatStore } from "@/stores/aichat";
import { Search } from "@element-plus/icons-vue";
// import { t } from "@/i18n";
// import { notifyInfo,notifySuccess } from "@/util/msg.ts";
const chatStore = useAiChatStore(); const chatStore = useAiChatStore();
const showBox = (flag: any) => {
chatStore.isEditor = flag;
if (flag === true) {
chatStore.editInfo = toRaw(chatStore.chatInfo);
} else {
chatStore.editInfo = {
title: "",
model: "",
prompt: "",
promptName: "",
};
}
chatStore.showInfo = true;
};
</script> </script>
<template> <template>
<el-dialog v-model="chatStore.showInfo" width="600" append-to-body>
<ai-chat-info />
</el-dialog>
<el-scrollbar> <el-scrollbar>
<el-space direction="vertical"> <el-header class="search">
<el-card <el-input placeholder="搜索" :prefix-icon="Search" class="search-input" v-model="chatStore.searchInput" />
v-for="(item, key) in chatStore.chatList" <button class="add-chat" @click="showBox(false)">
:key="key" <el-icon>
class="box-card" <Plus />
style="width: 190px" </el-icon>
:shadow="chatStore.activeId == item.id ? 'always' : 'hover'" </button>
> </el-header>
<el-row type="flex" justify="space-between"> <div v-for="(item, key) in chatStore.chatList" :key="key">
<el-button <div class="list-item">
type="info" <el-row justify="space-around">
text <el-col :span="17" @click.stop="chatStore.setActiveId(item.id)" class="chat-title">
@click.stop="chatStore.setActiveId(item.id)"
size="small"
>
{{ item.title }} {{ item.title }}
</el-button> </el-col>
<el-button <el-col :span="7" class="iconlist">
icon="Delete" <el-icon size="15">
size="small" <Edit />
@click.stop="chatStore.deleteChat(item.id)" </el-icon>
circle <el-icon size="15" @click.stop="chatStore.deleteChat(item.id)">
></el-button> <Delete />
</el-icon>
</el-col>
</el-row> </el-row>
</el-card> </div>
</el-space> </div>
</el-scrollbar> </el-scrollbar>
</template> </template>
<style scoped lang="scss">
.search {
display: flex;
align-items: center;
justify-content: space-evenly;
width: 100%;
height: 50px;
padding: 0;
padding-right: 10px;
-webkit-app-region: drag;
border-bottom: 1px solid #e8e8e8;
border-left: 1px solid #e8e8e8;
}
.add-chat {
width: 40px;
height: 30px;
border: none;
border-radius: 4px;
background-color: #f0f0f0;
}
.search-input {
width: calc(100% - 20px);
margin: 10px;
height: 30px;
font-size: 0.7rem;
-webkit-app-region: no-drag;
--el-input-placeholder-color: #bfbfbf !important;
--el-input-icon-color: #bfbfbf !important;
}
.list-item {
width: 95%;
height: 60px;
transition: all 0.5s;
margin: 0 auto;
border-radius: 4px;
margin-bottom: 5px;
overflow: hidden;
margin-top: 5px;
background-color: #fff;
}
.list-item:hover,.list-item .active {
background-color: #e8f3ff;
}
.chat-title {
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
padding-left: 10px;
}
.iconlist {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 10px;
}
.iconlist .el-icon{
cursor: pointer;
}
</style>

26
frontend/src/components/localchat/AiChatMain.vue

@ -9,6 +9,7 @@ import { Vue3Lottie } from "vue3-lottie";
const chatStore = useAiChatStore(); const chatStore = useAiChatStore();
const modelStore = useModelStore(); const modelStore = useModelStore();
const isPadding = ref(false); // const isPadding = ref(false); //
const imageInput: any = ref(null); const imageInput: any = ref(null);
let imageData = ref(""); let imageData = ref("");
const messageContainerRef = ref<InstanceType<typeof ElScrollbar>>(); const messageContainerRef = ref<InstanceType<typeof ElScrollbar>>();
@ -118,8 +119,8 @@ const createCompletion = async () => {
} }
const res = await completion.json(); const res = await completion.json();
//console.log(res) //console.log(res)
if(res && res.choices && res.choices.length > 0){ if (res && res.choices && res.choices.length > 0) {
if(res.choices[0].message.content){ if (res.choices[0].message.content) {
const msg = res.choices[0].message.content; const msg = res.choices[0].message.content;
saveMessage.content = msg; saveMessage.content = msg;
chatStore.messageList.push(saveMessage); chatStore.messageList.push(saveMessage);
@ -127,7 +128,7 @@ const createCompletion = async () => {
} }
} }
isPadding.value = false; isPadding.value = false;
} catch (error:any) { } catch (error: any) {
isPadding.value = false; isPadding.value = false;
notifyError(error.message); notifyError(error.message);
} }
@ -150,7 +151,7 @@ watch(
deep: true, deep: true,
} }
); );
const handleKeydown = (e:any) => { const handleKeydown = (e: any) => {
if (e.key === "Enter" && (e.altKey || e.shiftKey)) { if (e.key === "Enter" && (e.altKey || e.shiftKey)) {
// altshift enter // altshift enter
e.preventDefault(); e.preventDefault();
@ -182,15 +183,15 @@ const uploadImage = async (event: any) => {
reader.readAsDataURL(file); reader.readAsDataURL(file);
}; };
</script> </script>
<template> <template>
<div class="chat-bot"> <div class="chat-bot">
<div class="messsage-area"> <div class="messsage-area">
<el-scrollbar v-if="chatStore.messageList.length > 0" class="message-container" ref="messageContainerRef"> <el-scrollbar v-if="chatStore.messageList.length > 0" class="message-container" ref="messageContainerRef">
<div ref="messageInnerRef"> <div ref="messageInnerRef">
<ai-chat-message v-for="message in chatStore.messageList" :key="message.messageId" <ai-chat-message v-for="message in chatStore.messageList" :key="message.messageId" :content="message.content"
:content="message.content" :link="message.link" :role="message.role" :link="message.link" :role="message.role" :createdAt="message.createdAt" />
:createdAt="message.createdAt" />
</div> </div>
</el-scrollbar> </el-scrollbar>
<div class="no-message-container" v-else> <div class="no-message-container" v-else>
@ -202,18 +203,15 @@ const uploadImage = async (event: any) => {
<el-row :gutter="24" style="border-bottom: none;"> <el-row :gutter="24" style="border-bottom: none;">
<el-col :span="2"> <el-col :span="2">
<el-button @click="selectImage" size="large" icon="Paperclip" circle /> <el-button @click="selectImage" size="large" icon="Paperclip" circle />
<input type="file" ref="imageInput" accept="image/*" style="display: none" <input type="file" ref="imageInput" accept="image/*" style="display: none" @change="uploadImage" />
@change="uploadImage" />
</el-col> </el-col>
<el-col :span="19"> <el-col :span="19">
<el-input v-model="userMessage" :placeholder="t('chat.askme')" size="large" clearable <el-input v-model="userMessage" :placeholder="t('aichat.askme')" size="large" clearable
@keydown="handleKeydown" autofocus /> @keydown="handleKeydown" autofocus />
</el-col> </el-col>
<el-col :span="2"> <el-col :span="2">
<el-button v-if="!isPadding" @click="sendMessage" icon="Promotion" type="info" size="large" <el-button v-if="!isPadding" @click="sendMessage" icon="Promotion" type="info" size="large" circle />
circle /> <el-button type="primary" size="large" v-else loading-icon="Eleme" icon="Loading" loading circle />
<el-button type="primary" size="large" v-else loading-icon="Eleme" icon="Loading" loading
circle />
</el-col> </el-col>
</el-row> </el-row>
</div> </div>

39
frontend/src/stores/aichat.ts

@ -1,21 +1,26 @@
import { defineStore } from "pinia" import { defineStore } from "pinia"
import { db } from "./db.js" import { db } from "./db.js"
import { t } from "@/i18n/index.ts" import { t } from "@/i18n/index.ts"
import {useAssistantStore} from "./assistant.ts"; import { useAssistantStore } from "./assistant.ts";
import { useModelStore } from "@/stores/model"; import { useModelStore } from "@/stores/model";
import { ref } from "vue"; import { ref } from "vue";
export const useAiChatStore = defineStore('aichat', () => { export const useAiChatStore = defineStore('aichat', () => {
const modelStore = useModelStore() const modelStore = useModelStore()
const promptStore = useAssistantStore() const promptStore = useAssistantStore()
const activeId: any = ref(0) const activeId: any = ref(0)
const currentMessage:any = ref({}) const currentMessage: any = ref({})
// 聊天列表 // 聊天列表
const chatList: any = ref([]) const chatList: any = ref([])
const chatInfo:any = ref({}) const chatInfo: any = ref({})
const messageList : any = ref([]) const messageList: any = ref([])
const modelList = ref([]) const modelList:any = ref([])
const promptList:any = ref([]) const promptList: any = ref([])
const newChat = async() => { const searchInput: any = ref('')
const showInfo = ref(false)
const editInfo: any = ref({}); //编辑聊天信息
const isEditor = ref(true);
const newChat = async () => {
const currentModel = await modelStore.getModel('chat') const currentModel = await modelStore.getModel('chat')
if (!currentModel) { if (!currentModel) {
return false return false
@ -28,7 +33,9 @@ export const useAiChatStore = defineStore('aichat', () => {
return await newChat() return await newChat()
} }
modelList.value = await modelStore.getModelAction('chat') modelList.value = await modelStore.getModelAction('chat')
promptList.value = await promptStore.getPrompts('chat') const promptRes = await promptStore.getPrompts('chat')
promptList.value = promptRes.list
} }
const getActiveChat = async () => { const getActiveChat = async () => {
chatInfo.value = await db.getOne('aichatlist', activeId.value) chatInfo.value = await db.getOne('aichatlist', activeId.value)
@ -41,7 +48,7 @@ export const useAiChatStore = defineStore('aichat', () => {
return chatList return chatList
} }
// 添加聊天 // 添加聊天
async function addChat(title: string, modelData: any, promptData: any, knowledgeId:string) { async function addChat(title: string, modelData: any, promptData: any, knowledgeId: string) {
const newChat = { const newChat = {
title, title,
prompt: promptData.prompt, prompt: promptData.prompt,
@ -63,16 +70,16 @@ export const useAiChatStore = defineStore('aichat', () => {
// 删除单个聊天 // 删除单个聊天
async function deleteChat(chatId: number) { async function deleteChat(chatId: number) {
await db.delete('aichatlist', chatId) await db.delete('aichatlist', chatId)
await db.deleteByField('aichatmsg','chatId', chatId) await db.deleteByField('aichatmsg', 'chatId', chatId)
//如果删除的id是当前id //如果删除的id是当前id
let id; let id;
if (chatId == activeId.value) { if (chatId == activeId.value) {
// //
const list = await db.getAll('aichatlist') const list = await db.getAll('aichatlist')
if(list.length > 0) { if (list.length > 0) {
id = list[0]['id'] id = list[0]['id']
}else{ } else {
id = await newChat() id = await newChat()
} }
setActiveId(id) setActiveId(id)
@ -82,7 +89,7 @@ export const useAiChatStore = defineStore('aichat', () => {
// 更新聊天菜单标题 // 更新聊天菜单标题
async function updateTitle(chatId: number, title: string) { async function updateTitle(chatId: number, title: string) {
await db.update('aichatlist', chatId, {title}) await db.update('aichatlist', chatId, { title })
} }
// 清空所有Chat // 清空所有Chat
@ -122,7 +129,7 @@ export const useAiChatStore = defineStore('aichat', () => {
// 更新聊天配置 // 更新聊天配置
async function updateChat(config: any, chatId: number) { async function updateChat(config: any, chatId: number) {
//console.log(config) //console.log(config)
return await db.update('aichatlist',chatId, config) return await db.update('aichatlist', chatId, config)
} }
return { return {
activeId, activeId,
@ -130,6 +137,10 @@ export const useAiChatStore = defineStore('aichat', () => {
messageList, messageList,
chatInfo, chatInfo,
currentMessage, currentMessage,
searchInput,
showInfo,
editInfo,
isEditor,
initChat, initChat,
setActiveId, setActiveId,
getActiveChat, getActiveChat,

2
packages/kanban/package.json

@ -30,7 +30,7 @@
"web-vitals": "^0.2.4" "web-vitals": "^0.2.4"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "dev": "react-scripts start",
"build": "react-scripts build && npm run cpfile", "build": "react-scripts build && npm run cpfile",
"cpfile": "shx cp -rf ./build/* ../../frontend/public/kanban", "cpfile": "shx cp -rf ./build/* ../../frontend/public/kanban",
"test": "react-scripts test", "test": "react-scripts test",

Loading…
Cancel
Save