HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

uniCloud中schema角色权限配置没有生效的原因

权限

现象
按照文档在admin项目中创建了权限和角色,正确分配给了用户,也正确配置了schema,但是测试过程中发现总是报没有权限。
原因
在文档的角落里发现了一句话:“uni-id将用户的角色权限缓存在token内。”,我顿时内心一万匹马儿跑过。在客户端增加了一个刷新token,果然可以。

继续阅读 »

现象
按照文档在admin项目中创建了权限和角色,正确分配给了用户,也正确配置了schema,但是测试过程中发现总是报没有权限。
原因
在文档的角落里发现了一句话:“uni-id将用户的角色权限缓存在token内。”,我顿时内心一万匹马儿跑过。在客户端增加了一个刷新token,果然可以。

收起阅读 »

上架 Google Play 被拒,提示“包含用于从 Google Play 之外的未知来源下载或安装应用程序的代码”的解决方案

GooglePlay下架 GooglePlay

使用HBuilderX 3.8.7以上版本打包上架google play的应用是正常的不受影响。

根据目前统计到的信息,审核被拒一般是以下两种情况:

  • 审核被拒的版本号为较老版本号,并不是本次升级对应的版本号。
  • 打包使用的HBuilderX的为3.8.7以前的版本。

解决方案:

  1. 升级HBuilderX到最新版并重新打包上架。
  2. 将所有发布过版本的轨道都用最新的HBuilderX重新打包上架,具体步骤参考下图(截图来自于google play邮件,具体可以参考Google Play审核邮件完整内容)。

    注意:发版时在预览并确认页面将分阶段发布百分比填100%,适用的国家/地区选择发布到所有目标国家/地区。确保所有用户都能升级到最新版。参考下图。

    如果按照上述方式上架仍然被拒。可以选择申诉。

申诉方式:点击政策状态->“设备和网络滥用”政策:违反“设备和网络滥用”政策,点击最下方申诉按钮,等待申诉结果。

<!--目前接收到的结果分三类:

  1. 申诉通过。
  2. 申诉失败,但是过段时间刷新google play后台显示通过。
  3. 申诉失败,重新提交上架审核,上架成功。

申诉结果可能会失败,但是目前所有的应用后续的上架审核都成功了。-->

继续阅读 »

使用HBuilderX 3.8.7以上版本打包上架google play的应用是正常的不受影响。

根据目前统计到的信息,审核被拒一般是以下两种情况:

  • 审核被拒的版本号为较老版本号,并不是本次升级对应的版本号。
  • 打包使用的HBuilderX的为3.8.7以前的版本。

解决方案:

  1. 升级HBuilderX到最新版并重新打包上架。
  2. 将所有发布过版本的轨道都用最新的HBuilderX重新打包上架,具体步骤参考下图(截图来自于google play邮件,具体可以参考Google Play审核邮件完整内容)。

    注意:发版时在预览并确认页面将分阶段发布百分比填100%,适用的国家/地区选择发布到所有目标国家/地区。确保所有用户都能升级到最新版。参考下图。

    如果按照上述方式上架仍然被拒。可以选择申诉。

申诉方式:点击政策状态->“设备和网络滥用”政策:违反“设备和网络滥用”政策,点击最下方申诉按钮,等待申诉结果。

<!--目前接收到的结果分三类:

  1. 申诉通过。
  2. 申诉失败,但是过段时间刷新google play后台显示通过。
  3. 申诉失败,重新提交上架审核,上架成功。

申诉结果可能会失败,但是目前所有的应用后续的上架审核都成功了。-->

收起阅读 »

华为审核申请权限告知目的

华为 上架被拒

华为app上架审核,申请权限的时候只有弹框,没有告知目的,被审核驳回了。。。
然后看官方,发现plus.android.requestPermissions只有点击才有回调啊,然后看到一个大佬代码设置了一个初始值,缝缝补补,整出来一个将就使用的完整代码。合不合适看最后压缩包视频就知道啦

//这是vuex单独一个模块的完整代码  
const state = {  
    WRITE_EXTERNAL_STORAGE: false,  
    READ_EXTERNAL_STORAGE: false,  
    CALL_PHONE: false,  
    isIos: plus.os.name == "iOS",  
    mapping: {  
        'WRITE_EXTERNAL_STORAGE': {  
            title: "存储空间/照片权限说明",  
            content: "便于您使用该功能上传您的照片/图片/视频及用于更换头像、发布评论/分享、下载、与客服沟通等场景中读取和写入相册和文件内容。",  
            methods: 'SET_WRITE_EXTERNAL_STORAGE'  
        },  
        'READ_EXTERNAL_STORAGE': {  
            title: "存储空间/照片权限说明",  
            content: "便于您使用该功能上传您的照片/图片/视频及用于更换头像、发布评论/分享、下载、与客服沟通等场景中读取和写入相册和文件内容。",  
            methods: 'SET_READ_EXTERNAL_STORAGE'  
        },  
        'CALL_PHONE': {  
            title: "拨打/管理电话权限说明",  
            content: "便于您使用该功能联系商家或者商家与您联系等场景",  
            methods: 'SET_CALL_PHONE'  
        }  
    }  
}  
const mutations = {  
    SET_WRITE_EXTERNAL_STORAGE(state, val) {  
        state.WRITE_EXTERNAL_STORAGE = val  
    },  
    SET_CALL_PHONE(state, val) {  
        state.CALL_PHONE = val  
    },  
    SET_READ_EXTERNAL_STORAGE(state, val) {  
        state.READ_EXTERNAL_STORAGE = val  
    }  
}  
const actions = {  
    //权限获取  
    async requestPermissions({  
        state,  
        dispatch,  
        commit  
    }, permissionID) {  
        try {  
            if (!state[permissionID]) {  
                var viewObj = await dispatch('nativeObjView', permissionID);  
                viewObj.show();  
            }  
            console.log('android.permission.' + permissionID, '当前手机权限');  
            return new Promise(async (resolve, reject) => {  
                // Android权限查询  
                function requestAndroidPermission(permissionID_) {  
                    return new Promise((resolve, reject) => {  
                        plus.android.requestPermissions(  
                            [  
                                permissionID_  
                            ], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装  
                            function (resultObj) {  
                                var result = 0;  
                                for (var i = 0; i < resultObj.granted.length; i++) {  
                                    var grantedPermission = resultObj.granted[i];  
                                    console.log('已获取的权限:' + grantedPermission);  
                                    result = 1  
                                }  
                                for (var i = 0; i < resultObj.deniedPresent  
                                    .length; i++) {  
                                    var deniedPresentPermission = resultObj  
                                        .deniedPresent[  
                                        i];  
                                    console.log('拒绝本次申请的权限:' + deniedPresentPermission);  
                                    result = 0  
                                }  
                                for (var i = 0; i < resultObj.deniedAlways  
                                    .length; i++) {  
                                    var deniedAlwaysPermission = resultObj.deniedAlways[  
                                        i];  
                                    console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);  
                                    result = -1  
                                }  
                                resolve(result);  
                            },  
                            function (error) {  
                                console.log('申请权限错误:' + error.code + " = " + error  
                                    .message);  
                                resolve({  
                                    code: error.code,  
                                    message: error.message  
                                });  
                            }  
                        );  
                    });  
                }  

                const result = await requestAndroidPermission(  
                    'android.permission.' + permissionID  
                );  
                if (result === 1) {  
                    //'已获得授权'  
                    commit(state.mapping[permissionID].methods, true)  
                } else if (result === 0) {  
                    //'未获得授权'  
                    commit(state.mapping[permissionID].methods, false)  
                } else {  
                    commit(state.mapping[permissionID].methods, true)  
                    uni.showModal({  
                        title: '提示',  
                        content: '操作权限已被拒绝,请手动前往设置',  
                        confirmText: "立即设置",  
                        success: (res) => {  
                            if (res.confirm) {  
                                dispatch('gotoAppPermissionSetting')  
                            }  
                        }  
                    })  
                }  
                if (viewObj) viewObj.close()  
                resolve(result);  
            });  
        } catch (error) {  
            console.log(error);  
            reject(error);  
        }  
    },  
    //提示框  
    nativeObjView({  
        state  
    }, permissionID) {  
        const systemInfo = uni.getSystemInfoSync();  
        const statusBarHeight = systemInfo.statusBarHeight;  
        const navigationBarHeight = systemInfo.platform === 'android' ? 48 :  
            44; // Set the navigation bar height based on the platform  
        const totalHeight = statusBarHeight + navigationBarHeight;  
        let view = new plus.nativeObj.View('per-modal', {  
            top: '0px',  
            left: '0px',  
            width: '100%',  
            backgroundColor: '#444',  
            //opacity: '.9'   
        })  
        view.drawRect({  
            color: '#fff',  
            radius: '5px'  
        }, {  
            top: totalHeight + 'px',  
            left: '5%',  
            width: '90%',  
            height: "100px",  
        })  
        view.drawText(state.mapping[permissionID].title, {  
            top: totalHeight + 5 + 'px',  
            left: "8%",  
            height: "30px"  
        }, {  
            align: "left",  
            color: "#000",  
        }, {  
            onClick: function (e) {  
                console.log(e);  
            }  
        })  
        view.drawText(state.mapping[permissionID].content, {  
            top: totalHeight + 35 + 'px',  
            height: "60px",  
            left: "8%",  
            width: "84%"  
        }, {  
            whiteSpace: 'normal',  
            size: "14px",  
            align: "left",  
            color: "#656563"  
        })  

        function show() {  
            view = plus.nativeObj.View.getViewById('per-modal');  
            view.show()  
            view = null  //这里要为null,不然隐藏不了,不知道为啥  
        }  

        function close() {  
            view = plus.nativeObj.View.getViewById('per-modal');  
            view.close();  
            view = null  
        }  
        return {  
            show,  
            close  
        }  
    },  

    // 跳转到**应用**的权限页面  
    gotoAppPermissionSetting({  
        state  
    }) {  
        if (state.isIos) {  
            var UIApplication = plus.ios.import("UIApplication");  
            var application2 = UIApplication.sharedApplication();  
            var NSURL2 = plus.ios.import("NSURL");  
            // var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");       
            var 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);  
            var Intent = plus.android.importClass("android.content.Intent");  
            var Settings = plus.android.importClass("android.provider.Settings");  
            var Uri = plus.android.importClass("android.net.Uri");  
            var mainActivity = plus.android.runtimeMainActivity();  
            var intent = new Intent();  
            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);  
            var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);  
            intent.setData(uri);  
            mainActivity.startActivity(intent);  
        }  
    }  
}  
export default {  
    namespaced: true,  
    state,  
    mutations,  
    actions  
};  

我是定义vuex里面的一个模块上面完整代码。
然后在原来的基础上加上这个判断就好啦。
<script>
export default {
data() {
return {
}
},
methods: {
async save() {
let result = await this.$store.dispatch("app/requestPermissions",
'WRITE_EXTERNAL_STORAGE')
if (result !== 1) return
uni.saveImageToPhotosAlbum({
filePath: "https://shop.baihuitimes.com/uploads/def/20230913/3fcaeccc6290e1b8367c00ea0aa600e4.png",
success: function(res) {
uni.showToast({
title: '保存成功',
icon:"success"
});
},
fail: function(res) {
uni.showToast({
title: '保存失败',
icon:"fail"
});
},
})
}
}
}
</script>

最后说明,如果用户没有点击授权的操作,将会无法及时拿到权限回调,可以自己在show里面再改改。上架审核人员前期点点点影响不到的。

继续阅读 »

华为app上架审核,申请权限的时候只有弹框,没有告知目的,被审核驳回了。。。
然后看官方,发现plus.android.requestPermissions只有点击才有回调啊,然后看到一个大佬代码设置了一个初始值,缝缝补补,整出来一个将就使用的完整代码。合不合适看最后压缩包视频就知道啦

