Переглянути джерело

Merge branch 'feat_theme_zyh' of United_Software/k_online_ui into feat_theme

Jack Zhou 11 місяців тому
батько
коміт
430bd14301
29 змінених файлів з 540 додано та 104 видалено
  1. 35 7
      src/components/ContainerStatus/src/ContainerStatus.vue
  2. 0 0
      src/components/ContainerStatus/src/image/no_data.png
  3. BIN
      src/components/ContainerStatus/src/image/no_data_dark.png
  4. 19 18
      src/components/CustomizeColumns/src/CustomizeColumns.vue
  5. 10 9
      src/components/ShipmentStatus/src/ShipmentStatus.vue
  6. 27 2
      src/components/VEmpty/src/VEmpty.vue
  7. BIN
      src/components/VEmpty/src/images/default_dark_image.png
  8. 4 1
      src/components/VSliderVerification/src/VSliderVerification.vue
  9. 5 14
      src/main.ts
  10. 38 0
      src/stores/modules/theme.ts
  11. 11 4
      src/styles/elementui.scss
  12. 85 5
      src/styles/theme.scss
  13. 3 3
      src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue
  14. 10 6
      src/views/Booking/src/components/BookingDetail/src/components/EmailView.vue
  15. 7 4
      src/views/Booking/src/components/BookingTable/src/components/DownloadDialog.vue
  16. 151 3
      src/views/Layout/src/components/Header/HeaderView.vue
  17. 3 0
      src/views/Layout/src/components/Header/components/ChangePasswordDialog.vue
  18. 1 0
      src/views/Layout/src/components/Menu/MenuView.vue
  19. 27 1
      src/views/Login/src/components/ChangePasswordCard.vue
  20. 1 1
      src/views/Login/src/components/ErrorTips.vue
  21. 26 1
      src/views/Login/src/loginView.vue
  22. 6 3
      src/views/OperationLog/src/components/BookingTable/src/components/DownloadDialog.vue
  23. 30 5
      src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue
  24. 2 1
      src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue
  25. 17 3
      src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue
  26. 5 1
      src/views/Tracking/src/components/TrackingDetail/src/components/EmailDrawer.vue
  27. 9 8
      src/views/Tracking/src/components/TrackingDetail/src/components/RoutesView.vue
  28. 1 0
      src/views/Tracking/src/components/TrackingDetail/src/components/TransportStep.vue
  29. 7 4
      src/views/Tracking/src/components/TrackingTable/src/components/DownloadDialog.vue

+ 35 - 7
src/components/ContainerStatus/src/ContainerStatus.vue

@@ -1,6 +1,31 @@
 <script setup lang="ts">
-import emptyImage from './image/no-data.png'
+import emptyImage from './image/no_data.png'
 import { formatTimezone } from '@/utils/tools'
