优化: AI Chat全栈多批审查修复与架构清理(risk_level清理/路由解耦/工具渲染/测试补测/死代码)
This commit is contained in:
@@ -89,6 +89,8 @@ export const state = reactive({
|
||||
agentRound: 0,
|
||||
// 归档分组折叠态(默认折叠)
|
||||
archivedCollapsed: true,
|
||||
// 时间分组折叠态(默认:更早折叠,今天/昨天展开;持久化进 df-ai-ui)
|
||||
foldedGroups: { today: false, yesterday: false, earlier: true } as Record<string, boolean>,
|
||||
// 对话搜索关键字(UX-06 §3.1):非空时侧栏取消时间分组,平铺展示标题匹配的会话(updated_at DESC)。
|
||||
// 清空恢复原分组。消费方:Sidebar 渲染(读 searchQuery 决定走 filteredConversations 还是 groupedActive)。
|
||||
searchQuery: '',
|
||||
|
||||
@@ -26,9 +26,13 @@ const state = reactive({
|
||||
// ── 异步竞态保护(请求序号,类似 ai.ts messages 的 switch token 模式) ──
|
||||
// 快速连点(切换筛选/连续检索/重连)时,旧响应晚到会覆盖新响应。序号自增,
|
||||
// 请求发起时捕获序号,响应回时比对:非最新请求的响应直接丢弃,只取最新。
|
||||
// items 序号覆盖 loadList/loadCandidates/search;candidates 与 items 共用同一队列
|
||||
// (loadList/search 写 items,loadCandidates 写 candidates),故 items 序号也兜底 candidates。
|
||||
let _itemsReqSeq = 0
|
||||
// 拆两套序号:loadList/search 都写 state.items 共用 _itemsSeq;loadCandidates 写
|
||||
// state.candidates 用独立 _candidatesSeq。各自防竞态,互不丢弃。
|
||||
// (P1 修复:原先三者共用 _itemsReqSeq,onMounted 并发 loadList+loadCandidates 会
|
||||
// 让后发起的 loadCandidates ++ 覆盖 loadList 的 seq,导致 loadList 响应被误判为
|
||||
// 旧响应丢弃,首次进入列表必空。)
|
||||
let _itemsSeq = 0
|
||||
let _candidatesSeq = 0
|
||||
let _configReqSeq = 0
|
||||
|
||||
// 7 种知识类型(snake_case,与后端 KnowledgeKind 对齐)
|
||||
@@ -59,48 +63,48 @@ export function useKnowledgeStore() {
|
||||
// 后端 knowledge_list 默认收窄为 published,无需前端传 status。
|
||||
// 显式 status 仍透传(如 status='archived' 查归档)。
|
||||
async function loadList(status?: string) {
|
||||
const seq = ++_itemsReqSeq
|
||||
const seq = ++_itemsSeq
|
||||
state.loading = true
|
||||
state.error = null
|
||||
try {
|
||||
const result = await knowledgeApi.list(status ?? null)
|
||||
if (seq !== _itemsReqSeq) return // 旧响应丢弃,只取最新
|
||||
if (seq !== _itemsSeq) return // 旧响应丢弃,只取最新
|
||||
state.items = result
|
||||
} catch (e: any) {
|
||||
if (seq !== _itemsReqSeq) return // 旧请求的失败不污染当前视图
|
||||
if (seq !== _itemsSeq) return // 旧请求的失败不污染当前视图
|
||||
state.error = e?.toString() ?? t('knowledge.err.loadFailed')
|
||||
} finally {
|
||||
if (seq === _itemsReqSeq) state.loading = false
|
||||
if (seq === _itemsSeq) state.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
// ── 加载待处理收件箱(candidate + pending_review,F-260616-02 决策 a) ──
|
||||
// 后端 knowledge_list_candidates 已收两种待处理状态,前端调用不变。
|
||||
async function loadCandidates() {
|
||||
const seq = ++_itemsReqSeq
|
||||
const seq = ++_candidatesSeq
|
||||
try {
|
||||
const result = await knowledgeApi.listCandidates()
|
||||
if (seq !== _itemsReqSeq) return // 旧响应丢弃,只取最新
|
||||
if (seq !== _candidatesSeq) return // 旧响应丢弃,只取最新
|
||||
state.candidates = result
|
||||
} catch (e: any) {
|
||||
if (seq !== _itemsReqSeq) return
|
||||
if (seq !== _candidatesSeq) return
|
||||
state.error = e?.toString() ?? t('knowledge.err.loadInboxFailed')
|
||||
}
|
||||
}
|
||||
|
||||
async function search(query: string, kind?: string) {
|
||||
const seq = ++_itemsReqSeq
|
||||
const seq = ++_itemsSeq
|
||||
state.loading = true
|
||||
state.error = null
|
||||
try {
|
||||
const result = await knowledgeApi.search({ query, kind })
|
||||
if (seq !== _itemsReqSeq) return // 旧响应丢弃,只取最新
|
||||
if (seq !== _itemsSeq) return // 旧响应丢弃,只取最新
|
||||
state.items = result
|
||||
} catch (e: any) {
|
||||
if (seq !== _itemsReqSeq) return
|
||||
if (seq !== _itemsSeq) return
|
||||
state.error = e?.toString() ?? t('knowledge.err.searchFailed')
|
||||
} finally {
|
||||
if (seq === _itemsReqSeq) state.loading = false
|
||||
if (seq === _itemsSeq) state.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user