# 一、和我聊天设置(聊天条数限制)

# 1. 入口设置

除了可以在设置进入外,后端也把设置入口推送给了游客和登录用户,在首页就可以看到。

# 2. 功能实现

# ① 在聊天页点击用户头像进入

在组件 /components/chat-item/chat-item.vue

<!-- 聊天内容 -->
<view ...>
    <!-- 好友 -->
    <!-- 头像 -->
    <u--image v-if="!isMe"
    :src="item.avatar" 
    mode="widthFix"
    width="80rpx" height="80rpx" radius="10rpx"
    @click="clickAvatar('notme')"></u--image>
    <!-- 气泡 -->
    <!-- 三角形 -->
    ...
    
    <!-- 聊天内容主体 -->
    ...
    
    <!---->
    <text v-if="isMe && needQipaoClass"
    class="iconfont font-md chat-right-icon">&#xe640;</text>
    <u--image v-if="isMe"
    :src="item.avatar" 
    mode="widthFix"
    width="80rpx" height="80rpx" radius="10rpx"
    @click="clickAvatar('me')"></u--image>
</view>
...
methods:{
    //点击头像
    clickAvatar(who){
        if(who == 'me'){
            uni.switchTab({
                url:'/pages/wode/wode'
            });
        }else{
            uni.redirectTo({
                url:`/pages/userinfo/userinfo?uuid=${this.item.sendUUid}`,
            });
        }
    },
    ...
},    

# ② 新建混入文件 /pages/userinfo/sendMessage.js

export default{
	methods:{
		//发消息
		sendMessageFun(){
			console.log('发消息的逻辑',this.me);
			if(this.me.ismygoodfriend){
				...
			}else{
				const role = this.me.role; //'visitor' | 'user'
				let userset = this.user.userset;
				console.log('看一下userset',userset);
				if(!userset){
					// 用户没有进行聊天的设置
					userset = new Object();
					userset.chatset = {
						visitor: {
							sendCount:1,
							needFollow:false,
						},
						user:{
							sendCount:1,
							needFollow:false,
						} 
					}
				}else{
					userset = JSON.parse(userset);
				}
				console.log('看一下userset',userset);
				const { sendCount, needFollow } = userset.chatset[role];
				// 1. 检查是否需要关注
				// if(needFollow && this.me.hasFollowed){
				// 	console.log('请先关注对方才能发消息');
				// 	return; // 程序结束
				// }
				// 2. 根据对方设置的聊天权限处理
				switch(sendCount){
					case 0:
					  ...
					  break;
					case 1:
					  console.log('可以发一条消息');
					  this.btn.clicktype = 'sendOne';
					  this.btn.text = '发消息';
					  console.log('去聊天页',this.user);
					  // uni.navigateTo({
					  // 	url: '/pages/chat/chat',
					  // });
					  this.groupOrSingleChatRedirect('single', {
						  id: this.user.id,
						  name: this.user.nickname || this.user.username,
						  avatar: this.user.avatar,
						  chatType: 'single',
					  });
					  break;
					default:
					  console.log('发消息不受限制');
					  this.groupOrSingleChatRedirect('single', {
						  id: this.user.id,
						  name: this.user.nickname || this.user.username,
						  avatar: this.user.avatar,
						  chatType: 'single',
					  });
					  break;
				}
							
			}
		},
		// 进群|单聊聊天跳转
		groupOrSingleChatRedirect(chatType = 'single', item = {}){
			// 进入聊天页 传一下用户的信息过去在页面展示
			const userchat = {
				id: item.id || this.chatpageset.id,
				name: item.name || this.chatpageset.name,
				avatar: item.avatar || this.chatpageset.avatar,
				chatType: item.chatType || chatType, // 单聊 single 群聊 group
			};
			uni.navigateTo({
				url: `/pages/chat/chat?arg=${encodeURIComponent(JSON.stringify(userchat))}`,
			});
		},
	},
}

# ③ 聊天设置页判断是否显示删除好友按钮

