antd结合styled-components实现换肤

3,553 阅读2分钟

项目地址:github.com/leonwgc/ant…

  1. 定义不同的主题色 , 如下绿,红,蓝,紫,黄, 分别定义颜色名称和颜色值
const colors = [
  { name: 'green', color: '#08bc63' }, // 绿
  { name: 'red', color: '#f5222d' }, //红
  {
    name: 'blue',  // 蓝
    color: '#004bcc',
  },
  {
    name: 'purple', //紫
    color: '#9254de',
  },
  {
    name: 'yellow', // 黄
    color: 'rgb(250, 173, 20)',
  },
];
  1. 根据 antd-custom-theme-generator 生成相应css主题 用来动态替换 antd默认主题
  • 安装 antd-custom-theme-generator

  • 定义 custom-theme.less ,以黄色为例:b

@primary-color: rgb(250, 173, 20);
@link-color: rgb(250, 173, 20);
  • 执行 npx generate-theme ./custom-theme.less ./custom-theme-yellow.css ,将生成custom-theme-yellow.css样色文件

  • 将生成的css主题文件拷贝到 public目录

theme.png

  1. 修改webpack devServer 配置 (当然也可以传到cdn引用)
 devServer: {
      historyApiFallback: true,
      headers: { 'Access-Control-Allow-Origin': '*' },
      setup: function (app, server) {
        app.use(express.static(path.join(__dirname, `public`)));
      },
    },
  1. 定义redux store , 将颜色信息存储到redux
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, configureStore } from 'simple-redux-store';
import { getSetting } from '~/utils/helper';
import { getEnv } from '~/utils/host';
import App from './App';

const colors = [
  { name: 'green', color: '#08bc63' },
  { name: 'red', color: '#f5222d' },
  {
    name: 'blue',
    color: '#004bcc',
  },
  {
    name: 'purple',
    color: '#9254de',
  },
  {
    name: 'yellow',
    color: 'rgb(250, 173, 20)',
  },
];

type Color = {
  name: string;
  color: string;
};

type StoreData = {
  menuCollapsed: boolean;
  isSettingVisile: boolean;
  colors: Color[];
  color: string; //当前颜色名称
};

const initData: StoreData = {
  menuCollapsed: false,
  isSettingVisile: false,
  colors,
  color: 'blue', //默认取蓝色
  ...getSetting(),
};

const store = configureStore(initData, getEnv() === 'test');

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

  1. 定义换肤设置面板, 这里用antd drawer 组件

读取redux colors数组, 动态生成颜色块 , 点击颜色块, 更新redux color颜色名, 动态下载对应css皮肤

drawer.png

import React, { useEffect } from 'react';
import { Menu, Avatar, Dropdown, Space, Button, Drawer } from 'antd';
import { useSelector, useUpdateStore } from 'simple-redux-store';
import { useHistory } from 'react-router-dom';
import { loadResource, saveSetting } from '~/utils/helper';
import {
  MenuUnfoldOutlined,
  MenuFoldOutlined,
  UserOutlined,
  SettingOutlined,
} from '@ant-design/icons';
import * as storage from 'simple-browser-store';
import styled from 'styled-components';

const StyledColorBlock = styled.div`
  width: 20px;
  height: 20px;
  margin-top: 8px;
  margin-right: 8px;
  color: #fff;
  font-weight: 700;
  text-align: center;
  border-radius: 2px;
  cursor: pointer;
`;

const StyledHeader = styled.header`
  display: flex;
  height: 48px;
  background: ${(props) => props.theme.color}; // 读取Styled Component的主题色
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;
  border-bottom: 1px solid #f5f5f5;

  .folder {
    font-size: 16px;
    color: rgba(0, 0, 0, 0.65);
    &:hover {
      color: #004bcc;
    }
  }
`;

export default function Header() {
  const history = useHistory();
  const { color = '', colors } = useSelector((state) => state.app);
  const updateStore = useUpdateStore();

  // color改变,动态加载新主题
  useEffect(() => {
    if (color) {
      // eslint-disable-next-line no-undef
      loadResource(`${__webpack_public_path__}custom-theme-${color}.css`);
    }
  }, [color]);

  return (
    <StyledHeader>
      <Drawer
        title="主题色设置"
        placement="right"
        closable={false}
        onClose={() => updateStore({ isSettingVisile: false })}
        visible={isSettingVisile}
      >
        <p>
          <Space>
            //动态生成颜色块
            {colors.map((c, i) => (
              <StyledColorBlock
                onClick={() => {
                  updateStore({ color: c.name });
                  saveSetting({ color: c.name });
                }}
                style={{ backgroundColor: c.color }}
                key={i}
              />
            ))}
          </Space>
        </p>
      </Drawer>
    </StyledHeader>
  );
}

  1. Styled component 通过ThemeProvider 同步redux主题色 ,** 处理非antd组件的换肤 **
const { color = '', colors } = useSelector((state) => state.app);

<ThemeProvider theme={{ color: colors.find((c) => c.name === color).color }}>
      <ConfigProvider locale={zhCN}>
        <Router>
          <Suspense fallback={<Spin spinning />}>
            <Switch>
              <Route exact path="/">
                <Redirect to={'/user/add'} />
              </Route>
              {routes.map((route, idx) => (
                <Route
                  key={idx}
                  path={route.path}
                  exact={route.exact}
                  component={route.component}
                />
              ))}
            </Switch>
          </Suspense>
        </Router>
      </ConfigProvider>
    </ThemeProvider>

运行效果如图

c1.png

c2.png

c3.png

c4.png