【解决】APP(ios端)没有Promise.allSettled
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);
};
}
来源:有点久远,百度的
↓↓↓ 各位大佬点点赞
收起阅读 »Wot Design Uni 增加 Sidebar 侧边栏组件 ,赶快进来看看效果吧!
Sidebar 侧边栏
垂直展示的导航栏,用于在不同的内容区域之间进行切换。
地址
先看交互效果
基础用法
通过 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 |
地址
Sidebar 侧边栏
垂直展示的导航栏,用于在不同的内容区域之间进行切换。
地址
先看交互效果
基础用法
通过 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 |
地址
收起阅读 »uniapp创建企微在ios端wx.config 以及wx.agentConfig问题的完美解决
之前写过一篇解决的文章 但是 后续发现还是有那么一点问题 就是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
步骤:
- 下载并安装夜神模拟器,这是夜神模拟器官网,我安装的是 3.8.5.7。
- 按照提示安装并启动夜神模拟器,注意一定要授权。
- HBuilder X,设置-运行配置-Android模拟器端口,端口号填写62001。这是官方端口号说明](https://www.yeshen.com/faqs/H15tDZ6YW),也可以通过在终端使用
adb devices来查看端口号。 - 运行,运行到安卓手机模拟器基座,这时应该就可以用了。
如果还是不行,检查你的/Applications/NemuPlayer.app/Contents/MacOS目录下是否有adb,hbuderX会默认找这个地址下的adb,adb目录可以在配置接口的地方配置。
说一下为什么不用mumu,我这边运行到mumu一直显示“同步手机端程序文件完成”,不能进入页面。
环境:
- mac:12.6
- cpu:intel
步骤:
- 下载并安装夜神模拟器,这是夜神模拟器官网,我安装的是 3.8.5.7。
- 按照提示安装并启动夜神模拟器,注意一定要授权。
- HBuilder X,设置-运行配置-Android模拟器端口,端口号填写62001。这是官方端口号说明](https://www.yeshen.com/faqs/H15tDZ6YW),也可以通过在终端使用
adb devices来查看端口号。 - 运行,运行到安卓手机模拟器基座,这时应该就可以用了。
如果还是不行,检查你的/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 之外的未知来源下载或安装应用程序的代码”的解决方案
使用HBuilderX 3.8.7以上版本打包上架google play的应用是正常的不受影响。
根据目前统计到的信息,审核被拒一般是以下两种情况:
- 审核被拒的版本号为较老版本号,并不是本次升级对应的版本号。
- 打包使用的HBuilderX的为3.8.7以前的版本。
解决方案:
- 升级HBuilderX到最新版并重新打包上架。
- 将所有发布过版本的轨道都用最新的HBuilderX重新打包上架,具体步骤参考下图(截图来自于google play邮件,具体可以参考Google Play审核邮件完整内容)。
注意:发版时在预览并确认页面将分阶段发布百分比填100%,适用的国家/地区选择发布到所有目标国家/地区。确保所有用户都能升级到最新版。参考下图。
如果按照上述方式上架仍然被拒。可以选择申诉。
申诉方式:点击政策状态->“设备和网络滥用”政策:违反“设备和网络滥用”政策,点击最下方申诉按钮,等待申诉结果。
<!--目前接收到的结果分三类:
- 申诉通过。
- 申诉失败,但是过段时间刷新google play后台显示通过。
- 申诉失败,重新提交上架审核,上架成功。
申诉结果可能会失败,但是目前所有的应用后续的上架审核都成功了。-->
使用HBuilderX 3.8.7以上版本打包上架google play的应用是正常的不受影响。
根据目前统计到的信息,审核被拒一般是以下两种情况:
- 审核被拒的版本号为较老版本号,并不是本次升级对应的版本号。
- 打包使用的HBuilderX的为3.8.7以前的版本。
解决方案:
- 升级HBuilderX到最新版并重新打包上架。
- 将所有发布过版本的轨道都用最新的HBuilderX重新打包上架,具体步骤参考下图(截图来自于google play邮件,具体可以参考Google Play审核邮件完整内容)。
注意:发版时在预览并确认页面将分阶段发布百分比填100%,适用的国家/地区选择发布到所有目标国家/地区。确保所有用户都能升级到最新版。参考下图。
如果按照上述方式上架仍然被拒。可以选择申诉。
申诉方式:点击政策状态->“设备和网络滥用”政策:违反“设备和网络滥用”政策,点击最下方申诉按钮,等待申诉结果。
<!--目前接收到的结果分三类:
- 申诉通过。
- 申诉失败,但是过段时间刷新google play后台显示通过。
- 申诉失败,重新提交上架审核,上架成功。
申诉结果可能会失败,但是目前所有的应用后续的上架审核都成功了。-->
收起阅读 »华为审核申请权限告知目的
华为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 ?解决经验分享
全局搜索??运算符,使用||替代??运算符,或更改为其他方式进行运算
全局搜索??运算符,使用||替代??运算符,或更改为其他方式进行运算
uniapp 蓝牙无法监听多个特征的问题解决办法
问题描述:
在开发app时,项目需要蓝牙同时订阅某个服务下的多个特征值的变化时,直接使用uni.notifyBLECharacteristicValueChange只能订阅一个特征值的变化。
具体实现:
1.将 this.onBLECharacteristicValueChange();放到onLoad()中注册,确保该方法只注册一次,如果该方法注册了多次,蓝牙发送的数据会重复上传。
- 在蓝牙的订阅函数notifyBLECharacteristicValueChange()中设置定时器来轮流订阅蓝牙的多个特征值,具体如下:
-
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()中注册,确保该方法只注册一次,如果该方法注册了多次,蓝牙发送的数据会重复上传。
- 在蓝牙的订阅函数notifyBLECharacteristicValueChange()中设置定时器来轮流订阅蓝牙的多个特征值,具体如下:
-
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执行一次订阅 },











