HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

uniapp创建企微在ios端wx.config 以及wx.agentConfig问题的完美解决

uniapp

之前写过一篇解决的文章 但是 后续发现还是有那么一点问题 就是ios 在调用 agentConfig的API 的时候会发现 agentConfig 找不到 是 undefined。

https://developer.work.weixin.qq.com/community/article/detail?content_id=16288602254668494166

之前的文章,通过后学的摸索,终于完美解决了。

步骤一:创建项目

我们知道 官方然我们在项目中 需要建立一个 index.html 文件用于 引入企微的SDK。我发现及时在index.html中引入了,uniapp在打包的时候还是会占用wx这个类,说明uniapp内部还是一个异步的过程。因此我们需要动态引入,而不是 在index中引入。

<head>  
        <meta charset="utf-8">  
        <meta http-equiv="X-UA-Compatible" content="IE=edge">  
        <meta name="viewport"  
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">  
        <title>  
            <%= htmlWebpackPlugin.options.title %>  
        </title>  
        <script>  
            document.addEventListener('DOMContentLoaded', function() {  
                document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'  
            })  
            window.wx = null;  
        </script>  
        <link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />  
        <!-- 企业微信h5授权 config注入 -->  
        <!-- <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> -->  
        <!-- 调用 wx.agentConfig需要引入 jwxwork sdk -->  
        <!-- <script type="text/javascript" src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script> -->  
        <!-- 用于调试所用 -->  
        <script type="text/javascript" src="https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js"></script>  
        <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/mobile-detect@1.4.5/mobile-detect.min.js"></script>  
        <script type="text/javascript">  
            delete window.wx;   

        </script>  
<head>

因此 在index中我们只引入 vconsole 就行了 不要引入企微的SDK。后续我们通过动态引入

二:动态引入sdk 并注册

我们可以在工程中建立一个专门的 企微的工具库 ,我的工程是放在一起的 没有分开。你可以根据自己的逻辑来建立库 我的库为 ESUtils.js 并将 企微的方法全放在了 wxUntils中

我们动态 引入SDK,动态引入

createdScript: function(callback) {  
            // window.wx = null;  
            let userInfo = ESUtils.ESDB.getLoginInfo(); //自己存的相关缓存  
            const script1 = document.createElement("script");  
            script1.setAttribute("type", "text/javascript");  
            script1.setAttribute("referrerpolicy", "origin");  
            script1.setAttribute("src", "https://res.wx.qq.com/open/js/jweixin-1.2.0.js");  
            document.head.appendChild(script1);  
            script1.onload = function() {  
                const script2 = document.createElement("script");  
                script2.setAttribute("type", "text/javascript");  
                script2.setAttribute("referrerpolicy", "origin");  
                script2.setAttribute("src", "https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js");  
                document.head.appendChild(script2);  
                script2.onload = () => {  
                    callback && callback(userInfo)  
                }  
            }  
        },  

注册 config


