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 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">
</head>
<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 [members, setMembers] = useState<AdminUser[]>([]);
const [managerMembers, setManagerMembers] = useState<AdminUser[]>([]);
const [managerLoading, setManagerLoading] = useState(false);
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(() => ({
page,
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) => {
setManagerModal({ open: true, team });
setSelectedManagerId(team.managerId || null);
setManagerLoading(true);
try {
const res = await getTeamMembersForAdmin(team.id);
setMembers(res.items || []);
} catch (e: any) {
message.error(e?.message || '加载成员失败');
setMembers([]);
} finally {
setManagerLoading(false);
}
const data = await loadTeamMembers(team.id);
setManagerMembers(data);
setManagerLoading(false);
};
const openMembersModal = async (team: AdminTeam) => {
setMembersModal({ open: true, team });
setMembersLoading(true);
const data = await loadTeamMembers(team.id);
setTeamMembers(data);
setMembersLoading(false);
};
const handleSetManager = async () => {
@@ -203,10 +220,11 @@ const AdminTeams: React.FC = () => {
{
title: '操作',
key: 'action',
width: 160,
width: 230,
fixed: 'right' as const,
render: (_: any, r: AdminTeam) => (
<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={<EditOutlined />} onClick={() => openEdit(r)}></Button>
<Popconfirm
@@ -313,7 +331,7 @@ const AdminTeams: React.FC = () => {
allowClear
loading={managerLoading}
optionFilterProp="label"
options={members.map((m) => ({
options={managerMembers.map((m) => ({
value: m.id,
label: `${m.username}${m.phone ? ` (${m.phone})` : ''}`,
}))}
@@ -321,6 +339,38 @@ const AdminTeams: React.FC = () => {
/>
</div>
</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>
);
};
+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 {
"id": team.id,
"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),
"created_at": team.created_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)
)
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 {
"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,
}