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

Vue3.5 企业级管理系统实战(十八):用户管理

本篇主要探讨用户管理功能,接口部分依然是使用 Apifox mock 模拟。

1 用户 api

在 src/api/user.ts 中添加用户相关 CRUD 接口,代码如下:

//src/api/user.ts 
import request from "@/api/config/request";
// 从 "./type" 模块中导入 ApiResponse 类型,用于定义接口响应数据的结构
import type { ApiResponse } from "./type";
import type { IRole } from "./role";/*** 定义用户登录所需的数据结构* @interface IUserLoginData* @property {string} username - 用户登录使用的用户名* @property {string} password - 用户登录使用的密码*/
export interface IUserLoginData {username: string;password: string;
}/*** 定义登录接口响应的数据结构* @interface ILoginResponseData* @property {string} token - 登录成功后返回的令牌,用于后续请求的身份验证*/
export interface ILoginResponseData {token: string;
}/*** 登录接口* @param {IUserLoginData} data - 用户登录所需的数据,包含用户名和密码* @returns {Promise<ApiResponse<ILoginResponseData>>} - 返回一个 Promise 对象,该对象解析为包含登录响应数据的 ApiResponse 类型*/
export const login = (data: IUserLoginData
): Promise<ApiResponse<ILoginResponseData>> => {return request.post("/4642164-4292760-default/287017559", data);
};
//test
export const logout_error = (): Promise<ApiResponse<ILoginResponseData>> => {return request.post("/4642164-4292760-default/auth/401");
};//个人中心接口
export interface Profile {id: number;username: string;email: string;mobile: string;isSuper: boolean;status: boolean;avatar: string;description: string;roles: IRole[];roleIds?: number[]; // 修改用户的时候,后端接受只要id
}export interface IUsers {users: Profile[];count: number;
}// 查询参数
export interface IUserQuery {pageNum?: number;pageSize?: number;mobile?: string;status?: boolean;username?: string;
}// 获取用户列表的接口
export const getUsers = (params: IUserQuery): Promise<ApiResponse<IUsers>> => {const {pageNum = 0,pageSize = 10,username = "",status,mobile = ""} = params;return request.get("/user", {params: {pageNum,pageSize,username,status,mobile}});
};// 删除用户
export const removeUser = (id: number): Promise<ApiResponse> => {return request.delete(`/user/${id}`);
};// 添加用户
export const addUser = (data: Profile): Promise<ApiResponse> => {return request.post("/auth/register", data);
};// 编辑用户
export const updateUser = (id: number, data: Profile): Promise<ApiResponse> => {return request.put(`/user/${id}`, data);
};

2 用户 Store

在 src/stores/user.ts 中添加用户相关方法,代码如下:

