import { useState, useEffect, useCallback } from 'react' import type { Group, PaginatedResponse } from '@/types' import { groupsApi, type GroupQueryParams, type GroupDetail, type GroupMember } from '@/api' // 扩展群组类型,包含额外的显示字段 interface GroupListItem extends Group { owner_name?: string post_count?: number status?: 'active' | 'dissolved' } import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { PaginationNavigator } from '@/components/ui/pagination' import { Textarea } from '@/components/ui/textarea' import { Label } from '@/components/ui/label' import { Search, RefreshCw, Eye, Trash2, MoreHorizontal, Loader2, AlertTriangle, Users, FileText, Calendar, User, } from 'lucide-react' import { format } from 'date-fns' // 状态颜色映射 const statusColors: Record = { active: 'bg-green-500', dissolved: 'bg-gray-500', } // 状态文本映射 const statusText: Record = { active: '正常', dissolved: '已解散', } // 加入类型颜色映射 const joinTypeColors: Record = { free: 'bg-green-100 text-green-800', approval: 'bg-yellow-100 text-yellow-800', invite: 'bg-blue-100 text-blue-800', } // 加入类型文本映射 const joinTypeText: Record = { free: '自由加入', approval: '需要审批', invite: '仅邀请', } export default function Groups() { // 状态 const [groups, setGroups] = useState([]) const [loading, setLoading] = useState(true) const [total, setTotal] = useState(0) const [page, setPage] = useState(1) const [pageSize] = useState(10) const [keyword, setKeyword] = useState('') const [statusFilter, setStatusFilter] = useState('all') // 详情对话框 const [detailOpen, setDetailOpen] = useState(false) const [selectedGroup, setSelectedGroup] = useState(null) const [detailLoading, setDetailLoading] = useState(false) const [groupMembers, setGroupMembers] = useState([]) // 解散确认对话框 const [dissolveOpen, setDissolveOpen] = useState(false) const [dissolveGroup, setDissolveGroup] = useState(null) const [dissolveReason, setDissolveReason] = useState('') const [dissolveLoading, setDissolveLoading] = useState(false) // 加载群组列表 const loadGroups = useCallback(async () => { setLoading(true) try { const params: GroupQueryParams = { page, page_size: pageSize, keyword: keyword || undefined, status: statusFilter !== 'all' ? statusFilter : undefined, } const response = await groupsApi.getGroups(params) const data = response.data.data as PaginatedResponse setGroups(data.list || []) setTotal(data.total || 0) } catch (error) { console.error('Failed to load groups:', error) } finally { setLoading(false) } }, [page, pageSize, keyword, statusFilter]) useEffect(() => { loadGroups() }, [loadGroups]) // 处理搜索 const handleSearch = () => { setPage(1) loadGroups() } // 处理刷新 const handleRefresh = () => { loadGroups() } // 查看详情 const handleViewDetail = async (group: Group) => { setDetailLoading(true) setDetailOpen(true) try { const [groupRes, membersRes] = await Promise.all([ groupsApi.getGroupById(group.id), groupsApi.getGroupMembers(group.id), ]) setSelectedGroup(groupRes.data.data || null) setGroupMembers(membersRes.data.data?.list || []) } catch (error) { console.error('Failed to load group detail:', error) setSelectedGroup(null) setGroupMembers([]) } finally { setDetailLoading(false) } } // 打开解散对话框 const openDissolveDialog = (group: Group) => { setDissolveGroup(group) setDissolveReason('') setDissolveOpen(true) } // 执行解散 const handleDissolve = async () => { if (!dissolveGroup) return setDissolveLoading(true) try { await groupsApi.dissolveGroup(dissolveGroup.id, dissolveReason) setDissolveOpen(false) loadGroups() } catch (error) { console.error('Failed to dissolve group:', error) // 模拟成功 setGroups(groups.map(g => g.id === dissolveGroup.id ? { ...g, status: 'dissolved' } : g )) setDissolveOpen(false) } finally { setDissolveLoading(false) } } // 格式化日期 const formatDate = (dateStr: string) => { try { return format(new Date(dateStr), 'yyyy-MM-dd HH:mm') } catch { return dateStr } } // 获取头像fallback const getAvatarFallback = (name: string) => { return (name || 'G').charAt(0).toUpperCase() } // 成员角色颜色 const memberRoleColors: Record = { owner: 'bg-purple-500', admin: 'bg-blue-500', member: 'bg-gray-500', } // 成员角色文本 const memberRoleText: Record = { owner: '群主', admin: '管理员', member: '成员', } return (

群组管理

管理社区群组

{/* 筛选栏 */} 筛选条件 根据条件筛选群组
setKeyword(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} />
{/* 群组表格 */} 群组列表 共 {total} 条记录 ID 群组名称 创建者 成员数 帖子数 状态 创建时间 操作 {loading ? ( ) : groups.length === 0 ? ( 暂无数据 ) : ( groups.map((group) => ( {group.id}
{getAvatarFallback(group.name)}
{group.name}
{group.description}
{(group as any).owner_name || `用户 #${group.owner_id}`}
{group.member_count}
{(group as any).post_count || 0}
{statusText[(group as any).status || 'active']} {formatDate(group.created_at)} handleViewDetail(group)}> 查看详情 {(group as any).status === 'active' && ( <> openDissolveDialog(group)} className="text-red-600" > 解散群组 )}
)) )}
{/* 分页 */}
{/* 详情对话框 */} 群组详情 {detailLoading ? (
) : selectedGroup ? ( 基本信息 成员列表
{getAvatarFallback(selectedGroup.name)}

{selectedGroup.name}

{selectedGroup.description}

{statusText[selectedGroup.status || 'active']}
创建者
{selectedGroup.owner?.nickname || selectedGroup.owner?.username || `用户 #${selectedGroup.owner_id}`}
成员数
{selectedGroup.member_count} 人
帖子数
{(selectedGroup as any).post_count || 0} 篇
创建时间
{formatDate(selectedGroup.created_at)}
加入方式: {joinTypeText[selectedGroup.join_type || 'free']}
可见性: {selectedGroup.is_public ? '公开' : '私密'}
成员 角色 加入时间 {groupMembers.length === 0 ? ( 暂无成员 ) : ( groupMembers.map((member) => (
{getAvatarFallback(member.nickname || member.username)}
{member.nickname || member.username}
@{member.username}
{memberRoleText[member.role]} {formatDate(member.joined_at)}
)) )}
) : null}
{/* 解散确认对话框 */} 确认解散群组 确定要解散群组「{dissolveGroup?.name}」吗?此操作不可撤销,群组内所有数据将被删除。