0.9.95 • Published 2 days ago

i3v-chat-system v0.9.95

Weekly downloads
-
License
ISC
Repository
-
Last release
2 days ago

支持的环境

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

0.9.94

2 days ago

0.9.95

2 days ago

0.9.93

3 days ago

0.9.10

4 days ago

0.9.11

4 days ago

0.9.92

4 days ago

0.9.9

10 days ago

0.9.1

10 days ago

0.0.99

11 days ago

0.0.98

12 days ago

0.0.97

16 days ago

0.0.96

28 days ago

0.0.95

29 days ago

0.0.94

29 days ago

0.0.93

1 month ago

0.0.87

1 month ago

0.0.88

1 month ago

0.0.89

1 month ago

0.0.90

1 month ago

0.0.91

1 month ago

0.0.86

1 month ago

0.0.85

1 month ago

0.0.84

2 months ago

0.0.80

2 months ago

0.0.81

2 months ago

0.0.82

2 months ago

0.0.83

2 months ago

0.0.79

2 months ago

0.0.73

2 months ago

0.0.74

2 months ago

0.0.75

2 months ago

0.0.76

2 months ago

0.0.77

2 months ago

0.0.78

2 months ago

0.0.70

2 months ago

0.0.71

2 months ago

0.0.72

2 months ago

0.0.68

2 months ago

0.0.69

2 months ago

0.0.63

2 months ago

0.0.64

2 months ago

0.0.65

2 months ago

0.0.66

2 months ago

0.0.62

2 months ago

0.0.61

2 months ago

0.0.60

3 months ago

0.0.59

3 months ago

0.0.51

6 months ago

0.0.52

5 months ago

0.0.53

5 months ago

0.0.54

5 months ago

0.0.55

5 months ago

0.0.56

5 months ago

0.0.57

5 months ago

0.0.50

7 months ago

0.0.49

7 months ago

0.0.45

9 months ago

0.0.46

9 months ago

0.0.47

8 months ago

0.0.48

8 months ago

0.0.40

9 months ago

0.0.41

9 months ago

0.0.42

9 months ago

0.0.20

11 months ago

0.0.43

9 months ago

0.0.21

11 months ago

0.0.44

9 months ago

0.0.22

11 months ago

0.0.23

11 months ago

0.0.24

11 months ago

0.0.25

11 months ago

0.0.37

9 months ago

0.0.15

11 months ago

0.0.38

9 months ago

0.0.16

11 months ago

0.0.39

9 months ago

0.0.17

11 months ago

0.0.18

11 months ago

0.0.19

11 months ago

0.0.30

10 months ago

0.0.31

10 months ago

0.0.32

10 months ago

0.0.10

12 months ago

0.0.33

10 months ago

0.0.11

12 months ago

0.0.34

10 months ago

0.0.12

12 months ago

0.0.35

10 months ago

0.0.13

11 months ago

0.0.36

10 months ago

0.0.14

11 months ago

0.0.26

11 months ago

0.0.9

12 months ago

0.0.27

11 months ago

0.0.8

12 months ago

0.0.28

11 months ago

0.0.29

11 months ago

0.0.5

12 months ago

0.0.7

12 months ago

0.0.6

12 months ago

0.0.4

12 months ago

0.0.3

12 months ago

0.0.2

12 months ago

0.0.1

12 months ago