si@aidatagov.com 4 months ago
parent
commit
ebea6fabb3
  1. 2
      vue-fastapi-backend/config/env.py
  2. 57
      vue-fastapi-backend/module_admin/controller/approval_controller.py
  3. 41
      vue-fastapi-backend/module_admin/controller/meta_controller.py
  4. 78
      vue-fastapi-backend/module_admin/dao/approval_dao.py
  5. 321
      vue-fastapi-backend/module_admin/dao/meta_dao.py
  6. 3
      vue-fastapi-backend/module_admin/entity/do/aichat_do.py
  7. 20
      vue-fastapi-backend/module_admin/entity/do/approval_do.py
  8. 156
      vue-fastapi-backend/module_admin/entity/do/meta_do.py
  9. 34
      vue-fastapi-backend/module_admin/entity/vo/approval_vo.py
  10. 56
      vue-fastapi-backend/module_admin/entity/vo/meta_vo.py
  11. 121
      vue-fastapi-backend/module_admin/service/approval_service.py
  12. 126
      vue-fastapi-backend/module_admin/service/meta_service.py
  13. 4
      vue-fastapi-backend/server.py
  14. 2
      vue-fastapi-frontend/package.json
  15. 2
      vue-fastapi-frontend/src/api/aichat/aichat.js
  16. 25
      vue-fastapi-frontend/src/api/flow/flow.js
  17. 46
      vue-fastapi-frontend/src/api/meta/metaInfo.js
  18. 2
      vue-fastapi-frontend/src/components/TopNav/index.vue
  19. 72
      vue-fastapi-frontend/src/components/codemirror/SQLCodeMirror.vue
  20. 35
      vue-fastapi-frontend/src/layout/components/Navbar.vue
  21. 14
      vue-fastapi-frontend/src/router/index.js
  22. 4
      vue-fastapi-frontend/src/store/modules/user.js
  23. 1
      vue-fastapi-frontend/src/utils/time.js
  24. 43
      vue-fastapi-frontend/src/views/aichat/aichat.vue
  25. 3
      vue-fastapi-frontend/src/views/login.vue
  26. 885
      vue-fastapi-frontend/src/views/meta/metaInfo/index.vue
  27. 11
      vue-fastapi-frontend/src/views/system/flow/conf.vue
  28. 140
      vue-fastapi-frontend/src/views/system/flow/index.vue
  29. 3
      vue-fastapi-frontend/vite.config.js

2
vue-fastapi-backend/config/env.py

@ -14,7 +14,7 @@ class AppSettings(BaseSettings):
app_env: str = 'dev' app_env: str = 'dev'
app_name: str = 'RuoYi-FasAPI' app_name: str = 'RuoYi-FasAPI'
app_root_path: str = '/dev-api' app_root_path: str = '/default-api'
app_host: str = '0.0.0.0' app_host: str = '0.0.0.0'
app_port: int = 9099 app_port: int = 9099
app_version: str = '1.0.0' app_version: str = '1.0.0'

57
vue-fastapi-backend/module_admin/controller/approval_controller.py

