HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

需求

需要一款功能完善 UI美观的一款 拉新APP 功能完善 需求一款取图 壁纸小程序 功能完善 玩法多 有的老板可以联系

需要一款功能完善 UI美观的一款 拉新APP 功能完善 需求一款取图 壁纸小程序 功能完善 玩法多 有的老板可以联系

在 H5 中使用了uni-app 的 editor 组件异常的解决方案

团队踩坑

首发自 ask 社区

如果问题没有解决,请参考这篇文章进行修改。https://ask.dcloud.net.cn/article/40900

如何判断你是否收到影响?

如果你同时符合下面两种情况,你需要仔细阅读:

  • 你使用 uni-app 构建 web 端,也称 H5
  • 在编辑器中搜索 uni-editor 或者 uniEditor 发现有引用

如果你符合上述两条要求,请继续阅读。

问题现象

近期很多用户反馈 <uni-editor /> 组件在 H5 端不能正常使用,表现为组件点击无反应、赋值不生效、控制台有提示资源获取超时等,但之前是好的,近期突然不正常了。

经过 uni-app 团队排查,发现是在 H5 环境下,激活编辑器 editor 组件时候,如果当前环境没有相关依赖,会从 unpkg 上下载资源。

近期部分地区无法正常访问 unpkg 导致组件资源获取不到,最终导致 editor 组件不能正常运行。

我该如何解决?

editor 组件官方文档一直有相关提示,点击这里: https://uniapp.dcloud.net.cn/component/editor.html 在注意事项部分有相关提示。

但很多用户没注意看,这里做个详细的指导,后续会进一步优化相关提示。

这里提供两个方案,供不同用户使用。

方案一:自己管理 CDN 资源 推荐

阅读官方文档,可以看到,有两个 js 需要加载,这里可以下载两个 js 到 static 目录,并在 index.html(Vue 3),或者 public/index.html(Vue2) 中在 head 之间添加 js

vue2 配合 hbuiderx 使用 editor ,demo 地址 https://gitee.com/xiaoxfa/vue2-hbuilderx-playground/tree/q%2Feditor/

<head>  
  <script src="/static/quill-1.3.7.min.js"></script>  
  <script src="/static/image-resize-3.0.1.min.js"></script>  
</head>

这样 editor 就可以正常使用了,代码逻辑中检测到有相关 window.Quill 变量,就不会从远程获取资源。

这种方案比较省事。如果你熟悉 npm 生态,熟悉构建流程的用户,推荐方案二。

方案二:使用 npm 相关依赖

方案一的解决思路是在 window 上进行挂载,同样,我们可以在项目入口 main.js 或者 App.vue中引用相关依赖即可。

基础代码附上,你可以按照这个基础 demo 里的代码进行测试。

https://gitcode.net/xiurensha5731/uni-app-questions/-/tree/q/editor

整体来说,在项目中添加相关依赖:

# 经过测试最新版本 1.3.7 可以运行  
npm i quill@1.3.7  
npm i quill-image-resize-mp

vue3+ts 的代码中这样引用:

<script setup lang="ts">  
declare global {  
  interface Window {  
    Quill: any;  
  }  
}  

// h5 依赖 quill 依赖,这里引入依赖,挂在全局避免去远程引入  
// #ifdef H5  
import ImageResize from "quill-image-resize-mp";  
import Quill from "quill";  

window.Quill = Quill;  
window.ImageResize = { default: ImageResize };  
// #endif  
</script>

这样做的好处是依赖清晰,引用透明。

问题总结

项目中谨慎使用远程 CDN,无论是国外的还是国内的,鼓励使用托管资源,使用 npm 进行管理,保证依赖清晰,引用透明,方便项目长期维护。

继续阅读 »

首发自 ask 社区

如果问题没有解决,请参考这篇文章进行修改。https://ask.dcloud.net.cn/article/40900

如何判断你是否收到影响?

如果你同时符合下面两种情况,你需要仔细阅读:

  • 你使用 uni-app 构建 web 端,也称 H5
  • 在编辑器中搜索 uni-editor 或者 uniEditor 发现有引用

如果你符合上述两条要求,请继续阅读。

问题现象

近期很多用户反馈 <uni-editor /> 组件在 H5 端不能正常使用,表现为组件点击无反应、赋值不生效、控制台有提示资源获取超时等,但之前是好的,近期突然不正常了。

经过 uni-app 团队排查,发现是在 H5 环境下,激活编辑器 editor 组件时候,如果当前环境没有相关依赖,会从 unpkg 上下载资源。

近期部分地区无法正常访问 unpkg 导致组件资源获取不到,最终导致 editor 组件不能正常运行。

我该如何解决?

editor 组件官方文档一直有相关提示,点击这里: https://uniapp.dcloud.net.cn/component/editor.html 在注意事项部分有相关提示。

但很多用户没注意看,这里做个详细的指导,后续会进一步优化相关提示。

这里提供两个方案,供不同用户使用。

方案一:自己管理 CDN 资源 推荐

阅读官方文档,可以看到,有两个 js 需要加载,这里可以下载两个 js 到 static 目录,并在 index.html(Vue 3),或者 public/index.html(Vue2) 中在 head 之间添加 js

vue2 配合 hbuiderx 使用 editor ,demo 地址 https://gitee.com/xiaoxfa/vue2-hbuilderx-playground/tree/q%2Feditor/

<head>  
  <script src="/static/quill-1.3.7.min.js"></script>  
  <script src="/static/image-resize-3.0.1.min.js"></script>  
</head>

这样 editor 就可以正常使用了,代码逻辑中检测到有相关 window.Quill 变量,就不会从远程获取资源。

这种方案比较省事。如果你熟悉 npm 生态,熟悉构建流程的用户,推荐方案二。

方案二:使用 npm 相关依赖

方案一的解决思路是在 window 上进行挂载,同样,我们可以在项目入口 main.js 或者 App.vue中引用相关依赖即可。

基础代码附上,你可以按照这个基础 demo 里的代码进行测试。

https://gitcode.net/xiurensha5731/uni-app-questions/-/tree/q/editor

整体来说,在项目中添加相关依赖:

# 经过测试最新版本 1.3.7 可以运行  
npm i quill@1.3.7  
npm i quill-image-resize-mp

vue3+ts 的代码中这样引用:

