feat(web): 更新应用外壳路由视图和政策制度页面,增强前端路由嵌套和页面展示能力
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
'policies-main': activeView === 'policies',
|
'policies-main': activeView === 'policies',
|
||||||
'audit-main': activeView === 'audit',
|
'audit-main': activeView === 'audit',
|
||||||
'audit-detail-main': activeView === 'audit' && auditDetailOpen,
|
'audit-detail-main': activeView === 'audit' && auditDetailOpen,
|
||||||
|
'logs-main': activeView === 'logs',
|
||||||
'employees-main': activeView === 'employees',
|
'employees-main': activeView === 'employees',
|
||||||
'settings-main': activeView === 'settings'
|
'settings-main': activeView === 'settings'
|
||||||
}"
|
}"
|
||||||
@@ -33,8 +34,10 @@
|
|||||||
:active-range="activeRange"
|
:active-range="activeRange"
|
||||||
:employee-summary="employeeSummary"
|
:employee-summary="employeeSummary"
|
||||||
:knowledge-summary="knowledgeSummary"
|
:knowledge-summary="knowledgeSummary"
|
||||||
|
:logs-summary="logsSummary"
|
||||||
:request-summary="requestSummary"
|
:request-summary="requestSummary"
|
||||||
:detail-mode="detailMode"
|
:detail-mode="detailMode"
|
||||||
|
:log-detail-mode="logDetailMode"
|
||||||
:detail-alerts="detailAlerts"
|
:detail-alerts="detailAlerts"
|
||||||
:custom-range="customRange"
|
:custom-range="customRange"
|
||||||
@update:search="search = $event"
|
@update:search="search = $event"
|
||||||
@@ -45,7 +48,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FilterBar
|
<FilterBar
|
||||||
v-if="activeView !== 'overview' && activeView !== 'workbench' && activeView !== 'requests' && activeView !== 'approval' && activeView !== 'policies' && activeView !== 'audit' && activeView !== 'employees' && activeView !== 'settings'"
|
v-if="activeView !== 'overview' && activeView !== 'workbench' && activeView !== 'requests' && activeView !== 'approval' && activeView !== 'policies' && activeView !== 'audit' && activeView !== 'logs' && activeView !== 'employees' && activeView !== 'settings'"
|
||||||
:compact="activeView === 'overview'"
|
:compact="activeView === 'overview'"
|
||||||
:filters="filters"
|
:filters="filters"
|
||||||
:ranges="ranges"
|
:ranges="ranges"
|
||||||
@@ -60,6 +63,7 @@
|
|||||||
'approval-workarea': activeView === 'approval',
|
'approval-workarea': activeView === 'approval',
|
||||||
'policies-workarea': activeView === 'policies',
|
'policies-workarea': activeView === 'policies',
|
||||||
'audit-workarea': activeView === 'audit',
|
'audit-workarea': activeView === 'audit',
|
||||||
|
'logs-workarea': activeView === 'logs',
|
||||||
'employees-workarea': activeView === 'employees',
|
'employees-workarea': activeView === 'employees',
|
||||||
'settings-workarea': activeView === 'settings'
|
'settings-workarea': activeView === 'settings'
|
||||||
}"
|
}"
|
||||||
@@ -102,6 +106,8 @@
|
|||||||
<ApprovalCenterView v-else-if="activeView === 'approval'" />
|
<ApprovalCenterView v-else-if="activeView === 'approval'" />
|
||||||
<PoliciesView v-else-if="activeView === 'policies'" @summary-change="knowledgeSummary = $event" />
|
<PoliciesView v-else-if="activeView === 'policies'" @summary-change="knowledgeSummary = $event" />
|
||||||
<AuditView v-else-if="activeView === 'audit'" @detail-open-change="auditDetailOpen = $event" />
|
<AuditView v-else-if="activeView === 'audit'" @detail-open-change="auditDetailOpen = $event" />
|
||||||
|
<LogDetailView v-else-if="activeView === 'logs' && logDetailMode" />
|
||||||
|
<LogsView v-else-if="activeView === 'logs'" @summary-change="logsSummary = $event" />
|
||||||
<EmployeeManagementView v-else-if="activeView === 'employees'" @overview-change="employeeSummary = $event" />
|
<EmployeeManagementView v-else-if="activeView === 'employees'" @overview-change="employeeSummary = $event" />
|
||||||
<SettingsView v-else />
|
<SettingsView v-else />
|
||||||
</section>
|
</section>
|
||||||
@@ -135,6 +141,8 @@ import RequestsView from './RequestsView.vue'
|
|||||||
import ApprovalCenterView from './ApprovalCenterView.vue'
|
import ApprovalCenterView from './ApprovalCenterView.vue'
|
||||||
import PoliciesView from './PoliciesView.vue'
|
import PoliciesView from './PoliciesView.vue'
|
||||||
import AuditView from './AuditView.vue'
|
import AuditView from './AuditView.vue'
|
||||||
|
import LogsView from './LogsView.vue'
|
||||||
|
import LogDetailView from './LogDetailView.vue'
|
||||||
import EmployeeManagementView from './EmployeeManagementView.vue'
|
import EmployeeManagementView from './EmployeeManagementView.vue'
|
||||||
import SettingsView from './SettingsView.vue'
|
import SettingsView from './SettingsView.vue'
|
||||||
|
|
||||||
@@ -144,6 +152,7 @@ import { filterNavItemsByAccess } from '../utils/accessControl.js'
|
|||||||
|
|
||||||
const employeeSummary = ref(null)
|
const employeeSummary = ref(null)
|
||||||
const knowledgeSummary = ref(null)
|
const knowledgeSummary = ref(null)
|
||||||
|
const logsSummary = ref(null)
|
||||||
const auditDetailOpen = ref(false)
|
const auditDetailOpen = ref(false)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -154,6 +163,7 @@ const {
|
|||||||
customRange,
|
customRange,
|
||||||
detailAlerts,
|
detailAlerts,
|
||||||
detailMode,
|
detailMode,
|
||||||
|
logDetailMode,
|
||||||
filteredRequests,
|
filteredRequests,
|
||||||
filters,
|
filters,
|
||||||
handleApprove,
|
handleApprove,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
v-if="isAdmin"
|
v-if="isAdmin"
|
||||||
class="more-btn ingest"
|
class="more-btn ingest"
|
||||||
type="button"
|
type="button"
|
||||||
:disabled="Boolean(ingestingId) || deletingId === doc.id"
|
:disabled="Boolean(ingestingId) || deletingId === doc.id || Number(doc.stateCode || 0) === 2"
|
||||||
:aria-label="resolveIngestActionTitle(doc)"
|
:aria-label="resolveIngestActionTitle(doc)"
|
||||||
:title="resolveIngestActionTitle(doc)"
|
:title="resolveIngestActionTitle(doc)"
|
||||||
@click="handleManualIngest(doc)"
|
@click="handleManualIngest(doc)"
|
||||||
@@ -356,6 +356,21 @@
|
|||||||
<div v-else-if="llmWikiError" class="preview-status error">{{ llmWikiError }}</div>
|
<div v-else-if="llmWikiError" class="preview-status error">{{ llmWikiError }}</div>
|
||||||
<div v-else-if="llmWikiDocument" class="llm-wiki-grid">
|
<div v-else-if="llmWikiDocument" class="llm-wiki-grid">
|
||||||
<section class="llm-wiki-section llm-wiki-summary-section">
|
<section class="llm-wiki-section llm-wiki-summary-section">
|
||||||
|
<div
|
||||||
|
v-if="llmWikiDocument.quality_status !== 'formal'"
|
||||||
|
class="llm-wiki-alert"
|
||||||
|
:class="resolveLlmWikiQualityTone(llmWikiDocument)"
|
||||||
|
>
|
||||||
|
<strong>{{ resolveLlmWikiQualityLabel(llmWikiDocument) }}</strong>
|
||||||
|
<p>{{ llmWikiDocument.quality_note || '当前展示内容不是正式 Hermes 归纳,请人工复核后再使用。' }}</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="llmWikiDocument.quality_note"
|
||||||
|
class="llm-wiki-alert info"
|
||||||
|
>
|
||||||
|
<strong>{{ resolveLlmWikiQualityLabel(llmWikiDocument) }}</strong>
|
||||||
|
<p>{{ llmWikiDocument.quality_note }}</p>
|
||||||
|
</div>
|
||||||
<div class="llm-wiki-section-head">
|
<div class="llm-wiki-section-head">
|
||||||
<div>
|
<div>
|
||||||
<h3>知识总结</h3>
|
<h3>知识总结</h3>
|
||||||
@@ -363,6 +378,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="llm-wiki-count">{{ llmWikiDocument.knowledge_candidate_count }} 条知识</span>
|
<span class="llm-wiki-count">{{ llmWikiDocument.knowledge_candidate_count }} 条知识</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="llm-wiki-stat-grid">
|
||||||
|
<span>正文分块 {{ llmWikiDocument.candidate_chunk_count }}</span>
|
||||||
|
<span>过滤分块 {{ llmWikiDocument.filtered_chunk_count }}</span>
|
||||||
|
<span>成功分组 {{ llmWikiDocument.successful_group_count }}/{{ llmWikiDocument.group_count }}</span>
|
||||||
|
<span>正式知识 {{ llmWikiDocument.formal_knowledge_candidate_count }}</span>
|
||||||
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
v-model="llmWikiSummaryDraft"
|
v-model="llmWikiSummaryDraft"
|
||||||
class="llm-wiki-editor"
|
class="llm-wiki-editor"
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import { resolveKnowledgePreviewLayoutState } from './knowledgePreviewLayout.js'
|
|||||||
import { resolveInitialKnowledgeFolder } from './knowledgeFolderSelection.js'
|
import { resolveInitialKnowledgeFolder } from './knowledgeFolderSelection.js'
|
||||||
import { buildOnlyOfficePreviewConfig } from './onlyOfficePreviewConfig.js'
|
import { buildOnlyOfficePreviewConfig } from './onlyOfficePreviewConfig.js'
|
||||||
|
|
||||||
|
const KNOWLEDGE_POLL_INTERVAL_MS = 5000
|
||||||
|
|
||||||
function triggerFileDownload(blob, filename) {
|
function triggerFileDownload(blob, filename) {
|
||||||
const url = URL.createObjectURL(blob)
|
const url = URL.createObjectURL(blob)
|
||||||
const anchor = document.createElement('a')
|
const anchor = document.createElement('a')
|
||||||
@@ -42,6 +44,7 @@ function triggerFileDownload(blob, filename) {
|
|||||||
|
|
||||||
let bodyOverflowSnapshot = ''
|
let bodyOverflowSnapshot = ''
|
||||||
let bodyOverscrollBehaviorSnapshot = ''
|
let bodyOverscrollBehaviorSnapshot = ''
|
||||||
|
let libraryPollTimer = 0
|
||||||
|
|
||||||
function setBodyScrollLocked(isLocked) {
|
function setBodyScrollLocked(isLocked) {
|
||||||
if (typeof document === 'undefined') {
|
if (typeof document === 'undefined') {
|
||||||
@@ -281,9 +284,15 @@ export default {
|
|||||||
activeFolder.value = resolveInitialKnowledgeFolder(folders.value, activeFolder.value)
|
activeFolder.value = resolveInitialKnowledgeFolder(folders.value, activeFolder.value)
|
||||||
|
|
||||||
if (options.preserveSelection && selectedDocument.value?.id) {
|
if (options.preserveSelection && selectedDocument.value?.id) {
|
||||||
const exists = documents.value.some((doc) => doc.id === selectedDocument.value.id)
|
const nextDocument = documents.value.find((doc) => doc.id === selectedDocument.value.id)
|
||||||
|
const exists = Boolean(nextDocument)
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
closePreview()
|
closePreview()
|
||||||
|
} else {
|
||||||
|
selectedDocument.value = {
|
||||||
|
...selectedDocument.value,
|
||||||
|
...nextDocument
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.preserveSelection && llmWikiDocument.value?.document_id) {
|
if (options.preserveSelection && llmWikiDocument.value?.document_id) {
|
||||||
@@ -292,6 +301,7 @@ export default {
|
|||||||
closeLlmWikiSummary()
|
closeLlmWikiSummary()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
syncLibraryPolling()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
emit('summary-change', { totalDocuments: 0 })
|
emit('summary-change', { totalDocuments: 0 })
|
||||||
toast(error.message || '知识库加载失败。')
|
toast(error.message || '知识库加载失败。')
|
||||||
@@ -351,10 +361,37 @@ export default {
|
|||||||
...patch
|
...patch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
syncLibraryPolling()
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasSyncingDocuments() {
|
||||||
|
return documents.value.some((doc) => Number(doc?.stateCode || 0) === 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopLibraryPolling() {
|
||||||
|
if (libraryPollTimer) {
|
||||||
|
window.clearInterval(libraryPollTimer)
|
||||||
|
libraryPollTimer = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLibraryPolling() {
|
||||||
|
stopLibraryPolling()
|
||||||
|
libraryPollTimer = window.setInterval(() => {
|
||||||
|
loadLibrary({ preserveSelection: true })
|
||||||
|
}, KNOWLEDGE_POLL_INTERVAL_MS)
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncLibraryPolling() {
|
||||||
|
if (hasSyncingDocuments()) {
|
||||||
|
startLibraryPolling()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stopLibraryPolling()
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveIngestActionLabel(document) {
|
function resolveIngestActionLabel(document) {
|
||||||
if (ingestingId.value === document.id) {
|
if (ingestingId.value === document.id || Number(document?.stateCode || 0) === 2) {
|
||||||
return '归纳中'
|
return '归纳中'
|
||||||
}
|
}
|
||||||
return Number(document?.stateCode || 0) === 3 ? '重新归纳' : '归纳'
|
return Number(document?.stateCode || 0) === 3 ? '重新归纳' : '归纳'
|
||||||
@@ -372,27 +409,64 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function canViewLlmWiki(document) {
|
function canViewLlmWiki(document) {
|
||||||
return isAdmin.value && Number(document?.stateCode || 0) === 3
|
return isAdmin.value && Boolean(document?.llmWikiAvailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveViewLlmWikiTitle(document) {
|
function resolveViewLlmWikiTitle(document) {
|
||||||
if (!isAdmin.value) {
|
if (!isAdmin.value) {
|
||||||
return '仅管理员可查看 LLM Wiki 归纳内容'
|
return '仅管理员可查看 LLM Wiki 归纳内容'
|
||||||
}
|
}
|
||||||
|
if (document?.llmWikiAvailable && Number(document?.stateCode || 0) === 4) {
|
||||||
|
return '查看本次降级归纳结果,仅供人工排查,不能视为正式知识'
|
||||||
|
}
|
||||||
|
if (document?.llmWikiAvailable && document?.llmWikiQualityStatus === 'partial_degraded') {
|
||||||
|
return '查看当前归纳结果,存在部分降级分组,请人工复核'
|
||||||
|
}
|
||||||
|
if (document?.llmWikiAvailable) {
|
||||||
|
return '查看并编辑当前文档的 LLM Wiki 归纳内容'
|
||||||
|
}
|
||||||
if (Number(document?.stateCode || 0) === 2) {
|
if (Number(document?.stateCode || 0) === 2) {
|
||||||
return 'Hermes 正在归纳当前文档,完成后可查看 LLM Wiki 知识总结'
|
return 'Hermes 正在归纳当前文档,完成后可查看 LLM Wiki 知识总结'
|
||||||
}
|
}
|
||||||
if (Number(document?.stateCode || 0) === 4) {
|
if (Number(document?.stateCode || 0) === 4) {
|
||||||
return '当前文档上次归纳失败,请重新归纳后再查看'
|
return '当前文档上次归纳失败,且没有可查看的 LLM Wiki 产物'
|
||||||
}
|
}
|
||||||
if (Number(document?.stateCode || 0) !== 3) {
|
if (Number(document?.stateCode || 0) !== 3) {
|
||||||
return '文档尚未完成归纳,暂无可查看的 LLM Wiki 知识总结'
|
return '文档尚未完成归纳,暂无可查看的 LLM Wiki 知识总结'
|
||||||
}
|
}
|
||||||
return '查看并编辑当前文档的 LLM Wiki 归纳内容'
|
return '查看当前文档的 LLM Wiki 归纳内容'
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLlmWikiQualityLabel(document) {
|
||||||
|
const qualityStatus = String(document?.quality_status || '').trim()
|
||||||
|
if (qualityStatus === 'partial_degraded') {
|
||||||
|
return '部分降级'
|
||||||
|
}
|
||||||
|
if (qualityStatus === 'fallback_only') {
|
||||||
|
return '降级兜底'
|
||||||
|
}
|
||||||
|
if (qualityStatus === 'runtime_only') {
|
||||||
|
return '非 Hermes 结果'
|
||||||
|
}
|
||||||
|
if (qualityStatus === 'failed') {
|
||||||
|
return '归纳失败'
|
||||||
|
}
|
||||||
|
return '正式归纳'
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveLlmWikiQualityTone(document) {
|
||||||
|
const qualityStatus = String(document?.quality_status || '').trim()
|
||||||
|
if (qualityStatus === 'formal') {
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
if (qualityStatus === 'partial_degraded') {
|
||||||
|
return 'warning'
|
||||||
|
}
|
||||||
|
return 'danger'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleManualIngest(document) {
|
async function handleManualIngest(document) {
|
||||||
if (!isAdmin.value || ingestingId.value || !document?.id) {
|
if (!isAdmin.value || ingestingId.value || !document?.id || Number(document?.stateCode || 0) === 2) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,19 +480,13 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const payload = await syncKnowledgeDocumentToLlmWiki({
|
const payload = await syncKnowledgeDocumentToLlmWiki({
|
||||||
folder: document.folder,
|
folder: document.folder,
|
||||||
documentId: document.id
|
documentId: document.id,
|
||||||
|
force: true
|
||||||
})
|
})
|
||||||
await loadLibrary({ preserveSelection: true })
|
await loadLibrary({ preserveSelection: true })
|
||||||
if (selectedDocument.value?.id === document.id) {
|
toast(payload.summary || 'Hermes 已进入后台归纳。')
|
||||||
await selectDocument(document.id)
|
|
||||||
}
|
|
||||||
toast(payload.summary || 'Hermes 已完成文档归纳。')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
patchDocumentState(document.id, {
|
await loadLibrary({ preserveSelection: true })
|
||||||
stateCode: 4,
|
|
||||||
state: '归纳失败',
|
|
||||||
stateTone: 'danger'
|
|
||||||
})
|
|
||||||
toast(error.message || 'Hermes 归纳文档失败。')
|
toast(error.message || 'Hermes 归纳文档失败。')
|
||||||
} finally {
|
} finally {
|
||||||
ingestingId.value = ''
|
ingestingId.value = ''
|
||||||
@@ -654,6 +722,7 @@ export default {
|
|||||||
revokePreviewBlob()
|
revokePreviewBlob()
|
||||||
destroyOnlyOfficeEditor()
|
destroyOnlyOfficeEditor()
|
||||||
setBodyScrollLocked(false)
|
setBodyScrollLocked(false)
|
||||||
|
stopLibraryPolling()
|
||||||
window.removeEventListener('keydown', handleWindowKeydown)
|
window.removeEventListener('keydown', handleWindowKeydown)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -711,6 +780,8 @@ export default {
|
|||||||
selectedDocument,
|
selectedDocument,
|
||||||
resolveIngestActionLabel,
|
resolveIngestActionLabel,
|
||||||
resolveIngestActionTitle,
|
resolveIngestActionTitle,
|
||||||
|
resolveLlmWikiQualityLabel,
|
||||||
|
resolveLlmWikiQualityTone,
|
||||||
resolveViewLlmWikiTitle,
|
resolveViewLlmWikiTitle,
|
||||||
saveLlmWikiSummary,
|
saveLlmWikiSummary,
|
||||||
totalCount,
|
totalCount,
|
||||||
|
|||||||
Reference in New Issue
Block a user