i3v-chat-system-2

0.0.52 • Public • Published

支持的环境

vue ^2.6.14
@vue/cli-plugin-babel": "~5.0.0"
@vue/cli-plugin-eslint": "~5.0.0"
@vue/cli-plugin-router": "~5.0.0"
@vue/cli-plugin-vuex": "~5.0.0"
@vue/cli-service": "~5.0.0"

安装

npm install i3v-chat-system@latest
npm install -D @babel/plugin-proposal-private-methods

配置

# 在babel.config.cjs文件添加以下配置
plugins: ["@babel/plugin-proposal-private-methods"],

初始化

import { $tinode } from "i3v-chat-system";
// 把$tinode放在data中,并且以后一直使用它:
tinode: $tinode;

// 初始化tinode,填写配置(只应该执行一次)
this.tinode.init({
  appName: "应用名字如I3VIM", // 必填,找管理员获取
  host: "101.35.189.186:6060", // 必填 使用wss必须填https域名如home.i3vsoft.com:6060
  apiKey: "应用的key", // 必填,找管理员获取
  transport: "ws", // 或者wss
  secure: false, // 如果用wss则填true
  persist: false, // 是否使用indexedDB缓存
});

// 禁用 console.log
this.$tinode.instance.enableLogging(false);

// 检查是否连接到聊天服务器
const isConnected = await this.tinode.connect();
if (!isConnected) {
  console.log("无法连接到聊天服务器");
  return;
}

// 通过用户名和密码登录
const { code } = await this.tinode.loginBasic(
  username, // 登录名一般是手机号
  password // 密码一般是sass系统或者业务系统的token
);
if (code === 200) {
  console.log("登录成功,可以使用聊天界面组件了");
}

退出聊天系统

this.tinode.instance.disconnect();

使用内置聊天组件

// 最完整的聊天界面
// 除了抬头,分为左中右三列模式:左侧是 消息、通讯录、会议;中列是会话列表;右列是消息界面和输入界面
import { I3vChat } from "i3v-chat-system"
components:{
  I3vChat
}
//  判断是否已经授权
const isAuthenticated = this.tinode.isAuthenticated()
// 输出聊天会话列表
<I3vChat
  v-if="isAuthenticated"
  :tinode="tinode"
  :getCompanyAndUserDataTree="getCompanyAndUserDataTree"
  :getProjectDataList="getProjectDataList"
  :getProjectUserDataTree="getProjectUserDataTree"
  :getRobotDataListByIds="getRobotDataListByIds"
  :getUserByIds="getUserByIds"
  :uploader="uploader"
  :downloader="downloader"
  :globalSearcher="globalSearcher"
  :topicFilter="topicFilter"
  :typeAlias="typeAlias"
  :msgAlias="msgAlias"
  :meetingUrl='meetingUrl'
  @select-topic="onSelectTopic"
  ref='chat'
/>
// 返回企业组织和用户混合树形数组;用户必须有个属性isUser,必须包含id,name,children(为保证id唯一性,插入uniqId用于树形组件)
async function getCompanyAndUserDataTree () {
  return [
    {
      uniqId:'x1',
      id: 'id1',
      name: 'xxx公司',
      isUser: false,
      children: [
        {
          uniqId:'x2',
          id: 'id2',
          name: '张三',
          isUser: true
        }
        // ... 重复上级结构
      ]
    }
  ]
}

// 返回项目内组织和用户混合树形数组;用户必须有个属性isUser,必须包含id,name,children(为保证id唯一性,插入uniqId作为树形结构的id)
async function getProjectUserDataTree (projectId) {
  return [
    {
      uniqId:'x1',
      id: 'id',
      name: 'xxx公司',
      isUser: false,
      avatar: '头像网址',
      children: [
        {
         // ... 重复上级结构
        }
      ]
    }
  ]
}

// 返回我参与的项目列表,必须包含id,name
async function getProjectDataList () {
  return [
    {
      id: 'id1',
      name: '项目1',
      avatar: '头像网址'
    }
    // ...
  ]
}

