diff --git a/web/src/views/Plan.vue b/web/src/views/Plan.vue index 5bd2eef..10ce958 100644 --- a/web/src/views/Plan.vue +++ b/web/src/views/Plan.vue @@ -1,70 +1,13 @@ - - diff --git a/web/src/views/plan/plan.css b/web/src/views/plan/plan.css new file mode 100644 index 0000000..c1ecc16 --- /dev/null +++ b/web/src/views/plan/plan.css @@ -0,0 +1,92 @@ +.plan-page { + background-color: #0f1419; +} + +.search-input { + background-color: #1f2937; + border: 1px solid #374151; + border-radius: 8px; + padding: 10px 12px 10px 36px; + color: white; + font-size: 14px; + outline: none; + transition: border-color 0.2s; +} + +.search-input:focus { + border-color: #f97316; +} + +.search-input::placeholder { + color: #6b7280; +} + +.table-row { + border-top: 1px solid #2a2a3a; + transition: background-color 0.2s; +} + +.table-row:hover { + background-color: rgba(255, 255, 255, 0.02); +} + +.task-tag { + background-color: #374151; + color: #d1d5db; + font-size: 11px; + padding: 2px 6px; + border-radius: 4px; +} + +.ml-13 { + margin-left: 3.25rem; +} + +.btn-icon { + padding: 6px; + border-radius: 6px; + transition: all 0.2s; +} + +.btn-icon:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +.empty-box { + min-height: 300px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.empty-icon { + width: 80px; + height: 80px; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #1f2937, #111827); + border-radius: 20px; + margin-bottom: 16px; + color: #6b7280; +} + +.empty-text { + font-size: 16px; + font-weight: 500; + color: white; + margin-bottom: 4px; +} + +.empty-tip { + font-size: 14px; + color: #6b7280; +} + +.line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} diff --git a/web/src/views/plan/usePlan.ts b/web/src/views/plan/usePlan.ts new file mode 100644 index 0000000..fba9fc3 --- /dev/null +++ b/web/src/views/plan/usePlan.ts @@ -0,0 +1,87 @@ +import { ref, computed } from 'vue' + +export interface PlanTask { + id: number + name: string + status: 'running' | 'stopped' + triggerType: string + nextRun: string + lastRun: string + notifyChannel: string + executionCount: number + description: string + tags: string[] +} + +export function usePlan() { + const tasks = ref([ + { + id: 1, + name: 'Human-like Heartbeat', + status: 'running', + triggerType: 'Interval 30 minutes', + nextRun: '2026/03/10 16:26', + lastRun: '2026/03/10 15:56', + notifyChannel: '-', + executionCount: 13, + description: 'Check if proactive messages need to be sent (greetings/reminders/follow-ups)', + tags: ['System Task', 'Agent Task', 'Interval'] + }, + { + id: 2, + name: 'Memory Organization', + status: 'running', + triggerType: 'Interval 3 hours', + nextRun: '2026/03/10 18:35', + lastRun: '2026/03/10 15:35', + notifyChannel: '-', + executionCount: 2, + description: 'Execute memory organization: organize chat history, extract key memories, refresh MEMORY.md', + tags: ['System Task', 'Agent Task', 'Interval'] + }, + { + id: 3, + name: 'System Self-Check', + status: 'running', + triggerType: 'Daily 04:00', + nextRun: '2026/03/11 04:00', + lastRun: 'Never', + notifyChannel: '-', + executionCount: 0, + description: 'Execute system self-check: analyze ERROR logs, try to fix tool issues, generate report', + tags: ['System Task', 'Agent Task', 'Daily'] + }, + ]) + + const filterStatus = ref('all') + const searchQuery = ref('') + + const filteredTasks = computed(() => { + let result = tasks.value + if (filterStatus.value !== 'all') { + result = result.filter(t => t.status === filterStatus.value) + } + if (searchQuery.value) { + const query = searchQuery.value.toLowerCase() + result = result.filter(t => + t.name.toLowerCase().includes(query) || + t.description.toLowerCase().includes(query) + ) + } + return result + }) + + const getTaskCount = (status: string) => { + if (status === 'running') return tasks.value.filter(t => t.status === 'running').length + if (status === 'stopped') return tasks.value.filter(t => t.status === 'stopped').length + return tasks.value.length + } + + return { + tasks, + filterStatus, + searchQuery, + filteredTasks, + getTaskCount, + } +}