@easemob-community/callkit-vue3 2.0.1
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.
- package/LICENSE +21 -0
- package/README.md +208 -0
- package/dist/callkit-vue3.css +1 -0
- package/dist/components/EasemobChatCallKitProvider.vue.d.ts +18 -0
- package/dist/components/EasemobChatMiniWindow.vue.d.ts +11 -0
- package/dist/components/InvitationNotification.vue.d.ts +2 -0
- package/dist/components/multiCall/EasemobChatGroupMemberList.vue.d.ts +19 -0
- package/dist/components/multiCall/EasemobChatMultiCall.vue.d.ts +69 -0
- package/dist/components/singleCall/CallControls.vue.d.ts +18 -0
- package/dist/components/singleCall/CallInfoBar.vue.d.ts +5 -0
- package/dist/components/singleCall/EasemobChatCallStream.vue.d.ts +12 -0
- package/dist/components/singleCall/EasemobChatCallWaiting.vue.d.ts +10 -0
- package/dist/components/singleCall/EasemobChatSingleCall.vue.d.ts +28 -0
- package/dist/composables/useAnswerCall.d.ts +10 -0
- package/dist/composables/useCallKit.d.ts +2 -0
- package/dist/composables/useCallKitCore.d.ts +998 -0
- package/dist/composables/useCallKitEvents.d.ts +42 -0
- package/dist/composables/useDraggable.d.ts +90 -0
- package/dist/composables/useEndCall.d.ts +15 -0
- package/dist/composables/useParticipants.d.ts +15 -0
- package/dist/composables/useRtcService.d.ts +80 -0
- package/dist/config/assets.d.ts +43 -0
- package/dist/core/events/CallKitEventBus.d.ts +40 -0
- package/dist/core/events/helpers.d.ts +59 -0
- package/dist/core/events/types.d.ts +264 -0
- package/dist/core/sdk/imSDK/index.d.ts +4 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +7806 -0
- package/dist/index.umd.js +72 -0
- package/dist/modules/groupCall/components/CallKitIcon.vue.d.ts +12 -0
- package/dist/modules/groupCall/components/GroupCallShell.vue.d.ts +34 -0
- package/dist/modules/groupCall/components/MainVideoLayout.vue.d.ts +32 -0
- package/dist/modules/groupCall/components/ParticipantTile.vue.d.ts +12 -0
- package/dist/modules/groupCall/components/VideoGrid.vue.d.ts +29 -0
- package/dist/modules/groupCall/components/iconRegistry.d.ts +2 -0
- package/dist/modules/groupCall/index.d.ts +7 -0
- package/dist/modules/groupCall/media/RtcMediaBridge.d.ts +26 -0
- package/dist/modules/groupCall/signaling/GroupCallSignalingAdapter.d.ts +29 -0
- package/dist/modules/groupCall/types.d.ts +37 -0
- package/dist/modules/groupCall/viewModel/GroupCallStore.d.ts +345 -0
- package/dist/modules/groupCall/viewModel/useGroupCallViewModel.d.ts +35 -0
- package/dist/services/CallService.d.ts +15 -0
- package/dist/services/RtcAdapter.d.ts +11 -0
- package/dist/services/RtcService.d.ts +187 -0
- package/dist/services/UserProfileService.d.ts +38 -0
- package/dist/store/callTimer.d.ts +33 -0
- package/dist/store/chatClient.d.ts +5335 -0
- package/dist/store/globalCall.d.ts +34 -0
- package/dist/store/index.d.ts +1 -0
- package/dist/store/rtcChannel.d.ts +42 -0
- package/dist/store/types.d.ts +50 -0
- package/dist/types/callstate.types.d.ts +61 -0
- package/dist/types/signal.types.d.ts +187 -0
- package/dist/types.d.ts +103 -0
- package/dist/utils/callUtils.d.ts +15 -0
- package/dist/utils/imSdkAdapter.d.ts +29 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/logger.d.ts +122 -0
- package/dist/utils/loggerDb.d.ts +41 -0
- package/dist/utils/ringtone.d.ts +20 -0
- package/dist/vite-env.d.ts +12 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Easemob
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# @easemob-community/callkit-vue3
|
|
2
|
+
|
|
3
|
+
基于 **Vue 3 + 环信 IM SDK + 声网 RTC SDK** 的音视频通话 UI 组件库。
|
|
4
|
+
|
|
5
|
+
内部封装了 `@easemob-community/callkit-core` 作为信令与状态核心,用户只需放置组件、调用 Composables 即可使用完整的单聊/群聊通话能力。
|
|
6
|
+
|
|
7
|
+
## 前置条件
|
|
8
|
+
|
|
9
|
+
- Vue 3 项目
|
|
10
|
+
- 已安装 **环信 IM SDK**(`easemob-websdk`)并完成登录
|
|
11
|
+
- 已安装 **声网 RTC SDK**(`agora-rtc-sdk-ng`)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add vue easemob-websdk agora-rtc-sdk-ng
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 安装
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @easemob-community/callkit-vue3
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 快速开始
|
|
24
|
+
|
|
25
|
+
### 1. 注册插件
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// main.ts
|
|
29
|
+
import { createApp } from 'vue'
|
|
30
|
+
import EasemobChatCallKit from '@easemob-community/callkit-vue3'
|
|
31
|
+
import App from './App.vue'
|
|
32
|
+
|
|
33
|
+
const app = createApp(App)
|
|
34
|
+
app.use(EasemobChatCallKit)
|
|
35
|
+
app.mount('#app')
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> `@easemob-community/callkit-vue3` 会自动注入 Pinia,无需在用户项目中手动安装/配置。
|
|
39
|
+
|
|
40
|
+
### 2. 在根组件放置 Provider
|
|
41
|
+
|
|
42
|
+
```vue
|
|
43
|
+
<template>
|
|
44
|
+
<EasemobChatCallKitProvider
|
|
45
|
+
:chat-client="chatClient"
|
|
46
|
+
:agora-client="agoraClient"
|
|
47
|
+
:init-config="{ logLevel: LogLevel.INFO }"
|
|
48
|
+
>
|
|
49
|
+
<!-- 你的应用内容 -->
|
|
50
|
+
<router-view />
|
|
51
|
+
|
|
52
|
+
<!-- 通话邀请通知(被叫时自动弹出接听/拒绝弹窗) -->
|
|
53
|
+
<InvitationNotification />
|
|
54
|
+
|
|
55
|
+
<!-- 单人通话组件(自动显示/隐藏) -->
|
|
56
|
+
<EasemobChatSingleCall />
|
|
57
|
+
|
|
58
|
+
<!-- 群组通话组件(自动显示/隐藏) -->
|
|
59
|
+
<EasemobChatMultiCall :group-id="groupId" />
|
|
60
|
+
</EasemobChatCallKitProvider>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<script setup lang="ts">
|
|
64
|
+
import {
|
|
65
|
+
EasemobChatCallKitProvider,
|
|
66
|
+
InvitationNotification,
|
|
67
|
+
EasemobChatSingleCall,
|
|
68
|
+
EasemobChatMultiCall,
|
|
69
|
+
LogLevel,
|
|
70
|
+
} from '@easemob-community/callkit-vue3'
|
|
71
|
+
import AgoraRTC from 'agora-rtc-sdk-ng'
|
|
72
|
+
|
|
73
|
+
// 外部创建的 Agora 客户端实例(推荐方式)
|
|
74
|
+
const agoraClient = AgoraRTC.createClient({ mode: 'rtc', codec: 'vp8' })
|
|
75
|
+
|
|
76
|
+
// 环信 IM SDK 登录后的 Connection 实例
|
|
77
|
+
const chatClient = /* 你的 easemob-websdk Connection */
|
|
78
|
+
const groupId = /* 当前群组 ID */
|
|
79
|
+
</script>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3. 发起通话
|
|
83
|
+
|
|
84
|
+
```vue
|
|
85
|
+
<template>
|
|
86
|
+
<div>
|
|
87
|
+
<input v-model="targetUserId" placeholder="输入用户ID" />
|
|
88
|
+
<button @click="startAudio">语音通话</button>
|
|
89
|
+
<button @click="startVideo">视频通话</button>
|
|
90
|
+
<button @click="endCall">结束通话</button>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
import { ref } from 'vue'
|
|
96
|
+
import { useCallKit } from '@easemob-community/callkit-vue3'
|
|
97
|
+
|
|
98
|
+
const targetUserId = ref('')
|
|
99
|
+
const { call, groupCall, hangup } = useCallKit()
|
|
100
|
+
|
|
101
|
+
const startAudio = async () => {
|
|
102
|
+
await call({ targetId: targetUserId.value, type: 'audio' })
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const startVideo = async () => {
|
|
106
|
+
await call({
|
|
107
|
+
targetId: targetUserId.value,
|
|
108
|
+
type: 'video',
|
|
109
|
+
userInfo: {
|
|
110
|
+
nickname: '张三',
|
|
111
|
+
avatarURL: 'https://example.com/avatar.png'
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const startGroupVideo = async () => {
|
|
117
|
+
await groupCall({
|
|
118
|
+
groupId: 'group001',
|
|
119
|
+
members: ['user1', 'user2'],
|
|
120
|
+
type: 'video',
|
|
121
|
+
groupName: '产品组'
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const endCall = async () => {
|
|
126
|
+
await hangup()
|
|
127
|
+
}
|
|
128
|
+
</script>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 4. 监听通话事件
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { useCallKitEvents } from '@easemob-community/callkit-vue3'
|
|
135
|
+
import { onUnmounted } from 'vue'
|
|
136
|
+
|
|
137
|
+
const { onCallStarted, onCallEnded, onIncomingCall, getCallRecord } = useCallKitEvents()
|
|
138
|
+
|
|
139
|
+
const unbindStarted = onCallStarted((e) => {
|
|
140
|
+
console.log('通话接通', e.callId, 'isCaller:', e.isCaller)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const unbindEnded = onCallEnded((e) => {
|
|
144
|
+
const sec = Math.round(e.duration / 1000)
|
|
145
|
+
console.log('通话结束', e.reason, '时长:', sec, '秒')
|
|
146
|
+
const record = getCallRecord()
|
|
147
|
+
// record 可直接用于插入本地消息或展示通话记录
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const unbindIncoming = onIncomingCall((e) => {
|
|
151
|
+
console.log('收到来电', e.callerUserId)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
onUnmounted(() => {
|
|
155
|
+
unbindStarted()
|
|
156
|
+
unbindEnded()
|
|
157
|
+
unbindIncoming()
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## 核心概念
|
|
162
|
+
|
|
163
|
+
### Provider 是根上下文
|
|
164
|
+
|
|
165
|
+
`EasemobChatCallKitProvider` 负责:
|
|
166
|
+
|
|
167
|
+
- 接收外部 `chatClient` 和 `agoraClient`
|
|
168
|
+
- 初始化 `@easemob-community/callkit-core`
|
|
169
|
+
- 注册用户/群组资料 Provider
|
|
170
|
+
- 自动挂载 IM 消息监听
|
|
171
|
+
|
|
172
|
+
### 组件自动显隐
|
|
173
|
+
|
|
174
|
+
- `EasemobChatSingleCall`:主叫 `INVITING` 或通话中自动显示;被叫响铃由 `InvitationNotification` 接管
|
|
175
|
+
- `EasemobChatMultiCall`:默认初始不显示,收到群聊通话事件后自动显示;也可通过 `autoShow` 控制
|
|
176
|
+
|
|
177
|
+
### 日志级别
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { LogLevel } from '@easemob-community/callkit-vue3'
|
|
181
|
+
|
|
182
|
+
<EasemobChatCallKitProvider
|
|
183
|
+
:chat-client="chatClient"
|
|
184
|
+
:init-config="{ logLevel: LogLevel.INFO, enableIDBLog: true }"
|
|
185
|
+
>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`initConfig.logLevel` 会同时控制 UI 层与 `@easemob-community/callkit-core` 的核心日志输出。
|
|
189
|
+
|
|
190
|
+
| 级别 | 说明 |
|
|
191
|
+
|------|------|
|
|
192
|
+
| `LogLevel.ERROR` | 只输出错误 |
|
|
193
|
+
| `LogLevel.WARN` | 错误 + 警告 |
|
|
194
|
+
| `LogLevel.INFO` | 推荐生产环境 |
|
|
195
|
+
| `LogLevel.DEBUG` | 开发调试 |
|
|
196
|
+
| `LogLevel.VERBOSE` | 完整信令日志 |
|
|
197
|
+
|
|
198
|
+
## 完整 API 参考
|
|
199
|
+
|
|
200
|
+
参见项目根目录 [USAGE.md](../USAGE.md)。
|
|
201
|
+
|
|
202
|
+
## 发布流程
|
|
203
|
+
|
|
204
|
+
参见项目根目录 [RELEASING.md](../RELEASING.md)。
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.callkit-icon[data-v-9e658d5b]{line-height:0}.callkit-icon[data-v-9e658d5b] svg{display:block;width:100%;height:100%}.gcall-tile[data-v-db8105bf]{position:relative;width:100%;height:100%;background:#1a1a1a;border-radius:8px;overflow:hidden;cursor:pointer;transition:transform .2s ease,box-shadow .2s ease}.gcall-tile[data-v-db8105bf]:hover{box-shadow:0 0 0 2px #33b1ff80}.gcall-tile-video[data-v-db8105bf]{width:100%!important;height:100%!important;max-width:100%!important;max-height:100%!important;object-fit:cover!important;display:block}.gcall-tile-video.mirror[data-v-db8105bf]{transform:scaleX(-1)}.gcall-tile-placeholder[data-v-db8105bf]{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:#fff;background:linear-gradient(135deg,#2a2a2a,#1a1a1a)}.gcall-tile-avatar[data-v-db8105bf]{width:80px;height:80px;border-radius:50%;object-fit:cover;border:2px solid rgba(255,255,255,.2);flex-shrink:0}.gcall-tile-avatar-fallback[data-v-db8105bf]{width:80px;height:80px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:500;color:#fff;border:2px solid rgba(255,255,255,.2);flex-shrink:0}.gcall-tile-loading[data-v-db8105bf]{display:flex;flex-direction:column;align-items:center;gap:8px;font-size:12px;color:#fffc}.gcall-spinner[data-v-db8105bf]{width:20px;height:20px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:gcall-spin-db8105bf 1s linear infinite}@keyframes gcall-spin-db8105bf{to{transform:rotate(360deg)}}.gcall-tile-hint[data-v-db8105bf]{font-size:12px;color:#fff9}.gcall-tile-info[data-v-db8105bf]{position:absolute;bottom:8px;left:8px;right:8px;display:flex;align-items:center;gap:6px;color:#fff;font-size:12px;background:#00000080;padding:4px 8px;border-radius:4px;pointer-events:none;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:3}.gcall-tile-name[data-v-db8105bf]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gcall-tile-mic[data-v-db8105bf]{color:#ff4757;flex-shrink:0;display:flex;align-items:center}.gcall-tile-speaking[data-v-db8105bf]{color:#0c7;flex-shrink:0;display:flex;align-items:center}.gcall-tile-hover-icon[data-v-db8105bf]{position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:50%;background:#00000080;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease;pointer-events:none;z-index:2}.gcall-tile:hover .gcall-tile-hover-icon[data-v-db8105bf]{opacity:1}@media (max-width: 480px){.gcall-tile-avatar[data-v-db8105bf],.gcall-tile-avatar-fallback[data-v-db8105bf]{width:56px;height:56px;font-size:20px}.gcall-tile-info[data-v-db8105bf]{font-size:11px;padding:3px 6px}}.gcall-main-layout[data-v-0b9e98dc]{flex:1;display:flex;flex-direction:column;min-height:0;padding:8px;gap:8px}.gcall-main-video[data-v-0b9e98dc]{flex:1;min-height:120px;position:relative;border-radius:8px;overflow:hidden;cursor:pointer}.gcall-main-video-inner[data-v-0b9e98dc]{position:absolute;inset:0;display:flex;overflow:hidden}.gcall-main-video-inner[data-v-0b9e98dc]>.gcall-tile{flex:1;min-height:0;border-radius:0}.gcall-main-video-exit[data-v-0b9e98dc]{position:absolute;top:8px;right:8px;width:32px;height:32px;border-radius:50%;background:#00000080;border:none;color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;opacity:0;transition:opacity .2s ease;z-index:5}.gcall-main-video:hover .gcall-main-video-exit[data-v-0b9e98dc]{opacity:1}.gcall-thumbnails[data-v-0b9e98dc]{height:84px;min-height:84px;max-height:84px;flex-shrink:0;display:flex;align-items:center;gap:8px;position:relative;overflow:hidden}.gcall-thumbnails-scroll[data-v-0b9e98dc]{flex:1;height:100%;overflow-x:auto;overflow-y:hidden;display:flex;align-items:center;gap:8px;scroll-behavior:smooth;scrollbar-width:none}.gcall-thumbnails-scroll[data-v-0b9e98dc]::-webkit-scrollbar{display:none}.gcall-thumbnail[data-v-0b9e98dc]{width:120px;height:72px;flex-shrink:0;border-radius:8px;overflow:hidden;cursor:pointer;transition:box-shadow .2s ease}.gcall-thumbnail.active[data-v-0b9e98dc]{box-shadow:0 0 0 2px #33b1ff}.gcall-thumbnail[data-v-0b9e98dc] .gcall-tile{border-radius:0}.gcall-thumbnail[data-v-0b9e98dc] .gcall-tile-info,.gcall-thumbnail[data-v-0b9e98dc] .gcall-tile-hover-icon{display:none}.gcall-thumbnail[data-v-0b9e98dc] .gcall-tile-avatar,.gcall-thumbnail[data-v-0b9e98dc] .gcall-tile-avatar-fallback{width:36px;height:36px;font-size:14px}.gcall-thumbnail[data-v-0b9e98dc] .gcall-tile-placeholder{gap:4px}.gcall-thumbnail[data-v-0b9e98dc] .gcall-tile-hint{font-size:10px}.gcall-scroll-btn[data-v-0b9e98dc]{width:24px;height:24px;border-radius:50%;background:#00000080;border:none;color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;transition:background .2s ease}.gcall-scroll-btn[data-v-0b9e98dc]:hover{background:#000000b3}@media (max-width: 768px){.gcall-thumbnails[data-v-0b9e98dc]{height:76px;min-height:76px;max-height:76px}.gcall-thumbnail[data-v-0b9e98dc]{width:108px;height:64px}}@media (max-width: 480px){.gcall-thumbnails[data-v-0b9e98dc]{height:68px;min-height:68px;max-height:68px}.gcall-thumbnail[data-v-0b9e98dc]{width:96px;height:56px}}.gcall-grid[data-v-ed398bd6]{flex:1;display:grid;gap:8px;padding:8px;overflow:auto;min-height:0}.gcall-grid-cell[data-v-ed398bd6]{min-width:0;min-height:0;aspect-ratio:16 / 10}@media (max-width: 768px){.gcall-grid[data-v-ed398bd6]{gap:6px;padding:6px}}@media (max-width: 480px){.gcall-grid[data-v-ed398bd6]{gap:4px;padding:4px}}.easemob-chat-member-list-modal[data-v-5f8a1ac8]{position:fixed;top:0;left:0;width:100%;height:100%;background:#0009;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;z-index:2000;animation:fadeIn-5f8a1ac8 .2s ease}@keyframes fadeIn-5f8a1ac8{0%{opacity:0}to{opacity:1}}.member-list-container[data-v-5f8a1ac8]{width:360px;max-height:70vh;background:#1e2224;border-radius:14px;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px #00000080;border:1px solid rgba(255,255,255,.06);animation:slideUp-5f8a1ac8 .25s cubic-bezier(.16,1,.3,1)}@keyframes slideUp-5f8a1ac8{0%{opacity:0;transform:translateY(20px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.member-list-header[data-v-5f8a1ac8]{padding:12px 16px;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.member-list-header h3[data-v-5f8a1ac8]{margin:0;font-size:17px;font-weight:600;color:#f9fafa;letter-spacing:.2px}.close-btn[data-v-5f8a1ac8]{width:32px;height:32px;border-radius:50%;border:none;background:#ffffff14;color:#fff9;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease}.close-btn[data-v-5f8a1ac8]:hover{background:#ffffff26;color:#f9fafa}.close-btn[data-v-5f8a1ac8]:active{transform:scale(.92)}.member-list-content[data-v-5f8a1ac8]{flex:1;overflow-y:auto;padding:0 12px 8px;min-height:200px}.member-list-content[data-v-5f8a1ac8]::-webkit-scrollbar{width:4px}.member-list-content[data-v-5f8a1ac8]::-webkit-scrollbar-track{background:transparent}.member-list-content[data-v-5f8a1ac8]::-webkit-scrollbar-thumb{background:#ffffff1a;border-radius:2px}.member-list-content[data-v-5f8a1ac8]::-webkit-scrollbar-thumb:hover{background:#fff3}.list[data-v-5f8a1ac8]{display:flex;flex-direction:column;gap:4px}.member-item[data-v-5f8a1ac8]{padding:10px 12px;display:flex;align-items:center;gap:12px;cursor:pointer;border-radius:10px;transition:all .15s ease;-webkit-user-select:none;user-select:none}.member-item[data-v-5f8a1ac8]:hover:not(.disabled){background:#ffffff0f}.member-item[data-v-5f8a1ac8]:active:not(.disabled){background:#ffffff1a;transform:scale(.995)}.member-item.disabled[data-v-5f8a1ac8]{cursor:not-allowed;opacity:.4}.member-item.selected[data-v-5f8a1ac8]{background:#0091ff1a}.member-item.selected[data-v-5f8a1ac8]:hover:not(.disabled){background:#0091ff24}.member-item.inviting[data-v-5f8a1ac8]{opacity:.7}.member-avatar-wrap[data-v-5f8a1ac8]{width:40px;height:40px;flex-shrink:0}.member-avatar[data-v-5f8a1ac8]{width:100%;height:100%;border-radius:50%;object-fit:cover;border:2px solid rgba(255,255,255,.08)}.member-avatar-fallback[data-v-5f8a1ac8]{width:100%;height:100%;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:600;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.3);border:2px solid rgba(255,255,255,.08)}.name[data-v-5f8a1ac8]{flex:1;font-size:14px;color:#f9fafa;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status[data-v-5f8a1ac8]{flex-shrink:0;display:flex;align-items:center}.status-text[data-v-5f8a1ac8]{font-size:12px;color:#ffffff80}.status-text.inviting[data-v-5f8a1ac8]{display:flex;align-items:center;gap:6px;color:#0091ff}.mini-spinner[data-v-5f8a1ac8]{display:inline-block;width:12px;height:12px;border:2px solid rgba(0,145,255,.3);border-top-color:#0091ff;border-radius:50%;animation:spin-5f8a1ac8 .8s linear infinite}@keyframes spin-5f8a1ac8{to{transform:rotate(360deg)}}.custom-checkbox[data-v-5f8a1ac8]{width:18px;height:18px;border-radius:50%;border:2px solid rgba(255,255,255,.35);display:flex;align-items:center;justify-content:center;transition:all .2s ease;color:#fff}.custom-checkbox.checked[data-v-5f8a1ac8]{background:#0091ff;border-color:#0091ff}.member-list-footer[data-v-5f8a1ac8]{padding:10px 16px;display:flex;justify-content:flex-end;align-items:center;gap:8px;flex-shrink:0;border-top:1px solid rgba(255,255,255,.06)}.cancel-btn[data-v-5f8a1ac8],.invite-btn[data-v-5f8a1ac8]{height:32px;padding:0 16px;display:inline-flex;align-items:center;justify-content:center;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:all .2s ease;white-space:nowrap}.cancel-btn[data-v-5f8a1ac8]{border:1px solid rgba(255,255,255,.15);background:transparent;color:#ffffffbf}.cancel-btn[data-v-5f8a1ac8]:hover{background:#ffffff1a;color:#f9fafa;border-color:#ffffff40}.cancel-btn[data-v-5f8a1ac8]:active{transform:scale(.96)}.invite-btn[data-v-5f8a1ac8]{background:#0091ff;color:#fff;border:none;font-weight:600}.invite-btn[data-v-5f8a1ac8]:hover:not(:disabled){background:#007ad9;box-shadow:0 4px 12px #0091ff4d}.invite-btn[data-v-5f8a1ac8]:active:not(:disabled){transform:scale(.96)}.invite-btn[data-v-5f8a1ac8]:disabled{background:#ffffff1a;color:#ffffff4d;cursor:not-allowed}.loading-state[data-v-5f8a1ac8],.empty-state[data-v-5f8a1ac8]{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:48px 0;color:#fff6;font-size:14px}.empty-state svg[data-v-5f8a1ac8]{opacity:.5}.spinner[data-v-5f8a1ac8]{width:28px;height:28px;border:3px solid rgba(255,255,255,.08);border-top-color:#ffffff80;border-radius:50%;animation:spin-5f8a1ac8 .8s linear infinite}@media (max-width: 480px){.member-list-container[data-v-5f8a1ac8]{width:90vw;max-height:75vh;border-radius:12px}}.gcall-shell[data-v-aae8b065]{background:#171a1c;color:#f9fafa;display:flex;flex-direction:column;border-radius:12px;overflow:hidden;box-shadow:0 20px 60px #00000080;-webkit-user-select:none;user-select:none}.gcall-shell.is-dragging[data-v-aae8b065]{cursor:grabbing;box-shadow:0 25px 70px #0009}.gcall-header[data-v-aae8b065]{cursor:grab}.gcall-header[data-v-aae8b065]:active,.gcall-shell.is-dragging .gcall-header[data-v-aae8b065]{cursor:grabbing}.gcall-header[data-v-aae8b065]{height:60px;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;padding:0 16px;z-index:10;transition:opacity .3s ease,transform .3s ease}.gcall-header.hidden[data-v-aae8b065]{opacity:0;pointer-events:none;transform:translateY(-10px)}.gcall-header-left[data-v-aae8b065]{display:flex;flex-direction:column;gap:2px}.gcall-title[data-v-aae8b065]{font-size:16px;font-weight:500;line-height:1.3;text-shadow:0 1px 2px rgba(0,0,0,.5)}.gcall-duration[data-v-aae8b065]{font-size:13px;color:#fff9;font-variant-numeric:tabular-nums;text-shadow:0 1px 2px rgba(0,0,0,.5)}.gcall-header-right[data-v-aae8b065]{display:flex;align-items:center;gap:8px}.gcall-header-btn[data-v-aae8b065]{width:36px;height:36px;border-radius:50%;border:none;background:#ffffff26;color:#f9fafa;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .2s ease;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.gcall-header-btn[data-v-aae8b065]:hover{background:#ffffff40}.gcall-content[data-v-aae8b065]{flex:1;display:flex;flex-direction:column;min-height:0;position:relative;cursor:pointer}.gcall-controls[data-v-aae8b065]{height:80px;flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:24px;padding:0 16px;z-index:10;transition:opacity .3s ease,transform .3s ease}.gcall-controls.hidden[data-v-aae8b065]{opacity:0;pointer-events:none;transform:translateY(10px)}.gcall-control-group[data-v-aae8b065]{display:flex;flex-direction:column;align-items:center;gap:6px;width:64px}.gcall-control-btn[data-v-aae8b065]{width:44px;height:44px;border-radius:50%;border:none;background:#fff3;color:#f9fafa;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.gcall-control-btn[data-v-aae8b065]:hover{background:#ffffff4d}.gcall-control-btn[data-v-aae8b065]:active{background:#fffc;color:#171a1c}.gcall-control-btn.active[data-v-aae8b065]{background:#ffffffe6;color:#171a1c}.gcall-control-btn.disabled-state[data-v-aae8b065]{background:#ff4757}.gcall-control-btn.disabled-state[data-v-aae8b065]:hover{background:#ff3838}.gcall-control-btn.hangup[data-v-aae8b065]{background:#ff6680}.gcall-control-btn.hangup[data-v-aae8b065]:hover{background:#ff4d6a}.gcall-control-btn.hangup[data-v-aae8b065]:active{background:#e64d66}.gcall-control-label[data-v-aae8b065]{font-size:11px;color:#f9fafa;text-align:center;text-shadow:0 1px 3px rgba(26,26,26,.3);white-space:nowrap}.gcall-clear-hint[data-v-aae8b065]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:#0009;color:#fffc;padding:8px 16px;border-radius:20px;font-size:13px;pointer-events:none;opacity:0;transition:opacity .3s ease}.gcall-clear-hint.show[data-v-aae8b065]{opacity:1}@media (max-width: 768px){.gcall-shell[data-v-aae8b065]{width:100%;height:100%;max-width:100vw;max-height:100vh;border-radius:0}.gcall-header[data-v-aae8b065]{height:50px;padding:0 12px}.gcall-title[data-v-aae8b065]{font-size:14px}.gcall-duration[data-v-aae8b065]{font-size:12px}.gcall-controls[data-v-aae8b065]{height:72px;gap:16px}.gcall-control-btn[data-v-aae8b065]{width:40px;height:40px}.gcall-control-label[data-v-aae8b065]{font-size:10px}}@media (max-width: 480px){.gcall-controls[data-v-aae8b065]{gap:12px}.gcall-control-group[data-v-aae8b065]{width:56px}.gcall-control-btn[data-v-aae8b065]{width:36px;height:36px}}.call-waiting[data-v-e9008329]{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff;background:#1a1a1a}.caller-info[data-v-e9008329]{display:flex;flex-direction:column;align-items:center;margin-bottom:50px}.caller-avatar[data-v-e9008329]{width:120px;height:120px;border-radius:50%;background:#ffffff1a;margin-bottom:20px;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}.caller-avatar img[data-v-e9008329]{width:100%;height:100%;object-fit:cover;border-radius:50%}.avatar-fallback[data-v-e9008329]{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:48px;font-weight:600;color:#fffc;background:linear-gradient(135deg,#667eea,#764ba2)}.caller-details[data-v-e9008329]{text-align:center}.caller-details h3[data-v-e9008329]{font-size:24px;margin:0 0 10px;font-weight:400}.caller-details p[data-v-e9008329]{font-size:32px;margin:0 0 10px;font-weight:700}.call-type-indicator[data-v-e9008329]{font-size:18px;color:#ffffffb3}.waiting-controls[data-v-e9008329]{display:flex;gap:30px;margin-bottom:40px}.cancel-btn[data-v-e9008329]{width:56px;height:56px;border-radius:50%;border:none;background:#ff4757;color:#fff;cursor:pointer;font-size:16px;transition:all .3s;display:flex;align-items:center;justify-content:center}.cancel-btn[data-v-e9008329]:hover{background:#ff3838;transform:scale(1.05)}.switch-btn[data-v-e9008329]{width:80px;height:80px;border-radius:50%;border:none;background:#007bff;color:#fff;cursor:pointer;font-size:16px;transition:all .3s}.switch-btn[data-v-e9008329]:hover{background:#0056b3;transform:scale(1.05)}.waiting-timer[data-v-e9008329]{font-size:20px;color:#ffffffb3}.call-info-bar[data-v-63fcbc95]{position:absolute;top:24px;left:24px;z-index:10}.call-status[data-v-63fcbc95]{display:flex;align-items:center;gap:8px;padding:10px 16px;background:#0006;border-radius:20px;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.status-dot[data-v-63fcbc95]{width:8px;height:8px;background:#4ade80;border-radius:50%;animation:blink-63fcbc95 2s ease-in-out infinite}@keyframes blink-63fcbc95{0%,to{opacity:1}50%{opacity:.3}}.call-duration[data-v-63fcbc95]{font-size:14px;font-weight:600;color:#fff;font-variant-numeric:tabular-nums}@media (max-width: 768px){.call-info-bar[data-v-63fcbc95]{top:16px;left:16px}}.call-controls[data-v-65c7eb86]{position:absolute;bottom:30px;left:50%;transform:translate(-50%);display:flex;gap:10px;z-index:10;padding:0 16px}.control-btn[data-v-65c7eb86]{display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 10px;min-width:52px;background:#ffffff26;border:1.5px solid rgba(255,255,255,.2);border-radius:10px;color:#fff;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);font-size:11px;font-weight:500}.control-btn[data-v-65c7eb86]:hover{background:#ffffff40;border-color:#ffffff4d;transform:translateY(-2px);box-shadow:0 6px 18px #0003}.control-btn[data-v-65c7eb86]:active{transform:translateY(0)}.control-btn.active[data-v-65c7eb86]{background:#ef4444e6;border-color:#ef4444}.control-btn.active[data-v-65c7eb86]:hover{background:#dc2626e6}.hangup-btn[data-v-65c7eb86]{background:#ef4444e6;border-color:#ef4444;min-width:56px}.hangup-btn[data-v-65c7eb86]:hover{background:#dc2626;border-color:#dc2626;transform:translateY(-2px) scale(1.05)}.btn-icon[data-v-65c7eb86]{width:18px;height:18px;color:currentColor}.btn-label[data-v-65c7eb86]{font-size:11px;font-weight:500;white-space:nowrap}@media (max-width: 768px){.call-controls[data-v-65c7eb86]{bottom:20px;gap:8px;padding:0 12px}.control-btn[data-v-65c7eb86]{padding:6px 8px;min-width:48px;border-radius:8px;gap:3px}.btn-label[data-v-65c7eb86]{font-size:10px}.btn-icon[data-v-65c7eb86]{width:16px;height:16px}.hangup-btn[data-v-65c7eb86]{min-width:52px}}@media (max-width: 480px){.call-controls[data-v-65c7eb86]{gap:6px;padding:0 8px}.control-btn[data-v-65c7eb86]{padding:5px 6px;min-width:44px;border-radius:6px}.hangup-btn[data-v-65c7eb86]{min-width:48px}.btn-label[data-v-65c7eb86]{font-size:9px}.btn-icon[data-v-65c7eb86]{width:14px;height:14px}}.call-stream-container[data-v-e5280444]{width:100%;height:100%;position:relative;background:transparent;display:flex;flex-direction:column;overflow:hidden}.call-stream-container.is-audio[data-v-e5280444],.is-audio .remote-video-container[data-v-e5280444]{background:#1a1a2e}.is-audio .remote-placeholder[data-v-e5280444]{color:#fff}.is-audio .avatar-placeholder[data-v-e5280444]{width:160px;height:160px;background:#2d2d44;border:3px solid rgba(255,255,255,.15);-webkit-backdrop-filter:none;backdrop-filter:none}.is-audio .user-icon[data-v-e5280444]{color:#fffc}.is-audio .remote-name[data-v-e5280444]{color:#fff;font-size:28px}.is-audio .call-status-text[data-v-e5280444]{color:#fff9}.remote-video-container[data-v-e5280444]{position:absolute;top:0;left:0;width:100%;height:100%;background:#1a1a2e;display:flex;align-items:center;justify-content:center}.remote-video[data-v-e5280444]{width:100%;height:100%;position:relative;overflow:hidden}.remote-video video[data-v-e5280444],.remote-video canvas[data-v-e5280444]{position:absolute!important;top:0!important;left:0!important;width:100%!important;height:100%!important;object-fit:cover}.remote-placeholder[data-v-e5280444]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:#1a1a1a;z-index:1}.avatar-placeholder[data-v-e5280444]{width:120px;height:120px;margin:0 auto 24px;background:#ffffff26;border-radius:50%;display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:3px solid rgba(255,255,255,.2)}.user-icon[data-v-e5280444]{width:60px;height:60px;color:#fff9}.remote-name[data-v-e5280444]{font-size:24px;font-weight:600;margin:0 0 8px;color:#fff}.connecting-text[data-v-e5280444]{font-size:14px;color:#ffffffb3;margin:0;animation:pulse-e5280444 2s ease-in-out infinite}.call-status-text[data-v-e5280444]{font-size:14px;color:#ffffffb3;margin:0}@keyframes pulse-e5280444{0%,to{opacity:1}50%{opacity:.5}}.local-video-container[data-v-e5280444]{position:absolute;top:24px;right:24px;width:120px;height:160px;background:transparent;border-radius:12px;overflow:hidden;box-shadow:0 8px 32px #0006;z-index:10}.local-video[data-v-e5280444]{width:100%;height:100%;object-fit:cover}.local-video-disabled[data-v-e5280444]{position:absolute;top:0;left:0;width:100%;height:100%;background:#000c;display:flex;align-items:center;justify-content:center}.camera-off-icon[data-v-e5280444]{width:40px;height:40px;color:#fff9}.remote-placeholder .avatar-placeholder.camera-off[data-v-e5280444]{background:#00000080}.remote-placeholder .avatar-placeholder.camera-off .camera-off-icon[data-v-e5280444]{width:48px;height:48px}.remote-placeholder .call-status-text[data-v-e5280444]{font-size:14px;color:#ffffffb3;margin:0}@media (max-width: 768px){.local-video-container[data-v-e5280444]{width:90px;height:120px;top:16px;right:16px}}@media (max-width: 480px){.local-video-container[data-v-e5280444]{width:75px;height:100px}}.easemob-mini-window[data-v-dca616eb]{position:fixed;transition:box-shadow .3s;-webkit-user-select:none;user-select:none}.easemob-mini-window[data-v-dca616eb]:hover{box-shadow:0 12px 48px #0009}.mini-window-audio[data-v-dca616eb]{width:100%;height:100%;background:linear-gradient(135deg,#667eea,#764ba2);display:flex;align-items:center;padding:12px 16px;gap:12px}.audio-icon[data-v-dca616eb]{width:40px;height:40px;background:#fff3;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.audio-icon svg[data-v-dca616eb]{width:24px;height:24px;color:#fff}.duration-info[data-v-dca616eb]{flex:1;display:flex;flex-direction:column;gap:2px;overflow:hidden}.duration-text[data-v-dca616eb]{font-size:16px;font-weight:600;color:#fff;font-variant-numeric:tabular-nums}.status-text[data-v-dca616eb]{font-size:12px;color:#fffc;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mini-window-video[data-v-dca616eb]{width:100%;height:100%;background:#1a1a2e;position:relative}.mini-video[data-v-dca616eb]{width:100%;height:100%;position:relative;overflow:hidden}.mini-video video[data-v-dca616eb],.mini-video canvas[data-v-dca616eb]{position:absolute!important;top:0!important;left:0!important;width:100%!important;height:100%!important;object-fit:cover}.video-placeholder[data-v-dca616eb]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:60px;height:60px;background:#ffffff1a;border-radius:50%;display:flex;align-items:center;justify-content:center}.user-icon[data-v-dca616eb]{width:32px;height:32px;color:#fff9}.mini-duration[data-v-dca616eb]{position:absolute;bottom:8px;left:8px;right:8px;text-align:center;color:#fff;background:#0009;padding:4px 8px;border-radius:4px;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}.easemob-chat-single-call[data-v-38e0ba9e]{width:360px;height:640px;max-width:90vw;max-height:90vh;background-size:cover;background-position:center;background-repeat:no-repeat;border-radius:16px;z-index:1000;display:flex;flex-direction:column;align-items:stretch;justify-content:center;box-shadow:0 20px 60px #00000080;overflow:hidden;will-change:left,top}.easemob-chat-single-call[data-v-38e0ba9e]:active{cursor:grabbing}.easemob-chat-single-call.is-dragging[data-v-38e0ba9e]{cursor:grabbing;box-shadow:0 25px 70px #0009;transition:none}.call-content[data-v-38e0ba9e]{width:100%;height:100%;position:relative;cursor:default;pointer-events:none}.call-content[data-v-38e0ba9e]>*{pointer-events:auto}.minimize-btn[data-v-38e0ba9e]{position:absolute;top:16px;right:16px;width:36px;height:36px;background:#ffffff26;border:2px solid rgba(255,255,255,.2);border-radius:50%;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);z-index:100}.minimize-btn[data-v-38e0ba9e]:hover{background:#ffffff40;border-color:#ffffff4d;transform:scale(1.05)}.minimize-btn svg[data-v-38e0ba9e]{width:18px;height:18px}.drag-handle[data-v-38e0ba9e]{position:absolute;top:0;left:0;right:0;height:40px;cursor:grab;z-index:50;background:linear-gradient(to bottom,rgba(0,0,0,.2),transparent)}.drag-handle[data-v-38e0ba9e]:active,.easemob-chat-single-call.is-dragging .drag-handle[data-v-38e0ba9e]{cursor:grabbing}.invitation-notification[data-v-dfb799c3]{position:fixed;top:20px;right:20px;z-index:9999;background:linear-gradient(135deg,#667eea,#764ba2);border-radius:12px;box-shadow:0 8px 24px #00000040;padding:16px;min-width:360px;max-width:400px;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.invitation-content[data-v-dfb799c3]{display:flex;align-items:center;gap:12px}.invitation-avatar[data-v-dfb799c3]{position:relative;width:56px;height:56px;flex-shrink:0}.invitation-avatar img[data-v-dfb799c3]{width:100%;height:100%;border-radius:50%;object-fit:cover;border:3px solid rgba(255,255,255,.3)}.avatar-placeholder[data-v-dfb799c3]{width:100%;height:100%;border-radius:50%;background:#fff3;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:600;color:#fff;border:3px solid rgba(255,255,255,.3)}.call-type-badge[data-v-dfb799c3]{position:absolute;bottom:-2px;right:-2px;width:28px;height:28px;background:linear-gradient(135deg,#667eea,#764ba2);border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid white;box-shadow:0 2px 8px #0003}.invitation-info[data-v-dfb799c3]{flex:1;min-width:0;color:#fff}.caller-name[data-v-dfb799c3]{font-size:16px;font-weight:600;line-height:22px;margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.call-description[data-v-dfb799c3]{font-size:14px;opacity:.9;line-height:20px}.invitation-actions[data-v-dfb799c3]{display:flex;gap:12px;flex-shrink:0}.invitation-actions button[data-v-dfb799c3]{width:44px;height:44px;border-radius:50%;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s ease;box-shadow:0 2px 8px #00000026}.invitation-actions button[data-v-dfb799c3]:disabled{opacity:.6;cursor:not-allowed}.reject-btn[data-v-dfb799c3]{background:#ef4444}.reject-btn[data-v-dfb799c3]:hover:not(:disabled){background:#dc2626;transform:scale(1.05)}.accept-btn[data-v-dfb799c3]{background:#10b981}.accept-btn[data-v-dfb799c3]:hover:not(:disabled){background:#059669;transform:scale(1.05)}.slide-down-enter-active[data-v-dfb799c3],.slide-down-leave-active[data-v-dfb799c3]{transition:all .3s cubic-bezier(.4,0,.2,1)}.slide-down-enter-from[data-v-dfb799c3],.slide-down-leave-to[data-v-dfb799c3]{opacity:0;transform:translateY(-20px)}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ProviderConfig } from '../types';
|
|
2
|
+
declare function __VLS_template(): {
|
|
3
|
+
attrs: Partial<{}>;
|
|
4
|
+
slots: {
|
|
5
|
+
default?(_: {}): any;
|
|
6
|
+
};
|
|
7
|
+
refs: {};
|
|
8
|
+
rootEl: HTMLDivElement;
|
|
9
|
+
};
|
|
10
|
+
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
11
|
+
declare const __VLS_component: import('vue').DefineComponent<ProviderConfig, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<ProviderConfig> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
12
|
+
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
13
|
+
export default _default;
|
|
14
|
+
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
15
|
+
new (): {
|
|
16
|
+
$slots: S;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
2
|
+
close: () => any;
|
|
3
|
+
expand: () => any;
|
|
4
|
+
}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{
|
|
5
|
+
onClose?: () => any;
|
|
6
|
+
onExpand?: () => any;
|
|
7
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
|
|
8
|
+
elementRef: HTMLDivElement;
|
|
9
|
+
miniRemoteVideo: HTMLDivElement;
|
|
10
|
+
}, any>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface Member {
|
|
2
|
+
userId: string;
|
|
3
|
+
userName: string;
|
|
4
|
+
avatar?: string;
|
|
5
|
+
}
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
groupId: string;
|
|
8
|
+
members?: Member[];
|
|
9
|
+
existingUserIds: string[];
|
|
10
|
+
invitingUserIds?: string[];
|
|
11
|
+
};
|
|
12
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
13
|
+
invite: (userIds: string[]) => any;
|
|
14
|
+
close: () => any;
|
|
15
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
onInvite?: (userIds: string[]) => any;
|
|
17
|
+
onClose?: () => any;
|
|
18
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
groupId?: string;
|
|
3
|
+
groupName?: string;
|
|
4
|
+
groupAvatar?: string;
|
|
5
|
+
type?: 'audio' | 'video';
|
|
6
|
+
currentUserId?: string;
|
|
7
|
+
autoShow?: boolean;
|
|
8
|
+
groupMembers?: Array<{
|
|
9
|
+
userId: string;
|
|
10
|
+
userName: string;
|
|
11
|
+
avatar?: string;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
15
|
+
error: (error: Error) => any;
|
|
16
|
+
callStarted: () => any;
|
|
17
|
+
callEnded: () => any;
|
|
18
|
+
addParticipant: () => any;
|
|
19
|
+
participantTimeout: (userId: string) => any;
|
|
20
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
21
|
+
onError?: (error: Error) => any;
|
|
22
|
+
onCallStarted?: () => any;
|
|
23
|
+
onCallEnded?: () => any;
|
|
24
|
+
onAddParticipant?: () => any;
|
|
25
|
+
onParticipantTimeout?: (userId: string) => any;
|
|
26
|
+
}>, {
|
|
27
|
+
autoShow: boolean;
|
|
28
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
|
|
29
|
+
groupCallShellRef: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<import('../../modules/groupCall/components/GroupCallShell.vue').GroupCallShellProps> & Readonly<{
|
|
30
|
+
onHangup?: () => any;
|
|
31
|
+
onAddParticipant?: () => any;
|
|
32
|
+
}>, {
|
|
33
|
+
startSession: (payload: {
|
|
34
|
+
sessionId: string;
|
|
35
|
+
callType: "video" | "audio";
|
|
36
|
+
}) => void;
|
|
37
|
+
addRemoteParticipant: (userId: string, nickname: string, avatarUrl?: string) => void;
|
|
38
|
+
markRemoteAccepted: (userId: string) => void;
|
|
39
|
+
bindRtcService: (rtcService: import('../..').RtcService) => void;
|
|
40
|
+
unbindRtcService: () => void;
|
|
41
|
+
sendInvite: (userIds: string[], groupId: string, message: string) => Promise<void>;
|
|
42
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
43
|
+
hangup: () => any;
|
|
44
|
+
addParticipant: () => any;
|
|
45
|
+
}, import('vue').PublicProps, {}, false, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
|
|
46
|
+
shellRef: HTMLDivElement;
|
|
47
|
+
}, HTMLDivElement, import('vue').ComponentProvideOptions, {
|
|
48
|
+
P: {};
|
|
49
|
+
B: {};
|
|
50
|
+
D: {};
|
|
51
|
+
C: {};
|
|
52
|
+
M: {};
|
|
53
|
+
Defaults: {};
|
|
54
|
+
}, Readonly<import('../../modules/groupCall/components/GroupCallShell.vue').GroupCallShellProps> & Readonly<{
|
|
55
|
+
onHangup?: () => any;
|
|
56
|
+
onAddParticipant?: () => any;
|
|
57
|
+
}>, {
|
|
58
|
+
startSession: (payload: {
|
|
59
|
+
sessionId: string;
|
|
60
|
+
callType: "video" | "audio";
|
|
61
|
+
}) => void;
|
|
62
|
+
addRemoteParticipant: (userId: string, nickname: string, avatarUrl?: string) => void;
|
|
63
|
+
markRemoteAccepted: (userId: string) => void;
|
|
64
|
+
bindRtcService: (rtcService: import('../..').RtcService) => void;
|
|
65
|
+
unbindRtcService: () => void;
|
|
66
|
+
sendInvite: (userIds: string[], groupId: string, message: string) => Promise<void>;
|
|
67
|
+
}, {}, {}, {}, {}>;
|
|
68
|
+
}, any>;
|
|
69
|
+
export default _default;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface CallControlsProps {
|
|
2
|
+
isMuted: boolean;
|
|
3
|
+
isVideoEnabled?: boolean;
|
|
4
|
+
showVideo?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const _default: import('vue').DefineComponent<CallControlsProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
7
|
+
toggleVideo: () => any;
|
|
8
|
+
toggleMute: () => any;
|
|
9
|
+
endCall: () => any;
|
|
10
|
+
}, string, import('vue').PublicProps, Readonly<CallControlsProps> & Readonly<{
|
|
11
|
+
onToggleVideo?: () => any;
|
|
12
|
+
onToggleMute?: () => any;
|
|
13
|
+
onEndCall?: () => any;
|
|
14
|
+
}>, {
|
|
15
|
+
showVideo: boolean;
|
|
16
|
+
isVideoEnabled: boolean;
|
|
17
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
interface CallInfoBarProps {
|
|
2
|
+
duration: string;
|
|
3
|
+
}
|
|
4
|
+
declare const _default: import('vue').DefineComponent<CallInfoBarProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<CallInfoBarProps> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
5
|
+
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface CallStreamProps {
|
|
2
|
+
type: 'audio' | 'video';
|
|
3
|
+
}
|
|
4
|
+
declare const _default: import('vue').DefineComponent<CallStreamProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
5
|
+
ended: () => any;
|
|
6
|
+
}, string, import('vue').PublicProps, Readonly<CallStreamProps> & Readonly<{
|
|
7
|
+
onEnded?: () => any;
|
|
8
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
9
|
+
remoteVideo: HTMLDivElement;
|
|
10
|
+
localVideo: HTMLVideoElement;
|
|
11
|
+
}, HTMLDivElement>;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface CallWaitingProps {
|
|
2
|
+
targetUser: string;
|
|
3
|
+
type: 'audio' | 'video';
|
|
4
|
+
}
|
|
5
|
+
declare const _default: import('vue').DefineComponent<CallWaitingProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
6
|
+
cancel: () => any;
|
|
7
|
+
}, string, import('vue').PublicProps, Readonly<CallWaitingProps> & Readonly<{
|
|
8
|
+
onCancel?: () => any;
|
|
9
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
interface SingleCallProps {
|
|
2
|
+
/**
|
|
3
|
+
* 目标用户ID(主叫方传入)。组件内部也会自动从 coreCallState 读取
|
|
4
|
+
*/
|
|
5
|
+
targetUser?: string;
|
|
6
|
+
type?: 'audio' | 'video';
|
|
7
|
+
enableRingtone?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* 自定义背景图 URL
|
|
10
|
+
* 如果不传则使用默认 CDN 背景图
|
|
11
|
+
* 如需离线使用,可传本地路径如:'/callkit-static-assets/images/callkit_bg.png'
|
|
12
|
+
*/
|
|
13
|
+
backgroundImage?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const _default: import('vue').DefineComponent<SingleCallProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
16
|
+
callStarted: () => any;
|
|
17
|
+
callEnded: () => any;
|
|
18
|
+
callCanceled: () => any;
|
|
19
|
+
}, string, import('vue').PublicProps, Readonly<SingleCallProps> & Readonly<{
|
|
20
|
+
onCallStarted?: () => any;
|
|
21
|
+
onCallEnded?: () => any;
|
|
22
|
+
onCallCanceled?: () => any;
|
|
23
|
+
}>, {
|
|
24
|
+
enableRingtone: boolean;
|
|
25
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
|
|
26
|
+
elementRef: HTMLDivElement;
|
|
27
|
+
}, any>;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface UseAnswerCallReturn {
|
|
2
|
+
acceptCall: () => Promise<void>;
|
|
3
|
+
rejectCall: () => Promise<void>;
|
|
4
|
+
busyRejectCall: () => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 被叫方应答通话的组合式API
|
|
8
|
+
* 提供接受、拒绝通话的方法(状态由 callkit-core 维护)
|
|
9
|
+
*/
|
|
10
|
+
export declare function useAnswerCall(): UseAnswerCallReturn;
|