// 根据消息通告Id数组从业务系统获取业务数据的方法,接受两个参数:sendIds(消息的id数组),topic(主题,一般是聊天机器人对象)
async function getRobotDataListByIds (sendIds = ['id1','id2'], topicObj={}) {
  // 一般使用annountCement/getInfoByIds根据sendIds获取数据
  // 返回数组,结构如下
  return [
    {
      id: 'id1',
      name: 'xxx',
      content: '消息数据的json字符串对象',
      typeId: '类型id',
      belongId: '所属id',
      belongData: {
        //...'数据对象'
        },
      isRead: true // 是否已读
      // ...
    }
  ]
}

// 根据用户id数组返回用户信息.入参是用户id数组,返回值如下
async function getUserByIds (ids = ['id1','id2']) {
  return [
    {
      id: 'id1',
      name: 'xxx',
      avatar: 'http://www.xxx.com/avatar.jpg',
      department: '广州君和信息技术有限公司-广州总部-产品部-产品设计2部',
      title: '产品经理'
    }
  ]
}

// 上传附件的接口, 接收参数  file
// 必须返回文件链接;失败返回false
async function uploader (file) {
// ...上传逻辑
  return {
      id: "fileId",
      src: "https://xxx.xxx.xxx/xxx/file.jpg" // 分片上传的文件不会有这个值
    } // 如果失败,应该抛出错误 throw new Error('errmessage')
}
// 下载、打开文件的接口
// 入参是一个文件id列表或者文件链接列表,业务系统负责将它们下载
async function downloader (ids=[], forceZip = false) {
// ...根据文件id数组下载文件
// 返回数组,数组的格式是:[ src, src, src ]
// 如果forceZip为true,则压缩为zip文件直接下载
}

// 全局搜索接口, 接收参数对象 { searchType: "", keyword: "", tags: [] }
// searchType的值:
// "user": 从企业查找用户
// "file": 从聊天系统上传网盘查找文件
// "": 分别查找上述内容
// keyword的值:
// 当searchType === 'user',模糊查找手机和姓名
// 当searchType === 'file',模糊查找聊天系统上传网盘的文件
// tags的值:
// 当searchType === 'file',限定从网盘查找文件的分类

async function globalSearcher ({ searchType, keyword, tags }) {
  return {
    user: { total:1, rows: [ { id, name, avatar, department, title } ] } ,
    file: { total:1, rows: [{ id, name, user: {id, avatar, name}, size }] }
  }
}

// 过滤会话列表的函数,它被用于作为会话列表的filter参数。
// 参数是会话对象(参考下面topicObj说明)
function topicFilter (topicObj)=> {
  return true
  // return (topicObj &&  topic.isGroupType === "function") && topic.isGroupType() // 示例: 只显示群组
  // return (topicObj &&  topic.isP2PType === "function") && topic.isP2PType() // 示例: 只显示个人
  // return topicObj &&  robotListIds.includes(topic.topic) // 示例: 只显示机器人,robotListIds是机器人id数组
  // return  topicObj && topicObj.tags().includes('group:${groupId}') // 实例: 只显示包含指定项目标签的群组
}

typeAlias: String, // dataList元素中"busType"字段的别名;如果dataList元素里业务类型字段名是businessType,可以通过设置 typeAlias: "businessType"做映射
msgAlias: String, // dataList元素中"msgContent"字段的别名;如果dataList元素里消息内容字段名是msgContent,可以通过设置 typeAlias: "msgContent"做映射

// 会议系统的链接,需要能够接受token一键登录, 格式为:
// http://xxx.xxx.xxx?token={{sassToken}}
// 其中的{{sassToken}}是要被替换掉的内容,由业务系统通过$tinode.setState({{sassToken}})设置
meetingUrl:String,

function setTopicId(topicId){
  // 主动设置当前会话
  // 个人会话topicId格式是`usr${userId}`,如:usr123456
  // 群会话的topicId格式是`grp${groupId}`,如:grp123456
}

在 I3vChat 中使用自定义业务显示组件 slot

这个 slot 会替换 MessageAndInput 的消息显示组件

<I3vChat
// ....
>
  <div v-if="needMyOwnSlot">我的显示组件</div>
</I3vChat>

topicObj 会话对象的数据结构和方法

