快速集成 uni-app 源码(Vue 3)

更新时间: 2024/04/12 11:37:48

IM uni-app Demo 是基于网易云信 NIM SDK 的一款 uniapp Demo,它提供了一些通用的功能,包含会话、聊天、群组等,您可以基于源码搭建自己的 IM 业务逻辑。

界面效果

IM uni-app Demo 的部分界面效果如下:

通讯模块主要界面.png

Demo.png

平台支持

当前 IM uni-app Demo 仅支持 Android、iOS、H5、和微信小程序。

前提条件

开始跑通 Demo 之前,请确保:

  • 已在云信控制台创建应用,获取 App Key。

  • 注册云信 IM 账号,获取 accid 和 token。

  • 开发环境满足如下要求:

    • HBuilderX
    • Vue 3
    • TypeScript
    • sass(sass-loader 版本 <= 10.1.1)
    • node(12.13.0 <= node 版本 <= 17.0.0, 推荐使用 Node.js 官方 LTS 版本 16.17.0)
    • npm(版本请与 node 版本匹配)

源码集成

步骤1:创建项目

创建 uni-app 项目,详情参见 uni-app 官网

如已创建项目,请忽略本步骤。

步骤2:下载组件

下载 NEUIKit 组件。

# 找一个目录,clone 组件项目
git clone https://github.com/netease-kit/nim-uikit-uniapp.git
# 在自己的项目根目录下执行以下命令,将组件 copy 过来
mkdir -p ./pages/NEUIKit
# macOS
mv ${组件项目路径}/NEUIKit/* ./pages/NEUIKit
# windows
move ${组件项目路径}/NEUIKit/* .\pages\NEUIKit

下载成功后, 目录结构如下图所示:

步骤3:添加依赖和图片引入

  1. 在项目的根目录下添加依赖和图片引入。

    npm init -y
    npm i @xkit-yx/core-kit@0.13.0  @xkit-yx/im-store@0.3.0  @xkit-yx/utils@0.5.6 dayjs@1.11.7 mobx@6.6.1 pinyin@3.1.0 --save
    
  2. 在自己的项目根目录下执行以下命令,将组件需要的图片复制到static目录下,若命令执行不成功,请按照路径手动复制。

    mkdir -p static
    
  3. 当前静态资源放置在 NOS 中,您可通过链接的方式在组件中进行访问。

    由于访问频率有限制,建议您将静态资源放置在自己的服务器上,然后修改 /pages/NEUIKit/components/Icon.vue 组件中的链接即可。

    # macOS
    cp -r pages/NEUIKit/static static/YX_IMG
    # windows
    xcopy /E pages\NEUIKit\static static\YX_IMG
    

步骤4:引入组件

在 App.vue 文件引入 NEUIKit 组件。

<script>
import RootStore from '@xkit-yx/im-store'
import { NimKitCore } from '@xkit-yx/core-kit/dist/uniapp-nim-core'
import { getMsgContentTipByType } from './pages/NEUIKit/utils/msg'
import { getUniPlatform } from './pages/NEUIKit/utils'
// #ifdef APP-PLUS
const nimPushPlugin = uni.requireNativePlugin('NIMUniPlugin-PluginModule')
// #endif
export default {
  onLaunch: function () {
    const isWeixinApp = getUniPlatform() === 'mp-weixin'
    // @ts-ignore
    const nim = uni.$UIKitNIM = new NimKitCore({
      initOptions: {
        "appkey": "", // 请填写你的appkey
        "account": "", // 请填写你的account
        "token": "", // 请填写你的token
        "lbsUrls": isWeixinApp ? [
          "https://lbs.netease.im/lbs/wxwebconf.jsp"
        ] : [
          "https://lbs.netease.im/lbs/webconf.jsp"
        ],
        "linkUrl": isWeixinApp ? 'wlnimsc0.netease.im' : 'weblink.netease.im',
        "needReconnect": true,
        /**
        * 使用固定设备ID,
        */
        isFixedDeviceId: true,
        // "reconnectionAttempts": 5,
        debugLevel: 'debug',
      },
      platform: 'UniApp',
    })
    // @ts-ignore
    const store = uni.$UIKitStore = new RootStore(nim, {
      addFriendNeedVerify: false,
      teamBeInviteMode: 'noVerify',
      teamJoinMode: 'noVerify',
      teamUpdateExtMode: 'all',
      teamUpdateTeamMode: 'all',
      teamInviteMode: 'all',
      sendMsgBefore: async (options, type) => {
        const pushContent = getMsgContentTipByType({ body: options.body, type })
        const yxAitMsg = options.ext ? options.ext.yxAitMsg : { forcePushIDsList: '[]', needForcePush: false }

        // 如果是 at 消息,需要走离线强推
        const { forcePushIDsList, needForcePush } = yxAitMsg
          // @ts-ignore
          ? store.msgStore._formatExtAitToPushInfo(yxAitMsg, options.body)
          : { forcePushIDsList: '[]', needForcePush: false }

        console.log('forcePushIDsList: ', forcePushIDsList)

        // 不同产商的推送消息体
        const { scene, to } = options
        const pushPayload = JSON.stringify({
          // oppo
          oppoField: {
            "click_action_type": 4, // 参考 oppo 官网
            "click_action_activity": '', // 各端不一样 TODO
            "action_parameters": { "sessionId": scene, "sessionType": to } // 自定义
          },

          // vivo
          vivoField: {
            "pushMode": 0 //推送模式 0:正式推送;1:测试推送,不填默认为0
          },

          // huawei
          hwField: {
            click_action: {
              'type': 1,
              'action': '' // 各端不一样 TODO
            },
            androidConfig: {
              'category': 'IM',
              'data': JSON.stringify({ 'sessionId': to, 'sessionType': scene })
            }
          },

          // 通用
          sessionId: to,
          sessionType: scene
        })

        const pushInfo = {
          needPush: true,
          needPushBadge: true,
          pushPayload: '{}',
          pushContent,
          needForcePush,
          forcePushIDsList,
          forcePushContent: pushContent,
        }
        return { ...options, pushInfo }
      },
    })
    // #ifdef APP-PLUS
    // 注册推送
    nim.getNIM().offlinePush.setOfflinePushConfig({
      plugin: nimPushPlugin,
      authConfig: {
        // xiaomi
        xmAppId: "",
        xmAppKey: "",
        xmCertificateName: "KIT_UNIAPP_MI_PUSH",

        // huawei
        hwAppId: "",
        hwCertificateName: "KIT_UNIAPP_HW_PUSH",

        // oppo
        oppoAppId: "",
        oppoAppKey: "",
        oppoAppSecret: "",
        oppoCertificateName: "KIT_UNIAPP_OPPO_PUSH",

        /**
        * 注意vivo的appid和appkey需要同时在此处,以及manifest.json(即插件参数配置)中配置
        */
        vivoAppId: "",
        vivoAppKey: "",
        vivoCertificateName: "KIT_UNIAPP_VIVO_PUSH",

        // fcm
        fcmCertificateName: "KIT_UNIAPP_FCM_PUSH",

        // meizu
        mzAppId: "",
        mzAppKey: "",
        mzCertificateName: "KIT_UNIAPP_MZ_PUSH",

        // iOS
        apnsCertificateName: "dis_im_uniapp"
      }
    })
    // #endif

    nim.connect()

  },
  onShow: function () {
    console.log('App Show')
  },
  onHide: function () {
    console.log('App Hide')
  }
}
</script>

