---
nav:
  title: 组件
  order: 1
group:
  title: Data Entry
  order: 1
title: PisellFind
order: 1
category: pro
---

# PisellFind 组件内查找组件

## 组件介绍

PisellFind 是 Pisell 2.0 组件库中的**组件内查找组件**，基于 PisellLookup 封装，专门为已加载数据的查找场景优化。

**核心特性**：

- ✅ 基于 PisellLookup：继承所有搜索交互能力
- ✅ 开箱即用：提供针对"组件内查找"的最佳默认配置
- ✅ 纯 UI 组件：只负责搜索入口，数据过滤由外部处理
- ✅ 默认按钮触发：不干扰主界面布局
- ✅ 输入即搜索：实时过滤，流畅体验
- ✅ 搜索历史：自动保存，提升效率
- ✅ 完全灵活：所有配置都可覆盖

**设计理念**：

PisellFind 是 PisellLookup 的"语义化预设版本"，就像 `<Button type="primary" />` 是对 Button 的预设一样，PisellFind 是对
PisellLookup 在"组件内查找"场景下的预设配置。

---

## 快速开始

### 最简单的用法

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {Table} from 'antd';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro', price: 7999},
        {id: 2, name: 'MacBook Pro', price: 12999},
        {id: 3, name: 'iPad Air', price: 4799},
    ]);

    const [filteredData, setFilteredData] = useState(data);

    return (
        <div>
            <PisellFind
                onSearch={(keyword) => {
                    if (keyword) {
                        const filtered = data.filter(item =>
                            item.name.toLowerCase().includes(keyword.toLowerCase())
                        );
                        setFilteredData(filtered);
                    } else {
                        setFilteredData(data);
                    }
                }}
            />

            <Table
                dataSource={filteredData}
                columns={[
                    {title: '商品名称', dataIndex: 'name'},
                    {title: '价格', dataIndex: 'price'},
                ]}
                style={{marginTop: 16}}
            />
        </div>
    );
};
```

---

## 与 PisellLookup 的关系

### 差异对比

| 属性                | PisellLookup 默认值 | PisellFind 默认值 |
|-------------------|------------------|----------------|
| triggerType       | `'input'`        | `'button'`     |
| showConfirmButton | `true`           | `false`        |
| showHistory       | `false`          | `true`         |
| resultRender      | -                | `undefined`    |

### 等价关系

```tsx
import {PisellFind, PisellLookup} from '@pisell/materials';

// 业务侧过滤函数，仅作示例
const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => (
    <>
        {/* 使用 PisellFind（推荐） */}
        <PisellFind onSearch={(keyword) => filterData(keyword)}/>
        {/* 等价于使用 PisellLookup */}
        <PisellLookup
            triggerType="button"
            showConfirmButton={false}
            showHistory={true}
            onSearch={(keyword) => filterData(keyword)}
        />
    </>
);
```

---

## 基础用法

### 列表查找

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {List, Tag, Space} from 'antd';

export default () => {
    const [products] = useState([
        {id: 1, name: 'iPhone 15 Pro', price: 7999, category: '手机'},
        {id: 2, name: 'MacBook Pro', price: 12999, category: '电脑'},
        {id: 3, name: 'iPad Air', price: 4799, category: '平板'},
    ]);

    const [filteredProducts, setFilteredProducts] = useState(products);

    return (
        <div>
            <div style={{marginBottom: 16}}>
                <PisellFind
                    onSearch={(keyword) => {
                        if (keyword) {
                            const filtered = products.filter(item =>
                                item.name.toLowerCase().includes(keyword.toLowerCase())
                            );
                            setFilteredProducts(filtered);
                        } else {
                            setFilteredProducts(products);
                        }
                    }}
                />
            </div>

            <List
                dataSource={filteredProducts}
                renderItem={(item) => (
                    <List.Item>
                        <List.Item.Meta
                            title={item.name}
                            description={
                                <Space>
                                    <Tag color="blue">{item.category}</Tag>
                                    <span style={{color: '#ff4d4f'}}>¥{item.price}</span>
                                </Space>
                            }
                        />
                    </List.Item>
                )}
            />
        </div>
    );
};
```

