Add files via upload

This commit is contained in:
公明
2026-07-10 20:20:05 +08:00
committed by GitHub
parent e020ffed49
commit e87011b081
3 changed files with 196 additions and 37 deletions
+83 -6
View File
@@ -36249,16 +36249,88 @@ html[data-theme="dark"] .form-group select {
position: relative;
}
.rbac-summary-toggle {
margin-left: 4px;
vertical-align: middle;
.rbac-summary-count {
display: block;
margin-top: 3px;
color: var(--text-primary);
font-size: 0.9375rem;
font-weight: 700;
line-height: 1.2;
}
.rbac-effective-permissions {
margin: 0 16px 12px;
.rbac-summary-count--interactive {
display: inline-flex;
align-items: center;
margin-top: 3px;
padding: 0 0 1px;
border: none;
border-bottom: 1px dashed color-mix(in srgb, var(--text-secondary) 55%, transparent);
border-radius: 0;
background: none;
color: var(--text-secondary);
font-size: 0.9375rem;
font-weight: 700;
line-height: 1.2;
cursor: pointer;
transition: color 0.15s ease, border-color 0.15s ease;
}
.rbac-summary-count--interactive:hover {
color: var(--text-primary);
border-bottom-color: color-mix(in srgb, var(--text-secondary) 80%, transparent);
}
.rbac-summary-count--interactive[aria-expanded="true"] {
color: var(--text-primary);
border-bottom-color: var(--text-secondary);
border-bottom-style: solid;
}
.rbac-summary-count--interactive:focus-visible {
outline: 2px solid color-mix(in srgb, var(--text-secondary) 35%, transparent);
outline-offset: 3px;
}
.rbac-permissions-popover {
position: fixed;
z-index: 220;
width: min(380px, calc(100vw - 32px));
}
.rbac-permissions-popover[hidden] {
display: none !important;
}
.rbac-permissions-popover-inner {
position: relative;
overflow: hidden;
border: 1px solid var(--border-color);
border-radius: 8px;
border-radius: 10px;
background: var(--bg-primary);
box-shadow: var(--shadow-md, 0 8px 24px rgba(15, 23, 42, 0.12));
}
.rbac-permissions-popover-inner::before {
content: '';
position: absolute;
top: -5px;
left: var(--rbac-popover-arrow-left, 18px);
width: 10px;
height: 10px;
background: var(--bg-primary);
border-left: 1px solid var(--border-color);
border-top: 1px solid var(--border-color);
transform: rotate(45deg);
}
.rbac-permissions-popover-count {
color: var(--text-muted);
font-size: 0.75rem;
font-weight: 500;
}
.rbac-permissions-popover .rbac-effective-permissions-body {
max-height: min(320px, calc(100vh - 180px));
}
.rbac-effective-permissions-head,
@@ -36744,6 +36816,11 @@ html[data-theme="dark"] .form-group select {
font-size: 0.875rem;
}
.rbac-panel-detail .rbac-selected-summary .rbac-summary-count--interactive {
margin-top: 2px;
font-size: 0.875rem;
}
.rbac-panel-detail .rbac-detail-head {
min-height: 0;
padding: 8px 14px;
+113 -30
View File
@@ -251,43 +251,120 @@ function resolveUserEffectiveAccess(user) {
return { userRoles, effectivePermissions, permissionsByModule, effectiveScope, assignmentCount };
}
function toggleRbacEffectivePermissions() {
rbacState.showEffectivePermissions = !rbacState.showEffectivePermissions;
renderRbacEffectivePermissions(selectedRbacUser());
let rbacPermissionsPopoverDocBound = false;
function positionRbacPermissionsPopover() {
const popover = document.getElementById('rbac-permissions-popover');
const btn = document.querySelector('.rbac-summary-count--interactive');
if (!popover || !btn || popover.hidden) return;
const rect = btn.getBoundingClientRect();
const width = Math.min(380, window.innerWidth - 32);
let left = rect.left;
if (left + width > window.innerWidth - 16) left = window.innerWidth - width - 16;
left = Math.max(16, left);
const top = Math.min(rect.bottom + 8, window.innerHeight - 16);
popover.style.width = `${width}px`;
popover.style.left = `${left}px`;
popover.style.top = `${top}px`;
const arrowLeft = Math.min(Math.max(rect.left + rect.width / 2 - left - 5, 12), width - 22);
popover.style.setProperty('--rbac-popover-arrow-left', `${arrowLeft}px`);
}
function renderRbacEffectivePermissions(user) {
const box = document.getElementById('rbac-effective-permissions');
if (!box) return;
if (!user || !rbacState.showEffectivePermissions) {
box.hidden = true;
box.innerHTML = '';
return;
function onRbacPermissionsPopoverReposition() {
if (rbacState.showEffectivePermissions) positionRbacPermissionsPopover();
}
function setRbacPermissionsPopoverOpen(open) {
rbacState.showEffectivePermissions = open;
const popover = document.getElementById('rbac-permissions-popover');
const btn = document.querySelector('.rbac-summary-count--interactive');
if (popover) popover.hidden = !open;
if (btn) btn.setAttribute('aria-expanded', open ? 'true' : 'false');
if (open) {
requestAnimationFrame(() => positionRbacPermissionsPopover());
}
}
function closeRbacPermissionsPopover() {
setRbacPermissionsPopoverOpen(false);
}
function toggleRbacEffectivePermissions(ev) {
if (ev) {
ev.preventDefault();
ev.stopPropagation();
}
const opening = !rbacState.showEffectivePermissions;
if (opening) renderRbacEffectivePermissionsContent(selectedRbacUser());
setRbacPermissionsPopoverOpen(opening);
}
function onRbacPermissionsPopoverPointerDown(ev) {
if (!rbacState.showEffectivePermissions) return;
const popover = document.getElementById('rbac-permissions-popover');
const btn = document.querySelector('.rbac-summary-count--interactive');
if (popover && popover.contains(ev.target)) return;
if (btn && btn.contains(ev.target)) return;
closeRbacPermissionsPopover();
}
function bindRbacPermissionsPopoverDismiss() {
if (rbacPermissionsPopoverDocBound) return;
rbacPermissionsPopoverDocBound = true;
document.addEventListener('mousedown', onRbacPermissionsPopoverPointerDown);
document.addEventListener('keydown', ev => {
if (ev.key === 'Escape' && rbacState.showEffectivePermissions) closeRbacPermissionsPopover();
});
window.addEventListener('resize', onRbacPermissionsPopoverReposition);
window.addEventListener('scroll', onRbacPermissionsPopoverReposition, true);
}
function renderRbacEffectivePermissionsContent(user) {
const popover = document.getElementById('rbac-permissions-popover');
if (!popover || !user) return;
const access = resolveUserEffectiveAccess(user);
const modules = Object.keys(access.permissionsByModule).sort();
if (!modules.length) {
box.hidden = false;
box.innerHTML = `<div class="rbac-empty"><strong>${rbacT('rbac.empty.noEffectivePermissions', '暂无有效权限')}</strong><span>${rbacEscape(rbacT('rbac.empty.assignRoleFirst', '分配角色后即可查看权限明细'))}</span></div>`;
popover.innerHTML = `<div class="rbac-permissions-popover-inner"><div class="rbac-empty"><strong>${rbacT('rbac.empty.noEffectivePermissions', '暂无有效权限')}</strong><span>${rbacEscape(rbacT('rbac.empty.assignRoleFirst', '分配角色后即可查看权限明细'))}</span></div></div>`;
return;
}
box.hidden = false;
box.innerHTML = `
<div class="rbac-effective-permissions-head">
<strong>${rbacEscape(rbacT('rbac.effectivePermissionsDetail', '有效权限明细'))}</strong>
<button type="button" class="btn-link btn-small" onclick="toggleRbacEffectivePermissions()">${rbacEscape(rbacT('common.close', '关闭'))}</button>
</div>
<div class="rbac-effective-permissions-body">
${modules.map(module => `
<section class="rbac-effective-module">
<h4>${rbacEscape(rbacPermissionModuleLabel(module))}<span>${access.permissionsByModule[module].length}</span></h4>
<div class="rbac-effective-permission-tags">
${access.permissionsByModule[module].sort().map(key => `<span title="${rbacEscape(rbacPermissionLabel(key))}">${rbacEscape(rbacPermissionLabel(key))}</span>`).join('')}
</div>
</section>`).join('')}
popover.innerHTML = `
<div class="rbac-permissions-popover-inner">
<div class="rbac-effective-permissions-head">
<strong>${rbacEscape(rbacT('rbac.effectivePermissionsDetail', '有效权限明细'))}</strong>
<span class="rbac-permissions-popover-count">${access.effectivePermissions.size}</span>
</div>
<div class="rbac-effective-permissions-body">
${modules.map(module => `
<section class="rbac-effective-module">
<h4>${rbacEscape(rbacPermissionModuleLabel(module))}<span>${access.permissionsByModule[module].length}</span></h4>
<div class="rbac-effective-permission-tags">
${access.permissionsByModule[module].sort().map(key => `<span title="${rbacEscape(rbacPermissionLabel(key))}">${rbacEscape(rbacPermissionLabel(key))}</span>`).join('')}
</div>
</section>`).join('')}
</div>
</div>`;
}
function renderRbacEffectivePermissions(user) {
const popover = document.getElementById('rbac-permissions-popover');
const btn = document.querySelector('.rbac-summary-count--interactive');
if (!popover) return;
if (!user) {
popover.hidden = true;
popover.innerHTML = '';
rbacState.showEffectivePermissions = false;
if (btn) btn.setAttribute('aria-expanded', 'false');
return;
}
if (btn) btn.setAttribute('aria-expanded', rbacState.showEffectivePermissions ? 'true' : 'false');
popover.hidden = !rbacState.showEffectivePermissions;
if (rbacState.showEffectivePermissions) {
renderRbacEffectivePermissionsContent(user);
requestAnimationFrame(() => positionRbacPermissionsPopover());
}
}
function rbacRoleIsSystem(role) {
return !!(role && (role.isSystem || role.is_system));
}
@@ -306,6 +383,7 @@ async function initPlatformRbacPage() {
if (typeof window !== 'undefined' && window.i18nReady) await window.i18nReady;
const typeInput = document.getElementById('rbac-assignment-type');
if (typeInput) rbacState.assignmentResourceType = typeInput.value || 'conversation';
bindRbacPermissionsPopoverDismiss();
await rbacRun(loadPlatformRbac, rbacT('rbac.errors.loadFailed', '加载失败'));
initRbacSelects();
}
@@ -562,12 +640,17 @@ function renderRbacRoles() {
} else {
const access = resolveUserEffectiveAccess(user);
const scope = rbacScopeInfo(access.effectiveScope);
const permissionsToggle = access.effectivePermissions.size
? ` <button type="button" class="btn-link btn-small rbac-summary-toggle" onclick="toggleRbacEffectivePermissions()">${rbacEscape(rbacT(rbacState.showEffectivePermissions ? 'rbac.hidePermissions' : 'rbac.viewPermissionsDetail', rbacState.showEffectivePermissions ? '收起明细' : '查看明细'))}</button>`
: '';
const permissionCount = access.effectivePermissions.size;
const permissionCountHtml = permissionCount
? `<button type="button" class="rbac-summary-count rbac-summary-count--interactive" aria-expanded="${rbacState.showEffectivePermissions ? 'true' : 'false'}" aria-haspopup="dialog" aria-controls="rbac-permissions-popover" title="${rbacEscape(rbacT('rbac.viewPermissionsDetail', '查看明细'))}" onclick="toggleRbacEffectivePermissions(event)">${permissionCount}</button>`
: `<strong class="rbac-summary-count">${permissionCount}</strong>`;
summary.innerHTML = `
<div><span>${rbacT('rbac.roles', '角色')}</span><strong>${access.userRoles.length}</strong></div>
<div class="rbac-summary-permissions"><span>${rbacT('rbac.effectivePermissions', '有效权限')}</span><strong>${access.effectivePermissions.size}</strong>${permissionsToggle}</div>
<div class="rbac-summary-permissions">
<span>${rbacT('rbac.effectivePermissions', '有效权限')}</span>
${permissionCountHtml}
<div id="rbac-permissions-popover" class="rbac-permissions-popover" role="dialog" aria-label="${rbacEscape(rbacT('rbac.effectivePermissionsDetail', '有效权限明细'))}" hidden></div>
</div>
<div><span>${rbacT('rbac.effectiveScope', '有效资源范围')}</span><strong title="${rbacEscape(scope.hint)}">${rbacEscape(scope.label)}</strong></div>
<div><span>${rbacT('rbac.metricAssignments', '资源授权')}</span><strong>${access.assignmentCount}</strong></div>
`;
-1
View File
@@ -4219,7 +4219,6 @@
</div>
</div>
<div id="rbac-selected-summary" class="rbac-selected-summary"></div>
<div id="rbac-effective-permissions" class="rbac-effective-permissions" hidden></div>
<div id="rbac-no-role-notice" class="rbac-no-role-notice" hidden></div>
<div class="rbac-tabs" role="tablist">
<button type="button" class="rbac-tab is-active" data-rbac-tab="roles" onclick="switchRbacTab('roles')" data-i18n="rbac.tabRoles">角色权限</button>