---
nav:
  title: 组件
  order: 1
group:
  title: General
  order: 1
title: PisellCurrency
order: 1
category: pro
---

# PisellCurrency 货币组件

## 组件介绍

PisellCurrency 是基于 PisellNumber 派生的货币组件，专门用于处理价格、费用、金额等货币相关的数值场景。它继承了 PisellNumber
的所有基础能力，并添加了货币符号、千分位分隔等货币特有的语义化扩展。

**核心特性**：

- ✅ 继承 PisellNumber 的所有能力
- ✅ 货币符号：支持 ¥、$、€、£ 等
- ✅ 符号位置：前置或后置
- ✅ 默认千分位分隔符
- ✅ 默认 2 位小数精度
- ✅ 负数控制：价格场景禁止，退款场景允许

---

## 基础用法

### 基础示例（人民币）

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

export default () => {
  const [price, setPrice] = useState<number | null>(1234.56);

  return (
    <PisellCurrency
      value={price}
      onChange={setPrice}
      currencySymbol="¥"
      placeholder="请输入价格"
    />
  );
};
```

### 不同货币

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

export default () => {
  const [rmb, setRmb] = useState<number | null>(6999.99);
  const [usd, setUsd] = useState<number | null>(1234.56);
  const [eur, setEur] = useState<number | null>(999.99);
  const [gbp, setGbp] = useState<number | null>(599.99);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* 人民币（前置符号） */}
      <PisellCurrency
        value={rmb}
        onChange={setRmb}
        currencySymbol="¥"
      />

      {/* 美元（前置符号） */}
      <PisellCurrency
        value={usd}
        onChange={setUsd}
        currencySymbol="$"
      />

      {/* 欧元（后置符号） */}
      <PisellCurrency
        value={eur}
        onChange={setEur}
        currencySymbol="€"
        symbolPosition="after"
      />

      {/* 英镑（前置符号） */}
      <PisellCurrency
        value={gbp}
        onChange={setGbp}
        currencySymbol="£"
      />
    </div>
  );
};
```

### 只读态展示

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

export default () => {
  return (
    <PisellCurrency
      mode="read"
      value={1234.56}
      currencySymbol="¥"
    />
  );
};
```

### 禁用态

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

export default () => {
  return (
    <PisellCurrency
      disabled
      value={1000}
      currencySymbol="¥"
    />
  );
};
```

---

## 货币配置

### 不显示货币符号

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

export default () => {
  const [value, setValue] = useState<number | null>(1234.56);

  return (
    <PisellCurrency
      value={value}
      onChange={setValue}
      showSymbol={false}
    />
  );
};
```

### 自定义精度

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

export default () => {
  const [jpy, setJpy] = useState<number | null>(1000);
  const [btc, setBtc] = useState<number | null>(1234.567);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* 整数（如日元） */}
      <PisellCurrency
        value={jpy}
        onChange={setJpy}
        currencySymbol="¥"
        currencyCode="JPY"
        precision={0}
      />

      {/* 3位小数（如虚拟货币） */}
      <PisellCurrency
        value={btc}
        onChange={setBtc}
        currencySymbol="BTC"
        precision={3}
      />
    </div>
  );
};
```

### 货币代码（国际化标识）

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

export default () => {
  const [cny, setCny] = useState<number | null>(1234.56);
  const [usd, setUsd] = useState<number | null>(1234.56);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <PisellCurrency
        value={cny}
        onChange={setCny}
        currencySymbol="¥"
        currencyCode="CNY"
      />

      <PisellCurrency
        value={usd}
        onChange={setUsd}
        currencySymbol="$"
        currencyCode="USD"
      />
    </div>
  );
};
```

---

## 负数处理

### 商品价格（不允许负数）

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

export default () => {
  const [price, setPrice] = useState<number | null>(null);

  return (
    <PisellCurrency
      value={price}
      onChange={setPrice}
      currencySymbol="¥"
      allowNegative={false}
      min={0}
      placeholder="请输入价格"
    />
  );
};
```

### 退款金额（允许负数）

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

