前言
以一个登录页面为例子,这篇文章简单介绍了vue,element-plus的一些组件使用,vue-router页面跳转,pinia及持久化存储,axios发送请求的使用。后面的页面都大差不差,也都这么实现,只是内容,页面布局会变一些。其他插件功能,用到再介绍。
一、实现效果图
- 一个大的div,class=main
- 在main这个div中,有一个div,class=“login_box”
- login_box中,分上面部分,包含icon和标题;下面部分就是登录表单数据
<template>
<div class="main">
<div class="login_box">
<div class="head">
</div>
<div class="login-form" >
</div>
</div>
</div>
</template>
二、页面内容
1.main
main只需要铺满整个页面,给个背景图就行。得到一个铺满页面的图片
.main{
width: 100vw;
height: 100vh;
background: url('../../assets/bg.jpeg');
background-size: cover;
}
2.login_box
.login_box{
width: 500px;
height: 460px;
box-shadow: 0 0 5px var(--el-color-primary);
position:absolute;
top:calc(50vh - 230px);
left: calc(50vw - 250px);
border-radius: 20px;
background-color: rgba(255, 255, 255, 0.8);
}
3. login_box -> head
<div class="head">
<img src="@/assets/logo.jpeg" alt="logo" class="logo">
<div class="title">接口自动化平台</div>
</div>
.head{
display: flex;
justify-content: center;
align-items: center;
height: 150px;
.logo{
width: 60px;
height: 60px;
border-radius: 50px;
}
.title{
font-size: 30px;
font-weight: bold;
color: var(--el-color-primary);
margin-left: 20px;
}
}
4. login_box -> login_form
<div class="login-form" >
<el-form :model="loginForm">
<el-form-item >
<el-input prefix-icon="User" v-model="loginForm.username"
placeholder="请输入用户名"
/>
</el-form-item>
<el-form-item >
<el-input type="password" prefix-icon="Lock" v-model="loginForm.password"
placeholder="请输入密码"
/>
</el-form-item>
<el-form-item>
<el-switch v-model="loginForm.status" inactive-text='记住登录状态' />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="LoginSubmit(loginFormRef)" style="width: 100%;">登 录</el-button>
</el-form-item>
<el-link @click="router.push('register')" type="primary">没有账号?点击注册</el-link>
</el-form>
</div>
.login-form{
margin: 0 30px;
}
三、数据绑定
1. 动态数据绑定
使用 v-bind:(可以简写为:) 来进行动态数据绑定,
例如:将这里的登录按钮,是否可以点击设计为只有用户名和密码都不空才能点击。
可以设置disable属性,给这个属性动态绑定一个值‘canClick’,这个值是计算出来的
<el-form-item>
<el-button :disabled='canClick' type="primary" @click="LoginSubmit(loginFormRef)" style="width: 100%;">登 录</el-button>
</el-form-item>
// 未输入账号密码禁用登录
const canClick = computed(()=>{
if (loginForm.username!=='' && loginForm.password!==''){return false}
else{return true}
})
计算属性computed
此时,没有输入账号密码,登录按钮就不能点击了。
更多关于计算属性可以参考: vue官网-计算属性
2. 双向绑定
v-model=" " 这样进行数据双向绑定
<el-input type="password" prefix-icon="Lock" v-model="loginForm.password"
placeholder="请输入密码"/>
...
<script setup>
const loginForm = reactive({
username:"",
password:"",
status:true
})
</script>
将input输入框的内容与loginForm.password双向绑定。
3. 响应式数据
ref 和 reactive 我自己理解来说,就是ref定义简单一个具有响应性类型数据,比如字符串,布尔值,数值。而reactive可以定义复合的内容。在更改他们值的时候,页面显示也会同时更改
他们区别简单来说,就是reactive只存储对象,而ref存储基本数据类型,比如数字、字符串等。
更多可以参考其他文档,这里不主要介绍这块,因为我自己也不是很懂,平时拿来简单用用就是了,没深入了解过。嘿嘿
ref和reactive官网介绍
ref和reactive区别
四、Element表单数据校验
elment-plus表单校验
1. 创建表单校验规则
Form 组件提供了表单验证的功能,只需为 rules 属性传入约定的验证规则,并将 form-Item 的 prop 属性设置为需要验证的特殊键值即可
//表单校验规则
const loginRules = reactive({
username: [
{ required: true, message: '账号不能为空', trigger: 'blur' },
{ min: 4, max: 18, message: '账号长度4-18位', trigger: 'blur' },
],
password :[
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 6, max: 18, message: '密码长度6-18位', trigger: 'blur' },
]
})
2. 给Form组件加上规则
< el-form model="loginForm" :rules="loginRules" >
3. 将 form-item 的 prop 属性设置为需要验证的特殊键值
例如:
<el-form-item prop="username">
<el-input prefix-icon="User" v-model="loginForm.username"
placeholder="请输入用户名"/>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" prefix-icon="Lock"
v-model="loginForm.password" placeholder="请输入密码"/>
</el-form-item>
校验规则是光标移出输入框校验,所以没有输入用户名,点到密码。就会提示自定义的校验规则内容
五、发送请求(后端接口使用之前fastapi项目)
在api/module/UserApi.js里,定义所有用户相关的接口
1. 用户登录模块接口
// api/module/UserApi.js
import request from "@/api/request";
//导出请求方法
export default {
//登录
loginApi(data){
return request.post('/api/users/login/',data)
},
}
这个接口是之前fastapi栏写的,这里直接用。
2. 使用表单引用对象
在点击登录时,将表单引用对象传过去。首先进行表单预校验,预校验通过后,再调用接口,这样不会浪费资源
<el-form :model="loginForm" :rules="loginRules" ref='loginFormRef'>
......
<el-form-item>
<el-button :disabled='canClick' type="primary" @click="LoginSubmit(loginFormRef)" style="width: 100%;">登 录</el-button>
</el-form-item>
<script setup>
import http from '@/api/index'
...
const loginFormRef=ref(null)
...
const LoginSubmit = async(loginRef)=>{
//表单预校验
await loginRef.validate(async (valid)=>{
if(valid){
const response = await http.user.loginApi(loginForm)
if(response.status === 200){
ElNotification({
title: '登录成功',
message: '您已登录成功,进入首页',
type: 'success',
duration: 3,
})
}else{
ElMessage({
message: response.data.detail,
type: 'error',
duration: 3,
})
}
}
})
}
</script>
elemet-plus通知消息弹窗
为了页面效果更美观,点击后得到反馈结果。使用了这个消息弹窗
import { ElNotification,ElMessage} from 'element-plus'
Feedback 反馈组件
3. 发送请求(跨域问题)
能看到这里发送请求成功了,前端请求这块是没问题了。
应该是跨域配置有问题,一般是服务端配置问题。
重新配置了一下服务端,现在可以正常访问了。
# pyhton服务端
origins = ['http://127.0.0.1:5173',"http://localhost:5173"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
allow_headers=["*"],
)
六、pinia数据持久化
1. 下载pinia-plugin-persist
npm install pinia-plugin-persist
2.注册插件
//main.js
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'
// 创建一个pinia
const pinia = createPinia()
// 将持久化的插件注册到pinia中
pinia.use(piniaPluginPersist)
app.use(pinia)
3.在store中设置
在store下创建UserStore.js用来存储用户相关的信息,
使用localStorage来存储数据,也可以使用sessionStorage,看个人需求。
- localStorage:数据的持久化程度高,即使用户关闭浏览器后,数据依然会被保留。除非显式删除,数据可以一直存在,适用于持久保存用户偏好设置、用户认证信息等需要长时间保存的数据。
- sessionStorage:数据仅在浏览器的会话期间有效。一旦关闭页面或标签页,数据就会被清除,适用于存储临时性的数据,如表单状态、页面之间的传递数据等。
import {defineStore} from 'pinia'
export const UserStore = defineStore('ustore',{
//全局数据
state:()=>{
return {
token:null,
userinfo:null,
isLogin:false
}
},
// 定义持久化配置
persist:{
enabled:true,
strategies:[
{
key:'userInfo',
storage:localStorage
}
]
}
})
4. 在需要用到的地方进行使用
以登录成功为例,登录后保存用户token和用户信息。
//LoginView.vue
import{UserStore} from '@/stores/UserStore'
//创建用户store对象
const uStore = UserStore()
...
LoginSubmit()=>{...
...
//登录成功后保存用户信息
//保存用户token和用户信息
uStore.token = response.data.token
uStore.userinfo = response.data.user
// 修改认证状态
if(loginForm.status){
uStore.isLogin = true
}
}
最开始localStorage没有存储数据
登录成功后,就把数据记录下来了key:'userInfo'
七、页面跳转及其他页面使用pinia存储的数据
实现一个功能:登录成功后,需要跳转首页,首页展示token信息。
1. 创建一个home页面
2. 注册路径
在router/index中注册home的路径
3. 登录成功跳转
在登录页面,进行跳转
import {useRouter} from 'vue-router'
//创建路由对象
const router = useRouter()
...
//登录成功,跳转home页面
router.push({name:"home"})
项目相关部分代码
LoginView.vue
<template>
<div class="main">
<div class="login_box">
<div class="head">
<img src="@/assets/logo.jpeg" alt="logo" class="logo">
<div class="title">接口自动化平台</div>
</div>
<div class="login-form" >
<el-form :model="loginForm" :rules="loginRules" ref='loginFormRef'>
<el-form-item prop="username">
<el-input prefix-icon="User" v-model="loginForm.username"
placeholder="请输入用户名"
/>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" prefix-icon="Lock" v-model="loginForm.password"
placeholder="请输入密码"
/>
</el-form-item>
<el-form-item>
<el-switch v-model="loginForm.status" inactive-text='记住登录状态' />
</el-form-item>
<el-form-item>
<el-button :disabled='canClick' type="primary" @click="LoginSubmit(loginFormRef)" style="width: 100%;">登 录</el-button>
</el-form-item>
<el-link @click="router.push('register')" type="primary">没有账号?点击注册</el-link>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import {reactive,ref,computed} from 'vue'
import http from '@/api/index'
import { ElNotification,ElMessage} from 'element-plus'
import{UserStore} from '@/stores/UserStore'
import {useRouter} from 'vue-router'
//创建路由对象
const router = useRouter()
//创建用户store对象
const uStore = UserStore()
const loginFormRef=ref(null)
const loginForm = reactive({
username:"",
password:"",
status:true
})
const canClick = computed(()=>{
if (loginForm.username!=='' && loginForm.password!==''){return false}
else{return true}
})
//表单校验规则
const loginRules = reactive({
username: [
{ required: true, message: '账号不能为空', trigger: 'blur' },
{ min: 4, max: 18, message: '账号长度4-18位', trigger: 'blur' },
],
password :[
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ min: 6, max: 18, message: '密码长度6-18位', trigger: 'blur' },
]
})
const LoginSubmit = async(loginRef)=>{
console.log('click!!!')
//表单预校验
await loginRef.validate(async (valid)=>{
if(valid){
const response = await http.user.loginApi(loginForm)
if(response.status === 200){
ElNotification({
title: '登录成功',
message: '您已登录成功,进入首页',
type: 'success',
duration: 3000,
})
//保存用户token和用户信息
uStore.token = response.data.token
uStore.userinfo = response.data.user
// 修改认证状态
if(loginForm.status){
uStore.isLogin = true
}
// 跳转到home页面
router.push({name:"home"})
}else{
ElMessage({
message: response.data.detail,
type: 'error',
duration: 3,
})
}
}
})
}
</script>
<style scoped lang='scss'>
.main{
width: 100vw;
height: 100vh;
/* background: url('../assets/hz.svg'); */
background: url('../../assets/bg.jpeg');
background-size: cover;
.login_box{
width: 500px;
height: 460px;
box-shadow: 0 0 5px var(--el-color-primary);
position:absolute;
top:calc(50vh - 230px);
left: calc(50vw - 250px);
border-radius: 20px;
background-color: rgba(255, 255, 255, 0.8);
}
.head{
display: flex;
justify-content: center;
align-items: center;
height: 150px;
.logo{
width: 60px;
height: 60px;
border-radius: 50px;
}
.title{
font-size: 30px;
font-weight: bold;
color: var(--el-color-primary);
margin-left: 20px;
}
}
.login-form{
margin: 0 30px;
}
}
</style>
stores/UserStore.js
import {defineStore} from 'pinia'
export const UserStore = defineStore('ustore',{
//全局数据
state:()=>{
return {
token:null,
userinfo:null,
isLogin:false
}
},
// 定义持久化配置
persist:{
enabled:true,
strategies:[
{
key:'userInfo',
storage:localStorage
}
]
}
})
router/index.js
import { createRouter, createWebHistory } from 'vue-router'
// import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path:'/',
redirect:'/login'
},
{
path: '/login',
name: 'login',
component: () => import('../views/User/LoginView.vue'),
},
{
path: '/home',
name: 'home',
component: () => import('../views/Home/HomeView.vue'),
},
],
})
export default router
main.js
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//语言国际化
import zhCn from 'element-plus/es/locale/lang/zh-cn'
//icon组件
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
//注册element-plus语言国际化
app.use(ElementPlus, {
locale: zhCn,
})
//注册elemnt-plus的icon组件
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
// 创建一个pinia
const upinia = createPinia()
// 将持久化的插件注册到pinia中
upinia.use(piniaPluginPersist)
app.use(upinia)
app.use(router)
app.mount('#app')