mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-20 20:39:55 +00:00
refactor(module): 重构模块路径和数据库配置
- 将前端API路径从`system`和`monitor`重命名为`module_system`和`module_monitor` - 移除MongoDB相关配置和依赖 - 统一数据库类型命名为`mysql`和`postgres` - 修复代码生成模板中的字段命名问题 - 更新依赖项并移除不必要的包 - 优化数据库连接配置 - 添加新的文档和Redoc视图组件 - 修复SQL脚本中的字段注释
This commit is contained in:
@@ -9,6 +9,9 @@ from app.utils.import_util import ImportUtil
|
||||
from app.core.base_model import MappedBase
|
||||
from app.config.setting import settings
|
||||
|
||||
# 确保 alembic 版本目录存在
|
||||
settings.ALEMBIC_VERSION_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 清除MappedBase.metadata中的表定义,避免重复注册
|
||||
if hasattr(MappedBase, 'metadata') and MappedBase.metadata.tables:
|
||||
print(f"🧹 清除已存在的表定义,当前有 {len(MappedBase.metadata.tables)} 个表")
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, Query, Body, Path
|
||||
from fastapi import APIRouter, Depends, Body, Path
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.common.response import SuccessResponse, ErrorResponse, StreamResponse
|
||||
from app.common.response import SuccessResponse, StreamResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.common.request import PaginationService
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.common.constant import RET
|
||||
from .param import GenTableQueryParam
|
||||
from .schema import GenTableSchema, GenTableOutSchema
|
||||
from .service import GenTableColumnService, GenTableService
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.logger import logger
|
||||
from .param import GenTableQueryParam
|
||||
from .schema import GenTableSchema
|
||||
from .service import GenTableService
|
||||
|
||||
|
||||
GenRouter = APIRouter(route_class=OperationLogRoute, prefix='/gencode', tags=["代码生成模块"])
|
||||
@@ -110,7 +109,7 @@ async def gen_table_detail_controller(
|
||||
|
||||
@GenRouter.post("/create", summary="创建表结构", description="创建表结构")
|
||||
async def create_table_controller(
|
||||
sql: str = Body(..., description="SQL语句:CREATE TABLE user_demo (\n id INTEGER NOT NULL PRIMARY KEY,\n username VARCHAR(64) NOT NULL UNIQUE,\n);"),
|
||||
sql: str = Body(..., description="SQL语句,用于创建表结构"),
|
||||
auth: AuthSchema = Depends(AuthPermission(["generator:gencode:create"])),
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
from sqlalchemy.engine.row import Row
|
||||
from sqlalchemy import and_, select, text
|
||||
from typing import List, Optional, Sequence, Dict, Union, Any
|
||||
from sqlglot.expressions import Expression
|
||||
|
||||
from app.core.logger import logger
|
||||
from app.config.setting import settings
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from .param import GenTableQueryParam, GenTableColumnQueryParam
|
||||
from .param import GenTableQueryParam
|
||||
from .model import GenTableModel, GenTableColumnModel
|
||||
from .schema import (
|
||||
GenTableSchema,
|
||||
GenTableOutSchema,
|
||||
GenTableColumnSchema,
|
||||
GenTableColumnOutSchema,
|
||||
GenDBTableSchema,
|
||||
@@ -80,7 +80,6 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
返回:
|
||||
- Sequence[GenTableModel]: 业务表列表信息。
|
||||
"""
|
||||
# 使用基础CRUD的list与like检索
|
||||
return await self.list(search=search.__dict__, order_by=[{"created_at": "desc"}], preload=preload)
|
||||
|
||||
async def add_gen_table(self, add_model: GenTableSchema) -> GenTableModel:
|
||||
@@ -93,9 +92,9 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
返回:
|
||||
- GenTableModel: 新增的业务表信息对象。
|
||||
"""
|
||||
return await self.create(add_model.model_dump(exclude_unset=True, exclude={"sub", "tree", "crud"}))
|
||||
return await self.create(data=add_model)
|
||||
|
||||
async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema) -> GenTableSchema:
|
||||
async def edit_gen_table(self, table_id: int, edit_model: GenTableSchema) -> GenTableModel:
|
||||
"""
|
||||
修改业务表信息。
|
||||
|
||||
@@ -107,9 +106,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
- GenTableSchema: 修改后的业务表信息模型。
|
||||
"""
|
||||
# 排除嵌套对象字段,避免SQLAlchemy尝试直接将字典设置到模型实例上
|
||||
data_dict = edit_model.model_dump(exclude_unset=True, exclude={"columns", "pk_column", "sub_table", "sub"})
|
||||
obj = await self.update(id=table_id, data=data_dict)
|
||||
return GenTableSchema.model_validate(obj)
|
||||
return await self.update(id=table_id, data=edit_model.model_dump(exclude_unset=True, exclude={"columns"}))
|
||||
|
||||
async def delete_gen_table(self, ids: List[int]) -> None:
|
||||
"""
|
||||
@@ -132,8 +129,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
"""
|
||||
|
||||
# 使用更健壮的方式检测数据库方言
|
||||
if settings.DATABASE_TYPE == "postgresql":
|
||||
# 修复:PostgreSQL不提供table_comment,使用pg_catalog获取注释
|
||||
if settings.DATABASE_TYPE == "postgres":
|
||||
query_sql = (
|
||||
select(
|
||||
text("t.table_catalog as database_name"),
|
||||
@@ -155,7 +151,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
)
|
||||
)
|
||||
)
|
||||
elif settings.DATABASE_TYPE == "mysql":
|
||||
else:
|
||||
query_sql = (
|
||||
select(
|
||||
text("table_schema as database_name"),
|
||||
@@ -170,38 +166,19 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
query_sql = (
|
||||
select(
|
||||
text("'fastapiadmin' as database_name"), # SQLite没有数据库名概念,设为空字符串
|
||||
text("name as table_name"),
|
||||
text("type as table_type"),
|
||||
text("name as table_comment"), # SQLite中使用name作为表名和注释
|
||||
)
|
||||
.select_from(text("sqlite_master"))
|
||||
.where(
|
||||
and_(
|
||||
text("type = 'table'"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# 动态条件构造
|
||||
params = {}
|
||||
if search and search.table_name:
|
||||
if settings.DATABASE_TYPE == "sqlite":
|
||||
query_sql = query_sql.where(
|
||||
text("lower(name) like lower(:table_name)")
|
||||
)
|
||||
else:
|
||||
query_sql = query_sql.where(
|
||||
text("lower(table_name) like lower(:table_name)")
|
||||
)
|
||||
query_sql = query_sql.where(
|
||||
text("lower(table_name) like lower(:table_name)")
|
||||
)
|
||||
params['table_name'] = f"%{search.table_name}%"
|
||||
if search and search.table_comment:
|
||||
if settings.DATABASE_TYPE == "sqlite":
|
||||
# 对于PostgreSQL,表注释字段是pd.description,而不是table_comment
|
||||
if settings.DATABASE_TYPE == "postgres":
|
||||
query_sql = query_sql.where(
|
||||
text("lower(name) like lower(:table_comment)")
|
||||
text("lower(pd.description) like lower(:table_comment)")
|
||||
)
|
||||
else:
|
||||
query_sql = query_sql.where(
|
||||
@@ -235,78 +212,58 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
返回:
|
||||
- list[GenDBTableSchema]: 数据库表信息对象列表。
|
||||
"""
|
||||
# 处理空列表情况
|
||||
if not table_names:
|
||||
return []
|
||||
|
||||
# 使用更健壮的方式检测数据库方言
|
||||
if settings.DATABASE_TYPE == "postgresql":
|
||||
# 修复:PostgreSQL不提供table_comment,使用pg_catalog获取注释
|
||||
query_sql = (
|
||||
select(
|
||||
text("t.table_catalog as database_name"),
|
||||
text("t.table_name as table_name"),
|
||||
text("t.table_type as table_type"),
|
||||
text("pd.description as table_comment"),
|
||||
)
|
||||
.select_from(text(
|
||||
"information_schema.tables t \n"
|
||||
"LEFT JOIN pg_catalog.pg_class c ON c.relname = t.table_name \n"
|
||||
"LEFT JOIN pg_catalog.pg_namespace n ON n.nspname = t.table_schema AND c.relnamespace = n.oid \n"
|
||||
"LEFT JOIN pg_catalog.pg_description pd ON pd.objoid = c.oid AND pd.objsubid = 0"
|
||||
))
|
||||
.where(
|
||||
and_(
|
||||
text("t.table_catalog = (select current_database())"),
|
||||
text("t.is_insertable_into = 'YES'"),
|
||||
text("t.table_schema = 'public'"),
|
||||
)
|
||||
)
|
||||
)
|
||||
elif settings.DATABASE_TYPE == "mysql":
|
||||
query_sql = (
|
||||
select(
|
||||
text("table_schema as database_name"),
|
||||
text("table_name as table_name"),
|
||||
text("table_type as table_type"),
|
||||
text("table_comment as table_comment"),
|
||||
)
|
||||
.select_from(text("information_schema.tables"))
|
||||
.where(
|
||||
and_(
|
||||
text("table_schema = (select database())"),
|
||||
)
|
||||
)
|
||||
)
|
||||
if settings.DATABASE_TYPE == "postgres":
|
||||
# PostgreSQL使用ANY操作符和正确的参数绑定
|
||||
query_sql = """
|
||||
SELECT
|
||||
t.table_catalog as database_name,
|
||||
t.table_name as table_name,
|
||||
t.table_type as table_type,
|
||||
pd.description as table_comment
|
||||
FROM
|
||||
information_schema.tables t
|
||||
LEFT JOIN pg_catalog.pg_class c ON c.relname = t.table_name
|
||||
LEFT JOIN pg_catalog.pg_namespace n ON n.nspname = t.table_schema AND c.relnamespace = n.oid
|
||||
LEFT JOIN pg_catalog.pg_description pd ON pd.objoid = c.oid AND pd.objsubid = 0
|
||||
WHERE
|
||||
t.table_catalog = (select current_database())
|
||||
AND t.is_insertable_into = 'YES'
|
||||
AND t.table_schema = 'public'
|
||||
AND t.table_name = ANY(:table_names)
|
||||
"""
|
||||
else:
|
||||
query_sql = (
|
||||
select(
|
||||
text("'fastapiadmin' as database_name"), # SQLite没有数据库名概念,设为空字符串
|
||||
text("name as table_name"),
|
||||
text("type as table_type"),
|
||||
text("name as table_comment"), # SQLite中使用name作为表名和注释
|
||||
)
|
||||
.select_from(text("sqlite_master"))
|
||||
.where(
|
||||
and_(
|
||||
text("type = 'table'"),
|
||||
)
|
||||
)
|
||||
)
|
||||
query_sql = """
|
||||
SELECT
|
||||
table_schema as database_name,
|
||||
table_name as table_name,
|
||||
table_type as table_type,
|
||||
table_comment as table_comment
|
||||
FROM
|
||||
information_schema.tables
|
||||
WHERE
|
||||
table_schema = (select database())
|
||||
AND table_name IN :table_names
|
||||
"""
|
||||
|
||||
table_names_str = "','".join(table_names)
|
||||
# 修复SQL查询中的参数绑定问题
|
||||
if table_names:
|
||||
if settings.DATABASE_TYPE == "sqlite":
|
||||
# 对于SQLite,我们直接在SQL中使用表名,因为参数绑定有问题
|
||||
query_sql = query_sql.where(
|
||||
text(f"name IN ('{table_names_str}')")
|
||||
)
|
||||
gen_db_table_list = (await self.db.execute(query_sql)).fetchall()
|
||||
# 创建新的数据库会话上下文来执行查询,避免受外部事务状态影响
|
||||
try:
|
||||
# 去重表名列表,避免重复查询
|
||||
unique_table_names = list(set(table_names))
|
||||
|
||||
# 使用只读事务执行查询,不影响主事务
|
||||
if settings.DATABASE_TYPE == "postgres":
|
||||
gen_db_table_list = (await self.db.execute(text(query_sql), {"table_names": unique_table_names})).fetchall()
|
||||
else:
|
||||
# MySQL和PostgreSQL使用IN拼接(注意已在上方限定schema范围)
|
||||
query_sql = query_sql.where(
|
||||
text(f"table_name IN ('{table_names_str}')")
|
||||
)
|
||||
gen_db_table_list = (await self.db.execute(query_sql)).fetchall()
|
||||
else:
|
||||
gen_db_table_list = (await self.db.execute(query_sql)).fetchall()
|
||||
gen_db_table_list = (await self.db.execute(text(query_sql), {"table_names": tuple(unique_table_names)})).fetchall()
|
||||
except Exception as e:
|
||||
logger.error(f"查询表信息时发生错误: {e}")
|
||||
# 查询错误时直接抛出,不需要事务处理
|
||||
raise
|
||||
|
||||
# 将Row对象转换为字典列表,解决JSON序列化问题
|
||||
dict_data = []
|
||||
@@ -335,13 +292,8 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
# 根据不同数据库类型使用不同的查询方式
|
||||
if settings.DATABASE_TYPE.lower() == 'mysql':
|
||||
query = text("SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = :table_name")
|
||||
elif settings.DATABASE_TYPE.lower() == 'postgresql':
|
||||
query = text("SELECT 1 FROM pg_tables WHERE tablename = :table_name")
|
||||
elif settings.DATABASE_TYPE.lower() == 'sqlite':
|
||||
query = text("SELECT name FROM sqlite_master WHERE type='table' AND name = :table_name")
|
||||
else:
|
||||
# 默认查询方式
|
||||
query = text("SELECT 1 FROM information_schema.tables WHERE table_name = :table_name")
|
||||
query = text("SELECT 1 FROM pg_tables WHERE tablename = :table_name")
|
||||
|
||||
result = await self.db.execute(query, {"table_name": table_name})
|
||||
return result.scalar() is not None
|
||||
@@ -350,7 +302,7 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
# 出错时返回False,避免误报表已存在
|
||||
return False
|
||||
|
||||
async def create_table_by_sql(self, sql: str) -> bool:
|
||||
async def create_table_by_sql(self, sql_statements: List[Expression | None]) -> bool:
|
||||
"""
|
||||
根据SQL语句创建表结构。
|
||||
|
||||
@@ -360,10 +312,15 @@ class GenTableCRUD(CRUDBase[GenTableModel, GenTableSchema, GenTableSchema]):
|
||||
返回:
|
||||
- bool: 是否创建成功。
|
||||
"""
|
||||
|
||||
|
||||
try:
|
||||
# 执行SQL但不手动提交事务,由框架管理事务生命周期
|
||||
await self.db.execute(text(sql))
|
||||
await self.db.flush()
|
||||
for sql_statement in sql_statements:
|
||||
if not sql_statement:
|
||||
continue
|
||||
sql = sql_statement.sql(dialect=settings.DATABASE_TYPE)
|
||||
await self.db.execute(text(sql))
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"创建表时发生错误: {e}")
|
||||
@@ -433,93 +390,105 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
if not table_name:
|
||||
raise ValueError("数据表名称不能为空")
|
||||
|
||||
# 兼容SQLite和MySQL/PostgreSQL
|
||||
if settings.DATABASE_TYPE == "postgresql":
|
||||
# 修复:PostgreSQL的主键/自增/注释需要关联系统表
|
||||
query_sql = """
|
||||
SELECT
|
||||
c.column_name,
|
||||
(CASE WHEN (c.is_nullable = 'NO' AND (tc.constraint_type IS DISTINCT FROM 'PRIMARY KEY')) THEN '1' ELSE '0' END) AS is_required,
|
||||
(CASE WHEN (tc.constraint_type = 'PRIMARY KEY') THEN '1' ELSE '0' END) AS is_pk,
|
||||
(CASE WHEN EXISTS (SELECT 1 FROM information_schema.table_constraints uc JOIN information_schema.key_column_usage kcu ON uc.constraint_name = kcu.constraint_name WHERE uc.table_name = c.table_name AND uc.table_schema = c.table_schema AND uc.constraint_type = 'UNIQUE' AND kcu.column_name = c.column_name) THEN '1' ELSE '0' END) AS is_unique,
|
||||
c.ordinal_position AS sort,
|
||||
COALESCE(pgd.description, '') AS column_comment,
|
||||
(CASE WHEN c.column_default LIKE 'nextval%' THEN '1' ELSE '0' END) AS is_increment,
|
||||
c.udt_name AS column_type,
|
||||
c.character_maximum_length AS column_length,
|
||||
c.column_default AS column_default
|
||||
FROM information_schema.columns c
|
||||
LEFT JOIN information_schema.key_column_usage kcu
|
||||
ON c.table_name = kcu.table_name AND c.column_name = kcu.column_name AND kcu.table_schema = c.table_schema
|
||||
LEFT JOIN information_schema.table_constraints tc
|
||||
ON tc.constraint_name = kcu.constraint_name AND tc.table_name = c.table_name AND tc.table_schema = c.table_schema
|
||||
LEFT JOIN pg_catalog.pg_statio_all_tables st
|
||||
ON st.relname = c.table_name
|
||||
LEFT JOIN pg_catalog.pg_description pgd
|
||||
ON pgd.objoid = st.relid AND pgd.objsubid = c.ordinal_position
|
||||
WHERE c.table_catalog = current_database()
|
||||
AND c.table_schema = 'public'
|
||||
AND c.table_name = :table_name
|
||||
ORDER BY c.ordinal_position
|
||||
"""
|
||||
elif settings.DATABASE_TYPE == "mysql":
|
||||
query_sql = """
|
||||
SELECT
|
||||
c.column_name,
|
||||
(CASE WHEN (c.is_nullable = 'NO' AND c.column_key != 'PRI') THEN '1' ELSE '0' END) AS is_required,
|
||||
(CASE WHEN c.column_key = 'PRI' THEN '1' ELSE '0' END) AS is_pk,
|
||||
(CASE WHEN EXISTS (SELECT 1 FROM information_schema.statistics s WHERE s.table_schema = c.table_schema AND s.table_name = c.table_name AND s.column_name = c.column_name AND s.non_unique = 0 AND s.index_name != 'PRIMARY') THEN '1' ELSE '0' END) AS is_unique,
|
||||
c.ordinal_position AS sort,
|
||||
c.column_comment,
|
||||
(CASE WHEN c.extra = 'auto_increment' THEN '1' ELSE '0' end) AS is_increment,
|
||||
c.column_type,
|
||||
c.character_maximum_length AS column_length,
|
||||
c.column_default AS column_default
|
||||
FROM
|
||||
information_schema.columns c
|
||||
WHERE
|
||||
c.table_schema = (SELECT DATABASE())
|
||||
AND c.table_name = :table_name
|
||||
ORDER BY c.ordinal_position
|
||||
"""
|
||||
else:
|
||||
# 修复SQLite查询语句,使用PRAGMA获取表结构信息
|
||||
query_sql = """
|
||||
SELECT
|
||||
name as column_name,
|
||||
(CASE WHEN (type != '' AND pk != 1) THEN '1' ELSE '0' END) AS is_required,
|
||||
(CASE WHEN pk = 1 THEN '1' ELSE '0' END) AS is_pk,
|
||||
(CASE WHEN (SELECT COUNT(*) FROM pragma_index_list(:table_name) pil JOIN pragma_index_info(pil.name) pii ON 1=1 WHERE pil.unique = 1 AND pii.name = pragma_table_info.name AND pil.name NOT LIKE 'sqlite_%') > 0 THEN '1' ELSE '0' END) AS is_unique,
|
||||
cid AS sort,
|
||||
'' as column_comment,
|
||||
(CASE WHEN type LIKE '%AUTOINCREMENT%' THEN '1' ELSE '0' END) AS is_increment,
|
||||
type as column_type,
|
||||
(CASE WHEN type LIKE 'varchar(%' OR type LIKE 'char(%' THEN substr(type, instr(type, '(') + 1, instr(type, ')') - instr(type, '(') - 1) ELSE NULL END) AS column_length,
|
||||
dflt_value AS column_default
|
||||
FROM
|
||||
pragma_table_info(:table_name)
|
||||
ORDER BY cid
|
||||
"""
|
||||
|
||||
query = text(query_sql).bindparams(table_name=table_name)
|
||||
rows = (await self.db.execute(query)).fetchall()
|
||||
result = [
|
||||
GenTableColumnOutSchema(
|
||||
column_name=row[0],
|
||||
is_required=row[1],
|
||||
is_pk=row[2],
|
||||
is_unique=row[3],
|
||||
sort=row[4],
|
||||
column_comment=row[5],
|
||||
is_increment=row[6],
|
||||
column_type=row[7],
|
||||
column_length=str(row[8]) if row[8] is not None else None,
|
||||
column_default=str(row[9]) if row[9] is not None else None
|
||||
)
|
||||
for row in rows
|
||||
]
|
||||
|
||||
return result
|
||||
try:
|
||||
if settings.DATABASE_TYPE == "mysql":
|
||||
query_sql = """
|
||||
SELECT
|
||||
c.column_name AS column_name,
|
||||
c.column_comment AS column_comment,
|
||||
c.column_type AS column_type,
|
||||
c.character_maximum_length AS column_length,
|
||||
c.column_default AS column_default,
|
||||
c.ordinal_position AS sort,
|
||||
(CASE WHEN c.column_key = 'PRI' THEN 1 ELSE 0 END) AS is_pk,
|
||||
(CASE WHEN c.extra = 'auto_increment' THEN 1 ELSE 0 END) AS is_increment,
|
||||
(CASE WHEN (c.is_nullable = 'NO' AND c.column_key != 'PRI') THEN 1 ELSE 0 END) AS is_nullable,
|
||||
(CASE
|
||||
WHEN c.column_name IN (
|
||||
SELECT k.column_name
|
||||
FROM information_schema.key_column_usage k
|
||||
JOIN information_schema.table_constraints t
|
||||
ON k.constraint_name = t.constraint_name
|
||||
WHERE k.table_schema = c.table_schema
|
||||
AND k.table_name = c.table_name
|
||||
AND t.constraint_type = 'UNIQUE'
|
||||
) THEN 1 ELSE 0
|
||||
END) AS is_unique
|
||||
FROM
|
||||
information_schema.columns c
|
||||
WHERE c.table_schema = (SELECT DATABASE())
|
||||
AND c.table_name = :table_name
|
||||
ORDER BY
|
||||
c.ordinal_position
|
||||
"""
|
||||
else:
|
||||
query_sql = """
|
||||
SELECT
|
||||
c.column_name AS column_name,
|
||||
COALESCE(pgd.description, '') AS column_comment,
|
||||
c.udt_name AS column_type,
|
||||
c.character_maximum_length AS column_length,
|
||||
c.column_default AS column_default,
|
||||
c.ordinal_position AS sort,
|
||||
(CASE WHEN EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name
|
||||
WHERE tc.table_name = c.table_name
|
||||
AND tc.constraint_type = 'PRIMARY KEY'
|
||||
AND ccu.column_name = c.column_name
|
||||
) THEN 1 ELSE 0 END) AS is_pk,
|
||||
(CASE WHEN c.column_default LIKE 'nextval%' THEN 1 ELSE 0 END) AS is_increment,
|
||||
(CASE WHEN c.is_nullable = 'NO' THEN 1 ELSE 0 END) AS is_nullable,
|
||||
(CASE WHEN EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name
|
||||
WHERE tc.table_name = c.table_name
|
||||
AND tc.constraint_type = 'UNIQUE'
|
||||
AND ccu.column_name = c.column_name
|
||||
) THEN 1 ELSE 0 END) AS is_unique
|
||||
FROM
|
||||
information_schema.columns c
|
||||
LEFT JOIN pg_catalog.pg_description pgd ON
|
||||
pgd.objoid = (SELECT oid FROM pg_class WHERE relname = c.table_name)
|
||||
AND pgd.objsubid = c.ordinal_position
|
||||
WHERE c.table_catalog = current_database()
|
||||
AND c.table_schema = 'public'
|
||||
AND c.table_name = :table_name
|
||||
ORDER BY
|
||||
c.ordinal_position
|
||||
"""
|
||||
|
||||
query = text(query_sql).bindparams(table_name=table_name)
|
||||
result = await self.db.execute(query)
|
||||
rows = result.fetchall() if result else []
|
||||
|
||||
# 确保rows是可迭代对象
|
||||
if not rows:
|
||||
return []
|
||||
|
||||
columns_list = []
|
||||
for row in rows:
|
||||
# 防御性编程:检查row是否有足够的元素
|
||||
if len(row) >= 10:
|
||||
columns_list.append(
|
||||
GenTableColumnOutSchema(
|
||||
column_name=row[0],
|
||||
column_comment=row[1],
|
||||
column_type=row[2],
|
||||
column_length=str(row[3]) if row[3] is not None else '',
|
||||
column_default=str(row[4]) if row[4] is not None else '',
|
||||
sort=row[5],
|
||||
is_pk=row[6],
|
||||
is_increment=row[7],
|
||||
is_nullable=row[8],
|
||||
is_unique=row[9],
|
||||
)
|
||||
)
|
||||
return columns_list
|
||||
except Exception as e:
|
||||
logger.error(f"获取表{table_name}的字段列表时出错: {str(e)}")
|
||||
# 确保即使出错也返回空列表而不是None
|
||||
raise
|
||||
|
||||
async def list_gen_table_column_crud(self, search: Optional[Dict] = None, order_by: Optional[List[Dict[str, str]]] = None, preload: Optional[List[Union[str, Any]]] = None) -> Sequence[GenTableColumnModel]:
|
||||
"""根据业务表字段查询业务表字段列表。
|
||||
@@ -559,7 +528,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
data_dict = data.model_dump(exclude_unset=True)
|
||||
return await self.update(id=id, data=data_dict)
|
||||
|
||||
async def delete_gen_table_column_by_table_id_dao(self, table_ids: List[int]) -> None:
|
||||
async def delete_gen_table_column_by_table_id_crud(self, table_ids: List[int]) -> None:
|
||||
"""根据业务表ID批量删除业务表字段。
|
||||
|
||||
参数:
|
||||
@@ -577,7 +546,7 @@ class GenTableColumnCRUD(CRUDBase[GenTableColumnModel, GenTableColumnSchema, Gen
|
||||
if column_ids:
|
||||
await self.delete(ids=column_ids)
|
||||
|
||||
async def delete_gen_table_column_by_column_id_dao(self, column_ids: List[int]) -> None:
|
||||
async def delete_gen_table_column_by_column_id_crud(self, column_ids: List[int]) -> None:
|
||||
"""根据业务表字段ID批量删除业务表字段。
|
||||
|
||||
参数:
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import String, Integer, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from typing import Optional, List, Dict, Any
|
||||
from sqlalchemy import String, Integer, ForeignKey, Boolean, JSON, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship, validates
|
||||
from sqlalchemy.sql import expression
|
||||
|
||||
from app.config.setting import settings
|
||||
from app.core.base_model import CreatorMixin
|
||||
@@ -17,25 +18,36 @@ class GenTableModel(CreatorMixin):
|
||||
__table_args__ = ({'comment': '代码生成表'})
|
||||
__loader_options__ = ["columns", "creator"]
|
||||
|
||||
table_name: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, default='', comment='表名称')
|
||||
table_comment: Mapped[Optional[str]] = mapped_column(String(500), nullable=True, default='', comment='表描述')
|
||||
sub_table_name : Mapped[Optional[str]] = mapped_column(String(64), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE), comment='关联子表的表名',)
|
||||
sub_table_fk_name: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE), comment='子表关联的外键名',)
|
||||
class_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, default='', comment='实体类名称')
|
||||
table_name: Mapped[str] = mapped_column(String(200), nullable=False, default='', comment='表名称')
|
||||
table_comment: Mapped[Optional[str]] = mapped_column(String(500), nullable=True, comment='表描述')
|
||||
|
||||
class_name: Mapped[str] = mapped_column(String(100), nullable=False, default='', comment='实体类名称')
|
||||
package_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment='生成包路径')
|
||||
module_name: Mapped[Optional[str]] = mapped_column(String(30), nullable=True, comment='生成模块名')
|
||||
business_name: Mapped[Optional[str]] = mapped_column(String(30), nullable=True, comment='生成业务名')
|
||||
function_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment='生成功能名')
|
||||
gen_type: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, default='0', comment='生成代码方式(0zip压缩包 1生成项目路径)')
|
||||
options: Mapped[Optional[str]] = mapped_column(String(1000), nullable=True, comment='其它生成选项')
|
||||
|
||||
sub_table_name: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE), comment='关联子表的表名')
|
||||
sub_table_fk_name: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, server_default=SqlalchemyUtil.get_server_default_null(settings.DATABASE_TYPE), comment='子表关联的外键名')
|
||||
|
||||
parent_menu_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, comment='父菜单ID')
|
||||
|
||||
# 关系定义
|
||||
columns: Mapped[List['GenTableColumnModel']] = relationship(
|
||||
'GenTableColumnModel',
|
||||
order_by='GenTableColumnModel.sort',
|
||||
back_populates='tables',
|
||||
cascade='all, delete-orphan'
|
||||
)
|
||||
columns: Mapped[List['GenTableColumnModel']] = relationship('GenTableColumnModel', order_by='GenTableColumnModel.sort', back_populates='table',cascade='all, delete-orphan')
|
||||
|
||||
@validates('table_name')
|
||||
def validate_table_name(self, key: str, table_name: str) -> str:
|
||||
"""验证表名不为空"""
|
||||
if not table_name or not table_name.strip():
|
||||
raise ValueError('表名称不能为空')
|
||||
return table_name.strip()
|
||||
|
||||
@validates('class_name')
|
||||
def validate_class_name(self, key: str, class_name: str) -> str:
|
||||
"""验证类名不为空"""
|
||||
if not class_name or not class_name.strip():
|
||||
raise ValueError('实体类名称不能为空')
|
||||
return class_name.strip()
|
||||
|
||||
|
||||
class GenTableColumnModel(CreatorMixin):
|
||||
@@ -44,38 +56,59 @@ class GenTableColumnModel(CreatorMixin):
|
||||
"""
|
||||
__tablename__ = 'gen_table_column'
|
||||
__table_args__ = ({'comment': '代码生成表字段'})
|
||||
__loader_options__ = ["tables", "creator"]
|
||||
__loader_options__ = ["table", "creator"]
|
||||
|
||||
column_name: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment='列名称')
|
||||
# 数据库设计表字段
|
||||
column_name: Mapped[str] = mapped_column(String(200), nullable=False, comment='列名称')
|
||||
column_comment: Mapped[Optional[str]] = mapped_column(String(500), nullable=True, comment='列描述')
|
||||
column_type: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment='列类型')
|
||||
column_type: Mapped[str] = mapped_column(String(100), nullable=False, comment='列类型')
|
||||
column_length: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, comment='列长度')
|
||||
column_default: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment='列默认值')
|
||||
python_type: Mapped[Optional[str]] = mapped_column(String(500), nullable=True, comment='PYTHON类型')
|
||||
python_field: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment='PYTHON字段名')
|
||||
is_pk: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否主键(1是)')
|
||||
is_increment: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否自增(1是)')
|
||||
is_required: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否必填(1是)')
|
||||
is_unique: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否唯一(1是)')
|
||||
is_insert: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否为插入字段(1是)')
|
||||
is_edit: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否编辑字段(1是)')
|
||||
is_list: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否列表字段(1是)')
|
||||
is_query: Mapped[Optional[str]] = mapped_column(String(1), nullable=True, comment='是否查询字段(1是)')
|
||||
query_type: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, default='EQ', comment='查询方式(等于、不等于、大于、小于、范围)')
|
||||
html_type: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment='显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)')
|
||||
is_pk: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment='是否主键')
|
||||
is_increment: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment='是否自增')
|
||||
is_nullable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment='是否允许为空')
|
||||
is_unique: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment='是否唯一')
|
||||
|
||||
# Python字段映射
|
||||
python_type: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, comment='Python类型')
|
||||
python_field: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, comment='Python字段名')
|
||||
|
||||
# 序列化配置
|
||||
is_insert: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment='是否为新增字段')
|
||||
is_edit: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment='是否编辑字段')
|
||||
is_list: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default=expression.true(), comment='是否列表字段')
|
||||
is_query: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=expression.false(), comment='是否查询字段')
|
||||
query_type: Mapped[Optional[str]] = mapped_column(String(50), nullable=True, default=None, comment='查询方式')
|
||||
|
||||
# 前端展示配置
|
||||
html_type: Mapped[Optional[str]] = mapped_column(String(100), nullable=True, default='input', comment='显示类型')
|
||||
dict_type: Mapped[Optional[str]] = mapped_column(String(200), nullable=True, default='', comment='字典类型')
|
||||
sort: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, comment='排序')
|
||||
|
||||
# 外键关系
|
||||
table_id: Mapped[Optional[int]] = mapped_column(
|
||||
|
||||
# 排序和扩展配置
|
||||
sort: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment='排序')
|
||||
|
||||
# 归属关系
|
||||
table_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey('gen_table.id', ondelete='CASCADE'),
|
||||
nullable=True,
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment='归属表编号'
|
||||
)
|
||||
|
||||
|
||||
# 关系定义
|
||||
tables: Mapped['GenTableModel'] = relationship(
|
||||
'GenTableModel',
|
||||
back_populates='columns'
|
||||
)
|
||||
table: Mapped['GenTableModel'] = relationship('GenTableModel', back_populates='columns')
|
||||
|
||||
@validates('column_name')
|
||||
def validate_column_name(self, key: str, column_name: str) -> str:
|
||||
"""验证列名不为空"""
|
||||
if not column_name or not column_name.strip():
|
||||
raise ValueError('列名称不能为空')
|
||||
return column_name.strip()
|
||||
|
||||
@validates('column_type')
|
||||
def validate_column_type(self, key: str, column_type: str) -> str:
|
||||
"""验证列类型不为空"""
|
||||
if not column_type or not column_type.strip():
|
||||
raise ValueError('列类型不能为空')
|
||||
return column_type.strip()
|
||||
@@ -22,7 +22,7 @@ class GenTableQueryParam:
|
||||
|
||||
class GenTableColumnQueryParam:
|
||||
"""代码生成业务表字段查询参数
|
||||
- `column_name`按like规则模糊查询(透传到CRUD层)。
|
||||
- `column_name`按like规则模糊查询(透传到CRUD层)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from typing import List, Literal, Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
from app.core.base_schema import BaseSchema
|
||||
|
||||
|
||||
class GenTableOptionSchema(BaseModel):
|
||||
"""代码生成表的附加选项(存入`options`字段的JSON)。
|
||||
- parent_menu_id:菜单归属;树模板依赖。
|
||||
- tree_*:树形结构必需的编码/父编码/名称字段。
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
parent_menu_id: Optional[int] = Field(default=None, description='所属父级分类')
|
||||
|
||||
|
||||
|
||||
class GenDBTableSchema(BaseModel):
|
||||
"""数据库中的表信息(跨方言统一结构)。
|
||||
- 供“导入表结构”与“同步结构”环节使用。
|
||||
@@ -31,7 +19,10 @@ class GenDBTableSchema(BaseModel):
|
||||
table_comment: Optional[str] = Field(default=None, description='表描述')
|
||||
|
||||
|
||||
class GenTableBaseSchema(BaseModel):
|
||||
class GenTableSchema(BaseModel):
|
||||
"""代码生成业务表更新模型(扩展聚合字段)。
|
||||
- 聚合:`columns`字段包含字段列表;`pk_column`主键字段;子表结构`sub_table`。
|
||||
"""
|
||||
"""代码生成业务表基础模型(创建/更新共享字段)。
|
||||
- 说明:`params`为前端结构体,后端持久化为`options`的JSON。
|
||||
"""
|
||||
@@ -39,85 +30,69 @@ class GenTableBaseSchema(BaseModel):
|
||||
|
||||
table_name: str= Field(..., description='表名称')
|
||||
table_comment: Optional[str] = Field(default=None, description='表描述')
|
||||
sub_table_name: Optional[str] = Field(default=None, description='关联子表的表名')
|
||||
sub_table_fk_name: Optional[str] = Field(default=None, description='子表关联的外键名')
|
||||
class_name: Optional[str] = Field(default=None, description='实体类名称')
|
||||
package_name: Optional[str] = Field(default=None, description='生成包路径')
|
||||
module_name: Optional[str] = Field(default=None, description='生成模块名')
|
||||
business_name: Optional[str] = Field(default=None, description='生成业务名')
|
||||
function_name: Optional[str] = Field(default=None, description='生成功能名')
|
||||
gen_type: Optional[Literal['0', '1']] = Field(default=None, description='生成代码方式(0zip压缩包 1生成项目路径)')
|
||||
options: Optional[str] = Field(default=None, description='其它生成选项(JSON字符串)')
|
||||
description: Optional[str] = Field(default=None, description='功能描述')
|
||||
sub_table_name: Optional[str] = Field(default=None, description='关联子表的表名')
|
||||
sub_table_fk_name: Optional[str] = Field(default=None, description='子表关联的外键名')
|
||||
parent_menu_id: Optional[int] = Field(default=None, description='所属父级分类,生成页面时候生成菜单使用')
|
||||
description: Optional[str] = Field(default=None, max_length=255, description="描述")
|
||||
|
||||
|
||||
class GenTableSchema(GenTableBaseSchema):
|
||||
"""代码生成业务表更新模型(扩展聚合字段)。
|
||||
- 聚合:`columns`字段包含字段列表;`pk_column`主键字段;子表结构`sub_table`。
|
||||
"""
|
||||
|
||||
pk_column: Optional['GenTableColumnOutSchema'] = Field(default=None, description='主键信息')
|
||||
sub_table: Optional['GenTableSchema'] = Field(default=None, description='子表信息')
|
||||
columns: Optional[List['GenTableColumnOutSchema']] = Field(default=None, description='表列信息')
|
||||
parent_menu_id: Optional[int] = Field(default=None, description='上级菜单ID字段')
|
||||
parent_menu_name: Optional[str] = Field(default=None, description='上级菜单名称字段')
|
||||
sub: Optional[bool] = Field(default=None, description='是否为子表')
|
||||
|
||||
@field_validator('table_name')
|
||||
@classmethod
|
||||
def table_name_update(cls, v: str) -> str:
|
||||
"""更新表名称"""
|
||||
if not v:
|
||||
raise ValueError('表名称不能为空')
|
||||
return v
|
||||
|
||||
|
||||
class GenTableOutSchema(GenTableSchema, BaseSchema):
|
||||
"""业务表输出模型(面向控制器/前端)。
|
||||
- 清洗:统一处理None值,保证`columns`为列表;文本字段为空字符串。
|
||||
- 兼容:既支持传入ORM对象,也支持字典输入。
|
||||
"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
pk_column: Optional['GenTableColumnOutSchema'] = Field(default=None, description='主键信息')
|
||||
sub_table: Optional['GenTableSchema'] = Field(default=None, description='子表信息')
|
||||
sub: Optional[bool] = Field(default=None, description='是否为子表')
|
||||
|
||||
|
||||
class GenTableColumnSchema(BaseModel):
|
||||
"""代码生成业务表字段创建模型(原始字段+生成配置)。
|
||||
- 原始:`column_name/column_type/column_comment` 等。
|
||||
- 生成:`python_type/html_type/query_type/dict_type` 等由工具初始化。
|
||||
- 标记:所有 is_* 字段默认使用字符串'1'表示启用,便于前端和模板处理。
|
||||
- 从根本上解决问题:所有字段都设置了合理的默认值,避免None值问题
|
||||
"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
table_id: Optional[int] = Field(default=None, description='归属表编号')
|
||||
column_name: Optional[str] = Field(default=None, description='列名称')
|
||||
column_comment: Optional[str] = Field(default=None, description='列描述')
|
||||
column_type: Optional[str] = Field(default=None, description='列类型')
|
||||
column_length: Optional[str] = Field(default=None, description='列长度')
|
||||
column_default: Optional[str] = Field(default=None, description='列默认值')
|
||||
python_type: Optional[str] = Field(default=None, description='python类型')
|
||||
python_field: Optional[str] = Field(default=None, description='python字段名')
|
||||
is_pk: Optional[str] = Field(default=None, description='是否主键(1是)')
|
||||
is_increment: Optional[str] = Field(default=None, description='是否自增(1是)')
|
||||
is_required: Optional[str] = Field(default=None, description='是否必填(1是)')
|
||||
is_unique: Optional[str] = Field(default=None, description='是否唯一(1是)')
|
||||
is_insert: Optional[str] = Field(default=None, description='是否为插入字段(1是)')
|
||||
is_edit: Optional[str] = Field(default=None, description='是否编辑字段(1是)')
|
||||
is_list: Optional[str] = Field(default=None, description='是否列表字段(1是)')
|
||||
is_query: Optional[str] = Field(default=None, description='是否查询字段(1是)')
|
||||
table_id: int = Field(default=0, description='归属表编号')
|
||||
column_name: str = Field(default='', description='列名称')
|
||||
column_comment: Optional[str] = Field(default='', description='列描述')
|
||||
column_type: str = Field(default='varchar(255)', description='列类型')
|
||||
column_length: Optional[str] = Field(default='', description='列长度')
|
||||
column_default: Optional[str] = Field(default='', description='列默认值')
|
||||
is_pk: bool = Field(default=False, description='是否主键(True是 False否)')
|
||||
is_increment: bool = Field(default=False, description='是否自增(True是 False否)')
|
||||
is_nullable: bool = Field(default=True, description='是否允许为空(True是 False否)')
|
||||
is_unique: bool = Field(default=False, description='是否唯一(True是 False否)')
|
||||
python_type: Optional[str] = Field(default='str', description='python类型')
|
||||
python_field: Optional[str] = Field(default='', description='python字段名')
|
||||
is_insert: bool = Field(default=True, description='是否为插入字段(True是 False否)')
|
||||
is_edit: bool = Field(default=True, description='是否编辑字段(True是 False否)')
|
||||
is_list: bool = Field(default=True, description='是否列表字段(True是 False否)')
|
||||
is_query: bool = Field(default=True, description='是否查询字段(True是 False否)')
|
||||
query_type: Optional[str] = Field(default=None, description='查询方式(等于、不等于、大于、小于、范围)')
|
||||
html_type: Optional[str] = Field(default=None, description='显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)')
|
||||
dict_type: Optional[str] = Field(default=None, description='字典类型')
|
||||
sort: Optional[int] = Field(default=None, description='排序')
|
||||
description: Optional[str] = Field(default=None, description='功能描述')
|
||||
html_type: Optional[str] = Field(default='input', description='显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)')
|
||||
dict_type: Optional[str] = Field(default='', description='字典类型')
|
||||
sort: int = Field(default=0, description='排序')
|
||||
|
||||
|
||||
class GenTableColumnOutSchema(GenTableColumnSchema, BaseSchema):
|
||||
"""业务表字段输出模型(布尔派生+便捷字段)。
|
||||
- 布尔:将字符串 is_* 转为布尔 `pk/increment/...`,供前端/模板快捷使用。
|
||||
- 便捷:`cap_python_field` 存放大驼峰字段名(模板场景常用)。
|
||||
"""
|
||||
业务表字段输出模型
|
||||
"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
cap_python_field: Optional[str] = Field(default=None, description='字段大写形式')
|
||||
pk: Optional[bool] = Field(default=False, description='是否主键')
|
||||
increment: Optional[bool] = Field(default=False, description='是否自增')
|
||||
required: Optional[bool] = Field(default=False, description='是否必填')
|
||||
unique: Optional[bool] = Field(default=False, description='是否唯一')
|
||||
insert: Optional[bool] = Field(default=False, description='是否为插入字段')
|
||||
edit: Optional[bool] = Field(default=False, description='是否编辑字段')
|
||||
list: Optional[bool] = Field(default=False, description='是否列表字段')
|
||||
query: Optional[bool] = Field(default=False, description='是否查询字段')
|
||||
super_column: Optional[bool] = Field(default=False, description='是否为基类字段')
|
||||
usable_column: Optional[bool] = Field(default=False, description='是否为基类字段白名单')
|
||||
super_column: Optional[str] = Field(default='0', description='是否为基类字段(1是 0否)')
|
||||
@@ -10,7 +10,6 @@ from sqlglot import parse as sqlglot_parse
|
||||
|
||||
from app.config.setting import settings
|
||||
from app.core.logger import logger
|
||||
from app.common.response import ErrorResponse
|
||||
from app.core.exceptions import CustomException
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.utils.gen_util import GenUtils
|
||||
@@ -41,10 +40,7 @@ class GenTableService:
|
||||
- 备注:优先解析`options`为`GenTableOptionSchema`,设置`parent_menu_id`等选项;保证`columns`与`tables`结构完整。
|
||||
"""
|
||||
gen_table = await cls.get_gen_table_by_id_service(auth, table_id)
|
||||
gen_tables = await cls.get_gen_table_all_service(auth)
|
||||
gen_columns = await GenTableColumnService.get_gen_table_column_list_by_table_id_service(auth, table_id)
|
||||
gen_table.columns = gen_columns
|
||||
return dict(info=gen_table, rows=gen_columns, tables=gen_tables)
|
||||
return GenTableOutSchema.model_validate(gen_table).model_dump()
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
@@ -100,52 +96,38 @@ class GenTableService:
|
||||
"""
|
||||
# 检查是否有表需要导入
|
||||
if not gen_table_list:
|
||||
raise CustomException(msg="没有可导入的表结构")
|
||||
|
||||
# 检查表是否已存在
|
||||
existing_tables = []
|
||||
for table in gen_table_list:
|
||||
table_name = table.table_name
|
||||
# 检查表是否已存在
|
||||
existing_table = await GenTableCRUD(auth).get_gen_table_by_name(table_name)
|
||||
if existing_table:
|
||||
existing_tables.append(table_name)
|
||||
|
||||
# 如果有已存在的表,抛出异常
|
||||
if existing_tables:
|
||||
raise CustomException(msg=f"以下表已存在,不能重复导入: {', '.join(existing_tables)}")
|
||||
|
||||
raise CustomException(msg="导入的表结构不能为空")
|
||||
try:
|
||||
for table in gen_table_list:
|
||||
table_name = table.table_name
|
||||
# 检查表是否已存在
|
||||
existing_table = await GenTableCRUD(auth).get_gen_table_by_name(table_name)
|
||||
if existing_table:
|
||||
raise CustomException(msg=f"以下表已存在,不能重复导入: {table_name}")
|
||||
GenUtils.init_table(table)
|
||||
add_gen_table = await GenTableCRUD(auth).add_gen_table(table)
|
||||
if add_gen_table:
|
||||
if not table.columns:
|
||||
table.columns = []
|
||||
add_gen_table = await GenTableCRUD(auth).add_gen_table(GenTableSchema.model_validate(table.model_dump()))
|
||||
gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name)
|
||||
if len(gen_table_columns) > 0:
|
||||
table.id = add_gen_table.id
|
||||
# 获取数据库表的字段信息
|
||||
gen_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name)
|
||||
|
||||
# 为每个字段初始化并保存到数据库
|
||||
for column in gen_table_columns:
|
||||
# 将GenTableColumnOutSchema转换为GenTableColumnSchema,确保所有字段正确设置
|
||||
column_schema = GenTableColumnSchema(
|
||||
table_id=table.id,
|
||||
column_name=column.column_name,
|
||||
column_comment=column.column_comment,
|
||||
column_type=column.column_type,
|
||||
column_length=column.column_length if column.column_length is not None else '',
|
||||
column_default=column.column_default if column.column_default is not None else '',
|
||||
column_length=column.column_length,
|
||||
column_default=column.column_default,
|
||||
is_pk=column.is_pk,
|
||||
is_increment=column.is_increment,
|
||||
is_nullable=column.is_nullable,
|
||||
is_unique=column.is_unique,
|
||||
sort=column.sort,
|
||||
python_type=column.python_type,
|
||||
python_field=column.python_field,
|
||||
is_pk=str(column.is_pk) if column.is_pk is not None else '0',
|
||||
is_increment=str(column.is_increment) if column.is_increment is not None else '0',
|
||||
is_required=str(column.is_required) if column.is_required is not None else '0',
|
||||
is_unique=str(column.is_unique) if column.is_unique is not None else '0',
|
||||
sort=column.sort
|
||||
)
|
||||
# 初始化字段属性
|
||||
GenUtils.init_column_field(column_schema, table)
|
||||
# 保存到数据库
|
||||
await GenTableColumnCRUD(auth).create_gen_table_column_crud(column_schema)
|
||||
return True
|
||||
except Exception as e:
|
||||
@@ -165,16 +147,15 @@ class GenTableService:
|
||||
try:
|
||||
# 解析SQL语句
|
||||
sql_statements = sqlglot_parse(sql, dialect=settings.DATABASE_TYPE)
|
||||
if not sql_statements:
|
||||
raise CustomException(msg='无法解析SQL语句,请检查SQL语法')
|
||||
|
||||
# 校验sql语句是否为合法的建表语句
|
||||
if not cls.__is_valid_create_table(sql_statements):
|
||||
raise CustomException(msg='sql语句不是合法的建表语句')
|
||||
|
||||
# 获取要创建的表名
|
||||
table_names = cls.__get_table_names(sql_statements)
|
||||
if not table_names:
|
||||
raise CustomException(msg='无法从SQL语句中提取表名')
|
||||
|
||||
table_names = cls.__get_table_names(sql_statements)
|
||||
# 创建CRUD实例
|
||||
gen_table_crud = GenTableCRUD(auth=auth)
|
||||
|
||||
@@ -190,17 +171,19 @@ class GenTableService:
|
||||
raise CustomException(msg=f'表 {table_name} 已在代码生成模块中存在,请检查并修改表名后重试')
|
||||
|
||||
# 表不存在,执行SQL语句创建表
|
||||
await gen_table_crud.create_table_by_sql(sql)
|
||||
result = await gen_table_crud.create_table_by_sql(sql_statements)
|
||||
if not result:
|
||||
raise CustomException(msg=f'创建表 {table_names} 失败,请检查SQL语句')
|
||||
|
||||
# 导入表结构到代码生成模块
|
||||
# 导入表结构到代码生成模块 - 简化逻辑,移除多余的None检查
|
||||
gen_table_list = await cls.get_gen_db_table_list_by_name_service(auth, table_names)
|
||||
|
||||
import_result = await cls.import_gen_table_service(auth, gen_table_list)
|
||||
|
||||
return import_result
|
||||
except CustomException:
|
||||
# 直接传递已格式化的CustomException
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f'创建表结构失败: {str(e)}。SQL预览: {sql}')
|
||||
raise CustomException(msg=f'创建表结构失败: {str(e)}')
|
||||
|
||||
@classmethod
|
||||
def __is_valid_create_table(cls, sql_statements: List[Expression | None]) -> bool:
|
||||
@@ -242,7 +225,7 @@ class GenTableService:
|
||||
table = sql_statement.find(Table)
|
||||
if table and table.name:
|
||||
table_names.append(table.name)
|
||||
return table_names
|
||||
return list(set(table_names))
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
@@ -264,7 +247,7 @@ class GenTableService:
|
||||
if hasattr(gen_table_column, 'id') and gen_table_column.id:
|
||||
column_schema = GenTableColumnSchema(**gen_table_column.model_dump())
|
||||
await GenTableColumnCRUD(auth).update_gen_table_column_crud(gen_table_column.id, column_schema)
|
||||
return result.model_dump()
|
||||
return GenTableOutSchema.model_validate(result).model_dump()
|
||||
except Exception as e:
|
||||
raise CustomException(msg=str(e))
|
||||
else:
|
||||
@@ -280,7 +263,7 @@ class GenTableService:
|
||||
|
||||
try:
|
||||
# 先删除相关的字段信息
|
||||
await GenTableColumnCRUD(auth=auth).delete_gen_table_column_by_table_id_dao(ids)
|
||||
await GenTableColumnCRUD(auth=auth).delete_gen_table_column_by_table_id_crud(ids)
|
||||
# 再删除表信息
|
||||
await GenTableCRUD(auth=auth).delete_gen_table(ids)
|
||||
except Exception as e:
|
||||
@@ -297,21 +280,18 @@ class GenTableService:
|
||||
raise CustomException(msg='业务表不存在')
|
||||
|
||||
result = GenTableOutSchema.model_validate(gen_table)
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def get_gen_table_all_service(cls, auth: AuthSchema) -> List[GenTableOutSchema]:
|
||||
"""获取所有业务表信息(列表)。"""
|
||||
gen_table_all = await GenTableCRUD(auth=auth).get_gen_table_all()
|
||||
gen_table_all = await GenTableCRUD(auth=auth).get_gen_table_all() or []
|
||||
result = []
|
||||
for gen_table in gen_table_all:
|
||||
try:
|
||||
# 确保转换为输出模型,并处理可能的None值
|
||||
# 简化转换,利用schema层的默认值处理None情况
|
||||
table_out = GenTableOutSchema.model_validate(gen_table)
|
||||
if table_out.columns is None:
|
||||
table_out.columns = []
|
||||
result.append(table_out)
|
||||
except Exception as e:
|
||||
logger.warning(f"转换业务表时出错: {str(e)}")
|
||||
@@ -348,15 +328,11 @@ class GenTableService:
|
||||
async def generate_code_service(cls, auth: AuthSchema, table_name: str) -> bool:
|
||||
"""生成代码至指定路径(安全写入+可跳过覆盖)。
|
||||
- 安全:限制写入在项目根目录内;越界路径自动回退到项目根目录。
|
||||
- 覆盖:尊重`settings.allow_overwrite`,不允许时跳过写入。
|
||||
"""
|
||||
# 验证表名非空
|
||||
if not table_name or not table_name.strip():
|
||||
raise CustomException(msg='表名不能为空')
|
||||
|
||||
if not settings.allow_overwrite:
|
||||
logger.error('【系统预设】不允许生成文件覆盖到本地')
|
||||
raise CustomException(msg='【系统预设】不允许生成文件覆盖到本地')
|
||||
env = Jinja2TemplateUtil.get_env()
|
||||
render_info = await cls.__get_gen_render_info(auth, table_name)
|
||||
gen_table_schema = render_info[3]
|
||||
@@ -390,6 +366,9 @@ class GenTableService:
|
||||
zip_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
for table_name in table_names:
|
||||
if not table_name.strip():
|
||||
continue
|
||||
|
||||
try:
|
||||
env = Jinja2TemplateUtil.get_env()
|
||||
render_info = await cls.__get_gen_render_info(auth, table_name)
|
||||
@@ -419,51 +398,49 @@ class GenTableService:
|
||||
if not gen_table:
|
||||
raise CustomException(msg='业务表不存在')
|
||||
table = GenTableOutSchema.model_validate(gen_table)
|
||||
if not table.id:
|
||||
raise CustomException(msg='业务表ID不能为空')
|
||||
table_columns = table.columns or []
|
||||
table_column_map = {column.column_name: column for column in table_columns}
|
||||
db_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name)
|
||||
# 确保db_table_columns始终是列表类型,避免None值
|
||||
db_table_columns = await GenTableColumnCRUD(auth).get_gen_db_table_columns_by_name(table_name) or []
|
||||
db_table_columns = [col for col in db_table_columns if col is not None]
|
||||
db_table_column_names = [column.column_name for column in db_table_columns]
|
||||
try:
|
||||
for column in db_table_columns:
|
||||
# 仅在缺省时初始化默认属性(包含 table_id 关联)
|
||||
GenUtils.init_column_field(column, table)
|
||||
# 确保column_length和column_default字段有默认值
|
||||
if column.column_length is None:
|
||||
column.column_length = ''
|
||||
if column.column_default is None:
|
||||
column.column_default = ''
|
||||
# 利用schema层的默认值,移除多余的None检查
|
||||
if column.column_name in table_column_map:
|
||||
prev_column = table_column_map[column.column_name]
|
||||
# 复用旧记录ID,确保执行更新
|
||||
if hasattr(prev_column, 'id') and prev_column.id:
|
||||
column.id = prev_column.id
|
||||
|
||||
# 保留用户配置的显示与查询属性
|
||||
if getattr(prev_column, 'dict_type', None):
|
||||
# 保留用户配置的显示与查询属性 - 使用getattr确保安全访问
|
||||
if hasattr(prev_column, 'dict_type') and prev_column.dict_type:
|
||||
column.dict_type = prev_column.dict_type
|
||||
if getattr(prev_column, 'query_type', None):
|
||||
if hasattr(prev_column, 'query_type') and prev_column.query_type:
|
||||
column.query_type = prev_column.query_type
|
||||
if getattr(prev_column, 'html_type', None):
|
||||
if hasattr(prev_column, 'html_type') and prev_column.html_type:
|
||||
column.html_type = prev_column.html_type
|
||||
|
||||
# 保留 is_* 标志(旧值非空则保留),主键不设置必填
|
||||
def keep_str(orig, current):
|
||||
return orig if (orig is not None and orig != '') else current
|
||||
|
||||
is_pk_bool = bool(getattr(prev_column, 'pk', False)) or (prev_column.is_pk == '1')
|
||||
if not is_pk_bool:
|
||||
column.is_required = keep_str(prev_column.is_required, column.is_required)
|
||||
column.column_length = keep_str(prev_column.column_length, column.column_length)
|
||||
column.column_default = keep_str(prev_column.column_default, column.column_default)
|
||||
column.python_type = keep_str(prev_column.python_type, column.python_type)
|
||||
column.python_field = keep_str(prev_column.python_field, column.python_field)
|
||||
column.is_pk = keep_str(prev_column.is_pk, column.is_pk)
|
||||
column.is_increment = keep_str(prev_column.is_increment, column.is_increment)
|
||||
column.is_unique = keep_str(prev_column.is_unique, column.is_unique)
|
||||
column.is_insert = keep_str(prev_column.is_insert, column.is_insert)
|
||||
column.is_edit = keep_str(prev_column.is_edit, column.is_edit)
|
||||
column.is_list = keep_str(prev_column.is_list, column.is_list)
|
||||
column.is_query = keep_str(prev_column.is_query, column.is_query)
|
||||
# 保留关键用户自定义属性 - 安全处理is_pk
|
||||
is_pk_bool = False
|
||||
if hasattr(prev_column, 'is_pk'):
|
||||
# 处理不同类型的is_pk值
|
||||
if isinstance(prev_column.is_pk, bool):
|
||||
is_pk_bool = prev_column.is_pk
|
||||
else:
|
||||
is_pk_bool = str(prev_column.is_pk) == '1'
|
||||
|
||||
# 安全处理nullable属性
|
||||
if hasattr(prev_column, 'is_nullable') and not is_pk_bool:
|
||||
column.is_nullable = prev_column.is_nullable
|
||||
|
||||
# 保留其他重要用户设置
|
||||
if hasattr(prev_column, 'python_field'):
|
||||
column.python_field = prev_column.python_field or column.python_field
|
||||
|
||||
if hasattr(column, 'id') and column.id:
|
||||
await GenTableColumnCRUD(auth).update_gen_table_column_crud(column.id, column)
|
||||
@@ -477,7 +454,7 @@ class GenTableService:
|
||||
if del_columns:
|
||||
for column in del_columns:
|
||||
if hasattr(column, 'id') and column.id:
|
||||
await GenTableColumnCRUD(auth).delete_gen_table_column_by_column_id_dao([column.id])
|
||||
await GenTableColumnCRUD(auth).delete_gen_table_column_by_column_id_crud([column.id])
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f'同步失败: {str(e)}')
|
||||
|
||||
@@ -550,47 +527,8 @@ class GenTableColumnService:
|
||||
|
||||
@classmethod
|
||||
@handle_service_exception
|
||||
async def get_gen_table_column_list_by_table_id_service(cls, auth: AuthSchema, table_id: int) -> List[GenTableColumnOutSchema]:
|
||||
async def get_gen_table_column_list_by_table_id_service(cls, auth: AuthSchema, table_id: int) -> List[Dict[str, Any]]:
|
||||
"""获取业务表字段列表信息(输出模型)。"""
|
||||
gen_table_column_list_result = await GenTableColumnCRUD(auth).list_gen_table_column_crud({"table_id": table_id})
|
||||
result = []
|
||||
for gen_table_column in gen_table_column_list_result:
|
||||
try:
|
||||
# 转换为输出模型前确保必要字段正确设置
|
||||
# 确保is_*字段为字符串格式
|
||||
if hasattr(gen_table_column, 'is_pk') and gen_table_column.is_pk is not None and not isinstance(gen_table_column.is_pk, str):
|
||||
gen_table_column.is_pk = str(gen_table_column.is_pk)
|
||||
if hasattr(gen_table_column, 'is_increment') and gen_table_column.is_increment is not None and not isinstance(gen_table_column.is_increment, str):
|
||||
gen_table_column.is_increment = str(gen_table_column.is_increment)
|
||||
if hasattr(gen_table_column, 'is_required') and gen_table_column.is_required is not None and not isinstance(gen_table_column.is_required, str):
|
||||
gen_table_column.is_required = str(gen_table_column.is_required)
|
||||
if hasattr(gen_table_column, 'is_unique') and gen_table_column.is_unique is not None and not isinstance(gen_table_column.is_unique, str):
|
||||
gen_table_column.is_unique = str(gen_table_column.is_unique)
|
||||
if hasattr(gen_table_column, 'is_insert') and gen_table_column.is_insert is not None and not isinstance(gen_table_column.is_insert, str):
|
||||
gen_table_column.is_insert = str(gen_table_column.is_insert)
|
||||
if hasattr(gen_table_column, 'is_edit') and gen_table_column.is_edit is not None and not isinstance(gen_table_column.is_edit, str):
|
||||
gen_table_column.is_edit = str(gen_table_column.is_edit)
|
||||
if hasattr(gen_table_column, 'is_list') and gen_table_column.is_list is not None and not isinstance(gen_table_column.is_list, str):
|
||||
gen_table_column.is_list = str(gen_table_column.is_list)
|
||||
if hasattr(gen_table_column, 'is_query') and gen_table_column.is_query is not None and not isinstance(gen_table_column.is_query, str):
|
||||
gen_table_column.is_query = str(gen_table_column.is_query)
|
||||
|
||||
|
||||
# 转换为输出模型
|
||||
column_out = GenTableColumnOutSchema.model_validate(gen_table_column)
|
||||
|
||||
# 确保输出模型中的布尔字段正确设置
|
||||
column_out.pk = column_out.is_pk == '1'
|
||||
column_out.increment = column_out.is_increment == '1'
|
||||
column_out.required = column_out.is_required == '1'
|
||||
column_out.unique = column_out.is_unique == '1'
|
||||
column_out.insert = column_out.is_insert == '1'
|
||||
column_out.edit = column_out.is_edit == '1'
|
||||
column_out.list = column_out.is_list == '1'
|
||||
column_out.query = column_out.is_query == '1'
|
||||
|
||||
result.append(column_out)
|
||||
except Exception as e:
|
||||
logger.warning(f"转换字段模型时出错: {str(e)}")
|
||||
continue
|
||||
result = [GenTableColumnOutSchema.model_validate(gen_table_column).model_dump() for gen_table_column in gen_table_column_list_result]
|
||||
return result
|
||||
@@ -1,12 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Path, Query, Request, UploadFile, Form
|
||||
from fastapi import APIRouter, Body, Depends, Query, Request, UploadFile, Form
|
||||
from fastapi.responses import JSONResponse, StreamingResponse, FileResponse
|
||||
from typing import List, Optional
|
||||
|
||||
# from oss2 import auth # 预留阿里云OSS,后期使用
|
||||
|
||||
from app.common.response import StreamResponse, SuccessResponse, ErrorResponse
|
||||
from app.common.response import StreamResponse, SuccessResponse
|
||||
from app.common.request import PaginationService
|
||||
from app.utils.common_util import bytes2file_response
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
|
||||
@@ -357,13 +357,13 @@ class GenConstant:
|
||||
# 数据库字符串类型
|
||||
COLUMNTYPE_STR = (
|
||||
['character varying', 'varchar', 'character', 'char']
|
||||
if settings.DATABASE_TYPE == 'postgresql'
|
||||
if settings.DATABASE_TYPE == 'postgres'
|
||||
else ['char', 'varchar', 'nvarchar', 'varchar2']
|
||||
)
|
||||
|
||||
# 数据库文本类型
|
||||
COLUMNTYPE_TEXT = (
|
||||
['text', 'citext'] if settings.DATABASE_TYPE == 'postgresql' else ['tinytext', 'text', 'mediumtext', 'longtext']
|
||||
['text', 'citext'] if settings.DATABASE_TYPE == 'postgres' else ['tinytext', 'text', 'mediumtext', 'longtext']
|
||||
)
|
||||
|
||||
# 数据库时间类型
|
||||
@@ -378,14 +378,14 @@ class GenConstant:
|
||||
'timestamp without time zone',
|
||||
'interval',
|
||||
]
|
||||
if settings.DATABASE_TYPE == 'postgresql'
|
||||
if settings.DATABASE_TYPE == 'postgres'
|
||||
else ['datetime', 'time', 'date', 'timestamp']
|
||||
)
|
||||
|
||||
# 数据库字空间类型
|
||||
COLUMNTYPE_GEOMETRY = (
|
||||
['point', 'line', 'lseg', 'box', 'path', 'polygon', 'circle']
|
||||
if settings.DATABASE_TYPE == 'postgresql'
|
||||
if settings.DATABASE_TYPE == 'postgres'
|
||||
else [
|
||||
'geometry',
|
||||
'point',
|
||||
@@ -478,7 +478,7 @@ class GenConstant:
|
||||
QUERY_EQ = 'EQ'
|
||||
|
||||
# 需要
|
||||
REQUIRE = '1'
|
||||
REQUIRE = True
|
||||
|
||||
# 数据库类型与sqlalchemy类型映射
|
||||
DB_TO_SQLALCHEMY = (
|
||||
@@ -537,7 +537,7 @@ class GenConstant:
|
||||
'oidvector': 'ARRAY',
|
||||
'pg_node_tree': 'Text',
|
||||
}
|
||||
if settings.DATABASE_TYPE == 'postgresql'
|
||||
if settings.DATABASE_TYPE == 'postgres'
|
||||
else {
|
||||
# 数值类型
|
||||
'TINYINT': 'SmallInteger',
|
||||
@@ -647,7 +647,7 @@ class GenConstant:
|
||||
'oidvector': 'list',
|
||||
'pg_node_tree': 'str',
|
||||
}
|
||||
if settings.DATABASE_TYPE == 'postgresql'
|
||||
if settings.DATABASE_TYPE == 'postgres'
|
||||
else {
|
||||
# 数值类型
|
||||
'TINYINT': 'int',
|
||||
|
||||
@@ -54,6 +54,12 @@ class Settings(BaseSettings):
|
||||
REDOC_URL: str = "/redoc" # ReDoc路径
|
||||
ROOT_PATH: str = "/api/v1" # API路由前缀
|
||||
|
||||
# ================================================= #
|
||||
# ******************* alembic配置 ****************** #
|
||||
# ================================================= #
|
||||
# alembic 迁移文件存放路径
|
||||
ALEMBIC_VERSION_DIR: Path = BASE_DIR / 'app' / 'alembic' / 'versions'
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 跨域配置 ******************** #
|
||||
# ================================================= #
|
||||
@@ -82,37 +88,28 @@ class Settings(BaseSettings):
|
||||
SQL_DB_ENABLE: bool = True # 是否启用数据库
|
||||
DATABASE_ECHO: bool | Literal['debug'] = False # 是否显示SQL日志
|
||||
ECHO_POOL: bool | Literal['debug'] = False # 是否显示连接池日志
|
||||
POOL_SIZE: int = 20 # 连接池大小
|
||||
MAX_OVERFLOW: int = 10 # 最大溢出连接数
|
||||
POOL_SIZE: int = 10 # 连接池大小
|
||||
MAX_OVERFLOW: int = 20 # 最大溢出连接数
|
||||
POOL_TIMEOUT: int = 30 # 连接超时时间(秒)
|
||||
POOL_RECYCLE: int = 1800 # 连接回收时间(秒)
|
||||
POOL_USE_LIFO: bool = True # 是否使用LIFO连接池
|
||||
POOL_PRE_PING: bool = True # 是否开启连接预检
|
||||
FUTURE: bool = True # 是否使用SQLAlchemy 2.0特性
|
||||
AUTOCOMMIT: bool = False # 是否自动提交
|
||||
AUTOFETCH: bool = False # 是否自动获取
|
||||
AUTOFETCH: bool = False # 是否自动刷新
|
||||
EXPIRE_ON_COMMIT: bool = False # 是否在提交时过期
|
||||
|
||||
# 数据库类型
|
||||
DATABASE_TYPE: Literal['sqlite','mysql', 'postgresql'] = 'sqlite'
|
||||
DATABASE_TYPE: Literal['mysql', 'postgres'] = 'mysql'
|
||||
|
||||
|
||||
# MySQL/PostgreSQL/SQLite数据库连接
|
||||
# MySQL/PostgreSQL数据库连接
|
||||
DATABASE_HOST: str = 'localhost'
|
||||
DATABASE_PORT: int = 3306
|
||||
DATABASE_USER: str = 'root'
|
||||
DATABASE_PASSWORD: str = 'ServBay.dev'
|
||||
DATABASE_NAME: str = 'fastapiadmin'
|
||||
|
||||
# ================================================= #
|
||||
# ******************** MongoDB配置 ******************* #
|
||||
# ================================================= #
|
||||
MONGO_DB_ENABLE: bool = False # 是否启用MongoDB
|
||||
MONGO_DB_USER: str = ''
|
||||
MONGO_DB_PASSWORD: str = ''
|
||||
MONGO_DB_HOST: str = 'localhost'
|
||||
MONGO_DB_PORT: int = 27017
|
||||
MONGO_DB_NAME: str = 'admin'
|
||||
|
||||
# ================================================= #
|
||||
# ******************** Redis配置 ******************* #
|
||||
# ================================================= #
|
||||
@@ -197,14 +194,6 @@ class Settings(BaseSettings):
|
||||
# ================================================= #
|
||||
SCRIPT_DIR: Path = BASE_DIR.joinpath('app/scripts/data')
|
||||
|
||||
# ================================================= #
|
||||
# ******************* 代码生成配置 ****************** #
|
||||
# ================================================= #
|
||||
package_name: str = 'module_gencode' # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
|
||||
auto_remove_pre: bool = False # 自动去除表前缀,默认是True
|
||||
table_prefix: str = 'gen_' # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
|
||||
allow_overwrite: bool = True # 是否允许生成文件覆盖到本地(自定义路径),默认不允许
|
||||
|
||||
# ================================================= #
|
||||
# ******************* AI大模型配置 ****************** #
|
||||
# ================================================= #
|
||||
@@ -230,7 +219,6 @@ class Settings(BaseSettings):
|
||||
def EVENT_LIST(self) -> List[Optional[str]]:
|
||||
"""获取事件列表"""
|
||||
EVENTS: List[Optional[str]] = [
|
||||
"app.core.database.mongodb_connect" if self.MONGO_DB_ENABLE else None,
|
||||
"app.core.database.redis_connect" if self.REDIS_ENABLE else None,
|
||||
]
|
||||
return EVENTS
|
||||
@@ -240,29 +228,20 @@ class Settings(BaseSettings):
|
||||
"""获取异步数据库连接"""
|
||||
if self.DATABASE_TYPE == "mysql":
|
||||
return f"mysql+asyncmy://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}?charset=utf8mb4"
|
||||
elif self.DATABASE_TYPE == "postgresql":
|
||||
elif self.DATABASE_TYPE == "postgres":
|
||||
return f"postgresql+asyncpg://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
|
||||
elif self.DATABASE_TYPE == "sqlite":
|
||||
return f"sqlite+aiosqlite:///{self.BASE_DIR.joinpath(self.DATABASE_NAME + '.db')}?characterEncoding=UTF-8"
|
||||
else:
|
||||
raise ValueError(f"数据库驱动不支持: {self.DATABASE_TYPE}, 请选择 请选择 mysql、postgresql、sqlite")
|
||||
raise ValueError(f"数据库驱动不支持: {self.DATABASE_TYPE}, 请选择 请选择 mysql、postgres")
|
||||
|
||||
@property
|
||||
def DB_URI(self) -> str:
|
||||
"""获取同步数据库连接"""
|
||||
if self.DATABASE_TYPE == "mysql":
|
||||
return f"mysql+pymysql://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}?charset=utf8mb4"
|
||||
elif self.DATABASE_TYPE == "postgresql":
|
||||
elif self.DATABASE_TYPE == "postgres":
|
||||
return f"postgresql+psycopg2://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
|
||||
elif self.DATABASE_TYPE == "sqlite":
|
||||
return f"sqlite:///{self.BASE_DIR.joinpath(self.DATABASE_NAME + '.db')}?charset=utf8"
|
||||
else:
|
||||
raise ValueError(f"数据库驱动不支持: {self.DATABASE_TYPE}, 请选择 mysql、postgresql、sqlite")
|
||||
|
||||
@property
|
||||
def MONGO_DB_URI(self) -> str:
|
||||
"""获取MongoDB连接"""
|
||||
return f"mongodb://{self.MONGO_DB_USER}:{self.MONGO_DB_PASSWORD}@{self.MONGO_DB_HOST}:{self.MONGO_DB_PORT}/{self.MONGO_DB_NAME}"
|
||||
raise ValueError(f"数据库驱动不支持: {self.DATABASE_TYPE}, 请选择 mysql、postgres")
|
||||
|
||||
@property
|
||||
def REDIS_URI(self) -> str:
|
||||
|
||||
@@ -37,9 +37,10 @@ async_engine: AsyncEngine = create_async_engine(
|
||||
pool_pre_ping=settings.POOL_PRE_PING,
|
||||
future=settings.FUTURE,
|
||||
pool_recycle=settings.POOL_RECYCLE,
|
||||
# pool_size=settings.POOL_SIZE, # sqlite 不支持
|
||||
# max_overflow=settings.MAX_OVERFLOW, # sqlite 不支持
|
||||
# pool_timeout=settings.POOL_TIMEOUT, # sqlite 不支持
|
||||
pool_size=settings.POOL_SIZE,
|
||||
max_overflow=settings.MAX_OVERFLOW,
|
||||
pool_timeout=settings.POOL_TIMEOUT,
|
||||
pool_use_lifo=settings.POOL_USE_LIFO,
|
||||
)
|
||||
|
||||
# 异步数据库会话工厂
|
||||
@@ -103,37 +104,3 @@ async def redis_connect(app: FastAPI, status: bool) -> Redis | None:
|
||||
else:
|
||||
await app.state.redis.close()
|
||||
logger.info('✅️ Redis连接已关闭')
|
||||
|
||||
async def mongodb_connect(app: FastAPI, status: bool) -> AsyncIOMotorClient | None:
|
||||
"""
|
||||
创建或关闭MongoDB连接。
|
||||
|
||||
参数:
|
||||
- app (FastAPI): FastAPI应用实例。
|
||||
- status (bool): 连接状态,True为创建连接,False为关闭连接。
|
||||
|
||||
返回:
|
||||
- AsyncIOMotorClient | None: MongoDB异步客户端实例,如果连接失败则返回None。
|
||||
"""
|
||||
if not settings.MONGO_DB_ENABLE:
|
||||
raise CustomException(msg="请先开启MongoDB连接", data="请启用 app/core/config.py: MONGO_DB_ENABLE")
|
||||
|
||||
if status:
|
||||
try:
|
||||
|
||||
client = AsyncIOMotorClient(
|
||||
settings.MONGO_DB_URI,
|
||||
maxPoolSize=settings.POOL_SIZE,
|
||||
minPoolSize=settings.MAX_OVERFLOW,
|
||||
serverSelectionTimeoutMS=settings.POOL_TIMEOUT * 1000
|
||||
)
|
||||
app.state.mongo_client = client
|
||||
app.state.mongo = client[settings.MONGO_DB_NAME]
|
||||
data = await client.server_info()
|
||||
logger.info("✅️ MongoDB连接成功...", data)
|
||||
return client
|
||||
except Exception as e:
|
||||
raise ValueError(f"MongoDB连接失败: {e}")
|
||||
else:
|
||||
app.state.mongo_client.close()
|
||||
logger.info("✅️ MongoDB连接已关闭")
|
||||
@@ -6,7 +6,6 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
from typing import AsyncGenerator, Optional
|
||||
from fastapi import Depends, Request
|
||||
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||
from fastapi import Depends
|
||||
|
||||
from app.api.v1.module_system.user.schema import UserOutSchema
|
||||
@@ -43,17 +42,6 @@ async def redis_getter(request: Request) -> Redis:
|
||||
"""
|
||||
return request.app.state.redis
|
||||
|
||||
async def mongo_getter(request: Request) -> AsyncIOMotorDatabase:
|
||||
"""获取MongoDB连接
|
||||
|
||||
参数:
|
||||
- request (Request): 请求对象
|
||||
|
||||
返回:
|
||||
- AsyncIOMotorDatabase: MongoDB连接
|
||||
"""
|
||||
return request.app.state.mongo
|
||||
|
||||
async def get_current_user(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(db_getter),
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
# mongo_curd.py
|
||||
import datetime
|
||||
import json
|
||||
from typing import Any, List, Optional, Dict, Union
|
||||
from bson import ObjectId
|
||||
from bson.errors import InvalidId
|
||||
from bson.json_util import dumps
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||
from pymongo.results import InsertOneResult, UpdateResult, DeleteResult
|
||||
|
||||
from app.core.exceptions import CustomException
|
||||
|
||||
class MongoCURD:
|
||||
"""
|
||||
MongoDB 数据库管理器
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
db: AsyncIOMotorDatabase,
|
||||
collection: str,
|
||||
schema: Any = None
|
||||
):
|
||||
"""
|
||||
初始化 MongoDB CURD 类。
|
||||
|
||||
参数:
|
||||
- db (AsyncIOMotorDatabase): 数据库连接。
|
||||
- collection (str): 集合名称。
|
||||
- schema (Any | None): 序列化对象,传入模型以进行编码,默认 None。
|
||||
|
||||
返回:
|
||||
- None
|
||||
"""
|
||||
self.db = db
|
||||
self.collection = db[collection]
|
||||
self.schema = schema
|
||||
|
||||
async def get(self, _id: Optional[str] = None, **kwargs) -> Optional[Dict]:
|
||||
"""
|
||||
获取单个数据,默认使用 ID 查询,否则使用关键词查询。
|
||||
|
||||
参数:
|
||||
- _id (str | None): 数据 ID,若提供则按 ID 查询。
|
||||
- kwargs (Dict[str, Any]): 查询条件键值对。
|
||||
|
||||
返回:
|
||||
- Dict | None: 查询到的数据字典,未找到返回 None。
|
||||
|
||||
异常:
|
||||
- CustomException: 当 ID 无效或查询发生错误时抛出。
|
||||
"""
|
||||
try:
|
||||
if _id:
|
||||
kwargs["_id"] = ObjectId(_id)
|
||||
params = self.filter_condition(**kwargs)
|
||||
data = await self.collection.find_one(params)
|
||||
if not data:
|
||||
return None
|
||||
return jsonable_encoder(self.schema(**data)) if self.schema else data
|
||||
except InvalidId:
|
||||
raise CustomException(msg="无效的ID格式")
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f"查询数据失败: {str(e)}")
|
||||
|
||||
async def create(self, data: Union[Dict, Any]) -> InsertOneResult:
|
||||
"""
|
||||
创建数据。
|
||||
|
||||
参数:
|
||||
- data (Dict | Any): 要创建的数据,可为字典或可编码对象。
|
||||
|
||||
返回:
|
||||
- InsertOneResult: 插入操作结果。
|
||||
|
||||
异常:
|
||||
- CustomException: 创建失败时抛出。
|
||||
"""
|
||||
try:
|
||||
if not isinstance(data, dict):
|
||||
data = jsonable_encoder(data)
|
||||
|
||||
# 添加时间戳
|
||||
now = datetime.datetime.now()
|
||||
data.update({
|
||||
'created_at': now,
|
||||
'updated_at': now
|
||||
})
|
||||
|
||||
result = await self.collection.insert_one(data)
|
||||
if not result.acknowledged:
|
||||
raise CustomException(msg="创建数据失败")
|
||||
return result
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f"创建数据失败: {str(e)}")
|
||||
|
||||
async def update(self, _id: str, data: Union[Dict, Any], upsert: bool = False) -> UpdateResult:
|
||||
"""
|
||||
更新数据。
|
||||
|
||||
参数:
|
||||
- _id (str): 数据 ID。
|
||||
- data (Dict | Any): 要更新的数据,可为字典或可编码对象。
|
||||
- upsert (bool): 不存在是否插入,默认 False。
|
||||
|
||||
返回:
|
||||
- UpdateResult: 更新操作结果。
|
||||
|
||||
异常:
|
||||
- CustomException: ID 无效或更新失败时抛出。
|
||||
"""
|
||||
try:
|
||||
if not isinstance(data, dict):
|
||||
data = jsonable_encoder(data)
|
||||
|
||||
# 更新时间戳
|
||||
data['updated_at'] = datetime.datetime.now()
|
||||
|
||||
result = await self.collection.update_one(
|
||||
{'_id': ObjectId(_id)},
|
||||
{'$set': data},
|
||||
upsert=upsert
|
||||
)
|
||||
if result.matched_count == 0 and not upsert:
|
||||
raise CustomException(msg="更新失败,未找到对应数据")
|
||||
return result
|
||||
except InvalidId:
|
||||
raise CustomException(msg="无效的ID格式")
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f"更新数据失败: {str(e)}")
|
||||
|
||||
async def delete(self, _id: Union[str, List[str]]) -> DeleteResult:
|
||||
"""
|
||||
删除数据,支持批量删除。
|
||||
|
||||
参数:
|
||||
- _id (str | List[str]): 单个 ID 或 ID 列表。
|
||||
|
||||
返回:
|
||||
- DeleteResult: 删除操作结果。
|
||||
|
||||
异常:
|
||||
- CustomException: ID 无效或未删除任何数据时抛出。
|
||||
"""
|
||||
try:
|
||||
if isinstance(_id, list):
|
||||
result = await self.collection.delete_many({'_id': {'$in': [ObjectId(i) for i in _id]}})
|
||||
else:
|
||||
result = await self.collection.delete_one({'_id': ObjectId(_id)})
|
||||
|
||||
if result.deleted_count == 0:
|
||||
raise CustomException(msg="删除失败,未找到对应数据")
|
||||
return result
|
||||
except InvalidId:
|
||||
raise CustomException(msg="无效的ID格式")
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f"删除数据失败: {str(e)}")
|
||||
|
||||
async def list(
|
||||
self,
|
||||
page_no: Optional[int] = 1,
|
||||
page_size: Optional[int] = 10,
|
||||
order_by: Optional[List[Dict]] = None,
|
||||
**kwargs
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
查询数据列表。
|
||||
|
||||
参数:
|
||||
- page_no (int | None): 页码,默认 1。
|
||||
- page_size (int | None): 每页数量,默认 10。
|
||||
- order_by (List[Dict] | None): 排序条件,形如 [{'field': '字段名', 'direction': 1}]。
|
||||
- kwargs (Dict[str, Any]): 查询条件键值对。
|
||||
|
||||
返回:
|
||||
- List[Dict]: 数据列表。
|
||||
|
||||
异常:
|
||||
- CustomException: 查询失败时抛出。
|
||||
"""
|
||||
try:
|
||||
params = self.filter_condition(**kwargs)
|
||||
cursor = self.collection.find(params)
|
||||
|
||||
# 排序处理
|
||||
if order_by:
|
||||
sort_conditions = [(item['field'], item['direction']) for item in order_by]
|
||||
cursor.sort(sort_conditions)
|
||||
|
||||
# 分页处理
|
||||
if page_no and page_size:
|
||||
cursor.skip((page_no - 1) * page_size).limit(page_size)
|
||||
|
||||
data_list = [json.loads(dumps(row)) async for row in cursor]
|
||||
return [jsonable_encoder(self.schema(**data)) for data in data_list] if self.schema else data_list
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f"查询列表失败: {str(e)}")
|
||||
|
||||
async def count(self, **kwargs) -> int:
|
||||
"""
|
||||
获取数据总数。
|
||||
|
||||
参数:
|
||||
- kwargs (Dict[str, Any]): 查询条件键值对。
|
||||
|
||||
返回:
|
||||
- int: 数据总数。
|
||||
|
||||
异常:
|
||||
- CustomException: 统计失败时抛出。
|
||||
"""
|
||||
try:
|
||||
params = self.filter_condition(**kwargs)
|
||||
return await self.collection.count_documents(params)
|
||||
except Exception as e:
|
||||
raise CustomException(msg=f"统计数据失败: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def filter_condition(**kwargs) -> Dict:
|
||||
"""
|
||||
构建过滤条件。
|
||||
|
||||
参数:
|
||||
- kwargs (Dict[str, Any]): 查询参数,支持 ('like'|'between'|'ObjectId'|'in'|'gt'|'gte'|'lt'|'lte') 等操作。
|
||||
|
||||
返回:
|
||||
- Dict: 过滤条件字典。
|
||||
|
||||
异常:
|
||||
- CustomException: 当 ObjectId 格式无效时抛出。
|
||||
"""
|
||||
params = {}
|
||||
for k, v in kwargs.items():
|
||||
if not v:
|
||||
continue
|
||||
|
||||
if isinstance(v, tuple):
|
||||
if v[0] == "like" and v[1]:
|
||||
params[k] = {'$regex': v[1], '$options': 'i'} # i表示不区分大小写
|
||||
elif v[0] == "between" and len(v[1]) == 2:
|
||||
params[k] = {
|
||||
'$gte': f"{v[1][0]} 00:00:00",
|
||||
'$lt': f"{v[1][1]} 23:59:59"
|
||||
}
|
||||
elif v[0] == "ObjectId" and v[1]:
|
||||
try:
|
||||
params[k] = ObjectId(v[1])
|
||||
except InvalidId:
|
||||
raise CustomException(msg="无效的ObjectId格式")
|
||||
elif v[0] == "in" and v[1]:
|
||||
params[k] = {'$in': v[1]}
|
||||
elif v[0] == "gt":
|
||||
params[k] = {'$gt': v[1]}
|
||||
elif v[0] == "gte":
|
||||
params[k] = {'$gte': v[1]}
|
||||
elif v[0] == "lt":
|
||||
params[k] = {'$lt': v[1]}
|
||||
elif v[0] == "lte":
|
||||
params[k] = {'$lte': v[1]}
|
||||
else:
|
||||
params[k] = v
|
||||
|
||||
return params
|
||||
|
||||
# from app.api.v1.module_system.log.schema import OperationLogOutSchema
|
||||
|
||||
# class OperationRecordDal(MongoCURD):
|
||||
# """
|
||||
# 操作记录数据访问层
|
||||
# """
|
||||
|
||||
# def __init__(self, db: AsyncIOMotorDatabase):
|
||||
# """
|
||||
# 初始化操作记录数据访问层。
|
||||
|
||||
# :param db: 数据库连接
|
||||
# """
|
||||
# super().__init__(
|
||||
# db=db,
|
||||
# collection="system_operation_log",
|
||||
# schema=OperationLogOutSchema,
|
||||
# )
|
||||
|
||||
# 创建日志到mongodb(已测试成功,可以成功创建):暂时注释,是因为该中间保存日志到mongodb(已调试成功),而我现在实现的是记录到mysql的log表
|
||||
# if not settings.MONGO_DB_ENABLE:
|
||||
# return response
|
||||
# document = OperationLogCreateSchema(
|
||||
# request_ip = request.client.host,
|
||||
# request_os = user_agent.os.family,
|
||||
# request_browser = user_agent.browser.family,
|
||||
# request_path = request.url.path,
|
||||
# request_method = request.method,
|
||||
# request_payload = oper_param,
|
||||
# response_code = response.status_code,
|
||||
# response_json = response_data.decode(),
|
||||
# description = route.name,
|
||||
# creator_id = creator_id
|
||||
# )
|
||||
# from app.core.mongo_curd import OperationRecordDal
|
||||
# from app.core.dependencies import mongo_getter
|
||||
# operation_record_dal = OperationRecordDal(db = await mongo_getter(request))
|
||||
# await operation_record_dal.create(data=document.model_dump())
|
||||
@@ -66,7 +66,7 @@
|
||||
"permission": "system:menu:query",
|
||||
"route_name": "Menu",
|
||||
"route_path": "/system/menu",
|
||||
"component_path": "system/menu/index",
|
||||
"component_path": "module_system/menu/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -82,7 +82,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:menu:create",
|
||||
"permission": "module_system:menu:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -101,7 +101,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:menu:update",
|
||||
"permission": "module_system:menu:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -120,7 +120,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:menu:delete",
|
||||
"permission": "module_system:menu:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -139,7 +139,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:menu:patch",
|
||||
"permission": "module_system:menu:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -160,10 +160,10 @@
|
||||
"type": 2,
|
||||
"icon": "tree",
|
||||
"order": 2,
|
||||
"permission": "system:dept:query",
|
||||
"permission": "module_system:dept:query",
|
||||
"route_name": "Dept",
|
||||
"route_path": "/system/dept",
|
||||
"component_path": "system/dept/index",
|
||||
"component_path": "module_system/dept/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"always_show": false,
|
||||
@@ -179,7 +179,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:dept:create",
|
||||
"permission": "module_system:dept:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -198,7 +198,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:dept:update",
|
||||
"permission": "module_system:dept:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -217,7 +217,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:dept:delete",
|
||||
"permission": "module_system:dept:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -236,7 +236,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:dept:patch",
|
||||
"permission": "module_system:dept:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -257,10 +257,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-Coordinate",
|
||||
"order": 3,
|
||||
"permission": "system:position:query",
|
||||
"permission": "module_system:position:query",
|
||||
"route_name": "Position",
|
||||
"route_path": "/system/position",
|
||||
"component_path": "system/position/index",
|
||||
"component_path": "module_system/position/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"always_show": false,
|
||||
@@ -276,7 +276,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:position:create",
|
||||
"permission": "module_system:position:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -295,7 +295,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:position:update",
|
||||
"permission": "module_system:position:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -314,7 +314,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:position:delete",
|
||||
"permission": "module_system:position:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -333,7 +333,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:position:patch",
|
||||
"permission": "module_system:position:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -352,7 +352,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "system:position:export",
|
||||
"permission": "module_system:position:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -371,7 +371,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 8,
|
||||
"permission": "system:role:permission",
|
||||
"permission": "module_system:role:permission",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -392,10 +392,10 @@
|
||||
"type": 2,
|
||||
"icon": "role",
|
||||
"order": 4,
|
||||
"permission": "system:role:query",
|
||||
"permission": "module_system:role:query",
|
||||
"route_name": "Role",
|
||||
"route_path": "/system/role",
|
||||
"component_path": "system/role/index",
|
||||
"component_path": "module_system/role/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -411,7 +411,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:role:create",
|
||||
"permission": "module_system:role:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -430,7 +430,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:role:update",
|
||||
"permission": "module_system:role:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -449,7 +449,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:role:delete",
|
||||
"permission": "module_system:role:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -468,7 +468,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:role:patch",
|
||||
"permission": "module_system:role:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -487,7 +487,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 6,
|
||||
"permission": "system:role:export",
|
||||
"permission": "module_system:role:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -508,10 +508,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-User",
|
||||
"order": 5,
|
||||
"permission": "system:user:query",
|
||||
"permission": "module_system:user:query",
|
||||
"route_name": "User",
|
||||
"route_path": "/system/user",
|
||||
"component_path": "system/user/index",
|
||||
"component_path": "module_system/user/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -527,7 +527,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:user:create",
|
||||
"permission": "module_system:user:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -546,7 +546,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:user:update",
|
||||
"permission": "module_system:user:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -565,7 +565,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:user:delete",
|
||||
"permission": "module_system:user:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -584,7 +584,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:user:patch",
|
||||
"permission": "module_system:user:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -603,7 +603,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "system:user:export",
|
||||
"permission": "module_system:user:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -622,7 +622,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 6,
|
||||
"permission": "system:user:import",
|
||||
"permission": "module_system:user:import",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -643,10 +643,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-Aim",
|
||||
"order": 6,
|
||||
"permission": "system:log:query",
|
||||
"permission": "module_system:log:query",
|
||||
"route_name": "Log",
|
||||
"route_path": "/system/log",
|
||||
"component_path": "system/log/index",
|
||||
"component_path": "module_system/log/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -662,7 +662,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:operation_log:delete",
|
||||
"permission": "module_system:operation_log:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -681,7 +681,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:operation_log:export",
|
||||
"permission": "module_system:operation_log:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -702,10 +702,10 @@
|
||||
"type": 2,
|
||||
"icon": "bell",
|
||||
"order": 7,
|
||||
"permission": "system:notice:query",
|
||||
"permission": "module_system:notice:query",
|
||||
"route_name": "Notice",
|
||||
"route_path": "/system/notice",
|
||||
"component_path": "system/notice/index",
|
||||
"component_path": "module_system/notice/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -721,7 +721,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:notice:create",
|
||||
"permission": "module_system:notice:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -740,7 +740,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:notice:update",
|
||||
"permission": "module_system:notice:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -759,7 +759,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:notice:delete",
|
||||
"permission": "module_system:notice:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -778,7 +778,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:notice:export",
|
||||
"permission": "module_system:notice:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -797,7 +797,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "system:notice:patch",
|
||||
"permission": "module_system:notice:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -818,10 +818,10 @@
|
||||
"type": 2,
|
||||
"icon": "setting",
|
||||
"order": 8,
|
||||
"permission": "system:param:query",
|
||||
"permission": "module_system:param:query",
|
||||
"route_name": "Params",
|
||||
"route_path": "/system/param",
|
||||
"component_path": "system/param/index",
|
||||
"component_path": "module_system/param/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -837,7 +837,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:param:create",
|
||||
"permission": "module_system:param:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -856,7 +856,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:param:update",
|
||||
"permission": "module_system:param:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -875,7 +875,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:param:delete",
|
||||
"permission": "module_system:param:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -894,7 +894,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:param:export",
|
||||
"permission": "module_system:param:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -913,7 +913,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "system:param:upload",
|
||||
"permission": "module_system:param:upload",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -934,10 +934,10 @@
|
||||
"type": 2,
|
||||
"icon": "dict",
|
||||
"order": 9,
|
||||
"permission": "system:dict_type:query",
|
||||
"permission": "module_system:dict_type:query",
|
||||
"route_name": "Dict",
|
||||
"route_path": "/system/dict",
|
||||
"component_path": "system/dict/index",
|
||||
"component_path": "module_system/dict/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -953,7 +953,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "system:dict_type:create",
|
||||
"permission": "module_system:dict_type:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -972,7 +972,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "system:dict_type:update",
|
||||
"permission": "module_system:dict_type:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -991,7 +991,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "system:dict_type:delete",
|
||||
"permission": "module_system:dict_type:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1010,7 +1010,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "system:dict_type:export",
|
||||
"permission": "module_system:dict_type:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1029,7 +1029,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "system:dict_type:patch",
|
||||
"permission": "module_system:dict_type:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1048,7 +1048,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 6,
|
||||
"permission": "system:dict_data:query",
|
||||
"permission": "module_system:dict_data:query",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1067,7 +1067,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 7,
|
||||
"permission": "system:dict_data:create",
|
||||
"permission": "module_system:dict_data:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1086,7 +1086,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 8,
|
||||
"permission": "system:dict_data:update",
|
||||
"permission": "module_system:dict_data:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1105,7 +1105,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 9,
|
||||
"permission": "system:dict_data:delete",
|
||||
"permission": "module_system:dict_data:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1124,7 +1124,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 10,
|
||||
"permission": "system:dict_data:export",
|
||||
"permission": "module_system:dict_data:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1143,7 +1143,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 11,
|
||||
"permission": "system:dict_data:patch",
|
||||
"permission": "module_system:dict_data:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1185,10 +1185,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-ShoppingCartFull",
|
||||
"order": 1,
|
||||
"permission": "app:myapp:query",
|
||||
"permission": "module_application:myapp:query",
|
||||
"route_name": "MYAPP",
|
||||
"route_path": "/application/myapp",
|
||||
"component_path": "application/myapp/index",
|
||||
"component_path": "module_application/myapp/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -1204,7 +1204,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "app:myapp:create",
|
||||
"permission": "module_application:myapp:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1223,7 +1223,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "app:myapp:update",
|
||||
"permission": "module_application:myapp:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1242,7 +1242,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "app:myapp:delete",
|
||||
"permission": "module_application:myapp:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1261,7 +1261,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "app:myapp:patch",
|
||||
"permission": "module_application:myapp:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1282,10 +1282,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-DataLine",
|
||||
"order": 2,
|
||||
"permission": "app:job:query",
|
||||
"permission": "module_application:job:query",
|
||||
"route_name": "Job",
|
||||
"route_path": "/application/job",
|
||||
"component_path": "application/job/index",
|
||||
"component_path": "module_application/job/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -1301,7 +1301,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "app:job:create",
|
||||
"permission": "module_application:job:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1320,7 +1320,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "app:job:update",
|
||||
"permission": "module_application:job:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1339,7 +1339,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "app:job:delete",
|
||||
"permission": "module_application:job:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1358,7 +1358,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "app:job:export",
|
||||
"permission": "module_application:job:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1379,10 +1379,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-ToiletPaper",
|
||||
"order": 3,
|
||||
"permission": "app:ai:chat",
|
||||
"permission": "module_application:ai:chat",
|
||||
"route_name": "AI",
|
||||
"route_path": "/application/ai",
|
||||
"component_path": "application/ai/index",
|
||||
"component_path": "module_application/ai/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -1398,7 +1398,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "app:ai:chat",
|
||||
"permission": "module_application:ai:chat",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1419,10 +1419,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-ShoppingBag",
|
||||
"order": 4,
|
||||
"permission": "app:workflow:query",
|
||||
"permission": "module_application:workflow:query",
|
||||
"route_name": "Workflow",
|
||||
"route_path": "/application/workflow",
|
||||
"component_path": "application/workflow/index",
|
||||
"component_path": "module_application/workflow/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -1459,10 +1459,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-Headset",
|
||||
"order": 1,
|
||||
"permission": "monitor:online:query",
|
||||
"permission": "module_monitor:online:query",
|
||||
"route_name": "MonitorOnline",
|
||||
"route_path": "/monitor/online",
|
||||
"component_path": "monitor/online/index",
|
||||
"component_path": "module_monitor/online/index",
|
||||
"status": true,
|
||||
"keep_alive": false,
|
||||
"hidden": false,
|
||||
@@ -1478,7 +1478,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "monitor:online:delete",
|
||||
"permission": "module_monitor:online:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1499,10 +1499,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-Odometer",
|
||||
"order": 2,
|
||||
"permission": "monitor:server:query",
|
||||
"permission": "module_monitor:server:query",
|
||||
"route_name": "MonitorServer",
|
||||
"route_path": "/monitor/server",
|
||||
"component_path": "monitor/server/index",
|
||||
"component_path": "module_monitor/server/index",
|
||||
"status": true,
|
||||
"keep_alive": false,
|
||||
"hidden": false,
|
||||
@@ -1518,10 +1518,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-Stopwatch",
|
||||
"order": 3,
|
||||
"permission": "monitor:cache:query",
|
||||
"permission": "module_monitor:cache:query",
|
||||
"route_name": "MonitorCache",
|
||||
"route_path": "/monitor/cache",
|
||||
"component_path": "monitor/cache/index",
|
||||
"component_path": "module_monitor/cache/index",
|
||||
"status": true,
|
||||
"keep_alive": false,
|
||||
"hidden": false,
|
||||
@@ -1537,7 +1537,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "monitor:cache:delete",
|
||||
"permission": "module_monitor:cache:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1558,10 +1558,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-Files",
|
||||
"order": 4,
|
||||
"permission": "monitor:resource:query",
|
||||
"permission": "module_monitor:resource:query",
|
||||
"route_name": "Resource",
|
||||
"route_path": "/monitor/resource",
|
||||
"component_path": "monitor/resource/index",
|
||||
"component_path": "module_monitor/resource/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -1577,7 +1577,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "monitor:resource:upload",
|
||||
"permission": "module_monitor:resource:upload",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1596,7 +1596,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "monitor:resource:download",
|
||||
"permission": "module_monitor:resource:download",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1615,7 +1615,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "monitor:resource:delete",
|
||||
"permission": "module_monitor:resource:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1634,7 +1634,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "monitor:resource:move",
|
||||
"permission": "module_monitor:resource:move",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1653,7 +1653,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "rmonitor:resource:copy",
|
||||
"permission": "module_monitor:resource:copy",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1672,7 +1672,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 6,
|
||||
"permission": "monitor:resource:rename",
|
||||
"permission": "module_monitor:resource:rename",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1691,7 +1691,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 7,
|
||||
"permission": "monitor:resource:create_dir",
|
||||
"permission": "module_monitor:resource:create_dir",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1710,7 +1710,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 9,
|
||||
"permission": "rmonitor:resource:export",
|
||||
"permission": "module_monitor:resource:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1752,10 +1752,10 @@
|
||||
"type": 2,
|
||||
"icon": "code",
|
||||
"order": 1,
|
||||
"permission": "generator:gencode:query",
|
||||
"permission": "module_generator:gencode:query",
|
||||
"route_name": "Backcode",
|
||||
"route_path": "/gencode/backcode",
|
||||
"component_path": "gencode/backcode/index",
|
||||
"component_path": "module_generator/backcode/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -1771,7 +1771,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "generator:gencode:query",
|
||||
"permission": "module_generator:gencode:query",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1790,7 +1790,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "generator:gencode:create",
|
||||
"permission": "module_generator:gencode:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1809,7 +1809,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "generator:gencode:update",
|
||||
"permission": "module_generator:gencode:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1828,7 +1828,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "generator:gencode:delete",
|
||||
"permission": "module_generator:gencode:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1847,7 +1847,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "generator:gencode:import",
|
||||
"permission": "module_generator:gencode:import",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1866,7 +1866,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 6,
|
||||
"permission": "generator:gencode:operate",
|
||||
"permission": "module_generator:gencode:operate",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1885,7 +1885,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 7,
|
||||
"permission": "generator:gencode:code",
|
||||
"permission": "module_generator:gencode:code",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1904,7 +1904,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 8,
|
||||
"permission": "generator:dblist:query",
|
||||
"permission": "module_generator:dblist:query",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1923,7 +1923,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 9,
|
||||
"permission": "generator:db:sync",
|
||||
"permission": "module_generator:db:sync",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1944,10 +1944,10 @@
|
||||
"type": 2,
|
||||
"icon": "el-icon-DataLine",
|
||||
"order": 3,
|
||||
"permission": "generator:demo:query",
|
||||
"permission": "module_generator:demo:query",
|
||||
"route_name": "Demo",
|
||||
"route_path": "/gencode/demo",
|
||||
"component_path": "gencode/demo/index",
|
||||
"component_path": "module_generator/demo/index",
|
||||
"status": true,
|
||||
"keep_alive": true,
|
||||
"hidden": false,
|
||||
@@ -1963,7 +1963,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"permission": "generator:demo:create",
|
||||
"permission": "module_generator:demo:create",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -1982,7 +1982,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 2,
|
||||
"permission": "generator:demo:update",
|
||||
"permission": "module_generator:demo:update",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -2001,7 +2001,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 3,
|
||||
"permission": "generator:demo:delete",
|
||||
"permission": "module_generator:demo:delete",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -2020,7 +2020,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 4,
|
||||
"permission": "generator:demo:patch",
|
||||
"permission": "module_generator:demo:patch",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -2039,7 +2039,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"permission": "generator:demo:export",
|
||||
"permission": "module_generator:demo:export",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -2058,7 +2058,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 6,
|
||||
"permission": "generator:demo:import",
|
||||
"permission": "module_generator:demo:import",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -2077,7 +2077,7 @@
|
||||
"type": 3,
|
||||
"icon": null,
|
||||
"order": 7,
|
||||
"permission": "generator:demo:download",
|
||||
"permission": "module_generator:demo:download",
|
||||
"route_name": null,
|
||||
"route_path": null,
|
||||
"component_path": null,
|
||||
@@ -2119,10 +2119,10 @@
|
||||
"type": 4,
|
||||
"icon": "api",
|
||||
"order": 1,
|
||||
"permission": "common:docs:query",
|
||||
"permission": "module_common:docs:query",
|
||||
"route_name": "Docs",
|
||||
"route_path": "/common/docs",
|
||||
"component_path": "common/docs/index",
|
||||
"component_path": "module_common/docs/index",
|
||||
"status": true,
|
||||
"keep_alive": false,
|
||||
"hidden": false,
|
||||
@@ -2138,10 +2138,10 @@
|
||||
"type": 4,
|
||||
"icon": "el-icon-Document",
|
||||
"order": 2,
|
||||
"permission": "common:redoc:query",
|
||||
"permission": "module_common:redoc:query",
|
||||
"route_name": "Redoc",
|
||||
"route_path": "/common/redoc",
|
||||
"component_path": "common/redoc/index",
|
||||
"component_path": "module_common/redoc/index",
|
||||
"status": true,
|
||||
"keep_alive": false,
|
||||
"hidden": false,
|
||||
|
||||
@@ -340,7 +340,7 @@ class SqlalchemyUtil:
|
||||
:param need_explicit_null: 是否需要显式DEFAULT NULL
|
||||
:return: 不同数据库方言对应的null_server_default
|
||||
"""
|
||||
if need_explicit_null and dialect_name == 'postgresql':
|
||||
if need_explicit_null and dialect_name == 'postgres':
|
||||
return null()
|
||||
return None
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import re
|
||||
from typing import List
|
||||
|
||||
from app.common.constant import GenConstant
|
||||
from app.config.setting import settings
|
||||
from app.utils.string_util import StringUtil
|
||||
from app.api.v1.module_generator.gencode.schema import GenTableOutSchema, GenTableSchema, GenTableColumnSchema
|
||||
|
||||
@@ -25,8 +24,8 @@ class GenUtils:
|
||||
"""
|
||||
# 只有当字段为None时才设置默认值
|
||||
gen_table.class_name = cls.convert_class_name(gen_table.table_name or "")
|
||||
gen_table.package_name = settings.package_name
|
||||
gen_table.module_name = settings.package_name.split('.')[-1]
|
||||
gen_table.package_name = 'module_gencode'
|
||||
gen_table.module_name = gen_table.package_name.split('.')[-1]
|
||||
gen_table.business_name = gen_table.table_name.split('_')[-1]
|
||||
gen_table.function_name = re.sub(r'(?:表|测试)', '', gen_table.table_comment or "")
|
||||
|
||||
@@ -44,32 +43,16 @@ class GenUtils:
|
||||
"""
|
||||
data_type = cls.get_db_type(column.column_type or "")
|
||||
column_name = column.column_name or ""
|
||||
if not table.id:
|
||||
raise ValueError("业务表ID不能为空")
|
||||
column.table_id = table.id
|
||||
column.python_field = cls.to_camel_case(column_name)
|
||||
# 只有当python_type为None时才设置默认类型
|
||||
column.python_type = StringUtil.get_mapping_value_by_key_ignore_case(GenConstant.DB_TO_PYTHON, data_type)
|
||||
# 查询类型:优先根据字段语义(如以name结尾走LIKE),否则默认EQ
|
||||
column.query_type = GenConstant.QUERY_LIKE
|
||||
|
||||
# 确保is_pk等字段为字符串格式
|
||||
# 将布尔值或其他类型转换为字符串'1'或'0'
|
||||
if column.is_pk is not None and not isinstance(column.is_pk, str):
|
||||
column.is_pk = '1' if bool(column.is_pk) else '0'
|
||||
if column.is_increment is not None and not isinstance(column.is_increment, str):
|
||||
column.is_increment = '1' if bool(column.is_increment) else '0'
|
||||
if column.is_required is not None and not isinstance(column.is_required, str):
|
||||
column.is_required = '1' if bool(column.is_required) else '0'
|
||||
if column.is_unique is not None and not isinstance(column.is_unique, str):
|
||||
column.is_unique = '1' if bool(column.is_unique) else '0'
|
||||
|
||||
# 确保None值默认为'0'
|
||||
column.is_pk = column.is_pk or '0'
|
||||
column.is_increment = column.is_increment or '0'
|
||||
column.is_required = column.is_required or '0'
|
||||
column.is_unique = column.is_unique or '0'
|
||||
# 确保column_length和column_default字段有默认值
|
||||
if column.column_length is None:
|
||||
column.column_length = ''
|
||||
|
||||
if column.column_default is None:
|
||||
column.column_default = ''
|
||||
|
||||
@@ -103,52 +86,58 @@ class GenUtils:
|
||||
column.html_type = GenConstant.HTML_INPUT
|
||||
|
||||
# 只有当is_insert为None时才设置插入字段(默认所有字段都需要插入)
|
||||
if column.is_insert is None:
|
||||
if column.is_insert:
|
||||
column.is_insert = GenConstant.REQUIRE
|
||||
else:
|
||||
# 确保is_insert为字符串格式,并且值为'0'或'1'
|
||||
column.is_insert = '1' if (column.is_insert is True or str(column.is_insert).lower() in ('1', 'true', 'yes')) else '0'
|
||||
column.is_insert = False
|
||||
|
||||
# 只有当is_edit为None时才设置编辑字段
|
||||
if column.is_edit is None:
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name) and column.is_pk != '1':
|
||||
column.is_edit = GenConstant.REQUIRE
|
||||
else:
|
||||
column.is_edit = '0'
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_EDIT, column_name) and not column.is_pk:
|
||||
column.is_edit = GenConstant.REQUIRE
|
||||
else:
|
||||
# 确保is_edit为字符串格式,并且值为'0'或'1'
|
||||
column.is_edit = '1' if (column.is_edit is True or str(column.is_edit).lower() in ('1', 'true', 'yes')) else '0'
|
||||
column.is_edit = False
|
||||
|
||||
# 只有当is_list为None时才设置列表字段
|
||||
if column.is_list is None:
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name) and column.is_pk != '1':
|
||||
column.is_list = GenConstant.REQUIRE
|
||||
else:
|
||||
column.is_list = '0'
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_LIST, column_name) and not column.is_pk:
|
||||
column.is_list = GenConstant.REQUIRE
|
||||
else:
|
||||
# 确保is_list为字符串格式,并且值为'0'或'1'
|
||||
column.is_list = '1' if (column.is_list is True or str(column.is_list).lower() in ('1', 'true', 'yes')) else '0'
|
||||
column.is_list = False
|
||||
|
||||
# 只有当is_query为None时才设置查询字段
|
||||
if column.is_query is None:
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name) and column.is_pk != '1':
|
||||
column.is_query = GenConstant.REQUIRE
|
||||
if not cls.arrays_contains(GenConstant.COLUMNNAME_NOT_QUERY, column_name) and not column.is_pk:
|
||||
column.is_query = GenConstant.REQUIRE
|
||||
# 直接设置查询类型,因为我们已经确定这是一个查询字段
|
||||
if column_name.lower().endswith('name') or data_type in ['varchar', 'char', 'text']:
|
||||
column.query_type = GenConstant.QUERY_LIKE
|
||||
else:
|
||||
column.is_query = '0'
|
||||
column.query_type = GenConstant.QUERY_EQ
|
||||
else:
|
||||
# 确保is_query为字符串格式,并且值为'0'或'1'
|
||||
column.is_query = '1' if (column.is_query is True or str(column.is_query).lower() in ('1', 'true', 'yes')) else '0'
|
||||
column.is_query = False
|
||||
column.query_type = None
|
||||
|
||||
@classmethod
|
||||
def arrays_contains(cls, arr: List[str], target_value: str) -> bool:
|
||||
def arrays_contains(cls, arr, target_value) -> bool:
|
||||
"""
|
||||
校验数组是否包含指定值
|
||||
检查目标值是否在数组中
|
||||
|
||||
注意:从根本上解决问题,现在确保传入的参数都是正确的类型:
|
||||
- arr 是列表类型,且在GenConstant中定义
|
||||
- target_value 不会是None
|
||||
|
||||
param arr: 数组
|
||||
param target_value: 需要校验的值
|
||||
:return: 校验结果
|
||||
参数:
|
||||
- arr: 数组类型
|
||||
- target_value: 目标值
|
||||
|
||||
返回:
|
||||
- bool: 如果目标值在数组中,返回True;否则返回False
|
||||
"""
|
||||
return target_value in arr
|
||||
# 从根本上解决问题,不再需要复杂的防御性检查
|
||||
# 因为现在我们确保传入的arr是GenConstant中定义的列表常量
|
||||
# 并且target_value在调用前已经被处理过不会是None
|
||||
|
||||
# 简单直接地执行包含检查
|
||||
target_str = str(target_value).lower()
|
||||
return any(str(item).lower() == target_str for item in arr)
|
||||
|
||||
@classmethod
|
||||
def convert_class_name(cls, table_name: str) -> str:
|
||||
@@ -161,11 +150,6 @@ class GenUtils:
|
||||
返回:
|
||||
- str: Python 类名。
|
||||
"""
|
||||
auto_remove_pre = settings.auto_remove_pre
|
||||
table_prefix = settings.table_prefix
|
||||
if auto_remove_pre and table_prefix:
|
||||
search_list = table_prefix.split(',')
|
||||
table_name = cls.replace_first(table_name, search_list)
|
||||
return StringUtil.convert_to_camel_case(table_name)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -173,15 +173,15 @@ class Jinja2TemplateUtil:
|
||||
|
||||
# 映射表方式简化
|
||||
template_mapping = {
|
||||
'controller.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/module_{module_name}/{business_name}/controller.py',
|
||||
'service.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/module_{module_name}/{business_name}/service.py',
|
||||
'crud.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/module_{module_name}/{business_name}/crud.py',
|
||||
'model.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/module_{module_name}/{business_name}/model.py',
|
||||
'param.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/module_{module_name}/{business_name}/param.py',
|
||||
'schema.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/module_{module_name}/{business_name}/schema.py',
|
||||
'sql.sql.j2': f'{cls.BACKEND_PROJECT_PATH}/sql/module_{module_name}/{business_name}_menu.sql',
|
||||
'api.ts.j2': f'{cls.FRONTEND_PROJECT_PATH}/src/api/module_{module_name}/{business_name}.ts',
|
||||
'index.vue.j2': f'{cls.FRONTEND_PROJECT_PATH}/src/views/module_{module_name}/{business_name}/index.vue'
|
||||
'controller.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/controller.py',
|
||||
'service.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/service.py',
|
||||
'crud.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/crud.py',
|
||||
'model.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/model.py',
|
||||
'param.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/param.py',
|
||||
'schema.py.j2': f'{cls.BACKEND_PROJECT_PATH}/app/api/v1/{module_name}/{business_name}/schema.py',
|
||||
'sql.sql.j2': f'{cls.BACKEND_PROJECT_PATH}/sql/{module_name}/{business_name}_menu.sql',
|
||||
'api.ts.j2': f'{cls.FRONTEND_PROJECT_PATH}/src/api/{module_name}/{business_name}.ts',
|
||||
'index.vue.j2': f'{cls.FRONTEND_PROJECT_PATH}/src/views/{module_name}/{business_name}/index.vue'
|
||||
}
|
||||
|
||||
# 查找匹配的模板路径
|
||||
@@ -242,7 +242,7 @@ class Jinja2TemplateUtil:
|
||||
"""
|
||||
columns = gen_table.columns or []
|
||||
import_list = set()
|
||||
import_list.add('from sqlalchemy import Column')
|
||||
|
||||
for column in columns:
|
||||
if column.column_type:
|
||||
data_type = cls.get_db_type(column.column_type)
|
||||
@@ -339,8 +339,7 @@ class Jinja2TemplateUtil:
|
||||
- Set[str]: 更新后的字典类型集合。
|
||||
"""
|
||||
for column in columns:
|
||||
# 处理column.super_column, column.dict_type, column.html_type为None的情况
|
||||
super_column = column.super_column if column.super_column is not None else False
|
||||
super_column = column.super_column if column.super_column is not None else '0'
|
||||
dict_type = column.dict_type or ''
|
||||
html_type = column.html_type or ''
|
||||
|
||||
|
||||
Vendored
+1
-9
@@ -30,7 +30,7 @@ DESCRIPTION = "该项目是一个基于python的web服务框架,基于fastapi
|
||||
DEMO_ENABLE = False
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_TYPE = "sqlite" # sqlite、mysql、postgresql
|
||||
DATABASE_TYPE = "mysql" # mysql、postgres
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_HOST = "localhost"
|
||||
@@ -47,14 +47,6 @@ REDIS_USER = ''
|
||||
REDIS_PASSWORD = ''
|
||||
REDIS_DB_NAME = 1
|
||||
|
||||
# MongoDB配置
|
||||
MONGO_DB_ENABLE = False
|
||||
MONGO_DB_HOST = "localhost"
|
||||
MONGO_DB_USER = ""
|
||||
MONGO_DB_PASSWORD = ""
|
||||
MONGO_DB_PORT = 27017
|
||||
MONGO_DB_NAME = "admin"
|
||||
|
||||
# 日志配置
|
||||
LOGGER_LEVEL = 'DEBUG' # 日志级别
|
||||
|
||||
|
||||
Vendored
+1
-9
@@ -30,7 +30,7 @@ DESCRIPTION = "该项目是一个基于python的web服务框架,基于fastapi
|
||||
DEMO_ENABLE = True
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_TYPE = "mysql" # sqlite、mysql、postgresql
|
||||
DATABASE_TYPE = "mysql" # mysql、postgres
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_HOST = "172.18.52.77"
|
||||
@@ -47,14 +47,6 @@ REDIS_USER = ''
|
||||
REDIS_PASSWORD = 'FastApi123abc'
|
||||
REDIS_DB_NAME = 1
|
||||
|
||||
# MongoDB配置
|
||||
MONGO_DB_ENABLE = False
|
||||
MONGO_DB_HOST = "172.18.52.77"
|
||||
MONGO_DB_USER = "root"
|
||||
MONGO_DB_PASSWORD = 'FastApi123abc'
|
||||
MONGO_DB_PORT = 27017
|
||||
MONGO_DB_NAME = "fastapiadmin"
|
||||
|
||||
# 日志配置
|
||||
LOGGER_LEVEL = 'INFO'
|
||||
|
||||
|
||||
@@ -23,16 +23,14 @@ greenlet==3.1.1 # 协程框架
|
||||
bcrypt==4.0.1 # 密码加密解析,切勿升级,如果升级,请同时升级python版本
|
||||
itsdangerous==2.2.0 # 用于安全处理各种数据,如密码、密钥等
|
||||
aiofiles==24.1.0 # 文件操作
|
||||
redis==5.2.1 # redis 同步操作数据库(用户celery配套使用)redis 异步操作数据库 redis已经完全具备了aioredis的功能,无需重复安全,且aioredis已经不再维护也不兼容3.10+的版本
|
||||
aiosqlite==0.17.0 # sqlite 异步操作数据库
|
||||
redis==5.2.1 # redis 同/异步操作数据库(用户celery配套使用)redis 异步操作数据库 redis已经完全具备了aioredis的功能,无需重复安全,且aioredis已经不再维护也不兼容3.10+的版本
|
||||
asyncmy==0.2.9 # mysql 异步操作数据库:基于 mysqlclient:asyncmy 是 mysqlclient 的异步版本,mysqlclient 是一个 C 语言编写的 MySQL 客户端,性能较高。性能:asyncmy 通常在性能上优于 aiomysql,特别是在高并发和大数据量的场景下。
|
||||
motor==3.6.0 # mongodb 驱动
|
||||
PyMySQL==1.1.2 # mysql 同步步操作数据库基于 pymysql:aiomysql 是 pymysql 的异步版本,pymysql 是一个纯 Python 实现的 MySQL 客户端。成熟度:aiomysql 相对较为成熟,社区支持较好,文档也比较完善。
|
||||
asyncpg==0.30.0 # postgresql 异步操作数据库基于 psycopg2:asyncpg 是 psycopg2 的异步版本,psycopg2 是一个 pure-Python PostgreSQL 数据库适配器。性能:asyncpg 通常在性能上优于 psycopg2,特别是在高并发和大数据量的场景下。
|
||||
psycopg2==2.9.10 # postgresql 同步操作数据库基于 psycopg2:psycopg2 是一个 pure-Python PostgreSQL 适配器。
|
||||
PyMySQL==1.1.2 # mysql 异步操作数据库基于 pymysql:aiomysql 是 pymysql 的异步版本,pymysql 是一个纯 Python 实现的 MySQL 客户端。成熟度:aiomysql 相对较为成熟,社区支持较好,文档也比较完善。
|
||||
cryptography==45.0.2 # mysql8 密码加密
|
||||
openai==1.55.2 # ai 大模型
|
||||
oss2==2.18.4 # 阿里云对象存储
|
||||
rich==13.9.4 # 终端打印美化
|
||||
sqlglot[rs]==27.8.0 # sql 解析
|
||||
pydantic_validation_decorator==0.1.4 # 模型验证
|
||||
pydantic_validation_decorator==0.1.4 # 模型验证
|
||||
loguru
|
||||
@@ -278,7 +278,7 @@ CREATE TABLE `gen_table_column` (
|
||||
`python_field` varchar(200) DEFAULT NULL COMMENT 'PYTHON字段名',
|
||||
`is_pk` varchar(1) DEFAULT NULL COMMENT '是否主键(1是)',
|
||||
`is_increment` varchar(1) DEFAULT NULL COMMENT '是否自增(1是)',
|
||||
`is_required` varchar(1) DEFAULT NULL COMMENT '是否必填(1是)',
|
||||
`is_nullable` varchar(1) DEFAULT NULL COMMENT '是否必填(1是)',
|
||||
`is_unique` varchar(1) DEFAULT NULL COMMENT '是否唯一(1是)',
|
||||
`is_insert` varchar(1) DEFAULT NULL COMMENT '是否为插入字段(1是)',
|
||||
`is_edit` varchar(1) DEFAULT NULL COMMENT '是否编辑字段(1是)',
|
||||
|
||||
@@ -863,7 +863,7 @@ CREATE TABLE public.gen_table_column (
|
||||
python_field character varying(200),
|
||||
is_pk character varying(1),
|
||||
is_increment character varying(1),
|
||||
is_required character varying(1),
|
||||
is_nullable character varying(1),
|
||||
is_unique character varying(1),
|
||||
is_insert character varying(1),
|
||||
is_edit character varying(1),
|
||||
@@ -955,10 +955,10 @@ COMMENT ON COLUMN public.gen_table_column.is_increment IS '是否自增(1是
|
||||
|
||||
|
||||
--
|
||||
-- Name: COLUMN gen_table_column.is_required; Type: COMMENT; Schema: public; Owner: tao
|
||||
-- Name: COLUMN gen_table_column.is_nullable; Type: COMMENT; Schema: public; Owner: tao
|
||||
--
|
||||
|
||||
COMMENT ON COLUMN public.gen_table_column.is_required IS '是否必填(1是)';
|
||||
COMMENT ON COLUMN public.gen_table_column.is_nullable IS '是否必填(1是)';
|
||||
|
||||
|
||||
--
|
||||
@@ -2788,7 +2788,7 @@ COPY public.gen_table (table_name, table_comment, sub_table_name, sub_table_fk_n
|
||||
-- Data for Name: gen_table_column; Type: TABLE DATA; Schema: public; Owner: tao
|
||||
--
|
||||
|
||||
COPY public.gen_table_column (column_name, column_comment, column_type, column_length, column_default, python_type, python_field, is_pk, is_increment, is_required, is_unique, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, table_id, creator_id, id, description, created_at, updated_at) FROM stdin;
|
||||
COPY public.gen_table_column (column_name, column_comment, column_type, column_length, column_default, python_type, python_field, is_pk, is_increment, is_nullable, is_unique, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, table_id, creator_id, id, description, created_at, updated_at) FROM stdin;
|
||||
\.
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Optional
|
||||
from sqlalchemy import String, Integer, Text, DateTime, Boolean, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
{% for model_import in model_import_list %}
|
||||
{{ model_import }}
|
||||
{% endfor %}
|
||||
{% if table.sub %}
|
||||
from sqlalchemy.orm import relationship
|
||||
{% endif %}
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import CreatorMixin
|
||||
|
||||
|
||||
@@ -15,11 +15,13 @@ class {{ class_name }}QueryParam:
|
||||
{{ column.column_name }}: Optional[{{ column.python_type }}] = Query(None, description="{{ column.column_comment }}"),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for column in columns %}
|
||||
{% if column.column_name == 'status' %}
|
||||
status: Optional[bool] = Query(None, description="是否启用"),
|
||||
{% if column.column_name == 'EQ' %}
|
||||
{{ column.column_name }}: Optional[{{ column.python_type }}] = Query(None, description="{{ column.column_comment }}"),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
creator: Optional[int] = Query(None, description="创建人"),
|
||||
start_time: Optional[DateTimeStr] = Query(None, description="开始时间", example="2025-01-01 00:00:00"),
|
||||
end_time: Optional[DateTimeStr] = Query(None, description="结束时间", example="2025-12-31 23:59:59"),
|
||||
@@ -33,12 +35,12 @@ class {{ class_name }}QueryParam:
|
||||
{% endfor %}
|
||||
|
||||
# 精确查询字段
|
||||
self.creator_id = creator
|
||||
{% for column in columns %}
|
||||
{% if column.column_name == 'status' %}
|
||||
self.status = status
|
||||
{% if column.query_type == 'EQ' %}
|
||||
self.{{ column.column_name }} = {{ column.column_name }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
self.creator_id = creator
|
||||
|
||||
# 时间范围查询
|
||||
if start_time and end_time:
|
||||
|
||||
@@ -1,37 +1,11 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
{% set pkField = pk_column.python_field %}
|
||||
{% set pk_field = pk_column.python_field | camel_to_snake %}
|
||||
{% set pkParentheseIndex = pk_column.column_comment.find("(") %}
|
||||
{% set pk_field_comment = pk_column.column_comment[:pkParentheseIndex] if pkParentheseIndex != -1 else pk_column.column_comment %}
|
||||
{% set vo_field_required = namespace(has_required=False) %}
|
||||
{% set vo_field_daterange = namespace(has_daterange=False) %}
|
||||
{% for column in columns %}
|
||||
{% if column.required %}
|
||||
{% set vo_field_required.has_required = True %}
|
||||
{% endif %}
|
||||
{% if column.html_type == "datetime" and column.query_type == "BETWEEN" %}
|
||||
{% set vo_field_daterange.has_daterange = True %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% set sub_vo_field_required = namespace(has_required=False) %}
|
||||
{% if table.sub %}
|
||||
{% for sub_column in subTable.columns %}
|
||||
{% if sub_column.required %}
|
||||
{% set sub_vo_field_required.has_required = True %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for vo_import in vo_import_list %}
|
||||
{{ vo_import }}
|
||||
{% endfor %}
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
|
||||
{% if table.sub %}
|
||||
from typing import List, Optional
|
||||
{% else %}
|
||||
from typing import Optional
|
||||
{% endif %}
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.core.base_schema import BaseSchema
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ VALUES ('{{ function_name }}批量状态修改', 3, 7, {{ b_true }}, '{{ permiss
|
||||
INSERT INTO `system_menu` (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
VALUES ('{{ function_name }}下载导入模板', 3, 8, {{ b_true }}, '{{ permission_prefix }}:download', NULL, NULL, NULL, NULL, NULL, {{ b_false }}, {{ b_true }}, {{ b_false }}, '{{ function_name }}下载导入模板', NULL, {{ b_false }}, @parentId, '', now(), now());
|
||||
|
||||
{% elif db_type == 'postgresql' %}
|
||||
{% elif db_type == 'postgres' %}
|
||||
-- 父菜单 + 子按钮(PostgreSQL 使用 CTE 获取父ID)
|
||||
WITH parent AS (
|
||||
INSERT INTO public.system_menu (name, type, {{ order_col }}, status, permission, icon, route_name, route_path, component_path, redirect, hidden, keep_alive, always_show, title, params, affix, parent_id, description, created_at, updated_at)
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:query']" type="primary" icon="search" @click="handleQuery">查询</el-button>
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name|lower }}:query']" icon="refresh" @click="handleResetQuery">重置</el-button>
|
||||
<el-button v-hasPerm="['{{ module_name }}:{{ business_name }}:query']" icon="refresh" @click="handleResetQuery">重置</el-button>
|
||||
<template v-if="isExpandable">
|
||||
<el-link class="ml-3" type="primary" underline="never" @click="isExpand = !isExpand">
|
||||
{{ '{{' }} isExpand ? "收起" : "展开" {{ '}}' }}
|
||||
@@ -214,7 +214,7 @@
|
||||
{% set column_comment = column.column_comment if column.column_comment else '' %}
|
||||
{% set parentheseIndex = column_comment.find("(") %}
|
||||
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
|
||||
{% set required = 'true' if column.is_required == '1' else 'false' %}
|
||||
{% set required = 'true' if column.is_nullable == '1' else 'false' %}
|
||||
|
||||
{% if column.python_field == "status" %}
|
||||
<el-form-item label="状态" prop="status" :required="true">
|
||||
@@ -268,8 +268,8 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCloseDialog">取消</el-button>
|
||||
<el-button v-if="dialogVisible.type !== 'detail'" v-hasPerm="['{{ module_name }}:{{ business_name|lower }}:submit']" type="primary" @click="handleSubmit">确定</el-button>
|
||||
<el-button v-else v-hasPerm="['{{ module_name }}:{{ business_name|lower }}:detail']" type="primary" @click="handleCloseDialog">确定</el-button>
|
||||
<el-button v-if="dialogVisible.type !== 'detail'" v-hasPerm="['{{ module_name }}:{{ business_name }}:submit']" type="primary" @click="handleSubmit">确定</el-button>
|
||||
<el-button v-else v-hasPerm="['{{ module_name }}:{{ business_name }}:detail']" type="primary" @click="handleCloseDialog">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -292,6 +292,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "{{ class_name }}",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ResultEnum } from '@/enums/api/result.enum'
|
||||
@@ -358,11 +362,11 @@ const curdContentConfig = {
|
||||
exportsAction: async (params: any) => {
|
||||
const query: any = { ...params };
|
||||
if (typeof query.status === 'string') {
|
||||
query.status = query.status === 'true'
|
||||
query.status = query.status === 'true';
|
||||
}
|
||||
query.page_no = 1
|
||||
query.page_size = 9999
|
||||
const all: any[] = []
|
||||
query.page_no = 1;
|
||||
query.page_size = 9999;
|
||||
const all: any[] = [];
|
||||
while (true) {
|
||||
const res = await {{ class_name }}API.list(query)
|
||||
const items = res.data?.data?.items || []
|
||||
@@ -371,7 +375,7 @@ const curdContentConfig = {
|
||||
if (all.length >= total || items.length === 0) break
|
||||
query.page_no += 1
|
||||
}
|
||||
return all
|
||||
return all;
|
||||
},
|
||||
} as unknown as IContentConfig
|
||||
|
||||
@@ -392,11 +396,31 @@ const formData = reactive({
|
||||
{% endfor %}
|
||||
})
|
||||
|
||||
// 表单验证规则(必填项按 is_required 生成)
|
||||
// 定义初始表单数据常量
|
||||
const initialFormData = {
|
||||
id: undefined,
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == "1" or column.is_edit == "1" %}
|
||||
{{ column.python_field }}: {{ 'true' if column.python_field == 'status' else ('' if column.html_type == 'textarea' else 'undefined') }},
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
async function resetForm() {
|
||||
if (dataFormRef.value) {
|
||||
dataFormRef.value.resetFields();
|
||||
dataFormRef.value.clearValidate();
|
||||
}
|
||||
// 完全重置 formData 为初始状态
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
// 表单验证规则(必填项按 is_nullable 生成)
|
||||
const rules = reactive({
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == "1" or column.is_edit == "1" %}
|
||||
{% set required = 'true' if column.is_required == '1' else 'false' %}
|
||||
{% set required = 'true' if column.is_nullable == '1' else 'false' %}
|
||||
{{ column.python_field }}: [
|
||||
{ required: {{ required }}, message: '请输入{{ column.column_comment or column.python_field }}', trigger: 'blur' },
|
||||
],
|
||||
@@ -405,18 +429,18 @@ const rules = reactive({
|
||||
})
|
||||
|
||||
// 详情表单
|
||||
const detailFormData = ref<any>({})
|
||||
const detailFormData = ref<any>({});
|
||||
|
||||
// 统一日期范围
|
||||
const dateRange = ref<[Date, Date] | []>([])
|
||||
const dateRange = ref<[Date, Date] | []>([]);
|
||||
function handleDateRangeChange(range: [Date, Date]) {
|
||||
dateRange.value = range
|
||||
dateRange.value = range;
|
||||
if (range && range.length === 2) {
|
||||
queryFormData.start_time = formatToDateTime(range[0])
|
||||
queryFormData.end_time = formatToDateTime(range[1])
|
||||
queryFormData.start_time = formatToDateTime(range[0]);
|
||||
queryFormData.end_time = formatToDateTime(range[1]);
|
||||
} else {
|
||||
queryFormData.start_time = undefined
|
||||
queryFormData.end_time = undefined
|
||||
queryFormData.start_time = undefined;
|
||||
queryFormData.end_time = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,22 +460,22 @@ const queryFormData = reactive({
|
||||
|
||||
// 加载表格数据
|
||||
async function loadingData() {
|
||||
loading.value = true
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await {{ class_name }}API.list(queryFormData)
|
||||
pageTableData.value = response.data.data.items
|
||||
total.value = response.data.data.total
|
||||
const response = await {{ class_name }}API.list(queryFormData);
|
||||
pageTableData.value = response.data.data.items;
|
||||
total.value = response.data.data.total;
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.error(error);
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询(重置页码后获取数据)
|
||||
async function handleQuery() {
|
||||
queryFormData.page_no = 1
|
||||
loadingData()
|
||||
queryFormData.page_no = 1;
|
||||
loadingData();
|
||||
}
|
||||
|
||||
// 选择创建人后触发查询
|
||||
@@ -461,12 +485,12 @@ function handleConfirm() {
|
||||
|
||||
// 重置查询
|
||||
async function handleResetQuery() {
|
||||
queryFormRef.value.resetFields()
|
||||
queryFormData.page_no = 1
|
||||
dateRange.value = []
|
||||
queryFormData.start_time = undefined
|
||||
queryFormData.end_time = undefined
|
||||
loadingData()
|
||||
queryFormRef.value.resetFields();
|
||||
queryFormData.page_no = 1;
|
||||
dateRange.value = [];
|
||||
queryFormData.start_time = undefined;
|
||||
queryFormData.end_time = undefined;
|
||||
loadingData();
|
||||
}
|
||||
|
||||
// 行复选框选中项变化
|
||||
@@ -476,27 +500,28 @@ function handleSelectionChange(selection: any[]) {
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
function handleCloseDialog() {
|
||||
dialogVisible.visible = false
|
||||
async function handleCloseDialog() {
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: number) {
|
||||
dialogVisible.type = type
|
||||
if (id) {
|
||||
const response = await {{ class_name }}API.detail(id)
|
||||
const response = await {{ class_name }}API.detail(id);
|
||||
if (type === 'detail') {
|
||||
dialogVisible.title = '详情'
|
||||
Object.assign(detailFormData.value, response.data.data)
|
||||
dialogVisible.title = '详情';
|
||||
Object.assign(detailFormData.value, response.data.data);
|
||||
} else if (type === 'update') {
|
||||
dialogVisible.title = '修改'
|
||||
Object.assign(formData, response.data.data)
|
||||
dialogVisible.title = '修改';
|
||||
Object.assign(formData, response.data.data);
|
||||
}
|
||||
} else {
|
||||
dialogVisible.title = '新增{{ function_name }}'
|
||||
formData.id = undefined
|
||||
dialogVisible.title = '新增{{ function_name }}';
|
||||
formData.id = undefined;
|
||||
}
|
||||
dialogVisible.visible = true
|
||||
dialogVisible.visible = true;
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
@@ -507,12 +532,18 @@ async function handleSubmit() {
|
||||
try {
|
||||
const id = formData.id
|
||||
if (id) {
|
||||
await {{ class_name }}API.update(id, { id, ...formData })
|
||||
await {{ class_name }}API.update(id, { id, ...formData });
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
} else {
|
||||
await {{ class_name }}API.create(formData)
|
||||
await {{ class_name }}API.create(formData);
|
||||
dialogVisible.visible = false;
|
||||
resetForm();
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
}
|
||||
dialogVisible.visible = false
|
||||
handleResetQuery()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
@@ -531,8 +562,8 @@ async function handleDelete(ids: number[]) {
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
await {{ class_name }}API.delete(ids)
|
||||
loading.value = true;
|
||||
await {{ class_name }}API.delete(ids);
|
||||
handleResetQuery()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -555,7 +586,7 @@ async function handleMoreClick(status: boolean) {
|
||||
}).then(async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
await {{ class_name }}API.batchAvailable({ ids: selectIds.value, status })
|
||||
await {{ class_name }}API.batchAvailable({ ids: selectIds.value, status });
|
||||
handleResetQuery()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -585,7 +616,7 @@ function handleOpenExportsModal() {
|
||||
// 处理上传
|
||||
const handleUpload = async (formData: FormData) => {
|
||||
try {
|
||||
const response = await {{ class_name }}API.import(formData)
|
||||
const response = await {{ class_name }}API.import(formData);
|
||||
if (response.data.code === ResultEnum.SUCCESS) {
|
||||
ElMessage.success(`${response.data.msg},${response.data.data}`)
|
||||
importDialogVisible.value = false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "fastapi-vue3-admin",
|
||||
"name": "FastapiAdmin",
|
||||
"description": "Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
|
||||
+35
-142
@@ -4,8 +4,8 @@ const API_PATH = "/generator/gencode";
|
||||
|
||||
const GencodeAPI = {
|
||||
// 查询生成表数据
|
||||
listTable(query: GenTableQueryParam) {
|
||||
return request<ApiResponse<PageResult<GenTableOutVO[]>>>({
|
||||
listTable(query: GenTablePageQuery) {
|
||||
return request<ApiResponse<PageResult<GenTableSchema[]>>>({
|
||||
url: `${API_PATH}/list`,
|
||||
method: 'get',
|
||||
params: query
|
||||
@@ -13,8 +13,8 @@ const GencodeAPI = {
|
||||
},
|
||||
|
||||
// 查询db数据库列表
|
||||
listDbTable(query: GenTableQueryParam) {
|
||||
return request<ApiResponse<PageResult<DatabaseTable[]>>>({
|
||||
listDbTable(query: DBTablePageQuery) {
|
||||
return request<ApiResponse<PageResult<DBTableSchema[]>>>({
|
||||
url: `${API_PATH}/db/list`,
|
||||
method: 'get',
|
||||
params: query
|
||||
@@ -32,7 +32,7 @@ const GencodeAPI = {
|
||||
|
||||
// 查询表详细信息
|
||||
getGenTableDetail(table_id: number) {
|
||||
return request<ApiResponse<GenTableDetailResult>>({
|
||||
return request<ApiResponse<GenTableSchema>>({
|
||||
url: `${API_PATH}/detail/${table_id}`,
|
||||
method: 'get'
|
||||
})
|
||||
@@ -112,41 +112,32 @@ export interface GeneratorPreviewVO {
|
||||
content: string;
|
||||
}
|
||||
|
||||
/** 数据表分页查询参数 */
|
||||
export interface GenTableQueryParam {
|
||||
page_no: number;
|
||||
page_size: number;
|
||||
table_name?: string;
|
||||
table_comment?: string;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
export interface TablePageQuery extends PageQuery {
|
||||
/** 代码生成表分页查询参数 */
|
||||
export interface DBTablePageQuery extends PageQuery {
|
||||
/** 表名称 */
|
||||
table_name?: string;
|
||||
/** 表描述 */
|
||||
table_comment?: string;
|
||||
}
|
||||
|
||||
/** 数据表分页对象 */
|
||||
export interface TablePageVO {
|
||||
/** 表名称 */
|
||||
table_name: string;
|
||||
|
||||
/** 表描述 */
|
||||
table_comment: string;
|
||||
|
||||
/** 数据库名称 */
|
||||
database_name: string;
|
||||
|
||||
/** 表单类型 */
|
||||
table_type: string;
|
||||
|
||||
/**数据库表基本信息接口 */
|
||||
export interface DBTableSchema {
|
||||
database_name?: string;
|
||||
table_name?: string;
|
||||
table_comment?: string;
|
||||
table_type?: string;
|
||||
}
|
||||
|
||||
/** 代码生成表输出对象 */
|
||||
export interface GenTableOutVO {
|
||||
/** 代码生成表分页查询参数 */
|
||||
export interface GenTablePageQuery extends PageQuery {
|
||||
/** 表名称 */
|
||||
table_name?: string;
|
||||
/** 表描述 */
|
||||
table_comment?: string;
|
||||
}
|
||||
|
||||
/** 代码生成业务表模型 */
|
||||
export interface GenTableSchema {
|
||||
/** 主键 */
|
||||
id?: number;
|
||||
/** 表名称 */
|
||||
@@ -167,32 +158,16 @@ export interface GenTableOutVO {
|
||||
business_name?: string;
|
||||
/** 生成功能名 */
|
||||
function_name?: string;
|
||||
/** 生成代码方式(0zip压缩包 1自定义路径) */
|
||||
gen_type?: string;
|
||||
/** 其它生成选项 */
|
||||
options?: GenTableOptionModel;
|
||||
}
|
||||
|
||||
/** 表选项模型 */
|
||||
export interface GenTableOptionModel {
|
||||
/** 所属父级分类 */
|
||||
parent_menu_id?: number;
|
||||
}
|
||||
|
||||
/** 代码生成业务表模型 */
|
||||
export interface GenTableSchema extends GenTableOutVO {
|
||||
/** 表描述 */
|
||||
description?: string;
|
||||
/** 上级菜单ID字段 */
|
||||
parent_menu_id?: number;
|
||||
/** 上级菜单名称字段 */
|
||||
parent_menu_name?: string;
|
||||
/** 表列信息 */
|
||||
columns: GenTableColumnSchema[];
|
||||
/** 主键信息 */
|
||||
pk_column?: GenTableColumnOutSchema;
|
||||
pk_column?: GenTableColumnSchema;
|
||||
/** 子表信息 */
|
||||
sub_table?: GenTableSchema;
|
||||
/** 表列信息 */
|
||||
columns: GenTableColumnOutSchema[];
|
||||
/** 是否为子表 */
|
||||
sub?: boolean;
|
||||
}
|
||||
@@ -218,21 +193,21 @@ export interface GenTableColumnSchema {
|
||||
/** PYTHON字段名 */
|
||||
python_field?: string;
|
||||
/** 是否主键(1是) */
|
||||
is_pk?: string;
|
||||
is_pk?: boolean;
|
||||
/** 是否自增(1是) */
|
||||
is_increment?: string;
|
||||
is_increment?: boolean;
|
||||
/** 是否必填(1是) */
|
||||
is_required?: string;
|
||||
is_nullable?: boolean;
|
||||
/** 是否唯一(1是) */
|
||||
is_unique?: string;
|
||||
/** 是否为插入字段(1是) */
|
||||
is_insert?: string;
|
||||
is_unique?: boolean;
|
||||
/** 是否为新增字段(1是) */
|
||||
is_insert?: boolean;
|
||||
/** 是否编辑字段(1是) */
|
||||
is_edit?: string;
|
||||
is_edit?: boolean;
|
||||
/** 是否列表字段(1是) */
|
||||
is_list?: string;
|
||||
is_list?: boolean;
|
||||
/** 是否查询字段(1是) */
|
||||
is_query?: string;
|
||||
is_query?: boolean;
|
||||
/** 查询方式(等于、不等于、大于、小于、范围) */
|
||||
query_type?: string;
|
||||
/** 显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件) */
|
||||
@@ -245,85 +220,3 @@ export interface GenTableColumnSchema {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** 代码生成业务表列输出模型 */
|
||||
export interface GenTableColumnOutSchema extends GenTableColumnSchema {
|
||||
/** 字段大写形式 */
|
||||
cap_python_field?: string;
|
||||
/** 是否主键 */
|
||||
pk?: boolean;
|
||||
/** 是否自增 */
|
||||
increment?: boolean;
|
||||
/** 是否必填 */
|
||||
required?: boolean;
|
||||
/** 是否唯一 */
|
||||
unique?: boolean;
|
||||
/** 是否为插入字段 */
|
||||
insert?: boolean;
|
||||
/** 是否编辑字段 */
|
||||
edit?: boolean;
|
||||
/** 是否列表字段 */
|
||||
list?: boolean;
|
||||
/** 是否查询字段 */
|
||||
query?: boolean;
|
||||
/** 是否为基类字段 */
|
||||
super_column?: boolean;
|
||||
/** 是否为基类字段白名单 */
|
||||
usable_column?: boolean;
|
||||
}
|
||||
|
||||
/** 表详情查询结果 */
|
||||
export interface GenTableDetailResult {
|
||||
/** 表信息 */
|
||||
info: GenTableOutVO;
|
||||
/** 表列信息 */
|
||||
rows: GenTableColumnOutSchema[];
|
||||
/** 所有表信息 */
|
||||
tables: GenTableOutVO[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库表基本信息接口
|
||||
*/
|
||||
export interface DatabaseTable {
|
||||
database_name?: string;
|
||||
table_name?: string;
|
||||
table_comment?: string;
|
||||
table_type?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表列基本信息接口
|
||||
*/
|
||||
export interface TableColumn {
|
||||
column_name: string;
|
||||
column_comment: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表基本信息接口
|
||||
*/
|
||||
export interface TableInfo {
|
||||
table_name?: string;
|
||||
table_comment?: string;
|
||||
columns?: TableColumn[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入表查询表单数据接口
|
||||
*/
|
||||
export interface ImportTableQueryForm {
|
||||
page_no: number;
|
||||
page_size: number;
|
||||
table_name?: string;
|
||||
table_comment?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基本信息表单数据接口
|
||||
*/
|
||||
export interface BasicInfoFormData {
|
||||
table_name?: string;
|
||||
table_comment?: string;
|
||||
class_name?: string;
|
||||
description?: string;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import request from "@/utils/request";
|
||||
import { MenuTable, MenuForm } from "@/api/system/menu";
|
||||
import { MenuTable, MenuForm } from "@/api/module_system/menu";
|
||||
|
||||
const API_PATH = "/system/user";
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import NoticeAPI, { NoticeTable } from "@/api/system/notice";
|
||||
import NoticeAPI, { NoticeTable } from "@/api/module_system/notice";
|
||||
import router from "@/router";
|
||||
import { useNoticeStore } from "@/store";
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { UploadRawFile, UploadRequestOptions, ElMessage, type UploadUserFile } from "element-plus";
|
||||
import ParamsAPI from '@/api/system/params';
|
||||
import ParamsAPI from '@/api/module_system/params';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
|
||||
@@ -111,7 +111,7 @@ import Notification from "@/components/Notification/index.vue";
|
||||
import LockDialog from './LockDialog.vue'
|
||||
import LockPage from './LockPage.vue'
|
||||
import Guide from '@/components/Guide/index.vue'
|
||||
import ConfigInfoDrawer from "@/views/system/param/components/ConfigInfoDrawer.vue"
|
||||
import ConfigInfoDrawer from "@/views/module_system/param/components/ConfigInfoDrawer.vue"
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -20,7 +20,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
meta: { hidden: true },
|
||||
component: () => import("@/views/system/auth/index.vue"),
|
||||
component: () => import("@/views/module_system/auth/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "/401",
|
||||
@@ -76,7 +76,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: "internal-app/:appId",
|
||||
name: "InternalApp",
|
||||
meta: { title: "内部应用", icon: "Monitor", hidden: true, keepAlive: false },
|
||||
component: () => import("@/views/application/myapp/components/InternalApp.vue"),
|
||||
component: () => import("@/views/module_application/myapp/components/InternalApp.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { store } from "@/store";
|
||||
import ParamsAPI, { ConfigTable } from "@/api/system/params";
|
||||
import ParamsAPI, { ConfigTable } from "@/api/module_system/params";
|
||||
|
||||
interface ConfigState {
|
||||
// 网站信息
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { store } from "@/store";
|
||||
import DictAPI, { DictDataTable } from "@/api/system/dict";
|
||||
import DictAPI, { DictDataTable } from "@/api/module_system/dict";
|
||||
|
||||
export const useDictStore = defineStore("dict", {
|
||||
state: () => ({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { store } from "@/store";
|
||||
import NoticeAPI, { NoticeTable } from "@/api/system/notice";
|
||||
import NoticeAPI, { NoticeTable } from "@/api/module_system/notice";
|
||||
|
||||
export const useNoticeStore = defineStore("notice", {
|
||||
state: () => ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import router, { constantRoutes } from "@/router";
|
||||
import { store, useUserStore } from "@/store";
|
||||
import { MenuTable } from "@/api/system/menu";
|
||||
import { MenuTable } from "@/api/module_system/menu";
|
||||
|
||||
const modules = import.meta.glob("../../views/**/**.vue");
|
||||
const Layout = () => import("@/layouts/index.vue");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { store, useTagsViewStore, usePermissionStoreHook, useDictStoreHook } from "@/store";
|
||||
|
||||
import AuthAPI, {type LoginFormData } from "@/api/system/auth";
|
||||
import UserAPI, {type UserInfo } from "@/api/system/user";
|
||||
import type { MenuTable } from "@/api/system/menu";
|
||||
import AuthAPI, {type LoginFormData } from "@/api/module_system/auth";
|
||||
import UserAPI, {type UserInfo } from "@/api/module_system/user";
|
||||
import type { MenuTable } from "@/api/module_system/menu";
|
||||
import { Auth } from "@/utils/auth";
|
||||
|
||||
export const useUserStore = defineStore("user", {
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance, UploadRequestOptions, UploadFile, ElUpload, ComponentSize } from 'element-plus'
|
||||
import UserAPI, { type InfoFormState, type PasswordFormState } from '@/api/system/user';
|
||||
import UserAPI, { type InfoFormState, type PasswordFormState } from '@/api/module_system/user';
|
||||
import { useUserStore, useDictStore } from "@/store";
|
||||
import { useUserStoreHook } from "@/store/modules/user.store";
|
||||
import { Camera } from '@element-plus/icons-vue';
|
||||
|
||||
@@ -186,7 +186,7 @@ defineOptions({
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { useUserStore } from "@/store/index";
|
||||
import { greetings } from '@/utils/common';
|
||||
import NoticeAPI, { NoticeTable } from '@/api/system/notice';
|
||||
import NoticeAPI, { NoticeTable } from '@/api/module_system/notice';
|
||||
import { ref, onMounted, reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
+1
-1
@@ -172,7 +172,7 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
import JobAPI, { JobLogPageQuery, JobLogTable } from "@/api/application/job";
|
||||
import JobAPI, { JobLogPageQuery, JobLogTable } from "@/api/module_application/job";
|
||||
import { useAppStore } from "@/store/modules/app.store";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
+2
-5
@@ -594,9 +594,6 @@
|
||||
:selection-data="selectionRows"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -605,12 +602,12 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import JobAPI, { JobTable, JobForm, JobPageQuery } from "@/api/application/job";
|
||||
import JobAPI, { JobTable, JobForm, JobPageQuery } from "@/api/module_application/job";
|
||||
import IntervalTab from "@/components/IntervalTab/index.vue";
|
||||
import { useDictStore } from "@/store/index";
|
||||
import { vue3CronPlus } from "vue3-cron-plus";
|
||||
import "vue3-cron-plus/dist/index.css"; // 引入样式
|
||||
import JobLogDrawer from "@/views/application/job/components/JobLogDrawer.vue"
|
||||
import JobLogDrawer from "@/views/module_application/job/components/JobLogDrawer.vue"
|
||||
import OperationColumn from "@/components/OperationColumn/index.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
+1
-1
@@ -232,7 +232,7 @@ import { useTagsViewStore } from "@/store";
|
||||
import { useRouter } from "vue-router";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
import { Monitor, User, Clock } from '@element-plus/icons-vue';
|
||||
import ApplicationAPI, { type ApplicationForm, type ApplicationInfo, type ApplicationPageQuery } from "@/api/application/myapp";
|
||||
import ApplicationAPI, { type ApplicationForm, type ApplicationInfo, type ApplicationPageQuery } from "@/api/module_application/myapp";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
|
||||
const appStore = useAppStore();
|
||||
+1
-1
@@ -203,7 +203,7 @@ defineOptions({
|
||||
});
|
||||
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import ExampleAPI, { ExampleTable, ExampleForm, ExamplePageQuery } from "@/api/generator/demo";
|
||||
import ExampleAPI, { ExampleTable, ExampleForm, ExamplePageQuery } from "@/api/module_generator/demo";
|
||||
import DatePicker from "@/components/DatePicker/index.vue";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
|
||||
+194
-173
@@ -43,7 +43,7 @@
|
||||
<el-button v-hasPerm="['generator:gencode:delete']" type="danger" plain icon="Delete" :disabled="ids.length === 0" @click="handleDelete()">批量删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPerm="['generator:gencode:code']" type="warning" plain icon="Download" :disabled="!canGenerate" @click="handleGenTable()">批量生成</el-button>
|
||||
<el-button v-hasPerm="['generator:gencode:code']" type="warning" plain icon="Download" :disabled="!canGenerate" @click="handleGenTable('0')">批量生成</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@@ -116,13 +116,33 @@
|
||||
</el-card>
|
||||
|
||||
<!-- 创建表 -->
|
||||
<el-dialog v-model="createTableVisible" title="创建表" append-to-body>
|
||||
<el-dialog v-model="createTableVisible" title="创建表" append-to-body >
|
||||
<span>创建表语句(支持多个建表sql语句):</span>
|
||||
<el-input v-model="createContent" type="textarea" :rows="10" placeholder="请输入创建表sql语句" clearable :rules="[{ required: true, message: '请输入创建表sql语句', trigger: 'blur' }]"></el-input>
|
||||
<el-button type="warning" size="small" class="ml-1 mb-1" @click="loadExampleMysql">加载MySQL示例</el-button>
|
||||
<el-button type="primary" size="small" class="ml-1 mb-1" @click="loadExamplePostgres">加载Postgres示例</el-button>
|
||||
<el-scrollbar max-height="72vh">
|
||||
<div class="absolute z-36 right-5 top-2">
|
||||
<el-link type="primary" @click="handleCopyCode">
|
||||
<el-icon>
|
||||
<CopyDocument />
|
||||
</el-icon>
|
||||
复制代码
|
||||
</el-link>
|
||||
</div>
|
||||
|
||||
<Codemirror
|
||||
ref="sqlRef"
|
||||
v-model:value="createContent"
|
||||
:options="sqlOptions"
|
||||
border
|
||||
:height="'300px'"
|
||||
width="100%"
|
||||
/>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" :loading="loading" @click="handleCreateTable(createContent)">确 定</el-button>
|
||||
<el-button @click="createTableVisible = false">取 消</el-button>
|
||||
<el-button @click="handleCreateTableCancel()">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -154,12 +174,12 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row>
|
||||
<el-table ref="table" :data="dbTableList" height="300px" @row-click="clickRow" @selection-change="handleImportTableSelectionChange">
|
||||
<el-table ref="table" :data="dbTableList" height="300px" border @row-click="clickRow" @selection-change="handleImportTableSelectionChange">
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="序号" type="index" min-width="55" align="center" fixed>
|
||||
<el-table-column label="序号" type="index" min-width="30" align="center" fixed>
|
||||
<template #default="scope">
|
||||
<span>{{(importQueryFormData.page_no - 1) * importQueryFormData.page_size + scope.$index + 1}}</span>
|
||||
</template>
|
||||
@@ -185,12 +205,11 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 代码生成抽屉 -->
|
||||
<el-drawer v-model="editVisible" :title="'【代码生成】' + info.table_name" size="80%" @close="handleClose">
|
||||
<el-drawer v-model="editVisible" :title="'【代码生成】' + info.table_name" size="85%" @close="handleClose">
|
||||
<el-steps :active="activeStep" finish-status="success" simple>
|
||||
<el-step title="基础配置" />
|
||||
<el-step title="字段配置" />
|
||||
<el-step title="预览代码" />
|
||||
<el-step title="代码生成" />
|
||||
</el-steps>
|
||||
|
||||
<div class="mt-5">
|
||||
@@ -212,6 +231,68 @@
|
||||
<el-input v-model="info.class_name" placeholder="请输入"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="package_name">
|
||||
<template #label>
|
||||
包名
|
||||
<el-tooltip content="生成在哪个python模块下,例如 module_gencode" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.package_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="module_name">
|
||||
<template #label>
|
||||
模块名
|
||||
<el-tooltip content="可理解为子系统名,例如 system" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.module_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="business_name">
|
||||
<template #label>
|
||||
业务名
|
||||
<el-tooltip content="可理解为功能英文名,例如 user" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.business_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="function_name">
|
||||
<template #label>
|
||||
功能名
|
||||
<el-tooltip content="用作类描述,例如 用户" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.function_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
上级菜单
|
||||
<el-tooltip content="分配到指定菜单下,例如 系统管理" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-tree-select
|
||||
v-model="info.parent_menu_id"
|
||||
:data="menuOptions"
|
||||
placeholder="请选择系统菜单"
|
||||
check-strictly
|
||||
filterable
|
||||
:render-after-expand="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="description">
|
||||
<el-input v-model="info.description" type="textarea" :rows="3"></el-input>
|
||||
@@ -266,7 +347,7 @@
|
||||
<el-table
|
||||
ref="dragTable"
|
||||
v-loading="loading"
|
||||
:data="columns"
|
||||
:data="info.columns"
|
||||
row-key="id"
|
||||
max-height="680"
|
||||
highlight--currentrow
|
||||
@@ -277,7 +358,7 @@
|
||||
<template #empty>
|
||||
<el-empty :image-size="80" description="暂无数据" />
|
||||
</template>
|
||||
<el-table-column label="序号" type="index" min-width="5%" fixed />
|
||||
<el-table-column label="序号" type="index" min-width="7%" fixed />
|
||||
<el-table-column label="列名" prop="column_name" min-width="10%" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="类型" prop="column_type" min-width="10%" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="长度" prop="column_length" min-width="8%" :show-overflow-tooltip="true">
|
||||
@@ -355,9 +436,9 @@
|
||||
<el-checkbox v-model="scope.row.is_increment" true-value="1" false-value="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="必填" min-width="10%">
|
||||
<el-table-column label="允许空" min-width="10%">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.is_required" true-value="1" false-value="0"></el-checkbox>
|
||||
<el-checkbox v-model="scope.row.is_nullable" true-value="0" false-value="1"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="唯一" min-width="10%">
|
||||
@@ -365,6 +446,11 @@
|
||||
<el-checkbox v-model="scope.row.is_unique" true-value="1" false-value="0"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="主键" min-width="10%">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.is_pk" true-value="1" false-value="0"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="表单类型" min-width="12%">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.html_type">
|
||||
@@ -454,120 +540,16 @@
|
||||
</el-scrollbar>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第四步:代码生成 -->
|
||||
<el-form v-show="activeStep == 3" ref="genInfo" :model="info" :rules="rules" label-width="150px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="package_name">
|
||||
<template #label>
|
||||
生成包路径
|
||||
<el-tooltip content="生成在哪个python模块下,例如 module_gencode" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.package_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="module_name">
|
||||
<template #label>
|
||||
生成模块名
|
||||
<el-tooltip content="可理解为子系统名,例如 system" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.module_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="business_name">
|
||||
<template #label>
|
||||
生成业务名
|
||||
<el-tooltip content="可理解为功能英文名,例如 user" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.business_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="function_name">
|
||||
<template #label>
|
||||
生成功能名
|
||||
<el-tooltip content="用作类描述,例如 用户" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.function_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="gen_type">
|
||||
<template #label>
|
||||
生成代码方式
|
||||
<el-tooltip content="默认为zip压缩包下载,也可以自定义生成路径" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-radio v-model="info.gen_type" value="0">zip压缩包</el-radio>
|
||||
<el-radio v-model="info.gen_type" value="1">项目目录</el-radio>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
上级菜单
|
||||
<el-tooltip content="分配到指定菜单下,例如 系统管理" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-tree-select
|
||||
v-model="info.parent_menu_id"
|
||||
:data="menuOptions"
|
||||
placeholder="请选择系统菜单"
|
||||
check-strictly
|
||||
filterable
|
||||
:render-after-expand="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<!-- 公共按钮:关闭 -->
|
||||
<el-button :icon="Close" @click="close">关闭</el-button>
|
||||
|
||||
<!-- 第一步:只有“下一步” -->
|
||||
<el-button v-if="activeStep === 0" type="primary" @click="nextStep">
|
||||
下一步,字段配置<el-icon class="el-icon--right"><Right /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<!-- 第二步:有“上一步”和“下一步” -->
|
||||
<el-button v-if="activeStep === 1" type="success" :icon="Back" @click="prevStep">上一步,基础配置</el-button>
|
||||
<el-button v-if="activeStep === 1" v-hasPerm="['generator:gencode:update']" type="warning" :icon="Edit" @click="submitForm">保存字段配置</el-button>
|
||||
<el-button v-if="activeStep === 1" type="primary" @click="nextStep">
|
||||
下一步,预览代码<el-icon class="el-icon--right"><View /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<!-- 第三步:有“上一步”和“下载代码” -->
|
||||
<el-button v-if="activeStep === 2" type="success" :icon="Back" @click="prevStep">上一步,字段配置</el-button>
|
||||
<el-button v-if="activeStep === 2" type="primary" @click="nextStep">
|
||||
下一步,生成代码<el-icon class="el-icon--right"><Right /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<!-- 第四步:有“上一步”和“写入本地” -->
|
||||
<el-button v-if="activeStep === 3" type="success" :icon="View" @click="prevStep">上一步,预览代码</el-button>
|
||||
<el-button v-if="activeStep === 3" :disabled="info.gen_type != '0'" type="warning" :icon="Download" :loading="loading" @click="handleGenTable(info)">下载代码</el-button>
|
||||
<el-button v-if="activeStep === 3" :disabled="info.gen_type != '1'" type="primary" :icon="FolderOpened" :loading="loading" @click="handleGenTable(info)">写入本地</el-button>
|
||||
<el-button v-if="activeStep != 0" type="success" :icon="Back" @click="prevStep">上一步</el-button>
|
||||
<el-button v-if="activeStep != 2" v-hasPerm="['generator:gencode:update']" type="warning" :icon="Edit" :loading="loading" @click="submitForm">保存配置</el-button>
|
||||
<el-button v-if="activeStep != 2" type="primary" @click="nextStep">下一步<el-icon class="el-icon--right"><Right /></el-icon></el-button>
|
||||
<el-button v-if="activeStep === 2" type="warning" :icon="Download" :loading="loading" @click="handleGenTable('0', info)">下载代码</el-button>
|
||||
<el-button v-if="activeStep === 2" type="primary" :icon="FolderOpened" :loading="loading" @click="handleGenTable('1', info)">写入本地</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
@@ -580,6 +562,7 @@ defineOptions({
|
||||
});
|
||||
|
||||
import "codemirror/mode/javascript/javascript.js";
|
||||
import "codemirror/mode/sql/sql.js";
|
||||
import { ref, reactive, computed, onActivated, onMounted } from 'vue';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { useRoute } from 'vue-router';
|
||||
@@ -587,10 +570,10 @@ import Codemirror from "codemirror-editor-vue3";
|
||||
import type { EditorConfiguration } from "codemirror";
|
||||
import type { CmComponentRef } from "codemirror-editor-vue3";
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type TableInstance } from 'element-plus';
|
||||
import { QuestionFilled, MagicStick, View, CopyDocument, Close, Right, FolderOpened, Back, Download, Edit } from '@element-plus/icons-vue';
|
||||
import GencodeAPI, { type GenTableOutVO, type DatabaseTable, type GenTableQueryParam, type GenTableColumnOutSchema, type GenTableSchema } from "@/api/generator/gencode";
|
||||
import MenuAPI, { MenuTable } from "@/api/system/menu";
|
||||
import DictAPI, { DictTable } from "@/api/system/dict";
|
||||
import { QuestionFilled, MagicStick, CopyDocument, Close, Right, FolderOpened, Back, Download, Edit } from '@element-plus/icons-vue';
|
||||
import GencodeAPI, { type GenTableSchema, type DBTableSchema, type GenTablePageQuery } from "@/api/module_generator/gencode";
|
||||
import MenuAPI, { MenuTable } from "@/api/module_system/menu";
|
||||
import DictAPI, { DictTable } from "@/api/module_system/dict";
|
||||
import { formatTree } from "@/utils/common";
|
||||
import { MenuTypeEnum } from "@/enums";
|
||||
|
||||
@@ -621,8 +604,8 @@ interface FileData {
|
||||
const queryRef = ref<FormInstance>();
|
||||
const table = ref<TableInstance>();
|
||||
const cmRef = ref<CmComponentRef>();
|
||||
const sqlRef = ref<CmComponentRef>();
|
||||
const basicInfo = ref<FormInstance>();
|
||||
const genInfo = ref<FormInstance>();
|
||||
const dragTable = ref<TableInstance>();
|
||||
|
||||
// 状态管理
|
||||
@@ -639,15 +622,15 @@ const importVisible = ref(false);
|
||||
// 表单和列表数据
|
||||
const createContent = ref("");
|
||||
const dateRange = ref<[Date, Date] | []>([]);
|
||||
const tableList = ref<GenTableOutVO[]>([]);
|
||||
const dbTableList = ref<DatabaseTable[]>([]);
|
||||
const tableList = ref<GenTableSchema[]>([]);
|
||||
const dbTableList = ref<DBTableSchema[]>([]);
|
||||
const ids = ref<number[]>([]);
|
||||
const tableNames = ref<string[]>([]);
|
||||
|
||||
// 导入弹窗专用状态
|
||||
const importLoading = ref(false);
|
||||
const importTotal = ref<number>(0);
|
||||
const importQueryFormData = reactive<GenTableQueryParam>({
|
||||
const importQueryFormData = reactive<GenTablePageQuery>({
|
||||
page_no: 1,
|
||||
page_size: 10,
|
||||
table_name: undefined,
|
||||
@@ -664,7 +647,6 @@ type TableItem = {
|
||||
table_comment: string;
|
||||
};
|
||||
const tables = ref<TableItem[]>([]);
|
||||
const columns = ref<GenTableColumnOutSchema[]>([]);
|
||||
|
||||
// 导入按钮点击事件
|
||||
async function handleImportClick() {
|
||||
@@ -687,13 +669,11 @@ const code = ref<string>('');
|
||||
const treeData = ref<TreeNode[]>([]);
|
||||
|
||||
// 分页查询参数
|
||||
const queryFormData = reactive<GenTableQueryParam>({
|
||||
const queryFormData = reactive<GenTablePageQuery>({
|
||||
page_no: 1,
|
||||
page_size: 10,
|
||||
table_name: undefined,
|
||||
table_comment: undefined,
|
||||
start_time: undefined,
|
||||
end_time: undefined,
|
||||
});
|
||||
|
||||
// 表格列配置
|
||||
@@ -718,6 +698,17 @@ const cmOptions: EditorConfiguration = {
|
||||
readOnly: true
|
||||
};
|
||||
|
||||
const sqlOptions: EditorConfiguration = {
|
||||
mode: "text/x-sql",
|
||||
lineNumbers: true,
|
||||
smartIndent: true,
|
||||
indentUnit: 2,
|
||||
tabSize: 2,
|
||||
readOnly: false,
|
||||
theme: "default",
|
||||
lineWrapping: true,
|
||||
};
|
||||
|
||||
|
||||
// 工具函数
|
||||
const { copy } = useClipboard();
|
||||
@@ -831,7 +822,7 @@ function buildTree(data: FileData[]): TreeNode {
|
||||
}
|
||||
|
||||
/** 获取生成预览 */
|
||||
async function handlePreview(row: GenTableOutVO): Promise<void> {
|
||||
async function handlePreview(row: GenTableSchema): Promise<void> {
|
||||
try {
|
||||
if (!row.id) {
|
||||
ElMessage.warning('无效的表ID');
|
||||
@@ -906,14 +897,12 @@ async function loadingData(): Promise<void> {
|
||||
}
|
||||
|
||||
/** 表格行内生成代码操作 */
|
||||
async function handleGenTable(row?: GenTableOutVO): Promise<void> {
|
||||
async function handleGenTable(targetGenType: string, row?: GenTableSchema): Promise<void> {
|
||||
let tbNames: string | string[] = [];
|
||||
let targetGenType = '0';
|
||||
|
||||
// 判断是单条还是批量操作
|
||||
if (row) {
|
||||
tbNames = [row.table_name || ''];
|
||||
targetGenType = row.gen_type || '0';
|
||||
} else if (tableNames.value.length > 0) {
|
||||
tbNames = tableNames.value;
|
||||
} else {
|
||||
@@ -954,7 +943,7 @@ async function handleGenTable(row?: GenTableOutVO): Promise<void> {
|
||||
}
|
||||
|
||||
/** 同步数据库操作 */
|
||||
async function handleSynchDb(row: GenTableOutVO): Promise<void> {
|
||||
async function handleSynchDb(row: GenTableSchema): Promise<void> {
|
||||
const tableName = row.table_name || '';
|
||||
|
||||
if (!tableName) {
|
||||
@@ -997,27 +986,19 @@ async function handleRefresh() {
|
||||
}
|
||||
|
||||
/** 多选框选中数据 - 主表格 */
|
||||
function handleTableSelectionChange(selection: GenTableOutVO[]): void {
|
||||
function handleTableSelectionChange(selection: GenTableSchema[]): void {
|
||||
ids.value = selection.map((item) => item.id!);
|
||||
tableNames.value = selection.map((item) => item.table_name || '').filter(Boolean);
|
||||
}
|
||||
|
||||
/** 多选框选中数据 - 导入表格 */
|
||||
function handleImportTableSelectionChange(selection: DatabaseTable[]): void {
|
||||
function handleImportTableSelectionChange(selection: DBTableSchema[]): void {
|
||||
tables.value = selection.map(item => ({
|
||||
table_name: item.table_name || '',
|
||||
table_comment: item.table_comment || ''
|
||||
}));
|
||||
}
|
||||
|
||||
/** 表格行内修改按钮操作 */
|
||||
/** 计算表格高度 */
|
||||
function calculateTableHeight() {
|
||||
// 为了确保表格有足够的高度显示,我们设置一个固定的合理值
|
||||
// 这里使用400px作为表格高度,这是一个适合大多数屏幕的高度
|
||||
|
||||
}
|
||||
|
||||
// 修改菜单选项过滤逻辑,添加递归过滤函数
|
||||
const filterMenuTypes = (nodes: MenuTable[]) => {
|
||||
return nodes
|
||||
@@ -1029,7 +1010,7 @@ const filterMenuTypes = (nodes: MenuTable[]) => {
|
||||
};
|
||||
|
||||
/** 表格行内修改按钮操作 */
|
||||
async function handlePreviewTable(row?: GenTableOutVO): Promise<void> {
|
||||
async function handlePreviewTable(row?: GenTableSchema): Promise<void> {
|
||||
const selectedTableId = row?.id || ids.value[0];
|
||||
if (selectedTableId) {
|
||||
// 设置编辑的表ID和名称
|
||||
@@ -1043,17 +1024,13 @@ async function handlePreviewTable(row?: GenTableOutVO): Promise<void> {
|
||||
|
||||
const dict_response = await DictAPI.getDictTypeList({page_no: 1, page_size: 100});
|
||||
dictOptions.value = dict_response.data.data.items;
|
||||
// 延迟计算表格高度,确保DOM已经渲染完成
|
||||
setTimeout(() => {
|
||||
calculateTableHeight();
|
||||
}, 100);
|
||||
} else {
|
||||
ElMessage.error('请选择要修改的数据');
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
async function handleDelete(row?: GenTableOutVO): Promise<void> {
|
||||
async function handleDelete(row?: GenTableSchema): Promise<void> {
|
||||
const tableIds = row?.id ? [row.id] : ids.value;
|
||||
|
||||
if (tableIds.length === 0) {
|
||||
@@ -1084,6 +1061,50 @@ async function handleDelete(row?: GenTableOutVO): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载SQL示例 */
|
||||
function loadExampleMysql(): void {
|
||||
const exampleSql = `-- MySQL SQL案例
|
||||
CREATE TABLE \`gen_demo01\` (
|
||||
\`name\` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
|
||||
\`status\` tinyint(1) NOT NULL COMMENT '是否启用(True:启用 False:禁用)',
|
||||
\`creator_id\` int DEFAULT NULL COMMENT '创建人ID',
|
||||
\`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
\`description\` text COLLATE utf8mb4_unicode_ci COMMENT '备注/描述',
|
||||
\`created_at\` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
\`updated_at\` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (\`id\`),
|
||||
KEY \`ix_gen_demo01_creator_id\` (\`creator_id\`),
|
||||
CONSTRAINT \`gen_demo01_ibfk_1\` FOREIGN KEY (\`creator_id\`) REFERENCES \`system_users\` (\`id\`) ON DELETE SET NULL ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='示例表';`;
|
||||
createContent.value = exampleSql;
|
||||
}
|
||||
|
||||
function loadExamplePostgres(): void {
|
||||
const exampleSql = `-- Psstgres SQL案例
|
||||
CREATE TABLE gen_demo01(
|
||||
name varchar(64),
|
||||
status boolean NOT NULL,
|
||||
creator_id integer,
|
||||
id SERIAL NOT NULL,
|
||||
description text,
|
||||
created_at timestamp without time zone,
|
||||
updated_at timestamp without time zone,
|
||||
PRIMARY KEY(id),
|
||||
CONSTRAINT gen_demo01_creator_id_fkey FOREIGN key(creator_id) REFERENCES system_users(id)
|
||||
);
|
||||
CREATE INDEX ix_gen_demo01_creator_id ON public.gen_demo01 USING btree (creator_id);
|
||||
COMMENT ON TABLE gen_demo01 IS '示例表';
|
||||
COMMENT ON COLUMN gen_demo01.name IS '名称';
|
||||
COMMENT ON COLUMN gen_demo01.status IS '是否启用(True:启用 False:禁用)';
|
||||
COMMENT ON COLUMN gen_demo01.creator_id IS '创建人ID';
|
||||
COMMENT ON COLUMN gen_demo01.id IS '主键ID';
|
||||
COMMENT ON COLUMN gen_demo01.description IS '备注/描述';
|
||||
COMMENT ON COLUMN gen_demo01.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN gen_demo01.updated_at IS '更新时间';`;
|
||||
|
||||
createContent.value = exampleSql;
|
||||
}
|
||||
|
||||
/** 创建表操作 */
|
||||
async function handleCreateTable(sql: string): Promise<void> {
|
||||
if (!sql || sql.trim() === '') {
|
||||
@@ -1104,6 +1125,14 @@ async function handleCreateTable(sql: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/** 取消创建表操作 */
|
||||
function handleCreateTableCancel(): void {
|
||||
createTableVisible.value = false;
|
||||
createContent.value = '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 导入表操作 */
|
||||
async function handleImportTable(): Promise<void> {
|
||||
if (tables.value.length === 0) {
|
||||
@@ -1126,7 +1155,7 @@ async function handleImportTable(): Promise<void> {
|
||||
}
|
||||
|
||||
/** 单击选择行 */
|
||||
function clickRow(row: DatabaseTable): void {
|
||||
function clickRow(row: DBTableSchema): void {
|
||||
table.value?.toggleRowSelection(row);
|
||||
}
|
||||
|
||||
@@ -1195,11 +1224,8 @@ const info = reactive<GenTableSchema>({
|
||||
module_name: '',
|
||||
business_name: '',
|
||||
function_name: '',
|
||||
gen_type: '0',
|
||||
options: {parent_menu_id: undefined,},
|
||||
description: '',
|
||||
parent_menu_id: undefined,
|
||||
parent_menu_name: '',
|
||||
pk_column: undefined,
|
||||
sub_table: undefined,
|
||||
columns: [],
|
||||
@@ -1263,7 +1289,7 @@ async function nextStep(): Promise<void> {
|
||||
|
||||
// 当从字段配置进入预览步骤时,自动加载预览数据
|
||||
if (activeStep.value === 2 && info.id) {
|
||||
await handlePreview({ id: info.id, table_name: info.table_name } as GenTableOutVO);
|
||||
await handlePreview({ id: info.id, table_name: info.table_name } as GenTableSchema);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1297,7 +1323,6 @@ function close(): void {
|
||||
// 清除表单验证状态
|
||||
setTimeout(() => {
|
||||
basicInfo.value?.resetFields();
|
||||
genInfo.value?.resetFields();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
@@ -1316,21 +1341,17 @@ async function loadTableDetail(id: number | string) {
|
||||
const data = response.data.data;
|
||||
|
||||
// 填充表单数据
|
||||
Object.assign(info, { ...data.info });
|
||||
Object.assign(info, { ...data });
|
||||
|
||||
// 处理列数据
|
||||
if (data.rows) {
|
||||
// 深拷贝确保数据独立性
|
||||
info.columns = JSON.parse(JSON.stringify(data.rows));
|
||||
// 设置列的选中状态
|
||||
if (info.columns) {
|
||||
info.columns.forEach((item: any) => {
|
||||
item.select = true;
|
||||
});
|
||||
// 创建新数组避免引用问题
|
||||
columns.value = [...info.columns];
|
||||
}
|
||||
}
|
||||
if (data && data.columns && Array.isArray(data.columns)) {
|
||||
// 深拷贝确保数据独立性
|
||||
info.columns = JSON.parse(JSON.stringify(data.columns));
|
||||
// 设置列的选中状态
|
||||
info.columns.forEach((item: any) => {
|
||||
item.select = true;
|
||||
});
|
||||
}
|
||||
|
||||
// 重置当前步骤为第一步
|
||||
activeStep.value = 0;
|
||||
+1
-1
@@ -266,7 +266,7 @@ defineOptions({
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { ResultEnum } from "@/enums/api/result.enum";
|
||||
import ExampleAPI, { ExampleTable, ExampleForm, ExamplePageQuery } from "@/api/generator/demo";
|
||||
import ExampleAPI, { ExampleTable, ExampleForm, ExamplePageQuery } from "@/api/module_generator/demo";
|
||||
import ImportModal from "@/components/CURD/ImportModal.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import DatePicker from "@/components/DatePicker/index.vue";
|
||||
Vendored
+1
-1
@@ -235,7 +235,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import CacheAPI, { type CacheInfo, type CacheForm, type CacheMonitor, type RedisInfo } from "@/api/monitor/cache";
|
||||
import CacheAPI, { type CacheInfo, type CacheForm, type CacheMonitor, type RedisInfo } from "@/api/module_monitor/cache";
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 响应式状态定义
|
||||
+1
-1
@@ -111,7 +111,7 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import OnlineAPI, { type OnlineUserPageQuery, type OnlineUserTable } from "@/api/monitor/online";
|
||||
import OnlineAPI, { type OnlineUserPageQuery, type OnlineUserTable } from "@/api/module_monitor/online";
|
||||
|
||||
const queryFormRef = ref();
|
||||
const total = ref(0);
|
||||
+1
-1
@@ -287,7 +287,7 @@ import {
|
||||
UploadFilled,
|
||||
QuestionFilled
|
||||
} from '@element-plus/icons-vue'
|
||||
import { ResourceAPI, type ResourceItem, type ResourcePageQuery } from '@/api/monitor/resource'
|
||||
import { ResourceAPI, type ResourceItem, type ResourcePageQuery } from '@/api/module_monitor/resource'
|
||||
|
||||
// 分页表单
|
||||
const fileList = ref<ResourceItem[]>([])
|
||||
+1
-1
@@ -231,7 +231,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ServerAPI, {type ServerInfo } from '@/api/monitor/server'
|
||||
import ServerAPI, {type ServerInfo } from '@/api/module_monitor/server'
|
||||
|
||||
|
||||
const loading = ref(false);
|
||||
+1
-1
@@ -104,7 +104,7 @@ import type { FormInstance } from "element-plus";
|
||||
import { LocationQuery, RouteLocationRaw, useRoute, useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { onActivated, onMounted, watch } from "vue";
|
||||
import AuthAPI, {type LoginFormData, type CaptchaInfo } from "@/api/system/auth";
|
||||
import AuthAPI, {type LoginFormData, type CaptchaInfo } from "@/api/module_system/auth";
|
||||
import { useAppStore, useUserStore, useSettingsStore } from "@/store";
|
||||
import CommonWrapper from "@/components/CommonWrapper/index.vue";
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from "element-plus";
|
||||
import { Lock } from "@element-plus/icons-vue";
|
||||
import UserAPI, { type RegisterForm } from "@/api/system/user";
|
||||
import UserAPI, { type RegisterForm } from "@/api/module_system/user";
|
||||
import { useConfigStore } from "@/store";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { FormInstance } from "element-plus";
|
||||
import UserAPI, { type ForgetPasswordForm } from "@/api/system/user";
|
||||
import UserAPI, { type ForgetPasswordForm } from "@/api/module_system/user";
|
||||
|
||||
const loading = ref(false); // 按钮 loading 状态
|
||||
const isCapsLock = ref(false); // 是否大写锁定
|
||||
+1
-1
@@ -199,7 +199,7 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import DeptAPI, { DeptTable, DeptForm, DeptPageQuery } from "@/api/system/dept";
|
||||
import DeptAPI, { DeptTable, DeptForm, DeptPageQuery } from "@/api/module_system/dept";
|
||||
import { useUserStore } from "@/store";
|
||||
import { formatTree } from "@/utils/common";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
+2
-2
@@ -276,10 +276,10 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const drawerVisible = defineModel<boolean>()
|
||||
import DictAPI, { DictDataTable, DictDataForm, DictDataPageQuery } from "@/api/system/dict";
|
||||
import DictAPI, { DictDataTable, DictDataForm, DictDataPageQuery } from "@/api/module_system/dict";
|
||||
import { useAppStore } from "@/store/modules/app.store";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
import UserTableSelect from "@/views/system/user/components/UserTableSelect.vue";
|
||||
import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
+3
-3
@@ -234,9 +234,9 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import DictAPI, { DictTable, DictForm, DictPageQuery } from "@/api/system/dict";
|
||||
import DataDrawer from "@/views/system/dict/components/DataDrawer.vue"
|
||||
import UserTableSelect from "@/views/system/user/components/UserTableSelect.vue";
|
||||
import DictAPI, { DictTable, DictForm, DictPageQuery } from "@/api/module_system/dict";
|
||||
import DataDrawer from "@/views/module_system/dict/components/DataDrawer.vue"
|
||||
import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
+2
-2
@@ -216,8 +216,8 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import LogAPI, { LogTable, LogPageQuery } from "@/api/system/log";
|
||||
import UserTableSelect from "@/views/system/user/components/UserTableSelect.vue";
|
||||
import LogAPI, { LogTable, LogPageQuery } from "@/api/module_system/log";
|
||||
import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import JsonPretty from "@/components/JsonPretty/index.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
+1
-1
@@ -482,7 +482,7 @@ import { useAppStore } from "@/store/modules/app.store";
|
||||
import { useUserStore } from "@/store/modules/user.store";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
|
||||
import MenuAPI, { MenuPageQuery, MenuForm, MenuTable } from "@/api/system/menu";
|
||||
import MenuAPI, { MenuPageQuery, MenuForm, MenuTable } from "@/api/module_system/menu";
|
||||
import { MenuTypeEnum } from "@/enums/system/menu.enum";
|
||||
import { formatTree } from "@/utils/common";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
+2
-2
@@ -267,7 +267,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDictStore } from "@/store/index";
|
||||
import UserTableSelect from "@/views/system/user/components/UserTableSelect.vue";
|
||||
import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
@@ -278,7 +278,7 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import NoticeAPI, { NoticeTable, NoticeForm, NoticePageQuery } from "@/api/system/notice";
|
||||
import NoticeAPI, { NoticeTable, NoticeForm, NoticePageQuery } from "@/api/module_system/notice";
|
||||
|
||||
const queryFormRef = ref();
|
||||
const dataFormRef = ref();
|
||||
+1
-1
@@ -254,7 +254,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import ParamsAPI, { type ConfigTable } from '@/api/system/params';
|
||||
import ParamsAPI, { type ConfigTable } from '@/api/module_system/params';
|
||||
import { useConfigStore } from "@/store";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
+2
-2
@@ -210,8 +210,8 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import ParamsAPI, { ConfigTable, ConfigForm, ConfigPageQuery } from "@/api/system/params";
|
||||
import UserTableSelect from "@/views/system/user/components/UserTableSelect.vue";
|
||||
import ParamsAPI, { ConfigTable, ConfigForm, ConfigPageQuery } from "@/api/module_system/params";
|
||||
import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
+2
-2
@@ -218,9 +218,9 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import PositionAPI, { PositionTable, PositionForm, PositionPageQuery } from "@/api/system/position";
|
||||
import PositionAPI, { PositionTable, PositionForm, PositionPageQuery } from "@/api/module_system/position";
|
||||
import { useUserStore } from "@/store";
|
||||
import UserTableSelect from "@/views/system/user/components/UserTableSelect.vue";
|
||||
import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
+3
-3
@@ -146,9 +146,9 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
import { listToTree, formatTree } from "@/utils/common";
|
||||
import RoleAPI, { permissionDataType, permissionDeptType, permissionMenuType } from "@/api/system/role";
|
||||
import DeptAPI from "@/api/system/dept";
|
||||
import MenuAPI from "@/api/system/menu";
|
||||
import RoleAPI, { permissionDataType, permissionDeptType, permissionMenuType } from "@/api/module_system/role";
|
||||
import DeptAPI from "@/api/module_system/dept";
|
||||
import MenuAPI from "@/api/module_system/menu";
|
||||
import type { TreeInstance } from 'element-plus'
|
||||
import { useAppStore } from "@/store/modules/app.store";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
+2
-2
@@ -285,9 +285,9 @@ defineOptions({
|
||||
});
|
||||
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import RoleAPI, { RoleTable, RoleForm, TablePageQuery } from "@/api/system/role";
|
||||
import RoleAPI, { RoleTable, RoleForm, TablePageQuery } from "@/api/module_system/role";
|
||||
import { useUserStore } from "@/store";
|
||||
import UserTableSelect from "@/views/system/user/components/UserTableSelect.vue";
|
||||
import UserTableSelect from "@/views/module_system/user/components/UserTableSelect.vue";
|
||||
import ExportModal from "@/components/CURD/ExportModal.vue";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
import { QuestionFilled, ArrowUp, ArrowDown } from "@element-plus/icons-vue";
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DeptAPI, { DeptPageQuery } from "@/api/system/dept";
|
||||
import DeptAPI, { DeptPageQuery } from "@/api/module_system/dept";
|
||||
import { formatTree } from "@/utils/common";
|
||||
import type { FilterNodeMethodFunction, TreeInstance } from 'element-plus'
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ISelectConfig } from "@/components/TableSelect/index.vue";
|
||||
import UserAPI from "@/api/system/user";
|
||||
import UserAPI from "@/api/module_system/user";
|
||||
|
||||
// 父组件双向绑定的值(选中用户ID)
|
||||
const props = defineProps<{ modelValue?: number }>();
|
||||
+4
-4
@@ -369,11 +369,11 @@ import { useAppStore } from "@/store/modules/app.store";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
import { ResultEnum } from "@/enums/api/result.enum";
|
||||
|
||||
import UserAPI, { type UserForm, type UserInfo, type UserPageQuery } from "@/api/system/user";
|
||||
import UserAPI, { type UserForm, type UserInfo, type UserPageQuery } from "@/api/module_system/user";
|
||||
import { formatTree } from "@/utils/common";
|
||||
import PositionAPI from "@/api/system/position";
|
||||
import DeptAPI from "@/api/system/dept";
|
||||
import RoleAPI from "@/api/system/role";
|
||||
import PositionAPI from "@/api/module_system/position";
|
||||
import DeptAPI from "@/api/module_system/dept";
|
||||
import RoleAPI from "@/api/module_system/role";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
|
||||
import DeptTree from "./components/DeptTree.vue";
|
||||
Reference in New Issue
Block a user