新增: 工具工作流补全(diff_files工具 + 技能清单注入 + 工作流进度共享 + human端到端测试 + 知识库MCP工具)
This commit is contained in:
@@ -310,6 +310,22 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- B-41 工作流实时进度:最近一次执行的 DAG + 节点状态高亮(共享 workflow store 派生) -->
|
||||
<section v-if="hasWfProgress" class="panel wf-progress-panel">
|
||||
<div class="panel-header">
|
||||
<h2>{{ $t('projectDetail.workflowProgressTitle') }}</h2>
|
||||
</div>
|
||||
<div class="wf-progress">
|
||||
<span v-if="wfProgress.runningNode">{{ $t('taskDetail.workflowStepRunning', { node: wfProgress.runningNode }) }}</span>
|
||||
<span v-else-if="wfProgress.doneCount > 0" class="wf-progress-count">
|
||||
{{ $t('taskDetail.workflowStepsProgress', { done: wfProgress.doneCount, total: wfProgress.totalNodes }) }}
|
||||
</span>
|
||||
<span v-if="wfProgress.result === 'completed'" class="wf-progress-hint">{{ $t('taskDetail.workflowCompletedHint') }}</span>
|
||||
<span v-if="wfProgress.result === 'failed'" class="wf-progress-hint wf-progress-hint-fail">{{ $t('taskDetail.workflowFailedHint') }}</span>
|
||||
</div>
|
||||
<WorkflowDagDisplay v-if="wfDagJson" :dag-json="wfDagJson" :node-statuses="wfProgress.nodeStatuses" />
|
||||
</section>
|
||||
|
||||
<!-- 工作流日志:无记录时折叠为摘要(标题 + 计数),有记录才展开列表 -->
|
||||
<section v-if="formattedEvents.length > 0 || !workflowLogCollapsed" class="panel workflow-log-panel">
|
||||
<div class="panel-header">
|
||||
@@ -396,7 +412,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { useProjectStore } from '@/stores/project'
|
||||
import { projectApi, taskApi } from '@/api'
|
||||
import { projectApi, taskApi, workflowApi } from '@/api'
|
||||
import { moduleApi, type ProjectModuleRecord } from '@/api/module'
|
||||
import { formatDate } from '@/utils/time'
|
||||
import { parseStack } from '@/utils/project'
|
||||
@@ -406,6 +422,7 @@ import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import ApprovalDialog from '@/components/project/ApprovalDialog.vue'
|
||||
import FileExplorer from '@/components/project/FileExplorer.vue'
|
||||
import DependencyGraph from '@/components/project/DependencyGraph.vue'
|
||||
import WorkflowDagDisplay from '@/components/workflow/WorkflowDagDisplay.vue'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import { useRendered } from '@/composables/useMarkdown'
|
||||
import type { ProjectId, TaskRecord } from '@/api/types'
|
||||
@@ -416,7 +433,6 @@ const store = useProjectStore()
|
||||
const { t, locale } = useI18n()
|
||||
const logListRef = ref<HTMLElement | null>(null)
|
||||
const showApprovalDialog = ref(false)
|
||||
let unlisten: (() => void) | null = null
|
||||
|
||||
// Tab 导航:概览(项目信息/任务/工作流日志) vs 文件浏览器(Batch 10)。
|
||||
// P1-g+ 问题10④:activeTab 持久化 localStorage(按项目隔离,切换项目不串扰)。
|
||||
@@ -672,6 +688,26 @@ const formattedEvents = computed<LogEntry[]>(() => {
|
||||
})
|
||||
})
|
||||
|
||||
// ── B-41 工作流实时进度 ──
|
||||
// ProjectDetail 无工作流推进入口,但审批弹窗/日志面板已接 liveEvents;此处展示最近一次
|
||||
// 工作流的 DAG + 节点状态高亮(共享 workflow store 派生,与 TaskDetail 同源),
|
||||
// 手动跑工作流时也能看到节点实时进度。无事件(本次会话没跑过)时整个面板不渲染。
|
||||
const wfDagJson = ref('')
|
||||
const latestWfExecId = computed(() => {
|
||||
const evs = store.liveEvents
|
||||
return evs.length > 0 ? evs[evs.length - 1].execution_id : null
|
||||
})
|
||||
const wfProgress = computed(() => store.workflowProgress(latestWfExecId.value))
|
||||
const hasWfProgress = computed(() => Object.keys(wfProgress.value.nodeStatuses).length > 0)
|
||||
watch(latestWfExecId, async (id) => {
|
||||
wfDagJson.value = ''
|
||||
if (!id) return
|
||||
try {
|
||||
const record = await workflowApi.getExecution(id)
|
||||
if (record?.dag_json) wfDagJson.value = record.dag_json
|
||||
} catch { /* 静默 */ }
|
||||
}, { immediate: true })
|
||||
|
||||
// ── 新建任务 ──
|
||||
const showNewTaskModal = ref(false)
|
||||
const newTaskTitle = ref('')
|
||||
@@ -822,7 +858,7 @@ onMounted(async () => {
|
||||
// 来源灵感回溯:项目有 idea_id 但 store.ideas 为空(本页未 load 过)时补拉,
|
||||
// 否则 sourceIdea computed 永远找不到对应灵感记录
|
||||
await store.loadIdeas()
|
||||
unlisten = await store.startEventListener()
|
||||
await store.startEventListener()
|
||||
await checkPath()
|
||||
})
|
||||
|
||||
@@ -838,10 +874,9 @@ watch(projectId, async (newId) => {
|
||||
watch(() => currentProject.value?.path, () => { checkPath() })
|
||||
|
||||
onUnmounted(() => {
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
unlisten = null
|
||||
}
|
||||
// 用 store.stopEventListener 统一停全局监听并复位句柄(直接调 unlisten() 停监听但
|
||||
// 不复位 _eventUnlisten,后续 startEventListener 幂等会误返回已死句柄,共享化后必查)
|
||||
store.stopEventListener()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1205,6 +1240,20 @@ onUnmounted(() => {
|
||||
}
|
||||
.workflow-log-panel { flex: 0 0 auto; }
|
||||
|
||||
/* B-41 工作流实时进度面板(与 TaskDetail wf-progress 同款视觉) */
|
||||
.wf-progress-panel { flex: 0 0 auto; }
|
||||
.wf-progress {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--df-text-secondary);
|
||||
}
|
||||
.wf-progress-count { color: var(--df-text-dim); }
|
||||
.wf-progress-hint { color: var(--df-success); }
|
||||
.wf-progress-hint-fail { color: var(--df-danger); }
|
||||
|
||||
/* ===== 面板 ===== */
|
||||
/* .panel / .panel-header 基础样式已收敛至全局 components.css(DRY 收口 B-260619),
|
||||
此处仅保留本组件特有 .task-count。 */
|
||||
|
||||
Reference in New Issue
Block a user