### 表格查找

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {Table, Tag} from 'antd';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro', price: 7999, category: '手机', stock: 100},
        {id: 2, name: 'MacBook Pro', price: 12999, category: '电脑', stock: 50},
        {id: 3, name: 'iPad Air', price: 4799, category: '平板', stock: 80},
    ]);

    const [filteredData, setFilteredData] = useState(data);

    return (
        <div>
            <div style={{marginBottom: 16, display: 'flex', justifyContent: 'space-between'}}>
                <h3>商品列表</h3>
                <PisellFind
                    buttonText="查找商品"
                    onSearch={(keyword) => {
                        if (keyword) {
                            const filtered = data.filter(item =>
                                item.name.toLowerCase().includes(keyword.toLowerCase())
                            );
                            setFilteredData(filtered);
                        } else {
                            setFilteredData(data);
                        }
                    }}
                />
            </div>

            <Table
                dataSource={filteredData}
                columns={[
                    {title: '商品名称', dataIndex: 'name'},
                    {title: '价格', dataIndex: 'price', render: (v) => `¥${v}`},
                    {title: '分类', dataIndex: 'category', render: (v) => <Tag>{v}</Tag>},
                    {title: '库存', dataIndex: 'stock'},
                ]}
            />
        </div>
    );
};
```

### 多字段搜索

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {Table} from 'antd';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro', price: 7999, category: '手机'},
        {id: 2, name: 'MacBook Pro', price: 12999, category: '电脑'},
        {id: 3, name: 'iPad Air', price: 4799, category: '平板'},
    ]);
    const [filteredData, setFilteredData] = useState(data);

    const columns = [
        {title: '商品名称', dataIndex: 'name'},
        {title: '价格', dataIndex: 'price'},
        {title: '分类', dataIndex: 'category'},
    ];

    return (
        <div>
            <PisellFind
                buttonText="搜索商品"
                placeholder="搜索商品名称或分类"
                onSearch={(keyword) => {
                    if (keyword) {
                        const kw = keyword.toLowerCase();
                        const filtered = data.filter(item =>
                            item.name.toLowerCase().includes(kw) ||
                            item.category.toLowerCase().includes(kw)
                        );
                        setFilteredData(filtered);
                    } else {
                        setFilteredData(data);
                    }
                }}
            />

            <Table dataSource={filteredData} columns={columns}/>
        </div>
    );
};
```

---

## 切换为 Input 模式

### Input + Inline 模式

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {List} from 'antd';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro', price: 7999},
        {id: 2, name: 'MacBook Pro', price: 12999},
        {id: 3, name: 'iPad Air', price: 4799},
    ]);
    const [filteredData, setFilteredData] = useState(data);

    return (
        <div>
            <PisellFind
                triggerType="input"
                placeholder="搜索商品名称"
                onSearch={(keyword) => {
                    const filtered = keyword
                        ? data.filter(item => item.name.includes(keyword))
                        : data;
                    setFilteredData(filtered);
                }}
            />

            <List
                dataSource={filteredData}
                renderItem={(item) => <List.Item>{item.name} ¥{item.price}</List.Item>}
            />
        </div>
    );
};
```

---

## 使用 Ref 控制

### 程序化控制

```tsx
import {PisellFind, PisellFindRef} from '@pisell/materials';
import {useRef} from 'react';
import {Button, Space} from 'antd';

const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => {
    const findRef = useRef<PisellFindRef>(null);

    return (
        <div>
            <Space style={{marginBottom: 16}}>
                <Button onClick={() => findRef.current?.open()}>打开查找</Button>
                <Button onClick={() => findRef.current?.close()}>关闭查找</Button>
                <Button onClick={() => findRef.current?.setKeyword('iPhone')}>
                    搜索 iPhone
                </Button>
            </Space>

            <PisellFind
                ref={findRef}
                onSearch={(keyword) => filterData(keyword)}
            />
        </div>
    );
};
```

---

## 高级配置

### 显示确认按钮

```tsx
import {PisellFind} from '@pisell/materials';

const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => {
    return (
        <PisellFind
            showConfirmButton={true}
            confirmButtonText="Search"
            searchMode="confirm"
            onSearch={(keyword) => filterData(keyword)}
        />
    );
};
```

### 关闭搜索历史

```tsx
import {PisellFind} from '@pisell/materials';

