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

﻿# PisellNumber 组件设计文档

## 组件概述

PisellNumber 是 Pisell 2.0 组件库中处理数值数据的基础组件。它提供了数值展示、输入、校验、排序和筛选等核心能力，适用于数量、次数、库存、排序权重等通用数值场景。

**设计原则**：

- 基于 Ant Design InputNumber 组件进行开发，继承其成熟的交互体验
- 参考 PisellSingleLineText 架构，采用三态视图（ReadOnly/Edit/Disabled）
- 使用 @pisell/utils 中的公共状态管理逻辑
- 支持受控和非受控两种模式

**Figma 设计链接**：
https://www.figma.com/design/pM8Ho6d7kCMv9vIBvFHUlj/Pisell-2.0-Component-Library?node-id=3796-6919&m=dev

---

## 核心功能

### 1. 三种交互状态

**只读态（ReadOnly）**：

- 以纯文本形式展示格式化后的数值
- 不响应任何点击或聚焦交互
- 视觉上表现为普通文本，可配置字体样式

**编辑态（Edit）**：

- 显示 Ant Design InputNumber 输入控件
- 支持键盘输入、步进器调节
- 支持聚焦、失焦交互
- 错误时显示红色边框和提示信息

**禁用态（Disabled）**：

- 视觉上应用 40% 透明度
- 禁用所有交互（hover、focus、点击）
- 状态由 `disabled` 和 `mode` 属性控制

### 2. 数值格式化系统

**基础格式化**：

- 精度控制：支持小数位数配置（`precision`）
- 取整规则：四舍五入/向上/向下
- 千分位分隔符：可选启用（`useGrouping`）

**自定义格式化**：

- 支持 `formatter` 函数，完全自定义显示格式
- 支持 `parser` 函数，自定义输入解析逻辑

**格式化示例**：

```
输入值: 1234.567
配置: precision: 2, useGrouping: true
输出: "1,234.57"
```

### 3. 验证规则

**通用验证**：

- 类型验证：确保输入为有效数字
- 范围验证：值必须在 `min` 和 `max` 定义的范围内
- 精度验证：自动裁剪超出 `precision` 的小数位

**验证触发时机**：

- `onChange`：输入时即时校验
- `onBlur`：失焦时校验
- `both`：输入和失焦时都校验

**自动修正**：

- 配置 `autoCorrect` 时，超出范围的值会自动修正为边界值

### 4. 筛选与排序能力

**表格集成**：

- 支持数字输入框筛选器
- 支持数字范围筛选器
- 支持升序（0→9）和降序（9→0）排序

---

## 接口定义

### Props 属性

| 属性名               | 类型                                             | 默认值        | 说明           |
|-------------------|------------------------------------------------|------------|--------------|
| `mode`            | `'read' \| 'edit'`                             | `'edit'`   | 显示模式         |
| `disabled`        | `boolean`                                      | `false`    | 是否禁用         |
| `value`           | `number \| null`                               | -          | 当前值（受控）      |
| `defaultValue`    | `number \| null`                               | -          | 默认值（非受控）     |
| `onChange`        | `(value: number \| null) => void`              | -          | 值变更回调        |
| `onBlur`          | `(value: number \| null) => void`              | -          | 失焦回调         |
| `onFocus`         | `() => void`                                   | -          | 聚焦回调         |
| `min`             | `number`                                       | -          | 最小值          |
| `max`             | `number`                                       | -          | 最大值          |
| `step`            | `number`                                       | `1`        | 步进值          |
| `precision`       | `number`                                       | -          | 小数精度         |
| `useGrouping`     | `boolean`                                      | `false`    | 是否使用千分位分隔符   |
| `formatter`       | `(value: number \| null) => string`            | -          | 自定义格式化函数     |
| `parser`          | `(displayValue: string) => number`             | -          | 自定义解析函数      |
| `placeholder`     | `string`                                       | `'请输入数字'`  | 占位文本         |
| `required`        | `boolean`                                      | `false`    | 是否必填         |
| `validator`       | `(value: number \| null) => boolean \| string` | -          | 自定义校验函数      |
| `validateTrigger` | `'onChange' \| 'onBlur' \| 'both'`             | `'onBlur'` | 校验触发时机       |
| `onValidate`      | `(isValid: boolean, errors: string[]) => void` | -          | 校验回调         |
| `autoCorrect`     | `boolean`                                      | `false`    | 是否自动修正超出范围的值 |
| `allowClear`      | `boolean`                                      | `false`    | 是否显示清空按钮     |
| `autoFocus`       | `boolean`                                      | `false`    | 是否自动聚焦       |
| `prefix`          | `ReactNode`                                    | -          | 前缀图标或文本      |
| `suffix`          | `ReactNode`                                    | -          | 后缀图标或文本      |
| `addonBefore`     | `ReactNode`                                    | -          | 前置标签         |
| `addonAfter`      | `ReactNode`                                    | -          | 后置标签         |

### 只读态特有属性

| 属性名          | 类型                              | 默认值      | 说明       |
|--------------|---------------------------------|----------|----------|
| `fontSize`   | `string \| number`              | -        | 字号       |
| `fontWeight` | `string \| number`              | -        | 字重       |
| `color`      | `string`                        | -        | 文本颜色     |
| `textAlign`  | `'left' \| 'center' \| 'right'` | `'left'` | 文本对齐     |
| `emptyText`  | `string`                        | `'-'`    | 空值时的显示文本 |

