互动消息
聊天消息收发、历史消息、消息撤回、已读管理等 IM 功能。
API 参考
fetchHistoryMessages()
⚠️ 业务开发者通常无需调用本方法。 进房时 SDK 已自动加载课堂历史; 业务层使用消息列表请优先使用以下三个 API:
loadOlderMessages(count)— UI 滚动到顶部时向上分页loadNewerMessages(count)— UI 滚动到底部时向下渐进分页seekToLatestMessages()— 一键回到最新("↓ N 条新消息"按钮)
⚠️ Application developers normally do NOT need to call this method. The SDK auto-loads classroom history on join. For day-to-day list interaction, prefer the three sibling APIs:
loadOlderMessages(count)— backward pagination on scroll-to-toploadNewerMessages(count)— progressive forward pagination on scroll-to-bottomseekToLatestMessages()— one-shot jump-to-latest ("↓ N new messages" button)
── 以下为高级/兜底用途说明(业务侧基本不会用到) ──
拉取课堂完整历史消息(进房历史,调用课堂后台 record/getClassMessage 接口)。 record/getClassMessage backend API).
进房时 SDK 会自动调用一次(fire-and-forget,不阻断进房流程),通常无需外部主动调用; 如需手动重拉/刷新(例如自动调用失败后的兜底入口),可显式调用本方法。
The SDK invokes this automatically once during joinClass (fire-and-forget; does not block the join flow), so external code rarely needs to call it. Use it as a manual refresh / retry entry point when the auto-call failed.
与 loadOlderMessages / loadNewerMessages 的区别:
fetchHistoryMessages()走课堂后台 HTTP,覆盖进房前服务端持久化的全部历史loadOlderMessages(count)走 TIM SDKgetMessageListHopping(direction=0),按 sequence 向上分页loadNewerMessages(count)走 TIM SDKgetMessageListHopping(direction=1),按 sequence 向下分页
Difference vs. loadOlderMessages / loadNewerMessages:
fetchHistoryMessages()uses the classroom HTTP backend, returning the full server-persisted history that existed before the user joined.loadOlderMessages(count)uses TIM SDKgetMessageListHopping(direction=0) for sequence-based backward pagination.loadNewerMessages(count)uses TIM SDKgetMessageListHopping(direction=1) for sequence-based forward pagination.
AV 群(AVChatRoom)不记录 TIM 历史消息,进房前的内容只能通过本接口拉取。
消息列表内置 500 条上限(IM_MSG_LIST_MAX_SIZE),后端返回过多时自动截断为最新一页; 截断的更老消息可后续通过 loadOlderMessages 按需补拉。
The list is capped at 500 messages; if the backend returns more, only the latest page is kept. Trimmed older messages can be paged in later via loadOlderMessages.
返回值: Promise<TResult<Message[]>>
示例:
// 自动重试:进房后若 messageList$ 仍为空,主动重拉一次
setTimeout(async () => {
if (classroom.state.messageList$.get().length === 0) {
const res = await classroom.fetchHistoryMessages();
console.log('补拉到', res.ok ? res.data.length : 0, '条历史消息');
}
}, 3000);loadNewerMessages()
向下分页加载更新的消息(TIM SDK getMessageListHopping,direction=1,UI 滚动到底部 / 用户从历史返回当前时调用)。
使用场景:用户调用 loadOlderMessages 多次后,state.messageList$ 末尾停留在过去某个 seq; 期间又有实时消息因 500 条上限被裁掉,本方法把这段缺失的更新消息补回末尾。
Use case: after multiple loadOlderMessages calls the list tail sits at an older seq; meanwhile real-time messages were trimmed due to the 500-entry cap. This method fills the gap by appending newer messages to the list tail.
实现:调用 TIM getMessageListHopping,pivot 取 state.messageList$ 末尾最新消息的 seq, direction=1 表示拉更新的消息;返回结果按 seq 升序 + 与已有列表防御性 seq 去重后 append 到末尾; append 模式可能触发 IM_MSG_LIST_MAX_SIZE 裁剪(裁掉头部最老消息)。
Implementation: calls TIM getMessageListHopping with direction=1 using the tail (newest) message seq of state.messageList$ as the pivot; returned messages are sorted by seq ASC, deduped against the current list by seq, then appended to the tail. Append mode may trigger trimming of the oldest messages (IM_MSG_LIST_MAX_SIZE).
注意:messageList$ 为空时无 seq 锚点,返回 tok([])。
对齐腾讯云 IM Web SDK 文档:https://cloud.tencent.com/document/product/269/75322
| 参数 | 类型 | 说明 |
|---|---|---|
| count? | number | 本次拉取条数(默认 15,TIM 单次最多 15) |
返回值: Promise<TResult<Message[]>>
示例:
// 滚动到底部时回追到最新 / Catch up to the present on scroll-to-bottom
msgListEl.addEventListener('scroll', async () => {
const atBottom = msgListEl.scrollTop + msgListEl.clientHeight >= msgListEl.scrollHeight - 2;
if (atBottom) {
const more = await classroom.loadNewerMessages(15);
if (more.ok && more.data.length === 0) console.log('已是最新');
}
});loadOlderMessages()
向上分页加载更早的消息(TIM SDK getMessageListHopping,direction=0,UI 滚动到顶部时调用)。
实现:调用 TIM getMessageListHopping,pivot 取 state.messageList$ 头部最老消息的 seq, direction=0 表示拉更旧的消息;返回结果按 seq 升序 + 与已有列表防御性 seq 去重后 prepend 到头部。
Implementation: calls TIM getMessageListHopping with direction=0 using the head (oldest) message seq of state.messageList$ as the pivot; returned messages are sorted by seq ASC, deduped against the current list by seq, then prepended to the head.
注意:messageList$ 为空时无 seq 锚点,返回 tok([]) 并提示需先调用 fetchHistoryMessages()。 should call fetchHistoryMessages() first to populate the initial history.
与 fetchHistoryMessages() 的区别:本方法走 TIM SDK 分页,后者走课堂后台 HTTP(一次性)。 fetchHistoryMessages() uses the classroom HTTP backend (one-shot).
消息列表内置 500 条上限,本方法 prepend 时不强制裁剪——允许用户主动扩窗加载更老内容; 后续实时消息追加(append 模式)会逐步把超额的老消息挤出。
The list is capped at 500 entries; this method prepends without forced trimming so users can intentionally widen the view. Subsequent real-time appends gradually displace the over-cap older messages.
对齐腾讯云 IM Web SDK 文档:https://cloud.tencent.com/document/product/269/75322
| 参数 | 类型 | 说明 |
|---|---|---|
| count? | number | 本次拉取条数(默认 15,TIM 单次最多 15) |
返回值: Promise<TResult<Message[]>>
示例:
// 滚动到顶部时分页加载 / Page in older messages on scroll-to-top
msgListEl.addEventListener('scroll', async () => {
if (msgListEl.scrollTop === 0) {
const more = await classroom.loadOlderMessages(15);
if (more.ok && more.data.length === 0) console.log('已到最顶');
}
});markAllMessagesAsRead()
标记所有消息已读,将未读计数归零。
同步更新 state.messageUnreadCount$ 为 0。
返回值: void
示例:
// 用户切换到聊天面板时调用 / Call when user switches to chat panel
onActivateChatPanel(() => classroom.markAllMessagesAsRead());markMessageAsRead()
标记指定序列号以前的消息为已读。
用于聊天列表滚动到某条消息时增量更新已读状态。
| 参数 | 类型 | 说明 |
|---|---|---|
| seq | number | 消息序列号(Message.seq 字段) |
返回值: void
示例:
// 滚动到底部时标记最新消息已读 / Mark latest as read when scrolled to bottom
onScrollToBottom(() => {
const last = classroom.state.messageList$.get().at(-1);
if (last) classroom.markMessageAsRead(last.seq);
});revokeMessage()
撤回消息(按消息 ID 撤回自己的消息)。
老师/助教如需撤回他人消息,请使用 revokeClassMessage(messageSeq)(按 IM seq 撤回,需老师/助教权限)。
| 参数 | 类型 | 说明 |
|---|---|---|
| messageId | string | 消息 ID(Message.id 字段,字符串) |
返回值: Promise<TResult>
示例:
// 撤回自己的消息 / Revoke one's own message
await classroom.revokeMessage(message.id);
// 老师撤回他人消息 / Teacher revoking another's message
await classroom.revokeClassMessage(message.seq);seekToLatestMessages()
一键回到最新消息(用户从历史浏览模式返回时使用)。
用途:用户向上 loadOlderMessages 翻阅一段历史后,想直接返回"实时尾", 不再渐进 loadNewerMessages。本方法循环调用 TIM SDK getMessageList, 累积最新约 100 条消息并整批替换 state.messageList$。
Use case: after scrolling up via loadOlderMessages for a while, the user wants to jump straight back to the live tail without progressively calling loadNewerMessages. This method loops getMessageList to gather the latest ~100 messages and replacesstate.messageList$ in a single batch.
状态:
state.hasNewerMessages$→ falsestate.newerMessagesCount$→ 0state.hasOlderMessages$→ 仅当服务端确认无更早消息时置 false,否则保持/置 true
State updates:
state.hasNewerMessages$→ falsestate.newerMessagesCount$→ 0state.hasOlderMessages$→ set to false only when the server confirms no older messages exist; otherwise kept/set to true.
与其它历史 API 的对比:
fetchHistoryMessages()— 课堂后台 HTTP,进房历史loadOlderMessages(count)— TIM 向上分页loadNewerMessages(count)— TIM 向下分页(渐进)seekToLatestMessages()— TIM 向后整批拉取,replace,固定 100 条
Comparison with other history APIs:
fetchHistoryMessages()— classroom HTTP backend (initial join history)loadOlderMessages(count)— TIM upward paginationloadNewerMessages(count)— TIM downward pagination (progressive)seekToLatestMessages()— TIM batch fetch latest, replace, fixed 100
返回值: Promise<TResult<Message[]>>
示例:
// 用户点击"↓ N 条新消息"按钮 / User taps the "↓ N new messages" button
if (classroom.state.hasNewerMessages$.get()) {
const r = await classroom.seekToLatestMessages();
if (r.ok) scrollChatToBottom();
}sendCustomMessage()
发送自定义消息到群聊(content 会被序列化为 JSON 字符串,用于业务自定义扩展)。
接收端通过 TEvent.RECV_CUSTOM_IM_MSG 事件收到原始数据,业务可自定义协议结构(如点赞动效、互动指令等)。
| 参数 | 类型 | 说明 |
|---|---|---|
| data | object | 自定义消息数据对象,将被 JSON.stringify 序列化 |
返回值: Promise<TResult>
示例:
// 发送 / Send
await classroom.sendCustomMessage({ type: 'reaction', emoji: '👏' });
// 接收 / Receive
classroom.on(TEvent.RECV_CUSTOM_IM_MSG, ({ data, fromUserId }) => {
const payload = JSON.parse(data); // { type: 'reaction', emoji: '👏' }
if (payload.type === 'reaction') showReactionAnimation(payload.emoji);
});sendDirectedMessage()
发送定向私聊消息,仅指定用户可见,不出现在群聊消息列表中。
接收端在 state.messageList$ 中可见(带 toUserId 字段),其他成员看不到该消息。
| 参数 | 类型 | 说明 |
|---|---|---|
| toUserId | string | 目标用户 ID |
| text | string | 消息文本 |
返回值: Promise<TResult>
示例:
// 老师私下提示某位学生 / Teacher privately reminds a student
await classroom.sendDirectedMessage('student_001', '请准备回答下一个问题');sendFileMessage()
发送文件消息到群聊(支持任意格式文件)。
与 uploadCourseware() 的区别:本方法只发送文件链接到聊天,不进入课件库、不触发服务端转码。
| 参数 | 类型 | 说明 |
|---|---|---|
| file | File | 文件对象 |
返回值: Promise<TResult>
示例:
const result = await classroom.sendFileMessage(file);
if (!result.ok) toast.error(result.message);sendImageMessage()
发送图片消息到群聊(支持 jpg/png/gif 等常见图片格式)。
SDK 内部完成 COS 上传 + 缩略图生成 + 群聊广播;上传进度暂不暴露。
| 参数 | 类型 | 说明 |
|---|---|---|
| file | File | 图片文件对象(通常来自 <input type="file"> 或拖拽事件) |
返回值: Promise<TResult>
示例:
// 通过 input 上传 / Upload via input
const file = (document.querySelector('input[type=file]') as HTMLInputElement).files![0];
const result = await classroom.sendImageMessage(file);
if (!result.ok) toast.error(result.message);sendTextMessage()
发送群聊文本消息,所有课堂参与者可见。
| 参数 | 类型 | 说明 |
|---|---|---|
| text | string | 消息文本内容 |
返回值: Promise<TResult>
示例:
const result = await classroom.sendTextMessage('大家好!');
if (!result.ok) toast.error(result.message);