fix: 优化报销创建页面样式与洞察面板交互

修复侧边栏和审计视图样式细节,完善差旅报销洞察面板和消息
组件布局,优化报销创建页面会话管理和流程状态持久化,增强
申请预览工具函数和导航图标,补充单元测试。
This commit is contained in:
caoxiaozhu
2026-05-27 10:32:08 +08:00
parent 2dcc72102d
commit b1a9c8a194
21 changed files with 922 additions and 148 deletions

View File

@@ -100,6 +100,16 @@ export function useTravelReimbursementFlow({
const runningFlowStep = computed(
() => flowSteps.value.find((step) => step.status === FLOW_STEP_STATUS_RUNNING) || null
)
const visibleFlowSteps = computed(() => {
const visibleSteps = []
for (const step of flowSteps.value) {
visibleSteps.push(step)
if (step.status !== FLOW_STEP_STATUS_COMPLETED) {
break
}
}
return visibleSteps
})
const flowOverallStatusTone = computed(() => {
if (flowSteps.value.some((step) => step.status === FLOW_STEP_STATUS_FAILED)) {
return 'failed'
@@ -209,7 +219,8 @@ export function useTravelReimbursementFlow({
durationMs: normalizedPatch.durationMs ?? null,
startedAt: normalizedPatch.startedAt || 0,
finishedAt: normalizedPatch.finishedAt || 0,
error: normalizedPatch.error || ''
error: normalizedPatch.error || '',
deferredCompletion: Boolean(normalizedPatch.deferredCompletion)
}
}
@@ -262,8 +273,15 @@ export function useTravelReimbursementFlow({
startedAt,
finishedAt: now,
durationMs: hasExplicitDuration ? explicitDuration : Math.max(0, now - startedAt),
error: ''
error: '',
deferredCompletion: false
})
if (
flowSteps.value.length
&& flowSteps.value.every((step) => [FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED].includes(step.status))
) {
flowFinishedAt.value = now
}
}
function failFlowStep(key, detail = '', error = '', patch = {}) {
@@ -291,19 +309,18 @@ export function useTravelReimbursementFlow({
const normalizedDuration = Number(durationMs)
const hasMeasuredDuration = Number.isFinite(normalizedDuration) && normalizedDuration > 0
if (!currentStep || currentStep.status === FLOW_STEP_STATUS_PENDING) {
if (!hasMeasuredDuration && !currentStep?.startedAt) {
upsertFlowStep(key, {
...patch,
status: FLOW_STEP_STATUS_COMPLETED,
detail: detail || findFlowDefinition(key)?.completedText || '',
startedAt: 0,
finishedAt: 0,
durationMs: null,
error: ''
})
return
}
startFlowStep(key, patch)
const revealOrder = flowSteps.value.length
startFlowStep(key, { ...patch, deferredCompletion: true })
const completionTimer = window.setTimeout(() => {
completeFlowStep(
key,
detail || findFlowDefinition(key)?.completedText || '',
hasMeasuredDuration ? normalizedDuration : null,
{ deferredCompletion: false }
)
}, 280 + Math.min(revealOrder, 4) * 180)
flowSimulationTimers.push(completionTimer)
return
}
completeFlowStep(key, detail, hasMeasuredDuration ? normalizedDuration : null, patch)
}
@@ -606,16 +623,18 @@ export function useTravelReimbursementFlow({
const sceneSelectionPending = isExpenseSceneSelectionResult(payload)
flowSteps.value
.filter((step) => ![FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED].includes(step.status))
.filter((step) => !step.deferredCompletion)
.forEach((step) => {
const detail = sceneSelectionPending && step.key === 'expense-scene-selection'
? '已暂停后续识别,请先在主对话中选择报销场景。'
: resolveFlowStepDetail({ ...step, status: FLOW_STEP_STATUS_COMPLETED })
completeFlowStep(step.key, detail)
})
flowFinishedAt.value = Date.now()
if (reviewDrawerMode.value === REVIEW_DRAWER_MODE_FLOW && !sceneSelectionPending) {
reviewDrawerMode.value = REVIEW_DRAWER_MODE_REVIEW
}
flowFinishedAt.value = flowSteps.value.some(
(step) => ![FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED].includes(step.status)
)
? 0
: Date.now()
}
async function refreshFlowRunDetail() {
@@ -679,6 +698,7 @@ export function useTravelReimbursementFlow({
flowStartedAt,
flowFinishedAt,
flowSteps,
visibleFlowSteps,
flowRefreshBusy,
flowTick,
completedFlowStepCount,