im-ui-mobile 0.0.81 → 0.0.83

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 (41) hide show
  1. package/components/{im-head-image/im-head-image.vue → im-avatar/im-avatar.vue} +2 -2
  2. package/components/im-button/im-button.vue +603 -38
  3. package/components/im-cell/im-cell.vue +462 -0
  4. package/components/{im-bar-group/im-bar-group.vue → im-cell-group/im-cell-group.vue} +4 -4
  5. package/components/im-cell-switch/im-cell-switch.vue +223 -0
  6. package/components/im-chat-item/im-chat-item.vue +4 -5
  7. package/components/{im-long-press-menu/im-long-press-menu.vue → im-context-menu/im-context-menu.vue} +2 -2
  8. package/components/im-file-upload/im-file-upload.vue +9 -2
  9. package/components/im-friend-item/im-friend-item.vue +2 -2
  10. package/components/im-group-item/im-group-item.vue +2 -2
  11. package/components/im-group-member-selector/im-group-member-selector.vue +3 -3
  12. package/components/im-group-rtc-join/im-group-rtc-join.vue +3 -3
  13. package/components/im-image-upload/im-image-upload.vue +3 -2
  14. package/components/{im-chat-at-box/im-chat-at-box.vue → im-mention-picker/im-mention-picker.vue} +5 -5
  15. package/components/{im-chat-message-item/im-chat-message-item.vue → im-message-item/im-message-item.vue} +19 -21
  16. package/components/{im-chat-group-readed/im-chat-group-readed.vue → im-read-receipt/im-read-receipt.vue} +5 -5
  17. package/components/im-virtual-list/im-virtual-list.vue +444 -0
  18. package/components/{im-chat-record/im-chat-record.vue → im-voice-input/im-voice-input.vue} +11 -11
  19. package/index.js +5 -13
  20. package/package.json +4 -4
  21. package/types/components/{head-image.d.ts → avatar.d.ts} +4 -4
  22. package/types/components/button.d.ts +73 -3
  23. package/types/components/{bar-group.d.ts → cell-group.d.ts} +4 -4
  24. package/types/components/cell-switch.d.ts +31 -0
  25. package/types/components/cell.d.ts +46 -0
  26. package/types/components/context-menu.d.ts +23 -0
  27. package/types/components/{chat-at-box.d.ts → mention-picker.d.ts} +6 -6
  28. package/types/components/{chat-message-item.d.ts → message-item.d.ts} +6 -6
  29. package/types/components/{chat-group-readed.d.ts → read-receipt.d.ts} +6 -6
  30. package/types/components/virtual-list.d.ts +148 -0
  31. package/types/components/{chat-record.d.ts → voice-input.d.ts} +4 -4
  32. package/types/components.d.ts +9 -10
  33. package/types/index.d.ts +4 -2
  34. package/types/utils/emoji.d.ts +0 -2
  35. package/utils/datetime.js +1 -1
  36. package/utils/emoji.js +1 -33
  37. package/components/im-arrow-bar/im-arrow-bar.vue +0 -59
  38. package/components/im-sample/im-sample.vue +0 -192
  39. package/components/im-switch-bar/im-switch-bar.vue +0 -62
  40. package/types/components/long-press-menu.d.ts +0 -23
  41. package/types/components/switch-bar.d.ts +0 -19
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <view class="head-image none-pointer-events" @click="showUserInfo" :title="name">
2
+ <view class="avatar none-pointer-events" @click="showUserInfo" :title="name">
3
3
  <image v-if="url" class="avatar-image" :src="url" :style="avatarImageStyle" lazy-load="true"
4
4
  mode="aspectFill" />
5
5
  <view v-else class="avatar-text" :style="avatarTextStyle">
