npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zjp179280/web-call-components

v0.1.1

Published

Native Web Components for 1:1 audio/video calls, multi-window VOD/monitoring (with voice intercom) and multi-party video meeting (rooms, layouts, host controls, screen share, local recording). MQTT signaling + WebRTC. Works with Vue2/Vue3/React/vanilla.

Readme

@zjp179280/web-call-components

基于 MQTT 信令 + WebRTC 的一对一语音 / 视频通话组件,封装为原生 Web Component,Vue2 / Vue3 / 原生 HTML 均可直接使用。

  • 语音通话:<wc-audio-call>
  • 视频通话:<wc-video-call>

信令逻辑完整移植自原项目 mqtStore.js 的一对一通话路径(type 0/1/5/6/7/8/9/10/11/1012/1014/1015),去除了 Pinia / Element Plus / 全局 store 依赖,所有外部配置通过 config 属性注入。


一、安装

npm install @zjp179280/web-call-components

本地开发时可通过 file:../web-call-components 直接链接源码包(见同级 web-call-components-demo)。


二、最快上手(Vue3)

<template>
  <button @click="start">发起语音通话</button>

  <wc-audio-call
    v-if="show"
    ref="callEl"
    peer-name="设备A"
    @call-status-change="onCall"
    @mqtt-status-change="onMqtt"
    @error="onError"
    @close="show = false"
  ></wc-audio-call>
</template>

<script setup>
import { ref, nextTick } from 'vue'
import '@zjp179280/web-call-components'   // 样式已内联进各元素 Shadow DOM,无需再引入 css

const show = ref(false)
const callEl = ref(null)

async function start() {
  show.value = true
  await nextTick()
  // 对象配置必须以 DOM property 方式注入(不能写在模板属性里)
  callEl.value.config = {
    mqtt: {
      url: 'ws://your-mqtt-broker:8083/mqtt',
      clientId: 'Web~10001',
      username: '10001',
      password: '你的token'
    },
    rtc: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] },
    user: { id: '10001', name: '管理员' },
    targetId: '设备号'
  }
}

// 注意:自定义元素 DOM 事件的载荷统一放在 event.detail[0](Vue 把 emit 参数放进 detail 数组)
function onCall(e) { console.log('通话状态', e.detail[0]) }
function onMqtt(e) { console.log('MQTT', e.detail[0]) }
function onError(e) { console.error(e.detail[0]) }
</script>

视频通话同理,把标签换成 <wc-video-call> 即可。

Vue3 + Vite 工程必做:在 vite.config.js 告诉编译器 wc- 开头的标签是原生自定义元素(否则对象 props / 事件会被当成 Vue 组件处理而异常):

// vite.config.js
import vue from '@vitejs/plugin-vue'
export default {
  plugins: [
    vue({ template: { compilerOptions: { isCustomElement: (t) => t.startsWith('wc-') } } })
  ]
}

Vue3 + vue.config(CLI / webpack)则用 compilerOptions.isCustomElement 链式配置。


二·五、推荐:先连接 MQTT,确认成功后再拨号(两步法)

组件支持注入一个已连接好的 CallEngine,把“连 MQTT”和“拨号”拆开,便于先校验连接:

import { CallEngine } from '@zjp179280/web-call-components'

let engine = null

// 第一步:连接 MQTT
async function connectMqtt(config) {
  engine = new CallEngine(config)
  engine.on('mqttStatus', (s) => console.log('MQTT', s.status, s.message))
  await engine.connect()   // 会等待真正连上并订阅 p2p 主题;失败会抛错
}

// 第二步:拨号(复用上面的连接,组件不会重复连 MQTT)
async function startVoice(config) {
  const el = document.createElement('wc-audio-call')
  el.engine = engine        // 注入已连接引擎(关键)
  el.config = config        // 仍需传 config,用于 targetId / user 等
  document.body.appendChild(el)
}
  • 传入 engine 后,组件不再自建 MQTT 连接,通话结束也不会断开这条连接,可连续拨打。
  • 不传 engine 时,组件会在挂载后用 config 自行连接(一步法,向后兼容)。
  • 完整可运行示例见同级工程 web-call-components-demo(页面上就是「① 连接 MQTT → ② 发起通话」两个按钮)。

