Browse Source

feat: 新增修改密码和add VGM页面

zhouyuhao 1 year ago
parent
commit
353656bb6b

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

@@ -47,6 +47,19 @@ export const login = (params: any, config: any) => {
   )
 }
 
+// 退出登录
+export const logout = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'login',
+      operate: 'logout',
+      ...params
+    },
+    config
+  )
+}
+
 /**
  * 忘记密码
  */
@@ -61,3 +74,18 @@ export const forgotPassword = (params: any, config: any) => {
     config
   )
 }
+
+/**
+ * 更新密码
+ */
+export const changePassword = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'login',
+      operate: 'update_pwd_expires',
+      ...params
+    },
+    config
+  )
+}

+ 9 - 0
src/router/index.ts

@@ -40,6 +40,15 @@ const router = createRouter({
             activeMenu: '/tracking'
           }
         },
+        {
+          path: '/tracking/add-vgm',
+          name: 'Add VGM',
+          component: () =>
+            import('../views/Tracking/src/components/TrackingTable/src/components/VGMView.vue'),
+          meta: {
+            activeMenu: '/tracking'
+          }
+        },
         {
           path: '/public-tracking',
           name: 'Public Tracking',

+ 6 - 2
src/stores/modules/user.ts

@@ -14,8 +14,12 @@ export const useUserStore = defineStore('user', {
       this.username = username
     },
     logout() {
-      localStorage.removeItem('username')
-      this.username = ''
+      $api.logout().then((res: any) => {
+        if (res.code === 200) {
+          localStorage.removeItem('username')
+          this.username = ''
+        }
+      })
     }
   }
 })

+ 322 - 0
src/views/Login/src/components/ChangePasswordCard.vue

