uni-app 标准版(Vue3)

最近更新时间:2025-10-16 21:31:22

我的收藏

组件介绍

针对用户不同场景的诉求和体积要求,我们推出了多个版本的 UI 组件 ,您可以根据实际业务需求选择集成最适合的版本。
注意:
您目前预览的是 TUIKit 标准版的接入文档。
功能区分
聊天组件
聊天组件+通话组件
登录&登出
用户资料与管理
消息列表
文本&自定义消息
图片&视频消息
历史消息
会话列表
未读消息计数
1v1 视频、音频通话
-
全局监听来电
-
微信同款 UI 设计
-
呼叫/接听/拒绝/挂断
-
体积大小
0.51 MB
0.92 MB
标准版界面效果如下图所示:
会话列表
聊天页面
视频通话







环境要求

HBuilderX 需要升级到最新版本
TypeScript / JavaScript (TUIKit 使用 ts 语言开发,支持在 js 或者 ts 项目中集成
Vue2 / Vue3
sass(sass-loader 版本 ≤ 10.1.1)
node(12.13.0 ≤ node,推荐使用 Node.js 官方 LTS 版本 16.17.0)
npm(版本请与 node 版本匹配)

组件集成

完成以下步骤即可发送您的第一条消息。

步骤1:创建项目 (可选)

1. 打开 HBuilderX,在菜单栏中选择 “文件 > 创建 > 项目”,创建一个名为 chat-example 的 uni-app 项目。

2. 在终端输入npm init -y,创建package.json文件。
npm init -y

步骤2:引入组件

1. 下载组件。
MacOS 端
Windows 端
npm i tuikit-atomicx-uniapp-wx-standard
npm i tuikit-atomicx-uniapp-wx-standard
2. 拷贝源码。
MacOS 端
Windows 端
mkdir -p ./TUIKit && cp -r node_modules/tuikit-atomicx-uniapp-wx-standard/ ./TUIKit
xcopy node_modules\\tuikit-atomicx-uniapp-wx-standard .\\TUIKit /i /e

步骤3:获取鉴权

SDKAppID 信息,可通过 即时通信 IM 控制台 获取。

userID 信息,可单击 即时通信 IM 控制台 > 账号管理,切换至目标应用所在账号,创建 2 个 userID 方便后续体验聊天功能

userSig 信息,可单击 即时通信 IM 控制台 > 开发工具 > UserSig生成&校验,填写创建的 userID,即可生成 userSig。


步骤4:发送第一条消息

注意:
请检查以下页面文件是否已存在。如果不存在,请按照指定的路径结构新建对应文件。
登录页面
会话列表
聊天页面
个人中心
page.json
请将以下内容复制到 pages/login/login.vue 文件。
<template>
<view class="container">
<view class="login-section">
<text class="title">用户登录</text>
<input
class="input-box"
v-model="userID"
placeholder="请输入用户ID"
placeholder-style="color:#BBBBBB;"
/>
<button class="login-btn" @click="handleLogin">登录</button>
</view>
</view>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import { useLoginState } from '../../TUIKit'

const userID = ref('')

const handleLogin = async () => {
// 必填信息
// 测试TUIKit时可以从腾讯云IM控制台获取userSig
// 生产环境部署请从您的服务器获取
// 查看文档:https://cloud.tencent.com/document/product/269/32688
await useLoginState().login({
userId: userID.value,
userSig: '',
sdkAppId: 0,
})
wx.$globalCallPagePath = 'TUIKit/components/CallView/CallView'; // 配置全局监听页面路径
uni.switchTab({ url: '/pages/conversation/conversation' })
}
</script>

<style scoped>
.container {
padding: 40px;
height: 100vh;
}

.login-section {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 100px;
}

.title {
font-size: 24px;
font-weight: bold;
margin-bottom: 40px;
}

.input-box {
width: 80%;
height: 50px;
border: 1px solid #DDD;
border-radius: 8px;
padding: 0 15px;
margin-bottom: 20px;
}

.login-btn {
width: 80%;
height: 50px;
background: #006EFF;
color: white;
border-radius: 8px;
}
</style>
请将以下内容复制到 pages/conversation/conversation.vue 文件。
<template>
<view>
<Conversation :onConversationSelect="handleConversationSelect"></Conversation>
<view v-if="!conversationList?.length" class="empty-guide">
<text class="guide-text">暂无会话,请输入用户ID创建聊天</text>
<view class="input-container">
<input v-model="inputUserID" class="userid-input" placeholder="请输入对方用户ID"
placeholder-class="placeholder-style" />
<button class="guide-btn" @click="handleCreateConversation">创建会话</button>
</view>
</view>
</view>
</template>

<script lang="ts" setup>
import { onShow } from '@dcloudio/uni-app'
import { watch, ref } from 'vue';
import Conversation from '../../TUIKit/components/ConversationList/ConversationList.vue'
import { useConversationListState } from '../../TUIKit';

const { createC2CConversation, conversationList, setActiveConversation, totalUnRead } = useConversationListState();
const inputUserID = ref('');

const handleCreateConversation = async () => {
if (!inputUserID.value.trim()) {
return;
}
await createC2CConversation(inputUserID.value);
};

const updateTabBarBadge = () => {
const tabBarList = ['pages/profile/profile', 'pages/conversation/conversation']
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]?.route
const isTabBarPage = currentPage && tabBarList.includes(currentPage)

if (!isTabBarPage) {
return
}

if (totalUnRead.value > 0) {
uni.setTabBarBadge({
index: 0,
text: totalUnRead.value > 99 ? '99+' : totalUnRead.value.toString()
});
}
};

