swiper
# 左右滑动导航
# (1)使用:
<template>
<view>
<bodying :isSafeBottom="false">
<view slot="header">
<nav>头部</nav>
<navTab
ref="navTab"
:tabTitle="tabTitle"
@changeTab='changeTab'
></navTab>
</view>
<div slot="content" class="content">
<swiper :current="currentTab" @change="swiperTab">
<swiper-item>
<refresh @onLoadmore="lower1" pageName="userList0" :isRefresh="false" :customRefresh="true">
<p v-for="(item,index) in userList0.list" style="font-size: 18px; margin-bottom: 300px;">{{index+1}}:内容: {{item.orderId}}</p>
</refresh>
</swiper-item>
<swiper-item>
<refresh @onLoadmore="lower1" pageName="userList1" :isRefresh="false" :customRefresh="true">
<p v-for="(item,index) in userList1.list" style="font-size: 18px; margin-bottom: 300px;">{{index+1}}:内容: {{item.orderId}}</p>
</refresh>
</swiper-item>
</swiper>
</div>
<view slot="bottom" class="box-bottom">
<div class="buy">购买</div>
</view>
</bodying>
</view>
</template>
<script>
import Refresh from '@/common/js/refresh.js';
export default {
data () {
return {
tabTitle : ['王者123','荣耀--------'],
currentTab: 0, //sweiper所在页
userList0 : new Refresh(this, {
apiName : 'getUserInfoList',
apiData : {
searchType : 'receive'
}
}),
userList1 : new Refresh(this, {
apiName : 'getUserInfoList',
apiData : {
searchType : 'orders'
}
})
}
},
onLoad () {
this.userList0.onRefresh();
this.userList1.onRefresh();
},
onPullDownRefresh() {
this[`userList${this.currentTab}`].onRefresh();
setTimeout(function () {
uni.stopPullDownRefresh();
}, 2000);
},
methods : {
changeTab(index){
this.currentTab = index;
},
// swiper 滑动
swiperTab (e) {
var index = e.detail.current //获取索引
this.$refs.navTab.longClick(index);
},
lower1 () {
// console.log('1111111111111');
this[`userList${this.currentTab}`].onLoadmore()
}
}
}
</script>
<style lang="scss" scoped>
nav {
height:44px;
background-color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
font-weight: bolder;
}
.content {
height: 100%;
swiper {
height: 100%;
}
}
.box-bottom {
padding:10rpx 40rpx;
box-shadow: 0px -30rpx 20rpx rgba(0, 0, 0, 0.05);
.buy {
padding:24rpx 0;
font-size: 28rpx;
color:#FFFFFF;
display: flex;
align-items: center;
justify-content: center;
background: #FF6E01;
border-radius: 44rpx;
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# (2)nav组件:
<template>
<view class="navTabBox">
<view class="longTab" :style="{background:navBG}">
<scroll-view scroll-x="true" style="white-space: nowrap; display: flex" scroll-with-animation :scroll-left="tabLeft">
<!-- title -->
<view
@click="longClick(index)"
class="longItem"
v-for="(item,index) in tabTitle"
:style="{
width : isWidth + 'rpx',
color : index == tabClick ? activeColor : noActiveColor,
fontSize : titleSize + 'rpx'
}"
:data-index="index"
:key="index"
:id="'id'+index"
>
{{item}}
</view>
<!-- line -->
<view class="underlineBox"
:style="{transform : 'translateX('+isLeft+'rpx)', width : isWidth + 'rpx'}">
<view class="underline" :style="{background : lineBg, width : lineWidth + 'rpx'}"></view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
name: 'navTab',
props: {
navBG : {
type : String,
default : '#808080'
},
// 传过来的nav数据
tabTitle: {
type: Array,
default: []
},
// 是否nav平均分(最多5个)
isAverage : {
type: Boolean,
default : true
},
// 当isAverage为false时,记得传nav宽度,单位为rpx
navWidth : {
type : Number,
default : 100
},
// 未选中的color
noActiveColor : {
type : String,
default : '#FFFFFF80',
},
// 选中的color
activeColor : {
type : String,
default : '#FFFFFF',
},
// 字体大小
titleSize : {
type : Number,
default: 24
},
// 横线的宽度
lineWidth : {
type : Number,
default : 32
},
// 横线的颜色
lineBg : {
type : String,
default : '#FFFFFF',
}
},
data() {
return {
tabClick: 0, //导航栏被点击
isLeft: 0, //导航栏下划线位置
isWidth: 0, //每个导航栏占位
tabLeft:0 //tab栏滚动距离
};
},
created() {
let that = this;
if (this.isAverage) {
// 获取设备宽度
uni.getSystemInfo({
success(e) {
if(that.tabTitle.length<= 5 ){
//宽度除以导航标题个数=一个导航所占宽度
that.isWidth = e.windowWidth / that.tabTitle.length
} else {
that.isWidth = e.windowWidth / 5
}
}
})
//转换为rpx
that.isWidth = that.isWidth * 2;
} else {
that.isWidth = that.navWidth;
}
},
methods: {
// 导航栏点击
longClick(index){
if(this.tabTitle.length>5){
//当tab栏大于5时需要滚动的距离(第四个才滚动)
//因为tabLeft单位为px,所以除以2
this.tabLeft = (index-2) * this.isWidth / 2;
}
//设置导航点击了哪一个
this.tabClick = index;
//设置下划线位置
this.isLeft = index * this.isWidth;
//设置swiper的第几页
this.$emit('changeTab', index);
// this.$parent.currentTab = index //设置swiper的第几页
}
}
}
</script>
<style lang="scss">
.navTabBox {
width: 100vw;
.longTab {
width: 100%;
.longItem{
height:96rpx;
display: inline-block;
line-height: 96rpx;
text-align: center;
}
.underlineBox {
display: flex;
align-content: center;
justify-content: center;
transition: .5s;
.underline {
height: 8rpx;
border-radius: 4rpx;
}
}
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**需要vw的地方全部代码*/
<template>
<view class="navTabBox">
<view class="longTab" :style="{background:navBG}">
<scroll-view scroll-x="true" style="white-space: nowrap; display: flex" scroll-with-animation :scroll-left="tabLeft">
<!-- title -->
<view
@click="longClick(index)"
class="longItem"
v-for="(item,index) in tabTitle"
:style="{
width : isWidth + 'rpx',
color : index == tabClick ? activeColor : noActiveColor,
fontWeight : index == tabClick ? '600' : '',
fontSize : titleSize + 'vw'
}"
:data-index="index"
:key="index"
:id="'id'+index"
>
{{item}}
</view>
<!-- line -->
<view class="underlineBox"
:style="{transform : 'translateX('+isLeft+'rpx)', width : isWidth + 'rpx'}">
<view class="underline" :style="{background : lineBg, width : lineWidth + 'vw'}"></view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
name: 'navTab',
props: {
navBG : {
type : String,
default : '#FFFFFF'
},
// 传过来的nav数据
tabTitle: {
type: Array,
default: []
},
// 是否nav平均分(最多5个)
isAverage : {
type: Boolean,
default : true
},
// 当isAverage为false时,记得传nav宽度,单位为rpx
navWidth : {
type : Number,
default : 100
},
// 未选中的color
noActiveColor : {
type : String,
default : '#A9ABB3',
},
// 选中的color
activeColor : {
type : String,
default : '#000000',
},
// 字体大小
titleSize : {
type : Number,
default: 3.2
},
// 横线的宽度
lineWidth : {
type : Number,
default : 21.33
},
// 横线的颜色
lineBg : {
type : String,
default : '#CA332D',
}
},
data() {
return {
tabClick: 0, //导航栏被点击
isLeft: 0, //导航栏下划线位置
isWidth: 0, //每个导航栏占位
tabLeft:0 //tab栏滚动距离
};
},
created() {
let that = this;
if (this.isAverage) {
// 获取设备宽度
uni.getSystemInfo({
success(e) {
if(that.tabTitle.length<= 5 ){
//宽度除以导航标题个数=一个导航所占宽度
that.isWidth = e.windowWidth / that.tabTitle.length
} else {
that.isWidth = e.windowWidth / 5
}
}
})
//转换为rpx
that.isWidth = that.isWidth * 2;
} else {
that.isWidth = that.navWidth;
}
},
methods: {
// 导航栏点击
longClick(index){
if(this.tabTitle.length>5){
//当tab栏大于5时需要滚动的距离(第四个才滚动)
//因为tabLeft单位为px,所以除以2
this.tabLeft = (index-2) * this.isWidth / 2;
}
//设置导航点击了哪一个
this.tabClick = index;
//设置下划线位置
this.isLeft = index * this.isWidth;
//设置swiper的第几页
this.$emit('changeTab', index);
// this.$parent.currentTab = index //设置swiper的第几页
}
}
}
</script>
<style lang="scss">
.navTabBox {
width: 100vw;
.longTab {
width: 100%;
border-bottom: 1px solid #F8F8F8;
.longItem{
height:10.14vw;
display: inline-block;
line-height: 10.14vw;
text-align: center;
}
.underlineBox {
display: flex;
align-content: center;
justify-content: center;
transition: .5s;
.underline {
height: 0.53vw;
border-radius: 2vw;
}
}
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# (3)body 组件
<template>
<view class="body">
<!-- 顶部安全距离 -->
<view :style="{height:isSafeTop ? iStatusBarTop + 'px' : '', background:safeTopBG}"></view>
<slot name="header"></slot>
<view class="body-content">
<slot name="content"></slot>
</view>
<slot name="bottom"></slot>
<!-- 底部安全距离 -->
<view :style="{height:isSafeBottom ? iStatusBarBottom + 'px' : '', background:safeBottomBG}"></view>
</view>
</template>
<script>
export default {
props : {
isSafeTop : { //是否需要头部安全距离
type : Boolean,
default : true,
},
safeTopBG : { //头部安全距离颜色
type : String,
default : '#FFFFFF'
},
isSafeBottom : { //是否需要底部的安全距离
type : Boolean,
default : true,
},
safeBottomBG : { //底部安全距离颜色
type : String,
default : 'transparent'
},
},
data () {
return {
iStatusBarTop : 0,
iStatusBarBottom : 0
}
},
mounted () {
this.iStatusBarTop = uni.getSystemInfoSync().statusBarHeight;
this.iStatusBarBottom = uni.getSystemInfoSync().safeAreaInsets.bottom;
console.log(this.iStatusBarTop);
}
}
</script>
<style lang="scss" scoped>
.body {
height:100vh;
width: 100vw;
box-sizing: border-box;
display: flex;
flex-direction: column;
position: relative;
.body-content {
position: relative;
background: #FAFAFA;
flex:1;
// overflow: auto;
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# (4)refresh组件
<template>
<view>
<scroll-view
class="swiper-item-content"
scroll-y="true"
@scrolltolower="lower1"
:refresher-threshold="120"
:refresher-enabled="isRefresh"
lower-threshold="40"
>
<slot></slot>
<u-loadmore :status="refresh.status" icon-type="flower" :load-text="loadText" />
<!-- <div class="more" v-if="refresh.status == 'loadmore'">加载更多</div>
<div class="more" v-if="refresh.status == 'loading'">加载中<image src="@/static/loading.gif"></image>...</div>
<div class="more" v-if="refresh.status == 'nomore'">没有数据了</div> -->
<div class="safeBottom"></div>
</scroll-view>
</view>
</template>
<script>
import util from '@/common/js/util.js';
export default {
props : {
//每个刷新组件的名字,一个页面可能会有多个刷新组件,例如三个分页的那种格式
pageName: {
type: String,
default: 'page'
},
//是否调用自定义刷新函数
customRefresh: {
type: Boolean,
default: false
},
// 是否下拉刷新
isRefresh : {
type : Boolean,
default : true
}
},
data () {
return {
currentTab : null,
refresh : {},
loadText: {
loadmore: '轻轻上拉',
loading: '努力加载中',
nomore: '实在没有了'
}
}
},
mounted () {
this.refresh = this.getRefresh();
},
methods : {
lower1: util.throttle(async function(e) {
console.log('加载更多。。。。。。。。。。。。。');
if (this.customRefresh) this.$emit('onLoadmore');
else await this.refresh.onLoadmore();
}, 300),
getRefresh () {
var that = this;
for (let i = 0; i < 8; i++) {
console.log(i);
if (that[this.pageName]) return that[this.pageName];
that = that.$parent;
if (!that) return {}
}
return {};
}
}
}
</script>
<style lang="scss">
.swiper-item-content {
position: absolute;
top:0;
bottom:0;
}
.more {
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 50px;
image {
width: 20px;
height: 20px;
background-color: transparent;
}
}
.safeBottom{
padding-bottom: calc(1.33vw + env(safe-area-inset-bottom));
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# (5)工具类:refresh.js
function Refresh (vm, { apiName, apiData, list }) {
/** 接口名字*/
this.apiName = apiName;
/** 接口参数*/
this.apiData = apiData ? apiData : {};
/** 列表数据*/
this.list = list ? list : [];
/** 加载更多的状态 ,加载前值为loadmore,加载中为loading,没有数据为nomore*/
this.status = 'loadmore';
/** 分页参数*/
this.pageAO = {
pageNum: 1,
pageSize: 4
}
/** 请求数据*/
this.onRequest = async () => {
this.status = 'loading';
let res = vm.$u.api[this.apiName]({
...this.pageAO,
...this.apiData
});
return res;
}
/** 下拉刷新*/
this.onRefresh = async () => {
if (this.status == 'loading') return;
this.status = 'loading';
this.pageAO.pageNum = 1;
try{
let res = await this.onRequest();
this.list = res.records;
// 当请求的数据长度小于返回的 pageSize 时,终止
if (this.list.length < res.size) {
this.status = 'nomore';
return uni.showToast({
icon : 'none',
title : '没有更多数据了'
})
} else {
this.status = 'loadmore';
this.pageAO.pageNum++;
}
}catch(e){
//TODO handle the exception
this.status = 'loadmore';
return uni.showToast({
icon : 'none',
title : '11网络错误'
})
}
}
/** 上拉加载*/
this.onLoadmore = async () => {
if (this.status == 'loading' || this.status == 'nomore') return;
this.status = 'loading';
try{
let res = await this.onRequest();
if (this.list.length === res.total) {
this.status = 'nomore';
console.log(this.status);
return uni.showToast({
icon : 'none',
title : '没有更多数据了'
})
}
if (res.records.length === res.size) this.pageAO.pageNum++;
if(res.records.length == 0) this.status = 'nomore';
else this.status = 'loadmore';
this.list.push(...res.records);
}catch(e){
this.status = 'loadmore';
//TODO handle the exception
return uni.showToast({
icon : 'none',
title : '网络错误'
})
}
}
}
export default Refresh;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# (6)工具类:util.js
//节流
function throttle(fn, gapTime) {
if (gapTime == null || gapTime == undefined) {
gapTime = 1500
}
let _lastTime = null
// 返回新的函数
return function () {
let _nowTime = + new Date()
if (_nowTime - _lastTime > gapTime || !_lastTime) {
fn.apply(this, arguments) //将this和参数传给原函数
_lastTime = _nowTime
}
}
}
export default{
throttle
}
// module.exports = {
// throttle:throttle,
// vuemixin:{
// created: function () { console.log(1) }
// }
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
上次更新: 5/13/2023, 5:48:10 PM