168 lines
4.2 KiB
Vue
168 lines
4.2 KiB
Vue
<template>
|
|
<el-dialog v-model="visible" title="角色详情" width="600px" @close="handleClose">
|
|
<el-descriptions :column="1" border v-loading="loading">
|
|
<el-descriptions-item label="角色ID">
|
|
{{ roleDetail.id }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="角色名称">
|
|
{{ roleDetail.name }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="状态">
|
|
<el-tag :type="roleDetail.status === 1 ? 'success' : 'info'" size="small">
|
|
{{ roleDetail.status === 1 ? "启用" : "禁用" }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="权限列表">
|
|
<div v-if="menuNames.length > 0" style="max-height: 300px; overflow-y: auto;">
|
|
<el-tag
|
|
v-for="(name, index) in menuNames"
|
|
:key="index"
|
|
style="margin: 4px;"
|
|
size="small"
|
|
>
|
|
{{ name }}
|
|
</el-tag>
|
|
</div>
|
|
<span v-else style="color: var(--el-text-color-secondary);">
|
|
暂无权限
|
|
</span>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="创建时间">
|
|
{{ roleDetail.create_time }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="更新时间">
|
|
{{ roleDetail.update_time }}
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
|
|
<template #footer>
|
|
<el-button @click="handleClose">关闭</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch, computed } from "vue";
|
|
import { ElMessage } from "element-plus";
|
|
import { getRoleById } from "@/api/role";
|
|
import { getAllMenus } from "@/api/menu";
|
|
|
|
interface Props {
|
|
modelValue: boolean;
|
|
roleId?: number | null;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
modelValue: false,
|
|
roleId: null,
|
|
});
|
|
|
|
const emit = defineEmits(["update:modelValue"]);
|
|
|
|
const visible = ref(false);
|
|
const loading = ref(false);
|
|
const roleDetail = ref<any>({});
|
|
const allMenus = ref<any[]>([]);
|
|
|
|
// 监听 modelValue
|
|
watch(
|
|
() => props.modelValue,
|
|
(val) => {
|
|
visible.value = val;
|
|
if (val && props.roleId) {
|
|
loadRoleDetail();
|
|
loadMenus();
|
|
}
|
|
}
|
|
);
|
|
|
|
// 监听 visible
|
|
watch(visible, (val) => {
|
|
emit("update:modelValue", val);
|
|
});
|
|
|
|
// 解析权限ID
|
|
const parseRights = (rights: any): number[] => {
|
|
if (!rights) return [];
|
|
if (Array.isArray(rights)) return rights;
|
|
if (typeof rights === "string") {
|
|
try {
|
|
const parsed = JSON.parse(rights);
|
|
return Array.isArray(parsed) ? parsed : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
return [];
|
|
};
|
|
|
|
// 递归获取所有菜单ID和名称的映射
|
|
const getMenuMap = (menus: any[]): Map<number, string> => {
|
|
const map = new Map<number, string>();
|
|
const traverse = (items: any[]) => {
|
|
for (const item of items) {
|
|
map.set(item.id, item.title || item.name || `菜单${item.id}`);
|
|
if (item.children && item.children.length > 0) {
|
|
traverse(item.children);
|
|
}
|
|
}
|
|
};
|
|
traverse(menus);
|
|
return map;
|
|
};
|
|
|
|
// 计算菜单名称列表
|
|
const menuNames = computed(() => {
|
|
if (!roleDetail.value.rights) return [];
|
|
const rightIds = parseRights(roleDetail.value.rights);
|
|
const menuMap = getMenuMap(allMenus.value);
|
|
return rightIds
|
|
.map((id) => menuMap.get(id))
|
|
.filter((name) => name !== undefined) as string[];
|
|
});
|
|
|
|
// 加载角色详情
|
|
const loadRoleDetail = async () => {
|
|
if (!props.roleId) return;
|
|
|
|
loading.value = true;
|
|
try {
|
|
const res = await getRoleById(props.roleId);
|
|
if (res.code === 200) {
|
|
roleDetail.value = res.data || {};
|
|
} else {
|
|
ElMessage.error(res.msg || "获取角色详情失败");
|
|
}
|
|
} catch (error) {
|
|
console.error("加载角色详情失败:", error);
|
|
ElMessage.error("获取角色详情失败");
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
// 加载菜单树
|
|
const loadMenus = async () => {
|
|
try {
|
|
const res = await getAllMenus();
|
|
if (res.code === 200) {
|
|
allMenus.value = res.data || [];
|
|
}
|
|
} catch (error) {
|
|
console.error("加载菜单失败:", error);
|
|
}
|
|
};
|
|
|
|
// 关闭对话框
|
|
const handleClose = () => {
|
|
visible.value = false;
|
|
roleDetail.value = {};
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
:deep(.el-descriptions__label) {
|
|
width: 120px;
|
|
}
|
|
</style>
|