# UI Components (Crawlful Hub)

> **定位**：Crawlful Hub 前端 UI 组件规范 - 基于 Ant Design 5.x 的组件库使用指南。
> **更新日期**: 2026-03-18

---

## 1. 设计系统

### 1.1 色彩规范

```typescript
// 主色调
const colors = {
  primary: '#1890ff',      // 主色
  success: '#52c41a',      // 成功
  warning: '#faad14',      // 警告
  error: '#f5222d',        // 错误
  info: '#1890ff',         // 信息
};

// 中性色
const neutral = {
  textPrimary: 'rgba(0, 0, 0, 0.85)',
  textSecondary: 'rgba(0, 0, 0, 0.65)',
  textDisabled: 'rgba(0, 0, 0, 0.25)',
  border: '#d9d9d9',
  background: '#f5f5f5',
};
```

### 1.2 字体规范

```typescript
const typography = {
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial',
  fontSize: {
    small: '12px',
    base: '14px',
    medium: '16px',
    large: '20px',
    xlarge: '24px',
  },
};
```

---

## 2. 基础组件

### 2.1 按钮 (Button)

**使用场景**
- 主操作：蓝色主按钮
- 次操作：默认按钮
- 危险操作：红色按钮
- 文字链接：链接按钮

**示例**
```tsx
import { Button, Space } from 'antd';

// 主操作
<Button type="primary">创建商品</Button>

// 次操作
<Button>取消</Button>

// 危险操作
<Button type="primary" danger>删除</Button>

// 图标按钮
<Button icon={<PlusOutlined />}>新增</Button>

// 加载状态
<Button loading>保存中</Button>
```

### 2.2 表单 (Form)

**使用场景**
- 商品创建/编辑
- 订单审核
- 用户设置

**示例**
```tsx
import { Form, Input, Select, Button } from 'antd';

const ProductForm: React.FC = () => {
  const [form] = Form.useForm();
  
  const onFinish = (values: any) => {
    console.log(values);
  };
  
  return (
    <Form form={form} onFinish={onFinish} layout="vertical">
      <Form.Item 
        name="title" 
        label="商品名称" 
        rules={[{ required: true }]}
      >
        <Input placeholder="请输入商品名称" />
      </Form.Item>
      
      <Form.Item name="platform" label="平台">
        <Select>
          <Select.Option value="AMAZON">Amazon</Select.Option>
          <Select.Option value="EBAY">eBay</Select.Option>
          <Select.Option value="SHOPIFY">Shopify</Select.Option>
        </Select>
      </Form.Item>
      
      <Form.Item>
        <Button type="primary" htmlType="submit">提交</Button>
      </Form.Item>
    </Form>
  );
};
```

### 2.3 表格 (Table)

**使用场景**
- 商品列表
- 订单列表
- 交易流水

**示例**
```tsx
import { Table, Tag } from 'antd';

const ProductTable: React.FC = () => {
  const columns = [
    {
      title: '商品名称',
      dataIndex: 'title',
      key: 'title',
    },
    {
      title: '平台',
      dataIndex: 'platform',
      key: 'platform',
    },
    {
      title: '状态',
      dataIndex: 'status',
      key: 'status',
      render: (status: string) => {
        const colorMap: Record<string, string> = {
          DRAFTED: 'default',
          PENDING_REVIEW: 'processing',
          APPROVED: 'success',
          REJECTED: 'error',
        };
        return <Tag color={colorMap[status]}>{status}</Tag>;
      },
    },
    {
      title: '售价',
      dataIndex: 'sellingPrice',
      key: 'sellingPrice',
      render: (price: number) => `$${price.toFixed(2)}`,
    },
    {
      title: '操作',
      key: 'action',
      render: (_, record) => (
        <Space>
          <Button type="link">查看</Button>
          <Button type="link">编辑</Button>
        </Space>
      ),
    },
  ];
  
  return (
    <Table 
      columns={columns} 
      dataSource={products}
      rowKey="id"
      pagination={{ pageSize: 20 }}
    />
  );
};
```

