通用vue组件化登录页面

news2024/11/23 9:30:42

一、首页设置大体的样式布局

1.首先建立一个login文件夹,在里面建立对应的login.vue文件

2.设置登录页面的背景图,在App.vue文件中使用router-view进行展示登录组件

3.先给App.vue的div元素设置高度100%,之后在login.vue里面去设置背景图

App.vue

<template>
  <div class="app">
    <router-view></router-view>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue"

export default defineComponent({
  name: "App"
})
</script>

<style lang="less">
/* 先将主面板设置宽度,之后再去设置对应的组件login */
.app {
  height: 100%;
}
</style>

login.vue

这里注意怎么使login组件垂直水平居中的样式

<template>
  <div class="login">login</div>
</template>
<script lang="ts">
import { defineComponent } from "vue"
export default defineComponent({
  name: "login"
})
</script>
<style lang="less" scoped>
.login {
  /* 实现水平垂直居中 */
  /* **display:flex**布局 justify-content定义水平方向的元素位置,align-items定义垂直方向的元素位置 */
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  background: url(../../assets/img/login-bg.svg);
}
</style>

 二、开始使用组件化引用

1.首先建立一个cpns的文件夹,里面有一个login-panel.vue文件

2.之后在login.vue文件中引用这个组件

3.将登录页面的组件代码封装在login-panel.vue

login.vue

<template>
  <div class="login">
//引用组件
    <login-panel></login-panel>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue"
import LoginPanel from "./cpns/login-panel.vue"
export default defineComponent({
//注册组件
  components: {
    LoginPanel
  }
})
</script>
<style lang="less" scoped>
.login {
  /* 实现水平垂直居中 */
  /* **display:flex**布局 justify-content定义水平方向的元素位置,align-items定义垂直方向的元素位置 */
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  background: url(../../assets/img/login-bg.svg);
}
</style>

 login-panel.vue

<template>
  <div class="login-panel">
    <h1>后台管理系统</h1>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue"
export default defineComponent({
  name: "login-panel"
})
</script>
<style lang="less" scoped></style>

 三.开始编写中间登录界面的主要代码

1.首先去element-plus官网找适合的模板样式,之后直接copy到login-panel.vue组件中,分别给对应的登添加相应的icon图标

(1)首先先安装npm install @element-plus/icons

(2)之后复制相应的图标代码avatar

(3)新的icon需要有一层el-icon去包裹才能生效

<el-icon style="vertical-align: middle"

                ><avatar class="tubiao"

              /></el-icon>

(4)在对应的文件里面引用相应的文件,还得同时注册一下组件

import { Avatar, Cellphone } from "@element-plus/icons"

  components: {

    Avatar,

    Cellphone,

  }

2.建立账号登录和手机登录的两个组件文件login-account.vue和login-phone.vue

3.将封装好的组件引用到login-panel.vue对应的地方

<template>
  <div class="login-panel">
    <h1 class="title">后台管理系统</h1>
    <!-- stretch这个是设置是否自适应宽度 -->
    <el-tabs type="border-card" class="demo-tabs" stretch>
      <el-tab-pane>
        <template #label>
          <span class="custom-tabs-label">
            <el-icon><calendar /></el-icon>
            <span>
              <el-icon style="vertical-align: middle"
                ><avatar class="tubiao"
              /></el-icon>
              账号登录</span
            >
          </span>
        </template>
        <login-account></login-account>
      </el-tab-pane>
      <el-tab-pane>
        <template #label>
          <span class="custom-tabs-label">
            <el-icon><calendar /></el-icon>
            <span
              ><el-icon style="vertical-align: middle"
                ><cellphone class="tubiao" /></el-icon
              >手机登录</span
            >
          </span>
        </template>
        <login-phone></login-phone>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue"
import { Avatar, Cellphone } from "@element-plus/icons"
import LoginPhone from "./login-phone.vue"
import LoginAccount from "./login-account.vue"

