feat: 添加前端页面和业务说明书
refactor(server): 重构服务层代码结构 feat(server): 添加基础设施、跨境电商、AI决策等核心服务 docs: 完善前端业务说明书和开发进度文档 style: 格式化代码和文档
This commit is contained in:
302
client/src/components/ComponentLibrary.tsx
Normal file
302
client/src/components/ComponentLibrary.tsx
Normal file
@@ -0,0 +1,302 @@
|
||||
import React from 'react';
|
||||
import { Button, Card, Table, Input, Select, DatePicker, Space, Modal, Form, InputNumber, Tag, Badge, message, Spin, Alert } from 'antd';
|
||||
|
||||
// 业务按钮组件
|
||||
export const BusinessButton: React.FC<{
|
||||
type: 'primary' | 'default' | 'dashed' | 'danger' | 'link';
|
||||
onClick: () => void;
|
||||
children: React.ReactNode;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
}> = ({ type, onClick, children, loading = false, disabled = false }) => {
|
||||
return (
|
||||
<Button
|
||||
type={type}
|
||||
onClick={onClick}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
fontWeight: '500',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务卡片组件
|
||||
export const BusinessCard: React.FC<{
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}> = ({ title, children, extra, style }) => {
|
||||
return (
|
||||
<Card
|
||||
title={title}
|
||||
extra={extra}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.09)',
|
||||
marginBottom: 16,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务表格组件
|
||||
export const BusinessTable: React.FC<{
|
||||
columns: any[];
|
||||
dataSource: any[];
|
||||
rowKey: string;
|
||||
pagination?: boolean | any;
|
||||
rowSelection?: any;
|
||||
}> = ({ columns, dataSource, rowKey, pagination = true, rowSelection }) => {
|
||||
return (
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
rowKey={rowKey}
|
||||
pagination={pagination}
|
||||
rowSelection={rowSelection}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
size="middle"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务输入框组件
|
||||
export const BusinessInput: React.FC<{
|
||||
placeholder: string;
|
||||
value: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
style?: React.CSSProperties;
|
||||
prefix?: React.ReactNode;
|
||||
suffix?: React.ReactNode;
|
||||
}> = ({ placeholder, value, onChange, style, prefix, suffix }) => {
|
||||
return (
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
...style,
|
||||
}}
|
||||
prefix={prefix}
|
||||
suffix={suffix}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务选择器组件
|
||||
export const BusinessSelect: React.FC<{
|
||||
placeholder: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
options: { label: string; value: string }[];
|
||||
style?: React.CSSProperties;
|
||||
}> = ({ placeholder, value, onChange, options, style }) => {
|
||||
return (
|
||||
<Select
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<Select.Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务日期选择器组件
|
||||
export const BusinessDatePicker: React.FC<{
|
||||
placeholder: string;
|
||||
value: any;
|
||||
onChange: (dates: any, dateStrings: any) => void;
|
||||
style?: React.CSSProperties;
|
||||
range?: boolean;
|
||||
}> = ({ placeholder, value, onChange, style, range = false }) => {
|
||||
const { RangePicker, DatePicker: AntDatePicker } = DatePicker;
|
||||
return range ? (
|
||||
<RangePicker
|
||||
placeholder={[placeholder, placeholder]}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
...style,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<AntDatePicker
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
...style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务模态框组件
|
||||
export const BusinessModal: React.FC<{
|
||||
title: string;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: () => void;
|
||||
children: React.ReactNode;
|
||||
width?: number;
|
||||
footer?: React.ReactNode;
|
||||
}> = ({ title, open, onCancel, onOk, children, width = 600, footer }) => {
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
onOk={onOk}
|
||||
width={width}
|
||||
footer={footer}
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务表单组件
|
||||
export const BusinessForm: React.FC<{
|
||||
form: any;
|
||||
onFinish: (values: any) => void;
|
||||
children: React.ReactNode;
|
||||
layout?: 'horizontal' | 'vertical' | 'inline';
|
||||
}> = ({ form, onFinish, children, layout = 'vertical' }) => {
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
layout={layout}
|
||||
style={{
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务加载组件
|
||||
export const BusinessLoading: React.FC<{
|
||||
loading: boolean;
|
||||
children: React.ReactNode;
|
||||
tip?: string;
|
||||
}> = ({ loading, children, tip = 'Loading...' }) => {
|
||||
return (
|
||||
<Spin spinning={loading} tip={tip}>
|
||||
{children}
|
||||
</Spin>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务提示组件
|
||||
export const BusinessAlert: React.FC<{
|
||||
type: 'success' | 'info' | 'warning' | 'error';
|
||||
message: string;
|
||||
description?: string;
|
||||
closable?: boolean;
|
||||
}> = ({ type, message, description, closable = true }) => {
|
||||
return (
|
||||
<Alert
|
||||
type={type}
|
||||
message={message}
|
||||
description={description}
|
||||
closable={closable}
|
||||
style={{
|
||||
marginBottom: 16,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务标签组件
|
||||
export const BusinessTag: React.FC<{
|
||||
color: string;
|
||||
children: React.ReactNode;
|
||||
}> = ({ color, children }) => {
|
||||
return (
|
||||
<Tag color={color} style={{ borderRadius: 4 }}>
|
||||
{children}
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
// 业务徽章组件
|
||||
export const BusinessBadge: React.FC<{
|
||||
status: 'success' | 'processing' | 'default' | 'error' | 'warning';
|
||||
text: string;
|
||||
}> = ({ status, text }) => {
|
||||
return (
|
||||
<Badge status={status} text={text} />
|
||||
);
|
||||
};
|
||||
|
||||
// 业务消息组件
|
||||
export const BusinessMessage: {
|
||||
success: (content: string) => void;
|
||||
error: (content: string) => void;
|
||||
warning: (content: string) => void;
|
||||
info: (content: string) => void;
|
||||
} = {
|
||||
success: (content) => message.success(content),
|
||||
error: (content) => message.error(content),
|
||||
warning: (content) => message.warning(content),
|
||||
info: (content) => message.info(content),
|
||||
};
|
||||
|
||||
// 业务间距组件
|
||||
export const BusinessSpace: React.FC<{
|
||||
children: React.ReactNode;
|
||||
size?: 'small' | 'middle' | 'large';
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
}> = ({ children, size = 'middle', direction = 'horizontal' }) => {
|
||||
return (
|
||||
<Space size={size} direction={direction}>
|
||||
{children}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
BusinessButton,
|
||||
BusinessCard,
|
||||
BusinessTable,
|
||||
BusinessInput,
|
||||
BusinessSelect,
|
||||
BusinessDatePicker,
|
||||
BusinessModal,
|
||||
BusinessForm,
|
||||
BusinessLoading,
|
||||
BusinessAlert,
|
||||
BusinessTag,
|
||||
BusinessBadge,
|
||||
BusinessMessage,
|
||||
BusinessSpace,
|
||||
};
|
||||
26
client/src/components/Layout.tsx
Normal file
26
client/src/components/Layout.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { Layout as AntLayout } from 'antd';
|
||||
import MenuComponent from './MenuComponent';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
const { Sider, Content } = AntLayout;
|
||||
|
||||
const Layout: React.FC = () => {
|
||||
return (
|
||||
<AntLayout style={{ minHeight: '100vh' }}>
|
||||
<Sider width={200} style={{ background: '#fff' }}>
|
||||
<div className="logo" style={{ padding: '16px', textAlign: 'center', fontWeight: 'bold', fontSize: '18px' }}>
|
||||
Crawlful Hub
|
||||
</div>
|
||||
<MenuComponent />
|
||||
</Sider>
|
||||
<AntLayout>
|
||||
<Content style={{ margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280 }}>
|
||||
<Outlet />
|
||||
</Content>
|
||||
</AntLayout>
|
||||
</AntLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
89
client/src/components/MenuComponent.tsx
Normal file
89
client/src/components/MenuComponent.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import { Menu } from 'antd';
|
||||
import {
|
||||
DashboardOutlined,
|
||||
ShoppingOutlined,
|
||||
OrderedListOutlined,
|
||||
TabletOutlined,
|
||||
InboxOutlined,
|
||||
TeamOutlined,
|
||||
DollarOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SettingOutlined,
|
||||
HomeOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
|
||||
const MenuComponent: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const currentPath = location.pathname;
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
key: '/dashboard',
|
||||
icon: <DashboardOutlined />,
|
||||
label: <Link to="/dashboard">Dashboard</Link>,
|
||||
},
|
||||
{
|
||||
key: '/product',
|
||||
icon: <ShoppingOutlined />,
|
||||
label: <Link to="/product">Product</Link>,
|
||||
},
|
||||
{
|
||||
key: '/orders',
|
||||
icon: <OrderedListOutlined />,
|
||||
label: <Link to="/orders">Orders</Link>,
|
||||
},
|
||||
{
|
||||
key: '/ad',
|
||||
icon: <TabletOutlined />,
|
||||
label: <Link to="/ad">Ad</Link>,
|
||||
},
|
||||
{
|
||||
key: '/inventory',
|
||||
icon: <InboxOutlined />,
|
||||
label: <Link to="/inventory">Inventory</Link>,
|
||||
},
|
||||
{
|
||||
key: '/b2b',
|
||||
icon: <TeamOutlined />,
|
||||
label: <Link to="/b2b">B2B</Link>,
|
||||
},
|
||||
{
|
||||
key: '/finance',
|
||||
icon: <DollarOutlined />,
|
||||
label: <Link to="/finance">Finance</Link>,
|
||||
},
|
||||
{
|
||||
key: '/compliance',
|
||||
icon: <SafetyCertificateOutlined />,
|
||||
label: <Link to="/compliance">Compliance</Link>,
|
||||
},
|
||||
{
|
||||
key: '/settings',
|
||||
icon: <SettingOutlined />,
|
||||
label: <Link to="/settings">Settings</Link>,
|
||||
},
|
||||
{
|
||||
key: '/independent-site',
|
||||
icon: <HomeOutlined />,
|
||||
label: <Link to="/independent-site">Independent Site</Link>,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="menu-container">
|
||||
<Menu
|
||||
mode="inline"
|
||||
selectedKeys={[currentPath]}
|
||||
style={{
|
||||
height: '100%',
|
||||
borderRight: 0,
|
||||
}}
|
||||
items={menuItems}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuComponent;
|
||||
Reference in New Issue
Block a user