【干货满满】H5+APP 之 蓝牙连接

初始化一些参数

const BluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter') as any
const Intent = plus.android.importClass('android.content.Intent') as any
const IntentFilter = plus.android.importClass('android.content.IntentFilter') as any
const BluetoothDevice = plus.android.importClass('android.bluetooth.BluetoothDevice') as any
const UUID = plus.android.importClass('java.util.UUID') as any
const Toast = plus.android.importClass('android.widget.Toast') as any
//连接串口设备的 UUID
const MY_UUID = UUID.fromString('00001101-0000-1000-8000-00805F9B34FB')

const invoke = plus.android.invoke
const btAdapter = BluetoothAdapter.getDefaultAdapter()
const activity = plus.android.runtimeMainActivity() as any

let btSocket: any = null
let btInStream: any = null
let btOutStream: any = null
let setIntervalId = 0

let btFindReceiver: any = null //蓝牙搜索广播接收器
let btStatusReceiver: any = null //蓝牙状态监听广播
interface BlueToothToolStateType {
    bluetoothEnable: boolean
    bluetoothState: string
    discoveryDeviceState: boolean
    readThreadState: boolean
}
interface BlueToothTooloptionsType {
    listenBTStatusCallback: (arg?: any) => void
    discoveryDeviceCallback: (arg?: any) => void
    discoveryFinishedCallback: () => void
    readDataCallback: (arg?: any) => void
    connExceptionCallback: (arg?: any) => void
}

权限文件

/**
 * 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
 */

// #ifdef APP-PLUS
const isIos = plus.os.name == 'iOS'
// #endif

// 判断推送权限是否开启
function judgeIosPermissionPush() {
    let result = false
    const UIApplication = (plus.ios as any).import('UIApplication')
    const app = UIApplication.sharedApplication()
    let enabledTypes = 0
    if (app.currentUserNotificationSettings) {
        const settings = app.currentUserNotificationSettings()
        enabledTypes = settings.plusGetAttribute('types')
        console.log('enabledTypes1:' + enabledTypes)
        if (enabledTypes == 0) {
            console.log('推送权限没有开启')
        } else {
            result = true
            console.log('已经开启推送功能!')
        }
        plus.ios.deleteObject(settings)
    } else {
        enabledTypes = app.enabledRemoteNotificationTypes()
        if (enabledTypes == 0) {
            console.log('推送权限没有开启!')
        } else {
            result = true
            console.log('已经开启推送功能!')
        }
        console.log('enabledTypes2:' + enabledTypes)
    }
    plus.ios.deleteObject(app)
    plus.ios.deleteObject(UIApplication)
    return result
}

// 判断定位权限是否开启
function judgeIosPermissionLocation() {
    let result = false
    const cllocationManger = (plus.ios as any).import('CLLocationManager')
    const status = cllocationManger.authorizationStatus()
    result = status != 2
    console.log('定位权限开启:' + result)
    // 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
    /* const enable = cllocationManger.locationServicesEnabled();
	const status = cllocationManger.authorizationStatus();
	console.log("enable:" + enable);
	console.log("status:" + status);
	if (enable && status != 2) {
		result = true;
		console.log("手机定位服务已开启且已授予定位权限");
	} else {
		console.log("手机系统的定位没有打开或未给予定位权限");
	} */
    plus.ios.deleteObject(cllocationManger)
    return result
}

// 判断麦克风权限是否开启
function judgeIosPermissionRecord() {
    let result = false
    const avaudiosession = (plus.ios as any).import('AVAudioSession')
    const avaudio = avaudiosession.sharedInstance()
    const permissionStatus = avaudio.recordPermission()
    console.log('permissionStatus:' + permissionStatus)
    if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
        console.log('麦克风权限没有开启')
    } else {
        result = true
        console.log('麦克风权限已经开启')
    }
    plus.ios.deleteObject(avaudiosession)
    return result
}

