refactor file tree
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<div v-can="{ value: [corePermissionCodes.role.read, corePermissionCodes.permission.read], mode: 'and' }" class="permission-list">
|
||||
<t-pull-down-refresh
|
||||
class="list-wrapper"
|
||||
v-model="isRefreshing"
|
||||
:refresh-timeout="30000"
|
||||
@refresh="refresh"
|
||||
>
|
||||
<div class="content">
|
||||
<t-cell-group class="header" theme="card">
|
||||
<t-cell title="当前账号角色聚合权限 P">
|
||||
<template #description>
|
||||
<div class="description">
|
||||
<div>你拥有以下权限,可执行权限对应的功能,这些权限来自上级角色直接授权或子角色继承。</div>
|
||||
<div class="legends">
|
||||
<div class="legend">
|
||||
<span class="block direct"></span>
|
||||
<span>上级角色直接授权</span>
|
||||
</div>
|
||||
<div class="legend">
|
||||
<span class="block inherited"></span>
|
||||
<span>子角色继承授权</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<t-cell-group v-if="sortedPermissions.length" theme="card">
|
||||
<t-swipe-cell
|
||||
v-for="item in sortedPermissions"
|
||||
:key="item.id"
|
||||
class="item"
|
||||
>
|
||||
<t-cell :title="item.name">
|
||||
<template #leftIcon>
|
||||
<span class="source" :class="`source-${item.source}`"/>
|
||||
</template>
|
||||
<template #note>
|
||||
<span class="code" v-text="formatPermissionCode(item)" />
|
||||
</template>
|
||||
</t-cell>
|
||||
</t-swipe-cell>
|
||||
</t-cell-group>
|
||||
<t-empty v-else-if="!permissionStore.loading && !isSourceLoading" description="暂无权限" />
|
||||
</div>
|
||||
</t-pull-down-refresh>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Role as UserRole, RoleAPI, useUserStore } from "timi-web";
|
||||
import type { Permission } from "@/store/permissionSettingsStore";
|
||||
import { moduleCode, usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||
import { corePermissionCodes } from "@/utils/permission";
|
||||
|
||||
defineOptions({
|
||||
name: "PermissionList"
|
||||
});
|
||||
|
||||
const permissionStore = usePermissionSettingsStore();
|
||||
const userStore = useUserStore();
|
||||
const isRefreshing = ref(false);
|
||||
const isSourceLoading = ref(false);
|
||||
|
||||
// ---------- 列表数据 ----------
|
||||
|
||||
type PermissionSource = "direct" | "inherited" | "unknown";
|
||||
type PermissionWithSource = Permission & {
|
||||
source: PermissionSource;
|
||||
};
|
||||
|
||||
const sourceRoleList = ref<UserRole[]>([]);
|
||||
const sortedPermissions = computed<PermissionWithSource[]>(() => {
|
||||
const permissionMap = new Map<string, PermissionWithSource>();
|
||||
for (const role of sourceRoleList.value) {
|
||||
for (const permission of getRoleAggregatePermissions(role)) {
|
||||
permissionMap.set(permission.id, {
|
||||
...permission,
|
||||
source: getPermissionSource(permission.id)
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const permission of getLoginUserPermissions()) {
|
||||
if (!permissionMap.has(permission.id)) {
|
||||
permissionMap.set(permission.id, {
|
||||
...permission,
|
||||
source: "unknown"
|
||||
});
|
||||
}
|
||||
}
|
||||
return Array.from(permissionMap.values()).sort((left, right) => formatPermissionCode(left).localeCompare(formatPermissionCode(right)));
|
||||
});
|
||||
|
||||
function getRoleAggregatePermissions(role: UserRole): Permission[] {
|
||||
if (role.allPermissionList?.length) {
|
||||
return role.allPermissionList;
|
||||
}
|
||||
if (role.permissionList?.length) {
|
||||
return role.permissionList;
|
||||
}
|
||||
return (role.permissionIdList || []).map(permissionStore.getPermissionById).filter(Boolean) as Permission[];
|
||||
}
|
||||
|
||||
function getLoginUserPermissions(): Permission[] {
|
||||
const permissionCodeSet = new Set(userStore.getPermissionList());
|
||||
return permissionStore.permissionList.filter(item => permissionCodeSet.has(formatPermissionCode(item)));
|
||||
}
|
||||
|
||||
function getPermissionSource(permissionId: string): PermissionSource {
|
||||
if (sourceRoleList.value.some(role => getDirectPermissionIdSet(role).has(permissionId))) {
|
||||
return "direct";
|
||||
}
|
||||
if (sourceRoleList.value.some(role => role.allPermissionList?.some(permission => permission.id === permissionId))) {
|
||||
return "inherited";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function getDirectPermissionIdSet(role: UserRole): Set<string> {
|
||||
if (role.permissionList?.length) {
|
||||
return new Set(role.permissionList.map(item => item.id));
|
||||
}
|
||||
return new Set(role.permissionIdList || []);
|
||||
}
|
||||
|
||||
function formatPermissionCode(permission: Permission): string {
|
||||
return `${permission.moduleCode}:${permission.code}`;
|
||||
}
|
||||
|
||||
// ---------- 页面数据 ----------
|
||||
|
||||
async function refresh(): Promise<void> {
|
||||
isRefreshing.value = true;
|
||||
try {
|
||||
await permissionStore.loadAll(true);
|
||||
await loadSourceRoles();
|
||||
} finally {
|
||||
isRefreshing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await permissionStore.loadAll();
|
||||
await loadSourceRoles();
|
||||
});
|
||||
|
||||
async function loadSourceRoles(): Promise<void> {
|
||||
isSourceLoading.value = true;
|
||||
try {
|
||||
sourceRoleList.value = await RoleAPI.currentDetailList(moduleCode);
|
||||
} finally {
|
||||
isSourceLoading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.permission-list {
|
||||
--source-direct-color: #2563EB;
|
||||
--source-inherited-color: #14B8A6;
|
||||
--source-unknown-color: #94A3B8;
|
||||
|
||||
padding: calc(var(--app-nav-height) + 1rem) 0 1rem 0;
|
||||
|
||||
.list-wrapper {
|
||||
|
||||
.content {
|
||||
gap: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.header {
|
||||
|
||||
.description {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.legends {
|
||||
gap: 1.5rem;
|
||||
display: flex;
|
||||
|
||||
.legend {
|
||||
gap: .25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.block {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
display: block;
|
||||
border-radius: .25rem;
|
||||
|
||||
&.direct {
|
||||
background: var(--source-direct-color);
|
||||
}
|
||||
|
||||
&.inherited {
|
||||
background: var(--source-inherited-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
|
||||
&:last-child {
|
||||
|
||||
:deep(.t-cell:after) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.t-cell) {
|
||||
|
||||
&__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__title {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__title-text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__note {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 56%;
|
||||
text-align: right;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.source {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
display: block;
|
||||
border-radius: .25rem;
|
||||
|
||||
&-direct {
|
||||
background: var(--source-direct-color);
|
||||
}
|
||||
|
||||
&-inherited {
|
||||
background: var(--source-inherited-color);
|
||||
}
|
||||
|
||||
&-unknown {
|
||||
background: var(--source-unknown-color);
|
||||
}
|
||||
}
|
||||
|
||||
.code {
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
.permission-list {
|
||||
.list-wrapper {
|
||||
.content {
|
||||
.item {
|
||||
:deep(.t-cell) {
|
||||
.source {
|
||||
&-direct {
|
||||
background: #60a5fa;
|
||||
}
|
||||
|
||||
&-inherited {
|
||||
background: #2dd4bf;
|
||||
}
|
||||
|
||||
&-unknown {
|
||||
background: #64748b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user