7

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

 3 years ago
source link: https://blog.csdn.net/CUFEECR/article/details/113132778
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

一、图文列表样式开发

1.pages.json配置

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

先下载发帖的图标,演示如下:
uniapp social app index develop post icon add

解压下载后的压缩包并将其中的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如下:

{
	"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"
			}
		]
	}
}

显示:
uniapp social app index develop pages search show

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

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页面设置头像显示如下:

<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>

显示:
uniapp social app index develop list header

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

如需直接使用样式和素材等文件,可以直接点击加QQ群 ,在群文件夹uni-app实战之社区交友APP中下载即可。

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

<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>

显示:
uniapp social app index develop list header nickname follow

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

3.标题和互动按钮

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

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

index.vue如下:

<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>

显示:
uniapp social app index develop list header dianzan cai

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

4.封装样式组件

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

/* 内外边距 */
.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保存本项目全局样式,如下:

/* 本项目全局样式 */
.bg-main {
	background-color: #FF4A6A;
}

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

<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简化如下:

<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>

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

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

<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>

显示:
uniapp social app index develop pages list render

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

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

<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中导入并使用组件即可,如下:

<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如下:

<template>
	<view style="height: 15rpx; background-color: #F5F5F4;"></view>
</template>

<script>
</script>

<style>
</style>

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

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中无需导入、直接使用即可,如下:

<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>

显示:
uniapp social app index develop list divider

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

2.动画特效

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

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

<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增加测试数据,如下:

<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>

显示:
uniapp social app index develop optimize titlepic v-if

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

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

<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>

显示:
uniapp social app index develop optimize follow animation

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

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

<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如下:

/* 本项目全局样式 */
/* 背景 */
.bg-main {
	background-color: #FF4A6A;
}

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

显示:
uniapp social app index develop optimize dianzan animation

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

3.关注功能完善

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

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

<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>

显示:
uniapp social app index develop optimize follow interface

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

<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如下:

<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的消息传递。

显示:
uniapp social app index develop optimize follow

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

4.顶踩功能

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

common-list.vue如下:

<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如下:

<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时显示文本等。

显示:
uniapp social app index develop optimize ding cai

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

三、滚动选项卡开发

1.选项卡动态显示

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

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

/* 内外边距 */
.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中增加滚动选项卡如下:

<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>

显示:
uniapp social app index develop scrolltab scroll

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

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

<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如下:

/* 内外边距 */
.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 */

显示:
uniapp social app index develop scrolltab scroll list

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

<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>

显示:
uniapp social app index develop scrolltab scroll dynamic

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

2.列表滑动实现

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

属性名类型默认值含义indicator-dotsBooleanfalse是否显示面板指示点autoplayBooleanfalse是否自动切换currentNumber0当前所在滑块的 indexintervalNumber5000自动切换时间间隔durationNumber500滑动动画时长

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

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

<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>

显示:
uniapp social app index develop scrolltab swiper connect

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

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

<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为单位)。

显示:
uniapp social app index develop scrolltab swiper list

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

3.列表显示和同步

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

<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>

显示:
uniapp social app index develop scrolltab swiper list data

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

4.上拉加载开发

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

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

<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>

显示:
uniapp social app index develop loadmore first

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

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

<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变量来操作数据,是因为指向了相同的地址,即同一个引用。

显示:
uniapp social app index develop loadmore data double

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

5.封装上拉加载组件

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

index.vue如下:

<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如下:

<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中使用组件如下:

<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.封装无数据默认组件

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

<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定义样式如下:

/* 内外边距 */
.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 */

显示:
uniapp social app index develop nodata view

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

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

<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中引入全局组件如下:

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中使用全局组件如下:

<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样式提取等,这都有利于项目的维护和扩展。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK