im-ui-mobile 0.1.0 → 0.1.2

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 (85) hide show
  1. package/components/im-avatar/im-avatar.vue +7 -7
  2. package/components/im-badge/im-badge.vue +326 -0
  3. package/components/im-button/im-button.vue +71 -34
  4. package/components/im-card/im-card.vue +563 -0
  5. package/components/im-chat-item/im-chat-item.vue +5 -4
  6. package/components/im-col/im-col.vue +191 -0
  7. package/components/im-dialog/im-dialog.vue +543 -0
  8. package/components/im-double-tap-view/im-double-tap-view.vue +93 -0
  9. package/components/im-emoji-picker/im-emoji-picker.vue +1143 -0
  10. package/components/im-friend-item/im-friend-item.vue +1 -1
  11. package/components/im-group-item/im-group-item.vue +1 -1
  12. package/components/im-group-member-selector/im-group-member-selector.vue +5 -5
  13. package/components/im-group-rtc-join/im-group-rtc-join.vue +8 -8
  14. package/components/im-icon/im-icon.vue +593 -0
  15. package/components/im-image-upload/im-image-upload.vue +0 -2
  16. package/components/im-link/im-link.vue +628 -0
  17. package/components/im-loading/im-loading.vue +13 -4
  18. package/components/im-mention-picker/im-mention-picker.vue +8 -7
  19. package/components/im-message-action/im-message-action.vue +678 -0
  20. package/components/im-message-item/im-message-item.vue +28 -26
  21. package/components/im-message-list/im-message-list.vue +1108 -0
  22. package/components/im-modal/im-modal.vue +373 -0
  23. package/components/im-nav-bar/im-nav-bar.vue +689 -75
  24. package/components/im-parse/im-parse.vue +1054 -0
  25. package/components/im-popup/im-popup.vue +467 -0
  26. package/components/im-read-receipt/im-read-receipt.vue +10 -10
  27. package/components/im-row/im-row.vue +189 -0
  28. package/components/im-search/im-search.vue +762 -0
  29. package/components/im-sku/im-sku.vue +720 -0
  30. package/components/im-sku/utils/helper.ts +182 -0
  31. package/components/im-stepper/im-stepper.vue +585 -0
  32. package/components/im-stepper/utils/helper.ts +167 -0
  33. package/components/im-tabs/im-tabs.vue +1022 -0
  34. package/components/im-tabs/tabs-navigation.vue +489 -0
  35. package/components/im-tabs/utils/helper.ts +181 -0
  36. package/components/im-tabs-tab-pane/im-tabs-tab-pane.vue +145 -0
  37. package/components/im-upload/im-upload.vue +1236 -0
  38. package/components/im-voice-input/im-voice-input.vue +1 -1
  39. package/index.js +3 -5
  40. package/index.scss +19 -0
  41. package/libs/emoji-data.ts +229 -0
  42. package/libs/index.ts +16 -16
  43. package/package.json +1 -2
  44. package/styles/button.scss +33 -33
  45. package/theme.scss +2 -2
  46. package/types/components/badge.d.ts +42 -0
  47. package/types/components/button.d.ts +2 -1
  48. package/types/components/card.d.ts +122 -0
  49. package/types/components/col.d.ts +37 -0
  50. package/types/components/dialog.d.ts +125 -0
  51. package/types/components/double-tap-view.d.ts +31 -0
  52. package/types/components/emoji-picker.d.ts +121 -0
  53. package/types/components/group-rtc-join.d.ts +1 -1
  54. package/types/components/icon.d.ts +77 -0
  55. package/types/components/link.d.ts +55 -0
  56. package/types/components/loading.d.ts +1 -0
  57. package/types/components/message-action.d.ts +96 -0
  58. package/types/components/message-item.d.ts +2 -2
  59. package/types/components/message-list.d.ts +136 -0
  60. package/types/components/modal.d.ts +106 -0
  61. package/types/components/nav-bar.d.ts +125 -0
  62. package/types/components/parse.d.ts +90 -0
  63. package/types/components/popup.d.ts +58 -0
  64. package/types/components/row.d.ts +31 -0
  65. package/types/components/search.d.ts +54 -0
  66. package/types/components/sku.d.ts +195 -0
  67. package/types/components/stepper.d.ts +99 -0
  68. package/types/components/tabs-tab-pane.d.ts +27 -0
  69. package/types/components/tabs.d.ts +117 -0
  70. package/types/components/upload.d.ts +137 -0
  71. package/types/components.d.ts +19 -1
  72. package/types/index.d.ts +38 -1
  73. package/types/libs/index.d.ts +10 -10
  74. package/types/utils/base64.d.ts +5 -0
  75. package/types/utils/dom.d.ts +3 -0
  76. package/types/utils/enums.d.ts +4 -5
  77. package/types/utils/validator.d.ts +74 -0
  78. package/utils/base64.js +18 -0
  79. package/utils/dom.js +353 -1
  80. package/utils/enums.js +4 -5
  81. package/utils/validator.js +230 -0
  82. package/components/im-file-upload/im-file-upload.vue +0 -309
  83. package/plugins/uview-plus.js +0 -29
  84. package/types/components/arrow-bar.d.ts +0 -14
  85. package/types/components/file-upload.d.ts +0 -58
