fix 角色授权模式修改,数据权限修改

This commit is contained in:
yxh 2024-04-12 17:14:34 +08:00
parent a7fc7b583d
commit f69356a4b6
16 changed files with 597 additions and 181 deletions

8
package-lock.json generated
View File

@ -19,7 +19,7 @@
"echarts": "^5.4.2", "echarts": "^5.4.2",
"echarts-gl": "^2.0.9", "echarts-gl": "^2.0.9",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "^2.3.3", "element-plus": "^2.6.3",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"jsplumb": "^2.15.6", "jsplumb": "^2.15.6",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@ -2243,9 +2243,9 @@
"peer": true "peer": true
}, },
"node_modules/element-plus": { "node_modules/element-plus": {
"version": "2.4.3", "version": "2.6.3",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.4.3.tgz", "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.6.3.tgz",
"integrity": "sha512-b3q26j+lM4SBqiyzw8HybybGnP2pk4MWgrnzzzYW5qKQUgV6EG1Zg7nMCfgCVccI8tNvZoTiUHb2mFaiB9qT8w==", "integrity": "sha512-U4L/mr+1r+EmAUYUHrs0V/8hHMdBGP07rPymSC72LZCN4jK1UwygQYICegTQ5us4mxeqBvW6wfoEfo003fwCqw==",
"dependencies": { "dependencies": {
"@ctrl/tinycolor": "^3.4.1", "@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",

View File

@ -20,7 +20,7 @@
"echarts": "^5.4.2", "echarts": "^5.4.2",
"echarts-gl": "^2.0.9", "echarts-gl": "^2.0.9",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "^2.3.3", "element-plus": "^2.6.3",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"jsplumb": "^2.15.6", "jsplumb": "^2.15.6",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View File

@ -57,13 +57,20 @@ export function dataScope(data:any) {
}) })
} }
export function setRoleUsers(data:any) {
return request({
url: '/api/v1/system/role/setRoleUser',
method: 'put',
data:data
})
}
// 根据角色ID查询部门树结构 // 根据角色ID查询部门树结构
export function roleDeptTreeSelect(roleId:number) { export function roleDeptTreeSelect() {
return request({ return request({
url: '/api/v1/system/role/deptTreeSelect', url: '/api/v1/system/role/deptTreeSelect',
method: 'get', method: 'get'
params:{roleId}
}) })
} }

View File

