
在 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
}
关于接口请求
小程序新版本更新
其他
获取手机号登录;快速填充微信头像和昵称;打开微信自带的隐私政策;支持配置微信聊天素材更多打开方式;评价发布组件,可自定义邀请小程序用户评价;
uni-app小程序快速开发模板,基于uni+uviewui进行开发。已封装api请求、版本更新提示、分包加载等完善基础功能,是uni-app快速开发小程序的一套基础模板!
插件地址:小程序快速开发模板
小程序分包加载,避免体积超限
"optimization" : {
"subPackages" : true
}
关于接口请求
小程序新版本更新
其他
获取手机号登录;快速填充微信头像和昵称;打开微信自带的隐私政策;支持配置微信聊天素材更多打开方式;评价发布组件,可自定义邀请小程序用户评价;
https://ext.dcloud.net.cn/plugin?id=15514
收起阅读 »
uni爬虫抓取小说保存到数据库例子

//抓取网络小说保存到芒果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://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服务器以后再次异常导致自己的开发工作受影响?
- DCloud会努力保证服务器的正常,跨云切换是非常低频的事件,日常维护方面DCloud从域名到服务器均有主备策略,我们会加大对主备切换相关代码逻辑的完善。
- DCloud计划后续更新HX,确保DCloud服务器异常时不影响开发者打包Web和Wgt。
HX发行时会校验开发者账户和AppId的匹配关系,这是防止黑产盗用正常用户的AppID。而AppId对应着DCloud的很多服务,付费插件、uniCloud、安全网络、加固...
(当然仅凭这个校验无法防护所有黑产,DCloud还有其他措施配套。) - DCloud一直以来都提供离线打包,App有离线打包,导出Wgt也将确保不因DCloud服务器异常而受影响。uni-app的cli也一直都可以离线发行Web和小程序。
我们无法保证DCloud的服务器一直不出问题,但我们可以保证:
- 相关人员吸取了教训,变得更有经验
- 相关代码会被仔细review,变得更健壮
- 相关制度会更加完善和细化,进一步降低问题出现的概率和破坏性
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服务器以后再次异常导致自己的开发工作受影响?
- DCloud会努力保证服务器的正常,跨云切换是非常低频的事件,日常维护方面DCloud从域名到服务器均有主备策略,我们会加大对主备切换相关代码逻辑的完善。
- DCloud计划后续更新HX,确保DCloud服务器异常时不影响开发者打包Web和Wgt。
HX发行时会校验开发者账户和AppId的匹配关系,这是防止黑产盗用正常用户的AppID。而AppId对应着DCloud的很多服务,付费插件、uniCloud、安全网络、加固...
(当然仅凭这个校验无法防护所有黑产,DCloud还有其他措施配套。) - DCloud一直以来都提供离线打包,App有离线打包,导出Wgt也将确保不因DCloud服务器异常而受影响。uni-app的cli也一直都可以离线发行Web和小程序。
我们无法保证DCloud的服务器一直不出问题,但我们可以保证:
- 相关人员吸取了教训,变得更有经验
- 相关代码会被仔细review,变得更健壮
- 相关制度会更加完善和细化,进一步降低问题出现的概率和破坏性
DCloud是一个负责任的公司,有自己的行为准则,无论您后续是否继续使用DCloud的工具和服务,无论您是否为DCloud支付费用,
这些不影响DCloud将严格要求自身,按照更高质量要求的标准提供服务。
更新:
18日上午及中午,随着开发者流量增加,一批与存储相关的业务并发性能受到影响。
该问题是因为存储文件体积较大,部分文件还未迁移到新云,老云的文件通过外网流量访问,而外网流量带宽不足导致。
随着各处的带宽配置增加,该问题已解决。
由于积压了较多打包排队请求,DCloud已经扩容了打包机,目前打包拥堵已恢复正常。

【跳转到App Store】ios更新,跳转到app store更新
你的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分享
需要先获取到组件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卡死,重启电脑再次打开依然卡死。
全局搜索了个uni.requireNativePlugin,直接吧Hbuilder卡死,重启电脑再次打开依然卡死。
写代码写上一会就特别卡。
全局搜索了个uni.requireNativePlugin,直接吧Hbuilder卡死,重启电脑再次打开依然卡死。
写代码写上一会就特别卡。

2023年11月阿里云版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 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插件市场下载源码
-
Gitee下载地址:https://gitee.com/liuming9157/easyx
下载源码之后解压,找到components目录,将该目录下的所有内容复制到你的UniAPP X项目下的components目录即可使用。
eaxyX支持按需引用,各个组件的具体使用可以查看相关easyX组件文档
当然,您也可以直接下载安卓easyX的安卓apk体验。
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插件市场下载源码
-
Gitee下载地址:https://gitee.com/liuming9157/easyx
下载源码之后解压,找到components目录,将该目录下的所有内容复制到你的UniAPP X项目下的components目录即可使用。
eaxyX支持按需引用,各个组件的具体使用可以查看相关easyX组件文档
当然,您也可以直接下载安卓easyX的安卓apk体验。
easyX采用MIT协议开源,组件库和模板库仍在不断扩充。对于开发原生商城应用的同学来说,是个宝藏库。等UniAPPX支持鸿蒙后,easyX用来开发鸿蒙原生商城,想想真是吊炸天的开发利器。
应用打包
安卓打包是需要证书的。如果想省事,直接使用Dcloud的公共云证书,傻瓜式操作,不过因为有人滥用这个证书,现在很多品牌的手机会报毒,所以还是建议自己申请证书。
免费证书申请攻略
应用上线
如果要上架到华为、小米、oppo等应用市场,那就需要软件著作权和应用备案了。软件著作权的申请周期比较长,大概3个月,应用备案就快很多了。网上很多服务商提供这两项服务,大家也可以私信我,公司业务可以按照市场最低价给大家服务,个人业务可以提供一些免费指导然后你自己操作。
我是刘明,十年创业老兵,开源技术爱好者。
有问题欢迎私信。