注意:推荐使用更高效的 AI 集成助手
注意:我们为你提供了全新的 AI 集成方式,只需要描述你的需求,即可自动生成集成代码,大幅提升开发效率。
注意:点击这里,立即体验 AI 集成
二、步骤1:开通服务
请参见 开通服务,获取体验版或付费版 SDK。
三、步骤2:在当前项目中导入 AtomicXCore
1. 安装组件:打开 AtomicXCore SDK,单击下载插件并导入 HBuilderX,选择需要集成的项目并单击确定。
也可从 GitHub 下载源码,将 App/uni_modules/tuikit-atomic-x 目录拷贝至你项目的 uni_modules/ 下。
2. 配置工程权限: 请在应用的 manifest.json 文件的 app-plus > distribute 节点下,确保添加了以下必要的权限:
·Android 平台(Android 节点):在 permissions 数组中,请确保包含以下权限:
"<uses-permission android:name=\\"android.permission.RECORD_AUDIO\\" />","<uses-permission android:name=\\"android.permission.CAMERA\\"/>"
·iOS 平台(iOS 节点):在 privacyDescription 对象中,请确保包含以下描述:
"NSCameraUsageDescription" : "应用需要访问你的相机以进行通话","NSMicrophoneUsageDescription" : "应用需要访问你的麦克风以进行通话"
在 UIBackgroundModes 数组中,添加 audio 以支持后台音频播放。
"UIBackgroundModes" : [ "audio" ]
四、步骤3:实现登录逻辑
在你的项目中调用 LoginState 中的 login 方法完成登录,这是使用 AtomicXCore 所有功能的关键前提。
注意:重要:
注意:推荐在你 App 自身的用户账户登录成功后,再调用 LoginState 中的 login 方法,以确保登录业务逻辑的清晰和一致。
import { useLoginState } from "@/uni_modules/tuikit-atomic-x/state/LoginState"; const { login } = useLoginState(); const handleLogin = () => { login({ sdkAppID: 1400000001, // 替换为你的 SDKAppID userID: "test_001", // 替换为你的 UserID userSig: "xxxxxxxxxx" // 替换为你的 UserSig }) }
登录接口参数说明:
| 参数 | 类型 | 说明 |
| sdkAppID | number | 从 控制台 获取,通常是以 140 或 160 开头的 10 位整数。 |
| userID | string | 当前用户的唯一 ID,仅包含英文字母、数字、连字符和下划线。为避免多端登录冲突,请勿使用 1、123 等简单 ID。 |
| userSig | string | 用于腾讯云鉴权的票据。请注意:开发环境:你可以采用本地 GenerateTestUserSig.genTestSig 函数生成 userSig 或者通过 UserSig 辅助工具 生成临时的 UserSig。生产环境:为了防止密钥泄露,请务必采用服务端生成 UserSig 的方式。详细信息请参考 服务端生成 UserSig。更多信息请参见 如何计算及使用 UserSig。 |
六、步骤1:创建通话界面
你需要创建一个通话页面,当发起通话时唤起通话页面,实现方式如下:
1. 创建通话页面:你可以新建一个页面作为通话宿主页面,用于响应来电时的跳转逻辑。
2. 通话页面绑定 CallCoreView : 通话视图核心组件,自动监听 CallState 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。
<call-core-view style="height: 50px; width: 50px;"></call-core-view>
注意:说明:
注意:CallCoreView 为原生组件,不需要在文件中单独进行 import。挂载在页面上即可使用。
CallCoreView 视图组件功能说明:
| 功能 | 说明 | 参考文档 |
| 设置布局模式 | 支持自由切换布局模式。若未设置,将根据通话人数自动适配布局。 | 切换布局模式 |
| 设置头像 | 支持通过传入头像资源路径,为特定用户自定义头像。 | 自定义默认头像 |
| 设置音量提示图标 | 支持根据不同音量等级,配置个性化的音量指示图标。 | 自定义音量提示的图标 |
| 设置网络提示图标 | 支持根据实时网络质量,配置对应的网络状态提示图标。 | 自定义网络提示的图标 |
| 设置等待接听用户的动画 | 在多人通话场景下,支持传入 GIF 图像路径,为待接听状态的用户展示动画。 | 自定义 loading 动画 |
七、步骤2:添加通话控制按钮
你可以参考 DeviceState、CallState 提供的 API ,自定义添加你的按钮。
·DeviceState 功能说明:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。建议将对应方法绑定至按钮点击事件,并通过监听设备状态变更来实时刷新按钮的 UI 状态。
·CallState 功能说明:接听、挂断、拒接等核心通话控制能力。建议将对应方法绑定至按钮点击事件,并监听通话状态的变化,以确保按钮显示与当前通话阶段保持同步。
·图标资源下载:按钮图标可以直接从 GitHub 下载。这些图标由我们的设计师专为 TUICallKit 打造,无版权风险,可放心使用。
| 图标: | | | | | | | | |
以添加挂断、麦克风、摄像头按钮为例,实现方式如下:
1. 添加挂断按钮:创建并添加挂断按钮,在点击事件中调用 hangup 并销毁界面。
<template> <view class="btn" @tap="handleHangup"> <image class="btn-img" :style="[style]" :src="HANGUP_SRC"></image> <text class="btn-text" v-if="isShowText"> 挂断 </text> </view></template><script setup lang="ts"> import { computed } from "vue"; import HANGUP_SRC from "../../../static/icon/hangup.png"; // 根据实际情况替换为你存放图标的路径 import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState'; const { hangup } = useCallState() const props = defineProps({ size: { type: Number, default: 60, }, isShowText: { type: Boolean, default: true, }, }); const style = computed(() => ({ width: props.size + "px", height: props.size + "px", })); const handleHangup = () => { hangup(); // 在点击事件中调用 hangup 接口并销毁页面 };</script><style scoped> .btn { margin: 10px 20px; } .btn-img { width: 60px; height: 60px; border-radius: 140px; } .btn-text { font-size: 12px; color: #d5e0f2; font-weight: 400; text-align: center; margin-top: 10px; }</style>
2. 添加麦克风开关按钮:创建并添加麦克风开关按钮,在点击事件中调用 openLocalMicrophone 或 closeLocalMicrophone 接口,并监听麦克风状态,实时更新按钮文本。
<template> <view class="btn" @tap="handleMic"> <image class="btn-img" :src="microphoneStatus === DeviceStatus.ON ? MIC_ON_SRC : MIC_OFF_SRC"></image> <text class="btn-text"> {{ microphoneStatus === DeviceStatus.ON ? '麦克风已开' : '麦克风已关' }} </text> </view></template><script setup lang="ts"> import MIC_ON_SRC from "../../../static/icon/mic-on.png"; // 根据实际情况替换为你存放图标的路径 import MIC_OFF_SRC from "../../../static/icon/mic-off.png"; // 根据实际情况替换为你存放图标的路径 import { useDeviceState, DeviceStatus } from '@/uni_modules/tuikit-atomic-x/state/DeviceState'; const { microphoneStatus, openLocalMicrophone, closeLocalMicrophone, } = useDeviceState() // 点击事件调用开启或关闭麦克风 const handleMic = () => { if (microphoneStatus.value === DeviceStatus.ON) { closeLocalMicrophone(); } else { openLocalMicrophone(); } };</script><style scoped> .btn { margin: 10px 20px; } .btn-img { width: 60px; height: 60px; border-radius: 140px; } .btn-text { font-size: 12px; color: #d5e0f2; font-weight: 400; text-align: center; margin-top: 10px; }</style>
3. 添加摄像头开关按钮:创建并添加摄像头开关按钮,在点击事件中调用 openLocalCamera 或 closeLocalCamera 接口,并监听摄像头的状态,实时更新按钮文本。
<template> <div class="btn" @tap="handleCamera"> <image class="btn-img" :src="cameraStatus === DeviceStatus.ON ? CAMERA_ON_SRC : CAMERA_OFF_SRC "></image> <text class="btn-text"> {{cameraStatus === DeviceStatus.ON ? '摄像头已开' : '摄像头已关'}} </text> </div></template><script setup lang="ts"> import CAMERA_ON_SRC from "../../../static/icon/camera-on.png"; // 根据实际情况替换为你存放图标的路径 import CAMERA_OFF_SRC from "../../../static/icon/camera-off.png"; // 根据实际情况替换为你存放图标的路径 import { useDeviceState, DeviceStatus } from '@/uni_modules/tuikit-atomic-x/state/DeviceState'; const { cameraStatus, openLocalCamera, closeLocalCamera, isFrontCamera, } = useDeviceState() // 摄像头按钮点击事件 const handleCamera = () => { if (cameraStatus.value === DeviceStatus.ON) { closeLocalCamera() } else { openLocalCamera({ isFront: isFrontCamera.value }) } };</script><style> .btn { margin: 10px 20px; } .btn-img { width: 60px; height: 60px; border-radius: 140px; } .btn-text { font-size: 12px; color: #d5e0f2; font-weight: 400; text-align: center; margin-top: 10px; }</style>
八、步骤3:发起通话
你可以在 calls 调用成功后跳转通话界面,我们建议你根据媒体类型自动开启麦克风或摄像头获得更好的通话体验,实现方式如下:
1. 发起通话:调用 calls 发起通话。
2. 开启媒体设备:发起通话成功后开启麦克风,如果是视频通话同时开启摄像头。
3. 唤起通话页面:发起通话成功,唤起通话页面。
<script setup lang="ts">import { ref } from 'vue';import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';import { useDeviceState } from '@/uni_modules/tuikit-atomic-x/state/DeviceState';const { calls } = useCallState();const { openLocalCamera, openLocalMicrophone } = useDeviceState();const calleeUserID = ref('');const mediaType = ref(1); // 0: 语音, 1: 视频const startCall = () => { const userID = calleeUserID.value.trim(); if (!userID) { uni.showToast({ icon: 'none', title: '请输入对方用户ID' }); return; } // 1. 打开本地设备 if (mediaType.value === 1) { openLocalCamera({ isFront: true }); } openLocalMicrophone(); // 2. 发起通话 calls({ participantIds: [userID], mediaType: mediaType.value, success: () => { console.log('发起通话成功'); uni.navigateTo({ url: '/uni_modules/tuikit-atomic-x/pages/call?layoutTemplate=Float' // 示例路径,请替换为你在步骤 1 中创建的真实通话界面路径 }); }, fail: (code: number, message: string) => { console.error('发起通话失败:', code, message); uni.showToast({ icon: 'none', title: `通话失败: ${message}` }); } });};</script>
九、步骤4:结束通话
无论你调用 hangup 挂断还是对方主动结束通话,均会触发 onCallEnded 事件。 建议监听该事件,当该事件被触发(即通话结束)时,执行关闭当前界面的操作。实现方式如下:
1. 监听通话结束事件:监听 onCallEnded 事件。
2. 销毁通话页面:onCallEnded 触发后,销毁通话页面。
import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';import { useDeviceState } from '@/uni_modules/tuikit-atomic-x/state/DeviceState';const { addCallListener } = useCallState();const { closeLocalCamera, closeLocalMicrophone } = useDeviceState();/** * 初始化通话服务(最小示例:仅处理通话结束事件并回退页面)建议在应用初始化时执行 */export function initCallService() { addCallListener('onCallEnded', (event: string) => { let res: any; try { res = JSON.parse(event); } catch (error) { console.error('[CallService] onCallEnded parse error:', error); return; } console.log('[CallService] onCallEnded, reason:', res.reason); // 关闭本地设备 closeLocalCamera(); closeLocalMicrophone(); // 页面回退 const pages = getCurrentPages(); if (pages.length > 1) { uni.navigateBack({ delta: 1 }); } else { uni.redirectTo({ url: '/pages/index/index' }); // 示例路径:请结合你的项目真实路径进行修改 } });}
onCallEnded 事件参数详细说明:
| 参数 | 类型 | 说明 |
| callId | String | 此次通话的唯一标识。 |
| mediaType | CallMediaType | 通话媒体类型,用于指定发起音频通话还是视频通话。CallMediaType.Video:视频通话。CallMediaType.Audio:语音通话。 |
| reason | CallEndReason | 通话结束的原因。Unknown:未知原因,无法确定结束原因。Hangup:正常挂断,用户主动挂断通话。Reject:拒绝接听,被叫方拒绝来电。Busy:对方忙线,被叫方正在通话中。Cancel:通话取消,主叫方在对方接听前取消。 |
| userId | String | 触发结束的用户 ID。 |
十、运行效果
当你完成以上 5 步后,"拨打一通电话"运行效果如下:
十一、定制页面
CallCoreView 提供了完善的 UI 定制能力,支持头像及音量提示等图标的自由替换。为助力快速集成,你可以直接从 GitHub 下载。这些图标由我们的设计师专为 TUICallKit 打造,无版权风险,可放心使用。
十二、自定义音量提示的图标
你可以通过设置 CallCoreView 组件的 volumeLevelIcons 属性来设置音量大小等级不同的提示图标。
volumeLevelIcons 属性设置示例代码:
<template> <view class="call-view-container"> <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"> <call-core-view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }" :volumeLevelIcons="volumeLevelIcons" ></call-core-view> </view> </view></template><script setup> import { ref, onMounted } from 'vue'; const systemInfo = ref({}); /** * 将 /static/ 相对路径转为原生层可用的本地绝对路径 */ function toAbsolutePath(relativePath) { if (plus && plus.io && plus.io.convertLocalFileSystemURL) { return plus.io.convertLocalFileSystemURL(relativePath); } return relativePath; } /** * 批量转换对象中所有 value 的路径为绝对路径 */ function convertIconPaths(iconMap) { const result = {}; Object.keys(iconMap).forEach(key => { result[key] = toAbsolutePath(iconMap[key]); }); return result; } // 音量等级图标:key 为等级名称,value 为图标本地绝对路径 const volumeLevelIcons = ref(convertIconPaths({ "Mute": "/static/images/callview-self-mute.png", // 可以替换你项目中的实际图标路径 "Low": "/static/images/callview-network.png" // 可以替换你项目中的实际图标路径 })); onMounted(() => { uni.getSystemInfo({ success: (res) => { systemInfo.value = res; } }); });</script><style> .call-view-container { background: rgba(15, 16, 20, 0.5); overflow: hidden; }</style>
注意:说明:
注意:uni-app 传递给原生层的参数只支持本地路径,因此需要将相对路径转化为本地路径。
·属性参数详细说明:
| 参数 | 类型 | 是否必填 | 说明 |
| icons | [String: String] | 是 | 音量等级与图标资源的映射表。结构说明如下:key ( VolumeLevel ) 表示音量等级:Mute:表示麦克风关闭,静音状态。Low:表示音量范围 (0-25]。Medium:表示音量范围 (25-50]。High:表示音量范围 (50-75]。Peak:表示音量范围 (75-100]。Value ( String ) 表示对应音量等级的图标资源路径。 |
·音量提示图标:
| 图标 | 说明 |
| | 【图标含义】音量提示图标。【推荐用法】你可以将该图标等级设置为 Low 或 Medium,当用户音量大于对应等级时显示。 |
| | 【图标含义】静音图标。【推荐用法】你可以将该图标等级设置为 Mute,当该用户静音时显示。 |
十三、自定义网络提示的图标
你可以设置 CallCoreView 组件的 networkQualityIcons 属性设置不同网络状态的提示图标。
·networkQualityIcons 属性设置示例代码:
<template> <view class="call-view-container"> <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"> <call-core-view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }" :networkQualityIcons="networkQualityIcons" ></call-core-view> </view> </view></template><script setup> import { ref, onMounted } from 'vue'; const systemInfo = ref({}); /** * 将 /static/ 相对路径转为原生层可用的本地绝对路径 */ function toAbsolutePath(relativePath) { if (plus && plus.io && plus.io.convertLocalFileSystemURL) { return plus.io.convertLocalFileSystemURL(relativePath); } return relativePath; } /** * 批量转换对象中所有 value 的路径为绝对路径 */ function convertIconPaths(iconMap) { const result = {}; Object.keys(iconMap).forEach(key => { result[key] = toAbsolutePath(iconMap[key]); }); return result; } const networkQualityIcons = ref(convertIconPaths({ "BAD": "/static/images/callview-network-bad.png", // 可以替换你项目中的实际图标路径 "VERY_BAD": "/static/images/callview-network-bad.png" // 可以替换你项目中的实际图标路径 })); onMounted(() => { uni.getSystemInfo({ success: (res) => { systemInfo.value = res; } }); });</script><style> .call-view-container { background: rgba(15, 16, 20, 0.5); overflow: hidden; }</style>
·networkQualityIcons 接口参数详细说明:
| 参数 | 类型 | 是否必填 | 说明 |
| icons | [String: String] | 是 | 网络质量与图标资源的映射表。结构说明如下:Key ( String):表示网络质量等级:UNKNOWN:未知网络状态。EXCELLENT:网络状态极佳。GOOD:网络状态较好。POOR:网络状态较差。BAD:网络状态差。VERY_BAD:网络状态极差。DOWN:网络断开。Value ( String ):对应网络状态的图标资源路径。 |
·网络较差的提示图标:
| 图标 | 说明 |
| | 【图标含义】网络较差的提示图标。【推荐用法】你可以将该图标等级设置为 BAD、VERY_BAD 或 DOWN,当网络较差时显示该图标。 |
十四、自定义默认头像
你可以设置 CallCoreView 的 participantAvatars 属性设置用户头像。建议你监听响应式数据 allParticipants(所有参与通话的成员):当获取到用户头像时设置并展示;若用户未设置头像或加载失败,则显示默认头像(占位图)。
participantAvatars 属性设置示例代码:
<template> <view class="call-view-container"> <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"> <call-core-view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }" :participantAvatars="participantAvatars" ></call-core-view> </view> </view></template><script setup> import { ref, watch, onMounted } from 'vue'; import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState'; const { allParticipants } = useCallState(); const systemInfo = ref({}); const participantAvatars = ref({}); const avatarCache = {}; const DEFAULT_AVATAR_URL = "https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar1.png"; let defaultAvatarLocalPath = ""; let defaultAvatarReady = false; /** * 将相对路径转为原生层可用的本地绝对路径 */ function toAbsolutePath(relativePath) { if (plus && plus.io && plus.io.convertLocalFileSystemURL) { return plus.io.convertLocalFileSystemURL(relativePath); } return relativePath; } /** * 下载头像并缓存,返回本地绝对路径 */ function downloadAvatar(url) { if (avatarCache[url]) { return Promise.resolve(avatarCache[url]); } return new Promise((resolve) => { uni.downloadFile({ url, success: (res) => { if (res.statusCode === 200) { const absolutePath = toAbsolutePath(res.tempFilePath); avatarCache[url] = absolutePath; resolve(absolutePath); } else { resolve(defaultAvatarLocalPath || ""); } }, fail: () => { resolve(defaultAvatarLocalPath || ""); } }); }); } // 预下载默认头像 const defaultAvatarPromise = downloadAvatar(DEFAULT_AVATAR_URL).then(localPath => { defaultAvatarLocalPath = localPath; defaultAvatarReady = true; // 默认头像就绪后,补刷一次参与者头像 if (allParticipants.value && allParticipants.value.length > 0) { updateParticipantAvatars(allParticipants.value); } }); /** * 根据参与者列表,下载头像并构建 { userId: localPath } 映射 */ async function updateParticipantAvatars(participants) { if (!participants || participants.length === 0) { participantAvatars.value = {}; return; } if (!defaultAvatarReady && defaultAvatarPromise) { await defaultAvatarPromise; } const avatarMap = {}; const tasks = []; participants.forEach(participant => { if (participant.id && participant.avatarURL) { tasks.push( downloadAvatar(participant.avatarURL).then(localPath => { avatarMap[participant.id] = localPath; }) ); } else if (participant.id) { avatarMap[participant.id] = defaultAvatarLocalPath || ""; } }); await Promise.all(tasks); participantAvatars.value = avatarMap; } // 监听参与者变化,更新头像映射 watch(() => allParticipants.value, async (newVal) => { if (!newVal) return; await updateParticipantAvatars(newVal); }, { immediate: true, deep: true }); onMounted(() => { uni.getSystemInfo({ success: (res) => { systemInfo.value = res; } }); });</script><style> .call-view-container { background: rgba(15, 16, 20, 0.5); overflow: hidden; }</style>
·participantAvatars 属性参数详细说明:
| 参数 | 类型 | 是否必填 | 说明 |
| avatars | [String: String] | 是 | 用户头像映射表。字典结构说明如下:Key:用户的 userID。Value:该用户的头像资源绝对路径。 |
·默认头像资源:
| 图标 | 说明 |
| | 【图标含义】默认头像。【推荐用法】当用户头像加载失败或无头像时,你可以给该用户设置此默认头像。 |
十五、自定义 loading 动画
你可以设置 CallCoreView 的 waitingAnimation 属性,为等待中用户设置等待动画获得更好的体验。
·waitingAnimation 属性设置示例代码:
<template> <view class="call-view-container"> <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"> <call-core-view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }" :waitingAnimation="waitingAnimation" ></call-core-view> </view> </view></template><script setup> import { ref, onMounted } from 'vue'; const systemInfo = ref({}); /** * 将 /static/ 相对路径转为原生层可用的本地绝对路径 */ function toAbsolutePath(relativePath) { if (plus && plus.io && plus.io.convertLocalFileSystemURL) { return plus.io.convertLocalFileSystemURL(relativePath); } return relativePath; } // 等待动画 GIF:传入本地绝对路径供原生组件渲染 const waitingAnimation = ref(toAbsolutePath("/static/images/callview-loading.gif")); // 可以替换你项目中的实际图标路径 onMounted(() => { uni.getSystemInfo({ success: (res) => { systemInfo.value = res; } }); });</script><style> .call-view-container { background: rgba(15, 16, 20, 0.5); overflow: hidden; }</style>
·waitingAnimation 接口参数详细说明:
| 参数 | 类型 | 是否必填 | 说明 |
| path | String | 是 | GIF 格式图像资源的绝对路径。 |
·等待接听的动画:
| 图标 | 说明 |
| | 【图标含义】用户等待接听动画。【推荐用法】群组通话时设置的动画。设置后,当用户的状态为等待接听时,显示该动画。 |
十六、添加通话计时提示
通话计时可通过响应式数据 activeCall 的 duration 字段实时获得,实时显示通话计时的实现方式如下:
绑定通话计时数据:将 activeCall.duration 字段绑定至 UI 。该字段为响应式数据,会自动驱动 UI 实时刷新,无需手动维护定时器。
<template> <view v-if="isConnected" class="call-timer"> <text class="timer-text">{{ formattedTime }}</text> </view></template><script setup lang="ts"> import { computed } from 'vue'; import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState'; const { selfInfo, activeCall } = useCallState(); const isConnected = computed(() => { return selfInfo.value?.status === 2; }); const formattedTime = computed(() => { const duration = activeCall.value?.duration ?? 0; const hours = Math.floor(duration / 3600); const minutes = Math.floor((duration % 3600) / 60); const seconds = duration % 60; const pad = (n: number) => n.toString().padStart(2, '0'); if (hours > 0) { return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`; } return `${pad(minutes)}:${pad(seconds)}`; });</script><style scoped> .call-timer { display: flex; align-items: center; justify-content: center; } .timer-text { font-size: 28rpx; color: #FFFFFF; font-weight: 400; }</style>
十八、自定义用户头像/昵称
通话开始前,你可以通过 setSelfInfo 方法,设置自己的昵称和头像。
·setSelfInfo 示例代码:
import { useLoginState } from "@/uni_modules/tuikit-atomic-x/state/LoginState";const { setSelfInfo } = useLoginState();/** * 设置当前登录用户的个人信息(昵称、头像等) */export const setSelfUserInfo = (userID: string, nickname: string, avatarURL: string): void => { setSelfInfo({ userProfile: { userID, nickname, avatarURL, }, success: () => { console.log('用户信息设置成功'); }, fail: (errCode: number, errMsg: string) => { console.error('用户信息设置失败:', errCode, errMsg); }, });};
·setSelfInfo 接口参数详细说明:
| 参数 | 类型 | 是否必填 | 说明 |
| userProfile | UserProfile | 是 | 用户信息结构体:userID:用户的 ID。avatarURL:用户头像的 URL。nickname:用户的昵称。更多字段详情可参考 UserProfile。 |
十九、切换布局模式
CallCoreView 内置三种布局模式,你可以设置 layoutTemplate 属性来设置布局模式。若未主动配置,CallCoreView 将根据通话人数自动适配:1 V 1 场景下默认采用 Float 模式,多人通话场景下则自动切换为 Grid 模式。不同布局模式的说明如下:
| Float 模式 | Grid 模式 | PIP 模式 |
| | | |
| 布局逻辑:呼叫等待时全屏显示己方画面;接通后全屏显示对方画面,己方画面以悬浮小窗展示。交互特性:支持小窗拖拽移动,点击小窗可实现大小画面互换。 | 布局逻辑:所有成员画面呈网格状平铺排列成宫格模式布局,适用 2 人以上通话,支持点击放大画面功能。交互特性:支持点击特定成员画面放大查看。 | 布局逻辑:1 v 1 场景固定显示对方画面,多人场景:采用当前发言者(Active Speaker) 策略,自动识别并全屏展示正在说话的用户。交互特性:等待时显示自己的画面,接通后还会显示通话计时。 |
·layoutTemplate 属性设置示例代码:
<template> <view class="call-view-container"> <view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }"> <call-core-view :style="{ height: systemInfo?.windowHeight + 'px', width: systemInfo?.safeArea?.width + 'px' }" :layoutTemplate="layoutTemplate" ></call-core-view> </view> </view></template><script setup> import { ref, watch, onMounted } from 'vue'; import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState'; const { activeCall } = useCallState(); const systemInfo = ref({}); // 布局模板:'Float' 单人浮窗布局,'Grid' 多人宫格布局 const layoutTemplate = ref("Grid"); // 根据通话信息自动切换布局:多人或群聊用 Grid,单人用 Float watch(() => activeCall.value, (newValue) => { if (!newValue || !newValue.inviteeIds) { return; } const newTemplate = (newValue.inviteeIds.length > 1 || newValue.chatGroupId !== '') ? 'Grid' : 'Float'; if (layoutTemplate.value !== newTemplate) { layoutTemplate.value = newTemplate; } }, { immediate: true, deep: true }); onMounted(() => { uni.getSystemInfo({ success: (res) => { systemInfo.value = res; } }); });</script><style> .call-view-container { background: rgba(15, 16, 20, 0.5); overflow: hidden; }</style>
·layoutTemplate 属性参数详细说明:
| 参数 | 类型 | 说明 |
| layoutTemplate | String | CallCoreView 的布局模式Float:布局逻辑:呼叫等待时全屏显示己方画面;接通后全屏显示对方画面,己方画面以悬浮小窗展示。交互特性:支持小窗拖拽移动,点击小窗可实现大小画面互换。Grid:布局逻辑:所有成员画面呈网格状平铺排列成宫格模式布局,适用 2 人以上通话,支持点击放大画面功能。交互特性:支持点击特定成员画面放大查看。PIP:布局逻辑:1v1 场景固定显示对方画面,多人场景:采用当前发言者(Active Speaker) 策略,自动识别并全屏展示正在说话的用户。交互特性:等待时显示自己的画面,接通后还会显示通话计时。 |
20、设置通话的默认超时时间
你可以在发起通话 calls 时,通过配置参数 CallParams 中的 timeout 字段来指定等待超时时间。示例代码如下:
<script setup>import { ref } from 'vue';import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';const { calls } = useCallState();const calleeUserID = ref('');const mediaType = ref(1); // 0: 语音, 1: 视频const startCall = () => { const userID = calleeUserID.value.trim(); if (!userID) { uni.showToast({ icon: 'none', title: '请输入对方用户ID' }); return; } calls({ participantIds: [userID], mediaType: mediaType.value, params: { timeout: 30 }, success: () => { console.log('发起通话成功'); uni.navigateTo({ url: '/uni_modules/tuikit-atomic-x/pages/call?layoutTemplate=Float' // 示例路径,请替换为你在步骤 1 中创建的真实通话界面路径 }); }, fail: (code, message) => { console.error('发起通话失败:', code, message); uni.showToast({ icon: 'none', title: `通话失败: ${message}` }); } });};</script>
| 参数 | 类型 | 是否必填 | 说明 |
| participantIds | Array | 是 | 目标用户的 userId 列表。 |
| mediaType | CallMediaType | 是 | 通话媒体类型,用于指定发起音频通话还是视频通话。CallMediaType.Video:视频通话。CallMediaType.Audio:语音通话。 |
| params | CallParams | 否 | 通话扩展参数,如:房间号、通话邀请超时时间等。roomId (String):房间 ID,可选参数,未指定时由服务端自动分配。timeout (Int):呼叫超时时间(秒)。userData (String):用户自定义数据。chatGroupId (String):Chat 群组 ID,用于群组通话场景。isEphemeralCall (Boolean):是否为加密通话(不产生通话记录)。 |
21、播放等待接听的提示音
你可以监听自己的通话状态,在等待接听时播放铃声,在接听通话或通话结束时停止播放铃声。
import { watch } from 'vue';import { useCallState } from '@/uni_modules/tuikit-atomic-x/state/CallState';const { selfInfo } = useCallState();function ensureAudioContext() { if (!uni.$innerAudioContext) { const ctx = uni.createInnerAudioContext(); ctx.onError((err: any) => { console.error('[CallService] innerAudioContext onError:', err?.errCode, err?.errMsg || err); }); uni.$innerAudioContext = ctx; } return uni.$innerAudioContext;}function playRingtone(src: string) { const ctx = ensureAudioContext(); try { ctx.stop(); } catch (_e) {} ctx.src = src; ctx.loop = true; ctx.autoplay = true; setTimeout(() => { try { ctx.play(); } catch (e) {} }, 50);}function stopRingtone() { if (uni.$innerAudioContext) { try { uni.$innerAudioContext.loop = false; uni.$innerAudioContext.stop(); } catch (e) {} }}export { playRingtone, stopRingtone };// 建议在应用初始化的时候执行该函数export function initCallService() { ensureAudioContext(); // 通话接通(status=2)或结束(status=0)时,停止铃声 watch(() => selfInfo.value, (newVal) => { if (newVal?.status === 2 || newVal?.status === 0) { stopRingtone(); } }, { immediate: true, deep: true });}
22、下一步
恭喜你,已经完成了"拨打一通电话",接下来,你可参考 接听第一通电话 实现接听电话功能。
24、如何保证通话过程中屏幕常亮?
uni-app 提供了对应的设置接口 uni.setScreenBrightness
25、如何禁止系统侧滑返回上一个页面的行为?
iOS 端可以通过设置页面上的 disableSwipeBack 属性进行禁止。详情参考 uni-app 产品文档。
Android 端可以通过 onBackPress 拦截系统侧滑和物理返回,示例代码如下:
注意:使用该方式进行系统侧滑和物理返回的拦截需要确保该页面使用的是 自定义导航栏。
<script setup> import { onBackPress } from '@dcloudio/uni-app' // 拦截系统侧滑和物理返回键,防止通话中误退出 onBackPress((option) => { if (option.from === 'backbutton') { return true; // 返回 true 阻止默认回退行为 } });</script>
26、联系我们
如果你在使用过程中,有什么建议或者意见,可以 联系我们,感谢你的反馈。