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,368 @@
1
+ <template>
2
+ <view
3
+ class="mu-cell-wrapper"
4
+ :class="[swipe && 'mu-cell-wrapper--swipe']"
5
+ @touchstart="onTouchStart"
6
+ @touchmove="onTouchMove"
7
+ @touchend="onTouchEnd"
8
+ >
9
+ <!-- 单元格滑动平移卡片 -->
10
+ <view
11
+ class="mu-cell"
12
+ :class="cellClasses"
13
+ :style="[cellStyle, swipeContentStyle]"
14
+ @tap="handleClick"
15
+ >
16
+ <view class="mu-cell__body">
17
+ <!-- 左侧图标/图片/插槽 -->
18
+ <view v-if="$slots.icon || icon" class="mu-cell__icon">
19
+ <slot name="icon">
20
+ <mu-icon
21
+ v-if="isVectorIcon(icon)"
22
+ :name="icon"
23
+ :size="iconSize || 20"
24
+ :color="iconColor || '#64748B'"
25
+ />
26
+ <image v-else class="mu-cell__icon-img" :src="icon" mode="aspectFit" />
27
+ </slot>
28
+ </view>
29
+
30
+ <!-- 中间标题与副说明 -->
31
+ <view class="mu-cell__content">
32
+ <view class="mu-cell__title">
33
+ <slot><text class="mu-cell__title-text">{{ title }}</text></slot>
34
+ </view>
35
+ <view v-if="label || $slots.label" class="mu-cell__label">
36
+ <slot name="label"><text class="mu-cell__label-text">{{ label }}</text></slot>
37
+ </view>
38
+ </view>
39
+
40
+ <!-- 右侧自定义插槽 (支持绑定/授权手机号按钮、开关、文案等) -->
41
+ <view class="mu-cell__right" @tap.stop>
42
+ <slot name="right">
43
+ <slot name="value">
44
+ <text v-if="value" class="mu-cell__value">{{ value }}</text>
45
+ </slot>
46
+ <view v-if="arrow" class="mu-cell__arrow" @tap="handleClick">
47
+ <mu-icon name="chevron_right" :size="16" color="#94A3B8" />
48
+ </view>
49
+ </slot>
50
+ </view>
51
+ </view>
52
+
53
+ <!-- 底部分隔线 -->
54
+ <view v-if="divider && !noBorder" class="mu-cell__border"></view>
55
+
56
+ <!-- 右侧躲藏在屏幕外面的滑动多按钮区 -->
57
+ <view v-if="swipe" class="mu-cell__swipe-actions" :style="swipeActionsStyle">
58
+ <view
59
+ v-for="(act, ai) in normalizedSwipeActions"
60
+ :key="ai"
61
+ class="mu-cell__swipe-btn mu-touchable-flat"
62
+ :style="{ background: act.bg || act.color || '#F43F5E', width: `${buttonWidth}rpx` }"
63
+ @tap.stop="handleSwipeBtnClick(ai, act)"
64
+ >
65
+ <text class="mu-cell__swipe-text">{{ act.text || '删除' }}</text>
66
+ </view>
67
+ </view>
68
+ </view>
69
+ </view>
70
+ </template>
71
+
72
+ <script setup>
73
+ import { ref, computed } from 'vue'
74
+
75
+ const props = defineProps({
76
+ title: { type: String, default: '' },
77
+ label: { type: String, default: '' },
78
+ value: { type: String, default: '' },
79
+ icon: { type: String, default: '' },
80
+ iconSize: { type: [Number, String], default: 20 },
81
+ iconColor: { type: String, default: '' },
82
+ arrow: { type: Boolean, default: false },
83
+ disabled: { type: Boolean, default: false },
84
+ required: { type: Boolean, default: false },
85
+ divider: { type: Boolean, default: true },
86
+ noBorder: { type: Boolean, default: false },
87
+ center: { type: Boolean, default: false },
88
+ background: { type: String, default: '' },
89
+ padding: { type: String, default: '' },
90
+ url: { type: String, default: '' },
91
+
92
+ // 左滑扩展
93
+ swipe: { type: Boolean, default: false },
94
+ swipeActions: { type: Array, default: () => [] },
95
+ buttonWidth: { type: Number, default: 130 } // 每个左滑按钮宽度(rpx)
96
+ })
97
+
98
+ const emit = defineEmits(['click', 'click-swipe', 'delete'])
99
+
100
+ const touchStartX = ref(0)
101
+ const touchStartY = ref(0)
102
+ const swipeOffset = ref(0)
103
+ const isSwiping = ref(false)
104
+
105
+ const normalizedSwipeActions = computed(() => {
106
+ if (props.swipeActions && props.swipeActions.length) {
107
+ return props.swipeActions
108
+ }
109
+ return [{ text: '删除', bg: '#F43F5E' }]
110
+ })
111
+
112
+ // 计算所有滑动按钮的总展开宽度 (rpx)
113
+ const maxSwipeOffset = computed(() => {
114
+ return normalizedSwipeActions.value.length * props.buttonWidth
115
+ })
116
+
117
+ const cellClasses = computed(() => ({
118
+ 'mu-cell--disabled': props.disabled,
119
+ 'mu-cell--center': props.center,
120
+ 'mu-cell--required': props.required,
121
+ 'mu-cell--clickable': props.arrow || props.url
122
+ }))
123
+
124
+ const cellStyle = computed(() => {
125
+ const s = {}
126
+ if (props.background) s.background = props.background
127
+ if (props.padding) s.padding = props.padding
128
+ return s
129
+ })
130
+
131
+ const swipeContentStyle = computed(() => {
132
+ if (!props.swipe) return {}
133
+ return {
134
+ transform: `translateX(-${swipeOffset.value}rpx)`,
135
+ transition: isSwiping.value ? 'none' : 'transform 0.25s cubic-bezier(0.25, 1, 0.5, 1)'
136
+ }
137
+ })
138
+
139
+ const swipeActionsStyle = computed(() => ({
140
+ width: `${maxSwipeOffset.value}rpx`
141
+ }))
142
+
143
+ function isVectorIcon(iconStr) {
144
+ if (!iconStr) return false
145
+ return !iconStr.includes('/') && !/\.(png|jpg|jpeg|gif|svg|webp)$/i.test(iconStr)
146
+ }
147
+
148
+ function onTouchStart(e) {
149
+ if (!props.swipe || !e || !e.touches || !e.touches[0]) return
150
+ const touch = e.touches[0]
151
+ touchStartX.value = touch.clientX
152
+ touchStartY.value = touch.clientY
153
+ isSwiping.value = true
154
+ }
155
+
156
+ function onTouchMove(e) {
157
+ if (!props.swipe || !e || !e.touches || !e.touches[0]) return
158
+ const touch = e.touches[0]
159
+ const diffX = touchStartX.value - touch.clientX
160
+ const diffY = Math.abs(touchStartY.value - touch.clientY)
161
+
162
+ // 垂直滚动优先避让
163
+ if (diffY > Math.abs(diffX)) return
164
+
165
+ if (diffX > 0) {
166
+ swipeOffset.value = Math.min(maxSwipeOffset.value, diffX * 1.5)
167
+ } else if (swipeOffset.value > 0) {
168
+ swipeOffset.value = Math.max(0, maxSwipeOffset.value + diffX * 1.5)
169
+ }
170
+ }
171
+
172
+ function onTouchEnd() {
173
+ if (!props.swipe) return
174
+ isSwiping.value = false
175
+ if (swipeOffset.value > maxSwipeOffset.value / 3) {
176
+ swipeOffset.value = maxSwipeOffset.value
177
+ } else {
178
+ swipeOffset.value = 0
179
+ }
180
+ }
181
+
182
+ function handleSwipeBtnClick(index, act) {
183
+ emit('click-swipe', { index, action: act })
184
+ if (act.text === '删除' || act.type === 'danger') {
185
+ emit('delete', { index, action: act })
186
+ }
187
+ swipeOffset.value = 0
188
+ }
189
+
190
+ function handleClick(e) {
191
+ if (props.disabled) return
192
+ if (swipeOffset.value > 0) {
193
+ swipeOffset.value = 0
194
+ return
195
+ }
196
+ if (props.url) {
197
+ uni.navigateTo({ url: props.url })
198
+ }
199
+ emit('click', e)
200
+ }
201
+ </script>
202
+
203
+ <style lang="scss" scoped>
204
+ @import "@/uni.scss";
205
+
206
+ .mu-cell-wrapper {
207
+ position: relative;
208
+ width: 100%;
209
+ overflow: hidden;
210
+ box-sizing: border-box;
211
+
212
+ &--swipe {
213
+ display: flex;
214
+ }
215
+ }
216
+
217
+ .mu-cell {
218
+ position: relative;
219
+ width: 100%;
220
+ background: $mu-bg-base;
221
+ transition: background $mu-duration $mu-ease;
222
+ box-sizing: border-box;
223
+
224
+ &--clickable {
225
+ cursor: pointer;
226
+ &:active {
227
+ background: rgba(0, 0, 0, 0.03);
228
+ }
229
+ }
230
+
231
+ &--disabled { opacity: 0.45; pointer-events: none; }
232
+ &--center .mu-cell__body { align-items: center; }
233
+
234
+ &--required .mu-cell__title-text::before {
235
+ content: '*';
236
+ color: $mu-danger;
237
+ margin-right: 6rpx;
238
+ font-size: 26rpx;
239
+ font-weight: 700;
240
+ }
241
+
242
+ display: flex;
243
+ flex-direction: column;
244
+
245
+ &__body {
246
+ display: flex;
247
+ align-items: center;
248
+ padding: 24rpx 28rpx;
249
+ flex: 1;
250
+ min-height: 92rpx;
251
+ box-sizing: border-box;
252
+ }
253
+
254
+ &__icon {
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ margin-right: 20rpx;
259
+ flex-shrink: 0;
260
+
261
+ &-img {
262
+ width: 40rpx;
263
+ height: 40rpx;
264
+ border-radius: 8rpx;
265
+ }
266
+ }
267
+
268
+ &__content {
269
+ flex: 1;
270
+ min-width: 0;
271
+ }
272
+
273
+ &__title {
274
+ &-text {
275
+ font-size: 28rpx;
276
+ font-weight: 600;
277
+ color: $mu-text-primary;
278
+ line-height: 1.4;
279
+ }
280
+ }
281
+
282
+ &__label {
283
+ margin-top: 4rpx;
284
+ &-text {
285
+ font-size: 22rpx;
286
+ color: #94A3B8;
287
+ line-height: 1.3;
288
+ }
289
+ }
290
+
291
+ &__right {
292
+ display: flex;
293
+ align-items: center;
294
+ margin-left: 20rpx;
295
+ flex-shrink: 0;
296
+ }
297
+
298
+ &__value {
299
+ font-size: 26rpx;
300
+ color: #64748B;
301
+ font-weight: 400;
302
+ }
303
+
304
+ &__arrow {
305
+ margin-left: 8rpx;
306
+ display: flex;
307
+ align-items: center;
308
+ justify-content: center;
309
+ }
310
+
311
+ &__border {
312
+ position: absolute;
313
+ bottom: 0;
314
+ left: 28rpx;
315
+ right: 0;
316
+ height: 0;
317
+ border-bottom: 1rpx solid rgba(226, 232, 240, 0.6);
318
+ }
319
+
320
+ /* 隐藏在视口最右侧外的滑动多按钮区 */
321
+ &__swipe-actions {
322
+ position: absolute;
323
+ right: 0;
324
+ top: 0;
325
+ bottom: 0;
326
+ transform: translateX(100%);
327
+ display: flex;
328
+ align-items: stretch;
329
+ z-index: 5;
330
+ }
331
+
332
+ &__swipe-btn {
333
+ display: flex;
334
+ align-items: center;
335
+ justify-content: center;
336
+ cursor: pointer;
337
+ height: 100%;
338
+ }
339
+
340
+ &__swipe-text {
341
+ font-size: 24rpx;
342
+ font-weight: 800;
343
+ color: #ffffff;
344
+ }
345
+ }
346
+
347
+ .mu-dark, page.mu-dark {
348
+
349
+ .mu-cell {
350
+ background: $mu-dark-bg-base;
351
+ &--clickable:active { background: rgba(255, 255, 255, 0.05); }
352
+ &__title-text { color: $mu-dark-text-primary; }
353
+ &__value { color: $mu-dark-text-secondary; }
354
+ &__border { border-color: rgba(255, 255, 255, 0.08); }
355
+ }
356
+ }
357
+
358
+ @media (prefers-color-scheme: dark) {
359
+
360
+ .mu-cell {
361
+ background: $mu-dark-bg-base;
362
+ &--clickable:active { background: rgba(255, 255, 255, 0.05); }
363
+ &__title-text { color: $mu-dark-text-primary; }
364
+ &__value { color: $mu-dark-text-secondary; }
365
+ &__border { border-color: rgba(255, 255, 255, 0.08); }
366
+ }
367
+ }
368
+ </style>
@@ -0,0 +1,116 @@
1
+ <template>
2
+ <view class="mu-checkbox" :class="{ 'mu-checkbox--disabled': disabled }" @tap="handleClick">
3
+ <view class="mu-checkbox__icon" :class="{ 'mu-checkbox__icon--checked': isChecked, 'mu-checkbox__icon--disabled': disabled }">
4
+ <mu-icon v-if="isChecked" name="check" :size="12" color="#ffffff" />
5
+ </view>
6
+ <view v-if="label || $slots.default" class="mu-checkbox__label" :class="{ 'mu-checkbox__label--disabled': disabled }">
7
+ <slot><text class="mu-checkbox__label-text">{{ label }}</text></slot>
8
+ </view>
9
+ </view>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { computed, inject } from 'vue'
14
+
15
+ const props = defineProps({
16
+ name: { type: [String, Number, Boolean], default: '' },
17
+ label: { type: String, default: '' },
18
+ disabled: { type: Boolean, default: false },
19
+ modelValue: { type: Boolean, default: false }
20
+ })
21
+
22
+ const emit = defineEmits(['update:modelValue', 'change'])
23
+
24
+ const checkboxGroup = inject('mu-checkbox-group', inject('checkboxGroup', null))
25
+
26
+ const isChecked = computed(() => {
27
+ if (checkboxGroup) {
28
+ const groupVal = checkboxGroup.modelValue ? checkboxGroup.modelValue.value || [] : []
29
+ return groupVal.includes(props.name)
30
+ }
31
+ return props.modelValue
32
+ })
33
+
34
+ function handleClick() {
35
+ if (props.disabled) return
36
+ if (checkboxGroup) {
37
+ const groupVal = checkboxGroup.modelValue ? (checkboxGroup.modelValue.value || []) : []
38
+ const arr = Array.isArray(groupVal) ? [...groupVal] : []
39
+ const idx = arr.indexOf(props.name)
40
+ if (idx > -1) arr.splice(idx, 1)
41
+ else arr.push(props.name)
42
+ checkboxGroup.changeValue(arr)
43
+ } else {
44
+ emit('update:modelValue', !props.modelValue)
45
+ }
46
+ emit('change', !isChecked.value)
47
+ }
48
+ </script>
49
+
50
+ <style lang="scss" scoped>
51
+ @import "@/uni.scss";
52
+
53
+ .mu-checkbox {
54
+ display: inline-flex;
55
+ align-items: center;
56
+ cursor: pointer;
57
+ padding: 10rpx 0;
58
+ transition: opacity $mu-duration $mu-ease;
59
+
60
+ &--disabled { opacity: $mu-opacity-disabled; cursor: not-allowed; }
61
+
62
+ &__icon {
63
+ width: 34rpx;
64
+ height: 34rpx;
65
+ border-radius: 8rpx;
66
+ border: 2rpx solid $mu-border;
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ transition: all $mu-duration $mu-ease;
71
+ flex-shrink: 0;
72
+ background: $mu-bg-base;
73
+
74
+ &--checked {
75
+ border-color: transparent;
76
+ background: $mu-primary-gradient;
77
+ box-shadow: none;
78
+ }
79
+ &--disabled { border-color: $mu-text-disabled; }
80
+ }
81
+ &__label {
82
+ margin-left: 14rpx;
83
+ display: inline-flex;
84
+ align-items: center;
85
+ &--disabled { color: $mu-text-disabled; }
86
+ &-text {
87
+ font-size: 28rpx;
88
+ font-weight: 600;
89
+ color: $mu-text-primary;
90
+ line-height: 1;
91
+ }
92
+ }
93
+ }
94
+
95
+ .mu-dark, page.mu-dark {
96
+
97
+ .mu-checkbox {
98
+ &__icon {
99
+ background: $mu-dark-bg-base;
100
+ border-color: $mu-dark-border;
101
+ }
102
+ &__label-text { color: $mu-dark-text-primary; }
103
+ }
104
+ }
105
+
106
+ @media (prefers-color-scheme: dark) {
107
+
108
+ .mu-checkbox {
109
+ &__icon {
110
+ background: $mu-dark-bg-base;
111
+ border-color: $mu-dark-border;
112
+ }
113
+ &__label-text { color: $mu-dark-text-primary; }
114
+ }
115
+ }
116
+ </style>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <view class="mu-checkbox-group" :class="{ 'mu-checkbox-group--horizontal': direction === 'horizontal' }">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script setup>
8
+ /**
9
+ * mu-checkbox-group 复选框组
10
+ * @description 包裹多个 mu-checkbox,管理选中数组
11
+ * @property {Array} modelValue - 当前选中值数组
12
+ * @property {string} direction - 排列方向 horizontal|vertical
13
+ * @property {number} max - 最多可选数量
14
+ */
15
+ import { provide, toRefs } from 'vue'
16
+
17
+ const props = defineProps({
18
+ modelValue: { type: Array, default: () => [] },
19
+ direction: { type: String, default: 'vertical' },
20
+ max: { type: Number, default: 0 }
21
+ })
22
+
23
+ const emit = defineEmits(['update:modelValue', 'change'])
24
+
25
+ provide('mu-checkbox-group', {
26
+ modelValue: toRefs(props).modelValue,
27
+ changeValue(arr) {
28
+ if (props.max > 0 && arr.length > props.max) return
29
+ emit('update:modelValue', arr)
30
+ emit('change', arr)
31
+ }
32
+ })
33
+ </script>
34
+
35
+ <style lang="scss" scoped>
36
+ .mu-checkbox-group {
37
+ display: flex; flex-direction: column; gap: 8rpx;
38
+ &--horizontal { flex-direction: row; flex-wrap: wrap; gap: 24rpx; }
39
+ }
40
+ </style>