@ -0,0 +1,57 @@
import json
import os
import shutil
from fastapi import APIRouter, Depends, Request, UploadFile, File, Form
from sqlalchemy.ext.asyncio import AsyncSession
from config.get_db import get_db
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.entity.vo.approval_vo import ApplyModel, OperateModel, ApprovalQueryObject
from module_admin.service.login_service import LoginService
from module_admin.service.approval_service import ApprovalService
from utils.log_util import logger
from utils.response_util import ResponseUtil
approvalController = APIRouter(prefix='/approval', dependencies=[Depends(LoginService.get_current_user)])
@approvalController.post("/apply")
async def flow_apply(request: Request,
apply: ApplyModel,
query_db: AsyncSession = Depends(get_db)):
apply_result = await ApprovalService.apply_services(query_db, apply)
return ResponseUtil.success(msg=apply_result.message)
@approvalController.post("/operate")
async def flow_operate(request: Request,
operate: OperateModel,
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
operate_result = await ApprovalService.operate_services(query_db, operate, current_user)
return ResponseUtil.success(msg=operate_result.message)
@approvalController.get("/list")
async def flow_list(request: Request,
query_param: ApprovalQueryObject = Depends(ApprovalQueryObject.as_query),
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
meta_query_result = await ApprovalService.get_flow_list_services(query_db, query_param, current_user)
return ResponseUtil.success(data=meta_query_result)
@approvalController.get("/waitingTotal")
async def getWaitingTotal(request: Request,
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
meta_query_result = await ApprovalService.get_waiting_total_services(query_db, current_user)
return ResponseUtil.success(data=meta_query_result)
@approvalController.post("/cancelApply/{flow_id}")
async def cancel_apply(request: Request,
flow_id: str,
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
operate_result = await ApprovalService.cancel_apply_services(query_db, flow_id, current_user)
return ResponseUtil.success(msg=operate_result.message)

41
vue-fastapi-backend/module_admin/controller/meta_controller.py

@ -0,0 +1,41 @@
from fastapi import APIRouter, Depends, Request
from sqlalchemy.ext.asyncio import AsyncSession
from config.get_db import get_db
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService
from utils.response_util import ResponseUtil
from module_admin.service.meta_service import MetaService
from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject, SuppleModel
metaController = APIRouter(prefix='/dasset', dependencies=[Depends(LoginService.get_current_user)])
@metaController.get("/meta/get")
async def get_meta_list(request: Request, meta_query: MetaPageObject = Depends(MetaPageObject.as_query),
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
meta_query_result = await MetaService.get_meta_list_services(query_db, meta_query, current_user)
return ResponseUtil.success(data=meta_query_result)
@metaController.get("/meta/column/list")
async def get_meta_list(request: Request, meta_query: MetaColObject = Depends(MetaColObject.as_query),
query_db: AsyncSession = Depends(get_db)):
meta_query_result = await MetaService.get_meta_col_list_services(query_db, meta_query)
return ResponseUtil.success(data=meta_query_result)
@metaController.get("/meta/clas/list")
async def get_meta_clas_list(request: Request, query_db: AsyncSession = Depends(get_db)):
result = await MetaService.get_meta_clas_list_services(query_db)
return ResponseUtil.success(data=result)
@metaController.post("/meta/supp")
async def meta_supp(request: Request,
supple: SuppleModel,
query_db: AsyncSession = Depends(get_db),
current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
result = await MetaService.meta_supp(query_db, supple, current_user)
return ResponseUtil.success(msg=result.message)

78
vue-fastapi-backend/module_admin/dao/approval_dao.py

@ -0,0 +1,78 @@
from sqlalchemy import desc, delete, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.do.approval_do import FlowApproval
from module_admin.entity.vo.approval_vo import ApprovalQueryObject
from module_admin.entity.vo.user_vo import CurrentUserModel
from sqlalchemy import select, text, cast, Integer, and_, or_, outerjoin, func, join
from utils.page_util import PageUtil
class ApprovalDao:
"""
菜单管理模块数据库操作层
"""
@classmethod
async def add_flow_approval(cls, db: AsyncSession, flow_approval: FlowApproval):
db.add(flow_approval)
await db.flush()
return flow_approval
@classmethod
async def get_flow_by_id(cls, db: AsyncSession, flowId: str):
result = (
(
await db.execute(
select(FlowApproval).where(
FlowApproval.id == flowId
)
)
)
.scalars()
.first()
)
return result
@classmethod
async def edit_flow_approval(cls, db: AsyncSession, flow: dict):
await db.execute(update(FlowApproval), [flow])
@classmethod
async def get_flow_list(cls, db: AsyncSession, query_param: ApprovalQueryObject, current_user: CurrentUserModel):
query = (
select(FlowApproval)
.where(
(FlowApproval.applicant == query_param.status) if query_param.applicant else True,
(FlowApproval.businessType == query_param.businessType) if query_param.businessType else True,
or_(
FlowApproval.approver == current_user.user.user_name,
FlowApproval.approver.in_(current_user.roles)
) if current_user.user.user_name != 'admin' else True
)
.order_by(FlowApproval.applyTime)
.distinct()
)
# 注意:这里不执行查询,而是将查询对象传递给 paginate 方法
result = await PageUtil.paginate(db, query, query_param.page_num, query_param.page_size, True)
return result
@classmethod
async def get_waiting_total(cls, db: AsyncSession, current_user: CurrentUserModel):
return (await db.execute(select(func.count()).select_from(FlowApproval)
.where(FlowApproval.approver == current_user.user.user_name))).scalar()
@classmethod
async def get_flow_by_idAndUser(cls, db: AsyncSession, flow_id: str, username: str):
result = (
(
await db.execute(
select(FlowApproval).where(
FlowApproval.id == flow_id,
FlowApproval.applicant == username
)
)
)
.scalars()
.first()
)
return result

321
vue-fastapi-backend/module_admin/dao/meta_dao.py

@ -0,0 +1,321 @@
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, text, cast, Integer, and_, or_, outerjoin, func, join, update, desc
from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject
from module_admin.entity.do.meta_do import MetadataExtractInfo, MetadataSuppInfo, MetadataFldTabExtractInfo, \
MetadataFldSuppInfo, MetadataClas, MetadataSuppInfoVett, MetadataFldSuppInfoVett
import json
import re
from utils.time_format_util import object_format_datetime, list_format_datetime, format_datetime_dict_list
from datetime import datetime, time
from typing import Union, List
from utils.log_util import logger
import uuid
import datetime
from utils.page_util import PageUtil
class MetaDao:
@classmethod
async def get_meta_rel_list(cls, db: AsyncSession, query_object: MetaPageObject):
"""
根据查询参数获取用户列表信息
"""
# 主查询
query = (
select(
MetadataExtractInfo.onum.label('extract_onum'),
MetadataExtractInfo.extract_ver_num,
MetadataExtractInfo.ver_desc.label('extract_ver_desc'),
MetadataExtractInfo.ssys_cd,
MetadataExtractInfo.data_whs_name,
MetadataExtractInfo.mdl_name,
MetadataExtractInfo.tab_no,
MetadataExtractInfo.tab_type,
MetadataExtractInfo.tab_eng_name,
MetadataExtractInfo.tab_cn_name,
MetadataExtractInfo.tab_rec_num,
MetadataExtractInfo.upd_time.label('extract_upd_time'),
MetadataSuppInfo.onum.label('supp_onum'),
MetadataSuppInfo.crrct_ver_num.label('supp_crrct_ver_num'),
MetadataSuppInfo.tab_crrct_name,
MetadataSuppInfo.tab_desc,
MetadataSuppInfo.pic,
MetadataSuppInfo.gov_flag,
MetadataSuppInfo.rec_stat.label('supp_rec_stat'),
MetadataSuppInfo.tab_clas,
MetadataSuppInfo.rec_subm_prsn,
MetadataSuppInfo.upd_time.label('supp_upd_time')
).join(MetadataSuppInfo,
and_(
MetadataExtractInfo.ssys_cd == MetadataSuppInfo.ssys_cd,
MetadataExtractInfo.mdl_name == MetadataSuppInfo.mdl_name,
MetadataExtractInfo.tab_eng_name == MetadataSuppInfo.tab_eng_name
), isouter=True)
.join(
MetadataFldTabExtractInfo,
and_(
MetadataExtractInfo.ssys_cd == MetadataFldTabExtractInfo.ssys_cd,
MetadataExtractInfo.mdl_name == MetadataFldTabExtractInfo.mdl_name,
MetadataExtractInfo.tab_eng_name == MetadataFldTabExtractInfo.tab_eng_name
), isouter=True)
.join(
MetadataFldSuppInfo,
and_(
MetadataExtractInfo.ssys_cd == MetadataFldSuppInfo.ssys_cd,
MetadataExtractInfo.mdl_name == MetadataFldSuppInfo.mdl_name,
MetadataExtractInfo.tab_eng_name == MetadataFldSuppInfo.tab_eng_name
), isouter=True)
.where(
MetadataExtractInfo.ssys_cd == query_object.ssys_cd if query_object.ssys_cd else True,
MetadataExtractInfo.mdl_name == query_object.mdl_name if query_object.mdl_name else True,
or_(MetadataExtractInfo.tab_eng_name.like(f'%{query_object.tab_name}%'),
MetadataExtractInfo.tab_cn_name.like(f'%{query_object.tab_name}%'),
MetadataSuppInfo.tab_eng_name.like(f'%{query_object.tab_name}%'),
MetadataSuppInfo.tab_crrct_name.like(f'%{query_object.tab_name}%'),
) if query_object.tab_name else True,
or_(
MetadataFldTabExtractInfo.fld_eng_name.like(f'%{query_object.col_name}%'),
MetadataFldTabExtractInfo.fld_cn_name.like(f'%{query_object.col_name}%'),
MetadataFldSuppInfo.fld_eng_name.like(f'%{query_object.col_name}%'),
MetadataFldSuppInfo.fld_crrct_name.like(f'%{query_object.col_name}%'),
) if query_object.col_name else True,
MetadataExtractInfo.tab_type == query_object.tab_type if query_object.tab_type else True,
MetadataExtractInfo.rec_stat == query_object.rec_stat if query_object.rec_stat else True,
).distinct()
)
result = await PageUtil.paginate(db, query, query_object.page_num, query_object.page_size, True)
return result
@classmethod
async def get_meta_col_list(cls, db: AsyncSession, query_object: MetaColObject):
query_result = (
(
await db.execute(
select(
MetadataFldTabExtractInfo.onum.label('extract_onum'),
MetadataFldTabExtractInfo.extract_ver_num,
MetadataFldTabExtractInfo.ssys_cd,
MetadataFldTabExtractInfo.data_whs_name,
MetadataFldTabExtractInfo.mdl_name,
MetadataFldTabExtractInfo.tab_no,
MetadataFldTabExtractInfo.tab_eng_name,
MetadataFldTabExtractInfo.fld_no,
MetadataFldTabExtractInfo.fld_eng_name,
MetadataFldTabExtractInfo.fld_cn_name,
MetadataFldTabExtractInfo.fld_type,
MetadataFldTabExtractInfo.pk_flag,
MetadataFldTabExtractInfo.require_flag,
MetadataFldTabExtractInfo.idx_flag,
MetadataFldTabExtractInfo.upd_time.label('extract_upd_time'),
MetadataFldSuppInfo.onum.label('supp_onum'),
MetadataFldSuppInfo.crrct_ver_num,
MetadataFldSuppInfo.fld_crrct_name,
MetadataFldSuppInfo.crrct_pk_flag,
MetadataFldSuppInfo.fld_desc,
MetadataFldSuppInfo.pic,
MetadataFldSuppInfo.fld_clas,
MetadataFldSuppInfo.fld_null_rate,
MetadataFldSuppInfo.rec_stat.label('supp_rec_stat'),
MetadataFldSuppInfo.upd_time.label('supp_upd_time')
).select_from(
join(
MetadataFldTabExtractInfo,
MetadataFldSuppInfo,
and_(
MetadataFldTabExtractInfo.ssys_cd == MetadataFldSuppInfo.ssys_cd,
MetadataFldTabExtractInfo.mdl_name == MetadataFldSuppInfo.mdl_name,
MetadataFldTabExtractInfo.tab_eng_name == MetadataFldSuppInfo.tab_eng_name,
MetadataFldTabExtractInfo.fld_eng_name == MetadataFldSuppInfo.fld_eng_name
), isouter=True
)
).where(
MetadataFldTabExtractInfo.ssys_cd == query_object.ssys_cd,
MetadataFldTabExtractInfo.mdl_name == query_object.mdl_name,
MetadataFldTabExtractInfo.tab_eng_name == query_object.tab_name
).distinct()
)
).all()
)
return [row._asdict() for row in query_result]
@classmethod
async def get_meta_clas_list(cls, db: AsyncSession):
query_result = (
(
await db.execute(
select(MetadataClas).where(MetadataClas.clas_eff_flag == 1).distinct()
)
).scalars().all()
)
return query_result
@classmethod
async def get_lastest_meta_data_supp_vett(cls, db: AsyncSession, ssys_cd: str, mdl_name: str, tab_eng_name: str):
query_result = (
(
await db.execute(
select(MetadataSuppInfoVett).where(MetadataSuppInfoVett.ssys_cd == ssys_cd,
MetadataSuppInfoVett.mdl_name == mdl_name,
MetadataSuppInfoVett.tab_eng_name == tab_eng_name)
.order_by(desc(MetadataSuppInfoVett.apply_time))
.distinct().limit(1)
)
).scalars().first()
)
return query_result
@classmethod
async def insertMetadataSuppInfoVett(cls, table: MetadataSuppInfoVett, db: AsyncSession):
db.add(table)
await db.flush()
return table
@classmethod
async def updateMetadataSuppInfoVett(cls, onum: str, operateType: str, db: AsyncSession):
table = dict(
onum=onum,
apply_status=operateType
)
await db.execute(update(MetadataSuppInfoVett), [table])
@classmethod
async def insertMetadataSuppInfo(cls, table: MetadataSuppInfoVett, db: AsyncSession):
suppTable = MetadataSuppInfo()
suppTable.onum = uuid.uuid4()
suppTable.ssys_cd = table.ssys_cd
suppTable.mdl_name = table.mdl_name
suppTable.tab_eng_name = table.tab_eng_name
suppTable.tab_crrct_name = table.tab_crrct_name
suppTable.tab_desc = table.tab_desc
suppTable.pic = table.pic
suppTable.gov_flag = table.gov_flag
suppTable.rec_stat = table.rec_stat
suppTable.tab_clas = table.tab_clas
suppTable.rec_subm_prsn = table.rec_subm_prsn
suppTable.upd_time = table.upd_time
db.add(suppTable)
await db.flush()
return suppTable
@classmethod
async def updateMetadataSuppInfo(cls, onum: str, table: MetadataSuppInfoVett, db: AsyncSession):
suppTable = dict(
onum=onum,
tab_crrct_name=table.tab_crrct_name,
tab_desc=table.tab_desc,
pic=table.pic,
gov_flag=table.gov_flag,
rec_stat=table.rec_stat,
tab_clas=table.tab_clas,
rec_subm_prsn=table.rec_subm_prsn,
upd_time=table.upd_time
)
await db.execute(update(MetadataSuppInfo), [suppTable])
@classmethod
async def get_supp_table_vett_by_id(cls, suppId: str, db: AsyncSession):
query_result = (
(
await db.execute(
select(MetadataSuppInfoVett).where(MetadataSuppInfoVett.onum == suppId).distinct()
)
).scalars().first()
)
return query_result
@classmethod
async def get_supp_table_by_vett(cls, sysCode: str, mdlName: str, tableName: str, db: AsyncSession):
query_result = (
(
await db.execute(
select(MetadataSuppInfo).where(MetadataSuppInfo.ssys_cd == sysCode,
MetadataSuppInfo.mdl_name == mdlName,
MetadataSuppInfo.tab_eng_name == tableName).distinct()
)
).scalars().first()
)
return query_result
@classmethod
async def insertMetadataFldSuppInfoVett(cls, column: MetadataFldSuppInfoVett, db: AsyncSession):
db.add(column)
await db.flush()
return column
@classmethod
async def updateMetadataFldSuppInfoVett(cls, onum: str, operateType: str, db: AsyncSession):
updateColumn = dict(
onum=onum, apply_status=operateType
)
await db.execute(update(MetadataFldSuppInfoVett), [updateColumn])
@classmethod
async def updateMetadataFldSuppInfo(cls, onum: str, column: MetadataFldSuppInfoVett, db: AsyncSession):
updateColumn = dict(
onum=onum,
fld_crrct_name=column.fld_crrct_name,
crrct_pk_flag=column.crrct_pk_flag,
fld_desc=column.fld_desc,
pic=column.pic,
fld_clas=column.fld_clas,
fld_null_rate=column.fld_null_rate,
rec_stat=column.rec_stat,
upd_time=column.upd_time,
)
await db.execute(update(MetadataFldSuppInfo), [updateColumn])
@classmethod
async def insertMetadataFldSuppInfo(cls, column: MetadataFldSuppInfoVett, db: AsyncSession):
suppColumn = MetadataFldSuppInfo()
suppColumn.onum = uuid.uuid4()
suppColumn.ssys_cd = column.ssys_cd
suppColumn.mdl_name = column.mdl_name
suppColumn.tab_eng_name = column.tab_eng_name
suppColumn.fld_eng_name = column.fld_eng_name
suppColumn.fld_crrct_name = column.fld_crrct_name
suppColumn.crrct_pk_flag = column.crrct_pk_flag
suppColumn.fld_desc = column.fld_desc
suppColumn.pic = column.pic
suppColumn.fld_clas = column.fld_clas
suppColumn.fld_null_rate = column.fld_null_rate
suppColumn.rec_stat = column.rec_stat
suppColumn.upd_time = column.upd_time
db.add(suppColumn)
await db.flush()
return suppColumn
@classmethod
async def get_meta_col_supp_vett(cls, table: MetadataSuppInfoVett, db: AsyncSession):
sql_query = text("select max(apply_time) from t_metadata_fld_supp_info_vett where ssys_cd ='" + table.ssys_cd +
"' and mdl_name = '" + table.mdl_name + "' and tab_eng_name = '" + table.tab_eng_name +
"'")
maxTime = (await db.execute(sql_query)).scalar()
query_result = (
(
await db.execute(
select(MetadataFldSuppInfoVett)
.where(MetadataFldSuppInfoVett.ssys_cd == table.ssys_cd,
MetadataFldSuppInfoVett.mdl_name == table.mdl_name,
MetadataFldSuppInfoVett.tab_eng_name == table.tab_eng_name,
MetadataFldSuppInfoVett.apply_time == maxTime).distinct()
)
).scalars().all()
)
return query_result
@classmethod
async def get_supp_column_by_vett(cls, column: MetadataFldSuppInfoVett, db: AsyncSession):
query_result = (
(
await db.execute(
select(MetadataFldSuppInfo)
.where(MetadataFldSuppInfo.ssys_cd == column.ssys_cd,
MetadataFldSuppInfo.mdl_name == column.mdl_name,
MetadataFldSuppInfo.tab_eng_name == column.tab_eng_name,
MetadataFldSuppInfo.fld_eng_name == column.fld_eng_name).distinct()
)
).scalars().first()
)
return query_result

3
vue-fastapi-backend/module_admin/entity/do/aichat_do.py

@ -1,6 +1,7 @@
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text
from config.database import Base from config.database import Base
from datetime import datetime from datetime import datetime
from sqlalchemy.dialects.mysql import LONGTEXT
class AiChatHistory(Base): class AiChatHistory(Base):
@ -17,7 +18,7 @@ class AiChatHistory(Base):
isStop = Column(Boolean, default=None, comment='是否停止') isStop = Column(Boolean, default=None, comment='是否停止')
user = Column(Integer, default=None, comment='所属用户') user = Column(Integer, default=None, comment='所属用户')
time = Column(String(50), default=None, comment='问答时间') time = Column(String(50), default=None, comment='问答时间')
content = Column(Text, default=None, comment='问答内容') content = Column(LONGTEXT, default=None, comment='问答内容')
operate = Column(String(50), default=None, comment='点赞,差评等操作') operate = Column(String(50), default=None, comment='点赞,差评等操作')
thumbDownReason = Column(String(255), default=None, comment='差评原因') thumbDownReason = Column(String(255), default=None, comment='差评原因')
file = Column(String(255), default=None, comment='文件id集合') file = Column(String(255), default=None, comment='文件id集合')

20
vue-fastapi-backend/module_admin/entity/do/approval_do.py

@ -0,0 +1,20 @@
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text
from config.database import Base
from datetime import datetime
class FlowApproval(Base):
"""
流程审批表
"""
__tablename__ = 'flow_approval'
id = Column(String(50), primary_key=True, comment='id')
businessType = Column(String(50), default='', comment='业务审批模块')
businessId = Column(String(255), default='', comment='业务id串')
applicant = Column(String(50), default=None, comment='申请人')
applyTime = Column(String(50), default=None, comment='审批时间')
approver = Column(String(50), default=None, comment='下一步审批人')
nextStep = Column(Integer, default=None, comment="下一步编号")
status = Column(String(10), default=None, comment='状态')
approvalFlow = Column(Text, default=None, comment='审批流') # [{审批人:‘’,审批时间:‘’,'审批结果':‘’,审批意见:''},{}]数组

156
vue-fastapi-backend/module_admin/entity/do/meta_do.py

@ -0,0 +1,156 @@
from sqlalchemy import Column, Float, Integer, String, Text, DateTime, Boolean, ForeignKey, UniqueConstraint, Index, \
text
from config.database import Base
from datetime import datetime
class MetadataExtractInfo(Base):
"""
元数据采集信息表
"""
__tablename__ = 't_metadata_extract_info'
onum = Column(Integer, primary_key=True, comment='唯一编号')
extract_ver_num = Column(String(50, collation='utf8_general_ci'), comment='采集版本号')
ver_desc = Column(String(250, collation='utf8_general_ci'), comment='版本描述')
ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='系统代码')
data_whs_name = Column(String(50, collation='utf8_general_ci'), comment='数据仓库名称')
mdl_name = Column(String(50, collation='utf8_general_ci'), comment='模式名称')
tab_no = Column(Integer, comment='表编号')
tab_type = Column(String(50, collation='utf8_general_ci'), comment='表类型')
tab_eng_name = Column(String(250, collation='utf8_general_ci'), comment='表英文名称')
tab_cn_name = Column(String(250, collation='utf8_general_ci'), comment='表中文名称')
tab_rec_num = Column(Integer, comment='记录数')
upd_time = Column(DateTime, comment='更新时间')
class MetadataSuppInfo(Base):
"""
元数据补充信息表
"""
__tablename__ = 't_metadata_supp_info'
# onum = Column(Integer, primary_key=True, comment='唯一编号')
onum = Column(String(36, collation='utf8mb4_unicode_ci'), primary_key=True, comment='唯一编号')
crrct_ver_num = Column(String(50, collation='utf8_general_ci'), comment='补录版本号')
ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='系统代码')
mdl_name = Column(String(50, collation='utf8_general_ci'), comment='模型名称')
tab_eng_name = Column(String(250, collation='utf8_general_ci'), comment='表英文名称')
tab_crrct_name = Column(String(250, collation='utf8_general_ci'), comment='表补录名称')
tab_desc = Column(String(500, collation='utf8_general_ci'), comment='表描述')
pic = Column(String(64, collation='utf8_general_ci'), comment='表图片')
gov_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='治理标志(0否 1是)')
rec_stat = Column(String(1, collation='utf8_general_ci'), default='0', comment='记录状态(0有效 1无效)')
tab_clas = Column(Text, comment='表分类')
rec_subm_prsn = Column(String(255, collation='utf8_general_ci'), comment='记录提交人')
upd_time = Column(DateTime, comment='更新时间')
class MetadataSuppInfoVett(Base):
"""
元数据补充信息审批表
"""
__tablename__ = 't_metadata_supp_info_vett'
# onum = Column(Integer, primary_key=True, comment='唯一编号')
onum = Column(String(36, collation='utf8mb4_unicode_ci'), primary_key=True, comment='唯一编号')
crrct_ver_num = Column(String(50, collation='utf8_general_ci'), comment='补录版本号')
ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='系统代码')
mdl_name = Column(String(50, collation='utf8_general_ci'), comment='模型名称')
tab_eng_name = Column(String(250, collation='utf8_general_ci'), comment='表英文名称')
tab_crrct_name = Column(String(250, collation='utf8_general_ci'), comment='表补录名称')
tab_desc = Column(String(500, collation='utf8_general_ci'), comment='表描述')
pic = Column(String(64, collation='utf8_general_ci'), comment='表图片')
gov_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='治理标志(0否 1是)')
rec_stat = Column(String(1, collation='utf8_general_ci'), default='0', comment='记录状态(0有效 1无效)')
tab_clas = Column(Text, comment='表分类')
rec_subm_prsn = Column(String(255, collation='utf8_general_ci'), comment='记录提交人')
apply_time = Column(String(255, collation='utf8_general_ci'), comment='申请时间')
apply_status = Column(String(255, collation='utf8_general_ci'), comment='申请状态')
upd_time = Column(String(255, collation='utf8_general_ci'), comment='更新时间')
class MetadataClas(Base):
"""
元数据分类表
"""
__tablename__ = 't_metadata_clas'
clas_onum = Column(Integer, primary_key=True, comment='分类编号')
clas_pri_clas = Column(String(50, collation='utf8_general_ci'), comment='一级分类')
clas_scd_clas = Column(String(50, collation='utf8_general_ci'), comment='二级分类')
clas_thre_clas = Column(String(50, collation='utf8_general_ci'), comment='三级分类')
clas_name = Column(String(255, collation='utf8_general_ci'), comment='分类名称')
clas_eff_flag = Column(String(1, collation='utf8_general_ci'), default='0', comment='生效标志(0有效 1无效)')
rec_subm_prsn = Column(String(255, collation='utf8_general_ci'), comment='记录提交人')
upd_time = Column(DateTime, comment='更新时间')
class MetadataFldTabExtractInfo(Base):
"""
字段采集信息表
"""
__tablename__ = 't_metadata_fld_tab_extract_info'
onum = Column(Integer, primary_key=True, comment='唯一编号')
extract_ver_num = Column(String(50, collation='utf8_general_ci'), comment='采集版本号')
ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='系统代码')
data_whs_name = Column(String(255, collation='utf8_general_ci'), comment='数据仓库名称')
mdl_name = Column(String(255, collation='utf8_general_ci'), comment='模块名称')
tab_no = Column(String(50, collation='utf8_general_ci'), comment='表编号')
tab_eng_name = Column(String(250, collation='utf8_general_ci'), comment='表英文名称')
fld_no = Column(Integer, comment='字段编号')
fld_eng_name = Column(String(255, collation='utf8_general_ci'), comment='字段英文名称')
fld_cn_name = Column(String(255, collation='utf8_general_ci'), comment='字段中文名称')
fld_type = Column(String(50, collation='utf8_general_ci'), comment='字段类型')
pk_flag = Column(Boolean, default=False, comment='是否为主键') #待确认字段类型
require_flag = Column(Boolean, default=False, comment='是否必填')
idx_flag = Column(Boolean, default=False, comment='是否为索引')
upd_time = Column(DateTime, comment='更新时间')
class MetadataFldSuppInfo(Base):
"""
字段补充信息表
"""
__tablename__ = 't_metadata_fld_supp_info'
onum = Column(String(50, collation='utf8_general_ci'), primary_key=True, comment='唯一编号')
crrct_ver_num = Column(String(50, collation='utf8_general_ci'), comment='补充版本号')
ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='系统代码')
mdl_name = Column(String(255, collation='utf8_general_ci'), comment='模块名称')
tab_eng_name = Column(String(250, collation='utf8_general_ci'), comment='表英文名称')
fld_eng_name = Column(String(255, collation='utf8_general_ci'), comment='字段英文名称')
fld_crrct_name = Column(String(255, collation='utf8_general_ci'), comment='补充字段名称')
crrct_pk_flag = Column(Boolean, default=False, comment='是否为主键')
fld_desc = Column(String(255, collation='utf8_general_ci'), comment='字段描述')
pic = Column(String(255, collation='utf8_general_ci'), comment='图片字段')
fld_clas = Column(Text, comment='字段分类')
fld_null_rate = Column(String(50, collation='utf8_general_ci'), comment='字段空值率')
rec_stat = Column(String(50, collation='utf8_general_ci'), comment='记录状态')
upd_time = Column(DateTime, comment='更新时间')
class MetadataFldSuppInfoVett(Base):
"""
字段补充信息表
"""
__tablename__ = 't_metadata_fld_supp_info_vett'
onum = Column(String(50, collation='utf8_general_ci'), primary_key=True, comment='唯一编号')
crrct_ver_num = Column(String(50, collation='utf8_general_ci'), comment='补充版本号')
ssys_cd = Column(String(50, collation='utf8_general_ci'), comment='系统代码')
mdl_name = Column(String(255, collation='utf8_general_ci'), comment='模块名称')
tab_eng_name = Column(String(250, collation='utf8_general_ci'), comment='表英文名称')
fld_eng_name = Column(String(255, collation='utf8_general_ci'), comment='字段英文名称')
fld_crrct_name = Column(String(255, collation='utf8_general_ci'), comment='补充字段名称')
crrct_pk_flag = Column(Boolean, default=False, comment='是否为主键')
fld_desc = Column(String(255, collation='utf8_general_ci'), comment='字段描述')
pic = Column(String(255, collation='utf8_general_ci'), comment='图片字段')
fld_clas = Column(Text, comment='字段分类')
fld_null_rate = Column(String(50, collation='utf8_general_ci'), comment='字段空值率')
rec_stat = Column(String(50, collation='utf8_general_ci'), comment='记录状态')
upd_time = Column(DateTime, comment='更新时间')
rec_subm_prsn = Column(String(255, collation='utf8_general_ci'), comment='记录提交人')
apply_time = Column(String(255, collation='utf8_general_ci'), comment='申请时间')
apply_status = Column(String(255, collation='utf8_general_ci'), comment='申请状态')

