更新记录

1.0.0(2024-11-20) 下载此版本

基本功能实现


平台兼容性

Vue2 Vue3
×
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 3.1.0 app-vue × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序
× × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × × × × × × × ×

hx-wechat 介绍

即时通讯聊天应用,基于Vue 2和uView-UI,通过WebSocket提供实时通讯。支持发送图片、语音、视频和文件。具备引用、删除、复制(下载文件)、撤回和重新编辑等功能,让沟通更便捷。

使用

1. 创建 store :chat.js

export const state = {
    conversitionList: [], //聊天记录  聊天框的聊天记录 
    socket: null, // websoket
    zdColse: false // 是否是主动关闭websocket
}
export const getters = {

}

export const mutations = {
    // 通用方法:更新数据方法
    setPropName(state, res) {
        state[res.propName] = res.value;
    }
}

export const actions = {

}

① main.js 引入

import store from '@/store';
Vue.prototype.$store = store;

2.引入uview

① main.js 引入

import uView from '@/uni_modules/uview-ui'
import store from '@/store';
Vue.use(uView)

②uni.scss 引入css @import '@/uni_modules/uview-ui/theme.scss';

3.组件方法

props传参

props属性 说明 类型 默认值
pageTitle 是否展示顶部标题 Boolean true
sender 发送人信息 Object {}
reciver 接受人信息 Object {}
conversitionList 获取接口的聊天记录 Array []
handleId 正在操作某聊天记录的操作栏 id String ''

sender发送人

事件名称 说明 类型 可选值 默认值 注意
avatar 发送人图片 String —— ''
id 发送人id String —— ''
name 发送人名称 String —— ''

reciver接受人

事件名称 说明 类型 可选值 默认值
professorIcon 接受人人图片 String —— ''
professorId 接受人id String —— ''
professorName 接受人名称 String —— ''

conversitionList数据 里的项

事件名称 说明 类型 可选值 默认值
id 聊天记录id String —— ''
createTime 聊天记录创建时间 String —— ''
sendId 发送者id String —— ''
sendType 发送人类型 String —— 'customer'
avatar 发送者头像 String —— ''
length 语音的时长 String —— ''
receiveId 接受者id String —— ‘'
receiveType 接受者类型 String —— "professor"
content 发送的内容【文本,表情包,图片,视频,语音,文件】 String —— ''
fileName 图片/视频/语音/文件文件名 String —— ''
fileSize 图片/视频/语音/文件文件大小 Number —— 0
type 内容类型 String mage 、video 、audio 、 text 'text'
operate 操作类型 Number 发送消息 0 、 撤回 1 、 引用 2 、删除 3 、系统消息 4、 结束消息 5 0
quoteId 引用消息内容的id String —— ''
quoteContent 引用消息内容 String —— ''
quoteSendId 引用消息发送人id String —— ''
quoteSendName 引用消息发送人名称 String —— ''
quoteType 引用消息内推类型 String mage 、video 、audio 、 text ''
id 删除、撤回的时候该数据id String —— ''

hx-wechat组件调业务组件实现传参,调接口

事件名称 说明 类型 回调参数
@uploadFile 上传图片/音频/视频/文件 接口 Fn conversition,fileObj,urls
@editMessageLog 修改咨询信息历史记录(引用,撤回,删除等) 接口 Fn conversition
@senderData 发送信息 接口 Fn conversition

conversition数据

事件名称 说明 类型 可选值 默认值
sendId 发送者id String —— ''
sendType 发送人类型 String —— 'customer'
avatar 发送者头像 String —— ''
length 语音的时长 String —— ''
receiveId 接受者id String —— ‘'
receiveType 接受者类型 String —— "professor"
content 发送的内容【文本,表情包,图片,视频,语音,文件】 String —— ''
fileName 图片/视频/语音/文件文件名 String —— ''
fileSize 图片/视频/语音/文件文件大小 Number —— 0
type 内容类型 String mage 、video 、audio 、 text 'text'
operate 操作类型 Number 发送消息 0 、 撤回 1 、 引用 2 、删除 3 、系统消息 4、 结束消息 5 0
quoteId 引用消息内容的id String —— ''
quoteContent 引用消息内容 String —— ''
quoteType 引用消息内推类型 String mage 、video 、audio 、 text ''
quoteSendId 引用消息发送人id String —— ''
quoteSendName 引用消息发送人名称 String —— ''
id 删除、撤回的时候该数据id String —— ''

