文章目录
- Ant Design Card 组件展示图片
- 理解card组件结构
- 隐藏卡片内容区域
- 下拉时加载图片卡片
- 加载更多
- 代码
- 分页的方式加载图片列表【推荐】
- 实现思路
- 代码demo
- 缩略图
- 在card组件下发显示图片文件名
- 卡片操作
- 分页
Ant Design Card 组件展示图片
官方文档:
Card卡片组件 https://4x.ant.design/components/card-cn/#Card
ProCard高级卡片 https://procomponents.ant.design/components/card
Card卡片
通用卡片容器。
何时使用#
最基础的卡片容器,可承载文字、列表、图片、段落,常用于后台概览页面。
理解card组件结构
如上图,Card组件分成如上组成部分,
- header这部分 是,title属性来配置
- 卡片封面 是sove属性来配置
- 卡片内容区域 Card.Meta 来配置
- 卡片动作部分,是action属性来配置
隐藏卡片内容区域
我们使用一个名为 custom-card 的类名来设置卡片容器的样式,并通过 bodyStyle 属性将内容区域的样式设置为 display: none,完全隐藏内容区域。
下拉时加载图片卡片
需求是展示支持分页的图片,并且希望在下拉时加载新的图片卡片,可以使用 Ant Design 的 List 组件和滚动事件来实现。
容器的高度自适应,并且卡片在宽度不足时自动换行显示,可以使用 CSS 中的 Flexbox 布局来实现
return (
<div style={{ display: 'flex', flexWrap: 'wrap' }} onScroll={handleScroll}>
<List
grid={{ gutter: 16, column: 4 }}
dataSource={data}
loading={loading}
loadMore={loading ? <Spin /> : undefined}
renderItem={(item) => (
<List.Item style={{ flexBasis: '25%' }}>
<Card cover={<img alt="Example Image" src={item} />} />
</List.Item>
)}
/>
</div>
);
我们将容器的样式设置为 display: ‘flex’,并使用 flexWrap: ‘wrap’ 让卡片在宽度不足时自动换行显示。
将卡片项的样式设置为 flexBasis: ‘20%’,这样每个卡片项将占据容器宽度的 20%。通过将 column 属性设置为 5,我们让 List 组件一行显示 5 个卡片。
加载更多
将List组件的 loadMore 属性的值设置为一个函数,用于触发加载更多数据的操作。
将 handleLoadMore 定义为一个函数,用于触发加载更多数据的操作。如果正在加载中,将 handleLoadMore 设置为空函数;否则,将其设置为 fetchData 函数,以触发加载更多数据。
通过这个修改,当滚动到底部时,将触发加载更多数据的操作,并通过模拟异步加载数据的方式展示加载更多的效果。
代码
import { List, Card, Spin } from 'antd';
import { useEffect, useState } from 'react';
const ImageList: React.FC = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<string[]>([]);
const fetchData = () => {
setLoading(true);
// 模拟异步加载数据
setTimeout(() => {
const newData = Array.from({ length: 10 }, (_, index) => `https://via.placeholder.com/300?text=Image${index + 1}`);
setData((prevData) => [...prevData, ...newData]);
setLoading(false);
}, 1000);
};
useEffect(() => {
fetchData();
}, []);
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
const isAtBottom = scrollTop + clientHeight === scrollHeight;
if (isAtBottom && !loading) {
fetchData();
}
};
const handleLoadMore = loading ? () => {} : null;
return (
<div style={{ display: 'flex', flexWrap: 'wrap' }} onScroll={handleScroll}>
<List
grid={{ gutter: 16, column: 5 }}
dataSource={data}
loading={loading}
loadMore={handleLoadMore}
renderItem={(item) => (
<List.Item style={{ flexBasis: '20%' }}>
<Card cover={<img alt="Example Image" src={item} />} />
</List.Item>
)}
/>
</div>
);
};
export default ImageList;
分页的方式加载图片列表【推荐】
使用分页的方式可以更加可控地展示数据,并提供更好的用户体验。通过显示当前页数、总页数以及提供页码切换的功能,用户可以清楚地知道当前所处的页面位置,并且可以自由地在不同页面之间进行切换。
实现思路
我更喜欢使用分页而不是加载更多的方式来展示数据,您可以结合 Ant Design 的 Pagination 组件来实现分页效果
代码demo
import { List, Card, Pagination } from 'antd';
import { useEffect, useState } from 'react';
const ImageList: React.FC = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<string[]>([]);
const [page, setPage] = useState(1);
const pageSize = 12; // 每页显示的图片数量
const fetchData = () => {
setLoading(true);
// 模拟异步加载数据
setTimeout(() => {
const newData = Array.from({ length: pageSize }, (_, index) => `https://via.placeholder.com/300?text=Image${(page - 1) * pageSize + index + 1}`);
setData(newData);
setLoading(false);
}, 1000);
};
useEffect(() => {
fetchData();
}, [page]);
const handlePageChange = (pageNumber: number) => {
setPage(pageNumber);
};
return (
<div>
<List
grid={{ gutter: 16, column: 4 }}
dataSource={data}
loading={loading}
renderItem={(item) => (
<List.Item>
<Card cover={<img alt="Example Image" src={item} />} />
</List.Item>
)}
/>
<Pagination current={page} pageSize={pageSize} total={100} onChange={handlePageChange} />
</div>
);
};
export default ImageList;
缩略图
import { List, Card, Pagination } from 'antd';
import axios from 'axios';
import { useEffect, useState } from 'react';
const ImageList: React.FC = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<string[]>([]);
const [page, setPage] = useState(1);
const pageSize = 12; // 每页显示的图片数量
const fetchData = async () => {
setLoading(true);
try {
let token = localStorage.getItem('token');
if (null === token) {
token = '';
}
const response = await axios.get('/api/v1/imageManage/listThumbImages', {
params: {
page: page,
pageSize: pageSize,
},
headers: {
Authorization: `Bearer ${token}`, // 替换为您的 Bearer Token
},
});
console.log("listThumbImages response data: ", response.data)
//前端只通过 Base64 字符串无法确定图片的格式。Base64 只是一种表示图像数据的编码方式,并不能直接指示图像的格式。
//在前端展示 Base64 图片时,通常需要提供图像的 MIME 类型来指示图像的格式。MIME 类型是一种标识数据类型的字符串,例如 "image/jpeg" 表示 JPEG 图像,"image/png" 表示 PNG 图像。
// 将图像的 MIME 类型一并返回,这样前端就能够根据提供的 MIME 类型来正确解析和显示图像
//根据文件名组装图像的 MIME 类型
const updatedData = response.data.map(item => {
const fileExtension = item.FileName.split('.').pop().toLowerCase();
let imageFormat = "image/jpeg"; // 默认格式为 JPEG
if (fileExtension === "png") {
imageFormat = "image/png";
} else if (fileExtension === "gif") {
imageFormat = "image/gif";
}
return {
...item,
ImageFormat: imageFormat,
};
});
setData(updatedData);
setLoading(false);
} catch (error) {
console.error('Error fetching thumbnails:', error);
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [page]);
const handlePageChange = (pageNumber: number) => {
setPage(pageNumber);
};
return (
<div>
<List
grid={{ gutter: 16, column: 6 }}
dataSource={data}
loading={loading}
renderItem={(item: any) => (
<List.Item>
<Card
cover={
item.ThumbnailBase64 ? (
<img alt="Example Image" src={`data:${item.ImageFormat};base64,${item.ThumbnailBase64}`} />
) : (
<div>No Thumbnail</div>
)
}
/>
</List.Item>
)}
/>
<Pagination current={page} pageSize={pageSize} total={100} onChange={handlePageChange} />
</div>
);
};
export default ImageList;
代码细节解释:
- 每行显示多个?
<List
grid={{ gutter: 16, column: 6 }}
grid 属性设置为 { gutter: 16, column: 6 },表示创建一个具有 6 列的网格布局,并且网格项之间的间距为 16 像素。
在 List 组件中,grid 属性用于定义网格布局的样式。它接受一个对象作为参数,该对象包含两个属性:gutter 和 column。
- gutter:用于设置网格项之间的间距。可以将其设置为一个数字来表示像素值,或者设置为一个数组 [水平间距, 垂直间距] 来分别表示水平和垂直方向上的间距。
- column:用于设置网格的列数。可以将其设置为一个数字来表示列数,或者设置为一个数组 [列数, 列宽] 来分别表示列数和列宽。
- 分页?
如下,添加 分页组件Pagination
即可
为 Card 添加预览、下载和删除功能的按钮
通过以上修改,预览、下载和删除的图标会在鼠标悬停在 Card 上时放大并显示。
在上述 CSS 文件中,我们通过设置 .image-actions 类的样式来实现鼠标悬停时的显示效果,并使用 .custom-card:hover .image-actions 选择器来控制悬停时图标的显示。我们还设置了 .icon 类的样式,用于调整图标的大小和颜色。
实现鼠标悬停时图标变色的效果,你可以使用 CSS 的 :hover 伪类选择器来设置图标的样式
.icon-wrapper {
display: flex;
gap: 10px;
}
.icon-wrapper .icon {
font-size: 24px;
color: #000;
cursor: pointer;
}
.icon-wrapper .icon:hover {
color: #ff0000; /* 设置图标的悬停颜色 */
}
在card组件下发显示图片文件名
Meta官方说明:https://4x.ant.design/components/card-cn/#Card.Meta
要将文件名显示在卡片的底部,您可以在 Card 组件中添加一个位于底部的 Meta 组件,并将文件名作为 Meta 组件的 description 属性。
可以利用 Card.Meta 支持更灵活的内容
卡片操作
官方文档:https://4x.ant.design/components/card-cn/#Card
使用 actions 卡片操作组,位置在卡片底部
分页
分页组件用法:
<Pagination current={page} pageSize={pageSize} total={totalRecords} onChange={handlePageChange} />
分页相关变量
const [page, setPage] = useState(1);
const pageSize = 10; // 每页显示的图片数量
const [totalRecords, setTotalRecords] = useState(0);
在 fetchData 函数中的成功回调中,使用 setPage 和 setTotalRecords来更新 page 和 totalRecords状态。这样就能确保在获取数据后及时更新这些状态。
获取数据后,设置分页信息变量
// 获取分页信息
const pagination = responseData.pagination;
console.log("获取分页信息 pagination: ",pagination)
setPage(pagination.page);
setTotalRecords(pagination.totalItems);
注意:在 Pagination 组件中,total 属性应该是总记录数。前端会根据 total 属性和每页显示的数量自动计算出分页的页数。这样做可以确保在数据更新或动态加载时,分页组件能够正确地显示页码和处理分页逻辑。