Pārlūkot izejas kodu

Merge branch 'dev_zyh' of United_Software/k_online_ui into dev

Jack Zhou 1 gadu atpakaļ
vecāks
revīzija
e1e14c1f0d
22 mainītis faili ar 465 papildinājumiem un 200 dzēšanām
  1. 1 0
      package.json
  2. 2 1
      src/auto-imports.d.ts
  3. 37 10
      src/components/ContainerStatus/src/ContainerStatus.vue
  4. BIN
      src/components/ContainerStatus/src/image/no-data.png
  5. 11 4
      src/components/VBreadcrumb/src/VBreadcrumb.vue
  6. 1 0
      src/components/VSliderVerification/index.ts
  7. 235 0
      src/components/VSliderVerification/src/VSliderVerification.vue
  8. BIN
      src/components/VSliderVerification/src/image/icon_refresh_bold_b.png
  9. BIN
      src/components/VSliderVerification/src/image/verification-img-1.png
  10. BIN
      src/components/VSliderVerification/src/image/verification-img-2.png
  11. BIN
      src/components/VSliderVerification/src/image/verification-img-3.png
  12. BIN
      src/components/VSliderVerification/src/image/verification-img-4.png
  13. 5 5
      src/stores/modules/breadCrumb.ts
  14. 14 7
      src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue
  15. 57 91
      src/views/Login/src/loginView.vue
  16. 18 9
      src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue
  17. 19 11
      src/views/Tracking/src/components/PublicTracking/src/components/MilestonesTable.vue
  18. 14 7
      src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue
  19. 16 9
      src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue
  20. 18 10
      src/views/Tracking/src/components/TrackingDetail/src/components/MilestonesTable.vue
  21. 7 1
      src/views/Tracking/src/components/TrackingDetail/src/components/RoutesView.vue
  22. 10 35
      src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

+ 1 - 0
package.json

@@ -37,6 +37,7 @@
     "vue": "^3.4.29",
     "vue-draggable-plus": "^0.5.3",
     "vue-router": "^4.3.3",
+    "vue3-puzzle-vcode": "^1.1.7",
     "vuedraggable": "^2.24.3",
     "vxe-pc-ui": "^4.1.7",
     "vxe-table": "^4.7.70",

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

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