uploadFile 方法的参数urls

字段名 说明 类型 可选值 默认值
urls 图片/视频/语音/文件地址url String —— ''

uploadFile 方法的参数fileObj

字段名 说明 类型 可选值 默认值
deviceType 设备 String mobile 移动端、 pcWchat 电脑版微信,开发中工具 "mobile"
type 信息类型 String 图片,视频,文件 '图片'
业务组件调hx-wechat组件事件 app.$refs.hxwechat.××() 事件名称 说明 参数 执行 注意
quoteDel 调完发送信息接口要删除引用id —— this.$refs.hxwechat.quoteDel()
sendInfo 调完上传图片/视频/语音/文件后要把数据传到hx-wechat组件里 去发送信息 2个参数,第一个是uploadFileData ,第二个是类型type [ successUploadFile 上传文件成功 、errorUploadFile 上次文件失败] .sendInfo(uploadFileData, 'errorUploadFile'); 这个方法是在@uploadFile 这个方法里调用,因 上传图片/视频/语音/文件后要把数据传到hx-wechat组件里 去发送信息

uploadFileData数据

事件名称 说明 类型 可选值 默认值
item @uploadFile 传递过来的数据 item —— {}
content 发送的内容【文本,表情包,图片,视频,语音,文件】 String —— ''
fileName 图片/视频/语音/文件文件名 String —— ''
fileSize 图片/视频/语音/文件文件大小 Number —— 0

4. 案例

<template>
    <hx-wechat ref="hxwechat" :reciver="reciver" :sender="sender" @uploadFile="uploadFileFn" @senderData="senderDataFn"
        :handleId="handleId" @editMessageLog="editMessageLogFn"></hx-wechat>
</template>