//这是vuex单独一个模块的完整代码  
const state = {  
    WRITE_EXTERNAL_STORAGE: false,  
    READ_EXTERNAL_STORAGE: false,  
    CALL_PHONE: false,  
    isIos: plus.os.name == "iOS",  
    mapping: {  
        'WRITE_EXTERNAL_STORAGE': {  
            title: "存储空间/照片权限说明",  
            content: "便于您使用该功能上传您的照片/图片/视频及用于更换头像、发布评论/分享、下载、与客服沟通等场景中读取和写入相册和文件内容。",  
            methods: 'SET_WRITE_EXTERNAL_STORAGE'  
        },  
        'READ_EXTERNAL_STORAGE': {  
            title: "存储空间/照片权限说明",  
            content: "便于您使用该功能上传您的照片/图片/视频及用于更换头像、发布评论/分享、下载、与客服沟通等场景中读取和写入相册和文件内容。",  
            methods: 'SET_READ_EXTERNAL_STORAGE'  
        },  
        'CALL_PHONE': {  
            title: "拨打/管理电话权限说明",  
            content: "便于您使用该功能联系商家或者商家与您联系等场景",  
            methods: 'SET_CALL_PHONE'  
        }  
    }  
}  
const mutations = {  
    SET_WRITE_EXTERNAL_STORAGE(state, val) {  
        state.WRITE_EXTERNAL_STORAGE = val  
    },  
    SET_CALL_PHONE(state, val) {  
        state.CALL_PHONE = val  
    },  
    SET_READ_EXTERNAL_STORAGE(state, val) {  
        state.READ_EXTERNAL_STORAGE = val  
    }  
}  
const actions = {  
    //权限获取  
    async requestPermissions({  
        state,  
        dispatch,  
        commit  
    }, permissionID) {  
        try {  
            if (!state[permissionID]) {  
                var viewObj = await dispatch('nativeObjView', permissionID);  
                viewObj.show();  
            }  
            console.log('android.permission.' + permissionID, '当前手机权限');  
            return new Promise(async (resolve, reject) => {  
                // Android权限查询  
                function requestAndroidPermission(permissionID_) {  
                    return new Promise((resolve, reject) => {  
                        plus.android.requestPermissions(  
                            [  
                                permissionID_  
                            ], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装  
                            function (resultObj) {  
                                var result = 0;  
                                for (var i = 0; i < resultObj.granted.length; i++) {  
                                    var grantedPermission = resultObj.granted[i];  
                                    console.log('已获取的权限:' + grantedPermission);  
                                    result = 1  
                                }  
                                for (var i = 0; i < resultObj.deniedPresent  
                                    .length; i++) {  
                                    var deniedPresentPermission = resultObj  
                                        .deniedPresent[  
                                        i];  
                                    console.log('拒绝本次申请的权限:' + deniedPresentPermission);  
                                    result = 0  
                                }  
                                for (var i = 0; i < resultObj.deniedAlways  
                                    .length; i++) {  
                                    var deniedAlwaysPermission = resultObj.deniedAlways[  
                                        i];  
                                    console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);  
                                    result = -1  
                                }  
                                resolve(result);  
                            },  
                            function (error) {  
                                console.log('申请权限错误:' + error.code + " = " + error  
                                    .message);  
                                resolve({  
                                    code: error.code,  
                                    message: error.message  
                                });  
                            }  
                        );  
                    });  
                }  

                const result = await requestAndroidPermission(  
                    'android.permission.' + permissionID  
                );  
                if (result === 1) {  
                    //'已获得授权'  
                    commit(state.mapping[permissionID].methods, true)  
                } else if (result === 0) {  
                    //'未获得授权'  
                    commit(state.mapping[permissionID].methods, false)  
                } else {  
                    commit(state.mapping[permissionID].methods, true)  
                    uni.showModal({  
                        title: '提示',  
                        content: '操作权限已被拒绝,请手动前往设置',  
                        confirmText: "立即设置",  
                        success: (res) => {  
                            if (res.confirm) {  
                                dispatch('gotoAppPermissionSetting')  
                            }  
                        }  
                    })  
                }  
                if (viewObj) viewObj.close()  
                resolve(result);  
            });  
        } catch (error) {  
            console.log(error);  
            reject(error);  
        }  
    },  
    //提示框  
    nativeObjView({  
        state  
    }, permissionID) {  
        const systemInfo = uni.getSystemInfoSync();  
        const statusBarHeight = systemInfo.statusBarHeight;  
        const navigationBarHeight = systemInfo.platform === 'android' ? 48 :  
            44; // Set the navigation bar height based on the platform  
        const totalHeight = statusBarHeight + navigationBarHeight;  
        let view = new plus.nativeObj.View('per-modal', {  
            top: '0px',  
            left: '0px',  
            width: '100%',  
            backgroundColor: '#444',  
            //opacity: '.9'   
        })  
        view.drawRect({  
            color: '#fff',  
            radius: '5px'  
        }, {  
            top: totalHeight + 'px',  
            left: '5%',  
            width: '90%',  
            height: "100px",  
        })  
        view.drawText(state.mapping[permissionID].title, {  
            top: totalHeight + 5 + 'px',  
            left: "8%",  
            height: "30px"  
        }, {  
            align: "left",  
            color: "#000",  
        }, {  
            onClick: function (e) {  
                console.log(e);  
            }  
        })  
        view.drawText(state.mapping[permissionID].content, {  
            top: totalHeight + 35 + 'px',  
            height: "60px",  
            left: "8%",  
            width: "84%"  
        }, {  
            whiteSpace: 'normal',  
            size: "14px",  
            align: "left",  
            color: "#656563"  
        })  

        function show() {  
            view = plus.nativeObj.View.getViewById('per-modal');  
            view.show()  
            view = null  //这里要为null,不然隐藏不了,不知道为啥  
        }  

        function close() {  
            view = plus.nativeObj.View.getViewById('per-modal');  
            view.close();  
            view = null  
        }  
        return {  
            show,  
            close  
        }  
    },  

    // 跳转到**应用**的权限页面  
    gotoAppPermissionSetting({  
        state  
    }) {  
        if (state.isIos) {  
            var UIApplication = plus.ios.import("UIApplication");  
            var application2 = UIApplication.sharedApplication();  
            var NSURL2 = plus.ios.import("NSURL");  
            // var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");       
            var 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);  
            var Intent = plus.android.importClass("android.content.Intent");  
            var Settings = plus.android.importClass("android.provider.Settings");  
            var Uri = plus.android.importClass("android.net.Uri");  
            var mainActivity = plus.android.runtimeMainActivity();  
            var intent = new Intent();  
            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);  
            var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);  
            intent.setData(uri);  
            mainActivity.startActivity(intent);  
        }  
    }  
}  
export default {  
    namespaced: true,  
    state,  
    mutations,  
    actions  
};  

