diff --git a/packages/uni-cli-shared/lib/theme.js b/packages/uni-cli-shared/lib/theme.js index 809c5555f6bee63a11bdc1243439dd797fc63bc5..2f478396634253722fa2b3a8bf329101dbd9ea11 100644 --- a/packages/uni-cli-shared/lib/theme.js +++ b/packages/uni-cli-shared/lib/theme.js @@ -15,9 +15,8 @@ function parseThemeByJsonStr (jsonStr, keys, theme) { return jsonStr } -const themeJsonPath = path.join(process.env.UNI_INPUT_DIR, 'theme.json') - -function hasTheme () { +function hasTheme (themeLocation = '') { + const themeJsonPath = path.join(process.env.UNI_INPUT_DIR, themeLocation || 'theme.json') return fs.existsSync(themeJsonPath) } @@ -25,19 +24,24 @@ function darkmode () { return !!(global.uniPlugin.options || {}).darkmode } +let themeConfig = {} + module.exports = { + getTheme: () => themeConfig, darkmode, hasTheme, - initTheme () { - if (!hasTheme()) { + initTheme (manifestJson = {}) { + const platform = process.env.UNI_PLATFORM + const themeLocation = (manifestJson[platform] || {}).themeLocation + if (!hasTheme(themeLocation)) { return } if (darkmode()) { return } try { - const theme = getJson('theme.json', true) - global.uniPlugin.defaultTheme = theme.light + themeConfig = getJson(themeLocation || 'theme.json', true) + global.uniPlugin.defaultTheme = themeConfig.light } catch (e) { console.error(e) } @@ -56,4 +60,4 @@ module.exports = { } return JSON.parse(parseThemeByJsonStr(JSON.stringify(json), keys, theme)) } -} +} diff --git a/packages/webpack-uni-pages-loader/lib/index-new.js b/packages/webpack-uni-pages-loader/lib/index-new.js index 752f4b20668b47cee8fc78c63383042caa9461d2..58172d2fc23b43e67adb555f464ed58c15a4696b 100644 --- a/packages/webpack-uni-pages-loader/lib/index-new.js +++ b/packages/webpack-uni-pages-loader/lib/index-new.js @@ -51,8 +51,6 @@ function renameUsingComponents (jsonObj) { module.exports = function (content, map) { this.cacheable && this.cacheable() - initTheme() - let isAppView = false if (this.resourceQuery) { const params = loaderUtils.parseQuery(this.resourceQuery) @@ -68,6 +66,8 @@ module.exports = function (content, map) { fs.readFileSync(manifestJsonPath, 'utf8') ) + initTheme(manifestJson) + // this.addDependency(pagesJsonJsPath) const localePath = path.resolve(process.env.UNI_INPUT_DIR, 'locale') // 路径不存在时会触发 webpack5 差量编译 @@ -76,7 +76,7 @@ module.exports = function (content, map) { } this.addDependency(manifestJsonPath) - let pagesJson = parsePagesJson(content, { + const originalPagesJson = parsePagesJson(content, { addDependency: file => { (process.UNI_PAGES_DEPS || (process.UNI_PAGES_DEPS = new Set())).add( normalizePath(file) @@ -85,7 +85,7 @@ module.exports = function (content, map) { } }) - if (!pagesJson.pages || pagesJson.pages.length === 0) { + if (!originalPagesJson.pages || originalPagesJson.pages.length === 0) { console.error(uniI18n.__('pagesLoader.pagesNodeCannotNull')) process.exit(0) } @@ -94,13 +94,13 @@ module.exports = function (content, map) { const queryParam = loaderUtils.parseQuery(this.resourceQuery) if (queryParam) { if (queryParam.type === 'origin-pages-json') { - return `export default ${JSON.stringify(pagesJson)}` + return `export default ${JSON.stringify(originalPagesJson)}` } } } + const pagesJson = parseTheme(originalPagesJson) if (global.uniPlugin.defaultTheme) { - pagesJson = parseTheme(pagesJson) this.addDependency(path.resolve(process.env.UNI_INPUT_DIR, 'theme.json')) } @@ -167,7 +167,7 @@ module.exports = function (content, map) { } const jsonFiles = require('./platforms/' + process.env.UNI_PLATFORM)( - pagesJson, + process.env.UNI_PLATFORM === 'app-plus' ? originalPagesJson : pagesJson, manifestJson, isAppView ) 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 5ff180dee6fcf1b7de8defbc929bd02beda67f3b..3a0735a096ac759ad8c31891bf46e071c6862583 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 @@ -7,6 +7,11 @@ const { normalizePath, getFlexDirection } = require('@dcloudio/uni-cli-shared') +const { + getTheme, + hasTheme, + parseTheme +} = require('@dcloudio/uni-cli-shared/lib/theme') const { compileI18nJsonStr } = require('@dcloudio/uni-i18n') @@ -104,6 +109,16 @@ function updateFileFlag (appJson) { } } +function _initTheme (appJson, userManifestJson) { + const manifestJson = userManifestJson[process.env.UNI_PLATFORM] || {} + appJson.darkmode = manifestJson.darkmode || false + const themeLocation = manifestJson.themeLocation || '' + if (themeLocation && hasTheme(themeLocation)) { + appJson.themeConfig = getTheme() + } + return appJson +} + module.exports = function (pagesJson, userManifestJson, isAppView) { const { app @@ -115,10 +130,12 @@ module.exports = function (pagesJson, userManifestJson, isAppView) { const appJson = app.content + _initTheme(appJson, userManifestJson) + const { navigationBarTextStyle = 'white', navigationBarBackgroundColor = '#000000' - } = appJson.window || {} + } = parseTheme(appJson.window) || {} const TABBAR_HEIGHT = 50 @@ -279,7 +296,7 @@ module.exports = function (pagesJson, userManifestJson, isAppView) { // 安全区配置 仅包含 tabBar 的时候才配置 if (!manifestJson.plus.safearea) { manifestJson.plus.safearea = { - background: appJson.tabBar.backgroundColor || '#FFFFFF', + background: parseTheme(appJson.tabBar).backgroundColor || '#FFFFFF', bottom: { offset: 'auto' } @@ -470,7 +487,7 @@ module.exports = function (pagesJson, userManifestJson, isAppView) { if (args && (args.path || args.pathName)) { entryPagePath = conditionPagePath = args.path || args.pathName } - } catch (e) {} + } catch (e) { } } let isNVueEntryPage = appJson.nvue && appJson.nvue.entryPagePath @@ -521,7 +538,7 @@ module.exports = function (pagesJson, userManifestJson, isAppView) { pagesJson.tabBar.list.length ) { const tabBar = (manifestJson.plus.tabBar = Object.assign({}, - pagesJson.tabBar + parseTheme(pagesJson.tabBar) )) const borderStyles = { black: 'rgba(0,0,0,0.4)', @@ -546,9 +563,9 @@ module.exports = function (pagesJson, userManifestJson, isAppView) { const item = tabBar.list.find( page => page.pagePath === - (process.env.UNI_USING_NATIVE - ? appJson.entryPagePath - : entryPagePath) + (process.env.UNI_USING_NATIVE + ? appJson.entryPagePath + : entryPagePath) ) if (item) { tabBar.child = ['lauchwebview'] @@ -634,7 +651,7 @@ function initUniStatistics (manifestJson) { let spaces = [] try { spaces = JSON.parse(process.env.UNI_CLOUD_PROVIDER) - } catch (e) {} + } catch (e) { } if (!Array.isArray(spaces) || !spaces.length) { return } diff --git a/packages/webpack-uni-pages-loader/lib/util.js b/packages/webpack-uni-pages-loader/lib/util.js index 44a33ad6e2180a8d2f595e28185e9982fd62c245..2d75721907f83c2f7ca91fa56b3f7d914ac5cda6 100644 --- a/packages/webpack-uni-pages-loader/lib/util.js +++ b/packages/webpack-uni-pages-loader/lib/util.js @@ -18,7 +18,13 @@ const alipayWindowMap = { navigationBarShadow: 'navigationBarShadow', titleImage: 'titleImage', transparentTitle: 'transparentTitle', - titlePenetrate: 'titlePenetrate' + titlePenetrate: 'titlePenetrate', + barButtonTheme: { + key: 'navigationBarTextStyle', + transform: function (value) { + + } + } } const alipayTabBarMap = { diff --git a/src/platforms/app-plus/service/api/device/system.js b/src/platforms/app-plus/service/api/device/system.js index f9241a59819f8ff9cce8322df3deb1399949a4e8..73b7ac59cefdaf0122e06f7d23272c9c0aa39da6 100644 --- a/src/platforms/app-plus/service/api/device/system.js +++ b/src/platforms/app-plus/service/api/device/system.js @@ -5,15 +5,16 @@ import { sortObject } from 'uni-shared' let systemInfo = {} let _initSystemInfo = true -function weexGetSystemInfoSync () { +export function weexGetSystemInfoSync () { if (!_initSystemInfo) return const { getSystemInfoSync } = weex.requireModule('plus') systemInfo = getSystemInfoSync() if (typeof systemInfo === 'string') { try { systemInfo = JSON.parse(systemInfo) - } catch (error) {} + } catch (error) { } } + return systemInfo } export function getDeviceInfo () { @@ -47,7 +48,7 @@ export function getAppBaseInfo () { hostPackageName, hostName, osLanguage, hostVersion, hostLanguage, hostTheme, appId, appName, appVersion, appVersionCode, - appWgtVersion + appWgtVersion, osTheme } = systemInfo const appLanguage = uni @@ -73,7 +74,7 @@ export function getAppBaseInfo () { hostFontSizeSetting: undefined, language: osLanguage, SDKVersion: '', - theme: undefined, + theme: hostTheme || osTheme, version: plus.runtime.innerVersion } } @@ -112,7 +113,9 @@ export function getSystemInfo () { delete _systemInfo.screenTop delete _systemInfo.enableDebug - delete _systemInfo.theme + if (!__uniConfig.darkmode) { + delete _systemInfo.theme + } return sortObject(_systemInfo) } diff --git a/src/platforms/app-plus/service/framework/tab-bar.js b/src/platforms/app-plus/service/framework/tab-bar.js index 3bf232ea247ae0b9b0a8eb7d4a84b6f4c7fa632a..77cdb6c3f18ede178a7db2a749bb40ebfa374086 100644 --- a/src/platforms/app-plus/service/framework/tab-bar.js +++ b/src/platforms/app-plus/service/framework/tab-bar.js @@ -7,6 +7,8 @@ import { requireNativePlugin } from '../bridge' +import { useTabBarThemeChange } from './theme' + const TABBAR_HEIGHT = 50 let config @@ -17,6 +19,10 @@ let visible = true let tabBar +function setTabBarItems (style) { + tabBar && tabBar.setTabBarItems(style) +} + /** * 设置角标 * @param {string} type @@ -60,9 +66,9 @@ function setTabBarItem (index, text, iconPath, selectedIconPath, visible, iconfo } if (selectedIconPath) { item.selectedIconPath = getRealPath(selectedIconPath) - } - if (iconfont !== undefined) { - item.iconfont = iconfont + } + if (iconfont !== undefined) { + item.iconfont = iconfont } if (visible !== undefined) { item.visible = config.list[index].visible = visible @@ -71,7 +77,7 @@ function setTabBarItem (index, text, iconPath, selectedIconPath, visible, iconfo const tabbarItems = config.list.map(item => ({ visible: item.visible })) tabbarItems[index] = item - tabBar && tabBar.setTabBarItems({ list: tabbarItems }) + setTabBarItems({ list: tabbarItems }) } else { tabBar && tabBar.setTabBarItem(item) } @@ -128,6 +134,8 @@ export default { tabBar && tabBar.onMidButtonClick(() => { publish('onTabBarMidButtonTap', {}) }) + + useTabBarThemeChange(tabBar, options) }, indexOf (page) { const config = this.config diff --git a/src/platforms/app-plus/service/framework/theme.js b/src/platforms/app-plus/service/framework/theme.js new file mode 100644 index 0000000000000000000000000000000000000000..aeb62b1ce8d94bd58d000e85917c8f00676bcba8 --- /dev/null +++ b/src/platforms/app-plus/service/framework/theme.js @@ -0,0 +1,80 @@ +import { normallizeStyles } from 'uni-shared' +import { weexGetSystemInfoSync } from '../api/device/system' + +const ON_THEME_CHANGE = 'api.onThemeChange' + +function onThemeChange (callback = () => { }) { + UniServiceJSBridge.on(ON_THEME_CHANGE, callback) +} + +function offThemeChange (callback = () => { }) { + UniServiceJSBridge.off(ON_THEME_CHANGE, callback) +} + +export function parseTheme (pageStyle) { + let parsedStyle = {} + if (__uniConfig.darkmode) { + let theme = 'light' + const systemInfo = weexGetSystemInfoSync() + if (systemInfo) { + theme = systemInfo.hostTheme || systemInfo.osTheme + } + parsedStyle = normallizeStyles(pageStyle, __uniConfig.themeConfig, theme) + } + return __uniConfig.darkmode ? parsedStyle : pageStyle +} + +export function useTabBarThemeChange (tabBar, options) { + if (__uniConfig.darkmode) { + const fn = () => { + const { + list = [], color, selectedColor, + backgroundColor, borderStyle + } = parseTheme(options, false) + const tabbarStyle = { + color, + selectedColor, + backgroundColor, + borderStyle + } + + tabBar && tabBar.setTabBarStyle(tabbarStyle) + tabBar && tabBar.setTabBarItems({ + list: list.map((item) => ({ + iconPath: item.iconPath, + selectedIconPath: item.selectedIconPath + })) + }) + // TODO 暂未实现 + // tabBar && tabBar.setAnimationAlphaBGColor(parseTheme((__uniConfig.window || {}).backgroundColor, false)) + } + + fn() + + onThemeChange(fn) + } +} + +export function useWebviewThemeChange (webview, getWebviewStyle) { + if (__uniConfig.darkmode) { + const fn = () => { + const { + animationAlphaBGColor, background, + backgroundColorBottom, backgroundColorTop, + titleNView: { backgroundColor, titleColor } = {} + } = getWebviewStyle() + webview && webview.setStyle({ + animationAlphaBGColor, + background, + backgroundColorBottom, + backgroundColorTop, + titleNView: { + backgroundColor, + titleColor + } + }) + } + onThemeChange(fn) + webview.addEventListener('close', () => offThemeChange(fn)) + } +} diff --git a/src/platforms/app-plus/service/framework/webview/index.js b/src/platforms/app-plus/service/framework/webview/index.js index ddfdadbe200cc94c1f9f0b627f225cbe4961ca55..59d45e3b1bc0829f6d9cd98b1edecf6b1c560982 100644 --- a/src/platforms/app-plus/service/framework/webview/index.js +++ b/src/platforms/app-plus/service/framework/webview/index.js @@ -30,6 +30,8 @@ import { onWebviewPopGesture } from './on-webview-pop-gesture' +import { useWebviewThemeChange } from '../theme' + export let preloadWebview let id = 2 @@ -71,21 +73,26 @@ function getDebugRefresh (path, query, routeOptions) { export function createWebview (path, routeOptions, query, extras = {}) { if (routeOptions.meta.isNVue) { - const webviewId = id++ - const webviewStyle = parseWebviewStyle( + const getWebviewStyle = () => parseWebviewStyle( webviewId, path, routeOptions ) + const webviewId = id++ + const webviewStyle = getWebviewStyle() webviewStyle.uniPageUrl = getUniPageUrl(path, query) if (process.env.NODE_ENV !== 'production') { console.log('[uni-app] createWebview', webviewId, path, webviewStyle) } // android 需要使用 webviewStyle.isTab = !!routeOptions.meta.isTabBar - return plus.webview.create('', String(webviewId), webviewStyle, Object.assign({ + const webview = plus.webview.create('', String(webviewId), webviewStyle, Object.assign({ nvue: true }, extras)) + + useWebviewThemeChange(webview, getWebviewStyle) + + return webview } if (id === 2) { // 如果首页非 nvue,则直接返回 Launch Webview return plus.webview.getLaunchWebview() @@ -97,11 +104,12 @@ export function createWebview (path, routeOptions, query, extras = {}) { export function initWebview (webview, routeOptions, path, query) { // 首页或非 nvue 页面 if (webview.id === '1' || !routeOptions.meta.isNVue) { - const webviewStyle = parseWebviewStyle( + const getWebviewStyle = () => parseWebviewStyle( parseInt(webview.id), '', routeOptions ) + const webviewStyle = getWebviewStyle() webviewStyle.uniPageUrl = getUniPageUrl(path, query) @@ -115,6 +123,8 @@ export function initWebview (webview, routeOptions, path, query) { console.log('[uni-app] updateWebview', webviewStyle) } + useWebviewThemeChange(webview, getWebviewStyle) + webview.setStyle(webviewStyle) } 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 905193556ec95469c60bdecd85629dc42d0bc899..34d86c536f63ca986909b27a2730e2ccc1eb8ae4 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 @@ -4,6 +4,8 @@ import { parsePullToRefresh } from './pull-to-refresh-parser' import { parseStyleUnit } from './style-unit-parser' +import { parseTheme } from '../../theme' + const WEBVIEW_STYLE_BLACKLIST = [ 'navigationBarBackgroundColor', 'navigationBarTextStyle', @@ -21,25 +23,27 @@ const WEBVIEW_STYLE_BLACKLIST = [ 'pullToRefresh' ] -export function parseWebviewStyle (id, path, routeOptions = {}) { +export function parseWebviewStyle (id, path, _routeOptions = {}) { const webviewStyle = { bounce: 'vertical' } // 合并 - routeOptions.window = parseStyleUnit( + _routeOptions.window = parseStyleUnit( Object.assign( JSON.parse(JSON.stringify(__uniConfig.window || {})), - routeOptions.window || {} + _routeOptions.window || {} ) ) - Object.keys(routeOptions.window).forEach(name => { + Object.keys(_routeOptions.window).forEach(name => { if (WEBVIEW_STYLE_BLACKLIST.indexOf(name) === -1) { - webviewStyle[name] = routeOptions.window[name] + webviewStyle[name] = _routeOptions.window[name] } }) + const routeOptions = parseTheme(_routeOptions) + const backgroundColor = routeOptions.window.backgroundColor if ( /^#[a-z0-9]{6}$/i.test(backgroundColor) || @@ -51,6 +55,15 @@ export function parseWebviewStyle (id, path, routeOptions = {}) { if (!webviewStyle.backgroundColorTop) { webviewStyle.backgroundColorTop = backgroundColor } + if (!webviewStyle.backgroundColorBottom) { + webviewStyle.backgroundColorBottom = backgroundColor + } + if (!webviewStyle.animationAlphaBGColor) { + webviewStyle.animationAlphaBGColor = backgroundColor + } + if (typeof webviewStyle.webviewBGTransparent === 'undefined') { + webviewStyle.webviewBGTransparent = true + } } const titleNView = parseTitleNView(id, routeOptions) diff --git a/src/shared/index.js b/src/shared/index.js index c5062af2abcbdc63ade2280432b974b41164e1d4..30131c808b1d68e3bbd407f54fa92134e0c55b91 100644 --- a/src/shared/index.js +++ b/src/shared/index.js @@ -4,3 +4,4 @@ export * from './color' export * from './query' export * from './platform' export * from './callback' +export * from './theme' diff --git a/src/shared/theme.js b/src/shared/theme.js new file mode 100644 index 0000000000000000000000000000000000000000..a99a5621651b12e76e139e28181fef3d890a5322 --- /dev/null +++ b/src/shared/theme.js @@ -0,0 +1,44 @@ +import { isPlainObject, isStr } from './util' + +const borderStyles = { + black: 'rgba(0,0,0,0.4)', + white: 'rgba(255,255,255,0.4)' +} + +export function normalizeTabBarStyles (borderStyle) { + if (borderStyle && borderStyle in borderStyles) { + return borderStyles[borderStyle] + } + return borderStyle +} + +export function normallizeStyles (pageStyle, themeConfig, mode = 'light') { + const modeStyle = themeConfig[mode] + const styles = {} + if (!modeStyle) { + return styles + } + Object.keys(pageStyle).forEach((key) => { + const styleItem = pageStyle[key] // Object Array String + styles[key] = (() => { + if (isPlainObject(styleItem)) { + return normallizeStyles(styleItem, themeConfig, mode) + } else if (Array.isArray(styleItem)) { + return styleItem.map((item) => isPlainObject(item) + ? normallizeStyles(item, themeConfig, mode) + : item) + } else if (isStr(styleItem) && styleItem.startsWith('@')) { + const _key = styleItem.replace('@', '') + let _styleItem = modeStyle[_key] + switch (key) { + case 'borderStyle': + _styleItem = normalizeTabBarStyles(_styleItem) + break + } + return _styleItem + } + return styleItem + })() + }) + return styles +}