### 2.4 卡片 (Card)

**使用场景**
- 仪表盘统计卡片
- 商品信息展示
- 订单概要

**示例**
```tsx
import { Card, Statistic } from 'antd';
import { ArrowUpOutlined } from '@ant-design/icons';

// 统计卡片
<Card>
  <Statistic
    title="本月营收"
    value={112893}
    precision={2}
    valueStyle={{ color: '#3f8600' }}
    prefix="$"
    suffix={<ArrowUpOutlined />}
  />
</Card>

// 带标题的卡片
<Card title="商品信息" extra={<a href="#">更多</a>}>
  <p>商品名称: Product Name</p>
  <p>平台: Amazon</p>
  <p>状态: 已上架</p>
</Card>
```

---

## 3. 业务组件

### 3.1 状态徽章 (StatusBadge)

**组件定义**
```tsx
import { Badge } from 'antd';

interface StatusBadgeProps {
  status: string;
  type: 'product' | 'order' | 'payment';
}

const statusMap: Record<string, Record<string, { color: string; text: string }>> = {
  product: {
    DRAFTED: { color: 'default', text: '草稿' },
    PENDING_REVIEW: { color: 'processing', text: '待审核' },
    APPROVED: { color: 'success', text: '已通过' },
    REJECTED: { color: 'error', text: '已拒绝' },
    LISTED: { color: 'success', text: '已上架' },
    DELISTED: { color: 'default', text: '已下架' },
  },
  order: {
    PULLED: { color: 'default', text: '已拉取' },
    PENDING_REVIEW: { color: 'processing', text: '待审核' },
    CONFIRMED: { color: 'success', text: '已确认' },
    SHIPPED: { color: 'blue', text: '已发货' },
    DELIVERED: { color: 'success', text: '已送达' },
  },
  payment: {
    PENDING: { color: 'warning', text: '待支付' },
    COMPLETED: { color: 'success', text: '已完成' },
    FAILED: { color: 'error', text: '失败' },
    REFUNDED: { color: 'default', text: '已退款' },
  },
};

export const StatusBadge: React.FC<StatusBadgeProps> = ({ status, type }) => {
  const config = statusMap[type]?.[status] || { color: 'default', text: status };
  return <Badge status={config.color as any} text={config.text} />;
};
```

### 3.2 筛选面板 (FilterPanel)

**组件定义**
```tsx
import { Form, Input, Select, DatePicker, Button, Space } from 'antd';

interface FilterPanelProps {
  onFilter: (values: any) => void;
  onReset: () => void;
}

export const FilterPanel: React.FC<FilterPanelProps> = ({ onFilter, onReset }) => {
  const [form] = Form.useForm();
  
  return (
    <Form form={form} layout="inline" onFinish={onFilter}>
      <Form.Item name="keyword" label="关键词">
        <Input placeholder="搜索..." allowClear />
      </Form.Item>
      
      <Form.Item name="platform" label="平台">
        <Select allowClear style={{ width: 120 }}>
          <Select.Option value="AMAZON">Amazon</Select.Option>
          <Select.Option value="EBAY">eBay</Select.Option>
          <Select.Option value="SHOPIFY">Shopify</Select.Option>
        </Select>
      </Form.Item>
      
      <Form.Item name="dateRange" label="日期">
        <DatePicker.RangePicker />
      </Form.Item>
      
      <Form.Item>
        <Space>
          <Button type="primary" htmlType="submit">筛选</Button>
          <Button onClick={() => { form.resetFields(); onReset(); }}>重置</Button>
        </Space>
      </Form.Item>
    </Form>
  );
};
```

### 3.3 数据表格 (DataTable)

**组件定义**
```tsx
import { Table, TableProps } from 'antd';

interface DataTableProps<T> extends TableProps<T> {
  loading?: boolean;
  pagination?: {
    current: number;
    pageSize: number;
    total: number;
  };
  onPageChange?: (page: number, pageSize: number) => void;
}

export function DataTable<T extends object>({
  loading,
  pagination,
  onPageChange,
  ...tableProps
}: DataTableProps<T>) {
  return (
    <Table<T>
      {...tableProps}
      loading={loading}
      pagination={pagination ? {
        ...pagination,
        showSizeChanger: true,
        showTotal: (total) => `共 ${total} 条`,
        onChange: onPageChange,
      } : false}
      scroll={{ x: 'max-content' }}
    />
  );
}
```

