Spaces:
Runtime error
Runtime error
| import React, { useState, useEffect } from 'react'; | |
| import { Workflow, HistoryItem } from '../types/types'; | |
| interface HistoryPanelProps { | |
| onSelectWorkflow: (workflow: Workflow) => void; | |
| currentWorkflow: Workflow | null; | |
| } | |
| // 本地存储键 | |
| const HISTORY_STORAGE_KEY = 'workflow_history'; | |
| // 最大历史记录数 | |
| const MAX_HISTORY_ITEMS = 10; | |
| const HistoryPanel: React.FC<HistoryPanelProps> = ({ | |
| onSelectWorkflow, | |
| currentWorkflow, | |
| }) => { | |
| const [historyItems, setHistoryItems] = useState<HistoryItem[]>([]); | |
| const [expanded, setExpanded] = useState<boolean>(true); | |
| // 加载历史记录 | |
| useEffect(() => { | |
| const loadHistory = () => { | |
| try { | |
| const savedHistory = localStorage.getItem(HISTORY_STORAGE_KEY); | |
| if (savedHistory) { | |
| const parsedHistory = JSON.parse(savedHistory) as HistoryItem[]; | |
| setHistoryItems(parsedHistory); | |
| } | |
| } catch (error) { | |
| console.error('加载历史记录失败:', error); | |
| } | |
| }; | |
| loadHistory(); | |
| }, []); | |
| // 保存工作流到历史记录 | |
| const saveToHistory = (workflow: Workflow, result?: any) => { | |
| try { | |
| // 创建新的历史项 | |
| const newItem: HistoryItem = { | |
| id: `hist_${Date.now()}`, | |
| workflow, | |
| result, | |
| timestamp: new Date().toISOString(), | |
| }; | |
| // 更新state | |
| const updatedHistory = [newItem, ...historyItems] | |
| .slice(0, MAX_HISTORY_ITEMS); | |
| setHistoryItems(updatedHistory); | |
| // 保存到localStorage | |
| localStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(updatedHistory)); | |
| } catch (error) { | |
| console.error('保存历史记录失败:', error); | |
| } | |
| }; | |
| // 清空历史记录 | |
| const clearHistory = () => { | |
| setHistoryItems([]); | |
| localStorage.removeItem(HISTORY_STORAGE_KEY); | |
| }; | |
| // 选择历史项 | |
| const handleSelectHistoryItem = (item: HistoryItem) => { | |
| onSelectWorkflow(item.workflow); | |
| }; | |
| // 删除单个历史项 | |
| const handleDeleteHistoryItem = (itemId: string, e: React.MouseEvent) => { | |
| e.stopPropagation(); // 防止点击传播到父元素 | |
| const updatedHistory = historyItems.filter(item => item.id !== itemId); | |
| setHistoryItems(updatedHistory); | |
| localStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(updatedHistory)); | |
| }; | |
| // 格式化时间戳 | |
| const formatTimestamp = (timestamp: string) => { | |
| try { | |
| const date = new Date(timestamp); | |
| // 如果是今天,只显示时间 | |
| const now = new Date(); | |
| const isToday = | |
| date.getDate() === now.getDate() && | |
| date.getMonth() === now.getMonth() && | |
| date.getFullYear() === now.getFullYear(); | |
| if (isToday) { | |
| return date.toLocaleTimeString(); | |
| } | |
| // 否则显示日期和时间 | |
| return date.toLocaleString(); | |
| } catch (error) { | |
| return timestamp; | |
| } | |
| }; | |
| // 切换展开/折叠 | |
| const toggleExpanded = () => { | |
| setExpanded(!expanded); | |
| }; | |
| return ( | |
| <div className="history-panel"> | |
| <div className="history-header" onClick={toggleExpanded}> | |
| <h3>历史记录</h3> | |
| <span className={`expand-icon ${expanded ? 'expanded' : ''}`}> | |
| {expanded ? '▼' : '▶'} | |
| </span> | |
| </div> | |
| {expanded && ( | |
| <> | |
| {historyItems.length > 0 ? ( | |
| <div className="history-list"> | |
| {historyItems.map((item) => ( | |
| <div | |
| key={item.id} | |
| className={`history-item ${currentWorkflow?.name === item.workflow.name ? 'active' : ''}`} | |
| onClick={() => handleSelectHistoryItem(item)} | |
| > | |
| <div className="history-item-content"> | |
| <div className="history-item-name"> | |
| {item.workflow.name || '未命名工作流'} | |
| </div> | |
| <div className="history-item-timestamp"> | |
| {formatTimestamp(item.timestamp)} | |
| </div> | |
| </div> | |
| <div className="history-item-stats"> | |
| <span>{item.workflow.nodes.length} 节点</span> | |
| <span>{item.workflow.edges.length} 边</span> | |
| </div> | |
| <div className="history-item-actions"> | |
| <button | |
| className="history-item-delete" | |
| onClick={(e) => handleDeleteHistoryItem(item.id, e)} | |
| title="删除" | |
| > | |
| ✕ | |
| </button> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ) : ( | |
| <div className="empty-history"> | |
| <p>没有历史记录</p> | |
| <small>发送工作流后将在这里显示</small> | |
| </div> | |
| )} | |
| {historyItems.length > 0 && ( | |
| <div className="history-actions"> | |
| <button | |
| className="clear-history" | |
| onClick={clearHistory} | |
| > | |
| 清空历史记录 | |
| </button> | |
| </div> | |
| )} | |
| </> | |
| )} | |
| <style jsx>{` | |
| .history-panel { | |
| background-color: #f8f9fa; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); | |
| overflow: hidden; | |
| } | |
| .history-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 15px; | |
| background-color: #f1f3f5; | |
| cursor: pointer; | |
| user-select: none; | |
| } | |
| .history-header h3 { | |
| margin: 0; | |
| font-size: 18px; | |
| } | |
| .expand-icon { | |
| font-size: 12px; | |
| transition: transform 0.2s; | |
| } | |
| .expand-icon.expanded { | |
| transform: rotate(0deg); | |
| } | |
| .history-list { | |
| max-height: 300px; | |
| overflow-y: auto; | |
| } | |
| .history-item { | |
| padding: 12px 15px; | |
| border-bottom: 1px solid #e9ecef; | |
| cursor: pointer; | |
| transition: background-color 0.2s; | |
| position: relative; | |
| } | |
| .history-item:hover { | |
| background-color: #e9ecef; | |
| } | |
| .history-item.active { | |
| background-color: rgba(0, 123, 255, 0.1); | |
| border-left: 3px solid #007bff; | |
| } | |
| .history-item-content { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 5px; | |
| } | |
| .history-item-name { | |
| font-weight: 500; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| max-width: 200px; | |
| } | |
| .history-item-timestamp { | |
| font-size: 12px; | |
| color: #6c757d; | |
| } | |
| .history-item-stats { | |
| display: flex; | |
| gap: 10px; | |
| font-size: 12px; | |
| color: #6c757d; | |
| } | |
| .history-item-actions { | |
| position: absolute; | |
| top: 50%; | |
| right: 10px; | |
| transform: translateY(-50%); | |
| opacity: 0; | |
| transition: opacity 0.2s; | |
| } | |
| .history-item:hover .history-item-actions { | |
| opacity: 1; | |
| } | |
| .history-item-delete { | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 50%; | |
| background-color: #f8d7da; | |
| color: #dc3545; | |
| border: none; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 12px; | |
| transition: all 0.2s; | |
| } | |
| .history-item-delete:hover { | |
| background-color: #dc3545; | |
| color: white; | |
| } | |
| .empty-history { | |
| padding: 20px; | |
| text-align: center; | |
| color: #6c757d; | |
| } | |
| .empty-history p { | |
| margin: 0 0 5px; | |
| } | |
| .empty-history small { | |
| font-size: 12px; | |
| } | |
| .history-actions { | |
| padding: 10px 15px; | |
| display: flex; | |
| justify-content: center; | |
| border-top: 1px solid #e9ecef; | |
| } | |
| .clear-history { | |
| padding: 6px 12px; | |
| background-color: #f8d7da; | |
| color: #dc3545; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 12px; | |
| transition: all 0.2s; | |
| } | |
| .clear-history:hover { | |
| background-color: #dc3545; | |
| color: white; | |
| } | |
| `}</style> | |
| </div> | |
| ); | |
| }; | |
| // 添加保存工作流方法到组件上 | |
| HistoryPanel.saveToHistory = (workflow: Workflow, result?: any) => { | |
| try { | |
| // 获取当前历史记录 | |
| const savedHistory = localStorage.getItem(HISTORY_STORAGE_KEY); | |
| let history: HistoryItem[] = []; | |
| if (savedHistory) { | |
| history = JSON.parse(savedHistory); | |
| } | |
| // 创建新的历史项 | |
| const newItem: HistoryItem = { | |
| id: `hist_${Date.now()}`, | |
| workflow, | |
| result, | |
| timestamp: new Date().toISOString(), | |
| }; | |
| // 更新历史记录 | |
| const updatedHistory = [newItem, ...history].slice(0, MAX_HISTORY_ITEMS); | |
| // 保存到localStorage | |
| localStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(updatedHistory)); | |
| return true; | |
| } catch (error) { | |
| console.error('保存历史记录失败:', error); | |
| return false; | |
| } | |
| }; | |
| export default HistoryPanel; | |