refactor: 重构项目结构并优化类型定义

- 移除extension模块,将功能迁移至node-agent
- 修复类型导出问题,使用export type明确类型导出
- 统一数据库连接方式,从直接导入改为使用config/database
- 更新文档中的项目结构描述
- 添加多个服务的实用方法,如getForecast、getBalances等
- 修复类型错误和TS1205警告
- 优化RedisService调用方式
- 添加新的实体类型定义
- 更新审计日志格式,统一字段命名
This commit is contained in:
2026-03-21 15:04:06 +08:00
parent 888d3844f3
commit 15ee1758f5
286 changed files with 9110 additions and 21453 deletions

View File

@@ -174,7 +174,7 @@ export const ABTestConfigPage: React.FC = () => {
setLoading(true);
try {
const values = await form.validateFields();
const config: ABTestConfig = {
const config: ABTestConfigData = {
...values,
variants,
};

View File

@@ -287,7 +287,12 @@ const AIDecisionLogPage: React.FC = () => {
const [loading, setLoading] = useState(false);
const [detailVisible, setDetailVisible] = useState(false);
const [selectedLog, setSelectedLog] = useState<any>(null);
const [filters, setFilters] = useState({
const [filters, setFilters] = useState<{
decision_type: string | undefined;
status: string | undefined;
date_range: [any, any] | null;
search: string;
}>({
decision_type: undefined,
status: undefined,
date_range: null,
@@ -412,7 +417,7 @@ const AIDecisionLogPage: React.FC = () => {
title: '操作',
key: 'action',
width: 150,
fixed: 'right',
fixed: 'right' as const,
render: (_: any, record: any) => (
<Space>
<Button
@@ -625,7 +630,11 @@ const AIDecisionLogPage: React.FC = () => {
showQuickJumper: true,
showTotal: total => `${total} 条记录`,
}}
onChange={setPagination}
onChange={(pag) => setPagination({
current: pag.current || 1,
pageSize: pag.pageSize || 10,
total: pag.total || 0,
})}
scroll={{ x: 1500 }}
/>
</Card>

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Card, Typography, Row, Col, Form, Input, Select, Button, Table, Statistic, Spin, message, Alert } from 'antd';
import { LineChart, Line, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';
import { RocketOutlined, SaveOutlined, BarChartOutlined, TargetOutlined, ReloadOutlined } from '@ant-design/icons';
import { RocketOutlined, SaveOutlined, BarChartOutlined, AimOutlined, ReloadOutlined } from '@ant-design/icons';
import { Link } from 'react-router-dom';
import { adOptimizationDataSource, OptimizationSuggestion, AdPerformance } from '@/services/adOptimizationDataSource';

View File

@@ -259,8 +259,8 @@ export const AdDelivery: React.FC = () => {
return (spend / clicks).toFixed(2);
};
const calculateROAS = (sales: number, spend: number) => {
if (spend === 0) return 0;
const calculateROAS = (sales: number, spend: number): string => {
if (spend === 0) return '0.00';
return (sales / spend).toFixed(2);
};

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Card, Typography, Row, Col, Form, Input, Select, Button, Table, Switch, DatePicker, TimePicker, message, Alert, Modal } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, SaveOutlined, PlayCircleOutlined, PauseCircleOutlined, HistoryOutlined } from '@ant-design/icons';
import { Link } from 'umi';
import { Link } from 'react-router-dom';
import { marketingDataSource, Ad } from '@/services/marketingDataSource';
const { Title, Text, Paragraph } = Typography;
@@ -49,7 +49,7 @@ const AutoAdjustment: React.FC = () => {
type: 'ROI',
condition: 'ROI < 3.0',
action: '降低CPC 10%',
status: 'active',
status: 'active' as const,
createdAt: '2026-03-10',
lastExecuted: '2026-03-18 10:30',
executionCount: 15,
@@ -62,7 +62,7 @@ const AutoAdjustment: React.FC = () => {
type: 'CTR',
condition: 'CTR < 2.0%',
action: '调整创意',
status: 'paused',
status: 'paused' as const,
createdAt: '2026-03-08',
lastExecuted: '2026-03-15 14:20',
executionCount: 8,
@@ -75,7 +75,7 @@ const AutoAdjustment: React.FC = () => {
type: 'Budget',
condition: '日花费 < 预算的80%',
action: '增加预算10%',
status: 'active',
status: 'active' as const,
createdAt: '2026-03-05',
lastExecuted: '2026-03-18 09:15',
executionCount: 20,
@@ -152,7 +152,7 @@ const AutoAdjustment: React.FC = () => {
const newStrategy: Strategy = {
id: (strategies.length + 1).toString(),
...values,
status: 'inactive' as const,
status: (values.status || 'inactive') as 'active' | 'inactive' | 'paused',
createdAt: new Date().toISOString().split('T')[0],
lastExecuted: '-',
executionCount: 0,

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Card, Typography, Row, Col, Select, Button, Table, Statistic, Spin, message, Alert, Badge, Dropdown, Menu } from 'antd';
import { LineChart, Line, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';
import { ReloadOutlined, ExportOutlined, FilterOutlined, BellOutlined, EyeOutlined } from '@ant-design/icons';
import { Link } from 'umi';
import { Link } from 'react-router-dom';
import { marketingDataSource, Ad } from '@/services/marketingDataSource';
const { Title, Text, Paragraph } = Typography;

View File

@@ -30,30 +30,14 @@ import {
AuditOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { afterSalesDataSource, RefundRecord } from '@/services/afterSalesDataSource';
import { afterSalesDataSource } from '@/services/afterSalesDataSource';
import type { RefundRecord as RefundRecordType } from '@/services/afterSalesDataSource';
const { TextArea } = Input;
const { Option } = Select;
const { TabPane } = Tabs;
interface RefundRecord {
id: string;
orderId: string;
refundId: string;
customerId: string;
customerName: string;
amount: number;
currency: string;
reason: string;
status: 'PENDING' | 'APPROVED' | 'REJECTED' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
createdAt: string;
updatedAt: string;
platform: string;
refundMethod: string;
transactionId?: string;
processedBy?: string;
remark?: string;
}
type RefundRecord = RefundRecordType;
const REFUND_STATUS_MAP: Record<string, { color: string; text: string; icon: React.ReactNode }> = {
PENDING: { color: 'warning', text: 'Pending', icon: <ClockCircleOutlined /> },

View File

@@ -42,8 +42,8 @@ import {
LineChartOutlined,
PieChartOutlined,
DollarOutlined,
TrendingUpOutlined,
TrendingDownOutlined,
RiseOutlined,
FallOutlined,
DownloadOutlined,
SearchOutlined,
FilterOutlined,
@@ -55,8 +55,6 @@ import {
ShopOutlined,
ProductOutlined,
OrderedListOutlined,
RiseOutlined,
FallOutlined,
EyeOutlined,
EditOutlined,
DeleteOutlined,
@@ -361,7 +359,7 @@ export const Analytics: React.FC = () => {
<div>
<div style={{ fontWeight: 500 }}>{text}</div>
<div style={{ fontSize: 12, color: '#999' }}>{record.productId}</div>
<Tag size="small">{record.category}</Tag>
<Tag style={{ fontSize: 11 }}>{record.category}</Tag>
</div>
</Space>
),
@@ -723,7 +721,7 @@ export const Analytics: React.FC = () => {
<Descriptions bordered column={1}>
{Object.entries(selectedItem as object).map(([key, value]) => (
<Descriptions.Item key={key} label={key}>
{typeof value === 'object' ? JSON.stringify(value) : String(value)}
{typeof value === 'object' ? JSON.stringify(value) : String(value ?? '')}
</Descriptions.Item>
))}
</Descriptions>

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { useNavigate } from 'umi';
import { useNavigate } from 'react-router-dom';
import {
Card,
Form,

View File

@@ -1 +0,0 @@
export { default } from './index.tsx';

View File

@@ -62,6 +62,7 @@ import {
AlertOutlined,
InfoCircleOutlined,
PlusOutlined,
RiseOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import moment from 'moment';
@@ -193,15 +194,19 @@ const AutoProductSelection: React.FC = () => {
const values = await ruleForm.validateFields();
await productSelectionDataSource.createSelectionRule({
name: values.name,
description: values.description || '',
category: values.category,
min_roi: values.minROI,
max_roi: values.maxROI,
min_profit: values.minProfit,
max_price: values.maxPrice,
min_sales_volume: values.minSalesVolume,
max_competition_level: values.maxCompetitionLevel,
min_rating: values.minRating,
trend_filter: values.trendFilter,
criteria: {
min_roi: values.minROI,
max_roi: values.maxROI,
min_profit: values.minProfit,
max_price: values.maxPrice,
min_sales_volume: values.minSalesVolume,
max_competition_level: values.maxCompetitionLevel,
min_rating: values.minRating,
trend_filter: values.trendFilter,
},
enabled: true,
});
setRuleModalVisible(false);
ruleForm.resetFields();
@@ -335,7 +340,7 @@ const AutoProductSelection: React.FC = () => {
<div style={{ fontWeight: 500 }}>{text}</div>
<Text type="secondary" style={{ fontSize: 12 }}>SKU: {record.product_id}</Text>
<div>
{record.tags.map(tag => (
{(record.tags || []).map(tag => (
<Tag key={tag} color="blue" style={{ fontSize: 10 }}>{tag}</Tag>
))}
</div>
@@ -355,8 +360,8 @@ const AutoProductSelection: React.FC = () => {
width: 150,
render: (_, record) => (
<div>
<div>¥{record.price.toFixed(2)}</div>
<Text type="secondary" style={{ fontSize: 12 }}>: ¥{record.cost_price.toFixed(2)}</Text>
<div>¥{(record.price ?? 0).toFixed(2)}</div>
<Text type="secondary" style={{ fontSize: 12 }}>: ¥{(record.cost_price ?? 0).toFixed(2)}</Text>
</div>
),
},
@@ -366,7 +371,7 @@ const AutoProductSelection: React.FC = () => {
width: 120,
render: (_, record) => (
<div>
<div style={{ color: '#52c41a', fontWeight: 500 }}>¥{record.profit.toFixed(2)}</div>
<div style={{ color: '#52c41a', fontWeight: 500 }}>¥{(record.profit ?? 0).toFixed(2)}</div>
<Tag color="green" style={{ fontSize: 11 }}>{record.roi.toFixed(0)}% ROI</Tag>
</div>
),
@@ -377,7 +382,7 @@ const AutoProductSelection: React.FC = () => {
width: 120,
render: (_, record) => (
<div>
<div>{record.sales_volume.toLocaleString()} </div>
<div>{(record.sales_volume ?? 0).toLocaleString()} </div>
<Space>
<StarOutlined style={{ color: '#faad14', fontSize: 12 }} />
<Text style={{ fontSize: 12 }}>{record.rating} ({record.review_count})</Text>
@@ -455,7 +460,7 @@ const AutoProductSelection: React.FC = () => {
title: 'ROI范围',
key: 'roi',
width: 120,
render: (_, record) => `${record.min_roi}% - ${record.max_roi}%`,
render: (_, record) => `${record.criteria?.min_roi ?? 0}% - ${record.criteria?.max_roi ?? 0}%`,
},
{
title: '利润要求',
@@ -669,7 +674,7 @@ const AutoProductSelection: React.FC = () => {
title="平均ROI"
value={selectionStats?.avgROI.toFixed(1) || 0}
suffix="%"
prefix={<TrendingUpOutlined />}
prefix={<RiseOutlined />}
valueStyle={{ color: '#52c41a' }}
/>
</Col>
@@ -1153,7 +1158,7 @@ const AutoProductSelection: React.FC = () => {
<div>
<Text strong>: </Text>
<Space wrap>
{selectedProduct.tags.map(tag => <Tag key={tag} color="blue">{tag}</Tag>)}
{(selectedProduct.tags || []).map(tag => <Tag key={tag} color="blue">{tag}</Tag>)}
</Space>
</div>
</Space>

View File

@@ -98,11 +98,16 @@ export const BatchOrder: React.FC = () => {
const mockOrders: B2BBatchOrder[] = [
{
id: 'BO-2026-001',
batchId: 'BO-2026-001',
customerId: 'C001',
customerName: 'TechCorp Industries',
totalItems: 2,
validItems: 2,
invalidItems: 0,
currency: 'USD',
items: [
{ key: '1', productId: 'P001', productName: 'Industrial Sensor A', quantity: 50, unitPrice: 142.50, totalPrice: 7125 },
{ key: '2', productId: 'P002', productName: 'Control Module B', quantity: 20, unitPrice: 266.00, totalPrice: 5320 },
{ key: '1', lineNumber: 1, productId: 'P001', sku: 'SKU-P001', productName: 'Industrial Sensor A', quantity: 50, unitPrice: 142.50, totalPrice: 7125, customerId: 'C001', customerName: 'TechCorp Industries', status: 'VALID' },
{ key: '2', lineNumber: 2, productId: 'P002', sku: 'SKU-P002', productName: 'Control Module B', quantity: 20, unitPrice: 266.00, totalPrice: 5320, customerId: 'C001', customerName: 'TechCorp Industries', status: 'VALID' },
],
totalAmount: 12445,
status: 'PROCESSING',
@@ -111,10 +116,15 @@ export const BatchOrder: React.FC = () => {
},
{
id: 'BO-2026-002',
batchId: 'BO-2026-002',
customerId: 'C002',
customerName: 'Global Manufacturing Co',
totalItems: 1,
validItems: 1,
invalidItems: 0,
currency: 'USD',
items: [
{ key: '1', productId: 'P003', productName: 'Power Supply Unit C', quantity: 100, unitPrice: 85.50, totalPrice: 8550 },
{ key: '1', lineNumber: 1, productId: 'P003', sku: 'SKU-P003', productName: 'Power Supply Unit C', quantity: 100, unitPrice: 85.50, totalPrice: 8550, customerId: 'C002', customerName: 'Global Manufacturing Co', status: 'VALID' },
],
totalAmount: 8550,
status: 'CONFIRMED',
@@ -123,11 +133,16 @@ export const BatchOrder: React.FC = () => {
},
{
id: 'BO-2026-003',
batchId: 'BO-2026-003',
customerId: 'C003',
customerName: 'Smart Factory Solutions',
totalItems: 2,
validItems: 2,
invalidItems: 0,
currency: 'USD',
items: [
{ key: '1', productId: 'P004', productName: 'Communication Gateway D', quantity: 30, unitPrice: 399.00, totalPrice: 11970 },
{ key: '2', productId: 'P005', productName: 'Display Panel E', quantity: 50, unitPrice: 175.75, totalPrice: 8787.50 },
{ key: '1', lineNumber: 1, productId: 'P004', sku: 'SKU-P004', productName: 'Communication Gateway D', quantity: 30, unitPrice: 399.00, totalPrice: 11970, customerId: 'C003', customerName: 'Smart Factory Solutions', status: 'VALID' },
{ key: '2', lineNumber: 2, productId: 'P005', sku: 'SKU-P005', productName: 'Display Panel E', quantity: 50, unitPrice: 175.75, totalPrice: 8787.50, customerId: 'C003', customerName: 'Smart Factory Solutions', status: 'VALID' },
],
totalAmount: 20757.50,
status: 'COMPLETED',
@@ -192,12 +207,16 @@ export const BatchOrder: React.FC = () => {
try {
const newOrder: B2BBatchOrder = {
id: `BO-${Date.now()}`,
batchId: `BATCH-${Date.now()}`,
customerId: selectedCustomer.id,
customerName: selectedCustomer.name,
items: orderItems,
totalItems: orderItems.length,
totalAmount: orderItems.reduce((sum, item) => sum + item.totalPrice, 0),
currency: 'USD',
status: 'DRAFT',
createdAt: new Date().toISOString(),
validItems: orderItems.length,
invalidItems: 0,
};
setOrders([newOrder, ...orders]);

View File

@@ -7,6 +7,7 @@ import {
Modal,
Form,
Input,
InputNumber,
Select,
DatePicker,
Descriptions,
@@ -96,7 +97,7 @@ export const ContractManage: React.FC = () => {
const mockContracts: Contract[] = [
{
id: '1',
contractNo: 'CTR-2026-001',
contractId: 'CTR-2026-001',
customerId: 'C001',
customerName: 'TechCorp Industries',
title: 'Annual Supply Agreement 2026',
@@ -105,6 +106,7 @@ export const ContractManage: React.FC = () => {
startDate: '2026-01-01',
endDate: '2026-12-31',
totalAmount: 500000,
currency: 'USD',
signedDate: '2025-12-15',
signedBy: 'John Smith',
attachments: ['contract_ctr_2026_001.pdf'],
@@ -114,7 +116,7 @@ export const ContractManage: React.FC = () => {
},
{
id: '2',
contractNo: 'CTR-2026-002',
contractId: 'CTR-2026-002',
customerId: 'C002',
customerName: 'Global Manufacturing Co',
title: 'OEM Manufacturing Agreement',
@@ -123,6 +125,7 @@ export const ContractManage: React.FC = () => {
startDate: '2026-03-01',
endDate: '2027-02-28',
totalAmount: 800000,
currency: 'USD',
attachments: ['oem_agreement_draft.pdf', 'specification_v2.pdf'],
terms: 'Exclusive manufacturing rights for North America region. Quality standards: ISO 9001:2015. Lead time: 45 days.',
createdAt: '2026-03-10 09:00:00',
@@ -130,7 +133,7 @@ export const ContractManage: React.FC = () => {
},
{
id: '3',
contractNo: 'CTR-2025-015',
contractId: 'CTR-2025-015',
customerId: 'C003',
customerName: 'Smart Factory Solutions',
title: 'Distribution Partnership Agreement',
@@ -139,6 +142,7 @@ export const ContractManage: React.FC = () => {
startDate: '2025-01-01',
endDate: '2025-12-31',
totalAmount: 300000,
currency: 'USD',
signedDate: '2024-12-20',
signedBy: 'Mike Johnson',
attachments: ['distribution_2025.pdf'],
@@ -149,7 +153,7 @@ export const ContractManage: React.FC = () => {
},
{
id: '4',
contractNo: 'CTR-2026-003',
contractId: 'CTR-2026-003',
customerId: 'C001',
customerName: 'TechCorp Industries',
title: 'Service Level Agreement',
@@ -158,6 +162,7 @@ export const ContractManage: React.FC = () => {
startDate: '2026-04-01',
endDate: '2027-03-31',
totalAmount: 120000,
currency: 'USD',
attachments: [],
terms: '24/7 technical support. Response time: 4 hours for critical issues. On-site support included.',
createdAt: '2026-03-18 10:30:00',
@@ -176,7 +181,7 @@ export const ContractManage: React.FC = () => {
const customer = MOCK_CUSTOMERS.find(c => c.id === values.customerId);
const newContract: Contract = {
id: `${Date.now()}`,
contractNo: `CTR-${new Date().getFullYear()}-${String(contracts.length + 1).padStart(3, '0')}`,
contractId: `CTR-${new Date().getFullYear()}-${String(contracts.length + 1).padStart(3, '0')}`,
customerId: values.customerId,
customerName: customer?.name || '',
title: values.title,
@@ -185,6 +190,7 @@ export const ContractManage: React.FC = () => {
startDate: values.dateRange[0].format('YYYY-MM-DD'),
endDate: values.dateRange[1].format('YYYY-MM-DD'),
totalAmount: values.totalAmount,
currency: 'USD',
attachments: [],
terms: values.terms,
createdAt: new Date().toISOString(),
@@ -252,8 +258,8 @@ export const ContractManage: React.FC = () => {
const columns: ColumnsType<Contract> = [
{
title: 'Contract No',
dataIndex: 'contractNo',
key: 'contractNo',
dataIndex: 'contractId',
key: 'contractId',
width: 140,
render: (no: string, record: Contract) => (
<a onClick={() => handleViewDetail(record)}>{no}</a>
@@ -468,7 +474,7 @@ export const ContractManage: React.FC = () => {
{selectedContract && (
<>
<Descriptions bordered column={2}>
<Descriptions.Item label="Contract No">{selectedContract.contractNo}</Descriptions.Item>
<Descriptions.Item label="Contract No">{selectedContract.contractId}</Descriptions.Item>
<Descriptions.Item label="Status">
<Tag color={STATUS_CONFIG[selectedContract.status].color}>
{STATUS_CONFIG[selectedContract.status].text}
@@ -505,11 +511,11 @@ export const ContractManage: React.FC = () => {
]}
/>
{selectedContract.attachments.length > 0 && (
{selectedContract.attachments && selectedContract.attachments.length > 0 && (
<>
<Divider>Attachments</Divider>
<Space>
{selectedContract.attachments.map((file, index) => (
{selectedContract.attachments.map((file: string, index: number) => (
<Button key={index} icon={<DownloadOutlined />} size="small">
{file}
</Button>

View File

@@ -127,7 +127,7 @@ export const B2BTradeBatchOrder: React.FC = () => {
const parseUploadedFile = async (file: any) => {
try {
const items = await b2bTradeDataSource.parseOrderFile(file, selectedCustomer);
const items = await b2bTradeDataSource.parseUploadFile(file, selectedCustomer?.id || '');
setOrderItems(items);
setCurrentStep(1);
message.success('File parsed successfully');
@@ -143,9 +143,11 @@ export const B2BTradeBatchOrder: React.FC = () => {
const handleValidateItems = async () => {
setLoading(true);
try {
const result = await b2bTradeDataSource.validateOrderItems(orderItems);
setOrderItems(result.items);
message.success(`Validation complete: ${result.validCount} valid, ${result.invalidCount} invalid`);
const result = await b2bTradeDataSource.validateItems(orderItems);
setOrderItems(result);
const validCount = result.filter(i => i.status === 'VALID').length;
const invalidCount = result.filter(i => i.status === 'INVALID').length;
message.success(`Validation complete: ${validCount} valid, ${invalidCount} invalid`);
} catch (error) {
message.error('Validation failed');
} finally {
@@ -163,10 +165,9 @@ export const B2BTradeBatchOrder: React.FC = () => {
}
setLoading(true);
await b2bTradeDataSource.submitBatchOrder({
await b2bTradeDataSource.createBatchOrder({
customerId: values.customerId,
items: validItems,
note: values.note,
});
message.success('Batch order submitted successfully');

View File

@@ -3,6 +3,7 @@ import {
Card,
Form,
Input,
InputNumber,
Select,
Button,
Table,
@@ -132,6 +133,7 @@ export const ContractManage: React.FC = () => {
status: 'ACTIVE',
startDate: '2026-01-01',
endDate: '2026-12-31',
totalAmount: 500000.00,
totalValue: 500000.00,
currency: 'USD',
signedDate: '2025-12-15',
@@ -151,6 +153,7 @@ export const ContractManage: React.FC = () => {
status: 'ACTIVE',
startDate: '2026-02-01',
endDate: '2028-01-31',
totalAmount: 0,
totalValue: 0,
currency: 'USD',
signedDate: '2026-01-20',
@@ -167,9 +170,10 @@ export const ContractManage: React.FC = () => {
customerName: 'Global Import Inc.',
title: 'Purchase Agreement Q2',
type: 'PURCHASE',
status: 'PENDING_SIGN',
status: 'PENDING_REVIEW',
startDate: '2026-04-01',
endDate: '2026-06-30',
totalAmount: 150000.00,
totalValue: 150000.00,
currency: 'USD',
attachments: ['purchase_agreement.pdf'],
@@ -187,6 +191,7 @@ export const ContractManage: React.FC = () => {
status: 'EXPIRED',
startDate: '2025-01-01',
endDate: '2025-12-31',
totalAmount: 350000.00,
totalValue: 350000.00,
currency: 'USD',
signedDate: '2025-01-05',
@@ -661,7 +666,7 @@ export const ContractManage: React.FC = () => {
</Tag>
</Descriptions.Item>
<Descriptions.Item label="Value">
{selectedContract.totalValue > 0 ? `$${selectedContract.totalValue.toLocaleString()}` : '-'}
{selectedContract.totalValue != null ? `$${selectedContract.totalValue.toLocaleString()}` : '-'}
</Descriptions.Item>
<Descriptions.Item label="Start Date">{selectedContract.startDate}</Descriptions.Item>
<Descriptions.Item label="End Date">{selectedContract.endDate}</Descriptions.Item>
@@ -675,11 +680,11 @@ export const ContractManage: React.FC = () => {
<Descriptions.Item label="Updated">{selectedContract.updatedAt}</Descriptions.Item>
</Descriptions>
{selectedContract.attachments.length > 0 && (
{selectedContract.attachments && selectedContract.attachments.length > 0 && (
<>
<Divider>Attachments</Divider>
<Space>
{selectedContract.attachments.map((file, index) => (
{selectedContract.attachments.map((file: string, index: number) => (
<Button
key={index}
icon={<DownloadOutlined />}

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'umi';
import { Link, useNavigate } from 'react-router-dom';
import { Card, Button, Typography, Row, Col, Tag, Carousel, Pagination } from 'antd';
import { ArrowRightOutlined, CheckCircleOutlined, DollarOutlined, BarChartOutlined, ZapOutlined, GlobeOutlined, StarOutlined, UserOutlined, CompanyOutlined, TrendingUpOutlined } from '@ant-design/icons';
import Navbar from '@/components/Navbar';

View File

@@ -0,0 +1,86 @@
.homepage-button-primary {
padding: 16px 40px;
font-size: 18px;
border-radius: 30px;
box-shadow: 0 8px 24px rgba(24, 144, 255, 0.4);
transition: all 0.3s ease;
}
.homepage-button-primary:hover {
transform: translateY(-3px);
box-shadow: 0 12px 32px rgba(24, 144, 255, 0.5);
}
.homepage-button-secondary {
padding: 16px 40px;
font-size: 18px;
border-radius: 30px;
background: rgba(255, 255, 255, 0.1);
color: #fff;
border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
}
.homepage-button-secondary:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-3px);
color: #fff;
}
.homepage-card {
transition: all 0.3s ease;
}
.homepage-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
}
.homepage-feature-card {
transition: all 0.3s ease;
border: 1px solid #f0f0f0;
}
.homepage-feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
border-color: #1890ff;
}
.homepage-pricing-card {
transition: all 0.3s ease;
border: 1px solid #f0f0f0;
}
.homepage-pricing-card:hover {
transform: scale(1.02);
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.1);
}
.homepage-pricing-card-popular {
border: 2px solid #1890ff;
position: relative;
}
.homepage-pricing-card-popular:hover {
border-color: #40a9ff;
}
.homepage-footer-link {
color: rgba(255, 255, 255, 0.8);
transition: color 0.3s ease;
}
.homepage-footer-link:hover {
color: #fff;
}
.homepage-social-icon {
color: rgba(255, 255, 255, 0.8);
transition: all 0.3s ease;
}
.homepage-social-icon:hover {
color: #fff;
transform: translateY(-2px);
}

View File

@@ -22,27 +22,27 @@ const Homepage: React.FC = () => {
const features = [
{
title: '智能选品系统',
description: '基于AI的商品推荐自动识别高潜力商品提升选品效率',
description: '基于AI的商品推荐自动识别高潜力商品提升选品效率'
},
{
title: 'AI动态定价',
description: '实时市场分析,优化定价策略,最大化利润',
description: '实时市场分析,优化定价策略,最大化利润'
},
{
title: '自动上架系统',
description: '一键批量上架,节省人工成本,提高运营效率',
description: '一键批量上架,节省人工成本,提高运营效率'
},
{
title: '跨平台套利',
description: '发现价格差异,实现利润最大化,降低运营风险',
description: '发现价格差异,实现利润最大化,降低运营风险'
},
{
title: 'AI店铺托管',
description: '24/7智能运营解放人力提升店铺表现',
description: '24/7智能运营解放人力提升店铺表现'
},
{
title: '多商户管理',
description: '集中管理多店铺,数据一目了然,简化管理流程',
description: '集中管理多店铺,数据一目了然,简化管理流程'
},
];
@@ -130,12 +130,8 @@ const Homepage: React.FC = () => {
fontSize: '18px',
borderRadius: '30px',
boxShadow: '0 8px 24px rgba(24, 144, 255, 0.4)',
transition: 'all 0.3s ease',
'&:hover': {
transform: 'translateY(-3px)',
boxShadow: '0 12px 32px rgba(24, 144, 255, 0.5)'
}
}}
transition: 'all 0.3s ease'
}}
>
</Button>
@@ -149,12 +145,8 @@ const Homepage: React.FC = () => {
background: 'rgba(255, 255, 255, 0.1)',
color: '#fff',
border: '1px solid rgba(255, 255, 255, 0.3)',
transition: 'all 0.3s ease',
'&:hover': {
background: 'rgba(255, 255, 255, 0.2)',
transform: 'translateY(-3px)'
}
}}
transition: 'all 0.3s ease'
}}
>
</Button>
@@ -187,12 +179,8 @@ const Homepage: React.FC = () => {
borderRadius: '16px',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.4)',
transition: 'all 0.3s ease',
transformStyle: 'preserve-3d',
'&:hover': {
transform: 'scale(1.05) rotateY(5deg)',
boxShadow: '0 24px 72px rgba(0, 0, 0, 0.5)'
}
}}
transformStyle: 'preserve-3d'
}}
/>
</div>
</Col>
@@ -259,13 +247,8 @@ const Homepage: React.FC = () => {
transition: 'all 0.4s ease',
border: '1px solid #e8e8e8',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.08)',
height: '100%',
'&:hover': {
transform: 'translateY(-12px)',
boxShadow: '0 16px 48px rgba(0, 0, 0, 0.15)',
borderColor: '#1890ff'
}
}}
height: '100%'
}}
>
<div style={{ marginBottom: '32px', paddingTop: '32px' }}>
<span style={{
@@ -280,12 +263,8 @@ const Homepage: React.FC = () => {
color: '#fff',
borderRadius: '20px',
boxShadow: '0 6px 20px rgba(24, 144, 255, 0.4)',
transition: 'all 0.3s ease',
'&:hover': {
transform: 'scale(1.1)',
boxShadow: '0 8px 24px rgba(24, 144, 255, 0.5)'
}
}}>{feature.title.charAt(0)}</span>
transition: 'all 0.3s ease'
}}>{feature.title.charAt(0)}</span>
</div>
<Title level={4} style={{
marginBottom: '20px',
@@ -308,12 +287,8 @@ const Homepage: React.FC = () => {
alignItems: 'center',
gap: '10px',
transition: 'all 0.3s ease',
fontSize: '16px',
'&:hover': {
color: '#40a9ff',
transform: 'translateX(6px)'
}
}}
fontSize: '16px'
}}
>
<ArrowRightOutlined />
</Button>
@@ -368,12 +343,8 @@ const Homepage: React.FC = () => {
borderRadius: '16px',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.15)',
transition: 'all 0.4s ease',
transformStyle: 'preserve-3d',
'&:hover': {
transform: 'scale(1.05) rotateY(-5deg)',
boxShadow: '0 24px 72px rgba(0, 0, 0, 0.2)'
}
}}
transformStyle: 'preserve-3d'
}}
/>
</div>
</Col>
@@ -407,13 +378,8 @@ const Homepage: React.FC = () => {
padding: '24px',
background: '#fafafa',
borderRadius: '16px',
transition: 'all 0.4s ease',
'&:hover': {
background: 'rgba(24, 144, 255, 0.05)',
transform: 'translateX(12px)',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.08)'
}
}}>
transition: 'all 0.4s ease'
}}>
<div style={{
width: '56px',
height: '56px',
@@ -450,13 +416,8 @@ const Homepage: React.FC = () => {
padding: '24px',
background: '#fafafa',
borderRadius: '16px',
transition: 'all 0.4s ease',
'&:hover': {
background: 'rgba(24, 144, 255, 0.05)',
transform: 'translateX(12px)',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.08)'
}
}}>
transition: 'all 0.4s ease'
}}>
<div style={{
width: '56px',
height: '56px',
@@ -493,13 +454,8 @@ const Homepage: React.FC = () => {
padding: '24px',
background: '#fafafa',
borderRadius: '16px',
transition: 'all 0.4s ease',
'&:hover': {
background: 'rgba(24, 144, 255, 0.05)',
transform: 'translateX(12px)',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.08)'
}
}}>
transition: 'all 0.4s ease'
}}>
<div style={{
width: '56px',
height: '56px',
@@ -536,13 +492,8 @@ const Homepage: React.FC = () => {
padding: '24px',
background: '#fafafa',
borderRadius: '16px',
transition: 'all 0.4s ease',
'&:hover': {
background: 'rgba(24, 144, 255, 0.05)',
transform: 'translateX(12px)',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.08)'
}
}}>
transition: 'all 0.4s ease'
}}>
<div style={{
width: '56px',
height: '56px',
@@ -635,13 +586,8 @@ const Homepage: React.FC = () => {
transition: 'all 0.4s ease',
border: '1px solid #e8e8e8',
boxShadow: '0 12px 32px rgba(0, 0, 0, 0.1)',
height: '100%',
'&:hover': {
transform: 'translateY(-16px)',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.18)',
borderColor: '#1890ff'
}
}}
height: '100%'
}}
>
<div style={{
marginBottom: '0',
@@ -656,11 +602,8 @@ const Homepage: React.FC = () => {
width: '100%',
height: '100%',
objectFit: 'cover',
transition: 'all 0.4s ease',
'&:hover': {
transform: 'scale(1.1)'
}
}}
transition: 'all 0.4s ease'
}}
/>
<div style={{
position: 'absolute',
@@ -708,12 +651,8 @@ const Homepage: React.FC = () => {
alignItems: 'center',
gap: '10px',
transition: 'all 0.3s ease',
fontSize: '16px',
'&:hover': {
color: '#40a9ff',
transform: 'translateX(6px)'
}
}}
fontSize: '16px'
}}
>
<ArrowRightOutlined />
</Button>
@@ -729,13 +668,8 @@ const Homepage: React.FC = () => {
transition: 'all 0.4s ease',
border: '1px solid #e8e8e8',
boxShadow: '0 12px 32px rgba(0, 0, 0, 0.1)',
height: '100%',
'&:hover': {
transform: 'translateY(-16px)',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.18)',
borderColor: '#1890ff'
}
}}
height: '100%'
}}
>
<div style={{
marginBottom: '0',
@@ -750,11 +684,8 @@ const Homepage: React.FC = () => {
width: '100%',
height: '100%',
objectFit: 'cover',
transition: 'all 0.4s ease',
'&:hover': {
transform: 'scale(1.1)'
}
}}
transition: 'all 0.4s ease'
}}
/>
<div style={{
position: 'absolute',
@@ -802,12 +733,8 @@ const Homepage: React.FC = () => {
alignItems: 'center',
gap: '10px',
transition: 'all 0.3s ease',
fontSize: '16px',
'&:hover': {
color: '#40a9ff',
transform: 'translateX(6px)'
}
}}
fontSize: '16px'
}}
>
<ArrowRightOutlined />
</Button>
@@ -823,13 +750,8 @@ const Homepage: React.FC = () => {
transition: 'all 0.4s ease',
border: '1px solid #e8e8e8',
boxShadow: '0 12px 32px rgba(0, 0, 0, 0.1)',
height: '100%',
'&:hover': {
transform: 'translateY(-16px)',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.18)',
borderColor: '#1890ff'
}
}}
height: '100%'
}}
>
<div style={{
marginBottom: '0',
@@ -844,11 +766,8 @@ const Homepage: React.FC = () => {
width: '100%',
height: '100%',
objectFit: 'cover',
transition: 'all 0.4s ease',
'&:hover': {
transform: 'scale(1.1)'
}
}}
transition: 'all 0.4s ease'
}}
/>
<div style={{
position: 'absolute',
@@ -896,12 +815,8 @@ const Homepage: React.FC = () => {
alignItems: 'center',
gap: '10px',
transition: 'all 0.3s ease',
fontSize: '16px',
'&:hover': {
color: '#40a9ff',
transform: 'translateX(6px)'
}
}}
fontSize: '16px'
}}
>
<ArrowRightOutlined />
</Button>
@@ -918,12 +833,8 @@ const Homepage: React.FC = () => {
fontSize: '18px',
borderRadius: '30px',
boxShadow: '0 6px 18px rgba(24, 144, 255, 0.4)',
transition: 'all 0.3s ease',
'&:hover': {
transform: 'translateY(-3px)',
boxShadow: '0 8px 24px rgba(24, 144, 255, 0.5)'
}
}}
transition: 'all 0.3s ease'
}}
>
</Button>
@@ -1000,12 +911,8 @@ const Homepage: React.FC = () => {
fontSize: '20px',
borderRadius: '35px',
boxShadow: '0 8px 24px rgba(24, 144, 255, 0.4)',
transition: 'all 0.3s ease',
'&:hover': {
transform: 'translateY(-3px)',
boxShadow: '0 12px 32px rgba(24, 144, 255, 0.5)'
}
}}
transition: 'all 0.3s ease'
}}
>
</Button>
@@ -1062,13 +969,8 @@ const Homepage: React.FC = () => {
borderRadius: '50%',
background: 'rgba(255, 255, 255, 0.08)',
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
'&:hover': {
background: '#1890ff',
color: '#fff',
transform: 'translateY(-2px)'
}
}}>
transition: 'all 0.3s ease'
}}>
<TwitterOutlined />
</a>
<a href="#" style={{
@@ -1080,13 +982,8 @@ const Homepage: React.FC = () => {
borderRadius: '50%',
background: 'rgba(255, 255, 255, 0.08)',
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
'&:hover': {
background: '#1890ff',
color: '#fff',
transform: 'translateY(-2px)'
}
}}>
transition: 'all 0.3s ease'
}}>
<LinkedinOutlined />
</a>
<a href="#" style={{
@@ -1098,13 +995,8 @@ const Homepage: React.FC = () => {
borderRadius: '50%',
background: 'rgba(255, 255, 255, 0.08)',
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
'&:hover': {
background: '#1890ff',
color: '#fff',
transform: 'translateY(-2px)'
}
}}>
transition: 'all 0.3s ease'
}}>
<GithubOutlined />
</a>
</div>
@@ -1122,42 +1014,26 @@ const Homepage: React.FC = () => {
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/case-study" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}>API文档</Link></li>
fontSize: '16px'
}}>API文档</Link></li>
</ul>
</Col>
<Col xs={24} md={6}>
@@ -1173,42 +1049,26 @@ const Homepage: React.FC = () => {
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
</ul>
</Col>
<Col xs={24} md={6}>
@@ -1224,42 +1084,26 @@ const Homepage: React.FC = () => {
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}></Link></li>
fontSize: '16px'
}}></Link></li>
<li style={{ marginBottom: '20px' }}><Link to="/" style={{
color: 'rgba(255, 255, 255, 0.6)',
transition: 'all 0.3s ease',
textDecoration: 'none',
fontSize: '16px',
'&:hover': {
color: '#1890ff',
transform: 'translateX(4px)'
}
}}>API文档</Link></li>
fontSize: '16px'
}}>API文档</Link></li>
</ul>
</Col>
</Row>
@@ -1274,29 +1118,20 @@ const Homepage: React.FC = () => {
color: 'rgba(255, 255, 255, 0.4)',
transition: 'color 0.3s ease',
textDecoration: 'none',
margin: '0 16px',
'&:hover': {
color: '#1890ff'
}
}}></Link>
margin: '0 16px'
}}></Link>
<Link to="/" style={{
color: 'rgba(255, 255, 255, 0.4)',
transition: 'color 0.3s ease',
textDecoration: 'none',
margin: '0 16px',
'&:hover': {
color: '#1890ff'
}
}}></Link>
margin: '0 16px'
}}></Link>
<Link to="/" style={{
color: 'rgba(255, 255, 255, 0.4)',
transition: 'color 0.3s ease',
textDecoration: 'none',
margin: '0 16px',
'&:hover': {
color: '#1890ff'
}
}}>Cookie政策</Link>
margin: '0 16px'
}}>Cookie政策</Link>
</div>
© 2025 Crawlful Hub. All rights reserved.
</div>

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Card, Typography, Row, Col, Button, Statistic, Space, Tabs } from 'antd';
import { FileTextOutlined, AlertOutlined, BarChartOutlined, SyncOutlined, PlusOutlined, SearchOutlined, FilterOutlined, DollarOutlined, TruckOutlined, ClockCircleOutlined, CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
import { Link } from 'umi';
import { Link } from 'react-router-dom';
import { Area, Pie, Column } from '@ant-design/plots';
import moment from 'moment';

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Card, Typography, Row, Col, Button, Table, Input, Select, Modal, Form, InputNumber, message, Tag, Space, Popconfirm, Tabs } from 'antd';
import { PlusOutlined, UploadOutlined, EditOutlined, SearchOutlined, FilterOutlined, SyncOutlined, DeleteOutlined, EyeOutlined, GlobalOutlined } from '@ant-design/icons';
import { Link } from 'umi';
import { Link } from 'react-router-dom';
import CrossPlatformManage from './CrossPlatformManage';
const { Title, Text } = Typography;