diff --git a/src/ChatItem/index.tsx b/src/ChatItem/index.tsx index f70d3b40..333e87ba 100644 --- a/src/ChatItem/index.tsx +++ b/src/ChatItem/index.tsx @@ -168,14 +168,16 @@ const ChatItem = memo((props) => { {mobile && type === 'block' && } ); - return ( - chatItemRenderConfig?.render?.(props, { + return chatItemRenderConfig?.render?.( + props, + { avatar: avatarDom, messageContent: messageContentDom, actions: actionsDom, title: titleDom, itemDom, - }) || itemDom + }, + itemDom, ); }); diff --git a/src/ChatItem/type.ts b/src/ChatItem/type.ts index dc54d0c5..4cf682f5 100644 --- a/src/ChatItem/type.ts +++ b/src/ChatItem/type.ts @@ -87,13 +87,14 @@ export interface ChatItemProps> { render?: WithFalse< ( props: ChatItemProps, - defaultDom: { + domsMap: { avatar: ReactNode; title: ReactNode; messageContent: ReactNode; actions: ReactNode; itemDom: ReactNode; }, + defaultDom: ReactNode, ) => ReactNode >; }; diff --git a/src/ChatList/ActionsBar.tsx b/src/ChatList/ActionsBar.tsx index e7fc3112..8380d4dd 100644 --- a/src/ChatList/ActionsBar.tsx +++ b/src/ChatList/ActionsBar.tsx @@ -3,16 +3,41 @@ import { memo } from 'react'; import ActionIconGroup, { type ActionIconGroupProps } from '@/ActionIconGroup'; import { useChatListActionsBar } from '@/hooks/useChatListActionsBar'; +/** + * ActionsBar组件的属性类型定义 + */ export interface ActionsBarProps extends ActionIconGroupProps { + /** + * 文本内容 + */ text?: { + /** + * 复制文本 + */ copy?: string; + /** + * 删除文本 + */ delete?: string; + /** + * 编辑文本 + */ edit?: string; + /** + * 重新生成文本 + */ regenerate?: string; }; + /** + * 内容 + */ content?: React.ReactNode | undefined; } +/** + * ActionsBar 组件 + * 用于渲染操作按钮组。 + */ const ActionsBar = memo(({ text, ...rest }) => { const { regenerate, edit, copy, divider, del } = useChatListActionsBar(text); return ( diff --git a/src/ChatList/ChatListItem.tsx b/src/ChatList/ChatListItem.tsx index cef9b436..1b05b8d5 100644 --- a/src/ChatList/ChatListItem.tsx +++ b/src/ChatList/ChatListItem.tsx @@ -19,48 +19,64 @@ export type RenderMessageExtra = FC; export type RenderErrorMessage = FC; export type RenderAction = FC; +/** + * 聊天列表项的属性。 + * @template T 聊天列表项的额外数据类型。 + */ export interface ListItemProps> { + /** + * 聊天项的导航组件。 + */ groupNav?: ChatItemProps['avatarAddon']; + /** + * 是否正在加载。 + */ loading?: boolean; /** - * @description 点击操作按钮的回调函数 + * 点击操作按钮的回调函数。 */ onActionsClick?: OnActionClick; /** - * @description 消息变化的回调函数 + * 消息变化的回调函数。 */ onMessageChange?: OnMessageChange; + /** + * 渲染操作按钮的函数。 + */ renderActions?: { [actionKey: string]: RenderAction; }; /** - * @description 渲染错误消息的函数 + * 渲染错误消息的函数。 */ renderErrorMessages?: { [errorType: 'default' | string]: RenderErrorMessage; }; + /** + * 渲染列表项的函数。 + */ renderItems?: { [role: RenderRole]: RenderItem; }; /** - * @description 渲染消息的函数 + * 渲染消息的函数。 */ renderMessages?: { [role: RenderRole]: RenderMessage; }; /** - * @description 渲染消息额外内容的函数 + * 渲染消息额外内容的函数。 */ renderMessagesExtra?: { [role: RenderRole]: RenderMessageExtra; }; /** - * @description 是否显示聊天项的名称 + * 是否显示聊天项的名称。 * @default false */ showTitle?: boolean; /** - * @description 文本内容 + * 文本内容。 */ text?: ChatItemProps['text'] & ActionsBarProps['text'] & { @@ -70,27 +86,36 @@ export interface ListItemProps> { [key: string]: string; }; /** - * @description 聊天列表的类型 + * 聊天列表的类型。 * @default 'chat' */ type?: 'docs' | 'chat'; - /** - * @description 聊天项的类名 + * 聊天项的类名。 * @default '' */ chatItemClassName?: string; - /** - * @description 聊天项的渲染函数 + * 聊天项的渲染函数。 */ chatItemRenderConfig?: ChatItemProps['chatItemRenderConfig']; - + /** + * 原始数据。 + */ originData?: ChatItemProps['originData']; } +/** + * 聊天列表项的属性。 + * @template T 聊天列表项的额外数据类型。 + */ export type ChatListItemProps> = ChatMessage & ListItemProps; +/** + * 聊天列表项组件。 + * @param props 组件属性。 + * @returns 聊天列表项组件。 + */ const ChatListItem = (props: ChatListItemProps) => { const { renderMessagesExtra, @@ -115,6 +140,10 @@ const ChatListItem = (props: ChatListItemProps) => { const { message } = App.useApp(); + /** + * 渲染列表项的函数。 + * @returns 渲染列表项的函数。 + */ const RenderItem = useMemo(() => { if (!renderItems || !item?.role) return; let renderFunction; @@ -124,6 +153,12 @@ const ChatListItem = (props: ChatListItemProps) => { return renderFunction; }, [renderItems?.[item.role]]); + /** + * 渲染消息的函数。 + * @param editableContent 可编辑的内容。 + * @param data 聊天消息的数据。 + * @returns 渲染消息的组件。 + */ const RenderMessage = useRefFunction( ({ editableContent, data }: { data: ChatMessage; editableContent: ReactNode }) => { if (!renderMessages || !item?.role) return; @@ -136,6 +171,11 @@ const ChatListItem = (props: ChatListItemProps) => { }, ); + /** + * 渲染消息额外内容的函数。 + * @param data 聊天消息的数据。 + * @returns 渲染消息额外内容的组件。 + */ const MessageExtra = useRefFunction(({ data }: { data: ChatMessage }) => { if (!renderMessagesExtra || !item?.role) return; let RenderFunction; @@ -146,6 +186,11 @@ const ChatListItem = (props: ChatListItemProps) => { return ; }); + /** + * 渲染错误消息的函数。 + * @param data 聊天消息的数据。 + * @returns 渲染错误消息的组件。 + */ const ErrorMessage = useRefFunction(({ data }: { data: ChatMessage }) => { if (!renderErrorMessages || !item?.error?.type) return; let RenderFunction; @@ -157,6 +202,11 @@ const ChatListItem = (props: ChatListItemProps) => { return ; }); + /** + * 渲染操作按钮的函数。 + * @param data 聊天消息的数据。 + * @returns 渲染操作按钮的组件。 + */ const Actions = useRefFunction(({ data }: { data: ChatMessage }) => { if (!renderActions || !item?.role) return; let RenderFunction; @@ -188,6 +238,10 @@ const ChatListItem = (props: ChatListItemProps) => { ); }); + /** + * 错误信息。 + * @returns 错误信息对象。 + */ const error = useMemo(() => { if (!item.error) return; return { diff --git a/src/ChatList/HistoryDivider.tsx b/src/ChatList/HistoryDivider.tsx index bb92a48e..497067b4 100644 --- a/src/ChatList/HistoryDivider.tsx +++ b/src/ChatList/HistoryDivider.tsx @@ -5,11 +5,23 @@ import { memo } from 'react'; import Icon from '@/Icon'; import Tag from '@/components/Tag'; +/** + * 历史记录分割线组件的属性。 + */ interface HistoryDividerProps { + /** + * 是否启用分割线。 + */ enable?: boolean; + /** + * 分割线文本。 + */ text?: string; } +/** + * 历史记录分割线组件。 + */ const HistoryDivider = memo(({ enable, text }) => { if (!enable) return null; diff --git a/src/ChatList/ShouldUpdateItem.tsx b/src/ChatList/ShouldUpdateItem.tsx index 8e9884c2..e7488123 100644 --- a/src/ChatList/ShouldUpdateItem.tsx +++ b/src/ChatList/ShouldUpdateItem.tsx @@ -1,6 +1,8 @@ import isEqual from 'fast-deep-equal'; import { Component } from 'react'; - +/** + * 组件用于判断是否需要更新的辅助类。 + */ class ShouldUpdateItem extends Component< { shouldUpdate?: (prevProps: any, nextProps: any) => boolean; @@ -9,6 +11,11 @@ class ShouldUpdateItem extends Component< }, any > { + /** + * 判断组件是否需要更新。 + * @param nextProps - 下一个属性对象。 + * @returns 如果需要更新则返回 true,否则返回 false。 + */ shouldComponentUpdate(nextProps: any) { if (nextProps.shouldUpdate) { return nextProps.shouldUpdate(this.props, nextProps); @@ -19,6 +26,7 @@ class ShouldUpdateItem extends Component< return true; } } + render() { return this.props.children; } diff --git a/src/ChatList/index.tsx b/src/ChatList/index.tsx index 17b5f6f9..1190fb82 100644 --- a/src/ChatList/index.tsx +++ b/src/ChatList/index.tsx @@ -28,6 +28,30 @@ export type { RenderMessageExtra, } from './ChatListItem'; +/** + * ChatList组件用于显示聊天列表。 + * + * @component + * @param {Object} props - 组件属性 + * @param {Function} props.onActionsClick - 点击操作按钮时的回调函数 + * @param {Function} props.renderMessagesExtra - 渲染额外的消息内容的回调函数 + * @param {string} props.className - 自定义类名 + * @param {Array} props.data - 聊天数据数组 + * @param {string} [props.type='chat'] - 聊天类型,默认为'chat' + * @param {string} props.text - 文本内容 + * @param {boolean} props.showTitle - 是否显示标题 + * @param {Function} props.itemShouldUpdate - 判断聊天项是否需要更新的回调函数 + * @param {Function} props.onMessageChange - 消息内容变化时的回调函数 + * @param {Function} props.renderMessages - 渲染消息内容的回调函数 + * @param {Function} props.renderErrorMessages - 渲染错误消息的回调函数 + * @param {string} props.loadingId - 正在加载的聊天项的ID + * @param {Function} props.renderItems - 渲染聊天项的回调函数 + * @param {boolean} props.enableHistoryCount - 是否启用历史记录计数 + * @param {Function} props.renderActions - 渲染操作按钮的回调函数 + * @param {number} [props.historyCount=0] - 历史记录计数 + * @param {Object} props.chatItemRenderConfig - 聊天项渲染配置 + * @returns {JSX.Element} 聊天列表组件 + */ const ChatList = memo( ({ onActionsClick, @@ -52,6 +76,7 @@ const ChatList = memo( const { cx, styles } = useStyles(); const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixClass = getPrefixCls('pro-chat'); + return (
{data.map((item, index) => { diff --git a/src/ProChat/demos/bigData.tsx b/src/ProChat/demos/bigData.tsx index 7074bf6d..ddd93b8b 100644 --- a/src/ProChat/demos/bigData.tsx +++ b/src/ProChat/demos/bigData.tsx @@ -5,6 +5,7 @@ import { ProChat } from '@ant-design/pro-chat'; import { useTheme } from 'antd-style'; +import { Alert } from 'antd'; import { MockResponse } from '../mocks/streamResponse'; const initialChats = new Array(100) @@ -26,6 +27,16 @@ const initialChats = new Array(100) {} as Record, ); +initialChats['chat-20'] = { + id: 'chat-20', + content: '这是一条通知消息' + 20, + role: 'notification', + updateAt: Date.now(), + createAt: Date.now(), +}; + +console.log(initialChats); + export default () => { const theme = useTheme(); @@ -64,6 +75,22 @@ export default () => { ); }, actionsRender: false, + render: (item, dom, defaultDom) => { + if (item?.originData?.role === 'notification') { + return ( +
+ +
+ ); + } + return defaultDom; + }, }} request={async (messages) => { const mockedData: string = `这是一段模拟的流式字符串数据。本次会话传入了${messages.length}条消息`; diff --git a/src/components/Avatar/index.tsx b/src/components/Avatar/index.tsx index e11c93ec..d8c65e33 100644 --- a/src/components/Avatar/index.tsx +++ b/src/components/Avatar/index.tsx @@ -31,6 +31,23 @@ export interface AvatarProps extends AntAvatarProps { title?: string; } +/** + * @module Avatar + * @description 头像组件,用于显示用户头像信息 + * @exports Avatar + * @component + * + * @param {string} [avatar] - 头像图片的URL或base64数据 + * @param {string} [background] - 头像的背景颜色 + * @param {'circle' | 'square'} [shape='circle'] - 头像的形状,默认为圆形 + * @param {number} [size=40] - 头像的尺寸(像素) + * @param {string} [title] - 如果未提供头像,则显示的标题文本 + * @param {string} [className] - 自定义的CSS类名 + * @param {Function} [onClick] - 点击头像时触发的回调函数 + * @param {Object} [style] - 自定义的行内样式 + * @param {Object} [props] - 其他传递给AntAvatar组件的属性 + * + */ const Avatar = memo( ({ className,