'use client' import { useState, useEffect, useCallback } from 'react' import { Card, CardContent } from '@/components/ui/Card' import { Button } from '@/components/ui/Button' import { Modal } from '@/components/ui/Modal' import { SuccessTag, PendingTag } from '@/components/ui/Tag' import { useToast } from '@/components/ui/Toast' import { Search, Plus, Users, Copy, CheckCircle, MoreVertical, Building2, AlertCircle, UserPlus, Trash2, FolderPlus, Loader2, } from 'lucide-react' import { api } from '@/lib/api' import { USE_MOCK } from '@/contexts/AuthContext' import type { AgencyDetail } from '@/types/organization' import type { ProjectResponse } from '@/types/project' // ==================== Mock 数据 ==================== const mockAgencies: AgencyDetail[] = [ { id: 'AG789012', name: '星耀传媒', contact_name: '张经理', force_pass_enabled: true }, { id: 'AG456789', name: '创意无限', contact_name: '李总', force_pass_enabled: false }, { id: 'AG123456', name: '美妆达人MCN', contact_name: '王经理', force_pass_enabled: false }, { id: 'AG111111', name: '蓝海科技', force_pass_enabled: true }, ] const mockProjects: ProjectResponse[] = [ { id: 'PJ000001', name: 'XX品牌618推广', brand_id: 'BR000001', status: 'active', agencies: [], task_count: 5, created_at: '2025-06-01', updated_at: '2025-06-01' }, { id: 'PJ000002', name: '口红系列推广', brand_id: 'BR000001', status: 'active', agencies: [], task_count: 3, created_at: '2025-07-01', updated_at: '2025-07-01' }, ] function StatusTag({ forcePass }: { forcePass: boolean }) { if (forcePass) return 可强制通过 return 标准权限 } function AgencySkeleton() { return (
) } export default function AgenciesManagePage() { const toast = useToast() const [searchQuery, setSearchQuery] = useState('') const [agencies, setAgencies] = useState([]) const [projects, setProjects] = useState([]) const [loading, setLoading] = useState(true) const [copiedId, setCopiedId] = useState(null) // 邀请代理商弹窗 const [showInviteModal, setShowInviteModal] = useState(false) const [inviteAgencyId, setInviteAgencyId] = useState('') const [inviting, setInviting] = useState(false) const [inviteResult, setInviteResult] = useState<{ success: boolean; message: string } | null>(null) // 操作菜单状态 const [openMenuId, setOpenMenuId] = useState(null) // 删除确认弹窗状态 const [deleteModal, setDeleteModal] = useState<{ open: boolean; agency: AgencyDetail | null }>({ open: false, agency: null }) const [deleting, setDeleting] = useState(false) // 分配项目弹窗状态 const [assignModal, setAssignModal] = useState<{ open: boolean; agency: AgencyDetail | null }>({ open: false, agency: null }) const [selectedProjects, setSelectedProjects] = useState([]) const [assigning, setAssigning] = useState(false) const loadData = useCallback(async () => { if (USE_MOCK) { setAgencies(mockAgencies) setProjects(mockProjects) setLoading(false) return } try { const [agencyRes, projectRes] = await Promise.all([ api.listBrandAgencies(), api.listProjects(1, 100), ]) setAgencies(agencyRes.items) setProjects(projectRes.items) } catch (err) { console.error('Failed to load data:', err) toast.error('加载数据失败') } finally { setLoading(false) } }, [toast]) useEffect(() => { loadData() }, [loadData]) const filteredAgencies = agencies.filter(agency => agency.name.toLowerCase().includes(searchQuery.toLowerCase()) || agency.id.toLowerCase().includes(searchQuery.toLowerCase()) || (agency.contact_name || '').toLowerCase().includes(searchQuery.toLowerCase()) ) // 复制代理商ID const handleCopyAgencyId = async (agencyId: string) => { await navigator.clipboard.writeText(agencyId) setCopiedId(agencyId) setTimeout(() => setCopiedId(null), 2000) } // 邀请代理商 const handleInvite = async () => { if (!inviteAgencyId.trim()) { setInviteResult({ success: false, message: '请输入代理商ID' }) return } const idPattern = /^AG\d{6}$/ if (!idPattern.test(inviteAgencyId.toUpperCase())) { setInviteResult({ success: false, message: '代理商ID格式错误,应为AG+6位数字' }) return } if (agencies.some(a => a.id === inviteAgencyId.toUpperCase())) { setInviteResult({ success: false, message: '该代理商已在您的列表中' }) return } setInviting(true) try { if (USE_MOCK) { await new Promise(resolve => setTimeout(resolve, 500)) } else { await api.inviteAgency(inviteAgencyId.toUpperCase()) } setInviteResult({ success: true, message: `已向代理商 ${inviteAgencyId.toUpperCase()} 发送邀请` }) } catch (err) { setInviteResult({ success: false, message: err instanceof Error ? err.message : '邀请失败' }) } finally { setInviting(false) } } const handleCloseInviteModal = () => { setShowInviteModal(false) setInviteAgencyId('') setInviteResult(null) } const handleConfirmInvite = async () => { if (inviteResult?.success) { handleCloseInviteModal() await loadData() } } // 打开删除确认 const handleOpenDelete = (agency: AgencyDetail) => { setDeleteModal({ open: true, agency }) setOpenMenuId(null) } // 确认删除 const handleConfirmDelete = async () => { if (!deleteModal.agency) return setDeleting(true) try { if (USE_MOCK) { await new Promise(resolve => setTimeout(resolve, 500)) setAgencies(prev => prev.filter(a => a.id !== deleteModal.agency!.id)) } else { await api.removeAgency(deleteModal.agency.id) await loadData() } toast.success('已移除代理商') } catch (err) { toast.error('移除失败') } finally { setDeleting(false) setDeleteModal({ open: false, agency: null }) } } // 打开分配项目弹窗 const handleOpenAssign = (agency: AgencyDetail) => { setSelectedProjects([]) setAssignModal({ open: true, agency }) setOpenMenuId(null) } // 切换项目选择 const toggleProjectSelection = (projectId: string) => { setSelectedProjects(prev => prev.includes(projectId) ? prev.filter(id => id !== projectId) : [...prev, projectId] ) } // 确认分配项目 const handleConfirmAssign = async () => { if (!assignModal.agency || selectedProjects.length === 0) return setAssigning(true) try { if (USE_MOCK) { await new Promise(resolve => setTimeout(resolve, 500)) } else { for (const projectId of selectedProjects) { await api.assignAgencies(projectId, [assignModal.agency.id]) } } const projectNames = projects .filter(p => selectedProjects.includes(p.id)) .map(p => p.name) .join('、') toast.success(`已将代理商「${assignModal.agency.name}」分配到项目「${projectNames}」`) } catch (err) { toast.error('分配失败') } finally { setAssigning(false) setAssignModal({ open: false, agency: null }) setSelectedProjects([]) } } return (
{/* 页面标题 */}

代理商管理

管理合作代理商,查看代理商绩效数据

{/* 统计卡片 */}

总代理商

{agencies.length}

可强制通过

{agencies.filter(a => a.force_pass_enabled).length}

关联项目

{projects.length}

{/* 搜索 */}
setSearchQuery(e.target.value)} className="w-full pl-10 pr-4 py-2.5 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo" />
{/* 代理商列表 */} {loading ? (
) : ( {filteredAgencies.map((agency) => ( ))}
代理商 代理商ID 联系人 权限 操作
{agency.name}
{agency.id}
{agency.contact_name || '-'}
{openMenuId === agency.id && (
)}
)} {!loading && filteredAgencies.length === 0 && (

没有找到匹配的代理商

)}
{/* 邀请代理商弹窗 */}