export default () => {
  const [refund, setRefund] = useState<number | null>(null);

  return (
    <PisellCurrency
      value={refund}
      onChange={setRefund}
      currencySymbol="¥"
      allowNegative
      max={0}
      placeholder="请输入退款金额"
    />
  );
};
```

---

## 表单场景

### 价格表单

```tsx
import { Form } from 'antd';
import { PisellCurrency } from '@pisell/materials';
import { useState } from 'react';

export default () => {
  const [price, setPrice] = useState<number | null>(null);
  const [cost, setCost] = useState<number | null>(null);

  return (
    <Form layout="vertical">
      <Form.Item label="商品价格" required>
        <PisellCurrency
          value={price}
          onChange={setPrice}
          currencySymbol="¥"
          min={0}
          max={999999.99}
          allowNegative={false}
          required
          placeholder="请输入价格"
          validateTrigger="both"
        />
      </Form.Item>

      <Form.Item label="成本价">
        <PisellCurrency
          value={cost}
          onChange={setCost}
          currencySymbol="¥"
          min={0}
          allowNegative={false}
          placeholder="请输入成本"
        />
      </Form.Item>
    </Form>
  );
}
```

---

## 表格场景

### 价格表展示

```tsx
import { Table } from 'antd';
import { PisellCurrency } from '@pisell/materials';

export default () => {
  const columns = [
  {
    title: '商品名称',
    dataIndex: 'name',
  },
  {
    title: '单价',
    dataIndex: 'price',
    render: (value: number) => (
      <PisellCurrency
        mode="read"
        value={value}
        currencySymbol="¥"
      />
    ),
    sorter: (a, b) => a.price - b.price,
  },
  {
    title: '成本',
    dataIndex: 'cost',
    render: (value: number) => (
      <PisellCurrency
        mode="read"
        value={value}
        currencySymbol="¥"
      />
    ),
    sorter: (a, b) => a.cost - b.cost,
  },
  {
    title: '利润',
    dataIndex: 'profit',
    render: (value: number) => (
      <PisellCurrency
        mode="read"
        value={value}
        currencySymbol="¥"
        color={value >= 0 ? '#52c41a' : '#ff4d4f'}
      />
    ),
    sorter: (a, b) => a.profit - b.profit,
  },
];

  const dataSource = [
    { id: 1, name: '商品A', price: 6999.99, cost: 5000, profit: 1999.99 },
    { id: 2, name: '商品B', price: 129.5, cost: 100, profit: 29.5 },
  ];

  return <Table dataSource={dataSource} columns={columns} />;
};
```

---

## 只读态样式配置

### 字体样式

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

export default () => {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* 默认样式 */}
      <PisellCurrency mode="read" value={1234.56} currencySymbol="¥" />

      {/* 大字号加粗（统计数据） */}
      <PisellCurrency
        mode="read"
        value={1234567.89}
        currencySymbol="¥"
        fontSize={24}
        fontWeight="bold"
      />

      {/* 红色（负债、亏损） */}
      <PisellCurrency
        mode="read"
        value={-1234.56}
        currencySymbol="¥"
        color="#ff4d4f"
      />

      {/* 绿色（收益、盈利） */}
      <PisellCurrency
        mode="read"
        value={1234.56}
        currencySymbol="¥"
        color="#52c41a"
      />
    </div>
  );
};
```

### 对齐方式

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

export default () => {
  return (
    <PisellCurrency
      mode="read"
      value={1234.56}
      currencySymbol="¥"
      textAlign="right"
      style={{ width: 150 }}
    />
  );
};
```

---

## 完整示例

### 商品价格管理

```tsx
import { Form, Button } from 'antd';
import { PisellCurrency } from '@pisell/materials';
import { useState } from 'react';

