更新记录
1.0.5(2026-09-11)
- 支持连接多设备
- 优化
1.0.4(2026-05-11)
- 连接接口新增RFCOMM Socket链路类型的服务UUID
1.0.3(2026-05-11)
- 新增纯血鸿蒙端
平台兼容性
uni-app(4.87)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | Android插件版本 | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | × | × | 5.0 | 1.0.1 | × | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(4.87)
| Chrome | Safari | Android | Android插件版本 | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|
| × | × | 5.0 | 1.0.1 | × | √ | × |
yt-uts-bluetooth
基于 UTS 的 Android、HarmonyOS 经典蓝牙 插件:配对列表、扫描周边设备、连接、收发数据、MTU 设置与释放资源。
特别提醒
- 购买本插件前,请先试用并充分自测确认满足需求之后再行购买;虚拟物品一旦购买之后无法退款。
- 如有使用上的疑问或 bug,可进交流群联系作者;作者可承接插件定制。
- 请在合法范围内使用;若使用本插件从事非法开发,本方概不负责。
- 试用需自定义基座(见
package.json中uni_modules平台说明)。 - 当前实现仅支持 App Android、HarmonyOS(
app-ios不支持)。 - android示例安装包
- 低功耗蓝牙插件可使用这款:低功耗/BLE蓝牙
引入方式
业务侧从 uni_modules 引入 函数 与 类型(类型定义见 utssdk/interface.uts):
import *as BTManager from '@/uni_modules/yt-uts-bluetooth'
权限与初始化
initBluetooth()
- 作用:申请蓝牙相关权限;全部通过后创建内部经典蓝牙管理器。
- 参数:无。
- 设备与应用
targetSdkVersion均达到 Android 12(API 31)及以上:申请BLUETOOTH_SCAN、BLUETOOTH_CONNECT;其中扫描权限声明为neverForLocation,不用于推断物理位置。 - 设备低于 Android 12,或应用
targetSdkVersion < 31:使用旧蓝牙权限兼容模型,运行时申请ACCESS_FINE_LOCATION,用于经典蓝牙设备发现;不需要修改应用的targetSdkVersion。 - HarmonyOS: 申请
ohos.permission.ACCESS_BLUETOOTH
未成功拿到权限时,内部管理器不会创建;后续调用 getBondList 等依赖管理器的接口会失败。
initBluetoothWithOptions(options)(1.0.5 新增)
原有 initBluetooth() 保持不变。新项目若需要在权限处理完成后再开始扫描或连接,可使用:
BTManager.initBluetoothWithOptions({
success: () => {
// 此时可以安全调用 getBondList / discoveryBluetooth / connectBluetoothWithName
},
fail: (err) => {
console.log(err.errCode, err.mes)
}
})
API 一览(与 utssdk/app-android/index.uts 导出一致)
| 函数 | 说明 |
|---|---|
initBluetooth() |
请求权限并初始化内部管理器 |
initBluetoothWithOptions(options) |
带成功/失败回调地初始化;旧 API 不受影响 |
getBondList(options) |
获取系统已配对(绑定)设备列表 |
discoveryBluetooth(para) |
开始扫描周边设备 |
connectBluetoothWithName(options) |
按 MAC 连接设备并注册读/连接回调 |
disconnectBluetooth(address) |
按 MAC 断开指定设备 |
sendData(options) |
向已连接设备发送字节或字符串 |
setMtu(para) |
设置应用层单次写入分片上限(见类型说明) |
getMtu(para) |
查询当前 MTU |
destroy() |
释放插件内部资源;再次使用需重新 initBluetooth |
1.0.5 向后兼容说明
- 原有函数名、参数字段、回调参数及调用顺序全部保留,旧代码无需修改。
- Android 与 HarmonyOS 的连接/读取回调改为按设备和请求隔离,多设备使用时不再回调到最后一次连接参数。
- Android 同一设备的多次
sendData按调用顺序进入 FIFO 队列,避免串口报文乱序。 - 同一设备正在连接时再次连接会通过原有
onConnectFailed返回 10012;非法 UUID 返回 10011。 disconnectBluetooth、setMtu、getMtu现在统一接受冒号、短横线或连续 12 位 MAC 格式。destroy()仍保持原用法,但会让尚未完成的异步连接失效并关闭迟到的 socket。
多设备连接
Android 与 HarmonyOS 均支持同时连接多台经典蓝牙 RFCOMM/SPP 设备。插件使用规范化后的 MAC 地址作为连接标识,每台设备具有独立的:
- SPP Socket、连接/读取/断开回调;
- 应用层写入分片(MTU)配置;
- FIFO 写入队列,同一设备按
sendData调用顺序发送,不会串到其他设备。
分别连接两台设备:
function connectDevice(address) {
BTManager.connectBluetoothWithName({
address,
onConnected: (deviceAddress) => {
console.log('连接成功:' + deviceAddress)
},
readDataHandler: (result) => {
// 通过 result.deviceAddress 区分数据来自哪台设备。
console.log(result.deviceAddress, result.hexValue)
},
onDisconnected: (deviceAddress) => {
console.log('连接断开:' + deviceAddress)
},
onConnectFailed: (deviceAddress, code, message) => {
console.log(deviceAddress, code, message)
}
})
}
connectDevice('AA:BB:CC:DD:EE:01')
connectDevice('AA:BB:CC:DD:EE:02')
通过 deviceAddress 分别发送或断开:
BTManager.sendData({
deviceAddress: 'AA:BB:CC:DD:EE:01',
value: '01020304',
encoding: 2
})
BTManager.sendData({
deviceAddress: 'AA:BB:CC:DD:EE:02',
value: 'AABBCCDD',
encoding: 2
})
// 只断开第一台设备,第二台连接不受影响。
BTManager.disconnectBluetooth('AA:BB:CC:DD:EE:01')
使用限制:
- 外设必须支持经典蓝牙 RFCOMM/SPP;仅支持 BLE/GATT 的设备不能使用本插件连接。
- 同一个 MAC 地址只维护一条活动连接;该地址连接进行中时重复连接返回错误码 10012。
- 系统配对 UI 一次只处理一台未配对设备;建议完成第一台配对后再配对下一台。已经配对的设备可分别连接。
- 实际同时连接数量取决于手机蓝牙芯片、系统和外设能力,插件本身未设置固定数量上限。
disconnectBluetooth(address)只断开指定设备;destroy()会释放插件并断开当前全部设备。
类型与回调说明
BluetoothDeviceInfo
扫描与配对列表中的设备信息:
name:名称address:MAC 地址type:设备类型(系统BluetoothDevice.getType())alias:别名(若系统支持)
GetBoundDeviceOptions
success?(devices: BluetoothDeviceInfo[]):成功返回列表fail?(err: ApiFail):失败;未先调用initBluetooth时errCode为 90001,mes为「请先调用initBluetooth」
DiscoveryPara(扫描)
showEmptyName: boolean:是否把 名称为空 的设备也回调上来btNameFilter?: string:按名称过滤(可选)onDeviceFound(res: BluetoothDeviceInfo):必填,每发现一台设备回调一次;同一次扫描内同一设备可能出现多次,业务可自行去重onDiscoveryStarted?: () => void:扫描开始onDiscoveryFinished?: () => void:扫描结束(正常结束或内部取消后也会收到)onError?: (err: string) => void:扫描无法启动或适配器异常
ConnectBluetoothOptions(连接)
address?: string:建议使用 MAC 连接;支持AA:BB:CC:DD:EE:FF、AA-BB-...或连续 12 位十六进制(内部会规范为冒号大写等形式)uuid?: string:RFCOMM socket链路类型的服务UUID。选填默认:”00001101-0000-1000-8000-00805F9B34FB”(大量蓝牙串口模块、车载、打印机使用该 UUID;)readDataHandler?(res: ReadResultInfo):收到字节数据或读通道结束时回调onConnected?(address: string):RFCOMM 连接成功onConnectFailed?(deviceAddress, code, message):连接失败(含配对失败、Socket 连接失败等);code为字符串形式的错误码onDisconnected?(deviceAddress):断开(常见于主动disconnect或读写异常导致关闭;对端主动断开的即时性感知可按需扩展)
ReadResultInfo(读回调)
deviceAddress:设备 MACdata: number[]:原始字节(数组形式)hexValue/utf8Value/gbkValue:插件侧附加转换结果(便于调试或文本协议)state:'0'表示读到一段数据;'1'表示 读循环结束(如套接字关闭、disconnect)message:说明文案
注意:单次回调 不等于 一条完整业务报文;粘包/分包请在协议层拼接解析。
SendDataOptions(发送)
deviceAddress:目标 MAC(需已连接)data?: number[]与value?: string:若同时存在,data优先,按原始字节发送- 仅发字符串时:
encoding?: number:0= UTF-8,1= GBK,2= 十六进制字符串(可含空格;偶数位0-9A-Fa-f,解码为字节后发送)
mtuOverride?: number:可选,仅覆盖本次写入的应用层分片上限(1~65535 字节)onWriteSuccess?(deviceAddress)/onWriteFailed?(deviceAddress, code, message):写成功或失败
需要设备 ACK 时的推荐写法
onWriteSuccess 只表示数据已交给本机 SPP Socket,不表示外设已解析或执行。插件不知道不同设备的帧头、命令号和 CRC 规则,因此保留原 sendData 语义;需要可靠命令确认时,在业务协议层通过 readDataHandler 匹配应答。下面示例允许每台设备同时等待一条命令,并用接收缓冲兼容 ACK 拆包/粘包:
// key 使用 onConnected/readDataHandler 返回的规范化设备地址。
const pendingAckByDevice = new Map()
function sendAndWaitAck(deviceAddress, commandHex, expectedAckHex, timeoutMs = 3000) {
if (pendingAckByDevice.has(deviceAddress)) {
return Promise.reject(new Error('该设备仍有一条命令等待 ACK'))
}
if (expectedAckHex.replace(/\s+/g, '').length === 0) {
return Promise.reject(new Error('expectedAckHex 不能为空'))
}
return new Promise((resolve, reject) => {
const expected = expectedAckHex.replace(/\s+/g, '').toUpperCase()
const timer = setTimeout(() => {
pendingAckByDevice.delete(deviceAddress)
reject(new Error('等待设备 ACK 超时'))
}, timeoutMs)
pendingAckByDevice.set(deviceAddress, { expected, buffer: '', timer, resolve, reject })
BTManager.sendData({
deviceAddress,
value: commandHex,
encoding: 2,
// 此处不 resolve:继续等待 readDataHandler 收到真正的设备应答。
onWriteSuccess: () => {},
onWriteFailed: (_address, code, message) => {
const pending = pendingAckByDevice.get(deviceAddress)
if (pending == null) return
clearTimeout(pending.timer)
pendingAckByDevice.delete(deviceAddress)
reject(new Error(`写入失败 ${code}: ${message}`))
}
})
})
}
function handleBluetoothRead(result) {
if (result.state !== '0') return
const pending = pendingAckByDevice.get(result.deviceAddress)
if (pending == null) return
// 累积多次读取,既能匹配拆成多段的 ACK,也能从粘连报文中找到 ACK。
pending.buffer = (pending.buffer + result.hexValue).slice(-8192)
if (pending.buffer.includes(pending.expected)) {
clearTimeout(pending.timer)
pendingAckByDevice.delete(result.deviceAddress)
pending.resolve()
}
}
BTManager.connectBluetoothWithName({
address: targetAddress,
readDataHandler: handleBluetoothRead
})
实际项目应按设备协议把简单的 includes 替换为完整帧解析,例如校验帧头、长度、命令序号和 CRC。断线时还应清除该设备尚未完成的 ACK 定时器。
SetMtuPara / GetMtuPara
deviceAddress:设备 MACSetMtuPara.mtu:应用层单次写入分片上限(字节),不是 BLE ATT MTUSetMtuPara.mtuHandle(success: boolean):设置是否成功GetMtuPara.mtuHandle(mtu: number):返回当前 MTU
ApiFail
errCode: numbermes: string
推荐使用顺序
- 调用
initBluetooth(),确认权限已授予(可通过getBondList是否成功侧面验证)。 - 使用
getBondList取已配对设备,或使用discoveryBluetooth搜周边并在onDeviceFound里收集列表。 connectBluetoothWithName传入目标address,在onConnected/readDataHandler中处理连接与数据。sendData发送数据;需要时setMtu/getMtu。disconnectBluetooth(address)断开;不用蓝牙时destroy()。
权限清单
插件声明的权限以 package.json → dcloudext.declaration.permissions 为准(含定位、经典蓝牙与 Android 12+ 的扫描/连接等)。打包前请确认与业务隐私合规一致。
uniapp完整示例
<template>
<view class="content">
<view
style="font-size: 28rpx;color: cadetblue;display: flex;flex-direction: column;background-color: azure;width: 750rpx;display: flex;align-items: center;justify-content: center;">
<text>读取utf-8数据:{{blueToothValue_utf8}}</text>
<text>读取HEX数据:{{blueToothValue_hex}}</text>
<text>读取number[]数据:{{blueToothValue_number}}</text>
</view>
<view class="header">
<view class="btn" @click="boundDevices()">获取已绑定设备</view>
<view class="btn" @click="scanDevices()">扫描蓝牙</view>
<view class="btn" @click="closeBluetooth()">断开蓝牙</view>
<view class="btn" @click="sendDataAction()">发送数据</view>
</view>
<view class="header">
<view class="btn" @click="setMtuAction()">设置MTU</view>
<view class="btn" @click="getMtuAction()">获取MTU</view>
<view class="btn" @click="destroy()">释放资源</view>
</view>
<view style="width: 750rpx;display: flex;flex-direction: column;" v-if="bondDeviceList.length>0">
<view
style="width: 750rpx;height: 60rpx;background-color: bisque;display: flex;align-items: center;justify-content: center;">
<text>已配对设备</text>
</view>
<scroll-view class="bound_list" scroll-y="true">
<view class="cell" style="background-color: azure;" v-for="(item,index) in bondDeviceList"
:key="'bondDevice'+index" @click.stop="connectBluetooth(item.address)">
<text style="color:blueviolet;">名称:{{item.name}}</text>
<text style="color: blue;">地址:{{item.address}}</text>
<view class="line" style="width: 750rpx;height: 1px;background-color: rgba(0, 0, 0, 0.4);"></view>
</view>
</scroll-view>
</view>
<view style="width: 750rpx;display: flex;flex-direction: column;" v-if="discoveryList.length>0">
<view
style="width: 750rpx;height: 60rpx;background-color: bisque;display: flex;align-items: center;justify-content: center;">
<text>扫描到的设备</text>
</view>
<scroll-view class="list" scroll-y="true">
<view class="cell" style="background-color: azure;" v-for="(item,index) in discoveryList"
:key="'bondDevice'+index" @click.stop="connectBluetooth(item.address)">
<text style="color:blueviolet;">名称:{{item.name}}</text>
<text style="color: blue;">地址:{{item.address}}</text>
<view class="line" style="width: 750rpx;height: 1px;background-color: rgba(0, 0, 0, 0.4);"></view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import * as BTManager from '@/uni_modules/yt-uts-bluetooth'
export default {
data() {
return { //as BluetoothDeviceInfo[]
bondDeviceList: [], //已绑定蓝牙列表
blueToothValue_utf8: '', //蓝牙返回的数据
blueToothValue_hex: '',
blueToothValue_number: '',
discoveryList: [], //搜索周边蓝牙设备,
deviceAddress: '' //当前连接蓝牙的地址
}
},
onLoad() {
this.initBluetoothAction()
},
methods: {
initBluetoothAction() {
BTManager.initBluetooth()
},
boundDevices() {
BTManager.getBondList({
success: (res) => {
console.log(res)
this.bondDeviceList = res
},
fail: (error) => {
}
})
},
scanDevices() {
this.discoveryList = [];
BTManager.discoveryBluetooth({
showEmptyName: false,
onDeviceFound: (res) => {
this.discoveryList.push(res)
},
onDiscoveryStarted: () => {
console.log('扫描开始')
},
onDiscoveryFinished: () => {
console.log('扫描完成')
},
onError: (err) => {
console.log(err);
}
})
},
closeBluetooth() {
BTManager.disconnectBluetooth(this.deviceAddress)
},
sendDataAction() {
BTManager.sendData({
value: 'hello world',
deviceAddress: this.deviceAddress,
encoding: 1,
onWriteSuccess: (devideAdress) => {
console.log(`${devideAdress}写入成功`)
},
onWriteFailed: (deviceAddress, code, message) => {
console.log(`${deviceAddress}写入失败---code:${code}----message:${message}`);
}
})
},
connectBluetooth(address) {
BTManager.connectBluetoothWithName({
address: address,
onConnected: (address) => {
console.log(`连接成功:${address}`);
this.deviceAddress = address;
},
onConnectFailed: (address, code, mes) => {
console.log(`${address}连接失败--code:${code}---mes:${mes}`)
},
onDisconnected: (address) => {
console.log(`${address}已断开连接`)
this.deviceAddress = "";
},
readDataHandler: (result) => {
this.blueToothValue_utf8 = result.utf8Value ?? "";
this.blueToothValue_hex = result.hexValue ?? "";
this.blueToothValue_number = result.data.toString() ?? "";
}
})
},
//获取mtu
setMtuAction() {
BTManager.setMtu({
deviceAddress: this.deviceAddress,
mtu: 200,
mtuHandle: (res) => {
console.log('设置mtu:' + res)
}
})
},
getMtuAction() {
BTManager.getMtu({
deviceAddress: this.deviceAddress,
mtuHandle: (res) => {
console.log('获取mtu:' + res)
}
})
},
//释放资源
destroy() {
BTManager.destroy()
}
}
}
</script>
<style>
.content {
width: 750rpx;
display: flex;
height: 100%;
flex-direction: column;
}
.header {
width: 750rpx;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 20rpx;
box-sizing: border-box;
margin-top: 20rpx;
}
.btn {
display: flex;
height: 80rpx;
padding: 0 5rpx;
align-items: center;
justify-content: center;
background-color: cadetblue;
color: white;
margin-right: 20rpx;
font-size: 28rpx;
border-radius: 6rpx;
}
.bound_list {
width: 750rpx;
height: 300rpx;
}
.list {
width: 750rpx;
height: 600rpx;
}
.cell {
display: flex;
flex-direction: column;
justify-content: center;
padding: 10rpx;
box-sizing: border-box;
background-color: antiquewhite;
}
</style>
uniapp-x完整示例
<template>
<view class="content">
<view
style="font-size: 28rpx;color: cadetblue;height: 80rpx;background-color: azure;width: 750rpx;display: flex;align-items: center;justify-content: center;">
<text>读取数据:{{blueToothValue}}</text>
</view>
<view class="header">
<view class="btn" @click="boundDevices()">获取已绑定设备</view>
<view class="btn" @click="scanDevices()">扫描蓝牙</view>
<view class="btn" @click="closeBluetooth()">断开蓝牙</view>
<view class="btn" @click="sendDataAction()">发送数据</view>
</view>
<view class="header">
<view class="btn" @click="setMtuAction()">设置MTU</view>
<view class="btn" @click="getMtuAction()">获取MTU</view>
</view>
<view style="width: 750rpx;display: flex;flex-direction: column;" v-if="bondDeviceList.length>0">
<view
style="width: 750rpx;height: 60rpx;background-color: bisque;display: flex;align-items: center;justify-content: center;">
<text>已配对设备</text>
</view>
<scroll-view class="bound_list" scroll-y="true">
<view class="cell" style="background-color: azure;" v-for="(item,index) in bondDeviceList"
:key="'bondDevice'+index" @click.stop="connectBluetooth(item.name,item.address)">
<text style="color:blueviolet;">名称:{{item.name}}</text>
<text style="color: blue;">地址:{{item.address}}</text>
<view class="line" style="width: 750rpx;height: 1px;background-color: rgba(0, 0, 0, 0.4);"></view>
</view>
</scroll-view>
</view>
<view style="width: 750rpx;display: flex;flex-direction: column;" v-if="discoveryList.length>0">
<view
style="width: 750rpx;height: 60rpx;background-color: bisque;display: flex;align-items: center;justify-content: center;">
<text>扫描到的设备</text>
</view>
<scroll-view class="list" scroll-y="true">
<view class="cell" style="background-color: azure;" v-for="(item,index) in discoveryList"
:key="'bondDevice'+index" @click.stop="connectBluetooth(item.name,item.address)">
<text style="color:blueviolet;">名称:{{item.name}}</text>
<text style="color: blue;">地址:{{item.address}}</text>
<view class="line" style="width: 750rpx;height: 1px;background-color: rgba(0, 0, 0, 0.4);"></view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import *as BTManager from '@/uni_modules/yt-uts-bluetooth'
export default {
data() {
return {//as BluetoothDeviceInfo[]
bondDeviceList: [] as BTManager.BluetoothDeviceInfo[], //已绑定蓝牙列表
blueToothValue: '', //蓝牙返回的数据
discoveryList: [] as BTManager.BluetoothDeviceInfo[],//搜索周边蓝牙设备
deviceAddress: '' //当前连接蓝牙的地址
}
},
onLoad() {
this.initBluetoothAction()
},
methods: {
initBluetoothAction() {
BTManager.initBluetooth()
},
boundDevices() {
BTManager.getBondList({
success: (res : BTManager.BluetoothDeviceInfo[]) => {
console.log(res)
this.bondDeviceList = res
},
fail: (error : BTManager.ApiFail) => {
}
} as BTManager.GetBoundDeviceOptions)
},
scanDevices() {
this.discoveryList = [];
BTManager.discoveryBluetooth({
showEmptyName: false,
onDeviceFound: (res) => {
this.discoveryList.push(res)
},
onDiscoveryStarted: () => {
console.log('扫描开始')
},
onDiscoveryFinished: () => {
console.log('扫描完成')
},
onError: (err) => {
console.log(err);
}
} as BTManager.DiscoveryPara)
},
closeBluetooth() {
BTManager.disconnectBluetooth(this.deviceAddress)
},
sendDataAction() {
BTManager.sendData({
value: 'hello world',
deviceAddress: this.deviceAddress,
encoding: 1,
onWriteSuccess: (devideAdress : string) => {
console.log(`${devideAdress}写入成功`)
},
onWriteFailed: (deviceAddress : string, code : number, message : string) => {
console.log(`${deviceAddress}写入失败---code:${code}----message:${message}`);
}
} as BTManager.SendDataOptions)
},
connectBluetooth(deviceName : string, address : string) {
BTManager.connectBluetoothWithName({
address: address,
onConnected: (address : string) => {
console.log(`连接成功:${address}`);
this.deviceAddress = address;
},
onConnectFailed: (address : string, code : string, mes : string) => {
console.log(`${address}连接失败--code:${code}---mes:${mes}`)
},
onDisconnected: (address) => {
console.log(`${address}已断开连接`)
},
readDataHandler: (result : BTManager.ReadResultInfo) => {
// console.log(result.utf8Value ?? "")
this.blueToothValue = result.utf8Value ?? "";
}
} as BTManager.ConnectBluetoothOptions)
},
//获取mtu
setMtuAction() {
BTManager.setMtu({
deviceAddress: this.deviceAddress,
mtu: 100,
mtuHandle: (res) => {
console.log('设置mtu:' + res)
}
} as BTManager.SetMtuPara)
},
getMtuAction() {
BTManager.getMtu({
deviceAddress: this.deviceAddress,
mtuHandle: (res) => {
console.log('获取mtu:' + res)
}
} as BTManager.GetMtuPara)
}
}
}
</script>
<style>
.content {
width: 750rpx;
display: flex;
height: 100%;
flex-direction: column;
}
.header {
width: 750rpx;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 20rpx;
box-sizing: border-box;
margin-top: 20rpx;
}
.btn {
display: flex;
height: 80rpx;
padding: 0 5rpx;
align-items: center;
justify-content: center;
background-color: cadetblue;
color: white;
margin-right: 20rpx;
font-size: 28rpx;
border-radius: 6rpx;
}
.bound_list {
width: 750rpx;
height: 300rpx;
}
.list {
width: 750rpx;
height: 600rpx;
}
.cell {
display: flex;
flex-direction: column;
justify-content: center;
padding: 10rpx;
box-sizing: border-box;
background-color: antiquewhite;
}
</style>

收藏人数:
购买源码授权版(
试用
赞赏(5)
下载 620
赞赏 15
下载 12617222
赞赏 1949
赞赏
京公网安备:11010802035340号