注册
web

IM 聊天组件

IM 消息通常分为文本、图片、文件等 3 类,会对应不同的展示


im_3.png


传入参数


自定义内容:标题(title)、内容(children)、底部(footer)


弹框组件显隐控制:


一般通过一个变量控制显示或隐藏(visible);


并且暴露出一个事件,控制该变量(setVisible)


interface iProps {
title?: string // 标题
maskClose?: boolean // 点击 x 或 mask 回调
visible?: boolean // 是否显示
setVisible: (args) => void // 设置是否显示
children?: React.ReactNode | Array<React.ReactNode> // 自定义内容
footer?: React.ReactNode | Array<React.ReactNode> // 自定义底部
}

基础结构


IM 聊天组件基础结构包含:头部、内容区、尾部


function wsDialog(prop: iProps) {
const wsContentRef = useRef(null); // 消息区
const { title = "消息", maskClose, visible, setVisible } = prop; // 传入参数
const [message, setMessage] = useState(""); // 当前消息
const imMessage = useSelector(
(state: rootState) => state.mediaReducer.imMessage
); // 消息列表 全局管理

return (
<Modal
className={styles.ws_modal}
visible={visible}
transparent
onClose={handleMaskClose}
popup
animationType="slide-up"
>

<div className={styles.ws_modal_widget}>
{/* 头部 */}
<div className={styles.ws_header}></div>
{/* 内容区 */}
<div ref={wsContentRef} className={styles.ws_content}></div>
{/* 尾部区域 */}
<div className={styles.ws_footer}></div>
</div>
</Modal>

);
}

头部区


头部区域主要展示标题和关闭图标


标题内容可以自定义


不仅可以点击“右上角关闭图标”进行关闭


也可以通过点击“遮罩”进行关闭


// 头部关闭事件
function handleClose() {
slLog.log("[wsDialog]点击了关闭按钮");
setVisible(false);
}

// 弹框遮罩关闭事件
function handleMaskClose() {
if (maskClose) {
slLog.log("[wsDialog]点击了遮罩关闭");
setVisible(false);
}
}

// 头部区域
<div className={styles.ws_header}>
<div>{title}</div>
<div className={styles.ws_header_close} onClick={handleClose}>
<Icon type="cross" color="#999" size="lg" />
</div>

</div>;

内容区


消息内容分类展示:



  1. 文本:直接展示内容
  2. 图片:通过 a 标签包裹展示,可以在新标签页中打开,通过target="_blank"控制
  3. 文件:不同类型文件展示不同的图标,包括 zip、rar、doc、docx、xls、xlsx、pdf、txt 等;文件还可以进行下载

<div ref={wsContentRef} className={styles.ws_content}>
{imMessage &&
imMessage.length &&
imMessage.map((o, index) => {
return (
<div
key={index}
className={`${styles.item} ${
o.category === "send" ? styles.self_item : ""
}`}
>

<div className={styles.title}>{o.showName + " " + o.showNum}</div>
{/* 消息为图片 */}
{o.desc === "img" ? (
<a
className={`${styles.desc} ${styles.desc_image}`}
href={o.fileUrl}
title={o.fileName}
target="_blank"
>

<img src={o.fileUrl} />
</a>
) : o.desc === "file" ? (
// 消息为文件
<div className={`${styles.desc} ${styles.desc_file}`}>
<img
className={styles.file_icon}
src={handleSuffix(o.fileSuffix)}
/>

<div className={styles.file_content}>
<a title={o.fileName}>{o.fileName}</a>
<div>{o.fileSize}</div>
</div>
<img
className={styles.down_icon}
src={downIcon}
onClick={() =>
handleDownload(o)}
/>
</div>
) : (
// 消息为文本
<div className={`${styles.desc} ${styles.desc_message}`}>
{o.message}
</div>
)}
</div>

);
})}
</div>

文件下载通过 a 标签模拟实现


// 下载文件
function handleDownload(o) {
slLog.log("[SLIM]下载消息文件", o.fileUrl);
const a = document.createElement("a");
a.href = o.fileUrl;
a.download = o.fileName;
document.body.appendChild(a);
a.target = "_blank";
a.click();
a.remove();
}

监听消息内容,自动滚动到最底部处理


useEffect(() => {
if (visible && imMessage && imMessage.length) {
// 滚动到底部
wsContentRef.current.scrollTop = wsContentRef.current.scrollHeight;
}
}, [visible, imMessage]);

尾部区


主要是操作区,用于展示和发送文本、图片、文件等消息。


图片和文件通过原生input实现,通过accept属性控制文件类型


<div className={styles.ws_footer}>
<div className={styles.tools_panel}>
{/* 上传图片 */}
<div className={styles.tool}>
<img src={imageIcon} />
<input type="file" accept="image/*" onChange={handleChange("img")} />
</div>
{/* 上传文件 */}
<div className={styles.tool}>
<img src={fileIcon} />
<input
type="file"
accept=".doc,.docx,.pdf,.txt,.xls,.xlsx,.zip,.rar"
onChange={handleChange("file")}
/>

</div>
</div>

<div className={styles.input_panel}>
{/* 输入框,上传文本 */}
<input
placeholder="输入文本"
value={message}
onChange={handleInputChange}
className={`${styles.message} ${styles.mMessage}`}
onKeyUp={handleKeyUp}
/>

{/* 消息发送按钮 */}
<div onClick={handleMessage} className={styles.btn}>
发送
</div>
</div>

</div>

获取图片、文件信息:


// 消息处理
function handleChange(type) {
return (ev) => {
switch (type) {
case "img":
case "file":
msgObj.type = type === "img" ? 4 : 7;
const e = window.event || ev;
const files = e.target.files || e.dataTransfer.files;
const file = files[0];
msgObj.content = file;
break;
}
};
}

实现回车键发送消息:


通过输入框,发送文本消息时,一般需要监听回车事件(onKeyUp 事件中的 event.keyCode 为 13),也能发送消息


// 回车事件
function handleKeyUp(event) {
const value = event.target.value;
if (event.keyCode === 13) {
slLog.log("[wsDialog]onKeyUp", value, event.keyCode);
handleInputChange(event);
handleMessage();
}
}

组件封装


组件级别:公司级、系统级、业务级


组件封装优势:



  1. 提升开发效率,组件化、统一化管理
  2. 考虑发布成 npm 形式,远程发布通用

组件封装考虑点:



  1. 组件的分层和分治
  2. 设置扩展性(合理预留插槽)
  3. 兼容性考虑(向下兼容)
  4. 使用对象考虑
  5. 适用范围考虑

组件封装步骤:



  1. 建立组件的模板:基础架子,UI 样式,基本逻辑
  2. 定义数据输入:分析逻辑,定义 props 里面的数据、类型
  3. 定义数据输出:根据组件逻辑,定义要暴露出来的方法,$emit 实现等
  4. 完成组件内部的逻辑,考虑扩展性和维护性
  5. 编写详细的说明文档

作者:时光足迹
来源:juejin.cn/post/7249286405025022009

0 个评论

要回复文章请先登录注册