优化: 所有剩余UI/UX待办一批完成(持久化+AuditLog+解耦+total+原12大改+P2)
持久化(P1-c):新建 usePersistedRef composable,Tasks/AuditLog/ProjectDetail 等接入 localStorage
AuditLog(P1-d):后端 list_tool_executions 加 WHERE 筛选+返 {items,total,has_more},前端对接+长列折叠+筛选持久化
数据源解耦(P1-g):ProjectDetail projectTasks 按 project_id 独立加载 + ChatInput @项目联想独立加载(不读 store.tasks 当前页)
GitChanges(12a):后端 get_module_commits 加 git rev-list --count 返 total,前端显真实总数
原12大改:Dashboard 统计卡压底行(1)/Projects 列表卡片视图(2)/project_event 埋点排序(3)/TaskDetail 重设计(4)/IdeaDetail 重设计(5)/KnowledgeDetail 重设计(6)/界面持久化+侧栏Ctrl+B+审批数字键(7)/ProjectDetail 三栏改两栏(10)
P2打磨:文件浏览器(FileTree去重/FilePreview行号.md Diff/selectedFilePath归位)/settings反馈(假保存/端口校验)/AI会话(try-catch/scrollIntoView)/后端计数(move_queue事件/timeline total/workflow分页/import_batch分块)/杂项(TopBar/ConfirmDialog键盘/CIStatus i18n/ToolResultBody/ModuleNode/ApprovalDialog全选)
This commit is contained in:
@@ -87,7 +87,8 @@
|
||||
* 子组件只负责 emit('toggle-dir', path, entries) / emit('load-children', path),
|
||||
* 实际的网络请求与状态更新统一在 FileExplorer 中处理。
|
||||
* - 本组件自身的 loading/entries 仅用于"首次挂载时拉取自己 sub_path 的根条目";
|
||||
* 递归子树复用同一份 props/事件,不重复拉取。
|
||||
* 递归子树复用同一份 props/事件,且 loadEntries 会先查 loadedChildren 缓存命中即复用,
|
||||
* 避免父级 toggleDir 已拉取后子树 onMounted 再次重复请求同目录。
|
||||
*
|
||||
* 这样设计的好处:刷新(refresh)只需 FileExplorer 清空 loadedChildren 重拉根,
|
||||
* 所有展开的子树因 expandedPaths 被清而卸载,下次展开重新拉取,无脏数据。
|
||||
@@ -130,13 +131,21 @@ const error = ref<string | null>(null)
|
||||
async function loadEntries() {
|
||||
// moduleId 为空时不发起请求(工程列表还在加载中)
|
||||
if (!props.moduleId) return
|
||||
// 去重:递归子树实例挂载时,父级 toggleDir 通常已为本目录拉取并缓存。
|
||||
// 命中缓存则直接复用,不再发同样的请求(避免 onMounted 与 toggleDir 双重拉取)。
|
||||
const cacheKey = props.subPath || ''
|
||||
const cached = props.loadedChildren.get(cacheKey)
|
||||
if (cached) {
|
||||
entries.value = cached
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const res = await moduleApi.getModuleFileTree(props.moduleId, props.subPath || undefined)
|
||||
entries.value = res.entries
|
||||
// 缓存到顶层 loadedChildren(供 toggle 判断是否已有数据,避免重复请求)。
|
||||
props.loadedChildren.set(props.subPath || '', res.entries)
|
||||
props.loadedChildren.set(cacheKey, res.entries)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : String(e)
|
||||
} finally {
|
||||
@@ -153,10 +162,10 @@ async function toggleDir(entry: FileTreeEntry) {
|
||||
// 收起:仅移除展开标记(loadedChildren 缓存保留,下次展开秒开)。
|
||||
emit('toggle-dir', entry.path, [])
|
||||
} else {
|
||||
// 展开:若未加载过则先拉取(由顶层处理缓存命中),再 emit 通知展开。
|
||||
// 展开:若未加载过则先拉取并缓存(去重——只在这里拉一次;
|
||||
// 递归子树实例 onMounted 时会复用此缓存,不重复请求同目录)。
|
||||
if (!props.loadedChildren.has(entry.path)) {
|
||||
emit('load-children', entry.path)
|
||||
// 立即拉取本目录(子组件实例挂载后会自取 loadedChildren;此处同步拉保响应即时)。
|
||||
try {
|
||||
loading.value = true
|
||||
const res = await moduleApi.getModuleFileTree(props.moduleId, entry.path)
|
||||
|
||||
Reference in New Issue
Block a user