feat: 添加MSW模拟服务和数据源集成
refactor: 重构页面组件移除冗余Layout组件 feat: 实现WebSocket和事件总线系统 feat: 添加队列和调度系统 docs: 更新架构文档和服务映射 style: 清理重复接口定义使用数据源 chore: 更新依赖项配置 feat: 添加运行时系统和领域引导 ci: 配置ESLint边界检查规则 build: 添加Redis和WebSocket依赖 test: 添加MSW浏览器环境入口 perf: 优化数据获取逻辑使用统一数据源 fix: 修复类型定义和状态管理问题
This commit is contained in:
@@ -36,46 +36,11 @@ import {
|
||||
EyeOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { b2bTradeDataSource, BatchOrder, BatchOrderItem, Customer } from '@/services/b2bTradeDataSource';
|
||||
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
|
||||
interface BatchOrderItem {
|
||||
key: string;
|
||||
lineNumber: number;
|
||||
productId: string;
|
||||
sku: string;
|
||||
productName: string;
|
||||
quantity: number;
|
||||
unitPrice: number;
|
||||
totalPrice: number;
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
status: 'VALID' | 'INVALID' | 'PENDING';
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface BatchOrder {
|
||||
id: string;
|
||||
batchId: string;
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
totalItems: number;
|
||||
totalAmount: number;
|
||||
currency: string;
|
||||
status: 'DRAFT' | 'PENDING_REVIEW' | 'CONFIRMED' | 'PROCESSING' | 'COMPLETED' | 'CANCELLED';
|
||||
createdAt: string;
|
||||
validItems: number;
|
||||
invalidItems: number;
|
||||
}
|
||||
|
||||
interface Customer {
|
||||
id: string;
|
||||
name: string;
|
||||
company: string;
|
||||
tier: 'BASIC' | 'PRO' | 'ENTERPRISE';
|
||||
}
|
||||
|
||||
const BATCH_STATUS_MAP: Record<string, { color: string; text: string; icon: React.ReactNode }> = {
|
||||
DRAFT: { color: 'default', text: 'Draft', icon: <FileExcelOutlined /> },
|
||||
PENDING_REVIEW: { color: 'processing', text: 'Pending Review', icon: <SyncOutlined spin /> },
|
||||
@@ -119,49 +84,9 @@ export const BatchOrder: React.FC = () => {
|
||||
const fetchBatchOrders = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const mockOrders: BatchOrder[] = [
|
||||
{
|
||||
id: '1',
|
||||
batchId: 'BO-2026-001',
|
||||
customerId: 'CUST_001',
|
||||
customerName: 'ABC Trading Co.',
|
||||
totalItems: 50,
|
||||
totalAmount: 25000.00,
|
||||
currency: 'USD',
|
||||
status: 'CONFIRMED',
|
||||
createdAt: '2026-03-18 10:00:00',
|
||||
validItems: 48,
|
||||
invalidItems: 2,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
batchId: 'BO-2026-002',
|
||||
customerId: 'CUST_002',
|
||||
customerName: 'XYZ Electronics Ltd.',
|
||||
totalItems: 100,
|
||||
totalAmount: 45000.00,
|
||||
currency: 'USD',
|
||||
status: 'PENDING_REVIEW',
|
||||
createdAt: '2026-03-17 14:30:00',
|
||||
validItems: 95,
|
||||
invalidItems: 5,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
batchId: 'BO-2026-003',
|
||||
customerId: 'CUST_003',
|
||||
customerName: 'Global Import Inc.',
|
||||
totalItems: 200,
|
||||
totalAmount: 85000.00,
|
||||
currency: 'USD',
|
||||
status: 'COMPLETED',
|
||||
createdAt: '2026-03-15 09:00:00',
|
||||
validItems: 200,
|
||||
invalidItems: 0,
|
||||
},
|
||||
];
|
||||
setBatchOrders(mockOrders);
|
||||
calculateStats(mockOrders);
|
||||
const orders = await b2bTradeDataSource.fetchBatchOrders();
|
||||
setBatchOrders(orders);
|
||||
calculateStats(orders);
|
||||
} catch (error) {
|
||||
message.error('Failed to load batch orders');
|
||||
} finally {
|
||||
@@ -170,12 +95,12 @@ export const BatchOrder: React.FC = () => {
|
||||
};
|
||||
|
||||
const fetchCustomers = async () => {
|
||||
const mockCustomers: Customer[] = [
|
||||
{ id: 'CUST_001', name: 'ABC Trading Co.', company: 'ABC Trading', tier: 'ENTERPRISE' },
|
||||
{ id: 'CUST_002', name: 'XYZ Electronics Ltd.', company: 'XYZ Electronics', tier: 'PRO' },
|
||||
{ id: 'CUST_003', name: 'Global Import Inc.', company: 'Global Import', tier: 'ENTERPRISE' },
|
||||
];
|
||||
setCustomers(mockCustomers);
|
||||
try {
|
||||
const data = await b2bTradeDataSource.fetchCustomers();
|
||||
setCustomers(data);
|
||||
} catch (error) {
|
||||
message.error('Failed to load customers');
|
||||
}
|
||||
};
|
||||
|
||||
const calculateStats = (orders: BatchOrder[]) => {
|
||||
@@ -200,30 +125,32 @@ export const BatchOrder: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const parseUploadedFile = (file: any) => {
|
||||
const mockItems: BatchOrderItem[] = [
|
||||
{ key: '1', lineNumber: 1, productId: 'PROD_001', sku: 'SKU-001', productName: 'Wireless Headphones', quantity: 100, unitPrice: 22.00, totalPrice: 2200.00, customerId: selectedCustomer?.id || '', customerName: selectedCustomer?.name || '', status: 'VALID' },
|
||||
{ key: '2', lineNumber: 2, productId: 'PROD_002', sku: 'SKU-002', productName: 'USB-C Cable', quantity: 500, unitPrice: 4.50, totalPrice: 2250.00, customerId: selectedCustomer?.id || '', customerName: selectedCustomer?.name || '', status: 'VALID' },
|
||||
{ key: '3', lineNumber: 3, productId: 'PROD_003', sku: 'SKU-003', productName: 'Phone Case', quantity: 200, unitPrice: 7.00, totalPrice: 1400.00, customerId: selectedCustomer?.id || '', customerName: selectedCustomer?.name || '', status: 'VALID' },
|
||||
{ key: '4', lineNumber: 4, productId: 'PROD_004', sku: 'SKU-004', productName: 'Invalid Product', quantity: 50, unitPrice: 0, totalPrice: 0, customerId: selectedCustomer?.id || '', customerName: selectedCustomer?.name || '', status: 'INVALID', error: 'Product not found' },
|
||||
];
|
||||
setOrderItems(mockItems);
|
||||
setCurrentStep(1);
|
||||
message.success('File parsed successfully');
|
||||
const parseUploadedFile = async (file: any) => {
|
||||
try {
|
||||
const items = await b2bTradeDataSource.parseOrderFile(file, selectedCustomer);
|
||||
setOrderItems(items);
|
||||
setCurrentStep(1);
|
||||
message.success('File parsed successfully');
|
||||
} catch (error) {
|
||||
message.error('Failed to parse file');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveItem = (key: string) => {
|
||||
setOrderItems(orderItems.filter((item) => item.key !== key));
|
||||
};
|
||||
|
||||
const handleValidateItems = () => {
|
||||
const handleValidateItems = async () => {
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
const validCount = orderItems.filter((i) => i.status === 'VALID').length;
|
||||
const invalidCount = orderItems.filter((i) => i.status === 'INVALID').length;
|
||||
message.success(`Validation complete: ${validCount} valid, ${invalidCount} invalid`);
|
||||
try {
|
||||
const result = await b2bTradeDataSource.validateOrderItems(orderItems);
|
||||
setOrderItems(result.items);
|
||||
message.success(`Validation complete: ${result.validCount} valid, ${result.invalidCount} invalid`);
|
||||
} catch (error) {
|
||||
message.error('Validation failed');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmitBatch = async () => {
|
||||
@@ -236,18 +163,11 @@ export const BatchOrder: React.FC = () => {
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
const totalAmount = validItems.reduce((sum, item) => sum + item.totalPrice, 0);
|
||||
|
||||
const batchData = {
|
||||
await b2bTradeDataSource.submitBatchOrder({
|
||||
customerId: values.customerId,
|
||||
items: validItems,
|
||||
totalAmount,
|
||||
note: values.note,
|
||||
};
|
||||
|
||||
console.log('Submitting batch order:', batchData);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
});
|
||||
|
||||
message.success('Batch order submitted successfully');
|
||||
setCurrentStep(2);
|
||||
@@ -266,8 +186,7 @@ export const BatchOrder: React.FC = () => {
|
||||
const handleApproveBatch = async (batchId: string) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
console.log('Approving batch order:', batchId);
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
await b2bTradeDataSource.approveBatchOrder(batchId);
|
||||
message.success('Batch order approved');
|
||||
fetchBatchOrders();
|
||||
} catch (error) {
|
||||
|
||||
@@ -38,16 +38,12 @@ import {
|
||||
ClockCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { b2bTradeDataSource, Contract } from '@/services/b2bTradeDataSource';
|
||||
|
||||
const { Option } = Select;
|
||||
const { TabPane } = Tabs;
|
||||
const { RangePicker } = DatePicker;
|
||||
const { TextArea } = Input;
|
||||
|
||||
interface Contract {
|
||||
id: string;
|
||||
contractId: string;
|
||||
contractNumber: string;
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
title: string;
|
||||
|
||||
@@ -30,24 +30,11 @@ import {
|
||||
ShoppingOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { b2bTradeDataSource, Customer, Product } from '@/services/b2bTradeDataSource';
|
||||
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
interface Customer {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
company: string;
|
||||
tier: 'BASIC' | 'PRO' | 'ENTERPRISE';
|
||||
}
|
||||
|
||||
interface Product {
|
||||
id: string;
|
||||
sku: string;
|
||||
name: string;
|
||||
costPrice: number;
|
||||
suggestedPrice: number;
|
||||
moq: number;
|
||||
stock: number;
|
||||
|
||||
Reference in New Issue
Block a user