Преглед на файлове

Merge branch 'dev_zyh' of United_Software/k_online_ui into dev

Jack Zhou преди 5 месеца
родител
ревизия
46c27616d9
променени са 75 файла, в които са добавени 1708 реда и са изтрити 401 реда
  1. 1 0
      package.json
  2. BIN
      src/assets/image/done-btn.png
  3. BIN
      src/assets/image/next-btn.png
  4. BIN
      src/assets/image/pervious-btn.png
  5. BIN
      src/assets/image/start-btn.png
  6. 2 1
      src/auto-imports.d.ts
  7. 37 2
      src/components/MoreFilters/src/MoreFilters.vue
  8. BIN
      src/components/MoreFilters/src/image/booking-dark-more-filters-guide.png
  9. BIN
      src/components/MoreFilters/src/image/booking-more-filters-guide.png
  10. BIN
      src/components/MoreFilters/src/image/tracking-dark-more-filters-guide.png
  11. BIN
      src/components/MoreFilters/src/image/tracking-more-filters-guide.png
  12. 12 4
      src/components/VBox_Dashboard/src/VBox_Dashboard.vue
  13. 26 14
      src/components/VDriverGuide/src/VDriverGuide.vue
  14. BIN
      src/components/VDriverGuide/src/img/guide-icon.png
  15. 37 7
      src/components/VTipTooltip/src/VTipTooltip.vue
  16. 226 0
      src/stores/modules/guide.ts
  17. 30 43
      src/styles/driver.scss
  18. 6 0
      src/styles/index.scss
  19. 11 6
      src/styles/reset.scss
  20. 6 0
      src/styles/theme.scss
  21. 46 24
      src/utils/driverGuide.ts
  22. 5 11
      src/views/AIApiLog/src/components/LogDialog.vue
  23. 114 50
      src/views/Booking/src/BookingView.vue
  24. 245 0
      src/views/Booking/src/components/BookingGuide.vue
  25. 18 4
      src/views/Booking/src/components/BookingTable/src/BookingTable.vue
  26. BIN
      src/views/Booking/src/image/customize-columns.png
  27. BIN
      src/views/Booking/src/image/dark-customize-columns.png
  28. 231 22
      src/views/Dashboard/src/DashboardView.vue
  29. 36 4
      src/views/Dashboard/src/components/DashFiters.vue
  30. 156 0
      src/views/Dashboard/src/components/DashboardGuide.vue
  31. BIN
      src/views/Dashboard/src/guideImage/co2e-chart-tip.png
  32. BIN
      src/views/Dashboard/src/guideImage/container-count-chart-tip.png
  33. BIN
      src/views/Dashboard/src/guideImage/dark-kpi-chart-guide.png
  34. BIN
      src/views/Dashboard/src/guideImage/dark-save-config-guide.png
  35. BIN
      src/views/Dashboard/src/guideImage/dark-transport-mode.png
  36. BIN
      src/views/Dashboard/src/guideImage/dark-view-management.png
  37. BIN
      src/views/Dashboard/src/guideImage/etd-to-eta-chart-tip.png
  38. BIN
      src/views/Dashboard/src/guideImage/kpi-chart-guide.png
  39. 0 0
      src/views/Dashboard/src/guideImage/kpi-chart-tip.png
  40. BIN
      src/views/Dashboard/src/guideImage/pending-chart-tip.png
  41. BIN
      src/views/Dashboard/src/guideImage/recent-status-chart-tip.png
  42. BIN
      src/views/Dashboard/src/guideImage/revenue-spent-chart-tip.png
  43. BIN
      src/views/Dashboard/src/guideImage/save-config-guide.png
  44. 7 0
      src/views/Dashboard/src/guideImage/test.vue
  45. BIN
      src/views/Dashboard/src/guideImage/top-10-chart-tip.png
  46. BIN
      src/views/Dashboard/src/guideImage/transport-mode.png
  47. BIN
      src/views/Dashboard/src/guideImage/view-management.png
  48. BIN
      src/views/Dashboard/src/tipsImage/co2e-chart-tip.png
  49. BIN
      src/views/Dashboard/src/tipsImage/container-count-chart-tip.png
  50. BIN
      src/views/Dashboard/src/tipsImage/dark-co2e-chart-tip.png
  51. BIN
      src/views/Dashboard/src/tipsImage/dark-container-count-chart-tip.png
  52. BIN
      src/views/Dashboard/src/tipsImage/dark-etd-to-eta-chart-tip.png
  53. BIN
      src/views/Dashboard/src/tipsImage/dark-kpi-chart-tip.png
  54. BIN
      src/views/Dashboard/src/tipsImage/dark-pending-chart-tip.png
  55. BIN
      src/views/Dashboard/src/tipsImage/dark-recent-status-chart-tip.png
  56. BIN
      src/views/Dashboard/src/tipsImage/dark-revenue-spent-chart-tip.png
  57. BIN
      src/views/Dashboard/src/tipsImage/dark-top-10-chart-tip.png
  58. BIN
      src/views/Dashboard/src/tipsImage/etd-to-eta-chart-tip.png
  59. BIN
      src/views/Dashboard/src/tipsImage/kpi-chart-tip.png
  60. BIN
      src/views/Dashboard/src/tipsImage/pending-chart-tip.png
  61. BIN
      src/views/Dashboard/src/tipsImage/recent-status-chart-tip.png
  62. BIN
      src/views/Dashboard/src/tipsImage/revenue-spent-chart-tip.png
  63. BIN
      src/views/Dashboard/src/tipsImage/top-10-chart-tip.png
  64. 13 3
      src/views/Layout/src/components/Header/HeaderView.vue
  65. 83 58
      src/views/Tracking/src/TrackingView.vue
  66. 84 0
      src/views/Tracking/src/components/MultiHighlightGuide.vue
  67. 4 144
      src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue
  68. 254 0
      src/views/Tracking/src/components/TrackingGuide.vue
  69. 18 4
      src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue
  70. BIN
      src/views/Tracking/src/image/customize-columns.png
  71. BIN
      src/views/Tracking/src/image/dark-customize-columns.png
  72. BIN
      src/views/Tracking/src/image/dark-download-guide.png
  73. BIN
      src/views/Tracking/src/image/dark-more-filters-guide.png
  74. BIN
      src/views/Tracking/src/image/download-guide.png
  75. BIN
      src/views/Tracking/src/image/more-filters-guide.png

+ 1 - 0
package.json

@@ -43,6 +43,7 @@
     "sass-loader": "^16.0.2",
     "vue": "^3.4.29",
     "vue-draggable-plus": "^0.5.3",
+    "vue-json-pretty": "^2.4.0",
     "vue-router": "^4.3.3",
     "vue3-puzzle-vcode": "^1.1.7",
     "vue3-virtual-scroller": "^0.2.3",

BIN
src/assets/image/done-btn.png


BIN
src/assets/image/next-btn.png


BIN
src/assets/image/pervious-btn.png


BIN
src/assets/image/start-btn.png


+ 2 - 1
src/auto-imports.d.ts

@@ -3,6 +3,7 @@
 // @ts-nocheck
 // noinspection JSUnusedGlobalSymbols
 // Generated by unplugin-auto-import
