Skip to content

w840980261/Asset-management-system-VUE-

Repository files navigation

运行使用

npm install

npm start

相关概念

  • 实现了 RBAC 模型权限控制
  • 菜单与路由独立管理,完全由后端返回
  • user存储用户
  • admin标识用户是否为系统管理员
  • role存储角色信息
  • roleUser存储用户与角色的关联关系
  • menu存储菜单信息,类型分为菜单功能,一个菜单下可以有多个功能,菜单类型的permission字段标识访问这个菜单需要的功能权限,功能类型的permission字段相当于此功能的别称,所以菜单类型的permission字段为其某个功能类型子节点的permission
  • permission存储角色与功能的关联关系
  • interface存储接口信息
  • functionInterface存储功能与接口关联关系,通过查找用户所属角色,再查找相关角色所具备的功能权限,再通过相关功能就可以查出用户所能访问的接口
  • route存储前端路由信息,通过permission字段过滤出用户所能访问的路由

运行流程及相关 API

使用d2admin的原有登录逻辑,全局路由守卫中判断是否已经拉取权限信息,获取后标识为已获取。

后端需要返回的权限信息包括权限过滤后的角色编码集合,功能编码集合,接口信息集合,菜单列表,路由列表,以及是否系统管理员标识。格式如下

{
  "statusCode": 200,
  "msg": "",
  "data": {
    "userName": "MenuManager",
    "userRoles": [
      "R_MENUADMIN"
    ],
    "userPermissions": [
      "p_menu_view12",
      "p_menu_edit",
      "p_menu_menu"
    ],
    "accessMenus": [
      {
        "title": "系统",
        "path": "/system",
        "icon": "cogs",
        "children": [
          {
            "title": "系统设置",
            "icon": "cogs",
            "children": [
              {
                "title": "菜单管理",
                "path": "/system/menu",
                "icon": "th-list"
              }
            ]
          },
          {
            "title": "组织架构",
            "icon": "pie-chart",
            "children": [
              {
                "title": "部门管理",
                "icon": "html5"
              },
              {
                "title": "职位管理",
                "icon": "opencart"
              }
            ]
          }
        ]
      }
    ],
    "accessRoutes": [
      {
        "name": "System",
        "path": "/system",
        "component": "layoutHeaderAside",
        "componentPath": "layout/header-aside/layout",
        "meta": {
          "title": "系统设置",
          "cache": true
        },
        "children": [
          {
            "name": "MenuPage",
            "path": "/system/menu",
            "component": "menu",
            "componentPath": "pages/sys/menu/index",
            "meta": {
              "title": "菜单管理",
              "cache": true
            }
          },
          {
            "name": "RoutePage",
            "path": "/system/route",
            "component": "route",
            "componentPath": "pages/sys/route/index",
            "meta": {
              "title": "路由管理",
              "cache": true
            }
          },
          {
            "name": "RolePage",
            "path": "/system/role",
            "component": "role",
            "componentPath": "pages/sys/role/index",
            "meta": {
              "title": "角色管理",
              "cache": true
            }
          },
          {
            "name": "UserPage",
            "path": "/system/user",
            "component": "user",
            "componentPath": "pages/sys/user/index",
            "meta": {
              "title": "用户管理",
              "cache": true
            }
          },
          {
            "name": "InterfacePage",
            "path": "/system/interface",
            "component": "interface",
            "meta": {
              "title": "接口管理"
            }
          }
        ]
      }
    ],
    "accessInterfaces": [
      {
        "path": "/menu/:id",
        "method": "get"
      },
      {
        "path": "/menu",
        "method": "get"
      },
      {
        "path": "/menu/save",
        "method": "post"
      },
      {
        "path": "/interface/paged",
        "method": "get"
      }
    ],
    "isAdmin": 0,
    "avatarUrl": "https://api.adorable.io/avatars/85/abott@adorable.png"
  }
}

设置菜单

将固定菜单(/menu/header/menu/aside)与后端返回的权限菜单(accessMenus)合并后,存入相应的 vuex store 模块中

...
let allMenuAside = [...menuAside, ...permissionMenu]
let allMenuHeader = [...menuHeader, ...permissionMenu]
...
// 设置顶栏菜单
store.commit('d2admin/menu/headerSet', allMenuHeader)
// 设置侧边栏菜单
store.commit('d2admin/menu/fullAsideSet', allMenuAside)
// 初始化菜单搜索功能
store.commit('d2admin/search/init', allMenuHeader)

处理路由

默认使用routerMapComponents 的方式处理后端返回的权限路由

//处理动态添加的路由
const formatRoutes = function (routes) {
    routes.forEach(route => {
        route.component = routerMapComponents[route.component]
        if (route.children) {
        formatRoutes(route.children)
        }
    })
}
...
formatRoutes(permissionRouter)
//动态添加路由
router.addRoutes(permissionRouter);
// 处理路由 得到每一级的路由设置
store.commit('d2admin/page/init', [...frameInRoutes, ...permissionRouter])

设置权限信息

将角色编码集合,功能编码集合,接口信息集合,以及是否系统管理员标识存入相应的 vuex store 模块中

...
permission.functions = userPermissionInfo.userPermissions
permission.roles = userPermissionInfo.userRoles
permission.interfaces = util.formatInterfaces(userPermissionInfo.accessInterfaces)
permission.isAdmin = userPermissionInfo.isAdmin == 1
...
// 设置权限信息
store.commit('d2admin/permission/set', permission)

接口权限控制以及 loading 配置

支持使用角色编码,功能编码以及接口权限进行控制,如下

export function getMenuList() {
  return request({
    url: '/menu',
    method: 'get',
    interfaceCheck: true,
    permission: ['p_menu_view'],
    loading: {
      type: 'loading',
      options: {
        fullscreen: true,
        lock: true,
        text: '加载中...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.8)'
      }
    },
    success: {
      type: 'message',
      options: {
        message: '加载菜单成功',
        type: 'success'
      }
    }
  })
}

interfaceCheck: true表示使用接口权限进行控制,如果 vuex store 中存储的接口信息与当前要请求的接口想匹配,则可发起请求,否则请求将被拦截。

permission:["p_menu_view"]表示使用角色编码和功能编码进行权限校验,如果 vuex store 中存储的角色编码或功能编码与当前表示的编码相匹配,则可发起请求,否则请求将被拦截。

源码位置在libs/permission.js,可根据自己需求进行修改。

loading配置相关源码在libs/loading.js,根据自己需求进行配置,success也是如此,源码在libs/loading.js。 照此思路可以自行配置其它功能,比如请求失败等。

页面元素权限控制

使用指令v-permission

<el-button
  v-permission:function.all="['p_menu_edit']"
  type="primary"
  icon="el-icon-edit"
  size="mini"
  @click="batchEdit"
  >批量编辑</el-button
>

参数可为functionrole,表明以功能编码或角色编码进行校验,为空则使用两者进行校验。

修饰符all,表示必须全部匹配指令值中所有的编码。

源码位置在plugin/permission/index.js,根据自己实际需求进行修改。

使用v-if+全局方法:

<el-button
  v-if="canAdd"
  type="primary"
  icon="el-icon-circle-plus-outline"
  size="mini"
  @click="add"
  >添加</el-button
>
data() {
    return {
      canAdd: this.hasPermissions(["p_menu_edit"])
    };
  },

默认同时使用角色编码与功能编码进行校验,有一项匹配即可。

类似的方法还要hasFunctionshasRoles

源码位置在plugin/permission/index.js,根据自己实际需求进行修改。

不要使用v-if="hasPermissions(['p_menu_edit'])"这种方式,会导致方法多次执行

也可以直接在组件中从 vuex store 读取权限信息进行校验。

模态框loadding在'5000'时无法取消解决办法

例如:

postService.addPost(data).then(data => {
  this.loading = false //请求成功取消loading
  if (!data.hasOwnProperty('code')) { //判断是否2000
    this.dialogVisible = false  //模态框关闭
    this.$emit('submit')
  }
})

页面层级结构

src/api: 请求封装 src/layout: 整体布局 src/page: 页面

├─asset-management  	 // 资产管理
│  ├─asset-allocation		//调拨
│  │      allot.vue
│  │      detial.vue
│  │      index.vue
│  │      
│  ├─asset-list				//资产列表
│  │      detial.vue	
│  │      index.vue
│  │      
│  ├─asset-operations		//领用/归还
│  │      detial.vue
│  │      index.vue
│  │      use.vue
│  │      
│  ├─asset-scrap			//报废
│  │      index.vue
│  │      scrap.vue
│  │      
│  └─asset-transfer			//移交
│          detial.vue
│          index.vue
│          transfer.vue
│          
├─error-page-404			//404
│      index.js
│      page.vue
│      
├─finance					//财务
│  ├─record					//入账记录
│  │      detial.vue
│  │      index.vue
│  │      
│  └─waiting				//等待入账
│          detial.vue
│          index.vue
│          
├─login						//登陆页面
│  │  index.js
│  │  page.vue
│  │  style.scss
│          
├─procurement				//采购列表
│      detial.vue
│      editForm.vue
│      index.vue
│      
├─stock-in					//入库
│  ├─record					//入库记录
│  │      detial.vue
│  │      index.vue
│  │      
│  └─waiting				//等待入库
│          detial.vue
│          index.vue
│          
└─sys						//系统管理
    ├─authority-management	//用户管理
    │      editForm.vue
    │      index.vue
    │      
    └─data-dictionary		//字典管理
            editForm.vue
            index.vue

开发建议

  • 页面级别的组件放到pages/目录下,并且在routerMapCompnonents/index.js中以 key-value 的形式导出

  • 不需要权限控制的固定菜单放到menu/aside.jsmenu/header.js

  • 不需要权限控制的路由放到router/routes.js frameIn

  • 需要权限控制的菜单与路由通过界面的管理功能进行添加,确保菜单的path与路由的path相对应,路由的name与页面组件的name一致才能使keep-alive生效,路由的componentrouterMapCompnonents/index.js中能通过 key 匹配到。

  • 一级目录文件统一命名为xxx.xx,二级目录统一命名为xxx-xxx.xx。如:角色管理->role-management.vue

  • 代码格式化参照.eslintrc.js文件

  • 总之 Happy Coding!!!

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published