likes
comments
collection
share

实战ant-design-pro管理后台的样式

作者站长头像
站长
· 阅读数 33

效果图

PC端

实战ant-design-pro管理后台的样式

实战ant-design-pro管理后台的样式

手机端

实战ant-design-pro管理后台的样式

实战ant-design-pro管理后台的样式

CSS方法论及样式规范

CSS方法论是一种面向CSS、由个人和组织设计、已被诸多项目检验且公认有效的最佳实践。这些方法论都会涉及结构化的命名约定,并且在组织CSS时可提供相应的指南,从而提升代码的性能、可读性以及可维护性

实战项目主要采用BEM+SMACSS这两种方法论的结合命名约定,所以下面对这两种方法论进行简单介绍。

BEM

BEM(BlockElementModifier)是指块级元素修饰符,BEM分为三部分:

  1. 块(Block)是一个独立实体,最高级抽象,例如菜单、文本框等。
  2. 元素(Element)是块的组成部分,被包含在块中,无法自成一体,例如菜单项、标题等。
  3. 修饰符(Modifier)是块或元素的状态,可更改它们的外观或行为,例如高亮、选中等。

BEM中的块、元素和修饰符需要全部小写,名称中的单词用连字符(-)分隔,元素由双下划线(__)分隔,修饰符由双连字符(--)分隔。注意,块和元素都既不能是HTML元素名或ID,也不依赖其它块或元素。

.setting-menu{}
.setting-menu--open{}
.setting-menu__head{}
.setting-menu__head--fixed{}
.setting-menu__content{}

上面代码中,.setting-menu表示一个独立实体,.setting-menu__head、.setting-menu__content{}表示独立实体的组成部分,.setting-menu--open{}是对配置菜单展开状态的一种行为描述,.setting-menu__head--fixed{}是对配置菜单头部固定状态的一种行为描述。

SMACSS

指模块化的CSS架构

  1. 基础(Base)是为HTML元素定义默认样式,可以包含属性、伪类等选择器。
  2. 布局(Layout)会将页面分为几部分,可作为高级容器包含一个或多个模块,例如左右分栏、栅格系统等。
  3. 模块(Module)又名对象或块,是可重用的模块化部分,例如导航栏、产品列表等。
  4. 状态(State)描述的是任一模块或布局在特定状态下的外观,例如隐藏、激活等。
  5. 主题(Theme)也就是换肤,描述了页面的外观,它可修改前面四个类别的样式,例如链接颜色、布局方式等。

通过相应的命名前缀来完成对类别的表示,l-用作布局的前缀,例如.l-inline、.layout-grid等;m-模块命名前缀,例如.m-profile、.m-field等;is-用作状态的前缀,例如.is-collapsed、.is-active等;theme-用作主题的前缀,例如.theme-l-grid等。

在实际工作中,不需要局限于某一个CSS方法论,很多时候可以结合使用,共享模块化CSS的规则。例如遵循BEM的命名约定,以及SMACSS的分类前缀,具体如下所列。

.m-setting-menu{}
.m-setting-menu--open{}
.m-setting-menu__head{}
.m-setting-menu__head--fixed{}
.m-setting-menu__content{}

CSS文件划分

在中大型项目中,一般会对CSS进行文件划分,根据文件的性质与用途,大概会分成:

  • 公共型样式
  • 特殊型样式
  • 皮肤型样式

公共型样式可命名为global.css或common.css等名字,主要包括:

  • 重置默认样式reset
  • 网站通用布局
  • 通用模块
  • 通用元件
  • 通用响应式系统

特殊型样式主要是根据当前页面来决定的文件,只针对当前页面做出特殊处理的样式,例如只在首页中用到的样式可放置到index.css中,在登录页中用到的样式可放置到login.css中。

皮肤型样式是针对网站需要皮肤功能时,把颜色、背景等抽离出来放到文件中的形式,例如theme-pink.css、theme-skyblue.css等。

CSS文件的引入顺序如下:

<linkhref="assets/css/global.css"rel="stylesheet"type="text/css"/><linkhref="assets/css/index.css"rel="stylesheet"type="text/css"/><linkhref="assets/css/theme.css"rel="stylesheet"type="text/css"/>

页面整体结构

