feat:【antd】【ai】chat 的代码优化
This commit is contained in:
@@ -18,12 +18,12 @@ import {
|
|||||||
sendChatMessageStream,
|
sendChatMessageStream,
|
||||||
} from '#/api/ai/chat/message';
|
} from '#/api/ai/chat/message';
|
||||||
|
|
||||||
import ConversationList from './components/conversation/ConversationList.vue';
|
import ConversationList from './modules/conversation/list.vue';
|
||||||
import ConversationUpdateForm from './components/conversation/ConversationUpdateForm.vue';
|
import ConversationUpdateForm from './modules/conversation/update-form.vue';
|
||||||
import MessageList from './components/message/MessageList.vue';
|
import MessageListEmpty from './modules/message/list-empty.vue';
|
||||||
import MessageListEmpty from './components/message/MessageListEmpty.vue';
|
import MessageList from './modules/message/list.vue';
|
||||||
import MessageLoading from './components/message/MessageLoading.vue';
|
import MessageLoading from './modules/message/loading.vue';
|
||||||
import MessageNewConversation from './components/message/MessageNewConversation.vue';
|
import MessageNewConversation from './modules/message/new-conversation.vue';
|
||||||
|
|
||||||
/** AI 聊天对话 列表 */
|
/** AI 聊天对话 列表 */
|
||||||
defineOptions({ name: 'AiChat' });
|
defineOptions({ name: 'AiChat' });
|
||||||
@@ -33,6 +33,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
|||||||
connectedComponent: ConversationUpdateForm,
|
connectedComponent: ConversationUpdateForm,
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 聊天对话
|
// 聊天对话
|
||||||
const conversationListRef = ref();
|
const conversationListRef = ref();
|
||||||
const activeConversationId = ref<null | number>(null); // 选中的对话编号
|
const activeConversationId = ref<null | number>(null); // 选中的对话编号
|
||||||
@@ -87,7 +88,7 @@ async function handleConversationClick(
|
|||||||
) {
|
) {
|
||||||
// 对话进行中,不允许切换
|
// 对话进行中,不允许切换
|
||||||
if (conversationInProgress.value) {
|
if (conversationInProgress.value) {
|
||||||
alert('对话中,不允许切换!');
|
await alert('对话中,不允许切换!');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +98,7 @@ async function handleConversationClick(
|
|||||||
// 刷新 message 列表
|
// 刷新 message 列表
|
||||||
await getMessageList();
|
await getMessageList();
|
||||||
// 滚动底部
|
// 滚动底部
|
||||||
|
// TODO @AI:看看要不要 await
|
||||||
scrollToBottom(true);
|
scrollToBottom(true);
|
||||||
// 清空输入框
|
// 清空输入框
|
||||||
prompt.value = '';
|
prompt.value = '';
|
||||||
@@ -117,7 +119,7 @@ async function handlerConversationDelete(
|
|||||||
async function handleConversationClear() {
|
async function handleConversationClear() {
|
||||||
// 对话进行中,不允许切换
|
// 对话进行中,不允许切换
|
||||||
if (conversationInProgress.value) {
|
if (conversationInProgress.value) {
|
||||||
alert('对话中,不允许切换!');
|
await alert('对话中,不允许切换!');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
activeConversationId.value = null;
|
activeConversationId.value = null;
|
||||||
@@ -128,8 +130,9 @@ async function handleConversationClear() {
|
|||||||
async function openChatConversationUpdateForm() {
|
async function openChatConversationUpdateForm() {
|
||||||
formModalApi.setData({ id: activeConversationId.value }).open();
|
formModalApi.setData({ id: activeConversationId.value }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 对话更新成功,刷新最新信息 */
|
||||||
async function handleConversationUpdateSuccess() {
|
async function handleConversationUpdateSuccess() {
|
||||||
// 对话更新成功,刷新最新信息
|
|
||||||
await getConversation(activeConversationId.value);
|
await getConversation(activeConversationId.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +141,7 @@ async function handleConversationCreate() {
|
|||||||
// 创建对话
|
// 创建对话
|
||||||
await conversationListRef.value.createConversation();
|
await conversationListRef.value.createConversation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理聊天对话的创建成功 */
|
/** 处理聊天对话的创建成功 */
|
||||||
async function handleConversationCreateSuccess() {
|
async function handleConversationCreateSuccess() {
|
||||||
// 创建新的对话,清空输入框
|
// 创建新的对话,清空输入框
|
||||||
@@ -228,6 +232,7 @@ function handleGoTopMessage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// =========== 【发送消息】相关 ===========
|
// =========== 【发送消息】相关 ===========
|
||||||
|
|
||||||
/** 处理来自 keydown 的发送消息 */
|
/** 处理来自 keydown 的发送消息 */
|
||||||
async function handleSendByKeydown(event: any) {
|
async function handleSendByKeydown(event: any) {
|
||||||
// 判断用户是否在输入
|
// 判断用户是否在输入
|
||||||
@@ -282,7 +287,6 @@ function onCompositionstart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onCompositionend() {
|
function onCompositionend() {
|
||||||
// console.log('输入结束...')
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isComposing.value = false;
|
isComposing.value = false;
|
||||||
}, 200);
|
}, 200);
|
||||||
@@ -339,6 +343,7 @@ async function doSendMessageStream(userMessage: AiChatMessageApi.ChatMessage) {
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
await scrollToBottom(); // 底部
|
await scrollToBottom(); // 底部
|
||||||
// 1.3 开始滚动
|
// 1.3 开始滚动
|
||||||
|
// TODO @AI:要不要 await
|
||||||
textRoll();
|
textRoll();
|
||||||
|
|
||||||
// 2. 发送 event stream
|
// 2. 发送 event stream
|
||||||
@@ -351,7 +356,7 @@ async function doSendMessageStream(userMessage: AiChatMessageApi.ChatMessage) {
|
|||||||
async (res: any) => {
|
async (res: any) => {
|
||||||
const { code, data, msg } = JSON.parse(res.data);
|
const { code, data, msg } = JSON.parse(res.data);
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
alert(`对话异常! ${msg}`);
|
await alert(`对话异常! ${msg}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ import {
|
|||||||
} from '#/api/ai/chat/conversation';
|
} from '#/api/ai/chat/conversation';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import RoleRepository from '../role/RoleRepository.vue';
|
import RoleRepository from '../role/repository.vue';
|
||||||
|
|
||||||
// 定义组件 props
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
activeId: {
|
activeId: {
|
||||||
type: [Number, null] as PropType<null | number>,
|
type: [Number, null] as PropType<null | number>,
|
||||||
@@ -29,7 +28,6 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义钩子
|
|
||||||
const emits = defineEmits([
|
const emits = defineEmits([
|
||||||
'onConversationCreate',
|
'onConversationCreate',
|
||||||
'onConversationClick',
|
'onConversationClick',
|
||||||
@@ -41,7 +39,6 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
connectedComponent: RoleRepository,
|
connectedComponent: RoleRepository,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义属性
|
|
||||||
const searchName = ref<string>(''); // 对话搜索
|
const searchName = ref<string>(''); // 对话搜索
|
||||||
const activeConversationId = ref<null | number>(null); // 选中的对话,默认为 null
|
const activeConversationId = ref<null | number>(null); // 选中的对话,默认为 null
|
||||||
const hoverConversationId = ref<null | number>(null); // 悬浮上去的对话
|
const hoverConversationId = ref<null | number>(null); // 悬浮上去的对话
|
||||||
@@ -180,7 +177,7 @@ async function updateConversationTitle(
|
|||||||
conversation: AiChatConversationApi.ChatConversation,
|
conversation: AiChatConversationApi.ChatConversation,
|
||||||
) {
|
) {
|
||||||
// 1. 二次确认
|
// 1. 二次确认
|
||||||
prompt({
|
await prompt({
|
||||||
async beforeClose(scope) {
|
async beforeClose(scope) {
|
||||||
if (scope.isConfirm) {
|
if (scope.isConfirm) {
|
||||||
if (scope.value) {
|
if (scope.value) {
|
||||||
@@ -202,8 +199,7 @@ async function updateConversationTitle(
|
|||||||
if (
|
if (
|
||||||
filterConversationList.length > 0 &&
|
filterConversationList.length > 0 &&
|
||||||
filterConversationList[0] && // tip:避免切换对话
|
filterConversationList[0] && // tip:避免切换对话
|
||||||
activeConversationId.value ===
|
activeConversationId.value === filterConversationList[0].id!
|
||||||
(filterConversationList[0].id!)
|
|
||||||
) {
|
) {
|
||||||
emits('onConversationClick', filterConversationList[0]);
|
emits('onConversationClick', filterConversationList[0]);
|
||||||
}
|
}
|
||||||
@@ -252,7 +248,7 @@ async function handleClearConversation() {
|
|||||||
await confirm('确认后对话会全部清空,置顶的对话除外。');
|
await confirm('确认后对话会全部清空,置顶的对话除外。');
|
||||||
await deleteChatConversationMyByUnpinned();
|
await deleteChatConversationMyByUnpinned();
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
// 清空 对话 和 对话内容
|
// 清空对话、对话内容
|
||||||
activeConversationId.value = null;
|
activeConversationId.value = null;
|
||||||
// 获取对话列表
|
// 获取对话列表
|
||||||
await getChatConversationList();
|
await getChatConversationList();
|
||||||
@@ -283,7 +279,6 @@ watch(activeId, async (newValue) => {
|
|||||||
activeConversationId.value = newValue;
|
activeConversationId.value = newValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义 public 方法
|
|
||||||
defineExpose({ createConversation });
|
defineExpose({ createConversation });
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
@@ -298,7 +293,7 @@ onMounted(async () => {
|
|||||||
if (conversationList.value.length > 0 && conversationList.value[0]) {
|
if (conversationList.value.length > 0 && conversationList.value[0]) {
|
||||||
activeConversationId.value = conversationList.value[0].id;
|
activeConversationId.value = conversationList.value[0].id;
|
||||||
// 回调 onConversationClick
|
// 回调 onConversationClick
|
||||||
await emits('onConversationClick', conversationList.value[0]);
|
emits('onConversationClick', conversationList.value[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
<!-- 消息列表为空时,展示 prompt 列表 -->
|
<!-- 消息列表为空时,展示 prompt 列表 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// prompt 列表
|
|
||||||
|
|
||||||
const emits = defineEmits(['onPrompt']);
|
const emits = defineEmits(['onPrompt']);
|
||||||
|
|
||||||
const promptList = [
|
const promptList = [
|
||||||
{
|
{
|
||||||
prompt: '今天气怎么样?',
|
prompt: '今天气怎么样?',
|
||||||
@@ -10,7 +9,9 @@ const promptList = [
|
|||||||
{
|
{
|
||||||
prompt: '写一首好听的诗歌?',
|
prompt: '写一首好听的诗歌?',
|
||||||
},
|
},
|
||||||
]; /** 选中 prompt 点击 */
|
]; // prompt 列表
|
||||||
|
|
||||||
|
/** 选中 prompt 点击 */
|
||||||
async function handlerPromptClick(prompt: any) {
|
async function handlerPromptClick(prompt: any) {
|
||||||
emits('onPrompt', prompt.prompt);
|
emits('onPrompt', prompt.prompt);
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,8 @@ import { Avatar, Button, message } from 'ant-design-vue';
|
|||||||
import { deleteChatMessage } from '#/api/ai/chat/message';
|
import { deleteChatMessage } from '#/api/ai/chat/message';
|
||||||
import { MarkdownView } from '#/components/markdown-view';
|
import { MarkdownView } from '#/components/markdown-view';
|
||||||
|
|
||||||
import MessageKnowledge from './MessageKnowledge.vue';
|
import MessageKnowledge from './knowledge.vue';
|
||||||
// 定义 props
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
conversation: {
|
conversation: {
|
||||||
type: Object as PropType<AiChatConversationApi.ChatConversation>,
|
type: Object as PropType<AiChatConversationApi.ChatConversation>,
|
||||||
@@ -29,7 +29,6 @@ const props = defineProps({
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// 消息列表
|
|
||||||
|
|
||||||
const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']);
|
const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']);
|
||||||
const { copy } = useClipboard(); // 初始化 copy 到粘贴板
|
const { copy } = useClipboard(); // 初始化 copy 到粘贴板
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { Button } from 'ant-design-vue';
|
||||||
// 定义属性
|
|
||||||
defineProps({
|
defineProps({
|
||||||
categoryList: {
|
categoryList: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<string[]>,
|
||||||
@@ -15,8 +15,7 @@ defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义回调
|
const emits = defineEmits(['onCategoryClick']); // 定义回调
|
||||||
const emits = defineEmits(['onCategoryClick']);
|
|
||||||
|
|
||||||
/** 处理分类点击事件 */
|
/** 处理分类点击事件 */
|
||||||
async function handleCategoryClick(category: string) {
|
async function handleCategoryClick(category: string) {
|
||||||
@@ -9,9 +9,6 @@ import { IconifyIcon } from '@vben/icons';
|
|||||||
|
|
||||||
import { Avatar, Button, Card, Dropdown, Menu } from 'ant-design-vue';
|
import { Avatar, Button, Card, Dropdown, Menu } from 'ant-design-vue';
|
||||||
|
|
||||||
// tabs ref
|
|
||||||
|
|
||||||
// 定义属性
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -28,8 +25,8 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义钩子
|
|
||||||
const emits = defineEmits(['onDelete', 'onEdit', 'onUse', 'onPage']);
|
const emits = defineEmits(['onDelete', 'onEdit', 'onUse', 'onPage']);
|
||||||
|
|
||||||
const tabsRef = ref<any>();
|
const tabsRef = ref<any>();
|
||||||
|
|
||||||
/** 操作:编辑、删除 */
|
/** 操作:编辑、删除 */
|
||||||
@@ -53,7 +50,7 @@ async function handleTabsScroll() {
|
|||||||
if (tabsRef.value) {
|
if (tabsRef.value) {
|
||||||
const { scrollTop, scrollHeight, clientHeight } = tabsRef.value;
|
const { scrollTop, scrollHeight, clientHeight } = tabsRef.value;
|
||||||
if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) {
|
if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) {
|
||||||
await emits('onPage');
|
emits('onPage');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,10 +14,11 @@ import { createChatConversationMy } from '#/api/ai/chat/conversation';
|
|||||||
import { deleteMy, getCategoryList, getMyPage } from '#/api/ai/model/chatRole';
|
import { deleteMy, getCategoryList, getMyPage } from '#/api/ai/model/chatRole';
|
||||||
|
|
||||||
import Form from '../../../../model/chatRole/modules/form.vue';
|
import Form from '../../../../model/chatRole/modules/form.vue';
|
||||||
import RoleCategoryList from './RoleCategoryList.vue';
|
import RoleCategoryList from './category-list.vue';
|
||||||
import RoleList from './RoleList.vue';
|
import RoleList from './list.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [Drawer] = useVbenDrawer({
|
const [Drawer] = useVbenDrawer({
|
||||||
title: '角色管理',
|
title: '角色管理',
|
||||||
footer: false,
|
footer: false,
|
||||||
@@ -28,7 +29,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
|||||||
connectedComponent: Form,
|
connectedComponent: Form,
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
});
|
});
|
||||||
// 属性定义
|
|
||||||
const loading = ref<boolean>(false); // 加载中
|
const loading = ref<boolean>(false); // 加载中
|
||||||
const activeTab = ref<string>('my-role'); // 选中的角色 Tab
|
const activeTab = ref<string>('my-role'); // 选中的角色 Tab
|
||||||
const search = ref<string>(''); // 加载中
|
const search = ref<string>(''); // 加载中
|
||||||
Reference in New Issue
Block a user