一、用户管理
1.静态搭建
src/views/acl/user/index.vue
<template>
<el-card style="height:80px;">
<el-form :inline="true" class="form">
<el-form-item label="用户名:">
<el-input placeholder="请你输入用户名"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default">搜索</el-button>
<el-button type="primary" size="default">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin:10px 0;">
<el-button type="primary" size="default">添加用户</el-button>
<el-button type="primary" size="default">批量删除</el-button>
<!-- table展示表单信息 -->
<el-table style="margin:10px 0;" border>
<el-table-column type="selection" align="center"></el-table-column>
<el-table-column label="#" align="center"></el-table-column>
<el-table-column label="ID" align="center"></el-table-column>
<el-table-column label="用户名字" align="center"></el-table-column>
<el-table-column label="用户名称" align="center"></el-table-column>
<el-table-column label="用户角色" align="center"></el-table-column>
<el-table-column label="创建时间" align="center"></el-table-column>
<el-table-column label="更新时间" align="center"></el-table-column>
<el-table-column label="操作" width="260px" align="center"></el-table-column>
</el-table>
<!-- 分页器 -->
<el-pagination
v-model:current-page="pageNo"
v-model:page-size="pageSize"
:page-sizes="[3,5,7,9]"
:background="true"
layout="prev, pager, next, jumper,->,size,total"
:total="400"/>
</el-card>
</template>
<script setup lang="ts">
import {ref} from 'vue';
// 展示当前页
let pageNo=ref<number>(1)
// 每页几个数据
let pageSize=ref<number>(5)
</script>
<style scoped>
.form{
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
2.业务实现流程
2.1展示已有账号的数据
1.一上来就发请求获取数据,点击不同的页码也要发请求
2.定义接口数据
3.获取数据
4.存储数据
5.展示数据(在table和分页器都要展示)
2.2添加与修改用户
1.静态搭建
抽屉结构》drawer
添加点击按钮和编辑按钮弹出抽屉事件
2.完成添加业务
(1)添加地址接口(判断携带参数有无id)和定义数据
(2)收集数据用v-model收集到定义好的userParams里面。(定义参数userParams收集用户信息的响应式数据)
(3)添加点击保存按钮回调save.(code=200判断添加/更新有无id,关闭抽屉并弹出相应的提示信息,再次获取最新数据getHasUser)
(4)添加取消按钮的回调cancel.
(5)每次点击确定或者取消都要清空数据:在addUser里面清空数据
3.表单校验功能
(1)校验username,name,password
(2)给抽屉的身体部分添加校验规则:rules="rules"(告诉表单数据数据收集:model="userParams"并给表单项添加prop)
(3)校验规则// 校验用户名字回调函数 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 }] }
(4)注意:务必要在点击addUaer时进行校验
// 点击保存按钮的时候,务必需要保证表单全部符合条件再去发请求 await formRef.value.validate()
(5)用nexttick和清空表单校验信息在addUser内
// 清除上一次的错误提示信息 nextTick(() => { formRef.value.clearValidate('username') formRef.value.clearValidate('name') formRef.value.clearValidate('password') })
(6)注意触发时机,trigger要用blur不用change(因为会清空数据自动发生变化,无法清空校验错误信息)
4.更新业务完成
(1)动态展示标题和是否有密码<template #header> <h4>{{ userParams.id ? '更新用户' : '添加用户' }}</h4> </template> <el-form-item label="用户密码" prop="password" v-if="!userParams.id">
(2)updateUser回调记得清空数据
// 更新已有的用户按钮的回调 // row:即为已有用户的账号信息 const updateUser = (row: User) => { // 抽屉显示出来 drawer.value = true // 存储收集已有的账号xinx Object.assign(userParams, row) // 清除上一次的错误提示信息 nextTick(() => { formRef.value.clearValidate('username') formRef.value.clearValidate('name') }) }
(3)判断添加留在第一页、更新停留在当前页。在save内:
再添加了window刷新之后,这个可要可不要
// 获取最新的全部账号信息 getHasUser(userParams.id ? pageNo.value : 1)
(4)解决修改到当前登录用户信息需要重新登录的问题,在save内:
// 浏览器自动刷新一次(为了解决修改到当前登录用户信息需要重新登录的问题) window.location.reload()
2.3 分配角色
1.静态搭建
》抽屉el-drwer》全选框el-checkbox》复选框el-checkbox-group
点击分配角色按钮出现抽屉结构@click="setRole(row)"
展示用户姓名username信息:存储数据》展示数据》禁用效果<!-- 抽屉结构:用于某个已有账号进行职位分配 --> <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.roleName }}</el-checkbox> </el-checkbox-group> </el-form-item> </el-form> </template> <template #footer> <div style="flex: auto"> <el-button @click="drawer1 = false">取消</el-button> <el-button type="primary" @click="confirmClick">确认</el-button> </div> </template> </el-drawer>
2.分配角色业务
(1)定义API接口和方法,返回数据的ts类型
(2)获取全部职位数据allRolesList和当前用户职位的数据assignRoles:setRole里
(3)存储数据:
(4)展示所有职位:通过v-for和role.roleName展示
(5)展示勾选当前用户角色:v-model="userRole"
(6)全选、全不选效果,不确定样式// 分配角色按钮的回调 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 checkAll = ref<boolean>(false) //控制顶部全选复选框不确定的样式 const isIndeterminate = ref<boolean>(true) //存储全部职位的数据 let allRole = ref<AllRole>([]) //当前用户已有的职位 let userRole = ref<AllRole>([]) //顶部的全部复选框的change事件 const handleCheckAllChange = (val: boolean) => { //val:true(全选)|false(没有全选) userRole.value = val ? allRole.value : [] //不确定的样式(确定样式) isIndeterminate.value = false } //顶部全部的复选框的change事件 const handleCheckedCitiesChange = (value: string[]) => { //顶部复选框的勾选数据 //代表:勾选上的项目个数与全部的职位个数相等,顶部的复选框勾选上 checkAll.value = value.length === allRole.value.length //不确定的样式 isIndeterminate.value = value.length !== allRole.value.length }
3.点击确定取消业务
(1)定义接口方法,ts类型
(2)取消按钮
<el-button @click="drawer1 = false">取消</el-button>
(3)确定按钮@click="confirmClick"
需要收集参数(当前用户id,收集到的职位id)》分配用户职位(判断,提示信息,获取更新完毕用户的信息,更新后留在当前页)// 确定按钮的回调(分配职位) 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) } }
2.4删除业务
(1)定义删除某一个账号和批量删除的API接口、方法,ts数据类型定义
(2)气泡确认框<el-popconfirm :title="`你确定要删除${row.username}?`" width="260px" @confirm="deleteUser(row.id)"> <template #reference> <el-button type="primary" size="small" icon="Delete">删除</el-button> </template> </el-popconfirm>
(3)删除某一个用户
绑定事件@confirm="deleteUser(row.id)"(要根据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) } }
(4)批量删除
在table身上绑定@selection-change="selectChange"》选中和存储批量删除的用户Id
给批量删除按钮判断是否需要禁用效果,:disabled="selectIdArr.length?false:true"并绑定点击事件deleteSelectUser//准备一个数组存储批量删除的用户的ID let selectIdArr = ref<User[]>([]) //table复选框勾选的时候会触发的事件 const selectChange = (value: any) => { selectIdArr.value = value } // 批量删除按钮的回调 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) } }
2.5搜索业务
(1)收集用户输入进来的关键字
<el-input placeholder="请输入用户名称" v-model="keyword"></el-input> //定义响应式数据:收集用户输入进来的关键字 let keyword = ref<string>('')
(2)搜索按钮添加禁用效果
:disabled="keyword?false:true"
(3)搜索按钮绑定事件search
要注意的是,要给获取用户账号信息的接口添加一个username字段,才可以搜索// 获取用户账号信息的接口 export const reqUserInfo = (page: number, limit: number, username: string) => request.get<any, UserResponseData>(API.ALLUSER_URL + `${page}/${limit}/?username=${username}`)
// 搜索按钮的回调 const search = () => { //根据关键字获取相应的用户数据 getHasUser() //清空关键字 keyword.value = '' }
(4)重置按钮reset
要引入之前的仓库,来刷新import useLayOutSettingStore from '@/store/modules/setting' //获取模板setting仓库 let settingStore = useLayOutSettingStore()
//重置按钮 const reset = () => { settingStore.refresh = !settingStore.refresh }
3.代码
src/api/acl/user/index.ts
// 用户管理模块的接口
import request from '@/utils/request'
import type {
AllRoleResponseData,
SetRoleData,
User,
UserResponseData
} from './type'
enum API {
// 获取全部已有用户账号信息
ALLUSER_URL = '/admin/acl/user/',
// 添加一个新的用户账号
ADDUSER_URL = '/admin/acl/user/save',
// 更新已有的用户账号
UPDATEUSER_URL = '/admin/acl/user/update',
//获取全部职位,当前账号拥有的职位接口
ALLROLEURL = '/admin/acl/user/toAssign/',
//给已有的用户分配角色接口
SETROLE_URL = '/admin/acl/user/doAssignRole',
//删除某一个账号
DELETEUSER_URL = '/admin/acl/user/remove/',
//批量删除的接口
DELETEALLUSER_URL = '/admin/acl/user/batchRemove',
}
// 获取用户账号信息的接口
export const reqUserInfo = (page: number, limit: number, username: string) => request.get<any, UserResponseData>(API.ALLUSER_URL + `${page}/${limit}/?username=${username}`)
// 添加用户与更新已有用户的接口
export const reqAddOrUpdateUser = (data: User) => {
// 携带参数有ID更新
if (data.id) {
return request.put<any, any>(API.UPDATEUSER_URL, data)
} else {
return request.post<any, any>(API.ADDUSER_URL, data)
}
}//获取全部职位以及包含当前用户的已有的职位
export const reqAllRole = (userId: number) =>
request.get<any, AllRoleResponseData>(API.ALLROLEURL + userId)
//分配职位
export const reqSetUserRole = (data: SetRoleData) =>
request.post<any, any>(API.SETROLE_URL, data)
//删除某一个账号的信息
export const reqRemoveUser = (userId: number) =>
request.delete<any, any>(API.DELETEUSER_URL + userId)
//批量删除的接口
export const reqSelectUser = (idList: number[]) =>
request.delete(API.DELETEALLUSER_URL, { data: idList })
src/api/acl/user/type.ts
//账号信息的ts类型
export interface ResponseData {
code: number
message: string
ok: boolean
}
//代表一个账号信息的ts类型
export interface User {
id?: number
createTime?: string
updateTime?: string
username?: string
password?: string
name?: string
phone?: null
roleName?: string
}
//数组包含全部的用户信息
export type Records = User[]
//获取全部用户信息接口返回的数据ts类型
export interface UserResponseData extends ResponseData {
data: {
records: Records
total: number
size: number
current: number
pages: number
}
}
//代表一个职位的ts类型
export interface RoleData {
id?: number
createTime?: string
updateTime?: string
roleName: string
remark: null
}
//全部职位的列表
export type AllRole = RoleData[]
//获取全部职位的接口返回的数据ts类型
export interface AllRoleResponseData extends ResponseData {
data: {
assignRoles: AllRole
allRolesList: AllRole
}
}
//给用户分配职位接口携带参数的ts类型
export interface SetRoleData {
roleIdList: number[]
userId: number
}
src/views/acl/user/index.vue
<template>
<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" @click="search">搜索</el-button>
<el-button type="primary" plain @click="reset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin: 10px 0;">
<el-button type="primary" size="default" @click="addUser">添加用户</el-button>
<el-button type="danger" size="default" @click="deleteSelectUser">批量删除</el-button>
<!-- table展示用户信息 -->
<el-table @selection-change="selectChange" style="margin: 10px 0;" border :data="userArr">
<el-table-column type="selection" align="center"></el-table-column>
<el-table-column label="#" align="center" type="index"></el-table-column>
<el-table-column label="id" prop="id"></el-table-column>
<el-table-column label="用户名字" prop="username" show-overflow-tooltip></el-table-column>
<el-table-column label="用户名称" prop="name" show-overflow-tooltip></el-table-column>
<el-table-column label="用户角色" prop="roleName" show-overflow-tooltip></el-table-column>
<el-table-column label="创建时间" prop="createTime" show-overflow-tooltip></el-table-column>
<el-table-column label="更新时间" prop="updateTime" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="300px" align="center">
<template #="{ row, $index }">
<el-button type="primary" size="small" icon="User" @click="setRole(row)">分配角色</el-button>
<el-button type="primary" size="small" icon="Edit" @click="updateUser(row)">编辑</el-button>
<el-popconfirm :title="`你确定要删除${row.username}?`" width="260px" @confirm="deleteUser(row.id)">
<template #reference>
<el-button type="primary" size="small" icon="Delete">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="pageNo" v-model:page-size="pageSize" :page-sizes="[5, 7, 9, 11]"
:background="true" layout="prev, pager, next, jumper,->,sizes,total" :total="total" @current-change="getHasUser"
@size-change="handler" />
</el-card>
<!-- 抽屉结构:完成添加新的用户账号更新已有的账号信息 -->
<el-drawer v-model="drawer">
<template #header>
<h4>{{ userParams.id ? '更新用户' : '添加用户' }}</h4>
</template>
<template #default>
<el-form :model="userParams" :rules="rules" ref="formRef">
<el-form-item label="用户姓名" prop="username">
<el-input placeholder="请您输入用户姓名" v-model="userParams.username"></el-input>
</el-form-item>
<el-form-item label="用户昵称" prop="name">
<el-input placeholder="请您输入用户昵称" v-model="userParams.name"></el-input>
</el-form-item>
<el-form-item label="用户密码" prop="password" v-if="!userParams.id">
<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>
<!-- 抽屉结构:用于某个已有账号进行职位分配 -->
<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.roleName }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="drawer1 = false">取消</el-button>
<el-button type="primary" @click="confirmClick">确认</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import useLayOutSettingStore from '@/store/modules/setting'
import { reqAddOrUpdateUser, reqAllRole, reqRemoveUser, reqSelectUser, reqSetUserRole, reqUserInfo } from '@/api/acl/user';
import type { AllRole, AllRoleResponseData, Records, SetRoleData, User, UserResponseData } from '@/api/acl/user/type'
import { ElMessage } from 'element-plus';
import { nextTick, onMounted, reactive, ref } from 'vue';
// 默认页码
let pageNo = ref<number>(1)
// 一页展示几条数据
let pageSize = ref<number>(10)
// 用户总个数
let total = ref<number>(0)
// 存储全部用户的数组
let userArr = ref<Records>([])
// 定义响应式数据控制抽屉的显示与隐藏
let drawer = ref<boolean>(false)
// 控制分配角色抽屉显示与隐藏
let drawer1 = ref<boolean>(false)
// 收集用户信息的响应式数据
let userParams = reactive<User>({
username: '',
name: '',
password: ''
})
// 获取dorm组件实例
let formRef = ref()
//定义响应式数据:收集用户输入进来的关键字
let keyword = ref<string>('')
//获取模板setting仓库
let settingStore = useLayOutSettingStore()
// 组件挂载完毕
onMounted(() => {
getHasUser()
})
// 获取全部已有用户信息
const getHasUser = async (pager = 1) => {
// 收集当前页码
pageNo.value = pager
let result: UserResponseData = await reqUserInfo(pageNo.value, pageSize.value, keyword.value)
if (result.code === 200) {
total.value = result.data.total
userArr.value = result.data.records
}
}
// 分页器下拉菜单的自定义事件的回调
const handler = () => {
getHasUser()
}
// 添加用户按钮的回调
const addUser = () => {
// 抽屉显示出来
drawer.value = true
// 清空数据
Object.assign(userParams, {
id: 0,
username: '',
name: '',
password: ''
})
// 清除上一次的错误提示信息
nextTick(() => {
formRef.value.clearValidate('username')
formRef.value.clearValidate('name')
formRef.value.clearValidate('password')
})
}
// 更新已有的用户按钮的回调
// row:即为已有用户的账号信息
const updateUser = (row: User) => {
// 抽屉显示出来
drawer.value = true
// 存储收集已有的账号xinx
Object.assign(userParams, row)
// 清除上一次的错误提示信息
nextTick(() => {
formRef.value.clearValidate('username')
formRef.value.clearValidate('name')
})
}
// 保存按钮的回调
const save = async () => {
// 点击保存按钮的时候,务必需要保证表单全部符合条件再去发请求
await formRef.value.validate()
// 保存按钮:添加新的用户|更新已有的用户账号信息
let result: any = await reqAddOrUpdateUser(userParams)
if (result.code === 200) {
// 关闭抽屉
drawer.value = false
// 提示信息
ElMessage({
type: 'success',
message: userParams.id ? '更新成功' : '添加成功'
})
// 获取最新的全部账号信息
// getHasUser(userParams.id ? pageNo.value : 1)
// 浏览器自动刷新一次(为了解决修改到当前登录用户信息需要重新登录的问题)
window.location.reload()
} else {
// 关闭抽屉
drawer.value = false
// 提示信息
ElMessage({
type: 'error',
message: userParams.id ? '更新失败' : '添加失败'
})
}
}
// 取消按钮的回调
const cancel = () => {
// 关闭抽屉
drawer.value = 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 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 checkAll = ref<boolean>(false)
//控制顶部全选复选框不确定的样式
const isIndeterminate = ref<boolean>(true)
//存储全部职位的数据
let allRole = ref<AllRole>([])
//当前用户已有的职位
let userRole = ref<AllRole>([])
//顶部的全部复选框的change事件
const handleCheckAllChange = (val: boolean) => {
//val:true(全选)|false(没有全选)
userRole.value = val ? allRole.value : []
//不确定的样式(确定样式)
isIndeterminate.value = false
}
//顶部全部的复选框的change事件
const handleCheckedCitiesChange = (value: string[]) => {
//顶部复选框的勾选数据
//代表:勾选上的项目个数与全部的职位个数相等,顶部的复选框勾选上
checkAll.value = value.length === allRole.value.length
//不确定的样式
isIndeterminate.value = value.length !== allRole.value.length
}
// 确定按钮的回调(分配职位)
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)
}
}
// 删除某一个用户
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)
}
}
//准备一个数组存储批量删除的用户的ID
let selectIdArr = ref<User[]>([])
//table复选框勾选的时候会触发的事件
const selectChange = (value: any) => {
selectIdArr.value = value
}
// 批量删除按钮的回调
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)
}
}
// 搜索按钮的回调
const search = () => {
//根据关键字获取相应的用户数据
getHasUser()
//清空关键字
keyword.value = ''
}
//重置按钮
const reset = () => {
settingStore.refresh = !settingStore.refresh
}
</script>
<style scoped>
.form {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
二、角色管理
1.静态搭建
静态搭建这一部分和用户管理的极为相似这里就不细写。
2.业务实现流程
2.1数据的展示
2.1角色管理模块数据的展示
(1)定义API接口,方法,数据类型
(2)挂载时和切换页码时发送请求展示已有数据,引入onMounted和请求方法
(3)获取数据,存储全部已有的职位
(4)展示数据
分页器
:total="total" @current-change="getHasRole" @size-change="sizeChange",
table用:data="roleArr"
其他的用prop展示// 组件挂载完毕 onMounted(() => { // 获取角色请求 getHasRole() }) // 获取全部已有的角色信息的方法|分页器当前页码发生变化的回调 const getHasRole = async (pager = 1) => { // 修改当前页码 pageNo.value = pager let result: RoleResponseData = await reqRoleInfo( pageNo.value, pageSize.value, keyword.value, ) if (result.code === 200) { total.value = result.data.total roleArr.value = result.data.records } } // 分页器下拉菜单的自定义事件的回调 const sizeChange = () => { getHasRole() }
(5)搜索重置功能
先收集到输入的数据keyword,搜索按钮禁用效果,绑定search方法和重置reset方法// 搜索按钮的回调 const search = () => { //根据关键字获取相应的角色数据 getHasRole() //清空关键字 keyword.value = '' } //获取模板setting仓库 let settingStore = useLayOutSettingStore() // 重置按钮的回调 const reset = () => { settingStore.refresh = !settingStore.refresh }
2.2添加和更新角色
1.静态搭建
(1)添加新增角色更新角色的 地址和方法(两者可以封装到一个函数方法里面,唯一的区别就是有无id,新增没有id,更新有id)
(2)dialog对话框
(3)控制对话框显示与隐藏。给添加和编辑按钮添加事件addRole,updateRole(row)
(4)取消按钮 @click="dialogVisible = false"<!-- 添加角色与更新已有角色的结构:对话框 --> <el-dialog :title="roleParams.id ? '更新' : '添加'" v-model="dialogVisible"> <el-form :model="roleParams" :rules="rules" ref="form"> <el-form-item label="角色名称" prop="roleName"> <el-input placeholder="请填写角色名称" v-model="roleParams.roleName"></el-input> </el-form-item> </el-form> <template #footer> <el-button size="default" @click="dialogVisible = false">取消</el-button> <el-button type="primary" size="default" @click="save">确认</el-button> </template> </el-dialog>
2.完成添加职位
收集到新增的职位数据》表单校验》判断》再次获取全部已有数据》清空数据》清空错误提示》将对话框隐藏(记得更新返回的是当前页还是添加上一页)
(1)收集新增职位数据// 收集新增岗位数据 let roleParams = reactive<RoleData>({ roleName: '', })
(2)自定义表单校验
// 自定义校验规则的回调 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 }], }
(3)确定按钮save的回调
// 确定按钮的回调 const save = async () => { // 表单校验结果,结果通过再发请求,结果没通过不应该发请求 await form.value.validate() // 添加职位|更新角色的请求 let result: any = await reqAddOrUpdateRole(roleParams) if (result.code === 200) { // 提示信息 ElMessage({ type: 'success', message: roleParams.id ? '添加成功' : '更新成功', }) // 再次获取全部已有角色 getHasRole(roleArr.value.length > 1 ? pageNo.value : pageNo.value - 1) } else { // 提示信息 ElMessage({ type: 'error', message: roleParams.id ? '添加失败' : '更新失败', }) } // 对话框隐藏 dialogVisible.value = false }
(4)添加按钮的回调// 添加角色按钮的回调 const addRole = () => { // 对话框显示出来 dialogVisible.value = true // 清空数据 Object.assign(roleParams, { roleName: '', id: 0, }) // 清空上一次表单校验错误提示 nextTick(() => { form.value.clearValidate('roleName') }) }
3.更新(修改)按钮完成
updateRole回调
// 更新角色按钮的回调 const updateRole = (row: RoleData) => { // 对话框显示出来 dialogVisible.value = true // 存储已有角色---带有id的 Object.assign(roleParams, row) // 清空上一次表单校验错误提示 nextTick(() => { form.value.clearValidate('roleName') }) }
2.3分配权限
(1)静态搭建:抽屉组件drawer》tree树形控件
<!-- 抽屉组件:分配角色的菜单权限与按钮权限 --> <el-drawer v-model="drawer"> <template #header> <h4>分配菜单与按钮的权限</h4> </template> <template #default> <el-tree ref="tree" :data="menuArr" show-checkbox node-key="id" default-expand-all :default-checked-keys="selectArr" :props="defaultProps" /> </template> <template #footer> <div style="flex: auto"> <el-button @click="drawer = false">取消</el-button> <el-button type="primary" @click="handler">确认</el-button> </div> </template> </el-drawer>
(2)书写API接口和方法,定义数据类型
(3)分配权限按钮的回调setPermission//分配权限按钮的回调 //已有的职位的数据 const setPermission = 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', }
(4)展示菜单权限数据和按钮数据::data="menuArr"
(5)勾选用户已有的权限
准备数组存储四级select为true的id;//准备一个数组:数组用于存储勾选的节点的ID(四级的) let selectArr = ref<number[]>([])
过滤四级的id.(递归)
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 }
(6)抽屉确定按钮的回调
携带职位的id,选中节点的id,半选的id》下发权限》判断》页面刷新window
//抽屉确定按钮的回调 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 reqSetPermission(roleId, permissionId) if (result.code === 200) { //抽屉关闭 drawer.value = false //提示信息 ElMessage({ type: 'success', message: '分配权限成功', }) //页面刷新 window.location.reload() } }
2.3删除业务
(1)编写API方法,定义数据类型
(2)气泡确认框<el-popconfirm :title="`你确定要删除${row.roleName}?`" width="260px" @confirm="removeRole(row.id)"> <template #reference> <el-button type="primary" size="small" icon="Delete"> 删除 </el-button> </template> </el-popconfirm>
(3)删除的回调 removeRole
//删除已有的职位 const removeRole = async (id: number) => { let result: any = await reqRemoveRole(id) if (result.code === 200) { ElMessage({ type: 'success', message: '删除成功', }) getHasRole(roleArr.value.length > 1 ? pageNo.value : pageNo.value - 1) } }
3.代码
src/api/acl/role/index.ts
// 角色管理模块的接口
import request from '@/utils/request'
import type { RoleResponseData, RoleData, MenuResponseData } from './type'
// 枚举地址
enum API {
// 获取全部角色的接口
ALLROLE_URL = '/admin/acl/role/',
// 新增角色的接口地址
ADDROLE_URL = '/admin/acl/role/save',
// 更新已有的角色
UPDATEROLE_URL = '/admin/acl/role/update',
//获取全部的菜单与按钮的数据
ALLPERMISSION = '/admin/acl/permission/toAssign/',
//给相应的职位分配权限
SETPERMISTION_URL = '/admin/acl/permission/doAssign/?',
//删除已有的职位
REMOVEROLE_URL = '/admin/acl/role/remove/',
}
// 获取全部的角色
export const reqRoleInfo = (page: number, limit: number, roleName: string) =>
request.get<any, RoleResponseData>(
API.ALLROLE_URL + `${page}/${limit}/?roleName=${roleName}`,
)
// 添加与更新角色接口
export const reqAddOrUpdateRole = (data: RoleData) => {
if (data.id) {
return request.put<any, any>(API.UPDATEROLE_URL, data)
} else {
return request.post<any, any>(API.ADDROLE_URL, data)
}
}
//获取全部菜单与按钮权限数据
export const reqAllMenuList = (roleId: number) =>
request.get<any, MenuResponseData>(API.ALLPERMISSION + roleId)
//给相应的职位下发权限
export const reqSetPermission = (roleId: number, permissionId: number[]) =>
request.post(
API.SETPERMISTION_URL + `roleId=${roleId}&permissionId=${permissionId}`,
)
//删除已有的职位
export const reqRemoveRole = (roleId: number) =>
request.delete<any, any>(API.REMOVEROLE_URL + roleId)
src/api/acl/role/type.ts
export interface ResponseData {
code: number
message: string
ok: boolean
}
//职位数据类型
export interface RoleData {
id?: number
createTime?: string
updateTime?: string
roleName: string
remark?: null
}
//全部职位的数组的ts类型
export type Records = RoleData[]
//全部职位数据的相应的ts类型
export interface RoleResponseData extends ResponseData {
data: {
records: Records
total: number
size: number
current: number
orders: []
optimizeCountSql: boolean
hitCount: boolean
countId: null
maxLimit: null
searchCount: boolean
pages: number
}
}
//菜单与按钮数据的ts类型
export interface MunuData {
id: number
createTime: string
updateTime: string
pid: number
name: string
code: string
toCode: string
type: number
status: null
level: number
children?: MenuList
select: boolean
}
export type MenuList = MunuData[]
//菜单权限与按钮权限数据的ts类型
export interface MenuResponseData extends ResponseData {
data: MenuList
}
src/views/acl/role/index.vue
PS:filterSelectArr方法只需要过滤最深一个层级的id(即第四级),因为只要判断最深一级是否有勾选,如果有一个或多个勾选了,则它相关的所有上级必定是勾选状态;如果一个也没勾选,则它相关的所有上级必定也是未勾选状态。
<template>
<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" @click="search">搜索</el-button>
<el-button type="primary" plain="primary" @click="reset">
重置
</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin: 10px 0">
<el-button type="primary" size="default" icon="Plus" @click="addRole">
添加角色
</el-button>
<el-table border style="margin: 10px 0" :data="roleArr">
<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="300px" align="center">
<template #="{ row, $index }">
<el-button
type="primary"
size="small"
icon="User"
@click="setPermission(row)"
>
分配权限
</el-button>
<el-button
type="primary"
size="small"
icon="Edit"
@click="updateRole(row)"
>
编辑
</el-button>
<el-popconfirm
:title="`你确定要删除${row.roleName}?`"
width="260px"
@confirm="removeRole(row.id)"
>
<template #reference>
<el-button type="primary" size="small" icon="Delete">
删除
</el-button>
</template>
</el-popconfirm>
</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>
<!-- 添加角色与更新已有角色的结构:对话框 -->
<el-dialog :title="roleParams.id ? '更新' : '添加'" v-model="dialogVisible">
<el-form :model="roleParams" :rules="rules" ref="form">
<el-form-item label="角色名称" prop="roleName">
<el-input
placeholder="请填写角色名称"
v-model="roleParams.roleName"
></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button size="default" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" size="default" @click="save">确认</el-button>
</template>
</el-dialog>
<!-- 抽屉组件:分配角色的菜单权限与按钮权限 -->
<el-drawer v-model="drawer">
<template #header>
<h4>分配菜单与按钮的权限</h4>
</template>
<template #default>
<el-tree
ref="tree"
:data="menuArr"
show-checkbox
node-key="id"
default-expand-all
:default-checked-keys="selectArr"
:props="defaultProps"
/>
</template>
<template #footer>
<div style="flex: auto">
<el-button @click="drawer = false">取消</el-button>
<el-button type="primary" @click="handler">确认</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import useLayOutSettingStore from '@/store/modules/setting'
import {
reqAddOrUpdateRole,
reqAllMenuList,
reqRemoveRole,
reqRoleInfo,
reqSetPermission,
} from '@/api/acl/role'
import type {
RoleResponseData,
Records,
RoleData,
MenuResponseData,
MenuList,
} from '@/api/acl/role/type'
import { nextTick, onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
// 当前页码
let pageNo = ref<number>(1)
// 一夜展示几条数据
let pageSize = ref<number>(10)
// 角色总个数
let total = ref<number>(0)
// 存储全部角色的数组
let roleArr = ref<Records>([])
// 搜索角色关键字
let keyword = ref<string>('')
// 控制对话框显示与隐藏
let dialogVisible = ref<boolean>(false)
// 收集新增岗位数据
let roleParams = reactive<RoleData>({
roleName: '',
})
// 控制抽屉显示与隐藏
let drawer = ref<boolean>(false)
// 获取form组件实例
let form = ref<any>()
//定义数组存储用户权限的数据
let menuArr = ref<MenuList>([])
//准备一个数组:数组用于存储勾选的节点的ID(四级的)
let selectArr = ref<number[]>([])
//获取tree组件实例
let tree = ref<any>()
// 组件挂载完毕
onMounted(() => {
// 获取角色请求
getHasRole()
})
// 获取全部已有的角色信息的方法|分页器当前页码发生变化的回调
const getHasRole = async (pager = 1) => {
// 修改当前页码
pageNo.value = pager
let result: RoleResponseData = await reqRoleInfo(
pageNo.value,
pageSize.value,
keyword.value,
)
if (result.code === 200) {
total.value = result.data.total
roleArr.value = result.data.records
}
}
// 分页器下拉菜单的自定义事件的回调
const sizeChange = () => {
getHasRole()
}
// 搜索按钮的回调
const search = () => {
//根据关键字获取相应的角色数据
getHasRole()
//清空关键字
keyword.value = ''
}
//获取模板setting仓库
let settingStore = useLayOutSettingStore()
// 重置按钮的回调
const reset = () => {
settingStore.refresh = !settingStore.refresh
}
// 添加角色按钮的回调
const addRole = () => {
// 对话框显示出来
dialogVisible.value = true
// 清空数据
Object.assign(roleParams, {
roleName: '',
id: 0,
})
// 清空上一次表单校验错误提示
nextTick(() => {
form.value.clearValidate('roleName')
})
}
// 更新角色按钮的回调
const updateRole = (row: RoleData) => {
// 对话框显示出来
dialogVisible.value = true
// 存储已有角色---带有id的
Object.assign(roleParams, row)
// 清空上一次表单校验错误提示
nextTick(() => {
form.value.clearValidate('roleName')
})
}
// 自定义校验规则的回调
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 }],
}
// 确定按钮的回调
const save = async () => {
// 表单校验结果,结果通过再发请求,结果没通过不应该发请求
await form.value.validate()
// 添加职位|更新角色的请求
let result: any = await reqAddOrUpdateRole(roleParams)
if (result.code === 200) {
// 提示信息
ElMessage({
type: 'success',
message: roleParams.id ? '添加成功' : '更新成功',
})
// 再次获取全部已有角色
getHasRole(roleArr.value.length > 1 ? pageNo.value : pageNo.value - 1)
} else {
// 提示信息
ElMessage({
type: 'error',
message: roleParams.id ? '添加失败' : '更新失败',
})
}
// 对话框隐藏
dialogVisible.value = false
}
//分配权限按钮的回调
//已有的职位的数据
const setPermission = 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 reqSetPermission(roleId, permissionId)
if (result.code === 200) {
//抽屉关闭
drawer.value = false
//提示信息
ElMessage({
type: 'success',
message: '分配权限成功',
})
//页面刷新
window.location.reload()
}
}
//删除已有的职位
const removeRole = async (id: number) => {
let result: any = await reqRemoveRole(id)
if (result.code === 200) {
ElMessage({
type: 'success',
message: '删除成功',
})
getHasRole(roleArr.value.length > 1 ? pageNo.value : pageNo.value - 1)
}
}
</script>
<style scoped>
.form {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
三、菜单管理
1.展示已有菜单数据
展示已有菜单数据
(1)编写API接口,方法,定义数据类型
(2)静态搭建:table组件<el-table :data="permisstinArr" 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="操作"> <!-- row:即为已有菜单对象|按钮对象的数据 --> <template #="{ row, $index }"> <el-button @click="addPermission(row)" type="primary" size="small" :disabled="row.level === 4"> {{ row.level === 3 ? '添加功能' : '添加菜单' }} </el-button> <el-button @click="updatePermission(row)" type="primary" size="small" :disabled="row.level === 1"> 编辑 </el-button> <el-popconfirm :title="`你确定要删除${row.name}?`" width="260px" @confirm="removeMenu(row.id)"> <template #reference> <el-button type="primary" size="small" :disabled="row.level === 1"> 删除 </el-button> </template> </el-popconfirm> </template> </el-table-column> </el-table>
(3)组件挂载完毕获取菜单数据
(4)获取菜单数据
获取》存储》判断// 组件挂载完毕 onMounted(() => { getHasPermission() }) // 获取菜单数据的方法 const getHasPermission = async () => { let result: PermissionResponseData = await reqAllPermission() if (result.code === 200) { permisstinArr.value = result.data } }
(5)展示数据
el-table中:data="permisstinArr"
el-table-column中:prop
(6)判断按钮是否禁用
一级禁用编辑和删除按钮
四级禁用添加菜单按钮
2.添加与更新菜单
(1)静态搭建
el-dialog》<!-- 对话框组件:添加或者更新已有的菜单的数据结构 --> <el-dialog v-model="dialogVisible" :title="menuData.id ? '更新菜单' : '添加菜单'" width="30%"> <!-- 表单组件:收集新增与已有菜单的数据 --> <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="dialogVisible = false">取消</el-button> <el-button type="primary" @click="save">确认</el-button> </span> </template> </el-dialog>
(2)控制对话框的显示与隐藏
// 控制对话框的显示与隐藏
let dialogVisible = ref<boolean>(false)
(3)添加菜单按钮的回调addPermission(row)// 添加菜单按钮的回调 const addPermission = (row: Permission) => { // 清空数据 Object.assign(menuData, { id: 0, code: '', level: 0, name: '', pid: 0, }) // 对话框显示出来 dialogVisible.value = true // 收集新增菜单的level数值 menuData.level = row.level + 1 // 给谁新增子菜单 menuData.pid = row.id as number }
(4)编辑菜单按钮的回调updatePermission(row)
// 编辑已有菜单 const updatePermission = (row: Permission) => { // 对话框显示出来 dialogVisible.value = true //点击修改按钮:收集已有的菜单的数据进行更新 Object.assign(menuData, row) }
(5)确定按钮的回调save
// 确认按钮的回调 const save = async () => { let result: any = await reqAddOrUpdateMenu(menuData) if (result.code === 200) { //对话框隐藏 dialogVisible.value = false // 信息提示 ElMessage({ type: 'success', message: menuData.id ? '更新成功' : '添加成功', }) //再次获取全部最新的菜单的数据 getHasPermission() } }
(6)取消按钮
@click="dialogVisible = false"
(7)记得定义API接口、方法以及数据类型
3.删除已有菜单
(1)编写API接口、方法,定义数据类型
(2)删除按钮的回调。
静态》el-popconfirm<el-popconfirm :title="`你确定要删除${row.name}?`" width="260px" @confirm="removeMenu(row.id)"> <template #reference> <el-button type="primary" size="small" :disabled="row.level === 1"> 删除 </el-button> </template> </el-popconfirm>
发送请求》再次更新数据
// 删除按钮的回调 const removeMenu = async (id: number) => { let result: any = await reqRemoveMenu(id) if (result.code === 200) { ElMessage({ type: 'success', message: '删除成功', }) getHasPermission() } }
4.代码
src/api/acl/menu/index.ts
import request from '@/utils/request'
import type { PermissionResponseData, MenuParams } from './type'
//枚举地址
enum API {
//获取全部菜单与按钮的标识数据
ALLPERMISSION_URL = '/admin/acl/permission',
//给某一级菜单新增一个子菜单
ADDMENU_URL = '/admin/acl/permission/save',
//更新某一个已有的菜单
UPDATE_URL = '/admin/acl/permission/update',
//删除已有的菜单
DELETEMENU_URL = '/admin/acl/permission/remove/',
}
//获取菜单数据
export const reqAllPermission = () =>
request.get<any, PermissionResponseData>(API.ALLPERMISSION_URL)
//添加与更新菜单的方法
export const reqAddOrUpdateMenu = (data: MenuParams) => {
if (data.id) {
return request.put<any, any>(API.UPDATE_URL, data)
} else {
return request.post<any, any>(API.ADDMENU_URL, data)
}
}
//删除某一个已有的菜单
export const reqRemoveMenu = (id: number) =>
request.delete<any, any>(API.DELETEMENU_URL + id)
src/api/acl/menu/type.ts
//数据类型定义
export interface ResponseData {
code: number
message: string
ok: boolean
}
//菜单数据与按钮数据的ts类型
export interface Permission {
id?: number
createTime: string
updateTime: string
pid: number
name: string
code: null
toCode: null
type: number
status: null
level: number
children?: PermissionList
select: boolean
}
export type PermissionList = Permission[]
//菜单接口返回的数据类型
export interface PermissionResponseData extends ResponseData {
data: PermissionList
}
//添加与修改菜单携带的参数的ts类型
export interface MenuParams {
id?: number //ID
code: string //权限数值
level: number //几级菜单
name: string //菜单的名字
pid: number //菜单的ID
}
src/views/acl/permission/index.vue
<template>
<el-table
:data="permisstinArr"
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="操作">
<!-- row:即为已有菜单对象|按钮对象的数据 -->
<template #="{ row, $inde }">
<el-button
@click="addPermission(row)"
type="primary"
size="small"
:disabled="row.level === 4"
>
{{ row.level === 3 ? '添加功能' : '添加菜单' }}
</el-button>
<el-button
@click="updatePermission(row)"
type="primary"
size="small"
:disabled="row.level === 1"
>
编辑
</el-button>
<el-popconfirm
:title="`你确定要删除${row.name}?`"
width="260px"
@confirm="removeMenu(row.id)"
>
<template #reference>
<el-button type="primary" size="small" :disabled="row.level === 1">
删除
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- 对话框组件:添加或者更新已有的菜单的数据结构 -->
<el-dialog
v-model="dialogVisible"
:title="menuData.id ? '更新菜单' : '添加菜单'"
width="30%"
>
<!-- 表单组件:收集新增与已有菜单的数据 -->
<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="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import {
reqAddOrUpdateMenu,
reqAllPermission,
reqRemoveMenu,
} from '@/api/acl/menu'
import {
MenuParams,
Permission,
PermissionList,
PermissionResponseData,
} from '@/api/acl/menu/type'
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue'
let permisstinArr = ref<PermissionList>([])
// 控制对话框的显示与隐藏
let dialogVisible = ref<boolean>(false)
// 携带的参数
let menuData = reactive<MenuParams>({
code: '',
level: 0,
name: '',
pid: 0,
})
// 组件挂载完毕
onMounted(() => {
getHasPermission()
})
// 获取菜单数据的方法
const getHasPermission = async () => {
let result: PermissionResponseData = await reqAllPermission()
if (result.code === 200) {
permisstinArr.value = result.data
}
}
// 添加菜单按钮的回调
const addPermission = (row: Permission) => {
// 清空数据
Object.assign(menuData, {
id: 0,
code: '',
level: 0,
name: '',
pid: 0,
})
// 对话框显示出来
dialogVisible.value = true
// 收集新增菜单的level数值
menuData.level = row.level + 1
// 给谁新增子菜单
menuData.pid = row.id as number
}
// 编辑已有菜单
const updatePermission = (row: Permission) => {
// 对话框显示出来
dialogVisible.value = true
//点击修改按钮:收集已有的菜单的数据进行更新
Object.assign(menuData, row)
}
// 确认按钮的回调
const save = async () => {
let result: any = await reqAddOrUpdateMenu(menuData)
if (result.code === 200) {
//对话框隐藏
dialogVisible.value = false
// 信息提示
ElMessage({
type: 'success',
message: menuData.id ? '更新成功' : '添加成功',
})
//再次获取全部最新的菜单的数据
getHasPermission()
}
}
// 删除按钮的回调
const removeMenu = async (id: number) => {
let result: any = await reqRemoveMenu(id)
if (result.code === 200) {
ElMessage({
type: 'success',
message: '删除成功',
})
getHasPermission()
}
}
</script>
<style scoped></style>
四、首页
src/views/home/index.vue
<template>
<el-card>
<div class="box">
<img :src="userStore.avatar" class="avatar">
<div class="info">
<h3>{{ getTime() }}好!{{ userStore.username }}</h3>
<p>椰果甄选运营平台</p>
</div>
</div>
</el-card>
<div class="bottom">
<svg-icon name="welcome" width="600px" height="300px"></svg-icon>
</div>
</template>
<script setup lang="ts">
import { getTime } from '@/utils/time'
// 引入用户相关的仓库,获取当前用户的头像、昵称
import useUserStore from '@/store/modules/user'
// 获取存储用户信息的仓库对象
let userStore = useUserStore()
</script>
<style scoped lang="scss">
.box {
display: flex;
align-items: center;
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
}
.info {
margin-left: 20px;
h3 {
font-size: 28px;
font-weight: 500;
}
p {
margin-top: 20px;
color: #a49a9a;
}
}
}
.bottom {
display: flex;
justify-content: center;
margin-top: 100px;
}
</style>
五、暗黑模式和主题颜色的切换
暗黑模式使用可参考:暗黑模式 | Element Plus (element-plus.org)
主题颜色使用可参考:主题 | Element Plus (element-plus.org)
暗黑模式需要在main.ts文件中引入所需的样式
import 'element-plus/theme-chalk/dark/css-vars.css'
src/layout/tabbar/setting/index.vue
<el-popover placement="bottom" title="主题设置" :width="300" trigger="hover">
<!-- 表单元素 -->
<el-form>
<el-form-item label="主题颜色">
<el-color-picker @click.stop append-to-body @change="setColor" v-model="color" size="small" show-alpha
:predefine="predefineColors" :teleported="false" />
</el-form-item>
<el-form-item label="暗黑模式">
<el-switch @change="changeDark" v-model="dark" class="mt-2" style="margin-left: 24px" inline-prompt
active-icon="Moon" inactive-icon="Sunny" />
</el-form-item>
</el-form>
<template #reference>
<el-button size="small" icon="Setting" circle></el-button>
</template>
</el-popover>
import { ref } from 'vue'
//收集开关的数据
let dark = ref<boolean>(false);
//颜色组件组件的数据
const color = ref('rgba(255, 69, 0, 0.68)')
const predefineColors = ref([
'#ff4500',
'#ff8c00',
'#ffd700',
'#90ee90',
'#00ced1',
'#1e90ff',
'#c71585',
'rgba(255, 69, 0, 0.68)',
'rgb(255, 120, 0)',
'hsv(51, 100, 98)',
'hsva(120, 40, 94, 0.5)',
'hsl(181, 100%, 37%)',
'hsla(209, 100%, 56%, 0.73)',
'#c7158577',
])
// switch开关的chang事件进行暗黑模式的切换
const changeDark = () => {
//获取HTML根节点
let html = document.documentElement
//判断HTML标签是否有类名dark
dark.value ? html.className = 'dark' : html.className = ''
}
// 主题颜色的设置
const setColor = () => {
//通知js修改根节点的样式对象的属性与属性值
let html = document.documentElement
html.style.setProperty('--el-color-primary', color.value)
}