diff --git a/digital-avatar-app/src/views/KnowledgeManage.vue b/digital-avatar-app/src/views/KnowledgeManage.vue index 8941522..2bfcca2 100644 --- a/digital-avatar-app/src/views/KnowledgeManage.vue +++ b/digital-avatar-app/src/views/KnowledgeManage.vue @@ -32,7 +32,7 @@
-
+
{{ fileEmoji(doc.fileType) }}
@@ -46,9 +46,14 @@
-
+
+ {{ retryErrors[doc.id] }} + +
📂 暂无文档,先上传一个知识文件
@@ -115,6 +120,8 @@ const uploading = computed(() => pendingUploads.value.some((doc) => doc.localUpl const uploadError = ref('') const dragOver = ref(false) const fileInput = ref(null) +const retryingDocs = ref>({}) +const retryErrors = ref>({}) let documentPollingTimer: ReturnType | undefined const query = ref('') @@ -250,14 +257,24 @@ const uploadOne = async (file: File, ext: string) => { } } -const retryDoc = async (id: string) => { - if (!avatarId.value) return - uploadError.value = '' +const canRetryDoc = (doc: any) => + !doc.localOnly && doc.filePresent !== false && documentState(doc).tone === 'failed' + +const retryDoc = async (doc: any) => { + if (!avatarId.value || !canRetryDoc(doc) || retryingDocs.value[doc.id]) return + retryingDocs.value = { ...retryingDocs.value, [doc.id]: true } + retryErrors.value = { ...retryErrors.value, [doc.id]: '' } try { - await retryKnowledgeDoc(avatarId.value, id) - await loadDocs() + const updated: any = await retryKnowledgeDoc(avatarId.value, doc.id) + Object.assign(doc, updated) + startDocumentPolling() } catch (e: any) { - uploadError.value = e?.message || '重新索引失败' + retryErrors.value = { + ...retryErrors.value, + [doc.id]: e?.response?.data?.message || e?.response?.data?.detail || e?.message || '重新索引失败' + } + } finally { + retryingDocs.value = { ...retryingDocs.value, [doc.id]: false } } } @@ -378,6 +395,7 @@ onUnmounted(stopDocumentPolling) .panel-heading p { margin: -5px 0 0; color: #9398AE; font-size: 12px; } .mobile-card-list { display: grid; grid-template-columns: minmax(0, 1fr); width: 100%; min-width: 0; gap: 10px; } .knowledge-card { display: flex; align-items: center; width: 100%; min-width: 0; box-sizing: border-box; gap: 11px; padding: 14px; background: #fff; border: 1px solid #F1E1D3; border-radius: 16px; box-shadow: 0 5px 16px rgba(112, 62, 22, .04); } +.document-card { display: grid; grid-template-columns: 42px minmax(0, 1fr) auto; align-items: center; } .card-icon { flex: 0 0 auto; width: 42px; height: 42px; display: grid; place-items: center; border-radius: 13px; background: #FFF3E6; font-size: 22px; } .card-content { min-width: 0; flex: 1; overflow: hidden; } .card-title-row { display: flex; align-items: center; gap: 8px; min-width: 0; } @@ -388,10 +406,13 @@ onUnmounted(stopDocumentPolling) .card-meta, .card-detail { margin: 5px 0 0; color: #9398AE; font-size: 11px; line-height: 1.4; }.card-detail { color: #8B6B58; } .progress-track { width: 100%; height: 4px; margin-top: 8px; overflow: hidden; border-radius: 999px; background: #FDE7D1; } .progress-fill { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #FB923C, #F97316); transition: width .25s ease; } -.card-actions { flex: 0 0 auto; display: flex; flex-direction: column; align-items: stretch; gap: 6px; } +.card-actions { flex: 0 0 auto; display: flex; align-items: center; } .card-delete, .card-retry { align-self: center; border: 0; border-radius: 8px; padding: 7px 9px; font-size: 12px; cursor: pointer; white-space: nowrap; } .card-delete { color: #EF4444; background: #FEF2F2; } .card-retry { color: #C15F18; background: #FFF3E6; } +.card-retry:disabled { cursor: wait; opacity: .65; } +.card-retry-area { grid-column: 1 / -1; display: flex; align-items: center; justify-content: flex-end; gap: 10px; min-width: 0; } +.card-retry-error { min-width: 0; overflow: hidden; color: #DC2626; font-size: 11px; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; } .card-empty { padding: 42px 16px; border: 1px dashed #F1D9C3; border-radius: 16px; color: #9398AE; background: #fff; font-size: 14px; text-align: center; } .qa-card { align-items: stretch; text-align: left; }.qa-card.qa-disabled { opacity: .58; } .qa-card .card-content, @@ -617,8 +638,11 @@ onUnmounted(stopDocumentPolling) @media (max-width: 520px) { .knowledge-panel { padding: 0 12px; } .knowledge-card { display: grid; grid-template-columns: 42px minmax(0, 1fr); align-items: start; gap: 10px; padding: 13px; } + .document-card { grid-template-columns: 42px minmax(0, 1fr) auto; } .card-content { grid-column: 2; } - .card-delete { grid-column: 2; justify-self: end; margin-top: -2px; } + .card-actions { grid-column: 3; grid-row: 1; } + .card-delete { justify-self: end; margin-top: -2px; } + .card-retry-area { grid-column: 1 / -1; } .qa-card { display: block; } .qa-card .card-content { width: 100%; grid-column: 1; } .card-title-row { align-items: flex-start; flex-wrap: wrap; gap: 5px 7px; }