Răsfoiți Sursa

feat: 修改更改密码逻辑

zhouyuhao 1 an în urmă
părinte
comite
1f22450705

+ 0 - 1
package.json

@@ -21,7 +21,6 @@
     "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",

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

@@ -92,3 +92,18 @@ export const getVGMData = (params: any, config: any) => {
     config
   )
 }
+
+/**
+ * 保存 vgm页面数据
+ */
+export const saveVGMData = (params: any, config: any) => {
+  return HttpAxios.get(
+    `${baseUrl}`,
+    {
+      action: 'ocean_order',
+      operate: 'save_ocean_vgm',
+      ...params
+    },
+    config
+  )
+}

+ 7 - 0
src/router/index.ts

@@ -1,4 +1,5 @@
 import { createRouter, createWebHistory } from 'vue-router'
+import { useUserStore } from '@/stores/modules/user'
 
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
@@ -97,6 +98,8 @@ router.beforeEach(async (to, from, next) => {
   const whiteList = ['/login', '/public-tracking', '/public-tracking/detail', '/reset-password']
   // 判断是否登录
   if (!whiteList.includes(to.path) && !localStorage.getItem('username')) {
+    const userStore = useUserStore()
+    userStore.clearUsername()
     if (whiteList.includes(from.path)) {
       ElMessage.warning('Please log in to use this feature.')
       next(false)
@@ -105,6 +108,10 @@ router.beforeEach(async (to, from, next) => {
       next('/public-tracking')
     }
   }
+  if (to.path === '/Login') {
+    const userStore = useUserStore()
+    userStore.clearUsername()
+  }
   next()
 })
 

+ 4 - 0
src/stores/modules/user.ts

@@ -20,6 +20,10 @@ export const useUserStore = defineStore('user', {
           this.username = ''
         }
       })
+    },
+    clearUsername() {
+      localStorage.removeItem('username')
+      this.username = ''
     }
   }
 })

+ 44 - 0
src/styles/elementui.scss

@@ -442,6 +442,50 @@ div .el-badge {
   margin: 8px 0 0 8px;
 }
 
+.el-date-table td.current:not(.disabled) span.el-date-table-cell__text {
+  background-color: var(--color-theme);
+}
+.el-month-table td.current:not(.disabled) span.el-date-table-cell__text {
+  background-color: var(--color-theme);
+}
+.el-month-table td span.el-date-table-cell__text:hover {
+  color: var(--color-theme);
+}
+.el-year-table td.current:not(.disabled) span.el-date-table-cell__text {
+  background-color: var(--color-theme);
+}
+.el-year-table td.today span.el-date-table-cell__text {
+  color: var(--color-theme);
+}
+.el-year-table td span.el-date-table-cell__text:hover {
+  color: var(--color-theme);
+}
+div.el-date-picker__header {
+  padding-top: 4px;
+}
+span.el-date-picker__header-label {
+  display: inline-block;
+  margin-top: 11px;
+}
+div.el-picker-panel__content {
+  margin-top: 6px;
+}
+button.el-picker-panel__icon-btn:hover {
+  color: var(--color-theme);
+}
+span.el-date-picker__header-label:hover {
+  color: var(--color-theme);
+}
+button.el-time-panel__btn.confirm {
+  color: var(--color-theme);
+}
+.el-picker-panel__footer {
+  .el-button.is-plain {
+    --el-button-hover-text-color: var(--color-theme);
+    --el-button-hover-border-color: var(--color-theme);
+  }
+}
+
 div .el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
   background-color: var(--color-table-header-bg);
 }

+ 3 - 1
src/utils/axios.ts

@@ -1,6 +1,7 @@
 import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios'
 import router from '@/router'
 import { ElMessage, ElMessageBox } from 'element-plus'
+import { useUserStore } from '@/stores/modules/user'
 // import {
 //   showFullScreenLoading,
 //   tryHideFullScreenLoading