@@ -0,0 +1,322 @@
+<script setup lang="ts">
+import { useRouter } from 'vue-router'
+import { useUserStore } from '@/stores/modules/user'
+
+const router = useRouter()
+const loginForm = ref({
+  username: 'ra.admin',
+  oldPassword: 'abc123456789',
+  newPassword: '',
+  confirmPassword: ''
+})
+
+const loginError: any = ref({
+  oldPassword: false,
+  newPassword: false,
+  confirmPassword: false
+})
+
+const userStore = useUserStore()
+
+// 点击登录按钮
+const handleChangePwd = () => {
+  $api
+    .login({
+      uname: loginForm.value.username,
+      old_password: loginForm.value.oldPassword,
+      password: loginForm.value.newPassword
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        const { data } = res
+        if (data.msg === 'today') {
+          ElMessageBox.alert('Your password will expire today, please reset', 'Prompt', {
+            confirmButtonText: 'OK',
+            type: 'warning',
+            confirmButtonClass: 'el-button--dark'
+          })
+        } else if (data.msg === 'last') {
+          ElMessageBox.alert(
+            `Your password will expire in${data.data}days, please reset`,
+            'Prompt',
+            {
+              confirmButtonText: 'OK',
+              type: 'warning',
+              confirmButtonClass: 'el-button--dark'
+            }
+          )
+        }
+        userStore.setUsername(res.data.uname || '')
+        router.push('/')
+      } else if (res.code === 400) {
+        // 验证码错误
+        if (res.data.msg === 'password_error') {
+          loginError.value.password = true
+        } else if (res.data.msg === 'verifcation_error') {
+          loginError.value.code = true
+        } else if (res.data.msg === 'error_times') {
+          errorTipsRef.value.openDialog()
+        }
+      }
+    })
+}
+
+const confirmPwd = () => {
+  console.log('test')
+  if (loginForm.value.confirmPassword !== loginForm.value.newPassword) {
+    loginError.value.confirmPassword = true
+  } else {
+    loginError.value.confirmPassword = false
+  }
+}
+
+const isUserNameExit = ref(false)
+
+const emit = defineEmits<{
+  changeStatus: [string]
+}>()
+const handleForgot = () => {
+  emit('changeStatus', 'reset')
+}
+const handleBackLogin = () => {
+  emit('changeStatus', 'login')
+}
+
+const errorTipsRef = ref()
+</script>
+
+<template>
+  <el-card class="login-card">
+    <div class="title">
+      <span class="welcome">Change Password</span>
+      <span class="tips">Password expired, please change your password.</span>
+    </div>
+
+    <div class="login-form">
+      <div class="label">
+        <span>User Name</span>
+      </div>
+      <el-input :disabled="true" ref="userNameRef" v-model="loginForm.username" class="user-name">
+        <template #prefix>
+          <span class="font_family icon-icon_username_b"></span>
+        </template>
+        <template #suffix>
+          <span v-if="isUserNameExit" class="font_family icon-icon_confirm_b confirm-icon"></span>
+        </template>
+      </el-input>
+      <div class="label">
+        <span>Old Password</span>
+        <span class="forgot-password" @click="handleForgot">Forgot Password?</span>
+      </div>
+      <el-input
+        ref="passWordRef"
+        :class="{ 'is-error': loginError.oldPassword }"
+        v-model="loginForm.oldPassword"
+        type="password"
+        placeholder="Please input password"
+        show-password
+      >
+        <template #prefix>
+          <span class="font_family icon-icon_password_b"></span>
+        </template>
+      </el-input>
+      <div class="error" v-if="loginError.oldPassword">Incorrect password. Please try again.</div>
+      <div class="label">
+        <span>New Password</span>
+      </div>
+      <el-input
+        ref="passWordRef"
+        :class="{ 'is-error': loginError.newPassword }"
+        v-model="loginForm.newPassword"
+        type="password"
+        placeholder="Please input password"
+        show-password
+      >
+        <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="label">
+        <span>Confirm New Password</span>
+      </div>
+      <el-input
+        ref="passWordRef"
+        :class="{ 'is-error': loginError.confirmPassword }"
+        v-model="loginForm.confirmPassword"
+        type="password"
+        placeholder="Please input password"
+        show-password
+        @change="confirmPwd"
+      >
+        <template #prefix>
+          <span class="font_family icon-icon_password_b"></span>
+        </template>
+      </el-input>
+      <div class="error" v-if="loginError.confirmPassword">
+        The password does not match. Please try again.
+      </div>
+      <el-button @click="handleChangePwd" class="el-button--dark login-btn"
+        >Change Password</el-button
+      >
+      <div @click="handleBackLogin" class="back-text">
+        <span class="font_family icon-icon_back_b"></span>
+        <span class="text"> Back to login</span>
+      </div>
+    </div>
+    <template #footer>
+      <div class="license">
+        <span>© 2024 KTreker from <span class="company">Kerry Logistics</span></span>
+        <span>Version 0.67</span>
+      </div>
+    </template>
+  </el-card>
+</template>
+
+<style lang="scss" scoped>
+.login-card {
+  width: 400px;
+
+  .title {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    margin-bottom: 24px;
+
+    .welcome {
+      margin-bottom: 16px;
+      font-size: 24px;
+      font-weight: 700;
+    }
+  }
+
+  :deep(.el-card__body) {
+    padding: 40px;
+    padding-bottom: 16px;
+  }
+
+  .login-btn {
+    width: 100%;
+    height: 40px;
+    margin-top: 16px;
+  }
+}
+
+.login-form {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+
+  .el-input {
+    height: 40px;
+    .confirm-icon {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      height: 16px;
+      width: 16px;
+      margin-right: 4px;
+      font-size: 14px;
+      color: #fff;
+      background-color: var(--color-success);
+      border-radius: 50%;
+    }
+
+    &.is-error {
+      :deep(.el-input__wrapper) {
+        box-shadow: 0 0 0 1px var(--color-danger) inset;
+      }
+    }
+
+    :deep(.el-input__prefix) {
+      margin: 0 4px;
+      background-color: transparent;
+    }
+    &.is-disabled {
+      :deep(.el-input__wrapper) {
+        background-color: #f4f4f4;
+      }
+      :deep(.el-input__inner) {
+        -webkit-text-fill-color: var(--color-neutral-1);
+        color: var(--color-neutral-1);
+        font-weight: 700;
+      }
+    }
+  }
+  .verification-code {
+    margin-top: 16px;
+    .verification-code-img {
+      width: 130px;
+      height: 38px;
+      object-fit: cover;
+    }
+    :deep(.el-input-group__append) {
+      padding: 0;
+      padding-right: 1px;
+    }
+  }
+  .el-input.user-name {
+    :deep(.el-input__wrapper) {
+      padding-right: 6px;
+    }
+  }
+
+  .label {
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+    margin-top: 16px;
+    font-size: 12px;
+    line-height: 18px;
+
+    span {
+      color: var(--color-neutral-2);
+    }
+
+    .forgot-password {
+      color: var(--color-theme);
+      cursor: pointer;
+    }
+  }
+
+  .error {
+    font-size: 12px;
+    color: var(--color-danger);
+    line-height: 14px;
+  }
+
+  .back-text {
+    width: 100%;
+    height: 20px;
+    margin-top: 24px;
+    margin-bottom: 8px;
+    text-align: center;
+    cursor: pointer;
+
+    span {
+      color: var(--color-theme);
+    }
+
+    .text {
+      display: inline-block;
+      transform: translateY(-2px);
+      font-size: 12px;
+    }
+  }
+}
+
+.license {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  font-size: 12px;
+
+  .company {
+    color: var(--color-theme);
+  }
+
+  span {
+    color: var(--color-neutral-2);
+  }
+}
+</style>

