feat: 优化差旅报销预审流程与个人工作台 UI 体系

- 完善 user_agent_application 申请差旅报销预审槽位与消息组装
- 增强预算助理报告与风险建议卡片交互
- 重构登录页视觉样式与移动端响应式适配
- 优化个人工作台、文档中心、政策中心、员工管理等页面布局
- 拆分 travelRequestDetailPreReviewModel 为 advice/submit 模型
- 补充报销草稿、风险复核、Item Sync 与模板执行器测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-02 14:01:51 +08:00
parent 92444e7eae
commit ca691f3ee0
107 changed files with 5663 additions and 1542 deletions

View File

@@ -69,6 +69,10 @@ const guidedFlowScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementGuidedFlow.js', import.meta.url)),
'utf8'
)
const guidedModelScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/travelReimbursementGuidedFlowModel.js', import.meta.url)),
'utf8'
)
const sessionStateScript = readFileSync(
fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSessionState.js', import.meta.url)),
'utf8'
@@ -226,6 +230,7 @@ test('guided reimbursement requires application selection for travel and enterta
reason: '去上海支持项目部署',
location: '上海',
amount: 1800,
occurred_at: '2026-05-20T08:00:00Z',
status: 'approved',
created_at: '2026-05-20T08:00:00Z'
},
@@ -277,14 +282,25 @@ test('guided reimbursement requires application selection for travel and enterta
assert.equal(state.applicationCandidates[0].claim_no, 'AP-202605-001')
state = selectGuidedRequiredApplication(state, actions[0].payload)
assert.equal(state.stepKey, 'reason')
assert.equal(state.stepKey, 'summary')
assert.equal(isGuidedReimbursementReadyForReview(state), true)
assert.equal(state.values.application_claim_no, 'AP-202605-001')
assert.match(buildGuidedReimbursementSummaryText(state), /关联申请单AP-202605-001/)
const summaryText = buildGuidedReimbursementSummaryText(state)
assert.match(summaryText, /关联申请单AP-202605-001/)
assert.match(summaryText, /草稿详情中上传对应票据/)
assert.doesNotMatch(summaryText, /事由:待补充/)
const submitOptions = buildGuidedReviewSubmitOptions(state)
assert.equal(submitOptions.extraContext.review_action, 'save_draft')
assert.equal(submitOptions.extraContext.review_form_values.application_claim_no, 'AP-202605-001')
assert.equal(submitOptions.extraContext.review_form_values.reason, '去上海支持项目部署')
assert.equal(submitOptions.extraContext.review_form_values.business_location, '上海')
assert.equal(submitOptions.extraContext.review_form_values.amount, '')
assert.equal(submitOptions.extraContext.review_form_values.application_amount, '1800')
assert.equal(submitOptions.extraContext.review_form_values.application_business_time, '2026-05-20')
assert.equal(submitOptions.extraContext.expense_scene_selection.application_claim_no, 'AP-202605-001')
assert.match(submitOptions.rawText, /关联申请单AP-202605-001/)
assert.doesNotMatch(submitOptions.rawText, /事由:待补充/)
})
test('guided reimbursement interrupts suspicious questions before expensive flow', () => {
@@ -356,9 +372,11 @@ test('guided flow state is serializable and restored through session state', ()
test('guided flow is local until final confirmation or collected query handoff', () => {
assert.doesNotMatch(guidedFlowScript, /runOrchestrator/)
assert.doesNotMatch(guidedFlowScript, /startExpenseClaimDraftFlowStep/)
assert.doesNotMatch(guidedFlowScript, /review_action:\s*['"]save_draft['"]/)
assert.match(guidedModelScript, /review_action:\s*['"]save_draft['"]/)
assert.match(guidedFlowScript, /fetchExpenseClaims/)
assert.match(guidedFlowScript, /GUIDED_ACTION_SELECT_REQUIRED_APPLICATION/)
assert.match(guidedFlowScript, /isGuidedReimbursementReadyForReview\(guidedFlowState\.value\)[\s\S]*pushReimbursementSummary\(\)/)
assert.match(guidedFlowScript, /isGuidedReimbursementReadyForReview\(currentState\) && fileNames\.length[\s\S]*buildGuidedReviewSubmitOptions\(currentState, mergedFiles\)[\s\S]*skipDraftAssociationPrompt:\s*true[\s\S]*skipUserMessage:\s*true[\s\S]*submitExistingComposer\(submitOptions\)/)
assert.match(guidedFlowScript, /if \(!applications\.length\) \{[\s\S]*guidedFlowState\.value = createEmptyGuidedFlowState\(\)[\s\S]*meta: \['缺少可关联申请单'\][\s\S]*\}\)/)
assert.doesNotMatch(guidedFlowScript, /meta: \['缺少可关联申请单'\],[\s\S]{0,120}suggestedActions: buildGuidedExpenseTypeActions\(\)/)
assert.match(guidedFlowScript, /handleSceneSelectionApplicationGate/)