前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >uni-app实战之社区交友APP(7)消息页开发

uni-app实战之社区交友APP(7)消息页开发

作者头像
cutercorley
发布2021-02-04 11:06:31
2.1K0
发布2021-02-04 11:06:31
举报
文章被收录于专栏:Corley的开发笔记

文章目录

小编目前在做毕业设计,主题为“高考志愿信息交流平台”,面向高中生和大学生,辛苦各位读者大佬朋友们填下问卷,点击链接https://www.wjx.cn/jq/98944127.aspx或扫描二维码、微信小程序码均可,希望各位能提供一些调查数据,先在这里谢过各位了(*^_^*)

前言

本文主要介绍了消息也的开发,主要包括3部分: 消息列表页开发,包括页面配置、消息列表组件的开发和封装、下拉刷新功能和下拉弹出层组件的使用; 我的好友列表页开发,包括页面配置、导航组件开发、好友列表组件开发和封装、以及性别图标显示; 聊天页面开发,包括页面配置、输入框组件开发、聊天列表组件的开发和封装、以及聊天页功能完善。

一、消息列表页面开发

1.pages.json配置

消息页也需要在pages.json中配置顶部导航栏,包括好友和菜单图标,如下:

代码语言:javascript
复制
{
    "path" : "pages/msg/msg",
    "style" :                                                                                    
    {
        "navigationBarTitleText": "消息列表",
        "enablePullDownRefresh": false,
        "app-plus": {
            "titleNView": {
                "buttons": [
                    {
                        "color":"#333333",
                        "colorPressed":"#FD597C",
                        "float":"left",
                        "fontSize":"20px",
                        "fontSrc":"/static/iconfont.ttf",
                        "text": "\ue60d"
                    },
                    {
                        "color":"#333333",
                        "colorPressed":"#FD597C",
                        "float":"right",
                        "fontSize":"20px",
                        "fontSrc":"/static/iconfont.ttf",
                        "text": "\ue652"
                    }
                ]
            }
        }
    }
    
}

为了本文项目练手所需,需要在https://www.iconfont.cn/中下载好友男性女性个人发送等图标,同时更新icon.css和iconfont.ttf更新为最新状态。

显示:

显然,已经配置好左侧和右侧的图标。

2.消息列表组件开发和封装

先实现消息列表项,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 消息列表 -->
		<view class="flex align-center p-2">
			<image src="/static/img/userpic/5.jpg" style="width: 80rpx; height: 80rpx;" class="rounded-circle mr-2"></image>
			<view class="flex flex-column flex-1">
				<view class="flex align-center justify-between">
					<text class="font-md">Corley</text>
					<text class="font text-secondry">13:34</text>
				</view>
				<view class="flex align-center justify-between">
					<text class="text-secondry">大佬,你好</text>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				
			}
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

base.css如下:

代码语言:javascript
复制
/* 内外边距 */
.p-2 {
	padding: 20rpx;
}

/* flex布局 */
.flex {
	/* #ifndef APP-APP-PLUS-NVUE */
	display: flex;
	/* #endif */
	flex-direction: row;
}

.flex-wrap {
	flex-wrap: wrap;
}

.flex-column {
	flex-direction: column;
}

.align-center {
	align-items: center;
}

.align-start {
	align-items: flex-start;
}

.justify-between {
	justify-content: space-between;
}

.justify-center {
	justify-content: center;
}

.flex-1 {
	flex: 1;
}

/* 圆角 */
.rounded-circle {
	border-radius: 100%;
}

.rounded {
	border-radius: 8rpx;
}

/* margin */
.mr-2 {
	margin-right: 20rpx;
}

.mr-1 {
	margin-right: 10rpx;
}

.my-2 {
	margin-top: 20rpx;
	margin-bottom: 20rpx;
}

.my-1 {
	margin-top: 10rpx;
	margin-bottom: 10rpx;
}

.mx-2 {
	margin-left: 20rpx;
	margin-right: 20rpx;
}

.mx-1 {
	margin-left: 10rpx;
	margin-right: 10rpx;
}

.mt-1 {
	margin-top: 10rpx;
}

.ml-auto {
	margin-left: auto;
}

.ml-2 {
	margin-left: 20rpx;
}

/* padding */
.p-2 {
	padding-left: 20rpx;
	padding-right: 20rpx;
	padding-top: 20rpx;
	padding-bottom: 20rpx;
}

.px-5 {
	padding-left: 50rpx;
	padding-right: 50rpx;
}

.px-3 {
	padding-left: 30rpx;
	padding-right: 30rpx;
}

.px-2 {
	padding-left: 20rpx;
	padding-right: 20rpx;
}

.px-1 {
	padding-left: 10rpx;
	padding-right: 10rpx;
}

.py-3 {
	padding-top: 30rpx;
	padding-bottom: 30rpx;
}

.py-2 {
	padding-top: 20rpx;
	padding-bottom: 20rpx;
}

.pt-7 {
	padding-top: 70rpx;
}

.pb-2 {
	padding-bottom: 20rpx;
}

/* 边框 */
.border {
	border-width: 1rpx;
	border-style: solid;
	border-color: #DEE2E6;
}

.border-bottom {
	border-bottom: 1rpx solid #DEE2E6;
}

.border-top {
	border-top: 1rpx solid #DEE2E6;
}

.border-light-secondary {
	border: 1rpx solid #AAA8AB;
}

/* 字体 */
.font-lg {
	font-size: 40rpx;
}

.font-md {
	font-size: 35rpx;
}

.font {
	font-size: 30rpx;
}

.font-sm {
	font-size: 25rpx;
}

.font-weight-bold {
	font-weight: bold;
}

/* 文字颜色 */
.text-white {
	color: #FFFFFF;
}

.text-light-muted {
	color: #A9A5A0;
}

/* 文字换行溢出处理 */
.text-ellipsis {
	/* #ifndef APP-PLUS-APP-PLUS-NVUE */
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	/* #endif */
	/* #ifdef APP-PLUS-APP-PLUS-NVUE */
	lines: 1;
	/* #endif */
}

/* 宽度 */
/* #ifndef APP-PLUS-NVUE */
.w-100 {
	width: 100%;
}

/* #endif */

/* scroll-view */
/* #ifndef APP-PLUS-NVUE */
.scroll-row {
	width: 100%;
	white-space: nowrap;
}

.scroll-row-item {
	display: inline-block !important;
}

/* #endif */

/* 背景 */
.bg-light {
	background-color: #F8F9FA;
}

.bg-secondary {
	background-color: #AAA8AB;
}

.bg-white {
	background-color: #FFFFFF;
}

.bg-dark {
	background-color: #333333;
}

/* 定位 */
.position-relative {
	position: relative;
}

.position-absolute {
	position: absolute;
}

.position-fixed {
	position: fixed;
}

/* 定位-固定顶部 */
.fixed-top {
	position: fixed;
	top: 0;
	right: 0;
	left: 0;
	z-index: 1030;
}

/* 定位-固定底部 */
.fixed-bottom {
	position: fixed;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 1030;
}

.top-0 {
	top: 0;
}

.left-0 {
	left: 0;
}

.right-0 {
	right: 0;
}

.bottom-0 {
	bottom: 0;
}

common.css如下:

代码语言:javascript
复制
/* 本项目全局样式 */
/* 背景 */
.bg-main {
	background-color: #FF4A6A;
}

/* 文本颜色 */
.text-main {
	color: #FF4A6A;
}

.text-secondry {
	color: #AAA8AB;
}

.text-dark {
	color: #333333;
}

/* 下拉弹出框样式 */
.popup-content {
	background-color: #fff;
	padding: 7px;
}

显示:

已经正常显示了消息列表。

再实现新消息提示,需要使用uni-app提供的扩展组件数字角标uni-badge,可参考https://ext.dcloud.net.cn/plugin?id=21。 其位于hello_uniapp演示项目的components/uni-badge目录下,直接将uni-badge目录拷贝至本项目的components/uni-ui目录下即可。

