npm package: @hbis-uni/hbis-waterfall
Description: HBIS UNI Waterfall Component
Latest version: 1.0.1

# HbisWaterfall 瀑布流组件

## 简介

HbisWaterfall 是一个基于 Vue 3 开发的瀑布流布局组件，支持动态列数、动态数据加载、高度计算和自定义内容。适用于图片展示、商品列表等需要瀑布流布局的场景。

## 特性

- **动态列数**：支持 2 到 maxColumn 列的动态调整
- **高度计算**：根据组件宽度和图片比例计算高度，实现真正的瀑布流效果
- **响应式布局**：使用组件宽度而非屏幕宽度，适应不同容器大小
- **自定义内容**：通过插槽支持自定义图片下方的内容
- **实时调整**：支持通过滑块等控件实时调整列数和间距
- **性能优化**：只在必要时重新计算和分配数据
- **错误处理**：包含图片加载错误处理

## 安装

### 作为组件库的一部分安装

```bash
pnpm add @hbis-uni/components
```

### 单独安装

```bash
pnpm add @hbis-uni/hbis-waterfall
```

## 使用方法

### 基本使用

```vue
<template>
  <HbisWaterfall
    :data="waterfallData"
    :column="2"
    :max-column="5"
    :column-gap="10"
    :row-gap="10"
    image-key="url"
    item-key="id"
  />
</template>

<script setup>
import { HbisWaterfall } from '@hbis-uni/components';

const waterfallData = [
  {
    id: 'img-1',
    url: 'https://example.com/image1.jpg'
  },
  {
    id: 'img-2',
    url: 'https://example.com/image2.jpg'
  },
  // 更多数据...
];
</script>
```

### 带自定义内容的使用

```vue
<template>
  <HbisWaterfall
    ref="waterfallRef"
    :data="waterfallData"
    :column="3"
    :column-gap="15"
    :row-gap="15"
    image-key="url"
    item-key="id"
  >
    <template #default="{ item, index }">
      <div class="custom-content">
        <h3>图片 {{ index + 1 }}</h3>
        <p>描述: {{ item.description || '无描述' }}</p>
      </div>
    </template>
  </HbisWaterfall>
</template>

<script setup>
import { ref } from 'vue';
import { HbisWaterfall } from '@hbis-uni/components';

const waterfallRef = ref();
const waterfallData = [
  {
    id: 'img-1',
    url: 'https://example.com/image1.jpg',
    description: '这是一张测试图片'
  },
  // 更多数据...
];

// 手动刷新瀑布流
const refreshWaterfall = () => {
  waterfallRef.value?.refresh();
};
</script>

<style scoped>
.custom-content {
  padding: 15px;
  background-color: white;
  border-radius: 0 0 8px 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
</style>
```

## API

### 属性

| 属性名 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| data | Array | `[]` | 瀑布流数据数组，每个元素应包含图片URL等信息 |
| column | Number | `2` | 列数，最小为2 |
| maxColumn | Number | `5` | 最大列数 |
| columnGap | [Number, String] | `10` | 列间距（px） |
| rowGap | [Number, String] | `10` | 行间距（px） |
| itemKey | String | `'id'` | 数据唯一键字段名 |
| imageKey | String | `'url'` | 图片URL字段名 |

### 事件

| 事件名 | 说明 | 参数 |
| --- | --- | --- |
| load | 图片加载成功事件 | `event: Event` |
| error | 图片加载失败事件 | `event: Event` |

### 插槽

| 插槽名 | 说明 | 作用域变量 |
| --- | --- | --- |
| default | 自定义内容插槽，用于展示图片下方的内容 | `{ item: Object, index: Number }` |

### 方法

| 方法名 | 说明 | 参数 | 返回值 |
| --- | --- | --- | --- |
| refresh | 刷新数据，重新分配到多列 | 无 | 无 |

## 数据结构

### 基本数据结构

```javascript
const waterfallData = [
  {
    id: 'img-1',        // 唯一标识，对应 itemKey
    url: 'https://example.com/image1.jpg',  // 图片URL，对应 imageKey
    description: '图片描述'  // 自定义字段，可在插槽中使用
  },
  // 更多数据...
];
```

### 字段说明

- `id`：数据唯一键，用于 Vue 的 v-for 渲染，可通过 `itemKey` 属性指定其他字段
- `url`：图片URL，用于显示图片，可通过 `imageKey` 属性指定其他字段
- 其他字段：可自定义添加，在插槽中通过 `item` 访问

## 示例

### 完整示例

