From 7eb0355e81b9578a01646f4aa51ac4304e14812e Mon Sep 17 00:00:00 2001 From: qiang Date: Wed, 25 Sep 2019 20:09:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20app=20=E7=AB=AF=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=8E=9F=E7=94=9F=20tabBar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/platforms/app-plus/index.js | 72 ++- .../service/api/route/switch-tab.js | 17 +- .../app-plus/service/framework/app.js | 14 +- .../app-plus/service/framework/page.js | 7 +- .../app-plus/service/framework/tab-bar.js | 477 +++--------------- .../webview/parser/webview-style-parser.js | 11 +- 6 files changed, 116 insertions(+), 482 deletions(-) diff --git a/packages/webpack-uni-pages-loader/lib/platforms/app-plus/index.js b/packages/webpack-uni-pages-loader/lib/platforms/app-plus/index.js index d67eda46..f6c90df6 100644 --- a/packages/webpack-uni-pages-loader/lib/platforms/app-plus/index.js +++ b/packages/webpack-uni-pages-loader/lib/platforms/app-plus/index.js @@ -331,6 +331,10 @@ module.exports = function (pagesJson, userManifestJson) { if (conditionPagePath && isNVueEntryPage) { isNVueEntryPage = appJson.nvue.entryPagePath === conditionPagePath } + manifestJson.plus.useragent.value = 'uni-app' + manifestJson.launch_path = '__uniappview.html' + manifestJson.plus.launchwebview.id = '1' + manifestJson.plus.launchwebview.kernel = 'WKWebview' if (process.env.UNI_USING_NATIVE) { appJson.entryPagePath = appJson.nvue.entryPagePath // networkTimeout @@ -346,51 +350,43 @@ module.exports = function (pagesJson, userManifestJson) { }) delete appJson.nvue - + + delete manifestJson.plus.launchwebview.kernel + manifestJson.launch_path = '' + Object.assign(manifestJson.plus.launchwebview, { + id: '1', + uniNView: { + path: appJson.pages[0] + } + }) // 纯 nvue 带 tab if (pagesJson.tabBar && pagesJson.tabBar.list && pagesJson.tabBar.list.length) { - manifestJson.launch_path = '__uniapptabbar.html' - manifestJson.plus.secondwebview = { - id: '1', - mode: 'child', - top: 0, - bottom: 56, - uniNView: { - path: appJson.pages[0] - } + const tabBar = manifestJson.plus.tabBar = Object.assign({}, pagesJson.tabBar) + tabBar.height = '56px' + // 首页是 tabBar 页面 + const item = tabBar.list.find(page => page.pagePath === appJson.pages[0]) + if (item) { + tabBar.child = ['lauchwebview'] + tabBar.selected = tabBar.list.indexOf(item) } } else { // 纯 nvue 不带 tab - manifestJson.launch_path = '' - Object.assign(manifestJson.plus.launchwebview, { - id: '1', - uniNView: { - path: appJson.pages[0] - } - }) + } - } else if (isNVueEntryPage) { // 临时 tabBar 页面 - manifestJson.launch_path = '__uniapptabbar.html' } else if (pagesJson.tabBar && pagesJson.tabBar.list && pagesJson.tabBar.list.length) { - manifestJson.launch_path = '__uniapptabbar.html' - manifestJson.plus.secondwebview = { - id: '1' - } - manifestJson.plus.secondwebview.launch_path = '__uniappview.html' - if (!manifestJson.plus.secondwebview.kernel) { - manifestJson.plus.secondwebview.kernel = 'WKWebview' - } - // 首页是 tabBar 页面 - if (pagesJson.tabBar.list.find(page => page.pagePath === entryPagePath)) { - manifestJson.plus.secondwebview.mode = 'child' - manifestJson.plus.secondwebview.top = 0 - manifestJson.plus.secondwebview.bottom = 56 + const tabBar = manifestJson.plus.tabBar = Object.assign({}, pagesJson.tabBar) + tabBar.height = '56px' + if (isNVueEntryPage) { + manifestJson.plus.launchwebview.id = '2' + } else { + // 首页是 tabBar 页面 + const item = tabBar.list.find(page => page.pagePath === entryPagePath) + if (item) { + tabBar.child = ['lauchwebview'] + tabBar.selected = tabBar.list.indexOf(item) + } } } else { // 无 tabbar 的页面,launchWebview 的 id 为1 - manifestJson.launch_path = '__uniappview.html' - manifestJson.plus.launchwebview.id = '1' - if (!manifestJson.plus.launchwebview.kernel) { - manifestJson.plus.launchwebview.kernel = 'WKWebview' - } + } } @@ -415,4 +411,4 @@ module.exports = function (pagesJson, userManifestJson) { return [manifest, parseConfig(appJson)] } return [app, manifest] -} +} diff --git a/src/platforms/app-plus-nvue/service/api/route/switch-tab.js b/src/platforms/app-plus-nvue/service/api/route/switch-tab.js index 960edd4e..ffb60b2c 100644 --- a/src/platforms/app-plus-nvue/service/api/route/switch-tab.js +++ b/src/platforms/app-plus-nvue/service/api/route/switch-tab.js @@ -31,11 +31,14 @@ export function switchTab ({ } }) currentPage.$remove() - if (currentPage.$page.openType === 'redirect') { - currentPage.$getAppWebview().close(ANI_CLOSE, ANI_DURATION) - } else { - currentPage.$getAppWebview().close('auto') - } + // 延迟执行避免iOS应用退出 + setTimeout(() => { + if (currentPage.$page.openType === 'redirect') { + currentPage.$getAppWebview().close(ANI_CLOSE, ANI_DURATION) + } else { + currentPage.$getAppWebview().close('auto') + } + }, 100) } else { // 前一个 tabBar 触发 onHide currentPage.$vm.__call_hook('onHide') @@ -64,9 +67,9 @@ export function switchTab ({ path, query: {}, openType: 'switchTab' - }) + }), 'none', 0 ) } setStatusBarStyle() -} +} diff --git a/src/platforms/app-plus/service/framework/app.js b/src/platforms/app-plus/service/framework/app.js index 99937234..98224980 100644 --- a/src/platforms/app-plus/service/framework/app.js +++ b/src/platforms/app-plus/service/framework/app.js @@ -9,7 +9,6 @@ import { } from './page' import { - registerPlusMessage, consumePlusMessage } from './plus-message' @@ -112,7 +111,7 @@ function initTabBar () { __uniConfig.__ready__ = true const onLaunchWebviewReady = function onLaunchWebviewReady () { - const tabBarView = tabBar.init(__uniConfig.tabBar, (item, index) => { + tabBar.init(__uniConfig.tabBar, (item, index) => { UniServiceJSBridge.emit('onTabItemTap', { index, text: item.text, @@ -121,16 +120,11 @@ function initTabBar () { uni.switchTab({ url: '/' + item.pagePath, openType: 'switchTab', - from: 'tabbar' + from: 'tabBar' }) }) - tabBarView && plus.webview.getLaunchWebview().append(tabBarView) - } - if (plus.webview.getLaunchWebview()) { - onLaunchWebviewReady() - } else { - registerPlusMessage('UniWebviewReady-' + plus.runtime.appid, onLaunchWebviewReady, false) } + onLaunchWebviewReady() } export function registerApp (appVm) { @@ -152,4 +146,4 @@ export function registerApp (appVm) { initGlobalListeners() initTabBar() -} +} diff --git a/src/platforms/app-plus/service/framework/page.js b/src/platforms/app-plus/service/framework/page.js index b54fe665..745888b2 100644 --- a/src/platforms/app-plus/service/framework/page.js +++ b/src/platforms/app-plus/service/framework/page.js @@ -3,6 +3,8 @@ import { createWebview } from './webview/index' +import tabBar from '../framework/tab-bar' + const pages = [] export function getCurrentPages (returnAll) { @@ -25,6 +27,8 @@ export function registerPage ({ if (openType === 'reLaunch' || pages.length === 0) { // pages.length===0 表示首页触发 redirectTo routeOptions.meta.isQuit = true + } else if (!routeOptions.meta.isTabBar) { + routeOptions.meta.isQuit = false } if (!webview) { @@ -38,8 +42,7 @@ export function registerPage ({ } if (routeOptions.meta.isTabBar && webview.id !== '1') { - const launchWebview = plus.webview.getLaunchWebview() - launchWebview && launchWebview.append(webview) + tabBar.append(webview) } if (process.env.NODE_ENV !== 'production') { diff --git a/src/platforms/app-plus/service/framework/tab-bar.js b/src/platforms/app-plus/service/framework/tab-bar.js index 96053359..3c8463bb 100644 --- a/src/platforms/app-plus/service/framework/tab-bar.js +++ b/src/platforms/app-plus/service/framework/tab-bar.js @@ -1,368 +1,19 @@ import { - TABBAR_HEIGHT -} from '../constants' - -import { - getRealPath, - isTabBarPage + getRealPath } from '../api/util' -import safeArea from './safe-area' - -const IMAGE_TOP = 7 -const IMAGE_WIDTH = 24 -const IMAGE_HEIGHT = 24 -const TEXT_TOP = 36 -const TEXT_SIZE = 12 -const TEXT_NOICON_SIZE = 17 -const TEXT_HEIGHT = 12 -const IMAGE_ID = 'TABITEM_IMAGE_' -const TABBAR_VIEW_ID = 'TABBAR_VIEW_' +import { + requireNativePlugin +} from '../bridge' -let view let config -let winWidth -let itemWidth -let itemLength -let itemImageLeft -let itemRects = [] -const itemIcons = [] -const itemLayouts = [] -const itemDot = [] -const itemBadge = [] -let itemClickCallback -let selected = 0 /** * tabbar显示状态 */ let visible = true -const init = function () { - const list = config.list - itemLength = config.list.length - - calcItemLayout(list) // 计算选项卡布局 - - initBitmaps(list) // 初始化选项卡图标 - - initView() -} - -let initView = function () { - const viewStyles = { - height: TABBAR_HEIGHT - } - if (config.position === 'top') { - viewStyles.top = 0 - } else { - viewStyles.bottom = 0 - viewStyles.height += safeArea.bottom - } - view = new plus.nativeObj.View(TABBAR_VIEW_ID, viewStyles, getDraws()) - - view.interceptTouchEvent(true) - - view.addEventListener('click', (e) => { - if (!__uniConfig.__ready__) { // 未 ready,不允许点击 - return - } - const x = e.clientX - const y = e.clientY - for (let i = 0; i < itemRects.length; i++) { - if (isCross(x, y, itemRects[i])) { - const draws = getSelectedDraws(i) - const drawTab = !!draws.length - itemClickCallback && itemClickCallback(config.list[i], i, drawTab) - if (drawTab) { - setTimeout(() => view.draw(draws)) - } - break - } - } - }) - plus.globalEvent.addEventListener('orientationchange', () => { - calcItemLayout(config.list) - if (config.position !== 'top') { - let height = TABBAR_HEIGHT + safeArea.bottom - view.setStyle({ - height: height - }) - if (visible) { - setWebviewPosition(height) - } - } - view.draw(getDraws()) - }) - if (!visible) { - view.hide() - } -} - -let isCross = function (x, y, rect) { - if (x > rect.left && x < (rect.left + rect.width) && y > rect.top && y < (rect.top + rect.height)) { - return true - } - return false -} - -let initBitmaps = function (list) { - for (let i = 0; i < list.length; i++) { - const item = list[i] - if (item.iconData) { - const bitmap = new plus.nativeObj.Bitmap(IMAGE_ID + i) - bitmap.loadBase64Data(item.iconData) - const selectedBitmap = new plus.nativeObj.Bitmap(`${IMAGE_ID}SELECTED_${i}`) - selectedBitmap.loadBase64Data(item.selectedIconData) - itemIcons[i] = { - icon: bitmap, - selectedIcon: selectedBitmap - } - } else if (item.iconPath) { - itemIcons[i] = { - icon: item.iconPath, - selectedIcon: item.selectedIconPath - } - } else { - itemIcons[i] = { - icon: false, - selectedIcon: false - } - } - } -} - -let getDraws = function () { - const backgroundColor = config.backgroundColor - const borderHeight = Math.max(0.5, 1 / plus.screen.scale) // 高分屏最少0.5 - const borderTop = config.position === 'top' ? (TABBAR_HEIGHT - borderHeight) : 0 - const borderStyle = config.borderStyle === 'white' ? 'rgba(255,255,255,0.33)' : 'rgba(0,0,0,0.33)' - - const draws = [{ - id: `${TABBAR_VIEW_ID}BG`, // 背景色 - tag: 'rect', - color: backgroundColor, - position: { - top: 0, - left: 0, - width: '100%', - height: TABBAR_HEIGHT + safeArea.bottom - } - }, { - id: `${TABBAR_VIEW_ID}BORDER`, - tag: 'rect', - color: borderStyle, - position: { - top: borderTop, - left: 0, - width: '100%', - height: `${borderHeight}px` - } - }] - - for (let i = 0; i < itemLength; i++) { - const item = config.list[i] - if (i === selected) { - drawTabItem(draws, i, item.text, config.selectedColor, itemIcons[i].selectedIcon) - } else { - drawTabItem(draws, i, item.text, config.color, itemIcons[i].icon) - } - } - return draws -} - -let getSelectedDraws = function (newSelected) { - if (selected === newSelected) { - return false - } - const draws = [] - const lastSelected = selected - selected = newSelected - drawTabItem(draws, lastSelected) - drawTabItem(draws, selected) - return draws -} -/** - * 获取文字宽度(全角为1) - * @param {*} text - */ -function getFontWidth (text) { - // eslint-disable-next-line - return text.length - (text.match(/[\u0000-\u00ff]/g) || []).length / 2 -} -let calcItemLayout = function () { - winWidth = plus.screen.resolutionWidth - itemWidth = winWidth / itemLength - itemImageLeft = (itemWidth - IMAGE_WIDTH) / 2 - itemRects = [] - let dotTop = 0 - let dotLeft = 0 - for (let i = 0; i < itemLength; i++) { - itemRects.push({ - top: 0, - left: i * itemWidth, - width: itemWidth, - height: TABBAR_HEIGHT - }) - } - - for (let i = 0; i < itemLength; i++) { - const item = config.list[i] - let imgLeft = itemWidth * i + itemImageLeft - if ((item.iconData || item.iconPath) && item.text) { // 图文 - itemLayouts[i] = { - text: { - top: TEXT_TOP, - left: `${i * itemWidth}px`, - width: `${itemWidth}px`, - height: TEXT_HEIGHT - }, - img: { - top: IMAGE_TOP, - left: `${imgLeft}px`, - width: IMAGE_WIDTH, - height: IMAGE_HEIGHT - } - } - dotTop = IMAGE_TOP - dotLeft = imgLeft + IMAGE_WIDTH - } else if (!(item.iconData || item.iconPath) && item.text) { // 仅文字 - let textLeft = i * itemWidth - itemLayouts[i] = { - text: { - top: 0, - left: `${textLeft}px`, - width: `${itemWidth}px`, - height: TABBAR_HEIGHT - } - } - dotTop = (44 - TEXT_NOICON_SIZE) / 2 - dotLeft = textLeft + itemWidth * 0.5 + getFontWidth(item.text) * TEXT_NOICON_SIZE * 0.5 - } else if ((item.iconData || item.iconPath) && !item.text) { // 仅图片 - const diff = 10 - let imgTop = (TABBAR_HEIGHT - IMAGE_HEIGHT - diff) / 2 - let imgLeft = itemWidth * i + itemImageLeft - diff / 2 - itemLayouts[i] = { - img: { - top: `${imgTop}px`, - left: `${imgLeft}px`, - width: IMAGE_WIDTH + diff, - height: IMAGE_HEIGHT + diff - } - } - dotTop = imgTop - dotLeft = imgLeft + IMAGE_WIDTH + diff - } - let height = itemBadge[i] ? 14 : 10 - let badge = itemBadge[i] || '0' - let font = getFontWidth(badge) - 0.5 - font = font > 1 ? 1 : font - let width = height + font * 12 - width = width < height ? height : width - let itemLayout = itemLayouts[i] - if (itemLayout) { - itemLayout.doc = { - top: dotTop, - left: `${dotLeft - width * 0.382}px`, - width: `${width}px`, - height: `${height}px` - } - itemLayout.badge = { - top: dotTop, - left: `${dotLeft - width * 0.382}px`, - width: `${width}px`, - height: `${height}px` - } - } - } -} - -let drawTabItem = function (draws, index) { - const layout = itemLayouts[index] - - const item = config.list[index] - - let color = config.color - let icon = itemIcons[index].icon - let dot = itemDot[index] - let badge = itemBadge[index] || '' - - if (index === selected) { - color = config.selectedColor - icon = itemIcons[index].selectedIcon - } - - if (icon) { - draws.push({ - id: `${TABBAR_VIEW_ID}ITEM_${index}_ICON`, - tag: 'img', - position: layout.img, - src: icon - }) - } - if (item.text) { - draws.push({ - id: `${TABBAR_VIEW_ID}ITEM_${index}_TEXT`, - tag: 'font', - position: layout.text, - text: item.text, - textStyles: { - size: icon ? TEXT_SIZE : `${TEXT_NOICON_SIZE}px`, - color - } - }) - } - const hiddenPosition = { - letf: 0, - top: 0, - width: 0, - height: 0 - } - // 绘制小红点(角标背景) - draws.push({ - id: `${TABBAR_VIEW_ID}ITEM_${index}_DOT`, - tag: 'rect', - position: (dot || badge) ? layout.doc : hiddenPosition, - rectStyles: { - color: '#ff0000', - radius: badge ? '7px' : '5px' - } - }) - // 绘制角标文本 - draws.push({ - id: `${TABBAR_VIEW_ID}ITEM_${index}_BADGE`, - tag: 'font', - position: badge ? layout.badge : hiddenPosition, - text: badge || ' ', - textStyles: { - align: 'center', - verticalAlign: 'middle', - color: badge ? '#ffffff' : 'rgba(0,0,0,0)', - overflow: 'ellipsis', - size: '10px' - } - }) -} -/** - * { - "color": "#7A7E83", - "selectedColor": "#3cc51f", - "borderStyle": "black", - "backgroundColor": "#ffffff", - "list": [{ - "pagePath": "page/component/index.html", - "iconData": "", - "selectedIconData": "", - "text": "组件" - }, { - "pagePath": "page/API/index.html", - "iconData": "", - "selectedIconData": "", - "text": "接口" - }], - "position":"bottom"//bottom|top - } - */ +let tabBar /** * 设置角标 @@ -371,112 +22,96 @@ let drawTabItem = function (draws, index) { * @param {string} text */ function setTabBarBadge (type, index, text) { + if (!tabBar) { + return + } if (type === 'none') { - itemDot[index] = false - itemBadge[index] = '' + tabBar.hideTabBarRedDot({ + index + }) + tabBar.removeTabBarBadge({ + index + }) } else if (type === 'text') { - itemBadge[index] = text + tabBar.setTabBarBadge({ + index, + text + }) } else if (type === 'redDot') { - itemDot[index] = true - } - if (view) { - calcItemLayout(config.list) - view.draw(getDraws()) + tabBar.showTabBarRedDot({ + index + }) } } /** * 动态设置 tabBar 某一项的内容 */ function setTabBarItem (index, text, iconPath, selectedIconPath) { - if (iconPath || selectedIconPath) { - let itemIcon = itemIcons[index] = itemIcons[index] || {} - if (iconPath) { - itemIcon.icon = getRealPath(iconPath) - } - if (selectedIconPath) { - itemIcon.selectedIcon = getRealPath(selectedIconPath) - } + const item = {} + if (iconPath) { + item.iconPath = getRealPath(iconPath) } - if (text) { - config.list[index].text = text + if (selectedIconPath) { + item.selectedIconPath = getRealPath(selectedIconPath) } - view.draw(getDraws()) + tabBar && tabBar.setTabBarItem(Object.assign({ + index, + text + }, item)) } /** * 动态设置 tabBar 的整体样式 * @param {Object} style 样式 */ function setTabBarStyle (style) { - for (const key in style) { - config[key] = style[key] - } - view.draw(getDraws()) -} -/** - * 设置tab页底部或顶部距离 - * @param {*} value 距离 - */ -function setWebviewPosition (value) { - const position = config.position === 'top' ? 'top' : 'bottom' - plus.webview.all().forEach(webview => { - if (isTabBarPage(String(webview.__uniapp_route))) { - webview.setStyle({ - [position]: value - }) - } - }) + tabBar && tabBar.setTabBarStyle(style) } /** * 隐藏 tabBar * @param {boolean} animation 是否需要动画效果 暂未支持 */ function hideTabBar (animation) { - if (visible === false) { - return - } visible = false - if (view) { - view.hide() - setWebviewPosition(0) - } + tabBar && tabBar.hideTabBar({ + animation + }) } /** * 显示 tabBar * @param {boolean} animation 是否需要动画效果 暂未支持 */ function showTabBar (animation) { - if (visible === true) { - return - } visible = true - if (view) { - view.show() - setWebviewPosition(TABBAR_HEIGHT + safeArea.bottom) - } + tabBar && tabBar.showTabBar({ + animation + }) } export default { init (options, clickCallback) { if (options && options.list.length) { - selected = options.selected || 0 config = options - config.position = 'bottom' // 暂时强制使用bottom - itemClickCallback = clickCallback - init() - return view } + try { + tabBar = requireNativePlugin('uni-tabview') + } catch (error) { + console.log(`uni.requireNativePlugin("uni-tabview") error ${error}`) + } + tabBar && tabBar.onClick(({ index }) => { + clickCallback(config.list[index], index, true) + }) }, switchTab (page) { + const itemLength = config.list.length if (itemLength) { for (let i = 0; i < itemLength; i++) { if ( config.list[i].pagePath === page || config.list[i].pagePath === `${page}.html` ) { - const draws = getSelectedDraws(i) - if (draws.length) { - view.draw(draws) - } + tabBar && tabBar.switchSelect({ + index: i + }) return true } } @@ -488,7 +123,19 @@ export default { setTabBarStyle, hideTabBar, showTabBar, + append (webview) { + tabBar && tabBar.append({ + id: webview.id + }, ({ code }) => { + if (code !== 0) { + console.log('tab append error') + setTimeout(() => { + this.append(webview) + }, 100) + } + }) + }, get visible () { return visible } -} +} diff --git a/src/platforms/app-plus/service/framework/webview/parser/webview-style-parser.js b/src/platforms/app-plus/service/framework/webview/parser/webview-style-parser.js index bc1ea8af..1b6f1e17 100644 --- a/src/platforms/app-plus/service/framework/webview/parser/webview-style-parser.js +++ b/src/platforms/app-plus/service/framework/webview/parser/webview-style-parser.js @@ -6,10 +6,6 @@ import { parsePullToRefresh } from './pull-to-refresh-parser' -import { - TABBAR_HEIGHT -} from '../../../constants' - const WEBVIEW_STYLE_BLACKLIST = [ 'navigationBarBackgroundColor', 'navigationBarTextStyle', @@ -73,10 +69,5 @@ export function parseWebviewStyle (id, path, routeOptions = {}) { } } - if (routeOptions.meta.isTabBar) { - webviewStyle.top = 0 - webviewStyle.bottom = TABBAR_HEIGHT - } - return webviewStyle -} +} -- GitLab