当前位置:   article > 正文

Eladmin框架前端后台学习笔记

eladmin

Eladmin框架前端后台学习

前端源码:

https://github.com/elunez/eladmin-web

https://gitee.com/elunez/eladmin-web

项目简介

个简单且易上手的 Spring boot 后台管理框架。我们可以通过这个框架快速的构建出一个前端搭载element-ui组件库,带有权限控制的后台管理系统。

前台页面千变万化,需求不同展现出的效果也不同,我们很难去找出一套模板供大家去参考使用,但后台管理系统则与之不同,他们的需求有很多都是存在共性的。

就像最常见的几种管理模式

  • 多级角色进行层级管理
  • 基于权限的新增、删除、修改等操作
  • 基于角色的动态路由展示
  • 岗位管理
  • 部门管理
用户账号密码
  • 管理员: admin
  • 测试用户: test
  • 密码: 123456
运行:
# 配置镜像加速
https://www.ydyno.com/archives/1219.html

# 安装依赖
npm install

# 启动服务 localhost:8013
npm run dev

# 构建生产环境
npm run build:prod
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
技术栈
  • Vue
  • vue-router
  • axios
  • element ui

项目结构

|-- public 存放静态资源,存放在该文件夹的东西不会被打包影响,而是会原封不动的输出到dist文件夹中
    |-- favicon.ico 网站图标
    |-- index.html 主页,项目入口
|-- src
    |-- api  后端请求接口文件
    |-- assets  静态资源
    |-- components  公用组件
    |-- layout  系统布局:头部、侧边栏、设置、中间内容页面
    |-- mixins  混入文件
    |-- router  路由配置
    |-- store  vuex存放数据
    |-- utils  工具包
    	|-- auth.js	授权管理
    	|-- clipboard.js 简单的剪切板
    	|-- datetime.js Date对象补充函数
        |-- index.js 获取页面标题
        |-- permission.js 权限管理工具方法
        |-- request.js 拦截器
        |-- rsaEncrypt.js 加密、解密
        |-- shortcuts.js 日期工具补充
        |-- upload.js  上传函数
        |-- validate.js 一些正则验证
    |-- views  页面
    	|-- components 组件管理模块
        |-- dashboard 首页
        |-- features 401、404等页面
        |-- generator 代码生成页面
        |-- mnt 运维管理模块
        |-- monitor 系统监控模块
        |-- nested 多级菜单模块
        |-- system 系统管理模块
        |-- tools 系统工具模块
        |-- home.vue 基础页面(导航栏、左侧菜单栏)
        |-- login.vue 登录页面
    |-- app.vue  根组件
|-- main.js  入口文件
|-- settings.js  全局配置文件,存储一些键和值,供全局调用
|-- .gitignore  git忽略上传的文件格式
|-- .env.development  开发环境下的接口地址配置
|-- .env.production 生产环境下的接口地址配置
|-- .eslintignore  不用校验的文件
|-- .eslintrc.js  ES-lint校验(编码规范、校验规则)
|-- package.json 项目描述文件
|-- vue.config.js  cli配置文件
  • 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

常见问题

1、如何开发一个新模块?
  • 新建一个一级菜单(以新建一个数据分析页面为例)

    第一步:先在views目录下,新建文件目录如下:

    image-20201123210251346

    第二步:在菜单管理新增数据分析菜单

    image-20201123175629148

    第三步:点击数据分析菜单,即可显示

  • 新建一个二级菜单(以新建一个任务管理,包含二级菜单内容管理与类别管理 为例)

    • 第一步:先在views目录下,新建文件目录如下:

      image-20201123210605203

    • 第二步:在菜单管理新增栏目管理一级目录,内容管理与类别管理二级菜单

      image-20201123210747563

      image-20201123211108135

      第三步:点击内容管理与类别管理菜单,即可显示

2、如何做权限管理?

权限管理是需要菜单管理与代码配合实现的

