@hlw-uni/mp-vue 1.1.11 → 1.1.13
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/package.json
CHANGED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view v-if="visible" class="hlw-sheet-root" :class="{ show: shown }">
|
|
3
|
+
<view class="hlw-sheet-overlay" @tap="handleMaskTap" />
|
|
4
|
+
<view class="hlw-sheet-panel" :style="{ height: maxHeight, maxHeight }" @tap.stop>
|
|
5
|
+
<view class="hlw-sheet-header">
|
|
6
|
+
<view v-if="showHandle" class="hlw-sheet-handle" />
|
|
7
|
+
<text v-if="title" class="hlw-sheet-title">{{ title }}</text>
|
|
8
|
+
<view v-if="showClose" class="hlw-sheet-close" @tap="handleClose">
|
|
9
|
+
<text class="i-fa6-solid-xmark" />
|
|
10
|
+
</view>
|
|
11
|
+
</view>
|
|
12
|
+
|
|
13
|
+
<scroll-view class="hlw-sheet-body" scroll-y :show-scrollbar="false">
|
|
14
|
+
<view class="hlw-sheet-body-inner">
|
|
15
|
+
<slot />
|
|
16
|
+
<view v-if="showCta" class="hlw-sheet-cta" @tap="handleCta">
|
|
17
|
+
<text class="hlw-sheet-cta-text">{{ ctaText }}</text>
|
|
18
|
+
</view>
|
|
19
|
+
<slot name="footer" />
|
|
20
|
+
</view>
|
|
21
|
+
</scroll-view>
|
|
22
|
+
</view>
|
|
23
|
+
</view>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
/**
|
|
28
|
+
* HlwSheet — 通用底部弹窗(Bottom Sheet)
|
|
29
|
+
*
|
|
30
|
+
* 单根模板 + position:fixed 覆盖视口,内部 overlay 与 panel 使用 absolute 定位。
|
|
31
|
+
* 自管入场/出场动画时序(16ms 入场,300ms 出场),父组件只需 v-model 控制显隐。
|
|
32
|
+
*
|
|
33
|
+
* @props
|
|
34
|
+
* modelValue - 是否显示(v-model)
|
|
35
|
+
* title - 标题文字
|
|
36
|
+
* showHandle - 是否显示顶部拖拽条,默认 true
|
|
37
|
+
* showClose - 是否显示关闭按钮,默认 true
|
|
38
|
+
* closeOnMask - 点击遮罩是否关闭,默认 true
|
|
39
|
+
* showCta - 是否显示底部主按钮,默认 false
|
|
40
|
+
* ctaText - 底部按钮文字,默认 "确定"
|
|
41
|
+
* maxHeight - 最大高度,默认 "85vh"
|
|
42
|
+
*
|
|
43
|
+
* @events
|
|
44
|
+
* update:modelValue - v-model 更新
|
|
45
|
+
* close - 关闭触发
|
|
46
|
+
* cta - 点击底部按钮
|
|
47
|
+
*
|
|
48
|
+
* @slots
|
|
49
|
+
* default - 主内容(自动在 scroll-view 中滚动)
|
|
50
|
+
* footer - 自定义底部区(紧跟 CTA 按钮之后)
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```vue
|
|
54
|
+
* <hlw-sheet v-model="visible" title="标题" show-cta cta-text="确定" @cta="handleOk">
|
|
55
|
+
* <view>内容区</view>
|
|
56
|
+
* </hlw-sheet>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
import { ref, watch, onBeforeUnmount } from "vue";
|
|
60
|
+
|
|
61
|
+
interface Props {
|
|
62
|
+
modelValue: boolean;
|
|
63
|
+
title?: string;
|
|
64
|
+
showHandle?: boolean;
|
|
65
|
+
showClose?: boolean;
|
|
66
|
+
closeOnMask?: boolean;
|
|
67
|
+
showCta?: boolean;
|
|
68
|
+
ctaText?: string;
|
|
69
|
+
maxHeight?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
73
|
+
modelValue: false,
|
|
74
|
+
title: "",
|
|
75
|
+
showHandle: true,
|
|
76
|
+
showClose: true,
|
|
77
|
+
closeOnMask: true,
|
|
78
|
+
showCta: false,
|
|
79
|
+
ctaText: "确定",
|
|
80
|
+
maxHeight: "85vh",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const emit = defineEmits<{
|
|
84
|
+
(e: "update:modelValue", value: boolean): void;
|
|
85
|
+
(e: "close"): void;
|
|
86
|
+
(e: "cta"): void;
|
|
87
|
+
}>();
|
|
88
|
+
|
|
89
|
+
const visible = ref(false);
|
|
90
|
+
const shown = ref(false);
|
|
91
|
+
|
|
92
|
+
let closeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
93
|
+
let openTimer: ReturnType<typeof setTimeout> | null = null;
|
|
94
|
+
|
|
95
|
+
const clearTimers = () => {
|
|
96
|
+
if (closeTimer) {
|
|
97
|
+
clearTimeout(closeTimer);
|
|
98
|
+
closeTimer = null;
|
|
99
|
+
}
|
|
100
|
+
if (openTimer) {
|
|
101
|
+
clearTimeout(openTimer);
|
|
102
|
+
openTimer = null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
watch(
|
|
107
|
+
() => props.modelValue,
|
|
108
|
+
(val) => {
|
|
109
|
+
clearTimers();
|
|
110
|
+
if (val) {
|
|
111
|
+
visible.value = true;
|
|
112
|
+
openTimer = setTimeout(() => {
|
|
113
|
+
shown.value = true;
|
|
114
|
+
}, 16);
|
|
115
|
+
} else if (visible.value) {
|
|
116
|
+
shown.value = false;
|
|
117
|
+
closeTimer = setTimeout(() => {
|
|
118
|
+
visible.value = false;
|
|
119
|
+
}, 300);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{ immediate: true }
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const handleClose = () => {
|
|
126
|
+
emit("update:modelValue", false);
|
|
127
|
+
emit("close");
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const handleMaskTap = () => {
|
|
131
|
+
if (props.closeOnMask) handleClose();
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const handleCta = () => {
|
|
135
|
+
emit("cta");
|
|
136
|
+
emit("update:modelValue", false);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
onBeforeUnmount(() => {
|
|
140
|
+
clearTimers();
|
|
141
|
+
});
|
|
142
|
+
</script>
|
|
143
|
+
|
|
144
|
+
<style lang="scss" scoped>
|
|
145
|
+
.hlw-sheet-root {
|
|
146
|
+
position: fixed;
|
|
147
|
+
top: 0;
|
|
148
|
+
left: 0;
|
|
149
|
+
right: 0;
|
|
150
|
+
bottom: 0;
|
|
151
|
+
z-index: 1000;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.hlw-sheet-overlay {
|
|
155
|
+
position: absolute;
|
|
156
|
+
top: 0;
|
|
157
|
+
left: 0;
|
|
158
|
+
right: 0;
|
|
159
|
+
bottom: 0;
|
|
160
|
+
background: rgba(0, 0, 0, 0.55);
|
|
161
|
+
opacity: 0;
|
|
162
|
+
transition: opacity 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.hlw-sheet-root.show .hlw-sheet-overlay {
|
|
166
|
+
opacity: 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.hlw-sheet-panel {
|
|
170
|
+
position: absolute;
|
|
171
|
+
left: 0;
|
|
172
|
+
right: 0;
|
|
173
|
+
bottom: 0;
|
|
174
|
+
background: #ffffff;
|
|
175
|
+
border-radius: var(--radius-xl, 32rpx) var(--radius-xl, 32rpx) 0 0;
|
|
176
|
+
display: flex;
|
|
177
|
+
flex-direction: column;
|
|
178
|
+
overflow: hidden;
|
|
179
|
+
transform: translateY(100%);
|
|
180
|
+
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
181
|
+
box-shadow: 0 -12rpx 48rpx rgba(15, 23, 42, 0.15);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.hlw-sheet-root.show .hlw-sheet-panel {
|
|
185
|
+
transform: translateY(0);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.hlw-sheet-header {
|
|
189
|
+
position: relative;
|
|
190
|
+
display: flex;
|
|
191
|
+
flex-direction: column;
|
|
192
|
+
align-items: center;
|
|
193
|
+
padding: 20rpx 0 24rpx;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.hlw-sheet-handle {
|
|
197
|
+
width: 72rpx;
|
|
198
|
+
height: 8rpx;
|
|
199
|
+
border-radius: 999rpx;
|
|
200
|
+
background: var(--border-color, #e2e8f0);
|
|
201
|
+
margin-bottom: 20rpx;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.hlw-sheet-title {
|
|
205
|
+
font-size: var(--font-md, 32rpx);
|
|
206
|
+
font-weight: 700;
|
|
207
|
+
color: #0f172a;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.hlw-sheet-close {
|
|
211
|
+
position: absolute;
|
|
212
|
+
right: 24rpx;
|
|
213
|
+
top: 24rpx;
|
|
214
|
+
width: 56rpx;
|
|
215
|
+
height: 56rpx;
|
|
216
|
+
border-radius: 50%;
|
|
217
|
+
background: var(--border-color-light, #f1f5f9);
|
|
218
|
+
color: #64748b;
|
|
219
|
+
display: flex;
|
|
220
|
+
align-items: center;
|
|
221
|
+
justify-content: center;
|
|
222
|
+
font-size: var(--font-sm, 24rpx);
|
|
223
|
+
transition: background 0.2s ease;
|
|
224
|
+
|
|
225
|
+
&:active {
|
|
226
|
+
background: var(--border-color, #e2e8f0);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.hlw-sheet-body {
|
|
231
|
+
flex: 1 1 auto;
|
|
232
|
+
min-height: 0;
|
|
233
|
+
width: 100%;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.hlw-sheet-body-inner {
|
|
237
|
+
padding: 16rpx 36rpx 48rpx;
|
|
238
|
+
display: flex;
|
|
239
|
+
flex-direction: column;
|
|
240
|
+
gap: 32rpx;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.hlw-sheet-cta {
|
|
244
|
+
padding: 28rpx;
|
|
245
|
+
background: #0f172a;
|
|
246
|
+
border-radius: var(--radius-md, 16rpx);
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
justify-content: center;
|
|
250
|
+
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.2);
|
|
251
|
+
transition: transform 0.15s ease;
|
|
252
|
+
|
|
253
|
+
&:active {
|
|
254
|
+
transform: scale(0.98);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.hlw-sheet-cta-text {
|
|
259
|
+
font-size: var(--font-base, 28rpx);
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
color: #ffffff;
|
|
262
|
+
letter-spacing: 1rpx;
|
|
263
|
+
}
|
|
264
|
+
</style>
|