initJssdk: function(callback) {  
            this.createdScript((userInfo) => {  

                ESUtils.network.request({  
                    url: userInfo.server,  
                    data: {  
                        method: "Work_WXInitJSSDK", //初始化SDK  
                        userId:userInfo.userId,  
                        url:userInfo.url,  
                        cropId:userInfo.cropId  
                    },  
                    success: res => {  
                        if (res.data.response.code == 200) {  
                            let info = res.data.body;  //需要判断 ios 还是 安卓   
                            if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                                wx.config({  
                                    beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题  
                                    debug: false, // 开启调试模式  
                                    appId: info.appId, // 必填,企业微信的corpID  
                                    timestamp: info.timestamp, // 必填,生成签名的时间戳  
                                    nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                                    signature: info.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法  
                                    jsApiList: [  
                                        'chooseImage',  
                                        'scanQRCode',  
                                        'previewImage',  
                                        'getLocation',  
                                        'previewFile',  
                                        'openLocation',  
                                        'uploadImage',  
                                        'getLocalImgData',  
                                        'downloadImage',  
                                        'startRecord',  
                                        'stopRecord',  
                                        'onVoiceRecordEnd',  
                                        'playVoice',  
                                        'pauseVoice',  
                                        'stopVoice',  
                                        'onVoicePlayEnd',  
                                        'uploadVoice',  
                                        'downloadVoice',  
                                        'translateVoice'  
                                    ]  
                                });  
                                wx.ready((res) => {  
                                    callback && callback(info);  
                                })  
                                wx.error((err) => {  

                                });  
                            }else{  
                                jWeixin.config({  
                                    beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题  
                                    debug: false, // 开启调试模式  
                                    appId: info.appId, // 必填,企业微信的corpID  
                                    timestamp: info.timestamp, // 必填,生成签名的时间戳  
                                    nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                                    signature: info.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法  
                                    jsApiList: [  
                                        'chooseImage',  
                                        'scanQRCode',  
                                        'previewImage',  
                                        'getLocation',  
                                        'previewFile',  
                                        'openLocation',  
                                        'uploadImage',  
                                        'getLocalImgData',  
                                        'downloadImage',  
                                        'startRecord',  
                                        'stopRecord',  
                                        'onVoiceRecordEnd',  
                                        'playVoice',  
                                        'pauseVoice',  
                                        'stopVoice',  
                                        'onVoicePlayEnd',  
                                        'uploadVoice',  
                                        'downloadVoice',  
                                        'translateVoice'  
                                    ]  
                                });  
                                jWeixin.ready((res) => {  
                                    callback && callback(info);  
                                })  
                                jWeixin.error((err) => {  

                                });  
                            }  
                        }  
                    }  
                });  
            });   
        },  

注册 agentConfig  

//agentConfig必须是 config注册成功后才能 注册  
initJssdkAgent: function(callback) {          
            ESUtils.wxUntils.initJssdk((info) => {  
                if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                    setTimeout(()=>{  
                        wx.agentConfig({  
                            corpid: info.appId, // 必填,企业微信的corpid,必须与当前登录的企业一致  
                            agentid: info.angetId, // 必填,企业微信的应用id (e.g. 1000247)  
                            timestamp: info.timestamp, // 必填,生成签名的时间戳  
                            nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                            signature: info.signature1, // 必填,签名,见附录-JS-SDK使用权限签名算法  
                            jsApiList: [  
                                "selectEnterpriseContact",  
                                "getContext",  
                                "getCurExternalContact",  
                                "openUserProfile",  
                                "getCurExternalChat",  
                                "selectExternalContact",  
                                "selectPrivilegedContact",  
                                "createChatWithMsg",  
                                "sendChatMessage",  
                                "openExistedChatWithMsg",  
                                "shareToExternalContact",  
                                "shareToExternalChat",  
                                "setShareAttr",  
                                "getShareInfo",  
                                "shareAppMessage", //自定义转发发到会话  
                                "shareWechatMessage", //自定义转发到微信  
                                "openEnterpriseChat"  
                            ],  
                            success: res => {  
                                callback && callback(res)  
                            },  
                            fail: err => {  
                                callback && callback(err)  
                            }  
                        });   
                    }, 1000);  
                }else{  
                    setTimeout(()=>{  
                        jWeixin.agentConfig({  
                            corpid: info.appId, // 必填,企业微信的corpid,必须与当前登录的企业一致  
                            agentid: info.angetId, // 必填,企业微信的应用id (e.g. 1000247)  
                            timestamp: info.timestamp, // 必填,生成签名的时间戳  
                            nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                            signature: info.signature1, // 必填,签名,见附录-JS-SDK使用权限签名算法  
                            jsApiList: [  
                                "selectEnterpriseContact",  
                                "getContext",  
                                "getCurExternalContact",  
                                "openUserProfile",  
                                "getCurExternalChat",  
                                "selectExternalContact",  
                                "selectPrivilegedContact",  
                                "createChatWithMsg",  
                                "sendChatMessage",  
                                "openExistedChatWithMsg",  
                                "shareToExternalContact",  
                                "shareToExternalChat",  
                                "setShareAttr",  
                                "getShareInfo",  
                                "shareAppMessage", //自定义转发发到会话  
                                "shareWechatMessage", //自定义转发到微信  
                                "openEnterpriseChat"  
                            ],  
                            success: res => {  
                                callback && callback(res)  
                            },  
                            fail: err => {  
                                callback && callback(err)  
                            }  
                        });   
                    }, 1000);  
                }  

            });  
    },  