<script setup lang="ts">  
declare global {  
  interface Window {  
    Quill: any;  
  }  
}  

// h5 依赖 quill 依赖,这里引入依赖,挂在全局避免去远程引入  
// #ifdef H5  
import ImageResize from "quill-image-resize-mp";  
import Quill from "quill";  

window.Quill = Quill;  
window.ImageResize = { default: ImageResize };  
// #endif  
</script>

这样做的好处是依赖清晰,引用透明。

问题总结

项目中谨慎使用远程 CDN,无论是国外的还是国内的,鼓励使用托管资源,使用 npm 进行管理,保证依赖清晰,引用透明,方便项目长期维护。

收起阅读 »

小程序快速开发模板

分包 微信小程序

uni-app小程序快速开发模板,基于uni+uviewui进行开发。已封装api请求、版本更新提示、分包加载等完善基础功能,是uni-app快速开发小程序的一套基础模板!

插件地址:小程序快速开发模板

小程序分包加载,避免体积超限

小程序分包加载配置

"optimization" : {  
     "subPackages" : true  
}

关于接口请求

封装接口,默认POST/GET请求

小程序新版本更新

版本更新成功/失败

其他

获取手机号登录;快速填充微信头像和昵称;打开微信自带的隐私政策;支持配置微信聊天素材更多打开方式;评价发布组件,可自定义邀请小程序用户评价;

https://ext.dcloud.net.cn/plugin?id=15514

继续阅读 »

uni-app小程序快速开发模板,基于uni+uviewui进行开发。已封装api请求、版本更新提示、分包加载等完善基础功能,是uni-app快速开发小程序的一套基础模板!

插件地址:小程序快速开发模板

小程序分包加载,避免体积超限

小程序分包加载配置

"optimization" : {  
     "subPackages" : true  
}

关于接口请求

封装接口,默认POST/GET请求

小程序新版本更新

版本更新成功/失败

其他

获取手机号登录;快速填充微信头像和昵称;打开微信自带的隐私政策;支持配置微信聊天素材更多打开方式;评价发布组件,可自定义邀请小程序用户评价;

https://ext.dcloud.net.cn/plugin?id=15514

收起阅读 »

uni爬虫抓取小说保存到数据库例子

爬虫 node.js

//抓取网络小说保存到芒果db实例 nodejs 21.14 MongoDB2.11 2023-12-18
//引入第三方库
let axios = require('axios');
let cheerio = require('cheerio');
const {
MongoClient
} = require('mongodb');
const url = 'mongodb://localhost:27017'; // 请根据实际情况修改MongoDB的URL
const dbName = 'xiaoshuo'; // 请替换为你的数据库名称
const client = new MongoClient(url, {
useNewUrlParser: true,
useUnifiedTopology: true
});
//要抓取的小说id
const shuidlist = {
'0': 54255,
'1': 80304,
'2': 54254,
'3': 80292,
'4':20032,
'5':12891,
'6':66513,
'7':104404,
'8':104454,
'9':20312
};
//设置insertOne插入数据函数 insertOne(要插入的数据, 数据库表名)
async function insertOne(data, dbm) {
try {
await client.connect();
const db = client.db(dbName);
const collection = db.collection(dbm);
const result = await collection.insertOne(data);
console.log('插入第' + data.id + '条数据ID为:', result.insertedId);
} catch (err) {
console.error('连接失败或插入数据失败:', err);
} finally {
await client.close();
}
}
//抓取小说封面,return小说章节链接,保存到芒果db
async function getlist(shuid) {
const url = 'https://www.paomov.com/txt' + shuid + '.shtml'; // 替换为你要抓取的小说网页地址
const chapterList = [];
const chapterContents = {};
await axios.get(url).then(res => {
const html = res.data;
const ok = cheerio.load(html);
const fenlei = ok('meta[property="og:novel:category"]').attr('content'); //分类
const biaoti = ok('meta[property="og:novel:book_name"]').attr('content'); //标题
const zuozhe = ok('meta[property="og:novel:author"]').attr('content'); //作者
const tupian = 'https://www.paomov.com' + ok('meta[property="og:image"]').attr('content'); //图片
const shijian = ok('meta[property="og:novel:update_time"]').attr('content'); //时间
const jianjie = ok('meta[property="og:description"]').attr('content'); //简介
const data = {
'feileiid': 1,
'fenlei': fenlei,
'id': shuid 1,
'biaoti': biaoti,
'zuozhe': zuozhe,
'tupian': tupian,
'shijian': shijian,
'jianjie': jianjie
};
insertOne(data, 'aaaa'); //保存封面信息到数据库
ok('div.box_con > div#list a').each((index, element) => {
const chapterTitle = ok(element).text();
const chapterUrl = ok(element).attr('href').replace('//www', 'https://www');
chapterList.push(chapterTitle);
chapterContents[index] = chapterUrl;
});
}).catch(error => {
console.error('抓取封面失败:', error);
process.exit()
})
return chapterContents;
}
//抓取小说章节标题和内容保存到芒果db
async function getcontent(chapterList, i, shuid) {
await axios.get(chapterList).then(res => {
const html = res.data;
const ok = cheerio.load(html);
const biaoti = ok('h1').text(); //章节标题
const neirong = ok('div#content').text().replace(/\s
/g, ''); //章节内容
const data = {
'shuhao': shuid 1,
'id': i
1 + 1 * 1,
'biaoti': biaoti,
'neirong': neirong
};
insertOne(data, 'bbbb'); //保存章节内容到数据库
}).catch(error => {
console.error('抓取内容失败:', error);
process.exit();
})
}
//主函数抓取小说保存到数据库
async function run() {
const moshi = 0; //循环抓取模式,1为测试用
for (var key in shuidlist) {
const list = await getlist(shuidlist[key]);
if (moshi == 0) {
var shuid = shuidlist[key];
for (var i in list) {
//抓取全部
await getcontent(list[i], i, shuidlist[key]);
}
} else {
var shuid = shuidlist[key];
// console.log('小说抓取成功');
// console.log(shuid);
// console.log(list);
// return'1';
//抓取指定,测试用
for (var i = 0; i < 3; i++) {
await getcontent(list[i], i, shuid);
}
}
setTimeout(() => {
console.log('小说抓取成功');
}, 500)
}
setTimeout(() => {
console.log('全部小说抓取成功');
}, 1000)
}
run() //运行主函数