const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => {
    return (
        <PisellFind
            showHistory={false}
            onSearch={(keyword) => filterData(keyword)}
        />
    );
};
```

### 使用 Drawer 展示

```tsx
import {PisellFind} from '@pisell/materials';

const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => {
    return (
        <PisellFind
            displayType="drawer"
            drawerPlacement="right"
            onSearch={(keyword) => filterData(keyword)}
        />
    );
};
```

---

## API 文档

### Props

PisellFind 继承 PisellLookup 的所有 Props，只是提供不同的默认值。

#### 与 PisellLookup 不同的默认值

| 属性                | 类型                                        | 默认值                     | 说明         |
|-------------------|-------------------------------------------|-------------------------|------------|
| triggerType       | `'input' \| 'button' \| 'icon' \| 'text'` | `'button'`              | 触发器类型      |
| showConfirmButton | `boolean`                                 | `false`                 | 是否显示确认按钮   |
| showHistory       | `boolean`                                 | `true`                  | 是否显示搜索历史   |
| historyKey        | `string`                                  | `'pisell_find_history'` | 历史记录存储 key |

#### 常用配置

| 属性           | 类型                                                         | 默认值         | 说明       |
|--------------|------------------------------------------------------------|-------------|----------|
| buttonText   | `string`                                                   | `'Find'`    | 按钮文本     |
| placeholder  | `string`                                                   | `'请输入搜索内容'` | 占位符      |
| size         | `'large' \| 'middle' \| 'small'`                           | `'middle'`  | 输入框尺寸    |
| disabled     | `boolean`                                                  | `false`     | 是否禁用     |
| displayType  | `'inline' \| 'popup' \| 'modal' \| 'drawer' \| 'detached'` | 智能判断        | 展示类型     |
| debounceTime | `number`                                                   | `300`       | 防抖时间（毫秒） |
| mask         | `boolean`                                                  | `false`     | 是否显示遮罩层  |

#### 完整 Props

完整的 Props 列表请参考 [PisellLookup 文档](./pisellLookup.md#api-文档)。

### 事件回调

| 事件名      | 类型                          | 说明         |
|----------|-----------------------------|------------|
| onSearch | `(keyword: string) => void` | 搜索触发回调（必需） |
| onChange | `(value: string) => void`   | 关键词变化回调    |
| onOpen   | `() => void`                | 打开查找面板回调   |
| onClose  | `() => void`                | 关闭查找面板回调   |

### Ref 方法

| 方法名        | 类型                          | 说明      |
|------------|-----------------------------|---------|
| open       | `() => void`                | 打开查找面板  |
| close      | `() => void`                | 关闭查找面板  |
| getKeyword | `() => string`              | 获取当前关键词 |
| setKeyword | `(keyword: string) => void` | 设置关键词   |

---

## 常见问题

### 1. PisellFind 和 PisellLookup 有什么区别？

**PisellLookup**：通用搜索交互协议组件，适用于所有搜索场景，需要配置。

**PisellFind**：基于 PisellLookup 的预设配置版本，专门为"组件内查找"优化，开箱即用。

**选择建议：**

- 已加载数据的查找 → 使用 **PisellFind**
- 需要请求接口的搜索 → 使用 **PisellLookup**
- 需要高度自定义 → 使用 **PisellLookup**

### 2. 如何过滤数据？

在 `onSearch` 回调中处理数据过滤：

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro'},
        {id: 2, name: 'MacBook Pro'},
    ]);
    const [filteredData, setFilteredData] = useState(data);

    return (
        <PisellFind
            onSearch={(keyword) => {
                const filtered = keyword
                    ? data.filter(item => item.name.includes(keyword))
                    : data;
                setFilteredData(filtered);
            }}
        />
    );
};
```

**注意：** 空关键词时应该恢复显示所有数据。

### 3. 如何多字段搜索？

在 `onSearch` 回调中处理多字段匹配：

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone', category: '手机', description: '智能手机'},
        {id: 2, name: 'MacBook', category: '电脑', description: '笔记本电脑'},
    ]);
    const [filteredData, setFilteredData] = useState(data);

    return (
        <PisellFind
            onSearch={(keyword) => {
                if (keyword) {
                    const kw = keyword.toLowerCase();
                    const filtered = data.filter(item =>
                        item.name.toLowerCase().includes(kw) ||
                        item.category.toLowerCase().includes(kw) ||
                        item.description.toLowerCase().includes(kw)
                    );
                    setFilteredData(filtered);
                } else {
                    setFilteredData(data);
                }
            }}
        />
    );
};
```

### 4. 如何切换为 Input 模式？

设置 `triggerType="input"`：

```tsx
import {PisellFind} from '@pisell/materials';

