---
title: 开发文档
order: 3
category: pro
---

# PisellSingleLineText 组件开发文档

## 1 组件信息

### 1.1 基本信息

- **组件名称**：PisellSingleLineText
- **组件版本**：V1.0.0
- **组件分类**：表单输入 / 文本展示
- **开发状态**：开发中
- **最后更新**：2026-01-24

### 1.2 组件描述

PisellSingleLineText 是一个通用的单行文本输入/展示组件，支持只读、编辑、禁用三种状态模式。组件基于 Ant Design Input
组件开发，提供完整的校验框架、受控/非受控模式支持，并为后续的 LongText、Phone、Email、Url 组件提供可复用的公共逻辑。

## 2 需求点罗列

### 2.1 功能需求

#### 核心功能

- ✅ 支持三种状态：只读态、编辑态、禁用态
- ✅ 支持受控和非受控两种使用模式
- ✅ 基于 Ant Design Input 组件封装
- ✅ 完整的校验框架（必填、长度、正则、自定义）
- ✅ 校验触发时机可配置（onChange / onBlur / both）

#### 展示功能（只读态）

- ✅ 文本截断（ellipsis）
- ✅ Tooltip 显示完整内容
- ✅ 可配置字号、字重、颜色、对齐方式

#### 编辑功能（编辑态）

- ✅ 支持前缀/后缀图标或文本（prefix/suffix）
- ✅ 支持前置/后置标签（addonBefore/addonAfter）
- ✅ 字数统计（showCount）
- ✅ 清空按钮（allowClear）
- ✅ 自动聚焦（autoFocus）
- ✅ 最大长度限制（maxLength）

#### 校验功能

- ✅ 必填校验（required）
- ✅ 最小/最大长度校验（minLength/maxLength）
- ✅ 正则校验（pattern）
- ✅ 自定义校验函数（validator）
- ✅ 错误提示展示

### 2.2 非功能需求

- ✅ TypeScript 类型定义完整
- ✅ 代码注释使用 JSDoc 规范
- ✅ 性能优化（React.memo、useCallback、useMemo）
- ✅ 无障碍访问支持（ARIA 属性）
- ✅ 单元测试覆盖率 ≥ 85%

## 3 组件 API

### 3.1 Props 定义

#### 核心属性

| 属性名          | 类型               | 默认值    | 必填 | 说明       |
|--------------|------------------|--------|----|----------|
| value        | string           | -      | 否  | 输入值（受控）  |
| defaultValue | string           | -      | 否  | 默认值（非受控） |
| mode         | 'read' \| 'edit' | 'edit' | 否  | 状态模式     |
| disabled     | boolean          | false  | 否  | 是否禁用     |

#### 展示配置（只读态）

| 属性名         | 类型                            | 默认值    | 必填 | 说明       |
|-------------|-------------------------------|--------|----|----------|
| ellipsis    | boolean                       | true   | 否  | 超出省略     |
| showTooltip | boolean                       | true   | 否  | 悬停显示完整内容 |
| fontSize    | string \| number              | -      | 否  | 字号       |
| fontWeight  | string \| number              | -      | 否  | 字重       |
| color       | string                        | -      | 否  | 文本颜色     |
| textAlign   | 'left' \| 'center' \| 'right' | 'left' | 否  | 对齐方式     |

#### 编辑配置（编辑态）

| 属性名         | 类型              | 默认值   | 必填 | 说明      |
|-------------|-----------------|-------|----|---------|
| placeholder | string          | -     | 否  | 占位文本    |
| maxLength   | number          | -     | 否  | 最大长度    |
| showCount   | boolean         | false | 否  | 显示字数统计  |
| allowClear  | boolean         | false | 否  | 显示清空按钮  |
| autoFocus   | boolean         | false | 否  | 自动聚焦    |
| prefix      | React.ReactNode | -     | 否  | 前缀图标/文本 |
| suffix      | React.ReactNode | -     | 否  | 后缀图标/文本 |
| addonBefore | React.ReactNode | -     | 否  | 前置标签    |
| addonAfter  | React.ReactNode | -     | 否  | 后置标签    |

#### 校验配置

