登录界面开发
配置
组件
分别注册了两个组件,一个是login页面(src/views/login/index.vue)、另外一个是登陆后展示的layout页面(src/views/layout/index.vue)
路由配置
配置路由(src/router/index.ts)
1 | //引入createRouter和createWebHashHistory方法 |
在 Vue Router 中,router 和 route 是两个不同的概念。
router 是 Vue Router 的实例,它包含了一些方法和属性,如 push、> replace、go 等,用于控制路由的跳转和行为。你可以通过 this.$router 在 Vue 组件中访问到 router 实例。
route 是当前激活的路由信息对象,它包含了当前路由的路径、参数、查询字符串等信息。你可以通过 this.$route 在 Vue 组件中访问到当前的 route 对象。
router 和 route 的主要区别在于,router 是用于控制路由的跳转和行为的,而 route 是表示当前路由状态的信息对象。
在大多数情况下,你可能需要使用 this.$router 来进行路由跳转,如 this.$router.push('/path')。而当你需要获取当前路由的信息,如路径、参数等,你可以使用 this.$route,如 this.$route.params.id。
在app.vue中使用router-view
1 | <template> |
静态页面
el-row/el-col
1 | <template> |
css
1 | <style lang="scss" scoped> |
positon的值
static
:这是元素的默认值。静态定位的元素不会受到top
、bottom
、left
、right
的影响。relative
:元素按照正常的文档流进行定位,然后相对于它的正常位置进行偏移。偏移不会影响其他元素的位置。absolute
:元素脱离正常的文档流,并相对于最近的已定位父元素(position
属性为relative
、absolute
、fixed
或sticky
的元素)进行定位。如果没有已定位的父元素,那么它会相对于body
元素进行定位。fixed
:元素脱离正常的文档流,并相对于浏览器窗口进行定位。即使页面滚动,它仍然会停留在同一的位置。sticky
:元素在滚动范围内表现为relative
,而在滚动范围外表现为fixed
。也就是说,元素会“粘”在特定位置,然后随着页面滚动。
动态绑定
mock模拟服务器
使用mock模拟后端
- 安装mock
pnpm i mockjs --save
- 模拟数据和服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46//导入mock库
import Mock from 'mockjs'
//模拟数据
const user = Mock.mock({
fixedUser: [
{
username: 'admin',
password: 'admin111',
avatar:
' https://tse4-mm.cn.bing.net/th/id/OIP-C.NNF59vntIv7760rOlX1zMgAAAA?w=187&h=188&c=7&r=0&o=5&cb=10&dpr=1.2&pid=1.7'
},
{
username: 'user',
password: 'user111',
avatar:
'https://tse1-mm.cn.bing.net/th/id/OIP-C.D34PjxR7ud-vxeTDvs5Z8gAAAA?w=135&h=150&c=7&r=0&o=5&cb=10&dpr=1.2&pid=1.7'
}
]
})
export default()=>{
Mock.mock('/api/login', 'post', (req: any) => {
const { username, password } = JSON.parse(req.body)
//从模拟数据中找
const userItem = user.fixedUser.find(
(item: any) => item.username === username && item.password === password
)
//如果用户数据在模拟数据中,return200,和token
if (userItem) {
return {
code: 200,
data: {
//返回用户名,用户头像和token
username: userItem.username,
avatar: userItem.avatar,
token: 'admin-token'
}
}
} else {
return {
code: 400,
message: '用户名或密码错误'
}
}
})
} - 在main.ts中引入mock
1
2import configureMockServer from './mock/user.ts';
configureMockServer()
新建仓库
新建仓库用来存放用户数据,发起登录请求等
安装pinia
pnpm i pinia
新建一个大仓库(src/store/index.ts)
1
2
3import {createPinia} from 'pinia'
const pinia = createPinia()
export default pinia封装本地存储、获取和移除token的功能(src/utils/token)
1
2
3
4
5
6
7
8
9
10
11
12//封装本地存储和读取数据的方法
//本地存储数据
export const SET_TOKEN = (token: string) => {
localStorage.setItem('TOKEN', token)
}
//本地存储获取数据
export const GET_TOKEN = () => {
return localStorage.getItem('TOKEN')
}
export const REMOVE_TOKEN = () => {
return localStorage.removeItem('TOKEN')
}在main.ts中注册pinia
1
2import pinia from './store'
app.use(pinia)创建用户仓库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31//存放用户数据的仓库
import { defineStore } from 'pinia'
import { reqLogin } from '@/api/user'
import { SET_TOKEN, GET_TOKEN, } from '@/utils/token'
export const userStore = defineStore({
id: 'user',
state: () => {
return {
token: GET_TOKEN(),
username: '',
avatar: ''
}
},
//异步操作
actions: {
//登录
async userLogin(data: any) {
const res = await reqLogin(data)
if (res.code === 200) {
//将用户名、头像存入仓库
this.username = res.data.username
this.avatar = res.data.avatar
//将token存入localStorage
SET_TOKEN(res.data.token)
} else {
//登录失败
return Promise.reject(new Error(res.message))
}
}
}
})
创建并暴露用户登录api
在(src/api/user/index.ts)中书写相关代码
1 | //导入二次封装的axios |
v-model绑定
双向绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<template>
<div class="login_box">
<el-row>
<el-col :span="12" :xs="24">
{/* 使用ref获取el-form组件 */}
<el-form class="login_form" :model="LoginForm" :rules="rules" ref="loginFormRef">
<h2>我的平台</h2>
<el-form-item prop="username">
{/* 使用v-model双向绑定 */}
<el-input prefix-icon="User" v-model="LoginForm.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" prefix-icon="Lock" v-model="LoginForm.password"></el-input>
</el-form-item>
<el-form-item>
{/* 绑定点击事件发起登录请求 */}
<el-button type="primary" class="login_but" @click="login">登录</el-button>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" :xs="0"></el-col>
</el-row>
</div>
</template>点击事件触发login
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77<script setup lang="ts">
import { ref } from 'vue'
import { userStore } from '@/store/modules/user'
import {useRouter, useRoute} from 'vue-router'
import { ElNotification } from 'element-plus';
//新建一个loginForm用于收集表单数据
let LoginForm = ref({
username: '',
password: ''
})
//获取用户仓库
let ustore = userStore()
//获取路由器
let $router = useRouter()
let $route = useRoute()
//获取表单组件
let loginFormRef = ref()
const login = async()=>{
await loginFormRef.value.validate()
//登录逻辑
//首先,发起请求,将用户名和密码发送给后端
//然后,后端返回一个token,将token存储在本地
//最后,跳转到首页
try{
await ustore.userLogin(LoginForm.value)
//RU: 如果有redirect参数,就跳转到redirect,否则跳转到首页
let redirect: any = $route.query.redirect
$router.push({ path: redirect || '/' })
// 提示登录成功
ElNotification({
title: `欢迎回来${ustore.username}`,
message: '欢迎回来',
type: 'success'
});
} catch(error){
// 提示登录失败
ElNotification({
type: 'error',
message: (error as Error).message,
});
}
}
const validatorUsername = (rule:any , value:string, callback:any)=>{
if(value== undefined){
callback(new Error('用户名不能为空'))
}
if(value.length>=5 && value.length<=20){
callback()
}else{
callback(new Error('用户名必须是5-20位的字符串'))
}
}
const validatorPassword = (rule:any , value:string, callback:any)=>{
if(value== undefined){
callback(new Error('密码不能为空'))
}
if(value.length > 5){
callback()
}else{
callback(new Error('密码长度必须大于5位'))
}
}
//定义表达验证需要配置的对象
const rules = {
username: [
//validatorUsername在上面定义
{trigger:'blur', validator: validatorUsername}
],
password: [
{trigger:'blur', validator:validatorPassword}
]
}
</script>
规则校验
使用elementPlus规定好的关键字,实现进入规则校验
- 在表单中绑定rules属性为rules对对象
1
<el-form class="login_form" :model="LoginForm" :rules="rules" ref="loginFormRef">
- 在script中,定义表单验证的配置对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33//这点代码中定义的前两个对象用于rules对象中
const validatorUsername = (rule:any , value:string, callback:any)=>{
if(value== undefined){
callback(new Error('用户名不能为空'))
}
if(value.length>=5 && value.length<=20){
callback()
}else{
callback(new Error('用户名必须是5-20位的字符串'))
}
}
const validatorPassword = (rule:any , value:string, callback:any)=>{
if(value== undefined){
callback(new Error('密码不能为空'))
}
if(value.length > 5){
callback()
}else{
callback(new Error('密码长度必须大于5位'))
}
}
//定义表达验证需要配置的对象
const rules = {
username: [
//validatorUsername在上面定义
//失去焦点后触发
{trigger:'blur', validator: validatorUsername}
],
password: [
{trigger:'blur', validator:validatorPassword}
]
} - 在登录点击事件中,发起请求之前先进行规则校验,校验通过后才能发起请求
路由跳转
登录成功后跳转到页面
1 | let $router = useRouter() |