corrad-af-2024/components/rbac/PermissionMatrix.vue
Afiq f05dd42c16 Enhance README and implement RBAC system with Authentik integration
- Updated README.md to reflect the new project name and provide an overview of the Role-Based Access Control (RBAC) system.
- Added new components for RBAC management, including:
  - PermissionExample.vue: Demonstrates permission-based navigation.
  - GroupCard.vue: Displays group information and assigned roles.
  - PermissionMatrix.vue: Visual representation of permissions across roles and resources.
  - RoleTemplates.vue: Quick role templates for applying pre-configured permissions.
  - StatsCards.vue: Displays statistics related to users, groups, and roles.
- Introduced useRbacPermissions.js for managing permission checks.
- Created docker-compose.yml for PostgreSQL and Redis services.
- Developed comprehensive documentation for application management and Authentik integration.
- Added multiple pages for managing applications, groups, roles, and users, including bulk operations and templates.
- Updated navigation structure to include new RBAC management paths.
2025-05-31 15:58:41 +08:00

118 lines
3.7 KiB
Vue

<template>
<div class="permission-matrix">
<div class="overflow-x-auto">
<table class="min-w-full">
<thead>
<tr class="border-b border-gray-200 dark:border-gray-700">
<th class="text-left py-3 px-4 font-medium text-gray-900 dark:text-white">
{{ resourceTypeLabel }}
</th>
<th v-for="role in roles" :key="role.id" class="text-center py-3 px-4 font-medium text-gray-900 dark:text-white">
<div>{{ role.name }}</div>
<div v-if="showActions" class="text-xs font-normal text-gray-500 dark:text-gray-400">Actions</div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="resource in resources" :key="resource.id" class="border-b border-gray-100 dark:border-gray-800">
<td class="py-3 px-4">
<div :style="{ paddingLeft: (resource.level * 20) + 'px' }">
<div class="font-medium text-gray-900 dark:text-white">{{ resource.name }}</div>
<div class="text-xs text-gray-500 dark:text-gray-400 font-mono">{{ resource.key }}</div>
<div v-if="resource.path" class="text-xs text-gray-400 dark:text-gray-500">{{ resource.path }}</div>
</div>
</td>
<td v-for="role in roles" :key="role.id" class="text-center py-3 px-4">
<!-- Simple checkbox for menu/component permissions -->
<input
v-if="!showActions"
type="checkbox"
:checked="hasPermission(role.id, resource.id)"
@change="togglePermission(role.id, resource.id, 'view', $event)"
class="h-4 w-4 text-primary focus:ring-primary border-gray-300 rounded"
/>
<!-- Action-based permissions for features -->
<div v-else class="flex flex-wrap justify-center gap-1">
<label
v-for="action in actions"
:key="action.id"
class="flex items-center text-xs"
:title="action.label"
>
<input
type="checkbox"
:checked="hasPermission(role.id, resource.id, action.id)"
@change="togglePermission(role.id, resource.id, action.name, $event)"
class="h-3 w-3 text-primary focus:ring-primary border-gray-300 rounded mr-1"
/>
<Icon :name="action.icon" class="w-3 h-3" />
</label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup>
const props = defineProps({
resourceTypeLabel: {
type: String,
required: true
},
resources: {
type: Array,
required: true
},
roles: {
type: Array,
required: true
},
actions: {
type: Array,
default: () => []
},
showActions: {
type: Boolean,
default: false
},
permissions: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['permission-changed'])
const hasPermission = (roleId, resourceId, actionId = '1') => {
const key = `${roleId}-${resourceId}-${actionId}`
return props.permissions[key] || false
}
const togglePermission = (roleId, resourceId, action, event) => {
const granted = event.target.checked
emit('permission-changed', { roleId, resourceId, action, granted })
}
</script>
<style scoped>
.permission-matrix table {
@apply border-collapse;
}
.permission-matrix th,
.permission-matrix td {
@apply border-0;
}
.permission-matrix input[type="checkbox"] {
@apply transition-colors duration-200;
}
.permission-matrix input[type="checkbox"]:focus {
@apply ring-offset-0;
}
</style>