<!-- g-ant布局样式 -->
<div class="g-ant">
  <!-- 侧边栏 -->
  <div class="g-ant__sider">
    <div class="g-ant-sider__wrap g-ant-sider__wrap--fixed">
      <!-- 侧边栏头部 -->
      <div class="g-ant-sider__head"></div>
      <!-- 侧边栏主题内容区 -->
      <ul class="g-ant-sider__main">
        <li class="m-menu m-menu--selected"></li>
      </ul>
      <!-- 侧边栏页脚 -->
      <div class="g-ant-sider__foot">
        <i class="iconfont icon-outdent u-bar"></i>
      </div>
    </div>
  </div>
  <!-- 遮罩层 -->
  <div class="u-mask"></div>
  <!-- 主内容区 -->
  <div class="g-ant__main">
    <!-- 主内容区头部 -->
    <div class="g-ant-main__head g-ant-main__head--fixed"></div>
    <!-- 主内容区的内容区 -->
    <div class="g-ant-main__main"></div>
    <!-- 主内容区页脚 -->
    <div class="g-ant-main__foot"></div>
  </div>
</div>
<!-- 设置区 -->
<div class="m-setting">
  <div class="m-setting__bar">
    <i class="iconfont icon-setting"></i>
  </div>
  <!-- 设置项 -->
  <div class="m-setting__item"></div>
  <!-- 分隔符 -->
  <div class="u-divider"></div>
  <div class="m-setting__item"></div>
</div>

<div class="u-mask"></div>

页面整体样式

通用布局-主体布局

.g-ant {
  display: flex;
}
.g-ant__sider {
  // 不缩小
  flex-shrink: 0;
  // 设置宽度
  width: 208px;
  min-height: 100vh;
}
.g-ant-sider__wrap {
  height: 100%;
  display: flex;
  // 按列进行布局
  flex-direction: column;
}
.g-ant__main {
  // 沾满剩余空间
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}

侧边栏布局

stick footer

.g-ant-sider__head {
  flex-shrink: 0;
}
.g-ant-sider__main {
  flex-grow: 1;
  overflow: hidden auto;
}
.g-ant-sider__foot {
  flex-shrink: 0;
}

利用margin:auto实现如下效果:

<div class="m-menu__title">
  <i class="iconfont icon-car"></i>
  <span>Dashboard</span>
  <i class="iconfont icon-arrowup"></i>
</div>
.m-menu__title {
  display: flex;
  align-items: center;
}

.m-menu__title > span {
  margin-right: auto;
}

实战ant-design-pro管理后台的样式

需求:当点击标题的时候把子菜单弹出,同时切换右侧的箭头及高亮

html结构
<li class="m-menu m-menu--selected">
  <div class="m-menu__title">
    <i class="iconfont icon-car"></i>
    <span>Dashboard</span>
    <i class="iconfont icon-arrowup"></i>
  </div>
  <ul class="m-menu__sub">
    <li>分析页</li>
    <li class="m-menu__sub--selected">监控页</li>
    <li>工作台</li>
  </ul>
</li>
css
.m-menu__sub {
  display: none;
}
// 当选中的时候改变文字颜色
.m-menu--selected .m-menu__title {
  color: white;
}
// 当选中的时候展开子菜单
.m-menu--selected .m-menu__sub {
  display: block;
}
js
const menuTitles = document.querySelectorAll('.m-menu__title')
for (var i = 0; i < menuTitles.length; i++) {
  menuTitles[i].onclick = function () {
    var menu = this.parentElement
    menu.classList.toggle('m-menu--selected')
    if (menu.className.includes('m-menu--selected')) {
      // 打开状态
      var arrow = this.querySelector('[class*="icon-arrow"]')
      arrow.className = 'iconfont icon-arrowup'
    } else {
      // 关闭状态
      var arrow = this.querySelector('[class*="icon-arrow"]')
      arrow.className = 'iconfont icon-arrowdown'
    }
  }
}

注意

  • className获取或设置指定元素的class属性的值,我们可以直接进行设置
  • Element.classList 是一个只读属性,返回一个元素的类属性的实时 DOMTokenList 集合。相比将 element.className 作为以空格分隔的字符串来使用,classList 是一种更方便的访问元素的类列表的方法

实战ant-design-pro管理后台的样式

需求:当点击按钮把菜单栏缩小到只有图标

.g-ant__sider {
  width: 208px;
}
.g-ant__sider--closed {
  width: 48px;
  overflow: hidden;
}
const sider = document.querySelector('.g-ant__sider')
sider.classList.toggle('g-ant__sider--closed')

即通过改变width来实现

实战ant-design-pro管理后台的样式

主体区

