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

Nuxt3 全栈作品【通用信息管理系统】用户管理(含重置密码)

最终效果

用户列表

在这里插入图片描述
更多搜索条件(表格上默认最多显示4个搜索条件)

在这里插入图片描述

新增用户

在这里插入图片描述
在这里插入图片描述

修改用户

在这里插入图片描述
在这里插入图片描述

用户详情

在这里插入图片描述
在这里插入图片描述

重置密码

在这里插入图片描述

前端代码

pages/user/list.vue

<script lang="ts" setup>
import { Model } from "./model";Model.avatar.type = "avatar";
const PageConfig = {//被操作实体的名称entity: "user",entityName: "用户",// 表单是否分组formGrouped: true,// 未命名分组的默认名称groupName_default: "其他信息",api: {//获取详情detail: "/user/detail",//新增add: "/user/add",//修改edit: "/user/edit",},// 展示重置密码按钮showResetPasswordBtn: true,
};
const reactive_Model = reactive(Model);
const initOK = ref(false);const getData = async () => {try {const res = await $fetch(`/api/role/all`);reactive_Model.roles.options = res.data.map((item) => ({label: item.name,value: item._id,}));const res_position = await $fetch(`/api/position/all`);reactive_Model.positions.options = res_position.data.map((item) => ({label: item.name,value: item._id,}));reactive_Model.gender.options = await getDicList("gender");} catch (error) {}
};const tableDataFormate = (row: any) => {if (row.department !== undefined) {row.department_desc = getTargetFromTree(reactive_Model.department.treeData,"code",row.department,"label");}return row;
};onMounted(async () => {const res_department: {[key: string]: any;}[] = await $fetch(`/api/system/list`, {query: {name: "department",},});if (Array.isArray(res_department) && res_department.length > 0) {reactive_Model.department.treeData = JSON.parse(res_department[0].content);}getData();initOK.value = true;
});
</script>
<template><S-comMangeInfov-if="initOK":Model="reactive_Model":PageConfig="PageConfig":tableDataFormate="tableDataFormate"></S-comMangeInfo>
</template>

pages/user/model.ts

