# 九、聊天页设置界面开发和数据渲染
# 1. 聊天页设置相关信息获取
在类文件 /common/js/chatClass.js
...
// 聊天页设置相关信息获取
getChatPageSet(to_id, chatType){
let xiaoxiList = this.getXiaoXiList();
// 找到当前聊天
let index = xiaoxiList.findIndex(v => v.id == to_id && v.chatType == chatType);
if(index != -1){
// 找到了
return xiaoxiList[index];
}
return null;
}
# 2. 进入设置页面
在页面 /pages/chat/chat.nvue
//点击了三个点图标
openMore(){
console.log('点击了三个点图标',this.arg);
let op = {
action:'chatpageset',
title:'设置',
id: this.arg.id,
chatType: this.arg.chatType,
avatar : this.arg.avatar,
name : this.arg.name,
};
uni.navigateTo({
url: `/pages/setpageInfo/setpageInfo?action=${op.action}&title=${encodeURIComponent(op.title)}&id=${op.id}&chatType=${op.chatType}&avatar=${encodeURIComponent(op.avatar)}&name=${encodeURIComponent(op.name)}`,
});
},
# 3. 设置页面开发
在页面 /pages/setpageInfo/setpageInfo.vue
<template>
<view>
<!-- 导航栏 -->
...
<!-- 内容 -->
<view>
<!-- 聊天相关 -->
...
<!-- 聊天页设置 -->
<view v-if="setdata.action == 'chatpageset'">
<view v-if="chatpageset.id && chatpageset.chatType">
<!-- 头像部分 -->
<view class="flex flex-wrap p-3">
<!-- 单聊 -->
<view v-if="chatpageset.chatType == 'single'"
class="flex flex-column justify-center align-center mr-2">
<u-avatar :src="chatpageset.avatar" shape="square"
customStyle="border:1px solid #eeeeee;"></u-avatar>
<text class="font-sm mt-1 text-muted">{{chatpageset.name}}</text>
</view>
<!-- 群聊 循环头像昵称 -->
<!-- 加人组群 -->
<view class="border flex align-center justify-center"
style="width: 40px;height: 40px;border-style: dashed;"
@click="clickCell('加人组群', 'addUserToGroup')">
<u-icon name="plus" size="24" color="#999999"></u-icon>
</view>
<!-- 删除群聊用户 -->
<view v-if="chatpageset.chatType == 'group'"
class="border flex align-center justify-center ml-2"
style="width: 40px;height: 40px;border-style: dashed;"
@click="clickCell('群组删除某个用户', 'deleteUserFromGroup')">
<u-icon name="minus" size="24" color="#999999"></u-icon>
</view>
</view>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
<!-- 查找聊天内容 -->
<u-cell-group>
<u-cell title="查找聊天内容"
:border="false" clickable isLink
:customStyle="customStyles"
:iconStyle="iconStyles"
:titleStyle="titleStyles"
@click="clickCell('查找聊天内容', 'searchChatData')"></u-cell>
</u-cell-group>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
<!-- 消息设置 -->
<u-cell-group>
<u-cell title="消息免打扰"
:border="false" clickable
:customStyle="customStyles"
:iconStyle="iconStyles"
:titleStyle="titleStyles">
<view slot="value">
<u-switch v-model="chatpageset.nowarn" @change="switchChange('nowarn')"></u-switch>
</view>
</u-cell>
<u-cell title="置顶聊天"
:border="false" clickable
:customStyle="customStyles"
:iconStyle="iconStyles"
:titleStyle="titleStyles">
<view slot="value">
<u-switch v-model="chatpageset.istop" @change="switchChange('istop')"></u-switch>
</view>
</u-cell>
<u-cell title="提醒"
:border="false" clickable
:customStyle="customStyles"
:iconStyle="iconStyles"
:titleStyle="titleStyles">
<view slot="value">
<u-switch v-model="chatpageset.stongwarn" @change="switchChange('stongwarn')"></u-switch>
</view>
</u-cell>
</u-cell-group>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
<!-- 清空聊天记录 -->
<u-cell-group>
<u-cell title="清空聊天记录"
:border="false" clickable
:customStyle="customStyles"
:iconStyle="iconStyles"
:titleStyle="titleStyles"
@click="clickCell('清空聊天记录', 'deleteChatData')"></u-cell>
</u-cell-group>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
</view>
</view>
</view>
</view>
</template>
<script>
...
export default {
mixins:[toolJs],
data() {
return {
...,
chatpageset:{},
}
},
onLoad(e) {
...
// 动态生成数据
if(e.action){
this.setdata.action = e.action;
if(this.setdata.action == 'chatset'){
...
}else if(this.setdata.action == 'chatpageset'){
this.chatpageset.id = e.id;
this.chatpageset.chatType = e.chatType;
this.chatpageset.avatar = decodeURIComponent(e.avatar);
this.chatpageset.name = decodeURIComponent(e.name);
// 聊天页设置相关信息获取
let getChatPageSet = this.chatClass.getChatPageSet(this.chatpageset.id,this.chatpageset.chatType);
console.log('聊天页设置信息', getChatPageSet);
// 设置相关
this.chatpageset = {
...this.chatpageset,
// 单聊
istop: getChatPageSet.istop,
shownickname: getChatPageSet.shownickname ? true : false,
nowarn: getChatPageSet.nowarn ? true : false,
stongwarn: getChatPageSet.stongwarn ? true : false,
// 群聊还有以下字段
user_id: 0,
remark:'',
invite_confirm: 0,
};
console.log('设置相关',this.chatpageset);
}
}
},
computed:{
...mapState({
me : state => state.Chatuser.regloginUser,
chatClass : state => state.Chatuser.chatClass,
}),
customStyles(){
return `padding-top:20rpx;padding-bottom:20rpx`;
},
iconStyles(){
return `font-size:52rpx;margin-right:30rpx;color:#5696da`;
},
titleStyles(){
return `font-size:32rpx;`;
},
},
methods: {
switchChange(key){
console.log(`switch开关${key}`, this.chatpageset[key]);
},
// 点击某一项
clickCell(title, type){
console.log(title, type);
},
submitSet(){
...
}
}
}
</script>
<style>
</style>
# 十、添加好友到群聊
# 1. 新增进入聊天页设置的功能入口
考虑到微信小程序和H5(微信打开)没有三个点进入聊天页设置, 我们在聊天页扩展菜单新增一个入口。
在文件 /pages/chat/plusIconAction.js
data(){
return {
...
plusMenus:[ // 加号扩展菜单栏目
{ name:"聊天设置", icon:"setting-fill", iconType:"uview", eventType:"openMore" },
...
],
...
}
},
methods:{
//点击加号扩展菜单的某一项
async swiperItemClick(item, itemIndex) {
...
if (this.sendMessageMode === 'icon') {
...
} else {
...
switch (item.eventType){
case 'openMore':
this.openMore();
break;
case 'photo':
...
break;
case 'video':
...
break;
case 'cameraPhoto':
...
break;
case 'cameraVideo':
...
break;
case 'map':
break;
case 'mingpian':
break;
}
}
},
...
},
# 2. 聊天页设置点击加号进入添加好友到群聊
在页面 /pages/setpageInfo/setpageInfo.vue
clickCell(title, type){
console.log(title, type);
if(type == 'addUserToGroup'){
let op = {
action:'addUserToGroup',
title:encodeURIComponent(title),
};
uni.navigateTo({
url: `/pages/friendsList/friendsList?action=${op.action}&title=${op.title}`,
});
}
},
# 3. 添加好友到群聊页面布局开发
在页面 /pages/friendsList/friendsList.nvue
<template>
<view>
<!-- 导航栏 -->
<chat-navbar :title="title" ...></chat-navbar>
<!-- 朋友列表 -->
<u-index-list :indexList="indexList">
<view slot="header" class="list" v-if="action == 'friendList'">
...
</view>
<!-- 列表 -->
<!-- 选择框 -->
<checkbox-group @change="checkboxChange">
<template v-for="(item, index) in itemArr">
<!-- #ifdef APP-NVUE -->
<u-index-anchor :text="indexList[index]" :key="index"></u-index-anchor>
<!-- #endif -->
<u-index-item :key="index">
<!-- #ifndef APP-NVUE -->
<u-index-anchor :text="indexList[index]"></u-index-anchor>
<!-- #endif -->
<view class="list" v-for="(item1, index1) in item" :key="index1">
<view class="list__item"
@click="openUserInfo(item1,'info')">
<!-- 选择框 -->
<view class="mr-2">
<checkbox iconColor="#4cd964" :value="`${item1.id}`"></checkbox>
</view>
<image class="list__item__avatar" :src="item1.url"></image>
<text class="list__item__user-name">{{item1.name}}</text>
</view>
<u-line></u-line>
</view>
</u-index-item>
</template>
</checkbox-group>
<view slot="footer" ...>
...
</view>
</u-index-list>
<!-- 底部完成选择按钮 -->
<view v-if="action == 'addUserToGroup'">
<!-- 占位 -->
<view :style="btnBottomHeight"></view>
<!-- 底部按钮 -->
<view class="position-fixed left-0 right-0 bottom-0 bg-light
flex flex-row p-3 align-center justify-end"
style="z-index: 100;"
:style="btnBottomStyle">
<view style="height: 90rpx;" class="flex flex-row align-center">
<text v-if="addUserToGroup.userIds"
class="text-muted flex-shrink mr-2">已选:
{{addUserToGroup.userIds.length}} 个好友</text>
<u-button text="确定加入群聊" type="success"
@click="btnBottomSubmit"></u-button>
</view>
</view>
</view>
</view>
</template>
<script>
...
export default {
mixins:[toolJs],
data() {
return {
...,
title:'好友列表',
action:'friendList',
bottomSafeAreaHeight:0, // 底部安全距离
addUserToGroup:{
userIds:false, // 选择加入群聊的用户id
},
}
},
onLoad(e) {
...
//选择好友加入群聊
if(e && e.action == 'addUserToGroup' ){
this.title = e.title ? decodeURIComponent(e.title) : '选择好友';
uni.setNavigationBarTitle({title:this.title});
this.action = 'addUserToGroup';
}
},
onShow() {
...
},
mounted() {
let info = uni.getSystemInfoSync();
this.bottomSafeAreaHeight = info.safeAreaInsets.bottom;
},
computed: {
...mapState({
...
}),
btnBottomStyle(){
let pbottom = this.bottomSafeAreaHeight == 0 ?
uni.upx2px(30) : this.bottomSafeAreaHeight + uni.upx2px(30);
return `padding-bottom: ${pbottom}px;`;
},
btnBottomHeight(){
let pbottom = this.bottomSafeAreaHeight == 0 ?
uni.upx2px(30) : this.bottomSafeAreaHeight + uni.upx2px(30);
return `height:${uni.upx2px(30 + 90) + pbottom}px;`;
},
itemArr() {
...
return newList && newList.map(item => {
const arr = [];
for(let i = 0; i < item.list.length; i++){
...
arr.push({
...,
id: item.list[i].id,
})
}
return arr
});
},
indexList(){
...
},
},
methods: {
checkboxChange(e){
console.log('选择框',e);
this.addUserToGroup.userIds = e.detail.value;
console.log('已选',this.addUserToGroup.userIds);
},
btnBottomSubmit(){
if(!this.addUserToGroup.userIds || !this.addUserToGroup.userIds.length){
return uni.showToast({title: '请选择要加入群聊的好友',icon:'none'});
}
let userIds = this.addUserToGroup.userIds.filter(v=> parseInt(v))
.map(i=> parseInt(i));
console.log('提交选择的好友加入群聊',userIds);
},
...,
openUserInfo(item1,action){
if(this.action == 'addUserToGroup') return;
uni.navigateTo({
url: `/pages/userinfo/userinfo?uuid=${item1.uuid}`,
});
},
}
}
</script>
# 十一、创建群聊
# 1. 创建群聊接口说明
创建群聊接口,具体查看: 二十一、创建群聊(成功后通过webSocket通知群聊用户)
# 2. 提交创建群聊
在页面 /pages/friendsList/friendsList.nvue
btnBottomSubmit(){
...
console.log('提交选择的好友加入群聊',userIds);
uni.showLoading({title: '正在创建中...',mask: false});
uni.$u.http.post(requestUrl.http + `/api/chat/group/create`, {
userIds: userIds,
}, {
header: {
token: this.me.token,
},
}).then(res => {
console.log('加入群聊服务器返回',res);
if(res.data.data == 'ok'){
uni.switchTab({
url:'/pages/xiaoxi/xiaoxi'
});
}
}).finally(()=>{
uni.hideLoading();
});
},
如果没有出现提示,看一下类文件 /common/js/chatClass.js 中 方法(消息页的聊天列表更新一下): updateXiaoXiList 中有没有添加群聊的逻辑。没有的话,替换到这个方法:六、发消息前把消息添加到本地历史记录,消息页的聊天列表更新一下,发完之后更新一下本地历史记录
# 3. 创建群聊成功后,消息页通知信息头像、标题等处理
在组件 /components/chat-chatlist/chat-chatlist.vue
<template>
<view ...>
<!-- 头像 -->
<view ...>
<!-- 头像地址 -->
<!-- <image :src="item.avatar" mode="widthFix" style="width: 90rpx;height: 90rpx;"
class="rounded">
</image> -->
<view v-if="avatar">
<!-- 一张 -->
<view v-if="avatar.length == 1">
<!-- <u--image :src="avatar[0]" mode="widthFix"
width="90rpx" height="90rpx" radius="8rpx"></u--image> -->
<u-avatar :src="avatar[0]" shape="square" size="90rpx"></u-avatar>
</view>
<!-- 多张 -->
<view v-else style="width: 90rpx;background-color: #eeeeee;"
class="rounded">
<!-- 2张 3张 4张-->
<view v-if="avatar.length == 2 || avatar.length == 3 || avatar.length == 4"
class="flex flex-row flex-wrap align-center justify-center"
style="align-content: center;height:90rpx;">
<u-avatar v-for="(v,k) in avatar" :key="k"
:src="v" shape="square" size="45rpx"></u-avatar>
</view>
<!-- 5张到9张 -->
<view v-if="avatar.length >= 5"
class="flex flex-row flex-wrap align-center justify-center"
style="align-content: center;height:90rpx;">
<u-avatar v-for="(v,k) in avatar" :key="k"
:src="v" shape="square" size="30rpx"></u-avatar>
</view>
</view>
</view>
<!-- 消息数量 角标-->
<!-- <text class="bg-danger rounded-circle text-white font-sm position-absolute"
style="padding-left: 14rpx;padding-right: 14rpx;
padding-top: 2rpx;padding-bottom: 2rpx;
top: 16rpx;right:10rpx;z-index: 100;">9</text> -->
<u-badge :isDot="false" :value="item.datacount"
absolute :offset="['10rpx','18rpx']"
max="999" shape="circle" numberType="limit"></u-badge>
</view>
<!-- 右边 -->
<view class="flex flex-column border-bottom border-light-secondary flex-1 pr-3"
style="padding-top: 24rpx;padding-bottom: 24rpx;">
<!-- 上面:昵称 + 时间 -->
<view class="flex justify-between align-center mb-1">
<text style="font-size: 32rpx;">{{nickname}}</text>
<text class="font-small text-light-muted">{{item.chat_time}}</text>
</view>
<!-- 下面:聊天内容 -->
<view class="pr-5">
<text class="text-light-muted u-line-1"
style="font-size: 24rpx;">{{item.data}}</text>
</view>
</view>
</view>
</template>
<script>
...
import {requestUrl} from '@/common/mixins/configData.js';
export default{
...
methods:{
...
},
computed:{
...,
//昵称显示
nickname(){
if(this.item.nickname.length > 10){
return this.item.nickname.substring(0,10) + '...';
}
return this.item.nickname;
},
// 头像显示
avatar(){
let arr = this.item.avatar && this.item.avatar.split(',');
arr = arr.map(v => {
if(v.startsWith('http')){
return v;
}else{
return requestUrl.http + v;
}
});
console.log('头像',arr);
return arr;
}
}
}
</script>
...
# 十二、进入群聊发消息及接收群聊消息
# 1. 点击群聊进入聊天页显示群聊提示
在组件 /components/chat-item/chat-item.vue
<template>
<view class="px-3">
<!-- 时间 -->
<view v-if="chatShowTime"
class="flex align-center justify-center py-3">
<text class="font-sm text-light-muted">{{chatShowTime}}</text>
</view>
<!-- 撤回消息 -->
<view v-if="item.isremove"
class="flex align-center justify-center py-3">
<text class="font-sm text-light-muted">您撤回了一条信息</text>
</view>
<!-- 进群首条消息提示 -->
<view v-if="item.type == 'systemNotice'"
class="flex align-center justify-center py-3">
<text class="font-sm text-light-muted">{{item.nickname + `创建了一个群聊,大家可以聊天了`}}</text>
</view>
<!-- 聊天内容 -->
<view v-if="!item.isremove && item.type != 'systemNotice'"
class="flex align-start position-relative py-3"
:class="[!isMe ? 'justify-start' : 'justify-end']">
<!-- 好友 -->
<!-- 头像 -->
...
<!-- 气泡 -->
<!-- 三角形 -->
...
<!-- 内容 -->
<view class="flex flex-column">
<!-- 昵称显示 -->
<view class="position-absolute"
style="max-width: 500rpx;top:-36rpx;"
:style="contentStyle"
v-if="shownickname"
:class="[isMe ? 'right-0':'left-0']">
<text class="text-light-muted" style="font-size: 20rpx;">{{item.nickname}}</text>
</view>
<!-- 消息内容 -->
...
</view>
<!-- 我 -->
...
</view>
<!-- 给服务器发消息状态 -->
...
<!-- 弹出菜单 -->
...
</view>
</template>
<script>
...
export default{
...
props:{
...,
// 是否显示昵称
shownickname:{
type: Boolean,
default: false,
}
},
...
}
</script>
# 2. 聊天页接收消息及标题等处理
在页面 /pages/chat/chat.nvue
<template>
<view>
<!-- 导航栏 -->
<chat-navbar :title="pagetitle" ...>
...
</chat-navbar>
<!-- 聊天内容区域 -->
...
<!-- 底部聊天输入区域 --><!-- 修改:添加ref获取textarea实例 -->
...
<!-- 弹出菜单 --><!-- 主要修改区域 -->
...
<!-- 提示用户正在录音的界面 -->
...
</view>
</template>
<script>
...
export default {
...,
onLoad(e) {
...
try{
...
// 监听处理接收到的消息
uni.$on('onMessage', v => {
console.log('监听处理接收到的消息在聊天页',v);
// 这是私聊的判断
/*
if(v.from_id == this.arg.id && v.chatType == this.arg.chatType){
this.chatDataList.push(this.formatServerMsg(v));
}
*/
if((v.from_id == this.arg.id && v.chatType == 'single') ||
(v.chatType == 'group' && this.arg.id == v.to_id)){
this.chatDataList.push(this.formatServerMsg(v));
}
});
}catch{
...
}
},
...,
computed:{
...mapState({
...
}),
//标题
pagetitle(){
if(this.arg && this.arg.name){
// #ifdef APP || H5
if(this.arg.name.length > 15){
return this.arg.name.substring(0,15) + '...';
}
return this.arg.name;
// #endif
// #ifdef MP
if(this.arg.name.length > 7){
return this.arg.name.substring(0,7) + '...';
}
return this.arg.name;
// #endif
}
return ``;
},
...
},
}
</script>
# 十三、获取离线消息(不在线的时候别人给你发的或者群里面发的消息)
此消息是通过服务器websocket推送的消息(亮点:游客因为也有token, 所以他再次上线也可以接收客服给他发的消息)
# 1. 获取离线消息接口说明
接口文档查看:二十二、获取离线消息(不在线的时候别人给你发的或者群里面发的消息)
# 2. 执行获取离线消息方法
在聊天类文件 /common/js/chatClass.js
...
// 连接成功
onOpen() {
// 用户上线
this.isOnline = true;
// 获取离线消息(不在线的时候别人给你发的消息)
this.chatGetmessageOffLine();
}
...
// 获取离线消息(不在线的时候别人给你发的消息)
chatGetmessageOffLine(){
uni.$u.http.post(requestUrl.http + `/api/chat/chatGetmessageOffLine`, {}, {
header: {
token: this.user.token,
},
}).then(res => {
console.log('服务器返回离线消息', res);
});
}
# 十四、我的群聊列表
# 1. 我的群聊列表接口说明
接口文档查看:二十三、我的群聊列表
# 2. 从好友列表和我的两个页面进入群聊列表
在我的好友列表页面 /pages/friendsList/friendsList.nvue
export default {
...
data() {
return {
...
topMenus:[
{icon:'man-add-fill', title:'新的朋友', url:'/pages/applyMyfriend/applyMyfriend'},
// {icon:'tags-fill', title:'标签', url:''},
// {icon:'chrome-circle-fill', title:'朋友圈', url:''},
{icon:'plus-people-fill', title:'我的群聊列表',
url:`/pages/applyMyfriend/applyMyfriend?
action=grouplist&title=${encodeURIComponent('我的群聊列表')}`},
],
...
}
},
在我的页面 /pages/wode/wode.nvue
data() {
return {
...,
uCellMenus:[
...,
{icon:'man-add-fill',title:'我的群聊',url:`/pages/applyMyfriend/applyMyfriend?
action=grouplist&title=${encodeURIComponent('我的群聊列表')}`},
...
],
}
},
# 3. 我的群聊列表
在页面 /pages/applyMyfriend/applyMyfriend.vue
<template>
<view>
<!-- 导航栏 -->
<chat-navbar :title="title" :fixed="true" :showPlus="false" :showUser="false" :showBack="true"
navbarClass="bg-light" :h5WeiXinNeedNavbar="false">
</chat-navbar>
<!-- 新的朋友 -->
<view class="p-3" v-if="goodfriendapply.alldata.length > 0">
<!-- 申请加我为好友的用户列表 -->
<u-cell-group v-if="action == 'applyMyfriend'"
title="申请加我为好友的用户列表" :border="false">
<u-cell v-for="(item,index) in goodfriendapply.alldata"
: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.user.nickname || item.user.username}}</text>
<text class="font-sm text-light-muted">{{item.nickname}}</text>
</view>
</view>
<view slot="value">
<view v-if="item.status == 'pending'"
@click="openUserInfo(item,'doapply')">
<u-button text="处理" type="success" size="small"></u-button>
</view>
<view v-else>
<text class="font-sm text-primary ">{{item|statusText}}</text>
</view>
</view>
</u-cell>
</u-cell-group>
<!-- 我的群聊列表 -->
<u-cell-group v-if="action == 'grouplist'"
title="我加入过的群聊列表" :border="false">
<u-cell v-for="(item,index) in goodfriendapply.alldata"
:key="index">
<view slot="title" class="flex align-center"
@click="openUserInfo(item,'chatGroup')"
style="height: 500px;">
<!-- 群头像组合展示 -->
<view class="group-avatar-container">
<view v-if="item.avatarArr && item.avatarArr.length > 0">
<u-avatar
v-for="(avatar, idx) in item.avatarArr"
:key="idx"
:src="avatar"
shape="square"
:size="getAvatarSize(item.avatarArr.length)"
:customStyle="getAvatarStyle(idx, item.avatarArr.length)"
imgMode="aspectFill"
></u-avatar>
</view>
</view>
<!-- 群昵称 -->
<view class="flex flex-column justify-center ml-2 mr-1">
<text class="font-md">{{item|groupname}}</text>
<text class="font-sm text-light-muted">{{item.create_time + `加入该群聊`}}</text>
</view>
</view>
<view slot="value">
<view @click="openUserInfo(item,'chatGroup')">
<u-button text="进群聊天" type="success" size="small"></u-button>
</view>
</view>
</u-cell>
</u-cell-group>
<!-- 上拉加载更多 --><!-- 正在加载中 --><!-- 没有更多数据了 -->
<view>
<!-- 上拉加载更多 -->
<view v-if="!loadingIcon.show && moreData"
class="flex align-center justify-center py-3">
<text class="font-sm text-light-muted">上拉加载更多数据</text>
</view>
<!-- 正在加载中 -->
<u-loading-icon :text="loadingIcon.text" :textSize="loadingIcon.textSize"
:mode="loadingIcon.mode" :show="loadingIcon.show"></u-loading-icon>
<!-- 没有更多数据了 -->
<view v-if="!loadingIcon.show && !moreData"
class="w-100">
<u-divider text="我是有底线的"></u-divider>
</view>
</view>
</view>
<view v-else>
<u-empty></u-empty>
</view>
</view>
</template>
<script>
import toolJs from '@/common/mixins/tool.js';
import {requestUrl} from '@/common/mixins/configData.js';
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex';
export default {
mixins:[toolJs],
data() {
return {
page:1,
loadingIcon:{
text:'数据加载中',
textSize:12,
mode:'circle',
show:false,
},
moreData:true, // 是否可加载更多数据
action: 'applyMyfriend',
title:'',
}
},
onLoad(e) {
console.log('我的信息',this.me);
console.log('好友申请信息',this.goodfriendapply);
// if(!this.me || this.me.role == 'visitor') return this.navigateBack();
if(!this.me) return this.navigateBack();
if(!e || !e.action){
if(this.me.role == 'visitor') return this.navigateBack();
}
this.title = e.title ? decodeURIComponent(e.title) : '新的朋友';
uni.setNavigationBarTitle({
title: this.title
});
if(e.action == 'grouplist'){
this.action = 'grouplist';
}
},
computed:{
...mapState({
me : state => state.Chatuser.regloginUser,
goodfriendapply : state => state.Chatuser.goodfriendapply,
}),
},
filters:{
//头像
avatarShow(item){
if(item && item.user && item.user.avatar){
let avatar = item.user.avatar;
avatar = avatar && avatar.startsWith('http') ? avatar : `${requestUrl.http}${avatar}`;
return avatar;
}else if(item && item.avatar){
let avatar = item.avatar;
// 如果是群聊且包含逗号,取第一个头像
if(avatar && avatar.indexOf(',') > 0){
avatar = avatar.split(',')[0];
avatar = avatar && avatar.startsWith('http') ? avatar : `${requestUrl.http}${avatar}`;
return avatar;
}
}
return ``;
},
// 处理状态的显示
statusText(item){
let status = item && item.status;
let text = {
agree:'已同意',
refuse:'已拒绝',
ignore:'已忽略'
}
return text[status];
},
// 群名称
groupname(item){
if(item){
return item.name && item.name.length > 10 ?
item.name.substring(0,10) + `...` : item.name;
}
return ``;
}
},
// 监听用户下拉
onPullDownRefresh() {
this.getNewData();
},
//监听触底
onReachBottom() {
console.log('拉到底部了');
this.loadMore();
},
onShow() {
this.getNewData();
},
methods: {
// 获取最新数据
getNewData(){
this.page = 1;
const limit = 3;
if(this.action == 'applyMyfriend'){
this.$store.dispatch('getGoodfriendapply',{ page:this.page, limit:limit }).then(res=>{
uni.stopPullDownRefresh();
// uni.showToast({title: '刷新数据成功',icon: 'none'});
});
}else if(this.action == 'grouplist'){
this.getGroupList(this.page,limit).then(list => {
console.log('获取群列表最新数据',list);
this.goodfriendapply.alldata = list;
this.moreData = true;
});
}
},
// 获取群列表
getGroupList(page,limit){
return new Promise((resolve,reject)=>{
uni.$u.http.get(requestUrl.http + `/api/chat/grouplist/${page}`, {
params:{
limit:limit
},
header: {
token: this.me.token,
},
}).then(res => {
console.log('服务器返回的群列表数据', res);
let list = res.data.data;
if(list.length == 0) return resolve([]);
// 处理群头像数据
list.forEach(item => {
// 确保avatar存在
if (!item.avatar) {
item.avatarArr = [];
return;
}
// 拆分头像字符串
let avatars = item.avatar.split(',');
// 处理每个头像URL
item.avatarArr = avatars.filter(avatar => avatar && avatar.trim() !== '') // 过滤空值
.slice(0, 4) // 最多取4个
.map(avatar => {
// 处理相对路径
if (avatar.startsWith('/')) {
return requestUrl.http + avatar;
}
// 处理可能缺少协议的URL
if (!avatar.startsWith('http')) {
return requestUrl.http + '/' + avatar;
}
return avatar;
});
resolve(list);
});
}).finally(()=>{
uni.stopPullDownRefresh();
});
});
},
// 获取头像大小
getAvatarSize(count) {
if (count === 1) return 40;
return 18; // 多个头像时使用小尺寸
},
// 获取头像样式
getAvatarStyle(index, count) {
if (count === 1) {
return {};
}
// 位置计算
const positions = {
2: [
{ top: '0', left: '0' },
{ top: '22px', left: '22px' }
],
3: [
{ top: '0', left: '11px' },
{ top: '22px', left: '0' },
{ top: '22px', left: '22px' }
],
4: [
{ top: '0', left: '0' },
{ top: '0', left: '22px' },
{ top: '22px', left: '0' },
{ top: '22px', left: '22px' }
]
};
const style = {
position: 'absolute',
'z-index': 1,
'border': '1px solid #ffffff'
};
if (positions[count] && positions[count][index]) {
return Object.assign(style, positions[count][index]);
}
return style;
},
//触底加载下一页
loadMore(){
if(this.moreData){
this.loadingIcon.show = true;
const limit = 3;
this.page ++;
if(this.action == 'applyMyfriend'){
this.$store.dispatch('getGoodfriendapply',{ page:this.page, limit:limit })
.then(res=>{
console.log('页面显示是否有下一页',this.goodfriendapply.moredata);
this.moreData = this.goodfriendapply.moredata;
console.log('看一下此时的加载状态',this.goodfriendapply.loadingStatus);
this.loadingIcon.show = this.goodfriendapply.loadingStatus;
if(!this.moreData){
this.loadingIcon.show = false;
}
});
}else if(this.action == 'grouplist'){
console.log('执行下拉加载更多');
this.getGroupList(this.page,limit).then(list => {
console.log('获取群列表加载更多数据',list);
this.goodfriendapply.alldata = [...this.goodfriendapply.alldata, ...list];
this.loadingIcon.show = false;
if(this.goodfriendapply.alldata.length < this.page * limit){
this.moreData = false;
}
});
}
}
},
openUserInfo(item,action){
if(action == 'chatGroup'){
console.log('进群聊天',item);
// 进入聊天页 传一下用户的信息过去在页面展示
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{
uni.navigateTo({
url: `/pages/userinfo/userinfo?uuid=${item.user.uuid}&action=${action}`,
});
}
},
}
}
</script>
<style>
.group-avatar-container {
position: relative;
width: 40px;
height: 40px;
border-radius: 10rpx;
overflow: hidden;
background-color: #f5f5f5;
}
</style>
# 十五、获取群资料信息进行群设置
# 1. 获取群资料信息接口说明
接口文档查看:二十四、获取群资料信息
# 2. 获取群资料并展示群用户头像
在页面 /pages/setpageInfo/setpageInfo.vue
<template>
<view>
<!-- 导航栏 -->
...
<!-- 内容 -->
<view>
<!-- 聊天相关 -->
<view v-if="setdata.action == 'chatset'">
...
</view>
<!-- 聊天页设置 -->
<view v-if="setdata.action == 'chatpageset'">
<view v-if="chatpageset.id && chatpageset.chatType">
<!-- 头像部分 -->
<view class="flex flex-wrap p-3">
<!-- 单聊 -->
<view v-if="chatpageset.chatType == 'single'"
class="flex flex-column align-center justify-center mr-2">
<u-avatar :src="chatpageset|avatarShow" shape="square"
customStyle="border:1px solid #eeeeee;" :size="36"></u-avatar>
<text class="font-small mt-1 text-muted">{{chatpageset.name}}</text>
</view>
<!-- 群聊 循环头像昵称 -->
<view v-if="chatpageset.chatType == 'group'"
class="flex flex-row flex-wrap">
<view v-for="(item,index) in chatpageset.users" :key="index"
class="flex flex-column align-center justify-center mr-1 mb-2">
<u-avatar :src="item|avatarShow" shape="square"
customStyle="border:1px solid #eeeeee;" :size="36"></u-avatar>
<text class="font-small mt-1 text-muted">{{item|groupusername}}</text>
</view>
</view>
<!-- 加入群聊 -->
<view class="..."
style="width: 36px;height: 36px;border-style: dashed;"
@click="clickCell('选择好友', 'addUserToGroup')">
<u-icon name="plus" size="24" color="#999999"></u-icon>
</view>
<!-- 删除群聊用户 -->
<view v-if="chatpageset.chatType == 'group'"
class="border flex align-center justify-center ml-1"
style="width: 36px;height: 36px;border-style: dashed;"
@click="clickCell('群组删除某个用户', 'deleteUserFromGroup')">
<u-icon name="minus" size="24" color="#999999"></u-icon>
</view>
</view>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
...
</view>
</view>
</view>
</view>
</template>
<script>
...
export default {
mixins:[toolJs],
data() {
return {
...,
chatpageset:{
users:[],
},
}
},
onLoad(e) {
...
// 动态生成数据
if(e.action){
this.setdata.action = e.action;
if(this.setdata.action == 'chatset'){
...
}else if(this.setdata.action == 'chatpageset'){
...
console.log('设置相关', this.chatpageset);
//群聊设置
if(this.chatpageset.chatType == 'group'){
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;
console.log('显示到页面的群信息',this.chatpageset);
});
}
}
}
},
filters:{
//头像
avatarShow(item){
if(item){
if(item.avatar){
return item.avatar.startsWith('http') ? item.avatar :
`${requestUrl.http}${item.avatar}`;
}
return item.user.avatar.startsWith('http') ? item.user.avatar :
`${requestUrl.http}${item.user.avatar}`;
}
return ``;
},
// 群用户称呼
groupusername(item){
if(item){
if(item.nickname){
return item.nickname.length > 3 ?
item.nickname.substring(0,3) + `...` : item.nickname;
}
if(item.user.nickname){
return item.user.nickname.length > 3 ?
item.user.nickname.substring(0,3) + `...` : item.user.nickname;
}
if(item.user.username){
return item.user.username.length > 3 ?
item.user.username.substring(0,3) + `...` : item.user.username;
}
}
return ``;
}
},
methods: {
...,
clickCell(title, type){
console.log(title, type);
if(type == 'addUserToGroup'){
// 单独建群
if(this.chatpageset.chatType == 'single'){
let op = {
action: 'addUserToGroup',
title: encodeURIComponent(title),
};
uni.navigateTo({
url: `/pages/friendsList/friendsList?action=${op.action}&title=${op.title}`,
});
}else if(this.chatpageset.chatType == 'group'){
console.log('往当前群里拉人')
}
}
},
}
}
</script>
# 十六、修改群名称
# 1. 修改群名称接口说明
接口文档查看:二十五、修改群名称(成功后通过webSocket通知群聊用户)
# 2. 页面布局
在页面 /pages/setpageInfo/setpageInfo.vue
<template>
<view>
<!-- 导航栏 -->
...
<!-- 内容 -->
<view>
<!-- 聊天相关 -->
<view v-if="setdata.action == 'chatset'">
...
</view>
<!-- 聊天页设置 -->
<view v-if="setdata.action == 'chatpageset'">
<view v-if="chatpageset.id && chatpageset.chatType">
<!-- 头像部分 -->
<view class="flex flex-wrap p-3">
...
</view>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
<!-- 群相关项目 -->
<view v-if="chatpageset.chatType == 'group'">
<!-- 群聊名称 -->
<u-cell-group>
<u-cell>
<view slot="title">
<view class="mb-2">
<text class="font-weight-bold">群名称</text>
</view>
<u-input v-model="chatpageset.name"
border="false" :maxlength="20"
:adjustPosition="false" clearable
placeholder="群名称最多20个字"
@confirm="groupUpdateName"
:disabled="!chatpageset.user_id"></u-input>
</view>
</u-cell>
</u-cell-group>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
<!-- 群公告 -->
<u-cell-group>
<u-cell>
<view slot="title">
<view class="mb-2">
<text class="font-weight-bold">群公告</text>
</view>
<u-textarea v-model="chatpageset.remark"
border="false" placeholder="群公告设置"
:adjustPosition="false"
:maxlength="200" autoHeight count></u-textarea>
</view>
</u-cell>
</u-cell-group>
<u-gap height="10" bgColor="#EDEDED"></u-gap>
</view>
{# 查找聊天内容 #}
...
<!-- 消息设置 -->
...
</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'){
...
//群聊设置
if(this.chatpageset.chatType == 'group'){
uni.$u.http.get(requestUrl.http +
`/api/chat/groupinfo/${this.chatpageset.id}`,{
header:{
token: this.me.token,
}
}).then(res =>{
...
//console.log('显示到页面的群信息',this.chatpageset);
this.chatpageset.user_id =
this.chatpageset.user_id == this.me.id ? true : false;
console.log('显示到页面的群信息',this.chatpageset);
});
}
}
}
},
...,
methods: {
// 更新群名称
groupUpdateName(){
uni.$u.http.post(requestUrl.http + `/api/chat/groupUpdateName`, {
id: this.chatpageset.id,
name: this.chatpageset.name,
}, {
header: {
token: this.me.token,
},
}).then(res => {
console.log('服务器返回群更新结果', res);
if(res.data.data == 'ok'){
uni.showToast({title: '群名称修改成功', icon:'none'});
// 全局通知更新群名称(nvue页面H5端页面标题有兼容问题)
// uni.$emit('groupUpdateName',{
// name: this.chatpageset.name,
// });
// 使用vuex兼容H5端nvue页面标题
this.$store.dispatch('groupUpdateName',{
name: this.chatpageset.name,
});
}
}).catch(err =>{
console.log('群名称更新失败',err);
uni.showModal({
content: err.data.data,
showCancel:false,
confirmText:'我知道了'
});
});
},
...
}
}
</script>
# 3. 更新群名称完成之后返回聊天页修改导航栏群名称和页面名称
采用vuex方式修改群名称(全局通知更新群名称(nvue页面H5端页面标题有兼容问题))
在文件 /store/modules/chatuser.js
export default {
state: {
...
// 群名称更新处理
groupname:'',
},
actions: {
...
// 更新群名称
groupUpdateName({commit,state,dispatch}, payload = {}){
state.groupname = payload.name;
},
},
}
# 4. 聊天页更新群名称
在页面 /pages/chat/chat.nvue
<template>
<view>
...
</view>
</template>
<script>
...
export default {
...,
onLoad(e) {
...
try{
...
// 监听处理接收到的消息
...
// 监听更新群名称
// uni.$on('groupUpdateName', res => {
// this.groupUpdateName(res);
// });
// uni.$on('groupUpdateName', this.groupUpdateName);
}catch{
...
}
},
onShow() {
this.arg.name = this.groupname;
// #ifdef H5
document.title = this.groupname;
// #endif
},
destroyed() {
//销毁聊天对象信息
this.chatClass.destroyChatToObject();
//销毁监听更新群名称
// uni.$off('groupUpdateName', this.groupUpdateName);
},
computed:{
...mapState({
...,
groupname :state=>state.Chatuser.groupname,
}),
//标题
pagetitle(){
if(this.arg && this.arg.name){
// #ifdef H5 || APP
if(this.arg.name.length > 15){
return this.arg.name.substring(0,15) + '...';
}
return this.arg.name;
// #endif
// #ifdef MP
if(this.arg.name.length > 5){
return this.arg.name.substring(0,5) + '...';
}
return this.arg.name;
// #endif
}
return ``;
},
chatContentStyle(){
...
//如果是h5端用微信打开并且不需要导航栏的时候
if(this.isWeixinBrowser() && !this.h5WeiXinNeedNavbar){
...
uni.setNavigationBarTitle({
title: this.pagetitle,
});
}
},
...
},
methods: {
...,
// 更新聊天页群名称
groupUpdateName(e){
console.log('更新聊天页群名称',e);
// 不要直接给计算属性赋值,计算属性默认只有getter,没有setter
// 修改计算属性所依赖的数据
// this.pagetitle = e.name;
// 更新arg中的name,这样计算属性pagetitle会自动更新
this.arg.name = e.name;
// 同时更新页面标题
console.log('页面标题',this.pagetitle);
uni.setNavigationBarTitle({
title: this.groupname || this.pagetitle,
});
},
},
}
</script>
# 5. 群名称修改完成之后,通知其他群成员
在组件 /components/chat-item/chat-item.vue
<!-- 进群首条消息提示 -->
<view v-if="item.type == 'systemNotice'"
class="flex flex-row align-center justify-center py-3">
<text class="font-sm text-light-muted">{{item.data}}</text>
</view>
# 十七、修改群公告
内容较多,在新页面查看:聊天通讯群组更多内容