# 一、辅助功能(包括单聊和群的一些其它功能)

# 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. 优化加好友入群(不在群里面的好友才有加群按钮)

  1. 在文件 /pages/setpageInfo/group.js
data(){
	return {
		...
		// 群成员
		groupUsers: null,
	}
},
methods: {
	...
	// 往群里面加人
	addUserFromGroup(){
		...
		// 存一份群信息
		uni.setStorageSync('currentGroupInfo', JSON.stringify(this.chatpageset));
		// 跳转
		...
	},
	...
},
  1. 在页面 /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});
			});
		},
		...
	}
}

# 五. 接收消息给点提示音

  1. 提示音素材: https://docs-51yrc-com.oss-cn-hangzhou.aliyuncs.com/chat/audio/getMessage.mp3
  2. 播放声音有两种方式:
  1. uni-app官方提供的背景音频管理器 backgroundAudioManager 具体查看:https://uniapp.dcloud.net.cn/api/media/background-audio-manager.html (opens new window)
  1. 存在的问题:
  1. 背景音频,不是游戏的背景音乐,而是类似QQ音乐那样,App在后台时,仍然在播放音乐。
  2. 因为背景音频播放耗费手机电量,所以平台都有管控,需在manifest中填写申请。
  3. 各个平台要填写说明,上架要审核,因此,如果你不是做那种类似音乐播放器,则可以使用普通音频API uni.createInnerAudioContext
  1. 普通音频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>
<!-- 列表 -->
...
更新时间: 2025年9月16日星期二下午5点48分