@tencentcloud/sdk-mcp 1.4.0 → 1.4.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 (35) hide show
  1. package/package.json +1 -1
  2. package/resource/doc/callkit/core-sdk/android/answer-call.md +942 -0
  3. package/resource/doc/callkit/core-sdk/android/make-call.md +876 -0
  4. package/resource/doc/callkit/core-sdk/android/offline-push.md +368 -0
  5. package/resource/doc/callkit/core-sdk/flutter/answer-call.md +1132 -0
  6. package/resource/doc/callkit/core-sdk/flutter/make-call.md +1206 -0
  7. package/resource/doc/callkit/core-sdk/flutter/offline-push.md +357 -0
  8. package/resource/doc/callkit/core-sdk/ios/answer-call.md +1009 -0
  9. package/resource/doc/callkit/core-sdk/ios/make-call.md +1154 -0
  10. package/resource/doc/callkit/core-sdk/ios/offline-push.md +470 -0
  11. package/resource/doc/callkit/core-sdk/uni-app/answer-call.md +1146 -0
  12. package/resource/doc/callkit/core-sdk/uni-app/make-call.md +1267 -0
  13. package/resource/doc/callkit/preset/android/integration.md +1 -1
  14. package/resource/doc/callkit/preset/flutter/integration.md +1 -1
  15. package/resource/doc/callkit/preset/ios/integration.md +1 -1
  16. package/resource/doc/callkit/preset/react/api.md +1 -1
  17. package/resource/doc/callkit/preset/vue/api.md +1 -1
  18. package/server.js +1 -1
  19. package/tools/call/get_native_callkit_core_integration.js +67 -0
  20. package/tools/call/get_native_callkit_core_integration.js.map +1 -0
  21. package/tools/common/present_framework_choice.js +1 -1
  22. package/tools/common/present_framework_choice.js.map +1 -1
  23. package/tools/common/record_skill_session.js +58 -0
  24. package/tools/common/record_skill_session.js.map +1 -0
  25. package/tools/definitions/get_native_call_uikit_integration_def.js +9 -3
  26. package/tools/definitions/get_native_call_uikit_integration_def.js.map +1 -1
  27. package/tools/definitions/get_native_callkit_core_integration_def.js +63 -0
  28. package/tools/definitions/get_native_callkit_core_integration_def.js.map +1 -0
  29. package/tools/definitions/record_skill_session_def.js +31 -0
  30. package/tools/definitions/record_skill_session_def.js.map +1 -0
  31. package/tools/index.js +4 -0
  32. package/tools/index.js.map +1 -1
  33. package/utils/report-cls-client.js +10 -1
  34. package/utils/report-cls-client.js.map +1 -1
  35. package/utils/report-es-client.js +1 -1
