feat: 报销审批流重构与管家计划全链路贯通
- 重构报销状态注册表、审批流路由与平台风险标记 - 完善管家意图规划器与模型计划构建器全链路 - 新增 OCR Worker 脚本、数据库会话管理与通知状态 - 优化文档中心、日志视图、预算中心与员工管理交互 - 增强工作台摘要、图标资源与全局主题样式 - 补充审批路由、状态注册、OCR 服务与管家规划器测试覆盖
This commit is contained in:
@@ -248,8 +248,25 @@ import TableEmptyState from '../components/shared/TableEmptyState.vue'
|
||||
import TableLoadingState from '../components/shared/TableLoadingState.vue'
|
||||
import { useMinimumVisibleState } from '../composables/useMinimumVisibleState.js'
|
||||
import { mapExpenseClaimToRequest } from '../composables/useRequests.js'
|
||||
import { fetchApprovalExpenseClaims, fetchArchivedExpenseClaims } from '../services/reimbursements.js'
|
||||
import { countNewDocuments, isNewDocument, markDocumentViewed, markDocumentsViewed, readDocumentScope, readViewedDocumentKeys, writeDocumentScope } from '../utils/documentCenterNewState.js'
|
||||
import {
|
||||
REIMBURSEMENT_LIST_PREVIEW_PARAMS,
|
||||
extractExpenseClaimItems,
|
||||
fetchApprovalExpenseClaims,
|
||||
fetchArchivedExpenseClaims
|
||||
} from '../services/reimbursements.js'
|
||||
import { fetchNotificationStates, patchNotificationStates } from '../services/notificationStates.js'
|
||||
import {
|
||||
buildDocumentViewedStatePatch,
|
||||
buildDocumentsViewedStatePatches,
|
||||
countNewDocuments,
|
||||
isNewDocument,
|
||||
markDocumentViewed,
|
||||
markDocumentsViewed,
|
||||
mergeNotificationStatesIntoViewedDocumentKeys,
|
||||
readDocumentScope,
|
||||
readViewedDocumentKeys,
|
||||
writeDocumentScope
|
||||
} from '../utils/documentCenterNewState.js'
|
||||
import { sortDocumentRowsByLatestTime } from '../utils/documentCenterSort.js'
|
||||
import { extractDateText, formatDocumentListTime, resolveDocumentSortTime, resolveDocumentStayTimeDisplay } from '../utils/documentCenterTime.js'
|
||||
import { excludeArchivedDocumentRows, filterApplicationScopeNewRows, isArchivedDocumentRow, prepareApplicationScopeRows } from '../utils/documentCenterRows.js'
|
||||
@@ -860,9 +877,36 @@ function changePageSize(size) {
|
||||
currentPage.value = 1
|
||||
}
|
||||
|
||||
function applyRemoteViewedDocumentStates(states) {
|
||||
viewedDocumentKeys.value = mergeNotificationStatesIntoViewedDocumentKeys(states, viewedDocumentKeys.value)
|
||||
}
|
||||
|
||||
async function loadRemoteViewedDocumentKeys() {
|
||||
try {
|
||||
applyRemoteViewedDocumentStates(await fetchNotificationStates())
|
||||
} catch {
|
||||
// 接口不可用时保留本机已读缓存,避免影响单据中心主流程。
|
||||
}
|
||||
}
|
||||
|
||||
async function syncDocumentViewedPatches(patches) {
|
||||
const normalizedPatches = (Array.isArray(patches) ? patches : [patches]).filter(Boolean)
|
||||
if (!normalizedPatches.length) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
applyRemoteViewedDocumentStates(await patchNotificationStates(normalizedPatches))
|
||||
} catch {
|
||||
// 本机状态已先落地;远端失败时等待下次操作或刷新重试。
|
||||
}
|
||||
}
|
||||
|
||||
function openDocument(row) {
|
||||
writeDocumentScope(activeScopeTab.value, scopeTabs)
|
||||
const viewedPatch = buildDocumentViewedStatePatch(row)
|
||||
viewedDocumentKeys.value = markDocumentViewed(row, viewedDocumentKeys.value)
|
||||
void syncDocumentViewedPatches([viewedPatch])
|
||||
emit('open-document', row.rawRequest || row)
|
||||
}
|
||||
|
||||
@@ -871,7 +915,9 @@ function markAllDocumentsRead() {
|
||||
return
|
||||
}
|
||||
|
||||
const viewedPatches = buildDocumentsViewedStatePatches(allReadableDocumentRows.value, viewedDocumentKeys.value)
|
||||
viewedDocumentKeys.value = markDocumentsViewed(allReadableDocumentRows.value, viewedDocumentKeys.value)
|
||||
void syncDocumentViewedPatches(viewedPatches)
|
||||
}
|
||||
|
||||
async function loadSupportingRows() {
|
||||
@@ -879,30 +925,26 @@ async function loadSupportingRows() {
|
||||
supportingError.value = ''
|
||||
|
||||
const [approvalResult, archiveResult] = await Promise.allSettled([
|
||||
fetchApprovalExpenseClaims(),
|
||||
fetchArchivedExpenseClaims()
|
||||
fetchApprovalExpenseClaims(REIMBURSEMENT_LIST_PREVIEW_PARAMS),
|
||||
fetchArchivedExpenseClaims(REIMBURSEMENT_LIST_PREVIEW_PARAMS)
|
||||
])
|
||||
|
||||
if (approvalResult.status === 'fulfilled') {
|
||||
approvalRows.value = excludeArchivedDocumentRows(
|
||||
Array.isArray(approvalResult.value)
|
||||
? approvalResult.value
|
||||
extractExpenseClaimItems(approvalResult.value)
|
||||
.map((item) => mapExpenseClaimToRequest(item))
|
||||
.map((item) => buildDocumentRow(item, { source: 'approval' }))
|
||||
.filter(Boolean)
|
||||
: []
|
||||
)
|
||||
} else {
|
||||
approvalRows.value = []
|
||||
}
|
||||
|
||||
if (archiveResult.status === 'fulfilled') {
|
||||
archiveRows.value = Array.isArray(archiveResult.value)
|
||||
? archiveResult.value
|
||||
.map((item) => mapExpenseClaimToRequest(item))
|
||||
.map((item) => buildDocumentRow(item, { source: 'archive', archived: true }))
|
||||
.filter(Boolean)
|
||||
: []
|
||||
archiveRows.value = extractExpenseClaimItems(archiveResult.value)
|
||||
.map((item) => mapExpenseClaimToRequest(item))
|
||||
.map((item) => buildDocumentRow(item, { source: 'archive', archived: true }))
|
||||
.filter(Boolean)
|
||||
} else {
|
||||
archiveRows.value = []
|
||||
supportingError.value = archiveResult.reason instanceof Error
|
||||
@@ -915,6 +957,7 @@ async function loadSupportingRows() {
|
||||
|
||||
function reloadAll() {
|
||||
emit('reload')
|
||||
void loadRemoteViewedDocumentKeys()
|
||||
void loadSupportingRows()
|
||||
}
|
||||
|
||||
@@ -963,6 +1006,7 @@ watch(documentSummary, (summary) => {
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
void loadRemoteViewedDocumentKeys()
|
||||
void loadSupportingRows()
|
||||
})
|
||||
|
||||
@@ -970,6 +1014,7 @@ watch(
|
||||
() => props.refreshToken,
|
||||
(token, previousToken) => {
|
||||
if (token && token !== previousToken) {
|
||||
void loadRemoteViewedDocumentKeys()
|
||||
void loadSupportingRows()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user