Prechádzať zdrojové kódy

Merge branch 'dev_zyh' of United_Software/k_online_ui into dev

Jack Zhou 5 mesiacov pred
rodič
commit
de4f348e69

+ 1 - 0
package.json

@@ -27,6 +27,7 @@
     "crypto-js": "^4.2.0",
     "dayjs": "^1.11.13",
     "decimal.js": "^10.4.3",
+    "driver.js": "^1.3.6",
     "echarts": "^5.5.1",
     "element-plus": "^2.8.1",
     "exceljs": "^4.4.0",

+ 1 - 0
src/components/VDriverGuide/index.ts

@@ -0,0 +1 @@
+export { default } from './src/VDriverGuide.vue'

+ 20 - 0
src/components/VDriverGuide/src/VDriverGuide.vue

@@ -0,0 +1,20 @@
+<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
+    </span>
+  </el-button>
+</template>
+
+<style lang="scss" scoped></style>

+ 1 - 0
src/components/VTipTooltip/index.ts

@@ -0,0 +1 @@
+export { default } from './src/VTipTooltip.vue'

+ 29 - 0
src/components/VTipTooltip/src/VTipTooltip.vue

@@ -0,0 +1,29 @@
+<script setup lang="ts">
+const props = withDefaults(
+  defineProps<{
+    content?: string
+    placement?: 'top' | 'bottom' | 'left' | 'right'
+    img?: string
+  }>(),
+  {
+    content: '',
+    placement: 'bottom'
+  }
+)
+
+const visible = ref(false)
+</script>
+
+<template>
+  <div style="display: inline-block">
+    <el-tooltip v-model="visible" :placement="props.placement">
+      <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="" />
+      </template>
+    </el-tooltip>
+  </div>
+</template>
+
+<style lang="scss" scoped></style>

+ 3 - 1
src/main.ts

@@ -14,6 +14,8 @@ import VXETablePluginExportXLSX from 'vxe-table-plugin-export-xlsx'
 import ExcelJS from 'exceljs'
 import { VLoading } from './directive/VLoading'
 
+import 'driver.js/dist/driver.css' // 导入样式
+
 import { createApp } from 'vue'
 import { createPinia } from 'pinia'
 
@@ -87,4 +89,4 @@ app.config.globalProperties.$toggleDarkMode = () => {
   }
 }
 
-app.mount('#app')
+app.mount('#app')

+ 96 - 0
src/styles/driver.scss

@@ -0,0 +1,96 @@
+div.driver-popover {
+  width: 400px;
+  max-width: 400px;
+  padding: 15px;
+  padding-bottom: 8px;
+  background-color: var(--color-tour-popover-bg);
+}
+// 标题
+header.driver-popover-title {
+  color: var(--color-neutral-1);
+}
+
+// 角标
+div.driver-popover-arrow-side-left {
+  border-left-color: var(--color-tour-popover-bg);
+}
+div.driver-popover-arrow-side-right {
+  border-right-color: var(--color-tour-popover-bg);
+}
+div.driver-popover-arrow-side-top {
+  border-top-color: var(--color-tour-popover-bg);
+}
+div.driver-popover-arrow-side-bottom {
+  border-bottom-color: var(--color-tour-popover-bg);
+}
+
+// 遮罩
+.driver-overlay.driver-overlay-animated {
+  path {
+    fill: var(--color-tour-mask-bg) !important;
+  }
+}
+// 关闭图标
+button.driver-popover-close-btn {
+  top: 9px;
+  right: 9px;
+  color: var(--color-neutral-1);
+  &:hover,
+  &:focus {
+    color: var(--color-neutral-1);
+  }
+}
+// 内容
+div.driver-popover-description {
+  color: var(--color-neutral-1);
+  text-align: left;
+}
+
+// 页数
+span.driver-popover-progress-text {
+  color: var(--color-tour-step-color);
+  font-size: 12px;
+}
+.driver-popover-footer {
+  .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);
+        }
+      }
+    }
+    button.driver-popover-next-btn {
+      width: 96px;
+      height: 32px;
+      background-color: var(--color-tour-next-btn-bg);
+      fill: var(--color-tour-next-btn-color);
+      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;
+        }
+      }
+    }
+  }
+}

