diff --git a/crates/df-ai/src/openai_helpers.rs b/crates/df-ai/src/openai_helpers.rs index 4107389..467eb95 100644 --- a/crates/df-ai/src/openai_helpers.rs +++ b/crates/df-ai/src/openai_helpers.rs @@ -208,6 +208,14 @@ pub(crate) fn apply_openai_sse(data: &str, usage_accum: &mut Option) } // 提取 usage(带 include_usage 时末段 chunk 携带,覆盖累积) if let Some(u) = chunk.usage { + tracing::info!( + prompt = u.prompt_tokens, + completion = u.completion_tokens, + cache_hit = u.prompt_cache_hit_tokens, + cache_miss = u.prompt_cache_miss_tokens, + reasoning = u.reasoning_tokens, + "[OpenAI] 末 chunk usage 解析(deepseek 等报 cache)" + ); *usage_accum = Some(TokenUsage { prompt_tokens: u.prompt_tokens, completion_tokens: u.completion_tokens, diff --git a/src/components/project/ProjectCard.vue b/src/components/project/ProjectCard.vue index c67784e..2e29c1f 100644 --- a/src/components/project/ProjectCard.vue +++ b/src/components/project/ProjectCard.vue @@ -176,8 +176,8 @@ watch(() => props.view, () => nextTick(measureDesc)) } .card-desc-toggle:hover { text-decoration: underline; } -/* list 视图:更紧凑的内边距,单屏容纳更多卡片 */ -.project-card--list { padding: calc(var(--df-pad-panel) * 0.7); } +/* list 视图:padding 同 card(1× panel,文字不贴边);list 紧凑靠 line-clamp 2 + grid 单列,不靠缩 padding */ +.project-card--list { padding: var(--df-pad-panel); } .card-footer { display: flex; diff --git a/src/i18n/en/projectDetail.ts b/src/i18n/en/projectDetail.ts index 969518c..a068863 100644 --- a/src/i18n/en/projectDetail.ts +++ b/src/i18n/en/projectDetail.ts @@ -68,6 +68,8 @@ export default { approvalInvert: 'Invert', // Tech stack techStackLabel: 'Tech Stack', + // Module list (multi-module read-only display) + modulesLabel: 'Modules ({n})', // Tab navigation tabOverview: '📋 Overview', }, diff --git a/src/i18n/zh-CN/projectDetail.ts b/src/i18n/zh-CN/projectDetail.ts index 353a667..d9cd264 100644 --- a/src/i18n/zh-CN/projectDetail.ts +++ b/src/i18n/zh-CN/projectDetail.ts @@ -68,6 +68,8 @@ export default { approvalInvert: '反选', // 技术栈 techStackLabel: '技术栈', + // 工程列表(多工程只读展示) + modulesLabel: '工程({n})', // Tab 导航 tabOverview: '📋 概览', }, diff --git a/src/views/ProjectDetail.vue b/src/views/ProjectDetail.vue index f8fa581..7b8e7bb 100644 --- a/src/views/ProjectDetail.vue +++ b/src/views/ProjectDetail.vue @@ -117,6 +117,17 @@ + +
+ {{ $t('projectDetail.modulesLabel', { n: modules.length }) }} +
+
+ {{ m.name }} + {{ m.path }} +
+
+
+
{{ $t('projectDetail.techStackLabel') }} @@ -291,6 +302,7 @@ 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 { moduleApi, type ProjectModuleRecord } from '@/api/module' import { formatDate } from '@/utils/time' import { parseStack } from '@/utils/project' import { parseScores as parseScoresJson, assessmentClass, assessmentLabel as assessmentLabelI18n, scoreTier } from '@/utils/ideaEval' @@ -419,6 +431,18 @@ async function loadProjectTasks() { } // taskStatusLabel / taskStatusClass 由 ../constants/project 提供 +// ── 工程列表(只读展示)── +// 多工程(monorepo/多模块)在 FileExplorer 文件 Tab 内 CRUD,概览仅按 project_id 拉取展示。 +// 0 工程 v-if 隐藏,单工程也显(与多工程同列表)。 +const modules = ref([]) +async function loadModules() { + try { + modules.value = await moduleApi.listProjectModules(projectId.value) + } catch { + modules.value = [] + } +} + // ── 工作流 ── // demoDag + runDemoWorkflow 已下线(R-PD-2):"script" 节点不再注册到 NodeRegistry, // 前端构造含 script 节点的 DagDef 会在后端 build_dag 失败报错。 @@ -592,6 +616,8 @@ onMounted(async () => { await store.loadProjects() // 本项目任务独立拉取(不调 store.loadTasks,避免被 Tasks 视图分页/筛选污染) await loadProjectTasks() + // 工程列表独立拉取(FileExplorer 文件 Tab 内 CRUD,概览只读展示) + await loadModules() // 来源灵感回溯:项目有 idea_id 但 store.ideas 为空(本页未 load 过)时补拉, // 否则 sourceIdea computed 永远找不到对应灵感记录 await store.loadIdeas() @@ -604,6 +630,7 @@ watch(projectId, async (newId) => { if (!newId) return activeTab.value = readStoredTab(newId) await loadProjectTasks() + await loadModules() }) // 目录变更(重定位后)重新检测存在性 @@ -650,6 +677,36 @@ onUnmounted(() => { font-style: italic; } +/* ===== 工程列表(概览只读展示;对齐 .info-item label固定宽 + value) ===== */ +.module-list-overview { + display: flex; + flex-direction: column; + gap: 4px; + flex: 1; + min-width: 0; +} +.module-row-overview { + display: flex; + align-items: baseline; + gap: 8px; + min-width: 0; +} +.module-name-overview { + font-size: 13px; + color: var(--df-text); + font-weight: 500; + flex-shrink: 0; +} +.module-path-overview { + font-size: 12px; + font-family: 'SF Mono', 'Fira Code', monospace; + color: var(--df-text-dim); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + .status-badge { display: inline-block; padding: 2px 8px;