export default () => {
  const [form] = Form.useForm();
  const [price, setPrice] = useState<number | null>(null);
  const [cost, setCost] = useState<number | null>(null);
  const [shipping, setShipping] = useState<number | null>(null);

  const handleSubmit = () => {
    const profit = (price ?? 0) - (cost ?? 0) - (shipping ?? 0);
    console.log('利润:', profit);
  };

  return (
    <Form form={form} layout="vertical" style={{ maxWidth: 400 }}>
      <Form.Item label="商品价格" required>
        <PisellCurrency
          value={price}
          onChange={setPrice}
          currencySymbol="¥"
          min={0}
          allowNegative={false}
          required
          placeholder="请输入价格"
          validateTrigger="both"
        />
      </Form.Item>

      <Form.Item label="成本价" required>
        <PisellCurrency
          value={cost}
          onChange={setCost}
          currencySymbol="¥"
          min={0}
          allowNegative={false}
          required
          placeholder="请输入成本"
        />
      </Form.Item>

      <Form.Item label="运费">
        <PisellCurrency
          value={shipping}
          onChange={setShipping}
          currencySymbol="¥"
          min={0}
          allowNegative={false}
          placeholder="请输入运费"
        />
      </Form.Item>

      <Form.Item>
        <Button type="primary" onClick={handleSubmit}>
          计算利润
        </Button>
      </Form.Item>
    </Form>
  );
};
```

### 财务报表

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

export default () => {
  return (
    <div style={{ padding: 24 }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24 }}>
        <div style={{ padding: 16, border: '1px solid #d9d9d9', borderRadius: 8 }}>
          <div style={{ color: '#667085', marginBottom: 8 }}>总收入</div>
          <PisellCurrency
            mode="read"
            value={1234567.89}
            currencySymbol="¥"
            fontSize={32}
            fontWeight="bold"
            color="#52c41a"
          />
        </div>
        
        <div style={{ padding: 16, border: '1px solid #d9d9d9', borderRadius: 8 }}>
          <div style={{ color: '#667085', marginBottom: 8 }}>总支出</div>
          <PisellCurrency
            mode="read"
            value={987654.32}
            currencySymbol="¥"
            fontSize={32}
            fontWeight="bold"
            color="#ff4d4f"
          />
        </div>
        
        <div style={{ padding: 16, border: '1px solid #d9d9d9', borderRadius: 8 }}>
          <div style={{ color: '#667085', marginBottom: 8 }}>净利润</div>
          <PisellCurrency
            mode="read"
            value={246913.57}
            currencySymbol="¥"
            fontSize={32}
            fontWeight="bold"
            color="#1890ff"
          />
        </div>
      </div>
    </div>
  );
};
```

---

## API 文档

### Props 属性

| 属性名                 | 类型                                             | 默认值        | 说明                     |
|---------------------|------------------------------------------------|------------|------------------------|
| **货币特有属性**          |                                                |            |                        |
| currencySymbol      | `string`                                       | `'¥'`      | 货币符号                   |
| symbolPosition      | `'before' \| 'after'`                          | `'before'` | 符号位置                   |
| showSymbol          | `boolean`                                      | `true`     | 是否显示货币符号               |
| currencyCode        | `string`                                       | -          | 货币代码（CNY, USD等）        |
| allowNegative       | `boolean`                                      | `true`     | 是否允许负数                 |
| **继承 PisellNumber** |                                                |            |                        |
| mode                | `'read' \| 'edit'`                             | `'edit'`   | 显示模式                   |
| disabled            | `boolean`                                      | `false`    | 是否禁用                   |
| value               | `number \| null`                               | -          | 当前值（受控）                |
| defaultValue        | `number \| null`                               | -          | 默认值（非受控）               |
| min                 | `number`                                       | -          | 最小值                    |
| max                 | `number`                                       | -          | 最大值                    |
| step                | `number`                                       | `1`        | 步进值                    |
| precision           | `number`                                       | `2`        | 小数精度（货币默认2位）           |
| useGrouping         | `boolean`                                      | `true`     | 千分位分隔符（货币默认启用）         |
| required            | `boolean`                                      | `false`    | 是否必填                   |
| validator           | `(value: number \| null) => boolean \| string` | -          | 自定义校验                  |
| placeholder         | `string`                                       | `'请输入金额'`  | 占位文本                   |
| size                | `'large' \| 'middle' \| 'small'`               | `'middle'` | 输入框尺寸                  |
| className           | `string`                                       | -          | 自定义类名                  |
| style               | `CSSProperties`                                | -          | 自定义样式                  |
| ...                 | ...                                            | ...        | 其他继承自 PisellNumber 的属性 |

