@@ -1,70 +0,0 @@
|
||||
import { request } from "/@/utils/service";
|
||||
import XEUtils from "xe-utils";
|
||||
/**
|
||||
* 获取角色的授权列表
|
||||
* @param roleId
|
||||
* @param query
|
||||
*/
|
||||
export function getRolePermission(query:object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/get_role_permission/',
|
||||
method: 'get',
|
||||
params:query
|
||||
}).then((res:any)=>{
|
||||
return XEUtils.toArrayTree(res.data, {key: 'id', parentKey: 'parent',children: 'children',strict: false})
|
||||
})
|
||||
}
|
||||
|
||||
/***
|
||||
* 设置角色的权限
|
||||
* @param roleId
|
||||
* @param data
|
||||
*/
|
||||
export function setRolePremission(roleId:any,data:object) {
|
||||
return request({
|
||||
url: `/api/system/role_menu_button_permission/${roleId}/set_role_premission/`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getDataPermissionRange(query:object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/data_scope/',
|
||||
method: 'get',
|
||||
params:query
|
||||
})
|
||||
}
|
||||
|
||||
export function getDataPermissionRangeAll() {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/data_scope/',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
export function getDataPermissionDept(query:object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/role_to_dept_all/',
|
||||
method: 'get',
|
||||
params:query
|
||||
})
|
||||
}
|
||||
|
||||
export function getDataPermissionMenu() {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/get_role_permissions/',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置按钮的数据范围
|
||||
*/
|
||||
export function setBtnDatarange(roleId:number,data:object) {
|
||||
return request({
|
||||
url: `/api/system/role_menu_button_permission/${roleId}/set_btn_datarange/`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,451 +0,0 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="drawerVisibleNew"
|
||||
title="权限配置"
|
||||
direction="rtl"
|
||||
size="60%"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="handleDrawerClose"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<template #header>
|
||||
<el-row>
|
||||
<el-col :span="4">
|
||||
<div>
|
||||
当前授权角色:
|
||||
<el-tag>{{ props.roleName }}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div>
|
||||
<el-button size="small" type="primary" class="pc-save-btn" @click="handleSavePermission">保存菜单授权 </el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<div class="permission-com">
|
||||
<el-row class="menu-el-row" :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="menu-box menu-left-box">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="menuData"
|
||||
:props="defaultTreeProps"
|
||||
:default-checked-keys="menuDefaultCheckedKeys"
|
||||
@check="handleMenuCheck"
|
||||
@node-click="handleMenuClick"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
highlight-current
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="18">
|
||||
<div class="pc-collapse-main" v-if="menuCurrent.btns && menuCurrent.btns.length > 0">
|
||||
<div class="pccm-item">
|
||||
<div class="menu-form-alert">配置操作功能接口权限,配置数据权限点击小齿轮</div>
|
||||
<el-checkbox v-for="(btn, bIndex) in menuCurrent.btns" :key="bIndex" v-model="btn.isCheck" :label="btn.value">
|
||||
<div class="btn-item">
|
||||
{{ btn.data_range !== null ? `${btn.name}(${formatDataRange(btn.data_range)})` : btn.name }}
|
||||
<span v-show="btn.isCheck" @click.stop.prevent="handleSettingClick(menuCurrent, btn)">
|
||||
<el-icon>
|
||||
<Setting />
|
||||
</el-icon>
|
||||
</span>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="pccm-item" v-if="menuCurrent.columns && menuCurrent.columns.length > 0">
|
||||
<div class="menu-form-alert">配置数据列字段权限</div>
|
||||
<ul class="columns-list">
|
||||
<li class="columns-head">
|
||||
<div class="width-txt">
|
||||
<span>字段</span>
|
||||
</div>
|
||||
<div v-for="(head, hIndex) in column.header" :key="hIndex" class="width-check">
|
||||
<el-checkbox :label="head.value" @change="handleColumnChange($event, menuCurrent, head.value, head.disabled)">
|
||||
<span>{{ head.label }}</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li v-for="(c_item, c_index) in menuCurrent.columns" :key="c_index" class="columns-item">
|
||||
<div class="width-txt">{{ c_item.title }}</div>
|
||||
<div v-for="(col, cIndex) in column.header" :key="cIndex" class="width-check">
|
||||
<el-checkbox v-model="c_item[col.value]" class="ci-checkout" :disabled="c_item[col.disabled]"></el-checkbox>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-dialog v-model="dialogVisible" title="数据权限配置" width="400px" :close-on-click-modal="false" :before-close="handleDialogClose">
|
||||
<div class="pc-dialog">
|
||||
<el-select v-model="dataPermission" @change="handlePermissionRangeChange" class="dialog-select" placeholder="请选择">
|
||||
<el-option v-for="item in dataPermissionRange" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-tree-select
|
||||
v-show="dataPermission === 4"
|
||||
node-key="id"
|
||||
v-model="customDataPermission"
|
||||
:props="defaultTreeProps"
|
||||
:data="deptData"
|
||||
multiple
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
show-checkbox
|
||||
class="dialog-tree"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button type="primary" @click="handleDialogConfirm"> 确定</el-button>
|
||||
<el-button @click="handleDialogClose"> 取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, defineProps, watch, computed, reactive } from 'vue';
|
||||
import XEUtils from 'xe-utils';
|
||||
import { errorNotification } from '/@/utils/message';
|
||||
import { getDataPermissionRange, getDataPermissionDept, getRolePermission, setRolePremission, setBtnDatarange } from './api';
|
||||
import { MenuDataType, DataPermissionRangeType, CustomDataPermissionDeptType } from './types';
|
||||
import { ElMessage, ElTree } from 'element-plus';
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
roleId: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
roleName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
drawerVisible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['update:drawerVisible']);
|
||||
|
||||
const drawerVisibleNew = ref(false);
|
||||
watch(
|
||||
() => props.drawerVisible,
|
||||
(val) => {
|
||||
drawerVisibleNew .value = val;
|
||||
getMenuBtnPermission();
|
||||
getDataPermissionRangeLable();
|
||||
menuCurrent.value = {};
|
||||
}
|
||||
);
|
||||
const handleDrawerClose = () => {
|
||||
emit('update:drawerVisible', false);
|
||||
};
|
||||
|
||||
const defaultTreeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
};
|
||||
|
||||
let menuData = ref<MenuDataType[]>([]); // 菜单列表数据
|
||||
let menuDefaultCheckedKeys = ref<number[]>([]); // 默认选中的菜单列表
|
||||
let menuCurrent = ref<Partial<MenuDataType>>({}); // 当前选中的菜单
|
||||
|
||||
let menuBtnCurrent = ref<number>(-1);
|
||||
let dialogVisible = ref(false);
|
||||
let dataPermissionRange = ref<DataPermissionRangeType[]>([]);
|
||||
let dataPermissionRangeLabel = ref<DataPermissionRangeType[]>([]);
|
||||
|
||||
const formatDataRange = computed(() => {
|
||||
return function (datarange: number) {
|
||||
const findItem = dataPermissionRangeLabel.value.find((i) => i.value === datarange);
|
||||
return findItem?.label || '';
|
||||
};
|
||||
});
|
||||
let deptData = ref<CustomDataPermissionDeptType[]>([]);
|
||||
let dataPermission = ref();
|
||||
let customDataPermission = ref([]);
|
||||
|
||||
/**
|
||||
* 菜单复选框选中
|
||||
* @param node
|
||||
* @param data
|
||||
*/
|
||||
const handleMenuCheck = (node: any, data: any) => {
|
||||
XEUtils.eachTree(menuData.value, (item) => {
|
||||
item.isCheck = data.checkedKeys.includes(item.id);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 菜单点击
|
||||
* @param node
|
||||
* @param data
|
||||
*/
|
||||
const handleMenuClick = (selectNode: MenuDataType) => {
|
||||
menuCurrent.value = selectNode;
|
||||
};
|
||||
//获取菜单,按钮,权限
|
||||
const getMenuBtnPermission = async () => {
|
||||
const resMenu = await getRolePermission({ role: props.roleId });
|
||||
menuData.value = resMenu;
|
||||
menuDefaultCheckedKeys.value = XEUtils.toTreeArray(resMenu)
|
||||
.filter((i) => i.isCheck)
|
||||
.map((i) => i.id);
|
||||
};
|
||||
// 获取按钮的数据权限下拉选项
|
||||
const getDataPermissionRangeLable = async () => {
|
||||
const resRange = await getDataPermissionRange({ role: props.roleId });
|
||||
dataPermissionRangeLabel.value = resRange.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取按钮数据权限下拉选项
|
||||
* @param btnId 按钮id
|
||||
*/
|
||||
const fetchData = async (btnId: number) => {
|
||||
try {
|
||||
const resRange = await getDataPermissionRange({ menu_button: btnId });
|
||||
if (resRange?.code === 2000) {
|
||||
dataPermissionRange.value = resRange.data;
|
||||
}
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 设置按钮数据权限
|
||||
* @param record 当前菜单
|
||||
* @param btnType 按钮类型
|
||||
*/
|
||||
const handleSettingClick = (record: any, btn: MenuDataType['btns'][number]) => {
|
||||
menuCurrent.value = record;
|
||||
menuBtnCurrent.value = btn.id;
|
||||
dialogVisible.value = true;
|
||||
dataPermission.value = btn.data_range;
|
||||
handlePermissionRangeChange(btn.data_range);
|
||||
fetchData(btn.id);
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置列权限
|
||||
* @param val 是否选中
|
||||
* @param record 当前菜单
|
||||
* @param btnType 按钮类型
|
||||
* @param disabledType 禁用类型
|
||||
*/
|
||||
const handleColumnChange = (val: boolean, record: any, btnType: string, disabledType: string) => {
|
||||
for (const iterator of record.columns) {
|
||||
iterator[btnType] = iterator[disabledType] ? iterator[btnType] : val;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 数据权限设置
|
||||
*/
|
||||
const handlePermissionRangeChange = async (val: number) => {
|
||||
if (val === 4) {
|
||||
const res = await getDataPermissionDept({ role: props.roleId, menu_button: menuBtnCurrent.value });
|
||||
const depts = XEUtils.toArrayTree(res.data, { parentKey: 'parent', strict: false });
|
||||
deptData.value = depts;
|
||||
const btnObj = XEUtils.find(menuCurrent.value.btns, (item) => item.id === menuBtnCurrent.value);
|
||||
customDataPermission.value = btnObj.dept;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 数据权限设置确认
|
||||
*/
|
||||
const handleDialogConfirm = () => {
|
||||
if (dataPermission.value !== 0 && !dataPermission.value) {
|
||||
errorNotification('请选择');
|
||||
return;
|
||||
}
|
||||
for (const btn of menuCurrent.value?.btns || []) {
|
||||
if (btn.id === menuBtnCurrent.value) {
|
||||
const findItem = dataPermissionRange.value.find((i) => i.value === dataPermission.value);
|
||||
btn.data_range = findItem?.value || 0;
|
||||
if (btn.data_range === 4) {
|
||||
btn.dept = customDataPermission.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
handleDialogClose();
|
||||
};
|
||||
const handleDialogClose = () => {
|
||||
dialogVisible.value = false;
|
||||
customDataPermission.value = [];
|
||||
dataPermission.value = null;
|
||||
};
|
||||
|
||||
//保存菜单授权
|
||||
const handleSavePermission = () => {
|
||||
setRolePremission(props.roleId, XEUtils.toTreeArray(menuData.value)).then((res: any) => {
|
||||
ElMessage({
|
||||
message: res.msg,
|
||||
type: 'success',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const column = reactive({
|
||||
header: [
|
||||
{ value: 'is_create', label: '新增可见', disabled: 'disabled_create' },
|
||||
{ value: 'is_update', label: '编辑可见', disabled: 'disabled_update' },
|
||||
{ value: 'is_query', label: '列表可见', disabled: 'disabled_query' },
|
||||
],
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.permission-com {
|
||||
margin: 15px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.pc-save-btn {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.pc-collapse-title {
|
||||
line-height: 32px;
|
||||
text-align: left;
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.pc-collapse-main {
|
||||
box-sizing: border-box;
|
||||
|
||||
.pccm-item {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.menu-form-alert {
|
||||
color: #fff;
|
||||
line-height: 24px;
|
||||
padding: 8px 16px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.columns-list {
|
||||
.width-txt {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.width-check {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.width-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.columns-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
box-sizing: border-box;
|
||||
|
||||
span {
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
|
||||
.columns-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.ci-checkout {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pc-dialog {
|
||||
.dialog-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dialog-tree {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.permission-com {
|
||||
.el-collapse {
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.el-collapse-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.el-collapse-item__header {
|
||||
height: auto;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
border-left: 1px solid #ebeef5;
|
||||
border-right: 1px solid #ebeef5;
|
||||
box-sizing: border-box;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.el-collapse-item__header.is-active {
|
||||
border-radius: 8px 8px 0 0;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.el-collapse-item__wrap {
|
||||
padding: 15px;
|
||||
border-left: 1px solid #ebeef5;
|
||||
border-right: 1px solid #ebeef5;
|
||||
border-top: 1px solid #ebeef5;
|
||||
border-radius: 0 0 8px 8px;
|
||||
background-color: #fafafa;
|
||||
box-sizing: border-box;
|
||||
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,29 +0,0 @@
|
||||
export interface DataPermissionRangeType {
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface CustomDataPermissionDeptType {
|
||||
id: number;
|
||||
name: string;
|
||||
patent: number;
|
||||
children: CustomDataPermissionDeptType[];
|
||||
}
|
||||
|
||||
export interface CustomDataPermissionMenuType {
|
||||
id: number;
|
||||
name: string;
|
||||
is_catalog: boolean;
|
||||
menuPermission: { id: number; name: string; value: string }[] | null;
|
||||
columns: { id: number; name: string; title: string }[] | null;
|
||||
children: CustomDataPermissionMenuType[];
|
||||
}
|
||||
|
||||
export interface MenuDataType {
|
||||
id: string;
|
||||
name: string;
|
||||
isCheck: boolean;
|
||||
btns: { id: number; name: string; value: string; isCheck: boolean; data_range: number; dept: object }[];
|
||||
columns: { [key: string]: boolean | string; }[];
|
||||
children: MenuDataType[];
|
||||
}
|
||||
58
web/src/views/system/role/components/RoleDrawer.vue
Normal file
58
web/src/views/system/role/components/RoleDrawer.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="RoleDrawer.drawerVisible"
|
||||
title="权限配置"
|
||||
direction="rtl"
|
||||
size="80%"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="RoleDrawer.handleDrawerClose"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<template #header>
|
||||
<div>
|
||||
当前授权角色:
|
||||
<el-tag>{{ RoleDrawer.roleName }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<splitpanes class="default-theme" style="height: 100%">
|
||||
<pane min-size="20" size="22">
|
||||
<div class="pane-box">
|
||||
<MenuTree />
|
||||
</div>
|
||||
</pane>
|
||||
<pane min-size="20">
|
||||
<div class="pane-box">
|
||||
<el-tabs v-model="activeName" class="demo-tabs">
|
||||
<el-tab-pane label="接口权限" name="first"><MenuBtn /></el-tab-pane>
|
||||
<el-tab-pane label="列字段权限" name="second"><MenuField /></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Splitpanes, Pane } from 'splitpanes';
|
||||
import 'splitpanes/dist/splitpanes.css';
|
||||
import { RoleDrawerStores } from '../stores/RoleDrawerStores';
|
||||
import { defineAsyncComponent, ref } from 'vue';
|
||||
|
||||
const MenuTree = defineAsyncComponent(() => import('./RoleMenuTree.vue'));
|
||||
const MenuBtn = defineAsyncComponent(() => import('./RoleMenuBtn.vue'));
|
||||
const MenuField = defineAsyncComponent(() => import('./RoleMenuField.vue'));
|
||||
const RoleDrawer = RoleDrawerStores(); // 抽屉参数
|
||||
const activeName = ref('first');
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pane-box {
|
||||
width: 100vw; /* 视口宽度 */
|
||||
height: 100vh; /* 视口高度 */
|
||||
max-width: 100%; /* 确保不超过父元素的宽度 */
|
||||
max-height: 100%; /* 确保不超过父元素的高度 */
|
||||
overflow: auto; /* 当内容超出容器尺寸时显示滚动条 */
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
186
web/src/views/system/role/components/RoleMenuBtn.vue
Normal file
186
web/src/views/system/role/components/RoleMenuBtn.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div class="pccm-item" v-if="RoleMenuBtn.$state.length > 0">
|
||||
<div class="menu-form-alert">配置操作功能接口权限,配置数据权限点击小齿轮</div>
|
||||
<el-checkbox v-for="btn in RoleMenuBtn.$state" :key="btn.id" v-model="btn.isCheck" @change="handleCheckChange(btn)">
|
||||
<div class="btn-item">
|
||||
{{ btn.data_range !== null ? `${btn.name}(${formatDataRange(btn.data_range)})` : btn.name }}
|
||||
<span v-show="btn.isCheck" @click.stop.prevent="handleSettingClick(btn)">
|
||||
<el-icon>
|
||||
<Setting />
|
||||
</el-icon>
|
||||
</span>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<el-dialog v-model="dialogVisible" title="数据权限配置" width="400px" :close-on-click-modal="false" :before-close="handleDialogClose">
|
||||
<div class="pc-dialog">
|
||||
<el-select v-model="selectBtn.data_range" @change="handlePermissionRangeChange" placeholder="请选择">
|
||||
<el-option v-for="item in dataPermissionRange" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-tree-select
|
||||
v-show="selectBtn.data_range === 4"
|
||||
node-key="id"
|
||||
v-model="selectBtn.dept"
|
||||
:props="defaultTreeProps"
|
||||
:data="deptData"
|
||||
multiple
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
show-checkbox
|
||||
class="dialog-tree"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button type="primary" @click="handleDialogConfirm"> 确定</el-button>
|
||||
<el-button @click="handleDialogClose"> 取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { RoleDrawerStores } from '../stores/RoleDrawerStores';
|
||||
import { RoleMenuBtnStores } from '../stores/RoleMenuBtnStores';
|
||||
import { RoleMenuTreeStores } from '../stores/RoleMenuTreeStores';
|
||||
import { RoleMenuBtnType } from '../types';
|
||||
import { getRoleToDeptAll, setRoleMenuBtn, setRoleMenuBtnDataRange } from './api';
|
||||
import XEUtils from 'xe-utils';
|
||||
import { ElMessage } from 'element-plus';
|
||||
const RoleDrawer = RoleDrawerStores(); // 角色-菜单
|
||||
const RoleMenuTree = RoleMenuTreeStores(); // 角色-菜单
|
||||
const RoleMenuBtn = RoleMenuBtnStores(); // 角色-菜单-按钮
|
||||
const dialogVisible = ref(false);
|
||||
// 选中的按钮
|
||||
const selectBtn = ref<RoleMenuBtnType>({
|
||||
id: 0,
|
||||
menu_btn_pre_id: 0,
|
||||
/** 是否选中 */
|
||||
isCheck: false,
|
||||
/** 按钮名称 */
|
||||
name: '',
|
||||
/** 数据权限范围 */
|
||||
data_range: 0,
|
||||
dept: [],
|
||||
});
|
||||
/**
|
||||
* 数据权限范围
|
||||
*/
|
||||
const dataPermissionRange = ref([
|
||||
{ label: '仅本人数据权限', value: 0 },
|
||||
{ label: '本部门及以下数据权限', value: 1 },
|
||||
{ label: '本部门数据权限', value: 2 },
|
||||
{ label: '全部数据权限', value: 3 },
|
||||
{ label: '自定数据权限', value: 4 },
|
||||
]);
|
||||
/**
|
||||
* 自定义数据权限的部门树配置
|
||||
*/
|
||||
const defaultTreeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
};
|
||||
|
||||
/**
|
||||
* 自定数据权限下拉选择事件
|
||||
*/
|
||||
const handlePermissionRangeChange = async (val: number) => {
|
||||
if (val < 4) {
|
||||
selectBtn.value.dept = [];
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 格式化按钮数据范围
|
||||
*/
|
||||
const formatDataRange = computed(() => {
|
||||
return function (datarange: number) {
|
||||
const datarangeitem = XEUtils.find(dataPermissionRange.value, (item: any) => {
|
||||
if (item.value === datarange) {
|
||||
return item.label;
|
||||
}
|
||||
});
|
||||
return datarangeitem.label;
|
||||
};
|
||||
});
|
||||
/**
|
||||
* 勾选按钮
|
||||
*/
|
||||
const handleCheckChange = async (btn: RoleMenuBtnType) => {
|
||||
const put_data = {
|
||||
isCheck: btn.isCheck,
|
||||
roleId: RoleDrawer.roleId,
|
||||
menuId: RoleMenuTree.id,
|
||||
btnId: btn.id,
|
||||
};
|
||||
const { data, msg } = await setRoleMenuBtn(put_data);
|
||||
RoleMenuBtn.updateState(data);
|
||||
ElMessage({ message: msg, type: 'success' });
|
||||
};
|
||||
|
||||
/**
|
||||
* 按钮-数据范围确定
|
||||
*/
|
||||
const handleDialogConfirm = async () => {
|
||||
const { data, msg } = await setRoleMenuBtnDataRange(selectBtn.value);
|
||||
selectBtn.value = data;
|
||||
dialogVisible.value = false;
|
||||
ElMessage({ message: msg, type: 'success' });
|
||||
};
|
||||
/**
|
||||
* 数据范围关闭
|
||||
*/
|
||||
const handleDialogClose = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 齿轮点击
|
||||
*/
|
||||
const handleSettingClick = async (btn: RoleMenuBtnType) => {
|
||||
selectBtn.value = btn;
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 部门数据
|
||||
*
|
||||
*/
|
||||
const deptData = ref<number[]>([]);
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(async () => {
|
||||
const res = await getRoleToDeptAll({ role: RoleDrawer.roleId, menu_button: selectBtn.value.id });
|
||||
const depts = XEUtils.toArrayTree(res.data, { parentKey: 'parent', strict: false });
|
||||
deptData.value = depts;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pccm-item {
|
||||
margin-bottom: 10px;
|
||||
.menu-form-alert {
|
||||
color: #fff;
|
||||
line-height: 24px;
|
||||
padding: 8px 16px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
// .el-checkbox {
|
||||
// width: 200px;
|
||||
// }
|
||||
.btn-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center; /* 水平居中 */
|
||||
.el-icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.dialog-tree {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
118
web/src/views/system/role/components/RoleMenuField.vue
Normal file
118
web/src/views/system/role/components/RoleMenuField.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="pccm-item" v-if="RoleMenuField.$state.length > 0">
|
||||
<div class="menu-form-alert">
|
||||
<el-button size="small" @click="handleSaveField">保存 </el-button>
|
||||
配置数据列字段权限
|
||||
</div>
|
||||
|
||||
<ul class="columns-list">
|
||||
<li class="columns-head">
|
||||
<div class="width-txt">
|
||||
<span>字段</span>
|
||||
</div>
|
||||
<div v-for="(head, hIndex) in RoleMenuFieldHeader.$state" :key="hIndex" class="width-check">
|
||||
<el-checkbox v-model="head.checked" @change="handleColumnChange($event, head.value, head.disabled)">
|
||||
<span>{{ head.label }}</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</li>
|
||||
<div class="columns-content">
|
||||
<li v-for="(c_item, c_index) in RoleMenuField.$state" :key="c_index" class="columns-item">
|
||||
<div class="width-txt">{{ c_item.title }}</div>
|
||||
<div v-for="(col, cIndex) in RoleMenuFieldHeader.$state" :key="cIndex" class="width-check">
|
||||
<el-checkbox v-model="c_item[col.value]" class="ci-checkout" :disabled="c_item[col.disabled]"></el-checkbox>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { RoleDrawerStores } from '../stores/RoleDrawerStores';
|
||||
import { RoleMenuFieldStores, RoleMenuFieldHeaderStores } from '../stores/RoleMenuFieldStores';
|
||||
import { setRoleMenuField } from './api';
|
||||
const RoleMenuField = RoleMenuFieldStores();
|
||||
const RoleMenuFieldHeader = RoleMenuFieldHeaderStores();
|
||||
const RoleDrawer = RoleDrawerStores(); // 角色-抽屉
|
||||
/** 全选 */
|
||||
const handleColumnChange = (val: boolean, btnType: string, disabledType: string) => {
|
||||
for (const iterator of RoleMenuField.$state) {
|
||||
iterator[btnType] = iterator[disabledType] ? iterator[btnType] : val;
|
||||
}
|
||||
};
|
||||
const handleSaveField = async () => {
|
||||
const res = await setRoleMenuField(RoleDrawer.$state.roleId, RoleMenuField.$state);
|
||||
ElMessage({ message: res.msg, type: 'success' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pccm-item {
|
||||
margin-bottom: 10px;
|
||||
.menu-form-alert {
|
||||
color: #fff;
|
||||
line-height: 24px;
|
||||
padding: 8px 16px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
.menu-form-btn {
|
||||
margin-left: 10px;
|
||||
height: 40px;
|
||||
padding: 8px 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.columns-list {
|
||||
.width-txt {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.width-check {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.width-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.columns-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
box-sizing: border-box;
|
||||
|
||||
span {
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
.columns-content {
|
||||
max-height: calc(100vh - 240px); /* 视口高度 */
|
||||
overflow-y: auto; /* 当内容超出高度时显示垂直滚动条 */
|
||||
.columns-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.ci-checkout {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
81
web/src/views/system/role/components/RoleMenuTree.vue
Normal file
81
web/src/views/system/role/components/RoleMenuTree.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="menuData"
|
||||
:props="defaultTreeProps"
|
||||
:default-checked-keys="menuDefaultCheckedKeys"
|
||||
@check-change="handleMenuChange"
|
||||
@node-click="handleMenuClick"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
highlight-current
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
>
|
||||
</el-tree>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { RoleDrawerStores } from '../stores/RoleDrawerStores';
|
||||
import { RoleMenuTreeStores } from '../stores/RoleMenuTreeStores';
|
||||
import { RoleMenuBtnStores } from '../stores/RoleMenuBtnStores';
|
||||
import { RoleMenuFieldStores } from '../stores/RoleMenuFieldStores';
|
||||
import { RoleMenuFieldHeaderStores } from '../stores/RoleMenuFieldStores';
|
||||
import { getRoleMenu, getRoleMenuBtnField, setRoleMenu } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import XEUtils from 'xe-utils';
|
||||
import { RoleMenuTreeType } from '../types';
|
||||
|
||||
const RoleDrawer = RoleDrawerStores(); // 角色-抽屉
|
||||
const RoleMenuTree = RoleMenuTreeStores(); // 角色-菜单
|
||||
const RoleMenuBtn = RoleMenuBtnStores(); // 角色-菜单-按钮
|
||||
const RoleMenuField = RoleMenuFieldStores(); // 角色-菜单-列字段
|
||||
const RoleMenuFieldHeader = RoleMenuFieldHeaderStores();// 角色-菜单-列字段
|
||||
const menuData = ref<RoleMenuTreeType[]>([]); // 菜单列表数据
|
||||
const menuDefaultCheckedKeys = ref<(number | string | undefined)[]>([]); // 默认选中的菜单列表
|
||||
// 菜单配置
|
||||
const defaultTreeProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
};
|
||||
|
||||
/**
|
||||
* 菜单复选框选中
|
||||
* @param node:当前节点的 Node 对象
|
||||
* @param checked:布尔值,表示当前节点是否被选中
|
||||
*/
|
||||
const handleMenuChange = (node: any, checked: boolean) => {
|
||||
setRoleMenu({ roleId: RoleDrawer.roleId, menuId: node.id, isCheck: checked }).then((res: any) => {
|
||||
ElMessage({ message: res.msg, type: 'success' });
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 菜单点击事件
|
||||
*/
|
||||
const handleMenuClick = async (selectNode: RoleMenuTreeType) => {
|
||||
if (!selectNode.is_catalog) {
|
||||
RoleMenuTree.setRoleMenuTree(selectNode); // 更新当前选中的菜单
|
||||
// 获取当前菜单的按钮列表
|
||||
const { data } = await getRoleMenuBtnField({
|
||||
roleId: RoleDrawer.roleId,
|
||||
menuId: selectNode.id,
|
||||
});
|
||||
RoleMenuBtn.setState(data.menu_btn); // 更新按钮列表
|
||||
RoleMenuField.setState(data.menu_field); // 更新列字段列表
|
||||
} else {
|
||||
RoleMenuBtn.setState([]); // 更新按钮列表
|
||||
RoleMenuField.setState([]); // 更新列字段列表
|
||||
}
|
||||
RoleMenuFieldHeader.$reset()
|
||||
};
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(async () => {
|
||||
menuData.value = await getRoleMenu({ roleId: RoleDrawer.roleId });
|
||||
menuDefaultCheckedKeys.value = XEUtils.toTreeArray(menuData.value)
|
||||
.filter((i) => i.isCheck)
|
||||
.map((i) => i.id);
|
||||
});
|
||||
</script>
|
||||
88
web/src/views/system/role/components/api.ts
Normal file
88
web/src/views/system/role/components/api.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import XEUtils from 'xe-utils';
|
||||
/**
|
||||
* 获取 角色-菜单
|
||||
* @param query
|
||||
*/
|
||||
export function getRoleMenu(query: object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/get_role_menu/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
}).then((res: any) => {
|
||||
return XEUtils.toArrayTree(res.data, { key: 'id', parentKey: 'parent', children: 'children', strict: false });
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 设置 角色-菜单
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function setRoleMenu(data: object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/set_role_menu/',
|
||||
method: 'put',
|
||||
data,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 获取 角色-菜单-按钮-列字段
|
||||
* @param query
|
||||
*/
|
||||
export function getRoleMenuBtnField(query: object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/get_role_menu_btn_field/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 角色-菜单-按钮
|
||||
* @param data
|
||||
*/
|
||||
export function setRoleMenuBtn(data: object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/set_role_menu_btn/',
|
||||
method: 'put',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 角色-菜单-列字段
|
||||
* @param data
|
||||
*/
|
||||
export function setRoleMenuField(roleId: string | number | undefined, data: object) {
|
||||
return request({
|
||||
url: `/api/system/role_menu_button_permission/${roleId}/set_role_menu_field/`,
|
||||
method: 'put',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 角色-菜单-按钮-数据权限
|
||||
* @param query
|
||||
* @returns
|
||||
*/
|
||||
export function setRoleMenuBtnDataRange(data: object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/set_role_menu_btn_data_range/',
|
||||
method: 'put',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户角色下所能授权的部门
|
||||
* @param query
|
||||
* @returns
|
||||
*/
|
||||
export function getRoleToDeptAll(query: object) {
|
||||
return request({
|
||||
url: '/api/system/role_menu_button_permission/role_to_dept_all/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
@@ -1,189 +1,171 @@
|
||||
import {CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose, compute} from '@fast-crud/fast-crud';
|
||||
import { CreateCrudOptionsProps, CreateCrudOptionsRet, AddReq, DelReq, EditReq, dict, compute } from '@fast-crud/fast-crud';
|
||||
import * as api from './api';
|
||||
import {dictionary} from '/@/utils/dictionary';
|
||||
import {columnPermission} from '../../../utils/columnPermission';
|
||||
import {successMessage} from '../../../utils/message';
|
||||
import {auth} from '/@/utils/authFunction'
|
||||
import { dictionary } from '/@/utils/dictionary';
|
||||
import { successMessage } from '../../../utils/message';
|
||||
import { auth } from '/@/utils/authFunction';
|
||||
|
||||
interface CreateCrudOptionsTypes {
|
||||
output: any;
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param crudExpose:index传递过来的示例
|
||||
* @param context:index传递过来的自定义参数
|
||||
* @returns
|
||||
*/
|
||||
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: any) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
//此处为crudOptions配置
|
||||
export const createCrudOptions = function ({
|
||||
crudExpose,
|
||||
rolePermission,
|
||||
handleDrawerOpen,
|
||||
}: {
|
||||
crudExpose: CrudExpose;
|
||||
rolePermission: any;
|
||||
handleDrawerOpen: Function;
|
||||
}): CreateCrudOptionsTypes {
|
||||
const pageRequest = async (query: any) => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({form, row}: EditReq) => {
|
||||
form.id = row.id;
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({row}: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const addRequest = async ({form}: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
//权限判定
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
pagination: {
|
||||
show: true
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: {
|
||||
show: auth('role:Create')
|
||||
}
|
||||
}
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 320,
|
||||
buttons: {
|
||||
view: {
|
||||
show: true,
|
||||
},
|
||||
edit: {
|
||||
show: auth('role:Update'),
|
||||
},
|
||||
remove: {
|
||||
show: auth('role:Delete'),
|
||||
},
|
||||
permission: {
|
||||
type: 'primary',
|
||||
text: '权限配置',
|
||||
show: auth('role:Permission'),
|
||||
tooltip: {
|
||||
placement: 'top',
|
||||
content: '权限配置',
|
||||
},
|
||||
click: (context: any): void => {
|
||||
const {row} = context;
|
||||
handleDrawerOpen(row);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
col: {span: 24},
|
||||
labelWidth: '100px',
|
||||
wrapper: {
|
||||
is: 'el-dialog',
|
||||
width: '600px',
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: {show: false},
|
||||
column: {
|
||||
type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
},
|
||||
},
|
||||
id: {
|
||||
title: 'ID',
|
||||
type: 'text',
|
||||
column: {show: false},
|
||||
search: {show: false},
|
||||
form: {show: false},
|
||||
},
|
||||
name: {
|
||||
title: '角色名称',
|
||||
type: 'text',
|
||||
search: {show: true},
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '角色名称必填'}],
|
||||
component: {
|
||||
placeholder: '请输入角色名称',
|
||||
},
|
||||
},
|
||||
},
|
||||
key: {
|
||||
title: '权限标识',
|
||||
type: 'text',
|
||||
search: {show: false},
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: 'custom',
|
||||
columnSetDisabled: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '权限标识必填'}],
|
||||
component: {
|
||||
placeholder: '输入权限标识',
|
||||
},
|
||||
},
|
||||
valueBuilder(context) {
|
||||
const {row, key} = context
|
||||
return row[key]
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
title: '排序',
|
||||
search: {show: false},
|
||||
type: 'number',
|
||||
column: {
|
||||
minWidth: 90,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{required: true, message: '排序必填'}],
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
status: {
|
||||
title: '状态',
|
||||
search: {show: true},
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
width: 100,
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: dictionary('button_status_bool'),
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
pagination: {
|
||||
show: true,
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: {
|
||||
show: auth('role:Create'),
|
||||
},
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
//固定右侧
|
||||
fixed: 'right',
|
||||
width: 320,
|
||||
buttons: {
|
||||
view: {
|
||||
show: true,
|
||||
},
|
||||
edit: {
|
||||
show: auth('role:Update'),
|
||||
},
|
||||
remove: {
|
||||
show: auth('role:Delete'),
|
||||
},
|
||||
permission: {
|
||||
type: 'primary',
|
||||
text: '权限配置',
|
||||
show: auth('role:Permission'),
|
||||
click: (clickContext: any): void => {
|
||||
const { row } = clickContext;
|
||||
context.RoleDrawer.handleDrawerOpen(row);
|
||||
context.RoleMenuBtn.setState([]);
|
||||
context.RoleMenuField.setState([]);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
labelWidth: '100px',
|
||||
wrapper: {
|
||||
is: 'el-dialog',
|
||||
width: '600px',
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
_index: {
|
||||
title: '序号',
|
||||
form: { show: false },
|
||||
column: {
|
||||
type: 'index',
|
||||
align: 'center',
|
||||
width: '70px',
|
||||
columnSetDisabled: true, //禁止在列设置中选择
|
||||
},
|
||||
},
|
||||
id: {
|
||||
title: 'ID',
|
||||
column: { show: false },
|
||||
search: { show: false },
|
||||
form: { show: false },
|
||||
},
|
||||
name: {
|
||||
title: '角色名称',
|
||||
search: { show: true },
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '角色名称必填' }],
|
||||
component: {
|
||||
placeholder: '请输入角色名称',
|
||||
},
|
||||
},
|
||||
},
|
||||
key: {
|
||||
title: '权限标识',
|
||||
search: { show: false },
|
||||
column: {
|
||||
minWidth: 120,
|
||||
sortable: 'custom',
|
||||
columnSetDisabled: true,
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '权限标识必填' }],
|
||||
component: {
|
||||
placeholder: '输入权限标识',
|
||||
},
|
||||
},
|
||||
valueBuilder(context) {
|
||||
const { row, key } = context;
|
||||
return row[key];
|
||||
},
|
||||
},
|
||||
sort: {
|
||||
title: '排序',
|
||||
search: { show: false },
|
||||
type: 'number',
|
||||
column: {
|
||||
minWidth: 90,
|
||||
sortable: 'custom',
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: '排序必填' }],
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
status: {
|
||||
title: '状态',
|
||||
search: { show: true },
|
||||
type: 'dict-radio',
|
||||
column: {
|
||||
width: 100,
|
||||
component: {
|
||||
name: 'fs-dict-switch',
|
||||
activeText: '',
|
||||
inactiveText: '',
|
||||
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
|
||||
onChange: compute((context) => {
|
||||
return () => {
|
||||
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||
successMessage(res.msg as string);
|
||||
});
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
value: dictionary('button_status_bool'),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,69 +1,31 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #cell_url="scope">
|
||||
<el-tag size="small">{{ scope.row.url }}</el-tag>
|
||||
</template>
|
||||
</fs-crud>
|
||||
|
||||
<permission ref="rolePermission"></permission>
|
||||
|
||||
<PermissionComNew v-model:drawerVisible="drawerVisible" :roleId="roleId" :roleName="roleName" @drawerClose="handleDrawerClose" />
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
<PermissionDrawerCom ref="PermissionDrawerCom" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="role">
|
||||
import {ref, onMounted, inject, onBeforeUpdate} from 'vue';
|
||||
|
||||
import { GetPermission } from './api';
|
||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
||||
import { defineAsyncComponent, onMounted } from 'vue';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import { createCrudOptions } from './crud';
|
||||
import PermissionComNew from './components/PermissionComNew/index.vue';
|
||||
import _ from "lodash-es";
|
||||
import {handleColumnPermission} from "/@/utils/columnPermission";
|
||||
let drawerVisible = ref(false);
|
||||
let roleId = ref(null);
|
||||
let roleName = ref(null);
|
||||
import { RoleDrawerStores } from './stores/RoleDrawerStores';
|
||||
import { RoleMenuBtnStores } from './stores/RoleMenuBtnStores';
|
||||
import { RoleMenuFieldStores } from './stores/RoleMenuFieldStores';
|
||||
|
||||
const rolePermission = ref();
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
const PermissionDrawerCom = defineAsyncComponent(() => import('./components/RoleDrawer.vue'));
|
||||
|
||||
|
||||
const handleDrawerOpen = (row: any) => {
|
||||
roleId.value = row.id;
|
||||
roleName.value = row.name;
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
||||
|
||||
// 你的crud配置
|
||||
const { crudOptions } = createCrudOptions({ crudExpose, rolePermission, handleDrawerOpen });
|
||||
|
||||
// 初始化crud配置
|
||||
const { resetCrudOptions } = useCrud({
|
||||
crudExpose,
|
||||
crudOptions,
|
||||
context: {},
|
||||
const RoleDrawer = RoleDrawerStores(); // 权限配置抽屉参数
|
||||
const RoleMenuBtn = RoleMenuBtnStores(); // 角色-菜单
|
||||
const RoleMenuField = RoleMenuFieldStores();
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({
|
||||
createCrudOptions,
|
||||
context: { RoleDrawer, RoleMenuBtn,RoleMenuField },
|
||||
});
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted( async () => {
|
||||
|
||||
const newOptions = await handleColumnPermission(GetPermission,crudOptions)
|
||||
|
||||
|
||||
//重置crudBinding
|
||||
//resetCrudOptions(newOptions);
|
||||
onMounted(async () => {
|
||||
// 刷新
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
defineExpose(rolePermission);
|
||||
</script>
|
||||
|
||||
32
web/src/views/system/role/stores/RoleDrawerStores.ts
Normal file
32
web/src/views/system/role/stores/RoleDrawerStores.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { RoleDrawerType } from '../types';
|
||||
/**
|
||||
* 权限配置:抽屉
|
||||
*/
|
||||
const initialState: RoleDrawerType = {
|
||||
drawerVisible: false,
|
||||
roleId: undefined,
|
||||
roleName: undefined,
|
||||
};
|
||||
|
||||
export const RoleDrawerStores = defineStore('RoleDrawerStores', {
|
||||
state: (): RoleDrawerType => ({
|
||||
...initialState,
|
||||
}),
|
||||
actions: {
|
||||
/**
|
||||
* 打开权限修改抽屉
|
||||
*/
|
||||
handleDrawerOpen(row: any) {
|
||||
this.drawerVisible = true;
|
||||
this.roleName = row.name;
|
||||
this.roleId = row.id;
|
||||
},
|
||||
/**
|
||||
* 关闭权限修改抽屉
|
||||
*/
|
||||
handleDrawerClose() {
|
||||
Object.assign(this.$state, initialState);
|
||||
},
|
||||
},
|
||||
});
|
||||
24
web/src/views/system/role/stores/RoleMenuBtnStores.ts
Normal file
24
web/src/views/system/role/stores/RoleMenuBtnStores.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { RoleMenuBtnType } from '../types';
|
||||
/**
|
||||
* 权限配置:角色-菜单-按钮
|
||||
*/
|
||||
|
||||
export const RoleMenuBtnStores = defineStore('RoleMenuBtnStores', {
|
||||
state: (): RoleMenuBtnType[] => [],
|
||||
actions: {
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
setState(data: RoleMenuBtnType[]) {
|
||||
this.$state = data;
|
||||
this.$state.length = data.length;
|
||||
},
|
||||
updateState(data: RoleMenuBtnType) {
|
||||
const index = this.$state.findIndex((item) => item.id === data.id);
|
||||
if (index !== -1) {
|
||||
this.$state[index] = data;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
24
web/src/views/system/role/stores/RoleMenuFieldStores.ts
Normal file
24
web/src/views/system/role/stores/RoleMenuFieldStores.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { RoleMenuFieldType, RoleMenuFieldHeaderType } from '../types';
|
||||
/**
|
||||
* 权限配置:角色-菜单-列字段
|
||||
*/
|
||||
|
||||
export const RoleMenuFieldStores = defineStore('RoleMenuFieldStores', {
|
||||
state: (): RoleMenuFieldType[] => [],
|
||||
actions: {
|
||||
/** 重置 */
|
||||
setState(data: RoleMenuFieldType[]) {
|
||||
this.$state = data;
|
||||
this.$state.length = data.length;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const RoleMenuFieldHeaderStores = defineStore('RoleMenuFieldHeaderStores', {
|
||||
state: (): RoleMenuFieldHeaderType[] => [
|
||||
{ value: 'is_create', label: '新增可见', disabled: 'disabled_create', checked: false },
|
||||
{ value: 'is_update', label: '编辑可见', disabled: 'disabled_update', checked: false },
|
||||
{ value: 'is_query', label: '列表可见', disabled: 'disabled_query', checked: false },
|
||||
],
|
||||
});
|
||||
21
web/src/views/system/role/stores/RoleMenuTreeStores.ts
Normal file
21
web/src/views/system/role/stores/RoleMenuTreeStores.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { RoleMenuTreeType } from '../types';
|
||||
/**
|
||||
* 权限抽屉:角色-菜单
|
||||
*/
|
||||
|
||||
export const RoleMenuTreeStores = defineStore('RoleMenuTreeStores', {
|
||||
state: (): RoleMenuTreeType => ({
|
||||
id: 0,
|
||||
parent: 0,
|
||||
name: '',
|
||||
isCheck: false,
|
||||
is_catalog: false,
|
||||
}),
|
||||
actions: {
|
||||
/** 赋值 */
|
||||
setRoleMenuTree(data: RoleMenuTreeType) {
|
||||
this.$state = data;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,27 +1,88 @@
|
||||
import { CrudOptions, CrudExpose } from '@fast-crud/fast-crud';
|
||||
|
||||
export interface CreateCrudReturnTypes {
|
||||
crudOptions: CrudOptions;
|
||||
}
|
||||
|
||||
export interface CreateCrudOptionsTypes {
|
||||
crudExpose: CrudExpose;
|
||||
configPermission: Function;
|
||||
}
|
||||
|
||||
/**角色列表数据类型 */
|
||||
export interface RoleItemType {
|
||||
id: string | number;
|
||||
modifier_name: string;
|
||||
creator_name: string;
|
||||
create_datetime: string;
|
||||
update_datetime: string;
|
||||
description: string;
|
||||
modifier: string;
|
||||
dept_belong_id: string;
|
||||
name: string;
|
||||
key: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
admin: boolean;
|
||||
creator: string;
|
||||
}
|
||||
id: string | number;
|
||||
modifier_name: string;
|
||||
creator_name: string;
|
||||
create_datetime: string;
|
||||
update_datetime: string;
|
||||
description: string;
|
||||
modifier: string;
|
||||
dept_belong_id: string;
|
||||
name: string;
|
||||
key: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
admin: boolean;
|
||||
creator: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限配置 抽屉组件参数数据类型
|
||||
*/
|
||||
export interface RoleDrawerType {
|
||||
/** 是否显示抽屉*/
|
||||
drawerVisible: boolean;
|
||||
/** 角色id*/
|
||||
roleId: string | number | undefined;
|
||||
/** 角色名称*/
|
||||
roleName: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单数据类型
|
||||
*/
|
||||
export interface RoleMenuTreeType {
|
||||
id: number | string | undefined;
|
||||
/** 父级id */
|
||||
parent: number | string | undefined;
|
||||
name: string;
|
||||
/** 是否选中 */
|
||||
isCheck: boolean;
|
||||
/** 是否是目录 */
|
||||
is_catalog: boolean;
|
||||
}
|
||||
/**
|
||||
* 菜单-按钮数据类型
|
||||
*/
|
||||
export interface RoleMenuBtnType {
|
||||
id: string | number;
|
||||
menu_btn_pre_id: string | number;
|
||||
/** 是否选中 */
|
||||
isCheck: boolean;
|
||||
/** 按钮名称 */
|
||||
name: string;
|
||||
/** 数据权限范围 */
|
||||
data_range: number | null;
|
||||
/** 自定义部门 */
|
||||
dept: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单-列字段数据类型
|
||||
*/
|
||||
export interface RoleMenuFieldType {
|
||||
id: string | number | boolean;
|
||||
/** 模型表字段名 */
|
||||
field_name: string;
|
||||
/** 字段显示名 */
|
||||
title: string;
|
||||
/** 是否可查询 */
|
||||
is_query: boolean;
|
||||
/** 是否可创建 */
|
||||
is_create: boolean;
|
||||
/** 是否可更新 */
|
||||
is_update: boolean;
|
||||
[key: string]: string | number | boolean;
|
||||
}
|
||||
/**
|
||||
* 菜单-列字段-标题数据类型
|
||||
*/
|
||||
export interface RoleMenuFieldHeaderType {
|
||||
value: string;
|
||||
/** 模型表字段名 */
|
||||
label: string;
|
||||
/** 字段显示名 */
|
||||
disabled: string ;
|
||||
/** 是否可查询 */
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user