继续阅读 »

//抓取网络小说保存到芒果db实例 nodejs 21.14 MongoDB2.11 2023-12-18
//引入第三方库
let axios = require('axios');
let cheerio = require('cheerio');
const {
MongoClient
} = require('mongodb');
const url = 'mongodb://localhost:27017'; // 请根据实际情况修改MongoDB的URL
const dbName = 'xiaoshuo'; // 请替换为你的数据库名称
const client = new MongoClient(url, {
useNewUrlParser: true,
useUnifiedTopology: true
});
//要抓取的小说id
const shuidlist = {
'0': 54255,
'1': 80304,
'2': 54254,
'3': 80292,
'4':20032,
'5':12891,
'6':66513,
'7':104404,
'8':104454,
'9':20312
};
//设置insertOne插入数据函数 insertOne(要插入的数据, 数据库表名)
async function insertOne(data, dbm) {
try {
await client.connect();
const db = client.db(dbName);
const collection = db.collection(dbm);
const result = await collection.insertOne(data);
console.log('插入第' + data.id + '条数据ID为:', result.insertedId);
} catch (err) {
console.error('连接失败或插入数据失败:', err);
} finally {
await client.close();
}
}
//抓取小说封面,return小说章节链接,保存到芒果db
async function getlist(shuid) {
const url = 'https://www.paomov.com/txt' + shuid + '.shtml'; // 替换为你要抓取的小说网页地址
const chapterList = [];
const chapterContents = {};
await axios.get(url).then(res => {
const html = res.data;
const ok = cheerio.load(html);
const fenlei = ok('meta[property="og:novel:category"]').attr('content'); //分类
const biaoti = ok('meta[property="og:novel:book_name"]').attr('content'); //标题
const zuozhe = ok('meta[property="og:novel:author"]').attr('content'); //作者
const tupian = 'https://www.paomov.com' + ok('meta[property="og:image"]').attr('content'); //图片
const shijian = ok('meta[property="og:novel:update_time"]').attr('content'); //时间
const jianjie = ok('meta[property="og:description"]').attr('content'); //简介
const data = {
'feileiid': 1,
'fenlei': fenlei,
'id': shuid 1,
'biaoti': biaoti,
'zuozhe': zuozhe,
'tupian': tupian,
'shijian': shijian,
'jianjie': jianjie
};
insertOne(data, 'aaaa'); //保存封面信息到数据库
ok('div.box_con > div#list a').each((index, element) => {
const chapterTitle = ok(element).text();
const chapterUrl = ok(element).attr('href').replace('//www', 'https://www');
chapterList.push(chapterTitle);
chapterContents[index] = chapterUrl;
});
}).catch(error => {
console.error('抓取封面失败:', error);
process.exit()
})
return chapterContents;
}
//抓取小说章节标题和内容保存到芒果db
async function getcontent(chapterList, i, shuid) {
await axios.get(chapterList).then(res => {
const html = res.data;
const ok = cheerio.load(html);
const biaoti = ok('h1').text(); //章节标题
const neirong = ok('div#content').text().replace(/\s
/g, ''); //章节内容
const data = {
'shuhao': shuid 1,
'id': i
1 + 1 * 1,
'biaoti': biaoti,
'neirong': neirong
};
insertOne(data, 'bbbb'); //保存章节内容到数据库
}).catch(error => {
console.error('抓取内容失败:', error);
process.exit();
})
}
//主函数抓取小说保存到数据库
async function run() {
const moshi = 0; //循环抓取模式,1为测试用
for (var key in shuidlist) {
const list = await getlist(shuidlist[key]);
if (moshi == 0) {
var shuid = shuidlist[key];
for (var i in list) {
//抓取全部
await getcontent(list[i], i, shuidlist[key]);
}
} else {
var shuid = shuidlist[key];
// console.log('小说抓取成功');
// console.log(shuid);
// console.log(list);
// return'1';
//抓取指定,测试用
for (var i = 0; i < 3; i++) {
await getcontent(list[i], i, shuid);
}
}
setTimeout(() => {
console.log('小说抓取成功');
}, 500)
}
setTimeout(() => {
console.log('全部小说抓取成功');
}, 1000)
}
run() //运行主函数

收起阅读 »

云打包失败,可以试试离线打包。离线打包没那么难,7分钟即可掌握

云打包 离线打包

七分钟掌握离线打包视频演示:

https://www.bilibili.com/video/BV1Xc411o7tk/

安装 uapp 开源工具:

npm install -g uapp

操作教程,可查看文档:

https://github.com/uappkit/uapp

继续阅读 »

七分钟掌握离线打包视频演示:

https://www.bilibili.com/video/BV1Xc411o7tk/

安装 uapp 开源工具:

npm install -g uapp

操作教程,可查看文档:

https://github.com/uappkit/uapp

收起阅读 »

关于DCloud周日晚切换服务器引发问题的公告和总结

公告

故障描述

DCloud服务器在做跨云切换,这是DCloud第一次做这类事情,经验不足,轻视了风险,以为可以无缝切换,所以未提前发公告。

但从12月17日周日晚10点半起,还是造成了部分业务中断或缓慢,尤其是登陆鉴权服务的中断,给当时仍在使用的开发者带来了很大麻烦。

  • 开发者无法登陆HBuilder,鉴权失效,进而造成了无法打包等一系列故障。
  • uniCloud控制台无法登陆,无法更新云函数。

本次切换是提前在新云部署好后再切换的,但还是有2个故障点:

故障时间 故障范围 故障现象 故障原因
故障1 22点半到23点半 所有开发者 无法登陆HX和uniCloud控制台 流量较大,切换不及时
故障2 23点半到1点10分 token失效的HX开发者 点击登陆按钮没有反应 HX连接服务器的备份域名未及时切换,HX主备切换相关代码不完善,备份域名切换后需要重启HX才能恢复登陆

往上追一层的原因分析:

  • 故障1属于跨云切换经验不足。
  • 故障2暴露了相关代码不健壮,后续会升级HX和服务器交互代码来彻底解决主备切换的不完善。

再次说明:DCloud服务器故障不影响开发者的线上业务运行。
包括uniCloud虽然无法登陆管理,但云函数和数据库是在阿里云、腾讯云、支付宝小程序云上的,线上云函数连接不经过DCloud服务器,所以这块不会被影响。

