im-ui-mobile 0.1.22 → 0.1.24

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.
@@ -57,7 +57,7 @@
57
57
  </template>
58
58
 
59
59
  <script setup lang="ts">
60
- import { ref, computed, watch, nextTick, useSlots } from 'vue'
60
+ import { computed } from 'vue'
61
61
  import { validator } from '../../index'
62
62
 
63
63
  // 定义类型
@@ -65,7 +65,7 @@
65
65
  </view>
66
66
 
67
67
  <!-- 主体内容 -->
68
- <view class="im-card__body">
68
+ <view class="im-card__body" :style="bodyStyle">
69
69
  <slot></slot>
70
70
  </view>
71
71
 
@@ -91,7 +91,7 @@
91
91
  </template>
92
92
 
93
93
  <script setup lang="ts">
94
- import { ref, computed, watch, nextTick, useSlots } from 'vue'
94
+ import { computed } from 'vue'
95
95
  import type { CSSProperties } from 'vue'
96
96
  import ImButton from '../im-button/im-button.vue'
97
97
 
@@ -102,7 +102,8 @@ interface CardAction {
102
102
  plain?: boolean
103
103
  disabled?: boolean
104
104
  loading?: boolean
105
- data?: any
105
+ data?: any,
106
+ click?: Function
106
107
  }
107
108
 
108
109
  // Props 定义