---

## 组件架构

### 文件结构

```
pisellNumber/
├── index.tsx                      # 组件导出
├── PisellNumber.tsx               # 组件主入口（状态判断）
├── PisellNumber.less              # 组件样式
├── PisellNumber.test.tsx          # 单元测试
├── PisellNumber.stories.tsx       # Storybook 示例
├── types.ts                       # TypeScript 类型定义
├── components/                    # 子组件
│   ├── ReadOnlyView.tsx          # 只读态视图
│   ├── EditView.tsx              # 编辑态视图
│   └── DisabledView.tsx          # 禁用态视图
├── utils/                         # 工具函数
│   ├── index.ts                  # 工具函数导出
│   ├── numberFormatter.ts        # 数值格式化
│   └── numberValidator.ts        # 数值验证
└── docs/                          # 文档
    ├── pisellNumber.md           # 组件使用文档
    ├── pisellNumber.$tab-design.md   # 设计文档（本文件）
    ├── pisellNumber.$tab-dev.md      # 开发文档
    └── pisellNumber.$tab-test.md     # 测试文档
```

### 技术架构

**基础依赖**：

- Ant Design InputNumber：作为编辑态的基础控件
- @pisell/utils：使用公共状态管理逻辑

**状态管理参考**：
参考 PisellSingleLineText，使用 `@pisell/utils` 中的公共逻辑：

- `getDisplayState`：计算显示状态（read/edit/disabled）
- 可扩展使用类似 `useTextInputState` 的 hook 来管理数值输入状态

**视图拆分**：

```
// 主组件：状态判断和路由
PisellNumber.tsx
  ├─> ReadOnlyView    // 只读态：纯文本展示
  ├─> EditView        // 编辑态：基于 InputNumber
  └─> DisabledView    // 禁用态：禁用的 InputNumber
```

---

## 使用示例

### 1. 基础使用（受控模式）

```
import { PisellNumber } from '@pisell/components';
import { useState } from 'react';

function Example() {
  const [quantity, setQuantity] = useState<number>(1);

  return (
    <PisellNumber
      value={quantity}
      onChange={setQuantity}
      min={1}
      max={100}
      placeholder="请输入数量"
    />
  );
}
```

### 2. 非受控模式

```
<PisellNumber
  defaultValue={10}
  min={0}
  max={999}
  step={10}
/>
```

### 3. 只读态展示

```
<PisellNumber
  mode="read"
  value={2.5}
  precision={3}
  useGrouping
/>
```

### 4. 带前后缀

```
<PisellNumber
  value={weight}
  onChange={setWeight}
  precision={2}
  addonAfter="kg"
  min={0}
/>
```

### 5. 表单中使用（带校验）

```
<Form>
  <Form.Item label="购买数量">
    <PisellNumber
      value={quantity}
      onChange={setQuantity}
      min={1}
      max={100}
      step={1}
      precision={0}
      required
      validateTrigger="both"
      onValidate={(isValid, errors) => {
        console.log('校验结果:', isValid, errors);
      }}
    />
  </Form.Item>
</Form>
```

### 6. 表格中使用

```
const columns = [
  {
    title: '数量',
    dataIndex: 'quantity',
    render: (value: number) => (
      <PisellNumber
        mode="read"
        value={value}
        precision={0}
      />
    ),
    sorter: true,
    filters: [
      { text: '小于10', value: [0, 10] },
      { text: '10-100', value: [10, 100] },
    ],
  },
];
```

---

## 设计规范

### 视觉规范

**只读态**：

- 字号：继承父容器或自定义
- 颜色：默认 `rgba(0, 0, 0, 0.85)`
- 对齐：默认左对齐，支持配置

**编辑态**：

- 输入框高度：32px（默认）
- 边框颜色：`#d9d9d9`（正常）、`#1890ff`（聚焦）、`#ff4d4f`（错误）
- 步进器：右侧垂直排列

**禁用态**：

- 透明度：40%
- 背景色：`#f5f5f5`
- 光标：`not-allowed`

### 交互规范

**键盘交互**：

- ↑：增加 step 值
- ↓：减少 step 值
- Enter：触发 `onPressEnter` 回调（如有）
- 支持直接输入数字、小数点、负号

**鼠标交互**：

- 点击步进器按钮：增加/减少值
- 滚轮：在聚焦时滚动调节值（可配置）

---

## 扩展指南

### 创建派生组件

PisellNumber 作为基础组件，可以派生出具有特定语义的组件：

- **PisellCurrency**：货币组件（添加货币符号）
- **PisellPercent**：百分比组件（添加 % 符号）
- **PisellRating**：评分组件（使用星级图标）

派生组件应：

1. 继承 PisellNumber 的所有基础能力
2. 通过 Props 扩展添加新的语义属性
3. 可覆盖 `formatter` 和 `parser` 实现特定格式化逻辑

---

## 性能优化

1. **使用 React.memo**：避免不必要的重渲染
2. **事件函数缓存**：使用 useCallback 缓存事件处理函数
3. **格式化函数缓存**：使用 useMemo 缓存复杂的格式化函数
4. **表格渲染优化**：列定义的 render 函数使用 useCallback

---

## 无障碍支持

- 支持 `aria-label` 和 `aria-describedby` 属性
- 键盘导航友好
- 屏幕阅读器支持
