优化: 所有剩余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:
@@ -100,6 +100,7 @@
|
||||
v-for="conv in group.items"
|
||||
v-show="!store.state.foldedGroups[group.key]"
|
||||
:key="conv.id"
|
||||
:ref="(el) => setItemEl(conv.id, el)"
|
||||
class="ai-conv-item"
|
||||
:class="{ 'ai-conv-item--active': conv.id === store.state.activeConversationId, 'ai-conv-item--pinned': conv.pinned }"
|
||||
@click="store.switchConversation(conv.id)"
|
||||
@@ -168,6 +169,7 @@
|
||||
v-for="conv in archivedConvs"
|
||||
v-show="!store.state.archivedCollapsed"
|
||||
:key="'a-' + conv.id"
|
||||
:ref="(el) => setItemEl(conv.id, el)"
|
||||
class="ai-conv-item ai-conv-item--archived"
|
||||
:class="{ 'ai-conv-item--active': conv.id === store.state.activeConversationId, 'ai-conv-item--pinned': conv.pinned }"
|
||||
@click="store.switchConversation(conv.id)"
|
||||
@@ -230,7 +232,7 @@
|
||||
* 本组件自管的纯侧栏态:titleFlash/重命名/更多操作菜单/侧栏拖拽/搜索结果/时间分组。
|
||||
* 样式:零 scoped style(沿用 AiChat.vue 全局/父级样式,样式后续批下沉)。
|
||||
*/
|
||||
import { ref, computed, nextTick, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { ref, computed, nextTick, watch, onMounted, onBeforeUnmount, type ComponentPublicInstance } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAiStore } from '../../stores/ai'
|
||||
import { formatRelative } from '../../utils/time'
|
||||
@@ -322,10 +324,51 @@ function onActionsOutsideClick(e: MouseEvent) {
|
||||
|
||||
/**
|
||||
* UX-260617-23:搜索结果点击切对话后清空 searchQuery,退出搜索模式回原时间分组视图。
|
||||
* M3:命中项可能在折叠的时间分组或折叠的归档分组内,选中态不可见。切回分组视图后展开所在分组
|
||||
* 并 scrollIntoView,确保选中项立即可见(原仅切 searchQuery,选中项落在折叠组里看不到高亮)。
|
||||
*/
|
||||
function onSelectSearchResult(id: string) {
|
||||
void store.switchConversation(id)
|
||||
store.state.searchQuery = ''
|
||||
// nextTick 后视图已切回分组态;ensureVisible 展开所在组 + 滚动定位
|
||||
void nextTick(() => ensureActiveVisible())
|
||||
}
|
||||
|
||||
// ── M3:活跃会话项 DOM ref 收集(id → el),用于展开折叠组后 scrollIntoView ──
|
||||
// Vue 3 函数 ref:每项 :ref="(el) => setItemEl(id, el)"。卸载时 el 为 null,删 key 防 Map 泄漏。
|
||||
const itemEls = new Map<string, Element>()
|
||||
function setItemEl(id: string, el: Element | ComponentPublicInstance | null): void {
|
||||
if (!el) {
|
||||
itemEls.delete(id)
|
||||
return
|
||||
}
|
||||
// 组件实例取 $el,但本项是普通 div(el 即 Element)
|
||||
itemEls.set(id, el as Element)
|
||||
}
|
||||
|
||||
/**
|
||||
* M3:确保当前活跃会话在分组视图可见。若它落在折叠的时间分组(今天/昨天/更早)或折叠的归档分组,
|
||||
* 展开该组让 v-show 渲染出来,再 scrollIntoView。用于搜索点选、切对话后选中态不被折叠吞掉。
|
||||
*/
|
||||
function ensureActiveVisible(): void {
|
||||
const id = store.state.activeConversationId
|
||||
if (!id) return
|
||||
const conv = store.state.conversations.find(c => c.id === id)
|
||||
if (!conv) return
|
||||
// 先按归档态/时间桶决定应展开哪个组折叠态
|
||||
if (conv.archived) {
|
||||
if (store.state.archivedCollapsed) store.state.archivedCollapsed = false
|
||||
} else {
|
||||
const bucket = timeBucket(conv.updated_at)
|
||||
if (store.state.foldedGroups[bucket]) store.state.foldedGroups[bucket] = false
|
||||
}
|
||||
// 折叠态翻转后 v-show 需一帧才渲染,再 nextTick 取 el 滚动
|
||||
void nextTick(() => {
|
||||
const el = itemEls.get(id)
|
||||
if (el) {
|
||||
;(el as HTMLElement).scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ── 对话分组:今天 / 昨天 / 更早 ──
|
||||
|
||||
Reference in New Issue
Block a user