+ 0 - 79
src/views/Login/src/components/LoginCard.vue

@@ -1,79 +0,0 @@
-<script setup lang="ts"></script>
-
-<template>
-  <el-card class="login-card">
-    <div class="title">
-      <span class="welcome">Welcome to KLN Portal</span>
-      <span class="tips">Login to your account</span>
-    </div>
-    <div class="send-email-tips" :style="{ display: isEmailTips ? 'block' : 'none' }">
-      <span class="font_family icon-icon_confirm_b success-icon"></span>
-      New Password sent to registered email.
-      <span
-        @click="handleDeleteEmailTips"
-        class="font_family icon-icon_reject_b delete-icon"
-      ></span>
-    </div>
-    <div class="login-form">
-      <div class="label">
-        <span>User Name</span>
-      </div>
-      <el-input
-        ref="userNameRef"
-        :class="{ 'is-error': loginError.username }"
-        v-model="loginForm.username"
-        class="user-name"
-        placeholder="Please input user name"
-        @focus="handleDeleteEmailTips"
-        @change="isUserNameExit = true"
-      >
-        <template #prefix>
-          <span class="font_family icon-icon_username_b"></span>
-        </template>
-        <template #suffix>
-          <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 account does not exist.</div>
-      <div class="label">
-        <span>Password</span>
-        <span class="forgot-password" @click="handleForgot">Forgot Password?</span>
-      </div>
-      <el-input
-        ref="passWordRef"
-        :class="{ 'is-error': loginError.password }"
-        v-model="loginForm.password"
-        type="password"
-        placeholder="Please input password"
-        show-password
-        @focus="handleDeleteEmailTips"
-        ><template #prefix>
-          <span class="font_family icon-icon_password_b"></span>
-        </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"
-      >
-        <template #append>
-          <img class="verification-code-img" src="./image/code.png" alt="" />
-        </template>
-      </el-input>
-      <div class="error" v-if="loginError.code">Incorrect verification code.</div>
-      <el-button @click="handleSubmit" class="el-button--dark login-btn">Login</el-button>
-    </div>
-    <template #footer>
-      <div class="license">
-        <span>© 2024 KTreker from <span class="company">Kerry Logistics</span></span>
-        <span>Version 0.67</span>
-      </div>
-    </template>
-  </el-card>
-</template>
-
-<style lang="scss" scoped></style>

+ 35 - 9
src/views/Login/src/loginView.vue

@@ -2,6 +2,7 @@
 import { useRouter } from 'vue-router'
 import ErrorTips from './components/ErrorTips.vue'
 import { useUserStore } from '@/stores/modules/user'
+import ChangePasswordCard from './components/ChangePasswordCard.vue'
 
 const router = useRouter()
 const loginForm = ref({
@@ -36,15 +37,22 @@ const loginError: any = ref({
   code: false
 })
 const verificationCode = ref()
+const loading = ref(false)
 // 获取验证码
 const getCode = () => {
-  $api.getVerifcationCode().then((res: any) => {
-    if (res.code === 200) {
-      verificationCode.value = `data:image/png;base64,${res.data.imagePngBase64}`
-    } else {
-      verificationCode.value = ''
-    }
-  })
+  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
+    })
 }
 getCode()
 
@@ -147,10 +155,18 @@ const handleDeleteEmailTips = (type?: any) => {
 }
 
 const errorTipsRef = ref()
+
+const handleChangeStatus = (newStatus: string) => {
+  status.value = newStatus
+}
+const test = () => {
+  status.value = 'changePwd'
+}
 </script>
 
 <template>
   <div class="login">
