import { useMemo, useState } from 'react' import { keepPreviousData, useQuery } from '@tanstack/react-query' import { Alert, Button, Input, Select, Space, Tag, Typography, type TableColumnsType } from 'antd' import { EyeOutlined, ReloadOutlined } from '@ant-design/icons' import { fetchAdminDisputes } from '@/features/admin/api' import { AdminDisputeDetailDrawer } from '@/features/admin/AdminDisputeDetailDrawer' import { AdminPageHeader } from '@/features/admin/components/AdminPageHeader' import { DataTable } from '@/features/admin/components/DataTable' import { FilterBar, FilterField } from '@/features/admin/components/FilterBar' import { StatusTag } from '@/features/admin/components/StatusTag' import { disputeStatusMeta, disputeStatusOptions, disputeTypeLabel, disputeTypeOptions, } from '@/features/disputes/labels' import { orderStatusMap, readStatusMeta } from '@/features/admin/labels' import type { AdminDispute, AdminDisputeQuery } from '@/features/admin/types' import { useAdminSessionStore } from '@/stores/adminSession' import { formatDateTime, formatText } from '@/shared/utils/format' const emptyFilters: AdminDisputeQuery = { keyword: '', status: '', type: '', } function initiatorLabel(row: AdminDispute) { if (row.initiator_type === 'admin') { return `客服 #${row.initiator_admin_id ?? row.initiator_id}` } if (row.initiator_type === 'owner') return `号主 #${row.initiator_id}` return `租客 #${row.initiator_id}` } function buildColumns( onOpen: (row: AdminDispute) => void, canView: boolean ): TableColumnsType { return [ { title: '申诉', dataIndex: 'id', width: 170, fixed: 'left', render: (_, row) => (
{canView ? ( ) : ( 申诉 #{row.id} )} {disputeTypeLabel(row.type)}
), }, { title: '订单', key: 'order', width: 230, render: (_, row) => (
{row.order_no} {row.title} 商品 {formatText(row.listing_no)}
), }, { title: '双方', key: 'parties', width: 170, render: (_, row) => (
号主 {formatText(row.owner_phone)} 租客 {formatText(row.renter_phone)}
), }, { title: '发起方', key: 'initiator', width: 130, render: (_, row) => (
{initiatorLabel(row)} 目标用户 #{row.target_user_id}
), }, { title: '诉求描述', dataIndex: 'description', width: 260, ellipsis: true, render: value => {formatText(value)}, }, { title: '提交时间', dataIndex: 'created_at', width: 170, render: value => formatDateTime(value), }, { title: '操作', key: 'actions', width: 96, fixed: 'right', render: (_, row) => canView ? ( ) : ( 无权限 ), }, ] } export function AdminDisputesPage() { const [draft, setDraft] = useState(emptyFilters) const [applied, setApplied] = useState(emptyFilters) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) const [activeDispute, setActiveDispute] = useState(null) const [detailOpen, setDetailOpen] = useState(false) const permissions = useAdminSessionStore(state => state.admin?.permissions) const canView = Boolean(permissions?.includes('*') || permissions?.includes('dispute:view')) const { data, isPending, isFetching, isError, error, refetch } = useQuery({ queryKey: ['admin-disputes', applied, page, pageSize], queryFn: () => fetchAdminDisputes({ ...applied, page, page_size: pageSize }), placeholderData: keepPreviousData, }) const openDetail = (row: AdminDispute) => { if (!canView) return setActiveDispute(row) setDetailOpen(true) } const columns = useMemo(() => buildColumns(openDetail, canView), [canView]) const patchFilter = (patch: Partial) => { setDraft(current => ({ ...current, ...patch })) } const applyFilters = () => { setApplied({ ...draft, keyword: draft.keyword?.trim() ?? '' }) setPage(1) } const resetFilters = () => { setDraft(emptyFilters) setApplied(emptyFilters) setPage(1) } const hasActiveFilters = Boolean(draft.keyword?.trim()) || Boolean(draft.status) || Boolean(draft.type) const errorText = error instanceof Error ? error.message : '请稍后重试或重新登录后台。' const pendingCount = (data?.items ?? []).filter(item => ['open', 'processing'].includes(item.status) ).length return (
共 {data?.total ?? 0} 条 {pendingCount > 0 ? 本页待处理 {pendingCount} : null} {isFetching && !isPending ? 正在更新 : null} } extra={ } /> patchFilter({ keyword: event.target.value })} /> patchFilter({ type: value ?? '' })} /> {isError ? ( void refetch()}> 重试 } /> ) : null} title={申诉列表} extra={ {data ? `第 ${data.page} 页 · 当前 ${data.items.length} 条` : '加载中'} } rowKey="id" columns={columns} dataSource={data?.items ?? []} loading={isFetching} total={data?.total ?? 0} page={data?.page ?? page} pageSize={data?.page_size ?? pageSize} onPageChange={(nextPage, nextPageSize) => { setPage(nextPageSize !== pageSize ? 1 : nextPage) setPageSize(nextPageSize) }} onRowClick={canView ? row => openDetail(row) : undefined} emptyDescription="没有匹配的申诉" /> {canView ? ( setDetailOpen(false)} onUpdated={updated => { setActiveDispute(updated) void refetch() }} /> ) : null}
) }