一、初始工作
1、创建登录文件
在src/views中创建文件LoginView.vue文件
2、创建路由
在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: [//将子项全部存入一个大的路由中layout/index.vue,页面刚进入时调用的时layout/index.vue,在为导航条,默认显示页面/home的内容{path: '/',name: 'main',component: () => import('@/layout/index.vue'),children: [{path: '/home',name: 'home',component: HomeView,},{path: '/about',name: 'about',component: () => import('../views/AboutView.vue'),},]},//登录{path: '/login',name: 'login',component: () => import('@/views/LoginView.vue'),meta: {title: '登录'}},//404{path: '/:pathMatch(.*)*',name: 'not-found',component: () => import('@/views/NotFoundView.vue'),meta: {title: '页面未找到'}},],
})
export default router
二、搭建登录页面
1、实现效果
2、组件
LoginView.vue
实现底色为紫色,中间为白色卡片的登录效果,白色卡片左侧为图片效果,右侧为表单登录组件
<template><div class="page_all flex flex-center"><div class="login_all flex flex-between"><div class="login_left flex flex-center"><img src="/svg/login.png"></div><div class="login_right flex flex-center flex-column"><div class="form flex flex-center flex-column"><div class="title flex flex-center">CMS管理系统</div><el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="el-form demo-ruleForm":size="formSize" status-icon><el-form-item prop="username"><el-input v-model="ruleForm.username" placeholder="请输入账号" /></el-form-item><el-form-item prop="password"><el-input v-model="ruleForm.password" show-password placeholder="请输入密码" /></el-form-item><el-form-item class="btn-group"><el-button type="primary" @click="submitForm(ruleFormRef)">登录</el-button><el-button @click="resetForm(ruleFormRef)">重置</el-button></el-form-item></el-form></div></div></div></div>
</template>
代码解析
表单实现
参考路径:Form 表单 | Element Plus
①. <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="el-form demo-ruleForm" :size="formSize" status-icon>
-
ref="ruleFormRef"
:
给表单组件设置一个引用名ruleFormRef
,方便在 Vue 组件中通过this.$refs.ruleFormRef
访问表单实例。 -
:model="ruleForm"
:
将表单数据绑定到ruleForm
对象,ruleForm
是一个 Vue 组件的 data 属性,通常用于存储表单字段的值。 -
:rules="rules"
:
绑定表单验证规则,rules
是一个 Vue 组件的 data 属性,定义了表单字段的验证逻辑。 -
class="el-form demo-ruleForm"
:
给表单添加 CSS 类名,用于样式控制。 -
:size="formSize"
:
动态设置表单的尺寸(如large
、medium
、small
),formSize
是一个 Vue 组件的 data 属性。 -
status-icon
:
在表单验证时显示验证状态图标(如成功、失败)。
②. <el-form-item prop="username">
-
**
prop="username"
**:
指定该表单项绑定的字段名username
,用于表单验证和数据绑定。
③. <el-input v-model="ruleForm.username" placeholder="请输入账号" />
-
**
v-model="ruleForm.username"
**:
将输入框的值双向绑定到ruleForm.username
,用户输入的内容会实时更新到ruleForm.username
。 -
**
placeholder="请输入账号"
**:
设置输入框的占位符文本,提示用户输入账号。
④. <el-form-item prop="password">
-
**
prop="password"
**:
指定该表单项绑定的字段名password
,用于表单验证和数据绑定。
⑤. <el-input v-model="ruleForm.password" show-password placeholder="请输入密码" />
-
**
v-model="ruleForm.password"
**:
将输入框的值双向绑定到ruleForm.password
,用户输入的内容会实时更新到ruleForm.password
。 -
**
show-password
**:
启用密码显示/隐藏切换功能,用户可以通过点击图标切换密码的可见性。 -
**
placeholder="请输入密码"
**:
设置输入框的占位符文本,提示用户输入密码。
⑥. <el-form-item class="btn-group">
-
**
class="btn-group"
**:
给表单项添加 CSS 类名,用于样式控制。
⑦. <el-button type="primary" @click="submitForm(ruleFormRef)">登录</el-button>
-
**
type="primary"
**:
设置按钮的类型为“主按钮”,通常显示为蓝色。 -
**
@click="submitForm(ruleFormRef)"
**:
点击事件,调用submitForm
方法并传入表单实例ruleFormRef
,用于提交表单。
⑧. <el-button @click="resetForm(ruleFormRef)">重置</el-button>
-
**
@click="resetForm(ruleFormRef)"
**:
点击事件,调用resetForm
方法并传入表单实例ruleFormRef
,用于重置表单。
⑨. </el-form>
-
关闭表单组件。
3、样式
①LoginView.vue - css
大体使用flex布局实现
<style>
.page_all {width: 100%;height: 100vh;background-color: #808cdd;
}.login_all {width: 50%;height: 60%;background-color: white;
}.login_left {width: 50%;height: 98%;
}.login_left img {width: 95%;height: 80%;object-fit: contain;
}.login_right {width: 50%;height: 100%;
}.title {font-size: 25px;color: #646cff;letter-spacing: 3px;height: 20%;
}.form {flex: 1;width: 90%;
}.el-form {width: 60%;
}.el-input__inner {font-size: 12px;
}.btn-group {width: 100%;margin-top: 30px;
}.btn-group button {width: 45%;
}.el-form-item__content {justify-content: space-between;
}
</style>
②base.css
修改主体颜色
/* color palette from <https://github.com/vuejs/theme> */
:root {--vt-c-white: #ffffff;--vt-c-white-soft: #f8f8f8;--vt-c-white-mute: #f2f2f2;--vt-c-black: #181818;--vt-c-black-soft: #222222;--vt-c-black-mute: #282828;--vt-c-indigo: #2c3e50;--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);--vt-c-text-light-1: var(--vt-c-indigo);--vt-c-text-light-2: rgba(60, 60, 60, 0.66);--vt-c-text-dark-1: var(--vt-c-white);--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);--el-color-primary: #646cff;--el-button-hover-bg-color: #868bf7;--el-color-primary-light-3: #868bf7;--el-color-primary-light-5:#b4b7ef;--el-color-primary-light-9:#e5e6f7;--el-color-primary-light-7:#c6c8fa;
}
③main.js
@import './base.css';.flex {display: flex;
}.flex-between {justify-content: space-between;align-items: center;
}.flex-around {justify-content: space-around;align-items: center;
}.flex-center {justify-content: center;align-items: center;
}.flex-column {flex-direction: column;
}
三、js功能实现
1、引入方法
import { reactive, ref } from 'vue' //引入vue响应式
import { login } from '@/api/logininfo' //引入登录接口
import { useRouter } from 'vue-router' //引入路由
import { ElMessage } from 'element-plus' //引入提示框
import { setToken } from '@/utils/token' //引入token设置
2、设置基本表单数据
//设置表单大小
const formSize = ref('default')
//设置表单数据
const ruleFormRef = ref()
const ruleForm = reactive({username: '',//账号password: '',//密码
})
3、设置验证规则
新加的验证规则可以使用validator也可使用pattern直接使用正则取限制
//设置验证规则
const rules = reactive({username: [{ required: true, message: '请输入账号', trigger: 'blur' },{ min: 3, max: 10, message: '长度请在3-10之间', trigger: 'blur' },],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 6, max: 20, message: '长度请在6-20之间', trigger: 'blur' },{validator: (rule, value, callback) => {const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/;if (!passwordPattern.test(value)) {callback(new Error('密码必须包含大写字母、小写字母、数字和特殊字符'));} else {callback();}}, trigger: 'blur'},// 或者// {pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/, message: '密码必须包含大写字母、小写字母、数字和特殊字符', trigger: 'blur'}],
})
4、引入路由
//设置路由
const router = useRouter();
5、封装接口
①新建api接口方法
新建api接口方法,用于管理用户登录时需要请求的方法
②方法实现
api/logininfo.js
引入request中的post和get方法,封装login方法执行登录的请求
import { post, get } from '@/utils/request'
// 登录
export function login(data) {return post({url: '/user/login',data})
}
6、表单提交
提交表单:
发送登录请求
- 请求成功->提示成功消息->请求成功会返回一个token值,将这个token值存入本地缓存->跳转到主页面
- 请求失败->提示失败消息
//表单提交
const submitForm = async (formEl) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {console.log('submit!');// 1、请求登录接口进行登录// 参数为ruleFormconsole.log(ruleForm)// 2、请求登录接口进行登录login( ruleForm ).then(res => {console.log('res:', res)if (res.code == 1) {// 3、提示成功信息ElMessage.success(res.msg || '登录成功')//存入该账号的tokensetToken(res.data.token)// 4、跳转页面router.push('/')}else {ElMessage.error(res.msg || '登录失败')}})} else {console.log('error submit!', fields)}})
}
7、重置表单
//重置表单
const resetForm = (formEl) => {if (!formEl) returnformEl.resetFields()
}
注:这里使用的request.js的请求方法是拼接方法
export const get = (obj) => {obj.method = 'GET'if (obj.data) {obj.url += '?' + Object.keys(obj.data).map(key => key + '=' + obj.data[key]).join('&')}return request(obj)
}
export const post = (obj) => {obj.method = 'POST'return request(obj)
}
另外一种请求
如果使用的request.js的请求是
// 封装GET请求
export const get = (url, params = {}) => {return request.get(url, { params });
};
// 封装POST请求
export const post = (url, data = {}) => {return request.post(url, data);
};
// 导出request实例
export default request;
那么页面请求方法就是
接口为api/logininfo.js
import { post } from '@/utils/request';// 登录
export function login(data) {return post('/user/login', data); // 直接传递url和data
}
页面请求为
login(ruleForm).then(res => {
console.log('res:', res)
if (res.code == 1) {// 2、提示成功信息ElMessage.success(res.msg || '登录成功')//记住密码设置console.log('记住密码?:', remember.value);if(remember.value){setLoginInfo({username: ruleForm.username,password: ruleForm.password})}else{removeLoginInfo();}// return// 3、设置tokensetToken(res.data.token)// 4、跳转页面router.push('/')
}
else {ElMessage.error(res.msg || '登录失败')
}
四、接口实现
1、建立新接口
建立接口/user/login
设置请求参数username和password
2、设置期望
登录成功
登录失败
五、测试是否成功
1、页面样式
2、测试文本框
①登录文本框
必填测试
长度测试
②密码文本框
必填测试
长度测试
规范测试
③登录失败
根据apifox设置的期望,输入账号不等于test时,登录失败
④登录成功
在登录前查看本地缓存
登录成功后页面跳转,本地缓存中存入返回的token
六、完整代码
1、LoginView.vue
<template><div class="page_all flex flex-center"><div class="login_all flex flex-between"><div class="login_left flex flex-center"><img src="/svg/login.png"></div><div class="login_right flex flex-center flex-column"><div class="form flex flex-center flex-column"><div class="title flex flex-center">CMS管理系统</div><el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="el-form demo-ruleForm":size="formSize" status-icon><el-form-item prop="username"><el-input v-model="ruleForm.username" placeholder="请输入账号" /></el-form-item><el-form-item prop="password"><el-input v-model="ruleForm.password" show-password placeholder="请输入密码" /></el-form-item><el-form-item class="btn-group"><el-button type="primary" @click="submitForm(ruleFormRef)">登录</el-button><el-button @click="resetForm(ruleFormRef)">重置</el-button></el-form-item></el-form></div></div></div></div>
</template>
<script setup>
//引入方法
import { reactive, ref } from 'vue' //引入vue响应式
import { login } from '@/api/logininfo' //引入登录接口
import { useRouter } from 'vue-router' //引入路由
import { ElMessage } from 'element-plus' //引入提示框
import { setToken } from '@/utils/token' //引入token设置//设置表单大小
const formSize = ref('default')
//设置表单数据
const ruleFormRef = ref()
const ruleForm = reactive({username: '',password: '',
})//设置路由
const router = useRouter();
//设置验证规则
const rules = reactive({username: [{ required: true, message: '请输入账号', trigger: 'blur' },{ min: 3, max: 10, message: '长度请在3-10之间', trigger: 'blur' },],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 6, max: 20, message: '长度请在6-20之间', trigger: 'blur' },{validator: (rule, value, callback) => {const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/;if (!passwordPattern.test(value)) {callback(new Error('密码必须包含大写字母、小写字母、数字和特殊字符'));} else {callback();}}, trigger: 'blur'},// 或者// {pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/, message: '密码必须包含大写字母、小写字母、数字和特殊字符', trigger: 'blur'}],
})//表单提交
const submitForm = async (formEl) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {console.log('submit!');// 1、请求登录接口进行登录// 参数为ruleFormconsole.log(ruleForm)// 2、请求登录接口进行登录login( ruleForm ).then(res => {console.log('res:', res)if (res.code == 1) {// 3、提示成功信息ElMessage.success(res.msg || '登录成功')//存入该账号的tokensetToken(res.data.token)// 4、跳转页面router.push('/')}else {ElMessage.error(res.msg || '登录失败')}})} else {console.log('error submit!', fields)}})
}
//重置表单
const resetForm = (formEl) => {if (!formEl) returnformEl.resetFields()
}</script>
<style>
.page_all {width: 100%;height: 100vh;background-color: #808cdd;
}.login_all {width: 50%;height: 60%;background-color: white;
}.login_left {width: 50%;height: 98%;
}.login_left img {width: 95%;height: 80%;object-fit: contain;
}.login_right {width: 50%;height: 100%;
}.title {font-size: 25px;color: #646cff;letter-spacing: 3px;height: 20%;
}.form {flex: 1;width: 90%;
}.el-form {width: 60%;
}.el-input__inner {font-size: 12px;
}.btn-group {width: 100%;margin-top: 30px;
}.btn-group button {width: 45%;
}.el-form-item__content {justify-content: space-between;
}
</style>
2、main.css
@import './base.css';.flex {display: flex;
}.flex-between {/*将弹性容器内的子元素沿主轴 均匀分布,第一个子元素位于主轴起点,最后一个元素位于主轴终点,中间的元素之间等距相等*/justify-content: space-between;align-items: center;
}/* 水平轴上对齐 */
.flex-around {/*项目在主轴上均匀分布,每个项目两侧间距相等,第一个项目和最后一个项目距离容器边缘的艰苦是其他项目之间间距的一半*/justify-content: space-around;align-items: center;
}.flex-center {justify-content: center;align-items: center;
}
/* 隐藏底部的按钮 */
.vue-devtools__panel[data-v-e9063f7b]{display: none;
}
.flex-column {flex-direction: column;
}
/* 设置表单宽度 */
.el-form{width:100% !important;
}
3、base.css
/* color palette from <https://github.com/vuejs/theme> */
:root {--vt-c-white: #ffffff;--vt-c-white-soft: #f8f8f8;--vt-c-white-mute: #f2f2f2;--vt-c-black: #181818;--vt-c-black-soft: #222222;--vt-c-black-mute: #282828;--vt-c-indigo: #2c3e50;--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);--vt-c-text-light-1: var(--vt-c-indigo);--vt-c-text-light-2: rgba(60, 60, 60, 0.66);--vt-c-text-dark-1: var(--vt-c-white);--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);--el-color-primary: #646cff;--el-button-hover-bg-color: #868bf7;--el-color-primary-light-3: #868bf7;--el-color-primary-light-5:#b4b7ef;--el-color-primary-light-9:#e5e6f7;--el-color-primary-light-7:#c6c8fa;
}/* semantic color variables for this project */
:root {--color-background: var(--vt-c-white);--color-background-soft: var(--vt-c-white-soft);--color-background-mute: var(--vt-c-white-mute);--color-border: var(--vt-c-divider-light-2);--color-border-hover: var(--vt-c-divider-light-1);--color-heading: var(--vt-c-text-light-1);--color-text: var(--vt-c-text-light-1);--section-gap: 160px;
}/*
@media (prefers-color-scheme: dark) {:root {--color-background: var(--vt-c-black);--color-background-soft: var(--vt-c-black-soft);--color-background-mute: var(--vt-c-black-mute);--color-border: var(--vt-c-divider-dark-2);--color-border-hover: var(--vt-c-divider-dark-1);--color-heading: var(--vt-c-text-dark-1);--color-text: var(--vt-c-text-dark-2);}
} *//* *,
*::before,
*::after {box-sizing: border-box;margin: 0;font-weight: normal;
} */body {min-height: 100vh;color: var(--color-text);background: var(--color-background);transition:color 0.5s,background-color 0.5s;line-height: 1.6;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',sans-serif;font-size: 15px;text-rendering: optimizeLegibility;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;margin: 0;
}