feat: 角色组成员管理

pull/49/head
moonrailgun 3 years ago
parent 31fbd35cdc
commit 1556eed93f

@ -86,6 +86,7 @@ export {
showToasts,
setToasts,
showErrorToasts,
showSuccessToasts,
showAlert,
setAlert,
showGlobalLoading,

@ -1,3 +1,4 @@
import { t } from '../i18n';
import { buildRegFn } from './buildRegFn';
/**
@ -23,6 +24,13 @@ export function showErrorToasts(error: unknown) {
showToasts(msg, 'error');
}
/**
*
*/
export function showSuccessToasts(msg = t('操作成功')) {
showToasts(msg, 'success');
}
interface AlertOptions {
message: React.ReactNode;
onConfirm?: () => void | Promise<void>;

@ -7,7 +7,7 @@ export enum GroupPanelType {
}
export interface GroupMember {
role: string; // 角色
roles: string[]; // 角色
userId: string;
/**
* xxx
@ -139,6 +139,42 @@ export async function quitGroup(groupId: string) {
});
}
/**
*
* @param groupId ID
* @param memberIds
* @param roles
*/
export async function appendGroupMemberRoles(
groupId: string,
memberIds: string[],
roles: string[]
) {
await request.post('/api/group/appendGroupMemberRoles', {
groupId,
memberIds,
roles,
});
}
/**
*
* @param groupId ID
* @param memberIds
* @param roles
*/
export async function removeGroupMemberRoles(
groupId: string,
memberIds: string[],
roles: string[]
) {
await request.post('/api/group/removeGroupMemberRoles', {
groupId,
memberIds,
roles,
});
}
/**
*
* 7

@ -1,4 +1,4 @@
import { Checkbox, Input } from 'antd';
import { Checkbox, Empty, Input } from 'antd';
import React, { useCallback, useState } from 'react';
import { t, useUserInfoList } from 'tailchat-shared';
import _take from 'lodash/take';
@ -47,6 +47,11 @@ export const UserPicker: React.FC<UserPickerProps> = React.memo((props) => {
[selectedIds, onChange]
);
const matchedList = _take(
userInfoList.filter((info) => info.nickname.includes(searchValue)),
10
);
return (
<div>
{withSearch && (
@ -64,28 +69,29 @@ export const UserPicker: React.FC<UserPickerProps> = React.memo((props) => {
})}
</div>
{_take(
userInfoList.filter((info) => info.nickname.includes(searchValue)),
10
).map((info) => {
return (
<div key={info._id} className="my-1">
<Checkbox
className="mr-2 items-center"
checked={selectedIds.includes(info._id)}
onChange={(e) => handleSelectUser(info._id, e.target.checked)}
>
<div className="flex items-center">
<Avatar size="small" name={info.nickname} src={info.avatar} />
{matchedList.length > 0 ? (
matchedList.map((info) => {
return (
<div key={info._id} className="my-1">
<Checkbox
className="mr-2 items-center"
checked={selectedIds.includes(info._id)}
onChange={(e) => handleSelectUser(info._id, e.target.checked)}
>
<div className="flex items-center">
<Avatar size="small" name={info.nickname} src={info.avatar} />
<div className="ml-1 text-typography-light dark:text-typography-dark">
{info.nickname}
<div className="ml-1 text-typography-light dark:text-typography-dark">
{info.nickname}
</div>
</div>
</div>
</Checkbox>
</div>
);
})}
</Checkbox>
</div>
);
})
) : (
<Empty />
)}
</div>
);
});

@ -4,17 +4,29 @@ import {
} from '@/components/FullModal/Field';
import { IconBtn } from '@/components/IconBtn';
import { Loading } from '@/components/Loading';
import { closeModal, openModal } from '@/components/Modal';
import { PillTabPane, PillTabs } from '@/components/PillTabs';
import { UserListItem } from '@/components/UserListItem';
import { AllPermission, permissionList } from '@/utils/role-helper';
import { Button, Input } from 'antd';
import React, { useCallback, useMemo, useState } from 'react';
import { Icon } from 'tailchat-design';
import { t, useGroupInfo, useSearch, useUserInfoList } from 'tailchat-shared';
import {
model,
showErrorToasts,
showSuccessToasts,
t,
useGroupInfo,
useSearch,
useUserId,
useUserInfoList,
} from 'tailchat-shared';
import { SelectGroupMember } from '../../SelectGroupMember';
import { PermissionItem } from './PermissionItem';
import { RoleItem } from './RoleItem';
import { useModifyPermission } from './useModifyPermission';
import { useRoleActions } from './useRoleActions';
import _compact from 'lodash/compact';
interface GroupPermissionProps {
groupId: string;
@ -26,8 +38,12 @@ export const GroupRole: React.FC<GroupPermissionProps> = React.memo((props) => {
);
const groupInfo = useGroupInfo(groupId);
const roles = groupInfo?.roles ?? [];
const members = (groupInfo?.members ?? []).filter((m) => m.role === roleId);
const userInfoList = useUserInfoList(members.map((m) => m.userId));
const members =
roleId === AllPermission
? []
: (groupInfo?.members ?? []).filter((m) => m.roles.includes(roleId));
const memberIds = members.map((m) => m.userId);
const userInfoList = useUserInfoList(memberIds);
const {
searchText,
setSearchText,
@ -37,6 +53,7 @@ export const GroupRole: React.FC<GroupPermissionProps> = React.memo((props) => {
dataSource: userInfoList,
filterFn: (item, searchText) => item.nickname.includes(searchText),
});
const userId = useUserId();
const currentRoleInfo = useMemo(
() => roles.find((r) => r._id === roleId),
[roles, roleId]
@ -63,7 +80,42 @@ export const GroupRole: React.FC<GroupPermissionProps> = React.memo((props) => {
handleSwitchPermission,
} = useModifyPermission(currentRolePermissions);
const handleAddMember = useCallback(() => {}, []);
const handleAddMember = useCallback(() => {
const key = openModal(
<SelectGroupMember
groupId={groupId}
withoutMemberIds={_compact([...memberIds, userId])}
onConfirm={async (selectedIds) => {
if (!currentRoleInfo?._id) {
showErrorToasts(t('当前没有选择任何角色组'));
return;
}
await model.group.appendGroupMemberRoles(groupId, selectedIds, [
currentRoleInfo._id,
]);
showSuccessToasts();
closeModal(key);
}}
/>
);
}, [groupId, memberIds, currentRoleInfo?._id]);
const handleRemoveMember = useCallback(
async (memberId: string) => {
if (!currentRoleInfo?._id) {
showErrorToasts(t('当前没有选择任何角色组'));
return;
}
await model.group.removeGroupMemberRoles(
groupId,
[memberId],
[currentRoleInfo._id]
);
showSuccessToasts();
},
[groupId, currentRoleInfo?._id]
);
const handleResetPermission = useCallback(() => {
setEditingPermission(
@ -169,7 +221,13 @@ export const GroupRole: React.FC<GroupPermissionProps> = React.memo((props) => {
<UserListItem
key={m._id}
userId={m._id}
actions={[<IconBtn key="remove" icon="mdi:close" />]}
actions={[
<IconBtn
key="remove"
icon="mdi:close"
onClick={() => handleRemoveMember(m._id)}
/>,
]}
/>
))}
</PillTabPane>

@ -0,0 +1,59 @@
import { Button } from 'antd';
import React, { useMemo, useState } from 'react';
import { t, useAsyncFn, useGroupMemberIds } from 'tailchat-shared';
import { ModalWrapper } from '../Modal';
import { UserPicker } from '../UserPicker/UserPicker';
import _without from 'lodash/without';
interface SelectGroupMemberProps {
/**
* id
*/
groupId: string;
/**
* id
*
*/
withoutMemberIds?: string[];
/**
*
*/
onConfirm: (selectedUserIds: string[]) => void | Promise<void>;
}
/**
*
*/
export const SelectGroupMember: React.FC<SelectGroupMemberProps> = React.memo(
(props) => {
const { groupId, withoutMemberIds = [], onConfirm } = props;
const [selectedUserIds, setSelectedUserIds] = useState<string[]>([]);
const groupMemberIds = useGroupMemberIds(groupId);
const filterMemberIds = useMemo(
() => _without(groupMemberIds, ...withoutMemberIds),
[groupMemberIds, withoutMemberIds]
);
const [{ loading }, handleConfirm] = useAsyncFn(async () => {
await onConfirm(selectedUserIds);
}, [onConfirm, selectedUserIds]);
return (
<ModalWrapper title={t('选择群组成员')}>
<UserPicker
allUserIds={filterMemberIds}
selectedIds={selectedUserIds}
onChange={setSelectedUserIds}
/>
<div className="text-right">
<Button type="primary" loading={loading} onClick={handleConfirm}>
{t('确认')}
</Button>
</div>
</ModalWrapper>
);
}
);
SelectGroupMember.displayName = 'SelectGroupMember';
Loading…
Cancel
Save