后续改进

开发者肯定会追问,如何避免DCloud服务器以后再次异常导致自己的开发工作受影响?

  1. DCloud会努力保证服务器的正常,跨云切换是非常低频的事件,日常维护方面DCloud从域名到服务器均有主备策略,我们会加大对主备切换相关代码逻辑的完善。
  2. DCloud计划后续更新HX,确保DCloud服务器异常时不影响开发者打包Web和Wgt。
    HX发行时会校验开发者账户和AppId的匹配关系,这是防止黑产盗用正常用户的AppID。而AppId对应着DCloud的很多服务,付费插件、uniCloud、安全网络、加固...
    (当然仅凭这个校验无法防护所有黑产,DCloud还有其他措施配套。)
  3. DCloud一直以来都提供离线打包,App有离线打包,导出Wgt也将确保不因DCloud服务器异常而受影响。uni-app的cli也一直都可以离线发行Web和小程序。

我们无法保证DCloud的服务器一直不出问题,但我们可以保证:

  1. 相关人员吸取了教训,变得更有经验
  2. 相关代码会被仔细review,变得更健壮
  3. 相关制度会更加完善和细化,进一步降低问题出现的概率和破坏性

DCloud是一个负责任的公司,有自己的行为准则,无论您后续是否继续使用DCloud的工具和服务,无论您是否为DCloud支付费用,
这些不影响DCloud将严格要求自身,按照更高质量要求的标准提供服务。

更新:
18日上午及中午,随着开发者流量增加,一批与存储相关的业务并发性能受到影响。
该问题是因为存储文件体积较大,部分文件还未迁移到新云,老云的文件通过外网流量访问,而外网流量带宽不足导致。
随着各处的带宽配置增加,该问题已解决。
由于积压了较多打包排队请求,DCloud已经扩容了打包机,目前打包拥堵已恢复正常。

继续阅读 »

故障描述

DCloud服务器在做跨云切换,这是DCloud第一次做这类事情,经验不足,轻视了风险,以为可以无缝切换,所以未提前发公告。

但从12月17日周日晚10点半起,还是造成了部分业务中断或缓慢,尤其是登陆鉴权服务的中断,给当时仍在使用的开发者带来了很大麻烦。

  • 开发者无法登陆HBuilder,鉴权失效,进而造成了无法打包等一系列故障。
  • uniCloud控制台无法登陆,无法更新云函数。

本次切换是提前在新云部署好后再切换的,但还是有2个故障点:

故障时间 故障范围 故障现象 故障原因
故障1 22点半到23点半 所有开发者 无法登陆HX和uniCloud控制台 流量较大,切换不及时
故障2 23点半到1点10分 token失效的HX开发者 点击登陆按钮没有反应 HX连接服务器的备份域名未及时切换,HX主备切换相关代码不完善,备份域名切换后需要重启HX才能恢复登陆

往上追一层的原因分析:

  • 故障1属于跨云切换经验不足。
  • 故障2暴露了相关代码不健壮,后续会升级HX和服务器交互代码来彻底解决主备切换的不完善。

再次说明:DCloud服务器故障不影响开发者的线上业务运行。
包括uniCloud虽然无法登陆管理,但云函数和数据库是在阿里云、腾讯云、支付宝小程序云上的,线上云函数连接不经过DCloud服务器,所以这块不会被影响。

后续改进

开发者肯定会追问,如何避免DCloud服务器以后再次异常导致自己的开发工作受影响?

  1. DCloud会努力保证服务器的正常,跨云切换是非常低频的事件,日常维护方面DCloud从域名到服务器均有主备策略,我们会加大对主备切换相关代码逻辑的完善。
  2. DCloud计划后续更新HX,确保DCloud服务器异常时不影响开发者打包Web和Wgt。
    HX发行时会校验开发者账户和AppId的匹配关系,这是防止黑产盗用正常用户的AppID。而AppId对应着DCloud的很多服务,付费插件、uniCloud、安全网络、加固...
    (当然仅凭这个校验无法防护所有黑产,DCloud还有其他措施配套。)
  3. DCloud一直以来都提供离线打包,App有离线打包,导出Wgt也将确保不因DCloud服务器异常而受影响。uni-app的cli也一直都可以离线发行Web和小程序。

我们无法保证DCloud的服务器一直不出问题,但我们可以保证:

  1. 相关人员吸取了教训,变得更有经验
  2. 相关代码会被仔细review,变得更健壮
  3. 相关制度会更加完善和细化,进一步降低问题出现的概率和破坏性

DCloud是一个负责任的公司,有自己的行为准则,无论您后续是否继续使用DCloud的工具和服务,无论您是否为DCloud支付费用,
这些不影响DCloud将严格要求自身,按照更高质量要求的标准提供服务。

更新:
18日上午及中午,随着开发者流量增加,一批与存储相关的业务并发性能受到影响。
该问题是因为存储文件体积较大,部分文件还未迁移到新云,老云的文件通过外网流量访问,而外网流量带宽不足导致。
随着各处的带宽配置增加,该问题已解决。
由于积压了较多打包排队请求,DCloud已经扩容了打包机,目前打包拥堵已恢复正常。

收起阅读 »

【跳转到App Store】ios更新,跳转到app store更新

Appstore App iOS

你的AppleID:Apple Developer(发布ios app的地方)--->App信息--->综合信息里面

const url = `itms-apps://itunes.apple.com/cn/app/id${你的AppleID}?mt=8`  
plus.runtime.openURL(url);

↓↓↓ 各位大佬点点赞

继续阅读 »

你的AppleID:Apple Developer(发布ios app的地方)--->App信息--->综合信息里面

const url = `itms-apps://itunes.apple.com/cn/app/id${你的AppleID}?mt=8`  
plus.runtime.openURL(url);

↓↓↓ 各位大佬点点赞

收起阅读 »

关于华为应用市场上架,申请权限未告知目的被驳回问题的简单处理方式(修改篇)

权限

该文章是在https://ask.dcloud.net.cn/article/40875上的补充

