118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
import { useState } from 'react';
|
||
import { useNavigate } from '@remix-run/react';
|
||
|
||
interface Template {
|
||
id: string;
|
||
title: string;
|
||
type: string;
|
||
description: string;
|
||
updateTime: string;
|
||
useCount: number;
|
||
rating: number;
|
||
category: string;
|
||
}
|
||
|
||
interface TemplateCardProps {
|
||
template: Template;
|
||
onClick: () => void;
|
||
}
|
||
|
||
export function TemplateCard({ template, onClick }: TemplateCardProps) {
|
||
const [isFavorited, setIsFavorited] = useState(false);
|
||
const navigate = useNavigate();
|
||
|
||
const handleFavoriteClick = (e: React.MouseEvent) => {
|
||
e.stopPropagation();
|
||
setIsFavorited(!isFavorited);
|
||
};
|
||
|
||
const handleActionClick = (e: React.MouseEvent, action: string) => {
|
||
e.stopPropagation();
|
||
|
||
switch (action) {
|
||
case '立即使用':
|
||
console.log('下载并使用模板:', template.id);
|
||
// 这里应该触发下载逻辑
|
||
break;
|
||
case '预览':
|
||
// 导航到模板详情页面
|
||
navigate(`/contract-template/detail/${template.id}`);
|
||
break;
|
||
default:
|
||
console.log(`执行操作: ${action}`, template.id);
|
||
}
|
||
};
|
||
|
||
const renderStars = (rating: number) => {
|
||
const stars = [];
|
||
const fullStars = Math.floor(rating);
|
||
const hasHalfStar = rating % 1 !== 0;
|
||
|
||
for (let i = 0; i < 5; i++) {
|
||
if (i < fullStars) {
|
||
stars.push(<i key={i} className="ri-star-fill text-xs"></i>);
|
||
} else if (i === fullStars && hasHalfStar) {
|
||
stars.push(<i key={i} className="ri-star-half-fill text-xs"></i>);
|
||
} else {
|
||
stars.push(<i key={i} className="ri-star-line text-xs"></i>);
|
||
}
|
||
}
|
||
return stars;
|
||
};
|
||
|
||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||
if (e.key === 'Enter' || e.key === ' ') {
|
||
e.preventDefault();
|
||
onClick();
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div
|
||
className="template-card"
|
||
onClick={onClick}
|
||
onKeyDown={handleKeyDown}
|
||
role="button"
|
||
tabIndex={0}
|
||
aria-label={`查看${template.title}详情`}
|
||
>
|
||
<div className="template-header">
|
||
<div className="template-type">{template.type}</div>
|
||
<div className="flex items-center gap-1 text-yellow-500">
|
||
{renderStars(template.rating)}
|
||
<span className="text-xs ml-1">{template.rating}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 className="template-title">{template.title}</h3>
|
||
<p className="template-desc">{template.description}</p>
|
||
|
||
<div className="template-meta">
|
||
<span>更新时间:{template.updateTime}</span>
|
||
<span>使用次数:{template.useCount.toLocaleString()}</span>
|
||
</div>
|
||
|
||
<div className="template-actions mt-3">
|
||
<button
|
||
className="action-btn primary"
|
||
onClick={(e) => handleActionClick(e, '立即使用')}
|
||
>
|
||
立即使用
|
||
</button>
|
||
<button
|
||
className="action-btn"
|
||
onClick={(e) => handleActionClick(e, '预览')}
|
||
>
|
||
预览
|
||
</button>
|
||
<button
|
||
className="action-btn"
|
||
onClick={handleFavoriteClick}
|
||
title={isFavorited ? '取消收藏' : '收藏'}
|
||
>
|
||
<i className={isFavorited ? 'ri-star-fill text-yellow-500' : 'ri-star-line'}></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|