瀏覽代碼

Merge branch 'dev' into dev_g

AmandaG 3 月之前
父節點
當前提交
4d31ec8f53

+ 1 - 1
package.json

@@ -40,7 +40,7 @@
     "moment": "^2.30.1",
     "moment-timezone": "^0.5.46",
     "pinia": "^2.2.2",
-    "sass-loader": "^16.0.2",
+    "sass-loader": "^14.1.1",
     "vue": "^3.4.29",
     "vue-draggable-plus": "^0.5.3",
     "vue-json-pretty": "^2.5.0",

+ 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')
 }

+ 5 - 3
src/components/DateRange/src/DateRange.vue

@@ -15,10 +15,12 @@ onMounted(() => {
   emitter.on('clearTag', (tag: any) => {
     if (tag.includes('ETD')) {
       clearDateStart()
-    } else if (tag.includes('ETA')) {
+    }
+    if (tag.includes('ETA')) {
+      clearDateEnd()
+    }
+    if (tag.includes('Creation Time')) {
       clearDateEnd()
-    } else {
-      clearDateCreation()
     }
   })
   emitter.on('clearDaterangeObj', () => {

+ 8 - 2
src/components/DateRange/src/components/CalendarDate.vue

@@ -29,6 +29,10 @@ const props = defineProps({
   isETA: {
     type: Boolean,
     default: false
+  },
+  isShowPopupClass: {
+    type: Boolean,
+    default: false
   }
 })
 const ETDDate = ref([])
@@ -38,7 +42,6 @@ const CreatePlaceholder = computed(() => {
   } else {
     return ['Start ATA Time', 'End ATA Time']
   }
-
 })
 watch(
   () => props.Date,
@@ -48,6 +51,8 @@ watch(
         current[0] ? dayjs(current[0]).format(valueFormatDate) : '',
         current[1] ? dayjs(current[1]).format(valueFormatDate) : ''
       ]
+    } else {
+      ETDDate.value = []
     }
   },
   { immediate: true, deep: true }
@@ -138,10 +143,11 @@ const isTwoDate = (date: any) => {
         width: props.CalendarWidth,
         backgroundColor: props.isType ? 'var(--more-type-bg-color)' : 'var(--management-bg-color)'
       }"
+      :popupClassName="{ 'th-color': props.isShowPopupClass }"
       :open="open"
       :disabled="Disabled"
       @change="changeRangeData"
-      :placeholder="isNeedFooter? ['Start Time', 'End Time'] : CreatePlaceholder"
+      :placeholder="isNeedFooter ? ['Start Time', 'End Time'] : CreatePlaceholder"
       :format="userStore.dateFormat"
       valueFormat="MM/DD/YYYY"
       @openChange="handleCalendarOpen(ETDDate)"

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

@@ -9,6 +9,7 @@ interface UserInfo {
   last_name: string
   user_type: string
   email: string
+  employee_email: string
   expire_day: number
   date_format: string
   numbers_format: string
@@ -86,7 +87,7 @@ export const useUserStore = defineStore('user', {
 
     async logout(isNeedLogout: boolean = true) {
       if (isNeedLogout) {
-        await $api.logout().then(() => {})
+        await $api.logout().then(() => { })
       }
       localStorage.removeItem('userInfo')
       this.userInfo = {}

+ 3 - 0
src/styles/Antdui.scss

@@ -151,6 +151,9 @@ tr
 .ant-picker-dropdown .ant-picker-date-panel .ant-picker-content th {
   color: var(--color-neutral-2);
 }
+.th-color.ant-picker-dropdown .ant-picker-date-panel .ant-picker-content th {
+  color: var(--color-ant-picker-th);
+}
 .ant-picker-dropdown .ant-picker-decade-panel,
 .ant-picker-dropdown .ant-picker-year-panel,
 .ant-picker-dropdown .ant-picker-quarter-panel,

+ 2 - 1
src/styles/elementui.scss

@@ -559,10 +559,11 @@ div .custom-sheader-select .el-select-dropdown__item.is-selected {
 div .el-select-dropdown__item.is-hovering {
   background-color: var(--border-hover-color);
 }
-.el-select-dropdown__item {
+div .el-select-dropdown__item {
   border-radius: var(--border-radius-6);
   margin: 0 8px;
   margin-bottom: 4px;
+  padding:0 32px 0 8px;
   &:last-child {
     margin-bottom: 0;
   }

+ 5 - 0
src/styles/theme.scss

@@ -350,6 +350,8 @@
   --color-guide-icon-bg: rgba(237, 237, 237, 0.6);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.45);
   --color--process-data-tips-bg: #e8ebef;
+
+  --color-ant-picker-th: #b5b9bf;
 }
 
 :root.dark {
@@ -573,4 +575,7 @@
   --color-guide-icon-bg: rgba(237, 237, 237, 0.1);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.15);
   --color--process-data-tips-bg: #4f5760;
+  
+  --color-ant-picker-th: rgba(240, 241, 243,0.3);
 }
+  

+ 3 - 0
src/styles/vxeTable.scss

@@ -181,3 +181,6 @@ div.vxe-table--context-menu-wrapper {
 .vxe-tooltip--content {
   color: #f0f1f3;
 }
+.vxe-tooltip--wrapper.theme--dark {
+  z-index: 9999 !important;
+}

+ 1 - 0
src/views/ChatLog/src/components/TableView/src/TableView.vue

@@ -404,6 +404,7 @@ defineExpose({
       <!-- action操作栏的插槽 -->
       <template #action="{ row }">
         <el-button
+          class="el-button--blue"
           style="height: 24px; padding: 8px 4px; padding-left: 5px; font-size: 12px"
           @click="handleLogDetail(row)"
           v-if="row['Question Type'] !== 'Predefined Question'"

+ 5 - 5
src/views/DestinationDelivery/src/DestinationDelivery.vue

@@ -67,12 +67,12 @@ const clickCard = (filterTagItem: string) => {
 }
 
 const DateChange = (date: any) => {
-  queryData.value.created_time_start = date[0]
-  queryData.value.created_time_end = date[1]
+  queryData.value.created_time_start = date ? date[0] : ''
+  queryData.value.created_time_end = date ? date[1] : ''
 }
 const deliveryDataChange = (date) => {
-  queryData.value.delivery_date_start = date[0]
-  queryData.value.delivery_date_end = date[1]
+  queryData.value.delivery_date_start = date ? date[0] : ''
+  queryData.value.delivery_date_end = date ? date[1] : ''
 }
 
 const handleConfigurations = () => {
@@ -138,7 +138,7 @@ const handleSearch = () => {
         </div>
         <div class="date-tips_filter filter-item">
           <span class="label">Creation Date</span>
-          <CalendarDate @DateChange="DateChange"></CalendarDate>
+          <CalendarDate :isShowPopupClass="true" @DateChange="DateChange"></CalendarDate>
         </div>
         <div class="input-tips_filter filter-item">
           <el-input

+ 42 - 5
src/views/DestinationDelivery/src/components/DeliveryDate.vue

@@ -151,13 +151,13 @@ const handlePanelChange = (value: any, mode: any) => {
             <div class="status-item pending">
               <span class="status-text">Pending</span>
               <div class="count">
-                <span>3</span>
+                <span>{{ stateDataList[current.format('YYYY-MM-DD')].pending }}</span>
               </div>
             </div>
             <div class="status-item approved">
               <span class="status-text">Approved</span>
               <div class="count">
-                <span>3</span>
+                <span>{{ stateDataList[current.format('YYYY-MM-DD')].approved }}</span>
               </div>
             </div>
           </div>
@@ -343,6 +343,16 @@ div.delivery-date-range-picker {
     display: none !important;
   }
 
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-start:not(
+      .ant-picker-cell-range-start-single
+    ):before,
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-end:not(
+      .ant-picker-cell-range-end-single
+    ):before {
+    background-color: var(--color-theme) !important;
+  }
   &:where(.css-dev-only-do-not-override-1p3hq3p).ant-picker-dropdown
     .ant-picker-cell-in-view.ant-picker-cell-in-range::before {
     background-color: transparent !important;
@@ -358,6 +368,36 @@ div.delivery-date-range-picker {
     ) {
     background-color: transparent !important;
   }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-end.ant-picker-cell-selected.ant-picker-cell-range-end:not(
+      .ant-picker-cell-range-end-single
+    ):before {
+    background-color: var(--color-theme) !important;
+  }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cellrange-end:not(
+      .ant-picker-cell-range-end-single
+    ).ant-picker-cell-range-end.ant-picker-cell-range-hover-end:before {
+    background-color: var(--color-theme) !important;
+  }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cellrange-start:not(
+      .ant-picker-cell-range-start-single
+    ).ant-picker-cell-range-start.ant-picker-cell-range-hover-start:before {
+    background-color: var(--color-theme) !important;
+  }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-start.ant-picker-cell-range-start:not(
+      .ant-picker-cell-range-start-single
+    ):before {
+    background-color: var(--color-theme) !important;
+  }
+  &:where(.css-1p3hq3p).ant-picker-dropdown .ant-picker-cell-disabled {
+    background-color: rgba(0, 0, 0, 0.04) !important;
+    &::before {
+      background-color: transparent !important;
+    }
+  }
   .date-cell {
     position: relative;
     height: 100%;
@@ -479,6 +519,3 @@ div.delivery-date-range-picker {
   }
 }
 </style>
-ant-picker-cell ant-picker-cell-in-view ant-picker-cell-range-end ant-picker-cell-range-end-single
-ant-picker-cell-selected
-<style lang="scss"></style>

+ 28 - 5
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -9,6 +9,9 @@ import BookingDetailDialog from './components/BookingDetailDialog.vue'
 import EmailDialog from './components/EmailDialog.vue'
 import TipsDialog from './components/TipsDialog.vue'
 import { useRouter } from 'vue-router'
+import { useUserStore } from '@/stores/modules/user'
+
+const userStore = useUserStore()
 const router = useRouter()
 
 const props = defineProps({
@@ -388,7 +391,11 @@ defineExpose({
         </el-button>
         <!-- email -->
         <el-button
-          v-if="!isEmployeeRole && row.status === 'Approved'"
+          v-if="
+            !isEmployeeRole &&
+            row.status === 'Approved' &&
+            row.modify_by === userStore.userInfo.uname
+          "
           @click="clickEmailBtn(row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
@@ -400,7 +407,11 @@ defineExpose({
           @click="handleEdit(row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
-          v-if="!isEmployeeRole && (row.status === 'Pending Approval' || row.status === 'Rejected')"
+          v-if="
+            !isEmployeeRole &&
+            (row.status === 'Pending Approval' || row.status === 'Rejected') &&
+            row.modify_by === userStore.userInfo.uname
+          "
         >
           <span class="font_family icon-icon_edit_b"> </span>
         </el-button>
@@ -409,7 +420,11 @@ defineExpose({
           @click="handleTips('cancel', row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
-          v-if="!isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="
+            !isEmployeeRole &&
+            row.status === 'Pending Approval' &&
+            row.modify_by === userStore.userInfo.uname
+          "
         >
           <span class="font_family icon-icon_cancelled_b"> </span>
         </el-button>
@@ -417,14 +432,22 @@ defineExpose({
         <el-button
           @click="handleTips('approve', row)"
           class="action-btn el-button--blue"
-          v-if="isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="
+            isEmployeeRole &&
+            row.status === 'Pending Approval' &&
+            row.kln_pic.includes(userStore.userInfo.employee_email)
+          "
           style="height: 24px; width: 24px"
         >
           <span class="font_family icon-icon_confirm_b"> </span>
         </el-button>
         <!-- reject -->
         <el-button
-          v-if="isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="
+            isEmployeeRole &&
+            row.status === 'Pending Approval' &&
+            row.kln_pic.includes(userStore.userInfo.employee_email)
+          "
           @click="handleTips('reject', row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"

+ 97 - 0
src/views/Login/src/components/ActivatedDialog.vue

@@ -0,0 +1,97 @@
+<script setup lang="ts">
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+const dialogModel = ref(false)
+const state = ref<'reset' | 'activate'>('activate')
+
+const openDialog = (stateValue: 'reset' | 'activate') => {
+  state.value = stateValue
+  dialogModel.value = true
+}
+
+const tipsData = ref({
+  reset: {
+    title: 'Password Reset Successfully',
+    firstLine: 'Your password has been successfully reset.',
+    secondLine: 'You can now log in using your email and your new password.'
+  },
+  activate: {
+    title: 'Account Activated Successfully',
+    firstLine: 'Your account has been successfully activated.',
+    secondLine: 'You can now log in using your email and the password you just created.'
+  }
+})
+const handleGotoLogin = () => {
+  dialogModel.value = false
+  router.push({ name: 'Login' })
+}
+
+defineExpose({
+  openDialog
+})
+</script>
+
+<template>
+  <el-dialog v-model="dialogModel" top="23vh" class="activated-dialog" width="480">
+    <div class="activated-dialog-content">
+      <div class="successfully-icon">
+        <span style="font-size: 60px; color: white" class="font_family icon-icon_confirm_b"></span>
+      </div>
+      <div class="title">{{ tipsData[state].title }}</div>
+      <div class="description">
+        <p>{{ tipsData[state].firstLine }}</p>
+        <p>{{ tipsData[state].secondLine }}</p>
+      </div>
+    </div>
+    <template #footer>
+      <el-button style="width: 120px; height: 40px" class="el-button--dark" @click="handleGotoLogin"
+        >Go to Login</el-button
+      >
+    </template>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.activated-dialog {
+  background-color: red;
+  .successfully-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100px;
+    height: 100px;
+    background-color: rgb(0, 168, 112);
+    border-radius: 50%;
+  }
+  .activated-dialog-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding-top: 16px;
+  }
+  .title {
+    margin-top: 12px;
+    font-size: 18px;
+    font-weight: 700;
+  }
+  .description {
+    margin-top: 10px;
+    margin-bottom: 8px;
+    text-align: center;
+    line-height: 21px;
+  }
+}
+</style>
+<style lang="scss">
+.activated-dialog {
+  .el-dialog__header {
+    display: none;
+  }
+  .el-dialog__footer {
+    padding-left: 16px;
+    text-align: center;
+  }
+}
+</style>

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

@@ -0,0 +1,339 @@
+<script setup lang="ts">
+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: '',
+  oldPassword: '',
+  newPassword: '',
+  confirmPassword: ''
+})
+
+const newPwdErrTips = ref('')
+const loginError: any = ref({
+  oldPassword: false,
+  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
+    .resetPwd({
+      uname: loginForm.value.username,
+      old_password: loginForm.value.oldPassword,
+      password: loginForm.value.newPassword
+    })
+    .then((res: any) => {
+      emit('submit')
+      dialogModel.value = false
+      // if (res.code === 200) {
+      //   router.push({
+      //     name: 'Login',
+      //     query: {
+      //       isChange: 'true'
+      //     }
+      //   })
+      // } 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) => {
+  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 Caroline, 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>

+ 19 - 2
src/views/Login/src/loginView.vue

@@ -7,11 +7,26 @@ import { useThemeStore } from '@/stores/modules/theme'
 import ScoringSystem from '@/views/Dashboard/src/components/ScoringSystem.vue'
 import CryptoJS from 'crypto-js'
 import dayjs from 'dayjs'
+import ActivatedDialog from './components/ActivatedDialog.vue'
+import SetPasswordDialog from './components/SetPasswordDialog.vue'
 
 const router = useRouter()
 const route = useRoute()
 const themeStore = useThemeStore()
 
+const activatedDialogState = ref()
+const activatedDialogRef = ref()
+const setPasswordDialogRef = ref()
+onMounted(() => {
+  if (route.query.state) {
+    activatedDialogState.value = route.query.state
+    setPasswordDialogRef.value.openDialog(activatedDialogState.value)
+  }
+})
+const handleSetPassword = () => {
+  activatedDialogRef.value.openDialog(activatedDialogState.value)
+}
+
 // 手动获取url中的参数,直接获取route.query的参数时如果有+号会被转义成空格
 const getQueryParams = (url: string) => {
   const params = url.split('?')[1]
@@ -415,7 +430,7 @@ const firstLoginTipsRef = ref()
     </el-card>
     <el-card class="login-card" v-else-if="status === 'reset'">
       <div class="card-title" :class="{ 'is-dark': themeStore.theme === 'dark' }">
-        <span class="welcome">Password Retrieval</span>
+        <span class="welcome">Reset Password</span>
         <span class="tips">We'll send your password to your email address.</span>
       </div>
       <div class="login-form">
@@ -455,7 +470,7 @@ const firstLoginTipsRef = ref()
         <div class="error" v-if="loginError.email">Incorrect email. Please try again.</div>
 
         <el-button @click="handleVerification" class="el-button--dark login-btn"
-          >Send Password</el-button
+          >Send Reset Link</el-button
         >
         <div @click="backLogin(false)" class="back-text">
           <span class="font_family icon-icon_back_b"></span>
@@ -476,6 +491,8 @@ const firstLoginTipsRef = ref()
     ></VSliderVerification>
     <ErrorTips ref="errorTipsRef" @forget-password="status = 'reset'"></ErrorTips>
     <FirstLoginTips ref="firstLoginTipsRef" @forget-password="status = 'reset'"></FirstLoginTips>
+    <ActivatedDialog ref="activatedDialogRef"></ActivatedDialog>
+    <SetPasswordDialog @submit="handleSetPassword" ref="setPasswordDialogRef"></SetPasswordDialog>
   </div>
 </template>
 

+ 1 - 1
vite.config.ts

@@ -50,7 +50,7 @@ export default defineConfig(({ mode }) => {
       })
     ],
     server: {
-      port: 80,
+      port: 8080,
       hmr: true,
       open: true,
       // 设置 https 代理