var isIos    
// #ifdef APP-PLUS    
isIos = (plus.os.name == "iOS")    
// #endif    
var viewShow = true;  
// 判断推送权限是否开启    
function judgeIosPermissionPush() {    
    var result = false;    
    var UIApplication = plus.ios.import("UIApplication");    
    var app = UIApplication.sharedApplication();    
    var enabledTypes = 0;    
    if (app.currentUserNotificationSettings) {    
        var settings = app.currentUserNotificationSettings();    
        enabledTypes = settings.plusGetAttribute("types");    
        console.log("enabledTypes1:" + enabledTypes);    
        if (enabledTypes == 0) {    
            console.log("推送权限没有开启");    
        } else {    
            result = true;    
            console.log("已经开启推送功能!")    
        }    
        plus.ios.deleteObject(settings);    
    } else {    
        enabledTypes = app.enabledRemoteNotificationTypes();    
        if (enabledTypes == 0) {    
            console.log("推送权限没有开启!");    
        } else {    
            result = true;    
            console.log("已经开启推送功能!")    
        }    
        console.log("enabledTypes2:" + enabledTypes);    
    }    
    plus.ios.deleteObject(app);    
    plus.ios.deleteObject(UIApplication);    
    return result;    
}    

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

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

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

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

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

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

// 判断备忘录权限是否开启    
function judgeIosPermissionMemo() {    
    var result = false;    
    var EKEventStore = plus.ios.import("EKEventStore");    
    var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);    
    if (ekAuthStatus == 3) {    
        result = true;    
        console.log("备忘录权限已经开启");    
    } else {    
        console.log("备忘录权限没有开启");    
    }    
    plus.ios.deleteObject(EKEventStore);    
    return result;    
}    

// Android权限查询    
function requestAndroidPermission(permissionID) {    
    return new Promise((resolve, reject) => {    
        plus.android.requestPermissions(    
            permissionID.split(","),    
            // [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);    
                // 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限    
                // if (result != 1) {    
                // gotoAppPermissionSetting()    
                // }    
            },    
            function(error) {    
                console.log('申请权限错误:' + error.code + " = " + error.message);    
                resolve({    
                    code: error.code,    
                    message: error.message    
                });    
            }    
        );    
    });    
}    

// 使用一个方法,根据参数判断权限    
function judgeIosPermission(permissionID) {    
    if (permissionID == "location") {    
        return judgeIosPermissionLocation()    
    } else if (permissionID == "camera") {    
        return judgeIosPermissionCamera()    
    } else if (permissionID == "photoLibrary") {    
        return judgeIosPermissionPhotoLibrary()    
    } else if (permissionID == "record") {    
        return judgeIosPermissionRecord()    
    } else if (permissionID == "push") {    
        return judgeIosPermissionPush()    
    } else if (permissionID == "contact") {    
        return judgeIosPermissionContact()    
    } else if (permissionID == "calendar") {    
        return judgeIosPermissionCalendar()    
    } else if (permissionID == "memo") {    
        return judgeIosPermissionMemo()    
    }    
    return false;    
}    

// 跳转到**应用**的权限页面    
function gotoAppPermissionSetting() {    
    if (isIos) {    
        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);    
    }    
}    

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

let permissionMap = {    
    "android": {    
        "CAMERA_EXTERNAL_STORAGE": {    
            "name": "android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE,android.permission.CAMERA",    
            "title": "相机/相册权限说明",    
            "content": "便于您使用该功能上传您的照片/图片/视频及用于更换头像、意见反馈上传图片、与客服沟通等场景中读取和写入相册和文件内容"    
        },    
        "CAMERA": {    
            "name": "android.permission.CAMERA",    
            "title": "相机权限说明",    
            "content": "便于您使用该功能拍照更换头像、意见反馈上传图片、与客服沟通等场景中发送拍摄图片"    
        },    
        "EXTERNAL_STORAGE": {    
            "name": "android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE",    
            "title": "相册权限说明",    
            "content": "便于您使用该功能上传您的照片/图片/视频及用于更换头像、意见反馈上传图片、与客服沟通等场景中读取和写入相册和文件内容"    
        }    
    },    
    "ios": {}    
}    

let view = null;    

function showViewDesc(permission) {    
    let plat = isIos ? "ios" : "android";    
    view = new plus.nativeObj.View('per-modal', {    
        top: '0px',    
        left: '0px',    
        width: '100%',    
        backgroundColor: 'rgba(0,0,0,0.2)',    
        //opacity: '.9'       
    })    
    view.drawRect({    
        color: '#fff',    
        radius: '5px'    
    }, {    
        top: '30px',    
        left: '5%',    
        width: '90%',    
        height: "100px",    
    })    
    view.drawText(permissionMap[plat][permission]["title"], {    
        top: '40px',    
        left: "8%",    
        height: "30px"    
    }, {    
        align: "left",    
        color: "#000",    
    }, {    
        onClick: function(e) {    
            console.log(e);    
        }    
    })    
    view.drawText(permissionMap[plat][permission]["content"], {    
        top: '65px',    
        height: "60px",    
        left: "8%",    
        width: "84%"    
    }, {    
        whiteSpace: 'normal',    
        size: "14px",    
        align: "left",    
        color: "#656563"    
    })   
        setTimeout(()=>{  
            if(viewShow) view.show()  
        },200)  
}    

function premissionCheck(permission) {    
    return new Promise(async (resolve, reject) => {    
        let plat = isIos ? "ios" : "android";    
        if (isIos) { // ios    
            // const camera = permission.judgeIosPermission("camera");//判断ios是否给予摄像头权限    
            // //ios相册没权限,系统会自动弹出授权框    
            // //let photoLibrary = permission.judgeIosPermission("photoLibrary");//判断ios是否给予相册权限    
            // if(camera){    
            //     resolve();    
            // }else{    
            //     reject('需要开启相机使用权限');    
            // }    
            resolve(1)    
        } else { // android    
            let permission_arr = permissionMap[plat][permission]["name"].split(",");    
            let flag = true;    
            for(let i = 0;i<permission_arr.length;i++) {    
                let status = plus.navigator.checkPermission(permission_arr[i]);    
                if(status == "undetermined") {    
                    flag = false;    
                }    
            }    
            if (flag == false) { // 未完全授权    
                                showViewDesc(permission);  
                requestAndroidPermission(permissionMap[plat][permission]["name"]).then((res) => {    
                                        viewShow = false;  
                                        setTimeout(()=>{  
                                            viewShow = true;  
                                        },120)  
                    view.close();    
                    if (res == -1) {    
                        uni.showModal({    
                            title: '提示',    
                            content: '操作权限已被拒绝,请手动前往设置',    
                            confirmText: "立即设置",    
                            success: (res) => {    
                                if (res.confirm) {    
                                    gotoAppPermissionSetting()    
                                }    
                            }    
                        })    
                    }    
                    resolve(res)    
                })    
            } else {    
                resolve(1)    
            }    
        }    
    })    
}    

module.exports = {    
    judgeIosPermission: judgeIosPermission,    
    requestAndroidPermission: requestAndroidPermission,    
    checkSystemEnableLocation: checkSystemEnableLocation,    
    gotoAppPermissionSetting: gotoAppPermissionSetting,    
    premissionCheck: premissionCheck    
}
继续阅读 »

该文章是在https://ask.dcloud.net.cn/article/40875上的补充

var isIos    
// #ifdef APP-PLUS    
isIos = (plus.os.name == "iOS")    
// #endif    
var viewShow = true;  
// 判断推送权限是否开启    
function judgeIosPermissionPush() {    
    var result = false;    
    var UIApplication = plus.ios.import("UIApplication");    
    var app = UIApplication.sharedApplication();    
    var enabledTypes = 0;    
    if (app.currentUserNotificationSettings) {    
        var settings = app.currentUserNotificationSettings();    
        enabledTypes = settings.plusGetAttribute("types");    
        console.log("enabledTypes1:" + enabledTypes);    
        if (enabledTypes == 0) {    
            console.log("推送权限没有开启");    
        } else {    
            result = true;    
            console.log("已经开启推送功能!")    
        }    
        plus.ios.deleteObject(settings);    
    } else {    
        enabledTypes = app.enabledRemoteNotificationTypes();    
        if (enabledTypes == 0) {    
            console.log("推送权限没有开启!");    
        } else {    
            result = true;    
            console.log("已经开启推送功能!")    
        }    
        console.log("enabledTypes2:" + enabledTypes);    
    }    
    plus.ios.deleteObject(app);    
    plus.ios.deleteObject(UIApplication);    
    return result;    
}    

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

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

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

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

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

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

// 判断备忘录权限是否开启    
function judgeIosPermissionMemo() {    
    var result = false;    
    var EKEventStore = plus.ios.import("EKEventStore");    
    var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);    
    if (ekAuthStatus == 3) {    
        result = true;    
        console.log("备忘录权限已经开启");    
    } else {    
        console.log("备忘录权限没有开启");    
    }    
    plus.ios.deleteObject(EKEventStore);    
    return result;    
}    

// Android权限查询    
function requestAndroidPermission(permissionID) {    
    return new Promise((resolve, reject) => {    
        plus.android.requestPermissions(    
            permissionID.split(","),    
            // [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);    
                // 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限    
                // if (result != 1) {    
                // gotoAppPermissionSetting()    
                // }    
            },    
            function(error) {    
                console.log('申请权限错误:' + error.code + " = " + error.message);    
                resolve({    
                    code: error.code,    
                    message: error.message    
                });    
            }    
        );    
    });    
}    

// 使用一个方法,根据参数判断权限    
function judgeIosPermission(permissionID) {    
    if (permissionID == "location") {    
        return judgeIosPermissionLocation()    
    } else if (permissionID == "camera") {    
        return judgeIosPermissionCamera()    
    } else if (permissionID == "photoLibrary") {    
        return judgeIosPermissionPhotoLibrary()    
    } else if (permissionID == "record") {    
        return judgeIosPermissionRecord()    
    } else if (permissionID == "push") {    
        return judgeIosPermissionPush()    
    } else if (permissionID == "contact") {    
        return judgeIosPermissionContact()    
    } else if (permissionID == "calendar") {    
        return judgeIosPermissionCalendar()    
    } else if (permissionID == "memo") {    
        return judgeIosPermissionMemo()    
    }    
    return false;    
}    

// 跳转到**应用**的权限页面    
function gotoAppPermissionSetting() {    
    if (isIos) {    
        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);    
    }    
}    

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

let permissionMap = {    
    "android": {    
        "CAMERA_EXTERNAL_STORAGE": {    
            "name": "android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE,android.permission.CAMERA",    
            "title": "相机/相册权限说明",    
            "content": "便于您使用该功能上传您的照片/图片/视频及用于更换头像、意见反馈上传图片、与客服沟通等场景中读取和写入相册和文件内容"    
        },    
        "CAMERA": {    
            "name": "android.permission.CAMERA",    
            "title": "相机权限说明",    
            "content": "便于您使用该功能拍照更换头像、意见反馈上传图片、与客服沟通等场景中发送拍摄图片"    
        },    
        "EXTERNAL_STORAGE": {    
            "name": "android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE",    
            "title": "相册权限说明",    
            "content": "便于您使用该功能上传您的照片/图片/视频及用于更换头像、意见反馈上传图片、与客服沟通等场景中读取和写入相册和文件内容"    
        }    
    },    
    "ios": {}    
}    

let view = null;    

function showViewDesc(permission) {    
    let plat = isIos ? "ios" : "android";    
    view = new plus.nativeObj.View('per-modal', {    
        top: '0px',    
        left: '0px',    
        width: '100%',    
        backgroundColor: 'rgba(0,0,0,0.2)',    
        //opacity: '.9'       
    })    
    view.drawRect({    
        color: '#fff',    
        radius: '5px'    
    }, {    
        top: '30px',    
        left: '5%',    
        width: '90%',    
        height: "100px",    
    })    
    view.drawText(permissionMap[plat][permission]["title"], {    
        top: '40px',    
        left: "8%",    
        height: "30px"    
    }, {    
        align: "left",    
        color: "#000",    
    }, {    
        onClick: function(e) {    
            console.log(e);    
        }    
    })    
    view.drawText(permissionMap[plat][permission]["content"], {    
        top: '65px',    
        height: "60px",    
        left: "8%",    
        width: "84%"    
    }, {    
        whiteSpace: 'normal',    
        size: "14px",    
        align: "left",    
        color: "#656563"    
    })   
        setTimeout(()=>{  
            if(viewShow) view.show()  
        },200)  
}    

