"use client" import { BanIcon, CheckCircle2Icon, MoreHorizontalIcon, PlusIcon, RefreshCwIcon, SearchIcon, Trash2Icon, } from "lucide-react" import { useCallback, useEffect, useState } from "react" import { toast } from "sonner" import { ListPagination } from "@/components/list-pagination" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { ButtonGroup } from "@/components/ui/button-group" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { type PageResult } from "@/lib/api/admin" import { createCompany, deleteCompany, fetchCompanies, updateCompany, updateCompanyStatus, type AdminCompany, type CreateAdminCompanyPayload, } from "@/lib/api/company" import { getEnumOptions } from "@/lib/enums" import { Status, StatusLabels } from "@/lib/generated/enums" import { EditDialog } from "./_components/edit" const listStatusOptions = [ { value: "all", label: "全部状态" }, ...getEnumOptions(StatusLabels) .filter((item) => Number(item.value) !== Status.Deleted) .map((item) => ({ value: String(item.value), label: item.label, })), ] as const function getStatusLabel( value: string, options: ReadonlyArray<{ value: string; label: string }> ) { return options.find((item) => item.value === value)?.label ?? "请选择状态" } export default function DashboardCompaniesPage() { const [nameInput, setNameInput] = useState("") const [codeInput, setCodeInput] = useState("") const [statusFilterInput, setStatusFilterInput] = useState("all") const [name, setName] = useState("") const [code, setCode] = useState("") const [statusFilter, setStatusFilter] = useState("all") const [page, setPage] = useState(1) const [limit, setLimit] = useState(20) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [actionLoadingId, setActionLoadingId] = useState(null) const [dialogOpen, setDialogOpen] = useState(false) const [editingItem, setEditingItem] = useState(null) const [result, setResult] = useState>({ results: [], page: { page: 1, limit: 20, total: 0 }, }) const loadData = useCallback(async () => { setLoading(true) try { const data = await fetchCompanies({ name: name.trim() || undefined, code: code.trim() || undefined, status: statusFilter === "all" ? undefined : statusFilter, page, limit, }) setResult(data) } catch (error) { toast.error(error instanceof Error ? error.message : "加载公司列表失败") } finally { setLoading(false) } }, [code, limit, name, page, statusFilter]) useEffect(() => { void loadData() }, [loadData]) function applyFilters() { setName(nameInput) setCode(codeInput) setStatusFilter(statusFilterInput) setPage(1) } function handleFilterKeyDown(event: React.KeyboardEvent) { if (event.key !== "Enter") return event.preventDefault() applyFilters() } function handlePageChange(nextPage: number) { if (nextPage < 1 || nextPage === page) return setPage(nextPage) } function openCreateDialog() { setEditingItem(null) setDialogOpen(true) } function openEditDialog(item: AdminCompany) { setEditingItem(item) setDialogOpen(true) } function handleDialogOpenChange(open: boolean) { if (saving) return if (!open) setEditingItem(null) setDialogOpen(open) } async function handleSubmit(payload: CreateAdminCompanyPayload) { if (saving) return setSaving(true) try { if (editingItem) { await updateCompany({ id: editingItem.id, ...payload }) toast.success(`已更新公司:${editingItem.name}`) } else { await createCompany(payload) toast.success(`已创建公司:${payload.name}`) } setDialogOpen(false) setEditingItem(null) await loadData() } catch (error) { toast.error(error instanceof Error ? error.message : "保存公司失败") } finally { setSaving(false) } } async function handleToggleStatus(item: AdminCompany) { setActionLoadingId(item.id) try { const nextStatus = item.status === 0 ? 1 : 0 await updateCompanyStatus(item.id, nextStatus) toast.success(`已${nextStatus === 0 ? "启用" : "禁用"}:${item.name}`) await loadData() } catch (error) { toast.error(error instanceof Error ? error.message : "更新状态失败") } finally { setActionLoadingId(null) } } async function handleDelete(item: AdminCompany) { setActionLoadingId(item.id) try { await deleteCompany(item.id) toast.success(`已删除公司:${item.name}`) await loadData() } catch (error) { toast.error(error instanceof Error ? error.message : "删除公司失败") } finally { setActionLoadingId(null) } } return ( <>
setNameInput(event.target.value)} onKeyDown={handleFilterKeyDown} placeholder="按公司名称筛选" className="pl-9" />
setCodeInput(event.target.value)} onKeyDown={handleFilterKeyDown} placeholder="按公司编码筛选" className="w-full xl:w-48" />
ID 公司名称 公司编码 客户数 状态 备注 操作 {result.results.length === 0 && !loading ? ( 暂无公司数据 ) : ( result.results.map((item) => { const actionLoading = actionLoadingId === item.id return ( {item.id} {item.name} {item.code || "-"} {item.customerCount} {StatusLabels[item.status as Status] ?? "未知"}
{item.remark || "-"}
} aria-label={`更多操作 ${item.name}`} > void handleToggleStatus(item)} > {actionLoading ? ( "处理中..." ) : item.status === Status.Ok ? ( <> 禁用 ) : ( <> 启用 )} void handleDelete(item)} > 删除
) }) )}
{ setLimit(nextLimit) setPage(1) }} />
) }