refactor: 重构 Plan 页面代码结构

- 抽取 usePlan composable 逻辑
- 分离 plan.css 样式文件
- 简化 Plan.vue 组件代码

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 19:51:29 +08:00
parent c9f19f43fb
commit 20f2ea8c38
3 changed files with 186 additions and 160 deletions

View File

@@ -1,70 +1,13 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { Play, Pause, Edit, Trash2, Plus, Search, Clock } from 'lucide-vue-next'
import { usePlan } from './plan/usePlan'
import './plan/plan.css'
// Mock scheduled tasks data
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') // running, stopped, 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
}
const {
filterStatus,
searchQuery,
filteredTasks,
} = usePlan()
</script>
<template>
@@ -170,99 +113,3 @@ const getTaskCount = (status: string) => {
</div>
</div>
</template>
<style scoped>
.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 {
background: none;
border: none;
cursor: pointer;
padding: 6px;
border-radius: 4px;
transition: background-color 0.2s;
}
.btn-icon:hover {
background-color: rgba(255, 255, 255, 0.1);
}
/* 空状态样式 */
.empty-box {
min-height: 340px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.empty-icon {
width: 100px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #1f2937, #111827);
border-radius: 24px;
margin-bottom: 20px;
}
.empty-icon i {
font-size: 40px;
color: #6b7280;
}
.empty-text {
color: #d1d5db;
font-size: 1.25rem;
font-weight: 500;
margin-bottom: 8px;
}
.empty-tip {
color: #6b7280;
font-size: 0.875rem;
}
</style>

View File

@@ -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;
}

View File

@@ -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<PlanTask[]>([
{
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,
}
}