| 属性名             | 类型                                   | 默认值      | 必填 | 说明      |
|-----------------|--------------------------------------|----------|----|---------|
| required        | boolean                              | false    | 否  | 是否必填    |
| minLength       | number                               | -        | 否  | 最小长度    |
| pattern         | string \| RegExp                     | -        | 否  | 正则校验    |
| validator       | (value: string) => boolean \| string | -        | 否  | 自定义校验函数 |
| validateTrigger | 'onChange' \| 'onBlur' \| 'both'     | 'onBlur' | 否  | 校验触发时机  |
| errorMessage    | string                               | -        | 否  | 自定义错误提示 |

#### 事件回调

| 属性名          | 类型                                           | 说明    |
|--------------|----------------------------------------------|-------|
| onChange     | (value: string) => void                      | 值变化回调 |
| onBlur       | (value: string) => void                      | 失焦回调  |
| onFocus      | () => void                                   | 聚焦回调  |
| onPressEnter | (value: string) => void                      | 回车回调  |
| onValidate   | (isValid: boolean, errors: string[]) => void | 校验回调  |

#### 样式类名

| 属性名       | 类型                  | 说明    |
|-----------|---------------------|-------|
| className | string              | 自定义类名 |
| style     | React.CSSProperties | 自定义样式 |

#### 无障碍访问

| 属性名              | 类型     | 说明      |
|------------------|--------|---------|
| aria-label       | string | ARIA 标签 |
| aria-describedby | string | ARIA 描述 |

### 3.2 类型定义

详见 `types.ts` 文件中的 `PisellSingleLineTextProps` 接口定义。

## 4 组件模块拆解

### 4.1 目录结构

```
pisellSingleLineText/
├── index.tsx                          # 导出文件
├── PisellSingleLineText.tsx          # 组件主文件
├── PisellSingleLineText.less         # 样式文件
├── PisellSingleLineText.test.tsx     # 单元测试
├── types.ts                          # 类型定义
├── hooks/                            # 自定义 Hooks（可复用）
│   ├── index.ts
│   ├── useTextInputState.ts         # 状态管理 Hook
│   └── useTextValidation.ts         # 校验 Hook（可选）
├── utils/                            # 工具函数（可复用）
│   ├── index.ts
│   ├── validation.ts                # 校验函数
│   └── stateHelper.ts               # 状态辅助函数
├── components/                       # 子组件
│   ├── ReadOnlyView.tsx             # 只读态视图
│   ├── EditView.tsx                 # 编辑态视图
│   └── DisabledView.tsx             # 禁用态视图
└── docs/                            # 文档
    ├── pisellSingleLineText.md               # 使用文档
    ├── pisellSingleLineText.$tab-design.md   # 设计文档
    ├── pisellSingleLineText.$tab-dev.md      # 开发文档（本文档）
    └── pisellSingleLineText.$tab-test.md     # 测试文档
```

### 4.2 模块职责

#### 主组件（PisellSingleLineText.tsx）

- 统筹组件渲染逻辑
- 根据 mode 和 disabled 决定渲染哪个子组件
- 管理组件的整体状态

#### 子组件

**ReadOnlyView.tsx**

- 职责：渲染只读态视图
- 功能：文本展示、截断、Tooltip
- 依赖：Ant Design Typography.Text、Tooltip

**EditView.tsx**

- 职责：渲染编辑态视图
- 功能：输入框、校验、错误提示
- 依赖：Ant Design Input

**DisabledView.tsx**

- 职责：渲染禁用态视图
- 功能：灰化显示（基于 EditView 或 ReadOnlyView）
- 依赖：EditView 或 ReadOnlyView

#### Hooks

**useTextInputState.ts**（核心 Hook，可复用）

- 职责：管理组件内部状态
- 功能：
  - 判断受控/非受控模式
  - 管理值、校验状态、错误信息
  - 提供 handleChange、handleBlur、handleFocus 方法
- 可被其他文本组件复用

**useTextValidation.ts**（可选）

- 职责：封装校验逻辑
- 功能：执行各种校验规则，返回校验结果

#### Utils

**validation.ts**（核心工具，可复用）

- 职责：提供校验函数
- 功能：
  - validateValue：执行校验
  - 支持必填、长度、正则、自定义校验
- 可被其他文本组件复用

**stateHelper.ts**（可复用）

- 职责：状态辅助函数
- 功能：
  - getDisplayState：根据 mode 和 disabled 计算显示状态
- 可被其他文本组件复用

## 5 组件实现方案

### 5.1 技术栈

- React 18+
- TypeScript
- Ant Design 5.x
- Less（样式）

### 5.2 核心实现

#### 5.2.1 主组件结构

