Browse Source

新增权限管理功能

macro 5 years ago
parent
commit
993dcd2dc7

+ 54 - 0
src/api/login.js

@@ -24,3 +24,57 @@ export function logout() {
     method: 'post'
   })
 }
+
+export function fetchList(params) {
+  return request({
+    url: '/admin/list',
+    method: 'get',
+    params: params
+  })
+}
+
+export function createAdmin(data) {
+  return request({
+    url: '/admin/register',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateAdmin(id, data) {
+  return request({
+    url: '/admin/update/' + id,
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateStatus(id, params) {
+  return request({
+    url: '/admin/updateStatus/' + id,
+    method: 'post',
+    params: params
+  })
+}
+
+export function deleteAdmin(id) {
+  return request({
+    url: '/admin/delete/' + id,
+    method: 'post'
+  })
+}
+
+export function getRoleByAdmin(id) {
+  return request({
+    url: '/admin/role/' + id,
+    method: 'get'
+  })
+}
+
+export function allocRole(data) {
+  return request({
+    url: '/admin/role/update',
+    method: 'post',
+    data: data
+  })
+}

+ 55 - 0
src/api/menu.js

@@ -0,0 +1,55 @@
+import request from '@/utils/request'
+
+export function fetchList(parentId, params) {
+  return request({
+    url: '/menu/list/' + parentId,
+    method: 'get',
+    params: params
+  })
+}
+
+export function deleteMenu(id) {
+  return request({
+    url: '/menu/delete/' + id,
+    method: 'post'
+  })
+}
+
+export function createMenu(data) {
+  return request({
+    url: '/menu/create',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateMenu(id, data) {
+  return request({
+    url: '/menu/update/' + id,
+    method: 'post',
+    data: data
+  })
+}
+
+export function getMenu(id) {
+  return request({
+    url: '/menu/' + id,
+    method: 'get',
+  })
+}
+
+export function updateHidden(id, params) {
+  return request({
+    url: '/menu/updateHidden/' + id,
+    method: 'post',
+    params: params
+  })
+}
+
+export function fetchTreeList() {
+  return request({
+    url: '/menu/treeList',
+    method: 'get'
+  })
+}
+

+ 39 - 0
src/api/resource.js

@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+
+export function fetchList(params) {
+  return request({
+    url: '/resource/list',
+    method: 'get',
+    params: params
+  })
+}
+
+export function createResource(data) {
+  return request({
+    url: '/resource/create',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateResource(id, data) {
+  return request({
+    url: '/resource/update/' + id,
+    method: 'post',
+    data: data
+  })
+}
+
+export function deleteResource(id) {
+  return request({
+    url: '/resource/delete/' + id,
+    method: 'post'
+  })
+}
+
+export function fetchAllResourceList() {
+  return request({
+    url: '/resource/listAll',
+    method: 'get'
+  })
+}

+ 31 - 0
src/api/resourceCategory.js

@@ -0,0 +1,31 @@
+import request from '@/utils/request'
+
+export function listAllCate() {
+  return request({
+    url: '/resourceCategory/listAll',
+    method: 'get'
+  })
+}
+
+export function createResourceCategory(data) {
+  return request({
+    url: '/resourceCategory/create',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateResourceCategory(id, data) {
+  return request({
+    url: '/resourceCategory/update/' + id,
+    method: 'post',
+    data: data
+  })
+}
+
+export function deleteResourceCategory(id) {
+  return request({
+    url: '/resourceCategory/delete/' + id,
+    method: 'post'
+  })
+}

+ 78 - 0
src/api/role.js

@@ -0,0 +1,78 @@
+import request from '@/utils/request'
+
+export function fetchList(params) {
+  return request({
+    url: '/role/list',
+    method: 'get',
+    params: params
+  })
+}
+
+export function createRole(data) {
+  return request({
+    url: '/role/create',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateRole(id, data) {
+  return request({
+    url: '/role/update/' + id,
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateStatus(id, params) {
+  return request({
+    url: '/role/updateStatus/' + id,
+    method: 'post',
+    params: params
+  })
+}
+
+export function deleteRole(data) {
+  return request({
+    url:'/role/delete',
+    method:'post',
+    data:data
+  })
+}
+
+export function fetchAllRoleList() {
+  return request({
+    url: '/role/listAll',
+    method: 'get'
+  })
+}
+
+export function listMenuByRole(roleId) {
+  return request({
+    url: '/role/listMenu/'+roleId,
+    method: 'get'
+  })
+}
+
+export function listResourceByRole(roleId) {
+  return request({
+    url: '/role/listResource/'+roleId,
+    method: 'get'
+  })
+}
+
+export function allocMenu(data) {
+  return request({
+    url: '/role/allocMenu',
+    method: 'post',
+    data:data
+  })
+}
+
+export function allocResource(data) {
+  return request({
+    url: '/role/allocResource',
+    method: 'post',
+    data:data
+  })
+}

+ 1 - 0
src/icons/svg/ums-admin.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580710958385" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4938" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 1024C229.248 1024 0 794.752 0 512S229.248 0 512 0s512 229.248 512 512-229.248 512-512 512z m0-938.666667C276.352 85.333333 85.333333 276.352 85.333333 512s191.018667 426.666667 426.666667 426.666667 426.666667-191.018667 426.666667-426.666667S747.648 85.333333 512 85.333333z m0 768a340.650667 340.650667 0 0 1-266.24-128A340.650667 340.650667 0 0 1 512 597.333333a340.48 340.48 0 0 1 266.197333 128A340.48 340.48 0 0 1 512 853.333333z m0-298.666666a170.666667 170.666667 0 1 1 0-341.333334 170.666667 170.666667 0 0 1 0 341.333334z" p-id="4939"></path></svg>

+ 1 - 0
src/icons/svg/ums-menu.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580711460345" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7290" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M374.272 440.832H127.488c-33.792 0-61.44-27.648-61.44-61.44V132.608c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c-0.512 33.792-27.648 60.928-61.952 60.928zM127.488 132.608v247.296h247.296V132.608H127.488zM762.88 492.032c-16.384 0-31.744-6.144-43.52-17.92l-174.592-174.592c-11.776-11.776-17.92-27.136-17.92-43.52s6.144-31.744 17.92-43.52l174.592-174.592c11.776-11.776 27.136-17.92 43.52-17.92s31.744 6.144 43.52 17.92l174.592 174.592c11.776 11.776 17.92 27.136 17.92 43.52s-6.144 31.744-17.92 43.52l-174.592 174.592c-11.776 11.776-27.136 17.92-43.52 17.92z m0-410.624L588.288 256 762.88 430.592 937.472 256 762.88 81.408zM374.272 952.832H127.488c-33.792 0-61.44-27.648-61.44-61.44v-247.296c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c-0.512 34.304-27.648 61.44-61.952 61.44z m-246.784-308.224v247.296h247.296v-247.296H127.488zM886.272 952.832h-247.296c-33.792 0-61.44-27.648-61.44-61.44v-247.296c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c0 34.304-27.136 61.44-61.44 61.44z m-246.784-308.224v247.296h247.296v-247.296h-247.296z" p-id="7291"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/ums-resource.svg


File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/ums-role.svg


+ 1 - 0
src/icons/svg/ums.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580698548494" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2117" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 38.4L85.344 183.456V550.4c0 149.344 85.344 281.6 226.144 345.6l192 89.6 204.8-93.856c140.8-59.744 230.4-196.256 230.4-349.856v-358.4L512.032 38.432z m341.344 503.456c0 119.456-68.256 226.144-179.2 273.056l-170.656 76.8-157.856-72.544c-106.656-46.944-174.944-153.6-174.944-268.8v-307.2l341.344-115.2 341.344 115.2v298.656z" p-id="2118"></path><path d="M341.344 384c0 81.056 55.456 145.056 128 166.4V768h85.344v-42.656h42.656V640h-42.656v-89.6c72.544-17.056 128-85.344 128-166.4 0-93.856-76.8-170.656-170.656-170.656s-170.656 76.8-170.656 170.656z m256 0c0 46.944-38.4 85.344-85.344 85.344s-85.344-38.4-85.344-85.344 38.4-85.344 85.344-85.344c46.944 0 85.344 38.4 85.344 85.344z" p-id="2119"></path></svg>

+ 6 - 1
src/permission.js

@@ -15,7 +15,12 @@ router.beforeEach((to, from, next) => {
     } else {
       if (store.getters.roles.length === 0) {
         store.dispatch('GetInfo').then(res => { // 拉取用户信息
-          next()
+          let menus=res.data.menus;
+          let username=res.data.username;
+          store.dispatch('GenerateRoutes', { menus,username }).then(() => { // 生成可访问的路由表
+            router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
+            next({ ...to, replace: true })
+          })
         }).catch((err) => {
           store.dispatch('FedLogOut').then(() => {
             Message.error(err || 'Verification failed, please login again')

+ 72 - 15
src/router/index.js

@@ -31,7 +31,10 @@ export const constantRouterMap = [
       component: () => import('@/views/home/index'),
       meta: {title: '首页', icon: 'home'}
     }]
-  },
+  }
+]
+
+export const asyncRouterMap = [
   {
     path: '/pms',
     component: Layout,
@@ -58,20 +61,6 @@ export const constantRouterMap = [
         hidden: true
       },
       {
-        path: 'productRecycle',
-        name: 'productRecycle',
-        component: () => import('@/views/pms/product/index'),
-        meta: {title: '商品回收站', icon: 'product-recycle'},
-        hidden: true
-      },
-      {
-        path: 'productComment',
-        name: 'productComment',
-        component: () => import('@/views/pms/product/index'),
-        meta: {title: '商品评价', icon: 'product-comment'},
-        hidden: true
-      },
-      {
         path: 'productCate',
         name: 'productCate',
         component: () => import('@/views/pms/productCate/index'),
@@ -301,6 +290,74 @@ export const constantRouterMap = [
       }
     ]
   },
+  {
+    path:'/ums',
+    component: Layout,
+    redirect: '/ums/admin',
+    name: 'ums',
+    meta: {title: '权限', icon: 'ums'},
+    children: [
+      {
+        path: 'admin',
+        name: 'admin',
+        component: () => import('@/views/ums/admin/index'),
+        meta: {title: '用户列表', icon: 'ums-admin'}
+      },
+      {
+        path: 'role',
+        name: 'role',
+        component: () => import('@/views/ums/role/index'),
+        meta: {title: '角色列表', icon: 'ums-role'}
+      },
+      {
+        path: 'allocMenu',
+        name: 'allocMenu',
+        component: () => import('@/views/ums/role/allocMenu'),
+        meta: {title: '分配菜单'},
+        hidden: true
+      },
+      {
+        path: 'allocResource',
+        name: 'allocResource',
+        component: () => import('@/views/ums/role/allocResource'),
+        meta: {title: '分配资源'},
+        hidden: true
+      },
+      {
+        path: 'menu',
+        name: 'menu',
+        component: () => import('@/views/ums/menu/index'),
+        meta: {title: '菜单列表', icon: 'ums-menu'}
+      },
+      {
+        path: 'addMenu',
+        name: 'addMenu',
+        component: () => import('@/views/ums/menu/add'),
+        meta: {title: '添加菜单'},
+        hidden: true
+      },
+      {
+        path: 'updateMenu',
+        name: 'updateMenu',
+        component: () => import('@/views/ums/menu/update'),
+        meta: {title: '修改菜单'},
+        hidden: true
+      },
+      {
+        path: 'resource',
+        name: 'resource',
+        component: () => import('@/views/ums/resource/index'),
+        meta: {title: '资源列表', icon: 'ums-resource'}
+      },
+      {
+        path: 'resourceCategory',
+        name: 'resourceCategory',
+        component: () => import('@/views/ums/resource/categoryList'),
+        meta: {title: '资源分类'},
+        hidden: true
+      }
+    ]
+  },
   {path: '*', redirect: '/404', hidden: true}
 ]
 

+ 3 - 1
src/store/getters.js

@@ -4,6 +4,8 @@ const getters = {
   token: state => state.user.token,
   avatar: state => state.user.avatar,
   name: state => state.user.name,
-  roles: state => state.user.roles
+  roles: state => state.user.roles,
+  addRouters: state => state.permission.addRouters,
+  routers: state => state.permission.routers
 }
 export default getters

+ 3 - 1
src/store/index.js

@@ -2,6 +2,7 @@ import Vue from 'vue'
 import Vuex from 'vuex'
 import app from './modules/app'
 import user from './modules/user'
+import permission from './modules/permission'
 import getters from './getters'
 
 Vue.use(Vuex)
@@ -9,7 +10,8 @@ Vue.use(Vuex)
 const store = new Vuex.Store({
   modules: {
     app,
-    user
+    user,
+    permission
   },
   getters
 })

+ 110 - 0
src/store/modules/permission.js

@@ -0,0 +1,110 @@
+import { asyncRouterMap, constantRouterMap } from '@/router/index';
+
+//判断是否有权限访问该菜单
+function hasPermission(menus, route) {
+  if (route.name) {
+    let currMenu = getMenu(route.name, menus);
+    if (currMenu!=null) {
+      //设置菜单的标题、图标和可见性
+      if (currMenu.title != null && currMenu.title !== '') {
+        route.meta.title = currMenu.title;
+      }
+      if (currMenu.icon != null && currMenu.title !== '') {
+        route.meta.icon = currMenu.icon;
+      }
+      if(currMenu.hidden!=null){
+        route.hidden = currMenu.hidden !== 0;
+      }
+      if (currMenu.sort != null && currMenu.sort !== '') {
+        route.sort = currMenu.sort;
+      }
+      return true;
+    } else {
+      route.sort = 0;
+      if (route.hidden !== undefined && route.hidden === true) {
+        return true;
+      } else {
+        return false;
+      }
+    }
+  } else {
+    return true
+  }
+}
+
+//根据路由名称获取菜单
+function getMenu(name, menus) {
+  for (let i = 0; i < menus.length; i++) {
+    let menu = menus[i];
+    if (name===menu.name) {
+      return menu;
+    }
+  }
+  return null;
+}
+
+//对菜单进行排序
+function sortRouters(accessedRouters) {
+  for (let i = 0; i < accessedRouters.length; i++) {
+    let router = accessedRouters[i];
+    if(router.children && router.children.length > 0){
+      router.children.sort(compare("sort"));
+    }
+  }
+  accessedRouters.sort(compare("sort"));
+}
+
+//降序比较函数
+function compare(p){
+  return function(m,n){
+    let a = m[p];
+    let b = n[p];
+    return b - a;
+  }
+}
+
+const permission = {
+  state: {
+    routers: constantRouterMap,
+    addRouters: []
+  },
+  mutations: {
+    SET_ROUTERS: (state, routers) => {
+      state.addRouters = routers;
+      state.routers = constantRouterMap.concat(routers);
+    }
+  },
+  actions: {
+    GenerateRoutes({ commit }, data) {
+      return new Promise(resolve => {
+        const { menus } = data;
+        const { username } = data;
+        const accessedRouters = asyncRouterMap.filter(v => {
+          //admin帐号直接返回所有菜单
+          // if(username==='admin') return true;
+          if (hasPermission(menus, v)) {
+            if (v.children && v.children.length > 0) {
+              v.children = v.children.filter(child => {
+                if (hasPermission(menus, child)) {
+                  return child
+                }
+                return false;
+              });
+              return v
+            } else {
+              return v
+            }
+          }
+          return false;
+        });
+        //对菜单进行排序
+        sortRouters(accessedRouters);
+        commit('SET_ROUTERS', accessedRouters);
+        resolve();
+      })
+    }
+  }
+};
+
+export default permission;
+

+ 1 - 1
src/utils/request.js

@@ -36,7 +36,7 @@ service.interceptors.response.use(
       })
 
       // 401:未登录;
-      if (res.code === 401||res.code === 403) {
+      if (res.code === 401) {
         MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
           confirmButtonText: '重新登录',
           cancelButtonText: '取消',

+ 3 - 2
src/utils/validate.js

@@ -1,6 +1,7 @@
 export function isvalidUsername(str) {
-  const valid_map = ['admin', 'test']
-  return valid_map.indexOf(str.trim()) >= 0
+  // const valid_map = ['admin', 'test']
+  // return valid_map.indexOf(str.trim()) >= 0
+  return str.trim().length>=3
 }
 
 /* 合法uri*/

+ 4 - 2
src/views/layout/components/Sidebar/index.vue

@@ -23,10 +23,12 @@ export default {
   components: { SidebarItem, ScrollBar },
   computed: {
     ...mapGetters([
-      'sidebar'
+      'sidebar',
+      'routers'
     ]),
     routes() {
-      return this.$router.options.routes
+      // return this.$router.options.routes
+      return this.routers
     },
     isCollapse() {
       return !this.sidebar.opened

+ 344 - 0
src/views/ums/admin/index.vue

@@ -0,0 +1,344 @@
+<template> 
+  <div class="app-container">
+    <el-card class="filter-container" shadow="never">
+      <div>
+        <i class="el-icon-search"></i>
+        <span>筛选搜索</span>
+        <el-button
+          style="float:right"
+          type="primary"
+          @click="handleSearchList()"
+          size="small">
+          查询搜索
+        </el-button>
+        <el-button
+          style="float:right;margin-right: 15px"
+          @click="handleResetSearch()"
+          size="small">
+          重置
+        </el-button>
+      </div>
+      <div style="margin-top: 15px">
+        <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
+          <el-form-item label="输入搜索:">
+            <el-input v-model="listQuery.keyword" class="input-width" placeholder="帐号/姓名" clearable></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+    </el-card>
+    <el-card class="operate-container" shadow="never">
+      <i class="el-icon-tickets"></i>
+      <span>数据列表</span>
+      <el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
+    </el-card>
+    <div class="table-container">
+      <el-table ref="adminTable"
+                :data="list"
+                style="width: 100%;"
+                v-loading="listLoading" border>
+        <el-table-column label="编号" width="100" align="center">
+          <template slot-scope="scope">{{scope.row.id}}</template>
+        </el-table-column>
+        <el-table-column label="帐号" align="center">
+          <template slot-scope="scope">{{scope.row.username}}</template>
+        </el-table-column>
+        <el-table-column label="姓名" align="center">
+          <template slot-scope="scope">{{scope.row.nickName}}</template>
+        </el-table-column>
+        <el-table-column label="邮箱" align="center">
+          <template slot-scope="scope">{{scope.row.email}}</template>
+        </el-table-column>
+        <el-table-column label="添加时间" width="160" align="center">
+          <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
+        </el-table-column>
+        <el-table-column label="最后登录" width="160" align="center">
+          <template slot-scope="scope">{{scope.row.loginTime | formatDateTime}}</template>
+        </el-table-column>
+        <el-table-column label="是否启用" width="140" align="center">
+          <template slot-scope="scope">
+            <el-switch
+              @change="handleStatusChange(scope.$index, scope.row)"
+              :active-value="1"
+              :inactive-value="0"
+              v-model="scope.row.status">
+            </el-switch>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="180" align="center">
+          <template slot-scope="scope">
+            <el-button size="mini"
+                       type="text"
+                       @click="handleSelectRole(scope.$index, scope.row)">分配角色
+            </el-button>
+            <el-button size="mini"
+                       type="text"
+                       @click="handleUpdate(scope.$index, scope.row)">
+              编辑
+            </el-button>
+            <el-button size="mini"
+                       type="text"
+                       @click="handleDelete(scope.$index, scope.row)">删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="pagination-container">
+      <el-pagination
+        background
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        layout="total, sizes,prev, pager, next,jumper"
+        :current-page.sync="listQuery.pageNum"
+        :page-size="listQuery.pageSize"
+        :page-sizes="[10,15,20]"
+        :total="total">
+      </el-pagination>
+    </div>
+    <el-dialog
+      :title="isEdit?'编辑用户':'添加用户'"
+      :visible.sync="dialogVisible"
+      width="40%">
+      <el-form :model="admin"
+               ref="adminForm"
+               label-width="150px" size="small">
+        <el-form-item label="帐号:">
+          <el-input v-model="admin.username" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="姓名:">
+          <el-input v-model="admin.nickName" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="邮箱:">
+          <el-input v-model="admin.email" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="密码:">
+          <el-input v-model="admin.password"  type="password" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="备注:">
+          <el-input v-model="admin.note"
+                    type="textarea"
+                    :rows="5"
+                    style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="是否启用:">
+          <el-radio-group v-model="admin.status">
+            <el-radio :label="1">是</el-radio>
+            <el-radio :label="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false" size="small">取 消</el-button>
+        <el-button type="primary" @click="handleDialogConfirm()" size="small">确 定</el-button>
+      </span>
+    </el-dialog>
+    <el-dialog
+      title="分配角色"
+      :visible.sync="allocDialogVisible"
+      width="30%">
+      <el-select v-model="allocRoleIds" multiple placeholder="请选择" size="small" style="width: 80%">
+        <el-option
+          v-for="item in allRoleList"
+          :key="item.id"
+          :label="item.name"
+          :value="item.id">
+        </el-option>
+      </el-select>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="allocDialogVisible = false" size="small">取 消</el-button>
+        <el-button type="primary" @click="handleAllocDialogConfirm()" size="small">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import {fetchList,createAdmin,updateAdmin,updateStatus,deleteAdmin,getRoleByAdmin,allocRole} from '@/api/login';
+  import {fetchAllRoleList} from '@/api/role';
+  import {formatDate} from '@/utils/date';
+
+  const defaultListQuery = {
+    pageNum: 1,
+    pageSize: 10,
+    keyword: null
+  };
+  const defaultAdmin = {
+    id: null,
+    username: null,
+    password: null,
+    nickName: null,
+    email: null,
+    note: null,
+    status: 1
+  };
+  export default {
+    name: 'adminList',
+    data() {
+      return {
+        listQuery: Object.assign({}, defaultListQuery),
+        list: null,
+        total: null,
+        listLoading: false,
+        dialogVisible: false,
+        admin: Object.assign({}, defaultAdmin),
+        isEdit: false,
+        allocDialogVisible: false,
+        allocRoleIds:[],
+        allRoleList:[],
+        allocAdminId:null
+      }
+    },
+    created() {
+      this.getList();
+      this.getAllRoleList();
+    },
+    filters: {
+      formatDateTime(time) {
+        if (time == null || time === '') {
+          return 'N/A';
+        }
+        let date = new Date(time);
+        return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
+      }
+    },
+    methods: {
+      handleResetSearch() {
+        this.listQuery = Object.assign({}, defaultListQuery);
+      },
+      handleSearchList() {
+        this.listQuery.pageNum = 1;
+        this.getList();
+      },
+      handleSizeChange(val) {
+        this.listQuery.pageNum = 1;
+        this.listQuery.pageSize = val;
+        this.getList();
+      },
+      handleCurrentChange(val) {
+        this.listQuery.pageNum = val;
+        this.getList();
+      },
+      handleAdd() {
+        this.dialogVisible = true;
+        this.isEdit = false;
+        this.admin = Object.assign({},defaultAdmin);
+      },
+      handleStatusChange(index, row) {
+        this.$confirm('是否要修改该状态?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          updateStatus(row.id, {status: row.status}).then(response => {
+            this.$message({
+              type: 'success',
+              message: '修改成功!'
+            });
+          });
+        }).catch(() => {
+          this.$message({
+            type: 'info',
+            message: '取消修改'
+          });
+          this.getList();
+        });
+      },
+      handleDelete(index, row) {
+        this.$confirm('是否要删除该用户?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          deleteAdmin(row.id).then(response => {
+            this.$message({
+              type: 'success',
+              message: '删除成功!'
+            });
+            this.getList();
+          });
+        });
+      },
+      handleUpdate(index, row) {
+        this.dialogVisible = true;
+        this.isEdit = true;
+        this.admin = Object.assign({},row);
+      },
+      handleDialogConfirm() {
+        this.$confirm('是否要确认?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          if (this.isEdit) {
+            updateAdmin(this.admin.id,this.admin).then(response => {
+              this.$message({
+                message: '修改成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          } else {
+            createAdmin(this.admin).then(response => {
+              this.$message({
+                message: '添加成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          }
+        })
+      },
+      handleAllocDialogConfirm(){
+        this.$confirm('是否要确认?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          let params = new URLSearchParams();
+          params.append("adminId", this.allocAdminId);
+          params.append("roleIds", this.allocRoleIds);
+          allocRole(params).then(response => {
+            this.$message({
+              message: '分配成功!',
+              type: 'success'
+            });
+            this.allocDialogVisible = false;
+          })
+        })
+      },
+      handleSelectRole(index,row){
+        this.allocAdminId = row.id;
+        this.allocDialogVisible = true;
+        this.getRoleListByAdmin(row.id);
+      },
+      getList() {
+        this.listLoading = true;
+        fetchList(this.listQuery).then(response => {
+          this.listLoading = false;
+          this.list = response.data.list;
+          this.total = response.data.total;
+        });
+      },
+      getAllRoleList() {
+        fetchAllRoleList().then(response => {
+          this.allRoleList = response.data;
+        });
+      },
+      getRoleListByAdmin(adminId) {
+        getRoleByAdmin(adminId).then(response => {
+          let allocRoleList = response.data;
+          this.allocRoleIds=[];
+          if(allocRoleList!=null&&allocRoleList.length>0){
+            for(let i=0;i<allocRoleList.length;i++){
+              this.allocRoleIds.push(allocRoleList[i].id);
+            }
+          }
+        });
+      }
+    }
+  }
+</script>
+<style></style>
+
+

+ 14 - 0
src/views/ums/menu/add.vue

@@ -0,0 +1,14 @@
+<template> 
+  <menu-detail :is-edit='false'></menu-detail>
+</template>
+<script>
+  import MenuDetail from './components/MenuDetail'
+  export default {
+    name: 'addMenu',
+    components: { MenuDetail }
+  }
+</script>
+<style>
+</style>
+
+

+ 153 - 0
src/views/ums/menu/components/MenuDetail.vue

@@ -0,0 +1,153 @@
+<template>
+  <el-card class="form-container" shadow="never">
+    <el-form :model="menu"
+             :rules="rules"
+             ref="menuFrom"
+             label-width="150px">
+      <el-form-item label="菜单名称:" prop="title">
+        <el-input v-model="menu.title"></el-input>
+      </el-form-item>
+      <el-form-item label="上级菜单:">
+        <el-select v-model="menu.parentId"
+                   placeholder="请选择菜单">
+          <el-option
+            v-for="item in selectMenuList"
+            :key="item.id"
+            :label="item.title"
+            :value="item.id">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="前端名称:" prop="name">
+        <el-input v-model="menu.name"></el-input>
+      </el-form-item>
+      <el-form-item label="前端图标:" prop="icon">
+        <el-input v-model="menu.icon" style="width: 80%"></el-input>
+        <svg-icon style="margin-left: 8px" :icon-class="menu.icon"></svg-icon>
+      </el-form-item>
+      <el-form-item label="是否显示:">
+        <el-radio-group v-model="menu.hidden">
+          <el-radio :label="0">是</el-radio>
+          <el-radio :label="1">否</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="排序:">
+        <el-input v-model="menu.sort"></el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="onSubmit('menuFrom')">提交</el-button>
+        <el-button v-if="!isEdit" @click="resetForm('menuFrom')">重置</el-button>
+      </el-form-item>
+    </el-form>
+  </el-card>
+</template>
+
+<script>
+  import {fetchList, createMenu, updateMenu, getMenu} from '@/api/menu';
+
+  const defaultMenu = {
+    title: '',
+    parentId: 0,
+    name: '',
+    icon: '',
+    hidden: 0,
+    sort: 0
+  };
+  export default {
+    name: "MenuDetail",
+    props: {
+      isEdit: {
+        type: Boolean,
+        default: false
+      }
+    },
+    data() {
+      return {
+        menu: Object.assign({}, defaultMenu),
+        selectMenuList: [],
+        rules: {
+          title: [
+            {required: true, message: '请输入菜单名称', trigger: 'blur'},
+            {min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
+          ],
+          name: [
+            {required: true, message: '请输入前端名称', trigger: 'blur'},
+            {min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
+          ],
+          icon: [
+            {required: true, message: '请输入前端图标', trigger: 'blur'},
+            {min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
+          ]
+        }
+      }
+    },
+    created() {
+      if (this.isEdit) {
+        getMenu(this.$route.query.id).then(response => {
+          this.menu = response.data;
+        });
+      } else {
+        this.menu = Object.assign({}, defaultMenu);
+      }
+      this.getSelectMenuList();
+    },
+    methods: {
+      getSelectMenuList() {
+        fetchList(0, {pageSize: 100, pageNum: 1}).then(response => {
+          this.selectMenuList = response.data.list;
+          this.selectMenuList.unshift({id: 0, title: '无上级菜单'});
+        });
+      },
+      onSubmit(formName) {
+        this.$refs[formName].validate((valid) => {
+          if (valid) {
+            this.$confirm('是否提交数据', '提示', {
+              confirmButtonText: '确定',
+              cancelButtonText: '取消',
+              type: 'warning'
+            }).then(() => {
+              if (this.isEdit) {
+                updateMenu(this.$route.query.id, this.menu).then(response => {
+                  this.$message({
+                    message: '修改成功',
+                    type: 'success',
+                    duration: 1000
+                  });
+                  this.$router.back();
+                });
+              } else {
+                createMenu(this.menu).then(response => {
+                  this.$refs[formName].resetFields();
+                  this.resetForm(formName);
+                  this.$message({
+                    message: '提交成功',
+                    type: 'success',
+                    duration: 1000
+                  });
+                  this.$router.back();
+                });
+              }
+            });
+
+          } else {
+            this.$message({
+              message: '验证失败',
+              type: 'error',
+              duration: 1000
+            });
+            return false;
+          }
+        });
+      },
+      resetForm(formName) {
+        this.$refs[formName].resetFields();
+        this.menu = Object.assign({}, defaultMenu);
+        this.getSelectMenuList();
+      },
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 196 - 0
src/views/ums/menu/index.vue

@@ -0,0 +1,196 @@
+<template>
+  <div class="app-container">
+    <el-card class="operate-container" shadow="never">
+      <i class="el-icon-tickets" style="margin-top: 5px"></i>
+      <span style="margin-top: 5px">数据列表</span>
+      <el-button
+        class="btn-add"
+        @click="handleAddMenu()"
+        size="mini">
+        添加
+      </el-button>
+    </el-card>
+    <div class="table-container">
+      <el-table ref="menuTable"
+                style="width: 100%"
+                :data="list"
+                v-loading="listLoading" border>
+        <el-table-column label="编号" width="100" align="center">
+          <template slot-scope="scope">{{scope.row.id}}</template>
+        </el-table-column>
+        <el-table-column label="菜单名称" align="center">
+          <template slot-scope="scope">{{scope.row.title}}</template>
+        </el-table-column>
+        <el-table-column label="菜单级数" width="100" align="center">
+          <template slot-scope="scope">{{scope.row.level | levelFilter}}</template>
+        </el-table-column>
+        <el-table-column label="前端名称" align="center">
+          <template slot-scope="scope">{{scope.row.name}}</template>
+        </el-table-column>
+        <el-table-column label="前端图标" width="100" align="center">
+          <template slot-scope="scope"><svg-icon :icon-class="scope.row.icon"></svg-icon></template>
+        </el-table-column>
+        <el-table-column label="是否显示" width="100" align="center">
+          <template slot-scope="scope">
+            <el-switch
+              @change="handleHiddenChange(scope.$index, scope.row)"
+              :active-value="0"
+              :inactive-value="1"
+              v-model="scope.row.hidden">
+            </el-switch>
+          </template>
+        </el-table-column>
+        <el-table-column label="排序" width="100" align="center">
+          <template slot-scope="scope">{{scope.row.sort }}</template>
+        </el-table-column>
+        <el-table-column label="设置" width="120" align="center">
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              type="text"
+              :disabled="scope.row.level | disableNextLevel"
+              @click="handleShowNextLevel(scope.$index, scope.row)">查看下级
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="200" align="center">
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              type="text"
+              @click="handleUpdate(scope.$index, scope.row)">编辑
+            </el-button>
+            <el-button
+              size="mini"
+              type="text"
+              @click="handleDelete(scope.$index, scope.row)">删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="pagination-container">
+      <el-pagination
+        background
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        layout="total, sizes,prev, pager, next,jumper"
+        :page-size="listQuery.pageSize"
+        :page-sizes="[10,15,20]"
+        :current-page.sync="listQuery.pageNum"
+        :total="total">
+      </el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {fetchList,deleteMenu,updateMenu,updateHidden} from '@/api/menu'
+
+  export default {
+    name: "menuList",
+    data() {
+      return {
+        list: null,
+        total: null,
+        listLoading: true,
+        listQuery: {
+          pageNum: 1,
+          pageSize: 5
+        },
+        parentId: 0
+      }
+    },
+    created() {
+      this.resetParentId();
+      this.getList();
+    },
+    watch: {
+      $route(route) {
+        this.resetParentId();
+        this.getList();
+      }
+    },
+    methods: {
+      resetParentId(){
+        this.listQuery.pageNum = 1;
+        if (this.$route.query.parentId != null) {
+          this.parentId = this.$route.query.parentId;
+        } else {
+          this.parentId = 0;
+        }
+      },
+      handleAddMenu() {
+        this.$router.push('/ums/addMenu');
+      },
+      getList() {
+        this.listLoading = true;
+        fetchList(this.parentId, this.listQuery).then(response => {
+          this.listLoading = false;
+          this.list = response.data.list;
+          this.total = response.data.total;
+        });
+      },
+      handleSizeChange(val) {
+        this.listQuery.pageNum = 1;
+        this.listQuery.pageSize = val;
+        this.getList();
+      },
+      handleCurrentChange(val) {
+        this.listQuery.pageNum = val;
+        this.getList();
+      },
+      handleHiddenChange(index, row) {
+        updateHidden(row.id,{hidden:row.hidden}).then(response=>{
+          this.$message({
+            message: '修改成功',
+            type: 'success',
+            duration: 1000
+          });
+        });
+      },
+      handleShowNextLevel(index, row) {
+        this.$router.push({path: '/ums/menu', query: {parentId: row.id}})
+      },
+      handleUpdate(index, row) {
+        this.$router.push({path:'/ums/updateMenu',query:{id:row.id}});
+      },
+      handleDelete(index, row) {
+        this.$confirm('是否要删除该菜单', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          deleteMenu(row.id).then(response => {
+            this.$message({
+              message: '删除成功',
+              type: 'success',
+              duration: 1000
+            });
+            this.getList();
+          });
+        });
+      }
+    },
+    filters: {
+      levelFilter(value) {
+        if (value === 0) {
+          return '一级';
+        } else if (value === 1) {
+          return '二级';
+        }
+      },
+      disableNextLevel(value) {
+        if (value === 0) {
+          return false;
+        } else {
+          return true;
+        }
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 14 - 0
src/views/ums/menu/update.vue

@@ -0,0 +1,14 @@
+<template> 
+  <menu-detail :is-edit='true'></menu-detail>
+</template>
+<script>
+  import MenuDetail from './components/MenuDetail'
+  export default {
+    name: 'updateMenu',
+    components: { MenuDetail }
+  }
+</script>
+<style>
+</style>
+
+

+ 156 - 0
src/views/ums/resource/categoryList.vue

@@ -0,0 +1,156 @@
+<template> 
+  <div class="app-container">
+    <el-card shadow="never" class="operate-container">
+      <i class="el-icon-tickets"></i>
+      <span>数据列表</span>
+      <el-button size="mini" class="btn-add" @click="handleAdd()">添加</el-button>
+    </el-card>
+    <div class="table-container">
+      <el-table ref="resourceCategoryTable"
+                :data="list"
+                style="width: 100%;"
+                v-loading="listLoading" border>
+        <el-table-column label="编号" width="100" align="center">
+          <template slot-scope="scope">{{scope.row.id}}</template>
+        </el-table-column>
+        <el-table-column label="名称" align="center">
+          <template slot-scope="scope">{{scope.row.name}}</template>
+        </el-table-column>
+        <el-table-column label="创建时间" align="center">
+          <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
+        </el-table-column>
+        <el-table-column label="排序" align="center">
+          <template slot-scope="scope">{{scope.row.sort}}</template>
+        </el-table-column>
+        <el-table-column label="操作" width="180" align="center">
+          <template slot-scope="scope">
+            <el-button size="mini"
+                       type="text"
+                       @click="handleUpdate(scope.$index, scope.row)">编辑
+            </el-button>
+            <el-button size="mini"
+                       type="text"
+                       @click="handleDelete(scope.$index, scope.row)">删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <el-dialog
+      title="添加分类"
+      :visible.sync="dialogVisible"
+      width="40%">
+      <el-form :model="resourceCategory"
+               ref="resourceCategoryForm"
+               label-width="150px" size="small">
+        <el-form-item label="名称:">
+          <el-input v-model="resourceCategory.name" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="排序:">
+          <el-input v-model="resourceCategory.sort" style="width: 250px"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false" size="small">取 消</el-button>
+        <el-button type="primary" @click="handleDialogConfirm()" size="small">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import {listAllCate,createResourceCategory,updateResourceCategory,deleteResourceCategory} from '@/api/resourceCategory';
+  import {formatDate} from '@/utils/date';
+  const defaultResourceCategory={
+    name:null,
+    sort:0
+  };
+  export default {
+    name: 'resourceCategoryList',
+    data() {
+      return {
+        list: null,
+        listLoading: false,
+        dialogVisible:false,
+        isEdit:false,
+        resourceCategory:Object.assign({},defaultResourceCategory)
+      }
+    },
+    created() {
+      this.getList();
+    },
+    filters:{
+      formatDateTime(time) {
+        if (time == null || time === '') {
+          return 'N/A';
+        }
+        let date = new Date(time);
+        return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
+      }
+    },
+    methods: {
+      handleAdd() {
+        this.dialogVisible = true;
+        this.isEdit = false;
+        this.resourceCategory = Object.assign({},defaultResourceCategory);
+      },
+      handleUpdate(index,row){
+        this.dialogVisible = true;
+        this.isEdit = true;
+        this.resourceCategory = Object.assign({},row);
+      },
+      handleDelete(index,row){
+        this.$confirm('是否要删除该分类?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          deleteResourceCategory(row.id).then(response => {
+            this.$message({
+              type: 'success',
+              message: '删除成功!'
+            });
+            this.getList();
+          });
+        });
+      },
+      handleDialogConfirm() {
+        this.$confirm('是否要确认?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          if (this.isEdit) {
+            updateResourceCategory(this.resourceCategory.id,this.resourceCategory).then(response => {
+              this.$message({
+                message: '修改成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          } else {
+            createResourceCategory(this.resourceCategory).then(response => {
+              this.$message({
+                message: '添加成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          }
+        })
+      },
+      getList() {
+        this.listLoading = true;
+        listAllCate({}).then(response => {
+          this.listLoading = false;
+          this.list = response.data;
+        });
+      }
+    }
+  }
+</script>
+<style>
+</style>
+
+

+ 273 - 0
src/views/ums/resource/index.vue

@@ -0,0 +1,273 @@
+<template> 
+  <div class="app-container">
+    <el-card class="filter-container" shadow="never">
+      <div>
+        <i class="el-icon-search"></i>
+        <span>筛选搜索</span>
+        <el-button
+          style="float:right"
+          type="primary"
+          @click="handleSearchList()"
+          size="small">
+          查询搜索
+        </el-button>
+        <el-button
+          style="float:right;margin-right: 15px"
+          @click="handleResetSearch()"
+          size="small">
+          重置
+        </el-button>
+      </div>
+      <div style="margin-top: 15px">
+        <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
+          <el-form-item label="资源名称:">
+            <el-input v-model="listQuery.nameKeyword" class="input-width" placeholder="资源名称" clearable></el-input>
+          </el-form-item>
+          <el-form-item label="资源路径:">
+            <el-input v-model="listQuery.urlKeyword" class="input-width" placeholder="资料路径" clearable></el-input>
+          </el-form-item>
+          <el-form-item label="资源分类:">
+            <el-select v-model="listQuery.categoryId" placeholder="全部" clearable class="input-width">
+              <el-option v-for="item in categoryOptions"
+                         :key="item.value"
+                         :label="item.label"
+                         :value="item.value">
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
+    </el-card>
+    <el-card class="operate-container" shadow="never">
+      <i class="el-icon-tickets"></i>
+      <span>数据列表</span>
+      <el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
+      <el-button size="mini" class="btn-add" @click="handleShowCategory()">资源分类</el-button>
+    </el-card>
+    <div class="table-container">
+      <el-table ref="resourceTable"
+                :data="list"
+                style="width: 100%;"
+                v-loading="listLoading" border>
+        <el-table-column label="编号" width="100" align="center">
+          <template slot-scope="scope">{{scope.row.id}}</template>
+        </el-table-column>
+        <el-table-column label="资源名称" align="center">
+          <template slot-scope="scope">{{scope.row.name}}</template>
+        </el-table-column>
+        <el-table-column label="资源路径" align="center">
+          <template slot-scope="scope">{{scope.row.url}}</template>
+        </el-table-column>
+        <el-table-column label="描述" align="center">
+          <template slot-scope="scope">{{scope.row.description}}</template>
+        </el-table-column>
+        <el-table-column label="添加时间" width="160" align="center">
+          <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
+        </el-table-column>
+        <el-table-column label="操作" width="140" align="center">
+          <template slot-scope="scope">
+            <el-button size="mini"
+                       type="text"
+                       @click="handleUpdate(scope.$index, scope.row)">
+              编辑
+            </el-button>
+            <el-button size="mini"
+                       type="text"
+                       @click="handleDelete(scope.$index, scope.row)">删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="pagination-container">
+      <el-pagination
+        background
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        layout="total, sizes,prev, pager, next,jumper"
+        :current-page.sync="listQuery.pageNum"
+        :page-size="listQuery.pageSize"
+        :page-sizes="[10,15,20]"
+        :total="total">
+      </el-pagination>
+    </div>
+    <el-dialog
+      :title="isEdit?'编辑资源':'添加资源'"
+      :visible.sync="dialogVisible"
+      width="40%">
+      <el-form :model="resource"
+               ref="resourceForm"
+               label-width="150px" size="small">
+        <el-form-item label="资源名称:">
+          <el-input v-model="resource.name" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="资源路径:">
+          <el-input v-model="resource.url" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="资源分类:">
+          <el-select v-model="resource.categoryId" placeholder="全部" clearable style="width: 250px">
+            <el-option v-for="item in categoryOptions"
+                       :key="item.value"
+                       :label="item.label"
+                       :value="item.value">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="描述:">
+          <el-input v-model="resource.description"
+                    type="textarea"
+                    :rows="5"
+                    style="width: 250px"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false" size="small">取 消</el-button>
+        <el-button type="primary" @click="handleDialogConfirm()" size="small">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import {fetchList,createResource,updateResource,deleteResource} from '@/api/resource';
+  import {listAllCate} from '@/api/resourceCategory';
+  import {formatDate} from '@/utils/date';
+
+  const defaultListQuery = {
+    pageNum: 1,
+    pageSize: 10,
+    nameKeyword: null,
+    urlKeyword: null,
+    categoryId:null
+  };
+  const defaultResource = {
+    id: null,
+    name: null,
+    url: null,
+    categoryId: null,
+    description:''
+  };
+  export default {
+    name: 'resourceList',
+    data() {
+      return {
+        listQuery: Object.assign({}, defaultListQuery),
+        list: null,
+        total: null,
+        listLoading: false,
+        dialogVisible: false,
+        resource: Object.assign({}, defaultResource),
+        isEdit: false,
+        categoryOptions:[],
+        defaultCategoryId:null
+      }
+    },
+    created() {
+      this.getList();
+      this.getCateList();
+    },
+    filters: {
+      formatDateTime(time) {
+        if (time == null || time === '') {
+          return 'N/A';
+        }
+        let date = new Date(time);
+        return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
+      }
+    },
+    methods: {
+      handleResetSearch() {
+        this.listQuery = Object.assign({}, defaultListQuery);
+      },
+      handleSearchList() {
+        this.listQuery.pageNum = 1;
+        this.getList();
+      },
+      handleSizeChange(val) {
+        this.listQuery.pageNum = 1;
+        this.listQuery.pageSize = val;
+        this.getList();
+      },
+      handleCurrentChange(val) {
+        this.listQuery.pageNum = val;
+        this.getList();
+      },
+      handleAdd() {
+        this.dialogVisible = true;
+        this.isEdit = false;
+        this.resource = Object.assign({},defaultResource);
+        this.resource.categoryId = this.defaultCategoryId;
+      },
+      handleDelete(index, row) {
+        this.$confirm('是否要删除该资源?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          deleteResource(row.id).then(response => {
+            this.$message({
+              type: 'success',
+              message: '删除成功!'
+            });
+            this.getList();
+          });
+        });
+      },
+      handleUpdate(index, row) {
+        this.dialogVisible = true;
+        this.isEdit = true;
+        this.resource = Object.assign({},row);
+      },
+      handleDialogConfirm() {
+        this.$confirm('是否要确认?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          if (this.isEdit) {
+            updateResource(this.resource.id,this.resource).then(response => {
+              this.$message({
+                message: '修改成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          } else {
+            createResource(this.resource).then(response => {
+              this.$message({
+                message: '添加成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          }
+        })
+      },
+      handleShowCategory(){
+        this.$router.push({path: '/ums/resourceCategory'})
+      },
+      getList() {
+        this.listLoading = true;
+        fetchList(this.listQuery).then(response => {
+          this.listLoading = false;
+          this.list = response.data.list;
+          this.total = response.data.total;
+        });
+      },
+      getCateList(){
+        listAllCate().then(response=>{
+          let cateList = response.data;
+          for(let i=0;i<cateList.length;i++){
+            let cate = cateList[i];
+            this.categoryOptions.push({label:cate.name,value:cate.id});
+          }
+          this.defaultCategoryId=cateList[0].id;
+        })
+      }
+    }
+  }
+</script>
+<style></style>
+
+

+ 101 - 0
src/views/ums/role/allocMenu.vue

@@ -0,0 +1,101 @@
+<template>
+  <el-card class="form-container" shadow="never">
+    <el-tree
+      :data="menuTreeList"
+      show-checkbox
+      default-expand-all
+      node-key="id"
+      ref="tree"
+      highlight-current
+      :props="defaultProps">
+    </el-tree>
+    <div style="margin-top: 20px" align="center">
+      <el-button type="primary" @click="handleSave()">保存</el-button>
+      <el-button @click="handleClear()">清空</el-button>
+    </div>
+
+  </el-card>
+</template>
+
+<script>
+  import {fetchTreeList} from '@/api/menu';
+  import {listMenuByRole,allocMenu} from '@/api/role';
+
+  export default {
+    name: "allocMenu",
+    data() {
+      return {
+        menuTreeList: [],
+        defaultProps: {
+          children: 'children',
+          label: 'title'
+        },
+        roleId:null
+      };
+    },
+    created() {
+      this.roleId = this.$route.query.roleId;
+      this.treeList();
+      this.getRoleMenu(this.roleId);
+    },
+    methods: {
+      treeList() {
+        fetchTreeList().then(response => {
+          this.menuTreeList = response.data;
+        });
+      },
+      getRoleMenu(roleId){
+        listMenuByRole(roleId).then(response=>{
+          let menuList = response.data;
+          let checkedMenuIds=[];
+          if(menuList!=null&&menuList.length>0){
+            for(let i=0;i<menuList.length;i++){
+              let menu = menuList[i];
+              if(menu.parentId!==0){
+                checkedMenuIds.push(menu.id);
+              }
+            }
+          }
+          this.$refs.tree.setCheckedKeys(checkedMenuIds);
+        });
+      },
+      handleSave() {
+        let checkedNodes = this.$refs.tree.getCheckedNodes();
+        let checkedMenuIds=new Set();
+        if(checkedNodes!=null&&checkedNodes.length>0){
+          for(let i=0;i<checkedNodes.length;i++){
+            let checkedNode = checkedNodes[i];
+            checkedMenuIds.add(checkedNode.id);
+            if(checkedNode.parentId!==0){
+              checkedMenuIds.add(checkedNode.parentId);
+            }
+          }
+        }
+        this.$confirm('是否分配菜单?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(()=>{
+          let params = new URLSearchParams();
+          params.append("roleId",this.roleId);
+          params.append("menuIds",Array.from(checkedMenuIds));
+          allocMenu(params).then(response => {
+            this.$message({
+              message: '分配成功',
+              type: 'success',
+              duration: 1000
+            });
+            this.$router.back();
+          })
+        })
+      },
+      handleClear() {
+        this.$refs.tree.setCheckedKeys([]);
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 187 - 0
src/views/ums/role/allocResource.vue

@@ -0,0 +1,187 @@
+<template>
+  <el-card class="form-container" shadow="never">
+    <div v-for="(cate,index) in allResourceCate" :class="index===0?'top-line':null" :key="'cate'+cate.id">
+      <el-row class="table-layout" style="background: #F2F6FC;">
+        <el-checkbox v-model="cate.checked"
+                     :indeterminate="isIndeterminate(cate.id)"
+                     @change="handleCheckAllChange(cate)">
+          {{cate.name}}
+        </el-checkbox>
+      </el-row>
+      <el-row class="table-layout">
+        <el-col :span="8" v-for="resource in getResourceByCate(cate.id)" :key="resource.id" style="padding: 4px 0">
+          <el-checkbox v-model="resource.checked" @change="handleCheckChange(resource)">
+            {{resource.name}}
+          </el-checkbox>
+        </el-col>
+      </el-row>
+    </div>
+    <div style="margin-top: 20px" align="center">
+      <el-button type="primary" @click="handleSave()">保存</el-button>
+      <el-button @click="handleClear()">清空</el-button>
+    </div>
+
+  </el-card>
+</template>
+
+<script>
+  import {fetchAllResourceList} from '@/api/resource';
+  import {listAllCate} from '@/api/resourceCategory';
+  import {allocResource,listResourceByRole} from '@/api/role';
+
+  export default {
+    name: "allocResource",
+    data() {
+      return {
+        roleId: null,
+        allResource: null,
+        allResourceCate: null
+      };
+    },
+    created() {
+      this.roleId = this.$route.query.roleId;
+      this.getAllResourceCateList();
+    },
+    methods: {
+      getAllResourceList() {
+        fetchAllResourceList().then(response => {
+          this.allResource = response.data;
+          for (let i = 0; i < this.allResource.length; i++) {
+            this.allResource[i].checked = false;
+          }
+          this.getResourceByRole(this.roleId);
+        });
+      },
+      getAllResourceCateList() {
+        listAllCate().then(response => {
+          this.allResourceCate = response.data;
+          for (let i = 0; i < this.allResourceCate.length; i++) {
+            this.allResourceCate[i].checked = false;
+          }
+          this.getAllResourceList();
+        });
+      },
+      getResourceByCate(categoryId) {
+        let cateResource = [];
+        if (this.allResource == null) return null;
+        for (let i = 0; i < this.allResource.length; i++) {
+          let resource = this.allResource[i];
+          if (resource.categoryId === categoryId) {
+            cateResource.push(resource);
+          }
+        }
+        return cateResource;
+      },
+      getResourceByRole(roleId){
+        listResourceByRole(roleId).then(response=>{
+          let allocResource = response.data;
+          this.allResource.forEach(item=>{
+            item.checked = this.getResourceChecked(item.id,allocResource);
+          });
+          this.allResourceCate.forEach(item=>{
+            item.checked = this.isAllChecked(item.id);
+          });
+          this.$forceUpdate();
+        });
+      },
+      getResourceChecked(resourceId,allocResource){
+        if(allocResource==null||allocResource.length===0) return false;
+        for(let i=0;i<allocResource.length;i++){
+          if(allocResource[i].id===resourceId){
+            return true;
+          }
+        }
+        return false;
+      },
+      isIndeterminate(categoryId) {
+        let cateResources = this.getResourceByCate(categoryId);
+        if (cateResources == null) return false;
+        let checkedCount = 0;
+        for (let i = 0; i < cateResources.length; i++) {
+          if (cateResources[i].checked === true) {
+            checkedCount++;
+          }
+        }
+        return !(checkedCount === 0 || checkedCount === cateResources.length);
+      },
+      isAllChecked(categoryId) {
+        let cateResources = this.getResourceByCate(categoryId);
+        if (cateResources == null) return false;
+        let checkedCount = 0;
+        for (let i = 0; i < cateResources.length; i++) {
+          if (cateResources[i].checked === true) {
+            checkedCount++;
+          }
+        }
+        if(checkedCount===0){
+          return false;
+        }
+        return checkedCount === cateResources.length;
+      },
+      handleSave() {
+        this.$confirm('是否分配资源?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          let checkedResourceIds = new Set();
+          if (this.allResource != null && this.allResource.length > 0) {
+            this.allResource.forEach(item => {
+              if (item.checked) {
+                checkedResourceIds.add(item.id);
+              }
+            });
+          }
+          let params = new URLSearchParams();
+          params.append("roleId", this.roleId);
+          params.append("resourceIds", Array.from(checkedResourceIds));
+          allocResource(params).then(response => {
+            this.$message({
+              message: '分配成功',
+              type: 'success',
+              duration: 1000
+            });
+            this.$router.back();
+          })
+        })
+      },
+      handleClear() {
+        this.allResourceCate.forEach(item => {
+          item.checked = false;
+        });
+        this.allResource.forEach(item => {
+          item.checked = false;
+        });
+        this.$forceUpdate();
+      },
+      handleCheckAllChange(cate) {
+        let cateResources = this.getResourceByCate(cate.id);
+        for (let i = 0; i < cateResources.length; i++) {
+          cateResources[i].checked = cate.checked;
+        }
+        this.$forceUpdate();
+      },
+      handleCheckChange(resource) {
+        this.allResourceCate.forEach(item=>{
+          if(item.id===resource.categoryId){
+            item.checked = this.isAllChecked(resource.categoryId);
+          }
+        });
+        this.$forceUpdate();
+      }
+    }
+  }
+</script>
+
+<style scoped>
+  .table-layout {
+    padding: 20px;
+    border-left: 1px solid #DCDFE6;
+    border-right: 1px solid #DCDFE6;
+    border-bottom: 1px solid #DCDFE6;
+  }
+
+  .top-line {
+    border-top: 1px solid #DCDFE6;
+  }
+</style>

+ 286 - 0
src/views/ums/role/index.vue

@@ -0,0 +1,286 @@
+<template> 
+  <div class="app-container">
+    <el-card class="filter-container" shadow="never">
+      <div>
+        <i class="el-icon-search"></i>
+        <span>筛选搜索</span>
+        <el-button
+          style="float:right"
+          type="primary"
+          @click="handleSearchList()"
+          size="small">
+          查询搜索
+        </el-button>
+        <el-button
+          style="float:right;margin-right: 15px"
+          @click="handleResetSearch()"
+          size="small">
+          重置
+        </el-button>
+      </div>
+      <div style="margin-top: 15px">
+        <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
+          <el-form-item label="输入搜索:">
+            <el-input v-model="listQuery.keyword" class="input-width" placeholder="角色名称" clearable></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+    </el-card>
+    <el-card class="operate-container" shadow="never">
+      <i class="el-icon-tickets"></i>
+      <span>数据列表</span>
+      <el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
+    </el-card>
+    <div class="table-container">
+      <el-table ref="roleTable"
+                :data="list"
+                style="width: 100%;"
+                v-loading="listLoading" border>
+        <el-table-column label="编号" width="100" align="center">
+          <template slot-scope="scope">{{scope.row.id}}</template>
+        </el-table-column>
+        <el-table-column label="角色名称" align="center">
+          <template slot-scope="scope">{{scope.row.name}}</template>
+        </el-table-column>
+        <el-table-column label="描述" align="center">
+          <template slot-scope="scope">{{scope.row.description}}</template>
+        </el-table-column>
+        <el-table-column label="用户数"  width="100" align="center">
+          <template slot-scope="scope">{{scope.row.adminCount}}</template>
+        </el-table-column>
+        <el-table-column label="添加时间" width="160" align="center">
+          <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
+        </el-table-column>
+        <el-table-column label="是否启用" width="140" align="center">
+          <template slot-scope="scope">
+            <el-switch
+              @change="handleStatusChange(scope.$index, scope.row)"
+              :active-value="1"
+              :inactive-value="0"
+              v-model="scope.row.status">
+            </el-switch>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="160" align="center">
+          <template slot-scope="scope">
+            <el-row>
+              <el-button size="mini"
+                         type="text"
+                         @click="handleSelectMenu(scope.$index, scope.row)">分配菜单
+              </el-button>
+              <el-button size="mini"
+                         type="text"
+                         @click="handleSelectResource(scope.$index, scope.row)">分配资源
+              </el-button>
+            </el-row>
+            <el-row>
+            <el-button size="mini"
+                       type="text"
+                       @click="handleUpdate(scope.$index, scope.row)">
+              编辑
+            </el-button>
+            <el-button size="mini"
+                       type="text"
+                       @click="handleDelete(scope.$index, scope.row)">删除
+            </el-button>
+            </el-row>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="pagination-container">
+      <el-pagination
+        background
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        layout="total, sizes,prev, pager, next,jumper"
+        :current-page.sync="listQuery.pageNum"
+        :page-size="listQuery.pageSize"
+        :page-sizes="[5,10,15]"
+        :total="total">
+      </el-pagination>
+    </div>
+    <el-dialog
+      :title="isEdit?'编辑角色':'添加角色'"
+      :visible.sync="dialogVisible"
+      width="40%">
+      <el-form :model="role"
+               ref="roleForm"
+               label-width="150px" size="small">
+        <el-form-item label="角色名称:">
+          <el-input v-model="role.name" style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="描述:">
+          <el-input v-model="role.description"
+                    type="textarea"
+                    :rows="5"
+                    style="width: 250px"></el-input>
+        </el-form-item>
+        <el-form-item label="是否启用:">
+          <el-radio-group v-model="role.status">
+            <el-radio :label="1">是</el-radio>
+            <el-radio :label="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false" size="small">取 消</el-button>
+        <el-button type="primary" @click="handleDialogConfirm()" size="small">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import {fetchList,createRole,updateRole,updateStatus,deleteRole} from '@/api/role';
+  import {formatDate} from '@/utils/date';
+
+  const defaultListQuery = {
+    pageNum: 1,
+    pageSize: 5,
+    keyword: null
+  };
+  const defaultRole = {
+    id: null,
+    name: null,
+    description: null,
+    adminCount: 0,
+    status: 1
+  };
+  export default {
+    name: 'roleList',
+    data() {
+      return {
+        listQuery: Object.assign({}, defaultListQuery),
+        list: null,
+        total: null,
+        listLoading: false,
+        dialogVisible: false,
+        role: Object.assign({}, defaultRole),
+        isEdit: false
+      }
+    },
+    created() {
+      this.getList();
+    },
+    filters: {
+      formatDateTime(time) {
+        if (time == null || time === '') {
+          return 'N/A';
+        }
+        let date = new Date(time);
+        return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
+      }
+    },
+    methods: {
+      handleResetSearch() {
+        this.listQuery = Object.assign({}, defaultListQuery);
+      },
+      handleSearchList() {
+        this.listQuery.pageNum = 1;
+        this.getList();
+      },
+      handleSizeChange(val) {
+        this.listQuery.pageNum = 1;
+        this.listQuery.pageSize = val;
+        this.getList();
+      },
+      handleCurrentChange(val) {
+        this.listQuery.pageNum = val;
+        this.getList();
+      },
+      handleAdd() {
+        this.dialogVisible = true;
+        this.isEdit = false;
+        this.role = Object.assign({},defaultRole);
+      },
+      handleStatusChange(index, row) {
+        this.$confirm('是否要修改该状态?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          updateStatus(row.id, {status: row.status}).then(response => {
+            this.$message({
+              type: 'success',
+              message: '修改成功!'
+            });
+          });
+        }).catch(() => {
+          this.$message({
+            type: 'info',
+            message: '取消修改'
+          });
+          this.getList();
+        });
+      },
+      handleDelete(index, row) {
+        this.$confirm('是否要删除该角色?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          let ids = [];
+          ids.push(row.id);
+          let params=new URLSearchParams();
+          params.append("ids",ids);
+          deleteRole(params).then(response => {
+            this.$message({
+              type: 'success',
+              message: '删除成功!'
+            });
+            this.getList();
+          });
+        });
+      },
+      handleUpdate(index, row) {
+        this.dialogVisible = true;
+        this.isEdit = true;
+        this.role = Object.assign({},row);
+      },
+      handleDialogConfirm() {
+        this.$confirm('是否要确认?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          if (this.isEdit) {
+            updateRole(this.role.id,this.role).then(response => {
+              this.$message({
+                message: '修改成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          } else {
+            createRole(this.role).then(response => {
+              this.$message({
+                message: '添加成功!',
+                type: 'success'
+              });
+              this.dialogVisible =false;
+              this.getList();
+            })
+          }
+        })
+      },
+      handleSelectMenu(index,row){
+        this.$router.push({path:'/ums/allocMenu',query:{roleId:row.id}})
+      },
+      handleSelectResource(index,row){
+        this.$router.push({path:'/ums/allocResource',query:{roleId:row.id}})
+      },
+      getList() {
+        this.listLoading = true;
+        fetchList(this.listQuery).then(response => {
+          this.listLoading = false;
+          this.list = response.data.list;
+          this.total = response.data.total;
+        });
+      }
+    }
+  }
+</script>
+<style></style>
+
+

Some files were not shown because too many files changed in this diff