```vue
<template>
  <div class="waterfall-demo">
    <h2>瀑布流组件示例</h2>
    
    <div class="controls">
      <div class="control-group">
        <label>列数: {{ column }}</label>
        <input 
          type="range" 
          v-model.number="column" 
          min="2" 
          :max="maxColumn" 
          step="1"
        />
      </div>
      
      <div class="control-group">
        <label>列间距: {{ columnGap }}px</label>
        <input 
          type="range" 
          v-model.number="columnGap" 
          min="0" 
          max="30" 
          step="1"
        />
      </div>
      
      <div class="control-group">
        <label>行间距: {{ rowGap }}px</label>
        <input 
          type="range" 
          v-model.number="rowGap" 
          min="0" 
          max="30" 
          step="1"
        />
      </div>
      
      <button @click="addMoreData">加载更多</button>
    </div>
    
    <HbisWaterfall
      ref="waterfallRef"
      :data="waterfallData"
      :column="column"
      :max-column="maxColumn"
      :column-gap="columnGap"
      :row-gap="rowGap"
      image-key="url"
      item-key="id"
      @load="handleImageLoad"
      @error="handleImageError"
    >
      <template #default="{ item, index }">
        <div class="custom-content">
          <h3>图片 {{ index + 1 }}</h3>
          <p>描述: {{ item.description }}</p>
          <p>尺寸: {{ item.width }} × {{ item.height }}</p>
        </div>
      </template>
    </HbisWaterfall>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { HbisWaterfall } from '@hbis-uni/components';

const waterfallRef = ref();
const column = ref(2);
const maxColumn = ref(5);
const columnGap = ref(10);
const rowGap = ref(10);
const waterfallData = ref([]);

// 生成测试数据
const generateTestData = (count = 20) => {
  const data = [];
  const widths = [200, 250, 300, 350, 400];
  
  for (let i = 0; i < count; i++) {
    const width = widths[Math.floor(Math.random() * widths.length)];
    const height = Math.floor(width * (0.5 + Math.random()));
    
    data.push({
      id: `img-${Date.now()}-${i}`,
      url: `https://gips1.baidu.com/it/u=3874647369,3220417986&fm=3028&app=3028&f=JPEG&fmt=auto?w=720&h=1280`,
      description: `这是第 ${i + 1} 张测试图片`,
      width,
      height
    });
  }
  return data;
};

// 加载更多数据
const addMoreData = () => {
  const newData = generateTestData(10);
  waterfallData.value = [...waterfallData.value, ...newData];
};

// 处理图片加载
const handleImageLoad = (event) => {
  console.log('图片加载成功:', event.target.src);
};

// 处理图片错误
const handleImageError = (event) => {
  console.error('图片加载失败:', event.target.src);
};

// 组件挂载
onMounted(() => {
  waterfallData.value = generateTestData(20);
});
</script>

<style scoped>
.waterfall-demo {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.controls {
  background-color: #f5f5f5;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 20px;
}

.control-group {
  margin-bottom: 15px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.control-group label {
  width: 120px;
  font-weight: bold;
}

.control-group input[type="range"] {
  flex: 1;
}

button {
  padding: 10px 20px;
  background-color: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background-color: #66b1ff;
}

.custom-content {
  padding: 15px;
  background-color: white;
  border-radius: 0 0 8px 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.custom-content h3 {
  margin: 0 0 10px 0;
  color: #333;
  font-size: 16px;
}

.custom-content p {
  margin: 5px 0;
  color: #666;
  font-size: 14px;
}
</style>
```

## 注意事项

1. **数据项要求**：
   - 数据项必须包含唯一键（通过 itemKey 指定），用于 Vue 的 v-for 渲染
   - 数据项必须包含图片 URL（通过 imageKey 指定），用于显示图片

2. **性能优化**：
   - 当数据量较大时，建议使用虚拟滚动或分页加载
   - 图片建议使用适当的尺寸，避免过大的图片影响加载性能

3. **布局注意事项**：
   - 组件容器应设置适当的宽度，以便组件能够正确计算列宽
   - 当容器宽度变化时，组件会自动重新计算布局

4. **兼容性**：
   - 支持 Vue 3 项目和 Uniapp 项目
   - 在 Uniapp 中使用时，注意平台差异（如样式前缀等）

## 浏览器兼容性

- Chrome >= 60
- Firefox >= 55
- Safari >= 12
- Edge >= 79
- 支持现代移动浏览器

## 贡献

欢迎提交 Issue 和 Pull Request 来改进这个组件。

## 许可证

ISC