今天开始使用 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的实战项目吧。
目录
用户管理模块搭建
实现添加与修改业务
实现分配角色任务
实现删除业务
实现搜索重置业务
用户管理模块搭建
用户管理模块的搭建和之前几个模块搭建类似,也是采用element组件库提供的相关组件进行处理,在卡片中防止输入框、按钮、以及表格和分页器等组件,具体部分代码如下:
搭建完相关静态之后,通过编写接口函数以及相关ts类型获取相关数据,如下:
// 用户管理模块的接口
import request from '@/utils/request'
// 引入ts类型
import type { UserResponseData } from './type'
// 枚举地址
enum API {
// 获取全部已有用户账号信息
ALLUSER_URL = '/admin/acl/user/',
}
// 获取用户账号信息的接口
export const reqUserInfo = (page: number, limit: number) =>
request.get<any, UserResponseData>(API.ALLUSER_URL + `${page}/${limit}`)
编写完接口函数之后,就可以传递相关参数获取数据,然后将数据放置在表格中进行展示,如下:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 引入接口函数
import { reqUserInfo } from '@/api/acl/user'
// 引入ts类型
import type { UserResponseData, Records } from '@/api/acl/user/type'
// 当前的页码值
let pageNo = ref<number>(1)
// 页码展示的数据
let pageSize = ref<number>(5)
// 用户的总个数
let total = ref<number>(0)
// 存储全部用户的数组
let userArr = ref<Records>([])
// 组件挂载完毕执行函数
onMounted(() => {
getHasUser()
})
// 获取全部已有的信息
const getHasUser = async (pager = 1) => {
pageNo.value = pager
let result: UserResponseData = await reqUserInfo(pageNo.value, pageSize.value)
if (result.code == 200) {
total.value = result.data.total
userArr.value = result.data.records
}
}
// 更新当前页码能够显示的条数
const handler = () => {
getHasUser()
}
</script>
将定义的响应式变量数据赋予相关数据值之后,就可以在表格中进行相关的展示了,因为前几篇文章已经讲解过几次关于分页器进行页面切换效果的原理,这里就不再赘述,如下:
最终实现的效果为:
实现添加与修改业务
实现添加和修改业务需要对数据进行修改,这里仍然需要借助接口来实现,如下进行接口编写:
编写完接口后就可以对数据进行处理了,这里我采用element组件库中的抽屉功能实现添加和修改数据,如下:
<!-- 抽屉结构:完成添加或修改新的用户账号信息 -->
<el-drawer v-model="drawer">
<template #header>
<h4>添加用户</h4>
</template>
<template #default>
<el-form>
<el-form-item label="用户姓名">
<el-input placeholder="请输入用户姓名" v-model="userParams.username"></el-input>
</el-form-item>
<el-form-item label="用户昵称">
<el-input placeholder="请输入用户昵称" v-model="userParams.name"></el-input>
</el-form-item>
<el-form-item label="用户密码">
<el-input placeholder="请输入用户密码" v-model="userParams.password"></el-input>
</el-form-item>
</el-form>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</div>
</template>
</el-drawer>
给添加和修改数据的按钮绑定点击事件,对响应式数据drawer进行布尔值数据的切换用来控制抽屉的显示与隐藏,如下:
// 控制抽屉的显示与隐藏
let drawer = ref<boolean>(false)
当然为了对数据进行校验,我们可以设置自定义规则,给我们的数据进行相关的限制,如下:
接下来开始编写校验规则,如下:
// 校验用户名字的回调函数
const validatorUsername = (rule: any, value: any, callback: any) => {
// 用户名字|昵称,长度至少五位
if (value.trim().length >= 5) {
callback()
} else {
callback(new Error('用户的名称至少五位!'))
}
}
// 校验用户名称的回调函数
const validatorName = (rule: any, value: any, callback: any) => {
// 用户名字|昵称,长度至少五位
if (value.trim().length >= 5) {
callback()
} else {
callback(new Error('用户的昵称至少五位!'))
}
}
// 校验密码的回调函数
const validatorPassword = (rule: any, value: any, callback: any) => {
// 用户名字|昵称,长度至少五位
if (value.trim().length >= 6) {
callback()
} else {
callback(new Error('用户的密码至少六位!'))
}
}
// 表单校验的规则对象
const rules = {
// 用户名字
username: [{ required: true, trigger: 'blur', validator: validatorUsername }],
// 用户昵称
name: [{ required: true, trigger: 'blur', validator: validatorName }],
// 密码
password: [{ required: true, trigger: 'blur', validator: validatorPassword }],
}
编写完校验规则之后,在保存按钮处先进行表单校验,校验完成后才能执行保存回调后面的函数:
// 保存按钮的回调
const save = async () => {
// 点击保存按钮的时候,务必需要保证表单全部符合条件再去发起请求
await formRef.value.validate()
// 保存按钮:添加|修改已有的用户账号信息
let result = await reqAddOrUpdateUser(userParams)
// 添加或更新成功
if (result.code == 200) {
drawer.value = false // 抽屉关闭
// 提示消息
ElMessage({ type: 'success', message: userParams.id ? '更新成功' : '添加成功' })
// 获取最新的全部账号信息
getHasUser()
} else {
drawer.value = false // 抽屉关闭
// 提示消息
ElMessage({ type: 'error', message: userParams.id ? '更新失败' : '添加失败' })
}
}
注意:在校验规则书写完成之后,还要才重新点击添加处进行表单错误提示信息的一个清除
最终结果如下:
更新的业务很简单,只需要在点击更新按钮的回调处,将当前数据与初始数据进行一个合并即可:
// 更新已有用户按钮的回调
const updateUser = (row: User) => {
// 抽屉显示出来
drawer.value = true
// 存储收集已有的账号信息
Object.assign(userParams, row)
// 清除上一次的错误的提示信息
nextTick(() => {
formRef.value.clearValidate('username')
formRef.value.clearValidate('name')
})
}
判断当前是处于添加页面还是修改页面,只需要对当前的数据是否存在id值校验即可:
最终结果如下:
实现分配角色任务
实现分配角色的任务,这里也借助了一个抽屉进行数据的显示,给分配角色按钮设置点击事件,并传入当前数据 row 作为参数,通过点击按钮触发点击事件来实现抽屉的显示与隐藏以及数据的合并:
// 分配角色按钮的回调
const setRole = (row: User) => {
// 抽屉显示出来
drawer1.value = true
// 数据合并
Object.assign(userParams, row)
}
给抽屉设置如下的样式进行选择,这里我设置了一些假数据进行展示:
<!-- 抽屉结构:用户某一个已有的账号进行职位分配 -->
<el-drawer v-model="drawer1">
<template #header>
<h4>分配角色</h4>
</template>
<template #default>
<el-form>
<el-form-item label="用户姓名">
<el-input v-model="userParams.username" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="职位列表">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">
全选
</el-checkbox>
<!-- 显示职位的复选框 -->
<el-checkbox-group v-model="userRole" @change="handleCheckedCitiesChange">
<el-checkbox v-for="(role, index) in allRole" :key="index" :label="role">{{ role }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</div>
</template>
</el-drawer>
通过点击复选框来实现全选效果以及全选样式效果,如下:
// 全选复选框的change事件
const handleCheckAllChange = (val: boolean) => {
userRole.value = val ? allRole.value : []
isIndeterminate.value = false
}
// 底部复选框事件
const handleCheckedCitiesChange = (value: string[]) => {
// 已经勾选的这些项目的长度
const checkedCount = value.length
checkAll.value = checkedCount === allRole.value.length
// 顶部的复选框不确定的样式
isIndeterminate.value = !(checkedCount === allRole.value.length)
}
最终实现的效果如下:
接下来通过编写接口,获取全部职位,当前账号拥有的职位接口以及给已有的用户分配相应角色的接口,如下:
编写完接口后,给分配角色按钮的点击事件设置接口函数,获取当前id数据的所有职位信息:
// 分配角色按钮的回调
const setRole = async (row: User) => {
// 数据合并
Object.assign(userParams, row)
// 获取全部的职位的数据与当前用户已有的职位的数据
let result: AllRoleResponseData = await reqAllrole(userParams.id as number)
if (result.code == 200) {
// 存储全部职位
allRole.value = result.data.allRolesList
// 存储当前用户已有的职位
userRole.value = result.data.assignRoles
// 抽屉显示出来
drawer1.value = true
}
}
获取完职位信息之后,编写相关的参数数据,给抽屉的确定按钮设置接口函数来对当前分配角色数据进行更新,如下:
// 确定按钮回调(分配职位)
const confirmClick = async () => {
// 收集参数
let data: SetRoleData = {
userId: userParams.id as number,
roleIdList: userRole.value.map((item) => {
return item.id as number
}),
}
// 分配用户职位
let result: any = await reqSetUserRole(data)
if (result.code == 200) {
// 提示信息
ElMessage({ type: 'success', message: '分配职位成功!' })
// 关闭抽屉
drawer1.value = false
// 获取更新后的数据,更新完毕留在当前页
getHasUser(pageNo.value)
}
}
最终实现的结果如下:
实现删除业务
删除业务有两种情况:一种是单个删除,另一种是批量删除,这里的话都需要对其撰写相应的接口
编写完接口后,首先我们先实习单个删除的功能,给单个删除按钮设置气泡显示框,然后通过confirm事件传递相应的id值,之后调用接口删除即可:
// 删除某一个用户
const deleteUser = async (userId: number) => {
let result: any = await reqRemoveUser(userId)
if (result.code == 200) {
ElMessage({ type: 'success', message: '删除成功!' })
getHasUser(userArr.value.length > 1 ? pageNo.value : pageNo.value - 1)
} else {
ElMessage({ type: 'error', message: '删除失败!' })
}
}
批量删除业务,这里需要借助表格的一个监听事件,监听当前选择的是哪个数据的id,如下:
通过监听函数,将获取到的值在存储在响应式变量当中去:
// table复选框勾选的时候会触发的事件
const selectChange = (value: any) => {
selectIdArr.value = value
}
存储完变量之后,通过map函数将数组中的id数据提取出来,作为参数传递到批量删除接口函数中
// 批量删除
const deleteSelectUser = async () => {
// 整理批量删除的参数
let idsList: any = selectIdArr.value.map((item) => {
return item.id
})
// 批量删除的请求
let result: any = await reqSelectUser(idsList)
if (result.code == 200) {
ElMessage({ type: 'success', message: '删除成功!' })
getHasUser(userArr.value.length > 1 ? pageNo.value : pageNo.value - 1)
}
}
实现搜索重置业务
实现重置业务可以定义一个响应式ref变量,然后通过v-model双向数据绑定,绑定到搜索输入框当中,接下来通过修改一下之前编写获取用户账号信息的接口,给其添加一个username属性,从而进行username名字的检索操作,如下:
然后接下来改变之前获取用户账号信息的参数,给其添加一个keyword,因为一开始你并没有进行搜索,所以一开始的keyword的属性值为空,所以获取的是全部数据,如下:
这里设置一个禁用的条件,只有你搜索输入框中有数据才能进行搜索,否则就是禁用状态,这也就保证了搜索按钮的点击事件触发的正确性,如下在保证输入框有数据,也就是keyword不为空的情况下进行搜索,调用获取信息数据的函数,也就会将我们关键字username信息进行找到:
// 搜索按钮的回调
const search = () => {
// 根据关键字获取相应的数据
getHasUser()
// 清空关键字
keyword.value = ''
}
重置按钮操作更为简单,给其绑定点击事件,然后调用我之前文件讲解header中刷新按钮的操作,对仓库中的数据进行一个更新并重新挂载,这也就保证了数据的重新获取,点击函数如下:
// 重置按钮
const reset = () => {
settingStore.refsh = !settingStore.refsh
}
</script>
最终结果如下:
本项目的用户管理页面功能的搭建就讲解到这,下一篇文章将继续讲解其它模块的主体内容,关注博主学习更多前端vue知识,您的支持就是博主创作的最大动力!