一、路由配置
经过分析,项目一共需要4个一级路由:登录(login)、主页(home)、404、任意路由(重定向到404)。
1、安装路由插件
pnpm install vue-router
2、创建路由组件
在src目录下新建views文件夹,在views中创建login、home、404路由组件。这几个路由组件里面也要新建index.vue,以便于测试。
3、配置路由
在src目录下新建router文件夹,书写路由配置(包含index.ts和routes.ts,注意一定是ts文件!)
(1)src/router/routes.ts
// 对外暴露配置路由(常量路由)
export const constantRoute = [
{
// 登录
path: '/login',
component: () => import('@/views/login/index.vue'),
name: 'login'
},
{
// 登录成功以后展示数据的路由
path: '/',
component: () => import('@/views/home/index.vue'),
name: 'layout'
},
{
// 404
path: '/404',
component: () => import('@/views/404/index.vue'),
name: '404'
},
{
// 任意路由
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'Any'
}
]
(2)src/router/index.ts
// 通过vue-router插件实现路由配置
import { createRouter, createWebHashHistory } from 'vue-router';
// 引入routes配置项
import { constantRoute } from './routes';
// 创建路由
let router = createRouter({
// 路由模式hash
history: createWebHashHistory(),
routes: constantRoute,
// 滚动行为
scrollBehavior() {
return {
left: 0,
top: 0
}
}
})
export default router;
4 、引入路由
在main.ts引入
// 引入路由
import router from '@/router'
// 注册模板路由
app.use(router)
5、测试
在App.vue中
<router-view></router-view>
运行项目:
在这里输入不同的路径,home/login/404 ,能够切换即为成功
二、登录模块
1、登录路由静态的搭建
采用element-plus中的Layout布局(栅格布局)、From表单组件、input组件、button组件。
Layout布局:一共是24 分栏,:span代表栅格占据的列数,:xs代表屏幕宽度<768px时栅格占据的列数。
input组件::prefix-icon代表前缀图标,show-password代表是否显示切换密码图标
src/views/login/index.vue
<template>
<div class="login_container">
<el-row>
<el-col :span="12" :xs="0"></el-col>
<el-col :span="12" :xs="24">
<el-form class="login_from">
<h1>Hello</h1>
<h2>欢迎来到唧唧bong甄选</h2>
<el-form-item>
<el-input :prefix-icon="User" v-model="loginFrom.username"></el-input>
</el-form-item>
<el-form-item>
<el-input type="password" :prefix-icon="Lock" v-model="loginFrom.password" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button class="login_btn" type="primary" size="default">登录</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';
let loginFrom = reactive({
username: 'admin',
password: '111111'
})
</script>
<style scoped lang="scss">
.login_container {
width: 100%;
height: 100vh;
background: url('@/assets/images/background.jpg') no-repeat;
background-size: cover;
.login_from{
width: 80%;
position: relative;
top: 30vh;
background: url('@/assets/images/login_form.png') no-repeat;
background-size: cover;
padding: 40px;
h1{
color: white;
font-size: 40px;
}
h2{
color: white;
font-size: 20px;
margin: 20px 0;
}
.login_btn{
width: 100%;
}
}
}
</style>
2、模板封装登录业务
我们需要做的:点击登录时,会携带用户名和密码向服务器发请求获取token,此时我们需要把token存储起来,用于后续向服务端发请求获取信息的身份验证,这里我们用pinia和loacalStroage进行存储。并且实现弹窗提示信息,以及加载清除效果。
(1)安装pinia
pnpm i pinia
(2)创建大仓库:src/store/index.ts
import { createPinia } from 'pinia'
//创建大仓库
const pinia = createPinia()
//对外暴露:入口文件需要安装仓库
export default pinia
(3)在入口文件(main.ts)中引入并安装pinia:src/main.ts
// 引入大仓库
import pinia from './store'
// 安装仓库
app.use(pinia)
(4)创建小仓库:src/store/modules/user.ts
// 创建用户相关的小仓库
import { defineStore } from 'pinia'
// 接口
import {reqLogin} from '@/api/user'
// 引入数据类型
import type {loginForm} from '@/api/user/type'
//创建用户小仓库
let useUserStore = defineStore('User', {
//小仓库存储数据地
state: () => {
return {
token:localStorage.getItem("TOKEN"),//存储用户唯一标识,本地存储持久化token
}
},
//异步|逻辑的地方
actions: {
// 用户登录的方法
async userLogin(data:loginForm){
// 登录请求
let result:any = await reqLogin(data);
console.log(result);
//登录请求:成功200->token
//登录请求:失败201->登录失败错误的信息
if(result.code == 200){
//由于pinia|vuex存储数据其实利用js对象
//pinia仓库存储一下token
this.token = result.data.token;
//本地存储持久化存储一份
localStorage.setItem("TOKEN",result.data.token);
// 能保证当前asnyc函数返回一个成功的promise
return 'ok';
}else{
return Promise.reject(new Error (result.data.message))
}
}
},
getters: {},
})
//对外暴露获取小仓库方法
export default useUserStore
(5)在登录页面中引入小仓库,点击登录时通知user小仓库发请求,存储token:src/views/login/index.vue
<template>
<div class="login_container">
<el-row>
<el-col :span="12" :xs="0"></el-col>
<el-col :span="12" :xs="24">
<el-form class="login_from">
<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" v-model="loginForm.password" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button :loading="loading" class="login_btn" type="primary" size="default" @click="login">登录</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,ref } from 'vue';
// 引入用户相关的小仓库
import useUserStore from '@/store/modules/user'
import {useRouter} from 'vue-router';
import {ElNotification} from 'element-plus'
let useStore = useUserStore()
let $router = useRouter()
let loading = ref(false)
// 收集账号与密码的数据
let loginForm = reactive({
username: 'admin',
password: '111111'
})
// 登录按钮回调
const login = async()=>{
// 加载效果:开始加载
loading.value=true
// 成功的话到首页,失败的话弹出失败信息
try {
await useStore.userLogin(loginForm);
$router.push('/')
// 登陆成功提示
ElNotification({
type:'success',
message:'登陆成功'
})
// 登录成功加载效果消失
loading.value = false
} catch (error) {
// 登录失败加载效果消失
loading.value = false
// 登录失败的提示信息
ElNotification({
type: 'error',
message: (error as Error).message
})
}
}
</script>
(6)注意点
- userLogin会返回一个Promise,此处可以使用try...catch...或.then来进行下一步结果处理。
- 不管成功或失败,都需要使登录加载效果消失,因此也可以统一写在finally里面:
-
try { // 保证登录成功 await useStore.userLogin(loginFrom) // 编程式导航跳转到展示数据首页 $router.push('/') // 登录成功信息提示 ElNotification({ type: 'success', message: '登录成功', }) } catch (error) { // 登录失败的提示信息 ElNotification({ type: 'error', message: (error as Error).message }) } finally{ // 登录成功/失败加载效果消失 loading.value = false }
3、用户仓库数据ts类型的定义
我们要做的,封装用户仓库数据ts类型的定义 ,因为之前在user.ts里面使用了很多未封装未声明类型的ts数据,写的也很长,所以需要封装一下。
(1)定义小仓库数据state类型:src\store\modules\types\type.ts
// 定义小仓库数据state类型
export interface UserState {
token: string | null
}
(2)登录接口返回的数据类型:src\api\user\type.ts
登录请求可能返回成功/失败的数据,因此类型需要dataType需要包括成功的数据token和失败的数据message,且是可选的,要加上"?"。
interface dataType {
token?: string,
message?:string
}
// 登录接口返回的数据类