本文主要介绍了首页图文列表和滚动选项卡的开发: 图文列表的开发,包括顶部导航栏配置,图文列表项(头像、昵称、关注按钮、标题、标题封面图、点赞、踩、评论和分享)等的开发; 列表组件优化,包括分割线的开发和封装,动画特效实现,关注、顶踩功能的完善; 滚动选项卡开发,包括顶部选项卡开发、列表的同步显示和滑动,上拉加载的开发和封装,无数据组件开发等。
删除之前创建的demo页面及其目录,之前在配置pages.json时,"pages"
中并未设置style
,此时需要设置"app-plus"
,即配置编译到 App 平台时的特定样式。
先下载发帖的图标,演示如下:
解压下载后的压缩包并将其中的iconfont.ttf复制到项目目录下的static目录下,并作为首页配置的app-plus>titleNView>buttons>fontSrc
;
同时在我的项目页面选择Unicode,并复制发帖icon的代码,例如
,修改为uni-app支持的格式\ue668
,作为app-plus>titleNView>buttons>text
。
关于app-plus的说明可参考文档https://uniapp.dcloud.io/collocation/pages?id=app-plus。
pages.json如下:
{
"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"
}
]
}
}
显示:
可以看到,定义出了搜索框和发帖按钮,点击按钮会改变颜色。
uni-app 支持的通用 css 单位包括 px、rpx:
开发者可以通过设计稿基准宽度计算页面元素 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>
显示:
在不同尺寸的设备上,其大小会自动缩放。
如需直接使用样式和素材等文件,可以直接点击加QQ群
963624318 ,在群文件夹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>
显示:
显然,实现了头像、昵称、发布日期和关注按钮的展示。
现进一步实现列表项的第二部分,即发帖标题、点赞、评论和分享等。
先按照之前的方法在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>
显示:
可以看到,已经实现了一个列表项的基本内容。
虽然已经实现了列表,但是可以看到代码很冗余、有大量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>
显示:
显然,已经实现了列表渲染。
再实现封装到组件,项目下新建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>
可以达到与之前相同的效果。
全局分割线也是以组件的形式添加。 先在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>
显示:
可以看到,有比较明显的分割线效果。
现在进一步给列表组件添加动画特效。
因为有的帖子没有封面图,因此需要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>
显示:
可以看到,没有封面图片,也能正常显示。
此时再实现点击关注按钮、添加动画特效,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>
显示:
可以看到,点击关注按钮时,已实现了动画效果。
现进一步实现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;
}
显示:
显然,也实现了动画效果。
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>
显示:
可以看到,已经模拟出了接口操作。 现进一步实现关注功能,如下:
<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的消息传递。
显示:
显然,实现了正常的关注功能。
现完善顶踩功能接口。 顶踩有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时显示文本等。
显示:
显然,已经实现了顶踩的基本功能。
滚动选项卡采用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>
显示:
可以看到,已经实现了滚动。
现实现选项卡内容显示,如下:
<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 */
显示:
再绑定事件实现动态滚动和动画显示的效果,如下:
<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>
显示:
可以看到,已经实现了动态滑动。
现进一步实现点击选项卡,下面显示对应的列表,使用swiper
(滑块视图容器)实现,可以做轮播图和滑动列表,其常见属性和含义如下:
属性名 | 类型 | 默认值 | 含义 |
---|---|---|---|
indicator-dots | Boolean | false | 是否显示面板指示点 |
autoplay | Boolean | false | 是否自动切换 |
current | Number | 0 | 当前所在滑块的 index |
interval | Number | 5000 | 自动切换时间间隔 |
duration | Number | 500 | 滑动动画时长 |
具体可参考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>
显示:
可以看到,实现了滑块,并且滑块可以和选项卡实现联动同步。
现实现滑块列表示意,如下:
<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为单位)。
显示:
可以看到,实现了列表滑动。
现完善列表项,将之前实现的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>
显示:
可以看到,已经实现了动态切换和显示数据。
现实现上拉到底部加载数据,需要实现各个选项卡独立上拉加载。
先实现基本的下拉加载,如下:
<template>
<view>
<!-- 顶部选项卡 -->
<scroll-view scroll-x="true" class="scroll-row" :scroll-into-view="scrollInto" :scroll-with-animation="true" style="height: 100rpx;">
<view v-for="(item, index) in tabBars" :key="index" class="scroll-row-item px-3 py-2 font-md" :id="'tab'+index"
:class="tabIndex === index ? 'text-main font-lg font-weight-bold' : ''" @click="changeTab(index)">{{item.name}}</view>
</scroll-view>
<!-- 滑块 -->
<swiper :duration="150" :current="tabIndex" @change="onChangeTab" :style="'height: '+scrollH+'px;'">
<swiper-item v-for="(item, index) in newsList" :key="index">
<scroll-view scroll-y="true" :style="'height: '+scrollH+'px;'" @scrolltolower="loadMore(index)">
<!-- 列表 -->
<block v-for="(item2, index2) in item.list" :key="index2">
<!-- 列表组件 -->
<common-list :item="item2" :index="index2" @follow="follow" @doSupport="doSupport"></common-list>
<!-- 全局分割线 -->
<divider></divider>
</block>
<!-- 上拉加载 -->
<view class="flex align-center justify-center py-3">
<text class="font text-light-muted">{{item.loadmore}}</text>
</view>
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import commonList from '@/components/common/common-list.vue';
export default {
data() {
return {
newsList: [],
// 顶部选项卡
tabBars: [{
name: '关注'
},
{
name: '推荐'
},
{
name: '体育'
},
{
name: '热点'
},
{
name: '财经'
},
{
name: '娱乐'
},
{
name: '军事'
},
{
name: '历史'
},
{
name: '本地'
}
],
tabIndex: 0,
scrollInto: '',
// 列表高度
scrollH: 600
}
},
components: {
commonList
},
onLoad() {
uni.getSystemInfo({
success: function(res) {
console.log(res);
this.scrollH = res.windowHeight - uni.upx2px(100);
}
}),
// 根据选项生成列表
this.getData();
},
methods: {
// 关注
follow(e) {
console.log('Index followed');
console.log(e);
this.list[e].isFollow = true;
uni.showToast({
title: '关注' + this.list[e].username + '成功'
})
},
// 顶踩操作
doSupport(e) {
console.log(e);
// 获取当前列表项
let item = this.list[e.index];
let msg = e.type === 'support' ? '顶' : '踩';
// 之前未顶踩过
if (item.support.type === '') {
item.support[e.type + '_count']++;
}
// 之前已顶过并且现在的操作为踩,则顶-1、踩+1
else if (item.support.type === 'support' && e.type === 'unsupport') {
item.support.support_count--;
item.support.unsupport_count++;
}
// 之前已踩过并且现在的操作为顶,则踩-1、顶+1
else if (item.support.type === 'unsupport' && e.type === 'support') {
item.support.unsupport_count--;
item.support.support_count++;
}
item.support.type = e.type;
uni.showToast({
title: msg + '成功'
})
},
// 切换选项
changeTab(index) {
if (this.tabIndex === index) {
return;
}
this.tabIndex = index;
// 滚动到指定元素
this.scrollInto = 'tab' + index;
},
// 监听滑动
onChangeTab(e) {
console.log(e);
this.changeTab(e.detail.current);
},
// 获取数据
getData() {
var arr = [];
for (let i = 0; i < this.tabBars.length; i++) {
// 生成列表模板
let obj = {
// 3种状态:1.上拉加载更多;2.加载中...;3.没有更多了。
loadmore: "上拉加载更多",
list: [{
username: "Corley",
userpic: "/static/img/userpic/12.jpg",
newstime: "2021-01-24 上午11:30",
isFollow: false,
title: "uni-app入门教程",
titlepic: "/static/img/datapic/42.jpg",
support: {
type: "support", // 顶
support_count: 1,
unsupport_count: 2
},
comment_count: 2,
share_count: 2
},
{
username: "Brittany",
userpic: "/static/img/userpic/16.jpg",
newstime: "2021-01-24 下午14:00",
isFollow: false,
title: "商业数据分析从入门到入职",
support: {
type: "unsupport", // 踩
support_count: 2,
unsupport_count: 3
},
comment_count: 5,
share_count: 1
},
{
username: "Jessica",
userpic: "/static/img/userpic/7.jpg",
newstime: "2021-01-24 下午14:44",
isFollow: true,
title: "Django+Vue开发生鲜电商平台",
titlepic: "/static/img/datapic/11.jpg",
support: {
type: "", // 未操作
support_count: 2,
unsupport_count: 7
},
comment_count: 0,
share_count: 2
},
{
username: "Ashley",
userpic: "/static/img/userpic/20.jpg",
newstime: "2021-01-24 下午18:20",
isFollow: true,
title: "uni-app实战之社区交友APP",
titlepic: "/static/img/datapic/30.jpg",
support: {
type: "support",
support_count: 5,
unsupport_count: 1
},
comment_count: 3,
share_count: 0
}
]
}
arr.push(obj)
}
this.newsList = arr;
},
// 上拉加载更多
loadMore(index) {
this.newsList[index].loadmore = '加载中...';
}
}
}
</script>
<style>
</style>
显示:
可以看到,在下拉到底之前,消息为上拉加载更多
,到底后触发@scrolltolower
事件,变为加载中...
。
再实现模拟加载更多数据,如下:
<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
变量来操作数据,是因为指向了相同的地址,即同一个引用。
显示:
可以看到,模拟出了加载更多数据。
前面实现的上拉加载更多并没有进行判断,可以一直向下滑动加载更多,显然这是不合理的,因此需要进行判断。
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>
效果与之前相同。
先实现没有数据的情况,如下:
<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 */
显示:
可以看到,部分页面没有数据,显示的是图片和提示文字。
再实现封装为组件,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样式提取等,这都有利于项目的维护和扩展。