在页面 /pages/setpageInfo/setpageInfo.vue

<!-- 聊天页设置 -->
<view v-if="setdata.action == 'chatpageset'">
    <view v-if="chatpageset.id && chatpageset.chatType">
        <!-- 头像部分 -->
        <view class="flex flex-wrap p-3">
            <!-- 单聊 -->
            ...
            <!-- 群聊 循环头像昵称 -->
            ...
            <!-- 加入群聊 -->
            <view v-if="!(me && me.role == 'visitor')"
            class="border flex align-center justify-center"
            style="width: 36px;height: 36px;border-style: dashed;"
            @click="clickCell('选择好友', 'addUserToGroup')">
                <u-icon name="plus" size="24" color="#999999"></u-icon>
            </view>
            <!-- 删除群聊用户 -->
            ...
        </view>
        <u-gap height="10" bgColor="#EDEDED"></u-gap>
        <!-- 群相关设置 -->
        ...
        <!-- 查找聊天内容 -->
        ...
        <!-- 消息设置 -->
        ...
        <!-- 解散群或退群或者删除好友 -->
        <view v-if="isShowDeleteOrQuit">
            <view class="p-3">
                <u-button type="primary" plain :text="deleteOrQuitGroup"
                @click="deleteOrQuitGroupfn"></u-button>
            </view>
            <u-gap height="10" bgColor="#EDEDED"></u-gap>
        </view>
    </view>
</view>
...
data() {
    return {
        ...
        chatpageset:{
            ...
            ismygoodfriend: false, // 是否是我的好友
        },
    }
},
onLoad(e) {
    ...
    // 动态生成数据
    if(e.action){
        this.setdata.action = e.action;
        if(this.setdata.action == 'chatset'){
            ...
        }else if(this.setdata.action == 'chatpageset'){
            ...
            // 群聊设置
            ...
            // 是否是我的好友
            if(this.chatpageset.chatType == 'single'){
                this.ismygoodfriend();
            }
        }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'){
            ...
            
        }else if(this.setdata.action == 'applyAddMeFriendSet'){
            ...
        }
    }
},
computed:{
    ...,
    // 是否显示删除该好友按钮
    isShowDeleteOrQuit(){
        let show = true;
        if(this.chatpageset.chatType == 'single'){
            if(!this.chatpageset.ismygoodfriend){
                show = false;
            }
        }
        return show;
    },
},
methods: {
    // 查询某个用户是否我的好友
    ismygoodfriend(){
        // 我的信息
        // console.log('此时的this.me',this.me);return;
        uni.$u.http.post(requestUrl.http + `/api/chat/ismygoodfriend/${this.chatpageset.id}`,{},{
            header:{
                token: this.me.token,
            }
        }).then(res => {
                console.log('是不是我的好友',res.data.data);
                if(res.data.msg == 'ok'){
                    this.chatpageset.ismygoodfriend = res.data.data;
                }
        }).catch(err => {
            console.log('不是好友');
            this.chatpageset.ismygoodfriend = false;
        });
    },
    ...
},

# 二、使用场景体验

说明:

  1. 我们本套课程 即时通讯 项目,首先是兼容多端:微信小程序H5APP,我们在开发的时候,也是多端同时开发,大家有所体验。
  2. 即时通讯 作为现代前端项目中,非常重要的项目,其适合的场景非常的广泛,如: 外卖商城聊天视频聊天点餐物流社区教育游戏直播招聘培训等等,只要有用户参与的项目,这个功能都是必不可少的,你也可以将其作为你项目的亮点来说明。
  3. 我们本套课程将 即时通讯的主要基础功能已经全部开发完成了,你可以把我们当前的代码嵌入到任何一个项目中,作为基础功能来使用,当然,你也可以根据你的项目需求,进行二次开发,增加更多功能,如:语音聊天视频聊天外卖商城点餐物流社区教育游戏直播招聘培训等等,这些功能,我们也会在后续的课程中,进行讲解。
  4. 接下来,老师讲一个最简单的使用场景给大家体验一下。