现在我们就已经解决了 初始化及注册的问题了,然后我们将我们需要用到的 API 进行二次封装  

//扫一扫的 API   
scanCode: function(callback) {  

                if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                    wx.scanQRCode({  
                        desc: 'scanQRCode desc',  
                        needResult: 1, // 默认为0,扫描结果由企业微信处理,1则直接返回扫描结果,  
                        scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是条形码(一维码),默认二者都有  
                        success: function(res) {  
                            callback && callback(res);  
                        }  
                    });  
                }else{  
                    jWeixin.scanQRCode({  
                        desc: 'scanQRCode desc',  
                        needResult: 1, // 默认为0,扫描结果由企业微信处理,1则直接返回扫描结果,  
                        scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是条形码(一维码),默认二者都有  
                        success: function(res) {  
                            callback && callback(res);  
                        }  
                    });  
                }  

        },  

//获取进入H5页面的入口环境  
getContext: function(callback) {  
            if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                wx.invoke('getContext', {}, (res) => {  
                    callback && callback(res)  
                });  
            }else{  
                jWeixin.invoke('getContext', {}, (res) => {  
                    callback && callback(res)  
                });  
            }     
        },  
// 选择外部联系人  
selectEContact: function(callback) {  
                if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                    wx.invoke('selectExternalContact', {  
                        "filterType": 0, //0表示展示全部外部联系人列表,1表示仅展示未曾选择过的外部联系人。默认值为0;除了0与1,其他值非法。在企业微信2.4.22及以后版本支持该参数  
                    }, (res) => {  
                        if (res.err_msg == "selectExternalContact:ok") {  
                            callback && callback(res, 1);  
                        } else {  
                            callback && callback(res, 0);  
                        }  
                    });  
                }else{  
                    jWeixin.invoke('selectExternalContact', {  
                        "filterType": 0, //0表示展示全部外部联系人列表,1表示仅展示未曾选择过的外部联系人。默认值为0;除了0与1,其他值非法。在企业微信2.4.22及以后版本支持该参数  
                    }, (res) => {  
                        if (res.err_msg == "selectExternalContact:ok") {  
                            callback && callback(res, 1);  
                        } else {  
                            callback && callback(res, 0);  
                        }  
                    });  
                }     

        },  

上述为一部分的API封装  

三:页面的使用  

做完所有的准备工作后,我们就需要进行项目的建立了 首先我们需要在页面中进行引入。  

index.vue。  

这个有一个非常重要的信息,我最开始 也是 这样然后再页面引入了但是我通过console 发现 当第一次进入的时候 在ios中无法识别出 agentConfig,一次偶然的时机我通过刷新一次页面后发现就能抽取出来,但是 动态引入只能引入一次,因此我决定更具这个情况进行曲线救国策略。  

在index.vue中  

onload(option){  
      var url = document.location.href;  
      let value = ESUtils.fn.getQueryString("eventType");  
      //每次进入index的我们让他自动刷新一次  
      if(url.indexOf("refreshed=") == -1){  
            var time = new Date();  
            window.location.href=url+"&refreshed="+time.getTime();  
      }           

      this.$nextTick(()=>{  

            let cropId = ESUtils.ESDB.getLoginInfo().cropId;  
            let userId = ESUtils.ESDB.getLoginInfo().id;  
            let url = window.location.href;  
            ESUtils.wxUntils.initJssdkAgent(res=>{  
                //引入并注册SDK  
                WWOpenData.bind(document.querySelectorAll('ww-open-data'));  
                ESUtils.wxUntils.getContext(result=>{  //这是判断是否 从快捷栏进入的 从而跳转指定的页面 并将入口的参数传过去  
                    var loginInfo = ESUtils.ESDB.getLoginInfo();  
                    if(url.indexOf("refreshed=") != -1){  
                        if(option.eventType && option.eventType != ""){  
                            var push = ESPush[option.eventType];  
                            var params = '?pushEventId=' + option.eventId + "&workWX=" + result.entry;    
                            let page = "";  
                            if(ESUtils.ESDB.getLoginInfo().isJava == 1){  
                                page = push.page_java;  
                            }else{  
                                page = push.page;  
                            }  
                                push && uni.navigateTo({  
                                    url: page + params  
                                });  
                            }  
                        }  
                    });  
                })                    
            });     
     }  

