Commit 74bd42914d2058420c33d0b11861929b4c6d1a4f

Authored by shaofan
1 parent b59f2fb2

Refactor: Replace hardcoded RGBA colors with global CSS variables for consistent…

… theming and maintainability.
src/layouts/MainLayout.vue
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 <div class="menu-scroll"> 17 <div class="menu-scroll">
18 <a-menu 18 <a-menu
19 v-model:selectedKeys="selectedKeys" 19 v-model:selectedKeys="selectedKeys"
  20 + :theme="app.isDarkMode ? 'dark' : 'light'"
20 mode="inline" 21 mode="inline"
21 :inline-collapsed="collapsed" 22 :inline-collapsed="collapsed"
22 @click="onMenuClick" 23 @click="onMenuClick"
@@ -82,17 +83,18 @@ @@ -82,17 +83,18 @@
82 <menu-unfold-outlined /> 83 <menu-unfold-outlined />
83 </button> 84 </button>
84 <div class="topbar-header-info"> 85 <div class="topbar-header-info">
85 - <a-breadcrumb separator="/">  
86 - <a-breadcrumb-item key="home">  
87 - <router-link to="/">  
88 - <home-outlined />  
89 - </router-link>  
90 - </a-breadcrumb-item>  
91 - <a-breadcrumb-item v-for="bc in breadcrumbs" :key="bc.path">  
92 - {{ bc.title }}  
93 - </a-breadcrumb-item>  
94 - </a-breadcrumb>  
95 - <h1>{{ currentTitle }}</h1> 86 + <transition name="breadcrumb-fade" mode="out-in">
  87 + <a-breadcrumb :key="route.path" class="custom-breadcrumb">
  88 + <template #separator>
  89 + <span class="bc-separator"><right-outlined /></span>
  90 + </template>
  91 + <a-breadcrumb-item v-for="(bc, idx) in breadcrumbs" :key="idx">
  92 + <span :class="idx === breadcrumbs.length - 1 ? 'bc-last' : 'bc-parent'">
  93 + {{ bc.title }}
  94 + </span>
  95 + </a-breadcrumb-item>
  96 + </a-breadcrumb>
  97 + </transition>
96 </div> 98 </div>
97 </div> 99 </div>
98 <div class="topbar-actions"> 100 <div class="topbar-actions">
@@ -142,7 +144,11 @@ @@ -142,7 +144,11 @@
142 </header> 144 </header>
143 145
144 <div class="soft-page-shell"> 146 <div class="soft-page-shell">
145 - <router-view /> 147 + <router-view v-slot="{ Component }">
  148 + <transition name="fade-slide" mode="out-in">
  149 + <component :is="Component" />
  150 + </transition>
  151 + </router-view>
146 </div> 152 </div>
147 </main> 153 </main>
148 154
@@ -159,6 +165,7 @@ @@ -159,6 +165,7 @@
159 </div> 165 </div>
160 <a-menu 166 <a-menu
161 v-model:selectedKeys="selectedKeys" 167 v-model:selectedKeys="selectedKeys"
  168 + :theme="app.isDarkMode ? 'dark' : 'light'"