主组件采用状态驱动的渲染模式：

```
import React from 'react';
import {getDisplayState} from './utils/stateHelper';
import ReadOnlyView from './components/ReadOnlyView';
import EditView from './components/EditView';
import DisabledView from './components/DisabledView';
import type {PisellSingleLineTextProps} from './types';
import './PisellSingleLineText.less';

export const PisellSingleLineText: React.FC<PisellSingleLineTextProps> = (props) => {
    const {mode = 'edit', disabled = false} = props;

    // 计算显示状态
    const displayState = getDisplayState(mode, disabled);

    // 根据状态渲染对应子组件
    switch (displayState) {
        case 'read':
            return <ReadOnlyView {...props} />;
        case 'edit':
            return <EditView {...props} />;
        case 'disabled':
            return <DisabledView {...props} />;
        default:
            return null;
    }
};

// 性能优化：使用 memo
export default React.memo(PisellSingleLineText, (prevProps, nextProps) => {
    return prevProps.value === nextProps.value &&
        prevProps.mode === nextProps.mode &&
        prevProps.disabled === nextProps.disabled;
});
```

#### 5.2.2 状态管理 Hook

useTextInputState.ts 是核心 Hook，管理组件状态：

```
import {useState, useCallback, useMemo} from 'react';
import {validateValue} from '../utils/validation';
import type {PisellSingleLineTextProps} from '../types';

export function useTextInputState(props: PisellSingleLineTextProps) {
    // 判断是否为受控组件
    const isControlled = props.value !== undefined;

    // 内部状态
    const [innerValue, setInnerValue] = useState(props.defaultValue || '');
    const [isTouched, setIsTouched] = useState(false);
    const [isValid, setIsValid] = useState(true);
    const [errors, setErrors] = useState<string[]>([]);

    // 当前值（受控优先）
    const currentValue = isControlled ? props.value : innerValue;

    // 值变更处理
    const handleChange = useCallback((newValue: string) => {
        // 更新内部状态（非受控模式）
        if (!isControlled) {
            setInnerValue(newValue);
        }

        // 根据配置决定是否立即校验
        if (props.validateTrigger === 'onChange' || props.validateTrigger === 'both') {
            const validation = validateValue(newValue, props);
            setIsValid(validation.isValid);
            setErrors(validation.errors);
            props.onValidate?.(validation.isValid, validation.errors);
        }

        // 触发外部回调
        props.onChange?.(newValue);
    }, [isControlled, props]);

    // 失焦处理
    const handleBlur = useCallback(() => {
        setIsTouched(true);

        // 失焦时执行校验
        if (props.validateTrigger !== 'onChange') {
            const validation = validateValue(currentValue || '', props);
            setIsValid(validation.isValid);
            setErrors(validation.errors);
            props.onValidate?.(validation.isValid, validation.errors);
        }

        props.onBlur?.(currentValue || '');
    }, [currentValue, props]);

    // 聚焦处理
    const handleFocus = useCallback(() => {
        setIsTouched(true);
        props.onFocus?.();
    }, [props]);

    return {
        value: currentValue,
        isTouched,
        isValid,
        errors,
        handleChange,
        handleBlur,
        handleFocus
    };
}
```

#### 5.2.3 校验函数

validation.ts 提供统一的校验逻辑：

```
import type {PisellSingleLineTextProps} from '../types';

export function validateValue(
    value: string,
    props: PisellSingleLineTextProps
): { isValid: boolean; errors: string[] } {
    const errors: string[] = [];

    // 1. 必填校验
    if (props.required && !value?.trim()) {
        errors.push(props.errorMessage || '此字段为必填项');
        return {isValid: false, errors};
    }
    
    // 如果值为空且非必填，直接返回有效
    if (!value) {
        return {isValid: true, errors};
    }

    // 2. 最小长度校验
    if (props.minLength && value.length < props.minLength) {
        errors.push(`最少需要 ${props.minLength} 个字符`);
    }

    // 3. 最大长度校验
    if (props.maxLength && value.length > props.maxLength) {
        errors.push(`最多允许 ${props.maxLength} 个字符`);
    }

    // 4. 正则校验
    if (props.pattern) {
        const regex = typeof props.pattern === 'string'
            ? new RegExp(props.pattern)
            : props.pattern;
        if (!regex.test(value)) {
            errors.push(props.errorMessage || '格式不正确');
        }
    }

    // 5. 自定义校验
    if (props.validator) {
        const result = props.validator(value);
        if (result !== true) {
            errors.push(typeof result === 'string' ? result : '校验失败');
        }
    }

    return {
        isValid: errors.length === 0,
        errors
    };
}
```