@@ -143,6 +144,9 @@ interface Props {
143
144
  subtitleStyle?: CSSProperties
144
145
  descriptionStyle?: CSSProperties
145
146
 
147
+ // 内容区域
148
+ bodyStyle?: CSSProperties
149
+
146
150
  // 封面图片
147
151
  coverImage?: string
148
152
  coverMode?: 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | 'heightFix'
@@ -229,7 +233,13 @@ const handleCoverClick = (event: MouseEvent) => {
229
233
  }
230
234
 
231
235
  const handleActionClick = (action: CardAction, index: number) => {
232
- if (!props.disabled && !props.loading && !action.disabled) {
236
+ if (props.disabled || props.loading || action.disabled) {
237
+ return
238
+ }
239
+
240
+ if (action.click) {
241
+ action.click()
242
+ } else {
233
243
  emit('action-click', action, index)
234
244
  }
235
245
  }
@@ -0,0 +1,589 @@
1
+ <!-- components/im-checkbox/im-checkbox.vue -->
2
+ <template>
3
+ <view class="im-checkbox" :class="[
4
+ `im-checkbox--${innerSize}`,
5
+ `im-checkbox--${type}`,
6
+ {
7
+ 'im-checkbox--disabled': disabled,
8
+ 'im-checkbox--checked': isChecked,
9
+ 'im-checkbox--indeterminate': indeterminate,
10
+ 'im-checkbox--readonly': readonly,
11
+ 'im-checkbox--bordered': bordered
12
+ }
13
+ ]" :style="[customStyle, computedStyle]" @click="handleClick" @touchstart="handleTouchStart"
14
+ @touchend="handleTouchEnd">
15
+ <!-- 复选框内容 -->
16
+ <view class="im-checkbox__wrapper">
17
+ <!-- 图标区域 -->
18
+ <view class="im-checkbox__icon">
19
+ <slot name="icon" :checked="isChecked" :disabled="disabled" :indeterminate="indeterminate">
20
+ <!-- 默认图标 -->
21
+ <template v-if="type === 'square'">
22
+ <view class="im-checkbox__icon-square" :class="{
23
+ 'im-checkbox__icon-square--checked': isChecked,
24
+ 'im-checkbox__icon-square--indeterminate': indeterminate
25
+ }">
26
+ <view v-if="isChecked && !indeterminate" class="im-checkbox__checkmark" />
27
+ <view v-else-if="indeterminate" class="im-checkbox__indeterminate-mark" />
28
+ </view>
29
+ </template>
30
+
31
+ <!-- 圆形图标 -->
32
+ <template v-else-if="type === 'circle'">
33
+ <view class="im-checkbox__icon-circle" :class="{
34
+ 'im-checkbox__icon-circle--checked': isChecked
35
+ }">
36
+ <view v-if="isChecked" class="im-checkbox__checkmark" />
37
+ </view>
38
+ </template>
39
+
40
+ <!-- 开关样式 -->
41
+ <template v-else-if="type === 'switch'">
42
+ <!-- :color="switchColor" :backgroundColor="switchBackgroundColor" -->
43
+ <switch :checked="isChecked" :color="props.checkedColor"
44
+ :class="[`im-checkbox__switch--${innerSize}`]" :disabled="disabled" />
45
+ <!-- <view class="im-checkbox__switch" :class="{
46
+ 'im-checkbox__switch--checked': isChecked
47
+ }">
48
+ <view class="im-checkbox__switch-handle" />
49
+ </view> -->
50
+ </template>
51
+
52
+ <!-- 按钮样式 -->
53
+ <template v-else-if="type === 'button'">
54
+ <view class="im-checkbox__button" :class="{
55
+ 'im-checkbox__button--checked': isChecked
56
+ }">
57
+ <view class="im-checkbox__button-content">
58
+ <slot>
59
+ <text v-if="label" class="im-checkbox__button-text">
60
+ {{ label }}
61
+ </text>
62
+ </slot>
63
+ </view>
64
+ </view>
65
+ </template>
66
+ </slot>
67
+ </view>
68
+
69
+ <!-- 标签区域 -->
70
+ <view v-if="type !== 'button' && (label || $slots.default)" class="im-checkbox__label" :class="{
71
+ 'im-checkbox__label--disabled': disabled,
72
+ 'im-checkbox__label--checked': isChecked
73
+ }">
74
+ <!-- 默认插槽 -->
75
+ <slot>
76
+ <text class="im-checkbox__label-text">{{ label }}</text>
77
+ </slot>
78
+
79
+ <!-- 描述文本 -->
80
+ <text v-if="description" class="im-checkbox__description">
81
+ {{ description }}
82
+ </text>
83
+ </view>
84
+ </view>
85
+
86
+ <!-- 角标 -->
87
+ <view v-if="badge" class="im-checkbox__badge" :class="[
88
+ `im-checkbox__badge--${badge.type || 'dot'}`,
89
+ `im-checkbox__badge--${badge.position || 'top-right'}`
90
+ ]" :style="badgeStyle">
91
+ <text v-if="badge.type === 'number' || badge.type === 'text'" class="im-checkbox__badge-text">
92
+ {{ badge.value }}
93
+ </text>
94
+ </view>
95
+ </view>
96
+ </template>
97
+
98
+ <script setup lang="ts">
99
+ import {
100
+ computed,
101
+ inject
102
+ } from 'vue'
103
+ import type {
104
+ CheckboxProps,
105
+ CheckboxEmits
106
+ } from '../../types/components/checkbox'
107
+ import type {
108
+ CheckboxGroupContext
109
+ } from '../../types/components/checkbox-group'
110
+
111
+ const props = withDefaults(defineProps<CheckboxProps>(), {
112
+ modelValue: false,
113
+ value: '',
114
+ label: '',
115
+ description: '',
116
+ disabled: false,
117
+ readonly: false,
118
+ indeterminate: false,
119
+ size: '',
120
+ type: 'square',
121
+ checkedColor: '#07c160',
122
+ uncheckedColor: '#dcdee0',
123
+ disabledColor: '#c8c9cc',
124
+ borderColor: '',
125
+ bordered: false,
126
+ badge: undefined,
127
+ clickable: true,
128
+ customStyle: () => ({})
129
+ })
130
+
131
+ const emit = defineEmits<CheckboxEmits>()
132
+
133
+ // 获取复选框组上下文
134
+ const checkboxGroup = inject<CheckboxGroupContext | null>('imCheckboxGroup', null)
135
+
136
+ const innerSize = computed(() => {
137
+ const defultSize = 'medium'
138
+
139
+ if (checkboxGroup) {
140
+ if (checkboxGroup.size && props.size) {
141
+ return props.size
142
+ }
143
+
144
+ if (checkboxGroup.size) {
145
+ return checkboxGroup.size
146
+ }
147
+ else {
148
+ return props.size || defultSize
149
+ }
150
+ } else {
151
+ return props.size || defultSize
152
+ }
153
+ })
154
+
155
+ // 计算是否选中
156
+ const isChecked = computed(() => {
157
+ if (checkboxGroup) {
158
+ // 在复选框组中
159
+ const groupValue = checkboxGroup.modelValue
160
+ return groupValue.includes(props.value as string | number)
161
+ } else {
162
+ // 独立使用
163
+ return !!props.modelValue
164
+ }
165
+ })
166
+
167
+ // 计算样式
168
+ const computedStyle = computed(() => {
169
+ const style: Record<string, any> = {}
170
+
171
+ // 添加颜色变量
172
+ if (props.borderColor) {
173
+ style['--checkbox-border-color'] = props.borderColor
174
+ }
175
+
176
+ if (props.checkedColor) {
177
+ style['--checkbox-checked-color'] = props.checkedColor
178
+ }
179
+
180
+ return style
181
+ })
182
+
183
+ // 角标样式
184
+ const badgeStyle = computed(() => {
185
+ if (!props.badge) return {}
186
+
187
+ const style: Record<string, any> = {}
188
+
189
+ if (props.badge.color) {
190
+ style.color = props.badge.color
191
+ }
192
+
193
+ if (props.badge.backgroundColor) {
194
+ style.backgroundColor = props.badge.backgroundColor
195
+ }
196
+
197
+ return style
198
+ })
199
+
200
+ // 注册和注销
201
+ onMounted(() => {
202
+ if (checkboxGroup && checkboxGroup.registerCheckbox && props.value) {
203
+ checkboxGroup.registerCheckbox(props.value as string | number)
204
+ }
205
+ })
206
+
207
+ onUnmounted(() => {
208
+ if (checkboxGroup && checkboxGroup.unregisterCheckbox && props.value) {
209
+ checkboxGroup.unregisterCheckbox(props.value as string | number)
210
+ }
211
+ })
212
+
213
+ // 处理点击事件
214
+ const handleClick = (event: Event) => {
215
+ if (props.disabled || props.readonly || !props.clickable) {
216
+ return
217
+ }
218
+
219
+ emit('click', event)
220
+
221
+ if (checkboxGroup) {
222
+ checkboxGroup.handleChange(props.value as string | number)
223
+ } else {
224
+ // 独立使用
225
+ const newValue = !props.modelValue
226
+ emit('update:modelValue', newValue)
227
+ emit('change', newValue)
228
+ }
229
+ }
230
+
231
+ // 触摸开始
232
+ const handleTouchStart = (event: Event) => {
233
+ // 可以添加触摸反馈效果
234
+ }
235
+
236
+ // 触摸结束
237
+ const handleTouchEnd = (event: Event) => {
238
+ // 可以添加触摸反馈效果
239
+ }
240
+ </script>
241
+
242
+ <style lang="scss" scoped>
243
+ .im-checkbox {
244
+ position: relative;
245
+ display: inline-flex;
246
+ align-items: center;
247
+ cursor: pointer;
248
+ user-select: none;
249
+ transition: all 0.2s ease;
250
+
251
+ // 状态类
252
+ &--disabled {
253
+ cursor: not-allowed;
254
+ opacity: 0.6;
255
+
256
+ * {
257
+ pointer-events: none;
258
+ }
259
+ }
260
+
261
+ &--readonly {
262
+ cursor: default;
263
+ }
264
+
265
+ &:not(.im-checkbox--disabled):not(.im-checkbox--readonly):active {
266
+ opacity: 0.8;
267
+ }
268
+
269
+ // 带边框
270
+ &--bordered {
271
+ padding: 16rpx 24rpx;
272
+ border: 2rpx solid var(--checkbox-border-color, #f0f0f0);
273
+ border-radius: 12rpx;
274
+ background-color: #ffffff;
275
+
276
+ &:not(.im-checkbox--disabled):hover {
277
+ border-color: #e0e0e0;
278
+ }
279
+
280
+ &.im-checkbox--checked {
281
+ border-color: var(--checkbox-checked-color, #07c160);
282
+ background-color: rgba(7, 193, 96, 0.05);
283
+ }
284
+ }
285
+
286
+ // 大小类
287
+ $sizes: (
288
+ small: (wrapper-font-size: 24rpx,
289
+ icon-size: 32rpx,
290
+ checkmark-width: 16rpx,
291
+ checkmark-height: 8rpx,
292
+ switch-width: 80rpx,
293
+ switch-height: 40rpx,
294
+ switch-handle-size: 36rpx,
295
+ button-padding: 8rpx 16rpx,
296
+ button-font-size: 24rpx),
297
+ medium: (wrapper-font-size: 28rpx,
298
+ icon-size: 40rpx,
299
+ checkmark-width: 20rpx,
300
+ checkmark-height: 10rpx,
301
+ switch-width: 96rpx,
302
+ switch-height: 48rpx,
303
+ switch-handle-size: 44rpx,
304
+ button-padding: 12rpx 24rpx,
305
+ button-font-size: 28rpx),
306
+ large: (wrapper-font-size: 32rpx,
307
+ icon-size: 48rpx,
308
+ checkmark-width: 24rpx,
309
+ checkmark-height: 12rpx,
310
+ switch-width: 112rpx,
311
+ switch-height: 56rpx,
312
+ switch-handle-size: 52rpx,
313
+ button-padding: 16rpx 32rpx,
314
+ button-font-size: 32rpx)
315
+ );
316
+
317
+ @each $size, $map in $sizes {
318
+ &--#{$size} {
319
+ .im-checkbox__wrapper {
320
+ font-size: map-get($map, wrapper-font-size);
321
+ }
322
+
323
+ .im-checkbox__icon-square,
324
+ .im-checkbox__icon-circle {
325
+ width: map-get($map, icon-size);
326
+ height: map-get($map, icon-size);
327
+
328
+ &--checked {
329
+ .im-checkbox__checkmark {
330
+ width: map-get($map, checkmark-width);
331
+ height: map-get($map, checkmark-height);
332
+ }
333
+ }
334
+ }
335
+
336
+ .im-checkbox__switch {
337
+ width: map-get($map, switch-width);
338
+ height: map-get($map, switch-height);
339
+
340
+ &-handle {
341
+ width: map-get($map, switch-handle-size);
342
+ height: map-get($map, switch-handle-size);
343
+ }
344
+ }
345
+
346
+ .im-checkbox__button {
347
+ padding: map-get($map, button-padding);
348
+ font-size: map-get($map, button-font-size);
349
+ }
350
+ }
351
+ }
352
+
353
+ &__switch {
354
+ &--small {
355
+ transform: scale(0.5);
356
+ }
357
+
358
+ &--medium {
359
+ transform: scale(0.7);
360
+ }
361
+
362
+ &--large {
363
+ transform: scale(1.5);
364
+ }
365
+ }
366
+
367
+ // 组件内部样式
368
+ &__wrapper {
369
+ display: flex;
370
+ align-items: center;
371
+ flex: 1;
372
+ }
373
+
374
+ &__icon {
375
+ position: relative;
376
+ display: flex;
377
+ align-items: center;
378
+ justify-content: center;
379
+ flex-shrink: 0;
380
+
381
+ // 方形图标
382
+ &-square {
383
+ border: 2rpx solid var(--checkbox-unchecked-color, #dcdee0);
384
+ border-radius: 6rpx;
385
+ background-color: #ffffff;
386
+ transition: all 0.2s ease;
387
+
388
+ &--checked {
389
+ border-color: var(--checkbox-checked-color, #07c160);
390
+ background-color: var(--checkbox-checked-color, #07c160);
391
+ }
392
+
393
+ &--indeterminate {
394
+ border-color: var(--checkbox-checked-color, #07c160);
395
+ background-color: var(--checkbox-checked-color, #07c160);
396
+ }
397
+
398
+ .im-checkbox--disabled & {
399
+ border-color: var(--checkbox-disabled-color, #c8c9cc);
400
+ background-color: #f5f5f5;
401
+ }
402
+ }
403
+
404
+ // 圆形图标
405
+ &-circle {
406
+ border: 2rpx solid var(--checkbox-unchecked-color, #dcdee0);
407
+ border-radius: 50%;
408
+ background-color: #ffffff;
409
+ transition: all 0.2s ease;
410
+
411
+ &--checked {
412
+ border-color: var(--checkbox-checked-color, #07c160);
413
+ background-color: var(--checkbox-checked-color, #07c160);
414
+ }
415
+
416
+ .im-checkbox--disabled & {
417
+ border-color: var(--checkbox-disabled-color, #c8c9cc);
418
+ background-color: #f5f5f5;
419
+ }
420
+ }
421
+
422
+ // 开关
423
+ &-switch {
424
+ border-radius: 100rpx;
425
+ background-color: var(--checkbox-unchecked-color, #dcdee0);
426
+ transition: all 0.3s cubic-bezier(0.3, 0, 0.15, 1);
427
+ position: relative;
428
+
429
+ &--checked {
430
+ background-color: var(--checkbox-checked-color, #07c160);
431
+ }
432
+
433
+ .im-checkbox--disabled & {
434
+ background-color: var(--checkbox-disabled-color, #c8c9cc);
435
+ }
436
+ }
437
+ }
438
+
439
+ &__checkmark {
440
+ position: absolute;
441
+ top: 50%;
442
+ left: 50%;
443
+ transform: translate(-50%, -60%) rotate(-45deg);
444
+ transform-origin: center;
445
+
446
+ &::before {
447
+ content: '';
448
+ position: absolute;
449
+ top: 0;
450
+ left: 0;
451
+ width: 100%;
452
+ height: 100%;
453
+ border-left: solid 2rpx #ffffff;
454
+ border-bottom: solid 2rpx #ffffff;
455
+ box-sizing: border-box;
456
+ }
457
+ }
458
+
459
+ &__indeterminate-mark {
460
+ width: 16rpx;
461
+ height: 2rpx;
462
+ background-color: #ffffff;
463
+ border-radius: 1rpx;
464
+ }
465
+
466
+ &__switch-handle {
467
+ position: absolute;
468
+ top: 2rpx;
469
+ left: 2rpx;
470
+ border-radius: 50%;
471
+ background-color: #ffffff;
472
+ transition: all 0.3s cubic-bezier(0.3, 0, 0.15, 1);
473
+ box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
474
+
475
+ .im-checkbox__icon-switch--checked & {
476
+ left: calc(100% - 2rpx);
477
+ transform: translateX(-100%);
478
+ }
479
+ }
480
+
481
+ &__button {
482
+ border: 2rpx solid var(--checkbox-unchecked-color, #dcdee0);
483
+ border-radius: 8rpx;
484
+ background-color: #ffffff;
485
+ transition: all 0.2s ease;
486
+
487
+ &--checked {
488
+ border-color: var(--checkbox-checked-color, #07c160);
489
+ background-color: var(--checkbox-checked-color, #07c160);
490
+ color: #ffffff;
491
+ }
492
+
493
+ .im-checkbox--disabled & {
494
+ border-color: var(--checkbox-disabled-color, #c8c9cc);
495
+ background-color: #f5f5f5;
496
+ }
497
+
498
+ &-content {
499
+ display: flex;
500
+ align-items: center;
501
+ justify-content: center;
502
+ }
503
+
504
+ &-text {
505
+ font-weight: 500;
506
+ }
507
+ }
508
+
509
+ &__label {
510
+ margin-left: 16rpx;
511
+ color: #333333;
512
+ flex: 1;
513
+
514
+ &--disabled {
515
+ color: #999999;
516
+ }
517
+
518
+ &--checked {
519
+ font-weight: 500;
520
+ }
521
+ }
522
+
523
+ &__label-text {
524
+ line-height: 1.4;
525
+ }
526
+
527
+ &__description {
528
+ display: block;
529
+ margin-top: 4rpx;
530
+ font-size: 80%;
531
+ color: #666666;
532
+ line-height: 1.3;
533
+ }
534
+
535
+ // 角标
536
+ &__badge {
537
+ position: absolute;
538
+ z-index: 1;
539
+
540
+ // 位置
541
+ &--top-right {
542
+ top: -8rpx;
543
+ right: -8rpx;
544
+ }
545
+
546
+ &--top-left {
547
+ top: -8rpx;
548
+ left: -8rpx;
549
+ }
550
+
551
+ &--bottom-right {
552
+ bottom: -8rpx;
553
+ right: -8rpx;
554
+ }
555
+
556
+ &--bottom-left {
557
+ bottom: -8rpx;
558
+ left: -8rpx;
559
+ }
560
+
561
+ // 类型
562
+ &--dot {
563
+ width: 16rpx;
564
+ height: 16rpx;
565
+ border-radius: 50%;
566
+ background-color: #e64340;
567
+ }
568
+
569
+ &--number,
570
+ &--text {
571
+ min-width: 32rpx;
572
+ height: 32rpx;
573
+ padding: 0 8rpx;
574
+ border-radius: 16rpx;
575
+ background-color: #e64340;
576
+ display: flex;
577
+ align-items: center;
578
+ justify-content: center;
579
+ }
580
+
581
+ &-text {
582
+ font-size: 20rpx;
583
+ color: #ffffff;
584
+ font-weight: 500;
585
+ line-height: 1;
586
+ }
587
+ }
588
+ }
589
+ </style>
@@ -0,0 +1,28 @@
1
+ // components/im-checkbox/constants.ts
2
+
3
+ /**
4
+ * 组件常量定义
5
+ */
6
+ export const CHECKBOX_SIZES = ['small', 'medium', 'large'] as const
7
+ export const CHECKBOX_TYPES = ['square', 'circle', 'switch', 'button'] as const
8
+ export const CHECKBOX_DIRECTIONS = ['horizontal', 'vertical'] as const
9
+ export const BADGE_POSITIONS = ['top-right', 'top-left', 'bottom-right', 'bottom-left'] as const
10
+ export const BADGE_TYPES = ['dot', 'number', 'text'] as const
11
+
12
+ /**
13
+ * 默认配置
14
+ */
15
+ export const DEFAULT_CONFIG = {
16
+ // 复选框
17
+ TYPE: 'square' as const,
18
+ CHECKED_COLOR: '#07c160',
19
+ UNCHECKED_COLOR: '#dcdee0',
20
+ DISABLED_COLOR: '#c8c9cc',
21
+
22
+ // 复选框组
23
+ DIRECTION: 'vertical' as const,
24
+ MIN: 0,
25
+ MAX: Infinity,
26
+ SHOW_CHECK_ALL: false,
27
+ CHECK_ALL_TEXT: '全选'
28
+ } as const