watch(totalUnRead, updateTabBarBadge, { immediate: true });

const handleConversationSelect = (conversation) => {
const { conversationID } = conversation
setActiveConversation(conversationID);
uni.navigateTo({
url: '/pages/chat/chat',
});
}

onShow(() => {
if (totalUnRead.value > 0) {
uni.setTabBarBadge({
index: 0,
text: totalUnRead.value > 99 ? '99+' : totalUnRead.value.toString()
});
} else {
uni.removeTabBarBadge({ index: 0 });
}
});
</script>

<style scoped>
.empty-guide {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
}

.guide-text {
font-size: 16px;
color: red;
margin-bottom: 20px;
}

.input-container {
width: 80%;
display: flex;
flex-direction: column;
align-items: center;
}

.userid-input {
width: 100%;
height: 40px;
padding: 0 10px;
margin-bottom: 15px;
border: 1px solid #eee;
border-radius: 6px;
font-size: 14px;
}

.placeholder-style {
color: #ccc;
}

.guide-btn {
width: 50%;
line-height: 40px;
height: 40px;
background-color: #07c160;
color: white;
border-radius: 6px;
font-size: 16px;
}
</style>
请将以下内容复制到 pages/chat/chat.vue 文件。
<template>
<div class="TUIChat">
<MessageList />
<MessageInput />
</div>
</template>

<script lang="ts" setup>
import MessageInput from '../../TUIKit/components/MessageInput/MessageInput.vue';
import MessageList from '../../TUIKit/components/MessageList/MessageList.vue';
</script>

<style>
.TUIChat {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
}
</style>
请将以下内容复制到 pages/profile/profile.vue 文件。
<template>
<view class="container">
<view class="profile">
<image class="avatar" :src="userInfo.avatarUrl || ''" />
<text class="name">{{ userInfo.userName || '未设置昵称' }}</text>
<text class="id">userID: {{ userInfo.userId }}</text>
</view>

<input v-model="nick" placeholder="新昵称" />
<input v-model="avatar" placeholder="新头像URL" />

<button @click="save">保存</button>
<button @click="logoutHandle" class="logout">退出</button>
</view>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import { useLoginState } from '../../TUIKit'

const { loginUserInfo: userInfo, setSelfInfo, logout } = useLoginState()
const nick = ref('')
const avatar = ref('')

const save = async () => {
if (!nick.value && !avatar.value) return uni.showToast({ title: '请输入内容', icon: 'none' })
try {
await setSelfInfo({
...(nick.value && { userName: nick.value }),
...(avatar.value && { avatarUrl: avatar.value })
})
uni.showToast({ title: '保存成功' })
} catch {
uni.showToast({ title: '保存失败', icon: 'none' })
}
}

const logoutHandle = async () => {
await logout()
uni.reLaunch({ url: '/pages/login/login' })
}
</script>

<style scoped>
.container {
padding: 20px;
}

.profile {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}

.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
background: #f0f0f0;
}

.name {
font-size: 18px;
margin: 10px 0;
}

.id {
color: #888;
font-size: 14px;
}

input {
width: 100%;
height: 40px;
margin-bottom: 15px;
padding: 0 10px;
border: 1px solid #ddd;
border-radius: 6px;
}

button {
width: 100%;
height: 40px;
margin-bottom: 10px;
background: #07c160;
color: white;
border-radius: 6px;
}

