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

﻿# PisellLongText 组件设计文档

> **组件定位**：PisellLongText 是 Pisell 组件库中的长文本/多行文本输入/展示组件，适用于较长内容、多行描述、备注说明等场景。组件基于
> Ant Design Input.TextArea 组件开发，提供多行展示、展开/折叠功能和完整的校验框架。

## 1 组件概述

### 1.1 组件定位

PisellLongText 是一个专注于长文本处理的多行文本组件，具有以下特点：

- **多行文本支持**：基于 Ant Design TextArea，支持多行输入和展示
- **展开/折叠功能**：只读态下支持内容截断和展开收起
- **状态灵活切换**：支持只读、编辑、禁用三种状态
- **高度可配置**：行数、字数统计、展开按钮等均可配置
- **代码复用**：复用 PisellSingleLineText 的公共逻辑

### 1.2 使用场景

- 产品描述、详细说明
- 用户评论、反馈内容
- 备注、说明文本
- 文章摘要、简介
- 多行地址、邮寄信息

### 1.3 设计稿

Figma 设计稿：https://www.figma.com/design/pM8Ho6d7kCMv9vIBvFHUlj/Pisell-2.0-Component-Library?node-id=3263-6210&m=dev

### 1.4 与 SingleLineText 的关系

- **独立组件**：PisellLongText 是独立组件，不是继承关系
- **复用逻辑**：复用 `useTextInputState`、`validateValue` 等公共 hooks 和 utils
- **扩展功能**：在单行文本基础上增加多行展示、展开折叠等特性

## 2 组件状态设计

### 2.1 三种状态模式

PisellLongText 支持三种状态，与 SingleLineText 保持一致：

| 状态      | 视觉表现                  | 交互行为                  | 透明度  | 使用场景      |
|---------|-----------------------|-----------------------|------|-----------|
| **只读态** | 不显示输入框，多行文本展示，支持展开/折叠 | 不可编辑，可展开查看全文          | 100% | 信息展示、内容预览 |
| **编辑态** | 显示多行输入框（TextArea）     | 可输入、可换行、支持校验提示        | 100% | 表单输入、内容编辑 |
| **禁用态** | 输入框灰化显示               | 不可编辑，无 hover/focus 状态 | 40%  | 表单禁用、权限限制 |

### 2.2 状态切换逻辑

```
// 状态逻辑与 SingleLineText 一致
interface StateControl {
  mode: 'read' | 'edit';
  disabled?: boolean;
}

function getDisplayState(mode: string, disabled: boolean) {
  if (disabled) return 'disabled';
  return mode;
}
```

## 3 核心功能设计

### 3.1 多行展示控制

**行数配置**：

- `rows`：编辑态默认显示行数（默认 3）
- `minRows`：编辑态最小行数
- `maxRows`：编辑态最大行数（自动调整高度）
- `maxLines`：只读态最大显示行数（超出显示展开按钮）

**自动高度**：

- 编辑态支持自动调整高度（autoSize）
- 根据内容动态调整，在 minRows 和 maxRows 之间

### 3.2 展开/折叠功能

**只读态展开收起**：

```
// 展开/折叠状态
interface ExpandState {
  isExpanded: boolean; // 是否已展开
  needExpand: boolean; // 是否需要展开功能
  expandText: string; // 展开按钮文案，默认"展开"
  collapseText: string; // 收起按钮文案，默认"收起"
}

// 判断是否需要展开功能
function checkNeedExpand(content: string, maxLines: number): boolean {
  const lines = content.split('\n');
  return lines.length > maxLines;
}
```

**展开按钮配置**：

- `enableExpand`：是否启用展开功能（默认 true）
- `expandText`：展开按钮文案
- `collapseText`：收起按钮文案
- `expandButtonPosition`：按钮位置（'bottom' | 'inline'）

### 3.3 字数统计

**字数统计功能**：

- `showCount`：是否显示字数统计
- `showWordCount`：是否显示字数统计（只读态）
- 实时显示当前字数/最大字数

## 4 组件 Props 设计

### 4.1 完整 Props 定义

