Browse Source

fix: merge dev

AmandaG 2 tháng trước cách đây
mục cha
commit
76800e9f13

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

@@ -91,6 +91,21 @@ export const changePassword = (params: any, config: any) => {
   )
 }
 
+/**
+ * 重置和激活密码
+ */
+export const resetAndActivatePassword = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'login',
+      operate: 'resetAndActivateUpdate',
+      ...params
+    },
+    config
+  )
+}
+
 /**
  * 获取public tracking detail详情数据
  */

+ 4 - 0
src/styles/theme.scss

@@ -327,6 +327,8 @@
 
   --color-guide-icon-bg: rgba(237, 237, 237, 0.6);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.45);
+
+  --color-json-item-hover: #e6f7ff;
 }
 
 :root.dark {
@@ -532,4 +534,6 @@
 
   --color-guide-icon-bg: rgba(237, 237, 237, 0.1);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.15);
+
+  --color-json-item-hover: #3e5966;
 }

+ 4 - 1
src/views/AIApiLog/src/components/LogDialog.vue

@@ -87,5 +87,8 @@ defineExpose({
   .el-dialog__body {
     padding: 0;
   }
+  .vjs-tree-node:hover {
+    background-color: var(--color-json-item-hover);
+  }
 }
-</style>
+</style>

+ 4 - 0
src/views/Booking/src/components/BookingDetail/src/components/EmailView.vue

@@ -137,6 +137,10 @@ const handleFocusEditor = () => {
 const sendEmail = () => {
   const html = editorRef.value.getHtml()
   const text = editorRef.value.getText()
+  if (!text) {
+    ElMessage.warning('Please enter the email content')
+    return
+  }
   $api
     .sendEmailApi({
       action: 'ocean_booking',

+ 328 - 0
src/views/Login/src/components/SetPasswordDialog.vue

@@ -0,0 +1,328 @@
+<script setup lang="ts">
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+const dialogModel = ref(false)
+const state = ref<'reset' | 'activate'>('activate')
+
+const openDialog = (stateValue: 'reset' | 'activate') => {
+  state.value = stateValue
+  dialogModel.value = true
+}
+
+const loginForm = ref({
+  username: '',
+  newPassword: '',
+  confirmPassword: ''
+})
+
+const newPwdErrTips = ref('')
+const loginError: any = ref({
+  newPassword: false,
+  confirmPassword: false
+})
+
+const emit = defineEmits(['submit'])
+
+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
+    .resetAndActivatePassword({
+      verifcation_code: route.query.verifcation_code,
+      password: loginForm.value.newPassword
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        emit('submit')
+        dialogModel.value = false
+      } else if (res.code !== 500 && res.code !== 200 && res.code) {
+        ElMessage.error('Error! Please try again later.')
+      }
+    })
+}
+
+const hiddenError = (key: string) => {
+  loginError.value[key] = false
+}
+
+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
+}
+
+const confirmPwd = () => {
+  if (loginForm.value.confirmPassword !== loginForm.value.newPassword) {
+    loginError.value.confirmPassword = true
+  } else {
+    loginError.value.confirmPassword = false
+  }
+}
+defineExpose({
+  openDialog
+})
+</script>
+
+<template>
+  <el-dialog v-model="dialogModel" class="set-password-dialog" width="480">
+    <div class="content">
+      <div class="top-section">
+        <div class="title">Set Your Password</div>
+        <div class="description">
+          Hello {{ route.query.name }}, please create a new password for your account.
+        </div>
+      </div>
+      <div class="login-form">
+        <div class="label" style="margin-top: 0">
+          <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
+          @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">{{ 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
+              style="padding-top: 2px"
+              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 }"
+          v-model="loginForm.confirmPassword"
+          type="password"
+          placeholder="Please input password"
+          show-password
+          @focus="hiddenError('confirmPassword')"
+          @blur="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 set-btn">{{
+          state === 'reset' ? 'Reset Password' : 'Set Password & Activate Account'
+        }}</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.set-password-dialog {
+  .content {
+    padding-bottom: 40px;
+    .top-section {
+      width: 100%;
+      height: 158px;
+      padding-top: 64px;
+      background: url(../image/bg-login-card.png) no-repeat;
+      background-position: top left; /* 从左上角开始显示 */
+      background-size: 480px 200px; /* 保持背景图像的原始尺寸 */
+      .title {
+        font-size: 24px;
+        font-weight: 700;
+        text-align: center;
+      }
+      .description {
+        margin-top: 16px;
+        text-align: center;
+      }
+    }
+    .set-btn {
+      width: 100%;
+      height: 40px;
+      margin-top: 16px;
+    }
+  }
+  .login-form {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    padding: 0 40px;
+    .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__inner) {
+          -webkit-text-fill-color: var(--color-neutral-1);
+          color: var(--color-neutral-1);
+          font-weight: 700;
+        }
+      }
+    }
+    .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 {
+        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;
+        box-shadow: 0 0 0 1px var(--color-input-disabled-border) inset;
+      }
+    }
+
+    .label {
+      display: flex;
+      justify-content: space-between;
+      width: 100%;
+      margin-top: 14px;
+      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;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.set-password-dialog {
+  .el-dialog__header {
+    display: none;
+  }
+  .el-dialog__body {
+    padding: 0;
+  }
+}
+</style>

+ 4 - 0
src/views/Tracking/src/components/TrackingDetail/src/components/EmailDrawer.vue

@@ -138,6 +138,10 @@ const emit = defineEmits<{
 const sendEmail = () => {
   const html = editorRef.value.getHtml()
   const text = editorRef.value.getText()
+  if (!text) {
+    ElMessage.warning('Please enter the email content')
+    return
+  }
   $api
     .sendEmailApi({
       action: 'ocean_order',