修复: CR-28 knowledge error 通道接 Knowledge.vue banner+create 语义修正
knowledge.ts create() 改直接 listCandidates 绕开 loadCandidates 的 state.error 污染,失败 console.warn 非阻塞(保 create 成功语义纯净,原 loadCandidates 失败塞 error 与 create 返回 record 成功矛盾)+加 clearError action 导出;Knowledge.vue 加 error banner(v-if store.error + ✕ 关闭按钮 clearError)让加载/检索/配置失败对用户可见(原 state.error 全应用零消费只看空白列表)。批10 w8orghove,vue-tsc 0
This commit is contained in:
@@ -79,8 +79,14 @@ export function useKnowledgeStore() {
|
||||
|
||||
async function create(input: CreateKnowledgeInput) {
|
||||
const record = await knowledgeApi.create(input)
|
||||
// 手动录入默认 candidate,刷新收件箱
|
||||
await loadCandidates()
|
||||
// 手动录入默认 candidate,刷新收件箱。
|
||||
// create 已成功返回 record,刷新失败不应污染成功语义 —— 直接走原始 API + 非阻塞 console.warn,
|
||||
// 不经 loadCandidates(后者会写 state.error,污染 create 成功语义)。
|
||||
try {
|
||||
state.candidates = await knowledgeApi.listCandidates()
|
||||
} catch (e: any) {
|
||||
console.warn('create 后刷新收件箱失败(非阻塞)', e?.toString?.() ?? e)
|
||||
}
|
||||
return record
|
||||
}
|
||||
|
||||
@@ -140,6 +146,11 @@ export function useKnowledgeStore() {
|
||||
// 候选数量(供侧栏 badge 用)
|
||||
const candidateCount = computed(() => state.candidates.length)
|
||||
|
||||
// 清除错误条(供 Knowledge.vue error-banner 关闭按钮调用)
|
||||
function clearError() {
|
||||
state.error = null
|
||||
}
|
||||
|
||||
return reactive({
|
||||
// 状态用 getter 实时读 state(防快照不跟随,同 project store 模式)
|
||||
get items() { return state.items },
|
||||
@@ -152,5 +163,6 @@ export function useKnowledgeStore() {
|
||||
loadList, loadCandidates, search, create, updateStatus, archive,
|
||||
loadConfig, saveConfig, extractNow,
|
||||
getDetail, update, getEvents,
|
||||
clearError,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 错误条:消费 store.error(加载/检索/配置失败对用户可见) -->
|
||||
<div v-if="store.error" class="error-banner">
|
||||
<span class="error-text">{{ store.error }}</span>
|
||||
<button class="error-dismiss" @click="clearError">✕</button>
|
||||
</div>
|
||||
|
||||
<!-- 顶层 Tab: 知识库 / 审核收件箱 -->
|
||||
<div class="top-tabs">
|
||||
<button class="top-tab" :class="{ active: topTab === 'library' }" @click="switchTopTab('library')">
|
||||
@@ -282,6 +288,11 @@ function switchTopTab(tab: 'library' | 'inbox') {
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭错误条
|
||||
function clearError() {
|
||||
store.clearError()
|
||||
}
|
||||
|
||||
// 左侧列表(随 Tab 切换数据源 + 知识库的分类过滤)
|
||||
const listItems = computed(() => {
|
||||
const src = topTab.value === 'inbox' ? store.candidates : store.items
|
||||
@@ -523,6 +534,20 @@ onMounted(() => {
|
||||
.page-header h1 { font-size: 24px; font-weight: 500; color: var(--df-text); }
|
||||
.header-actions { display: flex; gap: 8px; }
|
||||
|
||||
/* ===== 错误条(消费 store.error) ===== */
|
||||
.error-banner {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 12px;
|
||||
padding: 8px 12px; margin-bottom: var(--df-gap-page);
|
||||
background: rgba(255,107,107,0.12); border: 0.5px solid var(--df-danger);
|
||||
border-radius: var(--df-radius-sm); color: var(--df-danger); font-size: 12px;
|
||||
}
|
||||
.error-text { word-break: break-word; }
|
||||
.error-dismiss {
|
||||
flex-shrink: 0; background: transparent; border: none; color: var(--df-danger);
|
||||
font-size: 14px; cursor: pointer; line-height: 1; padding: 0 2px;
|
||||
}
|
||||
.error-dismiss:hover { opacity: 0.7; }
|
||||
|
||||
/* ===== 顶层 Tab ===== */
|
||||
.top-tabs { display: flex; gap: 4px; margin-bottom: var(--df-gap-page); border-bottom: 0.5px solid var(--df-border); }
|
||||
.top-tab {
|
||||
|
||||
Reference in New Issue
Block a user