提交 e48a8334 authored 作者: pengxiaohui's avatar pengxiaohui

新增权限模块

上级 5953ff6c
...@@ -7,7 +7,7 @@ export function operateLog(params) { ...@@ -7,7 +7,7 @@ export function operateLog(params) {
const data = { const data = {
detail: logMsg detail: logMsg
} }
httpRequest.post('/api/live/admin/v2/system/log/write', data).then(res => { httpRequest.post('/api/live/admin/v3/system/log/write', data).then(res => {
}).catch(() => {}) }).catch(() => {})
} }
/** /**
...@@ -26,7 +26,7 @@ export function createAccount(data) { ...@@ -26,7 +26,7 @@ export function createAccount(data) {
* 获取腾讯用户列表(分页) * 获取腾讯用户列表(分页)
*/ */
export function getAccountList(params) { export function getAccountList(params) {
return httpRequest.get('/api/live/admin/v2/tencent/users', { params }) return httpRequest.get('/api/live/admin/v3/tencent/users', { params })
} }
/** /**
* 获取腾讯用户列表(不分页) * 获取腾讯用户列表(不分页)
......
...@@ -18,13 +18,8 @@ export function searchUserList(params) { ...@@ -18,13 +18,8 @@ export function searchUserList(params) {
return httpRequest.get('/api/live/common/v1/sso-user/search', { params }) return httpRequest.get('/api/live/common/v1/sso-user/search', { params })
} }
/* ----------------------------系统用户管理---------------------------- */ /* ----------------------------系统角色管理---------------------------- */
/**
* 获取当前用户的角色
*/
export function getUserRoles() {
return httpRequest.get('/api/live/admin/v2/user/roles')
}
/** /**
* 创建角色 * 创建角色
*/ */
...@@ -44,7 +39,7 @@ export function getRoleDetails(params) { ...@@ -44,7 +39,7 @@ export function getRoleDetails(params) {
return httpRequest.get(`/api/live/admin/v2/role/${params.role_id}/detail`, { params }) return httpRequest.get(`/api/live/admin/v2/role/${params.role_id}/detail`, { params })
} }
/** /**
* 删除 * 删除角色
*/ */
export function deleteRole(id) { export function deleteRole(id) {
return httpRequest.delete(`/api/live/admin/v2/role/${id}/delete`) return httpRequest.delete(`/api/live/admin/v2/role/${id}/delete`)
...@@ -59,25 +54,61 @@ export function getRoles(params) { ...@@ -59,25 +54,61 @@ export function getRoles(params) {
* 分配角色给用户 * 分配角色给用户
*/ */
export function roleToUser(data) { export function roleToUser(data) {
return httpRequest.post('/api/live/admin/v2/assign/roles-to-user', data) return httpRequest.post('/api/live/admin/v3/system/assign/roles-to-user', data)
} }
/** /**
* 获取角色下的用户列表 * 获取角色下的用户列表
*/ */
export function getUserListUnderRole(params) { export function getUserListUnderRole(params) {
return httpRequest.get(`/api/live/admin/v2/role/${params.role_id}/users`, { params }) return httpRequest.get(`/api/live/admin/v3/system/assign/role/${params.role_id}/users`, { params })
} }
/** /**
* 从角色中移除用户 * 从角色中移除用户
*/ */
export function romoveUserUnderRole(data) { export function removeUserUnderRole(data) {
return httpRequest.post('/api/live/admin/v2/assign/remove-user-form-role', data) return httpRequest.post('/api/live/admin/v3/system/assign/remove-user-form-role', data)
}
/**
* 获取角色的权限
*/
export function getRolePermissions(id) {
return httpRequest.get(`/api/live/admin/v3/system/assign/role/${id}/permissions`)
}
/**
* 分配权限给角色
*/
export function setRolePermissions(data) {
return httpRequest.post('/api/live/admin/v3/system/assign/permission-to-role', data)
}
/**
* 创建权限
*/
export function createPermission(data) {
return httpRequest.post('/api/live/admin/v3/system/permission', data)
} }
/** /**
* 获取权限列表 * 更新权限
*/
export function updatePermission(id, data) {
return httpRequest.put(`/api/live/admin/v3/system/permission/${id}/update`, data)
}
/**
* 删除权限
*/
export function deletePermission(data) {
return httpRequest.post('/api/live/admin/v3/system/permission/batch-delete', data)
}
/**
* 获取权限列表(分页)
*/ */
export function getPermissions(params) { export function getPermissions(params) {
return httpRequest.get('/api/live/admin/v2/permissions', { params }) return httpRequest.get('/api/live/admin/v3/system/permissions', { params })
}
/**
* 获取权限列表(全部)
*/
export function getAllPermissions() {
return httpRequest.get('/api/live/admin/v3/system/permissions/tree')
} }
/** /**
* 分配权限给角色 * 分配权限给角色
...@@ -105,3 +136,15 @@ export function deleteAccount(data) { ...@@ -105,3 +136,15 @@ export function deleteAccount(data) {
export function getAccountList(params) { export function getAccountList(params) {
return httpRequest.get('/api/live/admin/v2/users', { params }) return httpRequest.get('/api/live/admin/v2/users', { params })
} }
/**
* 获取当前用户的角色
*/
export function getUserRoles() {
return httpRequest.get('/api/live/admin/v2/user/roles')
}
/**
* 获取当前用户的权限
*/
export function getUserPermissions() {
return httpRequest.get('/api/live/admin/v3/system/user/permissions')
}
...@@ -129,6 +129,12 @@ export default { ...@@ -129,6 +129,12 @@ export default {
}, },
components: { ScheduleItem }, components: { ScheduleItem },
computed: { computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('f_tencentmeeting_create')
},
minWidth() { minWidth() {
return this.options.minWidth || '200px' return this.options.minWidth || '200px'
}, },
...@@ -281,7 +287,10 @@ export default { ...@@ -281,7 +287,10 @@ export default {
}, 100) }, 100)
}, },
newScheduleHandle(val) { newScheduleHandle(val) {
// console.log(val) if (!this.hasCreate) {
this.$message.error('你没有权限创建会议')
return
}
this.newScheduleData = Object.assign(val, { date: this.date }) this.newScheduleData = Object.assign(val, { date: this.date })
this.newPopoverBtnStyle.left = val.x this.newPopoverBtnStyle.left = val.x
this.newPopoverBtnStyle.top = val.y this.newPopoverBtnStyle.top = val.y
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
<el-button type="text" @click="handleExport" :disabled="exportLoading">导出excel</el-button><i class="el-icon-loading" v-show="exportLoading"></i> <el-button type="text" @click="handleExport" :disabled="exportLoading">导出excel</el-button><i class="el-icon-loading" v-show="exportLoading"></i>
</el-form-item> </el-form-item>
<el-form-item label="回放:" v-if="rowData.status === 2 && operatable"> <el-form-item label="回放:" v-if="rowData.status === 2 && operatable">
<el-button type="text" v-if="hasRecord" @click="handleDownload">下载</el-button> <el-button type="text" v-if="hasExport && hasRecord" @click="handleDownload">下载</el-button>
<el-button type="text" v-else disabled>暂无回放</el-button> <el-button type="text" v-else disabled>暂无回放</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
...@@ -49,6 +49,12 @@ export default { ...@@ -49,6 +49,12 @@ export default {
} }
}, },
computed: { computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasExport() {
return this.permissions.includes('f_tencentmeeting_export_participants')
},
hasRecord() { hasRecord() {
if (this.rowData.record_file_ids && Array.isArray(this.rowData.record_file_ids) && this.rowData.record_file_ids.length > 0) { if (this.rowData.record_file_ids && Array.isArray(this.rowData.record_file_ids) && this.rowData.record_file_ids.length > 0) {
return true return true
......
<template> <template>
<div class="btns"> <div class="btns">
<template v-if="status === 1"> <template v-if="status === 1">
<el-button type="text" size="small" v-if="operatable" @click="handleJoin">进入会议</el-button> <el-button type="text" size="small" v-if="hasJoin" @click="handleJoin">进入会议</el-button>
<el-button type="text" size="small" v-if="operatable && hasLive" @click="handleLive">观看直播</el-button> <el-button type="text" size="small" v-if="operatable && hasLive" @click="handleLive">观看直播</el-button>
<el-button type="text" size="small" v-if="operatable" @click="handleCopy">复制邀请</el-button> <el-button type="text" size="small" v-if="hasCopy" @click="handleCopy">复制邀请</el-button>
<el-button type="text" size="small" v-if="operatable" @click="handleStop">终止</el-button> <el-button type="text" size="small" v-if="hasStop" @click="handleStop">终止</el-button>
<el-button type="text" size="small" v-if="!operatable" @click="handleDetails">查看</el-button> <el-button type="text" size="small" v-else @click="handleDetails">查看</el-button>
</template> </template>
<template v-if="status === 0"> <template v-if="status === 0">
<el-button type="text" size="small" v-if="operatable" @click="handleJoin">进入会议</el-button> <el-button type="text" size="small" v-if="hasJoin" @click="handleJoin">进入会议</el-button>
<el-button type="text" size="small" v-if="operatable" @click="handleCopy">复制邀请</el-button> <el-button type="text" size="small" v-if="hasCopy" @click="handleCopy">复制邀请</el-button>
<el-button type="text" size="small" v-if="operatable" @click="handleUpdate">更新</el-button> <el-button type="text" size="small" v-if="hasUpdate" @click="handleUpdate">更新</el-button>
<!-- <el-dropdown v-if="operatable && isCycle"> <el-button type="text" size="small" v-if="hasCancel" @click="handleCancel">取消</el-button>
<span class="dropdown-link"> <el-button type="text" size="small" v-else @click="handleDetails">查看</el-button>
修改<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click="handleUpdate">修改本次会议</el-dropdown-item>
<el-dropdown-item @click="handleUpdate">修改周期会议</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown> -->
<el-button type="text" size="small" v-if="operatable" @click="handleCancel">取消</el-button>
<el-button type="text" size="small" v-if="!operatable" @click="handleDetails">查看</el-button>
</template> </template>
<template v-if="status === 2"> <template v-if="status === 2">
<el-button type="text" size="small" v-if="operatable && isFuture" @click="handleJoin">进入会议</el-button> <el-button type="text" size="small" v-if="hasJoin && isFuture" @click="handleJoin">进入会议</el-button>
<el-button type="text" size="small" v-if="operatable && isFuture" @click="handleUpdate">更新</el-button> <el-button type="text" size="small" v-if="hasUpdate && isFuture" @click="handleUpdate">更新</el-button>
<el-button type="text" size="small" v-if="operatable" @click="handleRecord">下载回放</el-button> <el-button type="text" size="small" v-if="hasDownload" @click="handleRecord">下载回放</el-button>
<el-button type="text" size="small" v-if="hasDelete" @click="handleDelete">删除</el-button>
<el-button type="text" size="small" @click="handleDetails">查看</el-button> <el-button type="text" size="small" @click="handleDetails">查看</el-button>
<el-button type="text" size="small" v-if="operatable" @click="handleDelete">删除</el-button>
</template> </template>
<template v-if="status === 3"> <template v-if="status === 3">
<el-button type="text" size="small" @click="handleDetails">查看</el-button> <el-button type="text" size="small" @click="handleDetails">查看</el-button>
...@@ -67,6 +58,30 @@ export default { ...@@ -67,6 +58,30 @@ export default {
}, },
computed: { computed: {
...mapGetters(['isSuperAdmin', 'user']), ...mapGetters(['isSuperAdmin', 'user']),
permissions() {
return this.$store.state.user.permissions || []
},
hasUpdate() {
return this.permissions.includes('f_tencentmeeting_update')
},
hasStop() {
return this.permissions.includes('f_tencentmeeting_dimiss')
},
hasCancel() {
return this.permissions.includes('f_tencentmeeting_cancel')
},
hasCopy() {
return this.permissions.includes('f_tencentmeeting_copy')
},
hasJoin() {
return this.permissions.includes('f_tencentmeeting_join')
},
hasDownload() {
return this.permissions.includes('f_tencentmeeting_records_address')
},
hasDelete() {
return this.permissions.includes('f_tencentmeeting_delete')
},
domicTitle() { domicTitle() {
let title = '' let title = ''
switch (this.dialogType) { switch (this.dialogType) {
......
<template>
<div class="table-list">
<div class="table-list-hd">
<!-- 筛选 -->
<div class="table-list-filter" v-if="filters.length">
<el-form :inline="true" :model="params" size="mini" ref="filterForm">
<template v-for="item in filters">
<el-form-item :prop="item.prop" :key="item.prop">
<slot :name="item.slots" v-bind="params" v-if="item.slots"></slot>
<template v-else>
<!-- input -->
<el-input v-model="params[item.prop]" v-bind="item" clearable v-if="item.type === 'input'" style="width:200px;"/>
<!-- select -->
<el-select
v-model="params[item.prop]"
clearable
v-bind="item"
v-if="item.type === 'select'"
@change="search"
style="width:200px;"
>
<template v-for="option in item.options">
<el-option
:label="option[item.labelKey] || option[item.alternateLabelKey] || option.label"
:value="option[item.valueKey] || option.value"
:key="option.value"
>
</el-option>
</template>
</el-select>
</template>
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="search">搜索</el-button>
<el-button icon="el-icon-refresh-left" @click="reset">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="table-list-hd-aside"><slot name="header-aside" /></div>
</div>
<slot></slot>
<div class="table-list-bd">
<el-table :data="dataList" size="mini" v-loading="loading" v-bind="$attrs" v-on="$listeners" height="100%">
<template v-for="item in columns">
<el-table-column v-bind="item" :key="item.prop" v-if="visible(item)">
<template v-slot:default="scope" v-if="item.slots || item.computed">
<slot :name="item.slots" v-bind="scope" v-if="item.slots"></slot>
<div v-html="item.computed(scope)" v-if="item.computed"></div>
</template>
</el-table-column>
</template>
</el-table>
</div>
<div class="table-list-ft">
<div>
<slot name="footer"></slot>
</div>
<el-pagination
class="table-list-pagination"
layout="total, prev, pager, next, sizes, jumper"
:current-page.sync="page.currentPage"
:page-sizes="[10, 20, 30, 50, 100]"
:page-size="page.size"
:total="page.total"
@size-change="pageSizeChange"
@current-change="fetchList"
v-if="hasPagination"
>
</el-pagination>
</div>
</div>
</template>
<script>
export default {
name: 'TableList',
props: {
// 接口请求
remote: { type: Object, default: () => ({}) },
// 筛选
filters: { type: Array, default: () => [] },
// 列表项
columns: { type: Array, default: () => [] },
// 列表数据
data: { type: Array, default: () => [] },
// 是否含有翻页
hasPagination: { type: Boolean, default: true },
// 每页多少条数据
limit: { type: Number, default: 20 }
},
data() {
return {
loading: false,
params: this.remote.params || {},
dataList: this.data,
page: { total: 0, size: this.limit, currentPage: 1 }
}
},
watch: {
'remote.params': {
immediate: true,
handler(data) {
this.params = data || {}
}
},
data: {
immediate: true,
handler(data) {
this.dataList = data
}
}
},
methods: {
fetchList() {
/**
* @param function httpRequest api接口
* @param function beforeRequest 接口请求之前
* @param function callback 接口请求成功回调
*/
const { httpRequest, beforeRequest, callback } = this.remote
if (!httpRequest) {
return
}
// 参数设置
let params = Object.assign({}, this.params)
// 翻页参数设置
if (this.hasPagination) {
params.page = this.page.currentPage
params.limit = this.page.size
}
// 接口请求之前
if (beforeRequest) {
params = beforeRequest(params)
}
for (const key in params) {
if (params[key] === '' || params[key] === undefined || params[key] === undefined) {
delete params[key]
}
}
this.loading = true
httpRequest(params)
.then(res => {
const { data = [], total = 0 } = res.data || {}
this.page.total = total
this.dataList = callback ? callback(data) : data
})
.finally(() => {
this.loading = false
})
},
// 搜索
search() {
this.page.currentPage = 1
this.fetchList()
},
// 重置
reset() {
// 清空筛选条件
this.$refs.filterForm && this.$refs.filterForm.resetFields()
// 初始化页码
this.page.currentPage = 1
// 刷新列表
this.fetchList()
},
// 刷新
refetch(isForce) {
isForce ? this.reset() : this.fetchList()
},
// 页数改变
pageSizeChange(value) {
this.page.currentPage = 1
this.page.size = value
this.fetchList()
},
visible(item) {
return Object.prototype.hasOwnProperty.call(item, 'visible') ? item.visible : true
}
},
beforeMount() {
this.fetchList()
}
}
</script>
<style lang="scss">
.table-list {
padding: 10px;
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.table-list-hd {
display: flex;
}
.table-list-filter {
flex: 1;
}
.table-list-bd {
flex: 1;
}
.table-list-bd .el-table th:first-child .cell{
padding-left:14px;
}
.table-list-ft {
display: flex;
align-items: center;
justify-content: space-between;
}
.table-list-pagination {
padding: 10px 0;
text-align: right;
}
</style>
<template> <template>
<el-menu-item v-if="hasChild(navItem)" :index="navItem.path"> <div v-if="!navItem.hidden">
<i v-if="navItem.icon" :class="navItem.icon"></i> <el-menu-item v-if="!hasChild(navItem)" :index="fullPath">
<span slot="title">{{navItem.title}}</span> <i :class="navItem.meta.icon"></i>
</el-menu-item> <span slot="title">{{navItem.meta.title}}</span>
<el-submenu v-else ref="subMenu" :index="navItem.path"> </el-menu-item>
<template slot="title"> <template v-else>
<i v-if="navItem.icon" :class="navItem.icon"></i> <el-menu-item v-if="hasOneShowingChild(navItem)" :index="fullPath">
<span>{{navItem.title}}</span> <i :class="navItem.children[0].meta.icon"></i>
<span slot="title">{{navItem.children[0].meta.title}}</span>
</el-menu-item>
<el-submenu v-else ref="subMenu" :index="fullPath">
<template slot="title">
<i :class="navItem.meta.icon"></i>
<span>{{navItem.meta.title}}</span>
</template>
<menu-item
v-for="child in navItem.children"
:key="child.path"
:navItem="child"
:parent-path="fullPath"
class="nest-menu"
/>
</el-submenu>
</template> </template>
<menu-item </div>
v-for="child in navItem.children"
:key="child.path"
:navItem="child"
class="nest-menu"
/>
</el-submenu>
</template> </template>
<script> <script>
import path from 'path'
/**
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
export default { export default {
name: 'MenuItem', name: 'MenuItem',
props: { props: {
navItem: { navItem: {
type: Object, type: Object,
required: true required: true
},
parentPath: {
type: String,
default: ''
} }
}, },
data() { data() {
return {} return {}
}, },
computed: {
routePath() {
return this.navItem.path
},
fullPath() {
if (isExternal(this.routePath)) {
return this.routePath
}
if (isExternal(this.parentPath)) {
return this.parentPath
}
return path.resolve(this.parentPath, this.routePath)
}
},
methods: { methods: {
hasOneShowingChild(item) {
return item.meta.onlyShowOneChild && item.children && item.children.length === 1
},
hasChild (item) { hasChild (item) {
return !item.children || (item.children && item.children.length === 0) return item.children && item.children.length > 0
} }
} }
} }
</script> </script>
\ No newline at end of file <style scoped>
::v-deep.el-menu-item i{
display:inline-block;
width:24px;
}
</style>
\ No newline at end of file
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
:collapse="isCollapse" :collapse="isCollapse"
@select="handlleSelect" @select="handlleSelect"
> >
<menu-item v-for="item in menu" :key="item.path" :navItem="item" /> <menu-item v-for="item in permission_routes" :key="item.path" :navItem="item" :parent-path="item.path"/>
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</div> </div>
...@@ -28,85 +28,6 @@ ...@@ -28,85 +28,6 @@
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import MenuItem from './MenuItem' import MenuItem from './MenuItem'
const defaultMenu = [
{
title: '直播日历',
icon: 'el-icon-date',
path: '/calendar',
meta: {}
},
{
title: '系统管理',
icon: 'el-icon-setting',
path: '/system',
meta: {
roles: ['administrator']
},
children: [
{
title: '角色管理',
icon: 'el-icon-s-check',
path: '/system/role'
},
{
title: '用户管理',
icon: 'el-icon-key',
path: '/system/account'
}
]
},
{
title: '腾讯会议设置',
icon: 'el-icon-message',
path: '/tencent',
meta: {
roles: ['administrator']
},
children: [
{
title: '腾讯账号管理',
icon: '',
path: '/tencent/account'
}
]
},
{
title: '个人设置',
icon: 'el-icon-user',
path: '/my'
}
]
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
export default { export default {
data() { data() {
return { return {
...@@ -115,7 +36,7 @@ export default { ...@@ -115,7 +36,7 @@ export default {
} }
}, },
computed: { computed: {
...mapGetters(['sidebar', 'roles']), ...mapGetters(['sidebar', 'permission_routes']),
isCollapse() { isCollapse() {
return !this.sidebar.opened return !this.sidebar.opened
} }
...@@ -128,20 +49,11 @@ export default { ...@@ -128,20 +49,11 @@ export default {
else this.defaultActive = '' else this.defaultActive = ''
}, },
immediate: true immediate: true
},
roles: {
handler: function(nv) {
let roleList
if (Array.isArray(nv)) {
roleList = nv.map(item => item.name)
}
const menus = filterAsyncRoutes(defaultMenu, roleList)
this.menu = menus
},
immediate: true,
deep: true
} }
}, },
created() {
console.log(this.permission_routes)
},
methods: { methods: {
handlleSelect(path) { handlleSelect(path) {
this.$router.push(path) this.$router.push(path)
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</div> </div>
<el-button type="primary" icon="el-icon-plus" size="mini" style="margin:12px 20px 0 0;float:right;" @click="toMeeting">创建会议</el-button> <el-button v-if="hasCreate" type="primary" icon="el-icon-plus" size="mini" style="margin:12px 20px 0 0;float:right;" @click="toMeeting">创建会议</el-button>
</div> </div>
</template> </template>
<script> <script>
...@@ -38,6 +38,12 @@ export default { ...@@ -38,6 +38,12 @@ export default {
}, },
computed: { computed: {
...mapGetters(['sidebar', 'user']), ...mapGetters(['sidebar', 'user']),
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('f_tencentmeeting_create')
},
avatar() { avatar() {
return this.user.avatar || defaultAvatar return this.user.avatar || defaultAvatar
} }
......
...@@ -13,7 +13,7 @@ Vue.use(ElementUI) ...@@ -13,7 +13,7 @@ Vue.use(ElementUI)
/* 导航守卫 */ /* 导航守卫 */
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
before.update(to, from, next) before.update(to, from, next)
next() // next()
}) })
new Vue({ new Vue({
......
...@@ -27,17 +27,9 @@ export default ({ ...@@ -27,17 +27,9 @@ export default ({
} }
}, },
computed: { computed: {
...mapGetters(['user', 'roles']), ...mapGetters(['user', 'isSuperAdmin']),
isReviewRecurring() { isReviewRecurring() {
let isSuperAdmin = false return this.reviewData && this.reviewData.meeting_type === 1 && !this.isSuperAdmin
if (this.roles) {
this.roles.forEach(role => {
if (role.name === 'administrator') {
isSuperAdmin = true
}
})
}
return this.reviewData && this.reviewData.meeting_type === 1 && !isSuperAdmin
} }
}, },
components: { MeetingForm, MeetingSuccessDialog }, components: { MeetingForm, MeetingSuccessDialog },
......
<template> <template>
<div class="account"> <div class="account">
<h5>用户管理 <el-button style="float:right;margin:12px 30px 0 0" size="mini" type="primary" plain @click="handleAdd" v-if="isSuperAdmin">新增用户</el-button></h5> <h5>用户管理 <el-button style="float:right;margin:12px 30px 0 0" size="mini" type="primary" plain @click="handleAdd" v-if="hasCreate">新增用户</el-button></h5>
<div class="inner"> <div class="inner">
<div class="search-filter"> <div class="search-filter">
<div class="filter-item"> <div class="filter-item">
...@@ -22,12 +22,12 @@ ...@@ -22,12 +22,12 @@
<el-button icon="el-icon-refresh-left" size="mini" @click="reset">重置</el-button> <el-button icon="el-icon-refresh-left" size="mini" @click="reset">重置</el-button>
</div> </div>
</div> </div>
<div class="delete-bar"> <div class="delete-bar" v-if="hasDelete">
<i class="el-icon-warning"></i> 已选择 <span class="num">{{selection.length || 0}}</span><el-button type="text" size="m" :disabled="!selection.length" @click="handleDelete('')" style="margin-left:15px;">删除</el-button> <i class="el-icon-warning"></i> 已选择 <span class="num">{{selection.length || 0}}</span><el-button type="text" size="m" :disabled="!selection.length" @click="handleDelete('')" style="margin-left:15px;">删除</el-button>
</div> </div>
<div class="table-container"> <div class="table-container">
<el-table :data="listData" style="width: 100%;" height="100%" v-loading="loading" @selection-change="handleSelectionChange"> <el-table :data="listData" style="width: 100%;" height="100%" v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="45" /> <el-table-column v-if="hasDelete" type="selection" width="45" />
<el-table-column prop="sso_user.nickname" label="姓名" min-width="100" /> <el-table-column prop="sso_user.nickname" label="姓名" min-width="100" />
<el-table-column label="手机号码" min-width="100"> <el-table-column label="手机号码" min-width="100">
<template slot-scope="{ row }"> <template slot-scope="{ row }">
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<span>{{row.last_login_time || '-'}}</span> <span>{{row.last_login_time || '-'}}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" min-width="80" v-if="isSuperAdmin"> <el-table-column label="操作" min-width="80" v-if="false">
<template slot-scope="scope"> <template slot-scope="scope">
<!-- <el-button type="text" size="small" @click="handleDelete(scope.row.userid)">删除</el-button> --> <!-- <el-button type="text" size="small" @click="handleDelete(scope.row.userid)">删除</el-button> -->
<el-button type="text" size="small" @click="handleEdit(scope.row)">配置权限</el-button> <el-button type="text" size="small" @click="handleEdit(scope.row)">配置权限</el-button>
...@@ -129,7 +129,16 @@ export default { ...@@ -129,7 +129,16 @@ export default {
} }
}, },
computed: { computed: {
...mapGetters(['isSuperAdmin', 'user']), ...mapGetters(['user']),
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('f_user_create')
},
hasDelete() {
return this.permissions.includes('f_user_batch_delete')
},
domicTitle() { domicTitle() {
let title = '新增用户' let title = '新增用户'
switch (this.dialogType) { switch (this.dialogType) {
......
<template>
<div class="permission">
<h5>用户管理
<el-button v-if="hasCreate" style="float:right;margin:12px 30px 0 0" size="mini" type="primary" plain @click="handleCreate">新建权限</el-button>
</h5>
<div class="inner">
<el-tree
:data="data"
node-key="id"
default-expand-all
:expand-on-click-node="false">
<div class="custom-tree-node" slot-scope="{ node, data }">
<p>{{ data.display_name }}</p>
<div>
<i v-if="hasCreate" class="el-icon-circle-plus-outline" title="新增子节点" @click="handleAdd(data)"></i>
<i v-if="hasDelete" class="el-icon-circle-close" title="删除此节点" @click="handleDelete(data.id)"></i>
<i v-if="hasUpdate" class="el-icon-edit" title="编辑此节点" @click="handleEdit(data)"></i>
</div>
</div>
</el-tree>
</div>
<el-dialog :title="domicTitle" :visible.sync="dialogVisible" width="420px" center @close="handleDialogClose">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="80px" class="demo-ruleForm">
<el-form-item label="唯一标识" prop="name">
<el-input v-model="form.name" size="small" placeholder="请输入唯一标识,只允许英文和_"></el-input>
</el-form-item>
<el-form-item label="权限名称" prop="display_name">
<el-input v-model="form.display_name" size="small" placeholder="请输入权限名称"></el-input>
</el-form-item>
<el-form-item label="权限描述" prop="description">
<el-input v-model="form.description" size="small" placeholder="请输入权限描述"></el-input>
</el-form-item>
<el-form-item label="权限类型" prop="type">
<el-select v-model="form.type" size="small" placeholder="请选择权限类型" style="width:100%;" :disabled="selectDisabled">
<el-option label="功能权限" :value="1"></el-option>
<el-option label="菜单权限" :value="2"></el-option>
<el-option label="数据权限" :value="3"></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleEnter" size="mini">确 定</el-button>
<el-button @click="handleDialogClose" size="mini">取 消</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { createPermission, updatePermission, getPermissions, deletePermission } from '@api/system'
const defaultForm = {
parent_id: 0,
name: '',
display_name: '',
effect_uri: '',
description: '',
order: 0,
type: ''
}
export default {
data() {
const validateName = (rule, value, callback) => {
if (value) {
if (!/^[a-zA-Z_]{1,}$/.test(value)) {
callback(new Error('手机号格式错误'));
} else {
callback()
}
}
}
return {
data: [],
form: Object.assign({}, defaultForm),
rules: {
name: [
{ required: true, message: '请输入唯一标识', trigger: 'blur' },
{ validator: validateName, trigger: 'blur' }
],
display_name: { required: true, message: '请输入权限名称', trigger: 'blur' },
description: { required: true, message: '请输入权限描述', trigger: 'blur' },
type: { required: true, message: '请输入权限类型', trigger: 'blur' }
},
dialogVisible: false,
dialogType: 'add',
selectDisabled: false
}
},
computed: {
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('f_permission_create')
},
hasUpdate() {
return this.permissions.includes('f_permission_update')
},
hasDelete() {
return this.permissions.includes('f_permission_batch_delete')
},
domicTitle() {
return '新建权限'
}
},
created() {
this.fetchGetPermission()
},
methods: {
handleCreate() {
this.dialogVisible = true
this.form.parent_id = 0
this.selectDisabled = false
this.dialogType = 'add'
},
handleAdd(item) {
this.dialogVisible = true
this.form.parent_id = item.id
this.form.type = item.type
this.selectDisabled = true
this.dialogType = 'add'
},
handleEdit(val) {
this.dialogVisible = true
this.form = Object.assign({}, val)
this.selectDisabled = true
this.dialogType = 'edit'
},
handleDelete(id) {
this.$confirm('确认执行此操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchDelete(id)
}).catch(() => {})
},
handleEnter() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
this.dialogType === 'add' ? this.fetchCreate() : this.fetchUpdate()
}
})
},
handleDialogClose() {
this.$refs.ruleForm.resetFields();
this.dialogVisible = false
this.form = Object.assign({}, defaultForm)
},
fetchGetPermission() {
const params = {
page: 1,
limit: 100
}
getPermissions(params).then(res => {
if (res.code === 0 && res.data && res.data.data) {
this.data = res.data.data
}
})
},
fetchCreate() {
createPermission(this.form).then(res => {
if (res.code === 0 && res.data && res.data.id) {
this.$message.success('创建权限成功')
this.fetchGetPermission()
this.handleDialogClose()
}
})
},
fetchUpdate() {
updatePermission(this.form.id, this.form).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('更新权限成功')
this.fetchGetPermission()
this.handleDialogClose()
} else {}
})
},
fetchDelete(id) {
const params = {
ids: [id]
}
deletePermission(params).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('删除权限成功')
this.fetchGetPermission()
}
})
}
}
}
</script>
<style scoped>
.permission{
height:100%;
}
h5{
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
color:#333;
font-weight: 500;
line-height:50px;
text-indent:20px;
}
.inner{
height: calc(100% - 50px - 15px);
background: #FFFFFF;
border-radius: 10px;
margin:0 16px;
box-sizing:border-box;
padding:14px 14px 6px;
display:flex;
flex-direction:column;
overflow-y:auto;
}
.custom-tree-node{
width: 260px;
display:flex;
}
.custom-tree-node>div{
margin-left:20px;
}
.custom-tree-node i{
font-size:19px;
visibility:hidden;
}
.custom-tree-node:hover i{
visibility:visible;
}
.custom-tree-node i:hover{
color:#409EFF;
}
</style>
\ No newline at end of file
...@@ -2,19 +2,20 @@ ...@@ -2,19 +2,20 @@
<div class="account"> <div class="account">
<h5> <h5>
角色管理 角色管理
<el-button style="float: right; margin: 12px 30px 0 0" size="mini" type="primary" plain @click="handleAdd">添加角色</el-button> <el-button v-if="hasCreate" style="float: right; margin: 12px 30px 0 0" size="mini" type="primary" plain @click="handleAdd">添加角色</el-button>
</h5> </h5>
<div class="inner"> <div class="inner">
<el-table :data="listData" style="width: 100%" height="calc(100% - 32px)" v-loading="loading"> <el-table :data="listData" style="width: 100%" height="calc(100% - 32px)" v-loading="loading">
<el-table-column prop="name" label="角色" min-width="140" v-if="!isSuperAdmin"></el-table-column> <el-table-column prop="name" label="角色" min-width="140"></el-table-column>
<el-table-column prop="display_name" label="角色名称" min-width="120"></el-table-column> <el-table-column prop="display_name" label="角色名称" min-width="120"></el-table-column>
<el-table-column prop="description" label="角色描述" min-width="120"></el-table-column> <el-table-column prop="description" label="角色描述" min-width="120"></el-table-column>
<el-table-column prop="updated_at" label="创建时间" min-width="120"></el-table-column> <el-table-column prop="updated_at" label="创建时间" min-width="120"></el-table-column>
<el-table-column label="操作" min-width="140"> <el-table-column label="操作" min-width="140">
<template slot-scope="scope"> <template slot-scope="scope">
<!-- <el-button type="text" size="small" @click="handlePermission(scope.row)">设置权限</el-button> --> <el-button v-if="hasPermission" type="text" size="small" @click="handlePermission(scope.row)">设置权限</el-button>
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button> <el-button v-if="hasRoleManage" type="text" size="small" @click="handleUsers(scope.row)">管理用户</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button> <el-button v-if="hasUpdate" type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button v-if="hasDelete" type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
...@@ -26,7 +27,7 @@ ...@@ -26,7 +27,7 @@
<el-input v-model="form.display_name" size="small"></el-input> <el-input v-model="form.display_name" size="small"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="角色key" prop="name"> <el-form-item label="角色key" prop="name">
<el-input v-model="form.name" size="small"></el-input> <el-input v-model="form.name" size="small" :disabled="keyDisabled"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="角色描述" prop="description"> <el-form-item label="角色描述" prop="description">
<el-input v-model="form.description" size="small"></el-input> <el-input v-model="form.description" size="small"></el-input>
...@@ -37,11 +38,32 @@ ...@@ -37,11 +38,32 @@
<el-button @click="handleDialogClose" size="mini">取 消</el-button> <el-button @click="handleDialogClose" size="mini">取 消</el-button>
</span> </span>
</el-dialog> </el-dialog>
<el-drawer :visible.sync="drawerVisible" size="600px" @close="drawerClose">
<div class="drawer-header" slot="title">
<span>设置权限</span>
<el-button style="float: right; margin-right: 20px;" size="mini" type="primary" plain @click="fetchPermUpdate">保存</el-button>
</div>
<el-tree
:data="permissions"
ref="tree"
show-checkbox
check-strictly
default-expand-all
node-key="id"
:default-expanded-keys="[]"
:default-checked-keys="defaultCheckedKeys"
:props="{
children: 'children',
label: 'display_name'
}"
@check-change="checkChange"
>
</el-tree>
</el-drawer>
</div> </div>
</template> </template>
<script> <script>
import { getRoles, createRole, operateLog, deleteRole, updateRole } from '@api/system' import { getRoles, createRole, operateLog, deleteRole, updateRole, getRolePermissions, getAllPermissions, setRolePermissions } from '@api/system'
import { mapGetters } from 'vuex'
const defaultForm = { const defaultForm = {
name: '', name: '',
display_name: '', display_name: '',
...@@ -58,22 +80,46 @@ export default { ...@@ -58,22 +80,46 @@ export default {
loading: false, loading: false,
dialogVisible: false, dialogVisible: false,
dialogType: 'add', dialogType: 'add',
keyDisabled: false,
form: Object.assign({}, defaultForm), form: Object.assign({}, defaultForm),
rules: { rules: {
name: { required: true, message: '请输入角色key', trigger: 'blur' }, name: { required: true, message: '请输入角色key', trigger: 'blur' },
display_name: { required: true, message: '请输入角色名称', trigger: 'blur' }, display_name: { required: true, message: '请输入角色名称', trigger: 'blur' },
description: { required: true, message: '请输入角色描述', trigger: 'blur' } description: { required: true, message: '请输入角色描述', trigger: 'blur' }
} },
selectedRoleId: '',
permissions: [],
drawerVisible: false,
defaultCheckedKeys: [],
curCheckedKeys: []
} }
}, },
computed: { computed: {
...mapGetters(['isSuperAdmin']), _permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this._permissions.includes('f_role_create')
},
hasUpdate() {
return this._permissions.includes('f_role_update')
},
hasDelete() {
return this._permissions.includes('f_role_batch_delete')
},
hasPermission() {
return this._permissions.includes('f_assign_permission_to_role')
},
hasRoleManage() {
return this._permissions.includes('f_assign_role_users')
},
domicTitle() { domicTitle() {
return this.dialogType === 'add' ? '新增角色' : '编辑角色' return this.dialogType === 'add' ? '新增角色' : '编辑角色'
} }
}, },
created() { created() {
this.fetchRoleList() this.fetchRoleList()
this.fetchPermissions()
}, },
methods: { methods: {
handleUsers(val) { handleUsers(val) {
...@@ -84,13 +130,19 @@ export default { ...@@ -84,13 +130,19 @@ export default {
this.dialogVisible = false this.dialogVisible = false
this.form = Object.assign({}, defaultForm) this.form = Object.assign({}, defaultForm)
}, },
handlePermission(val) { async handlePermission(data) {
console.log(val) this.selectedRoleId = data.id
const perms = await this.fetchRolePermissions(data.id)
console.log(perms)
const arr = perms.map(item => item.id)
this.defaultCheckedKeys = arr
this.drawerVisible = true
}, },
handleEdit(val) { handleEdit(val) {
this.form = Object.assign({}, val) this.form = Object.assign({}, val)
this.dialogVisible = true this.dialogVisible = true
this.dialogType = 'edit' this.dialogType = 'edit'
this.keyDisabled = true
}, },
handleDelete(val) { handleDelete(val) {
this.$confirm('删除此角色将导致相关联的用户不可用, 是否继续执行?', '提示', { this.$confirm('删除此角色将导致相关联的用户不可用, 是否继续执行?', '提示', {
...@@ -103,6 +155,7 @@ export default { ...@@ -103,6 +155,7 @@ export default {
handleAdd() { handleAdd() {
this.dialogVisible = true this.dialogVisible = true
this.dialogType = 'add' this.dialogType = 'add'
this.keyDisabled = false
}, },
handleEnter() { handleEnter() {
this.$refs.ruleForm.validate((valid) => { this.$refs.ruleForm.validate((valid) => {
...@@ -112,6 +165,14 @@ export default { ...@@ -112,6 +165,14 @@ export default {
} }
}) })
}, },
checkChange() {
this.curCheckedKeys = this.$refs.tree.getCheckedKeys()
},
drawerClose() {
this.drawerVisible = false
this.defaultCheckedKeys = []
this.$refs.tree.setCheckedKeys([])
},
pageChange() { pageChange() {
this.fetchRoleList() this.fetchRoleList()
}, },
...@@ -159,6 +220,40 @@ export default { ...@@ -159,6 +220,40 @@ export default {
this.$message.error(res.message || '删除角色失败') this.$message.error(res.message || '删除角色失败')
} }
}) })
},
fetchRolePermissions(id) {
return new Promise((resolve, reject) => {
getRolePermissions(id).then(res => {
if (res.code === 0) {
const { list = [] } = res.data
resolve(list)
} else {
this.$message.error(res.message || '获取角色权限失败')
reject(res)
}
}).catch((err) => reject(err))
})
},
fetchPermissions() {
getAllPermissions().then(res => {
if (res.code === 0) {
this.permissions = res.data
}
})
},
fetchPermUpdate() {
const params = {
role_id: this.selectedRoleId,
permission_ids: this.curCheckedKeys
}
setRolePermissions(params).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('设置权限成功')
this.drawerClose()
} else {
this.$message.error(res.message || '设置权限失败')
}
})
} }
} }
} }
...@@ -183,4 +278,12 @@ h5 { ...@@ -183,4 +278,12 @@ h5 {
box-sizing: border-box; box-sizing: border-box;
padding: 14px 14px 6px; padding: 14px 14px 6px;
} }
.drawer-header{
display:flex;
}
.drawer-header>span{
flex:1;
line-height:28px;
font-size:16px;
}
</style> </style>
\ No newline at end of file
<template>
<table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<template v-slot:filter-user>
<user-search v-model="tableOptions.remote.params.sso_id" :fetchApi="fetchApiSearch" style="width:260px;" :options="{ size: 'mini', clearable: true }"/>
</template>
<template #footer>
已选中 {{selection.length}}
<el-button size="mini" :disabled="!selection.length" @click="fetchRoleToUsers">添加</el-button>
</template>
</table-list>
</template>
<script>
import { getAccountList, roleToUser } from '@api/system'
import { searchUserList } from '@api/common'
import UserSearch from '@/components/UserSearch/index'
import TableList from '@/components/TableList'
export default {
props: {
id: {
type: String,
default: ''
}
},
components: { UserSearch, TableList },
data() {
return {
selection: [],
fetchApiSearch: searchUserList
}
},
computed: {
hasAdd() {
return true
},
roleId() {
return this.$route.query.id || ''
},
tableOptions() {
return {
remote: {
httpRequest: getAccountList,
params: { sso_id: '' }
},
filters: [
{
type: 'select',
placeholder: '输入邮箱/手机号搜索',
options: this.userList,
prop: 'sso_id',
labelKey: 'realname',
alternateLabelKey: 'nickname',
valueKey: 'id',
filterable: true,
remote: true,
'remote-method': this.fetchUserList,
slots: 'filter-user'
}
],
columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasAdd },
{ prop: 'sso_user.nickname', label: '用户昵称', minWidth: '160px', fixed: 'left' },
{ prop: 'sso_user.id', label: '用户ID', minWidth: '150px', fixed: 'left' },
{ prop: 'sso_user.mobile', label: '手机号', minWidth: '150px' },
{ prop: 'sso_user.email', label: '邮箱', minWidth: '150px', fixed: 'right' }
],
bodyHeight: 'calc(100% - 50px)'
}
}
},
created() {
console.log(this.id)
},
methods: {
handleSelectionChange(val) {
this.selection = val
},
fetchRoleToUsers() {
const params = {
role_id: this.id,
sso_ids: this.selection.map(item => item.user_id)
}
roleToUser(params).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('添加用户成功')
this.$emit('update')
}
})
}
}
}
</script>
\ No newline at end of file
...@@ -2,74 +2,92 @@ ...@@ -2,74 +2,92 @@
<div class="account"> <div class="account">
<h5> <h5>
角色管理 / 分配用户 角色管理 / 分配用户
<el-button style="float: right; margin: 12px 30px 0 0" size="mini" type="primary" plain @click="handleAdd" <el-button v-if="hasCreate" style="float: right; margin: 12px 30px 0 0" size="mini" type="primary" plain @click="drawerVisible = true">添加用户</el-button>
>角色下增加用户</el-button
>
</h5> </h5>
<div class="inner"> <div class="inner">
<el-table :data="listData" style="width: 100%" height="calc(100% - 32px)"> <table-list v-bind="tableOptions" ref="tabList" @selection-change="handleSelectionChange">
<!-- <el-table-column prop="name" label="角色" min-width="140"></el-table-column> --> <template v-slot:filter-user>
<el-table-column prop="user_info.nickname" label="用户昵称" min-width="120"></el-table-column> <user-search v-model="tableOptions.remote.params.sso_id" :fetchApi="fetchApiSearch" style="width:260px;" :options="{ size: 'mini', clearable: true }"/>
<el-table-column prop="user_info.id" label="用户ID" min-width="120"></el-table-column> </template>
<el-table-column prop="user_info.mobile" label="手机号" min-width="120"></el-table-column> <template #footer v-if="hasDelete">
<el-table-column prop="user_info.email" label="邮箱" min-width="120"></el-table-column> 已选中 {{selection.length}}
<el-table-column label="操作" min-width="140"> <el-button size="mini" :disabled="!selection.length" @click="handleDelete">删除</el-button>
<template slot-scope="scope"> </template>
<el-button type="text" size="small" @click="handleDelete(scope.row.userid)">从角色下删除</el-button> </table-list>
</template>
</el-table-column>
</el-table>
</div> </div>
<el-drawer title="添加用户" :visible.sync="drawerVisible" size="1000px">
<account-table :id="roleId" @update="handleUpdate" />
</el-drawer>
</div> </div>
</template> </template>
<script> <script>
import { getUserListUnderRole, removeUserUnderRole } from '@api/system'
import { searchUserList } from '@api/common'
import UserSearch from '@/components/UserSearch/index'
import TableList from '@/components/TableList'
import AccountTable from './AccountTable.vue'
export default { export default {
data() { data() {
return { return {
listData: [ ssoId: '',
{ fetchApiSearch: searchUserList,
created_at: '2021-04-01 10:45:38', listData: [],
updated_at: '2021-04-01 10:45:38', selection: [],
user_info: { drawerVisible: false
id: '6653195831513972736', }
username: 'ZJ6653195831513972736', },
email: 'wangyizheng@ezijing.com', components: { UserSearch, TableList, AccountTable },
mobile: '18435134258', computed: {
wechat_unionid: 'oJ6hPszybKN83GkWbvaY33q4oeIo', permissions() {
nickname: 'sdagads', return this.$store.state.user.permissions || []
country_code: '86', },
id_number: null, hasCreate() {
user_id: '6653195831513972736', return this.permissions.includes('f_assign_roles_to_user')
gender: '0', },
province: null, hasDelete() {
city: null, return this.permissions.includes('f_assign_remove_user_from_role')
county: null, },
company_name: null, roleId() {
position: null, return this.$route.query.id || ''
personal_signature: null, },
phone: null, tableOptions() {
qq: null, return {
wechat: null, remote: {
weibo: null, httpRequest: getUserListUnderRole,
tencent_weibo: null, params: { sso_id: '', role_id: this.roleId }
org: null, },
real_name: 'sdagads', filters: [
highest_degree: null, {
major: null, type: 'select',
birthday: null, placeholder: '输入邮箱/手机号搜索',
channel_code: null, options: this.userList,
created_time: '2020-04-07 15:44:57', prop: 'sso_id',
updated_time: '2020-08-26 17:12:10', labelKey: 'realname',
status: '0', alternateLabelKey: 'nickname',
avatar: null, valueKey: 'id',
realname: 'sdagads' filterable: true,
remote: true,
'remote-method': this.fetchUserList,
slots: 'filter-user'
} }
} ],
] columns: [
{ type: 'selection', minWidth: '50px', fixed: 'left', visible: this.hasDelete },
{ prop: 'user_info.nickname', label: '用户昵称', minWidth: '160px', fixed: 'left' },
{ prop: 'user_info.id', label: '用户ID', minWidth: '150px', fixed: 'left' },
{ prop: 'user_info.mobile', label: '手机号', minWidth: '150px' },
{ prop: 'user_info.email', label: '邮箱', minWidth: '150px', fixed: 'right' }
],
bodyHeight: 'calc(100% - 50px)'
}
} }
}, },
created() {
},
methods: { methods: {
handleAdd() {}, handleSelectionChange(val) {
this.selection = val
},
handleUsers(val) { handleUsers(val) {
console.log(val) console.log(val)
}, },
...@@ -79,8 +97,31 @@ export default { ...@@ -79,8 +97,31 @@ export default {
handleEdit(val) { handleEdit(val) {
console.log(val) console.log(val)
}, },
handleDelete(val) { handleDelete() {
console.log(val) this.$confirm('确定从角色下删除选中的用户?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.fetchRemove()
}).catch(() => {});
},
handleUpdate() {
this.drawerVisible = false
this.$refs.tabList.refetch()
},
fetchRemove() {
const params = {
role_id: this.roleId,
sso_ids: this.selection.map(item => {
if (item.user_info) return item.user_info.id
})
}
removeUserUnderRole(params).then(res => {
if (res.code === 0 && res.data && res.data.status) {
this.$message.success('删除用户成功')
this.$refs.tabList.refetch()
}
})
} }
} }
} }
...@@ -99,11 +140,16 @@ h5 { ...@@ -99,11 +140,16 @@ h5 {
} }
.inner { .inner {
height: calc(100% - 50px - 10px); height: calc(100% - 50px - 10px);
display: flex;
background: #ffffff; background: #ffffff;
border-radius: 10px; border-radius: 10px;
margin: 0 16px; margin: 0 16px;
box-sizing: border-box; box-sizing: border-box;
padding: 14px 14px 6px; padding: 14px 14px 0;
}
.table-container{
height:calc(100% - 105px);
}
::v-deep.el-drawer header{
margin-bottom:0;
} }
</style> </style>
\ No newline at end of file
<template> <template>
<div class="account"> <div class="account">
<h5>账号管理 <el-button style="float:right;margin:12px 30px 0 0" size="mini" type="primary" plain @click="handleAdd" v-if="isSuperAdmin">新增账号</el-button></h5> <h5>账号管理 <el-button style="float:right;margin:12px 30px 0 0" size="mini" type="primary" plain @click="handleAdd" v-if="hasCreate">新增账号</el-button></h5>
<div class="inner"> <div class="inner">
<div class="search-filter"> <div class="search-filter">
<div class="filter-item"> <div class="filter-item">
...@@ -25,12 +25,12 @@ ...@@ -25,12 +25,12 @@
<el-button type="text" size="mini" @click="isCollapse = !isCollapse">{{isCollapse ? '收起':'展开'}}</el-button> <el-button type="text" size="mini" @click="isCollapse = !isCollapse">{{isCollapse ? '收起':'展开'}}</el-button>
</div> </div>
</div> </div>
<div class="delete-bar"> <div class="delete-bar" v-if="hasDelete">
<i class="el-icon-warning"></i> 已选择 <span class="num">{{selection.length || 0}}</span><el-button type="text" size="m" :disabled="!selection.length" @click="handleDelete('')" style="margin-left:15px;">删除</el-button> <i class="el-icon-warning"></i> 已选择 <span class="num">{{selection.length || 0}}</span><el-button type="text" size="m" :disabled="!selection.length" @click="handleDelete('')" style="margin-left:15px;">删除</el-button>
</div> </div>
<div class="table-container"> <div class="table-container">
<el-table :data="listData" style="width: 100%;" height="100%" v-loading="loading" @selection-change="handleSelectionChange"> <el-table :data="listData" style="width: 100%;" height="100%" v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="45" /> <el-table-column type="selection" width="45" v-if="hasDelete" />
<el-table-column prop="username" label="姓名" min-width="120" /> <el-table-column prop="username" label="姓名" min-width="120" />
<el-table-column prop="phone" label="手机号码" min-width="80"/> <el-table-column prop="phone" label="手机号码" min-width="80"/>
<el-table-column prop="email" label="邮箱" min-width="140"/> <el-table-column prop="email" label="邮箱" min-width="140"/>
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
<span v-else>虚拟会议室</span> <span v-else>虚拟会议室</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" min-width="80" v-if="isSuperAdmin"> <el-table-column label="操作" min-width="80" v-if="hasUpdate">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button> <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<!-- <el-button type="text" size="small" @click="handleDelete(scope.row.userid)">删除</el-button> --> <!-- <el-button type="text" size="small" @click="handleDelete(scope.row.userid)">删除</el-button> -->
...@@ -146,7 +146,19 @@ export default { ...@@ -146,7 +146,19 @@ export default {
} }
}, },
computed: { computed: {
...mapGetters(['isSuperAdmin', 'user']), ...mapGetters(['user']),
permissions() {
return this.$store.state.user.permissions || []
},
hasCreate() {
return this.permissions.includes('f_tencentuser_create')
},
hasUpdate() {
return this.permissions.includes('f_tencentuser_update')
},
hasDelete() {
return this.permissions.includes('f_tencentuser_batch_delete')
},
domicTitle() { domicTitle() {
let title = '新增' let title = '新增'
switch (this.dialogType) { switch (this.dialogType) {
......
...@@ -4,53 +4,58 @@ export default [ ...@@ -4,53 +4,58 @@ export default [
path: '/', path: '/',
name: 'Layout', name: 'Layout',
component: Layout, component: Layout,
redirect: '/calendar' redirect: '/calendar',
hidden: true
}, },
{ {
path: '/meeting-create', path: '/meeting-create',
component: Layout, component: Layout,
hidden: true,
children: [ children: [
{ {
path: '', path: '',
component: () => import('@/pages/meeting/index.vue'), component: () => import('@/pages/meeting/index.vue'),
name: 'MeetingCreate', name: 'MeetingCreate',
meta: { title: '创建直播', affix: true } meta: { title: '创建直播', permission: 'f_tencentmeeting_create' }
} }
] ]
}, },
{ {
path: '/meeting-update', path: '/meeting-update',
component: Layout, component: Layout,
hidden: true,
children: [ children: [
{ {
path: '', path: '',
component: () => import('@/pages/meeting/index.vue'), component: () => import('@/pages/meeting/index.vue'),
name: 'MeetingUpdate', name: 'MeetingUpdate',
meta: { title: '更新直播', affix: true } meta: { title: '更新直播', permission: 'f_tencentmeeting_update' }
} }
] ]
}, },
{ {
path: '/meeting-account-create', path: '/meeting-account-create',
component: Layout, component: Layout,
hidden: true,
children: [ children: [
{ {
path: '', path: '',
component: () => import('@/pages/meeting/index.vue'), component: () => import('@/pages/meeting/index.vue'),
name: 'MeetingAccountCreate', name: 'MeetingAccountCreate',
meta: { title: '创建直播', affix: true } meta: { title: '创建直播', permission: 'f_tencentmeeting_create' }
} }
] ]
}, },
{ {
path: '/calendar', path: '/calendar',
component: Layout, component: Layout,
meta: { onlyShowOneChild: true, permission: 'menus_live_date_manage' },
children: [ children: [
{ {
path: '', path: '',
name: 'Calendar', name: 'Calendar',
component: () => import('@/pages/calendar/index.vue'), component: () => import('@/pages/calendar/index'),
meta: { title: '日历列表', icon: 'el-icon-date' } meta: { title: '直播日历', icon: 'el-icon-user' }
} }
] ]
}, },
...@@ -59,25 +64,32 @@ export default [ ...@@ -59,25 +64,32 @@ export default [
component: Layout, component: Layout,
redirect: '/system/role', redirect: '/system/role',
name: 'System', name: 'System',
meta: { title: '系统管理', icon: 'el-icon-setting' }, meta: { title: '系统管理', icon: 'el-icon-setting', permission: 'menus_system_manage' },
children: [ children: [
{ {
path: 'role', path: 'role',
name: 'Role', name: 'Role',
component: () => import('@/pages/system/role/index'), component: () => import('@/pages/system/role/index'),
meta: { title: '角色管理', icon: 'el-icon-s-check', roles: ['administrator'] } meta: { title: '角色管理', icon: 'el-icon-s-check', permission: 'menus_roles' }
}, },
{ {
path: 'roleToUser', path: 'roleToUser',
name: 'RoleToUser', name: 'RoleToUser',
hidden: true,
component: () => import('@/pages/system/role/role-to-user/index'), component: () => import('@/pages/system/role/role-to-user/index'),
meta: { title: '管理角色用户', icon: '', roles: ['administrator'] } meta: { title: '管理角色用户', icon: '' }
}, },
{ {
path: 'account', path: 'account',
name: 'Account', name: 'Account',
component: () => import('@/pages/system/account/index'), component: () => import('@/pages/system/account/index'),
meta: { title: '账号管理', icon: 'el-icon-key', roles: ['administrator'] } meta: { title: '账号管理', icon: 'el-icon-key', permission: 'menus_users' }
},
{
path: 'permission',
name: 'Permission',
component: () => import('@/pages/system/permission/index'),
meta: { title: '权限管理', icon: 'el-icon-finished', permission: 'menus_permissions' }
} }
] ]
}, },
...@@ -86,19 +98,20 @@ export default [ ...@@ -86,19 +98,20 @@ export default [
component: Layout, component: Layout,
redirect: '/tencent/account', redirect: '/tencent/account',
name: 'Tencent', name: 'Tencent',
meta: { title: '腾讯会议管理', icon: 'el-icon-setting' }, meta: { title: '腾讯会议管理', icon: 'el-icon-setting', permission: 'menus_tencent_meeting_manage' },
children: [ children: [
{ {
path: 'account', path: 'account',
name: 'TencentAccount', name: 'TencentAccount',
component: () => import('@/pages/tencent/account/index'), component: () => import('@/pages/tencent/account/index'),
meta: { title: '账号管理', icon: 'el-icon-key', roles: ['administrator'] } meta: { title: '账号管理', icon: 'el-icon-key', permission: 'menus_tencent_account' }
} }
] ]
}, },
{ {
path: '/my', path: '/my',
component: Layout, component: Layout,
meta: { onlyShowOneChild: true, permission: 'menus_personal' },
children: [ children: [
{ {
path: '', path: '',
...@@ -111,6 +124,7 @@ export default [ ...@@ -111,6 +124,7 @@ export default [
{ {
path: '/search', path: '/search',
component: Layout, component: Layout,
hidden: true,
children: [ children: [
{ {
path: '', path: '',
...@@ -125,7 +139,7 @@ export default [ ...@@ -125,7 +139,7 @@ export default [
component: Layout, component: Layout,
redirect: '/error-page/404', redirect: '/error-page/404',
name: 'ErrorPage', name: 'ErrorPage',
meta: { title: '访问出错', icon: 'el-icon-setting' }, meta: { title: '访问出错' },
hidden: true, hidden: true,
children: [ children: [
{ {
......
...@@ -2,6 +2,7 @@ const getters = { ...@@ -2,6 +2,7 @@ const getters = {
user: state => state.user.user, user: state => state.user.user,
roles: state => state.user.roles, roles: state => state.user.roles,
isSuperAdmin: state => state.user.isSuperAdmin, isSuperAdmin: state => state.user.isSuperAdmin,
sidebar: state => state.app.sidebar sidebar: state => state.app.sidebar,
permission_routes: state => state.permission.routes
} }
export default getters export default getters
...@@ -2,6 +2,7 @@ import Vue from 'vue' ...@@ -2,6 +2,7 @@ import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import app from './modules/app' import app from './modules/app'
import user from './modules/user' import user from './modules/user'
import permission from './modules/permission'
import getters from './getters' import getters from './getters'
Vue.use(Vuex) Vue.use(Vuex)
...@@ -10,7 +11,8 @@ const store = new Vuex.Store({ ...@@ -10,7 +11,8 @@ const store = new Vuex.Store({
namespaced: true, namespaced: true,
modules: { modules: {
app, app,
user user,
permission
}, },
getters getters
}) })
......
import { asyncRoutes } from '@/router'
/**
* Use meta.role to determine if the current user has permission
* @param permissions
* @param route
*/
function hasPermission(permissions, route) {
if (route.meta && route.meta.permission) {
return permissions.includes(route.meta.permission)
} else {
return true
}
}
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasRole(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
* @param type
*/
export function filterAsyncRoutes(routes, list, type) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (type === 'role') {
if (hasRole(list, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, list, type)
}
res.push(tmp)
}
} else {
if (hasPermission(list, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, list, type)
}
res.push(tmp)
}
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = [].concat(routes)
}
}
const actions = {
routesByRoles({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('administrator')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles, 'role')
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
},
routesByPermissions({ commit }, permissions) {
// console.log(permissions)
return new Promise(resolve => {
const accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions, 'permission')
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
import { getUser, logout } from '@/api/account' import { getUser, logout } from '@/api/account'
import { getUserRoles } from '@/api/system' import { getUserRoles, getUserPermissions } from '@/api/system'
import { resetRouter } from '@/router' import { resetRouter } from '@/router'
const user = { const user = {
state: { state: {
...@@ -7,7 +7,8 @@ const user = { ...@@ -7,7 +7,8 @@ const user = {
roles: [], roles: [],
isLogin: false, isLogin: false,
isSuperAdmin: false, isSuperAdmin: false,
hasRoles: false hasRoles: false,
permissions: []
}, },
mutations: { mutations: {
setUser(state, user) { setUser(state, user) {
...@@ -24,6 +25,9 @@ const user = { ...@@ -24,6 +25,9 @@ const user = {
}, },
setHasRoles(state, hasRoles) { setHasRoles(state, hasRoles) {
state.hasRoles = hasRoles state.hasRoles = hasRoles
},
setPermissions(state, permissions) {
state.permissions = permissions
} }
}, },
...@@ -41,6 +45,7 @@ const user = { ...@@ -41,6 +45,7 @@ const user = {
commit('setRoles', []) commit('setRoles', [])
commit('setSuperAdmin', false) commit('setSuperAdmin', false)
commit('setHasRoles', false) commit('setHasRoles', false)
commit('setPermissions', [])
resetRouter() resetRouter()
return response return response
}) })
...@@ -70,7 +75,7 @@ const user = { ...@@ -70,7 +75,7 @@ const user = {
commit('setIsLogin', isLogin) commit('setIsLogin', isLogin)
return isLogin return isLogin
}, },
// 检测登录状态 // 获取角色
async checkRoles({ commit }) { async checkRoles({ commit }) {
const hasRoles = await getUserRoles().then(res => { const hasRoles = await getUserRoles().then(res => {
const roles = res.data.roles const roles = res.data.roles
...@@ -90,6 +95,22 @@ const user = { ...@@ -90,6 +95,22 @@ const user = {
}) })
commit('setHasRoles', hasRoles) commit('setHasRoles', hasRoles)
return hasRoles return hasRoles
},
// 获取权限
async checkPermissions({ commit }) {
const perms = await getUserPermissions().then(res => {
if (res.code === 0) {
commit('setPermissions', res.data)
return res.data
} else {
commit('setPermissions', [])
return []
}
}).catch(() => {
commit('setPermissions', [])
return []
})
return perms
} }
} }
} }
......
...@@ -116,3 +116,18 @@ body { ...@@ -116,3 +116,18 @@ body {
.app-main .el-table th:first-child .cell{ .app-main .el-table th:first-child .cell{
padding-left:14px; padding-left:14px;
} }
/* element-ui drawer reset */
.el-drawer__header{
padding:14px 16px;
margin:0;
border-bottom:1px solid #DCDFE6;
}
.el-drawer__header>h5{
font-size:16px;
color:#666;
font-weight:600;
}
.el-drawer__body{
height:calc(100% - 82px);
overflow-y: auto;
}
import store from '@/store' import store from '@/store'
import router from '@/router' import router, { resetRouter } from '@/router'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param routeRoles
*/
function hasPermission(roles, routeRoles) {
if (Array.isArray(routeRoles)) {
return roles.some(role => routeRoles.includes(role))
} else {
return true
}
}
export default class BeforeEnter { export default class BeforeEnter {
constructor(opt) { constructor(opt) {
this.opt = opt || {} this.opt = opt || {}
...@@ -20,36 +8,48 @@ export default class BeforeEnter { ...@@ -20,36 +8,48 @@ export default class BeforeEnter {
async update(to, from, next) { async update(to, from, next) {
const isLogin = store.state.user.isLogin || (await store.dispatch('checkLogin')) const isLogin = store.state.user.isLogin || (await store.dispatch('checkLogin'))
if (!isLogin) { if (!isLogin) {
window.location.href = this.getLoginUrl() window.location.href = `${webConf.others.loginUrl}?rd=${encodeURIComponent(window.location.href)}`
next() next()
} else { } else {
const hasRoles = store.state.user.hasRoles || (await store.dispatch('checkRoles')) const hasPermissions = store.state.user.permissions && store.state.user.permissions.length > 0
if (hasRoles) { if (hasPermissions) {
const permissions = store.state.user.permissions
if (to.path.includes('error-page')) { if (to.path.includes('error-page')) {
next() next()
} else { } else {
const roles = store.state.user.roles.map(item => item.name) if (to.meta && to.meta.permission && !permissions.includes(to.meta.permission)) {
console.log(roles)
if (!hasPermission(roles, to.meta.roles)) {
// next(loginUrl)
router.push('/error-page/401') router.push('/error-page/401')
} }
next() next()
} }
} else { } else {
window.location.href = this.getLoginUrl() const permissions = await store.dispatch('checkPermissions')
store.dispatch('checkRoles')
if (!permissions.length) {
window.location.href = `${webConf.others.loginUrl}?rd=${encodeURIComponent(window.location.href)}`
next()
} else {
try {
// 基于权限获取可接入的路由
const accessRoutes = await store.dispatch('permission/routesByPermissions', permissions)
// 重新设置路由
resetRouter()
// 动态添加路由
router.addRoute(accessRoutes)
next({ ...to, replace: true })
} catch (error) {
next()
}
if (to.path.includes('error-page')) {
next()
} else {
if (to.meta && to.meta.permission && !permissions.includes(to.meta.permission)) {
router.replace('/error-page/401')
}
next()
}
}
} }
} }
next()
}
getLoginUrl() {
const loginUrl = webConf.others.loginUrl
let rdUrl = window.location.href
if (rdUrl.includes('error-page')) {
rdUrl = window.location.protocol + '//' + window.location.host
}
console.log(rdUrl)
return `${loginUrl}?rd=${encodeURIComponent(rdUrl)}`
} }
} }
import store from '@/store'
import router from '@/router'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param routeRoles
*/
function hasPermission(roles, routeRoles) {
if (Array.isArray(routeRoles)) {
return roles.some(role => routeRoles.includes(role))
} else {
return true
}
}
export default class BeforeEnter {
constructor(opt) {
this.opt = opt || {}
}
async update(to, from, next) {
const isLogin = store.state.user.isLogin || (await store.dispatch('checkLogin'))
if (!isLogin) {
window.location.href = this.getLoginUrl()
next()
} else {
const hasRoles = store.state.user.hasRoles || (await store.dispatch('checkRoles'))
const perms = await store.dispatch('checkPermissions')
console.log(perms)
if (hasRoles) {
if (to.path.includes('error-page')) {
next()
} else {
const roles = store.state.user.roles.map(item => item.name)
if (!hasPermission(roles, to.meta.roles)) {
// next(loginUrl)
router.push('/error-page/401')
}
next()
}
} else {
window.location.href = this.getLoginUrl()
}
}
next()
}
getLoginUrl() {
const loginUrl = webConf.others.loginUrl
let rdUrl = window.location.href
if (rdUrl.includes('error-page')) {
rdUrl = window.location.protocol + '//' + window.location.host
}
return `${loginUrl}?rd=${encodeURIComponent(rdUrl)}`
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论