34
vue-fastapi-backend/module_admin/entity/vo/approval_vo.py

@ -0,0 +1,34 @@
from pydantic import BaseModel
from typing import Union, Optional, List
from module_admin.annotation.pydantic_annotation import as_query
from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic.alias_generators import to_camel
class ApplyModel(BaseModel):
businessType: Optional[str] = None
businessId: Optional[str] = None
applicant: Optional[str] = None
class OperateModel(BaseModel):
flowId: Optional[str] = None # 流程编号
operateType: Optional[str] = None # 操作:驳回,同意:reject, success
operateComment: Optional[str] = None # 审批意见
@as_query
class ApprovalQueryObject(BaseModel):
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
page_num: int
page_size: int
applicant: Optional[str] = None
businessType: Optional[str] = None
class EditObjectModel(BaseModel):
id: Optional[str] = None
status: Optional[str] = None
approver: Optional[str] = None
nextStep: Optional[int] = None
approvalFlow: Optional[str] = None

56
vue-fastapi-backend/module_admin/entity/vo/meta_vo.py

@ -0,0 +1,56 @@
from typing import Union, Optional, List
from datetime import datetime
from module_admin.annotation.pydantic_annotation import as_query
from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic.alias_generators import to_camel
class MetaModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
ssys_cd: Optional[str] = Field(default=None, description='系统名称')
mdl_name: Optional[str] = Field(default=None, description='模式名称')
tab_name: Optional[str] = Field(default=None, description='表名')
tab_type: Optional[str] = Field(default=None, description='表类型')
col_name: Optional[str] = Field(default=None, description='字段名称')
tag_name: Optional[str] = Field(default=None, description='标签名称')
rec_stat: Optional[str] = Field(default=None, description='补录状态')
@as_query
class MetaPageObject(MetaModel):
page_num: int
page_size: int
@as_query
class MetaColObject(BaseModel):
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
ssys_cd: Optional[str] = Field(default=None, description='系统名称')
mdl_name: Optional[str] = Field(default=None, description='模式名称')
tab_name: Optional[str] = Field(default=None, description='表名')
class ColumnSuppleModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
fld_eng_name: Optional[str] = None
fld_crrct_name: Optional[str] = None
crrct_pk_flag: Optional[int] = None
fld_desc: Optional[str] = None
pic: Optional[str] = None
fld_clas: Optional[str] = None
fld_null_rate: Optional[str] = None
rec_stat: Optional[str] = None
class SuppleModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True)
ssys_cd: Optional[str] = None
mdl_name: Optional[str] = None
tab_eng_name: Optional[str] = None
tab_crrct_name: Optional[str] = None
tab_desc: Optional[str] = None
pic: Optional[str] = None
gov_flag: Optional[str] = None
rec_stat: Optional[str] = None
tab_clas: Optional[str] = None
columnInfo: Optional[List[ColumnSuppleModel]] = None

121
vue-fastapi-backend/module_admin/service/approval_service.py