通过这样 我们就可以发现  在ios 和安卓中我们都可以进行兼容了 ,不会出现  wx.config 或者 wx.agnetConfig 为undefined的问题了。注意 其他的界面最好不要再调用 initJssdkAgent()方法了  
如果 其他的朋友还有更好的解决方法,可以多多交流
继续阅读 »

之前写过一篇解决的文章 但是 后续发现还是有那么一点问题 就是ios 在调用 agentConfig的API 的时候会发现 agentConfig 找不到 是 undefined。

https://developer.work.weixin.qq.com/community/article/detail?content_id=16288602254668494166

之前的文章,通过后学的摸索,终于完美解决了。

步骤一:创建项目

我们知道 官方然我们在项目中 需要建立一个 index.html 文件用于 引入企微的SDK。我发现及时在index.html中引入了,uniapp在打包的时候还是会占用wx这个类,说明uniapp内部还是一个异步的过程。因此我们需要动态引入,而不是 在index中引入。

<head>  
        <meta charset="utf-8">  
        <meta http-equiv="X-UA-Compatible" content="IE=edge">  
        <meta name="viewport"  
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">  
        <title>  
            <%= htmlWebpackPlugin.options.title %>  
        </title>  
        <script>  
            document.addEventListener('DOMContentLoaded', function() {  
                document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'  
            })  
            window.wx = null;  
        </script>  
        <link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />  
        <!-- 企业微信h5授权 config注入 -->  
        <!-- <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> -->  
        <!-- 调用 wx.agentConfig需要引入 jwxwork sdk -->  
        <!-- <script type="text/javascript" src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script> -->  
        <!-- 用于调试所用 -->  
        <script type="text/javascript" src="https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js"></script>  
        <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/mobile-detect@1.4.5/mobile-detect.min.js"></script>  
        <script type="text/javascript">  
            delete window.wx;   

        </script>  
<head>

因此 在index中我们只引入 vconsole 就行了 不要引入企微的SDK。后续我们通过动态引入

二:动态引入sdk 并注册

我们可以在工程中建立一个专门的 企微的工具库 ,我的工程是放在一起的 没有分开。你可以根据自己的逻辑来建立库 我的库为 ESUtils.js 并将 企微的方法全放在了 wxUntils中

我们动态 引入SDK,动态引入

createdScript: function(callback) {  
            // window.wx = null;  
            let userInfo = ESUtils.ESDB.getLoginInfo(); //自己存的相关缓存  
            const script1 = document.createElement("script");  
            script1.setAttribute("type", "text/javascript");  
            script1.setAttribute("referrerpolicy", "origin");  
            script1.setAttribute("src", "https://res.wx.qq.com/open/js/jweixin-1.2.0.js");  
            document.head.appendChild(script1);  
            script1.onload = function() {  
                const script2 = document.createElement("script");  
                script2.setAttribute("type", "text/javascript");  
                script2.setAttribute("referrerpolicy", "origin");  
                script2.setAttribute("src", "https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js");  
                document.head.appendChild(script2);  
                script2.onload = () => {  
                    callback && callback(userInfo)  
                }  
            }  
        },  

注册 config