@@ -55,10 +56,11 @@ class HttpAxios {
   _responseInterceptors = (response: AxiosResponse) => {
     if (response.status === 200) {
       if (response.data.code === 401 || response.data.code === 403) {
+        const userStore = useUserStore()
+        userStore.clearUsername()
         router.push('/login')
         ElMessage.warning('Please log in to use this feature.')
       } else if (response.data.code !== 200 && response.data.code !== 400) {
-        console.log(response.data)
         ElMessageBox.alert(
           response.data?.data?.msg || 'The request failed. Please try again later',
           'Prompt',

+ 111 - 13
src/views/Layout/src/components/Header/components/ChangePasswordDialog.vue

@@ -15,20 +15,51 @@ const loginError: any = ref({
   newPassword: false,
   confirmPassword: false
 })
+const newPwdErrTips = ref('')
 
 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')
+  if (pwdData.value.newPassword !== pwdData.value.confirmPassword) {
+    loginError.value.confirmPassword = true
+    return
+  }
+  if (pwdData.value.newPassword.length < 12 || pwdData.value.newPassword.length > 20) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password length between 12 - 20'
+    return
+  }
+  if (!/[A-Z]/.test(pwdData.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain uppercase letters'
+    return
+  }
+  if (!/[a-z]/.test(pwdData.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain lowercase letters'
+    return
+  }
+  if (!/[0-9]/.test(pwdData.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain numbers'
+    return
+  }
+  $api
+    .changePwdByLogin({
+      opsw: pwdData.value.oldPassword,
+      npsw: pwdData.value.newPassword
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        ElMessage.success('Password updated successfully')
+        dialogVisible.value = false
+      } else if (res.code === 400) {
+        if (res.msg === 'Old password is incorrect') {
+          loginError.value.oldPassword = true
+        } else {
+          loginError.value.newPassword = true
+          newPwdErrTips.value = res.msg
+        }
+      }
+    })
 }
 
 const hiddenError = (key: string) => {
@@ -42,6 +73,26 @@ const confirmPwd = () => {
   }
 }
 
+const hasUppercase = ref(false)
+const hasLowercase = ref(false)
+const hasNumber = ref(false)
+const isValidLength = ref(false)
+const checkPassword = () => {
+  const pwd = pwdData.value.newPassword
+
+  // 检测是否包含大写字母
+  hasUppercase.value = /[A-Z]/.test(pwd)
+
+  // 检测是否包含小写字母
+  hasLowercase.value = /[a-z]/.test(pwd)
+
+  // 检测是否包含数字
+  hasNumber.value = /[0-9]/.test(pwd)
+
+  // 检测长度是否符合要求
+  isValidLength.value = pwd.length >= 12 && pwd.length <= 20
+}
+
 const clearData = () => {
   pwdData.value = {
     oldPassword: '',
@@ -94,12 +145,31 @@ defineExpose({
           :class="{ 'is-error': loginError.newPassword }"
           placeholder="New password must contain both letter and numeral"
           @focus="hiddenError('newPassword')"
+          @input="checkPassword"
         >
           <template #prefix>
             <span class="font_family icon-icon_password_b"></span>
           </template>
         </el-input>
-        <div class="error" v-if="loginError.newPassword">Incorrect password. Please try again.</div>
+        <div class="error" v-if="loginError.newPassword">{{ newPwdErrTips }}</div>
+        <div class="limit-tips">
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasUppercase }"></span>
+            <span>Password must contain uppercase letters</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasLowercase }"></span>
+            <span>Password must contain lowercase letters</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasNumber }"></span>
+            <span>Password must contain numbers</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: isValidLength }"></span>
+            <span>Password length between12 - 20 </span>
+          </div>
+        </div>
       </div>
       <div class="form-item">
         <span class="label">Confirm Password</span>
@@ -147,6 +217,34 @@ defineExpose({
       color: var(--color-danger);
       line-height: 14px;
     }
+    .limit-tips {
+      margin-top: 10px;
+      .tip-item {
+        display: flex;
+        align-items: center;
+        margin-bottom: 8px;
+        font-size: 12px;
+        color: var(--color-neutral-2);
+        .font_family {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          height: 12px;
+          width: 12px;
+          padding-top: 1px;
+          background-color: #b5b9bf;
+          font-size: 10px;
+          color: white;
+          border-radius: 50%;
+          &.active {
+            background-color: #00a870;
+          }
+        }
+        span {
+          margin-left: 4px;
+        }
+      }
+    }
     .el-input {
       height: 40px;
       &.is-error {

+ 110 - 7
src/views/Login/src/components/ChangePasswordCard.vue

@@ -9,6 +9,7 @@ const loginForm = ref({
   confirmPassword: ''
 })
 
+const newPwdErrTips = ref('')
 const loginError: any = ref({
   oldPassword: false,
   newPassword: false,
@@ -16,6 +17,30 @@ const loginError: any = ref({
 })
 
 const handleChangePwd = () => {
+  if (loginForm.value.newPassword !== loginForm.value.confirmPassword) {
+    loginError.value.confirmPassword = true
+    return
+  }
+  if (loginForm.value.newPassword.length < 12 || loginForm.value.newPassword.length > 20) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password length between 12 - 20'
+    return
+  }
+  if (!/[A-Z]/.test(loginForm.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain uppercase letters'
+    return
+  }
+  if (!/[a-z]/.test(loginForm.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain lowercase letters'
+    return
+  }
+  if (!/[0-9]/.test(loginForm.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain numbers'
+    return
+  }
   $api
     .resetPwd({
       uname: loginForm.value.username,
@@ -24,7 +49,19 @@ const handleChangePwd = () => {
     })
     .then((res: any) => {
       if (res.code === 200) {
-        console.log(res)
+        router.push({
+          name: 'Login',
+          query: {
+            status: 'login'
+          }
+        })
+      } else if (res.code === 400) {
+        if (res.msg === 'Old password is incorrect') {
+          loginError.value.oldPassword = true
+        } else {
+          loginError.value.newPassword = true
+          newPwdErrTips.value = res.msg
+        }
       }
     })
 }
@@ -42,11 +79,7 @@ const confirmPwd = () => {
 
 const isUserNameExit = ref(false)
 
-const emit = defineEmits<{
-  changeStatus: [string]
-}>()
 const handleForgot = () => {
-  emit('changeStatus', 'reset')
   router.push({
     name: 'Login',
     query: {
@@ -55,7 +88,6 @@ const handleForgot = () => {
   })
 }
 const handleBackLogin = () => {
-  emit('changeStatus', 'login')
   router.push({
     name: 'Login',
     query: {
@@ -63,6 +95,26 @@ const handleBackLogin = () => {
     }
   })
 }
+
+const hasUppercase = ref(false)
+const hasLowercase = ref(false)
+const hasNumber = ref(false)
+const isValidLength = ref(false)
+const checkPassword = () => {
+  const pwd = loginForm.value.newPassword
+
+  // 检测是否包含大写字母
+  hasUppercase.value = /[A-Z]/.test(pwd)
+
+  // 检测是否包含小写字母
+  hasLowercase.value = /[a-z]/.test(pwd)
+
+  // 检测是否包含数字
+  hasNumber.value = /[0-9]/.test(pwd)
+
+  // 检测长度是否符合要求
+  isValidLength.value = pwd.length >= 12 && pwd.length <= 20
+}
 </script>
 
 <template>
@@ -114,15 +166,35 @@ const handleBackLogin = () => {
           placeholder="Please input password"
           show-password
           @focus="hiddenError('newPassword')"
+          @input="checkPassword"
         >
           <template #prefix>
             <span class="font_family icon-icon_password_b"></span>
           </template>
         </el-input>
-        <div class="error" v-if="loginError.newPassword">Incorrect password. Please try again.</div>
+        <div class="error" v-if="loginError.newPassword">{{ newPwdErrTips }}</div>
+        <div class="limit-tips">
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasUppercase }"></span>
+            <span>Password must contain uppercase letters</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasLowercase }"></span>
+            <span>Password must contain lowercase letters</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasNumber }"></span>
+            <span>Password must contain numbers</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: isValidLength }"></span>
+            <span>Password length between12 - 20 </span>
+          </div>
+        </div>
         <div class="label">
           <span>Confirm New Password</span>
         </div>
+
         <el-input
           ref="passWordRef"
           :class="{ 'is-error': loginError.confirmPassword }"
@@ -239,6 +311,37 @@ const handleBackLogin = () => {
       }
     }
   }
+  .limit-tips {
+    margin-top: 10px;
+    .tip-item {
+      display: flex;
+      align-items: center;
+      margin-bottom: 8px;
+      font-size: 12px;
+      color: var(--color-neutral-2);
+      &:nth-last-child(1) {
+        margin-bottom: 0;
+      }
+      .font_family {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        height: 12px;
+        width: 12px;
+        padding-top: 1px;
+        background-color: #b5b9bf;
+        font-size: 10px;
+        color: white;
+        border-radius: 50%;
+        &.active {
+          background-color: #00a870;
+        }
+      }
+      span {
+        margin-left: 4px;
+      }
+    }
+  }
   .verification-code {
     margin-top: 16px;
     .verification-code-img {

+ 1 - 5
src/views/Login/src/loginView.vue

@@ -2,7 +2,6 @@
 import { useRouter, useRoute } 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()
@@ -315,10 +314,7 @@ const test = () => {
         </div>
       </template>
     </el-card>
-    <ChangePasswordCard
-      @changeStatus="handleChangeStatus"
-      v-else-if="status === 'changePwd'"
-    ></ChangePasswordCard>
+
     <ErrorTips ref="errorTipsRef" @forget-password="status = 'reset'"></ErrorTips>
   </div>
 </template>

+ 25 - 16
src/views/Tracking/src/components/TrackingDetail/src/components/MapView.vue

@@ -45,7 +45,6 @@ const resetZoomLevel = ref(5) // 默认缩放级别
 // 初始化地图(不添加标记)
 const initMap = () => {
   if (map) {
-    // 如果地图已经初始化,直接返回
     return
   }
 
@@ -56,8 +55,9 @@ const initMap = () => {
     attribution:
       '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
   }).addTo(map)
+}
 
-  // 创建自定义重置缩放按钮控件
+const addResetZoomButton = (center: L.LatLng, zoom: number) => {
   const ResetZoomControl = L.Control.extend({
     options: {
       position: 'topleft'
@@ -65,7 +65,7 @@ const initMap = () => {
     onAdd: function () {
       const container = L.DomUtil.create('div', 'reset-zoom-control leaflet-bar')
 
-      // 创建重置缩放级别的按钮
+      // 创建重置缩放按钮
       const resetZoomButton = L.DomUtil.create('a', 'reset-zoom-button leaflet-bar-part', container)
       resetZoomButton.href = '#'
       resetZoomButton.title = 'Reset Zoom'
@@ -74,27 +74,31 @@ const initMap = () => {
         </div>`
       resetZoomButton.setAttribute('role', 'button')
 
-      // 在点击时,使用响应式变量控制重置行为
+      // 点击按钮时重置地图到首次标记后的缩放和中心
       resetZoomButton.addEventListener('click', () => {
-        map!.setView(resetZoomCenter.value, resetZoomLevel.value) // 恢复到指定中心和缩放级别
+        map!.setView(center, zoom)
       })
 
       return container
     }
   })
 
-  // 添加自定义重置缩放按钮控件到地图
-  new ResetZoomControl().addTo(map)
+  // 添加重置按钮到地图
+  new ResetZoomControl().addTo(map!)
 }
 
-// 根据接口数据在地图上添加标记
+// 定义首次渲染地图的中心和缩放级别
+let initialCenter: L.LatLng | null = null
+let initialZoomLevel: number | null = null
+let isFirstRender = true // 标记是否为首次渲染
+
+// 添加标记后更新中心和缩放级别
 const addMarkersToMap = () => {
   if (!map) return // 确保地图已经初始化
   const latLngBounds: any = [] // 用来存储所有标记的坐标
   markerPositions.value.forEach((position) => {
     const marker = L.marker([position.lat, position.lng], { icon: position.icon }).addTo(map)
 
-    // 弹出窗口内容
     const customPopupContent = `
       <div class="popup-content">
         <p class="label">${position.label}</p>
@@ -111,17 +115,22 @@ const addMarkersToMap = () => {
         closeOnClick: false
       })
       .openPopup()
-    // 将标记的经纬度添加到 latLngBounds 中
     latLngBounds.push([position.lat, position.lng])
   })
-  // 如果有标记,则自动调整视图
+
   if (latLngBounds.length > 0) {
     const bounds = L.latLngBounds(latLngBounds)
-    map!.fitBounds(bounds, { paddingTopLeft: [20, 20], paddingBottomRight: [400, 40] }) // 自动缩放并调整视图,padding 可调整视图边距
+    map!.fitBounds(bounds, { paddingTopLeft: [20, 20], paddingBottomRight: [400, 40] })
 
-    // 更新重置按钮的中心为地图当前的中心
-    // resetZoomCenter.value = bounds.getCenter() // 更新中心点为数据中心
-    // resetZoomLevel.value = map!.getZoom() // 更新缩放级别为当前缩放级别
+    // 首次添加标记时保存中心和缩放级别
+    if (isFirstRender) {
+      initialCenter = bounds.getCenter() // 保存中心
+      initialZoomLevel = map!.getZoom() // 保存缩放比例
+      isFirstRender = false
+
+      // 接口请求成功并首次添加标记后,动态添加重置按钮
+      addResetZoomButton(initialCenter, initialZoomLevel)
+    }
   }
 }
 
@@ -150,7 +159,7 @@ const getMarker = () => {
             iconColor: iconColorList[item.label].color
           })
         })
-        // 接口请求成后添加标记
+        // 请求成后添加标记,并动态添加重置按钮
         addMarkersToMap()
       }
     })

+ 0 - 1
src/views/Tracking/src/components/TrackingDetail/src/components/UploadFilesDialog.vue

@@ -56,7 +56,6 @@ const clearData = () => {
   uploadFileList.value = []
 }
 const beforeAvatarUpload = (rawFile: any) => {
-  console.log(rawFile, '文件类型')
   if (
     ![
       'application/pdf',

+ 103 - 71
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -7,6 +7,7 @@ import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 const route = useRoute()
 const router = useRouter()
 
+const allData = ref<any>({})
 const loading = ref(false)
 const generalInfo = ref({
   baseInfo: [
@@ -51,48 +52,37 @@ const generalInfo = ref({
     is_send: false
   }
 })
-
+const testValue = ref('')
 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'
-    //   }
-    // }
+  columns: [],
+  data: [
+    {
+      container_no: '123',
+      carrier_booking_no: '123',
+      size: '123',
+      vgm_weight: '123',
+      vgm_kg_lg: '12311111111111111111111111111',
+      vgm_time: '',
+      vgm_method: '123',
+      cargo_weight_kg: '123',
+      cargo_weight_lb: '123'
+    },
+    {
+      container_no: '123',
+      carrier_booking_no: '123',
+      size: '123',
+      vgm_weight: '123',
+      vgm_kg_lg: '123',
+      vgm_time: '',
+      vgm_method: '123',
+      cargo_weight_kg: '123',
+      cargo_weight_lb: '123'
+    }
   ],
-  data: [],
   scrollY: { enabled: true, oSize: 20, gt: 30 },
   stripe: true,
   emptyText: ' ',
@@ -118,7 +108,7 @@ const handleColumns = (columns: any) => {
     }
 
     // 添加编辑插槽
-    if (item.type === 'input') {
+    if (item.edit_type === 'input') {
       curColumn = {
         ...curColumn,
         editRender: {
@@ -128,38 +118,41 @@ const handleColumns = (columns: any) => {
           edit: 'vInput'
         }
       }
-    } else if (item.type === 'select') {
+    } else if (item.edit_type === 'dateTime') {
       curColumn = {
         ...curColumn,
         editRender: {
-          name: 'vSelect'
+          name: 'editDate'
         },
         slots: {
-          edit: 'vSelect'
+          edit: 'editDate'
         }
       }
-    } else if (item.type === 'dateTime') {
+    }
+
+    if (item.title === 'VGM Weight') {
       curColumn = {
         ...curColumn,
         editRender: {
-          name: 'editDate'
+          name: 'vWeightSelect'
         },
         slots: {
-          edit: 'editDate'
+          edit: 'vWeightSelect'
         }
       }
-    }
-    // 格式化
-    if (item.formatter === 'date') {
+    } else if (item.title === 'VGM Method') {
       curColumn = {
         ...curColumn,
-        sortBy: ({ row, column }: any) => {
-          return dayjs(row[column.field]).unix()
+        editRender: {
+          name: 'vMethodSelect'
         },
-        formatter: ({ cellValue }: any) =>
-          cellValue ? dayjs(cellValue).format('MMM-YYYY-DD ') : '--'
+        slots: {
+          edit: 'vMethodSelect'
+        }
       }
-    } else if (item.formatter === 'dateTime') {
+    }
+    // 格式化
+    if (item.edit_type === 'dateTime') {
       curColumn = {
         ...curColumn,
         formatter: ({ cellValue }: any) =>
@@ -232,17 +225,19 @@ const getData = () => {
     })
     .then((res: any) => {
       if (res.code === 200) {
-        // 获取数据
+        allData.value = res.data
         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
+        // 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
+            } else if (item.title === 'VGM Time') {
+              item.width = 190
             }
           })
         })
@@ -255,7 +250,36 @@ const getData = () => {
 getData()
 
 const handleGoBack = () => {
-  router.push({ path: '/tracking' })
+  router.push({ name: 'Tracking' })
+}
+const handleSave = () => {
+  const generalData = {
+    all_carrier_booking: generalInfo.value.baseInfo['Carrier Booking No.'],
+    submitter: generalInfo.value.formData.Submitter,
+    signature: generalInfo.value.formData.signature,
+    authorized_email: generalInfo.value.formData.authorized_email,
+    authorized_tel: generalInfo.value.formData.authorized_tel,
+    is_send: generalInfo.value.formData.is_send
+  }
+  const tableRowData = tableData.value.data.map((item) => {
+    return {
+      ...item
+    }
+  })
+
+  const variableList = Object.keys(tableRowData[0])
+  const tableInfo = {}
+  variableList.forEach((item) => {
+    if (item === '_X_ROW_KEY') return
+    Object.assign(tableInfo, {
+      [item]: tableRowData.map((row) => row[item])
+    })
+  })
+  // $api.saveVGMData({
+  //   serial_no: allData.value.serial_no,
+  //   _schemas: allData.value.schemas,
+  //   ...generalData
+  // })
 }
 </script>
 
@@ -267,7 +291,7 @@ const handleGoBack = () => {
         <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">
+        <el-button class="el-button--main" @click="handleSave">
           <span class="font_family icon-icon_save_b"></span>
           Save</el-button
         >
@@ -355,32 +379,39 @@ const handleGoBack = () => {
               ></el-input>
             </template>
 
-            <template #vSelect="{ row, column }">
+            <template #vWeightSelect="{ row, column }">
               <vxe-select v-model="row[column.field]" placeholder="Please select..." clearable>
                 <vxe-option label="KGS" value="KGS"></vxe-option>
                 <vxe-option label="LBS" value="LBS"></vxe-option>
               </vxe-select>
             </template>
 
+            <template #vMethodSelect="{ row, column }">
+              <vxe-select v-model="row[column.field]" placeholder="Please select..." clearable>
+                <vxe-option label="SM1" value="SM1"></vxe-option>
+                <vxe-option label="SM2" value="SM2"></vxe-option>
+              </vxe-select>
+            </template>
+
             <template #editDate="{ row, column }">
-              <!-- <el-date-picker
+              <el-date-picker
                 v-model="row[column.field]"
                 type="datetime"
-                style="width: 100%"
+                style="width: 170px"
                 placeholder="Pick a Date"
-                date-format="MM/DD/YYYY"
-                value-format="YYYY-MM-DD HH:mm:ss"
-                format="MM/YYYY/DD HH:mm:ss"
-              /> -->
-              <a-date-picker
+                format="MMM-DD-DD HH:mm:ss"
+                date-format="MMM-DD-DD"
+                time-format="HH:mm:ss"
+              />
+              <!-- <a-date-picker
                 :showNow="false"
                 class="test-date-picker"
                 placement="topLeft"
                 v-model:value="row[column.field]"
-                format="YYYY-MM-DD HH:mm:ss"
+                format="MMM-DD-YYYY HH:mm:ss"
                 :getPopupContainer="(target) => target.parentElement"
                 :show-time="{ defaultValue: dayjs('00:00:00', 'HH:mm:ss') }"
-              />
+              /> -->
             </template>
           </vxe-grid>
         </div>
@@ -507,10 +538,11 @@ const handleGoBack = () => {
     color: #202020;
   }
 }
-.vgm-table {
-  .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>