likes
comments
collection
share

后台管理系统总结

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

后台管理系统

一、项目内容

1. 项目搭建

记录一下是怎么开始一个项目的

环境搭建

  1. 环境的配置包括两个方面:

    • node安装

      主要作用是本地服务器呈现网页效果,方便开发

      解决node下载慢的问题:

      • 使用yarn

        安装yarn

        npm i -g yarn
        
      • 安装国内镜像

        由于node是访问的国外服务器,访问速度较慢

        所以可以修改为国内淘宝镜像站

        npm config set registry https://registery.npmmirror.com/
        
    • vue脚手架安装

      脚手架用于快速创建项目的基本框架

    通常使用yarn来配置依赖,因为yarn弥补了npm下载慢、版本不统一的问题

    同时yarn会生成yarn.lock文件记录安装的依赖的相关信息等

  2. 注意安装的版本问题:

    • vue-cli对node的版本限制:从官方文档查
    • vue-cli本身的版本:版本的不兼容会导致编译错误

创建项目

  1. 创建命令

    cmd

    vue create <projectname>
    
  2. 注意点

    • 不能驼峰命名

      只能使用下划线或短线连接

    • 当前项目使用的是vue2

  3. 文件目录

    • 通常包含以下几个文件

      后台管理系统总结

      dist:打包后的项目文件夹

      public:通常放置网页的图标、主页面等

      src:包含assets/components/routers

      • routers存放路由,可以看成是路由表吧
      • views存放页面
    • package.json

      项目配置文件

      • scripts属性

        自定义命令

安装依赖

主要是按照官方文档来

  1. UI框架

    项目中使用element-ui的框架,根据官方文档指示来安装

  2. vue-router

    项目中使用适应于vue3.x版本的router,按照vue官方文档安装,但注意指定版本

    vue3.x适用于vue2

    vue4.x适用于vue3

    如果直接使用install指令,安装的是最新版本,可能无法兼容

    查询当前版本的方法

    • npm官网
    • 官网搜索:vue-router并选择versions标签
    • 在package.json中查看安装的依赖版本

2. eslint语法提示

  1. 问题

    编译时抛出错误:组件name属性必须是组合词 后台管理系统总结

  2. 原因

    代码检查工具eslint的语法提示

    eslint相关在后面

  3. 解决方法

    在配置文件中关闭组件名称的语法提示

    //package.json
    {
      ...
      "eslintConfig": {
        ...
        "rules": {
          "vue/multi-word-component-names": 0
        }
      },
      ...
    }
    

    解决结果 后台管理系统总结

  4. 相关&扩展

    • eslint为什么不能直接关掉?

      eslint,代码检查工具,检查代码规范代码风格

      对个人,可以帮助让代码更规范,可读性高了也容易维护

      对团队,统一规范代码风格团队成员间更容易协作,代码也好维护

    • 发现修改package.json后需要新建终端,否则仍会报错,为什么?

      • npm run serve发生了什么?

        -> 输入指令npm run serve

        -> npm在package.json的scripts中找对应指令

        后台管理系统总结

        -> 实际上执行的是 npm run vue-cli-service serve 指令

        这个命令本身的作用是启动一个本地开发服务器,用于开发和调试Vue项目

        -> 此时,相当于用户和操作系统之间建立了一个对话窗口(称为shell)

        用户输入这个指令,操作系统根据指令的路径(称为环境变量Path)去查找路径下的可执行文件并执行

        在此之前,npm已经在环境变量Path加了一个前缀node_modules/.bin​​,即操作系统查找的实际路径是node_modules/.bin/vue-cli-service​​,也即实际上在shell上实际执行的指令是node_modules/.bin/vue-cli-service serve​​​​

        后台管理系统总结

        node_modules/.bin目录下对应的文件

        • .cmd文件(可执行文件) 后台管理系统总结
        • .ps1文件 后台管理系统总结
      • 延伸知识点:

        • node_modules/.bin目录下的文件都是干嘛的?

          放置项目依赖的可执行文件,npm install​​所安装的依赖都放置在该目录下 后台管理系统总结

        • 可不可以用npm run vue-cli-service serve​​代替npm run serve​​呢?

          不行,npm中没有这个指令,注意上面说的原理“去package.json”中找对应指令

          但是可以直接在命令行执行node_modules/.bin/vue-cli-service serve​​指令,因为npm run serve​​本质就是它

      • 解释原问题:修改package.json后为什么得新建终端才行?

        新建终端,在这里实际上是关掉了原本开启的用于调试的本地服务器,然后重新运行

        执行npm run serve​​指令后,会启动一个本地开发服务器,该服务器会监听文件的变化,文件变化时会重新编译打包项目并刷新页面

        但是package.json​​文件只会在执行npm​​指令时生效,不会对正在运行的本地开发服务器产生影响

        npm run serve​​打包的文件包括页面文件、组件文件、入口文件,以及package.json​​中配置的依赖文件,也会被打包进代码文件

        package.json​​文件本身不会被打包进代码文件,因此修改了其中的配置需要重新执行指令

项目运行流程