三、在 Vue2 中使用

Vue2 需要告诉编译器这些标签是原生自定义元素:

// main.js
import Vue from 'vue'
import App from './App.vue'
import '@zjp179280/web-call-components'   // 样式已内联进各元素 Shadow DOM,无需再引入 css

Vue.config.ignoredElements = [/^wc-/]

new Vue({ render: h => h(App) }).$mount('#app')
<template>
  <wc-audio-call
    v-if="show"
    ref="call"
    peer-name="设备A"
    @call-status-change.native="onCall"
    @mqtt-status-change.native="onMqtt"
    @close.native="show = false"
  ></wc-audio-call>
</template>

<script>
export default {
  data: () => ({ show: false }),
  methods: {
    start() {
      this.show = true
      this.$nextTick(() => {
        this.$refs.call.config = { /* 同上方 config */ }
      })
    }
  }
}
</script>

Vue2 监听原生自定义事件需要加 .native。


四、在原生 HTML 中使用

<script type="module">
  import '@zjp179280/web-call-components'   // 样式已内联,无需引 css

  const call = document.createElement('wc-audio-call')
  call.peerName = '设备A'
  call.config = {
    mqtt: { url: 'ws://your-mqtt-broker:8083/mqtt', clientId: 'Web~1', username: '1', password: 'token' },
    rtc: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] },
    user: { id: '1', name: '管理员' },
    targetId: '设备号'
  }
  call.addEventListener('call-status-change', (e) => console.log(e.detail[0]))
  call.addEventListener('close', () => call.remove())
  document.body.appendChild(call)
</script>

五、完整 config 配置项

| 字段 | 类型 | 必填 | 说明 | | ---------------------- | ------------- | -- | ----------------------------- | | mqtt.url | string | ✅ | MQTT 地址,必须 ws:// 或 wss:// | | mqtt.clientId | string | ✅ | 本端客户端 ID,与后端约定(如 Web~用户ID) | | mqtt.username | string | 否 | 连接用户名(通常传用户 ID) | | mqtt.password | string | 否 | 连接密码(登录 token) | | mqtt.extraTopics | string[] | 否 | 额外订阅的业务主题 | | mqtt.reconnectPeriod | number | 否 | 重连间隔,默认 5000ms | | rtc.iceServers | array | 否 | STUN/TURN 服务器,默认 Google STUN | | rtc.bitrate | number | 否 | 视频发送码率上限,默认 1000000(1Mbps) | | user.id | string | 否 | 本端用户 ID,用于 extra_data | | user.name | string | 否 | 本端用户名,用于 extra_data | | targetId | string/number | ✅ | 对端设备 ID(也可用标签 target-id 属性) | | topics.call | string | 否 | 呼叫发布主题,默认 device/call | | topics.p2pPrefix | string | 否 | 响应主题前缀,默认 device/p2p/ | | http.baseURL | string | 否 | 业务接口地址(可选;通话本身走 MQTT) | | http.headers | object | 否 | 业务接口默认请求头 | | log | boolean | 否 | 是否打印调试日志,默认 true |


六、事件

组件派发以下 DOM 事件(CustomEvent)。Vue 自定义元素约定:emit 的参数被放进 event.detail 数组,业务载荷取 event.detail[0](close 无载荷):

| 事件 | event.detail[0] | 说明 | | -------------------- | --------------------------- | ----------------------------------------------------------------------- | | call-status-change | { message, errorMessage } | 通话状态:init/dialing/connected/rejected/ended/no_answer/errorMessageText | | mqtt-status-change | { status, message } | MQTT 状态:connecting/connected/reconnecting/disconnected/error | | error | Error | 致命错误(取 event.detail[0].message) | | close | — | 用户关闭面板 |

el.addEventListener('call-status-change', (e) => {
  const s = e.detail[0]          // ← 载荷在数组首项
  console.log(s.message, s.errorMessage)
})

直接使用 CallEngine 实例(不走 DOM 事件)时,engine.on('callStatus', s => ...) 回调里的 s 就是载荷本身,无需 [0]。


七、暴露的方法

通过 ref 调用:

callEl.value.hangup()   // 通话中挂断
callEl.value.cancel()   // 拨打中取消
callEl.value.close()    // 关闭面板