我是定义vuex里面的一个模块上面完整代码。
然后在原来的基础上加上这个判断就好啦。
<script>
export default {
data() {
return {
}
},
methods: {
async save() {
let result = await this.$store.dispatch("app/requestPermissions",
'WRITE_EXTERNAL_STORAGE')
if (result !== 1) return
uni.saveImageToPhotosAlbum({
filePath: "https://shop.baihuitimes.com/uploads/def/20230913/3fcaeccc6290e1b8367c00ea0aa600e4.png",
success: function(res) {
uni.showToast({
title: '保存成功',
icon:"success"
});
},
fail: function(res) {
uni.showToast({
title: '保存失败',
icon:"fail"
});
},
})
}
}
}
</script>

最后说明,如果用户没有点击授权的操作,将会无法及时拿到权限回调,可以自己在show里面再改改。上架审核人员前期点点点影响不到的。

收起阅读 »

调试模式提示Uncaught SyntaxError: Unexpected token ?解决经验分享

nvue

全局搜索??运算符,使用||替代??运算符,或更改为其他方式进行运算

全局搜索??运算符,使用||替代??运算符,或更改为其他方式进行运算

uniapp 蓝牙无法监听多个特征的问题解决办法

uniapp 教程

问题描述:

在开发app时,项目需要蓝牙同时订阅某个服务下的多个特征值的变化时,直接使用uni.notifyBLECharacteristicValueChange只能订阅一个特征值的变化。

具体实现:

1.将 this.onBLECharacteristicValueChange();放到onLoad()中注册,确保该方法只注册一次,如果该方法注册了多次,蓝牙发送的数据会重复上传。

  1. 在蓝牙的订阅函数notifyBLECharacteristicValueChange()中设置定时器来轮流订阅蓝牙的多个特征值,具体如下:
  2. notifyBLECharacteristicValueChange() {  
            let _this = this;  
            let deviceId = _this.mDevice.deviceId;//这里是蓝牙设备id  
            let serviceId = SERVICE_UUID;//这里是蓝牙的服务UUID  
            let reads = readUUID3;//这里是需要订阅的特征值UUID的数组,[特征值1,特征值2,特征值n]  
            let num = numberReadUUID;//这里是需要订阅的特征值的个数  
            // let characteristicId = reads[0];  
            let countConnect = 0;//连接次数统计  
            let cID=0;//要订阅的特征值的编号  
            _this.notifyCycleTimer = setInterval(()=>{  
                let characteristicId = reads[cID];//要订阅的特征值  
                countConnect = countConnect+1;  
                if(countConnect>10){//连接超过10次没有连接成功就直接返回  
                    uni.hideLoading();  
                    clearInterval(_this.notifyCycleTimer);//注册超时,清理掉定时器  
                    closeBLEConnection();//注册超时就断开蓝牙的连接  
                    closeBluetoothAdapter();//注册超时就关闭蓝牙适配器  
                    uni.showModal({  
                        title: '提示',  
                        content: '蓝牙连接失败,请重试!',  
                        showCancel:false,  
                        complete:function(){  
                            //返回上一界面  
                            uni.navigateBack({  
                                delta:1  
                            });  
                        }  
                    });  
                }  
                uni.notifyBLECharacteristicValueChange({  
                    state: true, // 启用 notify 功能  
                    deviceId,  
                    serviceId,  
                    characteristicId,  
                    success(res) {  
                        console.log('notifyBLECharacteristicValueChange success:' + characteristicId);  
                        cID=cID+1;//订阅成功就订阅下一个特征  
                        if(cID==num){  
                            uni.hideLoading();  
                            clearInterval(_this.notifyCycleTimer);//注册监听成功就清理掉定时器  
                            //返回上一界面  
                            uni.navigateBack({  
                                delta:1  
                            });  
                        }  
                    },  
                    fail(e) {  
                        console.log("订阅失败");  
                    }  
                });  
            },500);//每隔500ms执行一次订阅  
        },
继续阅读 »

问题描述:

在开发app时,项目需要蓝牙同时订阅某个服务下的多个特征值的变化时,直接使用uni.notifyBLECharacteristicValueChange只能订阅一个特征值的变化。

具体实现:

1.将 this.onBLECharacteristicValueChange();放到onLoad()中注册,确保该方法只注册一次,如果该方法注册了多次,蓝牙发送的数据会重复上传。

  1. 在蓝牙的订阅函数notifyBLECharacteristicValueChange()中设置定时器来轮流订阅蓝牙的多个特征值,具体如下:
  2. notifyBLECharacteristicValueChange() {  
            let _this = this;  
            let deviceId = _this.mDevice.deviceId;//这里是蓝牙设备id  
            let serviceId = SERVICE_UUID;//这里是蓝牙的服务UUID  
            let reads = readUUID3;//这里是需要订阅的特征值UUID的数组,[特征值1,特征值2,特征值n]  
            let num = numberReadUUID;//这里是需要订阅的特征值的个数  
            // let characteristicId = reads[0];  
            let countConnect = 0;//连接次数统计  
            let cID=0;//要订阅的特征值的编号  
            _this.notifyCycleTimer = setInterval(()=>{  
                let characteristicId = reads[cID];//要订阅的特征值  
                countConnect = countConnect+1;  
                if(countConnect>10){//连接超过10次没有连接成功就直接返回  
                    uni.hideLoading();  
                    clearInterval(_this.notifyCycleTimer);//注册超时,清理掉定时器  
                    closeBLEConnection();//注册超时就断开蓝牙的连接  
                    closeBluetoothAdapter();//注册超时就关闭蓝牙适配器  
                    uni.showModal({  
                        title: '提示',  
                        content: '蓝牙连接失败,请重试!',  
                        showCancel:false,  
                        complete:function(){  
                            //返回上一界面  
                            uni.navigateBack({  
                                delta:1  
                            });  
                        }  
                    });  
                }  
                uni.notifyBLECharacteristicValueChange({  
                    state: true, // 启用 notify 功能  
                    deviceId,  
                    serviceId,  
                    characteristicId,  
                    success(res) {  
                        console.log('notifyBLECharacteristicValueChange success:' + characteristicId);  
                        cID=cID+1;//订阅成功就订阅下一个特征  
                        if(cID==num){  
                            uni.hideLoading();  
                            clearInterval(_this.notifyCycleTimer);//注册监听成功就清理掉定时器  
                            //返回上一界面  
                            uni.navigateBack({  
                                delta:1  
                            });  
                        }  
                    },  
                    fail(e) {  
                        console.log("订阅失败");  
                    }  
                });  
            },500);//每隔500ms执行一次订阅  
        },
