mirror of https://github.com/msgbyte/tailchat
refactor: 拆分身份组管理逻辑
parent
a4f0c9389e
commit
12431a2c97
@ -0,0 +1,116 @@
|
|||||||
|
import { IconBtn } from '@/components/IconBtn';
|
||||||
|
import { closeModal, openModal } from '@/components/Modal';
|
||||||
|
import { SelectGroupMember } from '@/components/modals/SelectGroupMember';
|
||||||
|
import { UserListItem } from '@/components/UserListItem';
|
||||||
|
import { Button, Input } from 'antd';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { Icon } from 'tailchat-design';
|
||||||
|
import {
|
||||||
|
model,
|
||||||
|
showErrorToasts,
|
||||||
|
showSuccessToasts,
|
||||||
|
t,
|
||||||
|
useAsyncRequest,
|
||||||
|
useGroupInfo,
|
||||||
|
useSearch,
|
||||||
|
useUserId,
|
||||||
|
useUserInfoList,
|
||||||
|
} from 'tailchat-shared';
|
||||||
|
import _compact from 'lodash/compact';
|
||||||
|
|
||||||
|
interface RoleMemberProps {
|
||||||
|
groupId: string;
|
||||||
|
currentRoleInfo: model.group.GroupRole;
|
||||||
|
}
|
||||||
|
export const RoleMember: React.FC<RoleMemberProps> = React.memo((props) => {
|
||||||
|
const roleId = props.currentRoleInfo._id;
|
||||||
|
const userId = useUserId();
|
||||||
|
const groupInfo = useGroupInfo(props.groupId);
|
||||||
|
const members = (groupInfo?.members ?? []).filter((m) =>
|
||||||
|
m.roles.includes(roleId)
|
||||||
|
);
|
||||||
|
const memberIds = members.map((m) => m.userId);
|
||||||
|
const userInfoList = useUserInfoList(memberIds);
|
||||||
|
const {
|
||||||
|
searchText,
|
||||||
|
setSearchText,
|
||||||
|
isSearching,
|
||||||
|
searchResult: filterMembers,
|
||||||
|
} = useSearch({
|
||||||
|
dataSource: userInfoList,
|
||||||
|
filterFn: (item, searchText) => item.nickname.includes(searchText),
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAddMember = useCallback(() => {
|
||||||
|
const key = openModal(
|
||||||
|
<SelectGroupMember
|
||||||
|
groupId={props.groupId}
|
||||||
|
withoutMemberIds={_compact([...memberIds, userId])}
|
||||||
|
onConfirm={async (selectedIds) => {
|
||||||
|
try {
|
||||||
|
await model.group.appendGroupMemberRoles(
|
||||||
|
props.groupId,
|
||||||
|
selectedIds,
|
||||||
|
[props.currentRoleInfo._id]
|
||||||
|
);
|
||||||
|
showSuccessToasts();
|
||||||
|
closeModal(key);
|
||||||
|
} catch (err) {
|
||||||
|
showErrorToasts(err);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, [userId, props.groupId, memberIds, props.currentRoleInfo._id]);
|
||||||
|
|
||||||
|
const [, handleRemoveMember] = useAsyncRequest(
|
||||||
|
async (memberId: string) => {
|
||||||
|
if (!props.currentRoleInfo?._id) {
|
||||||
|
showErrorToasts(t('当前没有选择任何角色组'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await model.group.removeGroupMemberRoles(
|
||||||
|
props.groupId,
|
||||||
|
[memberId],
|
||||||
|
[props.currentRoleInfo._id]
|
||||||
|
);
|
||||||
|
showSuccessToasts();
|
||||||
|
},
|
||||||
|
[props.groupId, props.currentRoleInfo?._id]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* 管理成员 */}
|
||||||
|
<div className="text-right mb-2 flex space-x-1">
|
||||||
|
<Input
|
||||||
|
placeholder={t('搜索成员')}
|
||||||
|
size="middle"
|
||||||
|
suffix={<Icon fontSize={20} color="grey" icon="mdi:magnify" />}
|
||||||
|
value={searchText}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="primary" onClick={handleAddMember}>
|
||||||
|
{t('添加成员')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(isSearching ? filterMembers : userInfoList).map((m) => (
|
||||||
|
<UserListItem
|
||||||
|
key={m._id}
|
||||||
|
userId={m._id}
|
||||||
|
actions={[
|
||||||
|
<IconBtn
|
||||||
|
key="remove"
|
||||||
|
icon="mdi:close"
|
||||||
|
onClick={() => handleRemoveMember(m._id)}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
RoleMember.displayName = 'RoleMember';
|
@ -0,0 +1,65 @@
|
|||||||
|
import { AllPermission, permissionList } from '@/utils/role-helper';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import { model, t } from 'tailchat-shared';
|
||||||
|
import { PermissionItem } from '../PermissionItem';
|
||||||
|
import { useModifyPermission } from '../useModifyPermission';
|
||||||
|
|
||||||
|
interface RolePermissionProps {
|
||||||
|
roleId: typeof AllPermission | string;
|
||||||
|
currentRoleInfo?: model.group.GroupRole;
|
||||||
|
fallbackPermissions: string[];
|
||||||
|
onSavePermission: (permissions: string[]) => Promise<void>;
|
||||||
|
}
|
||||||
|
export const RolePermission: React.FC<RolePermissionProps> = React.memo(
|
||||||
|
(props) => {
|
||||||
|
const currentRolePermissions: string[] = useMemo(() => {
|
||||||
|
if (props.roleId === AllPermission) {
|
||||||
|
return props.fallbackPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.currentRoleInfo?.permissions ?? [];
|
||||||
|
}, [props.roleId, props.fallbackPermissions, props.currentRoleInfo]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
isEditing,
|
||||||
|
editingPermission,
|
||||||
|
setEditingPermission,
|
||||||
|
handleSwitchPermission,
|
||||||
|
} = useModifyPermission(currentRolePermissions);
|
||||||
|
|
||||||
|
const handleResetPermission = useCallback(() => {
|
||||||
|
setEditingPermission(
|
||||||
|
permissionList.filter((p) => p.default === true).map((p) => p.key)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 权限概述
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="mb-2 space-x-2 text-right">
|
||||||
|
<Button onClick={handleResetPermission}>{t('重置为默认值')}</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
disabled={!isEditing}
|
||||||
|
onClick={() => props.onSavePermission(editingPermission)}
|
||||||
|
>
|
||||||
|
{t('保存')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 权限详情 */}
|
||||||
|
{permissionList.map((p) => (
|
||||||
|
<PermissionItem
|
||||||
|
key={p.key}
|
||||||
|
title={p.title}
|
||||||
|
desc={p.desc}
|
||||||
|
checked={editingPermission.includes(p.key)}
|
||||||
|
onChange={(checked) => handleSwitchPermission(p.key, checked)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
RolePermission.displayName = 'RolePermission';
|
@ -0,0 +1,26 @@
|
|||||||
|
import {
|
||||||
|
DefaultFullModalInputEditorRender,
|
||||||
|
FullModalField,
|
||||||
|
} from '@/components/FullModal/Field';
|
||||||
|
import React from 'react';
|
||||||
|
import { model, t } from 'tailchat-shared';
|
||||||
|
|
||||||
|
interface RoleSummaryProps {
|
||||||
|
currentRoleInfo: model.group.GroupRole;
|
||||||
|
onChangeRoleName: (roleName: string) => void;
|
||||||
|
}
|
||||||
|
export const RoleSummary: React.FC<RoleSummaryProps> = React.memo((props) => {
|
||||||
|
// 权限概述
|
||||||
|
return (
|
||||||
|
<div className="px-2">
|
||||||
|
<FullModalField
|
||||||
|
title={t('身份组名称')}
|
||||||
|
value={props.currentRoleInfo.name}
|
||||||
|
editable={true}
|
||||||
|
renderEditor={DefaultFullModalInputEditorRender}
|
||||||
|
onSave={props.onChangeRoleName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
RoleSummary.displayName = 'RoleSummary';
|
Loading…
Reference in New Issue