<script>
    import * as Api from '@/api/meet.js'
    export default {
        data() {
            return {
                /*** websocket 连接模块**/
                SOCKETAPI: "ws://192.168.1.119:18080/****/oK4rA67BPm3Bx-mB_4htXQ4daU1I",
                // WebSocket 连接次数
                socketCount: 0,
                // 最大重连次数
                maxSocketCount: 5,
                // 重连定时器
                socketTime: null,

                /***接受人-专家**/
                reciver: {
                    professorIcon: "https://b0.bdstatic.com/ugc/plwcNl7cCzbLNwBlvLGcuAa0ea704a48784b19a64e448c1870ccfb.jpg",
                    professorId: "27",
                    professorName: "吴一",
                },
                /***发送人**/
                sender: {
                    id: "oK4rA67****I",
                    avatar: "https://img2.baidu.com/it/u=4006946835,4011191580&fm=253&fmt=auto&app=138&f=JPEG?w=682&h=560",
                    name: "婉约",
                },
                /*** 聊天记录**/
                handleId: 'false', //正在操作某聊天记录的操作栏 id 

            }
        },
        onShow() {
            this.initSocket();
        },
        computed: {},
        methods: {
            // 链接 websocket
            async initSocket() {
                let app = this;

                //1.判断是否有websocet对象,有的话清空
                await app.closeSocket();
                //2.创建websocket 
                let socket = uni.connectSocket({
                    url: app.SOCKETAPI,
                    success(res) {
                        console.log('websocket 创建成功', res);
                    },
                })

                //1.1 把socket 数据存在store里
                app.$store.commit('setPropName', {
                    propName: 'socket',
                    value: socket
                });
                //3。监听WebSocket连接打开事件
                app.$store.state.socket.onOpen((res) => {
                    console.info("监听WebSocket连接打开事件", res)
                    clearInterval(app.socketTime)
                });
                //4.监听WebSocket接受到服务器的消息事件
                app.$store.state.socket.onMessage((res) => {
                    console.log('监听WebSocket接受到服务器的消息事件', res)
                    app.getMessageLogListFn();
                });
                // 5.监听连接关闭close
                app.$store.state.socket.onClose((e) => {
                    console.log('WebSocket连接关闭', app.$store.state.zdColse);
                    app.$store.commit('setPropName', {
                        propName: 'socket',
                        value: null
                    });
                    if (!app.$store.state.zdColse) {
                        clearInterval(app.socketTime)
                        app.reSocket()
                    }

                })
                //5.连接失败
                this.$store.state.socket.onError((e) => {
                    console.log('WebSocket连接打开失败,请检查!2222!!', e);
                    clearInterval(app.socketTime)
                    app.closeSocket('error')
                })
            },
            //重新连接
            reSocket() {
                let app = this;
                app.socketCount++
                console.log("开始断线重连!!!!!!!!!!!");
                if (app.socketCount > app.maxSocketCount) {
                    console.log('WebSocket 重连次数已达上限,开始每隔30秒重连一次');
                    uni.showModal({
                        content: '请检查网络是否异常,请重新进入',
                        showCancel: false,
                        success: function(res) {
                            if (res.confirm) {
                                //退到首页,退到想象的页面
                                app.$navTo('pages/index/index')
                            }
                        }
                    });
                    return false
                }
                if (app.socketTime) {
                    clearInterval(app.socketTime)
                }
                app.socketTime = setTimeout(() => {
                    app.initSocket()
                }, 3000)

            },
            //关闭socket
            closeSocket(type) {
                let app = this;
                console.log('关闭socket:', type, this.$store.state.socket)
                //1. 判断是否有websocet对象,有的话清空
                if (this.$store.state.socket) {
                    console.log('执行:', this.$store.state.socket.close)
                    //2.关闭websocet
                    this.$store.state.socket.close({
                        success: () => {
                            console.log('WebSocket关闭成功');
                        },
                        fail: (err) => {
                            console.error('WebSocket关闭失败', err);
                            //2.1 关闭失败的话,则去首页
                            if (err.errMsg && err.errMsg.indexOf('fail:close wcwss return fail') != -1) {
                                if (type == 'error') {
                                    uni.showModal({
                                        content: '请检查网络是否异常,请重新进入',
                                        showCancel: false,
                                        success: function(res) {
                                            if (res.confirm) {
                                                app.$navTo('pages/index/index')
                                            }
                                        }
                                    });
                                }
                                return false
                            }

                        }
                    });
                }
            },
            //清楚Socket
            clearSocket() {
                const app = this
                clearInterval(app.socketTime)
                this.$store.commit('setPropName', {
                    propName: 'zdColse',
                    value: true
                });
                app.closeSocket()

            },
            //获取咨询信息历史记录
            async getMessageLogListFn() {
                let message = await Api.getMessageLogList({
                    professorId: this.reciver.professorId,
                    customerId: this.sender.openid
                })
                if (message.code == 200) {
                    let arr = message.data.records || []
                    this.isHandleId = null
                    // 把数据更新到store里
                    this.$store.commit('setPropName', {
                        propName: 'conversitionList',
                        value: arr
                    });
                } else {
                    // 把数据更新到store里
                    this.$store.commit('setPropName', {
                        propName: 'conversitionList',
                        value: []
                    });
                }
            },
            //发送数据
            senderDataFn(conversition) {
                // 发送数据接口
                Api.sendMessage(conversition).then(res => {
                    this.handleId = null
                    this.$refs.hxwechat.quoteDel()

                })
            },
            //修改咨询信息历史记录(引用,撤回,删除等)
            editMessageLogFn(conversition) {
                let app = this;
                Api.editMessageLog(conversition).then(res => {
                    if (conversition.operate == 3) {
                        app.getMessageLogListFn();
                    }
                    this.handleId = null
                })
            },
            //上传图片,视频,文件 接口
            uploadFileFn({
                item,
                fileObj,
                urls
            }) {
                const app = this

                return new Promise((resolve, reject) => {
                    //1.上传图片 到服务器
                    uni.uploadFile({
                        url: `http://192.168.1.119:18080/****/attachUpload`, //需要传图片的后台接口
                        filePath: urls, // 上传图片 通过uni-app的uni-file-picker组件 函数返回
                        name: 'file', //文件名字
                        header: {
                            openId: this.sender.openId,
                            appletSessionId: this.reciver.professorId,
                        },
                        success: res => {
                            let result = JSON.parse(res.data);
                            console.log('上传文件接口返回:', result.code)
                            //1.1 上传接口失败后
                            if (result.code != 200) {
                                console.log('1111')
                                setTimeout(() => {
                                    uni.showToast({
                                        title: `发送${fileObj.type}失败`,
                                        duration: 3500,
                                        icon: 'none'
                                    });
                                    app.$refs.hxwechat.sendInfo(null, 'errorUploadFile');
                                }, 100)
                                return false
                            }
                            //1.2 请求成功后  把数据发送 聊天的接口
                            let conversition = JSON.parse(JSON
                                .stringify(item))
                            conversition.content = result.data.url
                            conversition.fileSize = result.data.fileSize
                            conversition.fileName = conversition.fileName ? conversition
                                .fileName :
                                result.data.fileName
                            //2. 发送去聊天
                            app.$refs.hxwechat.sendInfo(conversition, 'successUploadFile');
                            resolve(result)

                        },
                        fail: e => {
                            console.log(e)
                        }
                    });

                });
            }
        }

    }
