当前位置: 首页 > news >正文

【Vue3+Ts项目】硅谷甄选 — 用户管理+角色管理+菜单管理+首页

一、用户管理

1.1 接口

1.1.1 接口定义

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 })

 1.1.2 数据ts类型定义

src/api/acl/user/type.ts 

//账号信息的ts类型
export interface ResponseData {code: numbermessage: stringok: boolean}//代表一个账号信息的ts类型export interface User {id?: numbercreateTime?: stringupdateTime?: stringusername?: stringpassword?: stringname?: stringphone?: nullroleName?: string}//数组包含全部的用户信息export type Records = User[]//获取全部用户信息接口返回的数据ts类型export interface UserResponseData extends ResponseData {data: {records: Recordstotal: numbersize: numbercurrent: numberpages: number}}//代表一个职位的ts类型export interface RoleData {id?: numbercreateTime?: stringupdateTime?: stringroleName: stringremark: null}//全部职位的列表export type AllRole = RoleData[]//获取全部职位的接口返回的数据ts类型export interface AllRoleResponseData extends ResponseData {data: {assignRoles: AllRoleallRolesList: AllRole}}//给用户分配职位接口携带参数的ts类型export interface SetRoleData {roleIdList: number[]userId: number}

 1.2 业务实现

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 = pagerlet result: UserResponseData = await reqUserInfo(pageNo.value, pageSize.value, keyword.value)if (result.code === 200) {total.value = result.data.totaluserArr.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// 存储收集已有的账号xinxObject.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>

 二、 角色管理

2.1 接口 

 2.1.1 接口定义

 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)

 2.1.2 数据ts类型定义

 src/api/acl/role/type.ts

export interface ResponseData {code: numbermessage: stringok: boolean}//职位数据类型export interface RoleData {id?: numbercreateTime?: stringupdateTime?: stringroleName: stringremark?: null}//全部职位的数组的ts类型export type Records = RoleData[]//全部职位数据的相应的ts类型export interface RoleResponseData extends ResponseData {data: {records: Recordstotal: numbersize: numbercurrent: numberorders: []optimizeCountSql: booleanhitCount: booleancountId: nullmaxLimit: nullsearchCount: booleanpages: number}}//菜单与按钮数据的ts类型export interface MunuData {id: numbercreateTime: stringupdateTime: stringpid: numbername: stringcode: stringtoCode: stringtype: numberstatus: nulllevel: numberchildren?: MenuListselect: boolean}export type MenuList = MunuData[]//菜单权限与按钮权限数据的ts类型export interface MenuResponseData extends ResponseData {data: MenuList}

2.2 业务实现

src/views/acl/role/index.vue   

PSfilterSelectArr方法只需要过滤最深一个层级的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-columnlabel="角色名称"align="center"show-overflow-tooltipprop="roleName"></el-table-column><el-table-columnlabel="创建时间"align="center"show-overflow-tooltipprop="createTime"></el-table-column><el-table-columnlabel="更新时间"align="center"show-overflow-tooltipprop="updateTime"></el-table-column><el-table-column label="操作" width="300px" align="center"><template #="{ row, $index }"><el-buttontype="primary"size="small"icon="User"@click="setPermission(row)">分配权限</el-button><el-buttontype="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-paginationv-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-inputplaceholder="请填写角色名称"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-treeref="tree":data="menuArr"show-checkboxnode-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 = pagerlet result: RoleResponseData = await reqRoleInfo(pageNo.value,pageSize.value,keyword.value,)if (result.code === 200) {total.value = result.data.totalroleArr.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.dataselectArr.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 () => {//职位的IDconst roleId = roleParams.id as number//选中节点的IDlet arr = tree.value.getCheckedKeys()//半选的IDlet 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>

 三、菜单管理

常量路由:谁都可以访问的路由(首页、数据大屏)

异步路由:只有部分人可以访问的路由(权限管理、商品管理及其子路由) 

3.1 接口 

3.1.1 接口定义

 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)

3.1.2 数据ts类型定义

 src/api/acl/menu/type.ts

//数据类型定义
export interface ResponseData {code: numbermessage: stringok: boolean
}
//菜单数据与按钮数据的ts类型
export interface Permission {id?: numbercreateTime: stringupdateTime: stringpid: numbername: stringcode: nulltoCode: nulltype: numberstatus: nulllevel: numberchildren?: PermissionListselect: boolean
}
export type PermissionList = Permission[]
//菜单接口返回的数据类型
export interface PermissionResponseData extends ResponseData {data: PermissionList
}//添加与修改菜单携带的参数的ts类型
export interface MenuParams {id?: number //IDcode: string //权限数值level: number //几级菜单name: string //菜单的名字pid: number //菜单的ID
}

