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

merge

...@@ -29,7 +29,7 @@ App端如需要更丰富的相机拍照API(如直接调用前置摄像头) ...@@ -29,7 +29,7 @@ App端如需要更丰富的相机拍照API(如直接调用前置摄像头)
|参数|类型|说明| |参数|类型|说明|
|:-|:-|:-| |:-|:-|:-|
|tempFilePaths|Array<String>|图片的本地文件路径列表| |tempFilePaths|Array<String>|图片的本地文件路径列表|
|tempFiles|Array<Object>|图片的本地文件列表,每一项是一个 File 对象| |tempFiles|Array<Object>、Array<File>|图片的本地文件列表,每一项是一个 File 对象|
**File 对象结构如下** **File 对象结构如下**
...@@ -38,6 +38,7 @@ App端如需要更丰富的相机拍照API(如直接调用前置摄像头) ...@@ -38,6 +38,7 @@ App端如需要更丰富的相机拍照API(如直接调用前置摄像头)
|path|String|本地文件路径| |path|String|本地文件路径|
|size|Number|本地文件大小,单位:B| |size|Number|本地文件大小,单位:B|
|name|String|包含扩展名的文件名称,仅H5支持| |name|String|包含扩展名的文件名称,仅H5支持|
|type|String|文件类型,仅H5支持|
**示例** **示例**
......
...@@ -21,14 +21,15 @@ ...@@ -21,14 +21,15 @@
**success 返回参数说明** **success 返回参数说明**
|参数|说明|平台差异说明说明| |参数|类型|说明|平台差异说明说明|
|:-|:-|:-| |:-|:-|:-|:-|
|tempFilePath|选定视频的临时文件路径|| |tempFilePath|String|选定视频的临时文件路径||
|duration|选定视频的时间长度,单位为 s|APP平台 2.1.0+、微信小程序| |tempFile|File|选定的视频文件|仅H5(2.7.0+)支持|
|size|选定视频的数据量大小|APP平台 2.1.0+、微信小程序| |duration|Number|选定视频的时间长度,单位为 s|APP 2.1.0+、H5、微信小程序|
|height|返回选定视频的高|APP平台 2.1.0+、微信小程序| |size|Number|选定视频的数据量大小|APP 2.1.0+、H5、微信小程序|
|width|返回选定视频的宽|APP平台 2.1.0+、微信小程序| |height|Number|返回选定视频的高|APP 2.1.0+、H5、微信小程序|
|name|包含扩展名的文件名称|仅H5支持| |width|Number|返回选定视频的宽|APP 2.1.0+、H5、微信小程序|
|name|String|包含扩展名的文件名称|仅H5支持|
**注意:** **注意:**
* 文件的临时路径,在应用本次启动期间可以正常使用,如需持久保存,需在主动调用 [uni.saveFile](api/file/file?id=savefile),在应用下次启动时才能访问得到。 * 文件的临时路径,在应用本次启动期间可以正常使用,如需持久保存,需在主动调用 [uni.saveFile](api/file/file?id=savefile),在应用下次启动时才能访问得到。
......
...@@ -9,8 +9,9 @@ ...@@ -9,8 +9,9 @@
|参数名|类型|必填|说明|平台差异说明| |参数名|类型|必填|说明|平台差异说明|
|:-|:-|:-|:-|:-| |:-|:-|:-|:-|:-|
|url|String|是|开发者服务器 url|| |url|String|是|开发者服务器 url||
|files|Array|否|需要上传的文件列表。**使用 files 时,filePath 和 name 不生效。**|App| |files|Array|否|需要上传的文件列表。**使用 files 时,filePath 和 name 不生效。**|App、H5( 2.7.0+)|
|fileType|String|见平台差异说明|文件类型,image/video/audio|仅支付宝小程序,且必填。| |fileType|String|见平台差异说明|文件类型,image/video/audio|仅支付宝小程序,且必填。|
|file|File|否|要上传的文件对象。|仅H5(2.7.0+)支持|
|filePath|String|是|要上传文件资源的路径。|| |filePath|String|是|要上传文件资源的路径。||
|name|String|是|文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容|| |name|String|是|文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容||
|header|Object|否|HTTP 请求 Header, header 中不能设置 Referer。|| |header|Object|否|HTTP 请求 Header, header 中不能设置 Referer。||
...@@ -33,6 +34,7 @@ files 参数是一个 file 对象的数组,file 对象的结构如下: ...@@ -33,6 +34,7 @@ files 参数是一个 file 对象的数组,file 对象的结构如下:
|参数名|类型|必填|说明| |参数名|类型|必填|说明|
|:-|:-|:-|:-| |:-|:-|:-|:-|
|name|String|否|multipart 提交时,表单的项目名,默认为 file| |name|String|否|multipart 提交时,表单的项目名,默认为 file|
|file|File|否|要上传的文件对象,仅H5(2.7.0+)支持|
|uri|String|是|文件的本地地址| |uri|String|是|文件的本地地址|
Tip: Tip:
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
|dataType|String|否|json |如果设为 json,会尝试对返回的数据做一次 JSON.parse|| |dataType|String|否|json |如果设为 json,会尝试对返回的数据做一次 JSON.parse||
|responseType|String|否|text |设置响应的数据类型。合法值:text、arraybuffer|App和支付宝小程序不支持| |responseType|String|否|text |设置响应的数据类型。合法值:text、arraybuffer|App和支付宝小程序不支持|
|sslVerify|Boolean|否|true|验证 ssl 证书|仅App安卓端支持(HBuilderX 2.3.3+)| |sslVerify|Boolean|否|true|验证 ssl 证书|仅App安卓端支持(HBuilderX 2.3.3+)|
|withCredentials|Boolean|否|false|跨域请求时是否携带凭证(cookies)|仅H5支持(HBuilderX 2.7.0+)|
|success|Function|否||收到开发者服务成功返回的回调函数|| |success|Function|否||收到开发者服务成功返回的回调函数||
|fail|Function|否||接口调用失败的回调函数|| |fail|Function|否||接口调用失败的回调函数||
|complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)| | |complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)| |
......
...@@ -231,13 +231,13 @@ const promiseInterceptor = { ...@@ -231,13 +231,13 @@ const promiseInterceptor = {
}; };
const SYNC_API_RE = const SYNC_API_RE =
/^\$|restoreGlobal|getCurrentSubNVue|getMenuButtonBoundingClientRect|^report|interceptors|Interceptor$|getSubNVueById|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$|base64ToArrayBuffer|arrayBufferToBase64/; /^\$|sendNativeEvent|restoreGlobal|getCurrentSubNVue|getMenuButtonBoundingClientRect|^report|interceptors|Interceptor$|getSubNVueById|requireNativePlugin|upx2px|hideKeyboard|canIUse|^create|Sync$|Manager$|base64ToArrayBuffer|arrayBufferToBase64/;
const CONTEXT_API_RE = /^create|Manager$/; const CONTEXT_API_RE = /^create|Manager$/;
const ASYNC_API = ['createBLEConnection']; const ASYNC_API = ['createBLEConnection'];
const CALLBACK_API_RE = /^on/; const CALLBACK_API_RE = /^on|^off/;
function isContextApi (name) { function isContextApi (name) {
return CONTEXT_API_RE.test(name) return CONTEXT_API_RE.test(name)
...@@ -1699,6 +1699,17 @@ function parsePage (vuePageOptions) { ...@@ -1699,6 +1699,17 @@ function parsePage (vuePageOptions) {
} else { } else {
this.is && console.warn(this.is + ' is not ready'); this.is && console.warn(this.is + ' is not ready');
} }
};
pageOptions.lifetimes.detached = function detached () {
this.$vm && this.$vm.$destroy();
// 清理
const webviewId = this.__webviewId__;
webviewId && Object.keys(instances).forEach(key => {
if (key.indexOf(webviewId + '_') === 0) {
delete instances[key];
}
});
}; };
return pageOptions return pageOptions
......
function typof (v) { function typof (v) {
var s = Object.prototype.toString.call(v) var s = Object.prototype.toString.call(v)
return s.substring(8, s.length - 1) return s.substring(8, s.length - 1)
} }
function isDebugMode () { function isDebugMode () {
/* eslint-disable no-undef */ /* eslint-disable no-undef */
return typeof __channelId__ === 'string' && __channelId__ return typeof __channelId__ === 'string' && __channelId__
} }
export function log (type) { export function log (type) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key] args[_key - 1] = arguments[_key]
} }
console[type].apply(console, args) console[type].apply(console, args)
} }
export default function formatLog () { export default function formatLog () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key] args[_key] = arguments[_key]
} }
var type = args.shift() var type = args.shift()
if (isDebugMode()) { if (isDebugMode()) {
args.push(args.pop().replace('at ', 'uni-app:///')) args.push(args.pop().replace('at ', 'uni-app:///'))
return console[type]['apply'](console, args) return console[type]['apply'](console, args)
} }
var msgs = args.map(function (v) { var msgs = args.map(function (v) {
var type = Object.prototype.toString.call(v).toLowerCase() var type = Object.prototype.toString.call(v).toLowerCase()
if (type === '[object object]' || type === '[object array]') { if (type === '[object object]' || type === '[object array]') {
try { try {
v = '---BEGIN:JSON---' + JSON.stringify(v) + '---END:JSON---' v = '---BEGIN:JSON---' + JSON.stringify(v) + '---END:JSON---'
} catch (e) { } catch (e) {
v = '[object object]' v = '[object object]'
} }
} else { } else {
if (v === null) { if (v === null) {
v = '---NULL---' v = '---NULL---'
} else if (v === undefined) { } else if (v === undefined) {
v = '---UNDEFINED---' v = '---UNDEFINED---'
} else { } else {
var vType = typof(v).toUpperCase() var vType = typof(v).toUpperCase()
if (vType === 'NUMBER' || vType === 'BOOLEAN') { if (vType === 'NUMBER' || vType === 'BOOLEAN') {
v = '---BEGIN:' + vType + '---' + v + '---END:' + vType + '---' v = '---BEGIN:' + vType + '---' + v + '---END:' + vType + '---'
} else { } else {
v = String(v) v = String(v)
} }
} }
} }
return v return v
}) })
var msg = '' var msg = ''
if (msgs.length > 1) { if (msgs.length > 1) {
var lastMsg = msgs.pop() var lastMsg = msgs.pop()
msg = msgs.join('---COMMA---') msg = msgs.join('---COMMA---')
if (lastMsg.indexOf(' at ') === 0) { if (lastMsg.indexOf(' at ') === 0) {
msg += lastMsg msg += lastMsg
} else { } else {
msg += '---COMMA---' + lastMsg msg += '---COMMA---' + lastMsg
} }
} else { } else {
msg = msgs[0] msg = msgs[0]
} }
console[type](msg) console[type](msg)
} }
...@@ -75,7 +75,10 @@ module.exports = function initOptions (options) { ...@@ -75,7 +75,10 @@ module.exports = function initOptions (options) {
options.css.loaderOptions.sass.sassOptions = {} options.css.loaderOptions.sass.sassOptions = {}
} }
// 指定 outputStyle, 否则 production 模式下会被默认成 compressed // 指定 outputStyle, 否则 production 模式下会被默认成 compressed
options.css.loaderOptions.sass.sassOptions.outputStyle = 'nested' const outputStyle = options.css.loaderOptions.sass.sassOptions.outputStyle
if (!outputStyle || outputStyle === 'compressed') {
options.css.loaderOptions.sass.sassOptions.outputStyle = 'expanded'
}
if (sassLoaderVersion < 8) { if (sassLoaderVersion < 8) {
options.css.loaderOptions.sass.data = sassData options.css.loaderOptions.sass.data = sassData
......
...@@ -26,6 +26,31 @@ function parseRoutes (config) { ...@@ -26,6 +26,31 @@ function parseRoutes (config) {
return __uniRoutes return __uniRoutes
} }
const GLOBALS = [
'global',
'window',
'document',
'frames',
'self',
'location',
'navigator',
'localStorage',
'history',
'Caches',
'screen',
'alert',
'confirm',
'prompt',
'fetch',
'XMLHttpRequest',
'WebSocket',
'webkit',
'print'
]
const globalStatement = GLOBALS.map(g => `${g}:void 0`).join(',')
module.exports = function definePages (appJson) { module.exports = function definePages (appJson) {
const __uniRoutes = parseRoutes(appJson) const __uniRoutes = parseRoutes(appJson)
...@@ -42,7 +67,7 @@ var isReady=false;var onReadyCallbacks=[]; ...@@ -42,7 +67,7 @@ var isReady=false;var onReadyCallbacks=[];
var __uniConfig = ${JSON.stringify(appJson, null)}; var __uniConfig = ${JSON.stringify(appJson, null)};
var __uniRoutes = ${JSON.stringify(__uniRoutes)}; var __uniRoutes = ${JSON.stringify(__uniRoutes)};
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}}); __uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:Math.round(f/20)})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,window:void 0,global:void 0}}}}); service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:Math.round(f/20)})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,${globalStatement}}}}});
` `
} }
} }
...@@ -355,7 +355,8 @@ import Vue from 'vue' ...@@ -355,7 +355,8 @@ import Vue from 'vue'
global['____${h5.appid}____'] = true; global['____${h5.appid}____'] = true;
delete global['____${h5.appid}____']; delete global['____${h5.appid}____'];
global.__uniConfig = ${JSON.stringify(pagesJson)}; global.__uniConfig = ${JSON.stringify(pagesJson)};
global.__uniConfig.router = ${JSON.stringify(h5.router)}; global.__uniConfig.router = ${JSON.stringify(h5.router)};
global.__uniConfig.publicPath = ${JSON.stringify(h5.publicPath)};
global.__uniConfig['async'] = ${JSON.stringify(h5['async'])}; global.__uniConfig['async'] = ${JSON.stringify(h5['async'])};
global.__uniConfig.debug = ${manifestJson.debug === true}; global.__uniConfig.debug = ${manifestJson.debug === true};
global.__uniConfig.networkTimeout = ${JSON.stringify(networkTimeoutConfig)}; global.__uniConfig.networkTimeout = ${JSON.stringify(networkTimeoutConfig)};
......
...@@ -100,5 +100,8 @@ export const request = { ...@@ -100,5 +100,8 @@ export const request = {
value = (value || '').toLowerCase() value = (value || '').toLowerCase()
params.responseType = Object.values(responseType).indexOf(value) < 0 ? responseType.TEXT : value params.responseType = Object.values(responseType).indexOf(value) < 0 ? responseType.TEXT : value
} }
},
withCredentials: {
type: Boolean
} }
} }
...@@ -9,6 +9,9 @@ export const uploadFile = { ...@@ -9,6 +9,9 @@ export const uploadFile = {
files: { files: {
type: Array type: Array
}, },
file: {
type: File
},
filePath: { filePath: {
type: String, type: String,
validator (value, params) { validator (value, params) {
......
...@@ -60,7 +60,7 @@ class SocketTask { ...@@ -60,7 +60,7 @@ class SocketTask {
success, success,
fail, fail,
complete complete
}, errMsg) { } = {}, errMsg) {
var data = { var data = {
errMsg errMsg
} }
......
...@@ -9,13 +9,13 @@ export default function initSubscribe (subscribe, { ...@@ -9,13 +9,13 @@ export default function initSubscribe (subscribe, {
getCurrentPages getCurrentPages
}) { }) {
function createPageEvent (eventType) { function createPageEvent (eventType) {
return function (args, pageId) { return function (args, pageId) {
pageId = parseInt(pageId) pageId = parseInt(pageId)
const pages = getCurrentPages() const pages = getCurrentPages()
const page = pages.find(page => page.$page.id === pageId) const page = pages.find(page => page.$page.id === pageId)
if (page) { if (page) {
callPageHook(page, eventType, args) callPageHook(page, eventType, args)
} else { } else if (process.env.NODE_ENV !== 'production') {
console.error(`Not Found:Page[${pageId}]`) console.error(`Not Found:Page[${pageId}]`)
} }
} }
...@@ -48,7 +48,7 @@ export default function initSubscribe (subscribe, { ...@@ -48,7 +48,7 @@ export default function initSubscribe (subscribe, {
} }
callback(res) callback(res)
} }
} }
if (__PLATFORM__ === 'h5') { if (__PLATFORM__ === 'h5') {
subscribe('onPageReady', createPageEvent('onReady')) subscribe('onPageReady', createPageEvent('onReady'))
...@@ -59,4 +59,4 @@ export default function initSubscribe (subscribe, { ...@@ -59,4 +59,4 @@ export default function initSubscribe (subscribe, {
subscribe('onRequestComponentInfo', onRequestComponentInfo) subscribe('onRequestComponentInfo', onRequestComponentInfo)
subscribe('onRequestComponentObserver', onRequestComponentObserver) subscribe('onRequestComponentObserver', onRequestComponentObserver)
} }
...@@ -48,7 +48,7 @@ const initStateChage = audioId => { ...@@ -48,7 +48,7 @@ const initStateChage = audioId => {
export function createAudioInstance () { export function createAudioInstance () {
const audioId = `${Date.now()}${Math.random()}` const audioId = `${Date.now()}${Math.random()}`
const audio = audios[audioId] = plus.audio.createPlayer('') const audio = audios[audioId] = plus.audio.createPlayer('')
audio.src = '' audio.src = ''
audio.volume = 1 audio.volume = 1
audio.startTime = 0 audio.startTime = 0
...@@ -122,7 +122,7 @@ export function getAudioState ({ ...@@ -122,7 +122,7 @@ export function getAudioState ({
errMsg: 'getAudioState:ok', errMsg: 'getAudioState:ok',
duration: 1e3 * (audio.getDuration() || 0), duration: 1e3 * (audio.getDuration() || 0),
currentTime: audio.isStopped ? 0 : 1e3 * audio.getPosition(), currentTime: audio.isStopped ? 0 : 1e3 * audio.getPosition(),
paused: audio.isPaused, paused: audio.isPaused(),
src, src,
volume, volume,
startTime: 1e3 * startTime, startTime: 1e3 * startTime,
...@@ -138,7 +138,7 @@ export function operateAudio ({ ...@@ -138,7 +138,7 @@ export function operateAudio ({
const audio = audios[audioId] const audio = audios[audioId]
const operationTypes = ['play', 'pause', 'stop'] const operationTypes = ['play', 'pause', 'stop']
if (operationTypes.indexOf(operationType) >= 0) { if (operationTypes.indexOf(operationType) >= 0) {
audio[operationType === operationTypes[0] && audio.isPaused ? 'resume' : operationType]() audio[operationType === operationTypes[0] && audio.isPaused() ? 'resume' : operationType]()
} else if (operationType === 'seek') { } else if (operationType === 'seek') {
audio.seekTo(currentTime / 1e3) audio.seekTo(currentTime / 1e3)
} }
......
...@@ -6,9 +6,9 @@ import { ...@@ -6,9 +6,9 @@ import {
publish publish
} from '../../bridge' } from '../../bridge'
let audio let audio
let timeUpdateTimer = null let timeUpdateTimer = null
const TIME_UPDATE = 250 const TIME_UPDATE = 250
const publishBackgroundAudioStateChange = (state, res = {}) => publish('onBackgroundAudioStateChange', Object.assign({ const publishBackgroundAudioStateChange = (state, res = {}) => publish('onBackgroundAudioStateChange', Object.assign({
...@@ -31,15 +31,15 @@ function initMusic () { ...@@ -31,15 +31,15 @@ function initMusic () {
audio.addEventListener(event, () => { audio.addEventListener(event, () => {
// 添加 isStopped 属性是为了解决 安卓设备停止播放后获取播放进度不正确的问题 // 添加 isStopped 属性是为了解决 安卓设备停止播放后获取播放进度不正确的问题
if (event === 'play') { if (event === 'play') {
audio.isStopped = false audio.isStopped = false
startTimeUpdateTimer() startTimeUpdateTimer()
} else if (event === 'stop') { } else if (event === 'stop') {
audio.isStopped = true audio.isStopped = true
} }
if (event === 'pause' || event === 'ended' || event === 'stop') { if (event === 'pause' || event === 'ended' || event === 'stop') {
stopTimeUpdateTimer() stopTimeUpdateTimer()
} }
const eventName = `onMusic${event[0].toUpperCase() + event.substr(1)}` const eventName = `onMusic${event[0].toUpperCase() + event.substr(1)}`
publish(eventName, { publish(eventName, {
...@@ -51,13 +51,13 @@ function initMusic () { ...@@ -51,13 +51,13 @@ function initMusic () {
}) })
}) })
}) })
audio.addEventListener('waiting', () => { audio.addEventListener('waiting', () => {
stopTimeUpdateTimer() stopTimeUpdateTimer()
publishBackgroundAudioStateChange('waiting', { publishBackgroundAudioStateChange('waiting', {
dataUrl: audio.src dataUrl: audio.src
}) })
}) })
audio.addEventListener('error', err => { audio.addEventListener('error', err => {
stopTimeUpdateTimer() stopTimeUpdateTimer()
publish('onMusicError', { publish('onMusicError', {
dataUrl: audio.src, dataUrl: audio.src,
...@@ -71,20 +71,20 @@ function initMusic () { ...@@ -71,20 +71,20 @@ function initMusic () {
}) })
audio.addEventListener('prev', () => publish('onBackgroundAudioPrev')) audio.addEventListener('prev', () => publish('onBackgroundAudioPrev'))
audio.addEventListener('next', () => publish('onBackgroundAudioNext')) audio.addEventListener('next', () => publish('onBackgroundAudioNext'))
} }
function startTimeUpdateTimer () {
stopTimeUpdateTimer()
timeUpdateTimer = setInterval(() => {
publishBackgroundAudioStateChange('timeUpdate', {})
}, TIME_UPDATE)
}
function stopTimeUpdateTimer () { function startTimeUpdateTimer () {
if (timeUpdateTimer !== null) { stopTimeUpdateTimer()
clearInterval(timeUpdateTimer) timeUpdateTimer = setInterval(() => {
} publishBackgroundAudioStateChange('timeUpdate', {})
} }, TIME_UPDATE)
}
function stopTimeUpdateTimer () {
if (timeUpdateTimer !== null) {
clearInterval(timeUpdateTimer)
}
}
function setMusicState (args) { function setMusicState (args) {
initMusic() initMusic()
...@@ -113,7 +113,7 @@ export function getMusicPlayerState () { ...@@ -113,7 +113,7 @@ export function getMusicPlayerState () {
dataUrl: audio.src, dataUrl: audio.src,
duration: audio.getDuration() || 0, duration: audio.getDuration() || 0,
currentPosition: audio.getPosition(), currentPosition: audio.getPosition(),
status: audio.isPaused ? 0 : 1, status: audio.isPaused() ? 0 : 1,
downloadPercent: Math.round(100 * audio.getBuffered() / audio.getDuration()), downloadPercent: Math.round(100 * audio.getBuffered() / audio.getDuration()),
errMsg: `getMusicPlayerState:ok` errMsg: `getMusicPlayerState:ok`
} }
...@@ -189,7 +189,7 @@ export function getBackgroundAudioState () { ...@@ -189,7 +189,7 @@ export function getBackgroundAudioState () {
let newData = { let newData = {
duration: audio.getDuration() || 0, duration: audio.getDuration() || 0,
currentTime: audio.isStopped ? 0 : audio.getPosition(), currentTime: audio.isStopped ? 0 : audio.getPosition(),
paused: audio.isPaused, paused: audio.isPaused(),
src: audio.src, src: audio.src,
buffered: audio.getBuffered(), buffered: audio.getBuffered(),
title: audio.title, title: audio.title,
......
...@@ -52,8 +52,7 @@ function diffElmData (newObj, oldObj) { ...@@ -52,8 +52,7 @@ function diffElmData (newObj, oldObj) {
cur = newObj[key] cur = newObj[key]
old = oldObj[key] old = oldObj[key]
if (old !== cur) { if (old !== cur) {
// 全量同步 style (因为 style 可能会动态删除部分样式) if (key === B_STYLE && isPlainObject(cur) && isPlainObject(old)) { // 全量同步 style (因为 style 可能会动态删除部分样式)
if (key === B_STYLE && isPlainObject(cur) && isPlainObject(old)) {
if (Object.keys(cur).length !== Object.keys(old).length) { // 长度不等 if (Object.keys(cur).length !== Object.keys(old).length) { // 长度不等
setResult(result || (result = Object.create(null)), B_STYLE, cur) setResult(result || (result = Object.create(null)), B_STYLE, cur)
} else { } else {
...@@ -64,6 +63,14 @@ function diffElmData (newObj, oldObj) { ...@@ -64,6 +63,14 @@ function diffElmData (newObj, oldObj) {
const vFor = diffArray(cur, old) const vFor = diffArray(cur, old)
vFor && setResult(result || (result = Object.create(null)), V_FOR, vFor) vFor && setResult(result || (result = Object.create(null)), V_FOR, vFor)
} else { } else {
if (key.indexOf('change:') === 0) { // wxs change:prop
try {
// 先简单的用 stringify 判断
if (JSON.stringify(cur) === JSON.stringify(old)) {
continue
}
} catch (e) {}
}
setResult(result || (result = Object.create(null)), key, cur) setResult(result || (result = Object.create(null)), key, cur)
} }
} }
......
...@@ -61,3 +61,8 @@ export function fileToUrl (file) { ...@@ -61,3 +61,8 @@ export function fileToUrl (file) {
files[url] = file files[url] = file
return url return url
} }
export function revokeObjectURL (url) {
(window.URL || window.webkitURL).revokeObjectURL(url)
delete files[url]
}
...@@ -50,26 +50,27 @@ export function chooseImage ({ ...@@ -50,26 +50,27 @@ export function chooseImage ({
document.body.appendChild(imageInput) document.body.appendChild(imageInput)
imageInput.addEventListener('change', function (event) { imageInput.addEventListener('change', function (event) {
const tempFilePaths = []
const tempFiles = [] const tempFiles = []
const fileCount = event.target.files.length const fileCount = event.target.files.length
for (let i = 0; i < fileCount; i++) { for (let i = 0; i < fileCount; i++) {
const file = event.target.files[i] const file = event.target.files[i]
const filePath = fileToUrl(file) let filePath
Object.defineProperty(file, 'filePath', {
tempFilePaths.push(filePath) get () {
tempFiles.push({ filePath = filePath || fileToUrl(file)
path: filePath, return filePath
size: file.size, }
name: file.name
}) })
tempFiles.push(file)
} }
const res = {
invoke(callbackId, {
errMsg: 'chooseImage:ok', errMsg: 'chooseImage:ok',
tempFilePaths: tempFilePaths, get tempFilePaths () {
return tempFiles.map(({ filePath }) => filePath)
},
tempFiles: tempFiles tempFiles: tempFiles
}) }
invoke(callbackId, res)
// TODO 用户取消选择时,触发 fail,目前尚未找到合适的方法。 // TODO 用户取消选择时,触发 fail,目前尚未找到合适的方法。
}) })
......
import { fileToUrl } from 'uni-platform/helpers/file' import { fileToUrl, revokeObjectURL } from 'uni-platform/helpers/file'
import { updateElementStyle } from 'uni-shared' import { updateElementStyle } from 'uni-shared'
const { const {
...@@ -42,23 +42,30 @@ export function chooseVideo ({ ...@@ -42,23 +42,30 @@ export function chooseVideo ({
videoInput.addEventListener('change', function (event) { videoInput.addEventListener('change', function (event) {
const file = event.target.files[0] const file = event.target.files[0]
const filePath = fileToUrl(file) const callbackResult = {
let callbackResult = {
errMsg: 'chooseVideo:ok', errMsg: 'chooseVideo:ok',
tempFilePath: filePath, tempFile: file,
size: file.size, size: file.size,
duration: 0, duration: 0,
width: 0, width: 0,
height: 0, height: 0,
name: file.name name: file.name
} }
let filePath
Object.defineProperty(callbackResult, 'tempFilePath', {
get () {
filePath = filePath || fileToUrl(this.tempFile)
return filePath
}
})
const video = document.createElement('video') const video = document.createElement('video')
if (video.onloadedmetadata !== undefined) { if (video.onloadedmetadata !== undefined) {
const filePath = fileToUrl(file)
// 尝试获取视频的宽高信息 // 尝试获取视频的宽高信息
video.onloadedmetadata = function () { video.onloadedmetadata = function () {
invoke(callbackId, Object.assign({}, callbackResult, { revokeObjectURL(filePath)
invoke(callbackId, Object.assign(callbackResult, {
duration: video.duration || 0, duration: video.duration || 0,
width: video.videoWidth || 0, width: video.videoWidth || 0,
height: video.videoHeight || 0 height: video.videoHeight || 0
...@@ -66,11 +73,9 @@ export function chooseVideo ({ ...@@ -66,11 +73,9 @@ export function chooseVideo ({
} }
// 部分浏览器(如微信内置浏览器)未播放无法触发loadedmetadata事件 // 部分浏览器(如微信内置浏览器)未播放无法触发loadedmetadata事件
setTimeout(() => { setTimeout(() => {
invoke(callbackId, Object.assign({}, callbackResult, { video.onloadedmetadata = null
duration: 0, revokeObjectURL(filePath)
width: 0, invoke(callbackId, callbackResult)
height: 0
}))
}, 300) }, 300)
video.src = filePath video.src = filePath
} else { } else {
......
...@@ -45,7 +45,8 @@ export function request ({ ...@@ -45,7 +45,8 @@ export function request ({
header, header,
method, method,
dataType, dataType,
responseType responseType,
withCredentials
}, callbackId) { }, callbackId) {
const { const {
invokeCallbackHandler: invoke invokeCallbackHandler: invoke
...@@ -143,6 +144,7 @@ export function request ({ ...@@ -143,6 +144,7 @@ export function request ({
errMsg: 'request:fail' errMsg: 'request:fail'
}) })
} }
xhr.withCredentials = withCredentials
xhr.send(body) xhr.send(body)
return requestTask return requestTask
} }
...@@ -45,8 +45,10 @@ class UploadTask { ...@@ -45,8 +45,10 @@ class UploadTask {
*/ */
export function uploadFile ({ export function uploadFile ({
url, url,
file,
filePath, filePath,
name, name,
files,
header, header,
formData formData
}, callbackId) { }, callbackId) {
...@@ -55,15 +57,24 @@ export function uploadFile ({ ...@@ -55,15 +57,24 @@ export function uploadFile ({
invokeCallbackHandler: invoke invokeCallbackHandler: invoke
} = UniServiceJSBridge } = UniServiceJSBridge
var uploadTask = new UploadTask(null, callbackId) var uploadTask = new UploadTask(null, callbackId)
if (!Array.isArray(files) || !files.length) {
function upload (file) { files = [{
name,
file,
uri: filePath
}]
}
function upload (realFiles) {
var xhr = new XMLHttpRequest() var xhr = new XMLHttpRequest()
var form = new FormData() var form = new FormData()
var timer var timer
Object.keys(formData).forEach(key => { Object.keys(formData).forEach(key => {
form.append(key, formData[key]) form.append(key, formData[key])
}) })
form.append(name, file, file.name || `file-${Date.now()}`) Object.values(files).forEach(({ name }, index) => {
const file = realFiles[index]
form.append(name || 'file', file, file.name || `file-${Date.now()}`)
})
xhr.open('POST', url) xhr.open('POST', url)
Object.keys(header).forEach(key => { Object.keys(header).forEach(key => {
xhr.setRequestHeader(key, header[key]) xhr.setRequestHeader(key, header[key])
...@@ -118,13 +129,16 @@ export function uploadFile ({ ...@@ -118,13 +129,16 @@ export function uploadFile ({
} }
} }
urlToFile(filePath).then(upload).catch(() => { Promise
setTimeout(() => { .all(files.map(({ file, uri }) => file instanceof File ? Promise.resolve(file) : urlToFile(uri)))
invoke(callbackId, { .then(upload)
errMsg: 'uploadFile:fail file error' .catch(() => {
}) setTimeout(() => {
}, 0) invoke(callbackId, {
}) errMsg: 'uploadFile:fail file error'
})
}, 0)
})
return uploadTask return uploadTask
} }
import { import {
isPage, isPage,
instances,
initRelation initRelation
} from './util' } from './util'
...@@ -21,6 +22,17 @@ export default function parsePage (vuePageOptions) { ...@@ -21,6 +22,17 @@ export default function parsePage (vuePageOptions) {
} else { } else {
this.is && console.warn(this.is + ' is not ready') this.is && console.warn(this.is + ' is not ready')
} }
}
pageOptions.lifetimes.detached = function detached () {
this.$vm && this.$vm.$destroy()
// 清理
const webviewId = this.__webviewId__
webviewId && Object.keys(instances).forEach(key => {
if (key.indexOf(webviewId + '_') === 0) {
delete instances[key]
}
})
} }
return pageOptions return pageOptions
......
...@@ -51,7 +51,7 @@ export function initRefs (vm) { ...@@ -51,7 +51,7 @@ export function initRefs (vm) {
} }
} }
const instances = Object.create(null) export const instances = Object.create(null)
export function initRelation ({ export function initRelation ({
vuePid, vuePid,
...@@ -101,4 +101,4 @@ export function handleLink ({ ...@@ -101,4 +101,4 @@ export function handleLink ({
vm._isMounted = true vm._isMounted = true
vm.__call_hook('mounted') vm.__call_hook('mounted')
vm.__call_hook('onReady') vm.__call_hook('onReady')
} }
...@@ -78,7 +78,8 @@ src ...@@ -78,7 +78,8 @@ src
#### 开发示例 #### 开发示例
- button 组件 [https://github.com/dcloudio/uni-app/tree/master/src/platforms/quickapp/view/components/button](https://github.com/dcloudio/uni-app/tree/master/src/platforms/quickapp/view/components/button) - button 组件 `src/platforms/quickapp/view/components/button`
- clipboard API `src/platforms/quickapp/service/api/device/clipboard`
......
import clipboard from '@system.clipboard'
import {
invoke
} from '../../bridge'
export function getClipboardData (options, callbackId) {
clipboard.get({
success: (ret) => {
invoke(callbackId, {
data: ret.text,
errMsg: 'getClipboardData:ok'
})
},
fail: (data, code) => {
invoke(callbackId, {
data: code,
errMsg: 'getClipboardData:fail'
})
}
})
}
export function setClipboardData ({
data
}) {
clipboard.set({
text: data
})
return {
errMsg: 'setClipboardData:ok'
}
}
export * from './api/route/navigate-back' export * from './api/route/navigate-back'
export * from './api/route/navigate-to' export * from './api/route/navigate-to'
export * from './api/route/redirect-to' export * from './api/route/redirect-to'
// device
export * from './api/device/clipboard'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册