基于 umi 从零搭建超实用的后台管理系统(git clone 下来就可以用的那种)

551 阅读3分钟

「Offer 驾到,掘友接招!我正在参与2022春招系列活动-项目经验复盘,点击查看 征文活动详情

创建

执行命令yarn create @umijs/umi-app通过官方工具创建项目,安装依赖,启动项目。

浏览器 http://localhost:8000/

image-20220302172931007.png

页面就是如此简单。


先来看一下我们实现的效果图

image.png

可以说是非常符合管理系统的样子。

图片.png

关于 umi 的基础知识,这里就不做过多的介绍了。相关的知识点,网上应该都是可以搜索到的。

路由部分

src/routes.ts,导出 router 对象。

import {
  HddOutlined,
  HourglassOutlined,
  ApartmentOutlined,
} from "@ant-design/icons";

export const routesMaps = [
  {
    name: "page1",
    path: "/page1/view1",
    label: "页面一",
    Icon: ApartmentOutlined,
    children: [
      {
        name: "page1-children",
        path: "/page1/view1/list",
        component: "@/pages/Page_one/List",
        label: "子页面",
        exact: true,
      },
    ],
  },
  {
    name: "page1-list-redirect",
    path: "/page1/list",
    component: "@/pages/Page_one/List",
    label: "个人工作台-重定向",
    exact: true,
    redirect: "/page1/list",
    menu: false,
  },
  {
    name: "page1-detail",
    path: "/page666/detail/:id/:type/:state",
    component: "@/pages/Page_one/Detail",
    label: "页面详情",
    menu: false,
  },
  {
    name: "page2",
    path: "/page2/view2",
    label: "页面二",
    Icon: HddOutlined,
    children: [
      {
        name: "page2-list",
        path: "/page2/view2/list",
        component: "@/pages/Page_two/List",
        label: "子页面",
        exact: true,
      },
      {
        name: "page2-panel",
        path: "/page2/view2/list2",
        component: "@/pages/Page_two/Panel",
        label: "另外一个子页面",
        exact: true,
      },
      {
        name: "Page_two-detail",
        path: "/page2/view2/detail/:id/:type/:state",
        component: "@/pages/Page_two/Detail",
        label: "没有出现在 menu 中的详情页面",
        menu: false,
      },
    ],
  },
  {
    name: "page3",
    path: "/page3/view1",
    label: "页面三",
    Icon: HourglassOutlined,
    children: [
      {
        name: "page3-list",
        path: "/page3/view1/list",
        component: "@/pages/Page_three/List",
        label: "子页面",
        exact: true,
      },
      {
        name: "page3-vvvvv",
        path: "/page3/view1/:id/:type/:state",
        component: "@/pages/Page_three/Detail",
        label: "没有出现在 menu 中的子页面",
        menu: false,
      },
    ],
  },
  {
    name: "page10",
    path: "/page10/view10/:id/:type/:state",
    component: "@/pages/Page_four/Detail",
    label: "没有出现在 menu 中的子页面",
    menu: false,
  }
];
  • exact: true 需要校验的页面
  • redirect: "/page1/list" 重定向页面
  • menu: false 不显示在侧边栏

重设浏览器的样式

最简单的初始化方法就是: * {margin: 0;padding: 0; }。这样写确实很快,但如果网站很大、CSS样式表文件很大的时候,用*号这样一个通用符来写初始化样式,浏览器会把所有的标签都初始化一遍,这样就大大地加强了网站运行的负载,会使网站加载的时候需要很长一段时间。

src/global.less

@import "~antd/lib/style/themes/default.less";
@import "~antd/dist/antd.less";

/* css reset code */
body {
  font-size: 62.5%;
}

html>body {
  font-size: 10px;
}

body,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
form,
fieldset,
input,
textarea,
p,
blockquote,
th,
td {
  margin: 0;
  padding: 0;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
}

address,
caption,
cite,
code,
dfn,
th,
var {
  font-weight: normal;
  font-style: normal;
}

ol,
ul {
  list-style: none;
}

caption,
th {
  text-align: left;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: normal;
  font-size: 100%;
}

q:before,
q:after {
  content: "";
}

abbr,
acronym {
  border: 0;
}

a {
  text-decoration: none;
}

.ml-10 {
  margin-left: 10px;
}

.ant-form-item-explain {
  margin-top: 0;
}

.ant-form .ant-picker-range {
  width: 100%;
}

侧边导航栏

src/layouts/index.tsx

import React from 'react';
import type { history as umiHistory } from 'umi';
import { Layout, ConfigProvider } from 'antd';
import zh_CN from 'antd/es/locale/zh_CN';
import Menu from './Menu';

import './index.less';

const isInIframe = window.top !== window.self;

interface MenuProps {
  history: typeof umiHistory;
  children: React.ReactNode;
}

export default (props: MenuProps) => {
  const { children, history } = props;

  return (
    <ConfigProvider locale={zh_CN}>
      <Layout className="x-layout">
        {isInIframe ? null : <Menu history={history} />}
        <Layout className="x-layout-main">{children}</Layout>
      </Layout>
    </ConfigProvider>
  );
};

<Menu history={history} />

image.png

沿用 Antd 的经典布局

<Sider
  trigger={null}
  collapsible
  collapsed={false}
  className="example-sider"
>
  <Row justify="center" align="middle" className="header-wrapper">
    <span className="main-title"></span>
  </Row>
  <Menu
    pathname={history.location.pathname}
    routesMaps={routesMaps}
    onClick={handlerClick}
    mode="inline"
    theme="dark"
  />
</Sider>

Menu 是基于 Antd Menu 二次封装的。

页面上方的上下班状态使用 Antd 的 Switch 进行控制。

主要的接口还是需要看具体的业务。

<div className="example">
  <Row className="example-header" align="middle" justify="end">
    {isTeamMember && (
      <Switch
        checkedChildren="上班"
        unCheckedChildren="下班"
        checked={workStatus}
        onChange={handleWorkStatusChange}
        className="switch"
      />
    )}
    <Text style={{ fontSize: 13 }}>Hi,{userNameZh}</Text>
  </Row>
  <div className="content-area">{children}</div>
</div>

我想想还有什么没说的,

其实封装还是需要根据自身的业务情况来执行,

不过,我将自己在项目中的实践抽离出来,

上方展示的项目代码我放在了 gitee.com/suiboyu/umi… 仓库中。

各位 git clone,安装依赖之后,尝试运行起来。

项目中也有示例代码。