```
interface PisellLongTextProps {
  // ========== 核心属性（继承自 SingleLineText）==========
  value?: string;
  defaultValue?: string;
  mode?: 'read' | 'edit';
  disabled?: boolean;

  // ========== 多行文本特有配置 ==========
  // 编辑态配置
  rows?: number;                     // 默认行数，默认 3
  minRows?: number;                  // 最小行数
  maxRows?: number;                  // 最大行数
  autoSize?: boolean | { minRows?: number; maxRows?: number }; // 自动高度

  // 只读态配置
  maxLines?: number;                 // 只读态最大显示行数，默认 3
  enableExpand?: boolean;            // 是否启用展开功能，默认 true
  expandText?: string;               // 展开按钮文案，默认"展开"
  collapseText?: string;             // 收起按钮文案，默认"收起"
  expandButtonPosition?: 'bottom' | 'inline'; // 按钮位置，默认 'bottom'

  // 字数统计
  showCount?: boolean;               // 编辑态是否显示字数统计
  showWordCount?: boolean;           // 只读态是否显示字数统计

  // ========== 展示配置 ==========
  ellipsis?: boolean;                // 超出省略（只读态）
  showTooltip?: boolean;             // 悬停显示完整内容
  fontSize?: string | number;
  fontWeight?: string | number;
  color?: string;
  lineHeight?: string | number;     // 行高

  // ========== 编辑配置 ==========
  placeholder?: string;
  maxLength?: number;
  allowClear?: boolean;
  autoFocus?: boolean;

  // ========== 校验配置（复用 SingleLineText）==========
  required?: boolean;
  minLength?: number;
  pattern?: string | RegExp;
  validator?: (value: string) => boolean | string;
  validateTrigger?: 'onChange' | 'onBlur' | 'both';
  errorMessage?: string;

  // ========== 事件回调 ==========
  onChange?: (value: string) => void;
  onBlur?: (value: string) => void;
  onFocus?: () => void;
  onPressEnter?: (e: React.KeyboardEvent) => void;
  onExpand?: (expanded: boolean) => void;  // 展开/收起回调
  onValidate?: (isValid: boolean, errors: string[]) => void;

  // ========== 样式类名 ==========
  className?: string;
  style?: React.CSSProperties;

  // ========== 无障碍访问 ==========
  'aria-label'?: string;
  'aria-describedby'?: string;
}
```

### 4.2 与 SingleLineText 的差异

| 配置项                   | SingleLineText | LongText | 说明                 |
|-----------------------|----------------|----------|--------------------|
| prefix/suffix/addon   | ✅              | ❌        | 长文本不需要前后缀          |
| rows/minRows/maxRows  | ❌              | ✅        | 多行文本特有             |
| maxLines/enableExpand | ❌              | ✅        | 展开折叠功能             |
| autoSize              | ❌              | ✅        | 自动高度调整             |
| lineHeight            | ❌              | ✅        | 行高配置（多行需要）         |
| onPressEnter          | 回调值            | 回调事件对象   | 长文本可能需要 Ctrl+Enter |

## 5 技术实现要点

### 5.1 基于 Ant Design TextArea

组件基于 `antd` 的 `Input.TextArea` 组件进行封装：

```
import { Input } from 'antd';
const { TextArea } = Input;

// 编辑态渲染
function EditView(props: PisellLongTextProps) {
  const { value, handleChange, isValid, errors } = useTextInputState(props);

  return (
    <div className="pisell-long-text-edit">
      <TextArea
        value={value}
        onChange={(e) => handleChange(e.target.value)}
        onBlur={handleBlur}
        onFocus={handleFocus}
        onPressEnter={props.onPressEnter}
        placeholder={props.placeholder}
        maxLength={props.maxLength}
        showCount={props.showCount}
        allowClear={props.allowClear}
        rows={props.rows || 3}
        autoSize={props.autoSize || {
          minRows: props.minRows,
          maxRows: props.maxRows
        }}
        disabled={props.disabled}
        status={isValid ? undefined : 'error'}
      />
      {!isValid && errors.length > 0 && (
        <div className="pisell-long-text-error">
          {errors[0]}
        </div>
      )}
    </div>
  );
}
```

### 5.2 只读态展开/折叠实现

```
import { Typography, Button } from 'antd';
const { Paragraph } = Typography;

function ReadOnlyView(props: PisellLongTextProps) {
  const [isExpanded, setIsExpanded] = useState(false);
  const content = props.value || '';
  const lines = content.split('\n');
  const needExpand = lines.length > (props.maxLines || 3);

  // 截断内容
  const displayContent = !isExpanded && needExpand
    ? lines.slice(0, props.maxLines || 3).join('\n') + '...'
    : content;

  const textStyle: React.CSSProperties = {
    fontSize: props.fontSize,
    fontWeight: props.fontWeight,
    color: props.color,
    lineHeight: props.lineHeight,
    whiteSpace: 'pre-wrap', // 保留换行和空格
  };

  return (
    <div className="pisell-long-text-read">
      <Paragraph style={textStyle}>
        {displayContent}
      </Paragraph>

      {props.enableExpand !== false && needExpand && (
        <Button
          type="link"
          size="small"
          onClick={() => {
            setIsExpanded(!isExpanded);
            props.onExpand?.(!isExpanded);
          }}
          className="pisell-long-text-expand-btn"
        >
          {isExpanded
            ? (props.collapseText || '收起')
            : (props.expandText || '展开')
          }
        </Button>
      )}

      {props.showWordCount && (
        <div className="pisell-long-text-word-count">
          {content.length} 字
        </div>
      )}
    </div>
  );
}
```

### 5.3 复用 SingleLineText 逻辑

