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

uni-app实战之社区交友APP(4)首页开发

作者头像
cutercorley
发布2021-01-29 09:55:08
2.7K0
发布2021-01-29 09:55:08
举报
文章被收录于专栏:Corley的开发笔记

文章目录

前言

本文主要介绍了首页图文列表和滚动选项卡的开发: 图文列表的开发,包括顶部导航栏配置,图文列表项(头像、昵称、关注按钮、标题、标题封面图、点赞、踩、评论和分享)等的开发; 列表组件优化,包括分割线的开发和封装,动画特效实现,关注、顶踩功能的完善; 滚动选项卡开发,包括顶部选项卡开发、列表的同步显示和滑动,上拉加载的开发和封装,无数据组件开发等。

一、图文列表样式开发

1.pages.json配置

删除之前创建的demo页面及其目录,之前在配置pages.json时,"pages"中并未设置style,此时需要设置"app-plus",即配置编译到 App 平台时的特定样式。

先下载发帖的图标,演示如下:

解压下载后的压缩包并将其中的iconfont.ttf复制到项目目录下的static目录下,并作为首页配置的app-plus>titleNView>buttons>fontSrc; 同时在我的项目页面选择Unicode,并复制发帖icon的代码,例如,修改为uni-app支持的格式\ue668,作为app-plus>titleNView>buttons>text。 关于app-plus的说明可参考文档https://uniapp.dcloud.io/collocation/pages?id=app-plus

pages.json如下:

代码语言:javascript
复制
{
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/index/index",
			"style": {
				"app-plus": {
					// 导航栏配置
					"titleNView": {
						// 搜索框配置
						"searchInput": {
							"align":"center",
							"backgroundColor":"#F5F4F2",
							"borderRadius":"4px",
							"disabled": true,
							"placeholder": "搜索帖子",
							"placeholderColor": "#6D6C67"
						},
						// 按钮设置
						"buttons": [
							{
								"color":"#333333",
								"colorPressed":"#FD597C",
								"float":"right",
								"fontSize":"20px",
								"fontSrc":"/static/iconfont.ttf",
								"text": "\ue668"
							}
						]
					}
				}
			}
		}
	    ,{
            "path" : "pages/news/news",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "",
                "enablePullDownRefresh": false
            }
            
        }
        ,{
            "path" : "pages/msg/msg",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "",
                "enablePullDownRefresh": false
            }
            
        }
        ,{
            "path" : "pages/my/my",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "",
                "enablePullDownRefresh": false
            }
            
        }
        
    ],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "Community Dating",
		"navigationBarBackgroundColor": "#FFFFFF",
		"backgroundColor": "#FFFFFF"
	},
	"tabBar": {
		"color":"#323232",
		"selectedColor":"#ED6384",
		"backgroundColor":"#FFFFFF",
		"borderStyle": "black",
		"list": [
			{
				"pagePath": "pages/index/index",
				"text": "首页",
				"iconPath": "static/tabbar/index.png",
				"selectedIconPath": "static/tabbar/indexed.png"
			},
			{
				"pagePath": "pages/news/news",
				"text": "动态",
				"iconPath": "static/tabbar/news.png",
				"selectedIconPath": "static/tabbar/newsed.png"
			},
			{
				"pagePath": "pages/msg/msg",
				"text": "消息",
				"iconPath": "static/tabbar/paper.png",
				"selectedIconPath": "static/tabbar/papered.png"
			},
			{
				"pagePath": "pages/my/my",
				"text": "我的",
				"iconPath": "static/tabbar/home.png",
				"selectedIconPath": "static/tabbar/homed.png"
			}
		]
	}
}

显示:

可以看到,定义出了搜索框和发帖按钮,点击按钮会改变颜色。

2.头像、昵称和关注按钮

uni-app 支持的通用 css 单位包括 px、rpx:

  • px 即屏幕像素。
  • rpx 即响应式px,一种根据屏幕宽度自适应的动态单位。以750宽的屏幕为基准,750rpx恰好为屏幕宽度。

开发者可以通过设计稿基准宽度计算页面元素 rpx 值,设计稿 1px 与框架样式 1rpx 转换公式为设计稿 1px / 设计稿基准宽度 = 框架样式 1rpx / 750rpx,所以页面元素宽度在 uni-app 中的宽度计算公式为750 * 元素在设计稿中的宽度 / 设计稿基准宽度。 更多可参考https://uniapp.dcloud.io/frame?id=尺寸单位

index.vue页面设置头像显示如下:

代码语言:javascript
复制
<template>
	<view>
		<image src="/static/img/userpic/12.jpg" style="width: 65rpx; height: 65rpx;"></image>
	</view>
</template>

