muzhiyu-ui 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.
Files changed (96) hide show
  1. package/README.md +202 -0
  2. package/components/muzhiyu/mu-action-sheet/mu-action-sheet.vue +335 -0
  3. package/components/muzhiyu/mu-alert/mu-alert.vue +220 -0
  4. package/components/muzhiyu/mu-back-top/mu-back-top.vue +161 -0
  5. package/components/muzhiyu/mu-badge/mu-badge.vue +150 -0
  6. package/components/muzhiyu/mu-barcode/mu-barcode.vue +100 -0
  7. package/components/muzhiyu/mu-blank/mu-blank.vue +30 -0
  8. package/components/muzhiyu/mu-bottom-nav/mu-bottom-nav.vue +331 -0
  9. package/components/muzhiyu/mu-bubble/mu-bubble.vue +316 -0
  10. package/components/muzhiyu/mu-button/mu-button.vue +431 -0
  11. package/components/muzhiyu/mu-calendar/mu-calendar.vue +429 -0
  12. package/components/muzhiyu/mu-car-input/mu-car-input.vue +209 -0
  13. package/components/muzhiyu/mu-card/mu-card.vue +317 -0
  14. package/components/muzhiyu/mu-cascader/mu-cascader.vue +336 -0
  15. package/components/muzhiyu/mu-cell/mu-cell.vue +368 -0
  16. package/components/muzhiyu/mu-checkbox/mu-checkbox.vue +116 -0
  17. package/components/muzhiyu/mu-checkbox-group/mu-checkbox-group.vue +40 -0
  18. package/components/muzhiyu/mu-code-input/mu-code-input.vue +264 -0
  19. package/components/muzhiyu/mu-collapse/mu-collapse.vue +316 -0
  20. package/components/muzhiyu/mu-color-picker/mu-color-picker.vue +466 -0
  21. package/components/muzhiyu/mu-config/index.js +34 -0
  22. package/components/muzhiyu/mu-config/mu-config.vue +54 -0
  23. package/components/muzhiyu/mu-countdown/mu-countdown.vue +338 -0
  24. package/components/muzhiyu/mu-cropper/mu-cropper.vue +415 -0
  25. package/components/muzhiyu/mu-date-picker/mu-date-picker.vue +290 -0
  26. package/components/muzhiyu/mu-datetime/mu-datetime.vue +262 -0
  27. package/components/muzhiyu/mu-divider/mu-divider.vue +229 -0
  28. package/components/muzhiyu/mu-drawer/mu-drawer.vue +172 -0
  29. package/components/muzhiyu/mu-dropdown/mu-dropdown.vue +215 -0
  30. package/components/muzhiyu/mu-dropdown-top/mu-dropdown-top.vue +243 -0
  31. package/components/muzhiyu/mu-empty/mu-empty.vue +217 -0
  32. package/components/muzhiyu/mu-fab/mu-fab.vue +324 -0
  33. package/components/muzhiyu/mu-footer/mu-footer.vue +21 -0
  34. package/components/muzhiyu/mu-form/mu-form.vue +170 -0
  35. package/components/muzhiyu/mu-grid/mu-grid.vue +95 -0
  36. package/components/muzhiyu/mu-grid-item/mu-grid-item.vue +247 -0
  37. package/components/muzhiyu/mu-html/mu-html.vue +145 -0
  38. package/components/muzhiyu/mu-icon/mu-icon.vue +822 -0
  39. package/components/muzhiyu/mu-image-grid/mu-image-grid.vue +225 -0
  40. package/components/muzhiyu/mu-image-preview/mu-image-preview.vue +286 -0
  41. package/components/muzhiyu/mu-index-list/mu-index-list.vue +381 -0
  42. package/components/muzhiyu/mu-input/mu-input.vue +570 -0
  43. package/components/muzhiyu/mu-input-number/mu-input-number.vue +193 -0
  44. package/components/muzhiyu/mu-keyboard/mu-keyboard.vue +415 -0
  45. package/components/muzhiyu/mu-list/mu-list.vue +89 -0
  46. package/components/muzhiyu/mu-loading/mu-loading.vue +124 -0
  47. package/components/muzhiyu/mu-loading-page/mu-loading-page.vue +529 -0
  48. package/components/muzhiyu/mu-loadmore/mu-loadmore.vue +26 -0
  49. package/components/muzhiyu/mu-modal/mu-modal.vue +298 -0
  50. package/components/muzhiyu/mu-navbar/mu-navbar.vue +223 -0
  51. package/components/muzhiyu/mu-nomore/mu-nomore.vue +107 -0
  52. package/components/muzhiyu/mu-notice-bar/mu-notice-bar.vue +284 -0
  53. package/components/muzhiyu/mu-notify/mu-notify.vue +202 -0
  54. package/components/muzhiyu/mu-overlay/mu-overlay.vue +94 -0
  55. package/components/muzhiyu/mu-pagination/mu-pagination.vue +330 -0
  56. package/components/muzhiyu/mu-password-input/mu-password-input.vue +119 -0
  57. package/components/muzhiyu/mu-picker/mu-picker.vue +297 -0
  58. package/components/muzhiyu/mu-popup/mu-popup.vue +297 -0
  59. package/components/muzhiyu/mu-progress/mu-progress.vue +156 -0
  60. package/components/muzhiyu/mu-pull-refresh/mu-pull-refresh.vue +54 -0
  61. package/components/muzhiyu/mu-qrcode/mu-qrcode.vue +340 -0
  62. package/components/muzhiyu/mu-qrcode/qrcode.js +2237 -0
  63. package/components/muzhiyu/mu-radio/mu-radio.vue +125 -0
  64. package/components/muzhiyu/mu-radio-group/mu-radio-group.vue +37 -0
  65. package/components/muzhiyu/mu-rate/mu-rate.vue +103 -0
  66. package/components/muzhiyu/mu-search/mu-search.vue +320 -0
  67. package/components/muzhiyu/mu-section/mu-section.vue +206 -0
  68. package/components/muzhiyu/mu-select/mu-select.vue +440 -0
  69. package/components/muzhiyu/mu-share-sheet/mu-share-sheet.vue +269 -0
  70. package/components/muzhiyu/mu-sidebar/mu-sidebar.vue +304 -0
  71. package/components/muzhiyu/mu-signature/mu-signature.vue +449 -0
  72. package/components/muzhiyu/mu-skeleton/mu-skeleton.vue +225 -0
  73. package/components/muzhiyu/mu-slider/mu-slider.vue +228 -0
  74. package/components/muzhiyu/mu-space/mu-space.vue +84 -0
  75. package/components/muzhiyu/mu-steps/mu-steps.vue +311 -0
  76. package/components/muzhiyu/mu-sticky/mu-sticky.vue +57 -0
  77. package/components/muzhiyu/mu-swipe-cell/mu-swipe-cell.vue +281 -0
  78. package/components/muzhiyu/mu-swiper/mu-swiper.vue +107 -0
  79. package/components/muzhiyu/mu-switch/mu-switch.vue +99 -0
  80. package/components/muzhiyu/mu-tabbar/mu-tabbar.vue +382 -0
  81. package/components/muzhiyu/mu-table/mu-table.vue +420 -0
  82. package/components/muzhiyu/mu-tabs/mu-tabs.vue +339 -0
  83. package/components/muzhiyu/mu-tag/mu-tag.vue +65 -0
  84. package/components/muzhiyu/mu-text-ellipsis/mu-text-ellipsis.vue +54 -0
  85. package/components/muzhiyu/mu-timeline/mu-timeline.vue +166 -0
  86. package/components/muzhiyu/mu-tips/mu-tips.vue +36 -0
  87. package/components/muzhiyu/mu-toast/mu-toast.vue +151 -0
  88. package/components/muzhiyu/mu-transfer/mu-transfer.vue +114 -0
  89. package/components/muzhiyu/mu-tree/mu-tree.vue +263 -0
  90. package/components/muzhiyu/mu-upload/mu-upload.vue +415 -0
  91. package/components/muzhiyu/mu-waterfall/mu-waterfall.vue +321 -0
  92. package/components/muzhiyu/mu-watermark/mu-watermark.vue +60 -0
  93. package/index.js +83 -0
  94. package/package.json +38 -0
  95. package/utils/index.js +600 -0
  96. package/utils/request.js +265 -0
