From f99927f1341a591402a84985478c204acd7e2b22 Mon Sep 17 00:00:00 2001 From: xueyinfei <1207092115@qq.com> Date: Sat, 30 Aug 2025 23:26:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=83=E6=95=B0=E6=8D=AE=E8=A1=A8=E5=85=B3?= =?UTF-8?q?=E7=B3=BB=E8=AE=A1=E7=AE=97=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/metadata_config_controller.py | 56 ++++ .../dao/batch_label_config_dao.py | 33 ++- .../entity/do/metadata_config_do.py | 17 ++ .../entity/vo/metadata_config_vo.py | 28 ++ .../service/batch_label_config_service.py | 42 +++ vue-fastapi-backend/utils/common_util.py | 78 ++++- .../src/api/metadataConfig/metadataConfig.js | 25 +- .../views/metadataConfig/metaclas/index.vue | 31 +- .../views/metadataConfig/relconf/index.vue | 274 ++++++++++++++++++ 9 files changed, 562 insertions(+), 22 deletions(-) create mode 100644 vue-fastapi-frontend/src/views/metadataConfig/relconf/index.vue diff --git a/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py b/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py index eaab5d6..7365a73 100644 --- a/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py +++ b/vue-fastapi-backend/module_admin/controller/metadata_config_controller.py @@ -22,6 +22,8 @@ from module_admin.entity.vo.metadata_config_vo import ( SecuBizPermiConfigPageQueryModel, BatchBusiLabelConfigModel, BatchDatatypeLabelConfigModel, + BatchTableRelaLabelConfigModel, + TableRelaLabelConfigQuery, ColOpTypeLabelPageQueryModel, BatchDataopLabelConfigModelVo, BatchBusiLabelConfigPageQueryModel, @@ -443,6 +445,18 @@ async def get_datatype_label_list( return ResponseUtil.success(data=result) +@metadataConfigController.get('/tableRelaLabel/list', response_model=PageResponseModel) +async def get_tableRela_label_list( + request: Request, + query: TableRelaLabelConfigQuery = Depends(TableRelaLabelConfigQuery.as_query), + query_db: AsyncSession = Depends(get_db), + +): + result = await BatchLabelConfigService.get_table_rel_list_services(query_db, query, False) + logger.info('获取数据类型标签配置列表成功') + return ResponseUtil.success(data=result) + + @metadataConfigController.post('/datatypeLabel') @ValidateFields(validate_model='add_datatype_label') async def add_datatype_label( @@ -459,6 +473,22 @@ async def add_datatype_label( return ResponseUtil.success(msg=result.message) +@metadataConfigController.post('/tableRelaLabel') +@ValidateFields(validate_model='add_datatype_label') +async def add_table_rela_label( + request: Request, + model: BatchTableRelaLabelConfigModel, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + model.upd_by = current_user.user.user_name + model.upd_time = datetime.now() + + result = await BatchLabelConfigService.add_table_rela_services(query_db, model) + logger.info(result.message) + return ResponseUtil.success(msg=result.message) + + @metadataConfigController.put('/datatypeLabel') @ValidateFields(validate_model='edit_datatype_label') async def edit_datatype_label( @@ -475,6 +505,21 @@ async def edit_datatype_label( return ResponseUtil.success(msg=result.message) +@metadataConfigController.put('/tableRelaLabel') +@ValidateFields(validate_model='edit_datatype_label') +async def edit_table_rela_label( + request: Request, + model: BatchTableRelaLabelConfigModel, + query_db: AsyncSession = Depends(get_db), + current_user: CurrentUserModel = Depends(LoginService.get_current_user), +): + model.upd_by = current_user.user.user_name + model.upd_time = datetime.now() + result = await BatchLabelConfigService.edit_table_rela_services(query_db, model) + logger.info(result.message) + return ResponseUtil.success(msg=result.message) + + @metadataConfigController.delete('/datatypeLabel/{onum_list}') async def delete_datatype_label( request: Request, @@ -486,6 +531,17 @@ async def delete_datatype_label( return ResponseUtil.success(msg=result.message) +@metadataConfigController.delete('/tableRelaLabel/{onum_list}') +async def delete_datatype_label( + request: Request, + onum_list: str, + query_db: AsyncSession = Depends(get_db), +): + result = await BatchLabelConfigService.delete_table_rela_services(query_db, onum_list) + logger.info(result.message) + return ResponseUtil.success(msg=result.message) + + @metadataConfigController.get('/datatypeLabel/{onum}', response_model=BatchDatatypeLabelConfigModel) async def get_datatype_label_detail( request: Request, diff --git a/vue-fastapi-backend/module_admin/dao/batch_label_config_dao.py b/vue-fastapi-backend/module_admin/dao/batch_label_config_dao.py index 478b6c9..1d95dc1 100644 --- a/vue-fastapi-backend/module_admin/dao/batch_label_config_dao.py +++ b/vue-fastapi-backend/module_admin/dao/batch_label_config_dao.py @@ -1,7 +1,7 @@ from sqlalchemy import delete, select, update, and_ from sqlalchemy.ext.asyncio import AsyncSession from module_admin.entity.do.metadata_config_do import BatchBusiLabelConfig, BatchDataopLabelConfig, \ - BatchDatatypeLabelConfig + BatchDatatypeLabelConfig, BatchTableRelaLabelConfig from utils.page_util import PageUtil @@ -90,6 +90,11 @@ class BatchLabelConfigDAO: result = await db.execute(select(BatchDatatypeLabelConfig).where(BatchDatatypeLabelConfig.onum == onum)) return result.scalars().first() + @classmethod + async def get_table_rela_detail_by_id(cls, db: AsyncSession, onum: int): + result = await db.execute(select(BatchTableRelaLabelConfig).where(BatchTableRelaLabelConfig.onum == onum)) + return result.scalars().first() + @classmethod async def get_datatype_list(cls, db: AsyncSession, query_object, is_page: bool = False): query = select(BatchDatatypeLabelConfig).where( @@ -99,6 +104,16 @@ class BatchLabelConfigDAO: return await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) + @classmethod + async def get_table_rel_list(cls, db: AsyncSession, query_object, is_page: bool = False): + query = select(BatchTableRelaLabelConfig).where( + BatchTableRelaLabelConfig.a_mdl_name.like(f'%{query_object.a_mdl_name}%') if query_object.a_mdl_name else True, + BatchTableRelaLabelConfig.b_mdl_name.like(f'%{query_object.b_mdl_name}%') if query_object.b_mdl_name else True, + BatchTableRelaLabelConfig.a_ssys_id == query_object.a_ssys_id if query_object.a_ssys_id else True, + BatchTableRelaLabelConfig.a_ssys_id == query_object.a_ssys_id if query_object.a_ssys_id else True, + ).order_by(BatchTableRelaLabelConfig.upd_time.desc()) + return await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, is_page) + @classmethod async def add_datatype(cls, db: AsyncSession, obj): db_obj = BatchDatatypeLabelConfig(**obj.model_dump()) @@ -106,13 +121,29 @@ class BatchLabelConfigDAO: await db.flush() return db_obj + @classmethod + async def add_table_rela(cls, db: AsyncSession, obj): + db_obj = BatchTableRelaLabelConfig(**obj.model_dump()) + db.add(db_obj) + await db.flush() + return db_obj + @classmethod async def edit_datatype(cls, db: AsyncSession, onum: int, update_data: dict): await db.execute( update(BatchDatatypeLabelConfig).where(BatchDatatypeLabelConfig.onum == onum).values(**update_data)) + @classmethod + async def edit_table_rela(cls, db: AsyncSession, onum: int, update_data: dict): + await db.execute( + update(BatchTableRelaLabelConfig).where(BatchTableRelaLabelConfig.onum == onum).values(**update_data)) + @classmethod async def delete_datatype(cls, db: AsyncSession, onum_list: list[int]): await db.execute(delete(BatchDatatypeLabelConfig).where(BatchDatatypeLabelConfig.onum.in_(onum_list))) + @classmethod + async def delete_table_rela(cls, db: AsyncSession, onum_list: list[int]): + await db.execute(delete(BatchTableRelaLabelConfig).where(BatchTableRelaLabelConfig.onum.in_(onum_list))) + # endregion diff --git a/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py b/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py index 46994ce..bc4bfd7 100644 --- a/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py +++ b/vue-fastapi-backend/module_admin/entity/do/metadata_config_do.py @@ -153,6 +153,23 @@ class BatchDatatypeLabelConfig(Base): upd_time = Column(DateTime, nullable=True, comment='更新时间') +class BatchTableRelaLabelConfig(Base): + """ + 批处理字段类型标签配置表 ORM 映射类,对应表 t_batch_datatype_label_config + """ + __tablename__ = "t_batch_tab_rela_label_config" + + onum = Column(Integer, primary_key=True, autoincrement=True, comment="唯一编号") + rela_type = Column(String(100), nullable=True, comment="字段类型") + ratio = Column(DECIMAL(10, 4), nullable=True, comment="比率") + a_ssys_id = Column(Integer, nullable=True) + a_mdl_name = Column(String(50), nullable=True) + b_ssys_id = Column(Integer, nullable=True) + b_mdl_name = Column(String(50), nullable=True) + upd_by = Column(String(50), nullable=True) + upd_time = Column(DateTime, nullable=True, comment='更新时间') + + class DatasecConfig(Base): """ 数据安全参数配置 ORM 映射类,对应表 t_datasec_config diff --git a/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py b/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py index 96300c9..804ce8d 100644 --- a/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py +++ b/vue-fastapi-backend/module_admin/entity/vo/metadata_config_vo.py @@ -279,6 +279,23 @@ class BatchDatatypeLabelConfigModel(BaseModel): upd_time: Optional[datetime] = Field(default=None, description='更新时间') +class BatchTableRelaLabelConfigModel(BaseModel): + """ + 批处理字段类型标签配置 Pydantic 模型 + """ + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + + onum: Optional[int] = Field(default=None, description='唯一编号') + rela_type: Optional[str] = Field(default=None, description='字段类型') + ratio: Optional[float] = Field(default=None, description='比率') + a_ssys_id: Optional[int] = Field(default=None, description='系统代码') + a_mdl_name: Optional[str] = Field(default=None, description='模块名称') + b_ssys_id: Optional[int] = Field(default=None, description='系统代码') + b_mdl_name: Optional[str] = Field(default=None, description='模块名称') + upd_by: Optional[str] = Field(default=None, description='更新人员') + upd_time: Optional[datetime] = Field(default=None, description='更新时间') + + class BatchDataopLabelConfigModelVo(BaseModel): """ 批处理字段类型标签配置 Pydantic 模型 @@ -321,6 +338,17 @@ class ColOpTypeLabelPageQueryModel(BaseModel): page_size: Optional[int] = Field(default=None, description='每页记录数') +@as_query +class TableRelaLabelConfigQuery(BaseModel): + model_config = ConfigDict(alias_generator=to_camel, from_attributes=True) + a_ssys_id: Optional[int] = Field(default=None, description='系统名称A') + a_mdl_name: Optional[str] = Field(default=None, description='模式名称A') + b_ssys_id: Optional[int] = Field(default=None, description='系统名称B') + b_mdl_name: Optional[str] = Field(default=None, description='模式名称B') + page_num: Optional[int] = Field(default=None, description='当前页码') + page_size: Optional[int] = Field(default=None, description='每页记录数') + + @as_query class BatchDatatypeLabelConfigPageQueryModel(BatchDatatypeLabelConfigModel): page_num: int = Field(default=1, description='当前页码') diff --git a/vue-fastapi-backend/module_admin/service/batch_label_config_service.py b/vue-fastapi-backend/module_admin/service/batch_label_config_service.py index 322afb5..88ae108 100644 --- a/vue-fastapi-backend/module_admin/service/batch_label_config_service.py +++ b/vue-fastapi-backend/module_admin/service/batch_label_config_service.py @@ -147,6 +147,10 @@ class BatchLabelConfigService: async def get_datatype_list_services(cls, db: AsyncSession, query_object, is_page: bool = False): return await BatchLabelConfigDAO.get_datatype_list(db, query_object, is_page) + @classmethod + async def get_table_rel_list_services(cls, db: AsyncSession, query_object, is_page: bool = False): + return await BatchLabelConfigDAO.get_table_rel_list(db, query_object, is_page) + @classmethod async def add_datatype_services(cls, db: AsyncSession, page_object): try: @@ -157,6 +161,16 @@ class BatchLabelConfigService: await db.rollback() raise e + @classmethod + async def add_table_rela_services(cls, db: AsyncSession, page_object): + try: + await BatchLabelConfigDAO.add_table_rela(db, page_object) + await db.commit() + return CrudResponseModel(is_success=True, message="新增成功") + except Exception as e: + await db.rollback() + raise e + @classmethod async def edit_datatype_services(cls, db: AsyncSession, page_object): edit_data = page_object.model_dump(exclude_unset=True) @@ -172,6 +186,21 @@ class BatchLabelConfigService: else: raise ServiceException(message="数据类型标签配置不存在") + @classmethod + async def edit_table_rela_services(cls, db: AsyncSession, page_object): + edit_data = page_object.model_dump(exclude_unset=True) + info = await BatchLabelConfigDAO.get_table_rela_detail_by_id(db, page_object.onum) + if info: + try: + await BatchLabelConfigDAO.edit_table_rela(db, page_object.onum, edit_data) + await db.commit() + return CrudResponseModel(is_success=True, message="更新成功") + except Exception as e: + await db.rollback() + raise e + else: + raise ServiceException(message="数据类型标签配置不存在") + @classmethod async def delete_datatype_services(cls, db: AsyncSession, onum_list: str): id_list = [int(x.strip()) for x in onum_list.split(",") if x.strip()] @@ -186,6 +215,19 @@ class BatchLabelConfigService: await db.rollback() raise e + @classmethod + async def delete_table_rela_services(cls, db: AsyncSession, onum_list: str): + id_list = [int(x.strip()) for x in onum_list.split(",") if x.strip()] + if not id_list: + raise ServiceException(message="无效的编号列表") + try: + await BatchLabelConfigDAO.delete_table_rela(db, id_list) + await db.commit() + return CrudResponseModel(is_success=True, message="删除成功") + except Exception as e: + await db.rollback() + raise e + @classmethod async def get_datatype_detail_services(cls, db: AsyncSession, onum: str): result = await BatchLabelConfigDAO.get_datatype_detail_by_id(db, onum) diff --git a/vue-fastapi-backend/utils/common_util.py b/vue-fastapi-backend/utils/common_util.py index 86f502e..68c10b7 100644 --- a/vue-fastapi-backend/utils/common_util.py +++ b/vue-fastapi-backend/utils/common_util.py @@ -46,7 +46,8 @@ class SqlalchemyUtil: @classmethod def base_to_dict( - cls, obj: Union[Base, Dict], transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' + cls, obj: Union[Base, Dict], + transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' ): """ 将sqlalchemy模型对象转换为字典 @@ -69,7 +70,7 @@ class SqlalchemyUtil: @classmethod def serialize_result( - cls, result: Any, transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' + cls, result: Any, transform_case: Literal['no_case', 'snake_to_camel', 'camel_to_snake'] = 'no_case' ): """ 将sqlalchemy查询结果序列化 @@ -254,6 +255,79 @@ def get_excel_template(header_list: List, selector_header_list: List, option_lis return excel_data +def get_excel_template_with_sheets(sheet_configs: List[Dict], # 每个Sheet的配置(包含表头、选择器等) + option_list: List[dict] # 全局选择器选项(所有Sheet共享) + ): + """ + 生成多Sheet页的Excel模板(支持下拉选择器) + + :param sheet_configs: 每个Sheet的配置,格式示例: + [ + { + "sheet_name": "Sheet1", # Sheet名称 + "header_list": ["姓名", "性别", "城市"], # 表头 + "selector_header_list": ["性别", "城市"], # 需要下拉选择的表头 + }, + { + "sheet_name": "Sheet2", + "header_list": ["产品", "类别", "价格"], + "selector_header_list": ["类别"], + } + ] + :param option_list: 下拉选项配置,格式示例: + [ + {"性别": ["男", "女"], "城市": ["北京", "上海"]}, # Sheet1的下拉选项 + {"类别": ["电子产品", "食品", "服装"]} # Sheet2的下拉选项 + ] + :return: Excel文件的二进制数据 + """ + wb = Workbook() + # 删除默认创建的Sheet(如果不需要) + if "Sheet" in wb.sheetnames: + wb.remove(wb["Sheet"]) + + # 遍历每个Sheet的配置 + for sheet_config in sheet_configs: + sheet_name = sheet_config["sheet_name"] + header_list = sheet_config["header_list"] + selector_header_list = sheet_config["selector_header_list"] + # 创建Sheet + ws = wb.create_sheet(title=sheet_name) + # 设置表头样式 + header_fill = PatternFill(start_color="ababab", end_color="ababab", fill_type="solid") + # 写入表头 + for col_num, header in enumerate(header_list, 1): + cell = ws.cell(row=1, column=col_num) + cell.value = header + cell.fill = header_fill + cell.alignment = Alignment(horizontal="center") + ws.column_dimensions[get_column_letter(col_num)].width = 12 + # 设置下拉选择器 + for selector_header in selector_header_list: + if selector_header not in header_list: + continue # 防止配置错误 + column_index = header_list.index(selector_header) + 1 + column_letter = get_column_letter(column_index) + # 从option_list中获取当前Sheet的选项(假设option_list顺序与sheet_configs一致) + sheet_options = option_list[sheet_configs.index(sheet_config)] + selector_options = sheet_options.get(selector_header, []) + if selector_options: + dv = DataValidation( + type="list", + formula1=f'"{",".join(selector_options)}"', # 下拉选项(逗号分隔) + ) + # 应用到整个列(从第2行开始) + dv.add(f"{column_letter}2:{column_letter}1048576") + ws.add_data_validation(dv) + # 保存为二进制数据 + file = io.BytesIO() + wb.save(file) + file.seek(0) + # 读取字节数据 + excel_data = file.getvalue() + return excel_data + + def get_filepath_from_url(url: str): """ 工具方法:根据请求参数获取文件路径 diff --git a/vue-fastapi-frontend/src/api/metadataConfig/metadataConfig.js b/vue-fastapi-frontend/src/api/metadataConfig/metadataConfig.js index f2771ab..e9493b6 100644 --- a/vue-fastapi-frontend/src/api/metadataConfig/metadataConfig.js +++ b/vue-fastapi-frontend/src/api/metadataConfig/metadataConfig.js @@ -291,6 +291,15 @@ export function getDatatypeLabelList(data) { }) } +// 获取表关系属性配置列表(分页) +export function getTableRelaLabelList(data) { + return request({ + url: '/default-api/metadataConfig/tableRelaLabel/list', + method: 'get', + params: data + }) +} + // 新增数据类型标签 export function addDatatypeLabel(data) { return request({ @@ -299,20 +308,28 @@ export function addDatatypeLabel(data) { data }) } +// 新增表关系类型标签 +export function addTableRelaLabel(data) { + return request({ + url: '/default-api/metadataConfig/tableRelaLabel', + method: 'post', + data + }) +} // 编辑数据类型标签 -export function editDatatypeLabel(data) { +export function editTableRelaLabel(data) { return request({ - url: '/default-api/metadataConfig/datatypeLabel', + url: '/default-api/metadataConfig/tableRelaLabel', method: 'put', data }) } // 删除数据类型标签 -export function deleteDatatypeLabel(onumList) { +export function deleteTableRelaLabel(onumList) { return request({ - url: '/default-api/metadataConfig/datatypeLabel/' + onumList, + url: '/default-api/metadataConfig/tableRelaLabel/' + onumList, method: 'delete' }) } diff --git a/vue-fastapi-frontend/src/views/metadataConfig/metaclas/index.vue b/vue-fastapi-frontend/src/views/metadataConfig/metaclas/index.vue index bef108d..4d6434d 100644 --- a/vue-fastapi-frontend/src/views/metadataConfig/metaclas/index.vue +++ b/vue-fastapi-frontend/src/views/metadataConfig/metaclas/index.vue @@ -131,12 +131,12 @@ > - + - +