@@ -0,0 +1,1267 @@
1
+ 本文档将帮助您使用 **AtomicXCore SDK** 的 **DeviceState**、**CallState** 以及核心组件 **CallCoreView**,快速构建一个支持通话功能的 **App**。
2
+
3
+ ## 准备工作
4
+
5
+ ### 步骤1:开通服务
6
+
7
+ 请参见 开通服务,获取体验版或付费版 SDK。
8
+
9
+ ### 步骤2:在当前项目中导入 AtomicXCore
10
+
11
+ 1. **安装组件**:打开 [AtomicXCore SDK](https://ext.dcloud.net.cn/plugin?name=tuikit-atomic-x),单击**下载插件并导入 HBuilderX**,选择需要集成的项目并单击**确定**。也可从 [GitHub 仓库](https://github.com/Tencent-RTC/TUIKit_uni-app) 下载源码,将 `App/uni_modules/tuikit-atomic-x` 目录拷贝至您项目的 `uni_modules/` 下。
12
+
13
+ 2. **配置工程权限:** 请在应用的 manifest.json 文件的 app-plus > distribute 节点下,确保添加了以下必要的权限:
14
+
15
+ Android 平台(`Android`节点):在 `permissions` 数组中,请确保包含以下权限:
16
+
17
+ ```json
18
+ "<uses-permission android:name=\"android.permission.RECORD_AUDIO\" />",
19
+ "<uses-permission android:name=\"android.permission.CAMERA\"/>"
20
+ ```
21
+
22
+ iOS 平台(`iOS` 节点):在 `privacyDescription` 对象中,请确保包含以下描述:
23
+
24
+ ```json
25
+ "NSCameraUsageDescription" : "应用需要访问您的相机以进行通话",
26
+ "NSMicrophoneUsageDescription" : "应用需要访问您的麦克风以进行通话"
27
+ ```
28
+
29
+ 在 `UIBackgroundModes` 数组中,添加 `audio` 以支持后台音频播放。
30
+
31
+ ```json
32
+ "UIBackgroundModes" : [ "audio" ]
33
+ ```
34
+
35
+ ### 步骤3:实现登录逻辑
36
+
37
+ 在您的项目中调用 LoginState 中的 login 方法完成登录,**这是使用 AtomicXCore 所有功能的关键前提**。
38
+
39
+ **重要:**
40
+
41
+ 推荐在您 App 自身的用户账户登录成功后,再调用 LoginState 中的 login 方法,以确保登录业务逻辑的清晰和一致。
42
+
43
+ ```typescript
44
+ import { useLoginState } from "@/uni_modules/tuikit-atomic-x/state/LoginState";
45
+ const { login } = useLoginState();
46
+ const handleLogin = () => {
47
+ login({
48
+ sdkAppID: 1400000001, // 替换为您的 SDKAppID
49
+ userID: "test_001", // 替换为您的 UserID
50
+ userSig: "xxxxxxxxxx" // 联调填临时 UserSig;MCP 已配置 sdkAppID、sdkSecretKey 时可由工具自动生成
51
+ })
52
+ }
53
+ ```
54
+
55
+ **登录接口参数说明:**
56
+
57
+ | **参数** | **类型** | **说明** |
58
+ | --- | --- | --- |
59
+ | `sdkAppID` | number | 从控制台获取,通常是以 140 或 160 开头的 10 位整数 |
60
+ | `userID` | string | 当前用户的唯一 ID,长度不超过32字节,不支持使用特殊字符,建议使用英文字母或数字。为避免多端登录冲突,请勿使用 1、123 等简单 ID。 |
61
+ | `userSig` | string | IM 登录鉴权票据。联调可参考 `genTestUserSig`(见 <https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/debug/GenerateTestUserSig.js>);**Tencent RTC MCP Server** 已配置 `sdkAppID` 与 `sdkSecretKey` 时可由工具自动生成。**生产环境**须由业务服务端使用密钥签发 UserSig,勿在客户端存储或下发 SecretKey。 |
62
+
63
+ ## 拨打第一通电话
64
+
65
+ ### 步骤1:创建通话界面
66
+
67
+ 您需要创建一个通话页面,当发起通话时唤起通话页面,实现方式如下:
68
+
69
+ 1. **创建通话页面**:您可以新建一个页面作为通话宿主页面,用于响应来电时的跳转逻辑。
70
+
71
+ 2. **通话页面绑定 CallCoreView** : 通话视图核心组件,自动监听 **CallState** 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。
72
+
73
+ ```vue
74
+ <call-core-view style="height: 50px; width: 50px;"></call-core-view>
75
+ ```
76
+
77
+ **说明:**
78
+
79
+ **CallCoreView** 为原生组件,不需要在文件中单独进行 **import**。挂载在页面上即可使用。
80
+
81
+ **CallCoreView 视图组件功能说明:**
82
+
83
+ | **功能** | **说明** | **参考文档** |
84
+ | --- | --- | --- |
85
+ | **设置布局模式** | 支持自由切换布局模式。若未设置,将根据通话人数自动适配布局。 | 切换布局模式 |
86
+ | **设置头像** | 支持通过传入头像资源路径,为特定用户自定义头像。 | 自定义默认头像 |
87
+ | **设置音量提示图标** | 支持根据不同音量等级,配置个性化的音量指示图标。 | 自定义音量提示的图标 |
88
+ | **设置网络提示图标** | 支持根据实时网络质量,配置对应的网络状态提示图标。 | 自定义网络提示的图标 |
89
+ | **设置等待接听用户的动画** | 在多人通话场景下,支持传入 GIF 图像路径,为待接听状态的用户展示动画。 | 自定义 loading 动画 |
90
+
91
+ ### 步骤2:添加通话控制按钮
92
+
93
+ 您可以参考 **DeviceState**、**CallState** 提供的 API ,自定义添加您的按钮。
94
+
95
+ **DeviceState 功能说明:**麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。建议将对应方法绑定至按钮点击事件,并通过监听设备状态变更来实时刷新按钮的 UI 状态。
96
+
97
+ **CallState 功能说明:**接听、挂断、拒接等核心通话控制能力。建议将对应方法绑定至按钮点击事件,并监听通话状态的变化,以确保按钮显示与当前通话阶段保持同步。
98
+
99
+ **图标资源下载:**按钮图标可以直接从 GitHub 下载。这些图标由我们的设计师专为 TUICallKit 打造,无版权风险,可放心使用。
100
+
101
+ | **资源文件名** | **用途** | **下载地址** |
102
+ | --- | --- | --- |
103
+ | accept.png | 接听按钮图标 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/accept.png) |
104
+ | hangup.png | 挂断 / 拒绝按钮图标 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/hangup.png) |
105
+ | camera-off.png | 摄像头关闭 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/camera-off.png) |
106
+ | camera-on.png | 摄像头开启 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/camera-on.png) |
107
+ | mic-off.png | 麦克风关闭 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/mic-off.png) |
108
+ | mic-on.png | 麦克风开启 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/mic-on.png) |
109
+ | handsfree-off.png | 扬声器关闭 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/handsfree-off.png) |
110
+ | handsfree-on.png | 扬声器开启 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/uni_modules/tuikit-atomic-x/static/icon/handsfree-on.png) |
111
+
112
+ 以添加挂断、麦克风、摄像头按钮为例,实现方式如下:
113
+
114
+ 1. **添加挂断按钮**:创建并添加挂断按钮,在点击事件中调用 hangup 并销毁界面。
115
+
116
+ ```vue
117
+ <template>
118
+ <view class="btn" @tap="handleHangup">
119
+ <image class="btn-img" :style="[style]" :src="HANGUP_SRC"></image>
120
+ <text class="btn-text" v-if="isShowText">
121
+ 挂断
122
+
123
+ </text>
124
+ </view>
125
+ </template>
126
+
127
+ <script setup lang="ts">
128
+ import { computed } from "vue";
129
+
130
+ import HANGUP_SRC from "../../../static/icon/hangup.png"; // 根据实际情况替换为您存放图标的路径
131
+
132
+ import {
133
+ useCallState
134
+
135
+ } from '@/uni_modules/tuikit-atomic-x/state/CallState';
136
+
137
+ const {
138
+ hangup
139
+
140
+ } = useCallState()
141
+
142
+ const props = defineProps({
143
+ size: {
144
+ type: Number,
145
+ default: 60,
146
+ },
147
+ isShowText: {
148
+ type: Boolean,
149
+ default: true,
150
+ },
151
+ });
152
+
153
+ const style = computed(() => ({
154
+ width: props.size + "px",
155
+ height: props.size + "px",
156
+ }));
157
+
158
+ const handleHangup = () => {
159
+ hangup();
160
+
161
+ // 在点击事件中调用 hangup 接口并销毁页面
162
+
163
+ };
164
+
165
+ </script>
166
+
167
+ <style scoped>
168
+ .btn {
169
+ margin: 10px 20px;
170
+ }
171
+
172
+ .btn-img {
173
+ width: 60px;
174
+
175
+ height: 60px;
176
+
177
+ border-radius: 140px;
178
+ }
179
+
180
+ .btn-text {
181
+ font-size: 12px;
182
+
183
+ color: #d5e0f2;
184
+
185
+ font-weight: 400;
186
+
187
+ text-align: center;
188
+
189
+ margin-top: 10px;
190
+ }
191
+
192
+ </style>
193
+ ```
194
+
195
+ 2. **添加麦克风开关按钮:**创建并添加麦克风开关按钮,在点击事件中调用 openLocalMicrophone 或 closeLocalMicrophone 接口,并监听麦克风状态,实时更新按钮文本。
196
+
197
+ ```vue
198
+ <template>
199
+ <view class="btn" @tap="handleMic">
200
+ <image class="btn-img" :src="microphoneStatus === DeviceStatus.ON ? MIC_ON_SRC : MIC_OFF_SRC"></image>
201
+ <text class="btn-text">
202
+ {{ microphoneStatus === DeviceStatus.ON ? '麦克风已开' : '麦克风已关' }}
203
+ </text>
204
+ </view>
205
+ </template>
206
+
207
+ <script setup lang="ts">
208
+ import MIC_ON_SRC from "../../../static/icon/mic-on.png"; // 根据实际情况替换为您存放图标的路径
209
+
210
+ import MIC_OFF_SRC from "../../../static/icon/mic-off.png"; // 根据实际情况替换为您存放图标的路径
211
+
212
+ import {
213
+ useDeviceState,
214
+ DeviceStatus
215
+
216
+ } from '@/uni_modules/tuikit-atomic-x/state/DeviceState';
217
+
218
+ const {
219
+ microphoneStatus,
220
+ openLocalMicrophone,
221
+ closeLocalMicrophone,
222
+ } = useDeviceState()
223
+
224
+ // 点击事件调用开启或关闭麦克风
225
+
226
+ const handleMic = () => {
227
+ if (microphoneStatus.value === DeviceStatus.ON) {
228
+ closeLocalMicrophone();
229
+
230
+ } else {
231
+ openLocalMicrophone();
232
+ }
233
+
234
+ };
235
+
236
+ </script>
237
+
238
+ <style scoped>
239
+ .btn {
240
+ margin: 10px 20px;
241
+ }
242
+
243
+ .btn-img {
244
+ width: 60px;
245
+
246
+ height: 60px;
247
+
248
+ border-radius: 140px;
249
+ }
250
+
251
+ .btn-text {
252
+ font-size: 12px;
253
+
254
+ color: #d5e0f2;
255
+
256
+ font-weight: 400;
257
+
258
+ text-align: center;
259
+
260
+ margin-top: 10px;
261
+ }
262
+
263
+ </style>
264
+ ```
265
+
266
+ 3. **添加摄像头开关按钮:**创建并添加摄像头开关按钮,在点击事件中调用 openLocalCamera 或 closeLocalCamera 接口,并监听摄像头的状态,实时更新按钮文本。
267
+
268
+ ```vue
269
+ <template>
270
+ <div class="btn" @tap="handleCamera">
271
+ <image class="btn-img" :src="cameraStatus === DeviceStatus.ON ? CAMERA_ON_SRC : CAMERA_OFF_SRC "></image>
272
+ <text class="btn-text">
273
+ {{cameraStatus === DeviceStatus.ON ? '摄像头已开' : '摄像头已关'}}
274
+ </text>
275
+ </div>
276
+ </template>
277
+
278
+ <script setup lang="ts">
279
+ import CAMERA_ON_SRC from "../../../static/icon/camera-on.png"; // 根据实际情况替换为您存放图标的路径
280
+
281
+ import CAMERA_OFF_SRC from "../../../static/icon/camera-off.png"; // 根据实际情况替换为您存放图标的路径
282
+
283
+ import {
284
+ useDeviceState,
285
+ DeviceStatus
286
+
287
+ } from '@/uni_modules/tuikit-atomic-x/state/DeviceState';
288
+
289
+ const {
290
+ cameraStatus,
291
+ openLocalCamera,
292
+ closeLocalCamera,
293
+ isFrontCamera,
294
+ } = useDeviceState()
295
+
296
+ // 摄像头按钮点击事件
297
+
298
+ const handleCamera = () => {
299
+ if (cameraStatus.value === DeviceStatus.ON) {
300
+ closeLocalCamera()
301
+
302
+ } else {
303
+ openLocalCamera({ isFront: isFrontCamera.value })
304
+ }
305
+
306
+ };
307
+
308
+ </script>
309
+
310
+ <style>
311
+ .btn {
312
+ margin: 10px 20px;
313
+ }
314
+
315
+ .btn-img {
316
+ width: 60px;
317
+
318
+ height: 60px;
319
+
320
+ border-radius: 140px;
321
+ }
322
+
323
+ .btn-text {
324
+ font-size: 12px;
325
+
326
+ color: #d5e0f2;
327
+
328
+ font-weight: 400;
329
+
330
+ text-align: center;
331
+
332
+ margin-top: 10px;
333
+ }
334
+
335
+ </style>
336
+ ```
337
+
338
+ ### 步骤3:发起通话
339
+
340
+ 您可以在 calls 调用成功后跳转通话界面,我们建议您根据媒体类型自动开启麦克风或摄像头获得更好的通话体验,实现方式如下:
341
+
342
+ 1. **发起通话:**调用 calls 发起通话。
343
+
344
+ 2. **开启媒体设备:**发起通话成功后开启麦克风,如果是视频通话同时开启摄像头。
345
+
346
+ 3. **唤起通话页面:**发起通话成功,唤起通话页面。
347
+
348
+ ```vue
349
+ <script setup lang="ts">
350
+ import { ref } from 'vue';
351
+ import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';
352
+ import { useDeviceState } from '@/uni_modules/tuikit-atomic-x/state/DeviceState';
353
+ const { calls } = useCallState();
354
+ const { openLocalCamera, openLocalMicrophone } = useDeviceState();
355
+ const calleeUserID = ref('');
356
+ const mediaType = ref(1); // 0: 语音, 1: 视频
357
+ const startCall = () => {
358
+ const userID = calleeUserID.value.trim();
359
+ ```
360
+
361
+ if (!userID) {
362
+ uni.showToast({ icon: 'none', title: '请输入对方用户ID' });
363
+
364
+ return;
365
+ }
366
+
367
+ ```typescript
368
+ // 1. 打开本地设备
369
+ ```
370
+
371
+ if (mediaType.value === 1) {
372
+ openLocalCamera({ isFront: true });
373
+ }
374
+
375
+ openLocalMicrophone();
376
+
377
+ ```typescript
378
+ // 2. 发起通话
379
+ ```
380
+
381
+ calls({
382
+ participantIds: [userID],
383
+ mediaType: mediaType.value,
384
+ success: () => {
385
+ console.log('发起通话成功');
386
+
387
+ uni.navigateTo({
388
+ url: '/uni_modules/tuikit-atomic-x/pages/call?layoutTemplate=Float' // 示例路径,请替换为您在步骤 1 中创建的真实通话界面路径
389
+ });
390
+ },
391
+ fail: (code: number, message: string) => {
392
+ console.error('发起通话失败:', code, message);
393
+
394
+ uni.showToast({ icon: 'none', title: `通话失败: ${message}` });
395
+ }
396
+ });
397
+ };
398
+
399
+ </script>
400
+
401
+ ### 步骤4:结束通话
402
+
403
+ 无论您调用 hangup 挂断还是对方主动结束通话,均会触发 `onCallEnded` 事件。 建议监听该事件,当该事件被触发(即通话结束)时,执行关闭当前界面的操作。实现方式如下:
404
+
405
+ 1. **监听通话结束事件**:监听 `onCallEnded` 事件。
406
+
407
+ 2. **销毁通话页面**:`onCallEnded` 触发后,销毁通话页面。
408
+
409
+ ```typescript
410
+ import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';
411
+ import { useDeviceState } from '@/uni_modules/tuikit-atomic-x/state/DeviceState';
412
+ const { addCallListener } = useCallState();
413
+ const { closeLocalCamera, closeLocalMicrophone } = useDeviceState();
414
+ ```
415
+
416
+ /**
417
+
418
+ * 初始化通话服务(最小示例:仅处理通话结束事件并回退页面)建议在应用初始化时执行
419
+
420
+ */
421
+
422
+ ```typescript
423
+ export function initCallService() {
424
+ addCallListener('onCallEnded', (event: string) => {
425
+ let res: any;
426
+ ```
427
+
428
+ try {
429
+ res = JSON.parse(event);
430
+
431
+ } catch (error) {
432
+ console.error('[CallService] onCallEnded parse error:', error);
433
+
434
+ return;
435
+ }
436
+
437
+ console.log('[CallService] onCallEnded, reason:', res.reason);
438
+
439
+ ```typescript
440
+ // 关闭本地设备
441
+ ```
442
+
443
+ closeLocalCamera();
444
+
445
+ closeLocalMicrophone();
446
+
447
+ ```typescript
448
+ // 页面回退
449
+ const pages = getCurrentPages();
450
+ ```
451
+
452
+ if (pages.length > 1) {
453
+ uni.navigateBack({ delta: 1 });
454
+
455
+ } else {
456
+ uni.redirectTo({ url: '/pages/index/index' }); // 示例路径:请结合您的项目真实路径进行修改
457
+ }
458
+ });
459
+ }
460
+
461
+ **onCallEnded 事件参数详细说明**:
462
+
463
+ | **参数** | **类型** | **说明** |
464
+ | --- | --- | --- |
465
+ | callId | String | 此次通话的唯一标识。 |
466
+ | mediaType | CallMediaType | 通话媒体类型,用于指定发起音频通话还是视频通话。CallMediaType.Video:视频通话。CallMediaType.Audio:语音通话。 |
467
+ | reason | CallEndReason | 通话结束的原因。Unknown:未知原因,无法确定结束原因。Hangup:正常挂断,用户主动挂断通话。Reject:拒绝接听,被叫方拒绝来电。Busy:对方忙线,被叫方正在通话中。Cancel:通话取消,主叫方在对方接听前取消。 |
468
+ | userId | String | 触发结束的用户 ID。 |
469
+
470
+ ### 运行效果
471
+
472
+ 当您完成以上 5 步后,"拨打一通电话"运行效果如下:
473
+
474
+ ## 定制页面
475
+
476
+ **CallCoreView** 提供了完善的 UI 定制能力,支持头像及音量提示等图标的自由替换。**为助力快速集成,您可以直接从** GitHub **下载。这些图标由我们的设计师专为 TUICallKit 打造,无版权风险,可放心使用。**
477
+
478
+ ### 自定义音量提示的图标
479
+
480
+ 您可以通过设置 **CallCoreView** 组件的 `volumeLevelIcons` 属性来设置音量大小等级不同的提示图标。
481
+
482
+ `volumeLevelIcons` **属性设置示例代码**:
483
+
484
+ ```vue
485
+ <template>
486
+ <view class="call-view-container">
487
+ <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }">
488
+ <call-core-view
489
+ :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"
490
+
491
+ :volumeLevelIcons="volumeLevelIcons"
492
+
493
+ ></call-core-view>
494
+
495
+ </view>
496
+ </view>
497
+ </template>
498
+
499
+ <script setup>
500
+ import { ref, onMounted } from 'vue';
501
+
502
+ const systemInfo = ref({});
503
+
504
+ /**
505
+
506
+ * 将 /static/ 相对路径转为原生层可用的本地绝对路径
507
+
508
+ */
509
+
510
+ function toAbsolutePath(relativePath) {
511
+ if (plus && plus.io && plus.io.convertLocalFileSystemURL) {
512
+ return plus.io.convertLocalFileSystemURL(relativePath);
513
+ }
514
+
515
+ return relativePath;
516
+ }
517
+
518
+ /**
519
+
520
+ * 批量转换对象中所有 value 的路径为绝对路径
521
+
522
+ */
523
+
524
+ function convertIconPaths(iconMap) {
525
+ const result = {};
526
+
527
+ Object.keys(iconMap).forEach(key => {
528
+ result[key] = toAbsolutePath(iconMap[key]);
529
+ });
530
+ return result;
531
+ }
532
+
533
+ // 音量等级图标:key 为等级名称,value 为图标本地绝对路径
534
+
535
+ const volumeLevelIcons = ref(convertIconPaths({
536
+ "Mute": "/static/images/callview-self-mute.png", // 可以替换您项目中的实际图标路径
537
+ "Low": "/static/images/callview-network.png" // 可以替换您项目中的实际图标路径
538
+ }));
539
+
540
+ onMounted(() => {
541
+ uni.getSystemInfo({
542
+ success: (res) => {
543
+ systemInfo.value = res;
544
+ }
545
+ });
546
+ });
547
+ </script>
548
+
549
+ <style>
550
+ .call-view-container {
551
+ background: rgba(15, 16, 20, 0.5);
552
+
553
+ overflow: hidden;
554
+ }
555
+
556
+ </style>
557
+ ```
558
+
559
+ **说明:**
560
+
561
+ uni-app 传递给原生层的参数只支持本地路径,因此需要将相对路径转化为本地路径。
562
+
563
+ **属性参数详细说明:**
564
+
565
+ | **参数** | **类型** | **是否必填** | **说明** |
566
+ | --- | --- | --- | --- |
567
+ | icons | \[String: String\] | 是 | 音量等级与图标资源的映射表。结构说明如下:key ( VolumeLevel ) 表示音量等级:Mute:表示麦克风关闭,静音状态。Low:表示音量范围 (0-25\]。Medium:表示音量范围 (25-50\]。High:表示音量范围 (50-75\]。Peak:表示音量范围 (75-100\]。Value ( String ) 表示对应音量等级的图标资源路径。 |
568
+
569
+ **音量提示图标:**
570
+
571
+ | **图标** | **说明** | **下载地址** |
572
+ | --- | --- | --- |
573
+ | 【图标含义】音量提示图标。【推荐用法】您可以将该图标等级设置为 Low 或 Medium,当用户音量大于对应等级时显示。 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/static/images/callview-network.png) |
574
+ | 【图标含义】静音图标。【推荐用法】您可以将该图标等级设置为 Mute,当该用户静音时显示。 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/static/images/callview-self-mute.png) |
575
+
576
+ ### 自定义网络提示的图标
577
+
578
+ 您可以设置 **CallCoreView** 组件的 `networkQualityIcons` 属性设置不同网络状态的提示图标。
579
+
580
+ `networkQualityIcons` **属性设置示例代码:**
581
+ <template>
582
+ <view class="call-view-container">
583
+ <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }">
584
+ <call-core-view
585
+ :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"
586
+
587
+ :networkQualityIcons="networkQualityIcons"
588
+
589
+ ></call-core-view>
590
+
591
+ </view>
592
+ </view>
593
+ </template>
594
+
595
+ ```vue
596
+ <script setup>
597
+ import { ref, onMounted } from 'vue';
598
+ const systemInfo = ref({});
599
+ ```
600
+
601
+ /**
602
+
603
+ * 将 /static/ 相对路径转为原生层可用的本地绝对路径
604
+
605
+ */
606
+
607
+ ```typescript
608
+ function toAbsolutePath(relativePath) {
609
+ if (plus && plus.io && plus.io.convertLocalFileSystemURL) {
610
+ return plus.io.convertLocalFileSystemURL(relativePath);
611
+ }
612
+ ```
613
+
614
+ return relativePath;
615
+ }
616
+
617
+ /**
618
+
619
+ * 批量转换对象中所有 value 的路径为绝对路径
620
+
621
+ */
622
+
623
+ ```typescript
624
+ function convertIconPaths(iconMap) {
625
+ const result = {};
626
+ ```
627
+
628
+ Object.keys(iconMap).forEach(key => {
629
+ result[key] = toAbsolutePath(iconMap[key]);
630
+ });
631
+ return result;
632
+ }
633
+
634
+ ```typescript
635
+ const networkQualityIcons = ref(convertIconPaths({
636
+ "BAD": "/static/images/callview-network-bad.png", // 可以替换您项目中的实际图标路径
637
+ "VERY_BAD": "/static/images/callview-network-bad.png" // 可以替换您项目中的实际图标路径
638
+ }));
639
+ ```
640
+
641
+ onMounted(() => {
642
+ uni.getSystemInfo({
643
+ success: (res) => {
644
+ systemInfo.value = res;
645
+ }
646
+ });
647
+ });
648
+ </script>
649
+
650
+ ```vue
651
+ <style>
652
+ .call-view-container {
653
+ background: rgba(15, 16, 20, 0.5);
654
+ ```
655
+
656
+ overflow: hidden;
657
+ }
658
+
659
+ </style>
660
+ `networkQualityIcons` **接口参数详细说明:**
661
+
662
+ | **参数** | **类型** | **是否必填** | **说明** |
663
+ | --- | --- | --- | --- |
664
+ | icons | \[String: String\] | 是 | 网络质量与图标资源的映射表。结构说明如下:Key ( String):表示网络质量等级:UNKNOWN:未知网络状态。EXCELLENT:网络状态极佳。GOOD:网络状态较好。POOR:网络状态较差。BAD:网络状态差。VERY\_BAD:网络状态极差。DOWN:网络断开。Value ( String ):对应网络状态的图标资源路径。 |
665
+
666
+ **网络较差的提示图标:**
667
+
668
+ | **图标** | **说明** | **下载地址** |
669
+ | --- | --- | --- |
670
+ | 【图标含义】网络较差的提示图标。【推荐用法】您可以将该图标等级设置为 BAD、VERY\_BAD 或 DOWN,当网络较差时显示该图标。 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/static/images/callview-network-bad.png) |
671
+
672
+ ### 自定义默认头像
673
+
674
+ 您可以设置 **CallCoreView** 的 `participantAvatars` 属性设置用户头像。建议您监听响应式数据 allParticipants(所有参与通话的成员):当获取到用户头像时设置并展示;若用户未设置头像或加载失败,则显示默认头像(占位图)。
675
+
676
+ `participantAvatars` **属性设置示例代码:**
677
+ <template>
678
+ <view class="call-view-container">
679
+ <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }">
680
+ <call-core-view
681
+ :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"
682
+
683
+ :participantAvatars="participantAvatars"
684
+
685
+ ></call-core-view>
686
+
687
+ </view>
688
+ </view>
689
+ </template>
690
+
691
+ ```vue
692
+ <script setup>
693
+ import { ref, watch, onMounted } from 'vue';
694
+ import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';
695
+ const { allParticipants } = useCallState();
696
+ const systemInfo = ref({});
697
+ const participantAvatars = ref({});
698
+ const avatarCache = {};
699
+ const DEFAULT_AVATAR_URL = "https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar1.png";
700
+ ```
701
+
702
+ let defaultAvatarLocalPath = "";
703
+
704
+ let defaultAvatarReady = false;
705
+
706
+ /**
707
+
708
+ * 将相对路径转为原生层可用的本地绝对路径
709
+
710
+ */
711
+
712
+ ```typescript
713
+ function toAbsolutePath(relativePath) {
714
+ if (plus && plus.io && plus.io.convertLocalFileSystemURL) {
715
+ return plus.io.convertLocalFileSystemURL(relativePath);
716
+ }
717
+ ```
718
+
719
+ return relativePath;
720
+ }
721
+
722
+ /**
723
+
724
+ * 下载头像并缓存,返回本地绝对路径
725
+
726
+ */
727
+
728
+ ```typescript
729
+ function downloadAvatar(url) {
730
+ if (avatarCache[url]) {
731
+ return Promise.resolve(avatarCache[url]);
732
+ }
733
+ ```
734
+
735
+ return new Promise((resolve) => {
736
+ uni.downloadFile({
737
+ url,
738
+ success: (res) => {
739
+ if (res.statusCode === 200) {
740
+ const absolutePath = toAbsolutePath(res.tempFilePath);
741
+
742
+ avatarCache[url] = absolutePath;
743
+
744
+ resolve(absolutePath);
745
+
746
+ } else {
747
+ resolve(defaultAvatarLocalPath || "");
748
+ }
749
+
750
+ },
751
+ fail: () => {
752
+ resolve(defaultAvatarLocalPath || "");
753
+ }
754
+ });
755
+ });
756
+ }
757
+
758
+ ```typescript
759
+ // 预下载默认头像
760
+ const defaultAvatarPromise = downloadAvatar(DEFAULT_AVATAR_URL).then(localPath => {
761
+ defaultAvatarLocalPath = localPath;
762
+ ```
763
+
764
+ defaultAvatarReady = true;
765
+
766
+ ```typescript
767
+ // 默认头像就绪后,补刷一次参与者头像
768
+ ```
769
+
770
+ if (allParticipants.value && allParticipants.value.length > 0) {
771
+ updateParticipantAvatars(allParticipants.value);
772
+ }
773
+ });
774
+
775
+ /**
776
+
777
+ * 根据参与者列表,下载头像并构建 { userId: localPath } 映射
778
+
779
+ */
780
+
781
+ async function updateParticipantAvatars(participants) {
782
+ if (!participants || participants.length === 0) {
783
+ participantAvatars.value = {};
784
+
785
+ return;
786
+ }
787
+
788
+ if (!defaultAvatarReady && defaultAvatarPromise) {
789
+ await defaultAvatarPromise;
790
+ }
791
+
792
+ ```typescript
793
+ const avatarMap = {};
794
+ const tasks = [];
795
+ ```
796
+
797
+ participants.forEach(participant => {
798
+ if (participant.id && participant.avatarURL) {
799
+ tasks.push(
800
+ downloadAvatar(participant.avatarURL).then(localPath => {
801
+ avatarMap[participant.id] = localPath;
802
+
803
+ ```typescript
804
+ })
805
+ ```
806
+
807
+ );
808
+
809
+ } else if (participant.id) {
810
+ avatarMap[participant.id] = defaultAvatarLocalPath || "";
811
+ }
812
+ });
813
+ await Promise.all(tasks);
814
+
815
+ participantAvatars.value = avatarMap;
816
+ }
817
+
818
+ ```typescript
819
+ // 监听参与者变化,更新头像映射
820
+ ```
821
+
822
+ watch(() => allParticipants.value, async (newVal) => {
823
+ if (!newVal) return;
824
+
825
+ await updateParticipantAvatars(newVal);
826
+
827
+ }, { immediate: true, deep: true });
828
+
829
+ onMounted(() => {
830
+ uni.getSystemInfo({
831
+ success: (res) => {
832
+ systemInfo.value = res;
833
+ }
834
+ });
835
+ });
836
+ </script>
837
+
838
+ ```vue
839
+ <style>
840
+ .call-view-container {
841
+ background: rgba(15, 16, 20, 0.5);
842
+ ```
843
+
844
+ overflow: hidden;
845
+ }
846
+
847
+ </style>
848
+ `participantAvatars` **属性参数详细说明:**
849
+
850
+ | **参数** | **类型** | **是否必填** | **说明** |
851
+ | --- | --- | --- | --- |
852
+ | avatars | \[String: String\] | 是 | 用户头像映射表。字典结构说明如下:Key:用户的 userID。Value:该用户的头像资源绝对路径。 |
853
+
854
+ **默认头像资源:**
855
+
856
+ | **图标** | **说明** | **下载地址** |
857
+ | --- | --- | --- |
858
+ | 【图标含义】默认头像。【推荐用法】当用户头像加载失败或无头像时,您可以给该用户设置此默认头像。 | [下载地址](https://github.com/Tencent-RTC/TUIKit_iOS/tree/main/atomic_x/Resources/AtomicX.xcassets/icon/call/default_participant_icon.imageset) |
859
+
860
+ ### 自定义 loading 动画
861
+
862
+ 您可以设置 **CallCoreView** 的 `waitingAnimation` 属性,为等待中用户设置等待动画获得更好的体验。
863
+
864
+ `waitingAnimation` **属性设置示例代码:**
865
+ <template>
866
+ <view class="call-view-container">
867
+ <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }">
868
+ <call-core-view
869
+ :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"
870
+
871
+ :waitingAnimation="waitingAnimation"
872
+
873
+ ></call-core-view>
874
+
875
+ </view>
876
+ </view>
877
+ </template>
878
+
879
+ ```vue
880
+ <script setup>
881
+ import { ref, onMounted } from 'vue';
882
+ const systemInfo = ref({});
883
+ ```
884
+
885
+ /**
886
+
887
+ * 将 /static/ 相对路径转为原生层可用的本地绝对路径
888
+
889
+ */
890
+
891
+ ```typescript
892
+ function toAbsolutePath(relativePath) {
893
+ if (plus && plus.io && plus.io.convertLocalFileSystemURL) {
894
+ return plus.io.convertLocalFileSystemURL(relativePath);
895
+ }
896
+ ```
897
+
898
+ return relativePath;
899
+ }
900
+
901
+ ```typescript
902
+ // 等待动画 GIF:传入本地绝对路径供原生组件渲染
903
+ const waitingAnimation = ref(toAbsolutePath("/static/images/callview-loading.gif")); // 可以替换您项目中的实际图标路径
904
+ ```
905
+
906
+ onMounted(() => {
907
+ uni.getSystemInfo({
908
+ success: (res) => {
909
+ systemInfo.value = res;
910
+ }
911
+ });
912
+ });
913
+ </script>
914
+
915
+ ```vue
916
+ <style>
917
+ .call-view-container {
918
+ background: rgba(15, 16, 20, 0.5);
919
+ ```
920
+
921
+ overflow: hidden;
922
+ }
923
+
924
+ </style>
925
+ `waitingAnimation` **接口参数详细说明:**
926
+
927
+ | **参数** | **类型** | **是否必填** | **说明** |
928
+ | --- | --- | --- | --- |
929
+ | path | String | 是 | GIF 格式图像资源的绝对路径。 |
930
+
931
+ **等待接听的动画:**
932
+
933
+ | **图标** | **说明** | **下载地址** |
934
+ | --- | --- | --- |
935
+ | 【图标含义】用户等待接听动画。【推荐用法】群组通话时设置的动画。设置后,当用户的状态为等待接听时,显示该动画。 | [下载地址](https://github.com/Tencent-RTC/TUIKit_uni-app/blob/main/App/static/images/callview-loading.gif) |
936
+
937
+ ### 添加通话计时提示
938
+
939
+ 通话计时可通过响应式数据 activeCall 的 `duration` 字段实时获得,实时显示通话计时的实现方式如下:
940
+
941
+ 1. **绑定通话计时数据**:将 `activeCall.duration` 字段绑定至 UI 。该字段为响应式数据,会自动驱动 UI 实时刷新,无需手动维护定时器。
942
+
943
+ ```vue
944
+ <template>
945
+ <view v-if="isConnected" class="call-timer">
946
+ <text class="timer-text">{{ formattedTime }}</text>
947
+ </view>
948
+ </template>
949
+
950
+ <script setup lang="ts">
951
+ import { computed } from 'vue';
952
+
953
+ import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';
954
+
955
+ const { selfInfo, activeCall } = useCallState();
956
+
957
+ const isConnected = computed(() => {
958
+ return selfInfo.value?.status === 2;
959
+ });
960
+
961
+ const formattedTime = computed(() => {
962
+ const duration = activeCall.value?.duration ?? 0;
963
+
964
+ const hours = Math.floor(duration / 3600);
965
+
966
+ const minutes = Math.floor((duration % 3600) / 60);
967
+
968
+ const seconds = duration % 60;
969
+
970
+ const pad = (n: number) => n.toString().padStart(2, '0');
971
+
972
+ if (hours > 0) {
973
+ return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
974
+ }
975
+
976
+ return `${pad(minutes)}:${pad(seconds)}`;
977
+ });
978
+ </script>
979
+
980
+ <style scoped>
981
+ .call-timer {
982
+ display: flex;
983
+
984
+ align-items: center;
985
+
986
+ justify-content: center;
987
+ }
988
+
989
+ .timer-text {
990
+ font-size: 28rpx;
991
+
992
+ color: #FFFFFF;
993
+
994
+ font-weight: 400;
995
+ }
996
+
997
+ </style>
998
+ ```
999
+
1000
+ ## 更多功能
1001
+
1002
+ ### 自定义用户头像/昵称
1003
+
1004
+ 通话开始前,您可以通过 setSelfInfo 方法,设置自己的昵称和头像。
1005
+
1006
+ **setSelfInfo 示例代码**:
1007
+ import { useLoginState } from "@/uni_modules/tuikit-atomic-x/state/LoginState";
1008
+
1009
+ ```typescript
1010
+ const { setSelfInfo } = useLoginState();
1011
+ ```
1012
+
1013
+ /**
1014
+
1015
+ * 设置当前登录用户的个人信息(昵称、头像等)
1016
+
1017
+ */
1018
+
1019
+ ```typescript
1020
+ export const setSelfUserInfo = (userID: string, nickname: string, avatarURL: string): void => {
1021
+ setSelfInfo({
1022
+ userProfile: {
1023
+ userID,
1024
+ nickname,
1025
+ avatarURL,
1026
+ },
1027
+ success: () => {
1028
+ console.log('用户信息设置成功');
1029
+ ```
1030
+
1031
+ },
1032
+ fail: (errCode: number, errMsg: string) => {
1033
+ console.error('用户信息设置失败:', errCode, errMsg);
1034
+
1035
+ },
1036
+ });
1037
+ };
1038
+
1039
+ **setSelfInfo 接口参数详细说明:**
1040
+
1041
+ | **参数** | **类型** | **是否必填** | **说明** |
1042
+ | --- | --- | --- | --- |
1043
+ | userProfile | UserProfile | 是 | 用户信息结构体:userID:用户的 IDavatarURL:用户头像的 URLnickname:用户的昵称更多字段详情可参考 UserProfile。 |
1044
+
1045
+ ### 切换布局模式
1046
+
1047
+ **CallCoreView** 内置三种布局模式,您可以设置 `layoutTemplate` 属性来设置布局模式。若未主动配置,**CallCoreView** 将根据通话人数自动适配:1 V 1 场景下默认采用 `Float` 模式,多人通话场景下则自动切换为 `Grid` 模式。不同布局模式的说明如下:
1048
+
1049
+ | **模式** | **布局逻辑** | **交互特性** |
1050
+ | --- | --- | --- |
1051
+ | **Float 模式** | 呼叫等待时全屏显示己方画面;接通后全屏显示对方画面,己方画面以悬浮小窗展示。 | 支持小窗拖拽移动,点击小窗可实现大小画面互换。 |
1052
+ | **Grid 模式** | 所有成员画面呈网格状平铺排列成宫格模式布局,适用 2 人以上通话,支持点击放大画面功能。 | 支持点击特定成员画面放大查看。 |
1053
+ | **PIP 模式** | 1 v 1 场景固定显示对方画面,多人场景:采用当前发言者(Active Speaker) 策略,自动识别并全屏展示正在说话的用户。 | 等待时显示自己的画面,接通后还会显示通话计时。 |
1054
+
1055
+ `layoutTemplate` **属性设置示例代码:**
1056
+ <template>
1057
+ <view class="call-view-container">
1058
+ <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }">
1059
+ <call-core-view
1060
+ :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"
1061
+
1062
+ :layoutTemplate="layoutTemplate"
1063
+
1064
+ ></call-core-view>
1065
+
1066
+ </view>
1067
+ </view>
1068
+ </template>
1069
+
1070
+ ```vue
1071
+ <script setup>
1072
+ import { ref, watch, onMounted } from 'vue';
1073
+ import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';
1074
+ const { activeCall } = useCallState();
1075
+ const systemInfo = ref({});
1076
+ // 布局模板:'Float' 单人浮窗布局,'Grid' 多人宫格布局
1077
+ const layoutTemplate = ref("Grid");
1078
+ // 根据通话信息自动切换布局:多人或群聊用 Grid,单人用 Float
1079
+ ```
1080
+
1081
+ watch(() => activeCall.value, (newValue) => {
1082
+ if (!newValue || !newValue.inviteeIds) {
1083
+ return;
1084
+ }
1085
+
1086
+ ```typescript
1087
+ const newTemplate = (newValue.inviteeIds.length > 1 || newValue.chatGroupId !== '') ? 'Grid' : 'Float';
1088
+ ```
1089
+
1090
+ if (layoutTemplate.value !== newTemplate) {
1091
+ layoutTemplate.value = newTemplate;
1092
+ }
1093
+
1094
+ }, { immediate: true, deep: true });
1095
+
1096
+ onMounted(() => {
1097
+ uni.getSystemInfo({
1098
+ success: (res) => {
1099
+ systemInfo.value = res;
1100
+ }
1101
+ });
1102
+ });
1103
+ </script>
1104
+
1105
+ ```vue
1106
+ <style>
1107
+ .call-view-container {
1108
+ background: rgba(15, 16, 20, 0.5);
1109
+ ```
1110
+
1111
+ overflow: hidden;
1112
+ }
1113
+
1114
+ </style>
1115
+ `layoutTemplate` **属性参数详细说明:**
1116
+
1117
+ | **参数** | **类型** | **说明** |
1118
+ | --- | --- | --- |
1119
+ | layoutTemplate | String | CallCoreView 的布局模式Float:布局逻辑:呼叫等待时全屏显示己方画面;接通后全屏显示对方画面,己方画面以悬浮小窗展示。交互特性:支持小窗拖拽移动,点击小窗可实现大小画面互换。Grid:布局逻辑:所有成员画面呈网格状平铺排列成宫格模式布局,适用 2 人以上通话,支持点击放大画面功能。交互特性:支持点击特定成员画面放大查看。PIP:布局逻辑:1v1 场景固定显示对方画面,多人场景:采用当前发言者(Active Speaker) 策略,自动识别并全屏展示正在说话的用户。交互特性:等待时显示自己的画面,接通后还会显示通话计时。 |
1120
+
1121
+ ### 设置通话的默认超时时间
1122
+
1123
+ 您可以在发起通话 calls 时,通过配置参数 CallParams 中的 `timeout` 字段来指定等待超时时间。示例代码如下:
1124
+ <script setup>
1125
+ import { ref } from 'vue';
1126
+
1127
+ ```typescript
1128
+ import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';
1129
+ const { calls } = useCallState();
1130
+ const calleeUserID = ref('');
1131
+ const mediaType = ref(1); // 0: 语音, 1: 视频
1132
+ const startCall = () => {
1133
+ const userID = calleeUserID.value.trim();
1134
+ ```
1135
+
1136
+ if (!userID) {
1137
+ uni.showToast({ icon: 'none', title: '请输入对方用户ID' });
1138
+
1139
+ return;
1140
+ }
1141
+
1142
+ calls({
1143
+ participantIds: [userID],
1144
+ mediaType: mediaType.value,
1145
+ params: {
1146
+ timeout: 30
1147
+
1148
+ },
1149
+ success: () => {
1150
+ console.log('发起通话成功');
1151
+
1152
+ uni.navigateTo({
1153
+ url: '/uni_modules/tuikit-atomic-x/pages/call?layoutTemplate=Float' // 示例路径,请替换为您在步骤 1 中创建的真实通话界面路径
1154
+ });
1155
+ },
1156
+ fail: (code, message) => {
1157
+ console.error('发起通话失败:', code, message);
1158
+
1159
+ uni.showToast({ icon: 'none', title: `通话失败: ${message}` });
1160
+ }
1161
+ });
1162
+ };
1163
+
1164
+ </script>
1165
+
1166
+ | **参数** | **类型** | **是否必填** | **说明** |
1167
+ | --- | --- | --- | --- |
1168
+ | participantIds | Array | 是 | 目标用户的 userId 列表。 |
1169
+ | mediaType | CallMediaType | 是 | 通话媒体类型,用于指定发起音频通话还是视频通话。CallMediaType.Video:视频通话。CallMediaType.Audio:语音通话。 |
1170
+ | params | CallParams | 否 | 通话扩展参数,如:房间号、通话邀请超时时间等。roomId (String):房间 ID,可选参数,未指定时由服务端自动分配。timeout (Int):呼叫超时时间(秒)。userData (String):用户自定义数据。chatGroupId (String):Chat 群组 ID,用于群组通话场景。isEphemeralCall (Boolean):是否为加密通话(不产生通话记录)。 |
1171
+
1172
+ ### 播放等待接听的提示音
1173
+
1174
+ 您可以监听自己的通话状态,在等待接听时播放铃声,在接听通话或通话结束时停止播放铃声。
1175
+
1176
+ ```typescript
1177
+ import { watch } from 'vue';
1178
+ import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';
1179
+ const { selfInfo } = useCallState();
1180
+ function ensureAudioContext() {
1181
+ if (!uni.$innerAudioContext) {
1182
+ const ctx = uni.createInnerAudioContext();
1183
+ ```
1184
+
1185
+ ctx.onError((err: any) => {
1186
+ console.error('[CallService] innerAudioContext onError:', err?.errCode, err?.errMsg || err);
1187
+ });
1188
+ uni.$innerAudioContext = ctx;
1189
+ }
1190
+
1191
+ return uni.$innerAudioContext;
1192
+ }
1193
+
1194
+ ```typescript
1195
+ function playRingtone(src: string) {
1196
+ const ctx = ensureAudioContext();
1197
+ try { ctx.stop(); } catch (_e) {}
1198
+ ```
1199
+
1200
+ ctx.src = src;
1201
+
1202
+ ctx.loop = true;
1203
+
1204
+ ctx.autoplay = true;
1205
+
1206
+ setTimeout(() => {
1207
+ try { ctx.play(); } catch (e) {}
1208
+
1209
+ }, 50);
1210
+ }
1211
+
1212
+ ```typescript
1213
+ function stopRingtone() {
1214
+ if (uni.$innerAudioContext) {
1215
+ try {
1216
+ uni.$innerAudioContext.loop = false;
1217
+ ```
1218
+
1219
+ uni.$innerAudioContext.stop();
1220
+ } catch (e) {}
1221
+ }
1222
+ }
1223
+
1224
+ ```typescript
1225
+ export { playRingtone, stopRingtone };
1226
+ // 建议在应用初始化的时候执行该函数
1227
+ export function initCallService() {
1228
+ ensureAudioContext();
1229
+ // 通话接通(status=2)或结束(status=0)时,停止铃声
1230
+ ```
1231
+
1232
+ watch(() => selfInfo.value, (newVal) => {
1233
+ if (newVal?.status === 2 || newVal?.status === 0) {
1234
+ stopRingtone();
1235
+ }
1236
+
1237
+ }, { immediate: true, deep: true });
1238
+ }
1239
+
1240
+ ## 常见问题
1241
+
1242
+ ### 如何保证通话过程中屏幕常亮?
1243
+
1244
+ uni-app 官方提供了对应的设置接口 uni.setScreenBrightness
1245
+
1246
+ ### 如何禁止系统侧滑返回上一个页面的行为?
1247
+
1248
+ iOS 端可以通过设置页面上的 disableSwipeBack 属性进行禁止。详情参考 uni-app 官方文档。
1249
+
1250
+ Android 端可以通过 onBackPress 拦截系统侧滑和物理返回,示例代码如下:
1251
+
1252
+ **注意:**
1253
+
1254
+ 使用该方式进行系统侧滑和物理返回的拦截需要确保该页面使用的是 自定义导航栏。
1255
+
1256
+ ```vue
1257
+ <script setup>
1258
+ import { onBackPress } from '@dcloudio/uni-app'
1259
+ // 拦截系统侧滑和物理返回键,防止通话中误退出
1260
+ ```
1261
+
1262
+ onBackPress((option) => {
1263
+ if (option.from === 'backbutton') {
1264
+ return true; // 返回 true 阻止默认回退行为
1265
+ }
1266
+ });
1267
+ </script>