收起阅读 »

globalEvent不起作用;引发globalEvent不起作用的原因之一

uniapp

Android原生插件
现象:插件的方法能正常运行,就是发送了mUniSDKInstance.fireGlobalEventCallback,uni这边的globalEvent监听不到消息,也不报错。
在onLoad中,接收页面传递参数时未判空,离线打包无任何错误提示,打包插件uni也不会有错误提示,即

//引发不起作用的原因  
onLoad(options){  
     if (JSON.parse(options.print)) {  
    console.log("TO DO")  
    }  
     globalEvent.addEventListener('myEvent', (e) => {  
    console.log("myEvent:", JSON.stringify(e))  
    });  
}

正确方式;
对options进行判空

onLoad(options){  
    if(optins!=null){  
       if (JSON.parse(options.print)) {  
        console.log("TO DO")  
    }  
     globalEvent.addEventListener('myEvent', (e) => {  
      console.log("myEvent:", JSON.stringify(e))  
    });  
    }  
}
继续阅读 »

Android原生插件
现象:插件的方法能正常运行,就是发送了mUniSDKInstance.fireGlobalEventCallback,uni这边的globalEvent监听不到消息,也不报错。
在onLoad中,接收页面传递参数时未判空,离线打包无任何错误提示,打包插件uni也不会有错误提示,即

//引发不起作用的原因  
onLoad(options){  
     if (JSON.parse(options.print)) {  
    console.log("TO DO")  
    }  
     globalEvent.addEventListener('myEvent', (e) => {  
    console.log("myEvent:", JSON.stringify(e))  
    });  
}

正确方式;
对options进行判空

onLoad(options){  
    if(optins!=null){  
       if (JSON.parse(options.print)) {  
        console.log("TO DO")  
    }  
     globalEvent.addEventListener('myEvent', (e) => {  
      console.log("myEvent:", JSON.stringify(e))  
    });  
    }  
}
收起阅读 »

Wot Design Uni 增加 Table 表格组件 ,赶快进来看看效果吧!

ui组件 组件

Table 表格

用于展示多条结构类似的数据, 可对数据进行排序等操作。

地址

Github
文档网站
插件市场
QQ群

先看交互效果

基础用法

通过data设置表格数据。

<wd-table :data="dataList">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>
const dataList = reactive([  
{  
  name: '赵云',  
  school: '武汉市阳逻妇幼保健学院',  
  major: '计算机科学与技术专业'  
},  
{  
  name: '孔明',  
  school: '武汉市阳逻卧龙学院',  
  major: '计算机科学与技术专业'  
},  
{  
  name: '刘备',  
  school: '武汉市阳逻编织学院',  
  major: '计算机科学与技术专业'  
}  
])

固定列

通过fixed设置表格列是否固定展示,默认false。 ::warning 提示 目前仅支持固定在左侧,且固定列组件的排列顺序要和实际想要展示的顺序相同。 ::
<wd-table :data="dataList">  
  <wd-table-col prop="name" label="姓名" fixed></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

斑马纹

通过stripe设置表格是否展示斑马纹,默认true

<wd-table :data="dataList" :stripe="false">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

边框

通过border设置表格是否展示边框,默认true

<wd-table :data="dataList" :border="false">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

表格高度

通过height设置表格高度,默认为80vh

<wd-table :data="dataList" height="328px">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

排序事件

当存在列参与排序时,点击会触发sort-method排序事件。

<wd-table :data="dataList" @sort-method="handleSort">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所" :sortable="true"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>
function handleSort(e) {  
  console.log('这里是排序事件')  
}

自定义列模板

自定义列的显示内容,可组合其他组件使用。
通过 Scoped slot 可以获取到 row 的数据,用法参考 demo。

<wd-table :data="dataList" @sort-method="handleSort">  
  <wd-table-col prop="name" label="姓名" fixed="true" width="320rpx" :sortable="true"></wd-table-col>  
  <wd-table-col prop="grade" label="分数" width="220rpx" :sortable="true">  
    <template #value="{row}">  
      <view class="custom-class">  
        <text>{{ row.grade }}</text>  
        <text>同比{{ row.compare }}</text>  
      </view>  
    </template>  
  </wd-table-col>  
  <wd-table-col prop="hobby" label="一言以蔽之" :sortable="true"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
  <wd-table-col prop="gender" label="性别"></wd-table-col>  
  <wd-table-col prop="graduation" label="学成时间"></wd-table-col>  
</wd-table>
import { ref } from 'vue'  

const dataList = ref<Record<string, any>[]>([  
  {  
    name: '张飞',  
    school: '武汉市阳逻杀猪学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 56,  
    compare: '10%',  
    hobby: '燕人张飞在此!'  
  },  
  {  
    name: '关羽',  
    school: '武汉市阳逻绿豆学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 66,  
    compare: '11%',  
    hobby: '颜良文丑,以吾观之,如土鸡瓦犬耳。'  
  },  
  {  
    name: '刘备',  
    school: '武汉市阳逻编织学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 45,  
    compare: '1%',  
    hobby: '我得空明,如鱼得水也'  
  },  
  {  
    name: '赵云',  
    school: '武汉市阳逻妇幼保健学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 69,  
    compare: '14%',  
    hobby: '子龙,子龙,世无双'  
  },  
  {  
    name: '孔明',  
    school: '武汉市阳逻卧龙学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 88,  
    compare: '21%',  
    hobby: '兴汉讨贼,克复中原'  
  },  
  {  
    name: '姜维',  
    school: '武汉市阳逻停水停电技术学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 87,  
    compare: '32%',  
    hobby: '我计不成,乃天命也!'  
  }  
])  