<div class="g-ant__main">
  <!-- 主内容区头部 -->
  <div class="g-ant-main__head g-ant-main__head--fixed"></div>
  <!-- 主内容区的内容区 -->
  <div class="g-ant-main__main"></div>
  <!-- 主内容区页脚 -->
  <div class="g-ant-main__foot"></div>
</div>
.g-ant__main {
  display: flex;
  flex-direction: column;
}
.g-ant-main__head {
  height: 48px;
  flex-shrink: 0;
}
.g-ant-main__head--fixed {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 2;
}
.g-ant-main__main {
  flex-grow: 1;
}
.g-ant-main__foot {
  margin-top: 70px;
  margin-bottom: 30px;
}

侧边栏设置

<div class="m-setting">
  <div class="m-setting__bar">
    <i class="iconfont icon-setting"></i>
  </div>
  <!-- 设置项 -->
  <div class="m-setting__item"></div>
  <!-- 分隔符 -->
  <div class="u-divider"></div>
  <div class="m-setting__item"></div>
</div>
.m-setting {
  // 宽度为300px
  width: 300px;
  // 高度为100%
  height: 100vh;
  position: fixed;
  // 为了把这部分内容隐藏
  right: -300px;
  top: 0;
  z-index: 10;
}
.m-setting__bar {
  width: 48px;
  height: 48px;
  background: var(--theme);
  position: absolute;
  // 使这个按钮出现在屏幕上
  left: -48px;
  top: calc(50% - 24px);
}
// 当点击bar的时候使主体内容显示在屏幕上
.m-setting--open {
  right: 0;
}
// 把遮罩层显示
.m-setting--open + .u-mask {
  display: block;
}
.u-mask {
  display: none;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.4);
  position: fixed;
  left: 0;
  top: 0;
  z-index: 5;
}
var settingBar = document.querySelector('.m-setting__bar')
settingBar.onclick = function () {
  setting.classList.toggle('m-setting--open')
  if (setting.className.includes('m-setting--open')) {
    var icon = this.querySelector('i')
    icon.className = 'iconfont icon-close'
  } else {
    var icon = this.querySelector('i')
    icon.className = 'iconfont icon-setting'
  }
}

css的命名

通用样式

:root {
  --theme: #1890ff;
}
.show {
  display: block !important;
}
.hide {
  display: none !important;
}

通用布局

通用布局以g-ant-为开头

.g-ant {
  display: flex;
}
.g-ant__sider {}
.g-ant__main {}

模块

模块以m-为开头

// logo模块
.m-logo {}
// 菜单模块
.m-menu {}
.m-menu__title {}
.m-menu__sub {}
.m-menu__sub--selected{}
// 设置模块
.m-setting {}
.m-setting--open {}
.m-setting__bar {}

通用元件

通用元件是指一些公用的样式,比如一个分割线,一个遮罩层,一个radio和一个checkbox。下面来介绍一个radio如何实现的,效果如下:

实战ant-design-pro管理后台的样式

<div class="u-switch j-head--fixed">
  <div class="u-switch__handle"></div>
</div>
.u-switch {
  width: 28px;
  height: 16px;
  border-radius: 100px;
  background: var(--theme);
  cursor: pointer;
}
.u-switch--closed {
  background: rgba(0, 0, 0, 0.25);
}
.u-switch--closed .u-switch__handle {
  // 改变小圆的位置
  left: 14px;
}
.u-switch__handle {
  width: 12px;
  height: 12px;
  background: white;
  border-radius: 50%;
  position: relative;
  left: 2px;
  top: 2px;
}
var headFixed = document.querySelector('.j-head--fixed')
headFixed.onclick = function () {
   this.classList.toggle('u-switch--closed')
}

设置颜色主题

实战ant-design-pro管理后台的样式

<div class="m-setting__item">
  <h3>主题色</h3>
  <ul class="m-setting-item__theme">
    <li class="u-foxiaolan">
      <i class="iconfont icon-check"></i>
    </li>
    <li class="u-bomu"></li>
    <li class="u-huoshan"></li>
    <li class="u-rimu"></li>
    <li class="u-mingqing"></li>
    <li class="u-jiguanglv"></li>
    <li class="u-jikelan"></li>
    <li class="u-jiangzi"></li>
  </ul>
