Parcourir la source

feat: 接入public tracking详情页数据、接入add VGM数据

zhouyuhao il y a 1 an
Parent
commit
77356bc2ce

+ 1 - 0
package.json

@@ -21,6 +21,7 @@
     "ant-design-vue": "^4.2.3",
     "axios": "^1.7.5",
     "dayjs": "^1.11.13",
+    "dayjs-ext": "^2.2.0",
     "echarts": "^5.5.1",
     "element-plus": "^2.8.1",
     "exceljs": "^4.4.0",

+ 1 - 3
src/api/module/common.ts

@@ -33,9 +33,7 @@ export const changePwdByLogin = (params: any, config: any) => {
   return HttpAxios.post(
     `${baseUrl}`,
     {
-      action: 'ajax',
-      operate: 'save_setting_display',
-      model_name: 'Booking_Search',
+      action: 'password',
       ...params
     },
     config

+ 30 - 0
src/api/module/login.ts

@@ -90,3 +90,33 @@ export const changePassword = (params: any, config: any) => {
     config
   )
 }
+
+/**
+ * 获取public tracking detail详情数据
+ */
+export const getPublicTrackingDetail = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'login',
+      operate: 'tracking_checked',
+      ...params
+    },
+    config
+  )
+}
+
+/**
+ * reset password
+ */
+export const resetPwd = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'login',
+      operate: 'update_pwd_expires',
+      ...params
+    },
+    config
+  )
+}

+ 15 - 0
src/api/module/tracking.ts

@@ -63,3 +63,18 @@ export const getTrackingAmsIsf = (params: any, config: any) => {
     config
   )
 }
+
+/**
+ * 获取add vgm页面数据
+ */
+export const getVGMData = (params: any, config: any) => {
+  return HttpAxios.get(
+    `${baseUrl}`,
+    {
+      action: 'ocean_order',
+      operate: 'ocean_vgm',
+      ...params
+    },
+    config
+  )
+}

+ 1 - 0
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']

+ 6 - 0
src/components/VBreadcrumd/src/VBreadcrumd.vue

@@ -17,6 +17,12 @@ const path = computed(() => {
       // 获取上一段字符串
       return path.substring(lastSlashIndex + 1, detailIndex)
     }
+  } else if (path.includes('/add-vgm')) {
+    const detailIndex = path.indexOf('/add-vgm')
+    const lastSlashIndex = path.lastIndexOf('/', detailIndex - 1)
+    if (lastSlashIndex !== -1) {
+      return path.substring(lastSlashIndex + 1, detailIndex)
+    }
   }
   // 如果没有找到或者不符合条件,则返回null
   return null

+ 8 - 4
src/components/VEmpty/src/VEmpty.vue

@@ -5,11 +5,15 @@
     <div class="empty-img">
       <img src="./images/default_image.png" alt="" />
     </div>
-    <p class="title">Whoops, No matches</p>
-    <p class="light">We didn't find any search results,</p>
-    <p class="light">please try to adjust your search keywords.</p>
+    <p class="title">
+      <slot name="title">No Results Found</slot>
+    </p>
+    <slot name="result">
+      <p class="light">We didn't find any search results,</p>
+      <p class="light">please try to adjust your search keywords.</p>
+    </slot>
     <div class="suggestion">
-      <slot></slot>
+      <slot name="suggestion"></slot>
     </div>
   </div>
 </template>

+ 1 - 1
src/views/Booking/src/components/BookingTable/src/BookingTable.vue