//src/stores/user.ts
import type { IUserLoginData, IUserQuery, IUsers, Profile } from "@/api/user";
import {login as loginApi,getUsers as getUsersApi, // 获取用户addUser as addUserApi,removeUser as removeUserApi,updateUser as updateUserApi
} from "@/api/user";
import { setToken, removeToken } from "@/utils/auth";
import { useTagsView } from "./tagsView";
import type { IRole } from "@/api/role";
export type IProfileQuery = Profile & {pageNum?: number;pageSize?: number;
};
export const useUserStore = defineStore("user", () => {const state = reactive({token: "",users: [] as IUsers["users"], // 用户列表count: 0, // 用户个数roles: [] as IRole[],userInfo: {} as Profile});const tagsViewStore = useTagsView();const login = async (userInfo: IUserLoginData) => {try {const { username, password } = userInfo;const response = await loginApi({ username: username.trim(), password });const { data } = response;state.token = data.token;setToken(data.token);} catch (e) {return Promise.reject(e);}};const logout = () => {state.token = "";removeToken();// 所有的信息都应该情况tagsViewStore.delAllView(); // ...};// 获取全部用户const getAllUsers = async (params: IUserQuery) => {const res = await getUsersApi(params);const { data } = res;state.users = data.users;state.count = data.count;};// 添加用户const addUser = async (data: IProfileQuery) => {const { pageSize, pageNum, ...params } = data;const res = await addUserApi(params);if (res.code === 0) {getAllUsers({pageSize,pageNum});}};// 删除用户const removeUser = async (data: IProfileQuery) => {const { pageSize, pageNum, id } = data;const res = await removeUserApi(id);if (res.code === 0) {getAllUsers({pageSize,pageNum});}};//编辑用户const editUser = async (data: IProfileQuery) => {const { pageSize, pageNum, ...params } = data;const res = await updateUserApi(params.id, params);if (res.code === 0) {getAllUsers({pageSize,pageNum});}};return {login,state,logout,getAllUsers,editUser,removeUser,addUser};
});

3 用户管理界面实现

3.1 editorUser 组件封装

在 src/views/system/user/components/editorUser.vue 中,实现用户新增编辑组件,代码如下:

//src/views/system/user/components/editorUser.vue
<template><div class="editor-container" p-20px><el-formref="editFormRef":model="editData":rules="menuFormRules"label-width="80px"><el-form-item label="用户名" prop="username"><el-input v-model="editData.username" placeholder="请输入用户名" /></el-form-item><el-form-item label="手机" prop="mobile"><el-inputv-model="editData.mobile"placeholder="请输入手机"maxlength="11"/></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="editData.email" placeholder="请输入邮箱" /></el-form-item><el-form-item label="状态" prop="status"><el-switch v-model="editData.status" /></el-form-item><el-form-item label="角色分配" prop="roleIds"><el-select multiple v-model="editData.roleIds" placeholder="请选择角色"><el-optionv-for="item in editData.roles":key="item.id":label="item.name":value="item.id"></el-option></el-select></el-form-item><el-form-item label="说明" prop="description"><el-inputtype="textarea":rows="3"v-model="editData.description"placeholder="请输入说明"/></el-form-item><el-form-item><el-button type="primary" @click="submitMenuForm">提交</el-button></el-form-item></el-form></div>
</template><script lang="ts" setup>
import type { Profile } from "@/api/user";
import type { FormInstance, FormItemRule } from "element-plus";
import type { PropType } from "vue";const props = defineProps({type: {// 操作类型 0编辑 1新增type: Number,required: true},data: {type: Object as PropType<Profile>}
});
const emit = defineEmits(["submit"]);const editFormRef = ref<FormInstance | null>(null);
const editData = ref<Partial<Profile>>({username: "",email: "",mobile: "",description: "",status: true
});// 验证规则
const validateMobile = (rule: unknown,value: string,callback: (arg?: Error) => void
) => {if (!isNaN(Number(value)) && value.length === 11) {callback();}callback(new Error("请输入正确格式手机号!"));
};const menuFormRules = {username: {required: true,message: "请输入用户名",trigger: "blur"},email: [{required: true,message: "请输入邮箱",trigger: "blur"},{type: "email",message: "请输入正确的邮箱地址",trigger: ["blur", "change"]}] as FormItemRule[],mobile: [{required: true,message: "请输入手机",trigger: "blur"},{message: "请输入正确11位手机号",trigger: "blur",validator: validateMobile}],roleIds: {required: true,message: "请至少选择一个角色!",trigger: "blur"}
};const defaultProps = {username: "",email: "",mobile: "",description: "",status: true
};watchEffect(() => {if (props.data) {// 移除之前表单效验结果editFormRef.value?.clearValidate();editData.value = { ...defaultProps, ...props.data };}
});// 提交编辑菜单
const submitMenuForm = () => {(editFormRef.value as FormInstance).validate((valid) => {if (valid) {emit("submit", editData.value);}});
};
</script>

3.2 用户管理页面

在 src/views/system/user/index.vue 中,编写用户管理静态页面,代码如下:

//src/views/system/user/index.vue
<template><div class="user-container" p-30px><h2>用户管理</h2><el-button type="primary" plain @click="handleAddUser" class="mb">添加用户</el-button><el-form :inline="true" :model="formQuery" ref="queryFormRef"><el-form-item label="用户名" prop="username"><el-inputv-model="formQuery.username"placeholder="请输入用户名"></el-input></el-form-item><el-form-item label="手机号" prop="mobile"><el-inputv-model="formQuery.mobile"placeholder="请输入手机号"></el-input></el-form-item><el-form-item label="状态" prop="status" w-200px><el-select v-model="formQuery.status" placeholder="状态"><el-option label="全部" value="all"></el-option><el-option label="禁用" :value="0"></el-option><el-option label="正常" :value="1"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="handleSubmitQuery">查询</el-button><el-button type="default" @click="handleResetFeilds">重置</el-button></el-form-item></el-form><div class="user-list"><el-table :data="users" max-height="400"><el-table-column prop="username" label="用户名"> </el-table-column><el-table-column prop="mobile" label="手机"> </el-table-column><el-table-column prop="email" label="邮箱"> </el-table-column><el-table-column prop="status" label="状态" :formatter="formatter"></el-table-column><el-table-column prop="createdAt" label="创建时间"> </el-table-column><el-table-column label="操作" fixed="right" width="150px"><template #default="scope"><el-buttonsize="small"link@click="handleEditUser(scope.$index, scope.row)">编辑</el-button><el-buttonsize="small"link@click="handleDeleteUser(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table><div class="user-container"><div class="user-list"><!--用户展示--><div class="user-pagination"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange"background:total="total":page-sizes="[1, 5, 10, 20]":page-size="pageSize"layout="total, prev, pager, next, sizes,jumper"></el-pagination></div></div></div></div><right-panel v-model="panelVisible" :title="panelTitle" :size="330"><editor-user:type="editType":data="editData"@submit="handleSubmitUser"/></right-panel></div>
</template><script lang="ts" setup>
import type { IUserQuery, Profile } from "@/api/user";
import { useRoleStore } from "@/stores/role";
import { type IProfileQuery, useUserStore } from "@/stores/user";
import type { FormInstance } from "element-plus";
const store = useUserStore();
// 用户列表
const users = computed(() => store.state.users);
// 分页相关状态
const pageNum = ref(0);
const pageSize = ref(10);
// 获取用户列表 支持分页
const getUserList = () => {store.getAllUsers({pageNum: pageNum.value,pageSize: pageSize.value,...formQuery// ... 搜索条件} as unknown as IUserQuery);
};
// 格式化status
const formatter = (row: Profile) => {return row.status ? "正常" : "禁用";
};
// 不使用watchEffect
onMounted(() => {getUserList();
});
// 删除用户
const { proxy } = getCurrentInstance()!;
const handleDeleteUser = async (index: number, row: Profile) => {try {await proxy?.$confirm(`您确认要删除用户${row.username}吗?`, "删除确认", {type: "warning"});await store.removeUser({id: row.id,pageNum: pageNum.value,pageSize: pageSize.value} as IProfileQuery);proxy?.$message.success("用户删除成功");} catch {proxy?.$message({type: "info",message: "已取消删除"});}
};
const handleEditUser = (index: number, row: Profile) => {editType.value = 0;editData.value = { ...row };// 获取当前编辑用户 现有角色列表editData.value.roleIds = row.roles.map((item) => item.id);editData.value.roles = roles.value!; // 所有角色列表panelVisible.value = true;
};
// 用户总条数
const total = computed(() => store.state.count);
// 分页
const handleSizeChange = (val: number) => {pageSize.value = val;getUserList();
};
const handleCurrentChange = (val: number) => {pageNum.value = val - 1; // 页码后端是从0开始的getUserList();
};// 查询参数
const formQuery = reactive({username: "",status: "all",mobile: ""
});
const handleSubmitQuery = () => {getUserList();
};// 重置
const queryFormRef = useTemplateRef<FormInstance | null>("queryFormRef");
const handleResetFeilds = () => {(queryFormRef.value as FormInstance).resetFields();getUserList();
};const editData = ref<Profile | undefined>(undefined);
// 控制面板显示
const panelVisible = ref(false);
const editType = ref(1);
const panelTitle = computed(() =>editType.value === 1 ? "新增用户" : "编辑用户"
);
const storeRole = useRoleStore();
storeRole.getRoles({pageNum: pageNum.value,pageSize: pageSize.value,flag: 0
});
const roles = computed(() => storeRole.state.roles); // 角色const handleAddUser = () => {editType.value = 1;editData.value = {} as Profile;editData.value.roles = roles.value; // 所有角色列表editData.value.roleIds = []; // 所选角色id列表panelVisible.value = true;
};
const editUser = async (data: IProfileQuery) => {store.editUser({...data,pageSize: pageSize.value,pageNum: pageNum.value});(queryFormRef.value as FormInstance).resetFields();proxy?.$message.success("用户编辑成功");panelVisible.value = false;
};
const handleSubmitUser = (data: Profile) => {if (editType.value === 1) {// 新增addNewUser(data);} else {editUser(data);}
};
const addNewUser = (data: Profile) => {store.addUser({...data,pageSize: pageSize.value,pageNum: pageNum.value});(queryFormRef.value as FormInstance).resetFields();proxy?.$message.success("用户添加成功");panelVisible.value = false;
};
</script>

npm run dev 启动后,页面效果如下:

以上,就是用户管理的全部内容。

下一篇将继续探讨 菜单管理,敬请期待~ 

http://www.xdnf.cn/news/4931.html

相关文章:

  • 回顾 Vue 3 基础【Plan - May - Week 1】
  • 零基础学Java——第十一章:实战项目 - 控制台应用开发
  • 力扣-2.两数相加
  • WPF内嵌其他进程的窗口
  • 鸿蒙NEXT开发动画案例5
  • tomcat6性能优化
  • MySQL 数据库操作
  • uniapp小程序轮播图高度自适应优化详解
  • 使用Python 打造多格式文件预览工具 — 图、PDF、Word、Excel 一站式查看
  • Java SE(10)——抽象类接口
  • 高效C/C++之十:Coverity修复问题:尽量多使用 c++强制类型转化
  • 人工智能之数学基础:二次型
  • 内网渗透——红日靶场三
  • HOT 100 | 【子串】76.最小覆盖子串、【普通数组】53.最大子数组和、【普通数组】56.合并区间
  • AI与计算机视觉(CV):目标检测与图像分割的最新进展
  • 行业 |四大痛点待破:“拆解”DeepSeek一体机
  • 英伟达Blackwell架构重构未来:AI算力革命背后的技术逻辑与产业变革
  • 【强化学习】动态规划(Dynamic Programming, DP)算法
  • Jenkins集成Maven
  • 如何构建容器镜像并将其推送到极狐GitLab容器镜像库?
  • 【亲测有效】如何清空但不删除GitHub仓库中的所有文件(main分支)
  • 单例模式的两种设计
  • [论文阅读]Deeply-Supervised Nets
  • Idea Code Templates配置
  • K8S - Harbor 镜像仓库部署与 GitLab CI 集成实战
  • 工业与协议融合篇:如何将多个协议集成进一个系统?
  • OpenCV中适用华为昇腾(Ascend)后端的逐元素操作(Per-element Operations)
  • MCU存储系统架构解析
  • 面试问题(连载。。。。)
  • 【Bootstrap V4系列】学习入门教程之 组件-下拉菜单(Dropdowns)