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,317 @@
1
+ <template>
2
+ <view class="mu-card" :class="cardClass" :style="cardStyle" @tap="handleClick">
3
+ <!-- 顶置媒体封面图片 -->
4
+ <view v-if="cover" class="mu-card__cover" :style="{ height: coverHeight }">
5
+ <image class="mu-card__cover-img" :src="cover" mode="aspectFill" />
6
+ <view v-if="coverTag" class="mu-card__cover-tag">{{ coverTag }}</view>
7
+ </view>
8
+
9
+ <!-- 头部 Header -->
10
+ <view v-if="title || subtitle || icon || $slots.header || $slots.extra || extra" class="mu-card__header">
11
+ <slot name="header">
12
+ <view class="mu-card__header-left">
13
+ <!-- 图标微光徽章 -->
14
+ <view v-if="icon || $slots.icon" class="mu-card__icon-badge" :style="iconBadgeStyle">
15
+ <slot name="icon">
16
+ <mu-icon :name="icon" :size="18" :color="iconColor || '#171717'" />
17
+ </slot>
18
+ </view>
19
+ <view class="mu-card__header-title-box">
20
+ <text v-if="title" class="mu-card__title">{{ title }}</text>
21
+ <text v-if="subtitle" class="mu-card__subtitle">{{ subtitle }}</text>
22
+ </view>
23
+ </view>
24
+ <view v-if="extra || $slots.extra" class="mu-card__header-extra">
25
+ <slot name="extra">
26
+ <view class="mu-card__extra-pill">
27
+ <text class="mu-card__extra-text">{{ extra }}</text>
28
+ </view>
29
+ </slot>
30
+ </view>
31
+ </slot>
32
+ </view>
33
+
34
+ <!-- 主体 Body -->
35
+ <view class="mu-card__body" :style="{ padding: bodyPadding }">
36
+ <slot></slot>
37
+ </view>
38
+
39
+ <!-- 底部 Footer -->
40
+ <view v-if="$slots.footer" class="mu-card__footer">
41
+ <slot name="footer"></slot>
42
+ </view>
43
+ </view>
44
+ </template>
45
+
46
+ <script setup>
47
+ import { computed } from 'vue'
48
+
49
+ const props = defineProps({
50
+ title: { type: String, default: '' },
51
+ subtitle: { type: String, default: '' },
52
+ icon: { type: String, default: '' },
53
+ iconColor: { type: String, default: '' },
54
+ iconBg: { type: String, default: '' },
55
+ extra: { type: String, default: '' },
56
+ cover: { type: String, default: '' },
57
+ coverTag: { type: String, default: '' },
58
+ coverHeight: { type: String, default: '320rpx' },
59
+ variant: { type: String, default: 'flat' }, // flat | elevated | glass | plain
60
+ bodyPadding: { type: String, default: '32rpx' },
61
+ radius: { type: String, default: '' },
62
+ border: { type: Boolean, default: true },
63
+ hoverable: { type: Boolean, default: false },
64
+ background: { type: String, default: '' },
65
+ margin: { type: String, default: '0' }
66
+ })
67
+
68
+ const emit = defineEmits(['click'])
69
+
70
+ const cardClass = computed(() => [
71
+ `mu-card--${props.variant}`,
72
+ props.border && 'mu-card--bordered',
73
+ props.hoverable && 'mu-card--hoverable'
74
+ ])
75
+
76
+ const cardStyle = computed(() => ({
77
+ ...(props.background ? { background: props.background } : {}),
78
+ ...(props.radius ? { borderRadius: props.radius } : {}),
79
+ ...(props.margin ? { margin: props.margin } : {})
80
+ }))
81
+
82
+ const iconBadgeStyle = computed(() => {
83
+ if (props.iconBg) return { background: props.iconBg, borderColor: 'transparent' }
84
+ return {}
85
+ })
86
+
87
+ function handleClick(e) {
88
+ emit('click', e)
89
+ }
90
+ </script>
91
+
92
+ <style lang="scss" scoped>
93
+ @import "@/uni.scss";
94
+
95
+ .mu-card {
96
+ position: relative;
97
+ background: rgba(255, 255, 255, 0.92);
98
+ border-radius: $mu-radius-xl;
99
+ overflow: hidden;
100
+ transition: all $mu-duration $mu-ease-spring;
101
+ box-sizing: border-box;
102
+
103
+ /* 纤细边框与双层内发光 */
104
+ &--bordered {
105
+ border: 1rpx solid rgba(226, 232, 240, 0.9);
106
+ box-shadow: inset 0 1rpx 0 0 rgba(255, 255, 255, 0.9);
107
+ }
108
+
109
+ /* 1. Flat 扁平双层包裹模式 */
110
+ &--flat {
111
+ box-shadow: 0 4rpx 16rpx -2rpx rgba(15, 23, 42, 0.04);
112
+ }
113
+
114
+ /* 2. Glass 毛玻璃模式 */
115
+ &--glass {
116
+ background: rgba(255, 255, 255, 0.75);
117
+ backdrop-filter: blur(20px);
118
+ border: 1rpx solid rgba(255, 255, 255, 0.95);
119
+ box-shadow: 0 8rpx 24rpx -4rpx rgba(15, 23, 42, 0.06);
120
+ }
121
+
122
+ /* 3. Elevated 悬浮光晕模式 */
123
+ &--elevated {
124
+ box-shadow: 0 12rpx 36rpx -6rpx rgba(15, 23, 42, 0.08), 0 4rpx 12rpx -2rpx rgba(15, 23, 42, 0.04);
125
+ border-color: rgba(226, 232, 240, 0.6);
126
+ }
127
+
128
+ /* 4. Plain 极简纯色模式 */
129
+ &--plain {
130
+ background: #ffffff;
131
+ border-color: $mu-border-light;
132
+ box-shadow: none;
133
+ }
134
+
135
+ /* 触控阻尼响应 */
136
+ &--hoverable:active {
137
+ transform: scale(0.982);
138
+ opacity: 0.96;
139
+ }
140
+
141
+ /* 顶置封面图 */
142
+ &__cover {
143
+ position: relative;
144
+ width: 100%;
145
+ overflow: hidden;
146
+ background: rgba(241, 245, 249, 0.5);
147
+
148
+ &-img {
149
+ width: 100%;
150
+ height: 100%;
151
+ display: block;
152
+ }
153
+
154
+ &-tag {
155
+ position: absolute;
156
+ top: 20rpx;
157
+ right: 20rpx;
158
+ padding: 6rpx 18rpx;
159
+ border-radius: $mu-radius-full;
160
+ background: rgba(15, 23, 42, 0.65);
161
+ backdrop-filter: blur(12px);
162
+ color: #ffffff;
163
+ font-size: 20rpx;
164
+ font-weight: 700;
165
+ border: 1rpx solid rgba(255, 255, 255, 0.3);
166
+ }
167
+ }
168
+
169
+ /* 头部 Header */
170
+ &__header {
171
+ padding: 28rpx 32rpx;
172
+ border-bottom: 1rpx solid rgba(241, 245, 249, 0.9);
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: space-between;
176
+
177
+ &-left {
178
+ display: flex;
179
+ align-items: center;
180
+ flex: 1;
181
+ min-width: 0;
182
+ }
183
+
184
+ &-title-box {
185
+ display: flex;
186
+ flex-direction: column;
187
+ min-width: 0;
188
+ }
189
+
190
+ &-extra {
191
+ margin-left: 20rpx;
192
+ flex-shrink: 0;
193
+ }
194
+ }
195
+
196
+ /* 图标光晕微章 */
197
+ &__icon-badge {
198
+ width: 60rpx;
199
+ height: 60rpx;
200
+ border-radius: 18rpx;
201
+ background: rgba(99, 102, 241, 0.08);
202
+ border: 1rpx solid rgba(99, 102, 241, 0.18);
203
+ display: flex;
204
+ align-items: center;
205
+ justify-content: center;
206
+ margin-right: 20rpx;
207
+ flex-shrink: 0;
208
+ }
209
+
210
+ &__title {
211
+ font-size: 30rpx;
212
+ font-weight: 800;
213
+ color: $mu-text-primary;
214
+ letter-spacing: -0.4rpx;
215
+ line-height: 1.35;
216
+ }
217
+
218
+ &__subtitle {
219
+ font-size: 22rpx;
220
+ color: $mu-text-tertiary;
221
+ margin-top: 4rpx;
222
+ font-weight: 500;
223
+ }
224
+
225
+ /* 右上角 Extra 胶囊按钮 */
226
+ &__extra-pill {
227
+ padding: 8rpx 20rpx;
228
+ border-radius: $mu-radius-full;
229
+ background: rgba(99, 102, 241, 0.06);
230
+ border: 1rpx solid rgba(99, 102, 241, 0.15);
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ }
235
+
236
+ &__extra-text {
237
+ font-size: 22rpx;
238
+ color: $mu-primary;
239
+ font-weight: 700;
240
+ }
241
+
242
+ /* 主体 Body */
243
+ &__body {
244
+ box-sizing: border-box;
245
+ }
246
+
247
+ /* 底部 Footer */
248
+ &__footer {
249
+ padding: 24rpx 32rpx;
250
+ border-top: 1rpx solid rgba(241, 245, 249, 0.9);
251
+ background: rgba(248, 250, 252, 0.6);
252
+ display: flex;
253
+ align-items: center;
254
+ justify-content: flex-end;
255
+ }
256
+ }
257
+
258
+ .mu-dark, page.mu-dark {
259
+
260
+ .mu-card {
261
+ background: rgba(18, 18, 24, 0.88);
262
+ border-color: rgba(255, 255, 255, 0.08);
263
+
264
+ &--glass {
265
+ background: rgba(18, 18, 24, 0.75);
266
+ border-color: rgba(255, 255, 255, 0.12);
267
+ }
268
+ &__header {
269
+ border-bottom-color: rgba(255, 255, 255, 0.06);
270
+ }
271
+ &__icon-badge {
272
+ background: rgba(255, 255, 255, 0.06);
273
+ border-color: rgba(255, 255, 255, 0.12);
274
+ }
275
+ &__title { color: $mu-dark-text-primary; }
276
+ &__subtitle { color: $mu-dark-text-secondary; }
277
+ &__extra-pill {
278
+ background: rgba(255, 255, 255, 0.08);
279
+ border-color: rgba(255, 255, 255, 0.15);
280
+ }
281
+ &__footer {
282
+ border-top-color: rgba(255, 255, 255, 0.06);
283
+ background: rgba(255, 255, 255, 0.02);
284
+ }
285
+ }
286
+ }
287
+
288
+ @media (prefers-color-scheme: dark) {
289
+
290
+ .mu-card {
291
+ background: rgba(18, 18, 24, 0.88);
292
+ border-color: rgba(255, 255, 255, 0.08);
293
+
294
+ &--glass {
295
+ background: rgba(18, 18, 24, 0.75);
296
+ border-color: rgba(255, 255, 255, 0.12);
297
+ }
298
+ &__header {
299
+ border-bottom-color: rgba(255, 255, 255, 0.06);
300
+ }
301
+ &__icon-badge {
302
+ background: rgba(255, 255, 255, 0.06);
303
+ border-color: rgba(255, 255, 255, 0.12);
304
+ }
305
+ &__title { color: $mu-dark-text-primary; }
306
+ &__subtitle { color: $mu-dark-text-secondary; }
307
+ &__extra-pill {
308
+ background: rgba(255, 255, 255, 0.08);
309
+ border-color: rgba(255, 255, 255, 0.15);
310
+ }
311
+ &__footer {
312
+ border-top-color: rgba(255, 255, 255, 0.06);
313
+ background: rgba(255, 255, 255, 0.02);
314
+ }
315
+ }
316
+ }
317
+ </style>
@@ -0,0 +1,336 @@
1
+ <template>
2
+ <view v-if="visible" class="mu-cascader" :class="[panelShow && 'mu-cascader--open']">
3
+ <!-- 遮罩层 -->
4
+ <view class="mu-cascader__mask" @tap="handleClose"></view>
5
+
6
+ <!-- 底部面板 Panel -->
7
+ <view class="mu-cascader__panel" :class="[panelShow && 'mu-cascader__panel--show']" @tap.stop>
8
+ <!-- 头部 Header -->
9
+ <view class="mu-cascader__header">
10
+ <text class="mu-cascader__title">{{ title }}</text>
11
+ <view class="mu-cascader__close mu-touchable-flat" @tap="handleClose">
12
+ <mu-icon name="close" :size="16" color="#94A3B8" />
13
+ </view>
14
+ </view>
15
+
16
+ <!-- 级联 Tab 选项卡组 -->
17
+ <scroll-view scroll-x class="mu-cascader__tabs-scroll" :show-scrollbar="false">
18
+ <view class="mu-cascader__tabs">
19
+ <view
20
+ v-for="(tab, i) in tabs"
21
+ :key="i"
22
+ class="mu-cascader__tab"
23
+ :class="[activeTab === i && 'mu-cascader__tab--active']"
24
+ @tap="switchTab(i)"
25
+ >
26
+ <text class="mu-cascader__tab-text">{{ tab || placeholder[i] || '请选择' }}</text>
27
+ <view v-if="activeTab === i" class="mu-cascader__tab-line"></view>
28
+ </view>
29
+ </view>
30
+ </scroll-view>
31
+
32
+ <!-- 选项列表 scroll-view -->
33
+ <scroll-view scroll-y class="mu-cascader__options">
34
+ <view
35
+ v-for="(opt, oi) in currentOptions"
36
+ :key="oi"
37
+ class="mu-cascader__option"
38
+ :class="[isSelected(opt, activeTab) && 'mu-cascader__option--active']"
39
+ @tap="selectOption(opt, oi)"
40
+ >
41
+ <text class="mu-cascader__option-text">{{ getField(opt, 'label') }}</text>
42
+ <mu-icon v-if="isSelected(opt, activeTab)" name="check" :size="16" color="#171717" />
43
+ </view>
44
+ </scroll-view>
45
+ </view>
46
+ </view>
47
+ </template>
48
+
49
+ <script setup>
50
+ import { ref, computed, watch } from 'vue'
51
+
52
+ const props = defineProps({
53
+ show: { type: Boolean, default: false },
54
+ modelValue: { type: Array, default: () => [] },
55
+ value: { type: Array, default: () => [] },
56
+ options: { type: Array, default: () => [] },
57
+ title: { type: String, default: '请选择' },
58
+ fieldNames: { type: Object, default: () => ({ label: 'label', value: 'value', children: 'children' }) }
59
+ })
60
+
61
+ const emit = defineEmits(['change', 'close', 'update:show', 'update:modelValue', 'update:value'])
62
+
63
+ const visible = ref(false)
64
+ const panelShow = ref(false)
65
+ const activeTab = ref(0)
66
+ const selectedPath = ref([])
67
+ const placeholder = ['请选择', '请选择', '请选择', '请选择']
68
+
69
+ function getField(obj, field) {
70
+ if (!obj) return ''
71
+ const key = props.fieldNames[field] || field
72
+ return obj[key]
73
+ }
74
+
75
+ const currentOptions = computed(() => {
76
+ let list = props.options
77
+ for (let i = 0; i < activeTab.value; i++) {
78
+ const selectedVal = selectedPath.value[i]
79
+ const selectedOpt = list.find(o => getField(o, 'value') === selectedVal)
80
+ if (selectedOpt && getField(selectedOpt, 'children')) {
81
+ list = getField(selectedOpt, 'children')
82
+ } else {
83
+ return []
84
+ }
85
+ }
86
+ return list || []
87
+ })
88
+
89
+ const tabs = computed(() => {
90
+ const result = []
91
+ let list = props.options
92
+
93
+ for (let i = 0; i < selectedPath.value.length; i++) {
94
+ const val = selectedPath.value[i]
95
+ const opt = list.find(o => getField(o, 'value') === val)
96
+ if (opt) {
97
+ result.push(getField(opt, 'label'))
98
+ const children = getField(opt, 'children')
99
+ if (children && children.length) {
100
+ list = children
101
+ } else {
102
+ return result
103
+ }
104
+ }
105
+ }
106
+
107
+ result.push('')
108
+ return result
109
+ })
110
+
111
+ function isSelected(opt, level) {
112
+ return selectedPath.value[level] === getField(opt, 'value')
113
+ }
114
+
115
+ function switchTab(i) {
116
+ activeTab.value = i
117
+ }
118
+
119
+ function selectOption(opt) {
120
+ const val = getField(opt, 'value')
121
+ selectedPath.value = selectedPath.value.slice(0, activeTab.value)
122
+ selectedPath.value.push(val)
123
+
124
+ const children = getField(opt, 'children')
125
+ if (children && children.length) {
126
+ activeTab.value++
127
+ } else {
128
+ emit('change', [...selectedPath.value])
129
+ emit('update:modelValue', [...selectedPath.value])
130
+ emit('update:value', [...selectedPath.value])
131
+ handleClose()
132
+ }
133
+ }
134
+
135
+ function handleClose() {
136
+ panelShow.value = false
137
+ emit('update:show', false)
138
+ setTimeout(() => {
139
+ visible.value = false
140
+ emit('close')
141
+ }, 250)
142
+ }
143
+
144
+ watch(() => props.show, (val) => {
145
+ if (val) {
146
+ visible.value = true
147
+ const initVal = props.modelValue.length ? props.modelValue : props.value
148
+ selectedPath.value = [...initVal]
149
+ activeTab.value = selectedPath.value.length > 0 ? selectedPath.value.length - 1 : 0
150
+ setTimeout(() => {
151
+ panelShow.value = true
152
+ }, 30)
153
+ } else {
154
+ panelShow.value = false
155
+ setTimeout(() => {
156
+ visible.value = false
157
+ }, 250)
158
+ }
159
+ }, { immediate: true })
160
+ </script>
161
+
162
+ <style lang="scss" scoped>
163
+ @import "@/uni.scss";
164
+
165
+ .mu-cascader {
166
+ position: fixed;
167
+ inset: 0;
168
+ z-index: $mu-z-popup;
169
+ display: flex;
170
+ flex-direction: column;
171
+ justify-content: flex-end;
172
+ box-sizing: border-box;
173
+
174
+ &__mask {
175
+ position: absolute;
176
+ inset: 0;
177
+ background: rgba(15, 23, 42, 0.45);
178
+ backdrop-filter: none !important;
179
+ opacity: 0;
180
+ transition: opacity 0.25s ease;
181
+ }
182
+
183
+ &--open &__mask {
184
+ opacity: 1;
185
+ }
186
+
187
+ &__panel {
188
+ position: relative;
189
+ background: #ffffff;
190
+ border-radius: 32rpx 32rpx 0 0;
191
+ max-height: 75vh;
192
+ display: flex;
193
+ flex-direction: column;
194
+ transform: translateY(100%);
195
+ transition: transform 0.3s cubic-bezier(0.25, 1, 0.5, 1);
196
+ z-index: 2;
197
+ overflow: hidden;
198
+
199
+ &--show {
200
+ transform: translateY(0);
201
+ }
202
+ }
203
+
204
+ &__header {
205
+ display: flex;
206
+ align-items: center;
207
+ justify-content: space-between;
208
+ padding: 28rpx 32rpx;
209
+ border-bottom: 1rpx solid rgba(226, 232, 240, 0.8);
210
+ }
211
+
212
+ &__title {
213
+ font-size: 30rpx;
214
+ font-weight: 800;
215
+ color: $mu-text-primary;
216
+ letter-spacing: -0.5rpx;
217
+ }
218
+
219
+ &__close {
220
+ width: 44rpx;
221
+ height: 44rpx;
222
+ border-radius: 50%;
223
+ background: #F1F5F9;
224
+ display: flex;
225
+ align-items: center;
226
+ justify-content: center;
227
+ }
228
+
229
+ &__tabs-scroll {
230
+ width: 100%;
231
+ border-bottom: 1rpx solid rgba(226, 232, 240, 0.8);
232
+ background: #F8FAFC;
233
+ }
234
+
235
+ &__tabs {
236
+ display: flex;
237
+ padding: 0 24rpx;
238
+ white-space: nowrap;
239
+ }
240
+
241
+ &__tab {
242
+ position: relative;
243
+ padding: 24rpx 20rpx;
244
+ display: inline-flex;
245
+ align-items: center;
246
+ justify-content: center;
247
+
248
+ &-text {
249
+ font-size: 26rpx;
250
+ color: #64748B;
251
+ font-weight: 600;
252
+ transition: color 0.2s ease;
253
+ }
254
+
255
+ &--active &-text {
256
+ color: #171717;
257
+ font-weight: 800;
258
+ }
259
+
260
+ &-line {
261
+ position: absolute;
262
+ bottom: 0;
263
+ left: 20rpx;
264
+ right: 20rpx;
265
+ height: 5rpx;
266
+ background: #171717;
267
+ border-radius: 3rpx;
268
+ }
269
+ }
270
+
271
+ &__options {
272
+ flex: 1;
273
+ max-height: 55vh;
274
+ }
275
+
276
+ &__option {
277
+ display: flex;
278
+ align-items: center;
279
+ justify-content: space-between;
280
+ padding: 28rpx 32rpx;
281
+ cursor: pointer;
282
+ transition: background 0.15s ease;
283
+
284
+ &:active {
285
+ background: rgba(99, 102, 241, 0.04);
286
+ }
287
+
288
+ &--active {
289
+ background: rgba(99, 102, 241, 0.06);
290
+
291
+ .mu-cascader__option-text {
292
+ color: #171717;
293
+ font-weight: 800;
294
+ }
295
+ }
296
+
297
+ &-text {
298
+ font-size: 28rpx;
299
+ color: $mu-text-primary;
300
+ font-weight: 500;
301
+ }
302
+ }
303
+ }
304
+
305
+ .mu-dark, page.mu-dark {
306
+
307
+ .mu-cascader {
308
+ &__mask { background: rgba(0, 0, 0, 0.65); }
309
+ &__panel { background: #14141E !important; }
310
+ &__header { border-color: rgba(255, 255, 255, 0.08); }
311
+ &__title { color: $mu-dark-text-primary; }
312
+ &__close { background: rgba(255, 255, 255, 0.1); }
313
+ &__tabs-scroll { background: rgba(255, 255, 255, 0.04); border-color: rgba(255, 255, 255, 0.06); }
314
+ &__tab-text { color: $mu-dark-text-secondary; }
315
+ &__tab--active .mu-cascader__tab-text { color: #FFFFFF; }
316
+ &__option-text { color: $mu-dark-text-primary; }
317
+ &__option--active { background: rgba(99, 102, 241, 0.15); .mu-cascader__option-text { color: #FFFFFF; } }
318
+ }
319
+ }
320
+
321
+ @media (prefers-color-scheme: dark) {
322
+
323
+ .mu-cascader {
324
+ &__mask { background: rgba(0, 0, 0, 0.65); }
325
+ &__panel { background: #14141E !important; }
326
+ &__header { border-color: rgba(255, 255, 255, 0.08); }
327
+ &__title { color: $mu-dark-text-primary; }
328
+ &__close { background: rgba(255, 255, 255, 0.1); }
329
+ &__tabs-scroll { background: rgba(255, 255, 255, 0.04); border-color: rgba(255, 255, 255, 0.06); }
330
+ &__tab-text { color: $mu-dark-text-secondary; }
331
+ &__tab--active .mu-cascader__tab-text { color: #FFFFFF; }
332
+ &__option-text { color: $mu-dark-text-primary; }
333
+ &__option--active { background: rgba(99, 102, 241, 0.15); .mu-cascader__option-text { color: #FFFFFF; } }
334
+ }
335
+ }
336
+ </style>