refactor: 重构项目结构并优化类型定义
- 移除extension模块,将功能迁移至node-agent - 修复类型导出问题,使用export type明确类型导出 - 统一数据库连接方式,从直接导入改为使用config/database - 更新文档中的项目结构描述 - 添加多个服务的实用方法,如getForecast、getBalances等 - 修复类型错误和TS1205警告 - 优化RedisService调用方式 - 添加新的实体类型定义 - 更新审计日志格式,统一字段命名
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 /> },
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'umi';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Card,
|
||||
Form,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './index.tsx';
|
||||
@@ -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>
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -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';
|
||||
|
||||
86
dashboard/src/pages/Homepage.css
Normal file
86
dashboard/src/pages/Homepage.css
Normal 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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user