新增: 文件浏览器增强(窗口分离/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:
55
src/views/FileExplorerDetached.vue
Normal file
55
src/views/FileExplorerDetached.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="fe-detached-shell">
|
||||
<!-- 等待 projectId 参数加载 -->
|
||||
<div v-if="!projectId" class="fe-detached-loading">
|
||||
<span class="spinner"></span>
|
||||
</div>
|
||||
<FileExplorer v-else :project-id="projectId" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 文件浏览器分离窗口视图(Batch 12)。
|
||||
* 作为独立 Tauri 窗口的全屏内容,通过 URL query `?projectId=xxx` 接收参数。
|
||||
*/
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import FileExplorer from '@/components/project/FileExplorer.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const projectId = computed(() => (route.query.projectId as string) || '')
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fe-detached-shell {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: var(--df-bg);
|
||||
}
|
||||
|
||||
.fe-detached-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
color: var(--df-text-dim);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid var(--df-border, #444);
|
||||
border-top-color: var(--df-accent, #3a8);
|
||||
border-radius: 50%;
|
||||
animation: df-spin 0.8s linear infinite;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@keyframes df-spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user