diff --git a/crop-x/next-env.d.ts b/crop-x/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/crop-x/next-env.d.ts +++ b/crop-x/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/crop-x/src/app/(app)/central-config/tenant/enterprise-management/components/enterpriseApi.ts b/crop-x/src/app/(app)/central-config/tenant/enterprise-management/components/enterpriseApi.ts new file mode 100644 index 0000000..fe8ea73 --- /dev/null +++ b/crop-x/src/app/(app)/central-config/tenant/enterprise-management/components/enterpriseApi.ts @@ -0,0 +1,232 @@ +/** + * filekorolheader: 企业管理API接口 - 企业数据查询接口服务 + * 功能:API请求封装、数据转换、错误处理、分页查询 + * 路径:/central-config/tenant/enterprise-management/components/enterpriseApi + * 规范:遵循crop-x/docs/开发项目规范.md,使用SDK API调用,TypeScript类型安全 + */ + +// API响应数据类型定义 +import { getAuthToken } from "@/utils/token.ts"; +import { listTenantsApiV1TenantsGet, getTenantAuditLogsApiV1TenantsAuditLogsGet } from "@/lib/api/sdk.gen"; +export interface TenantData { + id: string; + tenant_code: string; + is_active: boolean; + company_name: string; + company_type: string | null; + province: string | null; + city: string | null; + district: string | null; + detailed_address: string | null; + registrant: string | null; + contact_phone: string | null; + bank_account: string | null; + bank_name: string | null; + bank_full_name: string | null; + bank_address: string | null; + social_credit_code: string | null; + legal_person_name: string | null; + company_scale: string | null; + registered_capital: string | null; + established_date: string | null; + invoice_type: string | null; + business_scope: string | null; + submit_time: string | null; + audit_time: string | null; + auditor: string | null; + audit_status: string; + audit_comment: string | null; + created_at: string; + updated_at: string; +} + +// API响应接口 +export interface TenantsApiResponse { + data: TenantData[]; + total: number; + page: number; + size: number; + total_pages: number; + has_next: boolean; + has_prev: boolean; +} + +// 查询参数接口 +export interface TenantsQueryParams { + search?: string; + audit_status?: string; + page?: number; + size?: number; + order_by?: string; + sort_order?: 'asc' | 'desc'; +} + +// 企业页面数据类型(转换后的) +export interface Enterprise { + id: string; + name: string; + code: string; + type: string; + status: 'active' | 'inactive'; + auditStatus: 'not_submitted' | 'pending' | 'approved' | 'rejected' | 'draft'; + createdAt: string; + updatedAt: string; + contact?: string; + phone?: string; + contactPhone?: string; + province?: string; + city?: string; + district?: string; + address?: string; + registrant?: string; + companySize?: string; + registeredCapital?: string; + establishmentDate?: string; + invoiceType?: string; + socialCreditCode?: string; + businessScope?: string; + legalPerson?: string; + bankAccount?: string; + bankName?: string; + bankFullName?: string; + bankAddress?: string; + submitTime?: string; + auditTime?: string; + auditor?: string; + auditComment?: string; +} + +/** + * 获取企业列表数据 + */ +export async function fetchTenants(params: TenantsQueryParams = {}): Promise { + try { + // 构建查询参数对象 + const queryParams: any = {}; + + if (params.search) queryParams.search = params.search; + if (params.audit_status) queryParams.audit_status = params.audit_status; + if (params.page) queryParams.page = params.page; + if (params.size) queryParams.size = params.size; + if (params.order_by) queryParams.order_by = params.order_by; + if (params.sort_order) queryParams.sort_order = params.sort_order; + + // 默认参数 + if (!params.page) queryParams.page = 1; + if (!params.size) queryParams.size = 10; + if (!params.sort_order) queryParams.sort_order = 'desc'; + + // 使用SDK API调用,添加缓存破坏器和认证头部 + const token = getAuthToken(); + const response = await listTenantsApiV1TenantsGet({ + query: { + ...queryParams, + // 添加时间戳防止缓存 + _t: Date.now(), + }, + headers: token ? { + 'Authorization': `Bearer ${token}`, + } : undefined, + }); + + if (response.error) { + throw new Error(`API error: ${response.error.message || 'Unknown error'}`); + } + + const data = response.data as any; + + // 转换响应数据格式以匹配现有的接口 + // API返回的数据结构: { data: [...], total: 25, page: 1, size: 10, ... } + return { + data: data?.data || [], // 注意:实际数据在 data.data 中 + total: data?.total || 0, + page: data?.page || 1, + size: data?.size || 10, + total_pages: data?.total_pages || 0, + has_next: data?.has_next || false, + has_prev: data?.has_prev || false, + }; + } catch (error) { + console.error('Failed to fetch tenants:', error); + throw error; + } +} + +/** + * 将API数据转换为页面所需的企业数据格式 + */ +export function transformTenantData(tenant: TenantData): Enterprise { + return { + id: tenant.id, + name: tenant.company_name, + code: tenant.tenant_code, + type: tenant.company_type || '未分类', + status: tenant.is_active ? 'active' : 'inactive', + auditStatus: mapAuditStatus(tenant.audit_status), + createdAt: formatDate(tenant.created_at), + updatedAt: formatDate(tenant.updated_at), + contact: tenant.registrant, + phone: tenant.contact_phone, + contactPhone: tenant.contact_phone, + province: tenant.province, + city: tenant.city, + district: tenant.district, + address: tenant.detailed_address, + registrant: tenant.registrant, + companySize: tenant.company_scale, + registeredCapital: tenant.registered_capital, + establishmentDate: tenant.established_date ? + new Date(tenant.established_date).toLocaleDateString('zh-CN') : undefined, + invoiceType: tenant.invoice_type, + socialCreditCode: tenant.social_credit_code, + businessScope: tenant.business_scope, + legalPerson: tenant.legal_person_name, + bankAccount: tenant.bank_account, + bankName: tenant.bank_name, + bankFullName: tenant.bank_full_name, + bankAddress: tenant.bank_address, + submitTime: tenant.submit_time ? formatDate(tenant.submit_time) : undefined, + auditTime: tenant.audit_time ? formatDate(tenant.audit_time) : undefined, + auditor: tenant.auditor, + auditComment: tenant.audit_comment, + }; +} + +/** + * 映射审核状态 + */ +function mapAuditStatus(status: string): Enterprise['auditStatus'] { + switch (status) { + case '未提交': + case '草稿': + return 'draft'; + case '待审核': + return 'pending'; + case '已通过': + case '审核通过': + return 'approved'; + case '已拒绝': + case '已驳回': + return 'rejected'; + default: + return 'draft'; + } +} + +/** + * 格式化日期 + */ +function formatDate(dateString: string): string { + try { + const date = new Date(dateString); + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }).replace(/\//g, '-'); + } catch (error) { + return dateString; + } +} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/tenant/enterprise-management/components/enterpriseReducer.tsx b/crop-x/src/app/(app)/central-config/tenant/enterprise-management/components/enterpriseReducer.tsx new file mode 100644 index 0000000..11f64ed --- /dev/null +++ b/crop-x/src/app/(app)/central-config/tenant/enterprise-management/components/enterpriseReducer.tsx @@ -0,0 +1,164 @@ +/** + * filekorolheader: 企业管理状态管理 - 企业数据状态管理核心 + * 功能:API数据管理、分页状态、加载状态、错误处理 + * 路径:/central-config/tenant/enterprise-management/components/enterpriseReducer + * 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理模式 + */ + +import { Enterprise } from './enterpriseApi'; + +export interface FormData { + name: string; + code: string; + type: string; +} + +export interface EnterpriseState { + 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; + }; + showAddDialog: boolean; + showViewDialog: boolean; + showStatusDialog: boolean; + selectedEnterprise: Enterprise | null; + statusAction: 'enable' | 'disable'; + formData: FormData; + sortBy?: string; + sortOrder: 'asc' | 'desc'; +} + +export type EnterpriseAction = + | { type: 'SET_LOADING'; payload: boolean } + | { type: 'SET_ERROR'; payload: string | null } + | { type: 'SET_ENTERPRISES'; payload: { data: Enterprise[]; pagination: EnterpriseState['pagination'] } } + | { type: 'SET_FILTERS'; payload: Partial } + | { type: 'SET_PAGINATION'; payload: Partial } + | { type: 'SET_SORT'; payload: { sortBy?: string; sortOrder: 'asc' | 'desc' } } + | { type: 'TOGGLE_ADD_DIALOG'; payload: boolean } + | { type: 'TOGGLE_VIEW_DIALOG'; payload: boolean } + | { type: 'TOGGLE_STATUS_DIALOG'; payload: boolean } + | { type: 'SET_SELECTED_ENTERPRISE'; payload: Enterprise | null } + | { type: 'SET_STATUS_ACTION'; payload: 'enable' | 'disable' } + | { type: 'UPDATE_FORM_DATA'; payload: Partial } + | { type: 'RESET_FORM_DATA' } + | { type: 'REFRESH_DATA' }; + +// 初始状态 +export const initialState: EnterpriseState = { + enterprises: [], + loading: false, + error: null, + pagination: { + page: 1, + size: 10, + total: 0, + totalPages: 0, + hasNext: false, + hasPrev: false, + }, + filters: { + search: '', + audit_status: '', + }, + showAddDialog: false, + showViewDialog: false, + showStatusDialog: false, + selectedEnterprise: null, + statusAction: 'enable', + formData: { + name: '', + code: '', + type: '' + }, + sortBy: undefined, + sortOrder: 'desc', +}; + +// Reducer +export function enterpriseReducer(state: EnterpriseState, action: EnterpriseAction): EnterpriseState { + switch (action.type) { + case 'SET_LOADING': + return { ...state, loading: action.payload }; + + case 'SET_ERROR': + return { ...state, error: action.payload, loading: false }; + + case 'SET_ENTERPRISES': + return { + ...state, + enterprises: action.payload.data, + pagination: action.payload.pagination, + loading: false, + error: null, + }; + + case 'SET_FILTERS': + return { + ...state, + filters: { ...state.filters, ...action.payload }, + pagination: { ...state.pagination, page: 1 }, // 重置到第一页 + }; + + case 'SET_PAGINATION': + return { + ...state, + pagination: { ...state.pagination, ...action.payload }, + }; + + case 'SET_SORT': + return { + ...state, + sortBy: action.payload.sortBy, + sortOrder: action.payload.sortOrder, + }; + + case 'TOGGLE_ADD_DIALOG': + return { + ...state, + showAddDialog: action.payload, + ...(action.payload === false ? { formData: initialState.formData } : {}) + }; + + case 'TOGGLE_VIEW_DIALOG': + return { ...state, showViewDialog: action.payload }; + + case 'TOGGLE_STATUS_DIALOG': + return { ...state, showStatusDialog: action.payload }; + + case 'SET_SELECTED_ENTERPRISE': + return { ...state, selectedEnterprise: action.payload }; + + case 'SET_STATUS_ACTION': + return { ...state, statusAction: action.payload }; + + case 'UPDATE_FORM_DATA': + return { + ...state, + formData: { ...state.formData, ...action.payload } + }; + + case 'RESET_FORM_DATA': + return { ...state, formData: initialState.formData }; + + case 'REFRESH_DATA': + return { + ...state, + error: null, // 清除错误状态 + }; + + default: + return state; + } +} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/tenant/enterprise-management/page.tsx b/crop-x/src/app/(app)/central-config/tenant/enterprise-management/page.tsx index ec0cea3..face2d1 100644 --- a/crop-x/src/app/(app)/central-config/tenant/enterprise-management/page.tsx +++ b/crop-x/src/app/(app)/central-config/tenant/enterprise-management/page.tsx @@ -1,6 +1,12 @@ +/** + * filekorolheader: 企业管理 - 企业信息管理与维护页面 + * 功能:企业列表查询、详情查看、状态管理、分页筛选 + * 路径:/central-config/tenant/enterprise-management + * 规范:遵循crop-x/docs/开发项目规范.md,使用useReducer状态管理,API集成,shadcn语义化样式 + */ 'use client'; -import { useReducer, useMemo } from 'react'; +import { useReducer, useEffect, useMemo } from 'react'; import { Card } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -11,246 +17,119 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { Building2, Plus, Eye, Power, PowerOff, Search, Hash, FileText, CreditCard, User } from 'lucide-react'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Building2, Eye, Power, PowerOff, Search, FileText, CreditCard, User, RefreshCw, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-react'; import { toast } from 'sonner'; -// Types -interface Enterprise { - id: string; - name: string; - code: string; - type: string; - status: 'active' | 'inactive'; - auditStatus: 'not_submitted' | 'pending' | 'approved' | 'rejected'; - createdAt: string; - contact?: string; - phone?: string; - contactPhone?: string; - province?: string; - city?: string; - district?: string; - address?: string; - registrant?: string; - companySize?: string; - registeredCapital?: string; - establishmentDate?: string; - invoiceType?: string; - socialCreditCode?: string; - businessScope?: string; - businessLicense?: string; - bankAccount?: string; - bankName?: string; - bankFullName?: string; - bankAddress?: string; - bankLicense?: string; - legalPerson?: string; - idCardFront?: string; - idCardBack?: string; -} - -interface FormData { - name: string; - code: string; - type: string; -} - -interface EnterpriseState { - enterprises: Enterprise[]; - showAddDialog: boolean; - showViewDialog: boolean; - showStatusDialog: boolean; - selectedEnterprise: Enterprise | null; - searchText: string; - statusAction: 'enable' | 'disable'; - formData: FormData; -} - -type EnterpriseAction = - | { type: 'SET_ENTERPRISES'; payload: Enterprise[] } - | { type: 'TOGGLE_ADD_DIALOG'; payload: boolean } - | { type: 'TOGGLE_VIEW_DIALOG'; payload: boolean } - | { type: 'TOGGLE_STATUS_DIALOG'; payload: boolean } - | { type: 'SET_SELECTED_ENTERPRISE'; payload: Enterprise | null } - | { type: 'SET_SEARCH_TEXT'; payload: string } - | { type: 'SET_STATUS_ACTION'; payload: 'enable' | 'disable' } - | { type: 'UPDATE_FORM_DATA'; payload: Partial } - | { type: 'RESET_FORM_DATA' } - | { type: 'ADD_ENTERPRISE'; payload: Enterprise } - | { type: 'UPDATE_ENTERPRISE_STATUS'; payload: { id: string; status: 'active' | 'inactive' } }; - -// Mock data -const mockEnterprises: Enterprise[] = [ - { - id: 'ent-001', - name: '智慧农业科技有限公司', - code: 'ZHNY001', - type: '科技企业', - status: 'active', - auditStatus: 'approved', - createdAt: '2024-01-15 10:30:00', - contact: '张三', - phone: '13800138000', - contactPhone: '13800138000', - province: '北京市', - city: '北京市', - district: '海淀区', - address: '中关村大街1号', - registrant: '李四', - companySize: '100-500人', - registeredCapital: '1000万元', - establishmentDate: '2020-01-01', - invoiceType: '增值税专用发票', - socialCreditCode: '91110108MA01XXXXXX', - businessScope: '技术开发、技术服务、技术咨询', - legalPerson: '王五' - }, - { - id: 'ent-002', - name: '绿色农业合作社', - code: 'LSNY002', - type: '合作社', - status: 'active', - auditStatus: 'pending', - createdAt: '2024-02-20 14:15:00', - contact: '赵六', - phone: '13900139000' - }, - { - id: 'ent-003', - name: '现代农业发展有限公司', - code: 'XDNY003', - type: '农业企业', - status: 'inactive', - auditStatus: 'not_submitted', - createdAt: '2024-03-10 09:45:00', - contact: '钱七', - phone: '13700137000' - } -]; - -// Initial state -const initialState: EnterpriseState = { - enterprises: mockEnterprises, - showAddDialog: false, - showViewDialog: false, - showStatusDialog: false, - selectedEnterprise: null, - searchText: '', - statusAction: 'enable', - formData: { - name: '', - code: '', - type: '' - } -}; - -// Reducer -function enterpriseReducer(state: EnterpriseState, action: EnterpriseAction): EnterpriseState { - switch (action.type) { - case 'SET_ENTERPRISES': - return { ...state, enterprises: action.payload }; - - case 'TOGGLE_ADD_DIALOG': - return { - ...state, - showAddDialog: action.payload, - ...(action.payload === false ? { formData: initialState.formData } : {}) - }; - - case 'TOGGLE_VIEW_DIALOG': - return { ...state, showViewDialog: action.payload }; - - case 'TOGGLE_STATUS_DIALOG': - return { ...state, showStatusDialog: action.payload }; - - case 'SET_SELECTED_ENTERPRISE': - return { ...state, selectedEnterprise: action.payload }; - - case 'SET_SEARCH_TEXT': - return { ...state, searchText: action.payload }; - - case 'SET_STATUS_ACTION': - return { ...state, statusAction: action.payload }; - - case 'UPDATE_FORM_DATA': - return { - ...state, - formData: { ...state.formData, ...action.payload } - }; - - case 'RESET_FORM_DATA': - return { ...state, formData: initialState.formData }; - - case 'ADD_ENTERPRISE': - return { - ...state, - enterprises: [...state.enterprises, action.payload] - }; - - case 'UPDATE_ENTERPRISE_STATUS': - return { - ...state, - enterprises: state.enterprises.map(ent => - ent.id === action.payload.id - ? { ...ent, status: action.payload.status } - : ent - ) - }; - - default: - return state; - } -} +import { enterpriseReducer, initialState, EnterpriseState, EnterpriseAction } from './components/enterpriseReducer'; +import { fetchTenants, transformTenantData, TenantsQueryParams, Enterprise } from './components/enterpriseApi'; // Utility functions const getStatusBadge = (status: 'active' | 'inactive') => { if (status === 'active') { - return 启用; + return 启用; } - return 禁用; + return 禁用; }; -const getAuditStatusBadge = (auditStatus?: 'not_submitted' | 'pending' | 'approved' | 'rejected') => { +const getAuditStatusBadge = (auditStatus: Enterprise['auditStatus']) => { switch (auditStatus) { - case 'not_submitted': - return 未提交; + case 'draft': + return 草稿; case 'pending': - return 待审核; + return 待审核; case 'approved': - return 审核通过; + return 审核通过; case 'rejected': - return 已驳回; + return 已拒绝; default: - return 未提交; + return 草稿; } }; export default function EnterpriseManagement() { const [state, dispatch] = useReducer(enterpriseReducer, initialState); - // Computed values - const filteredEnterprises = useMemo(() => { - return state.enterprises.filter(ent => { - if (!state.searchText) return true; - const searchLower = state.searchText.toLowerCase(); - return ( - ent.name.toLowerCase().includes(searchLower) || - ent.code.toLowerCase().includes(searchLower) || - (ent.type && ent.type.toLowerCase().includes(searchLower)) - ); - }); - }, [state.enterprises, state.searchText]); + // 加载企业数据 + const loadEnterprises = async (resetPage = false) => { + try { + dispatch({ type: 'SET_LOADING', payload: true }); + const params: TenantsQueryParams = { + search: state.filters.search || undefined, + audit_status: state.filters.audit_status || undefined, + page: resetPage ? 1 : state.pagination.page, + size: state.pagination.size, + order_by: state.sortBy, + sort_order: state.sortOrder, + }; + + const response = await fetchTenants(params); + 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:', error); + const errorMessage = error instanceof Error ? error.message : '加载企业数据失败'; + dispatch({ type: 'SET_ERROR', payload: errorMessage }); + toast.error(errorMessage); + } + }; + + // 初始加载 + useEffect(() => { + loadEnterprises(true); + }, [state.filters.search, state.filters.audit_status, state.sortBy, state.sortOrder]); + + // 分页加载 + useEffect(() => { + if (state.pagination.page > 1) { + loadEnterprises(false); + } + }, [state.pagination.page]); + + // 计算统计数据 const stats = useMemo(() => ({ total: state.enterprises.length, active: state.enterprises.filter(e => e.status === 'active').length, inactive: state.enterprises.filter(e => e.status === 'inactive').length, }), [state.enterprises]); - // Event handlers - const handleAdd = () => { - dispatch({ type: 'RESET_FORM_DATA' }); - dispatch({ type: 'TOGGLE_ADD_DIALOG', payload: true }); + // 事件处理器 + const handleSearch = (value: string) => { + dispatch({ type: 'SET_FILTERS', payload: { search: value } }); + }; + + const handleAuditStatusFilter = (value: string) => { + dispatch({ type: 'SET_FILTERS', payload: { audit_status: value === 'all' ? '' : value } }); + }; + + const handleSort = (sortBy?: string) => { + const newSortOrder = state.sortBy === sortBy && state.sortOrder === 'desc' ? 'asc' : 'desc'; + dispatch({ type: 'SET_SORT', payload: { sortBy, sortOrder: newSortOrder } }); + }; + + const handlePageChange = (page: number) => { + dispatch({ type: 'SET_PAGINATION', payload: { page } }); + }; + + const handleRefresh = () => { + dispatch({ type: 'REFRESH_DATA' }); + loadEnterprises(true); + toast.success('数据已刷新'); }; const handleView = (enterprise: Enterprise) => { @@ -264,116 +143,96 @@ export default function EnterpriseManagement() { dispatch({ type: 'TOGGLE_STATUS_DIALOG', payload: true }); }; - const confirmAdd = () => { - const { formData } = state; - - if (!formData.name.trim()) { - toast.error('请输入企业名称'); - return; - } - if (!formData.code.trim()) { - toast.error('请输入企业编码'); - return; - } - - // 检查编码是否重复 - if (state.enterprises.some(e => e.code === formData.code)) { - toast.error('企业编码已存在'); - return; - } - - const newEnterprise: Enterprise = { - id: `ent-${Date.now()}`, - name: formData.name, - code: formData.code, - type: formData.type || '未分类', - status: 'active', - auditStatus: 'not_submitted', - createdAt: new Date().toISOString().replace('T', ' ').substring(0, 19), - }; - - dispatch({ type: 'ADD_ENTERPRISE', payload: newEnterprise }); - dispatch({ type: 'TOGGLE_ADD_DIALOG', payload: false }); - toast.success('企业创建成功'); - }; - const confirmStatusChange = () => { if (!state.selectedEnterprise) return; + // 这里应该调用API来更新企业状态 + // 暂时更新本地状态 const newStatus = state.statusAction === 'enable' ? 'active' : 'inactive'; dispatch({ - type: 'UPDATE_ENTERPRISE_STATUS', - payload: { id: state.selectedEnterprise.id, status: newStatus } + type: 'SET_ENTERPRISES', + payload: { + data: state.enterprises.map(ent => + ent.id === state.selectedEnterprise?.id + ? { ...ent, status: newStatus } + : ent + ), + pagination: state.pagination + } }); dispatch({ type: 'TOGGLE_STATUS_DIALOG', payload: false }); toast.success(state.statusAction === 'enable' ? '企业已启用' : '企业已禁用'); }; - const updateFormData = (field: keyof FormData, value: string) => { - dispatch({ type: 'UPDATE_FORM_DATA', payload: { [field]: value || '' } }); - }; - return (
{/* Page Header */} - -
- -
-