function premissionCheck(permission) {    
    return new Promise(async (resolve, reject) => {    
        let plat = isIos ? "ios" : "android";    
        if (isIos) { // ios    
            // const camera = permission.judgeIosPermission("camera");//判断ios是否给予摄像头权限    
            // //ios相册没权限,系统会自动弹出授权框    
            // //let photoLibrary = permission.judgeIosPermission("photoLibrary");//判断ios是否给予相册权限    
            // if(camera){    
            //     resolve();    
            // }else{    
            //     reject('需要开启相机使用权限');    
            // }    
            resolve(1)    
        } else { // android    
            let permission_arr = permissionMap[plat][permission]["name"].split(",");    
            let flag = true;    
            for(let i = 0;i<permission_arr.length;i++) {    
                let status = plus.navigator.checkPermission(permission_arr[i]);    
                if(status == "undetermined") {    
                    flag = false;    
                }    
            }    
            if (flag == false) { // 未完全授权    
                                showViewDesc(permission);  
                requestAndroidPermission(permissionMap[plat][permission]["name"]).then((res) => {    
                                        viewShow = false;  
                                        setTimeout(()=>{  
                                            viewShow = true;  
                                        },120)  
                    view.close();    
                    if (res == -1) {    
                        uni.showModal({    
                            title: '提示',    
                            content: '操作权限已被拒绝,请手动前往设置',    
                            confirmText: "立即设置",    
                            success: (res) => {    
                                if (res.confirm) {    
                                    gotoAppPermissionSetting()    
                                }    
                            }    
                        })    
                    }    
                    resolve(res)    
                })    
            } else {    
                resolve(1)    
            }    
        }    
    })    
}    

module.exports = {    
    judgeIosPermission: judgeIosPermission,    
    requestAndroidPermission: requestAndroidPermission,    
    checkSystemEnableLocation: checkSystemEnableLocation,    
    gotoAppPermissionSetting: gotoAppPermissionSetting,    
    premissionCheck: premissionCheck    
}
收起阅读 »

uniapp video H5页面设置srcObject分享

h5 stream video

需要先获取到组件video,直接获取绑定的id在uniapp中获取到的是uni-video标签,真正的标签是uni-video内包裹的video标签,要给内层的标签进行设置
代码如下:

<video id="video-dom"></video>

const vidoDom = document.querySelector('#video-dom video')
vidoDom.srcObject = stream
vidoDom.play();

珍惜成果,热心分享,给个

继续阅读 »

需要先获取到组件video,直接获取绑定的id在uniapp中获取到的是uni-video标签,真正的标签是uni-video内包裹的video标签,要给内层的标签进行设置
代码如下:

<video id="video-dom"></video>

const vidoDom = document.querySelector('#video-dom video')
vidoDom.srcObject = stream
vidoDom.play();

珍惜成果,热心分享,给个

收起阅读 »

全局搜索了个uni.requireNativePlugin,直接吧Hbuilder卡死,重启电脑再次打开依然卡死。

卡慢 HBuilderX

全局搜索了个uni.requireNativePlugin,直接吧Hbuilder卡死,重启电脑再次打开依然卡死。
写代码写上一会就特别卡。

全局搜索了个uni.requireNativePlugin,直接吧Hbuilder卡死,重启电脑再次打开依然卡死。
写代码写上一会就特别卡。

2023年11月阿里云版uniCloud故障说明及赔付方案公告

uniCloud

2023年11月份,阿里云版的uniCloud发生了3次故障,DCloud紧急联系阿里云快速治理恢复。

故障后,阿里云内部也及时做了复盘,现将故障原因及改进措施同步如下:

针对此次故障,依据相关SLA协议,DCloud将执行如下赔付方案:

  • 2023年11月,开发者实际消耗阿里云版uniCloud服务费用总额的10%,DCloud以代金券方式返还给开发者
  • DCloud会在1个月内(2024年1月12日之前),支持在购买包月套餐及按量扣费时,使用代金券

DCloud会继续督促阿里云切实提升服务质量,增强安全防控,确保服务的健壮稳定。

继续阅读 »

2023年11月份,阿里云版的uniCloud发生了3次故障,DCloud紧急联系阿里云快速治理恢复。

故障后,阿里云内部也及时做了复盘,现将故障原因及改进措施同步如下:

针对此次故障,依据相关SLA协议,DCloud将执行如下赔付方案:

  • 2023年11月,开发者实际消耗阿里云版uniCloud服务费用总额的10%,DCloud以代金券方式返还给开发者
  • DCloud会在1个月内(2024年1月12日之前),支持在购买包月套餐及按量扣费时,使用代金券

DCloud会继续督促阿里云切实提升服务质量,增强安全防控,确保服务的健壮稳定。

收起阅读 »

【UniAPP X快速上手】如何使用UniAPPX开发一个原生安卓商城

uniapp 教程 uniapp插件 uniapp模板

什么是UniAPP X

uni-app x,是下一代 uni-app,是一个跨平台应用开发引擎。

uni-app x 没有使用js和webview,它基于 uts 语言。在App端,uts在iOS编译为swift、在Android编译为kotlin,完全达到了原生应用的功能、性能。
更重要的是,UniAPP X未来也会支持鸿蒙,这里引用DCloud社区的官方答复
> DCloud有资源第一时间得到鸿蒙无apk手机的上市计划。我们和华为保持着紧密沟通,会把握好节奏,不用担心。大家可以观察一个信号,等微信的鸿蒙next版敲定了,鸿蒙无apk手机就可以明确上市计划了。鸿蒙的开发语言是arkTS,uni-app x是uts,都是变种ts,uni-app x编译到鸿蒙是靠谱的。
而把uni-app的js编译成arkTS不靠谱。除非编个h5跑在鸿蒙的webview里,但这个体验估计没多少人能接受。

如何学习UniAPP X

1. 学习官方文档

这里的重点和难点应该是UTS语言,因为UTS语言需要编译成Kotlin语言,所以有强类型要求,会TypeScript的同学适应起来会快一些,只是习惯js的同学可能上手会难,可以参考官方组件库easyX的写法,多多学习。

2. 学习UTS语言

UTS语言是TS的变种,建议先学习TS。就连鸿蒙原生开发用的ArkTS也是TS的变种(笔者最近在开发鸿蒙原生应用,会TS上手很快),可见TS的重要性。学习TS可以参考如下教程:
TypeScript 教程

