/** * filekorolheader: 企业审核页面 - 企业注册审核管理页面 * 功能:企业审核列表、搜索筛选、审核操作、详情查看 * 路径:/central-config/tenant/enterprise-audit * 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理,API集成,模块化组件,SearchFormPagination重构 */ 'use client'; import { useReducer, useEffect, useMemo, useRef, useCallback } from 'react'; import { toast } from 'sonner'; import { Building2, RefreshCw, Eye, Check, X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { SearchFormPagination, type SearchFieldConfig, type TableColumnConfig } from '@/components/common/searchFormPagination'; import { fetchTenantsForAudit, auditTenant, transformTenantData, TenantsQueryParams, Enterprise } from './components/enterpriseAuditApi'; import { AuditStatsCards } from './components/AuditStatsCards'; import { EnterpriseDetailDialog } from './components/EnterpriseDetailDialog'; // 审核状态管理 interface AuditState { enterprises: Enterprise[]; loading: boolean; error: string | null; pagination: { page: number; size: number; total: number; totalPages: number; hasNext: boolean; hasPrev: boolean; }; filters: { search: string; audit_status: string; }; sortBy?: string; sortOrder: 'asc' | 'desc'; selectedEnterprise: Enterprise | null; showDetailDialog: boolean; auditReason: string; actionLoading: boolean; } type AuditAction = | { type: 'SET_ENTERPRISES'; payload: { data: Enterprise[]; pagination: AuditState['pagination'] } } | { type: 'SET_LOADING'; payload: boolean } | { type: 'SET_ERROR'; payload: string | null } | { type: 'SET_FILTERS'; payload: Partial } | { type: 'SET_SORT'; payload: { sortBy?: string; sortOrder: 'asc' | 'desc' } } | { type: 'SET_PAGINATION'; payload: Partial } | { type: 'SET_SELECTED_ENTERPRISE'; payload: Enterprise | null } | { type: 'TOGGLE_DETAIL_DIALOG'; payload: boolean } | { type: 'SET_AUDIT_REASON'; payload: string } | { type: 'SET_ACTION_LOADING'; payload: boolean } | { type: 'REFRESH_DATA' }; const auditReducer = (state: AuditState, action: AuditAction): AuditState => { switch (action.type) { case 'SET_ENTERPRISES': return { ...state, enterprises: action.payload.data, pagination: action.payload.pagination, loading: false, error: null, }; case 'SET_LOADING': return { ...state, loading: action.payload }; case 'SET_ERROR': return { ...state, error: action.payload, loading: false }; case 'SET_FILTERS': return { ...state, filters: { ...state.filters, ...action.payload } }; case 'SET_SORT': return { ...state, sortBy: action.payload.sortBy, sortOrder: action.payload.sortOrder }; case 'SET_PAGINATION': return { ...state, pagination: { ...state.pagination, ...action.payload } }; case 'SET_SELECTED_ENTERPRISE': return { ...state, selectedEnterprise: action.payload }; case 'TOGGLE_DETAIL_DIALOG': return { ...state, showDetailDialog: !state.showDetailDialog }; case 'SET_AUDIT_REASON': return { ...state, auditReason: action.payload }; case 'SET_ACTION_LOADING': return { ...state, actionLoading: action.payload }; case 'REFRESH_DATA': return { ...state, error: null }; default: return state; } }; const initialState: AuditState = { enterprises: [], loading: false, error: null, pagination: { page: 1, size: 10, total: 0, totalPages: 0, hasNext: false, hasPrev: false, }, filters: { search: '', audit_status: 'all', }, sortBy: 'created_at', sortOrder: 'desc', selectedEnterprise: null, showDetailDialog: false, auditReason: '', actionLoading: false, }; export default function EnterpriseAuditPage() { const [state, dispatch] = useReducer(auditReducer, initialState); const isFirstLoad = useRef(true); // 搜索字段配置 const searchFields: SearchFieldConfig[] = [ { key: 'search', label: '搜索', type: 'text', placeholder: '搜索企业名称、编码...', }, { key: 'audit_status', label: '审核状态', type: 'select', defaultValue: 'all', options: [ { value: 'all', label: '全部状态' }, { value: '待审核', label: '待审核' }, { value: '已通过', label: '已通过' }, { value: '已驳回', label: '已驳回' }, ], }, ]; // 表格列配置 const columns: TableColumnConfig[] = [ { key: 'name', label: '企业名称', sortable: false, // 禁用排序 render: (value: string) => (
{value}
), }, { key: 'code', label: '企业编码', sortable: false, // 禁用排序 render: (value: string) => (
{value}
), }, { key: 'auditStatus', label: '审核状态', sortable: false, // 禁用排序 render: (value: string) => { const statusConfig = { '待审核': { label: '待审核', variant: 'default' as const, className: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200' }, '已通过': { label: '已通过', variant: 'default' as const, className: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' }, '已驳回': { label: '已驳回', variant: 'default' as const, className: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' }, }; const config = statusConfig[value as keyof typeof statusConfig] || statusConfig['待审核']; return ( {config.label} ); }, }, { key: 'contactPerson', label: '联系人', sortable: false, // 禁用排序 }, { key: 'contactPhone', label: '联系电话', sortable: false, // 禁用排序 render: (value: string) => (
{value || '-'}
), }, { key: 'createdAt', label: '创建时间', sortable: false, // 禁用排序 render: (value: string) => (
{value ? new Date(value).toLocaleDateString('zh-CN') : '-'}
), }, { key: 'actions', label: '操作', sortable: false, // 操作列不能排序 render: (_: any, row: Enterprise) => (
), }, ]; // 加载企业数据 - 移除依赖项,通过参数传递状态 const loadEnterprises = useCallback(async (params?: { filters?: Record; pagination?: { page: number; size: number }; sort?: { sortBy?: string; sortOrder: 'asc' | 'desc' }; resetPage?: boolean; }) => { try { dispatch({ type: 'SET_LOADING', payload: true }); const finalParams: TenantsQueryParams = { search: (params?.filters?.search ?? state.filters.search) || undefined, audit_status: params?.filters?.audit_status ?? state.filters.audit_status, page: params?.resetPage ? 1 : (params?.pagination?.page || state.pagination.page), size: params?.pagination?.size || state.pagination.size, order_by: params?.sort?.sortBy, sort_order: params?.sort?.sortOrder, }; // 处理audit_status,如果为'all'则不传该参数 if (finalParams.audit_status === 'all') { finalParams.audit_status = undefined; } const response = await fetchTenantsForAudit(finalParams); const transformedData = response.data.map(transformTenantData); dispatch({ type: 'SET_ENTERPRISES', payload: { data: transformedData, pagination: { page: response.page, size: response.size, total: response.total, totalPages: response.total_pages, hasNext: response.has_next, hasPrev: response.has_prev, } } }); } catch (error) { console.error('Failed to load enterprises for audit:', error); const errorMessage = error instanceof Error ? error.message : '加载企业审核数据失败'; dispatch({ type: 'SET_ERROR', payload: errorMessage }); toast.error(errorMessage); } }, []); // 移除所有依赖,使用参数传递状态变化 // 计算统计数据 const stats = useMemo(() => ({ total: state.pagination.total, pending: state.enterprises.filter(e => e.auditStatus === '待审核').length, approved: state.enterprises.filter(e => e.auditStatus === '已通过').length, rejected: state.enterprises.filter(e => e.auditStatus === '已驳回').length, }), [state.enterprises, state.pagination.total]); // 事件处理器 const handleSearch = useCallback((filters: Record) => { dispatch({ type: 'SET_FILTERS', payload: filters }); loadEnterprises({ filters, pagination: { page: 1, size: state.pagination.size } }); }, [loadEnterprises, state.pagination.size]); const handleSort = useCallback((sortBy: string, sortOrder: 'asc' | 'desc') => { dispatch({ type: 'SET_SORT', payload: { sortBy, sortOrder } }); loadEnterprises({ filters: state.filters, sort: { sortBy, sortOrder }, resetPage: true }); }, [loadEnterprises, state.filters]); const handlePageChange = useCallback((page: number) => { // 边界检查,确保页码在有效范围内 if (page < 1) { page = 1; } else if (page > state.pagination.totalPages && state.pagination.totalPages > 0) { page = state.pagination.totalPages; } dispatch({ type: 'SET_PAGINATION', payload: { page } }); loadEnterprises({ filters: state.filters, pagination: { page, size: state.pagination.size } }); }, [loadEnterprises, state.filters, state.pagination.size, state.pagination.totalPages]); const handleSizeChange = useCallback((size: number) => { dispatch({ type: 'SET_PAGINATION', payload: { size, page: 1 } }); loadEnterprises({ filters: state.filters, pagination: { page: 1, size } }); }, [loadEnterprises, state.filters]); const handleRefresh = useCallback(() => { dispatch({ type: 'REFRESH_DATA' }); loadEnterprises({ resetPage: true }); toast.success('数据已刷新'); }, [loadEnterprises]); const handleViewDetail = (enterprise: Enterprise) => { dispatch({ type: 'SET_SELECTED_ENTERPRISE', payload: enterprise }); dispatch({ type: 'SET_AUDIT_REASON', payload: '' }); dispatch({ type: 'TOGGLE_DETAIL_DIALOG', payload: true }); }; const handleAuditReasonChange = (value: string) => { dispatch({ type: 'SET_AUDIT_REASON', payload: value }); }; const handleApprove = async () => { if (!state.selectedEnterprise) return; try { dispatch({ type: 'SET_ACTION_LOADING', payload: true }); const updatedTenant = await auditTenant(state.selectedEnterprise.id, { audit_status: '已通过', audit_comment: state.auditReason || '审核通过', }); // 更新本地状态 const updatedEnterprise = transformTenantData(updatedTenant); dispatch({ type: 'SET_ENTERPRISES', payload: { data: state.enterprises.map(ent => ent.id === state.selectedEnterprise?.id ? updatedEnterprise : ent ), pagination: state.pagination } }); dispatch({ type: 'TOGGLE_DETAIL_DIALOG', payload: false }); toast.success('审核通过'); // 立即刷新列表,无需延迟 loadEnterprises({ resetPage: true }); } catch (error) { console.error('Approve failed:', error); const errorMessage = error instanceof Error ? error.message : '审核通过失败'; toast.error(errorMessage); } finally { dispatch({ type: 'SET_ACTION_LOADING', payload: false }); } }; const handleReject = async () => { if (!state.selectedEnterprise) return; if (!state.auditReason.trim()) { toast.error('请填写驳回原因'); return; } try { dispatch({ type: 'SET_ACTION_LOADING', payload: true }); const updatedTenant = await auditTenant(state.selectedEnterprise.id, { audit_status: '已驳回', audit_comment: state.auditReason, }); // 更新本地状态 const updatedEnterprise = transformTenantData(updatedTenant); dispatch({ type: 'SET_ENTERPRISES', payload: { data: state.enterprises.map(ent => ent.id === state.selectedEnterprise?.id ? updatedEnterprise : ent ), pagination: state.pagination } }); dispatch({ type: 'TOGGLE_DETAIL_DIALOG', payload: false }); toast.success('已驳回'); // 立即刷新列表,无需延迟 loadEnterprises({ resetPage: true }); } catch (error) { console.error('Reject failed:', error); const errorMessage = error instanceof Error ? error.message : '审核驳回失败'; toast.error(errorMessage); } finally { dispatch({ type: 'SET_ACTION_LOADING', payload: false }); } }; return (
{/* 页面标题和描述 */}

企业审核

管理企业注册与变更审核流程

{/* 统计卡片 - 保留原有功能 */} {/* 搜索、表格和分页 - 使用重构后的组件 */} 刷新 } searchFields={searchFields} columns={columns} data={state.enterprises} loading={state.loading} error={state.error} pagination={state.pagination} sortBy={state.sortBy} sortOrder={state.sortOrder} onPageChange={handlePageChange} onSizeChange={handleSizeChange} onSearch={handleSearch} onSort={handleSort} emptyIcon={} emptyText="暂无企业审核数据" showSizeSelector={true} showPageInfo={true} sizeOptions={[10, 20, 50, 100]} /> {/* 企业详情对话框 - 保留原有功能 */} dispatch({ type: 'TOGGLE_DETAIL_DIALOG', payload: open })} enterprise={state.selectedEnterprise} auditReason={state.auditReason} onAuditReasonChange={handleAuditReasonChange} onApprove={handleApprove} onReject={handleReject} loading={state.actionLoading} />
); }