---
nav:
  title: 组件
  order: 1
group:
  title: Data Display
  order: 1
title: ImageCard
order: 1
category: atom
---

# PisellImageCard 图片卡片

PisellImageCard 是一个功能强大的图片卡片组件，用于展示图片内容。支持占位图、失败兜底、文字降级、懒加载等丰富特性，可广泛应用于商品图、头像、封面图等场景。

## 何时使用

- 需要展示商品图片
- 作为头像显示
- 作为封面图或缩略图
- 需要优雅处理图片加载失败的场景
- 需要在图片加载时显示占位图
- 需要实现图片懒加载以优化性能

## 基础使用

### 基础图片卡片

最简单的用法，只需传入图片地址。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
    />
  );
};
```

## 填充模式

PisellImageCard 支持 4 种图片填充模式，通过 `fillMode` 属性控制。

### 覆盖模式（cover）

图片覆盖整个容器，保持比例，可能会裁剪。这是默认模式。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
      fillMode="cover"
    />
  );
};
```

### 包含模式（contain）

图片完整显示，保持比例，可能会留白。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
      fillMode="contain"
    />
  );
};
```

### 填充模式（fill）

图片拉伸填充容器，不保持比例。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
      fillMode="fill"
    />
  );
};
```

### 原始模式（none）

图片保持原始尺寸，不缩放。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
      fillMode="none"
    />
  );
};
```

## 图片状态处理

### 占位图

在图片加载时显示占位图。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
        fallbackSrc: 'https://via.placeholder.com/200x200?text=Loading',
      }}
      width={200}
      height={200}
    />
  );
};
```

### 失败兜底图

当图片加载失败时显示兜底图。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://invalid-url.com/image.png',
        alt: '商品图片',
        errorSrc: 'https://via.placeholder.com/200x200?text=Error',
      }}
      width={200}
      height={200}
    />
  );
};
```

### 完整图片状态处理

同时配置占位图和失败兜底图，提供完整的加载体验。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
        fallbackSrc: 'https://via.placeholder.com/200x200?text=Loading',
        errorSrc: 'https://via.placeholder.com/200x200?text=Error',
      }}
      width={200}
      height={200}
    />
  );
};
```

## 文字降级

当图片未设置或加载失败时，可以显示文字作为降级方案。

### 使用 alt 降级

自动截取 alt 的前 2 个字符作为降级文字。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        alt: '商品名称',
      }}
      width={200}
      height={200}
    />
  );
};
```

### 自定义降级文字

通过 `text` 属性自定义降级文字内容。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        alt: '商品名称',
        text: '商品',
      }}
      width={200}
      height={200}
    />
  );
};
```

## 懒加载

启用懒加载功能，图片仅在进入视口时才开始加载，有效优化页面性能。

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

export default () => {
  return (
    <div style={{ height: '2000px', paddingTop: '1500px' }}>
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
          alt: '商品图片',
          fallbackSrc: 'https://via.placeholder.com/200x200?text=Loading',
        }}
        width={200}
        height={200}
        lazy
      />
    </div>
  );
};
```

## 状态管理

### 激活状态

设置图片卡片的激活状态，可用于选中效果。

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

export default () => {
  const [activeId, setActiveId] = useState('1');

  return (
    <div style={{ display: 'flex', gap: '16px' }}>
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
          alt: '商品1',
        }}
        width={200}
        height={200}
        active={{ value: activeId === '1' }}
        onClick={() => setActiveId('1')}
      />
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e',
          alt: '商品2',
        }}
        width={200}
        height={200}
        active={{ value: activeId === '2' }}
        onClick={() => setActiveId('2')}
      />
    </div>
  );
};
```

### 激活状态附加内容

在激活状态下显示额外内容。

```tsx
import { PisellCards } from '@pisell/materials';
import { CheckCircleFilled } from '@ant-design/icons';

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
      active={{
        value: true,
        extra: (
          <CheckCircleFilled
            style={{
              fontSize: 24,
              color: '#52c41a',
              position: 'absolute',
              top: 8,
              right: 8,
            }}
          />
        ),
      }}
    />
  );
};
```

### 禁用状态

设置图片卡片的禁用状态。

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

export default () => {
  return (
    <div style={{ display: 'flex', gap: '16px' }}>
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
          alt: '正常',
        }}
        width={200}
        height={200}
        onClick={() => alert('点击正常图片')}
      />
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e',
          alt: '禁用',
        }}
        width={200}
        height={200}
        disabled={{ value: true }}
        onClick={() => alert('不会触发')}
      />
    </div>
  );
};
```

### 禁用状态附加内容

在禁用状态下显示额外内容，如蒙层提示。

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
      disabled={{
        value: true,
        extra: (
          <div
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              right: 0,
              bottom: 0,
              background: 'rgba(0,0,0,0.5)',
              color: '#fff',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            已售罄
          </div>
        ),
      }}
    />
  );
};
```

### 加载状态

设置图片卡片的加载状态。

```tsx
import { PisellCards } from '@pisell/materials';
import { Spin } from 'antd';

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '商品图片',
      }}
      width={200}
      height={200}
      loading={{
        value: true,
        extra: (
          <div
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              right: 0,
              bottom: 0,
              background: 'rgba(255,255,255,0.8)',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            <Spin />
          </div>
        ),
      }}
    />
  );
};
```

## 自定义尺寸

### 固定尺寸

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