3.2 业务实现 

 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-dialogv-model="dialogVisible":title="menuData.id ? '更新菜单' : '添加菜单'"width="30%"><!-- 表单组件:收集新增与已有菜单的数据 --><el-form><el-form-item label="名称"><el-inputplaceholder="请输入菜单名称"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>唧唧bong甄选运营平台</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>

相关文章:

【Vue3+Ts项目】硅谷甄选 — 用户管理+角色管理+菜单管理+首页

一、用户管理 1.1 接口 1.1.1 接口定义 src/api/acl/user/index.ts // 用户管理模块的接口 import request from /utils/request import type {AllRoleResponseData,SetRoleData,User,UserResponseData } from ./type enum API {// 获取全部已有用户账号信息ALLUSER_URL /…...

node连接Mysql失败

报错信息 Error: connect ETIMEDOUTat Connection._handleConnectTimeout (d:\课设\服务器端\node_modules\mysql\lib\Connection.js:409:13)at Object.onceWrapper (node:events:628:28)at Socket.emit (node:events:514:28)at Socket._onTimeout (node:net:589:8)at listOnT…...

运用AI搭建中间服务层(四)

MiddlewareService文件夹 在这个文件夹中&#xff0c;我们需要添加以下文件&#xff1a; 名人服务.cs 名人服务.cs 名人结果.cs ILandmarkService.cs 地标服务 .cs 地标结果 .cs ICelebrityService.cs – 包装多个串行的认知服务来实现名人识别的中间服务层的接口定义&…...

