gaoluyang
2026-06-29 27cd042df9aca0383a49f3514bc21958dd890912
src/views/im/home/components/rtc/rtc-call-incoming.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
<script lang="ts" setup>
import type { ImRtcCallNotification } from '../../store/rtcStore'
import { computed } from 'vue'
import { DICT_TYPE } from '#/packages/constants/src'
import { getDictLabel } from '#/packages/effects/hooks/src'
import { IconifyIcon as Icon } from '#/packages/icons/src'
import { useGroupCallMembers } from '../../composables/useGroupCallMembers'
import { UserAvatar } from '../user'
const props = defineProps<{
  accepting?: boolean
  isGroup?: boolean
  payload: ImRtcCallNotification | null
  rejecting?: boolean
}>()
defineEmits<{ accept: []; reject: [] }>()
/** æ¥ç”µæç¤ºæ–‡æ¡ˆï¼›åŒºåˆ†è¯­éŸ³ / è§†é¢‘ */
const tipText = computed(() => {
  if (!props.payload) return ''
  return `邀请你${getDictLabel(DICT_TYPE.IM_RTC_CALL_MEDIA_TYPE, props.payload.mediaType)}通话`
})
/** æŽ¥å¬æŒ‰é’®ç¦ç”¨æ€ */
const acceptDisabled = computed(() => !!props.accepting || !!props.rejecting)
/** æ‹’绝按钮禁用态 */
const rejectDisabled = computed(() => !!props.rejecting || !!props.accepting)
// ç¾¤é€šè¯æˆå‘˜ï¼›ç¼“存为空时用 INVITE è½½è·é‡Œçš„主叫兜底,避免空白
const callMembers = useGroupCallMembers(
  computed(() => (props.isGroup ? props.payload?.groupId : undefined)),
  computed(() => props.payload?.inviterUserId)
)
</script>
<template>
  <!--
    è¢«å«ç«¯æ¥ç”µå°æ¡ï¼›å‚考微信,右上角固定悬浮;
    ç¾¤èŠï¼šå·¦å¤´åƒ + ä¸­ï¼ˆé‚€è¯·äºº + æ–‡æ¡ˆä¸€è¡Œ / é€šè¯æˆå‘˜è¡Œï¼‰+ å³ä¸‹è§’拒绝 / æŽ¥å¬ï¼›
    ç§èŠï¼šå·¦å¤´åƒ + ä¸­ï¼ˆé‚€è¯·äºº + æ–‡æ¡ˆä¸¤è¡Œï¼‰+ å³ä¸‹è§’拒绝 / æŽ¥å¬
  -->
  <div
    class="fixed top-5 right-5 rounded-2xl overflow-hidden shadow-[0_12px_36px_rgba(0,0,0,0.4)] z-[9999] flex gap-3 items-end p-3 text-white bg-[#2a2a2c]"
    :class="isGroup ? 'w-[360px]' : 'w-[340px]'"
  >
    <!-- å·¦ï¼šé‚€è¯·è€…头像 -->
    <UserAvatar
      :url="payload?.inviterAvatar"
      :name="payload?.inviterNickname"
      :size="48"
      radius="8px"
      :clickable="false"
      class="self-start"
    />
    <!-- ä¸­ï¼šç¾¤èŠå•行(邀请人 + æ–‡æ¡ˆï¼‰+ é€šè¯æˆå‘˜ï¼›ç§èŠä¸¤è¡Œï¼ˆå / æ–‡æ¡ˆï¼‰ -->
    <div class="flex flex-col flex-1 gap-1 self-start min-w-0">
      <!-- å + æ–‡æ¡ˆï¼šç¾¤å•行内联,私聊上下两行 -->
      <div v-if="isGroup" class="text-sm truncate">
        <span class="font-medium">{{ payload?.inviterNickname || '对方' }}</span>
        <span class="ml-1 text-white/60">{{ tipText }}</span>
      </div>
      <template v-else>
        <div class="text-sm font-medium truncate">{{ payload?.inviterNickname || '对方' }}</div>
        <div class="text-13px text-white/60 truncate">{{ tipText }}</div>
      </template>
      <!-- ç¾¤é€šè¯æˆå‘˜è¡Œï¼›ç§èŠæ— ï¼›æŽ¥å…¥ä¸­çš„人半透明展示 -->
      <template v-if="isGroup && callMembers.length > 0">
        <div class="mt-1 text-xs text-white/45">通话成员</div>
        <div class="flex flex-wrap gap-1">
          <UserAvatar
            v-for="member in callMembers"
            :key="member.userId"
            :url="member.avatar"
            :name="member.nickname"
            :size="22"
            radius="4px"
            :clickable="false"
            :class="{ 'opacity-50': member.pending }"
            :title="member.pending ? `${member.nickname}(接入中)` : member.nickname"
          />
        </div>
      </template>
    </div>
    <!-- å³ä¸‹è§’:拒绝 / æŽ¥å¬ -->
    <div class="flex flex-shrink-0 gap-2 items-center">
      <button
        class="flex flex-shrink-0 justify-center items-center w-10 h-10 text-white rounded-full transition-opacity bg-[#f04a4a] hover:opacity-90"
        :class="{ 'opacity-60 cursor-not-allowed': rejectDisabled }"
        :disabled="rejectDisabled"
        @click="$emit('reject')"
      >
        <Icon icon="ant-design:phone-outlined" :size="18" class="rotate-[135deg]" />
      </button>
      <button
        class="flex flex-shrink-0 justify-center items-center w-10 h-10 text-white rounded-full transition-opacity bg-[#2ec27e] hover:opacity-90"
        :class="{ 'opacity-60 cursor-not-allowed': acceptDisabled }"
        :disabled="acceptDisabled"
        @click="$emit('accept')"
      >
        <Icon icon="ant-design:phone-outlined" :size="18" />
      </button>
    </div>
  </div>
</template>