提交 ee27f031 编写于 作者: fxy060608's avatar fxy060608

Merge branch 'dev' of https://github.com/dcloudio/uni-app into alpha

# Conflicts:
#	packages/uni-h5/dist/index.umd.min.js
......@@ -4,3 +4,4 @@ unpackage/
.vscode/
.idea
.DS_Store
!packages/uni-app-plus/dist
......@@ -5,7 +5,7 @@ const request = require('request')
const registry = 'https://registry.npmjs.org/@dcloudio/'
const pkgs = fs.readdirSync(path.resolve(__dirname, 'packages'))
const pkgs = fs.readdirSync(path.resolve(__dirname, 'packages')).filter(pkg => pkg.indexOf('.') !== 0)
const tag = process.argv[2] || 'alpha'
......
......@@ -2,7 +2,7 @@ const fs = require('fs')
const path = require('path')
const shellExec = require('shell-exec')
const pkgs = fs.readdirSync(path.resolve(__dirname, 'packages'))
const pkgs = fs.readdirSync(path.resolve(__dirname, 'packages')).filter(pkg => pkg.indexOf('.') !== 0)
const version = process.argv[2]
if (!version) {
......
......@@ -205,7 +205,7 @@ H5平台是SPA单页应用,普通的SEO信息即加meta字段只能在,自
|loading|String|AsyncLoading|页面 js 加载时使用的组件(需注册为全局组件)|
|error|String|AsyncError|页面 js 加载失败时使用的组件(需注册为全局组件)|
|delay|Number|200|展示 loading 加载组件的延时时间(页面 js 若在 delay 时间内加载完成,则不会显示 loading 组件)|
|timeout|Number|3000|页面 js 加载超时时间(超时后展示 error 对应的组件)|
|timeout|Number|60000|页面 js 加载超时时间(超时后展示 error 对应的组件)|
#### devServer
|属性|类型|默认值|说明|
......@@ -309,46 +309,46 @@ Tips:关于摇树优化(treeShaking)原理及优化结果,参考:[http
|urlCheck|Boolean|是否检查安全域名和 TLS 版本|
|es6|Boolean|ES6 转 ES5|
|postcss|Boolean|上传代码时样式是否自动补全|
|minified|Boolean|上传代码时是否自动压缩|
#### optimization
对微信小程序的优化配置
|minified|Boolean|上传代码时是否自动压缩|
#### optimization
对微信小程序的优化配置
|属性|类型|说明|
|:-|:-|:-|
|subPackages|Boolean|是否开启分包优化|
#### cloudfunctionRoot
如果需要使用微信小程序的云开发,需要在 mp-weixin 配置云开发目录
```javascript
"mp-weixin":{
// ...
"cloudfunctionRoot": "cloudfunctions/", // 配置云开发目录
// ...
}
```
配置目录之后,需要在项目根目录新建 `vue.config.js` 配置对应的文件编译规则
```javascript
{
plugins: [
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../cloudfunctions'),
to: path.join(__dirname, 'unpackage', 'dist', process.env.NODE_ENV === 'production' ? 'build' : 'dev', process.env.UNI_PLATFORM, 'cloudfunctions'),
},
]),
],
}
```
|subPackages|Boolean|是否开启分包优化|
#### cloudfunctionRoot
如果需要使用微信小程序的云开发,需要在 mp-weixin 配置云开发目录
```javascript
"mp-weixin":{
// ...
"cloudfunctionRoot": "cloudfunctions/", // 配置云开发目录
// ...
}
```
配置目录之后,需要在项目根目录新建 `vue.config.js` 配置对应的文件编译规则
```javascript
{
plugins: [
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../cloudfunctions'),
to: path.join(__dirname, 'unpackage', 'dist', process.env.NODE_ENV === 'production' ? 'build' : 'dev', process.env.UNI_PLATFORM, 'cloudfunctions'),
},
]),
],
}
```
### mp-alipay
|属性|类型|说明|
......@@ -364,14 +364,14 @@ Tips:关于摇树优化(treeShaking)原理及优化结果,参考:[http
|requiredBackgroundModes|Array|小程序需要在后台使用的能力,目前支持背景音频播放,"requiredBackgroundModes": ["audio"],[详见](https://smartprogram.baidu.com/docs/develop/tutorial/process/#requiredBackgroundModes) |
|prefetches|Array|预请求的所有url的列表,[详见](https://smartprogram.baidu.com/docs/develop/tutorial/process/#prefetches) |
|optimization|Object| 对百度小程序的优化配置 |
#### optimization
对百度小程序的优化配置
#### optimization
对百度小程序的优化配置
|属性|类型|说明|
|:-|:-|:-|
|subPackages|Boolean|是否开启分包优化|
|subPackages|Boolean|是否开启分包优化|
### mp-toutiao
......@@ -402,25 +402,25 @@ Tips:关于摇树优化(treeShaking)原理及优化结果,参考:[http
|workers |String |Worker 代码放置的目录。 [详见](https://q.qq.com/wiki/develop/miniprogram/frame/dispose.html#workers) |
|groupIdList |String Array |需要打开群资料卡的群号列表,详见button的open-type |
|optimization|Object| 对QQ小程序的优化配置 |
#### optimization
对QQ小程序的优化配置
#### optimization
对QQ小程序的优化配置
|属性|类型|说明|
|:-|:-|:-|
|subPackages|Boolean|是否开启分包优化|
mp-qq只支持自定义组件模式,不存在usingComponents配置
### 关于分包优化的说明
- 在对应平台的配置下添加`"optimization":{"subPackages":true}`开启分包优化
- 目前只支持`mp-weixin``mp-qq``mp-baidu`的分包优化
- 分包优化具体逻辑:
+ 静态文件:分包下支持 static 等静态资源拷贝
+ js文件:当某个 js 仅被一个分包引用时,该 js 会被打包到该分包内,否则仍打到主包(即被主包引用,或被超过 1 个分包引用)
+ 自定义组件:若某个自定义组件仅被一个分包引用时,且未放入到分包内,编译时会输出提示信息
### 关于分包优化的说明
- 在对应平台的配置下添加`"optimization":{"subPackages":true}`开启分包优化
- 目前只支持`mp-weixin``mp-qq``mp-baidu`的分包优化
- 分包优化具体逻辑:
+ 静态文件:分包下支持 static 等静态资源拷贝
+ js文件:当某个 js 仅被一个分包引用时,该 js 会被打包到该分包内,否则仍打到主包(即被主包引用,或被超过 1 个分包引用)
+ 自定义组件:若某个自定义组件仅被一个分包引用时,且未放入到分包内,编译时会输出提示信息
### 完整 manifest.json
......
......@@ -59,7 +59,8 @@ const media = [
'saveVideoToPhotosAlbum',
'createVideoContext',
'createCameraContext',
'createLivePlayerContext'
'createLivePlayerContext',
'createLivePusherContext'
]
const device = [
......@@ -201,6 +202,10 @@ const third = [
'setPageMeta'
]
const ad = [
'createRewardedVideoAd'
]
const apis = [
...base,
...network,
......@@ -214,7 +219,8 @@ const apis = [
...event,
...file,
...canvas,
...third
...third,
...ad
]
module.exports = apis
......@@ -66,7 +66,8 @@
"uni.saveVideoToPhotosAlbum": true,
"uni.createVideoContext": true,
"uni.createCameraContext": true,
"uni.createLivePlayerContext": true
"uni.createLivePlayerContext": true,
"uni.createLivePusherContext": true
}
}, {
"name": "device",
......@@ -206,4 +207,10 @@
"uni.base64ToArrayBuffer": true,
"uni.arrayBufferToBase64": true
}
}, {
"name": "ad",
"title": "广告",
"apiList": {
"uni.createRewardedVideoAd": true
}
}]
......@@ -71,7 +71,8 @@ var serviceContext = (function () {
'saveVideoToPhotosAlbum',
'createVideoContext',
'createCameraContext',
'createLivePlayerContext'
'createLivePlayerContext',
'createLivePusherContext'
];
const device = [
......@@ -213,6 +214,10 @@ var serviceContext = (function () {
'setPageMeta'
];
const ad = [
'createRewardedVideoAd'
];
const apis = [
...base,
...network,
......@@ -226,7 +231,8 @@ var serviceContext = (function () {
...event,
...file,
...canvas,
...third
...third,
...ad
];
var apis_1 = apis;
......@@ -292,10 +298,6 @@ var serviceContext = (function () {
return ('' + str).replace(/[^\x00-\xff]/g, '**').length
}
function guid () {
return Math.floor(4294967296 * (1 + Math.random())).toString(16).slice(1)
}
function debounce (fn, delay) {
let timeout;
return function () {
......@@ -1423,21 +1425,26 @@ var serviceContext = (function () {
closeSocket: closeSocket
});
// App端可以只使用files不传filePath和name
const uploadFile = {
url: {
type: String,
required: true
},
files: {
type: Array
},
filePath: {
type: String,
required: true,
validator (value, params) {
params.type = getRealPath(value);
if (value) {
params.type = getRealPath(value);
}
}
},
name: {
type: String,
required: true
type: String
},
header: {
type: Object,
......@@ -2327,8 +2334,13 @@ var serviceContext = (function () {
res.errMsg = apiName + ':ok';
} else if (res.errMsg.indexOf(':cancel') !== -1) {
res.errMsg = apiName + ':cancel';
} else if (res.errMsg.indexOf(':fail') !== -1) {
res.errMsg = apiName + ':fail' + res.errMsg.substr(res.errMsg.indexOf(' '));
} else if (res.errMsg.indexOf(':fail') !== -1) {
let errDetail = '';
let spaceIndex = res.errMsg.indexOf(' ');
if (spaceIndex > -1) {
errDetail = res.errMsg.substr(spaceIndex);
}
res.errMsg = apiName + ':fail' + errDetail;
}
const errMsg = res.errMsg;
......@@ -3531,6 +3543,84 @@ var serviceContext = (function () {
: operateVideoPlayer(videoId, pageVm, type, data);
}
class LivePusherContext {
constructor (id, ctx) {
this.id = id;
this.ctx = ctx;
}
start (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'start', cbs)
}
stop (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'stop', cbs)
}
pause (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'pause', cbs)
}
resume (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'resume', cbs)
}
switchCamera (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'switchCamera', cbs)
}
snapshot (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'snapshot', cbs)
}
toggleTorch (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'toggleTorch', cbs)
}
playBGM (args) {
return invokeVmMethod(this.ctx, 'playBGM', args)
}
stopBGM (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'stopBGM', cbs)
}
pauseBGM (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'pauseBGM', cbs)
}
resumeBGM (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'resumeBGM', cbs)
}
setBGMVolume (cbs) {
return invokeVmMethod(this.ctx, 'setBGMVolume', cbs)
}
startPreview (cbs) {
return invokeVmMethodWithoutArgs(this.ctx, 'startPreview', cbs)
}
stopPreview (args) {
return invokeVmMethodWithoutArgs(this.ctx, 'stopPreview', args)
}
}
function createLivePusherContext (id, vm) {
if (!vm) {
return console.warn('uni.createLivePusherContext 必须传入第二个参数,即当前 vm 对象(this)')
}
const elm = findElmById(id, vm);
if (!elm) {
return console.warn('Can not find `' + id + '`')
}
return new LivePusherContext(id, elm)
}
function createLivePusherContext$1 (id, vm) {
return createLivePusherContext(id, vm)
}
let watchAccelerationId = false;
let isWatchAcceleration = false;
......@@ -4872,7 +4962,9 @@ var serviceContext = (function () {
tabBar && tabBar.showTabBar({
animation
});
}
}
let maskClickCallback = [];
var tabBar$1 = {
id: '0',
......@@ -4884,7 +4976,12 @@ var serviceContext = (function () {
tabBar = requireNativePlugin('uni-tabview');
} catch (error) {
console.log(`uni.requireNativePlugin("uni-tabview") error ${error}`);
}
}
tabBar.onMaskClick(() => {
maskClickCallback.forEach((callback) => {
callback();
});
});
tabBar && tabBar.onClick(({ index }) => {
clickCallback(config.list[index], index);
});
......@@ -4950,8 +5047,12 @@ var serviceContext = (function () {
color: mask
});
},
addEventListener (name, callback) {
tabBar.onMaskClick(callback);
addEventListener (name, callback) {
maskClickCallback.push(callback);
},
removeEventListener (name, callback) {
let callbackIndex = maskClickCallback.indexOf(callback);
maskClickCallback.splice(callbackIndex, 1);
}
};
......@@ -6232,7 +6333,7 @@ var serviceContext = (function () {
statusCode: 0,
errMsg: 'timeout'
});
}, timeout);
}, (timeout + 200));// TODO +200 发消息到原生层有时间开销,以后考虑由原生层回调超时
}
const options = {
method,
......@@ -6483,7 +6584,7 @@ var serviceContext = (function () {
}
for (const name in formData) {
if (formData.hasOwnProperty(name)) {
uploader.addData(name, formData[name]);
uploader.addData(name, String(formData[name]));
}
}
if (files && files.length) {
......@@ -7429,6 +7530,10 @@ var serviceContext = (function () {
delete webviewStyle.popGesture;
}
if (routeOptions.meta.isQuit) { // 退出
webviewStyle.popGesture = plus.os.name === 'iOS' ? 'appback' : 'none';
}
// TODO 下拉刷新
if (path && routeOptions.meta.isNVue) {
......@@ -8229,6 +8334,9 @@ var serviceContext = (function () {
// 查找当前 tabBarPage,且设置 visible
getCurrentPages(true).forEach(page => {
if (('/' + page.route) === path) {
if (!page.$page.meta.visible) {
page.$vm.__call_hook('onShow');
}
page.$page.meta.visible = true;
tabBarPage = page;
} else {
......@@ -8239,7 +8347,6 @@ var serviceContext = (function () {
});
if (tabBarPage) {
tabBarPage.$vm.__call_hook('onShow');
tabBarPage.$getAppWebview().show('none');
} else {
return showWebview(registerPage({
......@@ -8791,6 +8898,98 @@ var serviceContext = (function () {
: requestComponentInfo(pageVm, queue, callback);
}
const eventNames = [
'load',
'close',
'error'
];
const ERROR_CODE_LIST = [-5001, -5002, -5003, -5004, -5005, -5006];
class RewardedVideoAd {
constructor (adpid) {
this._options = {
adpid: adpid
};
const _callbacks = this._callbacks = {};
eventNames.forEach(item => {
_callbacks[item] = [];
const name = item[0].toUpperCase() + item.substr(1);
this[`on${name}`] = function (callback) {
_callbacks[item].push(callback);
};
});
this._isLoad = false;
this._adError = '';
this._loadPromiseResolve = null;
this._loadPromiseReject = null;
const rewardAd = this._rewardAd = plus.ad.createRewardedVideoAd(this._options);
rewardAd.onLoad((e) => {
this._isLoad = true;
this._dispatchEvent('load', {});
if (this._loadPromiseResolve != null) {
this._loadPromiseResolve();
this._loadPromiseResolve = null;
}
});
rewardAd.onClose((e) => {
this._loadAd();
this._dispatchEvent('close', { isEnded: e.isEnded });
});
rewardAd.onError((e) => {
const { code, message } = e;
const data = { code: code, errMsg: message };
this._adError = message;
this._dispatchEvent('error', data);
if ((code === -5005 || ERROR_CODE_LIST.index(code) === -1) && this._loadPromiseReject != null) {
this._loadPromiseReject(data);
this._loadPromiseReject = null;
}
});
this._loadAd();
}
load () {
return new Promise((resolve, reject) => {
if (this._isLoad) {
resolve();
return
}
this._loadPromiseResolve = resolve;
this._loadPromiseReject = reject;
this._loadAd();
})
}
show () {
return new Promise((resolve, reject) => {
if (this._isLoad) {
this._rewardAd.show();
resolve();
} else {
reject(new Error(this._adError));
}
})
}
_loadAd () {
this._isLoad = false;
this._rewardAd.load();
}
_dispatchEvent (name, data) {
this._callbacks[name].forEach(callback => {
if (typeof callback === 'function') {
callback(data || {});
}
});
}
}
function createRewardedVideoAd ({
adpid = ''
} = {}) {
return new RewardedVideoAd(adpid)
}
var api = /*#__PURE__*/Object.freeze({
......@@ -8814,6 +9013,7 @@ var serviceContext = (function () {
base64ToTempFilePath: base64ToTempFilePath,
operateMapPlayer: operateMapPlayer$2,
operateVideoPlayer: operateVideoPlayer$2,
createLivePusherContext: createLivePusherContext$1,
enableAccelerometer: enableAccelerometer,
addPhoneContact: addPhoneContact,
openBluetoothAdapter: openBluetoothAdapter,
......@@ -8920,7 +9120,8 @@ var serviceContext = (function () {
setTabBarStyle: setTabBarStyle$2,
hideTabBar: hideTabBar$2,
showTabBar: showTabBar$2,
requestComponentInfo: requestComponentInfo$2
requestComponentInfo: requestComponentInfo$2,
createRewardedVideoAd: createRewardedVideoAd
});
var platformApi = Object.assign(Object.create(null), api, eventApis);
......@@ -8956,7 +9157,7 @@ var serviceContext = (function () {
return page && page.$page.id
}
const eventNames = [
const eventNames$1 = [
'canplay',
'play',
'pause',
......@@ -9021,7 +9222,7 @@ var serviceContext = (function () {
this.id = id;
this._callbacks = {};
this._options = {};
eventNames.forEach(name => {
eventNames$1.forEach(name => {
this._callbacks[name.toLowerCase()] = [];
});
props.forEach(item => {
......@@ -9075,7 +9276,7 @@ var serviceContext = (function () {
}
}
eventNames.forEach(item => {
eventNames$1.forEach(item => {
const name = item[0].toUpperCase() + item.substr(1);
item = item.toLowerCase();
InnerAudioContext.prototype[`on${name}`] = function (callback) {
......@@ -9140,7 +9341,7 @@ var serviceContext = (function () {
createInnerAudioContext: createInnerAudioContext
});
const eventNames$1 = [
const eventNames$2 = [
'canplay',
'play',
'pause',
......@@ -9153,7 +9354,7 @@ var serviceContext = (function () {
'waiting'
];
const callbacks$4 = {};
eventNames$1.forEach(name => {
eventNames$2.forEach(name => {
callbacks$4[name] = [];
});
......@@ -9256,7 +9457,7 @@ var serviceContext = (function () {
this._operate('stop');
}
seek (position) {
this._operate('play', {
this._operate('seek', {
currentTime: position
});
}
......@@ -9267,7 +9468,7 @@ var serviceContext = (function () {
}
}
eventNames$1.forEach(item => {
eventNames$2.forEach(item => {
const name = item[0].toUpperCase() + item.substr(1);
BackgroundAudioManager.prototype[`on${name}`] = function (callback) {
callbacks$4[item].push(callback);
......@@ -10996,6 +11197,23 @@ var serviceContext = (function () {
const STORAGE_DATA_TYPE = '__TYPE';
const STORAGE_KEYS = 'uni-storage-keys';
function parseValue (value) {
const types = ['object', 'string', 'number', 'boolean', 'undefined'];
try {
const object = typeof value === 'string' ? JSON.parse(value) : value;
const type = object.type;
if (types.indexOf(type) >= 0) {
const keys = Object.keys(object);
// eslint-disable-next-line valid-typeof
if (keys.length === 2 && 'data' in object && typeof object.data === type) {
return object.data
} else if (keys.length === 1) {
return ''
}
}
} catch (error) { }
}
function setStorage$1 ({
key,
data
......@@ -11006,6 +11224,11 @@ var serviceContext = (function () {
data: data
});
try {
if (type === 'string' && parseValue(value) !== undefined) {
localStorage.setItem(key + STORAGE_DATA_TYPE, type);
} else {
localStorage.removeItem(key + STORAGE_DATA_TYPE);
}
localStorage.setItem(key, value);
} catch (error) {
return {
......@@ -11035,22 +11258,26 @@ var serviceContext = (function () {
}
}
let data = value;
try {
const object = JSON.parse(value);
// 兼容App端历史格式
const type = localStorage.getItem(key + STORAGE_DATA_TYPE);
if (!type) {
const keys = Object.keys(object);
if (keys.length === 2 && 'type' in object && 'data' in object) {
data = object.data;
} else if (keys.length === 1 && 'type' in object) {
data = '';
const typeOrigin = localStorage.getItem(key + STORAGE_DATA_TYPE) || '';
const type = typeOrigin.toLowerCase();
if (type !== 'string' || (typeOrigin === 'String' && value === '{"type":"undefined"}')) {
try {
// 兼容H5和V3初期历史格式
let object = JSON.parse(value);
const result = parseValue(object);
if (result !== undefined) {
data = result;
} else if (type) {
// 兼容App端历史格式
data = object;
if (typeof object === 'string') {
object = JSON.parse(object);
// eslint-disable-next-line valid-typeof
data = typeof object === (type === 'null' ? 'object' : type) ? object : data;
}
}
} else if (type !== 'String') {
data = object;
data = typeof data === 'string' ? JSON.parse(data) : data;
}
} catch (error) { }
} catch (error) { }
}
return {
data,
errMsg: 'getStorage:ok'
......@@ -12074,10 +12301,6 @@ var serviceContext = (function () {
__uniConfig.tabBar.selected = 0;
const selected = __uniConfig.tabBar.list.findIndex(page => page.pagePath === __uniConfig.entryPagePath);
if (selected !== -1) {
// 取当前 tab 索引值
__uniConfig.tabBar.selected = selected;
}
tabBar$1.init(__uniConfig.tabBar, (item, index) => {
uni.switchTab({
......@@ -12093,22 +12316,36 @@ var serviceContext = (function () {
}
});
});
}
function initEntryPage () {
const argsJsonStr = plus.runtime.arguments;
if (!argsJsonStr) {
return
if (selected !== -1) {
// 取当前 tab 索引值
__uniConfig.tabBar.selected = selected;
selected !== 0 && tabBar$1.switchTab(__uniConfig.entryPagePath);
}
}
function initEntryPage () {
let entryPagePath;
let entryPageQuery;
try {
const args = JSON.parse(argsJsonStr);
entryPagePath = args.path || args.pathName;
entryPageQuery = (args.query ? ('?' + args.query) : '');
} catch (e) {}
const weexPlus = weex.requireModule('plus');
if (weexPlus.getRedirectInfo) {
const info = weexPlus.getRedirectInfo() || {};
entryPagePath = info.path;
entryPageQuery = info.query ? ('?' + info.query) : '';
} else {
const argsJsonStr = plus.runtime.arguments;
if (!argsJsonStr) {
return
}
try {
const args = JSON.parse(argsJsonStr);
entryPagePath = args.path || args.pathName;
entryPageQuery = args.query ? ('?' + args.query) : '';
} catch (e) {}
}
if (!entryPagePath || entryPagePath === __uniConfig.entryPagePath) {
return
}
......@@ -12135,8 +12372,8 @@ var serviceContext = (function () {
if (process.env.NODE_ENV !== 'production') {
console.log(`[uni-app] registerApp`);
}
appCtx = appVm;
appCtx = appVm;
appCtx.$vm = appVm;
Object.assign(appCtx, defaultApp); // 拷贝默认实现
......@@ -12230,7 +12467,7 @@ var serviceContext = (function () {
function initVue (Vue) {
Vue.config.errorHandler = function (err) {
const app = getApp();
const app = typeof getApp === 'function' && getApp();
if (app && hasLifecycleHook(app.$options, 'onError')) {
app.__call_hook('onError', err);
} else {
......@@ -12750,7 +12987,11 @@ var serviceContext = (function () {
this._$vdomSync = new VDomSync(this.$options.pageId, this.$options.pagePath, this);
}
if (this._$vd) {
this._$id = guid();
if (!this.$parent) {
this._$id = '-1';
} else {
this._$id = this.$parent._$id + ',' + this.$vnode.data.attrs._i;
}
this._$vd.addVm(this);
this._$vdMountedData = Object.create(null);
this._$setData(MOUNTED_DATA, this._$vdMountedData);
......@@ -13076,16 +13317,18 @@ var serviceContext = (function () {
Vue.prototype.$nextTick = function nextTick (cb) {
const renderWatcher = this._watcher;
const callback = typeof cb === 'function';
if (
renderWatcher &&
this._$queue.find(watcher => renderWatcher === watcher)
) {
vdSyncCallbacks.push(cb.bind(this));
const result = new Promise((resolve) => {
vdSyncCallbacks.push(callback ? cb.bind(this) : resolve);
});
return callback ? result : undefined
} else {
// $nextTick bind vm context
Vue.nextTick(() => {
cb.call(this);
});
return Vue.nextTick(callback ? () => cb.call(this) : undefined)
}
};
}
......
此差异已折叠。
此差异已折叠。
......@@ -14,14 +14,14 @@ const defaultAsync = {
loading: 'AsyncLoading',
error: 'AsyncError',
delay: 200,
timeout: 3000
timeout: 60000
}
const networkTimeout = {
request: 6000,
connectSocket: 6000,
uploadFile: 6000,
downloadFile: 6000
request: 60000,
connectSocket: 60000,
uploadFile: 60000,
downloadFile: 60000
}
function getManifestJson () {
......@@ -77,11 +77,11 @@ function getH5Options (manifestJson) {
}
} else { // 其他模式,启用 base
h5.publicPath = base
}
if (process.env.UNI_SUB_PLATFORM === 'mp-360') {
h5.router.base = '/'
h5.publicPath = '/'
}
if (process.env.UNI_SUB_PLATFORM === 'mp-360') {
h5.router.base = '/'
h5.publicPath = '/'
}
/* eslint-disable no-mixed-operators */
......@@ -98,4 +98,4 @@ module.exports = {
parseManifestJson,
getNetworkTimeout,
getH5Options
}
}
......@@ -579,7 +579,8 @@ module.exports = {
mergeLonghand: false,
mergeRules: false,
cssDeclarationSorter: false,
uniqueSelectors: false, // 标签排序影响头条小程序
uniqueSelectors: false, // 标签排序影响头条小程序
minifySelectors: false, // 标签排序影响头条小程序
discardComments: false,
discardDuplicates: false // 条件编译会导致重复
}
......
......@@ -136,6 +136,12 @@ describe('codegen', () => {
'<view data-a="1" :data-b="b"></view>',
`with(this){return _c('view',{attrs:{"data-b":_$s(0,'a-data-b',b),"_i":0}})}`
)
})
it('generate v-if directive', () => {
assertCodegen(
'<text v-if="a">1</text><text v-else-if="b">2</text><text v-else-if="c">3</text><text v-else>d</text>',
`with(this){return (_$s(0,'i',a))?_c('text'):(_$s(1,'e',b))?_c('text'):(_$s(2,'e',c))?_c('text'):_c('text')}`
)
})
})
/* eslint-enable quotes */
......@@ -75,6 +75,12 @@ describe('codegen', () => {
'<view data-a="1" :data-b="b"></view>',
`with(this){return _c('v-uni-view',{attrs:{"data-a":"1","data-b":_$g(0,'a-data-b'),"_i":0}})}`
)
})
it('generate v-if directive', () => {
assertCodegen(
'<text v-if="a">1</text><text v-else-if="b">2</text><text v-else-if="c">3</text><text v-else>d</text>',
`with(this){return (_$g(0,'i'))?_c('v-uni-text',{attrs:{"_i":0}},[_v("1")]):(_$g(1,'e'))?_c('v-uni-text',{attrs:{"_i":1}},[_v("2")]):(_$g(2,'e'))?_c('v-uni-text',{attrs:{"_i":2}},[_v("3")]):_c('v-uni-text',{attrs:{"_i":3}},[_v("d")])}`
)
})
})
/* eslint-enable quotes */
......@@ -141,14 +141,14 @@ describe('codegen', () => {
it('generate v-model directive', () => {
assertCodegen(
'<input v-model="test">',
`with(this){return _c('v-uni-input',{attrs:{"_i":0},model:{value:_$g(0,'v-model'),callback:function(){},expression:"test"}})}`
`with(this){return _c('v-uni-input',{attrs:{"_i":0},model:{value:_$g(0,'v-model'),callback:function($$v){$handleVModelEvent(0,$$v)},expression:"test"}})}`
)
})
it('generate multiline v-model directive', () => {
assertCodegen(
'<input v-model="\n test \n">',
`with(this){return _c('v-uni-input',{attrs:{"_i":0},model:{value:_$g(0,'v-model'),callback:function(){},expression:"\\n test \\n"}})}`
`with(this){return _c('v-uni-input',{attrs:{"_i":0},model:{value:_$g(0,'v-model'),callback:function($$v){$handleVModelEvent(0,$$v)},expression:"\\n test \\n"}})}`
)
})
......
......@@ -18,13 +18,8 @@ const scopedPath = path.resolve(__dirname, '../../')
const compiler = require('../lib')
const res = compiler.compile(
`
<view class="h-page">
<slot></slot>
<h-dialog></h-dialog>
<h-navbar></h-navbar>
<h-toast></h-toast>
</view>
`
<text v-if="a">1</text><text v-else-if="b">2</text><text v-else-if="c">3</text><text v-else>d</text>
`, {
miniprogram: true,
resourcePath: '/User/fxy/Documents/test.wxml',
......@@ -37,9 +32,9 @@ const res = compiler.compile(
mp: {
platform: 'mp-weixin'
},
filterModules: ['swipe']
filterModules: ['swipe'],
// service: true,
// view: true
view: true
})
console.log(require('util').inspect(res, {
......
......@@ -18,6 +18,10 @@ function parseIs (el, genVar) {
}
}
function isProcessed (exp) {
return String(exp).indexOf('_$') === 0
}
// 当根节点是由if,elseif,else组成,会调用多次parseIf来解析root
function parseIf (el, createGenVar, isScopedSlot) {
if (!el.if) {
return
......@@ -26,11 +30,13 @@ function parseIf (el, createGenVar, isScopedSlot) {
isScopedSlot = false
}
el.ifConditions.forEach(con => {
if (isVar(con.exp)) {
if (!isProcessed(con.exp) && isVar(con.exp)) {
con.exp = createGenVar(con.block.attrsMap[ID], isScopedSlot)(con.block.elseif ? V_ELSE_IF : V_IF, con.exp)
}
})
el.if = createGenVar(el.attrsMap[ID], isScopedSlot)(V_IF, el.if)
if (!isProcessed(el.if)) {
el.if = createGenVar(el.attrsMap[ID], isScopedSlot)(V_IF, el.if)
}
}
function parseFor (el, createGenVar, isScopedSlot, fill = false) {
......
......@@ -156,6 +156,11 @@ function transformNode (el, parent, state, isScopedSlot) {
function postTransformNode (el, options) {
if (!el.parent) { // 从根节点开始递归处理
if (options.root) { // 当根节点是由if,elseif,else组成
parseIf(options.root, createGenVar)
} else {
options.root = el
}
traverseNode(el, false, {
forIteratorId: 0,
transformNode,
......
......@@ -104,8 +104,8 @@ function updateForIterator (el, state) {
function updateForEleId (el, state) {
updateForIterator(el, state)
if (el.for) {
const it = el.$parentIterator3 ? (el.$parentIterator3 + '+' + el.iterator3) : el.iterator3
if (el.for) {
const it = el.$parentIterator3 ? (el.$parentIterator3 + '+' + "'-'" + '+' + el.iterator3) : el.iterator3
updateEleId(el, it, state)
}
}
......
......@@ -155,6 +155,11 @@ function transformNode (el, parent, state, isScopedSlot) {
function postTransformNode (el, options) {
if (!el.parent) { // 从根节点开始递归处理
if (options.root) { // 当根节点是由if,elseif,else组成
parseIf(options.root, createGenVar)
} else {
options.root = el
}
traverseNode(el, false, {
forIteratorId: 0,
transformNode,
......@@ -209,7 +214,7 @@ function handleViewEvents (events) {
function genVModel (el, isScopedSlot) {
if (el.model) {
el.model.value = createGenVar(el.attrsMap[ID], isScopedSlot)('v-model', el.model.value)
if (el.tag === 'v-uni-input' || el.tag === 'v-uni-textarea' && !(el.events && el.events.input)) {
if ((el.tag === 'v-uni-input' || el.tag === 'v-uni-textarea') && !(el.events && el.events.input)) {
el.model.callback = `function($$v){$handleVModelEvent(${el.attrsMap[ID]},$$v)}`
} else {
el.model.callback = `function(){}`
......@@ -235,4 +240,4 @@ module.exports = {
},
postTransformNode,
genData
}
}
const path = require('path')
const {
hyphenate,
isComponent
} = require('./util')
......@@ -44,7 +45,8 @@ function updateMPUsingAutoImportComponents (autoComponents, options) {
name,
source
}) => {
usingAutoImportComponents[name] = '/' + formatSource(source)
// 自定义组件统一格式化为 kebab-case
usingAutoImportComponents[hyphenate(name)] = '/' + formatSource(source)
})
updateUsingAutoImportComponents(resourcePath, usingAutoImportComponents) // 更新json
}
......
......@@ -59,7 +59,7 @@ function addVueId (path, state) {
state.options.$vueId = 1
}
const hashId = state.options.hashId
const vueId = String((hashId ? (hashId + '-') : '') + (state.options.$vueId++))
const vueId = (hashId ? (hashId + '-') : '') + (state.options.$vueId++)
let value
......
......@@ -101,7 +101,7 @@ module.exports = (api, options) => {
// inject dev & hot-reload middleware entries
if (!isProduction) {
const sockjsUrl = publicUrl
// explicitly configured via devServer.public
// explicitly configured via devServer.public
? `?${publicUrl}/sockjs-node`
: isInContainer
// can't infer public netowrk url if inside a container...
......@@ -163,7 +163,7 @@ module.exports = (api, options) => {
// this works with vue-devtools & @vue/cli-overlay
app.use('/__open-in-editor', launchEditorMiddleware(() => console.log(
`To specify an editor, sepcify the EDITOR env variable or ` +
`add "editor" field to your Vue project config.\n`
`add "editor" field to your Vue project config.\n`
)))
// allow other plugins to register middlewares, e.g. PWA
api.service.devServerConfigFns.forEach(fn => fn(app, server))
......@@ -173,7 +173,7 @@ module.exports = (api, options) => {
}
}))
;
;
['SIGINT', 'SIGTERM'].forEach(signal => {
process.on(signal, () => {
server.close(() => {
......@@ -249,9 +249,11 @@ module.exports = (api, options) => {
if (isFirstCompile) {
isFirstCompile = false
if (!isProduction) {
if (process.UNI_CLOUD_ALIYUN) {
console.warn(`当前项目使用了阿里云服务空间,暂不支持发行到H5平台`)
if (!isProduction) {
if (process.UNI_CLOUD) {
console.warn(
`当前项目使用了uniCloud,为避免云函数调用跨域问题,建议在HBuilderX内置浏览器里调试,如使用外部浏览器需处理跨域,详见:https://uniapp.dcloud.io/uniCloud/quickstart?id=useinh5`
)
}
// const buildCommand = hasProjectYarn(api.getCwd()) ? `yarn build` : `npm run build`
// console.log(` Note that the development build is not optimized.`)
......@@ -292,6 +294,10 @@ module.exports = (api, options) => {
}
})
if (server.showStatus) {
server.showStatus = function () {}
}
server.listen(port, host, err => {
if (err) {
reject(err)
......
......@@ -73,12 +73,12 @@ const v3 = {
webpackConfig.optimization.runtimeChunk = {
name: 'app-config'
}
webpackConfig.optimization.splitChunks = require('../split-chunks')()
} else if (isAppView) {
webpackConfig.optimization.runtimeChunk = false
webpackConfig.optimization.splitChunks = false
}
webpackConfig.optimization.splitChunks = false
let devtool = false
if (isAppService && process.env.NODE_ENV !== 'production') {
......
......@@ -159,7 +159,7 @@ module.exports = function configureWebpack (platformOptions, manifestPlatformOpt
}
// js preprocess
updateJsLoader(rawRules, 'foo.js', /^babel-loader/, {
updateJsLoader(rawRules, 'foo.js', /^(.*[/\\])?babel-loader/, {
loader: resolve('packages/webpack-preprocess-loader'),
options: jsPreprocessOptions
})
......@@ -264,4 +264,4 @@ module.exports = function configureWebpack (platformOptions, manifestPlatformOpt
}
}, platformWebpackConfig)
}
}
}
......@@ -3,44 +3,53 @@ const path = require('path')
const mkdirp = require('mkdirp')
const loaderUtils = require('loader-utils')
process.UNI_CLOUD = false
process.UNI_CLOUD_TCB = false
process.UNI_CLOUD_ALIYUN = false
process.env.UNI_CLOUD_PROVIDER = JSON.stringify({})
process.env.UNI_CLOUD_PROVIDER = JSON.stringify([])
if (process.env.UNI_CLOUD_SPACES) {
try {
const spaces = JSON.parse(process.env.UNI_CLOUD_SPACES)
if (Array.isArray(spaces)) {
process.UNI_CLOUD = spaces.length > 0
process.UNI_CLOUD_TCB = !!spaces.find(space => !space.clientSecret)
process.UNI_CLOUD_ALIYUN = !!spaces.find(space => space.clientSecret)
if (spaces.length === 1) {
const space = spaces[0]
if (space.clientSecret) {
process.env.UNI_CLOUD_PROVIDER = JSON.stringify({
provider: 'aliyun',
spaceName: space.name,
spaceId: space.id,
clientSecret: space.clientSecret,
endpoint: space.apiEndpoint
})
} else {
process.env.UNI_CLOUD_PROVIDER = JSON.stringify({
provider: 'tencent',
spaceName: space.name,
spaceId: space.id
})
}
}
console.log(`本项目的uniCloud使用的默认服务空间spaceId为:${space.id}`)
}
process.env.UNI_CLOUD_PROVIDER = JSON.stringify(spaces.map(space => {
if (space.clientSecret) {
return {
provider: 'aliyun',
spaceName: space.name,
spaceId: space.id,
clientSecret: space.clientSecret,
endpoint: space.apiEndpoint
}
} else {
return {
provider: 'tencent',
spaceName: space.name,
spaceId: space.id
}
}
}))
}
} catch (e) {}
}
if (process.UNI_CLOUD_TCB) {
console.warn(`当前项目使用了腾讯云云服务空间,需在uniCloud后台开启匿名登录,详见:https://uniapp.dcloud.io/uniCloud/authentication?id=auth-anonymously`)
}
// h5 暂不支持阿里云服务空间
if (
process.UNI_CLOUD_ALIYUN &&
process.UNI_CLOUD &&
process.env.UNI_PLATFORM === 'h5' &&
process.env.NODE_ENV === 'production'
) {
console.error(`当前项目使用了阿里云服务空间,暂不支持发行到H5平台`)
process.exit(0)
console.warn(`发布H5,需要在uniCloud后台操作,绑定安全域名,否则会因为跨域问题而无法访问。教程参考:https://uniapp.dcloud.io/uniCloud/quickstart-H5`)
}
if (process.env.UNI_PLATFORM === 'mp-360') {
......@@ -83,7 +92,8 @@ if (process.env.NODE_ENV === 'production') { // 发行模式,不启用 cache
delete process.env.UNI_USING_CACHE
}
const {
const {
normalizePath,
isSupportSubPackages,
runByHBuilderX,
// isInHBuilderXAlpha,
......@@ -192,6 +202,21 @@ if (process.env.UNI_PLATFORM === 'app-plus') {
process.env.UNI_USING_V8 = true
process.env.UNI_OUTPUT_TMP_DIR = ''
}
// v3 支持指定 js 混淆(仅发行模式)
if (
process.env.NODE_ENV === 'production' &&
process.env.UNI_USING_V3
) {
const resources = platformOptions.confusion &&
platformOptions.confusion.resources
const resourcesKeys = resources &&
Object.keys(resources).filter(filepath => path.extname(filepath) === '.js')
if (resourcesKeys && resourcesKeys.length) {
process.UNI_CONFUSION = resourcesKeys.map(filepath =>
normalizePath(path.resolve(process.env.UNI_INPUT_DIR, filepath))
)
}
}
} else { // 其他平台,待确认配置方案
if (
manifestJsonObj['app-plus'] &&
......
const path = require('path')
module.exports = function getSplitChunks () {
const {
normalizePath
} = require('@dcloudio/uni-cli-shared')
if (process.env.UNI_USING_V3) {
return false
if (!process.UNI_CONFUSION) { // 无加密
return false
}
return {
cacheGroups: {
vendor: {
minSize: 0,
minChunks: 1,
test: function (module) {
if (!module.resource) {
return false
}
if (process.UNI_CONFUSION.includes(normalizePath(module.resource))) {
return true
}
return false
},
name: 'app-confusion',
chunks: 'all'
}
}
}
}
if (!process.env.UNI_USING_COMPONENTS) {
return {
......@@ -16,10 +41,6 @@ module.exports = function getSplitChunks () {
}
}
const {
normalizePath
} = require('@dcloudio/uni-cli-shared')
const mainPath = normalizePath(path.resolve(process.env.UNI_INPUT_DIR, 'main.'))
if (!process.env.UNI_OPT_SUBPACKAGES) {
......
/*!
* Vue.js v2.6.11
* (c) 2014-2019 Evan You
* (c) 2014-2020 Evan You
* Released under the MIT License.
*/
/* */
......@@ -5634,7 +5634,7 @@ var patch = function(oldVnode, vnode) {
Object.keys(data).forEach(function (key) { //仅同步 data 中有的数据
mpData[key] = mpInstance.data[key];
});
var diffData = diff(data, mpData);
var diffData = this.$shouldDiffData === false ? data : diff(data, mpData);
if (Object.keys(diffData).length) {
if (process.env.VUE_APP_DEBUG) {
console.log('[' + (+new Date) + '][' + (mpInstance.is || mpInstance.route) + '][' + this._uid +
......
......@@ -10,6 +10,27 @@ const {
class WebpackUniAppPlugin {
apply(compiler) {
if (process.UNI_CONFUSION) {
compiler.hooks.emit.tapPromise('webpack-uni-app-emit', compilation => {
return new Promise((resolve, reject) => {
if (compilation.assets['app-confusion.js']) { //存在加密
const manifestJson = JSON.parse(`${compilation.assets['manifest.json'].source()}`)
manifestJson.plus.confusion.resources['app-confusion.js'] = {}
const source = JSON.stringify(manifestJson)
compilation.assets['manifest.json'] = {
size() {
return Buffer.byteLength(source, 'utf8')
},
source() {
return source
}
}
}
resolve()
})
})
}
compiler.hooks.invalid.tap('webpack-uni-app-invalid', (fileName, changeTime) => {
if (fileName && typeof fileName === 'string') {
if (fileName.indexOf('.vue') !== -1 || fileName.indexOf('.nvue') !== -1) {
......
......@@ -34,23 +34,23 @@ function isPlainObject (obj) {
function normalizeNetworkTimeout (appJson) {
if (!isPlainObject(appJson.networkTimeout)) {
appJson.networkTimeout = {
request: 6000,
connectSocket: 6000,
uploadFile: 6000,
downloadFile: 6000
request: 60000,
connectSocket: 60000,
uploadFile: 60000,
downloadFile: 60000
}
} else {
if (typeof appJson.networkTimeout.request === 'undefined') {
appJson.networkTimeout.request = 6000
appJson.networkTimeout.request = 60000
}
if (typeof appJson.networkTimeout.connectSocket === 'undefined') {
appJson.networkTimeout.connectSocket = 6000
appJson.networkTimeout.connectSocket = 60000
}
if (typeof appJson.networkTimeout.uploadFile === 'undefined') {
appJson.networkTimeout.uploadFile = 6000
appJson.networkTimeout.uploadFile = 60000
}
if (typeof appJson.networkTimeout.downloadFile === 'undefined') {
appJson.networkTimeout.downloadFile = 6000
appJson.networkTimeout.downloadFile = 60000
}
}
}
......@@ -321,15 +321,19 @@ module.exports = function (pagesJson, userManifestJson) {
// 检查原生混淆选项
const confusion = manifestJson.plus.confusion
if (confusion && confusion.resources) {
const resources = {}
const resources = {}
const nvuePages = (appJson.nvue && appJson.nvue.pages) || {}
for (const key in confusion.resources) {
if (path.extname(key) === '.js') { // 支持 js 混淆,过滤掉
continue
}
if (!/\.nvue$/.test(key)) {
throw new Error(`原生混淆仅支持 nvue 页面,错误的页面路径:${key}`)
} else {
resources[key.replace(/\.nvue$/, '.js')] = confusion.resources[key]
}
if (!Object.keys(appJson.nvue.pages).find(path => {
const subNVues = appJson.nvue.pages[path].window.subNVues || []
if (!Object.keys(nvuePages).find(path => {
const subNVues = nvuePages[path].window.subNVues || []
return path.replace(/\.html$/, '.nvue') === key || subNVues.find(({
path
}) => path === key.replace(/\.nvue$/, ''))
......
......@@ -62,14 +62,20 @@ module.exports = function (pagesJson, manifestJson) {
app.subPackages.push(subPackages[root])
})
copyToJson(app, pagesJson, pagesJson2AppJson)
copyToJson(app, pagesJson, pagesJson2AppJson)
const platformJson = manifestJson['mp-alipay'] || {}
if (hasOwn(platformJson, 'plugins')) {
app.plugins = platformJson.plugins
}
if (app.usingComponents) {
updateAppJsonUsingComponents(app.usingComponents)
}
const project = Object.assign({}, manifestJson['mp-alipay'] || {})
delete project.usingComponents
delete project.usingComponents
delete project.plugins
return [{
name: 'app',
......
......@@ -163,8 +163,13 @@ function createApiCallback (apiName, params = {}, extras = {}) {
res.errMsg = apiName + ':ok'
} else if (res.errMsg.indexOf(':cancel') !== -1) {
res.errMsg = apiName + ':cancel'
} else if (res.errMsg.indexOf(':fail') !== -1) {
res.errMsg = apiName + ':fail' + res.errMsg.substr(res.errMsg.indexOf(' '))
} else if (res.errMsg.indexOf(':fail') !== -1) {
let errDetail = ''
let spaceIndex = res.errMsg.indexOf(' ')
if (spaceIndex > -1) {
errDetail = res.errMsg.substr(spaceIndex)
}
res.errMsg = apiName + ':fail' + errDetail
}
const errMsg = res.errMsg
......
// App端可以只使用files不传filePath和name
import getRealPath from 'uni-platform/helpers/get-real-path'
export const uploadFile = {
......@@ -5,16 +6,19 @@ export const uploadFile = {
type: String,
required: true
},
files: {
type: Array
},
filePath: {
type: String,
required: true,
validator (value, params) {
params.type = getRealPath(value)
if (value) {
params.type = getRealPath(value)
}
}
},
name: {
type: String,
required: true
type: String
},
header: {
type: Object,
......
......@@ -119,7 +119,7 @@ class BackgroundAudioManager {
this._operate('stop')
}
seek (position) {
this._operate('play', {
this._operate('seek', {
currentTime: position
})
}
......
......@@ -59,8 +59,8 @@ function getLocation (base = '/') {
export default {
install (Vue, {
routes
} = {}) {
initPolyfill(Vue)
} = {}) {
initPolyfill(Vue)
lifecycleMixin(Vue)
......@@ -147,9 +147,15 @@ export default {
const pageMixin = createPageMixin()
// mixin page hooks
Object.keys(pageMixin).forEach(hook => {
options[hook] = options[hook] ? [].concat(pageMixin[hook], options[hook]) : [
pageMixin[hook]
]
if (options.mpOptions) { // 小程序适配出来的 vue 组件(保证先调用小程序适配里的 created,再触发 onLoad)
options[hook] = options[hook] ? [].concat(options[hook], pageMixin[hook]) : [
pageMixin[hook]
]
} else {
options[hook] = options[hook] ? [].concat(pageMixin[hook], options[hook]) : [
pageMixin[hook]
]
}
})
} else {
if (this.$parent && this.$parent.__page__) {
......
......@@ -84,14 +84,15 @@ function getNodeInfo (el, fields) {
function getNodesInfo (pageVm, component, selector, single, fields) {
const $el = findElm(component, pageVm)
if (!$el || ($el && $el.nodeType === 8)) { // Comment
return single ? null : []
}
if (single) {
const node = $el && ($el.matches(selector) ? $el : $el.querySelector(selector))
const node = $el.matches(selector) ? $el : $el.querySelector(selector)
if (node) {
return getNodeInfo(node, fields)
}
return null
} else if (!$el) {
return []
} else {
let infos = []
const nodeList = $el.querySelectorAll(selector)
......@@ -101,7 +102,7 @@ function getNodesInfo (pageVm, component, selector, single, fields) {
})
}
if ($el.matches(selector)) {
infos.unshift($el)
infos.unshift(getNodeInfo($el, fields))
}
return infos
}
......@@ -139,4 +140,4 @@ export function requestComponentInfo ({
reqId,
res: result
}, pageVm.$page.id)
}
}
......@@ -131,6 +131,7 @@ export default {
overflow: hidden;
color: #000000;
background-color: #F8F8F8;
cursor: pointer;
}
uni-button[hidden] {
......@@ -192,6 +193,7 @@ export default {
uni-button[disabled] {
color: rgba(255, 255, 255, 0.6);
cursor: not-allowed;
}
uni-button[disabled][type=default],
......@@ -287,6 +289,10 @@ export default {
padding: 0 1.34em;
}
uni-button[loading]:not([disabled]){
cursor: progress;
}
uni-button[loading]:before {
content: " ";
display: inline-block;
......@@ -375,4 +381,4 @@ export default {
border-color: rgba(230, 67, 64, 0.6);
background-color: transparent;
}
</style>
</style>
<template>
<uni-checkbox
v-on="$listeners"
<uni-checkbox
:disabled="disabled"
v-on="$listeners"
@click="_onClick">
<div class="uni-checkbox-wrapper">
<div
:class="[checkboxChecked ? 'uni-checkbox-input-checked' : '']"
:style="{color:color}"
<div
:class="[checkboxChecked ? 'uni-checkbox-input-checked' : '']"
:style="{color:color}"
class="uni-checkbox-input" />
<slot />
</div>
......@@ -97,12 +98,17 @@ export default {
uni-checkbox {
-webkit-tap-highlight-color: transparent;
display: inline-block;
cursor: pointer;
}
uni-checkbox[hidden] {
display: none;
}
uni-checkbox[disabled] {
cursor: not-allowed;
}
uni-checkbox .uni-checkbox-wrapper {
display: -webkit-inline-flex;
display: inline-flex;
......@@ -124,6 +130,10 @@ export default {
position: relative;
}
uni-checkbox:not([disabled]) .uni-checkbox-input:hover {
border-color: #007aff;
}
uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked {
color: #007aff;
}
......@@ -150,4 +160,4 @@ export default {
uni-checkbox-group {
display: block;
}
</style>
</style>
<template>
<uni-label
:class="{'uni-label-pointer':pointer}"
v-on="$listeners"
@click="_onClick">
<slot />
......@@ -18,6 +19,11 @@ export default {
default: ''
}
},
computed: {
pointer () {
return this.for || (this.$slots.default && this.$slots.default.length)
}
},
methods: {
_onClick ($event) {
let stopPropagation = /^uni-(checkbox|radio|switch)-/.test($event.target.className)
......@@ -38,5 +44,7 @@ export default {
}
</script>
<style>
</style>
.uni-label-pointer {
cursor: pointer;
}
</style>
<template>
<uni-movable-view v-on="$listeners">
<v-uni-resize-sensor @resize="setParent"/>
<slot/>
<v-uni-resize-sensor @resize="setParent" />
<slot />
</uni-movable-view>
</template>
<script>
......@@ -11,8 +11,8 @@ import {
Friction,
STD
} from './utils'
import {
disableScrollBounce
import {
disableScrollBounce
} from 'uni-shared'
var requesting = false
......@@ -317,28 +317,16 @@ export default {
x = event.detail.dx + this.__baseX
this.__touchInfo.historyX.shift()
this.__touchInfo.historyX.push(x)
if (!this.yMove) {
if (!null !== this._checkCanMove) {
if (Math.abs(event.detail.dx / event.detail.dy) > 1) {
this._checkCanMove = false
} else {
this._checkCanMove = true
}
}
if (!this.yMove && this._checkCanMove === null) {
this._checkCanMove = Math.abs(event.detail.dx / event.detail.dy) < 1
}
}
if (this.yMove) {
y = event.detail.dy + this.__baseY
this.__touchInfo.historyY.shift()
this.__touchInfo.historyY.push(y)
if (!this.xMove) {
if (!null !== this._checkCanMove) {
if (Math.abs(event.detail.dy / event.detail.dx) > 1) {
this._checkCanMove = false
} else {
this._checkCanMove = true
}
}
if (!this.xMove && this._checkCanMove === null) {
this._checkCanMove = Math.abs(event.detail.dy / event.detail.dx) < 1
}
}
this.__touchInfo.historyT.shift()
......@@ -658,6 +646,7 @@ uni-movable-view {
top: 0px;
left: 0px;
position: absolute;
cursor: grab;
}
uni-movable-view[hidden] {
......
<template>
<uni-navigator
v-if="hoverClass && hoverClass !== 'none'"
:class="[hovering ? hoverClass : '']"
<uni-navigator
v-if="hoverClass && hoverClass !== 'none'"
:class="[hovering ? hoverClass : '']"
@touchstart="_hoverTouchStart"
@touchend="_hoverTouchEnd"
@touchcancel="_hoverTouchCancel"
@click="_onClick"
@touchend="_hoverTouchEnd"
@touchcancel="_hoverTouchCancel"
@click="_onClick"
v-on="$listeners">
<slot />
</uni-navigator>
<uni-navigator
v-else
@click="_onClick"
<uni-navigator
v-else
@click="_onClick"
v-on="$listeners">
<slot />
</uni-navigator>
......@@ -97,18 +97,19 @@ export default {
}
</script>
<style>
.navigator-hover {
background-color: rgba(0, 0, 0, 0.1);
opacity: 0.7;
}
uni-navigator {
height: auto;
width: auto;
display: block;
cursor: pointer;
}
uni-navigator[hidden] {
display: none;
}
</style>
.navigator-hover {
background-color: rgba(0, 0, 0, 0.1);
opacity: 0.7;
}
</style>
......@@ -13,16 +13,15 @@ import {
function initClick (dom) {
const MAX_MOVE = 20
const hasTouchSupport = navigator.maxTouchPoints
let x = 0
let y = 0
dom.addEventListener(hasTouchSupport ? 'touchstart' : 'mousedown', (event) => {
const info = hasTouchSupport ? event.changedTouches[0] : event
dom.addEventListener('touchstart', (event) => {
const info = event.changedTouches[0]
x = info.clientX
y = info.clientY
})
dom.addEventListener(hasTouchSupport ? 'touchend' : 'mouseup', (event) => {
const info = hasTouchSupport ? event.changedTouches[0] : event
dom.addEventListener('touchend', (event) => {
const info = event.changedTouches[0]
if (Math.abs(info.clientX - x) < MAX_MOVE && Math.abs(info.clientY - y) < MAX_MOVE) {
let customEvent = new CustomEvent('click', {
bubbles: true,
......@@ -33,7 +32,6 @@ function initClick (dom) {
['screenX', 'screenY', 'clientX', 'clientY', 'pageX', 'pageY'].forEach(key => {
customEvent[key] = info[key]
})
console.log(customEvent)
event.target.dispatchEvent(customEvent)
}
})
......@@ -302,6 +300,7 @@ export default {
width: 100%;
will-change: transform;
padding: 102px 0;
cursor: pointer;
}
.uni-picker-view-content>* {
......
<template>
<uni-radio
v-on="$listeners"
<uni-radio
:disabled="disabled"
v-on="$listeners"
@click="_onClick">
<div class="uni-radio-wrapper">
<div
:class="radioChecked ? 'uni-radio-input-checked' : ''"
:style="radioChecked ? checkedStyle : ''"
<div
:class="radioChecked ? 'uni-radio-input-checked' : ''"
:style="radioChecked ? checkedStyle : ''"
class="uni-radio-input" />
<slot />
</div>
......@@ -102,12 +103,17 @@ export default {
uni-radio {
-webkit-tap-highlight-color: transparent;
display: inline-block;
cursor: pointer;
}
uni-radio[hidden] {
display: none;
}
uni-radio[disabled] {
cursor: not-allowed;
}
uni-radio .uni-radio-wrapper {
display: -webkit-inline-flex;
display: inline-flex;
......@@ -129,6 +135,10 @@ export default {
position: relative;
}
uni-radio:not([disabled]) .uni-radio-input:hover {
border-color: #007aff;
}
uni-radio .uni-radio-input.uni-radio-input-checked:before {
font: normal normal normal 14px/1 "uni";
content: "\EA08";
......@@ -153,4 +163,4 @@ export default {
uni-radio-group {
display: block;
}
</style>
</style>
......@@ -8,6 +8,48 @@
:style="{'overflow-x': scrollX?'auto':'hidden','overflow-y': scrollY?'auto':'hidden'}"
class="uni-scroll-view">
<div ref="content">
<div
v-if="refresherEnabled"
ref="refresherinner"
:style="{'background-color': refresherBackground, 'height': refresherHeight + 'px'}"
class="uni-scroll-view-refresher">
<div
v-if="refresherDefaultStyle !== 'none'"
class="uni-scroll-view-refresh">
<div class="uni-scroll-view-refresh-inner">
<svg
v-if="refreshState=='pulling'"
: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="M0 0h24v24H0z"
fill="none" />
</svg>
<svg
v-if="refreshState=='refreshing'"
class="uni-scroll-view-refresh__spinner"
width="24"
height="24"
viewBox="25 25 50 50">
<circle
cx="50"
cy="50"
r="20"
fill="none"
style="color: #2BD009;"
stroke-width="3"/>
</svg>
</div>
</div>
<slot
v-if="refresherDefaultStyle=='none'"
name="refresher"/>
</div>
<slot/>
</div>
</div>
......@@ -24,6 +66,10 @@ import {
const passiveOptions = supportsPassive ? {
passive: true
} : false
// const PULLING = 'pulling'
// const REFRESHING = 'refreshing'
export default {
name: 'ScrollView',
mixins: [scroller],
......@@ -63,6 +109,26 @@ export default {
enableBackToTop: {
type: [Boolean, String],
default: false
},
refresherEnabled: {
type: [Boolean, String],
default: false
},
refresherThreshold: {
type: Number,
default: 45
},
refresherDefaultStyle: {
type: String,
default: 'back'
},
refresherBackground: {
type: String,
default: '#fff'
},
refresherTriggered: {
type: [Boolean, String],
default: false
}
},
data () {
......@@ -70,7 +136,10 @@ export default {
lastScrollTop: this.scrollTopNumber,
lastScrollLeft: this.scrollLeftNumber,
lastScrollToUpperTime: 0,
lastScrollToLowerTime: 0
lastScrollToLowerTime: 0,
refresherHeight: 0,
refreshRotate: 0,
refreshState: ''
}
},
computed: {
......@@ -98,6 +167,14 @@ export default {
},
scrollIntoView (val) {
this._scrollIntoViewChanged(val)
},
refresherTriggered (val) {
// TODO
if (val === true) {
this._setRefreshState('refreshing')
} else if (val === false) {
this._setRefreshState('restore')
}
}
},
mounted () {
......@@ -151,6 +228,23 @@ export default {
if (needStop) {
event.stopPropagation()
}
if (self.refresherEnabled && self.refreshState === 'pulling') {
let dy = y - touchStart.y
self.refresherHeight = dy
let rotate = dy / self.refresherThreshold
if (rotate > 1) {
rotate = 1
} else {
rotate = rotate * 360
}
self.refreshRotate = rotate
self.$trigger('refresherpulling', event, {
deltaY: dy
})
}
}
this.__handleTouchStart = function (event) {
......@@ -163,12 +257,22 @@ export default {
x: event.touches[0].pageX,
y: event.touches[0].pageY
}
if (self.refresherEnabled && self.refreshState !== 'refreshing' && self.$refs.main.scrollTop === 0) {
self.refreshState = 'pulling'
}
}
}
this.__handleTouchEnd = function (event) {
touchStart = null
disableScrollBounce({
disable: false
})
if (self.refresherHeight >= self.refresherThreshold) {
self._setRefreshState('refreshing')
} else {
self.refresherHeight = 0
self.$trigger('refresherabort', event, {})
}
}
this.$refs.main.addEventListener('touchstart', this.__handleTouchStart, passiveOptions)
this.$refs.main.addEventListener('touchmove', this.__handleTouchMove, passiveOptions)
......@@ -374,6 +478,19 @@ export default {
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, {})
break
case 'restore':
this.refresherHeight = 0
this.$trigger('refresherrestore', {}, {})
break
}
this.refreshState = state
},
getScrollPosition () {
const main = this.$refs.main
return {
......@@ -402,4 +519,71 @@ uni-scroll-view[hidden] {
height: 100%;
max-height: inherit;
}
.uni-scroll-view-refresher {
position: relative;
overflow: hidden;
}
.uni-scroll-view-refresh {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.uni-scroll-view-refresh-inner {
display: flex;
align-items: center;
justify-content: center;
line-height: 0;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #fff;
box-shadow: 0 1px 6px rgba(0, 0, 0, .117647), 0 1px 4px rgba(0, 0, 0, .117647);
}
.uni-scroll-view-refresh__spinner {
transform-origin: center center;
animation: uni-scroll-view-refresh-rotate 2s linear infinite;
}
.uni-scroll-view-refresh__spinner > circle {
stroke: currentColor;
stroke-linecap: round;
animation: uni-scroll-view-refresh-dash 2s linear infinite;
}
@keyframes uni-scroll-view-refresh-rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes uni-scroll-view-refresh-dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124px;
}
}
</style>
<template>
<uni-slider
ref="uni-slider"
v-on="$listeners"
<uni-slider
ref="uni-slider"
v-on="$listeners"
@click="_onClick">
<div class="uni-slider-wrapper">
<div class="uni-slider-tap-area">
<div
:style="setBgColor"
<div
:style="setBgColor"
class="uni-slider-handle-wrapper">
<div
ref="uni-slider-handle"
:style="setBlockBg"
<div
ref="uni-slider-handle"
:style="setBlockBg"
class="uni-slider-handle" />
<div
:style="setBlockStyle"
<div
:style="setBlockStyle"
class="uni-slider-thumb" />
<div
:style="setActiveColor"
<div
:style="setActiveColor"
class="uni-slider-track" />
</div>
</div>
<span
v-show="showValue"
<span
v-show="showValue"
class="uni-slider-value">{{ sliderValue }}</span>
</div>
<slot />
......@@ -259,6 +259,7 @@ export default {
margin-left: -14px;
background-color: transparent;
z-index: 3;
cursor: grab;
}
uni-slider .uni-slider-thumb {
......@@ -288,4 +289,4 @@ export default {
background-color: #FFF;
border-color: #ccc;
}
</style>
</style>
......@@ -34,6 +34,7 @@ uni-swiper-item {
position: absolute;
width: 100%;
height: 100%;
cursor: grab;
}
uni-swiper-item[hidden] {
......
......@@ -596,6 +596,11 @@ export default {
for (let index = 0, length = swiperItems.length; index < length; index++) {
let currentSync = this.currentSync
slidesDots.push(createElement('div', {
on: {
click: () => {
this._animateViewport(this.currentSync = index, this.currentChangeSource = 'click', this.circularEnabled ? 1 : 0)
}
},
class: {
'uni-swiper-dot': true,
'uni-swiper-dot-active': (index < currentSync + this.displayMultipleItemsNumber && index >= currentSync) || (index < currentSync + this.displayMultipleItemsNumber - length)
......
<template>
<uni-switch
v-on="$listeners"
<uni-switch
:disabled="disabled"
v-on="$listeners"
@click="_onClick">
<div class="uni-switch-wrapper">
<div
v-show="type === 'switch'"
:class="[switchChecked ? 'uni-switch-input-checked' : '']"
<div
v-show="type === 'switch'"
:class="[switchChecked ? 'uni-switch-input-checked' : '']"
:style="{backgroundColor: switchChecked ? color : '#DFDFDF',borderColor:switchChecked ? color : '#DFDFDF'}"
class="uni-switch-input" />
<div
v-show="type === 'checkbox'"
:class="[switchChecked ? 'uni-checkbox-input-checked' : '']"
<div
v-show="type === 'checkbox'"
:class="[switchChecked ? 'uni-checkbox-input-checked' : '']"
:style="{color: color}"
class="uni-checkbox-input" />
</div>
......@@ -105,12 +106,17 @@ export default {
uni-switch {
-webkit-tap-highlight-color: transparent;
display: inline-block;
cursor: pointer;
}
uni-switch[hidden] {
display: none;
}
uni-switch[disabled] {
cursor: not-allowed;
}
uni-switch .uni-switch-wrapper {
display: -webkit-inline-flex;
display: inline-flex;
......@@ -134,6 +140,10 @@ export default {
transition: background-color 0.1s, border 0.1s;
}
uni-switch[disabled] .uni-switch-input {
opacity: .7;
}
uni-switch .uni-switch-input:before {
content: " ";
position: absolute;
......@@ -192,6 +202,10 @@ export default {
color: #007aff;
}
uni-switch:not([disabled]) .uni-checkbox-input:hover {
border-color: #007aff;
}
uni-switch .uni-checkbox-input.uni-checkbox-input-checked:before {
font: normal normal normal 14px/1 "uni";
content: "\EA08";
......@@ -211,4 +225,4 @@ export default {
uni-switch .uni-checkbox-input.uni-checkbox-input-disabled:before {
color: #ADADAD;
}
</style>
</style>
......@@ -17,6 +17,10 @@ export default {
type: [Number, String],
default: 0
},
showConfirmBar: {
type: [Boolean, String],
default: 'auto'
},
adjustPosition: {
type: Boolean,
default: true
......@@ -42,9 +46,10 @@ export default {
el.addEventListener('focus', () => {
UniViewJSBridge.subscribe('hideKeyboard', hideKeyboard)
document.addEventListener('click', iosHideKeyboard, false)
this.setSoftinputNavBar()
this.setSoftinputTemporary()
})
el.addEventListener('blur', this.onKeyboardHide)
el.addEventListener('blur', this.onKeyboardHide.bind(this))
},
showSoftKeybord () {
plusReady(() => {
......@@ -53,12 +58,12 @@ export default {
},
setSoftinputTemporary () {
plusReady(() => {
var currentWebview = plus.webview.currentWebview()
var style = currentWebview.getStyle() || {}
const currentWebview = plus.webview.currentWebview()
const style = currentWebview.getStyle() || {}
if (style.softinputMode === 'adjustResize') {
return
}
var rect = this.$el.getBoundingClientRect()
const rect = this.$el.getBoundingClientRect()
currentWebview.setSoftinputTemporary && currentWebview.setSoftinputTemporary({
mode: this.adjustPosition ? 'adjustPan' : 'nothing',
position: {
......@@ -68,9 +73,40 @@ export default {
})
})
},
setSoftinputNavBar () {
if (this.showConfirmBar === 'auto') {
delete this.__softinputNavBar
return
}
plusReady(() => {
const currentWebview = plus.webview.currentWebview()
const { softinputNavBar } = currentWebview.getStyle() || {}
const showConfirmBar = softinputNavBar !== 'none'
if (showConfirmBar !== this.showConfirmBar) {
this.__softinputNavBar = softinputNavBar || 'auto'
currentWebview.setStyle({
softinputNavBar: this.showConfirmBar ? 'auto' : 'none'
})
} else {
delete this.__softinputNavBar
}
})
},
resetSoftinputNavBar () {
const softinputNavBar = this.__softinputNavBar
if (softinputNavBar) {
plusReady(() => {
const currentWebview = plus.webview.currentWebview()
currentWebview.setStyle({
softinputNavBar
})
})
}
},
onKeyboardHide () {
UniViewJSBridge.unsubscribe('hideKeyboard', hideKeyboard)
document.removeEventListener('click', iosHideKeyboard, false)
this.resetSoftinputNavBar()
}
}
}
var addListenerToElement = function (element, type, callback, r) {
// 暂时忽略capture
const addListenerToElement = function (element, type, callback, capture) {
// 暂时忽略 capture
element.addEventListener(type, $event => {
if (typeof callback === 'function') {
if (callback($event) === false) {
......@@ -13,14 +13,18 @@ var addListenerToElement = function (element, type, callback, r) {
}
export default {
beforeDestroy () {
document.removeEventListener('mousemove', this.__mouseMoveEventListener)
document.removeEventListener('mouseup', this.__mouseUpEventListener)
},
methods: {
touchtrack: function (element, method, useCancel) {
var self = this
var x0 = 0
var y0 = 0
var x1 = 0
var y1 = 0
var fn = function ($event, state, x, y) {
const self = this
let x0 = 0
let y0 = 0
let x1 = 0
let y1 = 0
const fn = function ($event, state, x, y) {
if (self[method]({
target: $event.target,
currentTarget: $event.currentTarget,
......@@ -43,8 +47,11 @@ export default {
}
}
var $eventOld = null
let $eventOld = null
let hasTouchStart
let hasMouseDown
addListenerToElement(element, 'touchstart', function ($event) {
hasTouchStart = true
if ($event.touches.length === 1 && !$eventOld) {
$eventOld = $event
x0 = x1 = $event.touches[0].pageX
......@@ -52,23 +59,54 @@ export default {
return fn($event, 'start', x0, y0)
}
})
addListenerToElement(element, 'mousedown', function ($event) {
hasMouseDown = true
if (!hasTouchStart && !$eventOld) {
// TODO touches changedTouches
$eventOld = $event
x0 = x1 = $event.pageX
y0 = y1 = $event.pageY
return fn($event, 'start', x0, y0)
}
})
addListenerToElement(element, 'touchmove', function ($event) {
if ($event.touches.length === 1 && $eventOld) {
var res = fn($event, 'move', $event.touches[0].pageX, $event.touches[0].pageY)
const res = fn($event, 'move', $event.touches[0].pageX, $event.touches[0].pageY)
x1 = $event.touches[0].pageX
y1 = $event.touches[0].pageY
return res
}
})
const mouseMoveEventListener = this.__mouseMoveEventListener = function ($event) {
if (!hasTouchStart && hasMouseDown && $eventOld) {
// TODO target currentTarget touches changedTouches
const res = fn($event, 'move', $event.pageX, $event.pageY)
x1 = $event.pageX
y1 = $event.pageY
return res
}
}
document.addEventListener('mousemove', mouseMoveEventListener)
addListenerToElement(element, 'touchend', function ($event) {
if ($event.touches.length === 0 && $eventOld) {
hasTouchStart = false
$eventOld = null
return fn($event, 'end', $event.changedTouches[0].pageX, $event.changedTouches[0].pageY)
}
})
const mouseUpEventListener = this.__mouseUpEventListener = function ($event) {
hasMouseDown = false
if (!hasTouchStart && $eventOld) {
// TODO target currentTarget touches changedTouches
$eventOld = null
return fn($event, 'end', $event.pageX, $event.pageY)
}
}
document.addEventListener('mouseup', mouseUpEventListener)
addListenerToElement(element, 'touchcancel', function ($event) {
if ($eventOld) {
var $eventTemp = $eventOld
hasTouchStart = false
const $eventTemp = $eventOld
$eventOld = null
return fn($event, useCancel ? 'cancel' : 'end', $eventTemp.touches[0].pageX, $eventTemp.touches[0].pageY)
}
......
......@@ -7,7 +7,7 @@ import {
export default function initVue (Vue) {
Vue.config.errorHandler = function (err) {
const app = getApp()
const app = typeof getApp === 'function' && getApp()
if (app && hasLifecycleHook(app.$options, 'onError')) {
app.__call_hook('onError', err)
} else {
......
const eventNames = [
'load',
'close',
'error'
]
const ERROR_CODE_LIST = [-5001, -5002, -5003, -5004, -5005, -5006]
class RewardedVideoAd {
constructor (adpid) {
this._options = {
adpid: adpid
}
const _callbacks = this._callbacks = {}
eventNames.forEach(item => {
_callbacks[item] = []
const name = item[0].toUpperCase() + item.substr(1)
this[`on${name}`] = function (callback) {
_callbacks[item].push(callback)
}
})
this._isLoad = false
this._adError = ''
this._loadPromiseResolve = null
this._loadPromiseReject = null
const rewardAd = this._rewardAd = plus.ad.createRewardedVideoAd(this._options)
rewardAd.onLoad((e) => {
this._isLoad = true
this._dispatchEvent('load', {})
if (this._loadPromiseResolve != null) {
this._loadPromiseResolve()
this._loadPromiseResolve = null
}
})
rewardAd.onClose((e) => {
this._loadAd()
this._dispatchEvent('close', { isEnded: e.isEnded })
})
rewardAd.onError((e) => {
const { code, message } = e
const data = { code: code, errMsg: message }
this._adError = message
this._dispatchEvent('error', data)
if ((code === -5005 || ERROR_CODE_LIST.index(code) === -1) && this._loadPromiseReject != null) {
this._loadPromiseReject(data)
this._loadPromiseReject = null
}
})
this._loadAd()
}
load () {
return new Promise((resolve, reject) => {
if (this._isLoad) {
resolve()
return
}
this._loadPromiseResolve = resolve
this._loadPromiseReject = reject
this._loadAd()
})
}
show () {
return new Promise((resolve, reject) => {
if (this._isLoad) {
this._rewardAd.show()
resolve()
} else {
reject(new Error(this._adError))
}
})
}
_loadAd () {
this._isLoad = false
this._rewardAd.load()
}
_dispatchEvent (name, data) {
this._callbacks[name].forEach(callback => {
if (typeof callback === 'function') {
callback(data || {})
}
})
}
}
export function createRewardedVideoAd ({
adpid = ''
} = {}) {
return new RewardedVideoAd(adpid)
}
import {
createLivePusherContext as createLivePusher
} from 'uni-platforms/app-plus-nvue/service/api/context/live-pusher'
export function createLivePusherContext (id, vm) {
return createLivePusher(id, vm)
}
......@@ -4,6 +4,7 @@ export * from './context/background-audio'
export * from './context/canvas'
export * from './context/operate-map-player'
export * from './context/operate-video-player'
export * from './context/live-pusher'
export * from './device/accelerometer'
export * from './device/add-phone-contact'
......@@ -69,3 +70,5 @@ export {
export * from './ui/tab-bar'
export * from './ui/request-component-info'
export * from './ad/rewarded-video-ad'
......@@ -59,7 +59,7 @@ export function createRequestTaskById (requestTaskId, {
statusCode: 0,
errMsg: 'timeout'
})
}, timeout)
}, (timeout + 200))// TODO +200 发消息到原生层有时间开销,以后考虑由原生层回调超时
}
const options = {
method,
......
......@@ -60,6 +60,9 @@ function _switchTab ({
// 查找当前 tabBarPage,且设置 visible
getCurrentPages(true).forEach(page => {
if (('/' + page.route) === path) {
if (!page.$page.meta.visible) {
page.$vm.__call_hook('onShow')
}
page.$page.meta.visible = true
tabBarPage = page
} else {
......@@ -70,7 +73,6 @@ function _switchTab ({
})
if (tabBarPage) {
tabBarPage.$vm.__call_hook('onShow')
tabBarPage.$getAppWebview().show('none')
} else {
return showWebview(registerPage({
......@@ -105,4 +107,4 @@ export function switchTab ({
from
}, callbackId)
}, openType === 'appLaunch')
}
}
......@@ -119,10 +119,6 @@ function initTabBar () {
__uniConfig.tabBar.selected = 0
const selected = __uniConfig.tabBar.list.findIndex(page => page.pagePath === __uniConfig.entryPagePath)
if (selected !== -1) {
// 取当前 tab 索引值
__uniConfig.tabBar.selected = selected
}
tabBar.init(__uniConfig.tabBar, (item, index) => {
uni.switchTab({
......@@ -138,22 +134,36 @@ function initTabBar () {
}
})
})
}
function initEntryPage () {
const argsJsonStr = plus.runtime.arguments
if (!argsJsonStr) {
return
if (selected !== -1) {
// 取当前 tab 索引值
__uniConfig.tabBar.selected = selected
selected !== 0 && tabBar.switchTab(__uniConfig.entryPagePath)
}
}
function initEntryPage () {
let entryPagePath
let entryPageQuery
try {
const args = JSON.parse(argsJsonStr)
entryPagePath = args.path || args.pathName
entryPageQuery = (args.query ? ('?' + args.query) : '')
} catch (e) {}
const weexPlus = weex.requireModule('plus')
if (weexPlus.getRedirectInfo) {
const info = weexPlus.getRedirectInfo() || {}
entryPagePath = info.path
entryPageQuery = info.query ? ('?' + info.query) : ''
} else {
const argsJsonStr = plus.runtime.arguments
if (!argsJsonStr) {
return
}
try {
const args = JSON.parse(argsJsonStr)
entryPagePath = args.path || args.pathName
entryPageQuery = args.query ? ('?' + args.query) : ''
} catch (e) {}
}
if (!entryPagePath || entryPagePath === __uniConfig.entryPagePath) {
return
}
......@@ -180,8 +190,8 @@ export function registerApp (appVm) {
if (process.env.NODE_ENV !== 'production') {
console.log(`[uni-app] registerApp`)
}
appCtx = appVm
appCtx = appVm
appCtx.$vm = appVm
Object.assign(appCtx, defaultApp) // 拷贝默认实现
......
import {
guid,
hasOwn,
isObject,
camelize
......@@ -88,7 +87,11 @@ export function initData (Vue) {
this._$vdomSync = new VDomSync(this.$options.pageId, this.$options.pagePath, this)
}
if (this._$vd) {
this._$id = guid()
if (!this.$parent) {
this._$id = '-1'
} else {
this._$id = this.$parent._$id + ',' + this.$vnode.data.attrs._i
}
this._$vd.addVm(this)
this._$vdMountedData = Object.create(null)
this._$setData(MOUNTED_DATA, this._$vdMountedData)
......
......@@ -54,17 +54,19 @@ export default {
Vue.prototype.$nextTick = function nextTick (cb) {
const renderWatcher = this._watcher
const callback = typeof cb === 'function'
if (
renderWatcher &&
this._$queue.find(watcher => renderWatcher === watcher)
) {
vdSyncCallbacks.push(cb.bind(this))
const result = new Promise((resolve) => {
vdSyncCallbacks.push(callback ? cb.bind(this) : resolve)
})
return callback ? result : undefined
} else {
// $nextTick bind vm context
Vue.nextTick(() => {
cb.call(this)
})
return Vue.nextTick(callback ? () => cb.call(this) : undefined)
}
}
}
}
}
......@@ -92,7 +92,9 @@ function showTabBar (animation) {
tabBar && tabBar.showTabBar({
animation
})
}
}
let maskClickCallback = []
export default {
id: '0',
......@@ -104,7 +106,12 @@ export default {
tabBar = requireNativePlugin('uni-tabview')
} catch (error) {
console.log(`uni.requireNativePlugin("uni-tabview") error ${error}`)
}
}
tabBar.onMaskClick(() => {
maskClickCallback.forEach((callback) => {
callback()
})
})
tabBar && tabBar.onClick(({ index }) => {
clickCallback(config.list[index], index)
})
......@@ -170,7 +177,11 @@ export default {
color: mask
})
},
addEventListener (name, callback) {
tabBar.onMaskClick(callback)
addEventListener (name, callback) {
maskClickCallback.push(callback)
},
removeEventListener (name, callback) {
let callbackIndex = maskClickCallback.indexOf(callback)
maskClickCallback.splice(callbackIndex, 1)
}
}
......@@ -67,6 +67,10 @@ export function parseWebviewStyle (id, path, routeOptions = {}) {
delete webviewStyle.popGesture
}
if (routeOptions.meta.isQuit) { // 退出
webviewStyle.popGesture = plus.os.name === 'iOS' ? 'appback' : 'none'
}
// TODO 下拉刷新
if (path && routeOptions.meta.isNVue) {
......
......@@ -167,7 +167,7 @@ export default {
getAdData(adpid || this.adpid, this.position.width, (data) => {
this._fillData(data)
}, (err) => {
this.$trigger('error', err)
this.$trigger('error', {}, err)
})
},
_fillData (data) {
......
......@@ -27,6 +27,7 @@ function padLeft (num) {
return num > 9 ? num : (`0${num}`)
}
function getDate (str, mode_) {
str = String(str || '')
const date = new Date()
if (mode_ === mode.TIME) {
str = str.split(':')
......@@ -72,10 +73,7 @@ export default {
},
fields: {
type: String,
default: 'day',
validator (val) {
return Object.values(fields).indexOf(val) >= 0
}
default: ''
},
start: {
type: String,
......@@ -90,7 +88,7 @@ export default {
return year
case fields.MONTH:
return year + '-01'
case fields.DAY:
default:
return year + '-01-01'
}
}
......@@ -110,7 +108,7 @@ export default {
return year
case fields.MONTH:
return year + '-12'
case fields.DAY:
default:
return year + '-12-31'
}
}
......@@ -151,7 +149,7 @@ export default {
this._showPicker(Object.assign({}, this.$props))
},
_showPicker (data) {
if (this.mode === mode.TIME || this.mode === mode.DATE) {
if ((data.mode === mode.TIME || data.mode === mode.DATE) && !data.fields) {
plus.nativeUI[this.mode === mode.TIME ? 'pickTime' : 'pickDate']((res) => {
const date = res.date
this.$trigger('change', {}, {
......@@ -167,6 +165,7 @@ export default {
maxDate: getDate(this.end, mode.DATE)
})
} else {
data.fields = Object.values(fields).includes(data.fields) ? data.fields : fields.DAY
let res = { event: 'cancel' }
this.page = showPage({
url: '__uniapppicker',
......
import {
guid
} from 'uni-shared'
import {
VD_SYNC,
UI_EVENT
} from '../../../constants'
function findParentCid (vm) {
let parent = vm.$parent
while (parent) {
if (parent._$id) {
return parent._$id
}
parent = parent.$parent
}
}
export class VDomSync {
constructor (pageId) {
this.pageId = pageId
this.addBatchVData = []
this.addBatchVData = Object.create(null)
this.updateBatchVData = []
this.vms = Object.create(null)
}
addVData (cid, data = {}, options = {}) {
this.addBatchVData.push([cid, data, options])
this.addBatchVData[cid] = [data, options]
}
updateVData (cid, data = {}) {
......@@ -24,13 +30,22 @@ export class VDomSync {
}
initVm (vm) {
const [cid, data, options] = this.addBatchVData.shift()
if (!cid) {
vm._$id = guid()
if (!vm.$parent) {
vm._$id = '-1'
} else {
vm._$id = findParentCid(vm) + ',' + vm.$vnode.data.attrs._i
}
let vData = this.addBatchVData[vm._$id]
if (!vData) {
console.error('cid unmatched', vm)
vData = {
data: {},
options: {}
}
} else {
vm._$id = cid
delete this.addBatchVData[vm._$id]
}
const [data, options] = vData
Object.assign(vm.$options, options)
vm.$r = data || Object.create(null)
this.vms[vm._$id] = vm
......@@ -50,7 +65,12 @@ export class VDomSync {
}
clearAddBatchVData () {
this.addBatchVData.length = 0
if (process.env.NODE_ENV !== 'production') {
if (Object.keys(this.addBatchVData).length) {
console.error('this.addBatchVData...=' + JSON.stringify(this.addBatchVData))
}
}
this.addBatchVData = Object.create(null)
}
flush () {
......
......@@ -100,6 +100,7 @@ uni-tabbar .uni-tabbar__bd {
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
}
uni-tabbar .uni-tabbar__icon {
......
<template>
<div
class="uni-async-error"
<div
class="uni-async-error"
@click="_onClick">
网络不给力,点击屏幕重试
连接服务器超时,点击屏幕重试
</div>
</template>
<style>
......@@ -27,4 +27,4 @@ export default {
}
}
}
</script>
</script>
......@@ -175,6 +175,7 @@ uni-page-head .uni-page-head-bd {
margin: 0 2px;
word-break: keep-all;
white-space: pre;
cursor: pointer;
}
.uni-page-head-transparent .uni-page-head-btn {
......
<template>
<uni-picker
:disabled="disabled"
@click.stop="_show"
v-on="$listeners">
<div
......@@ -55,6 +56,42 @@
import { emitter } from 'uni-mixins'
import { formatDateTime } from 'uni-shared'
function getDefaultStartValue () {
if (this.mode === mode.TIME) {
return '00:00'
}
if (this.mode === mode.DATE) {
let year = new Date().getFullYear() - 100
switch (this.fields) {
case fields.YEAR:
return year
case fields.MONTH:
return year + '-01'
case fields.DAY:
return year + '-01-01'
}
}
return ''
}
function getDefaultEndValue () {
if (this.mode === mode.TIME) {
return '23:59'
}
if (this.mode === mode.DATE) {
let year = new Date().getFullYear() + 100
switch (this.fields) {
case fields.YEAR:
return year
case fields.MONTH:
return year + '-12'
case fields.DAY:
return year + '-12-31'
}
}
return ''
}
const mode = {
SELECTOR: 'selector',
MULTISELECTOR: 'multiSelector',
......@@ -106,43 +143,11 @@ export default {
},
start: {
type: String,
default () {
if (this.mode === mode.TIME) {
return '00:00'
}
if (this.mode === mode.DATE) {
let year = new Date().getFullYear() - 100
switch (this.fields) {
case fields.YEAR:
return year
case fields.MONTH:
return year + '-01'
case fields.DAY:
return year + '-01-01'
}
}
return ''
}
default: getDefaultStartValue
},
end: {
type: String,
default () {
if (this.mode === mode.TIME) {
return '23:59'
}
if (this.mode === mode.DATE) {
let year = new Date().getFullYear() + 100
switch (this.fields) {
case fields.YEAR:
return year
case fields.MONTH:
return year + '-12'
case fields.DAY:
return year + '-12-31'
}
}
return ''
}
default: getDefaultEndValue
},
disabled: {
type: [Boolean, String],
......@@ -185,24 +190,10 @@ export default {
}
},
startArray () {
var splitStr = this.mode === mode.DATE ? '-' : ':'
var array = this.mode === mode.DATE ? this.dateArray : this.timeArray
var val = this.start.split(splitStr).map((val, i) => array[i].indexOf(
val))
if (val.indexOf(-1) >= 0) {
val = array.map(() => 0)
}
return val
return this._getDateValueArray(this.start, getDefaultStartValue.bind(this)())
},
endArray () {
var splitStr = this.mode === mode.DATE ? '-' : ':'
var array = this.mode === mode.DATE ? this.dateArray : this.timeArray
var val = this.end.split(splitStr).map((val, i) => array[i].indexOf(
val))
if (val.indexOf(-1) >= 0) {
val = array.map((val) => val.length - 1)
}
return val
return this._getDateValueArray(this.end, getDefaultEndValue.bind(this)())
},
units () {
switch (this.mode) {
......@@ -241,13 +232,12 @@ export default {
if (this.mode === mode.DATE) {
const dateArray = this.dateArray
let max = dateArray[2].length
let day = dateArray[2][valueArray[2]]
let day = Number(dateArray[2][valueArray[2]]) || 1
let realDay = new Date(
`${dateArray[0][valueArray[0]]}/${
dateArray[1][valueArray[1]]
}/${day}`
).getDate()
day = Number(day)
if (realDay < day) {
valueArray[2] -= realDay + max - day
}
......@@ -367,48 +357,14 @@ export default {
valueArray = [...val]
break
case mode.TIME:
var timeValTestFail = false
if (typeof this.value !== 'string') {
timeValTestFail = true
} else {
val.split(':').map((val, i) => {
var valIndex = this.timeArray[i].indexOf(val)
if (valIndex === -1) {
timeValTestFail = true
}
})
}
// 处理默认值为当前时间
if (timeValTestFail) {
val = formatDateTime({
mode: mode.TIME
})
}
valueArray = val
.split(':')
.map((val, i) => this.timeArray[i].indexOf(val))
valueArray = this._getDateValueArray(val, formatDateTime({
mode: mode.TIME
}))
break
case mode.DATE:
var dateValTestFail = false
if (typeof this.value !== 'string') {
dateValTestFail = true
} else {
val.split('-').map((val, i) => {
var valIndex = this.dateArray[i].indexOf(val)
if (valIndex === -1) {
dateValTestFail = true
}
})
}
// 处理默认值为当前日期
if (dateValTestFail) {
val = formatDateTime({
mode: mode.DATE
})
}
valueArray = val
.split('-')
.map((val, i) => this.dateArray[i].indexOf(val))
valueArray = this._getDateValueArray(val, formatDateTime({
mode: mode.DATE
}))
break
}
this.oldValueArray = [...valueArray]
......@@ -431,6 +387,29 @@ export default {
.join('-')
}
},
_getDateValueArray (valueStr, defaultValue) {
const splitStr = this.mode === mode.DATE ? '-' : ':'
const array = this.mode === mode.DATE ? this.dateArray : this.timeArray
let max = 3
switch (this.fields) {
case fields.YEAR:
max = 1
break
case fields.MONTH:
max = 2
break
}
const inputArray = String(valueStr).split(splitStr)
let value = []
for (let i = 0; i < max; i++) {
const val = inputArray[i]
value.push(array[i].indexOf(val))
}
if (value.indexOf(-1) >= 0) {
value = defaultValue ? this._getDateValueArray(defaultValue) : value.map(() => 0)
}
return value
},
_change () {
this._close()
this.valueChangeSource = 'click'
......@@ -460,6 +439,15 @@ export default {
<style>
uni-picker {
display: block;
cursor: pointer;
}
uni-picker[hidden] {
display: none;
}
uni-picker[disabled] {
cursor: not-allowed;
}
.uni-picker-container {
......@@ -548,6 +536,7 @@ uni-picker {
font-size: 17px;
line-height: 45px;
overflow: hidden;
cursor: pointer;
}
.uni-picker-container .uni-picker-action.uni-picker-action-cancel {
......
......@@ -787,6 +787,7 @@ uni-video[hidden] {
background-size: 50%;
background-repeat: no-repeat;
background-position: 50% 50%;
cursor: pointer;
}
.uni-video-cover-duration {
......@@ -831,6 +832,7 @@ uni-video[hidden] {
padding: 14.5px 12.5px 14.5px 12.5px;
margin-left: -8.5px;
box-sizing: content-box;
cursor: pointer;
}
.uni-video-control-button::after {
......@@ -873,6 +875,7 @@ uni-video[hidden] {
margin: 21px 12px;
background-color: rgba(255, 255, 255, 0.4);
position: relative;
cursor: pointer;
}
.uni-video-progress-buffered {
......@@ -912,6 +915,7 @@ uni-video[hidden] {
font-size: 13px;
color: #fff;
margin: 0 8.5px;
cursor: pointer;
}
.uni-video-danmu-button.uni-video-danmu-button-active {
......@@ -928,6 +932,7 @@ uni-video[hidden] {
background-size: 50%;
background-position: 50% 50%;
background-repeat: no-repeat;
cursor: pointer;
}
.uni-video-fullscreen.uni-video-type-fullscreen {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册