@@ -360,7 +360,7 @@ defineExpose({
       <!-- 空数据时的插槽 -->
       <template #empty>
         <VEmpty>
-          <template #default>
+          <template #suggestion>
             <p style="color: var(--color-neutral-3)">
               We support the following references number to find booking:
             </p>

+ 23 - 6
src/views/Layout/src/components/Header/components/ChangePasswordDialog.vue

@@ -5,14 +5,29 @@ const openDialog = () => {
   dialogVisible.value = true
 }
 
-const data = ref({
+const pwdData = ref({
   oldPassword: '',
   newPassword: '',
   confirmPassword: ''
 })
 
+const handleUpdate = () => {
+  // if (pwdData.value.newPassword !== pwdData.value.confirmPassword) {
+  //   return
+  // }
+  // $api
+  //   .changePwdByLogin({
+  //     opsw: pwdData.value.oldPassword,
+  //     npsw: pwdData.value.newPassword
+  //   })
+  //   .then((res) => {
+  //     console.log(res)
+  //   })
+  ElMessage.success('Password updated successfully')
+}
+
 const clearData = () => {
-  data.value = {
+  pwdData.value = {
     oldPassword: '',
     newPassword: '',
     confirmPassword: ''
@@ -35,7 +50,7 @@ defineExpose({
     <div>
       <div class="form-item">
         <span class="label">Old Password</span>
-        <el-input v-model="data.oldPassword" placeholder="Please input Old Password">
+        <el-input v-model="pwdData.oldPassword" placeholder="Please input Old Password">
           <template #prefix>
             <span class="font_family icon-icon_password_b"></span>
           </template>
@@ -44,7 +59,7 @@ defineExpose({
       <div class="form-item">
         <span class="label">New Password</span>
         <el-input
-          v-model="data.newPassword"
+          v-model="pwdData.newPassword"
           placeholder="New password must contain both letter and numeral"
         >
           <template #prefix>
@@ -54,7 +69,7 @@ defineExpose({
       </div>
       <div class="form-item">
         <span class="label">Confirm Password</span>
-        <el-input v-model="data.confirmPassword" placeholder="Please Confirm Password">
+        <el-input v-model="pwdData.confirmPassword" placeholder="Please Confirm Password">
           <template #prefix>
             <span class="font_family icon-icon_password_b"></span>
           </template>
@@ -65,7 +80,9 @@ defineExpose({
       <el-button class="el-button--default" style="height: 40px" @click="dialogVisible = false"
         >Cancel</el-button
       >
-      <el-button class="el-button--dark" style="height: 40px">Update</el-button>
+      <el-button @click="handleUpdate" class="el-button--dark" style="height: 40px"
+        >Update</el-button
+      >
     </template>
   </el-dialog>
 </template>

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

@@ -18,10 +18,9 @@ const loginError: any = ref({
 
 const userStore = useUserStore()
 
-// 点击登录按钮
 const handleChangePwd = () => {
   $api
-    .login({
+    .resetPwd({
       uname: loginForm.value.username,
       old_password: loginForm.value.oldPassword,
       password: loginForm.value.newPassword
@@ -62,7 +61,6 @@ const handleChangePwd = () => {
 }
 
 const confirmPwd = () => {
-  console.log('test')
   if (loginForm.value.confirmPassword !== loginForm.value.newPassword) {
     loginError.value.confirmPassword = true
   } else {

BIN
src/views/Login/src/image/tips.png


+ 15 - 10
src/views/Login/src/loginView.vue

@@ -3,6 +3,7 @@ import { useRouter } from 'vue-router'
 import ErrorTips from './components/ErrorTips.vue'
 import { useUserStore } from '@/stores/modules/user'
 import ChangePasswordCard from './components/ChangePasswordCard.vue'
+import ScoringSystem from '@/views/Dashboard/src/components/ScoringSystem.vue'
 
 const router = useRouter()
 const loginForm = ref({
@@ -166,6 +167,7 @@ const test = () => {
 
 <template>
   <div class="login">
+    <ScoringSystem class="scoring-system"></ScoringSystem>
     <el-button @click="test">测试</el-button>
     <el-card class="login-card" v-if="status === 'login'">
       <div class="title">
@@ -191,7 +193,7 @@ const test = () => {
           class="user-name"
           placeholder="Please input user name"
           @focus="handleDeleteEmailTips('username')"
-          @change="handleCheckUser"
+          @blur="handleCheckUser"
         >
           <template #prefix>
             <span class="font_family icon-icon_username_b"></span>
@@ -261,6 +263,7 @@ const test = () => {
           class="user-name"
           placeholder="Please input user name"
           @focus="handleDeleteEmailTips('username')"
+          @blur="handleCheckUser"
         >
           <template #prefix>
             <span class="font_family icon-icon_username_b"></span>
@@ -269,9 +272,7 @@ const test = () => {
             <span v-if="isUserNameExit" class="font_family icon-icon_confirm_b confirm-icon"></span>
           </template>
         </el-input>
-        <div class="error" v-if="loginError.username">
-          This is the prompt information given by the verification
-        </div>
+        <div class="error" v-if="loginError.username">This account does not exist</div>
         <div class="label">
           <span>Email Address</span>
         </div>
@@ -285,9 +286,7 @@ const test = () => {
             <span class="font_family icon-icon_email_b"></span>
           </template>
         </el-input>
-        <div class="error" v-if="loginError.password">
-          This is the prompt information given by the verification
-        </div>
+        <div class="error" v-if="loginError.email">Incorrect email. Please try again.</div>
         <el-input
           ref="codeRef"
           :class="{ 'is-error': loginError.code }"
@@ -298,9 +297,7 @@ const test = () => {
           ><template #append>
             <img class="verification-code-img" :src="verificationCode" alt="" /> </template
         ></el-input>
-        <div class="error" v-if="loginError.code">
-          This is the prompt information given by the verification
-        </div>
+        <div class="error" v-if="loginError.code">Incorrect verification code.</div>
         <el-button @click="handleSendPassword" class="el-button--dark login-btn"
           >Send Password</el-button
         >
@@ -326,12 +323,20 @@ const test = () => {
 
 <style lang="scss" scoped>
 .login {
+  position: relative;
   display: flex;
   justify-content: center;
   align-items: center;
   height: 100%;
+  width: 100%;
   background: url(../src/image/bg-image.png) no-repeat center center;
   background-size: cover;
+  .scoring-system {
+    position: absolute;
+    top: 0;
+    width: 100%;
+    background: linear-gradient(251deg, #fff4eb 22.66%, #f0f3ff 44.57%, #e0f7f9 70.46%);
+  }
 }
 
 .login-card {

+ 1 - 1
src/views/OperationLog/src/components/BookingTable/src/BookingTable.vue

@@ -257,7 +257,7 @@ defineExpose({
       @checkbox-all="handleCheckAllChange"
     >
       <!-- 空数据时的插槽 -->
-      <template #empty>
+      <template #suggestion>
         <VEmpty>
           <template #default>
             <p style="color: var(--color-neutral-3)">

+ 44 - 7
src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue

@@ -1,16 +1,36 @@
 <script setup lang="ts">
 import { useRouter } from 'vue-router'
 const router = useRouter()
-const inputVModel = ref('')
+const inputVModel = ref('A170353006211')
 
+const searchResult = ref('')
+const loading = ref(false)
 const handleSearchNo = () => {
-  router.push(`/public-tracking/detail?searchNo=${inputVModel.value}`)
-  console.log('search no')
+  loading.value = true
+  $api
+    .getPublicTrackingDetail({ reference_number: inputVModel.value })
+    .then((res) => {
+      if (res.code === 200) {
+        const { data } = res
+        if (data.msg === 'No matches') {
+          searchResult.value = 'error'
+        } else if (data.msg === 'Multiple results') {
+          searchResult.value = 'multiple'
+        } else {
+          sessionStorage.setItem('publicTrackingData', JSON.stringify(data.data))
+          router.push(`/public-tracking/detail?searchNo=${inputVModel.value}`)
+        }
+      }
+    })
+    .finally(() => {
+      loading.value = false
+    })
+  // router.push(`/public-tracking/detail?searchNo=${inputVModel.value}`)
 }
 </script>
 
 <template>
-  <div class="public-tracking-search">
+  <div class="public-tracking-search" v-vloading="loading">
     <div class="search-info">
       <div class="title">Tracking</div>
       <el-input
@@ -23,8 +43,8 @@ const handleSearchNo = () => {
         </template>
       </el-input>
       <div class="empty">
-        <VEmpty>
-          <template #default>
+        <VEmpty class="error-empty" v-if="searchResult === 'error'">
+          <template #suggestion>
             <div class="suggestion-info">
               <p>We support the following references number to find shipment:</p>
               <p>
@@ -34,12 +54,24 @@ const handleSearchNo = () => {
             </div>
           </template>
         </VEmpty>
+        <VEmpty class="multiple-empty" v-else-if="searchResult === 'multiple'">
+          <template #title> Sorry,Multiple results </template>
+          <template #result>
+            <p class="light">To correctly display the details page,</p>
+            <p class="light">please search using the HBL No. Thank you.</p>
+          </template>
+        </VEmpty>
       </div>
     </div>
   </div>
 </template>
 
 <style lang="scss" scoped>
+.light {
+  font-size: 12px;
+  line-height: 18px;
+  color: var(--color-neutral-3);
+}
 .public-tracking-search {
   display: flex;
   justify-content: center;
@@ -89,7 +121,8 @@ const handleSearchNo = () => {
       }
     }
   }
-  .empty {
+  .error-empty,
+  .multiple-empty {
     width: 480px;
     height: 315px;
     padding: 16px;
@@ -102,5 +135,9 @@ const handleSearchNo = () => {
       }
     }
   }
+  div.multiple-empty {
+    height: 236px;
+    padding-top: 24px;
+  }
 }
 </style>

+ 69 - 15
src/views/Tracking/src/components/PublicTracking/src/components/BasicInformation.vue

@@ -1,8 +1,7 @@
 <script setup lang="ts">
-import { useRouter } from 'vue-router'
-
-const router = useRouter()
-
+const props = defineProps({
+  data: Object
+})
 const allData: any = ref({
   basicInformation: {
     top: [
@@ -69,11 +68,71 @@ const allData: any = ref({
     }
   ]
 })
-
-// 跳转到shipment页面
-const handLink = (id: string) => {
-  router.push({ path: '/tracking', query: { id } })
+const convertData = (data: any) => {
+  return {
+    basicInformation: {
+      top: [
+        {
+          label: 'MAWB/MBL No.',
+          content: data.basicInfo['MAWB/MBL No.']
+        },
+        {
+          label: 'HAWB/HBL No.',
+          content: data.basicInfo['HAWB/HBOL']
+        },
+        {
+          label: 'Booking No.',
+          content: data.basicInfo.Carrier_Booking_No
+        },
+        {
+          label: 'PO No.',
+          content: data.basicInfo.PO_NO
+        }
+      ]
+    },
+    businessPartners: [
+      {
+        title: 'Origin Agent',
+        conpany: data.businessPartners.origin.company,
+        address: data.businessPartners.origin.address,
+        phone: data.businessPartners.origin.phone
+      },
+      {
+        title: 'Destination Agent',
+        conpany: data.businessPartners.destination.company,
+        address: data.businessPartners.destination.address,
+        phone: data.businessPartners.destination.phone
+      }
+    ],
+    packing: [
+      {
+        label: 'Quantity / Unit',
+        content: data.packing['Quantity/Unit']
+      },
+      {
+        label: 'G. Weight',
+        content: data.packing['G. Weight']
+      },
+      {
+        label: 'Ch. Weight',
+        content: data.packing['Ch. Weight']
+      },
+      {
+        label: 'Volume',
+        content: data.packing.Volume
+      }
+    ]
+  }
 }
+watch(
+  () => props.data,
+  (newVal) => {
+    if (newVal) {
+      allData.value = convertData(newVal)
+    }
+  },
+  { immediate: true, deep: true }
+)
 </script>
 
 <template>
@@ -83,15 +142,10 @@ const handLink = (id: string) => {
         <div class="title">
           <span>{{ item.label }}</span>
         </div>
-        <div class="content" @click="handLink(item.content)">
+        <div class="content">
           {{ item.content }}
         </div>
       </div>
-
-      <div v-for="item in allData.basicInformation.bottom" :key="item.label" class="data-item">
-        <div class="title">{{ item.label }}</div>
-        <div class="content">{{ item.content }}</div>
-      </div>
     </div>
     <div class="right-section">
       <div class="business-partner">
@@ -136,7 +190,7 @@ const handLink = (id: string) => {
   .left-section {
     display: flex;
     flex-direction: column;
-    gap: 18px;
+    gap: 20px;
     width: 334px;
     padding: 16px 16px 16px 0;
     border-right: 1px solid var(--color-border);

+ 50 - 0
src/views/Tracking/src/components/PublicTracking/src/components/MilestonesTable.vue

@@ -1,7 +1,13 @@
 <script setup lang="ts">
+import dayjs from 'dayjs'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+import { autoWidth } from '@/utils/table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
 
+const props = defineProps({
+  data: Object
+})
+
 const tableData = ref<VxeGridProps<any>>({
   border: true,
   minHeight: 70,
@@ -54,6 +60,50 @@ const tableData = ref<VxeGridProps<any>>({
   rowConfig: { isHover: true }
 })
 
+const handleColumns = (columns: any) => {
+  const newColumns = columns.map((item: any) => {
+    let curColumn: any = {
+      title: item.title,
+      field: item.field
+    }
+
+    // 格式化
+    if (item.formatter === 'date') {
+      curColumn = {
+        ...curColumn,
+        sortBy: ({ row, column }: any) => {
+          return dayjs(row[column.field]).unix()
+        },
+        formatter: ({ cellValue }: any) => dayjs(cellValue).format('MMM-YYYY-DD ') || '--'
+      }
+    } else if (item.formatter === 'dateTime') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) => dayjs(cellValue).format('MMM-YYYY-DD HH:mm:ss') || '--'
+      }
+    }
+    return curColumn
+  })
+  return newColumns
+}
+watch(
+  () => props.data,
+  (newVal) => {
+    const milestones = newVal?.Milestones
+    if (milestones && milestones.Milestones_column) {
+      tableData.value.columns = handleColumns(milestones.Milestones_column)
+      tableData.value.data = milestones.Milestones_data
+      nextTick(() => {
+        tableRef.value && autoWidth(tableData.value, tableRef.value)
+      })
+    }
+  },
+  {
+    immediate: true,
+    deep: true
+  }
+)
+
 const tableRef = ref<VxeGridInstance | null>(null)
 // 实现行点击样式
 useRowClickStyle(tableRef)

+ 27 - 11
src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue

@@ -1,22 +1,35 @@
 <script setup lang="ts">
 import BasicInformation from './BasicInformation.vue'
 import MilestonesTable from './MilestonesTable.vue'
+import { transportationMode } from '@/components/TransportationMode'
+import dayjs from 'dayjs'
+
+const data: any = JSON.parse(sessionStorage.getItem('publicTrackingData'))
+console.log(data)
+
+const formatTime = (time: string) => {
+  return time ? dayjs(time).format('MMM-DD-YYYY hh:mm A') : '--'
+}
 </script>
 
 <template>
   <div class="tracking-detail">
     <div class="header">
       <div class="detail-status">
-        <span class="font_family icon-icon_ocean_b" style="font-size: 64px"></span>
-        <div class="no">Tracking No. B83131200164</div>
-        <VTag large type="Confirmed">Confirmed</VTag>
+        <span
+          class="font_family"
+          :class="[`icon-${transportationMode?.[data?.transportInfo?.mode]}`]"
+          style="font-size: 64px"
+        ></span>
+        <div class="no">Tracking No. {{ data.transportInfo['Tracking No.'] }}</div>
+        <VTag large type="Confirmed">{{ data.transportInfo?.status }}</VTag>
       </div>
       <div class="detail-info">
         <div class="item transport-way">
           <div class="place">
             <div class="title">Origin</div>
             <div class="content">
-              <span>Shenzhen</span>
+              <span>{{ data.transportInfo?.origin }}</span>
               <div class="line_container">
                 <hr color="#000000" />
                 <div class="right-icon"></div>
@@ -25,21 +38,23 @@ import MilestonesTable from './MilestonesTable.vue'
           </div>
           <div class="place">
             <div class="title">Destination</div>
-            <div class="content">Valencia</div>
+            <div class="content">{{ data.transportInfo?.destination }}</div>
           </div>
         </div>
         <div class="item">
           <div class="title">ETD/ATD</div>
           <div class="content">
-            <span>Jun-08-2024 12:00 AM / </span>
-            <span style="color: var(--color-neutral-1)">Jun-08-2024 12:00 AM</span>
+            <span>{{ formatTime(data?.transportInfo?.etd) }} / </span>
+            <span style="color: var(--color-neutral-1)">{{
+              formatTime(data?.transportInfo?.atd)
+            }}</span>
           </div>
         </div>
         <div class="item">
           <div class="title">ETA/ATA</div>
           <div class="content">
-            <span>Oct-26-2024 12:00 AM / </span>
-            <span>--</span>
+            <span>{{ formatTime(data?.transportInfo?.eta) }} / </span>
+            <span>{{ formatTime(data?.transportInfo?.ata) }}</span>
           </div>
         </div>
       </div>
@@ -55,14 +70,14 @@ import MilestonesTable from './MilestonesTable.vue'
           </div>
         </template>
         <template #content>
-          <BasicInformation ref="basicInformationRef"></BasicInformation>
+          <BasicInformation :data="data" ref="basicInformationRef"></BasicInformation>
         </template>
       </VBox>
       <!-- Milestones -->
       <VBox style="margin-top: 16px" :isSeeAll="false" :is-draggable="false">
         <template #header>Milestones</template>
         <template #content>
-          <MilestonesTable></MilestonesTable>
+          <MilestonesTable :data="data"></MilestonesTable>
         </template>
       </VBox>
     </div>
@@ -93,6 +108,7 @@ import MilestonesTable from './MilestonesTable.vue'
       position: relative;
       display: flex;
       align-items: center;
+      height: 64px;
       padding: 0 16px;
       border-bottom: 1px solid var(--color-border);
 

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

@@ -78,9 +78,40 @@ const handleEmailDrawer = () => {
   emailDrawerRef.value.openDrawer(allData.value)
 }
 
+const amsIsfData = ref()
+const getAmsIsfData = () => {
+  $api
+    .getTrackingAmsIsf({
+      ams_ss: allData.value.ams_ss,
+      isf_ss: allData.value.isf_ss,
+      _schemas: allData.value._schemas
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        // 获取数据
+        amsIsfData.value = res.data
+        isShowAMSISF.value = true
+      }
+    })
+}
+
 const AMSISFDrawerRef = ref()
+const isShowAMSISF = ref(false)
 const handleAMSISF = () => {
-  AMSISFDrawerRef.value.openDrawer(allData.value)
+  if (isShowAMSISF.value) {
+    AMSISFDrawerRef.value?.openDrawer(amsIsfData.value)
+  } else {
+    // 如果 isShowAMSISF 从 false 变为 true,自动打开抽屉
+    watch(
+      isShowAMSISF,
+      (newValue) => {
+        if (newValue) {
+          AMSISFDrawerRef.value?.openDrawer(amsIsfData.value)
+        }
+      },
+      { immediate: true }
+    )
+  }
 }
 
 const allData = ref()
@@ -101,6 +132,7 @@ const getData = () => {
     })
     .finally(() => {
       loading.value = false
+      getAmsIsfData()
     })
 }
 getData()

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

@@ -8,28 +8,42 @@ const openDrawer = (data: any) => {
   drawer.value = true
 }
 
-const tableLoading = ref(false)
-const getData = (data: any) => {
-  tableLoading.value = true
-  $api
-    .getTrackingAmsIsf({
-      ams_ss: data.ams_ss,
-      isf_ss: data.isf_ss,
-      _schemas: data._schemas
-    })
-    .then((res: any) => {
-      console.log(res)
-      if (res.code === 200) {
-        const { amsLog, isfLog } = res.data
-        AMSTableData.value.columns = amsLog.amsLog_column
-        AMSTableData.value.data = amsLog.data
-        ISFTableData.value.columns = isfLog.isfLog_column
-        ISFTableData.value.data = res.data.isf
+const canViewAMSLog = ref(false)
+const canViewISFLog = ref(false)
+const handleColumns = (columns: any) => {
+  const newColumns = columns.map((item: any) => {
+    let curColumn: any = {
+      title: item.title,
+      field: item.field
+    }
+
+    if (item.formatter === 'dateTime') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) => dayjs(cellValue).format('DD-MMM-YYYY HH:mm:ss') || '--'
       }
-    })
-    .finally(() => {
-      tableLoading.value = false
-    })
+    }
+    return curColumn
+  })
+  return newColumns
+}
+const getData = (data: any) => {
+  const { amsLog, isfLog } = 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)
+  ISFTableData.value.data = data.isf
+}
+const drawerTitle = () => {
+  if (canViewAMSLog.value && canViewISFLog.value) {
+    return 'AMS/ISF'
+  } else if (canViewAMSLog.value) {
+    return 'AMS'
+  } else if (canViewISFLog.value) {
+    return 'ISF'
+  }
 }
 
 const AMSTableRef = ref<VxeGridInstance | null>(null)
@@ -113,32 +127,32 @@ defineExpose({
     @close="clearData"
     :size="1000"
     v-model="drawer"
-    title="AMS/ISF"
+    :title="drawerTitle()"
     direction="rtl"
   >
     <div class="ams-isf">
-      <div class="label" style="margin-top: 8px">
+      <div class="label" v-if="canViewAMSLog" style="margin-top: 8px">
         <span>AMS-M1 Log</span>
         <el-button class="el-button--icon" @click="exportAMSTable">
           <span class="font_family icon-icon_export_b"></span>
         </el-button>
       </div>
       <vxe-grid
-        v-vloading="tableLoading"
+        v-if="canViewAMSLog"
         ref="AMSTableRef"
         class="radius-bottom"
         :style="{ border: 'none' }"
         v-bind="AMSTableData"
       >
       </vxe-grid>
-      <div class="label">
+      <div class="label" v-if="canViewISFLog">
         <span>ISF Log</span>
         <el-button class="el-button--icon" @click="exportISFTable">
           <span class="font_family icon-icon_export_b"></span>
         </el-button>
       </div>
       <vxe-grid
-        v-vloading="tableLoading"
+        v-if="canViewISFLog"
         class="radius-bottom"
         ref="ISFTableRef"
         :style="{ border: 'none' }"

+ 7 - 6
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -286,9 +286,10 @@ const handleCheckAllChange = ({ records }: any) => {
 }
 
 // VGM
-const handleVGM = () => {
+const handleVGM = (row) => {
   router.push({
-    path: '/tracking/add-vgm'
+    path: '/tracking/add-vgm',
+    query: { a: row.__serial_no, _schemas: row._schemas }
   })
 }
 defineExpose({
@@ -327,7 +328,7 @@ defineExpose({
       <!-- 空数据时的插槽 -->
       <template #empty>
         <VEmpty>
-          <template #default>
+          <template #suggestion>
             <p>We support the following references number to find tracking:</p>
             <p>
               · Tracking No./HAWB No./MAWB No./PO No./Carrier Tracking No./Contract No./File
@@ -337,8 +338,8 @@ defineExpose({
         </VEmpty>
       </template>
       <!-- action操作的插槽 -->
-      <template #action>
-        <el-button @click="handleVGM" class="el-button--blue" style="height: 24px">
+      <template #action="{ row }">
+        <el-button @click="handleVGM(row)" class="el-button--blue" style="height: 24px">
           <span class="font_family icon-icon_vgm_b"></span> <span style="font-size: 12px">VGM</span>
         </el-button>
       </template>
@@ -432,4 +433,4 @@ defineExpose({
     visibility: hidden;
   }
 }
-</style>
+</style>

+ 251 - 79
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -1,64 +1,98 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
-
+import { useRoute, useRouter } from 'vue-router'
+import { autoWidth } from '@/utils/table'
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 
-const formInline = ref({
-  user: '',
-  region: '',
-  date: ''
-})
-
-// 使用类型断言
-// const rules = reactive({
-//   user: [{ required: true, message: 'Please input Activity name', trigger: 'blur' }]
-// })
+const route = useRoute()
+const router = useRouter()
 
-const tableData = ref<VxeGridProps<any>>({
-  minHeight: 70,
-  border: true,
-  round: true,
-  columns: [
+const loading = ref(false)
+const generalInfo = ref({
+  baseInfo: [
     {
-      title: 'Container No.',
-      field: 'container_no',
-      width: 150
+      label: 'HBL No.',
+      value: ''
     },
     {
-      title: 'VGM Weight',
-      field: 'vgm_weight',
-      width: 150,
-      editRender: {
-        name: 'vInput'
-      },
-      slots: {
-        edit: 'vInput'
-      }
+      label: 'Carrier Booking No.',
+      value: ''
     },
     {
-      title: 'VGM Unit',
-      field: 'vgm_unit',
-      width: 150,
-      editRender: {
-        name: 'vSelect'
-      },
-      slots: {
-        edit: 'vSelect'
-      }
+      label: 'Vessel',
+      value: ''
     },
     {
-      title: 'VGM Date',
-      field: 'vgm_date',
-      width: 150,
-      editRender: {
-        name: 'editDate'
-      },
-      slots: {
-        edit: 'editDate'
-      }
+      label: 'Voyage',
+      value: ''
+    },
+    {
+      label: 'ETD',
+      value: ''
+    },
+    {
+      label: 'ETA ',
+      value: ''
+    },
+    {
+      label: 'Last Updated User',
+      value: ''
+    },
+    {
+      label: 'Last Updated Time',
+      value: ''
     }
   ],
-  data: [{}],
+  formData: {
+    Submitter: '',
+    signature: '',
+    authorized_email: '',
+    authorized_tel: '',
+    is_send: false
+  }
+})
+
+const tableRef = ref<VxeGridInstance | null>(null)
+const tableData = ref<VxeGridProps<any>>({
+  minHeight: 70,
+  border: true,
+  round: true,
+  columns: [
+    // {
+    //   title: 'VGM Weight',
+    //   field: 'vgm_weight',
+    //   width: 150,
+    //   editRender: {
+    //     name: 'vInput'
+    //   },
+    //   slots: {
+    //     edit: 'vInput'
+    //   }
+    // },
+    // {
+    //   title: 'VGM Unit',
+    //   field: 'vgm_unit',
+    //   width: 150,
+    //   editRender: {
+    //     name: 'vSelect'
+    //   },
+    //   slots: {
+    //     edit: 'vSelect'
+    //   }
+    // },
+    // {
+    //   title: 'VGM Date',
+    //   field: 'vgm_date',
+    //   width: 150,
+    //   editRender: {
+    //     name: 'editDate'
+    //   },
+    //   slots: {
+    //     edit: 'editDate'
+    //   }
+    // }
+  ],
+  data: [],
   scrollY: { enabled: true, oSize: 20, gt: 30 },
   stripe: true,
   emptyText: ' ',
@@ -76,15 +110,159 @@ const tableData = ref<VxeGridProps<any>>({
   columnConfig: { resizable: true, useKey: true },
   rowConfig: { isHover: true }
 })
-const tableRef = ref<VxeGridInstance | null>(null)
+const handleColumns = (columns: any) => {
+  const newColumns = columns.map((item: any) => {
+    let curColumn: any = {
+      title: item.title,
+      field: item.field
+    }
+
+    // 添加编辑插槽
+    if (item.type === 'input') {
+      curColumn = {
+        ...curColumn,
+        editRender: {
+          name: 'vInput'
+        },
+        slots: {
+          edit: 'vInput'
+        }
+      }
+    } else if (item.type === 'select') {
+      curColumn = {
+        ...curColumn,
+        editRender: {
+          name: 'vSelect'
+        },
+        slots: {
+          edit: 'vSelect'
+        }
+      }
+    } else if (item.type === 'dateTime') {
+      curColumn = {
+        ...curColumn,
+        editRender: {
+          name: 'editDate'
+        },
+        slots: {
+          edit: 'editDate'
+        }
+      }
+    }
+    // 格式化
+    if (item.formatter === 'date') {
+      curColumn = {
+        ...curColumn,
+        sortBy: ({ row, column }: any) => {
+          return dayjs(row[column.field]).unix()
+        },
+        formatter: ({ cellValue }: any) => dayjs(cellValue).format('MMM-YYYY-DD ') || '--'
+      }
+    } else if (item.formatter === 'dateTime') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) => dayjs(cellValue).format('MMM-YYYY-DD HH:mm:ss') || '--'
+      }
+    }
+    return curColumn
+  })
+  return newColumns
+}
+
+const convertData = (data: any) => {
+  const handleIsSend = (value: string) => {
+    if (value === 'f' || value === '') {
+      return false
+    } else if (value === 't') {
+      return true
+    }
+  }
+  generalInfo.value = {
+    baseInfo: [
+      {
+        label: 'HBL No.',
+        value: data?.['HBL No.']
+      },
+      {
+        label: 'Carrier Booking No.',
+        value: data?.['Carrier Booking No.']
+      },
+      {
+        label: 'Vessel',
+        value: data.Vessel
+      },
+      {
+        label: 'Voyage',
+        value: data.Voyage
+      },
+      {
+        label: 'ETD',
+        value: data.ETD
+      },
+      {
+        label: 'ETA ',
+        value: data.ETA
+      },
+      {
+        label: 'Last Updated User',
+        value: data?.['Last updated User']
+      },
+      {
+        label: 'Last Updated Time',
+        value: data?.['Last updated Time']
+      }
+    ],
+    formData: {
+      Submitter: data.Submitter,
+      signature: data.signature,
+      authorized_email: data.authorized_email,
+      authorized_tel: data.authorized_tel,
+      is_send: handleIsSend(data.is_send)
+    }
+  }
+}
+const getData = () => {
+  loading.value = true
+  $api
+    .getVGMData({
+      a: route.query.a,
+      _schemas: route.query._schemas
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        // 获取数据
+        convertData(res.data?.general_information)
+        tableData.value.columns = handleColumns(
+          res.data?.detail_information?.detail_information_column
+        )
+        tableData.value.data = res.data?.detail_information?.detail_information_data
+        nextTick(() => {
+          tableRef.value && autoWidth(tableData.value, tableRef.value)
+          tableData.value.columns.forEach((item) => {
+            if (item.title === 'SN') {
+              item.width = 50
+            }
+          })
+        })
+      }
+    })
+    .finally(() => {
+      loading.value = false
+    })
+}
+getData()
+
+const handleGoBack = () => {
+  router.push({ path: '/tracking' })
+}
 </script>
 
 <template>
-  <div class="vgm">
+  <div class="vgm" v-vloading="loading">
     <div class="header">
       <div class="title">Add VGM</div>
       <div class="right-option">
-        <el-button class="el-button--default"
+        <el-button class="el-button--default" @click="handleGoBack"
           ><span class="font_family icon-icon_return_b"></span> Cancel</el-button
         >
         <el-button class="el-button--main">
@@ -99,25 +277,9 @@ const tableRef = ref<VxeGridInstance | null>(null)
           <span>General Infomation</span>
         </div>
         <div class="description-info">
-          <div class="data-info">
-            <div class="label">HBL No.</div>
-            <div class="info">HDMUSZXZ96803400</div>
-          </div>
-          <div class="data-info">
-            <div class="label">HBL No.</div>
-            <div class="info">HDMUSZXZ96803400</div>
-          </div>
-          <div class="data-info">
-            <div class="label">HBL No.</div>
-            <div class="info">HDMUSZXZ96803400</div>
-          </div>
-          <div class="data-info">
-            <div class="label">HBL No.</div>
-            <div class="info">HDMUSZXZ96803400</div>
-          </div>
-          <div class="data-info">
-            <div class="label">HBL No.</div>
-            <div class="info">HDMUSZXZ96803400</div>
+          <div class="data-info" v-for="item in generalInfo.baseInfo" :key="item.label">
+            <div class="label">{{ item.label }}</div>
+            <div class="info">{{ item.value }}</div>
           </div>
         </div>
         <div class="form">
@@ -126,7 +288,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
               <div class="label">Submitter <span class="require-asterisk">*</span></div>
               <div class="content">
                 <el-input
-                  v-model="formInline.user"
+                  v-model="generalInfo.formData.Submitter"
                   placeholder="Please enter..."
                   clearable
                 ></el-input>
@@ -136,7 +298,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
               <div class="label">Signature <span class="require-asterisk">*</span></div>
               <div class="content">
                 <el-input
-                  v-model="formInline.region"
+                  v-model="generalInfo.formData.signature"
                   placeholder="Please enter..."
                   clearable
                 ></el-input>
@@ -149,7 +311,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
               <div class="label">Authorized Email <span class="require-asterisk">*</span></div>
               <div class="content">
                 <el-input
-                  v-model="formInline.user"
+                  v-model="generalInfo.formData.authorized_email"
                   placeholder="Please enter..."
                   clearable
                 ></el-input>
@@ -162,7 +324,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
               </div>
               <div class="content">
                 <el-input
-                  v-model="formInline.user"
+                  v-model="generalInfo.formData.authorized_tel"
                   placeholder="Please enter..."
                   clearable
                 ></el-input>
@@ -171,7 +333,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
             <div class="form-item" style="flex: 0 0 130px">
               <div class="label"></div>
               <div class="content">
-                <el-checkbox v-model="formInline.user" label="Option 1" size="large" />
+                <el-checkbox v-model="generalInfo.formData.is_send" label="Is Send" size="large" />
               </div>
             </div>
           </div>
@@ -182,7 +344,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
           <span>Detail Information</span>
         </div>
         <div class="table">
-          <vxe-grid ref="tableRef" v-bind="tableData">
+          <vxe-grid ref="tableRef" class="vgm-table" v-bind="tableData">
             <template #vInput="{ row, column }">
               <el-input
                 v-model="row[column.field]"
@@ -257,7 +419,6 @@ const tableRef = ref<VxeGridInstance | null>(null)
     border-radius: 12px;
     & > .title {
       height: 48px;
-      padding: 0 16px;
       line-height: 48px;
       span {
         font-size: 18px;
@@ -274,6 +435,14 @@ const tableRef = ref<VxeGridInstance | null>(null)
       border-top: 1px solid var(--color-border);
     }
   }
+  .general-info {
+    & > .title {
+      padding-left: 16px;
+    }
+  }
+  .detail-info {
+    padding: 0 16px 8px;
+  }
   .form {
     .form-row {
       display: flex;
@@ -318,6 +487,7 @@ const tableRef = ref<VxeGridInstance | null>(null)
     color: var(--dashboard-text-color);
   }
   .info {
+    height: 21px;
     line-height: 21px;
     font-weight: 700;
   }
@@ -335,8 +505,10 @@ const tableRef = ref<VxeGridInstance | null>(null)
     color: #202020;
   }
 }
-.vxe-grid .vxe-grid--table-wrapper,
-div.vxe-table--body-wrapper {
-  overflow: visible;
+.vgm-table {
+  .vxe-grid .vxe-grid--table-wrapper,
+  div.vxe-table--body-wrapper {
+    overflow: visible;
+  }
 }
 </style>