162 mode="inline" 169 mode="inline"
163 @click="onMenuClick(); collapsed = true" 170 @click="onMenuClick(); collapsed = true"
164 > 171 >
@@ -223,7 +230,7 @@ import { @@ -223,7 +230,7 @@ import {
223 UserOutlined, UnorderedListOutlined, ApiOutlined, DownOutlined, StarOutlined, 230 UserOutlined, UnorderedListOutlined, ApiOutlined, DownOutlined, StarOutlined,
224 CalendarOutlined, MenuFoldOutlined, MenuUnfoldOutlined, HomeOutlined, ControlOutlined, 231 CalendarOutlined, MenuFoldOutlined, MenuUnfoldOutlined, HomeOutlined, ControlOutlined,
225 FullscreenOutlined, FullscreenExitOutlined, BellOutlined, 232 FullscreenOutlined, FullscreenExitOutlined, BellOutlined,
226 - BulbOutlined, BulbFilled 233 + BulbOutlined, BulbFilled, RightOutlined
227 } from '@ant-design/icons-vue' 234 } from '@ant-design/icons-vue'
228 235
229 const router = useRouter() 236 const router = useRouter()
@@ -248,16 +255,30 @@ onUnmounted(() =&gt; { @@ -248,16 +255,30 @@ onUnmounted(() =&gt; {
248 watch(() => route.path, (p) => { selectedKeys.value = [p] }) 255 watch(() => route.path, (p) => { selectedKeys.value = [p] })
249 256
250 const isAdmin = computed(() => auth.userInfo?.role === 'admin') 257 const isAdmin = computed(() => auth.userInfo?.role === 'admin')
251 -const currentTitle = computed(() => (route.meta.title as string) || '外卖管理')  
252 258
253 // Breadcrumbs logic 259 // Breadcrumbs logic
  260 +const menuParents: Record<string, string> = {
  261 + '/merchant/enter': '商家管理',
  262 + '/merchant/store': '商家管理',
  263 + '/order': '订单管理',
  264 + '/refund': '订单管理',
  265 + '/delivery/order': '订单管理',
  266 + '/config/fee-plan': '配置中心',
  267 + '/dispatch/rule': '配置中心',
  268 + '/open': '开放平台',
  269 + '/open/mock-delivery': '开放平台',
  270 +}
  271 +
254 const breadcrumbs = computed(() => { 272 const breadcrumbs = computed(() => {
255 - return route.matched  
256 - .filter(r => r.meta && r.meta.title)  
257 - .map(r => ({  
258 - title: r.meta.title,  
259 - path: r.path || '/',  
260 - })) 273 + const list = []
  274 + const parentTitle = menuParents[route.path]
  275 + if (parentTitle) {
  276 + list.push({ title: parentTitle, path: '' })
  277 + }
  278 + if (route.meta && route.meta.title) {
  279 + list.push({ title: route.meta.title as string, path: route.path })
  280 + }
  281 + return list
261 }) 282 })
262 283
263 const isFullscreen = ref(false) 284 const isFullscreen = ref(false)
@@ -297,6 +318,7 @@ function handleLogout() { @@ -297,6 +318,7 @@ function handleLogout() {
297 grid-template-columns: 280px minmax(0, 1fr); 318 grid-template-columns: 280px minmax(0, 1fr);
298 gap: 16px; 319 gap: 16px;
299 padding: 16px; 320 padding: 16px;
  321 + transition: grid-template-columns 0.35s cubic-bezier(0.25, 0.8, 0.25, 1);
300 } 322 }
301 323
302 .layout-shell.collapsed { 324 .layout-shell.collapsed {
@@ -320,6 +342,7 @@ function handleLogout() { @@ -320,6 +342,7 @@ function handleLogout() {
320 display: flex; 342 display: flex;
321 flex-direction: column; 343 flex-direction: column;
322 overflow: hidden; 344 overflow: hidden;
  345 + transition: all 0.35s cubic-bezier(0.25, 0.8, 0.25, 1);
323 } 346 }
324 347
325 .menu-scroll { 348 .menu-scroll {
@@ -355,7 +378,7 @@ function handleLogout() { @@ -355,7 +378,7 @@ function handleLogout() {
355 border-radius: 12px; 378 border-radius: 12px;
356 border: none; 379 border: none;
357 background: var(--line-strong); 380 background: var(--line-strong);
358 - color: var(--soft-primary); 381 + color: var(--brand);
359 cursor: pointer; 382 cursor: pointer;
360 margin-bottom: 12px; 383 margin-bottom: 12px;
361 } 384 }
@@ -576,9 +599,54 @@ h1 { @@ -576,9 +599,54 @@ h1 {
576 gap: 16px; 599 gap: 16px;
577 } 600 }
578 601
579 -.topbar-header-info h1 {  
580 - margin: 2px 0 0;  
581 - font-size: 20px; 602 +.topbar-header-info {
  603 + display: flex;
  604 + align-items: center;
  605 + height: 40px;
  606 +}
  607 +
  608 +:deep(.custom-breadcrumb) {
  609 + display: flex;
  610 + align-items: center;
  611 + margin-bottom: 0;
  612 +}
  613 +
  614 +:deep(.custom-breadcrumb li) {
  615 + display: inline-flex;
  616 + align-items: center;
  617 +}
  618 +
  619 +.bc-separator {
  620 + font-size: 11px;
  621 + opacity: 0.6;
  622 + color: var(--text-soft);
  623 + margin: 0 4px;
  624 +}
  625 +
  626 +.bc-parent {
  627 + color: var(--text-soft) !important;
  628 + font-size: 14px;
  629 +}
  630 +
  631 +.bc-last {
  632 + font-size: 18px;
  633 + font-weight: 600;
  634 + color: var(--text-main) !important;
  635 + letter-spacing: 0.5px;
  636 +}
  637 +
  638 +/* Breadcrumb transition */
  639 +.breadcrumb-fade-enter-active,
  640 +.breadcrumb-fade-leave-active {
  641 + transition: all 0.25s cubic-bezier(0.25, 0.8, 0.25, 1);
  642 +}
  643 +.breadcrumb-fade-enter-from {
  644 + opacity: 0;
  645 + transform: translateX(10px);
  646 +}
  647 +.breadcrumb-fade-leave-to {
  648 + opacity: 0;
  649 + transform: translateX(-10px);
582 } 650 }
583 651
584 .header-action-group { 652 .header-action-group {
@@ -609,12 +677,18 @@ h1 { @@ -609,12 +677,18 @@ h1 {
609 padding: 12px 16px 24px; 677 padding: 12px 16px 24px;
610 } 678 }
611 679
612 -:deep(.ant-breadcrumb) {  
613 - font-size: 11px;  
614 -}  
615 680
616 -:deep(.ant-breadcrumb-link), :deep(.ant-breadcrumb-separator) {  
617 - color: var(--soft-subtext) !important; 681 +.fade-slide-enter-active,
  682 +.fade-slide-leave-active {
  683 + transition: all 0.25s cubic-bezier(0.25, 0.8, 0.25, 1);
  684 +}
  685 +.fade-slide-enter-from {
  686 + opacity: 0;
  687 + transform: translateY(10px);
  688 +}
  689 +.fade-slide-leave-to {
  690 + opacity: 0;
  691 + transform: translateY(-10px);
618 } 692 }
619 693
620 @media (max-width: 960px) { 694 @media (max-width: 960px) {
src/style.css
@@ -143,7 +143,7 @@ a { @@ -143,7 +143,7 @@ a {
143 143
144 .ant-table-wrapper .ant-table-container { 144 .ant-table-wrapper .ant-table-container {
145 border-radius: var(--radius-md); 145 border-radius: var(--radius-md);
146 - border: 1px solid rgba(194, 185, 239, 0.22); 146 + border: 1px solid var(--line);
147 overflow: hidden; 147 overflow: hidden;
148 } 148 }
149 149
@@ -338,6 +338,7 @@ a { @@ -338,6 +338,7 @@ a {
338 margin-block: 4px !important; 338 margin-block: 4px !important;
339 width: calc(100% - 16px) !important; 339 width: calc(100% - 16px) !important;
340 font-size: var(--font-size-md) !important; 340 font-size: var(--font-size-md) !important;
  341 + color: var(--text-main);
341 } 342 }
342 343
343 .ant-form-item { 344 .ant-form-item {
@@ -351,11 +352,10 @@ a { @@ -351,11 +352,10 @@ a {
351 font-size: var(--font-size-md); 352 font-size: var(--font-size-md);
352 } 353 }
353 354
354 -.ant-menu-light .ant-menu-item-selected,  
355 -.ant-menu-light > .ant-menu .ant-menu-item-selected,  
356 -.ant-menu-light .ant-menu-submenu-selected > .ant-menu-submenu-title {  
357 - background: linear-gradient(135deg, rgba(140, 124, 240, 0.18), rgba(255, 212, 235, 0.3)) !important;  
358 - color: var(--brand-deep) !important; 355 +.ant-menu-item-selected,
  356 +.ant-menu-submenu-selected > .ant-menu-submenu-title {
  357 + background: var(--panel-tint) !important;
  358 + color: var(--brand) !important;
359 } 359 }
360 360
361 .ant-menu-submenu-popup .ant-menu { 361 .ant-menu-submenu-popup .ant-menu {
@@ -384,15 +384,15 @@ a { @@ -384,15 +384,15 @@ a {
384 384
385 .ant-menu-item:hover, 385 .ant-menu-item:hover,
386 .ant-menu-submenu-title:hover { 386 .ant-menu-submenu-title:hover {
387 - color: var(--brand-deep) !important; 387 + color: var(--brand) !important;
388 background: var(--line) !important; 388 background: var(--line) !important;
389 } 389 }
390 390
391 .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) { 391 .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) {
392 - color: var(--brand-deep); 392 + color: var(--brand);
393 background: var(--panel-strong); 393 background: var(--panel-strong);
394 - border-color: rgba(140, 124, 240, 0.45);  
395 - box-shadow: 0 8px 18px rgba(140, 124, 240, 0.12); 394 + border-color: var(--brand);
  395 + box-shadow: var(--shadow-sm);
396 } 396 }
397 397
398 .ant-empty { 398 .ant-empty {
src/views/config/FeePlanList.vue
@@ -913,8 +913,8 @@ onMounted(loadCities) @@ -913,8 +913,8 @@ onMounted(loadCities)
913 min-height: 36px; 913 min-height: 36px;
914 padding: 0 14px; 914 padding: 0 14px;
915 border-radius: 999px; 915 border-radius: 999px;
916 - border: 1px solid rgba(194, 185, 239, 0.28);  
917 - background: rgba(246, 242, 255, 0.84); 916 + border: 1px solid var(--line);
  917 + background: var(--panel-strong);
918 } 918 }
919 919
920 .plan-list { 920 .plan-list {
@@ -985,7 +985,7 @@ onMounted(loadCities) @@ -985,7 +985,7 @@ onMounted(loadCities)
985 border: none; 985 border: none;
986 border-radius: 12px; 986 border-radius: 12px;
987 background: linear-gradient(135deg, #8c7cf0, #a98ff7 55%, #e5b5dc); 987 background: linear-gradient(135deg, #8c7cf0, #a98ff7 55%, #e5b5dc);
988 - box-shadow: 0 8px 18px rgba(140, 124, 240, 0.18); 988 + box-shadow: var(--shadow-sm);
989 } 989 }
990 990
991 .plan-save-button:hover, 991 .plan-save-button:hover,
@@ -1018,9 +1018,9 @@ onMounted(loadCities) @@ -1018,9 +1018,9 @@ onMounted(loadCities)
1018 min-height: 26px; 1018 min-height: 26px;
1019 padding: 0 12px; 1019 padding: 0 12px;
1020 border-radius: 999px; 1020 border-radius: 999px;
1021 - background: rgba(246, 242, 255, 0.95);  
1022 - border: 1px solid rgba(140, 124, 240, 0.18);  
1023 - color: #7f6de5; 1021 + background: var(--panel-strong);
  1022 + border: 1px solid var(--line-strong);
  1023 + color: var(--brand);
1024 font-size: 12px; 1024 font-size: 12px;
1025 font-weight: 700; 1025 font-weight: 700;
1026 } 1026 }
@@ -1049,7 +1049,8 @@ onMounted(loadCities) @@ -1049,7 +1049,8 @@ onMounted(loadCities)
1049 1049
1050 .preview-card { 1050 .preview-card {
1051 border-radius: 16px; 1051 border-radius: 16px;
1052 - background: #fafbff; 1052 + background: var(--panel-strong);
  1053 + border: 1px solid var(--line);
1053 } 1054 }
1054 1055
1055 .preview-subtitle { 1056 .preview-subtitle {
src/views/dispatch/DispatchRuleList.vue
@@ -672,7 +672,7 @@ onMounted(loadCities) @@ -672,7 +672,7 @@ onMounted(loadCities)
672 border: none; 672 border: none;
673 border-radius: 12px; 673 border-radius: 12px;
674 background: linear-gradient(135deg, #8c7cf0, #a98ff7 55%, #e5b5dc); 674 background: linear-gradient(135deg, #8c7cf0, #a98ff7 55%, #e5b5dc);
675 - box-shadow: 0 8px 18px rgba(140, 124, 240, 0.18); 675 + box-shadow: var(--shadow-sm);
676 } 676 }
677 677
678 .plan-save-button:hover, 678 .plan-save-button:hover,
@@ -701,9 +701,9 @@ onMounted(loadCities) @@ -701,9 +701,9 @@ onMounted(loadCities)
701 min-height: 26px; 701 min-height: 26px;
702 padding: 0 12px; 702 padding: 0 12px;
703 border-radius: 999px; 703 border-radius: 999px;
704 - background: rgba(246, 242, 255, 0.95);  
705 - border: 1px solid rgba(140, 124, 240, 0.18);  
706 - color: #7f6de5; 704 + background: var(--panel-strong);
  705 + border: 1px solid var(--line-strong);
  706 + color: var(--brand);
707 font-size: 12px; 707 font-size: 12px;
708 font-weight: 700; 708 font-weight: 700;
709 } 709 }
@@ -796,13 +796,13 @@ onMounted(loadCities) @@ -796,13 +796,13 @@ onMounted(loadCities)
796 796
797 :deep(.grab-scope-option-checked .grab-scope-card), 797 :deep(.grab-scope-option-checked .grab-scope-card),
798 :deep(.grab-scope-option.ant-radio-wrapper-checked .grab-scope-card) { 798 :deep(.grab-scope-option.ant-radio-wrapper-checked .grab-scope-card) {
799 - border-color: rgba(140, 124, 240, 0.5);  
800 - background: rgba(246, 242, 255, 0.96);  
801 - box-shadow: 0 10px 24px rgba(140, 124, 240, 0.12); 799 + border-color: var(--brand);
  800 + background: var(--panel-tint);
  801 + box-shadow: var(--shadow-sm);
802 } 802 }
803 803
804 :deep(.grab-scope-option:hover .grab-scope-card) { 804 :deep(.grab-scope-option:hover .grab-scope-card) {
805 - border-color: rgba(140, 124, 240, 0.34); 805 + border-color: var(--brand);
806 } 806 }
807 807
808 .dispatch-condition-list { 808 .dispatch-condition-list {
@@ -835,9 +835,9 @@ onMounted(loadCities) @@ -835,9 +835,9 @@ onMounted(loadCities)
835 font-family: var(--font-display); 835 font-family: var(--font-display);
836 font-size: 18px; 836 font-size: 18px;
837 font-weight: 700; 837 font-weight: 700;
838 - color: #7f6de5;  
839 - background: rgba(246, 242, 255, 0.92);  
840 - border: 1px solid rgba(140, 124, 240, 0.18); 838 + color: var(--brand);
  839 + background: var(--panel-strong);
  840 + border: 1px solid var(--line-strong);
841 } 841 }
842 842
843 .dispatch-condition-content { 843 .dispatch-condition-content {
@@ -871,7 +871,7 @@ onMounted(loadCities) @@ -871,7 +871,7 @@ onMounted(loadCities)
871 .dispatch-condition-body { 871 .dispatch-condition-body {
872 margin-top: 14px; 872 margin-top: 14px;
873 padding-top: 14px; 873 padding-top: 14px;
874 - border-top: 1px dashed rgba(194, 185, 239, 0.36); 874 + border-top: 1px dashed var(--line);
875 } 875 }
876 876
877 .dispatch-condition-input { 877 .dispatch-condition-input {
src/views/open/OpenMockDelivery.vue
@@ -523,7 +523,7 @@ watch( @@ -523,7 +523,7 @@ watch(
523 .mock-form-section { 523 .mock-form-section {
524 margin-top: 8px; 524 margin-top: 8px;
525 padding-top: 18px; 525 padding-top: 18px;
526 - border-top: 1px solid rgba(206, 196, 244, 0.22); 526 + border-top: 1px solid var(--line);
527 } 527 }
528 528
529 .mock-item-row { 529 .mock-item-row {