فهرست منبع

521记住密码滑动条

tangjunhao 3 ماه پیش
والد
کامیت
f4850b8e45
6فایلهای تغییر یافته به همراه924 افزوده شده و 3206 حذف شده
  1. 698 3200
      package-lock.json
  2. 1 0
      package.json
  3. 22 0
      src/utils/secureStorage.js
  4. 175 0
      src/views/login/SlideToUnlock.vue
  5. 27 5
      src/views/login/index.vue
  6. 1 1
      src/views/titlecomponent/MathFunc.vue

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 698 - 3200
package-lock.json


+ 1 - 0
package.json

@@ -22,6 +22,7 @@
     "@vue-flow/node-toolbar": "^1.1.0",
     "axios": "^1.5.0",
     "canvg": "^4.0.2",
+    "crypto-js": "^4.2.0",
     "echarts": "^5.4.3",
     "element-plus": "^2.3.14",
     "h5wasm": "^0.8.1",

+ 22 - 0
src/utils/secureStorage.js

@@ -0,0 +1,22 @@
+import CryptoJS from 'crypto-js'
+
+const SECRET_KEY = 'your-secret-key' // 设置加密密钥
+
+export default {
+  set(key, value) {
+    const encrypted = CryptoJS.AES.encrypt(value, SECRET_KEY).toString()
+    localStorage.setItem(key, encrypted)
+  },
+  get(key) {
+    const encrypted = localStorage.getItem(key)
+    if (!encrypted) return null
+    try {
+      return CryptoJS.AES.decrypt(encrypted, SECRET_KEY).toString(CryptoJS.enc.Utf8)
+    } catch {
+      return null
+    }
+  },
+  remove(key) {
+    localStorage.removeItem(key)
+  }
+}

+ 175 - 0
src/views/login/SlideToUnlock.vue

