KnowledgeCard.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. 'use client';
  2. import { useState } from 'react';
  3. import { MapPin, ChevronDown, ChevronUp, Calendar } from 'lucide-react';
  4. // 1. 引入 Hook
  5. import { useLanguage } from '@/lib/i18n/LanguageContext';
  6. import { CardData } from '@/types/card';
  7. const getImageUrl = (fidString: string | null) => {
  8. if (!fidString) return null;
  9. const firstFid = fidString.split(' ')[0];
  10. return `/api/resource/download_file?fid=${firstFid}`;
  11. };
  12. export default function KnowledgeCard({ data }: { data: CardData }) {
  13. // 2. 获取翻译函数和当前语言
  14. const { t, lang } = useLanguage();
  15. const [expanded, setExpanded] = useState(false);
  16. const imageUrl = getImageUrl(data.image);
  17. // 3. 根据语言格式化日期
  18. const formattedDate = new Date(data.created_at).toLocaleDateString(
  19. lang === 'zh' ? 'zh-CN' : 'en-US',
  20. { year: 'numeric', month: 'short', day: 'numeric' }
  21. );
  22. return (
  23. <div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden hover:shadow-md transition-all duration-300 flex flex-col">
  24. {/* 图片区域 */}
  25. {imageUrl && (
  26. <div className="h-48 w-full bg-slate-100 relative overflow-hidden group flex-shrink-0">
  27. <img
  28. src={imageUrl}
  29. alt={data.title}
  30. className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
  31. loading="lazy"
  32. />
  33. <div className="absolute top-3 left-3 flex gap-2">
  34. <span className="bg-black/60 text-white text-xs px-2 py-1 rounded backdrop-blur-sm flex items-center gap-1">
  35. <MapPin size={10} /> {data.country}
  36. </span>
  37. {data.label && (
  38. <span className="bg-blue-600/80 text-white text-xs px-2 py-1 rounded backdrop-blur-sm">
  39. {data.label}
  40. </span>
  41. )}
  42. </div>
  43. </div>
  44. )}
  45. <div className="p-5 flex flex-col">
  46. {/* 标题 */}
  47. <h3 className="text-lg font-bold text-slate-900 mb-3 line-clamp-2 leading-tight group-hover:text-blue-600 transition-colors">
  48. {data.title}
  49. </h3>
  50. {/* 内容预览 (HTML) */}
  51. <div
  52. className={`
  53. text-sm text-slate-600 leading-relaxed overflow-hidden transition-all duration-500 ease-in-out
  54. ${expanded ? 'max-h-[1000px]' : 'max-h-[80px] line-clamp-3'}
  55. /* === 样式修复:Tailwind Prose === */
  56. [&_a]:text-blue-600 [&_a]:underline [&_a]:font-medium hover:[&_a]:text-blue-800
  57. [&_ul]:list-disc [&_ul]:pl-5 [&_ul]:my-2
  58. [&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:my-2
  59. [&_p]:mb-2 last:[&_p]:mb-0
  60. [&_img]:max-w-full [&_img]:rounded-lg [&_img]:my-2
  61. `}
  62. dangerouslySetInnerHTML={{ __html: data.content }}
  63. />
  64. {/* 底部信息 & 展开按钮 */}
  65. <div className="mt-4 pt-4 flex items-center justify-between border-t border-slate-50">
  66. <div className="flex items-center text-xs text-slate-400 gap-1">
  67. <Calendar size={12} />
  68. {formattedDate}
  69. </div>
  70. <button
  71. onClick={(e) => {
  72. e.stopPropagation();
  73. setExpanded(!expanded);
  74. }}
  75. className="text-xs font-medium text-blue-600 hover:text-blue-800 flex items-center gap-1 bg-blue-50 px-2 py-1 rounded transition-colors select-none"
  76. >
  77. {expanded ? (
  78. <>{t('knowledge.collapse')} <ChevronUp size={14} /></>
  79. ) : (
  80. <>{t('knowledge.read_more')} <ChevronDown size={14} /></>
  81. )}
  82. </button>
  83. </div>
  84. </div>
  85. </div>
  86. );
  87. }