八、内置校验(重要)

组件在发起前会依次做以下校验,并通过 mqtt-status-change / call-status-change / error 反馈:

  1. MQTT 地址校验:缺少 mqtt.url 或不是 ws/wss 开头 → 直接报错,不发起呼叫。
  2. MQTT 连接校验:连接成功 / 重连中 / 断开 / 失败都会实时回传状态徽标。
  3. HTTP 配置校验:若未配置 http.baseURL,控制台给出警告(不阻断,因为通话走 MQTT)。
  4. 媒体设备校验:摄像头/麦克风权限被拒绝、设备不存在、被占用时,回传中文错误提示。
  5. targetId 校验:缺少对端设备 ID 时提示。
  6. 30 秒无应答:自动转为 no_answer 并取消呼叫。
  7. 资源释放:关闭/卸载时停止轨道、关闭 PeerConnection、断开音频元素。

九、样式自定义(CSS 变量 + ::part + 头像)

组件运行在 Shadow DOM 内:宿主页面的普通 CSS 选择器进不去(因此不会被全局样式污染,也不会泄漏样式),但有两条标准通道可以深度定制。所有样式已随 JS 内联进各自定义元素,无需、也不要再引入任何 css 文件。

9.1 方式一:覆盖 CSS 变量(推荐)

CSS 自定义属性会穿透 Shadow 边界。在你的页面里对元素本身(或 :root)赋值即可,随改随生效:

/* 单个组件定制 */
wc-audio-call {
  --wc-panel-bg: #0b1f33;        /* 面板底色(默认深蓝 rgb(0,29,55)) */
  --wc-primary: #13c2c2;         /* 主色:确定按钮、拨打波点、头像渐变起点 */
  --wc-accent: #ffd000;          /* 底部 L 形切角颜色 */
  --wc-corner-size: 22px;        /* 切角大小,设 0 可隐藏切角 */
  --wc-avatar-size: 84px;        /* 头像大小 */
}

/* 全局统一改两个组件 */
:root {
  --wc-hangup: #e53935;
  --wc-panel-radius: 12px;
}

可用变量(均在内部 :host 上给了默认值):

| 分类 | 变量 | 默认值 | 作用 | | ---- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------- | | 品牌色 | --wc-primary | #1890ff | 主色(确定按钮、波点、头像渐变起点) | | | --wc-primary-strong | #0275cf | 主色深色 | | | --wc-cyan | #00ffff | 通话中“激活/选中”描边 | | | --wc-accent | #18d9f5 | 切角、高光 | | | --wc-border | rgba(117,190,255,.9) | 面板边框 | | | --wc-hangup / --wc-hangup-hover / --wc-hangup-icon | #f44336 / #ff6659 / #fe5e5e | 挂断按钮 | | | --wc-danger / --wc-success / --wc-warning | #f56c6c / #67c23a / #ff9800 | 错误 / 正常 / 重连状态色 | | 面板 | --wc-panel-bg | #001d37 | 面板背景 | | | --wc-panel-radius | 8px | 面板圆角 | | | --wc-panel-border-width | 2px | 边框宽度 | | | --wc-panel-glow / --wc-panel-shadow | 内发光 + 外投影 | 面板光效(可设 none 关闭) | | | --wc-corner / --wc-corner-size | var(--wc-accent) / 16px | 底部 L 形切角颜色 / 大小(0 隐藏) | | 文字 | --wc-text / --wc-text-dim | #fff / rgba(255,255,255,.7) | 主文字 / 次文字 | | | --wc-font / --wc-title-font | 系统字体栈 / Fira Code 等宽 | 正文字体 / 标题字体 | | | --wc-title-size / --wc-name-size / --wc-code-size / --wc-status-size | 17/15/12/15px | 标题 / 名称 / 编号 / 状态字号 | | 标题底板 | --wc-title-plate-bg / --wc-title-plate-border | 蓝色渐变 / 半透明蓝边 | 标题底板背景 / 描边 | | | --wc-title-line | 蓝色渐变线 | 标题下分隔线 | | 头像 | --wc-avatar-size | 72px | 头像直径 | | | --wc-avatar-bg | linear-gradient(135deg,#1890ff,#36cfc9) | 无图片时的头像底(首字头像) | | | --wc-avatar-border / --wc-avatar-shadow | none | 头像描边 / 阴影 | | | --wc-avatar-image | none | 设为 url(...) 可直接铺头像图 | | 按钮 | --wc-btn-sm / --wc-btn-md / --wc-btn-lg | 36/42/48px | 小 / 中 / 大圆钮尺寸 | | | --wc-btn-bg / --wc-btn-bg-video / --wc-btn-hover | rgba(0,78,108,.7) / rgba(255,255,255,.2) / rgba(255,255,255,.22) | 语音钮 / 视频控制条钮 / 悬停底 | | | --wc-btn-icon | 22px | 按钮图标大小 | | 尺寸 | --wc-audio-width | 300px | 语音面板宽 | | | --wc-video-width / --wc-video-height | min(960px,94vw) / 540px | 视频舞台宽 / 高 | | | --wc-video-stage-radius | 8px | 视频舞台圆角 | | | --wc-pip-width / --wc-pip-height | 200px / 132px | 本地小窗宽 / 高(可拖拽) | | | --wc-pulse-size | 240px | 视频拨打中脉冲圆环直径 |