</div>
.u-foxiaolan,
.u-bomu,
.u-huoshan,
.u-rimu,
.u-mingqing,
.u-jiguanglv,
.u-jikelan,
.u-jiangzi {
  width: 20px;
  height: 20px;
  color: white;
  border-radius: 2px;
  cursor: pointer;
  line-height: 20px;
  text-align: center;
}
.u-foxiaolan {
  background: #188efc;
}
.u-bomu {
  background: #f5212d;
}
.u-huoshan {
  background: #fe531f;
}
.u-rimu {
  background: #fbae14;
}
.u-mingqing {
  background: #14c2c3;
}
.u-jiguanglv {
  background: #53c41a;
}
.u-jikelan {
  background: #2f54eb;
}
.u-jiangzi {
  background: #722ed1;
}

var themes = document.querySelectorAll('.m-setting-item__theme>li')
for (var i = 0; i < themes.length; i++) {
  themes[i].onclick = function () {
    // 先把里面的内容清空
    for (var i = 0; i < themes.length; i++) {
      themes[i].innerHTML = ''
    }
    // 然后把✔️加入进去
    this.innerHTML = '<i class="iconfont icon-check"></i>'
    // 获取所选取的颜色
    var color = getComputedStyle(this).backgroundColor
    // 设置颜色变量
    document.documentElement.style.setProperty('--theme', color)
  }
}

响应式

实战ant-design-pro管理后台的样式 实战ant-design-pro管理后台的样式

实战ant-design-pro管理后台的样式

实战ant-design-pro管理后台的样式

首先看看主体内容区如何实现响应式

<div class="index-main">
  <div class="m-card">
    <div class="m-card__title">活动实时交易情况</div>
    <div class="m-card__body">
      <div class="index-main__bg1"></div>
    </div>
  </div>
  <div class="m-card">
    <div class="m-card__title">活动情况预测</div>
    <div class="m-card__body">
      <div class="index-main__bg2"></div>
    </div>
  </div>
  <div class="m-card">
    <div class="m-card__title">券核效率</div>
    <div class="m-card__body">
      <div class="index-main__bg3"></div>
    </div>
  </div>
  <div class="m-card">
    <div class="m-card__title">各品类占比</div>
    <div class="m-card__body">
      <div class="index-main__bg4"></div>
    </div>
  </div>
  <div class="m-card">
    <div class="m-card__title">热门搜索</div>
    <div class="m-card__body">
      <div class="index-main__bg5"></div>
    </div>
  </div>
  <div class="m-card">
    <div class="m-card__title">资源剩余</div>
    <div class="m-card__body">
      <div class="index-main__bg6"></div>
    </div>
  </div>
</div>

.index-main {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	grid-template-rows: repeat(3, 285px);
	gap: 24px;
	margin: 24px;
	grid-template-areas:
		'a1 a1 a1 a2'
		'a1 a1 a1 a3'
		'a4 a4 a5 a6';
}
.index-main .m-card:nth-of-type(1) {
	grid-area: a1;
}

.index-main .m-card:nth-of-type(2) {
	grid-area: a2;
}

.index-main .m-card:nth-of-type(3) {
	grid-area: a3;
}

.index-main .m-card:nth-of-type(4) {
	grid-area: a4;
}

.index-main .m-card:nth-of-type(5) {
	grid-area: a5;
}

.index-main .m-card:nth-of-type(6) {
	grid-area: a6;
}
@media (max-width: 1200px) {
	.index-main {
		display: grid;
		grid-template-columns: repeat(3, 1fr);
		grid-template-rows: repeat(4, 285px);
		gap: 24px;
		margin: 24px;
		grid-template-areas:
			'a1 a1 a1'
			'a1 a1 a1'
			'a2 a3 a5'
			'a4 a4 a6';
	}
}

@media (max-width: 992px) {
	.index-main {
		display: grid;
		grid-template-columns: repeat(2, 1fr);
		grid-template-rows: repeat(5, 285px);
		gap: 24px;
		margin: 24px;
		grid-template-areas:
			'a1 a1'
			'a1 a1'
			'a2 a3'
			'a4 a4'
			'a5 a6';
	}
}

@media (max-width: 768px) {
	.index-main {
		display: grid;
		grid-template-columns: repeat(1, 1fr);
		grid-template-rows: repeat(6, 285px);
		gap: 24px;
		margin: 24px;
		grid-template-areas:
			'a1'
			'a2'
			'a3'
			'a4'
			'a5'
			'a6';
	}
}

接下来看看菜单栏如何响应式

@media (max-width: 992px) {
  .g-ant__sider {
    display: none;
    position: fixed;
    left: 0;
    top: 0;
    z-index: 6;
  }
  // g-ant__sider显示的时候展示遮罩层
  .g-ant__sider:is(.show) + .u-mask {
    display: block;
  }
}