基于Dash+FastAPI开发的一个通用中后台管理系统，本仓库为Gitee同步仓库，如需提交代码请移步至Gitee仓库，Gitee仓库地址如下：
from fastapi import FastAPI, Request
import uvicorn
from fastapi.exceptions import HTTPException
from fastapi.middleware.cors import CORSMiddleware
from module_admin.controller.login_controller import loginController
from module_admin.controller.captcha_controller import captchaController
from module_admin.controller.user_controller import userController
from module_admin.controller.menu_controller import menuController
from module_admin.controller.dept_controller import deptController
from module_admin.controller.role_controller import roleController
from module_admin.controller.post_controler import postController
from module_admin.controller.dict_controller import dictController
from module_admin.controller.config_controller import configController
from module_admin.controller.notice_controller import noticeController
from module_admin.controller.log_controller import logController
from module_admin.controller.online_controller import onlineController
from module_admin.controller.job_controller import jobController
from module_admin.controller.server_controller import serverController
from module_admin.controller.cache_controller import cacheController
from module_admin.controller.common_controller import commonController
from config.get_redis import RedisUtil
from config.get_db import init_create_table
from config.get_scheduler import SchedulerUtil
from utils.response_util import *
from utils.log_util import logger
from utils.common_util import worship

app = FastAPI(
    title='Dash-FastAPI',
    description='Dash-FastAPI接口文档',
    version='1.0.0',
)

# 前端页面url
origins = [
    "http://localhost:8088",
    "http://127.0.0.1:8088",
]

# 后台api允许跨域
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.on_event("startup")
async def startup_event():
    logger.info("Dash-FastAPI开始启动")
    worship()
    await init_create_table()
    app.state.redis = await RedisUtil.create_redis_pool()
    await RedisUtil.init_sys_dict(app.state.redis)
    await RedisUtil.init_sys_config(app.state.redis)
    await SchedulerUtil.init_system_scheduler()
    logger.info("Dash-FastAPI启动成功")


@app.on_event("shutdown")
async def shutdown_event():
    await RedisUtil.close_redis_pool(app)
    await SchedulerUtil.close_system_scheduler()


# 自定义token检验异常
@app.exception_handler(AuthException)
async def auth_exception_handler(request: Request, exc: AuthException):
    return response_401(data=exc.data, message=exc.message)


@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    return JSONResponse(
        content=jsonable_encoder({"message": exc.detail, "code": exc.status_code}),
        status_code=exc.status_code
    )


app.include_router(loginController, prefix="/login", tags=['登录模块'])
app.include_router(captchaController, prefix="/captcha", tags=['验证码模块'])
app.include_router(userController, prefix="/system", tags=['系统管理-用户管理'])
app.include_router(menuController, prefix="/system", tags=['系统管理-菜单管理'])
app.include_router(deptController, prefix="/system", tags=['系统管理-部门管理'])
app.include_router(roleController, prefix="/system", tags=['系统管理-角色管理'])
app.include_router(postController, prefix="/system", tags=['系统管理-岗位管理'])
app.include_router(dictController, prefix="/system", tags=['系统管理-字典管理'])
app.include_router(configController, prefix="/system", tags=['系统管理-参数管理'])
app.include_router(noticeController, prefix="/system", tags=['系统管理-通知公告管理'])
app.include_router(logController, prefix="/system", tags=['系统管理-日志管理'])
app.include_router(onlineController, prefix="/monitor", tags=['系统监控-在线用户'])
app.include_router(jobController, prefix="/monitor", tags=['系统监控-定时任务'])
app.include_router(serverController, prefix="/monitor", tags=['系统监控-服务监控'])
app.include_router(cacheController, prefix="/monitor", tags=['系统监控-缓存监控'])
app.include_router(commonController, prefix="/common", tags=['通用模块'])

if __name__ == '__main__':
    uvicorn.run(app='app:app', host="127.0.0.1", port=9099, reload=True)

dash-fastapi-backend/app.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from config.env import DataBaseConfig

SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.USERNAME}:{DataBaseConfig.PASSWORD}@" \
                          f"{DataBaseConfig.HOST}:{DataBaseConfig.PORT}/{DataBaseConfig.DB}"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL, echo=True
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

dash-fastapi-backend/config/database.py
insistence/Dash-FastAPI-Admin
import os


class JwtConfig:
    """
    Jwt配置
    """
    SECRET_KEY = "b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55"
    ALGORITHM = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES = 1440
    REDIS_TOKEN_EXPIRE_MINUTES = 30


class DataBaseConfig:
    """
    数据库配置
    """
    HOST = "127.0.0.1"
    PORT = 3306
    USERNAME = 'root'
    PASSWORD = 'mysqlroot'
    DB = 'dash-fastapi'


class RedisConfig:
    """
    Redis配置
    """
    HOST = "127.0.0.1"
    PORT = 6379
    USERNAME = ''
    PASSWORD = ''
    DB = 2


class CachePathConfig:
    """
    缓存目录配置
    """
    PATH = os.path.join(os.path.abspath(os.getcwd()), 'caches')
    PATHSTR = 'caches'

dash-fastapi-backend/config/env.py
insistence/Dash-FastAPI-Admin
from config.database import *
from utils.log_util import logger


def get_db_pro():
    """
    每一个请求处理完毕后会关闭当前连接，不同的请求使用不同的连接
    :return:
    """
    current_db = SessionLocal()
    try:
        yield current_db
    finally:
        current_db.close()


async def init_create_table():
    """
    应用启动时初始化数据库连接
    :return:
    """
    logger.info("初始化数据库连接...")
    Base.metadata.create_all(bind=engine)
    logger.info("数据库连接成功")


get_db = get_db_pro

dash-fastapi-backend/config/get_db.py
insistence/Dash-FastAPI-Admin
import aioredis
from module_admin.service.dict_service import DictDataService
from module_admin.service.config_service import ConfigService
from config.env import RedisConfig
from config.database import SessionLocal
from utils.log_util import logger


class RedisUtil:
    """
    Redis相关方法
    """

    @classmethod
    async def create_redis_pool(cls) -> aioredis.Redis:
        """
        应用启动时初始化redis连接
        :return: Redis连接对象
        """
        logger.info("开始连接redis...")
        redis = await aioredis.from_url(
            url=f"redis://{RedisConfig.HOST}",
            port=RedisConfig.PORT,
            username=RedisConfig.USERNAME,
            password=RedisConfig.PASSWORD,
            db=RedisConfig.DB,
            encoding="utf-8",
            decode_responses=True
        )
        logger.info("redis连接成功")
        return redis

    @classmethod
    async def close_redis_pool(cls, app):
        """
        应用关闭时关闭redis连接
        :param app: fastapi对象
        :return:
        """
        await app.state.redis.close()
        logger.info("关闭redis连接成功")

    @classmethod
    async def init_sys_dict(cls, redis):
        """
        应用启动时缓存字典表
        :param redis: redis对象
        :return:
        """
        session = SessionLocal()
        await DictDataService.init_cache_sys_dict_services(session, redis)

        session.close()

    @classmethod
    async def init_sys_config(cls, redis):
        """
        应用启动时缓存参数配置表
        :param redis: redis对象
        :return:
        """
        session = SessionLocal()
        await ConfigService.init_cache_sys_config_services(session, redis)

        session.close()

dash-fastapi-backend/config/get_redis.py
insistence/Dash-FastAPI-Admin
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.jobstores.redis import RedisJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
from apscheduler.triggers.cron import CronTrigger
from apscheduler.events import EVENT_ALL
import json
from datetime import datetime, timedelta
from config.database import engine, SQLALCHEMY_DATABASE_URL, SessionLocal
from config.env import RedisConfig
from module_admin.service.job_log_service import JobLogService, JobLogModel
from module_admin.dao.job_dao import Session, JobDao
from utils.log_util import logger
import module_task


# 重写Cron定时
class MyCronTrigger(CronTrigger):
    @classmethod
    def from_crontab(cls, expr, timezone=None):
        values = expr.split()
        if len(values) != 7:
            raise ValueError('Wrong number of fields; got {}, expected 7'.format(len(values)))

        return cls(second=values[0], minute=values[1], hour=values[2], day=values[3], month=values[4],
                   day_of_week=values[5], year=values[6], timezone=timezone)


job_stores = {
    'default': MemoryJobStore(),
    'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine),
    'redis': RedisJobStore(
        **dict(
            host=RedisConfig.HOST,
            port=RedisConfig.PORT,
            username=RedisConfig.USERNAME,
            password=RedisConfig.PASSWORD,
            db=RedisConfig.DB
        )
    )
}
executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}
job_defaults = {
    'coalesce': False,
    'max_instance': 1
}
scheduler = BackgroundScheduler()
scheduler.configure(jobstores=job_stores, executors=executors, job_defaults=job_defaults)


class SchedulerUtil:
    """
    定时任务相关方法
    """

    @classmethod
    async def init_system_scheduler(cls, result_db: Session = SessionLocal()):
        """
        应用启动时初始化定时任务
        :return:
        """
        logger.info("开始启动定时任务...")
        scheduler.start()
        job_list = JobDao.get_job_list_for_scheduler(result_db)
        for item in job_list:
            query_job = cls.get_scheduler_job(job_id=str(item.job_id))
            if query_job:
                cls.remove_scheduler_job(job_id=str(item.job_id))
            cls.add_scheduler_job(item)
        result_db.close()
        scheduler.add_listener(cls.scheduler_event_listener, EVENT_ALL)
        logger.info("系统初始定时任务加载成功")

    @classmethod
    async def close_system_scheduler(cls):
        """
        应用关闭时关闭定时任务
        :return:
        """
        scheduler.shutdown()
        logger.info("关闭定时任务成功")

    @classmethod
    def get_scheduler_job(cls, job_id):
        """
        根据任务id获取任务对象
        :param job_id: 任务id
        :return: 任务对象
        """
        query_job = scheduler.get_job(job_id=str(job_id))

        return query_job

    @classmethod
    def add_scheduler_job(cls, job_info):
        """
        根据输入的任务对象信息添加任务
        :param job_info: 任务对象信息
        :return:
        """
        scheduler.add_job(
            func=eval(job_info.invoke_target),
            trigger=MyCronTrigger.from_crontab(job_info.cron_expression),
            args=job_info.job_args.split(',') if job_info.job_args else None,
            kwargs=json.loads(job_info.job_kwargs) if job_info.job_kwargs else None,
            id=str(job_info.job_id),
            name=job_info.job_name,
            misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None,
            coalesce=True if job_info.misfire_policy == '2' else False,
            max_instances=3 if job_info.concurrent == '0' else 1,
            jobstore=job_info.job_group,
            executor=job_info.job_executor
        )

    @classmethod
    def execute_scheduler_job_once(cls, job_info):
        """
        根据输入的任务对象执行一次任务
        :param job_info: 任务对象信息
        :return:
        """
        scheduler.add_job(
            func=eval(job_info.invoke_target),
            trigger='date',
            run_date=datetime.now() + timedelta(seconds=1),
            args=job_info.job_args.split(',') if job_info.job_args else None,
            kwargs=json.loads(job_info.job_kwargs) if job_info.job_kwargs else None,
            id=str(job_info.job_id),
            name=job_info.job_name,
            misfire_grace_time=1000000000000 if job_info.misfire_policy == '3' else None,
            coalesce=True if job_info.misfire_policy == '2' else False,
            max_instances=3 if job_info.concurrent == '0' else 1,
            jobstore=job_info.job_group,
            executor=job_info.job_executor
        )

    @classmethod
    def remove_scheduler_job(cls, job_id):
        """
        根据任务id移除任务
        :param job_id: 任务id
        :return:
        """
        scheduler.remove_job(job_id=str(job_id))

    @classmethod
    def scheduler_event_listener(cls, event):
        # 获取事件类型和任务ID
        event_type = event.__class__.__name__
        # 获取任务执行异常信息
        status = '0'
        exception_info = ''
        if event_type == 'JobExecutionEvent' and event.exception:
            exception_info = str(event.exception)
            status = '1'
        job_id = event.job_id
        query_job = cls.get_scheduler_job(job_id=job_id)
        if query_job:
            query_job_info = query_job.__getstate__()
            # 获取任务名称
            job_name = query_job_info.get('name')
            # 获取任务组名
            job_group = query_job._jobstore_alias
            # 获取任务执行器
            job_executor = query_job_info.get('executor')
            # 获取调用目标字符串
            invoke_target = query_job_info.get('func')
            # 获取调用函数位置参数
            job_args = ','.join(query_job_info.get('args'))
            # 获取调用函数关键字参数
            job_kwargs = json.dumps(query_job_info.get('kwargs'))
            # 获取任务触发器
            job_trigger = str(query_job_info.get('trigger'))
            # 构造日志消息
            job_message = f"事件类型: {event_type}, 任务ID: {job_id}, 任务名称: {job_name}, 执行于{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
            job_log = dict(
                job_name=job_name,
                job_group=job_group,
                job_executor=job_executor,
                invoke_target=invoke_target,
                job_args=job_args,
                job_kwargs=job_kwargs,
                job_trigger=job_trigger,
                job_message=job_message,
                status=status,
                exception_info=exception_info
            )
            session = SessionLocal()
            JobLogService.add_job_log_services(session, JobLogModel(**job_log))
            session.close()

dash-fastapi-backend/config/get_scheduler.py
insistence/Dash-FastAPI-Admin
from functools import wraps
from fastapi import Request
from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse
import inspect
import os
import json
import time
from datetime import datetime
import requests
from user_agents import parse
from typing import Optional
from module_admin.service.login_service import get_current_user
from module_admin.service.log_service import OperationLogService, LoginLogService
from module_admin.entity.vo.log_vo import OperLogModel, LogininforModel


def log_decorator(title: str, business_type: int, log_type: Optional[str] = 'operation'):
    """
    日志装饰器
    :param log_type: 日志类型（login表示登录日志，为空表示为操作日志）
    :param title: 当前日志装饰器装饰的模块标题
    :param business_type: 业务类型（0其它 1新增 2修改 3删除 4授权 5导出 6导入 7强退 8生成代码 9清空数据）
    :return:
    """
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            start_time = time.time()
            # 获取被装饰函数的文件路径
            file_path = inspect.getfile(func)
            # 获取项目根路径
            project_root = os.getcwd()
            # 处理文件路径，去除项目根路径部分
            relative_path = os.path.relpath(file_path, start=project_root)[0:-2].replace('\\', '.')
            # 获取当前被装饰函数所在路径
            func_path = f'{relative_path}{func.__name__}()'
            # 获取上下文信息
            request: Request = kwargs.get('request')
            token = request.headers.get('Authorization')
            query_db = kwargs.get('query_db')
            request_method = request.method
            operator_type = 0
            user_agent = request.headers.get('User-Agent')
            if "Windows" in user_agent or "Macintosh" in user_agent or "Linux" in user_agent:
                operator_type = 1
            if "Mobile" in user_agent or "Android" in user_agent or "iPhone" in user_agent:
                operator_type = 2
            # 获取请求的url
            oper_url = request.url.path
            # 获取请求的ip及ip归属区域
            oper_ip = request.headers.get('remote_addr')
            oper_location = '内网IP'
            try:
                if oper_ip != '127.0.0.1' and oper_ip != 'localhost':
                    ip_result = requests.get(f'https://qifu-api.baidubce.com/ip/geo/v1/district?ip={oper_ip}')
                    if ip_result.status_code == 200:
                        prov = ip_result.json().get('data').get('prov')
                        city = ip_result.json().get('data').get('city')
                        if prov or city:
                            oper_location = f'{prov}-{city}'
                        else:
                            oper_location = '未知'
                    else:
                        oper_location = '未知'
            except Exception as e:
                oper_location = '未知'
                print(e)
            finally:
                # 根据不同的请求类型使用不同的方法获取请求参数
                content_type = request.headers.get("Content-Type")
                if content_type and ("multipart/form-data" in content_type or 'application/x-www-form-urlencoded' in content_type):
                    payload = await request.form()
                    oper_param = "\n".join([f"{key}: {value}" for key, value in payload.items()])
                else:
                    payload = await request.body()
                    oper_param = json.dumps(json.loads(str(payload, 'utf-8')), ensure_ascii=False)
                # 日志表请求参数字段长度最大为2000，因此在此处判断长度
                if len(oper_param) > 2000:
                    oper_param = '请求参数过长'

                # 获取操作时间
                oper_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                # 此处在登录之前向原始函数传递一些登录信息，用于监测在线用户的相关信息
                login_log = {}
                if log_type == 'login':
                    user_agent_info = parse(user_agent)
                    browser = f'{user_agent_info.browser.family} {user_agent_info.browser.version[0]}'
                    system_os = f'{user_agent_info.os.family} {user_agent_info.os.version[0]}'
                    login_log = dict(
                        ipaddr=oper_ip,
                        login_location=oper_location,
                        browser=browser,
                        os=system_os,
                        login_time=oper_time
                    )
                    kwargs['form_data'].login_info = login_log
                # 调用原始函数
                result = await func(*args, **kwargs)
                # 获取请求耗时
                cost_time = float(time.time() - start_time) * 100
                # 判断请求是否来自api文档
                request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
                request_from_redoc = request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False
                # 根据响应结果的类型使用不同的方法获取响应结果参数
                if isinstance(result, JSONResponse) or isinstance(result, ORJSONResponse) or isinstance(result, UJSONResponse):
                    result_dict = json.loads(str(result.body, 'utf-8'))
                else:
                    if request_from_swagger or request_from_redoc:
                        result_dict = {}
                    else:
                        if result.status_code == 200:
                            result_dict = {'code': result.status_code, 'message': '获取成功'}
                        else:
                            result_dict = {'code': result.status_code, 'message': '获取失败'}
                json_result = json.dumps(dict(code=result_dict.get('code'), message=result_dict.get('message')), ensure_ascii=False)
                # 根据响应结果获取响应状态及异常信息
                status = 1
                error_msg = ''
                if result_dict.get('code') == 200:
                    status = 0
                else:
                    error_msg = result_dict.get('message')
                # 根据日志类型向对应的日志表插入数据
                if log_type == 'login':
                    # 登录请求来自于api文档时不记录登录日志，其余情况则记录
                    if request_from_swagger or request_from_redoc:
                        pass
                    else:
                        user = kwargs.get('form_data')
                        user_name = user.username
                        login_log['user_name'] = user_name
                        login_log['status'] = str(status)
                        login_log['msg'] = result_dict.get('message')

                        LoginLogService.add_login_log_services(query_db, LogininforModel(**login_log))
                else:
                    current_user = await get_current_user(request, token, query_db)
                    oper_name = current_user.user.user_name
                    dept_name = current_user.dept.dept_name
                    operation_log = dict(
                        title=title,
                        business_type=business_type,
                        method=func_path,
                        request_method=request_method,
                        operator_type=operator_type,
                        oper_name=oper_name,
                        dept_name=dept_name,
                        oper_url=oper_url,
                        oper_ip=oper_ip,
                        oper_location=oper_location,
                        oper_param=oper_param,
                        json_result=json_result,
                        status=status,
                        error_msg=error_msg,
                        oper_time=oper_time,
                        cost_time=cost_time
                    )
                    OperationLogService.add_operation_log_services(query_db, OperLogModel(**operation_log))

                return result

        return wrapper

    return decorator

dash-fastapi-backend/module_admin/annotation/log_annotation.py
insistence/Dash-FastAPI-Admin
from fastapi import Depends
from module_admin.entity.vo.user_vo import CurrentUserInfoServiceResponse
from module_admin.service.login_service import get_current_user
from typing import Optional


class GetDataScope:
    """
    获取当前用户数据权限对应的查询sql语句
    """
    def __init__(self, query_alias: Optional[str] = '', db_alias: Optional[str] = 'db'):
        self.query_alias = query_alias
        self.db_alias = db_alias

    def __call__(self, current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
        user_id = current_user.user.user_id
        dept_id = current_user.user.dept_id
        role_datascope_list = [dict(role_id=item.role_id, data_scope=int(item.data_scope)) for item in current_user.role]
        max_data_scope_dict = min(role_datascope_list, key=lambda x: x['data_scope'])
        max_role_id = max_data_scope_dict['role_id']
        max_data_scope = max_data_scope_dict['data_scope']
        if self.query_alias == '' or max_data_scope == 1 or user_id == 1:
            param_sql = '1 == 1'
        elif max_data_scope == 2:
            param_sql = f'{self.query_alias}.dept_id.in_({self.db_alias}.query(SysRoleDept.dept_id).filter(SysRoleDept.role_id == {max_role_id}))'
        elif max_data_scope == 3:
            param_sql = f'{self.query_alias}.dept_id == {dept_id}'
        elif max_data_scope == 4:
            param_sql = f'{self.query_alias}.dept_id.in_({self.db_alias}.query(SysDept.dept_id).filter(or_(SysDept.dept_id == {dept_id}, func.find_in_set({dept_id}, SysDept.ancestors))))'
        elif max_data_scope == 5:
            param_sql = f'{self.query_alias}.user_id == {user_id}'
        else:
            param_sql = '1 == 0'

        return param_sql

dash-fastapi-backend/module_admin/aspect/data_scope.py
insistence/Dash-FastAPI-Admin
from fastapi import Depends
from module_admin.entity.vo.user_vo import CurrentUserInfoServiceResponse
from module_admin.service.login_service import get_current_user
from utils.response_util import AuthException


class CheckUserInterfaceAuth:
    """
    校验当前用户是否具有相应的接口权限
    """
    def __init__(self, perm_str: str = 'common'):
        self.perm_str = perm_str

    def __call__(self, current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
        user_auth_list = [item.perms for item in current_user.menu]
        user_auth_list.append('common')
        if self.perm_str in user_auth_list:
            return True
        raise AuthException(data="", message="该用户无此接口权限")

dash-fastapi-backend/module_admin/aspect/interface_auth.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter
from fastapi import Depends
from module_admin.service.login_service import get_current_user
from module_admin.service.cache_service import *
from utils.response_util import *
from utils.log_util import *
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth


cacheController = APIRouter(prefix='/cache', dependencies=[Depends(get_current_user)])


@cacheController.post("/statisticalInfo", response_model=CacheMonitorModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:cache:list'))])
async def get_monitor_cache_info(request: Request):
    try:
        # 获取全量数据
        cache_info_query_result = await CacheService.get_cache_monitor_statistical_info_services(request)
        logger.info('获取成功')
        return response_200(data=cache_info_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/cache_controller.py
insistence/Dash-FastAPI-Admin
import uuid
from fastapi import APIRouter, Request
from module_admin.service.captcha_service import *
from utils.response_util import *
from utils.log_util import *
from datetime import timedelta


captchaController = APIRouter()


@captchaController.post("/captchaImage")
async def get_captcha_image(request: Request):
    try:
        session_id = str(uuid.uuid4())
        captcha_result = CaptchaService.create_captcha_image_service()
        image = captcha_result[0]
        computed_result = captcha_result[1]
        await request.app.state.redis.set(f'captcha_codes:{session_id}', computed_result, ex=timedelta(minutes=2))
        logger.info(f'编号为{session_id}的会话获取图片验证码成功')
        return response_200(data={'image': image, 'session_id': session_id}, message='获取验证码成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/captcha_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
from fastapi import Depends, File, Form
from config.env import CachePathConfig
from module_admin.service.login_service import get_current_user
from module_admin.service.common_service import *
from module_admin.service.config_service import ConfigService
from utils.response_util import *
from utils.log_util import *
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth


commonController = APIRouter()


@commonController.post("/upload", dependencies=[Depends(get_current_user), Depends(CheckUserInterfaceAuth('common'))])
async def common_upload(request: Request, uploadId: str = Form(), file: UploadFile = File(...)):
    try:
        try:
            os.makedirs(os.path.join(CachePathConfig.PATH, uploadId))
        except FileExistsError:
            pass
        CommonService.upload_service(CachePathConfig.PATH, uploadId, file)
        logger.info('上传成功')
        return response_200(data={'filename': file.filename}, message="上传成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@commonController.post("/uploadForEditor", dependencies=[Depends(get_current_user), Depends(CheckUserInterfaceAuth('common'))])
async def editor_upload(request: Request, uploadId: str = Form(), file: UploadFile = File(...)):
    try:
        try:
            os.makedirs(os.path.join(CachePathConfig.PATH, uploadId))
        except FileExistsError:
            pass
        CommonService.upload_service(CachePathConfig.PATH, uploadId, file)
        logger.info('上传成功')
        return JSONResponse(
            status_code=status.HTTP_200_OK,
            content=jsonable_encoder(
                {
                    'errno': 0,
                    'data': {
                        'url': f'{request.base_url}common/{CachePathConfig.PATHSTR}?taskId={uploadId}&filename={file.filename}'
                    },
                }
            )
        )
    except Exception as e:
        logger.exception(e)
        return JSONResponse(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            content=jsonable_encoder(
                {
                    'errno': 1,
                    'message': str(e),
                }
            )
        )


@commonController.get(f"/{CachePathConfig.PATHSTR}")
def common_download(request: Request, taskId: str, filename: str):
    try:
        def generate_file():
            with open(os.path.join(CachePathConfig.PATH, taskId, filename), 'rb') as response_file:
                yield from response_file
        logger.info('获取成功')
        return streaming_response_200(data=generate_file())
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@commonController.get("/config/query/{config_key}")
async def query_system_config(request: Request, config_key: str):
    try:
        # 获取全量数据
        config_query_result = await ConfigService.query_config_list_from_cache_services(request.app.state.redis, config_key)
        logger.info('获取成功')
        return response_200(data=config_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/common_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter
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.config_service import *
from module_admin.entity.vo.config_vo import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import get_page_obj
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


configController = APIRouter(dependencies=[Depends(get_current_user)])


@configController.post("/config/get", response_model=ConfigPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:config:list'))])
async def get_system_config_list(request: Request, config_page_query: ConfigPageObject, query_db: Session = Depends(get_db)):
    try:
        config_query = ConfigQueryModel(**config_page_query.dict())
        # 获取全量数据
        config_query_result = ConfigService.get_config_list_services(query_db, config_query)
        # 分页操作
        config_page_query_result = get_page_obj(config_query_result, config_page_query.page_num, config_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=config_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@configController.post("/config/add", response_model=CrudConfigResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:config:add'))])
@log_decorator(title='参数管理', business_type=1)
async def add_system_config(request: Request, add_config: ConfigModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_config.create_by = current_user.user.user_name
        add_config.update_by = current_user.user.user_name
        add_config_result = await ConfigService.add_config_services(request, query_db, add_config)
        if add_config_result.is_success:
            logger.info(add_config_result.message)
            return response_200(data=add_config_result, message=add_config_result.message)
        else:
            logger.warning(add_config_result.message)
            return response_400(data="", message=add_config_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@configController.patch("/config/edit", response_model=CrudConfigResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:config:edit'))])
@log_decorator(title='参数管理', business_type=2)
async def edit_system_config(request: Request, edit_config: ConfigModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_config.update_by = current_user.user.user_name
        edit_config.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_config_result = await ConfigService.edit_config_services(request, query_db, edit_config)
        if edit_config_result.is_success:
            logger.info(edit_config_result.message)
            return response_200(data=edit_config_result, message=edit_config_result.message)
        else:
            logger.warning(edit_config_result.message)
            return response_400(data="", message=edit_config_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@configController.post("/config/delete", response_model=CrudConfigResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:config:remove'))])
@log_decorator(title='参数管理', business_type=3)
async def delete_system_config(request: Request, delete_config: DeleteConfigModel, query_db: Session = Depends(get_db)):
    try:
        delete_config_result = await ConfigService.delete_config_services(request, query_db, delete_config)
        if delete_config_result.is_success:
            logger.info(delete_config_result.message)
            return response_200(data=delete_config_result, message=delete_config_result.message)
        else:
            logger.warning(delete_config_result.message)
            return response_400(data="", message=delete_config_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@configController.get("/config/{config_id}", response_model=ConfigModel, dependencies=[Depends(CheckUserInterfaceAuth('system:config:query'))])
async def query_detail_system_config(request: Request, config_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_config_result = ConfigService.detail_config_services(query_db, config_id)
        logger.info(f'获取config_id为{config_id}的信息成功')
        return response_200(data=detail_config_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@configController.post("/config/export", dependencies=[Depends(CheckUserInterfaceAuth('system:config:export'))])
@log_decorator(title='参数管理', business_type=5)
async def export_system_config_list(request: Request, config_query: ConfigQueryModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        config_query_result = ConfigService.get_config_list_services(query_db, config_query)
        config_export_result = ConfigService.export_config_list_services(config_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(config_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@configController.post("/config/refresh", response_model=CrudConfigResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:config:edit'))])
@log_decorator(title='参数管理', business_type=2)
async def refresh_system_config(request: Request, query_db: Session = Depends(get_db)):
    try:
        refresh_config_result = await ConfigService.refresh_sys_config_services(request, query_db)
        if refresh_config_result.is_success:
            logger.info(refresh_config_result.message)
            return response_200(data=refresh_config_result, message=refresh_config_result.message)
        else:
            logger.warning(refresh_config_result.message)
            return response_400(data="", message=refresh_config_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/config_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
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.dept_service import *
from module_admin.entity.vo.dept_vo import *
from module_admin.dao.dept_dao import *
from utils.response_util import *
from utils.log_util import *
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


deptController = APIRouter(dependencies=[Depends(get_current_user)])


@deptController.post("/dept/tree", response_model=DeptTree, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_dept_tree(request: Request, dept_query: DeptModel, query_db: Session = Depends(get_db)):
    try:
        dept_query_result = DeptService.get_dept_tree_services(query_db, dept_query)
        logger.info('获取成功')
        return response_200(data=dept_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@deptController.post("/dept/forEditOption", response_model=DeptTree, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_dept_tree_for_edit_option(request: Request, dept_query: DeptModel, query_db: Session = Depends(get_db)):
    try:
        dept_query_result = DeptService.get_dept_tree_for_edit_option_services(query_db, dept_query)
        logger.info('获取成功')
        return response_200(data=dept_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@deptController.post("/dept/get", response_model=DeptResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dept:list'))])
async def get_system_dept_list(request: Request, dept_query: DeptModel, query_db: Session = Depends(get_db)):
    try:
        dept_query_result = DeptService.get_dept_list_services(query_db, dept_query)
        logger.info('获取成功')
        return response_200(data=dept_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@deptController.post("/dept/add", response_model=CrudDeptResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dept:add'))])
@log_decorator(title='部门管理', business_type=1)
async def add_system_dept(request: Request, add_dept: DeptModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_dept.create_by = current_user.user.user_name
        add_dept.update_by = current_user.user.user_name
        add_dept_result = DeptService.add_dept_services(query_db, add_dept)
        if add_dept_result.is_success:
            logger.info(add_dept_result.message)
            return response_200(data=add_dept_result, message=add_dept_result.message)
        else:
            logger.warning(add_dept_result.message)
            return response_400(data="", message=add_dept_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@deptController.patch("/dept/edit", response_model=CrudDeptResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dept:edit'))])
@log_decorator(title='部门管理', business_type=2)
async def edit_system_dept(request: Request, edit_dept: DeptModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_dept.update_by = current_user.user.user_name
        edit_dept.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_dept_result = DeptService.edit_dept_services(query_db, edit_dept)
        if edit_dept_result.is_success:
            logger.info(edit_dept_result.message)
            return response_200(data=edit_dept_result, message=edit_dept_result.message)
        else:
            logger.warning(edit_dept_result.message)
            return response_400(data="", message=edit_dept_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@deptController.post("/dept/delete", response_model=CrudDeptResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dept:remove'))])
@log_decorator(title='部门管理', business_type=3)
async def delete_system_dept(request: Request, delete_dept: DeleteDeptModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        delete_dept.update_by = current_user.user.user_name
        delete_dept.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        delete_dept_result = DeptService.delete_dept_services(query_db, delete_dept)
        if delete_dept_result.is_success:
            logger.info(delete_dept_result.message)
            return response_200(data=delete_dept_result, message=delete_dept_result.message)
        else:
            logger.warning(delete_dept_result.message)
            return response_400(data="", message=delete_dept_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@deptController.get("/dept/{dept_id}", response_model=DeptModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dept:query'))])
async def query_detail_system_dept(request: Request, dept_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_dept_result = DeptService.detail_dept_services(query_db, dept_id)
        logger.info(f'获取dept_id为{dept_id}的信息成功')
        return response_200(data=detail_dept_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/dept_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter
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.dict_service import *
from module_admin.entity.vo.dict_vo import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import get_page_obj
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


dictController = APIRouter(dependencies=[Depends(get_current_user)])


@dictController.post("/dictType/get", response_model=DictTypePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
async def get_system_dict_type_list(request: Request, dict_type_page_query: DictTypePageObject, query_db: Session = Depends(get_db)):
    try:
        dict_type_query = DictTypeQueryModel(**dict_type_page_query.dict())
        # 获取全量数据
        dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query)
        # 分页操作
        dict_type_page_query_result = get_page_obj(dict_type_query_result, dict_type_page_query.page_num, dict_type_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=dict_type_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictType/all", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
async def get_system_all_dict_type(request: Request, dict_type_query: DictTypeQueryModel, query_db: Session = Depends(get_db)):
    try:
        dict_type_query_result = DictTypeService.get_all_dict_type_services(query_db)
        logger.info('获取成功')
        return response_200(data=dict_type_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictType/add", response_model=CrudDictResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))])
@log_decorator(title='字典管理', business_type=1)
async def add_system_dict_type(request: Request, add_dict_type: DictTypeModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_dict_type.create_by = current_user.user.user_name
        add_dict_type.update_by = current_user.user.user_name
        add_dict_type_result = await DictTypeService.add_dict_type_services(request, query_db, add_dict_type)
        if add_dict_type_result.is_success:
            logger.info(add_dict_type_result.message)
            return response_200(data=add_dict_type_result, message=add_dict_type_result.message)
        else:
            logger.warning(add_dict_type_result.message)
            return response_400(data="", message=add_dict_type_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.patch("/dictType/edit", response_model=CrudDictResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))])
@log_decorator(title='字典管理', business_type=2)
async def edit_system_dict_type(request: Request, edit_dict_type: DictTypeModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_dict_type.update_by = current_user.user.user_name
        edit_dict_type.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_dict_type_result = await DictTypeService.edit_dict_type_services(request, query_db, edit_dict_type)
        if edit_dict_type_result.is_success:
            logger.info(edit_dict_type_result.message)
            return response_200(data=edit_dict_type_result, message=edit_dict_type_result.message)
        else:
            logger.warning(edit_dict_type_result.message)
            return response_400(data="", message=edit_dict_type_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictType/delete", response_model=CrudDictResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))])
@log_decorator(title='字典管理', business_type=3)
async def delete_system_dict_type(request: Request, delete_dict_type: DeleteDictTypeModel, query_db: Session = Depends(get_db)):
    try:
        delete_dict_type_result = await DictTypeService.delete_dict_type_services(request, query_db, delete_dict_type)
        if delete_dict_type_result.is_success:
            logger.info(delete_dict_type_result.message)
            return response_200(data=delete_dict_type_result, message=delete_dict_type_result.message)
        else:
            logger.warning(delete_dict_type_result.message)
            return response_400(data="", message=delete_dict_type_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.get("/dictType/{dict_id}", response_model=DictTypeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))])
async def query_detail_system_dict_type(request: Request, dict_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_dict_type_result = DictTypeService.detail_dict_type_services(query_db, dict_id)
        logger.info(f'获取dict_id为{dict_id}的信息成功')
        return response_200(data=detail_dict_type_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictType/export", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))])
@log_decorator(title='字典管理', business_type=5)
async def export_system_dict_type_list(request: Request, dict_type_query: DictTypeQueryModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query)
        dict_type_export_result = DictTypeService.export_dict_type_list_services(dict_type_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(dict_type_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictType/refresh", response_model=CrudDictResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))])
@log_decorator(title='字典管理', business_type=2)
async def refresh_system_dict(request: Request, query_db: Session = Depends(get_db)):
    try:
        refresh_dict_result = await DictTypeService.refresh_sys_dict_services(request, query_db)
        if refresh_dict_result.is_success:
            logger.info(refresh_dict_result.message)
            return response_200(data=refresh_dict_result, message=refresh_dict_result.message)
        else:
            logger.warning(refresh_dict_result.message)
            return response_400(data="", message=refresh_dict_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictData/get", response_model=DictDataPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
async def get_system_dict_data_list(request: Request, dict_data_page_query: DictDataPageObject, query_db: Session = Depends(get_db)):
    try:
        dict_data_query = DictDataModel(**dict_data_page_query.dict())
        # 获取全量数据
        dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query)
        # 分页操作
        dict_data_page_query_result = get_page_obj(dict_data_query_result, dict_data_page_query.page_num, dict_data_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=dict_data_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.get("/dictData/query/{dict_type}", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
async def query_system_dict_data_list(request: Request, dict_type: str, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        dict_data_query_result = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type)
        logger.info('获取成功')
        return response_200(data=dict_data_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictData/add", response_model=CrudDictResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:add'))])
@log_decorator(title='字典管理', business_type=1)
async def add_system_dict_data(request: Request, add_dict_data: DictDataModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_dict_data.create_by = current_user.user.user_name
        add_dict_data.update_by = current_user.user.user_name
        add_dict_data_result = await DictDataService.add_dict_data_services(request, query_db, add_dict_data)
        if add_dict_data_result.is_success:
            logger.info(add_dict_data_result.message)
            return response_200(data=add_dict_data_result, message=add_dict_data_result.message)
        else:
            logger.warning(add_dict_data_result.message)
            return response_400(data="", message=add_dict_data_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.patch("/dictData/edit", response_model=CrudDictResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:edit'))])
@log_decorator(title='字典管理', business_type=2)
async def edit_system_dict_data(request: Request, edit_dict_data: DictDataModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_dict_data.update_by = current_user.user.user_name
        edit_dict_data.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_dict_data_result = await DictDataService.edit_dict_data_services(request, query_db, edit_dict_data)
        if edit_dict_data_result.is_success:
            logger.info(edit_dict_data_result.message)
            return response_200(data=edit_dict_data_result, message=edit_dict_data_result.message)
        else:
            logger.warning(edit_dict_data_result.message)
            return response_400(data="", message=edit_dict_data_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictData/delete", response_model=CrudDictResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:remove'))])
@log_decorator(title='字典管理', business_type=3)
async def delete_system_dict_data(request: Request, delete_dict_data: DeleteDictDataModel, query_db: Session = Depends(get_db)):
    try:
        delete_dict_data_result = await DictDataService.delete_dict_data_services(request, query_db, delete_dict_data)
        if delete_dict_data_result.is_success:
            logger.info(delete_dict_data_result.message)
            return response_200(data=delete_dict_data_result, message=delete_dict_data_result.message)
        else:
            logger.warning(delete_dict_data_result.message)
            return response_400(data="", message=delete_dict_data_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.get("/dictData/{dict_code}", response_model=DictDataModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))])
async def query_detail_system_dict_data(request: Request, dict_code: int, query_db: Session = Depends(get_db)):
    try:
        detail_dict_data_result = DictDataService.detail_dict_data_services(query_db, dict_code)
        logger.info(f'获取dict_code为{dict_code}的信息成功')
        return response_200(data=detail_dict_data_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@dictController.post("/dictData/export", dependencies=[Depends(CheckUserInterfaceAuth('system:dict:export'))])
@log_decorator(title='字典管理', business_type=5)
async def export_system_dict_data_list(request: Request, dict_data_query: DictDataModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query)
        dict_data_export_result = DictDataService.export_dict_data_list_services(dict_data_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(dict_data_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/dict_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter
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.job_service import *
from module_admin.service.job_log_service import *
from module_admin.entity.vo.job_vo import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import get_page_obj
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


jobController = APIRouter(dependencies=[Depends(get_current_user)])


@jobController.post("/job/get", response_model=JobPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))])
async def get_system_job_list(request: Request, job_page_query: JobPageObject, query_db: Session = Depends(get_db)):
    try:
        job_query = JobModel(**job_page_query.dict())
        # 获取全量数据
        job_query_result = JobService.get_job_list_services(query_db, job_query)
        # 分页操作
        notice_page_query_result = get_page_obj(job_query_result, job_page_query.page_num, job_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=notice_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/job/add", response_model=CrudJobResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:add'))])
@log_decorator(title='定时任务管理', business_type=1)
async def add_system_job(request: Request, add_job: JobModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_job.create_by = current_user.user.user_name
        add_job.update_by = current_user.user.user_name
        add_job_result = JobService.add_job_services(query_db, add_job)
        if add_job_result.is_success:
            logger.info(add_job_result.message)
            return response_200(data=add_job_result, message=add_job_result.message)
        else:
            logger.warning(add_job_result.message)
            return response_400(data="", message=add_job_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.patch("/job/edit", response_model=CrudJobResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:edit'))])
@log_decorator(title='定时任务管理', business_type=2)
async def edit_system_job(request: Request, edit_job: EditJobModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_job.update_by = current_user.user.user_name
        edit_job.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_job_result = JobService.edit_job_services(query_db, edit_job)
        if edit_job_result.is_success:
            logger.info(edit_job_result.message)
            return response_200(data=edit_job_result, message=edit_job_result.message)
        else:
            logger.warning(edit_job_result.message)
            return response_400(data="", message=edit_job_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/job/changeStatus", response_model=CrudJobResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:changeStatus'))])
@log_decorator(title='定时任务管理', business_type=2)
async def execute_system_job(request: Request, execute_job: JobModel, query_db: Session = Depends(get_db)):
    try:
        execute_job_result = JobService.execute_job_once_services(query_db, execute_job)
        if execute_job_result.is_success:
            logger.info(execute_job_result.message)
            return response_200(data=execute_job_result, message=execute_job_result.message)
        else:
            logger.warning(execute_job_result.message)
            return response_400(data="", message=execute_job_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/job/delete", response_model=CrudJobResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))])
@log_decorator(title='定时任务管理', business_type=3)
async def delete_system_job(request: Request, delete_job: DeleteJobModel, query_db: Session = Depends(get_db)):
    try:
        delete_job_result = JobService.delete_job_services(query_db, delete_job)
        if delete_job_result.is_success:
            logger.info(delete_job_result.message)
            return response_200(data=delete_job_result, message=delete_job_result.message)
        else:
            logger.warning(delete_job_result.message)
            return response_400(data="", message=delete_job_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.get("/job/{job_id}", response_model=JobModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:query'))])
async def query_detail_system_job(request: Request, job_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_job_result = JobService.detail_job_services(query_db, job_id)
        logger.info(f'获取job_id为{job_id}的信息成功')
        return response_200(data=detail_job_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/job/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))])
@log_decorator(title='定时任务管理', business_type=5)
async def export_system_job_list(request: Request, job_query: JobModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        job_query_result = JobService.get_job_list_services(query_db, job_query)
        job_export_result = await JobService.export_job_list_services(request, job_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(job_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/jobLog/get", response_model=JobLogPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))])
async def get_system_job_log_list(request: Request, job_log_page_query: JobLogPageObject, query_db: Session = Depends(get_db)):
    try:
        job_log_query = JobLogQueryModel(**job_log_page_query.dict())
        # 获取全量数据
        job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query)
        # 分页操作
        notice_page_query_result = get_page_obj(job_log_query_result, job_log_page_query.page_num, job_log_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=notice_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/jobLog/delete", response_model=CrudJobResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))])
@log_decorator(title='定时任务日志管理', business_type=3)
async def delete_system_job_log(request: Request, delete_job_log: DeleteJobLogModel, query_db: Session = Depends(get_db)):
    try:
        delete_job_log_result = JobLogService.delete_job_log_services(query_db, delete_job_log)
        if delete_job_log_result.is_success:
            logger.info(delete_job_log_result.message)
            return response_200(data=delete_job_log_result, message=delete_job_log_result.message)
        else:
            logger.warning(delete_job_log_result.message)
            return response_400(data="", message=delete_job_log_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/jobLog/clear", response_model=CrudJobResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:remove'))])
@log_decorator(title='定时任务日志管理', business_type=9)
async def clear_system_job_log(request: Request, clear_job_log: ClearJobLogModel, query_db: Session = Depends(get_db)):
    try:
        clear_job_log_result = JobLogService.clear_job_log_services(query_db, clear_job_log)
        if clear_job_log_result.is_success:
            logger.info(clear_job_log_result.message)
            return response_200(data=clear_job_log_result, message=clear_job_log_result.message)
        else:
            logger.warning(clear_job_log_result.message)
            return response_400(data="", message=clear_job_log_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.get("/jobLog/{job_log_id}", response_model=JobLogModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:query'))])
async def query_detail_system_job_log(request: Request, job_log_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_job_log_result = JobLogService.detail_job_log_services(query_db, job_log_id)
        logger.info(f'获取job_log_id为{job_log_id}的信息成功')
        return response_200(data=detail_job_log_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@jobController.post("/jobLog/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:export'))])
@log_decorator(title='定时任务日志管理', business_type=5)
async def export_system_job_log_list(request: Request, job_log_query: JobLogQueryModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query)
        job_log_export_result = JobLogService.export_job_log_list_services(query_db, job_log_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(job_log_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/job_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter
from fastapi import Depends
from config.get_db import get_db
from module_admin.service.login_service import get_current_user
from module_admin.service.log_service import *
from module_admin.entity.vo.log_vo import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import get_page_obj
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


logController = APIRouter(prefix='/log', dependencies=[Depends(get_current_user)])


@logController.post("/operation/get", response_model=OperLogPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:list'))])
async def get_system_operation_log_list(request: Request, operation_log_page_query: OperLogPageObject, query_db: Session = Depends(get_db)):
    try:
        operation_log_query = OperLogQueryModel(**operation_log_page_query.dict())
        # 获取全量数据
        operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query)
        # 分页操作
        operation_log_page_query_result = get_page_obj(operation_log_query_result, operation_log_page_query.page_num, operation_log_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=operation_log_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/operation/delete", response_model=CrudLogResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))])
@log_decorator(title='操作日志管理', business_type=3)
async def delete_system_operation_log(request: Request, delete_operation_log: DeleteOperLogModel, query_db: Session = Depends(get_db)):
    try:
        delete_operation_log_result = OperationLogService.delete_operation_log_services(query_db, delete_operation_log)
        if delete_operation_log_result.is_success:
            logger.info(delete_operation_log_result.message)
            return response_200(data=delete_operation_log_result, message=delete_operation_log_result.message)
        else:
            logger.warning(delete_operation_log_result.message)
            return response_400(data="", message=delete_operation_log_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/operation/clear", response_model=CrudLogResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:remove'))])
@log_decorator(title='操作日志管理', business_type=9)
async def clear_system_operation_log(request: Request, clear_operation_log: ClearOperLogModel, query_db: Session = Depends(get_db)):
    try:
        clear_operation_log_result = OperationLogService.clear_operation_log_services(query_db, clear_operation_log)
        if clear_operation_log_result.is_success:
            logger.info(clear_operation_log_result.message)
            return response_200(data=clear_operation_log_result, message=clear_operation_log_result.message)
        else:
            logger.warning(clear_operation_log_result.message)
            return response_400(data="", message=clear_operation_log_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.get("/operation/{oper_id}", response_model=OperLogModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:query'))])
async def query_detail_system_operation_log(request: Request, oper_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_operation_log_result = OperationLogService.detail_operation_log_services(query_db, oper_id)
        logger.info(f'获取oper_id为{oper_id}的信息成功')
        return response_200(data=detail_operation_log_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/operation/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:export'))])
@log_decorator(title='操作日志管理', business_type=5)
async def export_system_operation_log_list(request: Request, operation_log_query: OperLogQueryModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query)
        operation_log_export_result = await OperationLogService.export_operation_log_list_services(request, operation_log_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(operation_log_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/login/get", response_model=LoginLogPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:list'))])
async def get_system_login_log_list(request: Request, login_log_page_query: LoginLogPageObject, query_db: Session = Depends(get_db)):
    try:
        login_log_query = LoginLogQueryModel(**login_log_page_query.dict())
        # 获取全量数据
        login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query)
        # 分页操作
        login_log_page_query_result = get_page_obj(login_log_query_result, login_log_page_query.page_num, login_log_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=login_log_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/login/delete", response_model=CrudLogResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))])
@log_decorator(title='登录日志管理', business_type=3)
async def delete_system_login_log(request: Request, delete_login_log: DeleteLoginLogModel, query_db: Session = Depends(get_db)):
    try:
        delete_login_log_result = LoginLogService.delete_login_log_services(query_db, delete_login_log)
        if delete_login_log_result.is_success:
            logger.info(delete_login_log_result.message)
            return response_200(data=delete_login_log_result, message=delete_login_log_result.message)
        else:
            logger.warning(delete_login_log_result.message)
            return response_400(data="", message=delete_login_log_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/login/clear", response_model=CrudLogResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))])
@log_decorator(title='登录日志管理', business_type=9)
async def clear_system_login_log(request: Request, clear_login_log: ClearLoginLogModel, query_db: Session = Depends(get_db)):
    try:
        clear_login_log_result = LoginLogService.clear_login_log_services(query_db, clear_login_log)
        if clear_login_log_result.is_success:
            logger.info(clear_login_log_result.message)
            return response_200(data=clear_login_log_result, message=clear_login_log_result.message)
        else:
            logger.warning(clear_login_log_result.message)
            return response_400(data="", message=clear_login_log_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/login/unlock", response_model=CrudLogResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:unlock'))])
@log_decorator(title='登录日志管理', business_type=0)
async def clear_system_login_log(request: Request, unlock_user: UnlockUser, query_db: Session = Depends(get_db)):
    try:
        unlock_user_result = await LoginLogService.unlock_user_services(request, unlock_user)
        if unlock_user_result.is_success:
            logger.info(unlock_user_result.message)
            return response_200(data=unlock_user_result, message=unlock_user_result.message)
        else:
            logger.warning(unlock_user_result.message)
            return response_400(data="", message=unlock_user_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@logController.post("/login/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:export'))])
@log_decorator(title='登录日志管理', business_type=5)
async def export_system_login_log_list(request: Request, login_log_query: LoginLogQueryModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query)
        login_log_export_result = LoginLogService.export_login_log_list_services(login_log_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(login_log_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/log_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter
from module_admin.service.login_service import *
from module_admin.entity.vo.login_vo import *
from module_admin.dao.login_dao import *
from config.env import JwtConfig
from utils.response_util import *
from utils.log_util import *
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator
from datetime import timedelta


loginController = APIRouter()


@loginController.post("/loginByAccount", response_model=Token)
@log_decorator(title='用户登录', business_type=0, log_type='login')
async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = Depends(), query_db: Session = Depends(get_db)):
    captcha_enabled = True if await request.app.state.redis.get(f'sys_config:sys.account.captchaEnabled') == 'true' else False
    user = UserLogin(
        **dict(
            user_name=form_data.username,
            password=form_data.password,
            captcha=form_data.captcha,
            session_id=form_data.session_id,
            login_info=form_data.login_info,
            captcha_enabled=captcha_enabled
        )
    )
    try:
        result = await authenticate_user(request, query_db, user)
    except LoginException as e:
        return response_400(data="", message=e.message)
    try:
        access_token_expires = timedelta(minutes=JwtConfig.ACCESS_TOKEN_EXPIRE_MINUTES)
        session_id = str(uuid.uuid4())
        access_token = create_access_token(
            data={
                "user_id": str(result[0].user_id),
                "user_name": result[0].user_name,
                "dept_name": result[1].dept_name,
                "session_id": session_id,
                "login_info": user.login_info
            },
            expires_delta=access_token_expires
        )
        await request.app.state.redis.set(f'access_token:{session_id}', access_token,
                                          ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
        # 此方法可实现同一账号同一时间只能登录一次
        # await request.app.state.redis.set(f'access_token:{result[0].user_id}', access_token,
        #                                   ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
        logger.info('登录成功')
        # 判断请求是否来自于api文档，如果是返回指定格式的结果，用于修复api文档认证成功后token显示undefined的bug
        request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
        request_from_redoc = request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False
        if request_from_swagger or request_from_redoc:
            return {'access_token': access_token, 'token_type': 'Bearer'}
        return response_200(
            data={'access_token': access_token, 'token_type': 'Bearer'},
            message='登录成功'
        )
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@loginController.post("/getSmsCode", response_model=SmsCode)
async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)):
    try:
        sms_result = await get_sms_code_services(request, query_db, user)
        if sms_result.is_success:
            logger.info('获取成功')
            return response_200(data=sms_result, message='获取成功')
        else:
            logger.warning(sms_result.message)
            return response_400(data='', message=sms_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@loginController.post("/forgetPwd", response_model=CrudUserResponse)
async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: Session = Depends(get_db)):
    try:
        forget_user_result = await forget_user_services(request, query_db, forget_user)
        if forget_user_result.is_success:
            logger.info(forget_user_result.message)
            return response_200(data=forget_user_result, message=forget_user_result.message)
        else:
            logger.warning(forget_user_result.message)
            return response_400(data="", message=forget_user_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@loginController.post("/getLoginUserInfo", response_model=CurrentUserInfoServiceResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_login_user_info(request: Request, current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        logger.info('获取成功')
        return response_200(data=current_user, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@loginController.post("/logout", dependencies=[Depends(get_current_user), Depends(CheckUserInterfaceAuth('common'))])
async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme), query_db: Session = Depends(get_db)):
    try:
        payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
        session_id: str = payload.get("session_id")
        await logout_services(request, session_id)
        logger.info('退出成功')
        return response_200(data="", message="退出成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/login_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
from fastapi import Depends
from config.get_db import get_db
from module_admin.service.login_service import get_current_user
from module_admin.service.menu_service import *
from module_admin.entity.vo.menu_vo import *
from module_admin.dao.menu_dao import *
from utils.response_util import *
from utils.log_util import *
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


menuController = APIRouter(dependencies=[Depends(get_current_user)])


@menuController.post("/menu/tree", response_model=MenuTree, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_menu_tree(request: Request, menu_query: MenuTreeModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        menu_query_result = MenuService.get_menu_tree_services(query_db, menu_query, current_user)
        logger.info('获取成功')
        return response_200(data=menu_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@menuController.post("/menu/forEditOption", response_model=MenuTree, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_menu_tree_for_edit_option(request: Request, menu_query: MenuModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        menu_query_result = MenuService.get_menu_tree_for_edit_option_services(query_db, menu_query, current_user)
        logger.info('获取成功')
        return response_200(data=menu_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@menuController.post("/menu/get", response_model=MenuResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:menu:list'))])
async def get_system_menu_list(request: Request, menu_query: MenuModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        menu_query_result = MenuService.get_menu_list_services(query_db, menu_query, current_user)
        logger.info('获取成功')
        return response_200(data=menu_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@menuController.post("/menu/add", response_model=CrudMenuResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:menu:add'))])
@log_decorator(title='菜单管理', business_type=1)
async def add_system_menu(request: Request, add_menu: MenuModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_menu.create_by = current_user.user.user_name
        add_menu.update_by = current_user.user.user_name
        add_menu_result = MenuService.add_menu_services(query_db, add_menu)
        if add_menu_result.is_success:
            logger.info(add_menu_result.message)
            return response_200(data=add_menu_result, message=add_menu_result.message)
        else:
            logger.warning(add_menu_result.message)
            return response_400(data="", message=add_menu_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@menuController.patch("/menu/edit", response_model=CrudMenuResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:menu:edit'))])
@log_decorator(title='菜单管理', business_type=2)
async def edit_system_menu(request: Request, edit_menu: MenuModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_menu.update_by = current_user.user.user_name
        edit_menu.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_menu_result = MenuService.edit_menu_services(query_db, edit_menu)
        if edit_menu_result.is_success:
            logger.info(edit_menu_result.message)
            return response_200(data=edit_menu_result, message=edit_menu_result.message)
        else:
            logger.warning(edit_menu_result.message)
            return response_400(data="", message=edit_menu_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@menuController.post("/menu/delete", response_model=CrudMenuResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:menu:remove'))])
@log_decorator(title='菜单管理', business_type=3)
async def delete_system_menu(request: Request, delete_menu: DeleteMenuModel, query_db: Session = Depends(get_db)):
    try:
        delete_menu_result = MenuService.delete_menu_services(query_db, delete_menu)
        if delete_menu_result.is_success:
            logger.info(delete_menu_result.message)
            return response_200(data=delete_menu_result, message=delete_menu_result.message)
        else:
            logger.warning(delete_menu_result.message)
            return response_400(data="", message=delete_menu_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@menuController.get("/menu/{menu_id}", response_model=MenuModel, dependencies=[Depends(CheckUserInterfaceAuth('system:menu:query'))])
async def query_detail_system_menu(request: Request, menu_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_menu_result = MenuService.detail_menu_services(query_db, menu_id)
        logger.info(f'获取menu_id为{menu_id}的信息成功')
        return response_200(data=detail_menu_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/menu_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
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.notice_service import *
from module_admin.entity.vo.notice_vo import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import get_page_obj
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


noticeController = APIRouter(dependencies=[Depends(get_current_user)])


@noticeController.post("/notice/get", response_model=NoticePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:list'))])
async def get_system_notice_list(request: Request, notice_page_query: NoticePageObject, query_db: Session = Depends(get_db)):
    try:
        notice_query = NoticeQueryModel(**notice_page_query.dict())
        # 获取全量数据
        notice_query_result = NoticeService.get_notice_list_services(query_db, notice_query)
        # 分页操作
        notice_page_query_result = get_page_obj(notice_query_result, notice_page_query.page_num, notice_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=notice_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@noticeController.post("/notice/add", response_model=CrudNoticeResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:add'))])
@log_decorator(title='通知公告管理', business_type=1)
async def add_system_notice(request: Request, add_notice: NoticeModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_notice.create_by = current_user.user.user_name
        add_notice.update_by = current_user.user.user_name
        add_notice_result = NoticeService.add_notice_services(query_db, add_notice)
        if add_notice_result.is_success:
            logger.info(add_notice_result.message)
            return response_200(data=add_notice_result, message=add_notice_result.message)
        else:
            logger.warning(add_notice_result.message)
            return response_400(data="", message=add_notice_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@noticeController.patch("/notice/edit", response_model=CrudNoticeResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:edit'))])
@log_decorator(title='通知公告管理', business_type=2)
async def edit_system_notice(request: Request, edit_notice: NoticeModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_notice.update_by = current_user.user.user_name
        edit_notice.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_notice_result = NoticeService.edit_notice_services(query_db, edit_notice)
        if edit_notice_result.is_success:
            logger.info(edit_notice_result.message)
            return response_200(data=edit_notice_result, message=edit_notice_result.message)
        else:
            logger.warning(edit_notice_result.message)
            return response_400(data="", message=edit_notice_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@noticeController.post("/notice/delete", response_model=CrudNoticeResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:remove'))])
@log_decorator(title='通知公告管理', business_type=3)
async def delete_system_notice(request: Request, delete_notice: DeleteNoticeModel, query_db: Session = Depends(get_db)):
    try:
        delete_notice_result = NoticeService.delete_notice_services(query_db, delete_notice)
        if delete_notice_result.is_success:
            logger.info(delete_notice_result.message)
            return response_200(data=delete_notice_result, message=delete_notice_result.message)
        else:
            logger.warning(delete_notice_result.message)
            return response_400(data="", message=delete_notice_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@noticeController.get("/notice/{notice_id}", response_model=NoticeModel, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:query'))])
async def query_detail_system_post(request: Request, notice_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_notice_result = NoticeService.detail_notice_services(query_db, notice_id)
        logger.info(f'获取notice_id为{notice_id}的信息成功')
        return response_200(data=detail_notice_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/notice_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter
from fastapi import Depends
from config.get_db import get_db
from module_admin.service.login_service import get_current_user, Session
from module_admin.service.online_service import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import get_page_obj
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


onlineController = APIRouter(prefix='/online', dependencies=[Depends(get_current_user)])


@onlineController.post("/get", response_model=OnlinePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:list'))])
async def get_monitor_online_list(request: Request, online_page_query: OnlinePageObject):
    try:
        # 获取全量数据
        online_query_result = await OnlineService.get_online_list_services(request, online_page_query)
        # 分页操作
        online_page_query_result = get_page_obj(online_query_result, online_page_query.page_num, online_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=online_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@onlineController.post("/forceLogout", response_model=CrudOnlineResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:forceLogout'))])
@log_decorator(title='在线用户', business_type=7)
async def delete_monitor_online(request: Request, delete_online: DeleteOnlineModel, query_db: Session = Depends(get_db)):
    try:
        delete_online_result = await OnlineService.delete_online_services(request, delete_online)
        if delete_online_result.is_success:
            logger.info(delete_online_result.message)
            return response_200(data=delete_online_result, message=delete_online_result.message)
        else:
            logger.warning(delete_online_result.message)
            return response_400(data="", message=delete_online_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@onlineController.post("/batchLogout", response_model=CrudOnlineResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:online:batchLogout'))])
@log_decorator(title='在线用户', business_type=7)
async def delete_monitor_online(request: Request, delete_online: DeleteOnlineModel, query_db: Session = Depends(get_db)):
    try:
        delete_online_result = await OnlineService.delete_online_services(request, delete_online)
        if delete_online_result.is_success:
            logger.info(delete_online_result.message)
            return response_200(data=delete_online_result, message=delete_online_result.message)
        else:
            logger.warning(delete_online_result.message)
            return response_400(data="", message=delete_online_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/online_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
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.post_service import *
from module_admin.entity.vo.post_vo import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import get_page_obj
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


postController = APIRouter(dependencies=[Depends(get_current_user)])


@postController.post("/post/forSelectOption", response_model=PostSelectOptionResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_post_select(request: Request, query_db: Session = Depends(get_db)):
    try:
        role_query_result = PostService.get_post_select_option_services(query_db)
        logger.info('获取成功')
        return response_200(data=role_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@postController.post("/post/get", response_model=PostPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:post:list'))])
async def get_system_post_list(request: Request, post_page_query: PostPageObject, query_db: Session = Depends(get_db)):
    try:
        post_query = PostModel(**post_page_query.dict())
        # 获取全量数据
        post_query_result = PostService.get_post_list_services(query_db, post_query)
        # 分页操作
        post_page_query_result = get_page_obj(post_query_result, post_page_query.page_num, post_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=post_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@postController.post("/post/add", response_model=CrudPostResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:post:add'))])
@log_decorator(title='岗位管理', business_type=1)
async def add_system_post(request: Request, add_post: PostModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_post.create_by = current_user.user.user_name
        add_post.update_by = current_user.user.user_name
        add_post_result = PostService.add_post_services(query_db, add_post)
        if add_post_result.is_success:
            logger.info(add_post_result.message)
            return response_200(data=add_post_result, message=add_post_result.message)
        else:
            logger.warning(add_post_result.message)
            return response_400(data="", message=add_post_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@postController.patch("/post/edit", response_model=CrudPostResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:post:edit'))])
@log_decorator(title='岗位管理', business_type=2)
async def edit_system_post(request: Request, edit_post: PostModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_post.update_by = current_user.user.user_name
        edit_post.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_post_result = PostService.edit_post_services(query_db, edit_post)
        if edit_post_result.is_success:
            logger.info(edit_post_result.message)
            return response_200(data=edit_post_result, message=edit_post_result.message)
        else:
            logger.warning(edit_post_result.message)
            return response_400(data="", message=edit_post_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@postController.post("/post/delete", response_model=CrudPostResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:post:remove'))])
@log_decorator(title='岗位管理', business_type=3)
async def delete_system_post(request: Request, delete_post: DeletePostModel, query_db: Session = Depends(get_db)):
    try:
        delete_post_result = PostService.delete_post_services(query_db, delete_post)
        if delete_post_result.is_success:
            logger.info(delete_post_result.message)
            return response_200(data=delete_post_result, message=delete_post_result.message)
        else:
            logger.warning(delete_post_result.message)
            return response_400(data="", message=delete_post_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@postController.get("/post/{post_id}", response_model=PostModel, dependencies=[Depends(CheckUserInterfaceAuth('system:post:query'))])
async def query_detail_system_post(request: Request, post_id: int, query_db: Session = Depends(get_db)):
    try:
        detail_post_result = PostService.detail_post_services(query_db, post_id)
        logger.info(f'获取post_id为{post_id}的信息成功')
        return response_200(data=detail_post_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@postController.post("/post/export", dependencies=[Depends(CheckUserInterfaceAuth('system:post:export'))])
@log_decorator(title='岗位管理', business_type=5)
async def export_system_post_list(request: Request, post_query: PostModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        post_query_result = PostService.get_post_list_services(query_db, post_query)
        post_export_result = PostService.export_post_list_services(post_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(post_export_result))
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/post_controler.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
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 *
from utils.page_util import get_page_obj
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator


roleController = APIRouter(dependencies=[Depends(get_current_user)])


@roleController.post("/role/forSelectOption", response_model=RoleSelectOptionResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_role_select(request: Request, query_db: Session = Depends(get_db)):
    try:
        role_query_result = RoleService.get_role_select_option_services(query_db)
        logger.info('获取成功')
        return response_200(data=role_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))
    
    
@roleController.post("/role/get", response_model=RolePageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))])
async def get_system_role_list(request: Request, role_page_query: RolePageObject, query_db: Session = Depends(get_db)):
    try:
        role_query = RoleQueryModel(**role_page_query.dict())
        role_query_result = RoleService.get_role_list_services(query_db, role_query)
        # 分页操作
        role_page_query_result = get_page_obj(role_query_result, role_page_query.page_num, role_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=role_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))
    
    
@roleController.post("/role/add", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:add'))])
@log_decorator(title='角色管理', business_type=1)
async def add_system_role(request: Request, add_role: AddRoleModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_role.create_by = current_user.user.user_name
        add_role.update_by = current_user.user.user_name
        add_role_result = RoleService.add_role_services(query_db, add_role)
        if add_role_result.is_success:
            logger.info(add_role_result.message)
            return response_200(data=add_role_result, message=add_role_result.message)
        else:
            logger.warning(add_role_result.message)
            return response_400(data="", message=add_role_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))
    
    
@roleController.patch("/role/edit", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
@log_decorator(title='角色管理', business_type=2)
async def edit_system_role(request: Request, edit_role: AddRoleModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_role.update_by = current_user.user.user_name
        edit_role.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_role_result = RoleService.edit_role_services(query_db, edit_role)
        if edit_role_result.is_success:
            logger.info(edit_role_result.message)
            return response_200(data=edit_role_result, message=edit_role_result.message)
        else:
            logger.warning(edit_role_result.message)
            return response_400(data="", message=edit_role_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@roleController.patch("/role/dataScope", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:edit'))])
@log_decorator(title='角色管理', business_type=4)
async def edit_system_role_datascope(request: Request, role_data_scope: RoleDataScopeModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        role_data_scope.update_by = current_user.user.user_name
        role_data_scope.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        role_data_scope_result = RoleService.role_datascope_services(query_db, role_data_scope)
        if role_data_scope_result.is_success:
            logger.info(role_data_scope_result.message)
            return response_200(data=role_data_scope_result, message=role_data_scope_result.message)
        else:
            logger.warning(role_data_scope_result.message)
            return response_400(data="", message=role_data_scope_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))
    
    
@roleController.post("/role/delete", response_model=CrudRoleResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:role:remove'))])
@log_decorator(title='角色管理', business_type=3)
async def delete_system_role(request: Request, delete_role: DeleteRoleModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        delete_role.update_by = current_user.user.user_name
        delete_role.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        delete_role_result = RoleService.delete_role_services(query_db, delete_role)
        if delete_role_result.is_success:
            logger.info(delete_role_result.message)
            return response_200(data=delete_role_result, message=delete_role_result.message)
        else:
            logger.warning(delete_role_result.message)
            return response_400(data="", message=delete_role_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))
    
    
@roleController.get("/role/{role_id}", response_model=RoleDetailModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:query'))])
async def query_detail_system_role(request: Request, role_id: int, query_db: Session = Depends(get_db)):
    try:
        delete_role_result = RoleService.detail_role_services(query_db, role_id)
        logger.info(f'获取role_id为{role_id}的信息成功')
        return response_200(data=delete_role_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@roleController.post("/role/export", dependencies=[Depends(CheckUserInterfaceAuth('system:role:export'))])
@log_decorator(title='角色管理', business_type=5)
async def export_system_role_list(request: Request, role_query: RoleQueryModel, query_db: Session = Depends(get_db)):
    try:
        # 获取全量数据
        role_query_result = RoleService.get_role_list_services(query_db, role_query)
        role_export_result = RoleService.export_role_list_services(role_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(role_export_result))
    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))

dash-fastapi-backend/module_admin/controller/role_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
from fastapi import Depends
from module_admin.service.login_service import get_current_user
from module_admin.service.server_service import *
from utils.response_util import *
from utils.log_util import *
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth


serverController = APIRouter(prefix='/server', dependencies=[Depends(get_current_user)])


@serverController.post("/statisticalInfo", response_model=ServerMonitorModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:server:list'))])
async def get_monitor_server_info(request: Request):
    try:
        # 获取全量数据
        server_info_query_result = ServerService.get_server_monitor_info()
        logger.info('获取成功')
        return response_200(data=server_info_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))

dash-fastapi-backend/module_admin/controller/server_controller.py
insistence/Dash-FastAPI-Admin
from fastapi import APIRouter, Request
from fastapi import Depends
import base64
from config.get_db import get_db
from config.env import CachePathConfig
from module_admin.service.login_service import get_current_user
from module_admin.service.user_service import *
from module_admin.entity.vo.user_vo import *
from module_admin.dao.user_dao import *
from utils.page_util import get_page_obj
from utils.response_util import *
from utils.log_util import *
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.aspect.data_scope import GetDataScope
from module_admin.annotation.log_annotation import log_decorator


userController = APIRouter(dependencies=[Depends(get_current_user)])


@userController.post("/user/get", response_model=UserPageObjectResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))])
async def get_system_user_list(request: Request, user_page_query: UserPageObject, query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))):
    try:
        user_query = UserQueryModel(**user_page_query.dict())
        # 获取全量数据
        user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql)
        # 分页操作
        user_page_query_result = get_page_obj(user_query_result, user_page_query.page_num, user_page_query.page_size)
        logger.info('获取成功')
        return response_200(data=user_page_query_result, message="获取成功")
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@userController.post("/user/add", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:user:add'))])
@log_decorator(title='用户管理', business_type=1)
async def add_system_user(request: Request, add_user: AddUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        add_user.password = PwdUtil.get_password_hash(add_user.password)
        add_user.create_by = current_user.user.user_name
        add_user.update_by = current_user.user.user_name
        add_user_result = UserService.add_user_services(query_db, add_user)
        if add_user_result.is_success:
            logger.info(add_user_result.message)
            return response_200(data=add_user_result, message=add_user_result.message)
        else:
            logger.warning(add_user_result.message)
            return response_400(data="", message=add_user_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@userController.patch("/user/edit", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:user:edit'))])
@log_decorator(title='用户管理', business_type=2)
async def edit_system_user(request: Request, edit_user: AddUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_user.update_by = current_user.user.user_name
        edit_user.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_user_result = UserService.edit_user_services(query_db, edit_user)
        if edit_user_result.is_success:
            logger.info(edit_user_result.message)
            return response_200(data=edit_user_result, message=edit_user_result.message)
        else:
            logger.warning(edit_user_result.message)
            return response_400(data="", message=edit_user_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@userController.post("/user/delete", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('system:user:remove'))])
@log_decorator(title='用户管理', business_type=3)
async def delete_system_user(request: Request, delete_user: DeleteUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        delete_user.update_by = current_user.user.user_name
        delete_user.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        delete_user_result = UserService.delete_user_services(query_db, delete_user)
        if delete_user_result.is_success:
            logger.info(delete_user_result.message)
            return response_200(data=delete_user_result, message=delete_user_result.message)
        else:
            logger.warning(delete_user_result.message)
            return response_400(data="", message=delete_user_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@userController.get("/user/{user_id}", response_model=UserDetailModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:query'))])
async def query_detail_system_user(request: Request, user_id: int, query_db: Session = Depends(get_db)):
    try:
        delete_user_result = UserService.detail_user_services(query_db, user_id)
        logger.info(f'获取user_id为{user_id}的信息成功')
        return response_200(data=delete_user_result, message='获取成功')
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@userController.patch("/user/profile/changeAvatar", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
@log_decorator(title='个人信息', business_type=2)
async def change_system_user_profile_avatar(request: Request, edit_user: AddUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        avatar = edit_user.avatar
        # 去除 base64 字符串中的头部信息（data:image/jpeg;base64, 等等）
        base64_string = avatar.split(',', 1)[1]
        # 解码 base64 字符串
        file_data = base64.b64decode(base64_string)
        dir_path = os.path.join(CachePathConfig.PATH, 'avatar')
        try:
            os.makedirs(dir_path)
        except FileExistsError:
            pass
        filepath = os.path.join(dir_path, f'{current_user.user.user_name}_avatar.jpeg')
        with open(filepath, 'wb') as f:
            f.write(file_data)
        edit_user.user_id = current_user.user.user_id
        edit_user.avatar = f'{request.base_url}common/{CachePathConfig.PATHSTR}?taskId=avatar&filename={current_user.user.user_name}_avatar.jpeg'
        edit_user.update_by = current_user.user.user_name
        edit_user.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_user_result = UserService.edit_user_services(query_db, edit_user)
        if edit_user_result.is_success:
            logger.info(edit_user_result.message)
            return response_200(data=edit_user_result, message=edit_user_result.message)
        else:
            logger.warning(edit_user_result.message)
            return response_400(data="", message=edit_user_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@userController.patch("/user/profile/changeInfo", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
@log_decorator(title='个人信息', business_type=2)
async def change_system_user_profile_info(request: Request, edit_user: AddUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
    try:
        edit_user.user_id = current_user.user.user_id
        edit_user.update_by = current_user.user.user_name
        edit_user.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        edit_user_result = UserService.edit_user_services(query_db, edit_user)
        if edit_user_result.is_success:
            logger.info(edit_user_result.message)
            return response_200(data=edit_user_result, message=edit_user_result.message)
        else:
            logger.warning(edit_user_result.message)
            return response_400(data="", message=edit_user_result.message)
    except Exception as e:
        logger.exception(e)
        return response_500(data="", message=str(e))


@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)):
    try:
        if not reset_user.user_id and reset_user.old_password:
            reset_user.user_id = current_user.user.user_id
        reset_user.password = PwdUtil.get_password_hash(reset_user.password)
        reset_user.update_by = current_user.user.user_name
        reset_user.update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        reset_user_result = UserService.reset_user_services(query_db, reset_user)
        if reset_user_result.is_success:
            logger.info(reset_user_result.message)
            return response_200(data=reset_user_result, message=reset_user_result.message)
        else:
            logger.warning(reset_user_result.message)
            return response_400(data="", message=reset_user_result.message)
    except Exception as e:
        logger.exception(e)
        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)):
    try:
        # 获取全量数据
        user_query_result = UserService.get_user_list_services(query_db, user_query)
        user_export_result = UserService.export_user_list_services(user_query_result)
        logger.info('导出成功')
        return streaming_response_200(data=bytes2file_response(user_export_result))
    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))

dash-fastapi-backend/module_admin/controller/user_controller.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from module_admin.entity.do.config_do import SysConfig
from module_admin.entity.vo.config_vo import ConfigModel, ConfigQueryModel
from utils.time_format_util import list_format_datetime
from datetime import datetime, time


class ConfigDao:
    """
    参数配置管理模块数据库操作层
    """

    @classmethod
    def get_config_detail_by_id(cls, db: Session, config_id: int):
        """
        根据参数配置id获取参数配置详细信息
        :param db: orm对象
        :param config_id: 参数配置id
        :return: 参数配置信息对象
        """
        config_info = db.query(SysConfig) \
            .filter(SysConfig.config_id == config_id) \
            .first()

        return config_info

    @classmethod
    def get_config_detail_by_info(cls, db: Session, config: ConfigModel):
        """
        根据参数配置参数获取参数配置信息
        :param db: orm对象
        :param config: 参数配置参数对象
        :return: 参数配置信息对象
        """
        config_info = db.query(SysConfig) \
            .filter(SysConfig.config_key == config.config_key if config.config_key else True,
                    SysConfig.config_value == config.config_value if config.config_value else True) \
            .first()

        return config_info

    @classmethod
    def get_all_config(cls, db: Session):
        """
        获取所有的参数配置信息
        :param db: orm对象
        :return: 参数配置信息列表对象
        """
        config_info = db.query(SysConfig).all()

        return list_format_datetime(config_info)

    @classmethod
    def get_config_list(cls, db: Session, query_object: ConfigQueryModel):
        """
        根据查询参数获取参数配置列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 参数配置列表信息对象
        """
        config_list = db.query(SysConfig) \
            .filter(SysConfig.config_name.like(f'%{query_object.config_name}%') if query_object.config_name else True,
                    SysConfig.config_key.like(f'%{query_object.config_key}%') if query_object.config_key else True,
                    SysConfig.config_type == query_object.config_type if query_object.config_type else True,
                    SysConfig.create_time.between(
                        datetime.combine(datetime.strptime(query_object.create_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.create_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                    if query_object.create_time_start and query_object.create_time_end else True
                    ) \
            .distinct().all()

        return list_format_datetime(config_list)

    @classmethod
    def add_config_dao(cls, db: Session, config: ConfigModel):
        """
        新增参数配置数据库操作
        :param db: orm对象
        :param config: 参数配置对象
        :return:
        """
        db_config = SysConfig(**config.dict())
        db.add(db_config)
        db.flush()

        return db_config

    @classmethod
    def edit_config_dao(cls, db: Session, config: dict):
        """
        编辑参数配置数据库操作
        :param db: orm对象
        :param config: 需要更新的参数配置字典
        :return:
        """
        db.query(SysConfig) \
            .filter(SysConfig.config_id == config.get('config_id')) \
            .update(config)

    @classmethod
    def delete_config_dao(cls, db: Session, config: ConfigModel):
        """
        删除参数配置数据库操作
        :param db: orm对象
        :param config: 参数配置对象
        :return:
        """
        db.query(SysConfig) \
            .filter(SysConfig.config_id == config.config_id) \
            .delete()

dash-fastapi-backend/module_admin/dao/config_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.vo.dept_vo import DeptModel, DeptResponse, CrudDeptResponse
from utils.time_format_util import list_format_datetime


class DeptDao:
    """
    部门管理模块数据库操作层
    """

    @classmethod
    def get_dept_by_id(cls, db: Session, dept_id: int):
        """
        根据部门id获取在用部门信息
        :param db: orm对象
        :param dept_id: 部门id
        :return: 在用部门信息对象
        """
        dept_info = db.query(SysDept) \
            .filter(SysDept.dept_id == dept_id,
                    SysDept.status == 0,
                    SysDept.del_flag == 0) \
            .first()

        return dept_info

    @classmethod
    def get_dept_by_id_for_list(cls, db: Session, dept_id: int):
        """
        用于获取部门列表的工具方法
        :param db: orm对象
        :param dept_id: 部门id
        :return: 部门id对应的信息对象
        """
        dept_info = db.query(SysDept) \
            .filter(SysDept.dept_id == dept_id,
                    SysDept.del_flag == 0) \
            .first()

        return dept_info

    @classmethod
    def get_dept_detail_by_id(cls, db: Session, dept_id: int):
        """
        根据部门id获取部门详细信息
        :param db: orm对象
        :param dept_id: 部门id
        :return: 部门信息对象
        """
        dept_info = db.query(SysDept) \
            .filter(SysDept.dept_id == dept_id,
                    SysDept.del_flag == 0) \
            .first()

        return dept_info

    @classmethod
    def get_dept_detail_by_info(cls, db: Session, dept: DeptModel):
        """
        根据部门参数获取部门信息
        :param db: orm对象
        :param dept: 部门参数对象
        :return: 部门信息对象
        """
        dept_info = db.query(SysDept) \
            .filter(SysDept.parent_id == dept.parent_id if dept.parent_id else True,
                    SysDept.dept_name == dept.dept_name if dept.dept_name else True) \
            .first()

        return dept_info

    @classmethod
    def get_dept_info_for_edit_option(cls, db: Session, dept_info: DeptModel):
        """
        获取部门编辑对应的在用部门列表信息
        :param db: orm对象
        :param dept_info: 部门对象
        :return: 部门列表信息
        """
        dept_result = db.query(SysDept) \
            .filter(SysDept.dept_id != dept_info.dept_id, SysDept.parent_id != dept_info.dept_id,
                    SysDept.del_flag == 0, SysDept.status == 0) \
            .all()

        return list_format_datetime(dept_result)

    @classmethod
    def get_children_dept(cls, db: Session, dept_id: int):
        """
        根据部门id查询当前部门的子部门列表信息
        :param db: orm对象
        :param dept_id: 部门id
        :return: 子部门信息列表
        """
        dept_result = db.query(SysDept) \
            .filter(SysDept.parent_id == dept_id,
                    SysDept.del_flag == 0) \
            .all()

        return list_format_datetime(dept_result)

    @classmethod
    def get_dept_all_ancestors(cls, db: Session):
        """
        获取所有部门的ancestors信息
        :param db: orm对象
        :return: ancestors信息列表
        """
        ancestors = db.query(SysDept.ancestors)\
            .filter(SysDept.del_flag == 0)\
            .all()

        return ancestors

    @classmethod
    def get_dept_list_for_tree(cls, db: Session, dept_info: DeptModel):
        """
        获取所有在用部门列表信息
        :param db: orm对象
        :param dept_info: 部门对象
        :return: 在用部门列表信息
        """
        if dept_info.dept_name:
            dept_query_all = db.query(SysDept) \
                .filter(SysDept.status == 0,
                        SysDept.del_flag == 0,
                        SysDept.dept_name.like(f'%{dept_info.dept_name}%') if dept_info.dept_name else True) \
                .order_by(SysDept.order_num) \
                .distinct().all()

            dept = []
            if dept_query_all:
                for dept_query in dept_query_all:
                    ancestor_info = dept_query.ancestors.split(',')
                    ancestor_info.append(dept_query.dept_id)
                    for ancestor in ancestor_info:
                        dept_item = cls.get_dept_by_id(db, int(ancestor))
                        if dept_item:
                            dept.append(dept_item)
            # 去重
            dept_result = list(set(dept))
        else:
            dept_result = db.query(SysDept) \
                .filter(SysDept.status == 0, SysDept.del_flag == 0) \
                .order_by(SysDept.order_num) \
                .distinct().all()

        return list_format_datetime(dept_result)

    @classmethod
    def get_dept_list(cls, db: Session, page_object: DeptModel):
        """
        根据查询参数获取部门列表信息
        :param db: orm对象
        :param page_object: 不分页查询参数对象
        :return: 部门列表信息对象
        """
        if page_object.dept_name or page_object.status:
            dept_query_all = db.query(SysDept) \
                .filter(SysDept.del_flag == 0,
                        SysDept.status == page_object.status if page_object.status else True,
                        SysDept.dept_name.like(f'%{page_object.dept_name}%') if page_object.dept_name else True) \
                .order_by(SysDept.order_num)\
                .distinct().all()

            dept = []
            if dept_query_all:
                for dept_query in dept_query_all:
                    ancestor_info = dept_query.ancestors.split(',')
                    ancestor_info.append(dept_query.dept_id)
                    for ancestor in ancestor_info:
                        dept_item = cls.get_dept_by_id_for_list(db, int(ancestor))
                        if dept_item:
                            dept.append(dept_item)
            # 去重
            dept_result = list(set(dept))
        else:
            dept_result = db.query(SysDept) \
                .filter(SysDept.del_flag == 0) \
                .order_by(SysDept.order_num) \
                .distinct().all()

        result = dict(
            rows=list_format_datetime(dept_result),
        )

        return DeptResponse(**result)

    @classmethod
    def add_dept_dao(cls, db: Session, dept: DeptModel):
        """
        新增部门数据库操作
        :param db: orm对象
        :param dept: 部门对象
        :return: 新增校验结果
        """
        db_dept = SysDept(**dept.dict())
        db.add(db_dept)
        db.flush()

        return db_dept

    @classmethod
    def edit_dept_dao(cls, db: Session, dept: dict):
        """
        编辑部门数据库操作
        :param db: orm对象
        :param dept: 需要更新的部门字典
        :return: 编辑校验结果
        """
        db.query(SysDept) \
            .filter(SysDept.dept_id == dept.get('dept_id')) \
            .update(dept)

    @classmethod
    def delete_dept_dao(cls, db: Session, dept: DeptModel):
        """
        删除部门数据库操作
        :param db: orm对象
        :param dept: 部门对象
        :return:
        """
        db.query(SysDept) \
            .filter(SysDept.dept_id == dept.dept_id) \
            .update({SysDept.del_flag: '2', SysDept.update_by: dept.update_by, SysDept.update_time: dept.update_time})

dash-fastapi-backend/module_admin/dao/dept_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import and_
from sqlalchemy.orm import Session
from module_admin.entity.do.dict_do import SysDictType, SysDictData
from module_admin.entity.vo.dict_vo import DictTypeModel, DictTypeQueryModel, DictDataModel
from utils.time_format_util import list_format_datetime
from datetime import datetime, time


class DictTypeDao:
    """
    字典类型管理模块数据库操作层
    """

    @classmethod
    def get_dict_type_detail_by_id(cls, db: Session, dict_id: int):
        """
        根据字典类型id获取字典类型详细信息
        :param db: orm对象
        :param dict_id: 字典类型id
        :return: 字典类型信息对象
        """
        dict_type_info = db.query(SysDictType) \
            .filter(SysDictType.dict_id == dict_id) \
            .first()

        return dict_type_info

    @classmethod
    def get_dict_type_detail_by_info(cls, db: Session, dict_type: DictTypeModel):
        """
        根据字典类型参数获取字典类型信息
        :param db: orm对象
        :param dict_type: 字典类型参数对象
        :return: 字典类型信息对象
        """
        dict_type_info = db.query(SysDictType) \
            .filter(SysDictType.dict_type == dict_type.dict_type if dict_type.dict_type else True,
                    SysDictType.dict_name == dict_type.dict_name if dict_type.dict_name else True) \
            .first()

        return dict_type_info

    @classmethod
    def get_all_dict_type(cls, db: Session):
        """
        获取所有的字典类型信息
        :param db: orm对象
        :return: 字典类型信息列表对象
        """
        dict_type_info = db.query(SysDictType).all()

        return list_format_datetime(dict_type_info)

    @classmethod
    def get_dict_type_list(cls, db: Session, query_object: DictTypeQueryModel):
        """
        根据查询参数获取字典类型列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 字典类型列表信息对象
        """
        dict_type_list = db.query(SysDictType) \
            .filter(SysDictType.dict_name.like(f'%{query_object.dict_name}%') if query_object.dict_name else True,
                    SysDictType.dict_type.like(f'%{query_object.dict_type}%') if query_object.dict_type else True,
                    SysDictType.status == query_object.status if query_object.status else True,
                    SysDictType.create_time.between(
                        datetime.combine(datetime.strptime(query_object.create_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.create_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                    if query_object.create_time_start and query_object.create_time_end else True
                    ) \
            .distinct().all()

        return list_format_datetime(dict_type_list)

    @classmethod
    def add_dict_type_dao(cls, db: Session, dict_type: DictTypeModel):
        """
        新增字典类型数据库操作
        :param db: orm对象
        :param dict_type: 字典类型对象
        :return:
        """
        db_dict_type = SysDictType(**dict_type.dict())
        db.add(db_dict_type)
        db.flush()

        return db_dict_type

    @classmethod
    def edit_dict_type_dao(cls, db: Session, dict_type: dict):
        """
        编辑字典类型数据库操作
        :param db: orm对象
        :param dict_type: 需要更新的字典类型字典
        :return:
        """
        db.query(SysDictType) \
            .filter(SysDictType.dict_id == dict_type.get('dict_id')) \
            .update(dict_type)

    @classmethod
    def delete_dict_type_dao(cls, db: Session, dict_type: DictTypeModel):
        """
        删除字典类型数据库操作
        :param db: orm对象
        :param dict_type: 字典类型对象
        :return:
        """
        db.query(SysDictType) \
            .filter(SysDictType.dict_id == dict_type.dict_id) \
            .delete()


class DictDataDao:
    """
    字典数据管理模块数据库操作层
    """

    @classmethod
    def get_dict_data_detail_by_id(cls, db: Session, dict_code: int):
        """
        根据字典数据id获取字典数据详细信息
        :param db: orm对象
        :param dict_code: 字典数据id
        :return: 字典数据信息对象
        """
        dict_data_info = db.query(SysDictData) \
            .filter(SysDictData.dict_code == dict_code) \
            .first()

        return dict_data_info

    @classmethod
    def get_dict_data_detail_by_info(cls, db: Session, dict_data: DictDataModel):
        """
        根据字典数据参数获取字典数据信息
        :param db: orm对象
        :param dict_data: 字典数据参数对象
        :return: 字典数据信息对象
        """
        dict_data_info = db.query(SysDictData) \
            .filter(SysDictData.dict_type == dict_data.dict_type if dict_data.dict_type else True,
                    SysDictData.dict_label == dict_data.dict_label if dict_data.dict_label else True,
                    SysDictData.dict_value == dict_data.dict_value if dict_data.dict_value else True) \
            .first()

        return dict_data_info

    @classmethod
    def get_dict_data_list(cls, db: Session, query_object: DictDataModel):
        """
        根据查询参数获取字典数据列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 字典数据列表信息对象
        """
        dict_data_list = db.query(SysDictData) \
            .filter(SysDictData.dict_type == query_object.dict_type if query_object.dict_type else True,
                    SysDictData.dict_label.like(f'%{query_object.dict_label}%') if query_object.dict_label else True,
                    SysDictData.status == query_object.status if query_object.status else True
                    ) \
            .order_by(SysDictData.dict_sort) \
            .distinct().all()

        return list_format_datetime(dict_data_list)

    @classmethod
    def query_dict_data_list(cls, db: Session, dict_type: str):
        """
        根据查询参数获取字典数据列表信息
        :param db: orm对象
        :param dict_type: 字典类型
        :return: 字典数据列表信息对象
        """
        dict_data_list = db.query(SysDictData).select_from(SysDictType) \
            .filter(SysDictType.dict_type == dict_type if dict_type else True, SysDictType.status == 0) \
            .outerjoin(SysDictData, and_(SysDictType.dict_type == SysDictData.dict_type, SysDictData.status == 0)) \
            .order_by(SysDictData.dict_sort) \
            .distinct().all()

        return list_format_datetime(dict_data_list)

    @classmethod
    def add_dict_data_dao(cls, db: Session, dict_data: DictDataModel):
        """
        新增字典数据数据库操作
        :param db: orm对象
        :param dict_data: 字典数据对象
        :return:
        """
        db_data_type = SysDictData(**dict_data.dict())
        db.add(db_data_type)
        db.flush()

        return db_data_type

    @classmethod
    def edit_dict_data_dao(cls, db: Session, dict_data: dict):
        """
        编辑字典数据数据库操作
        :param db: orm对象
        :param dict_data: 需要更新的字典数据字典
        :return:
        """
        db.query(SysDictData) \
            .filter(SysDictData.dict_code == dict_data.get('dict_code')) \
            .update(dict_data)

    @classmethod
    def delete_dict_data_dao(cls, db: Session, dict_data: DictDataModel):
        """
        删除字典数据数据库操作
        :param db: orm对象
        :param dict_data: 字典数据对象
        :return:
        """
        db.query(SysDictData) \
            .filter(SysDictData.dict_code == dict_data.dict_code) \
            .delete()

dash-fastapi-backend/module_admin/dao/dict_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from module_admin.entity.do.job_do import SysJob
from module_admin.entity.vo.job_vo import JobModel
from utils.time_format_util import list_format_datetime, object_format_datetime


class JobDao:
    """
    定时任务管理模块数据库操作层
    """

    @classmethod
    def get_job_detail_by_id(cls, db: Session, job_id: int):
        """
        根据定时任务id获取定时任务详细信息
        :param db: orm对象
        :param job_id: 定时任务id
        :return: 定时任务信息对象
        """
        job_info = db.query(SysJob) \
            .filter(SysJob.job_id == job_id) \
            .first()

        return object_format_datetime(job_info)

    @classmethod
    def get_job_detail_by_info(cls, db: Session, job: JobModel):
        """
        根据定时任务参数获取定时任务信息
        :param db: orm对象
        :param job: 定时任务参数对象
        :return: 定时任务信息对象
        """
        job_info = db.query(SysJob) \
            .filter(SysJob.job_name == job.job_name if job.job_name else True,
                    SysJob.job_group == job.job_group if job.job_group else True,
                    SysJob.invoke_target == job.invoke_target if job.invoke_target else True,
                    SysJob.cron_expression == job.cron_expression if job.cron_expression else True) \
            .first()

        return job_info

    @classmethod
    def get_job_list(cls, db: Session, query_object: JobModel):
        """
        根据查询参数获取定时任务列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 定时任务列表信息对象
        """
        job_list = db.query(SysJob) \
            .filter(SysJob.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
                    SysJob.job_group == query_object.job_group if query_object.job_group else True,
                    SysJob.status == query_object.status if query_object.status else True
                    ) \
            .distinct().all()

        return list_format_datetime(job_list)

    @classmethod
    def get_job_list_for_scheduler(cls, db: Session):
        """
        获取定时任务列表信息
        :param db: orm对象
        :return: 定时任务列表信息对象
        """
        job_list = db.query(SysJob) \
            .filter(SysJob.status == 0) \
            .distinct().all()

        return list_format_datetime(job_list)

    @classmethod
    def add_job_dao(cls, db: Session, job: JobModel):
        """
        新增定时任务数据库操作
        :param db: orm对象
        :param job: 定时任务对象
        :return:
        """
        db_job = SysJob(**job.dict())
        db.add(db_job)
        db.flush()

        return db_job

    @classmethod
    def edit_job_dao(cls, db: Session, job: dict):
        """
        编辑定时任务数据库操作
        :param db: orm对象
        :param job: 需要更新的定时任务字典
        :return:
        """
        db.query(SysJob) \
            .filter(SysJob.job_id == job.get('job_id')) \
            .update(job)

    @classmethod
    def delete_job_dao(cls, db: Session, job: JobModel):
        """
        删除定时任务数据库操作
        :param db: orm对象
        :param job: 定时任务对象
        :return:
        """
        db.query(SysJob) \
            .filter(SysJob.job_id == job.job_id) \
            .delete()

dash-fastapi-backend/module_admin/dao/job_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from module_admin.entity.do.job_do import SysJobLog
from module_admin.entity.vo.job_vo import JobLogModel, JobLogQueryModel
from utils.time_format_util import list_format_datetime, object_format_datetime
from datetime import datetime, time


class JobLogDao:
    """
    定时任务日志管理模块数据库操作层
    """

    @classmethod
    def get_job_log_detail_by_id(cls, db: Session, job_log_id: int):
        """
        根据定时任务日志id获取定时任务日志详细信息
        :param db: orm对象
        :param job_log_id: 定时任务日志id
        :return: 定时任务日志信息对象
        """
        job_log_info = db.query(SysJobLog) \
            .filter(SysJobLog.job_log_id == job_log_id) \
            .first()

        return object_format_datetime(job_log_info)

    @classmethod
    def get_job_log_list(cls, db: Session, query_object: JobLogQueryModel):
        """
        根据查询参数获取定时任务日志列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 定时任务日志列表信息对象
        """
        job_log_list = db.query(SysJobLog) \
            .filter(SysJobLog.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
                    SysJobLog.job_group == query_object.job_group if query_object.job_group else True,
                    SysJobLog.status == query_object.status if query_object.status else True,
                    SysJobLog.create_time.between(
                        datetime.combine(datetime.strptime(query_object.create_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.create_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                    if query_object.create_time_start and query_object.create_time_end else True
                    ) \
            .distinct().all()

        return list_format_datetime(job_log_list)

    @classmethod
    def add_job_log_dao(cls, db: Session, job_log: JobLogModel):
        """
        新增定时任务日志数据库操作
        :param db: orm对象
        :param job_log: 定时任务日志对象
        :return:
        """
        db_job_log = SysJobLog(**job_log.dict())
        db.add(db_job_log)
        db.flush()

        return db_job_log

    @classmethod
    def delete_job_log_dao(cls, db: Session, job_log: JobLogModel):
        """
        删除定时任务日志数据库操作
        :param db: orm对象
        :param job_log: 定时任务日志对象
        :return:
        """
        db.query(SysJobLog) \
            .filter(SysJobLog.job_log_id == job_log.job_log_id) \
            .delete()

    @classmethod
    def clear_job_log_dao(cls, db: Session):
        """
        清除定时任务日志数据库操作
        :param db: orm对象
        :return:
        """
        db.query(SysJobLog) \
            .delete()

dash-fastapi-backend/module_admin/dao/job_log_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from module_admin.entity.do.log_do import SysOperLog, SysLogininfor
from module_admin.entity.vo.log_vo import OperLogModel, LogininforModel, OperLogQueryModel, LoginLogQueryModel
from utils.time_format_util import object_format_datetime, list_format_datetime
from datetime import datetime, time


class OperationLogDao:
    """
    操作日志管理模块数据库操作层
    """

    @classmethod
    def get_operation_log_detail_by_id(cls, db: Session, oper_id: int):
        """
        根据操作日志id获取操作日志详细信息
        :param db: orm对象
        :param oper_id: 操作日志id
        :return: 操作日志信息对象
        """
        operation_log_info = db.query(SysOperLog) \
            .filter(SysOperLog.oper_id == oper_id) \
            .first()

        return object_format_datetime(operation_log_info)

    @classmethod
    def get_operation_log_list(cls, db: Session, query_object: OperLogQueryModel):
        """
        根据查询参数获取操作日志列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 操作日志列表信息对象
        """
        operation_log_list = db.query(SysOperLog) \
            .filter(SysOperLog.title.like(f'%{query_object.title}%') if query_object.title else True,
                    SysOperLog.oper_name.like(f'%{query_object.oper_name}%') if query_object.oper_name else True,
                    SysOperLog.business_type == query_object.business_type if query_object.business_type else True,
                    SysOperLog.status == query_object.status if query_object.status else True,
                    SysOperLog.oper_time.between(
                        datetime.combine(datetime.strptime(query_object.oper_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.oper_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                                if query_object.oper_time_start and query_object.oper_time_end else True
                    )\
            .distinct().all()

        return list_format_datetime(operation_log_list)

    @classmethod
    def add_operation_log_dao(cls, db: Session, operation_log: OperLogModel):
        """
        新增操作日志数据库操作
        :param db: orm对象
        :param operation_log: 操作日志对象
        :return: 新增校验结果
        """
        db_operation_log = SysOperLog(**operation_log.dict())
        db.add(db_operation_log)
        db.flush()

        return db_operation_log

    @classmethod
    def delete_operation_log_dao(cls, db: Session, operation_log: OperLogModel):
        """
        删除操作日志数据库操作
        :param db: orm对象
        :param operation_log: 操作日志对象
        :return:
        """
        db.query(SysOperLog) \
            .filter(SysOperLog.oper_id == operation_log.oper_id) \
            .delete()

    @classmethod
    def clear_operation_log_dao(cls, db: Session):
        """
        清除操作日志数据库操作
        :param db: orm对象
        :return:
        """
        db.query(SysOperLog) \
            .delete()


class LoginLogDao:
    """
    登录日志管理模块数据库操作层
    """

    @classmethod
    def get_login_log_list(cls, db: Session, query_object: LoginLogQueryModel):
        """
        根据查询参数获取登录日志列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 登录日志列表信息对象
        """
        login_log_list = db.query(SysLogininfor) \
            .filter(SysLogininfor.ipaddr.like(f'%{query_object.ipaddr}%') if query_object.ipaddr else True,
                    SysLogininfor.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
                    SysLogininfor.status == query_object.status if query_object.status else True,
                    SysLogininfor.login_time.between(
                        datetime.combine(datetime.strptime(query_object.login_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.login_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                                if query_object.login_time_start and query_object.login_time_end else True
                    )\
            .distinct().all()

        return list_format_datetime(login_log_list)

    @classmethod
    def add_login_log_dao(cls, db: Session, login_log: LogininforModel):
        """
        新增登录日志数据库操作
        :param db: orm对象
        :param login_log: 登录日志对象
        :return: 新增校验结果
        """
        db_login_log = SysLogininfor(**login_log.dict())
        db.add(db_login_log)
        db.flush()

        return db_login_log

    @classmethod
    def delete_login_log_dao(cls, db: Session, login_log: LogininforModel):
        """
        删除登录日志数据库操作
        :param db: orm对象
        :param login_log: 登录日志对象
        :return:
        """
        db.query(SysLogininfor) \
            .filter(SysLogininfor.info_id == login_log.info_id) \
            .delete()

    @classmethod
    def clear_login_log_dao(cls, db: Session):
        """
        清除登录日志数据库操作
        :param db: orm对象
        :return:
        """
        db.query(SysLogininfor) \
            .delete()

dash-fastapi-backend/module_admin/dao/log_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from sqlalchemy import and_
from module_admin.entity.do.user_do import SysUser
from module_admin.entity.do.dept_do import SysDept


def login_by_account(db: Session, user_name: str):
    """
    根据用户名查询用户信息
    :param db: orm对象
    :param user_name: 用户名
    :return: 用户对象
    """
    user = db.query(SysUser, SysDept)\
        .filter(SysUser.user_name == user_name, SysUser.del_flag == '0') \
        .outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
        .distinct() \
        .first()

    return user

dash-fastapi-backend/module_admin/dao/login_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import and_
from sqlalchemy.orm import Session
from module_admin.entity.do.menu_do import SysMenu
from module_admin.entity.do.user_do import SysUser, SysUserRole
from module_admin.entity.do.role_do import SysRole, SysRoleMenu
from module_admin.entity.vo.menu_vo import MenuModel, MenuResponse
from utils.time_format_util import list_format_datetime


class MenuDao:
    """
    菜单管理模块数据库操作层
    """

    @classmethod
    def get_menu_detail_by_id(cls, db: Session, menu_id: int):
        """
        根据菜单id获取菜单详细信息
        :param db: orm对象
        :param menu_id: 菜单id
        :return: 菜单信息对象
        """
        menu_info = db.query(SysMenu) \
            .filter(SysMenu.menu_id == menu_id) \
            .first()

        return menu_info

    @classmethod
    def get_menu_detail_by_info(cls, db: Session, menu: MenuModel):
        """
        根据菜单参数获取菜单信息
        :param db: orm对象
        :param menu: 菜单参数对象
        :return: 菜单信息对象
        """
        menu_info = db.query(SysMenu) \
            .filter(SysMenu.parent_id == menu.parent_id if menu.parent_id else True,
                    SysMenu.menu_name == menu.menu_name if menu.menu_name else True,
                    SysMenu.menu_type == menu.menu_type if menu.menu_type else True) \
            .first()

        return menu_info

    @classmethod
    def get_menu_info_for_edit_option(cls, db: Session, menu_info: MenuModel, user_id: int, role: list):
        """
        根据角色信息获取菜单编辑对应的在用菜单列表信息
        :param db: orm对象
        :param menu_info: 菜单对象
        :param user_id: 用户id
        :param role: 用户角色列表信息
        :return: 菜单列表信息
        """
        menu_result = []
        for item in role:
            if item.role_id == 1:
                menu_result = db.query(SysMenu) \
                    .filter(SysMenu.menu_id != menu_info.menu_id, SysMenu.parent_id != menu_info.menu_id,
                            SysMenu.status == 0) \
                    .all()
            else:
                menu_result = db.query(SysMenu).select_from(SysUser) \
                    .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
                    .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \
                    .outerjoin(SysRole,
                               and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \
                    .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \
                    .outerjoin(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id,
                                             SysMenu.menu_id != menu_info.menu_id, SysMenu.parent_id != menu_info.menu_id,
                                             SysMenu.status == 0)) \
                    .order_by(SysMenu.order_num) \
                    .distinct().all()

        return list_format_datetime(menu_result)

    @classmethod
    def get_menu_list_for_tree(cls, db: Session, menu_info: MenuModel, user_id: int, role: list):
        """
        根据角色信息获取所有在用菜单列表信息
        :param db: orm对象
        :param menu_info: 菜单对象
        :param user_id: 用户id
        :param role: 用户角色列表信息
        :return: 菜单列表信息
        """
        menu_query_all = []
        for item in role:
            if item.role_id == 1:
                menu_query_all = db.query(SysMenu) \
                    .filter(SysMenu.status == 0,
                            SysMenu.menu_name.like(f'%{menu_info.menu_name}%') if menu_info.menu_name else True) \
                    .order_by(SysMenu.order_num) \
                    .distinct().all()
                break
            else:
                menu_query_all = db.query(SysMenu).select_from(SysUser) \
                    .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
                    .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \
                    .outerjoin(SysRole,
                               and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \
                    .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \
                    .outerjoin(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id,
                                             SysMenu.status == 0,
                                             SysMenu.menu_name.like(
                                                 f'%{menu_info.menu_name}%') if menu_info.menu_name else True)) \
                    .order_by(SysMenu.order_num) \
                    .distinct().all()

        return list_format_datetime(menu_query_all)

    @classmethod
    def get_menu_list(cls, db: Session, page_object: MenuModel, user_id: int, role: list):
        """
        根据查询参数获取菜单列表信息
        :param db: orm对象
        :param page_object: 不分页查询参数对象
        :param user_id: 用户id
        :param role: 用户角色列表
        :return: 菜单列表信息对象
        """
        menu_query_all = []
        for item in role:
            if item.role_id == 1:
                menu_query_all = db.query(SysMenu) \
                    .filter(SysMenu.status == page_object.status if page_object.status else True,
                            SysMenu.menu_name.like(
                                f'%{page_object.menu_name}%') if page_object.menu_name else True) \
                    .order_by(SysMenu.order_num) \
                    .distinct().all()
                break
            else:
                menu_query_all = db.query(SysMenu).select_from(SysUser) \
                    .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
                    .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \
                    .outerjoin(SysRole,
                               and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \
                    .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \
                    .outerjoin(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id,
                                             SysMenu.status == page_object.status if page_object.status else True,
                                             SysMenu.menu_name.like(
                                                 f'%{page_object.menu_name}%') if page_object.menu_name else True)) \
                    .order_by(SysMenu.order_num) \
                    .distinct().all()

        result = dict(
            rows=list_format_datetime(menu_query_all),
        )

        return MenuResponse(**result)

    @classmethod
    def add_menu_dao(cls, db: Session, menu: MenuModel):
        """
        新增菜单数据库操作
        :param db: orm对象
        :param menu: 菜单对象
        :return:
        """
        db_menu = SysMenu(**menu.dict())
        db.add(db_menu)
        db.flush()

        return db_menu

    @classmethod
    def edit_menu_dao(cls, db: Session, menu: dict):
        """
        编辑菜单数据库操作
        :param db: orm对象
        :param menu: 需要更新的菜单字典
        :return:
        """
        db.query(SysMenu) \
            .filter(SysMenu.menu_id == menu.get('menu_id')) \
            .update(menu)

    @classmethod
    def delete_menu_dao(cls, db: Session, menu: MenuModel):
        """
        删除菜单数据库操作
        :param db: orm对象
        :param menu: 菜单对象
        :return:
        """
        db.query(SysMenu) \
            .filter(SysMenu.menu_id == menu.menu_id) \
            .delete()

dash-fastapi-backend/module_admin/dao/menu_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from module_admin.entity.do.notice_do import SysNotice
from module_admin.entity.vo.notice_vo import NoticeModel, NoticeQueryModel, CrudNoticeResponse
from utils.time_format_util import list_format_datetime, object_format_datetime
from datetime import datetime, time


class NoticeDao:
    """
    通知公告管理模块数据库操作层
    """

    @classmethod
    def get_notice_detail_by_id(cls, db: Session, notice_id: int):
        """
        根据通知公告id获取通知公告详细信息
        :param db: orm对象
        :param notice_id: 通知公告id
        :return: 通知公告信息对象
        """
        notice_info = db.query(SysNotice) \
            .filter(SysNotice.notice_id == notice_id) \
            .first()

        return object_format_datetime(notice_info)

    @classmethod
    def get_notice_detail_by_info(cls, db: Session, notice: NoticeModel):
        """
        根据通知公告参数获取通知公告信息
        :param db: orm对象
        :param notice: 通知公告参数对象
        :return: 通知公告信息对象
        """
        notice_info = db.query(SysNotice) \
            .filter(SysNotice.notice_title == notice.notice_title if notice.notice_title else True,
                    SysNotice.notice_type == notice.notice_type if notice.notice_type else True,
                    SysNotice.notice_content == notice.notice_content if notice.notice_content else True) \
            .first()

        return notice_info

    @classmethod
    def get_notice_list(cls, db: Session, query_object: NoticeQueryModel):
        """
        根据查询参数获取通知公告列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 通知公告列表信息对象
        """
        notice_list = db.query(SysNotice) \
            .filter(SysNotice.notice_title.like(f'%{query_object.notice_title}%') if query_object.notice_title else True,
                    SysNotice.update_by.like(f'%{query_object.update_by}%') if query_object.update_by else True,
                    SysNotice.notice_type == query_object.notice_type if query_object.notice_type else True,
                    SysNotice.create_time.between(
                        datetime.combine(datetime.strptime(query_object.create_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.create_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                                      if query_object.create_time_start and query_object.create_time_end else True
                    ) \
            .distinct().all()

        return list_format_datetime(notice_list)

    @classmethod
    def add_notice_dao(cls, db: Session, notice: NoticeModel):
        """
        新增通知公告数据库操作
        :param db: orm对象
        :param notice: 通知公告对象
        :return:
        """
        db_notice = SysNotice(**notice.dict())
        db.add(db_notice)
        db.flush()

        return db_notice

    @classmethod
    def edit_notice_dao(cls, db: Session, notice: dict):
        """
        编辑通知公告数据库操作
        :param db: orm对象
        :param notice: 需要更新的通知公告字典
        :return:
        """
        db.query(SysNotice) \
            .filter(SysNotice.notice_id == notice.get('notice_id')) \
            .update(notice)

    @classmethod
    def delete_notice_dao(cls, db: Session, notice: NoticeModel):
        """
        删除通知公告数据库操作
        :param db: orm对象
        :param notice: 通知公告对象
        :return:
        """
        db.query(SysNotice) \
            .filter(SysNotice.notice_id == notice.notice_id) \
            .delete()

dash-fastapi-backend/module_admin/dao/notice_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy.orm import Session
from module_admin.entity.do.post_do import SysPost
from module_admin.entity.vo.post_vo import PostModel
from utils.time_format_util import list_format_datetime


class PostDao:
    """
    岗位管理模块数据库操作层
    """

    @classmethod
    def get_post_by_id(cls, db: Session, post_id: int):
        """
        根据岗位id获取在用岗位详细信息
        :param db: orm对象
        :param post_id: 岗位id
        :return: 在用岗位信息对象
        """
        post_info = db.query(SysPost) \
            .filter(SysPost.post_id == post_id,
                    SysPost.status == 0) \
            .first()

        return post_info

    @classmethod
    def get_post_detail_by_id(cls, db: Session, post_id: int):
        """
        根据岗位id获取岗位详细信息
        :param db: orm对象
        :param post_id: 岗位id
        :return: 岗位信息对象
        """
        post_info = db.query(SysPost) \
            .filter(SysPost.post_id == post_id) \
            .first()

        return post_info

    @classmethod
    def get_post_detail_by_info(cls, db: Session, post: PostModel):
        """
        根据岗位参数获取岗位信息
        :param db: orm对象
        :param post: 岗位参数对象
        :return: 岗位信息对象
        """
        post_info = db.query(SysPost) \
            .filter(SysPost.post_name == post.post_name if post.post_name else True,
                    SysPost.post_code == post.post_code if post.post_code else True,
                    SysPost.post_sort == post.post_sort if post.post_sort else True) \
            .first()

        return post_info

    @classmethod
    def get_post_select_option_dao(cls, db: Session):
        """
        获取所有在用岗位信息
        :param db: orm对象
        :return: 在用岗位信息列表
        """
        post_info = db.query(SysPost) \
            .filter(SysPost.status == 0) \
            .all()

        return post_info

    @classmethod
    def get_post_list(cls, db: Session, query_object: PostModel):
        """
        根据查询参数获取岗位列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 岗位列表信息对象
        """
        post_list = db.query(SysPost) \
            .filter(SysPost.post_code.like(f'%{query_object.post_code}%') if query_object.post_code else True,
                    SysPost.post_name.like(f'%{query_object.post_name}%') if query_object.post_name else True,
                    SysPost.status == query_object.status if query_object.status else True
                    ) \
            .order_by(SysPost.post_sort) \
            .distinct().all()

        return list_format_datetime(post_list)

    @classmethod
    def add_post_dao(cls, db: Session, post: PostModel):
        """
        新增岗位数据库操作
        :param db: orm对象
        :param post: 岗位对象
        :return:
        """
        db_post = SysPost(**post.dict())
        db.add(db_post)
        db.flush()

        return db_post

    @classmethod
    def edit_post_dao(cls, db: Session, post: dict):
        """
        编辑岗位数据库操作
        :param db: orm对象
        :param post: 需要更新的岗位字典
        :return:
        """
        db.query(SysPost) \
            .filter(SysPost.post_id == post.get('post_id')) \
            .update(post)

    @classmethod
    def delete_post_dao(cls, db: Session, post: PostModel):
        """
        删除岗位数据库操作
        :param db: orm对象
        :param post: 岗位对象
        :return:
        """
        db.query(SysPost) \
            .filter(SysPost.post_id == post.post_id) \
            .delete()

dash-fastapi-backend/module_admin/dao/post_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import and_, desc
from sqlalchemy.orm import Session
from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept
from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.menu_do import SysMenu
from module_admin.entity.vo.role_vo import RoleModel, RoleMenuModel, RoleDeptModel, RoleQueryModel, RoleDetailModel
from utils.time_format_util import list_format_datetime, object_format_datetime
from datetime import datetime, time


class RoleDao:
    """
    角色管理模块数据库操作层
    """

    @classmethod
    def get_role_by_name(cls, db: Session, role_name: str):
        """
        根据角色名获取在用角色信息
        :param db: orm对象
        :param role_name: 角色名
        :return: 当前角色名的角色信息对象
        """
        query_role_info = db.query(SysRole) \
            .filter(SysRole.status == 0, SysRole.del_flag == 0, SysRole.role_name == role_name) \
            .order_by(desc(SysRole.create_time)).distinct().first()

        return query_role_info

    @classmethod
    def get_role_by_info(cls, db: Session, role: RoleModel):
        """
        根据角色参数获取角色信息
        :param db: orm对象
        :param role: 角色参数
        :return: 当前角色参数的角色信息对象
        """
        query_role_info = db.query(SysRole) \
            .filter(SysRole.del_flag == 0,
                    SysRole.role_name == role.role_name if role.role_name else True,
                    SysRole.role_key == role.role_key if role.role_key else True) \
            .order_by(desc(SysRole.create_time)).distinct().first()

        return query_role_info

    @classmethod
    def get_role_by_id(cls, db: Session, role_id: int):
        """
        根据角色id获取在用角色信息
        :param db: orm对象
        :param role_id: 角色id
        :return: 当前角色id的角色信息对象
        """
        role_info = db.query(SysRole) \
            .filter(SysRole.role_id == role_id,
                    SysRole.status == 0,
                    SysRole.del_flag == 0) \
            .first()

        return role_info

    @classmethod
    def get_role_detail_by_id(cls, db: Session, role_id: int):
        """
        根据role_id获取角色详细信息
        :param db: orm对象
        :param role_id: 角色id
        :return: 当前role_id的角色信息对象
        """
        query_role_basic_info = db.query(SysRole) \
            .filter(SysRole.del_flag == 0, SysRole.role_id == role_id) \
            .distinct().first()
        query_role_menu_info = db.query(SysMenu).select_from(SysRole) \
            .filter(SysRole.del_flag == 0, SysRole.role_id == role_id) \
            .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \
            .outerjoin(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == 0)) \
            .distinct().all()
        query_role_dept_info = db.query(SysDept).select_from(SysRole) \
            .filter(SysRole.del_flag == 0, SysRole.role_id == role_id) \
            .outerjoin(SysRoleDept, SysRole.role_id == SysRoleDept.role_id) \
            .outerjoin(SysDept, and_(SysRoleDept.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
            .distinct().all()
        results = dict(
            role=object_format_datetime(query_role_basic_info),
            menu=list_format_datetime(query_role_menu_info),
            dept=list_format_datetime(query_role_dept_info),
        )

        return RoleDetailModel(**results)

    @classmethod
    def get_role_select_option_dao(cls, db: Session):
        """
        获取编辑页面对应的在用角色列表信息
        :param db: orm对象
        :return: 角色列表信息
        """
        role_info = db.query(SysRole) \
            .filter(SysRole.role_id != 1, SysRole.status == 0, SysRole.del_flag == 0) \
            .all()

        return role_info

    @classmethod
    def get_role_list(cls, db: Session, query_object: RoleQueryModel):
        """
        根据查询参数获取角色列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :return: 角色列表信息对象
        """
        role_list = db.query(SysRole) \
            .filter(SysRole.del_flag == 0,
                    SysRole.role_name.like(f'%{query_object.role_name}%') if query_object.role_name else True,
                    SysRole.role_key.like(f'%{query_object.role_key}%') if query_object.role_key else True,
                    SysRole.status == query_object.status if query_object.status else True,
                    SysRole.create_time.between(
                        datetime.combine(datetime.strptime(query_object.create_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.create_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                    if query_object.create_time_start and query_object.create_time_end else True
                    ) \
            .order_by(SysRole.role_sort) \
            .distinct().all()

        return list_format_datetime(role_list)

    @classmethod
    def add_role_dao(cls, db: Session, role: RoleModel):
        """
        新增角色数据库操作
        :param db: orm对象
        :param role: 角色对象
        :return:
        """
        db_role = SysRole(**role.dict())
        db.add(db_role)
        db.flush()

        return db_role

    @classmethod
    def edit_role_dao(cls, db: Session, role: dict):
        """
        编辑角色数据库操作
        :param db: orm对象
        :param role: 需要更新的角色字典
        :return:
        """
        db.query(SysRole) \
            .filter(SysRole.role_id == role.get('role_id')) \
            .update(role)

    @classmethod
    def delete_role_dao(cls, db: Session, role: RoleModel):
        """
        删除角色数据库操作
        :param db: orm对象
        :param role: 角色对象
        :return:
        """
        db.query(SysRole) \
            .filter(SysRole.role_id == role.role_id) \
            .update({SysRole.del_flag: '2', SysRole.update_by: role.update_by, SysRole.update_time: role.update_time})

    @classmethod
    def add_role_menu_dao(cls, db: Session, role_menu: RoleMenuModel):
        """
        新增角色菜单关联信息数据库操作
        :param db: orm对象
        :param role_menu: 用户角色菜单关联对象
        :return:
        """
        db_role_menu = SysRoleMenu(**role_menu.dict())
        db.add(db_role_menu)

    @classmethod
    def delete_role_menu_dao(cls, db: Session, role_menu: RoleMenuModel):
        """
        删除角色菜单关联信息数据库操作
        :param db: orm对象
        :param role_menu: 角色菜单关联对象
        :return:
        """
        db.query(SysRoleMenu) \
            .filter(SysRoleMenu.role_id == role_menu.role_id) \
            .delete()

    @classmethod
    def add_role_dept_dao(cls, db: Session, role_dept: RoleDeptModel):
        """
        新增角色部门关联信息数据库操作
        :param db: orm对象
        :param role_dept: 用户角色部门关联对象
        :return:
        """
        db_role_dept = SysRoleDept(**role_dept.dict())
        db.add(db_role_dept)

    @classmethod
    def delete_role_dept_dao(cls, db: Session, role_dept: RoleDeptModel):
        """
        删除角色部门关联信息数据库操作
        :param db: orm对象
        :param role_dept: 角色部门关联对象
        :return:
        """
        db.query(SysRoleDept) \
            .filter(SysRoleDept.role_id == role_dept.role_id) \
            .delete()

dash-fastapi-backend/module_admin/dao/role_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import and_, or_, desc, func
from sqlalchemy.orm import Session
from module_admin.entity.do.user_do import SysUser, SysUserRole, SysUserPost
from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept
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, UserRoleQueryModel
from utils.time_format_util import list_format_datetime, format_datetime_dict_list
from datetime import datetime, time
from typing import Union, List


class UserDao:
    """
    用户管理模块数据库操作层
    """

    @classmethod
    def get_user_by_name(cls, db: Session, user_name: str):
        """
        根据用户名获取用户信息
        :param db: orm对象
        :param user_name: 用户名
        :return: 当前用户名的用户信息对象
        """
        query_user_info = db.query(SysUser) \
            .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_name == user_name) \
            .order_by(desc(SysUser.create_time)).distinct().first()

        return query_user_info

    @classmethod
    def get_user_by_info(cls, db: Session, user: UserModel):
        """
        根据用户参数获取用户信息
        :param db: orm对象
        :param user: 用户参数
        :return: 当前用户参数的用户信息对象
        """
        query_user_info = db.query(SysUser) \
            .filter(SysUser.del_flag == 0,
                    SysUser.user_name == user.user_name) \
            .order_by(desc(SysUser.create_time)).distinct().first()

        return query_user_info

    @classmethod
    def get_user_by_id(cls, db: Session, user_id: int):
        """
        根据user_id获取用户信息
        :param db: orm对象
        :param user_id: 用户id
        :return: 当前user_id的用户信息对象
        """
        query_user_basic_info = db.query(SysUser) \
            .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .distinct().all()
        query_user_dept_info = db.query(SysDept).select_from(SysUser) \
            .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
            .distinct().all()
        query_user_role_info = db.query(SysRole).select_from(SysUser) \
            .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \
            .outerjoin(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \
            .distinct().all()
        query_user_post_info = db.query(SysPost).select_from(SysUser) \
            .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .outerjoin(SysUserPost, SysUser.user_id == SysUserPost.user_id) \
            .outerjoin(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == 0)) \
            .distinct().all()
        query_user_menu_info = []
        for item in query_user_role_info:
            if item.role_id == 1:
                query_user_menu_info = db.query(SysMenu) \
                    .filter(SysMenu.status == 0) \
                    .distinct().all()
                break
            else:
                query_user_menu_info = db.query(SysMenu).select_from(SysUser) \
                    .filter(SysUser.status == 0, SysUser.del_flag == 0, SysUser.user_id == user_id) \
                    .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \
                    .outerjoin(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \
                    .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \
                    .outerjoin(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == 0)) \
                    .order_by(SysMenu.order_num) \
                    .distinct().all()
        results = dict(
            user_basic_info=list_format_datetime(query_user_basic_info),
            user_dept_info=list_format_datetime(query_user_dept_info),
            user_role_info=list_format_datetime(query_user_role_info),
            user_post_info=list_format_datetime(query_user_post_info),
            user_menu_info=list_format_datetime(query_user_menu_info)
        )

        return CurrentUserInfo(**results)

    @classmethod
    def get_user_detail_by_id(cls, db: Session, user_id: int):
        """
        根据user_id获取用户详细信息
        :param db: orm对象
        :param user_id: 用户id
        :return: 当前user_id的用户信息对象
        """
        query_user_basic_info = db.query(SysUser) \
            .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .distinct().all()
        query_user_dept_info = db.query(SysDept).select_from(SysUser) \
            .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
            .distinct().all()
        query_user_role_info = db.query(SysRole).select_from(SysUser) \
            .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \
            .outerjoin(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \
            .distinct().all()
        query_user_post_info = db.query(SysPost).select_from(SysUser) \
            .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .outerjoin(SysUserPost, SysUser.user_id == SysUserPost.user_id) \
            .outerjoin(SysPost, and_(SysUserPost.post_id == SysPost.post_id, SysPost.status == 0)) \
            .distinct().all()
        query_user_menu_info = db.query(SysMenu).select_from(SysUser) \
            .filter(SysUser.del_flag == 0, SysUser.user_id == user_id) \
            .outerjoin(SysUserRole, SysUser.user_id == SysUserRole.user_id) \
            .outerjoin(SysRole, and_(SysUserRole.role_id == SysRole.role_id, SysRole.status == 0, SysRole.del_flag == 0)) \
            .outerjoin(SysRoleMenu, SysRole.role_id == SysRoleMenu.role_id) \
            .outerjoin(SysMenu, and_(SysRoleMenu.menu_id == SysMenu.menu_id, SysMenu.status == 0)) \
            .distinct().all()
        results = dict(
            user_basic_info=list_format_datetime(query_user_basic_info),
            user_dept_info=list_format_datetime(query_user_dept_info),
            user_role_info=list_format_datetime(query_user_role_info),
            user_post_info=list_format_datetime(query_user_post_info),
            user_menu_info=list_format_datetime(query_user_menu_info)
        )

        return CurrentUserInfo(**results)

    @classmethod
    def get_user_list(cls, db: Session, query_object: UserQueryModel, data_scope_sql: str):
        """
        根据查询参数获取用户列表信息
        :param db: orm对象
        :param query_object: 查询参数对象
        :param data_scope_sql: 数据权限对应的查询sql语句
        :return: 用户列表信息对象
        """
        user_list = db.query(SysUser, SysDept) \
            .filter(SysUser.del_flag == 0,
                    or_(SysUser.dept_id == query_object.dept_id, SysUser.dept_id.in_(
                        db.query(SysDept.dept_id).filter(func.find_in_set(query_object.dept_id, SysDept.ancestors))
                    )) if query_object.dept_id else True,
                    SysUser.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
                    SysUser.nick_name.like(f'%{query_object.nick_name}%') if query_object.nick_name else True,
                    SysUser.email.like(f'%{query_object.email}%') if query_object.email else True,
                    SysUser.phonenumber.like(f'%{query_object.phonenumber}%') if query_object.phonenumber else True,
                    SysUser.status == query_object.status if query_object.status else True,
                    SysUser.sex == query_object.sex if query_object.sex else True,
                    SysUser.create_time.between(
                        datetime.combine(datetime.strptime(query_object.create_time_start, '%Y-%m-%d'), time(00, 00, 00)),
                        datetime.combine(datetime.strptime(query_object.create_time_end, '%Y-%m-%d'), time(23, 59, 59)))
                    if query_object.create_time_start and query_object.create_time_end else True,
                    eval(data_scope_sql)
                    ) \
            .outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
            .distinct().all()

        result_list: List[Union[dict, None]] = []
        if user_list:
            for item in user_list:
                obj = dict(
                    user_id=item[0].user_id,
                    dept_id=item[0].dept_id,
                    dept_name=item[1].dept_name if item[1] else '',
                    user_name=item[0].user_name,
                    nick_name=item[0].nick_name,
                    user_type=item[0].user_type,
                    email=item[0].email,
                    phonenumber=item[0].phonenumber,
                    sex=item[0].sex,
                    avatar=item[0].avatar,
                    status=item[0].status,
                    del_flag=item[0].del_flag,
                    login_ip=item[0].login_ip,
                    login_date=item[0].login_date,
                    create_by=item[0].create_by,
                    create_time=item[0].create_time,
                    update_by=item[0].update_by,
                    update_time=item[0].update_time,
                    remark=item[0].remark
                )
                result_list.append(obj)

        return format_datetime_dict_list(result_list)

    @classmethod
    def add_user_dao(cls, db: Session, user: UserModel):
        """
        新增用户数据库操作
        :param db: orm对象
        :param user: 用户对象
        :return: 新增校验结果
        """
        db_user = SysUser(**user.dict())
        db.add(db_user)
        db.flush()

        return db_user

    @classmethod
    def edit_user_dao(cls, db: Session, user: dict):
        """
        编辑用户数据库操作
        :param db: orm对象
        :param user: 需要更新的用户字典
        :return: 编辑校验结果
        """
        db.query(SysUser) \
            .filter(SysUser.user_id == user.get('user_id')) \
            .update(user)

    @classmethod
    def delete_user_dao(cls, db: Session, user: UserModel):
        """
        删除用户数据库操作
        :param db: orm对象
        :param user: 用户对象
        :return:
        """
        db.query(SysUser) \
            .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):
        """
        新增用户角色关联信息数据库操作
        :param db: orm对象
        :param user_role: 用户角色关联对象
        :return:
        """
        db_user_role = SysUserRole(**user_role.dict())
        db.add(db_user_role)

    @classmethod
    def delete_user_role_dao(cls, db: Session, user_role: UserRoleModel):
        """
        删除用户角色关联信息数据库操作
        :param db: orm对象
        :param user_role: 用户角色关联对象
        :return:
        """
        db.query(SysUserRole) \
            .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):
        """
        新增用户岗位关联信息数据库操作
        :param db: orm对象
        :param user_post: 用户岗位关联对象
        :return:
        """
        db_user_post = SysUserPost(**user_post.dict())
        db.add(db_user_post)

    @classmethod
    def delete_user_post_dao(cls, db: Session, user_post: UserPostModel):
        """
        删除用户岗位关联信息数据库操作
        :param db: orm对象
        :param user_post: 用户岗位关联对象
        :return:
        """
        db.query(SysUserPost) \
            .filter(SysUserPost.user_id == user_post.user_id) \
            .delete()

    @classmethod
    def get_user_dept_info(cls, db: Session, dept_id: int):
        dept_basic_info = db.query(SysDept) \
            .filter(SysDept.dept_id == dept_id,
                    SysDept.status == 0,
                    SysDept.del_flag == 0) \
            .first()
        return dept_basic_info

dash-fastapi-backend/module_admin/dao/user_dao.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime
from config.database import Base
from datetime import datetime


class SysConfig(Base):
    """
    参数配置表
    """
    __tablename__ = 'sys_config'

    config_id = Column(Integer, primary_key=True, autoincrement=True, comment='参数主键')
    config_name = Column(String(100), nullable=True, default='', comment='参数名称')
    config_key = Column(String(100), nullable=True, default='', comment='参数键名')
    config_value = Column(String(500), nullable=True, default='', comment='参数键值')
    config_type = Column(String(1), nullable=True, default='N', comment='系统内置（Y是 N否）')
    create_by = Column(String(64), nullable=True, default='', comment='创建者')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64), nullable=True, default='', comment='更新者')
    update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
    remark = Column(String(500), nullable=True, default='', comment='备注')
dash-fastapi-backend/module_admin/entity/do/config_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime
from config.database import Base
from datetime import datetime


class SysDept(Base):
    """
    部门表
    """
    __tablename__ = 'sys_dept'

    dept_id = Column(Integer, primary_key=True, autoincrement=True, comment='部门id')
    parent_id = Column(Integer, default=0, comment='父部门id')
    ancestors = Column(String(50), nullable=True, default='', comment='祖级列表')
    dept_name = Column(String(30), nullable=True, default='', comment='部门名称')
    order_num = Column(Integer, default=0, comment='显示顺序')
    leader = Column(String(20), nullable=True, default=None, comment='负责人')
    phone = Column(String(11), nullable=True, default=None, comment='联系电话')
    email = Column(String(50), nullable=True, default=None, comment='邮箱')
    status = Column(String(1), nullable=True, default=0, comment='部门状态（0正常 1停用）')
    del_flag = Column(String(1), nullable=True, default=0, comment='删除标志（0代表存在 2代表删除）')
    create_by = Column(String(64), nullable=True, default='', comment='创建者')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64), nullable=True, default='', comment='更新者')
    update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')

dash-fastapi-backend/module_admin/entity/do/dept_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime, UniqueConstraint
from config.database import Base
from datetime import datetime


class SysDictType(Base):
    """
    字典类型表
    """
    __tablename__ = 'sys_dict_type'

    dict_id = Column(Integer, primary_key=True, autoincrement=True, comment='字典主键')
    dict_name = Column(String(100), nullable=True, default='', comment='字典名称')
    dict_type = Column(String(100), nullable=True, default='', comment='字典类型')
    status = Column(String(1), nullable=True, default='0', comment='状态（0正常 1停用）')
    create_by = Column(String(64), nullable=True, default='', comment='创建者')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64), nullable=True, default='', comment='更新者')
    update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
    remark = Column(String(500), nullable=True, default='', comment='备注')

    __table_args__ = (
        UniqueConstraint('dict_type', name='uq_sys_dict_type_dict_type'),
    )


class SysDictData(Base):
    """
    字典数据表
    """
    __tablename__ = 'sys_dict_data'

    dict_code = Column(Integer, primary_key=True, autoincrement=True, comment='字典编码')
    dict_sort = Column(Integer, nullable=True, default=0, comment='字典排序')
    dict_label = Column(String(100), nullable=True, default='', comment='字典标签')
    dict_value = Column(String(100), nullable=True, default='', comment='字典键值')
    dict_type = Column(String(100), nullable=True, default='', comment='字典类型')
    css_class = Column(String(100), nullable=True, default='', comment='样式属性（其他样式扩展）')
    list_class = Column(String(100), nullable=True, default='', comment='表格回显样式')
    is_default = Column(String(1), nullable=True, default='N', comment='是否默认（Y是 N否）')
    status = Column(String(1), nullable=True, default='0', comment='状态（0正常 1停用）')
    create_by = Column(String(64), nullable=True, default='', comment='创建者')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64), nullable=True, default='', comment='更新者')
    update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
    remark = Column(String(500), nullable=True, default='', comment='备注')

dash-fastapi-backend/module_admin/entity/do/dict_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime
from config.database import Base
from datetime import datetime


class SysJob(Base):
    """
    定时任务调度表
    """
    __tablename__ = 'sys_job'

    job_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务ID')
    job_name = Column(String(64, collation='utf8_general_ci'), nullable=False, comment='任务名称')
    job_group = Column(String(64, collation='utf8_general_ci'), nullable=False, default='default', comment='任务组名')
    job_executor = Column(String(64, collation='utf8_general_ci'), nullable=False, default='default', comment='任务执行器')
    invoke_target = Column(String(500, collation='utf8_general_ci'), nullable=False, comment='调用目标字符串')
    job_args = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='位置参数')
    job_kwargs = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='关键字参数')
    cron_expression = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='cron执行表达式')
    misfire_policy = Column(String(20, collation='utf8_general_ci'), nullable=True, default='3', comment='计划执行错误策略（1立即执行 2执行一次 3放弃执行）')
    concurrent = Column(String(1, collation='utf8_general_ci'), nullable=True, default='1', comment='是否并发执行（0允许 1禁止）')
    status = Column(String(1, collation='utf8_general_ci'), nullable=True, default='0', comment='状态（0正常 1暂停）')
    create_by = Column(String(64, collation='utf8_general_ci'), nullable=True, default='', comment='创建者')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64, collation='utf8_general_ci'), nullable=True, default='', comment='更新者')
    update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
    remark = Column(String(500, collation='utf8_general_ci'), nullable=True, default='', comment='备注信息')


class SysJobLog(Base):
    """
    定时任务调度日志表
    """
    __tablename__ = 'sys_job_log'

    job_log_id = Column(Integer, primary_key=True, autoincrement=True, comment='任务日志ID')
    job_name = Column(String(64, collation='utf8_general_ci'), nullable=False, comment='任务名称')
    job_group = Column(String(64, collation='utf8_general_ci'), nullable=False, comment='任务组名')
    job_executor = Column(String(64, collation='utf8_general_ci'), nullable=False, default='default', comment='任务执行器')
    invoke_target = Column(String(500, collation='utf8_general_ci'), nullable=False, comment='调用目标字符串')
    job_args = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='位置参数')
    job_kwargs = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='关键字参数')
    job_trigger = Column(String(255, collation='utf8_general_ci'), nullable=True, comment='任务触发器')
    job_message = Column(String(500, collation='utf8_general_ci'), nullable=True, default='', comment='日志信息')
    status = Column(String(1, collation='utf8_general_ci'), nullable=True, default='0', comment='执行状态（0正常 1失败）')
    exception_info = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='异常信息')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')

dash-fastapi-backend/module_admin/entity/do/job_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime, Text, BigInteger, Index
from config.database import Base
from datetime import datetime


class SysLogininfor(Base):
    """
    系统访问记录
    """
    __tablename__ = 'sys_logininfor'

    info_id = Column(Integer, primary_key=True, autoincrement=True, comment='访问ID')
    user_name = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='用户账号')
    ipaddr = Column(String(128, collation='utf8_general_ci'), nullable=True, default='', comment='登录IP地址')
    login_location = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='登录地点')
    browser = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='浏览器类型')
    os = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='操作系统')
    status = Column(String(1, collation='utf8_general_ci'), nullable=True, default='0', comment='登录状态（0成功 1失败）')
    msg = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='提示消息')
    login_time = Column(DateTime, nullable=True, default=datetime.now(), comment='访问时间')

    idx_sys_logininfor_s = Index('idx_sys_logininfor_s', status)
    idx_sys_logininfor_lt = Index('idx_sys_logininfor_lt', login_time)


class SysOperLog(Base):
    """
    操作日志记录
    """
    __tablename__ = 'sys_oper_log'

    oper_id = Column(BigInteger, primary_key=True, autoincrement=True, comment='日志主键')
    title = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='模块标题')
    business_type = Column(Integer, default=0, comment='业务类型（0其它 1新增 2修改 3删除）')
    method = Column(String(100, collation='utf8_general_ci'), nullable=True, default='', comment='方法名称')
    request_method = Column(String(10, collation='utf8_general_ci'), nullable=True, default='', comment='请求方式')
    operator_type = Column(Integer, default=0, comment='操作类别（0其它 1后台用户 2手机端用户）')
    oper_name = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='操作人员')
    dept_name = Column(String(50, collation='utf8_general_ci'), nullable=True, default='', comment='部门名称')
    oper_url = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='请求URL')
    oper_ip = Column(String(128, collation='utf8_general_ci'), nullable=True, default='', comment='主机地址')
    oper_location = Column(String(255, collation='utf8_general_ci'), nullable=True, default='', comment='操作地点')
    oper_param = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='请求参数')
    json_result = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='返回参数')
    status = Column(Integer, default=0, comment='操作状态（0正常 1异常）')
    error_msg = Column(String(2000, collation='utf8_general_ci'), nullable=True, default='', comment='错误消息')
    oper_time = Column(DateTime, nullable=True, default=datetime.now(), comment='操作时间')
    cost_time = Column(BigInteger, default=0, comment='消耗时间')

    idx_sys_oper_log_bt = Index('idx_sys_oper_log_bt', business_type)
    idx_sys_oper_log_s = Index('idx_sys_oper_log_s', status)
    idx_sys_oper_log_ot = Index('idx_sys_oper_log_ot', oper_time)

dash-fastapi-backend/module_admin/entity/do/log_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime
from config.database import Base
from datetime import datetime


class SysMenu(Base):
    """
    菜单权限表
    """
    __tablename__ = 'sys_menu'

    menu_id = Column(Integer, primary_key=True, autoincrement=True, comment='菜单ID')
    menu_name = Column(String(50), nullable=False, default='', comment='菜单名称')
    parent_id = Column(Integer, default=0, comment='父菜单ID')
    order_num = Column(Integer, default=0, comment='显示顺序')
    path = Column(String(200), nullable=True, default='', comment='路由地址')
    component = Column(String(255), nullable=True, default=None, comment='组件路径')
    query = Column(String(255), nullable=True, default=None, comment='路由参数')
    is_frame = Column(Integer, default=1, comment='是否为外链（0是 1否）')
    is_cache = Column(Integer, default=0, comment='是否缓存（0缓存 1不缓存）')
    menu_type = Column(String(1), nullable=True, default='', comment='菜单类型（M目录 C菜单 F按钮）')
    visible = Column(String(1), nullable=True, default='0', comment='菜单状态（0显示 1隐藏）')
    status = Column(String(1), nullable=True, default='0', comment='菜单状态（0正常 1停用）')
    perms = Column(String(100), nullable=True, default=None, comment='权限标识')
    icon = Column(String(100), nullable=True, default='#', comment='菜单图标')
    create_by = Column(String(64), nullable=True, default='', comment='创建者')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64), nullable=True, default='', comment='更新者')
    update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
    remark = Column(String(500), nullable=True, default='', comment='备注')

dash-fastapi-backend/module_admin/entity/do/menu_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime, LargeBinary
from config.database import Base
from datetime import datetime


class SysNotice(Base):
    """
    通知公告表
    """
    __tablename__ = 'sys_notice'

    notice_id = Column(Integer, primary_key=True, autoincrement=True, comment='公告ID')
    notice_title = Column(String(50, collation='utf8_general_ci'), nullable=False, comment='公告标题')
    notice_type = Column(String(1, collation='utf8_general_ci'), nullable=False, comment='公告类型（1通知 2公告）')
    notice_content = Column(LargeBinary, comment='公告内容')
    status = Column(String(1, collation='utf8_general_ci'), default='0', comment='公告状态（0正常 1关闭）')
    create_by = Column(String(64, collation='utf8_general_ci'), default='', comment='创建者')
    create_time = Column(DateTime, comment='创建时间', default=datetime.now())
    update_by = Column(String(64, collation='utf8_general_ci'), default='', comment='更新者')
    update_time = Column(DateTime, comment='更新时间', default=datetime.now())
    remark = Column(String(255, collation='utf8_general_ci'), comment='备注')

dash-fastapi-backend/module_admin/entity/do/notice_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime
from config.database import Base
from datetime import datetime


class SysPost(Base):
    """
    岗位信息表
    """
    __tablename__ = 'sys_post'

    post_id = Column(Integer, primary_key=True, autoincrement=True, comment='岗位ID')
    post_code = Column(String(64), nullable=False, comment='岗位编码')
    post_name = Column(String(50), nullable=False, comment='岗位名称')
    post_sort = Column(Integer, nullable=False, comment='显示顺序')
    status = Column(String(1), nullable=False, default='0', comment='状态（0正常 1停用）')
    create_by = Column(String(64), default='', comment='创建者')
    create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64), default='', comment='更新者')
    update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
    remark = Column(String(500), nullable=True, default='', comment='备注')

dash-fastapi-backend/module_admin/entity/do/post_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime
from config.database import Base
from datetime import datetime


class SysRole(Base):
    """
    角色信息表
    """
    __tablename__ = 'sys_role'

    role_id = Column(Integer, primary_key=True, autoincrement=True, comment='角色ID')
    role_name = Column(String(30, collation='utf8_general_ci'), nullable=False, comment='角色名称')
    role_key = Column(String(100, collation='utf8_general_ci'), nullable=False, comment='角色权限字符串')
    role_sort = Column(Integer, nullable=False, comment='显示顺序')
    data_scope = Column(String(1, collation='utf8_general_ci'), default='1', comment='数据范围（1：全部数据权限 2：自定数据权限 3：本部门数据权限 4：本部门及以下数据权限）')
    menu_check_strictly = Column(Integer, default=1, comment='菜单树选择项是否关联显示')
    dept_check_strictly = Column(Integer, default=1, comment='部门树选择项是否关联显示')
    status = Column(String(1, collation='utf8_general_ci'), nullable=False, comment='角色状态（0正常 1停用）')
    del_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='删除标志（0代表存在 2代表删除）')
    create_by = Column(String(64, collation='utf8_general_ci'), default='', comment='创建者')
    create_time = Column(DateTime, default=datetime.now(), comment='创建时间')
    update_by = Column(String(64, collation='utf8_general_ci'), default='', comment='更新者')
    update_time = Column(DateTime, default=datetime.now(), comment='更新时间')
    remark = Column(String(500, collation='utf8_general_ci'), comment='备注')


class SysRoleDept(Base):
    """
    角色和部门关联表
    """
    __tablename__ = 'sys_role_dept'

    role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID')
    dept_id = Column(Integer, primary_key=True, nullable=False, comment='部门ID')


class SysRoleMenu(Base):
    """
    角色和菜单关联表
    """
    __tablename__ = 'sys_role_menu'

    role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID')
    menu_id = Column(Integer, primary_key=True, nullable=False, comment='菜单ID')

dash-fastapi-backend/module_admin/entity/do/role_do.py
insistence/Dash-FastAPI-Admin
from sqlalchemy import Column, Integer, String, DateTime
from config.database import Base
from datetime import datetime


class SysUser(Base):
    """
    用户信息表
    """
    __tablename__ = 'sys_user'

    user_id = Column(Integer, primary_key=True, autoincrement=True, comment='用户ID')
    dept_id = Column(Integer, comment='部门ID')
    user_name = Column(String(30, collation='utf8_general_ci'), nullable=False, comment='用户账号')
    nick_name = Column(String(30, collation='utf8_general_ci'), nullable=False, comment='用户昵称')
    user_type = Column(String(2, collation='utf8_general_ci'), default='00', comment='用户类型（00系统用户）')
    email = Column(String(50, collation='utf8_general_ci'), default='', comment='用户邮箱')
    phonenumber = Column(String(11, collation='utf8_general_ci'), default='', comment='手机号码')
    sex = Column(String(1, collation='utf8_general_ci'), default='0', comment='用户性别（0男 1女 2未知）')
    avatar = Column(String(100, collation='utf8_general_ci'), default='', comment='头像地址')
    password = Column(String(100, collation='utf8_general_ci'), default='', comment='密码')
    status = Column(String(1, collation='utf8_general_ci'), default='0', comment='帐号状态（0正常 1停用）')
    del_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='删除标志（0代表存在 2代表删除）')
    login_ip = Column(String(128, collation='utf8_general_ci'), default='', comment='最后登录IP')
    login_date = Column(DateTime, comment='最后登录时间')
    create_by = Column(String(64, collation='utf8_general_ci'), default='', comment='创建者')
    create_time = Column(DateTime, comment='创建时间', default=datetime.now())
    update_by = Column(String(64, collation='utf8_general_ci'), default='', comment='更新者')
    update_time = Column(DateTime, comment='更新时间', default=datetime.now())
    remark = Column(String(500, collation='utf8_general_ci'), comment='备注')


class SysUserRole(Base):
    """
    用户和角色关联表
    """
    __tablename__ = 'sys_user_role'

    user_id = Column(Integer, primary_key=True, nullable=False, comment='用户ID')
    role_id = Column(Integer, primary_key=True, nullable=False, comment='角色ID')


class SysUserPost(Base):
    """
    用户与岗位关联表
    """
    __tablename__ = 'sys_user_post'

    user_id = Column(Integer, primary_key=True, nullable=False, comment='用户ID')
    post_id = Column(Integer, primary_key=True, nullable=False, comment='岗位ID')

dash-fastapi-backend/module_admin/entity/do/user_do.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Optional, List


class CacheMonitorModel(BaseModel):
    """
    缓存监控信息对应pydantic模型
    """
    command_stats: Optional[List]
    db_size: Optional[int]
    info: Optional[dict]

dash-fastapi-backend/module_admin/entity/vo/cache_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class ConfigModel(BaseModel):
    """
    参数配置表对应pydantic模型
    """
    config_id: Optional[int]
    config_name: Optional[str]
    config_key: Optional[str]
    config_value: Optional[str]
    config_type: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class ConfigQueryModel(ConfigModel):
    """
    参数配置管理不分页查询模型
    """
    create_time_start: Optional[str]
    create_time_end: Optional[str]


class ConfigPageObject(ConfigQueryModel):
    """
    参数配置管理分页查询模型
    """
    page_num: int
    page_size: int


class ConfigPageObjectResponse(BaseModel):
    """
    参数配置管理列表分页查询返回模型
    """
    rows: List[Union[ConfigModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class DeleteConfigModel(BaseModel):
    """
    删除参数配置模型
    """
    config_ids: str


class CrudConfigResponse(BaseModel):
    """
    操作参数配置响应模型
    """
    is_success: bool
    message: str

dash-fastapi-backend/module_admin/entity/vo/config_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List
from module_admin.entity.vo.user_vo import DeptModel


class DeptPageObject(DeptModel):
    """
    部门管理分页查询模型
    """
    page_num: int
    page_size: int


class DeptPageObjectResponse(BaseModel):
    """
    用户管理列表分页查询返回模型
    """
    rows: List[Union[DeptModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class DeptResponse(BaseModel):
    """
    用户管理列表不分页查询返回模型
    """
    rows: List[Union[DeptModel, None]] = []


class DeptTree(BaseModel):
    """
    部门树响应模型
    """
    dept_tree: Union[List, None]


class CrudDeptResponse(BaseModel):
    """
    操作部门响应模型
    """
    is_success: bool
    message: str


class DeleteDeptModel(BaseModel):
    """
    删除部门模型
    """
    dept_ids: str
    update_by: Optional[str]
    update_time: Optional[str]

dash-fastapi-backend/module_admin/entity/vo/dept_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class DictTypeModel(BaseModel):
    """
    字典类型表对应pydantic模型
    """
    dict_id: Optional[int]
    dict_name: Optional[str]
    dict_type: Optional[str]
    status: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class DictDataModel(BaseModel):
    """
    字典数据表对应pydantic模型
    """
    dict_code: Optional[int]
    dict_sort: Optional[int]
    dict_label: Optional[str]
    dict_value: Optional[str]
    dict_type: Optional[str]
    css_class: Optional[str]
    list_class: Optional[str]
    is_default: Optional[str]
    status: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class DictTypeQueryModel(DictTypeModel):
    """
    字典类型管理不分页查询模型
    """
    create_time_start: Optional[str]
    create_time_end: Optional[str]


class DictTypePageObject(DictTypeQueryModel):
    """
    字典类型管理分页查询模型
    """
    page_num: int
    page_size: int


class DictTypePageObjectResponse(BaseModel):
    """
    字典类型管理列表分页查询返回模型
    """
    rows: List[Union[DictTypeModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class DeleteDictTypeModel(BaseModel):
    """
    删除字典类型模型
    """
    dict_ids: str


class DictDataPageObject(DictDataModel):
    """
    字典数据管理分页查询模型
    """
    page_num: int
    page_size: int


class DictDataPageObjectResponse(BaseModel):
    """
    字典数据管理列表分页查询返回模型
    """
    rows: List[Union[DictDataModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class DeleteDictDataModel(BaseModel):
    """
    删除字典数据模型
    """
    dict_codes: str


class CrudDictResponse(BaseModel):
    """
    操作字典响应模型
    """
    is_success: bool
    message: str

dash-fastapi-backend/module_admin/entity/vo/dict_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class JobModel(BaseModel):
    """
    定时任务调度表对应pydantic模型
    """
    job_id: Optional[int]
    job_name: Optional[str]
    job_group: Optional[str]
    job_executor: Optional[str]
    invoke_target: Optional[str]
    job_args: Optional[str]
    job_kwargs: Optional[str]
    cron_expression: Optional[str]
    misfire_policy: Optional[str]
    concurrent: Optional[str]
    status: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class JobLogModel(BaseModel):
    """
    定时任务调度日志表对应pydantic模型
    """
    job_log_id: Optional[int]
    job_name: Optional[str]
    job_group: Optional[str]
    job_executor: Optional[str]
    invoke_target: Optional[str]
    job_args: Optional[str]
    job_kwargs: Optional[str]
    job_trigger: Optional[str]
    job_message: Optional[str]
    status: Optional[str]
    exception_info: Optional[str]
    create_time: Optional[str]

    class Config:
        orm_mode = True


class JobPageObject(JobModel):
    """
    定时任务管理分页查询模型
    """
    page_num: int
    page_size: int


class JobPageObjectResponse(BaseModel):
    """
    定时任务管理列表分页查询返回模型
    """
    rows: List[Union[JobModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class CrudJobResponse(BaseModel):
    """
    操作定时任务及日志响应模型
    """
    is_success: bool
    message: str


class EditJobModel(JobModel):
    """
    编辑定时任务模型
    """
    type: Optional[str]


class DeleteJobModel(BaseModel):
    """
    删除定时任务模型
    """
    job_ids: str


class JobLogQueryModel(JobLogModel):
    """
    定时任务日志不分页查询模型
    """
    create_time_start: Optional[str]
    create_time_end: Optional[str]


class JobLogPageObject(JobLogQueryModel):
    """
    定时任务日志管理分页查询模型
    """
    page_num: int
    page_size: int


class JobLogPageObjectResponse(BaseModel):
    """
    定时任务日志管理列表分页查询返回模型
    """
    rows: List[Union[JobLogModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class DeleteJobLogModel(BaseModel):
    """
    删除定时任务日志模型
    """
    job_log_ids: str


class ClearJobLogModel(BaseModel):
    """
    清除定时任务日志模型
    """
    oper_type: str

dash-fastapi-backend/module_admin/entity/vo/job_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class OperLogModel(BaseModel):
    """
    操作日志表对应pydantic模型
    """
    oper_id: Optional[int]
    title: Optional[str]
    business_type: Optional[int]
    method: Optional[str]
    request_method: Optional[str]
    operator_type: Optional[int]
    oper_name: Optional[str]
    dept_name: Optional[str]
    oper_url: Optional[str]
    oper_ip: Optional[str]
    oper_location: Optional[str]
    oper_param: Optional[str]
    json_result: Optional[str]
    status: Optional[int]
    error_msg: Optional[str]
    oper_time: Optional[str]
    cost_time: Optional[int]

    class Config:
        orm_mode = True


class LogininforModel(BaseModel):
    """
    登录日志表对应pydantic模型
    """
    info_id: Optional[int]
    user_name: Optional[str]
    ipaddr: Optional[str]
    login_location: Optional[str]
    browser: Optional[str]
    os: Optional[str]
    status: Optional[str]
    msg: Optional[str]
    login_time: Optional[str]

    class Config:
        orm_mode = True


class OperLogQueryModel(OperLogModel):
    """
    操作日志管理不分页查询模型
    """
    oper_time_start: Optional[str]
    oper_time_end: Optional[str]


class OperLogPageObject(OperLogQueryModel):
    """
    操作日志管理分页查询模型
    """
    page_num: int
    page_size: int


class OperLogPageObjectResponse(BaseModel):
    """
    操作日志列表分页查询返回模型
    """
    rows: List[Union[OperLogModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class DeleteOperLogModel(BaseModel):
    """
    删除操作日志模型
    """
    oper_ids: str


class ClearOperLogModel(BaseModel):
    """
    清除操作日志模型
    """
    oper_type: str


class LoginLogQueryModel(LogininforModel):
    """
    登录日志管理不分页查询模型
    """
    login_time_start: Optional[str]
    login_time_end: Optional[str]


class LoginLogPageObject(LoginLogQueryModel):
    """
    登录日志管理分页查询模型
    """
    page_num: int
    page_size: int


class LoginLogPageObjectResponse(BaseModel):
    """
    登录日志列表分页查询返回模型
    """
    rows: List[Union[LogininforModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class DeleteLoginLogModel(BaseModel):
    """
    删除登录日志模型
    """
    info_ids: str


class ClearLoginLogModel(BaseModel):
    """
    清除登录日志模型
    """
    oper_type: str


class UnlockUser(BaseModel):
    """
    解锁用户模型
    """
    user_name: str


class CrudLogResponse(BaseModel):
    """
    操作各类日志响应模型
    """
    is_success: bool
    message: str

dash-fastapi-backend/module_admin/entity/vo/log_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Optional


class UserLogin(BaseModel):
    user_name: str
    password: str
    captcha: Optional[str]
    session_id: Optional[str]
    login_info: Optional[dict]
    captcha_enabled: Optional[bool]


class Token(BaseModel):
    access_token: str
    token_type: str


class SmsCode(BaseModel):
    is_success: Optional[bool]
    sms_code: str
    session_id: str
    message: Optional[str]

dash-fastapi-backend/module_admin/entity/vo/login_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class MenuModel(BaseModel):
    """
    菜单表对应pydantic模型
    """
    menu_id: Optional[int]
    menu_name: Optional[str]
    parent_id: Optional[int]
    order_num: Optional[int]
    path: Optional[str]
    component: Optional[str]
    query: Optional[str]
    is_frame: Optional[int]
    is_cache: Optional[int]
    menu_type: Optional[str]
    visible: Optional[str]
    status: Optional[str]
    perms: Optional[str]
    icon: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True
        
        
class MenuTreeModel(MenuModel):
    """
    菜单树查询模型
    """
    type: Optional[str]


class MenuPageObject(MenuModel):
    """
    菜单管理分页查询模型
    """
    page_num: int
    page_size: int


class MenuPageObjectResponse(BaseModel):
    """
    菜单管理列表分页查询返回模型
    """
    rows: List[Union[MenuModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class MenuResponse(BaseModel):
    """
    菜单管理列表不分页查询返回模型
    """
    rows: List[Union[MenuModel, None]] = []


class MenuTree(BaseModel):
    """
    菜单树响应模型
    """
    menu_tree: Union[List, None]


class CrudMenuResponse(BaseModel):
    """
    操作菜单响应模型
    """
    is_success: bool
    message: str


class DeleteMenuModel(BaseModel):
    """
    删除菜单模型
    """
    menu_ids: str

dash-fastapi-backend/module_admin/entity/vo/menu_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class NoticeModel(BaseModel):
    """
    通知公告表对应pydantic模型
    """
    notice_id: Optional[int]
    notice_title: Optional[str]
    notice_type: Optional[str]
    notice_content: Optional[bytes]
    status: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class NoticeQueryModel(NoticeModel):
    """
    通知公告管理不分页查询模型
    """
    create_time_start: Optional[str]
    create_time_end: Optional[str]


class NoticePageObject(NoticeQueryModel):
    """
    通知公告管理分页查询模型
    """
    page_num: int
    page_size: int


class NoticePageObjectResponse(BaseModel):
    """
    通知公告管理列表分页查询返回模型
    """
    rows: List[Union[NoticeModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class CrudNoticeResponse(BaseModel):
    """
    操作通知公告响应模型
    """
    is_success: bool
    message: str


class DeleteNoticeModel(BaseModel):
    """
    删除通知公告模型
    """
    notice_ids: str

dash-fastapi-backend/module_admin/entity/vo/notice_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class OnlineModel(BaseModel):
    """
    在线用户对应pydantic模型
    """
    session_id: Optional[str]
    user_name: Optional[str]
    dept_name: Optional[str]
    ipaddr: Optional[str]
    login_location: Optional[str]
    browser: Optional[str]
    os: Optional[str]
    login_time: Optional[str]


class OnlinePageObject(OnlineModel):
    """
    在线用户分页查询模型
    """
    page_num: int
    page_size: int


class OnlinePageObjectResponse(BaseModel):
    """
    在线用户列表分页查询返回模型
    """
    rows: List[Union[OnlineModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class CrudOnlineResponse(BaseModel):
    """
    操作在线用户响应模型
    """
    is_success: bool
    message: str


class DeleteOnlineModel(BaseModel):
    """
    强退在线用户模型
    """
    session_ids: str

dash-fastapi-backend/module_admin/entity/vo/online_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, List
from module_admin.entity.vo.user_vo import PostModel


class PostPageObject(PostModel):
    """
    岗位管理分页查询模型
    """
    page_num: int
    page_size: int


class PostPageObjectResponse(BaseModel):
    """
    岗位管理列表分页查询返回模型
    """
    rows: List[Union[PostModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class PostSelectOptionResponseModel(BaseModel):
    """
    岗位管理不分页查询模型
    """
    post: List[Union[PostModel, None]]


class CrudPostResponse(BaseModel):
    """
    操作岗位响应模型
    """
    is_success: bool
    message: str


class DeletePostModel(BaseModel):
    """
    删除岗位模型
    """
    post_ids: str

dash-fastapi-backend/module_admin/entity/vo/post_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List
from module_admin.entity.vo.user_vo import RoleModel
from module_admin.entity.vo.dept_vo import DeptModel
from module_admin.entity.vo.menu_vo import MenuModel


class RoleMenuModel(BaseModel):
    """
    角色和菜单关联表对应pydantic模型
    """
    role_id: Optional[int]
    menu_id: Optional[int]

    class Config:
        orm_mode = True


class RoleDeptModel(BaseModel):
    """
    角色和部门关联表对应pydantic模型
    """
    role_id: Optional[int]
    dept_id: Optional[int]

    class Config:
        orm_mode = True


class RoleQueryModel(RoleModel):
    """
    角色管理不分页查询模型
    """
    create_time_start: Optional[str]
    create_time_end: Optional[str]


class RolePageObject(RoleQueryModel):
    """
    角色管理分页查询模型
    """
    page_num: int
    page_size: int
    
    
class RolePageObjectResponse(BaseModel):
    """
    角色管理列表分页查询返回模型
    """
    rows: List[Union[RoleModel, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class RoleSelectOptionResponseModel(BaseModel):
    """
    角色管理不分页查询模型
    """
    role: List[Union[RoleModel, None]]
    
    
class CrudRoleResponse(BaseModel):
    """
    操作角色响应模型
    """
    is_success: bool
    message: str
    
    
class AddRoleModel(RoleModel):
    """
    新增角色模型
    """
    menu_id: Optional[str]
    type: Optional[str]


class RoleDataScopeModel(RoleModel):
    """
    角色数据权限模型
    """
    dept_id: Optional[str]


class DeleteRoleModel(BaseModel):
    """
    删除角色模型
    """
    role_ids: str
    update_by: Optional[str]
    update_time: Optional[str]
    
    
class RoleDetailModel(BaseModel):
    """
    获取角色详情信息响应模型
    """
    role: Union[RoleModel, None]
    menu: List[Union[MenuModel, None]]
    dept: List[Union[DeptModel, None]]

dash-fastapi-backend/module_admin/entity/vo/role_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Optional, List


class CpuInfo(BaseModel):
    cpu_num: Optional[int]
    used: Optional[str]
    sys: Optional[str]
    free: Optional[str]


class MemoryInfo(BaseModel):
    total: Optional[str]
    used: Optional[str]
    free: Optional[str]
    usage: Optional[str]


class SysInfo(BaseModel):
    computer_ip: Optional[str]
    computer_name: Optional[str]
    os_arch: Optional[str]
    os_name: Optional[str]


class PyInfo(BaseModel):
    name: Optional[str]
    version: Optional[str]
    start_time: Optional[str]
    run_time: Optional[str]
    home: Optional[str]
    project_dir: Optional[str]


class SysFiles(BaseModel):
    dir_name: Optional[str]
    sys_type_name: Optional[str]
    disk_name: Optional[str]
    total: Optional[str]
    used: Optional[str]
    free: Optional[str]
    usage: Optional[str]


class ServerMonitorModel(BaseModel):
    """
    服务监控对应pydantic模型
    """
    cpu: Optional[CpuInfo]
    py: Optional[PyInfo]
    mem: Optional[MemoryInfo]
    sys: Optional[SysInfo]
    sys_files: Optional[List[SysFiles]]

dash-fastapi-backend/module_admin/entity/vo/server_vo.py
insistence/Dash-FastAPI-Admin
from pydantic import BaseModel
from typing import Union, Optional, List


class TokenData(BaseModel):
    """
    token解析结果
    """
    user_id: Union[int, None] = None


class UserModel(BaseModel):
    """
    用户表对应pydantic模型
    """
    user_id: Optional[int]
    dept_id: Optional[int]
    user_name: Optional[str]
    nick_name: Optional[str]
    user_type: Optional[str]
    email: Optional[str]
    phonenumber: Optional[str]
    sex: Optional[str]
    avatar: Optional[str]
    password: Optional[str]
    status: Optional[str]
    del_flag: Optional[str]
    login_ip: Optional[str]
    login_date: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class UserRoleModel(BaseModel):
    """
    用户和角色关联表对应pydantic模型
    """
    user_id: Optional[int]
    role_id: Optional[int]

    class Config:
        orm_mode = True


class UserPostModel(BaseModel):
    """
    用户与岗位关联表对应pydantic模型
    """
    user_id: Optional[int]
    post_id: Optional[int]

    class Config:
        orm_mode = True


class DeptModel(BaseModel):
    """
    部门表对应pydantic模型
    """
    dept_id: Optional[int]
    parent_id: Optional[int]
    ancestors: Optional[str]
    dept_name: Optional[str]
    order_num: Optional[int]
    leader: Optional[str]
    phone: Optional[str]
    email: Optional[str]
    status: Optional[str]
    del_flag: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]

    class Config:
        orm_mode = True


class RoleModel(BaseModel):
    """
    角色表对应pydantic模型
    """
    role_id: Optional[int]
    role_name: Optional[str]
    role_key: Optional[str]
    role_sort: Optional[int]
    data_scope: Optional[str]
    menu_check_strictly: Optional[int]
    dept_check_strictly: Optional[int]
    status: Optional[str]
    del_flag: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class PostModel(BaseModel):
    """
    岗位信息表对应pydantic模型
    """
    post_id: Optional[int]
    post_code: Optional[str]
    post_name: Optional[str]
    post_sort: Optional[int]
    status: Optional[str]
    create_by: Optional[str]
    create_time: Optional[str]
    update_by: Optional[str]
    update_time: Optional[str]
    remark: Optional[str]

    class Config:
        orm_mode = True


class CurrentUserInfo(BaseModel):
    """
    数据库返回当前用户信息
    """
    user_basic_info: List[Union[UserModel, None]]
    user_dept_info: List[Union[DeptModel, None]]
    user_role_info: List[Union[RoleModel, None]]
    user_post_info: List[Union[PostModel, None]]
    user_menu_info: Union[List, None]


class UserDetailModel(BaseModel):
    """
    获取用户详情信息响应模型
    """
    user: Union[UserModel, None]
    dept: Union[DeptModel, None]
    role: List[Union[RoleModel, None]]
    post: List[Union[PostModel, None]]


class CurrentUserInfoServiceResponse(UserDetailModel):
    """
    获取当前用户信息响应模型
    """
    menu: Union[List, None]


class UserQueryModel(UserModel):
    """
    用户管理不分页查询模型
    """
    create_time_start: Optional[str]
    create_time_end: Optional[str]


class UserPageObject(UserQueryModel):
    """
    用户管理分页查询模型
    """
    page_num: int
    page_size: int


class UserInfoJoinDept(UserModel):
    """
    数据库查询用户列表返回模型
    """
    dept_name: Optional[str]


class UserPageObjectResponse(BaseModel):
    """
    用户管理列表分页查询返回模型
    """
    rows: List[Union[UserInfoJoinDept, None]] = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


class AddUserModel(UserModel):
    """
    新增用户模型
    """
    role_id: Optional[str]
    post_id: Optional[str]
    type: Optional[str]


class ResetUserModel(UserModel):
    """
    重置用户密码模型
    """
    old_password: Optional[str]
    sms_code: Optional[str]
    session_id: Optional[str]


class DeleteUserModel(BaseModel):
    """
    删除用户模型
    """
    user_ids: str
    update_by: Optional[str]
    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):
    """
    操作用户响应模型
    """
    is_success: bool
    message: str


class DeptInfo(BaseModel):
    """
    查询部门树
    """
    dept_id: int
    dept_name: str
    ancestors: str


class RoleInfo(BaseModel):
    """
    用户角色信息
    """
    role_info: Union[List, None]


class MenuList(BaseModel):
    """
    用户菜单信息
    """
    menu_info: Union[List, None]

dash-fastapi-backend/module_admin/entity/vo/user_vo.py
insistence/Dash-FastAPI-Admin
from fastapi import Request
from module_admin.entity.vo.cache_vo import *


class CacheService:
    """
    缓存监控模块服务层
    """

    @classmethod
    async def get_cache_monitor_statistical_info_services(cls, request: Request):
        """
        获取缓存监控信息service
        :param request: Request对象
        :return: 缓存监控信息
        """
        info = await request.app.state.redis.info()
        db_size = await request.app.state.redis.dbsize()
        command_stats_dict = await request.app.state.redis.info('commandstats')
        command_stats = [dict(name=key.split('_')[1], value=str(value.get('calls'))) for key, value in
                         command_stats_dict.items()]
        result = dict(command_stats=command_stats, db_size=db_size, info=info)

        return CacheMonitorModel(**result)

dash-fastapi-backend/module_admin/service/cache_service.py
insistence/Dash-FastAPI-Admin
from PIL import Image, ImageDraw, ImageFont
import io
import os
import random
import base64


class CaptchaService:
    """
    验证码模块服务层
    """

    @classmethod
    def create_captcha_image_service(cls):
        # 创建空白图像
        image = Image.new('RGB', (400, 300), color='#EAEAEA')

        # 创建绘图对象
        draw = ImageDraw.Draw(image)

        # 设置字体
        font = ImageFont.truetype(os.path.join(os.path.abspath(os.getcwd()), 'assets', 'font', 'Arial.ttf'), size=100)

        # 生成两个0-9之间的随机整数
        num1 = random.randint(0, 9)
        num2 = random.randint(0, 9)
        # 从运算符列表中随机选择一个
        operational_character_list = ['+', '-', '*']
        operational_character = random.choice(operational_character_list)
        # 根据选择的运算符进行计算
        if operational_character == '+':
            result = num1 + num2
        elif operational_character == '-':
            result = num1 - num2
        else:
            result = num1 * num2
        # 绘制文本
        text = f"{num1} {operational_character} {num2} = ?"
        draw.text((10, 120), text, fill='blue', font=font)

        # 将图像数据保存到内存中
        buffer = io.BytesIO()
        image.save(buffer, format='PNG')

        # 将图像数据转换为base64字符串
        base64_string = f'data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode()}'

        return [base64_string, result]

dash-fastapi-backend/module_admin/service/captcha_service.py
insistence/Dash-FastAPI-Admin
import os
from fastapi import UploadFile


class CommonService:
    """
    通用模块服务层
    """

    @classmethod
    def upload_service(cls, path: str, upload_id: str, file: UploadFile):

        filepath = os.path.join(path, upload_id, f'{file.filename}')
        with open(filepath, 'wb') as f:
            # 流式写出大型文件，这里的10代表10MB
            for chunk in iter(lambda: file.file.read(1024 * 1024 * 10), b''):
                f.write(chunk)


dash-fastapi-backend/module_admin/service/common_service.py
insistence/Dash-FastAPI-Admin
from fastapi import Request
from module_admin.entity.vo.config_vo import *
from module_admin.dao.config_dao import *
from utils.common_util import export_list2excel


class ConfigService:
    """
    参数配置管理模块服务层
    """

    @classmethod
    def get_config_list_services(cls, result_db: Session, query_object: ConfigQueryModel):
        """
        获取参数配置列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 参数配置列表信息对象
        """
        config_list_result = ConfigDao.get_config_list(result_db, query_object)

        return config_list_result

    @classmethod
    async def init_cache_sys_config_services(cls, result_db: Session, redis):
        """
        应用初始化：获取所有参数配置对应的键值对信息并缓存service
        :param result_db: orm对象
        :param redis: redis对象
        :return:
        """
        # 获取以sys_config:开头的键列表
        keys = await redis.keys('sys_config:*')
        # 删除匹配的键
        if keys:
            await redis.delete(*keys)
        config_all = ConfigDao.get_config_list(result_db, ConfigQueryModel(**dict()))
        for config_obj in config_all:
            if config_obj.config_type == 'Y':
                await redis.set(f'sys_config:{config_obj.config_key}', config_obj.config_value)

    @classmethod
    async def query_config_list_from_cache_services(cls, redis, config_key: str):
        """
        从缓存获取参数键名对应值service
        :param redis: redis对象
        :param config_key: 参数键名
        :return: 参数键名对应值
        """
        result = await redis.get(f'sys_config:{config_key}')

        return result

    @classmethod
    async def add_config_services(cls, request: Request, result_db: Session, page_object: ConfigModel):
        """
        新增参数配置信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 新增参数配置对象
        :return: 新增参数配置校验结果
        """
        config = ConfigDao.get_config_detail_by_info(result_db, ConfigModel(**dict(config_key=page_object.config_key)))
        if config:
            result = dict(is_success=False, message='参数键名已存在')
        else:
            try:
                ConfigDao.add_config_dao(result_db, page_object)
                result_db.commit()
                await cls.init_cache_sys_config_services(result_db, request.app.state.redis)
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudConfigResponse(**result)

    @classmethod
    async def edit_config_services(cls, request: Request, result_db: Session, page_object: ConfigModel):
        """
        编辑参数配置信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 编辑参数配置对象
        :return: 编辑参数配置校验结果
        """
        edit_config = page_object.dict(exclude_unset=True)
        config_info = cls.detail_config_services(result_db, edit_config.get('config_id'))
        if config_info:
            if config_info.config_key != page_object.config_key or config_info.config_value != page_object.config_value:
                config = ConfigDao.get_config_detail_by_info(result_db, page_object)
                if config:
                    result = dict(is_success=False, message='参数配置已存在')
                    return CrudConfigResponse(**result)
            try:
                ConfigDao.edit_config_dao(result_db, edit_config)
                result_db.commit()
                await cls.init_cache_sys_config_services(result_db, request.app.state.redis)
                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='参数配置不存在')

        return CrudConfigResponse(**result)

    @classmethod
    async def delete_config_services(cls, request: Request, result_db: Session, page_object: DeleteConfigModel):
        """
        删除参数配置信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 删除参数配置对象
        :return: 删除参数配置校验结果
        """
        if page_object.config_ids.split(','):
            config_id_list = page_object.config_ids.split(',')
            try:
                for config_id in config_id_list:
                    config_id_dict = dict(config_id=config_id)
                    ConfigDao.delete_config_dao(result_db, ConfigModel(**config_id_dict))
                result_db.commit()
                await cls.init_cache_sys_config_services(result_db, request.app.state.redis)
                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='传入字典数据id为空')
        return CrudConfigResponse(**result)

    @classmethod
    def detail_config_services(cls, result_db: Session, config_id: int):
        """
        获取参数配置详细信息service
        :param result_db: orm对象
        :param config_id: 参数配置id
        :return: 参数配置id对应的信息
        """
        config = ConfigDao.get_config_detail_by_id(result_db, config_id=config_id)

        return config

    @staticmethod
    def export_config_list_services(config_list: List):
        """
        导出参数配置信息service
        :param config_list: 参数配置信息列表
        :return: 参数配置信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "config_id": "参数主键",
            "config_name": "参数名称",
            "config_key": "参数键名",
            "config_value": "参数键值",
            "config_type": "系统内置",
            "create_by": "创建者",
            "create_time": "创建时间",
            "update_by": "更新者",
            "update_time": "更新时间",
            "remark": "备注",
        }

        data = [ConfigModel(**vars(row)).dict() for row in config_list]

        for item in data:
            if item.get('config_type') == 'Y':
                item['config_type'] = '是'
            else:
                item['config_type'] = '否'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data

    @classmethod
    async def refresh_sys_config_services(cls, request: Request, result_db: Session):
        """
        刷新字典缓存信息service
        :param request: Request对象
        :param result_db: orm对象
        :return: 刷新字典缓存校验结果
        """
        await cls.init_cache_sys_config_services(result_db, request.app.state.redis)
        result = dict(is_success=True, message='刷新成功')

        return CrudConfigResponse(**result)

dash-fastapi-backend/module_admin/service/config_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.dept_vo import *
from module_admin.dao.dept_dao import *


class DeptService:
    """
    部门管理模块服务层
    """

    @classmethod
    def get_dept_tree_services(cls, result_db: Session, page_object: DeptModel):
        """
        获取部门树信息service
        :param result_db: orm对象
        :param page_object: 查询参数对象
        :return: 部门树信息对象
        """
        dept_list_result = DeptDao.get_dept_list_for_tree(result_db, page_object)
        dept_tree_result = cls.get_dept_tree(0, DeptTree(dept_tree=dept_list_result))

        return dept_tree_result

    @classmethod
    def get_dept_tree_for_edit_option_services(cls, result_db: Session, page_object: DeptModel):
        """
        获取部门编辑部门树信息service
        :param result_db: orm对象
        :param page_object: 查询参数对象
        :return: 部门树信息对象
        """
        dept_list_result = DeptDao.get_dept_info_for_edit_option(result_db, page_object)
        dept_tree_result = cls.get_dept_tree(0, DeptTree(dept_tree=dept_list_result))

        return dept_tree_result

    @classmethod
    def get_dept_list_services(cls, result_db: Session, page_object: DeptModel):
        """
        获取部门列表信息service
        :param result_db: orm对象
        :param page_object: 分页查询参数对象
        :return: 部门列表信息对象
        """
        dept_list_result = DeptDao.get_dept_list(result_db, page_object)

        return dept_list_result

    @classmethod
    def add_dept_services(cls, result_db: Session, page_object: DeptModel):
        """
        新增部门信息service
        :param result_db: orm对象
        :param page_object: 新增部门对象
        :return: 新增部门校验结果
        """
        parent_info = DeptDao.get_dept_by_id(result_db, page_object.parent_id)
        if parent_info:
            page_object.ancestors = f'{parent_info.ancestors},{page_object.parent_id}'
        else:
            page_object.ancestors = '0'
        dept = DeptDao.get_dept_detail_by_info(result_db, DeptModel(**dict(parent_id=page_object.parent_id, dept_name=page_object.dept_name)))
        if dept:
            result = dict(is_success=False, message='同一部门下不允许存在同名的部门')
        else:
            try:
                DeptDao.add_dept_dao(result_db, page_object)
                result_db.commit()
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudDeptResponse(**result)

    @classmethod
    def edit_dept_services(cls, result_db: Session, page_object: DeptModel):
        """
        编辑部门信息service
        :param result_db: orm对象
        :param page_object: 编辑部门对象
        :return: 编辑部门校验结果
        """
        parent_info = DeptDao.get_dept_by_id(result_db, page_object.parent_id)
        if parent_info:
            page_object.ancestors = f'{parent_info.ancestors},{page_object.parent_id}'
        else:
            page_object.ancestors = '0'
        edit_dept = page_object.dict(exclude_unset=True)
        dept_info = cls.detail_dept_services(result_db, edit_dept.get('dept_id'))
        if dept_info:
            if dept_info.parent_id != page_object.parent_id or dept_info.dept_name != page_object.dept_name:
                dept = DeptDao.get_dept_detail_by_info(result_db, DeptModel(
                    **dict(parent_id=page_object.parent_id, dept_name=page_object.dept_name)))
                if dept:
                    result = dict(is_success=False, message='同一部门下不允许存在同名的部门')
                    return CrudDeptResponse(**result)
            try:
                DeptDao.edit_dept_dao(result_db, edit_dept)
                cls.update_children_info(result_db, DeptModel(dept_id=page_object.dept_id,
                                                              ancestors=page_object.ancestors,
                                                              update_by=page_object.update_by,
                                                              update_time=page_object.update_time
                                                              )
                                         )
                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='部门不存在')

        return CrudDeptResponse(**result)

    @classmethod
    def delete_dept_services(cls, result_db: Session, page_object: DeleteDeptModel):
        """
        删除部门信息service
        :param result_db: orm对象
        :param page_object: 删除部门对象
        :return: 删除部门校验结果
        """
        if page_object.dept_ids.split(','):
            dept_id_list = page_object.dept_ids.split(',')
            ancestors = DeptDao.get_dept_all_ancestors(result_db)
            try:
                for dept_id in dept_id_list:
                    for ancestor in ancestors:
                        if dept_id in ancestor[0]:
                            result = dict(is_success=False, message='该部门下有子部门，不允许删除')

                            return CrudDeptResponse(**result)

                    dept_id_dict = dict(dept_id=dept_id)
                    DeptDao.delete_dept_dao(result_db, DeptModel(**dept_id_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='传入部门id为空')
        return CrudDeptResponse(**result)

    @classmethod
    def detail_dept_services(cls, result_db: Session, dept_id: int):
        """
        获取部门详细信息service
        :param result_db: orm对象
        :param dept_id: 部门id
        :return: 部门id对应的信息
        """
        dept = DeptDao.get_dept_detail_by_id(result_db, dept_id=dept_id)

        return dept

    @classmethod
    def get_dept_tree(cls, pid: int, permission_list: DeptTree):
        """
        工具方法：根据部门信息生成树形嵌套数据
        :param pid: 部门id
        :param permission_list: 部门列表信息
        :return: 部门树形嵌套数据
        """
        dept_list = []
        for permission in permission_list.dept_tree:
            if permission.parent_id == pid:
                children = cls.get_dept_tree(permission.dept_id, permission_list)
                dept_list_data = {}
                if children:
                    dept_list_data['title'] = permission.dept_name
                    dept_list_data['key'] = str(permission.dept_id)
                    dept_list_data['value'] = str(permission.dept_id)
                    dept_list_data['children'] = children
                else:
                    dept_list_data['title'] = permission.dept_name
                    dept_list_data['key'] = str(permission.dept_id)
                    dept_list_data['value'] = str(permission.dept_id)
                dept_list.append(dept_list_data)

        return dept_list

    @classmethod
    def update_children_info(cls, result_db, page_object):
        """
        工具方法：递归更新子部门信息
        :param result_db: orm对象
        :param page_object: 编辑部门对象
        :return:
        """
        children_info = DeptDao.get_children_dept(result_db, page_object.dept_id)
        if children_info:
            for child in children_info:
                child.ancestors = f'{page_object.ancestors},{page_object.dept_id}'
                DeptDao.edit_dept_dao(result_db,
                                      dict(dept_id=child.dept_id,
                                           ancestors=child.ancestors,
                                           update_by=page_object.update_by,
                                           update_time=page_object.update_time
                                           )
                                      )
                cls.update_children_info(result_db, DeptModel(dept_id=child.dept_id,
                                                              ancestors=child.ancestors,
                                                              update_by=page_object.update_by,
                                                              update_time=page_object.update_time
                                                              ))

dash-fastapi-backend/module_admin/service/dept_service.py
insistence/Dash-FastAPI-Admin
from fastapi import Request
import json
from module_admin.entity.vo.dict_vo import *
from module_admin.dao.dict_dao import *
from utils.common_util import export_list2excel


class DictTypeService:
    """
    字典类型管理模块服务层
    """

    @classmethod
    def get_dict_type_list_services(cls, result_db: Session, query_object: DictTypeQueryModel):
        """
        获取字典类型列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 字典类型列表信息对象
        """
        dict_type_list_result = DictTypeDao.get_dict_type_list(result_db, query_object)

        return dict_type_list_result

    @classmethod
    def get_all_dict_type_services(cls, result_db: Session):
        """
        获取字所有典类型列表信息service
        :param result_db: orm对象
        :return: 字典类型列表信息对象
        """
        dict_type_list_result = DictTypeDao.get_all_dict_type(result_db)

        return dict_type_list_result

    @classmethod
    async def add_dict_type_services(cls, request: Request, result_db: Session, page_object: DictTypeModel):
        """
        新增字典类型信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 新增岗位对象
        :return: 新增字典类型校验结果
        """
        dict_type = DictTypeDao.get_dict_type_detail_by_info(result_db, DictTypeModel(**dict(dict_type=page_object.dict_type)))
        if dict_type:
            result = dict(is_success=False, message='字典类型已存在')
        else:
            try:
                DictTypeDao.add_dict_type_dao(result_db, page_object)
                result_db.commit()
                await DictDataService.init_cache_sys_dict_services(result_db, request.app.state.redis)
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudDictResponse(**result)

    @classmethod
    async def edit_dict_type_services(cls, request: Request, result_db: Session, page_object: DictTypeModel):
        """
        编辑字典类型信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 编辑字典类型对象
        :return: 编辑字典类型校验结果
        """
        edit_dict_type = page_object.dict(exclude_unset=True)
        dict_type_info = cls.detail_dict_type_services(result_db, edit_dict_type.get('dict_id'))
        if dict_type_info:
            if dict_type_info.dict_type != page_object.dict_type or dict_type_info.dict_name != page_object.dict_name:
                dict_type = DictTypeDao.get_dict_type_detail_by_info(result_db, DictTypeModel(
                    **dict(dict_type=page_object.dict_type)))
                if dict_type:
                    result = dict(is_success=False, message='字典类型已存在')
                    return CrudDictResponse(**result)
            try:
                if dict_type_info.dict_type != page_object.dict_type:
                    query_dict_data = DictDataModel(**(dict(dict_type=dict_type_info.dict_type)))
                    dict_data_list = DictDataDao.get_dict_data_list(result_db, query_dict_data)
                    for dict_data in dict_data_list:
                        edit_dict_data = DictDataModel(**(dict(dict_code=dict_data.dict_code, dict_type=page_object.dict_type, update_by=page_object.update_by))).dict(exclude_unset=True)
                        DictDataDao.edit_dict_data_dao(result_db, edit_dict_data)
                DictTypeDao.edit_dict_type_dao(result_db, edit_dict_type)
                result_db.commit()
                await DictDataService.init_cache_sys_dict_services(result_db, request.app.state.redis)
                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='字典类型不存在')

        return CrudDictResponse(**result)

    @classmethod
    async def delete_dict_type_services(cls, request: Request, result_db: Session, page_object: DeleteDictTypeModel):
        """
        删除字典类型信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 删除字典类型对象
        :return: 删除字典类型校验结果
        """
        if page_object.dict_ids.split(','):
            dict_id_list = page_object.dict_ids.split(',')
            try:
                for dict_id in dict_id_list:
                    dict_id_dict = dict(dict_id=dict_id)
                    DictTypeDao.delete_dict_type_dao(result_db, DictTypeModel(**dict_id_dict))
                result_db.commit()
                await DictDataService.init_cache_sys_dict_services(result_db, request.app.state.redis)
                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='传入字典类型id为空')
        return CrudDictResponse(**result)

    @classmethod
    def detail_dict_type_services(cls, result_db: Session, dict_id: int):
        """
        获取字典类型详细信息service
        :param result_db: orm对象
        :param dict_id: 字典类型id
        :return: 字典类型id对应的信息
        """
        dict_type = DictTypeDao.get_dict_type_detail_by_id(result_db, dict_id=dict_id)

        return dict_type

    @staticmethod
    def export_dict_type_list_services(dict_type_list: List):
        """
        导出字典类型信息service
        :param dict_type_list: 字典信息列表
        :return: 字典信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "dict_id": "字典编号",
            "dict_name": "字典名称",
            "dict_type": "字典类型",
            "status": "状态",
            "create_by": "创建者",
            "create_time": "创建时间",
            "update_by": "更新者",
            "update_time": "更新时间",
            "remark": "备注",
        }

        data = [DictTypeModel(**vars(row)).dict() for row in dict_type_list]

        for item in data:
            if item.get('status') == '0':
                item['status'] = '正常'
            else:
                item['status'] = '停用'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data

    @classmethod
    async def refresh_sys_dict_services(cls, request: Request, result_db: Session):
        """
        刷新字典缓存信息service
        :param request: Request对象
        :param result_db: orm对象
        :return: 刷新字典缓存校验结果
        """
        await DictDataService.init_cache_sys_dict_services(result_db, request.app.state.redis)
        result = dict(is_success=True, message='刷新成功')

        return CrudDictResponse(**result)


class DictDataService:
    """
    字典数据管理模块服务层
    """

    @classmethod
    def get_dict_data_list_services(cls, result_db: Session, query_object: DictDataModel):
        """
        获取字典数据列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 字典数据列表信息对象
        """
        dict_data_list_result = DictDataDao.get_dict_data_list(result_db, query_object)

        return dict_data_list_result

    @classmethod
    def query_dict_data_list_services(cls, result_db: Session, dict_type: str):
        """
        获取字典数据列表信息service
        :param result_db: orm对象
        :param dict_type: 字典类型
        :return: 字典数据列表信息对象
        """
        dict_data_list_result = DictDataDao.query_dict_data_list(result_db, dict_type)

        return dict_data_list_result

    @classmethod
    async def init_cache_sys_dict_services(cls, result_db: Session, redis):
        """
        应用初始化：获取所有字典类型对应的字典数据信息并缓存service
        :param result_db: orm对象
        :param redis: redis对象
        :return:
        """
        # 获取以sys_dict:开头的键列表
        keys = await redis.keys('sys_dict:*')
        # 删除匹配的键
        if keys:
            await redis.delete(*keys)
        dict_type_all = DictTypeDao.get_all_dict_type(result_db)
        for dict_type_obj in [item for item in dict_type_all if item.status == '0']:
            dict_type = dict_type_obj.dict_type
            dict_data_list = DictDataDao.query_dict_data_list(result_db, dict_type)
            dict_data = [DictDataModel(**vars(row)).dict() for row in dict_data_list if row]
            await redis.set(f'sys_dict:{dict_type}', json.dumps(dict_data, ensure_ascii=False))

    @classmethod
    async def query_dict_data_list_from_cache_services(cls, redis, dict_type: str):
        """
        从缓存获取字典数据列表信息service
        :param redis: redis对象
        :param dict_type: 字典类型
        :return: 字典数据列表信息对象
        """
        result = []
        dict_data_list_result = await redis.get(f'sys_dict:{dict_type}')
        if dict_data_list_result:
            result = json.loads(dict_data_list_result)

        return result

    @classmethod
    async def add_dict_data_services(cls, request: Request, result_db: Session, page_object: DictDataModel):
        """
        新增字典数据信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 新增岗位对象
        :return: 新增字典数据校验结果
        """
        dict_data = DictDataDao.get_dict_data_detail_by_info(result_db, page_object)
        if dict_data:
            result = dict(is_success=False, message='字典数据已存在')
        else:
            try:
                DictDataDao.add_dict_data_dao(result_db, page_object)
                result_db.commit()
                await cls.init_cache_sys_dict_services(result_db, request.app.state.redis)
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudDictResponse(**result)

    @classmethod
    async def edit_dict_data_services(cls, request: Request, result_db: Session, page_object: DictDataModel):
        """
        编辑字典数据信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 编辑字典数据对象
        :return: 编辑字典数据校验结果
        """
        edit_data_type = page_object.dict(exclude_unset=True)
        dict_data_info = cls.detail_dict_data_services(result_db, edit_data_type.get('dict_code'))
        if dict_data_info:
            if dict_data_info.dict_type != page_object.dict_type or dict_data_info.dict_label != page_object.dict_label or dict_data_info.dict_value != page_object.dict_value:
                dict_data = DictDataDao.get_dict_data_detail_by_info(result_db, page_object)
                if dict_data:
                    result = dict(is_success=False, message='字典数据已存在')
                    return CrudDictResponse(**result)
            try:
                DictDataDao.edit_dict_data_dao(result_db, edit_data_type)
                result_db.commit()
                await cls.init_cache_sys_dict_services(result_db, request.app.state.redis)
                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='字典数据不存在')

        return CrudDictResponse(**result)

    @classmethod
    async def delete_dict_data_services(cls, request: Request, result_db: Session, page_object: DeleteDictDataModel):
        """
        删除字典数据信息service
        :param request: Request对象
        :param result_db: orm对象
        :param page_object: 删除字典数据对象
        :return: 删除字典数据校验结果
        """
        if page_object.dict_codes.split(','):
            dict_code_list = page_object.dict_codes.split(',')
            try:
                for dict_code in dict_code_list:
                    dict_code_dict = dict(dict_code=dict_code)
                    DictDataDao.delete_dict_data_dao(result_db, DictDataModel(**dict_code_dict))
                result_db.commit()
                await cls.init_cache_sys_dict_services(result_db, request.app.state.redis)
                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='传入字典数据id为空')
        return CrudDictResponse(**result)

    @classmethod
    def detail_dict_data_services(cls, result_db: Session, dict_code: int):
        """
        获取字典数据详细信息service
        :param result_db: orm对象
        :param dict_code: 字典数据id
        :return: 字典数据id对应的信息
        """
        dict_data = DictDataDao.get_dict_data_detail_by_id(result_db, dict_code=dict_code)

        return dict_data

    @staticmethod
    def export_dict_data_list_services(dict_data_list: List):
        """
        导出字典数据信息service
        :param dict_data_list: 字典数据信息列表
        :return: 字典数据信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "dict_code": "字典编码",
            "dict_sort": "字典标签",
            "dict_label": "字典键值",
            "dict_value": "字典排序",
            "dict_type": "字典类型",
            "css_class": "样式属性",
            "list_class": "表格回显样式",
            "is_default": "是否默认",
            "status": "状态",
            "create_by": "创建者",
            "create_time": "创建时间",
            "update_by": "更新者",
            "update_time": "更新时间",
            "remark": "备注",
        }

        data = [DictDataModel(**vars(row)).dict() for row in dict_data_list]

        for item in data:
            if item.get('status') == '0':
                item['status'] = '正常'
            else:
                item['status'] = '停用'
            if item.get('is_default') == 'Y':
                item['is_default'] = '是'
            else:
                item['is_default'] = '否'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data

dash-fastapi-backend/module_admin/service/dict_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.job_vo import *
from module_admin.dao.job_log_dao import *
from module_admin.dao.dict_dao import DictDataDao
from utils.common_util import export_list2excel


class JobLogService:
    """
    定时任务日志管理模块服务层
    """

    @classmethod
    def get_job_log_list_services(cls, result_db: Session, query_object: JobLogQueryModel):
        """
        获取定时任务日志列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 定时任务日志列表信息对象
        """
        job_log_list_result = JobLogDao.get_job_log_list(result_db, query_object)

        return job_log_list_result

    @classmethod
    def add_job_log_services(cls, result_db: Session, page_object: JobLogModel):
        """
        新增定时任务日志信息service
        :param result_db: orm对象
        :param page_object: 新增定时任务日志对象
        :return: 新增定时任务日志校验结果
        """
        try:
            JobLogDao.add_job_log_dao(result_db, page_object)
            result_db.commit()
            result = dict(is_success=True, message='新增成功')
        except Exception as e:
            result_db.rollback()
            result = dict(is_success=False, message=str(e))

        return CrudJobResponse(**result)

    @classmethod
    def delete_job_log_services(cls, result_db: Session, page_object: DeleteJobLogModel):
        """
        删除定时任务日志信息service
        :param result_db: orm对象
        :param page_object: 删除定时任务日志对象
        :return: 删除定时任务日志校验结果
        """
        if page_object.job_log_ids.split(','):
            job_log_id_list = page_object.job_log_ids.split(',')
            try:
                for job_log_id in job_log_id_list:
                    job_log_id_dict = dict(job_log_id=job_log_id)
                    JobLogDao.delete_job_log_dao(result_db, JobLogModel(**job_log_id_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='传入定时任务日志id为空')
        return CrudJobResponse(**result)

    @classmethod
    def detail_job_log_services(cls, result_db: Session, job_log_id: int):
        """
        获取定时任务日志详细信息service
        :param result_db: orm对象
        :param job_log_id: 定时任务日志id
        :return: 定时任务日志id对应的信息
        """
        job_log = JobLogDao.get_job_log_detail_by_id(result_db, job_log_id=job_log_id)

        return job_log

    @classmethod
    def clear_job_log_services(cls, result_db: Session, page_object: ClearJobLogModel):
        """
        清除定时任务日志信息service
        :param result_db: orm对象
        :param page_object: 清除定时任务日志对象
        :return: 清除定时任务日志校验结果
        """
        if page_object.oper_type == 'clear':
            try:
                JobLogDao.clear_job_log_dao(result_db)
                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='清除标识不合法')

        return CrudJobResponse(**result)

    @staticmethod
    def export_job_log_list_services(result_db, job_log_list: List):
        """
        导出定时任务日志信息service
        :param result_db: orm对象
        :param job_log_list: 定时任务日志信息列表
        :return: 定时任务日志信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "job_log_id": "任务日志编码",
            "job_name": "任务名称",
            "job_group": "任务组名",
            "job_executor": "任务执行器",
            "invoke_target": "调用目标字符串",
            "job_args": "位置参数",
            "job_kwargs": "关键字参数",
            "job_trigger": "任务触发器",
            "job_message": "日志信息",
            "status": "执行状态",
            "exception_info": "异常信息",
            "create_time": "创建时间",
        }

        data = [JobLogModel(**vars(row)).dict() for row in job_log_list]
        job_group_list = DictDataDao.query_dict_data_list(result_db, dict_type='sys_job_group')
        job_group_option = [dict(label=item.dict_label, value=item.dict_value) for item in job_group_list]
        job_group_option_dict = {item.get('value'): item for item in job_group_option}

        for item in data:
            if item.get('status') == '0':
                item['status'] = '正常'
            else:
                item['status'] = '暂停'
            if str(item.get('job_group')) in job_group_option_dict.keys():
                item['job_group'] = job_group_option_dict.get(str(item.get('job_group'))).get('label')
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in
                    data]
        binary_data = export_list2excel(new_data)

        return binary_data

dash-fastapi-backend/module_admin/service/job_log_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.job_vo import *
from module_admin.dao.job_dao import *
from module_admin.service.dict_service import Request, DictDataService
from utils.common_util import export_list2excel
from config.get_scheduler import SchedulerUtil


class JobService:
    """
    定时任务管理模块服务层
    """

    @classmethod
    def get_job_list_services(cls, result_db: Session, query_object: JobModel):
        """
        获取定时任务列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 定时任务列表信息对象
        """
        job_list_result = JobDao.get_job_list(result_db, query_object)

        return job_list_result

    @classmethod
    def add_job_services(cls, result_db: Session, page_object: JobModel):
        """
        新增定时任务信息service
        :param result_db: orm对象
        :param page_object: 新增定时任务对象
        :return: 新增定时任务校验结果
        """
        job = JobDao.get_job_detail_by_info(result_db, page_object)
        if job:
            result = dict(is_success=False, message='定时任务已存在')
        else:
            try:
                JobDao.add_job_dao(result_db, page_object)
                job_info = JobDao.get_job_detail_by_info(result_db, page_object)
                if job_info.status == '0':
                    SchedulerUtil.add_scheduler_job(job_info=job_info)
                result_db.commit()
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudJobResponse(**result)

    @classmethod
    def edit_job_services(cls, result_db: Session, page_object: EditJobModel):
        """
        编辑定时任务信息service
        :param result_db: orm对象
        :param page_object: 编辑定时任务对象
        :return: 编辑定时任务校验结果
        """
        edit_job = page_object.dict(exclude_unset=True)
        if page_object.type == 'status':
            del edit_job['type']
        job_info = cls.detail_job_services(result_db, edit_job.get('job_id'))
        if job_info:
            if page_object.type != 'status' and (job_info.job_name != page_object.job_name or job_info.job_group != page_object.job_group or job_info.invoke_target != page_object.invoke_target or job_info.cron_expression != page_object.cron_expression):
                job = JobDao.get_job_detail_by_info(result_db, page_object)
                if job:
                    result = dict(is_success=False, message='定时任务已存在')
                    return CrudJobResponse(**result)
            try:
                JobDao.edit_job_dao(result_db, edit_job)
                query_job = SchedulerUtil.get_scheduler_job(job_id=edit_job.get('job_id'))
                if query_job:
                    SchedulerUtil.remove_scheduler_job(job_id=edit_job.get('job_id'))
                if edit_job.get('status') == '0':
                    SchedulerUtil.add_scheduler_job(job_info=job_info)
                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='定时任务不存在')

        return CrudJobResponse(**result)

    @classmethod
    def execute_job_once_services(cls, result_db: Session, page_object: JobModel):
        """
        执行一次定时任务service
        :param result_db: orm对象
        :param page_object: 定时任务对象
        :return: 执行一次定时任务结果
        """
        query_job = SchedulerUtil.get_scheduler_job(job_id=page_object.job_id)
        if query_job:
            SchedulerUtil.remove_scheduler_job(job_id=page_object.job_id)
        job_info = cls.detail_job_services(result_db, page_object.job_id)
        if job_info:
            SchedulerUtil.execute_scheduler_job_once(job_info=job_info)
            result = dict(is_success=True, message='执行成功')
        else:
            result = dict(is_success=False, message='定时任务不存在')

        return CrudJobResponse(**result)

    @classmethod
    def delete_job_services(cls, result_db: Session, page_object: DeleteJobModel):
        """
        删除定时任务信息service
        :param result_db: orm对象
        :param page_object: 删除定时任务对象
        :return: 删除定时任务校验结果
        """
        if page_object.job_ids.split(','):
            job_id_list = page_object.job_ids.split(',')
            try:
                for job_id in job_id_list:
                    job_id_dict = dict(job_id=job_id)
                    JobDao.delete_job_dao(result_db, JobModel(**job_id_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='传入定时任务id为空')
        return CrudJobResponse(**result)

    @classmethod
    def detail_job_services(cls, result_db: Session, job_id: int):
        """
        获取定时任务详细信息service
        :param result_db: orm对象
        :param job_id: 定时任务id
        :return: 定时任务id对应的信息
        """
        job = JobDao.get_job_detail_by_id(result_db, job_id=job_id)

        return job

    @staticmethod
    async def export_job_list_services(request: Request, job_list: List):
        """
        导出定时任务信息service
        :param request: Request对象
        :param job_list: 定时任务信息列表
        :return: 定时任务信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "job_id": "任务编码",
            "job_name": "任务名称",
            "job_group": "任务组名",
            "job_executor": "任务执行器",
            "invoke_target": "调用目标字符串",
            "job_args": "位置参数",
            "job_kwargs": "关键字参数",
            "cron_expression": "cron执行表达式",
            "misfire_policy": "计划执行错误策略",
            "concurrent": "是否并发执行",
            "status": "状态",
            "create_by": "创建者",
            "create_time": "创建时间",
            "update_by": "更新者",
            "update_time": "更新时间",
            "remark": "备注",
        }

        data = [JobModel(**vars(row)).dict() for row in job_list]
        job_group_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_job_group')
        job_group_option = [dict(label=item.get('dict_label'), value=item.get('dict_value')) for item in job_group_list]
        job_group_option_dict = {item.get('value'): item for item in job_group_option}

        for item in data:
            if item.get('status') == '0':
                item['status'] = '正常'
            else:
                item['status'] = '暂停'
            if str(item.get('job_group')) in job_group_option_dict.keys():
                item['job_group'] = job_group_option_dict.get(str(item.get('job_group'))).get('label')
            if item.get('misfire_policy') == '1':
                item['misfire_policy'] = '立即执行'
            elif item.get('misfire_policy') == '2':
                item['misfire_policy'] = '执行一次'
            else:
                item['misfire_policy'] = '放弃执行'
            if item.get('concurrent') == '0':
                item['concurrent'] = '允许'
            else:
                item['concurrent'] = '禁止'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data

dash-fastapi-backend/module_admin/service/job_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.log_vo import *
from module_admin.dao.log_dao import *
from module_admin.service.dict_service import Request, DictDataService
from utils.common_util import export_list2excel


class OperationLogService:
    """
    操作日志管理模块服务层
    """

    @classmethod
    def get_operation_log_list_services(cls, result_db: Session, query_object: OperLogQueryModel):
        """
        获取操作日志列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 操作日志列表信息对象
        """
        operation_log_list_result = OperationLogDao.get_operation_log_list(result_db, query_object)

        return operation_log_list_result

    @classmethod
    def add_operation_log_services(cls, result_db: Session, page_object: OperLogModel):
        """
        新增操作日志service
        :param result_db: orm对象
        :param page_object: 新增操作日志对象
        :return: 新增操作日志校验结果
        """
        try:
            OperationLogDao.add_operation_log_dao(result_db, page_object)
            result_db.commit()
            result = dict(is_success=True, message='新增成功')
        except Exception as e:
            result_db.rollback()
            result = dict(is_success=False, message=str(e))

        return CrudLogResponse(**result)

    @classmethod
    def delete_operation_log_services(cls, result_db: Session, page_object: DeleteOperLogModel):
        """
        删除操作日志信息service
        :param result_db: orm对象
        :param page_object: 删除操作日志对象
        :return: 删除操作日志校验结果
        """
        if page_object.oper_ids.split(','):
            oper_id_list = page_object.oper_ids.split(',')
            try:
                for oper_id in oper_id_list:
                    oper_id_dict = dict(oper_id=oper_id)
                    OperationLogDao.delete_operation_log_dao(result_db, OperLogModel(**oper_id_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='传入操作日志id为空')
        return CrudLogResponse(**result)

    @classmethod
    def clear_operation_log_services(cls, result_db: Session, page_object: ClearOperLogModel):
        """
        清除操作日志信息service
        :param result_db: orm对象
        :param page_object: 清除操作日志对象
        :return: 清除操作日志校验结果
        """
        if page_object.oper_type == 'clear':
            try:
                OperationLogDao.clear_operation_log_dao(result_db)
                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='清除标识不合法')

        return CrudLogResponse(**result)

    @classmethod
    def detail_operation_log_services(cls, result_db: Session, oper_id: int):
        """
        获取操作日志详细信息service
        :param result_db: orm对象
        :param oper_id: 操作日志id
        :return: 操作日志id对应的信息
        """
        operation_log = OperationLogDao.get_operation_log_detail_by_id(result_db, oper_id=oper_id)

        return operation_log

    @classmethod
    async def export_operation_log_list_services(cls, request: Request, operation_log_list: List):
        """
        导出操作日志信息service
        :param request: Request对象
        :param operation_log_list: 操作日志信息列表
        :return: 操作日志信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "oper_id": "日志编号",
            "title": "系统模块",
            "business_type": "操作类型",
            "method": "方法名称",
            "request_method": "请求方式",
            "oper_name": "操作人员",
            "dept_name": "部门名称",
            "oper_url": "请求URL",
            "oper_ip": "操作地址",
            "oper_location": "操作地点",
            "oper_param": "请求参数",
            "json_result": "返回参数",
            "status": "操作状态",
            "error_msg": "错误消息",
            "oper_time": "操作日期",
            "cost_time": "消耗时间（毫秒）"
        }

        data = [OperLogModel(**vars(row)).dict() for row in operation_log_list]
        operation_type_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_oper_type')
        operation_type_option = [dict(label=item.get('dict_label'), value=item.get('dict_value')) for item in operation_type_list]
        operation_type_option_dict = {item.get('value'): item for item in operation_type_option}

        for item in data:
            if item.get('status') == 0:
                item['status'] = '成功'
            else:
                item['status'] = '失败'
            if str(item.get('business_type')) in operation_type_option_dict.keys():
                item['business_type'] = operation_type_option_dict.get(str(item.get('business_type'))).get('label')

        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data


class LoginLogService:
    """
    登录日志管理模块服务层
    """

    @classmethod
    def get_login_log_list_services(cls, result_db: Session, query_object: LoginLogQueryModel):
        """
        获取登录日志列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 登录日志列表信息对象
        """
        operation_log_list_result = LoginLogDao.get_login_log_list(result_db, query_object)

        return operation_log_list_result

    @classmethod
    def add_login_log_services(cls, result_db: Session, page_object: LogininforModel):
        """
        新增登录日志service
        :param result_db: orm对象
        :param page_object: 新增登录日志对象
        :return: 新增登录日志校验结果
        """
        try:
            LoginLogDao.add_login_log_dao(result_db, page_object)
            result_db.commit()
            result = dict(is_success=True, message='新增成功')
        except Exception as e:
            result_db.rollback()
            result = dict(is_success=False, message=str(e))

        return CrudLogResponse(**result)

    @classmethod
    def delete_login_log_services(cls, result_db: Session, page_object: DeleteLoginLogModel):
        """
        删除操作日志信息service
        :param result_db: orm对象
        :param page_object: 删除操作日志对象
        :return: 删除操作日志校验结果
        """
        if page_object.info_ids.split(','):
            info_id_list = page_object.info_ids.split(',')
            try:
                for info_id in info_id_list:
                    info_id_dict = dict(info_id=info_id)
                    LoginLogDao.delete_login_log_dao(result_db, LogininforModel(**info_id_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='传入登录日志id为空')
        return CrudLogResponse(**result)

    @classmethod
    def clear_login_log_services(cls, result_db: Session, page_object: ClearLoginLogModel):
        """
        清除操作日志信息service
        :param result_db: orm对象
        :param page_object: 清除操作日志对象
        :return: 清除操作日志校验结果
        """
        if page_object.oper_type == 'clear':
            try:
                LoginLogDao.clear_login_log_dao(result_db)
                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='清除标识不合法')

        return CrudLogResponse(**result)

    @classmethod
    async def unlock_user_services(cls, request: Request, unlock_user: UnlockUser):
        locked_user = await request.app.state.redis.get(f"account_lock:{unlock_user.user_name}")
        if locked_user:
            await request.app.state.redis.delete(f"account_lock:{unlock_user.user_name}")
            result = dict(is_success=True, message='解锁成功')
        else:
            result = dict(is_success=False, message='该用户未锁定')
        return CrudLogResponse(**result)

    @staticmethod
    def export_login_log_list_services(login_log_list: List):
        """
        导出登录日志信息service
        :param login_log_list: 登录日志信息列表
        :return: 登录日志信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "info_id": "访问编号",
            "user_name": "用户名称",
            "ipaddr": "登录地址",
            "login_location": "登录地点",
            "browser": "浏览器",
            "os": "操作系统",
            "status": "登录状态",
            "msg": "操作信息",
            "login_time": "登录日期"
        }

        data = [LogininforModel(**vars(row)).dict() for row in login_log_list]

        for item in data:
            if item.get('status') == '0':
                item['status'] = '成功'
            else:
                item['status'] = '失败'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data

dash-fastapi-backend/module_admin/service/log_service.py
insistence/Dash-FastAPI-Admin
from fastapi import Request, Form
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
import random
import uuid
from datetime import timedelta
from typing import Dict
from module_admin.entity.vo.user_vo import *
from module_admin.entity.vo.login_vo import *
from module_admin.dao.login_dao import *
from module_admin.service.user_service import UserService
from module_admin.dao.user_dao import *
from config.env import JwtConfig
from utils.pwd_util import *
from utils.response_util import *
from utils.message_util import *
from config.get_db import get_db

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login/loginByAccount")


class CustomOAuth2PasswordRequestForm(OAuth2PasswordRequestForm):
    """
    自定义OAuth2PasswordRequestForm类，增加验证码及会话编号参数
    """
    def __init__(
            self,
            grant_type: str = Form(default=None, regex="password"),
            username: str = Form(),
            password: str = Form(),
            scope: str = Form(default=""),
            client_id: Optional[str] = Form(default=None),
            client_secret: Optional[str] = Form(default=None),
            captcha: Optional[str] = Form(default=""),
            session_id: Optional[str] = Form(default=""),
            login_info: Optional[Dict[str, str]] = Form(default=None)
    ):
        super().__init__(grant_type=grant_type, username=username, password=password,
                         scope=scope, client_id=client_id, client_secret=client_secret)
        self.captcha = captcha
        self.session_id = session_id
        self.login_info = login_info


async def get_current_user(request: Request = Request, token: str = Depends(oauth2_scheme),
                           result_db: Session = Depends(get_db)):
    """
    根据token获取当前用户信息
    :param request: Request对象
    :param token: 用户token
    :param result_db: orm对象
    :return: 当前用户信息对象
    :raise: 令牌异常AuthException
    """
    # if token[:6] != 'Bearer':
    #     logger.warning("用户token不合法")
    #     raise AuthException(data="", message="用户token不合法")
    try:
        if token.startswith('Bearer'):
            token = token.split(' ')[1]
        payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
        user_id: str = payload.get("user_id")
        session_id: str = payload.get("session_id")
        if user_id is None:
            logger.warning("用户token不合法")
            raise AuthException(data="", message="用户token不合法")
        token_data = TokenData(user_id=int(user_id))
    except JWTError:
        logger.warning("用户token已失效，请重新登录")
        raise AuthException(data="", message="用户token已失效，请重新登录")
    user = UserDao.get_user_by_id(result_db, user_id=token_data.user_id)
    if user is None:
        logger.warning("用户token不合法")
        raise AuthException(data="", message="用户token不合法")
    redis_token = await request.app.state.redis.get(f'access_token:{session_id}')
    # 此方法可实现同一账号同一时间只能登录一次
    # redis_token = await request.app.state.redis.get(f'access_token:{user.user_basic_info[0].user_id}')
    if token == redis_token:
        await request.app.state.redis.set(f'access_token:{session_id}', redis_token,
                                          ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
        # await request.app.state.redis.set(f'access_token:{user.user_basic_info[0].user_id}', redis_token,
        #                                   ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))

        return CurrentUserInfoServiceResponse(
            user=user.user_basic_info[0],
            dept=user.user_dept_info[0],
            role=user.user_role_info,
            post=user.user_post_info,
            menu=user.user_menu_info
        )
    else:
        logger.warning("用户token已失效，请重新登录")
        raise AuthException(data="", message="用户token已失效，请重新登录")


async def get_sms_code_services(request: Request, result_db: Session, user: ResetUserModel):
    """
    获取短信验证码service
    :param request: Request对象
    :param result_db: orm对象
    :param user: 用户对象
    :return: 短信验证码对象
    """
    redis_sms_result = await request.app.state.redis.get(f"sms_code:{user.session_id}")
    if redis_sms_result:
        return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='短信验证码仍在有效期内'))
    is_user = UserDao.get_user_by_name(result_db, user.user_name)
    if is_user:
        sms_code = str(random.randint(100000, 999999))
        session_id = str(uuid.uuid4())
        await request.app.state.redis.set(f"sms_code:{session_id}", sms_code, ex=timedelta(minutes=2))
        # 此处模拟调用短信服务
        message_service(sms_code)

        return SmsCode(**dict(is_success=True, sms_code=sms_code, session_id=session_id, message='获取成功'))

    return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='用户不存在'))


async def forget_user_services(request: Request, result_db: Session, forget_user: ResetUserModel):
    """
    用户忘记密码services
    :param request: Request对象
    :param result_db: orm对象
    :param forget_user: 重置用户对象
    :return: 重置结果
    """
    redis_sms_result = await request.app.state.redis.get(f"sms_code:{forget_user.session_id}")
    if forget_user.sms_code == redis_sms_result:
        forget_user.password = PwdUtil.get_password_hash(forget_user.password)
        forget_user.user_id = UserDao.get_user_by_name(result_db, forget_user.user_name).user_id
        edit_result = UserService.reset_user_services(result_db, forget_user)
        result = edit_result.dict()
    elif not redis_sms_result:
        result = dict(is_success=False, message='短信验证码已过期')
    else:
        await request.app.state.redis.delete(f"sms_code:{forget_user.session_id}")
        result = dict(is_success=False, message='短信验证码不正确')

    return CrudUserResponse(**result)


async def logout_services(request: Request, session_id: str):
    """
    退出登录services
    :param request: Request对象
    :param session_id: 会话编号
    :return: 退出登录结果
    """
    await request.app.state.redis.delete(f'access_token:{session_id}')
    # await request.app.state.redis.delete(f'{current_user.user.user_id}_access_token')
    # await request.app.state.redis.delete(f'{current_user.user.user_id}_session_id')

    return True


async def check_login_captcha(request: Request, login_user: UserLogin):
    """
    校验用户登录验证码
    :param request: Request对象
    :param login_user: 登录用户对象
    :return: 校验结果
    """
    captcha_value = await request.app.state.redis.get(f'captcha_codes:{login_user.session_id}')
    if not captcha_value:
        logger.warning("验证码已失效")
        raise LoginException(data="", message="验证码已失效")
    if login_user.captcha != str(captcha_value):
        logger.warning("验证码错误")
        raise LoginException(data="", message="验证码错误")
    return True


async def authenticate_user(request: Request, query_db: Session, login_user: UserLogin):
    """
    根据用户名密码校验用户登录
    :param request: Request对象
    :param query_db: orm对象
    :param login_user: 登录用户对象
    :return: 校验结果
    """
    account_lock = await request.app.state.redis.get(f"account_lock:{login_user.user_name}")
    if login_user.user_name == account_lock:
        logger.warning("账号已锁定，请稍后再试")
        raise LoginException(data="", message="账号已锁定，请稍后再试")
    # 判断是否开启验证码，开启则验证，否则不验证
    if login_user.captcha_enabled:
        await check_login_captcha(request, login_user)
    user = login_by_account(query_db, login_user.user_name)
    if not user:
        logger.warning("用户不存在")
        raise LoginException(data="", message="用户不存在")
    if not PwdUtil.verify_password(login_user.password, user[0].password):
        cache_password_error_count = await request.app.state.redis.get(f"password_error_count:{login_user.user_name}")
        password_error_counted = 0
        if cache_password_error_count:
            password_error_counted = cache_password_error_count
        password_error_count = int(password_error_counted) + 1
        await request.app.state.redis.set(f"password_error_count:{login_user.user_name}", password_error_count,
                                          ex=timedelta(minutes=10))
        if password_error_count > 5:
            await request.app.state.redis.delete(f"password_error_count:{login_user.user_name}")
            await request.app.state.redis.set(f"account_lock:{login_user.user_name}", login_user.user_name,
                                              ex=timedelta(minutes=10))
            logger.warning("10分钟内密码已输错超过5次，账号已锁定，请10分钟后再试")
            raise LoginException(data="", message="10分钟内密码已输错超过5次，账号已锁定，请10分钟后再试")
        logger.warning("密码错误")
        raise LoginException(data="", message="密码错误")
    if user[0].status == '1':
        logger.warning("用户已停用")
        raise LoginException(data="", message="用户已停用")
    await request.app.state.redis.delete(f"password_error_count:{login_user.user_name}")
    return user


def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
    """
    根据登录信息创建当前用户token
    :param data: 登录信息
    :param expires_delta: token有效期
    :return: token
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, JwtConfig.SECRET_KEY, algorithm=JwtConfig.ALGORITHM)
    return encoded_jwt


def deal_user_dept_info(db: Session, dept_info: DeptInfo):
    tmp_dept_name = dept_info.dept_name
    dept_ancestors = dept_info.ancestors.split(',')
    tmp_dept_list = []
    for item in dept_ancestors:
        dept_obj = UserDao.get_user_dept_info(db, int(item))
        if dept_obj:
            tmp_dept_list.append(dept_obj.dept_name)
    tmp_dept_list.append(tmp_dept_name)
    user_dept_info = '/'.join(tmp_dept_list)

    return user_dept_info


def deal_user_role_info(role_info: RoleInfo):
    tmp_user_role_info = []
    for item in role_info.role_info:
        tmp_user_role_info.append(item.role_name)
    user_role_info = '/'.join(tmp_user_role_info)

    return user_role_info


def deal_user_menu_info(pid: int, permission_list: MenuList):
    """
    工具方法：根据菜单信息生成树形嵌套数据
    :param pid: 菜单id
    :param permission_list: 菜单列表信息
    :return: 菜单树形嵌套数据
    """
    menu_list = []
    for permission in permission_list.menu_info:
        if permission.parent_id == pid:
            children = deal_user_menu_info(permission.menu_id, permission_list)
            antd_menu_list_data = {}
            if children and permission.menu_type == 'M':
                antd_menu_list_data['component'] = 'SubMenu'
                antd_menu_list_data['props'] = {
                    'key': str(permission.menu_id),
                    'title': permission.menu_name,
                    'icon': permission.icon
                }
                antd_menu_list_data['children'] = children
            elif children and permission.menu_type == 'C':
                antd_menu_list_data['component'] = 'Item'
                antd_menu_list_data['props'] = {
                    'key': str(permission.menu_id),
                    'title': permission.menu_name,
                    'icon': permission.icon,
                    'href': permission.path,
                    'modules': permission.component
                }
                antd_menu_list_data['button'] = children
            elif permission.menu_type == 'F':
                antd_menu_list_data['component'] = 'Button'
                antd_menu_list_data['props'] = {
                    'key': str(permission.menu_id),
                    'title': permission.menu_name,
                    'icon': permission.icon
                }
            else:
                antd_menu_list_data['component'] = 'Item'
                antd_menu_list_data['props'] = {
                    'key': str(permission.menu_id),
                    'title': permission.menu_name,
                    'icon': permission.icon,
                    'href': permission.path,
                }
            menu_list.append(antd_menu_list_data)

    return menu_list

dash-fastapi-backend/module_admin/service/login_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.menu_vo import *
from module_admin.dao.menu_dao import *
from module_admin.entity.vo.user_vo import CurrentUserInfoServiceResponse


class MenuService:
    """
    菜单管理模块服务层
    """

    @classmethod
    def get_menu_tree_services(cls, result_db: Session, page_object: MenuTreeModel, current_user: Optional[CurrentUserInfoServiceResponse] = None):
        """
        获取菜单树信息service
        :param result_db: orm对象
        :param page_object: 查询参数对象
        :param current_user: 当前用户对象
        :return: 菜单树信息对象
        """
        menu_tree_option = []
        menu_list_result = MenuDao.get_menu_list_for_tree(result_db, MenuModel(**page_object.dict()), current_user.user.user_id, current_user.role)
        menu_tree_result = cls.get_menu_tree(0, MenuTree(menu_tree=menu_list_result))
        if page_object.type != 'role':
            menu_tree_option.append(dict(title='主类目', value='0', key='0', children=menu_tree_result))
        else:
            menu_tree_option = [menu_tree_result, menu_list_result]

        return menu_tree_option

    @classmethod
    def get_menu_tree_for_edit_option_services(cls, result_db: Session, page_object: MenuModel, current_user: Optional[CurrentUserInfoServiceResponse] = None):
        """
        获取菜单编辑菜单树信息service
        :param result_db: orm对象
        :param page_object: 查询参数对象
        :param current_user: 当前用户
        :return: 菜单树信息对象
        """
        menu_tree_option = []
        menu_list_result = MenuDao.get_menu_info_for_edit_option(result_db, page_object, current_user.user.user_id, current_user.role)
        menu_tree_result = cls.get_menu_tree(0, MenuTree(menu_tree=menu_list_result))
        menu_tree_option.append(dict(title='主类目', value='0', key='0', children=menu_tree_result))

        return menu_tree_option

    @classmethod
    def get_menu_list_services(cls, result_db: Session, page_object: MenuModel, current_user: Optional[CurrentUserInfoServiceResponse] = None):
        """
        获取菜单列表信息service
        :param result_db: orm对象
        :param page_object: 分页查询参数对象
        :param current_user: 当前用户对象
        :return: 菜单列表信息对象
        """
        menu_list_result = MenuDao.get_menu_list(result_db, page_object, current_user.user.user_id, current_user.role)

        return menu_list_result

    @classmethod
    def add_menu_services(cls, result_db: Session, page_object: MenuModel):
        """
        新增菜单信息service
        :param result_db: orm对象
        :param page_object: 新增菜单对象
        :return: 新增菜单校验结果
        """
        menu = MenuDao.get_menu_detail_by_info(result_db, MenuModel(
            **dict(parent_id=page_object.parent_id, menu_name=page_object.menu_name, menu_type=page_object.menu_type)))
        if menu:
            result = dict(is_success=False, message='同一目录下不允许存在同名同类型的菜单')
        else:
            try:
                MenuDao.add_menu_dao(result_db, page_object)
                result_db.commit()
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudMenuResponse(**result)

    @classmethod
    def edit_menu_services(cls, result_db: Session, page_object: MenuModel):
        """
        编辑菜单信息service
        :param result_db: orm对象
        :param page_object: 编辑部门对象
        :return: 编辑菜单校验结果
        """
        edit_menu = page_object.dict(exclude_unset=True)
        menu_info = cls.detail_menu_services(result_db, edit_menu.get('menu_id'))
        if menu_info:
            if menu_info.parent_id != page_object.parent_id or menu_info.menu_name != page_object.menu_name or menu_info.menu_type != page_object.menu_type:
                menu = MenuDao.get_menu_detail_by_info(result_db, MenuModel(
                    **dict(parent_id=page_object.parent_id, menu_name=page_object.menu_name, menu_type=page_object.menu_type)))
                if menu:
                    result = dict(is_success=False, message='同一目录下不允许存在同名同类型的菜单')
                    return CrudMenuResponse(**result)
            try:
                MenuDao.edit_menu_dao(result_db, edit_menu)
                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='菜单不存在')

        return CrudMenuResponse(**result)

    @classmethod
    def delete_menu_services(cls, result_db: Session, page_object: DeleteMenuModel):
        """
        删除菜单信息service
        :param result_db: orm对象
        :param page_object: 删除菜单对象
        :return: 删除菜单校验结果
        """
        if page_object.menu_ids.split(','):
            menu_id_list = page_object.menu_ids.split(',')
            try:
                for menu_id in menu_id_list:
                    menu_id_dict = dict(menu_id=menu_id)
                    MenuDao.delete_menu_dao(result_db, MenuModel(**menu_id_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='传入菜单id为空')
        return CrudMenuResponse(**result)

    @classmethod
    def detail_menu_services(cls, result_db: Session, menu_id: int):
        """
        获取菜单详细信息service
        :param result_db: orm对象
        :param menu_id: 菜单id
        :return: 菜单id对应的信息
        """
        menu = MenuDao.get_menu_detail_by_id(result_db, menu_id=menu_id)

        return menu

    @classmethod
    def get_menu_tree(cls, pid: int, permission_list: MenuTree):
        """
        工具方法：根据菜单信息生成树形嵌套数据
        :param pid: 菜单id
        :param permission_list: 菜单列表信息
        :return: 菜单树形嵌套数据
        """
        menu_list = []
        for permission in permission_list.menu_tree:
            if permission.parent_id == pid:
                children = cls.get_menu_tree(permission.menu_id, permission_list)
                menu_list_data = {}
                if children:
                    menu_list_data['title'] = permission.menu_name
                    menu_list_data['key'] = str(permission.menu_id)
                    menu_list_data['value'] = str(permission.menu_id)
                    menu_list_data['children'] = children
                else:
                    menu_list_data['title'] = permission.menu_name
                    menu_list_data['key'] = str(permission.menu_id)
                    menu_list_data['value'] = str(permission.menu_id)
                menu_list.append(menu_list_data)

        return menu_list

dash-fastapi-backend/module_admin/service/menu_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.notice_vo import *
from module_admin.dao.notice_dao import *


class NoticeService:
    """
    通知公告管理模块服务层
    """

    @classmethod
    def get_notice_list_services(cls, result_db: Session, query_object: NoticeQueryModel):
        """
        获取通知公告列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 通知公告列表信息对象
        """
        notice_list_result = NoticeDao.get_notice_list(result_db, query_object)

        return notice_list_result

    @classmethod
    def add_notice_services(cls, result_db: Session, page_object: NoticeModel):
        """
        新增通知公告信息service
        :param result_db: orm对象
        :param page_object: 新增通知公告对象
        :return: 新增通知公告校验结果
        """
        notice = NoticeDao.get_notice_detail_by_info(result_db, page_object)
        if notice:
            result = dict(is_success=False, message='通知公告已存在')
        else:
            try:
                NoticeDao.add_notice_dao(result_db, page_object)
                result_db.commit()
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudNoticeResponse(**result)

    @classmethod
    def edit_notice_services(cls, result_db: Session, page_object: NoticeModel):
        """
        编辑通知公告信息service
        :param result_db: orm对象
        :param page_object: 编辑通知公告对象
        :return: 编辑通知公告校验结果
        """
        edit_notice = page_object.dict(exclude_unset=True)
        notice_info = cls.detail_notice_services(result_db, edit_notice.get('notice_id'))
        if notice_info:
            if notice_info.notice_title != page_object.notice_title or notice_info.notice_type != page_object.notice_type or notice_info.notice_content != page_object.notice_content:
                notice = NoticeDao.get_notice_detail_by_info(result_db, page_object)
                if notice:
                    result = dict(is_success=False, message='通知公告已存在')
                    return CrudNoticeResponse(**result)
            try:
                NoticeDao.edit_notice_dao(result_db, edit_notice)
                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='通知公告不存在')

        return CrudNoticeResponse(**result)

    @classmethod
    def delete_notice_services(cls, result_db: Session, page_object: DeleteNoticeModel):
        """
        删除通知公告信息service
        :param result_db: orm对象
        :param page_object: 删除通知公告对象
        :return: 删除通知公告校验结果
        """
        if page_object.notice_ids.split(','):
            notice_id_list = page_object.notice_ids.split(',')
            try:
                for notice_id in notice_id_list:
                    notice_id_dict = dict(notice_id=notice_id)
                    NoticeDao.delete_notice_dao(result_db, NoticeModel(**notice_id_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='传入通知公告id为空')
        return CrudNoticeResponse(**result)

    @classmethod
    def detail_notice_services(cls, result_db: Session, notice_id: int):
        """
        获取通知公告详细信息service
        :param result_db: orm对象
        :param notice_id: 通知公告id
        :return: 通知公告id对应的信息
        """
        notice = NoticeDao.get_notice_detail_by_id(result_db, notice_id=notice_id)

        return notice

dash-fastapi-backend/module_admin/service/notice_service.py
insistence/Dash-FastAPI-Admin
from fastapi import Request
from jose import jwt
from config.env import JwtConfig
from module_admin.entity.vo.online_vo import *


class OnlineService:
    """
    在线用户管理模块服务层
    """

    @classmethod
    async def get_online_list_services(cls, request: Request, query_object: OnlinePageObject):
        """
        获取在线用户表信息service
        :param request: Request对象
        :param query_object: 查询参数对象
        :return: 在线用户列表信息
        """
        access_token_keys = await request.app.state.redis.keys('access_token*')
        if not access_token_keys:
            access_token_keys = []
        access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys]
        online_info_list = []
        for item in access_token_values_list:
            payload = jwt.decode(item, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
            online_dict = dict(
                session_id=payload.get('session_id'),
                user_name=payload.get('user_name'),
                dept_name=payload.get('dept_name'),
                ipaddr=payload.get('login_info').get('ipaddr'),
                login_location=payload.get('login_info').get('login_location'),
                browser=payload.get('login_info').get('browser'),
                os=payload.get('login_info').get('os'),
                login_time=payload.get('login_info').get('login_time')
            )
            if query_object.user_name and not query_object.ipaddr:
                if query_object.user_name == payload.get('user_name'):
                    online_info_list = [online_dict]
                    break
            elif not query_object.user_name and query_object.ipaddr:
                if query_object.ipaddr == payload.get('ipaddr'):
                    online_info_list = [online_dict]
                    break
            elif query_object.user_name and query_object.ipaddr:
                if query_object.user_name == payload.get('user_name') and query_object.ipaddr == payload.get('ipaddr'):
                    online_info_list = [online_dict]
                    break
            else:
                online_info_list.append(online_dict)

        return online_info_list

    @classmethod
    async def delete_online_services(cls, request: Request, page_object: DeleteOnlineModel):
        """
        强退在线用户信息service
        :param request: Request对象
        :param page_object: 强退在线用户对象
        :return: 强退在线用户校验结果
        """
        if page_object.session_ids.split(','):
            session_id_list = page_object.session_ids.split(',')
            for session_id in session_id_list:
                await request.app.state.redis.delete(f'access_token:{session_id}')
            result = dict(is_success=True, message='强退成功')
        else:
            result = dict(is_success=False, message='传入session_id为空')
        return CrudOnlineResponse(**result)

dash-fastapi-backend/module_admin/service/online_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.post_vo import *
from module_admin.dao.post_dao import *
from utils.common_util import export_list2excel


class PostService:
    """
    岗位管理模块服务层
    """

    @classmethod
    def get_post_select_option_services(cls, result_db: Session):
        """
        获取岗位列表不分页信息service
        :param result_db: orm对象
        :return: 岗位列表不分页信息对象
        """
        post_list_result = PostDao.get_post_select_option_dao(result_db)

        return post_list_result

    @classmethod
    def get_post_list_services(cls, result_db: Session, query_object: PostModel):
        """
        获取岗位列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 岗位列表信息对象
        """
        post_list_result = PostDao.get_post_list(result_db, query_object)

        return post_list_result

    @classmethod
    def add_post_services(cls, result_db: Session, page_object: PostModel):
        """
        新增岗位信息service
        :param result_db: orm对象
        :param page_object: 新增岗位对象
        :return: 新增岗位校验结果
        """
        post = PostDao.get_post_detail_by_info(result_db, PostModel(**dict(post_name=page_object.post_name)))
        if post:
            result = dict(is_success=False, message='岗位名称已存在')
        else:
            try:
                PostDao.add_post_dao(result_db, page_object)
                result_db.commit()
                result = dict(is_success=True, message='新增成功')
            except Exception as e:
                result_db.rollback()
                result = dict(is_success=False, message=str(e))

        return CrudPostResponse(**result)

    @classmethod
    def edit_post_services(cls, result_db: Session, page_object: PostModel):
        """
        编辑岗位信息service
        :param result_db: orm对象
        :param page_object: 编辑岗位对象
        :return: 编辑岗位校验结果
        """
        edit_post = page_object.dict(exclude_unset=True)
        post_info = cls.detail_post_services(result_db, edit_post.get('post_id'))
        if post_info:
            if post_info.post_name != page_object.post_name:
                post = PostDao.get_post_detail_by_info(result_db, PostModel(**dict(post_name=page_object.post_name)))
                if post:
                    result = dict(is_success=False, message='岗位名称已存在')
                    return CrudPostResponse(**result)
            try:
                PostDao.edit_post_dao(result_db, edit_post)
                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='岗位不存在')

        return CrudPostResponse(**result)

    @classmethod
    def delete_post_services(cls, result_db: Session, page_object: DeletePostModel):
        """
        删除岗位信息service
        :param result_db: orm对象
        :param page_object: 删除岗位对象
        :return: 删除岗位校验结果
        """
        if page_object.post_ids.split(','):
            post_id_list = page_object.post_ids.split(',')
            try:
                for post_id in post_id_list:
                    post_id_dict = dict(post_id=post_id)
                    PostDao.delete_post_dao(result_db, PostModel(**post_id_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='传入岗位id为空')
        return CrudPostResponse(**result)

    @classmethod
    def detail_post_services(cls, result_db: Session, post_id: int):
        """
        获取岗位详细信息service
        :param result_db: orm对象
        :param post_id: 岗位id
        :return: 岗位id对应的信息
        """
        post = PostDao.get_post_detail_by_id(result_db, post_id=post_id)

        return post

    @staticmethod
    def export_post_list_services(post_list: List):
        """
        导出岗位信息service
        :param post_list: 岗位信息列表
        :return: 岗位信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "post_id": "岗位编号",
            "post_code": "岗位编码",
            "post_name": "岗位名称",
            "post_sort": "显示顺序",
            "status": "状态",
            "create_by": "创建者",
            "create_time": "创建时间",
            "update_by": "更新者",
            "update_time": "更新时间",
            "remark": "备注",
        }

        data = [PostModel(**vars(row)).dict() for row in post_list]

        for item in data:
            if item.get('status') == '0':
                item['status'] = '正常'
            else:
                item['status'] = '停用'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data

dash-fastapi-backend/module_admin/service/post_service.py
insistence/Dash-FastAPI-Admin
from module_admin.entity.vo.role_vo import *
from module_admin.dao.role_dao import *
from utils.common_util import export_list2excel


class RoleService:
    """
    角色管理模块服务层
    """

    @classmethod
    def get_role_select_option_services(cls, result_db: Session):
        """
        获取角色列表不分页信息service
        :param result_db: orm对象
        :return: 角色列表不分页信息对象
        """
        role_list_result = RoleDao.get_role_select_option_dao(result_db)

        return role_list_result

    @classmethod
    def get_role_list_services(cls, result_db: Session, query_object: RoleQueryModel):
        """
        获取角色列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :return: 角色列表信息对象
        """
        role_list_result = RoleDao.get_role_list(result_db, query_object)

        return role_list_result

    @classmethod
    def add_role_services(cls, result_db: Session, page_object: AddRoleModel):
        """
        新增角色信息service
        :param result_db: orm对象
        :param page_object: 新增角色对象
        :return: 新增角色校验结果
        """
        add_role = RoleModel(**page_object.dict())
        role = RoleDao.get_role_by_info(result_db, RoleModel(**dict(role_name=page_object.role_name)))
        if role:
            result = dict(is_success=False, message='角色名称已存在')
        else:
            try:
                add_result = RoleDao.add_role_dao(result_db, add_role)
                role_id = add_result.role_id
                if page_object.menu_id:
                    menu_id_list = page_object.menu_id.split(',')
                    for menu in menu_id_list:
                        menu_dict = dict(role_id=role_id, menu_id=menu)
                        RoleDao.add_role_menu_dao(result_db, RoleMenuModel(**menu_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))

        return CrudRoleResponse(**result)

    @classmethod
    def edit_role_services(cls, result_db: Session, page_object: AddRoleModel):
        """
        编辑角色信息service
        :param result_db: orm对象
        :param page_object: 编辑角色对象
        :return: 编辑角色校验结果
        """
        edit_role = page_object.dict(exclude_unset=True)
        if page_object.type != 'status':
            del edit_role['menu_id']
        if page_object.type == 'status':
            del edit_role['type']
        role_info = cls.detail_role_services(result_db, edit_role.get('role_id'))
        if role_info:
            if page_object.type != 'status' and role_info.role.role_name != page_object.role_name:
                role = RoleDao.get_role_by_info(result_db, RoleModel(**dict(role_name=page_object.role_name)))
                if role:
                    result = dict(is_success=False, message='角色名称已存在')
                    return CrudRoleResponse(**result)
            try:
                RoleDao.edit_role_dao(result_db, edit_role)
                if page_object.type != 'status':
                    role_id_dict = dict(role_id=page_object.role_id)
                    RoleDao.delete_role_menu_dao(result_db, RoleMenuModel(**role_id_dict))
                    if page_object.menu_id:
                        menu_id_list = page_object.menu_id.split(',')
                        for menu in menu_id_list:
                            menu_dict = dict(role_id=page_object.role_id, menu_id=menu)
                            RoleDao.add_role_menu_dao(result_db, RoleMenuModel(**menu_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='角色不存在')

        return CrudRoleResponse(**result)

    @classmethod
    def role_datascope_services(cls, result_db: Session, page_object: RoleDataScopeModel):
        """
        分配角色数据权限service
        :param result_db: orm对象
        :param page_object: 角色数据权限对象
        :return: 分配角色数据权限结果
        """
        edit_role = page_object.dict(exclude_unset=True)
        del edit_role['dept_id']
        role_info = cls.detail_role_services(result_db, edit_role.get('role_id'))
        if role_info:
            if role_info.role.role_name != page_object.role_name:
                role = RoleDao.get_role_by_info(result_db, RoleModel(**dict(role_name=page_object.role_name)))
                if role:
                    result = dict(is_success=False, message='角色名称已存在')
                    return CrudRoleResponse(**result)
            try:
                RoleDao.edit_role_dao(result_db, edit_role)
                role_id_dict = dict(role_id=page_object.role_id)
                RoleDao.delete_role_dept_dao(result_db, RoleDeptModel(**role_id_dict))
                if page_object.dept_id and page_object.data_scope == '2':
                    dept_id_list = page_object.dept_id.split(',')
                    for dept in dept_id_list:
                        dept_dict = dict(role_id=page_object.role_id, dept_id=dept)
                        RoleDao.add_role_dept_dao(result_db, RoleDeptModel(**dept_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='角色不存在')

        return CrudRoleResponse(**result)

    @classmethod
    def delete_role_services(cls, result_db: Session, page_object: DeleteRoleModel):
        """
        删除角色信息service
        :param result_db: orm对象
        :param page_object: 删除角色对象
        :return: 删除角色校验结果
        """
        if page_object.role_ids.split(','):
            role_id_list = page_object.role_ids.split(',')
            try:
                for role_id in role_id_list:
                    role_id_dict = dict(role_id=role_id, update_by=page_object.update_by, update_time=page_object.update_time)
                    RoleDao.delete_role_menu_dao(result_db, RoleMenuModel(**role_id_dict))
                    RoleDao.delete_role_dao(result_db, RoleModel(**role_id_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='传入角色id为空')
        return CrudRoleResponse(**result)

    @classmethod
    def detail_role_services(cls, result_db: Session, role_id: int):
        """
        获取角色详细信息service
        :param result_db: orm对象
        :param role_id: 角色id
        :return: 角色id对应的信息
        """
        role = RoleDao.get_role_detail_by_id(result_db, role_id=role_id)

        return role

    @staticmethod
    def export_role_list_services(role_list: List):
        """
        导出角色列表信息service
        :param role_list: 角色信息列表
        :return: 角色列表信息对象
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "role_id": "角色编号",
            "role_name": "角色名称",
            "role_key": "权限字符",
            "role_sort": "显示顺序",
            "status": "状态",
            "create_by": "创建者",
            "create_time": "创建时间",
            "update_by": "更新者",
            "update_time": "更新时间",
            "remark": "备注",
        }

        data = [RoleModel(**vars(row)).dict() for row in role_list]

        for item in data:
            if item.get('status') == '0':
                item['status'] = '正常'
            else:
                item['status'] = '停用'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        binary_data = export_list2excel(new_data)

        return binary_data

dash-fastapi-backend/module_admin/service/role_service.py
insistence/Dash-FastAPI-Admin
import psutil
from utils.common_util import bytes2human
import platform
import socket
import os
import time
from module_admin.entity.vo.server_vo import *


class ServerService:
    """
    服务监控模块服务层
    """

    @staticmethod
    def get_server_monitor_info():
        # CPU信息
        # 获取CPU总核心数
        cpu_num = psutil.cpu_count(logical=True)
        cpu_usage_percent = psutil.cpu_times_percent()
        cpu_used = f'{cpu_usage_percent.user}%'
        cpu_sys = f'{cpu_usage_percent.system}%'
        cpu_free = f'{cpu_usage_percent.idle}%'
        cpu = CpuInfo(**dict(cpu_num=cpu_num, used=cpu_used, sys=cpu_sys, free=cpu_free))

        # 内存信息
        memory_info = psutil.virtual_memory()
        memory_total = bytes2human(memory_info.total)
        memory_used = bytes2human(memory_info.used)
        memory_free = bytes2human(memory_info.free)
        memory_usage = f'{memory_info.percent}%'
        mem = MemoryInfo(**dict(total=memory_total, used=memory_used, free=memory_free, usage=memory_usage))

        # 主机信息
        # 获取主机名
        hostname = socket.gethostname()
        # 获取IP
        computer_ip = socket.gethostbyname(hostname)
        os_name = platform.platform()
        computer_name = platform.node()
        os_arch = platform.machine()
        sys = SysInfo(**dict(computer_ip=computer_ip, computer_name=computer_name, os_arch=os_arch, os_name=os_name))

        # python解释器信息
        current_pid = os.getpid()
        current_process = psutil.Process(current_pid)
        python_name = current_process.name()
        python_version = platform.python_version()
        python_home = current_process.exe()
        start_time_stamp = current_process.create_time()
        start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time_stamp))
        current_time_stamp = time.time()
        difference = current_time_stamp - start_time_stamp
        # 将时间差转换为天、小时和分钟数
        days = int(difference // (24 * 60 * 60))  # 每天的秒数
        hours = int((difference % (24 * 60 * 60)) // (60 * 60))  # 每小时的秒数
        minutes = int((difference % (60 * 60)) // 60)  # 每分钟的秒数
        run_time = f"{days}天{hours}小时{minutes}分钟"
        project_dir = os.path.abspath(os.getcwd())
        py = PyInfo(
            **dict(
                name=python_name,
                version=python_version,
                start_time=start_time,
                run_time=run_time,
                home=python_home,
                project_dir=project_dir
            )
        )

        # 磁盘信息
        io = psutil.disk_partitions()
        sys_files = []
        for i in io:
            o = psutil.disk_usage(i.device)
            disk_data = {
                "dir_name": i.device,
                "sys_type_name": i.fstype,
                "disk_name": "本地固定磁盘（" + i.mountpoint.replace('\\', '') + "）",
                "total": bytes2human(o.total),
                "used": bytes2human(o.used),
                "free": bytes2human(o.free),
                "usage": f'{psutil.disk_usage(i.device).percent}%'
            }
            sys_files.append(SysFiles(**disk_data))

        result = dict(cpu=cpu, mem=mem, sys=sys, py=py, sys_files=sys_files)

        return ServerMonitorModel(**result)

dash-fastapi-backend/module_admin/service/server_service.py
insistence/Dash-FastAPI-Admin
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 *


class UserService:
    """
    用户管理模块服务层
    """

    @classmethod
    def get_user_list_services(cls, result_db: Session, query_object: UserQueryModel, data_scope_sql: str):
        """
        获取用户列表信息service
        :param result_db: orm对象
        :param query_object: 查询参数对象
        :param data_scope_sql: 数据权限对应的查询sql语句
        :return: 用户列表信息对象
        """
        user_list_result = UserDao.get_user_list(result_db, query_object, data_scope_sql)

        return user_list_result

    @classmethod
    def add_user_services(cls, result_db: Session, page_object: AddUserModel):
        """
        新增用户信息service
        :param result_db: orm对象
        :param page_object: 新增用户对象
        :return: 新增用户校验结果
        """
        add_user = UserModel(**page_object.dict())
        user = UserDao.get_user_by_info(result_db, UserModel(**dict(user_name=page_object.user_name)))
        if user:
            result = dict(is_success=False, message='用户名已存在')
        else:
            try:
                add_result = UserDao.add_user_dao(result_db, add_user)
                user_id = add_result.user_id
                if page_object.role_id:
                    role_id_list = page_object.role_id.split(',')
                    for role in role_id_list:
                        role_dict = dict(user_id=user_id, role_id=role)
                        UserDao.add_user_role_dao(result_db, UserRoleModel(**role_dict))
                if page_object.post_id:
                    post_id_list = page_object.post_id.split(',')
                    for post in post_id_list:
                        post_dict = dict(user_id=user_id, post_id=post)
                        UserDao.add_user_post_dao(result_db, UserPostModel(**post_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))

        return CrudUserResponse(**result)

    @classmethod
    def edit_user_services(cls, result_db: Session, page_object: AddUserModel):
        """
        编辑用户信息service
        :param result_db: orm对象
        :param page_object: 编辑用户对象
        :return: 编辑用户校验结果
        """
        edit_user = page_object.dict(exclude_unset=True)
        if page_object.type != 'status' and page_object.type != 'avatar':
            del edit_user['role_id']
            del edit_user['post_id']
        if page_object.type == 'status' or page_object.type == 'avatar':
            del edit_user['type']
        user_info = cls.detail_user_services(result_db, edit_user.get('user_id'))
        if user_info:
            if page_object.type != 'status' and page_object.type != 'avatar' and user_info.user.user_name != page_object.user_name:
                user = UserDao.get_user_by_info(result_db, UserModel(**dict(user_name=page_object.user_name)))
                if user:
                    result = dict(is_success=False, message='用户名已存在')
                    return CrudUserResponse(**result)
            try:
                UserDao.edit_user_dao(result_db, edit_user)
                if page_object.type != 'status' and page_object.type != 'avatar':
                    user_id_dict = dict(user_id=page_object.user_id)
                    UserDao.delete_user_role_dao(result_db, UserRoleModel(**user_id_dict))
                    UserDao.delete_user_post_dao(result_db, UserPostModel(**user_id_dict))
                    if page_object.role_id:
                        role_id_list = page_object.role_id.split(',')
                        for role in role_id_list:
                            role_dict = dict(user_id=page_object.user_id, role_id=role)
                            UserDao.add_user_role_dao(result_db, UserRoleModel(**role_dict))
                    if page_object.post_id:
                        post_id_list = page_object.post_id.split(',')
                        for post in post_id_list:
                            post_dict = dict(user_id=page_object.user_id, post_id=post)
                            UserDao.add_user_post_dao(result_db, UserPostModel(**post_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='用户不存在')

        return CrudUserResponse(**result)

    @classmethod
    def delete_user_services(cls, result_db: Session, page_object: DeleteUserModel):
        """
        删除用户信息service
        :param result_db: orm对象
        :param page_object: 删除用户对象
        :return: 删除用户校验结果
        """
        if page_object.user_ids.split(','):
            user_id_list = page_object.user_ids.split(',')
            try:
                for user_id in user_id_list:
                    user_id_dict = dict(user_id=user_id, update_by=page_object.update_by, update_time=page_object.update_time)
                    UserDao.delete_user_role_dao(result_db, UserRoleModel(**user_id_dict))
                    UserDao.delete_user_post_dao(result_db, UserPostModel(**user_id_dict))
                    UserDao.delete_user_dao(result_db, UserModel(**user_id_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='传入用户id为空')
        return CrudUserResponse(**result)

    @classmethod
    def detail_user_services(cls, result_db: Session, user_id: int):
        """
        获取用户详细信息service
        :param result_db: orm对象
        :param user_id: 用户id
        :return: 用户id对应的信息
        """
        user = UserDao.get_user_detail_by_id(result_db, user_id=user_id)

        return UserDetailModel(
            user=user.user_basic_info[0],
            dept=user.user_dept_info[0],
            role=user.user_role_info,
            post=user.user_post_info
        )

    @classmethod
    def reset_user_services(cls, result_db: Session, page_object: ResetUserModel):
        """
        重置用户密码service
        :param result_db: orm对象
        :param page_object: 重置用户对象
        :return: 重置用户校验结果
        """
        reset_user = page_object.dict(exclude_unset=True)
        if page_object.old_password:
            user = UserDao.get_user_detail_by_id(result_db, user_id=page_object.user_id).user_basic_info[0]
            if not PwdUtil.verify_password(page_object.old_password, user.password):
                result = dict(is_success=False, message='旧密码不正确')
                return CrudUserResponse(**result)
            else:
                del reset_user['old_password']
        if page_object.sms_code and page_object.session_id:
            del reset_user['sms_code']
            del reset_user['session_id']
        try:
            UserDao.edit_user_dao(result_db, reset_user)
            result_db.commit()
            result = dict(is_success=True, message='重置成功')
        except Exception as e:
            result_db.rollback()
            result = dict(is_success=False, message=str(e))

        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):
        """
        导出用户信息service
        :param user_list: 用户信息列表
        :return: 用户信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            "user_id": "用户编号",
            "user_name": "用户名称",
            "nick_name": "用户昵称",
            "dept_name": "部门",
            "email": "邮箱地址",
            "phonenumber": "手机号码",
            "sex": "性别",
            "status": "状态",
            "create_by": "创建者",
            "create_time": "创建时间",
            "update_by": "更新者",
            "update_time": "更新时间",
            "remark": "备注",
        }

        data = user_list

        for item in data:
            if item.get('status') == '0':
                item['status'] = '正常'
            else:
                item['status'] = '停用'
            if item.get('sex') == '0':
                item['sex'] = '男'
            elif item.get('sex') == '1':
                item['sex'] = '女'
            else:
                item['sex'] = '未知'
        new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in data]
        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

dash-fastapi-backend/module_admin/service/user_service.py
insistence/Dash-FastAPI-Admin
from . import scheduler_test

dash-fastapi-backend/module_task/__init__.py
insistence/Dash-FastAPI-Admin
from datetime import datetime


def job(*args, **kwargs):
    print(args)
    print(kwargs)
    print(f"{datetime.now()}执行了")

dash-fastapi-backend/module_task/scheduler_test.py
insistence/Dash-FastAPI-Admin
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 typing import List
from config.env import CachePathConfig


def worship():
    print("""
////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//             佛祖保佑       永不宕机      永无BUG                  //
////////////////////////////////////////////////////////////////////
    """)


def bytes2human(n, format_str="%(value).1f%(symbol)s"):
    """Used by various scripts. See:
    http://goo.gl/zeJZl

    >>> bytes2human(10000)
    '9.8K'
    >>> bytes2human(100001221)
    '95.4M'
    """
    symbols = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')
    prefix = {}
    for i, s in enumerate(symbols[1:]):
        prefix[s] = 1 << (i + 1) * 10
    for symbol in reversed(symbols[1:]):
        if n >= prefix[symbol]:
            value = float(n) / prefix[symbol]
            return format_str % locals()
    return format_str % dict(symbol=symbols[0], value=n)


def bytes2file_response(bytes_info):
    yield bytes_info


def export_list2excel(list_data: List):
    """
    工具方法：将需要导出的list数据转化为对应excel的二进制数据
    :param list_data: 数据列表
    :return: 字典信息对应excel的二进制数据
    """
    df = pd.DataFrame(list_data)
    binary_data = io.BytesIO()
    df.to_excel(binary_data, index=False, engine='openpyxl')
    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

dash-fastapi-backend/utils/common_util.py
insistence/Dash-FastAPI-Admin
import os
import time
from loguru import logger

log_path = os.path.join(os.getcwd(), 'logs')
if not os.path.exists(log_path):
    os.mkdir(log_path)

log_path_error = os.path.join(log_path, f'{time.strftime("%Y-%m-%d")}_error.log')

logger.add(log_path_error, rotation="50MB", encoding="utf-8", enqueue=True, compression="zip")

dash-fastapi-backend/utils/log_util.py
insistence/Dash-FastAPI-Admin
from utils.log_util import logger


def message_service(sms_code: str):
    logger.info(f"短信验证码为{sms_code}")

dash-fastapi-backend/utils/message_util.py
insistence/Dash-FastAPI-Admin
import math
from typing import List

from pydantic import BaseModel


class PageModel(BaseModel):
    """
    分页模型
    """
    offset: int
    page_num: int
    page_size: int
    total: int
    has_next: bool


class PageObjectResponse(BaseModel):
    """
    用户管理列表分页查询返回模型
    """
    rows: List = []
    page_num: int
    page_size: int
    total: int
    has_next: bool


def get_page_info(offset: int, page_num: int, page_size: int, count: int):
    """
    根据分页参数获取分页信息
    :param offset: 起始数据位置
    :param page_num: 当前页码
    :param page_size: 当前页面数据量
    :param count: 数据总数
    :return: 分页信息对象
    """
    has_next = False
    if offset >= count:
        res_offset_1 = (page_num - 2) * page_size
        if res_offset_1 < 0:
            res_offset = 0
            res_page_num = 1
        else:
            res_offset = res_offset_1
            res_page_num = page_num - 1
    else:
        res_offset = offset
        if (res_offset + page_size) < count:
            has_next = True
        res_page_num = page_num

    result = dict(offset=res_offset, page_num=res_page_num, page_size=page_size, total=count, has_next=has_next)

    return PageModel(**result)


def get_page_obj(data_list: List, page_num: int, page_size: int):
    """
    输入数据列表data_list和分页信息，返回分页数据列表结果
    :param data_list: 原始数据列表
    :param page_num: 当前页码
    :param page_size: 当前页面数据量
    :return: 分页数据对象
    """
    # 计算起始索引和结束索引
    start = (page_num - 1) * page_size
    end = page_num * page_size

    # 根据计算得到的起始索引和结束索引对数据列表进行切片
    paginated_data = data_list[start:end]
    has_next = True if math.ceil(len(data_list) / page_size) > page_num else False

    result = dict(
        rows=paginated_data,
        page_num=page_num,
        page_size=page_size,
        total=len(data_list),
        has_next=has_next
    )

    return PageObjectResponse(**result)



dash-fastapi-backend/utils/page_util.py
insistence/Dash-FastAPI-Admin
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


class PwdUtil:
    """
    密码工具类
    """

    @classmethod
    def verify_password(cls, plain_password, hashed_password):
        """
        工具方法：校验当前输入的密码与数据库存储的密码是否一致
        :param plain_password: 当前输入的密码
        :param hashed_password: 数据库存储的密码
        :return: 校验结果
        """
        return pwd_context.verify(plain_password, hashed_password)

    @classmethod
    def get_password_hash(cls, input_password):
        """
        工具方法：对当前输入的密码进行加密
        :param input_password: 输入的密码
        :return: 加密成功的密码
        """
        return pwd_context.hash(input_password)

dash-fastapi-backend/utils/pwd_util.py
insistence/Dash-FastAPI-Admin
from fastapi import status
from fastapi.responses import JSONResponse, Response, StreamingResponse
from fastapi.encoders import jsonable_encoder
from typing import Any
from datetime import datetime


def response_200(*, data: Any = None, message="获取成功") -> Response:
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content=jsonable_encoder(
            {
                'code': 200,
                'message': message,
                'data': data,
                'success': 'true',
                'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
        )
    )


def response_400(*, data: Any = None, message: str = "获取失败") -> Response:
    return JSONResponse(
        status_code=status.HTTP_400_BAD_REQUEST,
        content=jsonable_encoder(
            {
                'code': 400,
                'message': message,
                'data': data,
                'success': 'false',
                'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
        )
    )


def response_401(*, data: Any = None, message: str = "获取失败") -> Response:
    return JSONResponse(
        status_code=status.HTTP_401_UNAUTHORIZED,
        content=jsonable_encoder(
            {
                'code': 401,
                'message': message,
                'data': data,
                'success': 'false',
                'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
        )
    )


def response_500(*, data: Any = None, message: str = "接口异常") -> Response:
    return JSONResponse(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        content=jsonable_encoder(
            {
                'code': 500,
                'message': message,
                'data': data,
                'success': 'false',
                'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
        )
    )


def streaming_response_200(*, data: Any = None):
    return StreamingResponse(
        status_code=status.HTTP_200_OK,
        content=data,
    )
    
    
class AuthException(Exception):
    """
    自定义令牌异常AuthException
    """
    def __init__(self, data: str = None, message: str = None):
        self.data = data
        self.message = message


class LoginException(Exception):
    """
    自定义登录异常LoginException
    """
    def __init__(self, data: str = None, message: str = None):
        self.data = data
        self.message = message

dash-fastapi-backend/utils/response_util.py
insistence/Dash-FastAPI-Admin
import datetime


def object_format_datetime(obj):
    """
    :param obj: 输入一个对象
    :return:对目标对象所有datetime类型的属性格式化
    """
    for attr in dir(obj):
        value = getattr(obj, attr)
        if isinstance(value, datetime.datetime):
            setattr(obj, attr, value.strftime('%Y-%m-%d %H:%M:%S'))
    return obj


def list_format_datetime(lst):
    """
    :param lst: 输入一个嵌套对象的列表
    :return: 对目标列表中所有对象的datetime类型的属性格式化
    """
    for obj in lst:
        for attr in dir(obj):
            value = getattr(obj, attr)
            if isinstance(value, datetime.datetime):
                setattr(obj, attr, value.strftime('%Y-%m-%d %H:%M:%S'))
    return lst


def format_datetime_dict_list(dicts):
    """
    递归遍历嵌套字典，并将 datetime 值转换为字符串格式
    :param dicts: 输入一个嵌套字典的列表
    :return: 对目标列表中所有字典的datetime类型的属性格式化
    """
    result = []

    for item in dicts:
        new_item = {}
        for k, v in item.items():
            if isinstance(v, dict):
                # 递归遍历子字典
                new_item[k] = format_datetime_dict_list([v])[0]
            elif isinstance(v, datetime.datetime):
                # 如果值是 datetime 类型，则格式化为字符串
                new_item[k] = v.strftime('%Y-%m-%d %H:%M:%S')
            else:
                # 否则保留原始值
                new_item[k] = v
        result.append(new_item)

    return result

dash-fastapi-backend/utils/time_format_util.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_cache_statistical_info_api():

    return api_request(method='post', url='/monitor/cache/statisticalInfo', is_headers=True)

dash-fastapi-frontend/api/cache.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_config_list_api(page_obj: dict):

    return api_request(method='post', url='/system/config/get', is_headers=True, json=page_obj)


def query_config_list_api(config_key: str):

    return api_request(method='get', url=f'/common/config/query/{config_key}', is_headers=False)


def add_config_api(page_obj: dict):

    return api_request(method='post', url='/system/config/add', is_headers=True, json=page_obj)


def edit_config_api(page_obj: dict):

    return api_request(method='patch', url='/system/config/edit', is_headers=True, json=page_obj)


def delete_config_api(page_obj: dict):

    return api_request(method='post', url='/system/config/delete', is_headers=True, json=page_obj)


def get_config_detail_api(config_id: int):

    return api_request(method='get', url=f'/system/config/{config_id}', is_headers=True)


def export_config_list_api(page_obj: dict):

    return api_request(method='post', url='/system/config/export', is_headers=True, json=page_obj, stream=True)


def refresh_config_api(page_obj: dict):

    return api_request(method='post', url='/system/config/refresh', is_headers=True, json=page_obj)

dash-fastapi-frontend/api/config.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_dept_tree_api(page_obj: dict):

    return api_request(method='post', url='/system/dept/tree', is_headers=True, json=page_obj)


def get_dept_tree_for_edit_option_api(page_obj: dict):

    return api_request(method='post', url='/system/dept/forEditOption', is_headers=True, json=page_obj)


def get_dept_list_api(page_obj: dict):

    return api_request(method='post', url='/system/dept/get', is_headers=True, json=page_obj)


def add_dept_api(page_obj: dict):

    return api_request(method='post', url='/system/dept/add', is_headers=True, json=page_obj)


def edit_dept_api(page_obj: dict):

    return api_request(method='patch', url='/system/dept/edit', is_headers=True, json=page_obj)


def delete_dept_api(page_obj: dict):

    return api_request(method='post', url='/system/dept/delete', is_headers=True, json=page_obj)


def get_dept_detail_api(dept_id: int):

    return api_request(method='get', url=f'/system/dept/{dept_id}', is_headers=True)

dash-fastapi-frontend/api/dept.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_dict_type_list_api(page_obj: dict):

    return api_request(method='post', url='/system/dictType/get', is_headers=True, json=page_obj)


def get_all_dict_type_api(page_obj: dict):

    return api_request(method='post', url='/system/dictType/all', is_headers=True, json=page_obj)


def add_dict_type_api(page_obj: dict):

    return api_request(method='post', url='/system/dictType/add', is_headers=True, json=page_obj)


def edit_dict_type_api(page_obj: dict):

    return api_request(method='patch', url='/system/dictType/edit', is_headers=True, json=page_obj)


def delete_dict_type_api(page_obj: dict):

    return api_request(method='post', url='/system/dictType/delete', is_headers=True, json=page_obj)


def get_dict_type_detail_api(dict_id: int):

    return api_request(method='get', url=f'/system/dictType/{dict_id}', is_headers=True)


def export_dict_type_list_api(page_obj: dict):

    return api_request(method='post', url='/system/dictType/export', is_headers=True, json=page_obj, stream=True)


def refresh_dict_api(page_obj: dict):

    return api_request(method='post', url='/system/dictType/refresh', is_headers=True, json=page_obj)


def get_dict_data_list_api(page_obj: dict):

    return api_request(method='post', url='/system/dictData/get', is_headers=True, json=page_obj)


def query_dict_data_list_api(dict_type: str):

    return api_request(method='get', url=f'/system/dictData/query/{dict_type}', is_headers=True)


def add_dict_data_api(page_obj: dict):

    return api_request(method='post', url='/system/dictData/add', is_headers=True, json=page_obj)


def edit_dict_data_api(page_obj: dict):

    return api_request(method='patch', url='/system/dictData/edit', is_headers=True, json=page_obj)


def delete_dict_data_api(page_obj: dict):

    return api_request(method='post', url='/system/dictData/delete', is_headers=True, json=page_obj)


def get_dict_data_detail_api(dict_code: int):

    return api_request(method='get', url=f'/system/dictData/{dict_code}', is_headers=True)


def export_dict_data_list_api(page_obj: dict):

    return api_request(method='post', url='/system/dictData/export', is_headers=True, json=page_obj, stream=True)

dash-fastapi-frontend/api/dict.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_job_list_api(page_obj: dict):

    return api_request(method='post', url='/monitor/job/get', is_headers=True, json=page_obj)


def add_job_api(page_obj: dict):

    return api_request(method='post', url='/monitor/job/add', is_headers=True, json=page_obj)


def edit_job_api(page_obj: dict):

    return api_request(method='patch', url='/monitor/job/edit', is_headers=True, json=page_obj)


def execute_job_api(page_obj: dict):

    return api_request(method='post', url='/monitor/job/changeStatus', is_headers=True, json=page_obj)


def delete_job_api(page_obj: dict):

    return api_request(method='post', url='/monitor/job/delete', is_headers=True, json=page_obj)


def get_job_detail_api(job_id: int):

    return api_request(method='get', url=f'/monitor/job/{job_id}', is_headers=True)


def export_job_list_api(page_obj: dict):

    return api_request(method='post', url='/monitor/job/export', is_headers=True, json=page_obj, stream=True)


def get_job_log_list_api(page_obj: dict):

    return api_request(method='post', url='/monitor/jobLog/get', is_headers=True, json=page_obj)


def delete_job_log_api(page_obj: dict):

    return api_request(method='post', url='/monitor/jobLog/delete', is_headers=True, json=page_obj)


def clear_job_log_api(page_obj: dict):

    return api_request(method='post', url='/monitor/jobLog/clear', is_headers=True, json=page_obj)


def get_job_log_detail_api(job_log_id: int):

    return api_request(method='get', url=f'/monitor/jobLog/{job_log_id}', is_headers=True)


def export_job_log_list_api(page_obj: dict):

    return api_request(method='post', url='/monitor/jobLog/export', is_headers=True, json=page_obj, stream=True)

dash-fastapi-frontend/api/job.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_operation_log_list_api(page_obj: dict):

    return api_request(method='post', url='/system/log/operation/get', is_headers=True, json=page_obj)


def delete_operation_log_api(page_obj: dict):

    return api_request(method='post', url='/system/log/operation/delete', is_headers=True, json=page_obj)


def clear_operation_log_api(page_obj: dict):

    return api_request(method='post', url='/system/log/operation/clear', is_headers=True, json=page_obj)


def export_operation_log_list_api(page_obj: dict):

    return api_request(method='post', url='/system/log/operation/export', is_headers=True, json=page_obj, stream=True)


def get_operation_log_detail_api(oper_id: int):

    return api_request(method='get', url=f'/system/log/operation/{oper_id}', is_headers=True)


def get_login_log_list_api(page_obj: dict):

    return api_request(method='post', url='/system/log/login/get', is_headers=True, json=page_obj)


def delete_login_log_api(page_obj: dict):

    return api_request(method='post', url='/system/log/login/delete', is_headers=True, json=page_obj)


def clear_login_log_api(page_obj: dict):

    return api_request(method='post', url='/system/log/login/clear', is_headers=True, json=page_obj)


def unlock_user_api(page_obj: dict):

    return api_request(method='post', url='/system/log/login/unlock', is_headers=True, json=page_obj)


def export_login_log_list_api(page_obj: dict):

    return api_request(method='post', url='/system/log/login/export', is_headers=True, json=page_obj, stream=True)

dash-fastapi-frontend/api/log.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def login_api(page_obj: dict):

    return api_request(method='post', url='/login/loginByAccount', is_headers=False, data=page_obj)


def get_captcha_image_api():

    return api_request(method='post', url='/captcha/captchaImage', is_headers=False)


def get_current_user_info_api():

    return api_request(method='post', url='/login/getLoginUserInfo', is_headers=True)


def logout_api():
    return api_request(method='post', url='/login/logout', is_headers=True)

dash-fastapi-frontend/api/login.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_menu_tree_api(page_obj: dict):

    return api_request(method='post', url='/system/menu/tree', is_headers=True, json=page_obj)


def get_menu_tree_for_edit_option_api(page_obj: dict):

    return api_request(method='post', url='/system/menu/forEditOption', is_headers=True, json=page_obj)


def get_menu_list_api(page_obj: dict):

    return api_request(method='post', url='/system/menu/get', is_headers=True, json=page_obj)


def add_menu_api(page_obj: dict):

    return api_request(method='post', url='/system/menu/add', is_headers=True, json=page_obj)


def edit_menu_api(page_obj: dict):

    return api_request(method='patch', url='/system/menu/edit', is_headers=True, json=page_obj)


def delete_menu_api(page_obj: dict):

    return api_request(method='post', url='/system/menu/delete', is_headers=True, json=page_obj)


def get_menu_detail_api(menu_id: int):

    return api_request(method='get', url=f'/system/menu/{menu_id}', is_headers=True)

dash-fastapi-frontend/api/menu.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def send_message_api(page_obj: dict):

    return api_request(method='post', url='/login/getSmsCode', is_headers=False, json=page_obj)
dash-fastapi-frontend/api/message.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_notice_list_api(page_obj: dict):

    return api_request(method='post', url='/system/notice/get', is_headers=True, json=page_obj)


def add_notice_api(page_obj: dict):

    return api_request(method='post', url='/system/notice/add', is_headers=True, json=page_obj)


def edit_notice_api(page_obj: dict):

    return api_request(method='patch', url='/system/notice/edit', is_headers=True, json=page_obj)


def delete_notice_api(page_obj: dict):

    return api_request(method='post', url='/system/notice/delete', is_headers=True, json=page_obj)


def get_notice_detail_api(notice_id: int):

    return api_request(method='get', url=f'/system/notice/{notice_id}', is_headers=True)

dash-fastapi-frontend/api/notice.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_online_list_api(page_obj: dict):

    return api_request(method='post', url='/monitor/online/get', is_headers=True, json=page_obj)


def force_logout_online_api(page_obj: dict):

    return api_request(method='post', url='/monitor/online/forceLogout', is_headers=True, json=page_obj)


def batch_logout_online_api(page_obj: dict):

    return api_request(method='post', url='/monitor/online/batchLogout', is_headers=True, json=page_obj)

dash-fastapi-frontend/api/online.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_post_select_option_api():

    return api_request(method='post', url='/system/post/forSelectOption', is_headers=True)


def get_post_list_api(page_obj: dict):

    return api_request(method='post', url='/system/post/get', is_headers=True, json=page_obj)


def add_post_api(page_obj: dict):

    return api_request(method='post', url='/system/post/add', is_headers=True, json=page_obj)


def edit_post_api(page_obj: dict):

    return api_request(method='patch', url='/system/post/edit', is_headers=True, json=page_obj)


def delete_post_api(page_obj: dict):

    return api_request(method='post', url='/system/post/delete', is_headers=True, json=page_obj)


def get_post_detail_api(post_id: int):

    return api_request(method='get', url=f'/system/post/{post_id}', is_headers=True)


def export_post_list_api(page_obj: dict):

    return api_request(method='post', url='/system/post/export', is_headers=True, json=page_obj, stream=True)

dash-fastapi-frontend/api/post.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_role_select_option_api():

    return api_request(method='post', url='/system/role/forSelectOption', is_headers=True)


def get_role_list_api(page_obj: dict):

    return api_request(method='post', url='/system/role/get', is_headers=True, json=page_obj)


def add_role_api(page_obj: dict):

    return api_request(method='post', url='/system/role/add', is_headers=True, json=page_obj)


def edit_role_api(page_obj: dict):

    return api_request(method='patch', url='/system/role/edit', is_headers=True, json=page_obj)


def role_datascope_api(page_obj: dict):

    return api_request(method='patch', url='/system/role/dataScope', is_headers=True, json=page_obj)


def delete_role_api(page_obj: dict):

    return api_request(method='post', url='/system/role/delete', is_headers=True, json=page_obj)


def get_role_detail_api(role_id: int):

    return api_request(method='get', url=f'/system/role/{role_id}', is_headers=True)


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)

dash-fastapi-frontend/api/role.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def get_server_statistical_info_api():

    return api_request(method='post', url='/monitor/server/statisticalInfo', is_headers=True)

dash-fastapi-frontend/api/server.py
insistence/Dash-FastAPI-Admin
from utils.request import api_request


def forget_user_pwd_api(page_obj: dict):

    return api_request(method='post', url='/login/forgetPwd', is_headers=False, json=page_obj)


def get_user_list_api(page_obj: dict):

    return api_request(method='post', url='/system/user/get', is_headers=True, json=page_obj)


def add_user_api(page_obj: dict):

    return api_request(method='post', url='/system/user/add', is_headers=True, json=page_obj)


def edit_user_api(page_obj: dict):

    return api_request(method='patch', url='/system/user/edit', is_headers=True, json=page_obj)


def delete_user_api(page_obj: dict):

    return api_request(method='post', url='/system/user/delete', is_headers=True, json=page_obj)


def get_user_detail_api(user_id: int):

    return api_request(method='get', url=f'/system/user/{user_id}', is_headers=True)


def change_user_avatar_api(page_obj: dict):

    return api_request(method='patch', url='/system/user/profile/changeAvatar', is_headers=True, json=page_obj)


def change_user_info_api(page_obj: dict):

    return api_request(method='patch', url='/system/user/profile/changeInfo', is_headers=True, json=page_obj)


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)



dash-fastapi-frontend/api/user.py
insistence/Dash-FastAPI-Admin
import dash
import time
from dash import html, dcc
from dash.dependencies import Input, Output, State
import feffery_antd_components as fac
import feffery_utils_components as fuc
from flask import session

from server import app
from config.global_config import RouterConfig
from store.store import render_store_container

# 载入子页面
import views

from callbacks import app_c
from api.login import get_current_user_info_api
from utils.tree_tool import find_node_values, find_key_by_href, deal_user_menu_info, get_search_panel_data

app.layout = html.Div(
    [
        # 注入url监听
        fuc.FefferyLocation(id='url-container'),
        # 用于回调pathname信息
        dcc.Location(id='dcc-url', refresh=False),

        # 注入js执行容器
        fuc.FefferyExecuteJs(
            id='execute-js-container'
        ),

        # 注入页面内容挂载点
        html.Div(id='app-mount'),

        # 注入全局配置容器
        fac.AntdConfigProvider(id='app-config-provider'),

        # 注入快捷搜索面板
        fuc.FefferyShortcutPanel(
            id='search-panel',
            data=[],
            placeholder='输入你想要搜索的菜单...',
            panelStyles={
                'accentColor': '#1890ff',
                'zIndex': 99999
            }
        ),

        # 辅助处理多输入 -> 存储接口返回token校验信息
        render_store_container(),

        # 重定向容器
        html.Div(id='redirect-container'),

        # 登录消息失效对话框提示
        fac.AntdModal(
            html.Div(
                [
                    fac.AntdIcon(icon='fc-high-priority', style={'font-size': '28px'}),
                    fac.AntdText('用户信息已过期，请重新登录', style={'margin-left': '5px'}),
                ]
            ),
            id='token-invalid-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),

        # 注入全局消息提示容器
        html.Div(id='global-message-container'),
        # 注入全局通知信息容器
        html.Div(id='global-notification-container')
    ]
)


@app.callback(
    output=dict(
        app_mount=Output('app-mount', 'children'),
        redirect_container=Output('redirect-container', 'children', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        menu_current_key=Output('current-key-container', 'data'),
        menu_info=Output('menu-info-store-container', 'data'),
        menu_list=Output('menu-list-store-container', 'data'),
        search_panel_data=Output('search-panel', 'data')
    ),
    inputs=dict(pathname=Input('url-container', 'pathname')),
    state=dict(
        url_trigger=State('url-container', 'trigger'),
        session_token=State('token-container', 'data')
    ),
    prevent_initial_call=True
)
def router(pathname, url_trigger, session_token):
    """
    全局路由回调
    """
    # 检查当前会话是否已经登录
    token_result = session.get('Authorization')
    # 若已登录
    if token_result and session_token and token_result == session_token:
        try:
            current_user_result = get_current_user_info_api()
            if current_user_result['code'] == 200:
                current_user = current_user_result['data']
                menu_list = current_user['menu']
                user_menu_list = [item for item in menu_list if item.get('visible') == '0']
                menu_info = deal_user_menu_info(0, menu_list)
                user_menu_info = deal_user_menu_info(0, user_menu_list)
                search_panel_data = get_search_panel_data(user_menu_list)
                session['user_info'] = current_user['user']
                session['dept_info'] = current_user['dept']
                session['role_info'] = current_user['role']
                session['post_info'] = current_user['post']
                dynamic_valid_pathname_list = find_node_values(menu_info, 'href')
                valid_href_list = dynamic_valid_pathname_list + RouterConfig.STATIC_VALID_PATHNAME
                if pathname in valid_href_list:
                    current_key = find_key_by_href(menu_info, pathname)
                    if pathname == '/':
                        current_key = '首页'
                    if pathname == '/user/profile':
                        current_key = '个人资料'
                    if url_trigger == 'load':

                        # 根据pathname控制渲染行为
                        if pathname == '/login' or pathname == '/forget':
                            # 重定向到主页面
                            return dict(
                                app_mount=dash.no_update,
                                redirect_container=dcc.Location(pathname='/', id='router-redirect'),
                                global_message_container=None,
                                api_check_token_trigger={'timestamp': time.time()},
                                menu_current_key={'current_key': current_key},
                                menu_info={'menu_info': menu_info},
                                menu_list={'menu_list': menu_list},
                                search_panel_data=search_panel_data
                            )

                        # 否则正常渲染主页面
                        return dict(
                            app_mount=views.layout.render_content(user_menu_info),
                            redirect_container=None,
                            global_message_container=None,
                            api_check_token_trigger={'timestamp': time.time()},
                            menu_current_key={'current_key': current_key},
                            menu_info={'menu_info': menu_info},
                            menu_list={'menu_list': menu_list},
                            search_panel_data=search_panel_data
                        )

                    else:
                        return dict(
                            app_mount=dash.no_update,
                            redirect_container=None,
                            global_message_container=None,
                            api_check_token_trigger={'timestamp': time.time()},
                            menu_current_key={'current_key': current_key},
                            menu_info={'menu_info': menu_info},
                            menu_list={'menu_list': menu_list},
                            search_panel_data=search_panel_data
                        )

                else:
                    # 渲染404状态页
                    return dict(
                        app_mount=views.page_404.render_content(),
                        redirect_container=None,
                        global_message_container=None,
                        api_check_token_trigger={'timestamp': time.time()},
                        menu_current_key=dash.no_update,
                        menu_info=dash.no_update,
                        menu_list=dash.no_update,
                        search_panel_data=dash.no_update
                    )

            else:
                return dict(
                    app_mount=dash.no_update,
                    redirect_container=dash.no_update,
                    global_message_container=dash.no_update,
                    api_check_token_trigger={'timestamp': time.time()},
                    menu_current_key=dash.no_update,
                    menu_info=dash.no_update,
                    menu_list=dash.no_update,
                    search_panel_data=dash.no_update
                )

        except Exception as e:
            print(e)

            return dict(
                app_mount=dash.no_update,
                redirect_container=None,
                global_message_container=fuc.FefferyFancyNotification('接口异常', type='error', autoClose=2000),
                api_check_token_trigger={'timestamp': time.time()},
                menu_current_key=dash.no_update,
                menu_info=dash.no_update,
                menu_list=dash.no_update,
                search_panel_data=dash.no_update
            )
    else:
        # 若未登录
        # 根据pathname控制渲染行为
        # 检验pathname合法性
        if pathname not in RouterConfig.BASIC_VALID_PATHNAME:
            # 渲染404状态页
            return dict(
                app_mount=views.page_404.render_content(),
                redirect_container=None,
                global_message_container=None,
                api_check_token_trigger={'timestamp': time.time()},
                menu_current_key=dash.no_update,
                menu_info=dash.no_update,
                menu_list=dash.no_update,
                search_panel_data=dash.no_update
            )

        if pathname == '/login':
            return dict(
                app_mount=views.login.render_content(),
                redirect_container=None,
                global_message_container=None,
                api_check_token_trigger={'timestamp': time.time()},
                menu_current_key=dash.no_update,
                menu_info=dash.no_update,
                menu_list=dash.no_update,
                search_panel_data=dash.no_update
            )

        if pathname == '/forget':
            return dict(
                app_mount=views.forget.render_forget_content(),
                redirect_container=None,
                global_message_container=None,
                api_check_token_trigger={'timestamp': time.time()},
                menu_current_key=dash.no_update,
                menu_info=dash.no_update,
                menu_list=dash.no_update,
                search_panel_data=dash.no_update
            )

        # 否则重定向到登录页
        return dict(
            app_mount=dash.no_update,
            redirect_container=dcc.Location(pathname='/login', id='router-redirect'),
            global_message_container=None,
            api_check_token_trigger={'timestamp': time.time()},
            menu_current_key=dash.no_update,
            menu_info=dash.no_update,
            menu_list=dash.no_update,
            search_panel_data=dash.no_update
        )


if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8088, debug=True)

dash-fastapi-frontend/app.py
insistence/Dash-FastAPI-Admin
import dash
from dash import dcc
import feffery_utils_components as fuc
from dash.dependencies import Input, Output, State
from flask import session
from server import app
from api.config import query_config_list_api


# api拦截器——根据api返回编码确定是否强制退出
@app.callback(
    [Output('token-invalid-modal', 'visible'),
     Output('global-notification-container', 'children', allow_duplicate=True)],
    Input('api-check-token', 'data'),
    prevent_initial_call=True
)
def check_api_response(data):

    if session.get('code') == 401 and 'token' in session.get('message'):
        return [True, fuc.FefferyFancyNotification(session.get('message'), type='error', autoClose=2000)]

    elif session.get('code') == 200:
        return [dash.no_update, dash.no_update]

    else:
        return [dash.no_update, fuc.FefferyFancyNotification(session.get('message'), type='warning', autoClose=2000)]


# api拦截器——退出登录二次确认
@app.callback(
    Output('redirect-container', 'children', allow_duplicate=True),
    Input('token-invalid-modal', 'okCounts'),
    prevent_initial_call=True
)
def redirect_page(okCounts):

    if okCounts:
        session.clear()

        return [
            dcc.Location(
                pathname='/login',
                id='index-redirect'
            )
        ]

    return dash.no_update


# 应用初始化主题颜色
@app.callback(
    Output('system-app-primary-color-container', 'data'),
    Input('app-mount', 'id'),
    State('custom-app-primary-color-container', 'data')
)
def get_primary_color(_, custom_color):
    if not custom_color:
        primary_color_res = query_config_list_api(config_key='sys.index.skinName')
        if primary_color_res.get('code') == 200:
            primary_color = primary_color_res.get('data')

            return primary_color

    return dash.no_update


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
)

dash-fastapi-frontend/callbacks/app_c.py
insistence/Dash-FastAPI-Admin
import dash
from dash import dcc
import feffery_utils_components as fuc
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

from server import app
from api.user import forget_user_pwd_api
from api.message import send_message_api


@app.callback(
    output=dict(
        username_form_status=Output('forget-username-form-item', 'validateStatus'),
        password_form_status=Output('forget-password-form-item', 'validateStatus'),
        password_again_form_status=Output('forget-password-again-form-item', 'validateStatus'),
        captcha_form_status=Output('forget-captcha-form-item', 'validateStatus'),
        username_form_help=Output('forget-username-form-item', 'help'),
        password_form_help=Output('forget-password-form-item', 'help'),
        password_again_form_help=Output('forget-password-again-form-item', 'help'),
        captcha_form_help=Output('forget-captcha-form-item', 'help'),
        submit_loading=Output('forget-submit', 'loading'),
        redirect_container=Output('redirect-container', 'children', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        nClicks=Input('forget-submit', 'nClicks')
    ),
    state=dict(
        username=State('forget-username', 'value'),
        password=State('forget-password', 'value'),
        password_again=State('forget-password-again', 'value'),
        input_captcha=State('forget-input-captcha', 'value'),
        session_id=State('sms_code-session_id-container', 'data')
    ),
    prevent_initial_call=True
)
def forget_auth(nClicks, username, password, password_again, input_captcha, session_id):
    if nClicks:
    # 校验全部输入值是否不为空
        if all([username, password, password_again, input_captcha]):

            if password == password_again:
                try:
                    forget_params = dict(user_name=username, password=password, sms_code=input_captcha, session_id=session_id)
                    change_result = forget_user_pwd_api(forget_params)
                    if change_result.get('code') == 200:

                        return dict(
                            username_form_status=None,
                            password_form_status=None,
                            password_again_form_status=None,
                            captcha_form_status=None,
                            username_form_help=None,
                            password_form_help=None,
                            password_again_form_help=None,
                            captcha_form_help=None,
                            submit_loading=False,
                            redirect_container=dcc.Location(pathname='/login', id='forget-redirect'),
                            global_message_container=fuc.FefferyFancyMessage(change_result.get('message'), type='success')
                        )

                    else:

                        return dict(
                            username_form_status=None,
                            password_form_status=None,
                            password_again_form_status=None,
                            captcha_form_status=None,
                            username_form_help=None,
                            password_form_help=None,
                            password_again_form_help=None,
                            captcha_form_help=None,
                            submit_loading=False,
                            redirect_container=None,
                            global_message_container=fuc.FefferyFancyMessage(change_result.get('message'), type='error')
                        )
                except Exception as e:

                    return dict(
                        username_form_status=None,
                        password_form_status=None,
                        password_again_form_status=None,
                        captcha_form_status=None,
                        username_form_help=None,
                        password_form_help=None,
                        password_again_form_help=None,
                        captcha_form_help=None,
                        submit_loading=False,
                        redirect_container=None,
                        global_message_container=fuc.FefferyFancyMessage(str(e), type='error')
                    )

            else:
                return dict(
                    username_form_status=None,
                    password_form_status='error',
                    password_again_form_status='error',
                    captcha_form_status=None,
                    username_form_help=None,
                    password_form_help='两次密码不一致',
                    password_again_form_help='两次密码不一致',
                    captcha_form_help=None,
                    submit_loading=False,
                    redirect_container=None,
                    global_message_container=None
                )

        return dict(
            username_form_status=None if username else 'error',
            password_form_status=None if password else 'error',
            password_again_form_status=None if password_again else 'error',
            captcha_form_status=None if input_captcha else 'error',
            username_form_help=None if username else '请输入用户名！',
            password_form_help=None if password else '请输入新密码！',
            password_again_form_help=None if password_again else '请再次输入新密码！',
            captcha_form_help=None if input_captcha else '请输入短信验证码！',
            submit_loading=False,
            redirect_container=None,
            global_message_container=None
        )

    raise PreventUpdate


@app.callback(
    [Output('message-code-count-down', 'delay'),
     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'),
    [State('forget-username', 'value'),
     State('sms_code-session_id-container', 'data')],
    prevent_initial_call=True
)
def message_countdown(nClicks, username, session_id):
    if nClicks:

        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')
                    ]

            else:
                return [
                    dash.no_update,
                    False,
                    dash.no_update,
                    fuc.FefferyFancyMessage('请输入用户名', type='error')
                ]

        except Exception as e:

            return [
                dash.no_update,
                False,
                dash.no_update,
                fuc.FefferyFancyMessage(str(e), type='error')
            ]

    return [dash.no_update] * 4


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
)


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
)

dash-fastapi-frontend/callbacks/forget_c.py
insistence/Dash-FastAPI-Admin
from dash.dependencies import Input, Output

from server import app


# url-pathname控制currentKey回调
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
)

dash-fastapi-frontend/callbacks/layout_c/aside_c.py
insistence/Dash-FastAPI-Admin
from dash.dependencies import Input, Output, State

from server import app


# 侧边栏折叠回调
app.clientside_callback(
    '''
    (nClicks, collapsed) => {
            if (nClicks) {
                return [
                        collapsed ? {'width': 210} : {'width': 60},
                        !collapsed,
                        collapsed ? {'fontSize': '22px', 'color': 'rgb(255, 255, 255)'} : {'display': 'none'},
                        collapsed ? 'antd-menu-fold' : 'antd-menu-unfold',
                    ];
            }
            return window.dash_clientside.no_update;
    }
    ''',
    [Output('left-side-menu-container', 'style'),
     Output('menu-collapse-sider-custom', 'collapsed'),
     Output('logo-text', 'style'),
     Output('fold-side-menu-icon', 'icon')],
    Input('fold-side-menu', 'nClicks'),
    State('menu-collapse-sider-custom', 'collapsed'),
    prevent_initial_call=True
)

dash-fastapi-frontend/callbacks/layout_c/fold_side_menu.py
insistence/Dash-FastAPI-Admin
import dash
from dash import dcc
import feffery_utils_components as fuc
from flask import session
from dash.dependencies import Input, Output, State

from server import app
from api.login import logout_api


# 页首右侧个人中心选项卡回调
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')],
    Input('index-header-dropdown', 'nClicks'),
    State('index-header-dropdown', 'clickedKey'),
    prevent_initial_call=True
)


# 退出登录回调
@app.callback(
    Output('redirect-container', 'children', allow_duplicate=True),
    Input('logout-modal', 'okCounts'),
    prevent_initial_call=True
)
def logout_confirm(okCounts):
    if okCounts:
        result = logout_api()
        if result['code'] == 200:
            session.clear()

            return [
                dcc.Location(
                    pathname='/login',
                    id='index-redirect'
                ),
            ]

    return dash.no_update


# 全局页面重载回调
app.clientside_callback(
    '''
    (nClicks) => {
        return true;
    }
    ''',
    Output('trigger-reload-output', 'reload', allow_duplicate=True),
    Input('index-reload', 'nClicks'),
    prevent_initial_call=True
)


# 布局设置回调
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
)


@app.callback(
    [Output('selected-color-input', 'value'),
     Output('selected-color-input', 'style')],
    Input('hex-color-picker', 'color'),
    State('selected-color-input', 'style'),
    prevent_initial_call=True
)
def show_selected_color(pick_color, old_style):

    return [
        pick_color,
        {
            **old_style,
            'background': pick_color
        }
    ]


@app.callback(
    [Output('custom-app-primary-color-container', 'data'),
     Output('hex-color-picker', 'color', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    [Input('save-setting', 'nClicks'),
     Input('reset-setting', 'nClicks')],
    [State('selected-color-input', 'value'),
     State('system-app-primary-color-container', 'data')],
    prevent_initial_call=True
)
def save_rest_layout_setting(save_click, reset_click, picked_color, system_color):
    if save_click or reset_click:
        trigger_id = dash.ctx.triggered_id
        if trigger_id == 'save-setting':

            return [
                picked_color,
                dash.no_update,
                fuc.FefferyFancyMessage('保存成功', type='success')
            ]

        elif trigger_id == 'reset-setting':

            return [
                None,
                system_color,
                fuc.FefferyFancyMessage('重置成功', type='success')
            ]

    return [
        dash.no_update,
        dash.no_update,
        dash.no_update
    ]


dash-fastapi-frontend/callbacks/layout_c/head_c.py
insistence/Dash-FastAPI-Admin
import dash
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import feffery_antd_components as fac
from jsonpath_ng import parse
from flask import json
from collections import OrderedDict

from server import app
import views
from utils.tree_tool import find_title_by_key, find_modules_by_key, find_href_by_key,  find_parents


@app.callback(
    [Output('tabs-container', 'items', allow_duplicate=True),
     Output('tabs-container', 'activeKey', allow_duplicate=True)],
    [Input('index-side-menu', 'currentKey'),
     Input('tabs-container', 'latestDeletePane')],
    [State('tabs-container', 'items'),
     State('tabs-container', 'activeKey'),
     State('menu-info-store-container', 'data'),
     State('menu-list-store-container', 'data')],
    prevent_initial_call=True
)
def handle_tab_switch_and_create(currentKey, latestDeletePane, origin_items, activeKey, menu_info, menu_list):
    """
    这个回调函数用于处理标签页子项的新建、切换及删除
    具体策略：
        1.当左侧某个菜单项被新选中，且右侧标签页子项尚未包含此项时，新建并切换
        2.当左侧某个菜单项被新选中，且右侧标签页子项已包含此项时，切换
        3.当右侧标签页子项某项被删除时，销毁对应标签页的同时切换回最后新增的标签页
    """

    trigger_id = dash.ctx.triggered_id

    # 基于jsonpath对各标签页子项中所有已有记录的nClicks参数重置为None
    # 以避免每次新的items返回给标签页重新渲染后，
    # 先前已更新为非None的按钮的nClicks误触发通知弹出回调
    parser = parse('$..nClicks')
    origin_items = parser.update(origin_items, None)
    new_items = dash.Patch()

    if trigger_id == 'index-side-menu':

        # 判断当前新选中的菜单栏项对应标签页是否已创建
        if currentKey in [item['key'] for item in origin_items]:
            return [
                dash.no_update,
                currentKey
            ]

        if currentKey == '个人资料':
            menu_title = '个人资料'
            button_perms = []
            menu_modules = 'system.user.profile'
        else:
            menu_title = find_title_by_key(menu_info.get('menu_info'), currentKey)
            button_perms = [item.get('perms') for item in menu_list.get('menu_list') if str(item.get('parent_id')) == currentKey]
            # 判断当前选中的菜单栏项是否存在module，如果有，则动态导入module，否则返回404页面
            menu_modules = find_modules_by_key(menu_info.get('menu_info'), currentKey)

        for index, item in enumerate(origin_items):
            if {'key': '关闭右侧', 'label': '关闭右侧', 'icon': 'antd-arrow-right'} not in item['contextMenu']:
                item['contextMenu'].insert(-1, {
                    'key': '关闭右侧',
                    'label': '关闭右侧',
                    'icon': 'antd-arrow-right'
                })
            new_items[index]['contextMenu'] = item['contextMenu']
        context_menu = [
            {
                'key': '刷新页面',
                'label': '刷新页面',
                'icon': 'antd-reload'
            },
            {
                'key': '关闭当前',
                'label': '关闭当前',
                'icon': 'antd-close'
            },
            {
                'key': '关闭其他',
                'label': '关闭其他',
                'icon': 'antd-close-circle'
            },
            {
                'key': '全部关闭',
                'label': '全部关闭',
                'icon': 'antd-close-circle'
            }
        ]
        if len(origin_items) != 1:
            context_menu.insert(-1, {
                'key': '关闭左侧',
                'label': '关闭左侧',
                'icon': 'antd-arrow-left'
            })
        if menu_modules:
            if menu_modules == 'link':
                raise PreventUpdate
            else:
                # 否则追加子项返回
                # 其中若各标签页内元素类似，则推荐配合模式匹配构建交互逻辑
                new_items.append(
                    {
                        'label': menu_title,
                        'key': currentKey,
                        'children': eval('views.' + menu_modules + '.render(button_perms)'),
                        'contextMenu': context_menu
                    }
                )
        else:
            new_items.append(
                {
                    'label': menu_title,
                    'key': currentKey,
                    'children': fac.AntdResult(
                        status='404',
                        title='页面不存在',
                        subTitle='请先配置该路由的页面',
                        style={
                            'paddingBottom': 0,
                            'paddingTop': 0
                        }
                    ),
                    'contextMenu': context_menu
                }
            )

        return [
            new_items,
            currentKey
        ]

    elif trigger_id == 'tabs-container':

        # 如果删除的是当前标签页则回到最后新增的标签页，否则保持当前标签页不变
        for index, item in enumerate(origin_items):
            if item['key'] == latestDeletePane:
                context_menu = [
                    {
                        'key': '刷新页面',
                        'label': '刷新页面',
                        'icon': 'antd-reload'
                    },
                    {
                        'key': '关闭其他',
                        'label': '关闭其他',
                        'icon': 'antd-close-circle'
                    },
                    {
                        'key': '全部关闭',
                        'label': '全部关闭',
                        'icon': 'antd-close-circle'
                    }
                ]
                if index == 1:
                    if len(origin_items) == 2:
                        new_items[0]['contextMenu'] = context_menu
                    else:
                        origin_items[2]['contextMenu'].remove({
                            'key': '关闭左侧',
                            'label': '关闭左侧',
                            'icon': 'antd-arrow-left'
                        })
                        new_items[2]['contextMenu'] = origin_items[2]['contextMenu']
                elif index == 2:
                    if len(origin_items) == 3:
                        origin_items[1]['contextMenu'].remove({
                            'key': '关闭右侧',
                            'label': '关闭右侧',
                            'icon': 'antd-arrow-right'
                        })
                        new_items[1]['contextMenu'] = origin_items[1]['contextMenu']
                else:
                    if index == len(origin_items) - 1:
                        new_items[index - 1]['contextMenu'] = item['contextMenu']
                new_items.remove(item)
                break
        new_origin_items = [
            item for item in
            origin_items if item['key'] != latestDeletePane
        ]

        return [
            new_items,
            new_origin_items[-1]['key'] if activeKey == latestDeletePane else activeKey
        ]

    raise PreventUpdate


@app.callback(
    [Output('tabs-container', 'items', allow_duplicate=True),
     Output('tabs-container', 'activeKey', allow_duplicate=True),
     Output('trigger-reload-output', 'reload', allow_duplicate=True)],
    Input('tabs-container', 'clickedContextMenu'),
    [State('tabs-container', 'items'),
     State('tabs-container', 'activeKey')],
    prevent_initial_call=True
)
def handle_via_context_menu(clickedContextMenu, origin_items, activeKey):
    """
    基于标签页标题右键菜单的额外标签页控制
    """
    if clickedContextMenu['menuKey'] == '刷新页面':

        return [
            dash.no_update,
            dash.no_update,
            True
        ]

    if '关闭' in clickedContextMenu['menuKey']:
        new_items = dash.Patch()
        if clickedContextMenu['menuKey'] == '关闭当前':
            for index, item in enumerate(origin_items):
                if item['key'] == clickedContextMenu['tabKey']:
                    context_menu = [
                        {
                            'key': '刷新页面',
                            'label': '刷新页面',
                            'icon': 'antd-reload'
                        },
                        {
                            'key': '关闭其他',
                            'label': '关闭其他',
                            'icon': 'antd-close-circle'
                        },
                        {
                            'key': '全部关闭',
                            'label': '全部关闭',
                            'icon': 'antd-close-circle'
                        }
                    ]
                    if index == 1:
                        if len(origin_items) == 2:
                            new_items[0]['contextMenu'] = context_menu
                        else:
                            origin_items[2]['contextMenu'].remove({
                                'key': '关闭左侧',
                                'label': '关闭左侧',
                                'icon': 'antd-arrow-left'
                            })
                            new_items[2]['contextMenu'] = origin_items[2]['contextMenu']
                    elif index == 2:
                        if len(origin_items) == 3:
                            origin_items[1]['contextMenu'].remove({
                                'key': '关闭右侧',
                                'label': '关闭右侧',
                                'icon': 'antd-arrow-right'
                            })
                            new_items[1]['contextMenu'] = origin_items[1]['contextMenu']
                    else:
                        if index == len(origin_items) - 1:
                            new_items[index - 1]['contextMenu'] = item['contextMenu']
                    new_items.remove(item)
                    break
            new_origin_items = [
                item for item in
                origin_items if item['key'] != clickedContextMenu['tabKey']
            ]

            return [
                new_items,
                new_origin_items[-1]['key'] if activeKey == clickedContextMenu['tabKey'] else activeKey,
                dash.no_update
            ]

        elif clickedContextMenu['menuKey'] == '关闭其他':
            for item in origin_items:
                if item['key'] != clickedContextMenu['tabKey'] and item['key'] != '首页':
                    new_items.remove(item)
            context_menu = [
                {
                    'key': '刷新页面',
                    'label': '刷新页面',
                    'icon': 'antd-reload'
                },
                {
                    'key': '关闭其他',
                    'label': '关闭其他',
                    'icon': 'antd-close-circle'
                },
                {
                    'key': '全部关闭',
                    'label': '全部关闭',
                    'icon': 'antd-close-circle'
                }
            ]
            if clickedContextMenu['tabKey'] == '首页':
                new_items[0]['contextMenu'] = context_menu
            else:
                context_menu.insert(1, {
                    'key': '关闭当前',
                    'label': '关闭当前',
                    'icon': 'antd-close'
                })
                new_items[1]['contextMenu'] = context_menu

            return [
                new_items,
                clickedContextMenu['tabKey'],
                dash.no_update
            ]

        elif clickedContextMenu['menuKey'] == '关闭左侧':
            current_index = 0
            for index, item in enumerate(origin_items):
                if item['key'] == clickedContextMenu['tabKey']:
                    current_index = index
                    item['contextMenu'].remove({
                        'key': '关闭左侧',
                        'label': '关闭左侧',
                        'icon': 'antd-arrow-left'
                    })
                    new_items[index]['contextMenu'] = item['contextMenu']
                    break
            for item in origin_items[1:current_index]:
                new_items.remove(item)

            return [
                new_items,
                clickedContextMenu['tabKey'],
                dash.no_update
            ]

        elif clickedContextMenu['menuKey'] == '关闭右侧':
            current_index = 0
            for index, item in enumerate(origin_items):
                if item['key'] == clickedContextMenu['tabKey']:
                    current_index = index
                    item['contextMenu'].remove({
                        'key': '关闭右侧',
                        'label': '关闭右侧',
                        'icon': 'antd-arrow-right'
                    })
                    new_items[index]['contextMenu'] = item['contextMenu']
                    break
            for item in origin_items[current_index+1:]:
                new_items.remove(item)

            return [
                new_items,
                clickedContextMenu['tabKey'],
                dash.no_update
            ]

        for item in origin_items:
            if item['key'] != '首页':
                new_items.remove(item)
        new_items[0]['contextMenu'] = [
            {
                'key': '刷新页面',
                'label': '刷新页面',
                'icon': 'antd-reload'
            },
            {
                'key': '关闭其他',
                'label': '关闭其他',
                'icon': 'antd-close-circle'
            },
            {
                'key': '全部关闭',
                'label': '全部关闭',
                'icon': 'antd-close-circle'
            }
        ]
        # 否则则为全部关闭
        return [
            new_items,
            '首页',
            dash.no_update
        ]

    raise PreventUpdate


# 页首面包屑和hash回调
@app.callback(
    [Output('header-breadcrumb', 'items'),
     Output('dcc-url', 'pathname', allow_duplicate=True)],
    Input('tabs-container', 'activeKey'),
    State('menu-info-store-container', 'data'),
    prevent_initial_call=True
)
def get_current_breadcrumbs(active_key, menu_info):
    if active_key:

        if active_key == '首页':
            return [
                [
                    {
                        'title': '首页',
                        'icon': 'antd-dashboard',
                        'href': '/'
                    },
                ],
                '/'
            ]

        elif active_key == '个人资料':
            return [
                [
                    {
                        'title': '首页',
                        'icon': 'antd-dashboard',
                        'href': '/'
                    },
                    {
                        'title': '个人资料',
                    }
                ],
                '/user/profile'
            ]

        else:
            result = find_parents(menu_info.get('menu_info'), active_key)
            # 去除result的重复项
            parent_info = list(OrderedDict((json.dumps(d, ensure_ascii=False), d) for d in result).values())
            if parent_info:
                current_href = find_href_by_key(menu_info.get('menu_info'), active_key)

                return [
                    [
                        {
                            'title': '首页',
                            'icon': 'antd-dashboard',
                            'href': '/'
                        },
                    ] + parent_info,
                    current_href
                ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/layout_c/index_c.py
insistence/Dash-FastAPI-Admin
import dash
from dash import dcc
import feffery_utils_components as fuc
from dash.dependencies import Input, Output, State
from flask import session
import time

from server import app
from api.login import login_api, get_captcha_image_api


@app.callback(
    output=dict(
        username_form_status=Output('login-username-form-item', 'validateStatus'),
        password_form_status=Output('login-password-form-item', 'validateStatus'),
        captcha_form_status=Output('login-captcha-form-item', 'validateStatus'),
        username_form_help=Output('login-username-form-item', 'help'),
        password_form_help=Output('login-password-form-item', 'help'),
        captcha_form_help=Output('login-captcha-form-item', 'help'),
        image_click=Output('login-captcha-image-container', 'n_clicks'),
        submit_loading=Output('login-submit', 'loading'),
        token=Output('token-container', 'data'),
        redirect_container=Output('redirect-container', 'children', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        nClicks=Input('login-submit', 'nClicks')
    ),
    state=dict(
        username=State('login-username', 'value'),
        password=State('login-password', 'value'),
        input_captcha=State('login-captcha', 'value'),
        session_id=State('captcha_image-session_id-container', 'data'),
        image_click=State('login-captcha-image-container', 'n_clicks'),
        captcha_hidden=State('captcha-row-container', 'hidden')
    ),
    prevent_initial_call=True
)
def login_auth(nClicks, username, password, input_captcha, session_id, image_click, captcha_hidden):
    if nClicks:
        if captcha_hidden:
            input_captcha = 'hidden'
        # 校验全部输入值是否不为空
        if all([username, password, input_captcha]):

            try:
                user_params = dict(username=username, password=password, captcha=input_captcha, session_id=session_id)
                userinfo_result = login_api(user_params)
                if userinfo_result['code'] == 200:
                    token = userinfo_result['data']['access_token']
                    session['Authorization'] = token
                    return dict(
                        username_form_status=None,
                        password_form_status=None,
                        captcha_form_status=None,
                        username_form_help=None,
                        password_form_help=None,
                        captcha_form_help=None,
                        image_click=dash.no_update,
                        submit_loading=False,
                        token=token,
                        redirect_container=dcc.Location(pathname='/', id='login-redirect'),
                        global_message_container=fuc.FefferyFancyMessage('登录成功', type='success')
                    )

                else:

                    return dict(
                        username_form_status=None,
                        password_form_status=None,
                        captcha_form_status=None,
                        username_form_help=None,
                        password_form_help=None,
                        captcha_form_help=None,
                        image_click=image_click + 1,
                        submit_loading=False,
                        token=None,
                        redirect_container=None,
                        global_message_container=fuc.FefferyFancyMessage(userinfo_result.get('message'), type='error')
                    )
            except Exception as e:
                print(e)
                return dict(
                    username_form_status=None,
                    password_form_status=None,
                    captcha_form_status=None,
                    username_form_help=None,
                    password_form_help=None,
                    captcha_form_help=None,
                    image_click=image_click + 1,
                    submit_loading=False,
                    token=None,
                    redirect_container=None,
                    global_message_container=fuc.FefferyFancyMessage('接口异常', type='error')
                )

        return dict(
            username_form_status=None if username else 'error',
            password_form_status=None if password else 'error',
            captcha_form_status=None if input_captcha else 'error',
            username_form_help=None if username else '请输入用户名！',
            password_form_help=None if password else '请输入密码！',
            captcha_form_help=None if input_captcha else '请输入验证码！',
            image_click=dash.no_update,
            submit_loading=False,
            token=None,
            redirect_container=None,
            global_message_container=None
        )

    return dict(
        username_form_status=dash.no_update,
        password_form_status=dash.no_update,
        captcha_form_status=dash.no_update,
        username_form_help=dash.no_update,
        password_form_help=dash.no_update,
        captcha_form_help=dash.no_update,
        image_click=image_click + 1,
        submit_loading=dash.no_update,
        token=dash.no_update,
        redirect_container=dash.no_update,
        global_message_container=dash.no_update
    )


@app.callback(
    [Output('login-captcha-image', 'src'),
     Output('captcha_image-session_id-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('login-captcha-image-container', 'n_clicks'),
    prevent_initial_call=True
)
def change_login_captcha_image(captcha_click):
    if captcha_click:
        try:
            captcha_image_info = get_captcha_image_api()
            if captcha_image_info.get('code') == 200:
                captcha_image = captcha_image_info.get('data').get('image')
                session_id = captcha_image_info.get('data').get('session_id')

                return [
                    captcha_image,
                    session_id,
                    dash.no_update,
                    dash.no_update
                ]
            else:
                return [
                    dash.no_update,
                    dash.no_update,
                    {'timestamp': time.time()},
                    dash.no_update
                ]
        except Exception as e:

            return [dash.no_update, dash.no_update, {'timestamp': time.time()}, fuc.FefferyFancyMessage('接口异常', type='error')]

    return [dash.no_update] * 4


@app.callback(
    Output('container', 'style'),
    Input('url-container', 'pathname'),
    State('container', 'style')
)
def random_bg(pathname, old_style):
    return {
        **old_style,
        'backgroundImage': 'url({})'.format(dash.get_asset_url('imgs/login-background.jpg')),
        'backgroundRepeat': 'no-repeat',
        'backgroundSize': 'cover'
    }

dash-fastapi-frontend/callbacks/login_c.py
insistence/Dash-FastAPI-Admin
from dash.dependencies import Input, Output, State, ClientsideFunction

from server import app


# 初始化echarts图表数据
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
)


# 渲染命令统计图表
app.clientside_callback(
    ClientsideFunction(
        namespace='clientside_command_stats',
        function_name='render_command_stats_chart'
    ),
    Output('command-stats-charts-container', 'children'),
    Input('echarts-data-container', 'data')
)


# 渲染内存信息统计图表
app.clientside_callback(
    ClientsideFunction(
        namespace='clientside_memory',
        function_name='render_memory_chart'
    ),
    Output('memory-charts-container', 'children'),
    Input('echarts-data-container', 'data')
)


dash-fastapi-frontend/callbacks/monitor_c/cache_c/control_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
import json
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.job import get_job_list_api, get_job_detail_api, add_job_api, edit_job_api, execute_job_api, delete_job_api, export_job_list_api
from api.dict import query_dict_data_list_api


@app.callback(
    output=dict(
        job_table_data=Output('job-list-table', 'data', allow_duplicate=True),
        job_table_pagination=Output('job-list-table', 'pagination', allow_duplicate=True),
        job_table_key=Output('job-list-table', 'key'),
        job_table_selectedrowkeys=Output('job-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('job-search', 'nClicks'),
        refresh_click=Input('job-refresh', 'nClicks'),
        pagination=Input('job-list-table', 'pagination'),
        operations=Input('job-operations-store', 'data')
    ),
    state=dict(
        job_name=State('job-job_name-input', 'value'),
        job_group=State('job-job_group-select', 'value'),
        status_select=State('job-status-select', 'value'),
        button_perms=State('job-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_job_table_data(search_click, refresh_click, pagination, operations, job_name, job_group, status_select,
                       button_perms):
    """
    获取定时任务表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """
    query_params = dict(
        job_name=job_name,
        job_group=job_group,
        status=status_select,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'job-list-table':
        query_params = dict(
            job_name=job_name,
            job_group=job_group,
            status=status_select,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        option_table = []
        info = query_dict_data_list_api(dict_type='sys_job_group')
        if info.get('code') == 200:
            data = info.get('data')
            option_table = [
                dict(label=item.get('dict_label'), value=item.get('dict_value'), css_class=item.get('css_class')) for
                item
                in data]
        option_dict = {item.get('value'): item for item in option_table}

        table_info = get_job_list_api(query_params)
        if table_info['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(checked=True)
                else:
                    item['status'] = dict(checked=False)
                if str(item.get('job_group')) in option_dict.keys():
                    item['job_group'] = dict(
                        tag=option_dict.get(str(item.get('job_group'))).get('label'),
                        color=json.loads(option_dict.get(str(item.get('job_group'))).get('css_class')).get('color')
                    )
                item['key'] = str(item['job_id'])
                item['operation'] = [
                    {
                        'title': '修改',
                        'icon': 'antd-edit'
                    } if 'monitor:job:edit' in button_perms else None,
                    {
                        'title': '删除',
                        'icon': 'antd-delete'
                    } if 'monitor:job:remove' in button_perms else None,
                    {
                        'title': '执行一次',
                        'icon': 'antd-rocket'
                    } if 'monitor:job:changeStatus' in button_perms else None,
                    {
                        'title': '任务详细',
                        'icon': 'antd-eye'
                    } if 'monitor:job:query' in button_perms else None,
                    {
                        'title': '调度日志',
                        'icon': 'antd-history'
                    } if 'monitor:job:query' in button_perms else None
                ]

            return dict(
                job_table_data=table_data,
                job_table_pagination=table_pagination,
                job_table_key=str(uuid.uuid4()),
                job_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            job_table_data=dash.no_update,
            job_table_pagination=dash.no_update,
            job_table_key=dash.no_update,
            job_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置定时任务搜索表单数据回调
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'),
     Output('job-operations-store', 'data')],
    Input('job-reset', '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('job-search-form-container', 'hidden'),
     Output('job-hidden-tooltip', 'title')],
    Input('job-hidden', 'nClicks'),
    State('job-search-form-container', 'hidden'),
    prevent_initial_call=True
)


@app.callback(
    Output({'type': 'job-operation-button', 'index': 'edit'}, 'disabled'),
    Input('job-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_job_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output({'type': 'job-operation-button', 'index': 'delete'}, 'disabled'),
    Input('job-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_job_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('job-modal', 'visible', allow_duplicate=True),
        modal_title=Output('job-modal', 'title'),
        form_value=Output({'type': 'job-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'job-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'job-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('job-edit-id-store', 'data'),
        modal_type=Output('job-operations-store-bk', 'data')
    ),
    inputs=dict(
        operation_click=Input({'type': 'job-operation-button', 'index': ALL}, 'nClicks'),
        dropdown_click=Input('job-list-table', 'nClicksDropdownItem')
    ),
    state=dict(
        selected_row_keys=State('job-list-table', 'selectedRowKeys'),
        recently_clicked_dropdown_item_title=State('job-list-table', 'recentlyClickedDropdownItemTitle'),
        recently_dropdown_item_clicked_row=State('job-list-table', 'recentlyDropdownItemClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_job_modal(operation_click, dropdown_click, selected_row_keys, recently_clicked_dropdown_item_title,
                       recently_dropdown_item_clicked_row):
    """
    显示新增或编辑定时任务弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'job-operation-button'} \
            or trigger_id == {'index': 'edit', 'type': 'job-operation-button'} \
            or (trigger_id == 'job-list-table' and recently_clicked_dropdown_item_title == '修改'):
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]
        if trigger_id == {'index': 'add', 'type': 'job-operation-button'}:
            job_info = dict(
                job_name=None,
                job_group=None,
                invoke_target=None,
                cron_expression=None,
                job_args=None,
                job_kwargs=None,
                misfire_policy='1',
                concurrent='1',
                status='0'
            )
            return dict(
                modal_visible=True,
                modal_title='新增任务',
                form_value=[job_info.get(k) for k in form_value_list],
                form_label_validate_status=[None] * len(form_label_list),
                form_label_validate_info=[None] * len(form_label_list),
                api_check_token_trigger=dash.no_update,
                edit_row_info=None,
                modal_type={'type': 'add'}
            )
        elif trigger_id == {'index': 'edit', 'type': 'job-operation-button'} or (trigger_id == 'job-list-table' and recently_clicked_dropdown_item_title == '修改'):
            if trigger_id == {'index': 'edit', 'type': 'job-operation-button'}:
                job_id = int(','.join(selected_row_keys))
            else:
                job_id = int(recently_dropdown_item_clicked_row['key'])
            job_info_res = get_job_detail_api(job_id=job_id)
            if job_info_res['code'] == 200:
                job_info = job_info_res['data']
                return dict(
                    modal_visible=True,
                    modal_title='编辑任务',
                    form_value=[job_info.get(k) for k in form_value_list],
                    form_label_validate_status=[None] * len(form_label_list),
                    form_label_validate_info=[None] * len(form_label_list),
                    api_check_token_trigger={'timestamp': time.time()},
                    edit_row_info=job_info if job_info else None,
                    modal_type={'type': 'edit'}
                )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None,
            modal_type=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'job-form-label', 'index': ALL, 'required': True}, 'validateStatus',
                                          allow_duplicate=True),
        form_label_validate_info=Output({'type': 'job-form-label', 'index': ALL, 'required': True}, 'help',
                                        allow_duplicate=True),
        modal_visible=Output('job-modal', 'visible'),
        operations=Output('job-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('job-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('job-operations-store-bk', 'data'),
        edit_row_info=State('job-edit-id-store', 'data'),
        form_value=State({'type': 'job-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'job-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def job_confirm(confirm_trigger, modal_type, edit_row_info, form_value, form_label):
    """
    新增或编定时任务弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}
        if all([form_value_state.get(k) for k in form_label_output_list]):
            params_add = form_value_state
            params_edit = params_add.copy()
            params_edit['job_id'] = edit_row_info.get('job_id') if edit_row_info else None
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_job_api(params_add)
            if modal_type == 'edit':
                api_res = edit_job_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('job-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    [Input('job-list-table', 'recentlySwitchDataIndex'),
     Input('job-list-table', 'recentlySwitchStatus'),
     Input('job-list-table', 'recentlySwitchRow')],
    prevent_initial_call=True
)
def table_switch_job_status(recently_switch_data_index, recently_switch_status, recently_switch_row):
    """
    表格内切换定时任务状态回调
    """
    if recently_switch_data_index:
        if recently_switch_status:
            params = dict(job_id=int(recently_switch_row['key']), status='0', type='status')
        else:
            params = dict(job_id=int(recently_switch_row['key']), status='1', type='status')
        edit_button_result = edit_job_api(params)
        if edit_button_result['code'] == 200:

            return [
                {'type': 'switch-status'},
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('修改成功', type='success')
            ]

        return [
            {'type': 'switch-status'},
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('修改失败', type='error')
        ]

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('job_detail-modal', 'visible', allow_duplicate=True),
        modal_title=Output('job_detail-modal', 'title'),
        form_value=Output({'type': 'job_detail-form-value', 'index': ALL}, 'children'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        dropdown_click=Input('job-list-table', 'nClicksDropdownItem')
    ),
    state=dict(
        recently_clicked_dropdown_item_title=State('job-list-table', 'recentlyClickedDropdownItemTitle'),
        recently_dropdown_item_clicked_row=State('job-list-table', 'recentlyDropdownItemClickedRow')
    ),
    prevent_initial_call=True
)
def get_job_detail_modal(dropdown_click, recently_clicked_dropdown_item_title, recently_dropdown_item_clicked_row):
    """
    显示定时任务详情弹窗回调及执行一次定时任务回调
    """
    # 获取所有输出表单项对应value的index
    form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[-3]]
    # 显示定时任务详情弹窗
    if dropdown_click and recently_clicked_dropdown_item_title == '任务详细':
        job_id = int(recently_dropdown_item_clicked_row['key'])
        job_info_res = get_job_detail_api(job_id=job_id)
        if job_info_res['code'] == 200:
            job_info = job_info_res['data']
            if job_info.get('misfire_policy') == '1':
                job_info['misfire_policy'] = '立即执行'
            elif job_info.get('misfire_policy') == '2':
                job_info['misfire_policy'] = '执行一次'
            else:
                job_info['misfire_policy'] = '放弃执行'
            job_info['concurrent'] = '是' if job_info.get('concurrent') == '0' else '否'
            job_info['status'] = '正常' if job_info.get('status') == '0' else '停用'
            return dict(
                modal_visible=True,
                modal_title='任务详情',
                form_value=[job_info.get(k) for k in form_value_list],
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=None
            )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=None
        )

    # 执行一次定时任务
    if dropdown_click and recently_clicked_dropdown_item_title == '执行一次':
        job_id = int(recently_dropdown_item_clicked_row['key'])
        job_info_res = execute_job_api(dict(job_id=job_id))
        if job_info_res['code'] == 200:
            return dict(
                modal_visible=False,
                modal_title=None,
                form_value=[None] * len(form_value_list),
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('执行成功', type='success')
            )

        return dict(
            modal_visible=False,
            modal_title=None,
            form_value=[None] * len(form_value_list),
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('执行失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('job-delete-text', 'children'),
     Output('job-delete-confirm-modal', 'visible'),
     Output('job-delete-ids-store', 'data')],
    [Input({'type': 'job-operation-button', 'index': ALL}, 'nClicks'),
     Input('job-list-table', 'nClicksDropdownItem')],
    [State('job-list-table', 'selectedRowKeys'),
     State('job-list-table', 'recentlyClickedDropdownItemTitle'),
     State('job-list-table', 'recentlyDropdownItemClickedRow')],
    prevent_initial_call=True
)
def job_delete_modal(operation_click, dropdown_click,
                     selected_row_keys, recently_clicked_dropdown_item_title, recently_dropdown_item_clicked_row):
    """
    显示删除定时任务二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'job-operation-button'} or (
            trigger_id == 'job-list-table' and recently_clicked_dropdown_item_title == '删除'):

        if trigger_id == {'index': 'delete', 'type': 'job-operation-button'}:
            job_ids = ','.join(selected_row_keys)
        else:
            if recently_clicked_dropdown_item_title == '删除':
                job_ids = recently_dropdown_item_clicked_row['key']
            else:
                return [dash.no_update] * 3

        return [
            f'是否确认删除任务编号为{job_ids}的任务？',
            True,
            {'job_ids': job_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('job-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('job-delete-confirm-modal', 'okCounts'),
    State('job-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def job_delete_confirm(delete_confirm, job_ids_data):
    """
    删除定时任务弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = job_ids_data
        delete_button_info = delete_job_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')
        ]

    raise PreventUpdate


@app.callback(
    output=dict(
        job_log_modal_visible=Output('job_to_job_log-modal', 'visible'),
        job_log_modal_title=Output('job_to_job_log-modal', 'title'),
        job_log_job_name=Output('job_log-job_name-input', 'value', allow_duplicate=True),
        job_log_job_group_options=Output('job_log-job_group-select', 'options'),
        job_log_search_nclick=Output('job_log-search', 'nClicks'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        operation_click=Input({'type': 'job-operation-log', 'index': ALL}, 'nClicks'),
        dropdown_click=Input('job-list-table', 'nClicksDropdownItem')
    ),
    state=dict(
        recently_clicked_dropdown_item_title=State('job-list-table', 'recentlyClickedDropdownItemTitle'),
        recently_dropdown_item_clicked_row=State('job-list-table', 'recentlyDropdownItemClickedRow'),
        job_log_search_nclick=State('job_log-search', 'nClicks')
    ),
    prevent_initial_call=True
)
def job_to_job_log_modal(operation_click, dropdown_click, recently_clicked_dropdown_item_title, recently_dropdown_item_clicked_row, job_log_search_nclick):
    """
    显示定时任务对应调度日志表格弹窗回调
    """

    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'log', 'type': 'job-operation-log'} or (trigger_id == 'job-list-table' and recently_clicked_dropdown_item_title == '调度日志'):
        option_table = []
        info = query_dict_data_list_api(dict_type='sys_job_group')
        if info.get('code') == 200:
            data = info.get('data')
            option_table = [dict(label=item.get('dict_label'), value=item.get('dict_value')) for item in data]

            if trigger_id == 'job-list-table' and recently_clicked_dropdown_item_title == '调度日志':
                return dict(
                    job_log_modal_visible=True,
                    job_log_modal_title='任务调度日志',
                    job_log_job_name=recently_dropdown_item_clicked_row.get('job_name'),
                    job_log_job_group_options=option_table,
                    job_log_search_nclick=job_log_search_nclick + 1 if job_log_search_nclick else 1,
                    api_check_token_trigger={'timestamp': time.time()}
                )

        return dict(
            job_log_modal_visible=True,
            job_log_modal_title='任务调度日志',
            job_log_job_name=None,
            job_log_job_group_options=option_table,
            job_log_search_nclick=job_log_search_nclick + 1 if job_log_search_nclick else 1,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


@app.callback(
    [Output('job-export-container', 'data', allow_duplicate=True),
     Output('job-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('job-export', 'nClicks'),
    prevent_initial_call=True
)
def export_job_list(export_click):
    """
    导出定时任务信息回调
    """
    if export_click:
        export_job_res = export_job_list_api({})
        if export_job_res.status_code == 200:
            export_job = export_job_res.content

            return [
                dcc.send_bytes(export_job, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('job-export-container', 'data', allow_duplicate=True),
    Input('job-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_job_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:
        return None

    raise PreventUpdate

dash-fastapi-frontend/callbacks/monitor_c/job_c/job_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
import json
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.job import get_job_log_list_api, get_job_log_detail_api, delete_job_log_api, clear_job_log_api, export_job_log_list_api
from api.dict import query_dict_data_list_api


@app.callback(
    output=dict(
        job_log_table_data=Output('job_log-list-table', 'data', allow_duplicate=True),
        job_log_table_pagination=Output('job_log-list-table', 'pagination', allow_duplicate=True),
        job_log_table_key=Output('job_log-list-table', 'key'),
        job_log_table_selectedrowkeys=Output('job_log-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('job_log-search', 'nClicks'),
        refresh_click=Input('job_log-refresh', 'nClicks'),
        pagination=Input('job_log-list-table', 'pagination'),
        operations=Input('job_log-operations-store', 'data')
    ),
    state=dict(
        job_name=State('job_log-job_name-input', 'value'),
        job_group=State('job_log-job_group-select', 'value'),
        status_select=State('job_log-status-select', 'value'),
        create_time_range=State('job_log-create_time-range', 'value'),
        button_perms=State('job_log-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_job_log_table_data(search_click, refresh_click, pagination, operations, job_name, job_group, status_select, create_time_range, button_perms):
    """
    获取定时任务对应调度日志表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """

    create_time_start = None
    create_time_end = None
    if create_time_range:
        create_time_start = create_time_range[0]
        create_time_end = create_time_range[1]
    query_params = dict(
        job_name=job_name,
        job_group=job_group,
        status=status_select,
        create_time_start=create_time_start,
        create_time_end=create_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'job_log-list-table':
        query_params = dict(
            job_name=job_name,
            job_group=job_group,
            status=status_select,
            create_time_start=create_time_start,
            create_time_end=create_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        option_table = []
        info = query_dict_data_list_api(dict_type='sys_job_group')
        if info.get('code') == 200:
            data = info.get('data')
            option_table = [dict(label=item.get('dict_label'), value=item.get('dict_value'), css_class=item.get('css_class')) for item in data]
        option_dict = {item.get('value'): item for item in option_table}

        table_info = get_job_log_list_api(query_params)
        if table_info['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')
                if str(item.get('job_group')) in option_dict.keys():
                    item['job_group'] = dict(
                        tag=option_dict.get(str(item.get('job_group'))).get('label'),
                        color=json.loads(option_dict.get(str(item.get('job_group'))).get('css_class')).get('color')
                    )
                item['key'] = str(item['job_log_id'])
                item['operation'] = [
                    {
                        'content': '详情',
                        'type': 'link',
                        'icon': 'antd-eye'
                    } if 'monitor:job:query' in button_perms else {},
                ]

            return dict(
                job_log_table_data=table_data,
                job_log_table_pagination=table_pagination,
                job_log_table_key=str(uuid.uuid4()),
                job_log_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            job_log_table_data=dash.no_update,
            job_log_table_pagination=dash.no_update,
            job_log_table_key=dash.no_update,
            job_log_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置定时任务调度日志搜索表单数据回调
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'),
     Output('job_log-create_time-range', 'value'),
     Output('job_log-operations-store', 'data')],
    Input('job_log-reset', '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('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
)


@app.callback(
    output=dict(
        modal_visible=Output('job_log-modal', 'visible', allow_duplicate=True),
        modal_title=Output('job_log-modal', 'title'),
        form_value=Output({'type': 'job_log-form-value', 'index': ALL}, 'children'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        button_click=Input('job_log-list-table', 'nClicksButton')
    ),
    state=dict(
        clicked_content=State('job_log-list-table', 'clickedContent'),
        recently_button_clicked_row=State('job_log-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_job_log_modal(button_click, clicked_content, recently_button_clicked_row):
    if button_click:
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[-2]]
        job_log_id = int(recently_button_clicked_row['key'])
        job_log_info_res = get_job_log_detail_api(job_log_id=job_log_id)
        if job_log_info_res['code'] == 200:
            job_log_info = job_log_info_res['data']
            job_log_info['status'] = '成功' if job_log_info.get('status') == '0' else '失败'
            return dict(
                modal_visible=True,
                modal_title='任务执行日志详情',
                form_value=[job_log_info.get(k) for k in form_value_list],
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


@app.callback(
    Output({'type': 'job_log-operation-button', 'index': 'delete'}, 'disabled'),
    Input('job_log-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_job_log_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    [Output('job_log-delete-text', 'children'),
     Output('job_log-delete-confirm-modal', 'visible'),
     Output('job_log-delete-ids-store', 'data')],
    Input({'type': 'job_log-operation-button', 'index': ALL}, 'nClicks'),
    State('job_log-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def job_log_delete_modal(operation_click, selected_row_keys):
    """
    显示删除或清空定时任务调度日志二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id.index in ['delete', 'clear']:
        if trigger_id.index == 'delete':
            job_log_ids = ','.join(selected_row_keys)

            return [
                f'是否确认删除日志编号为{job_log_ids}的任务执行日志？',
                True,
                {'oper_type': 'delete', 'job_log_ids': job_log_ids}
            ]

        elif trigger_id.index == 'clear':
            return [
                f'是否确认清除所有的任务执行日志？',
                True,
                {'oper_type': 'clear', 'job_log_ids': ''}
            ]

    raise PreventUpdate


@app.callback(
    [Output('job_log-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('job_log-delete-confirm-modal', 'okCounts'),
    State('job_log-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def job_log_delete_confirm(delete_confirm, job_log_ids_data):
    """
    删除或清空定时任务调度日志弹窗确认回调，实现删除或清空操作
    """
    if delete_confirm:

        oper_type = job_log_ids_data.get('oper_type')
        if oper_type == 'clear':
            params = dict(oper_type=job_log_ids_data.get('oper_type'))
            clear_button_info = clear_job_log_api(params)
            if clear_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')
            ]
        else:
            params = dict(job_log_ids=job_log_ids_data.get('job_log_ids'))
            delete_button_info = delete_job_log_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')
            ]

    raise PreventUpdate


@app.callback(
    [Output('job_log-export-container', 'data', allow_duplicate=True),
     Output('job_log-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('job_log-export', 'nClicks'),
    prevent_initial_call=True
)
def export_job_log_list(export_click):
    """
    导出定时任务调度日志信息回调
    """
    if export_click:
        export_job_log_res = export_job_log_list_api({})
        if export_job_log_res.status_code == 200:
            export_job_log = export_job_log_res.content

            return [
                dcc.send_bytes(export_job_log, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('job_log-export-container', 'data', allow_duplicate=True),
    Input('job_log-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_job_log_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate

dash-fastapi-frontend/callbacks/monitor_c/job_c/job_log_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.log import get_login_log_list_api, delete_login_log_api, clear_login_log_api, unlock_user_api, export_login_log_list_api


@app.callback(
    output=dict(
        login_log_table_data=Output('login_log-list-table', 'data', allow_duplicate=True),
        login_log_table_pagination=Output('login_log-list-table', 'pagination', allow_duplicate=True),
        login_log_table_key=Output('login_log-list-table', 'key'),
        login_log_table_selectedrowkeys=Output('login_log-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('login_log-search', 'nClicks'),
        refresh_click=Input('login_log-refresh', 'nClicks'),
        pagination=Input('login_log-list-table', 'pagination'),
        operations=Input('login_log-operations-store', 'data')
    ),
    state=dict(
        ipaddr=State('login_log-ipaddr-input', 'value'),
        user_name=State('login_log-user_name-input', 'value'),
        status_select=State('login_log-status-select', 'value'),
        login_time_range=State('login_log-login_time-range', 'value'),
        button_perms=State('login_log-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_login_log_table_data(search_click, refresh_click, pagination, operations, ipaddr, user_name, status_select, login_time_range, button_perms):
    """
    获取登录日志表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """

    login_time_start = None
    login_time_end = None
    if login_time_range:
        login_time_start = login_time_range[0]
        login_time_end = login_time_range[1]
    query_params = dict(
        ipaddr=ipaddr,
        user_name=user_name,
        status=status_select,
        login_time_start=login_time_start,
        login_time_end=login_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'login_log-list-table':
        query_params = dict(
            ipaddr=ipaddr,
            user_name=user_name,
            status=status_select,
            login_time_start=login_time_start,
            login_time_end=login_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        table_info = get_login_log_list_api(query_params)
        if table_info['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['info_id'])

            return dict(
                login_log_table_data=table_data,
                login_log_table_pagination=table_pagination,
                login_log_table_key=str(uuid.uuid4()),
                login_log_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            login_log_table_data=dash.no_update,
            login_log_table_pagination=dash.no_update,
            login_log_table_key=dash.no_update,
            login_log_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置登录日志搜索表单数据回调
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'),
     Output('login_log-login_time-range', 'value'),
     Output('login_log-operations-store', 'data')],
    Input('login_log-reset', '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('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
)


@app.callback(
    Output({'type': 'login_log-operation-button', 'index': 'delete'}, 'disabled'),
    Input('login_log-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_login_log_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output('login_log-unlock', 'disabled'),
    Input('login_log-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_login_log_unlock_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制解锁按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    [Output('login_log-delete-text', 'children'),
     Output('login_log-delete-confirm-modal', 'visible'),
     Output('login_log-delete-ids-store', 'data')],
    Input({'type': 'login_log-operation-button', 'index': ALL}, 'nClicks'),
    State('login_log-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def login_log_delete_modal(operation_click, selected_row_keys):
    """
    显示删除或清空登录日志二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id.index in ['delete', 'clear']:
        if trigger_id.index == 'delete':
            info_ids = ','.join(selected_row_keys)

            return [
                f'是否确认删除访问编号为{info_ids}的登录日志？',
                True,
                {'oper_type': 'delete', 'info_ids': info_ids}
            ]

        elif trigger_id.index == 'clear':
            return [
                f'是否确认清除所有的登录日志？',
                True,
                {'oper_type': 'clear', 'info_ids': ''}
            ]

    raise PreventUpdate


@app.callback(
    [Output('login_log-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('login_log-delete-confirm-modal', 'okCounts'),
    State('login_log-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def login_log_delete_confirm(delete_confirm, info_ids_data):
    """
    删除或清空登录日志弹窗确认回调，实现删除或清空操作
    """
    if delete_confirm:

        oper_type = info_ids_data.get('oper_type')
        if oper_type == 'clear':
            params = dict(oper_type=info_ids_data.get('oper_type'))
            clear_button_info = clear_login_log_api(params)
            if clear_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')
            ]
        else:
            params = dict(info_ids=info_ids_data.get('info_ids'))
            delete_button_info = delete_login_log_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')
            ]

    raise PreventUpdate


@app.callback(
    [Output('login_log-export-container', 'data', allow_duplicate=True),
     Output('login_log-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('login_log-export', 'nClicks'),
    prevent_initial_call=True
)
def export_login_log_list(export_click):
    """
    导出登录日志信息回调
    """
    if export_click:
        export_login_log_res = export_login_log_list_api({})
        if export_login_log_res.status_code == 200:
            export_login_log = export_login_log_res.content

            return [
                dcc.send_bytes(export_login_log, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('login_log-export-container', 'data', allow_duplicate=True),
    Input('login_log-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_login_log_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate


@app.callback(
    [Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('login_log-unlock', 'nClicks'),
    State('login_log-list-table', 'selectedRows'),
    prevent_initial_call=True
)
def unlock_user(unlock_click, selected_rows):
    """
    解锁用户回调
    """
    if unlock_click:
        user_name = selected_rows[0].get('user_name')
        unlock_info_res = unlock_user_api(dict(user_name=user_name))
        if unlock_info_res.get('code') == 200:

            return [
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('解锁成功', type='success')
            ]

        return [
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('解锁失败', type='error')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/monitor_c/logininfor_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.online import get_online_list_api, force_logout_online_api, batch_logout_online_api


@app.callback(
    output=dict(
        online_table_data=Output('online-list-table', 'data', allow_duplicate=True),
        online_table_pagination=Output('online-list-table', 'pagination', allow_duplicate=True),
        online_table_key=Output('online-list-table', 'key'),
        online_table_selectedrowkeys=Output('online-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('online-search', 'nClicks'),
        refresh_click=Input('online-refresh', 'nClicks'),
        pagination=Input('online-list-table', 'pagination'),
        operations=Input('online-operations-store', 'data')
    ),
    state=dict(
        ipaddr=State('online-ipaddr-input', 'value'),
        user_name=State('online-user_name-input', 'value'),
        button_perms=State('online-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_online_table_data(search_click, refresh_click, pagination, operations, ipaddr, user_name, button_perms):
    """
    获取在线用户表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """
    query_params = dict(
        ipaddr=ipaddr,
        user_name=user_name,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'online-list-table':
        query_params = dict(
            ipaddr=ipaddr,
            user_name=user_name,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        table_info = get_online_list_api(query_params)
        if table_info['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:
                item['key'] = str(item['session_id'])
                item['operation'] = [
                    {
                        'content': '强退',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'monitor:online:forceLogout' in button_perms else {},
                ]

            return dict(
                online_table_data=table_data,
                online_table_pagination=table_pagination,
                online_table_key=str(uuid.uuid4()),
                online_table_selectedrowkeys=None,
                api_check_token_trigger= {'timestamp': time.time()}
            )

        return dict(
            online_table_data=dash.no_update,
            online_table_pagination=dash.no_update,
            online_table_key=dash.no_update,
            online_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置在线用户搜索表单数据回调
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
)


# 隐藏/显示在线用户搜索表单回调
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
)


@app.callback(
    Output({'type': 'online-operation-button', 'index': 'delete'}, 'disabled'),
    Input('online-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_online_edit_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制批量强退按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    [Output('online-delete-text', 'children'),
     Output('online-delete-confirm-modal', 'visible'),
     Output('online-delete-ids-store', 'data')],
    [Input({'type': 'online-operation-button', 'index': ALL}, 'nClicks'),
     Input('online-list-table', 'nClicksButton')],
    [State('online-list-table', 'selectedRowKeys'),
     State('online-list-table', 'clickedContent'),
     State('online-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def online_delete_modal(operation_click, button_click,
                      selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示强退在线用户二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'online-operation-button'} or (
            trigger_id == 'online-list-table' and clicked_content == '强退'):
        logout_type = ''

        if trigger_id == {'index': 'delete', 'type': 'online-operation-button'}:
            session_ids = ','.join(selected_row_keys)
        else:
            if clicked_content == '强退':
                session_ids = recently_button_clicked_row['key']
                logout_type = 'force'
            else:
                return dash.no_update

        return [
            f'是否确认强退会话编号为{session_ids}的会话？',
            True,
            {'session_ids': session_ids, 'logout_type': logout_type}
        ]

    raise PreventUpdate


@app.callback(
    [Output('online-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('online-delete-confirm-modal', 'okCounts'),
    State('online-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def online_delete_confirm(delete_confirm, session_ids_data):
    """
    强退在线用户弹窗确认回调，实现强退操作
    """
    if delete_confirm:

        params = dict(session_ids=session_ids_data.get('session_ids'))
        logout_type = session_ids_data.get('logout_type')
        if logout_type == 'force':
            delete_button_info = force_logout_online_api(params)
        else:
            delete_button_info = batch_logout_online_api(params)
        if delete_button_info['code'] == 200:
            return [
                {'type': 'force'},
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('强退成功', type='success')
            ]

        return [
            dash.no_update,
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('强退失败', type='error')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/monitor_c/online_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
import json
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.log import get_operation_log_list_api, get_operation_log_detail_api, delete_operation_log_api, clear_operation_log_api, export_operation_log_list_api
from api.dict import query_dict_data_list_api


@app.callback(
    output=dict(
        operation_log_table_data=Output('operation_log-list-table', 'data', allow_duplicate=True),
        operation_log_table_pagination=Output('operation_log-list-table', 'pagination', allow_duplicate=True),
        operation_log_table_key=Output('operation_log-list-table', 'key'),
        operation_log_table_selectedrowkeys=Output('operation_log-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('operation_log-search', 'nClicks'),
        refresh_click=Input('operation_log-refresh', 'nClicks'),
        pagination=Input('operation_log-list-table', 'pagination'),
        operations=Input('operation_log-operations-store', 'data')
    ),
    state=dict(
        title=State('operation_log-title-input', 'value'),
        oper_name=State('operation_log-oper_name-input', 'value'),
        business_type=State('operation_log-business_type-select', 'value'),
        status_select=State('operation_log-status-select', 'value'),
        oper_time_range=State('operation_log-oper_time-range', 'value'),
        button_perms=State('operation_log-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_operation_log_table_data(search_click, refresh_click, pagination, operations, title, oper_name, business_type, status_select, oper_time_range, button_perms):
    """
    获取操作日志表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """

    oper_time_start = None
    oper_time_end = None
    if oper_time_range:
        oper_time_start = oper_time_range[0]
        oper_time_end = oper_time_range[1]
    query_params = dict(
        title=title,
        oper_name=oper_name,
        business_type=business_type,
        status=status_select,
        oper_time_start=oper_time_start,
        oper_time_end=oper_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'operation_log-list-table':
        query_params = dict(
            title=title,
            oper_name=oper_name,
            business_type=business_type,
            status=status_select,
            oper_time_start=oper_time_start,
            oper_time_end=oper_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        option_table = []
        info = query_dict_data_list_api(dict_type='sys_oper_type')
        if info.get('code') == 200:
            data = info.get('data')
            option_table = [dict(label=item.get('dict_label'), value=item.get('dict_value'), css_class=item.get('css_class')) for item in data]
        option_dict = {item.get('value'): item for item in option_table}

        table_info = get_operation_log_list_api(query_params)
        if table_info['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')
                if str(item.get('business_type')) in option_dict.keys():
                    item['business_type'] = dict(
                        tag=option_dict.get(str(item.get('business_type'))).get('label'),
                        color=json.loads(option_dict.get(str(item.get('business_type'))).get('css_class')).get('color')
                    )
                item['key'] = str(item['oper_id'])
                item['cost_time'] = f"{item['cost_time']}毫秒"
                item['operation'] = [
                    {
                        'content': '详情',
                        'type': 'link',
                        'icon': 'antd-eye'
                    } if 'monitor:operlog:query' in button_perms else {},
                ]

            return dict(
                operation_log_table_data=table_data,
                operation_log_table_pagination=table_pagination,
                operation_log_table_key=str(uuid.uuid4()),
                operation_log_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            operation_log_table_data=dash.no_update,
            operation_log_table_pagination=dash.no_update,
            operation_log_table_key=dash.no_update,
            operation_log_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置操作日志搜索表单数据回调
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'),
     Output('operation_log-status-select', 'value'),
     Output('operation_log-oper_time-range', 'value'),
     Output('operation_log-operations-store', 'data')],
    Input('operation_log-reset', '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('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
)


@app.callback(
    output=dict(
        modal_visible=Output('operation_log-modal', 'visible', allow_duplicate=True),
        modal_title=Output('operation_log-modal', 'title'),
        form_value=Output({'type': 'operation_log-form-value', 'index': ALL}, 'children'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        button_click=Input('operation_log-list-table', 'nClicksButton')
    ),
    state=dict(
        clicked_content=State('operation_log-list-table', 'clickedContent'),
        recently_button_clicked_row=State('operation_log-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_operation_log_modal(button_click, clicked_content, recently_button_clicked_row):
    """
    显示操作日志详情弹窗回调
    """
    if button_click:
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[-2]]
        oper_id = int(recently_button_clicked_row['key'])
        operation_log_info_res = get_operation_log_detail_api(oper_id=oper_id)
        if operation_log_info_res['code'] == 200:
            operation_log_info = operation_log_info_res['data']
            oper_name = operation_log_info.get('oper_name') if operation_log_info.get('oper_name') else ''
            oper_ip = operation_log_info.get('oper_ip') if operation_log_info.get('oper_ip') else ''
            oper_location = operation_log_info.get('oper_location') if operation_log_info.get('oper_location') else ''
            operation_log_info['login_info'] = f'{oper_name} / {oper_ip} / {oper_location}'
            operation_log_info['status'] = '正常' if operation_log_info.get('status') == 0 else '失败'
            operation_log_info['cost_time'] = f"{operation_log_info.get('cost_time')}毫秒"
            return dict(
                modal_visible=True,
                modal_title='操作日志详情',
                form_value=[operation_log_info.get(k) for k in form_value_list],
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


@app.callback(
    Output({'type': 'operation_log-operation-button', 'index': 'delete'}, 'disabled'),
    Input('operation_log-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_operation_log_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    [Output('operation_log-delete-text', 'children'),
     Output('operation_log-delete-confirm-modal', 'visible'),
     Output('operation_log-delete-ids-store', 'data')],
    Input({'type': 'operation_log-operation-button', 'index': ALL}, 'nClicks'),
    State('operation_log-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def operation_log_delete_modal(operation_click, selected_row_keys):
    """
    显示删除或清空操作日志二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id.index in ['delete', 'clear']:
        if trigger_id.index == 'delete':
            oper_ids = ','.join(selected_row_keys)

            return [
                f'是否确认删除日志编号为{oper_ids}的操作日志？',
                True,
                {'oper_type': 'delete', 'oper_ids': oper_ids}
            ]

        elif trigger_id.index == 'clear':
            return [
                f'是否确认清除所有的操作日志？',
                True,
                {'oper_type': 'clear', 'oper_ids': ''}
            ]

    raise PreventUpdate


@app.callback(
    [Output('operation_log-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('operation_log-delete-confirm-modal', 'okCounts'),
    State('operation_log-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def operation_log_delete_confirm(delete_confirm, oper_ids_data):
    """
    删除或清空操作日志弹窗确认回调，实现删除或清空操作
    """
    if delete_confirm:

        oper_type = oper_ids_data.get('oper_type')
        if oper_type == 'clear':
            params = dict(oper_type=oper_ids_data.get('oper_type'))
            clear_button_info = clear_operation_log_api(params)
            if clear_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')
            ]
        else:
            params = dict(oper_ids=oper_ids_data.get('oper_ids'))
            delete_button_info = delete_operation_log_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')
            ]

    raise PreventUpdate


@app.callback(
    [Output('operation_log-export-container', 'data', allow_duplicate=True),
     Output('operation_log-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('operation_log-export', 'nClicks'),
    prevent_initial_call=True
)
def export_operation_log_list(export_click):
    """
    导出操作日志信息回调
    """
    if export_click:
        export_operation_log_res = export_operation_log_list_api({})
        if export_operation_log_res.status_code == 200:
            export_operation_log = export_operation_log_res.content

            return [
                dcc.send_bytes(export_operation_log, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('operation_log-export-container', 'data', allow_duplicate=True),
    Input('operation_log-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_operation_log_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate

dash-fastapi-frontend/callbacks/monitor_c/operlog_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.config import get_config_list_api, get_config_detail_api, add_config_api, edit_config_api, delete_config_api, export_config_list_api, refresh_config_api


@app.callback(
    output=dict(
        config_table_data=Output('config-list-table', 'data', allow_duplicate=True),
        config_table_pagination=Output('config-list-table', 'pagination', allow_duplicate=True),
        config_table_key=Output('config-list-table', 'key'),
        config_table_selectedrowkeys=Output('config-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('config-search', 'nClicks'),
        refresh_click=Input('config-refresh', 'nClicks'),
        pagination=Input('config-list-table', 'pagination'),
        operations=Input('config-operations-store', 'data')
    ),
    state=dict(
        config_name=State('config-config_name-input', 'value'),
        config_key=State('config-config_key-input', 'value'),
        config_type=State('config-config_type-select', 'value'),
        create_time_range=State('config-create_time-range', 'value'),
        button_perms=State('config-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_config_table_data(search_click, refresh_click, pagination, operations, config_name, config_key, config_type, create_time_range, button_perms):
    """
    获取参数设置表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """
    create_time_start = None
    create_time_end = None
    if create_time_range:
        create_time_start = create_time_range[0]
        create_time_end = create_time_range[1]

    query_params = dict(
        config_name=config_name,
        config_key=config_key,
        config_type=config_type,
        create_time_start=create_time_start,
        create_time_end=create_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'config-list-table':
        query_params = dict(
            config_name=config_name,
            config_key=config_key,
            config_type=config_type,
            create_time_start=create_time_start,
            create_time_end=create_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        table_info = get_config_list_api(query_params)
        if table_info['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['config_type'] == 'Y':
                    item['config_type'] = dict(tag='是', color='blue')
                else:
                    item['config_type'] = dict(tag='否', color='volcano')
                item['key'] = str(item['config_id'])
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:config:edit' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:config:remove' in button_perms else {},
                ]

            return dict(
                config_table_data=table_data,
                config_table_pagination=table_pagination,
                config_table_key=str(uuid.uuid4()),
                config_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            config_table_data=dash.no_update,
            config_table_pagination=dash.no_update,
            config_table_key=dash.no_update,
            config_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置参数设置搜索表单数据回调
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'),
     Output('config-create_time-range', 'value'),
     Output('config-operations-store', 'data')],
    Input('config-reset', '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('config-search-form-container', 'hidden'),
     Output('config-hidden-tooltip', 'title')],
    Input('config-hidden', 'nClicks'),
    State('config-search-form-container', 'hidden'),
    prevent_initial_call=True
)


@app.callback(
    Output({'type': 'config-operation-button', 'index': 'edit'}, 'disabled'),
    Input('config-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_config_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output({'type': 'config-operation-button', 'index': 'delete'}, 'disabled'),
    Input('config-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_config_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('config-modal', 'visible', allow_duplicate=True),
        modal_title=Output('config-modal', 'title'),
        form_value=Output({'type': 'config-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'config-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'config-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('config-edit-id-store', 'data'),
        modal_type=Output('config-operations-store-bk', 'data')
    ),
    inputs=dict(
        operation_click=Input({'type': 'config-operation-button', 'index': ALL}, 'nClicks'),
        button_click=Input('config-list-table', 'nClicksButton')
    ),
    state=dict(
        selected_row_keys=State('config-list-table', 'selectedRowKeys'),
        clicked_content=State('config-list-table', 'clickedContent'),
        recently_button_clicked_row=State('config-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_config_modal(operation_click, button_click, selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示新增或编辑参数设置弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'config-operation-button'} \
            or trigger_id == {'index': 'edit', 'type': 'config-operation-button'} \
            or (trigger_id == 'config-list-table' and clicked_content == '修改'):
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]
        if trigger_id == {'index': 'add', 'type': 'config-operation-button'}:
            config_info = dict(config_name=None, config_key=None, config_value=None, config_type='Y', remark=None)
            return dict(
                modal_visible=True,
                modal_title='新增参数',
                form_value=[config_info.get(k) for k in form_value_list],
                form_label_validate_status=[None] * len(form_label_list),
                form_label_validate_info=[None] * len(form_label_list),
                api_check_token_trigger=dash.no_update,
                edit_row_info=None,
                modal_type={'type': 'add'}
            )
        elif trigger_id == {'index': 'edit', 'type': 'config-operation-button'} or (trigger_id == 'config-list-table' and clicked_content == '修改'):
            if trigger_id == {'index': 'edit', 'type': 'config-operation-button'}:
                config_id = int(','.join(selected_row_keys))
            else:
                config_id = int(recently_button_clicked_row['key'])
            config_info_res = get_config_detail_api(config_id=config_id)
            if config_info_res['code'] == 200:
                config_info = config_info_res['data']
                return dict(
                    modal_visible=True,
                    modal_title='编辑参数',
                    form_value=[config_info.get(k) for k in form_value_list],
                    form_label_validate_status=[None] * len(form_label_list),
                    form_label_validate_info=[None] * len(form_label_list),
                    api_check_token_trigger={'timestamp': time.time()},
                    edit_row_info=config_info if config_info else None,
                    modal_type={'type': 'edit'}
                )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None,
            modal_type=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'config-form-label', 'index': ALL, 'required': True}, 'validateStatus',
                                          allow_duplicate=True),
        form_label_validate_info=Output({'type': 'config-form-label', 'index': ALL, 'required': True}, 'help',
                                        allow_duplicate=True),
        modal_visible=Output('config-modal', 'visible'),
        operations=Output('config-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('config-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('config-operations-store-bk', 'data'),
        edit_row_info=State('config-edit-id-store', 'data'),
        form_value=State({'type': 'config-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'config-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def dict_type_confirm(confirm_trigger, modal_type, edit_row_info, form_value, form_label):
    """
    新增或编辑参数设置弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}
        if all([form_value_state.get(k) for k in form_label_output_list]):
            params_add = form_value_state
            params_edit = params_add.copy()
            params_edit['config_id'] = edit_row_info.get('config_id') if edit_row_info else None
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_config_api(params_add)
            if modal_type == 'edit':
                api_res = edit_config_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('config-delete-text', 'children'),
     Output('config-delete-confirm-modal', 'visible'),
     Output('config-delete-ids-store', 'data')],
    [Input({'type': 'config-operation-button', 'index': ALL}, 'nClicks'),
     Input('config-list-table', 'nClicksButton')],
    [State('config-list-table', 'selectedRowKeys'),
     State('config-list-table', 'clickedContent'),
     State('config-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def config_delete_modal(operation_click, button_click,
                      selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示删除参数设置二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'config-operation-button'} or (
            trigger_id == 'config-list-table' and clicked_content == '删除'):

        if trigger_id == {'index': 'delete', 'type': 'config-operation-button'}:
            config_ids = ','.join(selected_row_keys)
        else:
            if clicked_content == '删除':
                config_ids = recently_button_clicked_row['key']
            else:
                return dash.no_update

        return [
            f'是否确认删除参数编号为{config_ids}的参数设置？',
            True,
            {'config_ids': config_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('config-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('config-delete-confirm-modal', 'okCounts'),
    State('config-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def config_delete_confirm(delete_confirm, config_ids_data):
    """
    删除参数设置弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = config_ids_data
        delete_button_info = delete_config_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')
        ]

    raise PreventUpdate


@app.callback(
    [Output('config-export-container', 'data', allow_duplicate=True),
     Output('config-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('config-export', 'nClicks'),
    prevent_initial_call=True
)
def export_config_list(export_click):
    """
    导出参数设置信息回调
    """
    if export_click:
        export_config_res = export_config_list_api({})
        if export_config_res.status_code == 200:
            export_config = export_config_res.content

            return [
                dcc.send_bytes(export_config, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('config-export-container', 'data', allow_duplicate=True),
    Input('config-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_config_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate


@app.callback(
    [Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('config-refresh-cache', 'nClicks'),
    prevent_initial_call=True
)
def refresh_config_cache(refresh_click):
    """
    刷新缓存回调
    """
    if refresh_click:
        refresh_info_res = refresh_config_api({})
        if refresh_info_res.get('code') == 200:
            return [
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('刷新成功', type='success')
            ]

        return [
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('刷新失败', type='error')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/config_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from utils.tree_tool import get_dept_tree
from api.dept import get_dept_tree_api, get_dept_list_api, add_dept_api, edit_dept_api, delete_dept_api, \
    get_dept_detail_api, get_dept_tree_for_edit_option_api


@app.callback(
    output=dict(
        dept_table_data=Output('dept-list-table', 'data', allow_duplicate=True),
        dept_table_key=Output('dept-list-table', 'key'),
        dept_table_defaultexpandedrowkeys=Output('dept-list-table', 'defaultExpandedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        fold_click=Output('dept-fold', 'nClicks')
    ),
    inputs=dict(
        search_click=Input('dept-search', 'nClicks'),
        refresh_click=Input('dept-refresh', 'nClicks'),
        operations=Input('dept-operations-store', 'data'),
        fold_click=Input('dept-fold', 'nClicks')
    ),
    state=dict(
        dept_name=State('dept-dept_name-input', 'value'),
        status_select=State('dept-status-select', 'value'),
        in_default_expanded_row_keys=State('dept-list-table', 'defaultExpandedRowKeys'),
        button_perms=State('dept-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_dept_table_data(search_click, refresh_click, operations, fold_click, dept_name, status_select,
                        in_default_expanded_row_keys, button_perms):
    """
    获取部门表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """
    query_params = dict(
        dept_name=dept_name,
        status=status_select
    )
    if search_click or refresh_click or operations or fold_click:
        table_info = get_dept_list_api(query_params)
        default_expanded_row_keys = []
        if table_info['code'] == 200:
            table_data = table_info['data']['rows']
            for item in table_data:
                default_expanded_row_keys.append(str(item['dept_id']))
                item['key'] = str(item['dept_id'])
                if item['parent_id'] == 0:
                    item['operation'] = [
                        {
                            'content': '修改',
                            'type': 'link',
                            'icon': 'antd-edit'
                        } if 'system:dept:edit' in button_perms else {},
                        {
                            'content': '新增',
                            'type': 'link',
                            'icon': 'antd-plus'
                        } if 'system:dept:add' in button_perms else {},
                    ]
                elif item['status'] == '1':
                    item['operation'] = [
                        {
                            'content': '修改',
                            'type': 'link',
                            'icon': 'antd-edit'
                        } if 'system:dept:edit' in button_perms else {},
                        {
                            'content': '删除',
                            'type': 'link',
                            'icon': 'antd-delete'
                        } if 'system:dept:remove' in button_perms else {},
                    ]
                else:
                    item['operation'] = [
                        {
                            'content': '修改',
                            'type': 'link',
                            'icon': 'antd-edit'
                        } if 'system:dept:edit' in button_perms else {},
                        {
                            'content': '新增',
                            'type': 'link',
                            'icon': 'antd-plus'
                        } if 'system:dept:add' in button_perms else {},
                        {
                            'content': '删除',
                            'type': 'link',
                            'icon': 'antd-delete'
                        } if 'system:dept:remove' in button_perms else {},
                    ]
                if item['status'] == '0':
                    item['status'] = dict(tag='正常', color='blue')
                else:
                    item['status'] = dict(tag='停用', color='volcano')
            table_data_new = get_dept_tree(0, table_data)

            if fold_click:
                if in_default_expanded_row_keys:
                    return dict(
                        dept_table_data=table_data_new,
                        dept_table_key=str(uuid.uuid4()),
                        dept_table_defaultexpandedrowkeys=[],
                        api_check_token_trigger={'timestamp': time.time()},
                        fold_click=None
                    )

            return dict(
                dept_table_data=table_data_new,
                dept_table_key=str(uuid.uuid4()),
                dept_table_defaultexpandedrowkeys=default_expanded_row_keys,
                api_check_token_trigger={'timestamp': time.time()},
                fold_click=None
            )

        return dict(
            dept_table_data=dash.no_update,
            dept_table_key=dash.no_update,
            dept_table_defaultexpandedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            fold_click=None
        )

    return dict(
        dept_table_data=dash.no_update,
        dept_table_key=dash.no_update,
        dept_table_defaultexpandedrowkeys=dash.no_update,
        api_check_token_trigger=dash.no_update,
        fold_click=None
    )


# 重置部门搜索表单数据回调
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
)

# 隐藏/显示部门搜索表单回调
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
)


@app.callback(
    output=dict(
        modal_visible=Output('dept-modal', 'visible', allow_duplicate=True),
        modal_title=Output('dept-modal', 'title'),
        parent_id_div_ishidden=Output('dept-parent_id-div', 'hidden'),
        parent_id_tree=Output({'type': 'dept-form-value', 'index': 'parent_id'}, 'treeData'),
        form_value=Output({'type': 'dept-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'dept-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'dept-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('dept-edit-id-store', 'data'),
        modal_type=Output('dept-operations-store-bk', 'data')
    ),
    inputs=dict(
        operation_click=Input({'type': 'dept-operation-button', 'index': ALL}, 'nClicks'),
        button_click=Input('dept-list-table', 'nClicksButton')
    ),
    state=dict(
        clicked_content=State('dept-list-table', 'clickedContent'),
        recently_button_clicked_row=State('dept-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_dept_modal(operation_click, button_click, clicked_content, recently_button_clicked_row):
    """
    显示新增或编辑部门弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'dept-operation-button'} or (
            trigger_id == 'dept-list-table' and clicked_content != '删除'):
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[4]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[5]]
        dept_params = dict(dept_name='')
        if trigger_id == 'dept-list-table' and clicked_content == '修改':
            dept_params['dept_id'] = int(recently_button_clicked_row['key'])
            tree_info = get_dept_tree_for_edit_option_api(dept_params)
        else:
            tree_info = get_dept_tree_api(dept_params)
        if tree_info['code'] == 200:
            tree_data = tree_info['data']

            if trigger_id == {'index': 'add', 'type': 'dept-operation-button'} or (trigger_id == 'dept-list-table' and clicked_content == '新增'):
                dept_info = dict(
                    parent_id=None if trigger_id == {'index': 'add', 'type': 'dept-operation-button'} else str(recently_button_clicked_row['key']),
                    dept_name=None,
                    order_num=None,
                    leader=None,
                    phone=None,
                    email=None,
                    status='0',
                )
                return dict(
                    modal_visible=True,
                    modal_title='新增部门',
                    parent_id_div_ishidden=False,
                    parent_id_tree=tree_data,
                    form_value=[dept_info.get(k) for k in form_value_list],
                    form_label_validate_status=[None] * len(form_label_list),
                    form_label_validate_info=[None] * len(form_label_list),
                    api_check_token_trigger={'timestamp': time.time()},
                    edit_row_info=None,
                    modal_type={'type': 'add'}
                )
            elif trigger_id == 'dept-list-table' and clicked_content == '修改':
                dept_id = int(recently_button_clicked_row['key'])
                dept_info_res = get_dept_detail_api(dept_id=dept_id)
                if dept_info_res['code'] == 200:
                    dept_info = dept_info_res['data']
                    return dict(
                        modal_visible=True,
                        modal_title='编辑部门',
                        parent_id_div_ishidden=dept_info.get('parent_id') == 0,
                        parent_id_tree=tree_data,
                        form_value=[dept_info.get(k) for k in form_value_list],
                        form_label_validate_status=[None] * len(form_label_list),
                        form_label_validate_info=[None] * len(form_label_list),
                        api_check_token_trigger={'timestamp': time.time()},
                        edit_row_info=dept_info,
                        modal_type={'type': 'edit'}
                    )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            parent_id_div_ishidden=dash.no_update,
            parent_id_tree=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None,
            modal_type=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'dept-form-label', 'index': ALL, 'required': True}, 'validateStatus',
                                          allow_duplicate=True),
        form_label_validate_info=Output({'type': 'dept-form-label', 'index': ALL, 'required': True}, 'help',
                                        allow_duplicate=True),
        modal_visible=Output('dept-modal', 'visible'),
        operations=Output('dept-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('dept-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('dept-operations-store-bk', 'data'),
        edit_row_info=State('dept-edit-id-store', 'data'),
        form_value=State({'type': 'dept-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'dept-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def dept_confirm(confirm_trigger, modal_type, edit_row_info, form_value, form_label):
    """
    新增或编辑部门弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}
        if all([form_value_state.get(k) for k in form_label_output_list]):
            params_add = form_value_state
            params_edit = params_add.copy()
            params_edit['dept_id'] = edit_row_info.get('dept_id') if edit_row_info else None
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_dept_api(params_add)
            if modal_type == 'edit':
                api_res = edit_dept_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('dept-delete-text', 'children'),
     Output('dept-delete-confirm-modal', 'visible'),
     Output('dept-delete-ids-store', 'data')],
    [Input('dept-list-table', 'nClicksButton')],
    [State('dept-list-table', 'clickedContent'),
     State('dept-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def dept_delete_modal(button_click, clicked_content, recently_button_clicked_row):
    """
    显示删除部门二次确认弹窗回调
    """
    if button_click:

        if clicked_content == '删除':
            dept_ids = recently_button_clicked_row['key']
        else:
            return dash.no_update

        return [
            f'是否确认删除部门编号为{dept_ids}的部门？',
            True,
            {'dept_ids': dept_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('dept-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('dept-delete-confirm-modal', 'okCounts'),
    State('dept-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def dept_delete_confirm(delete_confirm, dept_ids_data):
    """
    删除部门弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = dept_ids_data
        delete_button_info = delete_dept_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')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/dept_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.dict import get_dict_type_list_api, get_all_dict_type_api, get_dict_type_detail_api, add_dict_type_api, edit_dict_type_api, delete_dict_type_api, export_dict_type_list_api, refresh_dict_api


@app.callback(
    output=dict(
        dict_type_table_data=Output('dict_type-list-table', 'data', allow_duplicate=True),
        dict_type_table_pagination=Output('dict_type-list-table', 'pagination', allow_duplicate=True),
        dict_type_table_key=Output('dict_type-list-table', 'key'),
        dict_type_table_selectedrowkeys=Output('dict_type-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('dict_type-search', 'nClicks'),
        refresh_click=Input('dict_type-refresh', 'nClicks'),
        pagination=Input('dict_type-list-table', 'pagination'),
        operations=Input('dict_type-operations-store', 'data')
    ),
    state=dict(
        dict_name=State('dict_type-dict_name-input', 'value'),
        dict_type=State('dict_type-dict_type-input', 'value'),
        status_select=State('dict_type-status-select', 'value'),
        create_time_range=State('dict_type-create_time-range', 'value'),
        button_perms=State('dict_type-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_dict_type_table_data(search_click, refresh_click, pagination, operations, dict_name, dict_type, status_select, create_time_range, button_perms):
    """
    获取字典类型表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """
    create_time_start = None
    create_time_end = None
    if create_time_range:
        create_time_start = create_time_range[0]
        create_time_end = create_time_range[1]

    query_params = dict(
        dict_name=dict_name,
        dict_type=dict_type,
        status=status_select,
        create_time_start=create_time_start,
        create_time_end=create_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'dict_type-list-table':
        query_params = dict(
            dict_name=dict_name,
            dict_type=dict_type,
            status=status_select,
            create_time_start=create_time_start,
            create_time_end=create_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        table_info = get_dict_type_list_api(query_params)
        if table_info['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['dict_id'])
                item['dict_type'] = {
                    'content': item['dict_type'],
                    'type': 'link',
                }
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:dict:edit' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:dict:remove' in button_perms else {},
                ]

            return dict(
                dict_type_table_data=table_data,
                dict_type_table_pagination=table_pagination,
                dict_type_table_key=str(uuid.uuid4()),
                dict_type_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            dict_type_table_data=dash.no_update,
            dict_type_table_pagination=dash.no_update,
            dict_type_table_key=dash.no_update,
            dict_type_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置字典类型搜索表单数据回调
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'),
     Output('dict_type-create_time-range', 'value'),
     Output('dict_type-operations-store', 'data')],
    Input('dict_type-reset', '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('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
)


@app.callback(
    Output({'type': 'dict_type-operation-button', 'index': 'edit'}, 'disabled'),
    Input('dict_type-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_dict_type_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output({'type': 'dict_type-operation-button', 'index': 'delete'}, 'disabled'),
    Input('dict_type-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_dict_type_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('dict_type-modal', 'visible', allow_duplicate=True),
        modal_title=Output('dict_type-modal', 'title'),
        form_value=Output({'type': 'dict_type-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'dict_type-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'dict_type-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('dict_type-edit-id-store', 'data'),
        modal_type=Output('dict_type-operations-store-bk', 'data')
    ),
    inputs=dict(
        operation_click=Input({'type': 'dict_type-operation-button', 'index': ALL}, 'nClicks'),
        button_click=Input('dict_type-list-table', 'nClicksButton')
    ),
    state=dict(
        selected_row_keys=State('dict_type-list-table', 'selectedRowKeys'),
        clicked_content=State('dict_type-list-table', 'clickedContent'),
        recently_button_clicked_row=State('dict_type-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_dict_type_modal(operation_click, button_click, selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示新增或编辑字典类型弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'dict_type-operation-button'} \
            or trigger_id == {'index': 'edit', 'type': 'dict_type-operation-button'} \
            or (trigger_id == 'dict_type-list-table' and clicked_content == '修改'):
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]
        if trigger_id == {'index': 'add', 'type': 'dict_type-operation-button'}:
            dict_type_info = dict(dict_name=None, dict_type=None, status='0', remark=None,)
            return dict(
                modal_visible=True,
                modal_title='新增字典类型',
                form_value=[dict_type_info.get(k) for k in form_value_list],
                form_label_validate_status=[None] * len(form_label_list),
                form_label_validate_info=[None] * len(form_label_list),
                api_check_token_trigger=dash.no_update,
                edit_row_info=None,
                modal_type={'type': 'add'}
            )
        elif trigger_id == {'index': 'edit', 'type': 'dict_type-operation-button'} or (trigger_id == 'dict_type-list-table' and clicked_content == '修改'):
            if trigger_id == {'index': 'edit', 'type': 'dict_type-operation-button'}:
                dict_id = int(','.join(selected_row_keys))
            else:
                dict_id = int(recently_button_clicked_row['key'])
            dict_type_info_res = get_dict_type_detail_api(dict_id=dict_id)
            if dict_type_info_res['code'] == 200:
                dict_type_info = dict_type_info_res['data']
                return dict(
                    modal_visible=True,
                    modal_title='编辑字典类型',
                    form_value=[dict_type_info.get(k) for k in form_value_list],
                    form_label_validate_status=[None] * len(form_label_list),
                    form_label_validate_info=[None] * len(form_label_list),
                    api_check_token_trigger={'timestamp': time.time()},
                    edit_row_info=dict_type_info if dict_type_info else None,
                    modal_type={'type': 'edit'}
                )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None,
            modal_type=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'dict_type-form-label', 'index': ALL, 'required': True}, 'validateStatus',
                                          allow_duplicate=True),
        form_label_validate_info=Output({'type': 'dict_type-form-label', 'index': ALL, 'required': True}, 'help',
                                        allow_duplicate=True),
        modal_visible=Output('dict_type-modal', 'visible'),
        operations=Output('dict_type-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('dict_type-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('dict_type-operations-store-bk', 'data'),
        edit_row_info=State('dict_type-edit-id-store', 'data'),
        form_value=State({'type': 'dict_type-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'dict_type-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def dict_type_confirm(confirm_trigger, modal_type, edit_row_info, form_value, form_label):
    """
    新增或编字典类型弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}
        if all([form_value_state.get(k) for k in form_label_output_list]):
            params_add = form_value_state
            params_edit = params_add.copy()
            params_edit['dict_id'] = edit_row_info.get('dict_id') if edit_row_info else None
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_dict_type_api(params_add)
            if modal_type == 'edit':
                api_res = edit_dict_type_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('dict_type-delete-text', 'children'),
     Output('dict_type-delete-confirm-modal', 'visible'),
     Output('dict_type-delete-ids-store', 'data')],
    [Input({'type': 'dict_type-operation-button', 'index': ALL}, 'nClicks'),
     Input('dict_type-list-table', 'nClicksButton')],
    [State('dict_type-list-table', 'selectedRowKeys'),
     State('dict_type-list-table', 'clickedContent'),
     State('dict_type-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def dict_type_delete_modal(operation_click, button_click,
                      selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示删除字典类型二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'dict_type-operation-button'} or (
            trigger_id == 'dict_type-list-table' and clicked_content == '删除'):

        if trigger_id == {'index': 'delete', 'type': 'dict_type-operation-button'}:
            dict_ids = ','.join(selected_row_keys)
        else:
            if clicked_content == '删除':
                dict_ids = recently_button_clicked_row['key']
            else:
                return dash.no_update

        return [
            f'是否确认删除字典编号为{dict_ids}的字典类型？',
            True,
            {'dict_ids': dict_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('dict_type-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('dict_type-delete-confirm-modal', 'okCounts'),
    State('dict_type-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def dict_type_delete_confirm(delete_confirm, dict_ids_data):
    """
    删除字典类型弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = dict_ids_data
        delete_button_info = delete_dict_type_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')
        ]

    raise PreventUpdate


@app.callback(
    output=dict(
        dict_data_modal_visible=Output('dict_type_to_dict_data-modal', 'visible'),
        dict_data_modal_title=Output('dict_type_to_dict_data-modal', 'title'),
        dict_data_select_options=Output('dict_data-dict_type-select', 'options'),
        dict_data_select_value=Output('dict_data-dict_type-select', 'value', allow_duplicate=True),
        dict_data_search_nclick=Output('dict_data-search', 'nClicks'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        button_click=Input('dict_type-list-table', 'nClicksButton')
    ),
    state=dict(
        clicked_content=State('dict_type-list-table', 'clickedContent'),
        recently_button_clicked_row=State('dict_type-list-table', 'recentlyButtonClickedRow'),
        dict_data_search_nclick=State('dict_data-search', 'nClicks')
    ),
    prevent_initial_call=True
)
def dict_type_to_dict_data_modal(button_click, clicked_content, recently_button_clicked_row, dict_data_search_nclick):
    """
    显示字典类型对应数据表格弹窗回调
    """

    if button_click and clicked_content == recently_button_clicked_row.get('dict_type').get('content'):
        all_dict_type_info = get_all_dict_type_api({})
        if all_dict_type_info.get('code') == 200:
            all_dict_type = all_dict_type_info.get('data')
            dict_data_options = [dict(label=item.get('dict_name'), value=item.get('dict_type')) for item in all_dict_type]

            return dict(
                dict_data_modal_visible=True,
                dict_data_modal_title='字典数据',
                dict_data_select_options=dict_data_options,
                dict_data_select_value=recently_button_clicked_row.get('dict_type').get('content'),
                dict_data_search_nclick=dict_data_search_nclick + 1 if dict_data_search_nclick else 1,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            dict_data_modal_visible=True,
            dict_data_modal_title='字典数据',
            dict_data_select_options=[],
            dict_data_select_value=recently_button_clicked_row.get('dict_type').get('content'),
            dict_data_search_nclick=dict_data_search_nclick + 1 if dict_data_search_nclick else 1,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


@app.callback(
    [Output('dict_type-export-container', 'data', allow_duplicate=True),
     Output('dict_type-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('dict_type-export', 'nClicks'),
    prevent_initial_call=True
)
def export_dict_type_list(export_click):
    """
    导出字典类型信息回调
    """
    if export_click:
        export_dict_type_res = export_dict_type_list_api({})
        if export_dict_type_res.status_code == 200:
            export_dict_type = export_dict_type_res.content

            return [
                dcc.send_bytes(export_dict_type, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('dict_type-export-container', 'data', allow_duplicate=True),
    Input('dict_type-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_dict_type_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate


@app.callback(
    [Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('dict_type-refresh-cache', 'nClicks'),
    prevent_initial_call=True
)
def refresh_dict_cache(refresh_click):
    """
    刷新缓存回调
    """
    if refresh_click:
        refresh_info_res = refresh_dict_api({})
        if refresh_info_res.get('code') == 200:
            return [
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('刷新成功', type='success')
            ]

        return [
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('刷新失败', type='error')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/dict_c/dict_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.dict import get_dict_data_list_api, get_dict_data_detail_api, add_dict_data_api, edit_dict_data_api, delete_dict_data_api, export_dict_data_list_api


@app.callback(
    output=dict(
        dict_data_table_data=Output('dict_data-list-table', 'data', allow_duplicate=True),
        dict_data_table_pagination=Output('dict_data-list-table', 'pagination', allow_duplicate=True),
        dict_data_table_key=Output('dict_data-list-table', 'key'),
        dict_data_table_selectedrowkeys=Output('dict_data-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('dict_data-search', 'nClicks'),
        refresh_click=Input('dict_data-refresh', 'nClicks'),
        pagination=Input('dict_data-list-table', 'pagination'),
        operations=Input('dict_data-operations-store', 'data')
    ),
    state=dict(
        dict_type=State('dict_data-dict_type-select', 'value'),
        dict_label=State('dict_data-dict_label-input', 'value'),
        status_select=State('dict_data-status-select', 'value'),
        button_perms=State('dict_data-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_dict_data_table_data(search_click, refresh_click, pagination, operations, dict_type, dict_label, status_select, button_perms):
    """
    获取字典数据表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """

    query_params = dict(
        dict_type=dict_type,
        dict_label=dict_label,
        status=status_select,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'dict_data-list-table':
        query_params = dict(
            dict_type=dict_type,
            dict_label=dict_label,
            status=status_select,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        table_info = get_dict_data_list_api(query_params)
        if table_info['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['dict_code'])
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:dict:edit' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:dict:remove' in button_perms else {},
                ]

            return dict(
                dict_data_table_data=table_data,
                dict_data_table_pagination=table_pagination,
                dict_data_table_key=str(uuid.uuid4()),
                dict_data_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            dict_data_table_data=dash.no_update,
            dict_data_table_pagination=dash.no_update,
            dict_data_table_key=dash.no_update,
            dict_data_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置字典数据搜索表单数据回调
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
)


# 隐藏/显示字典数据搜索表单回调
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
)


@app.callback(
    Output({'type': 'dict_data-operation-button', 'index': 'edit'}, 'disabled'),
    Input('dict_data-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_dict_data_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output({'type': 'dict_data-operation-button', 'index': 'delete'}, 'disabled'),
    Input('dict_data-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_dict_data_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('dict_data-modal', 'visible', allow_duplicate=True),
        modal_title=Output('dict_data-modal', 'title'),
        form_value=Output({'type': 'dict_data-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'dict_data-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'dict_data-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('dict_data-edit-id-store', 'data'),
        modal_type=Output('dict_data-operations-store-bk', 'data')
    ),
    inputs=dict(
        operation_click=Input({'type': 'dict_data-operation-button', 'index': ALL}, 'nClicks'),
        button_click=Input('dict_data-list-table', 'nClicksButton')
    ),
    state=dict(
        selected_row_keys=State('dict_data-list-table', 'selectedRowKeys'),
        clicked_content=State('dict_data-list-table', 'clickedContent'),
        recently_button_clicked_row=State('dict_data-list-table', 'recentlyButtonClickedRow'),
        dict_type_select=State('dict_data-dict_type-select', 'value')
    ),
    prevent_initial_call=True
)
def add_edit_dict_data_modal(operation_click, button_click, selected_row_keys, clicked_content,
                        recently_button_clicked_row, dict_type_select):
    """
    显示新增或编辑字典数据弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'dict_data-operation-button'} \
            or trigger_id == {'index': 'edit', 'type': 'dict_data-operation-button'} \
            or (trigger_id == 'dict_data-list-table' and clicked_content == '修改'):
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]
        if trigger_id == {'index': 'add', 'type': 'dict_data-operation-button'}:
            dict_data_info = dict(
                dict_type=dict_type_select,
                dict_label=None,
                dict_value=None,
                css_class=None,
                dict_sort=0,
                list_class='default',
                status='0',
                remark=None
            )
            return dict(
                modal_visible=True,
                modal_title='新增字典数据',
                form_value=[dict_data_info.get(k) for k in form_value_list],
                form_label_validate_status=[None] * len(form_label_list),
                form_label_validate_info=[None] * len(form_label_list),
                api_check_token_trigger=dash.no_update,
                edit_row_info=None,
                modal_type={'type': 'add'}
            )
        elif trigger_id == {'index': 'edit', 'type': 'dict_data-operation-button'} or (trigger_id == 'dict_data-list-table' and clicked_content == '修改'):
            if trigger_id == {'index': 'edit', 'type': 'dict_data-operation-button'}:
                dict_code = int(','.join(selected_row_keys))
            else:
                dict_code = int(recently_button_clicked_row['key'])
            dict_data_info_res = get_dict_data_detail_api(dict_code=dict_code)
            if dict_data_info_res['code'] == 200:
                dict_data_info = dict_data_info_res['data']
                return dict(
                    modal_visible=True,
                    modal_title='编辑字典数据',
                    form_value=[dict_data_info.get(k) for k in form_value_list],
                    form_label_validate_status=[None] * len(form_label_list),
                    form_label_validate_info=[None] * len(form_label_list),
                    api_check_token_trigger={'timestamp': time.time()},
                    edit_row_info=dict_data_info if dict_data_info else None,
                    modal_type={'type': 'edit'}
                )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None,
            modal_type=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'dict_data-form-label', 'index': ALL, 'required': True}, 'validateStatus',
                                          allow_duplicate=True),
        form_label_validate_info=Output({'type': 'dict_data-form-label', 'index': ALL, 'required': True}, 'help',
                                        allow_duplicate=True),
        modal_visible=Output('dict_data-modal', 'visible'),
        operations=Output('dict_data-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('dict_data-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('dict_data-operations-store-bk', 'data'),
        edit_row_info=State('dict_data-edit-id-store', 'data'),
        form_value=State({'type': 'dict_data-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'dict_data-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def dict_data_confirm(confirm_trigger, modal_type, edit_row_info, form_value, form_label):
    """
    新增或编字典数据弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}
        if all([form_value_state.get(k) for k in form_label_output_list]):
            params_add = form_value_state
            params_edit = params_add.copy()
            params_edit['dict_code'] = edit_row_info.get('dict_code') if edit_row_info else None
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_dict_data_api(params_add)
            if modal_type == 'edit':
                api_res = edit_dict_data_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('dict_data-delete-text', 'children'),
     Output('dict_data-delete-confirm-modal', 'visible'),
     Output('dict_data-delete-ids-store', 'data')],
    [Input({'type': 'dict_data-operation-button', 'index': ALL}, 'nClicks'),
     Input('dict_data-list-table', 'nClicksButton')],
    [State('dict_data-list-table', 'selectedRowKeys'),
     State('dict_data-list-table', 'clickedContent'),
     State('dict_data-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def dict_data_delete_modal(operation_click, button_click,
                      selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示删除字典数据二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'dict_data-operation-button'} or (
            trigger_id == 'dict_data-list-table' and clicked_content == '删除'):

        if trigger_id == {'index': 'delete', 'type': 'dict_data-operation-button'}:
            dict_codes = ','.join(selected_row_keys)
        else:
            if clicked_content == '删除':
                dict_codes = recently_button_clicked_row['key']
            else:
                return dash.no_update

        return [
            f'是否确认删除字典编码为{dict_codes}的数据？',
            True,
            {'dict_codes': dict_codes}
        ]

    raise PreventUpdate


@app.callback(
    [Output('dict_data-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('dict_data-delete-confirm-modal', 'okCounts'),
    State('dict_data-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def dict_data_delete_confirm(delete_confirm, dict_codes_data):
    """
    删除字典数据弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = dict_codes_data
        delete_button_info = delete_dict_data_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')
        ]

    raise PreventUpdate


@app.callback(
    [Output('dict_data-export-container', 'data', allow_duplicate=True),
     Output('dict_data-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('dict_data-export', 'nClicks'),
    State('dict_data-dict_type-select', 'value'),
    prevent_initial_call=True
)
def export_dict_data_list(export_click, dict_type):
    """
    导出字典数据信息回调
    """
    if export_click:
        export_dict_data_res = export_dict_data_list_api(dict(dict_type=dict_type))
        if export_dict_data_res.status_code == 200:
            export_dict_data = export_dict_data_res.content

            return [
                dcc.send_bytes(export_dict_data, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('dict_data-export-container', 'data', allow_duplicate=True),
    Input('dict_data-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_dict_data_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/dict_c/dict_data_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.menu import add_menu_api, edit_menu_api


@app.callback(
    output=dict(
        form_validate=[
            Output('menu-parent_id-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-order_num-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-parent_id-form-item', 'help', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'help', allow_duplicate=True),
            Output('menu-order_num-form-item', 'help', allow_duplicate=True)
        ],
        modal_visible=Output('menu-modal', 'visible', allow_duplicate=True),
        operations=Output('menu-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('menu-modal-F-trigger', 'data'),
    ),
    state=dict(
        modal_type=State('menu-operations-store-bk', 'data'),
        edit_row_info=State('menu-edit-id-store', 'data'),
        parent_id=State('menu-parent_id', 'value'),
        menu_type=State('menu-menu_type', 'value'),
        icon=State('menu-icon', 'value'),
        menu_name=State('menu-menu_name', 'value'),
        order_num=State('menu-order_num', 'value'),
        perms=State('button-menu-perms', 'value')
    ),
    prevent_initial_call=True
)
def menu_confirm_button(confirm_trigger, modal_type, edit_row_info, parent_id, menu_type, icon, menu_name, order_num, perms):
    """
    菜单类型为按钮时新增或编辑弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        if all([parent_id, menu_name, order_num]):
            params_add = dict(parent_id=parent_id, menu_type=menu_type, icon=icon, menu_name=menu_name, order_num=order_num, perms=perms)
            params_edit = dict(menu_id=edit_row_info.get('menu_id') if edit_row_info else None, parent_id=parent_id, menu_type=menu_type, icon=icon,
                            menu_name=menu_name, order_num=order_num, perms=perms)
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_menu_api(params_add)
            if modal_type == 'edit':
                api_res = edit_menu_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_validate=[None] * 6,
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_validate=[None] * 6,
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_validate=[None] * 6,
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_validate=[
                None if parent_id else 'error',
                None if menu_name else 'error',
                None if order_num else 'error',
                None if parent_id else '请选择上级菜单！',
                None if menu_name else '请输入菜单名称！',
                None if order_num else '请输入显示排序！'
            ],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    Output('button-menu-perms', 'value'),
    Input('menu-edit-id-store', 'data')
)
def set_edit_info(edit_info):
    """
    菜单类型为按钮时回显菜单数据回调
    """
    if edit_info:
        return edit_info.get('perms')

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/menu_c/components_c/button_type_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.menu import add_menu_api, edit_menu_api


@app.callback(
    output=dict(
        form_validate=[
            Output('menu-parent_id-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-order_num-form-item', 'validateStatus', allow_duplicate=True),
            Output('content-menu-path-form-item', 'validateStatus'),
            Output('menu-parent_id-form-item', 'help', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'help', allow_duplicate=True),
            Output('menu-order_num-form-item', 'help', allow_duplicate=True),
            Output('content-menu-path-form-item', 'help'),
        ],
        modal_visible=Output('menu-modal', 'visible', allow_duplicate=True),
        operations=Output('menu-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('menu-modal-M-trigger', 'data')
    ),
    state=dict(
        modal_type=State('menu-operations-store-bk', 'data'),
        edit_row_info=State('menu-edit-id-store', 'data'),
        parent_id=State('menu-parent_id', 'value'),
        menu_type=State('menu-menu_type', 'value'),
        icon=State('menu-icon', 'value'),
        menu_name=State('menu-menu_name', 'value'),
        order_num=State('menu-order_num', 'value'),
        is_frame=State('content-menu-is_frame', 'value'),
        path=State('content-menu-path', 'value'),
        visible=State('content-menu-visible', 'value'),
        status=State('content-menu-status', 'value')
    ),
    prevent_initial_call=True
)
def menu_confirm_content(confirm_trigger, modal_type, edit_row_info, parent_id, menu_type, icon, menu_name, order_num, is_frame, path, visible, status):
    """
    菜单类型为目录时新增或编辑弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        if all([parent_id, menu_name, order_num, path]):
            params_add = dict(parent_id=parent_id, menu_type=menu_type, icon=icon, menu_name=menu_name, order_num=order_num,
                            is_frame=is_frame, path=path, visible=visible, status=status)
            params_edit = dict(menu_id=edit_row_info.get('menu_id') if edit_row_info else None, parent_id=parent_id, menu_type=menu_type, icon=icon,
                            menu_name=menu_name, order_num=order_num, is_frame=is_frame, path=path, visible=visible, status=status)
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_menu_api(params_add)
            if modal_type == 'edit':
                api_res = edit_menu_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_validate=[None] * 8,
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_validate=[None] * 8,
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_validate=[None] * 8,
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_validate=[
                None if parent_id else 'error',
                None if menu_name else 'error',
                None if order_num else 'error',
                None if path else 'error',
                None if parent_id else '请选择上级菜单！',
                None if menu_name else '请输入菜单名称！',
                None if order_num else '请输入显示排序！',
                None if path else '请输入路由地址！',
            ],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('content-menu-is_frame', 'value'),
     Output('content-menu-path', 'value'),
     Output('content-menu-visible', 'value'),
     Output('content-menu-status', 'value')],
    Input('menu-edit-id-store', 'data')
)
def set_edit_info(edit_info):
    """
    菜单类型为目录时回显菜单数据回调
    """
    if edit_info:
        return [
            edit_info.get('is_frame'),
            edit_info.get('path'),
            edit_info.get('visible'),
            edit_info.get('status')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/menu_c/components_c/content_type_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.menu import add_menu_api, edit_menu_api


@app.callback(
    output=dict(
        form_validate=[
            Output('menu-parent_id-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-order_num-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-menu-path-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-parent_id-form-item', 'help', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'help', allow_duplicate=True),
            Output('menu-order_num-form-item', 'help', allow_duplicate=True),
            Output('menu-menu-path-form-item', 'help', allow_duplicate=True),
        ],
        modal_visible=Output('menu-modal', 'visible', allow_duplicate=True),
        operations=Output('menu-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('menu-modal-C-trigger', 'data')
    ),
    state=dict(
        modal_type=State('menu-operations-store-bk', 'data'),
        edit_row_info=State('menu-edit-id-store', 'data'),
        parent_id=State('menu-parent_id', 'value'),
        menu_type=State('menu-menu_type', 'value'),
        icon=State('menu-icon', 'value'),
        menu_name=State('menu-menu_name', 'value'),
        order_num=State('menu-order_num', 'value'),
        is_frame=State('menu-menu-is_frame', 'value'),
        path=State('menu-menu-path', 'value'),
        component=State('menu-menu-component', 'value'),
        perms=State('menu-menu-perms', 'value'),
        query=State('menu-menu-query', 'value'),
        is_cache=State('menu-menu-is_cache', 'value'),
        visible=State('menu-menu-visible', 'value'),
        status=State('menu-menu-status', 'value')
    ),
    prevent_initial_call=True
)
def menu_confirm_menu(confirm_trigger, modal_type, edit_row_info, parent_id, menu_type, icon, menu_name, order_num, is_frame, path,
                 component, perms, query, is_cache, visible, status):
    """
    菜单类型为菜单时新增或编辑弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        if all([parent_id, menu_name, order_num, path]):
            params_add = dict(parent_id=parent_id, menu_type=menu_type, icon=icon, menu_name=menu_name, order_num=order_num, is_frame=is_frame, 
                              path=path, component=component, perms=perms, query=query, is_cache=is_cache, visible=visible, status=status)
            params_edit = dict(menu_id=edit_row_info.get('menu_id') if edit_row_info else None, parent_id=parent_id, menu_type=menu_type, icon=icon,
                            menu_name=menu_name, order_num=order_num, is_frame=is_frame, path=path, component=component, 
                            perms=perms, query=query, is_cache=is_cache, visible=visible, status=status)
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_menu_api(params_add)
            if modal_type == 'edit':
                api_res = edit_menu_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_validate=[None] * 8,
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_validate=[None] * 8,
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_validate=[None] * 8,
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_validate=[
                None if parent_id else 'error',
                None if menu_name else 'error',
                None if order_num else 'error',
                None if path else 'error',
                None if parent_id else '请选择上级菜单！',
                None if menu_name else '请输入菜单名称！',
                None if order_num else '请输入显示排序！',
                None if path else '请输入路由地址！'
            ],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('menu-menu-is_frame', 'value'),
     Output('menu-menu-path', 'value'),
     Output('menu-menu-component', 'value'),
     Output('menu-menu-perms', 'value'),
     Output('menu-menu-query', 'value'),
     Output('menu-menu-is_cache', 'value'),
     Output('menu-menu-visible', 'value'),
     Output('menu-menu-status', 'value')],
    Input('menu-edit-id-store', 'data')
)
def set_edit_info(edit_info):
    """
    菜单类型为菜单时回显菜单数据回调
    """
    if edit_info:
        return [
            edit_info.get('is_frame'),
            edit_info.get('path'),
            edit_info.get('component'),
            edit_info.get('perms'),
            edit_info.get('query'),
            edit_info.get('is_cache'),
            edit_info.get('visible'),
            edit_info.get('status')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/menu_c/components_c/menu_type_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_antd_components as fac
import feffery_utils_components as fuc

from server import app
from utils.tree_tool import list_to_tree
from views.system.menu.components import content_type, menu_type, button_type
from api.menu import get_menu_tree_api, get_menu_tree_for_edit_option_api, get_menu_list_api, delete_menu_api, get_menu_detail_api


@app.callback(
    output=dict(
        menu_table_data=Output('menu-list-table', 'data', allow_duplicate=True),
        menu_table_key=Output('menu-list-table', 'key'),
        menu_table_defaultexpandedrowkeys=Output('menu-list-table', 'defaultExpandedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        fold_click=Output('menu-fold', 'nClicks')
    ),
    inputs=dict(
        search_click=Input('menu-search', 'nClicks'),
        refresh_click=Input('menu-refresh', 'nClicks'),
        operations=Input('menu-operations-store', 'data'),
        fold_click=Input('menu-fold', 'nClicks')
    ),
    state=dict(
        menu_name=State('menu-menu_name-input', 'value'),
        status_select=State('menu-status-select', 'value'),
        in_default_expanded_row_keys=State('menu-list-table', 'defaultExpandedRowKeys'),
        button_perms=State('menu-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_menu_table_data(search_click, refresh_click, operations, fold_click, menu_name, status_select, in_default_expanded_row_keys, button_perms):
    """
    获取菜单表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """

    query_params = dict(
        menu_name=menu_name,
        status=status_select
    )
    if search_click or refresh_click or operations or fold_click:
        table_info = get_menu_list_api(query_params)
        default_expanded_row_keys = []
        if table_info['code'] == 200:
            table_data = table_info['data']['rows']
            for item in table_data:
                default_expanded_row_keys.append(str(item['menu_id']))
                item['key'] = str(item['menu_id'])
                item['icon'] = [
                    {
                        'type': 'link',
                        'icon': item['icon'],
                        'disabled': True,
                        'style': {
                            'color': 'rgba(0, 0, 0, 0.8)'
                        }
                    },
                ]
                if item['status'] == '1':
                    item['operation'] = [
                        {
                            'content': '修改',
                            'type': 'link',
                            'icon': 'antd-edit'
                        } if 'system:menu:edit' in button_perms else {},
                        {
                            'content': '删除',
                            'type': 'link',
                            'icon': 'antd-delete'
                        } if 'system:menu:remove' in button_perms else {},
                    ]
                else:
                    item['operation'] = [
                        {
                            'content': '修改',
                            'type': 'link',
                            'icon': 'antd-edit'
                        } if 'system:menu:edit' in button_perms else {},
                        {
                            'content': '新增',
                            'type': 'link',
                            'icon': 'antd-plus'
                        } if 'system:menu:add' in button_perms else {},
                        {
                            'content': '删除',
                            'type': 'link',
                            'icon': 'antd-delete'
                        } if 'system:menu:remove' in button_perms else {},
                    ]
                if item['status'] == '0':
                    item['status'] = dict(tag='正常', color='blue')
                else:
                    item['status'] = dict(tag='停用', color='volcano')
            table_data_new = list_to_tree(table_data)

            if fold_click:
                if not in_default_expanded_row_keys:
                    return dict(
                        menu_table_data=table_data_new,
                        menu_table_key=str(uuid.uuid4()),
                        menu_table_defaultexpandedrowkeys=default_expanded_row_keys,
                        api_check_token_trigger={'timestamp': time.time()},
                        fold_click=None
                    )

            return dict(
                menu_table_data=table_data_new,
                menu_table_key=str(uuid.uuid4()),
                menu_table_defaultexpandedrowkeys=[],
                api_check_token_trigger={'timestamp': time.time()},
                fold_click=None
            )

        return dict(
            menu_table_data=dash.no_update,
            menu_table_key=dash.no_update,
            menu_table_defaultexpandedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            fold_click=None
        )

    return dict(
        menu_table_data=dash.no_update,
        menu_table_key=dash.no_update,
        menu_table_defaultexpandedrowkeys=dash.no_update,
        api_check_token_trigger=dash.no_update,
        fold_click=None
    )


# 重置菜单搜索表单数据回调
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
)


# 隐藏/显示菜单搜索表单回调
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
)


@app.callback(
    [Output('menu-icon', 'value'),
     Output('menu-icon', 'prefix')],
    Input('icon-category', 'value'),
    prevent_initial_call=True
)
def get_select_icon(icon):
    """
    获取新增或编辑表单中选择的icon回调
    """
    if icon:
        return [
            icon,
            fac.AntdIcon(icon=icon)
        ]

    raise PreventUpdate


@app.callback(
    output=dict(
        modal=dict(visible=Output('menu-modal', 'visible', allow_duplicate=True), title=Output('menu-modal', 'title')),
        form_value=dict(
            parent_tree=Output('menu-parent_id', 'treeData'), parent_id=Output('menu-parent_id', 'value'),
            menu_type=Output('menu-menu_type', 'value'), icon=Output('menu-icon', 'value', allow_duplicate=True),
            icon_prefix=Output('menu-icon', 'prefix', allow_duplicate=True), icon_category=Output('icon-category', 'value'),
            menu_name=Output('menu-menu_name', 'value'), order_num=Output('menu-order_num', 'value')
        ),
        form_validate=[
            Output('menu-parent_id-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-order_num-form-item', 'validateStatus', allow_duplicate=True),
            Output('menu-parent_id-form-item', 'help', allow_duplicate=True),
            Output('menu-menu_name-form-item', 'help', allow_duplicate=True),
            Output('menu-order_num-form-item', 'help', allow_duplicate=True)
        ],
        other=dict(
            api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
            edit_row_info=Output('menu-edit-id-store', 'data'),
            modal_type=Output('menu-operations-store-bk', 'data')
        )
    ),
    inputs=dict(
        operation_click=Input({'type': 'menu-operation-button', 'index': ALL}, 'nClicks'),
        button_click=Input('menu-list-table', 'nClicksButton')
    ),
    state=dict(
        clicked_content=State('menu-list-table', 'clickedContent'),
        recently_button_clicked_row=State('menu-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_menu_modal(operation_click, button_click, clicked_content, recently_button_clicked_row):
    """
    显示新增或编辑菜单弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'menu-operation-button'} or (trigger_id == 'menu-list-table' and clicked_content != '删除'):
        menu_params = dict(menu_name='')
        if clicked_content == '修改':
            tree_info = get_menu_tree_for_edit_option_api(menu_params)
        else:
            tree_info = get_menu_tree_api(menu_params)
        if tree_info['code'] == 200:
            tree_data = tree_info['data']

            if trigger_id == {'index': 'add', 'type': 'menu-operation-button'}:
                return dict(
                    modal=dict(visible=True, title='新增菜单'),
                    form_value=dict(
                        parent_tree=tree_data, parent_id='0', menu_type='M', icon=None,
                        icon_prefix=None, icon_category=None, menu_name=None, order_num=None
                    ),
                    form_validate=[None] * 6,
                    other=dict(
                        api_check_token_trigger={'timestamp': time.time()},
                        edit_row_info=None,
                        modal_type={'type': 'add'}
                    )
                )
            elif trigger_id == 'menu-list-table' and clicked_content == '新增':
                return dict(
                    modal=dict(visible=True, title='新增菜单'),
                    form_value=dict(
                        parent_tree=tree_data, parent_id=str(recently_button_clicked_row['key']), menu_type='M',
                        icon=None, icon_prefix=None, icon_category=None, menu_name=None, order_num=None
                    ),
                    form_validate=[None] * 6,
                    other=dict(
                        api_check_token_trigger={'timestamp': time.time()},
                        edit_row_info=None,
                        modal_type={'type': 'add'}
                    )
                )
            elif trigger_id == 'menu-list-table' and clicked_content == '修改':
                menu_id = int(recently_button_clicked_row['key'])
                menu_info_res = get_menu_detail_api(menu_id=menu_id)
                if menu_info_res['code'] == 200:
                    menu_info = menu_info_res['data']
                    return dict(
                        modal=dict(visible=True, title='编辑菜单'),
                        form_value=dict(
                            parent_tree=tree_data, parent_id=str(menu_info.get('parent_id')),
                            menu_type=menu_info.get('menu_type'), icon=menu_info.get('icon'),
                            icon_prefix=fac.AntdIcon(icon=menu_info.get('icon')), icon_category=menu_info.get('icon'),
                            menu_name=menu_info.get('menu_name'), order_num=menu_info.get('order_num')
                        ),
                        form_validate=[None] * 6,
                        other=dict(
                            api_check_token_trigger={'timestamp': time.time()},
                            edit_row_info=menu_info,
                            modal_type={'type': 'edit'}
                        )
                    )

        return dict(
            modal=dict(visible=dash.no_update, title=dash.no_update),
            form_value=dict(
                parent_tree=dash.no_update, parent_id=dash.no_update, menu_type=dash.no_update,
                icon=dash.no_update, icon_prefix=dash.no_update, icon_category=dash.no_update,
                menu_name=dash.no_update, order_num=dash.no_update
            ),
            form_validate=[dash.no_update] * 6,
            other=dict(
                api_check_token_trigger={'timestamp': time.time()},
                edit_row_info=None,
                modal_type=None
            )
        )

    raise PreventUpdate


@app.callback(
    [Output('content-by-menu-type', 'children'),
     Output('content-by-menu-type', 'key'),
     Output('menu-modal-menu-type-store', 'data')],
    Input('menu-menu_type', 'value'),
    prevent_initial_call=True
)
def get_bottom_content(menu_value):
    """
    根据不同菜单类型渲染不同的子区域
    """
    if menu_value == 'M':
        return [content_type.render(), str(uuid.uuid4()), {'type': 'M'}]

    elif menu_value == 'C':
        return [menu_type.render(), str(uuid.uuid4()), {'type': 'C'}]

    elif menu_value == 'F':
        return [button_type.render(), str(uuid.uuid4()), {'type': 'F'}]

    raise PreventUpdate


@app.callback(
    [Output('menu-modal-M-trigger', 'data'),
     Output('menu-modal-C-trigger', 'data'),
     Output('menu-modal-F-trigger', 'data')],
    Input('menu-modal', 'okCounts'),
    State('menu-modal-menu-type-store', 'data'),
)
def modal_confirm_trigger(confirm, menu_type):
    """
    增加触发器，根据不同菜单类型触发不同的回调，解决组件不存在回调异常的问题
    """
    if confirm:
        if menu_type.get('type') == 'M':
            return [
                {'timestamp': time.time()},
                dash.no_update,
                dash.no_update
            ]
        if menu_type.get('type') == 'C':
            return [
                dash.no_update,
                {'timestamp': time.time()},
                dash.no_update
            ]
            
        if menu_type.get('type') == 'F':
            return [
                dash.no_update,
                dash.no_update,
                {'timestamp': time.time()}
            ]

    raise PreventUpdate


@app.callback(
    [Output('menu-delete-text', 'children'),
     Output('menu-delete-confirm-modal', 'visible'),
     Output('menu-delete-ids-store', 'data')],
    [Input('menu-list-table', 'nClicksButton')],
    [State('menu-list-table', 'clickedContent'),
     State('menu-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def menu_delete_modal(button_click, clicked_content, recently_button_clicked_row):
    """
    显示删除菜单二次确认弹窗回调
    """
    if button_click:

        if clicked_content == '删除':
            menu_ids = recently_button_clicked_row['key']
        else:
            return dash.no_update

        return [
            f'是否确认删除菜单编号为{menu_ids}的菜单？',
            True,
            {'menu_ids': menu_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('menu-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('menu-delete-confirm-modal', 'okCounts'),
    State('menu-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def menu_delete_confirm(delete_confirm, menu_ids_data):
    """
    删除菜单弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = menu_ids_data
        delete_button_info = delete_menu_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')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/menu_c/menu_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
import re
import json
from flask import session
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from config.global_config import ApiBaseUrlConfig
from api.notice import get_notice_list_api, add_notice_api, edit_notice_api, delete_notice_api, get_notice_detail_api
from api.dict import query_dict_data_list_api


@app.callback(
    output=dict(
        notice_table_data=Output('notice-list-table', 'data', allow_duplicate=True),
        notice_table_pagination=Output('notice-list-table', 'pagination', allow_duplicate=True),
        notice_table_key=Output('notice-list-table', 'key'),
        notice_table_selectedrowkeys=Output('notice-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('notice-search', 'nClicks'),
        refresh_click=Input('notice-refresh', 'nClicks'),
        pagination=Input('notice-list-table', 'pagination'),
        operations=Input('notice-operations-store', 'data')
    ),
    state=dict(
        notice_title=State('notice-notice_title-input', 'value'),
        update_by=State('notice-update_by-input', 'value'),
        notice_type=State('notice-notice_type-select', 'value'),
        create_time_range=State('notice-create_time-range', 'value'),
        button_perms=State('notice-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_notice_table_data(search_click, refresh_click, pagination, operations, notice_title, update_by, notice_type, create_time_range,
                          button_perms):
    """
    获取通知公告表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """
    create_time_start = None
    create_time_end = None
    if create_time_range:
        create_time_start = create_time_range[0]
        create_time_end = create_time_range[1]

    query_params = dict(
        notice_title=notice_title,
        update_by=update_by,
        notice_type=notice_type,
        create_time_start=create_time_start,
        create_time_end=create_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'notice-list-table':
        query_params = dict(
            notice_title=notice_title,
            update_by=update_by,
            notice_type=notice_type,
            create_time_start=create_time_start,
            create_time_end=create_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        option_table = []
        info = query_dict_data_list_api(dict_type='sys_notice_type')
        if info.get('code') == 200:
            data = info.get('data')
            option_table = [
                dict(label=item.get('dict_label'), value=item.get('dict_value'), css_class=item.get('css_class')) for
                item in data]
        option_dict = {item.get('value'): item for item in option_table}

        table_info = get_notice_list_api(query_params)
        if table_info['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['notice_type'] = dict(
                    tag=option_dict.get(str(item.get('notice_type'))).get('label'),
                    color=json.loads(option_dict.get(str(item.get('notice_type'))).get('css_class')).get('color')
                )
                item['key'] = str(item['notice_id'])
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:notice:edit' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:notice:remove' in button_perms else {},
                ]

            return dict(
                notice_table_data=table_data,
                notice_table_pagination=table_pagination,
                notice_table_key=str(uuid.uuid4()),
                notice_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            notice_table_data=dash.no_update,
            notice_table_pagination=dash.no_update,
            notice_table_key=dash.no_update,
            notice_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置通知公告搜索表单数据回调
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'),
     Output('notice-create_time-range', 'value'),
     Output('notice-operations-store', 'data')],
    Input('notice-reset', '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('notice-search-form-container', 'hidden'),
     Output('notice-hidden-tooltip', 'title')],
    Input('notice-hidden', 'nClicks'),
    State('notice-search-form-container', 'hidden'),
    prevent_initial_call=True
)


@app.callback(
    Output({'type': 'notice-operation-button', 'index': 'edit'}, 'disabled'),
    Input('notice-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_notice_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output({'type': 'notice-operation-button', 'index': 'delete'}, 'disabled'),
    Input('notice-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_notice_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output('notice-init-editor', 'jsString'),
    Input('notice-written-editor-store', 'data'),
    prevent_initial_call=True
)
def init_render_editor(html_string):
    """
    初始化富文本编辑器回调
    """
    url = f'{ApiBaseUrlConfig.BaseUrl}/common/uploadForEditor'
    token = 'Bearer ' + session.get('Authorization')

    js_string = '''
            const { i18nChangeLanguage, createEditor, createToolbar } = window.wangEditor
            
            // 切换至中文
            i18nChangeLanguage('zh-CN')
            
            const editorConfig = {
                placeholder: '请输入...',
                onChange(editor) {
                  const html = editor.getHtml()
                  sessionStorage.setItem('notice-content', JSON.stringify({html: html}))
                },
                // 图片和视频上传参数配置
                MENU_CONF: {
                    uploadImage: {
                        server: '% s',
                        // form-data fieldName ，默认值 'editor-uploaded-file'
                        fieldName: 'file',
                        // 单个文件的最大体积限制，默认为 2M
                        maxFileSize: 10 * 1024 * 1024, // 10M
                        // 最多可上传几个文件，默认为 100
                        maxNumberOfFiles: 10,
                        // 选择文件时的类型限制，默认为 ['image/*'] 。如不想限制，则设置为 []
                        allowedFileTypes: ['image/*'],
                        // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
                        meta: {
                            uploadId: '% s',
                        },
                        // 将 meta 拼接到 url 参数中，默认 false
                        metaWithUrl: true,
                        // 自定义增加 http  header
                        headers: {
                            Authorization: '% s'
                        },
                        // 跨域是否传递 cookie ，默认为 false
                        withCredentials: true,
                        // 超时时间，默认为 10 秒
                        timeout: 5 * 1000, // 5 秒
                        // 小于该值就插入 base64 格式（而不上传），默认为 0
                        base64LimitSize: 500 * 1024 // 500KB
                    },
                    uploadVideo: {
                        server: '% s',
                        // form-data fieldName ，默认值 'wangeditor-uploaded-video'
                        fieldName: 'file',
                        // 单个文件的最大体积限制，默认为 10M
                        maxFileSize: 100 * 1024 * 1024, // 100M
                        // 最多可上传几个文件，默认为 5
                        maxNumberOfFiles: 3,
                        // 选择文件时的类型限制，默认为 ['video/*'] 。如不想限制，则设置为 []
                        allowedFileTypes: ['video/*'],
                        // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
                        meta: {
                            uploadId: '% s',
                        },
                        // 将 meta 拼接到 url 参数中，默认 false
                        metaWithUrl: true,
                        // 自定义增加 http  header
                        headers: {
                            Authorization: '% s'
                        },
                        // 跨域是否传递 cookie ，默认为 false
                        withCredentials: true,
                        // 超时时间，默认为 30 秒
                        timeout: 15 * 1000, // 15 秒
                    }
                }
            }
            
            
            const editor = createEditor({
                selector: '#notice-notice_content-editor-container',
                html: '% s',
                config: editorConfig,
                mode: 'default'
            })
            
            const toolbarConfig = {}
            
            const toolbar = createToolbar({
                editor,
                selector: '#notice-notice_content-toolbar-container',
                config: toolbarConfig,
                mode: 'default'
            })
            ''' % (url, f'notice_{uuid.uuid4()}', token, url, f'notice_{uuid.uuid4()}', token, html_string)

    return js_string


@app.callback(
    output=dict(
        modal=dict(visible=Output('notice-modal', 'visible', allow_duplicate=True), title=Output('notice-modal', 'title')),
        form_value=dict(
            notice_title=Output('notice-notice_title', 'value'),
            notice_type=Output('notice-notice_type', 'value'),
            status=Output('notice-status', 'value'),
            editor_content=Output('notice-written-editor-store', 'data'),
        ),
        form_validate=[
            Output('notice-notice_title-form-item', 'validateStatus', allow_duplicate=True),
            Output('notice-notice_type-form-item', 'validateStatus', allow_duplicate=True),
            Output('notice-notice_title-form-item', 'help', allow_duplicate=True),
            Output('notice-notice_type-form-item', 'help', allow_duplicate=True)
        ],
        other=dict(
            api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
            edit_row_info=Output('notice-edit-id-store', 'data'),
            modal_type=Output('notice-operations-store-bk', 'data')
        )
    ),
    inputs=dict(
        operation_click=Input({'type': 'notice-operation-button', 'index': ALL}, 'nClicks'),
        button_click=Input('notice-list-table', 'nClicksButton')
    ),
    state=dict(
        selected_row_keys=State('notice-list-table', 'selectedRowKeys'),
        clicked_content=State('notice-list-table', 'clickedContent'),
        recently_button_clicked_row=State('notice-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_notice_modal(operation_click, button_click, selected_row_keys, clicked_content,
                          recently_button_clicked_row):
    """
    显示新增或编辑通知公告弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'notice-operation-button'} \
            or trigger_id == {'index': 'edit', 'type': 'notice-operation-button'} \
            or (trigger_id == 'notice-list-table' and clicked_content == '修改'):
        if trigger_id == {'index': 'add', 'type': 'notice-operation-button'}:
            return dict(
                modal=dict(visible=True, title='新增通知公告'),
                form_value=dict(notice_title=None, notice_type=None, status='0', editor_content='<p><br></p>'),
                form_validate=[None] * 4,
                other=dict(
                    api_check_token_trigger=dash.no_update,
                    edit_row_info=None,
                    modal_type={'type': 'add'}
                )
            )
        elif trigger_id == {'index': 'edit', 'type': 'notice-operation-button'} or (trigger_id == 'notice-list-table' and clicked_content == '修改'):
            if trigger_id == {'index': 'edit', 'type': 'notice-operation-button'}:
                notice_id = int(','.join(selected_row_keys))
            else:
                notice_id = int(recently_button_clicked_row['key'])
            notice_info_res = get_notice_detail_api(notice_id=notice_id)
            if notice_info_res['code'] == 200:
                notice_info = notice_info_res['data']
                notice_content = notice_info.get('notice_content')

                return dict(
                    modal=dict(visible=True, title='编辑通知公告'),
                    form_value=dict(
                        notice_title=notice_info.get('notice_title'),
                        notice_type=notice_info.get('notice_type'),
                        status=notice_info.get('status'),
                        editor_content=re.sub(r"\n", "", notice_content)
                    ),
                    form_validate=[None] * 4,
                    other=dict(
                        api_check_token_trigger={'timestamp': time.time()},
                        edit_row_info=notice_info if notice_info else None,
                        modal_type={'type': 'edit'}
                    )
                )

        return dict(
            modal=dict(visible=dash.no_update, title=dash.no_update),
            form_value=dict(
                notice_title=dash.no_update,
                notice_type=dash.no_update,
                status=dash.no_update,
                editor_content=dash.no_update
            ),
            form_validate=[dash.no_update] * 4,
            other=dict(
                api_check_token_trigger={'timestamp': time.time()},
                edit_row_info=None,
                modal_type=None
            )
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        notice_title_form_status=Output('notice-notice_title-form-item', 'validateStatus', allow_duplicate=True),
        notice_type_form_status=Output('notice-notice_type-form-item', 'validateStatus', allow_duplicate=True),
        notice_title_form_help=Output('notice-notice_title-form-item', 'help', allow_duplicate=True),
        notice_type_form_help=Output('notice-notice_type-form-item', 'help', allow_duplicate=True),
        modal_visible=Output('notice-modal', 'visible'),
        operations=Output('notice-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('notice-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('notice-operations-store-bk', 'data'),
        edit_row_info=State('notice-edit-id-store', 'data'),
        notice_title=State('notice-notice_title', 'value'),
        notice_type=State('notice-notice_type', 'value'),
        status=State('notice-status', 'value'),
        notice_content=State('notice-content', 'data')
    ),
    prevent_initial_call=True
)
def notice_confirm(confirm_trigger, modal_type, edit_row_info, notice_title, notice_type, status, notice_content):
    """
    新增或编辑通知公告弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        if all([notice_title, notice_type]):
            params_add = dict(notice_title=notice_title, notice_type=notice_type, status=status,
                              notice_content=notice_content.get('html'))
            params_edit = dict(notice_id=edit_row_info.get('notice_id') if edit_row_info else None,
                               notice_title=notice_title,
                               notice_type=notice_type, status=status, notice_content=notice_content.get('html'))
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_notice_api(params_add)
            if modal_type == 'edit':
                api_res = edit_notice_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        notice_title_form_status=None,
                        notice_type_form_status=None,
                        notice_title_form_help=None,
                        notice_type_form_help=None,
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        notice_title_form_status=None,
                        notice_type_form_status=None,
                        notice_title_form_help=None,
                        notice_type_form_help=None,
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                notice_title_form_status=None,
                notice_type_form_status=None,
                notice_title_form_help=None,
                notice_type_form_help=None,
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            notice_title_form_status=None if notice_title else 'error',
            notice_type_form_status=None if notice_type else 'error',
            notice_title_form_help=None if notice_title else '请输入公告标题！',
            notice_type_form_help=None if notice_type else '请输入公告类型！',
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('notice-delete-text', 'children'),
     Output('notice-delete-confirm-modal', 'visible'),
     Output('notice-delete-ids-store', 'data')],
    [Input({'type': 'notice-operation-button', 'index': ALL}, 'nClicks'),
     Input('notice-list-table', 'nClicksButton')],
    [State('notice-list-table', 'selectedRowKeys'),
     State('notice-list-table', 'clickedContent'),
     State('notice-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def notice_delete_modal(operation_click, button_click,
                        selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示删除通知公告二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'notice-operation-button'} or (
            trigger_id == 'notice-list-table' and clicked_content == '删除'):
        trigger_id = dash.ctx.triggered_id

        if trigger_id == {'index': 'delete', 'type': 'notice-operation-button'}:
            notice_ids = ','.join(selected_row_keys)
        else:
            if clicked_content == '删除':
                notice_ids = recently_button_clicked_row['key']
            else:
                return dash.no_update

        return [
            f'是否确认删除序号为{notice_ids}的通知公告？',
            True,
            {'notice_ids': notice_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('notice-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('notice-delete-confirm-modal', 'okCounts'),
    State('notice-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def notice_delete_confirm(delete_confirm, notice_ids_data):
    """
    删除岗通知公告弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = notice_ids_data
        delete_button_info = delete_notice_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')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/notice_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.post import get_post_list_api, get_post_detail_api, add_post_api, edit_post_api, delete_post_api, export_post_list_api


@app.callback(
    output=dict(
        post_table_data=Output('post-list-table', 'data', allow_duplicate=True),
        post_table_pagination=Output('post-list-table', 'pagination', allow_duplicate=True),
        post_table_key=Output('post-list-table', 'key'),
        post_table_selectedrowkeys=Output('post-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('post-search', 'nClicks'),
        refresh_click=Input('post-refresh', 'nClicks'),
        pagination=Input('post-list-table', 'pagination'),
        operations=Input('post-operations-store', 'data')
    ),
    state=dict(
        post_code=State('post-post_code-input', 'value'),
        post_name=State('post-post_name-input', 'value'),
        status_select=State('post-status-select', 'value'),
        button_perms=State('post-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_post_table_data(search_click, refresh_click, pagination, operations, post_code, post_name, status_select, button_perms):
    """
    获取岗位表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """

    query_params = dict(
        post_code=post_code,
        post_name=post_name,
        status=status_select,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'post-list-table':
        query_params = dict(
            post_code=post_code,
            post_name=post_name,
            status=status_select,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        table_info = get_post_list_api(query_params)
        if table_info['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['post_id'])
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:post:edit' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:post:remove' in button_perms else {},
                ]
            return dict(
                post_table_data=table_data,
                post_table_pagination=table_pagination,
                post_table_key=str(uuid.uuid4()),
                post_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            post_table_data=dash.no_update,
            post_table_pagination=dash.no_update,
            post_table_key=dash.no_update,
            post_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置岗位搜索表单数据回调
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'),
     Output('post-operations-store', 'data')],
    Input('post-reset', '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('post-search-form-container', 'hidden'),
     Output('post-hidden-tooltip', 'title')],
    Input('post-hidden', 'nClicks'),
    State('post-search-form-container', 'hidden'),
    prevent_initial_call=True
)


@app.callback(
    Output({'type': 'post-operation-button', 'index': 'edit'}, 'disabled'),
    Input('post-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_post_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output({'type': 'post-operation-button', 'index': 'delete'}, 'disabled'),
    Input('post-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_post_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:

            return False

        return True

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('post-modal', 'visible', allow_duplicate=True),
        modal_title=Output('post-modal', 'title'),
        form_value=Output({'type': 'post-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'post-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'post-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('post-edit-id-store', 'data'),
        modal_type=Output('post-operations-store-bk', 'data')
    ),
    inputs=dict(
        operation_click=Input({'type': 'post-operation-button', 'index': ALL}, 'nClicks'),
        button_click=Input('post-list-table', 'nClicksButton')
    ),
    state=dict(
        selected_row_keys=State('post-list-table', 'selectedRowKeys'),
        clicked_content=State('post-list-table', 'clickedContent'),
        recently_button_clicked_row=State('post-list-table', 'recentlyButtonClickedRow')
    ),
    prevent_initial_call=True
)
def add_edit_post_modal(operation_click, button_click, selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示新增或编辑岗位弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'add', 'type': 'post-operation-button'} \
            or trigger_id == {'index': 'edit', 'type': 'post-operation-button'} \
            or (trigger_id == 'post-list-table' and clicked_content == '修改'):
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]
        if trigger_id == {'index': 'add', 'type': 'post-operation-button'}:
            post_info = dict(post_name=None, post_code=None, post_sort=0, status='0', remark=None)
            return dict(
                modal_visible=True,
                modal_title='新增岗位',
                form_value=[post_info.get(k) for k in form_value_list],
                form_label_validate_status=[None] * len(form_label_list),
                form_label_validate_info=[None] * len(form_label_list),
                api_check_token_trigger=dash.no_update,
                edit_row_info=None,
                modal_type={'type': 'add'}
            )
        elif trigger_id == {'index': 'edit', 'type': 'post-operation-button'} or (trigger_id == 'post-list-table' and clicked_content == '修改'):
            if trigger_id == {'index': 'edit', 'type': 'post-operation-button'}:
                post_id = int(','.join(selected_row_keys))
            else:
                post_id = int(recently_button_clicked_row['key'])
            post_info_res = get_post_detail_api(post_id=post_id)
            if post_info_res['code'] == 200:
                post_info = post_info_res['data']
                return dict(
                    modal_visible=True,
                    modal_title='编辑岗位',
                    form_value=[post_info.get(k) for k in form_value_list],
                    form_label_validate_status=[None] * len(form_label_list),
                    form_label_validate_info=[None] * len(form_label_list),
                    api_check_token_trigger={'timestamp': time.time()},
                    edit_row_info=post_info if post_info else None,
                    modal_type={'type': 'edit'}
                )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None,
            modal_type=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'post-form-label', 'index': ALL, 'required': True}, 'validateStatus',
                                          allow_duplicate=True),
        form_label_validate_info=Output({'type': 'post-form-label', 'index': ALL, 'required': True}, 'help',
                                        allow_duplicate=True),
        modal_visible=Output('post-modal', 'visible'),
        operations=Output('post-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('post-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('post-operations-store-bk', 'data'),
        edit_row_info=State('post-edit-id-store', 'data'),
        form_value=State({'type': 'post-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'post-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def post_confirm(confirm_trigger, modal_type, edit_row_info, form_value, form_label):
    """
    新增或编辑岗位弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}
        if all([form_value_state.get(k) for k in form_label_output_list]):
            params_add = form_value_state
            params_edit = params_add.copy()
            params_edit['post_id'] = edit_row_info.get('post_id') if edit_row_info else None
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_post_api(params_add)
            if modal_type == 'edit':
                api_res = edit_post_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('post-delete-text', 'children'),
     Output('post-delete-confirm-modal', 'visible'),
     Output('post-delete-ids-store', 'data')],
    [Input({'type': 'post-operation-button', 'index': ALL}, 'nClicks'),
     Input('post-list-table', 'nClicksButton')],
    [State('post-list-table', 'selectedRowKeys'),
     State('post-list-table', 'clickedContent'),
     State('post-list-table', 'recentlyButtonClickedRow')],
    prevent_initial_call=True
)
def post_delete_modal(operation_click, button_click,
                      selected_row_keys, clicked_content, recently_button_clicked_row):
    """
    显示删除岗位二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'post-operation-button'} or (trigger_id == 'post-list-table' and clicked_content == '删除'):

        if trigger_id == {'index': 'delete', 'type': 'post-operation-button'}:
            post_ids = ','.join(selected_row_keys)
        else:
            if clicked_content == '删除':
                post_ids = recently_button_clicked_row['key']
            else:
                raise PreventUpdate

        return [
            f'是否确认删除岗位编号为{post_ids}的岗位？',
            True,
            {'post_ids': post_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('post-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('post-delete-confirm-modal', 'okCounts'),
    State('post-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def post_delete_confirm(delete_confirm, post_ids_data):
    """
    删除岗位弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = post_ids_data
        delete_button_info = delete_post_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')
        ]

    raise PreventUpdate


@app.callback(
    [Output('post-export-container', 'data', allow_duplicate=True),
     Output('post-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('post-export', 'nClicks'),
    prevent_initial_call=True
)
def export_post_list(export_click):
    """
    导出岗位信息回调
    """
    if export_click:
        export_post_res = export_post_list_api({})
        if export_post_res.status_code == 200:
            export_post = export_post_res.content

            return [
                dcc.send_bytes(export_post, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('post-export-container', 'data', allow_duplicate=True),
    Input('post-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_post_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/post_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash.dependencies import Input, Output, State, ALL, MATCH
from dash.exceptions import PreventUpdate
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):
    """
    使用模式匹配回调MATCH模式，根据不同类型获取角色已分配用户列表及未分配用户列表（进行表格相关增删查改操作后均会触发此回调）
    """

    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]

    raise PreventUpdate


# 重置分配用户搜索表单数据回调
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

    raise PreventUpdate


@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]

    raise PreventUpdate


@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')
        ]

    raise PreventUpdate


@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
        ]

    raise PreventUpdate


@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')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/role_c/allocate_user_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_utils_components as fuc

from server import app
from api.role import get_role_detail_api, role_datascope_api
from api.dept import get_dept_tree_api, get_dept_list_api


@app.callback(
    Output('role-dept-perms', 'expandedKeys'),
    Input('role-dept-perms-radio-fold-unfold', 'checked'),
    State('role-dept-store', 'data'),
    prevent_initial_call=True
)
def fold_unfold_role_dept(fold_unfold, dept_info):
    """
    数据权限表单中展开/折叠checkbox回调
    """
    if dept_info:
        default_expanded_keys = []
        for item in dept_info:
            if fold_unfold:
                default_expanded_keys.append(str(item.get('dept_id')))
            else:
                if item.get('parent_id') == 0:
                    default_expanded_keys.append(str(item.get('dept_id')))

        return default_expanded_keys

    return dash.no_update


@app.callback(
    Output('role-dept-perms', 'checkedKeys', allow_duplicate=True),
    Input('role-dept-perms-radio-all-none', 'checked'),
    State('role-dept-store', 'data'),
    prevent_initial_call=True
)
def all_none_role_dept_mode(all_none, dept_info):
    """
    数据权限表单中全选/全不选checkbox回调
    """
    if dept_info:
        default_expanded_keys = []
        for item in dept_info:
            if item.get('parent_id') == 0:
                default_expanded_keys.append(str(item.get('dept_id')))

        if all_none:
            return [str(item.get('dept_id')) for item in dept_info]
        else:
            return []

    return dash.no_update


@app.callback(
    [Output('role-dept-perms', 'checkStrictly'),
     Output('role-dept-perms', 'checkedKeys', allow_duplicate=True)],
    Input('role-dept-perms-radio-parent-children', 'checked'),
    State('current-role-dept-store', 'data'),
    prevent_initial_call=True
)
def change_role_dept_mode(parent_children, current_role_dept):
    """
    数据权限表单中父子联动checkbox回调
    """
    checked_dept = []
    if parent_children:
        if current_role_dept:
            for item in current_role_dept:
                has_children = False
                for other_item in current_role_dept:
                    if other_item['parent_id'] == item['dept_id']:
                        has_children = True
                        break
                if not has_children:
                    checked_dept.append(str(item.get('dept_id')))
        return [False, checked_dept]
    else:
        if current_role_dept:
            checked_dept = [str(item.get('dept_id')) for item in current_role_dept if item] or []
        return [True, checked_dept]


@app.callback(
    output=dict(
        dept_div=Output('role-dept-perms-div', 'hidden'),
        dept_perms_tree=Output('role-dept-perms', 'treeData'),
        dept_perms_expanded_check=Output('role-dept-perms-radio-fold-unfold', 'checked'),
        dept_perms_checkedkeys=Output('role-dept-perms', 'checkedKeys', allow_duplicate=True),
        dept_perms_halfcheckedkeys=Output('role-dept-perms', 'halfCheckedKeys', allow_duplicate=True),
        role_dept=Output('role-dept-store', 'data'),
        current_role_dept=Output('current-role-dept-store', 'data')
    ),
    inputs=dict(
        data_scope=Input({'type': 'datascope-form-value', 'index': 'data_scope'}, 'value'),
    ),
    state=dict(
        role_info=State('role-edit-id-store', 'data')
    ),
    prevent_initial_call=True
)
def get_role_dept_info(data_scope, role_info):
    if data_scope == '2':
        tree_info = get_dept_tree_api({})
        dept_list_info = get_dept_list_api({})
        if tree_info.get('code') == 200 and dept_list_info.get('code') == 200:
            tree_data = tree_info['data']
            dept_list = [item for item in dept_list_info['data']['rows'] if item.get('status') == '0']
            checked_dept = []
            checked_dept_all = []
            if role_info.get('dept')[0]:
                for item in role_info.get('dept'):
                    checked_dept_all.append(str(item.get('dept_id')))
                    has_children = False
                    for other_item in role_info.get('dept'):
                        if other_item['parent_id'] == item['dept_id']:
                            has_children = True
                            break
                    if not has_children:
                        checked_dept.append(str(item.get('dept_id')))
            half_checked_dept = [x for x in checked_dept_all if x not in checked_dept]

            return dict(
                dept_div=False,
                dept_perms_tree=tree_data,
                dept_perms_expanded_check=True,
                dept_perms_checkedkeys=checked_dept,
                dept_perms_halfcheckedkeys=half_checked_dept,
                role_dept=dept_list,
                current_role_dept=role_info.get('dept')
            )

        return dict(
            dept_div=False,
            dept_perms_tree=dash.no_update,
            dept_perms_expanded_check=dash.no_update,
            dept_perms_checkedkeys=dash.no_update,
            dept_perms_halfcheckedkeys=dash.no_update,
            role_dept=dash.no_update,
            current_role_dept=dash.no_update
        )

    return dict(
        dept_div=True,
        dept_perms_tree=dash.no_update,
        dept_perms_expanded_check=dash.no_update,
        dept_perms_checkedkeys=dash.no_update,
        dept_perms_halfcheckedkeys=dash.no_update,
        role_dept=dash.no_update,
        current_role_dept=dash.no_update
    )


@app.callback(
    output=dict(
        modal_visible=Output('role-datascope-modal', 'visible', allow_duplicate=True),
        form_value=Output({'type': 'datascope-form-value', 'index': ALL}, 'value'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('role-edit-id-store', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        button_click=Input({'type': 'role-operation-table', 'operation': ALL, 'index': ALL}, 'nClicks')
    ),
    prevent_initial_call=True
)
def edit_role_datascope_modal(button_click):
    """
    显示角色数据权限弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id.operation == 'datascope':
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[1]]
        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']
            return dict(
                modal_visible=True,
                form_value=[role_info.get('role').get(k) for k in form_value_list],
                api_check_token_trigger={'timestamp': time.time()},
                edit_row_info=role_info
            )

        return dict(
            modal_visible=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('role-datascope-modal', 'visible'),
        operations=Output('role-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('role-datascope-modal', 'okCounts')
    ),
    state=dict(
        edit_row_info=State('role-edit-id-store', 'data'),
        form_value=State({'type': 'datascope-form-value', 'index': ALL}, 'value'),
        dept_checked_keys=State('role-dept-perms', 'checkedKeys'),
        dept_half_checked_keys=State('role-dept-perms', 'halfCheckedKeys'),
        parent_checked=State('role-dept-perms-radio-parent-children', 'checked')
    ),
    prevent_initial_call=True
)
def role_datascope_confirm(confirm_trigger, edit_row_info, form_value, dept_checked_keys, dept_half_checked_keys, parent_checked):
    """
    角色数据权限弹窗确认回调，实现分配数据权限的操作
    """
    if confirm_trigger:
        # 获取所有输入表单项对应的value
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[1]}
        dept_half_checked_keys = dept_half_checked_keys if dept_half_checked_keys else []
        dept_checked_keys = dept_checked_keys if dept_checked_keys else []
        if parent_checked:
            dept_perms = dept_half_checked_keys + dept_checked_keys
        else:
            dept_perms = dept_checked_keys
        params_datascope = form_value_state
        params_datascope['dept_id'] = ','.join(dept_perms) if dept_perms else None
        params_datascope['role_id'] = edit_row_info.get('role').get('role_id') if edit_row_info else None
        api_res = role_datascope_api(params_datascope)
        if api_res.get('code') == 200:
            return dict(
                modal_visible=False,
                operations={'type': 'datascope'},
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('分配成功', type='success')
            )

        return dict(
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('分配失败', type='error')
        )

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/role_c/data_scope_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
import feffery_antd_components as fac
import feffery_utils_components as fuc

from server import app
from api.role import get_role_list_api, get_role_detail_api, add_role_api, edit_role_api, delete_role_api, export_role_list_api
from api.menu import get_menu_tree_api


@app.callback(
    output=dict(
        role_table_data=Output('role-list-table', 'data', allow_duplicate=True),
        role_table_pagination=Output('role-list-table', 'pagination', allow_duplicate=True),
        role_table_key=Output('role-list-table', 'key'),
        role_table_selectedrowkeys=Output('role-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        search_click=Input('role-search', 'nClicks'),
        refresh_click=Input('role-refresh', 'nClicks'),
        pagination=Input('role-list-table', 'pagination'),
        operations=Input('role-operations-store', 'data')
    ),
    state=dict(
        role_name=State('role-role_name-input', 'value'),
        role_key=State('role-role_key-input', 'value'),
        status_select=State('role-status-select', 'value'),
        create_time_range=State('role-create_time-range', 'value'),
        button_perms=State('role-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_role_table_data(search_click, refresh_click, pagination, operations, role_name, role_key, status_select, create_time_range, button_perms):
    """
    获取角色表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """

    create_time_start = None
    create_time_end = None
    if create_time_range:
        create_time_start = create_time_range[0]
        create_time_end = create_time_range[1]
    query_params = dict(
        role_name=role_name,
        role_key=role_key,
        status=status_select,
        create_time_start=create_time_start,
        create_time_end=create_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'role-list-table':
        query_params = dict(
            role_name=role_name,
            role_key=role_key,
            status=status_select,
            create_time_start=create_time_start,
            create_time_end=create_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if search_click or refresh_click or pagination or operations:
        table_info = get_role_list_api(query_params)
        if table_info['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(checked=True, disabled=item['role_id'] == 1)
                else:
                    item['status'] = dict(checked=False, disabled=item['role_id'] == 1)
                item['key'] = str(item['role_id'])
                if item['role_id'] == 1:
                    item['operation'] = []
                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'
                            ) if 'system:role:edit' in button_perms else []
                        ]
                    )

            return dict(
                role_table_data=table_data,
                role_table_pagination=table_pagination,
                role_table_key=str(uuid.uuid4()),
                role_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            role_table_data=dash.no_update,
            role_table_pagination=dash.no_update,
            role_table_key=dash.no_update,
            role_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置角色搜索表单数据回调
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'),
     Output('role-create_time-range', 'value'),
     Output('role-operations-store', 'data')],
    Input('role-reset', '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('role-search-form-container', 'hidden'),
     Output('role-hidden-tooltip', 'title')],
    Input('role-hidden', 'nClicks'),
    State('role-search-form-container', 'hidden'),
    prevent_initial_call=True
)


@app.callback(
    Output({'type': 'role-operation-button', 'operation': 'edit'}, 'disabled'),
    Input('role-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_role_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1 or '1' in table_rows_selected:
                return True

            return False

        return True

    return dash.no_update


@app.callback(
    Output({'type': 'role-operation-button', 'operation': 'delete'}, 'disabled'),
    Input('role-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_role_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if '1' in table_rows_selected:
                return True

            return False

        return True

    return dash.no_update


@app.callback(
    Output('role-menu-perms', 'expandedKeys', allow_duplicate=True),
    Input('role-menu-perms-radio-fold-unfold', 'checked'),
    State('role-menu-store', 'data'),
    prevent_initial_call=True
)
def fold_unfold_role_menu(fold_unfold, menu_info):
    """
    新增和编辑表单中展开/折叠checkbox回调
    """
    if menu_info:
        default_expanded_keys = []
        for item in menu_info:
            if item.get('parent_id') == 0:
                default_expanded_keys.append(str(item.get('menu_id')))
                
        if fold_unfold:
            return default_expanded_keys
        else:
            return []
    
    return dash.no_update


@app.callback(
    Output('role-menu-perms', 'checkedKeys', allow_duplicate=True),
    Input('role-menu-perms-radio-all-none', 'checked'),
    State('role-menu-store', 'data'),
    prevent_initial_call=True
)
def all_none_role_menu_mode(all_none, menu_info):
    """
    新增和编辑表单中全选/全不选checkbox回调
    """
    if menu_info:
        default_expanded_keys = []
        for item in menu_info:
            if item.get('parent_id') == 0:
                default_expanded_keys.append(str(item.get('menu_id')))
                
        if all_none:
            return [str(item.get('menu_id')) for item in menu_info]
        else:
            return []
    
    return dash.no_update


@app.callback(
    [Output('role-menu-perms', 'checkStrictly'),
     Output('role-menu-perms', 'checkedKeys', allow_duplicate=True)],
    Input('role-menu-perms-radio-parent-children', 'checked'),
    State('current-role-menu-store', 'data'),
    prevent_initial_call=True
)
def change_role_menu_mode(parent_children, current_role_menu):
    """
    新增和编辑表单中父子联动checkbox回调
    """
    checked_menu = []
    if parent_children:
        if current_role_menu:
            for item in current_role_menu:
                has_children = False
                for other_item in current_role_menu:
                    if other_item['parent_id'] == item['menu_id']:
                        has_children = True
                        break
                if not has_children:
                    checked_menu.append(str(item.get('menu_id')))
        return [False, checked_menu]
    else:
        if current_role_menu:
            checked_menu = [str(item.get('menu_id')) for item in current_role_menu if item] or []
        return [True, checked_menu]


@app.callback(
    output=dict(
        modal_visible=Output('role-modal', 'visible', allow_duplicate=True),
        modal_title=Output('role-modal', 'title'),
        form_value=Output({'type': 'role-form-value', 'index': ALL, 'required': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'role-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'role-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        menu_perms_tree=Output('role-menu-perms', 'treeData'),
        menu_perms_expandedkeys=Output('role-menu-perms', 'expandedKeys', allow_duplicate=True),
        menu_perms_checkedkeys=Output('role-menu-perms', 'checkedKeys', allow_duplicate=True),
        menu_perms_halfcheckedkeys=Output('role-menu-perms', 'halfCheckedKeys', allow_duplicate=True),
        role_menu=Output('role-menu-store', 'data'),
        current_role_menu=Output('current-role-menu-store', 'data'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        edit_row_info=Output('role-edit-id-store', 'data'),
        modal_type=Output('role-operations-store-bk', 'data')
    ),
    inputs=dict(
        operation_click=Input({'type': 'role-operation-button', 'operation': ALL}, 'nClicks'),
        button_click=Input({'type': 'role-operation-table', 'operation': ALL, 'index': ALL}, 'nClicks')
    ),
    state=dict(
        selected_row_keys=State('role-list-table', 'selectedRowKeys')
    ),
    prevent_initial_call=True
)
def add_edit_role_modal(operation_click, button_click, selected_row_keys):
    """
    显示新增或编辑角色弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id.operation in ['add', 'edit']:
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]
        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.type == 'role-operation-button' and trigger_id.operation == 'add':
                role_info = dict(role_name=None, role_key=None, role_sort=None, status='0', remark=None)
                return dict(
                    modal_visible=True,
                    modal_title='新增角色',
                    form_value=[role_info.get(k) for k in form_value_list],
                    form_label_validate_status=[None] * len(form_label_list),
                    form_label_validate_info=[None] * len(form_label_list),
                    menu_perms_tree=tree_data[0],
                    menu_perms_expandedkeys=[],
                    menu_perms_checkedkeys=None,
                    menu_perms_halfcheckedkeys=None,
                    role_menu=tree_data[1],
                    current_role_menu=None,
                    api_check_token_trigger={'timestamp': time.time()},
                    edit_row_info=None,
                    modal_type={'type': 'add'}
                )
            elif trigger_id.operation == 'edit':
                if trigger_id.type == 'role-operation-button':
                    role_id = int(','.join(selected_row_keys))
                else:
                    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']
                    checked_menu = []
                    checked_menu_all = []
                    if role_info.get('menu')[0]:
                        for item in role_info.get('menu'):
                            checked_menu_all.append(str(item.get('menu_id')))
                            has_children = False
                            for other_item in role_info.get('menu'):
                                if other_item['parent_id'] == item['menu_id']:
                                    has_children = True
                                    break
                            if not has_children:
                                checked_menu.append(str(item.get('menu_id')))
                    half_checked_menu = [x for x in checked_menu_all if x not in checked_menu]
                    return dict(
                        modal_visible=True,
                        modal_title='编辑角色',
                        form_value=[role_info.get('role').get(k) for k in form_value_list],
                        form_label_validate_status=[None] * len(form_label_list),
                        form_label_validate_info=[None] * len(form_label_list),
                        menu_perms_tree=tree_data[0],
                        menu_perms_expandedkeys=[],
                        menu_perms_checkedkeys=checked_menu,
                        menu_perms_halfcheckedkeys=half_checked_menu,
                        role_menu=tree_data[1],
                        current_role_menu=role_info.get('menu'),
                        api_check_token_trigger={'timestamp': time.time()},
                        edit_row_info=role_info.get('role') if role_info else None,
                        modal_type={'type': 'edit'}
                    )

        return dict(
            modal_visible=dash.no_update,
            modal_title=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_value_list),
            form_label_validate_info=[dash.no_update] * len(form_value_list),
            menu_perms_tree=dash.no_update,
            menu_perms_expandedkeys=dash.no_update,
            menu_perms_checkedkeys=dash.no_update,
            menu_perms_halfcheckedkeys=dash.no_update,
            role_menu=dash.no_update,
            current_role_menu=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            edit_row_info=None,
            modal_type=None
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'role-form-label', 'index': ALL, 'required': True}, 'validateStatus',
                                          allow_duplicate=True),
        form_label_validate_info=Output({'type': 'role-form-label', 'index': ALL, 'required': True}, 'help',
                                        allow_duplicate=True),
        modal_visible=Output('role-modal', 'visible'),
        operations=Output('role-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        confirm_trigger=Input('role-modal', 'okCounts')
    ),
    state=dict(
        modal_type=State('role-operations-store-bk', 'data'),
        edit_row_info=State('role-edit-id-store', 'data'),
        form_value=State({'type': 'role-form-value', 'index': ALL, 'required': ALL}, 'value'),
        form_label=State({'type': 'role-form-value', 'index': ALL, 'required': True}, 'placeholder'),
        menu_checked_keys=State('role-menu-perms', 'checkedKeys'),
        menu_half_checked_keys=State('role-menu-perms', 'halfCheckedKeys'),
        parent_checked=State('role-menu-perms-radio-parent-children', 'checked')
    ),
    prevent_initial_call=True
)
def role_confirm(confirm_trigger, modal_type, edit_row_info, form_value, form_label, menu_checked_keys, menu_half_checked_keys, parent_checked):
    """
    新增或编辑角色弹窗确认回调，实现新增或编辑操作
    """
    if confirm_trigger:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[3]}
        if all([form_value_state.get(k) for k in form_label_output_list]):
            menu_half_checked_keys = menu_half_checked_keys if menu_half_checked_keys else []
            menu_checked_keys = menu_checked_keys if menu_checked_keys else []
            if parent_checked:
                menu_perms = menu_half_checked_keys + menu_checked_keys
            else:
                menu_perms = menu_checked_keys
            params_add = form_value_state
            params_add['menu_id'] = ','.join(menu_perms) if menu_perms else None
            params_edit = params_add.copy()
            params_edit['role_id'] = edit_row_info.get('role_id') if edit_row_info else None
            api_res = {}
            modal_type = modal_type.get('type')
            if modal_type == 'add':
                api_res = add_role_api(params_add)
            if modal_type == 'edit':
                api_res = edit_role_api(params_edit)
            if api_res.get('code') == 200:
                if modal_type == 'add':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'add'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                    )
                if modal_type == 'edit':
                    return dict(
                        form_label_validate_status=[None] * len(form_label_output_list),
                        form_label_validate_info=[None] * len(form_label_output_list),
                        modal_visible=False,
                        operations={'type': 'edit'},
                        api_check_token_trigger={'timestamp': time.time()},
                        global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                    )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else form_label_state.get(k) for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('处理失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [Output('role-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    [Input('role-list-table', 'recentlySwitchDataIndex'),
     Input('role-list-table', 'recentlySwitchStatus'),
     Input('role-list-table', 'recentlySwitchRow')],
    prevent_initial_call=True
)
def table_switch_role_status(recently_switch_data_index, recently_switch_status, recently_switch_row):
    """
    表格内切换角色状态回调
    """
    if recently_switch_data_index:
        if recently_switch_status:
            params = dict(role_id=int(recently_switch_row['key']), status='0', type='status')
        else:
            params = dict(role_id=int(recently_switch_row['key']), status='1', type='status')
        edit_button_result = edit_role_api(params)
        if edit_button_result['code'] == 200:

            return [
                {'type': 'switch-status'},
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('修改成功', type='success')
            ]

        return [
            {'type': 'switch-status'},
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('修改失败', type='error')
        ]

    raise PreventUpdate


@app.callback(
    [Output('role-delete-text', 'children'),
     Output('role-delete-confirm-modal', 'visible'),
     Output('role-delete-ids-store', 'data')],
    [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):
    """
    显示删除角色二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id.operation == 'delete':

        if trigger_id.type == 'role-operation-button':
            role_ids = ','.join(selected_row_keys)
        else:
            if trigger_id.type == 'role-operation-table':
                role_ids = trigger_id.index
            else:
                raise PreventUpdate

        return [
            f'是否确认删除角色编号为{role_ids}的角色？',
            True,
            {'role_ids': role_ids}
        ]

    raise PreventUpdate


@app.callback(
    [Output('role-operations-store', 'data', allow_duplicate=True),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('role-delete-confirm-modal', 'okCounts'),
    State('role-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def role_delete_confirm(delete_confirm, role_ids_data):
    """
    删除角色弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = role_ids_data
        delete_button_info = delete_role_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')
        ]

    raise PreventUpdate


@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
        ]

    raise PreventUpdate


@app.callback(
    [Output('role-export-container', 'data', allow_duplicate=True),
     Output('role-export-complete-judge-container', 'data'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('role-export', 'nClicks'),
    prevent_initial_call=True
)
def export_role_list(export_click):
    """
    导出角色信息回调
    """
    if export_click:
        export_role_res = export_role_list_api({})
        if export_role_res.status_code == 200:
            export_role = export_role_res.content

            return [
                dcc.send_bytes(export_role, 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')
        ]

    raise PreventUpdate


@app.callback(
    Output('role-export-container', 'data', allow_duplicate=True),
    Input('role-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_role_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/role_c/role_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash.dependencies import Input, Output, State, ALL, MATCH
from dash.exceptions import PreventUpdate
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):
    """
    使用模式匹配回调MATCH模式，根据不同类型获取用户已分配角色列表及未分配角色列表（进行表格相关增删查改操作后均会触发此回调）
    """

    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]

    raise PreventUpdate


# 重置分配角色搜索表单数据回调
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

    raise PreventUpdate


@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]

    raise PreventUpdate


@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')
        ]

    raise PreventUpdate


@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
        ]

    raise PreventUpdate


@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')
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/user_c/allocate_role_c.py
insistence/Dash-FastAPI-Admin
import dash
import feffery_utils_components as fuc
import time
import uuid
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from server import app

from api.user import change_user_avatar_api


@app.callback(
    [Output('avatar-cropper-modal', 'visible', allow_duplicate=True),
     Output('avatar-src-data', 'data', allow_duplicate=True)],
    Input('avatar-edit-click', 'n_clicks'),
    State('user-avatar-image-info', 'src'),
    prevent_initial_call=True
)
def avatar_cropper_modal_visible(n_clicks, user_avatar_image_info):
    """
    显示编辑头像弹窗回调
    """
    if n_clicks:
        return [True, user_avatar_image_info]

    raise PreventUpdate


@app.callback(
    Output('avatar-src-data', 'data', allow_duplicate=True),
    Input('avatar-upload-choose', 'listUploadTaskRecord'),
    prevent_initial_call=True
)
def upload_user_avatar(list_upload_task_record):
    """
    上传用户头像获取后端url回调
    """
    if list_upload_task_record:

        return list_upload_task_record[-1].get('url')

    raise PreventUpdate


@app.callback(
    Output('avatar-cropper', 'jsString'),
    Input('avatar-src-data', 'data'),
    prevent_initial_call=True
)
def edit_user_avatar(src_data):
    """
    使用cropper.js编辑头像回调
    """

    return """
            // 创建新图像元素
            var newImage = document.createElement('img');
            newImage.id = 'user-avatar-image';
            newImage.src = '% s';
            newImage.onload = function() {
                // 删除旧图像元素
                var oldImage = document.getElementById('user-avatar-image');
                oldImage.parentNode.removeChild(oldImage);
                // 销毁旧的 Cropper.js 实例
                var oldCropper = oldImage.cropper;
                if (oldCropper) {
                    oldCropper.destroy();
                }
                // 将新图像添加到页面中
                var container = document.getElementById('avatar-cropper-container');
                container.appendChild(newImage);
                // var image = document.getElementById('user-avatar-image');
                var previewImage = document.getElementById('user-avatar-image-preview');
                // 创建新的 Cropper 实例
                var cropper = new Cropper(newImage, {
                  viewMode: 1,
                  dragMode: 'none',
                  initialAspectRatio: 1,
                  aspectRatio: 1,
                  preview: previewImage,
                  background: true,
                  autoCropArea: 0.6,
                  zoomOnWheel: true,
                  crop: function(event) {
                    // 当裁剪框的位置或尺寸发生改变时触发的回调函数
                    console.log(event.detail.x);
                    console.log(event.detail.y);
                    console.log(event.detail.width);
                    console.log(event.detail.height);
                    console.log(event.detail.rotate);
                    console.log(event.detail.scaleX);
                    console.log(event.detail.scaleY);
                    // 当需要获取裁剪后的数据时
                    var croppedDataUrl = cropper.getCroppedCanvas().toDataURL("image/jpeg", 1);
                    sessionStorage.setItem('cropper-avatar-base64', JSON.stringify({avatarBase64: croppedDataUrl}))
                  }
                });
                // 获取旋转按钮的引用
                var rotateLeftButton = document.getElementById('rotate-left');
                var rotateRightButton = document.getElementById('rotate-right');

                // 添加点击事件监听器
                rotateLeftButton.addEventListener('click', function() {
                  // 向左旋转图像90度
                  cropper.rotate(-90);
                });
                rotateRightButton.addEventListener('click', function() {
                  // 向右旋转图像90度
                  cropper.rotate(90);
                });
                // 获取缩小按钮和放大按钮的引用
                var zoomOutButton = document.getElementById('zoom-out');
                var zoomInButton = document.getElementById('zoom-in');

                // 添加点击事件监听器
                zoomOutButton.addEventListener('click', function() {
                  // 放大图像
                  cropper.zoom(0.1);
                });

                zoomInButton.addEventListener('click', function() {
                  // 缩小图像
                  cropper.zoom(-0.1);
                });
            }
        """ % src_data


@app.callback(
    [Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True),
     Output('avatar-cropper-modal', 'visible', allow_duplicate=True),
     Output('user-avatar-image-info', 'key'),
     Output('avatar-info', 'key')],
    Input('change-avatar-submit', 'nClicks'),
    State('cropper-avatar-base64', 'data'),
    prevent_initial_call=True
)
def change_user_avatar_callback(submit_click, avatar_data):
    """
    提交编辑完成头像数据回调，实现更新头像操作
    """

    if submit_click:
        params = dict(type='avatar', avatar=avatar_data['avatarBase64'])
        change_avatar_result = change_user_avatar_api(params)
        if change_avatar_result.get('code') == 200:

            return [
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('修改成功', type='success'),
                False,
                str(uuid.uuid4()),
                str(uuid.uuid4())
            ]

        return [
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('修改失败', type='error'),
            dash.no_update,
            dash.no_update,
            dash.no_update
        ]

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/user_c/profile_c/avatar_c.py
insistence/Dash-FastAPI-Admin
import dash
import feffery_utils_components as fuc
import time
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from server import app

from api.user import reset_user_password_api


@app.callback(
    [Output('reset-old-password-form-item', 'validateStatus'),
     Output('reset-new-password-form-item', 'validateStatus'),
     Output('reset-confirm-password-form-item', 'validateStatus'),
     Output('reset-old-password-form-item', 'help'),
     Output('reset-new-password-form-item', 'help'),
     Output('reset-confirm-password-form-item', 'help'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('reset-password-submit', 'nClicks'),
    [State('reset-old-password', 'value'),
     State('reset-new-password', 'value'),
     State('reset-confirm-password', 'value')],
    prevent_initial_call=True
)
def reset_submit_user_info(reset_click, old_password, new_password, confirm_password):
    """
    重置当前用户密码回调
    """
    if reset_click:
        if all([old_password, new_password, confirm_password]):

            if new_password == confirm_password:

                params = dict(type='avatar', old_password=old_password, password=new_password)
                reset_password_result = reset_user_password_api(params)
                if reset_password_result.get('code') == 200:

                    return [
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        {'timestamp': time.time()},
                        fuc.FefferyFancyMessage('修改成功', type='success'),
                    ]

                return [
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    {'timestamp': time.time()},
                    fuc.FefferyFancyMessage('修改失败', type='error'),
                ]

            return [
                None,
                None if new_password else 'error',
                None if confirm_password else 'error',
                None,
                None if new_password else '前后两次密码不一致！',
                None if confirm_password else '前后两次密码不一致！',
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('修改失败', type='error'),
            ]

        return [
            None if old_password else 'error',
            None if new_password else 'error',
            None if confirm_password else 'error',
            None if old_password else '请输入旧密码！',
            None if new_password else '请输入新密码！',
            None if confirm_password else '请输入确认密码！',
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('修改失败', type='error'),
        ]

    raise PreventUpdate


@app.callback(
    Output('tabs-container', 'latestDeletePane', allow_duplicate=True),
    Input('reset-password-close', 'nClicks'),
    prevent_initial_call=True
)
def close_personal_info_modal(close_click):
    """
    关闭当前个人资料标签页回调
    """
    if close_click:

        return '个人资料'
    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/user_c/profile_c/reset_pwd_c.py
insistence/Dash-FastAPI-Admin
import dash
import feffery_utils_components as fuc
import time
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from server import app

from api.user import change_user_info_api


@app.callback(
    [Output('reset-user-nick_name-form-item', 'validateStatus'),
     Output('reset-user-phonenumber-form-item', 'validateStatus'),
     Output('reset-user-email-form-item', 'validateStatus'),
     Output('reset-user-nick_name-form-item', 'help'),
     Output('reset-user-phonenumber-form-item', 'help'),
     Output('reset-user-email-form-item', 'help'),
     Output('api-check-token', 'data', allow_duplicate=True),
     Output('global-message-container', 'children', allow_duplicate=True)],
    Input('reset-submit', 'nClicks'),
    [State('reset-user-nick_name', 'value'),
     State('reset-user-phonenumber', 'value'),
     State('reset-user-email', 'value'),
     State('reset-user-sex', 'value')],
    prevent_initial_call=True
)
def reset_submit_user_info(reset_click, nick_name, phonenumber, email, sex):
    """
    修改当前用户信息回调
    """
    if reset_click:
        if all([nick_name, phonenumber, email]):

            params = dict(type='avatar', nick_name=nick_name, phonenumber=phonenumber, email=email, sex=sex)
            change_user_info_result = change_user_info_api(params)
            if change_user_info_result.get('code') == 200:

                return [
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    {'timestamp': time.time()},
                    fuc.FefferyFancyMessage('修改成功', type='success'),
                ]

            return [
                None,
                None,
                None,
                None,
                None,
                None,
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('修改失败', type='error'),
            ]

        return [
            None if nick_name else 'error',
            None if phonenumber else 'error',
            None if email else 'error',
            None if nick_name else '请输入用户昵称！',
            None if phonenumber else '请输入手机号码！',
            None if email else '请输入邮箱！',
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('修改失败', type='error'),
        ]

    raise PreventUpdate


@app.callback(
    Output('tabs-container', 'latestDeletePane', allow_duplicate=True),
    Input('reset-close', 'nClicks'),
    prevent_initial_call=True
)
def close_personal_info_modal(close_click):
    """
    关闭当前个人资料标签页回调
    """
    if close_click:

        return '个人资料'
    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/user_c/profile_c/user_info_c.py
insistence/Dash-FastAPI-Admin
import dash
import time
import uuid
from dash import dcc
from dash.dependencies import Input, Output, State, ALL
from dash.exceptions import PreventUpdate
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, 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


@app.callback(
    [Output('dept-tree', 'treeData'),
     Output('api-check-token', 'data', allow_duplicate=True)],
    Input('dept-input-search', 'value'),
    prevent_initial_call=True
)
def get_search_dept_tree(dept_input):
    dept_params = dict(dept_name=dept_input)
    tree_info = get_dept_tree_api(dept_params)
    if tree_info['code'] == 200:
        tree_data = tree_info['data']

        return [tree_data, {'timestamp': time.time()}]

    return [dash.no_update, {'timestamp': time.time()}]


@app.callback(
    output=dict(
        user_table_data=Output('user-list-table', 'data', allow_duplicate=True),
        user_table_pagination=Output('user-list-table', 'pagination', allow_duplicate=True),
        user_table_key=Output('user-list-table', 'key'),
        user_table_selectedrowkeys=Output('user-list-table', 'selectedRowKeys'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        selected_dept_tree=Input('dept-tree', 'selectedKeys'),
        search_click=Input('user-search', 'nClicks'),
        refresh_click=Input('user-refresh', 'nClicks'),
        pagination=Input('user-list-table', 'pagination'),
        operations=Input('user-operations-store', 'data')
    ),
    state=dict(
        user_name=State('user-user_name-input', 'value'),
        phone_number=State('user-phone_number-input', 'value'),
        status_select=State('user-status-select', 'value'),
        create_time_range=State('user-create_time-range', 'value'),
        button_perms=State('user-button-perms-container', 'data')
    ),
    prevent_initial_call=True
)
def get_user_table_data_by_dept_tree(selected_dept_tree, search_click, refresh_click, pagination, operations,
                                     user_name, phone_number, status_select, create_time_range, button_perms):
    """
    获取用户表格数据回调（进行表格相关增删查改操作后均会触发此回调）
    """
    dept_id = None
    create_time_start = None
    create_time_end = None
    if create_time_range:
        create_time_start = create_time_range[0]
        create_time_end = create_time_range[1]
    if selected_dept_tree:
        dept_id = int(selected_dept_tree[0])
    query_params = dict(
        dept_id=dept_id,
        user_name=user_name,
        phonenumber=phone_number,
        status=status_select,
        create_time_start=create_time_start,
        create_time_end=create_time_end,
        page_num=1,
        page_size=10
    )
    triggered_id = dash.ctx.triggered_id
    if triggered_id == 'user-list-table':
        query_params = dict(
            dept_id=dept_id,
            user_name=user_name,
            phonenumber=phone_number,
            status=status_select,
            create_time_start=create_time_start,
            create_time_end=create_time_end,
            page_num=pagination['current'],
            page_size=pagination['pageSize']
        )
    if selected_dept_tree or search_click or refresh_click or pagination or operations:
        table_info = get_user_list_api(query_params)
        if table_info['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(checked=True, disabled=item['user_id'] == 1)
                else:
                    item['status'] = dict(checked=False, disabled=item['user_id'] == 1)
                item['key'] = str(item['user_id'])
                if item['user_id'] == 1:
                    item['operation'] = []
                else:
                    item['operation'] = [
                        {
                            'title': '修改',
                            'icon': 'antd-edit'
                        } if 'system:user:edit' in button_perms else None,
                        {
                            'title': '删除',
                            'icon': 'antd-delete'
                        } if 'system:user:remove' in button_perms else None,
                        {
                            'title': '重置密码',
                            'icon': 'antd-key'
                        } if 'system:user:resetPwd' in button_perms else None,
                        {
                            'title': '分配角色',
                            'icon': 'antd-check-circle'
                        } if 'system:user:edit' in button_perms else None
                    ]

            return dict(
                user_table_data=table_data,
                user_table_pagination=table_pagination,
                user_table_key=str(uuid.uuid4()),
                user_table_selectedrowkeys=None,
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            user_table_data=dash.no_update,
            user_table_pagination=dash.no_update,
            user_table_key=dash.no_update,
            user_table_selectedrowkeys=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


# 重置用户搜索表单数据回调
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'),
     Output('user-status-select', 'value'),
     Output('user-create_time-range', 'value'),
     Output('user-operations-store', 'data')],
    Input('user-reset', '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('user-search-form-container', 'hidden'),
     Output('user-hidden-tooltip', 'title')],
    Input('user-hidden', 'nClicks'),
    State('user-search-form-container', 'hidden'),
    prevent_initial_call=True
)


@app.callback(
    Output({'type': 'user-operation-button', 'index': 'edit'}, 'disabled'),
    Input('user-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_user_edit_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制编辑按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if len(table_rows_selected) > 1 or '1' in table_rows_selected:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    Output({'type': 'user-operation-button', 'index': 'delete'}, 'disabled'),
    Input('user-list-table', 'selectedRowKeys'),
    prevent_initial_call=True
)
def change_user_delete_button_status(table_rows_selected):
    """
    根据选择的表格数据行数控制删除按钮状态回调
    """
    outputs_list = dash.ctx.outputs_list
    if outputs_list:
        if table_rows_selected:
            if '1' in table_rows_selected:
                return True

            return False

        return True

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('user-add-modal', 'visible', allow_duplicate=True),
        dept_tree=Output({'type': 'user_add-form-value', 'index': 'dept_id'}, 'treeData'),
        form_value=Output({'type': 'user_add-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'user_add-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'user_add-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        user_post=Output('user-add-post', 'value'),
        user_role=Output('user-add-role', 'value'),
        post_option=Output('user-add-post', 'options'),
        role_option=Output('user-add-role', 'options'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        add_click=Input('user-add', 'nClicks')
    ),
    prevent_initial_call=True
)
def add_user_modal(add_click):
    """
    显示新增用户弹窗回调
    """
    if add_click:
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]
        dept_params = dict(dept_name='')
        tree_info = get_dept_tree_api(dept_params)
        post_option_info = get_post_select_option_api()
        role_option_info = get_role_select_option_api()
        if tree_info['code'] == 200 and post_option_info['code'] == 200 and role_option_info['code'] == 200:
            tree_data = tree_info['data']
            post_option = post_option_info['data']
            role_option = role_option_info['data']
            user_info = dict(nick_name=None, dept_id=None, phonenumber=None, email=None, user_name=None, password=None, sex=None, status='0', remark=None)

            return dict(
                modal_visible=True,
                dept_tree=tree_data,
                form_value=[user_info.get(k) for k in form_value_list],
                form_label_validate_status=[None] * len(form_label_list),
                form_label_validate_info=[None] * len(form_label_list),
                user_post=None,
                user_role=None,
                post_option=[dict(label=item['post_name'], value=item['post_id']) for item in post_option],
                role_option=[dict(label=item['role_name'], value=item['role_id']) for item in role_option],
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            modal_visible=dash.no_update,
            dept_tree=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            user_post=dash.no_update,
            user_role=dash.no_update,
            post_option=dash.no_update,
            role_option=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'user_add-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'user_add-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        modal_visible=Output('user-add-modal', 'visible', allow_duplicate=True),
        operations=Output('user-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        add_confirm=Input('user-add-modal', 'okCounts')
    ),
    state=dict(
        post=State('user-add-post', 'value'),
        role=State('user-add-role', 'value'),
        form_value=State({'type': 'user_add-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'user_add-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def usr_add_confirm(add_confirm, post, role, form_value, form_label):
    if add_confirm:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}

        if all([form_value_state.get(k) for k in form_label_output_list]):
            params = form_value_state
            params['post_id'] = ','.join(map(str, post)) if post else ''
            params['role_id'] = ','.join(map(str, role)) if role else ''
            add_button_result = add_user_api(params)

            if add_button_result['code'] == 200:
                return dict(
                    form_label_validate_status=[None] * len(form_label_output_list),
                    form_label_validate_info=[None] * len(form_label_output_list),
                    modal_visible=False,
                    operations={'type': 'add'},
                    api_check_token_trigger={'timestamp': time.time()},
                    global_message_container=fuc.FefferyFancyMessage('新增成功', type='success')
                )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('新增失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('新增失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        modal_visible=Output('user-edit-modal', 'visible', allow_duplicate=True),
        dept_tree=Output({'type': 'user_edit-form-value', 'index': 'dept_id'}, 'treeData'),
        form_value=Output({'type': 'user_edit-form-value', 'index': ALL}, 'value'),
        form_label_validate_status=Output({'type': 'user_edit-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'user_edit-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        user_post=Output('user-edit-post', 'value'),
        user_role=Output('user-edit-role', 'value'),
        post_option=Output('user-edit-post', 'options'),
        role_option=Output('user-edit-role', 'options'),
        edit_row_info=Output('user-edit-id-store', 'data'),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True)
    ),
    inputs=dict(
        operation_click=Input({'type': 'user-operation-button', 'index': ALL}, 'nClicks'),
        dropdown_click=Input('user-list-table', 'nClicksDropdownItem')
    ),
    state=dict(
        selected_row_keys=State('user-list-table', 'selectedRowKeys'),
        recently_clicked_dropdown_item_title=State('user-list-table', 'recentlyClickedDropdownItemTitle'),
        recently_dropdown_item_clicked_row=State('user-list-table', 'recentlyDropdownItemClickedRow')
    ),
    prevent_initial_call=True
)
def user_edit_modal(operation_click, dropdown_click,
                    selected_row_keys, recently_clicked_dropdown_item_title, recently_dropdown_item_clicked_row):
    """
    显示编辑用户弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'edit', 'type': 'user-operation-button'} or (trigger_id == 'user-list-table' and recently_clicked_dropdown_item_title == '修改'):
        # 获取所有输出表单项对应value的index
        form_value_list = [x['id']['index'] for x in dash.ctx.outputs_list[2]]
        # 获取所有输出表单项对应label的index
        form_label_list = [x['id']['index'] for x in dash.ctx.outputs_list[3]]

        dept_params = dict(dept_name='')
        tree_data = get_dept_tree_api(dept_params)['data']
        post_option = get_post_select_option_api()['data']
        role_option = get_role_select_option_api()['data']

        if trigger_id == {'index': 'edit', 'type': 'user-operation-button'}:
            user_id = int(selected_row_keys[0])
        else:
            if recently_clicked_dropdown_item_title == '修改':
                user_id = int(recently_dropdown_item_clicked_row['key'])
            else:
                raise PreventUpdate

        edit_button_info = get_user_detail_api(user_id)
        if edit_button_info['code'] == 200:
            edit_button_result = edit_button_info['data']
            user = edit_button_result['user']
            role = edit_button_result['role']
            post = edit_button_result['post']

            return dict(
                modal_visible=True,
                dept_tree=tree_data,
                form_value=[user.get(k) for k in form_value_list],
                form_label_validate_status=[None] * len(form_label_list),
                form_label_validate_info=[None] * len(form_label_list),
                user_post=[item['post_id'] for item in post if item] or [],
                user_role=[item['role_id'] for item in role if item] or [],
                post_option=[dict(label=item['post_name'], value=item['post_id']) for item in post_option if item] or [],
                role_option=[dict(label=item['role_name'], value=item['role_id']) for item in role_option if item] or [],
                edit_row_info={'user_id': user_id},
                api_check_token_trigger={'timestamp': time.time()}
            )

        return dict(
            modal_visible=dash.no_update,
            dept_tree=dash.no_update,
            form_value=[dash.no_update] * len(form_value_list),
            form_label_validate_status=[dash.no_update] * len(form_label_list),
            form_label_validate_info=[dash.no_update] * len(form_label_list),
            user_post=dash.no_update,
            user_role=dash.no_update,
            post_option=dash.no_update,
            role_option=dash.no_update,
            edit_row_info=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()}
        )

    raise PreventUpdate


@app.callback(
    output=dict(
        form_label_validate_status=Output({'type': 'user_edit-form-label', 'index': ALL, 'required': True}, 'validateStatus', allow_duplicate=True),
        form_label_validate_info=Output({'type': 'user_edit-form-label', 'index': ALL, 'required': True}, 'help', allow_duplicate=True),
        modal_visible=Output('user-edit-modal', 'visible', allow_duplicate=True),
        operations=Output('user-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        edit_confirm=Input('user-edit-modal', 'okCounts')
    ),
    state=dict(
        post=State('user-edit-post', 'value'),
        role=State('user-edit-role', 'value'),
        edit_row_info=State('user-edit-id-store', 'data'),
        form_value=State({'type': 'user_edit-form-value', 'index': ALL}, 'value'),
        form_label=State({'type': 'user_edit-form-label', 'index': ALL, 'required': True}, 'label')
    ),
    prevent_initial_call=True
)
def usr_edit_confirm(edit_confirm, edit_row_info, post, role, form_value, form_label):
    if edit_confirm:
        # 获取所有输出表单项对应label的index
        form_label_output_list = [x['id']['index'] for x in dash.ctx.outputs_list[0]]
        # 获取所有输入表单项对应的value及label
        form_value_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-2]}
        form_label_state = {x['id']['index']: x.get('value') for x in dash.ctx.states_list[-1]}

        if all([form_value_state.get(k) for k in form_label_output_list]):
            params = form_value_state
            params['user_id'] = edit_row_info.get('user_id') if edit_row_info else None
            params['post_id'] = ','.join(map(str, post)) if post else ''
            params['role_id'] = ','.join(map(str, role)) if role else ''
            edit_button_result = edit_user_api(params)

            if edit_button_result['code'] == 200:
                return dict(
                    form_label_validate_status=[None] * len(form_label_output_list),
                    form_label_validate_info=[None] * len(form_label_output_list),
                    modal_visible=False,
                    operations={'type': 'edit'},
                    api_check_token_trigger={'timestamp': time.time()},
                    global_message_container=fuc.FefferyFancyMessage('编辑成功', type='success')
                )

            return dict(
                form_label_validate_status=[None] * len(form_label_output_list),
                form_label_validate_info=[None] * len(form_label_output_list),
                modal_visible=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('编辑失败', type='error')
            )

        return dict(
            form_label_validate_status=[None if form_value_state.get(k) else 'error' for k in form_label_output_list],
            form_label_validate_info=[None if form_value_state.get(k) else f'{form_label_state.get(k)}不能为空!' for k in form_label_output_list],
            modal_visible=dash.no_update,
            operations=dash.no_update,
            api_check_token_trigger={'timestamp': time.time()},
            global_message_container=fuc.FefferyFancyMessage('编辑失败', type='error')
        )

    raise PreventUpdate


@app.callback(
    [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-list-table', 'recentlySwitchDataIndex'),
     Input('user-list-table', 'recentlySwitchStatus'),
     Input('user-list-table', 'recentlySwitchRow')],
    prevent_initial_call=True
)
def table_switch_user_status(recently_switch_data_index, recently_switch_status, recently_switch_row):
    """
    表格内切换用户状态回调
    """
    if recently_switch_data_index:
        if recently_switch_status:
            params = dict(user_id=int(recently_switch_row['key']), status='0', type='status')
        else:
            params = dict(user_id=int(recently_switch_row['key']), status='1', type='status')
        edit_button_result = edit_user_api(params)
        if edit_button_result['code'] == 200:

            return [
                {'type': 'switch-status'},
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('修改成功', type='success')
            ]

        return [
            {'type': 'switch-status'},
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('修改失败', type='error')
        ]

    raise PreventUpdate


@app.callback(
    [Output('user-delete-text', 'children'),
     Output('user-delete-confirm-modal', 'visible'),
     Output('user-delete-ids-store', 'data')],
    [Input({'type': 'user-operation-button', 'index': ALL}, 'nClicks'),
     Input('user-list-table', 'nClicksDropdownItem')],
    [State('user-list-table', 'selectedRowKeys'),
     State('user-list-table', 'recentlyClickedDropdownItemTitle'),
     State('user-list-table', 'recentlyDropdownItemClickedRow')],
    prevent_initial_call=True
)
def user_delete_modal(operation_click, dropdown_click,
                      selected_row_keys, recently_clicked_dropdown_item_title, recently_dropdown_item_clicked_row):
    """
    显示删除用户二次确认弹窗回调
    """
    trigger_id = dash.ctx.triggered_id
    if trigger_id == {'index': 'delete', 'type': 'user-operation-button'} or (trigger_id == 'user-list-table' and recently_clicked_dropdown_item_title == '删除'):

        if trigger_id == {'index': 'delete', 'type': 'user-operation-button'}:
            user_ids = ','.join(selected_row_keys)
        else:
            if recently_clicked_dropdown_item_title == '删除':
                user_ids = recently_dropdown_item_clicked_row['key']
            else:
                raise PreventUpdate

        return [
            f'是否确认删除用户编号为{user_ids}的用户？',
            True,
            {'user_ids': user_ids}
        ]

    raise PreventUpdate


@app.callback(
    [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-delete-confirm-modal', 'okCounts'),
    State('user-delete-ids-store', 'data'),
    prevent_initial_call=True
)
def user_delete_confirm(delete_confirm, user_ids_data):
    """
    删除用户弹窗确认回调，实现删除操作
    """
    if delete_confirm:

        params = user_ids_data
        delete_button_info = delete_user_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')
        ]

    raise PreventUpdate


@app.callback(
    [Output('user-reset-password-confirm-modal', 'visible'),
     Output('reset-password-row-key-store', 'data'),
     Output('reset-password-input', 'value')],
    Input('user-list-table', 'nClicksDropdownItem'),
    [State('user-list-table', 'recentlyClickedDropdownItemTitle'),
     State('user-list-table', 'recentlyDropdownItemClickedRow')],
    prevent_initial_call=True
)
def user_reset_password_modal(dropdown_click, recently_clicked_dropdown_item_title, recently_dropdown_item_clicked_row):
    """
    显示重置用户密码弹窗回调
    """
    if dropdown_click:
        if recently_clicked_dropdown_item_title == '重置密码':
            user_id = recently_dropdown_item_clicked_row['key']
        else:
            raise PreventUpdate

        return [
            True,
            {'user_id': user_id},
            None
        ]

    raise PreventUpdate


@app.callback(
    [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-reset-password-confirm-modal', 'okCounts'),
    [State('reset-password-row-key-store', 'data'),
     State('reset-password-input', 'value')],
    prevent_initial_call=True
)
def user_reset_password_confirm(reset_confirm, user_id_data, reset_password):
    """
    重置用户密码弹窗确认回调，实现重置密码操作
    """
    if reset_confirm:

        user_id_data['password'] = reset_password
        params = user_id_data
        reset_button_info = reset_user_password_api(params)
        if reset_button_info['code'] == 200:
            return [
                {'type': 'reset-password'},
                {'timestamp': time.time()},
                fuc.FefferyFancyMessage('重置成功', type='success')
            ]

        return [
            dash.no_update,
            {'timestamp': time.time()},
            fuc.FefferyFancyMessage('重置失败', type='error')
        ]

    raise PreventUpdate


@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']
        ]

    raise PreventUpdate


# 显示用户导入弹窗及重置上传弹窗组件状态回调
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=dict(
        confirm_loading=Output('user-import-confirm-modal', 'confirmLoading'),
        modal_visible=Output('batch-result-modal', 'visible'),
        batch_result=Output('batch-result-content', 'children'),
        operations=Output('user-operations-store', 'data', allow_duplicate=True),
        api_check_token_trigger=Output('api-check-token', 'data', allow_duplicate=True),
        global_message_container=Output('global-message-container', 'children', allow_duplicate=True)
    ),
    inputs=dict(
        import_confirm=Input('user-import-confirm-modal', 'okCounts')
    ),
    state=dict(
        list_upload_task_record=State('user-upload-choose', 'listUploadTaskRecord'),
        is_update=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 dict(
                    confirm_loading=False,
                    modal_visible=True if batch_import_result.get('message') else False,
                    batch_result=batch_import_result.get('message'),
                    operations={'type': 'batch-import'},
                    api_check_token_trigger={'timestamp': time.time()},
                    global_message_container=fuc.FefferyFancyMessage('导入成功', type='success')
                )

            return dict(
                confirm_loading=False,
                modal_visible=True,
                batch_result=batch_import_result.get('message'),
                operations=dash.no_update,
                api_check_token_trigger={'timestamp': time.time()},
                global_message_container=fuc.FefferyFancyMessage('导入失败', type='error')
            )
        else:
            return dict(
                confirm_loading=False,
                modal_visible=dash.no_update,
                batch_result=dash.no_update,
                operations=dash.no_update,
                api_check_token_trigger=dash.no_update,
                global_message_container=fuc.FefferyFancyMessage('请上传需要导入的文件', type='error')
            )

    raise PreventUpdate


@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('download-user-import-template', 'nClicks')],
    prevent_initial_call=True
)
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 [
                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')
            ]

    raise PreventUpdate


@app.callback(
    Output('user-export-container', 'data', allow_duplicate=True),
    Input('user-export-complete-judge-container', 'data'),
    prevent_initial_call=True
)
def reset_user_export_status(data):
    """
    导出完成后重置下载组件数据回调，防止重复下载文件
    """
    time.sleep(0.5)
    if data:

        return None

    raise PreventUpdate

dash-fastapi-frontend/callbacks/system_c/user_c/user_c.py
insistence/Dash-FastAPI-Admin
import os


class PathConfig:

    # 项目绝对根目录
    ABS_ROOT_PATH = os.path.abspath(os.getcwd())


class RouterConfig:

    # 合法pathname列表
    BASIC_VALID_PATHNAME = [
        '/', '/login', '/forget'
    ]

    # 静态路由列表
    STATIC_VALID_PATHNAME = ['/', '/login', '/forget', '/user/profile']


class ApiBaseUrlConfig:

    # api基本url
    BaseUrl = 'http://127.0.0.1:9099'
    
    
class IconConfig:
    
    ICON_LIST = [
        'antd-carry-out',
        'antd-car',
        'antd-bulb',
        'antd-build',
        'antd-bug',
        'antd-bar-code',
        'antd-branches',
        'antd-aim',
        'antd-issues-close',
        'antd-ellipsis',
        'antd-user',
        'antd-unlock',
        'antd-repair',
        'antd-team',
        'antd-sync',
        'antd-setting',
        'antd-send',
        'antd-schedule',
        'antd-save',
        'antd-rocket',
        'antd-reload',
        'antd-read',
        'antd-qrcode',
        'antd-power-off',
        'antd-number',
        'antd-notification',
        'antd-menu',
        'antd-mail',
        'antd-lock',
        'antd-loading',
        'antd-key',
        'antd-hourglass',
        'antd-global',
        'antd-function',
        'antd-import',
        'antd-export',
        'antd-dashboard',
        'antd-control',
        'antd-console-sql',
        'antd-compass',
        'antd-comment',
        'antd-code',
        'antd-cluster',
        'antd-clear',
        'antd-camera',
        'antd-book',
        'antd-catalog',
        'antd-api',
        'antd-alert',
        'antd-account-book',
        'antd-alipay',
        'antd-alipay-circle',
        'antd-weibo',
        'antd-github',
        'antd-fall',
        'antd-rise',
        'antd-stock',
        'antd-home',
        'antd-fund',
        'antd-area-chart',
        'antd-radar-chart',
        'antd-bar-chart',
        'antd-pie-chart',
        'antd-box-plot',
        'antd-dot-chart',
        'antd-line-chart',
        'antd-field-binary',
        'antd-field-number',
        'antd-field-string',
        'antd-field-time',
        'antd-file-add',
        'antd-file-done',
        'antd-file',
        'antd-file-image',
        'antd-file-markdown',
        'antd-file-pdf',
        'antd-file-protect',
        'antd-file-sync',
        'antd-file-text',
        'antd-file-word',
        'antd-file-zip',
        'antd-filter',
        'antd-fire',
        'antd-woman',
        'antd-arrow-up',
        'antd-arrow-down',
        'antd-arrow-left',
        'antd-arrow-right',
        'antd-flag',
        'antd-user-add',
        'antd-folder-add',
        'antd-man',
        'antd-tag',
        'antd-folder',
        'antd-user-delete',
        'antd-trophy',
        'antd-shopping-cart',
        'antd-folder-open',
        'antd-fork',
        'antd-select',
        'antd-tags',
        'antd-thunderbolt',
        'antd-sound',
        'antd-fund-projection-screen',
        'antd-funnel-plot',
        'antd-gift',
        'antd-robot',
        'antd-pushpin',
        'antd-printer',
        'antd-phone',
        'antd-picture',
        'antd-idcard',
        'antd-partition',
        'antd-monitor',
        'antd-more',
        'antd-apartment',
        'antd-money-collect',
        'antd-experiment',
        'antd-link',
        'antd-mobile',
        'antd-coffee',
        'antd-layout',
        'antd-eye',
        'antd-eye-invisible',
        'antd-exception',
        'antd-dollar',
        'antd-euro',
        'antd-download',
        'antd-environment',
        'antd-deployment-unit',
        'antd-crown',
        'antd-desktop',
        'antd-like',
        'antd-dislike',
        'antd-disconnect',
        'antd-app-store',
        'antd-app-store-add',
        'antd-bell',
        'antd-calculator',
        'antd-calendar',
        'antd-database',
        'antd-history',
        'antd-search',
        'antd-file-search',
        'antd-cloud',
        'antd-cloud-upload',
        'antd-cloud-download',
        'antd-cloud-server',
        'antd-cloud-sync',
        'antd-swap',
        'antd-rollback',
        'antd-login',
        'antd-logout',
        'antd-menu-fold',
        'antd-menu-unfold',
        'antd-full-screen',
        'antd-full-screen-exit',
        'antd-question-circle',
        'antd-plus-circle',
        'antd-minus-circle',
        'antd-info-circle',
        'antd-exclamation-circle',
        'antd-close-circle',
        'antd-check-circle',
        'antd-clock-circle',
        'antd-stop',
        'antd-edit',
        'antd-delete',
        'antd-highlight',
        'antd-redo',
        'antd-undo',
        'antd-zoom-in',
        'antd-zoom-out',
        'antd-sort-ascending',
        'antd-sort-descending',
        'antd-table',
        'antd-question',
        'antd-plus',
        'antd-minus',
        'antd-close',
        'antd-check',
        'antd-sketch',
        'antd-bank',
        'antd-block',
        'antd-insurance',
        'antd-smile',
        'antd-skin',
        'antd-star',
        'antd-right-circle-two-tone',
        'antd-left-circle-two-tone',
        'antd-up-circle-two-tone',
        'antd-down-circle-two-tone',
        'antd-up-square-two-tone',
        'antd-down-square-two-tone',
        'antd-left-square-two-tone',
        'antd-right-square-two-tone',
        'antd-question-circle-two-tone',
        'antd-plus-circle-two-tone',
        'antd-minus-circle-two-tone',
        'antd-plus-square-two-tone',
        'antd-minus-square-two-tone',
        'antd-info-circle-two-tone',
        'antd-exclamation-circle-two-tone',
        'antd-close-circle-two-tone',
        'antd-close-square-two-tone',
        'antd-check-circle-two-tone',
        'antd-check-square-two-tone',
        'antd-edit-two-tone',
        'antd-delete-two-tone',
        'antd-highlight-two-tone',
        'antd-pie-chart-two-tone',
        'antd-box-chart-two-tone',
        'antd-fund-two-tone',
        'antd-sliders-two-tone',
        'antd-api-two-tone',
        'antd-cloud-two-tone',
        'antd-hourglass-two-tone',
        'antd-notification-two-tone',
        'antd-tool-two-tone',
        'antd-down',
        'antd-up',
        'antd-left',
        'antd-right',
        'md-star-half',
        'md-star-border',
        'md-star',
        'md-people',
        'md-plus-one',
        'md-notifications',
        'md-pin-drop',
        'md-layers-clear',
        'md-layers',
        'md-edit-location',
        'md-tune',
        'md-transform',
        'md-timer-off',
        'md-timer',
        'md-file-upload',
        'md-file-download',
        'md-create-new-folder',
        'md-cloud-upload',
        'md-cloud-queue',
        'md-cloud-download',
        'md-cloud-done',
        'md-insert-chart',
        'md-functions',
        'md-format-quote',
        'md-attach-file',
        'md-storage',
        'md-save',
        'md-remove-circle-outline',
        'md-remove-circle',
        'md-remove',
        'md-low-priority',
        'md-link',
        'md-gesture',
        'md-forward',
        'md-flag',
        'md-drafts',
        'md-create',
        'md-content-paste',
        'md-content-cut',
        'md-content-copy',
        'md-clear',
        'md-block',
        'md-backspace',
        'md-add-box',
        'md-add',
        'md-add-circle-outline',
        'md-add-circle',
        'md-location-on',
        'md-mail-outline',
        'md-email',
        'md-not-interested',
        'md-library-books',
        'md-library-add',
        'md-equalizer',
        'md-add-alert',
        'md-visibility-off',
        'md-visibility',
        'md-verified-user',
        'md-update',
        'md-trending-up',
        'md-trending-flat',
        'md-trending-down',
        'md-translate',
        'md-toc',
        'md-timeline',
        'md-thumb-up',
        'md-thumb-down',
        'md-swap-vert',
        'md-swap-horiz',
        'md-supervisor-account',
        'md-subject',
        'md-settings',
        'md-search',
        'md-schedule',
        'md-restore',
        'md-query-builder',
        'md-power-settings-new',
        'md-opacity',
        'md-note-add',
        'md-lock-outline',
        'md-lock-open',
        'md-list',
        'md-lightbulb-outline',
        'md-launch',
        'md-label-outline',
        'md-label',
        'md-input',
        'md-info-outline',
        'md-info',
        'md-hourglass',
        'md-home',
        'md-history',
        'md-highlight-off',
        'md-help-outline',
        'md-help',
        'md-get-app',
        'md-translate',
        'md-fingerprint',
        'md-findIn-page',
        'md-favorite-border',
        'md-favorite',
        'md-extension',
        'md-explore',
        'md-exit-to-app',
        'md-event',
        'md-description',
        'md-delete-forever',
        'md-delete',
        'md-dashboard',
        'md-code',
        'md-build',
        'md-bug-report',
        'md-assignment',
        'md-assessment',
        'md-alarm-on',
        'md-alarm-off',
        'md-alarm-add',
        'md-alarm',
        'md-account-circle',
        'fc-vlc',
        'fc-view-details',
        'fc-upload',
        'fc-tree-structure',
        'fc-timeline',
        'fc-template',
        'fc-survey',
        'fc-signature',
        'fc-share',
        'fc-services',
        'fc-rules',
        'fc-questions',
        'fc-process',
        'fc-plus',
        'fc-overtime',
        'fc-organization',
        'fc-numerical-sorting21',
        'fc-numerical-sorting12',
        'fc-multiple-inputs',
        'fc-mind-map',
        'fc-menu',
        'fc-list',
        'fc-like',
        'fc-like-placeholder',
        'fc-info',
        'fc-import',
        'fc-image-file',
        'fc-idea',
        'fc-home',
        'fc-high-priority',
        'fc-low-priority',
        'fc-genealogy',
        'fc-full-trash',
        'fc-document-search',
        'fc-file',
        'fc-faq',
        'fc-export',
        'fc-empty-trash',
        'fc-download',
        'fc-document',
        'fc-deployment',
        'fc-delete-database',
        'fc-conference-call',
        'fc-database',
        'fc-data-protection',
        'fc-data-encryption',
        'fc-data-configuration',
        'fc-data-backup',
        'fc-checkmark',
        'fc-cancel',
        'fc-briefcase',
        'fc-binoculars',
        'fc-automatic',
        'fc-alphabetical-sorting-za',
        'fc-alphabetical-sorting-az',
        'fc-add-database',
        'fc-accept-database',
        'fc-about',
        'fc-radar-chart',
        'fc-scatter-chart',
        'fc-pie-chart',
        'fc-line-chart',
        'fc-flow-chart',
        'fc-doughnut-chart',
        'fc-bar-chart',
        'fc-area-chart',
        'fc-line-bar-chart',
        'fc-workflow',
        'fc-todo-list',
        'fc-synchronize',
        'fc-repair',
        'fc-statistics',
        'fc-settings',
        'fc-search',
        'fc-serial-tasks',
        'fc-safe',
        'fc-negative-dynamic',
        'fc-positive-dynamic',
        'fc-planner',
        'fc-parallel-tasks',
        'fc-org-unit',
        'fc-opened-folder',
        'fc-ok',
        'fc-inspection',
        'fc-globe',
        'fc-folder',
        'fc-electronics',
        'fc-data-sheet',
        'fc-command-line',
        'fc-calendar',
        'fc-calculator',
        'fc-bullish',
        'fc-bearish',
        'fc-bookmark',
        'fc-approval',
        'fc-advertising',
        'di-linux',
        'di-python',
        'di-chrome',
        'di-database',
        'di-firefox',
        'di-markdown',
        'di-postgresql',
        'di-terminal',
        'di-windows',
        'bi-table',
        'bi-analyse',
        'bi-layer',
        'bi-layer-minus',
        'bi-layer-plus',
        'bs-list-task',
        'bs-list-check',
        'bs-link',
        'bs-link-45-deg',
        'bs-envelope-open',
        'bs-envelope',
        'bs-alarm',
        'gi-mesh-network',
        'im-earth',
        'im-sphere'
    ]

dash-fastapi-frontend/config/global_config.py
insistence/Dash-FastAPI-Admin
import dash
import os
import time
from loguru import logger
from flask import request, session
from user_agents import parse
from config.global_config import PathConfig

app = dash.Dash(
    __name__,
    compress=True,
    suppress_callback_exceptions=True,
    update_title=None
)

server = app.server

app.title = '通用后台管理系统'

# 配置密钥
app.server.secret_key = 'Dash-FastAPI'
app.server.config['COMPRESS_ALGORITHM'] = 'br'
app.server.config['COMPRESS_BR_LEVEL'] = 8

log_time = time.strftime("%Y%m%d", time.localtime())
# sys_log_file_path = os.path.join(PathConfig.ABS_ROOT_PATH, 'log', 'sys_log', f'sys_request_log_{log_time}.log')
api_log_file_path = os.path.join(PathConfig.ABS_ROOT_PATH, 'log', 'api_log', f'api_request_log_{log_time}.log')
# logger.add(sys_log_file_path, filter=lambda x: '[sys]' in x['message'],
#            rotation="50MB", encoding="utf-8", enqueue=True, compression="zip")
logger.add(api_log_file_path, filter=lambda x: '[api]' in x['message'],
           rotation="50MB", encoding="utf-8", enqueue=True, compression="zip")


# 获取用户浏览器信息
@server.before_request
def get_user_agent_info():
    user_string = str(request.user_agent)
    user_agent = parse(user_string)
    bw = user_agent.browser.family
    bw_version = user_agent.browser.version[0]
    if bw == 'IE':
        logger.warning("[sys]请求人:{}||请求IP:{}||请求方法:{}||请求Data:{}",
                session.get('name'), request.remote_addr, request.method, '用户使用IE内核')
        return "<h1 style='color: red'>请不要使用IE浏览器或360浏览器兼容模式</h1>"
    if bw_version < 71:
        logger.warning("[sys]请求人:{}||请求IP:{}||请求方法:{}||请求Data:{}",
                       session.get('name'), request.remote_addr, request.method, '用户Chrome内核版本太低')
        return "<h1 style='color: red'>Chrome内核版本号太低，请升级浏览器</h1>" \
               "<h1 style='color: red'><a href='https://www.google.cn/chrome/'>点击此处</a>可下载最新版Chrome浏览器</h1>"


# 配置系统日志
# @server.after_request
# def get_callbacks_log(response):
#     logger.info("[sys]请求人:{}||请求IP:{}||请求方法:{}||请求Data:{}",
#                 session.get('name'), request.remote_addr, request.method, request.data.decode("utf-8"))
#
#     return response

dash-fastapi-frontend/server.py
insistence/Dash-FastAPI-Admin
from dash import html, dcc


def render_store_container():

    return html.Div(
        [
            # 应用主题颜色存储容器
            dcc.Store(id='system-app-primary-color-container', data='#1890ff'),
            dcc.Store(id='custom-app-primary-color-container', storage_type='session'),
            # 接口校验返回存储容器
            dcc.Store(id='api-check-token'),
            # 接口校验返回存储容器
            dcc.Store(id='api-check-result-container'),
            # token存储容器
            dcc.Store(id='token-container', storage_type='session'),
            # 菜单信息存储容器
            dcc.Store(id='menu-info-store-container'),
            dcc.Store(id='menu-list-store-container'),
            # 菜单current_key存储容器
            dcc.Store(id='current-key-container'),
        ]
    )

dash-fastapi-frontend/store/store.py
insistence/Dash-FastAPI-Admin
import requests
from typing import Optional
from flask import session, request
from config.global_config import ApiBaseUrlConfig
from server import logger


def api_request(method: str, url: str, is_headers: bool, params: Optional[dict] = None, data: Optional[dict] = None,
                json: Optional[dict] = None, timeout: Optional[int] = None, stream: Optional[bool] = False):
    api_url = ApiBaseUrlConfig.BaseUrl + url
    method = method.lower().strip()
    user_agent = request.headers.get('User-Agent')
    authorization = session.get('Authorization') if session.get('Authorization') else ''
    if is_headers:
        api_headers = {'Authorization': 'Bearer ' + authorization, 'remote_addr': request.remote_addr,
                       'User-Agent': user_agent}
    else:
        api_headers = {'remote_addr': request.remote_addr, 'User-Agent': user_agent}
    try:
        if method == 'get':
            response = requests.get(url=api_url, params=params, data=data, json=json, headers=api_headers,
                                    timeout=timeout, stream=stream)
        elif method == 'post':
            response = requests.post(url=api_url, params=params, data=data, json=json, headers=api_headers,
                                     timeout=timeout, stream=stream)
        elif method == 'delete':
            response = requests.delete(url=api_url, params=params, data=data, json=json, headers=api_headers,
                                       timeout=timeout, stream=stream)
        elif method == 'put':
            response = requests.put(url=api_url, params=params, data=data, json=json, headers=api_headers,
                                    timeout=timeout, stream=stream)
        elif method == 'patch':
            response = requests.patch(url=api_url, params=params, data=data, json=json, headers=api_headers,
                                      timeout=timeout, stream=stream)
        else:
            raise ValueError(f'Unsupported HTTP method: {method}')

        data_list = [params, data, json]
        if stream:
            response_code = response.status_code
            response_message = '获取成功' if response_code == 200 else '获取失败'
        else:
            response_code = response.json().get('code')
            response_message = response.json().get('message')
        session['code'] = response_code
        session['message'] = response_message
        if response_code == 200:
            logger.info("[api]请求人:{}||请求IP:{}||请求方法:{}||请求Api:{}||请求参数:{}||请求结果:{}",
                        session.get('user_info').get('user_name') if session.get('user_info') else None,
                        request.remote_addr, method, url,
                        ','.join([str(x) for x in data_list if x]),
                        response_message)
        else:
            logger.warning("[api]请求人:{}||请求IP:{}||请求方法:{}||请求Api:{}||请求参数:{}||请求结果:{}",
                           session.get('user_info').get('user_name') if session.get('user_info') else None,
                           request.remote_addr, method, url,
                           ','.join([str(x) for x in data_list if x]),
                           response_message)

        return response if stream else response.json()
    except Exception as e:
        logger.error("[api]请求人:{}||请求IP:{}||请求方法:{}||请求Api:{}||请求结果:{}",
                     session.get('user')['user_name'], request.remote_addr, method, url, str(e))

        raise Exception

dash-fastapi-frontend/utils/request.py
insistence/Dash-FastAPI-Admin
def find_node_values(data, key):
    """
    递归查找所有包含目标键的字典，并返回该键对应的值组成的列表。
    :param data: 待查找的树形list
    :param key: 目标键
    :return: 包含目标键的字典中目标键对应的值组成的列表
    """
    result = []
    for item in data:
        if isinstance(item, dict):
            if key in item:
                result.append(item[key])
            # 递归查找子节点
            result.extend(find_node_values(item.values(), key))
        elif isinstance(item, list):
            # 递归查找子节点
            result.extend(find_node_values(item, key))
    return result


def find_key_by_href(data, href):
    """
        递归查找所有包含目标键的字典，并返回该键对应的值组成的列表。
        :param data: 待查找的树形list
        :param href: 目标pathname
        :return: 目标值对应的key
        """
    for item in data:
        if 'children' in item:
            result = find_key_by_href(item['children'], href)
            if result is not None:
                return result
        elif 'href' in item['props'] and item['props']['href'] == href:
            return item['props']['key']
    return None


def find_title_by_key(data, key):
    """
    递归查找所有包含目标键的字典，并返回该键对应的值组成的列表。
    :param data: 待查找的树形list
    :param key: 目标key
    :return: 目标值对应的title
    """
    for item in data:
        if 'children' in item:
            result = find_title_by_key(item['children'], key)
            if result is not None:
                return result
        elif 'key' in item['props'] and item['props']['key'] == key:
            return item['props']['title']
    return None


def find_href_by_key(data, key):
    """
    递归查找所有包含目标键的字典，并返回该键对应的值组成的列表。
    :param data: 待查找的树形list
    :param key: 目标key
    :return: 目标值对应的href
    """
    for item in data:
        if 'children' in item:
            result = find_href_by_key(item['children'], key)
            if result is not None:
                return result
        elif 'key' in item['props'] and item['props']['key'] == key:
            return item['props'].get('href')
    return None


def find_modules_by_key(data, key):
    """
    递归查找所有包含目标键的字典，并返回该键对应的值组成的列表。
    :param data: 待查找的树形list
    :param key: 目标key
    :return: 目标值对应的module
    """
    for item in data:
        if 'children' in item:
            result = find_modules_by_key(item['children'], key)
            if result is not None:
                return result
        elif 'key' in item['props'] and item['props']['key'] == key:
            return item['props'].get('modules')
    return None


def find_parents(tree, target_key):
    """
    递归查找所有包含目标键的字典，并返回该键对应的值组成的列表。
    :param tree: 待查找的树形list
    :param target_key: 目标target_key
    :return: 目标值对应的所有根节点的title
    """
    result = []

    def search_parents(node, key):
        if 'children' in node:
            for child in node['children']:
                temp_result = search_parents(child, key)
                if len(temp_result) > 0:
                    result.append({'title': node['props']['title']})
                    result.extend(temp_result)
                    return result

        if 'key' in node['props'] and node['props']['key'] == key:
            result.append({'title': node['props']['title']})
            return result

        return []

    for node in tree:
        result = search_parents(node, target_key)
        if len(result) > 0:
            break

    return result[::-1]


def deal_user_menu_info(pid: int, permission_list: list):
    """
    工具方法：根据菜单信息生成树形嵌套数据
    :param pid: 菜单id
    :param permission_list: 菜单列表信息
    :return: 菜单树形嵌套数据
    """
    menu_list = []
    for permission in permission_list:
        if permission['parent_id'] == pid:
            children = deal_user_menu_info(permission['menu_id'], permission_list)
            antd_menu_list_data = {}
            if children and permission['menu_type'] == 'M':
                antd_menu_list_data['component'] = 'SubMenu'
                antd_menu_list_data['props'] = {
                    'key': str(permission['menu_id']),
                    'title': permission['menu_name'],
                    'icon': permission['icon'],
                    'modules': permission['component']
                }
                antd_menu_list_data['children'] = children
            elif permission['menu_type'] == 'C':
                antd_menu_list_data['component'] = 'Item'
                antd_menu_list_data['props'] = {
                    'key': str(permission['menu_id']),
                    'title': permission['menu_name'],
                    'icon': permission['icon'],
                    'href': permission['path'],
                    'modules': permission['component']
                }
                antd_menu_list_data['button'] = children
            elif permission['menu_type'] == 'F':
                antd_menu_list_data['component'] = 'Button'
                antd_menu_list_data['props'] = {
                    'key': str(permission['menu_id']),
                    'title': permission['menu_name'],
                    'icon': permission['icon']
                }
            elif permission['is_frame'] == 0:
                antd_menu_list_data['component'] = 'Item'
                antd_menu_list_data['props'] = {
                    'key': str(permission['menu_id']),
                    'title': permission['menu_name'],
                    'icon': permission['icon'],
                    'href': permission['path'],
                    'target': '_blank',
                    'modules': 'link'
                }
            else:
                antd_menu_list_data['component'] = 'Item'
                antd_menu_list_data['props'] = {
                    'key': str(permission['menu_id']),
                    'title': permission['menu_name'],
                    'icon': permission['icon'],
                    'href': permission['path'],
                    'modules': permission['component']
                }
            menu_list.append(antd_menu_list_data)

    return menu_list


def get_dept_tree(pid: int, permission_list: list):
    """
    工具方法：根据部门信息生成树形嵌套数据
    :param pid: 部门id
    :param permission_list: 部门列表信息
    :return: 部门树形嵌套数据
    """
    dept_list = []
    for permission in permission_list:
        if permission['parent_id'] == pid:
            children = get_dept_tree(permission['dept_id'], permission_list)
            dept_list_data = {}
            if children:
                dept_list_data['children'] = children
            dept_list_data['key'] = str(permission['dept_id'])
            dept_list_data['dept_id'] = permission['dept_id']
            dept_list_data['dept_name'] = permission['dept_name']
            dept_list_data['order_num'] = permission['order_num']
            dept_list_data['status'] = permission['status']
            dept_list_data['create_time'] = permission['create_time']
            dept_list_data['operation'] = permission['operation']
            dept_list.append(dept_list_data)

    return dept_list


def list_to_tree(permission_list: list) -> list:
    """
    工具方法：根据菜单信息生成树形嵌套数据
    :param permission_list: 菜单列表信息
    :return: 菜单树形嵌套数据
    """
    # 转成menu_id为Key的字典
    mapping: dict = dict(zip([i['menu_id'] for i in permission_list], permission_list))

    # 树容器
    container: list = []

    for d in permission_list:
        # 如果找不到父级项，则是根节点
        parent: dict = mapping.get(d['parent_id'])
        if parent is None:
            container.append(d)
        else:
            children: list = parent.get('children')
            if not children:
                children = []
            children.append(d)
            parent.update({'children': children})

    return container


def get_search_panel_data(menu_list: list):
    search_data = []
    for item in menu_list:
        if item.get('menu_type') == 'C' or item.get('is_frame') == 0:
            item_dict = dict(
                id=str(item.get('menu_id')),
                title=item.get('menu_name'),
                handler='() => window.open("%s", "_self")' % item.get('path')
            )
            search_data.append(item_dict)

    return search_data

dash-fastapi-frontend/utils/tree_tool.py
insistence/Dash-FastAPI-Admin
from . import (
    layout,
    dashboard,
    system,
    monitor,
    tool,
    login,
    page_404,
    forget
)

dash-fastapi-frontend/views/__init__.py
insistence/Dash-FastAPI-Admin
from .components import page_top, page_bottom


def render_dashboard():
    return [
        page_top.render_page_top(),
        page_bottom.render_page_bottom()
    ]

dash-fastapi-frontend/views/dashboard/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac
import feffery_utils_components as fuc
import feffery_antd_charts as fact


def render_page_bottom():
    # 模拟数据
    radar_origin_data = [
        {
            'name': '个人',
            'ref': 10,
            'koubei': 8,
            'output': 4,
            'contribute': 5,
            'hot': 7
        },
        {
            'name': '团队',
            'ref': 3,
            'koubei': 9,
            'output': 6,
            'contribute': 3,
            'hot': 1
        },
        {
            'name': '部门',
            'ref': 4,
            'koubei': 1,
            'output': 6,
            'contribute': 5,
            'hot': 7
        }
    ]

    radar_data = []
    radar_title_map = {
        'ref': '引用',
        'koubei': '口碑',
        'output': '产量',
        'contribute': '贡献',
        'hot': '热度'
    }

    for item in radar_origin_data:
        for key, value in item.items():
            if key != 'name':
                radar_data.append({
                    'name': item['name'],
                    'label': radar_title_map[key],
                    'value': value
                })

    project_list = [
        {
            "id": "xxx1",
            "title": "Alipay",
            "logo": "https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png",
            "description": "那是一种内在的东西，他们到达不了，也无法触及的",
            "updatedAt": "2023-09-15T01:08:36.135Z",
            "member": "科学搬砖组",
            "href": "",
            "memberLink": ""
        },
        {
            "id": "xxx2",
            "title": "Angular",
            "logo": "https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png",
            "description": "希望是一个好东西，也许是最好的，好东西是不会消亡的",
            "updatedAt": "2017-07-24T00:00:00.000Z",
            "member": "全组都是吴彦祖",
            "href": "",
            "memberLink": ""
        },
        {
            "id": "xxx3",
            "title": "Ant Design",
            "logo": "https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png",
            "description": "城镇中有那么多的酒馆，她却偏偏走进了我的酒馆",
            "updatedAt": "2023-09-15T01:08:36.135Z",
            "member": "中二少女团",
            "href": "",
            "memberLink": ""
        },
        {
            "id": "xxx4",
            "title": "Ant Design Pro",
            "logo": "https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png",
            "description": "那时候我只会想自己想要什么，从不想自己拥有什么",
            "updatedAt": "2017-07-23T00:00:00.000Z",
            "member": "程序员日常",
            "href": "",
            "memberLink": ""
        },
        {
            "id": "xxx5",
            "title": "Bootstrap",
            "logo": "https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png",
            "description": "凛冬将至",
            "updatedAt": "2017-07-23T00:00:00.000Z",
            "member": "高逼格设计天团",
            "href": "",
            "memberLink": ""
        },
        {
            "id": "xxx6",
            "title": "React",
            "logo": "https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png",
            "description": "生命就像一盒巧克力，结果往往出人意料",
            "updatedAt": "2017-07-23T00:00:00.000Z",
            "member": "骗你来学计算机",
            "href": "",
            "memberLink": ""
        }
    ]

    activity_list = [
        {
            "id": "trend-1",
            "updatedAt": "2023-09-15 01:08:36",
            "user": {
                "name": "曲丽丽",
                "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
            },
            "group": {
                "name": "高逼格设计天团",
                "link": "http://github.com/"
            },
            "project": {
                "name": "六月迭代",
                "link": "http://github.com/"
            },
            "template": "新建项目"
        },
        {
            "id": "trend-2",
            "updatedAt": "2023-09-15 01:08:36",
            "user": {
                "name": "付小小",
                "avatar": "https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png"
            },
            "group": {
                "name": "高逼格设计天团",
                "link": "http://github.com/"
            },
            "project": {
                "name": "六月迭代",
                "link": "http://github.com/"
            },
            "template": "新建项目"
        },
        {
            "id": "trend-3",
            "updatedAt": "2023-09-15 01:08:36",
            "user": {
                "name": "林东东",
                "avatar": "https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png"
            },
            "group": {
                "name": "中二少女团",
                "link": "http://github.com/"
            },
            "project": {
                "name": "六月迭代",
                "link": "http://github.com/"
            },
            "template": "新建项目"
        },
        {
            "id": "trend-4",
            "updatedAt": "2023-09-15 01:08:36",
            "user": {
                "name": "周星星",
                "avatar": "https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png"
            },
            "group": {
                "name": "白鹭酱油开发组",
                "link": "http://github.com/"
            },
            "project": {
                "name": "5 月日常迭代",
                "link": "http://github.com/"
            },
            "template": "发布了"
        },
        {
            "id": "trend-5",
            "updatedAt": "2023-09-15 01:08:36",
            "user": {
                "name": "乐哥",
                "avatar": "https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png"
            },
            "group": {
                "name": "程序员日常",
                "link": "http://github.com/"
            },
            "project": {
                "name": "品牌迭代",
                "link": "http://github.com/"
            },
            "template": "新建项目"
        }
    ]

    return html.Div(
        [
            fac.AntdRow(
                [
                    fac.AntdCol(
                        [
                            fac.AntdCard(
                                [
                                    fac.AntdCardGrid(
                                        [
                                            html.Div(
                                                [
                                                    fac.AntdAvatar(
                                                        mode='image',
                                                        src=item.get('logo'),
                                                        size='small'
                                                    ),
                                                    html.A(
                                                        item.get('title')
                                                    )
                                                ],
                                                className='card-title'
                                            ),
                                            html.Div(
                                                item.get('description'),
                                                className='card-description'
                                            ),
                                            html.Div(
                                                [
                                                    html.A(item.get('member')),
                                                    html.Span(
                                                        '9小时前',
                                                        className='datetime'
                                                    )
                                                ],
                                                className='project-item'
                                            )
                                        ]
                                    )
                                    for item in project_list
                                ],
                                className='project-list',
                                title='进行中的项目',
                                bordered=False,
                                extraLink={
                                    'content': '全部项目'
                                },
                                bodyStyle={
                                    'padding': 0
                                },
                                style={
                                    'marginBottom': '24px',
                                    'boxShadow': 'rgba(0, 0, 0, 0.1) 0px 4px 12px'
                                }
                            ),
                            fac.AntdCard(
                                fac.AntdSpace(
                                    [
                                        html.Div(
                                            [
                                                html.Div(
                                                    [
                                                        html.Div(
                                                            fac.AntdAvatar(
                                                                mode='image',
                                                                src=item.get('user').get('avatar'),
                                                                size='small',
                                                            ),
                                                            style={
                                                                'flex': '0 1',
                                                                'marginRight': '16px'
                                                            }
                                                        ),
                                                        html.Div(
                                                            [
                                                                html.Div(
                                                                    [
                                                                        html.Span(f"{item.get('user').get('name')} 在 "),
                                                                        html.A(item.get('group').get('name'), href=item.get('group').get('link')),
                                                                        html.Span(f" {item.get('template')} "),
                                                                        html.A(item.get('project').get('name'), href=item.get('project').get('link'))
                                                                    ],
                                                                    key=item.get('id')
                                                                ),
                                                                html.Div(
                                                                    item.get('updatedAt'),
                                                                    style={
                                                                        'color': 'rgba(0,0,0,.45)',
                                                                        'fontSize': '14px',
                                                                        'lineHeight': '22px'
                                                                    }
                                                                ),
                                                            ],
                                                            style={
                                                                'flex': '1 1 auto'
                                                            }
                                                        ),
                                                    ],
                                                    style={
                                                        'display': 'flex'
                                                    }
                                                ),
                                                fac.AntdDivider()
                                            ]
                                        )
                                        for item in activity_list
                                    ],
                                    direction='vertical',
                                    style={
                                        'width': '100%',
                                        'maxHeight': '500px',
                                        'overflowY': 'auto'
                                    }
                                ),
                                title='动态',
                                bordered=False,
                                style={
                                    'marginBottom': '24px',
                                    'boxShadow': 'rgba(0, 0, 0, 0.1) 0px 4px 12px'
                                }
                            ),
                        ],
                        xl=16,
                        lg=24,
                        md=24,
                        sm=24,
                        xs=24
                    ),
                    fac.AntdCol(
                        [
                            fac.AntdCard(
                                html.Div(
                                    [
                                        html.A('操作一'),
                                        html.A('操作二'),
                                        html.A('操作三'),
                                        html.A('操作四'),
                                        html.A('操作五'),
                                        fac.AntdButton(
                                            '添加',
                                            type="primary",
                                            size='small',
                                            icon=fac.AntdIcon(icon='antd-plus'),
                                            style={
                                                'marginLeft': '20px'
                                            }
                                        )
                                    ],
                                    className='item-group'
                                ),
                                title='快速开始 / 便捷导航',
                                bordered=False,
                                style={
                                    'marginBottom': '24px',
                                    'boxShadow': 'rgba(0, 0, 0, 0.1) 0px 4px 12px'
                                }
                            ),
                            fac.AntdCard(
                                html.Div(
                                    fact.AntdRadar(
                                        height=343,
                                        data=radar_data,
                                        xField='label',
                                        yField='value',
                                        seriesField='name',
                                        point={},
                                        legend={
                                            'position': 'bottom'
                                        },
                                    ),
                                    style={
                                        'minHeight': '400px',
                                        'margin': '0 auto',
                                        'paddingTop': '30px'
                                    }
                                ),
                                title='XX 指数',
                                bordered=False,
                                bodyStyle={
                                    'padding': 0
                                },
                                style={
                                    'marginBottom': '24px',
                                    'boxShadow': 'rgba(0, 0, 0, 0.1) 0px 4px 12px'
                                }
                            ),
                            fac.AntdCard(
                                html.Div(
                                    fac.AntdRow(
                                        [
                                            fac.AntdCol(
                                                html.A(
                                                    [
                                                        fac.AntdAvatar(
                                                            mode='image',
                                                            src=item.get('logo'),
                                                            size='small'
                                                        ),
                                                        html.Span(
                                                            item.get('member'),
                                                            className='member'
                                                        )
                                                    ]
                                                ),
                                                span=12
                                            )
                                            for item in project_list
                                        ]
                                    ),
                                    className='members'
                                ),
                                title='团队',
                                bordered=False,
                                style={
                                    'marginBottom': '24px',
                                    'boxShadow': 'rgba(0, 0, 0, 0.1) 0px 4px 12px'
                                }
                            ),
                        ],
                        xl=8,
                        lg=24,
                        md=24,
                        sm=24,
                        xs=24,
                        style={
                            'padding': '0 12px'
                        }
                    ),
                ],
                gutter=24
            ),
            fuc.FefferyStyle(
                rawStyle='''
                    .project-list .card-title {
                        font-size: 0;
                    }

                    .project-list .card-title a {
                        color: rgba(0, 0, 0, 0.85);
                        margin-left: 12px;
                        line-height: 24px;
                        height: 24px;
                        display: inline-block;
                        vertical-align: top;
                        font-size: 14px;
                    }

                    .project-list .card-title a:hover {
                        color: #1890ff;
                    }

                    .project-list .card-description {
                        color: rgba(0, 0, 0, 0.45);
                        height: 44px;
                        line-height: 22px;
                        overflow: hidden;
                    }

                    .project-list .project-item {
                        display: flex;
                        margin-top: 8px;
                        overflow: hidden;
                        font-size: 12px;
                        height: 20px;
                        line-height: 20px;
                    }

                    .project-list .project-item a {
                        color: rgba(0, 0, 0, 0.45);
                        display: inline-block;
                        flex: 1 1 0;
                    }

                    .project-list .project-item a:hover {
                        color: #1890ff;
                    }

                    .project-list .project-item .datetime {
                        color: rgba(0, 0, 0, 0.25);
                        flex: 0 0 auto;
                        float: right;
                    }

                    .project-list .ant-card-meta-description {
                        color: rgba(0, 0, 0, 0.45);
                        height: 44px;
                        line-height: 22px;
                        overflow: hidden;
                    }

                    .item-group {
                        padding: 20px 0 8px 24px;
                        font-size: 0;
                    }

                    .item-group a {
                        color: rgba(0, 0, 0, 0.65);
                        display: inline-block;
                        font-size: 14px;
                        margin-bottom: 13px;
                        width: 25%;
                        padding-left: 20px;
                    }

                    .members a {
                        display: block;
                        margin: 12px 0;
                        line-height: 24px;
                        height: 24px;
                    }

                    .members a .member {
                        font-size: 14px;
                        color: rgba(0, 0, 0, 0.65);
                        line-height: 24px;
                        max-width: 100px;
                        vertical-align: top;
                        margin-left: 12px;
                        transition: all 0.3s;
                        display: inline-block;
                    }

                    .members a .member:hover span {
                        color: #1890ff;
                    }
                    '''
            )
        ]
    )

dash-fastapi-frontend/views/dashboard/components/page_bottom.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac
import feffery_utils_components as fuc

from flask import session


def render_page_top():

    return html.Div(
            [
                html.Div(
                    fac.AntdAvatar(
                        id='dashboard-avatar-info',
                        mode='image',
                        src=session.get('user_info').get('avatar'),
                        size='large'
                    ),
                    className='avatar',
                ),
                html.Div(
                    [
                        html.Div(
                            fac.AntdText(f"早安，{session.get('user_info').get('nick_name')}，祝你开心每一天！"),
                            className='content-title',
                        ),
                        html.Div('交互专家 |蚂蚁金服－某某某事业群－某某平台部－某某技术部－UED')
                    ],
                    className='content',
                ),
                html.Div(
                    [
                        html.Div(
                            fac.AntdStatistic(
                                title='项目数',
                                value=56
                            ),
                            className='stat-item'
                        ),
                        html.Div(
                            fac.AntdStatistic(
                                title='团队内排名',
                                value=8,
                                suffix='/ 24'
                            ),
                            className='stat-item'
                        ),
                        html.Div(
                            fac.AntdStatistic(
                                title='项目访问',
                                value=2223
                            ),
                            className='stat-item'
                        ),
                    ],
                    className='extra-content'
                ),
                fuc.FefferyStyle(
                    rawStyle='''
                    .page-header-content {
                        display: flex;
                    }

                    .page-header-content .avatar {
                        flex: 0 1 72px;
                    }

                    .page-header-content .avatar > span {
                        display: block;
                        width: 72px;
                        height: 72px;
                        border-radius: 72px;
                    }

                    .page-header-content .content {
                        position: relative;
                        top: 4px;
                        margin-left: 24px;
                        line-height: 22px;
                        color: rgba(0,0,0,.45);
                        flex: 1 1 auto;
                    }

                    .page-header-content .content .content-title {
                        margin-bottom: 12px;
                        font-size: 20px;
                        font-weight: 500;
                        line-height: 28px;
                        color: rgba(0,0,0,.85);
                    }

                    .extra-content {
                        float: right;
                        white-space: nowrap;
                    }

                    .extra-content .stat-item {
                        position: relative;
                        display: inline-block;
                        padding: 0 32px;
                    }

                    .extra-content .stat-item > p:first-child {
                        margin-bottom: 4px;
                        font-size: 14px;
                        line-height: 22px;
                        color: rgba(0,0,0,.45);
                    }

                    .extra-content .stat-item > p {
                        margin: 0;
                        font-size: 30px;
                        line-height: 38px;
                        color: rgba(0,0,0,.85);
                    }

                    .extra-content .stat-item > p > span {
                        font-size: 20px;
                        color: rgba(0,0,0,.45);
                    }

                    .extra-content .stat-item::after {
                        position: absolute;
                        top: 8px;
                        right: 0;
                        width: 1px;
                        height: 40px;
                        background-color: #e8e8e8;
                        content: '';
                    }

                    .extra-content .stat-item:last-child {
                        padding-right: 0;
                    }

                    .extra-content .stat-item:last-child::after {
                        display: none;
                    }
                    '''
                )
            ],
            className='page-header-content',
            style={
                'padding': '12px',
                'marginBottom': '24px',
                'boxShadow': 'rgba(0, 0, 0, 0.1) 0px 4px 12px'
            }
        )

dash-fastapi-frontend/views/dashboard/components/page_top.py
insistence/Dash-FastAPI-Admin
from dash import html, dcc
import feffery_antd_components as fac
import feffery_utils_components as fuc

import callbacks.forget_c


def render_forget_content():
    return html.Div(
        [

            fac.AntdCard(
                [
                    dcc.Store(id='sms_code-session_id-container', storage_type='session'),
                    fac.AntdForm(
                        [
                            fac.AntdFormItem(
                                fac.AntdInput(
                                    placeholder='请输入用户名',
                                    id='forget-username',
                                    size='large',
                                    prefix=fac.AntdIcon(
                                        icon='antd-user'
                                    ),
                                ),
                                id='forget-username-form-item'
                            ),
                            fac.AntdFormItem(
                                fac.AntdInput(
                                    placeholder='请输入新密码',
                                    id='forget-password',
                                    mode='password',
                                    passwordUseMd5=True,
                                    size='large',
                                    prefix=fac.AntdIcon(
                                        icon='antd-lock'
                                    ),
                                ),
                                id='forget-password-form-item'
                            ),
                            fac.AntdFormItem(
                                fac.AntdInput(
                                    placeholder='请再次输入新密码',
                                    id='forget-password-again',
                                    mode='password',
                                    passwordUseMd5=True,
                                    size='large',
                                    prefix=fac.AntdIcon(
                                        icon='antd-lock'
                                    ),
                                ),
                                id='forget-password-again-form-item'
                            ),
                            fac.AntdSpace(
                                [
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            placeholder='请输入短信验证码',
                                            id='forget-input-captcha',
                                            size='large',
                                            prefix=fac.AntdIcon(
                                                icon='antd-check-circle'
                                            ),
                                            style={
                                                'width': '270px'
                                            }
                                        ),
                                        id='forget-captcha-form-item'
                                    ),
                                    fac.AntdFormItem(
                                        fac.AntdButton(
                                            '获取验证码',
                                            id='get-message-code',
                                            type='primary',
                                            size='large'
                                        )
                                    ),
                                ],
                                align='end',
                                size=10
                            ),
                            fac.AntdFormItem(
                                fac.AntdButton(
                                    '保存',
                                    id='forget-submit',
                                    type='primary',
                                    loadingChildren='保存中',
                                    autoSpin=True,
                                    block=True,
                                    size='large',
                                ),
                                style={
                                    'marginTop': '20px'
                                }
                            )
                        ],
                        layout='vertical',
                        style={
                            'width': '100%'
                        }
                    ),

                    fuc.FefferyCountDown(id='message-code-count-down')
                ],
                id='forget-form-container',
                title='重置密码',
                hoverable=True,
                extraLink={
                    'content': '返回登录',
                    'href': '/login',
                    'target': '_self',
                    'style': {
                        'font-size': '16px'
                    }
                },
                headStyle={
                    'font-weight': 'bold',
                    'text-align': 'center',
                    'font-size': '30px'
                },
                style={
                    'position': 'fixed',
                    'top': '16%',
                    'left': '50%',
                    'width': '500px',
                    'padding': '0px 30px',
                    'transform': 'translateX(-50%)'
                }
            ),
        ]
    )


dash-fastapi-frontend/views/forget.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_utils_components as fuc
import feffery_antd_components as fac

from views.layout.components.head import render_head_content
from views.layout.components.content import render_main_content
from views.layout.components.aside import render_aside_content
import callbacks.layout_c.fold_side_menu
import callbacks.layout_c.index_c


def render_content(menu_info):

    return fuc.FefferyTopProgress(
        html.Div(
            [
                # 全局重载
                fuc.FefferyReload(id='trigger-reload-output'),

                html.Div(id='idle-placeholder-container'),

                # 布局设置抽屉
                fac.AntdDrawer(
                    [
                        fac.AntdText(
                            '主题颜色',
                            style={
                                'fontSize': 16,
                                'fontWeight': 500
                            }
                        ),
                        fuc.FefferyHexColorPicker(
                            id='hex-color-picker',
                            color='#1890ff',
                            showAlpha=True,
                            style={
                                'width': '100%',
                                'marginTop': '10px'
                            }
                        ),
                        fac.AntdInput(
                            id='selected-color-input',
                            value='#1890ff',
                            readOnly=True,
                            style={
                                'marginTop': '15px',
                                'background': '#1890ff'
                            }
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdButton(
                                    [
                                        fac.AntdIcon(
                                            icon='antd-save'
                                        ),
                                        '保存配置',
                                    ],
                                    id='save-setting',
                                    type='primary'
                                ),
                                fac.AntdButton(
                                    [
                                        fac.AntdIcon(
                                            icon='antd-sync'
                                        ),
                                        '重置配置',
                                    ],
                                    id='reset-setting',
                                ),
                            ],
                            style={
                                'marginTop': '15px'
                            }
                        )
                    ],
                    id='layout-setting-drawer',
                    visible=False,
                    title='布局设置',
                    width=320
                ),

                # 退出登录对话框提示
                fac.AntdModal(
                    html.Div(
                        [
                            fac.AntdIcon(icon='fc-info', style={'font-size': '28px'}),
                            fac.AntdText('确定注销并退出系统吗？', style={'margin-left': '5px'}),
                        ]
                    ),
                    id='logout-modal',
                    visible=False,
                    title='提示',
                    renderFooter=True,
                    centered=True
                ),

                # 平台主页面
                fac.AntdRow(
                    [
                        # 左侧固定菜单区域
                        fac.AntdCol(
                            fac.AntdAffix(
                                html.Div(
                                    render_aside_content(menu_info),
                                    id='side-menu',
                                    style={
                                        'height': '100vh',
                                        'overflowY': 'auto',
                                        'transition': 'width 1s',
                                        'background': '#001529'
                                    }
                                ),
                            ),
                            id='left-side-menu-container',
                            # style={
                            #     'flex': '1'
                            # }
                        ),

                        # 右侧区域
                        fac.AntdCol(
                            [
                                fac.AntdRow(
                                    render_head_content(),
                                    style={
                                        'height': '50px',
                                        'boxShadow': 'rgb(240 241 242) 0px 2px 14px',
                                        'background': 'white',
                                        'marginBottom': '10px',
                                        'position': 'sticky',
                                        'top': 0,
                                        'zIndex': 999
                                    }
                                ),
                                fac.AntdRow(
                                    render_main_content(),
                                    wrap=False
                                )
                            ],
                            style={
                                'flex': '6',
                                'width': '300px'
                            }
                        ),
                    ],
                )
            ],
            id='index-main-content-container',
        ),
        listenPropsMode='include',
        includeProps=[
            'tabs-container.items'
        ]
    )

dash-fastapi-frontend/views/layout/__init__.py
insistence/Dash-FastAPI-Admin
import feffery_antd_components as fac
import dash

import callbacks.layout_c.aside_c


def render_aside_content(menu_info):

    return [
        fac.AntdSider(
            [
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdImage(
                                width=32,
                                height=32,
                                src=dash.get_asset_url('imgs/logo.png'),
                                preview=False,
                            ),
                            flex='1',
                            style={
                                'height': '100%',
                                'display': 'flex',
                                'alignItems': 'center'
                            }
                        ),
                        fac.AntdCol(
                            fac.AntdText(
                                '后台管理系统',
                                id='logo-text',
                                style={
                                    'fontSize': '22px',
                                    # 'paddingLeft': '20px',
                                    'color': 'rgb(255, 255, 255)'
                                }
                            ),
                            flex='5',
                            style={
                                'height': '100%',
                                'display': 'flex',
                                'alignItems': 'center',
                            }
                        )
                    ],
                    style={
                        'height': '50px',
                        'background': '#001529',
                        'position': 'sticky',
                        'top': 0,
                        'zIndex': 999,
                        'paddingLeft': '10px'
                    }
                ),
                fac.AntdMenu(
                    id='index-side-menu',
                    menuItems=[
                                  {
                                      'component': 'Item',
                                      'props': {
                                          'key': '首页',
                                          'title': '首页',
                                          'icon': 'antd-dashboard',
                                          'href': '/'
                                      }
                                  }
                              ] + menu_info,
                    mode='inline',
                    theme='dark',
                    defaultSelectedKey='首页',
                    defaultOpenKeys=['-1_1'],
                    style={
                        'width': '100%',
                        'height': 'calc(100vh - 50px)'
                    }
                ),
            ],
            id='menu-collapse-sider-custom',
            collapsible=True,
            collapsedWidth=60,
            trigger=None,
            width=210
        ),
    ]

dash-fastapi-frontend/views/layout/components/aside.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac

from views.dashboard import render_dashboard


def render_main_content():
    return [
        # 右侧主体内容区域
        fac.AntdCol(
            [
                html.Div(
                    fac.AntdTabs(
                        items=[
                            {
                                'label': '首页',
                                'key': '首页',
                                'closable': False,
                                'children': render_dashboard(),
                                'contextMenu': [
                                    {
                                        'key': '刷新页面',
                                        'label': '刷新页面',
                                        'icon': 'antd-reload'
                                    },
                                    {
                                        'key': '关闭其他',
                                        'label': '关闭其他',
                                        'icon': 'antd-close-circle'
                                    },
                                    {
                                        'key': '全部关闭',
                                        'label': '全部关闭',
                                        'icon': 'antd-close-circle'
                                    }
                                ]
                            }
                        ],
                        id='tabs-container',
                        type='editable-card',
                        # defaultActiveKey='首页',
                        style={
                            'width': '100%',
                            'paddingLeft': '15px',
                            'paddingRight': '15px'
                        }
                    ),
                    # id='index-main-content-container',
                    style={
                        'width': '100%',
                        'height': '100%',
                        'backgroundColor': 'white',
                    }
                )
            ],
            flex='auto'
        )
    ]

dash-fastapi-frontend/views/layout/components/content.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac
from flask import session
import callbacks.layout_c.head_c


def render_head_content():
    return [
        # 页首左侧折叠按钮区域
        fac.AntdCol(
            html.Div(
                fac.AntdButton(
                    fac.AntdIcon(
                        id='fold-side-menu-icon',
                        icon='antd-menu-fold'
                    ),
                    id='fold-side-menu',
                    type='text',
                    shape='circle',
                    size='large',
                    style={
                        'marginLeft': '5px',
                        'background': 'white'
                    }
                ),
                style={
                    'height': '100%',
                    'display': 'flex',
                    'alignItems': 'center'
                }
            ),
            flex='1'
        ),

        # 页首面包屑区域
        fac.AntdCol(
            fac.AntdBreadcrumb(
                items=[
                    {
                        'title': '首页',
                        'icon': 'antd-dashboard',
                        'href': '/#'
                    }
                ],
                id='header-breadcrumb'
            ),
            style={
                'height': '100%',
                'display': 'flex',
                'alignItems': 'center'
            },
            flex='24'
        ),

        # 页首中部搜索区域
        fac.AntdCol(
            fac.AntdParagraph(
                [
                    fac.AntdText(
                        'Ctrl',
                        keyboard=True,
                        style={
                            'color': '#8c8c8c'
                        }
                    ),
                    fac.AntdText(
                        'K',
                        keyboard=True,
                        style={
                            'color': '#8c8c8c'
                        }
                    ),
                    fac.AntdText(
                        '唤出搜索面板',
                        style={
                            'color': '#8c8c8c'
                        }
                    )
                ],
                style={
                    'height': '100%',
                    'display': 'flex',
                    'alignItems': 'center'
                }
            ),
            flex='6'
        ),

        # 页首右侧用户信息区域
        fac.AntdCol(
            fac.AntdSpace(
                [
                    fac.AntdPopover(
                        fac.AntdBadge(
                            fac.AntdAvatar(
                                id='avatar-info',
                                mode='image',
                                src=session.get('user_info').get('avatar'),
                                size=36
                            ),
                            count=6,
                            size='small'
                        ),
                        content=fac.AntdTabs(
                            items=[
                                {
                                    'key': '未读消息',
                                    'label': '未读消息',
                                    'children': [
                                        fac.AntdSpace(
                                            [
                                                html.Div(
                                                    fac.AntdText(
                                                        f'消息示例{i}'
                                                    ),
                                                    style={
                                                        'padding': '5px 10px',
                                                        'height': 40,
                                                        'width': 300,
                                                        'borderBottom': '1px solid #f1f3f5'
                                                    }
                                                )
                                                for i in range(1, 8)
                                            ],
                                            direction='vertical',
                                            style={
                                                'height': 280,
                                                'overflowY': 'auto'
                                            }
                                        )
                                    ]
                                },
                                {
                                    'key': '已读消息',
                                    'label': '已读消息',
                                    'children': [
                                        fac.AntdSpace(
                                            [
                                                html.Div(
                                                    fac.AntdText(
                                                        f'消息示例{i}'
                                                    ),
                                                    style={
                                                        'padding': '5px 10px',
                                                        'height': 40,
                                                        'width': 300,
                                                        'borderBottom': '1px solid #f1f3f5'
                                                    }
                                                )
                                                for i in range(8, 15)
                                            ],
                                            direction='vertical',
                                            style={
                                                'height': 280,
                                                'overflowY': 'auto'
                                            }
                                        )
                                    ]
                                },
                            ],
                            centered=True
                        ),
                        placement='bottomRight'
                    ),

                    fac.AntdDropdown(
                        id='index-header-dropdown',
                        title=session.get('user_info').get('user_name'),
                        arrow=True,
                        menuItems=[
                            {
                                'title': '个人资料',
                                'key': '个人资料',
                                'icon': 'antd-idcard'
                            },
                            {
                                'title': '布局设置',
                                'key': '布局设置',
                                'icon': 'antd-layout'
                            },
                            {
                                'isDivider': True
                            },
                            {
                                'title': '退出登录',
                                'key': '退出登录',
                                'icon': 'antd-logout'
                            },
                        ],
                        placement='bottomRight',
                        overlayStyle={
                            'width': '100px'
                        }
                    )
                ],
                style={
                    'height': '100%',
                    'float': 'right',
                    'display': 'flex',
                    'alignItems': 'center'
                }
            ),
            flex='3'
        ),
        fac.AntdCol(
            # 全局刷新按钮
            html.Div(
                fac.AntdTooltip(
                    fac.AntdButton(
                        fac.AntdIcon(
                            id='index-reload-icon',
                            icon='fc-synchronize'
                        ),
                        id='index-reload',
                        type='text',
                        shape='circle',
                        size='large',
                        style={
                            'backgroundColor': 'rgb(255 255 255 / 0%)',
                        }
                    ),
                    title='刷新',
                    placement='bottom'
                )
            ),
            style={
                'height': '100%',
                'paddingRight': '3px',
                'display': 'flex',
                'alignItems': 'center'
            },
            flex='1'
        ),
    ]

dash-fastapi-frontend/views/layout/components/head.py
insistence/Dash-FastAPI-Admin
from dash import html, dcc
import feffery_antd_components as fac

import callbacks.login_c
from api.config import query_config_list_api


def render_content():
    captcha_enabled_info = query_config_list_api(config_key='sys.account.captchaEnabled')
    forget_enabled_info = query_config_list_api(config_key='sys.account.forgetUser')
    captcha_hidden = False
    forget_show = True
    if captcha_enabled_info.get('code') == 200:
        captcha_hidden = False if captcha_enabled_info.get('data') == 'true' else True
    if forget_enabled_info.get('code') == 200:
        forget_show = False if forget_enabled_info.get('data') == 'false' else True

    return html.Div(
        [
            dcc.Store(id='captcha_image-session_id-container'),
            html.Div(
                [
                    html.Div(
                        [
                            fac.AntdText('HELLO', style={'color': 'rgba(255,255,255,0.8)'})
                        ],
                        style={
                            'fontSize': '60px',
                            'fontWeight': '500'
                        }
                    ),
                    html.Div(
                        [
                            fac.AntdText('WELCOME', style={'color': 'rgba(255,255,255,0.8)'}),
                        ],
                        style={
                            'fontSize': '60px',
                            'fontWeight': '500'
                        }
                    ),
                    html.Div(
                        [
                            fac.AntdText('欢迎使用通用后台管理系统', style={'color': 'rgba(255,255,255,0.8)'}),
                        ],
                        style={
                            'fontSize': '18px',
                            'fontWeight': '600',
                            'marginTop': '20px'
                        }
                    ),
                ],
                style={
                    'position': 'fixed',
                    'top': '20%',
                    'left': '26%',
                    'width': '430px',
                    'padding': '0px 30px',
                    'transform': 'translateX(-50%)'
                }
            ),
            fac.AntdCard(
                [
                    fac.AntdForm(
                        [
                            fac.AntdFormItem(
                                fac.AntdInput(
                                    placeholder='请输入用户名',
                                    id='login-username',
                                    size='large',
                                    prefix=fac.AntdIcon(
                                        icon='antd-user'
                                    ),
                                ),
                                id='login-username-form-item'
                            ),
                            fac.AntdFormItem(
                                fac.AntdInput(
                                    placeholder='请输入密码',
                                    id='login-password',
                                    mode='password',
                                    passwordUseMd5=True,
                                    size='large',
                                    prefix=fac.AntdIcon(
                                        icon='antd-lock'
                                    ),
                                ),
                                id='login-password-form-item'
                            ),
                            html.Div(
                                [
                                    fac.AntdSpace(
                                        [
                                            fac.AntdFormItem(
                                                fac.AntdInput(
                                                    placeholder='请输入验证码',
                                                    id='login-captcha',
                                                    size='large',
                                                    prefix=fac.AntdIcon(
                                                        icon='antd-check-circle'
                                                    ),
                                                    style={
                                                        'width': '210px'
                                                    }
                                                ),
                                                id='login-captcha-form-item'
                                            ),
                                            fac.AntdFormItem(
                                                html.Div(
                                                    [
                                                        fac.AntdImage(
                                                            id='login-captcha-image',
                                                            src='',
                                                            height=37,
                                                            width=100,
                                                            preview=False
                                                        )
                                                    ],
                                                    id='login-captcha-image-container',
                                                    n_clicks=1,
                                                    style={
                                                        'border': '1px solid #ccc'
                                                    }
                                                )
                                            )
                                        ],
                                        align='end',
                                        size=10
                                    ),
                                ],
                                id='captcha-row-container',
                                hidden=captcha_hidden
                            ),
                            fac.AntdSpace(
                                [
                                    html.Div(id='test'),
                                    fac.AntdButton(
                                        '忘记密码',
                                        id='forget-password-link',
                                        type='link',
                                        href='/forget',
                                        target='_self'
                                    )
                                ],
                                align='center',
                                size=240
                            ) if forget_show else [],
                            fac.AntdFormItem(
                                fac.AntdButton(
                                    '登录',
                                    id='login-submit',
                                    type='primary',
                                    loadingChildren='登录中',
                                    autoSpin=True,
                                    block=True,
                                    size='large',
                                ),
                                style={
                                    'marginTop': '20px'
                                }
                            )
                        ],
                        layout='vertical',
                        style={
                            'width': '100%'
                        }
                    ),
                ],
                id='login-form-container',
                title='登录',
                hoverable=True,
                style={
                    'position': 'fixed',
                    'top': '16%',
                    'left': '70%',
                    'width': '430px',
                    'padding': '0px 30px',
                    'transform': 'translateX(-50%)'
                }
            ),
            fac.AntdFooter(
                html.Div(
                    fac.AntdText(
                        '版权所有©2023 Dash-FastAPI-Admin',
                        style={
                            'margin': '0'
                        }
                    ),
                    style={
                        'display': 'flex',
                        'height': '100%',
                        'justifyContent': 'center',
                        'alignItems': 'center'
                    }
                ),
                style={
                    'backgroundColor': 'rgb(255 255 255 / 0%)',
                    'height': '40px',
                    'position': 'fixed',
                    'bottom': 0,
                    'left': '50%',
                    'width': '500px',
                    'padding': '20px 50px',
                    'transform': 'translateX(-50%)'
                }
            ),
        ],
        id='container',
        style={
            'height': '100vh',
        }
    )

dash-fastapi-frontend/views/login.py
insistence/Dash-FastAPI-Admin
from . import (
    online,
    job,
    druid,
    server,
    cache,
    operlog,
    logininfor
)

dash-fastapi-frontend/views/monitor/__init__.py
insistence/Dash-FastAPI-Admin
from . import (
    control,
    list
)

dash-fastapi-frontend/views/monitor/cache/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html, dcc
import feffery_antd_components as fac

import callbacks.monitor_c.cache_c.control_c
from api.cache import get_cache_statistical_info_api


def render(button_perms):
    command_stats = []
    db_size = ''
    info = {}
    cache_info_res = get_cache_statistical_info_api()
    if cache_info_res.get('code') == 200:
        cache_info = cache_info_res.get('data')
        command_stats = cache_info.get('command_stats')
        db_size = cache_info.get('db_size')
        info = cache_info.get('info')

    return [
        dcc.Store(id='control-button-perms-container', data=button_perms),
        dcc.Store(id='init-echarts-data-container', data=dict(command_stats=command_stats, used_memory_human=info.get('used_memory_human'))),
        dcc.Store(id='echarts-data-container'),
        dcc.Interval(
            id='init-echarts-interval',
            n_intervals=0,
            interval=500,
            disabled=False
        ),
        html.Div(
            [
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    fac.AntdDescriptions(
                                        [
                                            fac.AntdDescriptionItem(
                                                info.get('redis_version'),
                                                label='Redis版本'
                                            ),
                                            fac.AntdDescriptionItem(
                                                '单机'if info.get('redis_mode') == 'standalone' else '集群',
                                                label='运行模式'
                                            ),
                                            fac.AntdDescriptionItem(
                                                info.get('tcp_port'),
                                                label='端口'
                                            ),
                                            fac.AntdDescriptionItem(
                                                info.get('connected_clients'),
                                                label='客户端数'
                                            ),
                                            fac.AntdDescriptionItem(
                                                info.get('uptime_in_days'),
                                                label='运行时间(天)'
                                            ),
                                            fac.AntdDescriptionItem(
                                                info.get('used_memory_human'),
                                                label='使用内存'
                                            ),
                                            fac.AntdDescriptionItem(
                                                info.get('used_cpu_user_children'),
                                                label='使用CPU'
                                            ),
                                            fac.AntdDescriptionItem(
                                                info.get('maxmemory_human'),
                                                label='内存配置'
                                            ),
                                            fac.AntdDescriptionItem(
                                                '否' if info.get('aof_enabled') == 0 else '是',
                                                label='AOF是否开启'
                                            ),
                                            fac.AntdDescriptionItem(
                                                info.get('rdb_last_bgsave_status'),
                                                label='RDB是否成功'
                                            ),
                                            fac.AntdDescriptionItem(
                                                db_size,
                                                label='Key数量'
                                            ),
                                            fac.AntdDescriptionItem(
                                                f"{info.get('instantaneous_input_kbps')}kps/{info.get('instantaneous_output_kbps')}kps",
                                                label='网络入口/出口'
                                            ),
                                        ],
                                        bordered=True,
                                        column=4,
                                        labelStyle={
                                            'fontWeight': 600,
                                            'textAlign': 'center'
                                        },
                                        style={
                                            'width': '100%',
                                            'textAlign': 'center',
                                            'marginLeft': '20px',
                                            'marginRight': '20px'
                                        }
                                    )
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-desktop'),
                                        fac.AntdText('基本信息', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=24
                        ),
                    ],
                    gutter=20
                ),
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    html.Div(
                                        id='command-stats-charts-container',
                                        style={
                                            'height': '420px',
                                            'width': '100%'
                                        }
                                    ),
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-pie-chart'),
                                        fac.AntdText('命令统计', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=12
                        ),
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    html.Div(
                                        id='memory-charts-container',
                                        style={
                                            'height': '420px',
                                            'width': '100%'
                                        }
                                    ),
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-compass'),
                                        fac.AntdText('内存信息', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=12
                        ),
                    ],
                    gutter=20,
                    style={
                        'marginTop': '20px',
                        'marginBottom': '20px'
                    }
                ),
            ],
        )
    ]

dash-fastapi-frontend/views/monitor/cache/control/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_utils_components as fuc
import feffery_antd_components as fac


def render(button_perms):

    return html.Div('我是缓存列表')

dash-fastapi-frontend/views/monitor/cache/list/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_utils_components as fuc
import feffery_antd_components as fac


def render(button_perms):

    return html.Div('我是数据监控')

dash-fastapi-frontend/views/monitor/druid/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac
import json

import callbacks.monitor_c.job_c.job_c
from . import job_log
from api.job import get_job_list_api
from api.dict import query_dict_data_list_api


def render(button_perms):

    option = []
    option_table = []
    info = query_dict_data_list_api(dict_type='sys_job_group')
    if info.get('code') == 200:
        data = info.get('data')
        option = [dict(label=item.get('dict_label'), value=item.get('dict_value')) for item in data]
        option_table = [
            dict(label=item.get('dict_label'), value=item.get('dict_value'), css_class=item.get('css_class')) for item
            in data]
    option_dict = {item.get('value'): item for item in option_table}

    job_params = dict(page_num=1, page_size=10)
    table_info = get_job_list_api(job_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        total = table_info['data']['total']
        for item in table_data:
            if item['status'] == '0':
                item['status'] = dict(checked=True)
            else:
                item['status'] = dict(checked=False)
            if str(item.get('job_group')) in option_dict.keys():
                item['job_group'] = dict(
                    tag=option_dict.get(str(item.get('job_group'))).get('label'),
                    color=json.loads(option_dict.get(str(item.get('job_group'))).get('css_class')).get('color')
                )
            item['key'] = str(item['job_id'])
            item['operation'] = [
                {
                    'title': '修改',
                    'icon': 'antd-edit'
                } if 'monitor:job:edit' in button_perms else None,
                {
                    'title': '删除',
                    'icon': 'antd-delete'
                } if 'monitor:job:remove' in button_perms else None,
                {
                    'title': '执行一次',
                    'icon': 'antd-rocket'
                } if 'monitor:job:changeStatus' in button_perms else None,
                {
                    'title': '任务详细',
                    'icon': 'antd-eye'
                } if 'monitor:job:query' in button_perms else None,
                {
                    'title': '调度日志',
                    'icon': 'antd-history'
                } if 'monitor:job:query' in button_perms else None
            ]

    return [
        dcc.Store(id='job-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='job-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='job-export-container'),
        # 定时任务模块操作类型存储容器
        dcc.Store(id='job-operations-store'),
        dcc.Store(id='job-operations-store-bk'),
        # 定时任务模块修改操作行key存储容器
        dcc.Store(id='job-edit-id-store'),
        # 定时任务模块删除操作行key存储容器
        dcc.Store(id='job-delete-ids-store'),
        # 定时任务日志管理模块操作类型存储容器
        dcc.Store(id='job_log-operations-store'),
        # 定时任务日志管理模块删除操作行key存储容器
        dcc.Store(id='job_log-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='job-job_name-input',
                                                                    placeholder='请输入任务名称',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 210
                                                                    }
                                                                ),
                                                                label='任务名称'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdSelect(
                                                                    id='job-job_group-select',
                                                                    placeholder='请选择任务组名',
                                                                    options=option,
                                                                    style={
                                                                        'width': 210
                                                                    }
                                                                ),
                                                                label='任务组名'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdSelect(
                                                                    id='job-status-select',
                                                                    placeholder='请选择任务状态',
                                                                    options=[
                                                                        {
                                                                            'label': '正常',
                                                                            'value': '0'
                                                                        },
                                                                        {
                                                                            'label': '暂停',
                                                                            'value': '1'
                                                                        }
                                                                    ],
                                                                    style={
                                                                        'width': 200
                                                                    }
                                                                ),
                                                                label='任务状态'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '搜索',
                                                                    id='job-search',
                                                                    type='primary',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-search'
                                                                    )
                                                                )
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '重置',
                                                                    id='job-reset',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-sync'
                                                                    )
                                                                )
                                                            )
                                                        ],
                                                        style={
                                                            'paddingBottom': '10px'
                                                        }
                                                    ),
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='job-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'job-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'monitor:job:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'job-operation-button',
                                                    'index': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0'
                                                }
                                            ) if 'monitor:job:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'job-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:job:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='job-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'monitor:job:export' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-history'
                                                    ),
                                                    '调度日志',
                                                ],
                                                id={
                                                    'type': 'job-operation-log',
                                                    'index': 'log'
                                                },
                                                style={
                                                    'color': '#909399',
                                                    'background': '#f4f4f5',
                                                    'border-color': '#d3d4d6'
                                                }
                                            ) if 'monitor:job:query' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='job-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='job-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='job-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='job-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'job_id',
                                                    'title': '任务编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'job_name',
                                                    'title': '任务名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'job_group',
                                                    'title': '任务组名',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'invoke_target',
                                                    'title': '调用目标字符串',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'cron_expression',
                                                    'title': 'cron执行表达式',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'renderOptions': {
                                                        'renderType': 'switch'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'dropdown',
                                                        'dropdownProps': {
                                                            'title': '更多'
                                                        }
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑定时任务表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'job_name'
                                            },
                                            placeholder='请输入任务名称',
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'job_name',
                                            'required': True
                                        },
                                        required=True,
                                        label='任务名称',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdSelect(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'job_group'
                                            },
                                            placeholder='请选择任务分组',
                                            options=option,
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'job_group',
                                            'required': False
                                        },
                                        label='任务分组',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'invoke_target'
                                            },
                                            placeholder='请输入调用目标字符串',
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'invoke_target',
                                            'required': True
                                        },
                                        required=True,
                                        label='调用方法',
                                        labelCol={
                                            'span': 3
                                        },
                                        wrapperCol={
                                            'span': 21
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'job_args'
                                            },
                                            placeholder='请输入位置参数',
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'job_args',
                                            'required': False
                                        },
                                        label='位置参数',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'job_kwargs'
                                            },
                                            placeholder='请输入关键字参数',
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'job_kwargs',
                                            'required': False
                                        },
                                        label='关键字参数',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'cron_expression'
                                            },
                                            placeholder='请输入cron执行表达式',
                                            addonAfter=html.Div(
                                                [
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdText('生成表达式'),
                                                            fac.AntdIcon(icon='antd-field-time')
                                                        ]
                                                    )
                                                ],
                                                id='generate-expression'
                                            ),
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'cron_expression',
                                            'required': True
                                        },
                                        required=True,
                                        label='cron表达式',
                                        labelCol={
                                            'span': 3
                                        },
                                        wrapperCol={
                                            'span': 21
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'misfire_policy'
                                            },
                                            options=[
                                                {
                                                    'label': '立即执行',
                                                    'value': '1'
                                                },
                                                {
                                                    'label': '执行一次',
                                                    'value': '2'
                                                },
                                                {
                                                    'label': '放弃执行',
                                                    'value': '3'
                                                }
                                            ],
                                            defaultValue='1',
                                            optionType='button',
                                            buttonStyle='solid'
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'misfire_policy',
                                            'required': False
                                        },
                                        label='执行策略',
                                        labelCol={
                                            'span': 3
                                        },
                                        wrapperCol={
                                            'span': 21
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'concurrent'
                                            },
                                            options=[
                                                {
                                                    'label': '允许',
                                                    'value': '0'
                                                },
                                                {
                                                    'label': '禁止',
                                                    'value': '1'
                                                }
                                            ],
                                            defaultValue='1',
                                            optionType='button',
                                            buttonStyle='solid'
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'concurrent',
                                            'required': False
                                        },
                                        label='是否并发',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id={
                                                'type': 'job-form-value',
                                                'index': 'status'
                                            },
                                            options=[
                                                {
                                                    'label': '正常',
                                                    'value': '0'
                                                },
                                                {
                                                    'label': '暂停',
                                                    'value': '1'
                                                },
                                            ],
                                            defaultValue='0',
                                        ),
                                        id={
                                            'type': 'job-form-label',
                                            'index': 'status',
                                            'required': False
                                        },
                                        label='状态',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        )
                    ],
                    style={
                        'marginRight': '30px'
                    }
                )
            ],
            id='job-modal',
            mask=False,
            width=850,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除定时任务二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='job-delete-text'),
            id='job-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),

        # 任务调度日志modal
        fac.AntdModal(
            job_log.render(button_perms),
            id='job_to_job_log-modal',
            mask=False,
            maskClosable=False,
            width=1050,
            renderFooter=False,
            okClickClose=False
        ),

        # 任务明细modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'job_name'
                                            }
                                        ),
                                        label='任务名称',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'job_name'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'job_group'
                                            }
                                        ),
                                        label='任务分组',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'job_group'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'job_executor'
                                            }
                                        ),
                                        label='任务执行器',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'job_executor'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'invoke_target'
                                            }
                                        ),
                                        label='调用目标函数',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'invoke_target'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'job_args'
                                            }
                                        ),
                                        label='位置参数',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'job_args'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'job_kwargs'
                                            }
                                        ),
                                        label='关键字参数',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'job_kwargs'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'cron_expression'
                                            }
                                        ),
                                        label='cron表达式',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'cron_expression'
                                        },
                                        labelCol={
                                            'span': 4
                                        },
                                        wrapperCol={
                                            'span': 20
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'misfire_policy'
                                            }
                                        ),
                                        label='执行策略',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'misfire_policy'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'concurrent'
                                            }
                                        ),
                                        label='是否并发',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'concurrent'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'status'
                                            }
                                        ),
                                        label='任务状态',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'status'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_detail-form-value',
                                                'index': 'create_time'
                                            }
                                        ),
                                        label='创建时间',
                                        required=True,
                                        id={
                                            'type': 'job_detail-form-label',
                                            'index': 'create_time'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                        ),
                    ],
                    labelCol={
                        'span': 8
                    },
                    wrapperCol={
                        'span': 16
                    },
                    style={
                        'marginRight': '15px'
                    }
                )
            ],
            id='job_detail-modal',
            mask=False,
            width=850,
            renderFooter=False,
        ),
    ]

dash-fastapi-frontend/views/monitor/job/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.monitor_c.job_c.job_log_c


def render(button_perms):
    return [
        dcc.Store(id='job_log-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='job_log-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='job_log-export-container'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='job_log-job_name-input',
                                                            placeholder='请输入任务名称',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='任务名称',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='job_log-job_group-select',
                                                            placeholder='请选择任务组名',
                                                            options=[],
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='任务组名',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='job_log-status-select',
                                                            placeholder='请选择执行状态',
                                                            options=[
                                                                {
                                                                    'label': '成功',
                                                                    'value': '0'
                                                                },
                                                                {
                                                                    'label': '失败',
                                                                    'value': '1'
                                                                }
                                                            ],
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='执行状态',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdDateRangePicker(
                                                            id='job_log-create_time-range',
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='执行时间',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='job_log-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='job_log-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='job_log-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-delete'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'job_log-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:job:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-clear'
                                                    ),
                                                    '清空',
                                                ],
                                                id={
                                                    'type': 'job_log-operation-button',
                                                    'index': 'clear'
                                                },
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:job:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='job_log-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'monitor:job:export' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='job_log-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='job_log-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='job_log-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='job_log-list-table',
                                            data=[],
                                            columns=[
                                                {
                                                    'dataIndex': 'job_log_id',
                                                    'title': '日志编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'job_name',
                                                    'title': '任务名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'job_group',
                                                    'title': '任务组名',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'invoke_target',
                                                    'title': '调用目标字符串',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'job_message',
                                                    'title': '日志信息',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '执行状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '执行时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            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
        ),

        # 任务调度日志明细modal，表单项id使用字典类型，index与后端数据库字段一一对应
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'job_name'
                                            }
                                        ),
                                        label='任务名称',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'job_name'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'job_group'
                                            }
                                        ),
                                        label='任务分组',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'job_group'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'job_executor'
                                            }
                                        ),
                                        label='任务执行器',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'job_executor'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'invoke_target'
                                            }
                                        ),
                                        label='调用目标字符串',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'invoke_target'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'job_args'
                                            }
                                        ),
                                        label='位置参数',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'job_args'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'job_kwargs'
                                            }
                                        ),
                                        label='关键字参数',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'job_kwargs'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'job_trigger'
                                            }
                                        ),
                                        label='任务触发器',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'job_trigger'
                                        },
                                        labelCol={
                                            'span': 4
                                        },
                                        wrapperCol={
                                            'span': 20
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'job_message'
                                            }
                                        ),
                                        label='日志信息',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'job_message'
                                        },
                                        labelCol={
                                            'span': 4
                                        },
                                        wrapperCol={
                                            'span': 20
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'status'
                                            }
                                        ),
                                        label='执行状态',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'status'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'create_time'
                                            }
                                        ),
                                        label='执行时间',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'create_time'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'job_log-form-value',
                                                'index': 'exception_info'
                                            }
                                        ),
                                        label='异常信息',
                                        required=True,
                                        id={
                                            'type': 'job_log-form-label',
                                            'index': 'exception_info'
                                        },
                                        labelCol={
                                            'span': 4
                                        },
                                        wrapperCol={
                                            'span': 20
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                        ),
                    ],
                    labelCol={
                        'span': 8
                    },
                    wrapperCol={
                        'span': 16
                    },
                    style={
                        'marginRight': '15px'
                    }
                )
            ],
            id='job_log-modal',
            mask=False,
            width=850,
            renderFooter=False,
        ),

        # 删除任务调度日志二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='job_log-delete-text'),
            id='job_log-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/monitor/job/job_log.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.monitor_c.logininfor_c
from api.log import get_login_log_list_api


def render(button_perms):

    login_log_params = dict(page_num=1, page_size=10)
    table_info = get_login_log_list_api(login_log_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        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['info_id'])

    return [
        dcc.Store(id='login_log-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='login_log-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='login_log-export-container'),
        # 登录日志管理模块操作类型存储容器
        dcc.Store(id='login_log-operations-store'),
        # 登录日志管理模块删除操作行key存储容器
        dcc.Store(id='login_log-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='login_log-ipaddr-input',
                                                            placeholder='请输入登录地址',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='登录地址',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='login_log-user_name-input',
                                                            placeholder='请输入用户名称',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='用户名称',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='login_log-status-select',
                                                            placeholder='登录状态',
                                                            options=[
                                                                {
                                                                    'label': '成功',
                                                                    'value': 0
                                                                },
                                                                {
                                                                    'label': '失败',
                                                                    'value': 1
                                                                }
                                                            ],
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='状态',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdDateRangePicker(
                                                            id='login_log-login_time-range',
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='登录时间',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='login_log-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='login_log-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='login_log-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-delete'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'login_log-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:logininfor:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-clear'
                                                    ),
                                                    '清空',
                                                ],
                                                id={
                                                    'type': 'login_log-operation-button',
                                                    'index': 'clear'
                                                },
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:logininfor:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-unlock'
                                                    ),
                                                    '解锁',
                                                ],
                                                id='login_log-unlock',
                                                disabled=True,
                                                style={
                                                    'color': '#74bcff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#d1e9ff'
                                                }
                                            ) if 'monitor:logininfor:unlock' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='login_log-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'monitor:logininfor:export' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='login_log-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='login_log-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='login_log-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='login_log-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'info_id',
                                                    'title': '访问编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'user_name',
                                                    'title': '用户名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'ipaddr',
                                                    'title': '登录地址',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'login_location',
                                                    'title': '登录地点',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'browser',
                                                    'title': '浏览器',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'os',
                                                    'title': '操作系统',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '登录状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'msg',
                                                    'title': '操作信息',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'login_time',
                                                    'title': '登录日期',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 删除操作日志二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='login_log-delete-text'),
            id='login_log-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/monitor/logininfor/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.monitor_c.online_c
from api.online import get_online_list_api


def render(button_perms):

    online_params = dict(page_num=1, page_size=10)
    table_info = get_online_list_api(online_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        total = table_info['data']['total']
        for item in table_data:
            item['key'] = str(item['session_id'])
            item['operation'] = [
                {
                    'content': '强退',
                    'type': 'link',
                    'icon': 'antd-delete'
                } if 'monitor:online:forceLogout' in button_perms else {},
            ]

    return [
        dcc.Store(id='online-button-perms-container', data=button_perms),
        # 在线用户模块操作类型存储容器
        dcc.Store(id='online-operations-store'),
        dcc.Store(id='online-operations-store-bk'),
        # 在线用户模块删除操作行key存储容器
        dcc.Store(id='online-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='online-ipaddr-input',
                                                                    placeholder='请输入登录地址',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 210
                                                                    }
                                                                ),
                                                                label='登录地址'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='online-user_name-input',
                                                                    placeholder='请输入用户名称',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 210
                                                                    }
                                                                ),
                                                                label='用户名称'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '搜索',
                                                                    id='online-search',
                                                                    type='primary',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-search'
                                                                    )
                                                                )
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '重置',
                                                                    id='online-reset',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-sync'
                                                                    )
                                                                )
                                                            )
                                                        ],
                                                        style={
                                                            'paddingBottom': '10px'
                                                        }
                                                    ),
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='online-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-delete'
                                                    ),
                                                    '批量强退',
                                                ],
                                                id={
                                                    'type': 'online-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:online:batchLogout' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='online-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='online-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='online-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='online-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'session_id',
                                                    'title': '会话编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'user_name',
                                                    'title': '登录名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'dept_name',
                                                    'title': '部门名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'ipaddr',
                                                    'title': '主机',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'login_location',
                                                    'title': '登录地点',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'browser',
                                                    'title': '浏览器',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'os',
                                                    'title': '操作系统',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'login_time',
                                                    'title': '登录时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 强退会话二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认强退？', id='online-delete-text'),
            id='online-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/monitor/online/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac
import json

import callbacks.monitor_c.operlog_c
from api.log import get_operation_log_list_api
from api.dict import query_dict_data_list_api


def render(button_perms):

    option = []
    option_table = []
    info = query_dict_data_list_api(dict_type='sys_oper_type')
    if info.get('code') == 200:
        data = info.get('data')
        option = [dict(label=item.get('dict_label'), value=item.get('dict_value')) for item in data]
        option_table = [
            dict(label=item.get('dict_label'), value=item.get('dict_value'), css_class=item.get('css_class')) for item
            in data]
    option_dict = {item.get('value'): item for item in option_table}

    operation_log_params = dict(page_num=1, page_size=10)
    table_info = get_operation_log_list_api(operation_log_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        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')
            if str(item.get('business_type')) in option_dict.keys():
                item['business_type'] = dict(
                    tag=option_dict.get(str(item.get('business_type'))).get('label'),
                    color=json.loads(option_dict.get(str(item.get('business_type'))).get('css_class')).get('color')
                )
            item['key'] = str(item['oper_id'])
            item['cost_time'] = f"{item['cost_time']}毫秒"
            item['operation'] = [
                {
                    'content': '详情',
                    'type': 'link',
                    'icon': 'antd-eye'
                } if 'monitor:operlog:query' in button_perms else {},
            ]

    return [
        dcc.Store(id='operation_log-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='operation_log-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='operation_log-export-container'),
        # 操作日志管理模块操作类型存储容器
        dcc.Store(id='operation_log-operations-store'),
        # 操作日志管理模块删除操作行key存储容器
        dcc.Store(id='operation_log-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='operation_log-title-input',
                                                            placeholder='请输入系统模块',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='系统模块',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='operation_log-oper_name-input',
                                                            placeholder='请输入操作人员',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='操作人员',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='operation_log-business_type-select',
                                                            placeholder='操作类型',
                                                            options=option,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='类型',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='operation_log-status-select',
                                                            placeholder='操作状态',
                                                            options=[
                                                                {
                                                                    'label': '成功',
                                                                    'value': 0
                                                                },
                                                                {
                                                                    'label': '失败',
                                                                    'value': 1
                                                                }
                                                            ],
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='状态',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdDateRangePicker(
                                                            id='operation_log-oper_time-range',
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='操作时间',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='operation_log-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='operation_log-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='operation_log-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-delete'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'operation_log-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:operlog:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-clear'
                                                    ),
                                                    '清空',
                                                ],
                                                id={
                                                    'type': 'operation_log-operation-button',
                                                    'index': 'clear'
                                                },
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'monitor:operlog:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='operation_log-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'monitor:operlog:export' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='operation_log-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='operation_log-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='operation_log-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='operation_log-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'oper_id',
                                                    'title': '日志编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'title',
                                                    'title': '系统模块',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'business_type',
                                                    'title': '操作类型',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'oper_name',
                                                    'title': '操作人员',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'oper_ip',
                                                    'title': '操作地址',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'oper_location',
                                                    'title': '操作地点',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '操作状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'oper_time',
                                                    'title': '操作日期',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'cost_time',
                                                    'title': '消耗时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 操作日志明细modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'title'
                                            }
                                        ),
                                        label='操作模块',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'title'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'oper_url'
                                            }
                                        ),
                                        label='请求地址',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'oper_url'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'login_info'
                                            }
                                        ),
                                        label='登录信息',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'login_info'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'request_method'
                                            }
                                        ),
                                        label='请求方式',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'request_method'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'method'
                                            }
                                        ),
                                        label='操作方法',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'method'
                                        },
                                        labelCol={
                                            'span': 4
                                        },
                                        wrapperCol={
                                            'span': 20
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'oper_param'
                                            }
                                        ),
                                        label='请求参数',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'oper_param'
                                        },
                                        labelCol={
                                            'span': 4
                                        },
                                        wrapperCol={
                                            'span': 20
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'json_result'
                                            }
                                        ),
                                        label='返回参数',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'json_result'
                                        },
                                        labelCol={
                                            'span': 4
                                        },
                                        wrapperCol={
                                            'span': 20
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'status'
                                            }
                                        ),
                                        label='操作状态',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'status'
                                        },
                                        labelCol={
                                            'span': 12
                                        },
                                        wrapperCol={
                                            'span': 12
                                        }
                                    ),
                                    span=8
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'cost_time'
                                            }
                                        ),
                                        label='消耗时间',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'cost_time'
                                        },
                                        labelCol={
                                            'span': 12
                                        },
                                        wrapperCol={
                                            'span': 12
                                        }
                                    ),
                                    span=6
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdText(
                                            id={
                                                'type': 'operation_log-form-value',
                                                'index': 'oper_time'
                                            }
                                        ),
                                        label='操作时间',
                                        required=True,
                                        id={
                                            'type': 'operation_log-form-label',
                                            'index': 'oper_time'
                                        },
                                        labelCol={
                                            'span': 8
                                        },
                                        wrapperCol={
                                            'span': 16
                                        }
                                    ),
                                    span=10
                                ),
                            ],
                            gutter=5
                        ),
                    ],
                    labelCol={
                        'span': 8
                    },
                    wrapperCol={
                        'span': 16
                    },
                    style={
                        'marginRight': '15px'
                    }
                )
            ],
            id='operation_log-modal',
            mask=False,
            width=850,
            renderFooter=False,
        ),

        # 删除操作日志二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='operation_log-delete-text'),
            id='operation_log-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/monitor/operlog/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html, dcc
import feffery_antd_components as fac

from api.server import get_server_statistical_info_api


def render(button_perms):
    cpu = {}
    mem = {}
    sys = {}
    py = {}
    sys_files = []
    server_info_res = get_server_statistical_info_api()
    if server_info_res.get('code') == 200:
        server_info = server_info_res.get('data')
        cpu = [dict(name=key, value=value) for key, value in server_info.get('cpu').items()]
        for item in cpu:
            if item.get('name') == 'cpu_num':
                item['name'] = '核心数'
            if item.get('name') == 'used':
                item['name'] = '用户使用率'
            if item.get('name') == 'sys':
                item['name'] = '系统使用率'
            if item.get('name') == 'free':
                item['name'] = '当前空闲率'
        mem = [dict(name=key, value=value) for key, value in server_info.get('mem').items()]
        for item in mem:
            if item.get('name') == 'total':
                item['name'] = '总内存'
            if item.get('name') == 'used':
                item['name'] = '已用内存'
            if item.get('name') == 'free':
                item['name'] = '剩余内存'
            if item.get('name') == 'usage':
                item['name'] = '使用率'
        sys = server_info.get('sys')
        py = server_info.get('py')
        sys_files = server_info.get('sys_files')

    return [
        dcc.Store(id='server-button-perms-container', data=button_perms),
        html.Div(
            [
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    fac.AntdTable(
                                        data=cpu,
                                        columns=[
                                            {
                                                'dataIndex': 'name',
                                                'title': '属性',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'value',
                                                'title': '值',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                        ],
                                        bordered=False,
                                        pagination={
                                            'hideOnSinglePage': True
                                        }
                                    )
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-box-plot'),
                                        fac.AntdText('CPU', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=12
                        ),
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    fac.AntdTable(
                                        data=mem,
                                        columns=[
                                            {
                                                'dataIndex': 'name',
                                                'title': '属性',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'value',
                                                'title': '内存',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                        ],
                                        bordered=False,
                                        pagination={
                                            'hideOnSinglePage': True
                                        }
                                    )
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-file-text'),
                                        fac.AntdText('内存', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=12
                        ),
                    ],
                    gutter=20
                ),
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    fac.AntdDescriptions(
                                        [
                                            fac.AntdDescriptionItem(
                                                sys.get('computer_name'),
                                                label='服务器名称'
                                            ),
                                            fac.AntdDescriptionItem(
                                                sys.get('os_name'),
                                                label='操作系统'
                                            ),
                                            fac.AntdDescriptionItem(
                                                sys.get('computer_ip'),
                                                label='服务器IP'
                                            ),
                                            fac.AntdDescriptionItem(
                                                sys.get('os_arch'),
                                                label='系统架构'
                                            ),
                                        ],
                                        bordered=True,
                                        column=2,
                                        labelStyle={
                                            'textAlign': 'center'
                                        },
                                        style={
                                            'width': '100%',
                                            'textAlign': 'center',
                                            'marginLeft': '20px',
                                            'marginRight': '20px'
                                        }
                                    )
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-desktop'),
                                        fac.AntdText('服务器信息', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=24
                        ),
                    ],
                    gutter=20,
                    style={
                        'marginTop': '20px',
                        'marginBottom': '20px'
                    }
                ),
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    fac.AntdDescriptions(
                                        [
                                            fac.AntdDescriptionItem(
                                                py.get('name'),
                                                label='Python名称'
                                            ),
                                            fac.AntdDescriptionItem(
                                                py.get('version'),
                                                label='Python版本'
                                            ),
                                            fac.AntdDescriptionItem(
                                                py.get('start_time'),
                                                label='启动时间'
                                            ),
                                            fac.AntdDescriptionItem(
                                                py.get('run_time'),
                                                label='运行时长'
                                            ),
                                            fac.AntdDescriptionItem(
                                                py.get('home'),
                                                label='安装路径'
                                            ),
                                            fac.AntdDescriptionItem(
                                                py.get('project_dir'),
                                                label='项目路径'
                                            ),
                                        ],
                                        bordered=True,
                                        column=2,
                                        labelStyle={
                                            'textAlign': 'center'
                                        },
                                        style={
                                            'width': '100%',
                                            'textAlign': 'center',
                                            'marginLeft': '20px',
                                            'marginRight': '20px'
                                        }
                                    )
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-filter'),
                                        fac.AntdText('Python解释器信息', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=24
                        ),
                    ],
                    gutter=20,
                    style={
                        'marginTop': '20px',
                        'marginBottom': '20px'
                    }
                ),
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdCard(
                                [
                                    fac.AntdTable(
                                        data=sys_files,
                                        columns=[
                                            {
                                                'dataIndex': 'dir_name',
                                                'title': '盘符路径',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'sys_type_name',
                                                'title': '文件系统',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'disk_name',
                                                'title': '盘符类型',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'total',
                                                'title': '总大小',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'free',
                                                'title': '可用大小',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'used',
                                                'title': '已用大小',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                            {
                                                'dataIndex': 'usage',
                                                'title': '已用百分比',
                                                'renderOptions': {
                                                    'renderType': 'ellipsis'
                                                },
                                            },
                                        ],
                                        bordered=False,
                                        pagination={
                                            'hideOnSinglePage': True
                                        }
                                    )
                                ],
                                title=html.Div(
                                    [
                                        fac.AntdIcon(icon='antd-file-sync'),
                                        fac.AntdText('磁盘状态', style={'marginLeft': '10px'})
                                    ]
                                ),
                                size='small',
                                style={
                                    'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                                }
                            ),
                            span=24
                        ),
                    ],
                    gutter=20,
                    style={
                        'marginTop': '20px',
                        'marginBottom': '20px'
                    }
                ),
            ],
        )
    ]

dash-fastapi-frontend/views/monitor/server/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac


def render_content():

    return html.Div(
        [
            html.Div(
                [
                    fac.AntdResult(
                        status='404',
                        title='页面不存在',
                        subTitle='检查您的网址输入是否正确',
                        style={
                            'paddingBottom': 0,
                            'paddingTop': 0
                        }
                    ),
                    fac.AntdButton(
                        '回到首页',
                        type='link',
                        href='/',
                        target='_self'
                    )
                ],
                style={
                    'textAlign': 'center'
                }
            )
        ],
        style={
            'height': '100vh',
            'display': 'flex',
            'alignItems': 'center',
            'justifyContent': 'center'
        }
    )

dash-fastapi-frontend/views/page_404.py
insistence/Dash-FastAPI-Admin
from . import (
    user,
    role,
    menu,
    dept,
    post,
    dict,
    config,
    notice
)

dash-fastapi-frontend/views/system/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.system_c.config_c
from api.config import get_config_list_api


def render(button_perms):

    config_params = dict(page_num=1, page_size=10)
    table_info = get_config_list_api(config_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        total = table_info['data']['total']
        for item in table_data:
            if item['config_type'] == 'Y':
                item['config_type'] = dict(tag='是', color='blue')
            else:
                item['config_type'] = dict(tag='否', color='volcano')
            item['key'] = str(item['config_id'])
            item['operation'] = [
                {
                    'content': '修改',
                    'type': 'link',
                    'icon': 'antd-edit'
                } if 'system:config:edit' in button_perms else {},
                {
                    'content': '删除',
                    'type': 'link',
                    'icon': 'antd-delete'
                } if 'system:config:remove' in button_perms else {},
            ]

    return [
        dcc.Store(id='config-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='config-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='config-export-container'),
        # 参数管理模块操作类型存储容器
        dcc.Store(id='config-operations-store'),
        dcc.Store(id='config-operations-store-bk'),
        # 参数管理模块修改操作行key存储容器
        dcc.Store(id='config-edit-id-store'),
        # 参数管理模块删除操作行key存储容器
        dcc.Store(id='config-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='config-config_name-input',
                                                            placeholder='请输入参数名称',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 235
                                                            }
                                                        ),
                                                        label='参数名称',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='config-config_key-input',
                                                            placeholder='请输入参数键名',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 235
                                                            }
                                                        ),
                                                        label='参数键名',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='config-config_type-select',
                                                            placeholder='系统内置',
                                                            options=[
                                                                {
                                                                    'label': '是',
                                                                    'value': 'Y'
                                                                },
                                                                {
                                                                    'label': '否',
                                                                    'value': 'N'
                                                                }
                                                            ],
                                                            style={
                                                                'width': 235
                                                            }
                                                        ),
                                                        label='系统内置',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdDateRangePicker(
                                                            id='config-create_time-range',
                                                            style={
                                                                'width': 235
                                                            }
                                                        ),
                                                        label='创建时间',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='config-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='config-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='config-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'config-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:config:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'config-operation-button',
                                                    'index': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0'
                                                }
                                            ) if 'system:config:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'config-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'system:config:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='config-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'system:config:export' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-sync'
                                                    ),
                                                    '刷新缓存',
                                                ],
                                                id='config-refresh-cache',
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb',
                                                    'marginRight': '10px'
                                                }
                                            ) if 'system:config:edit' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='config-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='config-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='config-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='config-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'config_id',
                                                    'title': '参数编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'config_name',
                                                    'title': '参数名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'config_key',
                                                    'title': '参数键名',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'config_value',
                                                    'title': '参数键值',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'config_type',
                                                    'title': '系统内置',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'remark',
                                                    'title': '备注',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑参数配置表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'config-form-value',
                                                'index': 'config_name'
                                            },
                                            placeholder='请输入参数名称',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='参数名称',
                                        required=True,
                                        id={
                                            'type': 'config-form-label',
                                            'index': 'config_name',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'config-form-value',
                                                'index': 'config_key'
                                            },
                                            placeholder='请输入参数键名',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='参数键名',
                                        required=True,
                                        id={
                                            'type': 'config-form-label',
                                            'index': 'config_key',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'config-form-value',
                                                'index': 'config_value'
                                            },
                                            placeholder='请输入参数键值',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='参数键值',
                                        required=True,
                                        id={
                                            'type': 'config-form-label',
                                            'index': 'config_value',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id={
                                                'type': 'config-form-value',
                                                'index': 'config_type'
                                            },
                                            options=[
                                                {
                                                    'label': '是',
                                                    'value': 'Y'
                                                },
                                                {
                                                    'label': '否',
                                                    'value': 'N'
                                                },
                                            ],
                                            defaultValue='0',
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='系统内置',
                                        id={
                                            'type': 'config-form-label',
                                            'index': 'config_type',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'config-form-value',
                                                'index': 'remark'
                                            },
                                            placeholder='请输入内容',
                                            allowClear=True,
                                            mode='text-area',
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='备注',
                                        id={
                                            'type': 'config-form-label',
                                            'index': 'remark',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                    ],
                    labelCol={
                        'span': 6
                    },
                    wrapperCol={
                        'span': 18
                    }
                )
            ],
            id='config-modal',
            mask=False,
            width=580,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除参数配置二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='config-delete-text'),
            id='config-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        )
    ]

dash-fastapi-frontend/views/system/config/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.system_c.dept_c
from api.dept import get_dept_list_api
from utils.tree_tool import get_dept_tree


def render(button_perms):
    table_data_new = []
    default_expanded_row_keys = []
    table_info = get_dept_list_api({})
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        for item in table_data:
            default_expanded_row_keys.append(str(item['dept_id']))
            item['key'] = str(item['dept_id'])
            if item['parent_id'] == 0:
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:dept:edit' in button_perms else {},
                    {
                        'content': '新增',
                        'type': 'link',
                        'icon': 'antd-plus'
                    } if 'system:dept:add' in button_perms else {},
                ]
            elif item['status'] == '1':
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:dept:edit' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:dept:remove' in button_perms else {},
                ]
            else:
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:dept:edit' in button_perms else {},
                    {
                        'content': '新增',
                        'type': 'link',
                        'icon': 'antd-plus'
                    } if 'system:dept:add' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:dept:remove' in button_perms else {},
                ]
            if item['status'] == '0':
                item['status'] = dict(tag='正常', color='blue')
            else:
                item['status'] = dict(tag='停用', color='volcano')
        table_data_new = get_dept_tree(0, table_data)

    return [
        dcc.Store(id='dept-button-perms-container', data=button_perms),
        # 部门管理模块操作类型存储容器
        dcc.Store(id='dept-operations-store'),
        dcc.Store(id='dept-operations-store-bk'),
        # 部门管理模块修改操作行key存储容器
        dcc.Store(id='dept-edit-id-store'),
        # 部门管理模块删除操作行key存储容器
        dcc.Store(id='dept-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='dept-dept_name-input',
                                                                    placeholder='请输入部门名称',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='部门名称'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdSelect(
                                                                    id='dept-status-select',
                                                                    placeholder='部门状态',
                                                                    options=[
                                                                        {
                                                                            'label': '正常',
                                                                            'value': '0'
                                                                        },
                                                                        {
                                                                            'label': '停用',
                                                                            'value': '1'
                                                                        }
                                                                    ],
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='部门状态'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '搜索',
                                                                    id='dept-search',
                                                                    type='primary',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-search'
                                                                    )
                                                                )
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '重置',
                                                                    id='dept-reset',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-sync'
                                                                    )
                                                                )
                                                            )
                                                        ],
                                                        style={
                                                            'paddingBottom': '10px'
                                                        }
                                                    ),
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        hidden=False,
                                        id='dept-search-form-container',
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'dept-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:dept:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-swap'
                                                    ),
                                                    '展开/折叠',
                                                ],
                                                id='dept-fold',
                                                style={
                                                    'color': '#909399',
                                                    'background': '#f4f4f5',
                                                    'border-color': '#d3d4d6'
                                                }
                                            ),
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='dept-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='dept-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='dept-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='dept-list-table',
                                            data=table_data_new,
                                            columns=[
                                                {
                                                    'dataIndex': 'dept_id',
                                                    'title': '部门编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                    'hidden': True
                                                },
                                                {
                                                    'dataIndex': 'dept_name',
                                                    'title': '部门名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'order_num',
                                                    'title': '排序',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            bordered=True,
                                            pagination={
                                                'hideOnSinglePage': True
                                            },
                                            defaultExpandedRowKeys=default_expanded_row_keys,
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px',
                                                'padding-bottom': '20px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑部门表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdFormItem(
                                                fac.AntdTreeSelect(
                                                    id={
                                                        'type': 'dept-form-value',
                                                        'index': 'parent_id'
                                                    },
                                                    placeholder='请选择上级部门',
                                                    treeData=[],
                                                    treeNodeFilterProp='title',
                                                    style={
                                                        'width': '100%'
                                                    }
                                                ),
                                                label='上级部门',
                                                required=True,
                                                id={
                                                    'type': 'dept-form-label',
                                                    'index': 'parent_id',
                                                    'required': True
                                                },
                                                labelCol={
                                                    'span': 4
                                                },
                                                wrapperCol={
                                                    'span': 20
                                                }
                                            ),
                                        ],
                                        id='dept-parent_id-div',
                                        hidden=False
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dept-form-value',
                                                'index': 'dept_name'
                                            },
                                            placeholder='请输入部门名称',
                                            allowClear=True,
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        label='部门名称',
                                        required=True,
                                        id={
                                            'type': 'dept-form-label',
                                            'index': 'dept_name',
                                            'required': True
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInputNumber(
                                            id={
                                                'type': 'dept-form-value',
                                                'index': 'order_num'
                                            },
                                            min=0,
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        label='显示顺序',
                                        required=True,
                                        id={
                                            'type': 'dept-form-label',
                                            'index': 'order_num',
                                            'required': True
                                        }
                                    ),
                                    span=12
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dept-form-value',
                                                'index': 'leader'
                                            },
                                            placeholder='请输入负责人',
                                            allowClear=True,
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        label='负责人',
                                        id={
                                            'type': 'dept-form-label',
                                            'index': 'leader',
                                            'required': False
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dept-form-value',
                                                'index': 'phone'
                                            },
                                            placeholder='请输入联系电话',
                                            allowClear=True,
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        label='联系电话',
                                        id={
                                            'type': 'dept-form-label',
                                            'index': 'phone',
                                            'required': False
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dept-form-value',
                                                'index': 'email'
                                            },
                                            placeholder='请输入邮箱',
                                            allowClear=True,
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        label='邮箱',
                                        id={
                                            'type': 'dept-form-label',
                                            'index': 'email',
                                            'required': False
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id={
                                                'type': 'dept-form-value',
                                                'index': 'status'
                                            },
                                            options=[
                                                {
                                                    'label': '正常',
                                                    'value': '0'
                                                },
                                                {
                                                    'label': '停用',
                                                    'value': '1'
                                                },
                                            ],
                                            defaultValue='0',
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        label='部门状态',
                                        id={
                                            'type': 'dept-form-label',
                                            'index': 'status',
                                            'required': False
                                        }
                                    ),
                                    span=12
                                ),
                            ],
                            gutter=5
                        ),
                    ],
                    labelCol={
                        'span': 8
                    },
                    wrapperCol={
                        'span': 16
                    },
                    style={
                        'marginRight': '15px'
                    }
                )
            ],
            id='dept-modal',
            mask=False,
            width=650,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除部门二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='dept-delete-text'),
            id='dept-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/system/dept/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.system_c.dict_c.dict_c
from . import dict_data
from api.dict import get_dict_type_list_api


def render(button_perms):

    dict_type_params = dict(page_num=1, page_size=10)
    table_info = get_dict_type_list_api(dict_type_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        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['dict_id'])
            item['dict_type'] = {
                'content': item['dict_type'],
                'type': 'link',
            }
            item['operation'] = [
                {
                    'content': '修改',
                    'type': 'link',
                    'icon': 'antd-edit'
                } if 'system:dict:edit' in button_perms else {},
                {
                    'content': '删除',
                    'type': 'link',
                    'icon': 'antd-delete'
                } if 'system:dict:remove' in button_perms else {},
            ]

    return [
        dcc.Store(id='dict_type-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='dict_type-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='dict_type-export-container'),
        # 字典管理模块操作类型存储容器
        dcc.Store(id='dict_type-operations-store'),
        dcc.Store(id='dict_type-operations-store-bk'),
        dcc.Store(id='dict_data-operations-store'),
        dcc.Store(id='dict_data-operations-store-bk'),
        # 字典管理模块修改操作行key存储容器
        dcc.Store(id='dict_type-edit-id-store'),
        dcc.Store(id='dict_data-edit-id-store'),
        # 字典管理模块删除操作行key存储容器
        dcc.Store(id='dict_type-delete-ids-store'),
        dcc.Store(id='dict_data-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='dict_type-dict_name-input',
                                                            placeholder='请输入字典名称',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='字典名称',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='dict_type-dict_type-input',
                                                            placeholder='请输入字典类型',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='字典类型',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='dict_type-status-select',
                                                            placeholder='字典状态',
                                                            options=[
                                                                {
                                                                    'label': '正常',
                                                                    'value': '0'
                                                                },
                                                                {
                                                                    'label': '停用',
                                                                    'value': '1'
                                                                }
                                                            ],
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='状态',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdDateRangePicker(
                                                            id='dict_type-create_time-range',
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='创建时间',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='dict_type-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='dict_type-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='dict_type-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'dict_type-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:dict:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'dict_type-operation-button',
                                                    'index': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0'
                                                }
                                            ) if 'system:dict:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'dict_type-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'system:dict:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='dict_type-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'system:dict:export' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-sync'
                                                    ),
                                                    '刷新缓存',
                                                ],
                                                id='dict_type-refresh-cache',
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'system:dict:edit' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='dict_type-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='dict_type-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='dict_type-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='dict_type-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'dict_id',
                                                    'title': '字典编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'dict_name',
                                                    'title': '字典名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'dict_type',
                                                    'title': '字典类型',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'remark',
                                                    'title': '备注',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑字典类型表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_type-form-value',
                                                'index': 'dict_name'
                                            },
                                            placeholder='请输入字典名称',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='字典名称',
                                        required=True,
                                        id={
                                            'type': 'dict_type-form-label',
                                            'index': 'dict_name',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_type-form-value',
                                                'index': 'dict_type'
                                            },
                                            placeholder='请输入字典类型',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='字典类型',
                                        required=True,
                                        id={
                                            'type': 'dict_type-form-label',
                                            'index': 'dict_type',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id={
                                                'type': 'dict_type-form-value',
                                                'index': 'status'
                                            },
                                            options=[
                                                {
                                                    'label': '正常',
                                                    'value': '0'
                                                },
                                                {
                                                    'label': '停用',
                                                    'value': '1'
                                                },
                                            ],
                                            defaultValue='0',
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='状态',
                                        id={
                                            'type': 'dict_type-form-label',
                                            'index': 'status',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_type-form-value',
                                                'index': 'remark'
                                            },
                                            placeholder='请输入内容',
                                            allowClear=True,
                                            mode='text-area',
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='备注',
                                        id={
                                            'type': 'dict_type-form-label',
                                            'index': 'remark',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                    ],
                    labelCol={
                        'span': 6
                    },
                    wrapperCol={
                        'span': 18
                    }
                )
            ],
            id='dict_type-modal',
            mask=False,
            width=580,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除字典类型二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='dict_type-delete-text'),
            id='dict_type-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),

        # 字典数据modal
        fac.AntdModal(
            dict_data.render(button_perms),
            id='dict_type_to_dict_data-modal',
            mask=False,
            maskClosable=False,
            width=1000,
            renderFooter=False,
            okClickClose=False
        )
    ]

dash-fastapi-frontend/views/system/dict/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.system_c.dict_c.dict_data_c


def render(button_perms):

    return [
        dcc.Store(id='dict_data-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='dict_data-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='dict_data-export-container'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='dict_data-dict_type-select',
                                                            placeholder='字典名称',
                                                            options=[],
                                                            allowClear=False,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='字典名称',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='dict_data-dict_label-input',
                                                            placeholder='请输入字典标签',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='字典标签',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='dict_data-status-select',
                                                            placeholder='数据状态',
                                                            options=[
                                                                {
                                                                    'label': '正常',
                                                                    'value': '0'
                                                                },
                                                                {
                                                                    'label': '停用',
                                                                    'value': '1'
                                                                }
                                                            ],
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='状态',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='dict_data-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='dict_data-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='dict_data-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'dict_data-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:dict:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'dict_data-operation-button',
                                                    'index': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0'
                                                }
                                            ) if 'system:dict:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'dict_data-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'system:dict:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='dict_data-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'system:dict:export' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='dict_data-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='dict_data-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='dict_data-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='dict_data-list-table',
                                            data=[],
                                            columns=[
                                                {
                                                    'dataIndex': 'dict_code',
                                                    'title': '字典编码',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'dict_label',
                                                    'title': '字典标签',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'dict_value',
                                                    'title': '字典键值',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'dict_sort',
                                                    'title': '字典排序',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'remark',
                                                    'title': '备注',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'fixed': 'right',
                                                    'width': 150,
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            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
        ),

        # 新增和编辑字典数据表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'dict_type'
                                            },
                                            placeholder='请输入字典类型',
                                            disabled=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='字典类型',
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'dict_type',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'dict_label'
                                            },
                                            placeholder='请输入数据标签',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='数据标签',
                                        required=True,
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'dict_label',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'dict_value'
                                            },
                                            placeholder='请输入数据键值',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='数据键值',
                                        required=True,
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'dict_value',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'css_class'
                                            },
                                            placeholder='请输入样式属性',
                                            allowClear=True,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='样式属性',
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'css_class',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInputNumber(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'dict_sort'
                                            },
                                            defaultValue=0,
                                            min=0,
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='显示排序',
                                        required=True,
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'dict_sort',
                                            'required': True
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdSelect(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'list_class'
                                            },
                                            placeholder='回显样式',
                                            options=[
                                                {
                                                    'label': '默认',
                                                    'value': 'default'
                                                },
                                                {
                                                    'label': '主要',
                                                    'value': 'primary'
                                                },
                                                {
                                                    'label': '成功',
                                                    'value': 'success'
                                                },
                                                {
                                                    'label': '信息',
                                                    'value': 'info'
                                                },
                                                {
                                                    'label': '警告',
                                                    'value': 'warning'
                                                },
                                                {
                                                    'label': '危险',
                                                    'value': 'danger'
                                                }
                                            ],
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='回显样式',
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'list_class',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'status'
                                            },
                                            options=[
                                                {
                                                    'label': '正常',
                                                    'value': '0'
                                                },
                                                {
                                                    'label': '停用',
                                                    'value': '1'
                                                },
                                            ],
                                            defaultValue='0',
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='状态',
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'status',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id={
                                                'type': 'dict_data-form-value',
                                                'index': 'remark'
                                            },
                                            placeholder='请输入内容',
                                            allowClear=True,
                                            mode='text-area',
                                            style={
                                                'width': 350
                                            }
                                        ),
                                        label='备注',
                                        id={
                                            'type': 'dict_data-form-label',
                                            'index': 'remark',
                                            'required': False
                                        }
                                    ),
                                    span=24
                                ),
                            ]
                        ),
                    ],
                    labelCol={
                        'span': 6
                    },
                    wrapperCol={
                        'span': 18
                    }
                )
            ],
            id='dict_data-modal',
            mask=False,
            maskClosable=False,
            width=580,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除字典数据二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='dict_data-delete-text'),
            id='dict_data-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True
        ),
    ]

dash-fastapi-frontend/views/system/dict/dict_data.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

from api.menu import get_menu_list_api
from utils.tree_tool import list_to_tree
from views.system.menu.components.icon_category import render_icon
import callbacks.system_c.menu_c.menu_c


def render(button_perms):
    table_data_new = []
    table_info = get_menu_list_api({})
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        for item in table_data:
            item['key'] = str(item['menu_id'])
            item['icon'] = [
                {
                    'type': 'link',
                    'icon': item['icon'],
                    'disabled': True,
                    'style': {
                        'color': 'rgba(0, 0, 0, 0.8)'
                    }
                },
            ]
            if item['status'] == '1':
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:menu:edit' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:menu:remove' in button_perms else {},
                ]
            else:
                item['operation'] = [
                    {
                        'content': '修改',
                        'type': 'link',
                        'icon': 'antd-edit'
                    } if 'system:menu:edit' in button_perms else {},
                    {
                        'content': '新增',
                        'type': 'link',
                        'icon': 'antd-plus'
                    } if 'system:menu:add' in button_perms else {},
                    {
                        'content': '删除',
                        'type': 'link',
                        'icon': 'antd-delete'
                    } if 'system:menu:remove' in button_perms else {},
                ]
            if item['status'] == '0':
                item['status'] = dict(tag='正常', color='blue')
            else:
                item['status'] = dict(tag='停用', color='volcano')
        table_data_new = list_to_tree(table_data)

    return [
        dcc.Store(id='menu-button-perms-container', data=button_perms),
        # 菜单管理模块操作类型存储容器
        dcc.Store(id='menu-operations-store'),
        dcc.Store(id='menu-operations-store-bk'),
        # modal菜单类型存储容器
        dcc.Store(id='menu-modal-menu-type-store'),
        # 不同菜单类型的触发器
        dcc.Store(id='menu-modal-M-trigger'),
        dcc.Store(id='menu-modal-C-trigger'),
        dcc.Store(id='menu-modal-F-trigger'),
        # 菜单管理模块修改操作行key存储容器
        dcc.Store(id='menu-edit-id-store'),
        # 菜单管理模块删除操作行key存储容器
        dcc.Store(id='menu-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='menu-menu_name-input',
                                                                    placeholder='请输入菜单名称',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='菜单名称'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdSelect(
                                                                    id='menu-status-select',
                                                                    placeholder='菜单状态',
                                                                    options=[
                                                                        {
                                                                            'label': '正常',
                                                                            'value': '0'
                                                                        },
                                                                        {
                                                                            'label': '停用',
                                                                            'value': '1'
                                                                        }
                                                                    ],
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='菜单状态'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '搜索',
                                                                    id='menu-search',
                                                                    type='primary',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-search'
                                                                    )
                                                                )
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '重置',
                                                                    id='menu-reset',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-sync'
                                                                    )
                                                                )
                                                            )
                                                        ],
                                                        style={
                                                            'paddingBottom': '10px'
                                                        }
                                                    ),
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='menu-search-form-container',
                                        hidden=False
                                    )
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'menu-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:menu:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-swap'
                                                    ),
                                                    '展开/折叠',
                                                ],
                                                id='menu-fold',
                                                style={
                                                    'color': '#909399',
                                                    'background': '#f4f4f5',
                                                    'border-color': '#d3d4d6'
                                                }
                                            ),
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='menu-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='menu-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='menu-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='menu-list-table',
                                            data=table_data_new,
                                            columns=[
                                                {
                                                    'dataIndex': 'menu_id',
                                                    'title': '菜单编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                    'hidden': True
                                                },
                                                {
                                                    'dataIndex': 'menu_name',
                                                    'title': '菜单名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'icon',
                                                    'title': '图标',
                                                    'width': 80,
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'order_num',
                                                    'title': '排序',
                                                    'width': 80,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'perms',
                                                    'title': '权限标识',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'component',
                                                    'title': '组件路径',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'width': 90,
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'width': 150,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            bordered=True,
                                            pagination={
                                                'hideOnSinglePage': True
                                            },
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px',
                                                'padding-bottom': '20px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑菜单表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdTreeSelect(
                                        id='menu-parent_id',
                                        placeholder='请选择上级菜单',
                                        treeData=[],
                                        defaultValue='0',
                                        treeNodeFilterProp='title',
                                        style={
                                            'width': 495
                                        }
                                    ),
                                    label='上级菜单',
                                    required=True,
                                    id='menu-parent_id-form-item',
                                    labelCol={
                                        'span': 4,
                                    },
                                    wrapperCol={
                                        'span': 20
                                    }
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdRadioGroup(
                                        id='menu-menu_type',
                                        options=[
                                            {
                                                'label': '目录',
                                                'value': 'M'
                                            },
                                            {
                                                'label': '菜单',
                                                'value': 'C'
                                            },
                                            {
                                                'label': '按钮',
                                                'value': 'F'
                                            },
                                        ],
                                        defaultValue='M',
                                        style={
                                            'width': 495
                                        }
                                    ),
                                    label='菜单类型',
                                    required=True,
                                    id='menu-menu_type-form-item',
                                    labelCol={
                                        'span': 4,
                                    },
                                    wrapperCol={
                                        'span': 20
                                    }
                                )
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdPopover(
                                        fac.AntdInput(
                                            id='menu-icon',
                                            placeholder='点击此处选择图标',
                                            readOnly=True,
                                            style={
                                                'width': 495
                                            }
                                        ),
                                        content=render_icon(),
                                        trigger='click',
                                        placement='bottom'
                                    ),
                                    label='菜单图标',
                                    id='menu-icon-form-item',
                                    labelCol={
                                        'span': 4,
                                    },
                                    wrapperCol={
                                        'span': 20
                                    }
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id='menu-menu_name',
                                        placeholder='请输入菜单名称',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='菜单名称',
                                    required=True,
                                    id='menu-menu_name-form-item',
                                    labelCol={
                                        'span': 8,
                                    },
                                    wrapperCol={
                                        'span': 16
                                    }
                                ),
                                fac.AntdFormItem(
                                    fac.AntdInputNumber(
                                        id='menu-order_num',
                                        min=0,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='显示排序',
                                    required=True,
                                    id='menu-order_num-form-item',
                                    labelCol={
                                        'span': 8,
                                    },
                                    wrapperCol={
                                        'span': 16
                                    }
                                ),
                            ],
                            size="middle"
                        ),
                        html.Div(id='content-by-menu-type'),
                    ]
                )
            ],
            id='menu-modal',
            mask=False,
            width=680,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除菜单二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='menu-delete-text'),
            id='menu-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/system/menu/__init__.py
insistence/Dash-FastAPI-Admin
from . import (
    content_type,
    menu_type,
    button_type
)

dash-fastapi-frontend/views/system/menu/components/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac

import callbacks.system_c.menu_c.components_c.button_type_c


def render():
    return [
        fac.AntdSpace(
            [
                fac.AntdFormItem(
                    fac.AntdInput(
                        id='button-menu-perms',
                        placeholder='请输入权限字符',
                        allowClear=True,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='控制器中定义的权限字符，如：system:user:list'
                            ),
                            fac.AntdText('权限字符')
                        ]
                    ),
                    id='button-menu-perms-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
            ],
            size="middle"
        ),
    ]

dash-fastapi-frontend/views/system/menu/components/button_type.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac

import callbacks.system_c.menu_c.components_c.content_type_c


def render():
    return [
        fac.AntdSpace(
            [
                fac.AntdFormItem(
                    fac.AntdRadioGroup(
                        id='content-menu-is_frame',
                        options=[
                            {
                                'label': '是',
                                'value': 0
                            },
                            {
                                'label': '否',
                                'value': 1
                            },
                        ],
                        defaultValue=1,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='选择是外链则路由地址需要以`http(s)://`开头'
                            ),
                            fac.AntdText('是否外链')
                        ]
                    ),
                    id='content-menu-is_frame-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdInput(
                        id='content-menu-path',
                        placeholder='请输入路由地址',
                        allowClear=True,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='访问的路由地址，如：`user`，如外网地址需内链访问则以`http(s)://`开头'
                            ),
                            fac.AntdText('路由地址')
                        ]
                    ),
                    required=True,
                    id='content-menu-path-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
            ],
            size="middle"
        ),
        fac.AntdSpace(
            [
                fac.AntdFormItem(
                    fac.AntdRadioGroup(
                        id='content-menu-visible',
                        options=[
                            {
                                'label': '显示',
                                'value': '0'
                            },
                            {
                                'label': '隐藏',
                                'value': '1'
                            },
                        ],
                        defaultValue='0',
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='选择隐藏则路由将不会出现在侧边栏，但仍然可以访问'
                            ),
                            fac.AntdText('显示状态')
                        ]
                    ),
                    id='content-menu-visible-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdRadioGroup(
                        id='content-menu-status',
                        options=[
                            {
                                'label': '正常',
                                'value': '0'
                            },
                            {
                                'label': '停用',
                                'value': '1'
                            },
                        ],
                        defaultValue='0',
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='选择停用则路由将不会出现在侧边栏，也不能被访问'
                            ),
                            fac.AntdText('菜单状态')
                        ]
                    ),
                    id='content-menu-status-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
            ],
            size="middle"
        )
    ]

dash-fastapi-frontend/views/system/menu/components/content_type.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac
from config.global_config import IconConfig


def render_icon():

    return html.Div(
        [
            fac.AntdRadioGroup(
                id='icon-category',
                options=[
                    {
                        'label': fac.AntdIcon(
                            icon=icon,
                        ),
                        'value': icon
                    }
                    for icon in IconConfig.ICON_LIST
                ],
                style={
                    'width': 450,
                    'paddingLeft': '10px'
                }
            ),
        ],
        style={
            'maxHeight': '135px',
            'overflow': 'auto'
        }
    )

dash-fastapi-frontend/views/system/menu/components/icon_category.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac

import callbacks.system_c.menu_c.components_c.menu_type_c


def render():
    return [
        fac.AntdSpace(
            [
                fac.AntdFormItem(
                    fac.AntdRadioGroup(
                        id='menu-menu-is_frame',
                        options=[
                            {
                                'label': '是',
                                'value': 0
                            },
                            {
                                'label': '否',
                                'value': 1
                            },
                        ],
                        defaultValue=1,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='选择是外链则路由地址需要以`http(s)://`开头'
                            ),
                            fac.AntdText('是否外链')
                        ]
                    ),
                    id='menu-menu-is_frame-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdInput(
                        id='menu-menu-path',
                        placeholder='请输入路由地址',
                        allowClear=True,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='访问的路由地址，如：`user`，如外网地址需内链访问则以`http(s)://`开头'
                            ),
                            fac.AntdText('路由地址')
                        ]
                    ),
                    required=True,
                    id='menu-menu-path-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
            ],
            size="middle"
        ),
        fac.AntdSpace(
            [
                fac.AntdFormItem(
                    fac.AntdInput(
                        id='menu-menu-component',
                        placeholder='请输入组件路径',
                        allowClear=True,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='访问的组件路径，如：`system.user.index`，默认在`views`目录下'
                            ),
                            fac.AntdText('组件路径')
                        ]
                    ),
                    id='menu-menu-component-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdInput(
                        id='menu-menu-perms',
                        placeholder='请输入权限字符',
                        allowClear=True,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='控制器中定义的权限字符，如：system:user:list'
                            ),
                            fac.AntdText('权限字符')
                        ]
                    ),
                    id='menu-menu-perms-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
            ],
            size="middle"
        ),
        fac.AntdSpace(
            [
                fac.AntdFormItem(
                    fac.AntdInput(
                        id='menu-menu-query',
                        placeholder='请输入路由参数',
                        allowClear=True,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='访问路由的默认传递参数，如：`{"id": 1, "name": "ry"}`'
                            ),
                            fac.AntdText('路由参数')
                        ]
                    ),
                    id='menu-menu-query-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdRadioGroup(
                        id='menu-menu-is_cache',
                        options=[
                            {
                                'label': '缓存',
                                'value': 0
                            },
                            {
                                'label': '不缓存',
                                'value': 1
                            },
                        ],
                        defaultValue=0,
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='选择是则会被`keep-alive`缓存，需要匹配组件的`name`和地址保持一致'
                            ),
                            fac.AntdText('是否缓存')
                        ]
                    ),
                    id='menu-menu-is_cache-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
            ],
            size="middle"
        ),
        fac.AntdSpace(
            [
                fac.AntdFormItem(
                    fac.AntdRadioGroup(
                        id='menu-menu-visible',
                        options=[
                            {
                                'label': '显示',
                                'value': '0'
                            },
                            {
                                'label': '隐藏',
                                'value': '1'
                            },
                        ],
                        defaultValue='0',
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='选择隐藏则路由将不会出现在侧边栏，但仍然可以访问'
                            ),
                            fac.AntdText('显示状态')
                        ]
                    ),
                    id='menu-menu-visible-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdRadioGroup(
                        id='menu-menu-status',
                        options=[
                            {
                                'label': '正常',
                                'value': '0'
                            },
                            {
                                'label': '停用',
                                'value': '1'
                            },
                        ],
                        defaultValue='0',
                        style={
                            'width': 200
                        }
                    ),
                    label=html.Div(
                        [
                            fac.AntdTooltip(
                                fac.AntdIcon(
                                    icon='antd-question-circle'
                                ),
                                title='选择停用则路由将不会出现在侧边栏，也不能被访问'
                            ),
                            fac.AntdText('菜单状态')
                        ]
                    ),
                    id='menu-menu-status-form-item',
                    labelCol={
                        'span': 8,
                    },
                    wrapperCol={
                        'span': 16
                    }
                ),
            ],
            size="middle"
        )
    ]

dash-fastapi-frontend/views/system/menu/components/menu_type.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac
import feffery_utils_components as fuc
import json

import callbacks.system_c.notice_c
from api.notice import get_notice_list_api
from api.dict import query_dict_data_list_api


def render(button_perms):

    option = []
    option_table = []
    info = query_dict_data_list_api(dict_type='sys_notice_type')
    if info.get('code') == 200:
        data = info.get('data')
        option = [dict(label=item.get('dict_label'), value=item.get('dict_value')) for item in data]
        option_table = [
            dict(label=item.get('dict_label'), value=item.get('dict_value'), css_class=item.get('css_class')) for item
            in data]
    option_dict = {item.get('value'): item for item in option_table}

    notice_params = dict(page_num=1, page_size=10)
    table_info = get_notice_list_api(notice_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        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')
            if str(item.get('notice_type')) in option_dict.keys():
                item['notice_type'] = dict(
                    tag=option_dict.get(str(item.get('notice_type'))).get('label'),
                    color=json.loads(option_dict.get(str(item.get('notice_type'))).get('css_class')).get('color')
                )
            item['key'] = str(item['notice_id'])
            item['operation'] = [
                {
                    'content': '修改',
                    'type': 'link',
                    'icon': 'antd-edit'
                } if 'system:notice:edit' in button_perms else {},
                {
                    'content': '删除',
                    'type': 'link',
                    'icon': 'antd-delete'
                } if 'system:notice:remove' in button_perms else {},
            ]

    return [
        dcc.Store(id='notice-button-perms-container', data=button_perms),
        # 通知公告管理模块操作类型存储容器
        dcc.Store(id='notice-operations-store'),
        dcc.Store(id='notice-operations-store-bk'),
        # 通知公告管理模块修改操作行key存储容器
        dcc.Store(id='notice-edit-id-store'),
        # 通知公告管理模块删除操作行key存储容器
        dcc.Store(id='notice-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='notice-notice_title-input',
                                                            placeholder='请输入公告标题',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='公告标题',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='notice-update_by-input',
                                                            placeholder='请输入操作人员',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='操作人员',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='notice-notice_type-select',
                                                            placeholder='公告类型',
                                                            options=option,
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='类型',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdDateRangePicker(
                                                            id='notice-create_time-range',
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='创建时间',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='notice-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='notice-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='notice-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'notice-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:notice:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'notice-operation-button',
                                                    'index': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0'
                                                }
                                            ) if 'system:notice:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'notice-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'system:notice:remove' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='notice-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='notice-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='notice-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='notice-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'notice_id',
                                                    'title': '序号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'notice_title',
                                                    'title': '公告标题',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'notice_type',
                                                    'title': '公告类型',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_by',
                                                    'title': '创建者',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑通知公告modal
        # 初始化渲染富文本编辑器
        fuc.FefferyExecuteJs(
            id='notice-init-editor'
        ),
        # 回写内容到富文本编辑器
        dcc.Store(id='notice-written-editor-store'),
        # 监听富文本编辑器内容并取回
        fuc.FefferySessionStorage(
            id='notice-content'
        ),

        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdInput(
                                            id='notice-notice_title',
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id='notice-notice_title-form-item',
                                        required=True,
                                        label='公告标题',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                ),
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdSelect(
                                            id='notice-notice_type',
                                            options=option,
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id='notice-notice_type-form-item',
                                        required=True,
                                        label='公告类型',
                                        labelCol={
                                            'span': 6
                                        },
                                        wrapperCol={
                                            'span': 18
                                        }
                                    ),
                                    span=12
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        fac.AntdRadioGroup(
                                            id='notice-status',
                                            options=[
                                                {
                                                    'label': '正常',
                                                    'value': '0'
                                                },
                                                {
                                                    'label': '关闭',
                                                    'value': '1'
                                                }
                                            ],
                                            style={
                                                'width': '100%'
                                            }
                                        ),
                                        id='notice-status-form-item',
                                        label='状态',
                                        labelCol={
                                            'span': 3
                                        },
                                        wrapperCol={
                                            'span': 21
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdFormItem(
                                        html.Div(
                                            [
                                                html.Div(
                                                    id='notice-notice_content-toolbar-container',
                                                    style={
                                                        'borderBottom': '1px solid #ccc'
                                                    }
                                                ),
                                                html.Div(
                                                    id='notice-notice_content-editor-container',
                                                    style={
                                                        'height': 300,
                                                        'width': '100%'
                                                    }
                                                )
                                            ],
                                            id='notice-notice_content-editor-wrapper',
                                            style={
                                                'zIndex': 9999,
                                                'border': '1px solid #ccc',
                                                'marginBottom': 15
                                            }
                                        ),
                                        id='notice-notice_content-form-item',
                                        label='内容',
                                        labelCol={
                                            'span': 3
                                        },
                                        wrapperCol={
                                            'span': 21
                                        }
                                    ),
                                    span=24
                                ),
                            ],
                            gutter=5
                        )
                    ],
                    style={
                        'marginRight': '30px'
                    }
                )
            ],
            id='notice-modal',
            mask=False,
            width=900,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除通知公告二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='notice-delete-text'),
            id='notice-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/system/notice/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.system_c.post_c
from api.post import get_post_list_api


def render(button_perms):

    post_params = dict(page_num=1, page_size=10)
    table_info = get_post_list_api(post_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        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['post_id'])
            item['operation'] = [
                {
                    'content': '修改',
                    'type': 'link',
                    'icon': 'antd-edit'
                } if 'system:post:edit' in button_perms else {},
                {
                    'content': '删除',
                    'type': 'link',
                    'icon': 'antd-delete'
                } if 'system:post:remove' in button_perms else {},
            ]

    return [
        dcc.Store(id='post-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='post-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='post-export-container'),
        # 岗位管理模块操作类型存储容器
        dcc.Store(id='post-operations-store'),
        dcc.Store(id='post-operations-store-bk'),
        # 岗位管理模块修改操作行key存储容器
        dcc.Store(id='post-edit-id-store'),
        # 岗位管理模块删除操作行key存储容器
        dcc.Store(id='post-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='post-post_code-input',
                                                                    placeholder='请输入岗位编码',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 210
                                                                    }
                                                                ),
                                                                label='岗位编码'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='post-post_name-input',
                                                                    placeholder='请输入岗位名称',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 210
                                                                    }
                                                                ),
                                                                label='岗位名称'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdSelect(
                                                                    id='post-status-select',
                                                                    placeholder='岗位状态',
                                                                    options=[
                                                                        {
                                                                            'label': '正常',
                                                                            'value': '0'
                                                                        },
                                                                        {
                                                                            'label': '停用',
                                                                            'value': '1'
                                                                        }
                                                                    ],
                                                                    style={
                                                                        'width': 200
                                                                    }
                                                                ),
                                                                label='岗位状态'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '搜索',
                                                                    id='post-search',
                                                                    type='primary',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-search'
                                                                    )
                                                                )
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '重置',
                                                                    id='post-reset',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-sync'
                                                                    )
                                                                )
                                                            )
                                                        ],
                                                        style={
                                                            'paddingBottom': '10px'
                                                        }
                                                    ),
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='post-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'post-operation-button',
                                                    'index': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:post:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'post-operation-button',
                                                    'index': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0'
                                                }
                                            ) if 'system:post:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'post-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'system:post:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='post-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'system:post:export' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px',
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='post-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='post-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='post-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='post-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'post_id',
                                                    'title': '岗位编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'post_code',
                                                    'title': '岗位编码',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'post_name',
                                                    'title': '岗位名称',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'post_sort',
                                                    'title': '岗位排序',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'renderOptions': {
                                                        'renderType': 'tags'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'button'
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑岗位表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdFormItem(
                            fac.AntdInput(
                                id={
                                    'type': 'post-form-value',
                                    'index': 'post_name'
                                },
                                placeholder='请输入岗位名称',
                                allowClear=True,
                                style={
                                    'width': 350
                                }
                            ),
                            label='岗位名称',
                            required=True,
                            id={
                                'type': 'post-form-label',
                                'index': 'post_name',
                                'required': True
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdInput(
                                id={
                                    'type': 'post-form-value',
                                    'index': 'post_code'
                                },
                                placeholder='请输入岗位编码',
                                allowClear=True,
                                style={
                                    'width': 350
                                }
                            ),
                            label='岗位编码',
                            required=True,
                            id={
                                'type': 'post-form-label',
                                'index': 'post_code',
                                'required': True
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdInputNumber(
                                id={
                                    'type': 'post-form-value',
                                    'index': 'post_sort'
                                },
                                defaultValue=0,
                                min=0,
                                style={
                                    'width': 350
                                }
                            ),
                            label='岗位顺序',
                            required=True,
                            id={
                                'type': 'post-form-label',
                                'index': 'post_sort',
                                'required': True
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdRadioGroup(
                                id={
                                    'type': 'post-form-value',
                                    'index': 'status'
                                },
                                options=[
                                    {
                                        'label': '正常',
                                        'value': '0'
                                    },
                                    {
                                        'label': '停用',
                                        'value': '1'
                                    },
                                ],
                                defaultValue='0',
                                style={
                                    'width': 350
                                }
                            ),
                            label='岗位状态',
                            id={
                                'type': 'post-form-label',
                                'index': 'status',
                                'required': False
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdInput(
                                id={
                                    'type': 'post-form-value',
                                    'index': 'remark'
                                },
                                placeholder='请输入内容',
                                allowClear=True,
                                mode='text-area',
                                style={
                                    'width': 350
                                }
                            ),
                            label='备注',
                            id={
                                'type': 'post-form-label',
                                'index': 'remark',
                                'required': False
                            }
                        ),
                    ],
                    labelCol={
                        'span': 6
                    },
                    wrapperCol={
                        'span': 18
                    }
                )
            ],
            id='post-modal',
            mask=False,
            width=580,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除岗位二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='post-delete-text'),
            id='post-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),
    ]

dash-fastapi-frontend/views/system/post/__init__.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac

import callbacks.system_c.role_c.role_c
from . import data_scope, allocate_user
from api.role import get_role_list_api


def render(button_perms):

    role_params = dict(page_num=1, page_size=10)
    table_info = get_role_list_api(role_params)
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        total = table_info['data']['total']
        for item in table_data:
            if item['status'] == '0':
                item['status'] = dict(checked=True, disabled=item['role_id'] == 1)
            else:
                item['status'] = dict(checked=False, disabled=item['role_id'] == 1)
            item['key'] = str(item['role_id'])
            if item['role_id'] == 1:
                item['operation'] = []
            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'
                        ) if 'system:role:edit' in button_perms else []
                    ]
                )

    return [
        dcc.Store(id='role-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='role-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='role-export-container'),
        # 角色管理模块操作类型存储容器
        dcc.Store(id='role-operations-store'),
        dcc.Store(id='role-operations-store-bk'),
        # 角色管理模块修改操作行key存储容器
        dcc.Store(id='role-edit-id-store'),
        # 角色管理模块删除操作行key存储容器
        dcc.Store(id='role-delete-ids-store'),
        # 角色管理模块菜单权限存储容器
        dcc.Store(id='role-menu-store'),
        dcc.Store(id='current-role-menu-store'),
        # 角色管理模块数据权限存储容器
        dcc.Store(id='role-dept-store'),
        dcc.Store(id='current-role-dept-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='role-role_name-input',
                                                            placeholder='请输入角色名称',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 220
                                                            }
                                                        ),
                                                        label='角色名称',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdInput(
                                                            id='role-role_key-input',
                                                            placeholder='请输入权限字符',
                                                            autoComplete='off',
                                                            allowClear=True,
                                                            style={
                                                                'width': 220
                                                            }
                                                        ),
                                                        label='权限字符',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdSelect(
                                                            id='role-status-select',
                                                            placeholder='角色状态',
                                                            options=[
                                                                {
                                                                    'label': '正常',
                                                                    'value': '0'
                                                                },
                                                                {
                                                                    'label': '停用',
                                                                    'value': '1'
                                                                }
                                                            ],
                                                            style={
                                                                'width': 220
                                                            }
                                                        ),
                                                        label='状态',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdDateRangePicker(
                                                            id='role-create_time-range',
                                                            style={
                                                                'width': 240
                                                            }
                                                        ),
                                                        label='创建时间',
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '搜索',
                                                            id='role-search',
                                                            type='primary',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-search'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    ),
                                                    fac.AntdFormItem(
                                                        fac.AntdButton(
                                                            '重置',
                                                            id='role-reset',
                                                            icon=fac.AntdIcon(
                                                                icon='antd-sync'
                                                            )
                                                        ),
                                                        style={'paddingBottom': '10px'},
                                                    )
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='role-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id={
                                                    'type': 'role-operation-button',
                                                    'operation': 'add'
                                                },
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff',
                                                    'marginRight': '10px'
                                                }
                                            ) if 'system:role:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'role-operation-button',
                                                    'operation': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0',
                                                    'marginRight': '10px'
                                                }
                                            ) if 'system:role:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'role-operation-button',
                                                    'operation': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb',
                                                    'marginRight': '10px'
                                                }
                                            ) if 'system:role:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='role-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399',
                                                    'marginRight': '10px'
                                                }
                                            ) if 'system:role:export' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='role-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='role-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='role-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='role-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'role_id',
                                                    'title': '角色编号',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    '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': 'switch'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'width': 180,
                                                    'dataIndex': 'operation',
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'padding-right': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=24
                )
            ],
            gutter=5
        ),

        # 新增和编辑角色表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdFormItem(
                            fac.AntdInput(
                                id={
                                    'type': 'role-form-value',
                                    'index': 'role_name',
                                    'required': True
                                },
                                placeholder='请输入角色名称',
                                allowClear=True,
                                style={
                                    'width': 350
                                }
                            ),
                            label='角色名称',
                            required=True,
                            id={
                                'type': 'role-form-label',
                                'index': 'role_name',
                                'required': True
                            },
                            labelCol={
                                'span': 6
                            },
                            wrapperCol={
                                'span': 18
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdInput(
                                id={
                                    'type': 'role-form-value',
                                    'index': 'role_key',
                                    'required': True
                                },
                                placeholder='请输入权限字符',
                                allowClear=True,
                                style={
                                    'width': 350
                                }
                            ),
                            label=html.Div(
                                [
                                    fac.AntdTooltip(
                                        fac.AntdIcon(
                                            icon='antd-question-circle'
                                        ),
                                        title='控制器中定义的权限字符，如：common'
                                    ),
                                    fac.AntdText('权限字符')
                                ]
                            ),
                            required=True,
                            id={
                                'type': 'role-form-label',
                                'index': 'role_key',
                                'required': True
                            },
                            labelCol={
                                'span': 6
                            },
                            wrapperCol={
                                'span': 18
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdInputNumber(
                                id={
                                    'type': 'role-form-value',
                                    'index': 'role_sort',
                                    'required': True
                                },
                                placeholder='请输入角色顺序',
                                defaultValue=0,
                                min=0,
                                style={
                                    'width': 350
                                }
                            ),
                            label='角色顺序',
                            required=True,
                            id={
                                'type': 'role-form-label',
                                'index': 'role_sort',
                                'required': True
                            },
                            labelCol={
                                'span': 6
                            },
                            wrapperCol={
                                'span': 18
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdRadioGroup(
                                id={
                                    'type': 'role-form-value',
                                    'index': 'status',
                                    'required': False
                                },
                                options=[
                                    {
                                        'label': '正常',
                                        'value': '0'
                                    },
                                    {
                                        'label': '停用',
                                        'value': '1'
                                    },
                                ],
                                style={
                                    'width': 350
                                }
                            ),
                            label='状态',
                            id={
                                'type': 'role-form-label',
                                'index': 'status',
                                'required': False
                            },
                            labelCol={
                                'span': 6
                            },
                            wrapperCol={
                                'span': 18
                            }
                        ),
                        fac.AntdFormItem(
                            [
                                fac.AntdRow(
                                    [
                                        fac.AntdCol(
                                            fac.AntdCheckbox(
                                                id='role-menu-perms-radio-fold-unfold',
                                                label='展开/折叠'
                                            ),
                                            span=7,
                                        ),
                                        fac.AntdCol(
                                            fac.AntdCheckbox(
                                                id='role-menu-perms-radio-all-none',
                                                label='全选/全不选'
                                            ),
                                            span=8,
                                        ),
                                        fac.AntdCol(
                                            fac.AntdCheckbox(
                                                id='role-menu-perms-radio-parent-children',
                                                label='父子联动',
                                                checked=True
                                            ),
                                            span=6,
                                        ),
                                    ],
                                    style={
                                        'paddingTop': '6px'
                                    }
                                ),
                                fac.AntdRow(
                                    fac.AntdCol(
                                        html.Div(
                                            [
                                                fac.AntdTree(
                                                    id='role-menu-perms',
                                                    treeData=[],
                                                    multiple=True,
                                                    checkable=True,
                                                    showLine=False,
                                                    selectable=False
                                                )
                                            ],
                                            style={
                                                'border': 'solid 1px rgba(0, 0, 0, 0.2)',
                                                'border-radius': '5px',
                                                'width': 350
                                            }
                                        )
                                    ),
                                    style={
                                        'paddingTop': '6px'
                                    }
                                ),
                            ],
                            label='菜单权限',
                            id='role-menu-perms-form-item',
                            labelCol={
                                'span': 6
                            },
                            wrapperCol={
                                'span': 18
                            }
                        ),
                        fac.AntdFormItem(
                            fac.AntdInput(
                                id={
                                    'type': 'role-form-value',
                                    'index': 'remark',
                                    'required': False
                                },
                                placeholder='请输入内容',
                                allowClear=True,
                                mode='text-area',
                                style={
                                    'width': 350
                                }
                            ),
                            label='备注',
                            id={
                                'type': 'role-form-label',
                                'index': 'remark',
                                'required': False
                            },
                            labelCol={
                                'span': 6
                            },
                            wrapperCol={
                                'span': 18
                            }
                        ),
                    ]
                )
            ],
            id='role-modal',
            mask=False,
            width=600,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除角色二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='role-delete-text'),
            id='role-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            centered=True
        ),

        # 数据权限modal
        fac.AntdModal(
            data_scope.render(),
            id='role-datascope-modal',
            title='数据权限',
            mask=False,
            width=600,
            renderFooter=True,
            okClickClose=False
        ),

        # 分配用户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
        )
    ]

dash-fastapi-frontend/views/system/role/__init__.py
insistence/Dash-FastAPI-Admin
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
        ),
    ]

dash-fastapi-frontend/views/system/role/allocate_user.py
insistence/Dash-FastAPI-Admin
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
        )

dash-fastapi-frontend/views/system/role/component/query_form_table.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_antd_components as fac

import callbacks.system_c.role_c.data_scope_c


def render():
    return [
        fac.AntdForm(
            [
                fac.AntdFormItem(
                    fac.AntdInput(
                        id={
                            'type': 'datascope-form-value',
                            'index': 'role_name'
                        },
                        placeholder='请输入角色名称',
                        allowClear=True,
                        disabled=True,
                        style={
                            'width': 350
                        }
                    ),
                    label='角色名称',
                    id={
                        'type': 'datascope-form-label',
                        'index': 'role_name'
                    },
                    labelCol={
                        'span': 6
                    },
                    wrapperCol={
                        'span': 18
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdInput(
                        id={
                            'type': 'datascope-form-value',
                            'index': 'role_key'
                        },
                        placeholder='请输入权限字符',
                        allowClear=True,
                        disabled=True,
                        style={
                            'width': 350
                        }
                    ),
                    label='权限字符',
                    id={
                        'type': 'datascope-form-label',
                        'index': 'role_key'
                    },
                    labelCol={
                        'span': 6
                    },
                    wrapperCol={
                        'span': 18
                    }
                ),
                fac.AntdFormItem(
                    fac.AntdSelect(
                        id={
                            'type': 'datascope-form-value',
                            'index': 'data_scope'
                        },
                        options=[
                            {
                                'label': '全部数据权限',
                                'value': '1'
                            },
                            {
                                'label': '自定义数据权限',
                                'value': '2'
                            },
                            {
                                'label': '本部门数据权限',
                                'value': '3'
                            },{
                                'label': '本部门及以下数据权限',
                                'value': '4'
                            },
                            {
                                'label': '仅本人数据权限',
                                'value': '5'
                            }
                        ],
                        placeholder='请选择权限范围',
                        style={
                            'width': 350
                        }
                    ),
                    label='权限范围',
                    id={
                        'type': 'datascope-form-label',
                        'index': 'data_scope'
                    },
                    labelCol={
                        'span': 6
                    },
                    wrapperCol={
                        'span': 18
                    }
                ),
                html.Div(
                    fac.AntdFormItem(
                        [
                            fac.AntdRow(
                                [
                                    fac.AntdCol(
                                        fac.AntdCheckbox(
                                            id='role-dept-perms-radio-fold-unfold',
                                            label='展开/折叠'
                                        ),
                                        span=7,
                                    ),
                                    fac.AntdCol(
                                        fac.AntdCheckbox(
                                            id='role-dept-perms-radio-all-none',
                                            label='全选/全不选'
                                        ),
                                        span=8,
                                    ),
                                    fac.AntdCol(
                                        fac.AntdCheckbox(
                                            id='role-dept-perms-radio-parent-children',
                                            label='父子联动',
                                            checked=True
                                        ),
                                        span=6,
                                    ),
                                ],
                                style={
                                    'paddingTop': '6px'
                                }
                            ),
                            fac.AntdRow(
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdTree(
                                                id='role-dept-perms',
                                                treeData=[],
                                                multiple=True,
                                                checkable=True,
                                                showLine=False,
                                                selectable=False
                                            )
                                        ],
                                        style={
                                            'border': 'solid 1px rgba(0, 0, 0, 0.2)',
                                            'border-radius': '5px',
                                            'width': 350
                                        }
                                    )
                                ),
                                style={
                                    'paddingTop': '6px'
                                }
                            ),
                        ],
                        label='数据权限',
                        id='role-dept-perms-form-item',
                        labelCol={
                            'span': 6
                        },
                        wrapperCol={
                            'span': 18
                        }
                    ),
                    id='role-dept-perms-div'
                )
            ]
        )
    ]

dash-fastapi-frontend/views/system/role/data_scope.py
insistence/Dash-FastAPI-Admin
from dash import dcc, html
import feffery_antd_components as fac
from flask import session

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


def render(button_perms):
    dept_params = dict(dept_name='')
    user_params = dict(page_num=1, page_size=10)
    tree_info = get_dept_tree_api(dept_params)
    table_info = get_user_list_api(user_params)
    tree_data = []
    table_data = []
    page_num = 1
    page_size = 10
    total = 0
    if tree_info['code'] == 200:
        tree_data = tree_info['data']
    if table_info['code'] == 200:
        table_data = table_info['data']['rows']
        page_num = table_info['data']['page_num']
        page_size = table_info['data']['page_size']
        total = table_info['data']['total']
        for item in table_data:
            if item['status'] == '0':
                item['status'] = dict(checked=True, disabled=item['user_id'] == 1)
            else:
                item['status'] = dict(checked=False, disabled=item['user_id'] == 1)
            item['key'] = str(item['user_id'])
            if item['user_id'] == 1:
                item['operation'] = []
            else:
                item['operation'] = [
                    {
                        'title': '修改',
                        'icon': 'antd-edit'
                    } if 'system:user:edit' in button_perms else None,
                    {
                        'title': '删除',
                        'icon': 'antd-delete'
                    } if 'system:user:remove' in button_perms else None,
                    {
                        'title': '重置密码',
                        'icon': 'antd-key'
                    } if 'system:user:resetPwd' in button_perms else None,
                    {
                        'title': '分配角色',
                        'icon': 'antd-check-circle'
                    } if 'system:user:edit' in button_perms else None
                ]

    return [
        dcc.Store(id='user-button-perms-container', data=button_perms),
        # 用于导出成功后重置dcc.Download的状态，防止多次下载文件
        dcc.Store(id='user-export-complete-judge-container'),
        # 绑定的导出组件
        dcc.Download(id='user-export-container'),
        # 用户管理模块操作类型存储容器
        dcc.Store(id='user-operations-store'),
        # 用户管理模块修改操作行key存储容器
        dcc.Store(id='user-edit-id-store'),
        # 用户管理模块删除操作行key存储容器
        dcc.Store(id='user-delete-ids-store'),
        fac.AntdRow(
            [
                fac.AntdCol(
                    [
                        fac.AntdInput(
                            id='dept-input-search',
                            placeholder='请输入部门名称',
                            autoComplete='off',
                            allowClear=True,
                            prefix=fac.AntdIcon(
                                icon='antd-search'
                            ),
                            style={
                                'width': '85%'
                            }
                        ),
                        fac.AntdTree(
                            id='dept-tree',
                            treeData=tree_data,
                            defaultExpandAll=True,
                            showLine=False,
                            style={
                                'margin-top': '10px'
                            }
                        )
                    ],
                    span=4
                ),
                fac.AntdCol(
                    [
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    html.Div(
                                        [
                                            fac.AntdForm(
                                                [
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='user-user_name-input',
                                                                    placeholder='请输入用户名称',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='用户名称'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdInput(
                                                                    id='user-phone_number-input',
                                                                    placeholder='请输入手机号码',
                                                                    autoComplete='off',
                                                                    allowClear=True,
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='手机号码'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdSelect(
                                                                    id='user-status-select',
                                                                    placeholder='用户状态',
                                                                    options=[
                                                                        {
                                                                            'label': '正常',
                                                                            'value': '0'
                                                                        },
                                                                        {
                                                                            'label': '停用',
                                                                            'value': '1'
                                                                        }
                                                                    ],
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='用户状态'
                                                            ),
                                                        ],
                                                        style={
                                                            'paddingBottom': '10px'
                                                        }
                                                    ),
                                                    fac.AntdSpace(
                                                        [
                                                            fac.AntdFormItem(
                                                                fac.AntdDateRangePicker(
                                                                    id='user-create_time-range',
                                                                    style={
                                                                        'width': 240
                                                                    }
                                                                ),
                                                                label='创建时间'
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '搜索',
                                                                    id='user-search',
                                                                    type='primary',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-search'
                                                                    )
                                                                )
                                                            ),
                                                            fac.AntdFormItem(
                                                                fac.AntdButton(
                                                                    '重置',
                                                                    id='user-reset',
                                                                    icon=fac.AntdIcon(
                                                                        icon='antd-sync'
                                                                    )
                                                                )
                                                            )
                                                        ],
                                                        style={
                                                            'paddingBottom': '10px'
                                                        }
                                                    ),
                                                ],
                                                layout='inline',
                                            )
                                        ],
                                        id='user-search-form-container',
                                        hidden=False
                                    ),
                                )
                            ]
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-plus'
                                                    ),
                                                    '新增',
                                                ],
                                                id='user-add',
                                                style={
                                                    'color': '#1890ff',
                                                    'background': '#e8f4ff',
                                                    'border-color': '#a3d3ff'
                                                }
                                            ) if 'system:user:add' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-edit'
                                                    ),
                                                    '修改',
                                                ],
                                                id={
                                                    'type': 'user-operation-button',
                                                    'index': 'edit'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#71e2a3',
                                                    'background': '#e7faf0',
                                                    'border-color': '#d0f5e0'
                                                }
                                            ) if 'system:user:edit' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-minus'
                                                    ),
                                                    '删除',
                                                ],
                                                id={
                                                    'type': 'user-operation-button',
                                                    'index': 'delete'
                                                },
                                                disabled=True,
                                                style={
                                                    'color': '#ff9292',
                                                    'background': '#ffeded',
                                                    'border-color': '#ffdbdb'
                                                }
                                            ) if 'system:user:remove' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-up'
                                                    ),
                                                    '导入',
                                                ],
                                                id='user-import',
                                                style={
                                                    'color': '#909399',
                                                    'background': '#f4f4f5',
                                                    'border-color': '#d3d4d6'
                                                }
                                            ) if 'system:user:export' in button_perms else [],
                                            fac.AntdButton(
                                                [
                                                    fac.AntdIcon(
                                                        icon='antd-arrow-down'
                                                    ),
                                                    '导出',
                                                ],
                                                id='user-export',
                                                style={
                                                    'color': '#ffba00',
                                                    'background': '#fff8e6',
                                                    'border-color': '#ffe399'
                                                }
                                            ) if 'system:user:import' in button_perms else [],
                                        ],
                                        style={
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=16
                                ),
                                fac.AntdCol(
                                    fac.AntdSpace(
                                        [
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-search'
                                                            ),
                                                        ],
                                                        id='user-hidden',
                                                        shape='circle'
                                                    ),
                                                    id='user-hidden-tooltip',
                                                    title='隐藏搜索'
                                                )
                                            ),
                                            html.Div(
                                                fac.AntdTooltip(
                                                    fac.AntdButton(
                                                        [
                                                            fac.AntdIcon(
                                                                icon='antd-sync'
                                                            ),
                                                        ],
                                                        id='user-refresh',
                                                        shape='circle'
                                                    ),
                                                    title='刷新'
                                                )
                                            ),
                                        ],
                                        style={
                                            'float': 'right',
                                            'paddingBottom': '10px'
                                        }
                                    ),
                                    span=8,
                                    style={
                                        'paddingRight': '10px'
                                    }
                                )
                            ],
                            gutter=5
                        ),
                        fac.AntdRow(
                            [
                                fac.AntdCol(
                                    fac.AntdSpin(
                                        fac.AntdTable(
                                            id='user-list-table',
                                            data=table_data,
                                            columns=[
                                                {
                                                    'dataIndex': 'user_id',
                                                    'title': '用户编号',
                                                    'width': 100,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'user_name',
                                                    'title': '用户名称',
                                                    'width': 120,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'nick_name',
                                                    'title': '用户昵称',
                                                    'width': 120,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'dept_name',
                                                    'title': '部门',
                                                    'width': 130,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'phonenumber',
                                                    'title': '手机号码',
                                                    'width': 130,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'status',
                                                    'title': '状态',
                                                    'width': 110,
                                                    'renderOptions': {
                                                        'renderType': 'switch'
                                                    },
                                                },
                                                {
                                                    'dataIndex': 'create_time',
                                                    'title': '创建时间',
                                                    'width': 160,
                                                    'renderOptions': {
                                                        'renderType': 'ellipsis'
                                                    },
                                                },
                                                {
                                                    'title': '操作',
                                                    'dataIndex': 'operation',
                                                    'renderOptions': {
                                                        'renderType': 'dropdown',
                                                        'dropdownProps': {
                                                            'title': '更多'
                                                        }
                                                    },
                                                }
                                            ],
                                            rowSelectionType='checkbox',
                                            rowSelectionWidth=50,
                                            bordered=True,
                                            pagination={
                                                'pageSize': page_size,
                                                'current': page_num,
                                                'showSizeChanger': True,
                                                'pageSizeOptions': [10, 30, 50, 100],
                                                'showQuickJumper': True,
                                                'total': total
                                            },
                                            mode='server-side',
                                            style={
                                                'width': '100%',
                                                'paddingRight': '10px'
                                            }
                                        ),
                                        text='数据加载中'
                                    ),
                                )
                            ]
                        ),
                    ],
                    span=20
                )
            ],
            gutter=5
        ),

        # 新增用户表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'nick_name'
                                        },
                                        placeholder='请输入用户昵称',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户昵称',
                                    required=True,
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'nick_name',
                                        'required': True
                                    }
                                ),
                                fac.AntdFormItem(
                                    fac.AntdTreeSelect(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'dept_id'
                                        },
                                        placeholder='请选择归属部门',
                                        treeData=[],
                                        treeNodeFilterProp='title',
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='归属部门',
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'dept_id',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 1
                                    },
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'phonenumber'
                                        },
                                        placeholder='请输入手机号码',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='手机号码',
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'phonenumber',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 1
                                    },
                                ),
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'email'
                                        },
                                        placeholder='请输入邮箱',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='邮箱',
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'email',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 5
                                    },
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'user_name'
                                        },
                                        placeholder='请输入用户名称',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户名称',
                                    required=True,
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'user_name',
                                        'required': True
                                    }
                                ),
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'password'
                                        },
                                        placeholder='请输入密码',
                                        mode='password',
                                        passwordUseMd5=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户密码',
                                    required=True,
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'password',
                                        'required': True
                                    }
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdSelect(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'sex'
                                        },
                                        placeholder='请选择性别',
                                        options=[
                                            {
                                                'label': '男',
                                                'value': '0'
                                            },
                                            {
                                                'label': '女',
                                                'value': '1'
                                            },
                                            {
                                                'label': '未知',
                                                'value': '2'
                                            },
                                        ],
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户性别',
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'sex',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 1
                                    },
                                ),
                                fac.AntdFormItem(
                                    fac.AntdRadioGroup(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'status'
                                        },
                                        options=[
                                            {
                                                'label': '正常',
                                                'value': '0'
                                            },
                                            {
                                                'label': '停用',
                                                'value': '1'
                                            },
                                        ],
                                        defaultValue='0',
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户状态',
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'status',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 2
                                    },
                                )
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdSelect(
                                        id='user-add-post',
                                        placeholder='请选择岗位',
                                        options=[],
                                        mode='multiple',
                                        optionFilterProp='label',
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='岗位',
                                    id='user-add-post-form-item',
                                    labelCol={
                                        'offset': 4
                                    },
                                ),
                                fac.AntdFormItem(
                                    fac.AntdSelect(
                                        id='user-add-role',
                                        placeholder='请选择角色',
                                        options=[],
                                        mode='multiple',
                                        optionFilterProp='label',
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='角色',
                                    id='user-add-role-form-item',
                                    labelCol={
                                        'offset': 8
                                    },
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_add-form-value',
                                            'index': 'remark'
                                        },
                                        placeholder='请输入内容',
                                        allowClear=True,
                                        mode='text-area',
                                        style={
                                            'width': 490
                                        }
                                    ),
                                    label='备注',
                                    id={
                                        'type': 'user_add-form-label',
                                        'index': 'remark',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 2
                                    },
                                ),
                            ]
                        )
                    ]
                )
            ],
            id='user-add-modal',
            title='新增用户',
            mask=False,
            width=650,
            renderFooter=True,
            okClickClose=False
        ),

        # 编辑用户表单modal
        fac.AntdModal(
            [
                fac.AntdForm(
                    [
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_edit-form-value',
                                            'index': 'nick_name'
                                        },
                                        placeholder='请输入用户昵称',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户昵称',
                                    required=True,
                                    id={
                                        'type': 'user_edit-form-label',
                                        'index': 'nick_name',
                                        'required': True
                                    }
                                ),
                                fac.AntdFormItem(
                                    fac.AntdTreeSelect(
                                        id={
                                            'type': 'user_edit-form-value',
                                            'index': 'dept_id'
                                        },
                                        placeholder='请选择归属部门',
                                        treeData=[],
                                        treeNodeFilterProp='title',
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='归属部门',
                                    id={
                                        'type': 'user_edit-form-label',
                                        'index': 'dept_id',
                                        'required': False
                                    }
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_edit-form-value',
                                            'index': 'phonenumber'
                                        },
                                        placeholder='请输入手机号码',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='手机号码',
                                    id={
                                        'type': 'user_edit-form-label',
                                        'index': 'phonenumber',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 1
                                    },
                                ),
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_edit-form-value',
                                            'index': 'email'
                                        },
                                        placeholder='请输入邮箱',
                                        allowClear=True,
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='邮箱',
                                    id={
                                        'type': 'user_edit-form-label',
                                        'index': 'email',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 4
                                    },
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdSelect(
                                        id={
                                            'type': 'user_edit-form-value',
                                            'index': 'sex'
                                        },
                                        placeholder='请选择性别',
                                        options=[
                                            {
                                                'label': '男',
                                                'value': '0'
                                            },
                                            {
                                                'label': '女',
                                                'value': '1'
                                            },
                                            {
                                                'label': '未知',
                                                'value': '2'
                                            },
                                        ],
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户性别',
                                    id={
                                        'type': 'user_edit-form-label',
                                        'index': 'sex',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 1
                                    },
                                ),
                                fac.AntdFormItem(
                                    fac.AntdRadioGroup(
                                        id={
                                            'type': 'user_edit-form-value',
                                            'index': 'status'
                                        },
                                        options=[
                                            {
                                                'label': '正常',
                                                'value': '0'
                                            },
                                            {
                                                'label': '停用',
                                                'value': '1'
                                            },
                                        ],
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='用户状态',
                                    id={
                                        'type': 'user_edit-form-label',
                                        'index': 'status',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 1
                                    },
                                )
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdSelect(
                                        id='user-edit-post',
                                        placeholder='请选择岗位',
                                        options=[],
                                        mode='multiple',
                                        optionFilterProp='label',
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='岗位',
                                    id='user-edit-post-form-item',
                                    labelCol={
                                        'offset': 4
                                    },
                                ),
                                fac.AntdFormItem(
                                    fac.AntdSelect(
                                        id='user-edit-role',
                                        placeholder='请选择角色',
                                        options=[],
                                        mode='multiple',
                                        optionFilterProp='label',
                                        style={
                                            'width': 200
                                        }
                                    ),
                                    label='角色',
                                    id='user-edit-role-form-item',
                                    labelCol={
                                        'offset': 7
                                    },
                                ),
                            ],
                            size="middle"
                        ),
                        fac.AntdSpace(
                            [
                                fac.AntdFormItem(
                                    fac.AntdInput(
                                        id={
                                            'type': 'user_edit-form-value',
                                            'index': 'remark'
                                        },
                                        placeholder='请输入内容',
                                        allowClear=True,
                                        mode='text-area',
                                        style={
                                            'width': 485
                                        }
                                    ),
                                    label='备注',
                                    id={
                                        'type': 'user_edit-form-label',
                                        'index': 'remark',
                                        'required': False
                                    },
                                    labelCol={
                                        'offset': 2
                                    },
                                ),
                            ]
                        )
                    ]
                )
            ],
            id='user-edit-modal',
            title='编辑用户',
            mask=False,
            width=650,
            renderFooter=True,
            okClickClose=False
        ),

        # 删除用户二次确认modal
        fac.AntdModal(
            fac.AntdText('是否确认删除？', id='user-delete-text'),
            id='user-delete-confirm-modal',
            visible=False,
            title='提示',
            renderFooter=True,
            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(
            [
                fac.AntdForm(
                    [
                        fac.AntdFormItem(
                            fac.AntdInput(
                                id='reset-password-input',
                                mode='password'
                            ),
                            label='请输入新密码'
                        ),
                    ],
                    layout='vertical'
                ),
                dcc.Store(id='reset-password-row-key-store')
            ],
            id='user-reset-password-confirm-modal',
            visible=False,
            title='重置密码',
            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
        )
    ]

dash-fastapi-frontend/views/system/user/__init__.py
insistence/Dash-FastAPI-Admin
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
        ),
    ]

dash-fastapi-frontend/views/system/user/allocate_role.py
insistence/Dash-FastAPI-Admin
from . import (
    query_form_table
)

dash-fastapi-frontend/views/system/user/component/__init__.py
insistence/Dash-FastAPI-Admin
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
        )

dash-fastapi-frontend/views/system/user/component/query_form_table.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_utils_components as fuc
import feffery_antd_components as fac
from flask import session
from . import user_avatar, user_info, reset_pwd


def render(button_perms):

    return [
        fac.AntdRow(
            [
                fac.AntdCol(
                    fac.AntdCard(
                        [
                            html.Div(
                                [
                                    html.Div(
                                        user_avatar.render(),
                                        style={
                                            'textAlign': 'center',
                                            'marginBottom': '10px'
                                        }
                                    ),
                                    html.Ul(
                                        [
                                            html.Li(
                                                [
                                                    fac.AntdIcon(icon='antd-user'),
                                                    fac.AntdText('用户名称'),
                                                    html.Div(
                                                        session.get('user_info').get('user_name'),
                                                        id='profile_c-username',
                                                        className='pull-right'
                                                    )
                                                ],
                                                className='list-group-item'
                                            ),
                                            html.Li(
                                                [
                                                    fac.AntdIcon(icon='antd-mobile'),
                                                    fac.AntdText('手机号码'),
                                                    html.Div(
                                                        session.get('user_info').get('phonenumber'),
                                                        id='profile_c-phonenumber',
                                                        className='pull-right'
                                                    )
                                                ],
                                                className='list-group-item'
                                            ),
                                            html.Li(
                                                [
                                                    fac.AntdIcon(icon='antd-mail'),
                                                    fac.AntdText('用户邮箱'),
                                                    html.Div(
                                                        session.get('user_info').get('email'),
                                                        id='profile_c-email',
                                                        className='pull-right'
                                                    )
                                                ],
                                                className='list-group-item'
                                            ),
                                            html.Li(
                                                [
                                                    fac.AntdIcon(icon='antd-cluster'),
                                                    fac.AntdText('所属部门'),
                                                    html.Div(
                                                        session.get('dept_info').get('dept_name') + "/" + ','.join(
                                                            [item.get('post_name') for item in
                                                             session.get('post_info')]),
                                                        id='profile_c-dept',
                                                        className='pull-right'
                                                    )
                                                ],
                                                className='list-group-item'
                                            ),
                                            html.Li(
                                                [
                                                    fac.AntdIcon(icon='antd-team'),
                                                    fac.AntdText('所属角色'),
                                                    html.Div(
                                                        ','.join([item.get('role_name') for item in
                                                                  session.get('role_info')]),
                                                        id='profile_c-role',
                                                        className='pull-right'
                                                    )
                                                ],
                                                className='list-group-item'
                                            ),
                                            html.Li(
                                                [
                                                    fac.AntdIcon(icon='antd-schedule'),
                                                    fac.AntdText('创建日期'),
                                                    html.Div(
                                                        session.get('user_info').get('create_time'),
                                                        id='profile_c-create_time',
                                                        className='pull-right'
                                                    )
                                                ],
                                                className='list-group-item'
                                            ),
                                        ],
                                        className='list-group list-group-striped'
                                    ),
                                    fuc.FefferyStyle(
                                        rawStyle=
                                        '''
                                        .list-group-striped > .list-group-item {
                                            border-left: 0;
                                            border-right: 0;
                                            border-radius: 0;
                                            padding-left: 0;
                                            padding-right: 0;
                                        }
                                        
                                        .list-group {
                                            padding-left: 0px;
                                            list-style: none;
                                        }
                                        
                                        .list-group-item {
                                            border-bottom: 1px solid #e7eaec;
                                            border-top: 1px solid #e7eaec;
                                            margin-bottom: -1px;
                                            padding: 11px 0px;
                                            font-size: 13px;
                                        }
                                        
                                        .pull-right {
                                            float: right !important;
                                        }                    
                                        '''
                                    )
                                ],
                                style={
                                    'width': '100%'
                                }
                            ),
                        ],
                        title='个人信息',
                        size='small',
                        style={
                            'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                        }
                    ),
                    span=10
                ),
                fac.AntdCol(
                    fac.AntdCard(
                        [
                            fac.AntdTabs(
                                items=[
                                    {
                                        'key': '基本资料',
                                        'label': '基本资料',
                                        'children': user_info.render()
                                    },
                                    {
                                        'key': '修改密码',
                                        'label': '修改密码',
                                        'children': reset_pwd.render()
                                    }
                                ],
                                style={
                                    'width': '100%'
                                }
                            )
                        ],
                        'size="small"',
                        title='基本资料',
                        size='small',
                        style={
                            'boxShadow': 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'
                        }
                    ),
                    span=14
                ),
            ],
            gutter=10
        ),
    ]

dash-fastapi-frontend/views/system/user/profile/__init__.py
insistence/Dash-FastAPI-Admin
import feffery_antd_components as fac

import callbacks.system_c.user_c.profile_c.reset_pwd_c


def render():
    return fac.AntdForm(
        [
            fac.AntdFormItem(
                fac.AntdInput(
                    id='reset-old-password',
                    mode='password'
                ),
                id='reset-old-password-form-item',
                label='旧密码',
                required=True
            ),
            fac.AntdFormItem(
                fac.AntdInput(
                    id='reset-new-password',
                    mode='password'
                ),
                id='reset-new-password-form-item',
                label='新密码',
                required=True
            ),
            fac.AntdFormItem(
                fac.AntdInput(
                    id='reset-confirm-password',
                    mode='password'
                ),
                id='reset-confirm-password-form-item',
                label='确认密码',
                required=True
            ),
            fac.AntdFormItem(
                fac.AntdSpace(
                    [
                        fac.AntdButton(
                            '保存',
                            id='reset-password-submit',
                            type='primary'
                        ),
                        fac.AntdButton(
                            '关闭',
                            id='reset-password-close',
                            type='primary',
                            danger=True
                        ),
                    ],
                ),
                wrapperCol={
                    'offset': 4
                }
            )
        ],
        labelCol={
            'span': 4
        },
        wrapperCol={
            'span': 20
        },
        style={
            'margin': '0 auto'  # 以快捷实现居中布局效果
        }
    )

dash-fastapi-frontend/views/system/user/profile/reset_pwd.py
insistence/Dash-FastAPI-Admin
from dash import html, dcc
import feffery_utils_components as fuc
import feffery_antd_components as fac
from flask import session

from config.global_config import ApiBaseUrlConfig
import callbacks.system_c.user_c.profile_c.avatar_c


def render():
    return [
        dcc.Store(id='init-cropper'),
        dcc.Store(id='avatar-src-data'),
        # 监听裁剪的图片数据
        fuc.FefferySessionStorage(
            id='cropper-avatar-base64'
        ),
        html.Div(
            [
                fac.AntdImage(
                    id='user-avatar-image-info',
                    src=session.get('user_info').get('avatar'),
                    preview=False,
                    height='120px',
                    width='120px',
                    style={
                        'borderRadius': '50%'
                    }
                )
            ],
            id='avatar-edit-click',
            className='user-info-head'
        ),
        fuc.FefferyStyle(
            rawStyle='''
            .user-info-head {
              position: relative;
              display: inline-block;
              height: 120px;
            }

            .user-info-head:hover:after {
              content: '+';
              position: absolute;
              left: 0;
              right: 0;
              top: 0;
              bottom: 0;
              color: #eee;
              background: rgba(0, 0, 0, 0.5);
              font-size: 24px;
              font-style: normal;
              -webkit-font-smoothing: antialiased;
              -moz-osx-font-smoothing: grayscale;
              cursor: pointer;
              line-height: 110px;
              border-radius: 50%;
            }
            '''
        ),
        fuc.FefferyExecuteJs(id='avatar-cropper'),
        fac.AntdModal(
            [
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            [
                                html.Div(
                                    [
                                        html.Img(
                                            id='user-avatar-image',
                                            height='120px',
                                            width='120px'
                                        ),
                                    ],
                                    id='avatar-cropper-container',
                                    style={
                                        'height': '350px',
                                        'width': '100%'
                                    }
                                ),
                            ],
                            span=12
                        ),
                        fac.AntdCol(
                            [
                                html.Div(
                                    id='user-avatar-image-preview',
                                    className='avatar-upload-preview'
                                ),
                                fuc.FefferyStyle(
                                    rawStyle="""
                                    .avatar-upload-preview {
                                        margin: 18% auto;
                                        width: 220px;
                                        height: 220px;
                                        border-radius: 50%;
                                        box-shadow: 0 0 4px #ccc;
                                        overflow: hidden;
                                    }
                                    """
                                )
                            ],
                            span=12
                        )
                    ]
                ),
                html.Br(),
                fac.AntdRow(
                    [
                        fac.AntdCol(
                            fac.AntdUpload(
                                id='avatar-upload-choose',
                                apiUrl=f'{ApiBaseUrlConfig.BaseUrl}/common/upload',
                                downloadUrl=f'{ApiBaseUrlConfig.BaseUrl}/common/caches',
                                headers={'Authorization': 'Bearer ' + session.get('Authorization')},
                                fileMaxSize=10,
                                showUploadList=False,
                                fileTypes=['jpeg', 'jpg', 'png'],
                                buttonContent='选择'
                            ),
                            span=4
                        ),
                        fac.AntdCol(
                            fac.AntdButton(
                                id='zoom-out',
                                icon=fac.AntdIcon(
                                    icon='antd-plus'
                                )
                            ),
                            span=2
                        ),
                        fac.AntdCol(
                            fac.AntdButton(
                                id='zoom-in',
                                icon=fac.AntdIcon(
                                    icon='antd-minus'
                                )
                            ),
                            span=2
                        ),
                        fac.AntdCol(
                            fac.AntdButton(
                                icon=fac.AntdIcon(
                                    id='rotate-left',
                                    icon='antd-undo'
                                )
                            ),
                            span=2
                        ),
                        fac.AntdCol(
                            fac.AntdButton(
                                icon=fac.AntdIcon(
                                    id='rotate-right',
                                    icon='antd-redo'
                                )
                            ),
                            span=7
                        ),
                        fac.AntdCol(
                            fac.AntdButton(
                                '提交',
                                id='change-avatar-submit',
                                type='primary'
                            ),
                            span=7
                        ),
                    ],
                    gutter=10
                )
            ],
            id='avatar-cropper-modal',
            title='修改头像',
            width=850,
            mask=False
        )
    ]

dash-fastapi-frontend/views/system/user/profile/user_avatar.py
insistence/Dash-FastAPI-Admin
import feffery_antd_components as fac

import callbacks.system_c.user_c.profile_c.user_info_c


def render():
    return fac.AntdForm(
        [
            fac.AntdFormItem(
                fac.AntdInput(
                    id='reset-user-nick_name',
                    placeholder='请输入用户昵称'
                ),
                id='reset-user-nick_name-form-item',
                label='用户昵称',
                required=True
            ),
            fac.AntdFormItem(
                fac.AntdInput(
                    id='reset-user-phonenumber',
                    placeholder='请输入手机号码'
                ),
                id='reset-user-phonenumber-form-item',
                label='手机号码',
                required=True
            ),
            fac.AntdFormItem(
                fac.AntdInput(
                    id='reset-user-email',
                    placeholder='请输入邮箱'
                ),
                id='reset-user-email-form-item',
                label='邮箱',
                required=True
            ),
            fac.AntdFormItem(
                fac.AntdRadioGroup(
                    id='reset-user-sex',
                    options=[
                        {
                            'label': '男',
                            'value': '0'
                        },
                        {
                            'label': '女',
                            'value': '1'
                        }
                    ],
                    defaultValue='1'
                ),
                id='reset-user-sex-form-item',
                label='性别'
            ),
            fac.AntdFormItem(
                fac.AntdSpace(
                    [
                        fac.AntdButton(
                            '保存',
                            id='reset-submit',
                            type='primary'
                        ),
                        fac.AntdButton(
                            '关闭',
                            id='reset-close',
                            type='primary',
                            danger=True
                        ),
                    ],
                ),
                wrapperCol={
                    'offset': 4
                }
            )
        ],
        labelCol={
            'span': 4
        },
        wrapperCol={
            'span': 20
        },
        style={
            'margin': '0 auto'  # 以快捷实现居中布局效果
        }
    )

dash-fastapi-frontend/views/system/user/profile/user_info.py
insistence/Dash-FastAPI-Admin
from . import (
    build,
    gen,
    swagger,
)

dash-fastapi-frontend/views/tool/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_utils_components as fuc
import feffery_antd_components as fac


def render(button_perms):

    return html.Div('我是表单构建')

dash-fastapi-frontend/views/tool/build/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_utils_components as fuc
import feffery_antd_components as fac


def render(button_perms):

    return html.Div('我是代码生成')

dash-fastapi-frontend/views/tool/gen/__init__.py
insistence/Dash-FastAPI-Admin
from dash import html
import feffery_utils_components as fuc
from config.global_config import ApiBaseUrlConfig


def render(button_perms):

    return [
        html.Div(
            [
                fuc.FefferyStyle(
                    rawStyle="""
                    iframe {
                        border: none;
                        width: 100%;
                        height: 100%;
                        display: block
                    }
                    """
                ),
                html.Iframe(
                    src=f'{ApiBaseUrlConfig.BaseUrl}/docs'
                )
            ],
            id='swagger-docs-container',
            style={
                'position': 'relative',
                'height': 'calc(100vh - 120px)'
            }
        )
    ]

dash-fastapi-frontend/views/tool/swagger/__init__.py
insistence/Dash-FastAPI-Admin
from waitress import serve
from app import app

serve(
    app.server,
    host='0.0.0.0',
    port=8088
)

dash-fastapi-frontend/wsgi.py
insistence/Dash-FastAPI-Admin