前言
实现需求
- 表单验证
- 防抖和节流
- 登录预验证
- 路由导航守卫
参考
开发步骤
| <script lang="ts"> |
| export default { |
| data() { |
| return { |
| ruleForm: { |
| name: '', |
| pass: '', |
| }, |
| flag: false, |
| rules: { |
| name: [ |
| { |
| required: true, |
| message: '请输入用户名!', |
| trigger: 'blur', |
| }, |
| ], |
| pass: [ |
| { |
| required: true, |
| message: '请输入密码!', |
| trigger: 'blur', |
| }, |
| ], |
| }, |
| } |
| }, |
| } |
| </script> |
| |
对提交按钮进行防抖和节流操作:提交按钮点击一次后隔2秒才能再次点击,避免用户狂点,减轻服务器压力
| <template> |
| <div> |
| <el-form-item> |
| |
| <el-button type="primary" @click="submitForm('ruleForm')" :disabled='flag' |
| >Login</el-button |
| > |
| <el-button @click="resetForm('ruleForm')">Reset</el-button> |
| </el-form-item> |
| </div> |
| </template> |
| |
| <script lang="ts"> |
| export default { |
| data() { |
| return { |
| flag: false, |
| methods: { |
| submitForm(formName) { |
| this.$refs[formName].validate((valid) => { |
| if (valid) { |
| console.log(this.ruleForm) |
| this.flag = true; |
| setTimeout(()=>{ |
| console.log("可再次提交!") |
| this.flag= false; |
| }, 2000) |
| } else { |
| console.log('error submit!!') |
| return false |
| } |
| }) |
| }, |
| resetForm(formName) { |
| this.$refs[formName].resetFields() |
| }, |
| }, |
| } |
| </script> |
| |
element plus的表单中封装了validate函数,只有通过了表单验证才能向后端发送请求
| <script lang="ts"> |
| export default { |
| methods: { |
| submitForm(formName) { |
| this.$refs[formName].validate((valid) => { |
| if (valid) { |
| |
| alert('submit!') |
| } else { |
| |
| console.log('error submit!!') |
| return false |
| } |
| }) |
| }, |
| resetForm(formName) { |
| this.$refs[formName].resetFields() |
| }, |
| }, |
| } |
| </script> |
| |
- 自定义验证规则时,在自定义的规则中添加
callback()
回调函数,否则验证完后永远无法进入validate函数,这里是一个坑
| <script lang="ts"> |
| export default { |
| data() { |
| const checkName = (rule, value, callback) => { |
| if (!value) { |
| return callback(new Error('请输入用户名!')) |
| } else { |
| callback() |
| } |
| } |
| const validatePass = (rule, value, callback) => { |
| if (value === '') { |
| callback(new Error('请输入密码!')) |
| } else { |
| if (this.ruleForm.checkPass !== '') { |
| this.$refs.ruleForm.validateField('checkPass') |
| } |
| callback() |
| } |
| } |
| const validatePass2 = (rule, value, callback) => { |
| if (value === '') { |
| callback(new Error('请再次输入密码!')) |
| } else if (value !== this.ruleForm.pass) { |
| callback(new Error("两次密码不一致!")) |
| } else { |
| callback() |
| } |
| } |
| return { |
| ruleForm: { |
| pass: '', |
| checkPass: '', |
| name: '', |
| }, |
| rules: { |
| pass: [{ validator: validatePass, trigger: 'blur' }], |
| checkPass: [{ validator: validatePass2, trigger: 'blur' }], |
| name: [{ validator: checkName, trigger: 'blur' }], |
| }, |
| flag: false, |
| } |
| }, |
| methods: { |
| submitForm(formName) { |
| this.$refs[formName].validate((valid) => { |
| if (valid) { |
| |
| console.log("success submit!") |
| } else { |
| console.log('error submit!!') |
| return false |
| } |
| }) |
| }, |
| resetForm(formName) { |
| this.$refs[formName].resetFields() |
| }, |
| }, |
| } |
| </script> |
| |
使用路由导航守卫:不能直接通过url跳转到控制台,只有当用户登录后才能访问控制台组件
| |
| <script lang="ts"> |
| export default { |
| methods: { |
| submitForm(formName) { |
| this.$refs[formName].validate((valid) => { |
| if (valid) { |
| console.log(this.ruleForm) |
| this.flag = true; |
| |
| const username = this.ruleForm.name; |
| const password = this.ruleForm.pass; |
| const reqData = { |
| username: username, |
| password, |
| } |
| this.$http.post("user/login", reqData).then((response)=>{ |
| console.log(response) |
| |
| window.sessionStorage.setItem('token', response.data) |
| |
| this.$router.push('/consoleManage') |
| }).catch(error=>{console.error(error)}); |
| |
| setTimeout(()=>{ |
| console.log("可再次提交!") |
| this.flag= false; |
| }, 2000) |
| } else { |
| console.log('error submit!!') |
| return false |
| } |
| }) |
| }, |
| resetForm(formName) { |
| this.$refs[formName].resetFields() |
| }, |
| }, |
| } |
| </script> |
| |
| |
| import router from './route/index' |
| |
| |
| router.beforeEach((to, from, next) => { |
| |
| if(to.path === '/consoleManage') { |
| const tokenStr = window.sessionStorage.getItem('token') |
| if(!tokenStr) return next('/userLogin') |
| } |
| next() |
| }) |
| |
-
登录时表单验证效果

-
登录成功后查看sessionStorage

-
退出登录
| <template> |
| <div> |
| <el-button type="info" @click="logout">退出登录</el-button> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| methods: { |
| logout() { |
| window.sessionStorage.clear(); |
| this.$router.push("/login"); |
| } |
| } |
| }; |
| </script> |
| |
点击查看详情
| <template> |
| <div class="box"> |
| <img src="https://pic.imgdb.cn/item/61b812a02ab3f51d91b0a6ca.png" alt=""> |
| <div class="register_box"> |
| <span class="regiter_span">注册用户</span> |
| <el-form |
| ref="ruleForm" |
| :model="ruleForm" |
| status-icon |
| :rules="rules" |
| label-width="120px" |
| class="demo-ruleForm" |
| > |
| <span class="icon_avatar"> |
| <el-icon><Avatar /></el-icon> |
| </span> |
| <el-form-item label="UserName" prop="name"> |
| <el-input v-model.number="ruleForm.name"></el-input> |
| </el-form-item> |
| <span class="icon_unlock"> |
| <el-icon><Unlock /></el-icon> |
| </span> |
| <el-form-item label="Password" prop="pass"> |
| <el-input |
| v-model="ruleForm.pass" |
| type="password" |
| autocomplete="off" |
| ></el-input> |
| </el-form-item> |
| <span class="icon_Sugar"> |
| <el-icon><Sugar /></el-icon> |
| </span> |
| <el-form-item label="ReUsePass" prop="checkPass"> |
| <el-input |
| v-model="ruleForm.checkPass" |
| type="password" |
| autocomplete="off" |
| ></el-input> |
| </el-form-item> |
| <el-form-item> |
| <el-button type="primary" @click="submitForm('ruleForm')" :disabled='flag' |
| >Submit</el-button |
| > |
| <el-button @click="resetForm('ruleForm')" class="but_re">Reset</el-button> |
| </el-form-item> |
| </el-form> |
| <span class="regiter_link"> |
| <router-link to="/"> 已有账号,去登录</router-link> |
| </span> |
| </div> |
| </div> |
| </template> |
| |
| <script lang="ts"> |
| |
| import { Avatar, Unlock, Sugar } from '@element-plus/icons' |
| export default { |
| components: { |
| Unlock, |
| Avatar, |
| Sugar |
| }, |
| setup() { |
| |
| }, |
| data() { |
| const checkName = (rule, value, callback) => { |
| if (!value) { |
| return callback(new Error('请输入用户名!')) |
| } else { |
| callback() |
| } |
| } |
| const validatePass = (rule, value, callback) => { |
| if (value === '') { |
| callback(new Error('请输入密码!')) |
| } else { |
| if (this.ruleForm.checkPass !== '') { |
| this.$refs.ruleForm.validateField('checkPass') |
| } |
| callback() |
| } |
| } |
| const validatePass2 = (rule, value, callback) => { |
| if (value === '') { |
| callback(new Error('请再次输入密码!')) |
| } else if (value !== this.ruleForm.pass) { |
| callback(new Error("两次密码不一致!")) |
| } else { |
| callback() |
| } |
| } |
| return { |
| ruleForm: { |
| pass: '', |
| checkPass: '', |
| name: '', |
| }, |
| rules: { |
| pass: [{ validator: validatePass, trigger: 'blur' }], |
| checkPass: [{ validator: validatePass2, trigger: 'blur' }], |
| name: [{ validator: checkName, trigger: 'blur' }], |
| }, |
| flag: false, |
| } |
| }, |
| methods: { |
| submitForm(formName) { |
| this.$refs[formName].validate((valid) => { |
| if (valid) { |
| |
| this.flag = true; |
| |
| const username = this.ruleForm.name; |
| const password = this.ruleForm.pass; |
| const reqData = { |
| username: username, |
| password, |
| } |
| this.$http.post("user/register", reqData).then((response)=>{ |
| console.log(response) |
| if(response.code == 200 ) { |
| console.log("注册成功!") |
| |
| this.$router.push('/userLogin') |
| } |
| }).catch(error=>{console.error(error)}); |
| |
| setTimeout(()=>{ |
| console.log("可再次提交!") |
| this.flag= false; |
| }, 2000) |
| } else { |
| console.log('error submit!!') |
| return false |
| } |
| }) |
| }, |
| resetForm(formName) { |
| this.$refs[formName].resetFields() |
| }, |
| }, |
| } |
| </script> |
| |
| <style lang="less"> |
| .box { |
| width: 100%; |
| height: 100%; |
| } |
| img { |
| float: left; |
| height: 810px; |
| width: 1556px; |
| opacity: 0.7; |
| } |
| .register_box { |
| width: 450px; |
| height: 380px; |
| border-radius: 5px; |
| border: 2px gray solid; |
| float: left; |
| position: absolute; |
| top: 200px; |
| left: 500px; |
| background-color: white; |
| opacity: 0.7; |
| } |
| .regiter_span { |
| font-size: larger; |
| position: relative; |
| top: 15px; |
| left: 180px; |
| } |
| .el-form { |
| position: relative; |
| top: 15px; |
| padding-right: 20px; |
| } |
| .icon_avatar{ |
| position: relative; |
| top: 30px; |
| left: 20px; |
| } |
| .icon_unlock{ |
| position: relative; |
| top: 30px; |
| left: 20px; |
| } |
| .icon_Sugar { |
| position: relative; |
| top: 30px; |
| left: 20px; |
| } |
| .regiter_link { |
| position: relative; |
| left: 305px; |
| top: 13px; |
| } |
| .el-button { |
| position: relative; |
| top: 35px; |
| left: 150px; |
| } |
| .el-button { |
| position: relative; |
| top: 5px; |
| left: 20px; |
| } |
| .but_re{ |
| position: relative; |
| left: 70px; |
| } |
| </style> |
| |
- 表单验证效果

补充
- 发送post请求时报错:
415Unsupported media type
- 错误原因:
传给后端接口的参数格式不正确
- 解决错误:
这是因为我自己封装的axios有问题,注释掉如下代码即可

· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术