const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => (
    <PisellFind
        triggerType="input"
        placeholder="搜索商品"
        onSearch={(keyword) => filterData(keyword)}
    />
);
```

### 5. 如何显示确认按钮？

设置 `showConfirmButton={true}`：

```tsx
import {PisellFind} from '@pisell/materials';

const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => (
    <PisellFind
        showConfirmButton={true}
        confirmButtonText="Search"
        onSearch={(keyword) => filterData(keyword)}
    />
);
```

### 6. 如何关闭搜索历史？

设置 `showHistory={false}`：

```tsx
import {PisellFind} from '@pisell/materials';

const filterData = (keyword: string) => {
    /* 在此根据 keyword 过滤数据并更新展示 */
};

export default () => (
    <PisellFind
        showHistory={false}
        onSearch={(keyword) => filterData(keyword)}
    />
);
```

### 7. 搜索结果如何在弹窗中展示？

提供 `resultRender` 函数：

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {List} from 'antd';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro'},
        {id: 2, name: 'MacBook Pro'},
    ]);
    const [filteredData, setFilteredData] = useState(data);

    return (
        <PisellFind
            onSearch={(keyword) => {
                const filtered = keyword
                    ? data.filter(item => item.name.includes(keyword))
                    : data;
                setFilteredData(filtered);
            }}
            resultRender={() => (
                <List
                    dataSource={filteredData}
                    renderItem={(item) => <List.Item>{item.name}</List.Item>}
                />
            )}
        />
    );
};
```

### 8. 如何实现大小写不敏感搜索？

在 `onSearch` 中使用 `toLowerCase()`：

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro'},
        {id: 2, name: 'MacBook Pro'},
    ]);
    const [filteredData, setFilteredData] = useState(data);

    return (
        <PisellFind
            onSearch={(keyword) => {
                const kw = keyword.toLowerCase();
                const filtered = data.filter(item =>
                    item.name.toLowerCase().includes(kw)
                );
                setFilteredData(filtered);
            }}
        />
    );
};
```

---

## 实际应用场景

### SKU 列表查找（Big Sale 场景）

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {Table, Button, Space} from 'antd';

export default () => {
    const [skuList] = useState([
        {sku: 'SKU001', name: 'iPhone 15 Pro 256GB', price: 7999, stock: 100},
        {sku: 'SKU002', name: 'iPhone 15 Pro 512GB', price: 8999, stock: 50},
        {sku: 'SKU003', name: 'MacBook Pro 14"', price: 12999, stock: 30},
    ]);

    const [filteredList, setFilteredList] = useState(skuList);
    const [selectedRows, setSelectedRows] = useState([]);

    return (
        <div>
            <div style={{marginBottom: 16, display: 'flex', justifyContent: 'space-between'}}>
                <Space>
                    <h3>SKU 列表</h3>
                    <PisellFind
                        buttonText="查找 SKU"
                        onSearch={(keyword) => {
                            if (keyword) {
                                const kw = keyword.toLowerCase();
                                const filtered = skuList.filter(item =>
                                    item.sku.toLowerCase().includes(kw) ||
                                    item.name.toLowerCase().includes(kw)
                                );
                                setFilteredList(filtered);
                            } else {
                                setFilteredList(skuList);
                            }
                        }}
                    />
                </Space>
                <Button type="primary" disabled={selectedRows.length === 0}>
                    加入购物车 ({selectedRows.length})
                </Button>
            </div>

            <Table
                dataSource={filteredList}
                columns={[
                    {title: 'SKU', dataIndex: 'sku'},
                    {title: '商品名称', dataIndex: 'name'},
                    {title: '价格', dataIndex: 'price', render: (v) => `¥${v}`},
                    {title: '库存', dataIndex: 'stock'},
                ]}
                rowSelection={{
                    selectedRowKeys: selectedRows,
                    onChange: (keys) => setSelectedRows(keys),
                }}
            />
        </div>
    );
};
```

