feat: 历史记录面板(回填/删除/清空)
This commit is contained in:
@@ -1,8 +1,73 @@
|
||||
import { useQrState } from '../store/qrContext';
|
||||
import { MODE_LABELS, type HistoryEntry } from '../types';
|
||||
|
||||
export default function HistoryList() {
|
||||
const { state, dispatch } = useQrState();
|
||||
|
||||
const handleClick = (entry: HistoryEntry) => {
|
||||
dispatch({ type: 'SET_MODE', payload: entry.mode as any });
|
||||
try {
|
||||
const formData = JSON.parse(entry.content);
|
||||
dispatch({ type: 'SET_FORM_DATA', payload: formData });
|
||||
} catch {
|
||||
dispatch({ type: 'SET_FORM_DATA', payload: { text: entry.content } });
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (e: React.MouseEvent, id: string) => {
|
||||
e.stopPropagation();
|
||||
dispatch({ type: 'REMOVE_HISTORY', payload: id });
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
dispatch({ type: 'SET_HISTORY', payload: [] });
|
||||
};
|
||||
|
||||
const formatTime = (ts: number) => {
|
||||
const d = new Date(ts);
|
||||
return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2">📋 历史记录</div>
|
||||
<p className="text-xs text-gray-400 text-center py-4">暂无记录</p>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs font-semibold text-gray-400 uppercase tracking-wider">
|
||||
📋 历史记录
|
||||
</span>
|
||||
{state.history.length > 0 && (
|
||||
<button onClick={handleClear}
|
||||
className="text-xs text-red-400 hover:text-red-600 transition-colors">
|
||||
清空
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto space-y-1">
|
||||
{state.history.length === 0 && (
|
||||
<p className="text-xs text-gray-400 text-center py-4">暂无记录</p>
|
||||
)}
|
||||
{state.history.map(entry => (
|
||||
<div key={entry.id}
|
||||
onClick={() => handleClick(entry)}
|
||||
className="group flex items-center justify-between px-2 py-1.5 rounded-lg text-xs cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition-all">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="px-1 py-0.5 rounded text-[10px] font-medium bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400">
|
||||
{MODE_LABELS[entry.mode as keyof typeof MODE_LABELS] || entry.mode}
|
||||
</span>
|
||||
<span className="text-gray-400">{formatTime(entry.timestamp)}</span>
|
||||
</div>
|
||||
<span className="text-gray-500 dark:text-gray-400 truncate block mt-0.5">
|
||||
{entry.content.length > 20 ? entry.content.slice(0, 20) + '...' : entry.content}
|
||||
</span>
|
||||
</div>
|
||||
<button onClick={(e) => handleDelete(e, entry.id)}
|
||||
className="opacity-0 group-hover:opacity-100 text-red-400 hover:text-red-600 ml-1 transition-all text-lg leading-none">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user