<script>
	export default {
		data() {
			return {

			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

显示:

在不同尺寸的设备上,其大小会自动缩放。

如需直接使用样式和素材等文件,可以直接点击加QQ群

963624318 ,在群文件夹uni-app实战之社区交友APP中下载即可。

先实现图文列表的第一部分,即头像、昵称和关注按钮,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 列表样式 -->
		<view style="padding: 20rpx;">
			<!-- 头像、昵称和关注按钮 -->
			<view style="display: flex; align-items: center; justify-content: space-between;">
				<view style="display: flex; align-items: center;">
					<!-- 头像 -->
					<image src="/static/img/userpic/12.jpg" mode="" style="width: 65rpx; height: 65rpx; border-radius: 100%; margin-right: 20rpx;" lazy-load></image>
					<!-- 昵称和发布时间 -->
					<view>
						<view style="font-size: 30rpx; line-height: 1.5;">Corley</view>
						<text style="color: #9D9589; font-size: 25rpx; line-height: 1.5;">2021-01-24 上午11:20</text>
					</view>
				</view>
				<!-- 按钮 -->
				<view style="width: 90rpx; height: 50rpx; display: flex; align-items: center; justify-content: center; border-radius: 5rpx; background-color: #FF4A6A; color: #FFFFFF;">
					关注
				</view>
			</view>
			<!-- 标题 -->
			<view></view>
			<!-- 图片 -->
			<view></view>
			<!-- 图标按钮 -->
			<view></view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {

			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

显示:

显然,实现了头像、昵称、发布日期和关注按钮的展示。

3.标题和互动按钮

现进一步实现列表项的第二部分,即发帖标题、点赞、评论和分享等。

先按照之前的方法在https://www.iconfont.cn/中添加点赞、踩、评论和分享的图标,再将iconfont.css更新至common/icon.css中。

index.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 列表样式 -->
		<view style="padding: 20rpx;">
			<!-- 头像、昵称和关注按钮 -->
			<view style="display: flex; align-items: center; justify-content: space-between;">
				<view style="display: flex; align-items: center;">
					<!-- 头像 -->
					<image src="/static/img/userpic/12.jpg" mode="" style="width: 65rpx; height: 65rpx; border-radius: 100%; margin-right: 20rpx;"
					 lazy-load></image>
					<!-- 昵称和发布时间 -->
					<view>
						<view style="font-size: 30rpx; line-height: 1.5;">Corley</view>
						<text style="color: #9D9589; font-size: 25rpx; line-height: 1.5;">2021-01-24 上午11:20</text>
					</view>
				</view>
				<!-- 按钮 -->
				<view style="width: 90rpx; height: 50rpx; display: flex; align-items: center; justify-content: center; border-radius: 5rpx; background-color: #FF4A6A; color: #FFFFFF;">
					关注
				</view>
			</view>
			<!-- 标题 -->
			<view style="font-size: 30rpx; margin: 10rpx 0;">
				uni-app入门教程
			</view>
			<!-- 图片 -->
			<image src="/static/img/datapic/42.jpg" style="height: 350rpx; width: 100%; border-radius: 5rpx;"></image>
			<!-- 图标按钮 -->
			<view style="display: flex; align-items: center;">
				<view style="flex: 1; display: flex; align-items: center; justify-content: center;">
					<text class="iconfont icon-dianzan" style="margin-right: 20rpx;"></text>
					<text>1</text>
				</view>
				<view style="flex: 1; display: flex; align-items: center; justify-content: center;">
					<text class="iconfont icon-cai" style="margin-right: 20rpx;"></text>
					<text>1</text>
				</view>
				<view style="flex: 1; display: flex; align-items: center; justify-content: center;">
					<text class="iconfont icon-pinglun" style="margin-right: 20rpx;"></text>
					<text>1</text>
				</view>
				<view style="flex: 1; display: flex; align-items: center; justify-content: center;">
					<text class="iconfont icon-fenxiang" style="margin-right: 20rpx;"></text>
					<text>1</text>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {

			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

显示:

可以看到,已经实现了一个列表项的基本内容。

4.封装样式组件

虽然已经实现了列表,但是可以看到代码很冗余、有大量CSS重复代码,同时列表可以复用,也可以封装成组件,来提高代码的复用率。 先优化项目代码,将CSS样式提取到公共的CSS文件中。 common下新建文件base.css,保存公共样式如下:

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

/* flex布局 */
.flex {
	display: flex;
}
.align-center {
	align-items: center;
}
.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;
}
.my-1 {
	margin-top: 10rpx;
	margin-bottom: 0rpx;
}

/* 字体 */
.font-md {
	font-size: 35rpx;
}
.font {
	font-size: 30rpx;
}
.font-sm {
	font-size: 25rpx;
}

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

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

base.css文件不仅可以应用于该项目,也可以应用于其他uni-app项目。

common目录下新建common.css保存本项目全局样式,如下:

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

App.vue中导入CSS文件,如下:

代码语言:javascript
复制
<script>
	export default {
		onLaunch: function() {
			console.log('App Launch')
		},
		onShow: function() {
			console.log('App Show')
		},
		onHide: function() {
			console.log('App Hide')
		}
	}
</script>

<style>
	/*每个页面公共css */
	/* 官方CSS库 */
	@import url("./common/uni.css");
	/* 自定义图标库 */
	@import url("./common/icon.css");
	/* 动画库 */
	@import url("./common/animate.css");
	/* 自定义样式库 */
	@import url("./common/base.css");
	/* 全局样式 */
	@import url("./common/common.css");
</style>

index.vue简化如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 列表样式 -->
		<view class="p-2">
			<!-- 头像、昵称和关注按钮 -->
			<view class="flex align-center justify-between">
				<view class="flex align-center">
					<!-- 头像 -->
					<image class="rounded-circle mr-2" src="/static/img/userpic/12.jpg" mode="" style="width: 65rpx; height: 65rpx;"
					 lazy-load></image>
					<!-- 昵称和发布时间 -->
					<view>
						<view class="font" style="line-height: 1.5;">Corley</view>
						<text class="font-sm text-light-muted" style="line-height: 1.5;">2021-01-24 上午11:20</text>
					</view>
				</view>
				<!-- 按钮 -->
				<view class="flex align-center justify-center rounded bg-main text-white" style="width: 90rpx; height: 50rpx;">
					关注
				</view>
			</view>
			<!-- 标题 -->
			<view class="font my-1">
				uni-app入门教程
			</view>
			<!-- 图片 -->
			<image class="rounded w-100" src="/static/img/datapic/42.jpg" style="height: 350rpx;"></image>
			<!-- 图标按钮 -->
			<view class="flex align-center">
				<view class="flex align-center justify-center flex-1">
					<text class="iconfont icon-dianzan mr-2"></text>
					<text>1</text>
				</view>
				<view class="flex align-center justify-center flex-1">
					<text class="iconfont icon-cai mr-2"></text>
					<text>1</text>
				</view>
				<view class="flex align-center justify-center flex-1">
					<text class="iconfont icon-pinglun mr-2"></text>
					<text>1</text>
				</view>
				<view class="flex align-center justify-center flex-1">
					<text class="iconfont icon-fenxiang mr-2"></text>
					<text>1</text>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {

			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

显然,此时代码更加简洁美观,并且可以达到与之前同样的效果。

现进一步实现将列表项封装为组件。 首先替换数据、实现列表渲染,如下:

代码语言:javascript
复制
<template>
	<view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表样式 -->
			<view class="p-2">
				<!-- 头像、昵称和关注按钮 -->
				<view class="flex align-center justify-between">
					<view class="flex align-center">
						<!-- 头像 -->
						<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;"
						 lazy-load></image>
						<!-- 昵称和发布时间 -->
						<view>
							<view class="font" style="line-height: 1.5;">{{item.username}}</view>
							<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
						</view>
					</view>
					<!-- 按钮 -->
					<view class="flex align-center justify-center rounded bg-main text-white" style="width: 90rpx; height: 50rpx;">
						{{item.isFollow?'已关注':'关注'}}
					</view>
				</view>
				<!-- 标题 -->
				<view class="font my-1">
					{{item.title}}
				</view>
				<!-- 图片 -->
				<image class="rounded w-100" :src="item.titlepic" style="height: 350rpx;"></image>
				<!-- 图标按钮 -->
				<view class="flex align-center">
					<view class="flex align-center justify-center flex-1">
						<text class="iconfont icon-dianzan mr-2"></text>
						<text>{{item.support.support_count}}</text>
					</view>
					<view class="flex align-center justify-center flex-1">
						<text class="iconfont icon-cai mr-2"></text>
						<text>{{item.support.unsupport_count}}</text>
					</view>
					<view class="flex align-center justify-center flex-1">
						<text class="iconfont icon-pinglun mr-2"></text>
						<text>{{item.comment_count}}</text>
					</view>
					<view class="flex align-center justify-center flex-1">
						<text class="iconfont icon-fenxiang mr-2"></text>
						<text>{{item.share_count}}</text>
					</view>
				</view>
			</view>
		</block>

	</view>
</template>

<script>
	export default {
		data() {
			return {
				list: [
					{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support",
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				]
			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

显示:

显然,已经实现了列表渲染。

再实现封装到组件,项目下新建components目录,下新建common目录,下建common-list.vue作为列表组件,如下:

代码语言:javascript
复制
<template>
	<view class="p-2">
		<!-- 头像、昵称和关注按钮 -->
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				<!-- 头像 -->
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;"
				 lazy-load></image>
				<!-- 昵称和发布时间 -->
				<view>
					<view class="font" style="line-height: 1.5;">{{item.username}}</view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
				</view>
			</view>
			<!-- 按钮 -->
			<view class="flex align-center justify-center rounded bg-main text-white" style="width: 90rpx; height: 50rpx;">
				{{item.isFollow?'已关注':'关注'}}
			</view>
		</view>
		<!-- 标题 -->
		<view class="font my-1">
			{{item.title}}
		</view>
		<!-- 图片 -->
		<image class="rounded w-100" :src="item.titlepic" style="height: 350rpx;"></image>
		<!-- 图标按钮 -->
		<view class="flex align-center">
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-dianzan mr-2"></text>
				<text>{{item.support.support_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-cai mr-2"></text>
				<text>{{item.support.unsupport_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-pinglun mr-2"></text>
				<text>{{item.comment_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-fenxiang mr-2"></text>
				<text>{{item.share_count}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			item: Object,
			index: Number
		},
	}
</script>

<style>
</style>

index.vue中导入并使用组件即可,如下:

代码语言:javascript
复制
<template>
	<view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index"></common-list>
		</block>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [
					{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support",
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				]
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

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

二、列表组件优化

1.全局分割线开发

全局分割线也是以组件的形式添加。 先在components/common下新建divider.vue如下:

代码语言:javascript
复制
<template>
	<view style="height: 15rpx; background-color: #F5F5F4;"></view>
</template>

<script>
</script>

<style>
</style>

因为分割线可能在很多地方都会用到,所以可以添加到全局组件,main.js中添加如下:

代码语言:javascript
复制
import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

// 引入全局组件
import divider from './components/common/divider.vue';
Vue.component('divider', divider)

App.mpType = 'app'

const app = new Vue({
    ...App
})
app.$mount()

index.vue中无需导入、直接使用即可,如下:

代码语言:javascript
复制
<template>
	<view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [
					{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support",
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				]
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

显示:

可以看到,有比较明显的分割线效果。

2.动画特效

现在进一步给列表组件添加动画特效。

因为有的帖子没有封面图,因此需要v-if进行判断,需要修改组件common-list.vue,如下:

代码语言:javascript
复制
<template>
	<view class="p-2">
		<!-- 头像、昵称和关注按钮 -->
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				<!-- 头像 -->
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;"
				 lazy-load></image>
				<!-- 昵称和发布时间 -->
				<view>
					<view class="font" style="line-height: 1.5;">{{item.username}}</view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
				</view>
			</view>
			<!-- 按钮 -->
			<view class="flex align-center justify-center rounded bg-main text-white" style="width: 90rpx; height: 50rpx;">
				{{item.isFollow?'已关注':'关注'}}
			</view>
		</view>
		<!-- 标题 -->
		<view class="font my-1">
			{{item.title}}
		</view>
		<!-- 图片 -->
		<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;"></image>
		<!-- 图标按钮 -->
		<view class="flex align-center">
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-dianzan mr-2"></text>
				<text>{{item.support.support_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-cai mr-2"></text>
				<text>{{item.support.unsupport_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-pinglun mr-2"></text>
				<text>{{item.comment_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-fenxiang mr-2"></text>
				<text>{{item.share_count}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			item: Object,
			index: Number
		},
	}
</script>

<style>
</style>

index.vue增加测试数据,如下:

代码语言:javascript
复制
<template>
	<view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [
					{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support",
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "support",
							support_count: 2,
							unsupport_count: 0
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				]
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>

</style>

显示:

可以看到,没有封面图片,也能正常显示。

此时再实现点击关注按钮、添加动画特效,common-list.vue如下:

代码语言:javascript
复制
<template>
	<view class="p-2">
		<!-- 头像、昵称和关注按钮 -->
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				<!-- 头像 -->
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;"
				 lazy-load></image>
				<!-- 昵称和发布时间 -->
				<view>
					<view class="font" style="line-height: 1.5;">{{item.username}}</view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
				</view>
			</view>
			<!-- 按钮 -->
			<view class="flex align-center justify-center rounded bg-main text-white animate__animated animate__faster" hover-class="animate__jello" style="width: 90rpx; height: 50rpx;">
				{{item.isFollow?'已关注':'关注'}}
			</view>
		</view>
		<!-- 标题 -->
		<view class="font my-1">
			{{item.title}}
		</view>
		<!-- 图片 -->
		<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;"></image>
		<!-- 图标按钮 -->
		<view class="flex align-center">
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-dianzan mr-2"></text>
				<text>{{item.support.support_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-cai mr-2"></text>
				<text>{{item.support.unsupport_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-pinglun mr-2"></text>
				<text>{{item.comment_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1">
				<text class="iconfont icon-fenxiang mr-2"></text>
				<text>{{item.share_count}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			item: Object,
			index: Number
		},
	}
</script>

<style>
</style>

显示:

可以看到,点击关注按钮时,已实现了动画效果。

现进一步实现4个图标的特效,如下:

代码语言:javascript
复制
<template>
	<view class="p-2">
		<!-- 头像、昵称和关注按钮 -->
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				<!-- 头像 -->
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;"
				 lazy-load></image>
				<!-- 昵称和发布时间 -->
				<view>
					<view class="font" style="line-height: 1.5;">{{item.username}}</view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
				</view>
			</view>
			<!-- 按钮 -->
			<view class="flex align-center justify-center rounded bg-main text-white animate__animated animate__faster" hover-class="animate__jello" style="width: 90rpx; height: 50rpx;">
				{{item.isFollow?'已关注':'关注'}}
			</view>
		</view>
		<!-- 标题 -->
		<view class="font my-1">
			{{item.title}}
		</view>
		<!-- 图片 -->
		<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;"></image>
		<!-- 图标按钮 -->
		<view class="flex align-center">
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main">
				<text class="iconfont icon-dianzan mr-2"></text>
				<text>{{item.support.support_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main">
				<text class="iconfont icon-cai mr-2"></text>
				<text>{{item.support.unsupport_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main">
				<text class="iconfont icon-pinglun mr-2"></text>
				<text>{{item.comment_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main">
				<text class="iconfont icon-fenxiang mr-2"></text>
				<text>{{item.share_count}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			item: Object,
			index: Number
		},
	}
</script>

<style>
</style>

common.css如下:

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

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

显示:

显然,也实现了动画效果。

3.关注功能完善

common-list组件需要定义接口,点击头像可以进入个人空间,点击关注按钮可以真正进行关注操作,点击点赞和踩实现数据更新,点击评论、转发、标题和图片等跳转到详情页,一般可以通过绑定事件实现。

common-list.vue组件绑定事件实现接口如下:

代码语言:javascript
复制
<template>
	<view class="p-2">
		<!-- 头像、昵称和关注按钮 -->
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				<!-- 头像 -->
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;" @click="openSpace"
				 lazy-load></image>
				<!-- 昵称和发布时间 -->
				<view>
					<view class="font" style="line-height: 1.5;">{{item.username}}</view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
				</view>
			</view>
			<!-- 按钮 -->
			<view class="flex align-center justify-center rounded bg-main text-white animate__animated animate__faster" hover-class="animate__jello" style="width: 90rpx; height: 50rpx;" @click="follow">
				{{item.isFollow?'已关注':'关注'}}
			</view>
		</view>
		<!-- 标题 -->
		<view class="font my-1" @click="openDetail">
			{{item.title}}
		</view>
		<!-- 图片 -->
		<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;" @click="openDetail"></image>
		<!-- 图标按钮 -->
		<view class="flex align-center">
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main" @click="doSupport('support')">
				<text class="iconfont icon-dianzan mr-2"></text>
				<text>{{item.support.support_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main" @click="doSupport('unsupport')">
				<text class="iconfont icon-cai mr-2"></text>
				<text>{{item.support.unsupport_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main" @click="openDetail">
				<text class="iconfont icon-pinglun mr-2"></text>
				<text>{{item.comment_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main" @click="openDetail">
				<text class="iconfont icon-fenxiang mr-2"></text>
				<text>{{item.share_count}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			item: Object,
			index: Number
		},
		methods: {
			// 打开个人空间
			openSpace(){
				console.log('Space opened');
			},
			// 关注
			follow(){
				console.log('Followed');
			},
			// 进入详情页
			openDetail(){
				console.log('Detail opened');
			},
			// 顶踩操作
			doSupport(type){
				console.log(type)
			}
		}
	}
</script>

<style>
</style>

显示:

可以看到,已经模拟出了接口操作。 现进一步实现关注功能,如下:

代码语言:javascript
复制
<template>
	<view class="p-2">
		<!-- 头像、昵称和关注按钮 -->
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				<!-- 头像 -->
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;" @click="openSpace"
				 lazy-load></image>
				<!-- 昵称和发布时间 -->
				<view>
					<view class="font" style="line-height: 1.5;">{{item.username}}</view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
				</view>
			</view>
			<!-- 按钮 -->
			<view v-if="!item.isFollow" class="flex align-center justify-center rounded bg-main text-white animate__animated animate__faster"
			 hover-class="animate__jello" style="width: 90rpx; height: 50rpx;" @click="follow">
				关注
			</view>
		</view>
		<!-- 标题 -->
		<view class="font my-1" @click="openDetail">
			{{item.title}}
		</view>
		<!-- 图片 -->
		<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;" @click="openDetail"></image>
		<!-- 图标按钮 -->
		<view class="flex align-center">
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('support')">
				<text class="iconfont icon-dianzan mr-2"></text>
				<text>{{item.support.support_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('unsupport')">
				<text class="iconfont icon-cai mr-2"></text>
				<text>{{item.support.unsupport_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="openDetail">
				<text class="iconfont icon-pinglun mr-2"></text>
				<text>{{item.comment_count}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="openDetail">
				<text class="iconfont icon-fenxiang mr-2"></text>
				<text>{{item.share_count}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			item: Object,
			index: Number
		},
		methods: {
			// 打开个人空间
			openSpace() {
				console.log('Space opened');
			},
			// 关注
			follow() {
				console.log('Followed');
				// 通知父组件
				this.$emit('follow', this.index);
			},
			// 进入详情页
			openDetail() {
				console.log('Detail opened');
			},
			// 顶踩操作
			doSupport(type) {
				console.log(type)
			}
		}
	}
</script>

<style>
</style>

父组件index.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index" @follow="follow"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [
					{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support",
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "support",
							support_count: 2,
							unsupport_count: 0
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				]
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {
			// 关注
			follow(e){
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注'+this.list[e].username+'成功'
				})
			}
		}
	}
</script>

<style>

</style>

可以看到,实现了子组件common-list向父组件index的消息传递。

显示:

显然,实现了正常的关注功能。

4.顶踩功能

现完善顶踩功能接口。 顶踩有3种状态:顶、踩或未操作,点击顶按钮后,对应数值加1,并且颜色变为激活状态。

common-list.vue如下:

代码语言:javascript
复制
<template>
	<view class="p-2">
		<!-- 头像、昵称和关注按钮 -->
		<view class="flex align-center justify-between">
			<view class="flex align-center">
				<!-- 头像 -->
				<image class="rounded-circle mr-2" :src="item.userpic" mode="" style="width: 65rpx; height: 65rpx;" @click="openSpace"
				 lazy-load></image>
				<!-- 昵称和发布时间 -->
				<view>
					<view class="font" style="line-height: 1.5;">{{item.username}}</view>
					<text class="font-sm text-light-muted" style="line-height: 1.5;">{{item.newstime}}</text>
				</view>
			</view>
			<!-- 按钮 -->
			<view v-if="!item.isFollow" class="flex align-center justify-center rounded bg-main text-white animate__animated animate__faster"
			 hover-class="animate__jello" style="width: 90rpx; height: 50rpx;" @click="follow">
				关注
			</view>
		</view>
		<!-- 标题 -->
		<view class="font my-1" @click="openDetail">
			{{item.title}}
		</view>
		<!-- 图片 -->
		<image v-if="item.titlepic" class="rounded w-100" :src="item.titlepic" style="height: 350rpx;" @click="openDetail"></image>
		<!-- 图标按钮 -->
		<view class="flex align-center">
			<!-- 顶 -->
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('support')" :class="item.support.type === 'support' ? 'support-active' : ''">
				<text class="iconfont icon-dianzan mr-2"></text>
				<text>{{item.support.support_count > 0 ? item.support.support_count : '支持'}}</text>
			</view>
			<!-- 踩 -->
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="doSupport('unsupport')" :class="item.support.type === 'unsupport' ? 'support-active' : ''">
				<text class="iconfont icon-cai mr-2"></text>
				<text>{{item.support.unsupport_count > 0 ? item.support.unsupport_count : '反对'}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="openDetail">
				<text class="iconfont icon-pinglun mr-2"></text>
				<text>{{item.comment_count > 0 ? item.comment_count : '评论'}}</text>
			</view>
			<view class="flex align-center justify-center flex-1 animate__animated animate__faster" hover-class="animate__jello text-main"
			 @click="openDetail">
				<text class="iconfont icon-fenxiang mr-2"></text>
				<text>{{item.share_count > 0 ? item.share_count : '分享'}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props: {
			item: Object,
			index: Number
		},
		methods: {
			// 打开个人空间
			openSpace() {
				console.log('Space opened');
			},
			// 关注
			follow() {
				console.log('Followed');
				// 通知父组件
				this.$emit('follow', this.index);
			},
			// 进入详情页
			openDetail() {
				console.log('Detail opened');
			},
			// 顶踩操作
			doSupport(type) {
				console.log(type);
				// 通知父组件
				this.$emit('doSupport', {
					type,
					index: this.index
				})
			}
		}
	}
</script>

<style>
	.support-active {
		color: #FF4A6A;
	}
</style>

index.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support", // 顶
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "unsupport", // 踩
							support_count: 2,
							unsupport_count: 3
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Jessica",
						userpic: "/static/img/userpic/7.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "Django+Vue开发生鲜电商平台",
						titlepic: "/static/img/datapic/11.jpg",
						support: {
							type: "", // 未操作
							support_count: 2,
							unsupport_count: 7
						},
						comment_count: 0,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午18:20",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				]
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			}
		}
	}
</script>

<style>

</style>

在父组件和子组件中进行了一系列优化,包括图标颜色变化,限制用户要么为顶要么为踩、并且只能踩顶一次、不能多次踩顶,次数为0时显示文本等。

显示:

显然,已经实现了顶踩的基本功能。

三、滚动选项卡开发

1.选项卡动态显示

滚动选项卡采用scroll-view组件实现,其scroll-into-view属性可以加速开发。

先实现滚动选项卡,base.css设置scroll-view样式如下:

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

/* flex布局 */
.flex {
	display: flex;
}

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

.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;
}

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

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

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

/* 字体 */
.font-md {
	font-size: 35rpx;
}

.font {
	font-size: 30rpx;
}

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

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

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

/* 宽度 */
/* #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 */

index.vue中增加滚动选项卡如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row">
			<view v-for="i in 20" :key="i" class="scroll-row-item px-5 py-3">{{i}}</view>
		</scroll-view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support", // 顶
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "unsupport", // 踩
							support_count: 2,
							unsupport_count: 3
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Jessica",
						userpic: "/static/img/userpic/7.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "Django+Vue开发生鲜电商平台",
						titlepic: "/static/img/datapic/11.jpg",
						support: {
							type: "", // 未操作
							support_count: 2,
							unsupport_count: 7
						},
						comment_count: 0,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午18:20",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				]
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,已经实现了滚动。

现实现选项卡内容显示,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row ">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index" :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''">{{item.name}}</view>
		</scroll-view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support", // 顶
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "unsupport", // 踩
							support_count: 2,
							unsupport_count: 3
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Jessica",
						userpic: "/static/img/userpic/7.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "Django+Vue开发生鲜电商平台",
						titlepic: "/static/img/datapic/11.jpg",
						support: {
							type: "", // 未操作
							support_count: 2,
							unsupport_count: 7
						},
						comment_count: 0,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午18:20",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					}
				],
				tabIndex: 0
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			}
		}
	}
</script>

<style>

</style>

base.css如下:

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

/* flex布局 */
.flex {
	display: flex;
}

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

.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;
}

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

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

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

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

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

/* 字体 */
.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;
}

/* 宽度 */
/* #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 */

显示:

再绑定事件实现动态滚动和动画显示的效果,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-view>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support", // 顶
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "unsupport", // 踩
							support_count: 2,
							unsupport_count: 3
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Jessica",
						userpic: "/static/img/userpic/7.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "Django+Vue开发生鲜电商平台",
						titlepic: "/static/img/datapic/11.jpg",
						support: {
							type: "", // 未操作
							support_count: 2,
							unsupport_count: 7
						},
						comment_count: 0,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午18:20",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}
					
				],
				tabIndex: 0,
				scrollInto: ''
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index){
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,已经实现了动态滑动。

2.列表滑动实现

现进一步实现点击选项卡,下面显示对应的列表,使用swiper(滑块视图容器)实现,可以做轮播图和滑动列表,其常见属性和含义如下:

属性名

类型

默认值

含义

indicator-dots

Boolean

false

是否显示面板指示点

autoplay

Boolean

false

是否自动切换

current

Number

0

当前所在滑块的 index

interval

Number

5000

自动切换时间间隔

duration

Number

500

滑动动画时长

具体可参考https://uniapp.dcloud.io/component/swiper

先实现滑块,并与上面的选项卡联动,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-view>
		<!-- 滑块 -->
		<swiper :duration="150" :current="tabIndex" @change="onChangeTab">
			<swiper-item v-for="(item, index) in tabBars" :key="index">
				<view class="swiper-item">{{item.name}}</view>
			</swiper-item>
		</swiper>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support", // 顶
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "unsupport", // 踩
							support_count: 2,
							unsupport_count: 3
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Jessica",
						userpic: "/static/img/userpic/7.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "Django+Vue开发生鲜电商平台",
						titlepic: "/static/img/datapic/11.jpg",
						support: {
							type: "", // 未操作
							support_count: 2,
							unsupport_count: 7
						},
						comment_count: 0,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午18:20",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}
					
				],
				tabIndex: 0,
				scrollInto: ''
			}
		},
		components: {
			commonList
		},
		onLoad() {

		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index){
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
		}
	}
</script>

<style>

</style>

显示:

可以看到,实现了滑块,并且滑块可以和选项卡实现联动同步

现实现滑块列表示意,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-view>
		<!-- 滑块 -->
		<swiper :duration="150" :current="tabIndex" @change="onChangeTab" :style="'height: '+scrollH+'px;'">
			<swiper-item v-for="(item, index) in tabBars" :key="index">
				<scroll-view scroll-y="true" :style="'height: '+scrollH+'px;'">
					<view v-for="i in 100" :key="i">{{i}}</view>
				</scroll-view>
			</swiper-item>
		</swiper>
		<block v-for="(item, index) in list" :key="index">
			<!-- 列表 -->
			<common-list :item="item" :index="index" @follow="follow" @doSupport="doSupport"></common-list>
			<!-- 全局分割线 -->
			<divider></divider>
		</block>
	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				list: [{
						username: "Corley",
						userpic: "/static/img/userpic/12.jpg",
						newstime: "2021-01-24 上午11:30",
						isFollow: false,
						title: "uni-app入门教程",
						titlepic: "/static/img/datapic/42.jpg",
						support: {
							type: "support", // 顶
							support_count: 1,
							unsupport_count: 2
						},
						comment_count: 2,
						share_count: 2
					},
					{
						username: "Brittany",
						userpic: "/static/img/userpic/16.jpg",
						newstime: "2021-01-24 下午14:00",
						isFollow: false,
						title: "商业数据分析从入门到入职",
						support: {
							type: "unsupport", // 踩
							support_count: 2,
							unsupport_count: 3
						},
						comment_count: 5,
						share_count: 1
					},
					{
						username: "Jessica",
						userpic: "/static/img/userpic/7.jpg",
						newstime: "2021-01-24 下午14:44",
						isFollow: true,
						title: "Django+Vue开发生鲜电商平台",
						titlepic: "/static/img/datapic/11.jpg",
						support: {
							type: "", // 未操作
							support_count: 2,
							unsupport_count: 7
						},
						comment_count: 0,
						share_count: 2
					},
					{
						username: "Ashley",
						userpic: "/static/img/userpic/20.jpg",
						newstime: "2021-01-24 下午18:20",
						isFollow: true,
						title: "uni-app实战之社区交友APP",
						titlepic: "/static/img/datapic/30.jpg",
						support: {
							type: "support",
							support_count: 5,
							unsupport_count: 1
						},
						comment_count: 3,
						share_count: 0
					}
				],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList
		},
		onLoad() {
			uni.getSystemInfo({
				success:function(res){
					console.log(res);
					this.scrollH = res.windowHeight - uni.upx2px(100);
				}
			})
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			}
		}
	}
</script>

<style>

</style>

为了给swiper设置高度,在生命周期onLoad()通过uni.getSystemInfo()获取windowHeight,即可使用窗口高度,具体为屏幕高度除去NavigationBar和TabBar的高度,再减去选项卡的高度(调用uni.upx2px()转化为以px为单位)。

显示:

可以看到,实现了列表滑动。

3.列表显示和同步

现完善列表项,将之前实现的block放入swiper中,并根据顶部选项卡显示不同的列表。 index.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-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;'">
					<block v-for="(item2, index2) in item.list" :key="index2">
						<!-- 列表 -->
						<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
						<!-- 全局分割线 -->
						<divider></divider>
					</block>
				</scroll-view>
			</swiper-item>
		</swiper>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				newsList: [],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList
		},
		onLoad() {
			uni.getSystemInfo({
					success: function(res) {
						console.log(res);
						this.scrollH = res.windowHeight - uni.upx2px(100);
					}
				}),
				// 根据选项生成列表
				this.getData();
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					let obj = {
						list: [{
								username: "Corley",
								userpic: "/static/img/userpic/12.jpg",
								newstime: "2021-01-24 上午11:30",
								isFollow: false,
								title: "uni-app入门教程",
								titlepic: "/static/img/datapic/42.jpg",
								support: {
									type: "support", // 顶
									support_count: 1,
									unsupport_count: 2
								},
								comment_count: 2,
								share_count: 2
							},
							{
								username: "Brittany",
								userpic: "/static/img/userpic/16.jpg",
								newstime: "2021-01-24 下午14:00",
								isFollow: false,
								title: "商业数据分析从入门到入职",
								support: {
									type: "unsupport", // 踩
									support_count: 2,
									unsupport_count: 3
								},
								comment_count: 5,
								share_count: 1
							},
							{
								username: "Jessica",
								userpic: "/static/img/userpic/7.jpg",
								newstime: "2021-01-24 下午14:44",
								isFollow: true,
								title: "Django+Vue开发生鲜电商平台",
								titlepic: "/static/img/datapic/11.jpg",
								support: {
									type: "", // 未操作
									support_count: 2,
									unsupport_count: 7
								},
								comment_count: 0,
								share_count: 2
							},
							{
								username: "Ashley",
								userpic: "/static/img/userpic/20.jpg",
								newstime: "2021-01-24 下午18:20",
								isFollow: true,
								title: "uni-app实战之社区交友APP",
								titlepic: "/static/img/datapic/30.jpg",
								support: {
									type: "support",
									support_count: 5,
									unsupport_count: 1
								},
								comment_count: 3,
								share_count: 0
							}
						]
					}
					arr.push(obj)
				}
				this.newsList = arr;
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,已经实现了动态切换和显示数据。

4.上拉加载开发

现实现上拉到底部加载数据,需要实现各个选项卡独立上拉加载。

先实现基本的下拉加载,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-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)">
					<!-- 列表 -->
					<block v-for="(item2, index2) in item.list" :key="index2">
						<!-- 列表组件 -->
						<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
						<!-- 全局分割线 -->
						<divider></divider>
					</block>
					<!-- 上拉加载 -->
					<view class="flex align-center justify-center py-3">
						<text class="font text-light-muted">{{item.loadmore}}</text>
					</view>
				</scroll-view>
			</swiper-item>
		</swiper>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				newsList: [],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList
		},
		onLoad() {
			uni.getSystemInfo({
					success: function(res) {
						console.log(res);
						this.scrollH = res.windowHeight - uni.upx2px(100);
					}
				}),
				// 根据选项生成列表
				this.getData();
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多", 
						list: [{
								username: "Corley",
								userpic: "/static/img/userpic/12.jpg",
								newstime: "2021-01-24 上午11:30",
								isFollow: false,
								title: "uni-app入门教程",
								titlepic: "/static/img/datapic/42.jpg",
								support: {
									type: "support", // 顶
									support_count: 1,
									unsupport_count: 2
								},
								comment_count: 2,
								share_count: 2
							},
							{
								username: "Brittany",
								userpic: "/static/img/userpic/16.jpg",
								newstime: "2021-01-24 下午14:00",
								isFollow: false,
								title: "商业数据分析从入门到入职",
								support: {
									type: "unsupport", // 踩
									support_count: 2,
									unsupport_count: 3
								},
								comment_count: 5,
								share_count: 1
							},
							{
								username: "Jessica",
								userpic: "/static/img/userpic/7.jpg",
								newstime: "2021-01-24 下午14:44",
								isFollow: true,
								title: "Django+Vue开发生鲜电商平台",
								titlepic: "/static/img/datapic/11.jpg",
								support: {
									type: "", // 未操作
									support_count: 2,
									unsupport_count: 7
								},
								comment_count: 0,
								share_count: 2
							},
							{
								username: "Ashley",
								userpic: "/static/img/userpic/20.jpg",
								newstime: "2021-01-24 下午18:20",
								isFollow: true,
								title: "uni-app实战之社区交友APP",
								titlepic: "/static/img/datapic/30.jpg",
								support: {
									type: "support",
									support_count: 5,
									unsupport_count: 1
								},
								comment_count: 3,
								share_count: 0
							}
						]
					}
					arr.push(obj)
				}
				this.newsList = arr;
			},
			// 上拉加载更多
			loadMore(index) {
				this.newsList[index].loadmore = '加载中...';
			}
		}
	}
</script>

<style>

</style>

显示:

可以看到,在下拉到底之前,消息为上拉加载更多,到底后触发@scrolltolower事件,变为加载中...

再实现模拟加载更多数据,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-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)">
					<!-- 列表 -->
					<block v-for="(item2, index2) in item.list" :key="index2">
						<!-- 列表组件 -->
						<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
						<!-- 全局分割线 -->
						<divider></divider>
					</block>
					<!-- 上拉加载 -->
					<view class="flex align-center justify-center py-3">
						<text class="font text-light-muted">{{item.loadmore}}</text>
					</view>
				</scroll-view>
			</swiper-item>
		</swiper>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				newsList: [],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList
		},
		onLoad() {
			uni.getSystemInfo({
					success: function(res) {
						console.log(res);
						this.scrollH = res.windowHeight - uni.upx2px(100);
					}
				}),
				// 根据选项生成列表
				this.getData();
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多", 
						list: [{
								username: "Corley",
								userpic: "/static/img/userpic/12.jpg",
								newstime: "2021-01-24 上午11:30",
								isFollow: false,
								title: "uni-app入门教程",
								titlepic: "/static/img/datapic/42.jpg",
								support: {
									type: "support", // 顶
									support_count: 1,
									unsupport_count: 2
								},
								comment_count: 2,
								share_count: 2
							},
							{
								username: "Brittany",
								userpic: "/static/img/userpic/16.jpg",
								newstime: "2021-01-24 下午14:00",
								isFollow: false,
								title: "商业数据分析从入门到入职",
								support: {
									type: "unsupport", // 踩
									support_count: 2,
									unsupport_count: 3
								},
								comment_count: 5,
								share_count: 1
							},
							{
								username: "Jessica",
								userpic: "/static/img/userpic/7.jpg",
								newstime: "2021-01-24 下午14:44",
								isFollow: true,
								title: "Django+Vue开发生鲜电商平台",
								titlepic: "/static/img/datapic/11.jpg",
								support: {
									type: "", // 未操作
									support_count: 2,
									unsupport_count: 7
								},
								comment_count: 0,
								share_count: 2
							},
							{
								username: "Ashley",
								userpic: "/static/img/userpic/20.jpg",
								newstime: "2021-01-24 下午18:20",
								isFollow: true,
								title: "uni-app实战之社区交友APP",
								titlepic: "/static/img/datapic/30.jpg",
								support: {
									type: "support",
									support_count: 5,
									unsupport_count: 1
								},
								comment_count: 3,
								share_count: 0
							}
						]
					}
					arr.push(obj)
				}
				this.newsList = arr;
			},
			// 上拉加载更多
			loadMore(index) {
				// 获取当前列表
				let item = this.newsList[index];
				// 修改当前列表加载状态
				item.loadmore = '加载中...';
				// 模拟数据请求
				setTimeout(()=>{
					// 加载数据
					item.list = [...item.list, ...item.list];
					// 恢复加载状态
					this.newsList[index].loadmore = '上拉加载更多';
				}, 2000)
			}
		}
	}
</script>

<style>

</style>

其中,[...item.list, ...item.list]是ES6的语法,为扩展运算符,即将item.list再复制一份; 之所以可以通过操作item变量来操作数据,是因为指向了相同的地址,即同一个引用。

显示:

可以看到,模拟出了加载更多数据。

5.封装上拉加载组件

前面实现的上拉加载更多并没有进行判断,可以一直向下滑动加载更多,显然这是不合理的,因此需要进行判断

index.vue如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-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)">
					<!-- 列表 -->
					<block v-for="(item2, index2) in item.list" :key="index2">
						<!-- 列表组件 -->
						<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
						<!-- 全局分割线 -->
						<divider></divider>
					</block>
					<!-- 上拉加载 -->
					<view class="flex align-center justify-center py-3">
						<text class="font text-light-muted">{{item.loadmore}}</text>
					</view>
				</scroll-view>
			</swiper-item>
		</swiper>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	export default {
		data() {
			return {
				newsList: [],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList
		},
		onLoad() {
			uni.getSystemInfo({
					success: function(res) {
						console.log(res);
						this.scrollH = res.windowHeight - uni.upx2px(100);
					}
				}),
				// 根据选项生成列表
				this.getData();
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多", 
						list: [{
								username: "Corley",
								userpic: "/static/img/userpic/12.jpg",
								newstime: "2021-01-24 上午11:30",
								isFollow: false,
								title: "uni-app入门教程",
								titlepic: "/static/img/datapic/42.jpg",
								support: {
									type: "support", // 顶
									support_count: 1,
									unsupport_count: 2
								},
								comment_count: 2,
								share_count: 2
							},
							{
								username: "Brittany",
								userpic: "/static/img/userpic/16.jpg",
								newstime: "2021-01-24 下午14:00",
								isFollow: false,
								title: "商业数据分析从入门到入职",
								support: {
									type: "unsupport", // 踩
									support_count: 2,
									unsupport_count: 3
								},
								comment_count: 5,
								share_count: 1
							},
							{
								username: "Jessica",
								userpic: "/static/img/userpic/7.jpg",
								newstime: "2021-01-24 下午14:44",
								isFollow: true,
								title: "Django+Vue开发生鲜电商平台",
								titlepic: "/static/img/datapic/11.jpg",
								support: {
									type: "", // 未操作
									support_count: 2,
									unsupport_count: 7
								},
								comment_count: 0,
								share_count: 2
							},
							{
								username: "Ashley",
								userpic: "/static/img/userpic/20.jpg",
								newstime: "2021-01-24 下午18:20",
								isFollow: true,
								title: "uni-app实战之社区交友APP",
								titlepic: "/static/img/datapic/30.jpg",
								support: {
									type: "support",
									support_count: 5,
									unsupport_count: 1
								},
								comment_count: 3,
								share_count: 0
							}
						]
					}
					arr.push(obj)
				}
				this.newsList = arr;
			},
			// 上拉加载更多
			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)
			}
		}
	}
</script>

<style>

</style>

效果与之前相同,如果loadmore不为上拉加载更多,则会停止加载更多数据。

现将下拉加载更多封装为组件,components/common下新建load-more.vue如下:

代码语言:javascript
复制
<template>
	<view class="flex align-center justify-center py-3">
		<text class="font text-light-muted">{{loadmore}}</text>
	</view>
</template>

<script>
	export default {
		props: ['loadmore']
	}
</script>

<style>
</style>

index.vue中使用组件如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-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)">
					<!-- 列表 -->
					<block v-for="(item2, index2) in item.list" :key="index2">
						<!-- 列表组件 -->
						<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
						<!-- 全局分割线 -->
						<divider></divider>
					</block>
					<!-- 上拉加载 -->
					<load-more :loadmore="item.loadmore"></load-more>
				</scroll-view>
			</swiper-item>
		</swiper>

	</view>
</template>

<script>
	import commonList from '@/components/common/common-list.vue';
	import loadMore from '@/components/common/load-more.vue';
	export default {
		data() {
			return {
				newsList: [],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList,
			loadMore
		},
		onLoad() {
			uni.getSystemInfo({
					success: function(res) {
						console.log(res);
						this.scrollH = res.windowHeight - uni.upx2px(100);
					}
				}),
				// 根据选项生成列表
				this.getData();
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多", 
						list: [{
								username: "Corley",
								userpic: "/static/img/userpic/12.jpg",
								newstime: "2021-01-24 上午11:30",
								isFollow: false,
								title: "uni-app入门教程",
								titlepic: "/static/img/datapic/42.jpg",
								support: {
									type: "support", // 顶
									support_count: 1,
									unsupport_count: 2
								},
								comment_count: 2,
								share_count: 2
							},
							{
								username: "Brittany",
								userpic: "/static/img/userpic/16.jpg",
								newstime: "2021-01-24 下午14:00",
								isFollow: false,
								title: "商业数据分析从入门到入职",
								support: {
									type: "unsupport", // 踩
									support_count: 2,
									unsupport_count: 3
								},
								comment_count: 5,
								share_count: 1
							},
							{
								username: "Jessica",
								userpic: "/static/img/userpic/7.jpg",
								newstime: "2021-01-24 下午14:44",
								isFollow: true,
								title: "Django+Vue开发生鲜电商平台",
								titlepic: "/static/img/datapic/11.jpg",
								support: {
									type: "", // 未操作
									support_count: 2,
									unsupport_count: 7
								},
								comment_count: 0,
								share_count: 2
							},
							{
								username: "Ashley",
								userpic: "/static/img/userpic/20.jpg",
								newstime: "2021-01-24 下午18:20",
								isFollow: true,
								title: "uni-app实战之社区交友APP",
								titlepic: "/static/img/datapic/30.jpg",
								support: {
									type: "support",
									support_count: 5,
									unsupport_count: 1
								},
								comment_count: 3,
								share_count: 0
							}
						]
					}
					arr.push(obj)
				}
				this.newsList = arr;
			},
			// 上拉加载更多
			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)
			}
		}
	}
