feat: 同步报销流程与工作台改动

This commit is contained in:
caoxiaozhu
2026-06-09 08:32:00 +00:00
parent e124e4bbcb
commit 25724c354f
64 changed files with 6518 additions and 687 deletions

View File

@@ -13,6 +13,14 @@ const workbench = readFileSync(
fileURLToPath(new URL('../src/components/business/PersonalWorkbench.vue', import.meta.url)),
'utf8'
)
const workbenchProgressPanel = readFileSync(
fileURLToPath(new URL('../src/components/business/PersonalWorkbenchProgressPanel.vue', import.meta.url)),
'utf8'
)
const workbenchProgressStyles = readFileSync(
fileURLToPath(new URL('../src/assets/styles/components/personal-workbench-progress.css', import.meta.url)),
'utf8'
)
const workbenchStyles = readFileSync(
fileURLToPath(new URL('../src/assets/styles/components/personal-workbench.css', import.meta.url)),
'utf8'
@@ -31,7 +39,7 @@ const workbenchInsightStyles = readFileSync(
'utf8'
)
const heroBackgroundAsset = fileURLToPath(
new URL('../src/assets/personal-workbench-hero-bg-theme-base.webp', import.meta.url)
new URL('../src/assets/images/hero-3d-banner.png', import.meta.url)
)
const capabilityGlassAsset = fileURLToPath(
new URL('../src/assets/personal-workbench-card-glass-capability.webp', import.meta.url)
@@ -43,8 +51,9 @@ const panelGlassAsset = fileURLToPath(
test('workbench assistant greets the current employee without the old helper tag', () => {
assert.doesNotMatch(workbench, /assistant-tag/)
assert.doesNotMatch(workbench, /AI 报销助手/)
assert.match(workbench, /嗨,\{\{ displayUserName \}\},我是您的 <span>AI 费用助手<\/span>/)
assert.match(workbench, /placeholder="请输入费用申请、报销问题、预算查询或制度问答\.\.\."/)
assert.match(workbench, /\{\{ typedTitlePrefix \}\}<span v-if="titleTypingDone">小财管家<\/span>/)
assert.match(workbench, /const heroTitleText = computed\(\(\) => `嗨,\$\{displayUserName\.value\},我是您的 `\)/)
assert.match(workbench, /placeholder="一次性描述申请、报销和附件处理事项,小财管家会先拆解再执行\.\.\."/)
assert.match(workbench, /const displayUserName = computed/)
assert.match(workbench, /user\.name/)
})
@@ -83,16 +92,16 @@ test('workbench capability cards keep user-entered context only', () => {
})
test('workbench hero uses theme-tintable background image', () => {
assert.match(workbench, /personal-workbench-hero-bg-theme-base\.webp/)
assert.doesNotMatch(workbench, /personal-workbench-hero-bg-theme-base\.png/)
assert.match(workbench, /hero-3d-banner\.png/)
assert.doesNotMatch(workbench, /personal-workbench-hero-bg-theme-base\.(webp|png)/)
assert.match(workbench, /--assistant-bg-image.*workbenchHeroBackground/)
assert.match(workbenchStyles, /--assistant-theme-tint:[\s\S]*--theme-primary-rgb/)
assert.match(workbenchStyles, /var\(--assistant-bg-image\) var\(--assistant-bg-position\) \/ var\(--assistant-bg-size\) no-repeat/)
assert.match(workbenchStyles, /background-blend-mode:\s*normal,\s*color,\s*luminosity;/)
assert.match(workbenchStyles, /\.assistant-hero::after\s*\{[\s\S]*content:\s*none;/)
assert.match(workbenchResponsiveStyles, /--assistant-bg-position:\s*68% center;/)
assert.match(workbenchStyles, /url\("\.\.\/\.\.\/images\/workbench-hero-right-bg\.png"\) var\(--assistant-bg-position\) \/ var\(--assistant-decor-width\) auto no-repeat/)
assert.match(workbenchStyles, /\.assistant-hero::after\s*\{[\s\S]*content:\s*"";/)
assert.match(workbenchResponsiveStyles, /--assistant-bg-position:\s*right center;/)
assert.doesNotMatch(workbenchResponsiveStyles, /homepage_backgraound/)
assert.ok(statSync(heroBackgroundAsset).size < 120 * 1024)
assert.ok(statSync(heroBackgroundAsset).size > 1024)
assert.ok(statSync(heroBackgroundAsset).size < 600 * 1024)
})
test('workbench cards use layered glass material instead of texture-led cards', () => {
@@ -103,13 +112,11 @@ test('workbench cards use layered glass material instead of texture-led cards',
assert.match(workbenchGlassStyles, /--workbench-glass-highlight:/)
assert.match(workbenchGlassStyles, /--workbench-glass-noise-opacity:\s*0\.012;/)
assert.match(workbenchGlassStyles, /--workbench-glass-blur:\s*blur\(18px\) saturate\(1\.28\);/)
assert.match(workbenchGlassStyles, /\.capability-card\s*\{[\s\S]*background-color:\s*rgba\(255,\s*255,\s*255,\s*0\.64\);[\s\S]*backdrop-filter:\s*var\(--workbench-glass-blur\)/)
assert.match(workbenchGlassStyles, /\.workbench-card\s*\{[\s\S]*background-color:\s*rgba\(255,\s*255,\s*255,\s*0\.66\);[\s\S]*backdrop-filter:\s*var\(--workbench-glass-blur\)/)
assert.match(workbenchGlassStyles, /\.capability-card\s*\{[\s\S]*background:\s*rgba\(255,\s*255,\s*255,\s*0\.96\);[\s\S]*backdrop-filter:\s*blur\(12px\) saturate\(150%\)/)
assert.match(workbenchGlassStyles, /\.workbench-card\s*\{[\s\S]*background:\s*rgba\(255,\s*255,\s*255,\s*0\.96\);[\s\S]*backdrop-filter:\s*blur\(12px\) saturate\(150%\)/)
assert.match(workbenchGlassStyles, /\.capability-card::before,[\s\S]*\.capability-card::after/)
assert.match(workbenchGlassStyles, /\.capability-card::before\s*\{[\s\S]*var\(--workbench-capability-bg-image\) 0 0 \/ var\(--workbench-capability-tile-size\) repeat;[\s\S]*opacity:\s*var\(--workbench-glass-noise-opacity\);/)
assert.match(workbenchGlassStyles, /\.workbench-card::before\s*\{[\s\S]*var\(--workbench-panel-bg-image\) 0 0 \/ var\(--workbench-panel-tile-size\) repeat;[\s\S]*opacity:\s*calc\(var\(--workbench-glass-noise-opacity\) \* 0\.8\);/)
assert.match(workbenchGlassStyles, /\.capability-card::after\s*\{[\s\S]*var\(--workbench-glass-highlight\)/)
assert.match(workbenchGlassStyles, /\.workbench-card::after\s*\{[\s\S]*var\(--workbench-glass-highlight\)/)
assert.match(workbenchGlassStyles, /\.workbench-card::before,[\s\S]*\.workbench-card::after\s*\{[\s\S]*display:\s*none !important;/)
assert.doesNotMatch(workbenchGlassStyles, /\.capability-card::after\s*\{[^}]*radial-gradient/)
assert.doesNotMatch(workbenchGlassStyles, /\.workbench-card::after\s*\{[^}]*radial-gradient/)
assert.match(workbenchGlassStyles, /\.workbench-card > \*\s*\{[\s\S]*z-index:\s*1;/)
@@ -143,29 +150,67 @@ test('workbench submit shows intent recognition feedback before assistant opens'
assert.match(workbench, /:readonly="isComposerPending"/)
})
test('workbench progress rows show update time first', () => {
assert.match(workbench, /class="progress-time"/)
assert.match(workbench, /class="progress-type"/)
assert.match(workbench, /费用类型/)
assert.match(workbench, /\{\{ item\.expenseTypeLabel \|\| '其他费用' \}\}/)
test('workbench document progress has range filter, document types and empty state', () => {
assert.match(workbench, /import PersonalWorkbenchProgressPanel from '\.\/PersonalWorkbenchProgressPanel\.vue'/)
assert.match(workbench, /<PersonalWorkbenchProgressPanel[\s\S]*:progress-items="workbenchSummary\.progressItems \|\| \[\]"[\s\S]*@open-target="openWorkbenchTarget"/)
assert.match(workbenchProgressPanel, /<h2>单据进度<\/h2>/)
assert.match(workbenchProgressPanel, /personal-workbench-progress\.css/)
assert.match(workbenchProgressPanel, /EnterpriseSelect/)
assert.match(workbenchProgressPanel, /近10日/)
assert.match(workbenchProgressPanel, /近30日/)
assert.match(workbenchProgressPanel, /近3个月/)
assert.match(workbenchProgressPanel, /class="progress-range-control"[\s\S]*@click\.stop[\s\S]*@mousedown\.stop[\s\S]*@pointerdown\.stop/)
assert.match(workbenchProgressPanel, /:teleported="true"/)
assert.match(workbenchProgressPanel, /@click="handleProgressItemClick\(\$event, item\)"/)
assert.match(workbenchProgressPanel, /function handleProgressItemClick\(event, item\)/)
assert.match(workbenchProgressPanel, /return true/)
assert.match(workbenchProgressPanel, /class="progress-time"/)
assert.match(workbenchProgressPanel, /class="progress-applicant"/)
assert.match(workbenchProgressPanel, /class="progress-table-shell"/)
assert.match(workbenchProgressPanel, />\u66f4\u65b0\u65f6\u95f4<\/span>/)
assert.doesNotMatch(workbenchProgressPanel, /\u5355\u636e\u52a8\u6001/)
assert.doesNotMatch(workbenchProgressPanel, /time-capsule/)
assert.doesNotMatch(workbenchProgressPanel, /<small>\u7533\u8bf7\u4eba<\/small>/)
assert.match(workbenchProgressPanel, /\{\{ item\.applicantLabel \|\| '待补充' \}\}/)
assert.match(workbenchProgressPanel, /class="progress-type"/)
assert.match(workbenchProgressPanel, /\{\{ item\.documentTypeLabel \}\} · \{\{ item\.expenseTypeLabel \|\| '其他费用' \}\}/)
assert.match(workbenchProgressPanel, /当前范围暂无单据/)
assert.match(workbenchProgressPanel, /没有申请单或报销单进度/)
assert.match(workbench, /source:\s*'workbench'/)
assert.match(workbench, /returnTo:\s*'workbench'/)
assert.match(workbench, /has-long-duration-divider/)
assert.match(workbench, /hasLongDurationDivider/)
assert.match(workbench, /const LONG_DURATION_DAYS = 10/)
assert.match(workbench, /isLongDurationProgress\(item\?\.updatedAt\)/)
assert.match(workbench, /<time :datetime="item\.updatedAt \|\| ''">\{\{ item\.displayTime \}\}<\/time>/)
assert.match(workbench, /displayTime: formatProgressTime\(item\?\.updatedAt\)/)
assert.match(workbench, /function formatProgressTime\(value\)/)
assert.doesNotMatch(workbench, />全部进度/)
assert.match(workbenchStyles, /\.progress-row\s*\{[\s\S]*grid-template-columns:\s*minmax\(78px,\s*0\.42fr\)[\s\S]*minmax\(84px,\s*0\.42fr\)/)
assert.doesNotMatch(workbenchProgressPanel, /has-long-duration-divider/)
assert.doesNotMatch(workbenchProgressPanel, /LONG_DURATION_DAYS/)
assert.doesNotMatch(workbenchProgressPanel, /10日以上/)
assert.doesNotMatch(workbenchStyles, /10日以上/)
assert.doesNotMatch(workbenchStyles, /has-long-duration-divider/)
assert.doesNotMatch(workbench, />费用进度</)
assert.match(workbenchProgressPanel, /<time :datetime="item\.updatedAt \|\| ''">\{\{ item\.displayTime \}\}<\/time>/)
assert.match(workbenchProgressPanel, /function formatProgressTime\(value\)/)
assert.match(workbenchStyles, /\.progress-row\s*\{[\s\S]*grid-template-columns:\s*minmax\(118px,\s*0\.58fr\)[\s\S]*minmax\(84px,\s*0\.42fr\)/)
assert.match(workbenchStyles, /\.progress-type\s*\{[\s\S]*justify-self:\s*stretch;[\s\S]*justify-items:\s*center;[\s\S]*text-align:\s*center;/)
assert.match(workbenchStyles, /\.progress-type strong\s*\{[\s\S]*justify-content:\s*center;/)
assert.match(workbenchStyles, /\.progress-type strong\s*\{[\s\S]*var\(--workbench-primary-active\)/)
assert.match(workbenchStyles, /\.progress-row\.has-long-duration-divider::before\s*\{[\s\S]*content:\s*"10日以上"/)
assert.match(workbenchStyles, /\.progress-row\.has-long-duration-divider::before\s*\{[\s\S]*left:\s*50%;[\s\S]*transform:\s*translateX\(-50%\);/)
assert.match(workbenchStyles, /\.progress-row\.has-long-duration-divider::before\s*\{[\s\S]*color:\s*var\(--theme-primary-active\);/)
assert.match(workbenchStyles, /\.progress-row\.has-long-duration-divider::after\s*\{[\s\S]*rgba\(var\(--theme-primary-rgb/)
assert.match(workbenchStyles, /\.progress-list\s*\{[\s\S]*overflow-y:\s*auto;/)
assert.match(workbenchStyles, /\.progress-empty-state\s*\{[\s\S]*border:\s*1px dashed/)
assert.match(workbenchStyles, /\.progress-range-select\s*\{[\s\S]*width:\s*124px;/)
assert.match(workbenchProgressStyles, /\.progress-panel\s*\{[\s\S]*grid-template-rows:\s*auto minmax\(0,\s*1fr\);/)
assert.match(workbenchProgressStyles, /--progress-table-columns:[\s\S]*minmax\(96px,\s*0\.44fr\);/)
assert.match(workbenchProgressStyles, /\.progress-table-shell\s*\{[\s\S]*grid-template-rows:\s*36px minmax\(0,\s*1fr\);[\s\S]*overflow:\s*hidden;/)
assert.match(workbenchProgressStyles, /\.progress-table-header\s*\{[\s\S]*grid-template-columns:\s*var\(--progress-table-columns\);/)
assert.match(workbenchProgressStyles, /\.progress-table-header\s*\{[\s\S]*height:\s*36px;[\s\S]*max-height:\s*36px;[\s\S]*overflow:\s*hidden;/)
assert.match(workbenchProgressStyles, /\.header-cell\s*\{[\s\S]*justify-content:\s*center;[\s\S]*white-space:\s*nowrap;/)
assert.match(workbenchProgressStyles, /\.progress-list\s*\{[\s\S]*grid-auto-rows:\s*minmax\(76px,\s*auto\)/)
assert.match(workbenchProgressStyles, /\.progress-row\s*\{[\s\S]*grid-template-columns:\s*var\(--progress-table-columns\);[\s\S]*gap:\s*8px;[\s\S]*min-height:\s*76px;[\s\S]*text-align:\s*center;/)
assert.match(workbenchProgressStyles, /\.progress-time-wrapper\s*\{[\s\S]*justify-content:\s*center;/)
assert.match(workbenchProgressStyles, /\.progress-identity\s*\{[\s\S]*align-items:\s*center;[\s\S]*text-align:\s*center;/)
assert.match(workbenchProgressStyles, /\.progress-applicant\s*\{[\s\S]*justify-items:\s*center;/)
assert.match(workbenchProgressStyles, /\.progress-result\s*\{[\s\S]*justify-self:\s*stretch;[\s\S]*justify-content:\s*center;[\s\S]*justify-items:\s*center;/)
assert.match(workbenchProgressStyles, /\.progress-result strong\s*\{[\s\S]*width:\s*100%;[\s\S]*text-align:\s*center;/)
assert.match(workbenchProgressStyles, /\.progress-steps\s*\{[\s\S]*justify-items:\s*stretch;/)
assert.match(workbenchProgressStyles, /\.progress-step\s*\{[\s\S]*justify-items:\s*center;/)
assert.match(workbenchProgressStyles, /\.progress-range-control\s*\{[\s\S]*z-index:\s*6;/)
assert.match(workbenchProgressStyles, /\.progress-range-select:deep\(\.el-select__wrapper\)/)
assert.match(workbenchProgressStyles, /\.progress-empty-state\s*\{[\s\S]*border:\s*1px dashed/)
assert.match(workbenchStyles, /\.progress-time\s*\{[\s\S]*color:\s*var\(--workbench-muted\);/)
assert.match(workbenchResponsiveStyles, /grid-template-areas:[\s\S]*"time identity result"[\s\S]*"type type type"[\s\S]*"steps steps steps"/)
assert.match(workbenchResponsiveStyles, /\.progress-type\s*\{[\s\S]*grid-area:\s*type;/)