mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-13 20:51:09 +00:00
@@ -79,3 +79,4 @@ frontend/app/*
|
|||||||
.plan/*
|
.plan/*
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
AGENTS.md
|
AGENTS.md
|
||||||
|
.codebuddy
|
||||||
@@ -23,7 +23,7 @@ class MenuModel(ModelMixin):
|
|||||||
|
|
||||||
__tablename__: str = "platform_menu"
|
__tablename__: str = "platform_menu"
|
||||||
__table_args__: dict[str, str] = {"comment": "平台菜单表"}
|
__table_args__: dict[str, str] = {"comment": "平台菜单表"}
|
||||||
__loader_options__: list[str] = ["roles"]
|
__loader_options__: list[str] = ["roles", "children"]
|
||||||
__permission_strategy__: PermissionFilterStrategy = PermissionFilterStrategy.ROLE_BASED
|
__permission_strategy__: PermissionFilterStrategy = PermissionFilterStrategy.ROLE_BASED
|
||||||
|
|
||||||
name: Mapped[str] = mapped_column(String(50), nullable=False, comment="菜单名称")
|
name: Mapped[str] = mapped_column(String(50), nullable=False, comment="菜单名称")
|
||||||
@@ -115,6 +115,7 @@ class MenuModel(ModelMixin):
|
|||||||
back_populates="parent",
|
back_populates="parent",
|
||||||
foreign_keys="MenuModel.parent_id",
|
foreign_keys="MenuModel.parent_id",
|
||||||
order_by="MenuModel.order",
|
order_by="MenuModel.order",
|
||||||
|
lazy="selectin",
|
||||||
)
|
)
|
||||||
roles: Mapped[list["RoleModel"]] = relationship(
|
roles: Mapped[list["RoleModel"]] = relationship(
|
||||||
secondary="sys_role_menus", back_populates="menus", lazy="selectin"
|
secondary="sys_role_menus", back_populates="menus", lazy="selectin"
|
||||||
|
|||||||
@@ -181,13 +181,22 @@ class MenuUpdateSchema(BaseModel):
|
|||||||
return menu_request_validator(self)
|
return menu_request_validator(self)
|
||||||
|
|
||||||
|
|
||||||
class MenuOutSchema(MenuCreateSchema, BaseSchema):
|
class MenuDetailOutSchema(MenuCreateSchema, BaseSchema):
|
||||||
"""菜单响应模型"""
|
"""菜单详情响应模型(不含 children,用于详情和更新)"""
|
||||||
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
parent_name: str | None = Field(default=None, max_length=50, description="父菜单名称")
|
parent_name: str | None = Field(default=None, max_length=50, description="父菜单名称")
|
||||||
children: list["MenuOutSchema"] | None = Field(default=None, description="子菜单列表")
|
|
||||||
|
|
||||||
|
class MenuTreeOutSchema(MenuDetailOutSchema):
|
||||||
|
"""菜单树形响应模型(含 children,用于树形列表)"""
|
||||||
|
|
||||||
|
children: list["MenuTreeOutSchema"] | None = Field(default=None, description="子菜单列表")
|
||||||
|
|
||||||
|
|
||||||
|
# 兼容旧代码的别名(后续可逐步移除)
|
||||||
|
MenuOutSchema = MenuDetailOutSchema
|
||||||
|
|
||||||
|
|
||||||
class MenuQueryParam:
|
class MenuQueryParam:
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ from app.utils.common_util import (
|
|||||||
from .crud import MenuCRUD
|
from .crud import MenuCRUD
|
||||||
from .schema import (
|
from .schema import (
|
||||||
MenuCreateSchema,
|
MenuCreateSchema,
|
||||||
MenuOutSchema,
|
MenuDetailOutSchema,
|
||||||
MenuQueryParam,
|
MenuQueryParam,
|
||||||
|
MenuTreeOutSchema,
|
||||||
MenuUpdateSchema,
|
MenuUpdateSchema,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ class MenuService:
|
|||||||
raise CustomException(msg="子菜单终端须与父菜单一致(均为 pc 或均为 app)")
|
raise CustomException(msg="子菜单终端须与父菜单一致(均为 pc 或均为 app)")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_menu_detail_service(cls, auth: AuthSchema, id: int) -> MenuOutSchema:
|
async def get_menu_detail_service(cls, auth: AuthSchema, id: int) -> MenuDetailOutSchema:
|
||||||
"""
|
"""
|
||||||
获取菜单详情。
|
获取菜单详情。
|
||||||
|
|
||||||
@@ -75,16 +76,16 @@ class MenuService:
|
|||||||
- id (int): 菜单ID。
|
- id (int): 菜单ID。
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
- dict: 菜单详情对象。
|
- MenuDetailOutSchema: 菜单详情对象。
|
||||||
"""
|
"""
|
||||||
menu = await MenuCRUD(auth).get(id=id)
|
menu = await MenuCRUD(auth).get(id=id, preload=["roles"])
|
||||||
# 创建实例后再设置parent_name属性
|
if not menu:
|
||||||
menu_out = MenuOutSchema.model_validate(menu)
|
raise CustomException(msg="菜单不存在")
|
||||||
if menu and menu.parent_id:
|
menu_out = MenuDetailOutSchema.model_validate(menu)
|
||||||
|
if menu.parent_id:
|
||||||
parent = await MenuCRUD(auth).get(id=menu.parent_id)
|
parent = await MenuCRUD(auth).get(id=menu.parent_id)
|
||||||
if parent:
|
if parent:
|
||||||
menu_out.parent_name = parent.name
|
menu_out.parent_name = parent.name
|
||||||
|
|
||||||
return menu_out
|
return menu_out
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -109,13 +110,13 @@ class MenuService:
|
|||||||
menu_list = await MenuCRUD(auth).get_tree_list(
|
menu_list = await MenuCRUD(auth).get_tree_list(
|
||||||
search=search.__dict__, order_by=order_by
|
search=search.__dict__, order_by=order_by
|
||||||
)
|
)
|
||||||
# 转换为字典列表
|
# 转换为字典列表(使用树形 Schema)
|
||||||
menu_dict_list = [MenuOutSchema.model_validate(menu).model_dump() for menu in menu_list]
|
menu_dict_list = [MenuTreeOutSchema.model_validate(menu).model_dump() for menu in menu_list]
|
||||||
# 使用traversal_to_tree构建树形结构
|
# 使用traversal_to_tree构建树形结构
|
||||||
return traversal_to_tree(menu_dict_list)
|
return traversal_to_tree(menu_dict_list)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create_menu_service(cls, auth: AuthSchema, data: MenuCreateSchema) -> MenuOutSchema:
|
async def create_menu_service(cls, auth: AuthSchema, data: MenuCreateSchema) -> MenuDetailOutSchema:
|
||||||
"""
|
"""
|
||||||
创建菜单。
|
创建菜单。
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ class MenuService:
|
|||||||
- data (MenuCreateSchema): 创建参数对象。
|
- data (MenuCreateSchema): 创建参数对象。
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
- dict: 创建的菜单对象。
|
- MenuDetailOutSchema: 创建的菜单对象。
|
||||||
"""
|
"""
|
||||||
search: dict[str, Any] = {}
|
search: dict[str, Any] = {}
|
||||||
if data.title is not None:
|
if data.title is not None:
|
||||||
@@ -139,11 +140,10 @@ class MenuService:
|
|||||||
await cls._validate_parent_child_client(auth, data.parent_id, data.client)
|
await cls._validate_parent_child_client(auth, data.parent_id, data.client)
|
||||||
|
|
||||||
new_menu = await MenuCRUD(auth).create(data=data)
|
new_menu = await MenuCRUD(auth).create(data=data)
|
||||||
new_menu_dict = MenuOutSchema.model_validate(new_menu)
|
return MenuDetailOutSchema.model_validate(new_menu)
|
||||||
return new_menu_dict
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def update_menu_service(cls, auth: AuthSchema, id: int, data: MenuUpdateSchema) -> MenuOutSchema:
|
async def update_menu_service(cls, auth: AuthSchema, id: int, data: MenuUpdateSchema) -> MenuDetailOutSchema:
|
||||||
"""
|
"""
|
||||||
更新菜单。
|
更新菜单。
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ class MenuService:
|
|||||||
- data (MenuUpdateSchema): 更新参数对象。
|
- data (MenuUpdateSchema): 更新参数对象。
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
- dict: 更新的菜单对象。
|
- MenuDetailOutSchema: 更新的菜单对象。
|
||||||
"""
|
"""
|
||||||
menu = await MenuCRUD(auth).get(id=id)
|
menu = await MenuCRUD(auth).get(id=id)
|
||||||
if not menu:
|
if not menu:
|
||||||
@@ -172,6 +172,7 @@ class MenuService:
|
|||||||
parent_menu = await MenuCRUD(auth).get(id=data.parent_id)
|
parent_menu = await MenuCRUD(auth).get(id=data.parent_id)
|
||||||
if not parent_menu:
|
if not parent_menu:
|
||||||
raise CustomException(msg="更新失败,父级菜单不存在")
|
raise CustomException(msg="更新失败,父级菜单不存在")
|
||||||
|
|
||||||
new_menu = await MenuCRUD(auth).update(id=id, data=data)
|
new_menu = await MenuCRUD(auth).update(id=id, data=data)
|
||||||
|
|
||||||
if data.status is not None:
|
if data.status is not None:
|
||||||
@@ -179,8 +180,12 @@ class MenuService:
|
|||||||
auth=auth, data=BatchSetAvailable(ids=[id], status=data.status)
|
auth=auth, data=BatchSetAvailable(ids=[id], status=data.status)
|
||||||
)
|
)
|
||||||
|
|
||||||
new_menu_dict = MenuOutSchema.model_validate(new_menu)
|
menu_out = MenuDetailOutSchema.model_validate(new_menu)
|
||||||
return new_menu_dict
|
if menu_out.parent_id:
|
||||||
|
parent = await MenuCRUD(auth).get(id=menu_out.parent_id)
|
||||||
|
if parent:
|
||||||
|
menu_out.parent_name = parent.name
|
||||||
|
return menu_out
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def delete_menu_service(cls, auth: AuthSchema, ids: list[int]) -> None:
|
async def delete_menu_service(cls, auth: AuthSchema, ids: list[int]) -> None:
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class TenantService:
|
|||||||
"password": PwdUtil.set_password_hash(password=password),
|
"password": PwdUtil.set_password_hash(password=password),
|
||||||
"name": f"{tenant_obj.name}管理员",
|
"name": f"{tenant_obj.name}管理员",
|
||||||
"tenant_id": tenant_obj.id,
|
"tenant_id": tenant_obj.id,
|
||||||
"status": "0",
|
"status": 0,
|
||||||
"is_superuser": False,
|
"is_superuser": False,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class DeptModel(ModelMixin, TenantMixin):
|
|||||||
|
|
||||||
__tablename__: str = "sys_dept"
|
__tablename__: str = "sys_dept"
|
||||||
__table_args__ = (UniqueConstraint("tenant_id", "code"), {"comment": "部门表"})
|
__table_args__ = (UniqueConstraint("tenant_id", "code"), {"comment": "部门表"})
|
||||||
__loader_options__: list[str] = []
|
__loader_options__: list[str] = ["children"]
|
||||||
__permission_strategy__: PermissionFilterStrategy = PermissionFilterStrategy.DEPT_BASED
|
__permission_strategy__: PermissionFilterStrategy = PermissionFilterStrategy.DEPT_BASED
|
||||||
|
|
||||||
name: Mapped[str] = mapped_column(String(64), nullable=False, comment="部门名称")
|
name: Mapped[str] = mapped_column(String(64), nullable=False, comment="部门名称")
|
||||||
|
|||||||
@@ -46,13 +46,22 @@ class DeptUpdateSchema(DeptCreateSchema):
|
|||||||
"""部门更新模型"""
|
"""部门更新模型"""
|
||||||
|
|
||||||
|
|
||||||
class DeptOutSchema(DeptCreateSchema, BaseSchema):
|
class DeptDetailOutSchema(DeptCreateSchema, BaseSchema):
|
||||||
"""部门响应模型"""
|
"""部门详情响应模型(不含 children,用于详情和更新)"""
|
||||||
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
parent_name: str | None = Field(default=None, max_length=64, description="父部门名称")
|
parent_name: str | None = Field(default=None, max_length=64, description="父部门名称")
|
||||||
children: list["DeptOutSchema"] | None = Field(default=None, description="子部门列表")
|
|
||||||
|
|
||||||
|
class DeptTreeOutSchema(DeptDetailOutSchema):
|
||||||
|
"""部门树形响应模型(含 children,用于树形列表)"""
|
||||||
|
|
||||||
|
children: list["DeptTreeOutSchema"] | None = Field(default=None, description="子部门列表")
|
||||||
|
|
||||||
|
|
||||||
|
# 兼容旧代码的别名(后续可逐步移除)
|
||||||
|
DeptOutSchema = DeptDetailOutSchema
|
||||||
|
|
||||||
|
|
||||||
class DeptQueryParam:
|
class DeptQueryParam:
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ from app.utils.common_util import (
|
|||||||
from .crud import DeptCRUD
|
from .crud import DeptCRUD
|
||||||
from .schema import (
|
from .schema import (
|
||||||
DeptCreateSchema,
|
DeptCreateSchema,
|
||||||
DeptOutSchema,
|
DeptDetailOutSchema,
|
||||||
DeptQueryParam,
|
DeptQueryParam,
|
||||||
|
DeptTreeOutSchema,
|
||||||
DeptUpdateSchema,
|
DeptUpdateSchema,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ class DeptService:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_dept_detail_service(cls, auth: AuthSchema, id: int) -> DeptOutSchema:
|
async def get_dept_detail_service(cls, auth: AuthSchema, id: int) -> DeptDetailOutSchema:
|
||||||
"""
|
"""
|
||||||
获取部门详情。
|
获取部门详情。
|
||||||
|
|
||||||
@@ -31,20 +32,17 @@ class DeptService:
|
|||||||
- id (int): 部门 ID。
|
- id (int): 部门 ID。
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
- dict: 部门详情对象。
|
- DeptDetailOutSchema: 部门详情对象。
|
||||||
"""
|
"""
|
||||||
dept = await DeptCRUD(auth).get(id=id)
|
dept = await DeptCRUD(auth).get(id=id)
|
||||||
if not dept:
|
if not dept:
|
||||||
raise CustomException(msg="部门不存在")
|
raise CustomException(msg="部门不存在")
|
||||||
# 从列属性构建 dict,避免 Pydantic 访问 ORM 关系触发 async 惰性加载
|
dept_out = DeptDetailOutSchema.model_validate(dept)
|
||||||
dept_dict = {c.name: getattr(dept, c.name) for c in dept.__table__.columns}
|
|
||||||
dept_dict["children"] = None
|
|
||||||
dept_dict["parent_name"] = None
|
|
||||||
if dept.parent_id:
|
if dept.parent_id:
|
||||||
parent = await DeptCRUD(auth).get(id=dept.parent_id)
|
parent = await DeptCRUD(auth).get(id=dept.parent_id)
|
||||||
if parent:
|
if parent:
|
||||||
dept_dict["parent_name"] = parent.name
|
dept_out.parent_name = parent.name
|
||||||
return DeptOutSchema(**dept_dict)
|
return dept_out
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_dept_tree_service(
|
async def get_dept_tree_service(
|
||||||
@@ -68,13 +66,13 @@ class DeptService:
|
|||||||
dept_list = await DeptCRUD(auth).get_tree_list(
|
dept_list = await DeptCRUD(auth).get_tree_list(
|
||||||
search=search.__dict__ if search else {}, order_by=order_by
|
search=search.__dict__ if search else {}, order_by=order_by
|
||||||
)
|
)
|
||||||
# 转换为字典列表,tree_list 已通过 selectin 预加载 children
|
# 转换为字典列表(使用树形 Schema),tree_list 已通过 selectin 预加载 children
|
||||||
dept_dict_list = [DeptOutSchema.model_validate(dept).model_dump() for dept in dept_list]
|
dept_dict_list = [DeptTreeOutSchema.model_validate(dept).model_dump() for dept in dept_list]
|
||||||
# 仅保留根节点,子树已在 model_dump 中递归序列化
|
# 仅保留根节点,子树已在 model_dump 中递归序列化
|
||||||
return [d for d in dept_dict_list if d.get("parent_id") is None]
|
return [d for d in dept_dict_list if d.get("parent_id") is None]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create_dept_service(cls, auth: AuthSchema, data: DeptCreateSchema) -> DeptOutSchema:
|
async def create_dept_service(cls, auth: AuthSchema, data: DeptCreateSchema) -> DeptDetailOutSchema:
|
||||||
"""
|
"""
|
||||||
创建部门。
|
创建部门。
|
||||||
|
|
||||||
@@ -83,7 +81,7 @@ class DeptService:
|
|||||||
- data (DeptCreateSchema): 部门创建对象。
|
- data (DeptCreateSchema): 部门创建对象。
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
- dict: 新创建的部门对象。
|
- DeptDetailOutSchema: 新创建的部门对象。
|
||||||
|
|
||||||
异常:
|
异常:
|
||||||
- CustomException: 当部门已存在时抛出。
|
- CustomException: 当部门已存在时抛出。
|
||||||
@@ -100,10 +98,10 @@ class DeptService:
|
|||||||
await TenantService.check_quota_service(auth, auth.tenant_id, "dept")
|
await TenantService.check_quota_service(auth, auth.tenant_id, "dept")
|
||||||
|
|
||||||
dept = await DeptCRUD(auth).create(data=data)
|
dept = await DeptCRUD(auth).create(data=data)
|
||||||
return DeptOutSchema.model_validate(dept)
|
return DeptDetailOutSchema.model_validate(dept)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def update_dept_service(cls, auth: AuthSchema, id: int, data: DeptUpdateSchema) -> DeptOutSchema:
|
async def update_dept_service(cls, auth: AuthSchema, id: int, data: DeptUpdateSchema) -> DeptDetailOutSchema:
|
||||||
"""
|
"""
|
||||||
更新部门。
|
更新部门。
|
||||||
|
|
||||||
@@ -113,7 +111,7 @@ class DeptService:
|
|||||||
- data (DeptUpdateSchema): 部门更新对象。
|
- data (DeptUpdateSchema): 部门更新对象。
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
- dict: 更新后的部门对象。
|
- DeptDetailOutSchema: 更新后的部门对象。
|
||||||
|
|
||||||
异常:
|
异常:
|
||||||
- CustomException: 当部门不存在或名称重复时抛出。
|
- CustomException: 当部门不存在或名称重复时抛出。
|
||||||
@@ -127,8 +125,14 @@ class DeptService:
|
|||||||
exist_code = await DeptCRUD(auth).get(code=data.code)
|
exist_code = await DeptCRUD(auth).get(code=data.code)
|
||||||
if exist_code and exist_code.id != id:
|
if exist_code and exist_code.id != id:
|
||||||
raise CustomException(msg="更新失败,部门编码已存在")
|
raise CustomException(msg="更新失败,部门编码已存在")
|
||||||
|
|
||||||
dept = await DeptCRUD(auth).update(id=id, data=data)
|
dept = await DeptCRUD(auth).update(id=id, data=data)
|
||||||
return DeptOutSchema.model_validate(dept)
|
dept_out = DeptDetailOutSchema.model_validate(dept)
|
||||||
|
if dept_out.parent_id:
|
||||||
|
parent = await DeptCRUD(auth).get(id=dept_out.parent_id)
|
||||||
|
if parent:
|
||||||
|
dept_out.parent_name = parent.name
|
||||||
|
return dept_out
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def delete_dept_service(cls, auth: AuthSchema, ids: list[int]) -> None:
|
async def delete_dept_service(cls, auth: AuthSchema, ids: list[int]) -> None:
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class NoticeService:
|
|||||||
offset=0,
|
offset=0,
|
||||||
limit=10,
|
limit=10,
|
||||||
order_by=[{"id": "asc"}],
|
order_by=[{"id": "asc"}],
|
||||||
search={"status": "0"},
|
search={"status": 0},
|
||||||
out_schema=NoticeOutSchema,
|
out_schema=NoticeOutSchema,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ class PositionService:
|
|||||||
# 复制数据并转换状态
|
# 复制数据并转换状态
|
||||||
data = position_list.copy()
|
data = position_list.copy()
|
||||||
for item in data:
|
for item in data:
|
||||||
item["status"] = "启用" if item.get("status") == "0" else "停用"
|
item["status"] = "启用" if item.get("status") == 0 else "停用"
|
||||||
item["creator"] = (
|
item["creator"] = (
|
||||||
item.get("created_by", {}).get("name", "未知")
|
item.get("created_by", {}).get("name", "未知")
|
||||||
if isinstance(item.get("created_by"), dict)
|
if isinstance(item.get("created_by"), dict)
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ class RoleService:
|
|||||||
# 处理数据
|
# 处理数据
|
||||||
data = role_list.copy()
|
data = role_list.copy()
|
||||||
for item in data:
|
for item in data:
|
||||||
item["status"] = "启用" if item.get("status") == "0" else "停用"
|
item["status"] = "启用" if item.get("status") == 0 else "停用"
|
||||||
item["data_scope"] = data_scope_map.get(item.get("data_scope", 1), "")
|
item["data_scope"] = data_scope_map.get(item.get("data_scope", 1), "")
|
||||||
|
|
||||||
return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict)
|
return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict)
|
||||||
|
|||||||
@@ -234,12 +234,37 @@ class UserCreateSchema(CurrentUserUpdateSchema):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateSchema(UserCreateSchema):
|
class UserUpdateSchema(CurrentUserUpdateSchema):
|
||||||
"""更新"""
|
"""更新"""
|
||||||
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
last_login: DateTimeStr | None = Field(default=None, description="最后登录时间")
|
username: str | None = Field(default=None, max_length=32, description="用户名")
|
||||||
|
status: int | None = Field(default=None, ge=0, le=1, description="状态(0:正常 1:禁用)")
|
||||||
|
description: str | None = Field(default=None, max_length=255, description="备注")
|
||||||
|
dept_id: int | None = Field(default=None, description="部门ID")
|
||||||
|
role_ids: list[int] | None = Field(default=[], description="角色ID列表")
|
||||||
|
position_ids: list[int] | None = Field(default=[], description="岗位ID列表")
|
||||||
|
|
||||||
|
@field_validator("status")
|
||||||
|
@classmethod
|
||||||
|
def validate_status(cls, value: int | None):
|
||||||
|
"""校验状态:仅支持 0(正常)、1(禁用)"""
|
||||||
|
if value is not None and value not in {0, 1}:
|
||||||
|
raise ValueError("状态仅支持 0(正常) 或 1(禁用)")
|
||||||
|
return value
|
||||||
|
|
||||||
|
@field_validator("username")
|
||||||
|
@classmethod
|
||||||
|
def validate_username(cls, value: str | None):
|
||||||
|
"""校验账号:字母开头,2-32 位"""
|
||||||
|
if not value:
|
||||||
|
return value
|
||||||
|
v = value.strip()
|
||||||
|
import re
|
||||||
|
if not re.match(r"^[A-Za-z][A-Za-z0-9_.-]{1,31}$", v):
|
||||||
|
raise ValueError("账号需以字母开头,2-32 位,仅允许字母、数字、_ . -")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
class UserOutSchema(UserUpdateSchema, BaseSchema, UserBySchema, TenantBySchema):
|
class UserOutSchema(UserUpdateSchema, BaseSchema, UserBySchema, TenantBySchema):
|
||||||
|
|||||||
@@ -199,12 +199,8 @@ class UserService:
|
|||||||
if dept.status == 1:
|
if dept.status == 1:
|
||||||
raise CustomException(msg="部门已被禁用")
|
raise CustomException(msg="部门已被禁用")
|
||||||
|
|
||||||
# 更新用户 - 排除不应被修改的字段, 更新不更新密码
|
# 更新用户
|
||||||
user_dict = data.model_dump(
|
new_user = await UserCRUD(auth).update(id=id, data=data)
|
||||||
exclude_unset=True,
|
|
||||||
exclude={"role_ids", "position_ids", "last_login", "password"},
|
|
||||||
)
|
|
||||||
new_user = await UserCRUD(auth).update(id=id, data=user_dict)
|
|
||||||
|
|
||||||
# 更新角色和岗位
|
# 更新角色和岗位
|
||||||
if data.role_ids and len(data.role_ids) > 0:
|
if data.role_ids and len(data.role_ids) > 0:
|
||||||
@@ -293,7 +289,7 @@ class UserService:
|
|||||||
if auth.user and auth.user.is_superuser:
|
if auth.user and auth.user.is_superuser:
|
||||||
# 使用树形结构查询,预加载children关系(含 type=3 按钮,供前端权限列表使用)
|
# 使用树形结构查询,预加载children关系(含 type=3 按钮,供前端权限列表使用)
|
||||||
menu_all = await MenuCRUD(auth).get_tree_list(
|
menu_all = await MenuCRUD(auth).get_tree_list(
|
||||||
search={"type": ("in", [1, 2, 3, 4]), "status": "0", **_pc_only},
|
search={"type": ("in", [1, 2, 3, 4]), "status": 0, **_pc_only},
|
||||||
order_by=[{"order": "asc"}],
|
order_by=[{"order": "asc"}],
|
||||||
)
|
)
|
||||||
menus = [MenuOutSchema.model_validate(menu) for menu in menu_all]
|
menus = [MenuOutSchema.model_validate(menu) for menu in menu_all]
|
||||||
@@ -703,7 +699,7 @@ class UserService:
|
|||||||
# creator = {'id': 1, 'name': '管理员', 'username': 'admin'}
|
# creator = {'id': 1, 'name': '管理员', 'username': 'admin'}
|
||||||
data = user_list.copy()
|
data = user_list.copy()
|
||||||
for item in data:
|
for item in data:
|
||||||
item["status"] = "启用" if item.get("status") == "0" else "停用"
|
item["status"] = "启用" if item.get("status") == 0 else "停用"
|
||||||
gender = item.get("gender")
|
gender = item.get("gender")
|
||||||
item["gender"] = "男" if gender == "1" else ("女" if gender == "2" else "未知")
|
item["gender"] = "男" if gender == "1" else ("女" if gender == "2" else "未知")
|
||||||
item["is_superuser"] = "是" if item.get("is_superuser") else "否"
|
item["is_superuser"] = "是" if item.get("is_superuser") else "否"
|
||||||
|
|||||||
@@ -233,9 +233,9 @@ class Settings(BaseSettings):
|
|||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ******************* AI大模型配置 ****************** #
|
# ******************* AI大模型配置 ****************** #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
OPENAI_BASE_URL: str = ""
|
|
||||||
OPENAI_API_KEY: str = ""
|
OPENAI_API_KEY: str = ""
|
||||||
OPENAI_MODEL: str = ""
|
OPENAI_MODEL: str = ""
|
||||||
|
OPENAI_BASE_URL: str = "" # API Base URL,如 https://api.minimax.chat/v1
|
||||||
|
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ******************* ChromaDB配置 ****************** #
|
# ******************* ChromaDB配置 ****************** #
|
||||||
|
|||||||
@@ -359,7 +359,8 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
obj_dict = data.model_dump(exclude_unset=True, exclude={"id"})
|
obj_dict = data.model_dump(exclude_unset=True, exclude={"id"})
|
||||||
obj = await self._get_one(self.db, id=id, preload=[])
|
model_defaults = getattr(self.model, "__loader_options__", [])
|
||||||
|
obj = await self._get_one(self.db, id=id, preload=model_defaults)
|
||||||
if not obj:
|
if not obj:
|
||||||
raise CustomException(msg="更新对象不存在")
|
raise CustomException(msg="更新对象不存在")
|
||||||
|
|
||||||
|
|||||||
@@ -216,8 +216,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBarWithAudit> | null>(null)
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** 业务搜索项(审计四字段由 FaSearchBarWithAudit 自动追加) */
|
/** 业务搜索项(审计四字段由 FaSearchBarWithAudit 自动追加) */
|
||||||
@@ -504,7 +504,7 @@ const dialogFormItems = computed<FormItem[]>(() => [
|
|||||||
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
|
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
|
||||||
{% if column.column_name not in ['uuid', 'created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id', 'tenant_id'] and column.column_name != pk_column_name %}
|
{% if column.column_name not in ['uuid', 'created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id', 'tenant_id'] and column.column_name != pk_column_name %}
|
||||||
{% if column.column_name == "status" %}
|
{% if column.column_name == "status" %}
|
||||||
{ key: "status", label: "状态", type: "radio", props: { options: [{ label: "启用", value: "0" }, { label: "停用", value: "1" }] } },
|
{ key: "status", label: "状态", type: "radio", props: { options: [{ label: "启用", value: 0 }, { label: "停用", value: 1 }] } },
|
||||||
{% elif column.column_name == "description" %}
|
{% elif column.column_name == "description" %}
|
||||||
{ key: "description", label: "描述", type: "textarea", placeholder: "请输入描述" },
|
{ key: "description", label: "描述", type: "textarea", placeholder: "请输入描述" },
|
||||||
{% elif column.python_type == "int" or column.python_type == "float" or column.python_type == "Decimal" %}
|
{% elif column.python_type == "int" or column.python_type == "float" or column.python_type == "Decimal" %}
|
||||||
|
|||||||
Vendored
-15
@@ -44,23 +44,8 @@ REDIS_DB_NAME = 1
|
|||||||
LOGGER_LEVEL = "DEBUG" # 日志级别
|
LOGGER_LEVEL = "DEBUG" # 日志级别
|
||||||
|
|
||||||
# 大模型配置
|
# 大模型配置
|
||||||
OPENAI_BASE_URL = "https://api.deepseek.com"
|
|
||||||
OPENAI_API_KEY = "your_api_key"
|
OPENAI_API_KEY = "your_api_key"
|
||||||
OPENAI_MODEL = "deepseek-v4-flash"
|
OPENAI_MODEL = "deepseek-v4-flash"
|
||||||
|
|
||||||
# IP 归属地查询(登录时对外请求第三方 API,生产建议关闭)
|
# IP 归属地查询(登录时对外请求第三方 API,生产建议关闭)
|
||||||
IP_LOCATION_ENABLE=false
|
IP_LOCATION_ENABLE=false
|
||||||
# 支付配置
|
|
||||||
PAYMENT_GATEWAY = "mock" # alipay / wxpay / mock(开发环境默认 mock)
|
|
||||||
PAYMENT_ALIPAY_APP_ID = ""
|
|
||||||
PAYMENT_ALIPAY_PRIVATE_KEY = ""
|
|
||||||
PAYMENT_ALIPAY_PUBLIC_KEY = ""
|
|
||||||
PAYMENT_ALIPAY_SANDBOX = True
|
|
||||||
PAYMENT_WXPAY_APP_ID = ""
|
|
||||||
PAYMENT_WXPAY_MCH_ID = ""
|
|
||||||
PAYMENT_WXPAY_API_KEY = ""
|
|
||||||
SITE_URL = "http://localhost:8001"
|
|
||||||
|
|
||||||
# 安全配置(生产环境务必覆盖)
|
|
||||||
SECRET_KEY = "your-secret-key-change-in-production"
|
|
||||||
ALLOW_ORIGINS = ["http://localhost:5173", "http://localhost:4173", "http://127.0.0.1:5173"]
|
|
||||||
|
|||||||
Vendored
-17
@@ -44,25 +44,8 @@ REDIS_DB_NAME = 1
|
|||||||
LOGGER_LEVEL = 'INFO'
|
LOGGER_LEVEL = 'INFO'
|
||||||
|
|
||||||
# 大模型配置
|
# 大模型配置
|
||||||
OPENAI_BASE_URL = "https://api.deepseek.com"
|
|
||||||
OPENAI_API_KEY = "your_api_key"
|
OPENAI_API_KEY = "your_api_key"
|
||||||
OPENAI_MODEL = "deepseek-v4-flash"
|
OPENAI_MODEL = "deepseek-v4-flash"
|
||||||
|
|
||||||
# IP 归属地查询(登录时对外请求第三方 API,生产建议关闭)
|
# IP 归属地查询(登录时对外请求第三方 API,生产建议关闭)
|
||||||
IP_LOCATION_ENABLE=false
|
IP_LOCATION_ENABLE=false
|
||||||
# 支付配置
|
|
||||||
PAYMENT_GATEWAY = "alipay" # alipay / wxpay / mock(生产环境使用真实支付)
|
|
||||||
PAYMENT_ALIPAY_APP_ID = ""
|
|
||||||
PAYMENT_ALIPAY_PRIVATE_KEY = ""
|
|
||||||
PAYMENT_ALIPAY_PUBLIC_KEY = ""
|
|
||||||
PAYMENT_ALIPAY_SANDBOX = False
|
|
||||||
PAYMENT_WXPAY_APP_ID = ""
|
|
||||||
PAYMENT_WXPAY_MCH_ID = ""
|
|
||||||
PAYMENT_WXPAY_API_KEY = ""
|
|
||||||
SITE_URL = "https://your-domain.com"
|
|
||||||
|
|
||||||
# CORS 配置(生产环境需要配置具体域名)
|
|
||||||
ALLOW_ORIGINS = ["https://your-domain.com", "https://admin.your-domain.com"]
|
|
||||||
|
|
||||||
# 安全配置(生产环境务必修改)
|
|
||||||
SECRET_KEY = "your-secret-key-change-in-production"
|
|
||||||
|
|||||||
@@ -160,3 +160,119 @@ src/
|
|||||||
|
|
||||||
- **格式化与校验**:`pnpm lint`
|
- **格式化与校验**:`pnpm lint`
|
||||||
- **提交**:husky、lint-staged、commitlint;可使用 **`pnpm commit`**(Commitizen / cz-git)
|
- **提交**:husky、lint-staged、commitlint;可使用 **`pnpm commit`**(Commitizen / cz-git)
|
||||||
|
|
||||||
|
/\*\*
|
||||||
|
|
||||||
|
- 浅蓝色系主题 — #1975FC
|
||||||
|
-
|
||||||
|
- 来源:花瓣网 B端淡蓝色配色参考
|
||||||
|
- 主色 #1975FC — 明快透亮、B端专业感
|
||||||
|
-
|
||||||
|
- 【启用方式】在本文件中取消注释对应主题模式的变量块,
|
||||||
|
- 并在 src/styles/index.scss 中 @use "./core/theme-blue";
|
||||||
|
- 同时在 src/config/setting.ts 中将 themeColor 改为 "#1975FC"。
|
||||||
|
-
|
||||||
|
- 【色阶 palette】
|
||||||
|
- #f8fcff 最浅底色(页面背景)
|
||||||
|
- #f0f5ff 浅蓝悬停态
|
||||||
|
- #e5f2ff 选中态 / tag 背景
|
||||||
|
- #c5ddff 浅蓝边框 / 分割线点缀
|
||||||
|
- #82b8ff 中浅蓝(图标辅助色)
|
||||||
|
- #1975FC ★ 主色
|
||||||
|
- #115ac8 深蓝(hover 按下态)
|
||||||
|
-
|
||||||
|
- 【功能色】
|
||||||
|
- success: #16a34a
|
||||||
|
- warning: #d97706
|
||||||
|
- danger: #dc2626
|
||||||
|
- error: #fa896b
|
||||||
|
- info: #8b92a0
|
||||||
|
-
|
||||||
|
- ================================================================
|
||||||
|
\*/
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// 亮色主题 — 浅蓝色系
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
// :root {
|
||||||
|
// /_ ---- 侧边栏 ---- _/
|
||||||
|
// --sidebar-bg-color: #ffffff;
|
||||||
|
// --sidebar-border-color: #edf0f2;
|
||||||
|
// --sidebar-text-color: #485260;
|
||||||
|
// --sidebar-text-hover-color: #1975fc;
|
||||||
|
// --sidebar-text-active-color: #1975fc;
|
||||||
|
// --sidebar-bg-hover-color: #f0f5ff;
|
||||||
|
// --sidebar-bg-active-color: #e5f2ff;
|
||||||
|
// --sidebar-logo-color: #121926;
|
||||||
|
// --sidebar-section-title-color: #8b92a0;
|
||||||
|
//
|
||||||
|
// /_ ---- 页面 ---- _/
|
||||||
|
// --main-bg-color: #f4f6f9;
|
||||||
|
// --header-bg-color: #ffffff;
|
||||||
|
// --header-border-color: #edf0f2;
|
||||||
|
//
|
||||||
|
// /_ ---- 卡片 ---- _/
|
||||||
|
// --card-bg-color: #ffffff;
|
||||||
|
// --card-border-color: #edf0f2;
|
||||||
|
//
|
||||||
|
// /_ ---- 表格 ---- _/
|
||||||
|
// --table-header-bg: #f8fafc;
|
||||||
|
// --table-header-color: #8b92a0;
|
||||||
|
// --table-row-hover-bg: #f8fafc;
|
||||||
|
// --table-border-color: #edf0f2;
|
||||||
|
//
|
||||||
|
// /_ ---- 文字 ---- _/
|
||||||
|
// --text-primary: #121926;
|
||||||
|
// --text-regular: #334155;
|
||||||
|
// --text-secondary: #8b92a0;
|
||||||
|
//
|
||||||
|
// /_ ---- 标签 ---- _/
|
||||||
|
// --tag-bg-color: #e5f2ff;
|
||||||
|
// --tag-text-color: #1975fc;
|
||||||
|
//
|
||||||
|
// /_ ---- 按钮 —— 由 Element Plus 通过 primary 色阶自动生成 ---- _/
|
||||||
|
// /_ ---- 进度条 ---- _/
|
||||||
|
// --nprogress-color: #1975fc;
|
||||||
|
//
|
||||||
|
// /_ ---- 边框模式 ---- _/
|
||||||
|
// --fa-card-border: #edf0f2;
|
||||||
|
// --default-box-color: #ffffff;
|
||||||
|
// --default-border: #edf0f2;
|
||||||
|
// --custom-radius: 0.75rem;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// 暗色主题 — 浅蓝色系(可选)
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
// html.dark {
|
||||||
|
// --sidebar-bg-color: #0c1031;
|
||||||
|
// --sidebar-border-color: #1a1e3d;
|
||||||
|
// --sidebar-text-color: #a8adcc;
|
||||||
|
// --sidebar-text-hover-color: #5c9fff;
|
||||||
|
// --sidebar-text-active-color: #1975fc;
|
||||||
|
// --sidebar-bg-hover-color: rgba(25, 117, 252, 0.15);
|
||||||
|
// --sidebar-bg-active-color: rgba(25, 117, 252, 0.25);
|
||||||
|
// --sidebar-logo-color: #ffffff;
|
||||||
|
// --sidebar-section-title-color: #6b7094;
|
||||||
|
//
|
||||||
|
// --main-bg-color: #0f1420;
|
||||||
|
// --header-bg-color: #151a2d;
|
||||||
|
// --header-border-color: #1a1e3d;
|
||||||
|
//
|
||||||
|
// --card-bg-color: #151a2d;
|
||||||
|
// --card-border-color: #1a1e3d;
|
||||||
|
//
|
||||||
|
// --table-header-bg: #1a1e3d;
|
||||||
|
// --table-header-color: #6b7094;
|
||||||
|
// --table-row-hover-bg: #1a1e3d;
|
||||||
|
// --table-border-color: #1a1e3d;
|
||||||
|
//
|
||||||
|
// --text-primary: #e5e7eb;
|
||||||
|
// --text-regular: #a8adcc;
|
||||||
|
// --text-secondary: #6b7094;
|
||||||
|
//
|
||||||
|
// --tag-bg-color: rgba(25, 117, 252, 0.2);
|
||||||
|
// --tag-text-color: #5c9fff;
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
/**
|
|
||||||
* 浅蓝色系主题 — #1975FC
|
|
||||||
*
|
|
||||||
* 来源:花瓣网 B端淡蓝色配色参考
|
|
||||||
* 主色 #1975FC — 明快透亮、B端专业感
|
|
||||||
*
|
|
||||||
* 【启用方式】在本文件中取消注释对应主题模式的变量块,
|
|
||||||
* 并在 src/styles/index.scss 中 @use "./core/theme-blue";
|
|
||||||
* 同时在 src/config/setting.ts 中将 themeColor 改为 "#1975FC"。
|
|
||||||
*
|
|
||||||
* 【色阶 palette】
|
|
||||||
* #f8fcff 最浅底色(页面背景)
|
|
||||||
* #f0f5ff 浅蓝悬停态
|
|
||||||
* #e5f2ff 选中态 / tag 背景
|
|
||||||
* #c5ddff 浅蓝边框 / 分割线点缀
|
|
||||||
* #82b8ff 中浅蓝(图标辅助色)
|
|
||||||
* #1975FC ★ 主色
|
|
||||||
* #115ac8 深蓝(hover 按下态)
|
|
||||||
*
|
|
||||||
* 【功能色】
|
|
||||||
* success: #16a34a
|
|
||||||
* warning: #d97706
|
|
||||||
* danger: #dc2626
|
|
||||||
* error: #fa896b
|
|
||||||
* info: #8b92a0
|
|
||||||
*
|
|
||||||
* ================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ================================================================
|
|
||||||
// 亮色主题 — 浅蓝色系
|
|
||||||
// ================================================================
|
|
||||||
|
|
||||||
// :root {
|
|
||||||
// /* ---- 侧边栏 ---- */
|
|
||||||
// --sidebar-bg-color: #ffffff;
|
|
||||||
// --sidebar-border-color: #edf0f2;
|
|
||||||
// --sidebar-text-color: #485260;
|
|
||||||
// --sidebar-text-hover-color: #1975fc;
|
|
||||||
// --sidebar-text-active-color: #1975fc;
|
|
||||||
// --sidebar-bg-hover-color: #f0f5ff;
|
|
||||||
// --sidebar-bg-active-color: #e5f2ff;
|
|
||||||
// --sidebar-logo-color: #121926;
|
|
||||||
// --sidebar-section-title-color: #8b92a0;
|
|
||||||
//
|
|
||||||
// /* ---- 页面 ---- */
|
|
||||||
// --main-bg-color: #f4f6f9;
|
|
||||||
// --header-bg-color: #ffffff;
|
|
||||||
// --header-border-color: #edf0f2;
|
|
||||||
//
|
|
||||||
// /* ---- 卡片 ---- */
|
|
||||||
// --card-bg-color: #ffffff;
|
|
||||||
// --card-border-color: #edf0f2;
|
|
||||||
//
|
|
||||||
// /* ---- 表格 ---- */
|
|
||||||
// --table-header-bg: #f8fafc;
|
|
||||||
// --table-header-color: #8b92a0;
|
|
||||||
// --table-row-hover-bg: #f8fafc;
|
|
||||||
// --table-border-color: #edf0f2;
|
|
||||||
//
|
|
||||||
// /* ---- 文字 ---- */
|
|
||||||
// --text-primary: #121926;
|
|
||||||
// --text-regular: #334155;
|
|
||||||
// --text-secondary: #8b92a0;
|
|
||||||
//
|
|
||||||
// /* ---- 标签 ---- */
|
|
||||||
// --tag-bg-color: #e5f2ff;
|
|
||||||
// --tag-text-color: #1975fc;
|
|
||||||
//
|
|
||||||
// /* ---- 按钮 —— 由 Element Plus 通过 primary 色阶自动生成 ---- */
|
|
||||||
// /* ---- 进度条 ---- */
|
|
||||||
// --nprogress-color: #1975fc;
|
|
||||||
//
|
|
||||||
// /* ---- 边框模式 ---- */
|
|
||||||
// --fa-card-border: #edf0f2;
|
|
||||||
// --default-box-color: #ffffff;
|
|
||||||
// --default-border: #edf0f2;
|
|
||||||
// --custom-radius: 0.75rem;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ================================================================
|
|
||||||
// 暗色主题 — 浅蓝色系(可选)
|
|
||||||
// ================================================================
|
|
||||||
|
|
||||||
// html.dark {
|
|
||||||
// --sidebar-bg-color: #0c1031;
|
|
||||||
// --sidebar-border-color: #1a1e3d;
|
|
||||||
// --sidebar-text-color: #a8adcc;
|
|
||||||
// --sidebar-text-hover-color: #5c9fff;
|
|
||||||
// --sidebar-text-active-color: #1975fc;
|
|
||||||
// --sidebar-bg-hover-color: rgba(25, 117, 252, 0.15);
|
|
||||||
// --sidebar-bg-active-color: rgba(25, 117, 252, 0.25);
|
|
||||||
// --sidebar-logo-color: #ffffff;
|
|
||||||
// --sidebar-section-title-color: #6b7094;
|
|
||||||
//
|
|
||||||
// --main-bg-color: #0f1420;
|
|
||||||
// --header-bg-color: #151a2d;
|
|
||||||
// --header-border-color: #1a1e3d;
|
|
||||||
//
|
|
||||||
// --card-bg-color: #151a2d;
|
|
||||||
// --card-border-color: #1a1e3d;
|
|
||||||
//
|
|
||||||
// --table-header-bg: #1a1e3d;
|
|
||||||
// --table-header-color: #6b7094;
|
|
||||||
// --table-row-hover-bg: #1a1e3d;
|
|
||||||
// --table-border-color: #1a1e3d;
|
|
||||||
//
|
|
||||||
// --text-primary: #e5e7eb;
|
|
||||||
// --text-regular: #a8adcc;
|
|
||||||
// --text-secondary: #6b7094;
|
|
||||||
//
|
|
||||||
// --tag-bg-color: rgba(25, 117, 252, 0.2);
|
|
||||||
// --tag-text-color: #5c9fff;
|
|
||||||
// }
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fa-card p-5 pb-3 h-53 max-sm:h-48 flex flex-col">
|
<div class="fa-card p-5 pb-3 h-53 max-sm:h-53 flex flex-col">
|
||||||
<div class="fa-card-header">
|
<div class="fa-card-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h4>
|
<h4>
|
||||||
|
|||||||
@@ -458,8 +458,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const menuSearchItems = computed<SearchFormItem[]>(() => [
|
const menuSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -107,7 +107,13 @@
|
|||||||
|
|
||||||
<!-- 订单详情弹窗 -->
|
<!-- 订单详情弹窗 -->
|
||||||
<FaDialog v-model="detailVisible" title="订单详情" width="560px" :show-footer="false">
|
<FaDialog v-model="detailVisible" title="订单详情" width="560px" :show-footer="false">
|
||||||
<FaDescriptions v-if="orderDetail" :column="2" border :items="detailItems" />
|
<FaDescriptions
|
||||||
|
v-if="orderDetail"
|
||||||
|
:column="2"
|
||||||
|
border
|
||||||
|
:items="detailItems"
|
||||||
|
:data="orderDetail"
|
||||||
|
/>
|
||||||
</FaDialog>
|
</FaDialog>
|
||||||
|
|
||||||
<!-- 驳回退款弹窗 -->
|
<!-- 驳回退款弹窗 -->
|
||||||
@@ -345,7 +351,13 @@ const orderDetail = ref<OrderTable | null>(null);
|
|||||||
async function showOrderDetail(row: OrderTable) {
|
async function showOrderDetail(row: OrderTable) {
|
||||||
try {
|
try {
|
||||||
const res = await OrderAPI.detailOrder(row.id);
|
const res = await OrderAPI.detailOrder(row.id);
|
||||||
orderDetail.value = res.data.data as OrderTable;
|
const data = res.data.data as OrderTable;
|
||||||
|
if (data) {
|
||||||
|
data.amount = data.amount / 100;
|
||||||
|
data.order_type = orderTypeLabel(data.order_type);
|
||||||
|
data.pay_method = data.pay_method ? payMethodLabel(data.pay_method) : "";
|
||||||
|
}
|
||||||
|
orderDetail.value = data;
|
||||||
detailVisible.value = true;
|
detailVisible.value = true;
|
||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
@@ -362,16 +374,30 @@ async function cancelOrder(orderId: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailItems = computed(() => {
|
const detailItems = computed<
|
||||||
|
import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[]
|
||||||
|
>(() => {
|
||||||
if (!orderDetail.value) return [];
|
if (!orderDetail.value) return [];
|
||||||
return [
|
return [
|
||||||
{ label: "订单ID", prop: "id" },
|
{ label: "订单ID", prop: "id" },
|
||||||
{ label: "订单号", prop: "order_no" },
|
{ label: "订单号", prop: "order_no" },
|
||||||
{ label: "租户ID", prop: "tenant_id" },
|
{ label: "租户ID", prop: "tenant_id" },
|
||||||
{ label: "套餐ID", prop: "package_id" },
|
{ label: "套餐ID", prop: "package_id" },
|
||||||
{ label: "订单类型", prop: "order_type", slot: "order_type" },
|
{ label: "订单类型", prop: "order_type" },
|
||||||
{ label: "金额", prop: "amount", slot: "amount" },
|
{ label: "金额", prop: "amount" },
|
||||||
{ label: "状态", prop: "status", slot: "status" },
|
{
|
||||||
|
label: "状态",
|
||||||
|
prop: "status",
|
||||||
|
tag: {
|
||||||
|
map: {
|
||||||
|
"0": { type: "warning", text: "待支付" },
|
||||||
|
"1": { type: "success", text: "已支付" },
|
||||||
|
"2": { type: "info", text: "已取消" },
|
||||||
|
"3": { type: "warning", text: "已退款" },
|
||||||
|
"4": { type: "danger", text: "已过期" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{ label: "支付方式", prop: "pay_method" },
|
{ label: "支付方式", prop: "pay_method" },
|
||||||
{ label: "支付时间", prop: "pay_time" },
|
{ label: "支付时间", prop: "pay_time" },
|
||||||
{ label: "过期时间", prop: "expire_time" },
|
{ label: "过期时间", prop: "expire_time" },
|
||||||
|
|||||||
@@ -245,8 +245,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "正常", value: "0" },
|
{ label: "正常", value: 0 },
|
||||||
{ label: "禁用", value: "1" },
|
{ label: "禁用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const pkgSearchItems = computed<SearchFormItem[]>(() => [
|
const pkgSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -291,8 +291,8 @@ const categoryOptions = ref([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const pluginSearchItems = computed<SearchFormItem[]>(() => [
|
const pluginSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -209,8 +209,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const deptSearchItems = computed<SearchFormItem[]>(() => [
|
const deptSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -322,8 +322,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const dictDataSearchItems = computed<SearchFormItem[]>(() => [
|
const dictDataSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const dictTypeSearchItems = computed<SearchFormItem[]>(() => [
|
const dictTypeSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -213,8 +213,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const noticeTypeSearchOptions = computed(() =>
|
const noticeTypeSearchOptions = computed(() =>
|
||||||
|
|||||||
@@ -310,8 +310,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const positionSearchItems = computed<SearchFormItem[]>(() => [
|
const positionSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ const userDetailItems: DescriptionsItem[] = [
|
|||||||
label: "状态",
|
label: "状态",
|
||||||
prop: "status",
|
prop: "status",
|
||||||
tag: {
|
tag: {
|
||||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
map: { 0: { type: "success", text: "启用" }, 1: { type: "danger", text: "停用" } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ label: "上次登录时间", prop: "last_login" },
|
{ label: "上次登录时间", prop: "last_login" },
|
||||||
@@ -437,8 +437,8 @@ const userDialogFormItems = computed<FormItem[]>(() => [
|
|||||||
type: "radiogroup",
|
type: "radiogroup",
|
||||||
props: {
|
props: {
|
||||||
options: [
|
options: [
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -469,8 +469,8 @@ const searchBarRef = ref<InstanceType<typeof FaSearchBar> | null>(null);
|
|||||||
const searchBarRules: Record<string, unknown> = {};
|
const searchBarRules: Record<string, unknown> = {};
|
||||||
|
|
||||||
const statusOptions = ref([
|
const statusOptions = ref([
|
||||||
{ label: "启用", value: "0" },
|
{ label: "启用", value: 0 },
|
||||||
{ label: "停用", value: "1" },
|
{ label: "停用", value: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const userSearchItems = computed<SearchFormItem[]>(() => [
|
const userSearchItems = computed<SearchFormItem[]>(() => [
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
:show-search="true"
|
:show-search="true"
|
||||||
:disabled-search="false"
|
:disabled-search="false"
|
||||||
:default-expanded="false"
|
:default-expanded="false"
|
||||||
|
:button-left-limit="0"
|
||||||
@search="handleJobSearchBarSearch"
|
@search="handleJobSearchBarSearch"
|
||||||
@reset="onJobResetSearch"
|
@reset="onJobResetSearch"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
:show-search="true"
|
:show-search="true"
|
||||||
:disabled-search="false"
|
:disabled-search="false"
|
||||||
:default-expanded="false"
|
:default-expanded="false"
|
||||||
|
:button-left-limit="0"
|
||||||
@search="handleSearchBarSearch"
|
@search="handleSearchBarSearch"
|
||||||
@reset="onResetSearch"
|
@reset="onResetSearch"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user