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,262 @@
1
+ <template>
2
+ <view v-if="visible" class="mu-datetime" @tap="handleClose">
3
+ <view class="mu-datetime__mask"></view>
4
+ <view class="mu-datetime__panel" :class="{ 'mu-datetime__panel--show': panelShow }" @tap.stop>
5
+ <view class="mu-datetime__toolbar">
6
+ <text class="mu-datetime__cancel mu-touchable-flat" @tap="handleClose">取消</text>
7
+ <text class="mu-datetime__title">{{ title }}</text>
8
+ <text class="mu-datetime__confirm mu-touchable-flat" @tap="handleConfirm">确定</text>
9
+ </view>
10
+ <view class="mu-datetime__body">
11
+ <picker-view :value="pickerValue" class="mu-datetime__picker" @change="onPickerChange">
12
+ <picker-view-column v-if="showYear">
13
+ <view v-for="y in years" :key="y" class="mu-datetime__col">
14
+ <text class="mu-datetime__col-text">{{ y }}年</text>
15
+ </view>
16
+ </picker-view-column>
17
+ <picker-view-column v-if="showMonth">
18
+ <view v-for="m in months" :key="m" class="mu-datetime__col">
19
+ <text class="mu-datetime__col-text">{{ padZero(m) }}月</text>
20
+ </view>
21
+ </picker-view-column>
22
+ <picker-view-column v-if="showDay">
23
+ <view v-for="d in days" :key="d" class="mu-datetime__col">
24
+ <text class="mu-datetime__col-text">{{ padZero(d) }}日</text>
25
+ </view>
26
+ </picker-view-column>
27
+ <picker-view-column v-if="showHour">
28
+ <view v-for="h in 24" :key="h-1" class="mu-datetime__col">
29
+ <text class="mu-datetime__col-text">{{ padZero(h-1) }}时</text>
30
+ </view>
31
+ </picker-view-column>
32
+ <picker-view-column v-if="showMinute">
33
+ <view v-for="m in 60" :key="m-1" class="mu-datetime__col">
34
+ <text class="mu-datetime__col-text">{{ padZero(m-1) }}分</text>
35
+ </view>
36
+ </picker-view-column>
37
+ </picker-view>
38
+ </view>
39
+ </view>
40
+ </view>
41
+ </template>
42
+
43
+ <script setup>
44
+ import { ref, computed, watch } from 'vue'
45
+
46
+ const props = defineProps({
47
+ show: { type: Boolean, default: false },
48
+ value: { type: String, default: '' },
49
+ title: { type: String, default: '选择日期' },
50
+ mode: { type: String, default: 'date' },
51
+ startYear: { type: Number, default: 1970 },
52
+ endYear: { type: Number, default: 2050 }
53
+ })
54
+
55
+ const emit = defineEmits(['confirm', 'cancel', 'close', 'update:show'])
56
+
57
+ const visible = ref(false)
58
+ const panelShow = ref(false)
59
+ const pickerValue = ref([])
60
+
61
+ const showYear = computed(() => ['date', 'year-month', 'year'].includes(props.mode))
62
+ const showMonth = computed(() => ['date', 'year-month'].includes(props.mode))
63
+ const showDay = computed(() => props.mode === 'date')
64
+ const showHour = computed(() => ['datetime', 'time'].includes(props.mode))
65
+ const showMinute = computed(() => ['datetime', 'time'].includes(props.mode))
66
+
67
+ const years = computed(() => {
68
+ const arr = []
69
+ for (let i = props.startYear; i <= props.endYear; i++) arr.push(i)
70
+ return arr
71
+ })
72
+
73
+ const months = computed(() => Array.from({ length: 12 }, (_, i) => i + 1))
74
+
75
+ const days = computed(() => {
76
+ const month = getMonth()
77
+ const year = getYear()
78
+ const d = new Date(year, month, 0).getDate()
79
+ return Array.from({ length: d }, (_, i) => i + 1)
80
+ })
81
+
82
+ function getYear() {
83
+ if (!showYear.value) return new Date().getFullYear()
84
+ return years.value[pickerValue.value[getColumnIndex('year')]] || new Date().getFullYear()
85
+ }
86
+
87
+ function getMonth() {
88
+ if (!showMonth.value) return new Date().getMonth() + 1
89
+ const idx = getColumnIndex('month')
90
+ return (pickerValue.value[idx] || 0) + 1
91
+ }
92
+
93
+ function getColumnIndex(col) {
94
+ let i = 0
95
+ if (col === 'year') return 0
96
+ if (showYear.value) i++
97
+ if (col === 'month') return i
98
+ if (showMonth.value) i++
99
+ if (col === 'day') return i
100
+ if (showDay.value) i++
101
+ return i
102
+ }
103
+
104
+ function padZero(n) {
105
+ return n < 10 ? '0' + n : String(n)
106
+ }
107
+
108
+ function initPickerValue() {
109
+ const val = props.value ? new Date(props.value.replace(/-/g, '/')) : new Date()
110
+ const curYear = isNaN(val.getFullYear()) ? new Date().getFullYear() : val.getFullYear()
111
+ const curMonth = isNaN(val.getMonth()) ? new Date().getMonth() + 1 : val.getMonth() + 1
112
+ const curDay = isNaN(val.getDate()) ? new Date().getDate() : val.getDate()
113
+ const curHour = isNaN(val.getHours()) ? 0 : val.getHours()
114
+ const curMinute = isNaN(val.getMinutes()) ? 0 : val.getMinutes()
115
+
116
+ const res = []
117
+ if (showYear.value) res.push(Math.max(0, years.value.indexOf(curYear)))
118
+ if (showMonth.value) res.push(curMonth - 1)
119
+ if (showDay.value) res.push(curDay - 1)
120
+ if (showHour.value) res.push(curHour)
121
+ if (showMinute.value) res.push(curMinute)
122
+ pickerValue.value = res
123
+ }
124
+
125
+ function onPickerChange(e) {
126
+ pickerValue.value = e.detail.value
127
+ }
128
+
129
+ function handleConfirm() {
130
+ let result = ''
131
+ const year = getYear()
132
+ const month = padZero(getMonth())
133
+ const day = padZero(days.value[pickerValue.value[getColumnIndex('day')]] || 1)
134
+ const hour = padZero((pickerValue.value[getColumnIndex('hour')] || 0))
135
+ const minute = padZero((pickerValue.value[getColumnIndex('minute')] || 0))
136
+
137
+ if (props.mode === 'date') result = `${year}-${month}-${day}`
138
+ else if (props.mode === 'year-month') result = `${year}-${month}`
139
+ else if (props.mode === 'year') result = `${year}`
140
+ else if (props.mode === 'time') result = `${hour}:${minute}`
141
+ else if (props.mode === 'datetime') result = `${year}-${month}-${day} ${hour}:${minute}`
142
+
143
+ emit('confirm', result)
144
+ handleClose()
145
+ }
146
+
147
+ function handleClose() {
148
+ panelShow.value = false
149
+ setTimeout(() => {
150
+ visible.value = false
151
+ emit('update:show', false)
152
+ emit('close')
153
+ emit('cancel')
154
+ }, 250)
155
+ }
156
+
157
+ function open() {
158
+ initPickerValue()
159
+ visible.value = true
160
+ setTimeout(() => { panelShow.value = true }, 20)
161
+ }
162
+
163
+ watch(() => props.show, (v) => {
164
+ if (v) open()
165
+ else handleClose()
166
+ }, { immediate: true })
167
+ </script>
168
+
169
+ <style lang="scss" scoped>
170
+ @import "@/uni.scss";
171
+
172
+ .mu-datetime {
173
+ position: fixed;
174
+ inset: 0;
175
+ z-index: $mu-z-modal;
176
+ display: flex;
177
+ flex-direction: column;
178
+ justify-content: flex-end;
179
+
180
+ &__mask {
181
+ position: absolute;
182
+ inset: 0;
183
+ background: rgba(15, 23, 42, 0.6);
184
+ backdrop-filter: blur(12px);
185
+ }
186
+
187
+ &__panel {
188
+ position: relative;
189
+ z-index: 10;
190
+ background: #FFFFFF !important;
191
+ border-radius: 32rpx 32rpx 0 0;
192
+ transform: translateY(100%);
193
+ transition: transform 0.28s cubic-bezier(0.25, 1, 0.5, 1);
194
+
195
+ &--show { transform: translateY(0); }
196
+ }
197
+
198
+ &__toolbar {
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: space-between;
202
+ padding: 28rpx 36rpx;
203
+ border-bottom: 1rpx solid rgba(241, 245, 249, 0.9);
204
+ }
205
+
206
+ &__cancel {
207
+ font-size: 28rpx;
208
+ color: #64748B;
209
+ font-weight: 500;
210
+ padding: 8rpx 16rpx;
211
+ }
212
+
213
+ &__title {
214
+ font-size: 30rpx;
215
+ color: $mu-text-primary;
216
+ font-weight: 800;
217
+ }
218
+
219
+ &__confirm {
220
+ font-size: 28rpx;
221
+ color: #171717;
222
+ font-weight: 800;
223
+ padding: 8rpx 16rpx;
224
+ }
225
+
226
+ &__body { height: 480rpx; background: #FFFFFF; }
227
+ &__picker { height: 100%; background: #FFFFFF; }
228
+
229
+ &__col {
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: center;
233
+ &-text { font-size: 30rpx; color: $mu-text-primary; font-weight: 600; }
234
+ }
235
+ }
236
+
237
+ .mu-dark, page.mu-dark {
238
+ .mu-datetime {
239
+ &__panel, &__body, &__picker, picker-view, picker-view-column {
240
+ background: #14141E !important;
241
+ background-color: #14141E !important;
242
+ border-top: 1rpx solid rgba(255, 255, 255, 0.1);
243
+ }
244
+ &__toolbar { border-bottom-color: rgba(255, 255, 255, 0.06); }
245
+ &__title, &__col-text { color: #F8FAFC !important; }
246
+ &__cancel { color: #94A3B8 !important; }
247
+ }
248
+ }
249
+
250
+ @media (prefers-color-scheme: dark) {
251
+ .mu-datetime {
252
+ &__panel, &__body, &__picker, picker-view, picker-view-column {
253
+ background: #14141E !important;
254
+ background-color: #14141E !important;
255
+ border-top: 1rpx solid rgba(255, 255, 255, 0.1);
256
+ }
257
+ &__toolbar { border-bottom-color: rgba(255, 255, 255, 0.06); }
258
+ &__title, &__col-text { color: #F8FAFC !important; }
259
+ &__cancel { color: #94A3B8 !important; }
260
+ }
261
+ }
262
+ </style>
@@ -0,0 +1,229 @@
1
+ <template>
2
+ <view
3
+ class="mu-divider"
4
+ :class="[
5
+ vertical ? 'mu-divider--vertical' : 'mu-divider--horizontal',
6
+ dashed && 'mu-divider--dashed',
7
+ variant === 'gradient' && 'mu-divider--gradient',
8
+ `mu-divider--${computedPosition}`
9
+ ]"
10
+ :style="customStyle"
11
+ >
12
+ <!-- 垂直模式 -->
13
+ <view v-if="vertical" class="mu-divider__vertical-line" :style="verticalStyle"></view>
14
+
15
+ <!-- 水平模式 -->
16
+ <template v-else>
17
+ <view class="mu-divider__line mu-divider__line--left"></view>
18
+
19
+ <!-- 中间文字 / 图标内容区 -->
20
+ <view v-if="hasText" class="mu-divider__content">
21
+ <slot>
22
+ <view class="mu-divider__inner-wrap">
23
+ <view v-if="icon" class="mu-divider__icon-box">
24
+ <mu-icon :name="icon" :size="14" color="#94A3B8" />
25
+ </view>
26
+ <text class="mu-divider__text" :style="textCustomStyle">{{ displayText }}</text>
27
+ </view>
28
+ </slot>
29
+ </view>
30
+
31
+ <view class="mu-divider__line mu-divider__line--right"></view>
32
+ </template>
33
+ </view>
34
+ </template>
35
+
36
+ <script setup>
37
+ import { computed, useSlots } from 'vue'
38
+
39
+ const slots = useSlots()
40
+
41
+ const props = defineProps({
42
+ content: { type: String, default: '' },
43
+ contentPosition: { type: String, default: '' }, // left | center | right
44
+ orientation: { type: String, default: 'center' }, // left | center | right
45
+ dashed: { type: Boolean, default: false },
46
+ variant: { type: String, default: 'solid' }, // solid | dashed | gradient
47
+ color: { type: String, default: '' },
48
+ textColor: { type: String, default: '' },
49
+ icon: { type: String, default: '' },
50
+ vertical: { type: Boolean, default: false },
51
+ margin: { type: String, default: '' }
52
+ })
53
+
54
+ const computedPosition = computed(() => {
55
+ if (props.contentPosition) return props.contentPosition
56
+ return props.orientation || 'center'
57
+ })
58
+
59
+ const hasText = computed(() => !!slots.default || !!props.content || !!props.icon)
60
+
61
+ const displayText = computed(() => props.content || '')
62
+
63
+ const customStyle = computed(() => {
64
+ const s = {}
65
+ if (props.margin) s.margin = props.margin
66
+ return s
67
+ })
68
+
69
+ const textCustomStyle = computed(() => {
70
+ const s = {}
71
+ if (props.textColor) s.color = props.textColor
72
+ return s
73
+ })
74
+
75
+ const verticalStyle = computed(() => {
76
+ const s = {}
77
+ if (props.color) s.background = props.color
78
+ return s
79
+ })
80
+ </script>
81
+
82
+ <style lang="scss" scoped>
83
+ @import "@/uni.scss";
84
+
85
+ .mu-divider {
86
+ /* 1. 水平分割线 */
87
+ &--horizontal {
88
+ display: flex;
89
+ align-items: center;
90
+ width: 100%;
91
+ margin: 24rpx 0;
92
+ box-sizing: border-box;
93
+
94
+ .mu-divider__line {
95
+ flex: 1;
96
+ height: 0;
97
+ border-top: 1rpx solid rgba(226, 232, 240, 0.8);
98
+ }
99
+
100
+ &.mu-divider--dashed .mu-divider__line {
101
+ border-top-style: dashed;
102
+ }
103
+
104
+ &.mu-divider--gradient .mu-divider__line--left {
105
+ border-top: none;
106
+ height: 1rpx;
107
+ background: linear-gradient(to right, transparent, rgba(226, 232, 240, 0.9));
108
+ }
109
+ &.mu-divider--gradient .mu-divider__line--right {
110
+ border-top: none;
111
+ height: 1rpx;
112
+ background: linear-gradient(to left, transparent, rgba(226, 232, 240, 0.9));
113
+ }
114
+
115
+ /* 位置控制 */
116
+ &.mu-divider--left {
117
+ .mu-divider__line--left {
118
+ flex: 0 0 8%;
119
+ }
120
+ }
121
+
122
+ &.mu-divider--right {
123
+ .mu-divider__line--right {
124
+ flex: 0 0 8%;
125
+ }
126
+ }
127
+ }
128
+
129
+ /* 2. 垂直分割线 */
130
+ &--vertical {
131
+ display: inline-flex;
132
+ align-items: center;
133
+ vertical-align: middle;
134
+ margin: 0 16rpx;
135
+
136
+ .mu-divider__vertical-line {
137
+ width: 1rpx;
138
+ height: 24rpx;
139
+ background: rgba(226, 232, 240, 0.9);
140
+ }
141
+
142
+ &.mu-divider--dashed .mu-divider__vertical-line {
143
+ border-left: 1rpx dashed rgba(226, 232, 240, 0.9);
144
+ background: transparent;
145
+ width: 0;
146
+ }
147
+ }
148
+
149
+ &__content {
150
+ padding: 0 20rpx;
151
+ flex-shrink: 0;
152
+ display: inline-flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ }
156
+
157
+ &__inner-wrap {
158
+ display: inline-flex;
159
+ align-items: center;
160
+ justify-content: center;
161
+ vertical-align: middle;
162
+ line-height: 1;
163
+ }
164
+
165
+ &__icon-box {
166
+ display: inline-flex;
167
+ align-items: center;
168
+ justify-content: center;
169
+ margin-right: 8rpx;
170
+ line-height: 1;
171
+ }
172
+
173
+ &__text {
174
+ font-size: 22rpx;
175
+ color: #94A3B8;
176
+ font-weight: 500;
177
+ letter-spacing: 0.5rpx;
178
+ line-height: 1;
179
+ display: inline-block;
180
+ vertical-align: middle;
181
+ }
182
+ }
183
+
184
+ .mu-dark, page.mu-dark {
185
+
186
+ .mu-divider {
187
+ &--horizontal {
188
+ .mu-divider__line {
189
+ border-color: rgba(255, 255, 255, 0.08);
190
+ }
191
+ &.mu-divider--gradient .mu-divider__line--left {
192
+ background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.12));
193
+ }
194
+ &.mu-divider--gradient .mu-divider__line--right {
195
+ background: linear-gradient(to left, transparent, rgba(255, 255, 255, 0.12));
196
+ }
197
+ }
198
+ &--vertical .mu-divider__vertical-line {
199
+ background: rgba(255, 255, 255, 0.12);
200
+ }
201
+ &__text {
202
+ color: $mu-dark-text-secondary;
203
+ }
204
+ }
205
+ }
206
+
207
+ @media (prefers-color-scheme: dark) {
208
+
209
+ .mu-divider {
210
+ &--horizontal {
211
+ .mu-divider__line {
212
+ border-color: rgba(255, 255, 255, 0.08);
213
+ }
214
+ &.mu-divider--gradient .mu-divider__line--left {
215
+ background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.12));
216
+ }
217
+ &.mu-divider--gradient .mu-divider__line--right {
218
+ background: linear-gradient(to left, transparent, rgba(255, 255, 255, 0.12));
219
+ }
220
+ }
221
+ &--vertical .mu-divider__vertical-line {
222
+ background: rgba(255, 255, 255, 0.12);
223
+ }
224
+ &__text {
225
+ color: $mu-dark-text-secondary;
226
+ }
227
+ }
228
+ }
229
+ </style>
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <view v-if="visible" class="mu-drawer" @tap="onMaskTap">
3
+ <view class="mu-drawer__mask" :class="{ 'mu-drawer__mask--show': panelShow }"></view>
4
+ <view
5
+ class="mu-drawer__panel"
6
+ :class="[`mu-drawer__panel--${realMode}`, panelShow && 'mu-drawer__panel--show']"
7
+ :style="panelStyle"
8
+ @tap.stop
9
+ >
10
+ <!-- 顶部标题栏 -->
11
+ <view v-if="title" class="mu-drawer__header">
12
+ <text class="mu-drawer__title">{{ title }}</text>
13
+ <view class="mu-drawer__close mu-touchable-flat" @tap="onMaskTap">
14
+ <mu-icon name="close" :size="18" color="#94A3B8" />
15
+ </view>
16
+ </view>
17
+ <slot></slot>
18
+ </view>
19
+ </view>
20
+ </template>
21
+
22
+ <script setup>
23
+ import { ref, computed, watch } from 'vue'
24
+
25
+ const props = defineProps({
26
+ show: { type: Boolean, default: false },
27
+ // 兼容 mode 与 placement 两种写法
28
+ mode: { type: String, default: '' },
29
+ placement: { type: String, default: 'left' }, // left | right | bottom
30
+ title: { type: String, default: '' },
31
+ width: { type: String, default: '560rpx' },
32
+ height: { type: String, default: '60vh' }, // bottom 模式高度
33
+ maskClosable: { type: Boolean, default: true }
34
+ })
35
+
36
+ const emit = defineEmits(['close', 'update:show'])
37
+
38
+ const visible = ref(false)
39
+ const panelShow = ref(false)
40
+
41
+ // 兼容 mode 与 placement
42
+ const realMode = computed(() => props.mode || props.placement || 'left')
43
+
44
+ const panelStyle = computed(() => {
45
+ if (realMode.value === 'bottom') {
46
+ return { height: props.height }
47
+ }
48
+ return { width: props.width }
49
+ })
50
+
51
+ watch(() => props.show, (val) => {
52
+ if (val) {
53
+ visible.value = true
54
+ setTimeout(() => { panelShow.value = true }, 20)
55
+ } else {
56
+ panelShow.value = false
57
+ setTimeout(() => { visible.value = false }, 300)
58
+ }
59
+ })
60
+
61
+ function onMaskTap() {
62
+ if (!props.maskClosable) return
63
+ panelShow.value = false
64
+ emit('update:show', false)
65
+ setTimeout(() => { visible.value = false; emit('close') }, 300)
66
+ }
67
+
68
+ defineExpose({
69
+ open: () => { visible.value = true; emit('update:show', true); setTimeout(() => panelShow.value = true, 20) },
70
+ close: onMaskTap
71
+ })
72
+ </script>
73
+
74
+ <style lang="scss" scoped>
75
+ @import "@/uni.scss";
76
+
77
+ .mu-drawer {
78
+ position: fixed;
79
+ inset: 0;
80
+ z-index: $mu-z-popup;
81
+
82
+ &__mask {
83
+ position: absolute;
84
+ inset: 0;
85
+ background: rgba(15, 23, 42, 0.45);
86
+ opacity: 0;
87
+ transition: opacity $mu-duration-slow;
88
+
89
+ &--show { opacity: 1; }
90
+ }
91
+
92
+ &__panel {
93
+ position: absolute;
94
+ background: #ffffff;
95
+ transition: transform $mu-duration-slow $mu-ease;
96
+ overflow-y: auto;
97
+ box-shadow: 0 0 40rpx rgba(15, 23, 42, 0.12);
98
+
99
+ /* 左侧抽屉 */
100
+ &--left {
101
+ top: 0; bottom: 0; left: 0;
102
+ transform: translateX(-100%);
103
+ border-radius: 0 $mu-radius-xl $mu-radius-xl 0;
104
+
105
+ &.mu-drawer__panel--show { transform: translateX(0); }
106
+ }
107
+
108
+ /* 右侧抽屉 */
109
+ &--right {
110
+ top: 0; bottom: 0; right: 0;
111
+ transform: translateX(100%);
112
+ border-radius: $mu-radius-xl 0 0 $mu-radius-xl;
113
+
114
+ &.mu-drawer__panel--show { transform: translateX(0); }
115
+ }
116
+
117
+ /* 底部抽屉 */
118
+ &--bottom {
119
+ left: 0; right: 0; bottom: 0;
120
+ transform: translateY(100%);
121
+ border-radius: $mu-radius-xl $mu-radius-xl 0 0;
122
+ width: 100% !important;
123
+
124
+ &.mu-drawer__panel--show { transform: translateY(0); }
125
+ }
126
+ }
127
+
128
+ &__header {
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: space-between;
132
+ padding: 28rpx 24rpx 16rpx;
133
+ border-bottom: 1rpx solid rgba(226, 232, 240, 0.6);
134
+ }
135
+
136
+ &__title {
137
+ font-size: 30rpx;
138
+ font-weight: 800;
139
+ color: $mu-text-primary;
140
+ }
141
+
142
+ &__close {
143
+ width: 56rpx;
144
+ height: 56rpx;
145
+ border-radius: 50%;
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ background: rgba(148, 163, 184, 0.1);
150
+ }
151
+ }
152
+
153
+ .mu-dark, page.mu-dark {
154
+
155
+ .mu-drawer {
156
+ &__mask { background: rgba(0, 0, 0, 0.6); }
157
+ &__panel { background: #1E1E2E; }
158
+ &__header { border-bottom-color: rgba(255, 255, 255, 0.08); }
159
+ &__title { color: $mu-dark-text-primary; }
160
+ }
161
+ }
162
+
163
+ @media (prefers-color-scheme: dark) {
164
+
165
+ .mu-drawer {
166
+ &__mask { background: rgba(0, 0, 0, 0.6); }
167
+ &__panel { background: #1E1E2E; }
168
+ &__header { border-bottom-color: rgba(255, 255, 255, 0.08); }
169
+ &__title { color: $mu-dark-text-primary; }
170
+ }
171
+ }
172
+ </style>