|
@@ -0,0 +1,262 @@
|
|
|
|
+<template>
|
|
|
|
+ <el-dialog
|
|
|
|
+ v-model="visible"
|
|
|
|
+ :title="title"
|
|
|
|
+ width="600px"
|
|
|
|
+ :close-on-click-modal="false"
|
|
|
|
+ :close-on-press-escape="false"
|
|
|
|
+ top="0"
|
|
|
|
+ append-to-body
|
|
|
|
+ @close="handleClose"
|
|
|
|
+ @open="modalBeforeOpen"
|
|
|
|
+ >
|
|
|
|
+ <div class="privilege-content">
|
|
|
|
+ <div class="privilege-title">
|
|
|
|
+ <h4>权限配置</h4>
|
|
|
|
+ <el-space>
|
|
|
|
+ <el-button size="small" @click="expandAll">展开全部</el-button>
|
|
|
|
+ <el-button size="small" @click="collapseAll">收起全部</el-button>
|
|
|
|
+ <el-button size="small" type="primary" @click="checkAll"
|
|
|
|
+ >全选</el-button
|
|
|
|
+ >
|
|
|
|
+ <el-button size="small" @click="uncheckAll">取消全选</el-button>
|
|
|
|
+ </el-space>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <el-tree
|
|
|
|
+ ref="treeRef"
|
|
|
|
+ :data="treeData"
|
|
|
|
+ :props="treeProps"
|
|
|
|
+ show-checkbox
|
|
|
|
+ node-key="code"
|
|
|
|
+ :default-checked-keys="checkedKeys"
|
|
|
|
+ :default-expand-all="false"
|
|
|
|
+ class="privilege-tree"
|
|
|
|
+ >
|
|
|
|
+ <template #default="{ data }">
|
|
|
|
+ <span class="tree-node">
|
|
|
|
+ <span class="node-label">{{ data.name }}</span>
|
|
|
|
+ <span class="node-code">({{ data.code }})</span>
|
|
|
|
+ </span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-tree>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <template #footer>
|
|
|
|
+ <span class="dialog-footer">
|
|
|
|
+ <el-button @click="close">取消</el-button>
|
|
|
|
+ <el-button type="primary" :loading="loading" @click="confirm">
|
|
|
|
+ 保存
|
|
|
|
+ </el-button>
|
|
|
|
+ </span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dialog>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+ import { ref, computed } from 'vue';
|
|
|
|
+ import { ElMessage, ElTree } from 'element-plus';
|
|
|
|
+ import type { RoleItem, RolePrivilegeItem } from '@/api/types/user';
|
|
|
|
+ import { getRolePrivilegeList, updateRolePrivilege } from '@/api/user';
|
|
|
|
+ import useModal from '@/hooks/modal';
|
|
|
|
+ import useLoading from '@/hooks/loading';
|
|
|
|
+
|
|
|
|
+ defineOptions({
|
|
|
|
+ name: 'ModifyRolePrivilege',
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ /* modal */
|
|
|
|
+ const { visible, open, close } = useModal();
|
|
|
|
+ defineExpose({ open, close });
|
|
|
|
+
|
|
|
|
+ interface Props {
|
|
|
|
+ rowData: RoleItem;
|
|
|
|
+ schooldId: number;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const props = withDefaults(defineProps<Props>(), {
|
|
|
|
+ rowData: () => {},
|
|
|
|
+ });
|
|
|
|
+ const emit = defineEmits(['modified']);
|
|
|
|
+
|
|
|
|
+ const title = computed(() => `编辑角色权限 - ${props.rowData.name}`);
|
|
|
|
+
|
|
|
|
+ const treeRef = ref<InstanceType<typeof ElTree>>();
|
|
|
|
+ const privilegeList = ref<RolePrivilegeItem[]>([]);
|
|
|
|
+ const treeData = ref<any[]>([]);
|
|
|
|
+ const checkedKeys = ref<string[]>([]);
|
|
|
|
+
|
|
|
|
+ const treeProps = {
|
|
|
|
+ children: 'children',
|
|
|
|
+ label: 'name',
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 构建树形数据
|
|
|
|
+ const buildTreeData = (list: RolePrivilegeItem[]) => {
|
|
|
|
+ const map = new Map();
|
|
|
|
+ const roots: any[] = [];
|
|
|
|
+
|
|
|
|
+ // 创建节点映射
|
|
|
|
+ list.forEach((item) => {
|
|
|
|
+ map.set(item.code, {
|
|
|
|
+ ...item,
|
|
|
|
+ children: [],
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 构建树形结构
|
|
|
|
+ list.forEach((item) => {
|
|
|
|
+ const node = map.get(item.code);
|
|
|
|
+ if (item.parentCode && map.has(item.parentCode)) {
|
|
|
|
+ map.get(item.parentCode).children.push(node);
|
|
|
|
+ } else {
|
|
|
|
+ roots.push(node);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return roots;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 获取已选中的权限
|
|
|
|
+ const getCheckedKeys = (list: RolePrivilegeItem[]) => {
|
|
|
|
+ return list.filter((item) => item.enable).map((item) => item.code);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 展开全部
|
|
|
|
+ const expandAll = () => {
|
|
|
|
+ const allKeys = privilegeList.value.map((item) => item.code);
|
|
|
|
+ treeRef.value?.setExpandedKeys(allKeys);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 收起全部
|
|
|
|
+ const collapseAll = () => {
|
|
|
|
+ treeRef.value?.setExpandedKeys([]);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 全选
|
|
|
|
+ const checkAll = () => {
|
|
|
|
+ const allKeys = privilegeList.value.map((item) => item.code);
|
|
|
|
+ treeRef.value?.setCheckedKeys(allKeys);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 取消全选
|
|
|
|
+ const uncheckAll = () => {
|
|
|
|
+ treeRef.value?.setCheckedKeys([]);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const handleClose = () => {
|
|
|
|
+ privilegeList.value = [];
|
|
|
|
+ treeData.value = [];
|
|
|
|
+ checkedKeys.value = [];
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /* confirm */
|
|
|
|
+ const { loading, setLoading } = useLoading();
|
|
|
|
+ async function confirm() {
|
|
|
|
+ setLoading(true);
|
|
|
|
+ try {
|
|
|
|
+ const checkedNodes = treeRef.value?.getCheckedKeys() || [];
|
|
|
|
+ const halfCheckedNodes = treeRef.value?.getHalfCheckedKeys() || [];
|
|
|
|
+ const selectPrivileges = [
|
|
|
|
+ ...checkedNodes,
|
|
|
|
+ ...halfCheckedNodes,
|
|
|
|
+ ] as string[];
|
|
|
|
+
|
|
|
|
+ await updateRolePrivilege({
|
|
|
|
+ role: props.rowData.code,
|
|
|
|
+ schoolId: props.schooldId,
|
|
|
|
+ selectPrivileges,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ ElMessage.success('权限保存成功!');
|
|
|
|
+ emit('modified');
|
|
|
|
+ close();
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('保存权限失败:', error);
|
|
|
|
+ } finally {
|
|
|
|
+ setLoading(false);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* init modal */
|
|
|
|
+ async function modalBeforeOpen() {
|
|
|
|
+ if (!props.rowData.code) return;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ const response = await getRolePrivilegeList({
|
|
|
|
+ schoolId: props.schooldId,
|
|
|
|
+ role: props.rowData.code,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ privilegeList.value = response;
|
|
|
|
+ treeData.value = buildTreeData(response);
|
|
|
|
+ checkedKeys.value = getCheckedKeys(response);
|
|
|
|
+
|
|
|
|
+ // 延迟设置选中状态,确保树组件已渲染
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ treeRef.value?.setCheckedKeys(checkedKeys.value);
|
|
|
|
+ }, 100);
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('获取权限列表失败:', error);
|
|
|
|
+ ElMessage.error('获取权限列表失败');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style scoped>
|
|
|
|
+ .privilege-content {
|
|
|
|
+ max-height: 60vh;
|
|
|
|
+ overflow-y: auto;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .privilege-title {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-bottom: 15px;
|
|
|
|
+ padding-bottom: 10px;
|
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .privilege-title h4 {
|
|
|
|
+ margin: 0;
|
|
|
|
+ color: #303133;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .privilege-tree {
|
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ padding: 10px;
|
|
|
|
+ max-height: 400px;
|
|
|
|
+ overflow-y: auto;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .tree-node {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ width: 100%;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .node-label {
|
|
|
|
+ font-weight: 500;
|
|
|
|
+ color: #303133;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .node-code {
|
|
|
|
+ margin-left: 8px;
|
|
|
|
+ font-size: 12px;
|
|
|
|
+ color: #909399;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ :deep(.el-tree-node__content) {
|
|
|
|
+ height: 32px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ :deep(.el-tree-node__expand-icon) {
|
|
|
|
+ color: #c0c4cc;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ :deep(.el-tree-node__expand-icon.expanded) {
|
|
|
|
+ color: #409eff;
|
|
|
|
+ }
|
|
|
|
+</style>
|