今天开始使用 vue3 + ts 搭建一个项目管理的后台,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GithHub上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关注本专栏,学习更多前端vue知识,然后开篇先简单介绍一下本项目用到的技术栈都有哪几个方面(阅读本文章能够学习到的技术):
vite:快速轻量且功能丰富的前端构建工具,帮助开发人员更高效构建现代Web应用程序。
pnpm:高性能、轻量级npm替代品,帮助开发人员更加高效地处理应用程序的依赖关系。
Vue3:Vue.js最新版本的用于构建用户界面的渐进式JavaScript框架。
TypeScript:JavaScript的超集,提供了静态类型检查,使得代码更加健壮。
Animate:基于JavaScript的动画框架,它使开发者可以轻松创建各种炫酷的动画效果。
vue-router:Vue.js官方提供的路由管理器与Vue.js紧密耦合,非常方便与Vue.js一同使用。
Pinia:Vue3构建的Vuex替代品,具有响应式能力,提供非常简单的 API,进行状态管理。
element-plus:基于Vue.js 3.0的UI组件库,用于构建高品质的响应式Web应用程序。
axios:基于Promise的HTTP客户端,可以在浏览器和node.js中使用。
three:基于JavaScript的WebGL库,开发者可以编写高性能、高质量的3D场景呈现效果。
echarts:基于JavaScript的可视化图表库,支持多种类型的图表,可根据需要自行安装。】
当然还有许多其他的需要安装的第三方库,这里就不再一一介绍了,在项目中用到的地方自行会进行讲解,大家自行学习即可,现在就让我们走进vue3+ts的实战项目吧。
目录
初始路由配置
登录页面搭建
配置Pinia仓库
登录时间判断
登录表单校验
路由鉴权
实现登录框动画效果
初始路由配置
我们在开始搭建后台项目之前,都要仔细考虑一下路由应该如何配置,以下是本项目配置路由的详细步骤,如果想更加深入了解vue3路由知识,推荐参考我之前的文章:Vue 3 路由进阶——从基础到高级的完整指南:
安装配置路由的第三方包:
pnpm install vue-router
安装完路由包之后,接下来我们要先创建路由组件,因为项目仅仅是一个后台所以我们只需要在src目录下新进views文件,再创建3个路由组件即可:
创建完路由组件之后,接下来开始进行相关router配置:
// 通过vue-router插件实现模板路由配置
import { createRouter, createWebHistory } from 'vue-router'
import { constantRoute } from './routes'// 创建路由器
const router = createRouter({// 路由模式history: createWebHistory(),routes: constantRoute,// 滚动行为scrollBehavior() {return {left: 0,top: 0,}},
})
export default router
这里我单独将routes的属性对象抽离出一个js文件出来 ,便于后期的维护,代码如下:
// 对外暴露路由(常量路由)
export const constantRoute = [{path: '/login',name: 'login', // 命名路由component: () => import('@/views/login/index.vue'),meta: {title: '登录页面',},},{path: '/',name: 'home',component: () => import('@/views/home/index.vue'),meta: {title: '后台页面',},},{path: '/404',name: '404',component: () => import('@/views/404/index.vue'),meta: {title: '404界面',},},{path: '/:pathMatch(.*)*',name: 'any',redirect: '/404',},
]
如果想把写好的路由进行展示的话,需要通过 router-view 作为路由出口,路由匹配到的组件将进行响应的渲染,这里在App根组件上进行展示:
接下来在入口文件 main.ts 处进行路由router的挂载:
实现网页进度条: 在网页内容最顶部设置一个进度条来显示页面的加载程度,这里我将进度条单独抽离出一个组件:
<template><div class="wraps"><div ref="bar" class="bar"></div></div>
</template><script setup lang="ts">
import { ref } from 'vue'
let speed = ref<number>(1)
let bar = ref<HTMLElement>()
let timer = ref<number>(0)
const startLoading = () => {speed.value = 1let dom = bar.value as HTMLElementtimer.value = window.requestAnimationFrame(function fn() {if (speed.value < 90) {speed.value += 1dom.style.width = speed.value + '%'timer.value = window.requestAnimationFrame(fn)} else {speed.value = 1window.cancelAnimationFrame(timer.value)}})
}
const endLoading = () => {let dom = bar.value as HTMLElementsetTimeout(() => {window.requestAnimationFrame(() => {speed.value = 100dom.style.width = speed.value + '%'})dom.remove()}, 1000)
}
defineExpose({startLoading,endLoading,
})
</script><style lang="scss" scoped>
.wraps {position: fixed;top: 0;width: 100%;height: 2px;.bar {height: inherit; // 继承父级高度width: 0;background: red;}
}
</style>
接下来在main.ts入口文件处进行进度条的组件引入,然后通过导航守卫进行设置,代码如下:
// 导入进度条
import loadingBarVue from '@/components/loadingBar/index.vue'const Vnode = createVNode(loadingBarVue)
render(Vnode, document.body)
// 设置路由前置守卫
router.beforeEach((to, from, next) => {Vnode.component?.exposed?.startLoading()next()
})
// 设置路由后置守卫
router.afterEach((to, from) => {Vnode.component?.exposed?.endLoading()
})
实现标题的动态修改:
由于我在路由表中设置了meta属性,所以我们可以通过拿到这个属性值来动态修改标题
在导航前置守卫来获取标题名称并动态修改即可:
登录页面搭建
当你访问一个网站或者一个应用程序时,你可能需要输入一些凭证来验证你的身份,比如用户名和密码,这就是登录。登录是一种很普遍的操作,几乎所有的网站和应用程序都要求用户先登录才能进入其内部进行操作。接下来将详细讲解登录页面的搭建:
创建登录页面的背景:创建背景很简单,随便在网上找一个背景图然后通过以下代码进行实现即可
<template><div class="login_container"></div>
</template><script setup lang="ts"></script><style lang="scss" scoped>
.login_container {width: 100%;height: 100vh;background: url('@/assets/images/bg1.jpg') no-repeat;background-size: cover; // 调整背景图片的大小,使其填充整个元素背景,并尽可能保持比例不失真
}
</style>
我就找了个奥特曼的背景图,这个背景图随便哈:
书写登录框页面的样式:这里我采用了element的form表单,然后通过Element UI 中的栅格系统 el-row 和 el-col,来实现响应式布局和网格化布局,举个例子:
el-row 和 el-col 组合使用,可以将栅格系统划分为一个 24 格的栏目。你可以使用 :span 属性来指定每个列容器所占据的栏数,例如页面左侧侧边栏占用 4 栏,中间主体内容占用 16 栏,右侧广告栏占用 4 栏,则对应的代码可以写为:
<el-row><el-col :span="4">左侧侧边栏</el-col><el-col :span="16">中间主体内容</el-col><el-col :span="4">右侧广告栏</el-col>
</el-row>
上面的代码中,通过 :span 属性来指定了每个列容器所占据的栏数,当屏幕尺寸变化时,页面会自动根据栅格系统重新划分栏目和内容占比,以适应不同的设备屏幕和分辨率。
接下来开始实现真正的登录页面的书写,具体代码如下:
<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_form"><h1>欢迎来到后台管理系统</h1><el-form-item><el-inputclass="input":prefix-icon="User"v-model="loginForm.username"placeholder="请输入用户名"></el-input></el-form-item><el-form-item><el-inputclass="input":prefix-icon="Lock"v-model="loginForm.password"type="password"show-passwordplaceholder="请输入密码"></el-input></el-form-item><el-form-item><el-button class="login_btn" type="primary">登录</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 loginForm = reactive({username: '',password: '',
})
</script><style lang="scss" scoped>
.login_container {width: 100%;height: 100vh;background: url('@/assets/images/bg1.jpg') no-repeat;background-size: cover; // 调整背景图片的大小,使其填充整个元素背景,并尽可能保持比例不失真.login_form {position: relative;top: 30vh;height: 300px;width: 60%;background: rgba(0, 0, 0, 0.5);box-shadow: 0 15px 25px rgba(50, 10, 10, 0.65);border-radius: 10px;padding: 40px;}h1 {font-size: 30px;text-align: center;color: white;margin: 25px auto 40px;}.input {font-size: 15px;height: 38px;margin-bottom: 10px;}.login_btn {position: relative;height: 40px;font-size: 14px;color: #fff;margin: 20px auto;width: 80%;background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);border-radius: 30px;z-index: 1;&:hover {animation: ani 8s linear infinite;height: 40px;border: none;}@keyframes ani {0% {background-position: 0%;}100% {background-position: 400%;}}&:before {content: '';position: absolute;top: -5px;left: -5px;right: -5px;bottom: -5px;z-index: -1;background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);background-size: 400%;border-radius: 35px;transition: 1s;}&:hover::before {filter: blur(20px);}&:active {background: linear-gradient(32deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);}}
}
</style>
登录按钮设置点击事件:登录按钮这一块是需要将表单数据向仓库发起请求,看数据是否匹配成功,请求成功跳转首页展示数据,请求失败弹出失败登录信息。这里需要借助状态管理工具pinia来获取公共常用的数据,这里先在终端执行如下命令进行安装
pnpm i pinia
# 如果最新版本报错,推荐安装旧版本稳定版
pnpm i pinia@2.0.34
所以说登录按钮实现逻辑校验跳转方面,下面再接着讲,本标题内容的最后我们先实现这样效果,我们可能都有习惯在输入完账号密码后不点击登录按钮而是直接点击回车键实现登录效果,这里简单实现一下这样的功能:
当我们点击回车键时页面执行了login函数,这里先简单实现这个效果,后期再根据情况完善:
配置Pinia仓库
不了解pinia的朋友,推荐看一下我之前讲解的文章:探索Pinia:Vue状态管理的未来 ,本项目就不再着重讲解其具体知识了。具体讲解如下:
在src目录下新建store文件夹,文件夹下新建 index.ts 文件,暴露如下代码:
// 设置pinia仓库
import { createPinia } from 'pinia'
const pinia = createPinia()
// 对外暴露:入口文件需要安装仓库
export default pinia
在main.ts入口文件出进行pinia的挂载:
接下来在store中新建一个user.ts文件,用来存储用户信息数据的仓库,代码如下:
// 创建用户信息相关数据的仓库
import { defineStore } from 'pinia'
// 引入登录接口
import { reqLogin } from '@/api/user'
// 引入数据类型
import type { loginForm } from '@/api/user/type'const useUserStore = defineStore('User', {// 存储数据state: () => {return {token: localStorage.getItem('TOKEN'), // 用户唯一标识token}},actions: {// 处理用户登录async userLogin(data: loginForm) {const result = await reqLogin(data)if (result.code === 200) {this.token = result.data.token// 本地持久化存储localStorage.setItem('TOKEN', result.data.token)// 保证当前async函数返回一个成功的promisereturn 'ok'} else {return Promise.reject(new Error(result.data.message))}},},getters: {},
})
// 对外暴露仓库方法
export default useUserStore
我们在login文件中通过获取表单数据作为对象来调用我们在pinia中定义的处理用户登录的方法,然后我们在pinia中定义方法中对数据进行持久化存储token,上文代码已经讲解的很清楚了。
接下来开始书写逻辑代码通过输入我们之前在mock中定义的假数据才能进行登录跳转,否则是不会进行登录的。
<script setup lang="ts">
import { User, Lock } from '@element-plus/icons-vue'
// 引入用户信息相关数据的pinia仓库
import userUserStore from '@/store/user'
import { useRouter } from 'vue-router'
import { ElNotification } from 'element-plus'
import { reactive, ref } from 'vue'
// 收集表单数据
let loginForm = reactive({username: '',password: '',
})
let userStore = userUserStore()
// 获取路由器
let $router = useRouter()
// 定义变量控制按钮加载效果
let loading = ref(false)// 登录按钮点击事件
const login = async () => {// 开始加载按钮loading效果loading.value = truetry {await userStore.userLogin(loginForm)// 编程式导航跳转到首页$router.push('/')if (ElNotification) {ElNotification.closeAll()}// 登录成功的提示信息ElNotification({type: 'success',message: '登录成功!',})// 登录成功加载效果消失loading.value = false} catch (error) {// 登录失败加载效果消失loading.value = falseif (ElNotification) {ElNotification.closeAll()}ElNotification({type: 'error',message: (error as Error).message,})}
}// 监听页面回车键的点击
window.addEventListener('keyup', (e) => {if (e.key === 'Enter') {console.log(123)login()}
})
</script>
登录时间判断
接下来我们有一个需求就是,当我们输入正确的账号和密码之后,登录成功的提示消息换成上午或下午好欢迎回来的样式,这一块的难点就是我们如何来判断用户登录成功之后,是上文登录还是下午登录来去动态显示登录成功的弹框样式,给出方法如下:
我们在utils工具文件夹下新增一个time.ts文件用于封装获取当前时间的方法:
// 封装一个函数:获取一个结果:当前早上|上午|下午|晚上
export const getTime = () => {let message = ''// 通过内置构造函数Dateconst hours = new Date().getHours()if (hours <= 9) {message = '早上'} else if (hours <= 12) {message = '上午'} else if (hours <= 18) {message = '下午'} else {message = '晚上'}return message
}
接下来我们在login组件下引入该时间函数,然后通过模板字符串动态的改变当前的弹框信息:
登录表单校验
登录表单校验是在用户输入登录信息后,对表单中的各个输入框进行校验来确保用户输入的内容符合要求的过程。通过对输入框进行校验,可以防止用户输入无效或非法的信息,从而提高系统的安全性和可靠性。
一般而言,登录表单校验需要校验以下内容:
用户名和密码是否为空;
用户名和密码是否为空;
用户名和密码是否匹配,即在用户数据库中是否存在。
如果登录表单校验没有通过,应该向用户提供提示信息,让其了解哪里出了问题,并告诉他们应该如何解决这些问题。如果校验通过,则可以允许用户登录系统。
因为我们使用的是element-plus组件库,所以是十分简单的实现表单校验,具体的内容可自行去官网进行查看,这里不再赘述,具体实现步骤如下:
定义的规则如下:
如果想全部表单项校验通过再发请求,而不是有表单数据警告还得点击按钮有提示效果:
自定义校验: 默认校验规则有很多限制,不方便书写特别复杂的校验规则,这里我采用自定义校验规则,让表单校验的情况更加复杂化一点:
路由鉴权
先解释一下什么是路由鉴权,假设你知道登录后台主页的访问路径,在没有登录的情况下,你能直接访问后台主页的路径吗?答案是肯定的 (在没有设置路由鉴权的情况下) ,所以后台设置路由鉴权极为重要。具体过程如下:
实现思路:自己封装一个路由鉴权的高阶组件,实现未登录拦截,并跳转到登录页面。判断本地是否有token,如果有就返回登录之后的子组件,否则就重定向到登录的Login组件。
这里我在main.ts文件中进行路由前置守卫的路由鉴权判断,在路由配置信息中,使用meta字段标识当前路由是否需要进行登录鉴权,如果需要,则meta字段中可以定义一个布尔类型的requiresAuth属性来标识。如下:
实现登录框动画效果
路由中我们可以设置动画过渡效果,这里可以借助第三方动画库 animate ,具体的动画库的详细操作,我在之前的文章已经讲解过了:Vue--》实现动画与过渡效果 ,这里就不再赘述,仅仅展示如何使用:
安装animate.css:可以通过如下命令进行安装
pnpm install animate.css
在项目的入口文件中(例如main.ts或者App.vue)引入Animate.css的样式文件,例如:
import { createApp } from 'vue'
import App from './App.vue'
import 'element-plus/packages/theme-chalk/src/base.scss'
import 'animate.css'createApp(App).mount('#app')
接下来,在Vue组件中引入el-form组件,并且在模板template代码中进行渲染,例如:
本项目的登录页面功能的搭建就讲解到这,下一篇文章将讲解项目首页功能搭建,关注博主学习更多前端vue知识,您的支持就是博主创作的最大动力!