initJssdk: function(callback) {  
            this.createdScript((userInfo) => {  

                ESUtils.network.request({  
                    url: userInfo.server,  
                    data: {  
                        method: "Work_WXInitJSSDK", //初始化SDK  
                        userId:userInfo.userId,  
                        url:userInfo.url,  
                        cropId:userInfo.cropId  
                    },  
                    success: res => {  
                        if (res.data.response.code == 200) {  
                            let info = res.data.body;  //需要判断 ios 还是 安卓   
                            if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                                wx.config({  
                                    beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题  
                                    debug: false, // 开启调试模式  
                                    appId: info.appId, // 必填,企业微信的corpID  
                                    timestamp: info.timestamp, // 必填,生成签名的时间戳  
                                    nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                                    signature: info.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法  
                                    jsApiList: [  
                                        'chooseImage',  
                                        'scanQRCode',  
                                        'previewImage',  
                                        'getLocation',  
                                        'previewFile',  
                                        'openLocation',  
                                        'uploadImage',  
                                        'getLocalImgData',  
                                        'downloadImage',  
                                        'startRecord',  
                                        'stopRecord',  
                                        'onVoiceRecordEnd',  
                                        'playVoice',  
                                        'pauseVoice',  
                                        'stopVoice',  
                                        'onVoicePlayEnd',  
                                        'uploadVoice',  
                                        'downloadVoice',  
                                        'translateVoice'  
                                    ]  
                                });  
                                wx.ready((res) => {  
                                    callback && callback(info);  
                                })  
                                wx.error((err) => {  

                                });  
                            }else{  
                                jWeixin.config({  
                                    beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题  
                                    debug: false, // 开启调试模式  
                                    appId: info.appId, // 必填,企业微信的corpID  
                                    timestamp: info.timestamp, // 必填,生成签名的时间戳  
                                    nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                                    signature: info.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法  
                                    jsApiList: [  
                                        'chooseImage',  
                                        'scanQRCode',  
                                        'previewImage',  
                                        'getLocation',  
                                        'previewFile',  
                                        'openLocation',  
                                        'uploadImage',  
                                        'getLocalImgData',  
                                        'downloadImage',  
                                        'startRecord',  
                                        'stopRecord',  
                                        'onVoiceRecordEnd',  
                                        'playVoice',  
                                        'pauseVoice',  
                                        'stopVoice',  
                                        'onVoicePlayEnd',  
                                        'uploadVoice',  
                                        'downloadVoice',  
                                        'translateVoice'  
                                    ]  
                                });  
                                jWeixin.ready((res) => {  
                                    callback && callback(info);  
                                })  
                                jWeixin.error((err) => {  

                                });  
                            }  
                        }  
                    }  
                });  
            });   
        },  

注册 agentConfig  

//agentConfig必须是 config注册成功后才能 注册  
initJssdkAgent: function(callback) {          
            ESUtils.wxUntils.initJssdk((info) => {  
                if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                    setTimeout(()=>{  
                        wx.agentConfig({  
                            corpid: info.appId, // 必填,企业微信的corpid,必须与当前登录的企业一致  
                            agentid: info.angetId, // 必填,企业微信的应用id (e.g. 1000247)  
                            timestamp: info.timestamp, // 必填,生成签名的时间戳  
                            nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                            signature: info.signature1, // 必填,签名,见附录-JS-SDK使用权限签名算法  
                            jsApiList: [  
                                "selectEnterpriseContact",  
                                "getContext",  
                                "getCurExternalContact",  
                                "openUserProfile",  
                                "getCurExternalChat",  
                                "selectExternalContact",  
                                "selectPrivilegedContact",  
                                "createChatWithMsg",  
                                "sendChatMessage",  
                                "openExistedChatWithMsg",  
                                "shareToExternalContact",  
                                "shareToExternalChat",  
                                "setShareAttr",  
                                "getShareInfo",  
                                "shareAppMessage", //自定义转发发到会话  
                                "shareWechatMessage", //自定义转发到微信  
                                "openEnterpriseChat"  
                            ],  
                            success: res => {  
                                callback && callback(res)  
                            },  
                            fail: err => {  
                                callback && callback(err)  
                            }  
                        });   
                    }, 1000);  
                }else{  
                    setTimeout(()=>{  
                        jWeixin.agentConfig({  
                            corpid: info.appId, // 必填,企业微信的corpid,必须与当前登录的企业一致  
                            agentid: info.angetId, // 必填,企业微信的应用id (e.g. 1000247)  
                            timestamp: info.timestamp, // 必填,生成签名的时间戳  
                            nonceStr: info.nonceStr, // 必填,生成签名的随机串  
                            signature: info.signature1, // 必填,签名,见附录-JS-SDK使用权限签名算法  
                            jsApiList: [  
                                "selectEnterpriseContact",  
                                "getContext",  
                                "getCurExternalContact",  
                                "openUserProfile",  
                                "getCurExternalChat",  
                                "selectExternalContact",  
                                "selectPrivilegedContact",  
                                "createChatWithMsg",  
                                "sendChatMessage",  
                                "openExistedChatWithMsg",  
                                "shareToExternalContact",  
                                "shareToExternalChat",  
                                "setShareAttr",  
                                "getShareInfo",  
                                "shareAppMessage", //自定义转发发到会话  
                                "shareWechatMessage", //自定义转发到微信  
                                "openEnterpriseChat"  
                            ],  
                            success: res => {  
                                callback && callback(res)  
                            },  
                            fail: err => {  
                                callback && callback(err)  
                            }  
                        });   
                    }, 1000);  
                }  

            });  
    },  

