# mdm-im-v3 中文文档

规范地址：https://npmcn.com/package/mdm-im-v3/

版本：2\.1\.3
npm 元数据更新时间：2026-11-04T02:24:30.570Z
中文译文更新时间：2026-11-04T03:47:30.779Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/mdm-im-v3
原文对照：https://npmcn.com/package/mdm-im-v3/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：15.3 MB，64 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- 运行依赖：11 个
- peer 依赖：vue ^3\.5\.21，sass ^1\.86\.3，vant ^4\.10\.0，axios ^1\.8\.4，pinia ^3\.0\.2，vue-router ^4\.5\.0，element-plus ^2\.9\.7，pinia-plugin-persist ^1\.0\.0，@element-plus/icons-vue ^2\.3\.1
- 首次发布：2025-10-30T07:48:37.156Z
- 最新版本发布：2026-11-04T02:24:30.395Z
- 已发布版本数：20（近 12 个月 19 个）

## 中文简介

基于 OpenIM SDK 的 Vue 3 即时通讯组件库，提供会话列表、单聊/群聊消息、文件收发、入群审批等功能。**同时支持 PC 端（Element-Plus）和 H5 移动端（Vant 4）**，打包后通过 npm 分发，由同一包提供双端组件。

## 安装

```sh
npm install mdm-im-v3
```

也可以使用：`pnpm add mdm-im-v3`、`yarn add mdm-im-v3`、`bun add mdm-im-v3`

## README

# 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/client-sdk@0.0.18`） [技术文档](https://doc.rentsoft.cn/zh-hans/sdks/quickstart/browser)
-   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`，也不依赖宿主是否已有同名图标字体。

## 安装

```bash
pnpm i mdm-im-v3@latest -S
```

### 安装 peer 依赖

> **PC 端** 需安装以下依赖（选择你自己的包管理器）：

```bash
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：

```bash
# 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` 方法一键完成初始化、登录和会话加载：

```vue
<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](https://npmcn.com/package/mdm-im-v3/%E8%8E%B7%E5%8F%96%E5%8D%B3%E6%97%B6%E6%B6%88%E6%81%AF%E7%BB%84%E4%BB%B6mdm-im-v3%E7%9A%84%E6%9C%AA%E8%AF%BB%E6%B6%88%E6%81%AF%E6%95%B0%E9%87%8F%E7%9A%84%E8%B0%83%E7%94%A8%E6%96%B9%E5%BC%8F.md)。

1.  入口处调用 `useMessage(config)` 初始化 IM，获取未读数量
2.  用户切换到 IM 菜单时再渲染 `<mdm-im-v3>` 组件
3.  组件自动读取 store 中的已登录状态和 `window.imInstance`，无需重复初始化

```js
// ========== 步骤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)
  }
})
```

```vue
<!-- ========== 步骤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 并在入口文件中注册：

```bash
pnpm i vant@^4.10 -S
```

### 入口文件（main.js）

```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）

```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 端完全一致**：

```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()

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 端流程一致：

```js
// ========== 步骤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)
  }
})
```

```vue
<!-- ========== 步骤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>`：

```ts
// 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](https://npmcn.com/package/mdm-im-v3/#readme-%E7%BB%84%E4%BB%B6-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'` | 未读总数发生变化，如标记已读、其他端已读、新会话等 |

事件载荷：

```ts
{
  count: number          // 当前未读消息总数
  reason: 'init' | 'receive' | 'update'
  time: number           // 触发时间戳（毫秒）
  conversationID?: string // 仅 reason='receive'：最新消息所属会话 ID
}
```

PC / H5 用法一致：

```vue
<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)`

一键登录，参数对象：

```ts
{
  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` 事件配合使用：

```js
const handleSendChatText = ({ list, isReply, atUserList }) => {
  const filteredList = keywordFilter(list)
  chatRef.value.sendCheckedText({ list: filteredList, isReply, atUserList })
}
```

### `inviteUser(groupID, userIDs)`

邀请用户进群：

```js
chatRef.value.inviteUser('group-id-xxx', ['user1', 'user2'])
// 返回 Promise<boolean>
```

* * *

## `useMessage` API

从 `mdm-im-v3/core` 导入，PC / H5 两端通用，用于手动管理 IM 生命周期：

```js
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 内存中，不持久化到本地存储）。

```js
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` | 入群申请被拒绝 |

监听示例：

```js
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 变量驱动：

```html
<mdm-im-v3 theme="dark" />
<mdm-im-v3 theme="light" />
```

主题 CSS 变量定义在 `chat-dark-theme` / `chat-light-theme` 类名下，可通过覆盖 CSS 变量进行自定义。

## 常见问题

### mdm-im-v3 是什么？

基于 OpenIM SDK 的 Vue 3 即时通讯组件库，提供会话列表、单聊/群聊消息、文件收发、入群审批等功能。同时支持 PC 端（Element-Plus）和 H5 移动端（Vant 4），打包后通过 npm 分发，由同一包提供双端组件。

### 如何安装 mdm-im-v3？

在项目目录运行 npm install mdm-im-v3，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：vue（^3\.5\.21）、sass（^1\.86\.3）、vant（^4\.10\.0）、axios（^1\.8\.4）、pinia（^3\.0\.2）、vue-router（^4\.5\.0）、element-plus（^2\.9\.7）、pinia-plugin-persist（^1\.0\.0）、@element-plus/icons-vue（^2\.3\.1）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### mdm-im-v3 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/mdm-im-v3 是否存在。

### mdm-im-v3 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### mdm-im-v3 可以商用吗？

mdm-im-v3 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### mdm-im-v3 还在维护吗？

最新版本 2\.1\.3 发布于 2026/11/04（今天）；至今共发布 20 个版本，其中过去 12 个月发布了 19 个；首次发布于 2025/10/30。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