</script>

<style>

</style>

效果与之前相同。

6.封装无数据默认组件

先实现没有数据的情况,如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-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">
							<!-- 列表组件 -->
							<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
							<!-- 全局分割线 -->
							<divider></divider>
						</block>
						<!-- 上拉加载 -->
						<load-more :loadmore="item.loadmore"></load-more>
					</template>
					<!-- 无数据 -->
					<template v-else>
						<view class="flex flex-column align-center justify-center pt-7">
							<image src="@/static/common/nothing.jpg" style="width: 300rpx; height: 300rpx;"></image>
							<text class="font-md">这里什么都没有哦~</text>
						</view>
					</template>
				</scroll-view>
			</swiper-item>
		</swiper>

	</view>
</template>

<script>
	const test_data = [{
			username: "Corley",
			userpic: "/static/img/userpic/12.jpg",
			newstime: "2021-01-24 上午11:30",
			isFollow: false,
			title: "uni-app入门教程",
			titlepic: "/static/img/datapic/42.jpg",
			support: {
				type: "support", // 顶
				support_count: 1,
				unsupport_count: 2
			},
			comment_count: 2,
			share_count: 2
		},
		{
			username: "Brittany",
			userpic: "/static/img/userpic/16.jpg",
			newstime: "2021-01-24 下午14:00",
			isFollow: false,
			title: "商业数据分析从入门到入职",
			support: {
				type: "unsupport", // 踩
				support_count: 2,
				unsupport_count: 3
			},
			comment_count: 5,
			share_count: 1
		},
		{
			username: "Jessica",
			userpic: "/static/img/userpic/7.jpg",
			newstime: "2021-01-24 下午14:44",
			isFollow: true,
			title: "Django+Vue开发生鲜电商平台",
			titlepic: "/static/img/datapic/11.jpg",
			support: {
				type: "", // 未操作
				support_count: 2,
				unsupport_count: 7
			},
			comment_count: 0,
			share_count: 2
		},
		{
			username: "Ashley",
			userpic: "/static/img/userpic/20.jpg",
			newstime: "2021-01-24 下午18:20",
			isFollow: true,
			title: "uni-app实战之社区交友APP",
			titlepic: "/static/img/datapic/30.jpg",
			support: {
				type: "support",
				support_count: 5,
				unsupport_count: 1
			},
			comment_count: 3,
			share_count: 0
		}
	];
	import commonList from '@/components/common/common-list.vue';
	import loadMore from '@/components/common/load-more.vue';
	export default {
		data() {
			return {
				newsList: [],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList,
			loadMore
		},
		onLoad() {
			uni.getSystemInfo({
					success: function(res) {
						console.log(res);
						this.scrollH = res.windowHeight - uni.upx2px(100);
					}
				}),
				// 根据选项生成列表
				this.getData();
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多",
						list: []
					}
					if (i % 3 !== 2) {
						obj.list = test_data;
					}
					arr.push(obj)
				}
				this.newsList = arr;
			},
			// 上拉加载更多
			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)
			}
		}
	}
