Jelajahi Sumber

feat: 实现滑块验证

zhouyuhao 1 tahun lalu
induk
melakukan
4d0595ba23

+ 1 - 1
src/hooks/useOverflow.ts

@@ -18,7 +18,7 @@ export const useOverflow = (elementRef, dataRef) => {
 
   // 监听数据变化,更新溢出状态
   watch(
-    () => dataRef.value,
+    () => dataRef,
     () => {
       nextTick(() => {
         isOverflow.value = checkOverflow(elementRef.value)

+ 3 - 0
src/styles/elementui.scss

@@ -222,6 +222,9 @@ button.el-dialog__headerbtn:focus .el-dialog__close,
 button.el-dialog__headerbtn:hover .el-dialog__close {
   color: var(--color-theme);
 }
+div.el-overlay {
+  background-color: rgba(43, 47, 54, 0.7);
+}
 
 // radio
 label.el-radio {

+ 12 - 4
src/styles/icons/iconfont.css

@@ -1,9 +1,9 @@
 @font-face {
   font-family: "font_family"; /* Project id 4672385 */
-  src: url('iconfont.woff2?t=1726717215398') format('woff2'),
-       url('iconfont.woff?t=1726717215398') format('woff'),
-       url('iconfont.ttf?t=1726717215398') format('truetype'),
-       url('iconfont.svg?t=1726717215398#font_family') format('svg');
+  src: url('iconfont.woff2?t=1729653017918') format('woff2'),
+       url('iconfont.woff?t=1729653017918') format('woff'),
+       url('iconfont.ttf?t=1729653017918') format('truetype'),
+       url('iconfont.svg?t=1729653017918#font_family') format('svg');
 }
 
 .font_family {
@@ -14,6 +14,14 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-icon_drag__line_b:before {
+  content: "\e6c3";
+}
+
+.icon-icon_unverified_b:before {
+  content: "\e6c2";
+}
+
 .icon-icon_location_fill_b:before {
   content: "\e6c0";
 }

File diff ditekan karena terlalu besar
+ 0 - 0
src/styles/icons/iconfont.js


+ 4 - 0
src/styles/icons/iconfont.svg

@@ -14,6 +14,10 @@
     />
       <missing-glyph />
       
+      <glyph glyph-name="icon_drag__line_b" unicode="&#59075;" d="M447.104 370.432a19.2 19.2 0 0 1 0 27.136L236.672 608a19.2 19.2 0 0 0 0 27.136l40.704 40.768a19.2 19.2 0 0 0 27.136 0l257.92-257.92a48 48 0 0 0 0-67.904l-257.92-257.92a19.2 19.2 0 0 0-27.136 0l-40.704 40.704a19.2 19.2 0 0 0 0 27.136l210.432 210.432z m256 0a19.2 19.2 0 0 1 0 27.136L492.672 608a19.2 19.2 0 0 0 0 27.136l40.704 40.768a19.2 19.2 0 0 0 27.136 0l257.92-257.92a48 48 0 0 0 0-67.904l-257.92-257.92a19.2 19.2 0 0 0-27.136 0l-40.704 40.704a19.2 19.2 0 0 0 0 27.136l210.432 210.432z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="icon_unverified_b" unicode="&#59074;" d="M512-128A512 512 0 1 1 512 896a512 512 0 0 1 0-1024z m-38.656 313.728a48 48 0 0 0-67.84 0L188.032 403.072 256 471.04l183.424-183.424L768 616.192l67.84-67.84-362.496-362.56z"  horiz-adv-x="1024" />
+      
       <glyph glyph-name="icon_location_fill_b" unicode="&#59072;" d="M558.08-32.896c15.424 0 317.12 296.704 317.12 516.864a317.056 317.056 0 1 1-634.112 0c0-225.28 301.632-516.864 317.056-516.864z m0 400.96a128 128 0 1 0 0 256 128 128 0 0 0 0-256z"  horiz-adv-x="1088" />
       
       <glyph glyph-name="icon_unmark_b" unicode="&#59073;" d="M508.416 252.032L206.208 62.08V691.2a51.2 51.2 0 0 0 51.2 51.2h501.952a51.2 51.2 0 0 0 51.2-51.2v-629.056l-302.144 189.952z m0-90.688l329.92-207.36a32 32 0 0 1 49.024 27.072V691.136a128 128 0 0 1-128 128H257.408a128 128 0 0 1-128-128v-710.08a32 32 0 0 1 49.088-27.136l329.92 207.36z"  horiz-adv-x="1024" />

TEMPAT SAMPAH
src/styles/icons/iconfont.ttf


TEMPAT SAMPAH
src/styles/icons/iconfont.woff


TEMPAT SAMPAH
src/styles/icons/iconfont.woff2


+ 16 - 4
src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue

@@ -7,6 +7,7 @@ import EmailView from './components/EmailView.vue'
 import { cloneDeep } from 'lodash'
 import { transportationMode } from '@/components/TransportationMode'
 import { useRoute } from 'vue-router'
+import { useOverflow } from '@/hooks/useOverflow'
 
 const route = useRoute()
 
@@ -88,6 +89,11 @@ getData()
 const formatTime = (time: string) => {
   return time ? dayjs(time).format('MMM-DD-YYYY hh:mm A') : '--'
 }
+
+const originRef = ref()
+const destinationRef = ref()
+const { isOverflow: isOriginOverflow } = useOverflow(originRef, allData)
+const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allData)
 </script>
 
 <template>
@@ -110,12 +116,15 @@ const formatTime = (time: string) => {
             <div class="title">Origin</div>
             <div class="content">
               <!-- <span>{{ allData?.transportInfo?.origin }}</span> -->
-              <el-tooltip placement="top">
+              <el-tooltip v-if="isOriginOverflow" placement="top">
                 <template #content>{{ allData?.transportInfo?.origin || '--' }}</template>
-                <span class="info single-line-ellipsis">{{
+                <span ref="originRef" class="info single-line-ellipsis">{{
                   allData?.transportInfo?.origin || '--'
                 }}</span>
               </el-tooltip>
+              <span v-else ref="originRef" class="info single-line-ellipsis">{{
+                allData?.transportInfo?.origin || '--'
+              }}</span>
               <div class="line_container">
                 <hr color="#000000" />
                 <div class="right-icon"></div>
@@ -125,12 +134,15 @@ const formatTime = (time: string) => {
           <div class="destination">
             <div class="title">Destination</div>
             <div class="content">
-              <el-tooltip placement="top">
+              <el-tooltip v-if="isDestinationOverflow" placement="top">
                 <template #content>{{ allData?.transportInfo?.destination || '--' }}</template>
-                <span class="info single-line-ellipsis">{{
+                <span ref="destinationRef" class="info single-line-ellipsis">{{
                   allData?.transportInfo?.destination || '--'
                 }}</span>
               </el-tooltip>
+              <span v-else ref="destinationRef" class="info single-line-ellipsis">{{
+                allData?.transportInfo?.destination || '--'
+              }}</span>
             </div>
           </div>
         </div>

+ 0 - 1
src/views/Booking/src/components/BookingDetail/src/components/BasicInformation.vue

@@ -654,7 +654,6 @@ defineExpose({
   align-items: center;
   height: 16px !important;
   width: 16px;
-  margin-top: -1px;
   padding: 0 !important;
   color: var(--color-neutral-2);
   & > span {

TEMPAT SAMPAH
src/views/Login/src/image/bg-login-card.png


+ 170 - 161
src/views/Login/src/loginView.vue

@@ -249,168 +249,176 @@ const errorTipsRef = ref()
 <template>
   <div class="login">
     <ScoringSystem class="scoring-system"></ScoringSystem>
-    <el-card class="login-card" v-if="status === 'login'">
-      <div class="card-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 ? 'flex' : 'none' }">
-        <div>
-          <span class="font_family icon-icon_confirm_b success-icon"></span>
-          <span class="text-content">{{ emailTipsContent }}</span>
+    <div style="background-color: white">
+      <el-card class="login-card" v-if="status === 'login'">
+        <div class="card-title">
+          <span class="welcome">Welcome to KLN Portal</span>
+          <span class="tips">Login to your account</span>
         </div>
-
-        <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('username')"
-          @blur="handleCheckUser"
-        >
-          <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('password')"
-          ><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('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>
-      </div>
-      <template #footer>
-        <div class="license">
-          <span>© 2024 KTreker from <span class="company">Kerry Logistics</span></span>
-          <span>Version 0.67</span>
+        <div class="send-email-tips" :style="{ display: isEmailTips ? 'flex' : 'none' }">
+          <div>
+            <span class="font_family icon-icon_confirm_b success-icon"></span>
+            <span class="text-content">{{ emailTipsContent }}</span>
+          </div>
+
+          <span
+            @click="handleDeleteEmailTips"
+            class="font_family icon-icon_reject_b delete-icon"
+          ></span>
         </div>
-      </template>
-    </el-card>
-    <el-card class="login-card" v-else-if="status === 'reset'">
-      <div class="card-title">
-        <span class="welcome">Password Retrieval</span>
-        <span class="tips">We'll send your password to your email address.</span>
-      </div>
-      <div class="login-form">
-        <div class="label">
-          <span>User Name</span>
+        <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('username')"
+            @blur="handleCheckUser"
+          >
+            <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('password')"
+            ><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('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>
         </div>
-        <el-input
-          ref="userNameRef"
-          :class="{ 'is-error': loginError.username }"
-          v-model="loginForm.username"
-          class="user-name"
-          placeholder="Please input user name"
-          @focus="handleDeleteEmailTips('username')"
-          @blur="handleCheckUser"
-        >
-          <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>Email Address</span>
+        <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>
+      <el-card class="login-card" v-else-if="status === 'reset'">
+        <div class="card-title">
+          <span class="welcome">Password Retrieval</span>
+          <span class="tips">We'll send your password to your email address.</span>
         </div>
-        <el-input
-          ref="passWordRef"
-          :class="{ 'is-error': loginError.email }"
-          v-model="loginForm.email"
-          placeholder="Please input your email address"
-          @focus="handleDeleteEmailTips('email')"
-          ><template #prefix>
-            <span class="font_family icon-icon_email_b"></span>
-          </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"
-          >Send Password</el-button
-        >
-        <div @click="backLogin(false)" class="back-text">
-          <span class="font_family icon-icon_back_b"></span>
-          <span class="text"> Back to login</span>
+        <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('username')"
+            @blur="handleCheckUser"
+          >
+            <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>Email Address</span>
+          </div>
+          <el-input
+            ref="passWordRef"
+            :class="{ 'is-error': loginError.email }"
+            v-model="loginForm.email"
+            placeholder="Please input your email address"
+            @focus="handleDeleteEmailTips('email')"
+            ><template #prefix>
+              <span class="font_family icon-icon_email_b"></span>
+            </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"
+            >Send Password</el-button
+          >
+          <div @click="backLogin(false)" class="back-text">
+            <span class="font_family icon-icon_back_b"></span>
+            <span class="text"> Back to login</span>
+          </div>
         </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 #footer>
+          <div class="license">
+            <span>© 2024 KTreker from <span class="company">Kerry Logistics</span></span>
+            <span>Version 0.67</span>
+          </div>
+        </template>
+      </el-card>
+    </div>
 
     <ErrorTips ref="errorTipsRef" @forget-password="status = 'reset'"></ErrorTips>
   </div>
@@ -424,8 +432,7 @@ const errorTipsRef = ref()
   align-items: center;
   height: 100%;
   width: 100%;
-  background: url(../src/image/bg.png) no-repeat center center;
-  background-size: cover;
+  background: url(../src/image/bg.png) no-repeat;
   .scoring-system {
     position: absolute;
     top: 0;
@@ -436,7 +443,9 @@ const errorTipsRef = ref()
 
 .login-card {
   width: 400px;
-
+  background: url(../src/image/bg-login-card.png) no-repeat center center;
+  background-position: top left; /* 从左上角开始显示 */
+  background-size: 400px 100px; /* 保持背景图像的原始尺寸 */
   .card-title {
     display: flex;
     flex-direction: column;
@@ -451,7 +460,7 @@ const errorTipsRef = ref()
     //   #e0f7f999 80.46%,
     //   #ffffff4d
     // );
-    background: url(../src/image/bg-login-card.png) no-repeat center center;
+
     .welcome {
       margin-bottom: 16px;
       font-size: 24px;

+ 3 - 1
src/views/Tracking/src/components/PublicTracking/src/PublicTrackingSearch.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import { useRouter } from 'vue-router'
 import { useHeaderSearch } from '@/stores/modules/headerSearch'
+import SlideVerify from './components/SlideVerify.vue'
 
 const router = useRouter()
 
@@ -93,6 +94,7 @@ const handleSearchNo = () => {
         </VEmpty>
       </div>
     </div>
+    <SlideVerify></SlideVerify>
   </div>
 </template>
 
@@ -143,7 +145,7 @@ const handleSearchNo = () => {
       background-color: var(--color-table-header-bg);
     }
     :deep(.el-input-group__append) {
-      padding: 0 12px;
+      padding: 0;
       background-color: black;
       border: 1px solid black;
       border-radius: 0 6px 6px 0;

+ 0 - 1
src/views/Tracking/src/components/PublicTracking/src/components/BasicInformation.vue

@@ -402,7 +402,6 @@ const handleCopy = (data: any) => {
   height: 16px !important;
   width: 16px;
   padding: 0 !important;
-  margin-top: -1px;
   color: var(--color-neutral-2);
   & > span {
     display: block;

+ 16 - 4
src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue

@@ -4,6 +4,7 @@ import MilestonesTable from './MilestonesTable.vue'
 import { transportationMode } from '@/components/TransportationMode'
 import { useRoute } from 'vue-router'
 import dayjs from 'dayjs'
+import { useOverflow } from '@/hooks/useOverflow'
 
 const route = useRoute()
 
@@ -66,6 +67,11 @@ if (Object.keys(sharedData).length === 0) {
 const formatTime = (time: string) => {
   return time ? dayjs(time).format('MMM-DD-YYYY hh:mm A') : '--'
 }
+
+const originRef = ref()
+const destinationRef = ref()
+const { isOverflow: isOriginOverflow } = useOverflow(originRef, allData)
+const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allData)
 </script>
 
 <template>
@@ -85,12 +91,15 @@ const formatTime = (time: string) => {
           <div class="origin">
             <div class="title">Origin</div>
             <div class="content">
-              <el-tooltip placement="top">
+              <el-tooltip v-if="isOriginOverflow" placement="top">
                 <template #content>{{ allData?.transportInfo?.origin || '--' }}</template>
-                <span class="info single-line-ellipsis">{{
+                <span ref="originRef" class="info single-line-ellipsis">{{
                   allData?.transportInfo?.origin || '--'
                 }}</span>
               </el-tooltip>
+              <span ref="originRef" v-else class="info single-line-ellipsis">{{
+                allData?.transportInfo?.origin || '--'
+              }}</span>
               <div class="line_container">
                 <hr color="#000000" />
                 <div class="right-icon"></div>
@@ -100,12 +109,15 @@ const formatTime = (time: string) => {
           <div class="destination">
             <div class="title">Destination</div>
             <div class="content">
-              <el-tooltip placement="top">
+              <el-tooltip v-if="isDestinationOverflow" placement="top">
                 <template #content>{{ allData?.transportInfo?.destination || '--' }}</template>
-                <span class="info single-line-ellipsis">{{
+                <span ref="destinationRef" class="info single-line-ellipsis">{{
                   allData?.transportInfo?.destination || '--'
                 }}</span>
               </el-tooltip>
+              <span v-else ref="destinationRef" class="info single-line-ellipsis">{{
+                allData?.transportInfo?.destination || '--'
+              }}</span>
             </div>
           </div>
         </div>

+ 174 - 0
src/views/Tracking/src/components/PublicTracking/src/components/SlideVerify.vue

@@ -0,0 +1,174 @@
+<script lang="ts" setup>
+const dialogVisible = ref(true)
+
+const position = ref(0)
+const isDragging = ref(false)
+const verifyText = ref('Swipe right to verify')
+const sliderState = ref<'start' | 'success' | 'error'>('start')
+const styleMap = {
+  start: {
+    thumbColor: 'var(--color-neutral-1)',
+    thumbIcon: 'icon-icon_drag__line_b',
+    trackBackground: '#87909e'
+  },
+  success: {
+    thumbColor: '#fff',
+    thumbIcon: 'icon-icon_confirm_b',
+    trackBackground: 'var(--color-success)'
+  },
+  error: {
+    thumbColor: '#fff',
+    thumbIcon: 'icon-icon_reject_b',
+    trackBackground: '#c7353f'
+  }
+}
+const trackRef = ref<HTMLElement | null>(null)
+
+const startDrag = (event: MouseEvent) => {
+  // 如果已经成功验证,则不允许再次拖动
+  if (sliderState.value === 'success') {
+    return
+  }
+
+  isDragging.value = true
+  document.addEventListener('mousemove', onDrag)
+  document.addEventListener('mouseup', stopDrag)
+}
+
+const onDrag = (event: MouseEvent) => {
+  if (isDragging.value) {
+    if (trackRef.value) {
+      sliderState.value = 'start'
+      verifyText.value = 'Swipe right to verify'
+      const rect = trackRef.value.getBoundingClientRect()
+      const offsetX = event.clientX - rect.left
+      position.value = Math.min(Math.max(offsetX, 0), rect.width - 40) // 40是滑块的宽度
+    }
+  }
+}
+
+const stopDrag = () => {
+  isDragging.value = false
+  document.removeEventListener('mousemove', onDrag)
+  document.removeEventListener('mouseup', stopDrag)
+
+  // 检查是否滑动到位
+  if (trackRef.value) {
+    const trackWidth = trackRef.value.offsetWidth
+    if (position.value >= trackWidth - 40) {
+      sliderState.value = 'success'
+      verifyText.value = 'Verification successful'
+    } else {
+      sliderState.value = 'error'
+      verifyText.value = 'Verification failed'
+      // position.value = 0; // 重置位置
+    }
+  }
+}
+
+const moveSlider = (event: MouseEvent) => {
+  // 仅在未成功时允许点击移动滑块
+  if (sliderState.value !== 'success') {
+    onDrag(event)
+  }
+}
+</script>
+
+<template>
+  <el-dialog top="30vh" v-model="dialogVisible" width="400" class="slide-verify-dialog">
+    <div class="content">
+      <p>Please drag the slider below to complete the</p>
+      <p>verification to ensure normal access</p>
+      <div class="slider-container">
+        <div
+          class="slider-track"
+          :style="{ backgroundColor: styleMap[sliderState].trackBackground }"
+          @click="moveSlider"
+          ref="trackRef"
+        >
+          {{ verifyText }}
+          <div
+            class="slider-thumb"
+            :style="{ left: `${position}px`, borderColor: styleMap[sliderState].trackBackground }"
+            @mousedown="startDrag"
+          >
+            <span
+              v-if="sliderState === 'start'"
+              class="font_family"
+              :style="{ color: styleMap[sliderState].thumbColor }"
+              :class="[styleMap[sliderState].thumbIcon]"
+            ></span>
+            <span
+              v-else
+              class="font_family other-state"
+              :style="{
+                color: styleMap[sliderState].thumbColor,
+                backgroundColor: styleMap[sliderState].trackBackground
+              }"
+              :class="[styleMap[sliderState].thumbIcon]"
+            ></span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.content {
+  padding: 40px 0;
+  text-align: center;
+  & > p {
+    line-height: 21px;
+  }
+}
+
+.slider-container {
+  width: 320px;
+  margin: 16px auto 0;
+
+  text-align: center;
+}
+.slider-track {
+  position: relative;
+  width: 100%;
+  height: 40px;
+  padding: 1px;
+  background: #868f9d;
+  border-radius: 6px;
+  line-height: 38px;
+  color: #fff;
+}
+.slider-thumb {
+  position: absolute;
+  top: 0px;
+  left: 10px;
+  width: 40px;
+  height: 40px;
+  background: #fff;
+  cursor: pointer;
+  border-radius: 6px;
+  border: 1px solid #868f9d;
+  .font_family {
+    font-size: 14px;
+    &.other-state {
+      height: 16px;
+      width: 16px;
+      padding: 1px;
+      border-radius: 50%;
+      font-size: 14px;
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.slide-verify-dialog {
+  .el-dialog__header {
+    display: none;
+  }
+  .el-dialog__body {
+    padding: 0;
+  }
+}
+</style>

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

@@ -108,6 +108,7 @@ getData()
 const formatTime = (time: string) => {
   return time ? dayjs(time).format('MMM-DD-YYYY hh:mm A') : '--'
 }
+
 const originRef = ref()
 const destinationRef = ref()
 const { isOverflow: isOriginOverflow } = useOverflow(originRef, allData)

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

@@ -650,7 +650,6 @@ defineExpose({
   height: 16px !important;
   width: 16px;
   padding: 0 !important;
-  margin-top: -1px;
   color: var(--color-neutral-2);
   & > span {
     display: block;

+ 8 - 0
src/views/Tracking/src/components/TrackingDetail/src/components/RoutesView.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
 import { transportationMode } from '@/components/TransportationMode'
+import { useOverflow } from '@/hooks/useOverflow'
 
 const props = defineProps({
   data: Object
@@ -52,6 +53,13 @@ watch(
 const formatDate = (date: string) => {
   return date ? dayjs(date).format('MMM-DD-YYYY HH:mm A') : '--'
 }
+
+const basicOriginRef = ref()
+const basicDestinationRef = ref()
+let isOverflowData = ''
+
+const { isOverflow: isOriginOverflow } = useOverflow(basicOriginRef, props)
+const { isOverflow: isDestinationOverflow } = useOverflow(basicDestinationRef, props)
 </script>
 
 <template>

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini