refactor: 重构 Plan 页面代码结构
- 抽取 usePlan composable 逻辑 - 分离 plan.css 样式文件 - 简化 Plan.vue 组件代码 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
92
web/src/views/plan/plan.css
Normal file
92
web/src/views/plan/plan.css
Normal 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;
|
||||
}
|
||||
87
web/src/views/plan/usePlan.ts
Normal file
87
web/src/views/plan/usePlan.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user