Files
QRGen/gui/src-frontend/src/components/HistoryList.tsx
T
Serendipity c3956f0f36 chore: 前端工程化 + Git hooks + 对齐 PathEditor 规范
- 新增 .gitattributes(CRLF 统一)+ rust-toolchain.toml
- 新增 Prettier + ESLint + markdownlint 配置
- 新增 Husky Git hooks(pre-commit lint-staged + commit-msg commitlint)
- 新增 vitest 前端测试(12 tests, utils/qrText.ts)
- 新增 @ 路径别名(vite + tsconfig)
- 新增 ROADMAP / SUPPORT / CODEOWNERS / FUNDING / dependabot
- 更新 .gitignore + .editorconfig
- 更新 package.json(新增 lint/format/test 脚本)
- 全项目 prettier 格式化 + eslint 通过
- 更新 CLAUDE.md + README.md
2026-06-19 19:42:13 +08:00

84 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useQrState } from '../store/qrContext';
import { MODE_LABELS, type HistoryEntry } from '../types';
import { persistHistory } from '../hooks/useQrEncode';
export default function HistoryList() {
const { state, dispatch } = useQrState();
const handleClick = (entry: HistoryEntry) => {
dispatch({ type: 'SET_MODE', payload: entry.mode });
// 优先使用存储的 formData 恢复表单,否则回退到纯文本
if (entry.formData) {
dispatch({ type: 'SET_FORM_DATA', payload: entry.formData });
} else {
dispatch({ type: 'SET_FORM_DATA', payload: { text: entry.content } });
}
};
const handleDelete = (e: React.MouseEvent, id: string) => {
e.stopPropagation();
const updated = state.history.filter((h) => h.id !== id);
dispatch({ type: 'SET_HISTORY', payload: updated });
persistHistory(updated);
};
const handleClear = () => {
dispatch({ type: 'SET_HISTORY', payload: [] });
persistHistory([]);
};
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="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] || 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>
);
}