@@ -0,0 +1,678 @@
1
+ <template>
2
+ <!-- 遮罩层 -->
3
+ <view v-if="visible" class="im-message-action-mask" :class="{ 'im-message-action-mask--show': showMask }"
4
+ @click="close" @touchmove.prevent></view>
5
+
6
+ <!-- 操作菜单 -->
7
+ <view v-if="visible" class="im-message-action" :class="[
8
+ `im-message-action--${position}`,
9
+ `im-message-action--${theme}`,
10
+ { 'im-message-action--show': showMenu }
11
+ ]" :style="menuStyle" @touchmove.stop>
12
+ <!-- 操作项列表 -->
13
+ <scroll-view v-if="actions.length > 0" class="im-message-action__list" scroll-y :show-scrollbar="false">
14
+ <view v-for="(action, index) in filteredActions" :key="action.key || index" class="im-message-action__item"
15
+ :class="[
16
+ `im-message-action__item--${action.type || 'default'}`,
17
+ {
18
+ 'im-message-action__item--disabled': action.disabled,
19
+ 'im-message-action__item--danger': action.danger,
20
+ 'im-message-action__item--divider': action.divider
21
+ }
22
+ ]" @click="handleItemClick(action)">
23
+ <!-- 分隔线 -->
24
+ <view v-if="action.divider" class="im-message-action__divider"></view>
25
+
26
+ <!-- 正常操作项 -->
27
+ <template v-else>
28
+ <!-- 图标 -->
29
+ <view class="im-message-action__icon" v-if="action.icon">
30
+ <im-icon :name="action.icon" :size="iconSize" :color="getIconColor(action)"
31
+ :spin="action.loading" />
32
+ </view>
33
+
34
+ <!-- 文本 -->
35
+ <text class="im-message-action__text" :style="{ color: getTextColor(action) }">
36
+ {{ action.text }}
37
+ </text>
38
+
39
+ <!-- 角标/提示 -->
40
+ <view v-if="action.badge || action.hint" class="im-message-action__extra">
41
+ <im-badge v-if="action.badge" :content="action.badge" :type="action.badgeType || 'danger'"
42
+ size="mini" />
43
+ <text v-else-if="action.hint" class="im-message-action__hint">
44
+ {{ action.hint }}
45
+ </text>
46
+ </view>
47
+ </template>
48
+ </view>
49
+ </scroll-view>
50
+
51
+ <!-- 自定义内容插槽 -->
52
+ <slot v-else></slot>
53
+
54
+ <!-- 取消按钮 -->
55
+ <view v-if="showCancel" class="im-message-action__cancel" @click="handleCancel">
56
+ <text class="im-message-action__cancel-text">{{ cancelText }}</text>
57
+ </view>
58
+
59
+ <!-- 三角形箭头 -->
60
+ <view v-if="showArrow && position.includes('top')" class="im-message-action__arrow" :style="arrowStyle"></view>
61
+ </view>
62
+ </template>
63
+
64
+ <script setup lang="ts">
65
+ import { ref, computed, watch, nextTick } from 'vue'
66
+
67
+ // 类型定义
68
+ interface MessageAction {
69
+ key: string
70
+ text: string
71
+ icon?: string
72
+ type?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info'
73
+ disabled?: boolean
74
+ danger?: boolean
75
+ loading?: boolean
76
+ hidden?: boolean
77
+ divider?: boolean // 是否为分隔线
78
+ badge?: string | number
79
+ badgeType?: 'primary' | 'success' | 'warning' | 'danger' | 'info'
80
+ hint?: string
81
+ data?: any
82
+ handler?: () => void | Promise<void>
83
+ }
84
+
85
+ interface Props {
86
+ // 显示控制
87
+ visible?: boolean
88
+ position?: 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
89
+ theme?: 'light' | 'dark' | 'card'
90
+
91
+ // 数据
92
+ actions?: MessageAction[]
93
+ message?: any // 关联的消息对象
94
+
95
+ // 配置
96
+ showCancel?: boolean
97
+ cancelText?: string
98
+ maxHeight?: string | number
99
+ width?: string | number
100
+ borderRadius?: string | number
101
+ showArrow?: boolean
102
+ arrowSize?: number
103
+ iconSize?: number
104
+
105
+ // 位置控制
106
+ x?: number
107
+ y?: number
108
+ offsetX?: number
109
+ offsetY?: number
110
+ fitViewport?: boolean // 是否适应视口边界
111
+
112
+ // 动画
113
+ animation?: boolean
114
+ animationDuration?: number
115
+
116
+ // 遮罩
117
+ showMask?: boolean
118
+ maskClosable?: boolean
119
+ maskColor?: string
120
+ }
121
+
122
+ const props = withDefaults(defineProps<Props>(), {
123
+ visible: false,
124
+ position: 'bottom',
125
+ theme: 'light',
126
+ actions: () => [],
127
+ showCancel: true,
128
+ cancelText: '取消',
129
+ maxHeight: '400rpx',
130
+ width: '300rpx',
131
+ borderRadius: '16rpx',
132
+ showArrow: true,
133
+ arrowSize: 12,
134
+ iconSize: 20,
135
+ x: 0,
136
+ y: 0,
137
+ offsetX: 0,
138
+ offsetY: 10,
139
+ fitViewport: true,
140
+ animation: true,
141
+ animationDuration: 300,
142
+ showMask: true,
143
+ maskClosable: true,
144
+ maskColor: 'rgba(0, 0, 0, 0.5)'
145
+ })
146
+
147
+ const emit = defineEmits<{
148
+ // 基础事件
149
+ 'update:visible': [visible: boolean]
150
+ 'open': []
151
+ 'close': []
152
+ 'cancel': []
153
+
154
+ // 操作事件
155
+ 'select': [action: MessageAction, message?: any]
156
+ 'click': [actionKey: string, message?: any]
157
+
158
+ // 生命周期
159
+ 'before-open': []
160
+ 'after-open': []
161
+ 'before-close': []
162
+ 'after-close': []
163
+ }>()
164
+
165
+ // 响应式数据
166
+ const showMenu = ref(false)
167
+ const showMask = ref(false)
168
+ const menuRect = ref({ width: 0, height: 0 })
169
+ const viewportRect = ref({ width: 0, height: 0 })
170
+ const adjustedPosition = ref('bottom')
171
+
172
+ // 计算属性
173
+ const filteredActions = computed(() => {
174
+ return props.actions.filter(action => !action.hidden)
175
+ })
176
+
177
+ const menuStyle = computed(() => {
178
+ const style: Record<string, string> = {
179
+ '--max-height': typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight,
180
+ '--width': typeof props.width === 'number' ? `${props.width}px` : props.width,
181
+ '--border-radius': typeof props.borderRadius === 'number' ? `${props.borderRadius}px` : props.borderRadius,
182
+ '--animation-duration': `${props.animationDuration}ms`
183
+ }
184
+
185
+ // 动态位置
186
+ const positionStyle = getPositionStyle()
187
+ Object.assign(style, positionStyle)
188
+
189
+ return style
190
+ })
191
+
192
+ const arrowStyle = computed(() => {
193
+ const style: Record<string, string> = {
194
+ '--arrow-size': `${props.arrowSize}px`
195
+ }
196
+
197
+ if (props.position.includes('top')) {
198
+ style.bottom = `-${props.arrowSize}px`
199
+ } else {
200
+ style.top = `-${props.arrowSize}px`
201
+ }
202
+
203
+ // 箭头位置对齐
204
+ if (props.position.includes('left')) {
205
+ style.left = '20rpx'
206
+ } else if (props.position.includes('right')) {
207
+ style.right = '20rpx'
208
+ } else {
209
+ style.left = '50%'
210
+ style.transform = 'translateX(-50%)'
211
+ }
212
+
213
+ return style
214
+ })
215
+
216
+ // 方法
217
+ const getPositionStyle = () => {
218
+ const style: Record<string, string> = {}
219
+ const { x, y, offsetX, offsetY } = props
220
+
221
+ // 获取视口尺寸
222
+ const getViewportSize = () => {
223
+ const systemInfo = uni.getSystemInfoSync()
224
+ return {
225
+ width: systemInfo.windowWidth,
226
+ height: systemInfo.windowHeight
227
+ }
228
+ }
229
+
230
+ if (!viewportRect.value.width) {
231
+ viewportRect.value = getViewportSize()
232
+ }
233
+
234
+ let finalX = x + offsetX
235
+ let finalY = y + offsetY
236
+
237
+ // 适应视口边界
238
+ if (props.fitViewport && menuRect.value.width > 0) {
239
+ // 检查右侧边界
240
+ if (finalX + menuRect.value.width > viewportRect.value.width) {
241
+ finalX = viewportRect.value.width - menuRect.value.width - 10
242
+ }
243
+
244
+ // 检查左侧边界
245
+ if (finalX < 10) {
246
+ finalX = 10
247
+ }
248
+
249
+ // 检查底部边界(如果是top位置)
250
+ if (props.position.includes('top') && finalY + menuRect.value.height > viewportRect.value.height) {
251
+ // 如果超出底部,切换到bottom位置
252
+ adjustedPosition.value = props.position.replace('top', 'bottom')
253
+ finalY = y - offsetY - menuRect.value.height
254
+ }
255
+
256
+ // 检查顶部边界(如果是bottom位置)
257
+ if (props.position.includes('bottom') && finalY - menuRect.value.height < 0) {
258
+ // 如果超出顶部,切换到top位置
259
+ adjustedPosition.value = props.position.replace('bottom', 'top')
260
+ finalY = y + offsetY
261
+ }
262
+ }
263
+
264
+ // 设置位置
265
+ if (props.position.includes('left')) {
266
+ style.left = `${finalX}px`
267
+ } else if (props.position.includes('right')) {
268
+ style.right = `${viewportRect.value.width - finalX}px`
269
+ } else {
270
+ style.left = '50%'
271
+ style.transform = `translateX(-50%)`
272
+ style.top = `${finalY}px`
273
+ }
274
+
275
+ if (props.position.includes('top')) {
276
+ style.top = `${finalY}px`
277
+ } else if (props.position.includes('bottom')) {
278
+ style.bottom = `${viewportRect.value.height - finalY}px`
279
+ }
280
+
281
+ return style
282
+ }
283
+
284
+ const getIconColor = (action: MessageAction): string => {
285
+ if (action.disabled) return '#C0C4CC'
286
+
287
+ switch (action.type) {
288
+ case 'primary': return '#409EFF'
289
+ case 'success': return '#67C23A'
290
+ case 'warning': return '#E6A23C'
291
+ case 'danger': return '#F56C6C'
292
+ case 'info': return '#909399'
293
+ default: return '#606266'
294
+ }
295
+ }
296
+
297
+ const getTextColor = (action: MessageAction): string => {
298
+ if (action.disabled) return '#C0C4CC'
299
+ if (action.danger) return '#F56C6C'
300
+
301
+ switch (action.type) {
302
+ case 'primary': return '#409EFF'
303
+ case 'success': return '#67C23A'
304
+ case 'warning': return '#E6A23C'
305
+ case 'danger': return '#F56C6C'
306
+ case 'info': return '#909399'
307
+ default: return '#303133'
308
+ }
309
+ }
310
+
311
+ const handleItemClick = async (action: MessageAction) => {
312
+ if (action.disabled || action.loading) return
313
+
314
+ // 触发选择事件
315
+ emit('select', action, props.message)
316
+ emit('click', action.key, props.message)
317
+
318
+ // 执行自定义处理器
319
+ if (action.handler) {
320
+ try {
321
+ await action.handler()
322
+ } catch (error) {
323
+ console.error('Action handler error:', error)
324
+ }
325
+ }
326
+
327
+ // 非持续显示的操作项,执行后关闭
328
+ if (!action.loading) {
329
+ close()
330
+ }
331
+ }
332
+
333
+ const handleCancel = () => {
334
+ emit('cancel')
335
+ close()
336
+ }
337
+
338
+ const open = () => {
339
+ if (props.visible) return
340
+
341
+ emit('before-open')
342
+ emit('update:visible', true)
343
+
344
+ nextTick(() => {
345
+ setTimeout(() => {
346
+ showMask.value = true
347
+ showMenu.value = true
348
+ emit('open')
349
+ emit('after-open')
350
+ }, 50)
351
+ })
352
+ }
353
+
354
+ const close = () => {
355
+ if (!props.visible) return
356
+
357
+ emit('before-close')
358
+ showMenu.value = false
359
+ showMask.value = false
360
+
361
+ setTimeout(() => {
362
+ emit('update:visible', false)
363
+ emit('close')
364
+ emit('after-close')
365
+ adjustedPosition.value = props.position
366
+ }, props.animationDuration)
367
+ }
368
+
369
+ const updatePosition = (x: number, y: number) => {
370
+ // 更新位置的方法
371
+ const { offsetX, offsetY } = props
372
+
373
+ // 重新计算位置
374
+ const systemInfo = uni.getSystemInfoSync()
375
+ viewportRect.value = {
376
+ width: systemInfo.windowWidth,
377
+ height: systemInfo.windowHeight
378
+ }
379
+
380
+ // 触发重新计算
381
+ nextTick()
382
+ }
383
+
384
+ // 监听可见性变化
385
+ watch(() => props.visible, (newVal) => {
386
+ if (newVal) {
387
+ open()
388
+ } else {
389
+ close()
390
+ }
391
+ })
392
+
393
+ // 监听位置变化
394
+ watch(() => props.x, () => {
395
+ if (props.visible) {
396
+ nextTick()
397
+ }
398
+ })
399
+
400
+ watch(() => props.y, () => {
401
+ if (props.visible) {
402
+ nextTick()
403
+ }
404
+ })
405
+
406
+ // 暴露给父组件的方法
407
+ defineExpose({
408
+ open,
409
+ close,
410
+ updatePosition,
411
+ updateActions: (newActions: MessageAction[]) => {
412
+ // 可以通过ref调用更新actions
413
+ }
414
+ })
415
+ </script>
416
+
417
+ <style scoped lang="scss">
418
+ .im-message-action-mask {
419
+ position: fixed;
420
+ top: 0;
421
+ left: 0;
422
+ right: 0;
423
+ bottom: 0;
424
+ background-color: v-bind('props.maskColor');
425
+ z-index: 9998;
426
+ opacity: 0;
427
+ transition: opacity var(--animation-duration, 300ms) ease;
428
+
429
+ &--show {
430
+ opacity: 1;
431
+ }
432
+ }
433
+
434
+ .im-message-action {
435
+ position: fixed;
436
+ z-index: 9999;
437
+ min-width: 200rpx;
438
+ max-width: 90vw;
439
+ max-height: var(--max-height, 400rpx);
440
+ width: var(--width, 300rpx);
441
+ background-color: #ffffff;
442
+ border-radius: var(--border-radius, 16rpx);
443
+ box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
444
+ overflow: hidden;
445
+ opacity: 0;
446
+ transform: translateY(20rpx) scale(0.95);
447
+ transition: all var(--animation-duration, 300ms) cubic-bezier(0.34, 1.56, 0.64, 1);
448
+
449
+ // 主题变体
450
+ &--light {
451
+ background-color: #ffffff;
452
+ color: #303133;
453
+ }
454
+
455
+ &--dark {
456
+ background-color: #2a2a2a;
457
+ color: #ffffff;
458
+ }
459
+
460
+ &--card {
461
+ background-color: #ffffff;
462
+ border: 1rpx solid #e4e7ed;
463
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
464
+ }
465
+
466
+ // 显示状态
467
+ &--show {
468
+ opacity: 1;
469
+ transform: translateY(0) scale(1);
470
+ }
471
+
472
+ // 位置变体
473
+ &--top {
474
+ transform-origin: bottom center;
475
+ }
476
+
477
+ &--bottom {
478
+ transform-origin: top center;
479
+ }
480
+
481
+ &--top-left {
482
+ transform-origin: bottom right;
483
+ }
484
+
485
+ &--top-right {
486
+ transform-origin: bottom left;
487
+ }
488
+
489
+ &--bottom-left {
490
+ transform-origin: top right;
491
+ }
492
+
493
+ &--bottom-right {
494
+ transform-origin: top left;
495
+ }
496
+ }
497
+
498
+ .im-message-action__list {
499
+ max-height: calc(var(--max-height, 400rpx) - 100rpx);
500
+ }
501
+
502
+ .im-message-action__item {
503
+ display: flex;
504
+ align-items: center;
505
+ padding: 28rpx 32rpx;
506
+ position: relative;
507
+ cursor: pointer;
508
+ transition: background-color 0.2s ease;
509
+
510
+ &:active:not(&--disabled) {
511
+ background-color: #f5f7fa;
512
+
513
+ .im-message-action--dark & {
514
+ background-color: rgba(255, 255, 255, 0.1);
515
+ }
516
+
517
+ .im-message-action--card & {
518
+ background-color: #fafafa;
519
+ }
520
+ }
521
+
522
+ &--disabled {
523
+ cursor: not-allowed;
524
+ opacity: 0.5;
525
+ }
526
+
527
+ &--danger {
528
+ .im-message-action__text {
529
+ color: #f56c6c;
530
+ }
531
+ }
532
+
533
+ &--divider {
534
+ padding: 0;
535
+ height: 1rpx;
536
+ background-color: #e4e7ed;
537
+
538
+ .im-message-action--dark & {
539
+ background-color: rgba(255, 255, 255, 0.1);
540
+ }
541
+ }
542
+ }
543
+
544
+ .im-message-action__divider {
545
+ width: 100%;
546
+ height: 1rpx;
547
+ background-color: #e4e7ed;
548
+
549
+ .im-message-action--dark & {
550
+ background-color: rgba(255, 255, 255, 0.1);
551
+ }
552
+ }
553
+
554
+ .im-message-action__icon {
555
+ margin-right: 16rpx;
556
+ display: flex;
557
+ align-items: center;
558
+ justify-content: center;
559
+ width: 32rpx;
560
+ flex-shrink: 0;
561
+ }
562
+
563
+ .im-message-action__text {
564
+ flex: 1;
565
+ font-size: 30rpx;
566
+ line-height: 1.4;
567
+ overflow: hidden;
568
+ text-overflow: ellipsis;
569
+ white-space: nowrap;
570
+ }
571
+
572
+ .im-message-action__extra {
573
+ margin-left: 16rpx;
574
+ flex-shrink: 0;
575
+ }
576
+
577
+ .im-message-action__hint {
578
+ font-size: 24rpx;
579
+ color: #909399;
580
+
581
+ .im-message-action--dark & {
582
+ color: #aaaaaa;
583
+ }
584
+ }
585
+
586
+ .im-message-action__cancel {
587
+ border-top: 1rpx solid #e4e7ed;
588
+ padding: 28rpx 32rpx;
589
+ text-align: center;
590
+ cursor: pointer;
591
+ transition: background-color 0.2s ease;
592
+
593
+ .im-message-action--dark & {
594
+ border-top-color: rgba(255, 255, 255, 0.1);
595
+ }
596
+
597
+ &:active {
598
+ background-color: #f5f7fa;
599
+
600
+ .im-message-action--dark & {
601
+ background-color: rgba(255, 255, 255, 0.1);
602
+ }
603
+ }
604
+ }
605
+
606
+ .im-message-action__cancel-text {
607
+ font-size: 30rpx;
608
+ color: #606266;
609
+ font-weight: 500;
610
+
611
+ .im-message-action--dark & {
612
+ color: #cccccc;
613
+ }
614
+ }
615
+
616
+ .im-message-action__arrow {
617
+ position: absolute;
618
+ width: 0;
619
+ height: 0;
620
+ border-style: solid;
621
+
622
+ .im-message-action--top & {
623
+ border-width: var(--arrow-size, 12px) var(--arrow-size, 12px) 0 var(--arrow-size, 12px);
624
+ border-color: #ffffff transparent transparent transparent;
625
+
626
+ .im-message-action--dark & {
627
+ border-color: #2a2a2a transparent transparent transparent;
628
+ }
629
+
630
+ .im-message-action--card & {
631
+ border-color: #ffffff transparent transparent transparent;
632
+ }
633
+ }
634
+
635
+ .im-message-action--bottom & {
636
+ border-width: 0 var(--arrow-size, 12px) var(--arrow-size, 12px) var(--arrow-size, 12px);
637
+ border-color: transparent transparent #ffffff transparent;
638
+
639
+ .im-message-action--dark & {
640
+ border-color: transparent transparent #2a2a2a transparent;
641
+ }
642
+
643
+ .im-message-action--card & {
644
+ border-color: transparent transparent #ffffff transparent;
645
+ }
646
+ }
647
+ }
648
+
649
+ // 移除默认的滚动条
650
+ .im-message-action ::-webkit-scrollbar {
651
+ display: none;
652
+ width: 0;
653
+ height: 0;
654
+ color: transparent;
655
+ }
656
+
657
+ // 暗色模式适配
658
+ @media (prefers-color-scheme: dark) {
659
+ .im-message-action--light {
660
+ background-color: #2a2a2a;
661
+ color: #ffffff;
662
+ box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.3);
663
+ }
664
+ }
665
+
666
+ // 响应式调整
667
+ @media (max-width: 768px) {
668
+ .im-message-action {
669
+ width: 80vw;
670
+ max-width: 400rpx;
671
+ }
672
+
673
+ .im-message-action__item,
674
+ .im-message-action__cancel {
675
+ padding: 24rpx 28rpx;
676
+ }
677
+ }
678
+ </style>