// 判断相机权限是否开启
function judgeIosPermissionCamera() {
    let result = false
    const AVCaptureDevice = (plus.ios as any).import('AVCaptureDevice')
    const authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide')
    console.log('authStatus:' + authStatus)
    if (authStatus == 3) {
        result = true
        console.log('相机权限已经开启')
    } else {
        console.log('相机权限没有开启')
    }
    plus.ios.deleteObject(AVCaptureDevice)
    return result
}

// 判断相册权限是否开启
function judgeIosPermissionPhotoLibrary() {
    let result = false
    const PHPhotoLibrary = (plus.ios as any).import('PHPhotoLibrary')
    const authStatus = PHPhotoLibrary.authorizationStatus()
    console.log('authStatus:' + authStatus)
    if (authStatus == 3) {
        result = true
        console.log('相册权限已经开启')
    } else {
        console.log('相册权限没有开启')
    }
    plus.ios.deleteObject(PHPhotoLibrary)
    return result
}

// 判断通讯录权限是否开启
function judgeIosPermissionContact() {
    let result = false
    const CNContactStore = (plus.ios as any).import('CNContactStore')
    const cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0)
    if (cnAuthStatus == 3) {
        result = true
        console.log('通讯录权限已经开启')
    } else {
        console.log('通讯录权限没有开启')
    }
    plus.ios.deleteObject(CNContactStore)
    return result
}

// 判断日历权限是否开启
function judgeIosPermissionCalendar() {
    let result = false
    const EKEventStore = (plus.ios as any).import('EKEventStore')
    const ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0)
    if (ekAuthStatus == 3) {
        result = true
        console.log('日历权限已经开启')
    } else {
        console.log('日历权限没有开启')
    }
    plus.ios.deleteObject(EKEventStore)
    return result
}

// 判断备忘录权限是否开启
function judgeIosPermissionMemo() {
    let result = false
    const EKEventStore = (plus.ios as any).import('EKEventStore')
    const ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1)
    if (ekAuthStatus == 3) {
        result = true
        console.log('备忘录权限已经开启')
    } else {
        console.log('备忘录权限没有开启')
    }
    plus.ios.deleteObject(EKEventStore)
    return result
}
type AndroidPermissionIDType =
    | 'android.permission.ACCESS_FINE_LOCATION' //位置权限
    | 'android.permission.ACCESS_COARSE_LOCATION' //模糊位置权限(蓝牙\ble依赖)
    | 'android.permission.CAMERA' //摄像头权限
    | 'android.permission.READ_EXTERNAL_STORAGE' //外部存储(含相册)读取权限
    | 'android.permission.WRITE_EXTERNAL_STORAGE' //外部存储(含相册)写入权限
    | 'android.permission.RECORD_AUDIO	' //麦克风权限
    | 'android.permission.READ_CONTACTS' //通讯录读取权限
    | 'android.permission.WRITE_CONTACTS' //通讯录写入权限
    | 'android.permission.READ_CALENDAR' //日历读取权限
    | 'android.permission.WRITE_CALENDAR' //日历写入权限
    | 'android.permission.READ_SMS' //短信读取权限
    | 'android.permission.SEND_SMS' //短信发送权限
    | 'android.permission.RECEIVE_SMS' //接收新短信权限
    | 'android.permission.READ_PHONE_STATE' //获取手机识别码等信息的权限
    | 'android.permission.CALL_PHONE' //拨打电话权限
    | 'android.permission.READ_CALL_LOG' //获取通话记录权限