export default defineComponent({
  name: "login-panel",
  components: {
    Avatar,
    Cellphone,
    LoginPhone,
    LoginAccount
  }
})
</script>
<style lang="less" scoped>
.login-panel {
  margin-bottom: 100px;
  width: 320px;
  .title {
    /* 使标题居中 */
    text-align: center;
  }
  .account-control {
    margin-top: 10px;
    display: flex;
    justify-content: space-between;
  }
}
</style>

四、分别在对应的文件里面写对应的文本框login-account.vue和login-phone.vue 

(1)login-account.vue

  • 首先先选择合适的文本框
  • 然后编写相应的响应式数据
  • 编写相应的规则校验
<template>
  <div class="login-account">
    <el-form label-width="60px" :rules="rules" :model="account" ref="formRef">
      <el-form-item label="账号" prop="name">
        <el-input v-model="account.name" />
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input v-model="account.password" show-password />
      </el-form-item>
    </el-form>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue"

export default defineComponent({
  name: "login-account",
  components: {},
  setup() {
    const account = reactive({
      name: "",
      password: ""
    })
    // 编写规则
    const rules = {
      name: [
        {
          // 是否是必传
          required: true,
          message: "用户名是必传内容",
          // 失去焦点是判断是否符合要求规则
          trigger: "blur"
        },
        {
          pattern: /^[a-z0-9]{5,10}$/,
          message: "用户名必须是5-10个字母或者数字",
          trigger: "blur"
        }
      ],
      password: [
        {
          required: true,
          message: "密码是必传内容",
          trigger: "blur"
        },
        {
          pattern: /^[a-z0-9]{3,}$/,
          message: "密码必须是3位以上的字母或者数字",
          trigger: "blur"
        }
      ]
    }
    return {
      account,
      rules
    }
  }
})
</script>
<style lang="less" scoped></style>

为了使代码具有简洁性,可以选择把规则校验抽离到一个单独的文件account-config.ts中,之后可以选择把那些没有响应的数据单独抽离到一个配置文件,然后再引用进来

account-config.ts

// 编写规则
export const rules = {
  name: [
    {
      required: true,
      message: '用户名是必传内容',
      // 失去焦点是判断是否符合要求规则
      trigger: 'blur'
    },
    {
      pattern: /^[a-z0-9]{5,10}$/,
      message: '用户名必须是5-10个字母或者数字',
      trigger: 'blur'
    }
  ],
  password: [
    {
      required: true,
      message: '密码是必传内容',
      trigger: 'blur'
    },
    {
      pattern: /^[a-z0-9]{3,}$/,
      message: '密码必须是3位以上的字母或者数字',
      trigger: 'blur'
    }
  ]
}

login-account.vue

<template>
  <div class="login-account">
    <el-form label-width="60px" :rules="rules" :model="account" ref="formRef">
      <el-form-item label="账号" prop="name">
        <el-input v-model="account.name" />
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input v-model="account.password" show-password />
      </el-form-item>
    </el-form>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue"
import { rules } from "../config/account-config"

export default defineComponent({
  name: "login-account",
  components: {},
  setup() {
    const account = reactive({
      name: "",
      password: ""
    })

    return {
      account,
      rules
    }
  }
})
</script>
<style lang="less" scoped></style>

(2)login-phone.vue 

具体和账号登录一样的步骤

  • 首先先选择合适的文本框
  • 然后编写相应的响应式数据
  • 编写相应的规则校验

verify-code-config.ts

// 编写规则
export const rules = {
  phone: [
    {
      required: true,
      message: '手机是必传内容',
      // 失去焦点是判断是否符合要求规则
      trigger: 'blur'
    },
    {
      pattern: /^[a-z0-9]{5,10}$/,
      message: '手机号必须是5-10个字母或者数字',
      trigger: 'blur'
    }
  ],
  verifycode: [
    {
      required: true,
      message: '验证码是必传内容',
      trigger: 'blur'
    },
    {
      pattern: /^[a-z0-9]{3,}$/,
      message: '验证必须是3位以上的字母或者数字',
      trigger: 'blur'
    }
  ]
}

login-phone.vue

