新增: 文件浏览器增强(窗口分离/Git 变更/行号/Diff/分页提交历史)

- 窗口分离: FileExplorer 可弹出独立 Tauri 窗口
- 行号显示: 文件预览左侧显示行号列
- 按后缀图标: rs/ts/js/vue/css/html 等 20+ 类型彩色图标
- Diff 视图: 有 Git 变更的文件可切换 diff 红绿视图
- Git 变更面板: 变更文件列表(按目录缩进分组)+提交历史(分页加载)
- 提交详情: 点击提交查看变更文件列表及文件级 diff
- 时间显示: 提交历史显示相对时间/具体日期
- 面包屑导航不丢预览: 导航时不改变当前预览文件
- 刷新保留选中文件: 只清树缓存不清预览
- 中文编码修复: git 命令注入 LANG/LC_ALL 环境变量
- 自适应布局: 弹性 flex 布局,窄窗口适配
- 滚动条优化: 内容区内部滚动,不溢出页面层
- 多工程管理: 工具栏添加工程按钮+弹窗表单
- 审批加载超时缩短: 130s 降至 30s
- 文件变更自动刷新: write_file/patch_file 触发 df-data-changed
This commit is contained in:
2026-06-29 19:25:30 +08:00
parent 63936e3016
commit dfed588b54
20 changed files with 1812 additions and 159 deletions

View File

@@ -38,7 +38,8 @@
</div>
</div>
<!-- 阶段进度条 -->
<!-- 阶段进度条(暂时隐藏:阶段流转逻辑未接通,后续接入真实状态机后启用) -->
<!--
<div class="stage-pipeline">
<div
v-for="(stage, idx) in stages"
@@ -57,6 +58,7 @@
<div v-if="idx < stages.length - 1" class="stage-connector" :class="{ 'connector-done': idx < currentStageIndex }"></div>
</div>
</div>
-->
<!-- Tab 导航(概览 / 文件浏览器) -->
<nav class="detail-tabs">
@@ -272,7 +274,7 @@ import { projectApi } from '@/api'
import { formatDate } from '@/utils/time'
import { parseStack } from '@/utils/project'
import { parseScores as parseScoresJson, assessmentClass, assessmentLabel as assessmentLabelI18n } from '@/utils/ideaEval'
import { projectStatusLabel, projectStageInfo, taskStatusLabel, taskStatusClass } from '../constants/project'
import { projectStatusLabel, taskStatusLabel, taskStatusClass } from '../constants/project'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import ApprovalDialog from '@/components/project/ApprovalDialog.vue'
import FileExplorer from '@/components/project/FileExplorer.vue'
@@ -293,14 +295,14 @@ const activeTab = ref<'overview' | 'files'>('overview')
// 确认弹层状态机抽至 composables/useConfirm(原 4 视图重复:Projects/ProjectDetail/Ideas/Settings)
const { confirmState, confirmDialog, answerConfirm } = useConfirm()
// ── 阶段定义(labelKey 对应 i18n projectDetail.stage*) ──
const stages = [
{ key: 'idea', labelKey: 'stageIdea' },
{ key: 'requirement', labelKey: 'stageRequirement' },
{ key: 'coding', labelKey: 'stageCoding' },
{ key: 'testing', labelKey: 'stageTesting' },
{ key: 'release', labelKey: 'stageRelease' },
]
// ── 阶段定义(暂时隐藏,阶段进度条模板已注释,后续接入真实状态机后启用) ──
// const stages = [
// { key: 'idea', labelKey: 'stageIdea' },
// { key: 'requirement', labelKey: 'stageRequirement' },
// { key: 'coding', labelKey: 'stageCoding' },
// { key: 'testing', labelKey: 'stageTesting' },
// { key: 'release', labelKey: 'stageRelease' },
// ]
// ── 当前项目 ──
// 状态文案/阶段进度统一走 ../constants/project(与 Projects/Tasks/Dashboard 一致,
@@ -309,6 +311,8 @@ const projectId = computed(() => route.params.id as string)
const currentProject = computed(() =>
store.projects.find(p => p.id === projectId.value)
)
// 状态文案/阶段进度统一走 ../constants/project
// B-260615-25:项目描述 Markdown 渲染(复用 AiChat/TaskDetail 同款渲染器,模块级单例),
// useRendered 封装 computed(读 mdReady 触发响应式 + renderMd)+ ensureLoaded(幂等预热)
const { rendered: renderedDesc, ensureLoaded } = useRendered(
@@ -316,7 +320,7 @@ const { rendered: renderedDesc, ensureLoaded } = useRendered(
)
const stageKey = computed(() => currentProject.value?.status ?? 'planning')
const statusLabel = computed(() => projectStatusLabel(stageKey.value))
const currentStageIndex = computed(() => projectStageInfo(stageKey.value).stepIndex)
// const currentStageIndex = computed(() => projectStageInfo(stageKey.value).stepIndex)
// ── 来源灵感(晋升携带评估结论回溯)──
// ProjectRecord.idea_id 存灵感 id(promote_idea 写入)。
@@ -685,7 +689,7 @@ onUnmounted(() => {
</style>
<style scoped>
.project-detail { padding: 16px 20px 20px; }
.project-detail { padding: 16px 20px 20px; display: flex; flex-direction: column; min-height: 0; flex: 1; }
/* ===== Tab 导航(Batch 10 文件浏览器) ===== */
.detail-tabs {
@@ -713,10 +717,11 @@ onUnmounted(() => {
border-bottom-color: var(--df-accent);
}
/* 文件浏览器容器(占满高度,内部 FileExplorer 自管布局) */
/* 文件浏览器容器(弹性填充,内部 FileExplorer 自管布局) */
.file-explorer-wrap {
height: calc(100vh - 340px);
min-height: 480px;
flex: 1;
min-height: 0;
overflow: hidden;
}
.page-header {