</script>

<style>

</style>

base.css定义样式如下:

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

/* flex布局 */
.flex {
	display: flex;
}
.flex-column {
	flex-direction: column;
}

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

.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;
}

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

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

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

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

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

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

/* 字体 */
.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;
}

/* 宽度 */
/* #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 */

显示:

可以看到,部分页面没有数据,显示的是图片和提示文字。

再实现封装为组件,components/common下新建no-thing.vue如下:

代码语言:javascript
复制
<template>
	<view class="flex flex-column align-center justify-center pt-7">
		<image src="@/static/common/nothing.jpg" style="width: 300rpx; height: 300rpx;"></image>
		<text class="font-md">这里什么都没有哦~</text>
	</view>
</template>

<script>
</script>

<style>
</style>

main.js中引入全局组件如下:

代码语言:javascript
复制
import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

// 引入全局组件
import divider from './components/common/divider.vue';
Vue.component('divider', divider)
import noThing from './components/common/no-thing.vue';
Vue.component('no-thing', noThing)

App.mpType = 'app'

const app = new Vue({
    ...App
})
app.$mount()

index.vue中使用全局组件如下:

代码语言:javascript
复制
<template>
	<view>
		<!-- 顶部选项卡 -->
		<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
			<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
			 :class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
		</scroll-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">
							<!-- 列表组件 -->
							<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
							<!-- 全局分割线 -->
							<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>
	const test_data = [{
			username: "Corley",
			userpic: "/static/img/userpic/12.jpg",
			newstime: "2021-01-24 上午11:30",
			isFollow: false,
			title: "uni-app入门教程",
			titlepic: "/static/img/datapic/42.jpg",
			support: {
				type: "support", // 顶
				support_count: 1,
				unsupport_count: 2
			},
			comment_count: 2,
			share_count: 2
		},
		{
			username: "Brittany",
			userpic: "/static/img/userpic/16.jpg",
			newstime: "2021-01-24 下午14:00",
			isFollow: false,
			title: "商业数据分析从入门到入职",
			support: {
				type: "unsupport", // 踩
				support_count: 2,
				unsupport_count: 3
			},
			comment_count: 5,
			share_count: 1
		},
		{
			username: "Jessica",
			userpic: "/static/img/userpic/7.jpg",
			newstime: "2021-01-24 下午14:44",
			isFollow: true,
			title: "Django+Vue开发生鲜电商平台",
			titlepic: "/static/img/datapic/11.jpg",
			support: {
				type: "", // 未操作
				support_count: 2,
				unsupport_count: 7
			},
			comment_count: 0,
			share_count: 2
		},
		{
			username: "Ashley",
			userpic: "/static/img/userpic/20.jpg",
			newstime: "2021-01-24 下午18:20",
			isFollow: true,
			title: "uni-app实战之社区交友APP",
			titlepic: "/static/img/datapic/30.jpg",
			support: {
				type: "support",
				support_count: 5,
				unsupport_count: 1
			},
			comment_count: 3,
			share_count: 0
		}
	];
	import commonList from '@/components/common/common-list.vue';
	import loadMore from '@/components/common/load-more.vue';
	export default {
		data() {
			return {
				newsList: [],
				// 顶部选项卡
				tabBars: [{
						name: '关注'
					},
					{
						name: '推荐'
					},
					{
						name: '体育'
					},
					{
						name: '热点'
					},
					{
						name: '财经'
					},
					{
						name: '娱乐'
					},
					{
						name: '军事'
					},
					{
						name: '历史'
					},
					{
						name: '本地'
					}

				],
				tabIndex: 0,
				scrollInto: '',
				// 列表高度
				scrollH: 600
			}
		},
		components: {
			commonList,
			loadMore
		},
		onLoad() {
			uni.getSystemInfo({
					success: function(res) {
						console.log(res);
						this.scrollH = res.windowHeight - uni.upx2px(100);
					}
				}),
				// 根据选项生成列表
				this.getData();
		},
		methods: {
			// 关注
			follow(e) {
				console.log('Index followed');
				console.log(e);
				this.list[e].isFollow = true;
				uni.showToast({
					title: '关注' + this.list[e].username + '成功'
				})
			},
			// 顶踩操作
			doSupport(e) {
				console.log(e);
				// 获取当前列表项
				let item = this.list[e.index];
				let msg = e.type === 'support' ? '顶' : '踩';
				// 之前未顶踩过
				if (item.support.type === '') {
					item.support[e.type + '_count']++;
				}
				// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
				else if (item.support.type === 'support' && e.type === 'unsupport') {
					item.support.support_count--;
					item.support.unsupport_count++;
				}
				// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
				else if (item.support.type === 'unsupport' && e.type === 'support') {
					item.support.unsupport_count--;
					item.support.support_count++;
				}
				item.support.type = e.type;
				uni.showToast({
					title: msg + '成功'
				})
			},
			// 切换选项
			changeTab(index) {
				if (this.tabIndex === index) {
					return;
				}
				this.tabIndex = index;
				// 滚动到指定元素
				this.scrollInto = 'tab' + index;
			},
			// 监听滑动
			onChangeTab(e) {
				console.log(e);
				this.changeTab(e.detail.current);
			},
			// 获取数据
			getData() {
				var arr = [];
				for (let i = 0; i < this.tabBars.length; i++) {
					// 生成列表模板
					let obj = {
						// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
						loadmore: "上拉加载更多",
						list: []
					}
					if (i % 3 !== 2) {
						obj.list = test_data;
					}
					arr.push(obj)
				}
				this.newsList = arr;
			},
			// 上拉加载更多
			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)
			}
		}
	}
</script>

<style>

</style>

效果与之前相同。

总结

首页的开发标志着进入正式的开发阶段,代码量逐渐增多、逻辑也逐渐复杂,图文列表和滚动选项卡的开发业务逻辑较多,因此也进行了大量的优化,包括组件封装和CSS样式提取等,这都有利于项目的维护和扩展。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、图文列表样式开发
    • 1.pages.json配置
      • 2.头像、昵称和关注按钮
        • 3.标题和互动按钮
          • 4.封装样式组件
          • 二、列表组件优化
            • 1.全局分割线开发
              • 2.动画特效
                • 3.关注功能完善
                  • 4.顶踩功能
                  • 三、滚动选项卡开发
                    • 1.选项卡动态显示
                      • 2.列表滑动实现
                        • 3.列表显示和同步
                          • 4.上拉加载开发
                            • 5.封装上拉加载组件
                              • 6.封装无数据默认组件
                              • 总结
                              相关产品与服务
                              容器服务
                              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档