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,381 @@
1
+ <template>
2
+ <view class="mu-index-list">
3
+ <!-- 滚动容器 -->
4
+ <scroll-view
5
+ scroll-y
6
+ class="mu-index-list__scroll"
7
+ :scroll-into-view="scrollIntoId"
8
+ :style="{ height: height }"
9
+ >
10
+ <view
11
+ v-for="(group, gi) in groups"
12
+ :key="gi"
13
+ :id="'mu-idx-' + getGroupId(group.index)"
14
+ class="mu-index-list__group"
15
+ >
16
+ <!-- 粘性标头 -->
17
+ <view class="mu-index-list__header" :class="[sticky && 'mu-index-list__header--sticky']">
18
+ <slot name="header" :group="group">
19
+ <text class="mu-index-list__header-text">{{ group.index }}</text>
20
+ </slot>
21
+ </view>
22
+
23
+ <!-- 分组项列表 -->
24
+ <view
25
+ v-for="(item, ii) in group.items"
26
+ :key="ii"
27
+ class="mu-index-list__item mu-touchable-flat"
28
+ @tap="handleClick(item)"
29
+ >
30
+ <slot name="item" :item="item" :index="ii">
31
+ <!-- 默认通讯录 / 城市卡片视图 -->
32
+ <view class="mu-index-list__card">
33
+ <!-- 头像 Avatar -->
34
+ <view v-if="item.avatar" class="mu-index-list__avatar-box">
35
+ <image class="mu-index-list__avatar" :src="item.avatar" mode="aspectFill" />
36
+ </view>
37
+ <view v-else-if="item.name || item[labelKey]" class="mu-index-list__avatar-text">
38
+ <text class="mu-index-list__avatar-char">{{ getFirstChar(item[labelKey] || item.name) }}</text>
39
+ </view>
40
+
41
+ <!-- 详细文案 -->
42
+ <view class="mu-index-list__info">
43
+ <text class="mu-index-list__name">{{ item[labelKey] || item.name || item }}</text>
44
+ <text v-if="item.desc || item.phone" class="mu-index-list__desc">{{ item.desc || item.phone }}</text>
45
+ </view>
46
+ </view>
47
+ </slot>
48
+ </view>
49
+ </view>
50
+ </scroll-view>
51
+
52
+ <!-- 右侧悬浮索引字母栏 Sidebar -->
53
+ <view
54
+ class="mu-index-list__sidebar"
55
+ @touchstart="onSidebarTouch"
56
+ @touchmove.prevent="onSidebarTouchMove"
57
+ @touchend="onSidebarTouchEnd"
58
+ >
59
+ <view
60
+ v-for="(group, gi) in groups"
61
+ :key="gi"
62
+ class="mu-index-list__sidebar-item"
63
+ :class="[activeChar === group.index && 'mu-index-list__sidebar-item--active']"
64
+ @tap="scrollToIndex(group.index)"
65
+ >
66
+ <text class="mu-index-list__sidebar-text">{{ group.index }}</text>
67
+ </view>
68
+ </view>
69
+
70
+ <!-- 触控拖拽时的中央大水滴 / 气泡提示器 -->
71
+ <view v-if="showTip && touchingChar" class="mu-index-list__tip">
72
+ <text class="mu-index-list__tip-text">{{ touchingChar }}</text>
73
+ </view>
74
+ </view>
75
+ </template>
76
+
77
+ <script setup>
78
+ import { ref, computed, getCurrentInstance } from 'vue'
79
+
80
+ const props = defineProps({
81
+ data: { type: Array, default: () => [] },
82
+ height: { type: String, default: '100vh' },
83
+ sticky: { type: Boolean, default: true },
84
+ indexKey: { type: String, default: 'index' },
85
+ labelKey: { type: String, default: 'label' },
86
+ showTip: { type: Boolean, default: true }
87
+ })
88
+
89
+ const emit = defineEmits(['click', 'select', 'change'])
90
+
91
+ const instance = getCurrentInstance()
92
+ const activeChar = ref('')
93
+ const touchingChar = ref('')
94
+ const scrollIntoId = ref('')
95
+ let tipTimer = null
96
+
97
+ function getGroupId(idx) {
98
+ return String(idx).replace(/[^a-zA-Z0-9]/g, '_')
99
+ }
100
+
101
+ function getFirstChar(str) {
102
+ if (!str) return '#'
103
+ return String(str).charAt(0).toUpperCase()
104
+ }
105
+
106
+ const groups = computed(() => {
107
+ if (props.data.length && Array.isArray((props.data[0] && props.data[0].items))) {
108
+ return props.data.map(g => ({ index: (g[props.indexKey] !== undefined ? g[props.indexKey] : '#'), items: g.items }))
109
+ }
110
+ // 扁平数据:按首字母自动分组
111
+ const map = {}
112
+ const order = []
113
+ for (const item of props.data) {
114
+ const label = item[props.labelKey] || item.name || String(item)
115
+ const idx = item[props.indexKey] || getFirstChar(label)
116
+ if (!map[idx]) {
117
+ map[idx] = []
118
+ order.push(idx)
119
+ }
120
+ map[idx].push(item)
121
+ }
122
+ return order.sort().map(idx => ({ index: idx, items: map[idx] }))
123
+ })
124
+
125
+ function handleClick(item) {
126
+ emit('click', item)
127
+ emit('select', item)
128
+ }
129
+
130
+ function scrollToIndex(idx) {
131
+ activeChar.value = idx
132
+ touchingChar.value = idx
133
+ scrollIntoId.value = 'mu-idx-' + getGroupId(idx)
134
+
135
+ emit('change', idx)
136
+
137
+ if (tipTimer) clearTimeout(tipTimer)
138
+ tipTimer = setTimeout(() => {
139
+ touchingChar.value = ''
140
+ }, 1200)
141
+ }
142
+
143
+ function locateTouch(e) {
144
+ if (!e || !e.touches || !e.touches[0]) return
145
+ const touch = e.touches[0]
146
+ if (!groups.value.length) return
147
+
148
+ const query = uni.createSelectorQuery().in((instance && instance.proxy))
149
+ query.select('.mu-index-list__sidebar').boundingClientRect((rect) => {
150
+ if (!rect || !rect.height) return
151
+ const itemH = rect.height / groups.value.length
152
+ let idx = Math.floor((touch.clientY - rect.top) / itemH)
153
+ idx = Math.max(0, Math.min(groups.value.length - 1, idx))
154
+ scrollToIndex(groups.value[idx].index)
155
+ }).exec()
156
+ }
157
+
158
+ function onSidebarTouch(e) {
159
+ locateTouch(e)
160
+ }
161
+
162
+ function onSidebarTouchMove(e) {
163
+ locateTouch(e)
164
+ }
165
+
166
+ function onSidebarTouchEnd() {
167
+ if (tipTimer) clearTimeout(tipTimer)
168
+ tipTimer = setTimeout(() => {
169
+ touchingChar.value = ''
170
+ }, 800)
171
+ }
172
+ </script>
173
+
174
+ <style lang="scss" scoped>
175
+ @import "@/uni.scss";
176
+
177
+ .mu-index-list {
178
+ position: relative;
179
+ width: 100%;
180
+ height: 100%;
181
+ box-sizing: border-box;
182
+
183
+ &__scroll {
184
+ width: 100%;
185
+ }
186
+
187
+ &__group {
188
+ width: 100%;
189
+ }
190
+
191
+ &__header {
192
+ padding: 12rpx 32rpx;
193
+ background: rgba(248, 250, 252, 0.95);
194
+ backdrop-filter: blur(8px);
195
+ border-bottom: 1rpx solid rgba(226, 232, 240, 0.8);
196
+
197
+ &--sticky {
198
+ position: sticky;
199
+ top: 0;
200
+ z-index: 5;
201
+ }
202
+
203
+ &-text {
204
+ font-size: 24rpx;
205
+ font-weight: 800;
206
+ color: #171717;
207
+ letter-spacing: 1rpx;
208
+ }
209
+ }
210
+
211
+ &__item {
212
+ background: #ffffff;
213
+ border-bottom: 1rpx solid rgba(226, 232, 240, 0.6);
214
+ cursor: pointer;
215
+ transition: background 0.15s ease;
216
+
217
+ &:last-child {
218
+ border-bottom: none;
219
+ }
220
+
221
+ &:active {
222
+ background: rgba(99, 102, 241, 0.04);
223
+ }
224
+ }
225
+
226
+ &__card {
227
+ display: flex;
228
+ align-items: center;
229
+ padding: 24rpx 32rpx;
230
+ }
231
+
232
+ &__avatar-box {
233
+ width: 72rpx;
234
+ height: 72rpx;
235
+ border-radius: 50%;
236
+ overflow: hidden;
237
+ margin-right: 20rpx;
238
+ flex-shrink: 0;
239
+ }
240
+
241
+ &__avatar {
242
+ width: 100%;
243
+ height: 100%;
244
+ }
245
+
246
+ &__avatar-text {
247
+ width: 72rpx;
248
+ height: 72rpx;
249
+ border-radius: 50%;
250
+ background: #171717;
251
+ display: flex;
252
+ align-items: center;
253
+ justify-content: center;
254
+ margin-right: 20rpx;
255
+ flex-shrink: 0;
256
+ box-shadow: 0 4rpx 12rpx rgba(99, 102, 241, 0.25);
257
+ }
258
+
259
+ &__avatar-char {
260
+ font-size: 28rpx;
261
+ font-weight: 800;
262
+ color: #ffffff;
263
+ }
264
+
265
+ &__info {
266
+ flex: 1;
267
+ min-width: 0;
268
+ display: flex;
269
+ flex-direction: column;
270
+ }
271
+
272
+ &__name {
273
+ font-size: 28rpx;
274
+ font-weight: 700;
275
+ color: $mu-text-primary;
276
+ line-height: 1.3;
277
+ }
278
+
279
+ &__desc {
280
+ font-size: 22rpx;
281
+ color: #94A3B8;
282
+ margin-top: 6rpx;
283
+ }
284
+
285
+ &__sidebar {
286
+ position: absolute;
287
+ right: 12rpx;
288
+ top: 50%;
289
+ transform: translateY(-50%);
290
+ display: flex;
291
+ flex-direction: column;
292
+ align-items: center;
293
+ padding: 12rpx 8rpx;
294
+ background: rgba(255, 255, 255, 0.85);
295
+ backdrop-filter: blur(12px);
296
+ border-radius: 30rpx;
297
+ border: 1rpx solid rgba(226, 232, 240, 0.8);
298
+ box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.08);
299
+ z-index: 20;
300
+ }
301
+
302
+ &__sidebar-item {
303
+ width: 36rpx;
304
+ height: 36rpx;
305
+ border-radius: 50%;
306
+ display: flex;
307
+ align-items: center;
308
+ justify-content: center;
309
+ margin: 2rpx 0;
310
+ transition: all 0.2s ease;
311
+
312
+ &--active {
313
+ background: #171717;
314
+ box-shadow: 0 4rpx 10rpx rgba(99, 102, 241, 0.35);
315
+
316
+ .mu-index-list__sidebar-text {
317
+ color: #ffffff;
318
+ font-weight: 800;
319
+ }
320
+ }
321
+ }
322
+
323
+ &__sidebar-text {
324
+ font-size: 18rpx;
325
+ color: #64748B;
326
+ font-weight: 600;
327
+ }
328
+
329
+ &__tip {
330
+ position: absolute;
331
+ top: 50%;
332
+ left: 50%;
333
+ transform: translate(-50%, -50%);
334
+ width: 120rpx;
335
+ height: 120rpx;
336
+ border-radius: 32rpx;
337
+ background: #171717;
338
+ box-shadow: 0 12rpx 36rpx rgba(99, 102, 241, 0.4);
339
+ display: flex;
340
+ align-items: center;
341
+ justify-content: center;
342
+ z-index: 100;
343
+ animation: mu-tip-bounce 0.2s ease;
344
+
345
+ &-text {
346
+ font-size: 48rpx;
347
+ color: #ffffff;
348
+ font-weight: 900;
349
+ }
350
+ }
351
+ }
352
+
353
+ @keyframes mu-tip-bounce {
354
+ from { opacity: 0; transform: translate(-50%, -50%) scale(0.7); }
355
+ to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
356
+ }
357
+
358
+ .mu-dark, page.mu-dark {
359
+
360
+ .mu-index-list {
361
+ &__header { background: rgba(30, 41, 59, 0.95); border-color: rgba(255, 255, 255, 0.08); &-text { color: #FFFFFF; } }
362
+ &__item { background: $mu-dark-bg-base; border-color: rgba(255, 255, 255, 0.06); }
363
+ &__name { color: $mu-dark-text-primary; }
364
+ &__desc { color: $mu-dark-text-secondary; }
365
+ &__sidebar { background: rgba(30, 41, 59, 0.85); border-color: rgba(255, 255, 255, 0.1); }
366
+ &__sidebar-text { color: $mu-dark-text-secondary; }
367
+ }
368
+ }
369
+
370
+ @media (prefers-color-scheme: dark) {
371
+
372
+ .mu-index-list {
373
+ &__header { background: rgba(30, 41, 59, 0.95); border-color: rgba(255, 255, 255, 0.08); &-text { color: #FFFFFF; } }
374
+ &__item { background: $mu-dark-bg-base; border-color: rgba(255, 255, 255, 0.06); }
375
+ &__name { color: $mu-dark-text-primary; }
376
+ &__desc { color: $mu-dark-text-secondary; }
377
+ &__sidebar { background: rgba(30, 41, 59, 0.85); border-color: rgba(255, 255, 255, 0.1); }
378
+ &__sidebar-text { color: $mu-dark-text-secondary; }
379
+ }
380
+ }
381
+ </style>