# ① 将我们的即时通讯嵌入到某个项目中

在某个项目中,给一个链接即可
新建一个页面 /pages/case/case.vue

<template>
	<view class="chat-container">
		<!-- PC端布局 -->
		<view v-if="!isMobile" class="pc-layout">
			<!-- 左侧聊天列表 -->
			<view class="chat-list-panel" :style="{width: leftPanelWidth + 'px'}">
				<view class="resize-handle" @mousedown="startResize"></view>
				<view class="panel-header">
					<text class="title">聊天列表</text>
				</view>
				<scroll-view class="chat-list" scroll-y>
					<view 
						v-for="(item, index) in chatList" 
						:key="index"
						class="chat-item"
						:class="{active: activeChatIndex === index}"
						@click="selectChat(index)"
					>
						<image class="avatar" :src="item.avatar" mode="aspectFill"></image>
						<view class="chat-info">
							<text class="name">{{ item.name }}</text>
							<text class="desc">{{ item.desc }}</text>
						</view>
					</view>
				</scroll-view>
			</view>
			
			<!-- 右侧聊天界面 -->
			<view class="chat-content-panel">
				<!-- 使用iframe替代web-view,避免页面跳转 -->
				<iframe 
					v-if="activeChatUrl && !isMobile"
					:src="getFullUrl(activeChatUrl)"
					class="chat-iframe"
					frameborder="0"
				></iframe>
				<view v-else class="empty-chat">
					<text>请选择聊天对象</text>
				</view>
			</view>
		</view>
		
		<!-- 移动端布局 -->
		<view v-else class="mobile-layout">
			<!-- 移动端使用web-view,但需要处理路径 -->
			<view v-if="activeChatUrl" class="mobile-chat-content">
				<iframe 
					:src="getFullUrl(activeChatUrl)"
					class="mobile-chat-iframe"
					frameborder="0"
				></iframe>
			</view>
			<view v-else class="empty-chat-mobile">
				<text>暂无聊天</text>
			</view>
			
			<!-- 移动端返回按钮 -->
			<view v-if="isMobile && activeChatUrl" class="mobile-back" @click="showChatList = true">
				<text>返回列表</text>
			</view>
			
			<!-- 移动端聊天列表 -->
			<view v-if="isMobile && showChatList" class="mobile-chat-list">
				<view class="list-header">
					<text class="title">聊天列表</text>
					<text class="close-btn" @click="showChatList = false">关闭</text>
				</view>
				<scroll-view class="chat-list-mobile" scroll-y>
					<view 
						v-for="(item, index) in chatList" 
						:key="index"
						class="chat-item-mobile"
						@click="selectChatMobile(index)"
					>
						<image class="avatar-mobile" :src="item.avatar" mode="aspectFill"></image>
						<view class="chat-info-mobile">
							<text class="name-mobile">{{ item.name }}</text>
							<text class="desc-mobile">{{ item.desc }}</text>
						</view>
					</view>
				</scroll-view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				chatList: [
					{
						chatType: 'single',
						name: '睿晨编程客服老师',
						avatar: 'https://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250924/1758697243774_0e5a2764831b.png',
						desc: '跟课程相关的问题,可以咨询老师',
						path: 'http://192.168.2.6:8081/#/pages/setpageInfo/setpageInfo',
						params: {
							action: 'autoAddGroup',
							title: '加用户[睿晨编程客服老师]为好友',
							id: '49',
							chatType: 'single',
							name: '睿晨编程客服老师',
							avatar: 'https://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250924/1758697243774_0e5a2764831b.png',
							redirect: 'true'
						}
					},
					{
						chatType: 'group',
						name: '第三期第二季课程群',
						avatar: 'https://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250924/1758697243774_0e5a2764831b.png',
						desc: '学习第三期第二季课程的同学,可在群里进行问题答疑',
						path: 'http://192.168.2.6:8081/#/pages/setpageInfo/setpageInfo',
						params: {
							action: 'autoAddGroup',
							title: '群介绍',
							id: '41',
							chatType: 'group',
							name: '第三期第二季课程群',
							redirect: 'true'
						}
					}
				],
				isMobile: false,
				activeChatIndex: 0,
				leftPanelWidth: 450, // 默认宽度
				isResizing: false,
				showChatList: false, // 移动端是否显示聊天列表
				baseUrl: '', // 基础URL
				startX: 0, // 拖动起始位置
				startWidth: 0 // 拖动起始宽度
			}
		},
		computed: {
			activeChatUrl() {
				if (this.chatList.length > 0 && this.activeChatIndex >= 0) {
					const chat = this.chatList[this.activeChatIndex];
					console.log('看一下完整的url',this.buildUrl(chat.path, chat.params));
					return this.buildUrl(chat.path, chat.params);
				}
				return '';
			}
		},
		mounted() {
			this.checkDeviceType();
			window.addEventListener('resize', this.checkDeviceType);
			
			// 获取基础URL
			this.getBaseUrl();
			
			if (this.chatList.length > 0) {
				this.activeChatIndex = 0;
			}
		},
		beforeDestroy() {
			window.removeEventListener('resize', this.checkDeviceType);
		},
		methods: {
			// 检查设备类型
			checkDeviceType() {
				const screenWidth = window.innerWidth;
				this.isMobile = screenWidth < 750;
				
				// 如果是PC端,根据屏幕大小调整左侧面板宽度
				if (!this.isMobile) {
					this.adjustPanelWidth();
				}
			},
			
			// 根据屏幕大小调整面板宽度
			adjustPanelWidth() {
				const screenWidth = window.innerWidth;
				
				// 确保左侧面板宽度在合理范围内
				const minWidth = 350;
				const maxWidth = 450;
				
				if (this.leftPanelWidth < minWidth) {
					this.leftPanelWidth = minWidth;
				} else if (this.leftPanelWidth > maxWidth) {
					this.leftPanelWidth = maxWidth;
				}
			},
			
			// 获取基础URL
			getBaseUrl() {
				// 在H5环境中,获取当前页面的基础URL
				if (typeof window !== 'undefined') {
					this.baseUrl = window.location.origin;
					
					// 如果是开发环境,可能需要特殊处理
					if (process.env.NODE_ENV === 'development') {
						// 开发环境的基础路径处理
						const pathname = window.location.pathname;
						const paths = pathname.split('/');
						if (paths.length > 1) {
							this.baseUrl += '/' + paths[1];
						}
					}
				}
			},
			
			// 构建完整URL
			buildUrl(path, params) {
				if (!path) return '';
				
				// 构建参数字符串
				let paramStr = '';
				if (params) {
					paramStr = '?' + Object.keys(params)
						.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
						.join('&');
				}
				
				console.log('看一下右侧完整的url', path + paramStr);
				
				return path + paramStr;
			},
			
			// 获取完整的URL(用于iframe)
			getFullUrl(url) {
				if (!url) return '';
				
				// 如果是完整URL,直接返回
				if (url.startsWith('http')) {
					return url;
				}
				
				// 如果是相对路径,转换为绝对路径
				if (url.startsWith('/')) {
					return this.baseUrl + url;
				}
				
				// 其他情况直接返回
				return url;
			},
			
			// 选择聊天(PC端)
			selectChat(index) {
				this.activeChatIndex = index;
			},
			
			// 选择聊天(移动端)
			selectChatMobile(index) {
				this.activeChatIndex = index;
				this.showChatList = false; // 关闭聊天列表
			},
			
			// 开始调整左侧面板宽度
			startResize(e) {
				this.isResizing = true;
				this.startX = e.clientX;
				this.startWidth = this.leftPanelWidth;
				
				// 添加全局样式,改善拖动体验
				document.body.style.userSelect = 'none';
				document.body.style.cursor = 'col-resize';
				
				document.addEventListener('mousemove', this.handleResize);
				document.addEventListener('mouseup', this.stopResize);
				e.preventDefault();
			},
			
			// 处理调整宽度
			handleResize(e) {
				if (!this.isResizing) return;
				
				const deltaX = e.clientX - this.startX;
				const newWidth = this.startWidth + deltaX;
				
				// 设置宽度限制
				const minWidth = 350;
				const maxWidth = 450;
				
				if (newWidth >= minWidth && newWidth <= maxWidth) {
					this.leftPanelWidth = newWidth;
				} else if (newWidth < minWidth) {
					this.leftPanelWidth = minWidth;
				} else if (newWidth > maxWidth) {
					this.leftPanelWidth = maxWidth;
				}
			},
			
			// 停止调整宽度
			stopResize() {
				this.isResizing = false;
				
				// 恢复全局样式
				document.body.style.userSelect = '';
				document.body.style.cursor = '';
				
				document.removeEventListener('mousemove', this.handleResize);
				document.removeEventListener('mouseup', this.stopResize);
			}
		}
	}
