From 27cd042df9aca0383a49f3514bc21958dd890912 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 29 六月 2026 15:42:23 +0800
Subject: [PATCH] 银川 1.联调产品维护页面 2.添加IM即时通讯模块
---
src/views/im/home/components/rtc/rtc-call-participant-tile.vue | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 110 insertions(+), 0 deletions(-)
diff --git a/src/views/im/home/components/rtc/rtc-call-participant-tile.vue b/src/views/im/home/components/rtc/rtc-call-participant-tile.vue
new file mode 100644
index 0000000..6a12111
--- /dev/null
+++ b/src/views/im/home/components/rtc/rtc-call-participant-tile.vue
@@ -0,0 +1,110 @@
+<script lang="ts" setup>
+import { useMediaStreamElement } from '../../composables/useMediaStreamElement'
+import { UserAvatar } from '../user'
+
+export interface CallParticipantVM {
+ userId: number
+ nickname: string
+ avatar?: string
+ isLocal: boolean
+ videoStream?: MediaStream | null
+ audioStream?: MediaStream | null
+ /** 绛夊緟鍔犲叆锛沀I 鏄剧ず涓夌偣鍔ㄧ敾 */
+ pending?: boolean
+}
+
+const props = defineProps<{
+ participant: CallParticipantVM
+ /** 鎵0鍣ㄥ紑鍏筹紱涓� false 鏃堕潤闊宠鏍煎瓙鐨勮繙绔煶棰� */
+ speakerEnabled: boolean
+}>()
+
+const setVideoRef = useMediaStreamElement<HTMLVideoElement>(() => props.participant.videoStream)
+const setAudioRef = useMediaStreamElement<HTMLAudioElement>(() => props.participant.audioStream)
+</script>
+
+<template>
+ <!--
+ 缇ら�氳瘽瀹牸閲岀殑鍗曚釜鍙備笌鑰呮牸瀛愶紱
+ 浼樺厛娓叉煋杩滅瑙嗛锛堟湁 videoStream 鏃堕摵婊★級锛屾棤瑙嗛闄嶇骇娓叉煋澶村儚 + 鍚嶅瓧鑳跺泭锛�
+ pending=true 鏃跺彔鍔犮�屾帴鍏ヤ腑銆嶄笁鐐瑰姩鐢诲崰浣嶏紝鐢ㄤ簬琚個璇蜂汉鏈帴閫氬墠鐨勫崰浣嶆覆鏌擄紱
+ 杩滅闊抽閫氳繃闅愯棌 audio 鎾斁锛堟湰绔� isLocal 涓嶆覆鏌撻伩鍏嶅洖澹帮級
+ -->
+ <div
+ class="flex relative overflow-hidden justify-center items-center w-full h-full rounded-md bg-[#2a2a2c]"
+ >
+ <!-- 瑙嗛鍙敤锛氭覆鏌� video锛涘惁鍒欐覆鏌撳ご鍍忔垨榛樿鍗犱綅 -->
+ <video
+ v-if="participant.videoStream"
+ :ref="setVideoRef"
+ class="object-cover w-full h-full"
+ autoplay
+ playsinline
+ :muted="participant.isLocal"
+ ></video>
+ <div v-else class="flex justify-center items-center w-full h-full">
+ <UserAvatar
+ :url="participant.avatar"
+ :name="participant.nickname"
+ :size="64"
+ radius="50%"
+ :clickable="false"
+ />
+ </div>
+
+ <!-- 杩滅闊抽锛涢�氳繃 audio 鍏冪礌鎾斁锛屾湰绔潤闊抽伩鍏嶅洖澹帮紱鎵0鍣ㄥ叧闂椂鏁翠綋闈欓煶 -->
+ <audio
+ v-if="participant.audioStream && !participant.isLocal"
+ :ref="setAudioRef"
+ autoplay
+ :muted="!speakerEnabled"
+ ></audio>
+
+ <!-- 宸︿笅瑙掑悕瀛楄兌鍥� -->
+ <div
+ class="flex absolute bottom-3 left-3 gap-1.5 items-center py-[3px] pr-2.5 pl-[3px] text-13px text-white rounded-full bg-black/45 max-w-[calc(100%-60px)]"
+ >
+ <UserAvatar
+ :url="participant.avatar"
+ :name="participant.nickname"
+ :size="22"
+ radius="50%"
+ :clickable="false"
+ />
+ <span class="truncate min-w-0">{{ participant.nickname }}</span>
+ </div>
+
+ <!-- 绛夊緟瀵规柟鍔犲叆锛氫笁鐐瑰姩鐢� +銆屾帴鍏ヤ腑銆嶆枃妗堬紱浣嶇疆鍦ㄦ牸瀛愪腑蹇冧笅鏂� 60px -->
+ <div
+ v-if="participant.pending"
+ class="flex absolute left-1/2 flex-col gap-1.5 items-center -translate-x-1/2 -translate-y-1/2 top-[calc(50%+60px)]"
+ >
+ <div class="flex gap-1.5">
+ <span
+ v-for="i in 3"
+ :key="i"
+ class="tile-dot w-1.5 h-1.5 rounded-full bg-white/60"
+ :style="{ animationDelay: `${(i - 1) * 0.2}s` }"
+ ></span>
+ </div>
+ <span class="text-xs text-white/70">鎺ュ叆涓�</span>
+ </div>
+ </div>
+</template>
+
+<style scoped>
+/* 涓夌偣娣″叆娣″嚭鍔ㄧ敾锛汙keyframes 蹇呴』 CSS 瀹氫箟 */
+.tile-dot {
+ animation: tile-dot 1.4s infinite ease-in-out both;
+}
+@keyframes tile-dot {
+ 0%,
+ 80%,
+ 100% {
+ opacity: 0.3;
+ }
+ 40% {
+ opacity: 1;
+ }
+}
+</style>
--
Gitblit v1.9.3