修复: 前端UX一致性(Dashboard状态映射/状态徽章全局/Knowledge下一条与失败反馈/Ideas筛选与错误条/ConfirmDialog安全/快捷菜单状态机/分页越界) + 后端校验(queue/count-list一致/软删拒改/父聚合/promote CAS/MCP状态机收口/update白名单剔除id/created_at) + 销账

This commit is contained in:
lxy
2026-08-09 21:35:58 +08:00
parent 8bc5380ecb
commit fbd8fae44b
30 changed files with 829 additions and 138 deletions
+26 -11
View File
@@ -286,13 +286,20 @@ async function onDetailSave(payload: { id: string; data: { title: string; conten
// ===== 审核操作(收件箱) =====
// P0-②:发布/拒绝/归档后自动选中下一条(而非清空详情),减少审核者来回点击
// WK-2:index 须在 store 操作前快照(操作后 candidates 已 filter 移除 currentId,findIndex 恒 -1)
// WK-3:操作失败(updateStatus/archive 返 false)toast 报错 + 中止推进(不 selectNext,不清详情)
async function publishCurrent() {
if (!detail.value) return
const currentId = detail.value.knowledge.id
const idx = store.candidates.findIndex(c => c.id === currentId)
submitting.value = true
try {
await store.updateStatus(currentId, 'published')
await selectNextCandidate(currentId)
const ok = await store.updateStatus(currentId, 'published')
if (!ok) {
showToast(t('knowledge.err.updateStatusFailed'), 'error')
return
}
await selectNextCandidate(idx)
showToast(t('knowledge.publishedToast'), 'success')
} finally {
submitting.value = false
@@ -302,10 +309,15 @@ async function publishCurrent() {
async function rejectCurrent() {
if (!detail.value) return
const currentId = detail.value.knowledge.id
const idx = store.candidates.findIndex(c => c.id === currentId)
submitting.value = true
try {
await store.archive(currentId)
await selectNextCandidate(currentId)
const ok = await store.archive(currentId)
if (!ok) {
showToast(t('knowledge.err.archiveFailed'), 'error')
return
}
await selectNextCandidate(idx)
showToast(t('knowledge.rejectedToast'), 'info')
} finally {
submitting.value = false
@@ -315,12 +327,17 @@ async function rejectCurrent() {
async function archiveCurrent() {
if (!detail.value) return
const currentId = detail.value.knowledge.id
const idx = store.candidates.findIndex(c => c.id === currentId)
submitting.value = true
try {
await store.archive(currentId)
const ok = await store.archive(currentId)
if (!ok) {
showToast(t('knowledge.err.archiveFailed'), 'error')
return
}
// 知识库 tab 无「下一条」语义(library 不限 candidate),归档后清空详情
if (topTab.value === 'inbox') {
await selectNextCandidate(currentId)
await selectNextCandidate(idx)
} else {
selectedId.value = null
detail.value = null
@@ -338,11 +355,9 @@ function pickNextCandidate(remaining: KnowledgeRecord[], currentIndex: number):
return remaining[currentIndex] ?? remaining[currentIndex - 1] ?? null
}
async function selectNextCandidate(currentId: string) {
const before = store.candidates
const idx = before.findIndex(c => c.id === currentId)
const remaining = store.candidates // store 操作后已 filter 移除 currentId
const next = idx >= 0 ? pickNextCandidate(remaining, idx) : null
async function selectNextCandidate(currentIndex: number) {
const remaining = store.candidates // 操作后已 filter 移除 currentId
const next = currentIndex >= 0 ? pickNextCandidate(remaining, currentIndex) : null
if (next) {
await selectKnowledge(next.id)
} else {