si@aidatagov.com 2 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. 47
      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_name: str = 'RuoYi-FasAPI'
app_root_path: str = '/dev-api'
app_root_path: str = '/default-api'
app_host: str = '0.0.0.0'
app_port: int = 9099
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 config.database import Base
from datetime import datetime
from sqlalchemy.dialects.mysql import LONGTEXT
class AiChatHistory(Base):
@ -17,7 +18,7 @@ class AiChatHistory(Base):
isStop = Column(Boolean, default=None, comment='是否停止')
user = Column(Integer, 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='点赞,差评等操作')
thumbDownReason = Column(String(255), default=None, comment='差评原因')
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.aichat_controller import aichatController
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.approval_controller import approvalController
from sub_applications.handle import handle_sub_applications
from utils.common_util import worship
from utils.log_util import logger
@ -83,6 +85,8 @@ controller_list = [
{'router': metataskController, 'tags': ['元数据管理-元数据任务模块']},
{'router': metaSecurityController, 'tags': ['元数据管理-数据安全']},
{'router': aichatController, 'tags': ['智能问答模块']},
{'router': metaController, 'tags': ['元数据信息模块']},
{'router': approvalController, 'tags': ['审批模块']},
]
for controller in controller_list:

2
vue-fastapi-frontend/package.json

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

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

@ -8,7 +8,7 @@ export function listChatHistory(sessionId) {
sessionId = '0'
}
return request({
url: '/aichat/session/list/'+sessionId,
url: '/default-api/aichat/session/list/'+sessionId,
method: 'get',
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
const currentIndex = ref(null);
//
const hideList = ['/index', '/user/profile'];
const hideList = ['/index', '/user/profile', 'system/flow/index'];
const appStore = useAppStore()
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'">
<header-search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
<el-tooltip content="我的流程" effect="dark" placement="bottom">
<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 content="文档地址" effect="dark" placement="bottom">
@ -55,16 +60,20 @@ import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
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 useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
import cache from "@/plugins/cache";
import {getWaitingFlowCount} from "@/api/flow/flow.js"
const appStore = useAppStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()
const totalFlow = ref(cache.local.get("waitingTotal") || 0)
function toggleSideBar() {
appStore.toggleSideBar()
}
@ -98,6 +107,24 @@ const emits = defineEmits(['setLayout'])
function 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>
<style lang='scss' scoped>

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

@ -83,6 +83,20 @@ export const constantRoutes = [
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 defAva from '@/assets/images/profile.jpg'
import md5 from 'js-md5'
import cache from "@/plugins/cache.js";
const useUserStore = defineStore(
'user',
@ -64,6 +66,8 @@ const useUserStore = defineStore(
this.roles = []
this.permissions = []
removeToken()
cache.local.remove('username')
cache.local.remove('password')
resolve()
}).catch(error => {
reject(error)

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

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

47
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 {getToken} from "@/utils/auth.js";
import {postChatMessage} from "@/api/aichat/aichat.js"
import cache from "@/plugins/cache.js";
import Cookies from "js-cookie";
import {addChat} from "@/api/aichat/aichat";
@ -337,9 +338,9 @@ function regenerationChart(index){
chatList.value.push(chat)
let data = {
"query": chat.content,
"user_id": Cookies.get("username"),
"user_id": cache.local.get("username"),
"robot": currentMachine.value.length>0?currentMachine.value[0]:"",
"sessionId": props.sessionId,
"session_id": Cookies.get("chatSessionId"),
"doc": chat.file,
"history": []
}
@ -435,15 +436,15 @@ async function sendChatHandle(event) {
//
scrollDiv.value.setScrollTop(getMaxHeight())
})
inputValue.value = ''
let data = {
"query": inputValue.value.trim(),
"user_id": Cookies.get("username"),
"user_id": cache.local.get("username"),
"robot": currentMachine.value.length > 0 ? currentMachine.value[0] : "",
"sessionId": Cookies.get("chatSessionId"),
"session_id": Cookies.get("chatSessionId"),
"doc": currentFiles.value,
"history": []
}
inputValue.value = ''
sendChatMessage(data)
}
} else {
@ -460,7 +461,6 @@ function sendChatMessage(data){
}else {
currentFiles.value = []
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 write = getWrite(reader)
reader.read().then(write).then(()=> {
@ -499,7 +499,8 @@ const getWrite = (reader) => {
let str = decoder.decode(value, { stream: true });
// chunk
tempResult += str;
const split = tempResult.match(/data:.*}\n\n/g);
console.log(tempResult)
const split = tempResult.match(/data:.*}\r\n/g);
if (split) {
str = split.join('');
tempResult = tempResult.replace(str, '');
@ -509,48 +510,40 @@ const getWrite = (reader) => {
// str "data:"
if (str && str.startsWith('data:')) {
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){
//
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){
//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){
//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){
//
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 {
//
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")
if (last_answer.length > 0) {
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{
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{
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(() => {
//
scrollDiv.value.setScrollTop(getMaxHeight())
})
if (chunk.isEnd){
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].time = formatDate(new Date())
}
if (chunk.isEnd || chunk.is_end){
chatList.value[chatList.value.length - 1].isEnd = true
chatList.value[chatList.value.length - 1].time = formatDate(new Date())
nextTick(() => {
//
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 useUserStore from '@/store/modules/user';
import md5 from 'js-md5';
import cache from "@/plugins/cache.js";
const userStore = useUserStore()
const route = useRoute();
@ -126,6 +127,8 @@ function handleLogin() {
}
return acc;
}, {});
cache.local.set("username",loginForm.value.username)
cache.local.set("password",md5(loginForm.value.password))
router.push({ path: redirect.value || "/", query: otherQueryParams });
}).catch(() => {
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/, '')
},
'/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,
rewrite: (p) => p.replace(/^\/aichat-api/, '')
},

Loading…
Cancel
Save