1
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user