feat(web): 差旅申请详情进度 viewer 与审批/加载态组件增强
- 新增 requestProgressViewer,申请单在直属领导审批视角下将当前步骤展示为'等待批复',travel-request-detail/app-shell/useRequests 接入 - TravelRequestApprovalDialog 增强审批交互,TableLoadingState 补充表格加载占位,ConfirmDialog 扩展确认对话框能力 - useAppShell/useRequests/AppShellRouteView 配套适配申请详情跳转与会话状态 - 同步更新 requestProgressSteps、travel-request-detail-leader-approval、assistant-session-draft-delete、documents-center-status-filter、app-shell-financial-assistant-entry、request-progress-viewer 等测试
This commit is contained in:
@@ -97,7 +97,7 @@ test('workbench summary merges approval inbox requests without polluting documen
|
||||
assert.match(appShellComposable, /const workbenchRequests = computed\(\(\) =>[\s\S]*mergeWorkbenchRequests\(requests\.value, workbenchApprovalRequests\.value\)/)
|
||||
assert.match(appShellComposable, /buildWorkbenchSummary\(workbenchRequests\.value, currentUser\.value\)/)
|
||||
assert.match(appShellRouteView, /<DocumentsCenterView[\s\S]*:filtered-requests="requests"/)
|
||||
assert.doesNotMatch(appShellRouteView, /<DocumentsCenterView[\s\S]*workbenchRequests/)
|
||||
assert.doesNotMatch(appShellRouteView, /<DocumentsCenterView(?:(?!\/>)[\s\S])*workbenchRequests/)
|
||||
})
|
||||
|
||||
test('workbench progress refreshes after homepage create or detail updates', () => {
|
||||
|
||||
@@ -98,9 +98,9 @@ test('deleting an application draft marks AI workbench detail links as unavailab
|
||||
content: [
|
||||
'### 申请草稿已保存',
|
||||
'',
|
||||
'| 单据类型 | 单据编号 | 单据状态 | 当前节点 | 操作 |',
|
||||
'| --- | --- | --- | --- | --- |',
|
||||
'| 出差申请 | AP-20260620-DRAFT | 草稿 | 待提交 | [查看](#ai-open-application-detail:claim_id%3Dclaim-draft-1%26claim_no%3DAP-20260620-DRAFT) |'
|
||||
'| 单据类型 | 单据编号 | 单据状态 | 当前节点 | 日期 | 地点 | 事由 | 金额 | 操作 |',
|
||||
'| --- | --- | --- | --- | --- | --- | --- | --- | --- |',
|
||||
'| 出差申请 | AP-20260620-DRAFT | 草稿 | 待提交 | 2026-02-20 至 2026-02-23 | 上海 | 辅助国网仿生产服务器部署 | ¥2,600.00 | [查看](#ai-open-application-detail:claim_id%3Dclaim-draft-1%26claim_no%3DAP-20260620-DRAFT) |'
|
||||
].join('\n')
|
||||
}
|
||||
]
|
||||
|
||||
@@ -16,6 +16,10 @@ const documentListSharedStyles = readFileSync(
|
||||
fileURLToPath(new URL('../src/assets/styles/components/document-list-shared.css', import.meta.url)),
|
||||
'utf8'
|
||||
)
|
||||
const tableLoadingState = readFileSync(
|
||||
fileURLToPath(new URL('../src/components/shared/TableLoadingState.vue', import.meta.url)),
|
||||
'utf8'
|
||||
)
|
||||
const reimbursementService = readFileSync(
|
||||
fileURLToPath(new URL('../src/services/reimbursements.js', import.meta.url)),
|
||||
'utf8'
|
||||
@@ -38,6 +42,16 @@ test('documents center keeps only the top scope tabs and renders risk level as a
|
||||
assert.match(documentsCenterView, /aria-label="风险等级"/)
|
||||
})
|
||||
|
||||
test('documents center loading state uses a compact spinner instead of light band progress', () => {
|
||||
assert.match(documentsCenterView, /<TableLoadingState[\s\S]*title="单据数据同步中"[\s\S]*floating/)
|
||||
assert.match(tableLoadingState, /class="table-loading-spinner"/)
|
||||
assert.match(tableLoadingState, /@keyframes table-loading-spin/)
|
||||
assert.match(tableLoadingState, /\.table-loading-card \{[\s\S]*width: min\(420px, 100%\);[\s\S]*display: inline-flex;/)
|
||||
assert.match(tableLoadingState, /\.table-loading-spinner \{[\s\S]*border-top-color: var\(--theme-primary-active/)
|
||||
assert.match(tableLoadingState, /@media \(prefers-reduced-motion: reduce\) \{[\s\S]*animation: none;/)
|
||||
assert.doesNotMatch(tableLoadingState, /FloatingLightBandWindow/)
|
||||
})
|
||||
|
||||
test('documents center top tabs start from all and show document category labels', () => {
|
||||
assert.match(documentsCenterView, /const DOCUMENT_SCOPE_ALL = '全部'/)
|
||||
assert.match(documentsCenterView, /const DOCUMENT_SCOPE_APPLICATION = '申请单'/)
|
||||
|
||||
49
web/tests/request-progress-viewer.test.mjs
Normal file
49
web/tests/request-progress-viewer.test.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { resolveProgressStepsForViewer } from '../src/utils/requestProgressViewer.js'
|
||||
|
||||
test('progress viewer keeps approver name for applicant view', () => {
|
||||
const steps = [
|
||||
{
|
||||
label: '等待 李经理 批复',
|
||||
rawLabel: '直属领导审批',
|
||||
current: true,
|
||||
title: '当前等待 李经理 批复已停留 3小时15分钟'
|
||||
}
|
||||
]
|
||||
|
||||
assert.deepEqual(
|
||||
resolveProgressStepsForViewer(steps, {
|
||||
isApplicationDocument: true,
|
||||
isCurrentDirectManagerApprover: false
|
||||
}),
|
||||
steps
|
||||
)
|
||||
})
|
||||
|
||||
test('progress viewer hides approver name for current direct manager approval view', () => {
|
||||
const steps = [
|
||||
{
|
||||
label: '等待 李经理 批复',
|
||||
rawLabel: '直属领导审批',
|
||||
current: true,
|
||||
title: '当前等待 李经理 批复已停留 3小时15分钟'
|
||||
}
|
||||
]
|
||||
|
||||
assert.deepEqual(
|
||||
resolveProgressStepsForViewer(steps, {
|
||||
isApplicationDocument: true,
|
||||
isCurrentDirectManagerApprover: true
|
||||
}),
|
||||
[
|
||||
{
|
||||
label: '等待批复',
|
||||
rawLabel: '直属领导审批',
|
||||
current: true,
|
||||
title: '当前等待批复已停留 3小时15分钟'
|
||||
}
|
||||
]
|
||||
)
|
||||
})
|
||||
@@ -19,7 +19,7 @@ const WAIT_SUBMIT = '\u5f85\u63d0\u4ea4'
|
||||
const LINKED_APPLICATION = '\u5173\u8054\u5355\u636e'
|
||||
const PAID = '\u5df2\u4ed8\u6b3e'
|
||||
const ARCHIVED = '\u5df2\u5f52\u6863'
|
||||
const WAIT_LEADER_LI_APPROVAL = '\u7b49\u5f85 Leader Li \u6279\u590d'
|
||||
const WAIT_APPROVAL = '\u7b49\u5f85\u6279\u590d'
|
||||
const WAIT_BUDGET_ZHAO_APPROVAL = '\u7b49\u5f85 \u8d75\u9884\u7b97 \u6279\u590d'
|
||||
const WAIT_BUDGET_P8_EXECUTIVE_APPROVAL = '\u7b49\u5f85 P8 Executive \u6279\u590d'
|
||||
const WAIT_FINANCE_FIONA_APPROVAL = '\u7b49\u5f85 Fiona Finance \u6279\u590d'
|
||||
@@ -160,14 +160,14 @@ test('application claims are mapped as application documents', () => {
|
||||
assert.equal(request.expenseTableSummary, '预计金额已随申请提交')
|
||||
assert.deepEqual(
|
||||
request.progressSteps.map((step) => step.label),
|
||||
[CREATE_APPLICATION, WAIT_LEADER_LI_APPROVAL, APPLICATION_LINK_STATUS, ARCHIVED]
|
||||
[CREATE_APPLICATION, '等待 Leader Li 批复', APPLICATION_LINK_STATUS, ARCHIVED]
|
||||
)
|
||||
assert.equal(request.progressSteps.some((step) => step.label === 'AI预审'), false)
|
||||
assert.equal(request.progressSteps.some((step) => step.label === '财务审批'), false)
|
||||
assert.equal(request.progressSteps.some((step) => step.label === DIRECT_MANAGER_APPROVAL), false)
|
||||
assert.equal(request.progressSteps.some((step) => step.label === BUDGET_MANAGER_APPROVAL), false)
|
||||
assert.equal(request.progressSteps.find((step) => step.label === WAIT_LEADER_LI_APPROVAL)?.rawLabel, DIRECT_MANAGER_APPROVAL)
|
||||
assert.equal(request.progressSteps.find((step) => step.label === WAIT_LEADER_LI_APPROVAL)?.current, true)
|
||||
assert.equal(request.progressSteps.find((step) => step.label === '等待 Leader Li 批复')?.rawLabel, DIRECT_MANAGER_APPROVAL)
|
||||
assert.equal(request.progressSteps.find((step) => step.label === '等待 Leader Li 批复')?.current, true)
|
||||
})
|
||||
|
||||
test('travel application detail splits trip time into departure and return rows', () => {
|
||||
@@ -1241,7 +1241,8 @@ test('current direct manager step shows how long the claim has stayed there', ()
|
||||
|
||||
assert.equal(submitStep.time, '王五提交')
|
||||
assert.match(submitStep.detail, /2026-05-20/)
|
||||
assert.equal(leaderStep.label, '等待 李经理 批复')
|
||||
assert.equal(leaderStep.label, '等待批复')
|
||||
assert.doesNotMatch(leaderStep.label, /李经理/)
|
||||
assert.equal(leaderStep.rawLabel, '直属领导审批')
|
||||
assert.equal(leaderStep.current, true)
|
||||
assert.equal(leaderStep.time, '停留 3小时15分钟')
|
||||
|
||||
@@ -146,6 +146,8 @@ test('approval-mode detail collects leader opinion inside confirm dialog before
|
||||
assert.match(detailTemplate, /v-model:risk-confirmed="approvalRiskConfirmed"/)
|
||||
assert.match(detailTemplate, /:risk-confirm-items="approvalRiskConfirmItems"/)
|
||||
assert.doesNotMatch(detailTemplate, /:next-stage="approvalNextStage"/)
|
||||
assert.match(approvalDialog, /size="approval"/)
|
||||
assert.match(approvalDialog, /actions-align="end"/)
|
||||
assert.doesNotMatch(approvalDialog, /submit-confirm-summary/)
|
||||
assert.doesNotMatch(approvalDialog, /单据编号/)
|
||||
assert.doesNotMatch(approvalDialog, /当前节点/)
|
||||
@@ -184,8 +186,14 @@ test('approval-mode detail collects leader opinion inside confirm dialog before
|
||||
assert.match(approvalDialog, /update:risk-confirmed/)
|
||||
assert.match(approvalDialog, /:confirm-disabled="confirmDisabled"/)
|
||||
assert.match(approvalDialog, /props\.opinionRequired && !currentOpinion\.value\.trim\(\)/)
|
||||
assert.match(approvalDialog, /\.approval-opinion-field \{[\s\S]*gap: 6px;[\s\S]*margin-top: 8px;/)
|
||||
assert.match(approvalDialog, /\.approval-opinion-field textarea \{[\s\S]*min-height: 74px;/)
|
||||
assert.match(confirmDialog, /confirmDisabled:\s*\{\s*type:\s*Boolean,\s*default:\s*false\s*\}/)
|
||||
assert.match(confirmDialog, /:disabled="busy \|\| confirmDisabled"/)
|
||||
assert.match(confirmDialog, /\.shared-confirm-card--approval \{[\s\S]*width: min\(460px, calc\(100vw - 40px\)\);/)
|
||||
assert.match(confirmDialog, /\.shared-confirm-card--approval h4 \{[\s\S]*font-size: 20px;/)
|
||||
assert.match(confirmDialog, /\.shared-confirm-card--approval \.shared-confirm-body \{[\s\S]*max-height: min\(270px, calc\(100dvh - 238px\)\);/)
|
||||
assert.match(confirmDialog, /\.shared-confirm-card--approval \.shared-confirm-btn \{[\s\S]*min-width: 118px;[\s\S]*min-height: 38px;/)
|
||||
|
||||
assert.match(detailStyles, /\.detail-card-title-with-icon \{[\s\S]*display: inline-flex;[\s\S]*align-items: center;[\s\S]*gap: 8px;/)
|
||||
assert.match(detailStyles, /\.detail-card-title-with-icon i \{[\s\S]*font-size: 18px;[\s\S]*line-height: 1;/)
|
||||
|
||||
Reference in New Issue
Block a user