提交 4aafbc39 编写于 作者: D DCloud_LXH

feat(h5): darkmode

上级 a6f7b5a7
const mediaQuerys = []
module.exports = {
splitMediaPlugin: function (root, result) {
root.walkAtRules(rule => {
if (rule.params === '(perfers-color-scheme:dark)') {
root.removeChild(rule)
mediaQuerys.push(rule)
}
})
},
generateMediaQuerys: function ({ outputDir, filename = 'index.dark.css' }) {
if (mediaQuerys.length) {
const fs = require('fs')
const path = require('path')
const postcss = require('postcss')
var uglifycss = require('uglifycss')
const mediaRoot = postcss.root()
mediaRoot.append(mediaQuerys)
fs.writeFileSync(
path.resolve(outputDir, filename),
uglifycss.processString(mediaRoot.toResult().css),
{ encoding: 'utf-8', flag: 'w+' }
)
}
}
}
...@@ -4,7 +4,11 @@ const resolve = dir => path.resolve(__dirname, '../', dir) ...@@ -4,7 +4,11 @@ const resolve = dir => path.resolve(__dirname, '../', dir)
const pkgPath = resolve('package.json') const pkgPath = resolve('package.json')
const { splitMediaPlugin, generateMediaQuerys } = require('./postcssSplitMediaPlugin')
const webpack = require('webpack')
const webpackConfig = require('./webpack.config.js') const webpackConfig = require('./webpack.config.js')
const postCssConfig = require('../postcss.config')
let outputDir = resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist') let outputDir = resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist')
...@@ -16,6 +20,8 @@ if (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_VIEW === 'true') ...@@ -16,6 +20,8 @@ if (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_VIEW === 'true')
outputDir = resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist') outputDir = resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist')
} }
postCssConfig.plugins.push(splitMediaPlugin)
module.exports = { module.exports = {
publicPath: '/', publicPath: '/',
outputDir, outputDir,
...@@ -41,8 +47,22 @@ module.exports = { ...@@ -41,8 +47,22 @@ module.exports = {
configFile: pkgPath configFile: pkgPath
}) })
config.plugins.delete('hmr') // remove hot module reload config.plugins.delete('hmr') // remove hot module reload
config
.plugin('webpack-build-done')
.use(webpack.ProgressPlugin, [function (percentage, message, ...args) {
if (percentage === 1) {
console.log('webpack build done')
generateMediaQuerys({
outputDir
})
}
}])
}, },
css: { css: {
extract: true extract: true,
loaderOptions: {
postcss: postCssConfig
}
} }
} }
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
function getTemplatePath(template) { function getTemplatePath (template) {
if (template) { if (template) {
const userTemplate = path.resolve(process.env.UNI_INPUT_DIR, template) const userTemplate = path.resolve(process.env.UNI_INPUT_DIR, template)
if (fs.existsSync(userTemplate)) if (fs.existsSync(userTemplate)) { return userTemplate }
return userTemplate
} }
return path.resolve(process.env.UNI_CLI_CONTEXT, 'public/index.html') return path.resolve(process.env.UNI_CLI_CONTEXT, 'public/index.html')
} }
function transform(content) { function transform (content) {
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
return content + // shadow return content + // shadow
`body::after{position:fixed;content:'';left:-1000px;top:-1000px;-webkit-animation:shadow-preload .1s;-webkit-animation-delay:3s;animation:shadow-preload .1s;animation-delay:3s}@-webkit-keyframes shadow-preload{0%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}100%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}}@keyframes shadow-preload{0%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}100%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}}` 'body::after{position:fixed;content:\'\';left:-1000px;top:-1000px;-webkit-animation:shadow-preload .1s;-webkit-animation-delay:3s;animation:shadow-preload .1s;animation-delay:3s}@-webkit-keyframes shadow-preload{0%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}100%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}}@keyframes shadow-preload{0%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}100%{background-image:url(https://cdn.dcloud.net.cn/img/shadow-grey.png)}}'
} }
return content return content
} }
function getIndexCssPath(assetsDir, template) { function getIndexCssPath (assetsDir, template, hashKey) {
const CopyWebpackPluginVersion = Number(require('copy-webpack-plugin/package.json').version.split('.')[0]) const CopyWebpackPluginVersion = Number(require('copy-webpack-plugin/package.json').version.split('.')[0])
const VUE_APP_INDEX_CSS_HASH = process.env.VUE_APP_INDEX_CSS_HASH const VUE_APP_INDEX_CSS_HASH = process.env[hashKey]
if (VUE_APP_INDEX_CSS_HASH) { if (VUE_APP_INDEX_CSS_HASH) {
try { try {
const templateContent = fs.readFileSync(getTemplatePath(template)) const templateContent = fs.readFileSync(getTemplatePath(template))
if (/\bVUE_APP_INDEX_CSS_HASH\b/.test(templateContent)) { if (new RegExp('\\b' + hashKey + '\\b').test(templateContent)) {
return path.join(assetsDir, `[name].${VUE_APP_INDEX_CSS_HASH}${CopyWebpackPluginVersion > 5 ? '' : '.'}[ext]`) return path.join(assetsDir, `[name].${VUE_APP_INDEX_CSS_HASH}${CopyWebpackPluginVersion > 5 ? '' : '.'}[ext]`)
} }
} catch (e) {} } catch (e) { }
} }
return assetsDir return assetsDir
} }
...@@ -40,10 +39,11 @@ module.exports = { ...@@ -40,10 +39,11 @@ module.exports = {
filterTag: 'wxs', filterTag: 'wxs',
vue: '@dcloudio/vue-cli-plugin-uni/packages/h5-vue' vue: '@dcloudio/vue-cli-plugin-uni/packages/h5-vue'
}, },
copyWebpackOptions(platformOptions, vueOptions) { copyWebpackOptions (platformOptions, vueOptions) {
const copyOptions = [{ const copyOptions = [
{
from: require.resolve('@dcloudio/uni-h5/dist/index.css'), from: require.resolve('@dcloudio/uni-h5/dist/index.css'),
to: getIndexCssPath(vueOptions.assetsDir, platformOptions.template), to: getIndexCssPath(vueOptions.assetsDir, platformOptions.template, 'VUE_APP_INDEX_CSS_HASH'),
transform transform
}, },
'hybrid/html' 'hybrid/html'
...@@ -51,6 +51,22 @@ module.exports = { ...@@ -51,6 +51,22 @@ module.exports = {
global.uniModules.forEach(module => { global.uniModules.forEach(module => {
copyOptions.push('uni_modules/' + module + '/hybrid/html') copyOptions.push('uni_modules/' + module + '/hybrid/html')
}) })
// darkmode
let darkPath = ''
try {
darkPath = require.resolve('@dcloudio/uni-h5/dist/index.dark.css')
} catch (error) { }
if (platformOptions.darkmode === true && darkPath) {
copyOptions.push({
from: require.resolve('@dcloudio/uni-h5/dist/index.dark.css'),
to: getIndexCssPath(vueOptions.assetsDir, platformOptions.template, 'VUE_APP_INDEX_DARK_CSS_HASH'),
transform: (content) => {
}
})
}
return copyOptions return copyOptions
} }
} }
...@@ -65,6 +65,8 @@ const { ...@@ -65,6 +65,8 @@ const {
const uts = require('./uts') const uts = require('./uts')
const { parseTheme } = require('./theme')
module.exports = { module.exports = {
uts, uts,
md5, md5,
...@@ -115,5 +117,6 @@ module.exports = { ...@@ -115,5 +117,6 @@ module.exports = {
getPlatformGlobal, getPlatformGlobal,
getPlatformStat, getPlatformStat,
getPlatformPush, getPlatformPush,
getPlatformUniCloud getPlatformUniCloud,
} parseTheme
}
...@@ -47,8 +47,8 @@ module.exports = { ...@@ -47,8 +47,8 @@ module.exports = {
console.error(e) console.error(e)
} }
}, },
parseTheme (json) { parseTheme (json, _theme) {
const theme = global.uniPlugin.defaultTheme const theme = themeConfig[_theme] || global.uniPlugin.defaultTheme
if (!theme) { if (!theme) {
return json return json
} }
......
...@@ -35,6 +35,7 @@ process.env.UNI_APP_NAME = manifestJsonObj.name || '' ...@@ -35,6 +35,7 @@ process.env.UNI_APP_NAME = manifestJsonObj.name || ''
process.env.UNI_PLATFORM = process.env.UNI_PLATFORM || 'h5' process.env.UNI_PLATFORM = process.env.UNI_PLATFORM || 'h5'
process.env.UNI_APP_VERSION_NAME = manifestJsonObj.versionName process.env.UNI_APP_VERSION_NAME = manifestJsonObj.versionName
process.env.UNI_APP_VERSION_CODE = manifestJsonObj.versionCode process.env.UNI_APP_VERSION_CODE = manifestJsonObj.versionCode
process.env.VUE_APP_DARK_MODE = (manifestJsonObj[process.env.UNI_PLATFORM] || {}).darkmode
// 小程序 vue3 标记 // 小程序 vue3 标记
if (process.env.UNI_PLATFORM.indexOf('mp-') === 0) { if (process.env.UNI_PLATFORM.indexOf('mp-') === 0) {
...@@ -265,8 +266,16 @@ if (process.env.UNI_PLATFORM === 'h5') { ...@@ -265,8 +266,16 @@ if (process.env.UNI_PLATFORM === 'h5') {
process.env.UNI_OPT_PRELOAD = true process.env.UNI_OPT_PRELOAD = true
} }
} }
const buffer = fs.readFileSync(require.resolve('@dcloudio/uni-h5/dist/index.css')) const indexCssBuffer = fs.readFileSync(require.resolve('@dcloudio/uni-h5/dist/index.css'))
process.env.VUE_APP_INDEX_CSS_HASH = loaderUtils.getHashDigest(buffer, 'md5', 'hex', 8) process.env.VUE_APP_INDEX_CSS_HASH = loaderUtils.getHashDigest(indexCssBuffer, 'md5', 'hex', 8)
let indexDarkCssBuffer = ''
try {
indexDarkCssBuffer = fs.readFileSync(require.resolve('@dcloudio/uni-h5/dist/index.dark.css'))
process.env.VUE_APP_INDEX_DARK_CSS_HASH = loaderUtils.getHashDigest(indexDarkCssBuffer, 'md5', 'hex', 8)
} catch (error) {
process.env.VUE_APP_INDEX_DARK_CSS_HASH = ''
process.env.VUE_APP_DARK_MODE = false
}
} }
if (process.env.UNI_PLATFORM === 'mp-qq') { // QQ小程序 强制自定义组件模式 if (process.env.UNI_PLATFORM === 'mp-qq') { // QQ小程序 强制自定义组件模式
......
...@@ -6,6 +6,8 @@ if (process.env.UNI_USING_V3) { ...@@ -6,6 +6,8 @@ if (process.env.UNI_USING_V3) {
const valueParser = require('postcss-value-parser') const valueParser = require('postcss-value-parser')
const { const {
parseTheme,
getJson,
getPlatformCssVars getPlatformCssVars
} = require('@dcloudio/uni-cli-shared') } = require('@dcloudio/uni-cli-shared')
...@@ -20,6 +22,7 @@ if (process.env.UNI_USING_V3) { ...@@ -20,6 +22,7 @@ if (process.env.UNI_USING_V3) {
} }
const cssVars = getPlatformCssVars() const cssVars = getPlatformCssVars()
const pageJson = getJson('pages.json', true)
const transformSelector = (complexSelector, transformer) => { const transformSelector = (complexSelector, transformer) => {
return selectorParser(transformer).processSync(complexSelector) return selectorParser(transformer).processSync(complexSelector)
...@@ -170,6 +173,30 @@ if (process.env.UNI_USING_V3) { ...@@ -170,6 +173,30 @@ if (process.env.UNI_USING_V3) {
if (process.env.UNI_PLATFORM === 'h5') { if (process.env.UNI_PLATFORM === 'h5') {
// Transform CSS AST here // Transform CSS AST here
// darkmode
if (
process.env.VUE_APP_DARK_MODE === 'true' &&
root.source.input.file.indexOf('App.vue') !== -1
) {
const pageBGC = (pageJson.globalStyle || {}).backgroundColor || ''
if (pageBGC.indexOf('@') === 0) {
['dark', 'light'].forEach(theme => {
const { backgroundColor } = parseTheme({ backgroundColor: pageBGC }, theme)
if (backgroundColor !== 'undefined') {
const mediaRoot = postcss.parse(`
@media (prefers-color-scheme: ${theme}) {
body,
uni-page-body {
background-color: ${backgroundColor};
}
}
`)
root.nodes = [...mediaRoot.nodes, ...root.nodes]
}
})
}
}
root.walkRules(rule => { root.walkRules(rule => {
let hasPage = false let hasPage = false
// Transform each rule here // Transform each rule here
...@@ -269,7 +296,7 @@ if (process.env.UNI_USING_V3) { ...@@ -269,7 +296,7 @@ if (process.env.UNI_USING_V3) {
const version = Number(require('postcss/package.json').version.split('.')[0]) const version = Number(require('postcss/package.json').version.split('.')[0])
if (version < 8) { if (version <= 8) {
module.exports = postcss.plugin('postcss-uniapp-plugin', fn) module.exports = postcss.plugin('postcss-uniapp-plugin', fn)
} else { } else {
module.exports = function (opts) { module.exports = function (opts) {
......
...@@ -99,9 +99,16 @@ module.exports = function (content, map) { ...@@ -99,9 +99,16 @@ module.exports = function (content, map) {
} }
} }
const platformManifestJson = manifestJson[process.env.UNI_PLATFORM] || {}
const pagesJson = parseTheme(originalPagesJson) const pagesJson = parseTheme(originalPagesJson)
if (global.uniPlugin.defaultTheme) { if (global.uniPlugin.defaultTheme) {
this.addDependency(path.resolve(process.env.UNI_INPUT_DIR, 'theme.json')) this.addDependency(
path.resolve(
process.env.UNI_INPUT_DIR,
platformManifestJson.themeLocation || 'theme.json'
)
)
} }
// 组件自动导入配置 // 组件自动导入配置
...@@ -120,7 +127,7 @@ module.exports = function (content, map) { ...@@ -120,7 +127,7 @@ module.exports = function (content, map) {
if (process.env.UNI_PLATFORM === 'h5') { if (process.env.UNI_PLATFORM === 'h5') {
return this.callback( return this.callback(
null, null,
require('./platforms/h5')(pagesJson, manifestJson, this), require('./platforms/h5')(originalPagesJson, manifestJson, this),
map map
) )
} }
......
...@@ -14,6 +14,10 @@ const { ...@@ -14,6 +14,10 @@ const {
addPageUsingComponents addPageUsingComponents
} = require('@dcloudio/uni-cli-shared/lib/pages') } = require('@dcloudio/uni-cli-shared/lib/pages')
const {
getTheme
} = require('@dcloudio/uni-cli-shared/lib/theme')
const compilerVersion = require('@dcloudio/webpack-uni-pages-loader/package.json')['uni-app'].compilerVersion const compilerVersion = require('@dcloudio/webpack-uni-pages-loader/package.json')['uni-app'].compilerVersion
const PLATFORMS = getPlatforms() const PLATFORMS = getPlatforms()
...@@ -119,14 +123,27 @@ const getPageComponents = function (inputDir, pagesJson) { ...@@ -119,14 +123,27 @@ const getPageComponents = function (inputDir, pagesJson) {
always: 'float' always: 'float'
} }
let titleNView = pageStyle.titleNView let titleNView = pageStyle.titleNView
titleNView = Object.assign({}, { titleNView = Object.assign(
type: pageStyle.navigationStyle === 'custom' ? 'none' : 'default' {},
}, pageStyle.transparentTitle in titleNViewTypeList ? { {
type: titleNViewTypeList[pageStyle.transparentTitle], type: pageStyle.navigationStyle === 'custom' ? 'none' : 'default'
backgroundColor: 'rgba(0,0,0,0)' },
} : null, typeof titleNView === 'object' ? titleNView : (typeof titleNView === 'boolean' ? { pageStyle.transparentTitle in titleNViewTypeList
type: titleNView ? 'default' : 'none' ? {
} : null)) type: titleNViewTypeList[pageStyle.transparentTitle],
backgroundColor: 'rgba(0,0,0,0)'
}
: null,
typeof titleNView === 'object'
? titleNView
: (
typeof titleNView === 'boolean'
? {
type: titleNView ? 'default' : 'none'
}
: null
)
)
if (titleNView.type === 'none' || titleNView.type === 'transparent') { if (titleNView.type === 'none' || titleNView.type === 'transparent') {
windowTop = 0 windowTop = 0
} }
...@@ -407,9 +424,9 @@ module.exports = function (pagesJson, manifestJson, loader) { ...@@ -407,9 +424,9 @@ module.exports = function (pagesJson, manifestJson, loader) {
const googleMapKey = sdkConfigs.maps && sdkConfigs.maps.google && sdkConfigs.maps.google.key const googleMapKey = sdkConfigs.maps && sdkConfigs.maps.google && sdkConfigs.maps.google.key
const aMapKey = sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.key const aMapKey = sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.key
const aMapSecurityJsCode = const aMapSecurityJsCode =
sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.securityJsCode sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.securityJsCode
const aMapServiceHost = const aMapServiceHost =
sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.serviceHost sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.serviceHost
let locale = manifestJson.locale let locale = manifestJson.locale
locale = locale && locale.toUpperCase() !== 'AUTO' ? locale : '' locale = locale && locale.toUpperCase() !== 'AUTO' ? locale : ''
...@@ -422,6 +439,8 @@ global['____${h5.appid}____'] = true; ...@@ -422,6 +439,8 @@ global['____${h5.appid}____'] = true;
delete global['____${h5.appid}____']; delete global['____${h5.appid}____'];
global.__uniConfig = ${JSON.stringify(pagesJson)}; global.__uniConfig = ${JSON.stringify(pagesJson)};
global.__uniConfig.compilerVersion = '${compilerVersion}'; global.__uniConfig.compilerVersion = '${compilerVersion}';
global.__uniConfig.darkmode = ${JSON.stringify(h5.darkmode || false)};
global.__uniConfig.themeConfig = ${JSON.stringify(getTheme())};
global.__uniConfig.uniPlatform = '${process.env.UNI_PLATFORM}'; global.__uniConfig.uniPlatform = '${process.env.UNI_PLATFORM}';
global.__uniConfig.appId = '${process.env.UNI_APP_ID}'; global.__uniConfig.appId = '${process.env.UNI_APP_ID}';
global.__uniConfig.appName = '${process.env.UNI_APP_NAME}'; global.__uniConfig.appName = '${process.env.UNI_APP_NAME}';
......
export const NAVBAR_HEIGHT = 44 export const NAVBAR_HEIGHT = 44
export const TABBAR_HEIGHT = 50 export const TABBAR_HEIGHT = 50
// 576:landscape phones,768:tablets,992:desktops,1200:large desktops // 576:landscape phones,768:tablets,992:desktops,1200:large desktops
export const RESPONSIVE_MIN_WIDTH = 768 export const RESPONSIVE_MIN_WIDTH = 768
export const UNI_STORAGE_LOCALE = 'UNI_LOCALE' export const UNI_STORAGE_LOCALE = 'UNI_LOCALE'
export const ON_THEME_CHANGE = 'onThemeChange'
...@@ -6,10 +6,14 @@ import { ...@@ -6,10 +6,14 @@ import {
onMethod onMethod
} from '../../platform' } from '../../platform'
import {
ON_THEME_CHANGE
} from 'uni-helpers/constants'
const callbacks = [] const callbacks = []
const oldCallbacks = [] const oldCallbacks = []
onMethod('onThemeChange', function (res) { onMethod(ON_THEME_CHANGE, function (res) {
callbacks.forEach(callbackId => { callbacks.forEach(callbackId => {
invoke(callbackId, res) invoke(callbackId, res)
}) })
......
<template> <template>
<uni-app :class="{'uni-app--showtabbar':showTabBar,'uni-app--maxwidth':showMaxWidth}"> <uni-app :class="{ 'uni-app--showtabbar': showTabBar, 'uni-app--maxwidth': showMaxWidth }">
<layout <layout
ref="layout" ref="layout"
:router-key="key" :router-key="key"
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
v-bind="previewImage" v-bind="previewImage"
@close="_onPreviewClose" @close="_onPreviewClose"
/> />
<template v-if="sysComponents&&sysComponents.length"> <template v-if="sysComponents && sysComponents.length">
<component <component
:is="item" :is="item"
v-for="(item, index) in sysComponents" v-for="(item, index) in sysComponents"
...@@ -48,7 +48,8 @@ import { ...@@ -48,7 +48,8 @@ import {
} from 'uni-shared' } from 'uni-shared'
import { import {
TABBAR_HEIGHT TABBAR_HEIGHT,
ON_THEME_CHANGE
} from 'uni-helpers/constants' } from 'uni-helpers/constants'
import components from './components' import components from './components'
...@@ -59,6 +60,22 @@ import { ...@@ -59,6 +60,22 @@ import {
tabBar tabBar
} from './observable' } from './observable'
function onThemeChange () {
let mediaQueryList = null
try {
mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)')
} catch (error) { }
if (mediaQueryList) {
mediaQueryList.addEventListener('change', (e) => {
UniServiceJSBridge.emit('api.' + ON_THEME_CHANGE, {
theme: e.matches ? 'dark' : 'light'
})
})
}
}
export default { export default {
name: 'App', name: 'App',
components, components,
...@@ -93,10 +110,10 @@ export default { ...@@ -93,10 +110,10 @@ export default {
}, },
showTabBar () { showTabBar () {
return !this.hideTabBar && return !this.hideTabBar &&
( (
this.$route.meta.isTabBar || this.$route.meta.isTabBar ||
this.tabBarMediaQuery this.tabBarMediaQuery
) )
} }
}, },
watch: { watch: {
...@@ -136,6 +153,7 @@ export default { ...@@ -136,6 +153,7 @@ export default {
UniServiceJSBridge.emit('onAppEnterBackground') UniServiceJSBridge.emit('onAppEnterBackground')
} }
}) })
onThemeChange()
}, },
methods: { methods: {
onLayout (showLayout) { onLayout (showLayout) {
...@@ -147,8 +165,8 @@ export default { ...@@ -147,8 +165,8 @@ export default {
initMediaQuery () { initMediaQuery () {
if ( if (
window.matchMedia && window.matchMedia &&
tabBar.matchMedia && tabBar.matchMedia &&
hasOwn(tabBar.matchMedia, 'minWidth') hasOwn(tabBar.matchMedia, 'minWidth')
) { ) {
const mediaQueryList = window.matchMedia('(min-width: ' + tabBar.matchMedia.minWidth + 'px)') const mediaQueryList = window.matchMedia('(min-width: ' + tabBar.matchMedia.minWidth + 'px)')
mediaQueryList.addListener((e) => { mediaQueryList.addListener((e) => {
...@@ -162,12 +180,12 @@ export default { ...@@ -162,12 +180,12 @@ export default {
</script> </script>
<style> <style>
@import "~uni-core/view/index.css"; @import "~uni-core/view/index.css";
uni-app { uni-app {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>
import Vue from 'vue' import Vue from 'vue'
import { initTabBarI18n } from 'uni-helpers/i18n' import { initTabBarI18n } from 'uni-helpers/i18n'
__uniConfig.tabBar = Vue.observable(initTabBarI18n(__uniConfig.tabBar || {})) import { onThemeChange, parseTheme } from '../theme'
const originalTabBar = __uniConfig.tabBar || {}
__uniConfig.tabBar = Vue.observable(parseTheme(initTabBarI18n(originalTabBar)))
onThemeChange(() => {
const tabBarStyle = parseTheme(initTabBarI18n(originalTabBar))
__uniConfig.tabBar.backgroundColor = tabBarStyle.backgroundColor
__uniConfig.tabBar.borderStyle = tabBarStyle.borderStyle
__uniConfig.tabBar.color = tabBarStyle.color
__uniConfig.tabBar.selectedColor = tabBarStyle.selectedColor
__uniConfig.tabBar.blurEffect = tabBarStyle.blurEffect
})
export const tabBar = __uniConfig.tabBar export const tabBar = __uniConfig.tabBar
<template> <template>
<uni-page :data-page="$route.meta.pagePath"> <uni-page :data-page="$route.meta.pagePath">
<page-head <page-head
v-if="navigationBar.type!=='none'" v-if="navigationBar.type !== 'none'"
v-bind="navigationBar" v-bind="navigationBar"
/> />
<page-refresh <page-refresh
v-if="enablePullDownRefresh" v-if="enablePullDownRefresh"
ref="refresh" ref="refresh"
:color="refreshOptions.color" :color="refreshOptions.color"
:offset="refreshOptions.offset" :offset="refreshOptions.offset"
/> />
<page-body <page-body
v-if="enablePullDownRefresh" v-if="enablePullDownRefresh"
@touchstart.native="_touchstart" @touchstart.native="_touchstart"
@touchmove.native="_touchmove" @touchmove.native="_touchmove"
@touchend.native="_touchend" @touchend.native="_touchend"
@touchcancel.native="_touchend" @touchcancel.native="_touchend"
> >
<slot name="page" /> <slot name="page" />
</page-body> </page-body>
...@@ -25,11 +25,11 @@ ...@@ -25,11 +25,11 @@
</uni-page> </uni-page>
</template> </template>
<style> <style>
uni-page { uni-page {
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>
<script> <script>
import { import {
...@@ -58,6 +58,90 @@ import pullToRefresh from './pull-to-refresh' ...@@ -58,6 +58,90 @@ import pullToRefresh from './pull-to-refresh'
import safeAreaInsets from 'safe-area-insets' import safeAreaInsets from 'safe-area-insets'
import { onThemeChange, parseTheme } from '../theme'
function normalizeNavigationBar () {
// 目前简单处理,存在topWindow时,始终不显示page head
let navigationBar = {}
const titleNViewTypeList = {
none: 'default',
auto: 'transparent',
always: 'float'
}
// 将 navigationStyle 和 transparentTitle 都合并到 titleNView
let titleNView = this.titleNView
if ( // 无头
titleNView === false ||
titleNView === 'false' ||
(
this.navigationStyle === 'custom' &&
!isPlainObject(titleNView)
) || (
this.transparentTitle === 'always' &&
!isPlainObject(titleNView)
)
) {
titleNView = {
type: 'none'
}
} else {
titleNView = Object.assign({}, {
type: this.navigationStyle === 'custom' ? 'none' : 'default'
}, this.transparentTitle in titleNViewTypeList ? {
type: titleNViewTypeList[this.transparentTitle]
} : null, typeof titleNView === 'object' ? titleNView : (typeof titleNView === 'boolean' ? {
type: titleNView ? 'default' : 'none'
} : null))
}
const yesNoParseList = {
YES: true,
NO: false
}
navigationBar = mergeTitleNView({
loading: false,
backButton: !this.isQuit && !this.$route.meta.isQuit, // redirectTo,reLaunch时可能动态修改 meta.isQuit
backgroundColor: this.navigationBarBackgroundColor,
textColor: parseTheme({ testStyle: this.navigationBarTextStyle }).testStyle === 'black' ? '#000' : '#fff',
titleText: this.navigationBarTitleText,
titleImage: this.titleImage,
duration: '0',
timingFunc: '',
titlePenetrate: yesNoParseList[this.titlePenetrate]
}, titleNView)
navigationBar.shadow = this.navigationBarShadow
initNavigationBarI18n(navigationBar)
return {
navigationBar: __uniConfig.darkmode ? parseTheme(navigationBar) : navigationBar,
titleNView
}
}
function getRefreshOptions (titleNView) {
const refreshOptions = Object.assign({
support: true,
color: '#2BD009',
style: 'circle',
height: 70,
range: 150,
offset: 0
}, this.pullToRefresh)
let offset = upx2px(refreshOptions.offset)
if (titleNView.type !== 'none' && titleNView.type !== 'transparent') {
offset += NAVBAR_HEIGHT + safeAreaInsets.top
}
refreshOptions.offset = offset
refreshOptions.height = upx2px(refreshOptions.height)
refreshOptions.range = upx2px(refreshOptions.range)
return refreshOptions
}
export default { export default {
name: 'Page', name: 'Page',
mpType: 'page', mpType: 'page',
...@@ -91,7 +175,7 @@ export default { ...@@ -91,7 +175,7 @@ export default {
navigationBarTextStyle: { navigationBarTextStyle: {
default: 'white', default: 'white',
validator (value) { validator (value) {
return ['white', 'black'].indexOf(value) !== -1 return ['white', 'black'].indexOf(value) !== -1 || value.indexOf('@') === 0
} }
}, },
navigationBarTitleText: { navigationBarTitleText: {
...@@ -168,85 +252,29 @@ export default { ...@@ -168,85 +252,29 @@ export default {
} }
}, },
data () { data () {
// 目前简单处理,存在topWindow时,始终不显示page head const {
let navigationBar = {} navigationBar,
const titleNViewTypeList = { titleNView
none: 'default', } = normalizeNavigationBar.call(this)
auto: 'transparent',
always: 'float'
}
// 将 navigationStyle 和 transparentTitle 都合并到 titleNView
let titleNView = this.titleNView
if ( // 无头
titleNView === false ||
titleNView === 'false' ||
(
this.navigationStyle === 'custom' &&
!isPlainObject(titleNView)
) || (
this.transparentTitle === 'always' &&
!isPlainObject(titleNView)
)
) {
titleNView = {
type: 'none'
}
} else {
titleNView = Object.assign({}, {
type: this.navigationStyle === 'custom' ? 'none' : 'default'
}, this.transparentTitle in titleNViewTypeList ? {
type: titleNViewTypeList[this.transparentTitle]
} : null, typeof titleNView === 'object' ? titleNView : (typeof titleNView === 'boolean' ? {
type: titleNView ? 'default' : 'none'
} : null))
}
const yesNoParseList = { if (__uniConfig.darkmode) {
YES: true,
NO: false
}
navigationBar = mergeTitleNView({
loading: false,
backButton: !this.isQuit && !this.$route.meta.isQuit, // redirectTo,reLaunch时可能动态修改 meta.isQuit
backgroundColor: this.navigationBarBackgroundColor,
textColor: this.navigationBarTextStyle === 'black' ? '#000' : '#fff',
titleText: this.navigationBarTitleText,
titleImage: this.titleImage,
duration: '0',
timingFunc: '',
titlePenetrate: yesNoParseList[this.titlePenetrate]
}, titleNView)
navigationBar.shadow = this.navigationBarShadow
initNavigationBarI18n(navigationBar)
const refreshOptions = Object.assign({
support: true,
color: '#2BD009',
style: 'circle',
height: 70,
range: 150,
offset: 0
}, this.pullToRefresh)
let offset = upx2px(refreshOptions.offset)
if (titleNView.type !== 'none' && titleNView.type !== 'transparent') {
offset += NAVBAR_HEIGHT + safeAreaInsets.top
} }
refreshOptions.offset = offset
refreshOptions.height = upx2px(refreshOptions.height)
refreshOptions.range = upx2px(refreshOptions.range)
return { return {
navigationBar, navigationBar,
refreshOptions refreshOptions: getRefreshOptions.call(this, titleNView),
darkmodeStyle: {}
} }
}, },
created () { created () {
const navigationBar = this.navigationBar const navigationBar = this.navigationBar
document.title = navigationBar.titleText document.title = navigationBar.titleText
UniServiceJSBridge.emit('onNavigationBarChange', navigationBar) UniServiceJSBridge.emit('onNavigationBarChange', navigationBar)
onThemeChange(() => {
this.navigationBar = normalizeNavigationBar.call(this).navigationBar
})
} }
} }
</script> </script>
import { normallizeStyles } from 'uni-shared'
import { getTheme } from '../service/api/base/get-browser-info'
import {
ON_THEME_CHANGE
} from 'uni-helpers/constants'
export function onThemeChange (callback = () => { }) {
if (__uniConfig.darkmode) {
UniServiceJSBridge.on('api.' + ON_THEME_CHANGE, callback)
}
}
export function parseTheme (pageStyle) {
let parsedStyle = {}
if (__uniConfig.darkmode) {
const theme = getTheme()
parsedStyle = normallizeStyles(pageStyle, __uniConfig.themeConfig, theme)
}
return __uniConfig.darkmode ? parsedStyle : pageStyle
}
...@@ -21,6 +21,16 @@ function IEVersion () { ...@@ -21,6 +21,16 @@ function IEVersion () {
} }
} }
export function getTheme () {
try {
return window.matchMedia('(prefers-color-scheme: light)').matches
? 'light'
: 'dark'
} catch (error) {
return 'light'
}
}
const ua = navigator.userAgent const ua = navigator.userAgent
/** /**
* 是否安卓设备 * 是否安卓设备
...@@ -201,6 +211,6 @@ export function getBrowserInfo () { ...@@ -201,6 +211,6 @@ export function getBrowserInfo () {
ua, ua,
osname, osname,
osversion, osversion,
theme: undefined theme: getTheme()
} }
} }
...@@ -105,7 +105,7 @@ export function getSystemInfoSync () { ...@@ -105,7 +105,7 @@ export function getSystemInfoSync () {
delete systemInfo.screenTop delete systemInfo.screenTop
delete systemInfo.enableDebug delete systemInfo.enableDebug
delete systemInfo.theme if (!__uniConfig.darkmode) delete systemInfo.theme
return sortObject(systemInfo) return sortObject(systemInfo)
} }
......
...@@ -8890,6 +8890,11 @@ uglify-js@3.4.x: ...@@ -8890,6 +8890,11 @@ uglify-js@3.4.x:
commander "~2.19.0" commander "~2.19.0"
source-map "~0.6.1" source-map "~0.6.1"
uglifycss@^0.0.29:
version "0.0.29"
resolved "https://registry.yarnpkg.com/uglifycss/-/uglifycss-0.0.29.tgz#abe49531155d146e75dd2fdf933d371bc1180054"
integrity sha512-J2SQ2QLjiknNGbNdScaNZsXgmMGI0kYNrXaDlr4obnPW9ni1jljb1NeEVWAiTgZ8z+EBWP2ozfT9vpy03rjlMQ==
unc-path-regex@^0.1.0: unc-path-regex@^0.1.0:
version "0.1.2" version "0.1.2"
resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册