企业管理

-

- 管理平台所有企业信息,支持创建企业、查看详情、启用/禁用企业 -

-
- - - 快速创建 - - - - 状态管理 - - - - 详情查看 - + +
+
+ +
+

企业管理

+

+ 管理平台所有企业信息,支持查询、查看详情、启用/禁用企业 +

+
+ + + 智能查询 + + + + 状态管理 + + + + 详情查看 + +
+
+ +
{/* Statistics Cards */} -
- +
+
企业总数
-
{stats.total}
+
{state.pagination.total}
全部企业数量
- +
启用企业
-
{stats.active}
-
+
{stats.active}
+
正常运营中
- +
禁用企业
-
{stats.inactive}
+
{stats.inactive}
已暂停使用
@@ -381,177 +240,193 @@ export default function EnterpriseManagement() {
{/* Enterprise List */} - -
+ +

企业列表

-
+
dispatch({ type: 'SET_SEARCH_TEXT', payload: e.target.value || '' })} + value={state.filters.search} + onChange={(e) => handleSearch(e.target.value)} className="pl-10 w-64" />
- +
-
- - - - 企业编码 - 企业名称 - 企业类型 - 联系人 - 联系电话 - 创建时间 - 审核状态 - 状态 - 操作 - - - - {filteredEnterprises.map((enterprise) => ( - - {enterprise.code} - -
- - {enterprise.name} -
-
- - {enterprise.type || '未分类'} - - {enterprise.contact || '-'} - {enterprise.phone || '-'} - {enterprise.createdAt} - {getAuditStatusBadge(enterprise.auditStatus)} - {getStatusBadge(enterprise.status)} - -
- - {enterprise.status === 'active' ? ( - - ) : ( - - )} -
-
-
- ))} -
-
-
- - {filteredEnterprises.length === 0 && ( -
- -

暂无企业数据

+ {/* Error Display */} + {state.error && ( +
+
+ + {state.error} +
)} - - {/* Add Enterprise Dialog */} - dispatch({ type: 'TOGGLE_ADD_DIALOG', payload: open })}> - - - 新建企业 - - 创建新企业账号,管理员仅需填写基本信息,详细信息由企业登录后自行完善 - - - -
-
- -
- - updateFormData('name', e.target.value)} - className="pl-10" - /> -
-
- -
- -
- - updateFormData('code', e.target.value.toUpperCase())} - className="pl-10" - /> -
-