### 事件回调

| 事件名          | 参数                                             | 说明       |
|--------------|------------------------------------------------|----------|
| onChange     | `(value: number \| null) => void`              | 值变化时的回调  |
| onBlur       | `(value: number \| null) => void`              | 失焦时的回调   |
| onFocus      | `() => void`                                   | 聚焦时的回调   |
| onPressEnter | `(value: number \| null) => void`              | 按下回车键的回调 |
| onValidate   | `(isValid: boolean, errors: string[]) => void` | 校验回调     |

---

## 常见问题

### 1. 如何设置货币符号？

```
<PisellCurrency currencySymbol="$" />
```

### 2. 如何修改符号位置？

```
// 前置（默认）
<PisellCurrency currencySymbol="$" symbolPosition="before" />

// 后置
<PisellCurrency currencySymbol="€" symbolPosition="after" />
```

### 3. 如何禁止负数？

```
<PisellCurrency
  value={price}
  onChange={setPrice}
  allowNegative={false}
/>
```

### 4. PisellCurrency 和 PisellNumber 有什么区别？

| 特性   | PisellNumber | PisellCurrency  |
|------|--------------|-----------------|
| 用途   | 通用数值         | 货币金额            |
| 千分位  | 默认关闭         | 默认启用 ✅          |
| 精度   | 无默认值         | 默认2位小数 ✅        |
| 货币符号 | 需手动添加        | 内置支持 ✅          |
| 负数控制 | 无特殊处理        | allowNegative ✅ |
| 语义   | 通用           | 明确表示金额 ✅        |

### 5. 如何在表格中使用？

```
{
  title: '价格',
  dataIndex: 'price',
  render: (value) => (
    <PisellCurrency mode="read" value={value} currencySymbol="¥" />
  ),
}
```

### 6. 支持哪些货币？

支持任意货币符号，常用的有：

| 货币  | 符号 | 代码  | 位置     | 示例          |
|-----|----|-----|--------|-------------|
| 人民币 | ¥  | CNY | before | `¥1,234.56` |
| 美元  | $  | USD | before | `$1,234.56` |
| 欧元  | €  | EUR | after  | `1,234.56€` |
| 英镑  | £  | GBP | before | `£1,234.56` |
| 日元  | ¥  | JPY | before | `¥1,235`    |
| 韩元  | ₩  | KRW | before | `₩1,235`    |
| 卢布  | ₽  | RUB | after  | `1,234.56₽` |

---

## 最佳实践

1. **语义正确**：金额、价格场景必须使用 PisellCurrency，不要用 PisellNumber

2. **负数控制**：
  - 商品价格、成本价：`allowNegative={false}`
  - 退款金额、负债：`allowNegative={true}`

3. **精度设置**：
  - 大部分货币：`precision={2}`（默认）
  - 日元、韩元：`precision={0}`
  - 虚拟货币：根据需求设置

4. **范围限制**：始终设置合理的 min/max
   ```
   <PisellCurrency min={0} max={999999.99} />
   ```

5. **校验时机**：价格输入建议使用 `validateTrigger="both"`

6. **表格展示**：使用只读态，右对齐
   ```
   <PisellCurrency
     mode="read"
     value={value}
     textAlign="right"
   />
   ```

---

## 注意事项

1. **货币符号无空格**：显示为 `¥100` 而不是 `¥ 100`
2. **默认启用千分位**：useGrouping 默认为 true
3. **默认2位小数**：precision 默认为 2
4. **负数显示**：可以通过 color 属性设置负数为红色
5. **类型安全**：value 类型是 `number | null`

---

## 与 PisellNumber 的关系

PisellCurrency 是基于 PisellNumber 的派生组件：

- ✅ 继承所有 Props 和功能
- ✅ 添加货币特有属性
- ✅ 覆盖部分默认值
- ✅ 扩展货币特有校验

使用时根据场景选择：

- 通用数值 → `PisellNumber`
- 货币金额 → `PisellCurrency`

---

## 相关组件

- **PisellNumber** - 基础数值组件
- **PisellPercent** - 百分比组件
- **PisellRating** - 评分组件