对项目文件运行的顺序流程和为什么需要分成这么多文件好奇

  1. 项目文件运行顺序

    • index.html

      vue项目入口文件:

      作用:页面基本结构&引入打包后的JS和CSS文件

      位置:在目录的public路径下

      文件内容:下面文件代码是由vue创建项目时的初始状态,即还未经过npm run build​​打包的项目入口文件

      <!--public/index.html-->
      <!DOCTYPE html>
      <html lang="">
        <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width,initial-scale=1.0">
          <link rel="icon" href="<%= BASE_URL %>favicon.ico">
          <title><%= htmlWebpackPlugin.options.title %></title>
        </head>
        <body>
          <noscript>
            <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
          </noscript>
          <div id="app"></div>
          <!-- built files will be auto injected -->
        </body>
      </html>
      

      文件分析:

      • vue实例挂载点:

        <div id="app"></div>​​是vue应用程序的挂载点,也就是vue实例的挂载点;在main.js文件中的

        new Vue({
          router,
          render: h => h(App),
        }).$mount('#app')
        

        就是将vue实例挂载到index.html的这个挂载点上

      • noscript​​标签

         <noscript>
             <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
         </noscript>
        

        浏览器不支持JS或者用户禁用JS时会将这个标签的内容显示在页面,提示用户需要启用JS才能正常使用该网站

        项目标题的替换是webpack插件html-webpack-plugin完成的

      • 引入JS文件?

        • 何时生成?

          用脚手架创建项目时,自动生成index.html文件;

          而只有在npm run build​​打包项目代码时才会在index.html中引入JS文件;所以在文件初始状态下,看不到<script src="/dist/js/app.js">​​语句引入JS文件

        • 引入了什么? 后台管理系统总结

    • main.js

      作用:项目的主文件&入口文件之一

      内容:Vue实例化&挂载、引入路由、引入插件、引入组件库等

      类似于一本书的目录和致读者的东西,介绍了这个项目有啥、用了啥

      文件内容:大概看个框架吧

      //src/main.js
      import Vue from 'vue'
      import App from './App.vue'
      //引入路由
      import router from './routers'
      //引入组件库
      import ElementUI from 'element-ui';
      import 'element-ui/lib/theme-chalk/index.css';
      
      Vue.config.productionTip = false;
      Vue.use(ElementUI);
      
      //创建实例
      new Vue({
        router,
        render: h => h(App),
      }).$mount('#app')
      
    • App.vue

      作用:项目的根组件

      内容:页面基本结构&路由视图的渲染

      我的理解:其实就是主页面,可以理解成index.html是画板,app.vue是画纸,其他组件就是画纸上的东西

      不过可以在这个文件中规定一些全局的CSS样式

      在vue3中就将app和vue实例融合了,不用多套一层,更简洁一些

      文件内容:

      <!--src/App.vue-->
      <template>
        <div id="app">
          <router-view></router-view>
        </div>
      </template>
      
      <script>
      
      export default {
        name: 'App',
        components: {
         
        }
      }
      </script>
      
      <style>
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      </style>
      
    • 路由组件

      作用:根据路由的配置进行加载和渲染

      我的理解:因为main.js中引入了路由组件,即引入了routers文件夹,所以会自动去执行/routers/index.js文件

      这个步骤应该是和App.vue几乎同时进行的,因为App.vue需要使用到路由映射

      index.js文件如下

      //src/routers/index.js
      import Vue from 'vue'
      import VueRouter from 'vue-router'
      
      import Main from '../views/Main.vue'
      import Home from '../views/Home.vue'
      import User from '../views/User.vue'
      
      Vue.use(VueRouter)
      
      //路由映射
      export default new VueRouter({
          routes: [
              {
                  path: '/',
                  component: Main,
                  children: [
                      {
                          path: '/home',
                          component: Home
                      },
                      {
                          path: '/user',
                          component: User
                      }
                  ]
              }
          ]
      })
      
    • 其他组件&其他JS文件&CSS文件

      作用:根据需求进行加载和渲染

  2. 思考&拓展

    • render函数完整写法?

      见 H1-VueRouter函数再认识

    • npm run build发生了什么?

      见下打包项目过程

3. 项目打包过程

npm run build怎么打包&给index.html写了什么

  1. 清空之前打包生成的文件夹,一般是 dist​​​ 文件夹
  2. 根据配置文件 vue.config.js​​​ 中的设置,进行静态资源的打包,如图片、字体等
  3. 通过 webpack 对项目中的代码进行编译打包,生成 JavaScript 代码文件、CSS 文件、HTML 文件等
  4. 对打包生成的文件进行压缩和优化,如使用 Gzip 压缩等
  5. 最终将打包生成的文件输出到指定的文件夹中,一般是 dist​​​ 文件夹
  6. 执行 npm run build​​​​ 后,我们就可以将打包生成的文件部署到服务器上,使其能够被用户访问

4. 组件库产生黑盒问题

现成的组件框架由于某些接口原理是未知的,所以会产生一些莫名的问题

自带样式

  1. 问题

    • 问题1:用UI库的时候发现有些组件由默认样式但是找不着改的地方,发现用组件文件的style标签中也没有,但是渲染到页面上就会添加上这个样式
    • 问题2:在组件文件的style标签中设置该样式并不会生效

    源代码

    <!--views/Main.vue-->
    <template>
      <div>
    
        <el-container>
    
          <el-aside>
            <common-aside />
          </el-aside>
    
          <el-container>
            <el-header>
               <common-header />
            </el-header>
            <el-main>
              <router-view></router-view>
            </el-main>
          </el-container>
    
        </el-container>
    
      </div>
    </template>
    
    <script>
    //引入组件库中的侧边栏组件
    import CommonAside from "../components/CommonAside.vue";
    import CommonHeader from "../components/CommonHeader.vue";
    
    export default {
      name: "",
      components: {
        CommonAside,
        CommonHeader
      },
    }
    </script>
    
    <style>
    
    </style>
    
  2. 分析

    • 问题1:莫名其妙的样式哪里来?

      UI库会将某些组件的样式作为一个属性并且赋默认值,例如 后台管理系统总结

      el-aside​​​​​组件将width​​​​​作为了一个属性,因此并不是用style="width: 200px"​​​​​的形式设置样式

      查看官方文档也可知 后台管理系统总结 因此,就算把el-aside​​​​​中的width​​​​​属性删掉,这个组件还是会有300px的宽

      从开发者面板也可知 后台管理系统总结

    • 问题2:为啥用style改不掉?

      chatGPT这样解释

      如果组件库将组件 A 的宽度作为一个属性,并将其默认值设置为 300px,则这个属性很可能是使用 CSS 变量或者 JavaScript 代码实现的,而不是使用普通的 CSS 属性(例如 width)。如果是使用 CSS 变量实现的话,那么你需要使用该变量名来修改组件的宽度。

      例如,在使用 CSS 变量时,你可以在根元素中定义一个变量 --component-a-width 来表示组件 A 的宽度,并在组件的样式表中使用该变量来设置宽度。在这种情况下,要将组件 A 的宽度设置为 200px,你应该这样做:

      :root {
        --component-a-width: 200px;
      }
      
      .component-a {
        width: var(--component-a-width, 300px); /* 使用 CSS 变量 */
      }
      

      上面的代码中,我们在根元素中定义了一个名为 --component-a-width 的变量,并将其值设置为 200px。然后,我们在组件 A 的样式表中使用 var() 函数来引用这个变量,并将其作为组件宽度的值。由于我们已经在 :root 中为该变量设置了默认值 300px,所以如果没有给该变量赋值,组件 A 的宽度将自动设置为 300px。

      另外,值得注意的是,如果你直接在组件的样式表中使用 width 属性来设置组件的宽度,这可能会被组件库内部的 CSS 样式所覆盖,导致你的样式无法生效。如果已经确认组件库是以这种方式封装组件的话,建议在阅读组件文档时了解其提供的修改组件样式的方法,并按照组件库的规范进行设置。

      因此,直接使用如下的方式是无法生效的

      <style>
      .el-aside{
        width: auto;
      }
      </style>
      

      开发者面板中可以发现自己设置的样式并没生效 后台管理系统总结

  3. 解决方法

    按照UI库的规矩来,用width​​​​​属性设置

          <el-aside width="auto">
            <common-aside />
          </el-aside>
    

    所以还是要多查官方文档

    注意:

    当组件库没有把样式封装成组件的一个属性时,通常还是可以直接通过CSS进行样式修改的,就是说在style中进行修改,可以通过开发者面板查看样式

  4. 延伸

    CSS3中的自定义变量