+import { useThemeStore } from '@/stores/modules/theme'
+import lightPng from './image/no_data.png'
+import darkPng from './image/no_data_dark.png'
+
+const emptyImg = ref(lightPng)
+
+const themeStore = useThemeStore()
+// 判断当前系统主题模式
+onMounted(() => {
+  watch(
+    () => themeStore.theme,
+    (newVal) => {
+      console.log(newVal, 'value')
+      if (newVal === 'dark') {
+        emptyImg.value = darkPng
+      } else {
+        emptyImg.value = lightPng
+      }
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+  )
+})
 
 const props = defineProps({
   data: Object
@@ -58,7 +83,12 @@ watch(
       </el-collapse-item>
     </el-collapse>
     <div v-else class="empty-content" style="">
-      <el-image :src="emptyImage" alt="empty"></el-image>
+      <img
+        :src="emptyImg"
+        :class="{ 'is-dark': themeStore.theme === 'dark' }"
+        fit="contain"
+        alt="empty"
+      />
       <div class="empty-text" style="">No data</div>
     </div>
     <div class="footer">
@@ -84,11 +114,9 @@ watch(
     flex-direction: column;
     align-items: center;
     height: 394px;
-    .el-image {
-      width: 200px;
-      height: 200px;
-      margin-top: 20px;
-      object-fit: cover;
+    img.is-dark {
+      display: block;
+      margin-top: 100px;
     }
     .empty-text {
       margin-top: 8px;

+ 0 - 0
src/components/ContainerStatus/src/image/no-data.png → src/components/ContainerStatus/src/image/no_data.png


BIN
src/components/ContainerStatus/src/image/no_data_dark.png


+ 19 - 18
src/components/CustomizeColumns/src/CustomizeColumns.vue

@@ -349,11 +349,7 @@ defineExpose({
           placeholder="Search columns you preffered"
         >
           <template #prefix>
-            <span class="iconfont_icon">
-              <svg class="iconfont" aria-hidden="true">
-                <use xlink:href="#icon-icon_search_b"></use>
-              </svg>
-            </span>
+            <span class="font_family icon-icon_search_b"></span>
           </template>
           <el-option
             v-for="item in searchOptions"
@@ -578,9 +574,10 @@ defineExpose({
       padding-left: 12px;
       border: 1px solid var(--color-border);
       border-radius: 6px;
+      background-color: var(--color-customize-column-item-bg);
 
       &:hover {
-        background-color: #fff1e5;
+        background-color: var(--color-customize-column-item-hover-bg);
         box-shadow: 4px 4px 32px 0px rgba(0, 0, 0, 0.1);
       }
 
@@ -590,7 +587,7 @@ defineExpose({
 
       span.draggable-icon {
         margin-right: 12px;
-        color: var(--color-neutral-3);
+        color: var(--color-customize-column-item-drag-icon);
       }
 
       .font_family {
@@ -608,13 +605,18 @@ defineExpose({
   }
 
   .ghost-column {
-    opacity: 0;
     cursor: move !important;
+    span {
+      opacity: 0;
+    }
+    border: 1px dashed var(--color-customize-column-item-drag-border) !important;
+    background-color: var(--color-customize-column-item-drag-bg) !important;
+    box-shadow: none !important;
   }
 
   .fallback-class {
     opacity: 1 !important;
-    background-color: #fff1e5 !important;
+    background-color: var(--color-customize-column-item-hover-bg) !important;
     cursor: move !important;
   }
 }
@@ -630,7 +632,7 @@ defineExpose({
     .el-tabs {
       .el-tabs__header {
         margin-bottom: 0px;
-        border-bottom: 1px solid #ebeef5;
+        border-bottom: 1px solid var(--color-customize-column-tabs-header-border);
       }
 
       .el-tabs__item {
@@ -655,9 +657,9 @@ defineExpose({
 }
 
 .right-select-columns {
-  background-color: #fffbf7;
+  background-color: var(--color-customize-column-right-section-bg);
   padding-top: 0;
-  border: 1px dashed var(--color-border);
+  border: 1px dashed var(--color-customize-column-right-section-border);
   border-radius: 12px;
 
   & > .title {
@@ -672,10 +674,6 @@ defineExpose({
     padding: 8px;
     padding-bottom: 0px;
   }
-
-  .column-item {
-    background-color: #fff;
-  }
 }
 </style>
 <style lang="scss">
@@ -701,13 +699,16 @@ defineExpose({
 
   .el-tabs__nav-prev {
     border-right: 1px solid var(--color-border);
-    box-shadow: 1px 0px 10px rgba(0, 0, 0, 0.2);
+    box-shadow: 2px 0px 12px rgba(0, 0, 0, 0.3);
+    // .el-icon {
+    //   color: white;
+    // }
     /* 左侧阴影 */
   }
 
   .el-tabs__nav-next {
     border-left: 1px solid var(--color-border);
-    box-shadow: -1px 0px 10px rgba(0, 0, 0, 0.2);
+    box-shadow: -2px 0px 12px rgba(0, 0, 0, 0.2);
     /* 左侧阴影 */
   }
 

+ 10 - 9
src/components/ShipmentStatus/src/ShipmentStatus.vue

@@ -111,7 +111,7 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
       font-weight: 700;
       text-align: center;
       line-height: 16px;
-      color: #fff;
+      color: var(--color-mode);
     }
     .right-info {
       flex: 1;
@@ -143,7 +143,7 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
         .label {
           // width: 96px;
           background-color: var(--color-neutral-1);
-          color: #fff;
+          color: var(--color-mode);
           font-weight: 500;
           font-size: 12px;
           border-radius: 3px 0 0 3px;
@@ -151,7 +151,7 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
         .detail-path {
           flex: 1;
           width: 252px;
-          background-color: #fff;
+          background-color: transparent;
           font-weight: 700;
           color: var(--color-neutral-1);
           border: 1px solid #bfc1c3;
@@ -190,21 +190,22 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
   &.last {
     & > .data {
       .left-step-icon {
-        background-color: #ccd1db;
+        background-color: var(--color-shipment-status-label-bg);
       }
       .right-info {
         .step-dot {
-          background-color: #ccd1db;
+          background-color: var(--color-shipment-status-label-bg);
         }
         .path {
           .label {
-            background-color: #ccd1db;
+            background-color: var(--color-shipment-status-label-bg);
 
-            font-weight: 600;
-            color: var(--color-neutral-1);
+            font-weight: 500;
+            color: var(--color-shipment-status-label-font-color);
           }
           .detail-path {
-            border-color: #ccd1db;
+            color: var(--color-shipment-status-detail-path-font-color);
+            border-color: var(--color-shipment-status-label-bg);
           }
         }
       }

+ 27 - 2
src/components/VEmpty/src/VEmpty.vue

@@ -1,9 +1,34 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import lightPng from './images/default_image.png'
+import darkPng from './images/default_dark_image.png'
+import { useThemeStore } from '@/stores/modules/theme'
+
+const emptyImg = ref(lightPng)
+
+const themeStore = useThemeStore()
+// 判断当前系统主题模式
+onMounted(() => {
+  watch(
+    () => themeStore.theme,
+    (newVal) => {
+      if (newVal === 'dark') {
+        emptyImg.value = darkPng
+      } else {
+        emptyImg.value = lightPng
+      }
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+  )
+})
+</script>
 
 <template>
   <div class="v-empty">
     <div class="empty-img">
-      <img src="./images/default_image.png" alt="" />
+      <img :src="emptyImg" alt="" />
     </div>
     <p class="title">
       <slot name="title">No Results Found</slot>

BIN
src/components/VEmpty/src/images/default_dark_image.png


+ 4 - 1
src/components/VSliderVerification/src/VSliderVerification.vue

@@ -47,7 +47,7 @@ const addTipsNode = () => {
 
 const styleMap = {
   start: {
-    thumbColor: 'var(--color-neutral-1)',
+    thumbColor: 'var(--color-slider-thumb-start)',
     thumbIcon: 'icon-icon_drag__line_b',
     trackBackground: 'var(--color-success)'
   },
@@ -163,6 +163,9 @@ defineExpose({
   width: 400px;
   height: 373px;
   padding: 40px;
+  background-color: var(--color-slider-bg);
+  border-radius: 16px;
+  box-shadow: -2px 2px 12px 0 rgba(0, 0, 0, 0.5);
   .tips {
     margin-bottom: 16px;
     text-align: center;

+ 5 - 14
src/main.ts

@@ -24,12 +24,6 @@ const app = createApp(App)
 
 // 动态加载暗黑主题
 async function loadDarkTheme() {
-  // const linkEl = document.createElement('link')
-  // linkEl.rel = 'stylesheet'
-  // linkEl.href = 'https://unpkg.com/element-plus/theme-chalk/dark/css-vars.css' // 暗黑主题 CSS
-  // linkEl.id = 'dark-theme-style'
-  // document.head.appendChild(linkEl)
-
   await import('element-plus/theme-chalk/dark/css-vars.css') // 动态导入暗黑主题
 }
 // 动态移除暗黑主题
@@ -39,18 +33,15 @@ function unloadDarkTheme() {
     darkThemeStylesheet.remove()
   }
 }
-// 根据用户偏好或系统设置决定是否加载暗黑主题
-if (
-  (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ||
-  localStorage.getItem('theme') === 'dark'
-) {
+// 根据用户偏好或系统设置决定是否加载暗黑主题 (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ||
+if (localStorage.getItem('theme') === 'dark') {
   loadDarkTheme()
   document.documentElement.classList.add('dark')
 }
 // 根据用户偏好或系统设置决定是否添加暗黑模式类名
-if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
-  document.documentElement.classList.add('dark')
-}
+// if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
+//   document.documentElement.classList.add('dark')
+// }
 // 提供一个全局方法来切换主题
 app.config.globalProperties.$toggleDarkMode = () => {
   const html = document.documentElement

+ 38 - 0
src/stores/modules/theme.ts

@@ -0,0 +1,38 @@
+import { defineStore } from 'pinia'
+
+interface ThemeState {
+  theme: string
+}
+export const useThemeStore = defineStore('theme', {
+  state: (): ThemeState => ({
+    theme: localStorage.getItem('theme') || 'light'
+  }),
+  actions: {
+    toggleTheme(theme: string) {
+      const html = document.documentElement
+      if (html.classList.contains('dark') && theme === 'light') {
+        unloadDarkTheme()
+        html.classList.remove('dark')
+        localStorage.setItem('theme', 'light')
+        this.theme = 'light'
+      } else if (!html.classList.contains('dark') && theme === 'dark') {
+        loadDarkTheme()
+        html.classList.add('dark')
+        localStorage.setItem('theme', 'dark')
+        this.theme = 'dark'
+      }
+    }
+  }
+})
+
+// 动态加载暗黑主题
+async function loadDarkTheme() {
+  await import('element-plus/theme-chalk/dark/css-vars.css') // 动态导入暗黑主题
+}
+// 动态移除暗黑主题
+function unloadDarkTheme() {
+  const darkThemeStylesheet = document.getElementById('dark-theme-style')
+  if (darkThemeStylesheet) {
+    darkThemeStylesheet.remove()
+  }
+}

+ 11 - 4
src/styles/elementui.scss

@@ -286,7 +286,7 @@ div.el-message-box {
   .el-message-box__header {
     height: 56px;
     padding: 16px;
-    background-color: var(--color-table-header-bg);
+    background-color: var(--color-message-box-header-bg);
     & > span {
       font-weight: 700;
       font-size: var(--font-size-4);
@@ -419,8 +419,13 @@ div .el-checkbox.el-checkbox--large span.el-checkbox__inner::after {
 }
 div .el-popper__arrow,
 div .el-popper__arrow:before {
-  height: 0;
-  width: 0;
+  // height: 0;
+  // width: 0;
+}
+.el-popper.is-dark,
+div.el-popper.is-dark > .el-popper__arrow:before {
+  background-color: var(--color-el-popper-bg);
+  border: var(--color-el-popper-bg);
 }
 div .el-popper[data-popper-placement^='bottom'] > .el-popper__arrow {
   top: 0;
@@ -447,10 +452,12 @@ div .el-popper[data-popper-placement^='bottom'] .el-popper__arrow:before {
 div .el-popover.el-popper {
   padding: 0;
 }
-.el-popper.is-dark {
+div.el-popper.is-dark {
   span {
     color: #fff;
   }
+  background-color: var(--color-el-popper-bg);
+  border-color: var(--color-el-popper-bg);
 }
 div .el-collapse-item__arrow,
 .el-tabs__nav {

+ 85 - 5
src/styles/theme.scss

@@ -148,6 +148,7 @@
 
   // 发送邮件部分
 
+  --color-avatar: #fff;
   --color-email-bg: #f6f8fa;
   --color-detail-email-record-bg: #eceef1;
   --color-email-border: #eaebed;
@@ -175,16 +176,47 @@
   );
   --scoring-smile-radio-color: #e0e2e6;
 
+  --color-el-popper-bg: #303133;
   // 滚动条
-  --color-scrollbar-thumb: #656f7d;
+  --color-scrollbar-thumb: #d9dddf;
 
+  --color-message-box-header-bg: #f6f8fa;
   --color-table-header-bg: #f8f9fd;
   --color-table-click-row-bg: #ffe3cc;
 
+  --color-download-file-filter-tag-bg: #eceef0;
+  --color-download-file-selected-column-tag-bg: #eceef0;
+
+  --color-tracking-routes-item-bg: #fef8f3;
+  --color-tracking-routes-item-leg-bg: #f6f2ee;
+
+  --color-customize-column-right-section-bg: #fffbf7;
+  --color-customize-column-right-section-border: #eaebed;
+  --color-customize-column-item-bg: #fff;
+  --color-customize-column-item-hover-bg: #fff1e5;
+  --color-customize-column-item-drag-icon: var(--color-neutral-3);
+  --color-customize-column-item-drag-border: var(--color-customize-column-right-section-bg);
+  --color-customize-column-item-drag-bg: var(--color-customize-column-right-section-bg);
+  --color-customize-column-tabs-header-border: #ebeef5;
+
+  --color-shipment-status-label-bg: #ccd1db;
+
+  --color-slider-bg: #fff;
+  --color-slider-thumb-start: #2b2f36;
+
+  --color-public-tracking-bg-mask: rgba(255, 255, 255, 0.85);
+  --color-public-tracking-search-input-btn: #000;
+  --color-public-tracking-search-input-border: #2b2f36;
+
+  --color-v-box-content-drag-bg: #fff;
+
+  --color-toggle-btn-module-bg: #f0f1f3;
+  --color-toggle-btn-module-active-bg: #fff;
+
+  --color-user-config-title-bottom-border: #eeeeed;
   // 输入框禁用的颜色
   --input-disabled-bg-color: #f5f7fa;
   --input-disabled-text-color: #a8abb2;
-
 }
 
 :root.dark {
@@ -199,20 +231,56 @@
 
   --border-hover-color: #654f3f;
 
-  --color-dialog-header-bg: #3e454f;
-  --color-dialog-body-bg: #30353c;
+  --color-download-file-filter-tag-bg: #3f4249;
+  --color-download-file-selected-column-tag-bg: #474e57;
+
+  --color-customize-column-right-section-bg: #453b36;
+  --color-customize-column-right-section-border: var(--color-theme);
+  --color-customize-column-item-bg: #3a4149;
+  --color-customize-column-item-hover-bg: #585b60;
+  --color-customize-column-item-drag-icon: var(--color-neutral-1);
+  --color-customize-column-item-drag-border: var(--color-theme);
+  --color-customize-column-item-drag-bg: #7d4c26;
+  --color-customize-column-tabs-header-border: #3f434a;
 
+  --color-shipment-status-label-bg: #656f7d;
+  --color-shipment-status-detail-path-font-color: #b5b7ba;
+  --color-shipment-status-label-font-color: #c6cad0;
+
+  --color-slider-bg: #3f434a;
+  --color-slider-thumb-start: #646a73;
+
+  --color-public-tracking-bg-mask: rgba(43, 47, 54, 0.75);
+  --color-public-tracking-search-input-btn: #ed6d00;
+  --color-public-tracking-search-input-border: #656f7d;
+
+  --color-toggle-btn-module-bg: #434b53;
+  --color-toggle-btn-module-active-bg: #656f7d;
+
+  --color-user-config-title-bottom-border: #3f434a;
   // 滚动条
   --color-scrollbar-thumb: #656f7d;
 
   // 菜单选中背景
   --color-mune-active-bg: #553d2b;
 
+  --color-tracking-routes-item-bg: #403631;
+  --color-tracking-routes-item-leg-bg: #524843;
+
+  --color-v-box-content-drag-bg: #2b2f36;
+
   // 邮件
   --w-e-toolbar-bg-color: var(--color-email-bg);
   --w-e-textarea-bg-color: var(--color-email-bg);
   --w-e-toolbar-border-color: #656f7d;
   --w-e-toolbar-color: var(--color-neutral-1);
+  --w-e-toolbar-active-bg-color: #553d2b;
+  button.w-e-menu-tooltip-v5::before {
+    color: var(--color-neutral-1);
+  }
+  div.w-e-bar-item button:hover {
+    color: var(--color-neutral-1);
+  }
   --color-email-border: #656f7d;
   --color-email-bg: #3c414a;
   --color-detail-email-record-bg: #343a43;
@@ -221,8 +289,9 @@
   --color-header-bg: #343a43;
 
   // ElementUI
+  --color-message-box-header-bg: #3e454f;
   // 整体背景颜色
-  --el-bg-color: #2b2f36;
+  --el-bg-color: #30353c;
   // 按钮边框颜色
   --color-accent-13: #656f7d;
   --el-border: #656f7d;
@@ -231,6 +300,17 @@
   }
   --color-table-header-bg: #34383f;
   --el-input-focus: #2b2f36;
+  --color-dialog-header-bg: #3e454f;
+  --color-dialog-body-bg: #30353c;
+  div.el-card {
+    --el-card-bg-color: #30353c;
+  }
+  --color-el-popper-bg: #1a1c20;
+  // --el-bg-color: var(--color-neutral-1);
+  --el-bg-color-overlay: #30353c;
+  div.el-popper.is-dark {
+    --el-bg-color: var(--color-neutral-1);
+  }
 
   // vxe-table
   --vxe-ui-layout-background-color: #2b2f36;

+ 3 - 3
src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue

@@ -317,6 +317,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
           .line_container hr {
             width: 100%;
             border-radius: 0 2px 6px 0;
+            border-color: var(--color-neutral-1);
           }
           .line_container .right-icon {
             position: absolute;
@@ -324,7 +325,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
             right: 0px;
             width: 10px;
             height: 11px;
-            border-top: 2px solid #000000;
+            border-top: 2px solid var(--color-neutral-1);
             transform: rotate(30deg);
             border-radius: 0 1px 0 0;
           }
@@ -339,9 +340,8 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
 }
 .fallback-class {
   opacity: 1 !important;
-  // background-color: #fff1e5 !important;
   cursor: move !important;
-  background-color: #fff;
+  background-color: var(--color-v-box-content-drag-bg);
   box-shadow: 4px 4px 32px 0px rgba(0, 0, 0, 0.2);
   border-radius: 12px;
 }

+ 10 - 6
src/views/Booking/src/components/BookingDetail/src/components/EmailView.vue

@@ -238,7 +238,7 @@ const sendEmail = () => {
   padding: 16px;
   border-radius: 12px;
   border-bottom: 1px solid var(--color-border);
-  background: var(--color-header-bg);
+  background: var(--color-email-bg);
 
   .show-records {
     max-height: 370px;
@@ -248,7 +248,7 @@ const sendEmail = () => {
 
 :deep(.w-e-text-container) {
   min-height: 170px;
-
+  border-radius: 0 0 6px 6px;
   p {
     margin: 0px;
   }
@@ -278,7 +278,11 @@ const sendEmail = () => {
 .text-editor {
   margin-top: 16px;
   border-radius: 6px;
-  border: 1px solid var(--color-border);
+  border: 1px solid var(--color-email-border);
+  // overflow: hidden;
+  :deep(div.w-e-toolbar) {
+    border-radius: 6px 6px 0 0;
+  }
 }
 
 .record-item {
@@ -300,7 +304,7 @@ const sendEmail = () => {
       div {
         height: 14px;
         line-height: 14px;
-        color: #fff;
+        color: var(--color-avatar);
         font-weight: 700;
       }
     }
@@ -324,14 +328,14 @@ const sendEmail = () => {
 
   & > .content {
     padding: 16px 6px;
-    background-color: #eceef1;
+    background-color: var(--color-detail-email-record-bg);
     border-radius: 6px;
   }
 }
 
 :deep(.text-editor) {
   & > div:first-of-type {
-    border-bottom: 1px solid var(--color-border) !important;
+    border-bottom: 1px solid var(--color-email-border) !important;
   }
 }
 </style>

+ 7 - 4
src/views/Booking/src/components/BookingTable/src/components/DownloadDialog.vue

@@ -34,7 +34,7 @@ defineExpose({
 
 <template>
   <div>
-    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="480">
+    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="540">
       <div class="download-dialog">
         <div class="select-data">
           <div style="display: inline-block">
@@ -95,12 +95,14 @@ defineExpose({
   display: flex;
   flex-wrap: wrap;
   gap: 8px;
+  max-height: 120px;
   margin-top: 8px;
+  overflow: auto;
 
   .filter-item {
     height: 22px;
     padding: 0px 8px;
-    background-color: #eceef0;
+    background-color: var(--color-download-file-filter-tag-bg);
     border-radius: 12px;
     line-height: 22px;
     font-size: 12px;
@@ -144,11 +146,12 @@ defineExpose({
     }
 
     .select-columns {
+      max-height: 350px;
       padding: 8px;
       margin-top: 8px;
       background-color: var(--color-header-bg);
       border-radius: 6px;
-      overflow: hidden;
+      overflow: auto;
 
       &.show {
         max-height: 500px;
@@ -168,7 +171,7 @@ defineExpose({
         .column-item {
           height: 22px;
           padding: 0px 8px;
-          background-color: #eceef0;
+          background-color: var(--color-download-file-selected-column-tag-bg);
           line-height: 22px;
           border-radius: 12px;
           font-size: 12px;

+ 151 - 3
src/views/Layout/src/components/Header/HeaderView.vue

@@ -7,12 +7,20 @@ import { useUserStore } from '@/stores/modules/user'
 import { useHeaderSearch } from '@/stores/modules/headerSearch'
 import { onBeforeRouteUpdate } from 'vue-router'
 import { useLoadingState } from '@/stores/modules/loadingState'
+import { useThemeStore } from '@/stores/modules/theme'
 
+const themeStore = useThemeStore()
 const userStore = useUserStore()
 const route = useRoute()
 const router = useRouter()
 const headerSearch = useHeaderSearch()
 
+const themePopoverRef = ref()
+// 切换系统主题颜色
+const toggleThemeMode = (theme: string) => {
+  themeStore.toggleTheme(theme)
+}
+
 const searchValue = ref('')
 // 用于判断是否在搜索后跳转页面,跳转后清空搜索框的值
 const isJumpPageBySearch = ref(false)
@@ -142,7 +150,6 @@ const handleLogin = () => {
   <div class="layout-toolbar">
     <VBreadcrumb></VBreadcrumb>
     <div class="right-info">
-      <el-button @click="$toggleDarkMode">切换主题</el-button>
       <el-input
         v-model="searchValue"
         size="large"
@@ -156,12 +163,67 @@ const handleLogin = () => {
       <!-- <span class="font_family icon-icon_notice_b" style="font-size: 18px"></span>
       <span class="font_family icon-icon_language_b" style="font-size: 16px"></span> -->
 
+      <el-popover
+        placement="bottom-end"
+        :width="400"
+        trigger="click"
+        ref="themePopoverRef"
+        popper-class="toggle-theme-popover"
+      >
+        <div>
+          <div class="header">
+            <span class="title">Themes</span>
+            <el-button @click="themePopoverRef.hide()" class="close-icon el-button--text">
+              <span class="font_family icon-icon_reject_b"></span>
+            </el-button>
+          </div>
+          <div class="tips">
+            Customize your workspace by changing the appearance and theme color
+          </div>
+          <div class="picture-module">
+            <div class="item" :class="{ active: themeStore.theme === 'light' }">
+              <img src="./images/kandroid.png" alt="" />
+              <div v-if="themeStore.theme === 'light'" class="selected-icon">
+                <span class="font_family icon-icon_confirm_b"></span>
+              </div>
+            </div>
+            <div class="item" :class="{ active: themeStore.theme === 'dark' }">
+              <img src="./images/kandroid.png" alt="" />
+              <div v-if="themeStore.theme === 'dark'" class="selected-icon">
+                <span class="font_family icon-icon_confirm_b"></span>
+              </div>
+            </div>
+          </div>
+          <div class="btn-module">
+            <div
+              class="btn-item"
+              @click="toggleThemeMode('light')"
+              :class="{ active: themeStore.theme === 'light' }"
+            >
+              <span class="font_family icon-icon_light_b"></span>
+              Light
+            </div>
+            <div
+              class="btn-item"
+              @click="toggleThemeMode('dark')"
+              :class="{ active: themeStore.theme === 'dark' }"
+            >
+              <span class="font_family icon-icon_dark_b"></span>Dark
+            </div>
+          </div>
+        </div>
+        <template #reference>
+          <el-button>
+            <span class="font_family icon-icon_themes_b" style="font-size: 16px"></span
+          ></el-button>
+        </template>
+      </el-popover>
+
       <el-popover
         placement="bottom-end"
         :width="256"
         trigger="click"
         popper-class="user-config-popover"
-        content="this is content, this is content, this is content"
       >
         <div class="title">
           <div class="avatar">
@@ -263,6 +325,92 @@ const handleLogin = () => {
 }
 </style>
 <style lang="scss">
+div.el-popover.el-popper.toggle-theme-popover {
+  width: 400px;
+  height: 278px;
+  padding: 16px;
+  .header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 8px;
+    .title {
+      font-size: 18px;
+      font-weight: 700;
+    }
+    .close-icon {
+      width: 24px;
+      cursor: pointer;
+    }
+  }
+  .tips {
+    color: var(--color-neutral-2);
+  }
+  .picture-module {
+    display: flex;
+    justify-content: center;
+    gap: 6px;
+    margin-top: 16px;
+    .item {
+      position: relative;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: 174px;
+      height: 108px;
+      padding: 8px;
+      img {
+        width: 154px;
+        height: 90px;
+        border-radius: 6px;
+      }
+      &.active {
+        border: 2px solid var(--color-theme);
+        border-radius: 6px;
+      }
+      .selected-icon {
+        position: absolute;
+        bottom: 8px;
+        left: 50%;
+        transform: translateX(-50%);
+        height: 16px;
+        span {
+          height: 16px;
+          width: 16px;
+          background-color: var(--color-theme);
+          color: #fff;
+          border-radius: 50%;
+        }
+      }
+    }
+  }
+  .btn-module {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 368px;
+    height: 40px;
+    margin-top: 10px;
+    padding: 0 8px;
+    background-color: var(--color-toggle-btn-module-bg);
+    border-radius: 6px;
+    .btn-item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 4px;
+      width: 180px;
+      height: 32px;
+      border-radius: 6px;
+      color: var(--color-neutral-2);
+      cursor: pointer;
+      &.active {
+        background-color: var(--color-toggle-btn-module-active-bg);
+        color: var(--color-neutral-1);
+      }
+    }
+  }
+}
 div.el-popover.el-popper.user-config-popover {
   padding: 8px;
   width: 240px;
@@ -272,7 +420,7 @@ div.el-popover.el-popper.user-config-popover {
     align-items: center;
     gap: 8px;
     height: 70px;
-    border-bottom: 1px solid #eeeeed;
+    border-bottom: 1px solid var(--color-user-config-title-bottom-border);
     & > .avatar {
       width: 48px;
       height: 48px;

+ 3 - 0
src/views/Layout/src/components/Header/components/ChangePasswordDialog.vue

@@ -210,7 +210,10 @@ defineExpose({
   .form-item {
     margin-bottom: 16px;
     .label {
+      display: inline-block;
+      margin-bottom: 4px;
       font-size: 12px;
+      color: var(--color-neutral-2);
     }
     .error {
       font-size: 12px;

+ 1 - 0
src/views/Layout/src/components/Menu/MenuView.vue

@@ -221,6 +221,7 @@ const jumpLink = (link: string) => {
   margin: 0 12px 2px;
   padding-left: 8px !important;
   border-radius: 6px;
+  color: var(--color-neutral-1);
   &:hover {
     background-color: var(--color-mune-active-bg);
   }

+ 27 - 1
src/views/Login/src/components/ChangePasswordCard.vue

@@ -1,7 +1,29 @@
 <script setup lang="ts">
 import { useRouter } from 'vue-router'
+import { useThemeStore } from '@/stores/modules/theme'
 
 const router = useRouter()
+
+const themeStore = useThemeStore()
+const changePasswordRef = ref()
+// 判断当前系统主题模式
+onMounted(() => {
+  watch(
+    () => themeStore.theme,
+    (newVal) => {
+      if (newVal === 'dark') {
+        changePasswordRef.value.classList.add('dark-bg')
+      } else {
+        changePasswordRef.value.classList.remove('dark-bg')
+      }
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+  )
+})
+
 const loginForm = ref({
   username: '',
   oldPassword: '',
@@ -112,7 +134,7 @@ const checkPassword = () => {
 </script>
 
 <template>
-  <div class="login">
+  <div class="login" ref="changePasswordRef">
     <el-card class="login-card">
       <div class="title">
         <span class="welcome">Change Password</span>
@@ -229,6 +251,10 @@ const checkPassword = () => {
   width: 100%;
   background: url(../image/bg.png) no-repeat center center;
   background-size: cover;
+  &.dark-bg {
+    background: url(../image/bg-dark.png) no-repeat center center;
+    background-size: cover;
+  }
 }
 
 .login-card {

+ 1 - 1
src/views/Login/src/components/ErrorTips.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-const dialogVisible = ref(false)
+const dialogVisible = ref(true)
 
 const openDialog = () => {
   dialogVisible.value = true

+ 26 - 1
src/views/Login/src/loginView.vue

@@ -2,12 +2,33 @@
 import { useRouter, useRoute } from 'vue-router'
 import ErrorTips from './components/ErrorTips.vue'
 import { useUserStore } from '@/stores/modules/user'
+import { useThemeStore } from '@/stores/modules/theme'
 import ScoringSystem from '@/views/Dashboard/src/components/ScoringSystem.vue'
 import CryptoJS from 'crypto-js'
 import dayjs from 'dayjs'
 
 const router = useRouter()
 const route = useRoute()
+const themeStore = useThemeStore()
+
+const loginRef = ref()
+// 判断当前系统主题模式
+onMounted(() => {
+  watch(
+    () => themeStore.theme,
+    (newVal) => {
+      if (newVal === 'dark') {
+        loginRef.value.classList.add('dark-bg')
+      } else {
+        loginRef.value.classList.remove('dark-bg')
+      }
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+  )
+})
 
 // 手动获取url中的参数,直接获取route.query的参数时如果有+号会被转义成空格
 const getQueryParams = (url: string) => {
@@ -340,7 +361,7 @@ const errorTipsRef = ref()
 </script>
 
 <template>
-  <div class="login">
+  <div class="login" ref="loginRef">
     <ScoringSystem class="scoring-system"></ScoringSystem>
     <el-card class="login-card" v-if="status === 'login'">
       <div class="card-title">
@@ -485,6 +506,10 @@ const errorTipsRef = ref()
   width: 100%;
   background: url(../src/image/bg.png) no-repeat center center;
   background-size: cover;
+  &.dark-bg {
+    background: url(../src/image/bg-dark.png) no-repeat center center;
+    background-size: cover;
+  }
   .scoring-system {
     position: absolute;
     top: 0;

+ 6 - 3
src/views/OperationLog/src/components/BookingTable/src/components/DownloadDialog.vue

@@ -32,7 +32,7 @@ defineExpose({
 
 <template>
   <div>
-    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="480">
+    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="540">
       <div class="download-dialog">
         <div class="select-data">
           <div style="display: inline-block">
@@ -90,12 +90,14 @@ defineExpose({
   display: flex;
   flex-wrap: wrap;
   gap: 8px;
+  max-height: 120px;
   margin-top: 8px;
+  overflow: auto;
 
   .filter-item {
     height: 22px;
     padding: 0px 8px;
-    background-color: #eceef0;
+    background-color: var(--color-download-file-filter-tag-bg);
     border-radius: 12px;
     line-height: 22px;
     font-size: 12px;
@@ -139,6 +141,7 @@ defineExpose({
     }
 
     .select-columns {
+      max-height: 350px;
       padding: 8px;
       margin-top: 8px;
       background-color: var(--color-header-bg);
@@ -163,7 +166,7 @@ defineExpose({
         .column-item {
           height: 22px;
           padding: 0px 8px;
-          background-color: #eceef0;
+          background-color: var(--color-download-file-selected-column-tag-bg);
           line-height: 22px;
           border-radius: 12px;
           font-size: 12px;

+ 30 - 5
src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue

@@ -4,6 +4,27 @@ import { useHeaderSearch } from '@/stores/modules/headerSearch'
 import SlideVerify from './components/SlideVerify.vue'
 import CryptoJS from 'crypto-js'
 import dayjs from 'dayjs'
+import { useThemeStore } from '@/stores/modules/theme'
+
+const themeStore = useThemeStore()
+const publicTrackingRef = ref()
+// 判断当前系统主题模式
+onMounted(() => {
+  watch(
+    () => themeStore.theme,
+    (newVal) => {
+      if (newVal === 'dark') {
+        publicTrackingRef.value.classList.add('dark-bg')
+      } else {
+        publicTrackingRef.value.classList.remove('dark-bg')
+      }
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+  )
+})
 
 const router = useRouter()
 
@@ -91,7 +112,7 @@ const encryptPassword = (password) => {
 </script>
 
 <template>
-  <div class="public-tracking-search" v-vloading="loading">
+  <div class="public-tracking-search" ref="publicTrackingRef" v-vloading="loading">
     <div class="search-info">
       <div class="title">Tracking</div>
       <el-input
@@ -155,6 +176,10 @@ const encryptPassword = (password) => {
   background-size: cover;
   z-index: 1;
   height: 100%;
+  &.dark-bg {
+    background: url(../../../../../Login/src/image/bg-dark.png) no-repeat center center;
+    background-size: cover;
+  }
 }
 
 .public-tracking-search::before {
@@ -164,7 +189,7 @@ const encryptPassword = (password) => {
   left: 0;
   right: 0;
   bottom: 0;
-  background-color: rgba(255, 255, 255, 0.85); /* 半透明白色背景层 */
+  background-color: var(--color-public-tracking-bg-mask); /* 半透明白色背景层 */
   z-index: -1; /* 将伪元素放置在背景图片的下方 */
 }
 
@@ -187,8 +212,8 @@ const encryptPassword = (password) => {
     }
     :deep(.el-input-group__append) {
       padding: 0;
-      background-color: black;
-      border: 1px solid black;
+      background-color: var(--color-public-tracking-search-input-btn);
+      border: 1px solid var(--color-public-tracking-search-input-btn);
       border-radius: 0 6px 6px 0;
       box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
       span {
@@ -217,7 +242,7 @@ const encryptPassword = (password) => {
 }
 .public-tracking-search-input {
   :deep(.el-input__wrapper) {
-    box-shadow: 0 0 0 1px #2b2f36 inset;
+    box-shadow: 0 0 0 1px var(--color-public-tracking-search-input-border) inset;
   }
 }
 </style>

+ 2 - 1
src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue

@@ -265,6 +265,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
           .line_container hr {
             width: 100%;
             border-radius: 0 2px 6px 0;
+            border-color: var(--color-neutral-1);
           }
           .line_container .right-icon {
             position: absolute;
@@ -272,7 +273,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
             right: 0px;
             width: 10px;
             height: 11px;
-            border-top: 2px solid #000000;
+            border-top: 2px solid var(--color-neutral-1);
             transform: rotate(30deg);
             border-radius: 0 1px 0 0;
           }

+ 17 - 3
src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue

@@ -14,9 +14,12 @@ import { transportationMode } from '@/components/TransportationMode'
 import { useRoute } from 'vue-router'
 import { useOverflow } from '@/hooks/useOverflow'
 import { formatTimezone } from '@/utils/tools'
+import { useThemeStore } from '@/stores/modules/theme'
 
 const route = useRoute()
 
+const themeStore = useThemeStore()
+
 // 可拖拽模块的列表
 const boxList = ref([
   { id: 1, name: 'Basic Information' },
@@ -113,7 +116,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
 
 <template>
   <div class="tracking-detail" v-vloading="loading">
-    <div class="header">
+    <div class="header" :class="{ 'is-dark': themeStore.theme === 'dark' }">
       <div class="detail-status">
         <span
           class="font_family"
@@ -308,6 +311,16 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
       rgba(224, 247, 249, 0.6) 80.46%,
       rgba(255, 255, 255, 0.3)
     );
+    &.is-dark {
+      background: linear-gradient(
+        270deg,
+        rgba(43, 47, 54, 0.1) 1.88%,
+        rgba(255, 182, 121, 0.1) 15.6%,
+        rgba(118, 145, 255, 0.1) 49.92%,
+        rgba(96, 242, 255, 0.1) 81.78%,
+        rgba(43, 47, 54, 0.1) 97.95%
+      );
+    }
 
     .detail-status {
       position: relative;
@@ -395,6 +408,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
           .line_container hr {
             width: 100%;
             border-radius: 0 2px 6px 0;
+            border-color: var(--color-neutral-1);
           }
           .line_container .right-icon {
             position: absolute;
@@ -402,7 +416,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
             right: 0px;
             width: 10px;
             height: 11px;
-            border-top: 2px solid #000000;
+            border-top: 2px solid var(--color-neutral-1);
             transform: rotate(30deg);
             border-radius: 0 1px 0 0;
           }
@@ -428,7 +442,7 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
 .fallback-class {
   opacity: 1 !important;
   cursor: move !important;
-  background-color: #fff;
+  background-color: var(--color-v-box-content-drag-bg);
   box-shadow: 4px 4px 32px 0px rgba(0, 0, 0, 0.2);
   border-radius: 12px;
 }

+ 5 - 1
src/views/Tracking/src/components/TrackingDetail/src/components/EmailDrawer.vue

@@ -251,6 +251,7 @@ const sendEmail = () => {
 
 :deep(.w-e-text-container) {
   min-height: 170px;
+  border-radius: 0 0 6px 6px;
   p {
     margin: 0px;
   }
@@ -283,7 +284,10 @@ const sendEmail = () => {
   margin-top: 16px;
   border-radius: 6px;
   border: 1px solid var(--color-email-border);
-  overflow: hidden;
+  // overflow: hidden;
+  :deep(div.w-e-toolbar) {
+    border-radius: 6px 6px 0 0;
+  }
 }
 
 .record-item {

+ 9 - 8
src/views/Tracking/src/components/TrackingDetail/src/components/RoutesView.vue

@@ -233,9 +233,9 @@ const { isOverflow: isDetailDestinationOverflow } = useOverflow(detailDestinatio
   height: 80px;
   cursor: pointer;
   &:hover {
-    background-color: #fef8f3;
+    background-color: var(--color-tracking-routes-item-bg);
     .serial-number {
-      background-color: #f6f2ee;
+      background-color: var(--color-tracking-routes-item-leg-bg);
     }
   }
   .serial-number {
@@ -357,16 +357,16 @@ const { isOverflow: isDetailDestinationOverflow } = useOverflow(detailDestinatio
     margin-left: 8px;
   }
   .line_container hr {
-    border-color: #484c52;
+    border-color: var(--color-neutral-1);
     border-top: none;
   }
 
   .line_container .right-icon {
-    border-top: 1px solid #484c52;
-    border-radius: 0 1px 1px 0;
-    top: -2px;
+    border-top: 2px solid var(--color-neutral-1);
+    border-radius: 0 2px 2px 0px;
+    top: -4px;
     right: 0px;
-    width: 10px;
+    width: 12px;
     height: 12px;
   }
   .date-info {
@@ -487,6 +487,7 @@ const { isOverflow: isDetailDestinationOverflow } = useOverflow(detailDestinatio
   width: 100%;
   min-width: 16px;
   border-radius: 0 2px 6px 0;
+  border-color: var(--color-neutral-1);
 }
 
 .line_container .right-icon {
@@ -495,7 +496,7 @@ const { isOverflow: isDetailDestinationOverflow } = useOverflow(detailDestinatio
   right: 0px;
   width: 10px;
   height: 11px;
-  border-top: 2px solid #000000;
+  border-top: 2px solid var(--color-neutral-1);
   transform: rotate(30deg);
   border-radius: 0 1px 0 0;
 }

+ 1 - 0
src/views/Tracking/src/components/TrackingDetail/src/components/TransportStep.vue

@@ -67,6 +67,7 @@ const handleTabClick = (name: string) => {
   }
   .content {
     height: calc(100% - 48px);
+    background-color: var(--color-mode);
   }
 }
 </style>

+ 7 - 4
src/views/Tracking/src/components/TrackingTable/src/components/DownloadDialog.vue

@@ -34,7 +34,7 @@ defineExpose({
 
 <template>
   <div>
-    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="480">
+    <el-dialog @close="clearData" v-model="dialogVisible" title="Download File" width="540">
       <div class="download-dialog">
         <div class="select-data">
           <div style="display: inline-block">
@@ -95,12 +95,14 @@ defineExpose({
   display: flex;
   flex-wrap: wrap;
   gap: 8px;
+  max-height: 120px;
   margin-top: 8px;
+  overflow: auto;
 
   .filter-item {
     height: 22px;
     padding: 0px 8px;
-    background-color: #eceef0;
+    background-color: var(--color-download-file-filter-tag-bg);
     border-radius: 12px;
     line-height: 22px;
     font-size: 12px;
@@ -144,11 +146,12 @@ defineExpose({
     }
 
     .select-columns {
+      max-height: 350px;
       padding: 8px;
       margin-top: 8px;
       background-color: var(--color-header-bg);
       border-radius: 6px;
-      overflow: hidden;
+      overflow: auto;
 
       &.show {
         max-height: 500px;
@@ -168,7 +171,7 @@ defineExpose({
         .column-item {
           height: 22px;
           padding: 0px 8px;
-          background-color: #eceef0;
+          background-color: var(--color-download-file-selected-column-tag-bg);
           line-height: 22px;
           border-radius: 12px;
           font-size: 12px;