v3.5.8和v3.5.9版本更新
CI / verify (pull_request) Waiting to run

This commit is contained in:
2026-08-25 09:20:32 +08:00
parent 7304c7a8e1
commit 5935024ea8
101 changed files with 7640 additions and 5364 deletions
+86 -54
View File
@@ -17,6 +17,7 @@ import {
import {
HQ_ADMIN_ROLES,
HQ_PERMISSION_CATALOG,
computeHqEffectivePermissionKeys,
type HqPermissionKey,
} from '@dukang/shared-types';
import { request, type HqProfile } from '../lib/api';
@@ -27,6 +28,8 @@ type AccountPermRes = {
account: AccountOption;
permissionKeys: string[];
rolePermissionKeys: string[];
grantKeys?: string[];
denyKeys?: string[];
userPermissionKeys: string[];
effectivePermissionKeys: string[];
};
@@ -46,11 +49,17 @@ function PermissionChecklist({
value,
onChange,
disabled,
roleKeys,
grantKeys,
}: {
value: string[];
onChange: (keys: string[]) => void;
disabled?: boolean;
roleKeys?: string[];
grantKeys?: string[];
}) {
const roleSet = new Set(roleKeys ?? []);
const grantSet = new Set(grantKeys ?? []);
return (
<Checkbox.Group
style={{ width: '100%' }}
@@ -81,6 +90,13 @@ function PermissionChecklist({
{items.map((item) => (
<Checkbox key={item.key} value={item.key}>
{item.label}
{roleSet.has(item.key) && !value.includes(item.key) ? (
<Tag style={{ marginLeft: 6 }} color="red"></Tag>
) : grantSet.has(item.key) ? (
<Tag style={{ marginLeft: 6 }} color="orange"></Tag>
) : roleSet.has(item.key) ? (
<Tag style={{ marginLeft: 6 }} color="blue"></Tag>
) : null}
</Checkbox>
))}
</Space>
@@ -88,7 +104,16 @@ function PermissionChecklist({
<Row gutter={[12, 10]}>
{items.map((item) => (
<Col key={item.key} xs={24} sm={12} md={span}>
<Checkbox value={item.key}>{item.label}</Checkbox>
<Checkbox value={item.key}>
{item.label}
{roleSet.has(item.key) && !value.includes(item.key) ? (
<Tag style={{ marginLeft: 6 }} color="red"></Tag>
) : grantSet.has(item.key) ? (
<Tag style={{ marginLeft: 6 }} color="orange"></Tag>
) : roleSet.has(item.key) ? (
<Tag style={{ marginLeft: 6 }} color="blue"></Tag>
) : null}
</Checkbox>
</Col>
))}
</Row>
@@ -100,6 +125,14 @@ function PermissionChecklist({
);
}
function splitAccountOverrides(roleKeys: string[], effectiveKeys: string[]) {
const roleSet = new Set(roleKeys);
const effectiveSet = new Set(effectiveKeys);
const grantKeys = effectiveKeys.filter((k) => !roleSet.has(k));
const denyKeys = roleKeys.filter((k) => !effectiveSet.has(k));
return { grantKeys, denyKeys };
}
export default function HqPermissionsPage() {
const [profile, setProfile] = useState<HqProfile | null>(null);
const [role, setRole] = useState<string>('OPS');
@@ -111,6 +144,8 @@ export default function HqPermissionsPage() {
const [accountId, setAccountId] = useState<string>();
const [accountKeys, setAccountKeys] = useState<string[]>([]);
const [roleInheritedKeys, setRoleInheritedKeys] = useState<string[]>([]);
const [grantKeys, setGrantKeys] = useState<string[]>([]);
const [denyKeys, setDenyKeys] = useState<string[]>([]);
const [accountLoading, setAccountLoading] = useState(false);
const [accountSaving, setAccountSaving] = useState(false);
@@ -121,8 +156,8 @@ export default function HqPermissionsPage() {
const selectedIsSuperAdmin = selectedAccount?.adminRole === 'SUPER_ADMIN';
const previewEffectiveKeys = useMemo(
() => [...new Set([...roleInheritedKeys, ...accountKeys])],
[roleInheritedKeys, accountKeys],
() => computeHqEffectivePermissionKeys(roleInheritedKeys, grantKeys, denyKeys),
[roleInheritedKeys, grantKeys, denyKeys],
);
const isSuperAdmin = profile?.adminRole === 'SUPER_ADMIN';
@@ -151,8 +186,10 @@ export default function HqPermissionsPage() {
setAccountLoading(true);
request<AccountPermRes>(`/admin/hq-permissions/accounts/${accountId}`)
.then((res) => {
setAccountKeys(res.userPermissionKeys);
setGrantKeys(res.grantKeys ?? res.userPermissionKeys ?? []);
setDenyKeys(res.denyKeys ?? []);
setRoleInheritedKeys(res.rolePermissionKeys);
setAccountKeys(res.effectivePermissionKeys);
})
.finally(() => setAccountLoading(false));
}, [isSuperAdmin, accountId]);
@@ -179,10 +216,12 @@ export default function HqPermissionsPage() {
try {
const res = await request<AccountPermRes>(`/admin/hq-permissions/accounts/${accountId}`, {
method: 'PUT',
body: JSON.stringify({ permissionKeys: accountKeys }),
body: JSON.stringify({ grantKeys, denyKeys }),
});
setAccountKeys(res.userPermissionKeys);
setGrantKeys(res.grantKeys ?? res.userPermissionKeys ?? []);
setDenyKeys(res.denyKeys ?? []);
setRoleInheritedKeys(res.rolePermissionKeys);
setAccountKeys(res.effectivePermissionKeys);
message.success('用户权限已保存');
} catch (e) {
message.error(e instanceof Error ? e.message : '保存失败');
@@ -204,8 +243,9 @@ export default function HqPermissionsPage() {
<div>
<Typography.Title level={4}></Typography.Title>
<Typography.Paragraph type="secondary">
=
=
//
/
//
</Typography.Paragraph>
@@ -275,59 +315,51 @@ export default function HqPermissionsPage() {
<Alert type="info" showIcon message="请先选择要配置的 HQ 账户" />
) : (
<>
<div style={{ marginBottom: 12 }}>
<span style={{ marginRight: 8 }}></span>
{selectedIsSuperAdmin ? (
<Tag color="blue"></Tag>
) : (
<Space wrap size={[4, 4]}>
{roleInheritedKeys.map((key) => {
const item = HQ_PERMISSION_CATALOG.find((p) => p.key === key);
return (
<Tag key={key} color="blue">
{item?.label || key}
</Tag>
);
})}
{!roleInheritedKeys.length ? (
<Typography.Text type="secondary"></Typography.Text>
) : null}
</Space>
)}
</div>
<Typography.Paragraph type="secondary">
//
/
</Typography.Paragraph>
<PermissionChecklist value={accountKeys} onChange={setAccountKeys} />
<PermissionChecklist
value={accountKeys}
roleKeys={roleInheritedKeys}
grantKeys={grantKeys}
disabled={selectedIsSuperAdmin}
onChange={(nextEffective) => {
if (selectedIsSuperAdmin) return;
const next = splitAccountOverrides(roleInheritedKeys, nextEffective);
setGrantKeys(next.grantKeys);
setDenyKeys(next.denyKeys);
setAccountKeys(nextEffective);
}}
/>
{selectedIsSuperAdmin ? (
<Alert
type="info"
showIcon
style={{ marginTop: 12 }}
message="超级管理员基础权限不可撤销。危险操作请到下方以外的方式:当前勾选仅展示生效权限。"
/>
) : null}
<div style={{ marginTop: 12 }}>
<span style={{ marginRight: 8 }}></span>
{selectedIsSuperAdmin ? (
<Space wrap size={[4, 4]}>
<Tag></Tag>
{accountKeys.map((key) => {
const item = HQ_PERMISSION_CATALOG.find((p) => p.key === (key as HqPermissionKey));
return (
<Tag key={key} color="orange">
{item?.label || key}
</Tag>
);
})}
</Space>
) : (
<Space wrap size={[4, 4]}>
{previewEffectiveKeys.map((key) => {
const item = HQ_PERMISSION_CATALOG.find((p) => p.key === (key as HqPermissionKey));
return (
<Tag key={key}>
{item?.label || key}
</Tag>
);
})}
</Space>
)}
<Space wrap size={[4, 4]}>
{previewEffectiveKeys.map((key) => {
const item = HQ_PERMISSION_CATALOG.find((p) => p.key === (key as HqPermissionKey));
return (
<Tag key={key}>
{item?.label || key}
</Tag>
);
})}
</Space>
</div>
<div style={{ marginTop: 16 }}>
<Button type="primary" loading={accountSaving} onClick={() => void saveAccountPermissions()}>
<Button
type="primary"
loading={accountSaving}
disabled={selectedIsSuperAdmin}
onClick={() => void saveAccountPermissions()}
>
</Button>
</div>