以用户管理为例,用户管理有四种操作:用户创建、用户编辑、用户删除、同步,我们可以利用此框架实现给不同的角色分配不同的权限,有的用户只可以增,有的用户可以增删改等,满足不同的需求

  • 用户管理菜单下有三个按钮并对应相应的权限标识

    image-20201123213538412

    image-20201123213607046

  • 我们可以在角色管理页面为角色分配权限

    image-20201123213811979

    给哪个角色分配相应的权限操作,则角色对应的用户则具有相应的操作权限

  • 第三步:代码里为相应的按钮添加权限标识

    先看user下的index.vue

    image-20201123214045127

    image-20201123214518453

    crudOperationCRUD.operation是组件,头部增删改查按钮的封装

    image-20201123220822874

    image-20201123215028653

    image-20201123214957687

    crudOperation接收父组件传来的permisssion参数,并对相应的按钮进行绑定。则可以实现,拥有adminuser:add权限标识的用户可以创建用户,拥有adminuser:edit权限标识的用户可以编辑用户…

经过上述分析,可以知道,使用权限的步骤一般为:

  • 菜单管理页面:为相应的菜单添加按钮权限
  • 角色管理页面:根据需求为不同的角色分配不同的权限
  • 代码:为按钮添加权限v-permission="XXX"
3、头部增删改查等按钮:不显示某个按钮,或从左侧或右侧新增按钮

头部增删改查按钮以及右侧的刷新等,被封装在CRUD.operation组件里

image-20201123220822874

  • 不显示某个按钮

    因为CRUD.operation按钮绑定了显示值可以控制是否显示,以新增按钮为例:v-if=“crud.optShow.add”,所以我们只需要在页面的created生命周期里定义this.crud.optShow.add = false,则新增按钮就不会在页面显示了,其它按钮也一样

  • 从左侧或右侧新增按钮
    image-20201123221623878

如图所示,CRUD.operation组件里分别在左右设置了两个插槽<slot name="left" /><slot name="right" />

<crudOperation>
    <!-- 左侧添加一个导入按钮 -->
    <el-button
   		slot="left"
       size="mini"
       :loading="importLoading"
       icon="el-icon-upload"
       type="primary"
    >
       导入
    </el-button>
    <!-- 右侧添加一个导出按钮 -->
    <el-button
       slot="right"
       class="filter-item"
       size="mini"
       type="warning"
       :loading="downloadLoading"
       icon="el-icon-download"
       @click="download()"
     >
       导出模板
    </el-button>
</crudOperation>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
4、页面初始化时不请求查询数据接口

因为使用的是混入模式,所以页面会先执行混入模式里的代码,在执行本页面的代码,所以会产生一种情况是本页面需要查询出数据之后,把其中的数据作为参数传递给查询接口。

这时候我们只需要把混入模式里的自动请求查询接口的布尔值(queryOnPresenterCreated)设置为false,即

cruds() {
    return CRUD({
        url: 'api/fellow',
        crudMethod: { ...crudFellowShip },
        queryOnPresenterCreated: false
    })
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这样就不会自动查询数据了,我们可以获取到相应的数据之后,把数据作为参数,然后再手动调用toQuery方法进行查询

5、路由跳转发起get请求的流程

views/xxx/index.vue

cruds() {
    return CRUD({
      title: "部署",
      url: "api/deploy",
      crudMethod: { ...crudDeploy },
    });
  },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

components/Crud/crud.js

created() {
      for (const k in this.$crud) {
        if (this.$crud[k].queryOnPresenterCreated) {
          // 默认查询
          this.$crud[k].toQuery()
        }
      }
},
// 搜索
    toQuery() {
        crud.page.page = 1
        crud.refresh()
    },
	// 刷新
        refresh() {
            if (!callVmHook(crud,CRUD.HOOK.beforeRefresh)) {
                return
            }
            return new Promise((resolve, reject) => {
                crud.loading = true
                // 请求数据
                initData(crud.url, crud.getQueryParams()).then(res => {
                    // console.log(123456)
                    // console.log(res)
                    const table = crud.getTable()
                    if (table && table.lazy) { // 懒加载子节点数据,清掉已加载的数据
                        table.store.states.treeData = {}
                        table.store.states.lazyTreeNodeMap = {}
                    }
                    crud.page.total = res.data.pages === undefined ? 0 : res.data.total
                    crud.data = res.data.pages === undefined ? res.data : res.data.records
                    crud.resetDataStatus()
                    // time 毫秒后显示表格
                    setTimeout(() => {
                        crud.loading = false
                        callVmHook(crud, CRUD.HOOK.afterRefresh)
                    }, crud.time)
                    resolve(data)
                }).catch(err => {
                    crud.loading = false
                    reject(err)
                })
            })
        },

  • 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

api/data.js

export function initData(url, params) {
  return request({
    url: url + '?' + qs.stringify(params, { indices: false }),
    method: 'get'
  })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
6、新增请求流程详解

components/Crud/CRUD.operation.vue

<el-button
    v-if="crud.optShow.add"
    v-permission="permission.add"
    class="filter-item"
    size="mini"
    type="primary"
    icon="el-icon-plus"
    @click="crud.toAdd"
    >
        新增
</el-button>
import CRUD, { crud } from '@crud/crud'

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

component/Crud/crud.js

// 点击新增按钮后,调用toAdd方法
toAdd() {
      crud.resetForm() //调用重置表单方法,渲染表单
      if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, 				CRUD.HOOK.beforeToCU, crud.form))) {
        return
      }
      crud.status.add = CRUD.STATUS.PREPARED
      callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form)
      callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
    },
	/**
     * 重置表单方法
     * @param {Array} data 数据
     */
        resetForm(data) {
            const form = data || (typeof crud.defaultForm === 'object' ? JSON.parse(JSON.stringify(crud.defaultForm)) : crud.defaultForm.apply(crud.findVM('form')))
            const crudFrom = crud.form
            for (const key in form) {
                if (crudFrom.hasOwnProperty(key)) {
                    crudFrom[key] = form[key]
                } else {
                    Vue.set(crudFrom, key, form[key])
                }
            }
        },
