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,382 @@
1
+ <template>
2
+ <view class="mu-tabbar" :class="[
3
+ fixed && 'mu-tabbar--fixed',
4
+ `mu-tabbar--${mode}`
5
+ ]">
6
+ <!-- raised 模式:凹弧缺口底栏 -->
7
+ <template v-if="mode === 'raised'">
8
+ <view class="mu-tabbar__raised-bar">
9
+ <!-- 左半栏 -->
10
+ <view class="mu-tabbar__raised-half mu-tabbar__raised-half--left">
11
+ <view
12
+ v-for="(item, index) in leftItems"
13
+ :key="index"
14
+ class="mu-tabbar__item mu-touchable-flat"
15
+ :class="{ 'mu-tabbar__item--active': index === current }"
16
+ @tap="handleClick(index)"
17
+ >
18
+ <mu-badge :count="item.badge" :is-dot="item.dot">
19
+ <mu-icon v-if="isVectorIcon(item)" :name="getIconName(item, index === current)" :size="22" :color="index === current ? (activeColor || '#171717') : '#94A3B8'" />
20
+ <image v-else-if="item.icon" class="mu-tabbar__icon" :src="index === current ? (item.selectedIcon || item.icon) : item.icon" mode="aspectFit" />
21
+ </mu-badge>
22
+ <text class="mu-tabbar__text" :style="{ color: index === current ? (activeColor || '#171717') : '#64748B' }">{{ item.text }}</text>
23
+ </view>
24
+ </view>
25
+
26
+ <!-- 中间凹弧缺口区 -->
27
+ <view class="mu-tabbar__notch">
28
+ <!-- 凹弧背景:左弧 + 缺口 + 右弧 -->
29
+ <view class="mu-tabbar__notch-bg">
30
+ <view class="mu-tabbar__notch-curve mu-tabbar__notch-curve--left"></view>
31
+ <view class="mu-tabbar__notch-hole"></view>
32
+ <view class="mu-tabbar__notch-curve mu-tabbar__notch-curve--right"></view>
33
+ </view>
34
+ <!-- 凸起圆形按钮 -->
35
+ <view class="mu-tabbar__raised-btn" :style="raisedBtnStyle" @tap="handleClick(raisedIndex)">
36
+ <mu-icon v-if="isVectorIcon(raisedItem)" :name="getIconName(raisedItem, raisedIndex === current)" :size="26" color="#FFFFFF" />
37
+ </view>
38
+ </view>
39
+
40
+ <!-- 右半栏 -->
41
+ <view class="mu-tabbar__raised-half mu-tabbar__raised-half--right">
42
+ <view
43
+ v-for="(item, index) in rightItems"
44
+ :key="raisedIndex + 1 + index"
45
+ class="mu-tabbar__item mu-touchable-flat"
46
+ :class="{ 'mu-tabbar__item--active': (raisedIndex + 1 + index) === current }"
47
+ @tap="handleClick(raisedIndex + 1 + index)"
48
+ >
49
+ <mu-badge :count="item.badge" :is-dot="item.dot">
50
+ <mu-icon v-if="isVectorIcon(item)" :name="getIconName(item, (raisedIndex + 1 + index) === current)" :size="22" :color="(raisedIndex + 1 + index) === current ? (activeColor || '#171717') : '#94A3B8'" />
51
+ <image v-else-if="item.icon" class="mu-tabbar__icon" :src="(raisedIndex + 1 + index) === current ? (item.selectedIcon || item.icon) : item.icon" mode="aspectFit" />
52
+ </mu-badge>
53
+ <text class="mu-tabbar__text" :style="{ color: (raisedIndex + 1 + index) === current ? (activeColor || '#171717') : '#64748B' }">{{ item.text }}</text>
54
+ </view>
55
+ </view>
56
+ </view>
57
+ </template>
58
+
59
+ <!-- normal / island 模式 -->
60
+ <template v-else>
61
+ <view class="mu-tabbar__bar" :style="barStyle">
62
+ <view
63
+ v-for="(item, index) in items"
64
+ :key="index"
65
+ class="mu-tabbar__item mu-touchable-flat"
66
+ :class="{ 'mu-tabbar__item--active': index === current }"
67
+ @tap="handleClick(index)"
68
+ >
69
+ <mu-badge :count="item.badge" :is-dot="item.dot">
70
+ <mu-icon v-if="isVectorIcon(item)" :name="getIconName(item, index === current)" :size="22" :color="index === current ? (activeColor || '#171717') : '#94A3B8'" />
71
+ <image v-else-if="item.icon" class="mu-tabbar__icon" :src="index === current ? (item.selectedIcon || item.icon) : item.icon" mode="aspectFit" />
72
+ </mu-badge>
73
+ <text class="mu-tabbar__text" :style="{ color: index === current ? (activeColor || '#171717') : '#64748B' }">{{ item.text }}</text>
74
+ </view>
75
+ </view>
76
+ </template>
77
+ </view>
78
+ <!-- 占位垫高 -->
79
+ <view v-if="fixed" class="mu-tabbar__placeholder" :class="[`mu-tabbar__placeholder--${mode}`]"></view>
80
+ </template>
81
+
82
+ <script setup>
83
+ import { computed } from 'vue'
84
+
85
+ const props = defineProps({
86
+ items: { type: Array, default: () => [] },
87
+ current: { type: Number, default: 0 },
88
+ fixed: { type: Boolean, default: true },
89
+ mode: { type: String, default: 'normal' }, // normal | island | raised
90
+ background: { type: String, default: '' },
91
+ activeColor: { type: String, default: '' },
92
+ raisedColor: { type: String, default: '' }
93
+ })
94
+
95
+ const emit = defineEmits(['change'])
96
+
97
+ const barStyle = computed(() => {
98
+ if (props.background) return { background: props.background }
99
+ return {}
100
+ })
101
+
102
+ const raisedBtnStyle = computed(() => {
103
+ const bg = props.raisedColor || props.activeColor || '#171717'
104
+ return { background: `linear-gradient(135deg, ${bg}, ${adjustColor(bg, 30)})` }
105
+ })
106
+
107
+ // 找到 raised: true 的项索引
108
+ const raisedIndex = computed(() => {
109
+ const idx = props.items.findIndex(i => i.raised)
110
+ return idx >= 0 ? idx : Math.floor(props.items.length / 2)
111
+ })
112
+
113
+ const raisedItem = computed(() => props.items[raisedIndex.value] || {})
114
+ const leftItems = computed(() => props.items.slice(0, raisedIndex.value))
115
+ const rightItems = computed(() => props.items.slice(raisedIndex.value + 1))
116
+
117
+ function adjustColor(hex, amount) {
118
+ try {
119
+ let c = hex.replace('#', '')
120
+ if (c.length === 3) c = c.split('').map(ch => ch + ch).join('')
121
+ const num = parseInt(c, 16)
122
+ let r = Math.min(255, (num >> 16) + amount)
123
+ let g = Math.min(255, ((num >> 8) & 0x00FF) + amount)
124
+ let b = Math.min(255, (num & 0x0000FF) + amount)
125
+ return `#${(1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1)}`
126
+ } catch { return hex }
127
+ }
128
+
129
+ function isVectorIcon(item) {
130
+ const iconStr = item.icon || ''
131
+ if (!iconStr) return false
132
+ return !iconStr.includes('/') && !/\.(png|jpg|jpeg|gif|svg|webp)$/i.test(iconStr)
133
+ }
134
+
135
+ function getIconName(item, isSelected) {
136
+ if (isSelected && item.selectedIcon) return item.selectedIcon
137
+ return item.icon || 'home'
138
+ }
139
+
140
+ function handleClick(index) {
141
+ if (index !== props.current) emit('change', index)
142
+ }
143
+ </script>
144
+
145
+ <style lang="scss" scoped>
146
+ @import "@/uni.scss";
147
+
148
+ .mu-tabbar {
149
+ width: 100%;
150
+ z-index: $mu-z-fixed;
151
+
152
+ &--fixed {
153
+ position: fixed;
154
+ bottom: 0;
155
+ left: 0;
156
+ right: 0;
157
+ box-sizing: border-box;
158
+ }
159
+
160
+ /* ========== 普通贴底 (normal) ========== */
161
+ &--normal &__bar {
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: space-around;
165
+ height: 100rpx;
166
+ background: #ffffff;
167
+ border-top: 1rpx solid rgba(226, 232, 240, 0.8);
168
+ padding-bottom: env(safe-area-inset-bottom, 0);
169
+ }
170
+
171
+ /* ========== 悬浮浮岛 (island) ========== */
172
+ &--island {
173
+ &.mu-tabbar--fixed {
174
+ bottom: 24rpx;
175
+ padding: 0 24rpx;
176
+ pointer-events: none;
177
+ }
178
+ }
179
+ &--island &__bar {
180
+ pointer-events: auto;
181
+ display: flex;
182
+ align-items: center;
183
+ justify-content: space-around;
184
+ height: 104rpx;
185
+ background: rgba(255, 255, 255, 0.92);
186
+ backdrop-filter: blur(24px);
187
+ border-radius: 999rpx;
188
+ border: 1rpx solid rgba(255, 255, 255, 0.8);
189
+ box-shadow: 0 16rpx 40rpx -8rpx rgba(15, 23, 42, 0.15);
190
+ margin: 0 auto;
191
+ max-width: 680rpx;
192
+ padding: 0 12rpx;
193
+ box-sizing: border-box;
194
+ }
195
+
196
+ /* ========== 异形凹弧缺口 (raised) ========== */
197
+ &__raised-bar {
198
+ display: flex;
199
+ align-items: stretch;
200
+ height: 100rpx;
201
+ background: #ffffff;
202
+ border-top: 1rpx solid rgba(226, 232, 240, 0.8);
203
+ padding-bottom: env(safe-area-inset-bottom, 0);
204
+ position: relative;
205
+ }
206
+
207
+ &__raised-half {
208
+ flex: 1;
209
+ display: flex;
210
+ align-items: center;
211
+ justify-content: space-around;
212
+ }
213
+
214
+ /* 凹弧缺口容器 */
215
+ &__notch {
216
+ width: 140rpx;
217
+ position: relative;
218
+ display: flex;
219
+ align-items: flex-start;
220
+ justify-content: center;
221
+ }
222
+
223
+ /* 凹弧背景形状 */
224
+ &__notch-bg {
225
+ position: absolute;
226
+ top: -1rpx;
227
+ left: 0;
228
+ right: 0;
229
+ height: 52rpx;
230
+ display: flex;
231
+ align-items: flex-start;
232
+ }
233
+
234
+ /* 左右两侧的凹弧圆角 */
235
+ &__notch-curve {
236
+ width: 24rpx;
237
+ height: 24rpx;
238
+ background: transparent;
239
+ position: relative;
240
+ overflow: hidden;
241
+
242
+ &--left {
243
+ border-bottom-right-radius: 24rpx;
244
+ box-shadow: 10rpx 10rpx 0 0 #ffffff;
245
+ margin-top: 0;
246
+ }
247
+ &--right {
248
+ border-bottom-left-radius: 24rpx;
249
+ box-shadow: -10rpx 10rpx 0 0 #ffffff;
250
+ margin-top: 0;
251
+ }
252
+ }
253
+
254
+ /* 中间圆弧缺口(透明让出空间) */
255
+ &__notch-hole {
256
+ flex: 1;
257
+ background: transparent;
258
+ }
259
+
260
+ /* 凸起圆形按钮 */
261
+ &__raised-btn {
262
+ width: 104rpx;
263
+ height: 104rpx;
264
+ border-radius: 50%;
265
+ display: flex;
266
+ align-items: center;
267
+ justify-content: center;
268
+ box-shadow: 0 8rpx 28rpx -4rpx rgba(99, 102, 241, 0.5);
269
+ margin-top: -52rpx;
270
+ border: 6rpx solid #ffffff;
271
+ box-sizing: border-box;
272
+ position: relative;
273
+ z-index: 10;
274
+ transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
275
+
276
+ &:active {
277
+ transform: scale(0.9);
278
+ }
279
+ }
280
+
281
+ /* ========== 通用 ========== */
282
+ &__item {
283
+ flex: 1;
284
+ display: flex;
285
+ flex-direction: column;
286
+ align-items: center;
287
+ justify-content: center;
288
+ position: relative;
289
+ height: 100%;
290
+ cursor: pointer;
291
+
292
+ &--active .mu-tabbar__text { font-weight: 800; }
293
+ }
294
+
295
+ &__icon-wrap {
296
+ position: relative;
297
+ display: flex;
298
+ align-items: center;
299
+ justify-content: center;
300
+ margin-bottom: 4rpx;
301
+ }
302
+
303
+ &__icon { width: 44rpx; height: 44rpx; display: block; }
304
+
305
+ &__badge {
306
+ position: absolute;
307
+ top: -8rpx;
308
+ right: -20rpx;
309
+ min-width: 32rpx;
310
+ height: 32rpx;
311
+ border-radius: 999rpx;
312
+ background: #F43F5E;
313
+ display: flex;
314
+ align-items: center;
315
+ justify-content: center;
316
+ padding: 0 8rpx;
317
+ box-sizing: border-box;
318
+
319
+ &-text { font-size: 18rpx; color: #FFFFFF; font-weight: 700; line-height: 1; }
320
+ }
321
+
322
+ &__dot {
323
+ position: absolute;
324
+ top: -2rpx;
325
+ right: -6rpx;
326
+ width: 12rpx;
327
+ height: 12rpx;
328
+ border-radius: 50%;
329
+ background: #F43F5E;
330
+ }
331
+
332
+ &__text {
333
+ font-size: 20rpx;
334
+ font-weight: 600;
335
+ line-height: 1;
336
+ transition: color 0.2s ease;
337
+ }
338
+
339
+ &__placeholder {
340
+ &--normal, &--raised { height: calc(100rpx + env(safe-area-inset-bottom, 0px)); }
341
+ &--island { height: 152rpx; }
342
+ }
343
+ }
344
+
345
+ .mu-dark, page.mu-dark {
346
+
347
+ .mu-tabbar {
348
+ &--normal &__bar,
349
+ &__raised-bar {
350
+ background: rgba(18, 18, 24, 0.98);
351
+ border-top-color: rgba(255, 255, 255, 0.08);
352
+ }
353
+ &--island &__bar {
354
+ background: rgba(18, 18, 24, 0.92);
355
+ border-color: rgba(255, 255, 255, 0.1);
356
+ }
357
+ &__notch-curve--left { box-shadow: 10rpx 10rpx 0 0 rgba(18, 18, 24, 0.98); }
358
+ &__notch-curve--right { box-shadow: -10rpx 10rpx 0 0 rgba(18, 18, 24, 0.98); }
359
+ &__raised-btn { border-color: rgba(18, 18, 24, 0.98); }
360
+ &__text { color: $mu-dark-text-secondary; }
361
+ }
362
+ }
363
+
364
+ @media (prefers-color-scheme: dark) {
365
+
366
+ .mu-tabbar {
367
+ &--normal &__bar,
368
+ &__raised-bar {
369
+ background: rgba(18, 18, 24, 0.98);
370
+ border-top-color: rgba(255, 255, 255, 0.08);
371
+ }
372
+ &--island &__bar {
373
+ background: rgba(18, 18, 24, 0.92);
374
+ border-color: rgba(255, 255, 255, 0.1);
375
+ }
376
+ &__notch-curve--left { box-shadow: 10rpx 10rpx 0 0 rgba(18, 18, 24, 0.98); }
377
+ &__notch-curve--right { box-shadow: -10rpx 10rpx 0 0 rgba(18, 18, 24, 0.98); }
378
+ &__raised-btn { border-color: rgba(18, 18, 24, 0.98); }
379
+ &__text { color: $mu-dark-text-secondary; }
380
+ }
381
+ }
382
+ </style>