<template>
  <div>
    <!--  :model="account"将下面的input的最新的值给传给form,如何进行验证-->
    <el-form label-width="70px" :rules="rules" :model="shouji">
      <el-form-item label="手机号" prop="phone">
        <el-input v-model="shouji.phone"></el-input>
      </el-form-item>
      <el-form-item label="验证码" prop="verifycode">
        <div class="get-code">
          <el-input v-model="shouji.verifycode"></el-input>
          <el-button type="primary" class="get-bin">获取验证码</el-button>
        </div>
      </el-form-item>
    </el-form>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue"
import { rules } from "../config/verify-code-config.ts"
export default defineComponent({
  setup() {
    const shouji = reactive({
      phone: "",
      verifycode: ""
    })
    return {
      shouji,
      rules
    }
  }
})
</script>
<style scoped>
.get-code {
  display: flex;
}
.get-bin {
  margin-left: 8px;
}
</style>

 五、对登录进行验证以及校验

1.先给点击登录按钮绑定点击事件

2.然后在对应的组件里面也绑定对应的事件

3.利用ts语法拿到账号登录组件

4.在账号登录组件里面拿到对应的表单验证的布尔值,当点击登录触发事件时,判断布尔值,决定是否能够登录成功

login-panel.vue

  // 拿到对应的组件LoginAccount
    const accountRef = ref<InstanceType<typeof LoginAccount>>()
    // 2.定义方法
    const handleLoginClick = () => {
      if (currentTab.value === "account") {
        // 为什么加?,是因为确保一开始是没有值的,这里就是表示可由可无
        accountRef.value?.loginAction(isKeepPassword.value)
      } else {
        console.log("phoneRef调用loginAction")
      }
    }

login-ccount.vue

 <el-form label-width="60px" :rules="rules" :model="account" ref="formRef">
      <el-form-item label="账号" prop="name">
        <el-input v-model="account.name" />
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input v-model="account.password" show-password />
      </el-form-item>
    </el-form>
  // formRe拿到上面的表单,InstanceType泛型
    const formRef = ref<InstanceType<typeof ElForm>>()

    const loginAction = (isKeepPassword: boolean) => {
      // 拿到formRe.value,即表单的对应数据,validate,拿到表单对应的验证值(布尔值)
      formRef.value?.validate((valid) => {
        console.log(valid)
      })
    }

 六、LocalCache账号密码的本地存储

1.首先建立一个util文件夹,然后里面写LocalCache的getCache这些方法

2.然后在login-account.vue里面根据点击登录之后是否记住密码的布尔值去 localCache.setCache("name", account.name)

login-account.vue

 const loginAction = (isKeepPassword: boolean) => {
      formRef.value?.validate((valid) => {
        if (valid) {
          // 1.判断是否需要记住密码
          if (isKeepPassword) {
            // 本地缓存
            localCache.setCache("name", account.name)
            localCache.setCache("password", account.password)
          } else {
            localCache.deleteCache("name")
            localCache.deleteCache("password")
          }

          // 2.开始进行登录验证
          store.dispatch("login/accountLoginAction", { ...account })
        }
      })
    }

cache.ts

class LocalCache {
  setCache(key: string, value: any) {
    window.localStorage.setItem(key, JSON.stringify(value))
  }
  getCache(key: string) {
    // obj->string->obj
    const value = window.localStorage.getItem(key)
    if (value) {
      return JSON.parse(value)
    }
  }
  deleteCache(key: string) {
    window.localStorage.removeItem(key)
  }
  clearCache() {
    window.localStorage.clear()
  }
}
export default new LocalCache()

七、账号登录的Vuex使用

1.首先先创建一个store文件夹,里面分别有index.ts和type.ts,还有login对应的login.ts和type.ts

2.安装vuex:npm install vuex --save

3.开始写vuex模块,首先先写根store,里面注册对应的login模块

import { createStore, Store, useStore as useVuexStore } from 'vuex'
import login from './login/login'
import { IRootState, IStoreType } from './types'
//IRootState根store类型
const store = createStore<IRootState>({
  state() {
    return {
      name: 'ZJE',
      age: 18,

    }
  },
  mutations: {
  },
  getters: {},
  actions: {
  },
  modules: {
    login,

  }
})

export default store

4.开始在login.ts里面写点击登录时触发的事件

