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 17 <div class="menu-scroll">
18 18 <a-menu
19 19 v-model:selectedKeys="selectedKeys"
  20 + :theme="app.isDarkMode ? 'dark' : 'light'"
20 21 mode="inline"
21 22 :inline-collapsed="collapsed"
22 23 @click="onMenuClick"
... ... @@ -82,17 +83,18 @@
82 83 <menu-unfold-outlined />
83 84 </button>
84 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 98 </div>
97 99 </div>
98 100 <div class="topbar-actions">
... ... @@ -142,7 +144,11 @@
142 144 </header>
143 145  
144 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 152 </div>
147 153 </main>
148 154  
... ... @@ -159,6 +165,7 @@
159 165 </div>
160 166 <a-menu
161 167 v-model:selectedKeys="selectedKeys"
  168 + :theme="app.isDarkMode ? 'dark' : 'light'"
162 169 mode="inline"
163 170 @click="onMenuClick(); collapsed = true"
164 171 >
... ... @@ -223,7 +230,7 @@ import {
223 230 UserOutlined, UnorderedListOutlined, ApiOutlined, DownOutlined, StarOutlined,
224 231 CalendarOutlined, MenuFoldOutlined, MenuUnfoldOutlined, HomeOutlined, ControlOutlined,
225 232 FullscreenOutlined, FullscreenExitOutlined, BellOutlined,
226   - BulbOutlined, BulbFilled
  233 + BulbOutlined, BulbFilled, RightOutlined
227 234 } from '@ant-design/icons-vue'
228 235  
229 236 const router = useRouter()
... ... @@ -248,16 +255,30 @@ onUnmounted(() =&gt; {
248 255 watch(() => route.path, (p) => { selectedKeys.value = [p] })
249 256  
250 257 const isAdmin = computed(() => auth.userInfo?.role === 'admin')
251   -const currentTitle = computed(() => (route.meta.title as string) || '外卖管理')
252 258  
253 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 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 284 const isFullscreen = ref(false)
... ... @@ -297,6 +318,7 @@ function handleLogout() {
297 318 grid-template-columns: 280px minmax(0, 1fr);
298 319 gap: 16px;
299 320 padding: 16px;
  321 + transition: grid-template-columns 0.35s cubic-bezier(0.25, 0.8, 0.25, 1);
300 322 }
301 323  
302 324 .layout-shell.collapsed {
... ... @@ -320,6 +342,7 @@ function handleLogout() {
320 342 display: flex;
321 343 flex-direction: column;
322 344 overflow: hidden;
  345 + transition: all 0.35s cubic-bezier(0.25, 0.8, 0.25, 1);
323 346 }
324 347  
325 348 .menu-scroll {
... ... @@ -355,7 +378,7 @@ function handleLogout() {
355 378 border-radius: 12px;
356 379 border: none;
357 380 background: var(--line-strong);
358   - color: var(--soft-primary);
  381 + color: var(--brand);
359 382 cursor: pointer;
360 383 margin-bottom: 12px;
361 384 }
... ... @@ -576,9 +599,54 @@ h1 {
576 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 652 .header-action-group {
... ... @@ -609,12 +677,18 @@ h1 {
609 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 694 @media (max-width: 960px) {
... ...
src/style.css
... ... @@ -143,7 +143,7 @@ a {
143 143  
144 144 .ant-table-wrapper .ant-table-container {
145 145 border-radius: var(--radius-md);
146   - border: 1px solid rgba(194, 185, 239, 0.22);
  146 + border: 1px solid var(--line);
147 147 overflow: hidden;
148 148 }
149 149  
... ... @@ -338,6 +338,7 @@ a {
338 338 margin-block: 4px !important;
339 339 width: calc(100% - 16px) !important;
340 340 font-size: var(--font-size-md) !important;
  341 + color: var(--text-main);
341 342 }
342 343  
343 344 .ant-form-item {
... ... @@ -351,11 +352,10 @@ a {
351 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 361 .ant-menu-submenu-popup .ant-menu {
... ... @@ -384,15 +384,15 @@ a {
384 384  
385 385 .ant-menu-item:hover,
386 386 .ant-menu-submenu-title:hover {
387   - color: var(--brand-deep) !important;
  387 + color: var(--brand) !important;
388 388 background: var(--line) !important;
389 389 }
390 390  
391 391 .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) {
392   - color: var(--brand-deep);
  392 + color: var(--brand);
393 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 398 .ant-empty {
... ...
src/views/config/FeePlanList.vue
... ... @@ -913,8 +913,8 @@ onMounted(loadCities)
913 913 min-height: 36px;
914 914 padding: 0 14px;
915 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 920 .plan-list {
... ... @@ -985,7 +985,7 @@ onMounted(loadCities)
985 985 border: none;
986 986 border-radius: 12px;
987 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 991 .plan-save-button:hover,
... ... @@ -1018,9 +1018,9 @@ onMounted(loadCities)
1018 1018 min-height: 26px;
1019 1019 padding: 0 12px;
1020 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 1024 font-size: 12px;
1025 1025 font-weight: 700;
1026 1026 }
... ... @@ -1049,7 +1049,8 @@ onMounted(loadCities)
1049 1049  
1050 1050 .preview-card {
1051 1051 border-radius: 16px;
1052   - background: #fafbff;
  1052 + background: var(--panel-strong);
  1053 + border: 1px solid var(--line);
1053 1054 }
1054 1055  
1055 1056 .preview-subtitle {
... ...
src/views/dispatch/DispatchRuleList.vue
... ... @@ -672,7 +672,7 @@ onMounted(loadCities)
672 672 border: none;
673 673 border-radius: 12px;
674 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 678 .plan-save-button:hover,
... ... @@ -701,9 +701,9 @@ onMounted(loadCities)
701 701 min-height: 26px;
702 702 padding: 0 12px;
703 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 707 font-size: 12px;
708 708 font-weight: 700;
709 709 }
... ... @@ -796,13 +796,13 @@ onMounted(loadCities)
796 796  
797 797 :deep(.grab-scope-option-checked .grab-scope-card),
798 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 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 808 .dispatch-condition-list {
... ... @@ -835,9 +835,9 @@ onMounted(loadCities)
835 835 font-family: var(--font-display);
836 836 font-size: 18px;
837 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 843 .dispatch-condition-content {
... ... @@ -871,7 +871,7 @@ onMounted(loadCities)
871 871 .dispatch-condition-body {
872 872 margin-top: 14px;
873 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 877 .dispatch-condition-input {
... ...
src/views/open/OpenMockDelivery.vue
... ... @@ -523,7 +523,7 @@ watch(
523 523 .mock-form-section {
524 524 margin-top: 8px;
525 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 529 .mock-item-row {
... ...