소스 검색

Merge branch 'dev_zyh' of United_Software/k_online_ui into dev

Jack Zhou 1 년 전
부모
커밋
164b12a26e
22개의 변경된 파일225개의 추가작업 그리고 209개의 파일을 삭제
  1. 1 1
      index.html
  2. 14 0
      src/api/module/common.ts
  3. 2 1
      src/auto-imports.d.ts
  4. 1 1
      src/components/VTag/src/VTag.vue
  5. 2 2
      src/styles/elementui.scss
  6. 15 15
      src/styles/theme.scss
  7. 0 3
      src/views/Booking/src/components/BookingTable/src/BookingTable.vue
  8. 51 60
      src/views/Layout/src/components/Menu/MenuView.vue
  9. 21 13
      src/views/Login/src/loginView.vue
  10. 0 3
      src/views/Tracking/src/TrackingView.vue
  11. 2 3
      src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue
  12. 5 5
      src/views/Tracking/src/components/PublicTracking/src/components/BasicInformation.vue
  13. 11 5
      src/views/Tracking/src/components/PublicTracking/src/components/MilestonesTable.vue
  14. 2 2
      src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue
  15. 5 1
      src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue
  16. 2 2
      src/views/Tracking/src/components/TrackingDetail/src/components/AMS&ISF.vue
  17. 9 26
      src/views/Tracking/src/components/TrackingDetail/src/components/AttachmentView.vue
  18. 9 9
      src/views/Tracking/src/components/TrackingDetail/src/components/BasicInformation.vue
  19. 12 13
      src/views/Tracking/src/components/TrackingDetail/src/components/MapView.vue
  20. 15 40
      src/views/Tracking/src/components/TrackingDetail/src/components/MilestonesTable.vue
  21. 45 3
      src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue
  22. 1 1
      src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

+ 1 - 1
index.html

@@ -7,7 +7,7 @@
     <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
     <meta http-equiv="Pragma" content="no-cache" />
     <meta http-equiv="Expires" content="0" />
-    <title>online</title>
+    <title>Apex Online Tracking</title>
   </head>
   <body>
     <div id="app"></div>

+ 14 - 0
src/api/module/common.ts

@@ -40,6 +40,20 @@ export const changePwdByLogin = (params: any, config: any) => {
   )
 }
 
+/**
+ * 获取菜单列表
+ */
+export const getMenuList = (params: any, config: any) => {
+  return HttpAxios.get(
+    `${baseUrl}`,
+    {
+      action: 'main',
+      ...params
+    },
+    config
+  )
+}
+
 /**
  * 获取日志列
  */

+ 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')
 }

+ 1 - 1
src/components/VTag/src/VTag.vue

@@ -38,7 +38,7 @@ defineProps<internalProps>()
   justify-content: center;
   cursor: default;
   align-items: center;
-  padding: 4px 8px;
+  padding: 5px 8px;
   height: 22px;
   line-height: 28px;
   font-size: 12px;

+ 2 - 2
src/styles/elementui.scss

@@ -378,7 +378,7 @@ span.el-checkbox__input.is-checked + .el-checkbox__label {
   color: var(--color-neutral-2);
 }
 /* 修改选中时打勾图标的大小 */