### 卡片流查找

```tsx
import {PisellFind} from '@pisell/materials';
import {useState} from 'react';
import {Card, Tag} from 'antd';

export default () => {
    const [products] = useState([
        {id: 1, name: 'iPhone 15 Pro', price: 7999, category: '手机'},
        {id: 2, name: 'MacBook Pro', price: 12999, category: '电脑'},
        {id: 3, name: 'iPad Air', price: 4799, category: '平板'},
    ]);
    const [filteredProducts, setFilteredProducts] = useState(products);

    return (
        <div>
            <div style={{marginBottom: 16}}>
                <PisellFind
                    triggerType="input"
                    placeholder="搜索商品"
                    onSearch={(keyword) => {
                        const filtered = keyword
                            ? products.filter(item => item.name.includes(keyword))
                            : products;
                        setFilteredProducts(filtered);
                    }}
                />
            </div>

            <div style={{display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16}}>
                {filteredProducts.map(item => (
                    <Card key={item.id} hoverable>
                        <h4>{item.name}</h4>
                        <p style={{color: '#ff4d4f', fontWeight: 'bold'}}>¥{item.price}</p>
                        <Tag color="blue">{item.category}</Tag>
                    </Card>
                ))}
            </div>
        </div>
    );
};
```

---

## 最佳实践

### 1. 数据过滤逻辑

**推荐：** 在 onSearch 中处理空关键词

```
onSearch={(keyword) => {
  if (keyword) {
    // 有关键词：过滤数据
    const filtered = data.filter(item => item.name.includes(keyword));
    setFilteredData(filtered);
  } else {
    // 空关键词：恢复所有数据
    setFilteredData(data);
  }
}}
```

### 2. 大小写不敏感

```
onSearch={(keyword) => {
  if (keyword) {
    const kw = keyword.toLowerCase();
    const filtered = data.filter(item =>
      item.name.toLowerCase().includes(kw)
    );
    setFilteredData(filtered);
  } else {
    setFilteredData(data);
  }
}}
```

### 3. 多字段搜索

```
onSearch={(keyword) => {
  if (keyword) {
    const kw = keyword.toLowerCase();
    const filtered = data.filter(item =>
      item.name.toLowerCase().includes(kw) ||
      item.category.toLowerCase().includes(kw)
    );
    setFilteredData(filtered);
  } else {
    setFilteredData(data);
  }
}}
```

### 4. 性能优化

对于大数据量（1000+ 条），可以使用防抖和 useMemo：

```tsx
import {PisellFind} from '@pisell/materials';
import {useState, useMemo} from 'react';

export default () => {
    const [data] = useState([
        {id: 1, name: 'iPhone 15 Pro'},
        {id: 2, name: 'MacBook Pro'},
        {id: 3, name: 'iPad Air'},
    ]);
    const [keyword, setKeyword] = useState('');

    const filteredData = useMemo(() => {
        if (keyword) {
            const kw = keyword.toLowerCase();
            return data.filter(item => item.name.toLowerCase().includes(kw));
        }
        return data;
    }, [keyword, data]);

    return (
        <PisellFind
            value={keyword}
            onChange={setKeyword}
            onSearch={setKeyword}
        />
    );
};
```

---

## 注意事项

1. **职责边界**：PisellFind 只负责搜索入口交互，数据过滤逻辑由外部在 `onSearch` 回调中处理。

2. **必需的 onSearch**：必须提供 `onSearch` 回调来处理数据过滤，否则组件无实际作用。

3. **空关键词处理**：onSearch 会传递空字符串（清空输入时），外部应该恢复显示所有数据。

4. **实时搜索**：默认使用实时搜索模式（输入即触发），注意设置合理的 `debounceTime`（默认 300ms）。

5. **数据范围**：PisellFind 适用于已加载到内存的数据，不适合需要请求接口的搜索场景（应使用 PisellLookup）。

6. **历史记录**：默认保存搜索历史到 localStorage（key: `pisell_find_history`），可以通过 `historyKey` 自定义。

---

## 相关组件

- **PisellLookup** - 通用搜索交互协议组件（PisellFind 的基础）
- **PisellFilter** - 筛选组件
- **PisellSort** - 排序组件
- **PisellQuickFilter** - 快速筛选组件