---

## 4. 图表组件

### 4.1 利润趋势图

**使用 Ant Design Charts**
```tsx
import { Line } from '@ant-design/charts';

const ProfitChart: React.FC = () => {
  const data = [
    { date: '2026-03-01', profit: 1000 },
    { date: '2026-03-02', profit: 1200 },
    { date: '2026-03-03', profit: 900 },
    // ...
  ];
  
  const config = {
    data,
    xField: 'date',
    yField: 'profit',
    smooth: true,
    point: {
      size: 5,
      shape: 'diamond',
    },
    label: {
      style: {
        fill: '#aaa',
      },
    },
  };
  
  return <Line {...config} />;
};
```

### 4.2 订单分布图

```tsx
import { Pie } from '@ant-design/charts';

const OrderDistributionChart: React.FC = () => {
  const data = [
    { type: 'Amazon', value: 400 },
    { type: 'eBay', value: 300 },
    { type: 'Shopify', value: 300 },
  ];
  
  const config = {
    data,
    angleField: 'value',
    colorField: 'type',
    radius: 0.8,
    label: {
      type: 'outer',
    },
  };
  
  return <Pie {...config} />;
};
```

---

## 5. 布局组件

### 5.1 主布局 (MainLayout)

```tsx
import { Layout, Menu } from 'antd';
import { Outlet, useNavigate } from 'react-router-dom';

const { Header, Sider, Content } = Layout;

const MainLayout: React.FC = () => {
  const navigate = useNavigate();
  
  const menuItems = [
    { key: '/', label: '仪表盘', icon: <DashboardOutlined /> },
    { key: '/products', label: '商品管理', icon: <ShoppingOutlined /> },
    { key: '/orders', label: '订单管理', icon: <FileTextOutlined /> },
    { key: '/finance', label: '财务管理', icon: <DollarOutlined /> },
    { key: '/inventory', label: '库存管理', icon: <InboxOutlined /> },
    { key: '/marketing', label: '营销广告', icon: <BarChartOutlined /> },
    { key: '/suppliers', label: '供应商', icon: <TeamOutlined /> },
    { key: '/reports', label: '报表分析', icon: <PieChartOutlined /> },
    { key: '/settings', label: '系统设置', icon: <SettingOutlined /> },
  ];
  
  return (
    <Layout style={{ minHeight: '100vh' }}>
      <Sider theme="light">
        <div style={{ height: 64, padding: 16 }}>Crawlful Hub</div>
        <Menu
          mode="inline"
          items={menuItems}
          onClick={({ key }) => navigate(key)}
        />
      </Sider>
      <Layout>
        <Header style={{ background: '#fff', padding: '0 24px' }}>
          {/* Header content */}
        </Header>
        <Content style={{ margin: 24, padding: 24, background: '#fff' }}>
          <Outlet />
        </Content>
      </Layout>
    </Layout>
  );
};
```

---

## 6. 表单校验规则

```typescript
// utils/validators.ts
export const validators = {
  required: (message: string) => ({ required: true, message }),
  
  email: { type: 'email', message: '请输入有效的邮箱地址' },
  
  price: (min: number = 0) => ({
    validator: (_: any, value: number) => {
      if (value >= min) return Promise.resolve();
      return Promise.reject(new Error(`价格不能低于 ${min}`));
    },
  }),
  
  url: { type: 'url', message: '请输入有效的URL' },
};
```

---

## 7. 相关文档

- [Frontend Design](./Frontend_Design.md)
- [Pages Flow](./Pages_Flow.md)
- [Ant Design 官方文档](https://ant.design/)

---

*本文档基于 Ant Design 5.x，最后更新: 2026-03-18*
