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

feat(app): add switchTab

上级 50dc7df0
...@@ -2,6 +2,7 @@ import { UniLifecycleHooks } from '@dcloudio/uni-vue/src/apiLifecycle' ...@@ -2,6 +2,7 @@ import { UniLifecycleHooks } from '@dcloudio/uni-vue/src/apiLifecycle'
import { ComponentCustomProperties, ComponentInternalInstance } from 'vue' import { ComponentCustomProperties, ComponentInternalInstance } from 'vue'
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface ComponentCustomProperties { interface ComponentCustomProperties {
route: string
$scope: { $scope: {
$getAppWebview?: () => PlusWebviewWebviewObject $getAppWebview?: () => PlusWebviewWebviewObject
} }
......
...@@ -131,11 +131,14 @@ function wrapperOffApi<T extends ApiLike>( ...@@ -131,11 +131,14 @@ function wrapperOffApi<T extends ApiLike>(
} }
function normalizeErrMsg(errMsg: string | Error) { function normalizeErrMsg(errMsg: string | Error) {
if (errMsg instanceof Error) { if (isString(errMsg)) {
console.error(errMsg) return errMsg
}
if (errMsg.stack) {
console.error(errMsg.message + '\n' + errMsg.stack)
return errMsg.message return errMsg.message
} }
return errMsg return errMsg as unknown as string
} }
function wrapperTaskApi<T extends ApiLike>( function wrapperTaskApi<T extends ApiLike>(
......
...@@ -525,8 +525,11 @@ var serviceContext = (function (vue) { ...@@ -525,8 +525,11 @@ var serviceContext = (function (vue) {
}; };
} }
function normalizeErrMsg(errMsg) { function normalizeErrMsg(errMsg) {
if (errMsg instanceof Error) { if (isString(errMsg)) {
console.error(errMsg); return errMsg;
}
if (errMsg.stack) {
console.error(errMsg.message + '\n' + errMsg.stack);
return errMsg.message; return errMsg.message;
} }
return errMsg; return errMsg;
...@@ -1801,6 +1804,7 @@ var serviceContext = (function (vue) { ...@@ -1801,6 +1804,7 @@ var serviceContext = (function (vue) {
appVm.$mpType = 'app'; appVm.$mpType = 'app';
} }
function initPageVm(pageVm, page) { function initPageVm(pageVm, page) {
pageVm.route = page.route;
pageVm.$vm = pageVm; pageVm.$vm = pageVm;
pageVm.$page = page; pageVm.$page = page;
pageVm.$mpType = 'page'; pageVm.$mpType = 'page';
...@@ -4420,12 +4424,15 @@ var serviceContext = (function (vue) { ...@@ -4420,12 +4424,15 @@ var serviceContext = (function (vue) {
}, createAnimationProtocol(ANIMATION_OUT)); }, createAnimationProtocol(ANIMATION_OUT));
const RedirectToProtocol = BaseRouteProtocol; const RedirectToProtocol = BaseRouteProtocol;
const ReLaunchProtocol = BaseRouteProtocol; const ReLaunchProtocol = BaseRouteProtocol;
const SwitchTabProtocol = BaseRouteProtocol;
const NavigateToOptions = const NavigateToOptions =
/*#__PURE__*/ createRouteOptions(API_NAVIGATE_TO); /*#__PURE__*/ createRouteOptions(API_NAVIGATE_TO);
const RedirectToOptions = const RedirectToOptions =
/*#__PURE__*/ createRouteOptions(API_REDIRECT_TO); /*#__PURE__*/ createRouteOptions(API_REDIRECT_TO);
const ReLaunchOptions = const ReLaunchOptions =
/*#__PURE__*/ createRouteOptions(API_RE_LAUNCH); /*#__PURE__*/ createRouteOptions(API_RE_LAUNCH);
const SwitchTabOptions =
/*#__PURE__*/ createRouteOptions(API_SWITCH_TAB);
const NavigateBackOptions = { const NavigateBackOptions = {
formatArgs: { formatArgs: {
delta(value, params) { delta(value, params) {
...@@ -9068,6 +9075,10 @@ var serviceContext = (function (vue) { ...@@ -9068,6 +9075,10 @@ var serviceContext = (function (vue) {
console.log(formatLog('setPendingNavigator', path, msg)); console.log(formatLog('setPendingNavigator', path, msg));
} }
} }
function closePage(page, animationType, animationDuration) {
removePage(page);
closeWebview(page.$getAppWebview(), animationType, animationDuration);
}
function navigate(path, callback, isAppLaunch = false) { function navigate(path, callback, isAppLaunch = false) {
if (!isAppLaunch && pendingNavigator) { if (!isAppLaunch && pendingNavigator) {
return console.error(`Waiting to navigate to: ${pendingNavigator.path}, do not operate continuously: ${path}.`); return console.error(`Waiting to navigate to: ${pendingNavigator.path}, do not operate continuously: ${path}.`);
...@@ -9757,16 +9768,111 @@ var serviceContext = (function (vue) { ...@@ -9757,16 +9768,111 @@ var serviceContext = (function (vue) {
query, query,
openType: 'reLaunch', openType: 'reLaunch',
}), 'none', 0, () => { }), 'none', 0, () => {
pages.forEach((page) => { pages.forEach((page) => closePage(page, 'none'));
removePage(page);
closeWebview(page.$getAppWebview(), 'none');
});
resolve(undefined); resolve(undefined);
}); });
setStatusBarStyle(); setStatusBarStyle();
}); });
} }
const switchTab = defineAsyncApi(API_SWITCH_TAB, (args, { resolve, reject }) => {
const { url } = args;
const { path, query } = parseUrl(url);
navigate(path, () => {
_switchTab({
url,
path,
query,
})
.then(resolve)
.catch(reject);
}, args.openType === 'appLaunch');
}, SwitchTabProtocol, SwitchTabOptions);
function _switchTab({ url, path, query, }) {
tabBar$1.switchTab(path.slice(1));
const pages = getCurrentPages();
const len = pages.length;
let callOnHide = false;
let callOnShow = false;
let currentPage;
if (len >= 1) {
// 前一个页面是非 tabBar 页面
currentPage = pages[len - 1];
if (currentPage && !currentPage.__isTabBar) {
// 前一个页面为非 tabBar 页面时,目标tabBar需要强制触发onShow
// 该情况下目标页tabBarPage的visible是不对的
// 除非每次路由跳转都处理一遍tabBarPage的visible,目前仅switchTab会处理
// 简单起见,暂时直接判断该情况,执行onShow
callOnShow = true;
pages.reverse().forEach((page) => {
if (!page.__isTabBar && page !== currentPage) {
closePage(page, 'none');
}
});
removePage(currentPage);
// 延迟执行避免iOS应用退出
setTimeout(() => {
if (currentPage.$page.openType === 'redirectTo') {
closeWebview(currentPage.$getAppWebview(), ANI_CLOSE, ANI_DURATION);
}
else {
closeWebview(currentPage.$getAppWebview(), 'auto');
}
}, 100);
}
else {
callOnHide = true;
}
}
let tabBarPage;
// 查找当前 tabBarPage,且设置 visible
getAllPages().forEach((page) => {
if ('/' + page.route === path) {
if (!page.$.__isActive) {
// 之前未显示
callOnShow = true;
}
page.$.__isActive = true;
tabBarPage = page;
}
else {
if (page.__isTabBar) {
page.$.__isActive = false;
}
}
});
// 相同tabBar页面
if (currentPage === tabBarPage) {
callOnHide = false;
}
if (currentPage && callOnHide) {
invokeHook(currentPage, 'onHide');
}
return new Promise((resolve) => {
if (tabBarPage) {
const webview = tabBarPage.$getAppWebview();
webview.show('none');
// 等visible状态都切换完之后,再触发onShow,否则开发者在onShow里边 getCurrentPages 会不准确
if (callOnShow && !webview.__preload__) {
invokeHook(tabBarPage, 'onShow');
}
resolve(undefined);
}
else {
showWebview(registerPage({
url,
path,
query,
openType: 'switchTab',
}), 'none', 0, () => {
setStatusBarStyle();
resolve(undefined);
}, 70);
}
setStatusBarStyle();
});
}
var uni$1 = /*#__PURE__*/Object.freeze({ var uni$1 = /*#__PURE__*/Object.freeze({
__proto__: null, __proto__: null,
upx2px: upx2px, upx2px: upx2px,
...@@ -9897,7 +10003,8 @@ var serviceContext = (function (vue) { ...@@ -9897,7 +10003,8 @@ var serviceContext = (function (vue) {
navigateBack: navigateBack, navigateBack: navigateBack,
navigateTo: navigateTo, navigateTo: navigateTo,
redirectTo: redirectTo, redirectTo: redirectTo,
reLaunch: reLaunch reLaunch: reLaunch,
switchTab: switchTab
}); });
let invokeViewMethodId = 0; let invokeViewMethodId = 0;
......
...@@ -56,6 +56,7 @@ export * from './route/navigateBack' ...@@ -56,6 +56,7 @@ export * from './route/navigateBack'
export * from './route/navigateTo' export * from './route/navigateTo'
export * from './route/redirectTo' export * from './route/redirectTo'
export * from './route/reLaunch' export * from './route/reLaunch'
export * from './route/switchTab'
export { export {
upx2px, upx2px,
......
...@@ -6,13 +6,12 @@ import { ...@@ -6,13 +6,12 @@ import {
ReLaunchProtocol, ReLaunchProtocol,
} from '@dcloudio/uni-api' } from '@dcloudio/uni-api'
import { parseUrl } from '@dcloudio/uni-shared' import { parseUrl } from '@dcloudio/uni-shared'
import { ComponentPublicInstance } from 'vue'
import tabBar from '../../framework/app/tabBar' import tabBar from '../../framework/app/tabBar'
import { registerPage } from '../../framework/page' import { registerPage } from '../../framework/page'
import { getAllPages, removePage } from '../../framework/page/getCurrentPages' import { getAllPages } from '../../framework/page/getCurrentPages'
import { setStatusBarStyle } from '../../statusBar' import { setStatusBarStyle } from '../../statusBar'
import { navigate, RouteOptions } from './utils' import { closePage, navigate, RouteOptions } from './utils'
import { closeWebview, showWebview } from './webview' import { showWebview } from './webview'
export const reLaunch = defineAsyncApi<API_TYPE_RE_LAUNCH>( export const reLaunch = defineAsyncApi<API_TYPE_RE_LAUNCH>(
API_RE_LAUNCH, API_RE_LAUNCH,
...@@ -53,13 +52,7 @@ function _reLaunch({ url, path, query }: ReLaunchOptions): Promise<undefined> { ...@@ -53,13 +52,7 @@ function _reLaunch({ url, path, query }: ReLaunchOptions): Promise<undefined> {
'none', 'none',
0, 0,
() => { () => {
pages.forEach((page) => { pages.forEach((page) => closePage(page, 'none'))
removePage(page)
closeWebview(
(page as ComponentPublicInstance).$getAppWebview!(),
'none'
)
})
resolve(undefined) resolve(undefined)
} }
) )
......
import {
API_SWITCH_TAB,
API_TYPE_SWITCH_TAB,
defineAsyncApi,
SwitchTabOptions,
SwitchTabProtocol,
} from '@dcloudio/uni-api'
import { invokeHook } from '@dcloudio/uni-core'
import { parseUrl } from '@dcloudio/uni-shared'
import { ComponentPublicInstance } from 'vue'
import { ANI_CLOSE, ANI_DURATION } from '../../constants'
import tabBar from '../../framework/app/tabBar'
import { registerPage } from '../../framework/page'
import { getAllPages, removePage } from '../../framework/page/getCurrentPages'
import { setStatusBarStyle } from '../../statusBar'
import { closePage, navigate, RouteOptions } from './utils'
import { closeWebview, showWebview } from './webview'
export const switchTab = defineAsyncApi<API_TYPE_SWITCH_TAB>(
API_SWITCH_TAB,
(args, { resolve, reject }) => {
const { url } = args
const { path, query } = parseUrl(url)
navigate(
path,
() => {
_switchTab({
url,
path,
query,
})
.then(resolve)
.catch(reject)
},
(args as any).openType === 'appLaunch'
)
},
SwitchTabProtocol,
SwitchTabOptions
)
interface SwitchTabOptions extends RouteOptions {}
function _switchTab({
url,
path,
query,
}: SwitchTabOptions): Promise<undefined> {
tabBar.switchTab(path.slice(1))
const pages = getCurrentPages() as ComponentPublicInstance[]
const len = pages.length
let callOnHide = false
let callOnShow = false
let currentPage: ComponentPublicInstance | undefined
if (len >= 1) {
// 前一个页面是非 tabBar 页面
currentPage = pages[len - 1]! as ComponentPublicInstance
if (currentPage && !currentPage.__isTabBar) {
// 前一个页面为非 tabBar 页面时,目标tabBar需要强制触发onShow
// 该情况下目标页tabBarPage的visible是不对的
// 除非每次路由跳转都处理一遍tabBarPage的visible,目前仅switchTab会处理
// 简单起见,暂时直接判断该情况,执行onShow
callOnShow = true
pages.reverse().forEach((page) => {
if (!page.__isTabBar && page !== currentPage) {
closePage(page, 'none')
}
})
removePage(currentPage)
// 延迟执行避免iOS应用退出
setTimeout(() => {
if (currentPage!.$page.openType === 'redirectTo') {
closeWebview(currentPage!.$getAppWebview!(), ANI_CLOSE, ANI_DURATION)
} else {
closeWebview(currentPage!.$getAppWebview!(), 'auto')
}
}, 100)
} else {
callOnHide = true
}
}
let tabBarPage: ComponentPublicInstance | undefined
// 查找当前 tabBarPage,且设置 visible
getAllPages().forEach((page) => {
if ('/' + page.route === path) {
if (!page.$.__isActive) {
// 之前未显示
callOnShow = true
}
page.$.__isActive = true
tabBarPage = page
} else {
if (page.__isTabBar) {
page.$.__isActive = false
}
}
})
// 相同tabBar页面
if (currentPage === tabBarPage) {
callOnHide = false
}
if (currentPage && callOnHide) {
invokeHook(currentPage, 'onHide')
}
return new Promise((resolve) => {
if (tabBarPage) {
const webview = tabBarPage!.$getAppWebview!()
webview.show('none')
// 等visible状态都切换完之后,再触发onShow,否则开发者在onShow里边 getCurrentPages 会不准确
if (callOnShow && !(webview as any).__preload__) {
invokeHook(tabBarPage, 'onShow')
}
resolve(undefined)
} else {
showWebview(
registerPage({
url,
path,
query,
openType: 'switchTab',
}),
'none',
0,
() => {
setStatusBarStyle()
resolve(undefined)
},
70
)
}
setStatusBarStyle()
})
}
import { getRouteMeta } from '@dcloudio/uni-core' import { getRouteMeta } from '@dcloudio/uni-core'
import { formatLog } from '@dcloudio/uni-shared' import { formatLog } from '@dcloudio/uni-shared'
import { ComponentPublicInstance } from 'vue'
import { removePage } from '../../framework/page/getCurrentPages'
import { import {
createPreloadWebview, createPreloadWebview,
onWebviewReady, onWebviewReady,
preloadWebview, preloadWebview,
} from '../../framework/webview' } from '../../framework/webview'
import { closeWebview } from './webview'
export interface RouteOptions { export interface RouteOptions {
url: string url: string
...@@ -30,6 +33,15 @@ function setPendingNavigator(path: string, callback: Function, msg: string) { ...@@ -30,6 +33,15 @@ function setPendingNavigator(path: string, callback: Function, msg: string) {
} }
} }
export function closePage(
page: ComponentPublicInstance,
animationType: string,
animationDuration?: number
) {
removePage(page)
closeWebview(page.$getAppWebview!(), animationType, animationDuration)
}
export function navigate( export function navigate(
path: string, path: string,
callback: Function, callback: Function,
......
...@@ -27,6 +27,7 @@ interface AppUniConfig { ...@@ -27,6 +27,7 @@ interface AppUniConfig {
uploadFile: number uploadFile: number
downloadFile: number downloadFile: number
} }
tabBar?: UniApp.UniConfig['tabBar']
} }
export function normalizeAppUniConfig( export function normalizeAppUniConfig(
...@@ -54,6 +55,7 @@ export function normalizeAppUniConfig( ...@@ -54,6 +55,7 @@ export function normalizeAppUniConfig(
compilerVersion: process.env.UNI_COMPILER_VERSION, compilerVersion: process.env.UNI_COMPILER_VERSION,
entryPagePath: pagesJson.pages[0].path, entryPagePath: pagesJson.pages[0].path,
networkTimeout: normalizeNetworkTimeout(manifestJson.networkTimeout), networkTimeout: normalizeNetworkTimeout(manifestJson.networkTimeout),
tabBar: pagesJson.tabBar,
} }
// TODO 待支持分包 // TODO 待支持分包
return JSON.stringify(config) return JSON.stringify(config)
......
...@@ -19,6 +19,7 @@ export function initPageVm( ...@@ -19,6 +19,7 @@ export function initPageVm(
pageVm: ComponentPublicInstance, pageVm: ComponentPublicInstance,
page: Page.PageInstance['$page'] page: Page.PageInstance['$page']
) { ) {
pageVm.route = page.route
pageVm.$vm = pageVm pageVm.$vm = pageVm
pageVm.$page = page pageVm.$page = page
pageVm.$mpType = 'page' pageVm.$mpType = 'page'
......
...@@ -608,6 +608,7 @@ function initAppVm(appVm2) { ...@@ -608,6 +608,7 @@ function initAppVm(appVm2) {
appVm2.$mpType = "app"; appVm2.$mpType = "app";
} }
function initPageVm(pageVm, page) { function initPageVm(pageVm, page) {
pageVm.route = page.route;
pageVm.$vm = pageVm; pageVm.$vm = pageVm;
pageVm.$page = page; pageVm.$page = page;
pageVm.$mpType = "page"; pageVm.$mpType = "page";
......
...@@ -1378,6 +1378,7 @@ function initAppVm(appVm2) { ...@@ -1378,6 +1378,7 @@ function initAppVm(appVm2) {
appVm2.$mpType = "app"; appVm2.$mpType = "app";
} }
function initPageVm(pageVm, page) { function initPageVm(pageVm, page) {
pageVm.route = page.route;
pageVm.$vm = pageVm; pageVm.$vm = pageVm;
pageVm.$page = page; pageVm.$page = page;
pageVm.$mpType = "page"; pageVm.$mpType = "page";
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册