会话列表项自定义
ConversationListView 负责会话排序(置顶优先、更新时间倒序)、未读角标、草稿、@ 提醒、免打扰与选中态。它没有逐行 renderer 钩子;定制方式是选项开关、CSS 变量/类名覆盖、data-* 属性与 longpress 事件。需要完全自定义行结构时,应自行渲染列表并复用 Avatar/UnreadBadge/formatListTime。
概念
| 能力 | 选项 / 事件 | 说明 |
|---|---|---|
| 置顶 / 未读 / 草稿 / @ | options.showPinned/showUnread/showDraft/showMention | 默认全开 |
| 选中行 | activeTargetId + activeConversationType | 由宿主维护 |
| 点击 | select({conversation}) | 切换会话 |
| 长按/右键 | longpress({conversation}) | 600ms 或 contextmenu |
| 下拉刷新意图 | refresh() → refresh 事件 | 宿主重新拉取 |
| 空态/骨架 | loading、conversations=[] | 内置空态与骨架 |
- DOM:
.conv-list > .conv-item[data-key][data-conversation-type][data-target-id],内部含.conv-title/.conv-preview/.conv-time/.pin-mark/.mute-mark/.mention-badge。 - 未读角标复用
UnreadBadge;免打扰时隐藏红点、保留 @ 角标。 - 没有任何
renderItem/rowRenderer参数;不要按不存在的 API 接入。
完全自定义的做法
用 SDK 的 getConversationList() 取数据,自己渲染 .conv-item,复用 Avatar/UnreadBadge/formatListTime/messagePreview 保持视觉一致,再自行处理选中与长按。
前置条件
- 已按 快速开始 完成
init与connect,页面能收发消息。 - 使用
@ni-paas/im-web/imkit(浏览器原生 ESM,无第三方依赖);自定义代码只调用 IMKit/IMLib 公开入口。 - 已引入默认样式:调用
ensureStyles(),或自行加载 IMKit 样式表。 - 自托管版无“UI 主题商店/付费皮肤”;所有自定义均为本地代码(不适用项)。
步骤
- 准备会话模型
每个会话至少包含
conversationType、targetId、targetName、updatedAt、unreadCount、lastMessage;可选isPinned/draft/mentionedCount/notificationLevel。 - 挂载列表
new ConversationListView({conversations, activeTargetId, activeConversationType});切换会话时update()或重建。 - 处理交互
select切聊天;longpress弹菜单(置顶/免打扰/删除);refresh重新拉取。 - 覆盖样式
用 CSS 变量与类名覆盖间距/字号/选中背景;不要改
data-*语义。
参数表
| Props / 方法 / 事件 | 类型 | 说明 |
|---|---|---|
conversations | Array | 会话数组 |
activeTargetId / activeConversationType | string / number | 选中态 |
options | object | 四个 show* 开关 |
loading / theme / locale | boolean / Theme / string | 骨架、主题、语言 |
refresh() / setActive(type,id) | 方法 | 触发刷新 / 设置选中 |
select / longpress / refresh | 事件 | 宿主交互入口 |
示例
会话列表 + 选中切换 + 长按菜单
JavaScript
import { ConversationListView, Theme, ensureStyles } from '@ni-paas/im-web/imkit';
ensureStyles();
const list = new ConversationListView({
conversations: [
{ conversationType: 1, targetId: 'bob', targetName: 'Bob', updatedAt: Date.now(), unreadCount: 3 },
{ conversationType: 2, targetId: 'g_demo', targetName: '项目群', updatedAt: Date.now() - 60000, unreadCount: 128, mentionedCount: 2 },
],
activeTargetId: 'bob',
activeConversationType: 1,
options: { showPinned: true, showUnread: true, showDraft: true, showMention: true },
theme: Theme.light({ background: '#f8fafc' }),
});
list.mount(document.getElementById('conv-host'));
list.on('select', ({ conversation }) => {
list.setActive(conversation.conversationType, conversation.targetId);
openChat(conversation);
});
list.on('longpress', ({ conversation }) => {
openContextMenu(conversation); // 置顶 / 免打扰 / 删除 / 标记已读
});
list.on('refresh', async () => {
const result = await client.getConversationList();
if (result.code === 0) list.update({ conversations: result.data });
});只覆盖列表项样式
JavaScript
/* 选中行背景与未读角标颜色 */
#conv-host .conv-item.active {
background: color-mix(in srgb, var(--imkit-primary) 12%, transparent);
}
#conv-host .conv-item .conv-title {
font-weight: 600;
}
#conv-host .conv-item[data-conversation-type="2"] .conv-title::after {
content: '群';
margin-left: 6px;
font-size: 11px;
color: var(--imkit-text-secondary);
}常见问题
能自定义每一行的 DOM 吗?
没有行渲染器。请用 CSS/选项,或自渲染列表并复用 IMKit 的格式化与小组件。
未读 99+ 是怎么来的?
unreadBadgeText 规则:0 隐藏,>99 显示 99+;免打扰时隐藏红点、保留 @。
置顶排序要自己做吗?
不用。组件按 isPinned 优先、updatedAt 倒序排序。
为什么长按没触发?
需要 600ms 按住或右键(contextmenu);触屏环境请确认没有阻止默认手势。