feat:【antd】【ai】chat 的代码优化

This commit is contained in:
YunaiV
2025-11-15 13:50:55 +08:00
parent 7c7a843e94
commit cc8703ca90
11 changed files with 36 additions and 39 deletions

View File

@@ -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;
} }

View File

@@ -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]);
} }
} }
}); });

View File

@@ -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);
} }

View File

@@ -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

View File

@@ -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) {

View File

@@ -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');
} }
} }
} }

View File

@@ -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>(''); //