yymini-pop-list 1.0.0

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/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # yymini-pop-list
2
+ 小程序(抖音) 轻量级 底部列表弹出框 自定义组件,无需额外依赖,开箱即用。
3
+
4
+ ## 🌟 特性
5
+ - 原生小程序语法,兼容小程序(抖音)基础库
6
+ - 支持自定义标题、列表内容、背景图等
7
+ - 提供 确认/取消 回调,支持遮罩关闭
8
+ - 样式简洁美观,可自定义修改
9
+
10
+ ## 📦 安装
11
+ 在你的小程序项目根目录执行:
12
+
13
+ ```bash
14
+ npm install yymini-pop-list --save
15
+ or
16
+ yarn add yymini-pop-list
17
+
18
+ 然后在 开发者工具中 构建 NPM
19
+ ```
20
+
21
+ ## 🎨 引用
22
+ ```json
23
+ {
24
+ "usingComponents": {
25
+ "pop-list": "/miniprogram_npm/yymini-pop-list/pop-list"
26
+ }
27
+ }
28
+
29
+ ```
30
+ ## ▶️ use
31
+ ```ttml / wxml
32
+ <pop-list visible="{{true}}" title="这里是 title" dataList="[这里是列表数据]" bind:close="onClose" />
33
+ ```
34
+
35
+ ## 👀 示例效果
36
+ ![弹窗示例](https://raw.githubusercontent.com/AllenGe/yy/main/pop-image-list.png)
package/back.png ADDED
Binary file
package/bgSrc.png ADDED
Binary file
package/drow_kong2.png ADDED
Binary file
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "yymini-pop-list",
3
+ "version": "1.0.0",
4
+ "description": "小程序(抖音)-底部列表弹出框 自定义组件",
5
+ "main": "pop-list.js",
6
+ "miniprogram": ".",
7
+ "keywords": [
8
+ "douyin",
9
+ "miniProgram",
10
+ "抖音小程序",
11
+ "confirm",
12
+ "modal",
13
+ "popup",
14
+ "弹出框",
15
+ "底部弹出框",
16
+ "列表"
17
+ ],
18
+ "author": "Allen.ge <gyjshow@163.com>",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "",
22
+ "url": ""
23
+ },
24
+ "bugs": {
25
+ "url": ""
26
+ },
27
+ "homepage": "",
28
+ "private": false,
29
+ "files": [
30
+ "*.png",
31
+ "pop-list.*",
32
+ "README.md"
33
+ ],
34
+ "scripts": {
35
+ "test": "echo \"Error: no test specified\" && exit 1"
36
+ }
37
+ }
package/pop-list.js ADDED
@@ -0,0 +1,222 @@
1
+ Component({
2
+ properties: {
3
+ // 是否显示弹窗
4
+ visible: {
5
+ type: Boolean,
6
+ value: false,
7
+ observer(newVal, oldVal) {
8
+ if (newVal !== oldVal) {
9
+ // 弹窗显示时自动获取数据
10
+ if (newVal) {
11
+ this.getBtnAreaInfo();
12
+ }
13
+ }
14
+ }
15
+ },
16
+ zindex: {
17
+ type: Number,
18
+ value: 999
19
+ }, //遮罩层弹出框的 层级
20
+
21
+ selector: {
22
+ type: String,
23
+ value: '.btn-area'
24
+ }, // 节点选择器(自定义要获取的元素类名)
25
+ isBackLeft: {
26
+ type: Boolean,
27
+ value: true
28
+ }, //是否显示返回按钮
29
+ title: {
30
+ type: String,
31
+ value: null
32
+ }, //弹出框标题
33
+ height: {
34
+ type: Number,
35
+ value: 85
36
+ }, // 弹窗高度 百分比(默认85%)
37
+ btnText: {
38
+ type: String,
39
+ value: '取消'
40
+ }, //取消按钮文本
41
+ maskClosable: {
42
+ type: Boolean,
43
+ value: true
44
+ }, //点击遮罩层是否关闭弹出框
45
+ bgSrc: {
46
+ type: String,
47
+ value: "bgSrc.png"
48
+ }, // 背景图片地址
49
+ dataList: {
50
+ type: Array,
51
+ value: []
52
+ }, // 列表数据源 dataList
53
+ },
54
+
55
+ data: {
56
+ animationData: {},
57
+ animating: false,
58
+ loading: false,
59
+ paramList: [],
60
+
61
+ // 下拉刷新状态
62
+ refreshing: false,
63
+ // 上拉加载状态
64
+ loadingMore: false, // 加载中
65
+ noMore: false, // 无更多数据
66
+ pageNum: 1, // 当前页码
67
+ pageSize: 4, // 每页条数(静态数据分页用)
68
+ },
69
+
70
+ methods: {
71
+
72
+ // 获取节点信息(组件内部自治)
73
+ getBtnAreaInfo() {
74
+ if (this.data.loading) return;
75
+ this.setData({
76
+ loading: true
77
+ }); // 先临时关闭加载(避免卡住)
78
+
79
+ console.log('getBtnAreaInfo 触发了!');
80
+ setTimeout(() => {
81
+ this.setData({
82
+ loading: false,
83
+ paramList: [{
84
+ key: 'width',
85
+ value: '100rpx'
86
+ },
87
+ {
88
+ key: 'height',
89
+ value: '88rpx'
90
+ },
91
+ {
92
+ key: 'top',
93
+ value: '200rpx'
94
+ },
95
+ {
96
+ key: 'bottom',
97
+ value: '288rpx'
98
+ },
99
+ {
100
+ key: 'left',
101
+ value: '50rpx'
102
+ },
103
+ {
104
+ key: 'right',
105
+ value: '150rpx'
106
+ },
107
+ {
108
+ key: 'scrollLeft',
109
+ value: '0rpx'
110
+ },
111
+ {
112
+ key: 'scrollTop',
113
+ value: '0rpx'
114
+ }
115
+ ]
116
+ }, () => {
117
+ console.log('getBtnAreaInfo 触发了!111111111111 ');
118
+ this.setData({
119
+ loading: false
120
+ });
121
+ });
122
+ }, 1000);
123
+
124
+ },
125
+
126
+ // 遮罩点击:触发关闭
127
+ onMaskClick() {
128
+ if (this.data.maskCloseable) {
129
+ this.triggerEvent('close', {
130
+ type: 'mask'
131
+ }); // 向父组件传事件
132
+ }
133
+ },
134
+
135
+ // 关闭弹窗:通知页面
136
+ onClose() {
137
+ this.triggerEvent('close', {
138
+ type: 'close'
139
+ });
140
+ },
141
+
142
+ // 阻止弹窗主体点击穿透到遮罩
143
+ stopPropagation() {},
144
+
145
+ // 下拉刷新处理方法
146
+ onRefresherRefresh() {
147
+ // 1. 显示刷新状态
148
+ this.setData({
149
+ refreshing: true
150
+ });
151
+ // 2. 模拟接口请求(延迟1s,实际替换为真实请求)
152
+ setTimeout(() => {
153
+ // 3. 重置页码,重新加载第一页数据
154
+ const list = [{
155
+ key: 'width',
156
+ value: '100rpx'
157
+ },
158
+ {
159
+ key: 'height',
160
+ value: '88rpx'
161
+ },
162
+ {
163
+ key: 'top',
164
+ value: '200rpx'
165
+ },
166
+ // { key: 'bottom', value: '288rpx' },
167
+ // { key: 'left', value: '50rpx' },
168
+ // { key: 'right', value: '150rpx' },
169
+ // { key: 'scrollLeft', value: '0rpx' },
170
+ // { key: 'scrollTop', value: '0rpx' }
171
+ ]
172
+
173
+ this.setData({
174
+ pageNum: 1,
175
+ paramList: list,
176
+ refreshing: false,
177
+ noMore: false
178
+ });
179
+
180
+ }, 2000);
181
+ },
182
+
183
+ // 上拉加载处理方法
184
+ onScrollToLower() {
185
+ console.log('onScrollToLower 滚动到底了。。。')
186
+ // 避免重复加载:加载中/无更多数据时不触发
187
+ if (this.data.loadingMore || this.data.noMore) return;
188
+ // 1. 显示加载中状态
189
+
190
+ const num = Math.floor(Math.random() * 100) + 1;
191
+
192
+ this.setData({
193
+ loadingMore: true
194
+ });
195
+ setTimeout(() => {
196
+
197
+ const list = [{
198
+ key: 'width',
199
+ value: num + 'rpx'
200
+ },
201
+ {
202
+ key: 'height',
203
+ value: num + 'rpx'
204
+ }
205
+ ]
206
+ const newList = [...this.data.paramList, ...list]
207
+
208
+ this.setData({
209
+ paramList: newList,
210
+ pageNum: this.data.pageNum + 1,
211
+ loadingMore: false,
212
+ noMore: newList.length > 10 ? true : false
213
+ });
214
+
215
+ }, 2000);
216
+ },
217
+
218
+ },
219
+
220
+ // 组件初始化
221
+ attached() {}
222
+ });
package/pop-list.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "component": true,
3
+ "usingComponents": {}
4
+ }
package/pop-list.ttml ADDED
@@ -0,0 +1,59 @@
1
+ <view class="view-mask {{visible ? 'show' : ''}}" style="z-index: {{zindex}};" bindtap="onMaskClick">
2
+ <view class="view-content" style="height: {{height}}%;" catchtap="stopPropagation">
3
+ <image src="{{bgSrc}}" class="img-bg"></image>
4
+ <view class="view-box">
5
+ <view class="view-top">
6
+ <button tt:if="{{isBackLeft}}" class="btn-left" bindtap="onClose">
7
+ <image src="back.png" mode="widthFix"></image>
8
+ </button>
9
+ <view tt:else class="btn-left"></view>
10
+ <view class="toast-title">{{title}}</view>
11
+ <view class="btn-left"></view>
12
+ </view>
13
+
14
+ <!-- 加载中提示 -->
15
+ <view tt:if="{{loading}}" class="loading">
16
+ <view class="loading-circle"></view>
17
+ <text class="loading-text">加载中...</text>
18
+ </view>
19
+
20
+ <!-- 空页面 -->
21
+ <view tt:elif="{{dataList.length < 1 }}" class="view-kong">
22
+ <image src="drow_kong2.png" class="drow_kong_box"></image>
23
+ </view>
24
+
25
+ <!-- 数据加载完成后显示 -->
26
+ <view tt:else class="info-wrap">
27
+ <scroll-view scroll-y="true" class="info-scroll" refresher-enabled="true" enhanced="true" bounces="true"
28
+ refresher-background="#FFFFFF00" refresher-triggered="{{refreshing}}"
29
+ bindrefresherrefresh="onRefresherRefresh" bindscrolltolower="onScrollToLower">
30
+ <view class="param-item" tt:for="{{dataList}}" tt:key="key" tt:index="index">
31
+ <view class="param-key"
32
+ style="{{item.key=='height'?'background-color: #666;' : 'background-color: #999;'}}">
33
+ {{index}}- {{item.key}}
34
+ </view>
35
+ <view class="param-value">{{item.value}}</view>
36
+ </view>
37
+ <!-- <view class="pullup-loading" tt:if="{{loadingMore}}">
38
+ <view class="dot"></view>
39
+ <view class="dot"></view>
40
+ <view class="dot"></view>
41
+ </view> -->
42
+ <view tt:if="{{loadingMore}}" class="loading-more">
43
+ <view class="loading-circle"></view>
44
+ <text class="loading-text">正在加载...</text>
45
+ </view>
46
+ <view tt:elif="{{noMore}}" class="loading-no">
47
+ <view class="loading-line"></view>
48
+ <view class="loading-dot"></view>
49
+ <view class="loading-line"></view>
50
+ </view>
51
+ </scroll-view>
52
+ </view>
53
+
54
+ <view tt:if="{{!loading && btnText}}" class="cancel-button" bindtap="onClose">
55
+ {{btnText}}
56
+ </view>
57
+ </view>
58
+ </view>
59
+ </view>
package/pop-list.ttss ADDED
@@ -0,0 +1,339 @@
1
+ /* 重置 button 本身的边框和外轮廓 */
2
+ button {
3
+ border: none !important;
4
+ /* 去掉默认边框 */
5
+ outline: none !important;
6
+ /* 去掉点击外轮廓(可选) */
7
+ background: transparent;
8
+ /* 可选:去掉默认背景色 */
9
+ border-radius: 0;
10
+ /* 可选:去掉默认圆角 */
11
+ }
12
+
13
+ /* 关键:去掉 button 伪元素 ::after 的边框(抖音小程序默认通过这个渲染边框) */
14
+ button::after {
15
+ border: none !important;
16
+ }
17
+
18
+ /* 弹窗遮罩层 */
19
+ .view-mask {
20
+ position: fixed;
21
+ top: 0;
22
+ left: 0;
23
+ right: 0;
24
+ bottom: 0;
25
+ display: flex;
26
+ flex: 1;
27
+ flex-direction: column;
28
+ background: rgba(0, 0, 0, 0.45);
29
+
30
+ opacity: 0;
31
+ pointer-events: none;
32
+ transition: opacity 0.45s ease;
33
+ z-index: 9999;
34
+
35
+ }
36
+
37
+ .view-mask.show {
38
+ opacity: 1;
39
+ pointer-events: auto;
40
+ }
41
+
42
+
43
+ .img-bg {
44
+ width: 100%;
45
+ height: 100%;
46
+ }
47
+
48
+ /* 弹窗内容区 */
49
+ .view-content {
50
+ position: fixed;
51
+ bottom: 0;
52
+ left: 0;
53
+ right: 0;
54
+
55
+ border-top-left-radius: 40rpx;
56
+ border-top-right-radius: 40rpx;
57
+
58
+ display: flex;
59
+ flex-direction: column;
60
+ overflow: hidden;
61
+
62
+ background: red;
63
+ /* 初始状态:滑出屏幕底部 */
64
+ transform: translateY(100%);
65
+ transition: transform 0.45s ease-out;
66
+ /* 滑动动画更丝滑 */
67
+ height: 50%;
68
+
69
+ }
70
+
71
+ .view-mask.show .view-content {
72
+ transform: translateY(0);
73
+ }
74
+
75
+ .view-box {
76
+ position: absolute;
77
+ left: 0;
78
+ top: 0;
79
+ right: 0;
80
+ bottom: 0;
81
+ display: flex;
82
+ flex-direction: column;
83
+ }
84
+
85
+ .view-top {
86
+ height: 120rpx;
87
+ display: flex;
88
+ flex-direction: row;
89
+ /* justify-content: center; */
90
+ align-items: center;
91
+ /* background-color: #00000080; */
92
+ }
93
+
94
+ .btn-left {
95
+ /* background-color: #FFF; */
96
+ /* background-color: #0088ff; */
97
+ display: flex;
98
+ height: 120rpx;
99
+ width: 96rpx;
100
+ justify-content: center;
101
+ align-items: center;
102
+ }
103
+
104
+ .btn-left:active {
105
+ box-shadow: inset 0 0 2rpx rgba(0, 0, 0, 0.05);
106
+ opacity: 0.9;
107
+ }
108
+
109
+ .img-left {
110
+ width: 48rpx;
111
+ height: 48rpx;
112
+ /* background-color: #000; */
113
+ }
114
+
115
+
116
+ /* 标题样式 */
117
+ .toast-title {
118
+ font-weight: bold;
119
+ font-size: 32rpx;
120
+ text-align: center;
121
+ height: 120rpx;
122
+ display: flex;
123
+ flex: 1;
124
+ justify-content: center;
125
+ align-items: center;
126
+ /* background-color: #FFF; */
127
+ }
128
+
129
+ .view-kong {
130
+ flex: 1;
131
+ display: flex;
132
+ flex-direction: column;
133
+ justify-content: center;
134
+ align-items: center;
135
+ }
136
+
137
+ .drow_kong_box {
138
+ width: 400rpx;
139
+ height: 271rpx;
140
+ }
141
+
142
+ /* 取消按钮 */
143
+ .cancel-button {
144
+ height: 100rpx;
145
+ width: 100%;
146
+ display: flex;
147
+ justify-content: center;
148
+ align-items: center;
149
+ background: #FFF;
150
+ border-top: 1rpx solid #e8e8e8;
151
+ font-size: 32rpx;
152
+ color: #333;
153
+ padding-bottom: 30rpx;
154
+ }
155
+
156
+ /* 组件 showAllInfo.ttss - 新增/修改样式 */
157
+ .info-wrap {
158
+ display: flex;
159
+ flex: 1;
160
+ padding: 1rpx 40rpx;
161
+ overflow: hidden;
162
+ /* background-color: #f7f7f7; */
163
+
164
+ }
165
+
166
+ .info-scroll {
167
+ width: 100%;
168
+ /* height: 100%; */
169
+ /* background-color: #0088ff; */
170
+ flex: 1;
171
+ /* padding-bottom: 30rpx; */
172
+ }
173
+
174
+
175
+ /* 一行两个参数的容器:横向排列 */
176
+ .param-item {
177
+ display: flex;
178
+ width: 100%;
179
+ /* border-bottom: 20rpx solid #FFFFFF00; */
180
+ /* 可选:加分割线,更清晰 */
181
+ border-radius: 10rpx;
182
+ overflow: hidden;
183
+ margin-bottom: 20rpx;
184
+ }
185
+
186
+ /* 第一个参数(参数名)样式 */
187
+ .param-key {
188
+ flex: 1;
189
+ padding: 30rpx;
190
+ font-size: 26rpx;
191
+ color: #fff;
192
+ text-align: center;
193
+ }
194
+
195
+ /* 第二个参数(参数值)样式 */
196
+ .param-value {
197
+ flex: 1;
198
+ padding: 30rpx;
199
+ font-size: 26rpx;
200
+ color: #333;
201
+ text-align: center;
202
+ background-color: #ccc;
203
+ }
204
+
205
+
206
+ /* 3. 参数项强制颜色,避免和背景色一致 */
207
+ .params {
208
+ padding: 30rpx;
209
+ font-size: 26rpx;
210
+ color: #000 !important;
211
+ /* 强制黑色,避免白色看不见 */
212
+ /* background-color: #999; */
213
+ /* 临时加背景色 */
214
+ }
215
+
216
+
217
+ /* 加载中样式 */
218
+ .loading {
219
+ flex: 1;
220
+ display: flex;
221
+ justify-content: center;
222
+ /* align-items: center; */
223
+ padding-top: 25%;
224
+ font-size: 28rpx;
225
+ color: #666;
226
+ /* background-color: #f7f7f7; */
227
+ gap: 20rpx;
228
+ }
229
+
230
+ .loading-more {
231
+ display: flex;
232
+ height: 100rpx;
233
+ justify-content: center;
234
+ padding-top: 10rpx;
235
+ /* align-items: center; */
236
+ font-size: 24rpx;
237
+ color: #666;
238
+ /* background-color: red; */
239
+ gap: 20rpx;
240
+ }
241
+
242
+ .loading-no {
243
+ display: flex;
244
+ height: 100rpx;
245
+ justify-content: center;
246
+ padding-top: 10rpx;
247
+ /* align-items: center; */
248
+ gap: 20rpx;
249
+
250
+ }
251
+
252
+ .loading-line {
253
+ width: 60rpx;
254
+ height: 1rpx;
255
+ background-color: #999;
256
+ }
257
+
258
+ .loading-dot {
259
+ width: 10rpx;
260
+ height: 10rpx;
261
+ border-radius: 50%;
262
+ background-color: #999;
263
+ }
264
+
265
+ /* 转圈 loading 圆圈样式 */
266
+ .loading-circle {
267
+ width: 28rpx;
268
+ height: 28rpx;
269
+ border: 4rpx solid #e0e0e0;
270
+ /* 灰色边框(底色) */
271
+ border-top-color: #666;
272
+ /* 顶部蓝色(动效色) */
273
+ border-radius: 50%;
274
+ /* 圆形 */
275
+ animation: loading-rotate 1s linear infinite;
276
+ /* 旋转动画 */
277
+ }
278
+
279
+ /* 旋转动画关键帧 */
280
+ @keyframes loading-rotate {
281
+ 0% {
282
+ transform: rotate(0deg);
283
+ }
284
+
285
+ 100% {
286
+ transform: rotate(360deg);
287
+ }
288
+ }
289
+
290
+ /* Loading 文字样式(可选优化) */
291
+ .loading-text {
292
+ font-size: 24rpx;
293
+ color: #666;
294
+ letter-spacing: 2rpx;
295
+ }
296
+
297
+
298
+ /* 上拉加载3个点容器 */
299
+ .pullup-loading {
300
+ height: 80rpx;
301
+ display: flex;
302
+ justify-content: center;
303
+ align-items: center;
304
+ gap: 12rpx;
305
+ /* 点之间的间距 */
306
+ /* background-color: #f7f7f7; */
307
+ }
308
+
309
+ /* 单个点样式 */
310
+ .pullup-loading .dot {
311
+ width: 14rpx;
312
+ height: 14rpx;
313
+ border-radius: 50%;
314
+ background-color: #999;
315
+ animation: dot-flash 1.4s ease-in-out infinite both;
316
+ }
317
+
318
+ /* 3个点依次闪烁(和原生下拉动画一致) */
319
+ .pullup-loading .dot:nth-child(1) {
320
+ animation-delay: -0.32s;
321
+ }
322
+
323
+ .pullup-loading .dot:nth-child(2) {
324
+ animation-delay: -0.16s;
325
+ }
326
+
327
+ /* 闪烁动画关键帧 */
328
+ @keyframes dot-flash {
329
+
330
+ 0%,
331
+ 100%,
332
+ 80% {
333
+ opacity: 0.2;
334
+ }
335
+
336
+ 40% {
337
+ opacity: 1;
338
+ }
339
+ }