login.ts

import { Module } from 'vuex'
import localCache from '@/utils/cache'
import router from '@/router'
import { ILoginState } from './types'
import { IRootState } from '../types'
// ILoginState模块的store类型,IRootState根store类型
const loginModule: Module<ILoginState, IRootState> = {
  namespaced: true,
  state() {
    return {
      token: '',
      userInfo: {}

    }
  },
  getters: {},
  mutations: {

  },
  actions: {
    //  store.dispatch("login/accountLoginAction", { ...account })会调用这里
    async accountLoginAction({ commit }, payload: any) {
      console.log("执行accountLoginAction", payload)
    }
  }
}
export default loginModule

type.ts


export interface ILoginState {
  token: string
  userInfo: any

}

login-account.vue

 

 5.在state里面开始发送请求接口函数,然后把登录成功获取的data存储在本地localCache,同时也将数据存储在vuex里面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/142296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

财务管理系统|基于Springboot开发实现公司财务管理系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

知识库软件应该具备的7个基本功能

SaaS公司最大的业务挑战是&#xff1a;如何留住客户&#xff0c;客户与你达成合作关系后&#xff0c;如何让其与你继续合作&#xff0c;达成长期合作关系。对于SaaS产品&#xff0c;丢失客户的成本是很昂贵的&#xff0c;赢得一个新客户的成本是留住现有客户的5到25倍&#xff…

Vue脚手架的使用

一、通过命令行使用vue-cli的指令创建 1. 安装&#xff1a;npm i -g vue/cli-init 2. 创建Vue项目&#xff08;Vue2.0项目&#xff09;&#xff1a; ​ &#xff08;1&#xff09;创建文件夹&#xff1a;vue2-demo ​ &#xff08;2&#xff09;进入文件夹&#xff1a;cd v…

[Leetcode] 相交链表

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。图示两个链表在节点 c1 开始相交&#xff1a;题目数据保证整个链式结构中不存在环。注意&#xff0c;函数返回结果后&#xf…

【 java 集合】Set 接口及常用实现类总结

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

JavaEE【Spring】:Spring事务和事务传播机制

文章目录前言一、Spring 中事务的实现1、MySQL 中的事务使用2、Spring 编程式事务&#xff08;了解&#xff09;3、Spring 声明式事务&#xff08;自动&#xff09;① Transactional 作用范围② Transactional 参数说明③ 注意事项Ⅰ. 抛出异常Ⅱ. 手动回滚④ Transactional ⼯…

2022年亚太地区大学生数学建模竞赛/2022年亚太杯1月加赛E题思路

问题1. 基本数据分析数据集中的OWID_WRL是什么&#xff1f;一般是指世界world。a) 哪些国家曾经拥有过核武器&#xff1f;现拥有核武器的国家有9个&#xff0c;分别为&#xff1a;美国、俄罗斯、英国、法国、中国、印度、巴基斯坦、以色列&#xff0c;朝鲜。曾经拥有核武的国家…

【python】导入同级、下级、上级目录中的模块

不想把代码都堆到一个文件里面&#xff0c;希望写的分层次&#xff0c;但又不是web框架&#xff0c;有入口文件和目录规则&#xff0c; 这个时候就要自己分包了&#xff0c;会遇到这个问题&#xff0c;明明ide智能追踪可以定位到包引用&#xff0c;但是却报错module undefine&a…

5G NR标准 第15章 上行功率和定时控制

第15章 上行功率和定时控制 上行链路功率控制和上行链路时序控制是本章的主题。 功率控制用于控制干扰&#xff0c;主要是针对其他小区的干扰&#xff0c;因为同一小区内的传输通常是正交的。 时序控制确保不同的设备以相同的时序接收&#xff0c;这是保持不同传输之间正交性…

Nacos设置为windows自启动服务

序言 众所周知&#xff0c;在 Windows 环境下想要启动 nacos 需要运行 bin 目录下的 startup.cmd。这样的启动方式需要保证 cmd 窗口一直开着&#xff0c;只要把这个窗口关掉&#xff0c;nacos 服务就停了。 所以为了避免人为的误关窗口&#xff0c;把 nacos 注册成一个 wins…

