137 lines
4.2 KiB
TypeScript
Executable File
137 lines
4.2 KiB
TypeScript
Executable File
import React, { useMemo } from "react";
|
|
import { Image, Button, Row, Col, Tooltip } from "antd";
|
|
import { ResourceDto } from "@nice/common";
|
|
import { env } from "@web/src/env";
|
|
import { getFileIcon } from "./utils";
|
|
import { formatFileSize, getCompressedImageUrl } from "@nice/utils";
|
|
|
|
export default function ResourcesShower({
|
|
resources = [],
|
|
}: {
|
|
resources: ResourceDto[];
|
|
}) {
|
|
const { resources: dealedResources } = useMemo(() => {
|
|
if (!resources) return { resources: [] };
|
|
|
|
const isImage = (url: string) =>
|
|
/\.(png|jpg|jpeg|gif|webp)$/i.test(url);
|
|
|
|
const sortedResources = resources
|
|
.map((resource) => {
|
|
const original = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${resource.url}`;
|
|
const isImg = isImage(resource.url);
|
|
return {
|
|
...resource,
|
|
url: isImg ? getCompressedImageUrl(original) : original,
|
|
originalUrl: original,
|
|
isImage: isImg,
|
|
};
|
|
})
|
|
.sort((a, b) => (a.isImage === b.isImage ? 0 : a.isImage ? -1 : 1));
|
|
|
|
return { resources: sortedResources };
|
|
}, [resources]);
|
|
|
|
const imageResources = dealedResources.filter((res) => res.isImage);
|
|
const fileResources = dealedResources.filter((res) => !res.isImage);
|
|
return (
|
|
<div className="space-y-3">
|
|
{imageResources.length > 0 && (
|
|
<Row gutter={[16, 16]} className="mb-6">
|
|
<Image.PreviewGroup>
|
|
{imageResources.map((resource) => (
|
|
<Col
|
|
key={resource.url}
|
|
xs={12}
|
|
sm={8}
|
|
md={6}
|
|
lg={6}
|
|
xl={4}
|
|
className="relative">
|
|
<div className="relative aspect-square rounded-lg overflow-hidden bg-gray-100">
|
|
<div className="w-full h-full">
|
|
<Image
|
|
src={resource.url}
|
|
alt={resource.title}
|
|
preview={{
|
|
src: resource.originalUrl,
|
|
mask: (
|
|
<div className="flex items-center justify-center text-white">
|
|
点击预览
|
|
</div>
|
|
),
|
|
}}
|
|
style={{
|
|
position: "absolute",
|
|
inset: 0,
|
|
width: "100%",
|
|
height: "100%",
|
|
objectFit: "cover",
|
|
}}
|
|
rootClassName="w-full h-full"
|
|
/>
|
|
</div>
|
|
{resource.title && (
|
|
<div className="absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/60 to-transparent text-white text-sm truncate">
|
|
{resource.title}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Col>
|
|
))}
|
|
</Image.PreviewGroup>
|
|
</Row>
|
|
)}
|
|
<div className=" text-sm px-2">附件:</div>
|
|
{fileResources.length > 0 && (
|
|
<div className="rounded-xl p-1 border border-gray-100 bg-white">
|
|
<div className="flex flex-nowrap overflow-x-auto scrollbar-hide gap-1.5">
|
|
{fileResources.map((resource) => {
|
|
return (
|
|
<a
|
|
key={resource.url}
|
|
className="flex-shrink-0 relative active:scale-95 transition-transform select-none "
|
|
href={resource.originalUrl}
|
|
target="_blank"
|
|
download={true}
|
|
title="点击下载文件">
|
|
{/* 超紧凑卡片容器 */}
|
|
<div className="w-[120px] h-[80px] p-2 flex flex-col items-center justify-between rounded-xl hover:bg-primary-50/40 cursor-pointer">
|
|
{/* 微型文件图标 */}
|
|
<div className="text-primary-600 text-base">
|
|
{getFileIcon(resource.url)}
|
|
</div>
|
|
|
|
{/* 压缩信息展示 */}
|
|
<div className="w-full text-center space-y-0.5">
|
|
<p className="text-xs font-medium text-gray-800 truncate">
|
|
{resource.title?.slice(0, 12) ||
|
|
"未命名"}
|
|
</p>
|
|
<div className="flex items-center justify-between text-xs text-gray-500 ">
|
|
<span className="bg-gray-100 px-0.5 rounded-sm mr-2 whitespace-pre-wrap">
|
|
{resource.url
|
|
.split(".")
|
|
.pop()
|
|
?.slice(0, 4)
|
|
.toUpperCase()}
|
|
</span>
|
|
<span className="flex bg-gray-100 px-0.5 rounded-sm justify-items-center whitespace-pre-wrap">
|
|
{resource.meta.size &&
|
|
formatFileSize(
|
|
resource.meta.size
|
|
)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|