|
|
@@ -40,15 +40,25 @@
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
|
|
|
- <!-- 权限列表 -->
|
|
|
- <el-form-item label="权限" prop="permissions" v-if="permissionList.length > 0">
|
|
|
- <div class="permission-list">
|
|
|
- <template v-if="permissionList.length > 0">
|
|
|
- <PermissionItem v-for="permission in permissionList" :key="permission.id" :permission="permission" :level="0" />
|
|
|
- </template>
|
|
|
- <div v-else class="permission-empty">
|
|
|
- <span class="empty-text">暂无权限</span>
|
|
|
- </div>
|
|
|
+ <!-- 权限展示:与角色管理相同的树状图,仅禁用展示 -->
|
|
|
+ <el-form-item label="权限" v-if="subAccountForm.roleId && permissionTreeData.length > 0">
|
|
|
+ <div class="permission-tree-container">
|
|
|
+ <el-tree
|
|
|
+ ref="permissionTreeRef"
|
|
|
+ :data="permissionTreeData"
|
|
|
+ :props="treeProps"
|
|
|
+ show-checkbox
|
|
|
+ node-key="id"
|
|
|
+ :default-expand-all="true"
|
|
|
+ :default-checked-keys="defaultCheckedKeys"
|
|
|
+ :expand-on-click-node="false"
|
|
|
+ >
|
|
|
+ <template #default="{ data }">
|
|
|
+ <span class="tree-node-label">
|
|
|
+ <span>{{ data.label }}</span>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-tree>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
</div>
|
|
|
@@ -66,7 +76,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts" name="subAccountCreate">
|
|
|
-import { ref, reactive, onMounted, computed } from "vue";
|
|
|
+import { ref, reactive, onMounted, computed, nextTick } from "vue";
|
|
|
import { useRouter, useRoute } from "vue-router";
|
|
|
import { ElMessage, type FormInstance, type FormRules } from "element-plus";
|
|
|
import { ArrowLeft } from "@element-plus/icons-vue";
|
|
|
@@ -80,7 +90,6 @@ import {
|
|
|
updateSubAccount
|
|
|
} from "@/api/modules/accountRoleManagement";
|
|
|
import { localGet } from "@/utils";
|
|
|
-import PermissionItem from "./PermissionItem.vue";
|
|
|
// 路由
|
|
|
const router = useRouter();
|
|
|
const route = useRoute();
|
|
|
@@ -98,7 +107,6 @@ const subAccountForm = reactive({
|
|
|
accountName: "",
|
|
|
phone: "",
|
|
|
roleId: undefined as number | undefined,
|
|
|
- permissions: [] as number[],
|
|
|
userId: undefined as number | undefined
|
|
|
});
|
|
|
|
|
|
@@ -115,17 +123,17 @@ const subAccountFormRules: FormRules = {
|
|
|
// 角色列表
|
|
|
const roleList = ref<RoleItem[]>([]);
|
|
|
|
|
|
-// 权限项接口
|
|
|
-interface PermissionItem {
|
|
|
- id: number;
|
|
|
- name: string;
|
|
|
- checked: boolean;
|
|
|
- expanded: boolean;
|
|
|
- children?: PermissionItem[];
|
|
|
-}
|
|
|
+// 树形组件配置(disabled 用于只读展示)
|
|
|
+const treeProps = {
|
|
|
+ children: "children",
|
|
|
+ label: "label",
|
|
|
+ disabled: "disabled"
|
|
|
+};
|
|
|
|
|
|
-// 权限列表
|
|
|
-const permissionList = ref<PermissionItem[]>([]);
|
|
|
+// 权限树数据(与角色管理同结构,节点全部 disabled)
|
|
|
+const permissionTreeData = ref<any[]>([]);
|
|
|
+const permissionTreeRef = ref<any>();
|
|
|
+const defaultCheckedKeys = ref<number[]>([]);
|
|
|
|
|
|
// 保存加载状态
|
|
|
const saveLoading = ref(false);
|
|
|
@@ -155,155 +163,96 @@ const loadRoleList = async () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 将扁平格式的权限数据转换为树形结构
|
|
|
-const transformFlatPermissionsToTree = (permissionList: RolePermissionItem[]): PermissionItem[] => {
|
|
|
- const level1Map = new Map<string, PermissionItem>();
|
|
|
- const level2Map = new Map<string, PermissionItem>();
|
|
|
- const level3Map = new Map<string, PermissionItem>();
|
|
|
+// 将角色权限(仅已选中的)转成树结构,不包含未选中的节点
|
|
|
+const transformRolePermissionsToTree = (list: RolePermissionItem[]): { tree: any[]; allIds: number[] } => {
|
|
|
+ const level1Map = new Map<string, any>();
|
|
|
+ const level2Map = new Map<string, any>();
|
|
|
+ const level3Map = new Map<string, any>();
|
|
|
+ const allIds: number[] = [];
|
|
|
let idCounter = 1;
|
|
|
|
|
|
- // 遍历所有权限项,构建树形结构
|
|
|
- permissionList.forEach(item => {
|
|
|
- // 处理一级权限
|
|
|
- if (item.level1Permission) {
|
|
|
- let level1 = level1Map.get(item.level1Permission);
|
|
|
-
|
|
|
- if (!level1) {
|
|
|
- level1 = {
|
|
|
- id: idCounter++,
|
|
|
- name: item.level1Permission,
|
|
|
- checked: false,
|
|
|
- expanded: true, // 一级权限默认展开
|
|
|
- children: []
|
|
|
- };
|
|
|
- level1Map.set(item.level1Permission, level1);
|
|
|
- }
|
|
|
+ for (const item of list || []) {
|
|
|
+ if (!item.level1Permission) continue;
|
|
|
|
|
|
- // 处理二级权限
|
|
|
- if (item.level2Permission) {
|
|
|
- const level2Key = `${item.level1Permission}-${item.level2Permission}`;
|
|
|
- let level2 = level2Map.get(level2Key);
|
|
|
-
|
|
|
- if (!level2) {
|
|
|
- level2 = {
|
|
|
- id: idCounter++,
|
|
|
- name: item.level2Permission,
|
|
|
- checked: false,
|
|
|
- expanded: false,
|
|
|
- children: []
|
|
|
- };
|
|
|
- level2Map.set(level2Key, level2);
|
|
|
-
|
|
|
- // 确保一级权限的children数组存在并添加二级权限
|
|
|
- if (!level1.children) {
|
|
|
- level1.children = [];
|
|
|
- }
|
|
|
- // 检查是否已存在,避免重复添加
|
|
|
- const existingLevel2 = level1.children.find(child => child.id === level2!.id);
|
|
|
- if (!existingLevel2) {
|
|
|
- level1.children.push(level2);
|
|
|
- }
|
|
|
- }
|
|
|
+ let level1 = level1Map.get(item.level1Permission);
|
|
|
+ if (!level1) {
|
|
|
+ level1 = { id: idCounter++, label: item.level1Permission, disabled: true, children: [] };
|
|
|
+ allIds.push(level1.id);
|
|
|
+ level1Map.set(item.level1Permission, level1);
|
|
|
+ }
|
|
|
|
|
|
- // 处理三级权限
|
|
|
- if (item.level3Permission) {
|
|
|
- // 重新获取 level2,确保它存在
|
|
|
- const currentLevel2 = level2Map.get(level2Key);
|
|
|
-
|
|
|
- if (currentLevel2) {
|
|
|
- const level3Key = `${level2Key}-${item.level3Permission}`;
|
|
|
- let level3 = level3Map.get(level3Key);
|
|
|
-
|
|
|
- if (!level3) {
|
|
|
- level3 = {
|
|
|
- id: idCounter++,
|
|
|
- name: item.level3Permission,
|
|
|
- checked: false,
|
|
|
- expanded: false
|
|
|
- };
|
|
|
- level3Map.set(level3Key, level3);
|
|
|
-
|
|
|
- // 确保二级权限的children数组存在并添加三级权限
|
|
|
- if (!currentLevel2.children) {
|
|
|
- currentLevel2.children = [];
|
|
|
- }
|
|
|
- // 检查是否已存在,避免重复添加
|
|
|
- const existingLevel3 = currentLevel2.children.find(child => child.id === level3!.id);
|
|
|
- if (!existingLevel3) {
|
|
|
- currentLevel2.children.push(level3);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if (item.level2Permission) {
|
|
|
+ const k2 = `${item.level1Permission}-${item.level2Permission}`;
|
|
|
+ let level2 = level2Map.get(k2);
|
|
|
+ if (!level2) {
|
|
|
+ level2 = { id: idCounter++, label: item.level2Permission, disabled: true, children: [] };
|
|
|
+ allIds.push(level2.id);
|
|
|
+ level2Map.set(k2, level2);
|
|
|
+ level1.children.push(level2);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.level3Permission?.trim()) {
|
|
|
+ const names = item.level3Permission
|
|
|
+ .split(/\s+/)
|
|
|
+ .map(s => s.trim())
|
|
|
+ .filter(Boolean);
|
|
|
+ for (const name of names) {
|
|
|
+ const k3 = `${k2}-${name}`;
|
|
|
+ if (level3Map.has(k3)) continue;
|
|
|
+ const level3 = { id: idCounter++, label: name, disabled: true };
|
|
|
+ allIds.push(level3.id);
|
|
|
+ level3Map.set(k3, level3);
|
|
|
+ level2.children.push(level3);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
|
|
|
- // 返回所有一级权限
|
|
|
- return Array.from(level1Map.values());
|
|
|
+ return { tree: Array.from(level1Map.values()), allIds };
|
|
|
};
|
|
|
|
|
|
-// 加载权限列表
|
|
|
+// 加载当前角色权限:只展示已选中的权限树(不展示未选中的)
|
|
|
const loadPermissionList = async () => {
|
|
|
if (!subAccountForm.roleId) {
|
|
|
- permissionList.value = [];
|
|
|
+ permissionTreeData.value = [];
|
|
|
+ defaultCheckedKeys.value = [];
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
const roleId = Number(subAccountForm.roleId);
|
|
|
if (!roleId || isNaN(roleId)) {
|
|
|
- permissionList.value = [];
|
|
|
+ permissionTreeData.value = [];
|
|
|
+ defaultCheckedKeys.value = [];
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const res = await getRolePermissionTable({ roleId });
|
|
|
const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
|
|
|
if (code === 200 && res.data) {
|
|
|
- permissionList.value = transformFlatPermissionsToTree(res.data);
|
|
|
+ const { tree, allIds } = transformRolePermissionsToTree(res.data);
|
|
|
+ permissionTreeData.value = tree;
|
|
|
+ defaultCheckedKeys.value = allIds;
|
|
|
+ nextTick(() => {
|
|
|
+ permissionTreeRef.value?.setCheckedKeys(allIds);
|
|
|
+ });
|
|
|
} else {
|
|
|
ElMessage.error(res.msg || "获取权限列表失败");
|
|
|
- permissionList.value = [];
|
|
|
+ permissionTreeData.value = [];
|
|
|
+ defaultCheckedKeys.value = [];
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error("获取权限列表失败:", error);
|
|
|
ElMessage.error("获取权限列表失败,请重试");
|
|
|
- permissionList.value = [];
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-// 递归收集所有选中的权限ID
|
|
|
-const collectCheckedPermissions = (permission: PermissionItem): number[] => {
|
|
|
- const permissions: number[] = [];
|
|
|
- if (permission.checked) {
|
|
|
- permissions.push(permission.id);
|
|
|
- }
|
|
|
- if (permission.children) {
|
|
|
- permission.children.forEach(child => {
|
|
|
- permissions.push(...collectCheckedPermissions(child));
|
|
|
- });
|
|
|
+ permissionTreeData.value = [];
|
|
|
+ defaultCheckedKeys.value = [];
|
|
|
}
|
|
|
- return permissions;
|
|
|
-};
|
|
|
-
|
|
|
-// 重置权限列表选中状态
|
|
|
-const resetPermissions = () => {
|
|
|
- const resetItem = (item: PermissionItem) => {
|
|
|
- item.checked = false;
|
|
|
- if (item.children) {
|
|
|
- item.children.forEach(child => resetItem(child));
|
|
|
- }
|
|
|
- };
|
|
|
- permissionList.value.forEach(item => resetItem(item));
|
|
|
- subAccountForm.permissions = [];
|
|
|
};
|
|
|
|
|
|
// 初始化数据
|
|
|
onMounted(() => {
|
|
|
loadRoleList();
|
|
|
- // 不在这里加载权限列表,等用户选择角色后再加载
|
|
|
if (isEdit.value || isView.value) {
|
|
|
- // 编辑或查看模式:加载子账号数据
|
|
|
loadSubAccountData();
|
|
|
}
|
|
|
});
|
|
|
@@ -330,16 +279,9 @@ const loadSubAccountData = async () => {
|
|
|
subAccountForm.roleId = accountData.roleId;
|
|
|
subAccountForm.userId = accountData.userId || Number(accountId.value);
|
|
|
|
|
|
- // 如果有权限数据,先加载角色权限列表,然后设置选中状态
|
|
|
+ // 按角色加载权限树勾选(权限由角色决定,仅展示)
|
|
|
if (subAccountForm.roleId) {
|
|
|
await loadPermissionList();
|
|
|
-
|
|
|
- // 如果接口返回了权限ID列表,设置选中状态
|
|
|
- if (accountData.permissions && Array.isArray(accountData.permissions)) {
|
|
|
- const permissionIds = accountData.permissions.map((p: any) => (typeof p === "object" ? p.id || p.permissionId : p));
|
|
|
- subAccountForm.permissions = permissionIds;
|
|
|
- setPermissionsChecked(permissionIds);
|
|
|
- }
|
|
|
}
|
|
|
} else {
|
|
|
ElMessage.error(res.msg || "获取子账号详情失败");
|
|
|
@@ -350,19 +292,6 @@ const loadSubAccountData = async () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 设置权限选中状态
|
|
|
-const setPermissionsChecked = (permissionIds: number[]) => {
|
|
|
- const setItemChecked = (item: PermissionItem) => {
|
|
|
- if (permissionIds.includes(item.id)) {
|
|
|
- item.checked = true;
|
|
|
- }
|
|
|
- if (item.children) {
|
|
|
- item.children.forEach(child => setItemChecked(child));
|
|
|
- }
|
|
|
- };
|
|
|
- permissionList.value.forEach(item => setItemChecked(item));
|
|
|
-};
|
|
|
-
|
|
|
// 返回
|
|
|
const handleBack = () => {
|
|
|
router.back();
|
|
|
@@ -401,13 +330,11 @@ const handleSave = async () => {
|
|
|
};
|
|
|
const res = await updateSubAccount(updateAccountDto);
|
|
|
const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
|
|
|
- if (code === 200) {
|
|
|
+ if (code == 200) {
|
|
|
ElMessage.success("编辑成功");
|
|
|
setTimeout(() => {
|
|
|
router.push("/accountRoleManagement/subAccountManagement");
|
|
|
}, 1000);
|
|
|
- } else {
|
|
|
- ElMessage.error(res.msg || "编辑失败");
|
|
|
}
|
|
|
} else {
|
|
|
// 创建模式:调用创建接口
|
|
|
@@ -420,17 +347,14 @@ const handleSave = async () => {
|
|
|
const res = await createAccountAndAssignRole(createAccountDto);
|
|
|
const code = typeof res.code === "string" ? parseInt(res.code) : res.code;
|
|
|
if (code === 200) {
|
|
|
- ElMessage.success("创建成功");
|
|
|
+ ElMessage.success("创建成功后,员工可以使用手机号与验证码进行登录");
|
|
|
setTimeout(() => {
|
|
|
router.push("/accountRoleManagement/subAccountManagement");
|
|
|
}, 1000);
|
|
|
- } else {
|
|
|
- ElMessage.error(res.msg || "创建失败");
|
|
|
}
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error("保存失败:", error);
|
|
|
- ElMessage.error("保存失败,请重试");
|
|
|
} finally {
|
|
|
saveLoading.value = false;
|
|
|
}
|
|
|
@@ -439,12 +363,12 @@ const handleSave = async () => {
|
|
|
// 处理角色选择变化
|
|
|
const handleRoleChange = (value: number | null | undefined) => {
|
|
|
subAccountForm.roleId = value as number | undefined;
|
|
|
- permissionList.value = [];
|
|
|
+ permissionTreeData.value = [];
|
|
|
+ defaultCheckedKeys.value = [];
|
|
|
if (value) {
|
|
|
loadPermissionList();
|
|
|
} else {
|
|
|
- // 如果清空了角色选择,重置权限列表
|
|
|
- resetPermissions();
|
|
|
+ nextTick(() => permissionTreeRef.value?.setCheckedKeys([]));
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
@@ -484,18 +408,51 @@ const handleRoleChange = (value: number | null | undefined) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 权限列表
|
|
|
- .permission-list {
|
|
|
+ // 权限树(与角色管理同款样式,仅禁用展示)
|
|
|
+ .permission-tree-container {
|
|
|
width: 100%;
|
|
|
- .permission-empty {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- padding: 40px 0;
|
|
|
- color: var(--el-text-color-placeholder);
|
|
|
- .empty-text {
|
|
|
+ min-height: 200px;
|
|
|
+ max-height: 500px;
|
|
|
+ padding: 16px;
|
|
|
+ overflow-y: auto;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ border-radius: 4px;
|
|
|
+ :deep(.el-tree) {
|
|
|
+ background: transparent;
|
|
|
+ .el-tree-node {
|
|
|
+ .el-tree-node__content {
|
|
|
+ height: 40px;
|
|
|
+ padding: 0 12px;
|
|
|
+ margin-bottom: 2px;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: background-color 0.2s;
|
|
|
+ &:hover {
|
|
|
+ background-color: #ecf5ff;
|
|
|
+ }
|
|
|
+ .tree-node-label {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 14px;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .el-checkbox {
|
|
|
+ margin-right: 8px;
|
|
|
+ }
|
|
|
+ .el-tree-node__children {
|
|
|
+ padding-left: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .el-tree-node__expand-icon {
|
|
|
+ margin-right: 6px;
|
|
|
font-size: 14px;
|
|
|
- color: var(--el-text-color-secondary);
|
|
|
+ color: #909399;
|
|
|
+ transform: translateX(-5px);
|
|
|
+ }
|
|
|
+ .el-tree-node__expand-icon.is-leaf {
|
|
|
+ display: none;
|
|
|
}
|
|
|
}
|
|
|
}
|