【可视化】无法理解PCA,条件概率,最小二乘回归?可视化帮你!

主成分分析PCA2D示例首先&#xff0c;只考虑两个维度的数据集&#xff0c;比如高度和重量。这个数据集可以绘制成平面上的点。但如果想要整理出变量&#xff0c;PCA会找到一个新的坐标系&#xff0c;其中每个点都有一个新的(x,y)值。坐标轴实际上没有任何物理意义。它们是高度和…

【JavaScript】如何转换blob数据与file文件还有url

大家好&#xff0c;关于blob对象和file对象有了解多少呢&#xff0c;它们都是一种文件的表示形式&#xff0c;文件之间是可以互相转换的&#xff0c;顺带一提&#xff0c;还有经常用到的临时文件路径tempFileURL。 文章目录文件类型Blob对象File对象URL临时路径文件类型 首先&…

c++ -- STL容器--vector

STL中最常用的容器为Vector&#xff0c;可以理解为数组#include <iostream> #include <vector> #include <algorithm> using namespace std;void myPrint(int val) {cout <<val<<endl; }//vector容器存放内置数据类型void test01() {//创建了一个…

数据库(tidb、clickhouse、hive)概念笔记

目录 1、有哪些分布式数据库 2、OLAP、OLTP、HTAP 3、TIDB、clickhouse、hive 一、TIDB 1. TiDb 核心特性&#xff1a; 2. TiDb 整体架构&#xff1a; 3.TiDB 存储&#xff1a; 二、clickhouse 三、hive 1.什么是 Hive&#xff1f; 2.Hive 架构和如何运作&#xff1…

KMP -- 代码求解next数组

代码求解next数组 1. KMP相关概念 前缀&#xff1a;包含首位字符但不包含末位字符后缀&#xff1a;包含末位字符但不包含首位字符next数字&#xff1a;主串与模式串不匹配时&#xff0c;模式串需要回退的位置next[j]&#xff1a;第 j 位字符前面的j-1位字符组成的字串的前后缀…

QGIS查看属性和选择要素

目录1. 查看属性和选择要素2. 调整图层样式&#xff0c;添加自动标注1. 查看属性和选择要素 #pic_center x400 暂时移除&#xff0c;不是删除&#xff0c;它还是存在它本来的位置&#xff0c;用的时候再次添加即可。 选择工具 点完工具后&#xff0c;点击图中一个点&#xf…

c++ -- STL容器--stack容器

5. stack容器5.1 简介① stack是一种先进后出的容器&#xff0c;它只有一个出口。② 栈中只有顶端的元素才可以被外界使用&#xff0c;因此栈不允许有遍历行为。③ 栈中进入数据称为&#xff1a;入栈 push④ 栈中弹出数据称为&#xff1a;出栈 pop5.2 常用接口① 功能描述&…

蓝桥杯STM32G431RBT6学习——GPIO

蓝桥杯STM32G431RBT6学习——GPIO GPIO外设分布 国信长天开发板使用的STM32G431RBT6为LQFP64的封装&#xff0c;可用的GPIO为49个&#xff0c;包括如下&#xff1a; PA0~PA15 PB0~PB15 PC0~PC15 PD2&#xff0c;PF0&#xff0c;PF1&#xff0c;PG10 其中PF0与PF1用于连接外部…

前端入门笔记 02 —— CSS

CSS标签通常要配合html或者js使用 CSS本身的构造 p{color : red}/*color 属性 red 值*//*p选择器 括号内内容 生命*/配合html <style>p{color : red}<\style>或者直接引入整个css文件 <head><style type text/css>import "1.3.css";<…

【vue组件之间的数据传递和组件的生命周期】一.组件之间的通信;二.组件的声明周期

目录 一.组件之间的通信 1.组件之间的关系&#xff1a;父子关系、兄弟关系、跨级关系 2.父子组件之间的通信&#xff08;数据传递&#xff09;&#xff1a; &#xff08;1&#xff09;父组件----》子组件&#xff1a;使用props &#xff08;2&#xff09;子组件----》父组件…