1. 静态页面的搭建
//src/views/login/index.vue
<template>
<div class="loginContainer">
<el-row>
<!-- 两列 -->
<!-- 左边这一列当视口宽度小于768时不显示 -->
<el-col :span="12" :xs="0">
</el-col>
<!-- 右边这一列要存放表单 -->
<el-col :span="12" :xs="24">
<el-form class="loginForm">
<h1>Hello</h1>
<h2>欢迎来到硅谷甄选</h2>
<el-form-item>
<el-input :prefix-icon="User" v-model="loginForm.username"></el-input>
</el-form-item>
<el-form-item>
<el-input type="password" :prefix-icon="Lock" show-password v-model="loginForm.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" class="loginBtn">登录</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { User, Lock } from '@element-plus/icons-vue'
import { reactive } from 'vue';
//收集表单数据
const loginForm = reactive({username: 'admin', password: '111111'})
</script>
<style scoped lang="scss">
.loginContainer {
width: 100%;
height: 100vh;
background: url('@/assets/images/background.jpg') no-repeat;
//背景覆盖整个容器
background-size: cover;
.loginForm {
position: relative;
top: 30vh;
width: 80%;
background: url("@/assets/images/login_form.png") no-repeat;
background-size: cover;
padding: 60px;
h1 {
color: #fff;
font-size: 40px;
}
h2 {
color: #fff;
font-size: 20px;
margin: 20px 0px;
}
.loginBtn {
width: 100%;
}
}
}
</style>
element-plus:
1. <el-row>行
2. <el-col>列,用grid布局,:span说明各站几格,:xs表示当宽度小于768时所占的格子数量
3. <el-form-item>存放表单元素的
4. <el-input>需要双向绑定收集数据,:prefix-icon则是前缀图标(这个图标需要从elementplus插件中引入。type表示这个input框的类型,show-password表示是否展示密码眼睛的空间)
2. 登录页面业务
登录发请求(让仓库pinia发)
登录成功,跳转到首页,并提示用户登录信息;
登录失败,弹出登录失败的信息;
登录成功之后,还需要用token向服务器发请求,要本人的数据
(1)配置pinia仓库
① 安装插件
pnpm i pinia
② 大仓库src/store/index.ts
//创建大仓库
import { createPinia } from "pinia";
const store = createPinia();
export default store;
③ 在main.ts中引入仓库
//引入仓库
import store from './store';
//注册仓库
app.use(store);
(2)创建存储用户信息的小仓库
//src/s trore/modules/user.ts
import { defineStore } from "pinia";
// import { ref } from "vue";
import { reqLogin } from "@/api/user";
import type { loginForm } from '@/api/user/type'
const useUserStore = defineStore('User', () => {
//点击登录会发起请求,成功的话要存储token
let token = localStorage.getItem('TOEKN');
//发起登录请求
const login = async (data: loginForm) => {
//请求会返回一个promise
const res = await reqLogin(data);
// 如果请求成功,就得把数据存储起来
if (res.code === 200) {
token = res.data.token!;
//要持久化
localStorage.setItem('TOKEN', res.data.token!)
return 'ok';
} else {
//请求失败,得返回一个错误
return Promise.reject(new Error(res.data.message))
}
}
return {
token,
login
}
})
export default useUserStore
(3)用户点击登录发起请求
① /views/login.index.view
首先,为其添加点击事件
<el-form-item>
<el-button type="primary" class="loginBtn" @click="loginBtnHandler" :loading="loading">登录</el-button>
</el-form-item>
其次,点击事件的回调函数
<script setup lang="ts">
import { User, Lock } from '@element-plus/icons-vue'
import { reactive, ref } from 'vue';
import useUserStore from '@/store/modules/user';
import { useRouter } from 'vue-router';
import { ElNotification } from 'element-plus';
import { getTimeInterval } from '@/utils/getTimeInterval';
//收集表单数据
const loginForm = reactive({username: 'admin', password: '111111'})
const userStore = useUserStore()
// 用户点击按钮后加载效果开启变量
const loading = ref(false);
const router = useRouter();
// //用户点击登录按钮的回调
const loginBtnHandler = async ()=>{
// 发起请求需要loading
loading.value = true
//pinia中action函数会返回一个promise对象
try {
await userStore.login(loginForm)
// 如果请求成功了还需要跳转页面,并提示用户
router.push('/');
ElNotification({
type:'success',
message: '欢迎回来',
title: `Hi,${getTimeInterval()}好`
})
} catch (error) {
ElNotification({
type:'error',
message: (error as Error).message
})
} finally {
loading.value = false
}
}
</script>
其中,getTimeInterval函数如下
//src/utils/getTimeInterval.ts
//用户获取登录成功之后打招呼的内容
export const getTimeInterval = () => {
const hour = new Date().getHours()
let message = ''
if (hour <= 9) {
message = '早上'
} else if (hour <= 12) {
message = '上午'
} else if (hour <= 18) {
message = '下午'
} else if (hour <= 24) {
message = '晚上'
}
return message
}
1. vue3中,要想跳转则需要引入useRouter,并创建实例router = userRouter(),在使用router.push()进行跳转
2. ElNotification组件和ElMessage组件差不多,都是提示用户的,但前者是侧边栏提示;后者是正中间提示
3. el-button组件上有个loading属性,用于显示加载中
问题1:我在发起请求的时候打印请求结果,发现是undefined,可是在调试面板中请求显示成功了
解决方法:原来是我请求API写错了,箭头函数写了{}就得写return
//登录接口
export const reqLogin = (data: loginForm) => { return request<any, loginRes>({ url: API.LOGIN_URL, method: 'post', data }) }
(4)表单校验
① <el-form>组件上绑定:model和:rules属性
<el-form class="loginForm" :model="loginForm" :rules="rules">
② <el-form-item>上绑定:prop属性'
<el-form-item prop="username">
prop绑定的名字必须和rules对应
rules如下
//表单校验规则
const rules = {
username: [{require: true, min: 6, max: 10, message: '用户名至少6位,至多10位', trigger: 'change'}],
password: [{require: true, min: 6, max: 15, message: '密码至少6位,至多15位', trigger: 'change'}]
}
③ 要等整个表单都通过校验后,才能发起请求
所以需要获取到<el-form>组件实例,然后里面有个方法validate,点击按钮就调用该方法进行校验
<el-form class="loginForm" :model="loginForm" :rules="rules" ref="formRef">
<script>
//获取整个表单实例
const formRef = ref()
//用户点击登录按钮的回调
const loginBtnHandler = async ()=>{
//整个表单校验通过再发起请求,validate返回的是一个promise
await formRef.value.validate()
// 发起请求需要loading
loading.value = true
//pinia中action函数会返回一个promise对象
try {
await userStore.login(loginForm)
// 如果请求成功了还需要跳转页面,并提示用户
router.push('/');
ElNotification({
type:'success',
message: '欢迎回来',
title: `Hi,${getTimeInterval()}好`
})
} catch (error) {
ElNotification({
type:'error',
message: (error as Error).message
})
} finally {
loading.value = false
}
}
</script>
由于formRef是ref类型,记住要写.value
④ 自定义校验规则
//校验密码
const validatorPassword = (rule:any, value: any, callback: any)=>{
if(value.length >= 6 && value.length <= 15) {
callback()
} else {
callback(new Error('密码至少6位,至多15位'))
}
}
//表单校验规则
const rules = {
username: [
// {require: true, min: 6, max: 10, message: '用户名至少6位,至多10位', trigger: 'change'},
{trigger: 'change', validator: validatorUsername}
],
password: [
// {require: true, min: 6, max: 15, message: '密码至少6位,至多15位', trigger: 'change'},
{trigger: 'change', validator: validatorPassword}
]
}