// hook VM
function callVmHook(crud, hook) {
  if (crud.debug) {
    console.log('callVmHook: ' + hook)
  }
  const tagHook = crud.tag ? hook + '$' + crud.tag : null
  let ret = true
  const nargs = [crud]
  for (let i = 2; i < arguments.length; ++i) {
    nargs.push(arguments[i])
  }
  // 有些组件扮演了多个角色,调用钩子时,需要去重
  const vmSet = new Set()
  crud.vms.forEach(vm => vm && vmSet.add(vm.vm))
  vmSet.forEach(vm => {
    if (vm[hook]) {
      ret = vm[hook].apply(vm, nargs) !== false && ret
    }
    if (tagHook && vm[tagHook]) {
      ret = vm[tagHook].apply(vm, nargs) !== false && ret
    }
  })
  return ret
}

// 当表单新增/编辑完毕后,点击提交
    /**
     * 提交新增/编辑
     */
        submitCU() {
            if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
                return;
            }
            crud.findVM('form').$refs['form'].validate(valid => {
                if (!valid) {
                    return;
                }
                if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
                    return;
                }
                // 根据crud状态判断是新增还是编辑
                if (crud.status.add === CRUD.STATUS.PREPARED) {
                    crud.doAdd(); //执行添加
                } else if (crud.status.edit === CRUD.STATUS.PREPARED) {
                    crud.doEdit();
                }
            });
        },
        /**
     * 执行添加
     */
        doAdd() {
            if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
                return;
            }
            crud.status.add = CRUD.STATUS.PROCESSING;
            crud.crudMethod.add(crud.form).then(() => {
            	// 发送请求回调成功后,改变crud状态,重置表单,响应,刷新页面
                crud.status.add = CRUD.STATUS.NORMAL;
                crud.resetForm();
                crud.addSuccessNotify();
                callVmHook(crud, CRUD.HOOK.afterSubmit);
                crud.toQuery();
            }).catch(() => {
                crud.status.add = CRUD.STATUS.PREPARED;
                callVmHook(crud, CRUD.HOOK.afterAddError);
            });
        },
  • 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
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
7、添加部署实现

新增编辑前做的操作

import crudDeploy from "@/api/mnt/deploy";

[CRUD.HOOK.beforeToCU](crud, form) {
      this.initSelect();
      const deploys = [];
      form.deploys.forEach(function (deploy, index) {
        deploys.push(deploy.id);
      });
      this.form.deploys = deploys;
},
        
initSelect() {
      crudDeploy.getApps().then((res) => {
        this.apps = res.content;
      });
      crudDeploy.getServers().then((res) => {
        this.servers = res.content;
      });
    },
       
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

api/mnt/deploy.js

export function getApps() {
  return request({
    url: 'api/app',
    method: 'get'
  })
}

