Sfoglia il codice sorgente

feat: 超管角色管理

zhangjie 1 settimana fa
parent
commit
dcd957955d

+ 0 - 1
src/api/subject.ts

@@ -5,7 +5,6 @@ import {
   SubjectListPageRes,
   SubjectSettingInfo,
   PaperStructureListPageRes,
-  PaperStructureItem,
   PaperStructureUpdateParams,
   SubjectItem,
   OptionalQuestionItem,

+ 40 - 0
src/api/types/user.ts

@@ -1,4 +1,5 @@
 import { RoleType, BatchAddRole, UserSource } from '@/constants/enumerate';
+import { PageResult, PageParams } from './common';
 
 export interface LoginData {
   loginName: string;
@@ -118,3 +119,42 @@ export interface SystemInfo {
   // 版本日期
   versionDate: string;
 }
+
+// 角色相关 --------->
+export interface RoleItem {
+  // 角色名称
+  name: string;
+  // 角色编码
+  code: string;
+  // 更新时间
+  updateTime: string;
+  // 更新人
+  updateName: string;
+}
+export type RoleListPageRes = PageResult<RoleItem>;
+export interface RoleListFilter {
+  // 学校ID
+  schoolId: number;
+  // 角色编码
+  role?: string;
+}
+export type RoleListPageParam = PageParams<RoleListFilter>;
+
+// 权限项目
+export interface RolePrivilegeItem {
+  // 权限编码
+  code: string;
+  // 权限名称
+  name: string;
+  // 是否勾选
+  enable: boolean;
+  // 父级权限编码
+  parentCode: string;
+}
+export interface RolePrivilegeUpdateParam {
+  // 角色编码
+  role: string;
+  schoolId: number;
+  // 选中的权限编码
+  selectPrivileges: string[];
+}

+ 31 - 0
src/api/user.ts

@@ -11,6 +11,11 @@ import type {
   EnableUserParam,
   BatchCreateUserParam,
   SystemInfo,
+  RoleListPageRes,
+  RolePrivilegeItem,
+  RolePrivilegeUpdateParam,
+  RoleListPageParam,
+  RoleItem,
 } from './types/user';
 
 // 登录
@@ -136,3 +141,29 @@ export function headerInspectorTemplate(): Promise<AxiosResponse<Blob>> {
     }
   );
 }
+
+// 角色权限 ------->
+// 获取角色列表
+export function getRoleList(
+  params: RoleListPageParam
+): Promise<RoleListPageRes> {
+  return axios.post('/api/admin/role/page', {}, { params });
+}
+// 获取所有角色
+export function getAllRoleList(schoolId: number): Promise<RoleItem[]> {
+  return axios.post('/api/admin/role/list/all', {}, { params: { schoolId } });
+}
+
+// 获取权限列表
+export function getRolePrivilegeList(params: {
+  schoolId: number;
+  role: string;
+}): Promise<RolePrivilegeItem[]> {
+  return axios.post('/api/admin/role/privilege/all', {}, { params });
+}
+// 保存角色权限
+export function updateRolePrivilege(
+  data: RolePrivilegeUpdateParam
+): Promise<boolean> {
+  return axios.post('/api/admin/role/privilege/save', data);
+}

+ 10 - 0
src/router/routes/modules/admin.ts

@@ -18,6 +18,16 @@ const ADMIN: AppRouteRecordRaw = {
         requiresAuth: true,
       },
     },