+    <el-button @click="test">测试</el-button>
     <el-card class="login-card" v-if="status === 'login'">
       <div class="title">
         <span class="welcome">Welcome to KLN Portal</span>
@@ -211,7 +227,12 @@ const errorTipsRef = ref()
           @focus="handleDeleteEmailTips('code')"
         >
           <template #append>
-            <img class="verification-code-img" :src="verificationCode" alt="" />
+            <img
+              v-vloading="loading"
+              class="verification-code-img"
+              :src="verificationCode"
+              alt=""
+            />
           </template>
         </el-input>
         <div class="error" v-if="loginError.code">Incorrect verification code.</div>
@@ -224,7 +245,7 @@ const errorTipsRef = ref()
         </div>
       </template>
     </el-card>
-    <el-card class="login-card" v-else>
+    <el-card class="login-card" v-else-if="status === 'reset'">
       <div class="title">
         <span class="welcome">Password Retrieval</span>
         <span class="tips">We'll send you new password in email</span>
@@ -295,6 +316,10 @@ const errorTipsRef = ref()
         </div>
       </template>
     </el-card>
+    <ChangePasswordCard
+      @changeStatus="handleChangeStatus"
+      v-else-if="status === 'changePwd'"
+    ></ChangePasswordCard>
     <ErrorTips ref="errorTipsRef" @forget-password="status = 'reset'"></ErrorTips>
   </div>
 </template>