@ -19,6 +19,15 @@ export function getUserList(query:Object) {
}) })
} }
export function getUsersById(roleId:number) {
return request({
url: '/api/v1/system/user/getByRoleId',
method: 'get',
params:{roleId}
})
}
export function getUserSelector(query:Object) { export function getUserSelector(query:Object) {
return request({ return request({
url: '/api/v1/system/user/selector', url: '/api/v1/system/user/selector',

View File

@ -1,14 +1,6 @@
<template> <template>
<el-row :gutter="10" style="width: 100%;" v-if="multiple">
<el-col :span="6">
<el-form-item>
<el-button size="default" type="success" class="ml10" @click="confirmUser">确定选取</el-button>
</el-form-item>
</el-col>
</el-row>
<div class="system-user-list-container" style="min-height: 600px;"> <div class="system-user-list-container" style="min-height: 600px;">
<el-table :data="tableData.data" style="width: 100%" v-if="!multiple"> <el-table :data="tableData.data" style="width: 100%" >
<el-table-column type="index" label="序号" width="60" /> <el-table-column type="index" label="序号" width="60" />
<el-table-column prop="userNickname" label="姓名" show-overflow-tooltip></el-table-column> <el-table-column prop="userNickname" label="姓名" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="100" > <el-table-column label="操作" width="100" >
@ -17,11 +9,6 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-table ref="tableMultipleRef" :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-else>
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="userNickname" label="姓名" show-overflow-tooltip></el-table-column>
</el-table>
<pagination <pagination
v-show="tableData.total>0" v-show="tableData.total>0"
:total="tableData.total" :total="tableData.total"
@ -33,9 +20,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import {toRefs, reactive, onMounted, ref, defineComponent, getCurrentInstance} from 'vue'; import {toRefs, reactive, defineComponent, getCurrentInstance} from 'vue';
import {ElMessageBox, ElMessage} from 'element-plus'; import {getUserSelector} from '/@/api/system/user';
import {getUserSelector} from '/@/api/system/user/index';
interface TableDataState { interface TableDataState {
ids:number[]; ids:number[];
@ -72,10 +58,6 @@ export default defineComponent({
genderData:{ genderData:{
type:Array, type:Array,
default:()=>[] default:()=>[]
},
multiple:{
type:Boolean,
default:false
} }
}, },
emits:['ok'], emits:['ok'],
@ -83,7 +65,6 @@ export default defineComponent({
setup(prop,{emit}) { setup(prop,{emit}) {
const {proxy,props} = <any>getCurrentInstance(); const {proxy,props} = <any>getCurrentInstance();
const {sys_user_sex} = proxy.useDict('sys_user_sex') const {sys_user_sex} = proxy.useDict('sys_user_sex')
const tableMultipleRef = ref();
const state = reactive<TableDataState>({ const state = reactive<TableDataState>({
ids:[], ids:[],
userItem:[], userItem:[],
@ -113,44 +94,15 @@ export default defineComponent({
getUserSelector(param).then((res:any)=>{ getUserSelector(param).then((res:any)=>{
state.tableData.data = res.data.userList??[]; state.tableData.data = res.data.userList??[];
state.tableData.total = res.data.total; state.tableData.total = res.data.total;
toggleSelection()
}); });
}; };
const toggleSelection = (rows?: any) => {
if (rows) {
rows.forEach((row) => {
tableMultipleRef.value!.toggleRowSelection(row, undefined)
})
} else {
tableMultipleRef.value!.clearSelection()
}
}
const confirmUser = () => {
let userItem:any[] = [];
userItem = state.userItem
if(userItem.length===0){
ElMessage.error('请勾选用户');
return
}else{
emit("ok",userItem);
}
};
//
const handleSelectionChange = (selection:any[])=> {
state.ids = selection.map(item => item.id)
state.userItem = selection.map(item => item)
};
const onOpenSelectUser = (row:any) => { const onOpenSelectUser = (row:any) => {
emit("ok",row); emit("ok",row);
} }
return { return {
sys_user_sex, sys_user_sex,
setUserList, setUserList,
handleSelectionChange,
onOpenSelectUser, onOpenSelectUser,
confirmUser,
tableMultipleRef,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<el-dialog title="选择用户" v-model="visible" width="60%" top="5vh" append-to-body :close-on-click-modal="false"> <el-dialog title="选择用户" v-model="visible" width="80%" top="5vh" append-to-body :close-on-click-modal="false">
<div class="system-user-container"> <div class="system-user-container">
<el-row :gutter="10" style="width: 100%;"> <el-row :gutter="10" style="width: 100%;">
<el-col :span="6"> <el-col :span="6">
@ -21,7 +21,7 @@
</el-aside> </el-aside>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="18"> <el-col :span="12">
<el-card shadow="hover"> <el-card shadow="hover">
<div class="system-user-search mb15"> <div class="system-user-search mb15">
<el-form :model="param" ref="queryRef" :inline="true" label-width="68px"> <el-form :model="param" ref="queryRef" :inline="true" label-width="68px">
@ -64,6 +64,32 @@
<UserList ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="param" :multiple="multiple" @ok="handleSelectUserOk"/> <UserList ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="param" :multiple="multiple" @ok="handleSelectUserOk"/>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="6">
<el-card shadow="hover">
<el-row :gutter="10">
<el-col :span="10">
<p style="height: 32px;line-height: 32px;">已选择{{selectedUsers.length}}</p>
</el-col>
<el-col :span="7">
<el-button type="success" plain @click="goBack">确认返回</el-button>
</el-col>
<el-col :span="7">
<el-button type="danger" plain @click="removeAll">全部移除</el-button>
</el-col>
<el-col :span="24">
<el-table :data="selectedUserInfo">
<el-table-column label="操作" >
<template #default="scope">
<el-button type="danger" plain @click="remove(scope.$index)">移除</el-button>
</template>
</el-table-column>
<el-table-column prop="userNickname" label="姓名" />
</el-table>
</el-col>
</el-row>
<el-pagination layout="prev, pager, next" :total="selectedUsers.length" v-model:current-page="selectedUsersPage"/>
</el-card>
</el-col>
</el-row> </el-row>
</div> </div>
</el-dialog> </el-dialog>
@ -71,7 +97,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import {toRefs, reactive, ref, defineComponent, watch, getCurrentInstance, nextTick} from 'vue'; import {toRefs, reactive, ref, defineComponent, watch, getCurrentInstance, nextTick, computed} from 'vue';
import {ElTree,FormInstance} from 'element-plus'; import {ElTree,FormInstance} from 'element-plus';
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import UserList from './component/userList.vue'; import UserList from './component/userList.vue';
@ -97,10 +123,15 @@ export default defineComponent({
multiple:{ multiple:{
type:Boolean, type:Boolean,
default:false default:false
},
selectedUsers:{
type:Array,
default:()=>[]
} }
}, },
emits:['selectUser'], emits:['selectUser','okBack'],
setup(prop,{emit}) { setup(prop,{emit}) {
const selectedUsersPage = ref(1)
const visible = ref(false) const visible = ref(false)
const {proxy} = <any>getCurrentInstance(); const {proxy} = <any>getCurrentInstance();
const {sys_user_sex} = proxy.useDict('sys_user_sex') const {sys_user_sex} = proxy.useDict('sys_user_sex')
@ -125,6 +156,16 @@ export default defineComponent({
dateRange:[] dateRange:[]
}, },
}); });
const selectedUserInfo = computed({
get:()=>{
let start = (selectedUsersPage.value-1)*10
let end = start+10
return prop.selectedUsers.slice(start,end)
} ,
set:(v)=>{
emit("selectUser",v);
}
});
const getUserList = ()=>{ const getUserList = ()=>{
userListRef.value.setUserList(); userListRef.value.setUserList();
}; };
@ -161,10 +202,24 @@ export default defineComponent({
}) })
} }
const handleSelectUserOk = (row:any)=>{ const handleSelectUserOk = (row:any)=>{
selectedUserInfo.value = [...selectedUserInfo.value,row]
}
const goBack = ()=>{
visible.value = false; visible.value = false;
emit("selectUser",row); emit("okBack");
}
const removeAll = ()=>{
selectedUserInfo.value = []
}
const remove = (index:number)=>{
index = (selectedUsersPage.value-1)*10+index
let newSel:any = [...selectedUserInfo.value]
selectedUserInfo.value = []
newSel.splice(index,1)
selectedUserInfo.value = newSel
} }
return { return {
selectedUsersPage,
visible, visible,
queryRef, queryRef,
userListRef, userListRef,
@ -173,11 +228,15 @@ export default defineComponent({
treeRef, treeRef,
search, search,
sys_user_sex, sys_user_sex,
selectedUserInfo,
openDialog, openDialog,
getUserList, getUserList,
handleSelectUserOk, handleSelectUserOk,
handleNodeClick, handleNodeClick,
resetQuery, resetQuery,
goBack,
removeAll,
remove,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -20,12 +20,9 @@
<img w-full :src="dialogImageUrl" alt="Preview Image" /> <img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog> </el-dialog>
</div> </div>
<div v-else> <div class="up-img" v-else>
<div class="up-img">
<el-upload <el-upload
v-model:file-list="dataFileList" v-model:file-list="dataFileList"
:limit="limit"
class="avatar-uploader" class="avatar-uploader"
:action="action" :action="action"
:show-file-list="false" :show-file-list="false"
@ -39,7 +36,6 @@
<el-icon v-else class="avatar-uploader-icon"><ele-Plus /></el-icon> <el-icon v-else class="avatar-uploader-icon"><ele-Plus /></el-icon>
</el-upload> </el-upload>
</div> </div>
</div>
</template> </template>
@ -90,6 +86,10 @@ export default defineComponent({
return item return item
}) })
uploadedFile = _.cloneDeep(value) uploadedFile = _.cloneDeep(value)
if(props.limit == 1){
uploadedFile = [];
imageUrl.value = (value[0]?value[0].url:'') as string;
}
return value return value
}, },
set: val => { set: val => {

View File

@ -12,7 +12,7 @@ import 'element-plus/dist/index.css';
import '/@/theme/index.scss'; import '/@/theme/index.scss';
import mitt from 'mitt'; import mitt from 'mitt';
import VueGridLayout from 'vue-grid-layout'; import VueGridLayout from 'vue-grid-layout';
import {getUpFileUrl, handleTree, parseTime, selectDictLabel} from '/@/utils/gfast'; import {findChildrenByPid, flattenTree, getUpFileUrl, handleTree, parseTime, selectDictLabel} from '/@/utils/gfast';
import Websocket from '/@/utils/websocket'; import Websocket from '/@/utils/websocket';
import {useDict} from '/@/api/system/dict/data'; import {useDict} from '/@/api/system/dict/data';
import {getItems, setItems, getOptionValue, isEmpty} from '/@/api/items' import {getItems, setItems, getOptionValue, isEmpty} from '/@/api/items'
@ -53,6 +53,8 @@ app.use(pinia)
app.config.globalProperties.getUpFileUrl=getUpFileUrl app.config.globalProperties.getUpFileUrl=getUpFileUrl
app.config.globalProperties.handleTree=handleTree app.config.globalProperties.handleTree=handleTree
app.config.globalProperties.flattenTree=flattenTree
app.config.globalProperties.findChildrenByPid=findChildrenByPid
app.config.globalProperties.useDict=useDict app.config.globalProperties.useDict=useDict
app.config.globalProperties.selectDictLabel=selectDictLabel app.config.globalProperties.selectDictLabel=selectDictLabel

View File

@ -80,6 +80,33 @@ export function handleTree(data:any[], id:string, parentId:string, children:stri
return treeData != '' ? treeData : data; return treeData != '' ? treeData : data;
} }
export function flattenTree(treeArray:any[]):any[] {
const result:any[] = [];
function flatten(node:any) {
result.push(node);
if (node.children && node.children.length > 0) {
node.children.forEach((child:any[]) => flatten(child));
}
}
treeArray.forEach(node => flatten(node));
return result;
}
export function findChildrenByPid(pid:any, flattenedArray :any[]):any[] {
const result:any[] = [];
flattenedArray.forEach(node => {
if (node.pid === pid) {
result.push(node);
const grandchildren = findChildrenByPid(node.id, flattenedArray);
if (grandchildren.length > 0) {
result.push(...grandchildren);
}
}
});
return result;
}
// 回显数据字典 // 回显数据字典
export function selectDictLabel(data:any[], value:string):string { export function selectDictLabel(data:any[], value:string):string {

View File

@ -40,7 +40,7 @@ service.interceptors.response.use(
ElMessageBox.alert('登录状态已过期,请重新登录', '提示', {confirmButtonText:'确定'}) ElMessageBox.alert('登录状态已过期,请重新登录', '提示', {confirmButtonText:'确定'})
.then(() => { .then(() => {
Session.clear(); // 清除浏览器全部临时缓存 Session.clear(); // 清除浏览器全部临时缓存
window.location.href = '/'; // 去登录页 window.location.href = import.meta.env.BASE_URL; // 去登录页
}) })
.catch(() => {}); .catch(() => {});
} else if (code !== 0) { } else if (code !== 0) {

View File

@ -68,13 +68,13 @@
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
<select-user ref="selectUserRef" @selectUser="confirmUser" :multiple="true"></select-user> <select-user ref="selectUserRef" @selectUser="confirmUser" :selectedUsers="deptUser"></select-user>
</template> </template>
<script lang="ts"> <script lang="ts">
import {reactive, toRefs, defineComponent, getCurrentInstance,ref,unref} from 'vue'; import {reactive, toRefs, defineComponent, getCurrentInstance,ref,unref} from 'vue';
import {addDept,editDept, getDeptList} from "/@/api/system/dept"; import {addDept,editDept, getDeptList} from "/@/api/system/dept";
import {getUserByIds} from '/@/api/system/user/index'; import {getUserByIds} from '/@/api/system/user';
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import selectUser from "/@/components/selectUser/index.vue" import selectUser from "/@/components/selectUser/index.vue"
@ -208,18 +208,23 @@ export default defineComponent({
const handleClose = (data:any,key:number) => { const handleClose = (data:any,key:number) => {
deptUser.value.splice(key, 1); deptUser.value.splice(key, 1);
state.ruleForm.leader = deptUser.value.map(item => item.id) state.ruleForm.leader = deptUser.value.map((item:any) => item.id)
}; };
const confirmUser = (data:any[]) => { const confirmUser = (data:any[]) => {
let leaderArr = state.ruleForm.leader??[]; let leaderArr = state.ruleForm.leader??[];
data.map(function (item) { if(data.length>0){
data.map((item:any)=>{
// //
if (!leaderArr.includes(item.id)){ if (!leaderArr.includes(item.id)){
deptUser.value.push(item) deptUser.value.push(item as never)
leaderArr.push(item.id) leaderArr.push(item.id)
} }
}); })
state.ruleForm.leader = leaderArr; state.ruleForm.leader = leaderArr;
}else{
deptUser.value = []
state.ruleForm.leader = []
}
}; };
// //
const handleSelectUser = () =>{ const handleSelectUser = () =>{

View File

@ -71,9 +71,19 @@
</template> </template>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" > <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
<el-form-item label="权限标识"> <el-form-item label="权限标识">
<el-select v-model="ruleForm.roles" multiple placeholder="选择角色" clearable class="w100"> <el-cascader
<el-option v-for="role in roles" :key="'role_'+role.id" :label="role.name" :value="role.id"></el-option> :options="roles"
</el-select> :props="{ checkStrictly: true,emitPath: false, value: 'id', label: 'name',multiple: true }"
placeholder="请选择角色"
clearable
class="w100"
v-model="ruleForm.roles"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" > <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" >
@ -213,7 +223,8 @@ export default defineComponent({
nextTick(()=>{ nextTick(()=>{
// //
getMenuParams().then((res:any)=>{ getMenuParams().then((res:any)=>{
state.roles = res.data.roles; const roles = res.data.roles??[];
state.roles = proxy.handleTree(roles??[], "id","pid","children",true);
const menu = { id: 0, title: '主类目', children: [] }; const menu = { id: 0, title: '主类目', children: [] };
menu.children = proxy.handleTree(res.data.menus, "id","pid"); menu.children = proxy.handleTree(res.data.menus, "id","pid");
state.menuData=new Array(menu) as any; state.menuData=new Array(menu) as any;

View File

@ -8,7 +8,7 @@
<div class="personal-user-left"> <div class="personal-user-left">
<el-upload <el-upload
class=" h100 personal-user-left-upload avatar-uploader" class=" h100 personal-user-left-upload avatar-uploader"
:action="baseURL+'/api/v1/system/upload/singleImg'" :action="baseURL+'api/v1/system/upload/singleImg'"
:show-file-list="false" :show-file-list="false"
:on-success="handleAvatarSuccess" :on-success="handleAvatarSuccess"
:data="dataParam" :data="dataParam"

View File

@ -1,28 +1,71 @@
<template> <template>
<div class="system-sysJob-edit"> <div class="system-sysJob-edit">
<!-- 分配角色数据权限对话框 --> <!-- 分配角色数据权限对话框 -->
<el-dialog title="数据权限设置" v-model="openDataScope" width="769px" append-to-body :close-on-click-modal="false"> <el-dialog title="数据权限设置" v-model="openDataScope" width="90%" append-to-body :close-on-click-modal="false">
<el-alert title="注:此功能界面需要与代码查询条件配合使用,并非所有接口都需设置数据权限,多用于业务模块!" :closable="false" type="warning" style="margin-bottom: 12px;"/>
<el-form ref="formRef" :model="form" label-width="80px"> <el-form ref="formRef" :model="form" label-width="80px">
<el-form-item label="角色名称"> <el-form-item label="角色名称">
<el-input v-model="form.roleName" :disabled="true" /> <el-input v-model="form.roleName" :disabled="true" />
</el-form-item> </el-form-item>
</el-form>
<el-table ref="tableRef" :data="menuTableData" style="width: 100%" row-key="path" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="菜单名称" >
<template #default="scope">
<SvgIcon :name="scope.row.icon" />
<span class="ml10">{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column label="api接口" >
<template #default="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-form-item label="权限范围"> <el-table-column label="类型" show-overflow-tooltip width="80">
<el-select v-model="form.dataScope"> <template #default="scope">
<el-option <el-tag :type="scope.row.menuType===0?'danger':(scope.row.menuType===1?'success':'warning')"
v-for="item in dataScopeOptions" size="small">{{scope.row.menuType===0?'目录':(scope.row.menuType===1?'菜单':'按钮') }}</el-tag>
:key="item.value" </template>
:label="item.label" </el-table-column>
:value="item.value"
></el-option> <el-table-column label="操作" width="600">
</el-select> <template #header>
</el-form-item> <el-radio-group v-model="optionsActionValue" @change="setOptionsActionValueHandle">
<el-form-item label="数据权限" v-show="form.dataScope == 2"> <template v-for="item in dataScopeOptions" :key="item.value">
<el-radio v-if="item.value!='2'" :label="item.value" >{{item.label}}</el-radio>
</template>
</el-radio-group>
</template>
<template #default="scope">
<div class="option-con">
<el-radio-group v-model="menuAuthData[scope.row.id]">
<template v-for="item in dataScopeOptions" :key="item.value">
<el-radio :label="item.value" @change="setOptionsItemHandle(item.value,scope.row.id)">
<el-badge v-if="item.value=='2' && menuAuthData[scope.row.id]=='2'" type="success" :value="getDeptLen(scope.row.id)" :max="99" class="item" :show-zero="false">{{item.label}}</el-badge>
<span v-else>{{item.label}}</span>
</el-radio>
</template>
</el-radio-group>
<el-link type="primary" @click="openSelDeptHandler(scope.row.id)" :underline="false" v-show="menuAuthData[scope.row.id] && menuAuthData[scope.row.id]=='2'">选择部门</el-link>
</div>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitDataScope"> </el-button>
<el-button @click="cancelDataScope"> </el-button>
</div>
</template>
</el-dialog>
<!-- 选择部门对话框 -->
<el-dialog title="选择部门" v-model="openSelDept" width="800px" append-to-body :close-on-click-modal="false">
<el-row :gutter="35"> <el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"> <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event)">展开/折叠</el-checkbox> <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event)">展开/折叠</el-checkbox>
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event)">全选/全不选</el-checkbox> <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event)">全选/全不选</el-checkbox>
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event)">父子联动</el-checkbox> <el-checkbox v-model="deptCheckStrictly" @change="handleCheckedTreeConnect($event)">父子联动</el-checkbox>
</el-col> </el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20"> <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-tree <el-tree
@ -32,17 +75,16 @@
default-expand-all default-expand-all
ref="deptRef" ref="deptRef"
node-key="deptId" node-key="deptId"
:check-strictly="!form.deptCheckStrictly" :check-strictly="!deptCheckStrictly"
:props="deptProps" :props="deptProps"
></el-tree> ></el-tree>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item>
</el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="submitDataScope"> </el-button> <el-button type="primary" @click="submitDept"> </el-button>
<el-button @click="cancelDataScope"> </el-button> <el-button type="success" @click="submitSonMenuDept">确定并应用到子菜单</el-button>
<el-button @click="cancelDept"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -50,13 +92,23 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent, reactive, ref} from "vue"; import {defineComponent, getCurrentInstance, nextTick, reactive, ref} from "vue";
import {dataScope, getRole, roleDeptTreeSelect} from "/@/api/system/role"; import {dataScope, getRole, roleDeptTreeSelect, roleMenuTreeSelect} from "/@/api/system/role";
import {ElMessage} from "element-plus/es"; import {ElMessage} from "element-plus/es";
import {findChildrenByPid, flattenTree} from "/@/utils/gfast";
import * as events from "events";
export default defineComponent({ export default defineComponent({
name: "dataScope", name: "dataScope",
computed: {
events() {
return events
}
},
setup(props,{emit}){ setup(props,{emit}){
const tableRef = ref()
const openSelDept = ref(false);
const {proxy} = getCurrentInstance() as any;
const openDataScope = ref(false) const openDataScope = ref(false)
const deptExpand = ref(true) const deptExpand = ref(true)
const deptNodeAll = ref(false) const deptNodeAll = ref(false)
@ -64,6 +116,9 @@ export default defineComponent({
const menuOptions = ref([]) const menuOptions = ref([])
const deptRef = ref(); const deptRef = ref();
const formRef = ref(); const formRef = ref();
const deptCheckStrictly = ref(false)
const optionsActionValue = ref("0")
const setDeptId = ref(0)
const deptProps = ref({ const deptProps = ref({
children: "children", children: "children",
label: "deptName" label: "deptName"
@ -71,33 +126,39 @@ export default defineComponent({
// //
const dataScopeOptions = ref([ const dataScopeOptions = ref([
{ {
value: "1", value: 1,
label: "全部数据权限" label: "全部"
}, },
{ {
value: "2", value: 3,
label: "自定数据权限" label: "本部门"
}, },
{ {
value: "3", value: 4,
label: "本部门数据权限" label: "本部门及以下"
}, },
{ {
value: "4", value: 5,
label: "本部门及以下数据权限" label: "仅本人"
}, },
{ {
value: "5", value: 2,
label: "仅本人数据权限" label: "自定义"
} }
]) ])
const menuAuthData = ref([])
const deptAuthData = ref([])
const form = reactive({ const form = reactive({
roleId:undefined, roleId:undefined,
roleName:'', roleName:'',
dataScope:'3', authData:[{
deptCheckStrictly: false, menuId:'0',
scope:'0',
deptIds:[] deptIds:[]
}]
}) })
const menuTableData = ref([])
const menuTableList = ref([])
// / // /
const handleCheckedTreeExpand = (value:any) => { const handleCheckedTreeExpand = (value:any) => {
let treeList = deptOptions.value; let treeList = deptOptions.value;
@ -111,12 +172,21 @@ export default defineComponent({
} }
/** 树权限(父子联动) */ /** 树权限(父子联动) */
const handleCheckedTreeConnect = (value:any) => { const handleCheckedTreeConnect = (value:any) => {
form.deptCheckStrictly = value ? true : false; deptCheckStrictly.value = value ? true : false;
} }
/** 提交按钮(数据权限) */ /** 提交按钮(数据权限) */
const submitDataScope = () => { const submitDataScope = () => {
if (form.roleId != undefined) { form.authData = []
form.deptIds = getDeptAllCheckedKeys(); //
let rows = tableRef.value.getSelectionRows()
rows.map((item:any)=>{
let index = item.id
form.authData.push({
menuId:index,
scope:menuAuthData.value[index],
deptIds:deptAuthData.value[index]??[]
})
})
dataScope(form).then((response:any) => { dataScope(form).then((response:any) => {
if (response.code === 0) { if (response.code === 0) {
ElMessage.success("设置成功"); ElMessage.success("设置成功");
@ -126,32 +196,83 @@ export default defineComponent({
ElMessage.error("设置失败") ElMessage.error("设置失败")
} }
}); });
}
} }
const cancelDataScope = () => { const cancelDataScope = () => {
closeDialog(); closeDialog();
}; };
const submitDept = ()=>{
deptAuthData.value[setDeptId.value] = getDeptAllCheckedKeys() as never;
cancelDept()
}
const submitSonMenuDept = ()=>{
const checkedDeptId = getDeptAllCheckedKeys() as never;
deptAuthData.value[setDeptId.value] = getDeptAllCheckedKeys() as never;
//
const children = proxy.findChildrenByPid(setDeptId.value,menuTableList.value)
children.map((item:any)=>{
deptAuthData.value[item.id] = getDeptAllCheckedKeys() as never;
})
cancelDept()
}
const cancelDept = () => {
deptCheckStrictly.value= false
openSelDept.value = false
};
// //
const openDialog = (row: any) => { const openDialog = (row: any) => {
openDataScope.value = true;
roleDeptTreeSelect().then(response => {
deptOptions.value = response.data.depts||[];
});
resetForm(); resetForm();
if(row) { if(row) {
getRole(row.id).then((res:any)=>{ getRole(row.id).then((res:any)=>{
if(res.data.role){ if(res.data.role){
form.roleName = res.data.role.name; form.roleName = res.data.role.name;
form.roleId = res.data.role.id; form.roleId = res.data.role.id;
form.dataScope=res.data.role.dataScope.toString();
} }
}) })
getRoleDeptTreeselect(row.id) getMenuTreeselect(row.id)
} }
openDataScope.value = true;
}; };
/** 根据角色ID查询部门树结构 */ /** 根据角色ID查询部门树结构 */
const getRoleDeptTreeselect = (roleId:number) =>{ const getRoleDeptTreeselect = (menuId:any) =>{
roleDeptTreeSelect(roleId).then(response => { nextTick(()=>{
deptOptions.value = response.data.depts||[]; deptRef.value?.setCheckedKeys([], false)
deptRef.value.setCheckedKeys(response.data.checkedKeys, true); if(deptAuthData.value[menuId]){
}); deptRef.value.setCheckedKeys(deptAuthData.value[menuId], true);
}
})
}
const getMenuTreeselect = (roleId:number) =>{
roleMenuTreeSelect(roleId).then(res=>{
menuTableData.value = proxy.handleTree(res.data.rules??[], "id","pid");
menuTableList.value = proxy.flattenTree(menuTableData.value)
const menuData:any = []
const deptData:any = []
const rows:never[] = []
if(res.data.dataScope){
res.data.dataScope.map((item:any)=>{
menuData[item.menuId] = item.dataScope
deptData[item.menuId] = item.deptIds
})
menuAuthData.value = menuData
deptAuthData.value = deptData
//
menuTableList.value.map((item:any)=>{
if(menuAuthData.value[item.id]){
rows.push(item as never)
}
})
nextTick(()=>{
//
rows.map((item:any)=>{
tableRef.value.toggleRowSelection(item,true)
})
})
}
})
} }
// //
const closeDialog = () => { const closeDialog = () => {
@ -166,26 +287,81 @@ export default defineComponent({
const resetForm = ()=>{ const resetForm = ()=>{
form.roleId=undefined form.roleId=undefined
form.roleName='' form.roleName=''
form.dataScope='3' form.authData = []
form.deptCheckStrictly= false deptCheckStrictly.value= false
form.deptIds=[] menuAuthData.value = []
deptAuthData.value = []
}; };
const handleSelectionChange = (val: any[]) => {
}
const setOptionsActionValueHandle = (value:any)=>{
//
menuTableList.value.map((item:any)=>{
menuAuthData.value[item.id] = value as never
})
}
const setOptionsItemHandle = (value:any,id:any)=>{
const children = proxy.findChildrenByPid(id,menuTableList.value)
//
menuTableList.value.some((item:any)=>{
if(item.id == id){
children.unshift(item)
return true
}
return false
})
children.map((item:any)=>{
tableRef.value.toggleRowSelection(item,true)
})
//
children.map((item:any)=>{
menuAuthData.value[item.id] = value as never
})
}
const openSelDeptHandler = (id:any)=>{
openSelDept.value = true
setDeptId.value = id
getRoleDeptTreeselect(id)
}
const getDeptLen = (id:any)=>{
if(deptAuthData.value[id]){
return (<Array<any>>deptAuthData.value[id]).length
}
return 0
}
return { return {
tableRef,
openSelDept,
openDialog, openDialog,
dataScopeOptions, dataScopeOptions,
deptExpand, deptExpand,
openDataScope, openDataScope,
deptNodeAll, deptNodeAll,
deptOptions, deptOptions,
deptCheckStrictly,
deptProps, deptProps,
menuOptions, menuOptions,
deptRef, deptRef,
formRef, formRef,
optionsActionValue,
cancelDataScope, cancelDataScope,
submitDataScope, submitDataScope,
handleCheckedTreeExpand, handleCheckedTreeExpand,
handleCheckedTreeNodeAll, handleCheckedTreeNodeAll,
handleCheckedTreeConnect, handleCheckedTreeConnect,
menuTableData,
handleSelectionChange,
setOptionsActionValueHandle,
menuAuthData,
setOptionsItemHandle,
openSelDeptHandler,
cancelDept,
submitDept,
submitSonMenuDept,
deptAuthData,
setDeptId,
getDeptLen,
form form
} }
} }
@ -205,4 +381,14 @@ export default defineComponent({
padding: 5px; padding: 5px;
} }
} }
.action{
margin-top: 5px;
}
.option-con{
display: flex;
height: 40px;
.el-link{
margin-left: 30px;
}
}
</style> </style>

View File

@ -40,6 +40,53 @@
<el-input v-model="formData.remark" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input> <el-input v-model="formData.remark" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" >
<el-form-item label="有效时间">
<el-radio-group v-model="formData.effectiveType">
<el-radio :label="0">不设置</el-radio>
<el-radio :label="1">按起止日期</el-radio>
<el-radio :label="2">按时间段</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-show="formData.effectiveType===2">
<el-form-item label="每周">
<el-checkbox-group v-model="formData.weekDay">
<el-checkbox :label="1" >周一</el-checkbox>
<el-checkbox :label="2" >周二</el-checkbox>
<el-checkbox :label="3" >周三</el-checkbox>
<el-checkbox :label="4" >周四</el-checkbox>
<el-checkbox :label="5" >周五</el-checkbox>
<el-checkbox :label="6" >周六</el-checkbox>
<el-checkbox :label="0" >周日</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-show="formData.effectiveType===2">
<el-form-item label="时间段" style="width: 360px;">
<el-time-picker
v-model="formData.dayRange"
is-range
format="HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="截止时间"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-show="formData.effectiveType===1">
<el-form-item label="起止日期" style="width: 450px">
<el-date-picker
v-model="formData.dateRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" > <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" >
<el-form-item label="菜单权限"> <el-form-item label="菜单权限">
<el-row :gutter="35"> <el-row :gutter="35">
@ -95,6 +142,10 @@ interface DialogRow {
listOrder: number; listOrder: number;
remark: string; remark: string;
menuIds:Array<number> menuIds:Array<number>
effectiveType:number;
weekDay:Array<number>;
dayRange:Array<string>;
dateRange:Array<string>;
} }
interface RoleState { interface RoleState {
loading:boolean; loading:boolean;
@ -135,6 +186,13 @@ export default defineComponent({
listOrder: 0, listOrder: 0,
remark: '', remark: '',
menuIds:[], menuIds:[],
effectiveType:0,
weekDay:[1,2,3,4,5],
dayRange:[
'2024-02-01 08:00:00',
'2024-02-01 19:00:00',
],
dateRange:[],
}, },
// //
rules: { rules: {
@ -160,6 +218,9 @@ export default defineComponent({
getRole(row.id).then((res:any)=>{ getRole(row.id).then((res:any)=>{
if(res.data.role){ if(res.data.role){
state.formData = res.data.role; state.formData = res.data.role;
if(!state.formData.weekDay){
state.formData.weekDay = [1,2,3,4,5]
}
state.formData.menuIds = res.data.menuIds??[] state.formData.menuIds = res.data.menuIds??[]
} }
}) })
@ -229,6 +290,13 @@ export default defineComponent({
listOrder: 0, listOrder: 0,
remark: '', remark: '',
menuIds:[], menuIds:[],
effectiveType:0,
weekDay:[1,2,3,4,5],
dayRange:[
'2024-02-01 08:00:00',
'2024-02-01 19:00:00',
],
dateRange:[]
} }
}; };
/** 树权限(展开/折叠)*/ /** 树权限(展开/折叠)*/

View File

@ -51,7 +51,18 @@
<el-table-column label="操作" width="220"> <el-table-column label="操作" width="220">
<template #default="scope"> <template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)"><el-icon><ele-EditPen /></el-icon>修改</el-button> <el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)"><el-icon><ele-EditPen /></el-icon>修改</el-button>
<el-button size="small" text type="primary" @click="handleDataScope(scope.row)"><el-icon><ele-Avatar /></el-icon>数据权限</el-button> <el-dropdown class="auth-action" @command="handleCommand">
<span class="el-dropdown-link">
授权 <el-icon><ele-ArrowDown /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu class="auth-action-menu">
<el-dropdown-item :command="'resource_'+scope.row.id">资源权限</el-dropdown-item>
<el-dropdown-item :command="'data_'+scope.row.id">数据权限</el-dropdown-item>
<el-dropdown-item :command="'user_'+scope.row.id">用户授权</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)"><el-icon><ele-DeleteFilled /></el-icon>删除</el-button> <el-button size="small" text type="primary" @click="onRowDel(scope.row)"><el-icon><ele-DeleteFilled /></el-icon>删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -59,21 +70,22 @@
</el-card> </el-card>
<EditRole ref="editRoleRef" @getRoleList="roleList" :roleData="tableData.data"/> <EditRole ref="editRoleRef" @getRoleList="roleList" :roleData="tableData.data"/>
<DataScope ref="dataScopeRef" @getRoleList="roleList"/> <DataScope ref="dataScopeRef" @getRoleList="roleList"/>
<el-dialog :title="selectRow.name+'-用户列表'" v-model="isShowDialog" width="70vw"> <el-dialog :title="selectRow.name+'-用户列表'" v-model="isShowDialog" width="70vw">
<UserList v-if="isShowDialog" ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="userListParam" @getUserList="userList"/> <UserList v-if="isShowDialog" ref="userListRef" :dept-data="deptData" :gender-data="sys_user_sex" :param="userListParam" @getUserList="userList"/>
</el-dialog> </el-dialog>
<select-user ref="selectUserRef" @selectUser="confirmUser" @okBack="setRoleUser" :selectedUsers="roleUsers"></select-user>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {toRefs, reactive, onMounted, ref, defineComponent, toRaw,getCurrentInstance} from 'vue'; import {toRefs, reactive, onMounted, ref, defineComponent, toRaw,getCurrentInstance} from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus'; import { ElMessageBox, ElMessage,ElLoading } from 'element-plus';
import EditRole from '/@/views/system/role/component/editRole.vue'; import EditRole from '/@/views/system/role/component/editRole.vue';
import DataScope from '/@/views/system/role/component/dataScope.vue'; import DataScope from '/@/views/system/role/component/dataScope.vue';
import {deleteRole, getRoleList} from "/@/api/system/role"; import {deleteRole, getRoleList, setRoleUsers} from "/@/api/system/role";
import {getDeptTree} from '/@/api/system/user/index'; import {getDeptTree, getUsersById} from '/@/api/system/user';
import UserList from '/@/views/system/user/component/userList.vue'; import UserList from '/@/views/system/user/component/userList.vue';
import selectUser from "/@/components/selectUser/index.vue";
// //
interface TableData { interface TableData {
@ -89,7 +101,7 @@ interface TableData {
} }
interface TableDataState { interface TableDataState {
isShowDialog:boolean; isShowDialog:boolean;
selectRow:object; selectRow:any;
deptData:any[]; deptData:any[];
userListParam: { userListParam: {
roleId:number | undefined; roleId:number | undefined;
@ -109,14 +121,18 @@ interface TableDataState {
export default defineComponent({ export default defineComponent({
name: 'apiV1SystemRoleList', name: 'apiV1SystemRoleList',
components: {EditRole,DataScope,UserList}, components: {selectUser, EditRole,DataScope,UserList},
setup() { setup() {
const selectUserRef = ref()
const {proxy} = getCurrentInstance() as any; const {proxy} = getCurrentInstance() as any;
const {sys_user_sex} = proxy.useDict('sys_user_sex') const {sys_user_sex} = proxy.useDict('sys_user_sex')
const addRoleRef = ref(); const addRoleRef = ref();
const userListRef = ref(); const userListRef = ref();
const editRoleRef = ref(); const editRoleRef = ref();
const dataScopeRef =ref(); const dataScopeRef =ref();
const roleListData = ref<Array<TableData>>()
const roleUsers = ref([])
const setRole = ref(0)
const state = reactive<TableDataState>({ const state = reactive<TableDataState>({
isShowDialog: false, isShowDialog: false,
deptData: [], deptData: [],
@ -157,6 +173,7 @@ export default defineComponent({
createdAt: item.createdAt, createdAt: item.createdAt,
}); });
}) })
roleListData.value = data
state.tableData.data = proxy.handleTree(data??[], "id","pid","children",true); state.tableData.data = proxy.handleTree(data??[], "id","pid","children",true);
}) })
}; };
@ -178,13 +195,23 @@ export default defineComponent({
editRoleRef.value.openDialog(); editRoleRef.value.openDialog();
}; };
// //
const onOpenEditRole = (row: Object) => { const onOpenEditRole = (row: Object|undefined) => {
editRoleRef.value.openDialog(toRaw(row)); editRoleRef.value.openDialog(toRaw(row));
}; };
// //
const handleDataScope=(row:any)=>{ const handleDataScope=(row:any)=>{
dataScopeRef.value.openDialog(toRaw(row)) dataScopeRef.value.openDialog(toRaw(row))
} }
//
const handleUserScope = ((row:any)=>{
const ld = ElLoading.service()
setRole.value = row.id
getUsersById(row.id).then((res:any)=>{
ld.close()
roleUsers.value=res.data.userList??[]
selectUserRef.value.openDialog()
})
})
// //
const onRowDel = (row: any) => { const onRowDel = (row: any) => {
ElMessageBox.confirm(`此操作将永久删除角色:“${row.name}”,是否继续?`, '提示', { ElMessageBox.confirm(`此操作将永久删除角色:“${row.name}”,是否继续?`, '提示', {
@ -216,11 +243,54 @@ export default defineComponent({
onMounted(() => { onMounted(() => {
initTableData(); initTableData();
}); });
const handleCommand = (command: string )=>{
let commandArr = command.split('_')
let row = roleListData.value?.filter((item:TableData)=>{
return item.id==parseInt(commandArr[1])
})?.[0]
switch (commandArr[0]){
case 'resource':
onOpenEditRole(row)
break
case 'data':
handleDataScope(row)
break
case 'user':
handleUserScope(row)
break
}
}
const confirmUser = (data:any[]) => {
if(data.length>0){
const ids = roleUsers.value.map((item:any)=>{
return item.id
})
console.log('ids = ',ids)
data.map((item:any)=>{
//
if (!ids.includes(item.id)){
roleUsers.value.push(item as never)
}
})
}else{
roleUsers.value = []
}
};
const setRoleUser = ()=>{
const ids = roleUsers.value.map((item:any)=>{
return item.id
})
//
setRoleUsers({roleId:setRole.value,userIds:ids}).then((res:any)=>{
roleList()
})
}
return { return {
addRoleRef, addRoleRef,
editRoleRef, editRoleRef,
dataScopeRef, dataScopeRef,
sys_user_sex, sys_user_sex,
selectUserRef,
userList, userList,
onOpenUserList, onOpenUserList,
onOpenAddRole, onOpenAddRole,
@ -230,8 +300,28 @@ export default defineComponent({
onHandleCurrentChange, onHandleCurrentChange,
roleList, roleList,
handleDataScope, handleDataScope,
handleUserScope,
handleCommand,
roleUsers,
confirmUser,
setRoleUser,
...toRefs(state), ...toRefs(state),
}; };
}, },
}); });
</script> </script>
<style scoped lang="scss">
.auth-action{
margin:6px 8px 0 8px;
.el-dropdown-link {
cursor: pointer;
color: var(--el-color-primary);
font-size: 12px;
display: flex;
align-items: center;
}
.auth-action-menu{
font-size: 12px;
}
}
</style>