@@ -0,0 +1,225 @@
1
+ <template>
2
+ <view
3
+ class="mu-image-grid"
4
+ :class="[
5
+ mode === 'scroll' && 'mu-image-grid--scroll'
6
+ ]"
7
+ :style="gridContainerStyle"
8
+ >
9
+ <view
10
+ v-for="(img, index) in displayImages"
11
+ :key="index"
12
+ class="mu-image-grid__item mu-touchable-flat"
13
+ :style="getItemStyle(index)"
14
+ @tap="handlePreview(index)"
15
+ >
16
+ <image
17
+ class="mu-image-grid__img"
18
+ :src="getImageUrl(img)"
19
+ :mode="getImageMode(index)"
20
+ />
21
+
22
+ <!-- 超出最大数量 +N 暗化遮罩 -->
23
+ <view
24
+ v-if="index === max - 1 && remainingCount > 0"
25
+ class="mu-image-grid__more-overlay"
26
+ >
27
+ <text class="mu-image-grid__more-text">+{{ remainingCount }}</text>
28
+ </view>
29
+
30
+ <!-- 可选的右顶部删除图标 (deletable) -->
31
+ <view
32
+ v-if="deletable"
33
+ class="mu-image-grid__delete-btn"
34
+ @tap.stop="handleDelete(index)"
35
+ >
36
+ <mu-icon name="close" :size="12" color="#FFFFFF" />
37
+ </view>
38
+ </view>
39
+ </view>
40
+ </template>
41
+
42
+ <script setup>
43
+ import { computed } from 'vue'
44
+
45
+ const props = defineProps({
46
+ images: { type: Array, default: () => [] }, // 图片列表 (字符串数组或 [{url, desc}])
47
+ max: { type: Number, default: 9 }, // 最多显示张数
48
+ columns: { type: [Number, String], default: 0 }, // 强制指定列数 (0 为自动朋友圈 1/2/3 列智能自适应)
49
+ gap: { type: [Number, String], default: 12 }, // 图片间距 (rpx)
50
+ radius: { type: [Number, String], default: 16 }, // 图片圆角 (rpx)
51
+ mode: { type: String, default: 'grid' }, // grid (网格) | scroll (横向滚动)
52
+ preview: { type: Boolean, default: true }, // 点击是否自动大图预览
53
+ deletable: { type: Boolean, default: false } // 是否显示删除按钮
54
+ })
55
+
56
+ const emit = defineEmits(['click', 'preview', 'delete'])
57
+
58
+ const normalizedImages = computed(() => props.images || [])
59
+
60
+ const displayImages = computed(() => {
61
+ return normalizedImages.value.slice(0, props.max)
62
+ })
63
+
64
+ const remainingCount = computed(() => {
65
+ return normalizedImages.value.length - props.max
66
+ })
67
+
68
+ // 计算自适应网格列数 (微信朋友圈黄金规则)
69
+ const currentColumns = computed(() => {
70
+ const forcedCols = Number(props.columns)
71
+ if (forcedCols > 0) return forcedCols
72
+
73
+ const count = displayImages.value.length
74
+ if (count === 1) return 1
75
+ if (count === 2 || count === 4) return 2
76
+ return 3
77
+ })
78
+
79
+ const gridContainerStyle = computed(() => {
80
+ const g = Number(props.gap)
81
+ if (props.mode === 'scroll') {
82
+ return { gap: `${g}rpx` }
83
+ }
84
+ const cols = currentColumns.value
85
+ return {
86
+ display: 'grid',
87
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
88
+ gap: `${g}rpx`
89
+ }
90
+ })
91
+
92
+ function getImageUrl(item) {
93
+ if (typeof item === 'string') return item
94
+ if (typeof item === 'object' && item.url) return item.url
95
+ return ''
96
+ }
97
+
98
+ function getImageMode(index) {
99
+ if (currentColumns.value === 1) return 'aspectFill'
100
+ return 'aspectFill'
101
+ }
102
+
103
+ function getItemStyle(index) {
104
+ const r = typeof props.radius === 'number' ? `${props.radius}rpx` : props.radius
105
+ const style = { borderRadius: r }
106
+
107
+ // 单张图智能高大图
108
+ if (currentColumns.value === 1) {
109
+ style.width = '380rpx'
110
+ style.height = '380rpx'
111
+ } else if (props.mode === 'scroll') {
112
+ style.width = '200rpx'
113
+ style.height = '200rpx'
114
+ style.flexShrink = '0'
115
+ } else {
116
+ style.width = '100%'
117
+ style.paddingBottom = '100%' // 维持 1:1 正方形网格比例
118
+ }
119
+ return style
120
+ }
121
+
122
+ function handlePreview(index) {
123
+ const rawList = normalizedImages.value.map(item => getImageUrl(item))
124
+ emit('click', { index, src: rawList[index] })
125
+
126
+ if (props.preview) {
127
+ uni.previewImage({
128
+ urls: rawList,
129
+ current: index
130
+ })
131
+ }
132
+ emit('preview', { index, src: rawList[index] })
133
+ }
134
+
135
+ function handleDelete(index) {
136
+ emit('delete', { index, item: normalizedImages.value[index] })
137
+ }
138
+ </script>
139
+
140
+ <style lang="scss" scoped>
141
+ @import "@/uni.scss";
142
+
143
+ .mu-image-grid {
144
+ position: relative;
145
+ width: 100%;
146
+ box-sizing: border-box;
147
+
148
+ &--scroll {
149
+ display: flex;
150
+ flex-wrap: nowrap;
151
+ overflow-x: auto;
152
+ -webkit-overflow-scrolling: touch;
153
+ }
154
+
155
+ &__item {
156
+ position: relative;
157
+ overflow: hidden;
158
+ cursor: pointer;
159
+ background: #F1F5F9;
160
+ box-sizing: border-box;
161
+ }
162
+
163
+ &__img {
164
+ position: absolute;
165
+ top: 0;
166
+ left: 0;
167
+ width: 100%;
168
+ height: 100%;
169
+ display: block;
170
+ object-fit: cover;
171
+ }
172
+
173
+ &__more-overlay {
174
+ position: absolute;
175
+ inset: 0;
176
+ background: rgba(15, 23, 42, 0.55);
177
+ backdrop-filter: blur(4px);
178
+ display: flex;
179
+ align-items: center;
180
+ justify-content: center;
181
+ z-index: 2;
182
+
183
+ &-text {
184
+ color: #ffffff;
185
+ font-size: 36rpx;
186
+ font-weight: 800;
187
+ letter-spacing: 1rpx;
188
+ }
189
+ }
190
+
191
+ &__delete-btn {
192
+ position: absolute;
193
+ top: 10rpx;
194
+ right: 10rpx;
195
+ width: 40rpx;
196
+ height: 40rpx;
197
+ border-radius: 50%;
198
+ background: rgba(15, 23, 42, 0.7);
199
+ backdrop-filter: blur(4px);
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: center;
203
+ z-index: 3;
204
+ cursor: pointer;
205
+ }
206
+ }
207
+
208
+ .mu-dark, page.mu-dark {
209
+
210
+ .mu-image-grid {
211
+ &__item {
212
+ background: rgba(255, 255, 255, 0.05);
213
+ }
214
+ }
215
+ }
216
+
217
+ @media (prefers-color-scheme: dark) {
218
+
219
+ .mu-image-grid {
220
+ &__item {
221
+ background: rgba(255, 255, 255, 0.05);
222
+ }
223
+ }
224
+ }
225
+ </style>
@@ -0,0 +1,286 @@
1
+ <template>
2
+ <view
3
+ v-if="visible"
4
+ class="mu-image-preview"
5
+ :class="[animating && 'mu-image-preview--show']"
6
+ @tap="handleMaskClick"
7
+ >
8
+ <!-- 顶部悬浮毛玻璃操作栏 -->
9
+ <view class="mu-image-preview__header" @tap.stop>
10
+ <!-- 极光数字页码胶囊 -->
11
+ <view class="mu-image-preview__page-badge">
12
+ <text class="mu-image-preview__page-text">{{ currentIndex + 1 }} / {{ normalizedImages.length }}</text>
13
+ </view>
14
+
15
+ <!-- 顶部右侧快捷按钮群 (保存 + 关闭) -->
16
+ <view class="mu-image-preview__header-actions">
17
+ <view v-if="showSave" class="mu-image-preview__icon-btn mu-touchable-flat" @tap="handleSaveImage">
18
+ <mu-icon name="download" :size="20" color="#FFFFFF" />
19
+ </view>
20
+ <view v-if="closeable" class="mu-image-preview__icon-btn mu-touchable-flat" @tap="close">
21
+ <mu-icon name="close" :size="20" color="#FFFFFF" />
22
+ </view>
23
+ </view>
24
+ </view>
25
+
26
+ <!-- 图片轮播 Swiper 视口 -->
27
+ <swiper
28
+ class="mu-image-preview__swiper"
29
+ :current="currentIndex"
30
+ :circular="loop"
31
+ :duration="300"
32
+ @change="onSwiperChange"
33
+ >
34
+ <swiper-item
35
+ v-for="(img, i) in normalizedImages"
36
+ :key="i"
37
+ class="mu-image-preview__swiper-item"
38
+ @tap="handleMaskClick"
39
+ >
40
+ <image
41
+ class="mu-image-preview__image"
42
+ :src="getImageUrl(img)"
43
+ mode="aspectFit"
44
+ :show-menu-by-longpress="true"
45
+ />
46
+ </swiper-item>
47
+ </swiper>
48
+
49
+ <!-- 底部描述信息栏 -->
50
+ <view
51
+ v-if="currentDescription"
52
+ class="mu-image-preview__footer"
53
+ @tap.stop
54
+ >
55
+ <text class="mu-image-preview__desc-text">{{ currentDescription }}</text>
56
+ </view>
57
+ </view>
58
+ </template>
59
+
60
+ <script setup>
61
+ import { ref, computed, watch } from 'vue'
62
+
63
+ const props = defineProps({
64
+ show: { type: Boolean, default: false },
65
+ images: { type: Array, default: () => [] }, // 图片数组 (支持字符串 URL 或对象 [{url, desc}])
66
+ current: { type: Number, default: 0 }, // 初始索引
67
+ loop: { type: Boolean, default: true }, // 是否循环轮播
68
+ closeable: { type: Boolean, default: true }, // 是否显示关闭按钮
69
+ closeOnClickMask: { type: Boolean, default: true }, // 点击遮罩是否关闭
70
+ showSave: { type: Boolean, default: true } // 是否显示保存图片按钮
71
+ })
72
+
73
+ const emit = defineEmits(['update:show', 'close', 'change', 'save'])
74
+
75
+ const visible = ref(false)
76
+ const animating = ref(false)
77
+ const currentIndex = ref(0)
78
+
79
+ const normalizedImages = computed(() => {
80
+ if (!props.images) return []
81
+ return props.images
82
+ })
83
+
84
+ const currentDescription = computed(() => {
85
+ const item = normalizedImages.value[currentIndex.value]
86
+ if (!item) return ''
87
+ if (typeof item === 'object' && item.desc) return item.desc
88
+ return ''
89
+ })
90
+
91
+ function getImageUrl(item) {
92
+ if (typeof item === 'string') return item
93
+ if (typeof item === 'object' && item.url) return item.url
94
+ return ''
95
+ }
96
+
97
+ function onSwiperChange(e) {
98
+ currentIndex.value = e.detail.current
99
+ emit('change', e.detail.current)
100
+ }
101
+
102
+ function handleMaskClick() {
103
+ if (props.closeOnClickMask) {
104
+ close()
105
+ }
106
+ }
107
+
108
+ function handleSaveImage() {
109
+ const currentUrl = getImageUrl(normalizedImages.value[currentIndex.value])
110
+ if (!currentUrl) return
111
+
112
+ emit('save', { url: currentUrl, index: currentIndex.value })
113
+
114
+ // #ifdef H5
115
+ try {
116
+ const a = document.createElement('a')
117
+ a.href = currentUrl
118
+ a.download = `preview_${Date.now()}.png`
119
+ document.body.appendChild(a)
120
+ a.click()
121
+ document.body.removeChild(a)
122
+ uni.showToast({ title: '已触发保存图片', icon: 'success' })
123
+ } catch (e) {
124
+ uni.showToast({ title: '长按图片保存到相册', icon: 'none' })
125
+ }
126
+ // #endif
127
+
128
+ // #ifndef H5
129
+ if (currentUrl.startsWith('http')) {
130
+ uni.downloadFile({
131
+ url: currentUrl,
132
+ success: (res) => {
133
+ if (res.statusCode === 200 && res.tempFilePath) {
134
+ uni.saveImageToPhotosAlbum({
135
+ filePath: res.tempFilePath,
136
+ success: () => uni.showToast({ title: '保存成功', icon: 'success' }),
137
+ fail: () => uni.showToast({ title: '保存失败或拒绝授权', icon: 'none' })
138
+ })
139
+ } else {
140
+ uni.showToast({ title: '图片下载失败', icon: 'none' })
141
+ }
142
+ },
143
+ fail: () => uni.showToast({ title: '图片下载失败', icon: 'none' })
144
+ })
145
+ } else {
146
+ uni.saveImageToPhotosAlbum({
147
+ filePath: currentUrl,
148
+ success: () => uni.showToast({ title: '保存成功', icon: 'success' }),
149
+ fail: () => uni.showToast({ title: '保存失败或拒绝授权', icon: 'none' })
150
+ })
151
+ }
152
+ // #endif
153
+ }
154
+
155
+ function open(index = 0) {
156
+ currentIndex.value = index
157
+ visible.value = true
158
+ setTimeout(() => {
159
+ animating.value = true
160
+ }, 30)
161
+ }
162
+
163
+ function close() {
164
+ animating.value = false
165
+ setTimeout(() => {
166
+ visible.value = false
167
+ emit('update:show', false)
168
+ emit('close')
169
+ }, 200)
170
+ }
171
+
172
+ watch(() => props.show, (val) => {
173
+ if (val) {
174
+ open(props.current)
175
+ } else {
176
+ close()
177
+ }
178
+ })
179
+
180
+ defineExpose({ open, close, handleSaveImage })
181
+ </script>
182
+
183
+ <style lang="scss" scoped>
184
+ @import "@/uni.scss";
185
+
186
+ .mu-image-preview {
187
+ position: fixed;
188
+ inset: 0;
189
+ z-index: $mu-z-modal;
190
+ background: rgba(0, 0, 0, 0.95);
191
+ backdrop-filter: blur(16px);
192
+ display: flex;
193
+ flex-direction: column;
194
+ opacity: 0;
195
+ transition: opacity 0.25s ease;
196
+
197
+ &--show {
198
+ opacity: 1;
199
+ }
200
+
201
+ &__header {
202
+ position: absolute;
203
+ top: 0;
204
+ left: 0;
205
+ right: 0;
206
+ z-index: 10;
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: space-between;
210
+ padding: 32rpx 36rpx;
211
+ padding-top: calc(32rpx + env(safe-area-inset-top));
212
+ }
213
+
214
+ &__page-badge {
215
+ padding: 8rpx 24rpx;
216
+ background: rgba(255, 255, 255, 0.15);
217
+ backdrop-filter: blur(12px);
218
+ border-radius: 999rpx;
219
+ border: 1rpx solid rgba(255, 255, 255, 0.2);
220
+ }
221
+
222
+ &__page-text {
223
+ font-size: 24rpx;
224
+ font-weight: 700;
225
+ color: #ffffff;
226
+ letter-spacing: 1rpx;
227
+ }
228
+
229
+ &__header-actions {
230
+ display: flex;
231
+ align-items: center;
232
+ gap: 20rpx;
233
+ }
234
+
235
+ &__icon-btn {
236
+ width: 68rpx;
237
+ height: 68rpx;
238
+ border-radius: 50%;
239
+ background: rgba(255, 255, 255, 0.15);
240
+ backdrop-filter: blur(12px);
241
+ border: 1rpx solid rgba(255, 255, 255, 0.2);
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: center;
245
+ cursor: pointer;
246
+ }
247
+
248
+ &__swiper {
249
+ width: 100vw;
250
+ height: 100vh;
251
+
252
+ &-item {
253
+ display: flex;
254
+ align-items: center;
255
+ justify-content: center;
256
+ box-sizing: border-box;
257
+ }
258
+ }
259
+
260
+ &__image {
261
+ width: 100%;
262
+ height: 100%;
263
+ will-change: transform;
264
+ }
265
+
266
+ &__footer {
267
+ position: absolute;
268
+ bottom: 0;
269
+ left: 0;
270
+ right: 0;
271
+ z-index: 10;
272
+ padding: 32rpx 40rpx;
273
+ padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
274
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.85), transparent);
275
+ backdrop-filter: blur(8px);
276
+ }
277
+
278
+ &__desc-text {
279
+ font-size: 26rpx;
280
+ color: rgba(255, 255, 255, 0.92);
281
+ line-height: 1.5;
282
+ display: block;
283
+ text-align: center;
284
+ }
285
+ }
286
+ </style>