@@ -401,6 +426,7 @@ const errorTipsRef = ref()
   .verification-code {
     margin-top: 16px;
     .verification-code-img {
+      display: block;
       width: 130px;
       height: 38px;
       object-fit: cover;

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

@@ -221,8 +221,6 @@ const SearchInput = () => {
     :tagsData="tagsData"
     ref="TrackingTable_ref"
   ></TrackingTable>
-  <!-- <ShipmentStatus></ShipmentStatus> -->
-  <!-- <ContainerStatus></ContainerStatus> -->
 </template>
 
 <style lang="scss" scoped>

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

@@ -7,7 +7,10 @@ const openDrawer = (data: any) => {
   getData(data)
   drawer.value = true
 }
+
+const tableLoading = ref(false)
 const getData = (data: any) => {
+  tableLoading.value = true
   $api
     .getTrackingAmsIsf({
       ams_ss: data.ams_ss,
@@ -24,6 +27,9 @@ const getData = (data: any) => {
         ISFTableData.value.data = res.data.isf
       }
     })
+    .finally(() => {
+      tableLoading.value = false
+    })
 }
 
 const AMSTableRef = ref<VxeGridInstance | null>(null)
@@ -89,13 +95,27 @@ const exportISFTable = () => {
   })
 }
 
+const clearData = () => {
+  AMSTableData.value.data = []
+  AMSTableData.value.columns = []
+  ISFTableData.value.data = []
+  ISFTableData.value.columns = []
+}
+
 defineExpose({
   openDrawer
 })
 </script>
 
 <template>
-  <el-drawer :modal="false" :size="1000" v-model="drawer" title="AMS/ISF" direction="rtl">
+  <el-drawer
+    :modal="false"
+    @close="clearData"
+    :size="1000"
+    v-model="drawer"
+    title="AMS/ISF"
+    direction="rtl"
+  >
     <div class="ams-isf">
       <div class="label" style="margin-top: 8px">
         <span>AMS-M1 Log</span>
@@ -104,6 +124,7 @@ defineExpose({
         </el-button>
       </div>
       <vxe-grid
+        v-vloading="tableLoading"
         ref="AMSTableRef"
         class="radius-bottom"
         :style="{ border: 'none' }"
@@ -117,6 +138,7 @@ defineExpose({
         </el-button>
       </div>
       <vxe-grid
+        v-vloading="tableLoading"
         class="radius-bottom"
         ref="ISFTableRef"
         :style="{ border: 'none' }"

+ 2 - 2
src/views/Tracking/src/components/TrackingDetail/src/components/MapView.vue

@@ -8,10 +8,10 @@ import Location from '../images/location.png'
 
 const initMap = () => {
   // 地图初始化
-  const map = L.map('map').setView([51.505, -0.09], 3)
+  const map = L.map('map').setView([51.505, -0.09], 5)
 
   // 添加 TileLayer
-  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+  L.tileLayer('https://map.kerryapex.com/osm_tiles/{z}/{x}/{y}.png', {
     attribution:
       '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
   }).addTo(map)

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

@@ -72,7 +72,8 @@ const getTableColumns = async (isInit: boolean) => {
     if (res.code === 200) {
       trackingTable.value.columns = [
         { type: 'checkbox', width: 50, fixed: 'left' },
-        ...handleColumns(res.data.TrackingTableColumns)
+        ...handleColumns(res.data.TrackingTableColumns),
+        { title: 'Action', fixed: 'left', width: 80, slots: { default: 'action' } }
       ]
       tableOriginColumnsField.value = res.data.TrackingTableColumns
     }
@@ -228,7 +229,7 @@ const exportTable = (status: number) => {
   }
 }
 
-const tableLoading = ref(true)
+const tableLoading = ref(false)
 
 const CustomizeColumnsRef = ref()
 // 打开定制表格弹窗
@@ -285,6 +286,13 @@ const handleCheckboxChange = ({ records }: any) => {
 const handleCheckAllChange = ({ records }: any) => {
   selectedNumber.value = records.length
 }
+
+// VGM
+const handleVGM = () => {
+  router.push({
+    path: '/tracking/add-vgm'
+  })
+}
 defineExpose({
   searchTableData,
   TransportListItem,
@@ -332,7 +340,7 @@ defineExpose({
       </template>
       <!-- action操作的插槽 -->
       <template #action>
-        <el-button class="el-button--blue" style="height: 24px">
+        <el-button @click="handleVGM" 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>

+ 149 - 0
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -0,0 +1,149 @@
+<script setup lang="ts">
+const formInline: any = ref({
+  user: '',
+  region: '',
+  date: ''
+})
+const onSubmit = () => {
+  console.log('submit!')
+}
+</script>
+
+<template>
+  <div class="vgm">
+    <div class="header">
+      <div class="title">Add VGM</div>
+      <div class="right-option">
+        <el-button class="el-button--default"
+          ><span class="font_family icon-icon_return_b"></span> Cancel</el-button
+        >
+        <el-button class="el-button--main">
+          <span class="font_family icon-icon_save_b"></span>
+          Save</el-button
+        >
+      </div>
+    </div>
+    <div class="content">
+      <div class="general-info">
+        <div class="title">
+          <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>
+        </div>
+        <div class="form">
+          <el-form :inline="true" :model="formInline" class="demo-form-inline">
+            <el-form-item label="Approved by">
+              <el-input v-model="formInline.user" placeholder="Approved by" clearable />
+            </el-form-item>
+            <el-form-item label="Activity zone">
+              <el-select v-model="formInline.region" placeholder="Activity zone" clearable>
+                <el-option label="Zone one" value="shanghai" />
+                <el-option label="Zone two" value="beijing" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="Activity time">
+              <el-date-picker
+                v-model="formInline.date"
+                type="date"
+                placeholder="Pick a date"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="onSubmit">Query</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+      <div class="detail-info"></div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.vgm {
+  .header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 72px;
+    padding: 0 24px;
+    border-bottom: 1px solid var(--color-border);
+    .title {
+      font-size: 24px;
+      font-weight: 700;
+    }
+    .right-option {
+      .el-button {
+        height: 40px;
+        padding: 8px 24px;
+        .font_family {
+          margin-right: 4px;
+        }
+      }
+    }
+  }
+  & > .content {
+    padding: 8px 24px 16px;
+  }
+  .general-info,
+  .detail-info {
+    border: 1px solid var(--color-border);
+    border-radius: 12px;
+    & > .title {
+      height: 48px;
+      padding: 0 16px;
+      line-height: 48px;
+      span {
+        font-size: 18px;
+        font-weight: 700;
+      }
+    }
+    & > .description-info {
+      display: flex;
+      flex-wrap: wrap;
+      padding: 8px 16px 0;
+    }
+    .form {
+      padding: 16px;
+      border-top: 1px solid var(--color-border);
+    }
+  }
+}
+.data-info {
+  display: flex;
+  flex-direction: column;
+  width: 25%;
+  margin-bottom: 20px;
+  .label {
+    margin-bottom: 8px;
+    font-size: 12px;
+    line-height: 16px;
+    color: var(--dashboard-text-color);
+  }
+  .info {
+    line-height: 21px;
+    font-weight: 700;
+  }
+}
+</style>