+ 20 - 4
src/styles/icons/iconfont.css

@@ -1,9 +1,9 @@
 @font-face {
   font-family: "font_family"; /* Project id 4672385 */
-  src: url('iconfont.woff2?t=1745286986564') format('woff2'),
-       url('iconfont.woff?t=1745286986564') format('woff'),
-       url('iconfont.ttf?t=1745286986564') format('truetype'),
-       url('iconfont.svg?t=1745286986564#font_family') format('svg');
+  src: url('iconfont.woff2?t=1750149505564') format('woff2'),
+       url('iconfont.woff?t=1750149505564') format('woff'),
+       url('iconfont.ttf?t=1750149505564') format('truetype'),
+       url('iconfont.svg?t=1750149505564#font_family') format('svg');
 }
 
 .font_family {
@@ -14,6 +14,22 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-icon_guidelines_b:before {
+  content: "\e719";
+}
+
+.icon-icon_cancelled_b:before {
+  content: "\e71a";
+}
+
+.icon-icon_configurations_b:before {
+  content: "\e718";
+}
+
+.icon-a-1:before {
+  content: "\e717";
+}
+
 .icon-icon_good_b:before {
   content: "\e713";
 }

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/styles/icons/iconfont.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 6 - 0
src/styles/icons/iconfont.svg


BIN
src/styles/icons/iconfont.ttf


BIN
src/styles/icons/iconfont.woff


BIN
src/styles/icons/iconfont.woff2


+ 1 - 0
src/styles/index.scss

@@ -4,6 +4,7 @@
 @import './theme.scss';
 @import './Antdui.scss';
 @import './icons/iconfont.css';
+@import './driver.scss';
 
 #app {
   font-size: var(--font-size-3);

+ 16 - 0
src/styles/theme.scss

@@ -317,6 +317,13 @@
   --color-prompt-diaolog-bg: #f8f9fd;
   --color-prompt-disabled-bg: #f4f4f4;
   --color-prompt-disabled-border: rgba(234, 235, 237, 0.3);
+  --color-tour-popover-bg: #fff;
+  --color-tour-prev-btn-border: #eaebed;
+  --color-tour-next-btn-bg: #2b2f36;
+  --color-tour-next-btn-hover-bg: #2b2f36;
+  --color-tour-next-btn-color: #fff;
+  --color-tour-step-color: #b5b9bf;
+  --color-tour-mask-bg: rgba(43, 47, 54, 0.7);
 }
 
 :root.dark {
@@ -326,6 +333,7 @@
   --color-neutral-1: #f0f1f3;
   --color-neutral-2: rgba(240, 241, 243, 0.7);
   --color-neutral-3: rgba(240, 241, 243, 0.3);
+
   --color-border: #3f434a;
   --color-header-bg: #30353c;
 
@@ -510,4 +518,12 @@
       border-color: #3f434a;
     }
   }
+
+  --color-tour-popover-bg: #cf5f00;
+  --color-tour-prev-btn-border: #e6c5aa;
+  --color-tour-next-btn-bg: #f0f1f3;
+  --color-tour-next-btn-hover-bg: #e3e5e8;
+  --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);
 }

+ 40 - 0
src/utils/driverGuide.ts