+ 37 - 10
src/components/ContainerStatus/src/ContainerStatus.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
+import emptyImage from './image/no-data.png'
 
 const props = defineProps({
   data: Object
@@ -28,22 +29,27 @@ watch(
 
 const formatTimezone = (time: string, timezone: string) => {
   if (!time) return '--'
-
-  const formattedTime =
-    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')
-    // 替换 "+07:00" 为 "GMT+7"
-    gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+  let formattedTime = ''
+  // 有时分秒才有添加时区的必要
+  if (time.length > 12) {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY hh:mm A')
+    let gmtOffset = ''
+    if (timezone && timezone.length > 3) {
+      const timeZoneOffset = dayjs().tz(timezone).format('Z')
+      // 替换 "+07:00" 为 "GMT+7"
+      gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+    }
+    return `${formattedTime} ${gmtOffset}`
+  } else {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY')
+    return formattedTime
   }
-  return `${formattedTime} ${gmtOffset}`
 }
 </script>
 
 <template>
   <div class="container-status">
-    <el-collapse class="container" v-model="activeNames">
+    <el-collapse class="container" v-if="containerStatusData.length !== 0" v-model="activeNames">
       <el-collapse-item
         :title="containers.label"
         v-for="(containers, name) in containerStatusData"
@@ -70,6 +76,10 @@ const formatTimezone = (time: string, timezone: string) => {
         </div>
       </el-collapse-item>
     </el-collapse>
+    <div v-else class="empty-content" style="">
+      <el-image :src="emptyImage" alt="empty"></el-image>
+      <div class="empty-text" style="">No data</div>
+    </div>
     <div class="footer">
       Tracking on carrier website:
       <a href="http://www.rcjgroup.com/" target="_blank" class="link">http://www.rcjgroup.com/</a>
@@ -88,6 +98,22 @@ const formatTimezone = (time: string, timezone: string) => {
     padding-bottom: 8px;
     overflow: auto;
   }
+  .empty-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    height: 394px;
+    .el-image {
+      width: 200px;
+      height: 200px;
+      margin-top: 20px;
+      object-fit: cover;
+    }
+    .empty-text {
+      margin-top: 8px;
+      color: #b5b9bf;
+    }
+  }
   .footer {
     line-height: 38px;
     color: #999;
@@ -162,6 +188,7 @@ const formatTimezone = (time: string, timezone: string) => {
         }
       }
       .right-country {
+        width: 94px;
         padding: 0 24px;
         line-height: 52px;
         font-weight: 700;

BIN
src/components/ContainerStatus/src/image/no-data.png


+ 11 - 4
src/components/VBreadcrumb/src/VBreadcrumb.vue

@@ -6,12 +6,17 @@ const router = useRouter()
 const breadCrumb = useBreadCrumb()
 
 const handleGoBack = () => {
-  router.go(-1)
+  const routeData = breadCrumb.getUpperRoute()
+  router.push({
+    name: routeData.label,
+    query: routeData.query
+  })
 }
-const jumpLink = (label: string) => {
+const jumpLink = (label: string, query: any) => {
   label &&
     router.push({
-      name: label
+      name: label,
+      query: query
     })
 }
 </script>
@@ -21,7 +26,9 @@ const jumpLink = (label: string) => {
     <span class="font_family icon-icon_back_b" @click="handleGoBack"></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 @click="jumpLink(routeItem.label, routeItem.query)" class="previous-route">{{
+          routeItem.label
+        }}</span>
         <span class="interval">|</span>
       </template>
       <span v-else style="font-weight: 700">{{ routeItem.label }}</span>

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

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

+ 235 - 0
src/components/VSliderVerification/src/VSliderVerification.vue

@@ -0,0 +1,235 @@
+<script lang="ts" setup>
+//滑动图片验证码部分
+import Img01 from './image/verification-img-1.png'
+import Img02 from './image/verification-img-2.png'
+import Img03 from './image/verification-img-3.png'
+import Img04 from './image/verification-img-4.png'
+import { ref } from 'vue'
+//引入'vue3-puzzle-vcode'插件
+import Vcode from 'vue3-puzzle-vcode'
+
+const openDialog = () => {
+  isShow.value = true
+  nextTick(() => {
+    addElement()
+    onSuccess()
+    updateSliderBackground('success')
+  })
+}
+// 添加自定义元素
+const addElement = () => {
+  addTipsNode()
+  addSliderBtnNode('start')
+  updateRefreshImg()
+}
+
+const updateRefreshImg = () => {
+  const targetNode: any = document.querySelector('img.reset_')
+  if (targetNode) {
+    // 使用 import.meta.url 结合 new URL 获取图片的正确路径
+    targetNode.src = new URL('./image/icon_refresh_bold_b.png', import.meta.url).href
+  }
+}
+const addTipsNode = () => {
+  const childNode = document.createElement('div')
+  childNode.className = 'tips'
+  childNode.innerHTML = `
+    <p>Please drag the slider below to complete the</p>
+    <p>verification to ensure normal access</p>
+  `
+  const parentNode = document.querySelector('.vue-auth-box_')
+  if (parentNode.firstChild) {
+    parentNode.insertBefore(childNode, parentNode.firstChild)
+  } else {
+    parentNode.appendChild(childNode)
+  }
+}
+
+const styleMap = {
+  start: {
+    thumbColor: 'var(--color-neutral-1)',
+    thumbIcon: 'icon-icon_drag__line_b',
+    trackBackground: 'var(--color-success)'
+  },
+  success: {
+    thumbColor: '#fff',
+    thumbIcon: 'icon-icon_confirm_b',
+    trackBackground: 'var(--color-success)'
+  },
+  error: {
+    thumbColor: '#fff',
+    thumbIcon: 'icon-icon_reject_b',
+    trackBackground: '#c7353f'
+  }
+}
+
+const addSliderBtnNode = (state: string) => {
+  const parentNode = document.querySelector('.range-btn')
+  if (state === 'start') {
+    parentNode.innerHTML = `
+      <span style="color: ${styleMap[state].thumbColor}" class="font_family ${styleMap[state].thumbIcon}"></span>
+  `
+  } else {
+    parentNode.innerHTML = `
+    <div class="icon-border" style="background: ${styleMap[state].trackBackground}">
+      <span style="color: ${styleMap[state].thumbColor}" class="font_family ${styleMap[state].thumbIcon}"></span>
+    </div>
+  `
+  }
+}
+
+const isShow = ref(false)
+
+// 根据不同的状态,设置已滑动区域的背景颜色
+const updateSliderBackground = (state: string) => {
+  const targetNode: any = document.querySelector('.range-slider')
+  targetNode.style.background = styleMap[state].trackBackground
+}
+
+const emit = defineEmits<{
+  close: []
+}>()
+// 监听验证成功事件,因为这里库中的成功事件有0.8秒的延迟,所以这里手动监听验证成功事件
+const onSuccess = () => {
+  // 选择要监听的目标元素
+  const targetNode = document.querySelector('.info-box_') // 将此选择器替换为你的实际目标
+  if (targetNode) {
+    const observer = new MutationObserver((mutationsList) => {
+      mutationsList.forEach((mutation) => {
+        if (mutation.type === 'childList') {
+          if ((mutation.target as any).innerHTML === 'Verification successful') {
+            updateSliderBackground('success')
+            addSliderBtnNode('success')
+            setTimeout(() => {
+              emit('close')
+              isShow.value = false
+            }, 500)
+          }
+        }
+      })
+    })
+
+    // 配置 MutationObserver,监听子节点的变化
+    const config = { childList: true, subtree: true }
+
+    // 启动监听
+    observer.observe(targetNode, config)
+  }
+}
+const close = () => {
+  isShow.value = true
+}
+const fail = () => {
+  updateSliderBackground('error')
+  addSliderBtnNode('error')
+  setTimeout(() => {
+    updateSliderBackground('start')
+    addSliderBtnNode('start')
+  }, 800)
+}
+
+defineExpose({
+  openDialog
+})
+</script>
+<template>
+  <Vcode
+    :show="isShow"
+    @close="close"
+    @fail="fail"
+    :canvasWidth="320"
+    :canvasHeight="180"
+    sliderText="Swipe to verify"
+    :sliderSize="38"
+    successText="Verification successful"
+    failText="Verification failed"
+    :range="5"
+    :imgs="[Img01, Img02, Img03, Img04]"
+  ></Vcode>
+</template>
+<style lang="scss" scoped>
+.slider-verification {
+  width: 400px;
+  height: 373px;
+  padding: 40px;
+  .tips {
+    text-align: center;
+  }
+}
+</style>
+<style lang="scss">
+// 整体框架
+.vue-auth-box_ {
+  width: 400px;
+  height: 373px;
+  padding: 40px;
+  .tips {
+    margin-bottom: 16px;
+    text-align: center;
+  }
+  .icon-border {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 16px !important;
+    height: 16px !important;
+    border-radius: 50%;
+    border: none !important;
+    span {
+      font-size: 14px !important;
+    }
+  }
+}
+
+div.vue-puzzle-vcode {
+  background-color: rgba(43, 47, 54, 0.7);
+}
+
+.vue-auth-box_ .auth-control_ div.range-box {
+  background-color: #87909e;
+  box-shadow: none;
+}
+.vue-auth-box_ div.auth-body_ {
+  border-radius: 6px;
+}
+// 已滑动区域的样式
+.vue-auth-box_ .auth-control_ .range-box div.range-slider {
+  background-color: var(--color-success);
+}
+
+.vue-auth-box_ .auth-body_ div.loading-box_.hide_ {
+  display: none;
+}
+
+// 成功的提示
+.vue-auth-box_ .auth-body_ div.info-box_ {
+  background: var(--color-success);
+}
+// 失败的提示
+.vue-auth-box_ .auth-body_ div.info-box_.fail {
+  background: #c7353f;
+}
+
+.vue-auth-box_ .auth-control_ .range-box .range-slider {
+  border-radius: 6px;
+}
+
+.vue-auth-box_ .auth-control_ div.range-box {
+  border-radius: 6px;
+}
+// 滑块
+.vue-auth-box_ .auth-control_ .range-box .range-slider .range-btn {
+  border-radius: 6px;
+}
+
+.vue-auth-box_ .auth-body_ .reset_ {
+  width: 20px;
+  height: 20px;
+  top: 10px;
+  right: 10px;
+}
+
+.vue-auth-box_ .auth-control_ .range-box .range-text {
+  color: #fff;
+}
+</style>

BIN
src/components/VSliderVerification/src/image/icon_refresh_bold_b.png


BIN
src/components/VSliderVerification/src/image/verification-img-1.png


BIN
src/components/VSliderVerification/src/image/verification-img-2.png


BIN
src/components/VSliderVerification/src/image/verification-img-3.png


BIN
src/components/VSliderVerification/src/image/verification-img-4.png


+ 5 - 5
src/stores/modules/breadCrumb.ts

@@ -21,9 +21,7 @@ export const useBreadCrumb = defineStore('breadCrumb', {
       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') {
+      } else if (toRoute.name === 'Public Tracking Detail') {
         this.routeList = [
           {
             label: 'Public Tracking',
@@ -36,8 +34,7 @@ export const useBreadCrumb = defineStore('breadCrumb', {
             query: toRoute.query
           }
         ]
-      }
-      if (toRoute.name && whiteList.includes(toRoute.name)) {
+      } else if (toRoute.name && whiteList.includes(toRoute.name)) {
         this.routeList.push({
           label: toRoute.name,
           path: toRoute.path,
@@ -53,6 +50,9 @@ export const useBreadCrumb = defineStore('breadCrumb', {
         ]
       }
       localStorage.setItem('routeList', JSON.stringify(this.routeList))
+    },
+    getUpperRoute() {
+      return this.routeList[this.routeList.length - 2]
     }
   }
 })

+ 14 - 7
src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue

@@ -93,14 +93,21 @@ getData()
 
 const formatTimezone = (time: string, timezone: string) => {
   if (!time) return '--'
-  const formattedTime = dayjs(time).format('MMM-DD-YYYY')
-  let gmtOffset = ''
-  if (timezone && timezone.length > 3) {
-    const timeZoneOffset = dayjs().tz(timezone).format('Z')
-    // 替换 "+07:00" 为 "GMT+7"
-    gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+  let formattedTime = ''
+  // 有时分秒才有添加时区的必要
+  if (time.length > 12) {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY hh:mm A')
+    let gmtOffset = ''
+    if (timezone && timezone.length > 3) {
+      const timeZoneOffset = dayjs().tz(timezone).format('Z')
+      // 替换 "+07:00" 为 "GMT+7"
+      gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+    }
+    return `${formattedTime} ${gmtOffset}`
+  } else {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY')
+    return formattedTime
   }
-  return `${formattedTime} ${gmtOffset}`
 }
 
 const originRef = ref()

+ 57 - 91
src/views/Login/src/loginView.vue

@@ -4,6 +4,7 @@ import ErrorTips from './components/ErrorTips.vue'
 import { useUserStore } from '@/stores/modules/user'
 import ScoringSystem from '@/views/Dashboard/src/components/ScoringSystem.vue'
 import CryptoJS from 'crypto-js'
+import dayjs from 'dayjs'
 
 const router = useRouter()
 const route = useRoute()
@@ -11,8 +12,7 @@ const route = useRoute()
 const loginForm = ref({
   username: '',
   password: '',
-  email: '',
-  code: ''
+  email: ''
 })
 
 const isRememerPwd = ref(false)
@@ -81,63 +81,76 @@ watch(status, () => {
   loginForm.value = {
     username: '',
     password: '',
-    email: '',
-    code: ''
+    email: ''
   }
   loginError.value = {
     username: false,
     password: false,
-    email: false,
-    code: false
+    email: false
   }
   isRememerPwd.value = false
   isUserNameExit.value = false
-  verificationCode.value = ''
-  getCode()
 })
 
 const loginError: any = ref({
   username: false,
   password: false,
-  email: false,
-  code: false
-})
-const verificationCode = ref()
-const loading = ref(false)
-// 获取验证码
-const getCode = () => {
-  if (loading.value) return
-  loading.value = true
-  $api
-    .getVerifcationCode()
-    .then((res: any) => {
-      if (res.code === 200) {
-        verificationCode.value = `data:image/png;base64,${res.data.imagePngBase64}`
-      } else {
-        verificationCode.value = ''
-      }
-    })
-    .finally(() => {
-      loading.value = false
-    })
-}
-onMounted(() => {
-  getCode()
+  email: false
 })
 
 const userStore = useUserStore()
 
+const sliderVerificationRef = ref()
+const sliderVerificationFn = () => {
+  isShowSliderVerification.value = true
+  nextTick(() => {
+    sliderVerificationRef.value.openDialog()
+  })
+}
+const isShowSliderVerification = ref(false)
+
+const confirmVerifyStatus = ref('')
+const secretVerificationKey = 'fT5!R1k$7Mv@4Q9X'
+const fixedIv = '1234567890123456' // 固定的初始化向量,长度为16字符
+// 判断用户是否完成验证,完成后生成一个密文传给后端
+// AES 加密函数
+const encryptVerificationPwd = (password) => {
+  const key = CryptoJS.enc.Utf8.parse(secretVerificationKey) // 解析密钥
+  const iv = CryptoJS.enc.Utf8.parse(fixedIv) // 解析IV
+  const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(password), key, { iv: iv })
+  return encrypted.toString()
+}
+
 // 点击登录按钮
-const handleLogin = () => {
+const handleVerification = () => {
   if (!isUserNameExit.value || !loginForm.value.username) {
     return
   }
+  sliderVerificationFn()
   // 这里是登录逻辑
+  // handleLoginAfterVerify()
+}
+// 验证结束后通过status值判断调登录还是忘记密码接口
+const confirmVerification = () => {
+  // 生成验证成功的密文
+  const pwd = dayjs().unix()
+  confirmVerifyStatus.value = encryptVerificationPwd(pwd)
+  isShowSliderVerification.value = false
+
+  if (status.value === 'login') {
+    handleLoginAfterVerify()
+  } else {
+    handleSendPassword()
+  }
+}
+
+// 验证结束后调用登录接口
+const handleLoginAfterVerify = () => {
   $api
     .login({
       uname: loginForm.value.username,
       psw: loginForm.value.password,
-      verifcation_code: loginForm.value.code
+      verifcation_code: confirmVerifyStatus.value
     })
     .then((res: any) => {
       if (res.code === 200) {
@@ -168,11 +181,8 @@ const handleLogin = () => {
         router.push('/')
       } else if (res.code === 400) {
         const { data } = res
-        // 验证码错误
         if (data.msg === 'password_error') {
           loginError.value.password = true
-        } else if (data.msg === 'verifcation_error') {
-          loginError.value.code = true
         } else if (data.msg === 'error_times') {
           errorTipsRef.value.openDialog()
         } else if (data.msg === 'passwordExpires') {
@@ -186,12 +196,8 @@ const handleLogin = () => {
             name: 'Reset Password'
           })
         }
-        getCode()
       }
     })
-    .catch(() => {
-      getCode()
-    })
 }
 
 // 从忘记密码返回登录
@@ -225,21 +231,13 @@ const handleSendPassword = () => {
     .forgotPassword({
       login: loginForm.value.username,
       email: loginForm.value.email,
-      verifcation_code: loginForm.value.code
+      verifcation_code: confirmVerifyStatus.value
     })
     .then((res: any) => {
       if (res.code === 200) {
         backLogin(true)
-      } else if (res.code === 400) {
-        const { data } = res
-        if (data.msg === 'verifcation_error') {
-          loginError.value.code = true
-        }
       }
     })
-    .finally(() => {
-      getCode()
-    })
 }
 
 const isEmailTips = ref(false)
@@ -323,33 +321,14 @@ const errorTipsRef = ref()
           </template>
         </el-input>
         <div class="error" v-if="loginError.password">Incorrect password. Please try again.</div>
-        <el-input
-          ref="codeRef"
-          :class="{ 'is-error': loginError.code }"
-          class="verification-code"
-          v-model="loginForm.code"
-          placeholder="Verification Code"
-          @focus="handleDeleteEmailTips('code')"
-          @keyup.enter="handleLogin"
-        >
-          <template #append>
-            <img
-              v-vloading="loading"
-              @click="getCode"
-              class="verification-code-img"
-              :src="verificationCode"
-              alt=""
-            />
-          </template>
-        </el-input>
-        <div class="error" v-if="loginError.code">Incorrect verification code.</div>
+
         <el-checkbox
           class="remember-password"
           v-model="isRememerPwd"
           label="Remember Password"
           size="large"
         />
-        <el-button @click="handleLogin" class="el-button--dark login-btn">Login</el-button>
+        <el-button @click="handleVerification" class="el-button--dark login-btn">Login</el-button>
       </div>
       <template #footer>
         <div class="license">
@@ -398,25 +377,8 @@ const errorTipsRef = ref()
           </template>
         </el-input>
         <div class="error" v-if="loginError.email">Incorrect email. Please try again.</div>
-        <el-input
-          ref="codeRef"
-          :class="{ 'is-error': loginError.code }"
-          class="verification-code"
-          v-model="loginForm.code"
-          placeholder="Verification Code"
-          @focus="handleDeleteEmailTips('code')"
-          @keyup.enter="handleSendPassword"
-          ><template #append>
-            <img
-              v-vloading="loading"
-              class="verification-code-img"
-              :src="verificationCode"
-              @click="getCode"
-              alt=""
-            /> </template
-        ></el-input>
-        <div class="error" v-if="loginError.code">Incorrect verification code.</div>
-        <el-button @click="handleSendPassword" class="el-button--dark login-btn"
+
+        <el-button @click="handleVerification" class="el-button--dark login-btn"
           >Send Password</el-button
         >
         <div @click="backLogin(false)" class="back-text">
@@ -431,7 +393,11 @@ const errorTipsRef = ref()
         </div>
       </template>
     </el-card>
-
+    <VSliderVerification
+      v-if="isShowSliderVerification"
+      @close="confirmVerification"
+      ref="sliderVerificationRef"
+    ></VSliderVerification>
     <ErrorTips ref="errorTipsRef" @forget-password="status = 'reset'"></ErrorTips>
   </div>
 </template>

+ 18 - 9
src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue

@@ -57,7 +57,10 @@ const handleSearchNo = () => {
       } else if (res.code === 400) {
         const { data } = res
         if (data.msg === 'visit limit') {
-          slideVerifyRef.value?.openDialog()
+          isShowSliderVerification.value = true
+          nextTick(() => {
+            sliderVerificationRef.value?.openDialog()
+          })
         }
       }
     })
@@ -67,7 +70,14 @@ const handleSearchNo = () => {
     })
 }
 
-const slideVerifyRef = ref<InstanceType<typeof SlideVerify> | null>(null)
+const sliderVerificationRef = ref()
+const isShowSliderVerification = ref(false)
+const confirmVerification = () => {
+  const pwd = dayjs().unix()
+  confirmVerifyStatus.value = encryptPassword(pwd)
+  handleSearchNo()
+}
+
 const secretKey = 'fT5!R1k$7Mv@4Q9X'
 const fixedIv = '1234567890123456' // 固定的初始化向量,长度为16字符
 
@@ -78,12 +88,6 @@ const encryptPassword = (password) => {
   const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(password), key, { iv: iv })
   return encrypted.toString()
 }
-
-// 验证滑块成功
-const confirmVerify = () => {
-  const pwd = dayjs().unix()
-  confirmVerifyStatus.value = encryptPassword(pwd)
-}
 </script>
 
 <template>
@@ -126,7 +130,12 @@ const confirmVerify = () => {
         </VEmpty>
       </div>
     </div>
-    <SlideVerify ref="slideVerifyRef" @verify-success="confirmVerify"></SlideVerify>
+    <!-- <SlideVerify ref="slideVerifyRef" @verify-success="confirmVerify"></SlideVerify> -->
+    <VSliderVerification
+      v-if="isShowSliderVerification"
+      @close="confirmVerification"
+      ref="sliderVerificationRef"
+    ></VSliderVerification>
   </div>
 </template>
 

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

@@ -47,17 +47,21 @@ const handleColumns = (columns: any) => {
         ...curColumn,
         formatter: ({ cellValue, row }: any) => {
           if (!cellValue) return '--'
-          const formattedTime =
-            cellValue.length > 12
-              ? dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
-              : dayjs(cellValue).format('MMM-DD-YYYY')
-          let gmtOffset = ''
-          if (row.timezone && row.timezone.length > 3) {
-            const timeZoneOffset = dayjs().tz(row.timezone).format('Z')
-            // 替换 "+07:00" 为 "GMT+7"
-            gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+          let formattedTime = ''
+          // 有时分秒才有添加时区的必要
+          if (cellValue.length > 12) {
+            formattedTime = dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
+            let gmtOffset = ''
+            if (row.timezone && row.timezone.length > 3) {
+              const timeZoneOffset = dayjs().tz(row.timezone).format('Z')
+              // 替换 "+07:00" 为 "GMT+7"
+              gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+            }
+            return `${formattedTime} ${gmtOffset}`
+          } else {
+            formattedTime = dayjs(cellValue).format('MMM-DD-YYYY')
+            return formattedTime
           }
-          return `${formattedTime} ${gmtOffset}`
         }
       }
     }
@@ -90,7 +94,11 @@ useRowClickStyle(tableRef)
 
 <template>
   <div class="containers">
-    <vxe-grid ref="tableRef" :style="{ border: 'none' }" v-bind="tableData"> </vxe-grid>
+    <vxe-grid ref="tableRef" :style="{ border: 'none' }" v-bind="tableData">
+      <template #empty>
+        <div class="empty">No data</div>
+      </template>
+    </vxe-grid>
   </div>
 </template>
 

+ 14 - 7
src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue

@@ -70,14 +70,21 @@ if (Object.keys(sharedData).length === 0) {
 
 const formatTimezone = (time: string, timezone: string) => {
   if (!time) return '--'
-  const formattedTime = dayjs(time).format('MMM-DD-YYYY')
-  let gmtOffset = ''
-  if (timezone && timezone.length > 3) {
-    const timeZoneOffset = dayjs().tz(timezone).format('Z')
-    // 替换 "+07:00" 为 "GMT+7"
-    gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+  let formattedTime = ''
+  // 有时分秒才有添加时区的必要
+  if (time.length > 12) {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY hh:mm A')
+    let gmtOffset = ''
+    if (timezone && timezone.length > 3) {
+      const timeZoneOffset = dayjs().tz(timezone).format('Z')
+      // 替换 "+07:00" 为 "GMT+7"
+      gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+    }
+    return `${formattedTime} ${gmtOffset}`
+  } else {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY')
+    return formattedTime
   }
-  return `${formattedTime} ${gmtOffset}`
 }
 
 const originRef = ref()

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

@@ -110,16 +110,23 @@ const getData = () => {
 }
 getData()
 
-const formatTimezone = (tiem: string, timezone: string) => {
-  if (!tiem) return '--'
-  const formattedTime = dayjs(tiem).format('MMM-DD-YYYY')
-  let gmtOffset = ''
-  if (timezone && timezone.length > 3) {
-    const timeZoneOffset = dayjs().tz(timezone).format('Z')
-    // 替换 "+07:00" 为 "GMT+7"
-    gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+const formatTimezone = (time: string, timezone: string) => {
+  if (!time) return '--'
+  let formattedTime = ''
+  // 有时分秒才有添加时区的必要
+  if (time.length > 12) {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY hh:mm A')
+    let gmtOffset = ''
+    if (timezone && timezone.length > 3) {
+      const timeZoneOffset = dayjs().tz(timezone).format('Z')
+      // 替换 "+07:00" 为 "GMT+7"
+      gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+    }
+    return `${formattedTime} ${gmtOffset}`
+  } else {
+    formattedTime = dayjs(time).format('MMM-DD-YYYY')
+    return formattedTime
   }
-  return `${formattedTime} ${gmtOffset}`
 }
 
 const originRef = ref()

+ 18 - 10
src/views/Tracking/src/components/TrackingDetail/src/components/MilestonesTable.vue

@@ -47,17 +47,22 @@ const handleColumns = (columns: any) => {
         ...curColumn,
         formatter: ({ row, cellValue }: any) => {
           if (!cellValue) return '--'
-          const formattedTime =
-            cellValue.length > 12
-              ? dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
-              : dayjs(cellValue).format('MMM-DD-YYYY')
-          let gmtOffset = ''
-          if (row.timezone && row.timezone.length > 3) {
-            const timeZoneOffset = dayjs().tz(row.timezone).format('Z')
-            // 替换 "+07:00" 为 "GMT+7"
-            gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+
+          let formattedTime = ''
+          // 有时分秒才有添加时区的必要
+          if (cellValue.length > 12) {
+            formattedTime = dayjs(cellValue).format('MMM-DD-YYYY hh:mm A')
+            let gmtOffset = ''
+            if (row.timezone && row.timezone.length > 3) {
+              const timeZoneOffset = dayjs().tz(row.timezone).format('Z')
+              // 替换 "+07:00" 为 "GMT+7"
+              gmtOffset = `(GMT${timeZoneOffset.slice(0, 3)})`
+            }
+            return `${formattedTime} ${gmtOffset}`
+          } else {
+            formattedTime = dayjs(cellValue).format('MMM-DD-YYYY')
+            return formattedTime
           }
-          return `${formattedTime} ${gmtOffset}`
         }
       }
     }
@@ -90,6 +95,9 @@ useRowClickStyle(tableRef)
 <template>
   <div class="Milestones">
     <vxe-grid class="radius-bottom" ref="tableRef" :style="{ border: 'none' }" v-bind="tableData">
+      <template #empty>
+        <div class="empty">No data</div>
+      </template>
     </vxe-grid>
   </div>
 </template>

+ 7 - 1
src/views/Tracking/src/components/TrackingDetail/src/components/RoutesView.vue

@@ -51,7 +51,13 @@ watch(
 )
 
 const formatDate = (date: string) => {
-  return date ? dayjs(date).format('MMM-DD-YYYY HH:mm A') : '--'
+  if (!date) {
+    return '--'
+  } else {
+    return date.length > 12
+      ? dayjs(date).format('MMM-DD-YYYY hh:mm A')
+      : dayjs(date).format('MMM-DD-YYYY')
+  }
 }
 
 const basicOriginRef = ref()

+ 10 - 35
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -260,42 +260,17 @@ 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 checkField = (field, errorKey) => {
+    const fieldValue = generalInfo.value.formData[field]
+    isVerificationError.value[errorKey] =
+      fieldValue === null || fieldValue === undefined || fieldValue === ''
   }
+
+  checkField('Submitter', 'submitter')
+  checkField('signature', 'signature')
+  checkField('authorized_email', 'authorized_email')
+  checkField('authorized_tel', 'authorized_tel')
 }
 
 const isVerification = (value) => {