```
// 复用状态管理 Hook
import { useTextInputState } from '../pisellSingleLineText/hooks/useTextInputState';
import { validateValue } from '../pisellSingleLineText/utils/validation';
import { getDisplayState } from '../pisellSingleLineText/utils/stateHelper';

// 可以直接使用这些公共逻辑
function PisellLongText(props: PisellLongTextProps) {
  const displayState = getDisplayState(props.mode, props.disabled);
  const textState = useTextInputState(props);

  // ... 组件实现
}
```

### 5.4 性能优化

```
// 1. 防抖处理（复用）
import { useDebouncedChange } from '../pisellSingleLineText/hooks/useDebouncedChange';

// 2. memo 优化
export const PisellLongText = React.memo((props: PisellLongTextProps) => {
  // 组件实现
}, (prevProps, nextProps) => {
  return prevProps.value === nextProps.value &&
    prevProps.mode === nextProps.mode &&
    prevProps.disabled === nextProps.disabled;
});

// 3. 虚拟滚动（可选，超长文本场景）
// 当内容超过 1000 行时，可以考虑使用虚拟滚动
```

## 6 校验框架

### 6.1 校验规则（复用 SingleLineText）

| 优先级 | 校验规则    | 配置项       | 说明                   |
|-----|---------|-----------|----------------------|
| 1   | 必填校验    | required  | 值不能为空或纯空白字符          |
| 2   | 最小长度校验  | minLength | 字符数不能少于指定值           |
| 3   | 最大长度校验  | maxLength | 字符数不能超过指定值           |
| 4   | 正则校验    | pattern   | 值必须匹配指定的正则表达式        |
| 5   | 自定义校验函数 | validator | 自定义校验逻辑，返回错误信息或 true |

### 6.2 特殊校验场景

```
// 针对长文本的特殊校验
// 例如：检查换行符数量、段落数量等
const validator = (value: string) => {
  const lines = value.split('\n');
  if (lines.length > 50) {
    return '内容不能超过 50 段';
  }
  return true;
};

<PisellLongText validator={validator} />
```

## 7 开发规范

### 7.1 文件结构

```
pisellLongText/
├── index.tsx # 导出文件
├── PisellLongText.tsx # 组件主文件
├── PisellLongText.less # 样式文件
├── PisellLongText.test.tsx # 单元测试
├── types.ts # 类型定义
├── components/ # 子组件
│   ├── ReadOnlyView.tsx # 只读态视图（含展开折叠）
│   ├── EditView.tsx # 编辑态视图
│   └── DisabledView.tsx # 禁用态视图
└── docs/ # 文档
    ├── pisellLongText.md # 使用文档
    ├── pisellLongText.$tab-design.md # 设计文档（本文档）
    ├── pisellLongText.$tab-dev.md # 开发文档
    └── pisellLongText.$tab-test.md # 测试文档
```

**注意**：不需要创建 hooks 和 utils 目录，直接复用 `pisellSingleLineText` 的公共逻辑。

### 7.2 代码质量要求

1. **TypeScript 类型**：所有 Props 必须有明确的类型定义
2. **注释规范**：使用 JSDoc 注释，说明组件、方法用途
3. **测试覆盖**：单元测试覆盖率不低于 85%
4. **代码风格**：遵循 ESLint 和 Prettier 配置
5. **无障碍访问**：支持 ARIA 属性

### 7.3 设计原则

1. **基于 Ant Design**：复用 Ant Design TextArea 的能力
2. **逻辑复用**：复用 SingleLineText 的公共 hooks 和 utils
3. **UI 逻辑分离**：展开折叠逻辑独立封装
4. **性能优先**：使用 memo、useCallback 等优化手段

## 8 使用示例

### 8.1 基础用法

```
// 编辑态
<PisellLongText
  mode="edit"
  placeholder="请输入内容"
  rows={3}
  maxLength={500}
  showCount
/>

// 只读态（展开折叠）
<PisellLongText
  mode="read"
  value={longText}
  maxLines={3}
  enableExpand
  expandText="查看更多"
  collapseText="收起"
/>
```

### 8.2 自动高度

```
<PisellLongText
  mode="edit"
  autoSize={{ minRows: 3, maxRows: 10 }}
  placeholder="输入内容，自动调整高度"
/>
```

### 8.3 校验

```
<PisellLongText
  mode="edit"
  required
  minLength={10}
  maxLength={500}
  validator={(value) => {
    if (value.includes('敏感词')) {
      return '内容包含敏感词';
    }
    return true;
  }}
  validateTrigger="onBlur"
/>
```

## 9 总结

PisellLongText 是一个专注于长文本处理的多行文本组件，具备以下核心能力：

- ✅ 三种状态模式（只读、编辑、禁用）
- ✅ 基于 Ant Design TextArea，支持多行输入
- ✅ 展开/折叠功能（只读态）
- ✅ 自动高度调整
- ✅ 字数统计
- ✅ 完善的校验框架（复用 SingleLineText）
- ✅ 代码复用，降低维护成本

本组件与 PisellSingleLineText 共享公共逻辑（hooks/utils），保持一致的设计模式和用户体验。