@@ -0,0 +1,40 @@
+// 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
+
+/**
+ * useDriver composable
+ * @param steps 引导步骤数组
+ * @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 })
+      }
+    })
+  }
+
+  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 // 暴露实例给你更多操作
+  }
+}

+ 9 - 3
src/views/AIApiLog/src/components/LogDialog.vue

@@ -10,9 +10,8 @@ const responseHeight = ref(580)
 const openDialog = (request, response) => {
   dialogVisible.value = true
   requestContent.value = request
-  responseContent.value = response.choices[0].message.content
-  // requestList.value = JSON.stringify(request).split('\n')
-  // responseList.value = JSON.stringify(response).split('\n')
+  responseContent.value =
+    response.choices?.[0]?.message?.content || response.content?.[0]?.text || response
   nextTick(() => {
     if (requestContentRef.value) {
       const height = requestContentRef.value.scrollHeight
@@ -20,6 +19,12 @@ const openDialog = (request, response) => {
     }
   })
 }
+
+const clearData = () => {
+  requestContent.value = ''
+  responseContent.value = ''
+  responseHeight.value = 580
+}
 defineExpose({
   openDialog
 })
@@ -29,6 +34,7 @@ defineExpose({
   <el-dialog
     v-model="dialogVisible"
     class="ai-api-log-dialog"
+    @closed="clearData"
     title="AI API Log"
     width="1000"
     top="10vh"

+ 5 - 1
src/views/Dashboard/src/DashboardView.vue

@@ -889,7 +889,10 @@ const ClickParams = (val: any) => {
             <VBox_Dashboard @changeCancel="changeCancel(item.id)">
               <template #header>
                 <div class="Title_flex">
-                  {{ item.title }}
+                  <span>
+                    {{ item.title }}
+                    <!-- <VTipTooltip></VTipTooltip> -->
+                  </span>
                   <DashFilters
                     :defaultData="KPIDefaulteData"
                     @FilterSearch="GetKpiData"
@@ -941,6 +944,7 @@ const ClickParams = (val: any) => {
                   <DashFilters
                     :defaultData="PendingDefaulteData"
                     :radioisDisabled="true"
+                    :img="'./image/kpi-chart-tip.png'"
                     @FilterSearch="GetPendingEcharts"
                   ></DashFilters>
                 </div>

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


+ 11 - 1
src/views/Tracking/src/TrackingView.vue

@@ -8,7 +8,13 @@ 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)
@@ -637,7 +643,11 @@ const SearchInput = () => {
 </script>
 
 <template>
-  <div class="Title">Tracking</div>
+  <div class="Title">
+    <span>Tracking</span>
+    <!-- <VDriverGuide @click="handleGuide"></VDriverGuide> -->
+  </div>
+
   <div class="display" ref="filterRef">
     <FilterTags :TagsListItem="TagsList" @changeTag="changeTag"></FilterTags>
     <div class="heaer_top">

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

@@ -17,6 +17,149 @@ 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() // 开始引导
+}
 
 const route = useRoute()
 
@@ -162,7 +305,8 @@ const SubscribeShipments = () => {
         <VTag large :type="allData?.transportInfo?.status">{{
           allData?.transportInfo?.status
         }}</VTag>
-        <div class="right-operation">
+        <VDriverGuide @click="handleGuide"></VDriverGuide>
+        <div class="right-operation" id="driver-step-tracking-detail-1">
           <el-button
             v-if="
               (allData?.canViewAMSLog || allData?.canViewISFLog) &&
@@ -336,7 +480,7 @@ const SubscribeShipments = () => {
           </div>
 
           <!-- Attachment -->
-          <div v-if="item.id === 5">
+          <div v-if="item.id === 5" id="driver-step-tracking-detail-5">
             <VBox :id="item.id" :isSeeAll="false" @draggable="handleDraggable">
               <template #header>Attachment</template>
               <template #content>
@@ -346,7 +490,12 @@ const SubscribeShipments = () => {
           </div>
         </template>
       </VueDraggable>
-      <EmailDrawer @sendEmailSuccess="getData" :data="allData" ref="emailDrawerRef"></EmailDrawer>
+      <EmailDrawer
+        id="driver-step-tracking-detail-6"
+        @sendEmailSuccess="getData"
+        :data="allData"
+        ref="emailDrawerRef"
+      ></EmailDrawer>
     </div>
     <AMSISFDrawer ref="AMSISFDrawerRef"></AMSISFDrawer>
     <ShareLinkDialog

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

@@ -20,6 +20,7 @@ const handleTabClick = (name: string) => {
             activeName === 'shipmentStatus' && props.data?.transportInfo?.mode !== 'Air Freight',
           'only-one-mode': props.data?.transportInfo?.mode === 'Air Freight'
         }"
+        id="driver-step-tracking-detail-2"
         @click="handleTabClick('shipmentStatus')"
       >
         Shipment Status
@@ -27,6 +28,7 @@ const handleTabClick = (name: string) => {
       <div
         v-if="props.data?.transportInfo?.mode !== 'Air Freight'"
         class="tab"
+        id="driver-step-tracking-detail-3"
         :class="{ 'is-active': activeName === 'containerStatus' }"
         @click="handleTabClick('containerStatus')"
       >

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov