feat(role): 角色模块的增删改查
This commit is contained in:
@@ -38,7 +38,7 @@ export function listSelectMenus() {
|
|||||||
*/
|
*/
|
||||||
export function listTreeSelectMenus() {
|
export function listTreeSelectMenus() {
|
||||||
return request({
|
return request({
|
||||||
url: '/youlai-admin/api/v1/menus/tree-select',
|
url: '/youlai-admin/api/v1/menus/tree_select',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function listRoles(queryParams: object) {
|
|||||||
*
|
*
|
||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
export function getPermDetail(id: number) {
|
export function getRoleDetail(id: number) {
|
||||||
return request({
|
return request({
|
||||||
url: '/youlai-admin/api/v1/roles/' + id,
|
url: '/youlai-admin/api/v1/roles/' + id,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
@@ -43,7 +43,7 @@ export function getPermDetail(id: number) {
|
|||||||
*
|
*
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
export function addPerm(data: object) {
|
export function addRole(data: object) {
|
||||||
return request({
|
return request({
|
||||||
url: '/youlai-admin/api/v1/roles',
|
url: '/youlai-admin/api/v1/roles',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@@ -57,7 +57,7 @@ export function addPerm(data: object) {
|
|||||||
* @param id
|
* @param id
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
export function updatePerm(id: number, data: object) {
|
export function updateRole(id: number, data: object) {
|
||||||
return request({
|
return request({
|
||||||
url: '/youlai-admin/api/v1/roles/' + id,
|
url: '/youlai-admin/api/v1/roles/' + id,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@@ -77,3 +77,57 @@ export function deleteRoles(ids: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色的菜单列表
|
||||||
|
*
|
||||||
|
* @param roleId
|
||||||
|
*/
|
||||||
|
export function listRoleMenuIds(roleId: number) {
|
||||||
|
return request({
|
||||||
|
url: '/youlai-admin/api/v1/roles/' + roleId + '/menu_ids',
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改角色的菜单
|
||||||
|
*
|
||||||
|
* @param roleId
|
||||||
|
* @param menuIds
|
||||||
|
*/
|
||||||
|
export function updateRoleMenu(roleId: number, menuIds: Array<Number>) {
|
||||||
|
return request({
|
||||||
|
url: '/youlai-admin/api/v1/roles/' + roleId + '/menus',
|
||||||
|
method: 'put',
|
||||||
|
data: {menuIds: menuIds}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色的权限列表
|
||||||
|
*
|
||||||
|
* @param roleId
|
||||||
|
*/
|
||||||
|
export function listRolePerms(roleId: number) {
|
||||||
|
return request({
|
||||||
|
url: '/youlai-admin/api/v1/roles/' + roleId + '/permissions',
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存角色权限
|
||||||
|
*
|
||||||
|
* @param menuId 菜单ID,归类权限
|
||||||
|
* @param roleId
|
||||||
|
* @param permIds
|
||||||
|
*/
|
||||||
|
export function saveRolePerms(menuId: number, roleId: number, permIds: Array<number>) {
|
||||||
|
return request({
|
||||||
|
url: '/youlai-admin/api/v1/roles/' + roleId + '/permissions',
|
||||||
|
method: 'put',
|
||||||
|
data: {menuId: menuId, permIds: permIds}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -7,92 +7,86 @@
|
|||||||
:layout="layout"
|
:layout="layout"
|
||||||
:page-sizes="pageSizes"
|
:page-sizes="pageSizes"
|
||||||
:total="total"
|
:total="total"
|
||||||
v-bind="$attrs"
|
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup>
|
||||||
import {computed, defineComponent} from "vue";
|
import {computed,defineProps,defineEmits} from "vue";
|
||||||
import {scrollTo} from '@/utils/scroll-to'
|
import {scrollTo} from '@/utils/scroll-to'
|
||||||
|
|
||||||
export default defineComponent({
|
const props=defineProps({
|
||||||
name: 'Pagination',
|
total: {
|
||||||
props: {
|
required: true,
|
||||||
total: {
|
type: Number,
|
||||||
required: true,
|
default: 0
|
||||||
type: Number,
|
},
|
||||||
default: 0
|
page: {
|
||||||
},
|
type: Number,
|
||||||
page: {
|
default: 1
|
||||||
type: Number,
|
},
|
||||||
default: 1
|
limit: {
|
||||||
},
|
type: Number,
|
||||||
limit: {
|
default: 20
|
||||||
type: Number,
|
},
|
||||||
default: 20
|
pageSizes: {
|
||||||
},
|
type: Array,
|
||||||
pageSizes: {
|
default() {
|
||||||
type: Array,
|
return [10, 20, 30, 50]
|
||||||
default() {
|
|
||||||
return [10, 20, 30, 50]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
type: String,
|
|
||||||
default: 'total, sizes, prev, pager, next, jumper'
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
autoScroll: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
hidden: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['pagination', 'update:page', 'update:limit'],
|
layout: {
|
||||||
|
type: String,
|
||||||
setup(props: any, ctx) {
|
default: 'total, sizes, prev, pager, next, jumper'
|
||||||
const currentPage = computed({
|
},
|
||||||
get() {
|
background: {
|
||||||
return props.page
|
type: Boolean,
|
||||||
},
|
default: true
|
||||||
set(val) {
|
},
|
||||||
ctx.emit('update:page', val)
|
autoScroll: {
|
||||||
}
|
type: Boolean,
|
||||||
})
|
default: true
|
||||||
|
},
|
||||||
const pageSize = computed({
|
hidden: {
|
||||||
get() {
|
type: Boolean,
|
||||||
return props.limit
|
default: false
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
ctx.emit('update:limit', val)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSizeChange = (val: number) => {
|
|
||||||
ctx.emit('pagination', {page: currentPage, limit: val})
|
|
||||||
if (props.autoScroll) {
|
|
||||||
scrollTo(0, 800)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const handleCurrentChange = (val: number) => {
|
|
||||||
ctx.emit('pagination', {page: val, limit: props.pageSizes})
|
|
||||||
if (props.autoScroll) {
|
|
||||||
scrollTo(0, 800)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {currentPage, pageSize, handleSizeChange, handleCurrentChange}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits();
|
||||||
|
|
||||||
|
const currentPage = computed({
|
||||||
|
get() {
|
||||||
|
return props.page
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
emit('update:page', val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const pageSize = computed({
|
||||||
|
get() {
|
||||||
|
return props.limit
|
||||||
|
},
|
||||||
|
set(val){
|
||||||
|
emit('update:limit', val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSizeChange(val){
|
||||||
|
emit('pagination', {page: currentPage, limit: val})
|
||||||
|
if (props.autoScroll) {
|
||||||
|
scrollTo(0, 800)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleCurrentChange(val){
|
||||||
|
emit('pagination', {page: val, limit: props.pageSizes})
|
||||||
|
if (props.autoScroll) {
|
||||||
|
scrollTo(0, 800)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
size="mini"
|
size="mini"
|
||||||
>
|
>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" :icon="Plus" @click="handleAdd">新增</el-button>
|
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
|
||||||
<el-button type="success" :icon="Edit" :disabled="state.single" @click="handleUpdate">修改</el-button>
|
|
||||||
<el-button type="danger" :icon='Delete' :disabled="state.multiple" @click="handleDelete">删除</el-button>
|
<el-button type="danger" :icon='Delete' :disabled="state.multiple" @click="handleDelete">删除</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
@@ -71,8 +70,8 @@
|
|||||||
<pagination
|
<pagination
|
||||||
v-show="state.total>0"
|
v-show="state.total>0"
|
||||||
:total="state.total"
|
:total="state.total"
|
||||||
:page.sync="state.queryParams.pageNum"
|
v-model:page="state.queryParams.pageNum"
|
||||||
:limit.sync="state.queryParams.pageSize"
|
v-model:limit="state.queryParams.pageSize"
|
||||||
@pagination="handleQuery"
|
@pagination="handleQuery"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -114,8 +113,9 @@
|
|||||||
import {listClientsWithPage, detail, update, add, del} from '@/api/system/client'
|
import {listClientsWithPage, detail, update, add, del} from '@/api/system/client'
|
||||||
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons'
|
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons'
|
||||||
import {onMounted, reactive, getCurrentInstance, ref, unref} from 'vue'
|
import {onMounted, reactive, getCurrentInstance, ref, unref} from 'vue'
|
||||||
import {ElForm, ElMessage, ElMessageBox} from "element-plus";
|
import {ElForm, ElMessage, ElMessageBox} from "element-plus"
|
||||||
|
|
||||||
|
const dataForm = ref(ElForm)
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
loading: true,
|
loading: true,
|
||||||
// 选中ID数组
|
// 选中ID数组
|
||||||
@@ -200,7 +200,7 @@ function handleUpdate(row: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataForm = ref(ElForm)
|
|
||||||
function submitForm() {
|
function submitForm() {
|
||||||
const form = unref(dataForm)
|
const form = unref(dataForm)
|
||||||
form.validate((valid: any) => {
|
form.validate((valid: any) => {
|
||||||
@@ -237,6 +237,7 @@ function resetForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
resetForm()
|
||||||
state.dialog.visible = false
|
state.dialog.visible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,8 @@
|
|||||||
<pagination
|
<pagination
|
||||||
v-show="state.total>0"
|
v-show="state.total>0"
|
||||||
:total="state.total"
|
:total="state.total"
|
||||||
:page.sync="state.queryParams.pageNum"
|
v-model:page="state.queryParams.pageNum"
|
||||||
:limit.sync="state.queryParams.pageSize"
|
v-model:limit="state.queryParams.pageSize"
|
||||||
@pagination="handleQuery"
|
@pagination="handleQuery"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
|
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="state.queryParams.name"
|
v-model="state.queryParams.name"
|
||||||
@@ -169,7 +170,7 @@
|
|||||||
import {listTableMenus, getMenuDetail, listTreeSelectMenus, addMenu, deleteMenus, updateMenu} from "@/api/system/menu";
|
import {listTableMenus, getMenuDetail, listTreeSelectMenus, addMenu, deleteMenus, updateMenu} from "@/api/system/menu";
|
||||||
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons'
|
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons'
|
||||||
import {ElForm, ElMessage, ElMessageBox} from "element-plus";
|
import {ElForm, ElMessage, ElMessageBox} from "element-plus";
|
||||||
import {defineEmits, reactive, ref, unref, onMounted, watch, getCurrentInstance, computed} from "vue";
|
import {defineEmits, reactive, ref, unref, onMounted} from "vue";
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
import TreeSelect from '@/components/TreeSelect/index.vue';
|
import TreeSelect from '@/components/TreeSelect/index.vue';
|
||||||
import IconSelect from '@/components/IconSelect/index.vue';
|
import IconSelect from '@/components/IconSelect/index.vue';
|
||||||
@@ -230,8 +231,6 @@ function handleQuery() {
|
|||||||
state.menuList = data
|
state.menuList = data
|
||||||
state.total = total
|
state.total = total
|
||||||
state.loading = false
|
state.loading = false
|
||||||
}).catch(() => {
|
|
||||||
state.loading = false
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +330,6 @@ function submitForm() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
state.formData = {
|
state.formData = {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
@@ -346,6 +344,7 @@ function resetForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
resetForm()
|
||||||
state.dialog.visible = false
|
state.dialog.visible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +374,6 @@ function selected(name: string) {
|
|||||||
showChooseIcon.value = false;
|
showChooseIcon.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
handleQuery()
|
handleQuery()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<svg-icon color="#333" icon-class="perm"/>
|
<svg-icon color="#333" icon-class="perm"/>
|
||||||
<span style="margin:0 5px;">权限列表</span>
|
<span style="margin:0 5px;">权限列表</span>
|
||||||
<el-tag type="success" v-if=" state.menuId">{{ state.menuName }}</el-tag>
|
<el-tag type="success" v-if="state.menuId">{{ state.menuName }}</el-tag>
|
||||||
<el-tag type="warning" v-else size="small">请点击左侧菜单列表选择</el-tag>
|
<el-tag type="warning" v-else size="small">请点击左侧菜单列表选择</el-tag>
|
||||||
</template>
|
</template>
|
||||||
<perm-table :menuId="state.menuId" :menuName="state.menuName"/>
|
<perm-table :menuId="state.menuId" :menuName="state.menuName"/>
|
||||||
@@ -37,7 +37,7 @@ const state = reactive({
|
|||||||
menuName: ''
|
menuName: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleMenuClick = (menuRow: any) => {
|
function handleMenuClick (menuRow: any){
|
||||||
if (menuRow) {
|
if (menuRow) {
|
||||||
state.menuId = menuRow.id
|
state.menuId = menuRow.id
|
||||||
state.menuName = menuRow.name
|
state.menuName = menuRow.name
|
||||||
|
|||||||
92
src/views/system/role/components/Menu.vue
Normal file
92
src/views/system/role/components/Menu.vue
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<el-form size="mini" >
|
||||||
|
<el-form-item>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-button type="success" plain :icon="Switch" >展开/折叠</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" style="text-align: right">
|
||||||
|
<el-button type="primary" :icon="Check" @click="handleQuery">提交</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
|
||||||
|
<el-tree
|
||||||
|
ref="menuRef"
|
||||||
|
:default-expanded-keys="state.expandedKeys"
|
||||||
|
:default-expand-all="true"
|
||||||
|
:data="state.menuOptions"
|
||||||
|
show-checkbox
|
||||||
|
node-key="id"
|
||||||
|
empty-text="加载菜单中..."
|
||||||
|
:check-strictly="state.checkStrictly"
|
||||||
|
highlight-current
|
||||||
|
@node-click="handleNodeClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {listTreeSelectMenus} from "@/api/system/menu";
|
||||||
|
import {listRoleMenuIds, updateRoleMenu} from "@/api/system/role"
|
||||||
|
import {defineEmits, defineProps,onMounted, reactive, ref, watch} from "vue"
|
||||||
|
import {ElTree, ElMessage, ElMessageBox} from "element-plus"
|
||||||
|
|
||||||
|
import {Switch,Check} from '@element-plus/icons'
|
||||||
|
|
||||||
|
const emit = defineEmits(['menuClick'])
|
||||||
|
const props = defineProps({
|
||||||
|
role: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const menuRef = ref(ElTree) // 属性名必须和元素的ref属性值一致
|
||||||
|
|
||||||
|
watch(() => props.role.id as any, (newVal, oldVal) => {
|
||||||
|
const roleId = props.role.id
|
||||||
|
if (roleId) {
|
||||||
|
state.checkStrictly = true
|
||||||
|
listRoleMenuIds(roleId).then(response => {
|
||||||
|
const checkedMenuIds = response.data
|
||||||
|
console.log('选中的菜单',checkedMenuIds)
|
||||||
|
menuRef.value.setCheckedKeys(checkedMenuIds)
|
||||||
|
state.checkStrictly = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
expandedKeys: [], // 展开的节点
|
||||||
|
menuOptions: [],
|
||||||
|
checkStrictly: false
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载菜单树
|
||||||
|
*/
|
||||||
|
async function loadTreeSelectMenuOptions() {
|
||||||
|
await listTreeSelectMenus().then(response => {
|
||||||
|
state.menuOptions = response.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNodeClick(node: any) {
|
||||||
|
emit('menuClick', node)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadTreeSelectMenuOptions()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
187
src/views/system/role/components/Perm.vue
Normal file
187
src/views/system/role/components/Perm.vue
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<div class="perm-container">
|
||||||
|
<el-card class="box-card" shadow="always">
|
||||||
|
<div class="clearfix" slot="header">
|
||||||
|
<b>
|
||||||
|
<svg-icon icon-class="menu"/>
|
||||||
|
{{ menu && menu.label ? "【" + menu.label + "】" : "" }}权限分配
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
|
<el-row style="margin-bottom: 10px">
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-tag v-if="role" type="primary">{{ role.name }}</el-tag>
|
||||||
|
<el-tag v-if="menu" type="success" style="margin-left: 5px">{{
|
||||||
|
menu.label
|
||||||
|
}}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="!role" type="info" style="margin-left: 5px"
|
||||||
|
><i class="el-icon-info"> </i> 请选择角色
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="!menu" type="info" style="margin-left: 5px"
|
||||||
|
><i class="el-icon-info"> </i> 请选择菜单
|
||||||
|
</el-tag>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6" style="text-align: right">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:disabled="isRoot"
|
||||||
|
icon="el-icon-check"
|
||||||
|
size="mini"
|
||||||
|
@click="handleSubmit"
|
||||||
|
>提交
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div v-if="permissionList.length > 0">
|
||||||
|
<el-checkbox
|
||||||
|
:indeterminate="isIndeterminate"
|
||||||
|
v-model="checkAll"
|
||||||
|
@change="handleCheckAllChange"
|
||||||
|
style="margin-top: 20px"
|
||||||
|
>全选
|
||||||
|
</el-checkbox>
|
||||||
|
<el-row>
|
||||||
|
<el-col
|
||||||
|
:span="8"
|
||||||
|
v-for="permission in permissionList"
|
||||||
|
style="margin-top: 20px"
|
||||||
|
>
|
||||||
|
<el-checkbox
|
||||||
|
border
|
||||||
|
v-model="permission.checked"
|
||||||
|
:label="permission.id"
|
||||||
|
:key="permission.id"
|
||||||
|
@change="handleCheckChange"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
|
{{ permission.name }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center" v-else>
|
||||||
|
<el-empty :description=" !role? '请选择角色': !menu? '请选择菜单': '暂无数据,您可在【菜单管理】配置权限数据'"></el-empty>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getPermissionList} from "@/api/system/permission";
|
||||||
|
import {listRolePermission, updateRolePermission} from "@/api/system/role";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Permission",
|
||||||
|
props: ["type"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
ids: [],
|
||||||
|
initialCheckedPermissionIds: [],
|
||||||
|
menu: undefined,
|
||||||
|
role: undefined,
|
||||||
|
isIndeterminate: true,
|
||||||
|
checkAll: false,
|
||||||
|
permissionList: [],
|
||||||
|
isRoot: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleQuery() {
|
||||||
|
this.loading = true;
|
||||||
|
const menuId = this.menu.value;
|
||||||
|
getPermissionList({
|
||||||
|
menuId: menuId
|
||||||
|
}).then((response) => {
|
||||||
|
const {data} = response;
|
||||||
|
if (this.role.code == this.ROOT_ROLE_CODE) {
|
||||||
|
// 如果是超级管理员默认勾选全部且不可编辑
|
||||||
|
this.isRoot = true
|
||||||
|
this.checkAll = true
|
||||||
|
this.isIndeterminate = false
|
||||||
|
data.map((item) => this.$set(item, "checked", true))
|
||||||
|
this.permissionList = data
|
||||||
|
this.loading = false
|
||||||
|
} else {
|
||||||
|
this.isRoot = false;
|
||||||
|
listRolePermission(this.role.id, {menuId: menuId}).then((res) => {
|
||||||
|
this.initialCheckedPermissionIds = res.data;
|
||||||
|
let checkAll = true
|
||||||
|
data.map((item) => {
|
||||||
|
if (this.initialCheckedPermissionIds.includes(item.id)) {
|
||||||
|
item.checked = true;
|
||||||
|
} else {
|
||||||
|
checkAll = false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.checkAll = checkAll
|
||||||
|
if (checkAll) {
|
||||||
|
this.isIndeterminate = false
|
||||||
|
}
|
||||||
|
this.permissionList = data;
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
menuClick(menu, role) {
|
||||||
|
this.role = role;
|
||||||
|
this.menu = menu;
|
||||||
|
|
||||||
|
if (role && menu) {
|
||||||
|
this.handleQuery();
|
||||||
|
} else {
|
||||||
|
this.permissionList = [];
|
||||||
|
this.initialCheckedPermissionIds = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSubmit: function () {
|
||||||
|
const checkedPermissionIds = this.permissionList
|
||||||
|
.filter((item) => item.checked)
|
||||||
|
.map((item) => item.id);
|
||||||
|
// 判断选中权限是否变动
|
||||||
|
if (
|
||||||
|
this.initialCheckedPermissionIds.length ==
|
||||||
|
checkedPermissionIds.length &&
|
||||||
|
this.initialCheckedPermissionIds.sort().every(function (v, i) {
|
||||||
|
return v == checkedPermissionIds[i];
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
this.$message.warning("数据未变动");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateRolePermission(
|
||||||
|
this.menu.value,
|
||||||
|
this.role.id,
|
||||||
|
checkedPermissionIds
|
||||||
|
).then((response) => {
|
||||||
|
this.$message.success("提交成功");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleCheckAllChange(checked) {
|
||||||
|
if (checked) {
|
||||||
|
this.permissionList.map((item) => (item.checked = true));
|
||||||
|
} else {
|
||||||
|
// 全不选
|
||||||
|
this.permissionList.map((item) => (item.checked = false));
|
||||||
|
}
|
||||||
|
this.isIndeterminate = false;
|
||||||
|
},
|
||||||
|
handleCheckChange(item, val) {
|
||||||
|
const checkedCount = this.permissionList.filter(
|
||||||
|
(item) => item.checked
|
||||||
|
).length;
|
||||||
|
this.checkAll = checkedCount === this.permissionList.length;
|
||||||
|
this.isIndeterminate =
|
||||||
|
checkedCount > 0 && checkedCount < this.permissionList.length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.perm-container {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
270
src/views/system/role/components/Role.vue
Normal file
270
src/views/system/role/components/Role.vue
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-form
|
||||||
|
ref="queryForm"
|
||||||
|
:model="state.queryParams"
|
||||||
|
size="mini"
|
||||||
|
:inline="true"
|
||||||
|
>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
|
||||||
|
<el-button type="danger" :icon='Delete' :disabled="state.multiple" @click="handleDelete">删除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="name">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryParams.name"
|
||||||
|
placeholder="角色名称"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-table
|
||||||
|
ref="roleTable"
|
||||||
|
v-loading="state.loading"
|
||||||
|
:data="state.pageList"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-click="handleRowClick"
|
||||||
|
border
|
||||||
|
highlight-current-row
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" align="center"/>
|
||||||
|
<el-table-column label="角色名称" prop="name"/>
|
||||||
|
<el-table-column label="角色编码" prop="code"/>
|
||||||
|
<el-table-column label="操作" align="center" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="Edit"
|
||||||
|
size="mini"
|
||||||
|
circle
|
||||||
|
plain
|
||||||
|
@click.stop="handleUpdate(scope.row)"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:icon="Delete"
|
||||||
|
size="mini"
|
||||||
|
circle
|
||||||
|
plain
|
||||||
|
@click.stop="handleDelete(scope.row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页工具条 -->
|
||||||
|
<pagination
|
||||||
|
v-show="state.total>0"
|
||||||
|
:total="state.total"
|
||||||
|
v-model:page="state.queryParams.pageNum"
|
||||||
|
v-model:limit="state.queryParams.pageSize"
|
||||||
|
@pagination="handleQuery"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 表单弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
:title="state.dialog.title"
|
||||||
|
v-model="state.dialog.visible"
|
||||||
|
width="450px"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="dataForm"
|
||||||
|
:model="state.formData"
|
||||||
|
:rules="state.rules"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item label="角色名称" prop="name">
|
||||||
|
<el-input v-model="state.formData.name" placeholder="请输入角色名称"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="角色编码" prop="code">
|
||||||
|
<el-input v-model="state.formData.code" placeholder="请输入角色编码"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="排序" prop="sort">
|
||||||
|
<el-input-number v-model="state.formData.sort" controls-position="right" :min="0" style="width: 100px"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-radio-group v-model="state.formData.status">
|
||||||
|
<el-radio :label="1">正常</el-radio>
|
||||||
|
<el-radio :label="0">停用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {listRolesWithPage, updateRole, getRoleDetail, addRole, deleteRoles} from '@/api/system/role'
|
||||||
|
import {defineEmits, defineProps, onMounted, reactive, ref, unref} from "vue"
|
||||||
|
import {add, del, detail, update} from "@api/system/client";
|
||||||
|
import {ElForm, ElMessage, ElMessageBox} from "element-plus";
|
||||||
|
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons'
|
||||||
|
|
||||||
|
const emit = defineEmits(['roleClick'])
|
||||||
|
|
||||||
|
const dataForm = ref() // 属性名必须和元素的ref属性值一致
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
loading: true,
|
||||||
|
// 选中ID数组
|
||||||
|
ids: [],
|
||||||
|
// 非单个禁用
|
||||||
|
single: true,
|
||||||
|
// 非多个禁用
|
||||||
|
multiple: true,
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: undefined
|
||||||
|
},
|
||||||
|
pageList: [],
|
||||||
|
total: 0,
|
||||||
|
dialog: {
|
||||||
|
title: '',
|
||||||
|
visible: false
|
||||||
|
},
|
||||||
|
formData: {
|
||||||
|
id: undefined,
|
||||||
|
parentId: 0,
|
||||||
|
name: undefined,
|
||||||
|
sort: 1,
|
||||||
|
status: 1
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
name: [
|
||||||
|
{required: true, message: '请输入角色名称', trigger: 'blur'}
|
||||||
|
],
|
||||||
|
code: [
|
||||||
|
{required: true, message: '请输入角色编码', trigger: 'blur'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleQuery() {
|
||||||
|
state.loading = true
|
||||||
|
listRolesWithPage(state.queryParams).then(response => {
|
||||||
|
const {data, total} = response as any
|
||||||
|
state.pageList = data
|
||||||
|
state.total = total
|
||||||
|
state.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
state.queryParams = {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: undefined
|
||||||
|
}
|
||||||
|
handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelectionChange(selection: any) {
|
||||||
|
state.ids = selection.map((item: any) => item.id)
|
||||||
|
state.single = selection.length !== 1
|
||||||
|
state.multiple = !selection.length
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRowClick(row: any) {
|
||||||
|
emit('roleClick', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
resetForm()
|
||||||
|
state.dialog = {
|
||||||
|
title: '添加角色',
|
||||||
|
visible: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdate(row: any) {
|
||||||
|
resetForm()
|
||||||
|
state.dialog = {
|
||||||
|
title: '修改角色',
|
||||||
|
visible: true,
|
||||||
|
}
|
||||||
|
const roleId = row.id || state.ids
|
||||||
|
getRoleDetail(roleId).then(response => {
|
||||||
|
state.formData = response.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitForm() {
|
||||||
|
const form = unref(dataForm)
|
||||||
|
form.validate((valid: any) => {
|
||||||
|
if (valid) {
|
||||||
|
if (state.formData.id) {
|
||||||
|
updateRole(state.formData.id as any, state.formData).then(response => {
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
state.dialog.visible = false
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addRole(state.formData).then(response => {
|
||||||
|
ElMessage.success('新增成功')
|
||||||
|
state.dialog.visible = false
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
state.formData = {
|
||||||
|
id: undefined,
|
||||||
|
parentId: 0,
|
||||||
|
name: undefined,
|
||||||
|
sort: 1,
|
||||||
|
status: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
resetForm()
|
||||||
|
state.dialog.visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(row: any) {
|
||||||
|
const ids = [row.id || state.ids].join(',')
|
||||||
|
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteRoles(ids).then(() => {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
}).catch(() =>
|
||||||
|
ElMessage.info('已取消删除')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
101
src/views/system/role/index.vue
Normal file
101
src/views/system/role/index.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row :gutter="10">
|
||||||
|
<el-col :span="10" :xs="24">
|
||||||
|
<el-card class="box-card" shadow="always">
|
||||||
|
<template #header>
|
||||||
|
|
||||||
|
<svg-icon color="#333" icon-class="menu"/>
|
||||||
|
角色列表
|
||||||
|
</template>
|
||||||
|
<role ref="role" @roleClick="handleRoleClick"/>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="6" :xs="24">
|
||||||
|
<el-card class="box-card" shadow="always">
|
||||||
|
<template #header>
|
||||||
|
<svg-icon color="#333" icon-class="menu"/>
|
||||||
|
<span style="margin:0 5px;">菜单分配</span>
|
||||||
|
<el-tag type="success" v-if="state.role.id" size="small">{{ state.role.name }}</el-tag>
|
||||||
|
<el-tag type="warning" v-else size="small">请选择角色</el-tag>
|
||||||
|
</template>
|
||||||
|
<menus ref="menu" @menuClick="handleMenuClick" :role="state.role"/>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="8" :xs="24">
|
||||||
|
<el-card class="box-card" shadow="always">
|
||||||
|
<template #header>
|
||||||
|
<svg-icon color="#333" icon-class="menu"/>
|
||||||
|
<span style="margin:0 5px;">权限分配</span>
|
||||||
|
<el-tag type="success" v-if="state.role.id" size="small">{{ state.role.name }}</el-tag>
|
||||||
|
<el-tag type="warning" v-else size="small"> 请选择角色</el-tag>
|
||||||
|
|
||||||
|
<el-tag type="success" v-if="state.role.id" size="small">{{ state.role.name }}</el-tag>
|
||||||
|
<el-tag type="warning" v-else size="small"> 请选择菜单</el-tag>
|
||||||
|
</template>
|
||||||
|
<!--<perm ref="perm" :menu="state.menu" :role="state.role"/>-->
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Role from './components/Role.vue'
|
||||||
|
import Menus from './components/Menu.vue'
|
||||||
|
import {reactive} from "vue";
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
role: {
|
||||||
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleRoleClick(roleRow: any) {
|
||||||
|
if (roleRow) {
|
||||||
|
state.role = {
|
||||||
|
id: roleRow.id,
|
||||||
|
name: roleRow.name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.role = {
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMenuClick(menuRow: any) {
|
||||||
|
if (!state.role.id) {
|
||||||
|
ElMessage.warning('请选择角色')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menuRow) {
|
||||||
|
state.menu = {
|
||||||
|
id: menuRow.id,
|
||||||
|
name: menuRow.name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.menu = {
|
||||||
|
id: undefined,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user