refactor(admin): 统一前后端CRUD操作的成功提示并移除重复逻辑

1. 为角色、权限、用户的增删改请求统一添加强制成功提示
2. 移除onSuccess回调中的重复成功提示逻辑
3. 关闭表格删除操作的内置成功通知,避免重复弹窗
4. 重构用户管理页面,移除手动绑定的成功/错误回调,交由crud统一处理
5. 修复后端角色模块的FastCRUD使用问题,改用原生SQLAlchemy操作数据库
This commit is contained in:
jayhgq 2026-05-28 23:34:19 +08:00
parent 2c78e254e1
commit 2641352f5e
5 changed files with 72 additions and 62 deletions

View File

@ -11,8 +11,8 @@ from utils.like_filter import ilike_contains
router = APIRouter(prefix="/roles", tags=["roles"])
# 创建FastCRUD实例
role_crud = FastCRUD(Role, RoleResponse)
# 创建FastCRUD实例指定update_schema为RoleCreate
role_crud = FastCRUD(Role, RoleResponse, RoleCreate)
@router.post("/", response_model=RoleResponse)
@ -33,11 +33,18 @@ async def create_role(
# 设置创建人为当前登录用户
role_data.creator = current_user.nickname or current_user.username
# 使用FastCRUD创建角色指定返回模型
created_role = await role_crud.create(
db, role_data, schema_to_select=RoleResponse, return_as_model=True
# 创建角色对象
new_role = Role(
name=role_data.name,
creator=role_data.creator
)
return created_role
db.add(new_role)
await db.flush() # 获取生成的ID
await db.refresh(new_role)
await db.commit()
return RoleResponse.model_validate(new_role)
@router.post("/search", response_model=list[RoleResponse])
@ -64,10 +71,11 @@ async def get_role(
current_user: User = Depends(get_current_user),
):
"""获取单个角色"""
role = await role_crud.get(db, {"id": role_id})
result = await db.execute(select(Role).filter(Role.id == role_id))
role = result.scalar_one_or_none()
if not role:
raise HTTPException(status_code=404, detail="角色不存在")
return role
return RoleResponse.model_validate(role)
@router.put("/{role_id}", response_model=RoleResponse)
@ -88,13 +96,25 @@ async def update_role(
if existing_role:
raise HTTPException(status_code=400, detail="角色名已存在")
# 准备更新数据
update_data = role_data.model_dump()
# 使用原生 SQLAlchemy 更新角色FastCRUD 有问题)
from sqlalchemy import update
# 使用FastCRUD更新角色
updated_role = await role_crud.update(db, {"id": role_id}, update_data)
if not updated_role:
stmt = (
update(Role)
.where(Role.id == role_id)
.values(name=role_data.name)
)
result = await db.execute(stmt)
if result.rowcount == 0:
raise HTTPException(status_code=404, detail="角色不存在")
await db.commit()
# 查询更新后的数据
result = await db.execute(select(Role).filter(Role.id == role_id))
updated_role = result.scalar_one_or_none()
return updated_role
@ -115,8 +135,14 @@ async def delete_role(
status_code=400, detail=f"该角色正在被 {len(users)} 个用户使用,无法删除"
)
# 使用FastCRUD删除角色
deleted = await role_crud.delete(db, {"id": role_id})
if not deleted:
# 使用原生 SQLAlchemy 删除角色FastCRUD 有问题)
from sqlalchemy import delete as sql_delete
stmt = sql_delete(Role).where(Role.id == role_id)
result = await db.execute(stmt)
if result.rowcount == 0:
raise HTTPException(status_code=404, detail="角色不存在")
await db.commit()
return {"message": "角色删除成功"}

View File

@ -50,22 +50,23 @@ export const createPermissionCrudOptions: CreateCrudOptions<PermissionRow> = ()
},
addRequest: async ({ form }) => {
await http.post("/permissions/", form);
ElMessage.success({ message: "添加成功", duration: 5000 });
},
editRequest: async ({ form, row }) => {
if (!row) return;
await http.put(`/permissions/${row.id}`, form);
ElMessage.success({ message: "修改成功", duration: 5000 });
},
delRequest: async ({ row }) => {
if (!row) return;
await http.delete(`/permissions/${row.id}`);
ElMessage.success({ message: "删除成功", duration: 5000 });
},
onSuccess: ({ action }: { action: string }) => {
if (action === "add") {
ElMessage.success({ message: "添加成功", duration: 5000 });
} else if (action === "edit") {
ElMessage.success({ message: "修改成功", duration: 5000 });
} else if (action === "del") {
ElMessage.success({ message: "删除成功", duration: 5000 });
}
},
onError: (error: any) => {
@ -81,6 +82,11 @@ export const createPermissionCrudOptions: CreateCrudOptions<PermissionRow> = ()
ElMessage.error({ message, duration: 5000 });
},
},
table: {
remove: {
showSuccessNotification: false,
},
},
rowHandle: {
buttons: {
edit: { text: "编辑" },

View File

@ -46,22 +46,23 @@ export function buildRoleCrudOptions(h: RoleCrudHandlers): CreateCrudOptions<Rol
},
addRequest: async ({ form }) => {
await http.post("/roles/", form);
ElMessage.success({ message: "添加成功", duration: 5000 });
},
editRequest: async ({ form, row }) => {
if (!row) return;
await http.put(`/roles/${row.id}`, form);
ElMessage.success({ message: "修改成功", duration: 5000 });
},
delRequest: async ({ row }) => {
if (!row) return;
await http.delete(`/roles/${row.id}`);
ElMessage.success({ message: "删除成功", duration: 5000 });
},
onSuccess: ({ action }: { action: string }) => {
if (action === "add") {
ElMessage.success({ message: "添加成功", duration: 5000 });
} else if (action === "edit") {
ElMessage.success({ message: "修改成功", duration: 5000 });
} else if (action === "del") {
ElMessage.success({ message: "删除成功", duration: 5000 });
}
},
onError: (error: any) => {
@ -77,6 +78,11 @@ export function buildRoleCrudOptions(h: RoleCrudHandlers): CreateCrudOptions<Rol
ElMessage.error({ message, duration: 5000 });
},
},
table: {
remove: {
showSuccessNotification: false,
},
},
rowHandle: {
width: 300,
buttons: {

View File

@ -1,55 +1,18 @@
<template>
<fs-page>
<fs-crud ref="crudRef" v-bind="crudBinding" @add-success="onAddSuccess" @edit-success="onEditSuccess" @del-success="onDelSuccess" @add-error="onError" @edit-error="onError" @del-error="onError" />
<fs-crud ref="crudRef" v-bind="crudBinding" />
</fs-page>
</template>
<script setup lang="ts">
import { onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import { ElMessage } from "element-plus";
import { createUserCrudOptions } from "./user-crud";
const { crudBinding, crudRef, crudExpose } = useFs({
createCrudOptions: createUserCrudOptions,
});
const onAddSuccess = () => {
ElMessage.success({ message: "添加成功", duration: 5000 });
};
const onEditSuccess = () => {
ElMessage.success({ message: "修改成功", duration: 5000 });
};
const onDelSuccess = () => {
ElMessage.success({ message: "删除成功", duration: 5000 });
};
const onError = (error: any) => {
let message = "操作失败";
if (error.response?.data) {
const data = error.response.data;
if (data.errors && Array.isArray(data.errors)) {
message = data.errors.map((e: any) => e.message).join("");
} else if (data.message) {
message = data.message;
} else if (data.detail) {
const detail = data.detail;
if (typeof detail === "string") {
if (detail.includes("at least 6 characters")) {
message = "密码长度至少为6个字符";
} else {
message = detail;
}
}
}
} else if (error.message) {
message = error.message;
}
ElMessage.error({ message, duration: 5000 });
};
onMounted(() => {
crudExpose.doRefresh();
});

View File

@ -76,31 +76,40 @@ export const createUserCrudOptions: CreateCrudOptions<UserRow> = () => {
},
addRequest: async ({ form }) => {
await http.post("/users/", form);
ElMessage.success({ message: "添加成功", duration: 5000 });
},
editRequest: async ({ form, row }) => {
if (!row) return;
const payload: Record<string, unknown> = { ...form };
if (!payload.password) delete payload.password;
await http.put(`/users/${row.id}`, payload);
ElMessage.success({ message: "修改成功", duration: 5000 });
},
delRequest: async ({ row }) => {
if (!row) return;
await http.delete(`/users/${row.id}`);
ElMessage.success({ message: "删除成功", duration: 5000 });
},
onSuccess: ({ action }: { action: string }) => {
if (action === "add") {
ElMessage.success({ message: "添加成功", duration: 5000 });
} else if (action === "edit") {
ElMessage.success({ message: "修改成功", duration: 5000 });
} else if (action === "del") {
ElMessage.success({ message: "删除成功", duration: 5000 });
}
},
},
table: {
remove: {
showSuccessNotification: false,
},
},
rowHandle: {
buttons: {
edit: { text: "编辑" },
remove: { text: "删除" },
remove: {
text: "删除",
show: true,
},
},
},
columns: {