wuin-cloud-phone
v0.1.0
Published
Framework-independent cloud phone player with Vue, React and isolated vendor runtimes
Readme
wuin-cloud-phone
云手机播放器组件,支持 Vue 3 和 React,提供设备画面、导航键、工具栏、连接状态和事件接口。
安装
npm install wuin-cloud-phone按项目选择 Vue 3.5.2+ 或 React 18/19。包使用 ESM,运行于现代浏览器。使用组件时需引入样式:
import 'wuin-cloud-phone/style.css'准备连接配置
Vue 和 React 使用相同的 CloudPhoneOptions。先在业务项目中创建 phone-options.ts:
import type { CloudPhoneOptions, PhoneSession } from 'wuin-cloud-phone'
export const phoneOptions: CloudPhoneOptions = {
phoneId: 'your-phone-id',
vendor: 'your-runtime-key',
runtimeUrl: 'https://your-domain.example/cloud-phone/runtime.html',
sessionProvider: async ({ phoneId, vendor, reason, signal }) => {
const response = await fetch('/api/cloud-phone/session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phoneId, vendor, reason }),
signal,
})
if (!response.ok) throw new Error('获取云手机会话失败')
const session: PhoneSession = await response.json()
return session
},
}将示例中的设备 ID、运行配置标识和 URL 替换为业务实际值:
| 配置 | 说明 |
| --- | --- |
| phoneId | 当前云手机的设备 ID |
| vendor | 运行配置标识,须与已部署的 runtime 配置一致 |
| runtimeUrl | 业务已部署的 runtime 页面的完整 HTTP(S) 地址;本包不提供托管地址 |
| sessionProvider | 获取连接会话的异步函数,首次连接和重连时调用 |
/api/cloud-phone/session 需要由业务提供,返回 { credentials, expiresAt? }。credentials 为 runtime 所需的短期凭证对象,必须可结构化克隆;可选的 expiresAt 为到期时间的毫秒时间戳。请将 signal 传给网络请求,以便取消已过期的连接请求。
Vue 使用
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import { WuinCloudPhone } from 'wuin-cloud-phone/vue'
import type { PhoneEvent } from 'wuin-cloud-phone'
import { phoneOptions } from './phone-options'
import 'wuin-cloud-phone/style.css'
const phone = useTemplateRef<InstanceType<typeof WuinCloudPhone>>('phone')
function onEvent(event: PhoneEvent) {
if (event.type === 'error') console.error(event.data.message)
}
function reconnect() {
void phone.value?.getClient()?.reconnect().catch(console.error)
}
</script>
<template>
<WuinCloudPhone
ref="phone"
:options="phoneOptions"
title="我的云手机"
@event="onEvent"
/>
<button type="button" @click="reconnect">重新连接</button>
</template>React 使用
import { useRef } from 'react'
import { WuinCloudPhone } from 'wuin-cloud-phone/react'
import type { WuinCloudPhoneRef } from 'wuin-cloud-phone/react'
import type { PhoneEvent } from 'wuin-cloud-phone'
import { phoneOptions } from './phone-options'
import 'wuin-cloud-phone/style.css'
export function PhonePanel() {
const phoneRef = useRef<WuinCloudPhoneRef>(null)
function onEvent(event: PhoneEvent) {
if (event.type === 'error') console.error(event.data.message)
}
function reconnect() {
void phoneRef.current?.getClient()?.reconnect().catch(console.error)
}
return (
<>
<WuinCloudPhone
ref={phoneRef}
options={phoneOptions}
title="我的云手机"
onEvent={onEvent}
/>
<button type="button" onClick={reconnect}>重新连接</button>
</>
)
}组件挂载后会自动连接,卸载时自动清理。保持 options 对象引用稳定;React 中动态生成配置时可使用 useMemo。切换设备或连接配置时传入新的 options 对象,组件会重建实例,不要仅修改原对象的字段。
组件配置
Vue 和 React 支持以下通用属性;Vue 模板中使用 :toolbar-layout 等短横线写法。
| 属性 | 默认值 | 说明 |
| --- | --- | --- |
| options | 必填 | 连接配置与业务操作 |
| toolbar | true | 显示右侧工具栏 |
| navigation | true | 显示返回、主页、最近任务按钮 |
| locale | 'zh-CN' | 支持 'zh-CN'、'en-US' |
| title | 随语言设置 | 播放器标题 |
| showUnsupported | false | 显示暂不支持的工具按钮,按钮保持禁用 |
| toolbarLayout | 内置布局 | 自定义工具栏顺序 |
工具栏顺序
import type { ToolbarLayout } from 'wuin-cloud-phone/ui'
const toolbarLayout: ToolbarLayout = {
main: ['file.open', 'quality.set', 'volume.up', 'volume.down', 'more'],
more: ['live.open', 'device.restart', 'device.powerOff'],
bottom: ['apps.open'],
}Vue 传入 :toolbar-layout="toolbarLayout",React 传入 toolbarLayout={toolbarLayout}。main 为主菜单,more 为更多菜单,bottom 为底部菜单;三个数组均需提供。未列出的内置菜单会补入更多菜单,不支持的操作默认隐藏。更新布局时替换整个布局对象即可,不会重新连接。
业务操作
通过 options.services 为文件、应用、直播及设备管理等菜单配置业务回调。以下路径和接口均需由业务项目提供:
import type { CloudPhoneOptions, ServiceHandlers } from 'wuin-cloud-phone'
import { phoneOptions } from './phone-options'
const services: ServiceHandlers = {
'file.open': async (_, { phoneId }) => {
window.open(`/phones/${encodeURIComponent(phoneId)}/files`, '_blank', 'noopener')
},
'device.restart': async (_, { phoneId, vendor, signal }) => {
const response = await fetch('/api/cloud-phone/restart', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phoneId, vendor }),
signal,
})
if (!response.ok) throw new Error('重启请求失败')
},
}
const options: CloudPhoneOptions = { ...phoneOptions, services }将这里的 options 传给组件。其他常用键包括 apps.open、live.open、scan.open、device.powerOff、device.reset、device.new。回调接收当前设备信息和 AbortSignal,返回的 Promise 应覆盖实际业务请求;失败时抛出 Error,工具栏会显示错误信息。文件、应用和直播页面的内容由业务自行实现。
工具栏执行设备管理操作时会弹出确认框;通过代码调用 execute() 时需自行确认。已配置的关机、重启、重置和新机操作可在断开状态执行。可通过 options.permissions 禁用指定命令,例如 { 'device.reset': false }。
事件和实例方法
Vue 使用 @event、@ready、@error,React 使用 onEvent、onReady、onError。通用事件包含 type、instanceId、phoneId、vendor、timestamp 和对应的 data。
| 事件 | 用途 |
| --- | --- |
| stateChange | 读取 data.state,跟踪连接状态 |
| ready | 连接就绪,读取 data.capabilities 获取可用操作 |
| firstFrame | 首帧到达,读取画面 width、height |
| metrics | 读取延迟、帧率、码率等可用指标 |
| capabilitiesChange | 更新可用操作和权限状态 |
| reconnecting / reconnected | 跟踪连接恢复过程 |
| error / warning | 读取错误信息及 retryable |
| closed | 连接关闭,读取 data.reason |
通过 Vue 的 phone.value?.getClient() 或 React 的 phoneRef.current?.getClient() 获取 CloudPhone 实例。组件尚未完成初始化时可能返回 undefined。
| 方法 / 属性 | 用途 |
| --- | --- |
| connect() | 建立连接;已就绪时直接返回 |
| reconnect() | 重新获取会话并连接 |
| disconnect() | 断开当前连接,不执行设备关机 |
| destroy() | 销毁实例;再次使用需重新挂载组件 |
| execute(command, payload?) | 执行当前可用的操作 |
| state / capabilities | 当前状态和能力 |
| on(name, handler) / onEvent(handler) | 订阅指定或全部事件,返回取消订阅函数 |
例如,在连接就绪后切换画质:
import type { CloudPhone } from 'wuin-cloud-phone'
async function setHighQuality(client: CloudPhone) {
const capability = client.capabilities['quality.set']
if (client.state === 'ready' && capability?.supported && capability.enabled) {
await client.execute('quality.set', { level: 'hd' })
}
}画质支持 smooth、standard、hd。其他常见调用包括 execute('navigation.home')、execute('volume.up') 和 execute('clipboard.write', { text: '内容' }),是否可用以当前 capabilities 为准。调用方法时应处理 Promise 拒绝。会话不会自动定时刷新,业务可在需要时调用 reconnect() 获取新会话。
本地开发
npm install
npm run dev # 启动 Vue / React 演示
npm run check # 类型、测试、构建及消费端类型检查
npm run build:demo # 构建演示页面到 demo-dist
npm pack # 生成可在其他项目安装的 npm 包