---
title: 设计文档
order: 2
category: pro
---

# Pisell Basic Grid 设计文档

## 1. 概述

Pisell Basic Grid 是一个基于 Flexbox 的基础布局组件，主要用于控制循环型数据组件的排列方式。它提供了灵活的布局配置能力，包括排列方向、换行控制、间距设置、对齐方式等，是构建复杂布局的基础原子组件。

> **设计原则**：该组件不包含任何 UI 样式，仅提供布局能力，具体样式由业务场景自定义。

## 2. 核心功能

### 2.1 排列方向 (direction)

| 值                    | 说明       | CSS 对应值                          |
|----------------------|----------|----------------------------------|
| `horizontal`         | 横向排列（默认） | `flex-direction: row`            |
| `horizontal-reverse` | 横向反向排列   | `flex-direction: row-reverse`    |
| `vertical`           | 竖向排列     | `flex-direction: column`         |
| `vertical-reverse`   | 竖向反向排列   | `flex-direction: column-reverse` |

### 2.2 换行控制 (wrap)

| 值              | 说明      | CSS 对应值                   |
|----------------|---------|---------------------------|
| `nowrap`       | 不换行（默认） | `flex-wrap: nowrap`       |
| `wrap`         | 换行      | `flex-wrap: wrap`         |
| `wrap-reverse` | 反向换行    | `flex-wrap: wrap-reverse` |

### 2.3 一行 N 个 (columns)

通过设置子元素的 `flex-basis` 来控制一行显示的元素个数。

| 值        | 说明         | 计算方式                         |
|----------|------------|------------------------------|
| `number` | 一行显示 N 个元素 | `flex-basis: calc(100% / N)` |
| `auto`   | 自适应宽度（默认）  | `flex-basis: auto`           |

### 2.4 等分模式 (equalWidth)

| 值       | 说明             | CSS 对应值          |
|---------|----------------|------------------|
| `true`  | 子元素等分容器宽度      | `flex: 1`        |
| `false` | 子元素自适应内容宽度（默认） | `flex: 0 0 auto` |

### 2.5 间距控制 (gap)

支持统一间距和分别设置行列间距：

| 属性          | 类型                 | 说明       |
|-------------|--------------------|----------|
| `gap`       | `number \| string` | 统一设置行列间距 |
| `rowGap`    | `number \| string` | 行间距      |
| `columnGap` | `number \| string` | 列间距      |

### 2.6 主轴对齐 (justify)

| 值         | 说明       | CSS 对应值                          |
|-----------|----------|----------------------------------|
| `start`   | 起始对齐（默认） | `justify-content: flex-start`    |
| `end`     | 末尾对齐     | `justify-content: flex-end`      |
| `center`  | 居中对齐     | `justify-content: center`        |
| `between` | 两端对齐     | `justify-content: space-between` |
| `around`  | 等间距对齐    | `justify-content: space-around`  |
| `evenly`  | 均匀分布     | `justify-content: space-evenly`  |

### 2.7 交叉轴对齐 (align)

| 值          | 说明       | CSS 对应值                   |
|------------|----------|---------------------------|
| `start`    | 顶部对齐     | `align-items: flex-start` |
| `end`      | 底部对齐     | `align-items: flex-end`   |
| `center`   | 居中对齐     | `align-items: center`     |
| `stretch`  | 拉伸填充（默认） | `align-items: stretch`    |
| `baseline` | 基线对齐     | `align-items: baseline`   |

### 2.8 多行对齐 (alignContent)

当存在多行时，控制行与行之间的对齐方式：

| 值         | 说明       | CSS 对应值                        |
|-----------|----------|--------------------------------|
| `start`   | 顶部对齐     | `align-content: flex-start`    |
| `end`     | 底部对齐     | `align-content: flex-end`      |
| `center`  | 居中对齐     | `align-content: center`        |
| `between` | 两端对齐     | `align-content: space-between` |
| `around`  | 等间距对齐    | `align-content: space-around`  |
| `stretch` | 拉伸填充（默认） | `align-content: stretch`       |

### 2.9 数据源渲染 (dataSource + renderItem)

组件支持两种渲染模式：

#### 模式一：children 模式（默认）

直接传入子元素，由外部控制循环逻辑：

```
<PisellBasicGrid columns={4} gap={16}>
  {list.map((item) => (
    <Card key={item.id}>{item.name}</Card>
  ))}
</PisellBasicGrid>
```

#### 模式二：dataSource 模式

传入数据源和渲染函数，由组件内部控制循环逻辑：

```
<PisellBasicGrid
  columns={4}
  gap={16}
  dataSource={list}
  renderItem={(item, index) => <Card key={item.id}>{item.name}</Card>}
/>
```

#### 相关属性