@@ -89,7 +89,7 @@ const textColor = computed(() => {
89
89
  </script>
90
90
 
91
91
  <style scoped lang="scss">
92
- .head-image {
92
+ .avatar {
93
93
  position: relative;
94
94
  cursor: pointer;
95
95
 
@@ -1,62 +1,627 @@
1
1
  <template>
2
- <view class="button" :style="style">
3
- <text v-if="icon" class="icon iconfont" :class="icon"></text>
4
- <text class="title">{{ title }}</text>
5
- </view>
2
+ <button class="im-button" :class="[
3
+ `im-button--${type}`,
4
+ `im-button--${size}`,
5
+ `im-button--${shape}`,
6
+ {
7
+ 'im-button--disabled': disabled,
8
+ 'im-button--loading': loading,
9
+ 'im-button--block': block,
10
+ 'im-button--plain': plain,
11
+ 'im-button--hairline': hairline,
12
+ 'im-button--native': native
13
+ }
14
+ ]" :style="buttonStyles" :disabled="disabled || loading" :loading="loading && showLoading" :form-type="formType"
15
+ :open-type="openType" :hover-class="disabled || loading ? '' : hoverClass" :hover-start-time="hoverStartTime"
16
+ :hover-stay-time="hoverStayTime" :app-parameter="appParameter" :lang="lang" :session-from="sessionFrom"
17
+ :send-message-title="sendMessageTitle" :send-message-path="sendMessagePath" :send-message-img="sendMessageImg"
18
+ :show-message-card="showMessageCard" @click="handleClick" @getuserinfo="onGetUserInfo" @contact="onContact"
19
+ @getphonenumber="onGetPhoneNumber" @error="onError" @launchapp="onLaunchApp" @opensetting="onOpenSetting"
20
+ @chooseavatar="onChooseAvatar">
21
+ <!-- 加载状态 -->
22
+ <view v-if="loading && showLoading" class="im-button__loading">
23
+ <view class="im-button__loading-spinner" :style="loadingStyle">
24
+ <view v-for="n in loadingDotCount" :key="n" class="im-button__loading-dot" :style="{
25
+ animationDelay: `${(n - 1) * 0.15}s`,
26
+ backgroundColor: loadingColor
27
+ }" />
28
+ </view>
29
+ <text v-if="loadingText" class="im-button__loading-text">
30
+ {{ loadingText }}
31
+ </text>
32
+ </view>
33
+
34
+ <!-- 图标 -->
35
+ <view v-if="(icon || $slots.icon) && !loading" class="im-button__icon">
36
+ <slot name="icon">
37
+ <image v-if="icon" :src="icon" class="im-button__icon-image"
38
+ :class="[`im-button__icon--${iconPosition}`]" mode="aspectFit" />
39
+ </slot>
40
+ </view>
41
+
42
+ <!-- 文本内容 -->
43
+ <text v-if="!loading || !hideTextWhenLoading" class="im-button__text" :class="[`im-button__text--${textSize}`]"
44
+ :style="textStyles">
45
+ <slot>{{ text }}</slot>
46
+ </text>
47
+
48
+ <!-- 角标 -->
49
+ <view v-if="badge || $slots.badge" class="im-button__badge">
50
+ <slot name="badge">
51
+ <view v-if="badge" class="im-button__badge-content" :class="{
52
+ 'im-button__badge--dot': badge === true,
53
+ 'im-button__badge--text': badge !== true
54
+ }">
55
+ <text v-if="badge !== true" class="im-button__badge-text">
56
+ {{ formatBadge(badge) }}
57
+ </text>
58
+ </view>
59
+ </slot>
60
+ </view>
61
+ </button>
6
62
  </template>
7
63
 
8
64
  <script setup lang="ts">
9
- import { computed } from 'vue'
65
+ import { computed, withDefaults } from 'vue'
66
+
67
+ // 定义类型
68
+ type ButtonType = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info'
69
+ type ButtonSize = 'mini' | 'small' | 'medium' | 'large'
70
+ type ButtonShape = 'square' | 'round' | 'circle'
71
+ type ButtonFormType = 'submit' | 'reset'
72
+ type ButtonOpenType =
73
+ | 'contact'
74
+ | 'share'
75
+ | 'getPhoneNumber'
76
+ | 'getUserInfo'
77
+ | 'launchApp'
78
+ | 'openSetting'
79
+ | 'feedback'
80
+ | 'chooseAvatar'
81
+ type IconPosition = 'left' | 'right'
82
+ type TextSize = 'small' | 'medium' | 'large'
10
83
 
11
84
  interface Props {
12
- title: string;
13
- icon?: string;
14
- type?: 'normal' | 'danger' | 'primary';
15
- color?: string;
85
+ // 基础属性
86
+ text?: string
87
+ type?: ButtonType
88
+ size?: ButtonSize
89
+ shape?: ButtonShape
90
+ disabled?: boolean
91
+ loading?: boolean
92
+ loadingText?: string
93
+ loadingColor?: string
94
+ showLoading?: boolean
95
+ hideTextWhenLoading?: boolean
96
+ block?: boolean
97
+ plain?: boolean
98
+ hairline?: boolean
99
+ native?: boolean
100
+
101
+ // 图标
102
+ icon?: string
103
+ iconPosition?: IconPosition
104
+ iconSize?: string | number
105
+
106
+ // 样式
107
+ color?: string
108
+ bgColor?: string
109
+ borderColor?: string
110
+ textColor?: string
111
+ textSize?: TextSize
112
+ width?: string
113
+ height?: string
114
+ padding?: string
115
+ margin?: string
116
+ radius?: string
117
+ shadow?: string
118
+
119
+ // 角标
120
+ badge?: boolean | number | string
121
+
122
+ // 表单相关
123
+ formType?: ButtonFormType
124
+
125
+ // 开放能力
126
+ openType?: ButtonOpenType
127
+ appParameter?: string
128
+ lang?: 'en' | 'zh_CN' | 'zh_TW'
129
+ sessionFrom?: string
130
+ sendMessageTitle?: string
131
+ sendMessagePath?: string
132
+ sendMessageImg?: string
133
+ showMessageCard?: boolean
134
+
135
+ // 交互效果
136
+ hoverClass?: string
137
+ hoverStartTime?: number
138
+ hoverStayTime?: number
139
+ }
140
+
141
+ // 定义 Emits
142
+ interface Emits {
143
+ (e: 'click', event: PointerEvent): void
144
+ (e: 'getuserinfo', detail: any): void
145
+ (e: 'contact', detail: any): void
146
+ (e: 'getphonenumber', detail: any): void
147
+ (e: 'error', detail: any): void
148
+ (e: 'launchapp', detail: any): void
149
+ (e: 'opensetting', detail: any): void
150
+ (e: 'chooseavatar', detail: any): void
16
151
  }
17
152
 
153
+ // Props 和 Emits
18
154
  const props = withDefaults(defineProps<Props>(), {
19
- icon: '',
20
- type: 'normal',
21
- color: '#000'
22
- });
155
+ type: 'default',
156
+ size: 'medium',
157
+ shape: 'round',
158
+ disabled: false,
159
+ loading: false,
160
+ loadingColor: '#ffffff',
161
+ showLoading: true,
162
+ hideTextWhenLoading: false,
163
+ block: false,
164
+ plain: false,
165
+ hairline: false,
166
+ native: false,
167
+ iconPosition: 'left',
168
+ iconSize: '32rpx',
169
+ textSize: 'medium',
170
+ color: '',
171
+ bgColor: '',
172
+ borderColor: '',
173
+ textColor: '',
174
+ width: '',
175
+ height: '',
176
+ padding: '',
177
+ margin: '',
178
+ radius: '',
179
+ shadow: '',
180
+ hoverClass: 'im-button--hover',
181
+ hoverStartTime: 20,
182
+ hoverStayTime: 70,
183
+ lang: 'zh_CN'
184
+ })
185
+
186
+ const emit = defineEmits<Emits>()
23
187
 
