diff --git a/frontend/src/views/scheduler/SchedulerLogPanel.vue b/frontend/src/views/scheduler/SchedulerLogPanel.vue index db2e22b..2c1d6ef 100644 --- a/frontend/src/views/scheduler/SchedulerLogPanel.vue +++ b/frontend/src/views/scheduler/SchedulerLogPanel.vue @@ -1,89 +1,101 @@ \ No newline at end of file diff --git a/frontend/src/views/scheduler/SchedulerQueuePanel.vue b/frontend/src/views/scheduler/SchedulerQueuePanel.vue index 9a44dd6..d8340d4 100644 --- a/frontend/src/views/scheduler/SchedulerQueuePanel.vue +++ b/frontend/src/views/scheduler/SchedulerQueuePanel.vue @@ -11,25 +11,30 @@
-
- + - \ No newline at end of file diff --git a/frontend/src/views/scheduler/TaskOverviewPanel.vue b/frontend/src/views/scheduler/TaskOverviewPanel.vue new file mode 100644 index 0000000..e30dd88 --- /dev/null +++ b/frontend/src/views/scheduler/TaskOverviewPanel.vue @@ -0,0 +1,230 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/scheduler/index.vue b/frontend/src/views/scheduler/index.vue index 4279004..fbae625 100644 --- a/frontend/src/views/scheduler/index.vue +++ b/frontend/src/views/scheduler/index.vue @@ -53,7 +53,7 @@ -
+
- - - - +
+ - - - - - - - - - +
+
- - +
+
@@ -165,8 +149,8 @@ import { } from './schedulerConstants' import { useSchedulerLogic } from './useSchedulerLogic' import SchedulerTaskControl from './SchedulerTaskControl.vue' -import SchedulerQueuePanel from './SchedulerQueuePanel.vue' import SchedulerLogPanel from './SchedulerLogPanel.vue' +import TaskOverviewPanel from './TaskOverviewPanel.vue' // 使用业务逻辑层 const { @@ -219,10 +203,6 @@ const onSchedulerTabEdit = (targetKey: string | MouseEvent, action: 'add' | 'rem } } -// 清空指定标签页的日志 -const clearTabLogs = (tab: SchedulerTab) => { - tab.logs.splice(0) -} // 生命周期 onMounted(() => { @@ -241,7 +221,6 @@ onUnmounted(() => { display: flex; flex-direction: column; overflow: hidden; - padding: 16px; background-color: var(--ant-color-bg-layout); } @@ -300,6 +279,55 @@ onUnmounted(() => { background-color: var(--ant-color-bg-container); } +/* 任务卡片统一容器 */ +.task-unified-card { + background-color: transparent; + box-shadow: none; + height: calc(100vh - 230px); /* 动态计算高度 */ + display: flex; + flex-direction: column; + overflow: hidden; +} + +/* 根据状态变化的样式 */ +.task-unified-card.status-新建 { + background-color: transparent; +} + +.task-unified-card.status-运行 { + background-color: transparent; +} + +.task-unified-card.status-结束 { + background-color: transparent; +} + +/* 状态容器 */ +.status-container { + display: flex; + flex: 1; + overflow: hidden; + gap: 16px; + padding: 0; + margin: 0; +} + +/* 任务总览面板容器 */ +.overview-panel-container { + flex: 0 0 33.333333%; /* 占据1/3宽度 */ + display: flex; + flex-direction: column; + overflow: hidden; +} + +/* 日志面板容器 */ +.log-panel-container { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; +} + /* 响应式 - 移动端适配 */ @media (max-width: 768px) { .scheduler-main { @@ -320,5 +348,15 @@ onUnmounted(() => { .power-label { display: none; } + + .status-container { + flex-direction: column; + } + + .overview-panel-container, + .log-panel-container { + flex: 1; + width: 100%; + } } \ No newline at end of file diff --git a/frontend/src/views/scheduler/useSchedulerLogic.ts b/frontend/src/views/scheduler/useSchedulerLogic.ts index 59c3fee..ab3b711 100644 --- a/frontend/src/views/scheduler/useSchedulerLogic.ts +++ b/frontend/src/views/scheduler/useSchedulerLogic.ts @@ -5,6 +5,7 @@ import { TaskCreateIn } from '@/api/models/TaskCreateIn' import { PowerIn } from '@/api/models/PowerIn' import { useWebSocket } from '@/composables/useWebSocket' import type { ComboBoxItem } from '@/api/models/ComboBoxItem' +import type { QueueItem } from './schedulerConstants' import { getPowerActionText, LOG_MAX_LENGTH, @@ -333,13 +334,41 @@ export function useSchedulerLogic() { } const handleUpdateMessage = (tab: SchedulerTab, data: any) => { + // 处理task_dict初始化消息 + if (data.task_dict && Array.isArray(data.task_dict)) { + // 初始化任务队列 + const newTaskQueue = data.task_dict.map((item: any) => ({ + name: item.name || '未知任务', + status: '等待', + })); + + // 初始化用户队列(仅包含运行状态下的用户) + const newUserQueue: QueueItem[] = []; + data.task_dict.forEach((taskItem: any) => { + if (taskItem.user_list && Array.isArray(taskItem.user_list)) { + taskItem.user_list.forEach((user: any) => { + // 只有在用户状态为运行时才添加到用户队列中 + if (user.status === '运行') { + newUserQueue.push({ + name: `${taskItem.name}-${user.name}`, + status: user.status, + }); + } + }); + } + }); + + tab.taskQueue.splice(0, tab.taskQueue.length, ...newTaskQueue); + tab.userQueue.splice(0, tab.userQueue.length, ...newUserQueue); + } + // 更新任务队列 if (data.task_list && Array.isArray(data.task_list)) { const newTaskQueue = data.task_list.map((item: any) => ({ name: item.name || '未知任务', status: item.status || '未知', - })) - tab.taskQueue.splice(0, tab.taskQueue.length, ...newTaskQueue) + })); + tab.taskQueue.splice(0, tab.taskQueue.length, ...newTaskQueue); } // 更新用户队列 @@ -347,19 +376,20 @@ export function useSchedulerLogic() { const newUserQueue = data.user_list.map((item: any) => ({ name: item.name || '未知用户', status: item.status || '未知', - })) - tab.userQueue.splice(0, tab.userQueue.length, ...newUserQueue) + })); + tab.userQueue.splice(0, tab.userQueue.length, ...newUserQueue); } - // 处理日志 + // 处理日志 - 直接显示完整日志内容,覆盖上次显示的内容 if (data.log) { if (typeof data.log === 'string') { - addLog(tab, data.log, 'info') + // 直接替换日志内容,不添加时间戳,不保留历史记录 + tab.lastLogContent = data.log; } else if (typeof data.log === 'object') { - if (data.log.Error) addLog(tab, data.log.Error, 'error') - else if (data.log.Warning) addLog(tab, data.log.Warning, 'warning') - else if (data.log.Info) addLog(tab, data.log.Info, 'info') - else addLog(tab, JSON.stringify(data.log), 'info') + if (data.log.Error) tab.lastLogContent = data.log.Error; + else if (data.log.Warning) tab.lastLogContent = data.log.Warning; + else if (data.log.Info) tab.lastLogContent = data.log.Info; + else tab.lastLogContent = JSON.stringify(data.log); } } saveTabsToStorage(schedulerTabs.value) @@ -409,33 +439,6 @@ export function useSchedulerLogic() { } } - // 日志管理 - const addLog = (tab: SchedulerTab, message: string, type: LogEntry['type'] = 'info') => { - const logEntry: LogEntry = { - time: new Date().toLocaleTimeString(), - message, - type, - timestamp: Date.now(), - } - - tab.logs.push(logEntry) - - // 限制日志条数 - if (tab.logs.length > LOG_MAX_LENGTH) { - tab.logs.splice(0, tab.logs.length - LOG_MAX_LENGTH) - } - - // 自动滚动到底部 - if (tab.isLogAtBottom) { - nextTick(() => { - const el = logRefs.value.get(tab.key) - if (el) { - el.scrollTop = el.scrollHeight - } - }) - } - } - const onLogScroll = (tab: SchedulerTab) => { const el = logRefs.value.get(tab.key) if (!el) return @@ -589,7 +592,6 @@ export function useSchedulerLogic() { stopTask, // 日志操作 - addLog, onLogScroll, setLogRef, diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e8efc5c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,331 @@ +{ + "name": "AUTO-MAS", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "vue-draggable-next": "^2.3.0", + "vuedraggable": "^4.1.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT", + "peer": true + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", + "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", + "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", + "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/runtime-core": "3.5.21", + "@vue/shared": "3.5.21", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", + "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "vue": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "license": "MIT", + "peer": true + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT", + "peer": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT", + "peer": true + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC", + "peer": true + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/sortablejs": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", + "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", + "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-sfc": "3.5.21", + "@vue/runtime-dom": "3.5.21", + "@vue/server-renderer": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-draggable-next": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vue-draggable-next/-/vue-draggable-next-2.3.0.tgz", + "integrity": "sha512-ymbY0UIwfSdg0iDN/iyNNwUrTqZ/6KbPryzsvTNXBLuDCuOBdNijSK8yynNtmiSj6RapTPQfjLGQdJrZkzBd2w==", + "license": "MIT", + "peerDependencies": { + "sortablejs": "^1.14.0", + "vue": "^3.5.17" + } + }, + "node_modules/vuedraggable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", + "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==", + "license": "MIT", + "dependencies": { + "sortablejs": "1.14.0" + }, + "peerDependencies": { + "vue": "^3.0.1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b4734aa --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "vue-draggable-next": "^2.3.0", + "vuedraggable": "^4.1.0" + } +}