优化: ProjectDetail多工程列表 + ProjectCard list padding + provider usage诊断
ProjectDetail 概览加工程列表(moduleApi.listProjectModules 显所有 module 名/路径,只读,多工程可见,不只项目级 bind_directory) ProjectCard list padding 1×(原 0.7× 紧凑文字贴边,panel 14/16px) openai_helpers apply_openai_sse usage 诊断 tracing(确认 deepseek 报 cache/usage 否,定位 completion 0) i18n projectDetail.modulesLabel zh/en
This commit is contained in:
@@ -208,6 +208,14 @@ pub(crate) fn apply_openai_sse(data: &str, usage_accum: &mut Option<TokenUsage>)
|
|||||||
}
|
}
|
||||||
// 提取 usage(带 include_usage 时末段 chunk 携带,覆盖累积)
|
// 提取 usage(带 include_usage 时末段 chunk 携带,覆盖累积)
|
||||||
if let Some(u) = chunk.usage {
|
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 {
|
*usage_accum = Some(TokenUsage {
|
||||||
prompt_tokens: u.prompt_tokens,
|
prompt_tokens: u.prompt_tokens,
|
||||||
completion_tokens: u.completion_tokens,
|
completion_tokens: u.completion_tokens,
|
||||||
|
|||||||
@@ -176,8 +176,8 @@ watch(() => props.view, () => nextTick(measureDesc))
|
|||||||
}
|
}
|
||||||
.card-desc-toggle:hover { text-decoration: underline; }
|
.card-desc-toggle:hover { text-decoration: underline; }
|
||||||
|
|
||||||
/* list 视图:更紧凑的内边距,单屏容纳更多卡片 */
|
/* list 视图:padding 同 card(1× panel,文字不贴边);list 紧凑靠 line-clamp 2 + grid 单列,不靠缩 padding */
|
||||||
.project-card--list { padding: calc(var(--df-pad-panel) * 0.7); }
|
.project-card--list { padding: var(--df-pad-panel); }
|
||||||
|
|
||||||
.card-footer {
|
.card-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ export default {
|
|||||||
approvalInvert: 'Invert',
|
approvalInvert: 'Invert',
|
||||||
// Tech stack
|
// Tech stack
|
||||||
techStackLabel: 'Tech Stack',
|
techStackLabel: 'Tech Stack',
|
||||||
|
// Module list (multi-module read-only display)
|
||||||
|
modulesLabel: 'Modules ({n})',
|
||||||
// Tab navigation
|
// Tab navigation
|
||||||
tabOverview: '📋 Overview',
|
tabOverview: '📋 Overview',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ export default {
|
|||||||
approvalInvert: '反选',
|
approvalInvert: '反选',
|
||||||
// 技术栈
|
// 技术栈
|
||||||
techStackLabel: '技术栈',
|
techStackLabel: '技术栈',
|
||||||
|
// 工程列表(多工程只读展示)
|
||||||
|
modulesLabel: '工程({n})',
|
||||||
// Tab 导航
|
// Tab 导航
|
||||||
tabOverview: '📋 概览',
|
tabOverview: '📋 概览',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -117,6 +117,17 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 工程列表(多工程只读展示;CRUD 在文件 Tab) -->
|
||||||
|
<div class="info-item" v-if="modules.length">
|
||||||
|
<span class="label">{{ $t('projectDetail.modulesLabel', { n: modules.length }) }}</span>
|
||||||
|
<div class="module-list-overview">
|
||||||
|
<div v-for="m in modules" :key="m.id" class="module-row-overview">
|
||||||
|
<span class="module-name-overview">{{ m.name }}</span>
|
||||||
|
<span class="module-path-overview" :title="m.path">{{ m.path }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 技术栈 -->
|
<!-- 技术栈 -->
|
||||||
<div class="info-item" v-if="parseStack(currentProject.stack).length">
|
<div class="info-item" v-if="parseStack(currentProject.stack).length">
|
||||||
<span class="label">{{ $t('projectDetail.techStackLabel') }}</span>
|
<span class="label">{{ $t('projectDetail.techStackLabel') }}</span>
|
||||||
@@ -291,6 +302,7 @@ import { Message } from '@arco-design/web-vue'
|
|||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { open } from '@tauri-apps/plugin-dialog'
|
||||||
import { useProjectStore } from '@/stores/project'
|
import { useProjectStore } from '@/stores/project'
|
||||||
import { projectApi, taskApi } from '@/api'
|
import { projectApi, taskApi } from '@/api'
|
||||||
|
import { moduleApi, type ProjectModuleRecord } from '@/api/module'
|
||||||
import { formatDate } from '@/utils/time'
|
import { formatDate } from '@/utils/time'
|
||||||
import { parseStack } from '@/utils/project'
|
import { parseStack } from '@/utils/project'
|
||||||
import { parseScores as parseScoresJson, assessmentClass, assessmentLabel as assessmentLabelI18n, scoreTier } from '@/utils/ideaEval'
|
import { parseScores as parseScoresJson, assessmentClass, assessmentLabel as assessmentLabelI18n, scoreTier } from '@/utils/ideaEval'
|
||||||
@@ -419,6 +431,18 @@ async function loadProjectTasks() {
|
|||||||
}
|
}
|
||||||
// taskStatusLabel / taskStatusClass 由 ../constants/project 提供
|
// taskStatusLabel / taskStatusClass 由 ../constants/project 提供
|
||||||
|
|
||||||
|
// ── 工程列表(只读展示)──
|
||||||
|
// 多工程(monorepo/多模块)在 FileExplorer 文件 Tab 内 CRUD,概览仅按 project_id 拉取展示。
|
||||||
|
// 0 工程 v-if 隐藏,单工程也显(与多工程同列表)。
|
||||||
|
const modules = ref<ProjectModuleRecord[]>([])
|
||||||
|
async function loadModules() {
|
||||||
|
try {
|
||||||
|
modules.value = await moduleApi.listProjectModules(projectId.value)
|
||||||
|
} catch {
|
||||||
|
modules.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 工作流 ──
|
// ── 工作流 ──
|
||||||
// demoDag + runDemoWorkflow 已下线(R-PD-2):"script" 节点不再注册到 NodeRegistry,
|
// demoDag + runDemoWorkflow 已下线(R-PD-2):"script" 节点不再注册到 NodeRegistry,
|
||||||
// 前端构造含 script 节点的 DagDef 会在后端 build_dag 失败报错。
|
// 前端构造含 script 节点的 DagDef 会在后端 build_dag 失败报错。
|
||||||
@@ -592,6 +616,8 @@ onMounted(async () => {
|
|||||||
await store.loadProjects()
|
await store.loadProjects()
|
||||||
// 本项目任务独立拉取(不调 store.loadTasks,避免被 Tasks 视图分页/筛选污染)
|
// 本项目任务独立拉取(不调 store.loadTasks,避免被 Tasks 视图分页/筛选污染)
|
||||||
await loadProjectTasks()
|
await loadProjectTasks()
|
||||||
|
// 工程列表独立拉取(FileExplorer 文件 Tab 内 CRUD,概览只读展示)
|
||||||
|
await loadModules()
|
||||||
// 来源灵感回溯:项目有 idea_id 但 store.ideas 为空(本页未 load 过)时补拉,
|
// 来源灵感回溯:项目有 idea_id 但 store.ideas 为空(本页未 load 过)时补拉,
|
||||||
// 否则 sourceIdea computed 永远找不到对应灵感记录
|
// 否则 sourceIdea computed 永远找不到对应灵感记录
|
||||||
await store.loadIdeas()
|
await store.loadIdeas()
|
||||||
@@ -604,6 +630,7 @@ watch(projectId, async (newId) => {
|
|||||||
if (!newId) return
|
if (!newId) return
|
||||||
activeTab.value = readStoredTab(newId)
|
activeTab.value = readStoredTab(newId)
|
||||||
await loadProjectTasks()
|
await loadProjectTasks()
|
||||||
|
await loadModules()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 目录变更(重定位后)重新检测存在性
|
// 目录变更(重定位后)重新检测存在性
|
||||||
@@ -650,6 +677,36 @@ onUnmounted(() => {
|
|||||||
font-style: italic;
|
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 {
|
.status-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
|
|||||||
Reference in New Issue
Block a user