[C#]winform部署yolov5-onnx模型

【官方框架地址】 https://github.com/ultralytics/yolov5 【算法介绍】 Yolov5&#xff0c;全称为You Only Look Once version 5&#xff0c;是计算机视觉领域目标检测算法的一个里程碑式模型。该模型由ultralytics团队开发&#xff0c;并因其简洁高效的特点而备受关注。Yol…...

基于SpringBoot的洗衣店管理系统

基于SpringBoot的洗衣店管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 可视化展示 用户界面 管理员界面 摘要 洗衣店管理系统基于Spring Boot框…...

AMEYA360:广和通RedCap模组FG131FG132系列

2024年1月&#xff0c;广和通RedCap模组FG131&FG132系列已进入工程送样阶段&#xff0c;可为终端客户提供样片。广和通RedCap模组系列满足不同终端对5G速率、功耗、尺寸、成本的需求&#xff0c;全面助力RedCap技术的行业应用。 FG131&FG132系列基于骁龙X35 5G调制解调…...

RGB,RGB-D,单目,双目,sterro相机,实例相机介绍

相机—特点及区别 1.相机种类 RGB&#xff0c;RGB-D&#xff0c;单目&#xff0c;双目&#xff0c;sterro相机&#xff0c;实例相机 2.相机特点 2.1单目 只使用一个摄像头进行SLAM&#xff0c;结构简单&#xff0c;成本低 三维空间的二维投影 必须移动相机&#xff0c;才…...

【linux】history命令显示时间的例子

在Linux中&#xff0c;你可以通过设置HISTTIMEFORMAT环境变量来显示命令的执行时间。这个环境变量定义了history命令中时间的显示格式。以下是设置和说明的步骤&#xff1a; 打开终端&#xff1a; 打开你的终端应用。 编辑配置文件&#xff1a; 使用文本编辑器&#xff08;如n…...

Nginx负载均衡以及常用的7层协议和4层协议的介绍

一、引言 明人不说暗话&#xff0c;下面来解析一下 Nginx 的负载均衡。需要有 Linux 和 Nginx 环境哈。 二、nginx负载均衡的作用 高并发&#xff1a;负载均衡通过算法调整负载&#xff0c;尽力均匀的分配应用集群中各节点的工作量&#xff0c;以此提高应用集群的并发处理能力…...

【机器学习300问】4、机器学习到底在学习什么?

首先我们先了解一个前置问题&#xff0c;再回答机器学习到底在学习什么。 一、求机器学习问题有哪几步&#xff1f; 求解机器学习问题的步骤可以分为“学习”和“推理”两个阶段。首先&#xff0c;在学习阶段进行模型的学习&#xff0c;然后&#xff0c;在推理阶段用学到的模型…...

设计一个简易版的数据库路由

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…...

接口自动化测试面试题

前言 前面总结了一篇关于接口测试的常规面试题&#xff0c;现在接口自动化测试用的比较多&#xff0c;也是被很多公司看好。那么想做接口自动化测试需要具备哪些能力呢&#xff1f; 也就是面试的过程中&#xff0c;面试官会考哪些问题&#xff0c;知道你是不是真的做过接口自动…...

Tampermonkey油猴插件-各大网盘批量分享,解放双手-上

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列...

【DB2】installSAM执行后会重启这件事

碎碎念 在使用自动化工具安装TSAMP的过程中&#xff0c;机器会自动重启这件事。 TSAMP真的挺折磨的&#xff0c;一个月居然因为这件事情debug两次了。 在测试自动化脚本的时候&#xff0c;第一遍安装都是好好的&#xff0c;从第二遍开始&#xff08;因为要测试脚本的幂等性&…...

RTSP网络视频协议

一.RTSP网络视频协议介绍 RTSP是类似HTTP的应用层协议&#xff0c;一个典型的流媒体框架网络体系可参考下图&#xff0c;其中rtsp主要用于控制命令&#xff0c;rtcp主要用于视频质量的反馈&#xff0c;rtp用于视频、音频流从传输。 1、RTSP&#xff08;Real Time Streaming P…...

Python 网络数据采集(四):Selenium 自动化

Python 网络数据采集&#xff08;四&#xff09;&#xff1a;Selenium 自动化 前言一、背景知识Selenium 4Selenium WebDriver 二、Selenium WebDriver 的安装与配置2.1 下载 Chrome 浏览器的驱动程序2.2 配置环境变量三、Python 安装 Selenium四、页面元素定位4.1 选择浏览器开…...

实现秒杀功能设计

页面 登录页面 登录成功后&#xff0c;跳转商品列表 商品列表页 加载商品信息 商品详情页 根据商品id查出商品信息返回VO&#xff08;包括rmiaoshaStatus、emainSeconds&#xff09;前端根据数据展示秒杀按钮&#xff0c;点击开始秒杀 订单详情页 秒杀页面设置 后端返回秒杀…...

每天刷两道题——第十四天

1.1矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用原地算法。 输入&#xff1a;matrix [[0,1,2,0],[3,4,5,2],[1,3,1,5]] 输出&#xff1a;[[0,0,0,0],[0,4,5,0],[0,3,1,0]] 原地算法&#xff08;…...

快速掌握Postman实现接口测试

快速掌握Postman实现接口测试 Postman简介 Postman是谷歌开发的一款网页调试和接口测试工具&#xff0c;能够发送任何类型的http请求&#xff0c;支持GET/PUT/POST/DELETE等方法。Postman非常简单易用&#xff0c;可以直接填写URL&#xff0c;header&#xff0c;body等就可以发…...

jmeter--3.使用提取器进行接口关联

目录 1. 正则表达式提取器 1.1 提取单个数据 1.2 名词解释 1.3 提取多个数据 2. 边界值提取器 2.2 名词解释 3. JSON提取器 3.1 Json语法 3.2 名词解释 3.3 如果有多组数据&#xff0c;同正则方式引用数据 1. 正则表达式提取器 示例数据&#xff1a;{"access_to…...

移动通信系统关键技术多址接入MIMO学习(8)

1.Multiple-antenna Techniques多天线技术MIMO&#xff0c;从SISO到SIMO到MISO到如今的MIMO&#xff1b; 2.SIMO单发多收&#xff0c;分为选择合并、增益合并&#xff1b;SIMO&#xff0c;基站通过两路路径将信号发送到终端&#xff0c;因为终端接收到的两路信号都是来自同一天…...

WorkPlus AI助理为企业提供智能客服的机器人解决方案

在数字化时代&#xff0c;企业面临着客户服务的重要挑战。AI客服机器人成为了提升客户体验和提高工作效率的关键工具。作为一款优秀的AI助理&#xff0c;WorkPlus AI助理以其智能化的特点和卓越的功能&#xff0c;为企业提供了全新的客服机器人解决方案。 为什么选择WorkPlus A…...

python类装饰器编写单体类

1 python类装饰器编写单体类 类装饰器用于装饰类&#xff0c;用于管理类自身&#xff0c;或用于管理实例创建调用。 单体类&#xff0c;不管创建多少次实例&#xff0c;都只有一个实例的类。可以通过类装饰器管理装饰类的全部实例&#xff0c;实现单体类。 1.1 字典存放单体…...

Java并发Condition 详解

1.引言 在Java并发编程中&#xff0c;线程间的协作是一个核心话题。为了实现线程间的协作&#xff0c;Java提供了多种机制&#xff0c;其中等待/通知机制是最常见的一种。在早期版本中&#xff0c;我们通过Object类提供的wait、notify和notifyAll方法来实现这种机制。然而&…...

如何使用CentOS系统中的Apache服务器提供静态HTTP服务

在CentOS系统中&#xff0c;Apache服务器是一个常用的Web服务器软件&#xff0c;它可以高效地提供静态HTTP服务。以下是在CentOS中使用Apache提供静态HTTP服务的步骤&#xff1a; 1. 安装Apache服务器 首先&#xff0c;您需要确保已安装Apache服务器。可以使用以下命令安装Ap…...

Python入门0基础学习笔记

1.编程之前 在编写代码之前&#xff0c;还有两件事需要做&#xff1a; 安装 Python 解释器&#xff1a;计算机是没法直接读懂 Python 代码的&#xff0c;需要一个解释器作为中间的翻译&#xff0c;把代码转换成字节码之后再执行。 Python 是翻译一行执行一行。一般说的安装 …...

python绘制热力图-数据处理-VOC数据类别标签分布及数量统计(附代码)

前言 当你需要统计训练数据中每个类别标签有多少&#xff0c;并且想知道坐标中心分布在图像的位置信息时&#xff0c;你可以利用一下脚本进行计算&#xff01; 步骤 要绘制热力图来分析VOC数据的分布统计&#xff0c;可以按照以下步骤进行&#xff1a; 数据处理&#xff1…...

【回顾2023,展望2024】砥砺前行

2023年总结 转眼间&#xff0c;迎来了新的一年2024年&#xff0c;回顾2023&#xff0c;对于我来说是一个充满平凡但又充实又幸运的一年。这一年经历了很多的事情&#xff0c;包括博客创作、技术学习、出书、买房等&#xff0c;基本上每件事情都是一个前所未有的挑战和机遇、使…...

Stable Diffusion初体验

体验了下 Stable Diffusion 2.0 的图片生成&#xff0c;效果还是挺惊艳的&#xff0c;没有细调prompt输入&#xff0c;直接输入了下面的内容&#xff1a; generate a Elimination Game image of burnning tree, Cyberpunk style 然后点击生成&#xff0c;经过了10多秒的等待就输…...

缓存解析:从架构设计到Redis应用及最佳实践

典型架构设计中缓存的存储位置 在现代软件架构中&#xff0c;缓存是优化数据检索、提高应用性能的关键组件。缓存的存储位置多种多样&#xff0c;每个位置针对特定的优化目标和需求。理解这些层级对于设计高效的系统至关重要。 浏览器缓存&#xff1a;这是最接近用户端的缓存层…...

做违法网站会怎么样/互联网营销师是干什么的

目录1、NIST的云计算定义1.1、云计算概念1.1.1、云计算资源1.1.2、云计算服务模型1.1.3、发布模型2、OpenStack概述2.1、OpenStack服务2.2、OpenStack优势3、OpenStack一键在线安装3.1、环境搭建3.1.2、配置网卡&#xff0c;挂载光盘3.1.3、配置yum源3.1.4、关闭防火墙和核心防…...

企业网站seo贵不贵/it培训

【产品介绍】&#xff1a; 含肽序列和末端的DBCO基团&#xff0c;由于增加了灵活性&#xff0c;Gly序列已 应用于融合蛋白应用中&#xff0c;没有侧链&#xff0c;序列作为可选的折叠断裂间隔物使用 例如&#xff0c;GGS是的折叠断裂连接器之一&#xff0c;可以很好地暴露融合…...

wordpress忘记用户名密码破解/网站排名软件优化

数组 &#xff1a;数据相同的容器 语法格式 数据类型[] 数组名称 new 数据类型[给定的长度]5、 for(int i0,j2 ; i<j ; i,j–) 特殊的表现形式 在Java中&#xff0c;可以使用以下格式来定义一个数组。如下 数据类型[] 数组名 new 数据类型[元素个数或数组长度]; int[] x n…...

衡水哪里可以做网站/乐清网站建设

rsa 密钥生成 见 http://blog.andsky.com/js-rsa-use-openssl-make-public-pirvate-key/android 客户端用rsa 公钥加密后经 base64 编码发到 服务端&#xff0c;服务端使用私钥解密客户端代码import java.math.BigInteger;import java.security.KeyFactory;import java.securit…...

onethink wordpress/个人网页怎么做

多用一个库&#xff0c;多一个坑&#xff0c;用一个库就要用足它的红利&#xff0c;可能的情况下尽量采纳同一个库。前面采用来restbed 来实现websocket 和http server &#xff0c;今天需要写一个influxdb 的C客户端。有一次选用restbed 来作为 http客户端。网上建议比较多的是…...

苏州教育网站建设/推广计划书范文

某日某站某博水民甲&#xff1a;我要买股票赚大钱&#xff0c;放消息啊方消息啊&#xff0c;我要更多消息水民乙&#xff1a;顶……n日后仍然没有消息水民甲&#xff1a;博主是一XXX&#xff0c;为什么不放消息&#xff1f;水民乙&#xff1a;博主高人啊&#xff0c;应该有内线…...