Files
gao-customer-h5/src/pages/permission/RoleList.vue
T
2026-09-23 21:54:19 +08:00

192 lines
5.5 KiB
Vue

<template>
<div v-can="{ value: [corePermissionCodes.role.read, corePermissionCodes.permission.read], mode: 'and' }" class="role-list">
<t-pull-down-refresh
class="list-wrapper"
v-model="isRefreshing"
:refresh-timeout="30000"
@refresh="refresh"
>
<t-cell-group v-if="sortedRoles.length" theme="card">
<t-swipe-cell
v-entrance-transition="index * 30"
v-for="(item, index) in sortedRoles"
:key="item.id"
class="item"
>
<t-cell
:title="item.name"
:note="`${item.code} · ${getRoleScopeLabel(item)}`"
arrow
hover
@click="router.push(`/management/permissions/roles/${item.id}`)"
>
<template #title>
<div class="title" v-text="item.name" :style="{ paddingLeft: `${item.level * 1.25}rem` }" />
</template>
</t-cell>
<template #right>
<t-button v-if="canDeleteRole(item)" v-can="corePermissionCodes.role.delete" class="delete" theme="danger" @click="removeRole(item.id)">删除</t-button>
</template>
</t-swipe-cell>
</t-cell-group>
<t-empty v-else-if="!permissionStore.loading" description="暂无角色" />
</t-pull-down-refresh>
</div>
</template>
<script setup lang="ts">
import { Message } from "@/utils/message";
import { useUserStore } from "timi-web-api";
import type { Role } from "@/store/permissionSettingsStore";
import { useNavBarStore } from "@/store/navBarStore";
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
import { getErrorMessage } from "@/utils/error";
import { corePermissionCodes, coreRoleCodes } from "@/utils/permission";
defineOptions({
name: "RoleList"
});
const router = useRouter();
const navBarStore = useNavBarStore();
const permissionStore = usePermissionSettingsStore();
const userStore = useUserStore();
const isRefreshing = ref(false);
const deletableRoleIdSet = ref(new Set<string>());
// ---------- 顶部导航 ----------
const canCreateRole = computed(() => userStore.hasPermissions(corePermissionCodes.role.create));
async function toCreate(): Promise<void> {
if (!canCreateRole.value) {
return;
}
await router.push("/management/permissions/roles/create");
}
// ---------- 列表数据 ----------
const displayRoleList = ref<Role[]>([]);
const sortedRoles = computed<Array<Role & { level: number }>>(() => {
if (userStore.hasRoles(coreRoleCodes.admin)) {
return permissionStore.roleTree;
}
return buildManageableRoleTree();
});
async function removeRole(id: string): Promise<void> {
try {
await permissionStore.removeRole(id);
await permissionStore.loadAll(true);
await refreshAuthorizationScope();
} catch (error) {
await permissionStore.loadAll(true);
await refreshAuthorizationScope();
Message.error(getErrorMessage(error, "角色删除失败"));
}
}
function canDeleteRole(role: Role): boolean {
if (userStore.hasRoles(coreRoleCodes.admin)) {
return true;
}
return role.builtin !== true && role.protectedRole !== true && deletableRoleIdSet.value.has(role.id);
}
function getRoleScopeLabel(role: Role): string {
return userStore.hasRoles(coreRoleCodes.admin) ? "系统角色" : role.ownerType === "STORE" ? "本店角色" : "可管理角色";
}
// ---------- 页面数据 ----------
async function refresh(): Promise<void> {
isRefreshing.value = true;
try {
await permissionStore.loadAll(true);
displayRoleList.value = getDisplayRoleList();
await refreshAuthorizationScope();
} finally {
isRefreshing.value = false;
}
}
onMounted(async () => {
navBarStore.clearRight();
if (canCreateRole.value) {
navBarStore.setRightText("添加", "var(--td-brand-color)", toCreate);
}
await permissionStore.loadAll();
await refreshAuthorizationScope();
if (!displayRoleList.value.length) {
displayRoleList.value = getDisplayRoleList();
}
});
onUnmounted(() => {
navBarStore.clearRight();
});
function getDisplayRoleList(): Role[] {
return userStore.hasRoles(coreRoleCodes.admin) ? permissionStore.roleList : permissionStore.manageableRoleList;
}
function buildManageableRoleTree(): Array<Role & { level: number }> {
const roleIdSet = new Set(displayRoleList.value.map(item => item.id));
const roleMap = new Map(displayRoleList.value.map(item => [item.id, item]));
const roots = displayRoleList.value.filter(item => !item.parentRoleId || !roleIdSet.has(item.parentRoleId));
const buildTree = (roleId: string, level: number, visitedRoleIds: Set<string>): Array<Role & { level: number }> => {
const role = roleMap.get(roleId);
if (!role || visitedRoleIds.has(roleId)) {
return [];
}
const nextVisitedRoleIds = new Set(visitedRoleIds);
nextVisitedRoleIds.add(roleId);
const childRoleIdList = displayRoleList.value
.filter(item => item.parentRoleId === roleId)
.map(item => item.id);
return [{ ...role, level }, ...childRoleIdList.flatMap(childRoleId => buildTree(childRoleId, level + 1, nextVisitedRoleIds))];
};
return roots.flatMap(item => buildTree(item.id, 0, new Set<string>()));
}
async function refreshAuthorizationScope(): Promise<void> {
if (userStore.hasRoles(coreRoleCodes.admin)) {
deletableRoleIdSet.value = new Set(permissionStore.roleList.map(item => item.id));
return;
}
displayRoleList.value = permissionStore.roleList;
deletableRoleIdSet.value = new Set(permissionStore.roleList.map(item => item.id));
}
</script>
<style scoped lang="less">
.role-list {
padding: calc(var(--app-nav-height) + 1rem) 0 1rem 0;
.list-wrapper {
padding-bottom: 6rem;
.item {
&:last-child {
:deep(.t-cell:after) {
display: none;
}
}
.delete {
height: 100%;
padding: 0 1rem;
border-radius: 0;
}
}
}
}
</style>