@ -0,0 +1,121 @@
import json
import uuid
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession
from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.entity.vo.approval_vo import ApplyModel, OperateModel, ApprovalQueryObject, EditObjectModel
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.entity.do.approval_do import FlowApproval
from exceptions.exception import ServiceException, ServiceWarning
from datetime import datetime
from utils.common_util import CamelCaseUtil
from module_admin.dao.approval_dao import ApprovalDao
from module_admin.dao.meta_dao import MetaDao
class ApprovalService:
"""
智能问答服务层
"""
@classmethod
async def apply_services(cls, result_db: AsyncSession, apply: ApplyModel):
flow_approval = FlowApproval()
flow_approval.id = uuid.uuid4()
flow_approval.businessType = apply.businessType
flow_approval.businessId = apply.businessId
flow_approval.applicant = apply.applicant
flow_approval.applyTime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
flow_approval.status = 'waiting'
# todo 后期进行流程配置,角色 or 人员 下一步审批人
flow_approval.approver = 'admin'
flow_approval.nextStep = '1'
await ApprovalDao.add_flow_approval(result_db, flow_approval)
await result_db.commit()
return CrudResponseModel(is_success=True, message='申请成功')
@classmethod
async def operate_services(cls, result_db: AsyncSession, operate: OperateModel, current_user: CurrentUserModel):
flow_approval = await ApprovalDao.get_flow_by_id(result_db, operate.flowId)
if flow_approval is None:
raise ServiceException(message=f'所操作的流程不存在')
if flow_approval.status == 'succeed' or flow_approval.status == 'rejected':
raise ServiceException(message='所操作的流程已结束')
if flow_approval.status == 'canceled':
raise ServiceException(message='所操作的流程已撤回')
array = []
if flow_approval.approvalFlow is not None:
array = json.loads(flow_approval.approvalFlow)
array.append({'operator': current_user.user.user_name,
'operateTime': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
'operate': operate.operateType,
'operateComment': operate.operateComment,
})
edit = EditObjectModel()
edit.id = flow_approval.id
edit.approvalFlow = json.dumps(array)
if operate.operateType == 'success':
# todo 增加流程配置,并查询出下一步操作人以及步骤
edit.status = 'succeed' # 有下一步执行人,则设置为 pending
edit.approver = None
edit.nextStep = -1
if operate.operateType == 'reject':
edit.status = 'rejected'
edit.approver = None
edit.nextStep = -1
if flow_approval.businessType == 't_metadata_supp_info':
await cls.syncSuppInfo(result_db, flow_approval.businessId, operate.operateType)
await ApprovalDao.edit_flow_approval(result_db, edit.model_dump(exclude_unset=True))
await result_db.commit()
return CrudResponseModel(is_success=True, message='操作成功')
@classmethod
async def syncSuppInfo(cls, result_db: AsyncSession, suppId: str, operateType: str):
table = await MetaDao.get_supp_table_vett_by_id(suppId, result_db)
if table is None:
raise ServiceException(message='所查询的业务数据不存在')
if table.apply_status == 'succeed' or table.apply_status == 'rejected':
raise ServiceException(message='所改业务已审核完毕')
column_list = await MetaDao.get_meta_col_supp_vett(table, result_db)
if column_list is not None and len(column_list) > 0:
for column in column_list:
column.apply_status = operateType
suppColumn = await MetaDao.get_supp_column_by_vett(column, result_db)
await MetaDao.updateMetadataFldSuppInfoVett(column.onum, operateType, result_db)
if suppColumn is None:
await MetaDao.insertMetadataFldSuppInfo(column, result_db)
else:
await MetaDao.updateMetadataFldSuppInfo(suppColumn.onum, column, result_db)
suppTable = await MetaDao.get_supp_table_by_vett(table.ssys_cd, table.mdl_name, table.tab_eng_name, result_db)
await MetaDao.updateMetadataSuppInfoVett(table.onum, operateType, result_db)
if suppTable is None:
await MetaDao.insertMetadataSuppInfo(table, result_db)
else:
await MetaDao.updateMetadataSuppInfo(suppTable.onum, table, result_db)
return CrudResponseModel(is_success=True, message='操作成功')
@classmethod
async def get_flow_list_services(cls, query_db: AsyncSession, query_param: ApprovalQueryObject,
current_user: CurrentUserModel):
result = await ApprovalDao.get_flow_list(query_db, query_param, current_user)
return result
@classmethod
async def get_waiting_total_services(cls, query_db: AsyncSession, current_user: CurrentUserModel):
result = await ApprovalDao.get_waiting_total(query_db, current_user)
return result
@classmethod
async def cancel_apply_services(cls, query_db: AsyncSession, flow_id: str,
current_user: CurrentUserModel):
flow = await ApprovalDao.get_flow_by_idAndUser(flow_id, current_user.user.user_name)
if flow is None:
raise ServiceException(message=f'所操作的流程不存在')
if flow.status == 'succeed' or flow.status == 'rejected':
raise ServiceException(message='所操作的流程已结束,无需撤回')
if flow.status == 'canceled':
raise ServiceException(message='所操作的流程已撤回,无需重复操作')
flow.status = 'canceled'
await ApprovalDao.edit_flow_approval(query_db, flow)
return CrudResponseModel(is_success=True, message='操作成功')

126
vue-fastapi-backend/module_admin/service/meta_service.py

@ -0,0 +1,126 @@
import uuid
from module_admin.entity.vo.meta_vo import MetaPageObject, MetaColObject, SuppleModel
from module_admin.entity.do.meta_do import MetadataSuppInfo, MetadataFldSuppInfo, MetadataSuppInfoVett, \
MetadataFldSuppInfoVett
from module_admin.dao.meta_dao import MetaDao
from datetime import datetime
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.entity.vo.approval_vo import ApplyModel
from sqlalchemy.ext.asyncio import AsyncSession
from exceptions.exception import ServiceException, ServiceWarning
from utils.pwd_util import *
from utils.common_util import *
from utils.log_util import logger
from module_admin.service.login_service import LoginService
from module_admin.service.approval_service import ApprovalService
from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.entity.vo.user_vo import CurrentUserModel
from utils.common_util import CamelCaseUtil
import re
class MetaService:
@classmethod
async def get_meta_list_services(cls, result_db: AsyncSession, query_object: MetaPageObject,
current_user: CurrentUserModel):
"""
获取元信息service
:param result_db: orm对象
:param query_object: 查询参数对象
:param data_scope_sql: 数据权限对应的查询sql语句
:return: 用户列表信息对象
"""
# print("********")
# 0,正常,1已发布,2审核中,3已审核,4已作废
if 'admin' in current_user.roles:
meta_rel_list = await MetaDao.get_meta_rel_list(result_db, query_object)
return meta_rel_list
else:
# meta_rel_list = MetaDao.get_meta_rel_list(result_db, query_object, data_scope_sql)
return None
@classmethod
async def get_meta_col_list_services(cls, result_db: AsyncSession, query_object: MetaColObject):
meta_result = await MetaDao.get_meta_col_list(result_db, query_object)
return cls.convert_json_keys(meta_result)
@classmethod
async def get_meta_clas_list_services(cls, result_db: AsyncSession):
result = await MetaDao.get_meta_clas_list(result_db)
return CamelCaseUtil.transform_result(result)
@classmethod
async def meta_supp(cls, result_db: AsyncSession, supple: SuppleModel, current_user: CurrentUserModel):
# 0,暂存 waiting,申请中,pending审核中,succeed,rejected已审核,canceled已取消
hasTable = await MetaDao.get_lastest_meta_data_supp_vett(result_db, supple.ssys_cd, supple.mdl_name,
supple.tab_eng_name)
tableOnum = uuid.uuid4()
if hasTable is not None:
if hasTable.apply_status == 'waiting':
raise ServiceException(message=f'所补录对象已存在补录待审核记录,请等待审批完成或撤回申请后,再行补录')
if hasTable.apply_status == 'pending':
raise ServiceException(message=f'所补录对象已存在待审核记录,请等待审批完成后,再行补录')
suppTableInfo = MetadataSuppInfoVett()
suppTableInfo.onum = tableOnum
suppTableInfo.ssys_cd = supple.ssys_cd
suppTableInfo.mdl_name = supple.mdl_name
suppTableInfo.tab_eng_name = supple.tab_eng_name
suppTableInfo.tab_crrct_name = supple.tab_crrct_name
suppTableInfo.tab_desc = supple.tab_desc
suppTableInfo.pic = supple.pic
suppTableInfo.gov_flag = supple.gov_flag
suppTableInfo.tab_clas = supple.tab_clas
suppTableInfo.rec_subm_prsn = current_user.user.user_name
suppTableInfo.apply_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
suppTableInfo.apply_status = 'waiting'
await MetaDao.insertMetadataSuppInfoVett(suppTableInfo, result_db)
await result_db.commit()
if supple.columnInfo is not None and len(supple.columnInfo) > 0:
for column in supple.columnInfo:
suppColumnInfo = MetadataFldSuppInfoVett()
suppColumnInfo.onum = uuid.uuid4()
suppColumnInfo.ssys_cd = supple.ssys_cd
suppColumnInfo.mdl_name = supple.mdl_name
suppColumnInfo.tab_eng_name = supple.tab_eng_name
suppColumnInfo.fld_eng_name = column.fld_eng_name
suppColumnInfo.fld_crrct_name = column.fld_crrct_name
suppColumnInfo.crrct_pk_flag = column.crrct_pk_flag
suppColumnInfo.fld_desc = column.fld_desc
suppColumnInfo.pic = column.pic
suppColumnInfo.fld_clas = column.fld_clas
suppColumnInfo.fld_null_rate = column.fld_null_rate
suppColumnInfo.rec_stat = column.rec_stat
suppColumnInfo.rec_subm_prsn = current_user.user.user_name
suppColumnInfo.apply_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
suppColumnInfo.apply_status = 'waiting'
await MetaDao.insertMetadataFldSuppInfoVett(suppColumnInfo, result_db)
await result_db.commit()
applyModel = ApplyModel()
applyModel.businessType = "t_metadata_supp_info"
applyModel.businessId = tableOnum
applyModel.applicant = current_user.user.user_name
await ApprovalService.apply_services(result_db, applyModel)
return CrudResponseModel(is_success=True, message='操作成功')
@classmethod
def snake_to_camel(cls, snake_str: str):
# 将字符串按下划线分割成列表
components = snake_str.split('_')
# 第一个单词保持小写,后续单词首字母大写
camel_str = components[0] + ''.join(word.capitalize() for word in components[1:])
return camel_str
@classmethod
def convert_json_keys(cls, data):
if isinstance(data, dict):
# 如果数据是字典,则转换键名并递归处理值
return {cls.snake_to_camel(k): cls.convert_json_keys(v) for k, v in data.items()}
elif isinstance(data, list):
# 如果数据是列表,则递归处理列表中的每个元素
return [cls.convert_json_keys(element) for element in data]
else:
# 如果数据不是字典或列表,则直接返回(即基本数据类型,如字符串、数字等)
return data

4
vue-fastapi-backend/server.py

@ -24,7 +24,9 @@ from module_admin.controller.server_controller import serverController
from module_admin.controller.user_controller import userController from module_admin.controller.user_controller import userController
from module_admin.controller.aichat_controller import aichatController from module_admin.controller.aichat_controller import aichatController
from module_admin.controller.metatask_controller import metataskController from module_admin.controller.metatask_controller import metataskController
from module_admin.controller.meta_controller import metaController
from module_admin.controller.metasecurity_controller import metaSecurityController from module_admin.controller.metasecurity_controller import metaSecurityController
from module_admin.controller.approval_controller import approvalController
from sub_applications.handle import handle_sub_applications from sub_applications.handle import handle_sub_applications
from utils.common_util import worship from utils.common_util import worship
from utils.log_util import logger from utils.log_util import logger
@ -83,6 +85,8 @@ controller_list = [
{'router': metataskController, 'tags': ['元数据管理-元数据任务模块']}, {'router': metataskController, 'tags': ['元数据管理-元数据任务模块']},
{'router': metaSecurityController, 'tags': ['元数据管理-数据安全']}, {'router': metaSecurityController, 'tags': ['元数据管理-数据安全']},
{'router': aichatController, 'tags': ['智能问答模块']}, {'router': aichatController, 'tags': ['智能问答模块']},
{'router': metaController, 'tags': ['元数据信息模块']},
{'router': approvalController, 'tags': ['审批模块']},
] ]
for controller in controller_list: for controller in controller_list:

2
vue-fastapi-frontend/package.json

@ -24,6 +24,7 @@
"@vueuse/core": "10.11.0", "@vueuse/core": "10.11.0",
"ant-design-vue": "^4.1.1", "ant-design-vue": "^4.1.1",
"axios": "0.28.1", "axios": "0.28.1",
"codemirror-editor-vue3": "^2.8.0",
"echarts": "5.5.1", "echarts": "5.5.1",
"element-plus": "2.8.0", "element-plus": "2.8.0",
"file-saver": "2.0.5", "file-saver": "2.0.5",
@ -36,6 +37,7 @@
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "2.1.7", "pinia": "2.1.7",
"remixicon": "^4.6.0", "remixicon": "^4.6.0",
"sql-formatter": "^15.4.10",
"uuid": "^11.0.4", "uuid": "^11.0.4",
"vue": "3.4.15", "vue": "3.4.15",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",

2
vue-fastapi-frontend/src/api/aichat/aichat.js

@ -8,7 +8,7 @@ export function listChatHistory(sessionId) {
sessionId = '0' sessionId = '0'
} }
return request({ return request({
url: '/aichat/session/list/'+sessionId, url: '/default-api/aichat/session/list/'+sessionId,
method: 'get', method: 'get',
params: {} params: {}
}) })

25
vue-fastapi-frontend/src/api/flow/flow.js

@ -0,0 +1,25 @@
import request from '@/utils/request'
export function getApprovalList(data) {
return request({
url: '/default-api/approval/list',
method: 'get',
params: data
})
}
export function getWaitingFlowCount() {
return request({
url: '/default-api/approval/waitingTotal',
method: 'get'
})
}
export function operateProcess(data) {
return request({
url: '/default-api/approval/operate',
method: 'post',
data: data
})
}

46
vue-fastapi-frontend/src/api/meta/metaInfo.js

