HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

【解决】APP(ios端)没有Promise.allSettled

uniapp iOS
if (!Promise.allSettled) {  
    const rejectHandler = reason => ({ status: 'rejected', reason });  
    const resolveHandler = value => ({ status: 'fulfilled', value });  
    Promise.allSettled = function (promises) {  
    const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));  
        return Promise.all(convertedPromises);  
    };  
}

来源:有点久远,百度的

↓↓↓ 各位大佬点点赞

继续阅读 »
if (!Promise.allSettled) {  
    const rejectHandler = reason => ({ status: 'rejected', reason });  
    const resolveHandler = value => ({ status: 'fulfilled', value });  
    Promise.allSettled = function (promises) {  
    const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));  
        return Promise.all(convertedPromises);  
    };  
}

来源:有点久远,百度的

↓↓↓ 各位大佬点点赞

收起阅读 »

3.96 这个搜索是不是有问题

HBuilderX

3.96 这个搜索是不是有问题

3.96 这个搜索是不是有问题

Wot Design Uni 增加 Sidebar 侧边栏组件 ,赶快进来看看效果吧!

uniapp

Sidebar 侧边栏

垂直展示的导航栏,用于在不同的内容区域之间进行切换。

地址

Github
文档网站
插件市场
QQ群

先看交互效果

基础用法

通过 v-model 绑定当前选中项的索引。

<wd-sidebar v-model="active">  
  <wd-sidebar-item :value="0" label="标签名称" />  
  <wd-sidebar-item :value="1" label="标签名称" />  
  <wd-sidebar-item :value="2" label="标签名称" />  
</wd-sidebar>
const active = ref(0)

徽标提示

设置 is-dot 属性后,会在右上角展示一个小红点;设置 badge 属性后,会在右上角展示相应的徽标。

<wd-sidebar v-model="active">  
  <wd-sidebar-item :value="0" label="标签名称" is-dot />  
  <wd-sidebar-item :value="1" label="标签名称" badge="5" />  
  <wd-sidebar-item :value="2" label="标签名称" />  
</wd-sidebar>

禁用选项

通过 disabled 属性禁用选项。

<wd-sidebar v-model="active">  
  <wd-sidebar-item :value="0" label="标签名称" />  
  <wd-sidebar-item :value="1" label="标签名称" disabled />  
  <wd-sidebar-item :value="2" label="标签名称" />  
</wd-sidebar>

监听切换事件

设置 change 方法来监听切换导航项时的事件。

<wd-sidebar v-model="active" @change="handleChange">  
  <wd-sidebar-item :value="0" label="标签名称 1" />  
  <wd-sidebar-item :value="1" label="标签名称 2" />  
  <wd-sidebar-item :value="2" label="标签名称 3" />  
</wd-sidebar>
import { useToast } from '@/uni_modules/wot-design-uni'  

const toast = useToast()  
const active = ref<number>(1)  

function handleChange({ value, label }) {  
  toast.show(`当前标签名 ${label}`)  
}

锚点用法示例

sidebar组件的锚点用法可以帮助用户在长页面上快速导航到特定的部分。
见文档

切换页面用法示例

sidebar组件在每次切换激活项时,跳转到指定的页面,且无法通过滚动导航到下一个sidebar项。

见文档

Attributes

参数 说明 类型 可选值 默认值 最低版本
modelValue/v-model 当前导航项的索引 string | number - 0 0.1.49

Events

事件名称 说明 参数 最低版本
change 选项切换时触发 (value: number \| string, label: string) 0.1.49

Slots

name 说明 参数 最低版本
default 自定义展示 - 0.1.49

外部样式类

类名 说明 最低版本
customStyle 自定义样式 0.1.49
customClass 自定义样式类 0.1.49

SidebarItem Attributes

参数 说明 类型 可选值 默认值 最低版本
label 当前选项标题 string - - 0.1.49
value 当前选项的值,唯一标识 number \| string - - 0.1.49
badge 徽标显示值 number \| string \| null - - 0.1.49
icon 图标 string - - 0.1.49
isDot 是否点状徽标 boolean - false 0.1.49
max 徽标最大值 number - 99 0.1.49
disabled 是否禁用 boolean - false 0.1.49