// Android权限查询
function requestAndroidPermission(permissionID: AndroidPermissionIDType) {
    return new Promise((resolve) => {
        plus.android.requestPermissions(
            [permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
            function (resultObj) {
                let result = 0
                for (let i = 0; i < resultObj.granted.length; i++) {
                    const grantedPermission = resultObj.granted[i]
                    console.log('已获取的权限:' + grantedPermission)
                    result = 1
                }
                for (let i = 0; i < resultObj.deniedPresent.length; i++) {
                    const deniedPresentPermission = resultObj.deniedPresent[i]
                    console.log('拒绝本次申请的权限:' + deniedPresentPermission)
                    result = 0
                }
                for (let i = 0; i < resultObj.deniedAlways.length; i++) {
                    const deniedAlwaysPermission = resultObj.deniedAlways[i]
                    console.log('永久拒绝申请的权限:' + deniedAlwaysPermission)
                    result = -1
                }
                resolve(result)
                // 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
                // if (result != 1) {
                // gotoAppPermissionSetting()
                // }
            },
            function (error) {
                console.log('申请权限错误:' + error.code + ' = ' + error.message)
                resolve({
                    code: error.code,
                    message: error.message,
                })
            },
        )
    })
}
type IosPermissionIDType = 'location' | 'push' | 'camera' | 'photoLibrary' | 'record' | 'contact' | 'calendar' | 'memo'
// 使用一个方法,根据参数判断权限
function judgeIosPermission(permissionID: IosPermissionIDType) {
    if (permissionID == 'location') {
        return judgeIosPermissionLocation()
    } else if (permissionID == 'camera') {
        return judgeIosPermissionCamera()
    } else if (permissionID == 'photoLibrary') {
        return judgeIosPermissionPhotoLibrary()
    } else if (permissionID == 'record') {
        return judgeIosPermissionRecord()
    } else if (permissionID == 'push') {
        return judgeIosPermissionPush()
    } else if (permissionID == 'contact') {
        return judgeIosPermissionContact()
    } else if (permissionID == 'calendar') {
        return judgeIosPermissionCalendar()
    } else if (permissionID == 'memo') {
        return judgeIosPermissionMemo()
    }
    return false
}

// 跳转到**应用**的权限页面
function gotoAppPermissionSetting() {
    if (isIos) {
        const UIApplication = (plus.ios as any).import('UIApplication')
        const application2 = UIApplication.sharedApplication()
        const NSURL2 = (plus.ios as any).import('NSURL')
        // const setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
        const setting2 = NSURL2.URLWithString('app-settings:')
        application2.openURL(setting2)

        plus.ios.deleteObject(setting2)
        plus.ios.deleteObject(NSURL2)
        plus.ios.deleteObject(application2)
    } else {
        // console.log(plus.device.vendor);
        const Intent = plus.android.importClass('android.content.Intent') as any
        const Settings = plus.android.importClass('android.provider.Settings') as any
        const Uri = plus.android.importClass('android.net.Uri') as any
        const mainActivity = plus.android.runtimeMainActivity() as any
        const intent = new Intent()
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
        const uri = Uri.fromParts('package', mainActivity.getPackageName(), null)
        intent.setData(uri)
        mainActivity.startActivity(intent)
    }
}

// 检查系统的设备服务是否开启
// const checkSystemEnableLocation = async function () {
function checkSystemEnableLocation() {
    if (isIos) {
        let result = false
        const cllocationManger = (plus.ios as any).import('CLLocationManager')
        result = cllocationManger.locationServicesEnabled() as unknown as boolean
        console.log('系统定位开启:' + result)
        plus.ios.deleteObject(cllocationManger)
        return result
    } else {
        const context = plus.android.importClass('android.content.Context') as any
        const locationManager = plus.android.importClass('android.location.LocationManager') as any
        const main = plus.android.runtimeMainActivity() as any
        const mainSvr = main.getSystemService(context.LOCATION_SERVICE)
        const result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER)
        console.log('系统定位开启:' + result)
        return result
    }
}

export default {
    judgeIosPermission: judgeIosPermission,
    requestAndroidPermission: requestAndroidPermission,
    checkSystemEnableLocation: checkSystemEnableLocation,
    gotoAppPermissionSetting: gotoAppPermissionSetting,
}

构建构造函数

