mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
chore: update
This commit is contained in:
parent
421b2827d6
commit
cb44488fca
@ -143,7 +143,7 @@ export const useSetChatBoxContext = () => {
|
||||
...options,
|
||||
onConversationCreate: (sessionId: string) => {
|
||||
setCurrentConversation(sessionId);
|
||||
conversationsService.refresh();
|
||||
conversationsService.run();
|
||||
},
|
||||
};
|
||||
const hasInfoFormValues = Object.values(infoForm?.values || []).filter(Boolean).length;
|
||||
|
@ -8,13 +8,17 @@
|
||||
*/
|
||||
|
||||
import { useAPIClient, useRequest } from '@nocobase/client';
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import React, { createContext, useCallback, useContext, useRef, useState } from 'react';
|
||||
import { Conversation } from '../types';
|
||||
import { useLoadMoreObserver } from './useLoadMoreObserver';
|
||||
import { ConversationsProps } from '@ant-design/x';
|
||||
|
||||
type ChatConversationContextValue = {
|
||||
currentConversation?: string;
|
||||
setCurrentConversation: (sessionId?: string) => void;
|
||||
conversationsService: any;
|
||||
lastConversationRef: (node: HTMLDivElement | null) => void;
|
||||
conversations: ConversationsProps['items'];
|
||||
};
|
||||
|
||||
export const ChatConversationsContext = createContext<ChatConversationContextValue | null>(null);
|
||||
@ -24,25 +28,57 @@ export const useChatConversations = () => useContext(ChatConversationsContext);
|
||||
export const ChatConversationsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const api = useAPIClient();
|
||||
const [currentConversation, setCurrentConversation] = useState<string>();
|
||||
const [conversations, setConversations] = useState<ConversationsProps['items']>([]);
|
||||
|
||||
const conversationsService = useRequest<Conversation[]>(
|
||||
() =>
|
||||
(page = 1) =>
|
||||
api
|
||||
.resource('aiConversations')
|
||||
.list({
|
||||
sort: ['-updatedAt'],
|
||||
appends: ['aiEmployee'],
|
||||
page,
|
||||
pageSize: 15,
|
||||
})
|
||||
.then((res) => res?.data?.data),
|
||||
.then((res) => res?.data),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: (data, params) => {
|
||||
const page = params[0];
|
||||
if (!data?.data?.length) {
|
||||
return;
|
||||
}
|
||||
const conversations: ConversationsProps['items'] = data.data.map((conversation) => ({
|
||||
key: conversation.sessionId,
|
||||
label: conversation.title,
|
||||
timestamp: new Date(conversation.updatedAt).getTime(),
|
||||
}));
|
||||
if (!page || page === 1) {
|
||||
setConversations(conversations);
|
||||
} else {
|
||||
setConversations((prev) => [...prev, ...conversations]);
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
const conversationsServiceRef = useRef<any>();
|
||||
conversationsServiceRef.current = conversationsService;
|
||||
const loadMoreConversations = useCallback(async () => {
|
||||
const conversationsService = conversationsServiceRef.current;
|
||||
const meta = conversationsService.data?.meta;
|
||||
if (conversationsService.loading || (meta && meta.page >= meta.totalPage)) {
|
||||
return;
|
||||
}
|
||||
await conversationsService.runAsync(meta.page + 1);
|
||||
}, []);
|
||||
const { ref: lastConversationRef } = useLoadMoreObserver({ loadMore: loadMoreConversations });
|
||||
|
||||
const value = {
|
||||
currentConversation,
|
||||
setCurrentConversation,
|
||||
conversationsService,
|
||||
lastConversationRef,
|
||||
conversations,
|
||||
};
|
||||
|
||||
return <ChatConversationsContext.Provider value={value}>{children}</ChatConversationsContext.Provider>;
|
||||
|
@ -28,7 +28,7 @@ interface ChatMessagesContextValue {
|
||||
},
|
||||
) => Promise<void>;
|
||||
messagesService: any;
|
||||
lastMessageRef: React.RefObject<any>;
|
||||
lastMessageRef: (node: HTMLElement | null) => void;
|
||||
}
|
||||
|
||||
export const ChatMessagesContext = createContext<ChatMessagesContextValue | null>(null);
|
||||
@ -189,18 +189,18 @@ export const ChatMessagesProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
hasMore?: boolean;
|
||||
};
|
||||
}>(
|
||||
(cursor?: string) =>
|
||||
(sessionId, cursor?: string) =>
|
||||
api
|
||||
.resource('aiConversations')
|
||||
.getMessages({
|
||||
sessionId: currentConversation,
|
||||
sessionId,
|
||||
cursor,
|
||||
})
|
||||
.then((res) => res?.data),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: (data, params) => {
|
||||
const cursor = params[0];
|
||||
const cursor = params[1];
|
||||
if (!data?.data?.length) {
|
||||
return;
|
||||
}
|
||||
@ -219,15 +219,9 @@ export const ChatMessagesProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
if (messagesService.loading || !messagesService.data?.meta?.hasMore) {
|
||||
return;
|
||||
}
|
||||
await messagesService.runAsync(messagesService.data?.meta?.cursor);
|
||||
}, []);
|
||||
const { ref: lastMessageRef } = useLoadMoreObserver({ loadMore: loadMoreMessages });
|
||||
useEffect(() => {
|
||||
if (!currentConversation) {
|
||||
return;
|
||||
}
|
||||
messagesServiceRef.current.run();
|
||||
await messagesService.runAsync(currentConversation, messagesService.data?.meta?.cursor);
|
||||
}, [currentConversation]);
|
||||
const { ref: lastMessageRef } = useLoadMoreObserver({ loadMore: loadMoreMessages });
|
||||
|
||||
return (
|
||||
<ChatMessagesContext.Provider
|
||||
|
@ -7,15 +7,15 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Layout, Input, Empty, Spin, App } from 'antd';
|
||||
import { Conversations as AntConversations } from '@ant-design/x';
|
||||
import type { ConversationsProps } from '@ant-design/x';
|
||||
import { useAPIClient, useToken } from '@nocobase/client';
|
||||
import { useChatBoxContext } from './ChatBoxContext';
|
||||
import { useT } from '../../locale';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { useChatConversations } from './ChatConversationsProvider';
|
||||
import { useChatMessages } from './ChatMessagesProvider';
|
||||
const { Header, Content } = Layout;
|
||||
|
||||
export const Conversations: React.FC = () => {
|
||||
@ -23,17 +23,35 @@ export const Conversations: React.FC = () => {
|
||||
const api = useAPIClient();
|
||||
const { modal, message } = App.useApp();
|
||||
const { token } = useToken();
|
||||
const { currentConversation, setCurrentConversation, conversationsService } = useChatConversations();
|
||||
const { currentConversation, setCurrentConversation, conversationsService, conversations, lastConversationRef } =
|
||||
useChatConversations();
|
||||
const { messagesService } = useChatMessages();
|
||||
const startNewConversation = useChatBoxContext('startNewConversation');
|
||||
const setCurrentEmployee = useChatBoxContext('setCurrentEmployee');
|
||||
const setSenderValue = useChatBoxContext('setSenderValue');
|
||||
const setSenderPlaceholder = useChatBoxContext('setSenderPlaceholder');
|
||||
const { loading: ConversationsLoading, data: conversationsRes } = conversationsService;
|
||||
const conversations: ConversationsProps['items'] = (conversationsRes || []).map((conversation) => ({
|
||||
key: conversation.sessionId,
|
||||
label: conversation.title,
|
||||
timestamp: new Date(conversation.updatedAt).getTime(),
|
||||
const { loading: conversationsLoading, data: conversationsRes } = conversationsService;
|
||||
|
||||
const items = useMemo(() => {
|
||||
const result = conversations.map((item, index) => ({
|
||||
...item,
|
||||
label: index === conversations.length - 1 ? <div ref={lastConversationRef}>{item.label}</div> : item.label,
|
||||
}));
|
||||
if (conversationsLoading) {
|
||||
result.push({
|
||||
key: 'loading',
|
||||
label: (
|
||||
<Spin
|
||||
style={{
|
||||
display: 'block',
|
||||
margin: '8px auto',
|
||||
}}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}, [conversations, conversationsLoading, lastConversationRef]);
|
||||
|
||||
const deleteConversation = async (sessionId: string) => {
|
||||
await api.resource('aiConversations').destroy({
|
||||
@ -49,14 +67,19 @@ export const Conversations: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
setCurrentConversation(sessionId);
|
||||
const conversation = conversationsRes.find((item) => item.sessionId === sessionId);
|
||||
const conversation = conversationsRes?.data?.find((item) => item.sessionId === sessionId);
|
||||
setCurrentEmployee(conversation?.aiEmployee);
|
||||
setSenderValue('');
|
||||
setSenderPlaceholder(conversation?.aiEmployee?.chatSettings?.senderPlaceholder);
|
||||
messagesService.run(sessionId);
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout
|
||||
style={{
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<Header
|
||||
style={{
|
||||
backgroundColor: token.colorBgContainer,
|
||||
@ -67,13 +90,17 @@ export const Conversations: React.FC = () => {
|
||||
>
|
||||
<Input.Search style={{ verticalAlign: 'middle' }} />
|
||||
</Header>
|
||||
<Content>
|
||||
<Spin spinning={ConversationsLoading}>
|
||||
<Content
|
||||
style={{
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{conversations && conversations.length ? (
|
||||
<AntConversations
|
||||
activeKey={currentConversation}
|
||||
onActiveChange={selectConversation}
|
||||
items={conversations}
|
||||
items={items}
|
||||
menu={(conversation) => ({
|
||||
items: [
|
||||
{
|
||||
@ -98,7 +125,6 @@ export const Conversations: React.FC = () => {
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
)}
|
||||
</Spin>
|
||||
</Content>
|
||||
</Layout>
|
||||
);
|
||||
|
@ -47,12 +47,15 @@ export const Messages: React.FC = () => {
|
||||
<div>
|
||||
{messages.map((msg, index) => {
|
||||
const role = roles[msg.role];
|
||||
if (!role) {
|
||||
return null;
|
||||
}
|
||||
return index === 0 ? (
|
||||
<div ref={lastMessageRef}>
|
||||
<Bubble {...role} key={msg.key} content={msg.content} />
|
||||
<div key={msg.key} ref={lastMessageRef}>
|
||||
<Bubble {...role} loading={msg.loading} content={msg.content} />
|
||||
</div>
|
||||
) : (
|
||||
<Bubble {...role} key={msg.key} content={msg.content} />
|
||||
<Bubble {...role} key={msg.key} loading={msg.loading} content={msg.content} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user