• 前端进击笔记第十八节 实战:三天实现管理端系统


    前面几讲我介绍了前端应用开发中常见的工具原理,包括前端框架、前端路由等,还介绍了前端编程的一些开发技巧,比如使用数据驱动、组件化和模块化设计、数据抽象等。这些内容到底该怎么在实际工作中使用呢?

    今天,我会以最常见的管理端系统为例,带你用这些编程技巧来提升开发效率,三天实现一个管理端系统。

    搭建一个管理端系统包括以下工作:

    1. 管理端路由与功能设计;

    2. 页面功能设计与实现。

    由于技术选型并不是本节内容的重点,这里我将直接选择入门简单的 Vue、配套 Vue 热门 UI 框架,使用 Vue CLI + Vue + ElementUI + vue-router 来搭建管理端。关于技术选型的内容,你可以关注后续第 22 讲的内容。

    本文中我不会花过多的篇幅去描述 Vue 的一些基本概念和工具,包括 Vue 组件、vue-router、ElementUI 组件等,这些内容在官方网站上有很详细的介绍,因此我们专注于介绍搭建思路。

    下面,我们先来设计管理端功能吧!

    管理端路由与功能设计

    常见的管理端系统长这个样子:

    Drawing 0.png

    一般来说,管理端系统包括这些功能页面结构。

    图片6.png

    我们先来设计管理端左侧的菜单,将 2-4 的页面结构都整合进去:

    Drawing 1.png

    页面路由设计

    菜单内容以及对应的页面结构都确定之后,我们可以先来设计管理端的路由。这时,我们需要将应用进行适当的模块化和组件化拆分。

    为了更直观地进行说明,我给你梳理了页面路由和页面组件嵌套的关系图(使用框框框起来的代表一个 Vue 组件):

    图片7.png

    将页面和路由的关系梳理清楚,是很关键的一个步骤。

    根据上述的页面路由和嵌套关系,下面我们以/作为根路由,进行路由和页面组件的设计。

    图片8.png

    应用的各个页面和路由嵌套关系梳理完成之后,我们可以对项目文件结构进行划分。

    目录结构划分

    项目设计的时候,我们需要初步约定项目代码的组织结构。

    对于前端项目来说,项目根目录会区分开发代码(src)、编译后代码(dist)和常见的配置(Babel 配置、Eslint 配置、项目配置等)。对于开发代码(src),也会划分为静态资源(assets)、页面(page)、组件库(component)、公共库(util)等。如下述目录结构所示:

    ├─dist                      // 编译之后的项目文件
    ├─src                       // 开发目录
    │  ├─assets                 // 静态资源
    │     ├─less                // 公共less
    │     ├─img                 // 图片资源
    │  ├─components             // **放这里是组件**
    │  ├─pages                  // **放这里是页面** 根据路由结构划分
    │  ├─utils                  // 工具库
    │  ├─App.vue                // 启动页面,最外层容器组件
    │  ├─main.js                // 入口脚本
    ├─babel.config.js          // babel 配置文件
    ├─vue.config.js            // vue 自定义配置,与 webpack 配置相关
    ├─package.json             // 项目配置
    ├─README.md                // 项目说明
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    项目的目录结构是否有规范约束、是否结构清晰,对一个项目的可维护性非常重要。通过目录结构,我们可以直观地看到项目中包括了哪些代码、分别都放在哪里。

    好了,项目目录和路由结构我们划分好了,我们来看看怎么根据上面的设计来配置路由,以及实现相互跳转。

    路由配置与开发

    Vue 框架本身的定位是核心关注视图层,所以路由配置、状态管理、测试工具等都不是自带的,我们需要自己找到对应的开源库配合使用。Vue 官方推荐的工具是vue-router

    根据以上的嵌套关系,我们可以设置最外层的根路由为"/",并加上其他嵌套子路由配置,比如登录页面、列表页和详情页等。

    // 配置路由信息
    // 每个路由应该映射一个组件。 其中"component" 可以是
    // 通过 Vue.extend() 创建的组件构造器,
    // 或者,只是一个组件配置对象。
    const routes = [
      {
        path: "/", // 父路由路径
        component: App, // 父路由组件,传入 vue component
        name: "App", // 路由名称
        // 设置子路由
        children: [
          {
            path: "login", // 子路由路径
            component: Login, // 子路由组件,会替换父组件中中的内容
            name: "Login", // 路由名称
          },
          {
            // 应用首页
            path: "home",
            component: Home,
            name: "Home",
            children: [
              // 服务列表
              { path: "service", component: Service, name: "Service" },
              // 产品容器
              {
                path: "product",
                component: Product,
                name: "Product",
                children: [
                  // 子路由内容
                  // 产品列表
                  { path: "list", component: ProductList, name: "ProductList" },
                  // 产品新增
                  { path: "add/0", component: ProductEdit, name: "ProductAdd" },
                  // 产品编辑
                  // 我们能看到,新增和编辑其实最终使用的是同一个组件,所以后面会有一些需要兼容处理的地方
                  // :id可匹配任意值,且可在组件中通过this.$route.params.id获取该值
                  { path: "edit/:id", component: ProductEdit, name: "ProductEdit" },
                ],
              },
            ],
          },
        ],
      },
    ];
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    上述的路由配置中涵盖了页面的路由嵌套关系,在 Vue 中可以使用组件,将渲染路径匹配到具体的视图组件,视图组件还可以内嵌自己的匹配子路由路径,从而渲染嵌套组件。

    比如,Home.vue页面中包括左侧菜单和右侧内容,在右侧内容中可以使用来嵌套子路由的组件进行渲染。

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    其中,在左侧的在菜单

    中,可以使用 Vue 中的组件,来绑定路由的跳转能力,进行页面的导航。

    路由的配置完成后,我们可以将路由的能力添加到应用中。

    在第 12 讲中,我们介绍了前端路由库都会支持两种路由模式:Hash 和 History。由于 History 模式需要后台配合,因此这里使用 Hash 模式来加载路由。

    在 Vue 中可以通过将router配置参数注入路由,给应用添加上路由功能,比如

    // 加载路由信息
    const router = new VueRouter({
      // mode: 路由模式,'hash' | 'history'
      // routes:传入路由配置信息,后面会讲怎么配置
      routes, // (缩写)相当于 routes: routes
    });
    // 启动一个 Vue 应用
    new Vue({
      el: "#app",
      router, // 传入路由能力
      render: (h) => h(App),
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    通过新建VueRouter,并在Vue中传入该路由示例,便可以给应用添加路由能力,这就是 vue-router 的基本功能。

    除此之外,vue-router 还提供了路由监控能力、鉴权能力等,可以结合实现非登录页的登录状态鉴权,比如使用 vue-router 的router.beforeEach导航守卫能力,当用户未登录时,则拒绝进入其他路由页面里:

    router.beforeEach((to, from, next) => {
      if (to.name !== "Login") {
        // 非 login 页面,检查是否登录
        if (!isUserLogin) {
          // 未登录则跳转去 login 页面
          next({ name: "Login" });
        }
      }
      // 其他情况正常执行
      next();
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样,我们就可以在用户未登录时,拦截所有通往内页的操作,并重新定向到登录页面。

    到这里,我们应用基本具备了登录、导航的能力。当然,这只是个静态页面,距离真正上线,我们还需要进行接口的联调、代码打包、发布上线等工作。

    管理端路由与功能设计过程中,我们分别使用了前端框架、前端路由以及组件化和模块化的设计。

    下面我们会结合数据抽象与数据驱动的方式,来进行页面内功能的具体设计与实现。

    页面功能设计与实现

    前面我们已经将管理端划分成多个模块和页面,接下来我会对页面进行组件的拆分,结合数据抽象的方式进行组件的设计。

    管理端页面的主要包括左侧菜单、列表和表单,我们可以按照这样的结构进行组件设计。

    下面我们先来分别对它们进行数据抽象和设计。

    菜单设计

    菜单的最终效果如图,这里会包括父菜单和子菜单两层结构。

    Drawing 3.png

    其中,每个父菜单需要展示以下的内容:

    • 图标icon

    • 菜单名字text

    • (可选)子菜单列表subMenus,以及子菜单名字text

    列表可以用数组来表示,因此我们可以将菜单组件的数据抽象为以下的数据结构(数组+对象):

    const menus = [
      {
        text: "服务管理", // 父菜单名字
        icon: "el-icon-setting", // 父菜单图标
        subMenus: [{ text: "服务信息" }, { text: "新增" }], // 子菜单列表
      },
      {
        text: "产品管理",
        icon: "el-icon-menu",
        subMenus: [{ text: "产品信息" }],
      },
      {
        text: "日志信息",
        icon: "el-icon-message",
      },
    ];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    当我们将菜单用这样的数据结构表示以后,实现 UI 的时候就可以轻松地通过数据绑定的方式来进行。此处使用了 Elmenet 的菜单组件如下所示:

    
    
      
      
    
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    如果需要绑定路由,我们还可以添加上一个路由的绑定信息。

    前面我们说过 vue-router 中可以使用组件来实现路由跳转,你可以试试看要怎么做,文末会有源码地址和最终页面效果参考哦。

    下面我们来看看列表和表单的设计。

    列表和表单设计

    我们看看列表的最终效果:

    图片9.png

    我们能看到,列表里每行内容包括这些信息:

    • 日期:date

    • 姓名:name

    • 电话:phone

    • 地址:address

    在列表中的每一个数据项,还需要使用一个唯一标识来作为标记(id),便于用户增删查改时进行标识。

    我们同样可以使用对象的方式来描述列表中的数据项:

    const tableItem = {
      id: 123,
      date: "2019-05-20", // 日期
      name: "被删", // 姓名
      phone: "13888888888", // 电话
      address: "深圳市南山区滨海大道 888 号", // 地址
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    那么列表则是由以上对象结构的数据组成的数组,我们可以使用 Element-Table 组件来绑定列表的 UI 展示。

    
    
      
      
      
      
      
      
      
      
      
      
      
      
        
      
    
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    可以看到,列表中支持了选项的删除、上下移动操作,当我们将页面抽象为数据之后,页面的功能可以对应于数据的如下操作。

    • 删除:删除数组中的某个对象

    • 位置移动:改变数组中对象的排序

    这些操作会改变并更新页面中的数据,使用 Vue 可以直接绑定数据操作的方法:

    export default {
      data() {
        // 绑定数据
        return {
          menus: menus, // 菜单数据
          tableData: tableData, // 列表数据
        };
      },
      methods: {
        // 新增一个数据
        addTableItem(item = {}) {
          // 添加到列表中,同时自增 id
          this.tableData.push({ ...item, id: this.tableData.length + 1 });
        },
        // 删除一个数据
        deleteTableItem(id) {
          // 查找到对应的索引,然后删除
          const index = this.tableData.findIndex((x) => x.id === id);
          this.tableData.splice(index, 1);
        },
        // 移动一个数据
        moveTableItem(id, direction) {
          const dataLength = this.tableData.length;
          // 查找到对应的索引,然后取出,再插入
          const index = this.tableData.findIndex((x) => x.id === id);
          switch (direction) {
            // 上移
            case "up":
              if (index > 0) {
                // 第一个不进行上移
                const item = this.tableData.splice(index, 1)[0];
                this.tableData.splice(index - 1, 0, item);
              }
              break;
            // 下移
            case "down":
              if (index < dataLength - 1) {
                // 最后一个不进行下移
                const item = this.tableData.splice(index, 1)[0];
                this.tableData.splice(index + 1, 0, item);
              }
              break;
          }
        },
      },
    };
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    由于使用了 Vue 框架,当我们绑定的数据发生变化的时候,框架会自动帮我们更新到页面里(具体实现可以参考第 10 讲)。

    列表常用于数据项的查看,而表单则通常用于对数据项的编辑和修改。对于同一个数据项来说,表单的数据结构与列表中数据项的结构是一致的,同样可以使用上述的对象结构来表达。

    使用 Element-Form 组件将表单的数据进行绑定之后,就可以直接进行编辑了:

    Drawing 5.png

    到这里,我们已经实现了一个带菜单、列表和表单的页面了,单列表和单表单的页面同样可以通过数据抽象设计+UI 组件绑定的方式来实现。

    这个页面也有挺多可以完善的地方,例如:

    • 左侧菜单可以支持收起;

    • 列表支持修改;

    • 列表支持批量删除;

    • 表单支持校验手机号和其他选项不为空。

    这些就当作课后作业来完成,最终的实现可以参考以下链接:

    小结

    今天我带你使用开源前端框架、前端路由库,通过模块划分、组件设计、数据抽象的方式来快速搭建实现管理端。

    虽然文章标题是三天实现管理端,实际上如果熟练之后,这些工作一天就能完成。

    或许你会觉得,这管理端也太简单了吧。在实际工作中,大家也都会对管理端系统感到苦恼:管理端多半是增删改查的东西,做多了就会变成重复性的工作。

    其实管理端的开发也可以不只是复制粘贴,我们还可以对管理端的主要功能进行抽象,然后通过配置化的方式来配置完成。如果实现了管理端的配置化,我们就可以从重复烦琐的工作中解放出来,去做更多有意思的事情。

    在日常工作中,对于前端应用的实现,是否可以使用更好的方式、又可以怎样去在重复的工作中提升自己呢?我认为这些思考才是最重要的,这决定了我们只是一个会用工具的工具人,还是一个可以用工具去改变工作方式的思考者。


    精选评论

    *锐:

    腾讯也是用得vue技术栈吗, 我们是不是可以在上方加个tab标签页,这样对用户更友好点

        讲师回复:

        腾讯里不同团队的技术栈不一样,各种框架都有在使用的。用户需要的话,可以加上 tab 的~

  • 相关阅读:
    ArcGIS中ArcMap导入mxd地图文档文件出现红色感叹号、地图空白的解决
    什么是GPT磁盘?介绍GPT(GUID 分区表)磁盘及其优势!
    【计算智能】读书笔记 第八章节 分布估计算法
    Apache Doris 系列: 基础篇-Stream Load
    OS 处理机调度
    mac制作ssl证书|生成自签名证书,nodejs+express在mac上搭建https+wss(websocket)服务器
    STM32 TIM(一)定时中断
    Pycharm 常用快捷键
    vue 放大镜(简易)
    软件开发项目文档系列之四如何成功撰写一份引人注目的投标文件
  • 原文地址:https://blog.csdn.net/fegus/article/details/126315896