From 1b04ee1c4cc86fb23b5ad68fc35431d0d4913a2b Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Mon, 22 Jun 2026 13:34:12 +0800 Subject: [PATCH] fix(web): restore travel detail child component styles --- .../components/travel-request-detail-hero.css | 289 ++++++++++++++++++ .../travel-request-progress-card.css | 228 ++++++++++++++ ...ravel-request-related-application-card.css | 127 ++++++++ .../travel/TravelRequestDetailHero.vue | 2 + .../travel/TravelRequestProgressCard.vue | 2 + .../TravelRequestRelatedApplicationCard.vue | 2 + ...travel-request-detail-risk-advice.test.mjs | 19 ++ 7 files changed, 669 insertions(+) create mode 100644 web/src/assets/styles/components/travel-request-detail-hero.css create mode 100644 web/src/assets/styles/components/travel-request-progress-card.css create mode 100644 web/src/assets/styles/components/travel-request-related-application-card.css diff --git a/web/src/assets/styles/components/travel-request-detail-hero.css b/web/src/assets/styles/components/travel-request-detail-hero.css new file mode 100644 index 0000000..3dd997f --- /dev/null +++ b/web/src/assets/styles/components/travel-request-detail-hero.css @@ -0,0 +1,289 @@ +.hero-banner { + display: grid; + gap: 0; +} + +.hero-banner-main { + display: grid; + grid-template-columns: minmax(260px, 1.1fr) minmax(0, 2fr); + align-items: center; + gap: 16px; + min-height: 104px; +} + +.applicant-card { + display: grid; + grid-template-columns: 88px minmax(0, 1fr); + align-items: center; + gap: 18px; + min-width: 0; +} + +.portrait { + width: 88px; + height: 88px; + overflow: hidden; + border: 1px solid #e2e8f0; + border-radius: 999px; + background: #f8fafc; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9); +} + +.portrait img { + width: 100%; + height: 100%; + display: block; + object-fit: cover; +} + +.applicant-copy { + min-width: 0; + display: grid; + gap: 14px; +} + +.applicant-name-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.applicant-card h2 { + color: #0f172a; + font-size: 20px; + font-weight: 900; + line-height: 1.2; +} + +.identity-badge { + display: inline-flex; + align-items: center; + min-height: 24px; + padding: 0 9px; + border-radius: 4px; + background: var(--theme-primary-soft); + border: 1px solid rgba(var(--theme-primary-rgb), .16); + color: var(--theme-primary-active); + font-size: 11px; + font-weight: 800; +} + +.applicant-profile-meta { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 12px 28px; +} + +.applicant-profile-meta__org { + display: grid; + gap: 6px; + min-width: 0; +} + +.applicant-profile-meta__role { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 8px 0; + min-width: 0; +} + +.applicant-meta-item { + min-width: 0; + position: relative; + display: inline-flex; + align-items: center; + gap: 6px; + color: #475569; + font-size: 13px; + line-height: 1.5; +} + +.applicant-profile-meta__role .applicant-meta-item + .applicant-meta-item { + margin-left: 16px; +} + +.applicant-profile-meta__role .applicant-meta-item + .applicant-meta-item::before { + content: "/"; + position: absolute; + left: -10px; + color: #cbd5e1; + font-size: 12px; +} + +.applicant-meta-item--sub strong { + font-weight: 750; +} + +.applicant-meta-item em { + font-style: normal; + color: #64748b; + flex-shrink: 0; +} + +.applicant-meta-item strong { + color: #0f172a; + font-weight: 800; +} + +.hero-fact-grid { + display: grid; + grid-template-columns: minmax(240px, 1.25fr) repeat(3, minmax(0, 1fr)); + gap: 0; +} + +.hero-fact { + display: grid; + align-content: center; + gap: 8px; + min-width: 0; + min-height: 92px; + padding: 8px 22px; + background: transparent; + border-left: 1px solid #eaf0f6; +} + +.hero-fact:first-child { + border-left: 0; +} + +.hero-fact-label { + display: inline-flex; + align-items: center; + gap: 8px; + color: #64748b; + font-size: 11px; + font-weight: 800; + letter-spacing: .03em; + text-transform: uppercase; +} + +.hero-fact-label i { + font-size: 15px; + color: #94a3b8; +} + +.hero-fact strong { + min-width: 0; + max-width: 100%; + color: #0f172a; + font-size: 16px; + font-weight: 800; + line-height: 1.4; + overflow-wrap: anywhere; + white-space: nowrap; +} + +.hero-fact:first-child strong { + font-size: 15px; +} + +.hero-fact strong.amount { + font-size: 22px; + font-weight: 900; +} + +.hero-fact strong.status { + color: #f97316; +} + +@media (max-width: 1320px) { + .hero-banner-main { + grid-template-columns: 1fr; + gap: 16px; + min-height: 0; + } + + .hero-fact-grid { + grid-template-columns: minmax(280px, 1.4fr) repeat(3, minmax(0, 1fr)); + overflow: visible; + } + + .hero-fact { + min-width: 0; + padding-inline: 16px; + } + + .hero-fact strong { + white-space: nowrap; + word-break: normal; + } +} + +@media (max-width: 760px) { + .hero-banner-main, + .hero-fact-grid, + .applicant-card { + min-width: 0; + max-width: 100%; + } + + .applicant-card { + grid-template-columns: 60px minmax(0, 1fr); + gap: 12px; + } + + .portrait { + width: 60px; + height: 60px; + } + + .applicant-copy { + gap: 8px; + } + + .applicant-card h2 { + font-size: 16px; + } + + .applicant-profile-meta { + display: grid; + gap: 10px; + } + + .applicant-profile-meta__role { + display: grid; + gap: 6px; + } + + .applicant-profile-meta__role .applicant-meta-item + .applicant-meta-item { + margin-left: 0; + } + + .applicant-profile-meta__role .applicant-meta-item + .applicant-meta-item::before { + content: none; + } + + .hero-fact-grid { + grid-template-columns: 1fr 1fr; + gap: 0; + overflow: hidden; + border-top: 1px solid #edf2f7; + } + + .hero-fact { + min-width: 0; + min-height: 78px; + padding: 14px 12px 12px; + border-left: 0; + border-bottom: 1px solid #edf2f7; + } + + .hero-fact:nth-child(2n) { + border-left: 1px solid #edf2f7; + } + + .hero-fact:last-child:nth-child(odd) { + grid-column: 1 / -1; + } + + .hero-fact:nth-last-child(-n + 2) { + border-bottom: 0; + } + + .hero-fact strong { + white-space: normal; + } +} diff --git a/web/src/assets/styles/components/travel-request-progress-card.css b/web/src/assets/styles/components/travel-request-progress-card.css new file mode 100644 index 0000000..7bbb601 --- /dev/null +++ b/web/src/assets/styles/components/travel-request-progress-card.css @@ -0,0 +1,228 @@ +.progress-block { + min-width: 0; + max-width: 100%; + padding-top: 0; + border-top: 0; +} + +.progress-head { + display: flex; + align-items: center; + justify-content: flex-start; + margin-bottom: 12px; +} + +.progress-head h3 { + display: inline-flex; + align-items: center; + gap: 8px; + color: #0f172a; + font-size: 14px; + font-weight: 850; +} + +.progress-head h3::before { + content: ""; + width: 8px; + height: 8px; + border-radius: 999px; + background: var(--theme-primary); + box-shadow: 0 0 0 4px var(--theme-focus-ring); +} + +.progress-line { + width: 100%; + min-width: 0; + max-width: 100%; + grid-column: 1 / -1; + display: grid; + grid-template-columns: repeat(var(--progress-columns, 5), minmax(118px, 1fr)); + overflow-x: auto; + overscroll-behavior-x: contain; + padding: 4px 2px 2px; +} + +.progress-step { + position: relative; + display: grid; + grid-template-rows: 26px minmax(62px, auto); + justify-items: center; + align-items: start; + gap: 10px; + min-width: 0; + padding: 0 6px; + color: #94a3b8; +} + +.progress-step::before, +.progress-step::after { + content: ""; + position: absolute; + top: 13px; + left: 0; + right: 0; + z-index: 0; + height: 2px; +} + +.progress-step::before { + background: #dbe4ee; +} + +.progress-step::after { + background: var(--theme-primary); + opacity: 0; +} + +.progress-step.done::after { + opacity: 1; +} + +.progress-step.current::after { + right: 50%; + opacity: 1; +} + +.progress-step:first-child::before, +.progress-step:first-child.done::after { + left: 50%; +} + +.progress-step:first-child.current::after { + left: 50%; + right: 50%; +} + +.progress-step:last-child::before, +.progress-step:last-child::after { + right: 50%; +} + +.progress-step span { + position: relative; + z-index: 1; + width: 24px; + height: 24px; + display: grid; + place-items: center; + border-radius: 999px; + background: #e2e8f0; + color: #64748b; + font-size: 13px; + font-weight: 900; +} + +.current-progress-ring { + position: absolute; + inset: -4px; + z-index: -1; + border: 2px solid rgba(var(--theme-primary-rgb), .42); + border-radius: 999px; + pointer-events: none; +} + +.progress-step.active span { + background: var(--theme-primary-active); + color: #fff; +} + +.progress-step.current span { + background: var(--theme-primary) !important; + color: #fff !important; + box-shadow: 0 0 0 4px var(--theme-focus-ring) !important; + animation: breathe-dot 3.2s ease-in-out infinite !important; + transform-origin: center !important; +} + +@keyframes breathe-dot { + 0%, 100% { + transform: scale(1); + box-shadow: 0 4px 12px rgba(var(--theme-primary-rgb), .3), 0 0 0 4px var(--theme-focus-ring); + } + + 50% { + transform: scale(1.12); + box-shadow: 0 4px 20px rgba(var(--theme-primary-rgb), .5), 0 0 0 10px rgba(var(--theme-primary-rgb), .08); + } +} + +.progress-step strong { + color: #334155; + font-size: 12px; + line-height: 1.35; + text-align: center; +} + +.progress-step.current strong { + color: var(--theme-primary-active); +} + +.progress-step-copy { + width: 100%; + min-width: 0; + display: grid; + justify-items: center; + align-content: start; + gap: 6px; +} + +.progress-step-status { + box-sizing: border-box; + width: 100%; + max-width: 136px; + min-width: 0; + min-height: 22px; + display: block; + padding: 0 9px; + border: 1px solid #e2e8f0; + border-radius: 4px; + background: #f8fafc; + color: #64748b; + font-size: 11px; + font-weight: 850; + line-height: 20px; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.progress-step.done .progress-step-status { + border-color: rgba(var(--theme-primary-rgb), .2); + background: var(--theme-primary-soft); + color: var(--theme-primary-active); +} + +.progress-step.current .progress-step-status { + border-color: rgba(var(--theme-primary-rgb), .22); + background: var(--theme-primary); + color: #fff; + box-shadow: 0 8px 18px var(--theme-primary-shadow); +} + +.progress-step:not(.done):not(.current) .progress-step-status { + background: #f8fafc; + color: #94a3b8; +} + +.progress-step.current small { + color: var(--theme-primary-active); +} + +.progress-step-meta { + display: block; + width: 100%; + min-height: 16px; + color: #64748b; + font-size: 11px; + font-style: normal; + line-height: 1.35; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.progress-step.current .progress-step-meta { + color: #475569; +} diff --git a/web/src/assets/styles/components/travel-request-related-application-card.css b/web/src/assets/styles/components/travel-request-related-application-card.css new file mode 100644 index 0000000..592164f --- /dev/null +++ b/web/src/assets/styles/components/travel-request-related-application-card.css @@ -0,0 +1,127 @@ +.detail-card-head { + min-width: 0; + max-width: 100%; + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + margin-bottom: 14px; +} + +.detail-card-head h3 { + margin: 0 0 4px; + color: #0f172a; + font-size: 16px; + font-weight: 850; +} + +.detail-card-head p { + margin: 0; + color: #64748b; + font-size: 12px; + line-height: 1.5; +} + +.application-detail-facts { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 4px; + overflow: hidden; + border: 1px solid #e2e8f0; + border-radius: 4px; + background: #fff; +} + +.application-detail-fact { + display: grid; + grid-template-columns: minmax(96px, 28%) minmax(0, 1fr); + min-height: 48px; + border-top: 1px solid #edf2f7; + border-left: 1px solid #edf2f7; +} + +.application-detail-fact:nth-child(-n + 2) { + border-top: 0; +} + +.application-detail-fact:nth-child(2n + 1) { + border-left: 0; +} + +.application-detail-fact span, +.application-detail-fact strong { + display: flex; + align-items: center; + min-width: 0; + padding: 11px 14px; + line-height: 1.5; +} + +.application-detail-fact span { + background: #f8fafc; + color: #64748b; + font-size: 12px; + font-weight: 800; +} + +.application-detail-fact strong { + border-left: 1px solid #edf2f7; + color: #0f172a; + font-size: 13px; + font-weight: 750; + overflow-wrap: anywhere; +} + +.application-detail-fact.highlight span { + background: var(--theme-primary-soft); + color: var(--theme-primary-active); +} + +.application-detail-fact.highlight strong { + background: color-mix(in srgb, var(--theme-primary-soft) 55%, #ffffff); +} + +.application-detail-fact.emphasis strong { + color: var(--theme-primary-active); + font-weight: 850; +} + +.related-application-facts { + margin-top: 0; +} + +.related-application-empty { + display: grid; + gap: 6px; + padding: 13px 14px; + border: 1px solid #e2e8f0; + border-radius: 4px; + background: #f8fafc; +} + +.related-application-empty strong { + color: #334155; + font-size: 13px; + font-weight: 850; +} + +.related-application-empty p { + margin: 0; + color: #64748b; + font-size: 12px; + line-height: 1.6; +} + +@media (max-width: 760px) { + .application-detail-facts { + grid-template-columns: 1fr; + } + + .application-detail-fact { + border-left: 0; + } + + .application-detail-fact:nth-child(2) { + border-top: 1px solid #edf2f7; + } +} diff --git a/web/src/components/travel/TravelRequestDetailHero.vue b/web/src/components/travel/TravelRequestDetailHero.vue index e48a157..bbf9c4a 100644 --- a/web/src/components/travel/TravelRequestDetailHero.vue +++ b/web/src/components/travel/TravelRequestDetailHero.vue @@ -55,3 +55,5 @@ defineProps({ heroFactItems: { type: Array, default: () => [] } }) + + diff --git a/web/src/components/travel/TravelRequestProgressCard.vue b/web/src/components/travel/TravelRequestProgressCard.vue index 8478491..a304949 100644 --- a/web/src/components/travel/TravelRequestProgressCard.vue +++ b/web/src/components/travel/TravelRequestProgressCard.vue @@ -41,3 +41,5 @@ defineProps({ currentProgressRingMotion: { type: Object, required: true } }) + + diff --git a/web/src/components/travel/TravelRequestRelatedApplicationCard.vue b/web/src/components/travel/TravelRequestRelatedApplicationCard.vue index ccecdc3..c897f5c 100644 --- a/web/src/components/travel/TravelRequestRelatedApplicationCard.vue +++ b/web/src/components/travel/TravelRequestRelatedApplicationCard.vue @@ -30,3 +30,5 @@ defineProps({ relatedApplicationFactItems: { type: Array, default: () => [] } }) + + diff --git a/web/tests/travel-request-detail-risk-advice.test.mjs b/web/tests/travel-request-detail-risk-advice.test.mjs index 1bd5c96..487e256 100644 --- a/web/tests/travel-request-detail-risk-advice.test.mjs +++ b/web/tests/travel-request-detail-risk-advice.test.mjs @@ -119,6 +119,10 @@ const stageRiskAdviceStyles = readFileSync( fileURLToPath(new URL('../src/assets/styles/components/stage-risk-advice-card.css', import.meta.url)), 'utf8' ) +const detailHeroTemplate = readFileSync( + fileURLToPath(new URL('../src/components/travel/TravelRequestDetailHero.vue', import.meta.url)), + 'utf8' +) const relatedApplicationCardTemplate = readFileSync( fileURLToPath(new URL('../src/components/travel/TravelRequestRelatedApplicationCard.vue', import.meta.url)), 'utf8' @@ -1055,6 +1059,21 @@ test('related application information is shown above expense details for reimbur assert.doesNotMatch(detailViewTemplate, /v-model="detailNoteEditorView"/) }) +test('split detail page header cards keep their scoped styles', () => { + assert.match( + detailHeroTemplate, + /