export default () => {
  return (
    <div style={{ display: 'flex', gap: '16px', alignItems: 'flex-end' }}>
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
          alt: '100x100',
        }}
        width={100}
        height={100}
      />
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
          alt: '200x200',
        }}
        width={200}
        height={200}
      />
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
          alt: '300x200',
        }}
        width={300}
        height={200}
      />
    </div>
  );
};
```

### 响应式尺寸

使用百分比或其他 CSS 单位实现响应式尺寸。

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

export default () => {
  return (
    <div style={{ width: '100%', maxWidth: 600 }}>
      <PisellCards.PisellImageCard
        dataSource={{
          src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
          alt: '响应式图片',
        }}
        width="100%"
        height={300}
      />
    </div>
  );
};
```

## 自定义样式

### 圆角

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '圆角图片',
      }}
      width={200}
      height={200}
      style={{ borderRadius: '16px', overflow: 'hidden' }}
    />
  );
};
```

### 圆形

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '圆形图片',
      }}
      width={200}
      height={200}
      style={{ borderRadius: '50%', overflow: 'hidden' }}
    />
  );
};
```

### 边框阴影

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

export default () => {
  return (
    <PisellCards.PisellImageCard
      dataSource={{
        src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
        alt: '带阴影图片',
      }}
      width={200}
      height={200}
      style={{
        borderRadius: '8px',
        overflow: 'hidden',
        boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
      }}
    />
  );
};
```

## 综合示例

### 商品图片展示

```tsx
import { PisellCards } from '@pisell/materials';
import { CheckCircleFilled } from '@ant-design/icons';
import { useState } from 'react';

export default () => {
  const [selected, setSelected] = useState('1');

  const products = [
    {
      id: '1',
      src: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30',
      alt: '耳机',
    },
    {
      id: '2',
      src: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e',
      alt: '耳机',
    },
    {
      id: '3',
      src: 'https://images.unsplash.com/photo-1572635196237-14b3f281503f',
      alt: '太阳镜',
    },
  ];

  return (
    <div style={{ display: 'flex', gap: '16px' }}>
      {products.map((product) => (
        <PisellCards.PisellImageCard
          key={product.id}
          dataSource={{
            src: product.src,
            alt: product.alt,
            fallbackSrc: 'https://via.placeholder.com/200?text=Loading',
            errorSrc: 'https://via.placeholder.com/200?text=Error',
          }}
          width={200}
          height={200}
          fillMode="cover"
          lazy
          active={{
            value: selected === product.id,
            extra: (
              <CheckCircleFilled
                style={{
                  fontSize: 24,
                  color: '#52c41a',
                  position: 'absolute',
                  top: 8,
                  right: 8,
                }}
              />
            ),
          }}
          onClick={() => setSelected(product.id)}
          style={{
            borderRadius: '8px',
            overflow: 'hidden',
            cursor: 'pointer',
            border: selected === product.id ? '2px solid #52c41a' : 'none',
          }}
        />
      ))}
    </div>
  );
};
```

## API

### PisellImageCardProps

| 参数       | 说明               | 类型                                       | 默认值    |
| ---------- | ------------------ | ------------------------------------------ | --------- |
| dataSource | 图片数据源配置     | `ImageDataSource`                          | -         |
| fillMode   | 图片填充模式       | `'cover' \| 'contain' \| 'fill' \| 'none'` | `'cover'` |
| width      | 图片宽度           | `number \| string`                         | `'100%'`  |
| height     | 图片高度           | `number \| string`                         | `'100%'`  |
| lazy       | 是否启用图片懒加载 | `boolean`                                  | `false`   |
| active     | 激活状态配置       | `ActiveConfig`                             | -         |
| disabled   | 禁用状态配置       | `DisabledConfig`                           | -         |
| loading    | 加载状态配置       | `LoadingConfig`                            | -         |
| onClick    | 点击事件           | `(e: MouseEvent) => void`                  | -         |
| className  | 自定义样式类名     | `string`                                   | -         |
| style      | 自定义样式         | `CSSProperties`                            | -         |
| children   | 子元素             | `ReactNode`                                | -         |

### ImageDataSource

| 参数        | 说明                                                             | 类型     | 默认值 |
| ----------- | ---------------------------------------------------------------- | -------- | ------ |
| src         | 图片地址                                                         | `string` | -      |
| alt         | 图片替换文本（当图片未设置或加载失败时，显示 alt 的前 2 个字符） | `string` | -      |
| errorSrc    | 图片加载失败后的兜底图地址                                       | `string` | -      |
| fallbackSrc | 首次加载时的占位图地址                                           | `string` | -      |
| text        | 自定义降级文字内容（优先级高于 alt）                             | `string` | -      |

### 状态配置（ActiveConfig / DisabledConfig / LoadingConfig）

| 参数      | 说明                   | 类型                       | 默认值 |
| --------- | ---------------------- | -------------------------- | ------ |
| value     | 是否处于该状态         | `boolean`                  | -      |
| style     | 该状态下的自定义样式   | `CSSProperties`            | -      |
| className | 该状态下的自定义类名   | `string`                   | -      |
| onChange  | 状态改变回调           | `(value: boolean) => void` | -      |
| extra     | 该状态下额外的渲染内容 | `ReactNode`                | -      |

## 注意事项

1. **图片优先级**：图片加载过程中的显示优先级为：`fallbackSrc`（加载中） > `errorSrc`（加载失败） > `src`（正常）
2. **文字降级**：当没有任何图片可显示时，会显示 `text` 或 `alt` 的前 2 个字符作为降级方案
3. **懒加载**：启用懒加载后，图片只有在进入视口时才开始加载，适合长列表场景
4. **状态优先级**：loading > disabled > active
5. **点击事件**：disabled 和 loading 状态下，点击事件不会触发
6. **尺寸单位**：width 和 height 支持任意 CSS 合法值（px、%、rem、vw 等）
7. **填充模式**：默认使用 `cover` 模式，适合大多数场景，如需完整显示图片可使用 `contain` 模式