@ -0,0 +1,46 @@
import request from '@/utils/request'
// 查询参数列表
export function getDataSourceList(query) {
return request({
url: '/ds-api/dolphinscheduler/datasources?pageNo=1&pageSize=100',
method: 'get',
headers: {dashUserName:query.userName,dashPassword:query.password}
})
}
// 查询参数列表
export function getMetaDataList(query) {
return request({
url: '/default-api/dasset/meta/get',
method: 'get',
params: query
})
}
export function getColumnList(query){
return request({
url:'/default-api/dasset/meta/column/list',
method: 'get',
params: query
})
}
export function getMetaClasList(){
return request({
url:'/default-api/dasset/meta/clas/list',
method: 'get',
})
}
export function postMetaSupp(data){
return request({
url:'/default-api/dasset/meta/supp',
method: 'post',
data: data
})
}

2
vue-fastapi-frontend/src/components/TopNav/index.vue

@ -44,7 +44,7 @@ const visibleNumber = ref(null);
// index // index
const currentIndex = ref(null); const currentIndex = ref(null);
// //
const hideList = ['/index', '/user/profile']; const hideList = ['/index', '/user/profile', 'system/flow/index'];
const appStore = useAppStore() const appStore = useAppStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()

72
vue-fastapi-frontend/src/components/codemirror/SQLCodeMirror.vue

@ -0,0 +1,72 @@
<template>
<codemirror v-model:value="value" :options="sqlOptions" />
</template>
<script setup>
// sql
import * as sqlFormatter from "sql-formatter";
import Codemirror from 'codemirror-editor-vue3';
import 'codemirror/mode/sql/sql.js';
import "codemirror/mode/javascript/javascript.js";
// language
import 'codemirror/mode/javascript/javascript.js';
// theme
import 'codemirror/theme/monokai.css';
//
import 'codemirror/addon/fold/foldcode.js';
import 'codemirror/addon/fold/foldgutter.js';
import 'codemirror/addon/fold/foldgutter.css';
import 'codemirror/addon/fold/brace-fold.js';
//
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/hint/javascript-hint.js';
// lint
import 'codemirror/addon/lint/lint.js';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/addon/lint/json-lint';
//
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/edit/closebrackets.js';
import "codemirror/addon/lint/json-lint.js";
import {watch,ref} from "vue";
const props = defineProps({
data: String,
})
const sqlOptions = {
autorefresh: true, //
smartIndent: true, //
tabSize: 4, // 4
mode: 'sql', //
line: true, //
viewportMargin: Infinity, //
highlightDifferences: true,
autofocus: false,
indentUnit: 2,
readOnly: true, //
showCursorWhenSelecting: true,
firstLineNumber: 1,
matchBrackets: true,//
lineWrapping: true, //
gutters: [
"CodeMirror-linenumbers",
"CodeMirror-foldgutter",
"CodeMirror-lint-markers",
],
lineNumbers: true, //
lint: true, // json
}
const value = ref("")
watch(() => props.data,
(val) =>{
value.value = sqlFormatter.format(val)
}
)
</script>
<style scoped lang="scss">
</style>

35
vue-fastapi-frontend/src/layout/components/Navbar.vue

@ -8,8 +8,13 @@
<template v-if="appStore.device !== 'mobile'"> <template v-if="appStore.device !== 'mobile'">
<header-search id="header-search" class="right-menu-item" /> <header-search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom"> <el-tooltip content="我的流程" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> <el-badge :value="totalFlow" :offset="[-9 , 15]">
<router-link to="/flow/index">
<i class="ri-todo-line right-menu-item hover-effect"></i>
</router-link>
</el-badge>
<!-- <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />-->
</el-tooltip> </el-tooltip>
<el-tooltip content="文档地址" effect="dark" placement="bottom"> <el-tooltip content="文档地址" effect="dark" placement="bottom">
@ -55,16 +60,20 @@ import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull' import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect' import SizeSelect from '@/components/SizeSelect'
import HeaderSearch from '@/components/HeaderSearch' import HeaderSearch from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git' // import RuoYiGit from '@/components/RuoYi/Git'
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
import RuoYiDoc from '@/components/RuoYi/Doc' import RuoYiDoc from '@/components/RuoYi/Doc'
import useAppStore from '@/store/modules/app' import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings'
import cache from "@/plugins/cache";
import {getWaitingFlowCount} from "@/api/flow/flow.js"
const appStore = useAppStore() const appStore = useAppStore()
const userStore = useUserStore() const userStore = useUserStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
const totalFlow = ref(cache.local.get("waitingTotal") || 0)
function toggleSideBar() { function toggleSideBar() {
appStore.toggleSideBar() appStore.toggleSideBar()
} }
@ -98,6 +107,24 @@ const emits = defineEmits(['setLayout'])
function setLayout() { function setLayout() {
emits('setLayout'); emits('setLayout');
} }
onMounted(()=>{
getWaitingFlowCount().then(res=>{
cache.local.set("waitingTotal",res.data)
})
})
// storage
const handleStorageChange = (event) => {
console.log(event)
if (event.key === 'waitingTotal') {
console.log(event)
totalFlow.value = event.newValue || 0;
}
};
onMounted(() => {
window.addEventListener('storage', handleStorageChange);
});
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>

14
vue-fastapi-frontend/src/router/index.js

@ -83,6 +83,20 @@ export const constantRoutes = [
meta: { title: '个人中心', icon: 'user' } meta: { title: '个人中心', icon: 'user' }
} }
] ]
},
{
path: '/flow',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'index',
component: () => import('@/views/system/flow/index'),
name: 'myFlow',
meta: { title: '我的流程', icon: 'ri-todo-line' }
}
]
} }
] ]

4
vue-fastapi-frontend/src/store/modules/user.js