组件的隐藏

  1. 问题描述

    • 前提:

      • 使用组件库的弹窗dialog​组件时,组件有自带一个visible​属性,用于设置组件是否可见

      • 弹窗组件内有一个表单form​组件,可以用于提交数据

      • 表单组件用ref​属性进行绑定,便于在js中对其进行操作

      • 表单组件内数据用对象form​进行双向数据绑定

      • 只有当点击页面的增加和编辑按钮时,才会将弹窗的visible​属性值改为true​,即弹窗可见;其他时候弹窗隐藏

      • 现在的需求是:每次关闭弹窗时,都要将弹窗中的表单的数据清空;关闭弹窗的方式包括点击弹窗的取消和确定按钮,以及弹窗右上角的叉叉 后台管理系统总结

        <!--弹窗内嵌表单的template内容-->
        <!-- 新增按钮点击后出现的表单:用于新增表格数据 -->
            ...
            <!--dialogFormVisible用来控制表单是否显示-->
            <el-dialog
              title="新增课程"
              :visible.sync="dialogFormVisible"
              :before-close="handleClose"
            >
              <el-form :model="form" :rules="rules" ref="form">
                <el-form-item
                  label="课程名称"
                  :label-width="formLabelWidth"
                  prop="name"
                >
                  <el-input v-model="form.name" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item
                  label="课程时间"
                  :label-width="formLabelWidth"
                  prop="date"
                >
                  <el-select v-model="form.date" placeholder="请选择时间">
                    <el-option label="星期一" value="Mon"></el-option>
                    <el-option label="星期二" value="The"></el-option>
                    <el-option label="星期三" value="Wed"></el-option>
                    <el-option label="星期四" value="Thu"></el-option>
                    <el-option label="星期五" value="Fri"></el-option>
                    <el-option label="星期六" value="Sat"></el-option>
                    <el-option label="星期日" value="Sun"></el-option>
                  </el-select>
                </el-form-item>
                <el-form-item
                  label="课程地点"
                  :label-width="formLabelWidth"
                  prop="region"
                >
                  <el-select v-model="form.region" placeholder="请选择地点">
                    <el-option label="A栋教学楼" value="A"></el-option>
                    <el-option label="B栋教学楼" value="B"></el-option>
                    <el-option label="C栋教学楼" value="C"></el-option>
                    <el-option label="D栋教学楼" value="D"></el-option>
                    <el-option label="E栋教学楼" value="E"></el-option>
                    <el-option label="F栋教学楼" value="F"></el-option>
                    <el-option label="G栋教学楼" value="G"></el-option>
                  </el-select>
                </el-form-item>
              </el-form>
              <div slot="footer" class="dialog-footer">
                <el-button @click="dialogFormVisible = false">取 消</el-button>
                <el-button type="primary" @click="submitForm">确 定</el-button>
              </div>
            </el-dialog>
            ...
        
        //弹窗相关js部分
        export default {
          data() {
            return {
              //提交表单的相关配置
              dialogFormVisible: false,
              formLabelWidth: "120px", 
              //存放表单数据的form对象
              form: {
                name: "",
                date: "",
                region: "",
              },
              //表格数据
              tableData: [],
              ...
            };
           methods: {
            ...
            //新增-新增按钮
            handleAdd() {
              this.dialogFormVisible = true;
              this.resetForm();
            },
            //提交表单数据-弹窗的确定按钮
            submitForm() {
              //检验表单是否有输入
              this.$refs["form"].validate((valid) => {、
                //关闭弹窗
                this.dialogFormVisible = false;
                 ... //一堆对请求操作
                //将表单清空
                this.resetForm();
              
              });
            //关闭弹窗前的操作-弹窗的叉叉
            handleClose() {
              this.dialogFormVisible = false;
              this.resetForm();
            },
            //清空表单数据
            resetForm() {
              //利用ref属性获取form元素节点进行清空操作
              this.$refs["form"].resetFields();
            },
            },
         };
        
    • 发现问题:

      当点击新增按钮或者关闭弹窗等会报错:表示找不到this.$ref​下的form后台管理系统总结

  2. 分析

    • this.$refs.form​究竟是啥?

      • 查看vue的开发者工具,发现当弹窗的dialogFormVisible​为true时,页面中不存在弹窗及其内嵌的表格元素

        即组件库将组件隐藏的方法不是设置css样式display: none​,而是通过将组件从页面中移除

        那么表单组件不存在,this.$refs["form"]​也不会存在,对表单的清空操作也实现不了

      • 所以,清空表单和关闭弹窗的操作要有先后顺序

        即先清空表单再关闭弹窗

        //以其中方法一个为例
            submitForm() {
              //检验表单是否有输入
              this.$refs["form"].validate((valid) => {
                 ... //一堆对请求操作
                //先将表单清空
                this.resetForm();
                //再关闭弹窗
                this.dialogFormVisible = false;
              });
            //新增-新增按钮则相反
            handleAdd() {
              //先有弹窗容器再清空表单
              this.dialogFormVisible = true;
              this.resetForm();
            },
        
    • 还是会有问题?

      因为弹窗的出现需要重新解析模板,再将组件dom挂载在页面上

      当我们获取this.$refs​时,可能dom节点还没有完全加载完成

      所以需要使用this.$nextTick(() => {})​,使页面渲染好再清空数据

         //清空表单数据
          resetForm() {
            console.log(this.$refs)
            this.$nextTick(() => {
              //等渲染好了再找这个节点
              this.$refs["form"].resetFields();
            });
          },
      

接口歧义

  1. 问题描述

    • 接上一个问题,同样出现了form没有被清空的问题

    • 比如,先点击了表格中张三这一行的编辑按钮,按钮绑定的事件会将张三的信息填在表单form中

      修改张三信息并提交,再点击新增按钮,发现表单form中出现了修改前的张三信息

  2. 分析

    个人认为是接口的歧义问题

    查看官方文档,我所使用的重置表单数据的方法是将表单数据重置为初始值 后台管理系统总结

    可能的逻辑是,因为一开始第一次点击的按钮时编辑按钮,编辑按钮对应的事件将张三数据赋给了form表单

    而组件就将这个数据当做了form的初始值,因此每次调用重置方法后都会将表单填上这个初始值

  3. 解决方法

    手动清除表单

    //User.vue
    ...
        //清空表单数据
        resetForm() {
          //等渲染好了再找这个节点
          this.$nextTick(() => {
            //手动清空
            for (let key in this.form) {
              this.form[key] = "";
            }
          });
        },
    

5. 组件名使用规范

模板中使用组件时,注意代码规范

  1. 模板<template>​中使用组件

    即应用到dom时

    模板使用组件:名称用统一小写加短线代替驼峰

    即使用<common-aside>​而不是<CommonAside>

    <template>
      <div class="">
        <el-container>
          <!-- 侧边栏 -->
          <el-aside width="auto">
            <common-aside />
          </el-aside>
        </el-container>
      </div>
    </template>
    
  2. <script>​中使用组件

    引入时仍可以使用驼峰

    import CommonAside from '../components/CommonAside.vue'
    
    export default {
      ...
      components: {
        CommonAside
      },
      ...
    }
    
  3. 分析

    原因是html对大小写并不敏感,在解析dom模板时,会将所有大写的字符转化为小写

    • 如果使用CommonAside​,则会转化为commonaside

      而注册组件时是使用CommonAside​,则会报错组件找不到

    • 如果使用common-aside​,则能够对应上注册的组件

6. 样式穿刺

感觉不是一种特别经常使用的写法,而且会导致代码的样式层叠杂乱,所以还是避免使用

  1. 问题描述

    使用了第三方组件库的组件,要修改组件的样式,发现无法生效

    <!--src/components/CommonHeader.vue-->
    <template>
          <!-- 面包屑显示的路径 -->
          <el-breadcrumb style="margin-left: 20px" separator="/">
            <el-breadcrumb-item
              v-for="item in tags"
              :key="item.path"
              :to="{ path: item.path }"
              >
              {{ item.label }}
             </el-breadcrumb-item>
          </el-breadcrumb>
    </template>
    
    <style lang="less" scoped>
    .el-breadcrumb__item {
        .el-breadcrumb__inner {
          font-weight: normal;
          &.is-link {
            color: #666;
          }
          &:last-child {
            .el-breadcrumb__inner {
              color: white;
            }
        }
    }
    </style>
    
  2. 分析

    • 在vue中为了避免多文件中的组件起了相同的class类名而产生样式污染的问题,会在style标签中添加scoped​属性,表示该样式只针对当前文件的组件生效

    • 而vue解析、渲染模板的最后是将所有文件打包成一个html文件,为了区分不同文件的相同组件,vue会给有重复的组件添加特殊的标识符,类似于身份证的字符串后台管理系统总结

      如上面的data-v-c1f1971a​字符串等

    • 如果使用了第三方组件库中的组件,样式是由组件库方写好的,所以vue并没有再给组件内的标签再添加标识符

      检查项目中的代码后台管理系统总结 发现el-breadcrumb_inner​并没有标识符,因此仅仅通过选择器.el-breadcrumb_inner​给它设置样式是没办法生效的,只有组件库默认的样式

  3. 解决方案

    使用深度选择器,即样式穿透/样式穿刺写法,在需要穿刺的选择器前面添加字段/deep/

    /deep/.el-breadcrumb__item {
          .el-breadcrumb__inner{
            font-weight: normal;
            &.is-link {
              color: #666;
            }
          }
          &:last-child {
            .el-breadcrumb__inner {
              color: white;
            }
          }
    }
    

    添加样式穿刺标识的选择器对应的组件,其子组件都会被穿刺,也就是说,对自身和其子组件的样式都会生效

    因此,在此处需要添加到最外层的选择器.el-breadcrumb__item​,这样其子组件.el-breadcrumb__item​和​ &:last-child​都会生效

    注意其中的&​符号表示其父组件,等同于css中的.el-breadcrumb__item :last-child​写法

7. 登录权限&token验证

token验证

用mock生成token,用js-cookie验证token

  1. 用插件js-cookie​​生成验证的token​​

    • 安装插件

      npm i js-cookie
      

      项目中使用的版本是3.0.1

    • 引入并使用插件

      在登录页面中使用

      //src/views/Login.vue
      import Mock from 'mockjs'
      import Cookie from 'js-cookie'
      
      export default {
        data() {
          return {
            //登录的信息
            form: {
              username: "",
              password: "",
            },
            ...
          };
        },
        methods: {
          //登录请求
          handleLogin() {
            this.$refs["form"].validate((valid) => {
              //valid表示输入框有输入
              if (valid) {
                login(this.form).then(({ data }) => {
                  //status==0表示登录成功
                  if (data.status == 0) {
                    //生成token并将token存入浏览器的cookie中
                    //用Mock生成一段随机字符串
                    const token = Mock.Random.guid()
                    //将token存入cookie
                    Cookie.set('token', token)
                    //跳转到主页面
                    this.$router.push({name: 'home'})
                  } 
                  //登录失败
                  else {
                    alert("账号或密码错误");
                  }
                });
              } 
              //输入框没有输入,提示用户输入
              else {
                console.log("error submit!!");
                return false;
              }
            });
          },
        },
      };
      
    • 配置守卫导航

      用于拦截跳转的操作,检查是否有token

      //src/main.js
      //引入cookie
      import Cookie from 'js-cookie'
      
      //添加全局前置导航守卫,要写在实例挂载之前
      //判断cookie是否存在
      router.beforeEach((to, from, next) => {
        const token = Cookie.get('token')
        //token不存在且要跳转的页面不是登录页面,则跳转至登录页面
        //防止在登录页的死循环跳转
        if (!token && to.name != 'login') {
          next({ name: 'login' })
        }
        //token存在且是从登录页面来的
        //只有是在登录页面发出登录请求才符合条件
        else if (token && to.name == 'login') {
          next({ name: 'home' })
        } 
        //其他情况
        else {
          next()
        }
      })
      
      ...
      
      new Vue({
        router,
        store,
        render: h => h(App),
      }).$mount('#app')
      

不同用户不同访问权限

导航栏显示不同内容

  1. 实现的基本逻辑

    • 用户登录账号,根据后端返回的用户和管理员身份区分码,确定身份

      在这个项目中的逻辑是后端直接返回应该显示的导航栏数据,我认为不对,这部分数据应该是由前端来写的

    • 将身份对应的导航栏内容存入store和Cookie中

      存入Cookie中是因为store.state的数据会因为浏览器刷新而重新初始化,所以要存入缓存,后面会详细说明

    • 在导航栏组件中读取导航栏内容

  2. 实操

    • 编写store中用于缓存导航栏数据的mutations

      //store/tab.js
      import Cookie from 'js-cookie'
      export default {
          state: {
              menu: []
          },
          mutations: {
              //不同用户的不同menu
              setMenu(state, menu) {
                  state.menu = menu;
                  //将菜单存入浏览器缓存中,防止每次刷新后数据丢失
                  //注意要将数据转化为字符串
                  Cookie.set('menu', JSON.stringify(menu))
              }
          }
      }
      
      //store/index.js
      import Vue from 'vue'
      import Vuex from 'vuex'
      
      import tab from './tab'
      
      Vue.use(Vuex)
      
      export default new Vuex.Store({
          //模块化管理
          modules: {
              tab
          }
      })
      
    • 用户登录,并存入到导航栏数据

      注意此处的导航栏数据是由后端返回

      //Login.vue
      import { login } from "../api/index"
      import Cookie from "js-cookie"
      
      export default {
        ...
        methods: {
          //登录请求
          handleLogin() {
            //发送登录请求
            login(this.form).then(({data}) => {
             ... //登录成功后
                    //将不同身份对应的不同菜单栏项存入store.tab中
                    this.$store.commit('setMenu', data.menu);
                    //跳转到页面
                    this.$router.push({ name: "home" });
            ... //后续其他处理
            })
          },
        },
      };
      

      其中,data数据段后台管理系统总结

    • 在导航栏组件中获取导航栏数据

      //src/components/CommonAside.vue
      export default {
         ...
        computated: {
            //不同用户不同导航栏呈现
            menuData(){
              //判断当前数据是否在缓存中
              //先在缓存中找,缓存中有说明已经登录过;
              //缓存中没有,说明是初次登录,菜单栏存在state中
              //注意要将字符串转回js对象数组
              return JSON.parse(Cookie.get('menu')) || this.$store.state.tab.menu
          }
        }
      }
      
  3. 分析

    为什么需要将数据存在Cookie缓存?

    • store中的state是保存在内存中的,只要浏览器页面不被关闭,它们的值就会一直存在

    • 刷新浏览器页面,store中的state数据将会被初始化为初始值

      因为在刷新浏览器时,页面和所有已加载的JavaScript代码都会被重新加载,包括Vuex store实例

      因此,当store实例被重新创建时,其中的状态(state)也会被重新初始化为初始值

    • 为了避免在刷新浏览器后丢失store的state数据,可以使用一些技术来将其持久化存储

      如使用浏览器的本地存储(localStorage)或其他外部存储库

      这样可以确保在下一次加载页面或重新打开浏览器后,数据仍然存在并可以被恢复到之前的状态

限制部分路由的跳转

动态注册路由

  1. 问题描述

    • 不同的登录身份会呈现不同导航菜单,也只能跳转特定的页面

    • 也就是说对VueRouter()​中route​的配置(即路由表)不能是写死的,而是先验证身份,再动态配置的

      //src/routers/index.js
      import Vue from 'vue'
      import VueRouter from 'vue-router'
      
      export default new VueRouter({
         routes: [] //里面不能写死数据,不然什么身份都能通过修改路径进行页面跳转
      })
      
  2. 解决方案

    使用vue封装好的动态注册路由的接口,动态添加路由到路由表中

    const userMenu = [
            {
                path: "/home",
                name: "home",
                label: "首页",
                icon: "s-home",
                url: "Home/Home",
            },
            {
                path: "/user",
                name: "user",
                label: "用户管理",
                icon: "user",
                url: "UserManage/UserManage",
            },
    ];
    
    //动态添加路由表内容
    router.addRoutes(userMenu)
    

    需要注意页面刷新后vue实例会被重新初始化,可能会出现路由表被重置为初始值的情况

    所以需要在Vue实例被创建后,再重新调用方法来获取路由表

    //src/main.js
    ...
    new Vue({
      router,
      store,
      render: h => h(App),
      create(){
        router.addRoutes(userMenu)
      }
    }).$mount('#app')
    

8. 拉取项目依赖版本问题

  1. 问题描述

    在github中找到一个较好的后台管理项目vue-admin-beautiful,在拉取到本地安装依赖时出现错误

  2. 分析

    • package.json​中指定的版本号是不确定的,初次安装依赖时会导致可能安装到最新版本
    • 而新版本的A可能与旧版本的B不兼容,因此会报错
  3. 解决1

    根据搞错提示调整版本

    至于应该调整到什么版本比较好,实在不太明白,目前也没想到好方法

    • 查看报错信息后台管理系统总结 就是在说,发现echarts​版本和vue-echarts​版本不太配,要么升级一个要么降一个

      最后三句是在说暴力破解,啥也不管,直接强制使用当前版本、使用旧版本,接收错误的解析结果后台管理系统总结

      一般不推荐这些方法,可能导致其他问题

    • 查找适合的版本

      npm官网查找相应的版本信息,搜索框输入要查找的插件名/模块名后台管理系统总结 选择versions​一项查找合适的版本 后台管理系统总结

    • 进行版本修改

      两种方法:删除原模块再重新安装或修改package.json​中的版本信息再安装

  4. 解决2

    解决1虽然听着行得通,但是实际操作时会牵一发而动全身,引发更多问题导致最后没办法运行

    原因是本质上我不知道到底哪个版本才是适合的,胡乱升降版本是没有依据的

    最后的解决方法。。。

    删除package.json中版本前的^​符号 + 使用报错信息给的指令 后台管理系统总结

    $ npm i --legacy-peer-deps
    

    前一个指令则不行,仍报错

  5. 拓展

    版本控制符号

    • ~​符号

      表示只允许安装到指定版本的最新小版本更新

      ~1.2.3​表示安装从1.2.3​开始到1.3.0​(不包括1.3.0​)之间的最新版本

    • ^​符号

      表示只允许安装到指定版本的最新大版本更新

      ^1.2.3​表示安装从1.2.3​开始到2.0.0​(不包括2.0.0​)之间的最新版本。

二、JS/ES6/vue

1. != 与 !==

  1. 两个都是用于比较两个值是否不相等的操作符

  2. !=

    不严格相等运算符:如果两个值在类型转换后不相等,则返回true

    • 1 != "1"将返回false,因为在比较之前,"1"被转换为数字1
    • null != undefined将返回false,因为这两个值在布尔上下文中被视为相等
    • "hello" != true,将返回true,因为在比较之前,true被转换为数字1,而"hello"被转换为NaN(无法转换为数字),这两个值不相等。
  3. !==

    严格不相等运算符:**比较两个值的类型和值,**只有在两个值的类型和值都不相等时才会返回true

    • 1 !== "1"将返回true,因为它们的类型不同
    • null !== undefined也返回true

2. 解构赋值

解构赋值的基本语法包括使用花括号 {}​ 或方括号 []​ 来匹配要提取的数据结构,并将其赋值给对应的变量

  1. 数组解构赋值

    // 基本语法:
    let [a, b] = [1, 2];
    console.log(a); // 输出 1
    console.log(b); // 输出 2
    
    // 可以忽略其中某些元素:
    let [c,,d] = [3, 4, 5];
    console.log(c); // 输出 3
    console.log(d); // 输出 5
    
    // 支持剩余运算符 ...
    let [e, ...f] = [6, 7, 8, 9];
    console.log(e); // 输出 6
    console.log(f); // 输出 [7, 8, 9]
    
  2. 对象解构赋值

    // 基本语法:
    let {foo, bar} = {foo: "hello", bar: "world"};
    console.log(foo); // 输出 "hello"
    console.log(bar); // 输出 "world"
    
    // 也可以使用别名:
    let {name: n, age: a} = {name: "Tom", age: 20};
    console.log(n); // 输出 "Tom"
    console.log(a); // 输出 20
    
    // 可以使用默认值:
    let {x = 0, y = 0} = {x: 1};
    console.log(x); // 输出 1
    console.log(y); // 输出 0
    
  3. 注意点

    • key的匹配

      被解构的对象中的key和被赋值的对象中key不一定需要完全相同,但是它们需要匹配

      即如果被解构的对象中存在一个key,而被赋值的对象中不存在该key,则该变量将被赋值为undefined

      const person = { name: 'John', age: 30 };
      const { name, gender } = person;
      console.log(name); // 输出 'John'
      console.log(gender); // 输出 undefined
      

      上面的代码将person​对象进行解构,并将结果赋值给name​和gender​变量

      由于person​对象中不存在gender​这个key,所以gender​变量的值为undefined

3. render函数

  1. 问题

    在main.js中的render函数作用是什么

    //src/main.js
    ...
    new Vue({
      router,
      render: h => h(App),
    }).$mount('#app')
    
  2. 分析

    • 上面这段代码是Vue的构造函数,Vue构造函数传入一个对象作为参数

      //传入的对象
      {
        router,
        render: h => h(App),
      }
      

      该对象中包含el/data/components等的Vue实例的选项和配置,而render就是配置之一,只不过render配置的是一个函数,而其他可能是配置布尔值、对象、数组等

    • render函数,渲染函数,用于帮助Vue创建虚拟DOM节点

    • render函数完整写法

      render: function (createElement) {
        return createElement(App)
      }
      

      其中,render配置的是一个函数,称为render函数;

      render函数再传入一个函数createElement​​作为参数,这个参数实际上是Vue内置的函数,可以传入一个组件或html标签,返回值为一个虚拟DOM(称为VNode)

      返回的虚拟DOM被Vue接收,Vue将其转化成真实DOM插入页面中

      而render函数常用简写形式:

      render: h => h(App)
      

      其中h即createElement​​函数,参考箭头函数简写方式

    • Vue渲染页面的流程中,vue先解析模板,将指令、插值表达式等转换成虚拟DOM节点;

      然后Vue再根据虚拟DOM节点创建一个虚拟DOM树(每个组件都有虚拟DOM树);

      而由于组件之间相连,最终都归到App这一个组件中去了,所以最终传入createElement的只有App组件;也就是说App组件是根,其他组件都与根相连,App被插入页面,其他组件也会被带动插入页面

4. 常用的数据处理方法

见Home.vue

查找元素

  1. arr = Object.keys(object)

    将可迭代对象中的键值对的键取出组成数组并返回

    任何可迭代对象都可以使用该方法

    const orderData = { 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5 };
    const xAxis = Object.keys(orderData);
    //则 xAxis = ['A', 'B', 'C', 'D', 'E']
    
  2. arr.forEach((item)=>{ })

    遍历数组,传入参数为一个回调函数,当前遍历的项会被传入该回调函数

    该方法没有返回值

    const arr = [];
    const xAxis = ['A', 'B', 'C'];
    const xAxis.forEach((item) => {
            arr.push({
              name: item,
              data: 111
            });
    });
    // arr = [ {name: 'A', data: 111}, {name: 'B', data: 111}, {name: 'C', data: 111} ]
    
  3. Object.map((item)=>{ })/arr.map((item)=>{ })

    也是遍历数组的方式,类似于forEach()​,不同的是这个方法有返回值,返回新创建的数组

    const arr = [1, 2, 3, 4]
    const num = arr.map((item) => item * 2);
    // num = [1, 2, 3, 4]
    
  4. const index = arr.findIndex(var => var.name == name)

    根据特定条件查找元素并返回元素下标

    当匿名函数中的判断成立时,会返回一个该元素的index

    const name = 'B'
    const arr = [ {name: 'A', data: 111}, {name: 'B', data: 111}, {name: 'C', data: 111} ]
    const index = arr.findIndex((var) => var.name == name)
    // index = 1
    

删除元素

  1. arr.splice(1,1)​​

    删除从下标为1开始的1个元素

    返回值为被删除的元素构成的数组

    const arr = [0, 1, 2, 3, 4]
    arr.splice(1, 2)
    // arr = [0, 3, 4]
    

筛选元素

  1. arr.filter()

    筛选数组中符合条件的元素集合

    参数为一个回调函数,被遍历的当前元素会作为参数传入该回调函数

    const numbers = [1, 2, 3, 4, 5, 6];
    
    const evenNumbers = numbers.filter(function(number) {
      return number % 2 === 0;
    });
    
    // evenNumbers = [2, 4, 6]
    

三、axios/node.js

1. Promise的then回调机制

axios是基于promise的机制:使用时发现对promise仍不够熟悉

  1. promise理解&在axios中应用

    • 在axios中会定义一个请求函数,返回值类型为promise对象,例如

      export const getData = ()=>{
          return app.get('/home/getData') //此处已经定义了baseURL,所以直接写路径
      }
      
    • promise对象本质由两个函数构成:resolve​​​和reject​​​,resolve​​​在promise状态为fulfilled​​​(异步操作成功)时被调用,返回数据;reject​​​在promise状态为rejected​​​(异步操作出错)时被调用,返回出错信息

      见promise的构造函数

      let myPromise = new Promise((resolve, reject) => {
        ...// 异步操作
        let result = performAsyncOperation(); //异步操作后的结果
        if (result) {
          resolve(result); // 将 Promise 对象状态设为 "fulfilled",并返回异步操作的结果
        } else {
          reject(new Error("Operation failed!")); // 将 Promise 对象状态设为 "rejected",并返回错误信息
        }
      });
      
    • 因此,上面例子中getData​​请求数据操作即为promise中的异步操作,请求成功函数则会返回后端给的数据,请求出错则会返回错误信息;通常把getData​​也叫作一个接口

  2. then的回调机制?

    • then接收两个回调函数

      promise.then( res=>{ 
            ...//对res操作 
        },
        rej => {
            ...//对rej操作 
      })
      
    • promise调用then方法后会根据promise的状态调用then中的回调函数并将数据传给该回调函数

      promise为fulfilled​​:调用第一个回调,将resolve​​返回值传给这个回调;即res​​为resolve​​的返回值

      promise为rejected​​:调用第二个回调,将rejected​​返回值传给这个回调;即rej​​为rejected​​的返回值

2. axios二次封装

就是对axios进行通用配置,以及将axios的各个部分功能分成模块

  1. 全局配置

    src/utils/request.js,其中utils文件夹下是工具模块,放的都是工具项目的类似于入口文件的东西

    //src/utils/request.js
    //axios的通用配置封装
    import axios from 'axios'
    
    //进行axios请求的配置
    const http = axios.create({
        //通用请求的地址前缀
        baseURL: '/api',
        //请求超时
        timeout: 10000
    })
    
    //配置拦截器
    //请求拦截器
    http.interceptors.request.use(function (config) {
        return config;
    }, function (error) {
        return Promise.reject(error)
    })
    //响应拦截器
    http.interceptors.response.use(function (config) {
        return config;
    }, function (error) {
        return Promise.reject(error)
    })
    
    //对外暴露
    export default http
    

    其中,添加拦截器的作用是什么?

    这个项目中只是进行了默认配置,config即为默认的请求配置

    • 请求拦截器:发送请求前进行操作,比如在请求头携带token等
    • 响应拦截器:接收响应前进行操作,比如对数据进行统一处理、检查响应码并在响应码为401这些时进行统一跳转操作等
  2. 接口封装

    src/api/index.js:将项目中所有要用到的发起请求的操作都封装在这个文件中,其他所有要发起请求的地方直接调用这些封装好的函数中即可(类似于前端的接口)

    //src/api/index.js
    //定义前端的请求接口
    //也就是说,将每一种数据请求放在该文件中,而不是放在页面代码中
    //组件只需要引入该文件就能够调用函数来请求数据
    import http from '../utils/request'
    
    //登录页面
    //Login.vue使用
    export const login = (data) => {
        return http.post('login/login', data)
    }
    
    //请求首页几个图表的数据
    //Home.vue使用
    export const getData = () => {
        //返回一个promise对象
        return http.get('home/getData')
    }
    
    //用户管理页面的接口
    //User.vue使用
    export const getTableData = (params) => {
        return http.get('user/getTableData', params)
    }
    export const addTableData = (data) => {
        return http.post('user/addTableData', data)
    }
    export const editTableData = (data) => {
        return http.post('user/editTableData', data)
    }
    export const delTableData = (data) => {
        return http.post('user/delTableData', data)
    }
    

    此处发起的请求路径和参数等的配置是在Mock模拟后端数据模块,这里只需要知道用了axios.get()​和axios.post()​等axios接口发起请求即可

    组件使用封装好的请求接口:引入接口 + 调用函数并对返回的promise进行操作

    import { getData } from '../api/index'
    
    export default {
       ...
       mounted() {
        //将返回的数据中的data解构出来
        getData().then(({ data }) => {
          //拿出所有有用的数据
          const val = data;
          ...
       }
       ...
    }
    

3. Mock模拟后端接口

模拟数据&接口

用mock.js工具来模拟后端的数据接口

此处模拟创建一个get和post请求

  1. 安装mock

    npm i mockjs
    
  2. 写后端数据

    • 将数据模块化

      在src下新建文件夹mockServerData,文件夹中存放的数据文件

      每一个文件都对应一个请求要返回的数据

      后台管理系统总结

    • 写数据文件

      //src/api/mockServerData/home.js
      //存放要给home页面的数据
      import Mock from 'mockjs'
      
      //图表数据
      let List = []
      export default {
          getStatisticalData: ()=>{
              //Mock.Random.float随机生成数字
              for(let i = 0; i < 6; i++){
                  List.push(
                      Mock.mock({
                          数据结构: Mock.Random.float(3.0, 5.0, 0, 0),
                          操作系统: Mock.Random.float(3.0, 5.0, 0, 0),
                          计算机组成: Mock.Random.float(3.0, 5.0, 0, 0),
                          计算机网络: Mock.Random.float(3.0, 5.0, 0, 0),
                          编译原理: Mock.Random.float(3.0, 5.0, 0, 0),
                      })
                  )
              }
              return {
                  code: 20000,
                  data: {
                      //饼图
                      videoData: [
                          {
                              name: '德',
                              value: 96
                          },
                          {
                              name: '智',
                              value: 89
                          },
                          {
                              name: '体',
                              value: 83
                          },
                          {
                              name: '美',
                              value: 89
                          },
                          {
                              name: '劳',
                              value: 83
                          }
                      ],
                      //柱状图
                      userData: [
                          {
                              date: 'MON',
                              new: 5,
                              active: 200
                          },
                          {
                              date: 'TUE',
                              new: 8,
                              active: 560
                          },
                          {
                              date: 'WED',
                              new: 6,
                              active: 340
                          },
                          {
                              date: 'TUE',
                              new: 3,
                              active: 150
                          },
                          {
                              date: 'FRI',
                              new: 5,
                              active: 490
                          },
                          {
                              date: 'SAT',
                              new: 4,
                              active: 180
                          },
                          {
                              date: 'SUN',
                              new: 5,
                              active: 250
                          },
                      ],
                      //折状图
                      orderData: {
                          date: ['2-1', '2-2', '2-2', '3-1','3-2'],
                          data: List
                      },
                      tableData: [
                          {
                              name: '数据结构',
                              today: 1,
                              month: 2,
                              total: 3
                          },
                          {
                              name: '操作系统',
                              today: 4,
                              month: 5,
                              total: 6
                          },
                          {
                              name: '计算机组成',
                              today: 7,
                              month: 8,
                              total: 9
                          },
                          {
                              name: '计算机网络',
                              today: 10,
                              month: 11,
                              total: 12
                          },
                          {
                              name: '编译原理',
                              today: 13,
                              month: 14,
                              total: 15
                          }
                      ]
                  }
              }
          }
      }
      
      //src/spi/mockServerData/login.js
      //用户登录页面返回的数据
      const admin = {
          massage: '登录成功(管理员)',
          status: 0,
          menu: [
              {
                  path: "/home",
                  name: "home",
                  label: "首页",
                  icon: "s-home",
                  url: "Home/Home",
              },
              {
                  path: "/mall",
                  name: "mall",
                  label: "商品管理",
                  icon: "video-play",
                  url: "MallManage/MallManage",
              },
              {
                  path: "/user",
                  name: "user",
                  label: "用户管理",
                  icon: "user",
                  url: "UserManage/UserManage",
              },
              {
                  name: "other",
                  label: "其他",
                  icon: "location",
                  children: [
                      {
                          path: "/page1",
                          name: "page1",
                          label: "设置1",
                          icon: "setting",
                          url: "Other/PageOne",
                      },
                      {
                          path: "/page2",
                          name: "page2",
                          label: "设置2",
                          icon: "setting",
                          url: "Other/PageTwo",
                      },
                  ],
              },
          ],
      }
      const user = {
          massage: '登录成功(用户)',
          status: 0,
          menu: [
              {
                  path: "/home",
                  name: "home",
                  label: "首页",
                  icon: "s-home",
                  url: "Home/Home",
              },
              {
                  path: "/user",
                  name: "user",
                  label: "用户管理",
                  icon: "user",
                  url: "UserManage/UserManage",
              },
          ],
      }
      const ERROR = {
          massage: '登录失败',
          status: -1
      }
      
      export default {
          login: (data) => {
              const info = JSON.parse(data.body)
              // console.log("login info", info);
              if ((info.username == 'admin') && (info.password == '123456')) {
                  return admin
              }
              else if((info.username == 'user') && (info.password == '123456')){
                  return user
              }
              else {
                  return ERROR
              }
          }
      } 
      
  3. 配置mock配置文件

    • src目录下创建mock.js

      后台管理系统总结

    • 配置mock.js文件

      //src/api/mock.js
      //后端数据的“api文档”
      //可以查看、配置每个请求的路径和请求的方法
      //引入Mock,注意此处mockjs没有点
      import Mock from 'mockjs'
      //引入封装好的数据文件
      import loginApi from './mockServerData/login'
      import homeApi from './mockServerData/home'
      
      //定义mock进行数据的请求回应
      //此处属于后端的配置,传入的第三个参数是后端数据中的方法,实际与前端无关
      //前两个参数前端可以用于参考,进行请求的发起:相当于第一个是接口,第二个是方法
      //登录页面的登录请求
      Mock.mock('/api/login/login', 'post', loginApi.login)
      //主页面图表数据
      Mock.mock('/api/home/getData', 'get', homeApi.getStatisticalData)
      
    • 主文件引入mock

      //src/main.js
      ...
      //引入mock
      import './api/mock'
      ...
      
  4. 检查请求是否成功

    当页面请求数据时,就会返回写好的数据

    后台管理系统总结

是否携带参数对请求的影响

  1. post请求:携带参数

    前端封装的接口,​**data进行参数接收**

    //src/api/index.js
    import http from '../utils/request'
    
    export const login = (data) => {
        return http.post('login/login', data)
    }
    

    Mock的mock.js文件,不需要体现参数的传递

    //src/api/mock.js
    Mock.mock('/api/login/login', 'post', loginApi.login)
    

    定义后端数据的文件中,用data接收参数

    //src/api/mockServerData/login.js
    export default {
        login: (data) => {
            const info = JSON.parse(data.body)  //取出数据并将数据从字符串转化为json格式数据
            if ((info.username == 'admin') && (info.password == '123456')) {
                return admin
            }
            else if((info.username == 'user') && (info.password == '123456')){
                return user
            }
            else {
                return ERROR
            }
        }
    } 
    

    注意此处的传过来的参数是一个包装好的对象,且参数为字符串,所以需要对数据进行处理

    后台管理系统总结

  2. get请求:

    • 不携带参数

      即为普通写法

      //src/api/index.js
      export const getData = () => {
          //返回一个promise对象
          return http.get('home/getData')
      }
      
      //src/api/index.js
      import http from '../utils/request'
      
      Mock.mock('/api/home/getData', 'get', homeApi.getStatisticalData)
      
      //src/api/mockServerData/home.js
      export default {
          return {
                  code: 20000,
                  data: {
                      //饼图
                      videoData: [
                          {
                              name: '德',
                              value: 86
                          },
                          {
                              name: '智',
                              value: 90
                          },
                          {
                              name: '体',
                              value: 53
                          },
                          {
                              name: '美',
                              value: 42
                          },
                          {
                              name: '劳',
                              value: 75
                          }
                      ],
           ...
      }
      
    • 携带参数

      前端封装的接口,​**params进行参数接收**

      //src/api/index.js
      import http from '../utils/request'
      
      export const getTableData = (params) => {
          return http.get('user/getTableData', params)
      }
      

      Mock的mock.js文件,不需要体现参数的传递

      但是注意**此处的路径要使用正则表达式,**因为get请求携带参数是通过url?key=value​的形式,所以需要对路径进行动态匹配

      //src/api/mock.js
      Mock.mock(/api\/user\/getTableData/, 'get', userApi.getTableData)
      

      定义后端数据的文件中,用data接收参数

      //src/api/mockServerData/user.js
      export default {
         getTableData: (params) => {
              // console.log("params", params.url.split('='))
      
              let limit = parseInt(params.url.split('=')[1].split('&')[0]);
              let page = parseInt(params.url.split('=')[2]);
              // console.log("limit", limit, "page", page);
      
              let data = {
                  tableData: tableData,
                  pageData: tableData.slice((page - 1) * limit, page * limit)
              }
              return data
          },
      } 
      

      此时传递过来的数据是这样的

      后台管理系统总结 所以需要从data.url​中截取出数据