<style>
uni-page-body {
  height: 100%;
}
uni-page-body > uni-view {
  height: 100%;
}
</style>

在初始化 NimKitCore 时,需要根据是否是微信小程序,传入不同的lbsUrlslinkUrl

单击查看具体示例
const nim = (uni.$UIKitNIM = new NimKitCore({
      initOptions: {
        appkey: "", // 请填写你的appkey
        account: "", // 请填写你的account
        token: "", // 请填写你的token
        lbsUrls: isWeixinApp
          ? ["https://lbs.netease.im/lbs/wxwebconf.jsp"]
          : ["https://lbs.netease.im/lbs/webconf.jsp"],
        linkUrl: isWeixinApp ? "wlnimsc0.netease.im" : "weblink.netease.im",
        needReconnect: true,
        /**
         * 使用固定设备ID,
         */
        isFixedDeviceId: true,
        // "reconnectionAttempts": 5,
        debugLevel: "debug",
      },
      platform: "UniApp",
    }));

步骤5:配置路由

在 pages/NEUIKit/utils/customNavigate.ts 中,修改 preUrl:

const preUrl = '/pages/NEUIKit'

在您项目的 pages.json 文件中的更新 pages 路由:

{
  "pages": [
    {
      "path": "pages/NEUIKit/pages/Conversation/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Login/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Chat/message/p2p-set",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-set/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-set/group-info-edit",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Contact/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Contact/contact-list/group-list",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Contact/contact-list/valid-list",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Contact/contact-list/black-list",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Chat/index",
      "style": {
        "navigationBarBackgroundColor": "#F6F8FA",
        "navigationBarTextStyle": "black",
        "navigationStyle": "custom",
        "enablePullDownRefresh": false,
        "app-plus": {
          "softinputNavBar": "none",
          "bounce": "none"
        }
      }
    },
    {
      "path": "pages/NEUIKit/pages/Chat/video-play",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-member/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-create/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-add/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Friend/add-friend/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/user-card/friend/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/user-card/my/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/user-card/my/setting",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/user-card/my-detail/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/user-card/detail-item/index",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-set/nick-in-team",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-set/group-manage",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-set/transform-team",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-set/group-manager-list",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Group/group-set/add-group-manager",
      "style": {
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/NEUIKit/pages/Chat/forward",
      "style": {
        "navigationStyle": "custom"
      }
    }
  ],
  "globalStyle": {
    "navigationBarTextStyle": "black"
  },
  "tabBar": {
      "backgroundColor": "#F6F8FA",
      "color": "#999999",
      "selectedColor": "#337EFF",
      "height": "60px",
      "list": [
        {
          "text": "消息",
          "iconPath": "static/YX_IMG/conversation.png",
          "selectedIconPath": "static/YX_IMG/conversation-selected.png",
          "pagePath": "pages/NEUIKit/pages/Conversation/index"
        },
        {
          "text": "通讯录",
          "iconPath": "static/YX_IMG/contact.png",
          "selectedIconPath": "static/YX_IMG/contact-selected.png",
          "pagePath": "pages/NEUIKit/pages/Contact/index"
        },
        {
          "text": "我的",
          "pagePath": "pages/NEUIKit/pages/user-card/my/index",
          "iconPath": "static/YX_IMG/me.png",
          "selectedIconPath": "static/YX_IMG/me-selected.png"
        }
      ]
    }
}

步骤6:运行 Demo

运行.png

相关参考

Demo 源码地址

请前往云信开源代码仓库获取源码。

API 参考

IM uni-app Demo 基于 UIKitStore 开发,相关 API 详情请参见 UIKitStore

使用原生 SDK 中的方法

如果您需要自行实现目前 uniapp UIKit 还未实现,但原生的 IM uniapp SDK 已提供的能力。您可以通过调用原生 IM uniapp SDK 中提供的接口来实现。原生 uniapp SDK 中的接口请参考对应的 API 文档

uni.$UIKitNIM = new NimKitCore({
    initOptions: {
      appkey: '',
      lbsUrls: isWeixinApp
        ? ['https://lbs.netease.im/lbs/wxwebconf.jsp']
        : ['https://lbs.netease.im/lbs/webconf.jsp'],
      linkUrl: isWeixinApp ? 'wlnimsc0.netease.im' : 'weblink.netease.im',
      needReconnect: true,
      /**
       * 使用固定设备ID,
       */
      isFixedDeviceId: true,
      // "reconnectionAttempts": 5,
      debugLevel: 'debug',
      ...opts,
    },
    platform: 'UniApp',
  })

例如,您想对 IM uniapp UIKit 的消息历史进行全文检索(按时间分页搜索),可以调用原生 SDK 中的 msgFtsInServerByTiming 方法,示例代码如下:

uni.$UIKitNIM.nim.msgFtsInServerByTiming({
  keyword: '你好',
})

示例中的 uni.$UIKitNIM.nim 为原生 im uniapp sdk 的实例。

常见问题

无法正常打开相册

相册拒绝授权后再次开启授权,由于 uniapp api 兼容问题,部分 Android 机型无法正常打开相册,需要用户自行处理。

iOS 低版本构建后无法运行

如果 iOS 低版本构建后无法运行,需在构建出的资源 dist/build/app/app-service.js 文件最前面添加 var globalThis = Function('return this')();

uniapp 兼容问题

部分 uniapp 兼容问题,已在代码中说明。

page 结构问题

由于 uniapp 自身的 uni.setNavigationBarTitle 限制,chat 页面的 NavigationBarTitle,需要您自行调整 page 结构设置。

uniapp 推送问题

uni-app 推送相关问题请参考uni-app 离线推送

业务登录问题

用户业务侧的登录需要结合云信 IM 的登录实现。

在您的登录页完成登录后,将你的业务服务器返回的 account、token 传入 app.vue 里的初始化登录 IM 的方法,待 IM 初始化、连接完成后,再重定向到您需要的界面。

无法切换深色模式

目前 Demo 不支持深色模式。如需要深色模式,需要用户在业务侧自行适配。

uni_modules 问题

NEUIKit 中使用到的 uniapp 提供的部分组件(例如 uni-popup),如果使用上产生冲突,您需要将 NEUIKit 的 uni-modules 与您使用的 uni-modules 进行合并,然后修改 NEUIkit 中引用的路径即可。

视频/语音模块离线打包问题

参考 uniapp 官网的[离线打包文档]](https://nativesupport.dcloud.net.cn/AppDocs/usesdk/android.html)。

H5 断网重连问题

若遇到 H5 断网重连失败的问题,请在 manifest.json 中,修改配置,示例如下:

"optimization" : {
  "treeShaking" : {
    "enable" : false
  }
}
此文档是否对你有帮助?
有帮助
去反馈
  • 界面效果
  • 平台支持
  • 前提条件
  • 源码集成
  • 步骤1:创建项目
  • 步骤2:下载组件
  • 步骤3:添加依赖和图片引入
  • 步骤4:引入组件
  • 步骤5:配置路由
  • 步骤6:运行 Demo
  • 相关参考
  • Demo 源码地址
  • API 参考
  • 使用原生 SDK 中的方法
  • 常见问题
  • 无法正常打开相册
  • iOS 低版本构建后无法运行
  • uniapp 兼容问题
  • page 结构问题
  • uniapp 推送问题
  • 业务登录问题
  • 无法切换深色模式
  • uni_modules 问题
  • 视频/语音模块离线打包问题
  • H5 断网重连问题