今天开始使用 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提供的模板进行数据的呈现展示,如下编写接口:
// 角色管理模块的接口
import request from '@/utils/request'
import type { RoleResponseData } from './type'
// 枚举地址
enum API {
// 获取全部的职位的接口
ALLROLE_URL = '/admin/acl/role/',
}
// 获取全部的角色
export const reqAllRoleList = (page: number, limit: number, roleName: string) =>
request.get<any, RoleResponseData>(API.ALLROLE_URL + `${page}/${limit}/?roleName=${roleName}`)
编写完接口函数之后,接下来就可以进行样式的搭建了,具体搭建过程就不再讲解了,前几篇文章都有重复性,如下给出代码:
<template>
<div>
<el-card style="height: 80px">
<el-form :inline="true" class="form">
<el-form-item label="职位搜索">
<el-input placeholder="请你输入搜索职位关键字" v-model="keyword"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default" :disabled="keyword ? false : true" @click="search">搜索</el-button>
<el-button type="primary" size="default" @click="reset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin: 10px 0px">
<el-button type="primary" size="default" icon="Plus">添加职位</el-button>
<el-table border style="margin: 10px 0px" :data="allRole">
<el-table-column type="index" label="#" align="center"></el-table-column>
<el-table-column label="ID" align="center" prop="id"></el-table-column>
<el-table-column label="职位名称" align="center" show-overflow-tooltip prop="roleName"></el-table-column>
<el-table-column label="创建时间" align="center" show-overflow-tooltip prop="createTime"></el-table-column>
<el-table-column label="更新时间" align="center" show-overflow-tooltip prop="updateTime"></el-table-column>
<el-table-column label="操作" width="350px" align="center">
<!-- row:已有的职位对象 -->
<template #default="{ row }">
<el-button type="primary" size="default" icon="User">分配权限</el-button>
<el-button type="primary" size="default" icon="Edit">编辑</el-button>
<el-button type="primary" size="default" icon="Delete">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="pageNo"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
:background="true"
layout="prev, pager, next, jumper, -> ,sizes, total"
:total="total"
@current-change="getHasRole"
@size-change="sizeChange"
/>
</el-card>
</div>
</template>
form表单的样式给出如下代码:
编写的接口不仅能够获取数据,还能进行数据的搜索,这里对输入框进行双向数据绑定,当然还有获取数据的接口函数的调用,以及声明了一些响应式的变量进行数据的存储与重新赋值:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 接口函数方法
import { reqAllRoleList } from '@/api/acl/role'
import type { RoleResponseData, Records } from '@/api/acl/role/type'
// 引入仓库
import useLayOutSettingStore from '@/store/settings'
let settingStore = useLayOutSettingStore()
// 当前的页面
let pageNo = ref<number>(1)
// 当前页面展示的数据条数
let pageSize = ref<number>(10)
// 搜索职位的关键字
let keyword = ref<string>('')
// 存储全部已有的职位
let allRole = ref<Records>([])
// 职位的总个数
let total = ref<number>(0)
// 组件挂载完毕发起请求
onMounted(() => {
// 获取职位的请求
getHasRole()
})
// 获取全部用户信息的方法|分页器当前页码发生变化的回调
const getHasRole = async (pager = 1) => {
// 修改当前页面
pageNo.value = pager
let result: RoleResponseData = await reqAllRoleList(pageNo.value, pageSize.value, keyword.value)
if (result.code == 200) {
total.value = result.data.total
allRole.value = result.data.records
}
}
// 下拉菜单的回调
const sizeChange = () => {
getHasRole()
}
// 搜索按钮的回调
const search = () => {
// 再次发起请求根据关键字
getHasRole()
keyword.value = ''
}
// 重置按钮的回调
const reset = () => {
settingStore.refsh = !settingStore.refsh
}
</script>
最终的结果如下:
实现添加与更新业务
实现添加和更新业务在前几篇文章都有讲解过,可谓是老生常谈了,这里就不再详细的讲解,大部分是给出具体的代码,看看具体的实现,接口代码如下:
实现添加和更新业务这里都借助了对话框来实现,如下:
这里通过rules对对话框的表单进行一个规则校验:
const validatorRoleName = (_rule: any, value: any, callBack: any) => {
if (value.trim().length >= 2) {
callBack()
} else {
callBack(new Error('职位名称至少两位!'))
}
}
// 职位相关的校验规则
const rules = {
roleName: [{ required: true, trigger: 'blur', validator: validatorRoleName }],
}
给添加职位和修改职位的按钮绑定点击事件进行处理,如下先进行对话框的显示然后进行数据清除和绑定,这里清除校验错误的话需要借助nextTick进行处理,如下借助表单给出特定的方法进行处理:
// 添加职位按钮的回调
const addRole = () => {
// 对话框显示出来
dialogVisite.value = true
// 清空数据
Object.assign(RoleParams, {
roleName: '',
id: 0,
})
// 清空上一次表单校验的错误结果
nextTick(() => {
form.value.clearValidate('roleName')
})
}
// 更新职位按钮的回调
const updateRole = (row: RoleData) => {
// 对话框显示出来
dialogVisite.value = true
// 存储已有的职位——带有ID的
Object.assign(RoleParams, row)
}
给确定按钮绑定点击事件,调用接口函数来实现添加或修改业务的实现,如下:
// 确定按钮的回调
const save = async () => {
// 表单校验结果,结果通过再发请求,结果没有通过不应该再发请求
await form.value.validate()
// 添加|更新职位的请求
let result: any = await reqAddOrUpdateRole(RoleParams)
if (result.code == 200) {
// 提示文字
ElMessage({ type: 'success', message: RoleParams.id ? '更新成功!' : '添加成功!' })
// 对话框显示
dialogVisite.value = false
// 再次获取全部的已有的职位
getHasRole(RoleParams.id ? pageNo.value : 1)
}
}
结果如下:
实现分配角色权限业务
实现分配权限的业务需要两个接口的实现,一个是获取所有职位的菜单数据;另一个是根据点击菜单选项下发分配权限的任务,首先我们先将相应的接口进行实现,如下:
编写完接口之后,接下来需要对分配权限的按钮设置点击事件,如下:
我们这里也是采用的element提供的抽屉样式进行展示和修改数据,并在抽屉中书写树形组件,关于树形组件其函数代表什么意思,这里就不再赘述,感兴趣的可以去官方文档进行查阅:
获取相应的数值很简单,这里比较难以理解的是需要我们如何获取点击复选框获取到的数据:
// 分配权限按钮的回调
const setPermisstion = async (row: RoleData) => {
drawer.value = true // 抽屉显示出来
// 收集当前要分类权限的职位的数据
Object.assign(RoleParams, row)
// 根据职位去获取权限的数据
let result: MenuResponseData = await reqAllMenuList(RoleParams.id as number)
if (result.code == 200) {
menuArr.value = result.data
selectArr.value = filterSelectArr(menuArr.value, [])
}
}
// 树形控件的测试数据
const defaultProps = {
children: 'children',
label: 'name',
}
const filterSelectArr = (allData: any, initArr: any) => {
allData.forEach((item: any) => {
if (item.select && item.level == 4) {
initArr.push(item.id)
}
if (item.children && item.children.length > 0) {
filterSelectArr(item.children, initArr)
}
})
return initArr
}
最后通过收集相应的数据然后调用相关接口就可以实现分配权限的任务了:
// 抽屉确定按钮的回调
const handler = async () => {
// 职位的Id
const roleId = RoleParams.id as number
// 选中节点的Id
let arr = tree.value.getCheckedKeys()
// 半选的Id
let arr1 = tree.value.getHalfCheckedKeys()
let permissionId = arr.concat(arr1)
// 下发权限
let result: any = await reqSetPermisstion(roleId, permissionId)
if (result.code == 200) {
drawer.value = false // 抽屉关闭
// 提示消息
ElMessage({ type: 'success', message: '分配权限成功' })
// 页面刷新
window.location.reload()
} else {
ElMessage({ type: 'error', message: '分配权限失败' })
}
}
最终结果如下:
实现删除业务
实现删除业务,前几篇文章讲解的可谓是不厌其烦了,很简单调接口穿id即可,如下:
设置气泡确认框然后通过confirm事件来进行传递数据
接下来通过调用相关接口即可删除数据
// 删除已有的职位
const removeRole = async (id: number) => {
let result: any = await reqRemoveRole(id)
if (result.code == 200) {
ElMessage({ type: 'success', message: '删除成功' })
getHasRole(allRole.value.length > 1 ? pageNo.value : pageNo.value - 1)
} else {
ElMessage({ type: 'error', message: '删除失败' })
}
}
结果如下:
实现菜单模块搭建
接下来对菜单模块的路由组件样式进行搭建,和前文一样,借助element组件库提供的表格样式进行快速的样式搭建处理,首先我们先编写获取菜单模块的相应数据接口:
import request from '@/utils/request'
import type { PermissionResponseData } from './type'
// 枚举地址
enum API {
// 获取全部菜单与按钮的标识数据
ALLPERMISSTION_URL = '/admin/acl/permission',
}
// 获取菜单数据
export const reqAllPermisstion = () => request.get<any, PermissionResponseData>(API.ALLPERMISSTION_URL)
接下来通过借助element中表格样式进行样式快速搭建,如下:
<template>
<div>
<el-table :data="PermisstionArr" style="width: 100%; margin-bottom: 20px" row-key="id" border>
<el-table-column label="名称" prop="name"></el-table-column>
<el-table-column label="权限值" prop="code"></el-table-column>
<el-table-column label="修改时间" prop="updateTime"></el-table-column>
<el-table-column label="操作">
<template #default="{ row }">
<el-button type="primary" size="small" :disabled="row.level == 4 ? true : false">
{{ row.level == 3 ? '添加功能' : '添加菜单' }}
</el-button>
<el-button type="primary" size="small" :disabled="row.level == 1 ? true : false">编辑</el-button>
<el-button type="primary" size="small" :disabled="row.level == 1 ? true : false">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
搭建完静态内容之后,我们通过调用相关接口函数,获取的数据赋予响应式ref变量中,然后在进行相应的引用,js代码如下:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 引入获取菜单请求的API
import { reqAllPermisstion } from '@/api/acl/menu'
// 引入ts类型
import type { PermissionResponseData, PermissionList } from '@/api/acl/menu/type'
// 存储菜单数据
let PermisstionArr = ref<PermissionList>([])
// 组件挂载完毕
onMounted(() => {
getHasPermisstion()
})
// 获取菜单数据的方法
const getHasPermisstion = async () => {
let result: PermissionResponseData = await reqAllPermisstion()
if (result.code == 200) {
PermisstionArr.value = result.data
}
}
</script>
最终实现的结果如下:
实现菜单模块的添加与更新业务
关于添加与更新业务,我们在前几篇文章已经讲解过好多次了,这次就大概讲解一下实现的过程,首先我们先编写添加与更新的接口,因为添加和更新的接口在于是否有id值,所有可以将这两个接口合并成为一个接口,如下:
添加和更新的页面展示,这里采用element的对话框来实现,如下:
<!-- 对话框组件:添加或更新已有的菜单的数据结构 -->
<el-dialog v-model="dislogVisible" :title="menuData.id ? '更新菜单' : '添加菜单'">
<!-- 表单组件:收集新增与已有的菜单的数据 -->
<el-form>
<el-form-item label="名称">
<el-input placeholder="请你输入菜单名称" v-model="menuData.name"></el-input>
</el-form-item>
<el-form-item label="权限">
<el-input placeholder="请你输入权限数值" v-model="menuData.code"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dislogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
设置完对话框样式之后,接下来就可以给添加和更新的按钮时候点击函数,如下:
// 添加菜单按钮的回调
const addPermisstion = (row: Permission) => {
// 清空数据
Object.assign(menuData, {
code: '',
level: 0,
name: '',
pid: 0,
})
dislogVisible.value = true // 对话框显示
// 收集新增菜单的level值
menuData.level = row.level + 1
// 给谁新增子菜单
menuData.pid = row.id as number
}
// 编辑已有的菜单
const updatePermisstion = (row: Permission) => {
dislogVisible.value = true // 对话框显示
// 点击修改按钮:收集已有的菜单的数据进行更新
Object.assign(menuData, row)
}
设置完电话函数之后,我们在对话框的确定按钮处,设置点击事件来调用相关的接口实现添加和修改效果,如下:
// 确定按钮的回调
const save = async () => {
let result: any = await reqAddOrUpdateMenu(menuData)
if (result.code == 200) {
dislogVisible.value = false // 对话框隐藏
// 提示消息
ElMessage({ type: 'success', message: menuData.id ? '更新成功' : '添加成功' })
// 再次获取全部数据
getHasPermisstion()
}
}
最终的结果如下:
实现菜单模块的删除业务
实现删除业务很简单,只需要调用相应的删除接口,然后将要删除的数据的id作为参数传递给接口函数即可,接口函数编写如下:
编写完接口函数之后,给删除按钮设置一个气泡确认框用来实现删除业务,如下:
给confirm事件设置函数,将row.id作为参数传递过去,如下:
// 删除按钮的回调
const removeMenu = async (id: number) => {
let result: any = await reqRemoveMenu(id)
if (result.code == 200) {
ElMessage({ type: 'success', message: '删除成功' })
// 再次获取数据
getHasPermisstion()
}
}
删除业务完成,最终呈现的效果如下:
本项目的角色管理和菜单管理页面功能的搭建就讲解到这,下一篇文章将继续讲解其它模块的主体内容,关注博主学习更多前端vue知识,您的支持就是博主创作的最大动力!