export function getServers() {
  return request({
    url: 'api/server-deploy',
    method: 'get'
  })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

点击提交前

[CRUD.HOOK.beforeSubmit]() {
      const deploys = [];
      this.form.deploys.forEach(function (data, index) {
        const deploy = { id: data };
        deploys.push(deploy);
      });
      this.form.deploys = deploys;
      return true;
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

提交

<el-button
          :loading="crud.status.cu === 2"
          type="primary"
          @click="crud.submitCU"
          >确认</el-button>
import CRUD, { presenter, header, form, crud } from "@crud/crud";
/**
     * 提交新增/编辑
     */
/components/Crud/crud.js

submitCU() {
    if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
        return
    }
    crud.findVM('form').$refs['form'].validate(valid => {
        if (!valid) {
            return
        }
        if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
            return
        }
        if (crud.status.add === CRUD.STATUS.PREPARED) {
            crud.doAdd()
        } else if (crud.status.edit === CRUD.STATUS.PREPARED) {
            crud.doEdit()
        }
    })
},
doAdd() {
      if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
        return
      }
      crud.status.add = CRUD.STATUS.PROCESSING
      crud.crudMethod.add(crud.form).then(() => {
        crud.status.add = CRUD.STATUS.NORMAL
        crud.resetForm()
        crud.addSuccessNotify()
        callVmHook(crud, CRUD.HOOK.afterSubmit)
        crud.toQuery()
      }).catch(() => {
        crud.status.add = CRUD.STATUS.PREPARED
        callVmHook(crud, CRUD.HOOK.afterAddError)2332342442we
      })
},
    
crudMethod: {
    add: (form) => { },
    del: (id) => { },
    edit: (form) => { },
    get: (id) => { }
},
/views/xxx/index.vue
cruds() {
    return CRUD({
      title: "部署",
      url: "api/deploy",
      crudMethod: { ...crudDeploy },
    });
  },
      
/api/mnt/deploy.js
export function add(data) {
  return request({
    url: 'api/deploy',
    method: 'post',
    data
  })
}
  • 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
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
8、查询服务器和项目记录
发起获取数据的请求,响应到数据中的res.data.records中 
crudDeploy.getApps().then((res) => {
        this.apps = res.data.records
      })
      crudDeploy.getServers().then((res) => {
        this.servers = res.data.records
      })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
9、组件挂载

自动挂载

var app=new Vue({
	el:'#app-3',
	data:{
	seen:true
	}
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

手动挂载

//可以实现延迟按需挂载
<p id="app">
    {{name}}
</p>
<button οnclick="test()">挂载
</button>
<script>
 var obj= {name: '张三'}
 var vm = new Vue({
 	data: obj
 })
 function test() {
 vm.$mount("#app");
 }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
// Vue.extend()创建没有挂载的的子类,可以使用该子累创建多个实例  
var app= Vue.extend({
template: '{{message}}',
 data: function () {
 return {
  message: 'message'
  }
 }
 })
new app().$mount('#app') // 创建 app实例,并挂载到一个元素上
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
10、无法加载验证码

​ ①看控制台请求是否返回,查看状态码

​ ②查看响应拦截器

​ ③询问后端返回的状态码是否和前端响应处理的一致

11、路由跳转的四种方式及区别
1)router-link
1. 不带参数
 <router-link :to="{name:'home'}"> 
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name 
// 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
 2.带参数
 <router-link :to="{name:'home', params: {id:1}}"> 
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id" 
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
<router-link :to="{name:'home', query: {id:1}}"> 
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
2)函数中调用this.$router.push()
1. 不带参数
 this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
2. query传参 
 this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
3. params传参
 this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name
 
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
 params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
3)this.$router.replace()用法同上

repalace和push的区别:push跳转后会向history栈中添加一个记录,而replace不会,点击后退会跳转到上上个页面。

4)this.$router.go(n)

n代表向前或向后跳转几个页面,正数为前,负数为后

12、el-selection绑定多个值

v-model=“value”

使用:value="item.label+’/’+item.value "

value.split(’/’)[0]

参考:

https://blog.csdn.net/Superman_peng/article/details/110305237

https://blog.csdn.net/qq_43781399/article/details/110286287

https://blog.csdn.net/weixin_44932487/article/details/114501495

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/497909
推荐阅读
相关标签
  

闽ICP备14008679号