现在我们就已经解决了 初始化及注册的问题了,然后我们将我们需要用到的 API 进行二次封装  

//扫一扫的 API   
scanCode: function(callback) {  

                if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                    wx.scanQRCode({  
                        desc: 'scanQRCode desc',  
                        needResult: 1, // 默认为0,扫描结果由企业微信处理,1则直接返回扫描结果,  
                        scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是条形码(一维码),默认二者都有  
                        success: function(res) {  
                            callback && callback(res);  
                        }  
                    });  
                }else{  
                    jWeixin.scanQRCode({  
                        desc: 'scanQRCode desc',  
                        needResult: 1, // 默认为0,扫描结果由企业微信处理,1则直接返回扫描结果,  
                        scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是条形码(一维码),默认二者都有  
                        success: function(res) {  
                            callback && callback(res);  
                        }  
                    });  
                }  

        },  

//获取进入H5页面的入口环境  
getContext: function(callback) {  
            if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                wx.invoke('getContext', {}, (res) => {  
                    callback && callback(res)  
                });  
            }else{  
                jWeixin.invoke('getContext', {}, (res) => {  
                    callback && callback(res)  
                });  
            }     
        },  
// 选择外部联系人  
selectEContact: function(callback) {  
                if (/(iPhone|iPad|iPod|iOS|macintosh|mac os x)/i.test(ESUtils.wxUntils.system)) {  
                    wx.invoke('selectExternalContact', {  
                        "filterType": 0, //0表示展示全部外部联系人列表,1表示仅展示未曾选择过的外部联系人。默认值为0;除了0与1,其他值非法。在企业微信2.4.22及以后版本支持该参数  
                    }, (res) => {  
                        if (res.err_msg == "selectExternalContact:ok") {  
                            callback && callback(res, 1);  
                        } else {  
                            callback && callback(res, 0);  
                        }  
                    });  
                }else{  
                    jWeixin.invoke('selectExternalContact', {  
                        "filterType": 0, //0表示展示全部外部联系人列表,1表示仅展示未曾选择过的外部联系人。默认值为0;除了0与1,其他值非法。在企业微信2.4.22及以后版本支持该参数  
                    }, (res) => {  
                        if (res.err_msg == "selectExternalContact:ok") {  
                            callback && callback(res, 1);  
                        } else {  
                            callback && callback(res, 0);  
                        }  
                    });  
                }     

        },  

上述为一部分的API封装  

三:页面的使用  

做完所有的准备工作后,我们就需要进行项目的建立了 首先我们需要在页面中进行引入。  

index.vue。  