SidebarItem Slots

name 说明 参数 最低版本
icon 自定义图标 - 0.1.49

SidebarItem 外部样式类

类名 说明 最低版本
customStyle 自定义样式 0.1.49
customClass 自定义样式类 0.1.49

地址

Github
文档网站
插件市场
QQ群

继续阅读 »

Sidebar 侧边栏

垂直展示的导航栏,用于在不同的内容区域之间进行切换。

地址

Github
文档网站
插件市场
QQ群

先看交互效果

基础用法

通过 v-model 绑定当前选中项的索引。

<wd-sidebar v-model="active">  
  <wd-sidebar-item :value="0" label="标签名称" />  
  <wd-sidebar-item :value="1" label="标签名称" />  
  <wd-sidebar-item :value="2" label="标签名称" />  
</wd-sidebar>
const active = ref(0)

徽标提示

设置 is-dot 属性后,会在右上角展示一个小红点;设置 badge 属性后,会在右上角展示相应的徽标。

<wd-sidebar v-model="active">  
  <wd-sidebar-item :value="0" label="标签名称" is-dot />  
  <wd-sidebar-item :value="1" label="标签名称" badge="5" />  
  <wd-sidebar-item :value="2" label="标签名称" />  
</wd-sidebar>

禁用选项

通过 disabled 属性禁用选项。

<wd-sidebar v-model="active">  
  <wd-sidebar-item :value="0" label="标签名称" />  
  <wd-sidebar-item :value="1" label="标签名称" disabled />  
  <wd-sidebar-item :value="2" label="标签名称" />  
</wd-sidebar>

监听切换事件

设置 change 方法来监听切换导航项时的事件。

<wd-sidebar v-model="active" @change="handleChange">  
  <wd-sidebar-item :value="0" label="标签名称 1" />  
  <wd-sidebar-item :value="1" label="标签名称 2" />  
  <wd-sidebar-item :value="2" label="标签名称 3" />  
</wd-sidebar>
import { useToast } from '@/uni_modules/wot-design-uni'  

const toast = useToast()  
const active = ref<number>(1)  

function handleChange({ value, label }) {  
  toast.show(`当前标签名 ${label}`)  
}

锚点用法示例

sidebar组件的锚点用法可以帮助用户在长页面上快速导航到特定的部分。
见文档

切换页面用法示例

sidebar组件在每次切换激活项时,跳转到指定的页面,且无法通过滚动导航到下一个sidebar项。

见文档

Attributes

参数 说明 类型 可选值 默认值 最低版本
modelValue/v-model 当前导航项的索引 string | number - 0 0.1.49

Events

事件名称 说明 参数 最低版本
change 选项切换时触发 (value: number \| string, label: string) 0.1.49

Slots

name 说明 参数 最低版本
default 自定义展示 - 0.1.49

外部样式类

类名 说明 最低版本
customStyle 自定义样式 0.1.49
customClass 自定义样式类 0.1.49

SidebarItem Attributes

参数 说明 类型 可选值 默认值 最低版本
label 当前选项标题 string - - 0.1.49
value 当前选项的值,唯一标识 number \| string - - 0.1.49
badge 徽标显示值 number \| string \| null - - 0.1.49
icon 图标 string - - 0.1.49
isDot 是否点状徽标 boolean - false 0.1.49
max 徽标最大值 number - 99 0.1.49
disabled 是否禁用 boolean - false 0.1.49

SidebarItem Slots

name 说明 参数 最低版本
icon 自定义图标 - 0.1.49

SidebarItem 外部样式类

类名 说明 最低版本
customStyle 自定义样式 0.1.49
customClass 自定义样式类 0.1.49

地址

Github
文档网站
插件市场
QQ群

收起阅读 »

记录一个神奇的reportJSException问题,希望以后可以帮到有缘人

直接上图,区别很明显,一个图多了一个空行然后就报错了

看报错

直接上图,区别很明显,一个图多了一个空行然后就报错了

看报错

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执行一次订阅  
        },
收起阅读 »