24
- const style = computed(() => {
25
- let color = props.color;
188
+ // 计算属性
189
+ const buttonStyles = computed(() => {
190
+ const styles: Record<string, string> = {}
191
+
192
+ // 自定义颜色
193
+ if (props.color && !props.plain) {
194
+ styles.backgroundColor = props.color
195
+ styles.borderColor = props.color
196
+ }
197
+
198
+ if (props.bgColor) {
199
+ styles.backgroundColor = props.bgColor
200
+ }
201
+
202
+ if (props.borderColor) {
203
+ styles.borderColor = props.borderColor
204
+ }
205
+
206
+ if (props.textColor) {
207
+ styles.color = props.textColor
208
+ }
209
+
210
+ // 尺寸和布局
211
+ if (props.width) {
212
+ styles.width = props.width
213
+ }
214
+
215
+ if (props.height) {
216
+ styles.height = props.height
217
+ }
218
+
219
+ if (props.padding) {
220
+ styles.padding = props.padding
221
+ }
222
+
223
+ if (props.margin) {
224
+ styles.margin = props.margin
225
+ }
226
+
227
+ if (props.radius) {
228
+ styles.borderRadius = props.radius
229
+ }
230
+
231
+ if (props.shadow) {
232
+ styles.boxShadow = props.shadow
233
+ }
26
234
 
27
- switch (props.type) {
28
- case 'danger':
29
- color = '#f14747';
30
- break;
31
- case 'primary':
32
- color = '#35567f';
33
- break;
235
+ if (props.block) {
236
+ styles.width = '100%'
34
237
  }
35
238
 
36
- return `color: ${color};`;
37
- });
239
+ return styles
240
+ })
241
+
242
+ const textStyles = computed(() => {
243
+ const styles: Record<string, string> = {}
244
+
245
+ if (props.textColor) {
246
+ styles.color = props.textColor
247
+ }
248
+
249
+ return styles
250
+ })
251
+
252
+ const loadingStyle = computed(() => {
253
+ return {
254
+ color: props.loadingColor
255
+ }
256
+ })
257
+
258
+ const loadingDotCount = computed(() => {
259
+ // 根据按钮大小决定点数
260
+ switch (props.size) {
261
+ case 'mini': return 3
262
+ case 'small': return 4
263
+ case 'medium': return 5
264
+ case 'large': return 6
265
+ default: return 5
266
+ }
267
+ })
268
+
269
+ // 方法
270
+ const formatBadge = (badge: boolean | number | string): string => {
271
+ if (typeof badge === 'boolean') return ''
272
+ if (typeof badge === 'number') {
273
+ return badge > 99 ? '99+' : badge.toString()
274
+ }
275
+ return badge
276
+ }
277
+
278
+ // 事件处理
279
+ const handleClick = (event: PointerEvent) => {
280
+ if (props.disabled || props.loading) {
281
+ return
282
+ }
283
+ emit('click', event)
284
+ }
285
+
286
+ const onGetUserInfo = (detail: any) => {
287
+ emit('getuserinfo', detail)
288
+ }
289
+
290
+ const onContact = (detail: any) => {
291
+ emit('contact', detail)
292
+ }
293
+
294
+ const onGetPhoneNumber = (detail: any) => {
295
+ emit('getphonenumber', detail)
296
+ }
297
+
298
+ const onError = (detail: any) => {
299
+ emit('error', detail)
300
+ }
301
+
302
+ const onLaunchApp = (detail: any) => {
303
+ emit('launchapp', detail)
304
+ }
305
+
306
+ const onOpenSetting = (detail: any) => {
307
+ emit('opensetting', detail)
308
+ }
309
+
310
+ const onChooseAvatar = (detail: any) => {
311
+ emit('chooseavatar', detail)
312
+ }
38
313
  </script>
39
314
 
40
315
  <style lang="scss" scoped>
41
- .button {
42
- width: 100%;
43
- height: 100rpx;
44
- margin-top: 5rpx;
45
- background-color: white;
46
- line-height: 100rpx;
47
- text-align: center;
48
- display: flex;
316
+ .im-button {
317
+ position: relative;
318
+ display: inline-flex;
319
+ align-items: center;
49
320
  justify-content: center;
321
+ border: 2rpx solid transparent;
322
+ font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
323
+ font-weight: 500;
324
+ text-align: center;
325
+ white-space: nowrap;
326
+ vertical-align: middle;
327
+ user-select: none;
328
+ cursor: pointer;
329
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
330
+ outline: none;
331
+ box-sizing: border-box;
332
+
333
+ // 禁用状态
334
+ &--disabled {
335
+ opacity: 0.6;
336
+ cursor: not-allowed;
337
+ }
50
338
 
51
- .icon {
52
- font-size: 40rpx;
53
- font-weight: 600;
54
- margin-right: 10rpx;
339
+ // 加载状态
340
+ &--loading {
341
+ cursor: not-allowed;
55
342
  }
56
343
 
57
- .title {
344
+ // 块级按钮
345
+ &--block {
346
+ display: flex;
347
+ width: 100%;
348
+ }
349
+
350
+ // 朴素按钮
351
+ &--plain {
352
+ background-color: transparent !important;
353
+ }
354
+
355
+ // 细边框
356
+ &--hairline {
357
+ position: relative;
358
+ border-width: 0;
359
+
360
+ &::after {
361
+ content: '';
362
+ position: absolute;
363
+ top: 0;
364
+ left: 0;
365
+ right: 0;
366
+ bottom: 0;
367
+ border: 2rpx solid currentColor;
368
+ border-radius: inherit;
369
+ pointer-events: none;
370
+ transform: scale(0.5);
371
+ transform-origin: center;
372
+ }
373
+ }
374
+
375
+ // 原生按钮
376
+ &--native {
377
+ padding: 0;
378
+ background-color: transparent;
379
+ border: none;
380
+ line-height: normal;
381
+
382
+ &::after {
383
+ display: none;
384
+ }
385
+ }
386
+
387
+ // 悬停效果
388
+ &--hover {
389
+ opacity: 0.8;
390
+ transform: translateY(-1rpx);
391
+ }
392
+
393
+ // 尺寸
394
+ &--mini {
395
+ height: 48rpx;
396
+ padding: 0 16rpx;
397
+ font-size: 24rpx;
398
+ border-radius: 20rpx;
399
+ }
400
+
401
+ &--small {
402
+ height: 64rpx;
403
+ padding: 0 24rpx;
404
+ font-size: 28rpx;
405
+ border-radius: 32rpx;
406
+ }
407
+
408
+ &--medium {
409
+ height: 80rpx;
410
+ padding: 0 32rpx;
58
411
  font-size: 32rpx;
59
- font-weight: 600;
412
+ border-radius: 40rpx;
413
+ }
414
+
415
+ &--large {
416
+ height: 96rpx;
417
+ padding: 0 40rpx;
418
+ font-size: 36rpx;
419
+ border-radius: 48rpx;
420
+ }
421
+
422
+ // 形状
423
+ &--square {
424
+ border-radius: 8rpx;
425
+ }
426
+
427
+ &--round {
428
+ // 圆角已经在尺寸中定义
429
+ }
430
+
431
+ &--circle {
432
+ border-radius: 50%;
433
+ padding: 0;
434
+ width: var(--button-height);
435
+ min-width: var(--button-height);
436
+ }
437
+
438
+ // 类型
439
+ &--default {
440
+ color: #606266;
441
+ background-color: #ffffff;
442
+ border-color: #dcdfe6;
443
+
444
+ &.im-button--plain {
445
+ color: #606266;
446
+ border-color: #dcdfe6;
447
+ }
448
+ }
449
+
450
+ &--primary {
451
+ color: #ffffff;
452
+ background-color: #409eff;
453
+ border-color: #409eff;
454
+
455
+ &.im-button--plain {
456
+ color: #409eff;
457
+ background-color: transparent;
458
+ border-color: #409eff;
459
+ }
460
+ }
461
+
462
+ &--success {
463
+ color: #ffffff;
464
+ background-color: #67c23a;
465
+ border-color: #67c23a;
466
+
467
+ &.im-button--plain {
468
+ color: #67c23a;
469
+ background-color: transparent;
470
+ border-color: #67c23a;
471
+ }
472
+ }
473
+
474
+ &--warning {
475
+ color: #ffffff;
476
+ background-color: #e6a23c;
477
+ border-color: #e6a23c;
478
+
479
+ &.im-button--plain {
480
+ color: #e6a23c;
481
+ background-color: transparent;
482
+ border-color: #e6a23c;
483
+ }
484
+ }
485
+
486
+ &--danger {
487
+ color: #ffffff;
488
+ background-color: #f56c6c;
489
+ border-color: #f56c6c;
490
+
491
+ &.im-button--plain {
492
+ color: #f56c6c;
493
+ background-color: transparent;
494
+ border-color: #f56c6c;
495
+ }
496
+ }
497
+
498
+ &--info {
499
+ color: #ffffff;
500
+ background-color: #909399;
501
+ border-color: #909399;
502
+
503
+ &.im-button--plain {
504
+ color: #909399;
505
+ background-color: transparent;
506
+ border-color: #909399;
507
+ }
508
+ }
509
+
510
+ // 内部元素
511
+ &__loading {
512
+ display: flex;
513
+ align-items: center;
514
+ justify-content: center;
515
+
516
+ &-spinner {
517
+ display: inline-flex;
518
+ align-items: center;
519
+ justify-content: center;
520
+ margin-right: 8rpx;
521
+ }
522
+
523
+ &-dot {
524
+ width: 6rpx;
525
+ height: 6rpx;
526
+ border-radius: 50%;
527
+ margin: 0 2rpx;
528
+ animation: button-loading 1.4s infinite ease-in-out both;
529
+
530
+ @keyframes button-loading {
531
+
532
+ 0%,
533
+ 80%,
534
+ 100% {
535
+ transform: scale(0.6);
536
+ opacity: 0.6;
537
+ }
538
+
539
+ 40% {
540
+ transform: scale(1);
541
+ opacity: 1;
542
+ }
543
+ }
544
+ }
545
+
546
+ &-text {
547
+ font-size: inherit;
548
+ color: inherit;
549
+ }
550
+ }
551
+
552
+ &__icon {
553
+ display: flex;
554
+ align-items: center;
555
+ justify-content: center;
556
+ margin-right: 8rpx;
557
+
558
+ &--right {
559
+ margin-right: 0;
560
+ margin-left: 8rpx;
561
+ order: 1;
562
+ }
563
+
564
+ &-image {
565
+ width: v-bind(iconSize);
566
+ height: v-bind(iconSize);
567
+ }
568
+ }
569
+
570
+ &__text {
571
+ display: inline-block;
572
+ line-height: 1;
573
+ overflow: hidden;
574
+ text-overflow: ellipsis;
575
+ white-space: nowrap;
576
+
577
+ &--small {
578
+ font-size: 24rpx;
579
+ }
580
+
581
+ &--medium {
582
+ font-size: 32rpx;
583
+ }
584
+
585
+ &--large {
586
+ font-size: 36rpx !important;
587
+ }
588
+ }
589
+
590
+ &__badge {
591
+ position: absolute;
592
+ top: -10rpx;
593
+ right: -10rpx;
594
+ z-index: 1;
595
+
596
+ &-content {
597
+ display: flex;
598
+ align-items: center;
599
+ justify-content: center;
600
+ }
601
+
602
+ &--dot {
603
+ width: 16rpx;
604
+ height: 16rpx;
605
+ background-color: #fa3534;
606
+ border-radius: 50%;
607
+ border: 2rpx solid #ffffff;
608
+ }
609
+
610
+ &--text {
611
+ min-width: 32rpx;
612
+ height: 32rpx;
613
+ padding: 0 8rpx;
614
+ background-color: #fa3534;
615
+ border-radius: 16rpx;
616
+ border: 2rpx solid #ffffff;
617
+ }
618
+
619
+ &-text {
620
+ color: #ffffff;
621
+ font-size: 20rpx;
622
+ font-weight: bold;
623
+ line-height: 1;
624
+ }
60
625
  }
61
626
  }
62
627
  </style>