xj-meeting-sdk-vue3
v1.0.10
Published
基于 Vue3 + Vite + Quasar2 的现代化视频会议 SDK 组件库。
Readme
XJ Meeting SDK Vue3
一个基于 Vue3 + Vite + Quasar2 的现代化 WebRTC 视频会议组件,集成了 xj WebRTC 服务器,支持多端会议、音视频、屏幕共享、设备管理等丰富功能。
技术栈
- Vue 3.x:响应式、组合式 API,现代前端主流框架
- Vite:极速开发与构建工具,支持 HMR 和现代 JS 特性
- Quasar 2.x:高颜值、高性能的 Material Design UI 框架
- WebRTC:实时音视频通信协议,支持多端互动
- TypeScript(可选):类型安全、增强开发体验
- ESLint/Prettier:代码规范与自动格式化
- Node.js:本地开发与脚本支持
主要功能特性
- 多端实时会议:支持多人音视频互动,会议室可动态加入/退出
- 音视频控制:本地/远程音频、视频开关,设备切换,分辨率选择
- 屏幕共享:支持桌面、窗口、标签页共享,状态实时同步
- 设备管理:摄像头、麦克风、扬声器选择与测试,设备权限检测
- 参会者管理:实时参会者列表,角色区分,媒体状态显示,单选/多选操作
- 会议详情与网络状态:会议标题、时长、分辨率、码率、网络质量可视化
- 自定义按钮:支持举手、投票、邀请等自定义会议操作
- 安全连接:全程 HTTPS/WSS,媒体流加密,支持自签名证书
- UI/UX 优化:响应式布局,暗色主题,动画过渡,移动端适配
- 日志与调试:内置 customLog 日志,自动行号脚本,便于定位与维护
安装与启动
# 安装依赖
pnpm install
# 或 yarn install
# 或 npm install
# 启动开发服务器
pnpm dev
# 或 yarn dev
# 或 npm run dev构建与预览
# 构建生产包
pnpm build
# 本地预览
npx serve dist
# 或用 https 预览
npx serve dist --ssl-cert ssl/cert.pem --ssl-key ssl/key.pem --listen 3000打包与集成
打包为组件库
pnpm build打包后 dist 目录下生成 UMD/ESM 格式的集成包,可在其他 Vue3 项目中直接 import 使用:
import { XjMeetingSdkVue3 } from 'xj-meeting-sdk-vue3'
import 'xj-meeting-sdk-vue3/dist/xj-meeting-sdk-vue3.css'通过 script 标签集成
<script src="dist/xj-meeting-sdk-vue3.umd.js"></script>
<link rel="stylesheet" href="dist/xj-meeting-sdk-vue3.css" />目录结构
src/- 源代码components/- 会议相关 Vue3 组件assets/- 静态资源
dist/- 构建输出public/- 公共资源
组件用法示例
MeetingComponent 组件(推荐 Vue3 <script setup> 语法)
<script setup>
import { ref } from 'vue'
import MeetingComponent from './components/MeetingComponent.vue'
const meetingComponent = ref(null)
const meetingCode = ref('123456')
const userName = ref('your_login_name')
const userPwd = ref('your_password')
const serverHost = ref('wss://your-server-host')
const wssPort = ref(4088)
const httpsPort = ref(8088)
const iceServers = ref([{ urls: 'stun:stun.l.google.com:19302' }])
const logLevel = ref(1)
const displayName = ref('张三')
const loginType = ref(1)
const sipServerHost = ref('')
const customButtons = ref([
{ name: 'invite', icon: 'person_add', color: 'primary', onClick: handleInvite }
])
const nameFilter = name => {
const match = name.match(/^([^:]+):/)
return match ? match[1] : name
}
function handleInvite({ userInfo }) {
// 自定义按钮点击逻辑
console.log('邀请', userInfo)
}
function onRegistrationSuccess() {
// SIP 注册成功回调
console.log('SIP 注册成功')
}
function onMeetingNotFound(code) {
// 会议号未找到回调
console.warn('会议未找到:', code)
}
function onCustomBtnClick({ button, userInfo }) {
// 处理自定义按钮点击
console.log('按钮', button, userInfo)
}
</script>
<template>
<MeetingComponent
ref="meetingComponent"
:meeting-code="meetingCode"
:login-name="userName"
:login-pwd="userPwd"
:server-host="serverHost"
:wss-port="wssPort"
:https-port="httpsPort"
:ice-servers="iceServers"
:log-level="logLevel"
:custom-buttons="customButtons"
:display-name="displayName"
:login-type="loginType"
:sip-server-host="sipServerHost"
:name-filter="nameFilter"
@registration-success="onRegistrationSuccess"
@meeting-not-found="onMeetingNotFound"
@custom-btn-click="onCustomBtnClick"
/>
</template>通过 ref 调用组件方法
// 例如在父组件中调用会议组件的方法:
meetingComponent.value?.init && meetingComponent.value.init()
meetingComponent.value?.callMeetingRoom && meetingComponent.value.callMeetingRoom('123456')props 说明
| Prop | 类型 | 必填 | 说明 | |------------------|----------------|------|----------------------------| | meeting-code | String | 是 | 会议号 | | login-name | String | 是 | 登录用户名 | | login-pwd | String | 是 | 登录密码 | | server-host | String | 是 | WebSocket 服务器主机 | | wss-port | Number | 否 | WebSocket 信令端口 | | https-port | Number | 否 | HTTPS API端口 | | ice-servers | Array | 否 | WebRTC ICE服务器配置 | | log-level | Number | 否 | 日志级别 | | custom-buttons | Array | 否 | 自定义按钮配置 | | display-name | String | 否 | 显示名称 | | login-type | String/Number | 否 | 登录类型(1/2) | | sip-server-host | String | 否 | SIP服务器主机 | | name-filter | Function/RegExp| 否 | 参会者名称过滤 |
事件说明
| 事件名 | 回调参数 | 说明 | |-----------------------|------------------------|----------------------------------------| | registration-success | 无 | SIP 注册成功 | | meeting-not-found | meetingCode: String | 会议号未找到 | | custom-btn-click | button, userInfo | 自定义按钮点击事件 |
更多用法请参考各组件源码和注释。
常见问题与注意事项
- HTTPS/WSS:建议开发和生产环境都使用 HTTPS/WSS,保证媒体和信令安全。
- 浏览器兼容:推荐 Chrome/Edge/Firefox/Safari 最新版。
- Quasar 样式:如需自定义样式,可在 src/quasar-variables.sass 或全局样式中覆盖。
- 设备权限:首次使用需允许浏览器访问摄像头和麦克风。
- 屏幕共享:需在安全上下文(HTTPS)下使用。
日志自动行号脚本
本项目提供了自动为 customLog 日志补全/替换真实行号的 Node.js 脚本:
node replaceCustomLogLineNumber.js版本更新说明
- 支持 Vue3 + Vite + Quasar2
- 组件全部重构为组合式 API
- 支持屏幕共享、设备管理、参会者管理
- 优化了 UI 体验和移动端适配
如有问题欢迎提 issue 或联系开发者。
安装
# 使用 pnpm 安装依赖
pnpm install开发
# 启动开发服务器
pnpm serve本地预览构建结果
HTTP 服务
# 启动 HTTP 服务
npx serve dist访问 http://localhost:3000
HTTPS 服务(推荐)
- 生成自签名证书:
mkdir -p ssl && openssl req -newkey rsa:2048 -new -nodes -x509 -days 365 -keyout ssl/key.pem -out ssl/cert.pem -subj "/C=CN/ST=State/L=City/O=Organization/OU=Unit/CN=localhost"- 启动 HTTPS 服务:
npx serve dist --ssl-cert ssl/cert.pem --ssl-key ssl/key.pem --listen 3000访问 https://localhost:3000
注意:使用自签名证书时,浏览器会显示证书不受信任的警告,这是正常的。在开发环境中可以忽略此警告继续访问。
WebSocket 安全配置
当通过 HTTPS 访问应用时,WebSocket 连接也必须使用安全连接(WSS)。配置已更新为:
- WebSocket:
wss://${host}:4188/ - HTTP API:
https://${host}:8088/janus
确保 xj 服务器:
- 配置了有效的 SSL 证书
- 启用了 WSS 支持
- 正确配置了 4188 端口的 WSS 服务
测试
# 运行单元测试
pnpm test目录结构
src/- 源代码目录components/- Vue 组件assets/- 静态资源
dist/- 构建输出目录tests/- 测试文件public/- 公共资源目录
浏览器支持
- Chrome ≥ 70
- Firefox ≥ 65
- Safari ≥ 12
- Edge ≥ 79
许可证
MIT
MeetingComponent 组件功能说明
组件概述
MeetingComponent.vue 是一个功能完整的视频会议组件,提供了丰富的会议功能和现代化的用户界面。
主要功能
1. 会议基础功能
- 会议加入/退出
- 自动加入指定会议室:通过 SIP 协议自动连接并加入指定的会议室,支持自定义会议号和用户信息
- 优雅的退出机制:在用户退出时自动关闭所有媒体流,清理资源,断开连接
- 显示加载状态和进度:使用优雅的动画效果显示会议加入进度,提供良好的用户体验
2. 媒体控制
音频控制
- 麦克风开关:一键控制本地音频流的开启/关闭,支持快捷键操作
- 扬声器开关:控制远程音频的播放/静音,适用于需要暂时屏蔽声音的场景
- 音量控制:实时调节音频输入输出音量,支持可视化音量显示
- 音频设备切换:动态切换音频输入输出设备,无需重新加入会议
视频控制
- 摄像头开关:一键控制本地视频流的开启/关闭,支持设备级别的控制
- 视频设备切换:在多个摄像头之间无缝切换,自动处理视频流的重新协商
- 视频预览功能:会议前预览本地视频画面,调整摄像头参数
屏幕共享
- 开启/停止屏幕共享:支持分享整个屏幕、应用窗口或浏览器标签
- 屏幕共享状态管理:实时显示共享状态,支持画质和帧率控制
- 异常处理:自动处理共享中断、权限变更等异常情况
3. 会议信息显示
状态栏信息
- 会议标题显示:清晰展示当前会议的名称和主题
- 会议时长实时计时:精确记录会议持续时间,支持多种时间格式
- 网络状态实时监控:通过图标和文字直观显示当前网络质量
- 全屏模式切换:支持一键切换全屏显示,优化会议体验
会议详情
- 会议标题:可自定义的会议名称,支持动态修改
- 会议号:唯一的会议标识符,便于分享和加入
- 开始时间:记录会议开始的具体时间,支持多时区显示
- 参会人数:实时统计当前参会人数,显示在线状态
4. 参会者管理
- 参会者列表
- 显示所有参会者:实时更新的参会者列表,包含在线状态
- 参会者角色显示:区分主持人、普通参会者等不同角色
- 参会者媒体状态:显示每个参会者的音视频状态
- 实时状态更新:自动同步参会者的状态变化,包括进入、退出等事件
5. 设备管理
- 设备设置
- 音频设备选择:支持选择和测试多个音频输入输出设备
- 视频设备选择:支持选择不同的摄像头,预览视频效果
- 扬声器设备选择:独立的扬声器设备选择,支持音频测试
- 设备切换过渡:平滑处理设备切换过程,确保通话不中断
6. 邀请功能
- 会议邀请
- 生成会议链接:自动生成包含会议信息的邀请链接
- 复制邀请链接:一键复制功能,支持多种格式的邀请信息
- 邀请分享功能:集成多种分享渠道,如邮件、消息等
7. 网络质量管理
- 网络状态监控
- 实时质量显示:通过图标直观展示当前网络状态
- 状态可视化:使用不同颜色和图标展示网络质量等级
- 自动网络调整:根据网络状况自动调整音视频质量
8. 用户界面特性
响应式设计
- 屏幕适配:自动适应不同尺寸的显示设备
- 移动端支持:针对触摸屏优化的交互体验
- 过渡动画:流畅的状态切换动画,提升用户体验
暗色主题
- 主题适配:自动检测并适应系统主题设置
- 暗色优化:专门优化的暗色模式配色方案
加载状态
- 加载动画:美观的加载动画效果
- 进度显示:清晰的进度指示器
- 状态反馈:及时的操作反馈和提示
9. 安全特性
- 安全连接
- WSS 支持:安全的 WebSocket 连接,防止中间人攻击
- HTTPS 协议:加密的 HTTP 通信,保护数据传输安全
- 媒体加密:端到端的媒体流加密,保护通话内容
技术实现特点
1. 媒体处理
- 使用 WebRTC 技术进行实时通信
- 支持音视频设备的动态切换
- 处理各种媒体流异常情况
2. 状态管理
- 完整的生命周期管理
- 可靠的资源清理机制
- 异常状态自动恢复
3. 性能优化
- 优化的视频渲染性能
- 高效的事件处理机制
- 智能的资源管理
4. 错误处理
- 完善的错误捕获机制
- 友好的错误提示
- 自动重试机制
修改了quasar的源码样式,然后打包到了插件中,使用的时候引用插件的样式就可以了
.disabled, [disabled], .disabled *, [disabled] * { outline: 0 !important; cursor: default; }
自定义按钮(custom-buttons)使用示例
方式一:事件监听方式
<meeting-component
...
:custom-buttons="[
{ label: '举手', value: 'raiseHand', icon: 'pan_tool', color: 'primary' },
{ label: '投票', value: 'vote', icon: 'how_to_vote', color: 'secondary' }
]"
@custom-button-click="onCustomButtonClick"
/>methods: {
onCustomButtonClick(btn) {
// btn.value 区分按钮类型
if (btn.value === 'raiseHand') {
// 举手逻辑
} else if (btn.value === 'vote') {
// 投票逻辑
}
}
}方式二:回调函数方式
<meeting-component
...
:custom-buttons="[
{ label: '举手', value: 'raiseHand', icon: 'pan_tool', color: 'primary', onClick: handleRaiseHand },
{ label: '投票', value: 'vote', icon: 'how_to_vote', color: 'secondary', onClick: handleVote }
]"
/>methods: {
handleRaiseHand(btn) {
// 举手逻辑
},
handleVote(btn) {
// 投票逻辑
}
}按钮对象字段说明: | 字段 | 类型 | 说明 | |---------|----------|------------------------------| | label | String | 按钮显示文本 | | value | String | 按钮唯一标识 | | icon | String | 按钮图标(可选) | | color | String | 按钮颜色(可选) | | onClick | Function | 按钮点击回调(可选,优先级高)|
如果按钮对象设置了 onClick 回调,则点击时会优先调用该回调,否则会触发 @custom-button-click 事件。
nameFilter 使用示例
只显示冒号前内容:
nameFilter: name => {
const match = name.match(/^([^:]+):/);
return match ? match[1] : name;
}在 App.vue 传递:
<meeting-component
...
:name-filter="nameFilter"
/>版本更新说明
1.0.3
1.0.5
- 增加登录接口密码加密传输逻辑,提升安全性[sdk至少使用这个版本]
- 过滤掉隐藏的账号属性
1.0.6
- 增加调用网关服务需要认证的操作
1.0.7
- 修复密码加入会议的时候,ip写死的问题