如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 消息列表 -->
		<view class="flex align-center p-2 border-bottom border-" hover-class="bg-light">
			<image src="/static/img/userpic/5.jpg" style="width: 80rpx; height: 80rpx;" class="rounded-circle mr-2"></image>
			<view class="flex flex-column flex-1">
				<view class="flex align-center justify-between">
					<text class="font-md">Corley</text>
					<text class="font text-secondry">13:34</text>
				</view>
				<view class="flex align-center justify-between">
					<text class="text-secondry">大佬,你好</text>
					<uni-badge text="3" type="error"></uni-badge>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import uniBadge from '@/components/uni-ui/uni-badge/uni-badge.vue';
	export default {
		data() {
			return {
				
			}
		},
		components: {
			uniBadge
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

显示:

可以看到,已经出现消息提示数字角标。

现在处理消息长度过长的问题,使用文字溢出的样式即可,如下:

代码语言:javascript
复制
<view class="flex align-center justify-between">
    <text class="text-secondry text-ellipsis" style="max-width: 500rpx;">大佬,你好,我想请教一个关于uni-app的问题,不知道是否方便?</text>
    <uni-badge text="3" type="error"></uni-badge>
</view>

显示:

多余的文字就会以省略号的形式显示。

将其封装为组件时,需要先构建数据,因为数据中传入的时间形式是时间戳,因此需要转化为时间字符串,使用时间处理库来转换时间,在common目录下新建time.js用于处理时间,如下:

代码语言:javascript
复制
export default {
	// 计算当前日期星座
	getHoroscope(date) {
		let c = ['摩羯', '水瓶', '双鱼', '白羊', '金牛', '双子', '巨蟹', '狮子', '处女', '天秤', '天蝎', '射手', '摩羯']
		date = new Date(date);
		let month = date.getMonth() + 1;
		let day = date.getDate();
		let startMonth = month - (day - 14 < '865778999988'.charAt(month));
		return c[startMonth] + '座';
	},
	// 计算指定时间与当前的时间差
	sumAge(data) {
		let dateBegin = new Date(data.replace(/-/g, "/"));
		let dateEnd = new Date(); //获取当前时间
		let dateDiff = dateEnd.getTime() - dateBegin.getTime(); //时间差的毫秒数
		let dayDiff = Math.floor(dateDiff / (24 * 3600 * 1000)); //计算出相差天数
		let leave1 = dateDiff % (24 * 3600 * 1000) //计算天数后剩余的毫秒数
		let hours = Math.floor(leave1 / (3600 * 1000)) //计算出小时数
		//计算相差分钟数
		let leave2 = leave1 % (3600 * 1000) //计算小时数后剩余的毫秒数
		let minutes = Math.floor(leave2 / (60 * 1000)) //计算相差分钟数
		//计算相差秒数
		let leave3 = leave2 % (60 * 1000) //计算分钟数后剩余的毫秒数
		let seconds = Math.round(leave3 / 1000)
		return dayDiff + "天 " + hours + "小时 ";
	},
	// 获取聊天时间(相差300s内的信息不会显示时间)
	getChatTime(v1, v2) {
		v1 = v1.toString().length < 13 ? v1 * 1000 : v1;
		v2 = v2.toString().length < 13 ? v2 * 1000 : v2;
		if (((parseInt(v1) - parseInt(v2)) / 1000) > 300) {
			return this.gettime(v1);
		}
	},
	// 人性化时间格式
	gettime(shorttime) {
		shorttime = shorttime.toString().length < 13 ? shorttime * 1000 : shorttime;
		let now = (new Date()).getTime();
		let cha = (now - parseInt(shorttime)) / 1000;

		if (cha < 43200) {
			// 当天
			return this.dateFormat(new Date(shorttime), "{A} {t}:{ii}");
		} else if (cha < 518400) {
			// 隔天 显示日期+时间
			return this.dateFormat(new Date(shorttime), "{Mon}月{DD}日 {A} {t}:{ii}");
		} else {
			// 隔年 显示完整日期+时间
			return this.dateFormat(new Date(shorttime), "{Y}-{MM}-{DD} {A} {t}:{ii}");
		}
	},

	parseNumber(num) {
		return num < 10 ? "0" + num : num;
	},

	dateFormat(date, formatStr) {
		let dateObj = {},
			rStr = /\{([^}]+)\}/,
			mons = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'];

		dateObj["Y"] = date.getFullYear();
		dateObj["M"] = date.getMonth() + 1;
		dateObj["MM"] = this.parseNumber(dateObj["M"]);
		dateObj["Mon"] = mons[dateObj['M'] - 1];
		dateObj["D"] = date.getDate();
		dateObj["DD"] = this.parseNumber(dateObj["D"]);
		dateObj["h"] = date.getHours();
		dateObj["hh"] = this.parseNumber(dateObj["h"]);
		dateObj["t"] = dateObj["h"] > 12 ? dateObj["h"] - 12 : dateObj["h"];
		dateObj["tt"] = this.parseNumber(dateObj["t"]);
		dateObj["A"] = dateObj["h"] > 12 ? '下午' : '上午';
		dateObj["i"] = date.getMinutes();
		dateObj["ii"] = this.parseNumber(dateObj["i"]);
		dateObj["s"] = date.getSeconds();
		dateObj["ss"] = this.parseNumber(dateObj["s"]);

		while (rStr.test(formatStr)) {
			formatStr = formatStr.replace(rStr, dateObj[RegExp.$1]);
		}
		return formatStr;
	},
	// 获取年龄
	getAgeByBirthday(data) {
		let birthday = new Date(data.replace(/-/g, "\/"));
		let d = new Date();
		return d.getFullYear() - birthday.getFullYear() - ((d.getMonth() < birthday.getMonth() || d.getMonth() == birthday.getMonth() &&
			d.getDate() < birthday.getDate()) ? 1 : 0);
	}
}

msg.vue创建数据如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 消息列表 -->
		<block v-for="(item, index) in list" :key="index">
			<view class="flex align-center p-2 border-bottom border-" hover-class="bg-light">
				<image :src="item.avatar" style="width: 80rpx; height: 80rpx;" class="rounded-circle mr-2"></image>
				<view class="flex flex-column flex-1">
					<view class="flex align-center justify-between">
						<text class="font-md">{{item.username}}</text>
						<text class="font-sm text-secondry">{{item.update_time | formatTime}}</text>
					</view>
					<view class="flex align-center justify-between">
						<text class="text-secondry text-ellipsis" style="max-width: 500rpx;">{{item.data}}</text>
						<uni-badge :text="item.noread" type="error"></uni-badge>
					</view>
				</view>
			</view>
		</block>
	</view>
</template>
<script>
	import uniBadge from '@/components/uni-ui/uni-badge/uni-badge.vue';
	import $T from '@/common/time.js';
	export default {
		data() {
			return {
				list: [
					{
						avatar: '/static/img/userpic/5.jpg',
						username: 'Corley',
						update_time: 1612075613,
						data: '我再看看吧,谢谢大佬',
						noread: 5
					},
					{
						avatar: '/static/img/userpic/1.jpg',
						username: 'Brynn',
						update_time: 1612075606,
						data: '大佬,你好,我想请教一个关于uni-app的问题,不知道是否方便?',
						noread: 0
					},
					{
						avatar: '/static/img/userpic/3.jpg',
						username: 'Ellie',
						update_time: 1612075255,
						data: '那我也有点懵了',
						noread: 3
					},
					{
						avatar: '/static/img/userpic/9.jpg',
						username: 'Ainsley',
						update_time: 1612075983,
						data: '真机调试不太方便,我就用浏览器和微信开发者工具调试的。',
						noread: 1
					},
					{
						avatar: '/static/img/userpic/17.jpg',
						username: 'Bella',
						update_time: 1612074571,
						data: 'mysql16进制数据怎么查询才快呢?',
						noread: 2
					}
				]
			}
		},
		components: {
			uniBadge
		},
		// 过滤器
		filters: {
			formatTime(value) {
				return $T.gettime(value);
			}
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

导入了time.js中的gettime()函数获取格式化时间,同时创建过滤器来将时间戳格式化。

显示:

可以看到,显示出了消息列表,并且时间是格式化后的时间。

此时再封装为组件,components下新建msg目录,下新建msg-list.vue组件如下:

代码语言:javascript
复制
<template>
	<view class="flex align-center p-2 border-bottom border-" hover-class="bg-light">
		<image :src="item.avatar" style="width: 80rpx; height: 80rpx;" class="rounded-circle mr-2"></image>
		<view class="flex flex-column flex-1">
			<view class="flex align-center justify-between">
				<text class="font-md">{{item.username}}</text>
				<text class="font-sm text-secondry">{{item.update_time | formatTime}}</text>
			</view>
			<view class="flex align-center justify-between">
				<text class="text-secondry text-ellipsis" style="max-width: 500rpx;">{{item.data}}</text>
				<uni-badge :text="item.noread" type="error"></uni-badge>
			</view>
		</view>
	</view>
</template>

<script>
	import uniBadge from '@/components/uni-ui/uni-badge/uni-badge.vue';
	import $T from '@/common/time.js';
	export default {
		props: {
			item: Object,
			index: Number
		},
		// 过滤器
		filters: {
			formatTime(value) {
				return $T.gettime(value);
			}
		},
		components: {
			uniBadge
		}
	}
</script>

<style>
</style>

msg.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 消息列表 -->
		<block v-for="(item, index) in list" :key="index">
			<msg-list :item="item" :index="index"></msg-list>
		</block>
	</view>
</template>
<script>
	import msgList from '@/components/msg/msg-list.vue';
	export default {
		data() {
			return {
				list: [
					{
						avatar: '/static/img/userpic/5.jpg',
						username: 'Corley',
						update_time: 1612075613,
						data: '我再看看吧,谢谢大佬',
						noread: 5
					},
					{
						avatar: '/static/img/userpic/1.jpg',
						username: 'Brynn',
						update_time: 1612075606,
						data: '大佬,你好,我想请教一个关于uni-app的问题,不知道是否方便?',
						noread: 0
					},
					{
						avatar: '/static/img/userpic/3.jpg',
						username: 'Ellie',
						update_time: 1612075255,
						data: '那我也有点懵了',
						noread: 3
					},
					{
						avatar: '/static/img/userpic/9.jpg',
						username: 'Ainsley',
						update_time: 1612075983,
						data: '真机调试不太方便,我就用浏览器和微信开发者工具调试的。',
						noread: 1
					},
					{
						avatar: '/static/img/userpic/17.jpg',
						username: 'Bella',
						update_time: 1612074571,
						data: 'mysql16进制数据怎么查询才快呢?',
						noread: 2
					}
				]
			}
		},
		components: {
			msgList
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

可以达到与之前相同的效果。

3.下拉刷新功能实现

消息页的下拉刷新在pages.json中配置enablePullDownRefresh属性为true,pages.json文件配置如下:

代码语言:javascript
复制
{
    "path" : "pages/msg/msg",
    "style" :                                                                                    
    {
        "navigationBarTitleText": "消息列表",
        "enablePullDownRefresh": true,
        "app-plus": {
            "titleNView": {
                "buttons": [
                    {
                        "color":"#333333",
                        "colorPressed":"#FD597C",
                        "float":"left",
                        "fontSize":"20px",
                        "fontSrc":"/static/iconfont.ttf",
                        "text": "\ue60d"
                    },
                    {
                        "color":"#333333",
                        "colorPressed":"#FD597C",
                        "float":"right",
                        "fontSize":"20px",
                        "fontSrc":"/static/iconfont.ttf",
                        "text": "\ue652"
                    }
                ]
            }
        }
    }
    
}

同时在页面中配置onPullDownRefresh生命周期,如下:

代码语言:javascript
复制
// 监听下拉刷新
onPullDownRefresh() {
    this.refresh();
},
methods: {
    refresh() {
        setTimeout(()=>{
            this.list = test_data;
            // 停止下拉刷新
            uni.stopPullDownRefresh();
        }, 2000)
    }
}

显示:

可以看到,已经实现了下拉刷新的效果。

现在再实现没有数据时的默认界面,通过条件渲染实现,如下:

代码语言:javascript
复制
<template>
	<view>
		<template v-if="list.length > 0">
			<!-- 消息列表 -->
			<block v-for="(item, index) in list" :key="index">
				<msg-list :item="item" :index="index"></msg-list>
			</block>
		</template>
		<template v-else>
			<no-thing></no-thing>
		</template>
	</view>
</template>
<script>
	const test_data = [{
			avatar: '/static/img/userpic/5.jpg',
			username: 'Corley',
			update_time: 1612075613,
			data: '我再看看吧,谢谢大佬',
			noread: 5
		},
		{
			avatar: '/static/img/userpic/1.jpg',
			username: 'Brynn',
			update_time: 1612075606,
			data: '大佬,你好,我想请教一个关于uni-app的问题,不知道是否方便?',
			noread: 0
		},
		{
			avatar: '/static/img/userpic/3.jpg',
			username: 'Ellie',
			update_time: 1612075255,
			data: '那我也有点懵了',
			noread: 3
		},
		{
			avatar: '/static/img/userpic/9.jpg',
			username: 'Ainsley',
			update_time: 1612075983,
			data: '真机调试不太方便,我就用浏览器和微信开发者工具调试的。',
			noread: 1
		},
		{
			avatar: '/static/img/userpic/17.jpg',
			username: 'Bella',
			update_time: 1612074571,
			data: 'mysql16进制数据怎么查询才快呢?',
			noread: 2
		}
	];
	import msgList from '@/components/msg/msg-list.vue';
	import noThing from '@/components/common/no-thing.vue';
	export default {
		data() {
			return {
				list: []
			}
		},
		components: {
			msgList,
			noThing
		},
		// 监听下拉刷新
		onPullDownRefresh() {
			this.refresh();
		},
		methods: {
			refresh() {
				setTimeout(()=>{
					this.list = test_data;
					// 停止下拉刷新
					uni.stopPullDownRefresh();
				}, 2000)
			}
		}
	}
</script>

<style>

</style>

显示:

显然,此时在没有数据时会显示给定的默认组件。

4.下拉弹出层组件使用

下拉弹出框需要使用uni-app提供的扩展组件,直接使用hello_uniapp项目下的components/uni-popup/uni-popup.vue组件即可,将uni-popup目录和同级的uni-transition目录拷贝至本项目的components/uni-ui目录下。

再使用下拉弹出层,如下:

代码语言:javascript
复制
<template>
	<view>
		<template v-if="list.length > 0">
			<!-- 消息列表 -->
			<block v-for="(item, index) in list" :key="index">
				<msg-list :item="item" :index="index"></msg-list>
			</block>
		</template>
		<template v-else>
			<no-thing></no-thing>
		</template>
		<!-- 弹出层 -->
		<uni-popup ref="popup" type="top">弹出层</uni-popup>
	</view>
</template>
<script>
	const test_data = [{
			avatar: '/static/img/userpic/5.jpg',
			username: 'Corley',
			update_time: 1612075613,
			data: '我再看看吧,谢谢大佬',
			noread: 5
		},
		{
			avatar: '/static/img/userpic/1.jpg',
			username: 'Brynn',
			update_time: 1612075606,
			data: '大佬,你好,我想请教一个关于uni-app的问题,不知道是否方便?',
			noread: 0
		},
		{
			avatar: '/static/img/userpic/3.jpg',
			username: 'Ellie',
			update_time: 1612075255,
			data: '那我也有点懵了',
			noread: 3
		},
		{
			avatar: '/static/img/userpic/9.jpg',
			username: 'Ainsley',
			update_time: 1612075983,
			data: '真机调试不太方便,我就用浏览器和微信开发者工具调试的。',
			noread: 1
		},
		{
			avatar: '/static/img/userpic/17.jpg',
			username: 'Bella',
			update_time: 1612074571,
			data: 'mysql16进制数据怎么查询才快呢?',
			noread: 2
		}
	];
	import msgList from '@/components/msg/msg-list.vue';
	import noThing from '@/components/common/no-thing.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
		data() {
			return {
				list: []
			}
		},
		components: {
			msgList,
			noThing,
			uniPopup
		},
		// 页面加载
		onLoad() {
			this.list = test_data;
		},
		// 监听下拉刷新
		onPullDownRefresh() {
			this.refresh();
		},
		// 监听原生导航栏按钮事件
		onNavigationBarButtonTap(e) {
			console.log(e);
			switch (e.index){
				case 0: // 左边
					break;
				case 1: // 右边
					this.$refs.popup.open();
					break;
				default:
					break;
			}
		},
		methods: {
			refresh() {
				setTimeout(()=>{
					this.list = test_data;
					// 停止下拉刷新
					uni.stopPullDownRefresh();
				}, 2000)
			}
		}
	}
</script>

<style>

</style>

因为下拉弹出层是通过点击右上角的菜单图标弹出的,因此需要生命周期onNavigationBarButtonTap来实现,其事件参数中有一个值为index,其值表示顶部可点击按钮的下标,从左往右第一个图标为0、第二个图标为1,即点击好友图标时index值为0、点击菜单图标时index值为1。

显示:

可以看到,已经实现了点击弹出下拉弹出框。

现在进一步完善下拉弹出选项,如下:

代码语言:javascript
复制
<template>
	<view>
		<template v-if="list.length > 0">
			<!-- 消息列表 -->
			<block v-for="(item, index) in list" :key="index">
				<msg-list :item="item" :index="index"></msg-list>
			</block>
		</template>
		<template v-else>
			<no-thing></no-thing>
		</template>
		<!-- 弹出层 -->
		<uni-popup ref="popup" type="top">
			<view class="popup-content flex align-center justify-center font-md border-bottom" hover-class="bg-light">
				<text class="iconfont icon-sousuo mr-2"></text> 添加好友
			</view>
			<view class="popup-content flex align-center justify-center font-md" hover-class="bg-light">
				<text class="iconfont icon-shanchu mr-2"></text> 清除列表
			</view>
		</uni-popup>
	</view>
</template>
<script>
	const test_data = [{
			avatar: '/static/img/userpic/5.jpg',
			username: 'Corley',
			update_time: 1612075613,
			data: '我再看看吧,谢谢大佬',
			noread: 5
		},
		{
			avatar: '/static/img/userpic/1.jpg',
			username: 'Brynn',
			update_time: 1612075606,
			data: '大佬,你好,我想请教一个关于uni-app的问题,不知道是否方便?',
			noread: 0
		},
		{
			avatar: '/static/img/userpic/3.jpg',
			username: 'Ellie',
			update_time: 1612075255,
			data: '那我也有点懵了',
			noread: 3
		},
		{
			avatar: '/static/img/userpic/9.jpg',
			username: 'Ainsley',
			update_time: 1612075983,
			data: '真机调试不太方便,我就用浏览器和微信开发者工具调试的。',
			noread: 1
		},
		{
			avatar: '/static/img/userpic/17.jpg',
			username: 'Bella',
			update_time: 1612074571,
			data: 'mysql16进制数据怎么查询才快呢?',
			noread: 2
		}
	];
	import msgList from '@/components/msg/msg-list.vue';
	import noThing from '@/components/common/no-thing.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
		data() {
			return {
				list: []
			}
		},
		components: {
			msgList,
			noThing,
			uniPopup
		},
		// 页面加载
		onLoad() {
			this.list = test_data;
		},
		// 监听下拉刷新
		onPullDownRefresh() {
			this.refresh();
		},
		// 监听原生导航栏按钮事件
		onNavigationBarButtonTap(e) {
			console.log(e);
			switch (e.index){
				case 0: // 左边
					break;
				case 1: // 右边
					this.$refs.popup.open();
					break;
				default:
					break;
			}
		},
		methods: {
			refresh() {
				setTimeout(()=>{
					this.list = test_data;
					// 停止下拉刷新
					uni.stopPullDownRefresh();
				}, 2000)
			}
		}
	}
</script>

<style>

</style>

显示:

现在给下拉弹出层选项添加点击事件,如下:

代码语言:javascript
复制
<template>
	<view>
		<template v-if="list.length > 0">
			<!-- 消息列表 -->
			<block v-for="(item, index) in list" :key="index">
				<msg-list :item="item" :index="index"></msg-list>
			</block>
		</template>
		<template v-else>
			<no-thing></no-thing>
		</template>
		<!-- 弹出层 -->
		<uni-popup ref="popup" type="top">
			<view class="popup-content flex align-center justify-center font-md border-bottom" hover-class="bg-light" @click="popupEvent('friend')">
				<text class="iconfont icon-sousuo mr-2"></text> 添加好友
			</view>
			<view class="popup-content flex align-center justify-center font-md" hover-class="bg-light" @click="popupEvent('clear')">
				<text class="iconfont icon-shanchu mr-2"></text> 清除列表
			</view>
		</uni-popup>
	</view>
</template>
<script>
	const test_data = [{
			avatar: '/static/img/userpic/5.jpg',
			username: 'Corley',
			update_time: 1612075613,
			data: '我再看看吧,谢谢大佬',
			noread: 5
		},
		{
			avatar: '/static/img/userpic/1.jpg',
			username: 'Brynn',
			update_time: 1612075606,
			data: '大佬,你好,我想请教一个关于uni-app的问题,不知道是否方便?',
			noread: 0
		},
		{
			avatar: '/static/img/userpic/3.jpg',
			username: 'Ellie',
			update_time: 1612075255,
			data: '那我也有点懵了',
			noread: 3
		},
		{
			avatar: '/static/img/userpic/9.jpg',
			username: 'Ainsley',
			update_time: 1612075983,
			data: '真机调试不太方便,我就用浏览器和微信开发者工具调试的。',
			noread: 1
		},
		{
			avatar: '/static/img/userpic/17.jpg',
			username: 'Bella',
			update_time: 1612074571,
			data: 'mysql16进制数据怎么查询才快呢?',
			noread: 2
		}
	];
	import msgList from '@/components/msg/msg-list.vue';
	import noThing from '@/components/common/no-thing.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
		data() {
			return {
				list: []
			}
		},
		components: {
			msgList,
			noThing,
			uniPopup
		},
		// 页面加载
		onLoad() {
			this.list = test_data;
		},
		// 监听下拉刷新
		onPullDownRefresh() {
			this.refresh();
		},
		// 监听原生导航栏按钮事件
		onNavigationBarButtonTap(e) {
			console.log(e);
			switch (e.index) {
				case 0: // 左边
					// 关闭弹出层
					this.$refs.popup.close();
					break;
				case 1: // 右边
					this.$refs.popup.open();
					break;
				default:
					break;
			}
		},
		methods: {
			// 下拉刷新
			refresh() {
				setTimeout(() => {
					this.list = test_data;
					// 停止下拉刷新
					uni.stopPullDownRefresh();
				}, 2000)
			},
			// 弹出层选项点击事件
			popupEvent(e) {
				switch (e) {
					case 'friend':
						console.log('Adding friend');
						break;
					case 'clear':
						console.log('Clearing list');
						break;
					default:
						break;
				}
				// 关闭弹出层
				this.$refs.popup.close();
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,正确地触发了点击事件。

二、我的好友列表页开发

1.pages.json配置

我的好友列表页入口在消息页,如下:

代码语言:javascript
复制
// 监听原生导航栏按钮事件
onNavigationBarButtonTap(e) {
    console.log(e);
    switch (e.index) {
        case 0: // 左边
        uni.navigateTo({
            url: '../user-list/user-list',
        });
            // 关闭弹出层
            this.$refs.popup.close();
            break;
        case 1: // 右边
            this.$refs.popup.open();
            break;
        default:
            break;
    }
},

先创建我的好友列表页user-list,配置pages.json如下:

代码语言:javascript
复制
{
    "path" : "pages/user-list/user-list",
    "style" :                                                                                    
    {
        "navigationBarTitleText": "",
        "enablePullDownRefresh": false,
        "app-plus": {
            // 动画效果
            "animationType":"slide-in-left",
            // 导航栏配置
            "titleNView": {
                // 取消返回按钮
                "autoBackButton": false,
                // 搜索框配置
                "searchInput": {
                    "align":"center",
                    "backgroundColor":"#F5F4F2",
                    "borderRadius":"4px",
                    "disabled": true,
                    "placeholder": "搜索用户",
                    "placeholderColor": "#6D6C67"
                },
                // 按钮设置
                "buttons": [
                    {
                        "color":"#333333",
                        "colorPressed":"#FD597C",
                        "float":"right",
                        "fontSize":"15px",
                        "text": "取消"
                    }
                ]
            }
        }
    }
    
}

其中,animationType用于设置窗口显示的动画效果为新窗体从左侧进入; autoBackButton用于取消返回按钮。

同时需要实现点击搜索框时跳转到搜索页、点击取消时返回上一页,user-list.vue如下:

代码语言:javascript
复制
<template>
	<view>
		
	</view>
</template>

<script>
	export default {
		data() {
			return {
				
			}
		},
		// 监听点击输入框事件
		onNavigationBarSearchInputClicked() {
			uni.navigateTo({
				url: '../search/search',
			});
		},
		// 监听点击按钮事件
		onNavigationBarButtonTap() {
			uni.navigateBack({
				delta: 1
			});
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

显示:

显然,实现了页面切换和动画效果显示。

2.导航组件开发

现进一步完善导航栏,包括互关、关注和粉丝3个导航栏选项,与话题详情页类似。

user-list.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 标签栏 -->
		<view class="flex align-center py-2">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{{item.name}} <text v-if="item.num > 0" class="ml-2">{{item.num}}</text></view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				tabIndex: 0,
				tabBars: [{
						name: '互关',
						num: 2
					},
					{
						name: '关注',
						num: 0
					},
					{
						name: '粉丝',
						num: 5
					}
				]
			}
		},
		// 监听点击输入框事件
		onNavigationBarSearchInputClicked() {
			uni.navigateTo({
				url: '../search/search',
			});
		},
		// 监听点击按钮事件
		onNavigationBarButtonTap() {
			uni.navigateBack({
				delta: 1
			});
		},
		methods: {
			changeTab(index) {
				this.tabIndex = index;
			}
		}
	}
</script>

<style>

</style>

其中,好友人数大于0时才显示个数。

显示:

可以看到,实现了顶部导航栏开发。

3.好友列表组件开发

好友列表和首页列表类似,先构建如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 标签栏 -->
		<view class="flex align-center py-2">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{{item.name}} <text v-if="item.num > 0" class="ml-2">{{item.num}}</text></view>
		</view>
		<!-- 好友列表 -->
		<!-- 滑块 -->
		<swiper :duration="150" :current="tabIndex" @change="onChangeTab" :style="'height: '+scrollH+'px;'">
			<swiper-item v-for="(item, index) in newsList" :key="index">
				<scroll-view scroll-y="true" :style="'height: '+scrollH+'px;'" @scrolltolower="loadMore(index)">
					<!-- 有数据 -->
					<template v-if="item.list.length > 0">
						<!-- 列表 -->
						<block v-for="(item2, index2) in item.list" :key="index2">
							<!-- 列表组件 -->
							<view>{{item2}}</view>
							<!-- 全局分割线 -->
							<divider></divider>
						</block>
						<!-- 上拉加载 -->
						<load-more :loadmore="item.loadmore"></load-more>
					</template>
					<!-- 无数据 -->
					<template v-else>
						<no-thing></no-thing>
					</template>
				</scroll-view>
			</swiper-item>
		</swiper>
	</view>
</template>

<script>
	import loadMore from '@/components/common/load-more.vue';
	export default {
		data() {
			return {
				tabIndex: 0,
				tabBars: [{
						name: '互关',
						num: 2
					},
					{
						name: '关注',
						num: 0
					},
					{
						name: '粉丝',
						num: 5
					}
				],
				// 列表高度
				scrollH: 600,
				newsList: []
			}
		},
		// 监听点击输入框事件
		onNavigationBarSearchInputClicked() {
			uni.navigateTo({
				url: '../search/search',
			});
		},
		// 监听点击按钮事件
		onNavigationBarButtonTap() {
			uni.navigateBack({
				delta: 1
			});
		},
		onLoad() {
			uni.getSystemInfo({
				success: function(res) {
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(100);
				}
			});
			// 根据选项生成列表
			this.getData();
		},
		methods: {
			changeTab(index) {
				this.tabIndex = index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 上拉加载更多
			loadMore(index) {
				// 获取当前列表
				let item = this.newsList[index];
				// 判断是否处于可加载状态
				if (item.loadmore !== '上拉加载更多') return;
				// 修改当前列表加载状态
				item.loadmore = '加载中...';
				// 模拟数据请求
				setTimeout(() => {
					// 加载数据
					item.list = [...item.list, ...item.list];
					// 恢复加载状态
					this.newsList[index].loadmore = '上拉加载更多';
				}, 2000)
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多",
						list: []
					}
					for (let j = 0; j < this.tabBars[i].num; j++) {
						obj.list.push('user' + (j + 1));
					}
					arr.push(obj);
				}
				this.newsList = arr;
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,模拟出了好友列表。

进一步构造好友列表项,如下:

代码语言:javascript
复制
<block v-for="(item2, index2) in item.list" :key="index2">
    <!-- 列表组件 -->
    <view class="flex align-center p-2 border-bottom">
        <image src="/static/img/userpic/6.jpg" style="width: 100rpx; height: 100rpx;" class="rounded-circle mr-2"></image>
        <view class="flex flex-column flex-1">
            <text class="font-md text-dark">Corley</text>
            <text>男</text>
        </view>
        <view class="uni-icon uni-icon-checkbox-filled text-light-muted"></view>
    </view>
</block>

显示:

可以看到,已经模拟出了好友列表信息。

4.性别图标显示

使用badge组件实现性别图标,为了可以插入性别图标文本,需要对官方提供的uni-badge组件进行稍微改进,如下:

代码语言:javascript
复制
<template>
	<view v-if="text" :class="inverted ? 'uni-badge--' + type + ' uni-badge--' + size + ' uni-badge--' + type + '-inverted' : 'uni-badge--' + type + ' uni-badge--' + size" :style="badgeStyle" class="uni-badge" @click="onClick()"><slot></slot> {{ text }}</view>
</template>

user-list.vue如下:

代码语言:javascript
复制
<!-- 列表组件 -->
<view class="flex align-center p-2 border-bottom">
    <image src="/static/img/userpic/6.jpg" style="width: 100rpx; height: 100rpx;" class="rounded-circle mr-2"></image>
    <view class="flex flex-column flex-1">
        <text class="font-md text-dark">Corley</text>
        <uni-badge text="18" type="error" size="small">
            <text class="iconfont icon-nanxing text-white font-sm" style="margin-right: 5rpx;"></text>
        </uni-badge>
    </view>
    <view class="uni-icon uni-icon-checkbox-filled text-light-muted"></view>
</view>

components: {
    loadMore,
    uniBadge
},

显示:

可以看到,已经显示出了性别和年龄。

5.封装好友列表组件

封装好友列表组件之前,需要先构建数据,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 标签栏 -->
		<view class="flex align-center py-2">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{{item.name}} <text v-if="item.num > 0" class="ml-2">{{item.num}}</text></view>
		</view>
		<!-- 好友列表 -->
		<!-- 滑块 -->
		<swiper :duration="150" :current="tabIndex" @change="onChangeTab" :style="'height: '+scrollH+'px;'">
			<swiper-item v-for="(item, index) in userList" :key="index">
				<scroll-view scroll-y="true" :style="'height: '+scrollH+'px;'" @scrolltolower="loadMore(index)">
					<!-- 有数据 -->
					<template v-if="item.list.length > 0">
						<!-- 列表 -->
						<block v-for="(item2, index2) in item.list" :key="index2">
							<!-- 列表组件 -->
							<view class="flex align-center p-2 border-bottom" hover-class="bg-light">
								<image :src="item2.avatar" style="width: 100rpx; height: 100rpx;" class="rounded-circle mr-2"></image>
								<view class="flex flex-column flex-1">
									<text class="font-md text-dark">{{item2.username}}</text>
									<uni-badge :text="item2.age" :type="item2.sex === 1 ? 'error' : (item2.sex === 2 ? 'primary' : 'default')" size="small">
										<template v-if="item2.sex > 0">
											<text class="iconfont text-white font-sm" :class="item2.sex === 1 ? 'icon-nvxing' : 'icon-nanxing'" style="margin-right: 5rpx;"></text>
										</template>
									</uni-badge>
								</view>
								<view class="uni-icon uni-icon-checkbox-filled" :class="item2.isFollow ? 'text-light-muted' : 'text-main'"></view>
							</view>
						</block>
						<!-- 上拉加载 -->
						<load-more :loadmore="item.loadmore"></load-more>
					</template>
					<!-- 无数据 -->
					<template v-else>
						<no-thing></no-thing>
					</template>
				</scroll-view>
			</swiper-item>
		</swiper>
	</view>
</template>

<script>
	const test_data1 = {
			avatar: '/static/img/userpic/15.jpg',
			username: 'Corley',
			sex: 1, // 0未知、1女性、2男性
			age: 23,
			isFollow: true
	}
	const test_data2 = {
			avatar: '/static/img/userpic/7.jpg',
			username: 'Casey',
			sex: 0, // 0未知、1女性、2男性
			age: 15,
			isFollow: false
	}
	const test_data3 = {
			avatar: '/static/img/userpic/13.jpg',
			username: 'Henry',
			sex: 2, // 0未知、1女性、2男性
			age: 18,
			isFollow: true
	}
	import loadMore from '@/components/common/load-more.vue';
	import uniBadge from '@/components/uni-ui/uni-badge/uni-badge.vue';
	export default {
		data() {
			return {
				tabIndex: 0,
				tabBars: [{
						name: '互关',
						num: 2
					},
					{
						name: '关注',
						num: 0
					},
					{
						name: '粉丝',
						num: 5
					}
				],
				// 列表高度
				scrollH: 600,
				userList: []
			}
		},
		// 监听点击输入框事件
		onNavigationBarSearchInputClicked() {
			uni.navigateTo({
				url: '../search/search',
			});
		},
		// 监听点击按钮事件
		onNavigationBarButtonTap() {
			uni.navigateBack({
				delta: 1
			});
		},
		onLoad() {
			uni.getSystemInfo({
				success: function(res) {
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(100);
				}
			});
			// 根据选项生成列表
			this.getData();
		},
		components: {
			loadMore,
			uniBadge
		},
		methods: {
			changeTab(index) {
				this.tabIndex = index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 上拉加载更多
			loadMore(index) {
				// 获取当前列表
				let item = this.userList[index];
				// 判断是否处于可加载状态
				if (item.loadmore !== '上拉加载更多') return;
				// 修改当前列表加载状态
				item.loadmore = '加载中...';
				// 模拟数据请求
				setTimeout(() => {
					// 加载数据
					item.list = [...item.list, ...item.list];
					// 恢复加载状态
					this.userList[index].loadmore = '上拉加载更多';
				}, 2000)
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多",
						list: []
					}
					for (let j = 0; j < this.tabBars[i].num; j++) {
						if (j % 3 === 0) {
							obj.list.push(test_data1);
						}
						else if (j % 3 === 1) {
							obj.list.push(test_data2);
						}
						else {
							obj.list.push(test_data3);
						}
					}
					arr.push(obj);
				}
				this.userList = arr;
				console.log(this.userList);
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,头像、昵称、性别、是否关注、点击效果等均正常显示。

再将其封装为组件,components下新建user-list组件(包含同名目录),如下:

代码语言:javascript
复制
<template>
	<view class="flex align-center p-2 border-bottom" hover-class="bg-light">
		<image :src="item.avatar" style="width: 100rpx; height: 100rpx;" class="rounded-circle mr-2"></image>
		<view class="flex flex-column flex-1">
			<text class="font-md text-dark">{{item.username}}</text>
			<uni-badge :text="item.age" :type="item.sex === 1 ? 'error' : (item.sex === 2 ? 'primary' : 'default')" size="small">
				<template v-if="item.sex > 0">
					<text class="iconfont text-white font-sm" :class="item.sex === 1 ? 'icon-nvxing' : 'icon-nanxing'" style="margin-right: 5rpx;"></text>
				</template>
			</uni-badge>
		</view>
		<view class="uni-icon uni-icon-checkbox-filled" :class="item.isFollow ? 'text-light-muted' : 'text-main'"></view>
	</view>
</template>

<script>
	import uniBadge from '@/components/uni-ui/uni-badge/uni-badge.vue';
	export default {
		props: {
			item: Object,
			index: Number
		},
		components: {
			uniBadge
		}
	}
</script>

<style>
</style>

pages/user-list/user-list.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 标签栏 -->
		<view class="flex align-center py-2">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{{item.name}} <text v-if="item.num > 0" class="ml-2">{{item.num}}</text></view>
		</view>
		<!-- 好友列表 -->
		<!-- 滑块 -->
		<swiper :duration="150" :current="tabIndex" @change="onChangeTab" :style="'height: '+scrollH+'px;'">
			<swiper-item v-for="(item, index) in userList" :key="index">
				<scroll-view scroll-y="true" :style="'height: '+scrollH+'px;'" @scrolltolower="loadMore(index)">
					<!-- 有数据 -->
					<template v-if="item.list.length > 0">
						<!-- 列表 -->
						<block v-for="(item2, index2) in item.list" :key="index2">
							<!-- 列表组件 -->
							<user-list :item="item2" :index="index2"></user-list>
						</block>
						<!-- 上拉加载 -->
						<load-more :loadmore="item.loadmore"></load-more>
					</template>
					<!-- 无数据 -->
					<template v-else>
						<no-thing></no-thing>
					</template>
				</scroll-view>
			</swiper-item>
		</swiper>
	</view>
</template>

<script>
	const test_data1 = {
			avatar: '/static/img/userpic/15.jpg',
			username: 'Corley',
			sex: 1, // 0未知、1女性、2男性
			age: 23,
			isFollow: true
	}
	const test_data2 = {
			avatar: '/static/img/userpic/7.jpg',
			username: 'Casey',
			sex: 0, // 0未知、1女性、2男性
			age: 15,
			isFollow: false
	}
	const test_data3 = {
			avatar: '/static/img/userpic/13.jpg',
			username: 'Henry',
			sex: 2, // 0未知、1女性、2男性
			age: 18,
			isFollow: true
	}
	import loadMore from '@/components/common/load-more.vue';
	import userList from '@/components/user-list/user-list.vue';
	export default {
		data() {
			return {
				tabIndex: 0,
				tabBars: [{
						name: '互关',
						num: 2
					},
					{
						name: '关注',
						num: 0
					},
					{
						name: '粉丝',
						num: 5
					}
				],
				// 列表高度
				scrollH: 600,
				userList: []
			}
		},
		// 监听点击输入框事件
		onNavigationBarSearchInputClicked() {
			uni.navigateTo({
				url: '../search/search',
			});
		},
		// 监听点击按钮事件
		onNavigationBarButtonTap() {
			uni.navigateBack({
				delta: 1
			});
		},
		onLoad() {
			uni.getSystemInfo({
				success: function(res) {
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(100);
				}
			});
			// 根据选项生成列表
			this.getData();
		},
		components: {
			loadMore,
			userList
		},
		methods: {
			changeTab(index) {
				this.tabIndex = index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 上拉加载更多
			loadMore(index) {
				// 获取当前列表
				let item = this.userList[index];
				// 判断是否处于可加载状态
				if (item.loadmore !== '上拉加载更多') return;
				// 修改当前列表加载状态
				item.loadmore = '加载中...';
				// 模拟数据请求
				setTimeout(() => {
					// 加载数据
					item.list = [...item.list, ...item.list];
					// 恢复加载状态
					this.userList[index].loadmore = '上拉加载更多';
				}, 2000)
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多",
						list: []
					}
					for (let j = 0; j < this.tabBars[i].num; j++) {
						if (j % 3 === 0) {
							obj.list.push(test_data1);
						}
						else if (j % 3 === 1) {
							obj.list.push(test_data2);
						}
						else {
							obj.list.push(test_data3);
						}
					}
					arr.push(obj);
				}
				this.userList = arr;
				console.log(this.userList);
			}
		}
	}
</script>

<style>

</style>

效果与之前相同。

现对用户列表页进行进一步优化,页面上下滑动时会出现闪动的情况,这是页面高度设置的问题,指定标签栏样式为style="height: 100rpx;"即可; 同时用户列表的数据较少时,上拉加载不会触发触底事件,此时可以隐藏上拉加载组件。

如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 标签栏 -->
		<view class="flex align-center" style="height: 100rpx;">
			<view class="flex-1 flex align-center justify-center" v-for="(item ,index) in tabBars" :key="'tab'+index" :class="index === tabIndex ? 'font-lg font-weight-bold text-main' : 'font-md'"
			 @click="changeTab(index)">{{item.name}} <text v-if="item.num > 0" class="ml-2">{{item.num}}</text></view>
		</view>
		<!-- 好友列表 -->
		<!-- 滑块 -->
		<swiper :duration="150" :current="tabIndex" @change="onChangeTab" :style="'height: '+scrollH+'px;'">
			<swiper-item v-for="(item, index) in userList" :key="index">
				<scroll-view scroll-y="true" :style="'height: '+scrollH+'px;'" @scrolltolower="loadMore(index)">
					<!-- 有数据 -->
					<template v-if="item.list.length > 0">
						<!-- 列表 -->
						<block v-for="(item2, index2) in item.list" :key="index2">
							<!-- 列表组件 -->
							<user-list :item="item2" :index="index2"></user-list>
						</block>
						<!-- 上拉加载 -->
						<template v-if="item.list.length > 10">
							<load-more :loadmore="item.loadmore"></load-more>
						</template>
					</template>
					<!-- 无数据 -->
					<template v-else>
						<no-thing></no-thing>
					</template>
				</scroll-view>
			</swiper-item>
		</swiper>
	</view>
</template>

<script>
	const test_data1 = {
			avatar: '/static/img/userpic/15.jpg',
			username: 'Corley',
			sex: 1, // 0未知、1女性、2男性
			age: 23,
			isFollow: true
	}
	const test_data2 = {
			avatar: '/static/img/userpic/7.jpg',
			username: 'Casey',
			sex: 0, // 0未知、1女性、2男性
			age: 15,
			isFollow: false
	}
	const test_data3 = {
			avatar: '/static/img/userpic/13.jpg',
			username: 'Henry',
			sex: 2, // 0未知、1女性、2男性
			age: 18,
			isFollow: true
	}
	import loadMore from '@/components/common/load-more.vue';
	import userList from '@/components/user-list/user-list.vue';
	export default {
		data() {
			return {
				tabIndex: 0,
				tabBars: [{
						name: '互关',
						num: 5
					},
					{
						name: '关注',
						num: 3
					},
					{
						name: '粉丝',
						num: 12
					}
				],
				// 列表高度
				scrollH: 600,
				userList: []
			}
		},
		// 监听点击输入框事件
		onNavigationBarSearchInputClicked() {
			uni.navigateTo({
				url: '../search/search',
			});
		},
		// 监听点击按钮事件
		onNavigationBarButtonTap() {
			uni.navigateBack({
				delta: 1
			});
		},
		onLoad() {
			uni.getSystemInfo({
				success: function(res) {
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(100);
				}
			});
			// 根据选项生成列表
			this.getData();
		},
		components: {
			loadMore,
			userList
		},
		methods: {
			changeTab(index) {
				this.tabIndex = index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 上拉加载更多
			loadMore(index) {
				// 获取当前列表
				let item = this.userList[index];
				// 判断是否处于可加载状态
				if (item.loadmore !== '上拉加载更多') return;
				// 修改当前列表加载状态
				item.loadmore = '加载中...';
				// 模拟数据请求
				setTimeout(() => {
					// 加载数据
					item.list = [...item.list, ...item.list];
					// 恢复加载状态
					this.userList[index].loadmore = '上拉加载更多';
				}, 2000)
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多",
						list: []
					}
					for (let j = 0; j < this.tabBars[i].num; j++) {
						if (j % 3 === 0) {
							obj.list.push(test_data1);
						}
						else if (j % 3 === 1) {
							obj.list.push(test_data2);
						}
						else {
							obj.list.push(test_data3);
						}
					}
					arr.push(obj);
				}
				this.userList = arr;
				console.log(this.userList);
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,有一定的优化效果。

三、聊天页面开发

1.pages.json配置

聊天界面主要实现文字聊天,先创建页面user-chat,其入口为消息列表msg-list,如下:

代码语言:javascript
复制
<template>
	<view class="flex align-center p-2 border-bottom border-" hover-class="bg-light" @click="open()">
		<image :src="item.avatar" style="width: 80rpx; height: 80rpx;" class="rounded-circle mr-2"></image>
		<view class="flex flex-column flex-1">
			<view class="flex align-center justify-between">
				<text class="font-md">{{item.username}}</text>
				<text class="font-sm text-secondry">{{item.update_time | formatTime}}</text>
			</view>
			<view class="flex align-center justify-between">
				<text class="text-secondry text-ellipsis" style="max-width: 500rpx;">{{item.data}}</text>
				<uni-badge :text="item.noread" type="error"></uni-badge>
			</view>
		</view>
	</view>
</template>

<script>
	import uniBadge from '@/components/uni-ui/uni-badge/uni-badge.vue';
	import $T from '@/common/time.js';
	export default {
		props: {
			item: Object,
			index: Number
		},
		// 过滤器
		filters: {
			formatTime(value) {
				return $T.gettime(value);
			}
		},
		components: {
			uniBadge
		},
		methods: {
			// 打开聊天页
			open() {
				uni.navigateTo({
					url: '../../pages/user-chat/user-chat'
				});
			}
		}
	}
</script>

<style>
</style>

同时配置pages.json如下:

代码语言:javascript
复制
{
    "path" : "pages/user-chat/user-chat",
    "style" :                                                                                    
    {
        "navigationBarTitleText": "",
        "enablePullDownRefresh": false,
        "app-plus": {
            "bounce":"none",
            "titleNView": {
                "buttons": [
                    {
                        "color":"#333333",
                        "colorPressed":"#FD597C",
                        "float":"right",
                        "fontSize":"20px",
                        "fontSrc":"/static/iconfont.ttf",
                        "text": "\ue60b"
                    }
                ]
            }
        }
    }
    
}

显示:

可以看到,实现了页面配置。

2.聊天输入框组件开发

现进一步实现底部聊天操作条,user-chat.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
			<input type="text" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" />
			<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;"></view>
		</view>
	</view>
</template>

显示:

可以看到,已经显示出底部消息发送框,并且有动画效果。

3.聊天列表组件开发

聊天消息列表通过scroll-view组件实现,先实现如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 聊天消息列表 -->
		<scroll-view scroll-y="true" :style="'height:'+scrollH+'px;'">
			<view v-for="i in 100" :key="i">message{{i}}</view>
		</scroll-view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
			<input type="text" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" />
			<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;"></view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				scrollH: 500
			}
		},
		onLoad() {
			uni.getSystemInfo({
				success(res) {
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(101);
				}
			})
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

显示:

再实现消息,消息包括头像和消息内容,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 聊天消息列表 -->
		<scroll-view scroll-y="true" :style="'height:'+scrollH+'px;'">
			<!-- 左边 -->
			<view class="flex align-start px-2 my-2">
				<image src="/static/img/userpic/14.jpg" class="rounded-circle" style="width: 100rpx; height: 100rpx;"></image>
				<view class="bg-light p-2 rounded mx-2" style="min-width: 100rpx; max-width: 400rpx;">
					大佬,你好
				</view>
			</view>
			<!-- 右边 -->
			<view class="flex align-start px-2" style="flex-direction: row-reverse;">
				<image src="/static/img/userpic/11.jpg" class="rounded-circle" style="width: 100rpx; height: 100rpx;"></image>
				<view class="bg-light p-2 rounded mx-2" style="min-width: 100rpx; max-width: 400rpx;">
					你好啊,大佬不敢当?
				</view>
			</view>
		</scroll-view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
			<input type="text" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" />
			<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;"></view>
		</view>
	</view>
</template>

右侧的消息是通过给左侧的消息添加样式flex-direction: row-reverse;实现的。

显示:

可以看到,实现了发送消息的列表效果。

4.封装聊天列表组件

封装组件之前需要先构建数据,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 聊天消息列表 -->
		<scroll-view scroll-y="true" :style="'height:'+scrollH+'px;'">
			<block v-for="(item, index) in list">
				<view class="flex align-start px-2 my-2" :style="item.user_id === uid ? 'flex-direction: row-reverse;' : ''">
					<image :src="item.avatar" class="rounded-circle" style="width: 100rpx; height: 100rpx;"></image>
					<view class="bg-light p-2 rounded mx-2" style="min-width: 100rpx; max-width: 400rpx;">
						{{item.data}}
					</view>
				</view>
			</block>
		</scroll-view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
			<input type="text" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" />
			<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;"></view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				scrollH: 500,
				list: [
					{
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '大佬,你好',
						type: 'text', // text、image、video、audio、link
						create_time: 1612156732
					},
					{
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '你好啊,大佬不敢当?',
						type: 'text', // text、image、video、audio、link
						create_time: 1612156872
					}
				],
				// 模拟当前登录用户userid
				uid: 1
			}
		},
		computed: {
			isSelf() {
				return this.data 
			}
		},
		onLoad() {
			uni.getSystemInfo({
				success(res) {
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(101);
				}
			})
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

可以达到与之前相同的效果。

再实现封装组件,在components目录下创建user-chat目录,下新建user-chat-list组件,如下:

代码语言:javascript
复制
<template>
	<view class="flex align-start px-2 my-2" :style="isSelf ? 'flex-direction: row-reverse;' : ''">
		<image :src="item.avatar" class="rounded-circle" style="width: 100rpx; height: 100rpx;"></image>
		<view class="bg-light p-2 rounded mx-2" style="min-width: 100rpx; max-width: 400rpx;">
			{{item.data}}
		</view>
	</view>
</template>

<script>
	// 模拟当前登录用户userid
	const uid = 1;
	export default {
		props: {
			item: Object,
			index: Number,
		},
		computed: {
			// 是否是登录用户本人
			isSelf() {
				return uid === this.item.user_id;
			}
		},
	}
</script>

<style>
</style>

user-chat.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 聊天消息列表 -->
		<scroll-view scroll-y="true" :style="'height:'+scrollH+'px;'">
			<block v-for="(item, index) in list">
				<user-chat-list :item="item" :index="index"></user-chat-list>
			</block>
		</scroll-view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
			<input type="text" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" />
			<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;"></view>
		</view>
	</view>
</template>

<script>
	import userChatList from '@/components/user-chat/user-chat-list.vue';
	export default {
		data() {
			return {
				scrollH: 500,
				list: [
					{
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '大佬,你好',
						type: 'text', // text、image、video、audio、link
						create_time: 1612156732
					},
					{
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '你好啊,大佬不敢当?',
						type: 'text', // text、image、video、audio、link
						create_time: 1612156872
					}
				]
			}
		},
		components: {
			userChatList
		},
		onLoad() {
			uni.getSystemInfo({
				success(res) {
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(101);
				}
			})
		},
		methods: {
			
		}
	}
</script>

<style>

</style>

效果与之前相同。

再完善消息时间显示,根据相邻消息对应的时间戳的差来判断是否显示,直接使用time.js中提供的getChatTime(v1, v2)函数来实现即可,默认时间差为5分钟,user-chat-list.vue如下:

代码语言:javascript
复制
<template>
	<view class="">
		<!-- 时间 -->
		<template v-if="shortTime">
			<view class="flex align-center justify-center py-2 font-sm text-light-muted">
				{{shortTime}}
			</view>
		</template>
		
		<!-- 消息气泡 -->
		<view class="flex align-start px-2 my-2" :style="isSelf ? 'flex-direction: row-reverse;' : ''">
			<image :src="item.avatar" class="rounded-circle" style="width: 100rpx; height: 100rpx;"></image>
			<view class="bg-light p-2 rounded mx-2" style="min-width: 100rpx; max-width: 400rpx;">
				{{item.data}}
			</view>
		</view>
	</view>
</template>

<script>
	// 模拟当前登录用户userid
	const uid = 1;
	import $T from '@/common/time.js';
	export default {
		props: {
			item: Object,
			index: Number,
			preTime: [Number, String]
		},
		computed: {
			// 是否是登录用户本人
			isSelf() {
				return uid === this.item.user_id;
			},
			// 转化时间
			shortTime() {
				return $T.getChatTime(this.item.create_time, this.preTime);
			}
		},
	}
</script>

<style>
</style>

需要父组件向该组件传递上一条消息的时间。

父组件user-chat.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 聊天消息列表 -->
		<scroll-view scroll-y="true" :style="'height:'+scrollH+'px;'">
			<block v-for="(item, index) in list">
				<user-chat-list :item="item" :index="index" :preTime="index > 0 ? list[index-1].create_time : 0"></user-chat-list>
			</block>
		</scroll-view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
			<input type="text" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" />
			<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;"></view>
		</view>
	</view>
</template>

显示:

可以看到,已经实现了显示消息时间,并且是人性化的时间显示。

5.聊天页功能完善

首先绑定消息输入框的输入内容; 同时绑定发送按钮的点击事件,实现发送消息; 并且实现在输入消息并发送之后,需要清空输入框; 并绑定@confirm事件,可以在点击完成或前往按钮时触发发送事件; 输入框会部分隐藏消息列表,此时需要修改组件的高度; 在进入聊天页时,需要默认滚到页面最底部,通过scroll-into-view属性来实现。

如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 聊天消息列表 -->
		<scroll-view scroll-y="true" style="position: absolute; left: 0; top: 0; right: 0; bottom: 100rpx;" :scroll-into-view="scrollInto" scroll-with-animation>
			<block v-for="(item, index) in list">
				<view :id="'chat'+index">
					<user-chat-list :item="item" :index="index" :preTime="index > 0 ? list[index-1].create_time : 0"></user-chat-list>
				</view>
			</block>
		</scroll-view>
		<!-- 底部操作条 -->
		<view style="height: 100rpx;" class="fixed-bottom flex align-center border-top bg-white">
			<input type="text" v-model="content" value="" class="flex-1 rounded bg-light ml-2" style="padding: 5rpx 0;" placeholder="文明发言" @confirm="submit()" />
			<view class="iconfont icon-fasong flex align-center justify-center font-lg animate__animated" hover-class="animate__jello text-main" style="width: 100rpx;" @click="submit()"></view>
		</view>
	</view>
</template>

<script>
	import userChatList from '@/components/user-chat/user-chat-list.vue';
	export default {
		data() {
			return {
				list: [
					{
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '大佬,你好',
						type: 'text', // text、image、video、audio、link
						create_time: 1612156712
					},
					{
						user_id: 2,
						username: 'Corley',
						avatar: '/static/img/userpic/14.jpg',
						data: '我想请教一个关于uni-app的问题,不知道是否方便?',
						type: 'text',
						create_time: 1612156872
					},
					{
						user_id: 1,
						username: 'Natalia',
						avatar: '/static/img/userpic/11.jpg',
						data: '你好啊,大佬不敢当?',
						type: 'text',
						create_time: 1612156905
					},
					{
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '有什么你就说吧',
						type: 'text',
						create_time: 1612157023
					},
					{
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '只要我会的都会解答',
						type: 'text',
						create_time: 1612157029
					},
					{
						user_id: 2,
						username: 'Corley',
						avatar: '/static/img/userpic/14.jpg',
						data: '有几个问题',
						type: 'text',
						create_time: 1612157411
					},
					{
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '1.在导航栏上单击搜索输入监听搜索框的事件该写在什么位置啊,为什么我写的触发不了?',
						type: 'text',
						create_time: 1612157439
					},
					{
						user_id: 2,
						username: 'Corley',
						avatar: '/static/img/userpic/14.jpg',
						data: '2.关注顶踩的动画css怎么获取到的啊?',
						type: 'text',
						create_time: 1612157455
					},
					{
						user_id: 2,
						username: 'Natalia',
						avatar: '/static/img/userpic/14.jpg',
						data: '3.首页开发最后代码写完,再点击关注和点赞,踩,就会报错。辛苦看一看啊',
						type: 'text',
						create_time: 1612157503
					},
					{
						user_id: 1,
						username: 'Corley',
						avatar: '/static/img/userpic/11.jpg',
						data: '好的,我马上看',
						type: 'text',
						create_time: 1612157821
					}
				],
				content: '',
				scrollInto: ''
			}
		},
		components: {
			userChatList
		},
		// 页面加载完成
		onReady() {
			this.pageToBottom();
		},
		methods: {
			// 发送消息
			submit() {
				if (this.content === ''){
					return uni.showToast({
						title: '消息不能为空',
						icon: 'none'
					});
				}
				let obj = {
					user_id: 1,
					username: 'Corley',
					avatar: '/static/img/userpic/11.jpg',
					data: this.content,
					type: 'text',
					create_time: (new Date()).getTime()
				}
				this.list.push(obj);
				// 清空输入框
				this.content = '';
				// 滚动到底部
				this.pageToBottom();
			},
			// 滚动到底部
			pageToBottom() {
				let lastIndex = this.list.length - 1;
				if (lastIndex < 0) return;
				this.scrollInto = 'chat' + lastIndex;
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,此时表现更好。

现在还需要完善聊天页导航栏标题,应该是当前聊天的用户名,使用页面消息传递的方式实现,msg-list.vue如下:

代码语言:javascript
复制
open() {
    uni.navigateTo({
        url: '../../pages/user-chat/user-chat?username=' + this.item.username
    });
}

user-chat.vue如下:

代码语言:javascript
复制
onLoad(e) {
    console.log(e);
    this.username = e.username;
},
// 页面加载完成
onReady() {
    this.pageToBottom();
    uni.setNavigationBarTitle({
        title: this.username
    });
},

显示:

此时,顶部导航栏标题是用户名。

总结

使用已经实现好的库和组件可以加速开发,文中使用到了uni-app官方提供的扩展组件uni-badge用于显示消息数、uni-popup用于实现下拉弹出框,同时使用专门的JS库来进行时间处理,不需要自己再造轮子,大大加快了开发。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、消息列表页面开发
    • 1.pages.json配置
      • 2.消息列表组件开发和封装
        • 3.下拉刷新功能实现
          • 4.下拉弹出层组件使用
          • 二、我的好友列表页开发
            • 1.pages.json配置
              • 2.导航组件开发
                • 3.好友列表组件开发
                  • 4.性别图标显示
                    • 5.封装好友列表组件
                    • 三、聊天页面开发
                      • 1.pages.json配置
                        • 2.聊天输入框组件开发
                          • 3.聊天列表组件开发
                            • 4.封装聊天列表组件
                              • 5.聊天页功能完善
                              • 总结
                              相关产品与服务
                              云开发 CLI 工具
                              云开发 CLI 工具(Cloudbase CLI Devtools,CCLID)是云开发官方指定的 CLI 工具,可以帮助开发者快速构建 Serverless 应用。CLI 工具提供能力包括文件储存的管理、云函数的部署、模板项目的创建、HTTP Service、静态网站托管等,您可以专注于编码,无需在平台中切换各类配置。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档