| 属性           | 类型                                               | 说明                         |
|--------------|--------------------------------------------------|----------------------------|
| `dataSource` | `T[]`                                            | 数据源数组                      |
| `renderItem` | `(item: T, index: number) => ReactNode`          | 渲染函数，接收当前项和索引，返回 ReactNode |
| `rowKey`     | `string \| ((item: T, index: number) => string)` | 指定每项的唯一 key，默认使用 index     |

#### 优先级规则

- 当同时传入 `dataSource` 和 `children` 时，**`dataSource` 优先级更高**
- 如果 `dataSource` 为空数组或未传入，则渲染 `children`

#### 为什么需要 dataSource 模式？

1. **低代码场景友好**：在低代码平台中，数据源和渲染逻辑可以分别配置，更符合可视化编排的思路
2. **统一数据管理**：数据源集中管理，便于后续扩展（如分页、筛选、排序等）
3. **类型安全**：通过泛型约束，确保 renderItem 中的 item 类型正确
4. **简化使用**：无需在外部手动处理循环逻辑，减少样板代码

## 3. 响应式支持（预留）

预留响应式断点配置能力，后续可扩展支持：

| 断点    | 屏幕宽度     | 说明    |
|-------|----------|-------|
| `xs`  | < 576px  | 超小屏幕  |
| `sm`  | ≥ 576px  | 小屏幕   |
| `md`  | ≥ 768px  | 中等屏幕  |
| `lg`  | ≥ 992px  | 大屏幕   |
| `xl`  | ≥ 1200px | 超大屏幕  |
| `xxl` | ≥ 1600px | 超超大屏幕 |

响应式配置示例（预留接口）：

```
// 预留接口设计
interface ResponsiveConfig {
    columns?: number;
    gap?: number | string;
    direction?: DirectionType;
}

// 使用示例
<PisellBasicGrid
    responsive = {{
        xs: {
            columns: 1
        },
        sm: {
            columns: 2
        },
        md: {
            columns: 3
        },
        lg: {
            columns: 4
        },
    }}
/>;
```

## 4. Props 设计

```
/**
 * 响应式配置类型（预留）
 */
interface ResponsiveConfig {
    columns?: number;
    gap?: number | string;
    direction?: DirectionType;
}

/**
 * PisellBasicGrid 组件 Props
 * @template T - 数据源项的类型
 */
interface PisellBasicGridProps<T = any> {
    /** 子元素（当未传入 dataSource 时使用） */
    children?: React.ReactNode;

    /** 自定义类名 */
    className?: string;

    /** 自定义样式（支持所有 CSS 属性） */
    style?: React.CSSProperties;

    /** 排列方向 */
    direction?:
        | 'horizontal'
        | 'horizontal-reverse'
        | 'vertical'
        | 'vertical-reverse';

    /** 换行控制 */
    wrap?: 'nowrap' | 'wrap' | 'wrap-reverse';

    /** 一行显示的元素个数，设置后子元素宽度为 100%/columns */
    columns?: number;

    /** 是否等分容器宽度 */
    equalWidth?: boolean;

    /** 间距（统一设置行列间距） */
    gap?: number | string;

    /** 行间距 */
    rowGap?: number | string;

    /** 列间距 */
    columnGap?: number | string;

    /** 主轴对齐方式 */
    justify?: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly';

    /** 交叉轴对齐方式 */
    align?: 'start' | 'end' | 'center' | 'stretch' | 'baseline';

    /** 多行对齐方式 */
    alignContent?: 'start' | 'end' | 'center' | 'between' | 'around' | 'stretch';

    /** 响应式配置（预留） */
    responsive?: Record<
        'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl',
        Partial<ResponsiveConfig>
    >;

    /* ==================== 数据源渲染相关 ==================== */

    /**
     * 数据源数组
     * 当传入 dataSource 时，组件内部会自动循环渲染
     */
    dataSource?: T[];

    /**
     * 渲染函数
     * @param item - 当前数据项
     * @param index - 当前索引
     * @returns ReactNode
     */
    renderItem?: (item: T, index: number) => React.ReactNode;

    /**
     * 指定每项的唯一 key
     * - 传入 string 时，作为数据项的属性名获取 key
     * - 传入函数时，通过函数返回 key
     * - 不传时，默认使用 index 作为 key
     */
    rowKey?: string | ((item: T, index: number) => string);
}
```

## 5. 使用场景

### 场景一：商品列表 - children 模式（一行 4 个，换行显示）

```
<PisellBasicGrid columns={4} gap={16} wrap="wrap">
  {products.map((product) => (
    <ProductCard key={product.id} data={product} />
  ))}
</PisellBasicGrid>
```

### 场景二：商品列表 - dataSource 模式（推荐）