上手TS之后,需要重点关注UTS和TS的区别。

  • UTS为Kotlin和Swift新增了一些专有数据类型;
  • UTS有一些特殊的内置对象和API

其实这些区别主要是为了原生开发的,但是比直接上手安卓和iOS原生开发要简单太多。

3. 关注UniAPP X的Vue规范

uni-app x的vue规范,按照vue3规范实现,但目前不支持setup组合式写法,仅支持option选项式写法。而且有些Vue3的api也不支持。大家在开发过程中遇到问题,要查看官方文档里重点列出的差异
UniAPP X的Vue实现

原生安卓商城的开发

前面的学习铺垫,是为了大家开发过程更加顺利。实际上,easyX已经封装好了常见的电商业务组件,并且已经上线了一些模板。即使是小白,也可以直接用easyX开发出一个原生商城。先看效果
商城首页模板
这个页面的代码可以查看Gitee示例代码, 大家直接复制粘贴即可。

使用easyX组件库和模板库

使用easyX有两种方式,一种是从Gitee下载源码,另一种是从UniAPP插件市场下载源码

下载源码之后解压,找到components目录,将该目录下的所有内容复制到你的UniAPP X项目下的components目录即可使用。
eaxyX支持按需引用,各个组件的具体使用可以查看相关easyX组件文档

当然,您也可以直接下载安卓easyX的安卓apk体验。

easyX演示包下载安装链接

easyX采用MIT协议开源,组件库和模板库仍在不断扩充。对于开发原生商城应用的同学来说,是个宝藏库。等UniAPPX支持鸿蒙后,easyX用来开发鸿蒙原生商城,想想真是吊炸天的开发利器。

应用打包

安卓打包是需要证书的。如果想省事,直接使用Dcloud的公共云证书,傻瓜式操作,不过因为有人滥用这个证书,现在很多品牌的手机会报毒,所以还是建议自己申请证书。
免费证书申请攻略

应用上线

如果要上架到华为、小米、oppo等应用市场,那就需要软件著作权和应用备案了。软件著作权的申请周期比较长,大概3个月,应用备案就快很多了。网上很多服务商提供这两项服务,大家也可以私信我,公司业务可以按照市场最低价给大家服务,个人业务可以提供一些免费指导然后你自己操作。

我是刘明,十年创业老兵,开源技术爱好者。
有问题欢迎私信。

继续阅读 »

什么是UniAPP X

uni-app x,是下一代 uni-app,是一个跨平台应用开发引擎。

uni-app x 没有使用js和webview,它基于 uts 语言。在App端,uts在iOS编译为swift、在Android编译为kotlin,完全达到了原生应用的功能、性能。
更重要的是,UniAPP X未来也会支持鸿蒙,这里引用DCloud社区的官方答复
> DCloud有资源第一时间得到鸿蒙无apk手机的上市计划。我们和华为保持着紧密沟通,会把握好节奏,不用担心。大家可以观察一个信号,等微信的鸿蒙next版敲定了,鸿蒙无apk手机就可以明确上市计划了。鸿蒙的开发语言是arkTS,uni-app x是uts,都是变种ts,uni-app x编译到鸿蒙是靠谱的。
而把uni-app的js编译成arkTS不靠谱。除非编个h5跑在鸿蒙的webview里,但这个体验估计没多少人能接受。

如何学习UniAPP X

1. 学习官方文档

这里的重点和难点应该是UTS语言,因为UTS语言需要编译成Kotlin语言,所以有强类型要求,会TypeScript的同学适应起来会快一些,只是习惯js的同学可能上手会难,可以参考官方组件库easyX的写法,多多学习。

2. 学习UTS语言

UTS语言是TS的变种,建议先学习TS。就连鸿蒙原生开发用的ArkTS也是TS的变种(笔者最近在开发鸿蒙原生应用,会TS上手很快),可见TS的重要性。学习TS可以参考如下教程:
TypeScript 教程

上手TS之后,需要重点关注UTS和TS的区别。

  • UTS为Kotlin和Swift新增了一些专有数据类型;
  • UTS有一些特殊的内置对象和API

其实这些区别主要是为了原生开发的,但是比直接上手安卓和iOS原生开发要简单太多。

3. 关注UniAPP X的Vue规范

uni-app x的vue规范,按照vue3规范实现,但目前不支持setup组合式写法,仅支持option选项式写法。而且有些Vue3的api也不支持。大家在开发过程中遇到问题,要查看官方文档里重点列出的差异
UniAPP X的Vue实现

原生安卓商城的开发

前面的学习铺垫,是为了大家开发过程更加顺利。实际上,easyX已经封装好了常见的电商业务组件,并且已经上线了一些模板。即使是小白,也可以直接用easyX开发出一个原生商城。先看效果
商城首页模板
这个页面的代码可以查看Gitee示例代码, 大家直接复制粘贴即可。

使用easyX组件库和模板库

使用easyX有两种方式,一种是从Gitee下载源码,另一种是从UniAPP插件市场下载源码

下载源码之后解压,找到components目录,将该目录下的所有内容复制到你的UniAPP X项目下的components目录即可使用。
eaxyX支持按需引用,各个组件的具体使用可以查看相关easyX组件文档

当然,您也可以直接下载安卓easyX的安卓apk体验。

easyX演示包下载安装链接

easyX采用MIT协议开源,组件库和模板库仍在不断扩充。对于开发原生商城应用的同学来说,是个宝藏库。等UniAPPX支持鸿蒙后,easyX用来开发鸿蒙原生商城,想想真是吊炸天的开发利器。

应用打包

安卓打包是需要证书的。如果想省事,直接使用Dcloud的公共云证书,傻瓜式操作,不过因为有人滥用这个证书,现在很多品牌的手机会报毒,所以还是建议自己申请证书。
免费证书申请攻略

应用上线

如果要上架到华为、小米、oppo等应用市场,那就需要软件著作权和应用备案了。软件著作权的申请周期比较长,大概3个月,应用备案就快很多了。网上很多服务商提供这两项服务,大家也可以私信我,公司业务可以按照市场最低价给大家服务,个人业务可以提供一些免费指导然后你自己操作。

我是刘明,十年创业老兵,开源技术爱好者。
有问题欢迎私信。

收起阅读 »