/**  
 * 排序  
 * @param e  
 */  
function handleSort(e) {  
  dataList.value = dataList.value.reverse()  
}
.custom-class {  
  height: 80rpx;  
  width: 220rpx;  
  display: flex;  
  flex-direction: col;  
  align-items: center;  
}

Attributes

参数 说明 类型 可选值 默认值 最低版本
data 显示的数据 Array - - 0.0.39
border 是否带有边框 boolean - true 0.0.39
stripe 是否为斑马纹表 boolean - true 0.0.39
height Table 的高度,默认为80vh string - 80vh 0.0.39
rowHeight 行高 number / string - 50 0.0.39
showHeader 是否显示表头 boolean - true 0.0.39
ellipsis 是否超出2行隐藏 boolean - true 0.0.39

TableColumn Attributes

参数 说明 类型 可选值 默认值 最低版本
prop 字段名称,对应列内容的字段名 string - - 0.0.39
label 显示的标题 string - - 0.0.39
width 对应列的宽度,单位为px number / string - 100 0.0.39
sortable 是否开启列排序 boolean - false 0.0.39
fixed 是否固定本列 boolean - false 0.0.39
align 列的对齐方式 AlignType left, center, right left 0.0.39

地址

Github
文档网站
插件市场
QQ群

继续阅读 »

Table 表格

用于展示多条结构类似的数据, 可对数据进行排序等操作。

地址

Github
文档网站
插件市场
QQ群

先看交互效果

基础用法

通过data设置表格数据。

<wd-table :data="dataList">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>
const dataList = reactive([  
{  
  name: '赵云',  
  school: '武汉市阳逻妇幼保健学院',  
  major: '计算机科学与技术专业'  
},  
{  
  name: '孔明',  
  school: '武汉市阳逻卧龙学院',  
  major: '计算机科学与技术专业'  
},  
{  
  name: '刘备',  
  school: '武汉市阳逻编织学院',  
  major: '计算机科学与技术专业'  
}  
])

固定列

通过fixed设置表格列是否固定展示,默认false。 ::warning 提示 目前仅支持固定在左侧,且固定列组件的排列顺序要和实际想要展示的顺序相同。 ::
<wd-table :data="dataList">  
  <wd-table-col prop="name" label="姓名" fixed></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

斑马纹

通过stripe设置表格是否展示斑马纹,默认true

<wd-table :data="dataList" :stripe="false">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

边框

通过border设置表格是否展示边框,默认true

<wd-table :data="dataList" :border="false">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

表格高度

通过height设置表格高度,默认为80vh

<wd-table :data="dataList" height="328px">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>

排序事件

当存在列参与排序时,点击会触发sort-method排序事件。

<wd-table :data="dataList" @sort-method="handleSort">  
  <wd-table-col prop="name" label="姓名"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所" :sortable="true"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
</wd-table>
function handleSort(e) {  
  console.log('这里是排序事件')  
}

自定义列模板

自定义列的显示内容,可组合其他组件使用。
通过 Scoped slot 可以获取到 row 的数据,用法参考 demo。

<wd-table :data="dataList" @sort-method="handleSort">  
  <wd-table-col prop="name" label="姓名" fixed="true" width="320rpx" :sortable="true"></wd-table-col>  
  <wd-table-col prop="grade" label="分数" width="220rpx" :sortable="true">  
    <template #value="{row}">  
      <view class="custom-class">  
        <text>{{ row.grade }}</text>  
        <text>同比{{ row.compare }}</text>  
      </view>  
    </template>  
  </wd-table-col>  
  <wd-table-col prop="hobby" label="一言以蔽之" :sortable="true"></wd-table-col>  
  <wd-table-col prop="school" label="求学之所"></wd-table-col>  
  <wd-table-col prop="major" label="专业"></wd-table-col>  
  <wd-table-col prop="gender" label="性别"></wd-table-col>  
  <wd-table-col prop="graduation" label="学成时间"></wd-table-col>  
</wd-table>
import { ref } from 'vue'  

const dataList = ref<Record<string, any>[]>([  
  {  
    name: '张飞',  
    school: '武汉市阳逻杀猪学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 56,  
    compare: '10%',  
    hobby: '燕人张飞在此!'  
  },  
  {  
    name: '关羽',  
    school: '武汉市阳逻绿豆学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 66,  
    compare: '11%',  
    hobby: '颜良文丑,以吾观之,如土鸡瓦犬耳。'  
  },  
  {  
    name: '刘备',  
    school: '武汉市阳逻编织学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 45,  
    compare: '1%',  
    hobby: '我得空明,如鱼得水也'  
  },  
  {  
    name: '赵云',  
    school: '武汉市阳逻妇幼保健学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 69,  
    compare: '14%',  
    hobby: '子龙,子龙,世无双'  
  },  
  {  
    name: '孔明',  
    school: '武汉市阳逻卧龙学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 88,  
    compare: '21%',  
    hobby: '兴汉讨贼,克复中原'  
  },  
  {  
    name: '姜维',  
    school: '武汉市阳逻停水停电技术学院',  
    major: '计算机科学与技术专业',  
    gender: '男',  
    graduation: '2022年1月12日',  
    grade: 87,  
    compare: '32%',  
    hobby: '我计不成,乃天命也!'  
  }  
])  

/**  
 * 排序  
 * @param e  
 */  
function handleSort(e) {  
  dataList.value = dataList.value.reverse()  
}
.custom-class {  
  height: 80rpx;  
  width: 220rpx;  
  display: flex;  
  flex-direction: col;  
  align-items: center;  
}

Attributes