{
  topic: 'usr123334', // 'grp55555' 会话的id,个人以usr开头,群组以grp开头
  public: { fn: "名称" ,note: "备注"},
  unread: 0, // 未读消息数量
  isGroupType () { return true }, // 是否是群组
  isP2PType () { return true }, // 是否是个人
  isMeType () { return true }, // 是否是我自己
  // ...
}

使用对接业务系统的批量显示组件 BizDataDisplay

import { BizDataDisplay } from "i3v-chat-system"

// 显示系统通知界面,包括任务通知等
<BizDataDisplay
  :tinode="tinode"
  :topicId="topicId"
  :dataList="dataList"
  :uid="myUserBizId"
  :typeAlias="typeAlias"
  :msgAlias="msgAlias"
  @on-emit="handleEmit"
/>

// 参数说明
  topicId: 聊天机器人id
  uid: 业务系统的用户id

 dataList是从业务系统传来的数据,必须包含以下字段:
{
  id: String, // 消息的id,必须是唯一值
  isRead: Boolean, // 是否已读
  busType: String, // 业务类型,如'2','approval'等
  msgContent: String // 包含消息内容的JSON字符串
  creatTime: String // 创建时间
  ...  // 其它数据
}

// @handleEmit传回的数据格式:
{
  operation: "操作字符串",
  busType: "业务类型",
  data: {} // 从dataList传入的的元素
}

除了可以通过 on-emit 获取交互,还可以通过 pubsubjs 监听消息("tinode-ask-action"),参考后面说明

operation 包含以下值

// operation的可能值列表
"ask-comment-task"; // 要求对任务评论
"ask-detail-task"; // 要求查看任务详情
"ask-confirm-task"; // 要求确认任务
"ask-reject-task"; // 要求拒绝任务
"ask-detail-tenent"; // 要求查看企业详情
"ask-confirm-tenent"; // 要求要求确认加入企业的申请
"ask-reject-tenent"; // 要求拒绝加入企业的申请
"ask-detail-project"; // 要求查看项目详情
"ask-confirm-task-attention"; // 要求确认任务关注
"ask-reject-task-attention"; // 要求拒绝任务关注
"ask-comment-approval"; // 要求评论立项审批
"ask-detail-approval"; // 要求查看立项审批详情
"ask-detail-project-report"; // 要求要求查看项目报告详情
"ask-comment-result-approval"; // 要求评论业绩审批
"ask-detail-result-approval"; // 要求查看业绩审批详情
"ask-mark-read"; // 要求对一条记录标记为已读
"ask-detail-clue"; // 要求查看线索详情
"ask-assign-clue"; // 要求分配线索
"ask-appoint-other-assign-clue"; // 要求指定他人分配线索
"ask-focus-clue"; // 要求关注线索
"ask-back-clue"; // 要求退回线索
"ask-change-clue"; // 要求转交线索
"ask-sure-clue"; // 要求确认线索
"on-scroll-top"; // 当滚动到顶部
"on-scroll-bottom"; // 当滚动到底部
"ask-detail-business"; // 要求查看商机详
"ask-detail-follow-up"; // 要求查看跟进详情
"ask-change-task"; // 要求转交任务
"ask-continue-task"; // 要求继续任务
"ask-accecptance-task"; // 要求验收任务
"ask-convert-task"; // 要求把聊天转任务,data包含:{ messages: [ {} ]}
"ask-form-task"; // 要求向聊天者发起任务
"ask-submit-log"; // 要求提交日志(业务系统弹出日志表单)
"ask-remind-log"; // 提醒别人提交日志(业务系统调用提醒接口)
"ask-detail-log"; // 查看日志中心
"ask-select-users": // 要求客户端选择用户
"ask-form-group": // 要求客户端输入建群信息

业务系统与 BizDataDisplay 的数据交换

业务系统对每一个 operation 做处理后更新数据,应该使用$set

  • this.$set(data, 'isRead', true) // 标记已读
  • this.$set(this.dataList, idx, newData) // 更新一条数据
  • this.dataList.push(...nextPageDataList) // 添加一页数据

使用对接业务系统的单条显示组件 BizDisplay

  <BizDisplay
    :tinode="tinode"
    :bizData="bizData"
    :uid="myUserBizId"
    :typeAlias="typeAlias"
    :msgAlias="msgAlias"
    @on-emit="handleEmit"
  />

分拆聊天会话界面与聊天显示、输入界面