这个有一个非常重要的信息,我最开始 也是 这样然后再页面引入了但是我通过console 发现 当第一次进入的时候 在ios中无法识别出 agentConfig,一次偶然的时机我通过刷新一次页面后发现就能抽取出来,但是 动态引入只能引入一次,因此我决定更具这个情况进行曲线救国策略。  

在index.vue中  

onload(option){  
      var url = document.location.href;  
      let value = ESUtils.fn.getQueryString("eventType");  
      //每次进入index的我们让他自动刷新一次  
      if(url.indexOf("refreshed=") == -1){  
            var time = new Date();  
            window.location.href=url+"&refreshed="+time.getTime();  
      }           

      this.$nextTick(()=>{  

            let cropId = ESUtils.ESDB.getLoginInfo().cropId;  
            let userId = ESUtils.ESDB.getLoginInfo().id;  
            let url = window.location.href;  
            ESUtils.wxUntils.initJssdkAgent(res=>{  
                //引入并注册SDK  
                WWOpenData.bind(document.querySelectorAll('ww-open-data'));  
                ESUtils.wxUntils.getContext(result=>{  //这是判断是否 从快捷栏进入的 从而跳转指定的页面 并将入口的参数传过去  
                    var loginInfo = ESUtils.ESDB.getLoginInfo();  
                    if(url.indexOf("refreshed=") != -1){  
                        if(option.eventType && option.eventType != ""){  
                            var push = ESPush[option.eventType];  
                            var params = '?pushEventId=' + option.eventId + "&workWX=" + result.entry;    
                            let page = "";  
                            if(ESUtils.ESDB.getLoginInfo().isJava == 1){  
                                page = push.page_java;  
                            }else{  
                                page = push.page;  
                            }  
                                push && uni.navigateTo({  
                                    url: page + params  
                                });  
                            }  
                        }  
                    });  
                })                    
            });     
     }  

通过这样 我们就可以发现  在ios 和安卓中我们都可以进行兼容了 ,不会出现  wx.config 或者 wx.agnetConfig 为undefined的问题了。注意 其他的界面最好不要再调用 initJssdkAgent()方法了  
如果 其他的朋友还有更好的解决方法,可以多多交流
收起阅读 »

mac环境,HBuilder X开发uni-app项目使用安卓模拟器

夜神模拟器

环境

  • mac:12.6
  • cpu:intel

步骤:

  1. 下载并安装夜神模拟器,这是夜神模拟器官网,我安装的是 3.8.5.7。
  2. 按照提示安装并启动夜神模拟器,注意一定要授权。
  3. HBuilder X,设置-运行配置-Android模拟器端口,端口号填写62001。这是官方端口号说明](https://www.yeshen.com/faqs/H15tDZ6YW),也可以通过在终端使用adb devices 来查看端口号。
  4. 运行,运行到安卓手机模拟器基座,这时应该就可以用了。

如果还是不行,检查你的/Applications/NemuPlayer.app/Contents/MacOS目录下是否有adb,hbuderX会默认找这个地址下的adb,adb目录可以在配置接口的地方配置。

说一下为什么不用mumu,我这边运行到mumu一直显示“同步手机端程序文件完成”,不能进入页面。

继续阅读 »

环境

  • mac:12.6
  • cpu:intel

步骤:

  1. 下载并安装夜神模拟器,这是夜神模拟器官网,我安装的是 3.8.5.7。
  2. 按照提示安装并启动夜神模拟器,注意一定要授权。
  3. HBuilder X,设置-运行配置-Android模拟器端口,端口号填写62001。这是官方端口号说明](https://www.yeshen.com/faqs/H15tDZ6YW),也可以通过在终端使用adb devices 来查看端口号。
  4. 运行,运行到安卓手机模拟器基座,这时应该就可以用了。

如果还是不行,检查你的/Applications/NemuPlayer.app/Contents/MacOS目录下是否有adb,hbuderX会默认找这个地址下的adb,adb目录可以在配置接口的地方配置。

说一下为什么不用mumu,我这边运行到mumu一直显示“同步手机端程序文件完成”,不能进入页面。

收起阅读 »

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 }

收起阅读 »