</script>

<style lang="scss" scoped>
	.chat-container {
		width: 100%;
		height: 100vh;
		overflow: hidden;
		background-color: #f5f5f5;
	}
	
	/* PC端布局样式 */
	.pc-layout {
		display: flex;
		width: 100%;
		height: 100vh;
		background-color: #ffffff;
		overflow: hidden;
		position: relative;
	}
	
	/* 屏幕宽度大于1000px时,整个聊天区域居中,最大宽度1000px */
	@media (min-width: 1000px) {
		.pc-layout {
			width: 1000px;
			max-width: 1000px;
			height: 90vh;
			margin: 5vh auto;
			border-radius: 12px;
			box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
			overflow: hidden;
		}
	}
	
	/* 超大屏幕优化 */
	@media (min-width: 1600px) {
		.pc-layout {
			height: 85vh;
			margin: 7.5vh auto;
		}
	}
	
	.chat-list-panel {
		width: 450px;
		min-width: 350px;
		max-width: 450px;
		background-color: #f8f9fa;
		border-right: 1px solid #e0e0e0;
		display: flex;
		flex-direction: column;
		position: relative;
		transition: width 0.1s ease;
	}
	
	.resize-handle {
		position: absolute;
		right: -2px;
		top: 0;
		width: 4px;
		height: 100%;
		background-color: transparent;
		cursor: col-resize;
		z-index: 10;
		transition: background-color 0.2s;
	}
	
	.resize-handle:hover,
	.resize-handle:active {
		background-color: #007AFF;
		width: 6px;
		right: -3px;
	}
	
	.panel-header {
		padding: 20px;
		border-bottom: 1px solid #e0e0e0;
		background-color: #ffffff;
		flex-shrink: 0;
		
		.title {
			font-size: 18px;
			font-weight: 600;
			color: #333333;
		}
	}
	
	.chat-list {
		flex: 1;
		overflow-y: auto;
	}
	
	.chat-item {
		display: flex;
		padding: 15px;
		border-bottom: 1px solid #f0f0f0;
		cursor: pointer;
		transition: background-color 0.2s;
		
		&:hover {
			background-color: #f0f0f0;
		}
		
		&.active {
			background-color: #e6f7ff;
			border-left: 3px solid #007AFF;
		}
	}
	
	.avatar {
		width: 50px;
		height: 50px;
		border-radius: 50%;
		margin-right: 12px;
		flex-shrink: 0;
	}
	
	.chat-info {
		flex: 1;
		display: flex;
		flex-direction: column;
		justify-content: center;
		overflow: hidden;
	}
	
	.name {
		font-size: 16px;
		font-weight: 500;
		color: #333333;
		margin-bottom: 5px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	
	.desc {
		font-size: 14px;
		color: #666666;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	
	.chat-content-panel {
		flex: 1;
		display: flex;
		min-width: 0; /* 允许缩小 */
		overflow: hidden;
		background-color: #f0f2f5;
		
		/* 右侧聊天区域固定宽度550px */
		width: 550px;
		min-width: 550px;
		max-width: 550px;
	}
	
	.chat-iframe {
		width: 100%;
		height: 100%;
		border: none;
		min-width: 550px;
	}
	
	.empty-chat {
		display: flex;
		align-items: center;
		justify-content: center;
		height: 100%;
		font-size: 16px;
		color: #999999;
		width: 100%;
	}
	
	/* 移动端布局样式 */
	.mobile-layout {
		width: 100%;
		height: 100vh;
		position: relative;
	}
	
	.mobile-chat-content {
		width: 100%;
		height: 100%;
	}
	
	.mobile-chat-iframe {
		width: 100%;
		height: 100%;
		border: none;
	}
	
	.empty-chat-mobile {
		display: flex;
		align-items: center;
		justify-content: center;
		height: 100%;
		font-size: 16px;
		color: #999999;
	}
	
	.mobile-back {
		position: absolute;
		top: 10px;
		left: 10px;
		background-color: rgba(0, 0, 0, 0.7);
		color: white;
		padding: 8px 12px;
		border-radius: 4px;
		z-index: 100;
		font-size: 14px;
		cursor: pointer;
	}
	
	.mobile-chat-list {
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		background-color: white;
		z-index: 200;
	}
	
	.list-header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 15px;
		border-bottom: 1px solid #e0e0e0;
		background-color: #f8f9fa;
		
		.title {
			font-size: 18px;
			font-weight: 600;
		}
		
		.close-btn {
			color: #007AFF;
			font-size: 16px;
			cursor: pointer;
		}
	}
	
	.chat-list-mobile {
		height: calc(100% - 60px);
	}
	
	.chat-item-mobile {
		display: flex;
		padding: 15px;
		border-bottom: 1px solid #f0f0f0;
		cursor: pointer;
	}
	
	.avatar-mobile {
		width: 50px;
		height: 50px;
		border-radius: 50%;
		margin-right: 12px;
	}
	
	.chat-info-mobile {
		flex: 1;
		display: flex;
		flex-direction: column;
		justify-content: center;
		overflow: hidden;
	}
	
	.name-mobile {
		font-size: 16px;
		font-weight: 500;
		margin-bottom: 5px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	
	.desc-mobile {
		font-size: 14px;
		color: #666666;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	
	/* 响应式调整 */
	@media (max-width: 750px) {
		.pc-layout {
			display: none;
		}
	}
	
	@media (min-width: 751px) {
		.mobile-layout {
			display: none;
		}
	}
	
	/* 中等屏幕适配(751px-999px) */
	@media (min-width: 751px) and (max-width: 999px) {
		.pc-layout {
			height: 100vh;
			box-shadow: none;
			border-radius: 0;
			width: 100%;
			max-width: 100%;
		}
		
		.chat-list-panel {
			width: 40%;
			min-width: 300px;
			max-width: 400px;
		}
		
		.chat-content-panel {
			width: 60%;
			min-width: auto;
			max-width: none;
			flex: 1;
		}
		
		.chat-iframe {
			min-width: auto;
		}
	}
	
	/* 小屏幕PC适配(751px-850px) */
	@media (min-width: 751px) and (max-width: 850px) {
		.chat-list-panel {
			width: 35%;
			min-width: 250px;
			max-width: 300px;
		}
		
		.chat-content-panel {
			width: 65%;
		}
	}
</style>

# ② 配置文件

在文件 /pages.json, 我们这里做一个简单场景,对H5端开发一个适用PC端和移动端开发的页面

// #ifdef H5 
,{
    "path" : "pages/case/case",
    "style" : 
    {
        "navigationBarTitleText" : "客服中心",
        "navigationStyle": "custom"
    }
}
// #endif 
更新时间: 2025年9月25日星期四晚上8点07分