输入区扩展(@ / 表情 / 引用)
MessageInput 是受控输入区:宿主通过事件接收发送/上传/typing 意图,通过 setQuote/insertMention 注入引用与 @,通过 setDisabled 表达禁言态。@ 有原生 API;表情没有专用 API,按“向 textarea 插入文本并触发 input”实现;引用由 quote 随 send 事件回传。
概念
| 能力 | 真实入口 | 说明 |
|---|---|---|
| 发送 | on("send", {text,quote}) | Enter 发送、Shift+Enter 换行;输入法组合态不触发 |
| 引用 | setQuote(quote) / getQuote() | 引用条显示 senderId + 文本;随 send 回传 |
| @ 提及 | insertMention({all,userIds}) | 插入 @所有人/@userId 并 emit mentionChanged |
| 表情 | 无专用 API | 向 textarea 写值并 dispatchEvent(new Event("input")) |
| 草稿 | saveDraft/draftChanged | 宿主持久化;draft prop 恢复 |
| typing | typing 事件(debounce/idle) | 默认 500ms/3000ms,可配 |
| 图片/文件/语音/位置/已读 | pickImage/pickFile/pickLocation/readVisible | 由宿主上传并发送 |
mentionCandidates只用于内部候选展示;选择成员后由宿主调用insertMention。maxLength默认 5000;超长时发送按钮禁用并提示。setDisabled(true, true)会同时处理禁用与禁言文案。send事件只上报文本;图片/文件/语音由各自事件交给宿主。
前置条件
- 已按 快速开始 完成
init与connect,页面能收发消息。 - 使用
@ni-paas/im-web/imkit(浏览器原生 ESM,无第三方依赖);自定义代码只调用 IMKit/IMLib 公开入口。 - 已引入默认样式:调用
ensureStyles(),或自行加载 IMKit 样式表。 - 自托管版无“UI 主题商店/付费皮肤”;所有自定义均为本地代码(不适用项)。
步骤
- 挂载输入区
独立使用
new MessageInput({conversationType,targetId,...}),或直接用ChatView(内部已组合输入区)。 - 处理发送与引用
监听
send,把quote一并写入消息 content;发送成功后clear()。 - 接入 @ 选择器
点击
pickMention打开成员选择器;确认后调用insertMention({userIds})。 - 接入表情
在光标处插入表情字符,再触发 input 事件让组件同步高度与草稿。
- 保存草稿与 typing
监听
saveDraft/draftChanged与typing,调用 SDK 的草稿与正在输入接口。
参数表
| Props / 方法 / 事件 | 类型 | 说明 |
|---|---|---|
draft | string | 初始草稿 |
disabled / muted | boolean | 禁用/禁言态 |
mentionCandidates | Array | @ 候选成员 |
maxLength | number | 默认 5000 |
typingDebounceMs / typingIdleMs | number | 默认 500/3000 |
draftDebounceMs | number | 默认 600 |
setQuote(q) / getQuote() | 方法 | q={messageId,senderId,text,type} |
insertMention({all,userIds}) | 方法 | 插入 @ 文本 |
setHint(text) / setDisabled(d,m) | 方法 | 提示与状态 |
clear() / focus() | 方法 | 清空/聚焦 |
示例
完整输入区:引用 + @ + 表情 + 草稿
JavaScript
import { MessageInput, ensureStyles } from '@ni-paas/im-web/imkit';
ensureStyles();
const input = new MessageInput({
conversationType: 2,
targetId: 'g_demo',
placeholder: '输入消息,Enter 发送',
mentionCandidates: [
{ userId: 'alice', name: 'Alice' },
{ userId: 'bob', name: 'Bob' },
],
maxLength: 2000,
});
input.mount(document.getElementById('composer-host'));
// 1) 发送:quote 随事件回传
input.on('send', async ({ text, quote }) => {
const content = quote
? { type: 'text', text, quote: { messageId: quote.messageId } }
: { type: 'text', text };
const result = await client.sendMessage({ conversationType: 2, targetId: 'g_demo', content });
if (result.code === 0) input.clear();
});
// 2) 引用某条消息
function quoteMessage(message) {
input.setQuote({
messageId: message.messageId,
senderId: message.senderId,
text: message.content && message.content.text,
type: message.content && message.content.type,
});
}
// 3) @ 选择器确认后插入
document.getElementById('mention-picker').addEventListener('pick', (event) => {
input.insertMention({ userIds: [event.detail.userId] }); // 或 { all: true }
});
// 4) 表情:没有专用 API,按文本插入并同步组件
function insertEmoji(emoji) {
const textarea = document.querySelector('#composer textarea');
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
textarea.value = textarea.value.slice(0, start) + emoji + textarea.value.slice(end);
textarea.selectionStart = textarea.selectionEnd = start + emoji.length;
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.focus();
}
// 5) 草稿 / typing
input.on('saveDraft', ({ draft }) => client.saveTextMessageDraft(2, 'g_demo', draft));
input.on('typing', ({ typing }) => client.sendTypingStatus(2, 'g_demo', typing));
input.on('error', (err) => console.warn('composer error', err));用 ChatView 的快捷方法(推荐)
JavaScript
const chat = new ChatView({ conversationType: 1, targetId: 'bob', currentUserId: 'alice' });
chat.mount(document.getElementById('chat-host'));
// 引用 / @ / 提示 / 禁言态
chat.setQuote({ messageId: 'm_1', senderId: 'bob', text: '在吗?', type: 'text' });
chat.applyMention({ userIds: ['alice'] });
chat.setHint('图片上传中…');
chat.setComposerState({ disabled: false, muted: false });常见问题
回车发送在中文输入法下会误发吗?
不会。组件检查 event.isComposing;组合态下 Enter 不触发发送。
表情有内置面板吗?
没有。IMKit 只提供输入区;表情面板与字符集由宿主实现,按上例插入文本即可。
@ 后发送的 mentionedInfo 谁负责?
SDK/协议负责 mentionedUserIds/isMentionAll;insertMention 只负责文本与 mentionChanged,发送时请从事件或业务状态取。
引用会随消息发出去吗?
send 事件会带 quote;需要你在 content 里写入引用字段(如 content.quote),组件不自动拼装协议字段。
禁言时输入区什么样?
setDisabled(true, true) 会禁用发送并显示禁言提示;发送尝试会 emit error。