import Perssimmon from './permission'
export class BlueToothTool {
    private state: BlueToothToolStateType = {
        bluetoothEnable: false, //蓝牙是否开启
        bluetoothState: '', //当前蓝牙状态
        discoveryDeviceState: false, //是否正在搜索蓝牙设备
        readThreadState: false, //蓝牙设备名称
    }
    private options: BlueToothTooloptionsType = {
        /**
         * 监听蓝牙状态回调
         * @param {String} state
         */
        listenBTStatusCallback: function () {},
        /**
         * 搜索到新的蓝牙设备回调
         * @param {Device} newDevice
         */
        discoveryDeviceCallback: function () {},
        /**
         * 蓝牙搜索完成回调
         */
        discoveryFinishedCallback: function () {},
        /**
         * 接收到数据回调
         * @param {Array} dataByteArr
         */
        readDataCallback: function () {},
        /**
         * 蓝牙连接中断回调
         * @param {Exception} e
         */
        connExceptionCallback: function () {},
    }

    constructor() {}

    Init(setOptions: any) {
        Object.assign(this.options, setOptions)
        this.state.bluetoothEnable = this.GetBluetoothStatus()
        this.ListenBluetoothStatus()
    }
    ShortToast(msg: string) {
        Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
    }
    /**
     * 是否支持蓝牙
     * @return {boolean}
     */
    IsSupportBluetooth() {
        if (btAdapter != null) {
            return true
        }
        return false
    }
    /**
     * 获取蓝牙的状态
     * @return {boolean} 是否已开启
     */
    GetBluetoothStatus() {
        if (btAdapter != null) {
            return btAdapter.isEnabled()
        }
        return false
    }
    /**
     * 断开连接设备
     * @param {Object} address
     * @return {Boolean}
     */
    CloseBtSocket() {
        this.state.readThreadState = false
        if (!btSocket) {
            return
        }
        try {
            btSocket.close()
        } catch (e) {
            console.error(e)
            btSocket = null
        }
    }
    /**
     * 取消发现
     */
    CancelDiscovery() {
        if (btAdapter.isDiscovering()) {
            btAdapter.cancelDiscovery()
        }
        if (btFindReceiver != null) {
            activity.unregisterReceiver(btFindReceiver)
            btFindReceiver = null
        }
        this.state.discoveryDeviceState = false
    }
    /**
     * 打开蓝牙
     * @param activity
     * @param requestCode
     */
    TurnOnBluetooth() {
        if (btAdapter == null) {
            this.ShortToast('没有蓝牙')
            return
        }
        if (!btAdapter.isEnabled()) {
            if (activity == null) {
                this.ShortToast('未获取到activity')
                return
            } else {
                const intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
                const requestCode = 1
                activity.startActivityForResult(intent, requestCode)
                return
            }
        } else {
            this.ShortToast('蓝牙已经打开')
        }
    }
    /**
     * 关闭蓝牙
     */
    TurnOffBluetooth() {
        if (btAdapter != null && btAdapter.isEnabled()) {
            btAdapter.disable()
        }
        if (btFindReceiver != null) {
            try {
                activity.unregisterReceiver(btFindReceiver)
            } catch (e) {
                console.log('TurnOffBluetooth - Err', e)
            }
            btFindReceiver = null
        }
        this.state.bluetoothEnable = false
        this.CancelDiscovery()
        this.CloseBtSocket()

        if (btAdapter != null && btAdapter.isEnabled()) {
            btAdapter.disable()
            this.ShortToast('蓝牙关闭成功')
        } else {
            this.ShortToast('蓝牙已经关闭')
        }
    }
    /**
     * 获取已经配对的设备
     * @return {Array} connetedDevices
     */
    GetPairedDevices() {
        const pairedDevices: any[] = []

        //蓝牙连接android原生对象,是一个set集合
        let pairedDevicesAndroid = null
        if (btAdapter != null && btAdapter.isEnabled()) {
            pairedDevicesAndroid = btAdapter.getBondedDevices()
        } else {
            this.ShortToast('蓝牙未开启')
        }

        if (!pairedDevicesAndroid) {
            return pairedDevices
        }

        //遍历连接设备的set集合,转换为js数组
        const it = invoke(pairedDevicesAndroid, 'iterator')
        while (invoke(it, 'hasNext')) {
            const device = invoke(it, 'next')
            pairedDevices.push({
                name: invoke(device, 'getName'),
                address: invoke(device, 'getAddress'),
            })
        }
        return pairedDevices
    }
    /**
     * 发现设备
     */
    DiscoveryNewDevice() {
        if (btFindReceiver != null) {
            try {
                activity.unregisterReceiver(btFindReceiver)
            } catch (e) {
                console.error(e)
            }
            btFindReceiver = null
            this.CancelDiscovery()
        }
        const Build = plus.android.importClass('android.os.Build') as any

        //6.0以后的如果需要利用本机查找周围的wifi和蓝牙设备, 申请权限
        if (Build.VERSION.SDK_INT >= 6.0) {
            await Perssimmon.requestAndroidPermission('android.permission.ACCESS_FINE_LOCATION')
            await Perssimmon.requestAndroidPermission('android.permission.ACCESS_COARSE_LOCATION')
            console.log('6.0以后的如果需要利用本机查找周围的wifi和蓝牙设备, 申请权限')
        }
        const options = this.options
        btFindReceiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
            onReceive: function (context: any, intent: any) {
                plus.android.importClass(context)
                plus.android.importClass(intent)
                const action = intent.getAction()

                if (BluetoothDevice.ACTION_FOUND == action) {
                    // 找到设备
                    const device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                    const newDevice = {
                        name: plus.android.invoke(device, 'getName'),
                        address: plus.android.invoke(device, 'getAddress'),
                    }
                    options.discoveryDeviceCallback && options.discoveryDeviceCallback(newDevice)
                }
                if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) {
                    // 搜索完成
                    this.CancelDiscovery()
                    options.discoveryFinishedCallback && options.discoveryFinishedCallback()
                }
            },
        })
        const filter = new IntentFilter()
        filter.addAction(BluetoothDevice.ACTION_FOUND)
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
        activity.registerReceiver(btFindReceiver, filter)
        btAdapter.startDiscovery() //开启搜索
        this.state.discoveryDeviceState = true
    }
    /**
     * 蓝牙状态监听
     * @param {Activity} activity
     */
    ListenBluetoothStatus() {
        if (btStatusReceiver != null) {
            try {
                activity.unregisterReceiver(btStatusReceiver)
            } catch (e) {
                console.error(e)
            }
            btStatusReceiver = null
        }
        btStatusReceiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
            onReceive: (context: any, intent: any) => {
                plus.android.importClass(context)
                plus.android.importClass(intent)

                const action = intent.getAction()
                const blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)
                let stateStr = ''
                switch (action) {
                    case BluetoothAdapter.ACTION_STATE_CHANGED:
                        switch (blueState) {
                            case BluetoothAdapter.STATE_TURNING_ON:
                                stateStr = 'STATE_TURNING_ON'
                                break
                            case BluetoothAdapter.STATE_ON:
                                this.state.bluetoothEnable = true
                                stateStr = 'STATE_ON'
                                break
                            case BluetoothAdapter.STATE_TURNING_OFF:
                                stateStr = 'STATE_TURNING_OFF'
                                break
                            case BluetoothAdapter.STATE_OFF:
                                stateStr = 'STATE_OFF'
                                this.state.bluetoothEnable = false
                                break
                        }
                        this.state.bluetoothState = stateStr
                        this.options.listenBTStatusCallback && this.options.listenBTStatusCallback(stateStr)
                        break
                }
            },
        })
        const filter = new IntentFilter()
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
        activity.registerReceiver(btStatusReceiver, filter)
        // 首次连接 状态回调
        if (this.state.bluetoothEnable) {
            this.options.listenBTStatusCallback && this.options.listenBTStatusCallback('STATE_ON')
        }
    }
    /**
     * 根据蓝牙地址,连接设备
     * @param {Stirng} address
     * @return {Boolean}
     */
    ConnDevice(address: any, callback: (arg: boolean) => void) {
        plus.android.importClass('java.io.InputStream')
        plus.android.importClass('java.io.OutputStream')
        plus.android.importClass('android.bluetooth.BluetoothSocket')

        this.CancelDiscovery()
        if (btSocket != null) {
            this.CloseBtSocket()
        }
        this.state.readThreadState = false

        try {
            const device = invoke(btAdapter, 'getRemoteDevice', address)
            btSocket = invoke(device, 'createRfcommSocketToServiceRecord', MY_UUID)
        } catch (e) {
            console.error(e)
            this.ShortToast('连接失败,获取Socket失败!')
            callback(false)
            return false
        }
        try {
            invoke(btSocket, 'connect')
            this.ReadData() //读数据
            this.ShortToast('连接成功')
            callback(true)
        } catch (e) {
            console.error(e)
            this.ShortToast('连接失败')
            callback(false)
            try {
                btSocket.close()
                btSocket = null
            } catch (e1) {
                console.error(e1)
            }
            return false
        }
        return true
    }
    /**
     * 读取数据
     * @param {Object} activity
     * @param {Function} callback
     * @return {Boolean}
     */
    ReadData() {
        if (!btSocket) {
            this.ShortToast('请先连接蓝牙设备!')
            return false
        }
        try {
            btInStream = invoke(btSocket, 'getInputStream')
            btOutStream = invoke(btSocket, 'getOutputStream')
            console.log('ReadData - invoke', btSocket)
        } catch (e) {
            console.error(e)
            this.ShortToast('创建输入输出流失败!')
            this.CloseBtSocket()
            return false
        }
        this.Read()
        this.state.readThreadState = true
        return true
    }
    /**
     * 模拟java多线程读取数据
     */
    Read() {
        // console.log('Read - 模拟java多线程读取数据', btOutStream)
        let setTimeCount = 0
        clearInterval(setIntervalId)
        setIntervalId = setInterval(() => {
            setTimeCount++
            if (this.state.readThreadState) {
                const t = new Date().getTime()
                //心跳检测
                if (setTimeCount % 20 == 0) {
                    try {
                        btOutStream.write([0b00]) //这里报错
                    } catch (e) {
                        this.state.readThreadState = false
                        this.options.connExceptionCallback && this.options.connExceptionCallback(e)
                    }
                }
                const dataArr = []
                while (invoke(btInStream, 'available') !== 0) {
                    const data = invoke(btInStream, 'read')
                    dataArr.push(data)
                    const ct = new Date().getTime()
                    if (ct - t > 20) {
                        break
                    }
                }
                if (dataArr.length > 0) {
                    this.options.readDataCallback && this.options.readDataCallback(dataArr)
                }
            }
        }, 40)
    }
    /**
     * 断开连接设备
     * @param {Object} address
     * @return {Boolean}
     */
    DisConnDevice() {
        if (btSocket != null) {
            this.CloseBtSocket()
        }
        this.state.readThreadState = false
        this.ShortToast('断开连接成功')
    }
    /**
     * 发送数据
     * @param {String} dataStr
     * @return {Boolean}
     */
    SendData(dataStr: string): boolean {
        if (!btOutStream) {
            this.ShortToast('创建输出流失败!')
            return false
        }
        const bytes = invoke(dataStr as any, 'getBytes', 'gbk')
        try {
            btOutStream.write(bytes)
        } catch (e) {
            return false
        }
        return true
    }
    SendByteData(byteData: any) {
        if (!btOutStream) {
            this.ShortToast('创建输出流失败!')
            return
        }
        try {
            btOutStream.write(byteData)
        } catch (e) {
            return false
        }
        return true
    }
}

使用嘛,就直接引入实例化就可以啦,不过由于蓝牙一直开着会很耗电,影响性能,如果连接上了,记得关闭蓝牙搜索功能

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值