Prechádzať zdrojové kódy

feat: 完成面包屑功能以及add vgm页面的验证

zhouyuhao 1 rok pred
rodič
commit
c0afa3c6b7

+ 1 - 3
src/components/ContainerStatus/src/ContainerStatus.vue

@@ -30,9 +30,7 @@ const formatTimezone = (time: string, timezone: string) => {
   if (!time) return '--'
 
   const formattedTime =
-    time.length !== 10
-      ? dayjs(time).format('MMM-DD-YYYY hh:mm A')
-      : dayjs(time).format('MMM-DD-YYYY')
+    time.length > 12 ? dayjs(time).format('MMM-DD-YYYY hh:mm A') : dayjs(time).format('MMM-DD-YYYY')
   let gmtOffset = ''
   if (timezone && timezone.length > 3) {
     const timeZoneOffset = dayjs().tz(timezone).format('Z')

+ 24 - 39
src/components/VBreadcrumb/src/VBreadcrumb.vue

@@ -1,54 +1,31 @@
 <script setup lang="ts">
-import { useRouter, useRoute } from 'vue-router'
+import { useRouter } from 'vue-router'
 import { useBreadCrumb } from '@/stores/modules/breadCrumb'
 
 const router = useRouter()
-const route = useRoute()
 const breadCrumb = useBreadCrumb()
 
-const path = computed(() => {
-  const path = route.path
-  // 检查URL中是否包含'detail'
-  if (path.includes('/detail')) {
-    // 获取'detail'的位置
-    const detailIndex = path.indexOf('/detail')
-    // 从开始到'/detail'前一个'/'的位置的子串
-    const lastSlashIndex = path.lastIndexOf('/', detailIndex - 1)
-    // 如果找到了'/'
-    if (lastSlashIndex !== -1) {
-      // 获取上一段字符串
-      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
-})
-
-const mapPathName = {
-  booking: 'Booking',
-  tracking: 'Tracking',
-  'public-tracking': 'Public Tracking'
-}
 const handleGoBack = () => {
-  router.push({ path: '/' + path.value })
+  router.go(-1)
+}
+const jumpLink = (label: string) => {
+  label &&
+    router.push({
+      name: label
+    })
 }
-const pathName = computed(() => {
-  return mapPathName[path.value]
-})
 </script>
 
 <template>
-  <div class="v-breadcrumd" v-if="path">
+  <div class="v-breadcrumd" v-if="breadCrumb.routeList.length > 1">
     <span class="font_family icon-icon_back_b" @click="handleGoBack"></span>
-    <span style="color: var(--color-neutral-3)">{{ pathName }}</span>
-    <span class="interval">|</span>
-    <span style="font-weight: 700">Detail</span>
+    <template v-for="(routeItem, index) in breadCrumb.routeList" :key="routeItem.label">
+      <template v-if="index + 1 !== breadCrumb.routeList.length">
+        <span @click="jumpLink(routeItem.label)" class="previous-route">{{ routeItem.label }}</span>
+        <span class="interval">|</span>
+      </template>
+      <span v-else style="font-weight: 700">{{ routeItem.label }}</span>
+    </template>
   </div>
   <div v-else></div>
 </template>
@@ -67,5 +44,13 @@ const pathName = computed(() => {
     color: var(--color-neutral-3);
     transform: rotate(20deg);
   }
+  .previous-route {
+    color: var(--color-neutral-3);
+    cursor: pointer;
+    &:hover {
+      font-weight: 500;
+      color: var(--color-theme);
+    }
+  }
 }
 </style>

+ 1 - 1
src/router/index.ts

@@ -95,7 +95,7 @@ const router = createRouter({
 
 // * 路由拦截 beforeEach
 router.beforeEach(async (to, from, next) => {
-  useBreadCrumb().setRouteList(to, from)
+  useBreadCrumb().setRouteList(to)
   // 如果手动跳转登录页,清除登录信息
   if (to.path === '/login') {
     const userStore = useUserStore()

+ 15 - 1
src/stores/modules/breadCrumb.ts

@@ -17,12 +17,26 @@ export const useBreadCrumb = defineStore('breadCrumb', {
   }),
   getters: {},
   actions: {
-    setRouteList(toRoute: any, fromRoute: any) {
+    setRouteList(toRoute: any) {
       const index = this.routeList.findIndex((item) => item.label === toRoute.name)
       if (index !== -1) {
         this.routeList.splice(index + 1)
         return
       }
+      if (toRoute.name === 'Public Tracking Detail') {
+        this.routeList = [
+          {
+            label: 'Public Tracking',
+            path: '/public-tracking',
+            query: ''
+          },
+          {
+            label: 'Public Tracking Detail',
+            path: '/public-tracking/detail',
+            query: toRoute.query
+          }
+        ]
+      }
       if (toRoute.name && whiteList.includes(toRoute.name)) {
         this.routeList.push({
           label: toRoute.name,

+ 1 - 1
src/utils/axios.ts

@@ -57,9 +57,9 @@ class HttpAxios {
   _responseInterceptors = (response: AxiosResponse) => {
     if (response.status === 200) {
       if (response.data.code === 401 || response.data.code === 403) {
+        router.push('/login')
         const userStore = useUserStore()
         userStore.logout(false)
-        router.push('/login')
         ElMessage.warning({
           message: 'Please log in to use this feature.',
           grouping: true

+ 8 - 12
src/views/Booking/src/components/BookingDetail/src/components/BasicInformation.vue

@@ -261,7 +261,7 @@ watch(
 )
 
 // 跳转到shipment页面
-const handLink = (id: string) => {
+const handLink = () => {
   router.push({
     path: '/tracking/detail',
     query: { a: props?.data?.__serial_no, _schemas: props?.data?._schemas }
@@ -311,11 +311,13 @@ defineExpose({
             Add Reference
           </el-button> -->
         </div>
+        <div class="content" v-if="item.label !== 'Ref No.' && item.type !== 'link'">
+          {{ item.content }}
+        </div>
         <div
-          class="content"
-          :class="{ link: item.type === 'link' }"
-          v-if="item.label !== 'Ref No.'"
-          @click="handLink(item.content)"
+          class="content link"
+          v-else-if="item.label !== 'Ref No.' && item.type === 'link'"
+          @click="handLink()"
         >
           {{ item.content }}
         </div>
@@ -332,13 +334,7 @@ defineExpose({
             </div>
           </template>
           <template v-else>
-            <div
-              class="content"
-              :class="{ link: item.type === 'link' }"
-              @click="handLink(item.content)"
-            >
-              --
-            </div>
+            <div class="content">--</div>
           </template>
         </template>
       </div>

+ 5 - 5
src/views/Booking/src/components/BookingDetail/src/components/ContainersView.vue

@@ -30,7 +30,8 @@ const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
       title: item.title,
-      field: item.field
+      field: item.field,
+      minWidth: 30
     }
 
     // 格式化
@@ -61,9 +62,9 @@ watch(
     if (containers && containers.container_column) {
       tableData.value.columns = handleColumns(containers.container_column)
       tableData.value.data = containers.container_data
-      nextTick(() => {
-        tableRef.value && autoWidth(tableData.value, tableRef.value)
-      })
+      // nextTick(() => {
+      //   tableRef.value && autoWidth(tableData.value, tableRef.value)
+      // })
     }
   },
   {
@@ -87,4 +88,3 @@ useRowClickStyle(tableRef)
   padding: 8px 16px;
 }
 </style>
-<style lang="scss"></style>

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

@@ -88,12 +88,13 @@ const getTableColumns = async () => {
 }
 
 const pageInfo = ref({ pageNo: 1, pageSize: 100, total: 0 })
-
+const curTableData = ref([])
 const tempSearch = ref()
 let filterdataobj: any = {}
 // 获得表格数据后赋值
 const assignTableData = (data: any) => {
   bookingTable.value.data = data.searchData || []
+  curTableData.value = data.searchData || []
   pageInfo.value.total = Number(data.rc)
   tempSearch.value = data.tmp_search
   // 拥有所有字段的表格
@@ -336,6 +337,8 @@ const exportTable = (status: number) => {
     }
   }
   allTableRef.value?.exportData(exportConfig)
+  // 将所有表格的值赋值为当前表格的值
+  allTable.value.data = curTableData.value
 }
 
 const tableLoadingColumn = ref(false)

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

@@ -97,6 +97,11 @@ const isAllowJump = (path: any) => {
       message: 'Please log in to use this feature.',
       grouping: true
     })
+    if (route.name !== 'Public Tracking') {
+      router.push({
+        name: 'Login'
+      })
+    }
     activeMenu.value = route.path // 保持选中状态不变
     return false
   }

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

@@ -37,7 +37,8 @@ const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
       title: item.title,
-      field: item.field
+      field: item.field,
+      minWidth: 30
     }
 
     // 格式化
@@ -47,7 +48,7 @@ const handleColumns = (columns: any) => {
         formatter: ({ cellValue, row }: any) => {
           if (!cellValue) return '--'
           const formattedTime =
-            cellValue.length !== 10
+            cellValue.length > 12
               ? dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
               : dayjs(cellValue).format('MMM-DD-YYYY')
           let gmtOffset = ''

+ 2 - 1
src/views/Tracking/src/components/TrackingDetail/src/components/AttachmentView.vue

@@ -31,7 +31,8 @@ const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
       title: item.title,
-      field: item.field
+      field: item.field,
+      minWidth: 30
     }
 
     if (item.field === 'file') {

+ 2 - 13
src/views/Tracking/src/components/TrackingDetail/src/components/BasicInformation.vue

@@ -308,12 +308,7 @@ defineExpose({
             Add Reference
           </el-button> -->
         </div>
-        <div
-          class="content"
-          :class="{ link: item.type === 'link' }"
-          v-if="item.label !== 'Ref No.'"
-          @click="handLink(item.content)"
-        >
+        <div class="content" v-if="item.label !== 'Ref No.'">
           {{ item.content }}
         </div>
         <template v-else>
@@ -329,13 +324,7 @@ defineExpose({
             </div>
           </template>
           <template v-else>
-            <div
-              class="content"
-              :class="{ link: item.type === 'link' }"
-              @click="handLink(item.content)"
-            >
-              --
-            </div>
+            <div class="content">--</div>
           </template>
         </template>
       </div>

+ 5 - 4
src/views/Tracking/src/components/TrackingDetail/src/components/ContainersView.vue

@@ -31,7 +31,8 @@ const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
       title: item.title,
-      field: item.field
+      field: item.field,
+      minWidth: 30
     }
 
     // 格式化
@@ -62,9 +63,9 @@ watch(
     if (containers && containers.container_column) {
       tableData.value.columns = handleColumns(containers.container_column)
       tableData.value.data = containers.container_data
-      nextTick(() => {
-        tableRef.value && autoWidth(tableData.value, tableRef.value)
-      })
+      // nextTick(() => {
+      //   tableRef.value && autoWidth(tableData.value, tableRef.value)
+      // })
     }
   },
   {

+ 4 - 5
src/views/Tracking/src/components/TrackingDetail/src/components/MilestonesTable.vue

@@ -2,14 +2,13 @@
 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'
 
+dayjs.extend(utc)
+dayjs.extend(timezone)
+
 const props = defineProps({
   data: Object
 })
@@ -49,7 +48,7 @@ const handleColumns = (columns: any) => {
         formatter: ({ row, cellValue }: any) => {
           if (!cellValue) return '--'
           const formattedTime =
-            cellValue.length !== 10
+            cellValue.length > 12
               ? dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
               : dayjs(cellValue).format('MMM-DD-YYYY')
           let gmtOffset = ''

+ 5 - 0
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -95,9 +95,12 @@ const pageInfo = ref({ pageNo: 1, pageSize: 100, total: 0 })
 const TagsList = ref()
 const tempSearch = ref('')
 
+const curTableData = ref([])
+
 // 获得表格数据后赋值
 const assignTableData = (data: any) => {
   trackingTable.value.data = data.searchData || []
+  curTableData.value = data.searchData || []
   pageInfo.value.total = Number(data.rc)
   TagsList.value = data.tagsList
   tempSearch.value = data.tmp_search
@@ -391,6 +394,8 @@ const exportTable = (status: number) => {
     }
   }
   allTableRef.value?.exportData(exportConfig)
+  // 将所有表格的值赋值为当前表格的值
+  allTable.value.data = curTableData.value
 }
 
 const tableLoadingColumn = ref(false)

+ 146 - 11
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -9,6 +9,7 @@ const router = useRouter()
 
 const allData = ref<any>({})
 const loading = ref(false)
+const messageTips = ref('')
 const generalInfo = ref({
   baseInfo: [
     {
@@ -81,7 +82,8 @@ const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
       title: item.title,
-      field: item.field
+      field: item.field,
+      minWidth: 30
     }
 
     // 添加编辑插槽
@@ -212,6 +214,9 @@ const getData = () => {
     })
     .then((res: any) => {
       if (res.code === 200) {
+        if (res.data.msg) {
+          messageTips.value = res.data.msg
+        }
         allData.value = res.data
         convertData(res.data?.general_information)
         tableData.value.columns = handleColumns(
@@ -219,7 +224,7 @@ const getData = () => {
         )
         tableData.value.data = res.data?.detail_information?.detail_information_data
         nextTick(() => {
-          tableRef.value && autoWidth(tableData.value, tableRef.value)
+          // tableRef.value && autoWidth(tableData.value, tableRef.value)
           tableData.value.columns.forEach((item) => {
             if (item.title === 'SN') {
               item.width = 50
@@ -241,7 +246,78 @@ getData()
 const handleGoBack = () => {
   router.push({ name: 'Tracking' })
 }
+
+const isVerificationError = ref({
+  submitter: false,
+  signature: false,
+  authorized_email: false,
+  authorized_tel: false
+})
+const verificationData = () => {
+  if (!generalInfo.value.formData.is_send) {
+    return
+  }
+  if (
+    generalInfo.value.formData.Submitter === null ||
+    generalInfo.value.formData.Submitter === undefined ||
+    generalInfo.value.formData.Submitter === ''
+  ) {
+    isVerificationError.value.submitter = true
+  } else {
+    isVerificationError.value.submitter = false
+  }
+  if (
+    generalInfo.value.formData.signature === null ||
+    generalInfo.value.formData.signature === undefined ||
+    generalInfo.value.formData.signature === ''
+  ) {
+    isVerificationError.value.signature = true
+  } else {
+    isVerificationError.value.signature = false
+  }
+  if (
+    generalInfo.value.formData.authorized_email === null ||
+    generalInfo.value.formData.authorized_email === undefined ||
+    generalInfo.value.formData.authorized_email === ''
+  ) {
+    isVerificationError.value.authorized_email = true
+  } else {
+    isVerificationError.value.authorized_email = false
+  }
+  if (
+    generalInfo.value.formData.authorized_tel === null ||
+    generalInfo.value.formData.authorized_tel === undefined ||
+    generalInfo.value.formData.authorized_tel === ''
+  ) {
+    isVerificationError.value.authorized_tel = true
+  } else {
+    isVerificationError.value.authorized_tel = false
+  }
+}
+
+const isVerification = (value) => {
+  if (value) {
+    verificationData()
+  } else {
+    isVerificationError.value = {
+      submitter: false,
+      signature: false,
+      authorized_email: false,
+      authorized_tel: false
+    }
+  }
+}
 const handleSave = () => {
+  if (
+    generalInfo.value.formData.is_send &&
+    (isVerificationError.value.submitter ||
+      isVerificationError.value.submitter ||
+      isVerificationError.value.authorized_email ||
+      isVerificationError.value.authorized_tel)
+  ) {
+    return
+  }
+
   const generalData = {
     all_carrier_booking: generalInfo.value.baseInfo['Carrier Booking No.'],
     submitter: generalInfo.value.formData.Submitter,
@@ -313,13 +389,17 @@ const stopScroll = (evt) => {
 
 <template>
   <div class="vgm" v-vloading="loading">
+    <div v-if="messageTips" class="vgm-message-tips">
+      <span class="font_family icon-icon_tipsfilled_b"></span>
+      <span class="text-content">{{ messageTips }}</span>
+    </div>
     <div class="header">
       <div class="title">Add VGM</div>
       <div class="right-option">
         <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" @click="handleSave">
+        <el-button v-if="!messageTips" class="el-button--main" @click="handleSave">
           <span class="font_family icon-icon_save_b"></span>
           Save</el-button
         >
@@ -339,22 +419,32 @@ const stopScroll = (evt) => {
         <div class="form">
           <div class="form-row">
             <div class="form-item">
-              <div class="label">Submitter <span class="require-asterisk">*</span></div>
+              <div class="label">
+                Submitter
+                <span v-if="generalInfo.formData.is_send" class="require-asterisk">*</span>
+              </div>
               <div class="content">
                 <el-input
+                  :class="{ 'is-error': isVerificationError.submitter }"
                   v-model="generalInfo.formData.Submitter"
                   placeholder="Please enter..."
                   clearable
+                  @blur="verificationData"
                 ></el-input>
               </div>
             </div>
             <div class="form-item">
-              <div class="label">Signature <span class="require-asterisk">*</span></div>
+              <div class="label">
+                Signature
+                <span v-if="generalInfo.formData.is_send" class="require-asterisk">*</span>
+              </div>
               <div class="content">
                 <el-input
+                  :class="{ 'is-error': isVerificationError.signature }"
                   v-model="generalInfo.formData.signature"
                   placeholder="Please enter..."
                   clearable
+                  @blur="verificationData"
                 ></el-input>
               </div>
             </div>
@@ -362,32 +452,44 @@ const stopScroll = (evt) => {
           </div>
           <div class="form-row">
             <div class="form-item">
-              <div class="label">Authorized Email <span class="require-asterisk">*</span></div>
+              <div class="label">
+                Authorized Email
+                <span v-if="generalInfo.formData.is_send" class="require-asterisk">*</span>
+              </div>
               <div class="content">
                 <el-input
+                  :class="{ 'is-error': isVerificationError.authorized_email }"
                   v-model="generalInfo.formData.authorized_email"
                   placeholder="Please enter..."
                   clearable
+                  @blur="verificationData"
                 ></el-input>
               </div>
             </div>
             <div class="form-item">
               <div class="label">
                 Authorized Tel
-                <span class="require-asterisk">*</span>
+                <span class="require-asterisk" v-if="generalInfo.formData.is_send">*</span>
               </div>
               <div class="content">
                 <el-input
+                  :class="{ 'is-error': isVerificationError.authorized_tel }"
                   v-model="generalInfo.formData.authorized_tel"
                   placeholder="Please enter..."
                   clearable
+                  @blur="verificationData"
                 ></el-input>
               </div>
             </div>
-            <div class="form-item" style="flex: 0 0 130px">
+            <div class="form-item" style="flex: 0 0 130px; display: flex; align-items: flex-end">
               <div class="label"></div>
               <div class="content">
-                <el-checkbox v-model="generalInfo.formData.is_send" label="Is Send" size="large" />
+                <el-checkbox
+                  @change="isVerification"
+                  v-model="generalInfo.formData.is_send"
+                  label="Is Send"
+                  size="large"
+                />
               </div>
             </div>
           </div>
@@ -447,6 +549,31 @@ const stopScroll = (evt) => {
 
 <style lang="scss" scoped>
 .vgm {
+  position: relative;
+  .vgm-message-tips {
+    position: absolute;
+    top: 53px;
+    left: 10%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 80%;
+    height: 40px;
+    text-align: center;
+    background-color: #fef5eb;
+    border-radius: 6px;
+    z-index: 10;
+    .font_family {
+      font-size: 18px;
+    }
+    .text-content {
+      margin-left: 4px;
+      margin-top: 2px;
+    }
+    span {
+      color: #f19d38;
+    }
+  }
   .header {
     display: flex;
     justify-content: space-between;
@@ -510,14 +637,17 @@ const stopScroll = (evt) => {
       .form-item {
         flex: 1;
         .label {
-          height: 16px;
+          display: flex;
+          align-items: flex-start;
           margin-bottom: 8px;
           font-size: 12px;
           line-height: 16px;
           color: var(--dashboard-text-color);
+
           .require-asterisk {
-            margin-left: -3px;
+            margin-left: 2px;
             font-size: 16px;
+            line-height: 16px;
             color: #c9353f;
           }
         }
@@ -528,6 +658,11 @@ const stopScroll = (evt) => {
           .el-input {
             width: 100%;
           }
+          .is-error {
+            :deep(.el-input__wrapper) {
+              box-shadow: 0 0 0 1px red;
+            }
+          }
         }
       }
     }