该项目git地址
- 结合项目源码更容易看懂 里面有很多注释
设计思路
-
如图所示
-
Login 里面注册 panel 页面 主要用来设置样式 居中
- panel 为父组件
- account 和 phone 为子组件 两种不同的登录方式 只讲一下 account 的登录
- 把登录放在 vuex 中对数据进行全局保存
具体实现
-
panel
-
<template> <div class="login-panel"> <h1 class="title">后台管理系统</h1> <el-tabs v-model="currentTab" type="border-card" stretch> <el-tab-pane name="account"> <template #label> <span><i class="el-icon-user-solid"></i> 账号登录</span> </template> <login-account ref="accountRef" /> </el-tab-pane> <el-tab-pane name="phone"> <template #label> <span><i class="el-icon-mobile-phone"></i> 手机登录</span> </template> <login-phone ref="phoneRef" /> </el-tab-pane> </el-tabs> <div class="account-control"> <el-checkbox v-model="isKeepPassword">记住密码</el-checkbox> <el-link type="primary">忘记密码</el-link> </div> <el-button type="primary" class="login-btn" @click="handleLoginClick" >立即登录</el-button > </div> </template> <script setup lang="ts"> import { ref } from 'vue' import LoginAccount from './login-account.vue' import LoginPhone from './login-phone.vue' // 1.定义属性 // 记住密码 const isKeepPassword = ref(true) // 获取输入账号信息 模块 const accountRef = ref<InstanceType<typeof LoginAccount>>() // 获取输入手机号 验证码 const phoneRef = ref<InstanceType<typeof LoginPhone>>() // 获取登录方式模块 const currentTab = ref('account') // 2.定义方法 const handleLoginClick = () => { console.log(accountRef.value) if (currentTab.value === 'account') { accountRef.value?.loginAction(isKeepPassword.value) } else { console.log('phoneRef调用loginAction') } } </script> <style scoped lang="less"> .login-panel { margin-bottom: 150px; width: 320px; .title { text-align: center; } .account-control { margin-top: 10px; display: flex; justify-content: space-between; } .login-btn { width: 100%; margin-top: 10px; } } </style>
-
-
account
-
<template> <div class="login-account"> <el-form ref="formRef" label-width="60px" :rules="rules" :model="account"> <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 setup lang="ts"> import { reactive, ref } from 'vue' import { useStore } from 'vuex' import { ElForm } from 'element-plus' // 导入本地缓存模块 import localCache from '../../../utils/cache' // 导入账号密码输入规则 import { rules } from '../config/account-config' const store = useStore() // 获取账号密码组件 const account = reactive({ name: localCache.getCache('name') ?? '', password: localCache.getCache('password') ?? '', }) // 获取哪种登录方式 const formRef = ref<InstanceType<typeof ElForm>>() 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 action 中有定义 名字要相同 传入账号密码参数 store.dispatch('login/accountLoginAction', { ...account }) } }) } // 因为使用了 setup 语法糖 父组件要获取函数属性的话 就要使用这个暴露 因为没有 return 了 defineExpose({ loginAction, }) </script> <style scoped></style>
-
store
-
import { Module } from 'vuex' import { accountLoginRequest } from '@/service/login/login' import localCache from '@/utils/cache' import router from '@/router' import { IAccount } from '@/service/login/type' import { ILoginState } from './types' import { IRootState } from '../types' const loginModule: Module<ILoginState, IRootState> = { namespaced: true, state() { return { token: '' } }, getters: {}, mutations: { changeToken(state, token: string) { state.token = token } }, actions: { async accountLoginAction({ commit }, payload: IAccount) { // 实现登录逻辑 const loginResult = await accountLoginRequest(payload) const { token } = loginResult.data commit('changeToken', token) localCache.setCache('token', token) // 跳到首页 router.push('/main') }, loadLocalLogin({ commit }) { // 用于更新公共仓库储存的数据 比如换了账号登录 这些信息就要更新 const token = localCache.getCache('token') if (token) { commit('changeToken', token) } }, }, } export default loginModule
\