5.4 消息中心
5.4.1 parseMsg 组件
文档路径:
src\components\DataPreview\session\chat\components\parseMsg\index.tsxsrc\components\DataPreview\session\chat\components\parseMsg\index.module.lesssrc\components\DataPreview\session\chat\components\parseMsg\textParagraph.tsx
5.4.1.1 功能概述
该文件主要负责解析和渲染不同类型的消息内容,包括图片、视频、文件、语音、名片、动态、任务等消息类型。 它提供了以下三个核心函数:
parseMsg: 解析并渲染普通消息parseCiteMsg: 解析并渲染引用消息parseForwardMsg: 解析并渲染转发消息
5.4.1.2 parseMsg 结构
parseMsg 主要用于解析并渲染普通消息,根据消息类型动态生成对应的 JSX 元素。 其输入参数是 item: IMessage, 消息对象,包含消息的类型、内容、发送者等信息。 parseMsg 主要支持的消息类型有:
MessageType.Image图片消息MessageType.Video视频消息MessageType.File文件消息MessageType.Voice语音消息MessageType.NameCard名片消息MessageType.Dynamic动态消息MessageType.Task任务消息- 默认类型:文本消息或其他类型
其代码逻辑主要如下:
export const parseMsg = (item: IMessage): any => {
switch (item.msgType) {
case MessageType.Image: {
const img: FileItemShare = parseAvatar(item.msgBody);
if (img && img.shareLink) {
return (
<div
className="con_content_txt con_content_image"
onClick={() => command.emitter('executor', 'open', img)}>
<Image
className="image"
width={300}
src={shareOpenLink(img.shareLink)}
preview={false}
/>
</div>
);
}
return <div className="con_content_txt">消息异常</div>;
}
case MessageType.Video: {
const img: FileItemShare = parseAvatar(item.msgBody);
if (img && img.shareLink) {
return (
<div
className="con_content_txt"
onClick={() => command.emitter('executor', 'open', img)}>
{img?.thumbnail ? (
<Image width={300} src={img.thumbnail} preview={false} />
) : (
<div style={{ color: '#154ad8' }}>{img.name}</div>
)}
</div>
);
}
return <div className="con_content_txt">消息异常</div>;
}
// 其他类型处理...
}
};关键点
- 图片消息: 使用 Image 组件展示图片。 点击图片时触发
command.emitter打开图片。 - 视频消息: 如果有缩略图,展示缩略图;否则展示视频名称。 点击视频时触发
command.emitter打开视频。 - 文件消息: 展示文件名称、大小和操作按钮(在线预览、下载)。
- 语音消息: 使用
audio标签播放语音。 - 名片消息: 展示名片的头像、名称和备注。 点击名片时触发
command.emitter打开名片详情。 - 动态消息: 展示动态的内容、资源和发布者信息。
- 任务消息: 使用
TaskMsg组件渲染任务内容。 - 默认消息: 如果消息包含图片标记 $IMG,则解析并展示图片;如果消息是 URL,则使用 LinkPreview 组件展示链接预览。默认展示为纯文本消息。
注意事项
- 消息解析的健壮性:确保所有消息类型都能正确解析,避免出现“消息异常”的情况。
- 性能优化:使用
React.Fragment和Image.PreviewGroup优化图片渲染。 - 安全性:使用
dangerouslySetInnerHTML时,确保消息内容已被安全过滤,防止XSS攻击。 - 扩展性:如果需要支持新的消息类型,可以在
switch语句中添加对应的处理逻辑。
以下是如何使用 parseMsg 函数的示例:
import { parseMsg } from './parseMsg';
const MessageItem = ({ message }: { message: IMessage }) => {
return <div className="message-item">{parseMsg(message)}</div>;
};5.4.1.3 parseCiteMsg 结构
parseCiteMsg 用于解析并渲染引用消息,展示被引用的消息内容。 其输入参数是 item: IMessage, 消息对象,包含消息的类型、内容、发送者等信息。 parseMsg 主要支持的消息类型:
MessageType.Image:图片消息MessageType.File:文件消息MessageType.Voice:语音消息MessageType.NameCard:名片消息MessageType.Dynamic:动态消息MessageType.Task:任务消息- 默认类型:文本消息或其他类型
其代码逻辑主要如下:
export const parseCiteMsg = (item: IMessage): any => {
switch (item.msgType) {
case MessageType.Image: {
const img: FileItemShare = parseAvatar(item.msgBody);
if (img && img.thumbnail) {
return (
<div className="con_content_cite_txt">
<span>{item.from.name}:</span>
<Image
src={img.thumbnail}
preview={{ src: shareOpenLink(img.shareLink) }}
/>
</div>
);
}
return <div className="con_content_cite_txt">消息异常</div>;
}
// 其他类型处理...
}
};关键点
- 引用消息的展示:在消息内容前添加发送者的名称。
- 图片引用:展示图片的缩略图。点击图片时打开大图预览。
- 文件引用:展示文件的名称和大小。提供下载链接。
- 语音引用:使用
audio标签播放语音。 - 名片引用:展示名片的头像、名称和备注。
- 动态引用:展示动态的内容和资源。
- 任务引用:使用
TaskMsg组件渲染任务内容。 - 默认引用:如果消息包含图片标记 $IMG,则解析并展示图片。
- 默认展示为纯文本消息。
5.4.1.4 parseForwardMsg 结构
parseForwardMsg 主要用于解析并渲染转发消息,展示转发的消息内容。 其输入参数是 item: IMessage[] 用于转发的消息列表和 viewForward?: (item: IMessage[]) => void 查看转发消息的回调函数。
其代码逻辑主要如下:
export const parseForwardMsg = (
item: IMessage[],
viewForward?: (item: IMessage[]) => void,
) => {
let formName = Array.from(
new Set(item.map((msg: IMessage) => msg.from.name).filter((name: string) => name)),
);
let showName =
formName && formName.length > 2
? '群聊'
: `${formName[0]}${formName[1] ? '和' + formName[1] : ''}的`;
return (
<div
className="con_content_forward_txt"
onClick={() => viewForward && viewForward(item)}>
<div className="con_content_forward_session">{`${showName}会话消息`}</div>
{item.map((msg: IMessage, idx: number) => {
if (idx > 2) return;
switch (msg.msgType) {
case MessageType.Image: {
const img: FileItemShare = parseAvatar(msg.msgBody);
if (img)
return (
<div className="con_content_forward_msg">
{msg.from.name}:{img.name}
</div>
);
return <div className="con_content_forward_msg">消息异常</div>;
}
// 其他类型处理...
}
})}
</div>
);
};关键点
- 转发消息的展示:展示转发的会话名称(如“群聊”或“某某和某某的会话消息”)。
- 消息内容:默认展示前 3 条消息内容。支持图片、文件、语音、任务等类型的消息。
- 点击事件:点击转发消息时,触发 viewForward 回调函数,查看完整的转发消息。
5.4.1.5 TextParagraph 结构
TextParagraph 是一个用于展示文本消息的 React 组件。它支持以下功能:
- 自动检测文本内容的高度,判断是否需要折叠显示。
- 支持将文本中的链接解析为可点击的超链接。
- 提供“展开/收起”功能,方便用户查看完整的长文本内容。
其代码结构主要如下:
textParagraph/
├── 引入依赖
│ ├── React: 用于构建组件。
│ ├── useState, useEffect, useRef: React 钩子,用于管理状态和 DOM 引用。
│ ├── parseTolink: 工具函数,用于将文本中的链接解析为超链接。
│ ├── index.module.less: 样式文件。
├── 接口定义
│ └── Iprops: 定义组件的 props。
├── 核心组件
│ └── TextParagraph: 主组件。
└── 导出组件
└── export default TextParagraph该组件主要定义了组件的输入参数 props。
interface Iporps {
msgBody: string; // 消息的文本内容
}其中的 msgBody 是消息的文本内容,支持普通文本和包含链接的文本。
核心组件 TextParagraph 是用于渲染消息文本内容,自动检测文本高度,判断是否需要折叠显示, 支持“展开/收起”功能,将文本中的链接解析为可点击的超链接。
其参数有 props: Iprops 包含消息的文本内容。 另外包含状态 show: boolean 是否显示“展开”按钮,初始值为 false。 当文本高度超过 150px 时,显示“展开”按钮。 点击“展开”按钮后,显示完整文本并隐藏按钮。 除此之外,还有参数 ref: React.RefObject<any> 用于获取文本容器的 DOM 节点,检测其高度。
其核心逻辑主要以下几条:
文本高度检测:
使用
useEffect钩子在组件挂载后检测文本容器的高度。 如果高度超过 150px,则设置show状态为true,显示“展开”按钮。链接解析:
使用
parseTolink工具函数将文本中的链接解析为超链接。 通过dangerouslySetInnerHTML将解析后的 HTML 内容插入到文本容器中。展开/收起功能:
当文本高度超过 150px 时,显示“展开”按钮。 点击“展开”按钮后,显示完整文本并隐藏按钮。
其代码实现主要如下:
const TextParagraph = (props: Iporps) => {
const ref = useRef<any>(); // 引用文本容器
const [show, setShow] = useState(false); // 是否显示“展开”按钮
useEffect(() => {
if (ref.current) {
const textHeight = ref.current.clientHeight; // 获取文本容器高度
setShow(textHeight > 150 ? true : false); // 判断是否需要折叠
}
}, []);
return (
<div className="con_content_txt">
<div style={{ display: 'flex' }}>
<div
className={show ? styles.showText : ''} // 根据状态应用样式
dangerouslySetInnerHTML={{ __html: parseTolink(props.msgBody) }} // 插入解析后的 HTML
ref={ref} // 绑定引用
/>
{show ? (
<div className={styles.showLink}>
<a style={{ whiteSpace: 'nowrap' }} onClick={() => setShow(false)}>
展开
</a>
</div>
) : (
<></>
)}
</div>
</div>
);
};parseTolink 是一个工具函数 其功能是将文本中的链接解析为可点击的超链接。
就比如对于示例,输入:
"请访问 https://example.com 查看详情。"则会输出:
"请访问 <a href='https://example.com' target='_blank'>https://example.com</a> 查看详情。"其使用方式是
dangerouslySetInnerHTML={{ __html: parseTolink(props.msgBody) }}以下是如何使用 TextParagraph 组件的示例代码:
import React from 'react';
import TextParagraph from './textParagraph';
const Example = () => {
const longText = `
这是一个很长的文本消息,包含多个段落和链接。
请访问 https://example.com 查看详情。
这是第二段内容,继续访问 https://another-example.com。
`;
return (
<div>
<h3>消息展示</h3>
<TextParagraph msgBody={longText} />
</div>
);
};
export default Example;注意事项
性能优化:
useEffect钩子仅在组件挂载时执行一次,避免重复计算文本高度。 使用dangerouslySetInnerHTML时,确保输入的 HTML 已经过滤,防止 XSS 攻击。样式控制: 文本折叠的高度限制为 150px,可以根据需求在样式文件中调整。 确保
index.module.less文件中的样式类名与组件中的类名一致。链接解析:
parseTolink函数需要确保对所有可能的链接格式进行正确解析。 如果消息内容中包含特殊字符(如 HTML 标签),需要对其进行转义处理。兼容性: 组件依赖于现代浏览器的
dangerouslySetInnerHTML和ref功能,确保在支持这些特性的环境中运行。
组件交互流程
组件挂载: 使用
useRef获取文本容器的 DOM 节点。 使用useEffect检测文本高度,判断是否需要折叠显示。文本渲染: 使用
parseTolink将文本中的链接解析为超链接。 使用dangerouslySetInnerHTML将解析后的 HTML 插入到文本容器中。用户交互: 如果文本高度超过 150px,显示“展开”按钮。 用户点击“展开”按钮后,显示完整文本并隐藏按钮。
5.4.2 information 组件
文件路径:src\components\DataPreview\session\chat\GroupContent\information.tsx
5.4.2.1 功能概述
Information 是一个用于展示消息接收人列表的组件,支持以下功能:
- 已读/未读消息的分类展示:通过标签页切换查看已读和未读的接收人列表。
- 搜索过滤功能:支持通过输入关键字过滤接收人列表。
- 滚动加载:未读列表支持滚动加载更多接收人数据。
- 接收人信息展示:展示接收人的头像、名称、标签以及消息接收时间。
5.4.2.2 文件结构
information/
├── 引入依赖
│ ├── React: 用于构建组件。
│ ├── antd: 提供 Drawer、List、Tabs 等 UI 组件。
│ ├── orgCtrl: 用户和组织相关的控制器。
│ ├── ScrollList: 自定义滚动列表组件。
│ ├── showChatTime: 工具函数,用于格式化时间。
├── 核心组件
│ └── Information: 主组件。
├── 辅助函数
│ └── filterLables: 过滤接收人列表。
│ └── loadLabelItem: 渲染接收人列表项。
└── 导出组件
└── export default Information5.4.2.3 组件详解
接口定义
information组件接受以下两个参数:
const Information = ({ msg, onClose }: { msg: IMessage; onClose: Function }) => { ... }msg: IMessage:消息对象,包含消息的元数据、已读/未读接收人列表等信息。onClose: Function:关闭抽屉的回调函数。
核心组件
功能:
- 展示消息接收人列表,分为“已读”和“未读”两类。
- 支持通过搜索框过滤接收人列表。
- 支持滚动加载未读接收人数据。
状态:
tabsKey: string:当前选中的标签页的key。unreadInfo: IMessageLabel[]:未读接收人列表,初始值为msg.unreadInfo。
5.4.2.4 核心逻辑
- 标签过滤 通过
filterLables函数对接收人列表进行过滤,支持根据接收人名称、标签或编码进行匹配。
const filterLables = (labels: IMessageLabel[], filter: string) => {
if (filter === "") return labels;
return labels.filter((i) => {
if (i.label.includes(filter)) return true;
var entity = orgCtrl.user.findMetadata<schema.XTarget>(i.userId);
return entity && (entity.name.includes(filter) || entity.code.includes(filter));
});
};- 已读列表 通过
readList函数渲染已读接收人列表,支持搜索过滤。
const readList = () => {
const [filter, setFilter] = useState('');
return (
<ScrollList
loaded
searchValue={filter}
height="calc(100vh - 220px)"
setSearchValue={(v) => setFilter(v)}
data={filterLables(
msg.labels.filter((a) => a.designateId != msg.metadata.designateId),
filter
)}
renderItem={loadLabelItem}
/>
);
};- 未读列表 通过
unRead函数渲染未读接收人列表,支持搜索过滤和滚动加载。
const unRead = () => {
const [filter, setFilter] = useState('');
return (
<ScrollList
loaded
data={filterLables(unreadInfo, filter)}
searchValue={filter}
height="calc(100vh - 220px)"
setSearchValue={(v) => setFilter(v)}
renderItem={loadLabelItem}
onLoadMore={() => {
msg.chat.target.loadMembers().then(() => {
setUnreadInfo(msg.unreadInfo);
});
}}
/>
);
};- 接收人列表项 通过
loadLabelItem函数渲染接收人列表中的每一项,包括头像、名称、标签和接收时间。
const loadLabelItem = (item: IMessageLabel) => {
return (
<List.Item
style={{ cursor: 'pointer', padding: 6 }}
actions={
item.time.length > 0
? [<div key={item.time}>{showChatTime(item.time)}</div>]
: []
}>
<List.Item.Meta
avatar={<TeamIcon entityId={item.designateId} size={42} />}
title={<strong>{item.labeler.name}</strong>}
description={
<div style={{ lineHeight: '16px' }}>
<div className="ellipsis1">{item.label}</div>
</div>
}
/>
</List.Item>
);
};- 标签页配置 通过
items数组配置“已读”和“未读”标签页。
const items: TabsProps['items'] = [
{
key: 'unRead',
label: `未读(${msg.chat.memberCount - 1 - msg.readedIds.length})`,
children: unRead(),
},
{ key: 'read', label: `已读(${msg.readedIds.length})`, children: readList() },
];- 抽屉渲染 通过
Drawer组件渲染消息接收人列表的抽屉。
return (
<Drawer width={480} title={'消息接收人列表'} onClose={() => onClose()} closable open>
<Tabs
centered
items={items}
defaultActiveKey={'unRead'}
activeKey={tabsKey}
onChange={(e) => setTabsKey(e)}
/>
</Drawer>
);5.4.2.5 辅助函数
filterLables:过滤接收人列表,支持根据标签、名称或编码进行匹配。
loadLabelItem: 渲染接收人列表中的每一项,包括头像、名称、标签和接收时间。
5.4.2.6 使用示例
以下是如何使用 Information 组件的示例代码:
import React from 'react';
import Information from './information';
const Example = ({ msg }) => {
const handleClose = () => {
console.log('关闭抽屉');
};
return <Information msg={msg} onClose={handleClose} />;
};
export default Example;5.4.2.7 注意事项
性能优化: 使用
useState和useEffect管理状态,避免不必要的重复渲染。 滚动加载未读列表时,确保数据的增量加载逻辑正确。样式控制: 确保
ScrollList和List.Item的样式与整体 UI 风格一致。 使用ellipsis1类名控制标签的文本溢出效果。数据完整性: 确保
msg对象中包含完整的已读和未读接收人数据。 在滚动加载时,确保msg.chat.target.loadMembers方法能够正确更新unreadInfo。扩展性: 如果需要支持更多的接收人分类(如“已回复”),可以在
items数组中添加新的标签页配置。
5.4.2.8 组件交互流程
组件挂载: 初始化
unreadInfo状态为msg.unreadInfo。 默认选中“未读”标签页。标签页切换: 用户点击标签页时,更新
tabsKey状态,切换到对应的接收人列表。搜索过滤: 用户在搜索框中输入关键字时,调用
filterLables函数过滤接收人列表。滚动加载: 用户滚动到未读列表底部时,调用
msg.chat.target.loadMembers方法加载更多接收人数据。关闭抽屉: 用户点击关闭按钮时,调用
onClose回调函数关闭抽屉。