提交 d106a072 编写于 作者: Q qiang

Merge branch 'dev' into alpha

......@@ -81,7 +81,7 @@
<img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/759713d0-4f2d-11eb-a16f-5b3e54966275.png" width="20" height="20"/>
<div class="contact-smg">
<div>官方QQ交流群</div>
<div>23:599958679 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=YDAuvwgiE1Bss5uBA67GSIwfKbDRLuuE&jump_from=webapi">点此加入</a></div>
<div>16:719211033 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=2JJ4I0h0fi2-sPKTM2ZANX-eak6RyD41&jump_from=webapi">点此加入</a></div>
<div>群35:713420817(2000人已满)</div>
<div>群34:530305531(2000人已满)</div>
<div>群33:498071674(2000人已满)</div>
......@@ -94,14 +94,14 @@
<div>群26:147867597(2000人已满)</div>
<div>群25:165297000(2000人已满)</div>
<div>群24:672494800(2000人已满)</div>
<!-- <div>群23:599958679(2000人已满)</div> -->
<div>群23:599958679(2000人已满)</div>
<div>群22:687186952(2000人已满)</div>
<div>群21:717019120(2000人已满)</div>
<div>群20:165796402(2000人已满)</div>
<div>群19:165657124(2000人已满)</div>
<div>群18:698592271(2000人已满)</div>
<div>群17:951348804(2000人已满)</div>
<div>群16:719211033(2000人已满)</div>
<!-- <div>群16:719211033(2000人已满)</div> -->
<div>群15:516984120(2000人已满)</div>
<div>群14:465953250(2000人已满)</div>
<div>群13:699478442(2000人已满)</div>
......
......@@ -183,7 +183,7 @@
<img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/759713d0-4f2d-11eb-a16f-5b3e54966275.png" width="20" height="20"/>
<div class="contact-smg">
<div>官方QQ交流群</div>
<div>群23:599958679 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=YDAuvwgiE1Bss5uBA67GSIwfKbDRLuuE&jump_from=webapi">点此加入</a></div>
<div>群25:165297000 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RKv-Wgx8HXXCUOeLa6B6WeRHXdfJhXTt&jump_from=webapi">点此加入</a></div>
<div>群35:713420817(2000人已满)</div>
<div>群34:530305531(2000人已满)</div>
<div>群33:498071674(2000人已满)</div>
......@@ -194,9 +194,9 @@
<div>群28:166188776(2000人已满)</div>
<div>群27:811363410(2000人已满)</div>
<div>群26:147867597(2000人已满)</div>
<div>群25:165297000(2000人已满)</div>
<!-- <div>群25:165297000(2000人已满)</div> -->
<div>群24:672494800(2000人已满)</div>
<!-- <div>群23:599958679(2000人已满)</div> -->
<div>群23:599958679(2000人已满)</div>
<div>群22:687186952(2000人已满)</div>
<div>群21:717019120(2000人已满)</div>
<div>群20:165796402(2000人已满)</div>
......
......@@ -22,7 +22,7 @@
* 小程序平台:在各自的小程序管理后台操作。
2. 申请广告位id
在各位后台申请广告位id
3. App端打包后生效,打包时必须选择要集成的广告SDK(优量汇、穿山甲)。
3. App端打包后生效,打包时必须选择要集成的广告SDK(优量汇、穿山甲、快手)。
### 语法
......
......@@ -566,10 +566,32 @@ rewardedVideoAd = uni.createRewardedVideoAd({
### 服务器回调事件
- HBuilderX 2.6.8+
穿山甲
```js
rewardedVideoAd.onVerify(e => {
// 广告商调用开发者服务器返回结果
console.log(e.isValid);
console.log('服务器发送验证请求且回调校验完成');
var provider= e.provider;
var valid = e.isValid; //获取校验结果
//valid为true表示通过了服务器的校验,false表示可能没有通过服务器的校验,或是服务器延迟或失败(此时需增加逻辑:轮询向服务器请求并验证结果)
})
```
优量汇
```js
rewardedVideoAd.onVerify(e => {
console.log('服务器已发送验证请求');
var provider= e.provider;
var transId = e.transId;
// 需增加逻辑:轮询向服务器请求并验证结果
})
```
快手
```js
rewardedVideoAd.onVerify(e => {
console.log('服务器已发送验证请求');
var provider= e.provider;
// 需增加逻辑:轮询向服务器请求并验证结果
})
```
......@@ -583,7 +605,17 @@ rewardedVideoAd.onVerify(e => {
4. `uniAdCallback` 接收广告商服务器回调验证签名并抹平穿山甲/优量汇/快手参数差异,然后以 [callFunction](https://uniapp.dcloud.net.cn/uniCloud/cf-functions?id=callbyfunction) 方式调用用户云函数
5. 用户在自己的云函数中处理业务
注意:服务器通信和前端事件是并行的,前端需要轮询向服务器请求并验证结果
注意:
1. 服务器通信和前端事件是并行的,前端需要轮询向服务器请求并验证结果
2. 不建议在 `uniAD` web控制修改回调的服务空间和云函数名称,因为修改后生效需要一段时间
### Q&A
Q: 回调为什么使用[uniCloud](https://uniapp.dcloud.net.cn/uniCloud/README),而不是直接配置开发者的服务器
A:
1. 由于多家广告商的回调和签名验证逻辑不同,开发者需要写很多逻辑,`uniCloud` 中的云函数 `uniAdCallback` 已抹平了差异,开发者按照统一的参数处理即可
2. 开发者的服务器有可能响应慢或失去响应造成回调数据丢失, 使用 `uniCloud` 可以帮助开发者保存一份来自广告商服务器的回调数据到开发者的云数据中,以便开发者主动查询
3. `uniCloud` 可以承载大并发、防DDoS攻击,无需运营人员维护,如果选择了 `阿里云` 且是免费的
### 云函数uniAdCallback传递的参数
......@@ -666,7 +698,7 @@ class UserServer {
}
async sendHttpRequest(url, data) {
let needRetry = parameters.provider !== ProviderType.GDT;
let needRetry = data.provider !== ProviderType.GDT;
let retryCount = needRetry ? DEFAUTL_RETRY_COUNT : 1;
let timeout = needRetry ? RETRY_TIMEOUT : DEFAUTL_TIMEOUT;
let result = null;
......
此差异已折叠。
......@@ -360,7 +360,7 @@ App端可调用手机的系统分享,实现所有注册分享的应用的呼
|App|H5|微信小程序|支付宝小程序|百度小程序|字节跳动小程序|QQ小程序|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|x|x|√|x|√|√|√|
|x|x|√|√|x|√|√|
|参数|类型|说明|平台差异说明|
......
......@@ -71,6 +71,10 @@ Error: not set parameter 'UniversalLinks' @'oauth-weixin'
>如果你是本地离线打包或者由于某种原因你需要用传统的方式:私有化部署服务器来托管apple-app-site-association文件创建通用链接。你仍然可以通过手动配置manifest.json实现。详情:[https://ask.dcloud.net.cn/article/36393#unilink](https://ask.dcloud.net.cn/article/36393#unilink)
##### 常见问题
1. 如何验证通用链接已经生效,有什么表现或者测试方案
1. 为什么我打开通用链接提示:`The requested file was not found on this server.`
你可以将通用链接输入到iphone自带Safari浏览器中,下拉即可看到通用链接对应到应用名称和一个打开按钮,点击按钮即可直接在浏览器打开对应的APP。详情:[点此查看演示视频](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-f184e7c3-1912-41b2-b81f-435d1b37c7b4/4e920b86-0f67-45ac-81f6-6b97f87ff0ae.mp4)
\ No newline at end of file
这是正常现象。通用链接并不要求是一个有效的路径,换句话说这个链接指向的目录允许为空。原理是访问这个链接时,iphone检测到当前域名为通用链接对应的域名,就读取域名服务器下的apple-app-site-association,然后根据域名后面的路径名称,验证并解析出要打开的app包名并唤醒对应的app。
2. 如何验证通用链接已经生效,有什么表现或者测试方案
你可以将通用链接输入到iphone自带Safari浏览器中,下拉即可看到通用链接对应到应用名称和一个打开按钮,点击按钮即可直接在浏览器打开对应的APP。详情:[点此查看演示视频](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-f184e7c3-1912-41b2-b81f-435d1b37c7b4/4e920b86-0f67-45ac-81f6-6b97f87ff0ae.mp4)
\ No newline at end of file
**剪贴板 API 平台差异说明**
|App|H5|微信小程序|支付宝小程序|百度小程序|字节跳动小程序|QQ小程序|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|√|x|√|√|√|√|√|
### uni.setClipboardData(OBJECT)
设置系统剪贴板的内容。
**OBJECT 参数说明**
|参数名|类型|必填|说明|
|:-|:-|:-|:-|
|data|String|是|需要设置的内容|
|success|Function|否|接口调用成功的回调|
|fail|Function|否|接口调用失败的回调函数|
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|
**示例**
```javascript
uni.setClipboardData({
data: 'hello',
success: function () {
console.log('success');
}
});
```
### uni.getClipboardData(OBJECT)
获取系统剪贴板内容。
**OBJECT 参数说明**
|参数名|类型|必填|说明|
|:-|:-|:-|:-|
|success|Function|否|接口调用成功的回调|
|fail|Function|否|接口调用失败的回调函数|
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|
**success 返回参数说明**
|参数|类型|说明|
|:-|:-|:-|
|data|String|剪贴板的内容|
**示例**
```javascript
uni.getClipboardData({
success: function (res) {
console.log(res.data);
}
});
```
#### **注意**
- 设置剪贴板内容后,小程序平台会自动弹出轻提示。App平台默认与小程序保持一致策略。如不希望在App平台弹出提示,可使用Native.js自行操作剪贴板,插件市场有封装好的示例[https://ext.dcloud.net.cn/plugin?id=712](https://ext.dcloud.net.cn/plugin?id=712)。也可以在设置剪切板后立即uni.hideToast()。
- H5的复制粘贴,可去插件市场搜索[剪贴板](https://ext.dcloud.net.cn/search?q=%E5%89%AA%E8%B4%B4%E6%9D%BF)
**剪贴板 API 平台差异说明**
|App|H5|微信小程序|支付宝小程序|百度小程序|字节跳动小程序|QQ小程序|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|√|√|√|√|√|√|√|
### uni.setClipboardData(OBJECT)
设置系统剪贴板的内容。
**OBJECT 参数说明**
|参数名|类型|必填|说明|
|:-|:-|:-|:-|
|data|String|是|需要设置的内容|
|success|Function|否|接口调用成功的回调|
|fail|Function|否|接口调用失败的回调函数|
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|
**示例**
```javascript
uni.setClipboardData({
data: 'hello',
success: function () {
console.log('success');
}
});
```
### uni.getClipboardData(OBJECT)
获取系统剪贴板内容。
**OBJECT 参数说明**
|参数名|类型|必填|说明|
|:-|:-|:-|:-|
|success|Function|否|接口调用成功的回调|
|fail|Function|否|接口调用失败的回调函数|
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|
**success 返回参数说明**
|参数|类型|说明|
|:-|:-|:-|
|data|String|剪贴板的内容|
**示例**
```javascript
uni.getClipboardData({
success: function (res) {
console.log(res.data);
}
});
```
#### **注意**
- 设置剪贴板内容后,小程序平台会自动弹出轻提示。App平台默认与小程序保持一致策略。如不希望在App平台弹出提示,可使用Native.js自行操作剪贴板,插件市场有封装好的示例[https://ext.dcloud.net.cn/plugin?id=712](https://ext.dcloud.net.cn/plugin?id=712)。也可以在设置剪切板后立即uni.hideToast()。
- H5的复制粘贴,可去插件市场搜索[剪贴板](https://ext.dcloud.net.cn/search?q=%E5%89%AA%E8%B4%B4%E6%9D%BF)
### uni.showToast(OBJECT)
显示消息提示框。
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|:-|
|title|String|是|提示的内容,长度与 icon 取值有关。||
|icon|String|否|图标,有效值详见下方说明。||
|image|String|否|自定义图标的本地路径(app端暂不支持gif)|App、H5、微信小程序、百度小程序|
|mask|Boolean|否|是否显示透明蒙层,防止触摸穿透,默认:false|App、微信小程序|
|duration|Number|否|提示的延迟时间,单位毫秒,默认:1500||
|position|String|否|纯文本轻提示显示位置,填写有效值后只有 `title` 属性生效, 有效值详见下方说明。|App|
|success|Function|否|接口调用成功的回调函数||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**icon 值说明**
|值|说明|平台差异说明|
|:-|:-|:-|
|success|显示成功图标,此时 title 文本最多显示 7 个汉字长度。默认值||
|loading|显示加载图标,此时 title 文本最多显示 7 个汉字长度。|支付宝小程序不支持|
|none|不显示图标,此时 title 文本在`小程序`最多可显示两行,`App`仅支持单行显示。|&nbsp;|
**示例**
```javascript
uni.showToast({
title: '标题',
duration: 2000
});
```
**position 值说明(仅App生效)**
|值|说明|
|:-|:-|
|top|居上显示|
|center|居中显示|
|bottom|居底显示|
**Tips**
- App端可通过[plus.nativeUI.toast API](https://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.toast)实现更多功能。
### uni.hideToast()
隐藏消息提示框。
**示例**
```javascript
uni.hideToast();
```
### uni.showLoading(OBJECT)
显示 loading 提示框, 需主动调用 [uni.hideLoading](api/ui/prompt?id=hideloading) 才能关闭提示框。
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|title|String|是|提示的文字内容,显示在loading的下方||
|mask|Boolean|否|是否显示透明蒙层,防止触摸穿透,默认:false|App、微信小程序、百度小程序|
|success|Function|否|接口调用成功的回调函数||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**示例**
```javascript
uni.showLoading({
title: '加载中'
});
```
### uni.hideLoading()
隐藏 loading 提示框。
**示例**
```javascript
uni.showLoading({
title: '加载中'
});
setTimeout(function () {
uni.hideLoading();
}, 2000);
```
### uni.showModal(OBJECT)
显示模态弹窗,可以只有一个确定按钮,也可以同时有确定和取消按钮。类似于一个API整合了 html 中:alert、confirm。
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|title|String|否|提示的标题||
|content|String|否|提示的内容||
|showCancel|Boolean|否|是否显示取消按钮,默认为 true||
|cancelText|String|否|取消按钮的文字,默认为"取消",最多 4 个字符||
|cancelColor|HexColor|否|取消按钮的文字颜色,默认为"#000000"|H5、微信小程序、百度小程序|
|confirmText|String|否|确定按钮的文字,默认为"确定",最多 4 个字符||
|confirmColor|HexColor|否|确定按钮的文字颜色,H5平台默认为"#007aff",微信小程序平台默认为"#3CC51F",百度小程序平台默认为"#3c76ff"|H5、微信小程序、百度小程序|
|success|Function|否|接口调用成功的回调函数||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**success返回参数说明**
|参数|类型|说明|
|:-|:-|:-|:-|
|confirm|Boolean|为 true 时,表示用户点击了确定按钮|
|cancel|Boolean|为 true 时,表示用户点击了取消(用于 Android 系统区分点击蒙层关闭还是点击取消按钮关闭)|
**示例**
```javascript
uni.showModal({
title: '提示',
content: '这是一个模态弹窗',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
```
**注意**
- 弹框同时使用确定取消时,需注意不同平台的确认取消按钮位置不同。在微信、H5中,确认按钮默认在右边。在App中,iOS的确认按钮默认在右边,而Android默认在左边。产生这种差异的原因是uni.showModa在App和小程序上调用的是原生提供的弹出框,原生平台的策略本身就不同。如果需要调整,可以通过自行控制按钮的文字,即“确定”按钮的文字其实可以设置为“取消”。
- showModal不满足需求时,可以自行开发组件弹框。插件市场有很多自定义弹框的组件,需注意在非H5平台,前端组件无法覆盖原生组件(如地图、video),遮罩也无法盖住tabbar和navigationbar。如需覆盖原生组件或遮罩tabbar等,App端推荐使用[subNvue](https://uniapp.dcloud.net.cn/api/window/subNVues)
- App端还有原生的[prompt API](https://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.prompt),弹出界面中内置一个输入框。其他平台需自行封装前端组件实现。
- 钉钉小程序真机与模拟器表现有差异,真机title,content均为必填项
### uni.showActionSheet(OBJECT)
从底部向上弹出操作菜单
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|itemList|Array&lt;String&gt;|是|按钮的文字数组|微信、百度、字节跳动小程序数组长度最大为6个|
|itemColor|HexColor|否|按钮的文字颜色,字符串格式,默认为"#000000"|App-iOS、字节跳动小程序不支持|
|popover|Object|否|大屏设备弹出原生选择按钮框的指示区域,默认居中显示|App-iPad(2.6.6+)、H5(2.9.2)|
|success|Function|否|接口调用成功的回调函数,详见返回参数说明||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**popover 值说明(仅App生效)**
|值|类型|说明|
|:-|:-|:-|
|top|Number|指示区域坐标,使用原生 navigationBar 时一般需要加上 navigationBar 的高度|
|left|Number|指示区域坐标|
|width|Number|指示区域宽度|
|height|Number|指示区域高度|
**success返回参数说明**
|参数|类型|说明|
|:-|:-|:-|
|tapIndex|Number|用户点击的按钮,从上到下的顺序,从0开始|
**示例**
```javascript
uni.showActionSheet({
itemList: ['A', 'B', 'C'],
success: function (res) {
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
},
fail: function (res) {
console.log(res.errMsg);
}
});
```
**Tips**
- App平台,iPad设备支持设置弹出框的位置,详见 [plus.nativeUI的文档](https://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.ActionSheetStyles)
- App平台,实现原生的、复杂的底部图文菜单,例如分享菜单,可参考[https://ext.dcloud.net.cn/plugin?id=69](https://ext.dcloud.net.cn/plugin?id=69)
**注意**
- 在非H5端,本章的所有弹出控件都是原生控件,层级最高,可覆盖video、map、tabbar等原生控件。
- [uni-app插件市场](https://ext.dcloud.net.cn/)有很多封装好的前端组件,但注意前端组件层级不是最高,无法覆盖原生组件,除非使用cover-view或nvue。
### uni.showToast(OBJECT)
显示消息提示框。
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|:-|
|title|String|是|提示的内容,长度与 icon 取值有关。||
|icon|String|否|图标,有效值详见下方说明。||
|image|String|否|自定义图标的本地路径(app端暂不支持gif)|App、H5、微信小程序、百度小程序|
|mask|Boolean|否|是否显示透明蒙层,防止触摸穿透,默认:false|App、微信小程序|
|duration|Number|否|提示的延迟时间,单位毫秒,默认:1500||
|position|String|否|纯文本轻提示显示位置,填写有效值后只有 `title` 属性生效, 有效值详见下方说明。|App|
|success|Function|否|接口调用成功的回调函数||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**icon 值说明**
|值|说明|平台差异说明|
|:-|:-|:-|
|success|显示成功图标,此时 title 文本最多显示 7 个汉字长度。默认值||
|loading|显示加载图标,此时 title 文本最多显示 7 个汉字长度。|支付宝小程序不支持|
|none|不显示图标,此时 title 文本在`小程序`最多可显示两行,`App`仅支持单行显示。|&nbsp;|
**示例**
```javascript
uni.showToast({
title: '标题',
duration: 2000
});
```
**position 值说明(仅App生效)**
|值|说明|
|:-|:-|
|top|居上显示|
|center|居中显示|
|bottom|居底显示|
**Tips**
- App端可通过[plus.nativeUI.toast API](https://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.toast)实现更多功能。
### uni.hideToast()
隐藏消息提示框。
**示例**
```javascript
uni.hideToast();
```
### uni.showLoading(OBJECT)
显示 loading 提示框, 需主动调用 [uni.hideLoading](api/ui/prompt?id=hideloading) 才能关闭提示框。
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|title|String|是|提示的文字内容,显示在loading的下方||
|mask|Boolean|否|是否显示透明蒙层,防止触摸穿透,默认:false|App、微信小程序、百度小程序|
|success|Function|否|接口调用成功的回调函数||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**示例**
```javascript
uni.showLoading({
title: '加载中'
});
```
### uni.hideLoading()
隐藏 loading 提示框。
**示例**
```javascript
uni.showLoading({
title: '加载中'
});
setTimeout(function () {
uni.hideLoading();
}, 2000);
```
### uni.showModal(OBJECT)
显示模态弹窗,可以只有一个确定按钮,也可以同时有确定和取消按钮。类似于一个API整合了 html 中:alert、confirm。
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|title|String|否|提示的标题||
|content|String|否|提示的内容||
|showCancel|Boolean|否|是否显示取消按钮,默认为 true||
|cancelText|String|否|取消按钮的文字,默认为"取消",最多 4 个字符||
|cancelColor|HexColor|否|取消按钮的文字颜色,默认为"#000000"|H5、微信小程序、百度小程序|
|confirmText|String|否|确定按钮的文字,默认为"确定",最多 4 个字符||
|confirmColor|HexColor|否|确定按钮的文字颜色,H5平台默认为"#007aff",微信小程序平台默认为"#3CC51F",百度小程序平台默认为"#3c76ff"|H5、微信小程序、百度小程序|
|success|Function|否|接口调用成功的回调函数||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**success返回参数说明**
|参数|类型|说明|
|:-|:-|:-|:-|
|confirm|Boolean|为 true 时,表示用户点击了确定按钮|
|cancel|Boolean|为 true 时,表示用户点击了取消(用于 Android 系统区分点击蒙层关闭还是点击取消按钮关闭)|
**示例**
```javascript
uni.showModal({
title: '提示',
content: '这是一个模态弹窗',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
```
**注意**
- 弹框同时使用确定取消时,需注意不同平台的确认取消按钮位置不同。在微信、H5中,确认按钮默认在右边。在App中,iOS的确认按钮默认在右边,而Android默认在左边。产生这种差异的原因是uni.showModa在App和小程序上调用的是原生提供的弹出框,原生平台的策略本身就不同。如果需要调整,可以通过自行控制按钮的文字,即“确定”按钮的文字其实可以设置为“取消”。
- showModal不满足需求时,可以自行开发组件弹框。插件市场有很多自定义弹框的组件,需注意在非H5平台,前端组件无法覆盖原生组件(如地图、video),遮罩也无法盖住tabbar和navigationbar。如需覆盖原生组件或遮罩tabbar等,App端推荐使用[subNvue](https://uniapp.dcloud.net.cn/api/window/subNVues)
- App端还有原生的[prompt API](https://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.prompt),弹出界面中内置一个输入框。其他平台需自行封装前端组件实现。
- 钉钉小程序真机与模拟器表现有差异,真机title,content均为必填项
### uni.showActionSheet(OBJECT)
从底部向上弹出操作菜单
**OBJECT参数说明**
|参数|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|itemList|Array&lt;String&gt;|是|按钮的文字数组|微信、百度、字节跳动小程序数组长度最大为6个|
|itemColor|HexColor|否|按钮的文字颜色,字符串格式,默认为"#000000"|App-iOS、字节跳动小程序不支持|
|popover|Object|否|大屏设备弹出原生选择按钮框的指示区域,默认居中显示|App-iPad(2.6.6+)、H5(2.9.2)|
|success|Function|否|接口调用成功的回调函数,详见返回参数说明||
|fail|Function|否|接口调用失败的回调函数||
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
**popover 值说明(仅App生效)**
|值|类型|说明|
|:-|:-|:-|
|top|Number|指示区域坐标,使用原生 navigationBar 时一般需要加上 navigationBar 的高度|
|left|Number|指示区域坐标|
|width|Number|指示区域宽度|
|height|Number|指示区域高度|
**success返回参数说明**
|参数|类型|说明|
|:-|:-|:-|
|tapIndex|Number|用户点击的按钮,从上到下的顺序,从0开始|
**示例**
```javascript
uni.showActionSheet({
itemList: ['A', 'B', 'C'],
success: function (res) {
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
},
fail: function (res) {
console.log(res.errMsg);
}
});
```
**Tips**
- App平台,iPad设备支持设置弹出框的位置,详见 [plus.nativeUI的文档](https://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.ActionSheetStyles)
- App平台,实现原生的、复杂的底部图文菜单,例如分享菜单,可参考[https://ext.dcloud.net.cn/plugin?id=69](https://ext.dcloud.net.cn/plugin?id=69)
**注意**
- 在非H5端,本章的所有弹出控件都是原生控件,层级最高,可覆盖video、map、tabbar等原生控件。
- [uni-app插件市场](https://ext.dcloud.net.cn/)有很多封装好的前端组件,但注意前端组件层级不是最高,无法覆盖原生组件,除非使用cover-view或nvue。
......@@ -43,13 +43,13 @@ uni-app助力数百家单位快速上线**抗疫系统**,开源众多项目,
#### 典型案例
<a href="https://m.qinxuan.honor.cn/" target="_blank" class="clear-style"><b>华为荣耀亲选商城:</b></a> 华为公司旗下荣耀品牌精品电商平台。
<a href="https://m.qinxuan.hihonor.com/" target="_blank" class="clear-style"><b>华为荣耀亲选商城:</b></a> 华为公司旗下荣耀品牌精品电商平台。
<div style="display:flex;justify-content: space-around;">
<a href="javascript:;" target="_self" class="clear-style barcode-view">
<img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/60aa65e0-d95c-11ea-b244-a9f5e5565f30.png" width="200"/>
<span style="margin-top:15px;">微信小程序</span>
</a>
<a href="https://m.qinxuan.honor.cn/" target="_blank" class="clear-style barcode-view">
<a href="https://m.qinxuan.hihonor.com/" target="_blank" class="clear-style barcode-view">
<img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-alicdn/1fa35320-c559-11ea-81ea-f115fe74321c.png" width="200"/>
<span style="margin-top:15px;">H5</span>
</a>
......
......@@ -55,7 +55,7 @@
<img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/759713d0-4f2d-11eb-a16f-5b3e54966275.png" width="20" height="20"/>
<div class="contact-smg">
<div>官方QQ交流群</div>
<div>群23:599958679 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=YDAuvwgiE1Bss5uBA67GSIwfKbDRLuuE&jump_from=webapi">点此加入</a></div>
<div>群25:165297000 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RKv-Wgx8HXXCUOeLa6B6WeRHXdfJhXTt&jump_from=webapi">点此加入</a></div>
<div>群35:713420817(2000人已满)</div>
<div>群34:530305531(2000人已满)</div>
<div>群33:498071674(2000人已满)</div>
......@@ -66,9 +66,9 @@
<div>群28:166188776(2000人已满)</div>
<div>群27:811363410(2000人已满)</div>
<div>群26:147867597(2000人已满)</div>
<div>群25:165297000(2000人已满)</div>
<!-- <div>群25:165297000(2000人已满)</div> -->
<div>群24:672494800(2000人已满)</div>
<!-- <div>群23:599958679(2000人已满)</div> -->
<div>群23:599958679(2000人已满)</div>
<div>群22:687186952(2000人已满)</div>
<div>群21:717019120(2000人已满)</div>
<div>群20:165796402(2000人已满)</div>
......
......@@ -51,8 +51,8 @@
|onResize|监听窗口尺寸变化|App、微信小程序||
|onPullDownRefresh|监听用户下拉动作,一般用于下拉刷新,参考[示例](api/ui/pulldown)|||
|onReachBottom|页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据。具体见下方注意事项|||
|onTabItemTap|点击 tab 时触发,参数为Object,具体见下方注意事项|微信小程序、支付宝小程序、百度小程序、H5、App(自定义组件模式)||
|onShareAppMessage|用户点击右上角分享|微信小程序、百度小程序、字节跳动小程序、支付宝小程序||
|onTabItemTap|点击 tab 时触发,参数为Object,具体见下方注意事项|微信小程序、QQ小程序、支付宝小程序、百度小程序、H5、App(自定义组件模式)||
|onShareAppMessage|用户点击右上角分享|微信小程序、QQ小程序、支付宝小程序、字节小程序||
|onPageScroll|监听页面滚动,参数为Object|nvue暂不支持||
|onNavigationBarButtonTap|监听原生标题栏按钮点击事件,参数为Object|App、H5||
|onBackPress|监听页面返回,返回 event = {from:backbutton、 navigateBack} ,backbutton 表示来源是左上角返回按钮或 android 返回键;navigateBack表示来源是 uni.navigateBack ;详细说明及使用:[onBackPress 详解](http://ask.dcloud.net.cn/article/35120)。支付宝小程序只有真机能触发,只能监听非navigateBack引起的返回,不可阻止默认行为。|app、H5、支付宝小程序||
......
此差异已折叠。
......@@ -932,8 +932,8 @@ h5 平台下拉刷新动画,只有 circle 类型。
|:-|:-|:-|:-|
|pagePath|String|是|页面路径,必须在 pages 中先定义|
|text|String|是|tab 上按钮文字,在 App 和 H5 平台为非必填。例如中间可放一个没有文字的+号图标|
|iconPath|String|否|图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片,不支持字体图标|
|selectedIconPath|String|否|选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效|
|iconPath|String|否|图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 position 为 top 时,此参数无效,不支持网络图片,不支持字体图标|
|selectedIconPath|String|否|选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 position 为 top 时,此参数无效|
**midButton 属性说明**
......
......@@ -147,7 +147,7 @@
<img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/759713d0-4f2d-11eb-a16f-5b3e54966275.png" width="20" height="20"/>
<div class="contact-smg">
<div>官方QQ交流群</div>
<div>群23:599958679 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=YDAuvwgiE1Bss5uBA67GSIwfKbDRLuuE&jump_from=webapi">点此加入</a></div>
<div>群25:165297000 &nbsp;<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RKv-Wgx8HXXCUOeLa6B6WeRHXdfJhXTt&jump_from=webapi">点此加入</a></div>
<div>群35:713420817(2000人已满)</div>
<div>群34:530305531(2000人已满)</div>
<div>群33:498071674(2000人已满)</div>
......@@ -158,9 +158,9 @@
<div>群28:166188776(2000人已满)</div>
<div>群27:811363410(2000人已满)</div>
<div>群26:147867597(2000人已满)</div>
<div>群25:165297000(2000人已满)</div>
<!-- <div>群25:165297000(2000人已满)</div> -->
<div>群24:672494800(2000人已满)</div>
<!-- <div>群23:599958679(2000人已满)</div> -->
<div>群23:599958679(2000人已满)</div>
<div>群22:687186952(2000人已满)</div>
<div>群21:717019120(2000人已满)</div>
<div>群20:165796402(2000人已满)</div>
......
......@@ -2,6 +2,10 @@
### 简介
⼀个视频内容频道,支持上下滑动切换视频内容
![](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a90b5f95-90ba-4d30-a6a7-cd4d057327db/9146fb82-0d0e-4210-804c-93e292f4273e.png)
**平台差异说明**
|App|H5|微信小程序|支付宝小程序|百度小程序|字节跳动小程序|QQ小程序|快应用|360小程序|
......@@ -40,7 +44,7 @@ HBuilder 基座的测试广告位 `adpid` 为 `1111111112`
```html
<template>
<view class="content">
<ad-content-page ref="adContentPage" adpid="1111111112" @load="onadload" @error="onaderror"></ad-content-page>
<ad-content-page class="ad-content-page" ref="adContentPage" adpid="1111111112" @load="onadload" @error="onaderror"></ad-content-page>
</view>
</template>
......@@ -52,8 +56,10 @@ export default {
}
},
onShow() {
// 需要在页面显示时调用广告组件的 show 方法
this.$refs.adContentPage.show();
this.$nextTick(() => {
// 需要在页面显示时调用广告组件的 show 方法
this.$refs.adContentPage.show();
})
},
onHide() {
// 需要在页面隐藏时调用广告组件的 hide 方法停止广告内容的声音
......@@ -69,6 +75,16 @@ export default {
}
}
</script>
<style>
.content {
flex: 1
}
.ad-content-page {
flex: 1
}
</style>
```
**注意**
......
......@@ -6,7 +6,7 @@
|:-|:-|:-|:-|:-|
|src|String||图片资源地址||
|mode|String|'scaleToFill'|图片裁剪、缩放的模式|<div style="width:68px;"></div>|
|lazy-load|Boolean|false|图片懒加载。只针对page与scroll-view下的image有效|微信小程序、App、百度小程序、字节跳动小程序|
|lazy-load|Boolean|false|图片懒加载。只针对page与scroll-view下的image有效|微信小程序、百度小程序、字节跳动小程序|
|fade-show|Boolean|true|图片显示动画效果|仅App-nvue 2.3.4+ Android有效|
|webp|boolean|false|默认不解析 webP 格式,只支持网络资源|微信小程序2.9.0|
|show-menu-by-longpress|boolean|false|开启长按图片显示识别小程序码菜单|微信小程序2.7.0|
......
......@@ -8,6 +8,7 @@
|:-|:-|:-|:-|:-|
|value|String||输入框的初始内容||
|type|String|text|input 的类型|H5 暂未支持动态切换,详见下方 Tips,请使用 v-if 进行整体切换|
|text-content-type|String| |文本区域的语义,根据类型自动填充|仅 App-nvue-iOS 支持|
|password|Boolean|false|是否是密码类型|H5和App写此属性时,type失效|
|placeholder|String||输入框为空时占位符||
|placeholder-style|String||指定 placeholder 的样式||
......@@ -56,6 +57,7 @@
|number|数字输入键盘|均支持,App平台、H5平台 3.1.22 以下版本 vue 页面在 iOS 平台显示的键盘包含负数和小数。|
|idcard|身份证输入键盘|微信、支付宝、百度、QQ小程序|
|digit|带小数点的数字键盘|均支持,App平台、H5平台 vue 页面在 iOS 平台显示的键盘包含负数。|
|tel|电话输入键盘|仅App的nvue页面支持|
**注意事项**
......@@ -65,6 +67,12 @@
- 小程序端input在置焦时,会表现为原生控件,此时会层级变高。如需前端组件遮盖input,需让input失焦,或使用cover-view等覆盖原生控件的方案,[参考](https://uniapp.dcloud.io/component/native-component)。具体来讲,阿里小程序的input为text且置焦为原生控件;微信、头条、QQ所有input置焦均为原生控件;百度小程序置焦时仍然是非原生的。也可以参考[原生控件](https://uniapp.dcloud.io/component/native-component)文档
- input组件若不想弹出软键盘,可设置为disabled
**text-content-type 有效值**
|值|说明|
|:-|:-|
|oneTimeCode|一次性验证码|
**confirm-type 有效值**
|值|说明|平台差异说明|
......
......@@ -120,8 +120,8 @@
|dottedLine|是否虚线|Boolean|否|默认false|App-nvue 2.1.5+、微信小程序、H5、百度小程序、支付宝小程序|
|arrowLine|带箭头的线|Boolean|否|默认false,微信小程序开发者工具暂不支持该属性|App-nvue 2.1.5+、微信小程序、百度小程序|
|arrowIconPath|更换箭头图标|String|否|在arrowLine为true时生效|App-nvue 2.1.5+、微信小程序、百度小程序|
|borderColor|线的边框颜色|String|否||App-nvue 2.1.5+、微信小程序、H5、百度小程序|
|borderWidth|线的厚度|Number|否||App-nvue 2.1.5+、微信小程序、H5、百度小程序|
|borderColor|线的边框颜色|String|否||微信小程序、H5、百度小程序|
|borderWidth|线的厚度|Number|否||微信小程序、H5、百度小程序|
|colorList|彩虹线|Array|false|存在时忽略 color 值|App-nvue 3.1.0+、微信小程序|
|level|压盖关系,默认为 abovelabels|String|false||App不支持(**需SDK提供支持**)、微信小程序|
......
......@@ -75,7 +75,7 @@
**Tips**
- movable-view必须在`<movable-area/>`组件中,并且必须是直接子节点,否则不能移动。
- 如果遇到x、y、scale属性设置不生效的问题参考:[组件属性设置不生效解决办法](/vue-api?id=_4-组件属性设置不生效解决办法)
- 如果遇到x、y、scale属性设置不生效的问题参考:[组件属性设置不生效解决办法](/vue-api?id=componentsolutions)
- 除了H5端和app-nvue端,其他平台不支持内嵌video、map等原生组件。更新:微信基础库2.4.4起支持了原生组件在 scroll-view、swiper、movable-view 中的使用
**示例**[查看演示](https://hellouniapp.dcloud.net.cn/pages/component/movable-view/movable-view)
......
......@@ -36,6 +36,7 @@
**注意**
- 跳转tabbar页面,必须设置open-type="switchTab"
- navigator-hover 默认为 {background-color: rgba(0, 0, 0, 0.1); opacity: 0.7;}, ``<navigator>`` 的子节点背景色应为透明色。
- navigator-`open-type`属性 如果使用对应的值,则对应值的功能会高于对应跳转路径。
- app-nvue 平台只有纯nvue项目(render为native)才支持 `<navigator>`。非render为native的情况下,nvue暂不支持navigator组件,请使用API跳转。
- app下退出应用,Android平台可以使用[plus.runtime.quit](https://www.html5plus.org/doc/zh_cn/runtime.html#plus.runtime.quit)。iOS没有退出应用的概念。
- [uLink组件](https://ext.dcloud.net.cn/plugin?id=1182)是navigator组件的增强版,样式上自带下划线,功能上支持打开在线网页、其他App的schema、mailto发邮件、tel打电话。
......
......@@ -171,5 +171,5 @@ export default {
- scroll-into-view 的优先级高于 scroll-top。
- scroll-view是区域滚动,不会触发页面滚动,无法触发pages.json配置的下拉刷新、页面触底onReachBottomDistance、titleNView的transparent透明渐变。
- 若要使用下拉刷新,建议使用页面的滚动,而不是 scroll-view 。插件市场有前端模拟的基于scroll-view的下拉刷新,但性能不佳。如必需使用前端下拉刷新,推荐使用基于wxs的下拉刷新,性能会比基于js监听方式更高。
- 如果遇到scroll-top、scroll-left属性设置不生效的问题参考:[组件属性设置不生效解决办法](/vue-api?id=componentsolutions)
- 如果遇到scroll-top、scroll-left、refresher-triggered属性设置不生效的问题参考:[组件属性设置不生效解决办法](/vue-api?id=componentsolutions)
- scroll-view的滚动条设置,可通过css的-webkit-scrollbar自定义,包括隐藏滚动条。(app-nvue无此css)
......@@ -35,9 +35,9 @@
|vslide-gesture-in-fullscreen|Boolean|true|在全屏模式下,是否开启亮度与音量调节手势|微信小程序|
|ad-unit-id|String||视频前贴广告单元ID,更多详情可参考开放能力[视频前贴广告](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/ad/video-patch-ad.html)|微信小程序|
|poster-for-crawler|String||用于给搜索等场景作为视频封面展示,建议使用无播放 icon 的视频封面图,只支持网络地址|微信小程序|
|codec|String|hardware|解码器选择,hardware:硬解码(硬解码可以增加解码算力,提高视频清晰度。少部分老旧硬件可能存在兼容性问题);software:ffmpeg 软解码;|App 3.1.0+|
|http-cache|Boolean|true|是否对 http、https 视频源开启本地缓存。缓存策略:开启了此开关的视频源,在视频播放时会在本地保存缓存文件,如果本地缓存池已超过100M,在进行缓存前会清空之前的缓存(不适用于m3u8等流媒体协议)|App 3.1.0+|
|play-strategy|Number|0| 播放策略,0:普通模式,适合绝大部分视频播放场景;1:平滑播放模式(降级),增加缓冲区大小,采用open sl解码音频,避免音视频脱轨的问题,可能会降低首屏展现速度、视频帧率,出现开屏音频延迟等。 适用于高码率视频的极端场景;3: M3U8优化模式,增加缓冲区大小,提升视频加载速度和流畅度,可能会降低首屏展现速度。 适用于M3U8在线播放的场景 |App 3.1.0+|
|codec|String|hardware|解码器选择,hardware:硬解码(硬解码可以增加解码算力,提高视频清晰度。少部分老旧硬件可能存在兼容性问题);software:ffmpeg 软解码;|App-Android 3.1.0+|
|http-cache|Boolean|true|是否对 http、https 视频源开启本地缓存。缓存策略:开启了此开关的视频源,在视频播放时会在本地保存缓存文件,如果本地缓存池已超过100M,在进行缓存前会清空之前的缓存(不适用于m3u8等流媒体协议)|App-Android 3.1.0+|
|play-strategy|Number|0| 播放策略,0:普通模式,适合绝大部分视频播放场景;1:平滑播放模式(降级),增加缓冲区大小,采用open sl解码音频,避免音视频脱轨的问题,可能会降低首屏展现速度、视频帧率,出现开屏音频延迟等。 适用于高码率视频的极端场景;3: M3U8优化模式,增加缓冲区大小,提升视频加载速度和流畅度,可能会降低首屏展现速度。 适用于M3U8在线播放的场景 |App-Android 3.1.0+|
|header|Object||HTTP 请求 Header|App 3.1.19+|
|@play|EventHandle||当开始/继续播放时触发play事件|字节跳动小程序不支持|
|@pause|EventHandle||当暂停播放时触发 pause 事件|字节跳动小程序不支持|
......
......@@ -408,3 +408,30 @@ uni.webView.navigateTo 示例,注意uni sdk放到body下面
</script>
</html>
```
nvue webview通信示例
```
<template>
<view>
<web-view ref="webview" class="webview" @onPostMessage="handlePostMessage"></web-view>
<button class="button" @click="evalJs">evalJs(改变webview背景颜色)</text>
</view>
</template>
<script>
module.exports = {
data: {
},
methods: {
// webview向外部发送消息
handlePostMessage: function(data) {
console.log("接收到消息:" + JSON.stringify(data.detail));
},
// 调用 webview 内部逻辑
evalJs: function() {
this.$refs.webview.evalJs("document.body.style.background ='#00FF00'");
}
}
}
</script>
```
......@@ -95,37 +95,43 @@ weex的组件和JS API,与uni-app不同。uni-app与微信小程序相同。
- uni-app
```javascript
// manifest.json
{
// ...
/* App平台特有配置 */
"app-plus": {
"nvueCompiler":"uni-app" //是否启用 uni-app 模式
}
}
// manifest.json
{
// ...
/* App平台特有配置 */
"app-plus": {
"nvueCompiler":"uni-app" //是否启用 uni-app 模式
}
}
```
在 `manifest.json` 配置文件中,HBuilderX2.4之前版本,默认值为 `weex` 模式,2.4起版本默认值改为 `uni-app` 模式。
在 `manifest.json` 配置文件中,HBuilderX2.4之前版本,默认值为 `weex` 模式,HBuilderX2.4+版本默认值改为 `uni-app` 模式。
weex 编译模式不支持 ```onNavigationBarButtonTap``` 生命周期函数的写法。在 nvue 中监听原生标题栏按钮点击事件,详见:[uni.onNavigationBarButtonTap](https://uniapp.dcloud.net.cn/frame?id=%e9%a1%b5%e9%9d%a2%e7%94%9f%e5%91%bd%e5%91%a8%e6%9c%9f)。
weex编译模式不支持onShow生命周期,但熟悉5+的话,可利用监听webview的```addEventListener``` show事件实现onShow效果。
weex 编译模式不支持```vuex```。
weex 编译模式不支持`vuex`。
nvue 的页面跳转,与 weex 不同,仍然遵循 uni-app 的路由模型。vue 页面和 nvue 页面之间不管怎么跳转,都遵循这个模型。包括 nvue 页面跳向 nvue 页面。每个页面都需要在 pages.json 中注册,调用 uni-app 的 [路由 API](https://uniapp.dcloud.net.cn/api/router) 进行跳转。
原生开发没有页面滚动的概念,页面内容高过屏幕高度并不会自动滚动,只有部分组件可滚动(```list```、```waterfall```、```scroll-view/scroller```),要滚得内容需要套在可滚动组件下。这不符合前端开发的习惯,所以在 nvue 编译为 uni-app模式时,给页面外层自动套了一个 ```scroller```,页面内容过高会自动滚动。(组件不会套,页面有```recycle-list```时也不会套)。 可以设置不自动套。
原生开发没有页面滚动的概念,页面内容高过屏幕高度时,内容并不会自动滚动;只有将页面内容放在`list`、`waterfall`、`scroll-view/scroller`这几个组件下内容才可滚动。这不符合前端开发的习惯,所以在 nvue 编译为 `uni-app`模式时,`uni-app`框架会给nvue页面外层自动嵌套一个 `scroller`,从而实现页面内容的自动滚动。
注意:
- `uni-app`框架仅对nvue页面嵌套`scroller`容器,不会给组件自动套`scroller`容器;
- 若nvue页面有`recycle-list`组件时,`uni-app`框架也不会自动给页面嵌套`scroller`容器
- 若你不希望自动嵌套`scroller`容器,可在`pages.json`中通过如下配置进行关闭:
```javascript
{
"path": "",
"style": {
"disableScroll": true // 不嵌套 scroller
}
}
{
"path": "",
"style": {
"disableScroll": true // 不嵌套 scroller
}
}
```
weex 编译模式下支持使用 weex ui ,例子[详见](https://ext.dcloud.net.cn/plugin?id=442)。但相比uni-app插件市场及官方[uni ui](https://ext.dcloud.net.cn/plugin?id=55)而言,weex语法的组件生态还是比较欠缺的。
......
#### 3.1.21.20210624-alpha
* 【uni-app】
+ App平台 修复 3.1.20 引出的 uni.showToast 接口 icon 固定为 error 类型的Bug [详情](https://ask.dcloud.net.cn/question/125773)
#### 3.1.20.20210623-alpha
* 【uni-app】
+ App平台、H5平台 修复 input 组件同时设置 type=number 和 maxlength 时,部分情况 value 同步错误的Bug
+ App平台、H5平台 修复 textarea 组件设置 min-height 后高度异常的Bug
+ App平台 新增 uni.showToast 接口 icon 支持 error 类型
+ App平台 优化 nvue 页面中去除 display:flex 相关警告
+ App平台 优化 uni.chooseLocation 搜索结果按综合排序 [详情](https://ask.dcloud.net.cn/question/125044)
+ App-Android平台 优化 快速频繁操作应用启动/关闭可能出现白屏现象的问题
+ App-Android平台 修复 uni.previewImage 长按保存图片可能失败的Bug [详情](https://ask.dcloud.net.cn/question/125357)
+ App-iOS平台 修复 调用 uni.hideKeyboard 后点击页面任意位置 input 组件自动聚焦的Bug [详情](https://ask.dcloud.net.cn/question/125233)
+ App-iOS平台 修复 nvue textarea 组件不设置 padding 时 placeholder 显示位置不正常的Bug [详情](https://ask.dcloud.net.cn/question/122376)
+ 百度小程序 修复 部分 class 写法编译后失效的Bug
+ QQ小程序 修复 默认启用 nodeModules 导致作用域插槽编译后运行报错的Bug
* 【uniCloud】
+ 新增 uniCloud响应体规范,方便前端拦截器统一处理、方便国际化 [详情](https://uniapp.dcloud.net.cn/uniCloud/unicloud-response-format)
+ 客户端 新增 添加拦截器、移除拦截器API [详情](https://uniapp.dcloud.net.cn/uniCloud/client-sdk?id=add-interceptor)
+ 客户端 修复 HBuilderX 3.1.17-alpha 引出的 db.on("error") 回调不执行的Bug
+ 客户端 修复 leftWindow、topWindow 中使用 uniCloud 腾讯云报错的Bug [详情](https://ask.dcloud.net.cn/question/125039)
+ DB Schema 调整 enum 属性最大可枚举500条数据
* 【App插件(含5+App和uni-app的App端)】
+ 更新 uni-AD 快手广告联盟SDK Android为3.3.10.2版,iOS为3.3.10 版;快手内容联盟SDK Android为3.3.18.1版,iOS为3.3.19版
+ Android平台 修复 手机语言设置为阿拉伯文后无法操作页面返回的Bug [详情](https://ask.dcloud.net.cn/question/124914)
+ Android平台 修复 H5页面中 intent:// 协议无法拉起三方App的Bug [详情](https://ask.dcloud.net.cn/question/124597)
+ Android平台 修复 云端打包 提交 Google Play 审核提示包含无法识别的语言的Bug [详情](https://ask.dcloud.net.cn/question/125203)
+ Android平台 修复 getVideoInfo 方法调用无响应的Bug [详情](https://ask.dcloud.net.cn/question/122739)
+ Android平台 修复 3.1.14版本引出的 微博登录取消授权后再次调用无响应的Bug [详情](https://ask.dcloud.net.cn/question/125273)
+ Android平台 修复 targetSdkVersion 设置为 30 在部分 Android 11 设备可能无法正常拉起支付App的Bug
+ iOS平台 新增 安全区域配置 safearea 支持 backgroundDark 属性设置暗黑模式的背景颜色 [详情](https://ask.dcloud.net.cn/article/36995#safearea)
+ iOS平台 更新 云端打包环境为XCode12.5,解决在 iOS15 设备无法安装的Bug
#### 3.1.19.20210613-alpha
* 【uni-app】
+ App平台、H5平台 新增 input 组件添加 verifyNumber 属性 [详情](https://uniapp.dcloud.io/component/input)
+ App平台、H5平台 修复 insertImage 多次触发 input 事件的Bug [详情](https://ask.dcloud.net.cn/question/124809)
+ App平台 新增 一键登录自定义按钮添加 provider 属性,用于动态生成 buttons 时区分按钮 [详情](https://uniapp.dcloud.io/univerify?id=用户点击一键登录自定义按钮)
+ App平台 新增 uni.chooseImage 支持 crop 配置 [详情](https://uniapp.dcloud.io/api/media/image?id=chooseimage)
+ App平台 新增 video 组件支持 header 配置 [详情](https://uniapp.dcloud.io/component/video)
+ App-Android平台 修复 uni.request 请求 header 中设置自定义 content-type 会添加 charset 的Bug [详情](https://ask.dcloud.net.cn/question/123961)
+ App-Android平台 修复 websocket 请求过多可能引起崩溃的Bug
+ App-Android平台 修复 nvue webview 组件设置 background 属性不生效的Bug [详情](https://ask.dcloud.net.cn/question/117845)
+ App-Android平台 修复 nvue video 组件暂定播放后可能出现黑边的Bug [详情](https://ask.dcloud.net.cn/question/124152)
+ 小程序平台 优化 作用域插槽内支持使用作用域外数据 [#495](https://github.com/dcloudio/uni-app/issues/495)
+ 百度小程序平台 修复 基础库 3.290.33 以上页面 mounted 执行两次的Bug [#2642](https://github.com/dcloudio/uni-app/issues/2642)
+ 百度小程序平台 修复 使用 usingComponents 后代码上传报错的Bug [#2652](https://github.com/dcloudio/uni-app/issues/2652)
+ 支付宝小程序平台 优化 支持 useDynamicPlugins 配置 [详情](https://ask.dcloud.net.cn/article/39114)
+ 字节小程序平台 修复 基础库 2.0 以上组件关系错乱的Bug [#2651](https://github.com/dcloudio/uni-app/issues/2651)
* 【App插件(含5+App和uni-app的App端)】
+ 新增 拍照和本地相册选择 支持设置 crop 裁剪编辑图片 [规范](https://www.html5plus.org/doc/zh_cn/camera.html#plus.camera.CameraOptions)
+ 新增 视频播放控件 VideoPlayer 播放http/https协议视频资源时支持设置请求的 header [规范](https://www.html5plus.org/doc/zh_cn/video.html#plus.video.VideoPlayerStyles)
+ 新增 登录鉴权服务对象支持 nativeClient 属性标识依赖的客户端App是否已安装 [规范](https://www.html5plus.org/doc/zh_cn/oauth.html#plus.oauth.AuthService.nativeClient)
+ Android平台 更新 LivePusher 直播推流模块基于开源项目[yasea](https://github.com/begeekmyfriend/yasea),支持 srs4.x
+ Android平台 更新 uni-AD 穿山甲SDK 为3.7.0.2版
+ Android平台 修复 storage 数据存储键值 key 中包含特殊字符时可能存取失败的Bug
+ Android平台 修复 getFileInfo 在 Android11 设备上可能无法正常获取文件信息的Bug [详情](https://ask.dcloud.net.cn/question/124440)
+ iOS平台 更新 uni-AD 腾讯优量汇SDK 为4.12.71版
+ iOS平台 更新 视频播放控件 VideoPlayer 使用的 FFmpeg 版本为 ff4.0--ijk0.8.8--20210426--001
+ iOS平台 修复 uni-AD 应用从后台切换到前台开屏广告可能被其它界面覆盖的Bug
+ iOS平台 修复 plus.sqlite.isOpenDatabase 不传入参数可能引起卡死或崩溃的Bug [详情](https://ask.dcloud.net.cn/question/114091)
+ iOS平台 修复 Geolocation 定位模块在用户未授权或设备关闭定位功能时返回错误码与规范不一致的Bug
* 【UniMPSDK】
+ iOS平台 修复 push 方式打开小程序手势返回关闭后偶现无法再次打开小程序页面的Bug
+ iOS平台 修复 动态设置 titleNView 样式后可能导致胶囊菜单按钮弹出的 actionSheet 部分 item 显示空白的Bug
#### 3.1.17.20210603-alpha
* 【uni-app】
+ App-Android平台 新增 nvue ad-content-page组件支持页面内显示快手短视频内容联盟 [规范](https://uniapp.dcloud.net.cn/component/ad-content-page)
+ App-Android平台 修复 nvue map 组件 marker 上的 label 可能会被 marker 本身覆盖的Bug [详情](https://ask.dcloud.net.cn/question/122872)
+ uni-ui uni-data-checkbox 新增 map 属性,可以方便映射text/value属性
+ uni-ui uni-data-checkbox 修复 不关联服务空间的情况下组件报错的Bug
+ uni-ui uni-data-picker 修复 上个版本引出的本地数据无法选择带有children的2级节点
+ uni-ui uni-forms 修复 动态删减数据导致报错的问题
+ uni-ui uni-forms 新增 modelValue 属性 ,value 即将废弃
+ uni-ui uni-forms 新增 uni-forms-item 可以设置单独的 rules
+ uni-ui uni-forms 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤
+ uni-ui uni-forms 优化 submit 事件重命名为 validate
+ uni-ui uni-data-picker 修复 无法加载云端数据的问题
+ uni-ui uni-data-picker 修复 v-model无效问题
+ uni-ui uni-data-picker 修复 loaddata 为空数据组时加载时间过长问题
+ uni-ui uni-datetime-picker 修复 图标在小程序上不显示的 bug
+ uni-ui uni-datetime-picker 优化 重命名引用组件,避免潜在组件命名冲突
+ uni-ui uni-datetime-picker 优化 代码目录扁平化
+ uni-ui uni-tag 修复 未定义 sass 变量 "$uni-color-royal" 的bug
* 【uniCloud】
+ 修复 HBuilderX 3.1.16 引出的未关联服务空间时无法获取 uniCloud.mixinDatacom 的Bug
+ 修复 HBuilderX 3.1.16 引出的某些情况下关联腾讯云服务空间的项目运行报错的Bug
......
#### 3.1.17.20210608
* 【uniCloud】
+ 【重要】clientDB联表查询策略调整,请参考此文档进行进行排查并调整:[clientDB联表查询策略调整](https://ask.dcloud.net.cn/article/38966)
+ 【重要】新增 uni-starter 集成商用项目开发常见功能的云端一体项目模板,大幅节省开发工作量 [详情](https://ext.dcloud.net.cn/plugin?id=5057)
+ clientDB 修复 日期类型(date)数据校验出错的Bug [详情](https://ask.dcloud.net.cn/question/122517)
+ clientDB 修复 action、validateFunction 内打印日志无法在web控制台查看的Bug
+ clientDB 新增 联表查询支持副表foreignKey联查,即副表字段的foreignKey指向主表,把副表数据挂在主表下面 [详情](https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=st-foreign-key)
+ clientDB 修复 部分情况下 action.after 会重复执行一次的bug
+ uniCloud本地调试插件 修复 阿里云偶发启动时多请求并发报错的Bug
+ JQL数据管理 修复 使用云端 schema 时找不到 schema 的Bug [详情](https://ask.dcloud.net.cn/question/123285)
* 【App插件(含5+App和uni-app的App端)】
+ 新增 uni-AD 快手广告联盟支持插屏广告
+ 新增 一键登录 全屏模式支持在登录界面添加自定义登录按钮 [详情](https://uniapp.dcloud.io/univerify)
+ 新增 获取视频信息 getVideoInfo 支持获取画面方向 orientation、视频格式 type、视频码率 bitrate [文档](https://www.html5plus.org/doc/zh_cn/io.html#plus.io.VideoInfo)
+ 更新 uni-AD 快手广告联盟SDK 为3.3.9 版;快手内容联盟SDK 为3.3.16 版;腾讯优量汇SDK Android为4.360.1230版;穿山甲SDK Android为3.6.1.3版,iOS为3.6.1.2版
+ Android平台 优化 原生模板隐私政策提示框逻辑,解决部分应用市场检测到弹出隐私政策框之前读取mac地址和应用列表的问题 [文档](https://ask.dcloud.net.cn/article/36937)
+ Android平台 优化 云端打包googleplay渠道使用 Android App Bundle (AAB) 格式 [详情](https://ask.dcloud.net.cn/article/39052)
+ Android平台 更新 UniPush 使用的个推SDK版本为3.2.0.0,提升在Android高版本设备上的推送消息到达率
+ Android平台 更新 新浪微博分享、授权登录 SDK 为 10.10.0 版,适配支持 Android11 设备
+ Android平台 更新 高德地图 SDK 为 7.9.1 版,高德定位 SDK 为 5.3.1 版,友盟统计 SDK 为 9.3.8 版
+ Android平台 修复 腾讯云等安全检测平台报的部分高风险漏洞 [详情](https://ask.dcloud.net.cn/article/39020)
+ Android平台 修复 uni-AD 开通基础开屏广告在弱网状态可能引起崩溃的Bug
+ Android平台 修复 uni-AD 快手联盟的信息流广告可能返回高度不正确导致显示异常的Bug
+ Android平台 修复 uni原生插件在原生模板隐私政策提示框之前可能进行初始化违规读取用户数据的Bug
+ Android平台 修复 QQ登录获取用户信息 getUserInfo 返回的昵称可能出现乱码的Bug [详情](https://ask.dcloud.net.cn/question/120265)
+ iOS平台 优化 相册目录选择操作界面
+ iOS平台 更新 高德地图 SDK 为 1.6.4无IDFA版,适配 iOS14.5 开始 AppStore 审核要求用户许可访问IDFA数据
+ iOS平台 更新 百度语音识别 SDK 为 3.0.10.0 版
+ iOS平台 修复 视频播放控件 VideoPlayer 调用 playbackRate 方法设置倍数播放值为 1.25、1.5 不生效的Bug [详情](https://ask.dcloud.net.cn/question/107802)
+ iOS平台 修复 扫码时息屏后再次打开引起扫描线动画停止的Bug [详情](https://ask.dcloud.net.cn/question/124001)
+ iOS平台 修复 保存文件名称中存在中文时报错的Bug
+ iOS平台 修复 一键登录 授权登录时需要读取IDFA的Bug
#### 3.1.13.20210514
* 【uni-app】
+ App平台、H5平台 修复 3.1.11 版本引出的 textarea 组件固定 box-sizing 导致部分情况显示异常的Bug [详情](https://ask.dcloud.net.cn/question/121893)
......@@ -94,7 +127,6 @@
+ 优化 一键登录 未通过审核时云端打包后调用API返回 -7 错误 [规范](https://uniapp.dcloud.io/univerify?id=%e9%94%99%e8%af%af%e7%a0%81)
+ 修复 一键登录 授权界面显示后,调用原生模态窗口无法正常显示的Bug
+ 修复 存在开屏广告时 splashclosed 事件可能在启动界面关闭前触发的Bug
+ Android平台 优化 原生模板隐私政策提示框逻辑,解决部分应用市场检测到弹出隐私政策框之前读取mac地址和应用列表的问题 [文档](https://ask.dcloud.net.cn/article/36937)
+ Android平台 更新 公共测试证书,解决某些检测机构报病毒的问题 [文档](https://ask.dcloud.net.cn/article/36522)
+ Android平台 更新 gif图片库 android-gif-drawable 为 1.2.23 版,解决安全检测报存在远程代码执行漏洞的问题
+ Android平台 修复 uni-AD 开通基础开屏广告在弱网状态可能引起崩溃的Bug
......
注意:本指南仅支持 vue3 版本的 uni-app 项目(h5平台),目前仅可通过创建uni-app cli项目的方式来使用,参考文档:[uni-app 项目小程序端、H5 端支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
#### 什么是服务器端渲染 (SSR)?
uni-app 默认情况下,是在客户端中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
服务器渲染的 uni-app 应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行。
#### 为什么使用服务器端渲染 (SSR)?
与传统 SPA (单页应用程序 (Single-Page Application)) 相比,服务器端渲染 (SSR) 的优势主要在于:
- 更好的 SEO,搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
- 更快的内容到达时间 (time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。无需等待所有的 JavaScript 都完成下载并执行,才显示服务器渲染的标记,所以你的用户将会更快速地看到完整渲染的页面。通常可以产生更好的用户体验,并且对于那些「内容到达时间(time-to-content) 与转化率直接相关」的应用程序而言,服务器端渲染 (SSR) 至关重要。
使用服务器端渲染 (SSR) 时还需要有一些权衡之处:
- 开发条件所限。浏览器特定的代码,只能在某些生命周期钩子函数 (lifecycle hook) 中使用;一些外部扩展库 (external library) 可能需要特殊处理,才能在服务器渲染应用程序中运行。
- 涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序 (SPA) 不同,服务器渲染应用程序,需要处于 Node.js server 运行环境。
- 更多的服务器端负载。在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 (high traffic) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
幸运的是,以上问题,[uniCloud](https://uniapp.dcloud.net.cn/uniCloud/README) 均为您提供了解决方案,**注意:目前仅腾讯云支持,阿里云后续也会支持**
- [unicloud-db](https://uniapp.dcloud.net.cn/uniCloud/unicloud-db) 组件是 uniCloud 提供的一个数据库查询组件,内置支持SSR,开发者无需任何额外开发。
- uniCloud 云函数与静态托管,提供了弹性扩容、大并发承载、防DDoS攻击的世界最顶级的IT基础设施,通过 HBuilderX 可将 uni-app 项目一键部署为支持 SSR 的 h5 网站
#### 编写通用代码
在进一步介绍之前,让我们花点时间来讨论编写"通用"代码时的约束条件 - 即运行在服务器和客户端的代码。由于用例和平台 API 的差异,当运行在不同环境中时,我们的代码将不会完全相同。所以这里我们将会阐述你需要理解的关键事项。
##### 服务器上的数据响应
在纯客户端应用程序 (client-only app) 中,每个用户会在他们各自的浏览器中使用新的应用程序实例。对于服务器端渲染,我们也希望如此:每个请求应该都是全新的、独立的应用程序实例,以便不会有交叉请求造成的状态污染 (cross-request state pollution)。
因为实际的渲染过程需要确定性,所以我们也将在服务器上“预取”数据 ("pre-fetching" data) - 这意味着在我们开始渲染时,我们的应用程序就已经解析完成其状态。也就是说,将数据进行响应式的过程在服务器上是多余的,所以默认情况下禁用。禁用响应式数据,还可以避免将「数据」转换为「响应式对象」的性能开销。
##### 组件生命周期钩子函数
由于没有动态更新,所有的生命周期钩子函数中,只有 beforeCreate 和 created 会在服务器端渲染 (SSR) 过程中被调用。这就是说任何其他生命周期钩子函数中的代码(例如 beforeMount 或 mounted),只会在客户端执行。
此外还需要注意的是,你应该避免在 beforeCreate 和 created 生命周期时产生全局副作用的代码,例如在其中使用 setInterval 设置 timer。在纯客户端 (client-side only) 的代码中,我们可以设置一个 timer,然后在 beforeUnmount 或 unmounted 生命周期时将其销毁。但是,由于在 SSR 期间并不会调用销毁钩子函数,所以 timer 将永远保留下来。为了避免这种情况,请将副作用代码移动到 beforeMount 或 mounted 生命周期中。
##### 访问特定平台(Platform-Specific) API
通用代码不可接受特定平台的 API,因此如果你的代码中,直接使用了像 window 或 document,这种仅浏览器可用的全局变量,则会在 Node.js 中执行时抛出错误,反之也是如此。
对于仅浏览器可用的 API,通常方式是,在「纯客户端 (client-only)」的生命周期钩子函数中惰性访问 (lazily access) 它们。
请注意,考虑到如果第三方 library 不是以上面的通用用法编写,则将其集成到服务器渲染的应用程序中,可能会很棘手。你可能要通过模拟 (mock) 一些全局变量来使其正常运行,但这只是 hack 的做法,并且可能会干扰到其他 library 的环境检测代码。
#### 数据预取和状态
在服务器端渲染(SSR)期间,我们本质上是在渲染我们应用程序的"快照",所以如果应用程序依赖于一些异步数据,那么在开始渲染过程之前,需要先预取和解析好这些数据。
另一个需要关注的问题是在客户端,在挂载 (mount) 到客户端应用程序之前,需要获取到与服务器端应用程序完全相同的数据 - 否则,客户端应用程序会因为使用与服务器端应用程序不同的状态,然后导致混合失败。
为了解决这个问题,获取的数据需要位于视图组件之外,即放置在专门的数据预取存储容器(data store)或"状态容器(state container)"中。首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store 中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store 获取到内联预置(inline)状态。
- 对于简单应用,我们可以直接使用`@dcloudio/uni-app`提供的`ssrRef`(等同于 vue3 的 [ref](https://v3.cn.vuejs.org/api/refs-api.html#ref)),`shallowSsrRef`(等同于 vue3 的 [shallowRef](https://v3.cn.vuejs.org/api/refs-api.html#shallowref)),来确保服务端数据与客户端数据的一致性
* 在非组件生命周期中使用`ssrRef``shallowSsrRef`时,数据将被存储在全局
* `ssrRef``shallowSsrRef`均支持第二个参数,作为数据的key
示例:
```js
import { ssrRef } from '@dcloudio/uni-app'
const categories = ssrRef([], 'categories'); // 在非组件生命周期中使用时,为全局数据,可以跨组件跨页面使用
export default {
setup (){
const items = ssrRef([]); // 在生命周期中使用时,为组件级别作用域,不指定第二个参数key的情况下,编译器会自动补充(默认,以文件+函数位置做base64生成key)
return { items, categories }
}
}
```
- 对于复杂应用,我们可以使用官方状态管理库[Vuex](https://github.com/vuejs/vuex/)
示例:
1. 我们先创建一个 store/index.js 文件,里面会模拟一些根据 id 获取 item 的逻辑:
```js
import { createStore } from 'vuex'
// 模拟接口获取数据
function fetchItem(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id,
title: 'title' + id,
})
}, 300)
})
}
export default () => {
return createStore({
state() {
return {
items: {},
}
},
actions: {
fetchItem({ commit }, id) {
return fetchItem(id).then((item) => {
commit('setItem', { id, item })
})
},
},
mutations: {
setItem(state, { id, item }) {
state.items[id] = item
},
},
})
}
```
2. 然后修改 main.js
```js
import { createSSRApp } from 'vue'
import App from './App.vue'
import createStore from './store'
export function createApp() {
const app = createSSRApp(App)
const store = createStore() // 创建 store
app.use(store)
return {
app,
store,// 必须返回 store
}
}
```
3. 在页面或组件中使用
```html
<template>
<text v-if="item">{{ item.title }}</text>
<text v-else>...</text>
</template>
<script>
const id = 1;// 模拟ID
export default {
computed: {
item() {
return this.$store.state.items[id]
}
},
serverPrefetch() {// 服务端预取数据的生命周期
return this.fetchItem()
},
mounted() { // 仅客户端执行的生命周期
if (!this.item) { // 判断服务端是否已正常获取,若未获取,重新调用加载数据
this.fetchItem()
}
},
methods: {
fetchItem() {
return this.$store.dispatch('fetchItem', id)
}
}
}
</script>
```
#### 发行与部署@distribute
- 部署到`uniCloud`
* 开通[uniCloud](https://unicloud.dcloud.net.cn)以及[前端网页托管](https://uniapp.dcloud.net.cn/uniCloud/hosting)
* 配置`vite.config.js`中的`base``前端网页托管`地址
```js
import {
defineConfig
} from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
// https://vitejs.dev/config/
export default defineConfig({
base: 'https://static-xxxx.bspapp.com/', // uniCloud 前端网页托管资源地址(主要是应用编译后的js,图片等静态资源,可以配置为二级目录)
plugins: [
uni(),
],
})
```
* 编译:
> cli工程:`npm run build:h5:ssr`或通过`HBuilderX`的`发行菜单->网站 PC-Web或手机H5`、勾选`ssr`
![以ssr模式发行](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-f184e7c3-1912-41b2-b81f-435d1b37c7b4/d7574ced-e253-4b73-8187-86d6a8811364.jpg)
* 部署静态资源到[前端网页托管](https://uniapp.dcloud.net.cn/uniCloud/hosting)
> 将编译后的`dist/build/h5/client`中的资源上传至前端网页托管,推荐使用免费的阿里云服务空间
* 部署`uni-ssr`云函数
> 从插件市场导入[uni-ssr](https://ext.dcloud.net.cn/plugin?id=5338),将编译后的`dist/build/h5/server`目录拷贝至`uni-ssr`云函数根目录,并上传
* 配置`uni-ssr`的云函数URL化路径,请参考文档:[云函数URL化](https://uniapp.dcloud.net.cn/uniCloud/http)
\ No newline at end of file
......@@ -187,6 +187,7 @@ uni云端一体生态的内容太多,让我们抽丝剥茧、归纳分类,
- uni-id:不用再开发用户系统。用户注册、登录(含社交登录、短信验证码登录、App一键登录)、修改或重置密码、token管理、图形验证码、RBAC权限角色系统...所有与用户相关的,不管前端还是云端,代码都是现成的。[详见](https://uniapp.dcloud.net.cn/uniCloud/uni-id)
- uniPay:不管微信还是支付宝,不管App、微信小程序、还是支付宝小程序,不管前端还是服务端,一切都现成的,拿来即用。[详见](https://uniapp.dcloud.net.cn/uniCloud/unipay)
- uSearch:云端一体搜索。搜索页面、输入联想、搜索历史记录、热搜词分析提取...一应俱全。[详见](https://ext.dcloud.net.cn/plugin?id=3851)
- uni-starter:云端一体应用快速开发基本项目模版,实现快速搭建一款应用。它集成了很多通用的功能,比如登录注册、头像、设置、拦截器、banner...[详见](https://ext.dcloud.net.cn/plugin?id=5057)
- uniCloud Admin:全端可用的admin后台。自带用户管理、权限管理、角色管理、菜单管理。更有众多admin插件,比如cms插件、banner管理插件、App升级管理插件...[详见](https://uniapp.dcloud.net.cn/uniCloud/admin)
- uni-file-picker:前端直传uniCloud存储组件。[详见](https://ext.dcloud.net.cn/plugin?id=4079)
- uni-captcha:云端一体图形验证码组件。[详见](https://ext.dcloud.net.cn/plugin?id=4048)
......
......@@ -29,15 +29,18 @@
* [客户端sdk](uniCloud/client-sdk.md)
* [uni-id用户体系](uniCloud/uni-id.md)
* 扩展能力
* [uni-config-center](https://ext.dcloud.net.cn/plugin?id=4425)
* [uni-config-center 配置中心](https://ext.dcloud.net.cn/plugin?id=4425)
* [uni-starter](https://ext.dcloud.net.cn/plugin?id=5057)
* [uniCloud admin](uniCloud/admin.md)
* [uni-upgrade-center](uniCloud/upgrade-center.md)
* [uni-upgrade-center App升级中心](uniCloud/upgrade-center.md)
* [uni-cloud-router](uniCloud/uni-cloud-router.md)
* [unipay](uniCloud/unipay.md)
* [unipay 统一支付](uniCloud/unipay.md)
* [发送短信](uniCloud/send-sms.md)
* [uni一键登录](uniCloud/univerify.md)
* [云端一体搜索](https://ext.dcloud.net.cn/plugin?id=3851)
* [uni-captcha图形验证码](https://ext.dcloud.net.cn/plugin?id=4048)
* [uSearch 云端一体搜索](https://ext.dcloud.net.cn/plugin?id=3851)
* [uni-captcha 图形验证码](https://ext.dcloud.net.cn/plugin?id=4048)
* [uni-sec-check 内容安全](https://ext.dcloud.net.cn/plugin?id=5460)
* [uniCloud响应体规范](uniCloud/unicloud-response-format.md)
* [前端网页托管](uniCloud/hosting.md)
* [日志输出](uniCloud/cf-logger.md)
* [同时连多服务空间](uniCloud/init.md)
......@@ -85,7 +88,8 @@
<img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/759713d0-4f2d-11eb-a16f-5b3e54966275.png" width="20" height="20"/>
<div class="contact-smg">
<div>uniCloud QQ交流群</div>
<div>群1:1012245137 &nbsp;<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=36ff837111d79a4e90e5fcd6185ec684be4fc276eb57259e08339512709d39fe">点此加入</a></div>
<div>群1:1012245137 (2000人已满)</div>
<div>群2:749911289 &nbsp;<a target="_blank" href="//qm.qq.com/cgi-bin/qm/qr?k=iN4JdFAGhvlrOgw-gghF6cUOX1otsLpx&jump_from=webapi">点此加入</a></div>
</div>
</div>
<div class="contact-item">
......
# uniCloud admin 框架
### **重要公告**:
> admin 1.5.0 以上版本在 HBuilderX alpha 3.1.19 以下版本中,使用「腾讯云服务空间」时 leftWindow 存在 `uni is not undefined` 的问题,该 bug 在 HBuilderX alpha 3.1.20 已修复,[下载HBuilderX alpha 3.1.20](https://dcloud.io/hbuilderx.html)
### 什么是 uniCloud admin
uniCloud admin 框架,是基于 uni-app 和 uniCloud 的应用后台管理的开源框架。
......
......@@ -102,6 +102,19 @@ exports.main = async (event, context) => {
}
```
### 获取云函数调用来源@context-source
```js
'use strict';
exports.main = async (event, context) => {
let source = context.SOURCE // 当前云函数被何种方式调用
// client 客户端callFunction方式调用
// http 云函数url化方式调用
// timing 定时触发器调用
// server 由管理端调用,HBuilderX里上传并运行,仅阿里云支持,腾讯云这种方式调用也是client
// function 由其他云函数callFunction调用,仅阿里云支持,腾讯云这种方式调用也是client
}
```
### 其他客户端信息@client-info
**以下四个属性只有使用uni-app以callFunction方式调用才能获取**
......@@ -364,6 +377,7 @@ serverless默认是没有固定的服务器IP的,因为有很多服务器在
- 云函数内存使用量会随着并发量增大而增加
- 如果并发的不同请求对全局变量同时进行读写会污染全局变量,可能会导致意想不到的后果,开启单实例多并发后请不要编写修改全局变量的代码,除非你熟悉这种技术带来的特殊应用,比如下文进阶部分提到的ip过滤。
- 设置过大的单实例多并发可能会导致实例底层网络请求排队从而导致请求超时
**适用场景**
......@@ -404,6 +418,10 @@ exports.main = async function(event, context) {
开启单实例多并发后的全局变量复用并非一定是坏的结果,如果你很了解此行为,也可以对此进行有效的利用
例:[ip-filter](https://ext.dcloud.net.cn/plugin?id=4619)中就利用云函数全局缓存一些ip访问信息来限制单ip访问频率,可以下载示例项目体验一下
### 云函数运行环境@runtime
目前腾讯云和阿里云均支持选择nodejs版本,有nodejs8、nodejs12两个选项,需要在云函数创建时设定,不可修改。需要在云函数的package.json文件的`cloudfunction-config->runtime`字段进行配置,详情参考:[云函数package.json](uniCloud/cf-functions.md?id=packagejson)
## 云函数package.json@packagejson
......@@ -434,7 +452,8 @@ package.json是一个标准json文件,不可带注释。下面是一个package
"type": "timer",
"config": "0 0 2 1 * * *"
}],
"path": ""
"path": "",
"runtime": "Nodejs8"
}
}
```
......@@ -456,7 +475,8 @@ package.json是一个标准json文件,不可带注释。下面是一个package
"config": "0 0 2 1 * * *"
}],
// 云函数Url化path部分,阿里云需要以/http/开头
"path": ""
"path": "",
"runtime": "" // nodejs版本,可选Nodejs8、Nodejs12,默认:Nodejs8
}
```
......@@ -467,6 +487,7 @@ package.json是一个标准json文件,不可带注释。下面是一个package
- 上传云函数时,如果项目下的package.json内包含云函数配置会同时进行云函数的配置更新
- package.json只有云端部署才生效,本地运行不生效。
- cloudfunction-config不可删除云端配置。例:云端已配置triggers(定时触发器),删除cloudfunction-config内的trigger不会删掉云端的定时触发器
- runtime参数(nodejs版本)仅可在创建云函数时生效,不可修改
## 使用cloudfunctions_init初始化云函数@init
......
......@@ -54,11 +54,9 @@ uniCloud分为客户端和云端两部分,有些接口名称相同,参数也
console.log(uniCloud.getCurrentUserInfo().role.indexOf('admin')>-1); // 如果是admin用户的话,打印结果为true
```
<!-- ### 新增拦截器@add-interceptor
### 新增拦截器@add-interceptor
```
+ clientDB客户端sdk 新增 添加拦截器、移除拦截器API [详情](https://uniapp.dcloud.net.cn/uniCloud/client-sdk?id=add-interceptor)
```
> 新增于HBuilderX 3.1.20
接口形式:`uniCloud.addInterceptor(String apiName, Object interceptorMap)`
......@@ -107,6 +105,8 @@ uniCloud.addInterceptor('callFunction', {
### 移除拦截器@remove-interceptor
> 新增于HBuilderX 3.1.20
接口形式:`uniCloud.removeInterceptor(String apiName, Object interceptorMap)`
**入参说明**
......@@ -127,7 +127,32 @@ uniCloud.addInterceptor('callFunction', {
**注意:**
- 要移除的拦截器内方法需和添加的方法一致才可以移除 -->
- 要移除的拦截器内方法需和添加的方法一致才可以移除,详情见下方示例
```js
// 错误用法,无法移除invoke拦截器
uniCloud.addInterceptor('callFunction', {
invoke(param) {
console.log('callFunction invoked, with param:',param)
}
})
uniCloud.removeInterceptor('callFunction', {
invoke(param) {
console.log('callFunction invoked, with param:',param)
}
})
// 正确用法
function invokeInterceptor(param) {
console.log('callFunction invoked, with param:',param)
}
uniCloud.addInterceptor('callFunction', {
invoke: invokeInterceptor
})
uniCloud.removeInterceptor('callFunction', {
invoke: invokeInterceptor
})
```
## 属性
......
......@@ -2387,6 +2387,93 @@ const res = await db.collection('score')
- distinct指对返回结果中完全相同的记录进行去重,重复的记录只保留一条。因为`_id`字段是必然不同的,所以使用distinct时必须同时指定field,且field中不可存在`_id`字段
### 同时发送多条数据库请求@multi-send
> HBuilderX 3.1.22及以上版本支持
在实际业务中通常会遇到一个页面需要查询多次的情况,比如应用首页需要查询轮播图列表、公告列表、首页商品列表等。如果分开请求需要发送很多次网络请求,这样会影响性能。使用multiSend可以将多个数据库请求合并成一个发送。
**用法**
```js
const bannerQuery = db.collection('banner').field('url,image').getTemp() // 这里使用getTemp不直接发送get请求,等到multiSend时再发送
const noticeQuery = db.collection('notice').field('text,url,level').getTemp()
const res = await db.multiSend(bannerQuery,noticeQuery)
```
**返回值**
```js
// 上述请求返回以下结构
res = {
code: 0, // 请求整体执行错误码,注意如果多条查询执行失败,这里的code依然是0,只有出现网络错误等问题时这里才会出现错误
message: '', // 错误信息
dataList: [{
code: 0, // bannerQuery 对应的错误码
message: '', // bannerQuery 对应的错误信息
data: [] // bannerQuery 查询到的数据
}, {
code: 0, // noticeQuery 对应的错误码
message: '', // noticeQuery 对应的错误信息
data: [] // noticeQuery 查询到的数据
}]
}
```
unicloud-db组件也支持使用getTemp方法,结合multiSend可以与其他数据库请求一起发送
用法示例:
```html
<template>
<view>
<!-- 设置unicloud-db 组件为手动加载 loadtime="manual" -->
<unicloud-db collection="banner" loadtime="manual" ref="udb" v-slot:default="{data, error}">
<view v-if="error">{{error.message}}</view>
<view v-else>
<view v-for="(item,index) in data" :key="index">
<image :src="item.url"></image>
</view>
</view>
</unicloud-db>
<button type="default" @click="test">test</button>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
test() {
const db = uniCloud.database()
const bannerQuery = this.$refs.udb.getTemp() // 调用模板内unicloud-db组件实例的getTemp方法
const noticeQuery = db.collection('notice').getTemp()
db.multiSend(bannerQuery, noticeQuery)
.then(res => {
console.log('banner', res.result.dataList[0]); // 使用unicloud-db组件的getTemp请求无需额外处理,查询结果会直接被设置到unicloud-db组件内
console.log('notice', res.result.dataList[1]); // 不使用unicloud-db组件的getTemp请求需要自行处理返回值
})
.catch(err => {
console.error(err)
})
// uniCloud.database().collection('test').get()
}
}
}
</script>
<style>
</style>
```
### 新增数据记录add
> 代码块`dbadd`
......
......@@ -8,6 +8,7 @@
- 跨平台。不管你在uniCloud里选择了阿里还是腾讯的serverless,均可以跨uni-app的全端使用。从pc到h5,从Android到iOS,以及各家小程序快应用,十几个平台全端支持
- uniCloud提供了`clientDB`神器,减少90%的服务器开发工作量,且保障数据安全。[详见](/uniCloud/database)
- uniCloud提供了[uni-id](/uniCloud/uni-id)[uniPay](/uniCloud/unipay)等重要框架,大幅减少开发者的相应功能开发量。
- uniCloud提供了[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057),客户端开发工作量大幅减少。
- uniCloud提供了[uniCloud admin](/uniCloud/admin),管理端开发工作量大幅减少。
- uniCloud提供了[schema2code](/uniCloud/schema?id=autocode),只需编制数据库schema文件,用户端和管理端的数据列表、分页、搜索、详情查看、修改、删除,全套代码均能自动生成。
- 更易学。uniCloud提供了`JQL`查询语言,比SQL和MongoDB的查询语法更简单易掌握,尤其是联表查询非常简单。[详见](https://uniapp.dcloud.io/uniCloud/database?id=jsquery)
......@@ -281,7 +282,7 @@ exports.main = async function(event){
开发期间经常需要多人共用同一个服务空间,此时可以在[DCloud开发者中心](https://dev.dcloud.net.cn/)将特定应用及其关联的服务空间共享给协作者,详细步骤如下
1. 在开发者中心`我创建的应用`列表页面选择特定的应用
1. 在开发者中心`我创建的应用`列表页面选择特定的应用,如果应用过多可以使用AppId进行查找
![我创建的应用](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-f184e7c3-1912-41b2-b81f-435d1b37c7b4/865a0df3-3169-48df-8b4c-8acacf1a621f.jpg)
......@@ -296,6 +297,8 @@ exports.main = async function(event){
5. 点击第4步弹出界面的`保存按钮`以及第3步的`保存权限设置`按钮
6. 协作者如需在uni-app项目关联此服务空间,需要在项目的`manifest.json`内配置上共享的应用的AppId(需要在源码视图编辑manifest.json)
### 如何使用promise/async/await@promise
uniCloud客户端callFunction及数据库相关接口会返回Promise类型结果,请参考以下写法使用:
......
......@@ -163,7 +163,7 @@ uniCloud提供包月、按量计费两种计费方式(仅腾讯云),具体
|云函数外网出流量|1GB/月|
|云函数并发限制|1000/云函数|
|云函数数量限制|9个|
|云函数支持固定出口IP||
|云函数支持固定出口IP|×|
|数据库容量|2GB|
|数据库同时连接数|5个|
|数据库读操作数|500次/天|
......@@ -185,7 +185,7 @@ uniCloud提供包月、按量计费两种计费方式(仅腾讯云),具体
|云函数外网出流量|1GB/月|
|云函数并发限制|1000/云函数|
|云函数数量限制|49个|
|云函数支持固定出口IP|×|
|云函数支持固定出口IP||
|数据库容量|2GB|
|数据库同时连接数|20个|
|数据库读操作数|5万次/天|
......@@ -207,7 +207,7 @@ uniCloud提供包月、按量计费两种计费方式(仅腾讯云),具体
|云函数外网出流量|3GB/月|
|云函数并发限制|1000/云函数|
|云函数数量限制|79个|
|云函数支持固定出口IP|×|
|云函数支持固定出口IP||
|数据库容量|3GB|
|数据库同时连接数|50个|
|数据库读操作数|25万次/天|
......@@ -273,7 +273,7 @@ uniCloud提供包月、按量计费两种计费方式(仅腾讯云),具体
|云函数外网出流量|5GB/月|
|云函数并发限制|1000/云函数|
|云函数数量限制|99个|
|云函数支持固定出口IP|×|
|云函数支持固定出口IP||
|数据库容量|5GB|
|数据库同时连接数|100个|
|数据库读操作数|50万次/天|
......@@ -295,7 +295,7 @@ uniCloud提供包月、按量计费两种计费方式(仅腾讯云),具体
|云函数外网出流量|3GB/月|
|云函数并发限制|1000/云函数|
|云函数数量限制|99个|
|云函数支持固定出口IP|×|
|云函数支持固定出口IP||
|数据库容量|3GB|
|数据库同时连接数|50个|
|数据库读操作数|25万次/天|
......@@ -317,7 +317,7 @@ uniCloud提供包月、按量计费两种计费方式(仅腾讯云),具体
|云函数外网出流量|10GB/月|
|云函数并发限制|1000/云函数|
|云函数数量限制|99个|
|云函数支持固定出口IP|×|
|云函数支持固定出口IP||
|数据库容量|10GB|
|数据库同时连接数|200个|
|数据库读操作数|150万次/天|
......@@ -427,7 +427,7 @@ uniCloud提供包月、按量计费两种计费方式(仅腾讯云),具体
|云函数外网出流量|25GB/月|
|云函数并发限制|1000/云函数|
|云函数数量限制|149个|
|云函数支持固定出口IP|×|
|云函数支持固定出口IP||
|数据库容量|10GB|
|数据库同时连接数|400个|
|数据库读操作数|500万次/天|
......
#### 2021-07-07
+ web控制台 新增 违规文件列表
+ web控制台 修复 云存储删除文件总数不变的Bug
+ web控制台 调整 云函数日志默认查询开始时间为2小时前
+ web控制台 腾讯云 新增 数据库导入、导出
+ web控制台 腾讯云 新增 前端网页托管可开启HTTP强制跳转HTTPS
+ web控制台 腾讯云 新增 云函数url化允许 / 作为触发路径
+ web控制台 阿里云 新增 前端网页托管域名归属验证
#### 2021-06-23
+ 新增 uniCloud响应体规范,方便前端拦截器统一处理、方便国际化 [详情](https://uniapp.dcloud.net.cn/uniCloud/unicloud-response-format)
+ 客户端 新增 添加拦截器、移除拦截器API [详情](https://uniapp.dcloud.net.cn/uniCloud/client-sdk?id=add-interceptor)
+ 客户端 修复 HBuilderX 3.1.17-alpha 引出的 db.on("error") 回调不执行的Bug
+ 客户端 修复 leftWindow、topWindow 中使用 uniCloud 腾讯云报错的Bug [详情](https://ask.dcloud.net.cn/question/125039)
+ DB Schema 调整 enum 属性最大可枚举500条数据
#### 2021-06-03
+ 修复 HBuilderX 3.1.16 引出的未关联服务空间时无法获取 uniCloud.mixinDatacom 的Bug
+ 修复 HBuilderX 3.1.16 引出的某些情况下关联腾讯云服务空间的项目运行报错的Bug
......
......@@ -94,7 +94,7 @@ properties里的字段列表,每个字段都有很多可以设置的属性,
|title|string|标题,开发者维护时自用。如果不填label属性,将在生成前端表单代码时,默认用于表单项前面的label|
|description|string|描述,开发者维护时自用。在生成前端表单代码时,如果字段未设置componentForEdit,且字段被渲染为input,那么input的placehold将默认为本描述|
|required|array|是否必填。支持填写必填的下级字段名称。required可以在表级的描述出现,约定该表有哪些字段必填。也可以在某个字段中出现,如果该字段是一个json,可以对这个json中的哪些字段必填进行描述。详见下方示例|
|enum|Array|字段值枚举范围,数组中至少要有一个元素,且数组内的每一个元素都是唯一的。|
|enum|Array|字段值枚举范围,数组中至少要有一个元素,且数组内的每一个元素都是唯一的。**enum最多只可以枚举500条**|
|enumType|String|字段值枚举类型,可选值tree。设为tree时,代表enum里的数据为树形结构。此时schema2code可生成多级级联选择组件|
|arrayType|String|数组项类型,bsonType="array" 时有效,HBuilderX 3.1.0+ 支持,具体见下表arrayType可用类型|
|fileMediaType|String|文件类型,可选值 all&#124;image&#124;video 默认值为all,表示所有文件,image表示图片类型文件,video表示视频类型文件 HBuilderX 3.1.0+|
......@@ -313,7 +313,7 @@ arrayType为file时,与单独的bsonType为file相同,`<uni-file-picker>`组
#### enum属性@enum
enum,即枚举。一个字段设定了enum后,该字段的合法内容,只能在enum设定的数据项中取值。
enum,即枚举。一个字段设定了enum后,该字段的合法内容,只能在enum设定的数据项中取值**enum最多只可以枚举500条**
enum支持3种数据格式:
1. 简单数组
......
......@@ -60,7 +60,7 @@
**短信测试模板说明**
运营商目前审核比较严格,处于开发阶段的应用可能无法通过运营商的审核。为方便开发者测试短信功能,DCloud 提供了一个测试模板,该模板的templateId为:uni_sms_test,内容为:`【DC】尊敬的用户,您的验证码是:${code}。5分钟内有效,请尽快验证。请勿泄漏您的验证码。`
运营商目前审核比较严格,处于开发阶段的应用可能无法通过运营商的审核。为方便开发者测试短信功能,DCloud 提供了一个测试模板,该模板的templateId为:uni_sms_test,内容为:`【DC】尊敬的用户,您的验证码是:${code}。5分钟内有效,请尽快验证。请勿泄漏您的验证码。` **(注意:目前测试模板仅联通及电信用户可用)**
使用该模板的限制:
......
......@@ -8,7 +8,7 @@
`uni-id``uniCloud`开发者提供了简单、统一、可扩展的用户管理能力封装。
[clientDB](uniCloud/clientDB)[DB Schema](uniCloud/schema)[uniCloud admin](uniCloud/admin),这些产品都基于`uni-id`的账户体系。可以说`uni-id`是uniCloud不可或缺的基础能力。
[clientDB](uniCloud/clientDB)[DB Schema](uniCloud/schema)[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057)[uniCloud admin](uniCloud/admin),这些产品都基于`uni-id`的账户体系。可以说`uni-id`是uniCloud不可或缺的基础能力。
# 组成部分
......@@ -52,7 +52,7 @@ uniCloud框架底层,会自动在callfunction时传递`uni-id`的token(uni-a
规范,还可以让上下游充分协同。插件市场会出现各种数据迁移插件,比如把从discuz里把用户迁移到`uni-id`中的插件,相信围绕这套规范的产业链会非常活跃。
事实上,[clientDB](uniCloud/clientDB)[DB Schema](uniCloud/schema)[uniCloud admin](uniCloud/admin)等重要uniCloud产品,以及插件市场上各种优秀的轮子,都是基于`uni-id`的。
事实上,[clientDB](uniCloud/clientDB)[DB Schema](uniCloud/schema)[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057)[uniCloud admin](uniCloud/admin)等重要uniCloud产品,以及插件市场上各种优秀的轮子,都是基于`uni-id`的。
# 现状和未来
......@@ -451,6 +451,7 @@ password入库时会自动进行一次sha1加密,不明文存储密码。这
但任何加密算法,在撞库等暴力手段面前被攻破只是时间和算力问题。使用自己特定的而不是默认的passwordSecret,并保护好passwordSecret可以数倍提升破解的算力代价。
uni-id公共模块没有限制密码的强度,如长度限制、是否包含大小写或数据等限制,这类限制需要开发者自行在云函数中处理。
**注意:RegisterParams不仅支持如上列举字段,比如可以直接传入mobile即可设置手机号码,切勿直接传入客户端传来的参数,否则这是一个极大的安全问题**
**响应参数**
......@@ -481,9 +482,9 @@ exports.main = async function(event,context) {
}
}
// 自动验证用户名是否与已经注册的用户名重复,如果重复会直接返回错误。否则会自动生成token并加密password存储username、password、token到数据表uni-id-users,并返回如上响应参数
const res = await uniID.register({
username,
password
const res = await uniID.register({ //支持传入任何值,比如可以直接传入mobile即可设置手机号码,切勿直接传入event否则这是一个极大的安全问题
username,
password
})
return res
}
......@@ -1260,7 +1261,7 @@ exports.main = async function(event,context) {
用法:`uniID.loginByUniverify(Object loginByUniverifyParams)`
> 需[开发者控制台](https://dev.dcloud.net.cn/uniLogin)开通一键登录并在config.json内配置univerify相关信息
> 需[开发者控制台](https://dev.dcloud.net.cn/uniLogin)开通一键登录并在config.json内配置univerify相关信息
**参数说明**
......@@ -1570,19 +1571,23 @@ exports.main = async function(event,context) {
**响应参数**
| 字段 | 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code | Number | 是 |错误码,0表示成功 |
| message | String | 是 |详细信息 |
| uid | String | 是 |用户uid |
| type | String | 是 |操作类型,`login`为登录、`register`为注册|
| openid | String | 是 |用户openid |
| unionid | String | 否 |用户unionid,能取到此参数时会返回 |
| token | String | 是 |登录成功之后返回的token信息 |
| userInfo | Object | 否 |用户全部信息,`type``login`时返回 |
| tokenExpired | String | 是 |token过期时间 |
| mobileConfirmed | Boolean | 是 |是否已验证手机号 |
| emailConfirmed | Boolean | 是 |是否已验证邮箱 |
| 字段 | 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code | Number | 是 |错误码,0表示成功 |
| message | String | 是 |详细信息 |
| uid | String | 是 |用户uid |
| type | String | 是 |操作类型,`login`为登录、`register`为注册 |
| openid | String | 是 |用户openid |
| unionid | String | 否 |用户unionid,能取到此参数时会返回 |
| token | String | 是 |登录成功之后返回的token信息 |
| userInfo | Object | 否 |用户全部信息,`type``login`时返回 |
| tokenExpired | String | 是 |token过期时间 |
| mobileConfirmed | Boolean | 是 |是否已验证手机号 |
| emailConfirmed | Boolean | 是 |是否已验证邮箱 |
| sessionKey | String | - |客户端为微信小程序时返回 |
| accessToken | String | - |客户端为APP时返回,微信接口调用凭证,新增于`uni-id 3.1.1` |
| refreshToken | String | - |客户端为APP时返回,用于刷新accessToken,新增于`uni-id 3.1.1` |
| expired | Number | - |客户端为APP时返回,accessToken 接口调用凭证超时时间对应的时间戳,新增于`uni-id 3.1.1`|
**示例代码**
......@@ -1674,6 +1679,8 @@ export default {
### 获取微信openid
> 此接口即将废弃
用法:`uniID.code2SessionWeixin(Object Code2SessionWeixinParams);`
**参数说明**
......@@ -1687,13 +1694,14 @@ export default {
| 字段 | 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code | Number| 是 |错误码,0表示成功 |
| message | String| 是 |详细信息 |
| message | String| 是 |详细信息 |
| openid | String| - |用户openid |
| unionid | String| - |用户unionid,可以取到此值时返回 |
| sessionKey | String| - |客户端为微信小程序时返回 |
| accessToken | String| - |客户端为APP时返回 |
| expiresIn | String| - |客户端为APP时返回,accessToken 接口调用凭证超时时间,单位(秒)|
| refreshToken| String| - |客户端为APP时返回,用于刷新accessToken |
| refreshToken| String| - |客户端为APP时返回,用于刷新accessToken |
| expired | Number| - |客户端为APP时返回,accessToken 接口调用凭证超时时间对应的时间戳,新增于`uni-id 3.1.1`|
| expiresIn | Number| - |客户端为APP时返回,accessToken 接口调用凭证过期时间,单位(秒)|
```js
// 云函数代码
......@@ -1719,10 +1727,16 @@ exports.main = async function(event,context) {
**响应参数**
| 字段| 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code| Number| 是 |错误码,0表示成功|
| message | String| 是 |详细信息 |
| 字段 | 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code | Number| 是 |错误码,0表示成功 |
| message | String| 是 |详细信息 |
| openid | String| 是 |用户openid |
| unionid | String| 否 |用户unionid,能取到此参数时会返回 |
| sessionKey | String| - |客户端为微信小程序时返回,新增于`uni-id 3.1.1` |
| accessToken | String| - |客户端为APP时返回,微信接口调用凭证,新增于`uni-id 3.1.1` |
| refreshToken| String| - |客户端为APP时返回,用于刷新accessToken,新增于`uni-id 3.1.1` |
| expired | Number| - |客户端为APP时返回,accessToken 接口调用凭证超时时间对应的时间戳,新增于`uni-id 3.1.1`|
```js
// 云函数代码
......@@ -1772,6 +1786,8 @@ exports.main = async function(event,context) {
### 微信数据解密
> 此接口即将废弃
用法:`uniID.wxBizDataCrypt(Object WxBizDataCryptParams);`
**参数说明**
......@@ -1816,19 +1832,19 @@ exports.main = async function(event,context) {
**LoginByAlipayParams参数说明**
| 字段 | 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code | String| 是 |支付宝登录返回的code |
| myInviteCode| String| 否 |设置当前注册用户自己的邀请码,type为`register`时生效 |
| 字段 | 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code | String | 是 |支付宝登录返回的code |
| myInviteCode | String | 否 |设置当前注册用户自己的邀请码,type为`register`时生效 |
| needPermission| Boolean | 否 |设置为true时会在checkToken时返回用户权限(permission),建议在管理控制台中使用 |
| role | Array | 否 |设定用户角色 ,当前用户为新注册时生效 |
| role | Array | 否 |设定用户角色,当前用户为新注册时生效 |
**响应参数**
| 字段 | 类型 | 必填| 说明 |
| --- | --- | --- | --- |
| code | Number | 是 |错误码,0表示成功 |
| message | String | 是 |详细信息 |
| message | String | 是 |详细信息 |
| uid | String | 是 |用户uid |
| type | String | 是 |操作类型,`login`为登录、`register`为注册|
| openid | String | 是 |用户openid |
......@@ -1856,6 +1872,8 @@ exports.main = async function(event,context) {
### 获取支付宝用户ID
> 此接口即将废弃
用法:`uniID.code2SessionAlipay(Object Code2SessionAlipayParams);`
**参数说明**
......@@ -2076,6 +2094,8 @@ export default {
### Apple登录校验identityToken
> 此接口即将废弃
用法:`uniID.verifyAppleIdentityToken(Object Code2SessionAppleParams);`
**参数说明**
......@@ -2689,9 +2709,44 @@ exports.main = async function(event,context) {
- 任务表:uni-id-task
- 任务日志表:uni-id-task-log
# 错误码
`1.1.0`版本使用此错误码规范
# 错误码@errcode
**自`3.1.1`版本起使用此错误码规范**
`3.1.1`版本起uni-id使用errCode作为错误码,errMsg作为错误信息,为兼容旧版本,code、message字段仍保留。
errCode和errMsg对照表如下:
|错误码(errCode) |详细信息(errMsg) |说明 |
|--- |--- |--- |
|0 |成功 |操作成功 |
|uni-id-account-banned |账号已禁用 |账号已禁用 |
|uni-id-user-not-exist |用户不存在 |用户不存在 |
|uni-id-multi-user-matched |匹配到多个账号 |匹配到多个账号 |
|uni-id-password-error |密码错误 |密码错误 |
|uni-id-password-error-exceed-limit |密码错误次数过多 |密码错误次数过多 |
|uni-id-account-already-registed |此{type}已注册 |此账号已注册、包括手机号、微信等 |
|uni-id-account-not-registed |此{type}尚未注册 |此账号尚未注册、包括手机号、微信等 |
|uni-id-invalid-invite-code |邀请码无效 |邀请码无效 |
|uni-id-get-third-party-account-failed |获取{account}失败 |获取三方平台账号失败 |
|uni-id-param-required |{param}不可为空 |字段不可为空 |
|uni-id-check-device-feature-failed |设备特征校验未通过 |设备特征校验未通过 |
|uni-id-token-not-exist |云端已不包含此token |云端已不包含此token |
|uni-id-token-expired |token已过期 |token已过期 |
|uni-id-check-token-failed |token校验未通过 |token校验未通过 |
|uni-id-invalid-old-password |旧密码错误 |旧密码错误 |
|uni-id-param-error |{param}参数错误,{reason}|参数错误 |
|uni-id-invalid-verify-code |验证码错误或已失效 |验证码错误或已失效 |
|uni-id-send-sms-code-failed |验证码发送失败 |验证码发送失败 |
|uni-id-account-already-bound |此{type}已绑定 |此账号已绑定、包括手机号、微信等 |
|uni-id-unbind-failed |解绑失败 |解绑失败 |
|uni-id-set-invite-code-failed |邀请码设置失败 |邀请码设置失败 |
|uni-id-modify-invite-code-is-not-allowed |邀请码不可修改 |邀请码不可修改 |
|uni-id-database-operation-failed |数据库读写异常 |数据库读写异常 |
|uni-id-role-not-exist |角色不存在 |角色不存在 |
|uni-id-permission-not-exist |权限不存在 |权限不存在 |
**自`1.1.0`版本使用此错误码规范**
|模块 |模块码 |错误代码 |错误信息 |
|:-: |:-: |:-: |:-: |
......
......@@ -479,6 +479,7 @@ udb为unicloud-db组件的ref属性值
|属性|类型|默认值|描述|
|:-|:-|:-|:-|
|action|string||云端执行数据库查询的前或后,触发某个action函数操作,进行预处理或后处理,详情。HBuilder 3.1.0+|
|showToast|boolean|true|是否显示更新成功后的提示框|
|toastTitle|string|新增成功|新增成功后的toast提示|
|needLoading|boolean|true|是否显示Loading,HBuilderX 3.1.5+|
|loadingTitle|string|''|显示loading的标题,HBuilderX 3.1.5+|
......@@ -541,6 +542,7 @@ udb为unicloud-db组件的ref属性值
|属性|类型|默认值|描述|
|:-|:-|:-|:-|
|action|string||云端执行数据库查询的前或后,触发某个action函数操作,进行预处理或后处理,详情。HBuilder 3.1.0+|
|showToast|boolean|true|是否显示更新成功后的提示框|
|toastTitle|string|修改成功|修改成功后的toast提示|
|needConfirm|boolean|true|控制是否有弹出框,HBuilderX 3.1.5+|
|needLoading|boolean|true|是否显示Loading,HBuilderX 3.1.5+|
......
## uniCloud响应体规范
uniCloud响应体规范(uniCloud response format),是DCloud官方制定的、服务器给客户端返回json数据的一种建议格式。
uniCloud响应体规范(uniCloud response format),是DCloud官方制定的、服务器给客户端返回json数据的一种建议格式。后续uni-id、uni-pay、clientDB等均会调整为此结构
**由来**
......@@ -43,7 +43,7 @@ errCode在成功时应返回数字`0`,失败时应返回一个以插件id开头
errMsg用于存放具体错误信息,包括展示给开发者、终端用户的错误信息
占位变量格式说明:
<!-- 占位变量格式说明:
## 支持情况
1. 从xx版本开始,clientDB遵循该格式
......@@ -51,4 +51,4 @@ errMsg用于存放具体错误信息,包括展示给开发者、终端用户
3. uniCloud客户端sdk后续会提供callFunction及数据库操作的拦截器,开发者可以在拦截器内对特定的错误码进行处理。例如:在收到token过期的错误码时进行提示并跳转到登录页面
4. uniCloud admin和uni-starter等插件内置的网络拦截器也均将支持该格式。
历史兼容说明:
\ No newline at end of file
历史兼容说明: -->
\ No newline at end of file
......@@ -193,7 +193,7 @@ univerifyStyle 数据结构:
"textColor": "#656565", // 其他登录按钮文字颜色 默认值:#656565
"title": "其他登录方式", // 其他登录方式按钮文字 默认值:“其他登录方式”
"borderColor": "", //边框颜色 默认值:透明(仅iOS支持)
"borderRadius": "0px" // 其他登录按钮圆角 默认值:"0px"
"borderRadius": "0px" // 其他登录按钮圆角 默认值:"24px" (按钮高度的一半)
},
"privacyTerms": {
"defaultCheckBoxState":"true", // 条款勾选框初始状态 默认值: true
......
......@@ -282,18 +282,23 @@
> 每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
- 这个 `prop` 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 `prop` 数据来使用。
- 这个 `prop` 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 `prop` 数据来使用。在这种情况下,最好定义一个本地的 `data property` 并将这个 `prop` 用作其**初始值**
```html
<template>
<view>
<!-- 我是子组件componentA -->
<view>{{title}}</view>
<view>{{myTitle}}</view>
</view>
</template>
<script>
export default {
props: ['title']
props: ['title'],
data() {
return {
myTitle:this.title
}
}
}
</script>
```
......
......@@ -54,6 +54,15 @@ describe('mp:compiler-mp-alipay', () => {
wxComponents: { component1: '/mycomponents/component1' }
}
)
assertCodegen(
'<credit-pay @change="onChange">text</credit-pay>',
'<credit-pay onChange="__e" vue-id="551070e6-1" data-event-opts="{{[[\'^change\',[[\'onChange\']]]]}}" data-com-type="wx" ref="__r" onVueInit="__l">text</credit-pay>',
undefined,
undefined,
{
wxComponents: { 'credit-pay': 'plugin://myPlugin/CreditPay' }
}
)
})
it('generate slot fallback content', () => {
assertCodegen(
......@@ -229,5 +238,10 @@ describe('mp:compiler-mp-alipay', () => {
'<movable-view @changeend="changeEnd"/>',
'<movable-view data-event-opts="{{[[\'changeEnd\',[[\'changeEnd\',[\'$event\']]]]]}}" onChangeEnd="__e"></movable-view>'
)
assertCodegen(
'<life-follow @close="close"/>',
'<life-follow data-event-opts="{{[[\'close\',[[\'close\',[\'$event\']]]]]}}" onClose="__e"></life-follow>'
)
})
})
......@@ -228,6 +228,14 @@ describe('mp:compiler-mp-weixin', () => {
scopedSlotsCompiler: 'augmented'
}
)
assertCodegen(
'<my-component><template v-slot="{item}">{{item}}<my-component><template v-slot="{item}">{{item}}<template></my-component><template></my-component>',
'<my-component vue-id="551070e6-1" bind:__l="__l" vue-slots="{{[\'default\']}}"><block><block wx:if="{{$root.m0}}">{{$root.m1}}<my-component vue-id="{{(\'551070e6-2\')+\',\'+(\'551070e6-1\')}}" bind:__l="__l" vue-slots="{{[\'default\']}}"><block><block wx:if="{{$root.m2}}">{{$root.m3}}</block></block></my-component></block></block></my-component>',
'with(this){var m0=$hasScopedSlotsParams("551070e6-1");var m1=m0?$getScopedSlotsParams("551070e6-1","default","item"):null;var m2=$hasScopedSlotsParams("551070e6-2");var m3=m2?$getScopedSlotsParams("551070e6-2","default","item"):null;$mp.data=Object.assign({},{$root:{m0:m0,m1:m1,m2:m2,m3:m3}})}',
{
scopedSlotsCompiler: 'augmented'
}
)
})
it('generate scoped slot', () => {
......
const EVENTS = {
click: 'tap'
}
const tags = [
'slot',
'block',
'component',
'template',
const tags = {
// 小程序平台通用组件
base: [
'slot',
'block',
'component',
'template',
'ad',
'audio',
'button',
'camera',
'canvas',
'checkbox',
'checkbox-group',
'cover-image',
'cover-view',
'form',
'functional-page-navigator',
'icon',
'image',
'input',
'label',
'live-player',
'live-pusher',
'map',
'movable-area',
'movable-view',
'navigator',
'official-account',
'open-data',
'picker',
'picker-view',
'picker-view-column',
'progress',
'radio',
'radio-group',
'rich-text',
'scroll-view',
'slider',
'swiper',
'swiper-item',
'switch',
'text',
'textarea',
'video',
'view',
'web-view',
'editor'
]
'ad',
'audio',
'button',
'camera',
'canvas',
'checkbox',
'checkbox-group',
'cover-image',
'cover-view',
'form',
'functional-page-navigator',
'icon',
'image',
'input',
'label',
'live-player',
'live-pusher',
'map',
'movable-area',
'movable-view',
'navigator',
'official-account',
'open-data',
'picker',
'picker-view',
'picker-view-column',
'progress',
'radio',
'radio-group',
'rich-text',
'scroll-view',
'slider',
'swiper',
'swiper-item',
'switch',
'text',
'textarea',
'video',
'view',
'web-view',
'editor',
],
// 支付宝小程序平台独有组件
'mp-alipay': [
'lifestyle',
'life-follow'
]
}
const baseCompiler = {
ref: 'data-ref',
......@@ -60,7 +68,7 @@ const baseCompiler = {
* 目前 template 在前,script 在后,要做的话,就需要把 wxml 的生成机制放到 plugin 中才可以拿到真实的组件列表
*/
isComponent (tagName) {
return !tags.includes(tagName)
return !tags.base.concat(tags[this.name] || []).includes(tagName)
},
createFilterTag (filterTag, {
content,
......
......@@ -26,11 +26,12 @@ function needSlotMode (path, ids) {
function replaceId (path, ids) {
let replaced
const fnPath = path.parentPath
path.traverse({
noScope: true,
noScope: false,
Identifier (path) {
const name = path.node.name
if (name in ids && path.key !== 'key' && (path.key !== 'property' || path.parent.computed)) {
if (name in ids && path.key !== 'key' && (path.key !== 'property' || path.parent.computed) && path.scope.path === fnPath) {
path.replaceWith(ids[name])
replaced = true
}
......
......@@ -265,6 +265,10 @@ if (platformOptions.usingComponents === true) {
const modes = ['legacy', 'auto', 'augmented']
const scopedSlotsCompiler = !platformOptions.scopedSlotsCompiler && platformOptions.betterScopedSlots ? modes[2] : platformOptions.scopedSlotsCompiler
process.env.SCOPED_SLOTS_COMPILER = modes.includes(scopedSlotsCompiler) ? scopedSlotsCompiler : modes[1]
// 快手小程序抽象组件编译报错,如未指定 legacy 固定为 augmented 模式
if (process.env.UNI_PLATFORM === 'mp-kuaishou' && process.env.SCOPED_SLOTS_COMPILER !== modes[0]) {
process.env.SCOPED_SLOTS_COMPILER = modes[2]
}
if (
process.env.UNI_USING_COMPONENTS ||
......
......@@ -4616,7 +4616,7 @@ function initProps (vm, propsOptions) {
defineReactive$$1(props, key, value, function () {
if (!isRoot && !isUpdatingChildComponent) {
{
if(vm.mpHost === 'mp-baidu'){//百度 observer 在 setData callback 之后触发,直接忽略该 warn
if(vm.mpHost === 'mp-baidu' || vm.mpHost === 'mp-kuaishou'){//百度、快手 observer 在 setData callback 之后触发,直接忽略该 warn
return
}
//fixed by xxxxxx __next_tick_pending,uni://form-field 时不告警
......
<template>
<uni-input v-on="$listeners">
<div
ref="wrapper"
class="uni-input-wrapper"
<div
ref="wrapper"
class="uni-input-wrapper"
>
<div
v-show="!(composing || valueSync.length || cachedValue === '-')"
......@@ -25,7 +25,7 @@
:enterkeyhint="confirmType"
:pattern="type === 'number' ? '[0-9]*' : null"
class="uni-input-input"
autocomplete="off"
:autocomplete="autocomplete"
@change.stop
@focus="_onFocus"
@blur="_onBlur"
......@@ -54,8 +54,10 @@
import {
field
} from 'uni-mixins'
const INPUT_TYPES = ['text', 'number', 'idcard', 'digit', 'password']
import { kebabCase } from 'uni-shared'
const INPUT_TYPES = ['text', 'number', 'idcard', 'digit', 'password', 'tel']
const NUMBER_TYPES = ['number', 'digit']
const AUTOCOMPLETES = ['off', 'one-time-code']
export default {
name: 'Input',
mixins: [field],
......@@ -95,6 +97,10 @@ export default {
confirmType: {
type: String,
default: 'done'
},
textContentType: {
type: String,
default: ''
}
},
data () {
......@@ -126,6 +132,16 @@ export default {
step () {
// 处理部分设备中无法输入小数点的问题
return ~NUMBER_TYPES.indexOf(this.type) ? '0.000000000000000001' : ''
},
autocomplete () {
const camelizeIndex = AUTOCOMPLETES.indexOf(this.textContentType)
const kebabCaseIndex = AUTOCOMPLETES.indexOf(kebabCase(this.textContentType))
const index = camelizeIndex !== -1
? camelizeIndex
: kebabCaseIndex !== -1
? kebabCaseIndex
: 0
return AUTOCOMPLETES[index]
}
},
watch: {
......
......@@ -6,7 +6,10 @@
>
<div
ref="main"
:style="{'overflow-x': scrollX?'auto':'hidden','overflow-y': scrollY?'auto':'hidden'}"
:style="{
'overflow-x': scrollX ? 'auto' : 'hidden',
'overflow-y': scrollY ? 'auto' : 'hidden',
}"
class="uni-scroll-view"
>
<div
......@@ -16,7 +19,10 @@
<div
v-if="refresherEnabled"
ref="refresherinner"
:style="{'background-color': refresherBackground, 'height': refresherHeight + 'px'}"
:style="{
'background-color': refresherBackground,
height: refresherHeight + 'px',
}"
class="uni-scroll-view-refresher"
>
<div
......@@ -25,23 +31,25 @@
>
<div class="uni-scroll-view-refresh-inner">
<svg
v-if="refreshState=='pulling'"
v-if="refreshState == 'pulling'"
key="refresh__icon"
:style="{'transform': 'rotate('+ refreshRotate +'deg)'}"
:style="{ transform: 'rotate(' + refreshRotate + 'deg)' }"
fill="#2BD009"
class="uni-scroll-view-refresh__icon"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" />
<path
d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
/>
<path
d="M0 0h24v24H0z"
fill="none"
/>
</svg>
<svg
v-if="refreshState=='refreshing'"
v-if="refreshState == 'refreshing'"
key="refresh__spinner"
class="uni-scroll-view-refresh__spinner"
width="24"
......@@ -53,14 +61,14 @@
cy="50"
r="20"
fill="none"
style="color: #2BD009;"
style="color: #2bd009"
stroke-width="3"
/>
</svg>
</div>
</div>
<slot
v-if="refresherDefaultStyle=='none'"
v-if="refresherDefaultStyle == 'none'"
name="refresher"
/>
</div>
......@@ -72,17 +80,17 @@
</template>
<script>
import scroller from 'uni-mixins/scroller/index'
import {
supportsPassive
} from 'uni-shared'
import { supportsPassive } from 'uni-shared'
import {
initScrollBounce,
disableScrollBounce
} from 'uni-platform/helpers/scroll'
const passiveOptions = supportsPassive ? {
passive: true
} : false
const passiveOptions = supportsPassive
? {
passive: true
}
: false
// const PULLING = 'pulling'
// const REFRESHING = 'refreshing'
......@@ -196,7 +204,14 @@ export default {
},
mounted () {
var self = this
var touchStart = null
var needStop = null
this._attached = true
this.toUpperNumber = 0 // 容器触顶时,此时鼠标Y轴位置
this.triggerAbort = false
this.beforeRefreshing = false
this._scrollTopChanged(this.scrollTopNumber)
this._scrollLeftChanged(this.scrollLeftNumber)
this._scrollIntoViewChanged(this.scrollIntoView)
......@@ -205,11 +220,6 @@ export default {
event.stopPropagation()
self._handleScroll.bind(self, event)()
}
var touchStart = null
var needStop = null
let toUpperNumber = 0 // 容器触顶时,此时鼠标Y轴位置
let triggerAbort = false
let beforeRefreshing = false
this.__handleTouchMove = function (event) {
var x = event.touches[0].pageX
......@@ -222,7 +232,10 @@ export default {
if (main.scrollLeft === 0 && x > touchStart.x) {
needStop = false
return
} else if (main.scrollWidth === main.offsetWidth + main.scrollLeft && x < touchStart.x) {
} else if (
main.scrollWidth === main.offsetWidth + main.scrollLeft &&
x < touchStart.x
) {
needStop = false
return
}
......@@ -233,15 +246,21 @@ export default {
} else {
// 纵向滑动
if (self.scrollY) {
if (self.refresherEnabled && main.scrollTop === 0 && y > touchStart.y) {
needStop = true
if (main.scrollTop === 0 && y > touchStart.y) {
needStop = false
// 刷新时,阻止页面滚动
if (event.cancelable !== false) event.preventDefault()
} else if (main.scrollHeight === main.offsetHeight + main.scrollTop && y < touchStart.y) {
if (self.refresherEnabled && event.cancelable !== false) {
event.preventDefault()
}
} else if (
main.scrollHeight === main.offsetHeight + main.scrollTop &&
y < touchStart.y
) {
needStop = false
return
} else {
needStop = true
}
needStop = true
} else {
needStop = false
}
......@@ -258,14 +277,14 @@ export default {
if (self.refresherEnabled && self.refreshState === 'pulling') {
const dy = y - touchStart.y
if (toUpperNumber === 0) {
toUpperNumber = y
if (self.toUpperNumber === 0) {
self.toUpperNumber = y
}
if (!beforeRefreshing) {
self.refresherHeight = y - toUpperNumber
if (!self.beforeRefreshing) {
self.refresherHeight = y - self.toUpperNumber
// 之前为刷新状态则不再触发pulling
if (self.refresherHeight > 0) {
triggerAbort = true
self.triggerAbort = true
self.$trigger('refresherpulling', event, {
deltaY: dy
})
......@@ -273,7 +292,7 @@ export default {
} else {
self.refresherHeight = dy + self.refresherThreshold
// 如果之前在刷新状态,则不触发刷新中断
triggerAbort = false
self.triggerAbort = false
}
const route = self.refresherHeight / self.refresherThreshold
......@@ -298,28 +317,31 @@ export default {
disable: false
})
if (self.refresherHeight >= self.refresherThreshold) {
self.refresherHeight = self.refresherThreshold
self.refreshState = 'refreshing'
// 之前是刷新状态则不再触发刷新
if (beforeRefreshing) return
beforeRefreshing = true
self._setRefreshState('refreshing')
} else {
beforeRefreshing = false
self.refreshState = 'refresherabort'
self.refresherHeight = toUpperNumber = 0
if (triggerAbort) {
triggerAbort = false
self.$trigger('refresherabort', event, {})
}
self._setRefreshState('refresherabort')
}
}
this.$refs.main.addEventListener('touchstart', this.__handleTouchStart, passiveOptions)
this.$refs.main.addEventListener(
'touchstart',
this.__handleTouchStart,
passiveOptions
)
this.$refs.main.addEventListener('touchmove', this.__handleTouchMove)
this.$refs.main.addEventListener('scroll', this.__handleScroll, supportsPassive ? {
passive: false
} : false)
this.$refs.main.addEventListener('touchend', this.__handleTouchEnd, passiveOptions)
this.$refs.main.addEventListener(
'scroll',
this.__handleScroll,
supportsPassive
? {
passive: false
}
: false
)
this.$refs.main.addEventListener(
'touchend',
this.__handleTouchEnd,
passiveOptions
)
initScrollBounce()
},
activated () {
......@@ -328,34 +350,70 @@ export default {
this.scrollX && (this.$refs.main.scrollLeft = this.lastScrollLeft)
},
beforeDestroy () {
this.$refs.main.removeEventListener('touchstart', this.__handleTouchStart, passiveOptions)
this.$refs.main.removeEventListener('touchmove', this.__handleTouchMove, passiveOptions)
this.$refs.main.removeEventListener('scroll', this.__handleScroll, supportsPassive ? {
passive: false
} : false)
this.$refs.main.removeEventListener('touchend', this.__handleTouchEnd, passiveOptions)
this.$refs.main.removeEventListener(
'touchstart',
this.__handleTouchStart,
passiveOptions
)
this.$refs.main.removeEventListener(
'touchmove',
this.__handleTouchMove,
passiveOptions
)
this.$refs.main.removeEventListener(
'scroll',
this.__handleScroll,
supportsPassive
? {
passive: false
}
: false
)
this.$refs.main.removeEventListener(
'touchend',
this.__handleTouchEnd,
passiveOptions
)
},
methods: {
scrollTo: function (t, n) {
var i = this.$refs.main
t < 0 ? t = 0 : n === 'x' && t > i.scrollWidth - i.offsetWidth ? t = i.scrollWidth - i.offsetWidth
: n === 'y' && t > i.scrollHeight - i.offsetHeight && (t = i.scrollHeight - i.offsetHeight)
t < 0
? (t = 0)
: n === 'x' && t > i.scrollWidth - i.offsetWidth
? (t = i.scrollWidth - i.offsetWidth)
: n === 'y' &&
t > i.scrollHeight - i.offsetHeight &&
(t = i.scrollHeight - i.offsetHeight)
var r = 0
var o = ''
n === 'x' ? r = i.scrollLeft - t : n === 'y' && (r = i.scrollTop - t)
n === 'x' ? (r = i.scrollLeft - t) : n === 'y' && (r = i.scrollTop - t)
if (r !== 0) {
this.$refs.content.style.transition = 'transform .3s ease-out'
this.$refs.content.style.webkitTransition = '-webkit-transform .3s ease-out'
this.$refs.content.style.webkitTransition =
'-webkit-transform .3s ease-out'
if (n === 'x') {
o = 'translateX(' + r + 'px) translateZ(0)'
} else {
n === 'y' && (o = 'translateY(' + r + 'px) translateZ(0)')
}
this.$refs.content.removeEventListener('transitionend', this.__transitionEnd)
this.$refs.content.removeEventListener('webkitTransitionEnd', this.__transitionEnd)
this.$refs.content.removeEventListener(
'transitionend',
this.__transitionEnd
)
this.$refs.content.removeEventListener(
'webkitTransitionEnd',
this.__transitionEnd
)
this.__transitionEnd = this._transitionEnd.bind(this, t, n)
this.$refs.content.addEventListener('transitionend', this.__transitionEnd)
this.$refs.content.addEventListener('webkitTransitionEnd', this.__transitionEnd)
this.$refs.content.addEventListener(
'transitionend',
this.__transitionEnd
)
this.$refs.content.addEventListener(
'webkitTransitionEnd',
this.__transitionEnd
)
if (n === 'x') {
// if (e !== 'ios') {
i.style.overflowX = 'hidden'
......@@ -379,14 +437,22 @@ export default {
this._noBubble = false
}
if (this._noBubble === null && this.scrollY) {
if (Math.abs(this._y - $event.detail.y) / Math.abs(this._x - $event.detail.x) > 1) {
if (
Math.abs(this._y - $event.detail.y) /
Math.abs(this._x - $event.detail.x) >
1
) {
this._noBubble = true
} else {
this._noBubble = false
}
}
if (this._noBubble === null && this.scrollX) {
if (Math.abs(this._x - $event.detail.x) / Math.abs(this._y - $event.detail.y) > 1) {
if (
Math.abs(this._x - $event.detail.x) /
Math.abs(this._y - $event.detail.y) >
1
) {
this._noBubble = true
} else {
this._noBubble = false
......@@ -411,13 +477,24 @@ export default {
deltaY: this.lastScrollTop - target.scrollTop
})
if (this.scrollY) {
if (target.scrollTop <= this.upperThresholdNumber && this.lastScrollTop - target.scrollTop > 0 && $event.timeStamp - this.lastScrollToUpperTime > 200) {
if (
target.scrollTop <= this.upperThresholdNumber &&
this.lastScrollTop - target.scrollTop > 0 &&
$event.timeStamp - this.lastScrollToUpperTime > 200
) {
this.$trigger('scrolltoupper', $event, {
direction: 'top'
})
this.lastScrollToUpperTime = $event.timeStamp
}
if (target.scrollTop + target.offsetHeight + this.lowerThresholdNumber >= target.scrollHeight && this.lastScrollTop - target.scrollTop < 0 && $event.timeStamp - this.lastScrollToLowerTime > 200) {
if (
target.scrollTop +
target.offsetHeight +
this.lowerThresholdNumber >=
target.scrollHeight &&
this.lastScrollTop - target.scrollTop < 0 &&
$event.timeStamp - this.lastScrollToLowerTime > 200
) {
this.$trigger('scrolltolower', $event, {
direction: 'bottom'
})
......@@ -425,13 +502,24 @@ export default {
}
}
if (this.scrollX) {
if (target.scrollLeft <= this.upperThresholdNumber && this.lastScrollLeft - target.scrollLeft > 0 && $event.timeStamp - this.lastScrollToUpperTime > 200) {
if (
target.scrollLeft <= this.upperThresholdNumber &&
this.lastScrollLeft - target.scrollLeft > 0 &&
$event.timeStamp - this.lastScrollToUpperTime > 200
) {
this.$trigger('scrolltoupper', $event, {
direction: 'left'
})
this.lastScrollToUpperTime = $event.timeStamp
}
if (target.scrollLeft + target.offsetWidth + this.lowerThresholdNumber >= target.scrollWidth && this.lastScrollLeft - target.scrollLeft < 0 && $event.timeStamp - this.lastScrollToLowerTime > 200) {
if (
target.scrollLeft +
target.offsetWidth +
this.lowerThresholdNumber >=
target.scrollWidth &&
this.lastScrollLeft - target.scrollLeft < 0 &&
$event.timeStamp - this.lastScrollToLowerTime > 200
) {
this.$trigger('scrolltolower', $event, {
direction: 'right'
})
......@@ -514,18 +602,38 @@ export default {
main.style.overflowY = this.scrollY ? 'auto' : 'hidden'
main.scrollTop = val
}
this.$refs.content.removeEventListener('transitionend', this.__transitionEnd)
this.$refs.content.removeEventListener('webkitTransitionEnd', this.__transitionEnd)
this.$refs.content.removeEventListener(
'transitionend',
this.__transitionEnd
)
this.$refs.content.removeEventListener(
'webkitTransitionEnd',
this.__transitionEnd
)
},
_setRefreshState (state) {
switch (state) {
case 'refreshing':
this.refresherHeight = this.refresherThreshold
this.$trigger('refresherrefresh', event, {})
// 之前是刷新状态则不再触发刷新
if (!this.beforeRefreshing) {
this.beforeRefreshing = true
this.$trigger('refresherrefresh', {}, {})
// this.$emit('update:refresherTriggered', true)
}
break
case 'restore':
this.refresherHeight = 0
this.$trigger('refresherrestore', {}, {})
case 'refresherabort':
this.beforeRefreshing = false
this.refresherHeight = this.toUpperNumber = 0
if (state === 'restore') {
this.triggerAbort = false
this.$trigger('refresherrestore', {}, {})
}
if (state === 'refresherabort' && this.triggerAbort) {
this.triggerAbort = false
this.$trigger('refresherabort', {}, {})
}
break
}
this.refreshState = state
......
......@@ -62,7 +62,7 @@ const createUploadTaskById = function (uploadTaskId, {
}
if (files && files.length) {
files.forEach(file => {
uploader.addFile(getRealPath(file.uri), {
uploader.addFile(getRealPath(file.uri || file.filePath), {
key: file.name || 'file'
})
})
......
......@@ -16,6 +16,11 @@ import {
initMocks
} from 'uni-wrapper/util'
import {
fixSetDataStart,
fixSetDataEnd
} from '../../../mp-weixin/runtime/wrapper/fix-set-data'
import parseBaseComponent from '../../../mp-weixin/runtime/wrapper/component-base-parser'
const newLifecycle = swan.canIUse('lifecycle-2-0')
......@@ -39,22 +44,7 @@ export default function parseComponent (vueOptions) {
}
// 处理百度小程序 onInit 生命周期调用 setData 无效的问题
const setData = this.setData
const setDataArgs = []
this.setData = function () {
setDataArgs.push(arguments)
}
this.__fixInitData = function () {
delete this.__fixInitData
this.setData = setData
if (setDataArgs.length) {
this.groupSetData(() => {
setDataArgs.forEach(args => {
setData.apply(this, args)
})
})
}
}
fixSetDataStart(this)
oldAttached.call(this)
this.pageinstance.$vm = this.$vm
this.$vm.__call_hook('onInit', query)
......@@ -64,7 +54,7 @@ export default function parseComponent (vueOptions) {
oldAttached.call(this)
} else {
initMocks(this.$vm, mocks)
this.__fixInitData && this.__fixInitData()
fixSetDataEnd(this)
}
if (isPage.call(this)) { // 百度 onLoad 在 attached 之前触发(基础库小于 3.70)
// 百度 当组件作为页面时 pageinstancce 不是原来组件的 instance
......
import parseBaseComponent from '../../../mp-weixin/runtime/wrapper/component-parser'
import { isPage } from '../../../mp-weixin/runtime/wrapper/util'
import {
fixSetDataStart,
fixSetDataEnd
} from '../../../mp-weixin/runtime/wrapper/fix-set-data'
const SDKVersion = __GLOBAL__.getSystemInfoSync().SDKVersion
export default function parseComponent (vueComponentOptions) {
return parseBaseComponent(vueComponentOptions)
const componentOptions = parseBaseComponent(vueComponentOptions)
const oldAttached = componentOptions.lifetimes.attached
componentOptions.lifetimes.attached = function attached () {
if (isPage.call(this) && parseFloat(SDKVersion) <= 1.13) {
// 解决快手小程序页面 attached 生命周期 setData 导致数据同步异常的问题
fixSetDataStart(this)
setTimeout(() => {
fixSetDataEnd(this)
}, 0)
}
oldAttached.call(this)
}
return componentOptions
}
/**
* 用于延迟调用 setData
* 在 setData 真实调用的时机需执行 fixSetDataEnd
* @param {*} mpInstance
*/
export function fixSetDataStart (mpInstance) {
const setData = mpInstance.setData
const setDataArgs = []
mpInstance.setData = function () {
setDataArgs.push(arguments)
}
mpInstance.__fixInitData = function () {
this.setData = setData
const fn = () => {
setDataArgs.forEach(args => {
setData.apply(this, args)
})
}
if (setDataArgs.length) {
if (this.groupSetData) {
this.groupSetData(fn)
} else {
fn()
}
}
}
}
/**
* 恢复真实的 setData 方法
* @param {*} mpInstance
*/
export function fixSetDataEnd (mpInstance) {
if (mpInstance.__fixInitData) {
mpInstance.__fixInitData()
delete mpInstance.__fixInitData
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册