.logout {
background: white;
color: #f56c6c;
border: 1px solid #f56c6c;
}
</style>
请将以下内容复制到 pages.json 文件。
{
"pages": [
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/conversation/conversation",
"style": {
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/profile/profile",
"style": {
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/chat/chat",
"style": {
"navigationBarTitleText": "uni-app"
}
}
],
"tabBar": {
"list": [
{
"pagePath": "pages/conversation/conversation",
"text": "消息",
"badge": "{{totalUnRead > 99 ? '99+' : totalUnRead.toString()}}"
},
{
"pagePath": "pages/profile/profile",
"text": "个人中心"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}
}

步骤5:拨打第一通电话(可选)

说明:
如果您不需要此功能,可参考移除音视频通话,能节省 400KB 的小程序主包体积。

1. 开通服务

在使用腾讯云提供的音视频服务前,您需要前往控制台,为应用开通音视频服务。具体步骤请参见 开通服务。

2. 配置微信开放平台

开通企业类小程序
小程序推拉流标签不支持个人小程序,只支持企业类小程序。需要在 注册 时填写主体类型为企业,如下图所示:

在小程序控制台开启实时音视频接口
小程序推拉流标签使用权限暂时只开放给有限类目,具体支持类目参见该地址
符合类目要求的小程序,需要在 微信公众平台 > 开发 > 开发管理 > 接口设置中自助开通该组件权限。

在小程序控制台配置域名
微信公众平台 > 开发 > 开发管理 > 开发设置 > 服务器域名中设置 request 合法域名 socket 合法域名

将以下域名添加到 socket 合法域名:
域名
说明
是否必须
wss://${SDKAppID}w4c.my-imcloud.com
v3.4.6起,SDK 支持独立域名,可更好地保障服务稳定性。
例如您的 SDKAppID 是 1400xxxxxx,则独立域名为: wss://1400xxxxxxw4c.my-imcloud.com
必须
wss://wss.im.qcloud.com
Web IM 业务域名
必须
wss://wss.tim.qq.com
Web IM 业务域名
必须
wss://wssv6.im.qcloud.com
Web IM 业务域名
必须
将以下域名添加到 request 合法域名:
域名
说明
是否必须
https://web.sdk.qcloud.com
Web IM 业务域名
必须
https://boce-cdn.my-imcloud.com
Web IM 业务域名
必须
https://api.im.qcloud.com
Web IM 业务域名
必须
https://events.im.qcloud.com
Web IM 业务域名
必须
https://webim.tim.qq.com
Web IM 业务域名
必须
https://wss.im.qcloud.com
Web IM 业务域名
必须
https://wss.tim.qq.com
Web IM 业务域名
必须
将以下域名添加到 uploadFile 合法域名:
域名
说明
是否必须
https://${SDKAppID}-cn.rich.my-imcloud.com
从 2024年9月10日起,新增应用默认分配 COS 独立域名。
例如您的 SDKAppID 是 1400xxxxxx,则 COS 独立域名为:https://1400xxxxxx-cn.rich.my-imcloud.com
必须
https://cn.rich.my-imcloud.com
文件上传域名
必须
https://cn.imrich.qcloud.com
文件上传域名
必须
https://cos.ap-shanghai.myqcloud.com
文件上传域名
必须
https://cos.ap-shanghai.tencentcos.cn
文件上传域名
必须
https://cos.ap-guangzhou.myqcloud.com
文件上传域名
必须
将以下域名添加到 downloadFile 合法域名:
域名
说明
是否必须
https://${SDKAppID}-cn.rich.my-imcloud.com
从 2024年9月10日起,新增应用默认分配 COS 独立域名。
例如您的 SDKAppID 是 1400xxxxxx,则 COS 独立域名为:https://1400xxxxxx-cn.rich.my-imcloud.com
必须
https://cn.rich.my-imcloud.com
文件下载域名
必须
https://cn.imrich.qcloud.com
文件下载域名
必须
https://cos.ap-shanghai.myqcloud.com
文件下载域名
必须
https://cos.ap-shanghai.tencentcos.cn
文件下载域名
必须
https://cos.ap-guangzhou.myqcloud.com
文件下载域名
必须

3. 拷贝源码

MacOS 端
Windows 端
cp node_modules/@trtc/call-engine-lite-wx/RTCCallEngine.wasm.br ./static
xcopy node_modules\\@trtc\\call-engine-lite-wx\\RTCCallEngine.wasm.br .\\static

4. 配置页面路由

在 pages.json 文件注册全局监听页面。
{
"path": "TUIKit/components/CallView/CallView",
"style": {
"navigationBarTitleText": "uni-app"
}
}

项目运行



配置小程序域名白名单

小程序域名白名单信息请参见:小程序域名白名单配置信息

删除 Debug 脚本

出于体积和安全双重因素考虑,请在发布前删除项目目录下 /TUIKit/debug 文件夹。在开发阶段为了方便开发,项目提供生成本地 UserSig 的脚本文件存放于TUIKit/debug文件夹中,但这并不安全,该方法中 SECRETKEY 很容易被反编译逆向破解,一旦您的密钥泄露,攻击者就可以盗用您的腾讯云流量,因此该方法仅适合本地跑通 Demo 和功能调试。因此,请在项目发布前删除 Debug 脚本,使用后台生成 UserSig,具体原因和操作步骤请参考文档:生成 UserSig

常见问题

移除音视频通话功能

移除通话功能
移除通话按钮
移除 TUIKit/index.ts 中的模块导出。

移除 TUIKit/components/MessageInput/MessageInput.vue 中的通话按钮。