输入代理商ID邀请合作。代理商ID可在代理商的个人中心查看。

{ setInviteAgencyId(e.target.value.toUpperCase()) setInviteResult(null) }} placeholder="例如: AG789012" className="flex-1 px-4 py-2.5 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary font-mono focus:outline-none focus:ring-2 focus:ring-accent-indigo" />

代理商ID格式:AG + 6位数字

{inviteResult && (
{inviteResult.success ? ( ) : ( )} {inviteResult.message}
)}
{/* 删除确认弹窗 */} setDeleteModal({ open: false, agency: null })} title="确认移除代理商" >

确定要移除代理商「{deleteModal.agency?.name}」吗?

移除后该代理商将无法继续参与您的项目,该代理商下的达人也将受到影响。

{/* 分配项目弹窗 */} { setAssignModal({ open: false, agency: null }); setSelectedProjects([]); }} title={`分配代理商到项目 - ${assignModal.agency?.name}`} >

选择要将代理商分配到的项目,可多选。

{projects.map((project) => { const isSelected = selectedProjects.includes(project.id) return ( ) })}
{selectedProjects.length > 0 && (

已选择 {selectedProjects.length} 个项目

)}
{/* 点击其他地方关闭菜单 */} {openMenuId && (
setOpenMenuId(null)} /> )}
) }