#### 5.2.4 编辑态实现

EditView.tsx 基于 Ant Design Input：

```
import React from 'react';
import {Input} from 'antd';
import {useTextInputState} from '../hooks/useTextInputState';
import type {PisellSingleLineTextProps} from '../types';

const EditView: React.FC<PisellSingleLineTextProps> = (props) => {
    const {value, isValid, errors, handleChange, handleBlur, handleFocus} = useTextInputState(props);

    return (
        <div className="pisell-single-line-text-edit">
            <Input
                value={value}
                onChange={(e) => handleChange(e.target.value)}
                onBlur={handleBlur}
                onFocus={handleFocus}
                onPressEnter={(e) => props.onPressEnter?.(e.currentTarget.value)}
                placeholder={props.placeholder}
                maxLength={props.maxLength}
                showCount={props.showCount}
                allowClear={props.allowClear}
                prefix={props.prefix}
                suffix={props.suffix}
                addonBefore={props.addonBefore}
                addonAfter={props.addonAfter}
                disabled={props.disabled}
                autoFocus={props.autoFocus}
                status={isValid ? undefined : 'error'}
                className={props.className}
                style={props.style}
                aria-label={props['aria-label']}
                aria-describedby={props['aria-describedby']}
            />
            {!isValid && errors.length > 0 && (
                <div className="pisell-single-line-text-error">
                    {errors[0]}
                </div>
            )}
        </div>
    );
};
```

export default EditView;

#### 5.2.5 只读态实现

ReadOnlyView.tsx 基于 Ant Design Typography：

```
import React from 'react';
import {Typography, Tooltip} from 'antd';
import type {PisellSingleLineTextProps} from '../types';

const ReadOnlyView: React.FC<PisellSingleLineTextProps> = (props) => {
    const textStyle: React.CSSProperties = {
        fontSize: props.fontSize,
        fontWeight: props.fontWeight,
        color: props.color,
        textAlign: props.textAlign,
        ...props.style
    };

    const textContent = (
        <Typography.Text
            ellipsis={props.ellipsis}
            style={textStyle}
            className={`pisell-single-line-text-read ${props.className || ''}`}
        >
            {props.value}
        </Typography.Text>
    );

    // 如果启用 tooltip 且内容被截断，显示悬浮提示
    if (props.showTooltip !== false && props.ellipsis !== false) {
        return (
            <Tooltip title={props.value}>
                {textContent}
            </Tooltip>
        );
    }

    return textContent;
};

export default ReadOnlyView;
```

### 5.3 样式实现

**重要：本组件不依赖全局 antd token 配置**

为了确保组件样式的独立性和可维护性，PisellSingleLineText 使用内置样式覆盖 antd Input 组件的全局 token 配置。这样做的好处：

1. **样式隔离**：组件样式不受全局 token 变化影响
2. **可预测性**：组件在任何环境下都保持一致的外观
3. **维护性**：样式修改只影响当前组件

PisellSingleLineText.less：

```less
.pisell-single-line-text {
  &-edit {
    position: relative;
    width: 100%;

    // Input 组件内置样式，覆盖 antd token 配置
    .ant-input,
    .ant-input-affix-wrapper {
      // 基础样式（基于 Figma 设计规范）
      font-size: 16px !important;
      line-height: 24px !important;
      border-radius: 8px !important;
      border: 1px solid #D0D5DD !important;
      padding: 12px 16px !important;
      height: 48px !important;
      color: #101828 !important;
      background-color: #FFFFFF !important;
      
      // ... 更多样式定义
    }
  }
  
  &-error {
    margin-top: 4px;
    font-size: 12px;
    color: #D92D20; // Error/600
    line-height: 1.5;
    animation: fadeIn 0.3s ease-in-out;
  }
  
  &-read {
    display: inline-block;
    line-height: 1.5;
  }
  
  &-disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }
}
```

**样式覆盖说明**：

1. 使用 `!important` 确保样式优先级高于全局 token
2. 所有颜色值、尺寸值均为硬编码，基于 Figma 设计规范
3. 包含所有状态样式：默认、聚焦、悬停、禁用、错误
4. 支持 Input 的所有变体：普通输入框、带前后缀、带前后置标签

### 5.4 导出设计

index.tsx：

```
export {default as PisellSingleLineText} from './PisellSingleLineText';
export type {PisellSingleLineTextProps} from './types';

// 导出可复用的 hooks 和 utils，供其他文本组件使用
export {useTextInputState} from './hooks/useTextInputState';
export {validateValue} from './utils/validation';
export {getDisplayState} from './utils/stateHelper';
```

## 6 组件组成与引用

### 6.1 依赖的外部组件

| 组件/库            | 来源    | 用途      |
|-----------------|-------|---------|
| Input           | antd  | 编辑态输入框  |
| Typography.Text | antd  | 只读态文本展示 |
| Tooltip         | antd  | 只读态悬停提示 |
| React           | react | 组件基础    |

### 6.2 组件结构图

```
PisellSingleLineText
├── ReadOnlyView
│   ├── Typography.Text (antd)
│   └── Tooltip (antd)
├── EditView
│   └── Input (antd)
└── DisabledView
    ├── ReadOnlyView（复用）
    └── EditView（复用）
```

### 6.3 公共逻辑复用

本组件的以下模块可被其他文本组件复用：

**Hooks**

- useTextInputState：状态管理
- useTextValidation：校验逻辑（可选）

**Utils**

- validation.ts：校验函数
- stateHelper.ts：状态辅助

**复用关系图**

```
PisellSingleLineText（基础组件）
    ↓ 复用 hooks/utils
├── PisellLongText（长文本）
├── PisellPhone（电话号码）
├── PisellEmail（邮箱地址）
└── PisellUrl（URL 链接）
```

## 7 扩展性设计

### 7.1 通用性

- 组件不绑定任何业务逻辑
- 所有配置项均可选
- 支持自定义样式和类名
- 支持自定义校验函数

### 7.2 可扩展点

1. **自定义校验**：通过 validator 属性传入自定义校验函数
2. **自定义样式**：通过 className 和 style 属性
3. **自定义前后缀**：通过 prefix、suffix、addonBefore、addonAfter
4. **自定义错误提示**：通过 errorMessage 属性

### 7.3 代码复用

- Hooks 和 Utils 可被导出，供其他文本组件复用
- 遵循开闭原则，对扩展开放，对修改关闭

## 8 性能优化

### 8.1 优化策略

1. **React.memo**：避免不必要的重渲染
2. **useCallback**：缓存事件处理函数
3. **useMemo**：缓存计算结果
4. **防抖处理**：可选的输入防抖（通过 HOC 实现）

### 8.2 性能指标

- 首次渲染：< 16ms
- 重渲染：< 8ms
- 内存占用：< 1MB

## 9 开发注意事项

### 9.1 代码规范

1. 使用 TypeScript 严格模式
2. 所有函数添加 JSDoc 注释
3. 遵循 ESLint 和 Prettier 规则
4. 变量命名清晰、语义化

### 9.2 测试要求

1. 单元测试覆盖率 ≥ 85%
2. 测试受控/非受控模式
3. 测试三种状态切换
4. 测试所有校验规则
5. 测试事件回调

### 9.3 文档要求

1. 组件使用文档（pisellSingleLineText.md）
2. API 文档完整
3. 使用示例丰富
4. 常见问题解答

## 10 后续迭代计划

### 10.1 V1.0.0（当前版本）

- ✅ 三种状态模式
- ✅ 完整校验框架
- ✅ 受控/非受控支持
- ✅ 代码复用设计

### 10.2 V1.1.0（计划中）

- 🔲 国际化支持（i18n）
- 🔲 主题定制能力增强
- 🔲 更多预设校验规则
- 🔲 输入格式化（如千分位）

### 10.3 V2.0.0（未来）

- 🔲 虚拟滚动（超长文本场景）
- 🔲 富文本编辑器集成
- 🔲 语音输入支持
- 🔲 OCR 识别支持

## 11 总结

PisellSingleLineText 是一个设计良好、高度可配置、易于扩展的单行文本组件。通过合理的模块拆分和代码复用设计，它不仅能够满足当前需求，还为后续的
LongText、Phone、Email、Url 等文本组件提供了坚实的基础。

**核心优势**：

- ✅ 基于成熟的 Ant Design 组件
- ✅ 完整的 TypeScript 类型支持
- ✅ 清晰的代码结构和职责分离
- ✅ 强大的可扩展性和复用性
- ✅ 完善的性能优化
