diff --git a/dash-fastapi-backend/module_admin/controller/role_controller.py b/dash-fastapi-backend/module_admin/controller/role_controller.py index 348ab9d526c1d18913e5f27d56dd6c1f90b87607..9f0658dd3a5aa01c8c69fe11602e07e8e369c897 100644 --- a/dash-fastapi-backend/module_admin/controller/role_controller.py +++ b/dash-fastapi-backend/module_admin/controller/role_controller.py @@ -3,6 +3,7 @@ from fastapi import Depends from config.get_db import get_db from module_admin.service.login_service import get_current_user, CurrentUserInfoServiceResponse from module_admin.service.role_service import * +from module_admin.service.user_service import UserService, UserRoleQueryModel, UserRolePageObject, UserRolePageObjectResponse, CrudUserRoleModel from module_admin.entity.vo.role_vo import * from utils.response_util import * from utils.log_util import * @@ -117,3 +118,63 @@ async def export_system_role_list(request: Request, role_query: RoleQueryModel, except Exception as e: logger.exception(e) return response_500(data="", message=str(e)) + + +@roleController.post("/role/authUser/allocatedList", response_model=UserRolePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_allocated_user_list(request: Request, user_role: UserRolePageObject, query_db: Session = Depends(get_db)): + try: + user_role_query = UserRoleQueryModel(**user_role.dict()) + user_role_allocated_query_result = UserService.get_user_role_allocated_list_services(query_db, user_role_query) + # 分页操作 + user_role_allocated_page_query_result = get_page_obj(user_role_allocated_query_result, user_role.page_num, user_role.page_size) + logger.info('获取成功') + return response_200(data=user_role_allocated_page_query_result, message="获取成功") + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@roleController.post("/role/authUser/unallocatedList", response_model=UserRolePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_unallocated_user_list(request: Request, user_role: UserRolePageObject, query_db: Session = Depends(get_db)): + try: + user_role_query = UserRoleQueryModel(**user_role.dict()) + user_role_unallocated_query_result = UserService.get_user_role_unallocated_list_services(query_db, user_role_query) + # 分页操作 + user_role_unallocated_page_query_result = get_page_obj(user_role_unallocated_query_result, user_role.page_num, user_role.page_size) + logger.info('获取成功') + return response_200(data=user_role_unallocated_page_query_result, message="获取成功") + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@roleController.post("/role/authUser/selectAll", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=4) +async def add_system_role_user(request: Request, add_user_role: CrudUserRoleModel, query_db: Session = Depends(get_db)): + try: + add_user_role_result = UserService.add_user_role_services(query_db, add_user_role) + if add_user_role_result.is_success: + logger.info(add_user_role_result.message) + return response_200(data=add_user_role_result, message=add_user_role_result.message) + else: + logger.warning(add_user_role_result.message) + return response_400(data="", message=add_user_role_result.message) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@roleController.post("/role/authUser/cancel", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))]) +@log_decorator(title='角色管理', business_type=4) +async def cancel_system_role_user(request: Request, cancel_user_role: CrudUserRoleModel, query_db: Session = Depends(get_db)): + try: + cancel_user_role_result = UserService.delete_user_role_services(query_db, cancel_user_role) + if cancel_user_role_result.is_success: + logger.info(cancel_user_role_result.message) + return response_200(data=cancel_user_role_result, message=cancel_user_role_result.message) + else: + logger.warning(cancel_user_role_result.message) + return response_400(data="", message=cancel_user_role_result.message) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) diff --git a/dash-fastapi-backend/module_admin/controller/user_controller.py b/dash-fastapi-backend/module_admin/controller/user_controller.py index 65e251c42ccdaf79a8bfdf0a0ffdcece4d193d64..a4c9b75d1c4db9f320cac5ded274dec2c285c477 100644 --- a/dash-fastapi-backend/module_admin/controller/user_controller.py +++ b/dash-fastapi-backend/module_admin/controller/user_controller.py @@ -153,7 +153,7 @@ async def change_system_user_profile_info(request: Request, edit_user: AddUserMo @userController.patch("/user/profile/resetPwd", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))]) @log_decorator(title='个人信息', business_type=2) -async def reset_system_user_password(request: Request, reset_user: ResetUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)): +async def reset_system_user_password(request: Request, reset_user: ResetUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)): try: if not reset_user.user_id and reset_user.old_password: reset_user.user_id = current_user.user.user_id @@ -172,6 +172,33 @@ async def reset_system_user_password(request: Request, reset_user: ResetUserMode return response_500(data="", message=str(e)) +@userController.post("/user/importData", dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))]) +@log_decorator(title='用户管理', business_type=6) +async def batch_import_system_user(request: Request, user_import: ImportUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)): + try: + batch_import_result = UserService.batch_import_user_services(query_db, user_import, current_user) + if batch_import_result.is_success: + logger.info(batch_import_result.message) + return response_200(data=batch_import_result, message=batch_import_result.message) + else: + logger.warning(batch_import_result.message) + return response_400(data="", message=batch_import_result.message) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@userController.post("/user/importTemplate", dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))]) +async def export_system_user_template(request: Request, query_db: Session = Depends(get_db)): + try: + user_import_template_result = UserService.get_user_import_template_services() + logger.info('获取成功') + return streaming_response_200(data=bytes2file_response(user_import_template_result)) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + @userController.post("/user/export", dependencies=[Depends(CheckUserInterfaceAuth('system:user:export'))]) @log_decorator(title='用户管理', business_type=5) async def export_system_user_list(request: Request, user_query: UserQueryModel, query_db: Session = Depends(get_db)): @@ -184,3 +211,63 @@ async def export_system_user_list(request: Request, user_query: UserQueryModel, except Exception as e: logger.exception(e) return response_500(data="", message=str(e)) + + +@userController.post("/user/authRole/allocatedList", response_model=UserRolePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_allocated_role_list(request: Request, user_role: UserRolePageObject, query_db: Session = Depends(get_db)): + try: + user_role_query = UserRoleQueryModel(**user_role.dict()) + user_role_allocated_query_result = UserService.get_user_role_allocated_list_services(query_db, user_role_query) + # 分页操作 + user_role_allocated_page_query_result = get_page_obj(user_role_allocated_query_result, user_role.page_num, user_role.page_size) + logger.info('获取成功') + return response_200(data=user_role_allocated_page_query_result, message="获取成功") + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@userController.post("/user/authRole/unallocatedList", response_model=UserRolePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))]) +async def get_system_unallocated_role_list(request: Request, user_role: UserRolePageObject, query_db: Session = Depends(get_db)): + try: + user_role_query = UserRoleQueryModel(**user_role.dict()) + user_role_unallocated_query_result = UserService.get_user_role_unallocated_list_services(query_db, user_role_query) + # 分页操作 + user_role_unallocated_page_query_result = get_page_obj(user_role_unallocated_query_result, user_role.page_num, user_role.page_size) + logger.info('获取成功') + return response_200(data=user_role_unallocated_page_query_result, message="获取成功") + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@userController.post("/user/authRole/selectAll", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))]) +@log_decorator(title='用户管理', business_type=4) +async def add_system_role_user(request: Request, add_user_role: CrudUserRoleModel, query_db: Session = Depends(get_db)): + try: + add_user_role_result = UserService.add_user_role_services(query_db, add_user_role) + if add_user_role_result.is_success: + logger.info(add_user_role_result.message) + return response_200(data=add_user_role_result, message=add_user_role_result.message) + else: + logger.warning(add_user_role_result.message) + return response_400(data="", message=add_user_role_result.message) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) + + +@userController.post("/user/authRole/cancel", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))]) +@log_decorator(title='用户管理', business_type=4) +async def cancel_system_role_user(request: Request, cancel_user_role: CrudUserRoleModel, query_db: Session = Depends(get_db)): + try: + cancel_user_role_result = UserService.delete_user_role_services(query_db, cancel_user_role) + if cancel_user_role_result.is_success: + logger.info(cancel_user_role_result.message) + return response_200(data=cancel_user_role_result, message=cancel_user_role_result.message) + else: + logger.warning(cancel_user_role_result.message) + return response_400(data="", message=cancel_user_role_result.message) + except Exception as e: + logger.exception(e) + return response_500(data="", message=str(e)) diff --git a/dash-fastapi-backend/module_admin/dao/user_dao.py b/dash-fastapi-backend/module_admin/dao/user_dao.py index 818823fb61304cb910fe877fcbdd3a4f92a8e1d3..19ea73abafdeb3efc06ac5612facfe43753c9ca2 100644 --- a/dash-fastapi-backend/module_admin/dao/user_dao.py +++ b/dash-fastapi-backend/module_admin/dao/user_dao.py @@ -5,7 +5,7 @@ from module_admin.entity.do.role_do import SysRole, SysRoleMenu from module_admin.entity.do.dept_do import SysDept from module_admin.entity.do.post_do import SysPost from module_admin.entity.do.menu_do import SysMenu -from module_admin.entity.vo.user_vo import UserModel, UserRoleModel, UserPostModel, CurrentUserInfo, UserQueryModel +from module_admin.entity.vo.user_vo import UserModel, UserRoleModel, UserPostModel, CurrentUserInfo, UserQueryModel, UserRoleQueryModel from utils.time_format_util import list_format_datetime, format_datetime_dict_list from datetime import datetime, time from typing import Union, List @@ -231,6 +231,90 @@ class UserDao: .filter(SysUser.user_id == user.user_id) \ .update({SysUser.del_flag: '2', SysUser.update_by: user.update_by, SysUser.update_time: user.update_time}) + @classmethod + def get_user_role_allocated_list_by_user_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据用户id获取用户已分配的角色列表信息数据库操作 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 用户已分配的角色列表信息 + """ + allocated_role_list = db.query(SysRole) \ + .filter( + SysRole.del_flag == 0, + SysRole.role_id != 1, + SysRole.role_name == query_object.role_name if query_object.role_name else True, + SysRole.role_key == query_object.role_key if query_object.role_key else True, + SysRole.role_id.in_( + db.query(SysUserRole.role_id).filter(SysUserRole.user_id == query_object.user_id) + ) + ).distinct().all() + + return list_format_datetime(allocated_role_list) + + @classmethod + def get_user_role_unallocated_list_by_user_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据用户id获取用户未分配的角色列表信息数据库操作 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 用户未分配的角色列表信息 + """ + unallocated_role_list = db.query(SysRole) \ + .filter( + SysRole.del_flag == 0, + SysRole.role_id != 1, + SysRole.role_name == query_object.role_name if query_object.role_name else True, + SysRole.role_key == query_object.role_key if query_object.role_key else True, + ~SysRole.role_id.in_( + db.query(SysUserRole.role_id).filter(SysUserRole.user_id == query_object.user_id) + ) + ).distinct().all() + + return list_format_datetime(unallocated_role_list) + + @classmethod + def get_user_role_allocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据角色id获取已分配的用户列表信息 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 角色已分配的用户列表信息 + """ + allocated_user_list = db.query(SysUser) \ + .filter( + SysUser.del_flag == 0, + SysUser.user_id != 1, + SysUser.user_name == query_object.user_name if query_object.user_name else True, + SysUser.phonenumber == query_object.phonenumber if query_object.phonenumber else True, + SysUser.user_id.in_( + db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id) + ) + ).distinct().all() + + return list_format_datetime(allocated_user_list) + + @classmethod + def get_user_role_unallocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel): + """ + 根据角色id获取未分配的用户列表信息 + :param db: orm对象 + :param query_object: 用户角色查询对象 + :return: 角色未分配的用户列表信息 + """ + unallocated_user_list = db.query(SysUser) \ + .filter( + SysUser.del_flag == 0, + SysUser.user_id != 1, + SysUser.user_name == query_object.user_name if query_object.user_name else True, + SysUser.phonenumber == query_object.phonenumber if query_object.phonenumber else True, + ~SysUser.user_id.in_( + db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id) + ) + ).distinct().all() + + return list_format_datetime(unallocated_user_list) + @classmethod def add_user_role_dao(cls, db: Session, user_role: UserRoleModel): """ @@ -254,6 +338,32 @@ class UserDao: .filter(SysUserRole.user_id == user_role.user_id) \ .delete() + @classmethod + def delete_user_role_by_user_and_role_dao(cls, db: Session, user_role: UserRoleModel): + """ + 根据用户id及角色id删除用户角色关联信息数据库操作 + :param db: orm对象 + :param user_role: 用户角色关联对象 + :return: + """ + db.query(SysUserRole) \ + .filter(SysUserRole.user_id == user_role.user_id, SysUserRole.role_id == user_role.role_id) \ + .delete() + + @classmethod + def get_user_role_detail(cls, db: Session, user_role: UserRoleModel): + """ + 根据用户角色关联获取用户角色关联详细信息 + :param db: orm对象 + :param user_role: 用户角色关联对象 + :return: 用户角色关联信息 + """ + user_role_info = db.query(SysUserRole) \ + .filter(SysUserRole.user_id == user_role.user_id, SysUserRole.role_id == user_role.role_id) \ + .distinct().first() + + return user_role_info + @classmethod def add_user_post_dao(cls, db: Session, user_post: UserPostModel): """ diff --git a/dash-fastapi-backend/module_admin/entity/vo/user_vo.py b/dash-fastapi-backend/module_admin/entity/vo/user_vo.py index 31b52c979cb39b6983bc61ce00efb5e7062169bf..21310f766e2ae4ea4f9c65a434094726f4b18d65 100644 --- a/dash-fastapi-backend/module_admin/entity/vo/user_vo.py +++ b/dash-fastapi-backend/module_admin/entity/vo/user_vo.py @@ -213,6 +213,51 @@ class DeleteUserModel(BaseModel): update_time: Optional[str] +class UserRoleQueryModel(UserRoleModel): + """ + 用户角色关联管理不分页查询模型 + """ + user_name: Optional[str] + phonenumber: Optional[str] + role_name: Optional[str] + role_key: Optional[str] + + +class UserRolePageObject(UserRoleQueryModel): + """ + 用户角色关联管理分页查询模型 + """ + page_num: int + page_size: int + + +class UserRolePageObjectResponse(BaseModel): + """ + 用户角色关联管理列表分页查询返回模型 + """ + rows: List = [] + page_num: int + page_size: int + total: int + has_next: bool + + +class CrudUserRoleModel(BaseModel): + """ + 新增、删除用户关联角色及角色关联用户模型 + """ + user_ids: Optional[str] + role_ids: Optional[str] + + +class ImportUserModel(BaseModel): + """ + 批量导入用户模型 + """ + url: str + is_update: bool + + class CrudUserResponse(BaseModel): """ 操作用户响应模型 diff --git a/dash-fastapi-backend/module_admin/service/user_service.py b/dash-fastapi-backend/module_admin/service/user_service.py index f42a3bc834298697caa82a0d78f760a2d5abb7b2..4f451111009992032d90b20ef864d4b02646de02 100644 --- a/dash-fastapi-backend/module_admin/service/user_service.py +++ b/dash-fastapi-backend/module_admin/service/user_service.py @@ -1,7 +1,7 @@ from module_admin.entity.vo.user_vo import * from module_admin.dao.user_dao import * from utils.pwd_util import * -from utils.common_util import export_list2excel +from utils.common_util import * class UserService: @@ -173,6 +173,98 @@ class UserService: return CrudUserResponse(**result) + @classmethod + def batch_import_user_services(cls, result_db: Session, user_import: ImportUserModel, current_user: CurrentUserInfoServiceResponse): + """ + 批量导入用户service + :param user_import: 用户导入参数对象 + :param result_db: orm对象 + :param current_user: 当前用户对象 + :return: 批量导入用户结果 + """ + header_dict = { + "部门编号": "dept_id", + "登录名称": "user_name", + "用户名称": "nick_name", + "用户邮箱": "email", + "手机号码": "phonenumber", + "用户性别": "sex", + "帐号状态": "status" + } + filepath = get_filepath_from_url(user_import.url) + df = pd.read_excel(filepath) + df.rename(columns=header_dict, inplace=True) + add_error_result = [] + count = 0 + try: + for index, row in df.iterrows(): + count = count + 1 + if row['sex'] == '男': + row['sex'] = '0' + if row['sex'] == '女': + row['sex'] = '1' + if row['sex'] == '未知': + row['sex'] = '2' + if row['status'] == '正常': + row['status'] = '0' + if row['status'] == '停用': + row['status'] = '1' + add_user = UserModel( + **dict( + dept_id=row['dept_id'], + user_name=row['user_name'], + password=PwdUtil.get_password_hash('123456'), + nick_name=row['nick_name'], + email=row['email'], + phonenumber=row['phonenumber'], + sex=row['sex'], + status=row['status'], + create_by=current_user.user.user_name, + update_by=current_user.user.user_name + ) + ) + user_info = UserDao.get_user_by_info(result_db, UserModel(**dict(user_name=row['user_name']))) + if user_info: + if user_import.is_update: + edit_user = UserModel( + **dict( + user_id=user_info.user_id, + dept_id=row['dept_id'], + user_name=row['user_name'], + nick_name=row['nick_name'], + email=row['email'], + phonenumber=row['phonenumber'], + sex=row['sex'], + status=row['status'], + update_by=current_user.user.user_name + ) + ).dict(exclude_unset=True) + UserDao.edit_user_dao(result_db, edit_user) + else: + add_error_result.append(f"{count}.用户账号{row['user_name']}已存在") + else: + UserDao.add_user_dao(result_db, add_user) + result_db.commit() + result = dict(is_success=True, message='\n'.join(add_error_result)) + except Exception as e: + result_db.rollback() + result = dict(is_success=False, message=str(e)) + + return CrudUserResponse(**result) + + @staticmethod + def get_user_import_template_services(): + """ + 获取用户导入模板service + :return: 用户导入模板excel的二进制数据 + """ + header_list = ["部门编号", "登录名称", "用户名称", "用户邮箱", "手机号码", "用户性别", "帐号状态"] + selector_header_list = ["用户性别", "帐号状态"] + option_list = [{"用户性别": ["男", "女", "未知"]}, {"帐号状态": ["正常", "停用"]}] + binary_data = get_excel_template(header_list=header_list, selector_header_list=selector_header_list, option_list=option_list) + + return binary_data + @staticmethod def export_user_list_services(user_list: List): """ @@ -214,3 +306,129 @@ class UserService: binary_data = export_list2excel(new_data) return binary_data + + @classmethod + def get_user_role_allocated_list_services(cls, result_db: Session, page_object: UserRoleQueryModel): + """ + 根据用户id获取已分配角色列表或根据角色id获取已分配用户列表 + :param result_db: orm对象 + :param page_object: 用户关联角色对象 + :return: 已分配角色列表或已分配用户列表 + """ + allocated_list = [] + if page_object.user_id: + allocated_list = UserDao.get_user_role_allocated_list_by_user_id(result_db, page_object) + if page_object.role_id: + allocated_list = UserDao.get_user_role_allocated_list_by_role_id(result_db, page_object) + + return allocated_list + + @classmethod + def get_user_role_unallocated_list_services(cls, result_db: Session, page_object: UserRoleQueryModel): + """ + 根据用户id获取未分配角色列表或根据角色id获取未分配用户列表 + :param result_db: orm对象 + :param page_object: 用户关联角色对象 + :return: 未分配角色列表或未分配用户列表 + """ + unallocated_list = [] + if page_object.user_id: + unallocated_list = UserDao.get_user_role_unallocated_list_by_user_id(result_db, page_object) + if page_object.role_id: + unallocated_list = UserDao.get_user_role_unallocated_list_by_role_id(result_db, page_object) + + return unallocated_list + + @classmethod + def add_user_role_services(cls, result_db: Session, page_object: CrudUserRoleModel): + """ + 新增用户关联角色信息service + :param result_db: orm对象 + :param page_object: 新增用户关联角色对象 + :return: 新增用户关联角色校验结果 + """ + if page_object.user_ids and page_object.role_ids: + user_id_list = page_object.user_ids.split(',') + role_id_list = page_object.role_ids.split(',') + if len(user_id_list) == 1 and len(role_id_list) >= 1: + try: + for role_id in role_id_list: + user_role_dict = dict(user_id=page_object.user_ids, role_id=role_id) + user_role = cls.detail_user_role_services(result_db, UserRoleModel(**user_role_dict)) + if user_role: + continue + else: + UserDao.add_user_role_dao(result_db, UserRoleModel(**user_role_dict)) + result_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + result_db.rollback() + result = dict(is_success=False, message=str(e)) + elif len(user_id_list) >= 1 and len(role_id_list) == 1: + try: + for user_id in user_id_list: + user_role_dict = dict(user_id=user_id, role_id=page_object.role_ids) + user_role = cls.detail_user_role_services(result_db, UserRoleModel(**user_role_dict)) + if user_role: + continue + else: + UserDao.add_user_role_dao(result_db, UserRoleModel(**user_role_dict)) + result_db.commit() + result = dict(is_success=True, message='新增成功') + except Exception as e: + result_db.rollback() + result = dict(is_success=False, message=str(e)) + else: + result = dict(is_success=False, message='不满足新增条件') + else: + result = dict(is_success=False, message='传入用户角色关联信息为空') + + return CrudUserResponse(**result) + + @classmethod + def delete_user_role_services(cls, result_db: Session, page_object: CrudUserRoleModel): + """ + 删除用户关联角色信息service + :param result_db: orm对象 + :param page_object: 删除用户关联角色对象 + :return: 删除用户关联角色校验结果 + """ + if page_object.user_ids and page_object.role_ids: + user_id_list = page_object.user_ids.split(',') + role_id_list = page_object.role_ids.split(',') + if len(user_id_list) == 1 and len(role_id_list) >= 1: + try: + for role_id in role_id_list: + UserDao.delete_user_role_by_user_and_role_dao(result_db, UserRoleModel(**dict(user_id=page_object.user_ids, role_id=role_id))) + result_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + result_db.rollback() + result = dict(is_success=False, message=str(e)) + elif len(user_id_list) >= 1 and len(role_id_list) == 1: + try: + for user_id in user_id_list: + UserDao.delete_user_role_by_user_and_role_dao(result_db, UserRoleModel(**dict(user_id=user_id, role_id=page_object.role_ids))) + result_db.commit() + result = dict(is_success=True, message='删除成功') + except Exception as e: + result_db.rollback() + result = dict(is_success=False, message=str(e)) + else: + result = dict(is_success=False, message='不满足删除条件') + else: + result = dict(is_success=False, message='传入用户角色关联信息为空') + + return CrudUserResponse(**result) + + @classmethod + def detail_user_role_services(cls, result_db: Session, page_object: UserRoleModel): + """ + 获取用户关联角色详细信息service + :param result_db: orm对象 + :param page_object: 用户关联角色对象 + :return: 用户关联角色详细信息 + """ + user_role = UserDao.get_user_role_detail(result_db, page_object) + + return user_role diff --git a/dash-fastapi-backend/utils/common_util.py b/dash-fastapi-backend/utils/common_util.py index 3715a76de207e8ca9879da8924b6acd279bb0c79..a58f9895f819181f9f425497182845739df9663d 100644 --- a/dash-fastapi-backend/utils/common_util.py +++ b/dash-fastapi-backend/utils/common_util.py @@ -1,5 +1,11 @@ import pandas as pd import io +import os +from openpyxl import Workbook +from openpyxl.styles import Alignment, PatternFill +from openpyxl.utils import get_column_letter +from openpyxl.worksheet.datavalidation import DataValidation +from config.env import CachePathConfig def worship(): @@ -65,3 +71,76 @@ def export_list2excel(list_data: list): binary_data = binary_data.getvalue() return binary_data + + +def get_excel_template(header_list: list, selector_header_list: list, option_list: list[dict]): + """ + 工具方法:将需要导出的list数据转化为对应excel的二进制数据 + :param header_list: 表头数据列表 + :param selector_header_list: 需要设置为选择器格式的表头数据列表 + :param option_list: 选择器格式的表头预设的选项列表 + :return: 模板excel的二进制数据 + """ + # 创建Excel工作簿 + wb = Workbook() + # 选择默认的活动工作表 + ws = wb.active + + # 设置表头文字 + headers = header_list + + # 设置表头背景样式为灰色,前景色为白色 + header_fill = PatternFill(start_color="ababab", end_color="ababab", fill_type="solid") + + # 将表头写入第一行 + for col_num, header in enumerate(headers, 1): + cell = ws.cell(row=1, column=col_num) + cell.value = header + cell.fill = header_fill + # 设置列宽度为16 + ws.column_dimensions[chr(64 + col_num)].width = 12 + # 设置水平居中对齐 + cell.alignment = Alignment(horizontal='center') + + # 设置选择器的预设选项 + options = option_list + + # 获取selector_header的字母索引 + for selector_header in selector_header_list: + column_selector_header_index = headers.index(selector_header) + 1 + + # 创建数据有效性规则 + header_option = [] + for option in options: + if option.get(selector_header): + header_option = option.get(selector_header) + dv = DataValidation(type="list", formula1=f'"{",".join(header_option)}"') + # 设置数据有效性规则的起始单元格和结束单元格 + dv.add( + f'{get_column_letter(column_selector_header_index)}2:{get_column_letter(column_selector_header_index)}1048576') + # 添加数据有效性规则到工作表 + ws.add_data_validation(dv) + + # 保存Excel文件为字节类型的数据 + file = io.BytesIO() + wb.save(file) + file.seek(0) + + # 读取字节数据 + excel_data = file.getvalue() + + return excel_data + + +def get_filepath_from_url(url: str): + """ + 工具方法:根据请求参数获取文件路径 + :param url: 请求参数中的url参数 + :return: 文件路径 + """ + file_info = url.split("?")[1].split("&") + task_id = file_info[0].split("=")[1] + file_name = file_info[1].split("=")[1] + filepath = os.path.join(CachePathConfig.PATH, task_id, file_name) + + return filepath diff --git a/dash-fastapi-frontend/api/role.py b/dash-fastapi-frontend/api/role.py index 8526d2bfae1af6a565c045f29113c06f8fb3aeac..97f9fc39bce26c5544ec95e6e98cddef4cd59b64 100644 --- a/dash-fastapi-frontend/api/role.py +++ b/dash-fastapi-frontend/api/role.py @@ -34,3 +34,23 @@ def get_role_detail_api(role_id: int): def export_role_list_api(page_obj: dict): return api_request(method='post', url='/system/role/export', is_headers=True, json=page_obj, stream=True) + + +def get_allocated_user_list_api(page_obj: dict): + + return api_request(method='post', url='/system/role/authUser/allocatedList', is_headers=True, json=page_obj) + + +def get_unallocated_user_list_api(page_obj: dict): + + return api_request(method='post', url='/system/role/authUser/unallocatedList', is_headers=True, json=page_obj) + + +def auth_user_select_all_api(page_obj: dict): + + return api_request(method='post', url='/system/role/authUser/selectAll', is_headers=True, json=page_obj) + + +def auth_user_cancel_api(page_obj: dict): + + return api_request(method='post', url='/system/role/authUser/cancel', is_headers=True, json=page_obj) diff --git a/dash-fastapi-frontend/api/user.py b/dash-fastapi-frontend/api/user.py index e931802ab5bf4fb016b5684cebd6b4d4d2909e8e..c426534db3ecd5cae2bea60cabb42a45eef6cf5f 100644 --- a/dash-fastapi-frontend/api/user.py +++ b/dash-fastapi-frontend/api/user.py @@ -46,7 +46,38 @@ def reset_user_password_api(page_obj: dict): return api_request(method='patch', url='/system/user/profile/resetPwd', is_headers=True, json=page_obj) +def batch_import_user_api(page_obj: dict): + + return api_request(method='post', url='/system/user/importData', is_headers=True, json=page_obj) + + +def download_user_import_template_api(): + + return api_request(method='post', url='/system/user/importTemplate', is_headers=True, stream=True) + + def export_user_list_api(page_obj: dict): return api_request(method='post', url='/system/user/export', is_headers=True, json=page_obj, stream=True) + +def get_allocated_role_list_api(page_obj: dict): + + return api_request(method='post', url='/system/user/authRole/allocatedList', is_headers=True, json=page_obj) + + +def get_unallocated_role_list_api(page_obj: dict): + + return api_request(method='post', url='/system/user/authRole/unallocatedList', is_headers=True, json=page_obj) + + +def auth_role_select_all_api(page_obj: dict): + + return api_request(method='post', url='/system/user/authRole/selectAll', is_headers=True, json=page_obj) + + +def auth_role_cancel_api(page_obj: dict): + + return api_request(method='post', url='/system/user/authRole/cancel', is_headers=True, json=page_obj) + + diff --git a/dash-fastapi-frontend/callbacks/app_c.py b/dash-fastapi-frontend/callbacks/app_c.py index adc09e94434c8a16c992d38a7b38cc03c1b38fa4..0210e4328131ced648253e9eb0a4d104099bd07f 100644 --- a/dash-fastapi-frontend/callbacks/app_c.py +++ b/dash-fastapi-frontend/callbacks/app_c.py @@ -64,14 +64,17 @@ def get_primary_color(_, custom_color): return dash.no_update -@app.callback( +app.clientside_callback( + ''' + (system_color, custom_color) => { + if (custom_color) { + return custom_color; + } + return system_color; + } + ''', Output('app-config-provider', 'primaryColor'), [Input('system-app-primary-color-container', 'data'), Input('custom-app-primary-color-container', 'data')], prevent_initial_call=True ) -def render_app_primary_color(system_color, custom_color): - if custom_color: - return custom_color - - return system_color diff --git a/dash-fastapi-frontend/callbacks/forget_c.py b/dash-fastapi-frontend/callbacks/forget_c.py index 45c5ddcbbcd057ce6c62765600658f5f992ece90..9527ce35be12513999a61fe5d8f45e26913d3d7b 100644 --- a/dash-fastapi-frontend/callbacks/forget_c.py +++ b/dash-fastapi-frontend/callbacks/forget_c.py @@ -121,75 +121,82 @@ def login_auth(nClicks, username, password, password_again, input_captcha, sessi @app.callback( [Output('message-code-count-down', 'delay'), - Output('get-message-code', 'disabled'), + Output('get-message-code', 'disabled', allow_duplicate=True), Output('sms_code-session_id-container', 'data'), Output('global-message-container', 'children', allow_duplicate=True)], - [Input('get-message-code', 'nClicks'), - Input('message-code-count-down', 'countdown')], + Input('get-message-code', 'nClicks'), [State('forget-username', 'value'), State('sms_code-session_id-container', 'data')], prevent_initial_call=True ) -def message_countdown(nClicks, countdown, username, session_id): +def message_countdown(nClicks, username, session_id): if nClicks: - if dash.ctx.triggered_id == 'get-message-code': - - try: - if username: - send_result = send_message_api(dict(user_name=username, session_id=session_id)) - if send_result.get('code') == 200: - - return [ - 120, - True, - send_result.get('data').get('session_id'), - fuc.FefferyFancyMessage(send_result.get('message'), type='success') - ] - else: - - return [ - dash.no_update, - False, - dash.no_update, - fuc.FefferyFancyMessage(send_result.get('message'), type='error') - ] + try: + if username: + send_result = send_message_api(dict(user_name=username, session_id=session_id)) + if send_result.get('code') == 200: + return [ + 120, + True, + send_result.get('data').get('session_id'), + fuc.FefferyFancyMessage(send_result.get('message'), type='success') + ] else: + return [ dash.no_update, False, dash.no_update, - fuc.FefferyFancyMessage('请输入用户名', type='error') + fuc.FefferyFancyMessage(send_result.get('message'), type='error') ] - except Exception as e: - + else: return [ dash.no_update, False, dash.no_update, - fuc.FefferyFancyMessage(str(e), type='error') + fuc.FefferyFancyMessage('请输入用户名', type='error') ] - if dash.ctx.triggered_id == 'message-code-count-down': - if countdown: - return [ - dash.no_update, True, dash.no_update, dash.no_update - ] + except Exception as e: - return dash.no_update, False, dash.no_update, dash.no_update + return [ + dash.no_update, + False, + dash.no_update, + fuc.FefferyFancyMessage(str(e), type='error') + ] return [dash.no_update] * 4 -@app.callback( - Output('get-message-code', 'children'), +app.clientside_callback( + ''' + (countdown) => { + if (countdown) { + return true; + } + return false; + } + ''', + Output('get-message-code', 'disabled', allow_duplicate=True), Input('message-code-count-down', 'countdown'), prevent_initial_call=True ) -def update_button_content(countdown): - if countdown: - return f"获取中{countdown}s" - return "获取验证码" + +app.clientside_callback( + ''' + (countdown) => { + if (countdown) { + return `获取中${countdown}s` + } + return '获取验证码' + } + ''', + Output('get-message-code', 'children'), + Input('message-code-count-down', 'countdown'), + prevent_initial_call=True +) diff --git a/dash-fastapi-frontend/callbacks/layout_c/aside_c.py b/dash-fastapi-frontend/callbacks/layout_c/aside_c.py index 63c4d99adce08b2b9e5f3870dcf344ca55e51d46..71dfe849fae3270537d15339607f1f3d68762f69 100644 --- a/dash-fastapi-frontend/callbacks/layout_c/aside_c.py +++ b/dash-fastapi-frontend/callbacks/layout_c/aside_c.py @@ -1,17 +1,19 @@ -import dash from dash.dependencies import Input, Output from server import app # url-pathname控制currentKey回调 -@app.callback( +app.clientside_callback( + ''' + (data) => { + if (data) { + return data['current_key']; + } + return window.dash_clientside.no_update; + } + ''', Output('index-side-menu', 'currentKey'), Input('current-key-container', 'data'), prevent_initial_call=True ) -def pathname_update_current_key(data): - if data: - return data['current_key'] - - return dash.no_update diff --git a/dash-fastapi-frontend/callbacks/layout_c/head_c.py b/dash-fastapi-frontend/callbacks/layout_c/head_c.py index 0a9607b4bc4a753d9ba0b364622dec5ae9d09868..e0e43538051bca4fba23bbd29e4bc93bab575140 100644 --- a/dash-fastapi-frontend/callbacks/layout_c/head_c.py +++ b/dash-fastapi-frontend/callbacks/layout_c/head_c.py @@ -9,7 +9,31 @@ from api.login import logout_api # 页首右侧个人中心选项卡回调 -@app.callback( +app.clientside_callback( + ''' + (nClicks, clickedKey) => { + if (clickedKey == '退出登录') { + return [ + window.dash_clientside.no_update, + true, + false + ]; + } else if (clickedKey == '个人资料') { + return [ + '/user/profile', + false, + false + ]; + } else if ( clickedKey == '布局设置') { + return [ + window.dash_clientside.no_update, + false, + true + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('dcc-url', 'pathname', allow_duplicate=True), Output('logout-modal', 'visible'), Output('layout-setting-drawer', 'visible')], @@ -17,29 +41,6 @@ from api.login import logout_api State('index-header-dropdown', 'clickedKey'), prevent_initial_call=True ) -def index_dropdown_click(nClicks, clickedKey): - if clickedKey == '退出登录': - return [ - dash.no_update, - True, - False - ] - - elif clickedKey == '个人资料': - return [ - '/user/profile', - False, - False - ] - - elif clickedKey == '布局设置': - return [ - dash.no_update, - False, - True - ] - - return [dash.no_update] * 3 # 退出登录回调 @@ -65,27 +66,35 @@ def logout_confirm(okCounts): # 全局页面重载回调 -@app.callback( +app.clientside_callback( + ''' + (nClicks) => { + return true; + } + ''', Output('trigger-reload-output', 'reload'), Input('index-reload', 'nClicks'), prevent_initial_call=True ) -def reload_page(nClicks): - return True # 布局设置回调 -@app.callback( +app.clientside_callback( + ''' + (visible, custom_color) => { + if (visible) { + if (custom_color) { + return custom_color; + } + } + return window.dash_clientside.no_update; + } + ''', Output('hex-color-picker', 'color', allow_duplicate=True), Input('layout-setting-drawer', 'visible'), State('custom-app-primary-color-container', 'data'), prevent_initial_call=True ) -def init_hex_color_picker(visible, custom_color): - if visible: - if custom_color: - return custom_color - return dash.no_update @app.callback( diff --git a/dash-fastapi-frontend/callbacks/login_c.py b/dash-fastapi-frontend/callbacks/login_c.py index 4c757befd0fb33dce618f1264b3740f82e3cf2b9..465e1cfcc0206b24c0502b1c6358dcf3228b8245 100644 --- a/dash-fastapi-frontend/callbacks/login_c.py +++ b/dash-fastapi-frontend/callbacks/login_c.py @@ -52,7 +52,7 @@ def login_auth(nClicks, username, password, input_captcha, session_id, image_cli None, None, dash.no_update, - True, + False, token, dcc.Location( pathname='/', diff --git a/dash-fastapi-frontend/callbacks/monitor_c/cache_c/control_c.py b/dash-fastapi-frontend/callbacks/monitor_c/cache_c/control_c.py index df657bdd997e28466d1d9c8c23525b9b6cc3a93b..16afc774249ff0219976acba4db15d0548f98d1b 100644 --- a/dash-fastapi-frontend/callbacks/monitor_c/cache_c/control_c.py +++ b/dash-fastapi-frontend/callbacks/monitor_c/cache_c/control_c.py @@ -3,16 +3,18 @@ from dash.dependencies import Input, Output, State, ClientsideFunction from server import app -@app.callback( +app.clientside_callback( + ''' + (n_intervals, data) => { + return [data, true]; + } + ''', [Output('echarts-data-container', 'data'), Output('init-echarts-interval', 'disabled')], Input('init-echarts-interval', 'n_intervals'), State('init-echarts-data-container', 'data'), prevent_initial_call=True ) -def init_echarts(n_intervals, data): - - return [data, True] app.clientside_callback( diff --git a/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_c.py b/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_c.py index 1e5efb234bbc17303edb84f988cd8ea479cda139..21fbb84aab0b34389e98e65e45c7953802e38a43 100644 --- a/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_c.py +++ b/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_c.py @@ -108,7 +108,15 @@ def get_job_table_data(search_click, refresh_click, pagination, operations, job_ return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('job-job_name-input', 'value'), Output('job-job_group-select', 'value'), Output('job-status-select', 'value'), @@ -116,24 +124,26 @@ def get_job_table_data(search_click, refresh_click, pagination, operations, job_ Input('job-reset', 'nClicks'), prevent_initial_call=True ) -def reset_job_query_params(reset_click): - if reset_click: - return [None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 4 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('job-search-form-container', 'hidden'), Output('job-hidden-tooltip', 'title')], Input('job-hidden', 'nClicks'), State('job-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_job_search_form(hidden_click, hidden_status): - if hidden_click: - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_log_c.py b/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_log_c.py index c5964d49612aaaa20c0c24f57d8e91c675dca6ba..61e05408c0756495123169aac26ba1636d34b78b 100644 --- a/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_log_c.py +++ b/dash-fastapi-frontend/callbacks/monitor_c/job_c/job_log_c.py @@ -100,7 +100,15 @@ def get_job_log_table_data(search_click, refresh_click, pagination, operations, return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('job_log-job_name-input', 'value'), Output('job_log-job_group-select', 'value'), Output('job_log-status-select', 'value'), @@ -109,25 +117,26 @@ def get_job_log_table_data(search_click, refresh_click, pagination, operations, Input('job_log-reset', 'nClicks'), prevent_initial_call=True ) -def reset_job_log_query_params(reset_click): - if reset_click: - return [None, None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('job_log-search-form-container', 'hidden'), Output('job_log-hidden-tooltip', 'title')], Input('job_log-hidden', 'nClicks'), State('job_log-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_job_log_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/monitor_c/logininfor_c.py b/dash-fastapi-frontend/callbacks/monitor_c/logininfor_c.py index 8e230c83b5cf9697567cb28f3d045465f0a62d95..2ec0ffd36ffdb2b65eabb5bf8d3712c52afe0914 100644 --- a/dash-fastapi-frontend/callbacks/monitor_c/logininfor_c.py +++ b/dash-fastapi-frontend/callbacks/monitor_c/logininfor_c.py @@ -79,7 +79,15 @@ def get_login_log_table_data(search_click, refresh_click, pagination, operations return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('login_log-ipaddr-input', 'value'), Output('login_log-user_name-input', 'value'), Output('login_log-status-select', 'value'), @@ -88,25 +96,26 @@ def get_login_log_table_data(search_click, refresh_click, pagination, operations Input('login_log-reset', 'nClicks'), prevent_initial_call=True ) -def reset_login_log_query_params(reset_click): - if reset_click: - return [None, None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('login_log-search-form-container', 'hidden'), Output('login_log-hidden-tooltip', 'title')], Input('login_log-hidden', 'nClicks'), State('login_log-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_login_log_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/monitor_c/online_c.py b/dash-fastapi-frontend/callbacks/monitor_c/online_c.py index b5288e4937d26135def03da3e2bf9d94db5c99f2..eacbc75d6f0fbeaf90d4dbe7bb1d7e3ffd06981a 100644 --- a/dash-fastapi-frontend/callbacks/monitor_c/online_c.py +++ b/dash-fastapi-frontend/callbacks/monitor_c/online_c.py @@ -67,32 +67,41 @@ def get_online_table_data(search_click, refresh_click, pagination, operations, i return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('online-ipaddr-input', 'value'), Output('online-user_name-input', 'value'), Output('online-operations-store', 'data')], Input('online-reset', 'nClicks'), prevent_initial_call=True ) -def reset_online_query_params(reset_click): - if reset_click: - return [None, None, {'type': 'reset'}] - - return [dash.no_update] * 3 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('online-search-form-container', 'hidden'), Output('online-hidden-tooltip', 'title')], Input('online-hidden', 'nClicks'), State('online-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_online_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/monitor_c/operlog_c.py b/dash-fastapi-frontend/callbacks/monitor_c/operlog_c.py index efcaf8c3959623c7a65e7244e53a58e86d6dbb2f..e45503f1516463adee6e65d531a56b15eaf3e180 100644 --- a/dash-fastapi-frontend/callbacks/monitor_c/operlog_c.py +++ b/dash-fastapi-frontend/callbacks/monitor_c/operlog_c.py @@ -104,7 +104,15 @@ def get_operation_log_table_data(search_click, refresh_click, pagination, operat return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('operation_log-title-input', 'value'), Output('operation_log-oper_name-input', 'value'), Output('operation_log-business_type-select', 'value'), @@ -114,25 +122,26 @@ def get_operation_log_table_data(search_click, refresh_click, pagination, operat Input('operation_log-reset', 'nClicks'), prevent_initial_call=True ) -def reset_operation_log_query_params(reset_click): - if reset_click: - return [None, None, None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 6 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('operation_log-search-form-container', 'hidden'), Output('operation_log-hidden-tooltip', 'title')], Input('operation_log-hidden', 'nClicks'), State('operation_log-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_operation_log_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/config_c.py b/dash-fastapi-frontend/callbacks/system_c/config_c.py index 6f946d7fff19c08420da70b6800a24f49d3d8519..b0d2ca4f675f719db4541cf9cc4a5be9ca054e79 100644 --- a/dash-fastapi-frontend/callbacks/system_c/config_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/config_c.py @@ -91,7 +91,15 @@ def get_config_table_data(search_click, refresh_click, pagination, operations, c return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('config-config_name-input', 'value'), Output('config-config_key-input', 'value'), Output('config-config_type-select', 'value'), @@ -100,25 +108,26 @@ def get_config_table_data(search_click, refresh_click, pagination, operations, c Input('config-reset', 'nClicks'), prevent_initial_call=True ) -def reset_config_query_params(reset_click): - if reset_click: - return [None, None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('config-search-form-container', 'hidden'), Output('config-hidden-tooltip', 'title')], Input('config-hidden', 'nClicks'), State('config-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_config_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/dept_c.py b/dash-fastapi-frontend/callbacks/system_c/dept_c.py index 42a175f06bd79547fb60636e454cc1c830158595..8ce33e5491dc0e8e912071fa18ba4458c7218a42 100644 --- a/dash-fastapi-frontend/callbacks/system_c/dept_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/dept_c.py @@ -101,32 +101,41 @@ def get_dept_table_data(search_click, refresh_click, operations, fold_click, dep return [dash.no_update] * 4 + [None] -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('dept-dept_name-input', 'value'), Output('dept-status-select', 'value'), Output('dept-operations-store', 'data')], Input('dept-reset', 'nClicks'), prevent_initial_call=True ) -def reset_dept_query_params(reset_click): - if reset_click: - return [None, None, {'type': 'reset'}] - - return [dash.no_update] * 3 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('dept-search-form-container', 'hidden'), Output('dept-hidden-tooltip', 'title')], Input('dept-hidden', 'nClicks'), State('dept-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_dept_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_c.py b/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_c.py index da08faeafcce9ba2fdbc7f56bf485528133c9302..2cb608f4f18f491a8861f07c4a713573431c7712 100644 --- a/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_c.py @@ -95,7 +95,15 @@ def get_dict_type_table_data(search_click, refresh_click, pagination, operations return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('dict_type-dict_name-input', 'value'), Output('dict_type-dict_type-input', 'value'), Output('dict_type-status-select', 'value'), @@ -104,25 +112,26 @@ def get_dict_type_table_data(search_click, refresh_click, pagination, operations Input('dict_type-reset', 'nClicks'), prevent_initial_call=True ) -def reset_dict_type_query_params(reset_click): - if reset_click: - return [None, None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('dict_type-search-form-container', 'hidden'), Output('dict_type-hidden-tooltip', 'title')], Input('dict_type-hidden', 'nClicks'), State('dict_type-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_dict_type_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_data_c.py b/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_data_c.py index abd84807c3a1f12339ed9b218c0d7c0f0f9470ea..c01bcb637d91cd60e70699a1ffc3749ce87f338e 100644 --- a/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_data_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/dict_c/dict_data_c.py @@ -81,32 +81,41 @@ def get_dict_data_table_data(search_click, refresh_click, pagination, operations return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('dict_data-dict_label-input', 'value'), Output('dict_data-status-select', 'value'), Output('dict_data-operations-store', 'data')], Input('dict_data-reset', 'nClicks'), prevent_initial_call=True ) -def reset_dict_data_query_params(reset_click): - if reset_click: - return [None, None, {'type': 'reset'}] - - return [dash.no_update] * 3 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('dict_data-search-form-container', 'hidden'), Output('dict_data-hidden-tooltip', 'title')], Input('dict_data-hidden', 'nClicks'), State('dict_data-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_dict_data_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/menu_c/menu_c.py b/dash-fastapi-frontend/callbacks/system_c/menu_c/menu_c.py index f63d6798b2de469ecd9f22e5c30a4f8562e9936b..de196db92fb53d4bcde5cbbf976f6c61b9376c8e 100644 --- a/dash-fastapi-frontend/callbacks/system_c/menu_c/menu_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/menu_c/menu_c.py @@ -99,32 +99,41 @@ def get_menu_table_data(search_click, refresh_click, operations, fold_click, men return [dash.no_update] * 4 + [None] -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('menu-menu_name-input', 'value'), Output('menu-status-select', 'value'), Output('menu-operations-store', 'data')], Input('menu-reset', 'nClicks'), prevent_initial_call=True ) -def reset_menu_query_params(reset_click): - if reset_click: - return [None, None, {'type': 'reset'}] - - return [dash.no_update] * 3 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('menu-search-form-container', 'hidden'), Output('menu-hidden-tooltip', 'title')], Input('menu-hidden', 'nClicks'), State('menu-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_menu_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/notice_c.py b/dash-fastapi-frontend/callbacks/system_c/notice_c.py index 2d3d8c7c9caeeac01b664679d6931b435ee5d535..953deeb02c9ec67f75eb1c76911743c90c4e29a7 100644 --- a/dash-fastapi-frontend/callbacks/system_c/notice_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/notice_c.py @@ -109,7 +109,15 @@ def get_notice_table_data(search_click, refresh_click, pagination, operations, n return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('notice-notice_title-input', 'value'), Output('notice-update_by-input', 'value'), Output('notice-notice_type-select', 'value'), @@ -118,25 +126,26 @@ def get_notice_table_data(search_click, refresh_click, pagination, operations, n Input('notice-reset', 'nClicks'), prevent_initial_call=True ) -def reset_notice_query_params(reset_click): - if reset_click: - return [None, None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('notice-search-form-container', 'hidden'), Output('notice-hidden-tooltip', 'title')], Input('notice-hidden', 'nClicks'), State('notice-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_notice_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/post_c.py b/dash-fastapi-frontend/callbacks/system_c/post_c.py index 776851f46f7ba645e39a566ecf2ce90c3ca939ee..30c46dc28190d80bea6bb8fff2c7e60d79b83b4e 100644 --- a/dash-fastapi-frontend/callbacks/system_c/post_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/post_c.py @@ -81,7 +81,15 @@ def get_post_table_data(search_click, refresh_click, pagination, operations, pos return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('post-post_code-input', 'value'), Output('post-post_name-input', 'value'), Output('post-status-select', 'value'), @@ -89,25 +97,26 @@ def get_post_table_data(search_click, refresh_click, pagination, operations, pos Input('post-reset', 'nClicks'), prevent_initial_call=True ) -def reset_post_query_params(reset_click): - if reset_click: - return [None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 4 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('post-search-form-container', 'hidden'), Output('post-hidden-tooltip', 'title')], Input('post-hidden', 'nClicks'), State('post-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_post_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( diff --git a/dash-fastapi-frontend/callbacks/system_c/role_c/allocate_user_c.py b/dash-fastapi-frontend/callbacks/system_c/role_c/allocate_user_c.py new file mode 100644 index 0000000000000000000000000000000000000000..326839503d3778dea9dcf3ed8fab1b0f5901e5d7 --- /dev/null +++ b/dash-fastapi-frontend/callbacks/system_c/role_c/allocate_user_c.py @@ -0,0 +1,252 @@ +import dash +import time +import uuid +from dash.dependencies import Input, Output, State, ALL, MATCH +import feffery_utils_components as fuc + +from server import app +from api.role import get_allocated_user_list_api, get_unallocated_user_list_api, auth_user_select_all_api, auth_user_cancel_api + + +@app.callback( + [Output({'type': 'allocate_user-list-table', 'index': MATCH}, 'data', allow_duplicate=True), + Output({'type': 'allocate_user-list-table', 'index': MATCH}, 'pagination', allow_duplicate=True), + Output({'type': 'allocate_user-list-table', 'index': MATCH}, 'key'), + Output({'type': 'allocate_user-list-table', 'index': MATCH}, 'selectedRowKeys')], + [Input({'type': 'allocate_user-search', 'index': MATCH}, 'nClicks'), + Input({'type': 'allocate_user-refresh', 'index': MATCH}, 'nClicks'), + Input({'type': 'allocate_user-list-table', 'index': MATCH}, 'pagination'), + Input({'type': 'allocate_user-operations-container', 'index': MATCH}, 'data')], + [State({'type': 'allocate_user-user_name-input', 'index': MATCH}, 'value'), + State({'type': 'allocate_user-phonenumber-input', 'index': MATCH}, 'value'), + State('allocate_user-role_id-container', 'data'), + State('allocate_user-button-perms-container', 'data')], + prevent_initial_call=True +) +def get_allocate_user_table_data(search_click, refresh_click, pagination, operations, user_name, phonenumber, role_id, button_perms): + + query_params = dict( + role_id=int(role_id), + user_name=user_name, + phonenumber=phonenumber, + page_num=1, + page_size=10 + ) + triggered_id = dash.ctx.triggered_id + if triggered_id.type == 'allocate_user-list-table': + query_params = dict( + role_id=int(role_id), + user_name=user_name, + phonenumber=phonenumber, + page_num=pagination['current'], + page_size=pagination['pageSize'] + ) + if search_click or refresh_click or pagination or operations: + table_info = {} + if triggered_id.index == 'allocated': + table_info = get_allocated_user_list_api(query_params) + if triggered_id.index == 'unallocated': + table_info = get_unallocated_user_list_api(query_params) + if table_info.get('code') == 200: + table_data = table_info['data']['rows'] + table_pagination = dict( + pageSize=table_info['data']['page_size'], + current=table_info['data']['page_num'], + showSizeChanger=True, + pageSizeOptions=[10, 30, 50, 100], + showQuickJumper=True, + total=table_info['data']['total'] + ) + for item in table_data: + if item['status'] == '0': + item['status'] = dict(tag='正常', color='blue') + else: + item['status'] = dict(tag='停用', color='volcano') + item['key'] = str(item['user_id']) + if triggered_id.index == 'allocated': + item['operation'] = [ + { + 'content': '取消授权', + 'type': 'link', + 'icon': 'antd-close-circle' + } if 'system:role:edit' in button_perms else {}, + ] + + return [table_data, table_pagination, str(uuid.uuid4()), None] + + return [dash.no_update, dash.no_update, dash.no_update, dash.no_update] + + return [dash.no_update] * 4 + + +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', + [Output({'type': 'allocate_user-user_name-input', 'index': MATCH}, 'value'), + Output({'type': 'allocate_user-phonenumber-input', 'index': MATCH}, 'value'), + Output({'type': 'allocate_user-operations-container', 'index': MATCH}, 'data')], + Input({'type': 'allocate_user-reset', 'index': MATCH}, 'nClicks'), + prevent_initial_call=True +) + + +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', + [Output({'type': 'allocate_user-search-form-container', 'index': MATCH}, 'hidden'), + Output({'type': 'allocate_user-hidden-tooltip', 'index': MATCH}, 'title')], + Input({'type': 'allocate_user-hidden', 'index': MATCH}, 'nClicks'), + State({'type': 'allocate_user-search-form-container', 'index': MATCH}, 'hidden'), + prevent_initial_call=True +) + + +@app.callback( + Output({'type': 'allocate_user-operation-button', 'index': 'delete'}, 'disabled'), + Input({'type': 'allocate_user-list-table', 'index': 'allocated'}, 'selectedRowKeys'), + prevent_initial_call=True +) +def change_allocated_user_delete_button_status(table_rows_selected): + outputs_list = dash.ctx.outputs_list + if outputs_list: + if table_rows_selected: + return False + + return True + + return dash.no_update + + +@app.callback( + [Output('allocate_user-modal', 'visible'), + Output({'type': 'allocate_user-search', 'index': 'unallocated'}, 'nClicks')], + Input('allocate_user-add', 'nClicks'), + State({'type': 'allocate_user-search', 'index': 'unallocated'}, 'nClicks'), + prevent_initial_call=True +) +def allocate_user_modal(add_click, unallocated_user): + if add_click: + + return [True, unallocated_user + 1 if unallocated_user else 1] + + return [dash.no_update] * 2 + + +@app.callback( + [Output({'type': 'allocate_user-operations-container', 'index': 'allocated'}, 'data', allow_duplicate=True), + Output({'type': 'allocate_user-operations-container', 'index': 'unallocated'}, 'data', allow_duplicate=True), + Output('api-check-token', 'data', allow_duplicate=True), + Output('global-message-container', 'children', allow_duplicate=True)], + Input('allocate_user-modal', 'okCounts'), + [State({'type': 'allocate_user-list-table', 'index': 'unallocated'}, 'selectedRowKeys'), + State('allocate_user-role_id-container', 'data')], + prevent_initial_call=True +) +def allocate_user_add_confirm(add_confirm, selected_row_keys, role_id): + if add_confirm: + if selected_row_keys: + + params = {'user_ids': ','.join(selected_row_keys), 'role_ids': role_id} + add_button_info = auth_user_select_all_api(params) + if add_button_info['code'] == 200: + return [ + {'type': 'delete'}, + {'type': 'delete'}, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('授权成功', type='success') + ] + + return [ + dash.no_update, + dash.no_update, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('授权失败', type='error') + ] + + return [ + dash.no_update, + dash.no_update, + dash.no_update, + fuc.FefferyFancyMessage('请选择用户', type='error') + ] + + return [dash.no_update] * 4 + + +@app.callback( + [Output('allocate_user-delete-text', 'children'), + Output('allocate_user-delete-confirm-modal', 'visible'), + Output('allocate_user-delete-ids-store', 'data')], + [Input({'type': 'allocate_user-operation-button', 'index': ALL}, 'nClicks'), + Input({'type': 'allocate_user-list-table', 'index': 'allocated'}, 'nClicksButton')], + [State({'type': 'allocate_user-list-table', 'index': 'allocated'}, 'selectedRowKeys'), + State({'type': 'allocate_user-list-table', 'index': 'allocated'}, 'clickedContent'), + State({'type': 'allocate_user-list-table', 'index': 'allocated'}, 'recentlyButtonClickedRow')], + prevent_initial_call=True +) +def allocate_user_delete_modal(operation_click, button_click, + selected_row_keys, clicked_content, recently_button_clicked_row): + trigger_id = dash.ctx.triggered_id + if trigger_id.type == 'allocate_user-operation-button' or ( + trigger_id.type == 'allocate_user-list-table' and clicked_content == '取消授权'): + + if trigger_id.type == 'allocate_user-operation-button': + user_ids = ','.join(selected_row_keys) + else: + if clicked_content == '取消授权': + user_ids = recently_button_clicked_row['key'] + else: + return dash.no_update + + return [ + f'是否确认取消用户id为{user_ids}的授权?', + True, + user_ids + ] + + return [dash.no_update] * 3 + + +@app.callback( + [Output({'type': 'allocate_user-operations-container', 'index': 'allocated'}, 'data', allow_duplicate=True), + Output('api-check-token', 'data', allow_duplicate=True), + Output('global-message-container', 'children', allow_duplicate=True)], + Input('allocate_user-delete-confirm-modal', 'okCounts'), + [State('allocate_user-delete-ids-store', 'data'), + State('allocate_user-role_id-container', 'data')], + prevent_initial_call=True +) +def allocate_user_delete_confirm(delete_confirm, user_ids_data, role_id): + if delete_confirm: + + params = {'user_ids': user_ids_data, 'role_ids': role_id} + delete_button_info = auth_user_cancel_api(params) + if delete_button_info['code'] == 200: + return [ + {'type': 'delete'}, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('取消授权成功', type='success') + ] + + return [ + dash.no_update, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('取消授权失败', type='error') + ] + + return [dash.no_update] * 3 diff --git a/dash-fastapi-frontend/callbacks/system_c/role_c.py b/dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py similarity index 72% rename from dash-fastapi-frontend/callbacks/system_c/role_c.py rename to dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py index 5c9b1355e8f24665b68b3084bd5e3202e0145623..b419d9bf709ce0a3d01d2ac9639a532b7f04687d 100644 --- a/dash-fastapi-frontend/callbacks/system_c/role_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py @@ -3,6 +3,7 @@ import time import uuid from dash import dcc from dash.dependencies import Input, Output, State, ALL +import feffery_antd_components as fac import feffery_utils_components as fuc from server import app @@ -75,18 +76,90 @@ def get_role_table_data(search_click, refresh_click, pagination, operations, rol if item['role_id'] == 1: item['operation'] = [] else: - item['operation'] = [ - { - 'content': '修改', - 'type': 'link', - 'icon': 'antd-edit' - } if 'system:role:edit' in button_perms else {}, - { - 'content': '删除', - 'type': 'link', - 'icon': 'antd-delete' - } if 'system:role:remove' in button_perms else {}, - ] + item['operation'] = fac.AntdSpace( + [ + fac.AntdButton( + '修改', + id={ + 'type': 'role-operation-table', + 'operation': 'edit', + 'index': str(item['role_id']) + }, + type='link', + icon=fac.AntdIcon( + icon='antd-edit' + ), + style={ + 'padding': 0 + } + ) if 'system:role:edit' in button_perms else [], + fac.AntdButton( + '删除', + id={ + 'type': 'role-operation-table', + 'operation': 'delete', + 'index': str(item['role_id']) + }, + type='link', + icon=fac.AntdIcon( + icon='antd-delete' + ), + style={ + 'padding': 0 + } + ) if 'system:role:remove' in button_perms else [], + fac.AntdPopover( + fac.AntdButton( + '更多', + type='link', + icon=fac.AntdIcon( + icon='antd-more' + ), + style={ + 'padding': 0 + } + ), + content=fac.AntdSpace( + [ + fac.AntdButton( + '数据权限', + id={ + 'type': 'role-operation-table', + 'operation': 'datascope', + 'index': str(item['role_id']) + }, + type='text', + block=True, + icon=fac.AntdIcon( + icon='antd-check-circle' + ), + style={ + 'padding': 0 + } + ), + fac.AntdButton( + '分配用户', + id={ + 'type': 'role-operation-table', + 'operation': 'allocation', + 'index': str(item['role_id']) + }, + type='text', + block=True, + icon=fac.AntdIcon( + icon='antd-user' + ), + style={ + 'padding': 0 + } + ), + ], + direction='vertical' + ), + placement='bottomRight' + ) + ] + ) return [table_data, table_pagination, str(uuid.uuid4()), None, {'timestamp': time.time()}] @@ -95,7 +168,15 @@ def get_role_table_data(search_click, refresh_click, pagination, operations, rol return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('role-role_name-input', 'value'), Output('role-role_key-input', 'value'), Output('role-status-select', 'value'), @@ -104,29 +185,30 @@ def get_role_table_data(search_click, refresh_click, pagination, operations, rol Input('role-reset', 'nClicks'), prevent_initial_call=True ) -def reset_role_query_params(reset_click): - if reset_click: - return [None, None, None, None, {'type': 'reset'}] - - return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('role-search-form-container', 'hidden'), Output('role-hidden-tooltip', 'title')], Input('role-hidden', 'nClicks'), State('role-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_role_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( - Output({'type': 'role-operation-button', 'index': 'edit'}, 'disabled'), + Output({'type': 'role-operation-button', 'operation': 'edit'}, 'disabled'), Input('role-list-table', 'selectedRowKeys'), prevent_initial_call=True ) @@ -145,7 +227,7 @@ def change_role_edit_button_status(table_rows_selected): @app.callback( - Output({'type': 'role-operation-button', 'index': 'delete'}, 'disabled'), + Output({'type': 'role-operation-button', 'operation': 'delete'}, 'disabled'), Input('role-list-table', 'selectedRowKeys'), prevent_initial_call=True ) @@ -250,23 +332,19 @@ def change_role_menu_mode(parent_children, current_role_menu): Output('api-check-token', 'data', allow_duplicate=True), Output('role-edit-id-store', 'data'), Output('role-operations-store-bk', 'data')], - [Input({'type': 'role-operation-button', 'index': ALL}, 'nClicks'), - Input('role-list-table', 'nClicksButton')], - [State('role-list-table', 'selectedRowKeys'), - State('role-list-table', 'clickedContent'), - State('role-list-table', 'recentlyButtonClickedRow')], + [Input({'type': 'role-operation-button', 'operation': ALL}, 'nClicks'), + Input({'type': 'role-operation-table', 'operation': ALL, 'index': ALL}, 'nClicks')], + State('role-list-table', 'selectedRowKeys'), prevent_initial_call=True ) -def add_edit_role_modal(operation_click, button_click, selected_row_keys, clicked_content, recently_button_clicked_row): +def add_edit_role_modal(operation_click, button_click, selected_row_keys): trigger_id = dash.ctx.triggered_id - if trigger_id == {'index': 'add', 'type': 'role-operation-button'} \ - or trigger_id == {'index': 'edit', 'type': 'role-operation-button'} \ - or (trigger_id == 'role-list-table' and clicked_content == '修改'): + if trigger_id.operation in ['add', 'edit']: menu_params = dict(menu_name='', type='role') tree_info = get_menu_tree_api(menu_params) if tree_info.get('code') == 200: tree_data = tree_info['data'] - if trigger_id == {'index': 'add', 'type': 'role-operation-button'}: + if trigger_id.type == 'role-operation-button' and trigger_id.operation == 'add': return [ True, '新增角色', @@ -285,11 +363,11 @@ def add_edit_role_modal(operation_click, button_click, selected_row_keys, clicke None, {'type': 'add'} ] - elif trigger_id == {'index': 'edit', 'type': 'role-operation-button'} or (trigger_id == 'role-list-table' and clicked_content == '修改'): - if trigger_id == {'index': 'edit', 'type': 'role-operation-button'}: + elif trigger_id.operation == 'edit': + if trigger_id.type == 'role-operation-button': role_id = int(','.join(selected_row_keys)) else: - role_id = int(recently_button_clicked_row['key']) + role_id = int(trigger_id.index) role_info_res = get_role_detail_api(role_id=role_id) if role_info_res['code'] == 200: role_info = role_info_res['data'] @@ -464,24 +542,20 @@ def table_switch_role_status(recently_switch_data_index, recently_switch_status, [Output('role-delete-text', 'children'), Output('role-delete-confirm-modal', 'visible'), Output('role-delete-ids-store', 'data')], - [Input({'type': 'role-operation-button', 'index': ALL}, 'nClicks'), - Input('role-list-table', 'nClicksButton')], - [State('role-list-table', 'selectedRowKeys'), - State('role-list-table', 'clickedContent'), - State('role-list-table', 'recentlyButtonClickedRow')], + [Input({'type': 'role-operation-button', 'operation': ALL}, 'nClicks'), + Input({'type': 'role-operation-table', 'operation': ALL, 'index': ALL}, 'nClicks')], + State('role-list-table', 'selectedRowKeys'), prevent_initial_call=True ) -def role_delete_modal(operation_click, button_click, - selected_row_keys, clicked_content, recently_button_clicked_row): +def role_delete_modal(operation_click, button_click, selected_row_keys): trigger_id = dash.ctx.triggered_id - if trigger_id == {'index': 'delete', 'type': 'role-operation-button'} or ( - trigger_id == 'role-list-table' and clicked_content == '删除'): + if trigger_id.operation == 'delete': - if trigger_id == {'index': 'delete', 'type': 'role-operation-button'}: + if trigger_id.type == 'role-operation-button': role_ids = ','.join(selected_row_keys) else: - if clicked_content == '删除': - role_ids = recently_button_clicked_row['key'] + if trigger_id.type == 'role-operation-table': + role_ids = trigger_id.index else: return dash.no_update @@ -523,6 +597,26 @@ def role_delete_confirm(delete_confirm, role_ids_data): return [dash.no_update] * 3 +@app.callback( + [Output('role_to_allocated_user-modal', 'visible'), + Output({'type': 'allocate_user-search', 'index': 'allocated'}, 'nClicks'), + Output('allocate_user-role_id-container', 'data')], + Input({'type': 'role-operation-table', 'operation': ALL, 'index': ALL}, 'nClicks'), + State({'type': 'allocate_user-search', 'index': 'allocated'}, 'nClicks'), + prevent_initial_call=True +) +def role_to_allocated_user_modal(allocated_click, allocated_user_search_nclick): + trigger_id = dash.ctx.triggered_id + if trigger_id.operation == 'allocation': + return [ + True, + allocated_user_search_nclick + 1 if allocated_user_search_nclick else 1, + trigger_id.index + ] + + return [dash.no_update] * 3 + + @app.callback( [Output('role-export-container', 'data', allow_duplicate=True), Output('role-export-complete-judge-container', 'data'), @@ -566,3 +660,16 @@ def reset_role_export_status(data): return None return dash.no_update + + +# 由于采用了自定义单元格元素,路由变化时需要重置selectedRows,不然会报错 +app.clientside_callback( + ''' + (url) => { + return null; + } + ''', + Output('role-list-table', 'selectedRows'), + Input('url-container', 'pathname'), + prevent_initial_call=True +) diff --git a/dash-fastapi-frontend/callbacks/system_c/user_c/allocate_role_c.py b/dash-fastapi-frontend/callbacks/system_c/user_c/allocate_role_c.py new file mode 100644 index 0000000000000000000000000000000000000000..a62052b449b83f80f40fcb07d26134e32fd297f0 --- /dev/null +++ b/dash-fastapi-frontend/callbacks/system_c/user_c/allocate_role_c.py @@ -0,0 +1,252 @@ +import dash +import time +import uuid +from dash.dependencies import Input, Output, State, ALL, MATCH +import feffery_utils_components as fuc + +from server import app +from api.user import get_allocated_role_list_api, get_unallocated_role_list_api, auth_role_select_all_api, auth_role_cancel_api + + +@app.callback( + [Output({'type': 'allocate_role-list-table', 'index': MATCH}, 'data', allow_duplicate=True), + Output({'type': 'allocate_role-list-table', 'index': MATCH}, 'pagination', allow_duplicate=True), + Output({'type': 'allocate_role-list-table', 'index': MATCH}, 'key'), + Output({'type': 'allocate_role-list-table', 'index': MATCH}, 'selectedRowKeys')], + [Input({'type': 'allocate_role-search', 'index': MATCH}, 'nClicks'), + Input({'type': 'allocate_role-refresh', 'index': MATCH}, 'nClicks'), + Input({'type': 'allocate_role-list-table', 'index': MATCH}, 'pagination'), + Input({'type': 'allocate_role-operations-container', 'index': MATCH}, 'data')], + [State({'type': 'allocate_role-role_name-input', 'index': MATCH}, 'value'), + State({'type': 'allocate_role-role_key-input', 'index': MATCH}, 'value'), + State('allocate_role-user_id-container', 'data'), + State('allocate_role-button-perms-container', 'data')], + prevent_initial_call=True +) +def get_allocate_role_table_data(search_click, refresh_click, pagination, operations, role_name, role_key, user_id, button_perms): + + query_params = dict( + user_id=int(user_id), + role_name=role_name, + role_key=role_key, + page_num=1, + page_size=10 + ) + triggered_id = dash.ctx.triggered_id + if triggered_id.type == 'allocate_role-list-table': + query_params = dict( + user_id=int(user_id), + role_name=role_name, + role_key=role_key, + page_num=pagination['current'], + page_size=pagination['pageSize'] + ) + if search_click or refresh_click or pagination or operations: + table_info = {} + if triggered_id.index == 'allocated': + table_info = get_allocated_role_list_api(query_params) + if triggered_id.index == 'unallocated': + table_info = get_unallocated_role_list_api(query_params) + if table_info.get('code') == 200: + table_data = table_info['data']['rows'] + table_pagination = dict( + pageSize=table_info['data']['page_size'], + current=table_info['data']['page_num'], + showSizeChanger=True, + pageSizeOptions=[10, 30, 50, 100], + showQuickJumper=True, + total=table_info['data']['total'] + ) + for item in table_data: + if item['status'] == '0': + item['status'] = dict(tag='正常', color='blue') + else: + item['status'] = dict(tag='停用', color='volcano') + item['key'] = str(item['role_id']) + if triggered_id.index == 'allocated': + item['operation'] = [ + { + 'content': '取消授权', + 'type': 'link', + 'icon': 'antd-close-circle' + } if 'system:user:edit' in button_perms else {}, + ] + + return [table_data, table_pagination, str(uuid.uuid4()), None] + + return [dash.no_update, dash.no_update, dash.no_update, dash.no_update] + + return [dash.no_update] * 4 + + +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', + [Output({'type': 'allocate_role-role_name-input', 'index': MATCH}, 'value'), + Output({'type': 'allocate_role-role_key-input', 'index': MATCH}, 'value'), + Output({'type': 'allocate_role-operations-container', 'index': MATCH}, 'data')], + Input({'type': 'allocate_role-reset', 'index': MATCH}, 'nClicks'), + prevent_initial_call=True +) + + +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', + [Output({'type': 'allocate_role-search-form-container', 'index': MATCH}, 'hidden'), + Output({'type': 'allocate_role-hidden-tooltip', 'index': MATCH}, 'title')], + Input({'type': 'allocate_role-hidden', 'index': MATCH}, 'nClicks'), + State({'type': 'allocate_role-search-form-container', 'index': MATCH}, 'hidden'), + prevent_initial_call=True +) + + +@app.callback( + Output({'type': 'allocate_role-operation-button', 'index': 'delete'}, 'disabled'), + Input({'type': 'allocate_role-list-table', 'index': 'allocated'}, 'selectedRowKeys'), + prevent_initial_call=True +) +def change_allocated_role_delete_button_status(table_rows_selected): + outputs_list = dash.ctx.outputs_list + if outputs_list: + if table_rows_selected: + return False + + return True + + return dash.no_update + + +@app.callback( + [Output('allocate_role-modal', 'visible'), + Output({'type': 'allocate_role-search', 'index': 'unallocated'}, 'nClicks')], + Input('allocate_role-add', 'nClicks'), + State({'type': 'allocate_role-search', 'index': 'unallocated'}, 'nClicks'), + prevent_initial_call=True +) +def allocate_role_modal(add_click, unallocated_role): + if add_click: + + return [True, unallocated_role + 1 if unallocated_role else 1] + + return [dash.no_update] * 2 + + +@app.callback( + [Output({'type': 'allocate_role-operations-container', 'index': 'allocated'}, 'data', allow_duplicate=True), + Output({'type': 'allocate_role-operations-container', 'index': 'unallocated'}, 'data', allow_duplicate=True), + Output('api-check-token', 'data', allow_duplicate=True), + Output('global-message-container', 'children', allow_duplicate=True)], + Input('allocate_role-modal', 'okCounts'), + [State({'type': 'allocate_role-list-table', 'index': 'unallocated'}, 'selectedRowKeys'), + State('allocate_role-user_id-container', 'data')], + prevent_initial_call=True +) +def allocate_user_add_confirm(add_confirm, selected_row_keys, user_id): + if add_confirm: + if selected_row_keys: + + params = {'user_ids': user_id, 'role_ids': ','.join(selected_row_keys)} + add_button_info = auth_role_select_all_api(params) + if add_button_info['code'] == 200: + return [ + {'type': 'delete'}, + {'type': 'delete'}, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('授权成功', type='success') + ] + + return [ + dash.no_update, + dash.no_update, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('授权失败', type='error') + ] + + return [ + dash.no_update, + dash.no_update, + dash.no_update, + fuc.FefferyFancyMessage('请选择角色', type='error') + ] + + return [dash.no_update] * 4 + + +@app.callback( + [Output('allocate_role-delete-text', 'children'), + Output('allocate_role-delete-confirm-modal', 'visible'), + Output('allocate_role-delete-ids-store', 'data')], + [Input({'type': 'allocate_role-operation-button', 'index': ALL}, 'nClicks'), + Input({'type': 'allocate_role-list-table', 'index': 'allocated'}, 'nClicksButton')], + [State({'type': 'allocate_role-list-table', 'index': 'allocated'}, 'selectedRowKeys'), + State({'type': 'allocate_role-list-table', 'index': 'allocated'}, 'clickedContent'), + State({'type': 'allocate_role-list-table', 'index': 'allocated'}, 'recentlyButtonClickedRow')], + prevent_initial_call=True +) +def allocate_role_delete_modal(operation_click, button_click, + selected_row_keys, clicked_content, recently_button_clicked_row): + trigger_id = dash.ctx.triggered_id + if trigger_id.type == 'allocate_role-operation-button' or ( + trigger_id.type == 'allocate_role-list-table' and clicked_content == '取消授权'): + + if trigger_id.type == 'allocate_role-operation-button': + role_ids = ','.join(selected_row_keys) + else: + if clicked_content == '取消授权': + role_ids = recently_button_clicked_row['key'] + else: + return dash.no_update + + return [ + f'是否确认取消角色id为{role_ids}的授权?', + True, + role_ids + ] + + return [dash.no_update] * 3 + + +@app.callback( + [Output({'type': 'allocate_role-operations-container', 'index': 'allocated'}, 'data', allow_duplicate=True), + Output('api-check-token', 'data', allow_duplicate=True), + Output('global-message-container', 'children', allow_duplicate=True)], + Input('allocate_role-delete-confirm-modal', 'okCounts'), + [State('allocate_role-delete-ids-store', 'data'), + State('allocate_role-user_id-container', 'data')], + prevent_initial_call=True +) +def allocate_role_delete_confirm(delete_confirm, role_ids_data, user_id): + if delete_confirm: + + params = {'user_ids': user_id, 'role_ids': role_ids_data} + delete_button_info = auth_role_cancel_api(params) + if delete_button_info['code'] == 200: + return [ + {'type': 'delete'}, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('取消授权成功', type='success') + ] + + return [ + dash.no_update, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('取消授权失败', type='error') + ] + + return [dash.no_update] * 3 diff --git a/dash-fastapi-frontend/callbacks/system_c/user_c/user_c.py b/dash-fastapi-frontend/callbacks/system_c/user_c/user_c.py index 4e53bf7bb8ed943e19e131ceeb56c24fad5b1545..7bdd6cee1f72c2413069598fe8ea54ffa6e50031 100644 --- a/dash-fastapi-frontend/callbacks/system_c/user_c/user_c.py +++ b/dash-fastapi-frontend/callbacks/system_c/user_c/user_c.py @@ -7,7 +7,7 @@ import feffery_utils_components as fuc from server import app from api.dept import get_dept_tree_api -from api.user import get_user_list_api, get_user_detail_api, add_user_api, edit_user_api, delete_user_api, reset_user_password_api, export_user_list_api +from api.user import get_user_list_api, get_user_detail_api, add_user_api, edit_user_api, delete_user_api, reset_user_password_api, batch_import_user_api, download_user_import_template_api, export_user_list_api from api.role import get_role_select_option_api from api.post import get_post_select_option_api @@ -93,9 +93,9 @@ def get_user_table_data_by_dept_tree(selected_dept_tree, search_click, refresh_c ) for item in table_data: if item['status'] == '0': - item['status'] = dict(checked=True) + item['status'] = dict(checked=True, disabled=item['user_id'] == 1) else: - item['status'] = dict(checked=False) + item['status'] = dict(checked=False, disabled=item['user_id'] == 1) item['key'] = str(item['user_id']) if item['user_id'] == 1: item['operation'] = [] @@ -112,7 +112,11 @@ def get_user_table_data_by_dept_tree(selected_dept_tree, search_click, refresh_c { 'title': '重置密码', 'icon': 'antd-key' - } if 'system:user:resetPwd' in button_perms else None + } if 'system:user:resetPwd' in button_perms else None, + { + 'title': '分配角色', + 'icon': 'antd-check-circle' + } if 'system:user:edit' in button_perms else None ] return [table_data, table_pagination, str(uuid.uuid4()), None, {'timestamp': time.time()}] @@ -122,7 +126,15 @@ def get_user_table_data_by_dept_tree(selected_dept_tree, search_click, refresh_c return [dash.no_update] * 5 -@app.callback( +app.clientside_callback( + ''' + (reset_click) => { + if (reset_click) { + return [null, null, null, null, null, {'type': 'reset'}] + } + return window.dash_clientside.no_update; + } + ''', [Output('dept-tree', 'selectedKeys'), Output('user-user_name-input', 'value'), Output('user-phone_number-input', 'value'), @@ -132,25 +144,26 @@ def get_user_table_data_by_dept_tree(selected_dept_tree, search_click, refresh_c Input('user-reset', 'nClicks'), prevent_initial_call=True ) -def reset_user_query_params(reset_click): - if reset_click: - return [None, None, None, None, None, {'type': 'reset'}] - return [dash.no_update] * 6 - -@app.callback( +app.clientside_callback( + ''' + (hidden_click, hidden_status) => { + if (hidden_click) { + return [ + !hidden_status, + hidden_status ? '隐藏搜索' : '显示搜索' + ] + } + return window.dash_clientside.no_update; + } + ''', [Output('user-search-form-container', 'hidden'), Output('user-hidden-tooltip', 'title')], Input('user-hidden', 'nClicks'), State('user-search-form-container', 'hidden'), prevent_initial_call=True ) -def hidden_user_search_form(hidden_click, hidden_status): - if hidden_click: - - return [not hidden_status, '隐藏搜索' if hidden_status else '显示搜索'] - return [dash.no_update] * 2 @app.callback( @@ -588,33 +601,155 @@ def user_reset_password_confirm(reset_confirm, user_id_data, reset_password): return [dash.no_update] * 3 +@app.callback( + [Output('user_to_allocated_role-modal', 'visible'), + Output({'type': 'allocate_role-search', 'index': 'allocated'}, 'nClicks'), + Output('allocate_role-user_id-container', 'data')], + Input('user-list-table', 'nClicksDropdownItem'), + [State('user-list-table', 'recentlyClickedDropdownItemTitle'), + State('user-list-table', 'recentlyDropdownItemClickedRow'), + State({'type': 'allocate_role-search', 'index': 'allocated'}, 'nClicks')], + prevent_initial_call=True +) +def role_to_allocated_user_modal(dropdown_click, recently_clicked_dropdown_item_title, recently_dropdown_item_clicked_row, allocated_role_search_nclick): + if dropdown_click and recently_clicked_dropdown_item_title == '分配角色': + + return [ + True, + allocated_role_search_nclick + 1 if allocated_role_search_nclick else 1, + recently_dropdown_item_clicked_row['key'] + ] + + return [dash.no_update] * 3 + + +app.clientside_callback( + ''' + (nClicks) => { + if (nClicks) { + return [ + true, + [], + [], + false + ]; + } + return [ + false, + window.dash_clientside.no_update, + window.dash_clientside.no_update, + window.dash_clientside.no_update + ]; + } + ''', + [Output('user-import-confirm-modal', 'visible'), + Output('user-upload-choose', 'listUploadTaskRecord'), + Output('user-upload-choose', 'defaultFileList'), + Output('user-import-update-check', 'checked')], + Input('user-import', 'nClicks'), + prevent_initial_call=True +) + + +@app.callback( + [Output('user-import-confirm-modal', 'confirmLoading'), + Output('batch-result-modal', 'visible'), + Output('batch-result-content', 'children'), + Output('user-operations-store', 'data', allow_duplicate=True), + Output('api-check-token', 'data', allow_duplicate=True), + Output('global-message-container', 'children', allow_duplicate=True)], + Input('user-import-confirm-modal', 'okCounts'), + [State('user-upload-choose', 'listUploadTaskRecord'), + State('user-import-update-check', 'checked')], + prevent_initial_call=True +) +def user_import_confirm(import_confirm, list_upload_task_record, is_update): + if import_confirm: + if list_upload_task_record: + url = list_upload_task_record[-1].get('url') + batch_param = dict(url=url, is_update=is_update) + batch_import_result = batch_import_user_api(batch_param) + if batch_import_result.get('code') == 200: + return [ + False, + True if batch_import_result.get('message') else False, + batch_import_result.get('message'), + {'type': 'batch-import'}, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('导入成功', type='success') + ] + + return [ + False, + True, + batch_import_result.get('message'), + dash.no_update, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('导入失败', type='error') + ] + else: + return [ + False, + dash.no_update, + dash.no_update, + dash.no_update, + dash.no_update, + fuc.FefferyFancyMessage('请上传需要导入的文件', type='error') + ] + + return [dash.no_update] * 6 + + @app.callback( [Output('user-export-container', 'data', allow_duplicate=True), Output('user-export-complete-judge-container', 'data'), Output('api-check-token', 'data', allow_duplicate=True), Output('global-message-container', 'children', allow_duplicate=True)], - Input('user-export', 'nClicks'), + [Input('user-export', 'nClicks'), + Input('download-user-import-template', 'nClicks')], prevent_initial_call=True ) -def export_user_list(export_click): - if export_click: - export_user_res = export_user_list_api({}) - if export_user_res.status_code == 200: - export_user = export_user_res.content +def export_user_list(export_click, download_click): + trigger_id = dash.ctx.triggered_id + if export_click or download_click: + + if trigger_id == 'user-export': + export_user_res = export_user_list_api({}) + if export_user_res.status_code == 200: + export_user = export_user_res.content + + return [ + dcc.send_bytes(export_user, f'用户信息_{time.strftime("%Y%m%d%H%M%S", time.localtime())}.xlsx'), + {'timestamp': time.time()}, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('导出成功', type='success') + ] return [ - dcc.send_bytes(export_user, f'用户信息_{time.strftime("%Y%m%d%H%M%S", time.localtime())}.xlsx'), - {'timestamp': time.time()}, + dash.no_update, + dash.no_update, {'timestamp': time.time()}, - fuc.FefferyFancyMessage('导出成功', type='success') + fuc.FefferyFancyMessage('导出失败', type='error') ] - return [ - dash.no_update, - dash.no_update, - {'timestamp': time.time()}, - fuc.FefferyFancyMessage('导出失败', type='error') - ] + if trigger_id == 'download-user-import-template': + download_template_res = download_user_import_template_api() + if download_template_res.status_code == 200: + download_template = download_template_res.content + + return [ + dcc.send_bytes(download_template, f'用户导入模板_{time.strftime("%Y%m%d%H%M%S", time.localtime())}.xlsx'), + {'timestamp': time.time()}, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('下载成功', type='success') + ] + + return [ + dash.no_update, + dash.no_update, + {'timestamp': time.time()}, + fuc.FefferyFancyMessage('下载失败', type='error') + ] return [dash.no_update] * 4 diff --git a/dash-fastapi-frontend/views/layout/components/aside.py b/dash-fastapi-frontend/views/layout/components/aside.py index dd9b7ebf196cbeb30f87fae1940c849e1e378a16..5be584e63f8e2e0b46a80cbc41863eea93a39092 100644 --- a/dash-fastapi-frontend/views/layout/components/aside.py +++ b/dash-fastapi-frontend/views/layout/components/aside.py @@ -1,4 +1,5 @@ import feffery_antd_components as fac +import dash import callbacks.layout_c.aside_c @@ -14,7 +15,7 @@ def render_aside_content(menu_info): fac.AntdImage( width=32, height=32, - src='assets/imgs/logo.png', + src=dash.get_asset_url('imgs/logo.png'), preview=False, ), flex='1', diff --git a/dash-fastapi-frontend/views/system/role/__init__.py b/dash-fastapi-frontend/views/system/role/__init__.py index 4fd964ef75a8d12437a9fc6103d7687ef71e79b6..1d42f81bc7f57f0d76db9a881de6d2e09bb6b5d2 100644 --- a/dash-fastapi-frontend/views/system/role/__init__.py +++ b/dash-fastapi-frontend/views/system/role/__init__.py @@ -1,7 +1,8 @@ from dash import dcc, html import feffery_antd_components as fac -import callbacks.system_c.role_c +import callbacks.system_c.role_c.role_c +from . import allocate_user from api.role import get_role_list_api @@ -27,18 +28,90 @@ def render(button_perms): if item['role_id'] == 1: item['operation'] = [] else: - item['operation'] = [ - { - 'content': '修改', - 'type': 'link', - 'icon': 'antd-edit' - } if 'system:role:edit' in button_perms else {}, - { - 'content': '删除', - 'type': 'link', - 'icon': 'antd-delete' - } if 'system:role:remove' in button_perms else {}, - ] + item['operation'] = fac.AntdSpace( + [ + fac.AntdButton( + '修改', + id={ + 'type': 'role-operation-table', + 'operation': 'edit', + 'index': str(item['role_id']) + }, + type='link', + icon=fac.AntdIcon( + icon='antd-edit' + ), + style={ + 'padding': 0 + } + ) if 'system:role:edit' in button_perms else [], + fac.AntdButton( + '删除', + id={ + 'type': 'role-operation-table', + 'operation': 'delete', + 'index': str(item['role_id']) + }, + type='link', + icon=fac.AntdIcon( + icon='antd-delete' + ), + style={ + 'padding': 0 + } + ) if 'system:role:remove' in button_perms else [], + fac.AntdPopover( + fac.AntdButton( + '更多', + type='link', + icon=fac.AntdIcon( + icon='antd-more' + ), + style={ + 'padding': 0 + } + ), + content=fac.AntdSpace( + [ + fac.AntdButton( + '数据权限', + id={ + 'type': 'role-operation-table', + 'operation': 'datascope', + 'index': str(item['role_id']) + }, + type='text', + block=True, + icon=fac.AntdIcon( + icon='antd-check-circle' + ), + style={ + 'padding': 0 + } + ), + fac.AntdButton( + '分配用户', + id={ + 'type': 'role-operation-table', + 'operation': 'allocation', + 'index': str(item['role_id']) + }, + type='text', + block=True, + icon=fac.AntdIcon( + icon='antd-user' + ), + style={ + 'padding': 0 + } + ), + ], + direction='vertical' + ), + placement='bottomRight' + ) + ] + ) return [ dcc.Store(id='role-button-perms-container', data=button_perms), @@ -159,7 +232,7 @@ def render(button_perms): ], id={ 'type': 'role-operation-button', - 'index': 'add' + 'operation': 'add' }, style={ 'color': '#1890ff', @@ -177,7 +250,7 @@ def render(button_perms): ], id={ 'type': 'role-operation-button', - 'index': 'edit' + 'operation': 'edit' }, disabled=True, style={ @@ -196,7 +269,7 @@ def render(button_perms): ], id={ 'type': 'role-operation-button', - 'index': 'delete' + 'operation': 'delete' }, disabled=True, style={ @@ -327,9 +400,6 @@ def render(button_perms): { 'title': '操作', 'dataIndex': 'operation', - 'renderOptions': { - 'renderType': 'button' - }, } ], rowSelectionType='checkbox', @@ -563,4 +633,16 @@ def render(button_perms): renderFooter=True, centered=True ), + + # 分配用户modal + fac.AntdModal( + allocate_user.render(button_perms), + id='role_to_allocated_user-modal', + title='分配用户', + mask=False, + maskClosable=False, + width=1000, + renderFooter=False, + okClickClose=False + ) ] diff --git a/dash-fastapi-frontend/views/system/role/allocate_user.py b/dash-fastapi-frontend/views/system/role/allocate_user.py new file mode 100644 index 0000000000000000000000000000000000000000..2deaa70c30981ae146013362aed03e1d130c936a --- /dev/null +++ b/dash-fastapi-frontend/views/system/role/allocate_user.py @@ -0,0 +1,48 @@ +from dash import dcc, html +import feffery_antd_components as fac + +from .component import query_form_table +import callbacks.system_c.role_c.allocate_user_c + + +def render(button_perms): + + return [ + dcc.Store(id='allocate_user-button-perms-container', data=button_perms), + dcc.Store(id='allocate_user-role_id-container'), + # 分配用户模块操作类型存储容器 + dcc.Store(id={ + 'type': 'allocate_user-operations-container', + 'index': 'allocated' + }), + dcc.Store(id={ + 'type': 'allocate_user-operations-container', + 'index': 'unallocated' + }), + # 分配用户模块删除操作行key存储容器 + dcc.Store(id='allocate_user-delete-ids-store'), + query_form_table.render(button_perms=button_perms, allocate_index='allocated', is_operation=True), + + # 添加用户表单modal + fac.AntdModal( + [ + query_form_table.render(button_perms=button_perms, allocate_index='unallocated', is_operation=False), + ], + id='allocate_user-modal', + title='选择用户', + mask=False, + maskClosable=False, + width=900, + renderFooter=True, + okClickClose=False + ), + + # 取消授权二次确认modal + fac.AntdModal( + fac.AntdText('是否确认取消授权?', id='allocate_user-delete-text'), + id='allocate_user-delete-confirm-modal', + visible=False, + title='提示', + renderFooter=True + ), + ] diff --git a/dash-fastapi-frontend/views/system/role/component/query_form_table.py b/dash-fastapi-frontend/views/system/role/component/query_form_table.py new file mode 100644 index 0000000000000000000000000000000000000000..893dd0b26d4d0374e31e06e73a5191a1699a2426 --- /dev/null +++ b/dash-fastapi-frontend/views/system/role/component/query_form_table.py @@ -0,0 +1,290 @@ +from dash import html +import feffery_antd_components as fac + + +def render(button_perms, allocate_index, is_operation): + table_column = [ + { + 'dataIndex': 'user_id', + 'title': '用户id', + 'hidden': True, + }, + { + 'dataIndex': 'user_name', + 'title': '用户名称', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + { + 'dataIndex': 'nick_name', + 'title': '用户昵称', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + { + 'dataIndex': 'email', + 'title': '邮箱', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + { + 'dataIndex': 'phonenumber', + 'title': '手机', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + { + 'dataIndex': 'status', + 'title': '状态', + 'renderOptions': { + 'renderType': 'tags' + }, + }, + { + 'dataIndex': 'create_time', + 'title': '创建时间', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + ] + + if is_operation: + table_column.append( + { + 'title': '操作', + 'dataIndex': 'operation', + 'fixed': 'right', + 'width': 150, + 'renderOptions': { + 'renderType': 'button' + }, + } + ) + + return fac.AntdRow( + [ + fac.AntdCol( + [ + fac.AntdRow( + [ + fac.AntdCol( + html.Div( + [ + fac.AntdForm( + [ + fac.AntdFormItem( + fac.AntdInput( + id={ + 'type': 'allocate_user-user_name-input', + 'index': allocate_index + }, + placeholder='请输入用户名称', + autoComplete='off', + allowClear=True, + style={ + 'width': 240 + } + ), + label='用户名称', + style={'paddingBottom': '10px'}, + ), + fac.AntdFormItem( + fac.AntdInput( + id={ + 'type': 'allocate_user-phonenumber-input', + 'index': allocate_index + }, + placeholder='请输入手机号码', + autoComplete='off', + allowClear=True, + style={ + 'width': 240 + } + ), + label='手机号码', + style={'paddingBottom': '10px'}, + ), + fac.AntdFormItem( + fac.AntdButton( + '搜索', + id={ + 'type': 'allocate_user-search', + 'index': allocate_index + }, + type='primary', + icon=fac.AntdIcon( + icon='antd-search' + ) + ), + style={'paddingBottom': '10px'}, + ), + fac.AntdFormItem( + fac.AntdButton( + '重置', + id={ + 'type': 'allocate_user-reset', + 'index': allocate_index + }, + icon=fac.AntdIcon( + icon='antd-sync' + ) + ), + style={'paddingBottom': '10px'}, + ) + ], + layout='inline', + ) + ], + id={ + 'type': 'allocate_user-search-form-container', + 'index': allocate_index + }, + hidden=False + ), + ) + ] + ), + fac.AntdRow( + [ + fac.AntdCol( + fac.AntdSpace( + [ + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-plus' + ), + '添加用户', + ], + id='allocate_user-add', + style={ + 'color': '#1890ff', + 'background': '#e8f4ff', + 'border-color': '#a3d3ff' + } + ) if 'system:role:edit' in button_perms else [], + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-close-circle' + ), + '批量取消授权', + ], + id={ + 'type': 'allocate_user-operation-button', + 'index': 'delete' + }, + disabled=True, + style={ + 'color': '#ff9292', + 'background': '#ffeded', + 'border-color': '#ffdbdb' + } + ) if 'system:role:edit' in button_perms else [], + ], + style={ + 'paddingBottom': '10px' + } + ), + span=16 + ) if is_operation else [], + fac.AntdCol( + fac.AntdSpace( + [ + html.Div( + fac.AntdTooltip( + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-search' + ), + ], + id={ + 'type': 'allocate_user-hidden', + 'index': allocate_index + }, + shape='circle' + ), + id={ + 'type': 'allocate_user-hidden-tooltip', + 'index': allocate_index + }, + title='隐藏搜索' + ) + ), + html.Div( + fac.AntdTooltip( + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-sync' + ), + ], + id={ + 'type': 'allocate_user-refresh', + 'index': allocate_index + }, + shape='circle' + ), + title='刷新' + ) + ), + ], + style={ + 'float': 'right', + 'paddingBottom': '10px' + } + ), + span=8 if is_operation else 24, + style={ + 'paddingRight': '10px' + } + ) + ], + gutter=5 + ), + fac.AntdRow( + [ + fac.AntdCol( + fac.AntdSpin( + fac.AntdTable( + id={ + 'type': 'allocate_user-list-table', + 'index': allocate_index + }, + data=[], + columns=table_column, + rowSelectionType='checkbox', + rowSelectionWidth=50, + bordered=True, + maxWidth=1000, + pagination={ + 'pageSize': 10, + 'current': 1, + 'showSizeChanger': True, + 'pageSizeOptions': [10, 30, 50, 100], + 'showQuickJumper': True, + 'total': 0 + }, + mode='server-side', + style={ + 'width': '100%', + 'padding-right': '10px' + } + ), + text='数据加载中' + ), + ) + ] + ), + ], + span=24 + ) + ], + gutter=5 + ) diff --git a/dash-fastapi-frontend/views/system/user/__init__.py b/dash-fastapi-frontend/views/system/user/__init__.py index ccc3d9a1e014c0abac4f051551c38e7b0dd35c96..6cfc0ffaa3423b5fd33f0ec1676f2da936c7a82a 100644 --- a/dash-fastapi-frontend/views/system/user/__init__.py +++ b/dash-fastapi-frontend/views/system/user/__init__.py @@ -1,9 +1,11 @@ from dash import dcc, html import feffery_antd_components as fac +from flask import session -from . import profile +from . import profile, allocate_role from api.user import get_user_list_api from api.dept import get_dept_tree_api +from config.global_config import ApiBaseUrlConfig import callbacks.system_c.user_c.user_c @@ -27,9 +29,9 @@ def render(button_perms): total = table_info['data']['total'] for item in table_data: if item['status'] == '0': - item['status'] = dict(checked=True) + item['status'] = dict(checked=True, disabled=item['user_id'] == 1) else: - item['status'] = dict(checked=False) + item['status'] = dict(checked=False, disabled=item['user_id'] == 1) item['key'] = str(item['user_id']) if item['user_id'] == 1: item['operation'] = [] @@ -46,7 +48,11 @@ def render(button_perms): { 'title': '重置密码', 'icon': 'antd-key' - } if 'system:user:resetPwd' in button_perms else None + } if 'system:user:resetPwd' in button_perms else None, + { + 'title': '分配角色', + 'icon': 'antd-check-circle' + } if 'system:user:edit' in button_perms else None ] return [ @@ -868,6 +874,83 @@ def render(button_perms): centered=True ), + # 用户导入modal + fac.AntdModal( + [ + html.Div( + fac.AntdDraggerUpload( + id='user-upload-choose', + apiUrl=f'{ApiBaseUrlConfig.BaseUrl}/common/upload', + downloadUrl=f'{ApiBaseUrlConfig.BaseUrl}/common/caches', + headers={'Authorization': 'Bearer ' + session.get('Authorization')}, + fileTypes=['xls', 'xlsx'], + fileListMaxLength=1, + text='用户导入', + hint='点击或拖拽文件至此处进行上传' + ), + style={ + 'marginTop': '10px' + } + ), + html.Div( + [ + fac.AntdCheckbox( + id='user-import-update-check', + checked=False + ), + fac.AntdText( + '是否更新已经存在的用户数据', + style={ + 'marginLeft': '5px' + } + ) + ], + style={ + 'textAlign': 'center', + 'marginTop': '10px' + } + ), + html.Div( + [ + fac.AntdText('仅允许导入xls、xlsx格式文件。'), + fac.AntdButton( + '下载模板', + id='download-user-import-template', + type='link' + ) + ], + style={ + 'textAlign': 'center', + 'marginTop': '10px' + } + ) + ], + id='user-import-confirm-modal', + visible=False, + title='用户导入', + width=600, + renderFooter=True, + centered=True, + okText='导入', + confirmAutoSpin=True, + loadingOkText='导入中', + okClickClose=False + ), + + fac.AntdModal( + fac.AntdText( + id='batch-result-content', + className={ + 'whiteSpace': 'break-spaces' + } + ), + id='batch-result-modal', + visible=False, + title='用户导入结果', + renderFooter=False, + centered=True + ), + # 重置密码modal fac.AntdModal( [ @@ -891,4 +974,16 @@ def render(button_perms): renderFooter=True, centered=True ), + + # 分配角色modal + fac.AntdModal( + allocate_role.render(button_perms), + id='user_to_allocated_role-modal', + title='分配角色', + mask=False, + maskClosable=False, + width=1000, + renderFooter=False, + okClickClose=False + ) ] diff --git a/dash-fastapi-frontend/views/system/user/allocate_role.py b/dash-fastapi-frontend/views/system/user/allocate_role.py new file mode 100644 index 0000000000000000000000000000000000000000..0b3f60f18d1ffec1287d61167e02be93a72c4491 --- /dev/null +++ b/dash-fastapi-frontend/views/system/user/allocate_role.py @@ -0,0 +1,48 @@ +from dash import dcc, html +import feffery_antd_components as fac + +from .component import query_form_table +import callbacks.system_c.user_c.allocate_role_c + + +def render(button_perms): + + return [ + dcc.Store(id='allocate_role-button-perms-container', data=button_perms), + dcc.Store(id='allocate_role-user_id-container'), + # 分配角色模块操作类型存储容器 + dcc.Store(id={ + 'type': 'allocate_role-operations-container', + 'index': 'allocated' + }), + dcc.Store(id={ + 'type': 'allocate_role-operations-container', + 'index': 'unallocated' + }), + # 分配角色模块删除操作行key存储容器 + dcc.Store(id='allocate_role-delete-ids-store'), + query_form_table.render(button_perms=button_perms, allocate_index='allocated', is_operation=True), + + # 添加用户表单modal + fac.AntdModal( + [ + query_form_table.render(button_perms=button_perms, allocate_index='unallocated', is_operation=False), + ], + id='allocate_role-modal', + title='选择角色', + mask=False, + maskClosable=False, + width=900, + renderFooter=True, + okClickClose=False + ), + + # 取消授权二次确认modal + fac.AntdModal( + fac.AntdText('是否确认取消授权?', id='allocate_role-delete-text'), + id='allocate_role-delete-confirm-modal', + visible=False, + title='提示', + renderFooter=True + ), + ] diff --git a/dash-fastapi-frontend/views/system/user/component/__init__.py b/dash-fastapi-frontend/views/system/user/component/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5ec896d0e14a33cf2b1e6c4aebdd107de7b587c0 --- /dev/null +++ b/dash-fastapi-frontend/views/system/user/component/__init__.py @@ -0,0 +1,3 @@ +from . import ( + query_form_table +) diff --git a/dash-fastapi-frontend/views/system/user/component/query_form_table.py b/dash-fastapi-frontend/views/system/user/component/query_form_table.py new file mode 100644 index 0000000000000000000000000000000000000000..7a74b13a99dc1953cb653226ea7260f9897b4e42 --- /dev/null +++ b/dash-fastapi-frontend/views/system/user/component/query_form_table.py @@ -0,0 +1,283 @@ +from dash import html +import feffery_antd_components as fac + + +def render(button_perms, allocate_index, is_operation): + table_column = [ + { + 'dataIndex': 'role_id', + 'title': '角色id', + 'hidden': True, + }, + { + 'dataIndex': 'role_name', + 'title': '角色名称', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + { + 'dataIndex': 'role_key', + 'title': '权限字符', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + { + 'dataIndex': 'role_sort', + 'title': '显示顺序', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + { + 'dataIndex': 'status', + 'title': '状态', + 'renderOptions': { + 'renderType': 'tags' + }, + }, + { + 'dataIndex': 'create_time', + 'title': '创建时间', + 'renderOptions': { + 'renderType': 'ellipsis' + }, + }, + ] + + if is_operation: + table_column.append( + { + 'title': '操作', + 'dataIndex': 'operation', + 'fixed': 'right', + 'width': 150, + 'renderOptions': { + 'renderType': 'button' + }, + } + ) + + return fac.AntdRow( + [ + fac.AntdCol( + [ + fac.AntdRow( + [ + fac.AntdCol( + html.Div( + [ + fac.AntdForm( + [ + fac.AntdFormItem( + fac.AntdInput( + id={ + 'type': 'allocate_role-role_name-input', + 'index': allocate_index + }, + placeholder='请输入角色名称', + autoComplete='off', + allowClear=True, + style={ + 'width': 240 + } + ), + label='角色名称', + style={'paddingBottom': '10px'}, + ), + fac.AntdFormItem( + fac.AntdInput( + id={ + 'type': 'allocate_role-role_key-input', + 'index': allocate_index + }, + placeholder='请输入权限字符', + autoComplete='off', + allowClear=True, + style={ + 'width': 240 + } + ), + label='权限字符', + style={'paddingBottom': '10px'}, + ), + fac.AntdFormItem( + fac.AntdButton( + '搜索', + id={ + 'type': 'allocate_role-search', + 'index': allocate_index + }, + type='primary', + icon=fac.AntdIcon( + icon='antd-search' + ) + ), + style={'paddingBottom': '10px'}, + ), + fac.AntdFormItem( + fac.AntdButton( + '重置', + id={ + 'type': 'allocate_role-reset', + 'index': allocate_index + }, + icon=fac.AntdIcon( + icon='antd-sync' + ) + ), + style={'paddingBottom': '10px'}, + ) + ], + layout='inline', + ) + ], + id={ + 'type': 'allocate_role-search-form-container', + 'index': allocate_index + }, + hidden=False + ), + ) + ] + ), + fac.AntdRow( + [ + fac.AntdCol( + fac.AntdSpace( + [ + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-plus' + ), + '添加角色', + ], + id='allocate_role-add', + style={ + 'color': '#1890ff', + 'background': '#e8f4ff', + 'border-color': '#a3d3ff' + } + ) if 'system:user:edit' in button_perms else [], + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-close-circle' + ), + '批量取消授权', + ], + id={ + 'type': 'allocate_role-operation-button', + 'index': 'delete' + }, + disabled=True, + style={ + 'color': '#ff9292', + 'background': '#ffeded', + 'border-color': '#ffdbdb' + } + ) if 'system:user:edit' in button_perms else [], + ], + style={ + 'paddingBottom': '10px' + } + ), + span=16 + ) if is_operation else [], + fac.AntdCol( + fac.AntdSpace( + [ + html.Div( + fac.AntdTooltip( + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-search' + ), + ], + id={ + 'type': 'allocate_role-hidden', + 'index': allocate_index + }, + shape='circle' + ), + id={ + 'type': 'allocate_role-hidden-tooltip', + 'index': allocate_index + }, + title='隐藏搜索' + ) + ), + html.Div( + fac.AntdTooltip( + fac.AntdButton( + [ + fac.AntdIcon( + icon='antd-sync' + ), + ], + id={ + 'type': 'allocate_role-refresh', + 'index': allocate_index + }, + shape='circle' + ), + title='刷新' + ) + ), + ], + style={ + 'float': 'right', + 'paddingBottom': '10px' + } + ), + span=8 if is_operation else 24, + style={ + 'paddingRight': '10px' + } + ) + ], + gutter=5 + ), + fac.AntdRow( + [ + fac.AntdCol( + fac.AntdSpin( + fac.AntdTable( + id={ + 'type': 'allocate_role-list-table', + 'index': allocate_index + }, + data=[], + columns=table_column, + rowSelectionType='checkbox', + rowSelectionWidth=50, + bordered=True, + maxWidth=1000, + pagination={ + 'pageSize': 10, + 'current': 1, + 'showSizeChanger': True, + 'pageSizeOptions': [10, 30, 50, 100], + 'showQuickJumper': True, + 'total': 0 + }, + mode='server-side', + style={ + 'width': '100%', + 'padding-right': '10px' + } + ), + text='数据加载中' + ), + ) + ] + ), + ], + span=24 + ) + ], + gutter=5 + ) diff --git a/dash-fastapi-frontend/views/system/user/profile/user_avatar.py b/dash-fastapi-frontend/views/system/user/profile/user_avatar.py index 438e5767e4c38b6a02151712ad6b62251f91f778..a70c29ef5ffcc62b80be3d67188168e613a1baa6 100644 --- a/dash-fastapi-frontend/views/system/user/profile/user_avatar.py +++ b/dash-fastapi-frontend/views/system/user/profile/user_avatar.py @@ -114,7 +114,7 @@ def render(): apiUrl=f'{ApiBaseUrlConfig.BaseUrl}/common/upload', downloadUrl=f'{ApiBaseUrlConfig.BaseUrl}/common/caches', headers={'Authorization': 'Bearer ' + session.get('Authorization')}, - fileMaxSize=1, + fileMaxSize=10, showUploadList=False, fileTypes=['jpeg', 'jpg', 'png'], buttonContent='选择'