import {  ChatMenu,  MessageAndInput } from "i3v-chat-system"
// 会话列表
  <ChatMenu
    :tinode="tinode"
    :chatMenuSize="240"
    :topicFilter="topicFilter"
    :projectId="projectId" // 当前项目的id
    @select-topic="onSelectTopic"
  />
// ChatMenu组件显示会话和群组列表
// onSelectTopic 函数返回当前的一个聊天对象,它的topic属性是聊天对象的id
    onSelectTopic(topicObj) {
      this.topicId = topicObj.topic;
    }
// 聊天窗口界面
    <MessageAndInput
      v-if="!!topicId"
      :tinode="tinode"
      :topicId="topicId"
      :getRobotDataListByIds="getRobotDataListByIds"
      :chatInputSize="240"
      :projectId="projectId"
      width = "100%"
    />

使用 pubsubjs 与组件和聊天系统交互

以下是 pubsub-helper.js

import PubSub from "pubsub-js";

/**
 * @description: 发布消息
 * @param {String} topic 订阅主题
 * @param {any} payload 负载
 * @return {token}
 */
function publish(topic, payload) {
  PubSub.publish(topic, payload);
}

/**
 * @description: 订阅消息
 * @param {Array} tokens 订阅列表
 * @param {String} topic 如on-click-row
 * @param {Function} callback 回调函数
 * @return {void}
 */
function subscribe(tokens, topic, callback) {
  validateTopic(topic);
  if (typeof callback === "function") {
    tokens.push(PubSub.subscribe(topic, (_, payload) => callback(payload)));
  }
}

/**
 * @description: 销毁订阅
 * @param {Array} tokens 订阅列表
 * @return {void}
 */
function unsubscribe(tokens) {
  tokens.forEach((token) => {
    PubSub.unsubscribe(token);
  });
  tokens.length = 0;
}
export default {
  publish,
  subscribe,
  unsubscribe,
};

客户端监听聊天系统的消息

import $pubsub from "./pubsub-helper.js";
// 获取到未读消息总数
this.pubsubTokens = [];
$pubsub.subscribe(
  this.pubsubTokens,
  "tinode-ask-action",
  ({ operation, busType, data }) => {
    console.log("业务处理", operation, busType, data);
  }
);

$pubsub.subscribe(this.pubsubTokens, "tinode-totalUnreadCount", (number) => {
  console.log("未读消息总数:", number);
});

// 关闭聊天界面
$pubsub.subscribe(this.pubsubTokens, "tinode-askClose", () => {
  /**业务系统关闭聊天界面*/
});

// 全屏聊天界面
$pubsub.subscribe(this.pubsubTokens, "tinode-askFullScreen", () => {
  /**业务系统全屏聊天界面*/
});
// 销毁订阅
  beforeDestroy() {
    $pubsub.unsubscribe(this.pubsubTokens);
  },

客户端向聊天系统发送消息

$pubsub.publish(topic, data);

客户端发送的 topic 和 data

// 客户端发送用户,
topic = "client-send-users", data格式是[{ id, name, avatar }]

// 客户端发送建群数据
topic =  "client-send-group-form",
data格式是
{
  name: '群名',
  users: [
    { id: "用户id", name: "用户姓名" , avatar: "用户头像" }
    ],
  project: { id: '项目id', name: '项目名' }
}

业务系统与聊天系统的数据交互

向聊天系统保存数据

this.tinode.setState({ key1: "value1", key2: "value2" });

从聊天系统获取数据

const value1 = this.tinode.getState("key1");
const value2 = this.tinode.getState("key2");

常用的 key 值

  • saasToken // 业务系统登录 saas 后提供的 token
  • topicList // Array 所有聊天会话的列表
  • robotList // Array 系统聊天机器人列表,格式是 [ { id, name, robotName, tenantId }], 用于禁止向其发聊天消息
  • myUserAvatar // String 登录用户的头像网址

消息数据格式 MESSAGE-FORMAT.md

Readme

Keywords

Package Sidebar

Install

npm i i3v-chat-system-2

Weekly Downloads

0

Version

0.0.52

License

ISC

Unpacked Size

1.38 MB

Total Files

13

Last publish

Collaborators

  • technologylulu