|
@@ -0,0 +1,288 @@
|
|
|
+<template>
|
|
|
+ <div class="permission-config">
|
|
|
+ <div class="config-header" @click="togglePanel">
|
|
|
+ <img src="../../assets/lock.png" alt="permission" class="config-icon" />
|
|
|
+ <span>权限配置</span>
|
|
|
+ <img
|
|
|
+ :src="isPanelOpen ? upIcon : downIcon"
|
|
|
+ alt="toggle"
|
|
|
+ class="toggle-icon"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-show="isPanelOpen" class="config-panel">
|
|
|
+ <div class="permission-list">
|
|
|
+ <div class="permission-group" v-for="group in permissionGroups" :key="group.label">
|
|
|
+ <div class="group-header">
|
|
|
+ <a-checkbox :value="group.value"
|
|
|
+ :checked="getParentChecked(group)"
|
|
|
+ @change="(e) => handleParentChange(group, e)">
|
|
|
+ {{ group.label }}
|
|
|
+ </a-checkbox>
|
|
|
+ </div>
|
|
|
+ <div class="checkbox-list">
|
|
|
+ <a-checkbox-group v-model:value="selectedPermissions[group.key]">
|
|
|
+ <div v-for="item in group.items" :key="item.value" class="checkbox-item">
|
|
|
+ <a-checkbox :value="item.value">
|
|
|
+ {{ item.label }}
|
|
|
+ <span class="item-desc">{{ item.description }}</span>
|
|
|
+ </a-checkbox>
|
|
|
+ </div>
|
|
|
+ </a-checkbox-group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="action-buttons">
|
|
|
+ <a-button type="primary" :loading="saving" @click="saveConfig">
|
|
|
+ 保存配置
|
|
|
+ </a-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, reactive, onMounted } from 'vue';
|
|
|
+import { message } from 'ant-design-vue';
|
|
|
+import axios from 'axios';
|
|
|
+import upIcon from '@/assets/svg/ups.svg';
|
|
|
+import downIcon from '@/assets/svg/down.svg';
|
|
|
+
|
|
|
+// 计算父级选中状态
|
|
|
+const getParentChecked = (group) => {
|
|
|
+ if (!selectedPermissions[group.key] || !group.items || group.items.length === 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 如果所有子项都被选中,则父级也选中
|
|
|
+ return group.items.every(item =>
|
|
|
+ selectedPermissions[group.key].includes(item.value)
|
|
|
+ );
|
|
|
+};
|
|
|
+// 处理父级选中状态变化
|
|
|
+const handleParentChange = (parent, e) => {
|
|
|
+ const checked = e.target.checked;
|
|
|
+ // 获取当前组的选中状态数组
|
|
|
+ if (!selectedPermissions[parent.key]) {
|
|
|
+ selectedPermissions[parent.key] = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果父级被选中,将所有子项添加到选中数组
|
|
|
+ if (checked) {
|
|
|
+ selectedPermissions[parent.key] = parent.items.map(item => item.value);
|
|
|
+ } else {
|
|
|
+ // 如果父级取消选中,清空该组的选中数组
|
|
|
+ selectedPermissions[parent.key] = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 权限组数据
|
|
|
+const permissionGroups = ref([]);
|
|
|
+const isPanelOpen = ref(false);
|
|
|
+const saving = ref(false);
|
|
|
+
|
|
|
+// 用于存储每个权限组的选中状态
|
|
|
+const selectedPermissions = reactive({});
|
|
|
+
|
|
|
+const togglePanel = () => {
|
|
|
+ isPanelOpen.value = !isPanelOpen.value;
|
|
|
+};
|
|
|
+
|
|
|
+const saveConfig = async () => {
|
|
|
+ saving.value = true;
|
|
|
+ try {
|
|
|
+ // 保存配置到 localStorage
|
|
|
+ localStorage.setItem('permissionConfig', JSON.stringify(selectedPermissions));
|
|
|
+ message.success('权限配置保存成功');
|
|
|
+ } catch (error) {
|
|
|
+ message.error('权限配置保存失败');
|
|
|
+ } finally {
|
|
|
+ saving.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 初始化配置
|
|
|
+const initConfig = () => {
|
|
|
+ const savedConfig = localStorage.getItem('permissionConfig');
|
|
|
+ if (savedConfig) {
|
|
|
+ const config = JSON.parse(savedConfig);
|
|
|
+ Object.keys(config).forEach(key => {
|
|
|
+ selectedPermissions[key] = config[key];
|
|
|
+ });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取权限列表
|
|
|
+const fetchPermissionGroups = async () => {
|
|
|
+ try {
|
|
|
+ const response = await axios.get(`https://backend.qicai321.com/api/system/role_menu_button_permission/get_role_menu/?roleId=5`,{
|
|
|
+ headers: {
|
|
|
+ 'authorization': `JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzg2Njg0Nzk0LCJpYXQiOjE3NTUxNDg3OTQsImp0aSI6IjdlYTI0Yzg2N2IzNTQ5NDA5Y2RlZGM1NGI5MGMxYWQzIiwidXNlcl9pZCI6MX0._yM1AMRyxVyf2J2-Kz9PlAErkXhEV5bfz9hBVkIg6OQ`
|
|
|
+ }
|
|
|
+ });
|
|
|
+ console.log(response);
|
|
|
+ // 构建权限树
|
|
|
+ const buildPermissionTree = (permissions) => {
|
|
|
+ // 创建一个映射表,用于快速查找权限项
|
|
|
+ const permMap = {};
|
|
|
+ permissions.forEach(perm => {
|
|
|
+ permMap[perm.id] = {
|
|
|
+ label: perm.name,
|
|
|
+ value: perm.id,
|
|
|
+ is_catalog: perm.is_catalog,
|
|
|
+ isCheck: perm.isCheck,
|
|
|
+ children: []
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ // 构建树形结构
|
|
|
+ const tree = [];
|
|
|
+ permissions.forEach(perm => {
|
|
|
+ const currentNode = permMap[perm.id];
|
|
|
+ if (perm.parent === null) {
|
|
|
+ // 如果没有父节点,则为根节点
|
|
|
+ tree.push(currentNode);
|
|
|
+ } else {
|
|
|
+ // 如果有父节点,将当前节点添加到父节点的children中
|
|
|
+ const parentNode = permMap[perm.parent];
|
|
|
+ if (parentNode) {
|
|
|
+ parentNode.children.push(currentNode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return tree;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理权限数据
|
|
|
+ const permissionTree = buildPermissionTree(response.data.data);
|
|
|
+
|
|
|
+ // 转换为组件所需的数据格式
|
|
|
+ const groupsData = permissionTree.map(node => ({
|
|
|
+ label: node.label,
|
|
|
+ key: node.value,
|
|
|
+ is_catalog: node.is_catalog,
|
|
|
+ value: node.isCheck,
|
|
|
+
|
|
|
+ items: node.children.map(child => ({
|
|
|
+ label: child.label,
|
|
|
+ value: child.value,
|
|
|
+ is_catalog: child.is_catalog,
|
|
|
+ isCheck: child.isCheck
|
|
|
+ }))
|
|
|
+ }));
|
|
|
+
|
|
|
+ permissionGroups.value = groupsData;
|
|
|
+
|
|
|
+ // 初始化选中状态对象
|
|
|
+ permissionGroups.value.forEach(group => {
|
|
|
+ if (!selectedPermissions[group.key]) {
|
|
|
+ selectedPermissions[group.key] = [];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ message.error('获取权限信息失败');
|
|
|
+ console.error('获取权限信息失败:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 组件挂载时获取数据
|
|
|
+onMounted(async () => {
|
|
|
+ await fetchPermissionGroups();
|
|
|
+ initConfig();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.permission-config {
|
|
|
+ border-top: 1px solid rgba(0, 0, 0, 0.06);
|
|
|
+ background: var(--secondary-bg);
|
|
|
+ color: var(--primary-text);
|
|
|
+}
|
|
|
+
|
|
|
+.config-header {
|
|
|
+ padding: 12px 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background-color 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.config-header:hover {
|
|
|
+ background: rgba(0, 0, 0, 0.02);
|
|
|
+}
|
|
|
+
|
|
|
+.config-icon {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.toggle-icon {
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ margin-left: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.config-panel {
|
|
|
+ padding: 12px 20px;
|
|
|
+ border-top: 1px solid rgba(0, 0, 0, 0.06);
|
|
|
+}
|
|
|
+
|
|
|
+.permission-list {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ height: 450px;
|
|
|
+ overflow: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.permission-group {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.permission-group:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.group-header {
|
|
|
+ font-size: 13px;
|
|
|
+ color: var(--primary-text);
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.checkbox-list {
|
|
|
+margin-left: 14px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.checkbox-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+.item-desc {
|
|
|
+ margin-left: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--primary-text);
|
|
|
+}
|
|
|
+
|
|
|
+.action-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-top: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-checkbox-wrapper) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-checkbox-group) {
|
|
|
+ width: 100%;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-checkbox-wrapper + .ant-checkbox-wrapper) {
|
|
|
+ margin-left: 0;
|
|
|
+}
|
|
|
+</style>
|