═══════════════════════════════════════════════════════════════════════════════ 程序鉴别材料 - 源代码前30页 纺织行业采购计划管理系统 V1.0.0 ═══════════════════════════════════════════════════════════════════════════════ 【文件1】src/index.tsx - 应用入口文件 ─────────────────────────────────────────────────────────────────────────────── import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import './styles/index.css'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( ); 【文件2】src/App.tsx - 根组件与路由配置 ─────────────────────────────────────────────────────────────────────────────── import React, { useState } from 'react'; import { HashRouter, Routes, Route, Navigate, useLocation } from 'react-router-dom'; import { AnimatePresence, motion } from 'framer-motion'; import { AuthProvider } from './contexts/AuthContext'; import { ErrorBoundary, PageErrorBoundary } from './components/ErrorBoundary'; import { LoginPage } from './pages/LoginPage'; import { RegisterPage } from './pages/RegisterPage'; import { RoleSelectPage } from './pages/RoleSelectPage'; import { PurchaserDashboard } from './pages/purchaser/Dashboard'; import { TextileDashboard } from './pages/textile/Dashboard'; import { WashingDashboard } from './pages/washing/Dashboard'; import { PlanOverview } from './pages/purchaser/PlanOverview'; import { NewPlan } from './pages/purchaser/NewPlan'; import { WarehouseManage } from './pages/purchaser/WarehouseManage'; import { FactoryManage } from './pages/purchaser/FactoryManage'; import { TextilePlanOverview } from './pages/textile/PlanOverview'; import { YarnWarehouse } from './pages/textile/YarnWarehouse'; import { FabricWarehouse } from './pages/textile/FabricWarehouse'; import { PaymentPending } from './pages/textile/PaymentPending'; import { MemberManage } from './pages/MemberManage'; import { ImportPlanPage } from './pages/ImportPlanPage'; import { NetworkStatusBar } from './components/NetworkStatusBar'; import './styles/index.css'; // 页面过渡动画配置 const pageTransitionVariants = { initial: { opacity: 0, y: 20, scale: 0.98 }, animate: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.35, ease: [0.25, 0.46, 0.45, 0.94] } }, exit: { opacity: 0, y: -10, scale: 0.98, transition: { duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] } }, }; function AnimatedRoute({ children, pageName }: { children: React.ReactNode; pageName?: string }) { return ( {children} ); } function AppRoutes() { const location = useLocation(); return ( } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> ); } function App() { return ( ); } export default App; 【文件3】src/contexts/AuthContext.tsx - 认证上下文(部分) ─────────────────────────────────────────────────────────────────────────────── import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; import { supabase } from '../supabase/client'; import type { Profile, Company, UserRole, AuthState } from '../types'; import { encryptPhone, decryptPhone } from '../utils/crypto'; interface RegisterData { username: string; password: string; displayName: string; companyName: string; phone: string; role: UserRole; } interface AuthContextType { auth: AuthState; login: (username: string, password: string) => Promise; register: (data: RegisterData) => Promise<{ success: boolean; error?: string }>; logout: () => Promise; selectRole: (role: UserRole) => void; loading: boolean; } const AuthContext = createContext(undefined); export function AuthProvider({ children }: { children: React.ReactNode }) { const [auth, setAuth] = useState({ isLoggedIn: false, user: null, company: null, currentRole: null }); const [loading, setLoading] = useState(true); useEffect(() => { const initAuth = async () => { const { data: { session } } = await supabase.auth.getSession(); if (session?.user) { const { data: profile } = await supabase .from('profiles').select('*').eq('id', session.user.id).maybeSingle(); if (profile) { let company: Company | null = null; if (profile.company_id) { const { data: comp } = await supabase .from('companies').select('*').eq('id', profile.company_id).maybeSingle(); company = comp; } setAuth({ isLoggedIn: true, user: profile, company, currentRole: company?.role || null }); } } setLoading(false); }; initAuth(); const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, session) => { if (event === 'SIGNED_IN' && session?.user) { setTimeout(async () => { const { data: profile } = await supabase .from('profiles').select('*').eq('id', session.user.id).maybeSingle(); if (profile) { let company: Company | null = null; if (profile.company_id) { const { data: comp } = await supabase .from('companies').select('*').eq('id', profile.company_id).maybeSingle(); company = comp; } setAuth({ isLoggedIn: true, user: profile, company, currentRole: company?.role || null }); } }, 0); } else if (event === 'SIGNED_OUT') { setAuth({ isLoggedIn: false, user: null, company: null, currentRole: null }); } }); return () => subscription.unsubscribe(); }, []); const login = useCallback(async (username: string, password: string): Promise => { const { error } = await supabase.auth.signInWithPassword({ email: `${username}@meoo.local`, password, }); return !error; }, []); const register = useCallback(async (data: RegisterData) => { try { const { data: companyData, error: companyError } = await supabase .from('companies').insert({ name: data.companyName, role: data.role, contact_phone: data.phone }) .select().single(); if (companyError || !companyData) return { success: false, error: '创建公司失败' }; const { data: authData, error: authError } = await supabase.auth.signUp({ email: `${data.username}@meoo.local`, password: data.password, }); if (authError || !authData.user) { await supabase.from('companies').delete().eq('id', companyData.id); return { success: false, error: '注册失败' }; } const { error: profileError } = await supabase.from('profiles').insert({ id: authData.user.id, username: data.username, display_name: data.displayName, phone: encryptPhone(data.phone), company_id: companyData.id, is_master: true }); if (profileError) { await supabase.from('companies').delete().eq('id', companyData.id); return { success: false, error: '创建用户资料失败' }; } return { success: true }; } catch (err: any) { return { success: false, error: err?.message || '注册失败' }; } }, []); const logout = useCallback(async () => { await supabase.auth.signOut(); setAuth({ isLoggedIn: false, user: null, company: null, currentRole: null }); }, []); const selectRole = useCallback((role: UserRole) => { setAuth(prev => ({ ...prev, currentRole: role })); }, []); return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (!context) throw new Error('useAuth must be used within AuthProvider'); return context; } 【文件4】src/supabase/client.ts - Supabase客户端配置 ─────────────────────────────────────────────────────────────────────────────── import { createClient } from '@supabase/supabase-js'; import type { Database } from './types'; export function getSupabaseUrl(): string { return `${(window as any).MEOO_CONFIG?.meoo_app_access_url || location.origin}/sb-api`; } export const supabaseUrl = getSupabaseUrl(); export const supabaseAnonKey = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...'; export const supabase = createClient(supabaseUrl, supabaseAnonKey, { auth: { persistSession: true, autoRefreshToken: true }, }); 【文件5】src/types/index.ts - 类型定义(部分) ─────────────────────────────────────────────────────────────────────────────── export type UserRole = 'purchaser' | 'textile' | 'washing'; export type PlanStatus = 'pending' | 'producing' | 'completed'; export type FactoryType = 'textile' | 'washing'; export interface Profile { id: string; username: string; display_name?: string; phone?: string; company_id?: string; is_master: boolean; master_id?: string; created_at: string; } export interface Company { id: string; name: string; role: UserRole; address?: string; contact_phone?: string; created_at: string; } export interface ProductionPlan { id: string; plan_code: string; product_name: string; color: string; fabric_code: string; color_code: string; remark?: string; purchaser_id: string; target_quantity: number; completed_quantity: number; yarn_usage_per_meter: number; production_price: number; status: PlanStatus; start_time?: string; created_by: string; created_at: string; updated_at: string; } export interface AuthState { isLoggedIn: boolean; user: Profile | null; company: Company | null; currentRole: UserRole | null; } 【文件6】src/hooks/useNetworkStatus.ts - 网络状态Hook(部分) ─────────────────────────────────────────────────────────────────────────────── import { useState, useEffect, useCallback, useRef } from 'react'; interface NetworkState { isOnline: boolean; isSlowConnection: boolean; lastOnlineTime: number | null; } interface QueuedOperation { id: string; operation: () => Promise; retryCount: number; maxRetries: number; onSuccess?: () => void; onError?: (error: Error) => void; } export function useNetworkStatus() { const [state, setState] = useState({ isOnline: navigator.onLine, isSlowConnection: false, lastOnlineTime: navigator.onLine ? Date.now() : null }); const queueRef = useRef([]); const processingRef = useRef(false); useEffect(() => { const handleOnline = () => { setState(prev => ({ ...prev, isOnline: true, lastOnlineTime: Date.now() })); processQueue(); }; const handleOffline = () => { setState(prev => ({ ...prev, isOnline: false })); }; window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); const checkConnectionSpeed = async () => { const connection = (navigator as any).connection; if (connection) { setState(prev => ({ ...prev, isSlowConnection: connection.effectiveType === 'slow-2g' || connection.effectiveType === '2g' })); } }; const interval = setInterval(checkConnectionSpeed, 120000); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); clearInterval(interval); }; }, []); const processQueue = useCallback(async () => { /* ... */ }, []); const enqueueOperation = useCallback((operation: () => Promise, options?: any) => { /* ... */ }, []); const executeWithRetry = useCallback(async (operation: () => Promise, options?: any) => { /* ... */ }, []); return { ...state, enqueueOperation, executeWithRetry, queueLength: queueRef.current.length }; } 【文件7】src/components/ErrorBoundary.tsx - 错误边界组件(部分) ─────────────────────────────────────────────────────────────────────────────── import React, { Component, ErrorInfo, ReactNode } from 'react'; import { AlertCircle, RefreshCw, Home } from 'lucide-react'; interface Props { children: ReactNode; fallback?: ReactNode; onError?: (error: Error, errorInfo: ErrorInfo) => void; } interface State { hasError: boolean; error: Error | null; errorInfo: ErrorInfo | null; } export class ErrorBoundary extends Component { constructor(props: Props) { super(props); this.state = { hasError: false, error: null, errorInfo: null }; } static getDerivedStateFromError(error: Error): Partial { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { this.setState({ errorInfo }); if (this.props.onError) this.props.onError(error, errorInfo); console.error('ErrorBoundary caught an error:', error, errorInfo); } handleReset = () => this.setState({ hasError: false, error: null, errorInfo: null }); handleGoHome = () => window.location.href = '/'; handleReload = () => window.location.reload(); render() { if (this.state.hasError) { if (this.props.fallback) return this.props.fallback; return (

页面出错了

抱歉,发生了意外错误

{this.state.error?.toString()}

); } return this.props.children; } } ═══════════════════════════════════════════════════════════════════════════════ 源代码特征说明 ═══════════════════════════════════════════════════════════════════════════════ 1. 编程语言:TypeScript (.ts, .tsx) 2. 前端框架:React 18 + React Router v6 3. 状态管理:React Context + Hooks 4. 动画库:Framer Motion 5. 样式方案:TailwindCSS 6. 后端服务:Supabase (PostgreSQL) 7. 构建工具:Webpack 5 代码特点: - 使用函数组件和React Hooks - 类型安全的TypeScript开发 - 模块化设计,组件化架构 - 响应式设计,支持移动端 - 错误边界处理机制 - 实时数据同步 ═══════════════════════════════════════════════════════════════════════════════ 第 1 页 / 共 30 页(前30页) ═══════════════════════════════════════════════════════════════════════════════