9.2 方式二:::part() 精准改结构元素

组件已把关键结构通过 part 暴露,可用 ::part() 直接改某个部位(背景、模糊、边框等):

/* 视频底部控制条加毛玻璃 */
wc-video-call::part(control-bar) { backdrop-filter: blur(8px); background: linear-gradient(to top, rgba(0,0,0,.8), transparent); }
/* 自定义“确定”按钮 */
wc-audio-call::part(btn-confirm) { background: #00b359; border-radius: 8px; }
/* 头像外圈 */
wc-audio-call::part(avatar) { border: 2px solid #fff; box-shadow: 0 0 12px rgba(255,255,255,.6); }

暴露的 part:

  • 公共:panel header title-plate title title-line close mqtt avatar peer-name peer-code status-text btn(以及具体按钮 btn-mute btn-speaker btn-hangup btn-cancel btn-confirm btn-camera)。
  • 语音:另有 body peer top-controls stage wave duration。
  • 视频:另有 stage banner(重连横幅)remote-video overlay pulse(脉冲环)local-pip local-video control-bar call-info。

提示:part="btn btn-mute" 这样的元素同时拥有两个 part 名——::part(btn) 命中所有按钮,::part(btn-mute) 只命中麦克风钮。

9.3 头像

三种方式(优先级从高到低):

  1. 属性传图片地址(推荐):<wc-audio-call avatar-url="https://xxx/a.png">,或 JS el.avatarUrl = url。
  2. CSS 变量铺图:wc-audio-call { --wc-avatar-image: url(https://xxx/a.png); }。
  3. 都不配时,显示 peer-name 的第一个字,底为 --wc-avatar-bg 渐变。

9.4 视频本地小窗

通话中右上角(默认定位,可拖到舞台任意位置)的本地预览窗,尺寸用 --wc-pip-width / --wc-pip-height 调整,边框 / 镜像可通过 ::part(local-pip)、::part(local-video) 覆盖;关闭摄像头时本地窗会自动变暗。


十、目录结构

web-call-components/
├── src/
│   ├── core/CallEngine.js          # MQTT+WebRTC 信令引擎(移植自 mqtStore)
│   ├── components/
│   │   ├── AudioCall.ce.vue        # <wc-audio-call>
│   │   └── VideoCall.ce.vue        # <wc-video-call>
│   ├── styles/theme.css            # 集中管理的设计变量与公共样式
│   └── index.js                    # 注册自定义元素
└── dist/                           # 构建产物

十一、发布到 npm

⚠️ 不要把账号密码写进任何文件。请在 npmjs.com 生成 Automation Token。

# 1. 登录(推荐用 token)
npm login

# 2. 构建
npm run build

# 3. 发布(首次发布需要 --access public)
npm publish --access public

发布后,业务项目即可 npm install @zjp179280/web-call-components 使用。


十二、本地测试项目111

同级目录 web-call-components-demo 是一个 Vue3 测试工程:

cd web-call-components-demo
npm install
npm run dev    # 默认 http://localhost:5199

打开页面后填写 MQTT 地址、clientId、对端设备号,点击「发起语音/视频通话」即可联调。