mdm-im-v3
基于 OpenIM SDK 的 Vue 3 即时通讯组件库,提供会话列表、单聊/群聊消息、文件收发、入群审批等功能。同时支持 PC 端(Element-Plus)和 H5 移动端(Vant 4),打包后通过 npm 分发,由同一包提供双端组件。
技术栈
- Node 18+(推荐 Node 20+ LTS)
- Vue 3 + Pinia + vue-router
- OpenIM client-sdk(
@openim/[email protected]) 技术文档 - PC 端:Element-Plus / H5 端:Vant 4
npm 包目录结构
安装 mdm-im-v3 后,node_modules 中的包目录如下:
node_modules/mdm-im-v3/
├── dist/ # PC 端构建产物
│ ├── index.es.js # 入口(导出 mdmImV3 组件)
│ ├── core.es.js # 入口(导出 useMessage + useChatStore)
│ ├── style/index.css # 样式文件
│ ├── index-*.js # 内部代码分割文件(Vite 自动分包)
│ ├── wangeditor-*.js # 富文本编辑器分包
│ ├── vue-office-*.js # 文档预览分包
│ └── assets/ # 图片/字体等资源文件
├── dist-h5/ # H5 端构建产物
│ ├── index.es.js # 入口(导出 mdmImV3H5 组件,内部自带路由)
│ ├── style/index-h5.css # 样式文件
│ ├── *H5-*.js # 按路由拆分的页面代码
│ └── assets/ # 图片/字体等资源文件
├── package.json # package exports 定义导入路径映射
└── README.md
导入路径映射
package.json 的 exports 字段定义了以下导入路径,消费方使用路径名导入即可,无需关心内部目录结构:
| 导入路径 | 对应文件 | 导出内容 | 用途 |
|---|---|---|---|
mdm-im-v3 | dist/index.es.js | mdmImV3 | PC 端主组件 |
mdm-im-v3/core | dist/core.es.js | useMessage, useChatStore | IM 登录/通信核心(PC/H5 通用) |
mdm-im-v3/h5 | dist-h5/index.es.js | mdmImV3H5 | H5 端主组件(内部自带路由) |
mdm-im-v3/style/index.css | dist/style/index.css | CSS | PC 端样式文件 |
mdm-im-v3/style/index-h5.css | dist-h5/style/index-h5.css | CSS | H5 端样式文件 |
核心原则:
mdm-im-v3/core在两端通用,PC 端额外导入mdm-im-v3(组件)和mdm-im-v3/style/index.css(样式),H5 端额外导入mdm-im-v3/h5(组件)和mdm-im-v3/style/index-h5.css(样式)。图标字体已内置: 两个样式文件都包含
@font-face与全部.mdm-icon-*类(字体文件随包输出到dist/assets/extracted/、dist-h5/assets/extracted/),消费方无需再单独引入iconfont.css,也不依赖宿主是否已有同名图标字体。
安装
pnpm i mdm-im-v3@latest -S
安装 peer 依赖
PC 端 需安装以下依赖(选择你自己的包管理器):
pnpm i vue@^3.5 element-plus@^2.9 @element-plus/icons-vue@^2.3 pinia@^3.0 pinia-plugin-persist@^1.0 vue-router@^4.5 axios@^1.8 sass@^1.86 -S
H5 端 同样需要安装 Vue 等基础依赖,但无需安装 element-plus,改为安装 Vant:
# H5 端需要的基础依赖(与 PC 端共享 pinia、vue-router、sass 等)
pnpm i vue@^3.5 pinia@^3.0 pinia-plugin-persist@^1.0 vue-router@^4.5 axios@^1.8 sass@^1.86 -S
# H5 端额外需要 Vant
pnpm i vant@^4.10 -S
H5 端不需要安装
element-plus和@element-plus/icons-vue。
PC 端使用
方式一:autoLogin(推荐)
通过组件暴露的 autoLogin 方法一键完成初始化、登录和会话加载:
<template>
<mdm-im-v3 ref="chatRef" theme="dark" />
</template>
<script setup>
import { ref } from 'vue'
import 'mdm-im-v3/style/index.css'
import { mdmImV3 } from 'mdm-im-v3'
const chatRef = ref()
const handleLogin = () => {
chatRef.value.autoLogin({
loginCode: 'your-account', // 用户登录账号,必填
userName: 'your-name', // 用户名
imToken: 'your-im-token', // OpenIM 用户令牌,必填
satoken: 'your-system-token',// 系统 token(上传文件等接口使用)
apiUrl: 'https://your-api-url:8443', // IM api 地址,必填
platformID: 5, // IM 平台标识,可选,5-网页端
})
}
</script>
autoLogin 内部自动完成:store 写入用户信息 → 初始化 IM SDK → 登录 → 轮询登录状态 → 状态变为已登录后拉取会话列表。
方式二:手动控制登录流程(分步加载)
适用于先登录 IM 获取未读数 → 再切换到 IM 页面展示内容的场景:
完整说明与可运行示例见 获取即时消息组件mdm-im-v3的未读消息数量的调用方式.md。
- 入口处调用
useMessage(config)初始化 IM,获取未读数量 - 用户切换到 IM 菜单时再渲染
<mdm-im-v3>组件 - 组件自动读取 store 中的已登录状态和
window.imInstance,无需重复初始化
// ========== 步骤1:应用入口处登录 IM ==========
import { watch } from 'vue'
import { useMessage } from 'mdm-im-v3/core'
const { initIMSDK, getUnReadMessageCount, loginStatus, messageStore } = useMessage({
userID: 'your-account', // 用户 ID,必填
nickname: 'your-name', // 用户昵称
faceURL: '', // 用户头像
imToken: 'your-im-token', // OpenIM 用户令牌,必填
satoken: 'your-system-token', // 系统 token
apiUrl: 'https://your-api-url:8443', // IM API 地址,必填
wsUrl: 'wss://your-ws-url:8443', // IM WebSocket 地址
tenantID: '', // 租户 ID
platformID: 5, // 平台标识,5-网页端
})
// 初始化并登录
await initIMSDK()
// 监控 IM 登录状态,登录成功后获取未读数
watch(loginStatus, status => {
if (status === 3) {
getUnReadMessageCount(true)
}
})
<!-- ========== 步骤2:切换菜单后渲染 PC 组件 ========== -->
<template>
<mdm-im-v3 ref="chatRef" theme="dark" />
</template>
<script setup>
import { ref } from 'vue'
import 'mdm-im-v3/style/index.css'
import { mdmImV3 } from 'mdm-im-v3'
// 组件会自动读取 store 和 window.imInstance,无需再次初始化
const chatRef = ref()
</script>
原理:
useMessage()内部自动将配置存入 Pinia store(useChatStore),initIMSDK()创建的 IM 实例挂载到window.imInstance。 组件挂载时检测到loginStatus === 3(已登录),直接复用已有的 IM 实例和会话数据,无需二次登录。
H5 移动端使用
本库同时提供 H5 移动端组件,基于 Vant 4 UI 库构建,采用独立全屏页面路由(区别于 PC 端左右分栏布局)。H5 端从 mdm-im-v3/h5 路径导入组件,与 PC 端一样,只需要一个入口组件,无需暴露路由。
前置准备
H5 端需要安装 Vant 并在入口文件中注册:
pnpm i vant@^4.10 -S
入口文件(main.js)
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import PiniaPersist from 'pinia-plugin-persist'
import Vant from 'vant'
import 'vant/lib/index.css'
import 'mdm-im-v3/style/index-h5.css'
import { mdmImV3H5 } from 'mdm-im-v3/h5'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
pinia.use(PiniaPersist)
app.use(pinia)
app.use(Vant)
app.mount('#app')
注意: H5 路由使用
createWebHashHistory,共有 11 个页面路径,全部内聚在mdmImV3H5组件内部,消费方无需注册路由,只需在模板中放置<mdm-im-v3-h5>标签即可。
根组件(App.vue)
<template>
<mdm-im-v3-h5 ref="h5Ref" theme="light" />
</template>
<script setup>
import { ref } from 'vue'
import { mdmImV3H5 } from 'mdm-im-v3/h5'
const h5Ref = ref()
</script>
方式一:autoLogin(推荐)
通过组件暴露的 autoLogin 方法一键完成初始化、登录和会话加载,与 PC 端完全一致:
<template>
<mdm-im-v3-h5 ref="h5Ref" theme="light" />
</template>
<script setup>
import { ref } from 'vue'
import { mdmImV3H5 } from 'mdm-im-v3/h5'
const h5Ref = ref()
const handleLogin = () => {
h5Ref.value.autoLogin({
loginCode: 'your-account', // 用户登录账号,必填
userName: 'your-name', // 用户名
imToken: 'your-im-token', // OpenIM 用户令牌,必填
satoken: 'your-system-token',// 系统 token(上传文件等接口使用)
apiUrl: 'https://your-api-url:8443', // IM api 地址,必填
platformID: 5, // IM 平台标识,可选,5-网页端
})
}
</script>
autoLogin 内部自动完成:store 写入用户信息 → 初始化 IM SDK → 登录 → 轮询登录状态 → 状态变为已登录后自动跳转到会话列表页。
方式二:分步加载(手动控制登录流程)
适用于先登录 IM 获取未读数 → 再切换到 IM 页面展示内容的场景,与 PC 端流程一致:
// ========== 步骤1:应用入口处登录 IM ==========
import { watch } from 'vue'
import { useMessage } from 'mdm-im-v3/core'
const { initIMSDK, getUnReadMessageCount, loginStatus } = useMessage({
userID: 'your-account',
nickname: 'your-name',
imToken: 'your-im-token',
satoken: 'your-system-token',
apiUrl: 'https://your-api-url:8443',
wsUrl: 'wss://your-ws-url:8443',
platformID: 5,
})
// 初始化并登录
await initIMSDK()
// 监控 IM 登录状态,登录成功后获取未读数
watch(loginStatus, status => {
if (status === 3) {
getUnReadMessageCount(true)
}
})
<!-- ========== 步骤2:切换菜单后渲染 H5 组件 ========== -->
<template>
<mdm-im-v3-h5 ref="h5Ref" theme="light" />
</template>
<script setup>
import { ref } from 'vue'
import { mdmImV3H5 } from 'mdm-im-v3/h5'
// 组件会自动读取 store 和 window.imInstance,无需再次初始化
const h5Ref = ref()
</script>
原理:
useMessage()内部自动将配置存入 Pinia store(useChatStore),initIMSDK()创建的 IM 实例挂载到window.imInstance。<mdm-im-v3-h5>组件挂载时检测到loginStatus === 3(已登录),直接复用已有的 IM 实例和会话数据,无需二次登录。
H5 内置路由表
以下路由全部内聚在 mdmImV3H5 组件内部,消费方无需关心:
| 路径 | 组件 | 说明 |
|---|---|---|
/ | 会话列表主页 | 展示会话列表,支持滑动操作 |
/chat | 聊天页 | 消息列表 + 文字/图片/文件输入 |
/search | 搜索页 | 搜索用户和本地会话 |
/notice | 群通知 | 入群申请审批列表 |
/create-group | 创建群组 | 选择成员创建群聊 |
/select-members | 选择成员 | 通用人员选择器 |
/member-detail | 人员详情 | 单聊/群成员信息卡片 |
/chat-settings | 聊天设置 | 免打扰/清聊天记录/退出群聊 |
/group-announcement | 群公告 | 查看/编辑群公告 |
/chat-history | 聊天记录 | 按日期查看历史消息 |
/group-members | 群成员列表 | 展示/管理群成员 |
H5 组件 Props
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
theme | String | 'light' | 主题:'dark' / 'light' |
baseConfig | Object | {} | IM 配置对象 |
groupID | String | '' | 指定群组 ID,登录后直接打开该群 |
loadMessageCount | Number | 50 | 每次加载的历史消息数量(10–100) |
tenantID | String|Number | '1' | 租户 ID |
onKeywordCheck | Boolean | false | 是否启用外部敏感词校验 |
outsideHandleUser | Boolean | false | 是否由外部处理人员选择 |
H5 组件暴露的方法
通过 ref 调用 <mdm-im-v3-h5>:
// autoLogin — 一键登录
h5Ref.value.autoLogin({
loginCode: string // 用户登录账号,必填
userName?: string // 用户名
imToken: string // OpenIM 用户令牌,必填
satoken?: string // 系统 token
apiUrl: string // IM API 地址,必填
platformID?: number // IM 平台标识(默认 5-网页端)
})
// checkLogin() — 检测 store 中已有登录状态后跳转到会话列表页
h5Ref.value.checkLogin()
H5 组件同样支持 unread-change 事件(参数与 PC 端一致,见 组件 Events),宿主可通过 @unread-change 监听未读消息数变化。
H5 端与 PC 端差异
| 维度 | PC | H5 |
|---|---|---|
| 导入路径 | mdm-im-v3 | mdm-im-v3/h5 |
| 导入内容 | mdmImV3(组件) | mdmImV3H5(组件) |
| 核心库 | mdm-im-v3/core | mdm-im-v3/core(共享) |
| 样式入口 | mdm-im-v3/style/index.css | mdm-im-v3/style/index-h5.css |
| UI 库 | Element-Plus | Vant 4 |
| 布局 | 左右分栏(会话列表 + 聊天面板同屏) | 独立全屏页面,路由导航 |
| 路由 | 无内置路由,消费方自行管理 | 内置路由(hash 模式,11 页面),内聚在组件内部 |
| 消息输入 | 富文本编辑器(@wangeditor) | 纯文本 <textarea> |
| 会话操作 | 右键 contextmenu 菜单 | 左滑操作 + 长按 ActionSheet |
| 截图 | 支持 js-web-screen-shot | 不支持 |
| 下拉刷新/无限滚动 | 自定义滚动事件 | van-pull-refresh + van-list |
组件 Props
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
theme | String | 'dark' | 主题:'dark'(深色) / 'light'(浅色) |
baseConfig | Object | {} | IM 配置对象,见下方说明 |
showConversationList | Boolean | true | 是否显示左侧会话列表(仅 PC 端有效) |
groupID | String | '' | 指定群组 ID,登录后直接打开该群会话 |
loadMessageCount | Number | 50 | 每次加载的历史消息数量(范围 10–100) |
tenantID | String|Number | '1' | 租户 ID,随消息扩展字段发送 |
onKeywordCheck | Boolean | false | 是否启用外部敏感词校验(开启后发送文本会触发 send-chat-text 事件) |
outsideHandleUser | Boolean | false | 是否由外部处理人员选择(开启后邀请成员会触发 invite-member 事件) |
baseConfig 字段
| 字段 | 类型 | 说明 |
|---|---|---|
apiUrl | String | IM API 地址,必填 |
loginCode | String | 用户登录账号,必填 |
imToken | String | OpenIM 用户令牌,必填 |
userName | String | 用户名 |
satoken | String | 系统 token |
platformID | String | IM 平台标识,默认 web |
组件 Events
PC 与 H5 组件支持相同的事件:
| 事件名 | 参数 | 说明 |
|---|---|---|
send-chat-text | { list, isReply, atUserList } | onKeywordCheck 为 true 时,文本发送前触发,用于外部敏感词过滤。过滤后调用 chatRef.sendCheckedText() 发送 |
invite-member | memberIds: string[] | outsideHandleUser 为 true 时,需要邀请成员时触发,由外部处理人员选择 |
unread-change | { count, reason, time, conversationID? } | IM 未读消息总数变化时触发,可用于同步宿主的消息红点/角标,见下方说明 |
unread-change(未读消息数)
组件在以下时机向上抛出该事件:
reason | 触发时机 |
|---|---|
'init' | IM 初始化加载完成(登录后会话列表拉取/缓存渲染结束),每次初始化触发一次 |
'receive' | 收到新消息(SDK 每批推送触发一次,即使未读总数没变) |
'update' | 未读总数发生变化,如标记已读、其他端已读、新会话等 |
事件载荷:
{
count: number // 当前未读消息总数
reason: 'init' | 'receive' | 'update'
time: number // 触发时间戳(毫秒)
conversationID?: string // 仅 reason='receive':最新消息所属会话 ID
}
PC / H5 用法一致:
<template>
<mdm-im-v3 ref="chatRef" @unread-change="handleUnreadChange" />
<!-- H5:<mdm-im-v3-h5 ref="chatRef" @unread-change="handleUnreadChange" /> -->
</template>
<script setup>
const handleUnreadChange = ({ count, reason, conversationID }) => {
// 同步宿主菜单/标签页的红点数量
badgeRef.value = count
console.log('[IM] 未读数:', count, reason, conversationID)
}
</script>
未读总数同时保存在 store 的
unReadCount中,可通过useChatStore().getUnReadCount读取;unread-change事件适用于不便订阅 store 的宿主(如非 Vue 渲染的菜单/角标)。
组件暴露的方法
通过 ref 调用:
autoLogin(config)
一键登录,参数对象:
{
loginCode: string // 用户登录账号,必填
userName?: string // 用户名
imToken: string // OpenIM 用户令牌,必填
satoken?: string // 系统 token
apiUrl: string // IM API 地址,必填
platformID?: number // IM 平台
}
无返回值。方法内部完成 store 写入、SDK 初始化、登录、轮询状态直到会话列表加载完毕。
checkLogin()
轮询检测登录状态:当 store 中 loginStatus === 3(已登录)且 window.imInstance 存在时,自动调用内部 afterLogged() 拉取会话列表。配合方式二(手动 useMessage 登录)使用;方式一(autoLogin)内部已自动调用。
注:
afterLogged()为组件内部方法,未通过defineExpose暴露,外部请调用checkLogin()触发会话加载。
sendCheckedText({ list, isReply, atUserList })
发送经过敏感词校验后的文本。与 send-chat-text 事件配合使用:
const handleSendChatText = ({ list, isReply, atUserList }) => {
const filteredList = keywordFilter(list)
chatRef.value.sendCheckedText({ list: filteredList, isReply, atUserList })
}
inviteUser(groupID, userIDs)
邀请用户进群:
chatRef.value.inviteUser('group-id-xxx', ['user1', 'user2'])
// 返回 Promise<boolean>
useMessage API
从 mdm-im-v3/core 导入,PC / H5 两端通用,用于手动管理 IM 生命周期:
import { useMessage, useChatStore } from 'mdm-im-v3/core'
const { initIMSDK, clearInstance, getUnReadMessageCount, setConversationMsgAsRead, mittBus } = useMessage({
userID: 'your-account',
nickname: 'your-name',
faceURL: '',
imToken: 'your-im-token',
satoken: 'your-system-token',
apiUrl: 'https://your-api-url:8443',
wsUrl: 'wss://your-ws-url:8443',
tenantID: '',
platformID: 5,
})
参数
useMessage(config?) 接收一个可选的配置对象,所有字段均为可选:
| 字段 | 类型 | 说明 |
|---|---|---|
userID | String | 用户 ID |
nickname | String | 用户昵称 |
faceURL | String | 用户头像 |
imToken | String | OpenIM 用户令牌 |
satoken | String | 系统 token |
apiUrl | String | IM API 地址 |
wsUrl | String | IM WebSocket 地址 |
tenantID | String | 租户 ID |
platformID | Number | IM 平台标识,5-网页端 |
传入配置后,useMessage 内部立刻调用 chatStore.setUserInfo() 将参数保存到 Pinia store。也可以不传参数,后续在调用 initIMSDK() 前手动写入 store。
返回值
| 方法/属性 | 说明 |
|---|---|
messageStore | Pinia store 实例(useChatStore()),可直接访问所有 state/getters/actions |
loginStatus | IM 登录状态的 computed 引用,便于外部 watch 监控。值:1-未登录,2-登录中,3-已登录 |
initIMSDK() | 初始化 SDK → 绑定所有 OpenIM 事件 → 登录。需先在 store 中设置 userID、imToken、apiUrl 等。返回 Promise<void>。 |
clearInstance(bool) | 解绑事件 → 登出 → 清除 SDK 实例。传入 true 时同时清空会话列表和 window.imInstance。 |
getUnReadMessageCount(fromServer?) | 获取未读消息总数。fromServer=true 从服务端获取,默认从本地会话列表累计。总数变化时会广播 IM_UNREAD_COUNT_CHANGED(组件转发为 unread-change 事件)。 |
publishUnreadCount(reason?, extra?) | 主动向宿主广播一次未读总数,通过 IM_UNREAD_COUNT_CHANGED 发给组件并转发为 unread-change 事件。reason 默认 'update',组件初始化完成时内部使用 'init'。 |
waitForSyncFinish(timeout?) | 等待登录后的服务端同步(OnSyncServerFinish)完成,返回 Promise<boolean>。同步完成前 SDK 本地会话缓存为空,此时拉取会话列表只会得到空数组,因此拉取会话列表前应先等待该方法;默认超时 8000ms,超时返回 false,按兜底流程继续。 |
setConversationMsgAsRead(conversationID?) | 将指定会话的消息标记为已读,不传则使用当前会话。窗口失焦时自动跳过。 |
mittBus | 全局事件总线实例,用于监听 IM 自定义事件(见下方事件列表) |
ChatStore
从 mdm-im-v3/core 导入 useChatStore 后调用获取,是 Pinia store(id: 'chatMessage'),已开启 localStorage 持久化,持久化字段包括:userID、nickname、faceURL、platformID、apiUrl、wsUrl、tenantID(imToken 和 satoken 仅保存在 Pinia 内存中,不持久化到本地存储)。
import { useChatStore } from 'mdm-im-v3/core'
const chatStore = useChatStore()
常用 State
| 字段 | 说明 |
|---|---|
loginStatus | 登录状态:1-未登录,2-登录中,3-已登录 |
userID | 当前用户 ID |
nickname | 用户昵称 |
imToken | IM 令牌 |
unReadCount | 未读消息总数 |
isFocused | 窗口是否聚焦 |
常用 Getters
| Getter | 说明 |
|---|---|
getLoginStatus | 获取登录状态 |
getUserInfo | 获取用户完整信息对象 |
getBaseUrl | 获取 API 地址 |
getUnReadCount | 获取未读总数 |
getIsFocused | 获取窗口聚焦状态 |
常用 Actions
| Action | 说明 |
|---|---|
setUserInfo(obj) | 合并写入用户信息 |
setLoginStatus(val) | 设置登录状态 |
setUnReadCount(count) | 设置未读消息数 |
setIsFocused(bool) | 设置窗口聚焦状态 |
MittBus 事件
通过 useMessage().mittBus 获取,可监听以下 IM 自定义事件:
| 事件名 | 说明 |
|---|---|
createSLRemote | 远程桌面连接已创建,携带 { address, session } |
applySLControl | 收到远程控制申请,携带 { sendUserName, sendUserID, recvUserID } |
acceptSLControl | 远程控制申请被接受 |
rejectSLControl | 远程控制申请被拒绝 |
inviteSLControl | 被邀请远程控制 |
acceptSLRemote | 远程桌面被接受 |
rejectSLRemote | 远程桌面被拒绝 |
LOGOUT_NOTICE | 账号在其他设备登录,被强制下线 |
IM_LOGOUT | IM 已登出 |
IM_SYNC_SERVER_FINISH | 登录后的服务端同步结束(参数 true-成功,false-失败)。此时会话/消息缓存才就绪,可用于登录后补拉会话列表 |
IM_UNREAD_COUNT_CHANGED | 未读消息总数变化,参数 { count, reason, time, conversationID? }。组件内部据此转发 unread-change 事件,一般无需直接监听 |
OnGroupInfoChanged | 群信息变更(名称、头像、公告等) |
OnJoinedGroupAdded | 被拉入新群 |
OnJoinedGroupDeleted | 退出或被踢出群 |
OnGroupMemberDeleted | 群成员减少(退群/被踢) |
OnGroupApplicationAdded | 收到新的入群申请 |
OnGroupApplicationAccepted | 入群申请被同意 |
OnGroupApplicationRejected | 入群申请被拒绝 |
监听示例:
const { mittBus } = useMessage()
mittBus.on('LOGOUT_NOTICE', () => {
router.push('/login')
})
阅后即焚(Burn After Reading)
单聊会话支持「阅后即焚」:谁开启,谁发送的消息才会在被对方阅读后倒计时并销毁;对方未开启时,其发送的消息不受影响。
使用入口
- PC:聊天窗口右上角「设置」抽屉 → 单聊会话 → 「阅后即焚」开关 + 「阅后即焚时间」;
- H5:聊天设置页(
/chat-settings)→ 单聊会话 → 「阅后即焚」开关 + 时间选择。
群聊不提供该入口(OpenIM 的 isPrivateChat 语义按私密会话设计,本期仅支持单聊)。
规则
| 项 | 说明 |
|---|---|
| 按用户持久化 | 开关、时长、开启人、目标人写入自己会话的 ex.burn:{ enabled, duration, fromUserID, toUserID, updatedAt };ex 由服务端按用户存储,换设备/清缓存后可恢复,且不会改动对方设置 |
| ex 兼容 | 写入 burn 时自动 merge,保留 isHidden / isDeleted / isRead 等业务标记;这些业务写 ex 时也会用 preserveBurnInEx() 保留 burn |
| 会话标识 | 开启后自己的会话列表对应会话显示 🔥(按 ex.burn.fromUserID === 当前用户 判断,对方列表不会显示) |
| 消息快照 | 开启方发送消息时在消息 ex 写入 { burnBySender, burnDuration, burnFromUserID, burnToUserID };未开启方发送的消息不带该标记,因此不会被焚毁 |
| 时长范围 | 1–86400 秒,默认 30 秒;PC 提供预设(10s/30s/1min/5min/30min/1h)+ 自定义 |
| 提交方式 | 开关、时长与 ex.burn 一次性提交 setConversation(),避免"开关开了但时长为 0、消息不销毁" |
| 关闭 | 删除 ex.burn,后续发送的消息不再带焚毁快照;已进入倒计时的消息按原快照走完 |
| 展示 | 带焚毁快照的消息在消息列表显示 🔥 与剩余秒数(通知类消息不展示、不参与焚毁) |
| 通知文案 | 1701 通知带上操作人:张三 开启了阅后即焚 / 张三 关闭了阅后即焚(自己操作显示"你") |
| 销毁触发 | 接收方打开会话、未读转已读后开始倒计时;发送方在收到已读回执后开始 |
| 销毁执行 | 倒计时结束:消息列表移除 + IndexedDB 缓存回写 + deleteMessage 删除;已切换走的会话也会在后台到点删除 |
服务端能力与限制
- 设置依赖服务端对会话
ex的持久化(本项目已用于隐藏/标记未读,属于既有能力); - 阅后即焚的「到期真删」属于 OpenIM 服务端能力,落地前需确认当前部署版本支持;即使服务端不支持,组件仍会按本地倒计时把消息从界面与缓存中移除(弱一致);
- 服务端会话上的
isPrivateChat/burnDuration仍会随设置同步(用于 1701 通知与 SDK 私密快照),但组件不再据此决定是否焚毁。
相关字段:会话
ex.burn(设置,按用户);消息ex.burnBySender/burnDuration/burnFromUserID/burnToUserID(发送方快照,组件焚毁依据);attachedInfoElem仅作兼容展示。
语音消息(Voice Message)
支持在 PC / H5 端录制并发送 OpenIM 语音消息(contentType = 103,soundElem),消息列表复用已有语音气泡(时长 + 声波 + 播放)。
使用入口
- H5:输入区左侧「语音/键盘」切换按钮 → 按住「按住 说话」开始录音,松开发送;上滑超过 80px 松开取消;
- PC:编辑器工具栏麦克风图标 → 录音弹窗 → 点击麦克风开始,点击「停止并发送」结束并发送;
- 另:PC 选择/拖拽音频文件、H5 选择音频文件,都会按语音消息发送(不再显示成文件卡片)。
录音与发送规格
| 项 | 说明 |
|---|---|
| 录音格式 | WAV(PCM),16kHz / 16bit / 单声道,全平台可播放 |
| 时长 | 最短 1 秒(太短提示"录音时间太短"),最长 60 秒(最后 10 秒倒计时,到点自动发送) |
| 发送链路 | 先上传业务文件服务(/api/v1/common/uploadFiles)→ createSoundMessageByURL → 乐观插入 → sendMessageNotOss |
| 时长取值 | 优先读音频元数据(getMediaDuration),其次录音器计时,最后按实际耗时兜底;存储为整秒且 ≥1 |
| 失败处理 | 上传失败不产生消息;发送失败气泡标记 status=3(红色感叹号) |
| 播放 | 复用 useAudioPlayer,录音开始会暂停正在播放的语音,避免外放混入 |
| 后台切换 | 录音中切到后台/隐藏页面自动取消,避免后台持续录音 |
依赖与权限
- 录音依赖
js-audio-recorder(已加入dependencies),产物为 WAV,iOS / 微信浏览器 / Electron 均可播放; - 浏览器录音要求 HTTPS 或 localhost,首次使用需授权麦克风;拒绝授权、无麦克风、浏览器不支持时均有 Toast 提示;
- PC Electron 需在系统层允许麦克风权限。
相关字段:
soundElem.duration(秒,必填)、sourceUrl/soundPath(播放地址,组件会补baseUrl并把/api/v1/common/reviewFile/重写为/api/v1/auth/reviewFile/)、dataSize、soundType(如wav)。
主题
支持深色(dark)和浅色(light)两套主题,通过 CSS 变量驱动:
<mdm-im-v3 theme="dark" />
<mdm-im-v3 theme="light" />
主题 CSS 变量定义在 chat-dark-theme / chat-light-theme 类名下,可通过覆盖 CSS 变量进行自定义。