File size: 7,525 Bytes
291850b 339fff1 291850b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
import React, { useState, useMemo } from 'react';
import { ChevronDown, ChevronUp, Download, FileSpreadsheet, TrendingUp, TrendingDown } from 'lucide-react';
import { KLinePoint } from '../types';
interface KLineTextTableProps {
data: KLinePoint[];
birthYear?: number;
className?: string;
}
const KLineTextTable: React.FC<KLineTextTableProps> = ({
data,
birthYear,
className = '',
}) => {
const [expanded, setExpanded] = useState(false);
const currentYear = new Date().getFullYear();
// Calculate display range - default show 10 years around current age
const displayData = useMemo(() => {
if (!data || data.length === 0) return [];
if (expanded) {
return data;
}
// Find current year index
const currentYearIndex = data.findIndex(d => d.year === currentYear);
if (currentYearIndex === -1) {
// If current year not found, show first 10 items
return data.slice(0, 10);
}
// Show 5 years before and 5 years after current year
const startIndex = Math.max(0, currentYearIndex - 5);
const endIndex = Math.min(data.length, currentYearIndex + 5);
return data.slice(startIndex, endIndex);
}, [data, expanded, currentYear]);
// Export to Excel (CSV format)
const handleExportExcel = () => {
if (!data || data.length === 0) return;
// Create CSV content with BOM for Excel compatibility
const BOM = '\uFEFF';
const headers = ['年龄', '年份', '干支', '大运', '评分', '趋势', '运势分析'];
const rows = data.map(item => {
const trend = item.close >= item.open ? '上涨' : '下跌';
// Escape quotes and handle line breaks in reason
const reason = (item.reason || '').replace(/"/g, '""').replace(/\n/g, ' ');
return [
`${item.age}岁`,
item.year,
item.ganZhi || '',
item.daYun || '',
item.score,
trend,
`"${reason}"`,
].join(',');
});
const csvContent = BOM + headers.join(',') + '\n' + rows.join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `比迹_流年详批_${new Date().getTime()}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
if (!data || data.length === 0) {
return null;
}
return (
<div className={`bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden ${className}`}>
{/* Header */}
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between bg-gradient-to-r from-gray-50 to-white">
<div className="flex items-center gap-2">
<div className="w-1 h-6 bg-indigo-600 rounded-full"></div>
<h3 className="text-lg font-bold text-gray-800 font-serif-sc">流年运势详批</h3>
<span className="text-sm text-gray-500">
({expanded ? `全部 ${data.length} 年` : `近10年`})
</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={handleExportExcel}
className="flex items-center gap-1.5 px-3 py-1.5 text-sm text-emerald-600 bg-emerald-50 hover:bg-emerald-100 rounded-lg transition-colors"
>
<FileSpreadsheet className="w-4 h-4" />
导出Excel
</button>
</div>
</div>
{/* Table */}
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="bg-gray-50 text-gray-600 font-medium">
<th className="px-4 py-3 text-center whitespace-nowrap border-r border-gray-100 w-16">年龄</th>
<th className="px-4 py-3 text-center whitespace-nowrap border-r border-gray-100 w-32">年份/干支</th>
<th className="px-4 py-3 text-center whitespace-nowrap border-r border-gray-100 w-16">大运</th>
<th className="px-4 py-3 text-center whitespace-nowrap border-r border-gray-100 w-20">评分</th>
<th className="px-4 py-3 text-left">运势分析</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100">
{displayData.map((item, index) => {
const isCurrentYear = item.year === currentYear;
const isUpTrend = item.close >= item.open;
const scoreColor = isUpTrend ? 'text-green-600' : 'text-red-600';
const scoreBg = isUpTrend ? 'bg-green-50' : 'bg-red-50';
return (
<tr
key={item.year}
className={`hover:bg-gray-50 transition-colors ${isCurrentYear ? 'bg-amber-50/50' : ''}`}
>
{/* Age */}
<td className="px-4 py-3 text-center border-r border-gray-100">
<span className="font-mono font-medium text-gray-700">
{item.age}<span className="text-gray-400 text-xs">岁</span>
</span>
</td>
{/* Year + GanZhi */}
<td className="px-4 py-3 text-center border-r border-gray-100">
<div className="flex flex-col items-center">
<span className="font-bold text-gray-800">{item.year}</span>
<span className="text-indigo-600 font-medium">{item.ganZhi || '-'}</span>
</div>
</td>
{/* DaYun */}
<td className="px-4 py-3 text-center border-r border-gray-100">
<span className="text-purple-600 font-medium">{item.daYun || '-'}</span>
</td>
{/* Score */}
<td className="px-4 py-3 text-center border-r border-gray-100">
<div className={`inline-flex items-center gap-1 px-2 py-1 rounded-lg ${scoreBg}`}>
<span className={`font-bold ${scoreColor}`}>{item.score}</span>
{isUpTrend ? (
<TrendingUp className={`w-3.5 h-3.5 ${scoreColor}`} />
) : (
<TrendingDown className={`w-3.5 h-3.5 ${scoreColor}`} />
)}
</div>
</td>
{/* Reason */}
<td className="px-4 py-3">
<p className="text-gray-700 leading-relaxed text-justify">
{item.reason || '暂无详细分析'}
</p>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
{/* Expand/Collapse Button */}
<div className="px-6 py-3 border-t border-gray-100 bg-gray-50">
<button
onClick={() => setExpanded(!expanded)}
className="w-full flex items-center justify-center gap-2 py-2 text-sm font-medium text-indigo-600 hover:text-indigo-700 hover:bg-indigo-50 rounded-lg transition-colors"
>
{expanded ? (
<>
<ChevronUp className="w-4 h-4" />
收起,仅显示近10年
</>
) : (
<>
<ChevronDown className="w-4 h-4" />
展开查看全部 {data.length} 年运势
</>
)}
</button>
</div>
</div>
);
};
export default KLineTextTable;
|