+// biome-ignore lint: disable
 export {}
 declare global {
   const $api: typeof import('@/api/index')['default']
@@ -68,6 +69,6 @@ declare global {
 // for type re-export
 declare global {
   // @ts-ignore
-  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
+  export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
   import('vue')
 }

+ 37 - 2
src/components/MoreFilters/src/MoreFilters.vue

@@ -527,7 +527,9 @@ const drawer = ref(false)
 
 const props = defineProps({
   isShipment: Boolean,
-  searchTableQeury: Object
+  searchTableQeury: Object,
+  isShowMoreFiltersGuidePhoto: Boolean,
+  pageMode: String
 })
 const PartyTypeoptions = computed(() => {
   if (props.isShipment) {
@@ -1007,9 +1009,34 @@ watch(
     searchTableQeurytest.value = current
   }
 )
+import trackingMoreFiltersImgLight from './image/tracking-more-filters-guide.png'
+import trackingMoreFiltersImgDark from './image/tracking-dark-more-filters-guide.png'
+import bookingMoreFiltersImgLight from './image/booking-more-filters-guide.png'
+import bookingMoreFiltersImgDark from './image/booking-dark-more-filters-guide.png'
+import { useThemeStore } from '@/stores/modules/theme'
+
+const themeStore = useThemeStore()
+
+const moreFiltersGuideImg = computed(() => {
+  console.log('props.pageMode', props.pageMode)
+  if (props.pageMode === 'tracking') {
+    return themeStore.theme === 'dark' ? trackingMoreFiltersImgDark : trackingMoreFiltersImgLight
+  } else {
+    return themeStore.theme === 'dark' ? bookingMoreFiltersImgDark : bookingMoreFiltersImgLight
+  }
+})
 </script>
 <template>
-  <div>
+  <div style="position: relative">
+    <div style="width: 0; height: 0">
+      <img
+        id="more-filters-guide"
+        v-show="props.isShowMoreFiltersGuidePhoto"
+        class="more-filters-guide-class position-absolute-guide"
+        :src="moreFiltersGuideImg"
+        alt=""
+      />
+    </div>
     <el-button class="More_Filters el-button--grey" @click="clickmorefilters">
       <span class="iconfont_icon icon_more">
         <svg class="iconfont" aria-hidden="true">
@@ -1211,6 +1238,13 @@ watch(
 </template>
 
 <style lang="scss" scoped>
+img.more-filters-guide-class {
+  right: -1px;
+  top: -1px;
+  width: 327px;
+  z-index: 20000;
+}
+
 .icon_more {
   margin-left: 8px;
   margin-right: 0;
@@ -1242,6 +1276,7 @@ watch(
 
 .Filters_title {
   margin: 0 8px;
+  margin-left: 7px;
 }
 
 :deep(.el-drawer__header) {

BIN
src/components/MoreFilters/src/image/booking-dark-more-filters-guide.png


BIN
src/components/MoreFilters/src/image/booking-more-filters-guide.png


BIN
src/components/MoreFilters/src/image/tracking-dark-more-filters-guide.png


BIN
src/components/MoreFilters/src/image/tracking-more-filters-guide.png


+ 12 - 4
src/components/VBox_Dashboard/src/VBox_Dashboard.vue

@@ -3,6 +3,7 @@ import { ref } from 'vue'
 const props = withDefaults(
   defineProps<{
     id?: number
+    isShowDragIconGudie?: boolean
   }>(),
   {}
 )
@@ -44,13 +45,20 @@ const vBoxPopoverRef = ref()
   <div class="v-box">
     <div class="header">
       <slot name="header">Title</slot>
-      <div class="option">
-        <el-button type="text" class="sort handle-draggable">
-          <span class="iconfont_icon">
+      <div class="option" style="width: 48px; height: 48px">
+        <el-button
+          type="text"
+          class="sort handle-draggable"
+          :id="isShowDragIconGudie ? 'drag-icon-guide' : ''"
+        >
+          <!-- <span class="iconfont_icon">
             <svg class="iconfont" aria-hidden="true">
               <use xlink:href="#icon-icon_dragsort__b"></use>
             </svg>
-          </span>
+          </span> -->
+
+          <!--  -->
+          <span class="font_family icon-icon_dragsort__b"></span>
         </el-button>
       </div>
       <div class="cancel" @click="changeCancel">

+ 26 - 14
src/components/VDriverGuide/src/VDriverGuide.vue

@@ -1,20 +1,32 @@
 <script setup lang="ts"></script>
 
 <template>
-  <el-button style="height: 24px; margin-left: 8px" class="driver-guide-btn el-button--text">
-    <span class="font_family icon-icon_guidelines_b" style="color: var(--color-theme)"></span>
-    <span
-      style="
-        display: inline-block;
-        margin-top: 2px;
-        margin-left: 2px;
-        font-size: 12px;
-        color: var(--color-theme);
-      "
-    >
-      Guidelines
+  <div id="page-guide-btn-guide" class="driver-guide-btn">
+    <img src="./img/guide-icon.png" alt="" />
+    <span style="margin-top: 2px; font-size: 12px; font-style: italic; color: var(--color-theme)">
+      Page Guide
     </span>
-  </el-button>
+  </div>
 </template>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.driver-guide-btn {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  height: 24px;
+  padding: 0 7px;
+  margin-left: 8px;
+  border-radius: 24px;
+  background: var(--color-guide-icon-bg);
+  cursor: pointer;
+  &:hover {
+    background: var(--color-guide-icon-hover-bg);
+  }
+  img {
+    width: 16px;
+    height: 16px;
+    margin-right: 2px;
+  }
+}
+</style>

BIN
src/components/VDriverGuide/src/img/guide-icon.png


+ 37 - 7
src/components/VTipTooltip/src/VTipTooltip.vue

@@ -1,12 +1,12 @@
 <script setup lang="ts">
 const props = withDefaults(
   defineProps<{
-    content?: string
-    placement?: 'top' | 'bottom' | 'left' | 'right'
+    label?: string
+    placement?: string
     img?: string
+    width?: number
   }>(),
   {
-    content: '',
     placement: 'bottom'
   }
 )
@@ -16,14 +16,44 @@ const visible = ref(false)
 
 <template>
   <div style="display: inline-block">
-    <el-tooltip v-model="visible" :placement="props.placement">
+    <el-tooltip
+      effect="dark"
+      popper-class="v-tip-tooltip"
+      v-model="visible"
+      :placement="props.placement"
+      trigger="hover"
+    >
       <span class="font_family icon-icon_info_b"></span>
       <template #content>
-        <div class="label">KPI Report:Day difference between actual and estimate.</div>
-        <img :src="props.img" alt="" />
+        <div class="label">{{ props.label }}</div>
+        <div style="text-align: center">
+          <img :style="{ width: props.width + 'px' }" class="photo" :src="props.img" alt="" />
+        </div>
       </template>
     </el-tooltip>
   </div>
 </template>
 
-<style lang="scss" scoped></style>
+<style lang="scss">
+div.v-tip-tooltip {
+  padding: 16px;
+  border-radius: 12px;
+  // background-color: var(--color-tour-next-btn-color) !important;
+  border: 0 !important;
+  box-shadow: 4px 4px 16px rgba(0, 0, 0, 0.1) !important;
+  // .el-popper__arrow:before {
+  //   border: 2 !important;
+  //   background-color: var(--color-tour-next-btn-color) !important;
+  //   box-shadow: 4px 4px 16px rgba(0, 0, 0, 0.1) !important;
+  // }
+  .label {
+    color: #f0f1f3;
+    font-size: 14px;
+  }
+  .photo {
+    width: 368px;
+    margin-top: 8px;
+    border-radius: 6px;
+  }
+}
+</style>

+ 226 - 0
src/stores/modules/guide.ts

@@ -0,0 +1,226 @@
+// store/guide.ts
+import { defineStore } from 'pinia'
+import { useDriver } from '@/utils/driverGuide'
+
+const oceanSteps: any = [
+  {
+    element: '#driver-step-tracking-detail-1',
+    popover: {
+      title: '',
+      description: 'Main operation button area',
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#driver-step-tracking-detail-2',
+    popover: {
+      description: 'Key shipment status',
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#driver-step-tracking-detail-3',
+    popover: {
+      description: 'Detail container status of each container',
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#tracking-map',
+    popover: {
+      description: `
+        <ul>
+          <li>Actual Line(Actual Trajectory)</li>
+          <li>Virtual Line(Planned Trajectory)</li>
+          <li>Arrow (Current Real-time Position)</li>
+        </ol>
+      `,
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#driver-step-tracking-detail-5',
+    popover: {
+      description: `
+      <ul>
+        <li>Upload Files</li>
+        <li>Clickable download icons to download files</li>
+      </ol>
+      `,
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#driver-step-tracking-detail-6',
+    popover: {
+      description: `
+        <p>Send email to site staff</p>
+        <p>Enter contents you want to communicate with, click “Send 
+         Email” button to send out.
+        </p>
+      `,
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#page-guide-btn-guide',
+    popover: {
+      title: '',
+      description:
+        'After closing, you can still click the "page guide" button to view the page guide of the current page.',
+      side: 'bottom'
+    }
+  }
+]
+const airSteps: any = [
+  {
+    element: '#driver-step-tracking-detail-1',
+    popover: {
+      title: '',
+      description: 'Main operation button area',
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#driver-step-tracking-detail-2',
+    popover: {
+      description: 'Key shipment status',
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#tracking-map',
+    popover: {
+      description: `
+        <ul>
+          <li>Actual Line(Actual Trajectory)</li>
+          <li>Virtual Line(Planned Trajectory)</li>
+          <li>Arrow (Current Real-time Position)</li>
+        </ol>
+      `,
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#driver-step-tracking-detail-5',
+    popover: {
+      description: `
+      <ul>
+        <li>Upload Files</li>
+        <li>Clickable download icons to download files</li>
+      </ol>
+      `,
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#driver-step-tracking-detail-6',
+    popover: {
+      description: `
+        <p>Send email to site staff</p>
+        <p>Enter contents you want to communicate with, click “Send 
+         Email” button to send out.
+        </p>
+      `,
+      side: 'bottom',
+      align: 'start'
+    }
+  },
+  {
+    element: '#page-guide-btn-guide',
+    popover: {
+      title: '',
+      description:
+        'After closing, you can still click the "page guide" button to view the page guide of the current page.',
+      side: 'bottom'
+    }
+  }
+]
+
+const guideTimer = ref<ReturnType<typeof setTimeout> | null>(null)
+export const useGuideStore = defineStore('guide', {
+  state: () => ({
+    booking: {
+      isShowMoreFiltersGuidePhoto: false,
+      isShowDownloadFileGuidePhoto: false,
+      isShowFilterGuidePhoto: false,
+      isShowCustomizeColumnsGuidePhoto: false
+    },
+    tracking: {
+      isShowFilterGuidePhoto: false,
+      isShowMoreFiltersGuidePhoto: false,
+      isShowDownloadFileGuidePhoto: false,
+      isShowCustomizeColumnsGuidePhoto: false
+    },
+    dashboard: {
+      isShowViewManagementGuidePhoto: false,
+      isShowTransportModeGuidePhoto: false,
+      isShowSaveConfigGuidePhoto: false,
+      isShowKpiChartGuidePhoto: false
+    },
+    trackingDetail: {
+      mode: 'Ocean Freight'
+    }
+  }),
+  actions: {
+    resetGuide(key: string) {
+      if (this.booking[key] !== undefined) {
+        this.booking[key] = false
+      } else if (this.tracking[key] !== undefined) {
+        this.tracking[key] = false
+      } else if (this.dashboard[key] !== undefined) {
+        this.dashboard[key] = false
+      }
+    },
+    handleTrackingDetailGuide() {
+      let steps = []
+      if (this.trackingDetail.mode === 'Ocean Freight') {
+        steps = oceanSteps
+      } else if (this.trackingDetail.mode === 'Air Freight') {
+        steps = airSteps
+      } else {
+        return
+      }
+      const { start, movePrevious, hasNextStep, moveTo, destroy } = useDriver(steps, {
+        onPrevClick: () => {
+          if (guideTimer.value) {
+            clearTimeout(guideTimer.value)
+            guideTimer.value = null
+          }
+          movePrevious()
+        },
+        onHighlightStarted: () => {
+          if (!hasNextStep()) {
+            guideTimer.value = setTimeout(() => {
+              destroy()
+            }, 3000)
+          }
+        },
+        onDestroyStarted: (element, step, options) => {
+          if (hasNextStep()) {
+            moveTo(options.config.steps.length - 1)
+            return
+          }
+          destroy()
+        },
+        onDestroyed: () => {
+          if (guideTimer.value) {
+            clearTimeout(guideTimer.value)
+            guideTimer.value = null
+          }
+        }
+      })
+      start() // 开始引导
+    }
+  }
+})

+ 30 - 43
src/styles/driver.scss

@@ -3,7 +3,9 @@ div.driver-popover {
   max-width: 400px;
   padding: 15px;
   padding-bottom: 8px;
-  background-color: var(--color-tour-popover-bg);
+  background-image: linear-gradient(96deg, #b58eff 2.25%, #fdbc94 97.98%);
+  box-shadow: 4px 4px 16px rgba(0, 0, 0, 0.1);
+  border-radius: 12px;
 }
 // 标题
 header.driver-popover-title {
@@ -12,16 +14,16 @@ header.driver-popover-title {
 
 // 角标
 div.driver-popover-arrow-side-left {
-  border-left-color: var(--color-tour-popover-bg);
+  border-left-color: #b78ffc;
 }
 div.driver-popover-arrow-side-right {
-  border-right-color: var(--color-tour-popover-bg);
+  border-right-color: #b78ffc;
 }
 div.driver-popover-arrow-side-top {
-  border-top-color: var(--color-tour-popover-bg);
+  border-top-color: #b78ffc;
 }
 div.driver-popover-arrow-side-bottom {
-  border-bottom-color: var(--color-tour-popover-bg);
+  border-bottom-color: #b78ffc;
 }
 
 // 遮罩
@@ -32,65 +34,50 @@ div.driver-popover-arrow-side-bottom {
 }
 // 关闭图标
 button.driver-popover-close-btn {
-  top: 9px;
-  right: 9px;
-  color: var(--color-neutral-1);
+  top: -1px;
+  right: -6px;
+  color: #2b2f36;
   &:hover,
   &:focus {
-    color: var(--color-neutral-1);
+    color: #2b2f36;
   }
 }
 // 内容
 div.driver-popover-description {
-  color: var(--color-neutral-1);
+  color: #2b2f36;
   text-align: left;
+  ul {
+    margin-left: 16px;
+    list-style: disc;
+    li {
+      color: #2b2f36;
+    }
+  }
 }
 
 // 页数
 span.driver-popover-progress-text {
-  color: var(--color-tour-step-color);
+  color: rgba($color: #fff, $alpha: 0.7);
   font-size: 12px;
 }
-.driver-popover-footer {
+footer.driver-popover-footer {
+  margin-top: 28px;
+  height: 36px;
   .driver-popover-navigation-btns {
     // 上一步
     button.driver-popover-prev-btn {
       width: 96px;
-      height: 32px;
-      background-color: var(--color-btn-default-bg-color);
-      color: var(--color-neutral-1);
-      fill: var(--color-neutral-1);
-      text-shadow: none;
-      border: 1px solid var(--color-tour-prev-btn-border);
-      margin-left: 8px !important;
-      border-radius: 6px;
-      text-align: center;
-      &:hover {
-        // border: 1px solid var(--color-btn-default-bg-hover);
-        background-color: var(--color-btn-default-bg-hover);
-        fill: var(--color-theme);
-        span {
-          color: var(--color-theme);
-        }
-      }
+      height: 36px;
+      padding: 0;
+      background-color: transparent;
+      border: none;
     }
     button.driver-popover-next-btn {
       width: 96px;
-      height: 32px;
-      background-color: var(--color-tour-next-btn-bg);
-      fill: var(--color-tour-next-btn-color);
+      height: 36px;
+      padding: 0;
+      background-color: transparent;
       border: none;
-      text-shadow: none;
-      color: var(--color-tour-next-btn-color);
-      border-radius: 6px;
-      text-align: center;
-      &:hover {
-        background-color: var(--color-tour-next-btn-hover-bg);
-        fill: var(--color-tour-next-btn-bg);
-        span {
-          color: var(--color-tour-next-btn-color) !important;
-        }
-      }
     }
   }
 }

+ 6 - 0
src/styles/index.scss

@@ -57,3 +57,9 @@
 .icon_dark {
   fill: var(--color-neutral-1);
 }
+
+.position-absolute-guide {
+  position: absolute;
+  z-index: 1200;
+  box-shadow: 3px 3px 16px 0px rgba(0, 0, 0, 0.2);
+}

+ 11 - 6
src/styles/reset.scss

@@ -158,15 +158,20 @@ div.markdown-body {
   color: #333;
 }
 
-.markdown-body h1,
-.markdown-body h2,
-.markdown-body h3,
-.markdown-body h4,
-.markdown-body h5,
-.markdown-body h6 {
+div.markdown-body h1,
+div.markdown-body h2,
+div.markdown-body h3,
+div.markdown-body h4,
+div.markdown-body h5,
+div.markdown-body h6 {
   margin: 1em 0 0.5em;
+  font-size: 14px;
   font-weight: bold;
 }
+
+div.markdown-body h1 {
+  font-size: 16px;
+}
 .markdown-body p {
   margin: 1em 0;
 }

+ 6 - 0
src/styles/theme.scss

@@ -324,6 +324,9 @@
   --color-tour-next-btn-color: #fff;
   --color-tour-step-color: #b5b9bf;
   --color-tour-mask-bg: rgba(43, 47, 54, 0.7);
+
+  --color-guide-icon-bg: rgba(237, 237, 237, 0.6);
+  --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.45);
 }
 
 :root.dark {
@@ -526,4 +529,7 @@
   --color-tour-next-btn-color: #ed6d00;
   --color-tour-step-color: rgba(240, 241, 243, 0.7);
   --color-tour-mask-bg: rgba(0, 0, 0, 0.7);
+
+  --color-guide-icon-bg: rgba(237, 237, 237, 0.1);
+  --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.15);
 }

+ 46 - 24
src/utils/driverGuide.ts

@@ -1,8 +1,8 @@
-// src/composables/useDriver.ts
 import { driver, type DriveStep, type Config } from 'driver.js'
 import 'driver.js/dist/driver.css'
-
-let driverInstance: ReturnType<typeof driver> | null = null
+import nextBtnImg from '@/assets/image/next-btn.png'
+import previousBtnImg from '@/assets/image/pervious-btn.png'
+import doneBtnImg from '@/assets/image/done-btn.png'
 
 /**
  * useDriver composable
@@ -10,31 +10,53 @@ let driverInstance: ReturnType<typeof driver> | null = null
  * @param globalConfig 可选的 driver 配置
  */
 export function useDriver(steps: DriveStep[], globalConfig?: Config) {
-  if (!driverInstance) {
-    driverInstance = driver({
-      ...globalConfig,
-      animate: false,
-      showProgress: true, // 显示进度条
-      overlayOpacity: 0.7, // 遮罩透明度
-      overlayColor: '#2b2f36', // 遮罩颜色
-      // allowClose: false, // 禁止用户关闭引导
-      showButtons: ['previous', 'next', 'close'],
-      onPopoverRender: (popoverDOM, { config, state, driver }) => {
-        // 如果用户配置了自己的 onPopoverRender,也执行它
-        globalConfig?.onPopoverRender?.(popoverDOM, { config, state, driver })
+  const driverInstance = driver({
+    ...globalConfig,
+    animate: false,
+    showProgress: true,
+    overlayOpacity: 0.7,
+    overlayColor: '#2b2f36',
+    showButtons: ['previous', 'next', 'close'],
+    onPopoverRender: (popoverDOM, { config, state, driver }) => {
+      globalConfig?.onPopoverRender?.(popoverDOM, { config, state, driver })
+
+      const stepsLength = steps.length
+      const nextBtn = popoverDOM.nextButton
+      if (nextBtn) {
+        nextBtn.innerHTML = '' // 清空原内容
+        const img = document.createElement('img')
+        img.src = stepsLength - 1 === state.activeIndex ? doneBtnImg : nextBtnImg // 替换成你的图片路径
+        img.alt = 'next'
+        img.style.width = '96px'
+        img.style.height = '36px'
+        nextBtn.appendChild(img)
       }
-    })
-  }
+
+      const previousBtn = popoverDOM.previousButton
+      if (previousBtn) {
+        previousBtn.innerHTML = '' // 清空原内容
+        const img = document.createElement('img')
+        img.src = previousBtnImg // 替换成你的图片路径
+        img.alt = 'previous'
+        img.style.width = '96px'
+        img.style.height = '36px'
+        previousBtn.appendChild(img)
+        img.style.display = state.activeIndex > 0 ? 'block' : 'none' // 如果是第一个步骤,则隐藏上一页按钮
+      }
+    }
+  })
 
   driverInstance.setSteps(steps)
 
   return {
-    start: (stepIndex = 0) => driverInstance?.drive(stepIndex),
-    moveNext: () => driverInstance?.moveNext(),
-    movePrevious: () => driverInstance?.movePrevious(),
-    highlight: (step: DriveStep) => driverInstance?.highlight(step),
-    destroy: () => driverInstance?.destroy(),
-    isActive: () => driverInstance?.isActive(),
-    driverInstance // 暴露实例给你更多操作
+    start: (stepIndex = 0) => driverInstance.drive(stepIndex),
+    moveNext: () => driverInstance.moveNext(),
+    movePrevious: () => driverInstance.movePrevious(),
+    highlight: (step: DriveStep) => driverInstance.highlight(step),
+    destroy: () => driverInstance.destroy(),
+    isActive: () => driverInstance.isActive(),
+    hasNextStep: () => driverInstance.hasNextStep(),
+    moveTo: (stepIndex: number) => driverInstance.moveTo(stepIndex),
+    driverInstance
   }
 }

+ 5 - 11
src/views/AIApiLog/src/components/LogDialog.vue

@@ -1,17 +1,17 @@
 <script setup lang="ts">
+import VueJsonPretty from 'vue-json-pretty'
+import 'vue-json-pretty/lib/styles.css'
+
 const dialogVisible = ref(false)
 
 const requestContent = ref()
-// const requestList = ref([])
 const responseContent = ref()
-// const responseList = ref()
 const requestContentRef = ref<HTMLElement | null>(null)
 const responseHeight = ref(580)
 const openDialog = (request, response) => {
   dialogVisible.value = true
   requestContent.value = request
-  responseContent.value =
-    response.choices?.[0]?.message?.content || response.content?.[0]?.text || response
+  responseContent.value = response
   nextTick(() => {
     if (requestContentRef.value) {
       const height = requestContentRef.value.scrollHeight
@@ -43,19 +43,13 @@ defineExpose({
       <div class="title">Request Content</div>
       <div class="content" ref="requestContentRef">
         {{ requestContent }}
-        <!-- <p v-for="(item, index) in requestList" :key="index">
-          {{ item }}
-        </p> -->
       </div>
     </div>
     <el-divider style="margin: 16px 0" />
     <div class="response-section">
       <div class="title">Response Content</div>
       <div class="content" :style="{ height: responseHeight + 'px' }">
-        <!-- <p v-for="(item, index) in responseList" :key="index">
-          {{ item }}
-        </p> -->
-        {{ responseContent }}
+        <vue-json-pretty :data="responseContent" :deep="4" />
       </div>
     </div>
   </el-dialog>

+ 114 - 50
src/views/Booking/src/BookingView.vue

@@ -388,71 +388,90 @@ const SearchInput = () => {
   sessionStorage.setItem('searchTableQeury', JSON.stringify(searchTableQeury))
   getbookingdata()
 }
+
+import BookingGuide from './components/BookingGuide.vue'
+import { useGuideStore } from '@/stores/modules/guide'
+
+const guideStore = useGuideStore()
+const bookingGuideRef = ref()
+const handleGuide = () => {
+  bookingGuideRef.value.startGuide() // 开始引导
+}
 </script>
 
 <template>
-  <div class="Title">Booking</div>
+  <div class="Title">
+    <span>Booking</span>
+    <VDriverGuide @click="handleGuide"></VDriverGuide>
+  </div>
+  <BookingGuide ref="bookingGuideRef"></BookingGuide>
   <div class="display" ref="filterRef">
-    <FilterTags :TagsListItem="TagsList" @changeTag="changeTag"></FilterTags>
-    <div class="heaer_top">
-      <div class="search">
-        <el-input
-          placeholder="Enter Booking/HBL/PO/Carrier Booking No. "
-          v-model="BookingSearch"
-          class="log_input"
-          @keyup.enter="SearchInput"
-        >
-          <template #prefix>
-            <span class="iconfont_icon">
-              <svg class="iconfont icon_search" aria-hidden="true">
-                <use xlink:href="#icon-icon_search_b"></use>
-              </svg>
-            </span>
-          </template>
-          <template #suffix>
-            <el-tooltip
-              v-if="isShowAlertIcon"
-              :offset="6"
-              popper-class="ShowAlerIcon"
-              effect="dark"
-              content="We support the following references number to find bookings:· Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote No."
-              placement="bottom"
+    <div class="filter-box">
+      <div class="filters-container" id="booking-filters-container-guide">
+        <FilterTags :TagsListItem="TagsList" @changeTag="changeTag"></FilterTags>
+        <div class="heaer_top">
+          <div class="search">
+            <el-input
+              placeholder="Enter Booking/HBL/PO/Carrier Booking No. "
+              v-model="BookingSearch"
+              class="log_input"
+              @keyup.enter="SearchInput"
             >
-              <span class="iconfont_icon iconfont_icon_tip">
-                <svg class="iconfont icon_search" aria-hidden="true">
-                  <use xlink:href="#icon-icon_info_b"></use>
-                </svg>
-              </span>
-            </el-tooltip>
-          </template>
-        </el-input>
+              <template #prefix>
+                <span class="iconfont_icon">
+                  <svg class="iconfont icon_search" aria-hidden="true">
+                    <use xlink:href="#icon-icon_search_b"></use>
+                  </svg>
+                </span>
+              </template>
+              <template #suffix>
+                <el-tooltip
+                  v-if="isShowAlertIcon"
+                  :offset="6"
+                  popper-class="ShowAlerIcon"
+                  effect="dark"
+                  content="We support the following references number to find bookings:· Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote No."
+                  placement="bottom"
+                >
+                  <span class="iconfont_icon iconfont_icon_tip">
+                    <svg class="iconfont icon_search" aria-hidden="true">
+                      <use xlink:href="#icon-icon_info_b"></use>
+                    </svg>
+                  </span>
+                </el-tooltip>
+              </template>
+            </el-input>
+          </div>
+          <TransportMode
+            :isShipment="false"
+            :TransportListItem="TransportListItem"
+            @TransportSearch="TransportSearch"
+            @defaultTransport="defaultTransport"
+            @clearTransportTags="clearTransportTags"
+          ></TransportMode>
+          <DateRange
+            :isShipment="false"
+            @DateRangeSearch="DateRangeSearch"
+            @clearDaterangeTags="clearDaterangeTags"
+            @defaultDate="defaultDate"
+          ></DateRange>
+        </div>
       </div>
-      <TransportMode
-        :isShipment="false"
-        :TransportListItem="TransportListItem"
-        @TransportSearch="TransportSearch"
-        @defaultTransport="defaultTransport"
-        @clearTransportTags="clearTransportTags"
-      ></TransportMode>
-      <DateRange
-        :isShipment="false"
-        @DateRangeSearch="DateRangeSearch"
-        @clearDaterangeTags="clearDaterangeTags"
-        @defaultDate="defaultDate"
-      ></DateRange>
       <MoreFilters
         :isShipment="false"
+        :pageMode="'booking'"
         :searchTableQeury="searchTableQeury"
         @MoreFiltersSearch="MoreFiltersSearch"
         @clearMoreFiltersTags="clearMoreFiltersTags"
         @defaultMorefilters="defaultMorefilters"
+        :isShowMoreFiltersGuidePhoto="guideStore.booking.isShowMoreFiltersGuidePhoto"
       ></MoreFilters>
       <el-button class="el-button--dark" style="margin-left: 8px" @click="SearchInput"
         >Search</el-button
       >
     </div>
     <!-- 筛选项 -->
-    <div class="filtersTag" v-if="tagsData.length">
+    <div class="filtersTag" v-if="tagsData.length" id="booking-filter-tag-guide">
       <el-tag
         :key="tag"
         class="tag"
@@ -484,6 +503,51 @@ const SearchInput = () => {
 </template>
 
 <style lang="scss" scoped>
+.filter-box {
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+  align-items: flex-end;
+  height: 100%;
+}
+.filters-container {
+  max-width: 1426px;
+  width: 80%;
+  display: flex;
+  flex-direction: column;
+}
+.filter-guide-class {
+  top: -3px;
+  left: -2px;
+  height: 29px;
+  width: 592px;
+}
+img.more-filters-guide-class {
+  right: 38px;
+  top: 155px;
+  height: 634px;
+  width: 243px;
+  z-index: 20000;
+}
+.download-file-guide-class {
+  right: 85px;
+  top: 243px;
+  width: 377px;
+  height: 236px;
+}
+.customize-columns-guide-class {
+  right: 8px;
+  top: 249px;
+  width: 694px;
+  height: 474px;
+}
+.tab-filter-guide-class {
+  left: 248px;
+  top: 118px;
+  height: 42px;
+  z-index: 20000;
+}
+
 .Title {
   display: flex;
   height: 68px;
@@ -500,7 +564,6 @@ const SearchInput = () => {
 }
 .heaer_top {
   margin-top: 6.57px;
-  margin-bottom: 8px;
   display: flex;
 }
 .search {
@@ -508,8 +571,9 @@ const SearchInput = () => {
   height: 32px;
 }
 .filtersTag {
-  margin-bottom: 8.7px;
-  display: flex;
+  margin-top: 8px;
+  margin-bottom: 4px;
+  display: inline-flex;
   align-items: center;
   flex-wrap: wrap;
 }

+ 245 - 0
src/views/Booking/src/components/BookingGuide.vue

@@ -0,0 +1,245 @@
+<script setup lang="ts">
+import { useDriver } from '@/utils/driverGuide'
+import { useGuideStore } from '@/stores/modules/guide'
+import { useThemeStore } from '@/stores/modules/theme'
+
+import customizeColumnsImgLight from '../image/customize-columns.png'
+import customizeColumnsImgDark from '../image/dark-customize-columns.png'
+
+import downloadFileImgLight from '@/views/Tracking/src/image/download-guide.png'
+import downloadFileImgDark from '@/views/Tracking/src/image/dark-download-guide.png'
+
+const themeStore = useThemeStore()
+
+const customizeColumnsImg = computed(() => {
+  return themeStore.theme === 'dark' ? customizeColumnsImgDark : customizeColumnsImgLight
+})
+const downloadFileImg = computed(() => {
+  return themeStore.theme === 'dark' ? downloadFileImgDark : downloadFileImgLight
+})
+
+const guideStore = useGuideStore()
+const bookingGuideStore = guideStore.booking
+
+const steps: any = [
+  {
+    element: '#booking-filters-container-guide',
+    popover: {
+      title: '',
+      description: `Frequently Used Search Criteria Display Area:
+        <ul>
+          <li>Key Booking Status</li>
+          <li>Reference Numbers</li>
+          <li>Transport Mode</li>
+          <li>Date Type & Range</li>
+        </ul>`,
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#booking-filter-tag-guide',
+    popover: {
+      title: '',
+      description: `
+        <ul>
+          <li>Selected query criteria display area</li>
+          <li>You can quickly clear selected conditions at this position</li>
+        </ul>
+      `,
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#more-filters-guide',
+    popover: {
+      title: '',
+      description: 'Click "More Filters" to see more search options.',
+      side: 'left'
+    }
+  },
+  {
+    element: '.booking-no-header',
+    popover: {
+      title: '',
+      description:
+        'Click on the Booking No. or double-click anywhere on a single booking data to enter the detailed page.',
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#booking-download-file-guide',
+    popover: {
+      title: '',
+      description: `
+        <ul>
+          <li>View the number of shipments selected for download</li>
+          <li>Two download list templates are available</li>
+        </ul>
+      `,
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#booking-customize-columns-guide',
+    popover: {
+      title: '',
+      description: `
+        <ul>
+          <li>Drag to right to add columns</li>
+          <li>Drag to left to unselect columns</li>
+          <li>Drag up and down to reorder or select/unselect</li>
+        </ul>
+      `,
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#page-guide-btn-guide',
+    popover: {
+      title: '',
+      description:
+        'After closing, you can still click the "page guide" button to view the page guide of the current page.',
+      side: 'bottom'
+    }
+  }
+]
+
+const guideTimer = ref<ReturnType<typeof setTimeout> | null>(null)
+const { start, moveNext, movePrevious, destroy, hasNextStep, moveTo } = useDriver(steps, {
+  onNextClick: (element, step, options) => {
+    if (options?.state?.activeIndex === 1) {
+      bookingGuideStore.isShowMoreFiltersGuidePhoto = true
+    } else if (options?.state?.activeIndex === 2) {
+      bookingGuideStore.isShowMoreFiltersGuidePhoto = false
+    } else if (options?.state?.activeIndex === 3) {
+      bookingGuideStore.isShowDownloadFileGuidePhoto = true
+    } else if (options?.state?.activeIndex === 4) {
+      bookingGuideStore.isShowDownloadFileGuidePhoto = false
+
+      bookingGuideStore.isShowCustomizeColumnsGuidePhoto = true
+    } else if (options?.state?.activeIndex === 5) {
+      bookingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    }
+    nextTick(() => {
+      moveNext() // 执行下一步
+    })
+  },
+  onPrevClick: (element, step, options) => {
+    if (options?.state?.activeIndex === 2) {
+      bookingGuideStore.isShowMoreFiltersGuidePhoto = false
+    } else if (options?.state?.activeIndex === 3) {
+      bookingGuideStore.isShowMoreFiltersGuidePhoto = true
+    } else if (options?.state?.activeIndex === 4) {
+      bookingGuideStore.isShowDownloadFileGuidePhoto = false
+    } else if (options?.state?.activeIndex === 5) {
+      bookingGuideStore.isShowDownloadFileGuidePhoto = true
+
+      bookingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    } else if (options?.state?.activeIndex === 6) {
+      bookingGuideStore.isShowCustomizeColumnsGuidePhoto = true
+    }
+    if (guideTimer.value) {
+      clearTimeout(guideTimer.value)
+      guideTimer.value = null
+    }
+    nextTick(() => {
+      movePrevious() // 执行上一步
+    })
+  },
+  onHighlightStarted: () => {
+    if (!hasNextStep()) {
+      guideTimer.value = setTimeout(() => {
+        destroy()
+      }, 3000)
+    }
+  },
+  onDestroyStarted: (element, step, options) => {
+    bookingGuideStore.isShowFilterGuidePhoto = false
+    bookingGuideStore.isShowMoreFiltersGuidePhoto = false
+    bookingGuideStore.isShowDownloadFileGuidePhoto = false
+    bookingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    if (hasNextStep()) {
+      moveTo(options.config.steps.length - 1)
+      return
+    }
+    nextTick(() => {
+      destroy() // 销毁导览
+    })
+  },
+  onDestroyed: () => {
+    bookingGuideStore.isShowFilterGuidePhoto = false
+    bookingGuideStore.isShowMoreFiltersGuidePhoto = false
+    bookingGuideStore.isShowDownloadFileGuidePhoto = false
+    bookingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    if (guideTimer.value) {
+      clearTimeout(guideTimer.value)
+      guideTimer.value = null
+    }
+  }
+})
+const startGuide = () => {
+  bookingGuideStore.isShowFilterGuidePhoto = true // 设置状态
+  nextTick(() => {
+    start() // 开始引导
+  }) // 延时1秒开始引导,确保图片加载完成
+}
+
+defineExpose({
+  startGuide
+})
+</script>
+
+<template>
+  <div>
+    <!-- download-file-guide -->
+    <Teleport to="body">
+      <img
+        :class="{ 'download-file-guide-dark-class': themeStore.theme === 'dark' }"
+        id="booking-download-file-guide"
+        v-show="bookingGuideStore.isShowDownloadFileGuidePhoto"
+        class="position-absolute-guide download-file-guide-class"
+        :src="downloadFileImg"
+        alt=""
+      />
+    </Teleport>
+    <!-- customize-columns-guide  -->
+    <Teleport to="body">
+      <img
+        :class="{ 'customize-columns-guide-dark-class': themeStore.theme === 'dark' }"
+        id="booking-customize-columns-guide"
+        v-show="bookingGuideStore.isShowCustomizeColumnsGuidePhoto"
+        class="position-absolute-guide customize-columns-guide-class"
+        :src="customizeColumnsImg"
+        alt=""
+      />
+    </Teleport>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.position-absolute-guide {
+  position: absolute;
+  z-index: 1200;
+}
+
+.download-file-guide-class {
+  right: 203px;
+  top: 246px;
+  width: 419px;
+  height: 285px;
+  &.download-file-guide-dark-class {
+    transform: translate(0px, -0.4px);
+    width: 411px;
+    height: 287px;
+  }
+}
+.customize-columns-guide-class {
+  right: 25px;
+  top: 245px;
+  width: 647px;
+  height: 441px;
+  &.customize-columns-guide-dark-class {
+    right: 24px;
+  }
+}
+</style>

+ 18 - 4
src/views/Booking/src/components/BookingTable/src/BookingTable.vue

@@ -70,6 +70,12 @@ const handleColumns = (columns: any, status?: string) => {
         formatter: ({ cellValue }: any) => formatNumber(Number(cellValue), item?.digits)
       }
     }
+    if (item.title === 'Booking No.') {
+      curColumn = {
+        ...curColumn,
+        headerClassName: 'booking-no-header'
+      }
+    }
     return curColumn
   })
   return newColumns
@@ -473,12 +479,20 @@ defineExpose({
     <div class="table-tools">
       <div class="left-total-records">{{ selectedNumber }} Selected</div>
       <div class="right-tools-btn">
-        <el-button class="el-button--main el-button--pain-theme" @click="handleDownload">
-          <span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
+        <el-button
+          :class="{ 'el-button--pain-theme': themeStore.theme === 'dark' }"
+          class="el-button--main el-button--pain-theme"
+          @click="handleDownload"
+          :style="{
+            paddingRight: themeStore.theme === 'dark' ? '13px' : '16px',
+            paddingLeft: themeStore.theme === 'dark' ? '13px' : '11px'
+          }"
+        >
+          <span style="margin-right: 7px" class="font_family icon-icon_download_b"></span>
           Download
         </el-button>
-        <el-button type="default" @click="handleCustomizeColumns">
-          <span style="margin-right: 8px" class="font_family icon-icon_column_b"></span>
+        <el-button style="padding: 0 17px 0 9px" type="default" @click="handleCustomizeColumns">
+          <span style="margin-right: 6px" class="font_family icon-icon_column_b"></span>
           Customize Columns
         </el-button>
       </div>

BIN
src/views/Booking/src/image/customize-columns.png


BIN
src/views/Booking/src/image/dark-customize-columns.png


+ 231 - 22
src/views/Dashboard/src/DashboardView.vue

@@ -775,15 +775,94 @@ const ClickParams = (val: any) => {
     })
   }
 }
+import kpiChartTipLight from './tipsImage/kpi-chart-tip.png'
+import kpiChartTipDark from './tipsImage/dark-kpi-chart-tip.png'
+import pendingChartTipLight from './tipsImage/pending-chart-tip.png'
+import pendingChartTipDark from './tipsImage/dark-pending-chart-tip.png'
+import etdToEtaChartsTipLight from './tipsImage/etd-to-eta-chart-tip.png'
+import etdToEtaChartsTipDark from './tipsImage/dark-etd-to-eta-chart-tip.png'
+import containerChartTipLight from './tipsImage/container-count-chart-tip.png'
+import containerChartTipDark from './tipsImage/dark-container-count-chart-tip.png'
+import top10ChartTipLight from './tipsImage/top-10-chart-tip.png'
+import top10ChartTipDark from './tipsImage/dark-top-10-chart-tip.png'
+import co2eChartTipLight from './tipsImage/co2e-chart-tip.png'
+import co2eChartTipDark from './tipsImage/dark-co2e-chart-tip.png'
+import revenueSpentChartTipLight from './tipsImage/revenue-spent-chart-tip.png'
+import revenueSpentChartTipDark from './tipsImage/dark-revenue-spent-chart-tip.png'
+import recentStatusChartTipLight from './tipsImage/recent-status-chart-tip.png'
+import recentStatusChartTipDark from './tipsImage/dark-recent-status-chart-tip.png'
+
+const kpiChartTip = computed(() => {
+  return themeStore.theme === 'dark' ? kpiChartTipDark : kpiChartTipLight
+})
+const pendingChartTip = computed(() => {
+  return themeStore.theme === 'dark' ? pendingChartTipDark : pendingChartTipLight
+})
+const etdToEtaChartsTip = computed(() => {
+  return themeStore.theme === 'dark' ? etdToEtaChartsTipDark : etdToEtaChartsTipLight
+})
+const containerChartTip = computed(() => {
+  return themeStore.theme === 'dark' ? containerChartTipDark : containerChartTipLight
+})
+const top10ChartTip = computed(() => {
+  return themeStore.theme === 'dark' ? top10ChartTipDark : top10ChartTipLight
+})
+const co2eChartTip = computed(() => {
+  return themeStore.theme === 'dark' ? co2eChartTipDark : co2eChartTipLight
+})
+const revenueSpentChartTip = computed(() => {
+  return themeStore.theme === 'dark' ? revenueSpentChartTipDark : revenueSpentChartTipLight
+})
+const recentStatusChartTip = computed(() => {
+  return themeStore.theme === 'dark' ? recentStatusChartTipDark : recentStatusChartTipLight
+})
+
+import DashboardGuide from '../src/components/DashboardGuide.vue'
+import { useGuideStore } from '@/stores/modules/guide'
+import { useThemeStore } from '@/stores/modules/theme'
+
+import viewManagementLight from './guideImage/view-management.png'
+import viewManagementDark from './guideImage/dark-view-management.png'
+import saveConfigLight from './guideImage/save-config-guide.png'
+import saveConfigDark from './guideImage/dark-save-config-guide.png'
+import kpiChartLight from './guideImage/kpi-chart-guide.png'
+import kpiChartDark from './guideImage/dark-kpi-chart-guide.png'
+
+const themeStore = useThemeStore()
+
+const viewManagementImg = computed(() => {
+  return themeStore.theme === 'dark' ? viewManagementDark : viewManagementLight
+})
+const saveConfigImg = computed(() => {
+  return themeStore.theme === 'dark' ? saveConfigDark : saveConfigLight
+})
+const kpiChartImg = computed(() => {
+  return themeStore.theme === 'dark' ? kpiChartDark : kpiChartLight
+})
+const guideStore = useGuideStore()
+const handleGuide = () => {
+  dashboardGuideRef.value?.startGuide()
+}
+const dashboardGuideRef = ref(null)
+
+function handleImageClick(event) {
+  event.stopPropagation() // 阻止事件冒泡
+  alert('Image clicked')
+}
 </script>
 <template>
   <div class="dashboard">
     <!-- 评分 -->
     <ScoringSystem></ScoringSystem>
+    <DashboardGuide ref="dashboardGuideRef"></DashboardGuide>
     <!-- Title -->
     <div class="Title">
-      <div>Dashboard</div>
       <div>
+        <span>Dashboard</span>
+        <VDriverGuide style="margin-top: -1px" @click="handleGuide"></VDriverGuide>
+      </div>
+
+      <div style="position: relative">
         <el-popover trigger="click" width="400" popper-style="border-radius: 12px">
           <template #reference>
             <el-button class="el-button--default">
@@ -795,6 +874,7 @@ const ClickParams = (val: any) => {
               View Management
             </el-button>
           </template>
+
           <div class="Management">
             <div class="title">View Management</div>
             <div class="management_content" v-for="(item, index) in Management" :key="index">
@@ -819,6 +899,14 @@ const ClickParams = (val: any) => {
             </div>
           </div>
         </el-popover>
+
+        <img
+          id="view-management-guide"
+          v-if="guideStore.dashboard.isShowViewManagementGuidePhoto"
+          class="view-management-guide-class"
+          :src="viewManagementImg"
+          alt=""
+        />
         <el-popover
           :visible="SaveVisible"
           :popper-style="{
@@ -864,6 +952,18 @@ const ClickParams = (val: any) => {
             <div>Save Layout</div>
           </div>
         </el-popover>
+
+        <!--  -->
+        <img
+          id="save-config-guide"
+          v-if="guideStore.dashboard.isShowSaveConfigGuidePhoto"
+          class="save-config-guide-class position-absolute-guide"
+          :src="saveConfigImg"
+          :class="{
+            'save-config-guide-dark-class': themeStore.theme === 'dark'
+          }"
+          alt=""
+        />
       </div>
     </div>
     <!-- 图表 -->
@@ -886,15 +986,32 @@ const ClickParams = (val: any) => {
         <template v-for="item in Management" :key="item">
           <div v-if="item.title === 'KPI' && item.switchValue" class="filters_left">
             <!-- KPI -->
-            <VBox_Dashboard @changeCancel="changeCancel(item.id)">
+            <VBox_Dashboard
+              style="overflow: visible"
+              @changeCancel="changeCancel(item.id)"
+              :isShowDragIconGudie="true"
+            >
               <template #header>
-                <div class="Title_flex">
-                  <span>
+                <div class="Title_flex" style="position: relative">
+                  <img
+                    v-if="guideStore.dashboard.isShowKpiChartGuidePhoto"
+                    id="kpi-chart-guide"
+                    class="kpi-chart-guide-class position-absolute-guide"
+                    :src="kpiChartImg"
+                    alt=""
+                  />
+                  <div>
                     {{ item.title }}
-                    <!-- <VTipTooltip></VTipTooltip> -->
-                  </span>
+                    <VTipTooltip
+                      :img="kpiChartTip"
+                      :width="410"
+                      :label="'KPI Report:Day difference between actual and estimate.'"
+                      placement="bottom-start"
+                    ></VTipTooltip>
+                  </div>
                   <DashFilters
                     :defaultData="KPIDefaulteData"
+                    :isShowTransportModeGuide="true"
                     @FilterSearch="GetKpiData"
                   ></DashFilters>
                   <!-- <el-tooltip
@@ -940,7 +1057,14 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)">
               <template #header>
                 <div class="Title_flex">
-                  {{ item.title }}
+                  <div>
+                    {{ item.title }}
+                    <VTipTooltip
+                      :img="pendingChartTip"
+                      :width="420"
+                      :label="'Pending Report:Showing shipments which are soon to depart/arrive.'"
+                    ></VTipTooltip>
+                  </div>
                   <DashFilters
                     :defaultData="PendingDefaulteData"
                     :radioisDisabled="true"
@@ -981,7 +1105,15 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)">
               <template #header>
                 <div class="Title_flex">
-                  {{ item.title }}
+                  <div>
+                    {{ item.title }}
+                    <VTipTooltip
+                      :img="etdToEtaChartsTip"
+                      :width="430"
+                      :placement="'bottom-start'"
+                      :label="'ETD to ETA (Days):Distribution of Transit Time (ETA-ETD) for All Shipments in Last 12 Months.'"
+                    ></VTipTooltip>
+                  </div>
                   <DashFilters
                     :defaultData="ETDDefaulteData"
                     @FilterSearch="GetETDEcharts"
@@ -1009,7 +1141,13 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)">
               <template #header>
                 <div class="Title_flex">
-                  {{ item.title }}
+                  <div>
+                    {{ item.title }}
+                    <VTipTooltip
+                      :img="containerChartTip"
+                      :label="'Container Count:Total Container Volume by Month (Last 12 Months)'"
+                    ></VTipTooltip>
+                  </div>
                   <DashFilters
                     :defaultData="ContainerefaultData"
                     @FilterSearch="GetContainerCountEcharts"
@@ -1037,15 +1175,23 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)" style="width: 100%">
               <template #header>
                 <div class="Title_flex" style="height: 48px">
-                  <el-tabs
-                    v-model="activeName"
-                    class="demo-tabs"
-                    style="height: 48px"
-                    @tab-click="handleTabClick"
-                  >
-                    <el-tab-pane :label="item.title1" name="first"></el-tab-pane>
-                    <el-tab-pane :label="item.title2" name="second"></el-tab-pane>
-                  </el-tabs>
+                  <div style="display: flex">
+                    <el-tabs
+                      v-model="activeName"
+                      class="demo-tabs"
+                      style="height: 48px"
+                      @tab-click="handleTabClick"
+                    >
+                      <el-tab-pane :label="item.title1" name="first"></el-tab-pane>
+                      <el-tab-pane :label="item.title2" name="second"></el-tab-pane>
+                    </el-tabs>
+                    <VTipTooltip
+                      style="margin-left: 4px"
+                      :img="top10ChartTip"
+                      :label="'Top 10 Origin & Destination: Last 12 Months Shipment Volume Rankings: Top 10 Origin Cities and Top 10 Destination Cities'"
+                      :width="700"
+                    ></VTipTooltip>
+                  </div>
                   <DashFilters
                     :defaultData="Top10DefaultData"
                     @FilterSearch="GetTop10ODEcharts"
@@ -1097,7 +1243,15 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)">
               <template #header>
                 <div class="Title_flex">
-                  {{ item.title }}
+                  <div>
+                    {{ item.title }}
+                    <VTipTooltip
+                      :img="co2eChartTip"
+                      :label="'CO2e Emission by Origin or Destination: Last 12 Months CO2e Emission Rankings: Top 10 Origin Cities and Top 10 Destination Cities'"
+                      :width="700"
+                      :placement="'bottom-start'"
+                    ></VTipTooltip>
+                  </div>
                   <DashFilters
                     :defaultData="Co2OriginDefaultData"
                     @FilterSearch="GetCo2EmissionEcharts"
@@ -1125,7 +1279,10 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)">
               <template #header>
                 <div class="Title_flex">
-                  {{ item.title }}
+                  <div>
+                    {{ item.title }}
+                    <!-- <VTipTooltip :img="co2eChartTip"></VTipTooltip> -->
+                  </div>
                   <DashFilters
                     :defaultData="Co2DestinationDefaultData"
                     @FilterSearch="GetCo2DestinationEcharts"
@@ -1159,7 +1316,15 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)" style="width: 100%">
               <template #header>
                 <div class="Title_flex">
-                  {{ item.title }}
+                  <div>
+                    {{ item.title }}
+                    <VTipTooltip
+                      :img="recentStatusChartTip"
+                      :label="'Recent Status: Active shipment list with ETD within the past three months and the next month.'"
+                      :width="700"
+                      :placement="'bottom-start'"
+                    ></VTipTooltip>
+                  </div>
                   <DashFilters
                     :defaultData="RecentDefaulteData"
                     @FilterSearch="getTableData"
@@ -1190,7 +1355,16 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)" style="width: 100%">
               <template #header>
                 <div class="Title_flex">
-                  Revenue Spent
+                  <div>
+                    Revenue Spent
+                    <VTipTooltip
+                      :img="revenueSpentChartTip"
+                      :label="'Revenue Spent: Based on the billto object, display the corresponding revenue data. '"
+                      :placement="'bottom-start'"
+                      :width="700"
+                    ></VTipTooltip>
+                  </div>
+
                   <DashFilters
                     :defaultData="RevenueDefaultData"
                     @FilterSearch="GetRevenueEcharts"
@@ -1230,6 +1404,36 @@ const ClickParams = (val: any) => {
 .iconfont {
   vertical-align: -2px;
 }
+
+.view-management-guide-class {
+  position: absolute;
+  top: 0px;
+  right: 89px;
+  width: 343px;
+  height: 478px;
+  z-index: 1500;
+}
+.save-config-guide-class {
+  position: absolute;
+  top: 0px;
+  right: 0px;
+  width: 186px;
+  height: 149px;
+  z-index: 1500;
+  &.save-config-guide-dark-class {
+    top: -1px;
+    width: 187px;
+    height: 153px;
+  }
+}
+.kpi-chart-guide-class {
+  top: -1px;
+  left: 10px;
+  width: 589px;
+  height: 478px;
+  z-index: 3500;
+}
+
 .Management {
   max-height: 640px;
   overflow-y: hidden;
@@ -1423,3 +1627,8 @@ const ClickParams = (val: any) => {
   margin-bottom: 0;
 }
 </style>
+<style lang="scss">
+:not(body):has(> img.driver-active-element) {
+  overflow: visible !important;
+}
+</style>

+ 36 - 4
src/views/Dashboard/src/components/DashFiters.vue

@@ -27,6 +27,10 @@ const props = defineProps({
   isRevenue: {
     type: Boolean,
     default: false
+  },
+  isShowTransportModeGuide: {
+    type: Boolean,
+    default: false
   }
 })
 
@@ -164,6 +168,18 @@ const DateRangeSearch = () => {
   }
   filters_visible.value = false
 }
+
+import { useThemeStore } from '@/stores/modules/theme'
+
+import { useGuideStore } from '@/stores/modules/guide'
+import transportModeLight from '@/views/Dashboard/src/guideImage/transport-mode.png'
+import transportModeDark from '@/views/Dashboard/src/guideImage/dark-transport-mode.png'
+
+const themeStore = useThemeStore()
+const transportModeImg = computed(() => {
+  return themeStore.theme === 'dark' ? transportModeDark : transportModeLight
+})
+const guideStore = useGuideStore()
 </script>
 <template>
   <div class="DashFilters">
@@ -270,13 +286,29 @@ const DateRangeSearch = () => {
         </div>
       </div>
     </el-popover>
+    <img
+      id="transport-mode-guide"
+      :src="transportModeImg"
+      v-if="isShowTransportModeGuide && guideStore.dashboard.isShowTransportModeGuidePhoto"
+      class="transport-mode-guide-class position-absolute-guide"
+      alt=""
+    />
   </div>
 </template>
 <style lang="scss" scoped>
 .DashFilters {
+  position: relative;
   display: flex;
   align-items: center;
 }
+.transport-mode-guide-class {
+  position: absolute;
+  right: 0px;
+  top: -1px;
+  width: 417px;
+  height: 314px;
+  z-index: 1000;
+}
 :deep(.el-checkbox-button__inner) {
   color: var(--tag-info-text-color);
   font-size: var(--font-size-3);
@@ -381,10 +413,10 @@ const DateRangeSearch = () => {
   align-items: center;
 }
 :deep(
-    .el-radio-button.is-active
-      .el-radio-button__original-radio:not(:disabled)
-      + .el-radio-button__inner
-  ) {
+  .el-radio-button.is-active
+    .el-radio-button__original-radio:not(:disabled)
+    + .el-radio-button__inner
+) {
   height: 40px;
   display: flex;
   align-items: center;

+ 156 - 0
src/views/Dashboard/src/components/DashboardGuide.vue

@@ -0,0 +1,156 @@
+<script setup lang="ts">
+import { useDriver } from '@/utils/driverGuide'
+import { useGuideStore } from '@/stores/modules/guide'
+
+const guideStore = useGuideStore()
+
+const dashboardGuideStore = guideStore.dashboard
+const steps: any = [
+  {
+    element: '#view-management-guide',
+    popover: {
+      title: '',
+      description:
+        'The Dashboard integrates different types of reports. You can freely select the reports you need. ',
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#transport-mode-guide',
+    popover: {
+      title: '',
+      description:
+        'Each report comes with its own filter options. Simply make your selection and click "Search" to refresh the data. ',
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#kpi-chart-guide',
+    popover: {
+      title: '',
+      description:
+        'Hover the icon next to the report name to view the data explanation for each report.',
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#drag-icon-guide',
+    popover: {
+      title: '',
+      description:
+        'Drag and drop reports using the drag handle (eight-dot icon) to reposition them in any direction. ',
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#save-config-guide',
+    popover: {
+      title: '',
+      description: 'Click "Save Layout" to preserve your customized report arrangement.',
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#page-guide-btn-guide',
+    popover: {
+      title: '',
+      description:
+        'After closing, you can still click the "page guide" button to view the page guide of the current page.',
+      side: 'bottom'
+    }
+  }
+]
+
+const guideTimer = ref<ReturnType<typeof setTimeout> | null>(null)
+const { start, moveNext, movePrevious, destroy, hasNextStep, moveTo } = useDriver(steps, {
+  onNextClick: (element, step, options) => {
+    if (options?.state?.activeIndex === 0) {
+      dashboardGuideStore.isShowViewManagementGuidePhoto = false
+      dashboardGuideStore.isShowTransportModeGuidePhoto = true
+    } else if (options?.state?.activeIndex === 1) {
+      dashboardGuideStore.isShowTransportModeGuidePhoto = false
+      dashboardGuideStore.isShowKpiChartGuidePhoto = true
+    } else if (options?.state?.activeIndex === 2) {
+      dashboardGuideStore.isShowKpiChartGuidePhoto = false
+    } else if (options?.state?.activeIndex === 3) {
+      dashboardGuideStore.isShowSaveConfigGuidePhoto = true
+    } else if (options?.state?.activeIndex === 4) {
+      dashboardGuideStore.isShowSaveConfigGuidePhoto = false
+    }
+    nextTick(() => {
+      moveNext() // 执行下一步
+    })
+  },
+  onPrevClick: (element, step, options) => {
+    if (options?.state?.activeIndex === 1) {
+      dashboardGuideStore.isShowViewManagementGuidePhoto = true
+      dashboardGuideStore.isShowTransportModeGuidePhoto = false
+    } else if (options?.state?.activeIndex === 2) {
+      dashboardGuideStore.isShowTransportModeGuidePhoto = true
+      dashboardGuideStore.isShowKpiChartGuidePhoto = false
+    } else if (options?.state?.activeIndex === 3) {
+      dashboardGuideStore.isShowKpiChartGuidePhoto = true
+      dashboardGuideStore.isShowSaveConfigGuidePhoto = false
+    } else if (options?.state?.activeIndex === 4) {
+      dashboardGuideStore.isShowSaveConfigGuidePhoto = false
+    } else if (options?.state?.activeIndex === 5) {
+      dashboardGuideStore.isShowSaveConfigGuidePhoto = true
+    }
+    if (guideTimer.value) {
+      clearTimeout(guideTimer.value)
+      guideTimer.value = null
+    }
+    nextTick(() => {
+      movePrevious() // 执行上一步
+    })
+  },
+  onHighlightStarted: () => {
+    if (!hasNextStep()) {
+      guideTimer.value = setTimeout(() => {
+        destroy()
+      }, 3000)
+    }
+  },
+  // 关闭导览时,隐藏所有图片
+  onDestroyStarted: (element, step, options) => {
+    dashboardGuideStore.isShowViewManagementGuidePhoto = false
+    dashboardGuideStore.isShowTransportModeGuidePhoto = false
+    dashboardGuideStore.isShowSaveConfigGuidePhoto = false
+    dashboardGuideStore.isShowKpiChartGuidePhoto = false
+    if (hasNextStep()) {
+      moveTo(options.config.steps.length - 1)
+      return
+    }
+    // showExtraHighlights.value = false // 隐藏额外高亮
+    nextTick(() => {
+      destroy() // 销毁导览
+    }) // 确保在下一次DOM更新后执行销毁
+  },
+  onDestroyed: () => {
+    dashboardGuideStore.isShowViewManagementGuidePhoto = false
+    dashboardGuideStore.isShowTransportModeGuidePhoto = false
+    dashboardGuideStore.isShowSaveConfigGuidePhoto = false
+    dashboardGuideStore.isShowKpiChartGuidePhoto = false
+    if (guideTimer.value) {
+      clearTimeout(guideTimer.value)
+      guideTimer.value = null
+    }
+  }
+})
+const startGuide = () => {
+  dashboardGuideStore.isShowViewManagementGuidePhoto = true
+  nextTick(() => {
+    start() // 开始引导
+  }) // 延时1秒开始引导,确保图片加载完成
+}
+
+defineExpose({
+  startGuide
+})
+</script>
+
+<template>
+  <div></div>
+</template>
+
+<style lang="scss" scoped></style>

BIN
src/views/Dashboard/src/guideImage/co2e-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/container-count-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/dark-kpi-chart-guide.png


BIN
src/views/Dashboard/src/guideImage/dark-save-config-guide.png


BIN
src/views/Dashboard/src/guideImage/dark-transport-mode.png


BIN
src/views/Dashboard/src/guideImage/dark-view-management.png


BIN
src/views/Dashboard/src/guideImage/etd-to-eta-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/kpi-chart-guide.png


+ 0 - 0
src/views/Dashboard/src/image/kpi-chart-tip.png → src/views/Dashboard/src/guideImage/kpi-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/pending-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/recent-status-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/revenue-spent-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/save-config-guide.png


+ 7 - 0
src/views/Dashboard/src/guideImage/test.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div></div>
+</template>
+
+<style lang="scss" scoped></style>

BIN
src/views/Dashboard/src/guideImage/top-10-chart-tip.png


BIN
src/views/Dashboard/src/guideImage/transport-mode.png


BIN
src/views/Dashboard/src/guideImage/view-management.png


BIN
src/views/Dashboard/src/tipsImage/co2e-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/container-count-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-co2e-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-container-count-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-etd-to-eta-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-kpi-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-pending-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-recent-status-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-revenue-spent-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/dark-top-10-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/etd-to-eta-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/kpi-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/pending-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/recent-status-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/revenue-spent-chart-tip.png


BIN
src/views/Dashboard/src/tipsImage/top-10-chart-tip.png


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

@@ -14,6 +14,9 @@ import TrainingCard from './components/TrainingCard.vue'
 import emitter from '@/utils/bus'
 
 const notificationMsgStore = useNotificationMessage()
+import { useGuideStore } from '@/stores/modules/guide'
+
+const guideStore = useGuideStore()
 const themeStore = useThemeStore()
 const userStore = useUserStore()
 const route = useRoute()
@@ -133,7 +136,7 @@ const handleUserManual = () => {
         }
         if (res.data?.code === 403) {
           sessionStorage.clear()
-          emitter.emit('login-out');
+          emitter.emit('login-out')
           router.push('/login')
           userStore.logout()
           ElMessage.warning({
@@ -206,8 +209,14 @@ onMounted(() => {
     element-loading-custom-class="element-loading"
     element-loading-background="rgb(43, 47, 54, 0.7)"
   >
-    <VBreadcrumb></VBreadcrumb>
-    <TrainingCard ref="trainingCardRef"></TrainingCard>
+    <div style="display: flex">
+      <VBreadcrumb></VBreadcrumb>
+      <TrainingCard ref="trainingCardRef"></TrainingCard>
+      <VDriverGuide
+        v-if="route.name === 'Tracking Detail'"
+        @click="guideStore.handleTrackingDetailGuide()"
+      ></VDriverGuide>
+    </div>
     <div class="right-info">
       <el-input
         v-model="searchValue"
@@ -384,6 +393,7 @@ onMounted(() => {
   display: flex;
   justify-content: space-between;
   position: relative;
+  align-items: center;
   height: 100%;
 }
 .right-info {

+ 83 - 58
src/views/Tracking/src/TrackingView.vue

@@ -8,13 +8,7 @@ import MoreFilters from '@/components/MoreFilters'
 import { ref, reactive, onMounted } from 'vue'
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
 import { useHeaderSearch } from '@/stores/modules/headerSearch'
-import { useDriver } from '@/utils/driverGuide'
 
-const steps = []
-const { start } = useDriver(steps)
-const handleGuide = () => {
-  start() // 开始引导
-}
 const headerSearch = useHeaderSearch()
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
@@ -640,75 +634,91 @@ const SearchInput = () => {
   sessionStorage.setItem('searchTableQeuryTracking', JSON.stringify(searchTableQeuryTracking))
   Gettrackingdata()
 }
+
+import TrackingGuide from './components/TrackingGuide.vue'
+import { useGuideStore } from '@/stores/modules/guide'
+
+const guideStore = useGuideStore()
+const trackingGuideRef = ref()
+const handleGuide = () => {
+  trackingGuideRef.value.startGuide() // 开始引导
+}
 </script>
 
 <template>
   <div class="Title">
     <span>Tracking</span>
-    <!-- <VDriverGuide @click="handleGuide"></VDriverGuide> -->
+    <VDriverGuide @click="handleGuide"></VDriverGuide>
   </div>
-
+  <TrackingGuide ref="trackingGuideRef"></TrackingGuide>
   <div class="display" ref="filterRef">
-    <FilterTags :TagsListItem="TagsList" @changeTag="changeTag"></FilterTags>
-    <div class="heaer_top">
-      <div class="search">
-        <el-input
-          placeholder="Enter Booking/HBL/PO/Container/Carrier Booking No. "
-          v-model="TrackingSearch"
-          class="log_input"
-          @keyup.enter="SearchInput"
-        >
-          <template #prefix>
-            <span class="iconfont_icon">
-              <svg class="iconfont icon_search" aria-hidden="true">
-                <use xlink:href="#icon-icon_search_b"></use>
-              </svg>
-            </span>
-          </template>
-          <template #suffix>
-            <el-tooltip
-              v-if="isShowAlertIcon"
-              :offset="6"
-              popper-class="ShowAlerIcon"
-              effect="dark"
-              content="We support the following references number to find bookings:· Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote No."
-              placement="bottom"
+    <div class="filter-box">
+      <div class="filters-container" id="filters-container-guide">
+        <FilterTags :TagsListItem="TagsList" @changeTag="changeTag"></FilterTags>
+        <div class="heaer_top">
+          <div class="search">
+            <el-input
+              placeholder="Enter Booking/HBL/PO/Container/Carrier Booking No. "
+              v-model="TrackingSearch"
+              class="log_input"
+              @keyup.enter="SearchInput"
             >
-              <span class="iconfont_icon iconfont_icon_tip">
-                <svg class="iconfont icon_search" aria-hidden="true">
-                  <use xlink:href="#icon-icon_info_b"></use>
-                </svg>
-              </span>
-            </el-tooltip>
-          </template>
-        </el-input>
+              <template #prefix>
+                <span class="iconfont_icon">
+                  <svg class="iconfont icon_search" aria-hidden="true">
+                    <use xlink:href="#icon-icon_search_b"></use>
+                  </svg>
+                </span>
+              </template>
+              <template #suffix>
+                <el-tooltip
+                  v-if="isShowAlertIcon"
+                  :offset="6"
+                  popper-class="ShowAlerIcon"
+                  effect="dark"
+                  content="We support the following references number to find bookings:· Booking No./HAWB No./MAWB No./PO No./Carrier Booking No./Contract No./File No./Quote No."
+                  placement="bottom"
+                >
+                  <span class="iconfont_icon iconfont_icon_tip">
+                    <svg class="iconfont icon_search" aria-hidden="true">
+                      <use xlink:href="#icon-icon_info_b"></use>
+                    </svg>
+                  </span>
+                </el-tooltip>
+              </template>
+            </el-input>
+          </div>
+
+          <TransportMode
+            :isShipment="true"
+            :TransportListItem="TransportListItem"
+            @TransportSearch="TransportSearch"
+            @defaultTransport="defaultTransport"
+            @clearTransportTags="clearTransportTags"
+          ></TransportMode>
+          <DateRange
+            :isShipment="true"
+            @DateRangeSearch="DateRangeSearch"
+            @clearDaterangeTags="clearDaterangeTags"
+            @defaultDate="defaultDate"
+          ></DateRange>
+        </div>
       </div>
-      <TransportMode
-        :isShipment="true"
-        :TransportListItem="TransportListItem"
-        @TransportSearch="TransportSearch"
-        @defaultTransport="defaultTransport"
-        @clearTransportTags="clearTransportTags"
-      ></TransportMode>
-      <DateRange
-        :isShipment="true"
-        @DateRangeSearch="DateRangeSearch"
-        @clearDaterangeTags="clearDaterangeTags"
-        @defaultDate="defaultDate"
-      ></DateRange>
       <MoreFilters
         :isShipment="true"
         :searchTableQeury="searchTableQeuryTracking"
         @MoreFiltersSearch="MoreFiltersSearch"
         @clearMoreFiltersTags="clearMoreFiltersTags"
         @defaultMorefilters="defaultMorefilters"
+        :isShowMoreFiltersGuidePhoto="guideStore.tracking.isShowMoreFiltersGuidePhoto"
+        :pageMode="'tracking'"
       ></MoreFilters>
       <el-button class="el-button--dark" style="margin-left: 8px" @click="SearchInput"
         >Search</el-button
       >
     </div>
     <!-- 筛选项 -->
-    <div class="filtersTag" v-if="tagsData.length">
+    <div class="filtersTag" v-if="tagsData.length" id="filter-tag-guide">
       <el-tag
         :key="tag"
         class="tag"
@@ -740,6 +750,20 @@ const SearchInput = () => {
 </template>
 
 <style lang="scss" scoped>
+.filter-box {
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+  align-items: flex-end;
+  height: 100%;
+}
+.filters-container {
+  max-width: 1426px;
+  width: 80%;
+  display: flex;
+  flex-direction: column;
+}
+
 .Title {
   display: flex;
   height: 68px;
@@ -757,17 +781,18 @@ const SearchInput = () => {
   padding: 0 24px;
 }
 .heaer_top {
+  position: relative;
   margin-top: 6.57px;
-  margin-bottom: 8px;
   display: flex;
 }
 .search {
-  width: 400px;
+  // width: 400px;
   height: 32px;
 }
 .filtersTag {
-  margin-bottom: 8.7px;
-  display: flex;
+  margin-top: 8px;
+  margin-bottom: 4px;
+  display: inline-flex;
   align-items: center;
   flex-wrap: wrap;
 }

+ 84 - 0
src/views/Tracking/src/components/MultiHighlightGuide.vue

@@ -0,0 +1,84 @@
+<template>
+  <teleport to="body">
+    <div style="background-color: red">
+      <div v-for="(item, index) in positionedItems" :key="index">
+        <div class="highlight-box" :style="item.boxStyle"></div>
+        <div class="highlight-tip" :style="item.tipStyle">
+          {{ item.tip }}
+        </div>
+      </div>
+    </div>
+  </teleport>
+</template>
+
+<script setup lang="ts">
+import { onMounted, onBeforeUnmount, ref, watch, nextTick } from 'vue'
+
+interface Item {
+  selector: string
+  tip: string
+}
+
+const props = defineProps<{ items: Item[] }>()
+
+const positionedItems = ref<{ boxStyle: any; tipStyle: any; tip: string }[]>([])
+
+const updatePositions = () => {
+  positionedItems.value = props.items.map(({ selector, tip }) => {
+    const el = document.querySelector(selector) as HTMLElement
+    if (!el) return { boxStyle: {}, tipStyle: {}, tip }
+    const rect = el.getBoundingClientRect()
+    return {
+      tip,
+      boxStyle: {
+        position: 'absolute',
+        top: `${rect.top + window.scrollY}px`,
+        left: `${rect.left + window.scrollX}px`,
+        width: `${rect.width}px`,
+        height: `${rect.height}px`,
+        borderRadius: '6px',
+        zIndex: 99998
+      },
+      tipStyle: {
+        position: 'absolute',
+        top: `${rect.top + window.scrollY - 52}px`,
+        left: `${rect.left + window.scrollX}px`,
+        backgroundColor: 'var(--color-tour-popover-bg)',
+        color: 'var(--color-neutral-1)',
+        padding: '16px',
+        borderRadius: '4px',
+        fontSize: '14px',
+        zIndex: 99999,
+        boxShadow: '0 2px 6px rgba(0, 0, 0, 0.2)'
+      }
+    }
+  })
+}
+
+onMounted(() => {
+  nextTick(updatePositions)
+  window.addEventListener('resize', updatePositions)
+  window.addEventListener('scroll', updatePositions)
+})
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', updatePositions)
+  window.removeEventListener('scroll', updatePositions)
+})
+
+watch(
+  () => props.items,
+  () => nextTick(updatePositions),
+  { deep: true }
+)
+</script>
+
+<style scoped>
+.highlight-box {
+  pointer-events: none;
+}
+.highlight-tip {
+  pointer-events: none;
+  position: absolute;
+}
+</style>

+ 4 - 144
src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue

@@ -17,149 +17,8 @@ import { useOverflow } from '@/hooks/useOverflow'
 import { formatTimezone } from '@/utils/tools'
 import { useThemeStore } from '@/stores/modules/theme'
 import ShareLinkDialog from './components/ShareLinkDialog.vue'
-import { useDriver } from '@/utils/driverGuide'
-
-// import { driver } from 'driver.js'
-
-const oceanSteps: any = [
-  {
-    element: '#driver-step-tracking-detail-1',
-    popover: {
-      title: '',
-      description: 'Main operation button area',
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#driver-step-tracking-detail-2',
-    popover: {
-      description: 'Key shipment status',
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#driver-step-tracking-detail-3',
-    popover: {
-      description: 'Detail container status of each container',
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#tracking-map',
-    popover: {
-      description: `
-        <ol style="margin-left: 16px; list-style: disc">
-          <li>Actual Line(Actual Trajectory)</li>
-          <li>Virtual Line(Planned Trajectory)</li>
-          <li>Arrow (Current Real-time Position)</li>
-        </ol>
-      `,
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#driver-step-tracking-detail-5',
-    popover: {
-      description: `
-      <ol style="margin-left: 16px; list-style: disc">
-        <li>Upload Files</li>
-        <li>Clickable download icons to download files</li>
-      </ol>
-      `,
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#driver-step-tracking-detail-6',
-    popover: {
-      description: `
-        <p>Send email to site staff</p>
-        <p>Enter contents you want to communicate with, click “Send 
-         Email” button to send out.
-        </p>
-      `,
-      side: 'bottom',
-      align: 'start'
-    }
-  }
-]
-const airSteps: any = [
-  {
-    element: '#driver-step-tracking-detail-1',
-    popover: {
-      title: '',
-      description: 'Main operation button area',
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#driver-step-tracking-detail-2',
-    popover: {
-      description: 'Key shipment status',
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#tracking-map',
-    popover: {
-      description: `
-        <ol style="margin-left: 16px; list-style: disc">
-          <li>Actual Line(Actual Trajectory)</li>
-          <li>Virtual Line(Planned Trajectory)</li>
-          <li>Arrow (Current Real-time Position)</li>
-        </ol>
-      `,
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#driver-step-tracking-detail-5',
-    popover: {
-      description: `
-      <ol style="margin-left: 16px; list-style: disc">
-        <li>Upload Files</li>
-        <li>Clickable download icons to download files</li>
-      </ol>
-      `,
-      side: 'bottom',
-      align: 'start'
-    }
-  },
-  {
-    element: '#driver-step-tracking-detail-6',
-    popover: {
-      description: `
-        <p>Send email to site staff</p>
-        <p>Enter contents you want to communicate with, click “Send 
-         Email” button to send out.
-        </p>
-      `,
-      side: 'bottom',
-      align: 'start'
-    }
-  }
-]
-const handleGuide = () => {
-  let steps = []
-  if (allData.value?.transportInfo?.mode === 'Ocean Freight') {
-    steps = oceanSteps
-  } else if (allData.value?.transportInfo?.mode === 'Air Freight') {
-    steps = airSteps
-  } else {
-    return
-  }
-  const { start } = useDriver(steps)
-  console.log(allData.value?.transportInfo?.mode, 'value')
-  start() // 开始引导
-}
+import { useGuideStore } from '@/stores/modules/guide'
+const guideStore = useGuideStore()
 
 const route = useRoute()
 
@@ -246,6 +105,7 @@ const getData = () => {
         is_subscribe.value = res.data.is_subscribe
         // 获取数据
         allData.value = res.data
+        guideStore.trackingDetail.mode = allData.value?.transportInfo?.mode || 'Ocean Freight'
       }
     })
     .finally(() => {
@@ -305,7 +165,7 @@ const SubscribeShipments = () => {
         <VTag large :type="allData?.transportInfo?.status">{{
           allData?.transportInfo?.status
         }}</VTag>
-        <VDriverGuide @click="handleGuide"></VDriverGuide>
+
         <div class="right-operation" id="driver-step-tracking-detail-1">
           <el-button
             v-if="

+ 254 - 0
src/views/Tracking/src/components/TrackingGuide.vue

@@ -0,0 +1,254 @@
+<script setup lang="ts">
+import { useDriver } from '@/utils/driverGuide'
+// import MultiHighlightGuide from './MultiHighlightGuide.vue'
+import { useGuideStore } from '@/stores/modules/guide'
+import { useThemeStore } from '@/stores/modules/theme'
+
+import customizeColumnsImgLight from '../image/customize-columns.png'
+import customizeColumnsImgDark from '../image/dark-customize-columns.png'
+import downloadFileImgLight from '../image/download-guide.png'
+import downloadFileImgDark from '../image/dark-download-guide.png'
+
+const themeStore = useThemeStore()
+const customizeColumnsImg = computed(() => {
+  return themeStore.theme === 'dark' ? customizeColumnsImgDark : customizeColumnsImgLight
+})
+const downloadFileImg = computed(() => {
+  return themeStore.theme === 'dark' ? downloadFileImgDark : downloadFileImgLight
+})
+
+const guideStore = useGuideStore()
+
+const trackingGuideStore = guideStore.tracking
+const steps: any = [
+  {
+    element: '#filters-container-guide',
+    popover: {
+      title: '',
+      description: `Frequently Used Search Criteria Display Area:
+        <ul>
+          <li>Key Booking Status</li>
+          <li>Reference Numbers</li>
+          <li>Transport Mode</li>
+          <li>Date Type & Range</li>
+        </ul>`,
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#filter-tag-guide',
+    popover: {
+      title: '',
+      description: `
+        <ul>
+          <li>Selected query criteria display area</li>
+          <li>You can quickly clear selected conditions at this position</li>
+        </ul>
+      `,
+      side: 'bottom'
+    }
+    // onHighlightStarted: () => {
+    //   showExtraHighlights.value = true
+    // },
+    // onDeselected: () => {
+    //   showExtraHighlights.value = false
+    // }
+  },
+  {
+    element: '#more-filters-guide',
+    popover: {
+      title: '',
+      description: 'Click "More Filters" to see more search options.',
+      side: 'left'
+    }
+  },
+  {
+    element: '.hbol-guide-header',
+    popover: {
+      title: '',
+      description:
+        'Click on the HBL No. or double-click anywhere on a single shipment data to enter the detailed page.',
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#download-file-guide',
+    popover: {
+      title: '',
+      description: `
+        <ul>
+          <li>View the number of shipments selected for download</li>
+          <li>Two download list templates are available</li>
+        </ul>
+      `,
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#customize-columns-guide',
+    popover: {
+      title: '',
+      description: `
+        <ul>
+          <li>Drag to right to add columns</li>
+          <li>Drag to left to unselect columns</li>
+          <li>Drag up and down to reorder or select/unselect</li>
+        </ul>
+      `,
+      side: 'bottom'
+    }
+  },
+  {
+    element: '#page-guide-btn-guide',
+    popover: {
+      title: '',
+      description:
+        'After closing, you can still click the "page guide" button to view the page guide of the current page.',
+      side: 'bottom'
+    }
+  }
+]
+
+// const showExtraHighlights = ref(false)
+// const extraHighlightItems = [
+//    { selector: '#tab-filter-guide', tip: 'Select booking status. The default is all.' }
+// ]
+
+const guideTimer = ref<ReturnType<typeof setTimeout> | null>(null)
+const { start, moveNext, movePrevious, destroy, hasNextStep, moveTo } = useDriver(steps, {
+  onNextClick: (element, step, options) => {
+    if (options?.state?.activeIndex === 1) {
+      trackingGuideStore.isShowMoreFiltersGuidePhoto = true
+    } else if (options?.state?.activeIndex === 2) {
+      trackingGuideStore.isShowMoreFiltersGuidePhoto = false
+    } else if (options?.state?.activeIndex === 3) {
+      trackingGuideStore.isShowDownloadFileGuidePhoto = true
+    } else if (options?.state?.activeIndex === 4) {
+      trackingGuideStore.isShowDownloadFileGuidePhoto = false
+
+      trackingGuideStore.isShowCustomizeColumnsGuidePhoto = true
+    } else if (options?.state?.activeIndex === 5) {
+      trackingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    }
+    nextTick(() => {
+      moveNext() // 执行下一步
+    })
+  },
+  onPrevClick: (element, step, options) => {
+    if (options?.state?.activeIndex === 2) {
+      trackingGuideStore.isShowMoreFiltersGuidePhoto = false
+    } else if (options?.state?.activeIndex === 3) {
+      trackingGuideStore.isShowMoreFiltersGuidePhoto = true
+    } else if (options?.state?.activeIndex === 4) {
+      trackingGuideStore.isShowDownloadFileGuidePhoto = false
+    } else if (options?.state?.activeIndex === 5) {
+      trackingGuideStore.isShowDownloadFileGuidePhoto = true
+      trackingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    } else if (options?.state?.activeIndex === 6) {
+      trackingGuideStore.isShowCustomizeColumnsGuidePhoto = true
+    }
+    if (guideTimer.value) {
+      clearTimeout(guideTimer.value)
+      guideTimer.value = null
+    }
+    nextTick(() => {
+      movePrevious() // 执行上一步
+    })
+  },
+  onHighlightStarted: () => {
+    if (!hasNextStep()) {
+      guideTimer.value = setTimeout(() => {
+        destroy()
+      }, 3000)
+    }
+  },
+  // 关闭导览时,隐藏所有图片
+  onDestroyStarted: (element, step, options) => {
+    trackingGuideStore.isShowFilterGuidePhoto = false
+    trackingGuideStore.isShowMoreFiltersGuidePhoto = false
+    trackingGuideStore.isShowDownloadFileGuidePhoto = false
+    trackingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    if (hasNextStep()) {
+      moveTo(options.config.steps.length - 1)
+      return
+    }
+    // showExtraHighlights.value = false // 隐藏额外高亮
+    nextTick(() => {
+      destroy() // 销毁导览
+    }) // 确保在下一次DOM更新后执行销毁
+  },
+  onDestroyed: () => {
+    trackingGuideStore.isShowFilterGuidePhoto = false
+    trackingGuideStore.isShowMoreFiltersGuidePhoto = false
+    trackingGuideStore.isShowDownloadFileGuidePhoto = false
+    trackingGuideStore.isShowCustomizeColumnsGuidePhoto = false
+    if (guideTimer.value) {
+      clearTimeout(guideTimer.value)
+      guideTimer.value = null
+    }
+  }
+})
+const startGuide = () => {
+  trackingGuideStore.isShowFilterGuidePhoto = true
+  nextTick(() => {
+    start() // 开始引导
+  }) // 延时1秒开始引导,确保图片加载完成
+}
+
+defineExpose({
+  startGuide
+})
+</script>
+
+<template>
+  <div>
+    <!-- 多区域自定义高亮组件 -->
+    <!-- <MultiHighlightGuide v-if="showExtraHighlights" :items="extraHighlightItems" /> -->
+
+    <!-- download-file-guide  -->
+    <Teleport to="body">
+      <img
+        style="box-shadow: 4px 4px 16px rgba(0, 0, 0, 0.1)"
+        id="download-file-guide"
+        v-if="trackingGuideStore.isShowDownloadFileGuidePhoto"
+        :class="{
+          'download-file-guide-dark-class': themeStore.theme === 'dark'
+        }"
+        class="position-absolute-guide download-file-guide-class"
+        :src="downloadFileImg"
+        alt=""
+      />
+    </Teleport>
+    <!-- customize-columns-guide -->
+    <Teleport to="body">
+      <img
+        id="customize-columns-guide"
+        v-if="trackingGuideStore.isShowCustomizeColumnsGuidePhoto"
+        class="position-absolute-guide customize-columns-guide-class"
+        :src="customizeColumnsImg"
+        alt=""
+      />
+    </Teleport>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.download-file-guide-class {
+  right: 203px;
+  top: 246px;
+  width: 416px;
+  height: 287px;
+  transform: translate(0.7px, -0.3px);
+  &.download-file-guide-dark-class {
+    transform: translate(0.7px, -0.3px);
+    width: 411px;
+    height: 287px;
+  }
+}
+.customize-columns-guide-class {
+  right: 24px;
+  top: 246px;
+  width: 642px;
+  height: 441px;
+}
+</style>

+ 18 - 4
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -67,6 +67,13 @@ const handleColumns = (columns: any, status?: string) => {
         formatter: ({ cellValue }: any) => formatNumber(Number(cellValue), item?.digits)
       }
     }
+
+    if (item.title === 'HBOL/HAWB No.') {
+      curColumn = {
+        ...curColumn,
+        headerClassName: 'hbol-guide-header'
+      }
+    }
     return curColumn
   })
   return newColumns
@@ -586,12 +593,19 @@ defineExpose({
     <div class="table-tools">
       <div class="left-total-records">{{ selectedNumber }} Selected</div>
       <div class="right-tools-btn">
-        <el-button class="el-button--main el-button--pain-theme" @click="handleDownload">
-          <span style="margin-right: 8px" class="font_family icon-icon_download_b"></span>
+        <el-button
+          class="el-button--main el-button--pain-theme"
+          @click="handleDownload"
+          :style="{
+            paddingRight: themeStore.theme === 'dark' ? '13px' : '16px',
+            paddingLeft: themeStore.theme === 'dark' ? '13px' : '11px'
+          }"
+        >
+          <span style="margin-right: 7px" class="font_family icon-icon_download_b"></span>
           Download
         </el-button>
-        <el-button type="default" @click="handleCustomizeColumns">
-          <span style="margin-right: 8px" class="font_family icon-icon_column_b"></span>
+        <el-button style="padding-left: 10px" type="default" @click="handleCustomizeColumns">
+          <span style="margin-right: 6px" class="font_family icon-icon_column_b"></span>
           Customize Columns
         </el-button>
       </div>

BIN
src/views/Tracking/src/image/customize-columns.png


BIN
src/views/Tracking/src/image/dark-customize-columns.png


BIN
src/views/Tracking/src/image/dark-download-guide.png


BIN
src/views/Tracking/src/image/dark-more-filters-guide.png


BIN
src/views/Tracking/src/image/download-guide.png


BIN
src/views/Tracking/src/image/more-filters-guide.png