uview-plus 3.4.66 → 3.4.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.md
CHANGED
|
@@ -106,6 +106,11 @@ export const props = defineMixin({
|
|
|
106
106
|
type: String,
|
|
107
107
|
default: () => defProps.numberBox.bgColor
|
|
108
108
|
},
|
|
109
|
+
// 按钮禁用背景色
|
|
110
|
+
disabledBgColor: {
|
|
111
|
+
type: String,
|
|
112
|
+
default: () => defProps.numberBox.disabledBgColor
|
|
113
|
+
},
|
|
109
114
|
// 输入框背景颜色
|
|
110
115
|
inputBgColor: {
|
|
111
116
|
type: String,
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
class="u-pull-refresh"
|
|
4
|
+
@touchstart="onTouchStart"
|
|
5
|
+
@touchmove="onTouchMove"
|
|
6
|
+
@touchend="onTouchEnd"
|
|
7
|
+
@touchcancel="onTouchEnd"
|
|
8
|
+
>
|
|
9
|
+
<!-- 下拉刷新区域 -->
|
|
10
|
+
<view
|
|
11
|
+
class="refresh-area"
|
|
12
|
+
:style="{ height: refreshDistance + 'px' }"
|
|
13
|
+
:class="{ refreshing: isRefreshing }"
|
|
14
|
+
>
|
|
15
|
+
<!-- 不同状态的插槽 -->
|
|
16
|
+
<slot
|
|
17
|
+
v-if="refreshStatus === 'pull'"
|
|
18
|
+
name="pull"
|
|
19
|
+
:distance="refreshDistance"
|
|
20
|
+
:threshold="threshold"
|
|
21
|
+
>
|
|
22
|
+
<!-- 默认下拉状态 -->
|
|
23
|
+
<view class="refresh-content">
|
|
24
|
+
<view class="refresh-indicator">
|
|
25
|
+
<view class="arrow"></view>
|
|
26
|
+
</view>
|
|
27
|
+
<text class="refresh-text">下拉刷新</text>
|
|
28
|
+
</view>
|
|
29
|
+
</slot>
|
|
30
|
+
|
|
31
|
+
<slot
|
|
32
|
+
v-else-if="refreshStatus === 'release'"
|
|
33
|
+
name="release"
|
|
34
|
+
:distance="refreshDistance"
|
|
35
|
+
:threshold="threshold"
|
|
36
|
+
>
|
|
37
|
+
<!-- 默认释放状态 -->
|
|
38
|
+
<view class="refresh-content">
|
|
39
|
+
<view class="refresh-indicator">
|
|
40
|
+
<view class="arrow rotate"></view>
|
|
41
|
+
</view>
|
|
42
|
+
<text class="refresh-text">释放刷新</text>
|
|
43
|
+
</view>
|
|
44
|
+
</slot>
|
|
45
|
+
|
|
46
|
+
<slot
|
|
47
|
+
v-else-if="refreshStatus === 'refreshing'"
|
|
48
|
+
name="refreshing"
|
|
49
|
+
>
|
|
50
|
+
<!-- 默认刷新中状态 -->
|
|
51
|
+
<view class="refresh-content">
|
|
52
|
+
<view class="refresh-indicator">
|
|
53
|
+
<view class="spinner"></view>
|
|
54
|
+
</view>
|
|
55
|
+
<text class="refresh-text">正在刷新...</text>
|
|
56
|
+
</view>
|
|
57
|
+
</slot>
|
|
58
|
+
</view>
|
|
59
|
+
|
|
60
|
+
<!-- 内容区域 -->
|
|
61
|
+
<view
|
|
62
|
+
class="refresh-content-wrapper"
|
|
63
|
+
:style="{ transform: `translateY(${contentTranslateY}px)` }"
|
|
64
|
+
>
|
|
65
|
+
<slot></slot>
|
|
66
|
+
</view>
|
|
67
|
+
</view>
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<script>
|
|
71
|
+
export default {
|
|
72
|
+
name: 'u-pull-refresh',
|
|
73
|
+
props: {
|
|
74
|
+
// 是否正在刷新
|
|
75
|
+
refreshing: {
|
|
76
|
+
type: Boolean,
|
|
77
|
+
default: false
|
|
78
|
+
},
|
|
79
|
+
// 下拉刷新阈值
|
|
80
|
+
threshold: {
|
|
81
|
+
type: Number,
|
|
82
|
+
default: 50
|
|
83
|
+
},
|
|
84
|
+
// 阻尼系数
|
|
85
|
+
damping: {
|
|
86
|
+
type: Number,
|
|
87
|
+
default: 0.4
|
|
88
|
+
},
|
|
89
|
+
// 最大下拉距离
|
|
90
|
+
maxDistance: {
|
|
91
|
+
type: Number,
|
|
92
|
+
default: 100
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
data() {
|
|
96
|
+
return {
|
|
97
|
+
// 下拉刷新相关
|
|
98
|
+
isRefreshing: false,
|
|
99
|
+
refreshStatus: 'pull', // pull, release, refreshing
|
|
100
|
+
refreshDistance: 0,
|
|
101
|
+
startY: 0,
|
|
102
|
+
currentY: 0,
|
|
103
|
+
touching: false,
|
|
104
|
+
|
|
105
|
+
// 动画相关
|
|
106
|
+
contentTranslateY: 0
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
watch: {
|
|
110
|
+
refreshing: {
|
|
111
|
+
handler(newVal) {
|
|
112
|
+
if (!newVal) {
|
|
113
|
+
this.finishRefresh()
|
|
114
|
+
} else {
|
|
115
|
+
this.startRefresh()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
methods: {
|
|
121
|
+
// 触摸开始
|
|
122
|
+
onTouchStart(e) {
|
|
123
|
+
if (this.isRefreshing) return
|
|
124
|
+
|
|
125
|
+
this.touching = true
|
|
126
|
+
this.startY = e.touches[0].pageY
|
|
127
|
+
this.currentY = this.startY
|
|
128
|
+
this.refreshStatus = 'pull'
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// 触摸移动
|
|
132
|
+
onTouchMove(e) {
|
|
133
|
+
if (!this.touching || this.isRefreshing) return
|
|
134
|
+
|
|
135
|
+
this.currentY = e.touches[0].pageY
|
|
136
|
+
const diff = this.currentY - this.startY
|
|
137
|
+
|
|
138
|
+
// 只有在顶部且下拉时才触发下拉刷新
|
|
139
|
+
if (diff > 0) {
|
|
140
|
+
this.refreshDistance = Math.min(diff * this.damping, this.maxDistance)
|
|
141
|
+
this.contentTranslateY = this.refreshDistance
|
|
142
|
+
|
|
143
|
+
// 更新状态
|
|
144
|
+
if (this.refreshDistance >= this.threshold) {
|
|
145
|
+
this.refreshStatus = 'release'
|
|
146
|
+
} else {
|
|
147
|
+
this.refreshStatus = 'pull'
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// 触摸结束
|
|
153
|
+
onTouchEnd() {
|
|
154
|
+
if (!this.touching) return
|
|
155
|
+
|
|
156
|
+
this.touching = false
|
|
157
|
+
|
|
158
|
+
if (this.refreshDistance >= this.threshold && !this.isRefreshing) {
|
|
159
|
+
// 触发刷新
|
|
160
|
+
this.startRefresh()
|
|
161
|
+
this.$emit('refresh')
|
|
162
|
+
} else {
|
|
163
|
+
// 回弹
|
|
164
|
+
this.resetRefresh()
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// 开始刷新
|
|
169
|
+
startRefresh() {
|
|
170
|
+
this.isRefreshing = true
|
|
171
|
+
this.refreshStatus = 'refreshing'
|
|
172
|
+
this.refreshDistance = this.threshold
|
|
173
|
+
this.contentTranslateY = this.threshold
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
// 完成刷新
|
|
177
|
+
finishRefresh() {
|
|
178
|
+
this.isRefreshing = false
|
|
179
|
+
this.refreshStatus = 'pull'
|
|
180
|
+
this.resetRefresh()
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
// 重置刷新状态
|
|
184
|
+
resetRefresh() {
|
|
185
|
+
this.refreshDistance = 0
|
|
186
|
+
this.contentTranslateY = 0
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
<style scoped lang="scss">
|
|
193
|
+
.u-pull-refresh {
|
|
194
|
+
position: relative;
|
|
195
|
+
height: 100%;
|
|
196
|
+
overflow: hidden;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.refresh-area {
|
|
200
|
+
position: absolute;
|
|
201
|
+
left: 0;
|
|
202
|
+
right: 0;
|
|
203
|
+
top: 0;
|
|
204
|
+
display: flex;
|
|
205
|
+
align-items: flex-end;
|
|
206
|
+
justify-content: center;
|
|
207
|
+
overflow: hidden;
|
|
208
|
+
transition: height 0.2s ease-out;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.refresh-content-wrapper {
|
|
212
|
+
height: 100%;
|
|
213
|
+
transition: transform 0.2s ease-out;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.refresh-content {
|
|
217
|
+
display: flex;
|
|
218
|
+
flex-direction: column;
|
|
219
|
+
align-items: center;
|
|
220
|
+
justify-content: center;
|
|
221
|
+
width: 100%;
|
|
222
|
+
padding-bottom: 10px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.refresh-indicator {
|
|
226
|
+
width: 24px;
|
|
227
|
+
height: 24px;
|
|
228
|
+
display: flex;
|
|
229
|
+
align-items: center;
|
|
230
|
+
justify-content: center;
|
|
231
|
+
margin-bottom: 8px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.arrow {
|
|
235
|
+
width: 12px;
|
|
236
|
+
height: 12px;
|
|
237
|
+
border-left: 2px solid #666;
|
|
238
|
+
border-bottom: 2px solid #666;
|
|
239
|
+
transform: rotate(-45deg);
|
|
240
|
+
transition: transform 0.2s;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.arrow.rotate {
|
|
244
|
+
transform: rotate(135deg);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.spinner {
|
|
248
|
+
width: 16px;
|
|
249
|
+
height: 16px;
|
|
250
|
+
border: 2px solid #f3f3f3;
|
|
251
|
+
border-top: 2px solid #666;
|
|
252
|
+
border-radius: 50%;
|
|
253
|
+
animation: spin 1s linear infinite;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@keyframes spin {
|
|
257
|
+
0% { transform: rotate(0deg); }
|
|
258
|
+
100% { transform: rotate(360deg); }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.refresh-text {
|
|
262
|
+
font-size: 14px;
|
|
263
|
+
color: #666;
|
|
264
|
+
}
|
|
265
|
+
</style>
|
package/package.json
CHANGED