参数 说明 类型 可选值 默认值 最低版本
data 显示的数据 Array - - 0.0.39
border 是否带有边框 boolean - true 0.0.39
stripe 是否为斑马纹表 boolean - true 0.0.39
height Table 的高度,默认为80vh string - 80vh 0.0.39
rowHeight 行高 number / string - 50 0.0.39
showHeader 是否显示表头 boolean - true 0.0.39
ellipsis 是否超出2行隐藏 boolean - true 0.0.39

TableColumn Attributes

参数 说明 类型 可选值 默认值 最低版本
prop 字段名称,对应列内容的字段名 string - - 0.0.39
label 显示的标题 string - - 0.0.39
width 对应列的宽度,单位为px number / string - 100 0.0.39
sortable 是否开启列排序 boolean - false 0.0.39
fixed 是否固定本列 boolean - false 0.0.39
align 列的对齐方式 AlignType left, center, right left 0.0.39

地址

Github
文档网站
插件市场
QQ群

收起阅读 »

uniapp对微信小程序异步加载分包组件的做法--vite插件方案

分包

微信原生文档见 文档
社区有方案,是在style中写入
这里的方案是使用vite插件,在编译完成后,对目标插件进行代码注入 (仅适用vue3 + vite) , 目前运行在uniapp 微信小程序上

目录结构:

/project-config  
      componentPlaceholder  
             componentPlaceholder.js  
      vite.uni-component-placeholder.js

vite插件代码

/**   
    实现了 uni编译完成后 , 处理 componentPlaceholder   

 */   
var fs = require('fs');  
var path = require('path');  

class ProcessComponentPlaceholder{  

    constructor(){  

        this.destFolder = process.env.UNI_OUTPUT_DIR;  

    }  

    process(){  
        let dev_fold = process.env.UNI_INPUT_DIR;  
        let dir = dev_fold+"/project-config/componentPlaceHolder/"  
        let files = fs.readdirSync(dir, 'utf-8');  
        let fileMap = {}  
        files.some((f) => {  
            let p = dir + "/" + f;  
            let stat = fs.lstatSync(p);  
            if (!stat.isDirectory()) {  
                fileMap[f] = p;  
            }  
        })  

        this.processNodes(fileMap)  

    }  

    processNodes(fileMap){  
        console.log("处理异步组件引用componentPlaceHolder",fileMap)  
        for(let jsonFileName in fileMap){  
            let path = fileMap[jsonFileName];  
            if(!path.lastIndexOf(".js")<0)continue;  
            let obj = require(path);  
            // console.log("读componentPlaceHolder", obj)  
            this.processOneConfig(obj);  
        }   
        console.table("componentPlaceHolder处理完毕")  
    }  

    processOneConfig(config){  
        for(let f in config){ //某个配置文件  
            let weixinJSONFile = this.destFolder+f+".json";  
            fs.readFile(weixinJSONFile,'utf8',(err, data)=>{  
                  if (err) {  
                    return console.log('componentPlaceHolder文件读取失败,失败原因是:' + err)  
                  }  
               let weixinJSON = JSON.parse(data);  
               // console.log("读componentPlaceHolder",weixinJSON);  

                //准备合并配置      
                let usingComponents = weixinJSON["usingComponents"]||{};  
                let componentPlaceholder = weixinJSON["componentPlaceholder"]||{};  
                let customConfig = config[f];  
                for(let tag in customConfig)   {  
                    let tagVal = customConfig[tag];  
                    let path = tagVal.path;  
                    let replace = tagVal.replace;  
                    // console.log(weixinJSONFile+ "  " +tag+" "+path )  
                    if(!usingComponents[tag]){  
                        usingComponents[tag]="../.."+path;  //这里的双层目录有必要可能动态算相对层级,根据项目自身情况而定  
                    }  
                    if(!componentPlaceholder[tag]){  
                        componentPlaceholder[tag] = replace;  
                    }  
                }  
                weixinJSON.usingComponents = usingComponents;  
                weixinJSON.componentPlaceholder = componentPlaceholder;  
               fs.writeFileSync(weixinJSONFile, JSON.stringify(weixinJSON,null,4))  

            }) ;  
        }  

    }  

}  

export default (options)=> {  
    var name = 'vite-plugin-copy-uniapp_config';  

    return {  
        name: name,  
        enforce: 'post',   
        closeBundle:()=>{  //buildEnd之后运行  
            options.forEach(function(option) {  

                    let processor =  new ProcessComponentPlaceholder();  
                     processor.process();  
            });  
        }  

    };  
}   

配置文件 componentPlaceholder.js 代码:

/**   
 配置:  

    {  
        "某个包的组件路径,不带.vue后缀":{  
            "组件名,一般为文件名不带.vue和路径":{  
                path:"引用某个包的组件路径,不带.vue后缀",  
                replace:"未加载完成时的替换组件,比如view或某个全局组件"  
            }   
        }  
    }   
 */  
module.exports =  {   
    "/pages/index/index":{  
        "tabbar-me":{  
            path:"/package-my/pages/my/my",  
            replace:"view"  
        },   
    },  

}  

vite.config.js

import viteComponentPlaceHolder from "./project-config/vite.uni-component-placeholder.js"  

plugins.push(viteComponentPlaceHolder([{}]))  

export default defineConfig({  
    plugins   
});  

启动时会有日志
08:48:43.602 处理异步组件引用componentPlaceHolder {
08:48:43.609 'componentPlaceholder.js': '/project-config/componentPlaceHolder//componentPlaceholder.js'
08:48:43.610 }

继续阅读 »

微信原生文档见 文档
社区有方案,是在style中写入
这里的方案是使用vite插件,在编译完成后,对目标插件进行代码注入 (仅适用vue3 + vite) , 目前运行在uniapp 微信小程序上

目录结构:

/project-config  
      componentPlaceholder  
             componentPlaceholder.js  
      vite.uni-component-placeholder.js

vite插件代码

/**   
    实现了 uni编译完成后 , 处理 componentPlaceholder   

 */   
var fs = require('fs');  
var path = require('path');  

class ProcessComponentPlaceholder{  

    constructor(){  

        this.destFolder = process.env.UNI_OUTPUT_DIR;  

    }  