编码创建后不可修改,请谨慎填写

-
- -
- -
- - updateFormData('type', e.target.value)} - className="pl-10" - /> -
-
- -
-

- 温馨提示: -

-
    -
  • • 企业创建后默认为启用状态,审核状态为"未提交"
  • -
  • • 联系人、电话、地址等详细信息由企业登录后自行填写
  • -
  • • 企业编码创建后不可修改,请确保准确无误
  • -
-
+ {/* Loading State */} + {state.loading && ( +
+ +

加载中...

+ )} - - - - - -
+ {/* Data Table */} + {!state.loading && !state.error && ( + <> +
+ + + + handleSort('tenant_code')} + > + 企业编码 + {state.sortBy === 'tenant_code' && ( + {state.sortOrder === 'asc' ? '↑' : '↓'} + )} + + handleSort('company_name')} + > + 企业名称 + {state.sortBy === 'company_name' && ( + {state.sortOrder === 'asc' ? '↑' : '↓'} + )} + + 企业类型 + 登记人 + 联系电话 + handleSort('created_at')} + > + 创建时间 + {state.sortBy === 'created_at' && ( + {state.sortOrder === 'asc' ? '↑' : '↓'} + )} + + 审核状态 + 状态 + 操作 + + + + {state.enterprises.map((enterprise) => ( + + {enterprise.code} + +
+ + {enterprise.name} +
+
+ + {enterprise.type} + + {enterprise.registrant || '-'} + {enterprise.contactPhone || '-'} + {enterprise.createdAt} + {getAuditStatusBadge(enterprise.auditStatus)} + {getStatusBadge(enterprise.status)} + +
+ + {enterprise.status === 'active' ? ( + + ) : ( + + )} +
+
+
+ ))} +
+
+
+ + {state.enterprises.length === 0 && ( +
+ +

暂无企业数据

+
+ )} + + {/* Pagination */} + {state.pagination.totalPages > 1 && ( +
+
+ 显示第 {state.pagination.page} 页,共 {state.pagination.totalPages} 页 + 总计 {state.pagination.total} 条记录 +
+
+ + + {state.pagination.page} / {state.pagination.totalPages} + + +
+
+ )} + + )} + {/* View Enterprise Details Dialog */} dispatch({ type: 'TOGGLE_VIEW_DIALOG', payload: open })}> @@ -605,7 +480,7 @@ export default function EnterpriseManagement() {
-
{state.selectedEnterprise.type || '-'}
+
{state.selectedEnterprise.type}
@@ -623,7 +498,7 @@ export default function EnterpriseManagement() {
-
{state.selectedEnterprise.contactPhone || state.selectedEnterprise.phone || '-'}
+
{state.selectedEnterprise.contactPhone || '-'}
@@ -661,19 +536,13 @@ export default function EnterpriseManagement() {
{state.selectedEnterprise.businessScope || '-'}
-
- -
- {state.selectedEnterprise.businessLicense ? ( - 营业执照 - ) : ( - 未上传 - )} -
+
+ +
{state.selectedEnterprise.submitTime || '-'}
+
+
+ +
{state.selectedEnterprise.auditTime || '-'}
@@ -703,20 +572,6 @@ export default function EnterpriseManagement() {
{state.selectedEnterprise.bankAddress || '-'}
-
- -
- {state.selectedEnterprise.bankLicense ? ( - 开户许可证 - ) : ( - 未上传 - )} -
-
@@ -729,35 +584,15 @@ export default function EnterpriseManagement() {
-
{state.selectedEnterprise.contact || '-'}
+
{state.selectedEnterprise.registrant || '-'}
-
- -
- {state.selectedEnterprise.idCardFront ? ( - 身份证正面 - ) : ( - 未上传 - )} -
+
+ +
{state.selectedEnterprise.auditor || '-'}
-
- -
- {state.selectedEnterprise.idCardBack ? ( - 身份证反面 - ) : ( - 未上传 - )} -
+
+ +
{state.selectedEnterprise.auditComment || '-'}
diff --git a/crop-x/src/app/(auth)/login/components/LoginForm.tsx b/crop-x/src/app/(auth)/login/components/LoginForm.tsx index de69230..4ab1aef 100644 --- a/crop-x/src/app/(auth)/login/components/LoginForm.tsx +++ b/crop-x/src/app/(auth)/login/components/LoginForm.tsx @@ -139,7 +139,6 @@ export function LoginForm({ onRegisterClick }: LoginFormProps) { apiResponse: response.data, timestamp: new Date().toISOString() }); - debugger // 验证token是否正确存储 if (userData.token) { console.log('🔑 Token已存储:', userData.token.substring(0, 20) + '...'); diff --git a/crop-x/src/lib/api/config.ts b/crop-x/src/lib/api/config.ts new file mode 100644 index 0000000..fb4f8d5 --- /dev/null +++ b/crop-x/src/lib/api/config.ts @@ -0,0 +1,37 @@ +/** + * filekorolheader: API配置文件 - API客户端配置和认证处理 + * 功能:API基础配置、认证头部处理、错误处理 + * 路径:/lib/api/config + * 规范:遵循crop-x/docs/开发项目规范.md,统一API调用配置 + */ + +import { getAuthToken } from '@/utils/token'; + +// API基础URL配置 - 开发环境直接使用真实API地址避免重定向问题 +export const API_BASE_URL = 'https://gitea-admin-hm-smart-agri-app.dev.maimaiag.com'; + +// 获取认证头部 +export const getAuthHeaders = () => { + const token = getAuthToken(); + const headers: Record = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + // 添加缓存控制头部 + 'Cache-Control': 'no-store, no-cache, must-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0', + }; + + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + return headers; +}; + +// API请求配置 +export const apiConfig = { + baseURL: API_BASE_URL, + timeout: 30000, // 30秒超时 + headers: getAuthHeaders(), +}; \ No newline at end of file diff --git a/crop-x/src/utils/token.ts b/crop-x/src/utils/token.ts new file mode 100644 index 0000000..e0c2a97 --- /dev/null +++ b/crop-x/src/utils/token.ts @@ -0,0 +1,11 @@ +export const getAuthToken = (): string | null => { + try { + const storedUser = localStorage.getItem('user'); + const user = storedUser ? JSON.parse(storedUser) : null; + return user?.token || null; + } catch (error) { + console.error('获取token失败:', error); + return null; + } + }; + \ No newline at end of file