# 一、辅助功能(包括单聊和群的一些其它功能)
# 1. 修改头像昵称头像显示问题
如果是上传到本地服务器,服务器头像地址是以/开头,需要加上域名
在页面 /pages/setpageInfo/setpageInfo.vue
onLoad(e) {
...
// 动态生成数据
if(e.action){
this.setdata.action = e.action;
if(this.setdata.action == 'chatset'){
...
}else if(this.setdata.action == 'chatpageset'){
...
}else if(this.setdata.action == 'groupQrcode'){
...
}else if(this.setdata.action == 'autoAddGroup'){
...
}else if(this.setdata.action == 'userinfoSet'){
console.log('账号信息设置', this.me);
this.me.avatar = this.me.avatar.startsWith('/') ?
requestUrl.http + this.me.avatar : this.me.avatar;
this.chatpageset = this.me;
}else if(this.setdata.action == 'avatarCut'){
...
}
}
},
# 2. 查找聊天内容(单聊或者群的聊天记录)
# ① 进入搜索聊天页面
在页面 /pages/setpageInfo/setpageInfo.vue
clickCell(title, type, url = ''){
console.log(title, type);
if(type == 'addUserToGroup'){
...
}else if(type == 'groupQrcode'){
...
}else if(type == 'switchTab' || type == 'navigateTo'){
...
}else if(type == 'deleteChatData'){
...
}else if(type == 'searhChatData'){
console.log('查找聊天内容',this.chatpageset);
let op = {
action: 'searhChatData',
title: encodeURIComponent('查找聊天内容'),
id: this.chatpageset.id,
name: encodeURIComponent(this.chatpageset.name),
avatar: encodeURIComponent(this.chatpageset.avatar),
chatType: this.chatpageset.chatType,
};
uni.reLaunch({
url:`/pages/chat/chat?arg=${encodeURIComponent(JSON.stringify(op))}`,
});
}
},
# ② 聊天页搜索聊天内容及展示
在页面 /pages/chat/chat.nvue
<template>
<view>
<!-- 导航栏或者搜索栏 -->
<view>
<!-- 搜索栏 -->
<view v-if="arg && arg.action == 'searhChatData'"
class="position-fixed left-0 right-0 top-0">
<!-- 状态栏 -->
<view :style="'height:' + statusBarHeight + 'px;'"></view>
<!-- 搜索框 -->
<!-- #ifdef MP -->
<view class="flex flex-row justify-center align-center px-3"
style="padding-top: 8rpx;padding-right: 220rpx;">
<!-- #endif -->
<!-- #ifndef MP -->
<view class="flex flex-row justify-center align-center px-3"
style="padding-top: 8rpx;">
<!-- #endif -->
<!-- 搜索框 -->
<u--input placeholder="查找聊天内容" prefixIcon="search"
prefixIconStyle="font-size:22px;color:#909399;"
confirmType="search" clearable
:maxlength="20" showWordLimit focus
:adjustPosition="false"
customStyle="background-color:#ffffff"
ref="searchInput"
@input="searhChatData"
@clear="clearSearch"></u--input>
<text class="font text-primary ml-2" @click="navigateBack">关闭</text>
</view>
</view>
<!-- 导航栏 -->
<chat-navbar v-else
...>
...
</chat-navbar>
</view>
<!-- 聊天内容区域 -->
...
<!-- 底部聊天输入区域 --><!-- 修改:添加ref获取textarea实例 -->
<view v-if="arg.action != 'searhChatData'"
...>
...
</view>
<!-- 弹出菜单 --><!-- 主要修改区域 -->
...
<!-- 提示用户正在录音的界面 -->
...
</view>
</template>
<script>
...
export default {
...
data() {
return {
...
// 新增原始数据副本
originalChatDataList: [],
}
},
mounted() {
...
},
onLoad(e) {
...
try{
...
if(chatdata.length){
// this.chatDataList = chatdata.map(v=>{
// return this.formatServerMsg(v);
// });
// 同时保存到原始数据和显示数据
this.originalChatDataList = chatdata.map(v => {
return this.formatServerMsg(v);
});
this.chatDataList = [...this.originalChatDataList]; // 使用浅拷贝
}else{
// 清空模拟数据
this.chatDataList = [];
this.originalChatDataList = [];
}
// 监听处理接收到的消息
...
// 监听删除了某条聊天记录
...
// 搜索聊天信息
if(this.arg.action && this.arg.action == 'searhChatData'){
console.log('搜索聊天信息');
}
}catch{
...
}
},
...,
computed:{
...,
chatContentStyle(){
let bottom = 0;
// 新增搜索聊天内容,去掉底部输入框的情况
if(this.arg && this.arg.action == 'searhChatData'){
let pbottom = this.bottomSafeAreaHeight == 0 ?
uni.upx2px(0) : this.bottomSafeAreaHeight;
bottom = pbottom;
}else{
// 正常处理
let pbottom = this.bottomSafeAreaHeight == 0 ?
uni.upx2px(12) : this.bottomSafeAreaHeight;
bottom = pbottom + uni.upx2px(90 + 12) + this.KeyboardHeight;
//如果是h5端用微信打开并且不需要导航栏的时候
if(this.isWeixinBrowser() && !this.h5WeiXinNeedNavbar){
this.fixedHeight = this.statusBarHeight + 0;
uni.setNavigationBarTitle({
title: this.groupname || this.pagetitle,
});
}
// #ifdef APP || MP
if(this.KeyboardHeight){
bottom = uni.upx2px(90 + 12 + 12) + this.KeyboardHeight;
}
// #endif
}
return `top:${this.fixedHeight}px;bottom:${bottom}px;`;
},
...,
},
methods: {
...,
// 搜索聊天记录
searhChatData(e){
console.log('搜索聊天记录关键字',e);
if(e){
// 搜索过滤-使用原始数据进行搜索
// console.log('使用原始数据进行搜索过滤',this.originalChatDataList);return;
const result = this.originalChatDataList.filter(v => {
if (!v.data) return false;
// 检查内容是否包含关键词
return v.data.includes(e);
});
this.chatDataList = result;
}else{
// 搜索关键词为空时,恢复显示所有数据
this.chatDataList = [...this.originalChatDataList];
}
},
// 新增:清除搜索
clearSearch() {
this.chatDataList = [...this.originalChatDataList];
},
},
}
</script>
# 二、群聊删除群成员功能
此功能仅群管理员可以操作
# 1. 群聊删除群成员接口说明
接口文档说明:三十五、删除群成员
# 2. 群聊删除群成员界面实现
在页面 /pages/setpageInfo/setpageInfo.vue
<template>
<view>
<!-- 导航栏 -->
...
<!-- 内容 -->
<view>
<!-- 聊天相关 -->
...
<!-- 聊天页设置 -->
...
<!-- 群二维码 -->
...
<!-- 扫码之后的页面 -->
...
<!-- 账号信息设置 -->
...
<!-- 头像剪裁500rpx * 500rpx -->
...
<!-- 删除群成员 -->
<view v-if="setdata.action == 'deleteUserFromGroup'">
<!-- 搜索部分 -->
<view class="position-fixed left-0 top-0 right-0"
:style="'height:' + fixedHeight + 'px;'">
<!-- 状态栏 -->
<view :style="'height:' + statusBarHeight + 'px;'"></view>
<!-- 搜索框及关闭 -->
<!-- #ifdef MP -->
<view class="flex flex-row align-center justify-center px-3"
style="padding-top: 8rpx;padding-right: 220rpx;">
<!-- #endif -->
<!-- #ifndef MP -->
<view class="flex flex-row align-center justify-center px-3"
style="padding-top: 8rpx;">
<!-- #endif -->
<!-- 搜索框 -->
<u--input placeholder="搜索群成员" prefixIcon="search"
prefixIconStyle="font-size:22px;color:#909399;"
confirmType="search" clearable
:maxlength="50" showWordLimit focus
:adjustPosition="false"
customStyle="background-color:#ffffff"
@input="searchGroupUser"
@clear="clearSearch"></u--input>
<!-- 关闭 -->
<text class="font text-primary ml-2" @click="navigateBack">关闭</text>
</view>
</view>
<!-- 占位 -->
<view :style="'height:' + fixedHeight + 'px;'"></view>
<!-- 群成员展示 -->
<view>
<u-cell-group>
<u-cell v-for="(item,index) in chatpageset.users"
:key="index">
<view slot="title" class="flex align-center"
@click="openUserInfo(item,'info')">
<u-avatar :src="item|avatarShow" shape="square"></u-avatar>
<!-- <u--image mode="widthFix"
:src="item|avatarShow"
width="40px" height="40px" radius="10rpx"></u--image> -->
<view class="flex flex-column justify-center ml-2 mr-1">
<text class="font-md">{{item.nickname||item.user.nickname||item.user.username}}</text>
<text class="font-small text-light-muted">
{{item.user.role == 'user' ? '' : '游客'}}
</text>
<!-- 用户全昵称展示 -->
<view v-if="item.user.role == 'user'"
class="flex flex-column justify-center mr-1">
<text v-if="item.nickname"
class="font-small text-light-muted">群昵称:{{item.nickname}}</text>
<text v-if="item.user.nickname"
class="font-small text-light-muted">账户昵称:{{item.user.nickname}}</text>
<text v-if="item.user.username"
class="font-small text-light-muted">账号:{{item.user.username}}</text>
</view>
</view>
</view>
<view slot="value">
<view @click="openUserInfo(item,'deleteGroupUser')">
<u-button text="删除" type="error" size="small"></u-button>
</view>
</view>
</u-cell>
</u-cell-group>
</view>
</view>
</view>
</view>
</template>
<script>
...
import GroupJs from '@/pages/setpageInfo/group.js';
export default {
mixins:[toolJs,updateUserInfoJs,avatarCutJs,GroupJs],
...,
onLoad(e) {
...
// 动态生成数据
if(e.action){
this.setdata.action = e.action;
if(this.setdata.action == 'chatset'){
...
}else if(this.setdata.action == 'chatpageset'){
...
}else if(this.setdata.action == 'groupQrcode'){
...
}else if(this.setdata.action == 'autoAddGroup'){
...
}else if(this.setdata.action == 'userinfoSet'){
...
}else if(this.setdata.action == 'avatarCut'){
...
}else if(this.setdata.action == 'deleteUserFromGroup'){
console.log('删除群成员', this.chatpageset.users);
let currentGroupInfo = uni.getStorageSync('currentGroupInfo');
if(currentGroupInfo){
// 群成员
currentGroupInfo = JSON.parse(currentGroupInfo);
this.chatpageset = currentGroupInfo;
console.log('删除群成员,当前群成员', this.chatpageset.users);
this.originalChatDataList = this.chatpageset.users;
this.chatpageset.users = [...this.originalChatDataList];
// 仅管理员可操作
if(!this.chatpageset.user_id) return this.navigateBack();
}
}
}
},
...,
methods: {
...,
clickCell(title, type, url = ''){
console.log(title, type);
if(type == 'addUserToGroup'){
...
}else if(type == 'groupQrcode'){
...
}else if(type == 'switchTab' || type == 'navigateTo'){
...
}else if(type == 'deleteChatData'){
...
}else if(type == 'searhChatData'){
...
}else if(type == 'deleteUserFromGroup'){
this.deleteUserFromGroup();
}
},
...
}
}
</script>
# 3. 群聊删除群成员功能实现
在文件 /pages/setpageInfo/group.js
import { requestUrl, uploadfileSaveType } from '@/common/mixins/configData.js';
export default{
data() {
return {
h5WeiXinNeedNavbar:false, // h5端微信上是否需要导航栏
statusBarHeight:0,//状态栏高度动态计算
fixedHeight:0, //占位:状态栏+导航栏
bottomSafeAreaHeight:0, // 底部安全距离
// 存一个聊天记录副本
originalChatDataList:[],
}
},
mounted() {
let info = uni.getSystemInfoSync();
this.statusBarHeight = info.statusBarHeight;
this.fixedHeight = this.statusBarHeight + uni.upx2px(90);
this.bottomSafeAreaHeight = info.safeAreaInsets.bottom;
},
destroyed() {
// 清除本地临时群信息
setTimeout(()=>{
uni.removeStorageSync('currentGroupInfo');
},800);
},
methods:{
// 点击群成员
openUserInfo(item,action){
console.log(item);
if(action == 'chatGroup'){
// 进入聊天页 传一下用户的信息过去在页面展示
const userchat = {
id: item.id,
name: item.name,
avatar: item.avatar,
chatType: 'group', // 单聊 single 群聊 group
};
uni.navigateTo({
url: `/pages/chat/chat?arg=${encodeURIComponent(JSON.stringify(userchat))}`,
});
}else if(action == 'deleteGroupUser'){
console.log('删除群成员',item);
uni.showModal({
content:`确定删除该群成员 [${item.nickname || item.user.nickname || item.user.username}] 吗?`,
success: (res) => {
if(res.confirm){
// 执行删除群成员
uni.$u.http.post(requestUrl.http + '/api/chat/groupDeleteUser',{
group_id: item.group_id,
user_id: item.user.id,
},{
header:{
token: this.me.token
}
}).then(res=>{
uni.showToast({title: res.data.data,icon:'success'});
setTimeout(()=>{
this.navigateBack();
},1500);
}).catch(err=>{
uni.showToast({title: err.data.data,icon:'error'});
});
}
}
})
}else{
uni.navigateTo({
url: `/pages/userinfo/userinfo?uuid=${item.user.uuid}&action=${action}`,
});
}
},
// 清除搜索群成员
clearSearch(){
this.chatpageset.users = [...this.originalChatDataList];
},
// 搜索群成员
searchGroupUser(e){
console.log('搜索群成员',e);
if(e){
const result = this.originalChatDataList.filter(v=>{
if(v.nickname){
if(v.nickname.includes(e)){
return true;
}else{
if(v.user.nickname){
if(v.user.nickname.includes(e)){
return true;
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
}
}else{
if(v.user.nickname){
if(v.user.nickname.includes(e)){
return true;
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
};
});
this.chatpageset.users = result;
}else{
this.chatpageset.users = [...this.originalChatDataList];
}
},
// 删除群成员
deleteUserFromGroup(){
console.log('删除群成员',this.chatpageset);
let op = {
action: 'deleteUserFromGroup',
title: encodeURIComponent('删除群成员'),
id: this.chatpageset.id,
name: encodeURIComponent(this.chatpageset.name),
avatar: encodeURIComponent(this.chatpageset.avatar),
chatType: this.chatpageset.chatType,
};
// 由于跳转群信息会丢失,要么重新请求要么存一份
// 存一份副本
uni.setStorageSync('currentGroupInfo', JSON.stringify(this.chatpageset));
// 跳转
uni.redirectTo({
url:`/pages/setpageInfo/setpageInfo?action=${op.action}&title=${op.title}&id=${op.id}&name=${op.name}&avatar=${op.avatar}&chatType=${op.chatType}`,
});
},
// 加入群聊
addGroupfn(){
console.log('加入群聊,看管理员怎么设置的');
},
// 解散群或者退群或者删除好友
deleteOrQuitGroupfn(){
if(this.chatpageset.chatType == 'group'){
const arg = {
apiurl: '/api/chat/groupDeleteOrQuit',
data: {
id: this.chatpageset.id,
},
};
this.groupUpdate(arg).then(res => {
uni.showToast({title:'操作成功', icon:'none'});
if(this.chatpageset.user_id){
return this.navigateBack();
}else{
uni.switchTab({
url:'/pages/xiaoxi/xiaoxi'
});
}
});
}else if(this.chatpageset.chatType == 'single'){
console.log('删除好友');
uni.showModal({
content:`确定删除这个好友吗?`,
success: (res) => {
if(res.confirm){
// 执行删除
uni.$u.http.post(requestUrl.http +
`/api/chat/deletegoodfriend/${this.chatpageset.id}`,{},{
header:{
token: this.me.token,
}
}).then(res=>{
uni.showToast({title:res.data.data, icon:'none'});
// 消息页清除聊天记录
this.chatClass.clearChatInfo(this.chatpageset.id,
this.chatpageset.chatType).then(()=>{
// 清空完成之后聊天页的数据也要清空
uni.$emit('clearChatInfo');
});
// 调转消息页
setTimeout(()=>{
uni.switchTab({
url:'/pages/xiaoxi/xiaoxi'
});
},2000);
}).catch(err=>{
uni.showToast({title:err.data.data, icon:'none'});
});
}
}
});
}
},
// 获取群资料
getgroupinfo(){
uni.$u.http.get(requestUrl.http + `/api/chat/groupinfo/${this.chatpageset.id}`, {
header: {
token: this.me.token,
},
}).then(res => {
console.log('服务器返回群资料', res);
let info = res.data.data;
this.chatpageset.user_id = info.user_id;
this.chatpageset.remark = info.remark;
this.chatpageset.invite_confirm = info.invite_confirm;
this.chatpageset.users = info.group_users;
//console.log('显示到页面的群资料',this.chatpageset);
this.chatpageset.user_id = this.chatpageset.user_id == this.me.id ? true : false;
// 我在群里面的昵称
let me_index = this.chatpageset.users.findIndex(v => v.user_id == this.me.id);
if(me_index != -1){
this.chatpageset.nickname = this.chatpageset.users[me_index].nickname;
}
// 群头像
this.chatpageset.avatar = info.avatar;
// 群名称
this.chatpageset.name = info.name;
console.log('显示到页面的群资料',this.chatpageset);
}).catch(err =>{
this.navigateBack();
});
},
// 修改我在群里面的昵称
groupnickname(){
const arg = {
apiurl: '/api/chat/groupnickname',
data: {
id: this.chatpageset.id,
nickname: this.chatpageset.nickname,
},
};
this.groupUpdate(arg).then(res => {
uni.showToast({title:'群昵称设置成功', icon:'none'});
this.getgroupinfo();
});
},
// 更新群公告
groupremark(){
const arg = {
apiurl: '/api/chat/groupremark',
data: {
id: this.chatpageset.id,
remark: this.chatpageset.remark,
},
};
this.groupUpdate(arg).then(res => {
uni.showToast({title:'群公告修改成功', icon:'none'});
});
},
// 更新群名称
groupUpdateName(){
const arg = {
apiurl: '/api/chat/groupUpdateName',
data: {
id: this.chatpageset.id,
name: this.chatpageset.name,
},
};
this.groupUpdate(arg).then(res => {
uni.showToast({title:'群名称修改成功', icon:'none'});
// 使用vuex兼容H5端nvue页面标题
this.$store.dispatch('groupUpdateName',{
name: this.chatpageset.name
});
});
},
// 更新群信息
groupUpdate(arg){
return new Promise((resolve,reject) => {
uni.$u.http.post(requestUrl.http + `${arg.apiurl}`, arg.data , {
header: {
token: this.me.token,
},
}).then(res => {
console.log('服务器返回群名称更新结果', res);
// if(res.data.data == 'ok'){
// resolve();
// }
resolve(res);
}).catch(err =>{
uni.showModal({
content:err.data.data,
showCancel:false,
confirmText:'我知道了'
});
});
});
},
}
}
# 三、邀请加入群聊或者自己申请加群
# 1. 邀请加入群聊或者自己申请加群接口说明
接口说明:三十六、邀请人进群(会进行websocket通知)
# 2. 加入群聊界面实现
在页面 /pages/setpageInfo/setpageInfo.vue
<template>
<view>
<!-- 导航栏 -->
<chat-navbar v-if="setdata.action != 'avatarCut' &&
setdata.action != 'deleteUserFromGroup' && setdata.action != 'addUserFromGroup'"
:title="title" :fixed="true" :showPlus="false" :showUser="false" :showBack="true"
navbarClass="bg-white" :h5WeiXinNeedNavbar="false">
</chat-navbar>
<!-- 内容 -->
<view>
<!-- 聊天相关 -->
...
<!-- 聊天页设置 -->
...
<!-- 群二维码 -->
...
<!-- 扫码之后的页面 -->
...
<!-- 账号信息设置 -->
...
<!-- 头像剪裁500rpx * 500rpx -->
...
<!-- 删除群成员 -->
...
<!-- 添加好友进群 -->
<view v-if="setdata.action == 'addUserFromGroup'">
<!-- 搜索栏 -->
<view class="position-fixed left-0 top-0 right-0 bg-white"
:style="'height:' + fixedHeight + 'px;'"
style="background-color: #ffffff;z-index: 100;">
<!-- 状态栏 -->
<view :style="'height:' + statusBarHeight + 'px;'"></view>
<!-- 搜索框及关闭 -->
<!-- #ifdef MP -->
<view class="flex flex-row align-center justify-center px-3"
style="padding-top: 8rpx;padding-right: 220rpx;">
<!-- #endif -->
<!-- #ifndef MP -->
<view class="flex flex-row align-center justify-center px-3"
style="padding-top: 8rpx;">
<!-- #endif -->
<!-- 搜索框 -->
<u--input placeholder="搜索好友" prefixIcon="search"
prefixIconStyle="font-size:22px;color:#909399;"
confirmType="search" clearable
:maxlength="50" showWordLimit focus
:adjustPosition="false"
customStyle="background-color:#ffffff"
@input="searchGoodFriend"
@clear="clearSearch"></u--input>
<!-- 关闭 -->
<text class="font text-primary ml-2" @click="navigateBack">关闭</text>
</view>
</view>
<!-- 占位 -->
<view :style="'height:' + fixedHeight + 'px;'"></view>
<!-- 好友展示 -->
<view>
<u-cell-group :border="false">
<u-cell v-for="(item,index) in chatpageset.users"
:key="index">
<view slot="title" class="flex align-center">
<u-avatar :src="item|avatarShow" shape="square"></u-avatar>
<!-- <u--image mode="widthFix"
:src="item|avatarShow"
width="40px" height="40px" radius="10rpx"></u--image> -->
<view class="flex flex-column justify-center ml-2 mr-1">
<text class="font-md">{{item.beizhu ||item.name||item.username}}</text>
<!-- 用户全部称呼的显示 -->
<view
class="flex flex-column justify-center mr-1">
<text v-if="item.beizhu"
class="font-small text-light-muted">备注:{{item.beizhu}}</text>
<text v-if="item.name"
class="font-small text-light-muted">昵称:{{item.name}}</text>
<text v-if="item.username"
class="font-small text-light-muted">账号:{{item.username}}</text>
</view>
</view>
</view>
<view slot="value">
<view @click="openUserInfo(item,'addGroupUser')">
<u-button text="加入群聊" type="success" size="small"></u-button>
</view>
</view>
</u-cell>
</u-cell-group>
</view>
</view>
</view>
</view>
</template>
<script>
...
export default {
...
onLoad(e) {
...
// 动态生成数据
if(e.action){
this.setdata.action = e.action;
if(this.setdata.action == 'chatset'){
...
}else if(this.setdata.action == 'chatpageset'){
...
}else if(this.setdata.action == 'groupQrcode'){
...
}else if(this.setdata.action == 'autoAddGroup'){
...
}else if(this.setdata.action == 'userinfoSet'){
...
}else if(this.setdata.action == 'avatarCut'){
...
}else if(this.setdata.action == 'deleteUserFromGroup'){
...
}else if(this.setdata.action == 'addUserFromGroup'){
console.log('---添加好友进群---');
this.chatpageset.id = e.id;
this.chatpageset.chatType = e.chatType;
this.chatpageset.avatar = decodeURIComponent(e.avatar);
this.chatpageset.name = decodeURIComponent(e.name);
// 获取好友列表
uni.$u.http.get(requestUrl.http + `/api/chat/goodfriendlist/1`,{
header:{
token: this.me.token,
}
}).then(res=>{
console.log('服务器获取好友列表', res.data.data);
if(res.data.data.count > 0){
let friendlist = res.data.data.rows.newList;
let arr = [];
for(let i = 0; i < friendlist.length; i++){
friendlist[i].list.forEach(v=>{
arr.push(v);
});
}
this.originalChatDataList = arr;
this.chatpageset.users = [...this.originalChatDataList];
console.log('---好友列表', this.originalChatDataList);
}else{
this.originalChatDataList = [];
this.chatpageset.users = [];
}
});
}
}
},
...,
methods: {
clickCell(title, type, url = ''){
console.log(title, type);
if(type == 'addUserToGroup'){
// 单独建群
if(this.chatpageset.chatType == 'single'){
...
}else if(this.chatpageset.chatType == 'group'){
this.addUserFromGroup();
}
}else if(type == 'groupQrcode'){
...
}else if(type == 'switchTab' || type == 'navigateTo'){
...
}else if(type == 'deleteChatData'){
...
}else if(type == 'searhChatData'){
...
}else if(type == 'deleteUserFromGroup'){
...
}
},
...
}
}
</script>
# 3. 加入群聊功能实现
在文件 /pages/setpageInfo/group.js
...
export default {
...
methods: {
// 添加好友进群
addGroupUser(item){
console.log('添加好友进群请求服务器', this.chatpageset);
uni.$u.http.post(requestUrl.http + `/api/chat/groupInviteUser`,{
group_id: this.chatpageset.id,
user_id: item.friend_id,
inviteuser_id: this.me.id,
},{
header:{
token: this.me.token,
}
}).then(res=>{
uni.showToast({title: res.data.data, icon:'success'});
setTimeout(()=>{
this.navigateBack();
},1500);
}).catch(err=>{
uni.showToast({title: err.data.data, icon:'error'});
});
},
// 搜索好友
searchGoodFriend(e){
console.log('搜索好友',e);
if(e){
console.log('查询的好友数据---',this.originalChatDataList);
const result = this.originalChatDataList.filter(v=>{
if(v.beizhu){
if(v.beizhu.includes(e)){
return true;
}else{
if(v.name){
if(v.name.includes(e)){
return true;
}else{
if(v.username){
if(v.username.includes(e)){
return true;
}else{
return false;
}
}
}
}else{
if(v.username){
if(v.username.includes(e)){
return true;
}else{
return false;
}
}
}
}
}else{
if(v.name){
if(v.name.includes(e)){
return true;
}else{
if(v.username){
if(v.username.includes(e)){
return true;
}else{
return false;
}
}
}
}else{
if(v.username){
if(v.username.includes(e)){
return true;
}else{
return false;
}
}
}
}
});
this.chatpageset.users = result;
}else{
this.chatpageset.users = [...this.originalChatDataList];
}
},
// 往群里面加人
addUserFromGroup(){
console.log('往当前的群里面加人',this.chatpageset);
let op = {
action: 'addUserFromGroup',
title: encodeURIComponent('添加好友进群'),
id: this.chatpageset.id,
name: encodeURIComponent(this.chatpageset.name),
avatar: encodeURIComponent(this.chatpageset.avatar),
chatType: this.chatpageset.chatType,
};
// 跳转
uni.redirectTo({
url:`/pages/setpageInfo/setpageInfo?action=${op.action}&title=${op.title}&id=${op.id}&name=${op.name}&avatar=${op.avatar}&chatType=${op.chatType}`,
});
},
// 点击群成员
openUserInfo(item,action){
console.log(item);
if(action == 'chatGroup'){
...
}else if(action == 'deleteGroupUser'){
...
}else if(action == 'addGroupUser'){
this.addGroupUser(item);
}else{
...
}
},
// 搜索群成员
searchGroupUser(e){
console.log('搜索群成员',e);
if(e){
const result = this.originalChatDataList.filter(v=>{
if(v.nickname){
if(v.nickname.includes(e)){
return true;
}else{
if(v.user.nickname){
if(v.user.nickname.includes(e)){
return true;
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
}
}else{
if(v.user.nickname){
if(v.user.nickname.includes(e)){
return true;
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
}else{
if(v.user.username){
if(v.user.username.includes(e)){
return true;
}else{
return false;
}
}
}
};
});
this.chatpageset.users = result;
}else{
this.chatpageset.users = [...this.originalChatDataList];
}
},
// 清空搜索
...,
// 删除群成员
...,
...
}
}
# 4. 优化加好友入群(不在群里面的好友才有加群按钮)
- 在文件
/pages/setpageInfo/group.js
data(){
return {
...
// 群成员
groupUsers: null,
}
},
methods: {
...
// 往群里面加人
addUserFromGroup(){
...
// 存一份群信息
uni.setStorageSync('currentGroupInfo', JSON.stringify(this.chatpageset));
// 跳转
...
},
...
},
- 在页面
/pages/setpageInfo/setpageInfo.vue
<view slot="value" v-if="groupUsers && groupUsers.length">
<view class="flex flex-column align-center justify-center">
<view v-if="item.isNotGroupUser"
@click="openUserInfo(item,'addGroupUser')">
<u-button text="加入群聊" type="success" size="small"></u-button>
</view>
<text v-else
class="text-light-muted font-small">已是群成员</text>
</view>
</view>
...
onLoad(e) {
...
}else if(this.setdata.action == 'addUserFromGroup'){
this.chatpageset.id = e.id;
this.chatpageset.chatType = e.chatType;
this.chatpageset.avatar = decodeURIComponent(e.avatar);
this.chatpageset.name = decodeURIComponent(e.name);
// 从缓存拿一下群信息
let currentGroupInfo = uni.getStorageSync('currentGroupInfo');
currentGroupInfo = currentGroupInfo ? JSON.parse(currentGroupInfo) : null;
this.groupUsers = currentGroupInfo ? currentGroupInfo.users : null;
console.log('从缓存拿一下群信息中的群成员',this.groupUsers);
// 获取好友列表
uni.$u.http.get(requestUrl.http + `/api/chat/goodfriendlist/1`,{
header:{
token: this.me.token,
}
}).then(res=>{
console.log('服务器返回的好友列表', res.data.data);
if(res.data.data.count > 0){
let friendlist = res.data.data.rows.newList;
let arr = [];
for(let i=0; i < friendlist.length; i++){
friendlist[i].list.forEach(v=>{
arr.push(v);
});
}
// 针对好友加一个是否是群成员字段
if(this.groupUsers && this.groupUsers.length){
let groupUsersIds = this.groupUsers.map(v=> v.user_id);
console.log('群成员id集合',groupUsersIds);
arr.forEach(v=>{
if(!groupUsersIds.includes(v.friend_id)){
v.isNotGroupUser = true;
}
});
}
this.originalChatDataList = arr;
this.chatpageset.users = [...this.originalChatDataList];
console.log('---好友列表数据', this.originalChatDataList);
}else{
this.originalChatDataList = [];
this.chatpageset.users = [];
}
});
}
}
# 四、根据群设置来进行加群处理
# 1. 进群设置接口说明
接口说明:三十七、进群设置
# 2. 界面部分
在文件 /pages/setpageInfo/group.js
data(){
return {
...
// 进群设置
addGroupSet: 0,
}
},
methods: {
// 进群设置
addGroupSetfun(value){
this.addGroupSet = value;
// 提交给服务器
uni.$u.http.post(requestUrl.http + `/api/chat/groupAddUserSet`,{
group_id: this.chatpageset.id,
addGroupSet: value,
},{
header:{
token: this.me.token,
}
}).then(res=>{
uni.showToast({title: res.data.data, icon:'success'});
}).catch(err=>{
uni.showToast({title: err.data.data, icon:'none'});
});
},
...
// 获取群资料
getgroupinfo(){
return new Promise((resolve,reject)=>{
uni.$u.http.get(...)
.then(res => {
...
console.log('显示到页面的群资料',this.chatpageset);
resolve(this.chatpageset);
}).catch(err =>{
...
});
});
},
...
}
在页面 /pages/setpageInfo/setpageInfo.vue
...
<!-- 我在群里面的昵称 -->
<view v-if="chatpageset.user_id">
<!-- 进群设置 -->
<u-cell-group>
<u-cell>
<view slot="title">
<view class="mb-2">
<text class="font-weight-bold">进群设置</text>
</view>
<view class="flex flex-nowrap align-center justify-center">
<u-button type="primary" text="任何人都可以进群"
@click="addGroupSetfun(0)"
size="small" :plain="addGroupSet == 0 ? false : true"></u-button>
<u-button type="primary" text="需先登录"
@click="addGroupSetfun(2)"
size="small" :plain="addGroupSet == 2 ? false : true"
:customStyle="{'margin-left':'10rpx','margin-right':'10rpx'}"></u-button>
<u-button type="primary" text="需群主同意"
@click="addGroupSetfun(1)"
size="small" :plain="addGroupSet == 1 ? false : true"></u-button>
</view>
</view>
</u-cell>
</u-cell-group>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
</view>
<!-- 群名称 -->
...
onLoad(e) {
...
}else if(this.setdata.action == 'chatpageset'){
...
// 设置相关
this.chatpageset = {
// 单聊
...
// 群聊还有以下字段
user_id: 0,
remark: '',
invite_confirm: getChatPageSet && getChatPageSet.invite_confirm,
}
console.log('设置相关', this.chatpageset);
// 群聊设置
if(this.chatpageset.chatType == 'group'){
this.getgroupinfo().then(res=>{
this.addGroupSet = res.invite_confirm;
});
}
}...
}
# 3. 当进群设置为:需群主同意,群主收到处理加群的通知
关于这部分内容跟后端有关,感兴趣的同学可以学习一下我们 第四期第一季的课程 ,这里就不做过多介绍了。
# 4. 群主处理加群申请
# 1. 同意或者拒绝用户进群接口
接口说明:三十八、同意或者拒绝用户进群(同意进群会进行websocket通知)
# 2. 界面部分
在页面 /pages/setpageInfo/setpageInfo.vue
<template>
<view>
<!-- 导航栏 -->
...
<!-- 内容 -->
<view>
<!-- 聊天相关 -->
...
<!-- 聊天页设置 -->
...
<!-- 群二维码 -->
...
<!-- 扫码之后的页面 -->
...
<!-- 账号信息设置 -->
...
<!-- 头像剪裁500rpx * 500rpx -->
...
<!-- 删除群成员 -->
...
<!-- 添加好友进群 -->
...
<!-- 加群申请处理 -->
<view v-if="setdata.action == 'applyAddGroup'"
class="p-3">
<!-- 要加入的群 -->
<view>
<!-- 要加入的群 -->
<view class="flex flex-column mb-5">
<text class="font-sm text-light-muted">要申请加入的群</text>
<view class="flex align-center mt-2">
<text class="font-weight-bold">{{chatpageset.name}}</text>
</view>
</view>
</view>
<!-- 申请信息 -->
<view v-if="chatpageset.applydesc">
<!-- 进群方式 -->
<view v-if="chatpageset.applydesc.addGroupUserType"
class="flex flex-column mb-5">
<text class="font-sm text-light-muted">进群方式</text>
<text class="font-weight-bold mt-2">{{chatpageset.applydesc.addGroupUserType}}</text>
</view>
<!-- 邀请人 -->
<view v-if="chatpageset.applydesc.inviteUser"
class="flex flex-column mb-5">
<text class="font-sm text-light-muted">邀请人</text>
<text class="font-weight-bold mt-2">{{chatpageset.applydesc.inviteUser}}</text>
</view>
<!-- 邀请加入时间 -->
<view v-if="chatpageset.applydesc.addGroupTime"
class="flex flex-column mb-5">
<text class="font-sm text-light-muted">邀请加入时间</text>
<text class="font-weight-bold mt-2">{{addGroupTime}}</text>
</view>
<!-- 加群说明 -->
<view v-if="chatpageset.applydesc.addGroupDesc"
class="flex flex-column mb-5">
<text class="font-sm text-light-muted">加群申请说明</text>
<text class="font-weight-bold mt-2">{{chatpageset.applydesc.addGroupDesc}}</text>
</view>
</view>
<!-- 申请人 -->
<view>
<!-- 申请人昵称头像 -->
<view class="flex flex-column mb-5">
<text class="font-sm text-light-muted">申请人</text>
<view class="flex align-center mt-2"
@click="openUserInfo(chatpageset.item,chatpageset.item.action)">
<u-avatar :src="chatpageset.applyUseravatar" shape="square"
customStyle="border:1px solid #eeeeee;" :size="36"></u-avatar>
<text class="font text-primary ml-2">{{chatpageset.applyUsername}}</text>
</view>
</view>
</view>
<!-- 处理 -->
<view class="position-fixed left-0 right-0 bottom-0 bg-white px-3"
style="z-index: 100;height: 120rpx;">
<!-- 处理按钮 -->
<view class="flex justify-between align-center">
<u-button type="success" text="同意进群" class="mr-1"
@click="doApplyaddGroup('agree')"></u-button>
<u-button type="warning" text="不同意进群" class="ml-1"
@click="doApplyaddGroup('no')"></u-button>
</view>
</view>
<!-- 占位 -->
<view style="height: 120rpx;"></view>
</view>
</view>
</view>
</template>
<script>
...
import parseTimeJs from '@/common/mixins/parseTime.js';
export default {
mixins:[...,parseTimeJs],
data() {
return {
...
chatpageset:{
...,
applydesc:'',
},
}
},
onLoad(e) {
...
// 动态生成数据
if(e.action){
this.setdata.action = e.action;
if(this.setdata.action == 'chatset'){
...
}else if(this.setdata.action == 'chatpageset'){
...
}else if(this.setdata.action == 'groupQrcode'){
...
}else if(this.setdata.action == 'autoAddGroup'){
...
}else if(this.setdata.action == 'userinfoSet'){
...
}else if(this.setdata.action == 'avatarCut'){
...
}else if(this.setdata.action == 'deleteUserFromGroup'){
...
}else if(this.setdata.action == 'addUserFromGroup'){
...
}else if(this.setdata.action == 'applyAddGroup'){
this.chatpageset.id = e.id;
this.chatpageset.chatType = e.chatType;
this.chatpageset.avatar = decodeURIComponent(e.avatar);
this.chatpageset.name = decodeURIComponent(e.name);
// 申请加群的人的id
this.chatpageset.applyUserid = e.applyUserid;
// 申请信息
this.chatpageset.applydesc = e.applydesc ? JSON.parse(e.applydesc) : null;
// 申请信息中有一个msgidKey对应消息页消息列表某条消息的id的key
this.chatpageset.msgidKey = this.chatpageset.applydesc ? this.chatpageset.applydesc.msgidKey : '';
console.log('对应消息页的消息id的key',this.chatpageset.msgidKey);
// 申请头像称呼
this.chatpageset.applyUseravatar = e.applyUseravatar.startsWith('/') ?
requestUrl.http + e.applyUseravatar : e.applyUseravatar ;
this.chatpageset.applyUsername = e.applyUsername;
this.chatpageset.applyUseruuid = e.applyUseruuid;
// 点击申请人跳转到用户详情
this.chatpageset.item = {
action: 'info',
uuid: e.applyUseruuid,
};
}
}
},
...,
computed:{
...
// h5端在微信打开
...,
// 申请加入群聊处理时间显示
addGroupTime(){
if(this.chatpageset && this.chatpageset.applydesc &&
this.chatpageset.applydesc.addGroupTime){
return parseTimeJs.gettime(this.chatpageset.applydesc.addGroupTime);
}
return ``;
},
},
...
}
</script>
# 3. 交互部分
在文件 /pages/setpageInfo/group.js
...
export default {
...,
methods: {
// 处理加群申请
doApplyaddGroup(type){
uni.showLoading({
title:'处理中...',
});
uni.$u.http.post(requestUrl.http + `/api/chat/groupAgreeOrNo`,{
group_id: this.chatpageset.id,
user_id: this.chatpageset.applyUserid,
type: type,
},{
header:{
token: this.me.token,
}
}).then(res=>{
uni.showToast({title: res.data.data, icon:'success'});
// 一旦处理完成,则把消息页的提示信息删除
console.log('对应消息页的消息id的key',this.chatpageset.msgidKey);
// 删除消息页某条信息
this.chatClass.deleteChatInfo(this.chatpageset.msgidKey, 'single');
// 返回上一页
setTimeout(()=>{
this.navigateBack();
},1500);
}).catch(err=>{
uni.showToast({title: err.data.data, icon:'none'});
}).finally(()=>{
uni.hideLoading();
});
},
// 进群设置
...,
// 添加好友进群
addGroupUser(item){
...
uni.$u.http.post(...).then(...).catch(err=>{
uni.showToast({title: err.data.data, icon:'none',duration:5000});
});
},
...
}
}
# 五. 接收消息给点提示音
- 提示音素材:
https://docs-51yrc-com.oss-cn-hangzhou.aliyuncs.com/chat/audio/getMessage.mp3 - 播放声音有两种方式:
- uni-app官方提供的背景音频管理器
backgroundAudioManager具体查看:https://uniapp.dcloud.net.cn/api/media/background-audio-manager.html (opens new window)
- 存在的问题:
- 背景音频,不是游戏的背景音乐,而是类似QQ音乐那样,App在后台时,仍然在播放音乐。
- 因为背景音频播放耗费手机电量,所以平台都有管控,需在manifest中填写申请。
- 各个平台要填写说明,上架要审核,因此,如果你不是做那种类似音乐播放器,则可以使用普通音频API
uni.createInnerAudioContext。
- 普通音频API
uni.createInnerAudioContext具体查看:https://uniapp.dcloud.net.cn/api/media/audio-context.html (opens new window)
# ① 定义一个来消息的提示音
在文件 /common/mixins/configData.js
// 消息提示音等数据
export const chatAudioInfo = {
// 来消息了
getMessageAudio: `https://docs-51yrc-com.oss-cn-hangzhou.aliyuncs.com/chat/audio/getMessage.mp3`,
};
# ② 放在接收消息的时候根据设置情况提示
在聊天类文件 /common/js/chatClass.js
// 构造函数
constructor() {
...,
//创建全局唯一背景音频播放管理器(因为背景音频播放耗费手机电量,平台都有管控)
// 我们还是换成使用普通音频API:innerAudioContext
// this.bgAudioManager = uni.getBackgroundAudioManager();
this.chatClassContext = uni.createInnerAudioContext();
},
...
// 处理接收到的消息
async doMessage(msg){
console.log('处理接收到的消息',msg);
if(msg.type == 'system'){
console.log('系统消息单独处理');
}else if(msg.type == 'singleChat'){
let msgData = msg.data;
if(msgData.actionType && msgData.actionType =='revoke'){
...
}else{
// 把聊天信息存在本地
let { data } = this.addChatInfo(msgData, false);
// 消息页的聊天列表更新一下
this.updateXiaoXiList(data, false);
// 全局通知数据
uni.$emit('onMessage', data);
// 来点提示音
// this.msgNotice();
// 根据用户的设置来点提示音
// 这里我们仅根据消息页的设置来判断(全面的话可以根据服务器设置判断)
let xiaoxiId = msgData.chatType == 'single' ? msgData.from_id : msgData.to_id;
// 查消息页对应消息聊天对象的设置
let xiaoxiList = this.getXiaoXiList();
// 找到当前聊天
let index = xiaoxiList.findIndex(v => v.id == xiaoxiId && v.chatType == msgData.chatType);
if(index != -1){
let setinfo = xiaoxiList[index];
console.log('找到消息页对应聊天对象看一下设置',setinfo);
if(!setinfo.nowarn){
// 来点提示音
this.msgNotice();
}
}
}
}
},
...
// 消息提示音
msgNotice(){
// 震动
// uni.vibrate();
// 提示音
this.chatClassContext.src = chatAudioInfo.getMessageAudio;
this.chatClassContext.play();
}
# ③ 界面样式
在消息页组件 /components/chat-chatlist/chat-chatlist.vue
<template>
<view ...>
<!-- 头像 -->
...
<!-- 右边 -->
<view ...>
<!-- 上面:昵称 + 时间 -->
...
<!-- 下面:聊天内容 -->
<view class="flex flex-row align-center flex-nowrap">
<!-- 消息小字部分 -->
<view class="flex flex-1 flex-row align-center">
<text class="text-light-muted u-line-1"
style="font-size: 24rpx;">{{showText}}</text>
</view>
<!-- 消息提示音图标 -->
<view v-if="item.nowarn"
class="flex flex-row align-center justify-end"
style="width: 50rpx;">
<u-icon name="volume-off"></u-icon>
</view>
</view>
</view>
</view>
</template>
...
# 六、问题修复
# ① 用户退出登录加个判断
在文件 /store/modules/chatuser.js
// 用户退出登录
logoutAction({commit,state}, callback = false) {
if(state.chatClass){
// 关闭socket链接
state.chatClass.close();
state.chatClass = null;
}
...
},
# ② 小程序朋友列表新的朋友等栏目不显示
在页面 /pages/friendsList/friendsList.nvue
把插槽和v-if判断分开写
<view slot="header" class="list">
<view v-if="action == 'friendList'">
<view v-for="(item,index) in topMenus" :key="index"
@click="opentopMenus(item,index)">
<view class="flex align-center justify-between pr-3">
<view class="list__item">
<u-avatar shape="square" size="35"
:icon="item.icon" fontSize="26" randomBgColor></u-avatar>
<text class="list__item__user-name">{{item.title}}</text>
</view>
<!-- 提示待处理的数量 -->
<view class="flex align-center">
<u-badge :max="99" :value="goodfriendapply.pendingCount"></u-badge>
</view>
</view>
<u-line></u-line>
</view>
</view>
</view>
<!-- 列表 -->
...