@ -3,6 +3,8 @@ import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate" import { isHttp, isEmpty } from "@/utils/validate"
import defAva from '@/assets/images/profile.jpg' import defAva from '@/assets/images/profile.jpg'
import md5 from 'js-md5' import md5 from 'js-md5'
import cache from "@/plugins/cache.js";
const useUserStore = defineStore( const useUserStore = defineStore(
'user', 'user',
@ -64,6 +66,8 @@ const useUserStore = defineStore(
this.roles = [] this.roles = []
this.permissions = [] this.permissions = []
removeToken() removeToken()
cache.local.remove('username')
cache.local.remove('password')
resolve() resolve()
}).catch(error => { }).catch(error => {
reject(error) reject(error)

1
vue-fastapi-frontend/src/utils/time.js

@ -4,6 +4,7 @@ moment.locale('zh-cn')
// 当天日期 YYYY-MM-DD // 当天日期 YYYY-MM-DD
export const nowDate = moment().format('YYYY-MM-DD') export const nowDate = moment().format('YYYY-MM-DD')
export const nowDateTime = moment().format('YYYY-MM-DD HH:mm:ss')
// 当前时间前n天 // 当前时间前n天
export function beforeDay(n) { export function beforeDay(n) {

43
vue-fastapi-frontend/src/views/aichat/aichat.vue

@ -186,6 +186,7 @@ import MdRenderer from '@/views/aichat/MdRenderer.vue'
import fullscreenG6 from '@/views/aichat/fullscreenG6.vue' import fullscreenG6 from '@/views/aichat/fullscreenG6.vue'
import {getToken} from "@/utils/auth.js"; import {getToken} from "@/utils/auth.js";
import {postChatMessage} from "@/api/aichat/aichat.js" import {postChatMessage} from "@/api/aichat/aichat.js"
import cache from "@/plugins/cache.js";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import {addChat} from "@/api/aichat/aichat"; import {addChat} from "@/api/aichat/aichat";
@ -337,9 +338,9 @@ function regenerationChart(index){
chatList.value.push(chat) chatList.value.push(chat)
let data = { let data = {
"query": chat.content, "query": chat.content,
"user_id": Cookies.get("username"), "user_id": cache.local.get("username"),
"robot": currentMachine.value.length>0?currentMachine.value[0]:"", "robot": currentMachine.value.length>0?currentMachine.value[0]:"",
"sessionId": props.sessionId, "session_id": Cookies.get("chatSessionId"),
"doc": chat.file, "doc": chat.file,
"history": [] "history": []
} }
@ -435,15 +436,15 @@ async function sendChatHandle(event) {
// //
scrollDiv.value.setScrollTop(getMaxHeight()) scrollDiv.value.setScrollTop(getMaxHeight())
}) })
inputValue.value = ''
let data = { let data = {
"query": inputValue.value.trim(), "query": inputValue.value.trim(),
"user_id": Cookies.get("username"), "user_id": cache.local.get("username"),
"robot": currentMachine.value.length > 0 ? currentMachine.value[0] : "", "robot": currentMachine.value.length > 0 ? currentMachine.value[0] : "",
"sessionId": Cookies.get("chatSessionId"), "session_id": Cookies.get("chatSessionId"),
"doc": currentFiles.value, "doc": currentFiles.value,
"history": [] "history": []
} }
inputValue.value = ''
sendChatMessage(data) sendChatMessage(data)
} }
} else { } else {
@ -460,7 +461,6 @@ function sendChatMessage(data){
}else { }else {
currentFiles.value = [] currentFiles.value = []
chatList.value.push({"type":"answer","content":[],"isEnd":false,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName, "operate":'',"thumbDownReason":''}) chatList.value.push({"type":"answer","content":[],"isEnd":false,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName, "operate":'',"thumbDownReason":''})
answerList.value.push({"index":chatList.value.length-1, "content":[], "isEnd":false})
const reader = res.body.getReader() const reader = res.body.getReader()
const write = getWrite(reader) const write = getWrite(reader)
reader.read().then(write).then(()=> { reader.read().then(write).then(()=> {
@ -499,7 +499,8 @@ const getWrite = (reader) => {
let str = decoder.decode(value, { stream: true }); let str = decoder.decode(value, { stream: true });
// chunk // chunk
tempResult += str; tempResult += str;
const split = tempResult.match(/data:.*}\n\n/g); console.log(tempResult)
const split = tempResult.match(/data:.*}\r\n/g);
if (split) { if (split) {
str = split.join(''); str = split.join('');
tempResult = tempResult.replace(str, ''); tempResult = tempResult.replace(str, '');
@ -509,48 +510,40 @@ const getWrite = (reader) => {
// str "data:" // str "data:"
if (str && str.startsWith('data:')) { if (str && str.startsWith('data:')) {
split.forEach((chunkStr) => { split.forEach((chunkStr) => {
const chunk = JSON.parse(chunkStr.replace('data:', '')); const chunk = JSON.parse(chunkStr.replace('data:', '').trim());
if (chunk.docs && chunk.docs[0].length > 0){ if (chunk.docs && chunk.docs[0].length > 0){
// //
answerList.value[answerList.value.length - 1].content.push({"content":chunk.docs[0],"type":"docs"}) chatList.value[chatList.value.length - 1].content.push({"content":chunk.docs[0],"type":"docs"})
}else if (chunk.G6_ER && chunk.G6_ER.length > 0){ }else if (chunk.G6_ER && chunk.G6_ER.length > 0){
//ER //ER
answerList.value[answerList.value.length - 1].content.push({"content":chunk.G6_ER,"type":"G6_ER"}) chatList.value[chatList.value.length - 1].content.push({"content":chunk.G6_ER,"type":"G6_ER"})
}else if (chunk.html_image && chunk.html_image.length > 0){ }else if (chunk.html_image && chunk.html_image.length > 0){
//htmlecharts //htmlecharts
answerList.value[answerList.value.length - 1].content.push({"content":chunk.html_image,"type":"html_image"}) chatList.value[chatList.value.length - 1].content.push({"content":chunk.html_image,"type":"html_image"})
}else if (chunk.table && chunk.table.length > 0){ }else if (chunk.table && chunk.table.length > 0){
// //
answerList.value[answerList.value.length - 1].content.push({"content":chunk.table,"type":"table"}) chatList.value[chatList.value.length - 1].content.push({"content":chunk.table,"type":"table"})
}else { }else {
// //
let last_answer = answerList.value[answerList.value.length - 1].content let last_answer = chatList.value[chatList.value.length - 1].content
chunk.choices[0].delta.content = chunk.choices[0].delta.content.replace("\n","\n\n") chunk.choices[0].delta.content = chunk.choices[0].delta.content.replace("\n","\n\n")
if (last_answer.length > 0) { if (last_answer.length > 0) {
if(last_answer[last_answer.length - 1].type === 'text'){ if(last_answer[last_answer.length - 1].type === 'text'){
answerList.value[answerList.value.length-1].content[last_answer.length - 1].content += chunk.choices[0].delta.content chatList.value[chatList.value.length - 1].content[last_answer.length - 1].content += chunk.choices[0].delta.content
}else{ }else{
answerList.value[answerList.value.length - 1].content.push({"content":chunk.choices[0].delta.content,"type":"text"}) chatList.value[chatList.value.length - 1].content.push({"content":chunk.choices[0].delta.content,"type":"text"})
} }
}else{ }else{
answerList.value[answerList.value.length - 1].content.push({"content":chunk.choices[0].delta.content,"type":"text"}) chatList.value[chatList.value.length - 1].content.push({"content":chunk.choices[0].delta.content,"type":"text"})
} }
} }
let isStop = chatList.value[chatList.value.length-1].isStop
if (!isStop){
chatList.value[chatList.value.length - 1].content = answerList.value[answerList.value.length - 1].content
}
nextTick(() => { nextTick(() => {
// //
scrollDiv.value.setScrollTop(getMaxHeight()) scrollDiv.value.setScrollTop(getMaxHeight())
}) })
if (chunk.isEnd){ if (chunk.isEnd || chunk.is_end){
answerList.value[answerList.value.length - 1].isEnd = true
answerList.value[answerList.value.length - 1].time = formatDate(new Date())
if (!isStop){
chatList.value[chatList.value.length - 1].isEnd = true chatList.value[chatList.value.length - 1].isEnd = true
chatList.value[chatList.value.length - 1].time = formatDate(new Date()) chatList.value[chatList.value.length - 1].time = formatDate(new Date())
}
nextTick(() => { nextTick(() => {
// //
scrollDiv.value.setScrollTop(getMaxHeight()) scrollDiv.value.setScrollTop(getMaxHeight())

3
vue-fastapi-frontend/src/views/login.vue

@ -70,6 +70,7 @@ import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt"; import { encrypt, decrypt } from "@/utils/jsencrypt";
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import md5 from 'js-md5'; import md5 from 'js-md5';
import cache from "@/plugins/cache.js";
const userStore = useUserStore() const userStore = useUserStore()
const route = useRoute(); const route = useRoute();
@ -126,6 +127,8 @@ function handleLogin() {
} }
return acc; return acc;
}, {}); }, {});
cache.local.set("username",loginForm.value.username)
cache.local.set("password",md5(loginForm.value.password))
router.push({ path: redirect.value || "/", query: otherQueryParams }); router.push({ path: redirect.value || "/", query: otherQueryParams });
}).catch(() => { }).catch(() => {
loading.value = false; loading.value = false;

885
vue-fastapi-frontend/src/views/meta/metaInfo/index.vue

@ -0,0 +1,885 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<el-col :span="4" :xs="24">
<div class="head-container">
<el-input
v-model="database"
placeholder="请输入搜索系统名/模式名"
clearable
prefix-icon="Search"
style="margin-bottom: 20px"
/>
</div>
<div class="head-container">
<el-tree
:data="databaseList"
:props="{ label: 'name', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="databaseTreeRef"
node-key="id"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-col>
<el-col :span="20" :xs="24">
<el-form :model="queryParams" :inline="true" label-width="75px">
<el-form-item label="对象名称:" prop="userName">
<el-input
v-model="queryParams.tabName"
placeholder="请输入搜索表中文/英文名"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="列名称:">
<el-input
v-model="queryParams.colName"
placeholder="请输入搜索字段中文/英文名"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="对象类型:">
<el-select
v-model="queryParams.tabType"
placeholder="请输入数据类型"
clearable
style="width: 240px"
>
<el-option key="TABLE" value="TABLE" label="表"/>
<el-option key="VIEW" value="VIEW" label="视图"/>
<el-option key="PROCEDURE" value="PROCEDURE" label="存储过程"/>
<el-option key="FUNCTION" value="FUNCTION" label="函数"/>
</el-select>
</el-form-item>
<el-form-item label="标签名称:">
<el-input
v-model="queryParams.tagName"
placeholder="请输入搜索标签名称"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="补录状态:">
<el-input
v-model="queryParams.recStat"
placeholder="请输入搜索状态"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Download"
@click="exportData"
>导出</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="系统英文名" width="100" align="center" prop="ssysCd"></el-table-column>
<el-table-column label="模式名称" align="center" prop="mdlName"></el-table-column>
<el-table-column label="对象英文名" width="170" align="center" prop="tabEngName" >
<template #default="scope">
<el-link type="primary" @click="showColumnDialog(scope.row)" :underline="false">{{ scope.row.tabEngName }}</el-link>
</template>
</el-table-column>
<el-table-column label="对象中文名" width="100" align="center" prop="tabCnName"></el-table-column>
<el-table-column label="对象标签" width="250" align="center" prop="clasNames"></el-table-column>
<el-table-column label="记录数" width="80" align="center" prop="tabRecNum"></el-table-column>
<el-table-column label="补录对象名称" width="150" align="center" prop="tabCrrctName"></el-table-column>
<el-table-column label="补录对象描述" width="150" align="center" prop="tabDesc"></el-table-column>
<el-table-column label="对象类型" align="center" prop="tabType"></el-table-column>
<el-table-column label="对象治理标志" width="120" align="center" prop="govFlag"></el-table-column>
<el-table-column label="补录审批状态" width="120" align="center" prop="recStat"></el-table-column>
<el-table-column label="负责人" align="center" prop="pic"></el-table-column>
<el-table-column label="采集时间" width="200" align="center" prop="extractUpdTime">
<template #default="scope">
<span>{{scope.row.extractUpdTime.replace("T"," ")}}</span>
</template>
</el-table-column>
<el-table-column label="补录时间" align="center" prop="suppUpdTime"></el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-col>
</el-row>
<el-drawer v-model="drawer" size="80%" :show-close="false">
<template #header>
<h3>元数据信息</h3>
<div style="flex: none">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">提交</el-button>
</div>
</template>
<template #default>
<el-row :gutter="20">
<el-col :span="12" :xs="24">
<el-form :model="currentMetaData" :inline="true" label-width="120px">
<el-form-item label="对象英文名">
<el-input
v-model="currentMetaData.tabEngName"
disabled
/>
</el-form-item>
<el-form-item label="对象中文名">
<el-input
v-model="currentMetaData.tabCnName"
disabled
/>
</el-form-item>
<el-form-item label="所属系统">
<el-input
v-model="currentMetaData.ssysCd"
disabled
/>
</el-form-item>
<el-form-item label="所属SCHEMA">
<el-input
v-model="currentMetaData.mdlName"
disabled
/>
</el-form-item>
<el-form-item label="补录对象名称">
<el-input
v-model="currentMetaData.tabCrrctName"
placeholder="补录对象名称"
clearable
/>
</el-form-item>
<el-form-item label="补录对象描述">
<el-input
v-model="currentMetaData.tabDesc"
placeholder="请输入描述内容"
clearable
/>
</el-form-item>
<el-form-item label="对象治理标志">
<el-select
v-model="currentMetaData.govFlag"
placeholder="请输入数据类型"
clearable
style="width: 192px"
>
<el-option key="0" :value="'0'" label="是"/>
<el-option key="1" :value="'1'" label="否"/>
</el-select>
</el-form-item>
<el-form-item label="对象负责人">
<el-input
v-model="currentMetaData.pic"
placeholder="请输入搜索状态"
clearable
/>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" :xs="24">
<el-divider content-position="left">对象标签集合
<el-button icon="Plus" type="primary" text @click="openTableTagsDialog">新增</el-button>
</el-divider>
<el-tag style="margin-left: 10px;margin-top: 10px" v-for="tag in currentMetaData.tags" :key="tag.tagName +'-'+tag.tagValue" size="large" closable type="primary" @close="handleTableTagClose(tag)">
{{ tag.tagName }}
</el-tag>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-tabs v-model="activeColumnTab" type="border-card" class="full-height-tabs" @tab-change="changeColumnTab">
<el-tab-pane label="字段信息" name="column">
<div class="table-container">
<el-table :data="columnList" height="100%">
<el-table-column label="字段英文名" width="100" align="center" prop="fldEngName">
<template #default="scope">
<el-link type="primary" @click="openEditColumnDialog(scope.row,scope.$index)" :underline="false">{{ scope.row.fldEngName }}</el-link>
</template>
</el-table-column>
<el-table-column label="字段中文名" align="center" prop="fldCnName"></el-table-column>
<el-table-column label="字段类型" width="170" align="center" prop="fldType" ></el-table-column>
<el-table-column label="主键标志" width="100" align="center" prop="pkFlag">
<template #default="scope">
<span v-if="scope.row.pkFlag === '1' || scope.row.pkFlag === 'Y' || scope.row.pkFlag === true"></span>
<span v-else></span>
</template>
</el-table-column>
<!-- <el-table-column label="字段描述" width="250" align="center" prop="fldDesc"></el-table-column>-->
<el-table-column label="是否必填" width="70" align="center" prop="requireFlag">
<template #default="scope">
<span v-if="scope.row.requireFlag === '1' || scope.row.requireFlag === 'Y' || scope.row.requireFlag === true"></span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="是否索引" width="150" align="center" prop="idxFlag">
<template #default="scope">
<span v-if="scope.row.idxFlag === '1' || scope.row.idxFlag === 'Y' || scope.row.idxFlag === true"></span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="字段补录名" width="150" align="center" prop="fldCrrctName">
<template #default="scope">
<span v-if="oldColumnsData[scope.$index].fldCrrctName" style="color:#67C23A">{{scope.row.fldCrrctName}}</span>
<span v-else>{{scope.row.fldCrrctName}}</span>
</template>
</el-table-column>
<el-table-column label="补录主键" align="center" prop="crrctPkFlag">
<template #default="scope">
<span v-if="oldColumnsData[scope.$index].crrctPkFlag" style="color:#67C23A">{{scope.row.crrctPkFlag}}</span>
<span v-else>{{scope.row.crrctPkFlag}}</span>
</template>
</el-table-column>
<el-table-column label="补录字段描述" align="center" prop="fldDesc">
<template #default="scope">
<span v-if="oldColumnsData[scope.$index].fldDesc" style="color:#67C23A">{{scope.row.fldDesc}}</span>
<span v-else>{{scope.row.fldDesc}}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="120" align="center" prop="suppRecStat">
<template #default="scope">
<span v-if="oldColumnsData[scope.$index].suppRecStat" style="color:#67C23A">{{scope.row.suppRecStat}}</span>
<span v-else>{{scope.row.suppRecStat}}</span>
</template>
</el-table-column>
<el-table-column label="负责人" align="center" prop="pic">
<template #default="scope">
<span v-if="oldColumnsData[scope.$index].pic" style="color:#67C23A">{{scope.row.pic}}</span>
<span v-else>{{scope.row.pic}}</span>
</template>
</el-table-column>
<el-table-column label="字段标签" width="150" align="center" prop="fldClas">
<template #default="scope">
<el-tag v-for="item in JSON.parse(scope.row.fldClas)">{{item.tagName}}</el-tag>
</template>
</el-table-column>
<el-table-column label="字段空值率" align="center" prop="fldNullRate">
<template #default="scope">
<span v-if="oldColumnsData[scope.$index].fldNullRate" style="color:#67C23A">{{scope.row.fldNullRate}}</span>
<span v-else>{{scope.row.fldNullRate}}</span>
</template>
</el-table-column>
<el-table-column label="引用字典/标准" align="center" prop="suppUpdTime"></el-table-column>
<el-table-column label="更新时间" align="center" prop="suppUpdTime"></el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="业务关系" name="businessRelation">Config</el-tab-pane>
<el-tab-pane label="血缘关系" name="bloodRelation">Role</el-tab-pane>
<el-tab-pane label="存储过程" name="proc">
<SQLCodeMirror :data="procStr"></SQLCodeMirror>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
</template>
</el-drawer>
<el-dialog
v-model="tableTagDialog"
title="配置标签分类"
width="1100"
>
<el-form :model="currentTableTag" :inline="true" label-width="75px">
<el-form-item label="一级分类:" prop="userName">
<el-input
v-model="currentTableTag.clasPriClas"
disabled
style="width: 240px"
/>
</el-form-item>
<el-form-item label="二级分类:">
<el-input
v-model="currentTableTag.clasScdClas"
disabled
style="width: 240px"
/>
</el-form-item>
<el-form-item label="三级分类:">
<el-input
v-model="currentTableTag.clasThreClas"
disabled
style="width: 240px"
></el-input>
</el-form-item>
<el-form-item label="选择标签:">
<el-select
v-model="currentTableTag.clasOnum"
filterable
style="width: 240px"
@change="changeTableTag"
>
<el-option v-for="item in metaClasList" :key="item.clasOnum" :value="item.clasOnum" :label="item.clasOnum+' - '+item.clasName"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Plus" @click="addTableTag">新增</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="tableTags">
<el-table-column label="标签编号" width="80" align="center" prop="tagOnum"></el-table-column>
<el-table-column label="标签名称" width="120" align="center" prop="tagName"></el-table-column>
<el-table-column label="标签值" width="170" align="center" prop="tagValue" >
<template #default="scope">
<el-input v-model="scope.row.tagValue"></el-input>
</template>
</el-table-column>
<el-table-column label="标签值说明" width="170" align="center" prop="tagRemark">
<template #default="scope">
<el-input v-model="scope.row.tagRemark"></el-input>
</template>
</el-table-column>
<el-table-column label="标签状态" align="center" prop="tagStatus">
<template #default="scope">
<el-input v-model="scope.row.tagStatus"></el-input>
</template>
</el-table-column>
<el-table-column label="设置方式" width="80" align="center" prop="tagSource"></el-table-column>
<el-table-column label="设置对象" width="80" align="center" prop="setUser"></el-table-column>
<el-table-column label="设置时间" width="150" align="center" prop="setTime"></el-table-column>
<el-table-column label="操作" align="center" width="80">
<template #default="scope">
<el-button link type="primary" icon="Delete" @click.prevent="deleteTableTag(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button @click="tableTagDialog = false">取消</el-button>
<el-button type="primary" @click="confirmTableTags">
确定
</el-button>
</div>
</template>
</el-dialog>
<el-dialog
v-model="columnInfoDialog"
title="字段信息"
width="1100"
>
<el-form :model="currentColumnData" :inline="true" label-width="100px">
<el-form-item label="字段英文名">
<el-input disabled v-model="currentColumnData.fldEngName"></el-input>
</el-form-item>
<el-form-item label="字段中文名">
<el-input disabled v-model="currentColumnData.fldCnName"></el-input>
</el-form-item>
<el-form-item label="字段类型">
<el-input disabled v-model="currentColumnData.fldType"></el-input>
</el-form-item>
<el-form-item label="主键标志">
<template v-if="currentColumnData.pkFlag === '1' || currentColumnData.pkFlag === 'Y' || currentColumnData.pkFlag === true">
<el-input disabled :model-value="'是'"></el-input>
</template>
<template v-else>
<el-input disabled :model-value="'否'"></el-input>
</template>
</el-form-item>
<el-form-item label="是否必填">
<template v-if="currentColumnData.requireFlag === '1' || currentColumnData.requireFlag === 'Y' || currentColumnData.requireFlag === true">
<el-input disabled :model-value="'是'"></el-input>
</template>
<template v-else>
<el-input disabled :model-value="'否'"></el-input>
</template>
</el-form-item>
<el-form-item label="是否索引">
<template v-if="currentColumnData.idxFlag === '1' || currentColumnData.idxFlag === 'Y' || currentColumnData.idxFlag === true">
<el-input disabled :model-value="'是'"></el-input>
</template>
<template v-else>
<el-input disabled :model-value="'否'"></el-input>
</template>
</el-form-item>
<el-form-item label="字段补录名">
<el-input v-model="currentColumnData.fldCrrctName"></el-input>
</el-form-item>
<el-form-item label="补录字段描述">
<el-input v-model="currentColumnData.fldDesc"/>
</el-form-item>
<el-form-item label="补录主键">
<div style="width: 192px">
<el-switch v-model="currentColumnData.crrctPkFlag"/>
</div>
</el-form-item>
<el-form-item label="状态">
<el-input v-model="currentColumnData.suppRecStat"/>
</el-form-item>
<el-form-item label="负责人">
<el-input v-model="currentColumnData.pic"/>
</el-form-item>
<el-form-item label="字段空值率">
<el-input v-model="currentColumnData.fldNullRate"/>
</el-form-item>
</el-form>
<el-form :model="currentColumnTag" :inline="true" label-width="100px">
<el-form-item label="一级分类:" prop="userName">
<el-input
v-model="currentColumnTag.clasPriClas"
disabled
/>
</el-form-item>
<el-form-item label="二级分类:">
<el-input
v-model="currentColumnTag.clasScdClas"
disabled
/>
</el-form-item>
<el-form-item label="三级分类:">
<el-input
v-model="currentColumnTag.clasThreClas"
disabled
></el-input>
</el-form-item>
<el-form-item label="选择标签:">
<el-select
v-model="currentColumnTag.clasOnum"
filterable
style="width: 192px"
@change="changeColumnTag"
>
<el-option v-for="item in metaClasList" :key="item.clasOnum" :value="item.clasOnum" :label="item.clasOnum+' - '+item.clasName"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Plus" @click="addColumnTag">新增</el-button>
</el-form-item>
</el-form>
<el-table :data="currentColumnData.fldClas">
<el-table-column label="标签编号" width="80" align="center" prop="tagOnum"></el-table-column>
<el-table-column label="标签名称" width="120" align="center" prop="tagName"></el-table-column>
<el-table-column label="标签值" width="170" align="center" prop="tagValue" >
<template #default="scope">
<el-input v-model="scope.row.tagValue"></el-input>
</template>
</el-table-column>
<el-table-column label="标签值说明" width="170" align="center" prop="tagRemark">
<template #default="scope">
<el-input v-model="scope.row.tagRemark"></el-input>
</template>
</el-table-column>
<el-table-column label="标签状态" align="center" prop="tagStatus">
<template #default="scope">
<el-input v-model="scope.row.tagStatus"></el-input>
</template>
</el-table-column>
<el-table-column label="设置方式" width="80" align="center" prop="tagSource"></el-table-column>
<el-table-column label="设置对象" width="80" align="center" prop="setUser"></el-table-column>
<el-table-column label="设置时间" width="150" align="center" prop="setTime"></el-table-column>
<el-table-column label="操作" align="center" width="80">
<template #default="scope">
<el-button link type="primary" icon="Delete" @click.prevent="deleteColumnTag(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button @click="columnInfoDialog = false">取消</el-button>
<el-button type="primary" @click="confirmColumn">
确定
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Meta">
import {getDataSourceList, getMetaDataList, getColumnList, getMetaClasList, postMetaSupp} from "@/api/meta/metaInfo.js"
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
import SQLCodeMirror from "@/components/codemirror/SQLCodeMirror.vue";
import cache from "@/plugins/cache.js";
const data = reactive({
queryParams:{
ssysCd:'',
mdlName:'',
tabName:'',
colName:'',
tabType:'',
tagName:'',
recStat:'',
pageNum:1,
pageSize:10
},
currentMetaData: {
tabEngName:'',
tabCnName:'',
ssysCd:'',
mdlName:'',
tabCrrctName:'',
tabDesc:'',
govFlag:null,
pic:'',
tags:[]
}
})
const { queryParams, currentMetaData } = toRefs(data);
const loading = ref(true);
const drawer = ref(false);
const dataList = ref([]);
const columnList = ref([]);
const tableTagDialog = ref(false);
const metaClasList = ref([]);
const columnInfoDialog = ref(false);
const currentColumnData = ref({});
const oldColumnsData = ref([]);
const currentTableTag = ref({
"clasThreClas": "",
"clasOnum": null,
"clasName": "",
"recSubmPrsn": "",
"clasPriClas": "",
"clasScdClas": ""
});
const currentColumnTag = ref({
"clasThreClas": "",
"clasOnum": null,
"clasName": "",
"recSubmPrsn": "",
"clasPriClas": "",
"clasScdClas": ""
});
const columnParam = ref({
total:0,
pageNum:1,
pageSize:10
});
const total = ref(0);
const databaseList = ref([]);
const tableTags = ref([]);
const database = ref("");
const procStr = ref("");
const activeColumnTab = ref("column");
const { proxy } = getCurrentInstance();
const changedColumns = ref([])
function changeColumnTab(){
if (activeColumnTab.value === 'proc'){
procStr.value = "--基金量化产品监管报送存储过程:ADS.SP_ADS_SAC_QNTPRD_ALL\n" +
"CREATE OR REPLACE PROCEDURE ADS.SP_ADS_SAC_QNTPRD_ALL(I_BUSI_DATE VARCHAR2, O_RET_CODE OUT NUMBER, O_RET_MSG OUT VARCHAR2) AS V_STATUS NUMBER DEFAULT 0; --状态,0:成功,-1失败 \n V_ETL_NAME VARCHAR2(50) DEFAULT 'ADS.SP_ADS_SAC_QNTPRD_ALL'; V_ETL_NAME_CN VARCHAR2(100) DEFAULT '量化产品监管报送报表总调度'; ----ETL中文名称 V_ETL_TYPE NUMBER DEFAULT 1; --ETL类型,0:采集,1:转换 V_START_TIME DATE DEFAULT SYSDATE; --开始时间 V_MSG VARCHAR2(1000) DEFAULT '成功'; ----ETL信息 V_SOURCE_TABLE VARCHAR2(200) DEFAULT 'ADS.SP_ADS_SAC_QNTPRD_DTL,ADS.SP_ADS_SAC_QNTPRD_AGGR'; V_DEST_TABLE VARCHAR2(200) DEFAULT 'ADS.T_ADS_SAC_QNTPRD_DTL_M_M_OC,ADS.T_ADS_SAC_QNTPRD_AGGR_M_M_OC'; V_SRC_CNT NUMBER DEFAULT 0; --源数据量 V_DEST_CNT NUMBER DEFAULT 0; --目标新增数据量 V_CNT NUMBER; V_SOURCE_CODE VARCHAR2(4) ; V_DATE DATE DEFAULT TO_DATE(I_BUSI_DATE,'YYYY-MM-DD'); --每月第一个交易日 V_RET_CODE NUMBER; V_RET_MSG VARCHAR2(4000); BEGIN O_RET_CODE := 0; O_RET_MSG := 'SUCCESS'; --每个月第一个交易日更新数据 SELECT T.TM_SH_FST_TRDY_FLAG INTO V_CNT FROM DWS.T_DWS_DIM_DATE_OC T WHERE T.D_DATE= V_DATE; IF V_CNT = 0 THEN RETURN; END IF; --加载明细表数据 ADS.SP_ADS_SAC_QNTPRD_DTL (I_BUSI_DATE => I_BUSI_DATE, --每月第一个交易日 O_RET_CODE => V_RET_CODE, O_RET_MSG => V_RET_MSG ) ; IF V_RET_CODE <> 0 THEN O_RET_CODE := V_RET_CODE; O_RET_MSG := V_RET_MSG; RETURN; END IF; --加载汇总表数据 ADS.SP_ADS_SAC_QNTPRD_AGGR(I_BUSI_DATE => I_BUSI_DATE, --每月第一个交易日 O_RET_CODE => V_RET_CODE, O_RET_MSG => V_RET_MSG ) ; IF V_RET_CODE <> 0 THEN O_RET_CODE := V_RET_CODE; O_RET_MSG := V_RET_MSG; RETURN; END IF; --日志:判断是否源表目标表的数量是否相同,如不相同则修改日志校验MSG --日志:写日志 DWS.SP_DWT_LOG(I_ETL_NAME => V_ETL_NAME, I_ETL_NAME_CN => V_ETL_NAME_CN, I_ETL_TYPE => V_ETL_TYPE, I_START_TIME => V_START_TIME, I_END_TIME => SYSDATE, I_STATUS => V_STATUS, I_MSG => V_MSG, I_SOURCE_TABLES => V_SOURCE_TABLE, I_DEST_TABLES => V_DEST_TABLE, I_SRC_CNT => V_SRC_CNT, I_DEST_CNT => V_DEST_CNT, I_VERI_SRC_CNT => NULL, I_VERI_DEST_CNT => NULL, I_VERI_MSG => NULL, I_VERI_STATUS => NULL, I_BUSI_DATE => I_BUSI_DATE); EXCEPTION WHEN OTHERS THEN O_RET_CODE := -1; O_RET_MSG := SQLCODE || ',' || SQLERRM; ROLLBACK; V_STATUS := -1; V_MSG := O_RET_MSG; DWS.SP_DWT_LOG(I_ETL_NAME => V_ETL_NAME, I_ETL_NAME_CN => V_ETL_NAME_CN, I_ETL_TYPE => V_ETL_TYPE, I_START_TIME => V_START_TIME, I_END_TIME => SYSDATE, I_STATUS => V_STATUS, I_MSG => V_MSG, I_SOURCE_TABLES => V_SOURCE_TABLE, I_DEST_TABLES => V_DEST_TABLE, I_SRC_CNT => V_SRC_CNT, I_DEST_CNT => V_DEST_CNT, I_VERI_SRC_CNT => NULL, I_VERI_DEST_CNT => NULL, I_VERI_MSG => NULL, I_VERI_STATUS => NULL, I_BUSI_DATE => I_BUSI_DATE); END;"
}
}
function confirmTableTags(){
currentMetaData.value.tags = JSON.parse(JSON.stringify(tableTags.value))
tableTagDialog.value = false
}
function confirmColumn(){
for (let i = 0; i < columnList.value.length; i++) {
if (columnList.value[i].fldEngName === currentColumnData.value.fldEngName){
if (columnList.value[i].fldCrrctName !== currentColumnData.value.fldCrrctName){
columnList.value[i].fldCrrctName = currentColumnData.value.fldCrrctName
oldColumnsData.value[i].fldCrrctName = true
}
if (columnList.value[i].fldDesc !== currentColumnData.value.fldDesc){
columnList.value[i].fldDesc = currentColumnData.value.fldDesc
oldColumnsData.value[i].fldDesc = true
}
if (columnList.value[i].crrctPkFlag !== currentColumnData.value.crrctPkFlag){
columnList.value[i].crrctPkFlag = currentColumnData.value.crrctPkFlag
oldColumnsData.value[i].crrctPkFlag = true
}
if (columnList.value[i].suppRecStat !== currentColumnData.value.suppRecStat){
columnList.value[i].suppRecStat = currentColumnData.value.suppRecStat
oldColumnsData.value[i].suppRecStat = true
}
if (columnList.value[i].pic !== currentColumnData.value.pic){
columnList.value[i].pic = currentColumnData.value.pic
oldColumnsData.value[i].pic = true
}
if (columnList.value[i].fldNullRate !== currentColumnData.value.fldNullRate){
columnList.value[i].fldNullRate = currentColumnData.value.fldNullRate
oldColumnsData.value[i].fldNullRate = true
}
if (columnList.value[i].fldClas !== null && columnList.value[i].fldClas !== ''){
if (JSON.parse(columnList.value[i].fldClas) !== currentColumnData.value.fldClas){
columnList.value[i].fldClas = JSON.stringify(currentColumnData.value.fldClas)
oldColumnsData.value[i].fldClas = true
}
}else {
if (currentColumnData.value.fldClas.length > 0){
columnList.value[i].fldClas = JSON.stringify(currentColumnData.value.fldClas)
oldColumnsData.value[i].fldClas = true
}
}
}
}
columnInfoDialog.value = false
changedColumns.value.push(currentColumnData.value)
}
function addTableTag(){
let json = JSON.parse(JSON.stringify(currentTableTag.value))
if (json.clasOnum !== null){
tableTags.value.push({
tagOnum: json.clasOnum,
tagName: json.clasName,
tagValue: '',
tagRemark:'',
tagStatus:'',
tagSource:'手工',
setUser: cache.local.get("username"),
setTime: getNowDateTime()
})
} else {
proxy.$modal.msgWarning("请选择一个标签");
}
}
function addColumnTag(){
let json = JSON.parse(JSON.stringify(currentColumnTag.value))
if (json.clasOnum !== null){
currentColumnData.value.fldClas.push({
tagOnum: json.clasOnum,
tagName: json.clasName,
tagValue: '',
tagRemark:'',
tagStatus:'',
tagSource:'手工',
setUser: cache.local.get("username"),
setTime: getNowDateTime()
})
} else {
proxy.$modal.msgWarning("请选择一个标签");
}
}
function getNowDateTime(){
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
function deleteTableTag(index){
tableTags.value.splice(index,1)
}
function deleteColumnTag(index){
currentColumnData.value.fldClas.splice(index,1)
}
function changeTableTag(){
if (typeof currentTableTag.value.clasOnum !== null){
for (let i = 0; i <metaClasList.value.length; i++) {
let item = metaClasList.value[i]
if (item.clasOnum === currentTableTag.value.clasOnum){
currentTableTag.value = item
}
}
}
}
function changeColumnTag(){
if (typeof currentColumnTag.value.clasOnum !== null){
for (let i = 0; i <metaClasList.value.length; i++) {
let item = metaClasList.value[i]
if (item.clasOnum === currentColumnTag.value.clasOnum){
currentColumnTag.value = item
}
}
}
}
function handleTableTagClose(tag){
for (let i = 0; i < currentMetaData.value.tags.length; i++) {
if (tag.tagName === currentMetaData.value.tags[i].tagName && tag.tagValue === currentMetaData.value.tags[i].tagValue){
currentMetaData.value.tags.splice(i, 1);
}
}
}
function openTableTagsDialog(){
tableTagDialog.value = true
currentTableTag.value = {
"clasThreClas": "",
"clasOnum": null,
"clasName": "",
"recSubmPrsn": "",
"clasPriClas": "",
"clasScdClas": ""
}
tableTags.value = JSON.parse(JSON.stringify(currentMetaData.value.tags))
getMetaClasList().then(res=>{
metaClasList.value = res.data
})
}
function openEditColumnDialog(row,index){
columnInfoDialog.value = true
currentColumnData.value = JSON.parse(JSON.stringify(row))
currentColumnTag.value = {
"clasThreClas": "",
"clasOnum": null,
"clasName": "",
"recSubmPrsn": "",
"clasPriClas": "",
"clasScdClas": ""
}
currentColumnData.value.fldClas = row.fldClas === null?[]:JSON.parse(row.fldClas)
getMetaClasList().then(res=>{
metaClasList.value = res.data
})
}
function showColumnDialog(row) {
let tableData = JSON.parse(JSON.stringify(row))
currentMetaData.value = {
tabEngName: tableData.tabEngName,
tabCnName: tableData.tabCnName,
ssysCd: tableData.ssysCd,
mdlName:tableData.mdlName,
tabCrrctName:tableData.tabCrrctName,
tabDesc:tableData.tabDesc,
govFlag:tableData.govFlag,
pic:tableData.pic,
tags: tableData.tabClas? JSON.parse(tableData.tabClas):[]
}
let columnData = {
tabName: row.tabEngName,
ssysCd: row.ssysCd,
mdlName: row.mdlName
}
getColumnList(columnData).then(res=>{
columnList.value = res.data
oldColumnsData.value = []
for (let i = 0; i < columnList.value.length; i++) {
oldColumnsData.value.push({
fldCrrctName:false,
crrctPkFlag:false,
fldDesc:false,
pic:false,
})
}
})
drawer.value = true
changedColumns.value = []
}
function getDatabaseList() {
let query = {
userName:cache.local.get("username"),
password:cache.local.get("password")
}
getDataSourceList(query).then(res=>{
databaseList.value = [{ id: 100, name: "数据源", parentId: 0, children: res.data.totalList.map(element => ({ ...element, parentId: "100" })) }]
})
}
function handleQuery(){
queryParams.value.pageNum = 1;
getList()
}
function resetQuery(){
queryParams.value ={
ssysCd:'',
mdlName:'',
tabName:'',
colName:'',
tabType:'',
tagName:'',
recStat:'',
pageNum:1,
pageSize:10
}
proxy.$refs.databaseTreeRef.setCurrentKey(null);
handleQuery()
}
function handleSelectionChange(){
}
function getList(){
getMetaDataList(queryParams.value).then(res=>{
dataList.value = res.data.rows
loading.value = false
total.value = res.data.total
})
}
function exportData(){
}
const filterNode = (value, data) => {
if (!value) return true;
return data.name.indexOf(value) !== -1;
};
function handleNodeClick(data) {
queryParams.value.ssysCd = data.name;
handleQuery();
}
watch(database, val => {
proxy.$refs["databaseTreeRef"].filter(val);
});
function submit(){
let data = {
ssysCd: currentMetaData.value.ssysCd,
mdlName: currentMetaData.value.mdlName,
tabEngName: currentMetaData.value.tabEngName,
tabCrrctName: currentMetaData.value.tabCrrctName,
tabDesc: currentMetaData.value.tabDesc,
pic: currentMetaData.value.pic,
govFlag: currentMetaData.value.govFlag,
tabClas: JSON.stringify(currentMetaData.value.tags),
columnInfo: []
}
if (changedColumns.value.length > 0){
for (let i = 0; i < changedColumns.value.length; i++) {
let column = changedColumns.value[i]
data.columnInfo.push({
fldEngName: column.fldEngName,
fldCrrctName: column.fldCrrctName,
crrctPkFlag: column.crrctPkFlag,
fldDesc: column.fldDesc,
pic: column.pic,
fldClas: JSON.stringify(column.fldClas),
fldNullRate: column.fldNullRate,
recStat: column.recStat
})
}
}
postMetaSupp(data).then(res=>{
proxy.$modal.msgSuccess("操作成功");
drawer.value = false
getList()
})
}
function cancel(){
drawer.value = false
}
onMounted(()=>{
getDatabaseList()
handleQuery()
})
</script>
<style scoped lang="scss">
:deep(.el-drawer__header) {
margin-bottom: 0 !important;
}
.full-height-tabs {
height: calc(100vh - 320px);
}
:deep(.el-tabs__content) {
height: 100%;
overflow: auto;
}
.table-container {
height: 100%;
overflow: auto;
}
</style>

11
vue-fastapi-frontend/src/views/system/flow/conf.vue

@ -0,0 +1,11 @@
<template>
<div class="app-container">
<span>你好</span>
</div>
</template>
<script setup>
</script>
<style scoped lang="scss">
</style>

140
vue-fastapi-frontend/src/views/system/flow/index.vue

@ -0,0 +1,140 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="申请人" prop="applicant">
<el-input
v-model="queryParams.applicant"
placeholder="请输入申请人"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="业务类型" prop="businessType">
<el-select
v-model="queryParams.businessType"
placeholder="业务类型"
clearable
style="width: 240px"
>
<el-option
key="t_metadata_supp_info"
label="元数据信息补录"
value="t_metadata_supp_info"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="flowList">
<el-table-column label="业务类型" align="center" prop="businessType">
<template #default="scope">
<span v-if="scope.row.businessType === 't_metadata_supp_info'">元数据信息补录</span>
</template>
</el-table-column>
<el-table-column label="业务编号" align="center" prop="businessId" :show-overflow-tooltip="true" width="280">
<template #default="scope">
<el-link type="primary" @click="showBusinessDataDialog(scope.row)" :underline="false">{{ scope.row.businessId }}</el-link>
</template>
</el-table-column>
<el-table-column label="申请人" align="center" prop="applicant" :show-overflow-tooltip="true" />
<el-table-column label="申请时间" align="center" prop="applyTime" :show-overflow-tooltip="true" />
<el-table-column label="当前状态" align="center" prop="configType">
<template #default="scope">
<span v-if="scope.row.status === 'waiting'">未审批</span>
<span v-if="scope.row.status === 'pending'">未审批</span>
<span v-if="scope.row.status === 'succeed'">已审批</span>
<span v-if="scope.row.status === 'rejected'">已驳回</span>
</template>
</el-table-column>
<el-table-column label="下一步审批人" align="center" prop="approver" :show-overflow-tooltip="true" >
<template #default="scope">
<el-link type="primary" @click="showFlowConfig(scope.row)" :underline="false">{{ scope.row.approver }}</el-link>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="success" icon="Select" @click="agree(scope.row)">同意</el-button>
<el-button link type="danger" icon="CloseBold" @click="reject(scope.row)">驳回</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup>
import {getApprovalList, operateProcess } from "@/api/flow/flow.js"
const { proxy } = getCurrentInstance();
import { ref, nextTick, computed, watch, reactive, onMounted } from 'vue'
import {getWaitingFlowCount} from "../../../api/flow/flow.js";
import cache from "../../../plugins/cache.js";
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
applicant: undefined,
businessType: undefined,
},
});
const total = ref(0);
const { queryParams } = toRefs(data);
const flowList = ref([]);
/** 查询参数列表 */
function getList() {
getApprovalList(queryParams.value).then(res=>{
flowList.value = res.data.rows
total.value = res.data.total
})
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
function resetQuery(){
queryParams.value = {
pageNum: 1,
pageSize: 10,
applicant: undefined,
businessType: undefined,
}
getList()
}
function showBusinessDataDialog(row){
}
function showFlowConfig(row){
}
function agree(row){
let data = {
flowId: row.id,
operateType: 'success',
operateComment: ''
}
operateProcess(data).then(res=>{
proxy.$modal.msgSuccess("操作成功");
getWaitingFlowCount().then(res=>{
cache.local.set("waitingTotal",res.data)
})
})
}
function reject(row){
}
onMounted(()=>{
handleQuery()
})
</script>

3
vue-fastapi-frontend/vite.config.js

@ -36,7 +36,8 @@ export default defineConfig(({ mode, command }) => {
rewrite: (p) => p.replace(/^\/default-api/, '') rewrite: (p) => p.replace(/^\/default-api/, '')
}, },
'/aichat-api': { '/aichat-api': {
target: 'http://127.0.0.1:8000', target: 'http://47.121.207.11:7861',
// target: 'http://127.0.0.1:8000',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/aichat-api/, '') rewrite: (p) => p.replace(/^\/aichat-api/, '')
}, },

Loading…
Cancel
Save