    process(){  
        let dev_fold = process.env.UNI_INPUT_DIR;  
        let dir = dev_fold+"/project-config/componentPlaceHolder/"  
        let files = fs.readdirSync(dir, 'utf-8');  
        let fileMap = {}  
        files.some((f) => {  
            let p = dir + "/" + f;  
            let stat = fs.lstatSync(p);  
            if (!stat.isDirectory()) {  
                fileMap[f] = p;  
            }  
        })  

        this.processNodes(fileMap)  

    }  

    processNodes(fileMap){  
        console.log("处理异步组件引用componentPlaceHolder",fileMap)  
        for(let jsonFileName in fileMap){  
            let path = fileMap[jsonFileName];  
            if(!path.lastIndexOf(".js")<0)continue;  
            let obj = require(path);  
            // console.log("读componentPlaceHolder", obj)  
            this.processOneConfig(obj);  
        }   
        console.table("componentPlaceHolder处理完毕")  
    }  

    processOneConfig(config){  
        for(let f in config){ //某个配置文件  
            let weixinJSONFile = this.destFolder+f+".json";  
            fs.readFile(weixinJSONFile,'utf8',(err, data)=>{  
                  if (err) {  
                    return console.log('componentPlaceHolder文件读取失败,失败原因是:' + err)  
                  }  
               let weixinJSON = JSON.parse(data);  
               // console.log("读componentPlaceHolder",weixinJSON);  

                //准备合并配置      
                let usingComponents = weixinJSON["usingComponents"]||{};  
                let componentPlaceholder = weixinJSON["componentPlaceholder"]||{};  
                let customConfig = config[f];  
                for(let tag in customConfig)   {  
                    let tagVal = customConfig[tag];  
                    let path = tagVal.path;  
                    let replace = tagVal.replace;  
                    // console.log(weixinJSONFile+ "  " +tag+" "+path )  
                    if(!usingComponents[tag]){  
                        usingComponents[tag]="../.."+path;  //这里的双层目录有必要可能动态算相对层级,根据项目自身情况而定  
                    }  
                    if(!componentPlaceholder[tag]){  
                        componentPlaceholder[tag] = replace;  
                    }  
                }  
                weixinJSON.usingComponents = usingComponents;  
                weixinJSON.componentPlaceholder = componentPlaceholder;  
               fs.writeFileSync(weixinJSONFile, JSON.stringify(weixinJSON,null,4))  

            }) ;  
        }  

    }  

}  

export default (options)=> {  
    var name = 'vite-plugin-copy-uniapp_config';  

    return {  
        name: name,  
        enforce: 'post',   
        closeBundle:()=>{  //buildEnd之后运行  
            options.forEach(function(option) {  

                    let processor =  new ProcessComponentPlaceholder();  
                     processor.process();  
            });  
        }  

    };  
}   

配置文件 componentPlaceholder.js 代码:

/**   
 配置:  

    {  
        "某个包的组件路径,不带.vue后缀":{  
            "组件名,一般为文件名不带.vue和路径":{  
                path:"引用某个包的组件路径,不带.vue后缀",  
                replace:"未加载完成时的替换组件,比如view或某个全局组件"  
            }   
        }  
    }   
 */  
module.exports =  {   
    "/pages/index/index":{  
        "tabbar-me":{  
            path:"/package-my/pages/my/my",  
            replace:"view"  
        },   
    },  

}  

vite.config.js

import viteComponentPlaceHolder from "./project-config/vite.uni-component-placeholder.js"  

plugins.push(viteComponentPlaceHolder([{}]))  

export default defineConfig({  
    plugins   
});  

启动时会有日志
08:48:43.602 处理异步组件引用componentPlaceHolder {
08:48:43.609 'componentPlaceholder.js': '/project-config/componentPlaceHolder//componentPlaceholder.js'
08:48:43.610 }

收起阅读 »

七分钟掌握 uni-app自定义基座,离线打包的生成方法

App离线打包 自定义基座
// what's uapp  
const uapp = 'universal app'

uapp 是一款跨平台APP开发工具箱,所有积累都来自多年产品开发中的不断实践。开发者仅需写一套代码,就能横扫所有平台。

制作了一个七分钟视频,包含了android,ios 手机平台上,离线打包,自定义基座的制作方法,视频首秀,喜欢的小伙伴帮忙点赞支持

https://www.ixigua.com/7294197408232276495

继续阅读 »
// what's uapp  
const uapp = 'universal app'

uapp 是一款跨平台APP开发工具箱,所有积累都来自多年产品开发中的不断实践。开发者仅需写一套代码,就能横扫所有平台。

制作了一个七分钟视频,包含了android,ios 手机平台上,离线打包,自定义基座的制作方法,视频首秀,喜欢的小伙伴帮忙点赞支持

https://www.ixigua.com/7294197408232276495

收起阅读 »

uni-app 开发中,监听 input 键盘事件获取不到按下按键值怎么办?

uniapp 教程

uniapp 开发 H5 时,无法监听按钮键盘事件的原因以及解决方法。

问题描述:
不少 uni-app 开发者在使用 input 组件时,监听 keyup 事件时,获取不到键盘的 keyCode。编写的代码如下:

<template>  
  <input @keyup="handleKeyUp">  
</template>

但是在 handleKeyUp() 方法里获取不到键盘的编码,出现这个问题的原因是 uni-app 的内置组件 <input> 其实是封装过的,编译为 h5 时不是 html 原生的 input 元素,所以才无法监听原生的键盘事件。

解决方法参考这个:uni-app input 键盘事件获取按键值

继续阅读 »

uniapp 开发 H5 时,无法监听按钮键盘事件的原因以及解决方法。

问题描述:
不少 uni-app 开发者在使用 input 组件时,监听 keyup 事件时,获取不到键盘的 keyCode。编写的代码如下:

<template>  
  <input @keyup="handleKeyUp">  
</template>

但是在 handleKeyUp() 方法里获取不到键盘的编码,出现这个问题的原因是 uni-app 的内置组件 <input> 其实是封装过的,编译为 h5 时不是 html 原生的 input 元素,所以才无法监听原生的键盘事件。

解决方法参考这个:uni-app input 键盘事件获取按键值

收起阅读 »