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,193 @@
1
+ <template>
2
+ <view class="mu-input-number" :class="{ 'mu-input-number--disabled': disabled }">
3
+ <!-- 减号按钮 -->
4
+ <view
5
+ class="mu-input-number__btn mu-input-number__btn--minus mu-touchable-flat"
6
+ :class="{ 'mu-input-number__btn--disabled': disabled || currentValue <= min }"
7
+ @tap="handleMinus"
8
+ >
9
+ <mu-icon name="minus" :size="12" :color="disabled || currentValue <= min ? '#94A3B8' : 'currentColor'" />
10
+ </view>
11
+
12
+ <!-- 输入/显示数字框 -->
13
+ <view class="mu-input-number__input-wrap">
14
+ <input v-if="editable" class="mu-input-number__input" type="number"
15
+ :value="String(currentValue)" :disabled="disabled"
16
+ @blur="onBlur" @input="onInput" />
17
+ <text v-else class="mu-input-number__display">{{ currentValue }}</text>
18
+ </view>
19
+
20
+ <!-- 加号按钮 -->
21
+ <view
22
+ class="mu-input-number__btn mu-input-number__btn--plus mu-touchable-flat"
23
+ :class="{ 'mu-input-number__btn--disabled': disabled || currentValue >= max }"
24
+ @tap="handlePlus"
25
+ >
26
+ <mu-icon name="plus" :size="12" :color="disabled || currentValue >= max ? '#94A3B8' : 'currentColor'" />
27
+ </view>
28
+ </view>
29
+ </template>
30
+
31
+ <script setup>
32
+ import { ref, watch } from 'vue'
33
+
34
+ const props = defineProps({
35
+ modelValue: { type: [Number, String], default: 1 },
36
+ value: { type: [Number, String], default: null }, // 兼容写 :value 的旧语法
37
+ min: { type: Number, default: 0 },
38
+ max: { type: Number, default: 9999 },
39
+ step: { type: Number, default: 1 },
40
+ disabled: { type: Boolean, default: false },
41
+ editable: { type: Boolean, default: false },
42
+ decimal: { type: Number, default: 0 }
43
+ })
44
+
45
+ const emit = defineEmits(['change', 'update:modelValue', 'update:value'])
46
+
47
+ const currentValue = ref(Number((props.modelValue !== undefined ? props.modelValue : (props.value !== undefined ? props.value : 1))))
48
+
49
+ watch(() => props.modelValue, (v) => {
50
+ if (v !== undefined && v !== null) currentValue.value = Number(v)
51
+ })
52
+ watch(() => props.value, (v) => {
53
+ if (v !== undefined && v !== null) currentValue.value = Number(v)
54
+ })
55
+
56
+ function clamp(val) {
57
+ let v = Number(val)
58
+ if (isNaN(v)) v = props.min
59
+ v = Math.max(props.min, Math.min(props.max, v))
60
+ if (props.decimal > 0) v = Number(v.toFixed(props.decimal))
61
+ return v
62
+ }
63
+
64
+ function setValue(v) {
65
+ const result = clamp(v)
66
+ currentValue.value = result
67
+ emit('change', result)
68
+ emit('update:modelValue', result)
69
+ emit('update:value', result)
70
+ }
71
+
72
+ function handleMinus() {
73
+ if (!props.disabled && currentValue.value > props.min) {
74
+ setValue(currentValue.value - props.step)
75
+ }
76
+ }
77
+
78
+ function handlePlus() {
79
+ if (!props.disabled && currentValue.value < props.max) {
80
+ setValue(currentValue.value + props.step)
81
+ }
82
+ }
83
+
84
+ function onBlur(e) {
85
+ setValue(e.detail.value)
86
+ }
87
+
88
+ function onInput(e) {
89
+ const v = Number(e.detail.value)
90
+ if (!isNaN(v)) currentValue.value = v
91
+ }
92
+ </script>
93
+
94
+ <style lang="scss" scoped>
95
+ @import "@/uni.scss";
96
+
97
+ .mu-input-number {
98
+ display: inline-flex;
99
+ align-items: center;
100
+ background: rgba(241, 245, 249, 0.7);
101
+ border-radius: 12rpx;
102
+ padding: 4rpx;
103
+ border: 1rpx solid rgba(226, 232, 240, 0.8);
104
+ box-sizing: border-box;
105
+
106
+ &--disabled {
107
+ opacity: $mu-opacity-disabled;
108
+ pointer-events: none;
109
+ }
110
+
111
+ &__btn {
112
+ width: 48rpx;
113
+ height: 48rpx;
114
+ border-radius: 8rpx;
115
+ background: #ffffff;
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ box-shadow: 0 2rpx 6rpx rgba(15, 23, 42, 0.08);
120
+ cursor: pointer;
121
+ transition: all 0.2s ease;
122
+
123
+ &:active {
124
+ background: rgba(241, 245, 249, 0.9);
125
+ transform: scale(0.95);
126
+ }
127
+
128
+ &--disabled {
129
+ background: transparent;
130
+ box-shadow: none;
131
+ cursor: not-allowed;
132
+ }
133
+ }
134
+
135
+ &__input-wrap {
136
+ min-width: 68rpx;
137
+ padding: 0 12rpx;
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ }
142
+
143
+ &__input {
144
+ width: 68rpx;
145
+ height: 48rpx;
146
+ text-align: center;
147
+ font-size: 26rpx;
148
+ font-weight: 700;
149
+ color: $mu-text-primary;
150
+ background: transparent;
151
+ }
152
+
153
+ &__display {
154
+ font-size: 26rpx;
155
+ font-weight: 700;
156
+ color: $mu-text-primary;
157
+ text-align: center;
158
+ }
159
+ }
160
+
161
+
162
+ .mu-dark, page.mu-dark {
163
+ .mu-input-number {
164
+ background: rgba(30, 30, 42, 0.9);
165
+ border-color: rgba(255, 255, 255, 0.1);
166
+ color: #F8FAFC;
167
+ &__btn {
168
+ background: rgba(45, 45, 60, 0.9);
169
+ color: #F8FAFC;
170
+ box-shadow: none !important;
171
+ }
172
+ &__display, &__input {
173
+ color: #F8FAFC !important;
174
+ }
175
+ }
176
+ }
177
+
178
+ @media (prefers-color-scheme: dark) {
179
+ .mu-input-number {
180
+ background: rgba(30, 30, 42, 0.9);
181
+ border-color: rgba(255, 255, 255, 0.1);
182
+ color: #F8FAFC;
183
+ &__btn {
184
+ background: rgba(45, 45, 60, 0.9);
185
+ color: #F8FAFC;
186
+ box-shadow: none !important;
187
+ }
188
+ &__display, &__input {
189
+ color: #F8FAFC !important;
190
+ }
191
+ }
192
+ }
193
+ </style>
@@ -0,0 +1,415 @@
1
+ <template>
2
+ <view v-if="visible" class="mu-keyboard" @tap="handleClose">
3
+ <view class="mu-keyboard__mask"></view>
4
+ <view class="mu-keyboard__panel" :class="{ 'mu-keyboard__panel--show': panelShow }" @tap.stop>
5
+ <!-- 顶部工具栏 Toolbar -->
6
+ <view v-if="showToolbar" class="mu-keyboard__toolbar">
7
+ <text class="mu-keyboard__cancel" @tap="handleClose">完成</text>
8
+ <text class="mu-keyboard__title">{{ title || computedDefaultTitle }}</text>
9
+ <view class="mu-keyboard__close" @tap="handleClose">
10
+ <mu-icon name="close" :size="16" color="#64748B" />
11
+ </view>
12
+ </view>
13
+
14
+ <!-- 键盘按键主容器 Keys Area -->
15
+ <view class="mu-keyboard__keys" :class="[mode === 'car' && 'mu-keyboard__keys--car']">
16
+ <!-- 车牌号模式: 省份简称键盘 (第1位) -->
17
+ <template v-if="mode === 'car' && currentCarPage === 'province'">
18
+ <view v-for="(row, ri) in carProvinces" :key="'prov-' + ri" class="mu-keyboard__row">
19
+ <view
20
+ v-for="(item, ki) in row"
21
+ :key="'prov-item-' + ki"
22
+ class="mu-keyboard__key"
23
+ :class="[item === 'del' && 'mu-keyboard__key--action']"
24
+ @tap="handleKey(item)"
25
+ >
26
+ <text v-if="item === 'del'" class="mu-keyboard__key-del">⌫</text>
27
+ <text v-else class="mu-keyboard__key-text">{{ item }}</text>
28
+ </view>
29
+ </view>
30
+ </template>
31
+
32
+ <!-- 车牌号模式: 数字字母键盘 (第2~8位) -->
33
+ <template v-else-if="mode === 'car' && currentCarPage === 'letter'">
34
+ <view v-for="(row, ri) in carLetters" :key="'let-' + ri" class="mu-keyboard__row">
35
+ <view
36
+ v-for="(item, ki) in row"
37
+ :key="'let-item-' + ki"
38
+ class="mu-keyboard__key"
39
+ :class="[
40
+ (item === 'del' || item === '省') && 'mu-keyboard__key--action',
41
+ item === '0' && 'mu-keyboard__key--wide'
42
+ ]"
43
+ @tap="handleKey(item)"
44
+ >
45
+ <text v-if="item === 'del'" class="mu-keyboard__key-del">⌫</text>
46
+ <text v-else-if="item === '省'" class="mu-keyboard__key-tag">省份</text>
47
+ <text v-else class="mu-keyboard__key-text">{{ item }}</text>
48
+ </view>
49
+ </view>
50
+ </template>
51
+
52
+ <!-- 标准数字 / 身份证键盘 (Standard Numeric / ID Card) -->
53
+ <template v-else>
54
+ <view v-for="(row, ri) in standardKeys" :key="'std-' + ri" class="mu-keyboard__row">
55
+ <view
56
+ v-for="(key, ki) in row"
57
+ :key="'std-key-' + ki"
58
+ class="mu-keyboard__key"
59
+ :class="[
60
+ key === '0' && 'mu-keyboard__key--wide',
61
+ (key === 'del' || key === '.' || key === 'X') && 'mu-keyboard__key--action'
62
+ ]"
63
+ @tap="handleKey(key)"
64
+ >
65
+ <text v-if="key === 'del'" class="mu-keyboard__key-del">⌫</text>
66
+ <text v-else class="mu-keyboard__key-text">{{ key }}</text>
67
+ </view>
68
+ </view>
69
+ </template>
70
+ </view>
71
+ </view>
72
+ </view>
73
+ </template>
74
+
75
+ <script setup>
76
+ import { ref, computed, watch } from 'vue'
77
+
78
+ const props = defineProps({
79
+ show: { type: Boolean, default: false },
80
+ title: { type: String, default: '' },
81
+ value: { type: String, default: '' },
82
+ mode: { type: String, default: 'number' }, // number | idcard | car
83
+ maxlength: { type: Number, default: 0 },
84
+ decimal: { type: Boolean, default: false },
85
+ showToolbar: { type: Boolean, default: true },
86
+ extraKey: { type: String, default: '' }
87
+ })
88
+
89
+ const emit = defineEmits(['input', 'change', 'close', 'update:show', 'update:value', 'delete', 'confirm'])
90
+
91
+ const visible = ref(false)
92
+ const panelShow = ref(false)
93
+
94
+ // 车牌号当前子页面: 'province' (省份) | 'letter' (字母数字)
95
+ const carStepManual = ref('')
96
+ const currentCarPage = computed(() => {
97
+ if (carStepManual.value) return carStepManual.value
98
+ if (!props.value || props.value.length === 0) return 'province'
99
+ return 'letter'
100
+ })
101
+
102
+ // 车牌号 - 省份简称矩阵 (31个省份及直辖市)
103
+ const carProvinces = [
104
+ ['京', '津', '沪', '渝', '冀', '豫', '云', '辽'],
105
+ ['黑', '湘', '皖', '鲁', '新', '苏', '浙', '赣'],
106
+ ['鄂', '桂', '甘', '晋', '蒙', '陕', '吉', '闽'],
107
+ ['贵', '粤', '青', '藏', '川', '宁', '琼', 'del']
108
+ ]
109
+
110
+ // 车牌号 - 数字与英文字母矩阵
111
+ const carLetters = [
112
+ ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
113
+ ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'P', '学', '港'],
114
+ ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', '澳'],
115
+ ['省', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '领', '使', 'del']
116
+ ]
117
+
118
+ // 标准 9 宫格 / 身份证数字按键矩阵
119
+ const standardKeys = computed(() => {
120
+ const rows = [
121
+ ['1', '2', '3'],
122
+ ['4', '5', '6'],
123
+ ['7', '8', '9']
124
+ ]
125
+ let bottomExtra = ''
126
+ if (props.mode === 'idcard') {
127
+ bottomExtra = 'X'
128
+ } else if (props.decimal) {
129
+ bottomExtra = '.'
130
+ } else if (props.extraKey) {
131
+ bottomExtra = props.extraKey
132
+ }
133
+
134
+ rows.push([bottomExtra, '0', 'del'])
135
+ return rows
136
+ })
137
+
138
+ const computedDefaultTitle = computed(() => {
139
+ if (props.mode === 'car') return '请输入车牌号码'
140
+ if (props.mode === 'idcard') return '请输入身份证号码'
141
+ return '安全数字键盘'
142
+ })
143
+
144
+ function handleKey(key) {
145
+ if (!key) return
146
+
147
+ // 切换回省份选择
148
+ if (key === '省') {
149
+ carStepManual.value = 'province'
150
+ return
151
+ }
152
+
153
+ // 删除按键
154
+ if (key === 'del') {
155
+ const v = props.value.slice(0, -1)
156
+ emit('change', v)
157
+ emit('update:value', v)
158
+ emit('delete')
159
+ if (props.mode === 'car' && v.length === 0) {
160
+ carStepManual.value = 'province'
161
+ }
162
+ return
163
+ }
164
+
165
+ // 最大长度防御判断
166
+ const max = props.maxlength > 0 ? props.maxlength : (props.mode === 'car' ? 8 : (props.mode === 'idcard' ? 18 : 0))
167
+ if (max > 0 && props.value.length >= max) return
168
+
169
+ const v = props.value + key
170
+ emit('change', v)
171
+ emit('update:value', v)
172
+ emit('input', key)
173
+
174
+ // 车牌号模式下,录入首个省份字后自动切换到数字字母页
175
+ if (props.mode === 'car' && currentCarPage.value === 'province') {
176
+ carStepManual.value = 'letter'
177
+ }
178
+ }
179
+
180
+ function handleClose() {
181
+ panelShow.value = false
182
+ emit('update:show', false)
183
+ emit('confirm', props.value)
184
+ setTimeout(() => {
185
+ visible.value = false
186
+ emit('close')
187
+ }, 300)
188
+ }
189
+
190
+ watch(() => props.show, (val) => {
191
+ if (val) {
192
+ visible.value = true
193
+ carStepManual.value = ''
194
+ setTimeout(() => { panelShow.value = true }, 20)
195
+ } else {
196
+ panelShow.value = false
197
+ setTimeout(() => { visible.value = false }, 300)
198
+ }
199
+ })
200
+
201
+ watch(() => props.value, (val) => {
202
+ if (!val || val.length === 0) {
203
+ carStepManual.value = 'province'
204
+ }
205
+ })
206
+ </script>
207
+
208
+ <style lang="scss" scoped>
209
+ @import "@/uni.scss";
210
+
211
+ .mu-keyboard {
212
+ position: fixed;
213
+ inset: 0;
214
+ z-index: $mu-z-popup;
215
+ display: flex;
216
+ flex-direction: column;
217
+ justify-content: flex-end;
218
+
219
+ &__mask {
220
+ position: absolute;
221
+ inset: 0;
222
+ background: rgba(15, 23, 42, 0.4);
223
+ backdrop-filter: blur(2px);
224
+ }
225
+
226
+ &__panel {
227
+ position: relative;
228
+ background: #F1F5F9;
229
+ padding-bottom: max(env(safe-area-inset-bottom), 16rpx);
230
+ transform: translateY(100%);
231
+ transition: transform 0.3s $mu-ease;
232
+ border-top-left-radius: 32rpx;
233
+ border-top-right-radius: 32rpx;
234
+ overflow: hidden;
235
+
236
+ &--show {
237
+ transform: translateY(0);
238
+ }
239
+ }
240
+
241
+ &__toolbar {
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: space-between;
245
+ padding: 24rpx 32rpx;
246
+ background: #FFFFFF;
247
+ border-bottom: 1rpx solid #E2E8F0;
248
+ }
249
+
250
+ &__cancel {
251
+ font-size: 28rpx;
252
+ color: $mu-primary;
253
+ font-weight: 700;
254
+ cursor: pointer;
255
+ }
256
+
257
+ &__title {
258
+ font-size: 26rpx;
259
+ color: $mu-text-primary;
260
+ font-weight: 700;
261
+ }
262
+
263
+ &__close {
264
+ width: 44rpx;
265
+ height: 44rpx;
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ cursor: pointer;
270
+ }
271
+
272
+ &__keys {
273
+ padding: 16rpx;
274
+ box-sizing: border-box;
275
+
276
+ &--car {
277
+ padding: 12rpx 8rpx;
278
+ }
279
+ }
280
+
281
+ &__row {
282
+ display: flex;
283
+ gap: 12rpx;
284
+ margin-bottom: 12rpx;
285
+
286
+ &:last-child {
287
+ margin-bottom: 0;
288
+ }
289
+ }
290
+
291
+ &__key {
292
+ flex: 1;
293
+ height: 96rpx;
294
+ background: #FFFFFF;
295
+ border-radius: 14rpx;
296
+ display: flex;
297
+ align-items: center;
298
+ justify-content: center;
299
+ cursor: pointer;
300
+ box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.04);
301
+ transition: all 0.15s ease;
302
+
303
+ &:active {
304
+ background: #E2E8F0;
305
+ transform: scale(0.96);
306
+ }
307
+
308
+ &--wide {
309
+ flex: 2;
310
+ }
311
+
312
+ &--action {
313
+ background: #CBD5E1;
314
+ box-shadow: none;
315
+
316
+ &:active {
317
+ background: #94A3B8;
318
+ }
319
+ }
320
+
321
+ &-text {
322
+ font-size: 32rpx;
323
+ color: #0F172A;
324
+ font-weight: 600;
325
+ }
326
+
327
+ &-tag {
328
+ font-size: 24rpx;
329
+ color: #334155;
330
+ font-weight: 700;
331
+ }
332
+
333
+ &-del {
334
+ font-size: 36rpx;
335
+ color: #334155;
336
+ }
337
+ }
338
+ }
339
+
340
+ /* 车牌号专用小紧凑按键 */
341
+ .mu-keyboard__keys--car {
342
+ .mu-keyboard__row {
343
+ gap: 8rpx;
344
+ margin-bottom: 10rpx;
345
+ }
346
+
347
+ .mu-keyboard__key {
348
+ height: 84rpx;
349
+ border-radius: 10rpx;
350
+
351
+ &-text {
352
+ font-size: 28rpx;
353
+ font-weight: 600;
354
+ }
355
+ }
356
+ }
357
+
358
+ .mu-dark, page.mu-dark {
359
+ .mu-keyboard {
360
+ &__panel {
361
+ background: #0F172A;
362
+ }
363
+ &__toolbar {
364
+ background: #1E293B;
365
+ border-color: rgba(255, 255, 255, 0.1);
366
+ }
367
+ &__title {
368
+ color: $mu-dark-text-primary;
369
+ }
370
+ &__key {
371
+ background: #1E293B;
372
+ box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.2);
373
+
374
+ &:active { background: #334155; }
375
+
376
+ &--action {
377
+ background: #334155;
378
+ &:active { background: #475569; }
379
+ }
380
+
381
+ &-text { color: $mu-dark-text-primary; }
382
+ &-tag, &-del { color: $mu-dark-text-primary; }
383
+ }
384
+ }
385
+ }
386
+
387
+ @media (prefers-color-scheme: dark) {
388
+ .mu-keyboard {
389
+ &__panel {
390
+ background: #0F172A;
391
+ }
392
+ &__toolbar {
393
+ background: #1E293B;
394
+ border-color: rgba(255, 255, 255, 0.1);
395
+ }
396
+ &__title {
397
+ color: $mu-dark-text-primary;
398
+ }
399
+ &__key {
400
+ background: #1E293B;
401
+ box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.2);
402
+
403
+ &:active { background: #334155; }
404
+
405
+ &--action {
406
+ background: #334155;
407
+ &:active { background: #475569; }
408
+ }
409
+
410
+ &-text { color: $mu-dark-text-primary; }
411
+ &-tag, &-del { color: $mu-dark-text-primary; }
412
+ }
413
+ }
414
+ }
415
+ </style>
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <view class="mu-list" :class="{ 'mu-list--bordered': bordered, 'mu-list--card': card }">
3
+ <view v-if="header || $slots.header" class="mu-list__header">
4
+ <slot name="header"><text class="mu-list__header-text">{{ header }}</text></slot>
5
+ </view>
6
+ <view class="mu-list__body">
7
+ <slot></slot>
8
+ </view>
9
+ <view v-if="footer || $slots.footer" class="mu-list__footer">
10
+ <slot name="footer"><text class="mu-list__footer-text">{{ footer }}</text></slot>
11
+ </view>
12
+ </view>
13
+ </template>
14
+
15
+ <script setup>
16
+ defineProps({
17
+ header: { type: String, default: '' },
18
+ footer: { type: String, default: '' },
19
+ bordered: { type: Boolean, default: false },
20
+ card: { type: Boolean, default: false }
21
+ })
22
+ </script>
23
+
24
+ <style lang="scss" scoped>
25
+ @import "@/uni.scss";
26
+
27
+ .mu-list {
28
+ background: #ffffff;
29
+ border-radius: 16rpx;
30
+ overflow: hidden;
31
+
32
+ &--bordered {
33
+ border: 1rpx solid rgba(226, 232, 240, 0.8);
34
+ }
35
+
36
+ &--card {
37
+ margin: 0 4rpx;
38
+ border-radius: 20rpx;
39
+ box-shadow: 0 2rpx 12rpx rgba(15, 23, 42, 0.03);
40
+ }
41
+
42
+ &__header {
43
+ padding: 20rpx 28rpx 12rpx;
44
+ background: transparent;
45
+
46
+ &-text {
47
+ font-size: 24rpx;
48
+ color: #94A3B8;
49
+ font-weight: 600;
50
+ text-transform: uppercase;
51
+ letter-spacing: 0.5rpx;
52
+ }
53
+ }
54
+
55
+ &__body {
56
+ position: relative;
57
+ }
58
+
59
+ &__footer {
60
+ padding: 12rpx 28rpx 20rpx;
61
+ background: transparent;
62
+
63
+ &-text {
64
+ font-size: 22rpx;
65
+ color: #94A3B8;
66
+ }
67
+ }
68
+ }
69
+
70
+ .mu-dark, page.mu-dark {
71
+
72
+ .mu-list {
73
+ background: $mu-dark-bg-base;
74
+ &--bordered { border-color: rgba(255, 255, 255, 0.08); }
75
+ &__header-text { color: $mu-dark-text-secondary; }
76
+ &__footer-text { color: $mu-dark-text-secondary; }
77
+ }
78
+ }
79
+
80
+ @media (prefers-color-scheme: dark) {
81
+
82
+ .mu-list {
83
+ background: $mu-dark-bg-base;
84
+ &--bordered { border-color: rgba(255, 255, 255, 0.08); }
85
+ &__header-text { color: $mu-dark-text-secondary; }
86
+ &__footer-text { color: $mu-dark-text-secondary; }
87
+ }
88
+ }
89
+ </style>