```
<PisellBasicGrid
  columns={4}
  gap={16}
  wrap="wrap"
  dataSource={products}
  rowKey="id"
  renderItem={(product, index) => <ProductCard data={product} />}
/>
```

### 场景三：标签列表（横向排列，自适应换行）

```
<PisellBasicGrid
  direction="horizontal"
  gap={8}
  wrap="wrap"
  dataSource={tags}
  rowKey="id"
  renderItem={(tag) => <Tag>{tag.name}</Tag>}
/>
```

### 场景四：表单项（竖向排列，固定间距）

```
<PisellBasicGrid
  direction="vertical"
  gap={24}
  dataSource={formItems}
  rowKey="name"
  renderItem={(item) => <FormItem {...item} />}
/>
```

### 场景五：操作按钮组（等分宽度，children 模式）

```
<PisellBasicGrid equalWidth gap={12}>
  <Button>取消</Button>
  <Button type="primary">确定</Button>
</PisellBasicGrid>
```

### 场景六：卡片列表（两端对齐，自定义 rowKey）

```
<PisellBasicGrid
  justify="between"
  wrap="wrap"
  rowGap={16}
  dataSource={cards}
  rowKey={(item, index) => `card-${item.id}-${index}`}
  renderItem={(card) => <Card style={{ width: '48%' }} {...card} />}
/>
```

### 场景七：低代码平台配置示例

在低代码平台中，可以通过可视化配置数据源和渲染模板：

```
// 低代码引擎生成的配置
const gridConfig = {
  componentName: 'PisellBasicGrid',
  props: {
    columns: 3,
    gap: 16,
    wrap: 'wrap',
    dataSource: {
      type: 'JSExpression',
      value: 'this.state.productList',
    },
    rowKey: 'id',
    renderItem: {
      type: 'JSSlot',
      value: {
        componentName: 'ProductCard',
        props: {
          data: {
            type: 'JSExpression',
            value: 'item',
          },
        },
      },
    },
  },
};
```

## 6. 与其他组件的关系

| 组件                   | 关系    | 说明                  |
|----------------------|-------|---------------------|
| `PisellBasicGrid`    | 本组件   | 基于 Flexbox 的基础布局组件  |
| `PisellGrid`（待开发）    | 独立组件  | 基于 CSS Grid 的高级布局组件 |
| `Ant Design Space`   | 可替代场景 | 简单的间距布局可使用 Space    |
| `Ant Design Row/Col` | 可替代场景 | 栅格系统布局可使用 Row/Col   |

## 7. 设计决策

### 7.1 为什么选择 Flexbox 而不是 CSS Grid？

- **兼容性更好**：Flexbox 在低版本浏览器中支持更好
- **场景更匹配**：循环型数据的布局场景更适合 Flexbox
- **学习成本低**：开发者对 Flexbox 更熟悉
- **后续扩展**：CSS Grid 作为独立组件开发，满足更复杂的布局需求

### 7.2 为什么不包含 UI 样式？

- **职责单一**：布局组件只负责布局，样式由业务决定
- **灵活性高**：避免样式冲突，方便在不同场景下使用
- **可组合性强**：可以与任何 UI 组件组合使用

### 7.3 子元素属性透传设计

组件本身不管理子元素的属性，所有子元素的样式和行为由使用方控制，这样设计的好处：

- **解耦**：布局组件与子组件完全解耦
- **灵活**：子元素可以是任意组件
- **可控**：使用方完全控制子元素的表现

### 7.4 为什么同时支持 children 和 dataSource 两种模式？

| 模式           | 适用场景            | 优势             |
|--------------|-----------------|----------------|
| `children`   | 静态内容、简单列表、按钮组等  | 直观、灵活、无需额外配置   |
| `dataSource` | 动态数据、低代码平台、复杂列表 | 数据驱动、易于配置、类型安全 |

两种模式互补，满足不同场景的需求：

1. **开发者友好**：熟悉 React 的开发者可以直接使用 children 模式
2. **低代码友好**：dataSource 模式更适合可视化配置
3. **渐进式迁移**：现有使用 children 的代码无需修改

## 8. 注意事项

1. **columns 与 equalWidth 的优先级**：当同时设置 `columns` 和 `equalWidth` 时，`columns` 优先级更高
2. **gap 的单位**：当 `gap` 为数字时，默认单位为 `px`
3. **响应式功能**：当前版本为预留接口，暂不实现，后续版本迭代
4. **性能考虑**：避免在大量数据场景下频繁更新布局属性
5. **dataSource 与 children 优先级**：当同时传入时，`dataSource` 优先级更高，`children` 将被忽略
6. **renderItem 必须返回有效节点**：当使用 dataSource 模式时，renderItem 不能返回 `undefined`
7. **rowKey 的重要性**：建议始终指定 rowKey，避免使用 index 作为 key 导致的渲染问题