+    {
+      path: 'role-manage/:schoolId',
+      name: 'RoleManage',
+      component: () => import('@/views/admin/role-manage/RoleManage.vue'),
+      meta: {
+        title: '角色管理',
+        requiresAuth: true,
+        relationRoutes: ['SchoolManage'],
+      },
+    },
     {
       path: 'set-manage',
       name: 'SetManage',

+ 262 - 0
src/views/admin/role-manage/ModifyRolePrivilege.vue

@@ -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>

+ 110 - 0
src/views/admin/role-manage/RoleManage.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="part-box is-filter">
+    <el-form inline>
+      <el-form-item label="角色">
+        <el-select
+          v-model="searchModel.role"
+          placeholder="请选择角色"
+          clearable
+          filterable
+        >
+          <el-option
+            v-for="item in roleList"
+            :key="item.code"
+            :label="item.name"
+            :value="item.code"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="toPage(1)">查询</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+  <div class="part-box">
+    <el-table
+      class="page-table"
+      :data="dataList"
+      :loading="loading"
+      border
+      stripe
+    >
+      <el-table-column prop="name" label="角色名称" min-width="120" />
+      <el-table-column prop="code" label="角色编码" min-width="120" />
+      <el-table-column prop="updateTime" label="更新时间" width="180" />
+      <el-table-column prop="updateName" label="更新人" width="120" />
+      <el-table-column label="操作" width="120" fixed="right">
+        <template #default="scope">
+          <el-button
+            size="small"
+            type="primary"
+            link
+            @click="onEdit(scope.row)"
+          >
+            编辑
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      v-model:current-page="pagination.pageNumber"
+      v-model:page-size="pagination.pageSize"
+      :layout="pagination.layout"
+      :total="pagination.total"
+      @size-change="pageSizeChange"
+      @current-change="toPage"
+    />
+  </div>
+
+  <!-- 编辑角色权限弹窗 -->
+  <ModifyRolePrivilege
+    ref="modifyRolePrivilegeRef"
+    :row-data="curRow"
+    :school-id="searchModel.schoolId"
+    @modified="getList"
+  />
+</template>
+
+<script setup lang="ts">
+  import { onMounted, reactive, ref } from 'vue';
+  import { useRoute } from 'vue-router';
+  import { getRoleList, getAllRoleList } from '@/api/user';
+  import { RoleItem, RoleListFilter } from '@/api/types/user';
+  import useTable from '@/hooks/table';
+
+  import ModifyRolePrivilege from './ModifyRolePrivilege.vue';
+
+  defineOptions({
+    name: 'RoleManage',
+  });
+
+  const route = useRoute();
+
+  const searchModel = reactive<RoleListFilter>({
+    schoolId: Number(route.params.schoolId),
+    role: '',
+  });
+
+  const { dataList, pagination, loading, getList, toPage, pageSizeChange } =
+    useTable<RoleItem>(getRoleList, searchModel, false);
+
+  const roleList = ref([] as RoleItem[]);
+
+  // 获取角色列表
+  async function fetchAllRoleList() {
+    roleList.value = await getAllRoleList(searchModel.schoolId);
+  }
+
+  // table action
+  const curRow = ref({} as RoleItem);
+  const modifyRolePrivilegeRef = ref();
+
+  function onEdit(row: RoleItem) {
+    curRow.value = row;
+    modifyRolePrivilegeRef.value?.open();
+  }
+
+  onMounted(async () => {
+    await fetchAllRoleList();
+  });
+</script>

+ 9 - 2
src/views/admin/school-manage/SchoolManage.vue

@@ -82,6 +82,7 @@
 
 <script setup lang="ts">
   import { reactive, ref } from 'vue';
+  import { useRoute } from 'vue-router';
   import { ElMessage } from 'element-plus';
   import { schoolListPage } from '@/api/admin';
   import { SchoolItem, SchoolListFilter } from '@/api/types/admin';
@@ -99,6 +100,8 @@
     name: 'SchoolManage',
   });
 
+  const route = useRoute();
+
   // const appStore = useAppStore();
   // const userStore = useUserStore();
 
@@ -130,8 +133,12 @@
     modifySchoolRef.value?.open();
   }
   function onEditRoleAuth(row: SchoolItem) {
-    // TODO:权限编辑
-    console.log(row);
+    route.push({
+      name: 'RoleManage',
+      params: {
+        schoolId: row.id,
+      },
+    });
   }
   // 导入
   const importDialogRef = ref();