import { LitElement, html, css } from 'lit'; import './help-tooltip.js'; class QueueTable extends LitElement { static properties = { queues: { type: Array }, rootPath: { type: String } }; static styles = css` :host { display: block; } table { width: 100%; border-collapse: collapse; } th { background: #424242; padding: 14px 16px; text-align: left; font-weight: 500; color: #bdbdbd; font-size: 0.9em; border-bottom: 1px solid #616161; } .th-content { display: flex; align-items: center; gap: 4px; } td { padding: 14px 16px; border-bottom: 1px solid #616161; } tr:last-child td { border-bottom: none; } tbody tr:hover { background: #5a5a5a; } .queue-name { font-weight: 500; color: #4fc3f7; cursor: pointer; } .queue-name:hover { text-decoration: underline; } .state-badge { display: inline-block; padding: 4px 10px; border-radius: 4px; font-size: 0.8em; font-weight: 500; background: #66bb6a; color: #1b5e20; } .state-badge.paused { background: #ffb74d; color: #e65100; } .memory-value { font-size: 0.9em; color: #bdbdbd; } .latency-value { color: #bdbdbd; } .action-btn { background: transparent; border: 1px solid #757575; color: #bdbdbd; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 0.8em; margin-right: 4px; } .action-btn:hover { background: #616161; color: #e0e0e0; } .action-btn.pause { border-color: #ffb74d; color: #ffb74d; } .action-btn.pause:hover { background: rgba(255, 183, 77, 0.2); } .action-btn.resume { border-color: #66bb6a; color: #66bb6a; } .action-btn.resume:hover { background: rgba(102, 187, 106, 0.2); } .empty-state { text-align: center; padding: 60px; color: #9e9e9e; } `; constructor() { super(); this.queues = []; this.rootPath = '/monitor'; } handleQueueClick(queue) { this.dispatchEvent(new CustomEvent('queue-click', { detail: { queue: queue.name } })); } async togglePause(queue) { const action = queue.paused ? 'unpause' : 'pause'; try { const response = await fetch( `${this.rootPath}/api/queues/${queue.name}/${action}`, { method: 'POST' } ); if (!response.ok) throw new Error(`HTTP ${response.status}`); // 触发刷新事件 this.dispatchEvent(new CustomEvent('queue-updated')); } catch (err) { console.error(`Failed to ${action} queue:`, err); alert(`Failed to ${action} queue: ${err.message}`); } } formatMemory(bytes) { if (!bytes || bytes === 0) return '0 B'; if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / 1024 / 1024).toFixed(2) + ' MB'; } renderTh(label, tooltip) { if (!tooltip) { return html`
| Queue | ${this.renderTh('State', 'run: 正常处理任务 | paused: 暂停处理新任务')} ${this.renderTh('Active', '正在被 worker 处理的任务数')} ${this.renderTh('Pending', '等待处理的任务数')} ${this.renderTh('Scheduled', '定时/延迟任务,到达指定时间后进入 Pending')} ${this.renderTh('Retry', '处理失败后等待重试的任务数')} ${this.renderTh('Archived', '超过最大重试次数的失败任务')} ${this.renderTh('Memory', '队列在 Redis 中占用的内存')} ${this.renderTh('Latency', '最老 Pending 任务的等待时间,反映处理及时性')}Actions | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| this.handleQueueClick(queue)}> ${queue.name} | ${queue.paused ? 'paused' : 'run'} | ${queue.active || 0} | ${queue.pending || 0} | ${queue.scheduled || 0} | ${queue.retry || 0} | ${queue.archived || 0} | ${this.formatMemory(queue.memory_usage)} | ${queue.latency || 0}ms |