-span.el-checkbox__inner::after {
+div .el-checkbox.el-checkbox--large span.el-checkbox__inner::after {
   left: 4px; /* 调整左边距 */
   top: 0px; /* 调整上边距 */
   width: 5px; /* 打勾图标宽度 */
@@ -646,4 +646,4 @@ div .el-radio-button__inner:hover {
 div .el-space {
   flex-wrap: wrap;
   margin: 3px 0 0 0;
-}
+}

+ 15 - 15
src/styles/theme.scss

@@ -5,14 +5,14 @@
   --color-neutral-1: #2b2f36;
   --color-neutral-2: #646a73;
   --color-neutral-3: #b5b9bf;
-  --color-neutral-4: #F5F7FA;
-  --color-neutral-5: #BFC1C3;
+  --color-neutral-4: #f5f7fa;
+  --color-neutral-5: #bfc1c3;
 
   --color-white: #fff;
   --color-success: #00a870;
   --color-warning: #edb82f;
   --color-danger: #c9353f;
-  --color-grey: #F4F5F5;
+  --color-grey: #f4f5f5;
 
   --color-accent-1: #2b2f36;
   --color-accent-2: #ed6d00;
@@ -45,7 +45,7 @@
   --color-orange-3: #fb8e00;
   --color-orange-4: #ffb74d;
   --color-orange-5: #ffd393;
-  --color-orange-6: #FDF0E6;
+  --color-orange-6: #fdf0e6;
 
   --color-tag-confirmed: #5bb462;
   --color-tag-cancelled: #243041;
@@ -60,7 +60,7 @@
   --color-tag-confirmed-bg: #e8fbe4;
   --color-tag-cancelled-bg: #ebeef1;
   --color-tag-created-bg: #e5f0fb;
-  --color-tag-booked-bg: #d9d9d9;
+  --color-tag-booked-bg: #f3e6fa;
   --color-tag-all-bg: #ffe5cf;
   --color-tag-cargo-received-bg: #eaecff;
   --color-tag-departure-bg: #ecf7ff;
@@ -68,8 +68,8 @@
   --color-tag-completed-bg: #e8fbe4;
 
   --color-border: #eaebed;
-  --color-border-1: #E8EAEE;
-  --color-border-2: #EAEBED;
+  --color-border-1: #e8eaee;
+  --color-border-2: #eaebed;
 
   --color-mune-active-bg: #fdf5f1;
 
@@ -100,7 +100,7 @@
   --color-tag-checked-cancelled: #ebeef1;
   --color-tag-checked-confirmed: #e8fbe4;
 
-  --color-border-top: #EFF0F1;
+  --color-border-top: #eff0f1;
 
   --color-header-bg: #f6f8fa;
 
@@ -122,13 +122,13 @@
   --border-hover-color: #fff1e6;
   --border-cancel-hover-color: #f4f5f5;
 
-  --icon-color-black:#202020;
-  --more-filters-background-color:#F9F9F9;
-  --addparties-background-color:#EFF1F5;
+  --icon-color-black: #202020;
+  --more-filters-background-color: #f9f9f9;
+  --addparties-background-color: #eff1f5;
 
-  --badge__content--warning:#ED6D00;
-  --tag-info-bg-color: #F2F2F2;
-  --tag-info-text-color: #2B2F36;
+  --badge__content--warning: #ed6d00;
+  --tag-info-bg-color: #f2f2f2;
+  --tag-info-text-color: #2b2f36;
 
-  --dashboard-text-color:#646A73;
+  --dashboard-text-color: #646a73;
 }

+ 0 - 3
src/views/Booking/src/components/BookingTable/src/BookingTable.vue

@@ -52,9 +52,6 @@ const handleColumns = (columns: any, status?: string) => {
     if (item.formatter === 'date') {
       curColumn = {
         ...curColumn,
-        sortBy: ({ row, column }: any) => {
-          return dayjs(row[column.field]).unix()
-        },
         formatter: ({ cellValue }: any) =>
           cellValue ? dayjs(cellValue).format('MMM-YYYY-DD ') : '--'
       }

+ 51 - 60
src/views/Layout/src/components/Menu/MenuView.vue

@@ -1,71 +1,62 @@
 <script setup lang="ts">
 import { useRoute, useRouter } from 'vue-router'
+import { useUserStore } from '@/stores/modules/user'
 
 const route = useRoute()
 const router = useRouter()
+const userStore = useUserStore()
 
 const isCollapse = defineModel<boolean>()
-const menuList = [
-  {
-    index: '1',
-    label: 'Dashboard',
-    icon: 'icon_data_fill_b',
-    path: '/dashboard'
-  },
-  // {
-  //   index: '2',
-  //   label: 'Quote',
-  //   icon: 'icon_quote__fill_b',
-  //   path: '/booking/detail'
-  // },
-  {
-    index: '3',
-    label: 'Booking',
-    icon: 'icon_booking__fill_b',
-    path: '/booking'
-  },
-  {
-    index: '4',
-    label: 'Tracking',
-    icon: 'icon_tracking__fill_b',
-    path: '/tracking'
-  },
-  // {
-  //   index: '5',
-  //   label: 'Report',
-  //   icon: 'icon_report__fill_b',
-  //   path: '/tracking/detail'
-  // },
-  {
-    index: '6',
-    label: 'System Management',
-    icon: 'icon_system__management_fill_b',
-    type: 'list',
-    children: [
-      // {
-      //   index: '5-1',
-      //   label: 'Account Management',
-      //   path: '/public-tracking'
-      // },
-      // {
-      //   index: '5-2',
-      //   label: 'Permission Management',
-      //   path: '/public-tracking/detail'
-      // },
-      // {
-      //   index: '5-3',
-      //   label: 'System Configuration',
-      //   path: '/login'
-      // },
-      {
-        index: '5-4',
-        label: 'Operation Log',
-        path: '/Operationlog'
-      }
-    ]
-  }
-]
+// [
+//   {
+//     index: '1',
+//     label: 'Dashboard',
+//     icon: 'icon_data_fill_b',
+//     path: '/dashboard'
+//   },
+//   {
+//     index: '3',
+//     label: 'Booking',
+//     icon: 'icon_booking__fill_b',
+//     path: '/booking'
+//   },
+//   {
+//     index: '4',
+//     label: 'Tracking',
+//     icon: 'icon_tracking__fill_b',
+//     path: '/tracking'
+//   },
 
+//   {
+//     index: '6',
+//     label: 'System Management',
+//     icon: 'icon_system__management_fill_b',
+//     type: 'list',
+//     children: [
+//       {
+//         index: '5-4',
+//         label: 'Operation Log',
+//         path: '/Operationlog'
+//       }
+//     ]
+//   }
+// ]
+const menuList = ref()
+watch(
+  () => userStore.username,
+  () => {
+    getMenuList()
+  }
+)
+const getMenuList = () => {
+  $api.getMenuList().then((res) => {
+    if (res.code === 200) {
+      menuList.value = res.data
+    }
+  })
+  // return menuList
+}
+getMenuList()
 //监听窗口大小
 const handler = () => {
   return (() => {

+ 21 - 13
src/views/Login/src/loginView.vue

@@ -68,6 +68,7 @@ watch(status, () => {
     email: false,
     code: false
   }
+  isRememerPwd.value = false
   verificationCode.value = ''
   getCode()
 })
@@ -166,10 +167,7 @@ const handleLogin = () => {
           loginError.value.code = true
         } else if (data.msg === 'error_times') {
           errorTipsRef.value.openDialog()
-        }
-      } else if (res.code === 500) {
-        const { data } = res
-        if (data.msg === 'passwordExpires') {
+        } else if (data.msg === 'passwordExpires') {
           ElMessageBox.alert('Password expired, please change your password', 'Prompt', {
             confirmButtonText: 'OK',
             type: 'warning',
@@ -179,12 +177,6 @@ const handleLogin = () => {
           router.push({
             name: 'Reset Password'
           })
-        } else {
-          ElMessageBox.alert(data.desc, {
-            confirmButtonText: 'OK',
-            type: 'warning',
-            confirmButtonClass: 'el-button--dark'
-          })
         }
       }
     })
@@ -193,6 +185,21 @@ const handleLogin = () => {
     })
 }
 
+// 从忘记密码返回登录
+const backLogin = (emailTips: boolean) => {
+  status.value = 'login'
+  isEmailTips.value = emailTips
+  // 如果是成功忘记密码,清空保存的账号密码
+  // 如果是直接返回登录,获取保存的账号密码
+  setTimeout(() => {
+    if (emailTips) {
+      clearCredentials()
+    } else {
+      getCredentials()
+    }
+  }, 0)
+}
+
 const isUserNameExit = ref(false)
 
 const handleForgot = () => {
@@ -205,11 +212,12 @@ const handleSendPassword = () => {
   $api
     .forgotPassword({
       login: loginForm.value.username,
-      email: loginForm.value.email
+      email: loginForm.value.email,
+      verifcation_code: loginForm.value.code
     })
     .then((res: any) => {
       if (res.code === 200) {
-        isEmailTips.value = true
+        backLogin(true)
       }
     })
 }
@@ -375,7 +383,7 @@ const errorTipsRef = ref()
         <el-button @click="handleSendPassword" class="el-button--dark login-btn"
           >Send Password</el-button
         >
-        <div @click="status = 'login'" class="back-text">
+        <div @click="backLogin(false)" class="back-text">
           <span class="font_family icon-icon_back_b"></span>
           <span class="text"> Back to login</span>
         </div>

+ 0 - 3
src/views/Tracking/src/TrackingView.vue

@@ -7,9 +7,6 @@ import DateRange from '@/components/DateRange'
 import MoreFilters from '@/components/MoreFilters'
 import { ref, reactive, onMounted } from 'vue'
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
-import { useRouter } from 'vue-router'
-
-const router = useRouter()
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
 

+ 2 - 3
src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue

@@ -15,7 +15,7 @@ const headerSearchdData = computed(() => headerSearch.searchValue)
 // 监听 sharedData 的变化并更新 inputValue
 headerSearchdData.value && (inputVModel.value = headerSearchdData.value)
 searchResult.value = headerSearch.searchResult
-
+headerSearch.clearSearchData()
 // 当 sharedData 发生变化时,更新 inputValue
 watch(
   () => headerSearchdData.value,
@@ -23,7 +23,7 @@ watch(
     // if (newData) {
     inputVModel.value = headerSearchdData.value
     searchResult.value = headerSearch.searchResult
-    // headerSearch.clearSearchData()
+    headerSearch.clearSearchData()
     // }
   }
 )
@@ -52,7 +52,6 @@ const handleSearchNo = () => {
     .finally(() => {
       loading.value = false
     })
-  // router.push(`/public-tracking/detail?searchNo=${inputVModel.value}`)
 }
 </script>
 

+ 5 - 5
src/views/Tracking/src/components/PublicTracking/src/components/BasicInformation.vue

@@ -27,13 +27,13 @@ const allData: any = ref({
   businessPartners: [
     {
       title: 'Origin Agent',
-      conpany: 'Dynarex Corp.',
+      company: 'Dynarex Corp.',
       address: '1975 LINDEN BLVD 3RD FL SUITE # 300 ELMONT',
       phone: '+1 212 5551234'
     },
     {
       title: 'Destination Agent',
-      conpany: 'Dynarex Corp.',
+      company: 'Dynarex Corp.',
       address: '1975 LINDEN BLVD 3RD FL SUITE # 300 ELMONT',
       phone: '+1 212 5551234'
     }
@@ -93,13 +93,13 @@ const convertData = (data: any) => {
     businessPartners: [
       {
         title: 'Origin Agent',
-        conpany: data.businessPartners.origin.company,
+        company: data.businessPartners.origin.company,
         address: data.businessPartners.origin.address,
         phone: data.businessPartners.origin.phone
       },
       {
         title: 'Destination Agent',
-        conpany: data.businessPartners.destination.company,
+        company: data.businessPartners.destination.company,
         address: data.businessPartners.destination.address,
         phone: data.businessPartners.destination.phone
       }
@@ -155,7 +155,7 @@ watch(
             <div class="info">
               <span class="font_family icon-icon_company_b"></span>
               <span style="color: var(--color-neutral-1); font-weight: 600">{{
-                item.conpany
+                item.company
               }}</span>
             </div>
             <div class="info">

+ 11 - 5
src/views/Tracking/src/components/PublicTracking/src/components/MilestonesTable.vue

@@ -9,13 +9,16 @@ import { useRowClickStyle } from '@/hooks/rowClickStyle'
 dayjs.extend(utc)
 dayjs.extend(timezone)
 const props = defineProps({
+  height: {
+    type: Number,
+    default: 240
+  },
   data: Object
 })
 
 const tableData = ref<VxeGridProps<any>>({
   border: true,
   minHeight: 70,
-  maxHeight: 440,
   columns: [],
   data: [],
   scrollY: { enabled: true, oSize: 20, gt: 30 },
@@ -38,13 +41,16 @@ const handleColumns = (columns: any) => {
     }
 
     // 格式化
-    if (item.type === 'dateTime') {
+    if (item.formatter === 'dateTime') {
       curColumn = {
         ...curColumn,
         formatter: ({ cellValue, row }: any) => {
-          return cellValue
-            ? dayjs(cellValue).tz(row.timezone).format('MMM-DD-YYYY hh:mm A (z)')
-            : '--'
+          if (!cellValue) return '--'
+          const formattedTime = dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
+          const timeZoneOffset = dayjs().tz(row.timezone).format('Z')
+          // 替换 "+07:00" 为 "GMT+7"
+          const gmtOffset = `GMT${timeZoneOffset.slice(0, 3)}`
+          return `${formattedTime} (${gmtOffset})`
         }
       }
     }

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

@@ -116,7 +116,7 @@ const formatTime = (time: string) => {
       </div>
     </div>
     <div class="info-content">
-      <VBox :is-see-all="false" :is-draggable="false">
+      <VBox :is-see-all="false" :is-draggable="false" ref="basicInformationRef">
         <template #header>
           <div style="display: flex; border-radius: 12px 12px 0 0">
             <div class="basic-information-header">
@@ -126,7 +126,7 @@ const formatTime = (time: string) => {
           </div>
         </template>
         <template #content>
-          <BasicInformation :data="allData" ref="basicInformationRef"></BasicInformation>
+          <BasicInformation :data="allData"></BasicInformation>
         </template>
       </VBox>
       <!-- Milestones -->

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

@@ -122,7 +122,11 @@ const formatTime = (time: string) => {
         <div class="no">Tracking No. {{ allData?.transportInfo?.['Tracking No.'] }}</div>
         <VTag large type="Confirmed">{{ allData?.transportInfo?.status }}</VTag>
         <div class="right-operation">
-          <el-button type="default" @click="handleAMSISF">
+          <el-button
+            v-if="allData?.canViewAMSLog || allData?.canViewISFLog"
+            type="default"
+            @click="handleAMSISF"
+          >
             <span class="font_family icon-icon_log_b" style="margin-right: 4px"></span
             >AMS/ISF</el-button
           >

+ 2 - 2
src/views/Tracking/src/components/TrackingDetail/src/components/AMS&ISF.vue

@@ -6,6 +6,8 @@ const drawer = ref(false)
 const openDrawer = (data: any) => {
   getData(data)
   drawer.value = true
+  canViewAMSLog.value = data.canViewISFLog
+  canViewISFLog.value = data.canViewAMSLog
 }
 
 const canViewAMSLog = ref(false)
@@ -41,8 +43,6 @@ const getData = (pageData: any) => {
         // 获取数据
         const data = res.data
         const { amsLog, isfLog } = res.data
-        canViewAMSLog.value = data.canViewAMSLog
-        canViewISFLog.value = data.canViewISFLog
         AMSTableData.value.columns = handleColumns(amsLog.amsLog_column)
         AMSTableData.value.data = amsLog.data
         ISFTableData.value.columns = handleColumns(isfLog.isfLog_column)

+ 9 - 26
src/views/Tracking/src/components/TrackingDetail/src/components/AttachmentView.vue

@@ -13,28 +13,8 @@ const tableData = ref<VxeGridProps<any>>({
   maxHeight: 440,
   border: true,
   round: true,
-  columns: [
-    {
-      field: 'fileType',
-      title: 'File Type',
-      minWidth: 120
-    },
-    {
-      field: 'fileName',
-      title: 'File',
-      minWidth: 80
-    },
-    {
-      title: 'Action',
-      width: 90,
-      slots: { default: 'action' }
-    }
-  ],
-  data: [
-    {
-      description: 'Description'
-    }
-  ],
+  columns: [],
+  data: [],
   scrollY: { enabled: true, oSize: 20, gt: 30 },
   stripe: true,
   emptyText: ' ',
@@ -96,7 +76,12 @@ onMounted(() => {
   tableRef.value && autoWidth(tableData.value, tableRef.value)
 })
 const handleDownload = (row: any) => {
-  const url = row.file?.url
+  // 如果from_system的值是TOPOCEAN_KSMART,不需要拼接url
+  const url =
+    row.from_system === 'TOPOCEAN_KSMART'
+      ? row.file?.url
+      : import.meta.env.VITE_API_HOST + '/' + row.file?.url
+  // const url = row.file?.url
   // 创建一个隐藏的 <a> 标签
   const link = document.createElement('a')
   link.href = url
@@ -109,9 +94,7 @@ const handleDownload = (row: any) => {
   link.click()
   document.body.removeChild(link)
 }
-const handleDelete = (row: any) => {
-  console.log('Delete', row)
-}
+const handleDelete = (row: any) => {}
 const uploadFilesRef = ref<InstanceType<typeof UploadFilesDialog> | null>(null)
 
 const openUploadFilesDialog = () => {

+ 9 - 9
src/views/Tracking/src/components/TrackingDetail/src/components/BasicInformation.vue

@@ -55,25 +55,25 @@ const allData: any = ref({
   businessPartners: [
     {
       title: 'Shipper',
-      conpany: '',
+      company: '',
       address: '',
       phone: ''
     },
     {
       title: 'Consignee',
-      conpany: '',
+      company: '',
       address: '',
       phone: ''
     },
     {
       title: 'Origin Agent',
-      conpany: '',
+      company: '',
       address: '',
       phone: ''
     },
     {
       title: 'Destination Agent',
-      conpany: '',
+      company: '',
       address: '',
       phone: ''
     }
@@ -155,25 +155,25 @@ const convertData = (data: any) => {
     businessPartners: [
       {
         title: 'Shipper',
-        conpany: data.businessPartners.shipper.company,
+        company: data.businessPartners.shipper.company,
         address: data.businessPartners.shipper.address,
         phone: data.businessPartners.shipper.phone
       },
       {
         title: 'Consignee',
-        conpany: data.businessPartners.consignee.company,
+        company: data.businessPartners.consignee.company,
         address: data.businessPartners.consignee.address,
         phone: data.businessPartners.consignee.phone
       },
       {
         title: 'Origin Agent',
-        conpany: data.businessPartners.origin.company,
+        company: data.businessPartners.origin.company,
         address: data.businessPartners.origin.address,
         phone: data.businessPartners.origin.phone
       },
       {
         title: 'Destination Agent',
-        conpany: data.businessPartners.destination.company,
+        company: data.businessPartners.destination.company,
         address: data.businessPartners.destination.address,
         phone: data.businessPartners.destination.phone
       }
@@ -349,7 +349,7 @@ defineExpose({
             <div class="info">
               <span class="font_family icon-icon_company_b"></span>
               <span style="color: var(--color-neutral-1); font-weight: 600">{{
-                item.conpany
+                item.company
               }}</span>
             </div>
             <div class="info">

+ 12 - 13
src/views/Tracking/src/components/TrackingDetail/src/components/MapView.vue

@@ -9,8 +9,8 @@ import TransferIcon from '../images/transferIcon.png'
 import { onMounted, ref, watch } from 'vue'
 
 const props = defineProps<{
-  serial_no: string
-  uncode: string
+  serial_no?: string
+  uncode?: string
 }>()
 
 const markerPositions = ref([])
@@ -116,17 +116,15 @@ const addMarkersToMap = () => {
 
   if (latLngBounds.length > 0) {
     const bounds = L.latLngBounds(latLngBounds)
-    map!.fitBounds(bounds, { paddingTopLeft: [20, 20], paddingBottomRight: [400, 40] })
-
-    // 首次添加标记时保存中心和缩放级别
-    if (isFirstRender) {
-      initialCenter = bounds.getCenter() // 保存中心
-      initialZoomLevel = map!.getZoom() // 保存缩放比例
-      isFirstRender = false
-
-      // 接口请求成功并首次添加标记后,动态添加重置按钮
-      addResetZoomButton(initialCenter, initialZoomLevel)
-    }
+    map!.fitBounds(bounds, { paddingTopLeft: [20, 70], paddingBottomRight: [400, 0] })
+    setTimeout(() => {
+      if (isFirstRender) {
+        initialCenter = map!.getCenter()
+        initialZoomLevel = map!.getZoom()
+        isFirstRender = false
+      }
+      addResetZoomButton(initialCenter!, initialZoomLevel!)
+    }, 0)
   }
 }
 
@@ -140,6 +138,7 @@ const getMarker = () => {
     .then((res) => {
       if (res.code === 200) {
         const { data } = res
+
         data &&
           data.forEach((item) => {
             const iconColorList = {

+ 15 - 40
src/views/Tracking/src/components/TrackingDetail/src/components/MilestonesTable.vue

@@ -1,5 +1,11 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
+import timezone from 'dayjs/plugin/timezone'
+import utc from 'dayjs/plugin/utc'
+
+dayjs.extend(utc)
+dayjs.extend(timezone)
+
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { autoWidth } from '@/utils/table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
@@ -13,42 +19,8 @@ const tableData = ref<VxeGridProps<any>>({
   round: true,
   minHeight: 70,
   maxHeight: 500,
-  columns: [
-    {
-      field: 'milestones',
-      title: 'Milestones',
-      minWidth: 120
-    },
-    {
-      field: 'dateTime',
-      title: 'Date Time',
-      minWidth: 80
-    },
-    {
-      field: 'locations',
-      title: 'Locations',
-      minWidth: 80
-    },
-    {
-      field: 'remarks',
-      title: 'Remarks',
-      minWidth: 80
-    }
-  ],
-  data: [
-    {
-      milestones: 'Milestone 1',
-      dateTime: 'Jun-08-2024 12:00 AM',
-      locations: 'Shenzhen',
-      remarks: 'Remarks 1'
-    },
-    {
-      milestones: 'Milestone 2',
-      dateTime: 'Jun-10-2024 12:00 AM',
-      locations: 'Valencia',
-      remarks: 'Remarks 2'
-    }
-  ],
+  columns: [],
+  data: [],
   scrollY: { enabled: true, oSize: 20, gt: 30 },
   stripe: true,
   emptyText: ' ',
@@ -74,10 +46,13 @@ const handleColumns = (columns: any) => {
     if (item.formatter === 'dateTime') {
       curColumn = {
         ...curColumn,
-        formatter: ({ cellValue, row }: any) => {
-          return cellValue
-            ? dayjs(cellValue).tz(row.timezone).format('MMM-DD-YYYY hh:mm A (z)')
-            : '--'
+        formatter: ({ row, cellValue }: any) => {
+          if (!cellValue) return '--'
+          const formattedTime = dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
+          const timeZoneOffset = dayjs().tz(row.timezone).format('Z')
+          // 替换 "+07:00" 为 "GMT+7"
+          const gmtOffset = `GMT${timeZoneOffset.slice(0, 3)}`
+          return `${formattedTime} (${gmtOffset})`
         }
       }
     }

+ 45 - 3
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -50,9 +50,6 @@ const handleColumns = (columns: any, status?: string) => {
     if (item.formatter === 'date') {
       curColumn = {
         ...curColumn,
-        sortBy: ({ row, column }: any) => {
-          return dayjs(row[column.field]).unix()
-        },
         formatter: ({ cellValue }: any) =>
           cellValue ? dayjs(cellValue).format('MMM-YYYY-DD ') : '--'
       }
@@ -273,7 +270,52 @@ const trackingTable = ref<any>({
   headerRowStyle: {
     backgroundColor: 'var(--color-table-header-bg)'
   },
+  sortConfig: {
+    sortMethod: (params) => {
+      const { data, sortList } = params
+      // 如果没有排序条件,直接返回原数据
+      if (sortList.length === 0) return data
+      // 对数据进行多重排序
+      const sortedData = [...data].sort((a, b) => {
+        for (const { field, order } of sortList) {
+          const curColumn = tableOriginColumnsField.value.find((item: any) => item.field === field)
+          if (!curColumn) continue
+
+          const typeName = curColumn.type
+          const aValue = a[field]
+          const bValue = b[field]
+
+          const compareResult = (aValue: any, bValue: any) => {
+            // 如果 aValue 或 bValue 是 null 或 undefined,优先处理这些值
+            if (aValue == null && bValue == null) {
+              return 0 // 如果两个值都为 null 或 undefined,视为相等
+            } else if (aValue == null) {
+              return -1 // 如果 aValue 是 null,bValue 不是,则将 aValue 视为较小值
+            } else if (bValue == null) {
+              return 1 // 如果 bValue 是 null,aValue 不是,则将 bValue 视为较小值
+            }
+
+            if (typeName === 'datetime' || typeName === 'date' || typeName === 'time') {
+              return dayjs(aValue).unix() - dayjs(bValue).unix()
+            } else if (isNaN(Number(aValue)) || isNaN(Number(bValue))) {
+              return aValue.localeCompare(bValue)
+            } else {
+              return Number(aValue) - Number(bValue)
+            }
+          }
 
+          const result = compareResult(aValue, bValue)
+          if (result !== 0) {
+            return order === 'asc' ? result : -result
+          }
+        }
+
+        return 0 // 如果所有字段都相等
+      })
+
+      return sortedData
+    }
+  },
   columnConfig: { resizable: true, useKey: true },
   rowConfig: { isHover: true },
   exportConfig: {

+ 1 - 1
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -288,7 +288,7 @@ const handleSave = () => {
   $api
     .saveVGMData({
       serial_no: allData.value.serial_no,
-      _schemas: allData.value.schemas,
+      schemas: allData.value.schemas,
       ...generalData,
       ...tableInfo
     })