PC端vue方案(antd-vue 1.x)
前言
react项目的antd因为使用了CSS-IN-JS,直接改变主题变量即可。vue的老项目antd版本较低,不支持CSS-IN-JS,所以换肤操作相对比较复杂。
方案
- 安装 webpack-theme-color-replacer
yarn add webpack-theme-color-replacer -S
- 封装webpack 插件,新建theme-color-replacer.js
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors/lib/generate').default
const getAntdSerials = (color) => {
// 淡化(即less的tint)
const lightens = new Array(9).fill().map((t, i) => {
return ThemeColorReplacer.varyColor.lighten(color, i / 10)
})
const colorPalettes = generate(color)
const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',')
return lightens.concat(colorPalettes).concat(rgb)
}
const themePluginOption = {
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getAntdSerials('#1890ff'), // 默认主色
// 改变样式选择器,解决样式覆盖问题
changeSelector (selector) {
switch (selector) {
case '.ant-calendar-today .ant-calendar-date':
return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
case '.ant-btn:focus,.ant-btn:hover':
return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-btn.active,.ant-btn:active':
return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
return ':not(.ant-steps-item-process)' + selector
// fixed https://github.com/vueComponent/ant-design-vue-pro/issues/876
case '.ant-steps-item-process .ant-steps-item-icon':
return ':not(.ant-steps-item-custom)' + selector
case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
case '.ant-menu-horizontal > .ant-menu-item-selected > a':
case '.ant-menu-horizontal>.ant-menu-item-selected>a':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
case '.ant-menu-horizontal > .ant-menu-item > a:hover':
case '.ant-menu-horizontal>.ant-menu-item>a:hover':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
default :
return selector
}
}
}
const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption)
module.exports = createThemeColorReplacerPlugin
- webpack 的配置 (vue.config.js)
// 动态修改主题色配置
const createThemeColorReplacerPlugin = require('./xxx/theme-color-replacer')
module.exports = {
// ..... other config
plugins: [
createThemeColorReplacerPlugin() // webpack plugins
]
}
- 封装公共方法
// themeColor.js
import client from 'webpack-theme-color-replacer/client'
import generate from '@ant-design/colors/lib/generate'
export default {
// 运行时更改主题颜色
changeColor (newColor) {
var options = {
newColors: this.getAntdSerials(newColor), // 新颜色数组,与" matchColors"一一对应
changeUrl (cssUrl) {
return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path
}
// appendToEl: 'head', //optional. The element selector for appending child with `<style>`, default is 'body'. Using `appendToEl: 'body'` can make the css priority higher than any css in <head>
}
return client.changer.changeColor(options, Promise)
}
}
- 使用
import themeColor from './themeColor.js'
//...
themeColor.changeColor('#123456').finally(() => {
// 切换成功后回调方法 这里可以关闭loading 或者 提示等等
})
- node高版本启动时遇到报错的话,参考修改package.json
"scripts": {
"serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
"build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build"
}
非antd样式处理
- 安装style-resources-loader
yarn add style-resources-loader -D
- vue.config.js增加代码,需要在modifyVars中定义默认值,否则会识别不到
module.exports = {
css: {
loaderOptions: {
less: {
modifyVars: {
'primary-color': '#3D92FF',
}
}
}
},
// ..... other config
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, 'node_modules/ant-design-vue/es/style/themes/default.less')]
}
}
}
- 直接使用变量写样式
<style scoped lang="less">
.test{
color: @primary-color;
}
</style>
小程序方案(uni-app,vue)
参考ext.dcloud.net.cn/plugin?id=6…
- 每个页面最外层view上绑定
:style="skin" - skin存放在vuex中,值:
--theme-color:#ffffff; - 样式直接使用变量
<style lang="less">
page {
background-color: var(--theme-color);
}
<style />
react方案(vite,antd5.x)
-
antd组件直接用ConfigProvider的theme配置主题
-
非组件样式
- vite.config.js
-
css: { // css预处理器 preprocessorOptions: { less: { // 自定义变量文件 additionalData: '@import "./src/styles/theme.less";' }, }, }, - theme.less中定义样式变量,页面less文件直接使用变量即可
-
@primary-color: var(--theme-color); - 动态在html根节点设置--theme-color的值