diff --git a/src/hooks/setting-hooks.ts b/src/hooks/setting-hooks.ts index f2b074a..1cb05ca 100644 --- a/src/hooks/setting-hooks.ts +++ b/src/hooks/setting-hooks.ts @@ -127,3 +127,125 @@ export function useSystemStatus() { }; } +/** + * 团队设置 + */ +export function useTeamSetting() { + const { userInfo, tenantInfo, tenantList, refreshUserData } = useUserData(); + const [tenantUsers, setTenantUsers] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + // 获取租户用户列表 + const fetchTenantUsers = useCallback(async () => { + if (!tenantInfo?.tenant_id) return; + + try { + const response = await userService.listTenantUser(tenantInfo.tenant_id); + if (response.data.code === 0) { + setTenantUsers(response.data.data || []); + } + } catch (error) { + logger.error('获取租户用户失败:', error); + } + }, [tenantInfo?.tenant_id]); + + useEffect(() => { + if (tenantInfo?.tenant_id) { + fetchTenantUsers(); + } + }, [fetchTenantUsers]); + + const inviteUser = async (email: string) => { + if (!email.trim() || !tenantInfo?.tenant_id) return; + + setLoading(true); + setError(null); + + try { + const response = await userService.addTenantUser(tenantInfo.tenant_id, email.trim()); + if (response.data.code === 0) { + await fetchTenantUsers(); + await refreshUserData(); + return { success: true }; + } else { + const errorMsg = response.data.message || '邀请失败'; + setError(errorMsg); + return { success: false, error: errorMsg }; + } + } catch (error: any) { + const errorMsg = error.message || '邀请失败'; + setError(errorMsg); + return { success: false, error: errorMsg }; + } finally { + setLoading(false); + } + }; + + const deleteUser = async (userId: string) => { + if (!tenantInfo?.tenant_id) return; + + try { + const response = await userService.deleteTenantUser({ + tenantId: tenantInfo.tenant_id, + userId + }); + if (response.data.code === 0) { + await fetchTenantUsers(); + return { success: true }; + } + return { success: false }; + } catch (error) { + logger.error('删除用户失败:', error); + return { success: false }; + } + }; + + const agreeTenant = async (tenantId: string) => { + try { + const response = await userService.agreeTenant(tenantId); + if (response.data.code === 0) { + await refreshUserData(); + return { success: true }; + } + return { success: false }; + } catch (error) { + logger.error('同意加入失败:', error); + return { success: false }; + } + }; + + const quitTenant = async (tenantId: string) => { + if (!userInfo?.id) return; + + try { + const response = await userService.deleteTenantUser({ + tenantId, + userId: userInfo.id + }); + if (response.data.code === 0) { + await refreshUserData(); + return { success: true }; + } + return { success: false }; + } catch (error) { + logger.error('退出租户失败:', error); + return { success: false }; + } + }; + + return { + userInfo, + tenantInfo, + tenantList, + tenantUsers, + loading, + error, + inviteUser, + deleteUser, + agreeTenant, + quitTenant, + refreshUserData, + }; +} + diff --git a/src/pages/setting/teams.tsx b/src/pages/setting/teams.tsx index 143b1a2..77d7854 100644 --- a/src/pages/setting/teams.tsx +++ b/src/pages/setting/teams.tsx @@ -1,8 +1,292 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Box, + Card, + CardContent, + Typography, + Button, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Chip, + IconButton, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Alert, + Divider, + Stack +} from '@mui/material'; +import { + PersonAdd as PersonAddIcon, + Person as PersonIcon, + Group as GroupIcon, + Delete as DeleteIcon, + Check as CheckIcon, + Close as CloseIcon, + ExitToApp as ExitToAppIcon +} from '@mui/icons-material'; +import { useTeamSetting } from '@/hooks/setting-hooks'; + +interface TenantUser { + id: string; + nickname: string; + email: string; + role: string; + update_date: string; + status: string; +} + +interface JoinedTenant { + tenant_id: string; + nickname: string; + email: string; + role: string; + update_date: string; +} + +const TENANT_ROLES = { + Owner: 'owner', + Invite: 'invite', + Normal: 'normal', +} as const + +type TenantRole = (typeof TENANT_ROLES)[keyof typeof TENANT_ROLES] + function TeamsSetting() { + const { t } = useTranslation(); + const { + userInfo, + tenantInfo, + tenantList, + tenantUsers, + loading, + error, + inviteUser, + deleteUser, + agreeTenant, + quitTenant + } = useTeamSetting(); + + const [inviteDialogOpen, setInviteDialogOpen] = useState(false); + const [inviteEmail, setInviteEmail] = useState(''); + + const handleInviteUser = async () => { + if (!inviteEmail.trim()) return; + + const result = await inviteUser(inviteEmail); + if (result?.success) { + setInviteDialogOpen(false); + setInviteEmail(''); + } + }; + + const handleDeleteUser = async (userId: string) => { + await deleteUser(userId); + }; + + const handleAgreeTenant = async (tenantId: string) => { + await agreeTenant(tenantId); + }; + + const handleQuitTenant = async (tenantId: string) => { + await quitTenant(tenantId); + }; + + const formatDate = (dateString: string) => { + return new Date(dateString).toLocaleString('zh-CN'); + }; + + const getRoleColor = (role: TenantRole) => { + if (!role) return 'primary'; + // "error" | "primary" | "success" | "info" | "warning" | "secondary" | "default" + if (role === 'owner') return 'primary'; + if (role === 'normal') return 'success'; + if (role === 'invite') return 'warning'; + return 'primary'; + }; + return ( -
-

Teams Setting

-
+ + {/* 工作空间卡片 */} + + + + + {userInfo?.nickname} {t('setting.workspace')} + + + + + + + {/* 团队成员表格 */} + + + + + {t('setting.teamMembers')} + + + + + + + {t('common.name')} + {t('setting.email')} + {t('setting.role')} + {t('setting.updateDate')} + {t('common.action')} + + + + {tenantUsers.map((user) => ( + + {user.nickname} + {user.email} + + + + {formatDate(user.update_date)} + + {user.id !== userInfo?.id && ( + handleDeleteUser(user.id)} + > + + + )} + + + ))} + +
+
+
+
+ + {/* 加入的团队表格 */} + + + + + {t('setting.joinedTeams')} + + + + + + + {t('common.name')} + {t('setting.email')} + {t('setting.updateDate')} + {t('common.action')} + + + + {tenantList.map((tenant) => ( + + {tenant.nickname} + {tenant.email} + {formatDate(tenant.update_date)} + + {tenant.role === TENANT_ROLES.Invite ? ( + + + + + ) : ( + tenant.role !== TENANT_ROLES.Owner && ( + + ) + )} + + + ))} + +
+
+
+
+ + {/* 邀请用户对话框 */} + setInviteDialogOpen(false)} maxWidth="sm" fullWidth> + {t('setting.invite')} + + {error && ( + + {error} + + )} + setInviteEmail(e.target.value)} + placeholder={t('setting.emailPlaceholder') || '请输入邀请用户的邮箱地址'} + /> + + + + + + +
); }