export const Model: {[key: string]: any;
} = {createdAt: {width: 150,formHide: "all",label: "注册日期",type: "dateRange",search: true,searchOrder: 1,tableOrder: 2,},avatar: {group: "基本信息",label: "头像",type: "custom",span: 24,tableHide: true,},account: {group: "账号信息",label: "账号",require: true,formDisable: ["edit"],search: true,searchOrder: 3,tableOrder: 1,},password: {group: "账号信息",label: "密码",type: "password",require: true,tableHide: true,formHide: ["edit", "detail"],},name: {group: "基本信息",label: "姓名",search: true,searchOrder: 4,formRules: [{ min: 2, message: "姓名不能少于 2 个字符", trigger: "blur" }],},nickName: {group: "基本信息",label: "昵称",search: true,},gender: {group: "基本信息",label: "性别",type: "select",options: [],tableHide: true,},age: {group: "基本信息",label: "年龄",type: "number",min: 0,max: 160,// 单位unit: "岁",tableHide: true,},address: {group: "基本信息",label: "地址",tableHide: true,},disabled: {group: "账号信息",label: "禁用",type: "switch",},roles: {group: "权限信息",label: "角色",type: "select",options: [],multSelect: true,tableHide: true,},department: {group: "权限信息",label: "部门",type: "treeSelect",treeData: [],// 树的键key: "code",search: true,searchOrder: 2,// 表格中显示的字段tableTransProp: "department_desc",},positions: {group: "权限信息",label: "岗位",type: "select",options: [],multSelect: true,tableHide: true,},
};

依赖组件 S-comMangeInfo

https://blog.csdn.net/weixin_41192489/article/details/149768541

接口开发

server/models/user.ts

import { defineMongooseModel } from "#nuxt/mongoose";
export interface UserDataProps {account: string;password: string;avatar: string;gender: string;name: string;nickName: string;age: number;createdAt: string;updatedAt: string;address: string;roles: string[];department: string;positions: string[];disabled?: boolean;
}
export const UserSchema = defineMongooseModel<UserDataProps>("User",{account: { type: String, unique: true, required: true },avatar: { type: String },gender: { type: String },age: { type: Number, min: 0, max: 160 },password: { type: String, required: true },name: { type: String },nickName: { type: String },address: { type: String },disabled: { type: Boolean },roles: { type: [String] },department: { type: String },positions: { type: [String] },},{timestamps: true,toJSON: {// 过滤掉敏感字段transform(doc: any, ret: Record<string, any>, options: any) {delete ret.__v;delete ret.password;},},}
);

validators/user.ts

import { z } from "zod";
export const userLoginSchema = z.object({account: z.string(),password: z.string(),
});
export type UserLoginType = z.infer<typeof userLoginSchema>;
export const userSignupSchema = z.object({account: z.string(),password: z.string(),passwordConfirm: z.string(),})// 自定义校验规则.refine((data) => data.password === data.passwordConfirm, {message: "两次密码输入不一致",path: ["passwordConfirm"],});
export type UserSignupType = z.infer<typeof userSignupSchema>;
export const userCreateSchema = z.object({_id: z.string().optional(),account: z.string(),password: z.string(),name: z.string().min(2, { message: "姓名至少为2个字符" }).optional(),age: z.number().int().min(0, { message: "年龄必须大于0" }).optional(),nickName: z.string().optional(),address: z.string().optional(),avatar: z.string().optional(),gender: z.string().optional(),phoneNumber: z.string().refine((value) => /^1[3-9]\d{9}$/.test(value), "手机号码格式错误").optional(),email: z.string().email().optional(),provider: z.enum(["gitee", "github"]).optional(),disabled: z.boolean().optional(),roles: z.array(z.string()).optional(),department: z.string().optional(),positions: z.array(z.string()).optional(),
});
export const userUpdateSchema = userCreateSchema.omit({ password: true });
export type UserCreateType = z.infer<typeof userCreateSchema>;

server/api/user/list.ts

import type { SortOrder } from "mongoose";
export default defineEventHandler(async (event) => {const queryObj = getQuery(event);const { orderBy = "createdAt", order = "desc" } = queryObj;const customSort = {[orderBy as string]: order as SortOrder,};const currentPage = Number(queryObj.currentPage) || 1;const pageSize = Number(queryObj.pageSize) || 10;const skip = (currentPage - 1) * pageSize;const findCondition: {[key: string]: any;} = {};if (queryObj.account) {findCondition.account = {// 模糊匹配$regex: queryObj.account,};}if (queryObj.name) {findCondition.name = {// 模糊匹配$regex: queryObj.name,};}if (queryObj.nickName) {findCondition.nickName = {// 模糊匹配$regex: queryObj.nickName,};}if (queryObj.department) {findCondition.department = queryObj.department;}if (queryObj.age) {findCondition.age = queryObj.age;}if (queryObj.address) {findCondition.address = {// 模糊匹配$regex: queryObj.address,};}if (queryObj.createdAt_start && queryObj.createdAt_end) {findCondition.createdAt = {$gte: new Date(queryObj.createdAt_start as string),$lte: new Date(queryObj.createdAt_end as string),};}const userList = await UserSchema.find(findCondition).select(["account","name","nickName","age","address","createdAt","updatedAt","disabled","roles","department","positions","gender","avatar",]).skip(skip).limit(pageSize).sort(customSort).lean();const total = await UserSchema.find(findCondition).countDocuments();return {data: userList,total,pageSize,currentPage,};
});

server/api/user/add.post.ts

import bcrypt from "bcrypt";
import { userCreateSchema } from "@/validators/user";
export default defineEventHandler(async (event) => {const result = await runValidate(userCreateSchema, event);const config = useRuntimeConfig();const { account, password } = result.data;const user = await UserSchema.findOne({ account: account }).lean();if (user) {throw createError({statusCode: 409,statusMessage: "该账号存在,请修改后重试",});}const hash = await bcrypt.hash(password, config.bcrypt.saltRounds);const userCreatedData = result.data;userCreatedData.password = hash;const newUser = await UserSchema.create(userCreatedData);return newUser;
});

server/api/user/edit.post.ts

import { userUpdateSchema } from "@/validators/user";
export default defineEventHandler(async (event) => {const result = await runValidate(userUpdateSchema, event);const newUser = UserSchema.findByIdAndUpdate(result.data._id, result.data, {new: true,});return newUser;
});

server/api/user/[id].patch.ts

修改单个属性,如重置密码,禁用启用等

// 用户的禁用、启用、重置密码
import { UserDataProps } from "~/server/models/user";
import bcrypt from "bcrypt";
export default defineEventHandler(async (event) => {const id = getRouterParam(event, "id");const query = getQuery(event);const { handleType, newPassword } = query;let newData: Partial<UserDataProps> = {};const config = useRuntimeConfig();switch (handleType) {case "disable":newData.disabled = true;break;case "enable":newData.disabled = false;break;case "resetPassword":newData = {password: await bcrypt.hash(config.bcrypt.defaultPassword,config.bcrypt.saltRounds),};break;case "editPassword":if (newPassword) {newData = {password: await bcrypt.hash(String(newPassword),config.bcrypt.saltRounds),};}break;default:break;}const newUser = UserSchema.findByIdAndUpdate(id, newData, { new: true });return newUser;
});

server/api/user/[id].delete.ts

export default defineEventHandler((event) => {const id = getRouterParam(event, "id");const result = UserSchema.findByIdAndDelete(id);return result;
});

server/api/user/current.ts

获取当前登录用户详情信息(根据唯一键–账号查询)

// 查询当前登录的用户信息
export default defineEventHandler(async (event) => {const currentUserData = await UserSchema.findOne({account: event.context.user.account,}).exec();return currentUserData?.toJSON();
});
http://www.lryc.cn/news/605027.html

相关文章:

  • 第十二天:C++ 标准库函数分类总结
  • spark入门-helloword
  • 干货 | ANSYS复合材料前后处理
  • 跨云部署实战:前端、后端 + RSYNC、全栈场景统一落地方案
  • Nestjs框架: 关于 OOP / FP / FRP 编程
  • Map 集合
  • 高可靠液晶屏系统解决方案深度解析
  • AI 驱动的软件测试革新:框架、检测与优化实践
  • 原生C++实现信号与槽机制:原理详解
  • 如何选择GEO优化公司哪家好?
  • Apache FOP实践——pdf模板引擎
  • 推扫式和凝视型高光谱相机分别采用哪些分光方式?
  • MaxKB+MinerU:通过API实现PDF文档解析并存储至知识库
  • 梳理Ego-Planner模式下5通道、6通道与无人机模式的关系
  • Camera相机人脸识别系列专题分析之十九:MTK ISP6S平台FDNode传递三方FFD到APP流程解析
  • 不可变类字段修复建议
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘dash’问题
  • Python 程序设计讲义(43):组合数据类型——元组类型:元组的常用操作
  • WSL2搭建基于Docker的ESP32开发环境
  • 机器学习项目完整流程详解
  • 基于C-MTEB/CMedQAv2-rerankingv的Qwen3-1.7b模型微调-demo
  • Android基础(二)了解Android项目
  • 端侧大模型迎来“轻”革命!移远通信 × RWKV 打造“轻量AI大脑”
  • 单片机电路基础
  • 【NCS随笔】如何在hello_world添加蓝牙功能(一)
  • sqli-labs:Less-7关卡详细解析
  • 国内数据集成厂商有哪些?如何选择最适合的数据集成平台?
  • Qt 与物联网(IoT)开发
  • 【Linux】重生之从零开始学习运维之备份恢复
  • String模拟实现的补充说明