</script>

<style>
</style>

问题

  1. 一旦聊天视频多了,就会出现同一页面存在多个video时,video无法正常播放一直在加载转圈的问题 视频呈现黑色 不建议同个页面使用多个video组件,建议不超过3个vide如果要实现video列表功能,请进行优化(点击images时让video播放,所以这边最好是上传视频的时候,可以截取第一帧当封面,可以参考下面的代码,不过这个第一帧当封面这个需要后端的配合,所以我插件源码代码注释,用的是video,大家自行解决 下面简单的说明
<view v-for="(item, index) in getConversitionList" :key="item.id">
    <view v-if="item.type === 'video'" class="mineSendVideo contentStyle" @click="handlePlayClick(item, index)">
      <video v-if="playingIndex === index" :src="item.content" controls></video>
      <image v-else src="https://img0.baidu.com/it/u=868358166,284066207&fm=253&fmt=auto&app=138&f=JPEG?w=285&h=300" @click="handlePlayClick(item, index)"></image>
    </view>
</view>
 getConversitionList: [
        { id: 1, type: 'video', content: "http://192.168.1.119:18080/attach/2024/11/19/LEMHtb2yyh0nbd09781d997ae492f8d0e0860cbd2f53_20241119095153000073953.mp4" },
        { id: 1, type: 'video', content: "http://192.168.1.119:18080/attach/2024/11/20/hjA3YD2L49sobd09781d997ae492f8d0e0860cbd2f53_20241120091858000082940.mp4" },
        { id: 2, type: 'video', content: "http://192.168.1.119:18080/attach/2024/11/19/视频_20241119100315000077705.mp4" }
      ],
playingIndex: -1 // 用于记录当前播放的视频索引
methods: {
  handlePlayClick(item, index) {
    // 如果当前点击的视频已经在播放,则停止播放
    if (this.playingIndex === index) {
      this.playingIndex = -1;
    } else {
      // 否则,播放当前点击的视频
      this.playingIndex = index;
    }
  }
}
  1. 1

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何SDK数据,接口请求数据格式插件有说明

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问