This commit is contained in:
2026-07-06 14:23:02 +08:00
parent 14e0fd8b25
commit fa4f48a62f
4 changed files with 85 additions and 16 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
} }
})(); })();
</script> </script>
<script type="module" crossorigin src="/assets/index-CI8eObtr.js"></script> <script type="module" crossorigin src="/assets/index-BaN8_2WC.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css"> <link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
</head> </head>
<body> <body>
+62 -12
View File
@@ -36,10 +36,15 @@ const AdminTeams: React.FC = () => {
// 管理人弹窗状态 // 管理人弹窗状态
const [managerModal, setManagerModal] = useState<{ open: boolean; team: AdminTeam | null }>({ open: false, team: null }); const [managerModal, setManagerModal] = useState<{ open: boolean; team: AdminTeam | null }>({ open: false, team: null });
const [members, setMembers] = useState<AdminUser[]>([]); const [managerMembers, setManagerMembers] = useState<AdminUser[]>([]);
const [managerLoading, setManagerLoading] = useState(false); const [managerLoading, setManagerLoading] = useState(false);
const [selectedManagerId, setSelectedManagerId] = useState<string | null>(null); const [selectedManagerId, setSelectedManagerId] = useState<string | null>(null);
// 查看成员弹窗状态
const [membersModal, setMembersModal] = useState<{ open: boolean; team: AdminTeam | null }>({ open: false, team: null });
const [teamMembers, setTeamMembers] = useState<AdminUser[]>([]);
const [membersLoading, setMembersLoading] = useState(false);
const query = useMemo(() => ({ const query = useMemo(() => ({
page, page,
pageSize, pageSize,
@@ -113,19 +118,31 @@ const AdminTeams: React.FC = () => {
} }
}; };
const loadTeamMembers = async (teamId: string): Promise<AdminUser[]> => {
try {
const res = await getTeamMembersForAdmin(teamId);
return res.items || [];
} catch (e: any) {
message.error(e?.message || '加载成员失败');
return [];
}
};
const openManagerModal = async (team: AdminTeam) => { const openManagerModal = async (team: AdminTeam) => {
setManagerModal({ open: true, team }); setManagerModal({ open: true, team });
setSelectedManagerId(team.managerId || null); setSelectedManagerId(team.managerId || null);
setManagerLoading(true); setManagerLoading(true);
try { const data = await loadTeamMembers(team.id);
const res = await getTeamMembersForAdmin(team.id); setManagerMembers(data);
setMembers(res.items || []); setManagerLoading(false);
} catch (e: any) { };
message.error(e?.message || '加载成员失败');
setMembers([]); const openMembersModal = async (team: AdminTeam) => {
} finally { setMembersModal({ open: true, team });
setManagerLoading(false); setMembersLoading(true);
} const data = await loadTeamMembers(team.id);
setTeamMembers(data);
setMembersLoading(false);
}; };
const handleSetManager = async () => { const handleSetManager = async () => {
@@ -203,10 +220,11 @@ const AdminTeams: React.FC = () => {
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
width: 160, width: 230,
fixed: 'right' as const, fixed: 'right' as const,
render: (_: any, r: AdminTeam) => ( render: (_: any, r: AdminTeam) => (
<Space size={4}> <Space size={4}>
<Button type="link" size="small" icon={<UserOutlined />} onClick={() => openMembersModal(r)}></Button>
<Button type="link" size="small" icon={<SettingOutlined />} onClick={() => openManagerModal(r)}></Button> <Button type="link" size="small" icon={<SettingOutlined />} onClick={() => openManagerModal(r)}></Button>
<Button type="link" size="small" icon={<EditOutlined />} onClick={() => openEdit(r)}></Button> <Button type="link" size="small" icon={<EditOutlined />} onClick={() => openEdit(r)}></Button>
<Popconfirm <Popconfirm
@@ -313,7 +331,7 @@ const AdminTeams: React.FC = () => {
allowClear allowClear
loading={managerLoading} loading={managerLoading}
optionFilterProp="label" optionFilterProp="label"
options={members.map((m) => ({ options={managerMembers.map((m) => ({
value: m.id, value: m.id,
label: `${m.username}${m.phone ? ` (${m.phone})` : ''}`, label: `${m.username}${m.phone ? ` (${m.phone})` : ''}`,
}))} }))}
@@ -321,6 +339,38 @@ const AdminTeams: React.FC = () => {
/> />
</div> </div>
</Modal> </Modal>
{/* 查看成员弹窗 */}
<Modal
title={<Space><UserOutlined /> - {membersModal.team?.name}</Space>}
open={membersModal.open}
onCancel={() => setMembersModal({ open: false, team: null })}
footer={null}
width={600}
>
<div style={{ marginTop: 8 }}>
{membersModal.team && (
<Table
size="small"
rowKey="id"
loading={membersLoading}
dataSource={teamMembers}
pagination={false}
scroll={{ y: 400 }}
columns={[
{ title: '用户名', dataIndex: 'username', width: 140, render: (v: string) => <Typography.Text strong>{v}</Typography.Text> },
{ title: '手机号', dataIndex: 'phone', width: 130, render: (v: string) => v || '-' },
{ title: '积分', dataIndex: 'credits', width: 100, render: (v: number) => <Typography.Text style={{ color: '#6366f1' }}>{(v ?? 0).toFixed(2)}</Typography.Text> },
{
title: '状态', dataIndex: 'isActive', width: 80,
render: (v: boolean) => <Tag color={v ? 'green' : 'red'}>{v ? '启用' : '禁用'}</Tag>,
},
]}
locale={{ emptyText: '该团队暂无成员' }}
/>
)}
</div>
</Modal>
</div> </div>
); );
}; };
+21 -2
View File
@@ -31,7 +31,7 @@ def _team_snapshot(team: Team | None) -> dict[str, Any]:
} }
def _team_out_payload(team: Team, member_count: int = 0) -> dict[str, Any]: def _team_out_payload(team: Team, member_count: int = 0, manager_name: str | None = None) -> dict[str, Any]:
return { return {
"id": team.id, "id": team.id,
"name": team.name, "name": team.name,
@@ -42,6 +42,8 @@ def _team_out_payload(team: Team, member_count: int = 0) -> dict[str, Any]:
"member_count": int(member_count or 0), "member_count": int(member_count or 0),
"created_at": team.created_at, "created_at": team.created_at,
"updated_at": team.updated_at, "updated_at": team.updated_at,
"manager_id": getattr(team, "manager_id", None),
"manager_name": manager_name,
} }
@@ -112,8 +114,25 @@ async def list_teams(
.group_by(User.team_id) .group_by(User.team_id)
) )
member_map = {row[0]: int(row[1] or 0) for row in member_result.all()} member_map = {row[0]: int(row[1] or 0) for row in member_result.all()}
# 批量获取管理人用户名
manager_ids = [getattr(t, "manager_id", None) for t in teams if getattr(t, "manager_id", None)]
manager_name_map: dict[str, str] = {}
if manager_ids:
mgr_result = await db.execute(
select(User.id, User.username).where(User.id.in_(manager_ids))
)
manager_name_map = {row[0]: row[1] for row in mgr_result.all()}
return { return {
"items": [_team_out_payload(team, member_map.get(team.id, 0)) for team in teams], "items": [
_team_out_payload(
team,
member_map.get(team.id, 0),
manager_name_map.get(getattr(team, "manager_id", None)),
)
for team in teams
],
"total": total, "total": total,
} }