定制app开发后台管理系统 - 页面布局设计

定制app开发前端的中后台管理系统定制app开发相比于其他普通项目,定制app开发从开发设计的角度来说定制app开发有几点比较特殊:

  • 定制app开发一个是权限设计,定制app开发具体实现可参考:。
  • 定制app开发一个是页面布局的设计,定制app开发也是本文要说的。定制app开发一个好的页面布局设计,无论是对于页面结构的稳定性,还是功能拓展的方便性,亦或是用户体验上,都有着重要的作用。

一、市面参考

先来看看市面上的一些优秀的开源系统项目的页面布局。

1、

是 vue 框架的一个优秀的后台管理系统,目前star数75k。

vue-element-admin采用的是侧边菜单布局,侧边菜单 + 顶部导航 + 内容区域,这也是我个人最推崇的布局方式。

2、 design pro

有提供三种布局。

(1)顶部菜单布局

即:顶部导航菜单 + 内容区域。

这种方式布局简单,但缺点很明显,菜单都挤在顶部导航区域,在菜单项越来越多时就放不下了,很难处理,可扩展性不强。

(2)侧边菜单布局

侧边菜单 + 顶部导航 + 内容区域。

同vue-element-admin类似,主要区别就是antd pro的面包屑导航是另起一行单独放的,这样挤压了内容区域的空间,个人觉得还是放在顶部和右上角的快捷按钮放同一行最好。

(3)混合菜单布局

其实和侧边菜单布局大同小异,还是属于侧边菜单布局的范畴。

只不过这样布局的话,面包屑导航就不适合和顶部放一行了,只能另起一行。

二、选型

参考市面上比较优秀的两款项目模板的布局后,个人还是觉得vue-element-admin的布局方式更胜一筹。

文本就围绕这种布局结构来设计。

  • 示例项目:
  • 技术栈:react 17 + antd 4 + react-router-dom 6 + ts
  • 路由统一管理使用 方案

效果图:

其实技术选型不那么重要,无论是react还是vue,element或是antd,思路一致,都只是实现代码的差异而已。

对于侧边栏菜单和面包屑导航,element和antd都有相应的组件可以直接使用,其他的手写实现。

三、css布局

良好的css布局代码才能保证页面布局的稳定性。

而对于整体布局来说,flex是首选,稳定性更好,不兼容ie9。

这里将整体布局封装成组件PageLayout

(1)首先,设置侧边栏右侧的盒子撑满屏幕剩余宽度。
flex布局有个特性是:只对一个子元素设置flex: 1属性时,该子元素默认会撑满父容器的剩余空间。

.c-PageLayout-index {  width: 100%;  height: 100%;  display: flex;  .appMainWrap {    height: 100%;    flex: 1; // 占据屏幕剩余宽度    position: relative;    padding-top: 50px; // 留出顶部导航栏区域,顶部导航栏使用悬浮置顶。  }  .appMain {    height: 100%;    overflow: auto;    padding: 15px; // 内容区域可以在这里统一设置下边距  }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • c-PageLayout-index 页面整体容器
  • appMainWrap 侧边栏右侧的(顶部导航区域 + 内容区域)容器
  • appMain 内容区域容器

(2)侧边菜单区域默认撑满高度,宽度可交给antd组件自适应,也可以自己设死。

.c-PageLayout-sideBar {  height: 100%;  overflow: auto;}
  • 1
  • 2
  • 3
  • 4

(3)顶部导航区域悬浮置顶。

.c-PageLayout-headBar {  height: 50px;  display: flex;  justify-content: space-between;  position: absolute;  top: 0;  right: 0;  width: 100%;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

四、侧边栏菜单

侧边栏的实现方式是难点,因为这里即涉及到如何和路由数据匹配,又涉及权限的筛选。

侧边栏最好是和路由配置共用一套数据,方便扩展和维护,这里得益于 已经封装好的路由管理方案(类vue-router),所以直接读取路由配置数据,动态生成菜单组件结构。

路由配置数据:

import PageLayout from '@/components/PageLayout'import { HomeOutlined } from '@ant-design/icons'const routes: RoutesTypeNew = [  {    path: '/',    element: <PageLayout />,    children: [      {        path: 'index',        component: () => import(/* webpackChunkName: "index" */ '@/views/index/index'),        meta: {          title: '首页',          icon: <HomeOutlined />,          accessId: '10000',        }      },    ]  },]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 如有点击跳转外链的菜单场景,可以在routes里添加个自定义的配置对象,例如通过url属性指代外链地址。

动态生成菜单:

// 递归获取层级菜单function getMenuList () {  const getList: any = (routeList = [], prePath = '') => {    let menuList: JSX.Element[] = []    routeList.forEach((v: RoutesItemTypeNew) => {      if (v.path === '/') {        menuList = menuList.concat(getList(v.children, '/'))      } else {        const currentPath = prePath + v.path        if (v.children) {          menuList.push((            <SubMenu key={currentPath} icon={v.meta.icon} title={v.meta.title}>              {getList(v.children, currentPath + '/')}            </SubMenu>          ))        } else {          menuList.push((            <ItemMenu key={currentPath} icon={v.meta.icon}>              <Link to={currentPath}>{v.meta.title}</Link>            </ItemMenu>          ))        }      }    })    return menuList  }  return getList(routes)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 如需要对权限路由做筛选,通过在路由配置数据meta里添加accessId字段作为路由权限id,然后在遍历routeList时,读取该accessId做权限判断。

五、面包屑导航

要使用面包屑导航,需要对路由路径配置有一定的约束规则,即,配置path路径时不要随意使用斜杠/划分,只通过嵌套路径自动划分路径。

这样才能对路由完整路径通过/分隔并匹配,来生成对应的面包屑导航数据。

首先,写个方法,遍历路由,生成路由路径和路由meta字段的映射数据:

function getRouteMetaMap () {  const getMap: any = (routeList = [], prePath = '') => {    let map = {}    routeList.forEach((v: RoutesItemTypeNew) => {      let currentPath = prePath + v.path      if (v.path === '/') {        currentPath = ''      } else {        map = {          ...map,          [currentPath]: v.meta || {}        }      }      if (v.children) {        map = {          ...map,          ...getMap(v.children, currentPath + '/')        }      }    })    return map  }  return getMap(routes)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 映射数据示例:
    {    "/index": {        "title": "首页",        "accessId": "10000"    },    "/nest": {        "title": "多级菜单",    },    "/nest/nest1": {        "title": "二级菜单1"    },    "/nest/nest1/nest11": {        "title": "三级菜单11"    }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

然后,获取当前路由完整路径(例如:/nest/nest1/nest11),再通过/分隔成多段子路由,和上述getRouteMetaMap方法取到的映射数据匹配,获取子路由的title标题组合成面包屑(多级菜单 / 二级菜单1 / 三级菜单11),展示出来。

const routeMetaMap = getRouteMetaMap()const pathSnippets = location.pathname.split('/').filter(i => i)const extraBreadcrumbItems = pathSnippets.map((_, index) => {  const url = `/${pathSnippets.slice(0, index + 1).join('/')}`  return (    <Breadcrumb.Item key={url}>      <span>{routeMetaMap[url].title}</span>    </Breadcrumb.Item>  )})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

本文示例项目源码:


参考链接:
https://panjiachen.gitee.io/vue-element-admin/
https://preview.pro.ant.design/form/basic-form

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发