@@ -0,0 +1,175 @@
+<template>
+  <!-- 滑动验证码容器 -->
+  <div class="slider-container" ref="containerRef">
+    <!-- 滑动遮罩层,显示滑动进度 -->
+    <div class="slider-mask" :style="{ width: sliderMaskWidth }" />
+    <!-- 滑块按钮 -->
+    <div
+      class="slider"
+      ref="sliderRef"
+      @mousedown.prevent="handleDragStart" 
+      :class="{ 'slider-success': verified }"
+      :style="{ left: sliderLeft }"
+    >
+      <!-- 滑块图标 -->
+      <el-icon><DArrowRight /></el-icon>
+    </div>
+    <!-- 提示文本 -->
+    <div class="slider-text" :class="{ 'slider-text-success': verified }">
+      {{ verified ? "验证通过" : "请按住滑块,拖动至最右侧" }}
+    </div>
+  </div>
+</template>
+ 
+<script setup lang="ts">
+import { DArrowRight } from "@element-plus/icons-vue"
+ 
+const emit = defineEmits(["verifySuccess"])
+ 
+// DOM 引用
+const containerRef = ref<HTMLElement | null>(null) // 容器元素引用
+const sliderRef = ref<HTMLElement | null>(null) // 滑块元素引用
+ 
+// 状态变量
+const sliderLeft = ref("0px") // 滑块左侧位置
+const sliderMaskWidth = ref("0px") // 遮罩层宽度
+const verified = ref(false) // 验证状态
+ 
+// 拖动相关变量
+let startX = 0 // 开始拖动时的 X 坐标
+let sliderLeft_temp = 0 // 临时存储滑块位置
+ 
+/**
+ * 开始拖动处理
+ * @param e 鼠标事件对象
+ */
+const handleDragStart = (e: MouseEvent) => {
+  if (verified.value) return // 如果已验证通过,不再处理
+  startX = e.clientX
+  sliderLeft_temp = parseInt(sliderLeft.value)
+  // 添加鼠标移动和松开事件监听
+  document.addEventListener("mousemove", handleDragMove)
+  document.addEventListener("mouseup", handleDragEnd)
+}
+ 
+/**
+ * 拖动过程处理
+ * @param e 鼠标事件对象
+ */
+const handleDragMove = (e: MouseEvent) => {
+  if (verified.value) return
+  const container = containerRef.value
+  if (!container) return
+ 
+  // 计算拖动距离
+  const diff = e.clientX - startX
+  const containerWidth = container.offsetWidth
+  const sliderWidth = sliderRef.value?.offsetWidth || 0
+  const maxLeft = containerWidth - sliderWidth
+ 
+  // 计算新位置并限制范围
+  let newLeft = sliderLeft_temp + diff
+  if (newLeft < 0) newLeft = 0
+  if (newLeft > maxLeft) newLeft = maxLeft
+ 
+  // 更新滑块和遮罩层位置
+  sliderLeft.value = newLeft + "px"
+  sliderMaskWidth.value = newLeft + sliderWidth / 2 + "px"
+ 
+  // 检查是否验证成功
+  if (newLeft === maxLeft) {
+    verified.value = true
+    emit("verifySuccess", true) // 触发验证成功
+    handleDragEnd()
+  }
+}
+ 
+/**
+ * 结束拖动处理
+ */
+const handleDragEnd = () => {
+  if (!verified.value) {
+    // 如果未验证成功,重置位置
+    sliderLeft.value = "0px"
+    sliderMaskWidth.value = "0px"
+  }
+  // 移除事件监听
+  document.removeEventListener("mousemove", handleDragMove)
+  document.removeEventListener("mouseup", handleDragEnd)
+}
+ 
+// 组件卸载时清理事件监听
+onUnmounted(() => {
+  document.removeEventListener("mousemove", handleDragMove)
+  document.removeEventListener("mouseup", handleDragEnd)
+})
+</script>
+ 
+<style scoped>
+/* 滑动验证码容器样式 */
+.slider-container {
+  position: relative;
+  width: 100%;
+  height: 30px;
+  background-color: #B3B3B3;
+  /* border: 1px solid #B3B3B3 ; */
+  border-radius: 4px;
+  margin-bottom: 20px;
+}
+ 
+/* 滑动遮罩层样式 */
+.slider-mask {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  background-color: #18961C;
+  border-radius: 4px;
+  /* transition: width 0.1s linear; */
+}
+ 
+/* 滑块按钮样式 */
+.slider {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 50px;
+  height: 30px;
+  background: #FFFFFF;
+  border: 1px solid #B3B3B3 ;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: background-color 0.2s;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #B3B3B3;
+}
+ 
+/* 验证成功时滑块样式 */
+.slider-success {
+  /* background-color: #18961C; */
+}
+ 
+/* 提示文本样式 */
+.slider-text {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  user-select: none;
+  font-weight: 400;
+  font-size: 12px;
+  color: #FFFFFF;
+  width: auto;
+
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+ 
+/* 验证成功时文本样式 */
+.slider-text-success {
+  color: #FFFFFF;
+}
+</style>

+ 27 - 5
src/views/login/index.vue

@@ -41,17 +41,22 @@
           
             <el-form-item prop="remember">
               <div style="width: 100%; display: flex; justify-content: space-between;">
-                <el-checkbox v-model="loginForm.remember">记住</el-checkbox>
+                <el-checkbox v-model="loginForm.remember">记住密码</el-checkbox>
                 <el-link type="info">忘记密码</el-link>
               </div>
             </el-form-item>
 
+            <el-form-item>
+              <SlideToUnlock @verifySuccess="verifySuccess" />
+            </el-form-item>
+
             <el-form-item>
               <el-button
                 type="primary"
                 class="login-btn"
                 :loading="loading"
                 @click="handleLogin"
+                :disabled="!isverify"
               >
                 登 录
               </el-button>
@@ -73,9 +78,11 @@
 import { ElMessage } from 'element-plus'
 import router from "@/router"
 import { request,enPassword } from "@/utils/request"
+import secureStorage from '@/utils/secureStorage'
 
 import { getToken, setToken, setUserId} from "@/utils/token"
 
+import SlideToUnlock from '@views/login/SlideToUnlock.vue'
 import loginbg from '@/assets/img/login-bg.png'
 import loginLogo from '@/assets/img/login-logo.png'
 // 表单引用
@@ -83,14 +90,16 @@ const loginFormRef = ref(null)
 
 // 登录表单数据
 const loginForm = reactive({
-  username: '',
-  password: '',
-  remember: false
+  username: secureStorage.get('savedUsername') || '',
+  password: secureStorage.get('savedPassword') || '',
+  remember: secureStorage.get('rememberPassword') === 'true'
 })
 
 // 加载状态
 const loading = ref(false)
 
+const isverify = ref(false)
+
 // 表单验证规则
 const loginRules = reactive({
   username: [
@@ -103,11 +112,24 @@ const loginRules = reactive({
   ]
 })
 
+// 滑动验证成功
+const verifySuccess = () => {
+  isverify.value = true
+}
+
 // 登录方法
 const handleLogin = () => {
   loginFormRef.value.validate(valid => {
     if (!valid) return
-    
+    if (loginForm.remember) {
+        secureStorage.set('savedUsername', loginForm.username)
+        secureStorage.set('savedPassword', loginForm.password)
+        secureStorage.set('rememberPassword', 'true')
+      } else {
+        secureStorage.remove('savedUsername')
+        secureStorage.remove('savedPassword')
+        secureStorage.remove('rememberPassword')
+      }
     loading.value = true
     
     const params = {

+ 1 - 1
src/views/titlecomponent/MathFunc.vue

@@ -169,7 +169,7 @@ watch(
 
     // 遍历 outParams,添加 name = {name} 到 equation
     newParams.forEach(param => {
-      newEquation += `${param.name} = \n`;
+      newEquation += `${param.name}= \n`;
     });
 
     // 更新 equation 的值

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است