Przeglądaj źródła

refactor: permission

Casper Dai 3 lat temu
rodzic
commit
9d6e7ef2eb

+ 15 - 13
src/components/Permission/index.js

@@ -1,11 +1,9 @@
-import { mapGetters } from 'vuex'
-
 function hasPermission (roles, include) {
   if (include) {
     if (typeof include === 'string') {
       return roles.has(include)
     }
-    return include.some(role => roles.includes(role))
+    return include.some(role => roles.has(role))
   }
   return false
 }
@@ -18,6 +16,10 @@ export default {
       type: [Boolean, String],
       default: false
     },
+    condition: {
+      type: Boolean,
+      default: true
+    },
     include: {
       type: [String, Array],
       default: null
@@ -28,21 +30,21 @@ export default {
     }
   },
   computed: {
-    ...mapGetters([
-      'roles',
-      'isAdmin',
-      'accesses'
-    ]),
     allow () {
-      let allow = this.isAdmin || hasPermission(this.roles, this.include)
-      if (allow && this.access) {
-        allow = this.accesses.has(this.access)
+      if (this.include || this.access) {
+        if (this.include && !hasPermission(this.roleSet, this.include)) {
+          return false
+        }
+        if (this.access && !this.accessSet.has(this.access)) {
+          return false
+        }
+        return true
       }
-      return allow
+      return false
     }
   },
   render () {
-    if (this.skip || this.allow) {
+    if (this.skip || this.condition && this.allow) {
       return this.$slots.default
     }
     return null

+ 76 - 0
src/constant.js

@@ -24,5 +24,81 @@ export const ScheduleType = {
 }
 
 export const Access = {
+  DEBUG: 'frontend:debug',
+
+  VIEW_PRODUCTS: 'frontend:view-products',
+  VIEW_DEVICES: 'frontend:view-devices',
+  VIEW_DEVICE: 'frontend:view-device',
+  VIEW_GROUPS: 'frontend:view-groups',
+  VIEW_ASSETS: 'frontend:view-assets',
+  VIEW_CALENDAR: 'frontend:view-calendar',
+  VIEW_LOGS: 'frontend:view-logs',
+  VIEW_UPGRADE: 'frontend:view-upgrade',
+
+  MANAGE_PROFILE: 'backend:manage-profile',
+  MANAGE_PRODUCTS: 'backend:manage-products',
+  MANAGE_DEVICES: 'backend:manage-devices',
+  MANAGE_DEVICE: 'backend:manage-device',
+  MANAGE_GROUPS: 'backend:manage-groups',
+  MANAGE_ASSETS: 'backend:manage-assets',
+  MANAGE_CALENDAR: 'backend:manage-calendar',
+  MANAGE_UPGRADE: 'backend:manage-upgrade',
+
+  PUBLISH_CALENDAR: 'backend:publish-calendar',
+  REVIEW: 'backend:review',
+
   DELETE_FORCE: 'backend-api:delete-force'
 }
+
+export const RoleAccess = {
+  [Role.ADMIN]: [
+    Access.DEBUG,
+    Access.VIEW_PRODUCTS,
+    Access.VIEW_GROUPS,
+    Access.VIEW_DEVICES,
+    Access.VIEW_DEVICE,
+    Access.VIEW_ASSETS,
+    Access.VIEW_CALENDAR,
+    Access.VIEW_LOGS,
+    Access.VIEW_UPGRADE,
+    Access.MANAGE_PROFILE,
+    Access.MANAGE_PRODUCTS,
+    Access.MANAGE_DEVICES,
+    Access.MANAGE_DEVICE,
+    Access.MANAGE_GROUPS,
+    Access.MANAGE_ASSETS,
+    Access.MANAGE_CALENDAR,
+    Access.MANAGE_UPGRADE,
+    Access.PUBLISH_CALENDAR,
+    Access.REVIEW
+  ],
+  [Role.SUPERVISOR]: [
+    Access.VIEW_DEVICES,
+    Access.VIEW_DEVICE,
+    Access.VIEW_ASSETS,
+    Access.VIEW_CALENDAR,
+    Access.VIEW_LOGS,
+    Access.MANAGE_PROFILE,
+    Access.MANAGE_DEVICE,
+    Access.MANAGE_GROUPS,
+    Access.MANAGE_ASSETS,
+    Access.MANAGE_CALENDAR,
+    Access.REVIEW
+  ],
+  [Role.STAFF]: [
+    Access.VIEW_DEVICES,
+    Access.VIEW_DEVICE,
+    Access.VIEW_ASSETS,
+    Access.VIEW_CALENDAR,
+    Access.MANAGE_PROFILE,
+    Access.MANAGE_DEVICE,
+    Access.MANAGE_GROUPS,
+    Access.MANAGE_ASSETS,
+    Access.MANAGE_CALENDAR,
+    Access.PUBLISH_CALENDAR
+  ],
+  [Role.VISITOR]: [
+    Access.VIEW_DEVICE,
+    Access.MANAGE_PROFILE
+  ]
+}

+ 13 - 0
src/layout/Solo.vue

@@ -6,10 +6,23 @@
 
 <script>
 export default {
+  name: 'Solo',
   computed: {
     cacheRoutes () {
       return this.$route.meta?.cache || []
     }
+  },
+  watch: {
+    '$route': {
+      handler () {
+        // solo无对应name
+        const matched = this.$route.matched
+        if (!matched[matched.length - 1].name) {
+          this.$router.replace({ path: '/' })
+        }
+      },
+      immediate: true
+    }
   }
 }
 </script>

+ 3 - 4
src/layout/components/Navbar/index.vue

@@ -7,10 +7,9 @@
     >
       <i class="c-navbar__count">1</i>
     </i>
-    <upload-dashboard
-      v-if="hasEditPermission"
-      class="l-flex__none c-navbar__item"
-    />
+    <permission :access="Access.MANAGE_ASSETS">
+      <upload-dashboard class="l-flex__none c-navbar__item" />
+    </permission>
     <el-dropdown
       class="l-flex__none c-navbar__item c-navbar__user u-pointer"
       trigger="click"

+ 4 - 5
src/layout/components/Sidebar/index.vue

@@ -46,10 +46,6 @@ function filterRoutes (routes, basePath) {
     }
     const { name, meta = {} } = route
     const path = basePath ? resolve(basePath, route.path) : route.path
-    if (basePath) {
-      res.push({ path, name, meta })
-      return
-    }
     let children = route.children
     if (children) {
       children = filterRoutes(children, path)
@@ -61,8 +57,10 @@ function filterRoutes (routes, basePath) {
         res.push({ path: resolve(path, cpath), name: cname, meta: { ...meta, ...cmeta } })
         return
       }
+      res.push({ path, name, meta, children })
+    } else {
+      res.push({ path, name, meta })
     }
-    res.push({ path, name, meta, children })
   })
 
   return res
@@ -74,6 +72,7 @@ export default {
   computed: {
     ...mapGetters(['permissionRoutes']),
     routers () {
+      console.log(filterRoutes(this.permissionRoutes), this.permissionRoutes)
       // 暂时只考虑两层
       return filterRoutes(this.permissionRoutes)
     },

+ 10 - 6
src/main.js

@@ -13,10 +13,7 @@ import router from './router'
 import './icons'
 import './permission'
 
-import {
-  Role,
-  Access
-} from './constant'
+import { Access } from './constant'
 
 import Wrapper from './components/Wrapper'
 import Permission from './components/Permission'
@@ -61,10 +58,17 @@ function startApp () {
   }
 
   store.dispatch('user/login', keycloak)
-  store.dispatch('permission/generateRoutes', store.getters.roles)
+  store.dispatch('permission/generateRoutes', {
+    roles: store.getters.roles,
+    accesses: store.getters.accesses
+  })
   router.addRoutes(store.getters.permissionRoutes)
 
-  Vue.prototype.hasEditPermission = !store.getters.roles.has(Role.VISITOR)
+  console.log(store.getters.roles)
+  console.log(store.getters.accesses)
+
+  Vue.prototype.roleSet = store.getters.roles
+  Vue.prototype.accessSet = store.getters.accesses
 
   new Vue({
     router,

+ 26 - 17
src/router/index.js

@@ -1,6 +1,6 @@
 import Vue from 'vue'
 import Router from 'vue-router'
-import { Role } from '@/constant'
+import { Access } from '@/constant'
 import Layout from '@/layout'
 import Solo from '@/layout/Solo'
 
@@ -8,15 +8,16 @@ Vue.use(Router)
 
 /**
  * sub-menu only appear when route children.length >= 1
- * dev: true                       if set true, item will not show in production
- * hidden: true                    if set true, item will not show in the sidebar(default is false)
- * name:'router-name'              the name is used by <keep-alive> (must set!!!)
+ * dev: true,                      if set true, item will not show in production
+ * hidden: true,                   if set true, item will not show in the sidebar(default is false)
+ * name:'router-name' ,            if it is not a wrapping layer(Layout or Solo), it must be set!!!
+ * include: ['admin'],             control the page roles (you can set multiple roles)
+ * access: 'manage-profile',       control the page access
  * meta : {
- *   include: ['admin'],           control the page roles (you can set multiple roles)
- *   exclude: ['visitor'],          control the page roles (you can set multiple roles)
  *   title: 'title',               the name show in sidebar and breadcrumb (recommend set)
  *   icon: 'svg-name'/'el-icon-x', the icon show in the sidebar
- *   activeMenu: '/example/list'   if set path, the sidebar will highlight the path you set
+ *   activeMenu: '/example/list',  if set path, the sidebar will highlight the path you set
+ *   cache: 'router-name'          if set, the router will be cached (only in Solo)
  * }
  */
 
@@ -53,6 +54,7 @@ export const asyncRoutes = [
         name: 'profile',
         path: 'profile',
         component: () => import('@/views/profile/index'),
+        access: Access.MANAGE_PROFILE,
         meta: { title: '个人设置' },
         hidden: true
       }
@@ -67,18 +69,20 @@ export const asyncRoutes = [
         name: 'media',
         path: 'media',
         component: () => import('@/views/media/index'),
-        exclude: [Role.VISITOR],
+        access: Access.VIEW_ASSETS,
         meta: { title: '媒资管理' }
       },
       {
         name: 'program',
         path: 'program',
         component: () => import('@/views/bigscreen/index'),
+        access: Access.VIEW_CALENDAR,
         meta: { title: '节目编排' }
       },
       {
         path: 'schedule',
         component: Solo,
+        access: Access.VIEW_CALENDAR,
         meta: { title: '节目排期' },
         children: [
           {
@@ -92,7 +96,7 @@ export const asyncRoutes = [
             name: 'schedule-design',
             path: ':id',
             component: () => import('@/views/schedule/designer/index'),
-            exclude: [Role.VISITOR],
+            access: Access.MANAGE_CALENDAR,
             meta: { title: '排期编辑', activeMenu: '/cm/schedule', cache: 'ScheduleList' },
             hidden: true
           }
@@ -102,20 +106,21 @@ export const asyncRoutes = [
         name: 'schedule-deploy',
         path: 'deploy',
         component: () => import('@/views/schedule/deploy/index'),
-        exclude: [Role.VISITOR],
+        access: Access.PUBLISH_CALENDAR,
         meta: { title: '排期发布' }
       },
       {
         name: 'review',
         path: 'review',
         component: () => import('@/views/review/index'),
-        include: [Role.SUPERVISOR],
+        access: Access.REVIEW,
         meta: { title: '审核管理' }
       },
       {
         name: 'schedule-deploy-history',
         path: 'history',
         component: () => import('@/views/schedule/history/index'),
+        access: Access.VIEW_CALENDAR,
         meta: { title: '发布历史' }
       }
     ]
@@ -123,6 +128,7 @@ export const asyncRoutes = [
   {
     path: '/dm',
     component: Layout,
+    access: Access.VIEW_DEVICE,
     meta: { title: '大屏设备', icon: 'el-icon-monitor' },
     children: [
       {
@@ -155,14 +161,14 @@ export const asyncRoutes = [
         name: 'category',
         path: 'category',
         component: () => import('@/views/device/category/index'),
-        include: [Role.ADMIN],
+        access: Access.VIEW_PRODUCTS,
         meta: { title: '产品分类' }
       },
       {
         name: 'product',
         path: 'product',
         component: () => import('@/views/device/product/index'),
-        include: [Role.ADMIN],
+        access: Access.VIEW_PRODUCTS,
         meta: { title: '产品管理' }
       },
       {
@@ -174,6 +180,7 @@ export const asyncRoutes = [
             name: 'device-list',
             path: '',
             component: () => import('@/views/device/index'),
+            access: Access.VIEW_DEVICES,
             meta: { cache: 'DeviceList' },
             hidden: true
           },
@@ -181,6 +188,7 @@ export const asyncRoutes = [
             name: 'device-detail',
             path: ':id',
             component: () => import('@/views/device/detail/index'),
+            access: Access.VIEW_DEVICE,
             meta: { title: '设备详情', activeMenu: '/m/device', cache: 'DeviceList' },
             hidden: true
           }
@@ -190,7 +198,7 @@ export const asyncRoutes = [
         name: 'group',
         path: 'group',
         component: () => import('@/views/device/group/index'),
-        exclude: [Role.VISITOR],
+        access: Access.VIEW_GROUPS,
         meta: { title: '分组管理' }
       }
     ]
@@ -198,6 +206,7 @@ export const asyncRoutes = [
   {
     path: '/l',
     component: Layout,
+    access: Access.VIEW_LOGS,
     meta: { icon: 'el-icon-edit-outline' },
     children: [
       {
@@ -211,7 +220,7 @@ export const asyncRoutes = [
   {
     path: '/d',
     component: Layout,
-    include: [Role.ADMIN],
+    access: Access.DEBUG,
     meta: { icon: 'bug' },
     children: [
       {
@@ -225,7 +234,7 @@ export const asyncRoutes = [
   {
     path: '/u',
     component: Layout,
-    include: [Role.ADMIN],
+    access: Access.VIEW_UPGRADE,
     meta: { title: '升级管理', icon: 'el-icon-upload' },
     children: [
       {
@@ -246,7 +255,7 @@ export const asyncRoutes = [
     name: 'design',
     path: '/design/:id',
     component: () => import('@/views/bigscreen/designer/index'),
-    exclude: [Role.VISITOR],
+    access: Access.MANAGE_CALENDAR,
     hidden: true
   },
   {

+ 0 - 5
src/store/getters.js

@@ -1,5 +1,3 @@
-import { Role } from '@/constant'
-
 const getters = {
   token (state) {
     return state.user.token
@@ -16,9 +14,6 @@ const getters = {
   permissionRoutes (state) {
     return state.permission.routes
   },
-  isAdmin (state, getters) {
-    return getters.roles.has(Role.ADMIN)
-  },
   accesses (state) {
     return state.user.accesses
   }

+ 9 - 12
src/store/modules/permission.js

@@ -1,20 +1,17 @@
 import { constantRoutes, asyncRoutes } from '@/router'
 import { Role } from '@/constant'
 
-function hasPermission (roles, { include, exclude }) {
-  if (include) {
-    return include.some(role => roles.has(role))
-  }
-  return !exclude || exclude.length < roles.size || exclude.some(role => !roles.has(role))
+function hasPermission (accessSet, access) {
+  return !access || accessSet.has(access)
 }
 
-export function filterAsyncRoutes (routes, roles, force) {
+export function filterAsyncRoutes (routes, accessSet) {
   const res = []
 
   routes.forEach(route => {
-    if (!route.dev && (force || hasPermission(roles, route))) {
+    if (!route.dev && hasPermission(accessSet, route.access)) {
       if (route.children) {
-        const children = filterAsyncRoutes(route.children, roles, force)
+        const children = filterAsyncRoutes(route.children, accessSet)
         if (!children.length) {
           return
         }
@@ -39,12 +36,12 @@ const mutations = {
 }
 
 const actions = {
-  generateRoutes ({ commit }, roles) {
+  generateRoutes ({ commit }, { roles, accesses }) {
     let accessedRoutes
-    if (roles.has(Role.ADMIN)) {
-      accessedRoutes = __DEV__ ? asyncRoutes : filterAsyncRoutes(asyncRoutes, roles, true)
+    if (__DEV__ && roles.has(Role.ADMIN)) {
+      accessedRoutes = asyncRoutes
     } else {
-      accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
+      accessedRoutes = filterAsyncRoutes(asyncRoutes, accesses)
     }
     commit('SET_ROUTES', constantRoutes.concat(accessedRoutes))
   }

+ 18 - 16
src/store/modules/user.js

@@ -1,6 +1,9 @@
 import Vue from 'vue'
 import { resetRouter } from '@/router'
-import { Role } from '@/constant'
+import {
+  Role,
+  RoleAccess
+} from '@/constant'
 
 const state = {
   token: '',
@@ -28,24 +31,22 @@ const mutations = {
   }
 }
 
-function parseRealmAccess (realmAccess) {
-  const roleSet = new Set()
+function parseAccess (realmAccess, resourceAccess) {
+  const roleSet = new Set([Role.VISITOR])
+  const accessSet = new Set()
   if (realmAccess) {
-    const roleArray = Object.values(Role)
+    const roles = new Set(Object.values(Role))
     realmAccess.roles.forEach(role => {
-      if (roleArray.includes(role)) {
+      if (roles.has(role)) {
         roleSet.add(role)
       }
     })
   }
-  if (!roleSet.size) {
-    roleSet.add(Role.VISITOR)
-  }
-  return roleSet
-}
-
-function parseResourceAccess (resourceAccess) {
-  const accessSet = new Set()
+  roleSet.forEach(role => {
+    RoleAccess[role].forEach(access => {
+      accessSet.add(access)
+    })
+  })
   if (resourceAccess) {
     Object.keys(resourceAccess).forEach(client => {
       resourceAccess[client].roles.forEach(role => {
@@ -53,16 +54,17 @@ function parseResourceAccess (resourceAccess) {
       })
     })
   }
-  return accessSet
+  return { roleSet, accessSet }
 }
 
 const actions = {
   login ({ commit }, keycloak) {
     if (keycloak.authenticated) {
       const { preferred_username, family_name, given_name, avatar } = keycloak.tokenParsed
+      const { roleSet, accessSet } = parseAccess(keycloak.realmAccess, keycloak.resourceAccess)
       commit('SET_TOKEN', keycloak.token)
-      commit('SET_ROLES', parseRealmAccess(keycloak.realmAccess))
-      commit('SET_ACCESSES', parseResourceAccess(keycloak.resourceAccess))
+      commit('SET_ROLES', roleSet)
+      commit('SET_ACCESSES', accessSet)
       commit('SET_NAME', family_name && given_name ? `${family_name}${/[a-zA-z]/.test(family_name) ? ' ' : ''}${given_name}` : preferred_username)
       commit('SET_AVATAR', avatar)
     }

+ 22 - 18
src/views/bigscreen/index.vue

@@ -8,7 +8,7 @@
     <div class="l-flex__none l-flex--row has-bottom-padding">
       <div class="l-flex__auto l-flex--row c-sibling-item">
         <button
-          v-if="hasEditPermission"
+          v-if="canEdit"
           class="o-button"
           @click="toAdd"
         >
@@ -17,7 +17,7 @@
         </button>
       </div>
       <el-select
-        v-if="hasEditPermission"
+        v-if="canEdit"
         v-model="params.status"
         class="l-flex__none c-sibling-item o-select"
         placeholder="请选择状态"
@@ -72,8 +72,9 @@
           </span>
           <div class="c-program__footer">
             <span class="c-program__time u-ellipsis">{{ item.createTime }}</span>
-            <template v-if="item.status === 0">
+            <template v-if="canEdit">
               <el-tooltip
+                v-if="item.status === 0"
                 content="提交"
                 :hide-after="2000"
               >
@@ -82,21 +83,21 @@
                   @click.stop="onSubmit(item)"
                 />
               </el-tooltip>
-            </template>
-            <permission
-              :skip="item.status === 0"
-              :access="Access.DELETE_FORCE"
-            >
-              <el-tooltip
-                content="删除"
-                :hide-after="2000"
+              <permission
+                :skip="item.status === 0"
+                :access="Access.DELETE_FORCE"
               >
-                <i
-                  class="o-icon--active el-icon-delete u-pointer"
-                  @click.stop="onDel(item)"
-                />
-              </el-tooltip>
-            </permission>
+                <el-tooltip
+                  content="删除"
+                  :hide-after="2000"
+                >
+                  <i
+                    class="o-icon--active el-icon-delete u-pointer"
+                    @click.stop="onDel(item)"
+                  />
+                </el-tooltip>
+              </permission>
+            </template>
           </div>
         </div>
         <edit-input
@@ -200,13 +201,16 @@ export default {
       ratios: [],
       ...createListOptions({
         pageSize: 8,
-        status: this.hasEditPermission ? void 0 : State.RESOLVED,
+        status: this.accessSet.has(this.Access.MANAGE_DEVICE) ? void 0 : State.RESOLVED,
         name: ''
       }),
       disabled: false
     }
   },
   computed: {
+    canEdit () {
+      return this.accessSet.has(this.Access.MANAGE_CALENDAR)
+    },
     isAbnormal () {
       return this.error || !this.loading && this.totalCount === 0
     }

+ 7 - 2
src/views/device/camera/index.vue

@@ -18,7 +18,7 @@
           <div class="l-flex--row c-table__header">
             <div class="l-flex__auto c-sibling-item">
               <button
-                v-if="hasEditPermission"
+                v-if="canEdit"
                 class="o-button"
                 @click="addbtn"
               >
@@ -71,7 +71,7 @@
                 </div>
                 <div class="o-video_buttom l-flex--row">
                   <div class="l-flex__auto">{{ video.name }}</div>
-                  <template v-if="hasEditPermission">
+                  <template v-if="canEdit">
                     <img
                       class="o-video_edit"
                       :src="imgUrl.edit"
@@ -317,6 +317,11 @@ export default {
       rowNum: 8
     }
   },
+  computed: {
+    canEdit () {
+      return this.accessSet.has(this.Access.MANAGE_DEVICES)
+    }
+  },
   created () {
     this.getCamera()
   },

+ 9 - 8
src/views/device/detail/components/DeviceInfo.vue

@@ -2,14 +2,15 @@
   <div class="c-info">
     <div class="l-flex--row has-bottom-padding u-bold">
       <span class="c-sibling-item">设备信息</span>
-      <span
-        v-if="hasEditPermission"
-        class="c-sibling-item c-info__edit u-pointer"
-        @click="toEdit"
-      >
-        <i class="el-icon-edit" />
-        编辑
-      </span>
+      <permission :access="Access.MANAGE_DEVICE">
+        <span
+          class="c-sibling-item c-info__edit u-pointer"
+          @click="toEdit"
+        >
+          <i class="el-icon-edit" />
+          编辑
+        </span>
+      </permission>
     </div>
     <div class="l-flex--row c-info__block">
       <div class="l-flex--row l-flex__fill c-info__item">

+ 1 - 3
src/views/device/detail/components/DeviceScreen.vue

@@ -37,7 +37,6 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
 import { getReceivingCard } from '@/api/device'
 import {
   send,
@@ -81,13 +80,12 @@ export default {
     }
   },
   computed: {
-    ...mapGetters(['isAdmin']),
     activeComponent () {
       switch (this.active) {
         case 'topology':
           return 'ReceivingCardTopology'
         case 'info':
-          return this.isAdmin ? 'ReceivingCardInfoEdit' : 'ReceivingCardInfo'
+          return this.accessSet.has(this.Access.MANAGE_DEVICES) ? 'ReceivingCardInfoEdit' : 'ReceivingCardInfo'
         default:
           return null
       }

+ 1 - 1
src/views/device/detail/index.vue

@@ -95,7 +95,7 @@ export default {
         { key: 'info', label: '设备信息' },
         { key: 'status', label: '运行状态' },
         { key: 'alarm', label: '设备告警' },
-        this.hasEditPermission ? { key: 'invoke', label: '设备操控' } : null,
+        this.accessSet.has(this.Access.MANAGE_DEVICE) ? { key: 'invoke', label: '设备操控' } : null,
         { key: 'screen', label: '屏体状态监测' }
       ].filter(val => val)
     }

+ 34 - 31
src/views/device/index.vue

@@ -122,7 +122,8 @@
         <template v-slot="scope">
           <el-tag
             v-if="scope.row.activate === 0"
-            class="o-tag u-pointer"
+            class="o-tag"
+            :class="{ 'u-pointer': canEdit }"
             type="warning"
             size="medium"
             @click.stop="toggleActivate(scope.row)"
@@ -131,7 +132,8 @@
           </el-tag>
           <el-tag
             v-if="scope.row.activate === 1"
-            class="o-tag u-pointer"
+            class="o-tag"
+            :class="{ 'u-pointer': canEdit }"
             size="medium"
             @click.stop="toggleActivate(scope.row)"
           >
@@ -139,7 +141,8 @@
           </el-tag>
           <el-tag
             v-if="scope.row.activate === 2"
-            class="o-tag u-pointer"
+            class="o-tag"
+            :class="{ 'u-pointer': canEdit }"
             :type="scope.row.onlineStatus === 1 ? 'success' : 'danger'"
             size="medium"
             @click.stop="toggleActivate(scope.row)"
@@ -155,36 +158,35 @@
       >
         <template v-slot="scope">
           <template v-if="!scope.row.empty">
-            <div
-              class="c-table__btn u-pointer"
-              @click.stop="toEditDevice(scope.row)"
-            >
-              查看
-            </div>
-            <template v-if="hasEditPermission">
-              <permission v-if="scope.row.isMaster">
-                <div
-                  class="c-table__btn u-pointer"
-                  @click.stop="toAddSubDevice(scope.row)"
-                >
-                  添加备份设备
-                </div>
-              </permission>
+            <permission :access="Access.VIEW_DEVICE">
+              <div
+                class="c-table__btn u-pointer"
+                @click.stop="toEditDevice(scope.row)"
+              >
+                查看
+              </div>
+            </permission>
+            <template v-if="canEdit">
+              <div
+                v-if="scope.row.isMaster"
+                class="c-table__btn u-pointer"
+                @click.stop="toAddSubDevice(scope.row)"
+              >
+                添加备份设备
+              </div>
               <div
-                v-if="__PLACEHOLDER__ && scope.row.isMaster && scope.row.activate > 1"
+                v-if="__PLACEHOLDER__ && scope.row.isMaster && scope.row.activate === 2"
                 class="c-table__btn u-pointer"
                 @click.stop="setDefaultProgram(scope.row)"
               >
                 默认节目
               </div>
-              <permission>
-                <div
-                  class="c-table__btn u-pointer"
-                  @click.stop="toDelDevice(scope.row)"
-                >
-                  删除
-                </div>
-              </permission>
+              <div
+                class="c-table__btn u-pointer"
+                @click.stop="toDelDevice(scope.row)"
+              >
+                删除
+              </div>
             </template>
           </template>
         </template>
@@ -284,7 +286,6 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
 import {
   getDevices,
   addDevice,
@@ -331,9 +332,11 @@ export default {
     }
   },
   computed: {
-    ...mapGetters(['isAdmin']),
+    canEdit () {
+      return this.accessSet.has(this.Access.MANAGE_DEVICES)
+    },
     operationColumnWidth () {
-      return this.isAdmin ? this.__PLACEHOLDER__ ? 340 : 260 : 200
+      return this.canEdit ? this.__PLACEHOLDER__ ? 340 : 260 : 120
     },
     type () {
       return this.isSub ? '备份设备' : '设备'
@@ -555,7 +558,7 @@ export default {
       }
     },
     toggleActivate (item) {
-      if (!this.hasEditPermission) {
+      if (!this.canEdit) {
         return
       }
       (item.activate ? deactivateDevice : activateDevice)(item).then(() => {

+ 2 - 28
src/views/profile/index.vue

@@ -24,10 +24,6 @@
         >
       </div>
       <div class="u-bold">{{ name }}</div>
-      <div class="u-relative">
-        <i class="c-profile__icon el-icon-user" />
-        {{ roleTip }}
-      </div>
     </div>
     <div class="l-flex__fill u-overflow-y--auto">
       <el-result
@@ -112,7 +108,6 @@ import {
   updateUser,
   getTicket
 } from '@/api/user'
-import { Role } from '@/constant'
 
 export default {
   name: 'Profile',
@@ -131,28 +126,13 @@ export default {
     }
   },
   computed: {
-    ...mapState('user', [
-      'name',
-      'roles'
-    ]),
+    ...mapState('user', ['name']),
     avatarStyle () {
       const avatar = this.avatar
       return avatar ? {
         backgroundImage: `url("${avatar}")`
       } : null
     },
-    roleTip () {
-      if (this.roles.has(Role.ADMIN)) {
-        return '超级管理员'
-      }
-      if (this.roles.has(Role.SUPERVISOR)) {
-        return '主管'
-      }
-      if (this.roles.has(Role.STAFF)) {
-        return '员工'
-      }
-      return '游客'
-    },
     tip () {
       return this.wechat ? '解除绑定' : '绑定微信'
     },
@@ -377,17 +357,11 @@ export default {
 
   &__header {
     justify-content: space-between;
-    height: 176px;
+    height: 140px;
     color: #fff;
     line-height: 1;
     background-color: $blue;
   }
-
-  &__icon {
-    position: absolute;
-    top: 50%;
-    transform: translate(calc(-100% - 16px), -50%);
-  }
 }
 
 .o-avatar {

+ 31 - 26
src/views/schedule/index.vue

@@ -26,7 +26,7 @@
       <template #header>
         <div class="l-flex__auto c-sibling-item">
           <button
-            v-if="hasEditPermission && isNormal"
+            v-if="canEdit && isNormal"
             class="o-button"
             @click="toAdd"
           >
@@ -63,7 +63,7 @@
           />
         </el-select>
         <el-select
-          v-if="hasEditPermission"
+          v-if="canEdit"
           v-model="currObj.params.status"
           class="l-flex__none c-sibling-item o-select"
           placeholder="请选择状态"
@@ -150,31 +150,33 @@
         width="180"
       >
         <template v-slot="scope">
-          <template v-if="scope.row.status === 0">
-            <div
-              class="c-table__btn u-pointer"
-              @click="toDesign(scope.row.id)"
+          <template v-if="canEdit">
+            <template v-if="scope.row.status === 0">
+              <div
+                class="c-table__btn u-pointer"
+                @click="toDesign(scope.row.id)"
+              >
+                编辑
+              </div>
+              <div
+                class="c-table__btn u-pointer"
+                @click="toSubmit(scope.row)"
+              >
+                提交
+              </div>
+            </template>
+            <permission
+              :skip="scope.row.status === 0"
+              :access="Access.DELETE_FORCE"
             >
-              编辑
-            </div>
-            <div
-              class="c-table__btn u-pointer"
-              @click="toSubmit(scope.row)"
-            >
-              提交
-            </div>
+              <div
+                class="c-table__btn u-pointer"
+                @click="toDel(scope.row)"
+              >
+                删除
+              </div>
+            </permission>
           </template>
-          <permission
-            :skip="scope.row.status === 0"
-            :access="Access.DELETE_FORCE"
-          >
-            <div
-              class="c-table__btn u-pointer"
-              @click="toDel(scope.row)"
-            >
-              删除
-            </div>
-          </permission>
           <div
             v-if="scope.row.status !== 0"
             class="c-table__btn u-pointer"
@@ -302,7 +304,7 @@ export default {
       active: 'normal',
       normal: createListOptions({
         type: void 0,
-        status: this.hasEditPermission ? void 0 : State.RESOLVED,
+        status: this.accessSet.has(this.Access.MANAGE_CALENDAR) ? void 0 : State.RESOLVED,
         resolutionRatio: void 0,
         name: ''
       }),
@@ -316,6 +318,9 @@ export default {
     }
   },
   computed: {
+    canEdit () {
+      return this.accessSet.has(this.Access.MANAGE_CALENDAR)
+    },
     currObj () {
       return this[this.active]
     },