元数据和数据质量功能迁入
Showing
32 changed files
with
15189 additions
and
4 deletions
This diff could not be displayed because it is too large.
src/api/modules/dataMetaService.ts
0 → 100644
| 1 | import request from "@/utils/request"; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * 元数据-采集任务 | ||
| 5 | **/ | ||
| 6 | // 新增 | ||
| 7 | export const addMetaDataTask = (params) => request({ | ||
| 8 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/add`, | ||
| 9 | method: 'post', | ||
| 10 | data: params | ||
| 11 | }) | ||
| 12 | // 删除 | ||
| 13 | export const deleteMetaDataTask = (params) => request({ | ||
| 14 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/del`, | ||
| 15 | method: 'delete', | ||
| 16 | data: params | ||
| 17 | }) | ||
| 18 | // 分页查询 | ||
| 19 | export const getMetaDataTask = (params) => request({ | ||
| 20 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/page-list`, | ||
| 21 | method: 'post', | ||
| 22 | data: params | ||
| 23 | }) | ||
| 24 | // 修改 | ||
| 25 | export const updateMetaDataTask = (params) => request({ | ||
| 26 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/update`, | ||
| 27 | method: 'put', | ||
| 28 | data: params | ||
| 29 | }) | ||
| 30 | // 详情 | ||
| 31 | export const getMetaDataTaskDetail = (params) => request({ | ||
| 32 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/detail/${params}`, | ||
| 33 | method: 'get' | ||
| 34 | }) | ||
| 35 | // 上线下线 | ||
| 36 | export const updateMetaDataState = (params) => request({ | ||
| 37 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/change-state`, | ||
| 38 | method: 'get', | ||
| 39 | params | ||
| 40 | }) | ||
| 41 | // 名称唯一性验证 | ||
| 42 | export const checkMetaDataTask = (params) => request({ | ||
| 43 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/check-exist`, | ||
| 44 | method: 'post', | ||
| 45 | data: { | ||
| 46 | collectTaskName: params, | ||
| 47 | } | ||
| 48 | }) | ||
| 49 | // 执行元数据采集任务 | ||
| 50 | export const executeMetaDataTask = (params) => request({ | ||
| 51 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/execute/${params}`, | ||
| 52 | method: 'get' | ||
| 53 | }) | ||
| 54 | // 执行日志 | ||
| 55 | export const getMetaDataTaskLog = (params) => request({ | ||
| 56 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-exec/page-list`, | ||
| 57 | method: 'post', | ||
| 58 | data: params | ||
| 59 | }) | ||
| 60 | // | ||
| 61 | export const saveMetaReportAnalysis = (params) => request({ | ||
| 62 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage-analysis-report/add`, | ||
| 63 | method: 'post', | ||
| 64 | data: params | ||
| 65 | }) | ||
| 66 | /** | ||
| 67 | * 元数据-元数据查询 | ||
| 68 | **/ | ||
| 69 | // 树形数据 | ||
| 70 | export const getMetaTreeData = (params) => request({ | ||
| 71 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-tree-list`, | ||
| 72 | method: 'post', | ||
| 73 | data: params | ||
| 74 | }) | ||
| 75 | // 数据库汇总信息 | ||
| 76 | export const getMetaDatabaseCollect = (params) => request({ | ||
| 77 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-database-collect-list`, | ||
| 78 | method: 'post', | ||
| 79 | data: params | ||
| 80 | }) | ||
| 81 | // 库分页查询 | ||
| 82 | export const getMetaDataBase = (params) => request({ | ||
| 83 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-table-collect-list`, | ||
| 84 | method: 'post', | ||
| 85 | data: params | ||
| 86 | }) | ||
| 87 | // 表分页查询 | ||
| 88 | export const getMetaDataSheet = (params) => request({ | ||
| 89 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-table-detail-list`, | ||
| 90 | method: 'post', | ||
| 91 | data: params | ||
| 92 | }) | ||
| 93 | // 表字段查询 | ||
| 94 | export const getMetaSheetField = (params) => request({ | ||
| 95 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-table-field-list`, | ||
| 96 | method: 'post', | ||
| 97 | params | ||
| 98 | }) | ||
| 99 | // 表索引查询 | ||
| 100 | export const getMetaSheetKeys = (params) => request({ | ||
| 101 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-table-index-list`, | ||
| 102 | method: 'post', | ||
| 103 | params | ||
| 104 | }) | ||
| 105 | // 变更查询 | ||
| 106 | export const getMetaChange = (params) => request({ | ||
| 107 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-collect-change-list`, | ||
| 108 | method: 'post', | ||
| 109 | data: params | ||
| 110 | }) | ||
| 111 | // 变更明细 | ||
| 112 | export const getMetaChangeRecord = (params) => request({ | ||
| 113 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-change-record-list`, | ||
| 114 | method: 'post', | ||
| 115 | data: params | ||
| 116 | }) | ||
| 117 | // | ||
| 118 | export const getMetacompareList = (params) => request({ | ||
| 119 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-change-compare-list/${params}`, | ||
| 120 | method: 'get', | ||
| 121 | //data: params | ||
| 122 | }) | ||
| 123 | |||
| 124 | // 表信息详情 | ||
| 125 | export const getMetaDetail = (params) => request({ | ||
| 126 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/detail/${params}`, | ||
| 127 | method: 'get', | ||
| 128 | }) | ||
| 129 | |||
| 130 | /** 根据表获取血缘数据 */ | ||
| 131 | export const getTableLineage = (params) => request({ | ||
| 132 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage/lineage-query?guid=${params.guid}&lineageType=tb`, | ||
| 133 | method: 'get', | ||
| 134 | }) | ||
| 135 | |||
| 136 | /** 根据字段获取血缘数据 */ | ||
| 137 | export const getTableFieldLineage = (params) => request({ | ||
| 138 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage/lineage-query?guid=${params.guid}&lineageType=co`, | ||
| 139 | method: 'get', | ||
| 140 | }) | ||
| 141 | |||
| 142 | /** 获取表的所有字段血缘数据 */ | ||
| 143 | export const getTableAllFieldLineage = (params) => request({ | ||
| 144 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage/lineage-query-field?databaseName=${params.databaseName}&tableName=${params.tableName}`, | ||
| 145 | method: 'get', | ||
| 146 | }) | ||
| 147 | |||
| 148 | // 查询列表 | ||
| 149 | export const getMetaList = (params) => request({ | ||
| 150 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/list-meta-all`, | ||
| 151 | method: 'post', | ||
| 152 | data:params | ||
| 153 | }) | ||
| 154 | // 元数据表字段查询 | ||
| 155 | export const getMetaTableField = (params) => request({ | ||
| 156 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/meta-table-field-list`, | ||
| 157 | method: 'post', | ||
| 158 | params, | ||
| 159 | }) | ||
| 160 | // 保存血缘字段节点 | ||
| 161 | export const saveLineageField = (params) => request({ | ||
| 162 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage/save-field`, | ||
| 163 | method: 'post', | ||
| 164 | data:params | ||
| 165 | }) | ||
| 166 | // 保存血源节点 | ||
| 167 | export const saveLineageTable = (params) => request({ | ||
| 168 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage/save-table`, | ||
| 169 | method: 'post', | ||
| 170 | data:params | ||
| 171 | }) | ||
| 172 | // 删除血源节点 | ||
| 173 | export const delLineageTable = (params) => request({ | ||
| 174 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage/del-vertex?vertexId=${params.vertexId}`, | ||
| 175 | method: 'delete', | ||
| 176 | //data:params | ||
| 177 | }) | ||
| 178 | /** 获取同步任务变更记录 */ | ||
| 179 | export const getTaskChangeList = (params) => request({ | ||
| 180 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/task-change-record/page-list`, | ||
| 181 | method: 'post', | ||
| 182 | data:params | ||
| 183 | }) | ||
| 184 | |||
| 185 | /** 获取元数据变更记录 */ | ||
| 186 | export const getMetaChangeList = (params) => request({ | ||
| 187 | url: ``, | ||
| 188 | method: 'post', | ||
| 189 | data:params | ||
| 190 | }) | ||
| 191 | |||
| 192 | /** 获取数据库选择列表 */ | ||
| 193 | export const getDatabase = (params) => request({ | ||
| 194 | url: `${import.meta.env.VITE_APP_API_BASEURL}/data-source/get-source-list`, | ||
| 195 | method: 'post', | ||
| 196 | data: params | ||
| 197 | }) | ||
| 198 | |||
| 199 | /** 源数据分析报告 */ | ||
| 200 | /**查询列表 */ | ||
| 201 | export const getAnalysisReportList = (params) => request({ | ||
| 202 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage-analysis-report/list`, | ||
| 203 | method: 'post', | ||
| 204 | data: params | ||
| 205 | }) | ||
| 206 | |||
| 207 | /** 根据guid删除 */ | ||
| 208 | export const delAnalysisRepor = (params) => request({ | ||
| 209 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage-analysis-report/del`, | ||
| 210 | method: 'delete', | ||
| 211 | data: params | ||
| 212 | }) | ||
| 213 | /** 根据guid更新 */ | ||
| 214 | export const updateAnalysisRepor = (params) => request({ | ||
| 215 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage-analysis-report/update`, | ||
| 216 | method: 'put', | ||
| 217 | data: params | ||
| 218 | }) | ||
| 219 | /** 删除边 */ | ||
| 220 | export const delLineAge = (params) => request({ | ||
| 221 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/lineage/del-edge?euid=${params}`, | ||
| 222 | method: 'delete', | ||
| 223 | }) | ||
| 224 | /** 判断是否有元数据数据 */ | ||
| 225 | export const checkTableData = (params) => request({ | ||
| 226 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-table/check-table-data/${params}`, | ||
| 227 | method: 'get', | ||
| 228 | }) | ||
| 229 | /**校验任务是否有数据库信息 */ | ||
| 230 | export const checkDatabaseIsExist = (dataSourceGuid) => request({ | ||
| 231 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/check-database-is-exist/${dataSourceGuid}`, | ||
| 232 | method: 'get', | ||
| 233 | }) | ||
| 234 | /**同步任务 变更详情展示 */ | ||
| 235 | |||
| 236 | export const syncChangeDetail = (guid) => request({ | ||
| 237 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/task-change-record/sync-change-detail/${guid}`, | ||
| 238 | method: 'get', | ||
| 239 | }) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/api/modules/dataQuality.ts
0 → 100644
| 1 | import request from "@/utils/request"; | ||
| 2 | |||
| 3 | /** 获取质量模型对应的所有分组表格数据 */ | ||
| 4 | export const getQualityTreeData = (params) => request({ | ||
| 5 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-group/quality-model-tree` + (params ? `?guid=${params}` : ''), | ||
| 6 | method: 'get' | ||
| 7 | }) | ||
| 8 | |||
| 9 | /** 获取数据源下,对应分组的表数据 */ | ||
| 10 | export const getQualityTreeDataByDs = (params) => request({ | ||
| 11 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-group/quality-model-tree?guid=${params.guid}&dataSourceGuid=${params.dataSourceGuid}`, | ||
| 12 | method: 'get' | ||
| 13 | }) | ||
| 14 | |||
| 15 | /** 获取质量模型对应的所有分组表格分页数据 */ | ||
| 16 | export const getQualityGroupData = (params) => request({ | ||
| 17 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-group/page-list`, | ||
| 18 | method: 'post', | ||
| 19 | data: params | ||
| 20 | }) | ||
| 21 | |||
| 22 | /** 删除质量模型的指定分组。 */ | ||
| 23 | export const deleteGroup = (params) => request({ | ||
| 24 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-group/del`, | ||
| 25 | method: 'delete', | ||
| 26 | data: params | ||
| 27 | }) | ||
| 28 | |||
| 29 | /** 添加分组 */ | ||
| 30 | export const addQualityGroup = (params) => request({ | ||
| 31 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-group/add`, | ||
| 32 | method: 'post', | ||
| 33 | data: params | ||
| 34 | }) | ||
| 35 | |||
| 36 | /** 修改分组 */ | ||
| 37 | export const updateQualityGroup = (params) => request({ | ||
| 38 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-group/update`, | ||
| 39 | method: 'put', | ||
| 40 | data: params | ||
| 41 | }) | ||
| 42 | |||
| 43 | /** 获取质量分组对应的表格分页数据 */ | ||
| 44 | export const getQualityTable = (params) => request({ | ||
| 45 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/page-list`, | ||
| 46 | method: 'post', | ||
| 47 | data: params | ||
| 48 | }) | ||
| 49 | |||
| 50 | /** 删除质检表 */ | ||
| 51 | export const deleteQualityTable = (params) => request({ | ||
| 52 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/del`, | ||
| 53 | method: 'delete', | ||
| 54 | data: params | ||
| 55 | }) | ||
| 56 | |||
| 57 | /** 获取质量表对应的所有规则数据 */ | ||
| 58 | export const getQualityTableRule = (params) => request({ | ||
| 59 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/list/model-rule`, | ||
| 60 | method: 'post', | ||
| 61 | data: params | ||
| 62 | }) | ||
| 63 | |||
| 64 | /** 删除质检表规则 */ | ||
| 65 | export const deleteQualityTableRule = (params) => request({ | ||
| 66 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/conf/del?ruleConfGuid=${params}`, | ||
| 67 | method: 'delete', | ||
| 68 | data: params | ||
| 69 | }) | ||
| 70 | |||
| 71 | /** 更新质检表规则的禁用和启用状态 */ | ||
| 72 | export const updateRuleBizState = (params) => request({ | ||
| 73 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/conf/upate-bizstate?ruleConfGuid=${params.ruleConfGuid}&bizState=${params.bizState}`, | ||
| 74 | method: 'post', | ||
| 75 | }) | ||
| 76 | |||
| 77 | /** 获取数据库表列表 */ | ||
| 78 | export const getDatabase = (params) => request({ | ||
| 79 | url: `${import.meta.env.VITE_APP_API_BASEURL}/data-source/get-source-list`, | ||
| 80 | method: 'post', | ||
| 81 | data: params | ||
| 82 | }) | ||
| 83 | |||
| 84 | /** 新建质检表,获取主题域分层的主题表树结构 */ | ||
| 85 | export const getSubjectTableTree = (params) => request({ | ||
| 86 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/data-catalog-directory/directory-tree-list`, | ||
| 87 | method: 'post', | ||
| 88 | data: params | ||
| 89 | }) | ||
| 90 | |||
| 91 | /** 新建质检表,获取主题域分层的主题表树结构 */ | ||
| 92 | export const getSubjectTableByDomain = (params) => request({ | ||
| 93 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/data-catalog-subject/list-by-domain-guid?domainGuid=${params}`, | ||
| 94 | method: 'get', | ||
| 95 | data: params | ||
| 96 | }) | ||
| 97 | |||
| 98 | /** 获取主题表的字段列表 */ | ||
| 99 | export const getSubjectFields = (params) => request({ | ||
| 100 | url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/data-catalog-subject/field/list?subjectGuid=${params}`, | ||
| 101 | method: 'get', | ||
| 102 | }) | ||
| 103 | |||
| 104 | /** 表的逻辑条件和sql检验。 */ | ||
| 105 | export const validateSubjectTableRule = (params) => request({ | ||
| 106 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/sql-operate/check-sql`, | ||
| 107 | method: 'post', | ||
| 108 | data: params | ||
| 109 | }) | ||
| 110 | |||
| 111 | /** 自定义sql检验 */ | ||
| 112 | export const validateCustomSql = (params) => request({ | ||
| 113 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/conf/check-custom-sql`, | ||
| 114 | method: 'post', | ||
| 115 | data: params | ||
| 116 | }) | ||
| 117 | |||
| 118 | /** 批量验证过滤条件 */ | ||
| 119 | export const batchValidateSubjectTableRule = (params) => request({ | ||
| 120 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/sql-operate/batch-check-sql`, | ||
| 121 | method: 'post', | ||
| 122 | data: params | ||
| 123 | }) | ||
| 124 | |||
| 125 | /** 保存质检表 */ | ||
| 126 | export const saveQualityTable = (params) => request({ | ||
| 127 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/add`, | ||
| 128 | method: 'post', | ||
| 129 | data: params | ||
| 130 | }) | ||
| 131 | |||
| 132 | // 获取规则类型的接口 | ||
| 133 | export const getRuleTypeList = () => request({ | ||
| 134 | url:`${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-rule/list`, | ||
| 135 | method: 'post', | ||
| 136 | data: {} | ||
| 137 | }) | ||
| 138 | |||
| 139 | // 获取规则大类的接口 | ||
| 140 | export const getLargeCategoryList = () => request({ | ||
| 141 | url:`${import.meta.env.VITE_APP_API_BASEURL}/data-dict/get-data-list`, | ||
| 142 | method: 'post', | ||
| 143 | data: { paramCode: "LARGE-CATEGORY" } | ||
| 144 | }) | ||
| 145 | |||
| 146 | // 获取规则小类的接口 | ||
| 147 | export const getSmallCategoryList = () => request({ | ||
| 148 | url:`${import.meta.env.VITE_APP_API_BASEURL}/data-dict/get-data-list`, | ||
| 149 | method: 'post', | ||
| 150 | data: { paramCode: "SMALL-CATEGORY" } | ||
| 151 | }) | ||
| 152 | |||
| 153 | // 根据规则guid获取规则的详情信息。 | ||
| 154 | export const getRuleConfDetail = (param) => request({ | ||
| 155 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/conf/detail?ruleConfGuid=${param}`, | ||
| 156 | method: 'get' | ||
| 157 | }) | ||
| 158 | |||
| 159 | // 根据质检模型guid获取详情信息。 | ||
| 160 | export const getModelDetail = (param) => request({ | ||
| 161 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/detail/${param}`, | ||
| 162 | method: 'get' | ||
| 163 | }) | ||
| 164 | |||
| 165 | // 更新规则信息。 | ||
| 166 | export const updateModelRule = (params) => request({ | ||
| 167 | url:`${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/conf/update`, | ||
| 168 | method: 'post', | ||
| 169 | data: params | ||
| 170 | }) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/api/modules/dataQualityAssess.ts
0 → 100644
| 1 | import request from "@/utils/request"; | ||
| 2 | |||
| 3 | /** 根据类型获取方案列表 */ | ||
| 4 | export const getPlanList = (params) => request({ | ||
| 5 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/list`, | ||
| 6 | method: 'post', | ||
| 7 | data: params | ||
| 8 | }) | ||
| 9 | |||
| 10 | /** 删除方案 */ | ||
| 11 | export const deletePlan = (guids) => request({ | ||
| 12 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/del`, | ||
| 13 | method: 'delete', | ||
| 14 | data: guids | ||
| 15 | }) | ||
| 16 | |||
| 17 | /** 更新指定方案的状态上线,下线。 */ | ||
| 18 | export const updateQualityPlanState = (params) => request({ | ||
| 19 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/state-change?guid=${params.guid}&state=${params.state}`, | ||
| 20 | method: 'post' | ||
| 21 | }) | ||
| 22 | |||
| 23 | /** 手动执行方案 */ | ||
| 24 | export const executePlan = (params) => request({ | ||
| 25 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/exec-plan?planGuid=${params}`, | ||
| 26 | method: 'post' | ||
| 27 | }) | ||
| 28 | |||
| 29 | /** 获取方案详情,用于编辑 */ | ||
| 30 | export const getAssessPlanDetail = (params) => request({ | ||
| 31 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/detail/${params}`, | ||
| 32 | method: 'get' | ||
| 33 | }) | ||
| 34 | |||
| 35 | /** 获取方案详情中的过滤条件,用于编辑 */ | ||
| 36 | export const getPlanFilterDetail = (params) => request({ | ||
| 37 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/query-plan-filter?planGuid=${params}`, | ||
| 38 | method: 'get' | ||
| 39 | }) | ||
| 40 | |||
| 41 | /** 获取方案查看详情列表数据。 */ | ||
| 42 | export const getAssessDetailTableData = (params) => request({ | ||
| 43 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/query-exec-list`, | ||
| 44 | method: 'post', | ||
| 45 | data: params | ||
| 46 | }) | ||
| 47 | |||
| 48 | /** 根据执行guid,获取方案执行详情。 */ | ||
| 49 | export const getExecPlanDetailTableData = (params) => request({ | ||
| 50 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/query-exec-detail?planGuid=${params.planGuid}&planExecGuid=${params.planExecGuid}`, | ||
| 51 | method: 'get' | ||
| 52 | }) | ||
| 53 | |||
| 54 | /** 获取方案详情中每个表的规则详细执行列表数据。 */ | ||
| 55 | export const getAssessTableRulesData = (params) => request({ | ||
| 56 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/query-exec-table-detail?planExecGuid=${params.planExecGuid}&qualityModelGuid=${params.qualityModelGuid}`, | ||
| 57 | method: 'get' | ||
| 58 | }) | ||
| 59 | |||
| 60 | /** 获取脏数据查询 */ | ||
| 61 | export const getQueryDirtyData = (params) => request({ | ||
| 62 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/query-dirty-data`, | ||
| 63 | method: 'post', | ||
| 64 | data: params | ||
| 65 | }) | ||
| 66 | |||
| 67 | /** 获取脏数据字段列表 */ | ||
| 68 | export const getQueryDirtyFields = (params) => request({ | ||
| 69 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/query-dirty-data-column?qualityModelGuid=${params}`, | ||
| 70 | method: 'get' | ||
| 71 | }) | ||
| 72 | |||
| 73 | /** 获取数据质量模型统计 */ | ||
| 74 | export const getModelCountList = (params) => request({ | ||
| 75 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/list/model-count`, | ||
| 76 | method: 'post', | ||
| 77 | data: params | ||
| 78 | }) | ||
| 79 | |||
| 80 | /** 获取规则大类统计 */ | ||
| 81 | export const getModelRuleCount = (params) => request({ | ||
| 82 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/list/model-rule-category-count`, | ||
| 83 | method: 'post', | ||
| 84 | data: params | ||
| 85 | }) | ||
| 86 | |||
| 87 | /** 根据modelGuid获取详细的规则统计 */ | ||
| 88 | export const getModelRules = (params) => request({ | ||
| 89 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/list/model-rule`, | ||
| 90 | method: 'post', | ||
| 91 | data: params | ||
| 92 | }) | ||
| 93 | |||
| 94 | /** 检查质检评估方案名称是否重复 */ | ||
| 95 | export const checkPlanExist = (params) => request({ | ||
| 96 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/exist?planName=${params}`, | ||
| 97 | method: 'get' | ||
| 98 | }) | ||
| 99 | |||
| 100 | /** 获取有效的数据分组,用于评估 表选择 */ | ||
| 101 | export const getValidGroup = () => request({ | ||
| 102 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-group/valid-group-list`, | ||
| 103 | method: 'get' | ||
| 104 | }) | ||
| 105 | |||
| 106 | /** 获取数据库列表 */ | ||
| 107 | export const getModelDbGp = (dsGuid: any = null) => request({ | ||
| 108 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model/list/model-db-gp` + (!dsGuid ? '' : `?dataSourceGuid=${dsGuid}`), | ||
| 109 | method: 'get' | ||
| 110 | }) | ||
| 111 | |||
| 112 | /** 保存质量方案 */ | ||
| 113 | export const saveQualityPlan = (params) => request({ | ||
| 114 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/add`, | ||
| 115 | method: 'post', | ||
| 116 | data: params | ||
| 117 | }) | ||
| 118 | |||
| 119 | /** 更新质量方案 */ | ||
| 120 | export const updateQualityPlan = (params) => request({ | ||
| 121 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/update`, | ||
| 122 | method: 'put', | ||
| 123 | data: params | ||
| 124 | }) | ||
| 125 | |||
| 126 | /** 下载脏数据 */ | ||
| 127 | export const downloadDirtyData = (params) => request({ | ||
| 128 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-plan/down-dirty-data`, | ||
| 129 | method: 'post', | ||
| 130 | data: params, | ||
| 131 | responseType: 'blob' | ||
| 132 | }) | ||
| 133 | |||
| 134 | /** 获取对应执行方案的规则详情 */ | ||
| 135 | export const getRecordRuleConfDetail = (param) => request({ | ||
| 136 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-model-record/conf/detail?ruleConfGuid=${param.ruleConfGuid}&planExecGuid=${param.planExecGuid}`, | ||
| 137 | method: 'get' | ||
| 138 | }); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/api/modules/dataQualityWord.ts
0 → 100644
| 1 | /** 用于质量分析报告模块 */ | ||
| 2 | import request from "@/utils/request"; | ||
| 3 | |||
| 4 | /** 获取质量分析报告列表数据。 */ | ||
| 5 | export const getQualityWordList = (params) => request({ | ||
| 6 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/page-list`, | ||
| 7 | method: 'post', | ||
| 8 | data: params | ||
| 9 | }) | ||
| 10 | |||
| 11 | /** 删除质量分析报告 */ | ||
| 12 | export const deleteQualityWord = (params) => request({ | ||
| 13 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/del`, | ||
| 14 | method: 'delete', | ||
| 15 | data: params | ||
| 16 | }) | ||
| 17 | |||
| 18 | /** 添加质量报告 */ | ||
| 19 | export const addQualityWord = (params) => request({ | ||
| 20 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/add`, | ||
| 21 | method: 'post', | ||
| 22 | data: params | ||
| 23 | }) | ||
| 24 | |||
| 25 | /** 更新质量报告 */ | ||
| 26 | export const updateQualityWord = (params) => request({ | ||
| 27 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/update`, | ||
| 28 | method: 'put', | ||
| 29 | data: params | ||
| 30 | }) | ||
| 31 | |||
| 32 | /** 根据质量报告获取对应的执行日志。 */ | ||
| 33 | export const getWordLogList = (params) => request({ | ||
| 34 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/page-exec-list`, | ||
| 35 | method: 'post', | ||
| 36 | data: params | ||
| 37 | }); | ||
| 38 | |||
| 39 | /** 获取质量分析报告的详细内容,根绝报告guid。 */ | ||
| 40 | export const getReportDetail = (params) => request({ | ||
| 41 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/get-report-data`, | ||
| 42 | method: 'post', | ||
| 43 | data: params | ||
| 44 | }); | ||
| 45 | |||
| 46 | /** 获取数据质量一级指标得分统计 */ | ||
| 47 | export const getLargeCategoryScore = (params) => request({ | ||
| 48 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/get-largeCategory-score?reportExecGuid=${params}`, | ||
| 49 | method: 'get' | ||
| 50 | }); | ||
| 51 | |||
| 52 | /** 获取方案执行明细 */ | ||
| 53 | export const getPlanDetail= (params) => request({ | ||
| 54 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/query-exec-table-detail?reportExecGuid=${params.reportExecGuid}&planGuid=${params.planGuid}`, | ||
| 55 | method: 'get' | ||
| 56 | }); | ||
| 57 | |||
| 58 | /** 获取方案执行表规则查看 */ | ||
| 59 | export const getTableRuleDetail= (params) => request({ | ||
| 60 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/query-exec-table-rule-detail?reportExecGuid=${params}`, | ||
| 61 | method: 'get' | ||
| 62 | }); | ||
| 63 | |||
| 64 | /** 手动执行报告 */ | ||
| 65 | export const executeReport = (params) => request({ | ||
| 66 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/exec?guid=${params}`, | ||
| 67 | method: 'get' | ||
| 68 | }) | ||
| 69 | |||
| 70 | /** html转word接口 */ | ||
| 71 | export const htmlToWord = (params) => request({ | ||
| 72 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/download/html-to-word`, | ||
| 73 | method: 'postJsonD', | ||
| 74 | data: params, | ||
| 75 | responseType: 'blob' | ||
| 76 | }); | ||
| 77 | |||
| 78 | /**上下线 */ | ||
| 79 | |||
| 80 | export const stateChange = (params) => request({ | ||
| 81 | url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/state-change?guid=${params.guid}&state=${params.state}`, | ||
| 82 | method: 'post', | ||
| 83 | |||
| 84 | }); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/components/PageNav/index.ts
0 → 100644
| 1 | /** 通用的分页配置 */ | ||
| 2 | export const commonPageConfig = { | ||
| 3 | limit: 50, | ||
| 4 | curr: 1, | ||
| 5 | sizes: [ | ||
| 6 | { label: "10", value: 10 }, | ||
| 7 | { label: "50", value: 50 }, | ||
| 8 | { label: "100", value: 100 }, | ||
| 9 | { label: "150", value: 150 }, | ||
| 10 | { label: "200", value: 200 }, | ||
| 11 | ] | ||
| 12 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -36,6 +36,10 @@ const pageCount = computed(() => { | ... | @@ -36,6 +36,10 @@ const pageCount = computed(() => { |
| 36 | return Math.ceil(props.pageInfo.rows / props.pageInfo.limit) | 36 | return Math.ceil(props.pageInfo.rows / props.pageInfo.limit) |
| 37 | }) | 37 | }) |
| 38 | 38 | ||
| 39 | const pagerCount = computed(() => { | ||
| 40 | return props.pageInfo.pagerCount ?? 7; | ||
| 41 | }) | ||
| 42 | |||
| 39 | const pageType = computed({ | 43 | const pageType = computed({ |
| 40 | get: () => { | 44 | get: () => { |
| 41 | return props.pageInfo.type | 45 | return props.pageInfo.type |
| ... | @@ -135,7 +139,7 @@ function inputPageJump(val) { | ... | @@ -135,7 +139,7 @@ function inputPageJump(val) { |
| 135 | 条数据</span> | 139 | 条数据</span> |
| 136 | </el-pagination> | 140 | </el-pagination> |
| 137 | <el-pagination small layout="prev, slot, next" :total="pageTotal" :default-page-size="pageLimit" | 141 | <el-pagination small layout="prev, slot, next" :total="pageTotal" :default-page-size="pageLimit" |
| 138 | :page-count="pageCount" :current-page="parseInt(pageNum)" @current-change="pageCurrentChange"> | 142 | :page-count="pageCount" :pager-count="pagerCount" :current-page="parseInt(pageNum)" @current-change="pageCurrentChange"> |
| 139 | <div class="page_code"> | 143 | <div class="page_code"> |
| 140 | <el-input class="pagnination" type="number" :min="1" :max="pageCount" v-model.trim="pageNumInput" size="small" | 144 | <el-input class="pagnination" type="number" :min="1" :max="pageCount" v-model.trim="pageNumInput" size="small" |
| 141 | style="width: 40px" @input="handleInput" @change="inputPageJump" :disabled="pageCount <= 1" /> | 145 | style="width: 40px" @input="handleInput" @change="inputPageJump" :disabled="pageCount <= 1" /> |
| ... | @@ -162,7 +166,7 @@ function inputPageJump(val) { | ... | @@ -162,7 +166,7 @@ function inputPageJump(val) { |
| 162 | }}</span> | 166 | }}</span> |
| 163 | 条数据</span> | 167 | 条数据</span> |
| 164 | </el-pagination> | 168 | </el-pagination> |
| 165 | <el-pagination background small layout="prev, pager, next, slot" :total="pageTotal" :default-page-size="pageLimit" | 169 | <el-pagination background small layout="prev, pager, next, slot" :total="pageTotal" :default-page-size="pageLimit" :pager-count="pagerCount" |
| 166 | :page-count="pageCount" :current-page="pageCurr" @current-change="pageCurrentChange"> | 170 | :page-count="pageCount" :current-page="pageCurr" @current-change="pageCurrentChange"> |
| 167 | <div class="page_jumper"> | 171 | <div class="page_jumper"> |
| 168 | <el-input class="pagnination" type="number" :min="1" :max="pageCount" v-model.trim="pageNumInput" size="small" | 172 | <el-input class="pagnination" type="number" :min="1" :max="pageCount" v-model.trim="pageNumInput" size="small" |
| ... | @@ -237,4 +241,4 @@ function inputPageJump(val) { | ... | @@ -237,4 +241,4 @@ function inputPageJump(val) { |
| 237 | padding: 0 8px; | 241 | padding: 0 8px; |
| 238 | } | 242 | } |
| 239 | } | 243 | } |
| 240 | </style>@/utils/common | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 244 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
src/router/modules/dataMeta.ts
0 → 100644
| 1 | import type { RouteRecordRaw } from 'vue-router' | ||
| 2 | |||
| 3 | function Layout() { | ||
| 4 | return import('@/layouts/index.vue') | ||
| 5 | } | ||
| 6 | |||
| 7 | const routes: RouteRecordRaw[] = [ | ||
| 8 | { | ||
| 9 | path: '/data-meta/collect-task', | ||
| 10 | component: Layout, | ||
| 11 | meta: { | ||
| 12 | title: '采集任务', | ||
| 13 | icon: 'sidebar-videos', | ||
| 14 | }, | ||
| 15 | children: [ | ||
| 16 | { | ||
| 17 | path: '', | ||
| 18 | name: 'collectorTask', | ||
| 19 | component: () => import('@/views/data_meta/collectorTask.vue'), | ||
| 20 | meta: { | ||
| 21 | title: '采集任务', | ||
| 22 | sidebar: false, | ||
| 23 | breadcrumb: false, | ||
| 24 | cache: true | ||
| 25 | }, | ||
| 26 | }, | ||
| 27 | { | ||
| 28 | path: 'excution-log', | ||
| 29 | name: 'excutionLog', | ||
| 30 | component: () => import('@/views/data_meta/executionLog.vue'), | ||
| 31 | meta: { | ||
| 32 | title: '采集日志', | ||
| 33 | sidebar: false, | ||
| 34 | breadcrumb: false, | ||
| 35 | }, | ||
| 36 | beforeEnter: (to, from) => { | ||
| 37 | if (to.query.name) { | ||
| 38 | to.meta.title = `采集日志-${to.query.name}`; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | }, | ||
| 42 | ], | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | path: '/data-meta/metadata-query', | ||
| 46 | component: Layout, | ||
| 47 | meta: { | ||
| 48 | title: '元数据查询', | ||
| 49 | icon: 'sidebar-videos', | ||
| 50 | }, | ||
| 51 | children: [ | ||
| 52 | { | ||
| 53 | path: '', | ||
| 54 | name: 'metadataQuery', | ||
| 55 | component: () => import('@/views/data_meta/metadataQuery.vue'), | ||
| 56 | meta: { | ||
| 57 | title: '元数据查询', | ||
| 58 | sidebar: false, | ||
| 59 | breadcrumb: false, | ||
| 60 | cache: true | ||
| 61 | }, | ||
| 62 | }, | ||
| 63 | { | ||
| 64 | path: 'meta-sheet', | ||
| 65 | name: 'metaSheet', | ||
| 66 | component: () => import('@/views/data_meta/metaSheet.vue'), | ||
| 67 | meta: { | ||
| 68 | title: '元数据详情-', | ||
| 69 | sidebar: false, | ||
| 70 | breadcrumb: false, | ||
| 71 | cache: true, | ||
| 72 | reuse: true | ||
| 73 | }, | ||
| 74 | beforeEnter: (to, from) => { | ||
| 75 | if (to.query.name) { | ||
| 76 | to.meta.title = `元数据详情-${to.query.name}`; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | }, | ||
| 80 | ], | ||
| 81 | }, | ||
| 82 | { | ||
| 83 | path: '/data-meta/metadata-lineage', | ||
| 84 | component: Layout, | ||
| 85 | meta: { | ||
| 86 | title: '元数据血缘', | ||
| 87 | icon: 'ep:grid', | ||
| 88 | }, | ||
| 89 | children: [ | ||
| 90 | { | ||
| 91 | path: 'analysis-view', | ||
| 92 | name: 'analysisView', | ||
| 93 | component: () => import('@/views/data_meta/analysisView.vue'), | ||
| 94 | meta: { | ||
| 95 | title: '查看血缘', | ||
| 96 | breadcrumb: false, | ||
| 97 | cache: true | ||
| 98 | }, | ||
| 99 | }, | ||
| 100 | { | ||
| 101 | path: 'change-detection', | ||
| 102 | name: 'changeDetection', | ||
| 103 | component: () => import('@/views/data_meta/changeDetection.vue'), | ||
| 104 | meta: { | ||
| 105 | title: '血缘变更检测', | ||
| 106 | breadcrumb: false, | ||
| 107 | cache: true | ||
| 108 | }, | ||
| 109 | }, | ||
| 110 | { | ||
| 111 | path: 'analysis-reports', | ||
| 112 | name: 'analysisReports', | ||
| 113 | component: () => import('@/views/data_meta/analysisReports.vue'), | ||
| 114 | meta: { | ||
| 115 | title: '血缘关系解析', | ||
| 116 | breadcrumb: false, | ||
| 117 | cache: true | ||
| 118 | }, | ||
| 119 | }, | ||
| 120 | ], | ||
| 121 | }, | ||
| 122 | ] | ||
| 123 | |||
| 124 | export default routes |
src/router/modules/dataQuality.ts
0 → 100644
| 1 | import type { RouteRecordRaw } from 'vue-router' | ||
| 2 | |||
| 3 | function Layout() { | ||
| 4 | return import('@/layouts/index.vue') | ||
| 5 | } | ||
| 6 | |||
| 7 | const routes: RouteRecordRaw[] = [ | ||
| 8 | { | ||
| 9 | path: '/data-quality/quality-rules', | ||
| 10 | component: Layout, | ||
| 11 | meta: { | ||
| 12 | title: '质量规则管理', | ||
| 13 | icon: 'sidebar-videos', | ||
| 14 | }, | ||
| 15 | children: [ | ||
| 16 | { | ||
| 17 | path: '', | ||
| 18 | name: 'qualityRules', | ||
| 19 | component: () => import('@/views/data_quality/qualityRules.vue'), | ||
| 20 | meta: { | ||
| 21 | title: '质量规则管理', | ||
| 22 | sidebar: false, | ||
| 23 | breadcrumb: false, | ||
| 24 | cache: true | ||
| 25 | }, | ||
| 26 | }, | ||
| 27 | { | ||
| 28 | path: 'rule-model', | ||
| 29 | name: 'ruleModel', | ||
| 30 | component: () => import('@/views/data_quality/ruleModel.vue'), | ||
| 31 | meta: { | ||
| 32 | title: '新建质检表', | ||
| 33 | sidebar: false, | ||
| 34 | breadcrumb: false, | ||
| 35 | cache: true, | ||
| 36 | reuse: true | ||
| 37 | }, | ||
| 38 | beforeEnter: (to, from) => { | ||
| 39 | if (to.query.groupGuid) { | ||
| 40 | to.meta.title = `新建质检表(${to.query.name})`; | ||
| 41 | to.meta.editPage = true; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | }, | ||
| 45 | { | ||
| 46 | path: 'rule-template', | ||
| 47 | name: 'ruleTemplate', | ||
| 48 | component: () => import('@/views/data_quality/ruleTemplate.vue'), | ||
| 49 | meta: { | ||
| 50 | title: '新建规则', | ||
| 51 | sidebar: false, | ||
| 52 | breadcrumb: false, | ||
| 53 | cache: true, | ||
| 54 | reuse: true | ||
| 55 | }, | ||
| 56 | beforeEnter: (to, from) => { | ||
| 57 | if (to.query.modelGuid) { | ||
| 58 | to.meta.title = `新建规则(${to.query.name})`; | ||
| 59 | to.meta.editPage = true; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | }, | ||
| 63 | { | ||
| 64 | path: 'rule-model-edit', | ||
| 65 | name: 'ruleModelEdit', | ||
| 66 | component: () => import('@/views/data_quality/ruleModelEdit.vue'), | ||
| 67 | meta: { | ||
| 68 | title: '编辑', | ||
| 69 | sidebar: false, | ||
| 70 | breadcrumb: false, | ||
| 71 | cache: true, | ||
| 72 | reuse: true, | ||
| 73 | editPage: true | ||
| 74 | }, | ||
| 75 | beforeEnter: (to, from) => { | ||
| 76 | if (to.query.guid) { | ||
| 77 | to.meta.title = `编辑-`; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | }, | ||
| 81 | { | ||
| 82 | path: 'import-file', | ||
| 83 | name: 'importFiles', | ||
| 84 | component: () => import('@/views/importFile.vue'), | ||
| 85 | meta: { | ||
| 86 | title: '文件导入', | ||
| 87 | sidebar: false, | ||
| 88 | breadcrumb: false, | ||
| 89 | cache: true, | ||
| 90 | reuse: true | ||
| 91 | }, | ||
| 92 | } | ||
| 93 | ], | ||
| 94 | }, | ||
| 95 | { | ||
| 96 | path: '/data-quality/quality-assess', | ||
| 97 | component: Layout, | ||
| 98 | meta: { | ||
| 99 | title: '质量评估方案', | ||
| 100 | icon: 'sidebar-videos', | ||
| 101 | }, | ||
| 102 | children: [ | ||
| 103 | { | ||
| 104 | path: '', | ||
| 105 | name: 'qualityAssess', | ||
| 106 | component: () => import('@/views/data_quality/qualityAssess.vue'), | ||
| 107 | meta: { | ||
| 108 | title: '质量评估方案', | ||
| 109 | sidebar: false, | ||
| 110 | breadcrumb: false, | ||
| 111 | cache: true | ||
| 112 | }, | ||
| 113 | }, | ||
| 114 | { | ||
| 115 | path: 'assess-template', | ||
| 116 | name: 'assessTemplate', | ||
| 117 | component: () => import('@/views/data_quality/assessTemplate.vue'), | ||
| 118 | meta: { | ||
| 119 | title: '新建质量评估方案', | ||
| 120 | sidebar: false, | ||
| 121 | breadcrumb: false, | ||
| 122 | cache: true, | ||
| 123 | reuse: true, | ||
| 124 | editPage: true | ||
| 125 | }, | ||
| 126 | beforeEnter: (to, from) => { | ||
| 127 | if (to.query.detail) { | ||
| 128 | to.meta.title = `方案详情-${to.query.planName}`; | ||
| 129 | to.meta.editPage = false; | ||
| 130 | } else if (to.query.planName) { | ||
| 131 | to.meta.title = `方案编辑-${to.query.planName}`; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | }, | ||
| 135 | { | ||
| 136 | path: 'assess-detail', | ||
| 137 | name: 'assessDetail', | ||
| 138 | component: () => import('@/views/data_quality/assessDetail.vue'), | ||
| 139 | meta: { | ||
| 140 | title: '查看结果', | ||
| 141 | sidebar: false, | ||
| 142 | breadcrumb: false, | ||
| 143 | cache: true, | ||
| 144 | reuse: true | ||
| 145 | }, | ||
| 146 | beforeEnter: (to, from) => { | ||
| 147 | if (to.query.name) { | ||
| 148 | to.meta.title = `查看结果-${to.query.name}`; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | }, | ||
| 152 | { | ||
| 153 | path: 'assess-dirty', | ||
| 154 | name: 'assessDirty', | ||
| 155 | component: () => import('@/views/data_quality/assessDirty.vue'), | ||
| 156 | meta: { | ||
| 157 | title: '脏数据', | ||
| 158 | sidebar: false, | ||
| 159 | breadcrumb: false, | ||
| 160 | cache: true, | ||
| 161 | reuse: true | ||
| 162 | }, | ||
| 163 | beforeEnter: (to, from) => { | ||
| 164 | if (to.query.name) { | ||
| 165 | to.meta.title = `脏数据-${to.query.name}`; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | }, | ||
| 169 | { | ||
| 170 | path: 'assess-log', | ||
| 171 | name: 'assessLog', | ||
| 172 | component: () => import('@/views/data_quality/assessLog.vue'), | ||
| 173 | meta: { | ||
| 174 | title: '执行日志', | ||
| 175 | sidebar: false, | ||
| 176 | breadcrumb: false, | ||
| 177 | cache: true, | ||
| 178 | reuse: true | ||
| 179 | }, | ||
| 180 | beforeEnter: (to, from) => { | ||
| 181 | if (to.query.guid) { | ||
| 182 | to.meta.title = `日志-${to.query.name}`; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | }, | ||
| 186 | ], | ||
| 187 | }, | ||
| 188 | { | ||
| 189 | path: '/data-quality/quality-analysis', | ||
| 190 | component: Layout, | ||
| 191 | meta: { | ||
| 192 | title: '质量分析报告', | ||
| 193 | icon: 'ep:grid', | ||
| 194 | }, | ||
| 195 | children: [ | ||
| 196 | { | ||
| 197 | path: '', | ||
| 198 | name: 'qualityAnalysis', | ||
| 199 | component: () => import('@/views/data_quality/qualityAnalysis.vue'), | ||
| 200 | meta: { | ||
| 201 | title: '质量分析报告', | ||
| 202 | sidebar: false, | ||
| 203 | breadcrumb: false, | ||
| 204 | cache: true | ||
| 205 | }, | ||
| 206 | }, | ||
| 207 | { | ||
| 208 | path: 'analysis-log', | ||
| 209 | name: 'analysisLog', | ||
| 210 | component: () => import('@/views/data_quality/analysisLog.vue'), | ||
| 211 | meta: { | ||
| 212 | title: '执行日志', | ||
| 213 | sidebar: false, | ||
| 214 | breadcrumb: false, | ||
| 215 | cache: true, | ||
| 216 | reuse: true | ||
| 217 | }, | ||
| 218 | beforeEnter: (to, from) => { | ||
| 219 | if (to.query.name) { | ||
| 220 | to.meta.title = `日志-${to.query.name}`; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | }, | ||
| 224 | { | ||
| 225 | path: 'analysis-report', | ||
| 226 | name: 'analysisReport', | ||
| 227 | component: () => import('@/views/data_quality/analysisReport.vue'), | ||
| 228 | meta: { | ||
| 229 | title: '分析报告', | ||
| 230 | sidebar: false, | ||
| 231 | breadcrumb: false, | ||
| 232 | cache: true, | ||
| 233 | reuse: true | ||
| 234 | }, | ||
| 235 | beforeEnter: (to, from) => { | ||
| 236 | if (to.query.name) { | ||
| 237 | to.meta.title = `分析报告-${to.query.name}`; | ||
| 238 | } | ||
| 239 | } | ||
| 240 | }, | ||
| 241 | ], | ||
| 242 | } | ||
| 243 | ] | ||
| 244 | |||
| 245 | export default routes |
| ... | @@ -2,6 +2,8 @@ import { setupLayouts } from 'virtual:meta-layouts' | ... | @@ -2,6 +2,8 @@ import { setupLayouts } from 'virtual:meta-layouts' |
| 2 | import generatedRoutes from 'virtual:generated-pages' | 2 | import generatedRoutes from 'virtual:generated-pages' |
| 3 | import type { RouteRecordRaw } from 'vue-router' | 3 | import type { RouteRecordRaw } from 'vue-router' |
| 4 | import DataAssess from './modules/dataAsset'; | 4 | import DataAssess from './modules/dataAsset'; |
| 5 | import DataMeta from './modules/dataMeta'; | ||
| 6 | import DataQuality from './modules/dataQuality'; | ||
| 5 | 7 | ||
| 6 | import type { Route } from '#/global' | 8 | import type { Route } from '#/global' |
| 7 | import useSettingsStore from '@/store/modules/settings' | 9 | import useSettingsStore from '@/store/modules/settings' |
| ... | @@ -105,6 +107,22 @@ const asyncRoutes: Route.recordMainRaw[] = [ | ... | @@ -105,6 +107,22 @@ const asyncRoutes: Route.recordMainRaw[] = [ |
| 105 | ...DataAssess, | 107 | ...DataAssess, |
| 106 | ], | 108 | ], |
| 107 | }, | 109 | }, |
| 110 | { | ||
| 111 | meta: { | ||
| 112 | title: '元数据', | ||
| 113 | }, | ||
| 114 | children: [ | ||
| 115 | ...DataMeta, | ||
| 116 | ], | ||
| 117 | }, | ||
| 118 | { | ||
| 119 | meta: { | ||
| 120 | title: '数据质量', | ||
| 121 | }, | ||
| 122 | children: [ | ||
| 123 | ...DataQuality, | ||
| 124 | ], | ||
| 125 | } | ||
| 108 | ] | 126 | ] |
| 109 | 127 | ||
| 110 | const constantRoutesByFilesystem = generatedRoutes.filter((item) => { | 128 | const constantRoutesByFilesystem = generatedRoutes.filter((item) => { | ... | ... |
| ... | @@ -46,6 +46,6 @@ const useDataQualityStore = defineStore( | ... | @@ -46,6 +46,6 @@ const useDataQualityStore = defineStore( |
| 46 | defaultPlanType | 46 | defaultPlanType |
| 47 | } | 47 | } |
| 48 | }, | 48 | }, |
| 49 | ) | 49 | ) |
| 50 | 50 | ||
| 51 | export default useDataQualityStore | 51 | export default useDataQualityStore |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
src/views/data_meta/analysisReports.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: analysisReports | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="analysisReports"> | ||
| 6 | import { ref } from 'vue' | ||
| 7 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 8 | import Table from '@/components/Table/index.vue' | ||
| 9 | import TableTools from '@/components/Tools/table_tools.vue' | ||
| 10 | import {getAnalysisReportList,delAnalysisRepor,updateAnalysisRepor} from "@/api/modules/dataMetaService" | ||
| 11 | import { getImageContent } from "@/api/modules/queryService"; | ||
| 12 | import Dialog from '@/components/Dialog/index.vue' | ||
| 13 | import { getDownloadUrl, download } from "@/utils/common"; | ||
| 14 | import { useRouter } from 'vue-router'; | ||
| 15 | const router = useRouter() | ||
| 16 | const page = ref({ | ||
| 17 | limit: 50, | ||
| 18 | curr: 1, | ||
| 19 | sizes: [ | ||
| 20 | { label: "10", value: 10 }, | ||
| 21 | { label: "50", value: 50 }, | ||
| 22 | { label: "100", value: 100 }, | ||
| 23 | { label: "150", value: 150 }, | ||
| 24 | { label: "200", value: 200 }, | ||
| 25 | ], | ||
| 26 | analysisReportName: '' | ||
| 27 | }); | ||
| 28 | const { proxy } = getCurrentInstance() as any; | ||
| 29 | const tableInfo = ref({ | ||
| 30 | id: 'analysis-reports-table', | ||
| 31 | fields: [ | ||
| 32 | { label: "序号", type: "index", width: 56, align: "center" }, | ||
| 33 | { label: "血缘关系名称", field: "analysisReportName", width: 150 }, | ||
| 34 | { label: "数据源中文名称", field: "databaseChName", width: 150 }, | ||
| 35 | { label: "表名称", field: "table", width: 150 }, | ||
| 36 | { label: "创建人", field: "updateUserName", width: 140 }, | ||
| 37 | { label: "创建时间", field: "updateTime", width: 180 } | ||
| 38 | ], | ||
| 39 | data: [], | ||
| 40 | page: { | ||
| 41 | type: "normal", | ||
| 42 | rows: 0, | ||
| 43 | ...page.value, | ||
| 44 | }, | ||
| 45 | actionInfo: { | ||
| 46 | label: "操作", | ||
| 47 | type: "btn", | ||
| 48 | width: 185, | ||
| 49 | fixed: 'right', | ||
| 50 | btns: (scope) => { | ||
| 51 | let btnsArr: any = [ | ||
| 52 | { label: "查看", value: "view" }, | ||
| 53 | { label: "下载", value: "export" }, | ||
| 54 | { label: "重命名", value: "rename" }, | ||
| 55 | { label: "删除", value: "delete" }, | ||
| 56 | ] | ||
| 57 | return btnsArr; | ||
| 58 | }, | ||
| 59 | }, | ||
| 60 | loading: false | ||
| 61 | }) | ||
| 62 | |||
| 63 | const currTableData: any = ref({}); | ||
| 64 | const formItems: any = ref([ | ||
| 65 | { | ||
| 66 | label: '血缘关系名称', | ||
| 67 | type: 'input', | ||
| 68 | placeholder: '请输入', | ||
| 69 | field: 'analysisReportName', | ||
| 70 | default: '', | ||
| 71 | clearable: true, | ||
| 72 | required: true | ||
| 73 | }, | ||
| 74 | ]) | ||
| 75 | const formRules: any = ref({ | ||
| 76 | analysisReportName: [ | ||
| 77 | { | ||
| 78 | required: true, | ||
| 79 | message: "请填写血缘关系名称", | ||
| 80 | trigger: "blur", | ||
| 81 | }, | ||
| 82 | ], | ||
| 83 | }) | ||
| 84 | const formInfo = ref({ | ||
| 85 | type: 'form', | ||
| 86 | title: '', | ||
| 87 | formInfo: { | ||
| 88 | id: 'add-dict-form', | ||
| 89 | items: formItems.value, | ||
| 90 | rules: formRules.value | ||
| 91 | } | ||
| 92 | }) | ||
| 93 | const reportDialogRef = ref(); | ||
| 94 | const dialogInfo = ref({ | ||
| 95 | visible: false, | ||
| 96 | size: 500, | ||
| 97 | direction: "column", | ||
| 98 | header: { | ||
| 99 | title: "重命名", | ||
| 100 | }, | ||
| 101 | type: '', | ||
| 102 | contents: [ | ||
| 103 | formInfo.value, | ||
| 104 | ], | ||
| 105 | footer: { | ||
| 106 | btns: [ | ||
| 107 | { type: "default", label: "取消", value: "cancel" }, | ||
| 108 | { type: "primary", label: "保存", value: "submit" }, | ||
| 109 | ], | ||
| 110 | }, | ||
| 111 | }); | ||
| 112 | const rowData:any = ref({}) | ||
| 113 | const tableSearchItemList: any = ref([{ | ||
| 114 | type: 'input', | ||
| 115 | label: '', | ||
| 116 | field: 'analysisReportName', | ||
| 117 | default: '', | ||
| 118 | maxlength: 50, | ||
| 119 | placeholder: '血缘关系名称', | ||
| 120 | clearable: true | ||
| 121 | }]); | ||
| 122 | const tableBtnClick = (scope, btn) => { | ||
| 123 | const type = btn.value; | ||
| 124 | let row = scope.row; | ||
| 125 | rowData.value = row | ||
| 126 | currTableData.value = row; | ||
| 127 | if (type == 'view') { | ||
| 128 | getImageContent(row.analysisReportUrl).then((res: any) => { | ||
| 129 | if (res && !res.msg) { | ||
| 130 | let name = row.analysisReportUrl; | ||
| 131 | var fileSuffix = name ? name.substring(name.lastIndexOf('.') + 1) : ''; | ||
| 132 | if (fileSuffix === 'png') { //浏览器可以支持图片和pdf预览 | ||
| 133 | let fileUrl = getDownloadUrl(res, name, fileSuffix); | ||
| 134 | let win = window.open(fileUrl, name); | ||
| 135 | win && (win.document.title = name); | ||
| 136 | } else { | ||
| 137 | download(res, row.analysisReportName, fileSuffix); | ||
| 138 | } | ||
| 139 | } else { | ||
| 140 | res?.msg && ElMessage.error(res?.msg); | ||
| 141 | } | ||
| 142 | }); | ||
| 143 | } else if (type == 'export') { | ||
| 144 | getImageContent(row.analysisReportUrl).then((res: any) => { | ||
| 145 | if (res && !res.msg) { | ||
| 146 | let name = row.analysisReportUrl; | ||
| 147 | var fileSuffix = name ? name.substring(name.lastIndexOf('.') + 1) : ''; | ||
| 148 | download(res, row.analysisReportName, fileSuffix); | ||
| 149 | } else { | ||
| 150 | res?.msg && ElMessage.error(res?.msg); | ||
| 151 | } | ||
| 152 | }); | ||
| 153 | } else if (type == "rename") { | ||
| 154 | dialogInfo.value.visible = true | ||
| 155 | formItems.value[0].default = row.analysisReportName | ||
| 156 | } else if (type == "delete") { | ||
| 157 | open("此操作将永久删除, 是否继续?", "warning"); | ||
| 158 | } | ||
| 159 | }; | ||
| 160 | |||
| 161 | const dialogBtnClick = (btn,scope)=>{ | ||
| 162 | if(btn.value==="cancel") { | ||
| 163 | dialogInfo.value.visible = false | ||
| 164 | } else { | ||
| 165 | const analysisReportName = reportDialogRef.value.dialogFormRef[0].formInline.analysisReportName; | ||
| 166 | if(analysisReportName){ | ||
| 167 | console.log(analysisReportName) | ||
| 168 | updateAnalysisRepor({guid:rowData.value.guid,analysisReportName}).then((res:any)=>{ | ||
| 169 | if(res.code===proxy.$passCode){ | ||
| 170 | ElMessage.success("血缘关系名称修改成功!") | ||
| 171 | getTableData() | ||
| 172 | dialogInfo.value.visible = false | ||
| 173 | } else { | ||
| 174 | ElMessage.error(res.msg) | ||
| 175 | } | ||
| 176 | }).catch((res:any)=>{ | ||
| 177 | ElMessage.error(res.msg) | ||
| 178 | }) | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | } | ||
| 183 | |||
| 184 | const open = (msg, type) => { | ||
| 185 | ElMessageBox.confirm(msg, "提示", { | ||
| 186 | confirmButtonText: "确定", | ||
| 187 | cancelButtonText: "取消", | ||
| 188 | type: type, | ||
| 189 | }).then(() => { | ||
| 190 | let guid = currTableData.value.guid; | ||
| 191 | delAnalysisRepor([guid]).then((res:any)=>{ | ||
| 192 | if(res.code===proxy.$passCode){ | ||
| 193 | getTableData() | ||
| 194 | ElMessage.success("删除成功") | ||
| 195 | } else { | ||
| 196 | ElMessage.error(res.msg) | ||
| 197 | } | ||
| 198 | }).catch((res)=>{ | ||
| 199 | ElMessage.error(res.msg) | ||
| 200 | }) | ||
| 201 | }); | ||
| 202 | }; | ||
| 203 | |||
| 204 | const tablePageChange = (info) => { | ||
| 205 | page.value.curr = Number(info.curr); | ||
| 206 | page.value.limit = Number(info.limit); | ||
| 207 | getTableData() | ||
| 208 | }; | ||
| 209 | |||
| 210 | |||
| 211 | const getTableData = ()=>{ | ||
| 212 | tableInfo.value.loading = true | ||
| 213 | getAnalysisReportList({pageIndex:page.value.curr,pageSize:page.value.limit, analysisReportName: page.value.analysisReportName}).then((res:any)=>{ | ||
| 214 | |||
| 215 | if(res.code===proxy.$passCode){ | ||
| 216 | tableInfo.value.data = res.data.records || [] | ||
| 217 | tableInfo.value.page.rows = res.data.totalRows | ||
| 218 | tableInfo.value.page.curr = res.data.pageIndex | ||
| 219 | tableInfo.value.page.limit = res.data.pageSize | ||
| 220 | tableInfo.value.loading = false | ||
| 221 | } else { | ||
| 222 | ElMessage.error(res.msg) | ||
| 223 | tableInfo.value.loading = false | ||
| 224 | } | ||
| 225 | |||
| 226 | }) | ||
| 227 | } | ||
| 228 | const toSearch = (val: any, clear: boolean = false) => { | ||
| 229 | if (clear) { | ||
| 230 | page.value.analysisReportName = ''; | ||
| 231 | } else { | ||
| 232 | page.value.analysisReportName = val.analysisReportName; | ||
| 233 | } | ||
| 234 | page.value.curr = 1; | ||
| 235 | getTableData(); | ||
| 236 | }; | ||
| 237 | onActivated(() => { | ||
| 238 | getTableData() | ||
| 239 | }); | ||
| 240 | |||
| 241 | </script> | ||
| 242 | |||
| 243 | <template> | ||
| 244 | <div class="container_wrap"> | ||
| 245 | <div class="table_tool_wrap"> | ||
| 246 | <TableTools :searchItems="tableSearchItemList" :init="false" searchId="report-table-search" @search="toSearch" /> | ||
| 247 | </div> | ||
| 248 | <div class="table_panel_wrap full"> | ||
| 249 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tablePageChange="tablePageChange" /> | ||
| 250 | </div> | ||
| 251 | <Dialog ref="reportDialogRef" :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" /> | ||
| 252 | </div> | ||
| 253 | </template> | ||
| 254 | |||
| 255 | <style scoped lang="scss"> | ||
| 256 | :deep(.el-overlay .el-form .el-form-item) { | ||
| 257 | width: calc(100%); | ||
| 258 | } | ||
| 259 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_meta/analysisView.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: analysisView | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="analysisView"> | ||
| 6 | import { ref } from 'vue'; | ||
| 7 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 8 | import Tree from '@/components/Tree/index.vue'; | ||
| 9 | import LineageGraph from '@/components/LineageGraph/index.vue'; | ||
| 10 | import Dialog from "@/components/Dialog/index.vue" | ||
| 11 | import { | ||
| 12 | getMetaTreeData, | ||
| 13 | getTableLineage, | ||
| 14 | getTableFieldLineage, | ||
| 15 | getTableAllFieldLineage, | ||
| 16 | getMetaList, | ||
| 17 | getMetaTableField, | ||
| 18 | saveLineageTable, | ||
| 19 | saveLineageField, | ||
| 20 | delLineageTable, | ||
| 21 | saveMetaReportAnalysis, | ||
| 22 | delLineAge, | ||
| 23 | checkTableData | ||
| 24 | } from '@/api/modules/dataMetaService'; | ||
| 25 | import { getFileUrl } from "@/api/modules/queryService" | ||
| 26 | import { useRouter, useRoute } from "vue-router"; | ||
| 27 | import useDataMetaStore from "@/store/modules/dataMeta" | ||
| 28 | import { cloneDeep } from 'lodash-es' | ||
| 29 | |||
| 30 | const router = useRouter(); | ||
| 31 | const route = useRoute() | ||
| 32 | const emunData = { | ||
| 33 | "1": "数据库表" | ||
| 34 | } | ||
| 35 | const { proxy } = getCurrentInstance() as any; | ||
| 36 | |||
| 37 | /** 添加上下游节点弹框时第一个下拉框下拉第一级内容。 */ | ||
| 38 | const metaTree = ref([]) | ||
| 39 | /** 血缘关系数据 */ | ||
| 40 | const lineageData = ref([]); | ||
| 41 | /** 血缘关系页面加载 */ | ||
| 42 | const lineageLoading = ref(false); | ||
| 43 | /** 当前切换字段血缘关系的开关 */ | ||
| 44 | const switchFieldLineage = ref(false); | ||
| 45 | /** 字段血缘关系 */ | ||
| 46 | const isFieldLineage = ref(false); | ||
| 47 | /** 添加上下游节点弹框表单规则配置。 */ | ||
| 48 | const formRules = ref({ | ||
| 49 | databaseGuid: [ | ||
| 50 | { | ||
| 51 | required: true, | ||
| 52 | message: "选择元数据名称", | ||
| 53 | trigger: "change", | ||
| 54 | }, | ||
| 55 | ], | ||
| 56 | tableName: [ | ||
| 57 | { | ||
| 58 | required: true, | ||
| 59 | message: "请添加节点", | ||
| 60 | trigger: "change", | ||
| 61 | }, | ||
| 62 | ], | ||
| 63 | tableField: [ | ||
| 64 | { | ||
| 65 | required: true, | ||
| 66 | message: "请添加节点", | ||
| 67 | trigger: "change", | ||
| 68 | }, | ||
| 69 | ] | ||
| 70 | }); | ||
| 71 | const formItems = ref([{ | ||
| 72 | label: '选择元数据名称', | ||
| 73 | type: 'tree-select', | ||
| 74 | placeholder: '请选择', | ||
| 75 | field: 'databaseGuid', | ||
| 76 | default: "", | ||
| 77 | props: { | ||
| 78 | label: "name", | ||
| 79 | children: "children", | ||
| 80 | value: 'guid', | ||
| 81 | isLeaf: 'isLeaf' | ||
| 82 | }, | ||
| 83 | teleported: false, | ||
| 84 | filterable: true, | ||
| 85 | checkStrictly: false, | ||
| 86 | options: [], | ||
| 87 | required: true, | ||
| 88 | block: true | ||
| 89 | }, | ||
| 90 | { | ||
| 91 | label: '添加节点', | ||
| 92 | type: 'select', | ||
| 93 | placeholder: '请选择', | ||
| 94 | field: 'tableName', | ||
| 95 | options: [], | ||
| 96 | clearable: true, | ||
| 97 | required: true, | ||
| 98 | block: true, | ||
| 99 | filterable: true, | ||
| 100 | visible: true, | ||
| 101 | readonly: true, | ||
| 102 | teleported: false, | ||
| 103 | }, | ||
| 104 | { | ||
| 105 | label: '添加节点', | ||
| 106 | type: 'tree-select', | ||
| 107 | placeholder: '请选择', | ||
| 108 | field: 'tableField', | ||
| 109 | default: "", | ||
| 110 | props: { | ||
| 111 | label: "label", | ||
| 112 | children: "children", | ||
| 113 | value: 'value', | ||
| 114 | isLeaf: 'isLeaf' | ||
| 115 | }, | ||
| 116 | teleported: false, | ||
| 117 | filterable: true, | ||
| 118 | checkStrictly: false, | ||
| 119 | options: [], | ||
| 120 | visible: false, | ||
| 121 | required: true, | ||
| 122 | block: true | ||
| 123 | }, | ||
| 124 | ]) | ||
| 125 | /** 添加上下游节点时记录的当前选择的添加节点信息,以及当前操作的节点信息。 */ | ||
| 126 | const formLine: any = ref({}) | ||
| 127 | const dialogInfo = ref({ | ||
| 128 | visible: false, | ||
| 129 | size: 500, | ||
| 130 | direction: "column", | ||
| 131 | showClose: true, | ||
| 132 | modalClass: "createNode", | ||
| 133 | header: { | ||
| 134 | title: "添加上游节点", | ||
| 135 | }, | ||
| 136 | type: '', | ||
| 137 | addType: 'table', //分为table和field两种节点。 | ||
| 138 | contents: [ | ||
| 139 | { | ||
| 140 | type: "form", | ||
| 141 | title: "", | ||
| 142 | formInfo: { | ||
| 143 | id: "timed-management-form", | ||
| 144 | items: formItems.value, | ||
| 145 | rules: formRules.value | ||
| 146 | }, | ||
| 147 | } | ||
| 148 | ], | ||
| 149 | footer: { | ||
| 150 | visible: true, | ||
| 151 | btns: [ | ||
| 152 | { type: "default", label: "取消", value: "cancel" }, | ||
| 153 | { type: "primary", label: "确定", value: "submit" }, | ||
| 154 | ], | ||
| 155 | }, | ||
| 156 | }); | ||
| 157 | |||
| 158 | const treeInfo = ref({ | ||
| 159 | id: "data-pickup-tree", | ||
| 160 | filter: true, | ||
| 161 | queryValue: "", | ||
| 162 | queryPlaceholder: "输入库/表名称搜索", | ||
| 163 | props: { | ||
| 164 | label: "name", | ||
| 165 | value: "guid", | ||
| 166 | isLeaf: "isLeaf", | ||
| 167 | }, | ||
| 168 | nodeKey: 'guid', | ||
| 169 | lazy: true, | ||
| 170 | expandedKey: [], | ||
| 171 | currentNodeKey: '', | ||
| 172 | expandOnNodeClick: false, | ||
| 173 | data: <any>[], | ||
| 174 | customFilter: true, | ||
| 175 | loading: false | ||
| 176 | }); | ||
| 177 | const currtableGuid = ref("") | ||
| 178 | |||
| 179 | /** 记录原始获取的所有树形结构数据,用于处理搜索. */ | ||
| 180 | const allTreeData: any = ref([]); | ||
| 181 | |||
| 182 | /** 获取左侧树数据. */ | ||
| 183 | const getTreeData = async () => { | ||
| 184 | treeInfo.value.loading = true | ||
| 185 | let params = {} | ||
| 186 | const res: any = await getMetaTreeData(params) | ||
| 187 | if (res.code == proxy.$passCode) { | ||
| 188 | const data = res.data || []; | ||
| 189 | let treeData: any = Object.keys(data).length ? [data] : []; | ||
| 190 | treeInfo.value.data = treeData; | ||
| 191 | allTreeData.value = treeData; | ||
| 192 | if (treeData.length) { | ||
| 193 | treeInfo.value.currentNodeKey = treeData[0].guid; | ||
| 194 | treeInfo.value.expandedKey = <any>[treeData[0].guid]; | ||
| 195 | } | ||
| 196 | } else { | ||
| 197 | ElMessage.error(res.msg); | ||
| 198 | } | ||
| 199 | treeInfo.value.loading = false | ||
| 200 | } | ||
| 201 | /** 左侧树的的组件引用. */ | ||
| 202 | const treeInfoRef = ref(); | ||
| 203 | |||
| 204 | /** 处理左侧树过滤方法.默认的树懒加载模式未展开过的不会过滤出结果,但是现在已经有了两级数据. */ | ||
| 205 | const handleFilterTree = (query) => { | ||
| 206 | if (!allTreeData.value.length) { | ||
| 207 | return []; | ||
| 208 | } | ||
| 209 | if (!query) { | ||
| 210 | treeInfo.value.data = allTreeData.value; | ||
| 211 | nextTick(() => { | ||
| 212 | treeInfoRef.value.treeRef.store.setData(treeInfo.value.data); | ||
| 213 | }); | ||
| 214 | return; | ||
| 215 | } | ||
| 216 | let expandKeys: any = [allTreeData.value[0].guid]; | ||
| 217 | const filterChildren = (children) => { | ||
| 218 | let nodes: any = []; | ||
| 219 | children.forEach(child => { | ||
| 220 | if (!child.children?.length) { | ||
| 221 | if (child[treeInfo.value.props.label].includes(query)) { | ||
| 222 | nodes.push(child); | ||
| 223 | } | ||
| 224 | } else { | ||
| 225 | let filterNodes = filterChildren(child.children); | ||
| 226 | if (filterNodes.length) { | ||
| 227 | child.children = filterNodes; | ||
| 228 | nodes.push(child); | ||
| 229 | expandKeys.push(child.guid); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | }); | ||
| 233 | return nodes; | ||
| 234 | } | ||
| 235 | treeInfo.value.data = [Object.assign({}, treeInfo.value.data[0], { | ||
| 236 | children: filterChildren(cloneDeep(allTreeData.value[0].children)) | ||
| 237 | })]; | ||
| 238 | treeInfo.value.expandedKey = expandKeys; | ||
| 239 | nextTick(() => { | ||
| 240 | treeInfoRef.value.treeRef.store.setData(treeInfo.value.data); | ||
| 241 | }); | ||
| 242 | } | ||
| 243 | |||
| 244 | /** 根据表获取元数据字段 */ | ||
| 245 | const getMetaTableFieldPromises: any = ref({}); | ||
| 246 | |||
| 247 | /** 处理树形懒加载,一级级展开. */ | ||
| 248 | const loadTreeNode = (node, resolve) => { | ||
| 249 | if (node.isLeaf) { | ||
| 250 | return resolve([]); | ||
| 251 | } | ||
| 252 | if (node.level === 0) { | ||
| 253 | resolve(treeInfo.value.data) | ||
| 254 | } else if (node.level === 1) { | ||
| 255 | resolve(node.data.children) | ||
| 256 | } else if (node.level === 2) { | ||
| 257 | resolve(node.data.children) | ||
| 258 | } else if (node.level === 3) { | ||
| 259 | let params = { | ||
| 260 | tableGuid: node.data.guid | ||
| 261 | } | ||
| 262 | if (getMetaTableFieldPromises.value[node.data.guid]) { | ||
| 263 | getMetaTableFieldPromises.value[node.data.guid].then((res: any) => { | ||
| 264 | delete getMetaTableFieldPromises.value[node.data.guid]; | ||
| 265 | if (res.code == proxy.$passCode) { | ||
| 266 | const data = res.data ?? []; | ||
| 267 | let isCurrentNodeField = false; | ||
| 268 | let currentNodeKey = treeInfo.value.currentNodeKey; | ||
| 269 | data.map(item => { | ||
| 270 | if (currentNodeKey == item.guid) { | ||
| 271 | isCurrentNodeField = true; | ||
| 272 | } | ||
| 273 | item.enName = item.enName; | ||
| 274 | item.name = item.chName || item.enName; | ||
| 275 | item.databaseName = node.data.databaseName; | ||
| 276 | item.databaseChName = node.data.databaseChName; | ||
| 277 | item.tableName = node.data.tableName; | ||
| 278 | item.type = 4 | ||
| 279 | item.dataTypeCode = item.fieldType | ||
| 280 | item.isLeaf = true | ||
| 281 | item.businessDefDesc = item.comment, | ||
| 282 | item.tableGuid = node.data.guid; | ||
| 283 | item.databaseGuid = node.parent.data.guid; | ||
| 284 | }) | ||
| 285 | node.data.children = data; | ||
| 286 | nextTick(() => { | ||
| 287 | treeInfo.value.currentNodeKey && treeInfoRef.value.setCurrentKey(treeInfo.value.currentNodeKey); | ||
| 288 | treeInfo.value.currentNodeKey && isCurrentNodeField && scrollToNode(treeInfo.value.currentNodeKey); | ||
| 289 | let node1 = treeInfoRef.value.treeRef.store.nodesMap[treeInfo.value.currentNodeKey]; | ||
| 290 | if (node1) { | ||
| 291 | node1.loaded = true; | ||
| 292 | node1.loading = false; | ||
| 293 | } | ||
| 294 | }); | ||
| 295 | resolve(data) | ||
| 296 | } else { | ||
| 297 | ElMessage({ | ||
| 298 | type: 'info', | ||
| 299 | message: res.msg, | ||
| 300 | }) | ||
| 301 | } | ||
| 302 | }); | ||
| 303 | } else { | ||
| 304 | getMetaTableFieldPromises.value[node.data.guid] = getMetaTableField(params).then((res: any) => { | ||
| 305 | delete getMetaTableFieldPromises.value[node.data.guid]; | ||
| 306 | if (res.code == proxy.$passCode) { | ||
| 307 | const data = res.data ?? [] | ||
| 308 | let isCurrentNodeField = false; | ||
| 309 | let currentNodeKey = treeInfo.value.currentNodeKey; | ||
| 310 | data.map(item => { | ||
| 311 | if (currentNodeKey == item.guid) { | ||
| 312 | isCurrentNodeField = true; | ||
| 313 | } | ||
| 314 | item.enName = item.enName; | ||
| 315 | item.name = item.chName || item.enName; | ||
| 316 | item.databaseName = node.data.databaseName; | ||
| 317 | item.databaseChName = node.data.databaseChName; | ||
| 318 | item.tableName = node.data.tableName; | ||
| 319 | item.type = 4 | ||
| 320 | item.dataTypeCode = item.fieldType | ||
| 321 | item.isLeaf = true | ||
| 322 | item.businessDefDesc = item.comment, | ||
| 323 | item.tableGuid = node.data.guid; | ||
| 324 | item.databaseGuid = node.parent.data.guid; | ||
| 325 | }) | ||
| 326 | node.data.children = data; | ||
| 327 | nextTick(() => { | ||
| 328 | treeInfo.value.currentNodeKey && treeInfoRef.value.setCurrentKey(treeInfo.value.currentNodeKey); | ||
| 329 | treeInfo.value.currentNodeKey && isCurrentNodeField && scrollToNode(treeInfo.value.currentNodeKey); | ||
| 330 | let node1 = treeInfoRef.value.treeRef.store.nodesMap[treeInfo.value.currentNodeKey]; | ||
| 331 | if (node1) { | ||
| 332 | node1.loaded = true; | ||
| 333 | node1.loading = false; | ||
| 334 | } | ||
| 335 | }); | ||
| 336 | resolve(data) | ||
| 337 | } else { | ||
| 338 | ElMessage({ | ||
| 339 | type: 'info', | ||
| 340 | message: res.msg, | ||
| 341 | }) | ||
| 342 | } | ||
| 343 | }) | ||
| 344 | } | ||
| 345 | } | ||
| 346 | } | ||
| 347 | /** 当前选中的树节点数据data */ | ||
| 348 | const lastClickNode: any = ref({ type: 1 }); | ||
| 349 | |||
| 350 | const keyValue = ref("") | ||
| 351 | /** 数据血缘关系图组件 */ | ||
| 352 | const lineageGraph: any = ref(); | ||
| 353 | const diaLogRef = ref() | ||
| 354 | /** 当前操作的血缘节点的相邻节点. */ | ||
| 355 | const neighbors = ref<{ tableName: string, databaseName: string, fieldName?: string }[]>([]) | ||
| 356 | |||
| 357 | /** 点击左侧树节点,更新对应的血缘关系图. */ | ||
| 358 | const nodeClick = (data) => { | ||
| 359 | console.log(data); | ||
| 360 | const ele = <HTMLElement>document.querySelector(".g6-component-contextmenu") | ||
| 361 | if (ele) { | ||
| 362 | ele.style.display = "none" | ||
| 363 | } | ||
| 364 | nextTick(() => { | ||
| 365 | lineageGraph.value?.tooltip1.hide() | ||
| 366 | }) | ||
| 367 | treeInfo.value.currentNodeKey = data.guid; | ||
| 368 | treeInfo.value.expandedKey = <any>[data.guid]; | ||
| 369 | let lastClickType = lastClickNode.value.type; | ||
| 370 | lastClickNode.value = data; | ||
| 371 | if (data.type === 3) {//点击的是表,显示血缘关系。 | ||
| 372 | lastClickType != 3 && (switchFieldLineage.value = false); | ||
| 373 | if (switchFieldLineage.value) { | ||
| 374 | isToggle.value = true; | ||
| 375 | getAllTableFieldLineageMap(); | ||
| 376 | } else { | ||
| 377 | getTableLineageMap() | ||
| 378 | } | ||
| 379 | } else if (data.type === 4) {//点击的是字段。 | ||
| 380 | lastClickType != 4 && (switchFieldLineage.value = true); | ||
| 381 | if (!switchFieldLineage.value) { | ||
| 382 | getTableLineageMap(); | ||
| 383 | } else { | ||
| 384 | isToggle.value = true; | ||
| 385 | getTableFieldLineageMap(); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | const getMetaTree = (params) => { | ||
| 391 | getMetaList(params).then((res) => { | ||
| 392 | if (params.dataType === "METADATA_MODEL") { | ||
| 393 | metaTree.value = res.data.map((item => { | ||
| 394 | item.name = emunData[item.metadataModel] | ||
| 395 | item.guid = emunData[item.metadataModel] | ||
| 396 | item.label = emunData[item.metadataModel] | ||
| 397 | item.value = emunData[item.metadataModel] | ||
| 398 | return item | ||
| 399 | })) || [] | ||
| 400 | dialogInfo.value.contents[0].formInfo.items[0].options = metaTree.value | ||
| 401 | } //查源模型 | ||
| 402 | }) | ||
| 403 | |||
| 404 | } | ||
| 405 | |||
| 406 | /** 选中树节点后自动滚动到可视范围内. */ | ||
| 407 | const scrollToNode = (nodeId) => { | ||
| 408 | nextTick(() => { | ||
| 409 | const nodeElement = treeInfoRef.value.treeRef.$el.querySelector(`[data-key="${nodeId}"]`); | ||
| 410 | if (nodeElement) { | ||
| 411 | nodeElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); | ||
| 412 | } | ||
| 413 | }); | ||
| 414 | } | ||
| 415 | |||
| 416 | /** 处理从详情处跳转而来的默认展示. */ | ||
| 417 | const processRouter = () => { | ||
| 418 | const { guid, databaseName, tableName, databaseChName, databaseGuid, fieldGuid, fieldEnName, set } = useDataMetaStore() | ||
| 419 | let isFL = useDataMetaStore().isFieldLineage; | ||
| 420 | if (fieldGuid) {//查看字段血缘的 | ||
| 421 | nextTick(() => { | ||
| 422 | treeInfo.value.expandedKey = <any>[databaseGuid, guid]; | ||
| 423 | treeInfo.value.currentNodeKey = fieldGuid as string; | ||
| 424 | scrollToNode(fieldGuid); | ||
| 425 | treeInfoRef.value.setCurrentKey(fieldGuid); | ||
| 426 | }) | ||
| 427 | lastClickNode.value = { guid: fieldGuid, tableGuid: guid, databaseGuid: databaseGuid, enName: fieldEnName, tableName, databaseName, type: 4, isLeaf: true, databaseChName }; | ||
| 428 | getTableFieldLineageMap(); | ||
| 429 | set() | ||
| 430 | } else if (guid) { | ||
| 431 | treeInfo.value.currentNodeKey = guid as string; | ||
| 432 | treeInfo.value.expandedKey = <any>[databaseGuid, guid]; | ||
| 433 | lastClickNode.value = { guid: guid, tableName, databaseName, type: 3, databaseChName }; | ||
| 434 | scrollToNode(guid); | ||
| 435 | isFieldLineage.value = isFL; | ||
| 436 | switchFieldLineage.value = isFL; | ||
| 437 | if (isFL) { | ||
| 438 | getAllTableFieldLineageMap(); | ||
| 439 | } else { | ||
| 440 | getTableLineageMap() | ||
| 441 | } | ||
| 442 | set() | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | onActivated(() => { | ||
| 447 | if (!allTreeData.value?.length) { | ||
| 448 | return; | ||
| 449 | } | ||
| 450 | processRouter(); | ||
| 451 | }); | ||
| 452 | |||
| 453 | onBeforeMount(async () => { | ||
| 454 | await getTreeData() | ||
| 455 | getMetaTree({ dataType: 'METADATA_MODEL', metadataModel: '1' }) | ||
| 456 | processRouter(); | ||
| 457 | }) | ||
| 458 | |||
| 459 | /** 处理血缘节点的右键菜单事件. */ | ||
| 460 | const handleTableContextMenu = (tableGuid, tableName, database, btnType, vertexId, databaseChName, chTable, neighbor, fieldGuid, fieldEnName, fieldChName) => { | ||
| 461 | // 当删除边的时候,第一个参数为euid 第二个是标识符 true | ||
| 462 | if (tableName === true) { | ||
| 463 | ElMessageBox.confirm('此操作将永久删除, 是否继续?', "提示", { | ||
| 464 | confirmButtonText: "确定", | ||
| 465 | cancelButtonText: "取消", | ||
| 466 | type: 'warning', | ||
| 467 | appendTo: lineageGraph.value.containerRef | ||
| 468 | }).then(() => { | ||
| 469 | delLineAge(tableGuid).then((res: any) => { | ||
| 470 | if (res.code == proxy.$passCode) { | ||
| 471 | ElMessage({ | ||
| 472 | type: "success", | ||
| 473 | message: "删除成功", | ||
| 474 | appendTo: lineageGraph.value.containerRef | ||
| 475 | }); | ||
| 476 | if (lastClickNode.value.type == 4) { | ||
| 477 | if (isFieldLineage.value) { | ||
| 478 | getTableFieldLineageMap(); | ||
| 479 | } else { | ||
| 480 | getTableLineageMap(); | ||
| 481 | } | ||
| 482 | } else { | ||
| 483 | if (isFieldLineage.value) { | ||
| 484 | getAllTableFieldLineageMap(); | ||
| 485 | } else { | ||
| 486 | getTableLineageMap(); | ||
| 487 | } | ||
| 488 | } | ||
| 489 | } else { | ||
| 490 | ElMessage({ | ||
| 491 | type: "error", | ||
| 492 | message: res.msg, | ||
| 493 | appendTo: lineageGraph.value.containerRef | ||
| 494 | }); | ||
| 495 | } | ||
| 496 | }) | ||
| 497 | }).catch(() => { | ||
| 498 | ElMessage({ | ||
| 499 | type: 'info', | ||
| 500 | message: '已取消删除', | ||
| 501 | appendTo: lineageGraph.value.containerRef | ||
| 502 | }); | ||
| 503 | }); | ||
| 504 | return | ||
| 505 | } | ||
| 506 | |||
| 507 | const databaseGuid = allTreeData.value[0].children?.find(child => child.name == databaseChName)?.guid; | ||
| 508 | |||
| 509 | neighbor.push({ tableName: tableName, databaseName: database, fieldName: fieldEnName }); | ||
| 510 | neighbors.value = neighbor; | ||
| 511 | currtableGuid.value = tableGuid | ||
| 512 | if (btnType == 1) {//查看元数据详情 | ||
| 513 | lineageGraph.value.handleEdit({});//先退出全屏再跳转,否则回来查看时就没有了。 | ||
| 514 | checkTableData(tableGuid).then((res: any) => { | ||
| 515 | if (res.code === proxy.$passCode) { | ||
| 516 | if (res.data) { | ||
| 517 | router.push({ | ||
| 518 | path: '/data-meta/metadata-query/meta-sheet', | ||
| 519 | query: { | ||
| 520 | id: tableGuid, | ||
| 521 | name: chTable || tableName | ||
| 522 | } | ||
| 523 | }) | ||
| 524 | } else { | ||
| 525 | ElMessage.warning("元数据详情不存在") | ||
| 526 | } | ||
| 527 | } | ||
| 528 | }) | ||
| 529 | |||
| 530 | } else if (btnType == 2) {//查看血缘。 | ||
| 531 | if (!isFieldLineage.value) { | ||
| 532 | treeInfo.value.currentNodeKey = tableGuid; | ||
| 533 | let node = treeInfoRef.value.treeRef.store.nodesMap[databaseGuid]; | ||
| 534 | node?.expand(); | ||
| 535 | let node1 = treeInfoRef.value.treeRef.store.nodesMap[tableGuid]; | ||
| 536 | node1?.expand(); | ||
| 537 | treeInfo.value.expandedKey = <any>['0', databaseGuid, tableGuid]; | ||
| 538 | nodeClick({ guid: tableGuid, tableName: tableName, databaseName: database, type: 3 }) | ||
| 539 | nextTick(() => { | ||
| 540 | scrollToNode(tableGuid); | ||
| 541 | }) | ||
| 542 | } else { | ||
| 543 | treeInfo.value.currentNodeKey = fieldGuid; | ||
| 544 | let node = treeInfoRef.value.treeRef.store.nodesMap[databaseGuid]; | ||
| 545 | node?.expand(); | ||
| 546 | let node1 = treeInfoRef.value.treeRef.store.nodesMap[tableGuid]; | ||
| 547 | node1?.expand(); | ||
| 548 | treeInfo.value.expandedKey = <any>['0', databaseGuid, tableGuid, fieldGuid]; | ||
| 549 | nextTick(() => { | ||
| 550 | scrollToNode(fieldGuid); | ||
| 551 | }) | ||
| 552 | nodeClick({ guid: fieldGuid, tableName: tableName, databaseName: database, type: 4, tableGuid: tableGuid, isLeaf: true, enName: fieldEnName, chName: fieldChName }) | ||
| 553 | } | ||
| 554 | } else if (btnType == 3) { | ||
| 555 | dialogInfo.value.header.title = "添加上游节点" | ||
| 556 | dialogInfo.value.visible = true; | ||
| 557 | dialogInfo.value.addType = isFieldLineage.value ? 'field' : 'table'; | ||
| 558 | if (isFieldLineage.value) { | ||
| 559 | dialogInfo.value.contents[0].formInfo.items[1].visible = false; | ||
| 560 | dialogInfo.value.contents[0].formInfo.items[2].visible = true; | ||
| 561 | } else { | ||
| 562 | dialogInfo.value.contents[0].formInfo.items[1].visible = true; | ||
| 563 | dialogInfo.value.contents[0].formInfo.items[2].visible = false; | ||
| 564 | } | ||
| 565 | dialogInfo.value.contents[0].formInfo.items[1].options = []; | ||
| 566 | dialogInfo.value.contents[0].formInfo.items[2].options = []; | ||
| 567 | formLine.value = { upOrDown: "up", vertexId, database, databaseName: database, databaseGuid, tableGuid, databaseChName, chTable, tableName: tableName, currEnName: fieldEnName, currChName: fieldChName, currDsChName: databaseChName } | ||
| 568 | nextTick(() => { | ||
| 569 | const ele = document.querySelector(".createNode") | ||
| 570 | if (ele) { | ||
| 571 | lineageGraph.value.containerRef.appendChild(ele) | ||
| 572 | |||
| 573 | } | ||
| 574 | }) | ||
| 575 | } else if (btnType == 4) { | ||
| 576 | dialogInfo.value.header.title = "添加下游节点" | ||
| 577 | dialogInfo.value.visible = true | ||
| 578 | dialogInfo.value.addType = isFieldLineage.value ? 'field' : 'table'; | ||
| 579 | if (isFieldLineage.value) { | ||
| 580 | dialogInfo.value.contents[0].formInfo.items[1].visible = false; | ||
| 581 | dialogInfo.value.contents[0].formInfo.items[2].visible = true; | ||
| 582 | } else { | ||
| 583 | dialogInfo.value.contents[0].formInfo.items[1].visible = true; | ||
| 584 | dialogInfo.value.contents[0].formInfo.items[2].visible = false; | ||
| 585 | } | ||
| 586 | dialogInfo.value.contents[0].formInfo.items[1].options = []; | ||
| 587 | dialogInfo.value.contents[0].formInfo.items[2].options = []; | ||
| 588 | formLine.value = { upOrDown: "down", vertexId, database, databaseName: database, databaseGuid, tableGuid, databaseChName, chTable, tableName, currEnName: fieldEnName, currChName: fieldChName, currDsChName: databaseChName } | ||
| 589 | nextTick(() => { | ||
| 590 | const ele = document.querySelector(".createNode") | ||
| 591 | if (ele) { | ||
| 592 | lineageGraph.value.containerRef.appendChild(ele) | ||
| 593 | } | ||
| 594 | }) | ||
| 595 | } else if (btnType == 5) { | ||
| 596 | ElMessageBox.confirm('此操作将永久删除, 是否继续?', "提示", { | ||
| 597 | confirmButtonText: "确定", | ||
| 598 | cancelButtonText: "取消", | ||
| 599 | type: 'warning', | ||
| 600 | appendTo: lineageGraph.value.containerRef | ||
| 601 | }).then(() => { | ||
| 602 | delLineageTable({ vertexId: vertexId }).then((res: any) => { | ||
| 603 | if (res.code == proxy.$passCode) { | ||
| 604 | ElMessage({ | ||
| 605 | type: "success", | ||
| 606 | message: "删除成功", | ||
| 607 | appendTo: lineageGraph.value.containerRef | ||
| 608 | }); | ||
| 609 | if (lastClickNode.value.type == 4) { | ||
| 610 | if (isFieldLineage.value) { | ||
| 611 | getTableFieldLineageMap(); | ||
| 612 | } else { | ||
| 613 | getTableLineageMap(); | ||
| 614 | } | ||
| 615 | } else { | ||
| 616 | if (isFieldLineage.value) { | ||
| 617 | getAllTableFieldLineageMap(); | ||
| 618 | } else { | ||
| 619 | getTableLineageMap(); | ||
| 620 | } | ||
| 621 | } | ||
| 622 | } else { | ||
| 623 | ElMessage({ | ||
| 624 | type: "error", | ||
| 625 | message: res.msg, | ||
| 626 | appendTo: lineageGraph.value.containerRef | ||
| 627 | }); | ||
| 628 | } | ||
| 629 | }) | ||
| 630 | }).catch(() => { | ||
| 631 | ElMessage({ | ||
| 632 | type: 'info', | ||
| 633 | message: '已取消删除', | ||
| 634 | appendTo: lineageGraph.value.containerRef | ||
| 635 | }); | ||
| 636 | }); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | |||
| 640 | /** 添加上下游节点对话框的确定取消事件处理. */ | ||
| 641 | const dialogBtnClick = (btn, info) => { | ||
| 642 | if (btn.value == 'submit') { | ||
| 643 | let params = {} | ||
| 644 | params = { | ||
| 645 | ...formLine.value, guid: currtableGuid.value | ||
| 646 | } | ||
| 647 | if (lastClickNode.value.type == 4) { | ||
| 648 | Object.assign(params, { | ||
| 649 | currentNode: { | ||
| 650 | enName: lastClickNode.value.enName, | ||
| 651 | chName: lastClickNode.value.chName, | ||
| 652 | tableName: lastClickNode.value.tableName, | ||
| 653 | tableGuid: lastClickNode.value.tableGuid, | ||
| 654 | databaseGuid: lastClickNode.value.databaseGuid, | ||
| 655 | databaseChName: formLine.value.databaseChName, | ||
| 656 | databaseName: lastClickNode.value.databaseName, | ||
| 657 | tableChName: formLine.value.chTable | ||
| 658 | } | ||
| 659 | }); | ||
| 660 | } else { | ||
| 661 | Object.assign(params, { | ||
| 662 | currentNode: { | ||
| 663 | enName: formLine.value.currEnName, | ||
| 664 | chName: formLine.value.currChName, | ||
| 665 | tableName: lastClickNode.value.tableName, | ||
| 666 | tableGuid: lastClickNode.value.guid, | ||
| 667 | databaseGuid: lastClickNode.value.parentGuid, | ||
| 668 | databaseChName: formLine.value.currDsChName, | ||
| 669 | databaseName: lastClickNode.value.databaseName, | ||
| 670 | tableChName: formLine.value.chTable | ||
| 671 | } | ||
| 672 | }); | ||
| 673 | } | ||
| 674 | saveLineageField(params).then().then((res: any) => { | ||
| 675 | if (res.code === proxy.$passCode) { | ||
| 676 | ElMessage({ | ||
| 677 | type: "success", | ||
| 678 | message: "添加节点成功!", | ||
| 679 | appendTo: lineageGraph.value.containerRef | ||
| 680 | }) | ||
| 681 | dialogInfo.value.visible = false; | ||
| 682 | if (lastClickNode.value.type == 4) { | ||
| 683 | if (isFieldLineage.value) { | ||
| 684 | getTableFieldLineageMap(); | ||
| 685 | } else { | ||
| 686 | getTableLineageMap(); | ||
| 687 | } | ||
| 688 | } else { | ||
| 689 | if (isFieldLineage.value) { | ||
| 690 | getAllTableFieldLineageMap(); | ||
| 691 | } else { | ||
| 692 | getTableLineageMap(); | ||
| 693 | } | ||
| 694 | } | ||
| 695 | } else { | ||
| 696 | ElMessage({ | ||
| 697 | type: 'error', | ||
| 698 | message: res.msg, | ||
| 699 | appendTo: lineageGraph.value.containerRef | ||
| 700 | }) | ||
| 701 | } | ||
| 702 | }).catch((res: any) => { | ||
| 703 | ElMessage({ | ||
| 704 | type: 'error', | ||
| 705 | message: res.msg, | ||
| 706 | appendTo: lineageGraph.value.containerRef | ||
| 707 | }) | ||
| 708 | }); | ||
| 709 | } else if (btn.value == 'cancel') { | ||
| 710 | dialogInfo.value.visible = false; | ||
| 711 | } | ||
| 712 | }; | ||
| 713 | |||
| 714 | const metaTableList = ref([]); | ||
| 715 | |||
| 716 | const nodeLoad = (node, resolve, item) => { | ||
| 717 | const data = node.data; | ||
| 718 | if (item.field == 'databaseGuid') { | ||
| 719 | if (node.level === 0) { | ||
| 720 | return resolve(metaTree.value) | ||
| 721 | } else if (node.level === 1) { | ||
| 722 | getMetaList({ dataType: "DATABASE", metadataModel: data.metadataModel }).then((res: any) => { | ||
| 723 | if (res.code == proxy.$passCode) { | ||
| 724 | const children = res.data.map((item) => { | ||
| 725 | item.isLeaf = true; | ||
| 726 | item.name = item.databaseNameZh | ||
| 727 | return item | ||
| 728 | }) || [] | ||
| 729 | resolve(children) | ||
| 730 | } else { | ||
| 731 | ElMessage.error(res.msg); | ||
| 732 | resolve([]); | ||
| 733 | } | ||
| 734 | }) | ||
| 735 | } else if (node.level === 2) { | ||
| 736 | resolve([]) | ||
| 737 | } | ||
| 738 | } else { | ||
| 739 | if (node.level === 0) { | ||
| 740 | return resolve(metaTableList.value) | ||
| 741 | } else if (node.level === 1) { | ||
| 742 | getMetaTableField({ tableGuid: data.guid }).then((res: any) => { | ||
| 743 | if (res.code == proxy.$passCode) { | ||
| 744 | const dataArray = res.data ?? [] | ||
| 745 | dataArray.map(item => { | ||
| 746 | item.label = item.chName ? (item.chName + "(" + item.enName + ")") : item.enName; | ||
| 747 | item.value = item.enName; | ||
| 748 | item.name = item.chName || item.enName; | ||
| 749 | item.databaseName = data.databaseName; | ||
| 750 | item.databaseChName = data.databaseChName; | ||
| 751 | item.tableName = data.tableName; | ||
| 752 | item.tableChName = data.tableChName; | ||
| 753 | item.tableGuid = data.guid; | ||
| 754 | item.type = 4 | ||
| 755 | item.dataTypeCode = item.fieldType | ||
| 756 | item.isLeaf = true | ||
| 757 | item.businessDefDesc = item.comment | ||
| 758 | if (neighbors.value.some(n => n.databaseName === item.databaseName && n.tableName === item.tableName && n.fieldName == item.enName)) { | ||
| 759 | item.disabled = true; | ||
| 760 | } | ||
| 761 | }) | ||
| 762 | resolve(dataArray); | ||
| 763 | } else { | ||
| 764 | ElMessage.error(res.msg); | ||
| 765 | } | ||
| 766 | }); | ||
| 767 | } | ||
| 768 | } | ||
| 769 | } | ||
| 770 | const treeSelectNodeChange = (node, item) => { | ||
| 771 | if (item.field == 'tableField' && node.isLeaf) { | ||
| 772 | formLine.value.enName = node.enName; | ||
| 773 | formLine.value.chName = node.chName; | ||
| 774 | formLine.value.tableName = node.tableName; | ||
| 775 | formLine.value.tableChName = node.tableChName; | ||
| 776 | formLine.value.tableGuid = node.tableGuid; | ||
| 777 | return; | ||
| 778 | } | ||
| 779 | if (!node.databaseNameEn || !node.isLeaf) { | ||
| 780 | return | ||
| 781 | } | ||
| 782 | console.log(diaLogRef.value.dialogFormRef[0].formInline) | ||
| 783 | diaLogRef.value.dialogFormRef[0].formInline.tableName = ""; | ||
| 784 | diaLogRef.value.dialogFormRef[0].formInline.tableField = ''; | ||
| 785 | dialogInfo.value.contents[0].formInfo.items[1].options = []; | ||
| 786 | dialogInfo.value.contents[0].formInfo.items[2].options = []; | ||
| 787 | formLine.value.databaseName = node.databaseNameEn || "" | ||
| 788 | formLine.value.databaseChName = node.databaseNameZh || ""; | ||
| 789 | formLine.value.databaseGuid = node.guid; | ||
| 790 | getMetaList({ dataType: "TABLE", dataSourceGuid: node.guid }).then((res: any) => { | ||
| 791 | if (res.code == proxy.$passCode) { | ||
| 792 | if (dialogInfo.value.addType != 'table') { | ||
| 793 | dialogInfo.value.contents[0].formInfo.items[2].options = metaTableList.value = res.data.map((item) => { | ||
| 794 | item.label = item.tableChName + "(" + item.tableName + ")" | ||
| 795 | item.value = item.tableName; | ||
| 796 | item.isLeaf = false; | ||
| 797 | let nbor = neighbors.value.at(-1); | ||
| 798 | if (nbor?.databaseName == node.databaseNameEn && nbor?.tableName == item.tableName) { | ||
| 799 | item.isLeaf = true; | ||
| 800 | item.disabled = true; | ||
| 801 | } else { | ||
| 802 | item.disabled = false; | ||
| 803 | } | ||
| 804 | return item | ||
| 805 | }) | ||
| 806 | } else { | ||
| 807 | dialogInfo.value.contents[0].formInfo.items[1].options = res.data.map((item) => { | ||
| 808 | item.label = item.tableChName + "(" + item.tableName + ")" | ||
| 809 | item.value = item.tableName | ||
| 810 | |||
| 811 | neighbors.value.forEach((item1) => { | ||
| 812 | if (item1.databaseName === item.databaseName && item1.tableName === item.tableName) { | ||
| 813 | item.disabled = true | ||
| 814 | } | ||
| 815 | }) | ||
| 816 | return item | ||
| 817 | }) | ||
| 818 | } | ||
| 819 | } else { | ||
| 820 | ElMessage.error(res.msg); | ||
| 821 | } | ||
| 822 | |||
| 823 | }) | ||
| 824 | } | ||
| 825 | |||
| 826 | const selectChange = (val, row, info) => { | ||
| 827 | formLine.value.databaseGuid = info.databaseGuid; | ||
| 828 | if (dialogInfo.value.addType == 'table') { | ||
| 829 | formLine.value.tableName = info.tableName | ||
| 830 | if (row.field === "tableName") { | ||
| 831 | const option = row.options.find((item) => item.tableName === val) | ||
| 832 | formLine.value.tableChName = option.tableChName | ||
| 833 | } | ||
| 834 | } else { | ||
| 835 | // formLine.value.enName = info.tableName | ||
| 836 | } | ||
| 837 | } | ||
| 838 | const file: any = ref({}) | ||
| 839 | const handleSave = (file1, fullRef) => { | ||
| 840 | file.value = file1 | ||
| 841 | dialogInfo1.value.visible = true | ||
| 842 | nextTick(() => { | ||
| 843 | const ele = document.querySelector(".saveDialog") | ||
| 844 | if (ele) { | ||
| 845 | fullRef.appendChild(ele) | ||
| 846 | } | ||
| 847 | }) | ||
| 848 | } | ||
| 849 | const pageSave = () => { | ||
| 850 | const analysisReportName = reportDialogRef.value.dialogFormRef[0].formInline.analysisReportName; | ||
| 851 | if (!analysisReportName) { | ||
| 852 | ElMessage({ | ||
| 853 | type: "error", | ||
| 854 | message: "血缘关系名称不能为空!", | ||
| 855 | appendTo: lineageGraph.value.containerRef | ||
| 856 | }) | ||
| 857 | return | ||
| 858 | } | ||
| 859 | |||
| 860 | let formData = new FormData(); | ||
| 861 | formData.append('file', file.value); | ||
| 862 | formData.append('fileName', `${analysisReportName}.png`); | ||
| 863 | getFileUrl(formData).then((res) => { | ||
| 864 | saveMetaReportAnalysis({ | ||
| 865 | table: lastClickNode.value.tableName, | ||
| 866 | database: lastClickNode.value.databaseName, | ||
| 867 | analysisReportUrl: res.data, | ||
| 868 | analysisReportName: analysisReportName, | ||
| 869 | databaseChName: lastClickNode.value.databaseChName | ||
| 870 | }).then((res: any) => { | ||
| 871 | if (res.code == proxy.$passCode) { | ||
| 872 | ElMessage({ | ||
| 873 | type: "success", | ||
| 874 | message: "保存成功", | ||
| 875 | appendTo: lineageGraph.value.containerRef | ||
| 876 | }) | ||
| 877 | dialogInfo1.value.visible = false | ||
| 878 | } else { | ||
| 879 | ElMessage({ | ||
| 880 | type: "error", | ||
| 881 | message: res.msg, | ||
| 882 | appendTo: lineageGraph.value.containerRef | ||
| 883 | }) | ||
| 884 | } | ||
| 885 | }) | ||
| 886 | }).catch((res) => { | ||
| 887 | ElMessage.error(res.msg) | ||
| 888 | }) | ||
| 889 | } | ||
| 890 | |||
| 891 | const formItems1: any = ref([ | ||
| 892 | { | ||
| 893 | label: '血缘关系名称', | ||
| 894 | type: 'input', | ||
| 895 | placeholder: '请输入', | ||
| 896 | field: 'analysisReportName', | ||
| 897 | default: '', | ||
| 898 | clearable: true, | ||
| 899 | required: true, | ||
| 900 | }, | ||
| 901 | ]) | ||
| 902 | const formRules1: any = ref({ | ||
| 903 | analysisReportName: [ | ||
| 904 | { | ||
| 905 | required: true, | ||
| 906 | message: "请填写血缘关系名称", | ||
| 907 | trigger: "blur", | ||
| 908 | }, | ||
| 909 | ], | ||
| 910 | }) | ||
| 911 | const formInfo1 = ref({ | ||
| 912 | type: 'form', | ||
| 913 | title: '', | ||
| 914 | formInfo: { | ||
| 915 | id: 'add-dict-form1', | ||
| 916 | items: formItems1.value, | ||
| 917 | rules: formRules1.value | ||
| 918 | } | ||
| 919 | }) | ||
| 920 | const reportDialogRef = ref(); | ||
| 921 | const dialogInfo1 = ref({ | ||
| 922 | visible: false, | ||
| 923 | size: 500, | ||
| 924 | direction: "column", | ||
| 925 | modalClass: "saveDialog", | ||
| 926 | header: { | ||
| 927 | title: "保存为血缘关系图片", | ||
| 928 | }, | ||
| 929 | type: '', | ||
| 930 | contents: [ | ||
| 931 | formInfo1.value, | ||
| 932 | ], | ||
| 933 | footer: { | ||
| 934 | btns: [ | ||
| 935 | { type: "default", label: "取消", value: "cancel" }, | ||
| 936 | { type: "primary", label: "保存", value: "submit" }, | ||
| 937 | ], | ||
| 938 | }, | ||
| 939 | }); | ||
| 940 | |||
| 941 | const dialogBtnClick1 = (btn, scope) => { | ||
| 942 | if (btn.value === "cancel") { | ||
| 943 | dialogInfo1.value.visible = false | ||
| 944 | } else { | ||
| 945 | pageSave() | ||
| 946 | } | ||
| 947 | } | ||
| 948 | /** 是否显示英文名称,默认不显示。 */ | ||
| 949 | const isCh = ref(false); | ||
| 950 | |||
| 951 | /** 是否切换为纵向布局。 */ | ||
| 952 | const isToggle = ref(true) | ||
| 953 | |||
| 954 | /** 获取对应表的血缘关系。 */ | ||
| 955 | const getTableLineageMap = () => { | ||
| 956 | lineageLoading.value = true; | ||
| 957 | isFieldLineage.value = false; | ||
| 958 | getTableLineage({ guid: lastClickNode.value.isLeaf ? lastClickNode.value.tableGuid : lastClickNode.value.guid }).then((res: any) => { | ||
| 959 | if (res.code == proxy.$passCode) { | ||
| 960 | let data1 = res.data || []; | ||
| 961 | lineageData.value = data1; | ||
| 962 | nextTick(() => { | ||
| 963 | if (lineageData.value.length > 0) { | ||
| 964 | lineageGraph.value.updateLayout(); | ||
| 965 | lineageLoading.value = false; | ||
| 966 | } else { | ||
| 967 | lineageLoading.value = false; | ||
| 968 | } | ||
| 969 | |||
| 970 | }); | ||
| 971 | } else { | ||
| 972 | lineageLoading.value = false; | ||
| 973 | ElMessage({ | ||
| 974 | type: 'error', | ||
| 975 | message: res.msg, | ||
| 976 | appendTo: lineageGraph.value.containerRef | ||
| 977 | }) | ||
| 978 | } | ||
| 979 | }) | ||
| 980 | } | ||
| 981 | |||
| 982 | /** 获取指定表字段的血缘关系 */ | ||
| 983 | const getTableFieldLineageMap = () => { | ||
| 984 | lineageLoading.value = true; | ||
| 985 | isFieldLineage.value = true; | ||
| 986 | getTableFieldLineage({ guid: lastClickNode.value.guid }).then((res: any) => { | ||
| 987 | if (res.code == proxy.$passCode) { | ||
| 988 | let data1 = res.data || []; | ||
| 989 | lineageData.value = data1; | ||
| 990 | nextTick(() => { | ||
| 991 | if (lineageData.value.length > 0) { | ||
| 992 | lineageGraph.value.updateLayout(); | ||
| 993 | lineageLoading.value = false; | ||
| 994 | } else { | ||
| 995 | lineageLoading.value = false; | ||
| 996 | } | ||
| 997 | }); | ||
| 998 | } else { | ||
| 999 | lineageLoading.value = false; | ||
| 1000 | ElMessage({ | ||
| 1001 | type: 'error', | ||
| 1002 | message: res.msg, | ||
| 1003 | appendTo: lineageGraph.value.containerRef | ||
| 1004 | }) | ||
| 1005 | } | ||
| 1006 | }) | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | /** 获取指定表的所有字段血缘关系。 */ | ||
| 1010 | const getAllTableFieldLineageMap = () => { | ||
| 1011 | lineageLoading.value = true; | ||
| 1012 | isFieldLineage.value = true; | ||
| 1013 | getTableAllFieldLineage({ databaseName: lastClickNode.value.databaseName, tableName: lastClickNode.value.tableName }).then((res: any) => { | ||
| 1014 | if (res.code == proxy.$passCode) { | ||
| 1015 | let data1 = res.data || []; | ||
| 1016 | lineageData.value = data1; | ||
| 1017 | isFieldLineage.value = true; | ||
| 1018 | nextTick(() => { | ||
| 1019 | if (lineageData.value.length > 0) { | ||
| 1020 | lineageGraph.value.updateLayout(); | ||
| 1021 | lineageLoading.value = false; | ||
| 1022 | } else { | ||
| 1023 | lineageLoading.value = false; | ||
| 1024 | } | ||
| 1025 | }); | ||
| 1026 | } else { | ||
| 1027 | lineageLoading.value = false; | ||
| 1028 | ElMessage({ | ||
| 1029 | type: 'error', | ||
| 1030 | message: res.msg, | ||
| 1031 | }) | ||
| 1032 | } | ||
| 1033 | }) | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | /** 处理刷新按钮。 */ | ||
| 1037 | const handleRefres = () => { | ||
| 1038 | if (lastClickNode.value.type === 3) {//点击的是表,显示血缘关系。 | ||
| 1039 | if (switchFieldLineage.value) { | ||
| 1040 | getAllTableFieldLineageMap(); | ||
| 1041 | } else { | ||
| 1042 | getTableLineageMap() | ||
| 1043 | } | ||
| 1044 | } else if (lastClickNode.value.type === 4) {//点击的是字段。 | ||
| 1045 | getTableFieldLineageMap(); | ||
| 1046 | } | ||
| 1047 | } | ||
| 1048 | /** 处理中英文切换。 */ | ||
| 1049 | const handleChOrEn = (flag) => { | ||
| 1050 | isCh.value = flag | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | /** 处理上下布局切换。 */ | ||
| 1054 | const handleToggle = () => { | ||
| 1055 | isToggle.value = !isToggle.value | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | /** 处理字段血缘开关开启关闭。 */ | ||
| 1059 | const handleLineageSwitchChange = (val, type) => { | ||
| 1060 | switchFieldLineage.value = val; | ||
| 1061 | if (val) { | ||
| 1062 | isToggle.value = true; | ||
| 1063 | getAllTableFieldLineageMap(); | ||
| 1064 | } else { | ||
| 1065 | getTableLineageMap(); | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | </script> | ||
| 1069 | |||
| 1070 | <template> | ||
| 1071 | <div class="container_wrap full flex"> | ||
| 1072 | <div class="aside_wrap"> | ||
| 1073 | <div class="aside_title">数据库目录列表</div> | ||
| 1074 | <Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" :key="keyValue" @loadNode="loadTreeNode" | ||
| 1075 | @filterTree="handleFilterTree" /> | ||
| 1076 | </div> | ||
| 1077 | <div class="main_wrap"> | ||
| 1078 | <LineageGraph v-show="lastClickNode?.type == 3 || lastClickNode?.type == 4" ref="lineageGraph" layout="vertical" | ||
| 1079 | :lineageData="lineageData" :isFieldLineage="isFieldLineage" v-if="lineageData.length > 0" | ||
| 1080 | v-loading="lineageLoading" :primary-table="lastClickNode?.tableName" | ||
| 1081 | :primary-field="lastClickNode?.type == 4 ? lastClickNode?.enName : ''" :isViewTable="lastClickNode?.type == 3" | ||
| 1082 | :is-detail="false" :primaryDatabase="lastClickNode?.databaseName" @tableContextMenu="handleTableContextMenu" | ||
| 1083 | @handleSave="handleSave" @handleRefres="handleRefres" @handleLineageSwitchChange="handleLineageSwitchChange" | ||
| 1084 | @handleChOrEn=handleChOrEn :isCh="isCh" @handleToggle="handleToggle" :isToggle="isToggle" /> | ||
| 1085 | <div v-show="lastClickNode && lastClickNode?.type !== 3 && lastClickNode?.type !== 4" class="main-placeholder"> | ||
| 1086 | <img width="210" height="100" src="../../../public/swzl_logo.png"> | ||
| 1087 | </div> | ||
| 1088 | <Dialog :dialogInfo="dialogInfo" ref="diaLogRef" class="timedDia" @btnClick="dialogBtnClick" | ||
| 1089 | @selectChange="selectChange" @treeSelectLoad="nodeLoad" @treeSelectNodeChange="treeSelectNodeChange" /> | ||
| 1090 | </div> | ||
| 1091 | <Dialog ref="reportDialogRef" class="createNode" :dialogInfo="dialogInfo1" @btnClick="dialogBtnClick1" /> | ||
| 1092 | </div> | ||
| 1093 | </template> | ||
| 1094 | |||
| 1095 | <style scoped lang="scss"> | ||
| 1096 | .container_wrap { | ||
| 1097 | |||
| 1098 | .aside_wrap { | ||
| 1099 | width: 200px; | ||
| 1100 | margin-right: 1px; | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | .main_wrap { | ||
| 1104 | :deep(.canvas-wrapper) { | ||
| 1105 | background-color: #f7f7f9; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | .main-placeholder { | ||
| 1109 | height: 100%; | ||
| 1110 | display: flex; | ||
| 1111 | justify-content: center; | ||
| 1112 | align-items: center; | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | } | ||
| 1117 | |||
| 1118 | .container_wrap.flex .main_wrap { | ||
| 1119 | padding: 0px; | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | .tree_panel { | ||
| 1123 | height: calc(100% - 36px); | ||
| 1124 | padding-top: 0; | ||
| 1125 | |||
| 1126 | :deep(.el-tree) { | ||
| 1127 | margin: 0; | ||
| 1128 | overflow: hidden auto; | ||
| 1129 | } | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | .card-noData { | ||
| 1133 | height: 100%; | ||
| 1134 | width: 100%; | ||
| 1135 | background: #fafafa; | ||
| 1136 | display: flex; | ||
| 1137 | flex-direction: column; | ||
| 1138 | justify-content: center; | ||
| 1139 | align-items: center; | ||
| 1140 | color: #909399; | ||
| 1141 | font-size: 14px; | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | :deep(.el-form .el-form-item) { | ||
| 1145 | width: calc(100%); | ||
| 1146 | // margin-right: 8px; | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | :deep(.el-message) { | ||
| 1150 | position: fixed; | ||
| 1151 | /* 使用fixed或absolute定位 */ | ||
| 1152 | z-index: 10000; | ||
| 1153 | /* 设置一个较高的z-index值确保在最上层显示 */ | ||
| 1154 | } | ||
| 1155 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_meta/changeDetection.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: changeDetection | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="changeDetection"> | ||
| 6 | |||
| 7 | import { ref } from 'vue'; | ||
| 8 | import { ElMessage } from "element-plus"; | ||
| 9 | import Tree from '@/components/Tree/index.vue'; | ||
| 10 | import { | ||
| 11 | getMetaTreeData, | ||
| 12 | getTaskChangeList, | ||
| 13 | getMetaChangeList, | ||
| 14 | getMetaChangeRecord, | ||
| 15 | getMetacompareList, | ||
| 16 | checkTableData, | ||
| 17 | syncChangeDetail | ||
| 18 | } from '@/api/modules/dataMetaService'; | ||
| 19 | import TableTools from '@/components/Tools/table_tools.vue'; | ||
| 20 | import { useRouter, useRoute } from "vue-router"; | ||
| 21 | |||
| 22 | const { proxy } = getCurrentInstance() as any; | ||
| 23 | |||
| 24 | const router = useRouter(); | ||
| 25 | |||
| 26 | const treeInfo:any = ref({ | ||
| 27 | id: "data-pickup-tree", | ||
| 28 | filter: true, | ||
| 29 | queryValue: "", | ||
| 30 | queryPlaceholder: "输入库/表名称搜索", | ||
| 31 | props: { | ||
| 32 | label: "name", | ||
| 33 | value: "guid", | ||
| 34 | }, | ||
| 35 | nodeKey: 'guid', | ||
| 36 | expandedKey: [], | ||
| 37 | currentNodeKey: '', | ||
| 38 | expandOnNodeClick: false, | ||
| 39 | data: [], | ||
| 40 | loading: false | ||
| 41 | }); | ||
| 42 | const treeData = ref([]) | ||
| 43 | const getTreeData = () => { | ||
| 44 | treeInfo.value.loading = true | ||
| 45 | let params = {} | ||
| 46 | getMetaTreeData(params).then((res: any) => { | ||
| 47 | if (res.code == proxy.$passCode) { | ||
| 48 | // let treeData: any = res.data?.children || []; | ||
| 49 | const data = res.data || {} | ||
| 50 | let treeData: any = Object.keys(data).length ? [data] : []; | ||
| 51 | treeInfo.value.data = treeData; | ||
| 52 | if (treeData.length) { | ||
| 53 | tableSearchValue.value.taskGuid = treeData[0].guid | ||
| 54 | treeInfo.value.currentNodeKey = treeData[0].guid; | ||
| 55 | treeInfo.value.expandedKey = treeInfo.value.expandedKey.length == 0 ? [treeData[0].guid] : treeInfo.value.expandedKey | ||
| 56 | } | ||
| 57 | } else { | ||
| 58 | ElMessage.error(res.msg); | ||
| 59 | } | ||
| 60 | treeInfo.value.loading = false | ||
| 61 | }).catch(() => { | ||
| 62 | treeInfo.value.loading = false | ||
| 63 | }) | ||
| 64 | } | ||
| 65 | |||
| 66 | const nodeClick = (data) => { | ||
| 67 | metaTableValue.value.dataSourceGuid = "" | ||
| 68 | tableSearchValue.value.dataSourceName = "" | ||
| 69 | tableSearchValue.value.tableGuid = "" | ||
| 70 | metaTableValue.value.tableGuid = "" | ||
| 71 | if(data.type === 1){ | ||
| 72 | if(activeTabName.value==="task"){ | ||
| 73 | getTaskChangeTableData() | ||
| 74 | } else { | ||
| 75 | getMetaChangeTableData() | ||
| 76 | } | ||
| 77 | } else if (data.type === 3) {//点击的是表,显示血缘关系。 | ||
| 78 | metaTableValue.value.dataSourceGuid = "" | ||
| 79 | tableSearchValue.value.dataSourceName = "" | ||
| 80 | tableSearchValue.value.tableGuid = data.guid | ||
| 81 | metaTableValue.value.tableGuid = data.guid | ||
| 82 | if(activeTabName.value==="task"){ | ||
| 83 | |||
| 84 | getTaskChangeTableData() | ||
| 85 | } else { | ||
| 86 | getMetaChangeTableData() | ||
| 87 | } | ||
| 88 | |||
| 89 | } else {//点击的不是表。 | ||
| 90 | tableSearchValue.value.tableGuid = "" | ||
| 91 | metaTableValue.value.tableGuid = "" | ||
| 92 | metaTableValue.value.dataSourceGuid = data.guid | ||
| 93 | tableSearchValue.value.dataSourceName = data.name | ||
| 94 | if(activeTabName.value==="task"){ | ||
| 95 | |||
| 96 | getTaskChangeTableData() | ||
| 97 | } else { | ||
| 98 | getMetaChangeTableData() | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | const getTaskChangeTableData = () => { | ||
| 104 | taskChangeTableInfo.value.loading = true; | ||
| 105 | getTaskChangeList(Object.assign(tableSearchValue.value,{pageIndex:taskChangePage.value.curr,pageSize:taskChangePage.value.limit})).then((res: any) => { | ||
| 106 | taskChangeTableInfo.value.loading = false; | ||
| 107 | if (res?.code == proxy.$passCode) { | ||
| 108 | const data = res.data || {}; | ||
| 109 | taskChangeTableInfo.value.data = data.records || []; | ||
| 110 | taskChangeTableInfo.value.page.curr = data.pageIndex; | ||
| 111 | taskChangeTableInfo.value.page.rows = data.totalRows; | ||
| 112 | taskChangeTableInfo.value.page.limit = data.pageSize; | ||
| 113 | taskChangePage.value.curr = data.pageIndex; | ||
| 114 | taskChangePage.value.limit = data.pageSize; | ||
| 115 | } else { | ||
| 116 | ElMessage({ | ||
| 117 | type: 'error', | ||
| 118 | message: res.msg, | ||
| 119 | }) | ||
| 120 | taskChangeTableInfo.value.loading = false | ||
| 121 | } | ||
| 122 | }); | ||
| 123 | } | ||
| 124 | |||
| 125 | const getMetaChangeTableData = () => { | ||
| 126 | metaChangeTableInfo.value.loading = true; | ||
| 127 | getMetaChangeRecord(Object.assign(metaTableValue.value,{pageIndex:metaChangePage.value.curr,pageSize:metaChangePage.value.limit,metaType:["TABLE","FIELD"]})).then((res: any) => { | ||
| 128 | metaChangeTableInfo.value.loading = false; | ||
| 129 | if (res.code == proxy.$passCode) { | ||
| 130 | const data = res.data || {}; | ||
| 131 | metaChangeTableInfo.value.data = data.records || []; | ||
| 132 | metaChangeTableInfo.value.page.curr = data.pageIndex; | ||
| 133 | metaChangeTableInfo.value.page.rows = data.totalRows; | ||
| 134 | taskChangePage.value.curr = data.pageIndex | ||
| 135 | taskChangePage.value.limit = data.pageSize | ||
| 136 | } else { | ||
| 137 | ElMessage({ | ||
| 138 | type: 'error', | ||
| 139 | message: res.msg, | ||
| 140 | }) | ||
| 141 | metaChangeTableInfo.value.loading = false; | ||
| 142 | } | ||
| 143 | }); | ||
| 144 | } | ||
| 145 | |||
| 146 | |||
| 147 | const activeTabName = ref('task'); | ||
| 148 | |||
| 149 | watch(() => activeTabName.value, (val) => { | ||
| 150 | if(val==="task"){ | ||
| 151 | getTaskChangeTableData() | ||
| 152 | } else { | ||
| 153 | getMetaChangeTableData() | ||
| 154 | } | ||
| 155 | }) | ||
| 156 | |||
| 157 | onMounted(()=>{ | ||
| 158 | getTaskChangeTableData() | ||
| 159 | }) | ||
| 160 | |||
| 161 | const tableSearchValue:any = ref({}) | ||
| 162 | const tableSearchItemList: any = ref( | ||
| 163 | [ | ||
| 164 | { | ||
| 165 | type: 'input', | ||
| 166 | label: '', | ||
| 167 | field: 'taskName', | ||
| 168 | default: '', | ||
| 169 | placeholder: '同步任务名称', | ||
| 170 | maxlength: 50, | ||
| 171 | clearable: true | ||
| 172 | }, { | ||
| 173 | type: 'select', | ||
| 174 | label: '', | ||
| 175 | field: 'changeType', | ||
| 176 | default: '', | ||
| 177 | placeholder: '变更类型', | ||
| 178 | options: [ | ||
| 179 | { label: '新增', value: 'I' }, | ||
| 180 | { label: '删除', value: 'D' }, | ||
| 181 | { label: '修改', value: 'U' }, | ||
| 182 | { label: '上线', value: 'Y' }, | ||
| 183 | { label: '下线', value: 'S' } | ||
| 184 | ], | ||
| 185 | clearable: true | ||
| 186 | }, | ||
| 187 | ]); | ||
| 188 | |||
| 189 | const toTableSearch = (val, clear) => { | ||
| 190 | if (clear) { | ||
| 191 | tableSearchItemList.value.map(item => item.default = '') | ||
| 192 | |||
| 193 | } | ||
| 194 | tableSearchValue.value = Object.keys(val).length ? { ...val,dataSourceName:tableSearchValue.value.dataSourceName,tableGuid:tableSearchValue.value.tableGuid} : {} | ||
| 195 | getTaskChangeTableData(); | ||
| 196 | } | ||
| 197 | |||
| 198 | const taskChangePage: any = ref({ | ||
| 199 | limit: 50, | ||
| 200 | curr: 1, | ||
| 201 | sizes: [ | ||
| 202 | { label: "10", value: 10 }, | ||
| 203 | { label: "50", value: 50 }, | ||
| 204 | { label: "100", value: 100 }, | ||
| 205 | { label: "150", value: 150 }, | ||
| 206 | { label: "200", value: 200 }, | ||
| 207 | ], | ||
| 208 | taskName: '', | ||
| 209 | changeState: null, | ||
| 210 | changeType: '' | ||
| 211 | }); | ||
| 212 | |||
| 213 | const taskChangeTableInfo = ref({ | ||
| 214 | id: 'task-change-table', | ||
| 215 | rowKey: 'guid', | ||
| 216 | loading: false, | ||
| 217 | fields: [ | ||
| 218 | { label: "序号", type: "index", width: 56, align: "center" }, | ||
| 219 | { label: "同步任务名称", field: "taskName", width: 150, align: "left" }, | ||
| 220 | { label: "目标端数据源", field: "targetDataSource", width: 160, align: "left" }, | ||
| 221 | { label: "目标端表英文名称", field: "targetTable", width: 150, align: "left",type: 'text_btn', value: 'targetTable',columClass:"text_btn" }, | ||
| 222 | { label: "目标端表中文名称", field: "targetTableZhName", width: 150, align: "left",type: 'text_btn', value: 'targetTableZhName',columClass:"text_btn" }, | ||
| 223 | { label: "源端数据源", field: "sourceDataSource", width: 160, align: "left" }, | ||
| 224 | { label: "源端表英文名称", field: "sourceTable", width: 150, align: "left" }, | ||
| 225 | { label: "源端表中文名称", field: "sourceTableZhName", width: 150, align: "left" }, | ||
| 226 | { label: "变更类型", field: "changeType", type :'popover',value:"changeType",checkName:(scope)=>{ | ||
| 227 | return scope.row['changeType']==="U" | ||
| 228 | },width: 96, menus:{I:"新增",U:"修改",D:"删除", Y:'上线', S: '下线'},getName:(scope)=>{ | ||
| 229 | const menus = {I:"新增",U:"修改",D:"删除", Y:'上线', S: '下线'} | ||
| 230 | return menus[scope.row['changeType']] | ||
| 231 | },column:[{field:"changeInfo",label:"变化信息",width:"150"},{field:"oldValue",label:"原值",width:"150"},{field:"newValue",label:"现值",width:"150"}] }, | ||
| 232 | { label: "任务变更时间", field: "changeTime", width: 180 }, | ||
| 233 | ], | ||
| 234 | data: [{ | ||
| 235 | guid: 1 | ||
| 236 | }], | ||
| 237 | popoverTitle:"", | ||
| 238 | popoverloading:false, | ||
| 239 | popoverData:[], | ||
| 240 | arraySpanMethod:({row,column,rowIndex,columnIndex})=>{ | ||
| 241 | |||
| 242 | // if (rowIndex === 0 && columnIndex === 1) { | ||
| 243 | |||
| 244 | // return { | ||
| 245 | // rowspan: 1, | ||
| 246 | // colspan: 2 | ||
| 247 | // }; | ||
| 248 | // } | ||
| 249 | if (rowIndex === 0) { | ||
| 250 | if (columnIndex === 1) { | ||
| 251 | return [1,2] | ||
| 252 | } else if (columnIndex === 2) { | ||
| 253 | return [0, 0] | ||
| 254 | } | ||
| 255 | } | ||
| 256 | }, | ||
| 257 | page: { | ||
| 258 | type: "normal", | ||
| 259 | rows: 0, | ||
| 260 | ...taskChangePage.value, | ||
| 261 | }, | ||
| 262 | actionInfo: { | ||
| 263 | show: false, | ||
| 264 | label: "操作", | ||
| 265 | type: "btn", | ||
| 266 | width: 100, | ||
| 267 | btns: (scope) => { | ||
| 268 | let row = scope.row; | ||
| 269 | return [{ label: '影响分析', value: 'analysis', disabled: row['changeState'] == 1 }]; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | }); | ||
| 273 | |||
| 274 | const taskChangeTablePageChange = (info) => { | ||
| 275 | taskChangePage.value.curr = Number(info.curr); | ||
| 276 | taskChangePage.value.limit = Number(info.limit); | ||
| 277 | taskChangeTableInfo.value.page.limit = taskChangePage.value.limit; | ||
| 278 | taskChangeTableInfo.value.page.curr = taskChangePage.value.curr; | ||
| 279 | getTaskChangeTableData(); | ||
| 280 | }; | ||
| 281 | |||
| 282 | const currTaskChangeTableData: any = ref<Object>({}); | ||
| 283 | const taskChangeTableBtnClick = (scope, btn) => { | ||
| 284 | const type = btn.value; | ||
| 285 | const row = scope.row; | ||
| 286 | currTaskChangeTableData.value = row; | ||
| 287 | if (type === 'analysis') { // 详情 | ||
| 288 | router.push({ | ||
| 289 | name: 'impactAnalysis', | ||
| 290 | query: { guid: row.approvalGuid } | ||
| 291 | }); | ||
| 292 | } else if(type ==='targetTable' ){ | ||
| 293 | checkTableData(row.targetTableGuid).then((res:any)=>{ | ||
| 294 | if(res.code===proxy.$passCode) { | ||
| 295 | if(res.data){ | ||
| 296 | router.push({ | ||
| 297 | path: '/data-meta/metadata-query/meta-sheet', | ||
| 298 | query: { | ||
| 299 | id: row.targetTableGuid, | ||
| 300 | name: row.targetTableZhName || row.targetTable | ||
| 301 | } | ||
| 302 | }) | ||
| 303 | } else { | ||
| 304 | ElMessage.warning("元数据详情不存在") | ||
| 305 | } | ||
| 306 | } | ||
| 307 | }) | ||
| 308 | |||
| 309 | } else if(type === "targetTableZhName"){ | ||
| 310 | checkTableData(row.targetTableGuid).then((res:any)=>{ | ||
| 311 | if(res.code===proxy.$passCode) { | ||
| 312 | if(res.data){ | ||
| 313 | router.push({ | ||
| 314 | path: '/data-meta/metadata-query/meta-sheet', | ||
| 315 | query: { | ||
| 316 | id: row.targetTableGuid, | ||
| 317 | name: row.targetTableZhName || row.targetTable | ||
| 318 | } | ||
| 319 | }) | ||
| 320 | } else { | ||
| 321 | ElMessage.warning("元数据详情不存在") | ||
| 322 | } | ||
| 323 | } | ||
| 324 | }) | ||
| 325 | } else if(type==="changeType"){ | ||
| 326 | taskChangeTableInfo.value.popoverloading = true | ||
| 327 | syncChangeDetail(row.guid).then((res:any)=>{ | ||
| 328 | taskChangeTableInfo.value.popoverloading = false | ||
| 329 | if(res.code===proxy.$passCode){ | ||
| 330 | taskChangeTableInfo.value.popoverData = res.data | ||
| 331 | taskChangeTableInfo.value.popoverTitle = row.targetTableZhName | ||
| 332 | } | ||
| 333 | |||
| 334 | }) | ||
| 335 | } | ||
| 336 | }; | ||
| 337 | |||
| 338 | const metaTableValue:any = ref({}) | ||
| 339 | |||
| 340 | const metaTableSearchItemList: any = ref( | ||
| 341 | [ | ||
| 342 | { | ||
| 343 | type: 'input', | ||
| 344 | label: '', | ||
| 345 | field: 'collectTaskName', | ||
| 346 | default: '', | ||
| 347 | placeholder: '元数据采集任务', | ||
| 348 | maxlength: 50, | ||
| 349 | clearable: true | ||
| 350 | }, { | ||
| 351 | type: 'select', | ||
| 352 | label: '', | ||
| 353 | field: 'metaChangeType', | ||
| 354 | default: '', | ||
| 355 | placeholder: '变更类型', | ||
| 356 | options: [ | ||
| 357 | { label: '新增', value: 'ADD' }, | ||
| 358 | { label: '删除', value: 'DELETE' }, | ||
| 359 | { label: '修改', value: 'UPDATE' } | ||
| 360 | ], | ||
| 361 | clearable: true | ||
| 362 | }, | ||
| 363 | ]); | ||
| 364 | |||
| 365 | const toMetaTableSearch = (val, clear) => { | ||
| 366 | if (clear) { | ||
| 367 | metaTableSearchItemList.value.map(item => item.default = '') | ||
| 368 | |||
| 369 | } | ||
| 370 | metaTableValue.value = Object.keys(val).length ? { ...val,dataSourceGuid:metaTableValue.value.dataSourceGuid,tableGuid:metaTableValue.value.tableGuid} : {} | ||
| 371 | getMetaChangeTableData() | ||
| 372 | } | ||
| 373 | |||
| 374 | const metaChangePage: any = ref({ | ||
| 375 | limit: 50, | ||
| 376 | curr: 1, | ||
| 377 | sizes: [ | ||
| 378 | { label: "10", value: 10 }, | ||
| 379 | { label: "50", value: 50 }, | ||
| 380 | { label: "100", value: 100 }, | ||
| 381 | { label: "150", value: 150 }, | ||
| 382 | { label: "200", value: 200 }, | ||
| 383 | ], | ||
| 384 | metaTaskName: '', | ||
| 385 | changeState: null, | ||
| 386 | changeType: '' | ||
| 387 | }); | ||
| 388 | |||
| 389 | const metaChangeTableInfo = ref({ | ||
| 390 | id: 'meta-change-table', | ||
| 391 | rowKey: 'guid', | ||
| 392 | loading: false, | ||
| 393 | fields: [ | ||
| 394 | { label: "序号", type: "index", width: 56, align: "center", fixed: "left" }, | ||
| 395 | { label: "元数据采集任务", field: "collectTaskName", width: 180, }, | ||
| 396 | { label: "数据源名称", field: "dataSourceName", width: 180, }, | ||
| 397 | { label: "表中文名称", field: "tableChName", width: 180, }, | ||
| 398 | { label: "表英文名称", field: "tableName", width: 180, }, | ||
| 399 | { label: "元数据名称", field: "metaName", width: 180, }, | ||
| 400 | { | ||
| 401 | label: "元数据类型", field: "metaType", width: 100, getName: (scope) => { | ||
| 402 | if (!scope.row.metaType) { | ||
| 403 | return '--'; | ||
| 404 | } | ||
| 405 | return scope.row.metaType == 'TABLE' ? '表' : (scope.row.metaType == 'INDEX' ? '索引' : (scope.row.metaType == 'PRI' ? '主键' : '字段')); | ||
| 406 | } | ||
| 407 | }, | ||
| 408 | { | ||
| 409 | label: "变更类型", field: "metaChangeType", width: 100, getName: (scope) => { | ||
| 410 | if (!scope.row.metaChangeType) { | ||
| 411 | return '--'; | ||
| 412 | } | ||
| 413 | return scope.row.metaChangeType == 'ADD' ? '新增' : (scope.row.metaChangeType == 'DELETE' ? '删除' : '修改'); | ||
| 414 | } | ||
| 415 | }, | ||
| 416 | { label: "变化", field: "metaChangeType", width: 120,type: 'popover',value:'metaChangeType',column:[{field:"item",label:"变化信息",width:"150"},{field:"oldValue",label:"原值",width:"150"},{field:"newValue",label:"现值",width:"150"}] }, | ||
| 417 | // { label: "原值", field: "metaOldValue", width: 120 }, | ||
| 418 | { label: "变更时间", field: "changeTime", width: 180, }, | ||
| 419 | { label: "元数据采集时间", field: "changeTime", width: 180, }, | ||
| 420 | // { label: "状态", field: "changeTime", width: 180, }, | ||
| 421 | // { label: "操作时间", field: "changeTime", width: 180, }, | ||
| 422 | ], | ||
| 423 | data: [{ | ||
| 424 | guid: 1 | ||
| 425 | }], | ||
| 426 | page: { | ||
| 427 | type: "normal", | ||
| 428 | rows: 0, | ||
| 429 | ...metaChangePage.value, | ||
| 430 | }, | ||
| 431 | popoverloading:false, | ||
| 432 | popoverData:[], | ||
| 433 | actionInfo: { | ||
| 434 | show:false, | ||
| 435 | label: "操作", | ||
| 436 | type: "btn", | ||
| 437 | width: 100, | ||
| 438 | btns: (scope) => { | ||
| 439 | let row = scope.row; | ||
| 440 | return [{ label: '影响分析', value: 'analysis', disabled: row['changeState'] == 1 }]; | ||
| 441 | } | ||
| 442 | } | ||
| 443 | }); | ||
| 444 | |||
| 445 | const metaChangeTablePageChange = (info) => { | ||
| 446 | |||
| 447 | metaChangePage.value.curr = Number(info.curr); | ||
| 448 | metaChangePage.value.limit = Number(info.limit); | ||
| 449 | metaChangeTableInfo.value.page.limit = metaChangePage.value.limit; | ||
| 450 | metaChangeTableInfo.value.page.curr = metaChangePage.value.curr; | ||
| 451 | getMetaChangeTableData(); | ||
| 452 | }; | ||
| 453 | |||
| 454 | const currMetaChangeTableData: any = ref<Object>({}); | ||
| 455 | const metaChangeTableBtnClick = (scope, btn) => { | ||
| 456 | const type = btn.value; | ||
| 457 | const row = scope.row; | ||
| 458 | currMetaChangeTableData.value = row; | ||
| 459 | if (type === 'analysis') { // 详情 | ||
| 460 | router.push({ | ||
| 461 | name: 'impactAnalysis', | ||
| 462 | query: { guid: row.approvalGuid } | ||
| 463 | }); | ||
| 464 | } else if(type==="metaChangeType") { | ||
| 465 | metaChangeTableInfo.value.popoverloading = true | ||
| 466 | getMetacompareList(scope.row.guid).then((res:any)=>{ | ||
| 467 | metaChangeTableInfo.value.popoverloading = false | ||
| 468 | metaChangeTableInfo.value.popoverData = res.data | ||
| 469 | |||
| 470 | }) | ||
| 471 | console.log(scope,btn) | ||
| 472 | } | ||
| 473 | }; | ||
| 474 | |||
| 475 | onBeforeMount(() => { | ||
| 476 | getTreeData() | ||
| 477 | }) | ||
| 478 | |||
| 479 | </script> | ||
| 480 | |||
| 481 | <template> | ||
| 482 | <div class="container_wrap full flex"> | ||
| 483 | <div class="aside_wrap"> | ||
| 484 | <div class="aside_title">数据库目录列表</div> | ||
| 485 | <Tree :treeInfo="treeInfo" @nodeClick="nodeClick" /> | ||
| 486 | </div> | ||
| 487 | <div class="main_wrap"> | ||
| 488 | <el-tabs v-model="activeTabName"> | ||
| 489 | <el-tab-pane label="同步任务变更记录" name="task"> | ||
| 490 | <div class="table_tool_wrap"> | ||
| 491 | <TableTools :searchItems="tableSearchItemList" :init="false" searchId="detect-table-search" | ||
| 492 | @search="toTableSearch" /> | ||
| 493 | </div> | ||
| 494 | <div class="table_panel_wrap"> | ||
| 495 | <Table :tableInfo="taskChangeTableInfo" @tableBtnClick="taskChangeTableBtnClick" | ||
| 496 | @tablePageChange="taskChangeTablePageChange" /> | ||
| 497 | </div> | ||
| 498 | </el-tab-pane> | ||
| 499 | <el-tab-pane label="元数据变更记录" name="meta"> | ||
| 500 | <div class="table_tool_wrap"> | ||
| 501 | <TableTools :searchItems="metaTableSearchItemList" :init="false" searchId="meta-detect-table-search" | ||
| 502 | @search="toMetaTableSearch" /> | ||
| 503 | </div> | ||
| 504 | <div class="table_panel_wrap"> | ||
| 505 | <Table :tableInfo="metaChangeTableInfo" @tableBtnClick="metaChangeTableBtnClick" | ||
| 506 | @tablePageChange="metaChangeTablePageChange" /> | ||
| 507 | </div> | ||
| 508 | </el-tab-pane> | ||
| 509 | </el-tabs> | ||
| 510 | </div> | ||
| 511 | </div> | ||
| 512 | </template> | ||
| 513 | |||
| 514 | <style scoped lang="scss"> | ||
| 515 | .container_wrap { | ||
| 516 | |||
| 517 | .aside_wrap { | ||
| 518 | width: 200px; | ||
| 519 | } | ||
| 520 | |||
| 521 | .main_wrap { | ||
| 522 | padding: 0px; | ||
| 523 | |||
| 524 | |||
| 525 | :deep(.el-tabs) { | ||
| 526 | height: 100%; | ||
| 527 | |||
| 528 | .el-tabs__header { | ||
| 529 | margin-bottom: 0; | ||
| 530 | } | ||
| 531 | |||
| 532 | .el-tabs__item { | ||
| 533 | height: 32px; | ||
| 534 | padding: 0px; | ||
| 535 | width: 144px; | ||
| 536 | |||
| 537 | &:last-child { | ||
| 538 | width: 130px; | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | .el-tabs__content { | ||
| 543 | height: calc(100% - 32px); | ||
| 544 | } | ||
| 545 | |||
| 546 | .el-tab-pane { | ||
| 547 | padding: 0px 16px; | ||
| 548 | height: 100%; | ||
| 549 | } | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | } | ||
| 554 | |||
| 555 | .tree_panel { | ||
| 556 | height: calc(100% - 36px); | ||
| 557 | padding-top: 0; | ||
| 558 | |||
| 559 | :deep(.el-tree) { | ||
| 560 | margin: 0; | ||
| 561 | overflow: hidden auto; | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | .table_tool_wrap { | ||
| 566 | display: flex; | ||
| 567 | flex-direction: column; | ||
| 568 | |||
| 569 | .el-input { | ||
| 570 | width: 230px; | ||
| 571 | height: 32px; | ||
| 572 | } | ||
| 573 | |||
| 574 | :deep(.el-input) { | ||
| 575 | .el-input__suffix-inner { | ||
| 576 | flex-direction: row-reverse; | ||
| 577 | -webkit-flex-direction: row-reverse; | ||
| 578 | display: flex; | ||
| 579 | } | ||
| 580 | } | ||
| 581 | } | ||
| 582 | |||
| 583 | .table_panel_wrap { | ||
| 584 | height: calc(100% - 44px); | ||
| 585 | } | ||
| 586 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_meta/collectorTask.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: collectorTask | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="collectorTask"> | ||
| 6 | import { ref } from 'vue' | ||
| 7 | import TableTools from "@/components/Tools/table_tools.vue"; | ||
| 8 | import { | ||
| 9 | addMetaDataTask, | ||
| 10 | deleteMetaDataTask, | ||
| 11 | getMetaDataTask, | ||
| 12 | updateMetaDataTask, | ||
| 13 | updateMetaDataState, | ||
| 14 | checkMetaDataTask, | ||
| 15 | executeMetaDataTask, | ||
| 16 | getDatabase, | ||
| 17 | checkDatabaseIsExist | ||
| 18 | } from '@/api/modules/dataMetaService'; | ||
| 19 | import { | ||
| 20 | getCronExecTime | ||
| 21 | } from '@/api/modules/queryService'; | ||
| 22 | import { TableColumnWidth } from '@/utils/enum'; | ||
| 23 | import { commonPageConfig } from '@/components/PageNav/index'; | ||
| 24 | import { useValidator } from '@/hooks/useValidator'; | ||
| 25 | |||
| 26 | const { required, checkExistName } = useValidator(); | ||
| 27 | const { proxy } = getCurrentInstance() as any; | ||
| 28 | |||
| 29 | const router = useRouter(); | ||
| 30 | |||
| 31 | /** 数据源选择列表数据。 */ | ||
| 32 | const dataSourceList = ref([]) | ||
| 33 | |||
| 34 | /** 头部搜索框配置 */ | ||
| 35 | const searchItemList = ref([ | ||
| 36 | { | ||
| 37 | type: "input", | ||
| 38 | label: "", | ||
| 39 | field: "collectTaskName", | ||
| 40 | default: "", | ||
| 41 | placeholder: "采集任务名称", | ||
| 42 | clearable: true, | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | type: "select", | ||
| 46 | label: "", | ||
| 47 | placeholder: '数据源名称', | ||
| 48 | field: 'dataSourceGuid', | ||
| 49 | default: '', | ||
| 50 | options: dataSourceList.value, | ||
| 51 | props: { | ||
| 52 | label: 'databaseNameZh', | ||
| 53 | value: 'guid' | ||
| 54 | }, | ||
| 55 | clearable: true, | ||
| 56 | }, | ||
| 57 | { | ||
| 58 | type: "select", | ||
| 59 | label: "", | ||
| 60 | field: "taskState", | ||
| 61 | default: "", | ||
| 62 | placeholder: "任务状态", | ||
| 63 | options: [ | ||
| 64 | { label: "上线", value: "1" }, | ||
| 65 | { label: "下线", value: "0" }, | ||
| 66 | ], | ||
| 67 | clearable: true, | ||
| 68 | } | ||
| 69 | ]); | ||
| 70 | |||
| 71 | const currTableData: any = ref<Object>({}); | ||
| 72 | /** 分页及搜索传参信息配置。 */ | ||
| 73 | const page = ref({ | ||
| 74 | ...commonPageConfig, | ||
| 75 | collectTaskName: '', | ||
| 76 | dataSourceGuid: '', | ||
| 77 | taskState: null | ||
| 78 | }); | ||
| 79 | |||
| 80 | const tableInfo = ref({ | ||
| 81 | id: 'data-source-table', | ||
| 82 | // multiple:true, | ||
| 83 | fields: [ | ||
| 84 | { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" }, | ||
| 85 | { label: "采集任务名称", field: "collectTaskName", width: 140 }, | ||
| 86 | { label: "元模型", field: "metadataModelChName", width: 140 }, | ||
| 87 | { label: "数据源名称", field: "datasourceName", width: 160,type: 'text_btn', value: 'datasourceName',columClass:"text_btn" }, | ||
| 88 | { label: '任务状态', field: 'taskState', type: 'switch', activeText: '上线', inactiveText: '下线', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 100, align: 'center' }, | ||
| 89 | { label: "执行计划", field: "collectType", type: 'filter', width: 90 }, | ||
| 90 | { label: "同步策略", field: "collectMode", type: 'filter', width: 90 }, | ||
| 91 | { label: "执行周期", field: "execCycle", width: TableColumnWidth.EXECCYCLE }, | ||
| 92 | { label: "执行状态", field: "runingState", width: TableColumnWidth.STATE, align: 'center', type: "tag" }, | ||
| 93 | { label: "下次执行时间", field: "nextExecuteTime", width: TableColumnWidth.DATETIME }, | ||
| 94 | { label: "最后执行时间", field: "lastRuningTime", width: TableColumnWidth.DATETIME, }, | ||
| 95 | { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME }, | ||
| 96 | { label: "修改时间", field: "updateTime", width: TableColumnWidth.DATETIME }, | ||
| 97 | ], | ||
| 98 | data: [], | ||
| 99 | page: { | ||
| 100 | type: "normal", | ||
| 101 | rows: 0, | ||
| 102 | ...page.value, | ||
| 103 | }, | ||
| 104 | actionInfo: { | ||
| 105 | label: "操作", | ||
| 106 | type: "btn", | ||
| 107 | width: 230, | ||
| 108 | fixed: 'right', | ||
| 109 | btns: (scope) => { | ||
| 110 | const row = scope.row | ||
| 111 | let btnsArr: any = []; | ||
| 112 | if (row.isCarry) { | ||
| 113 | btnsArr.splice(0, 0, { label: "执行中...", value: "carry", disabled: true }) | ||
| 114 | } else { | ||
| 115 | btnsArr.splice(0, 0, { label: "立即执行", value: "carry", disabled: row.taskState === 0 || row.execState == 1 }) | ||
| 116 | } | ||
| 117 | btnsArr.push({ label: "编辑", value: "edit", disabled: row.taskState === 1 || row.isCarry || row.execState == 1 }); | ||
| 118 | btnsArr.push({ label: "删除", value: "delete", disabled: row.isCarry || row.taskState === 1 || row.execState == 1 }); | ||
| 119 | btnsArr.push({ label: "执行日志", value: "log" }); | ||
| 120 | return btnsArr | ||
| 121 | }, | ||
| 122 | }, | ||
| 123 | loading: false | ||
| 124 | }) | ||
| 125 | |||
| 126 | const collectType = ref(1) | ||
| 127 | |||
| 128 | const formItems: any = ref([ | ||
| 129 | { | ||
| 130 | label: '元模型', | ||
| 131 | type: 'select', | ||
| 132 | placeholder: '请选择', | ||
| 133 | field: 'metadataModel', | ||
| 134 | default: '1', | ||
| 135 | options:[ | ||
| 136 | { label:"数据库表", value:'1' }, | ||
| 137 | // {label:"BI报表",value:2,}, | ||
| 138 | // {label:"数据服务",value:3,}, | ||
| 139 | { label:"ETL工具",value:'4' }, | ||
| 140 | // {label:"数据处理",value:5,} | ||
| 141 | ], | ||
| 142 | clearable: true, | ||
| 143 | required: true | ||
| 144 | }, { | ||
| 145 | label: '采集任务名称', | ||
| 146 | type: 'input', | ||
| 147 | placeholder: '请输入', | ||
| 148 | field: 'collectTaskName', | ||
| 149 | default: '', | ||
| 150 | maxlength: 50, | ||
| 151 | clearable: true, | ||
| 152 | required: true | ||
| 153 | }, { | ||
| 154 | label: '数据源名称', | ||
| 155 | type: 'select', | ||
| 156 | placeholder: '请选择', | ||
| 157 | field: 'dataSourceGuid', | ||
| 158 | default: '', | ||
| 159 | options: dataSourceList.value, | ||
| 160 | props: { | ||
| 161 | label: 'databaseNameZh', | ||
| 162 | value: 'guid' | ||
| 163 | }, | ||
| 164 | clearable: true, | ||
| 165 | required: true | ||
| 166 | }, { | ||
| 167 | label: "执行计划", | ||
| 168 | type: "radio-group", | ||
| 169 | placeholder: "", | ||
| 170 | field: "collectType", | ||
| 171 | default: 1, | ||
| 172 | options: [ | ||
| 173 | { | ||
| 174 | label: "离线", | ||
| 175 | value: 1, | ||
| 176 | }, | ||
| 177 | // { | ||
| 178 | // label: "实时", | ||
| 179 | // value: 2, | ||
| 180 | // }, | ||
| 181 | ], | ||
| 182 | required: true, | ||
| 183 | }, { | ||
| 184 | label: '同步策略', | ||
| 185 | type: 'select', | ||
| 186 | placeholder: '请选择', | ||
| 187 | field: 'collectMode', | ||
| 188 | default: 2, | ||
| 189 | options: [ | ||
| 190 | // { label: '增量', value: 1 }, | ||
| 191 | { label: '全量', value: 2 }, | ||
| 192 | ], | ||
| 193 | clearable: true, | ||
| 194 | required: true, | ||
| 195 | visible: true | ||
| 196 | }, { | ||
| 197 | label: '执行周期', | ||
| 198 | type: 'input-popover-panel', | ||
| 199 | placeholder: '', | ||
| 200 | field: 'execCycle', | ||
| 201 | default: '', | ||
| 202 | append: { | ||
| 203 | btn: { label: '执行时间', value: 'cron' } | ||
| 204 | }, | ||
| 205 | teleported: true, | ||
| 206 | clearable: true, | ||
| 207 | required: true, | ||
| 208 | block: true, | ||
| 209 | visible: true, | ||
| 210 | }, { | ||
| 211 | label: '接下来五次执行时间', | ||
| 212 | type: 'textarea', | ||
| 213 | placeholder: '执行时间', | ||
| 214 | field: 'zxzq', | ||
| 215 | clearable: true, | ||
| 216 | required: false, | ||
| 217 | rows: 5, | ||
| 218 | block: true, | ||
| 219 | visible: true, | ||
| 220 | readonly: true | ||
| 221 | }, { | ||
| 222 | label: "", | ||
| 223 | type: "checkbox", | ||
| 224 | placeholder: "抽取存量数据", | ||
| 225 | field: "isInit", | ||
| 226 | default: 'Y', | ||
| 227 | trueValue: 'Y', | ||
| 228 | falseValue: 'N', | ||
| 229 | required: false, | ||
| 230 | visible: false, | ||
| 231 | col: 'margin-top-21' | ||
| 232 | }, | ||
| 233 | ]) | ||
| 234 | |||
| 235 | /** 记录已校验过的信息。 */ | ||
| 236 | const checkedInfo: any = ref({}); | ||
| 237 | |||
| 238 | const formRules: any = ref({ | ||
| 239 | metadataModel: [required('请选择元模型')], | ||
| 240 | collectTaskName: [required('请填写采集任务名称'), checkExistName(checkedInfo.value, checkMetaDataTask, currTableData.value, 'collectTaskName')], | ||
| 241 | dataSourceGuid: [required('请选择数据源')], | ||
| 242 | execCycle: [required('请设置执行周期')], | ||
| 243 | collectMode: [required('请选择同步策略')], | ||
| 244 | }) | ||
| 245 | const formInfo = ref({ | ||
| 246 | type: 'form', | ||
| 247 | title: '', | ||
| 248 | formInfo: { | ||
| 249 | id: 'add-dict-form', | ||
| 250 | items: formItems.value, | ||
| 251 | rules: formRules.value | ||
| 252 | } | ||
| 253 | }) | ||
| 254 | /** 新建编辑采集任务对话框配置 */ | ||
| 255 | const dialogInfo = ref({ | ||
| 256 | visible: false, | ||
| 257 | size: 700, | ||
| 258 | direction: "column", | ||
| 259 | header: { | ||
| 260 | title: "新建", | ||
| 261 | }, | ||
| 262 | type: '', | ||
| 263 | contents: [ | ||
| 264 | formInfo.value, | ||
| 265 | ], | ||
| 266 | footer: { | ||
| 267 | btns: [ | ||
| 268 | { type: "default", label: "取消", value: "cancel" }, | ||
| 269 | { type: "primary", label: "确定", value: "submit" }, | ||
| 270 | ], | ||
| 271 | }, | ||
| 272 | }); | ||
| 273 | |||
| 274 | const toSearch = (val: any, clear: boolean = false) => { | ||
| 275 | if (clear) { | ||
| 276 | searchItemList.value.map((item) => (item.default = "")); | ||
| 277 | page.value.collectTaskName = ''; | ||
| 278 | page.value.dataSourceGuid = ""; | ||
| 279 | page.value.taskState = null; | ||
| 280 | } else { | ||
| 281 | page.value.collectTaskName = val.collectTaskName; | ||
| 282 | page.value.dataSourceGuid = val.dataSourceGuid; | ||
| 283 | page.value.taskState = val.taskState; | ||
| 284 | } | ||
| 285 | getTableData(); | ||
| 286 | }; | ||
| 287 | |||
| 288 | const getTableData = () => { | ||
| 289 | tableInfo.value.loading = true | ||
| 290 | getMetaDataTask({ | ||
| 291 | pageIndex: page.value.curr, | ||
| 292 | pageSize: page.value.limit, | ||
| 293 | collectTaskName: page.value.collectTaskName, | ||
| 294 | dataSourceGuid: page.value.dataSourceGuid, | ||
| 295 | taskState: page.value.taskState | ||
| 296 | }).then((res: any) => { | ||
| 297 | if (res.code == proxy.$passCode) { | ||
| 298 | const data = res.data || {} | ||
| 299 | tableInfo.value.data = data.records || [] | ||
| 300 | tableInfo.value.page.limit = data.pageSize | ||
| 301 | tableInfo.value.page.curr = data.pageIndex | ||
| 302 | tableInfo.value.page.rows = data.totalRows | ||
| 303 | } else { | ||
| 304 | proxy.$ElMessage({ | ||
| 305 | type: 'error', | ||
| 306 | message: res.msg, | ||
| 307 | }) | ||
| 308 | } | ||
| 309 | tableInfo.value.loading = false | ||
| 310 | }) | ||
| 311 | }; | ||
| 312 | |||
| 313 | const tableSwitchBeforeChange = (scope, field, callback) => { | ||
| 314 | const msg = `确定【${scope.row[field] == 1 ? '下线' : '上线'}】${scope.row.collectTaskName}?` | ||
| 315 | proxy.$openMessageBox(msg, () => { | ||
| 316 | const state = scope.row[field] == 1 ? 0 : 1 | ||
| 317 | const result = tableSwitchChange(state, scope, field) | ||
| 318 | callback(result) | ||
| 319 | }, () => { | ||
| 320 | callback(false) | ||
| 321 | }); | ||
| 322 | } | ||
| 323 | |||
| 324 | const tableSwitchChange = (val, scope, field) => { | ||
| 325 | return new Promise((resolve, reject) => { | ||
| 326 | let params = { | ||
| 327 | guid: scope.row.guid, | ||
| 328 | taskState: val | ||
| 329 | } | ||
| 330 | updateMetaDataState(params).then((res: any) => { | ||
| 331 | if (res.code == proxy.$passCode && res.data) { | ||
| 332 | getTableData(); | ||
| 333 | proxy.$ElMessage({ | ||
| 334 | type: "success", | ||
| 335 | message: `${scope.row.collectTaskName}【${val == 0 ? '下线' : '上线'}】成功`, | ||
| 336 | }); | ||
| 337 | resolve(true) | ||
| 338 | } else { | ||
| 339 | proxy.$ElMessage({ | ||
| 340 | type: "error", | ||
| 341 | message: res.msg, | ||
| 342 | }); | ||
| 343 | reject(false) | ||
| 344 | } | ||
| 345 | }).catch(() => { | ||
| 346 | reject(false) | ||
| 347 | }) | ||
| 348 | }) | ||
| 349 | } | ||
| 350 | |||
| 351 | const tablePageChange = (info) => { | ||
| 352 | page.value.curr = Number(info.curr); | ||
| 353 | page.value.limit = Number(info.limit); | ||
| 354 | getTableData(); | ||
| 355 | }; | ||
| 356 | |||
| 357 | const oldOriginValue = ref({}); | ||
| 358 | |||
| 359 | const setFormItems = (row: any = null) => { | ||
| 360 | let val = oldOriginValue.value = Object.assign({ collectType: collectType.value, collectMode: 2, isInit: 'Y', metadataModel: '1' }, oldOriginValue.value, row || {}); | ||
| 361 | formItems.value.map(item => { | ||
| 362 | if (item.field == 'dataSourceGuid') { | ||
| 363 | item.options = dataSourceList.value | ||
| 364 | } | ||
| 365 | item.default = val[item.field] || ""; | ||
| 366 | }) | ||
| 367 | } | ||
| 368 | |||
| 369 | const tableBtnClick = (scope, btn) => { | ||
| 370 | const type = btn.value; | ||
| 371 | let row = scope.row; | ||
| 372 | currTableData.value = row; | ||
| 373 | if (type == 'carry') { | ||
| 374 | row.isCarry = true | ||
| 375 | const guid = row.guid | ||
| 376 | executeMetaDataTask(guid).then((res: any) => { | ||
| 377 | if (res.code == proxy.$passCode) { | ||
| 378 | getTableData(); | ||
| 379 | proxy.$ElMessage({ | ||
| 380 | type: "success", | ||
| 381 | message: "立即执行成功", | ||
| 382 | }); | ||
| 383 | } else { | ||
| 384 | proxy.$ElMessage({ | ||
| 385 | type: "error", | ||
| 386 | message: res.msg, | ||
| 387 | }); | ||
| 388 | getTableData(); | ||
| 389 | } | ||
| 390 | row.isCarry = false | ||
| 391 | }).catch(() => { | ||
| 392 | row.isCarry = false | ||
| 393 | }) | ||
| 394 | } else if (type == 'log') { | ||
| 395 | router.push({ | ||
| 396 | path: '/data-meta/collect-task/excution-log', | ||
| 397 | query: { | ||
| 398 | guid: row.guid, | ||
| 399 | name: row.collectTaskName, | ||
| 400 | type: row.collectType | ||
| 401 | } | ||
| 402 | }) | ||
| 403 | } else if (type == "edit") { | ||
| 404 | dialogInfo.value.header.title = '编辑采集任务'; | ||
| 405 | dialogInfo.value.type = type | ||
| 406 | setDetailInfo(scope.row); | ||
| 407 | formInfo.value.formInfo.items[6].default = ''; | ||
| 408 | formRules.value.collectTaskName = [required('请填写采集任务名称'), checkExistName(checkedInfo.value, checkMetaDataTask, currTableData.value, 'collectTaskName')]; | ||
| 409 | dialogInfo.value.contents[0].formInfo.rules = formRules.value; | ||
| 410 | } else if (type == "delete") { | ||
| 411 | proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => { | ||
| 412 | let guids = [currTableData.value.guid] | ||
| 413 | deleteMetaDataTask(guids).then((res: any) => { | ||
| 414 | if (res.code == proxy.$passCode) { | ||
| 415 | getTableData(); | ||
| 416 | proxy.$ElMessage({ | ||
| 417 | type: "success", | ||
| 418 | message: "删除成功", | ||
| 419 | }); | ||
| 420 | } else { | ||
| 421 | proxy.$ElMessage({ | ||
| 422 | type: "error", | ||
| 423 | message: res.msg, | ||
| 424 | }); | ||
| 425 | } | ||
| 426 | }); | ||
| 427 | }) | ||
| 428 | } else if(type=="datasourceName"){ | ||
| 429 | if (row.metadataModel == '4') { | ||
| 430 | proxy.$ElMessage.warning("元模型为ETL工具的采集任务无元数据详情!") | ||
| 431 | return; | ||
| 432 | } | ||
| 433 | checkDatabaseIsExist(row.dataSourceGuid).then((res:any)=>{ | ||
| 434 | if(res.code===proxy.$passCode){ | ||
| 435 | if(res.data){ | ||
| 436 | router.push({ | ||
| 437 | name:"metadataQuery", | ||
| 438 | query:{ | ||
| 439 | dataSourceGuid:row.dataSourceGuid, | ||
| 440 | datasourceName:row.datasourceName, | ||
| 441 | } | ||
| 442 | }) | ||
| 443 | } else { | ||
| 444 | proxy.$ElMessage.warning("该任务暂未执行成功过!") | ||
| 445 | } | ||
| 446 | } | ||
| 447 | }) | ||
| 448 | } | ||
| 449 | }; | ||
| 450 | |||
| 451 | const loadDrawer = () => { | ||
| 452 | collectType.value = 1 | ||
| 453 | setFormItems() | ||
| 454 | setGroup() | ||
| 455 | formInfo.value.formInfo.items[5].default = '0 00 00 01 * ? ' | ||
| 456 | dialogInfo.value.header.title = '新建采集任务' | ||
| 457 | dialogInfo.value.type = 'add' | ||
| 458 | formInfo.value.formInfo.items = formItems.value | ||
| 459 | dialogInfo.value.visible = true; | ||
| 460 | checkedInfo.value = {}; | ||
| 461 | formItems.value[0].default = '1'; | ||
| 462 | formItems.value[0].disabled = false; | ||
| 463 | }; | ||
| 464 | |||
| 465 | const setDetailInfo = (row) => { | ||
| 466 | collectType.value = row.collectType | ||
| 467 | setFormItems(row) | ||
| 468 | setGroup() | ||
| 469 | formItems.value[0].disabled = true; | ||
| 470 | dialogInfo.value.visible = true; | ||
| 471 | checkedInfo.value = {}; | ||
| 472 | } | ||
| 473 | |||
| 474 | const getDataSourceList = () => { | ||
| 475 | getDatabase({ connectStatus: 1 }).then((res: any) => { | ||
| 476 | if (res.code == proxy.$passCode) { | ||
| 477 | dataSourceList.value = res.data || []; | ||
| 478 | formItems.value[1].options = dataSourceList.value; | ||
| 479 | searchItemList.value[1].options = dataSourceList.value; | ||
| 480 | } else { | ||
| 481 | proxy.$ElMessage({ | ||
| 482 | type: "error", | ||
| 483 | message: res.msg, | ||
| 484 | }); | ||
| 485 | } | ||
| 486 | }) | ||
| 487 | } | ||
| 488 | |||
| 489 | const radioGroupChange = (val, info) => { | ||
| 490 | collectType.value = val | ||
| 491 | setFormItems(info) | ||
| 492 | setGroup() | ||
| 493 | } | ||
| 494 | |||
| 495 | const scheduleChange = (val, rowValue) => { | ||
| 496 | setFormItems(rowValue) | ||
| 497 | formItems.value.at(-3).default = val | ||
| 498 | formInfo.value.formInfo.items = formItems.value | ||
| 499 | } | ||
| 500 | |||
| 501 | // 切换结构类型 设置选项显隐 | ||
| 502 | const setGroup = () => { | ||
| 503 | if (collectType.value == 2) { | ||
| 504 | formItems.value.at(-1).visible = true | ||
| 505 | formItems.value.at(-2).visible = false | ||
| 506 | formItems.value.at(-3).visible = false | ||
| 507 | formItems.value.at(-4).visible = false | ||
| 508 | } else { | ||
| 509 | formItems.value.at(-1).visible = false | ||
| 510 | formItems.value.at(-2).visible = true | ||
| 511 | formItems.value.at(-3).visible = true | ||
| 512 | formItems.value.at(-4).visible = true | ||
| 513 | } | ||
| 514 | formInfo.value.formInfo.items = formItems.value | ||
| 515 | } | ||
| 516 | |||
| 517 | const taskCreateDialogRef = ref(); | ||
| 518 | |||
| 519 | const dialogBtnClick = (btn, info) => { | ||
| 520 | if (btn.value == 'submit') { | ||
| 521 | let params = { ...info ,} | ||
| 522 | // params.taskState = 1; | ||
| 523 | if (dialogInfo.value.type == 'add') { | ||
| 524 | params.taskState = 1; | ||
| 525 | addMetaDataTask(params).then((res: any) => { | ||
| 526 | if (res.code == proxy.$passCode) { | ||
| 527 | page.value.curr = 1; | ||
| 528 | getTableData(); | ||
| 529 | proxy.$ElMessage({ | ||
| 530 | type: 'success', | ||
| 531 | message: '添加采集任务成功' | ||
| 532 | }) | ||
| 533 | dialogInfo.value.visible = false; | ||
| 534 | } else { | ||
| 535 | proxy.$ElMessage.error(res.msg); | ||
| 536 | } | ||
| 537 | }) | ||
| 538 | } else { | ||
| 539 | params.guid = currTableData.value.guid | ||
| 540 | updateMetaDataTask(params).then((res: any) => { | ||
| 541 | if (res.code == proxy.$passCode) { | ||
| 542 | getTableData(); | ||
| 543 | proxy.$ElMessage({ | ||
| 544 | type: 'success', | ||
| 545 | message: '修改采集任务成功' | ||
| 546 | }) | ||
| 547 | dialogInfo.value.visible = false; | ||
| 548 | } else { | ||
| 549 | proxy.$ElMessage.error(res.msg); | ||
| 550 | } | ||
| 551 | }) | ||
| 552 | } | ||
| 553 | } else if (btn.value == 'cancel') { | ||
| 554 | dialogInfo.value.visible = false; | ||
| 555 | } else if (btn.value = 'cron') { | ||
| 556 | let vInfo = taskCreateDialogRef.value.dialogFormRef[0].formInline; | ||
| 557 | if (!vInfo.execCycle) { | ||
| 558 | return; | ||
| 559 | } | ||
| 560 | getCronExecTime(vInfo.execCycle).then((res: any) => { | ||
| 561 | if (res?.length) { | ||
| 562 | vInfo.zxzq = res.join('\n'); | ||
| 563 | setFormItems(vInfo); | ||
| 564 | } else { | ||
| 565 | proxy.$ElMessage({ | ||
| 566 | type: 'error', | ||
| 567 | message: res.msg, | ||
| 568 | }) | ||
| 569 | } | ||
| 570 | }) | ||
| 571 | } | ||
| 572 | }; | ||
| 573 | |||
| 574 | onActivated(() => { | ||
| 575 | getDataSourceList() | ||
| 576 | }) | ||
| 577 | |||
| 578 | onBeforeMount(() => { | ||
| 579 | toSearch({}) | ||
| 580 | }) | ||
| 581 | </script> | ||
| 582 | |||
| 583 | <template> | ||
| 584 | <div class="container_wrap"> | ||
| 585 | <div class="table_tool_wrap"> | ||
| 586 | <!-- 头部搜索 --> | ||
| 587 | <TableTools :searchItems="searchItemList" :searchId="'data-source-search'" @search="toSearch" :init="false" /> | ||
| 588 | <div class="tools_btns"> | ||
| 589 | <el-button type="primary" @click="loadDrawer" v-preReClick>新建</el-button> | ||
| 590 | </div> | ||
| 591 | </div> | ||
| 592 | <div class="table_panel_wrap"> | ||
| 593 | <!-- 右侧采集任务表格 --> | ||
| 594 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" | ||
| 595 | @tablePageChange="tablePageChange" @tableSwitchBeforeChange="tableSwitchBeforeChange" /> | ||
| 596 | </div> | ||
| 597 | <!-- 新建编辑采集任务对话框 --> | ||
| 598 | <Dialog ref="taskCreateDialogRef" :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @radioGroupChange="radioGroupChange" | ||
| 599 | @scheduleChange="scheduleChange" /> | ||
| 600 | </div> | ||
| 601 | </template> | ||
| 602 | |||
| 603 | <style scoped lang="scss"> | ||
| 604 | |||
| 605 | .table_tool_wrap { | ||
| 606 | width: 100%; | ||
| 607 | height: 84px !important; | ||
| 608 | padding: 0 8px; | ||
| 609 | |||
| 610 | .tools_btns { | ||
| 611 | padding: 0px 0 0; | ||
| 612 | } | ||
| 613 | } | ||
| 614 | |||
| 615 | .table_panel_wrap { | ||
| 616 | width: 100%; | ||
| 617 | height: calc(100% - 84px); | ||
| 618 | padding: 0px 8px 0; | ||
| 619 | } | ||
| 620 | </style> |
src/views/data_meta/executionLog.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: executionLog | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="excutionLog"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { ElMessage } from "element-plus"; | ||
| 8 | import Table from "@/components/Table/index.vue"; | ||
| 9 | import Drawer from '@/components/Drawer/index.vue' | ||
| 10 | |||
| 11 | import { | ||
| 12 | getMetaDataTaskLog,getMetacompareList,getMetaChangeRecord | ||
| 13 | } from "@/api/modules/dataMetaService" | ||
| 14 | import { useRouter, useRoute } from "vue-router"; | ||
| 15 | import Moment from 'moment'; | ||
| 16 | import { changeNum } from '@/utils/common' | ||
| 17 | import { TableColumnWidth } from "@/utils/enum"; | ||
| 18 | |||
| 19 | const { proxy } = getCurrentInstance() as any; | ||
| 20 | |||
| 21 | const router = useRouter(); | ||
| 22 | const route = useRoute(); | ||
| 23 | |||
| 24 | const collectTaskGuid: any = ref(route.query.guid); | ||
| 25 | const collectTaskType: any = ref(route.query.type); | ||
| 26 | const page = ref({ | ||
| 27 | limit: 50, | ||
| 28 | curr: 1, | ||
| 29 | sizes: [ | ||
| 30 | { label: "10", value: 10 }, | ||
| 31 | { label: "50", value: 50 }, | ||
| 32 | { label: "100", value: 100 }, | ||
| 33 | { label: "150", value: 150 }, | ||
| 34 | { label: "200", value: 200 }, | ||
| 35 | ], | ||
| 36 | }); | ||
| 37 | |||
| 38 | const originFields: any = ref([ | ||
| 39 | { label: "采集任务名称", field: "collectTaskName", width: 140 }, | ||
| 40 | { label: "数据源名称", field: "datasourceName", width: 160 }, | ||
| 41 | { label: "执行计划", field: "collectType", type: 'filter', width: 90 }, | ||
| 42 | { label: "执行状态", field: "execResult", width: 100, type: "tag", align: 'center' }, | ||
| 43 | { | ||
| 44 | label: "执行时间", field: "execTime", width: TableColumnWidth.DATETIME, getName: (scope) => { | ||
| 45 | let row = scope.row; | ||
| 46 | return row.execTime && Moment(row.execTime).format('YYYY-MM-DD HH:mm:ss'); | ||
| 47 | } | ||
| 48 | }, | ||
| 49 | { | ||
| 50 | label: "耗时(秒)", field: "execDuration", width: TableColumnWidth.EXECDURATION, align: "right", getName: (scope) => { | ||
| 51 | return scope.row.execDuration != null ? changeNum(scope.row.execDuration ?? 0) : '--'; | ||
| 52 | } | ||
| 53 | }, | ||
| 54 | ]); | ||
| 55 | |||
| 56 | const tableInfo = ref({ | ||
| 57 | id: "user-authority-table", | ||
| 58 | fields: [], | ||
| 59 | data: [], | ||
| 60 | page: { | ||
| 61 | type: "normal", | ||
| 62 | rows: 0, | ||
| 63 | ...page.value, | ||
| 64 | }, | ||
| 65 | actionInfo: { | ||
| 66 | label: "操作", | ||
| 67 | type: "btn", | ||
| 68 | width: 150, | ||
| 69 | fixed: 'right', | ||
| 70 | btns: (scope)=>{ | ||
| 71 | |||
| 72 | return [ | ||
| 73 | { label: "元数据变更记录", value: "log_detail",disabled:scope.row['execResult']==='N' || scope.row['execResult']==='R'}, | ||
| 74 | ] | ||
| 75 | } | ||
| 76 | }, | ||
| 77 | loading: false | ||
| 78 | }); | ||
| 79 | const metaChangePage: any = ref({ | ||
| 80 | limit: 50, | ||
| 81 | curr: 1, | ||
| 82 | sizes: [ | ||
| 83 | { label: "10", value: 10 }, | ||
| 84 | { label: "50", value: 50 }, | ||
| 85 | { label: "100", value: 100 }, | ||
| 86 | { label: "150", value: 150 }, | ||
| 87 | { label: "200", value: 200 }, | ||
| 88 | ], | ||
| 89 | metaTaskName: '', | ||
| 90 | changeState: null, | ||
| 91 | changeType: '' | ||
| 92 | }); | ||
| 93 | const formTable = ref({ | ||
| 94 | type: "table", | ||
| 95 | title: "", | ||
| 96 | col: 'no-margin', | ||
| 97 | |||
| 98 | tableInfo: { | ||
| 99 | id: "task-detail-table", | ||
| 100 | minHeight: 'unset', | ||
| 101 | fields: [ | ||
| 102 | { label: "序号", type: "index", width: 56, align: "center", fixed: "left" }, | ||
| 103 | { label: "表中文名称", field: "tableChName", width: 180, }, | ||
| 104 | { label: "表英文名称", field: "tableName", width: 180, }, | ||
| 105 | { label: "元数据名称", field: "metaName", width: 180, }, | ||
| 106 | { | ||
| 107 | label: "元数据类型", field: "metaType", width: 100, getName: (scope) => { | ||
| 108 | if (!scope.row.metaType) { | ||
| 109 | return '--'; | ||
| 110 | } | ||
| 111 | return scope.row.metaType == 'TABLE' ? '表' : (scope.row.metaType == 'INDEX' ? '索引' : (scope.row.metaType == 'PRI' ? '主键' : '字段')); | ||
| 112 | } | ||
| 113 | }, | ||
| 114 | { | ||
| 115 | label: "变更类型", field: "metaChangeType", width: 100, getName: (scope) => { | ||
| 116 | if (!scope.row.metaChangeType) { | ||
| 117 | return '--'; | ||
| 118 | } | ||
| 119 | return scope.row.metaChangeType == 'ADD' ? '新增' : (scope.row.metaChangeType == 'DELETE' ? '删除' : '修改'); | ||
| 120 | } | ||
| 121 | }, | ||
| 122 | { label: "变化", field: "metaChangeType", width: 100,type: 'popover',value:'metaCurrValue',column:[{field:"item",label:"变化信息",width:"150"},{field:"oldValue",label:"原值",width:"150"},{field:"newValue",label:"现值",width:"150"}] }, | ||
| 123 | // { label: "原值", field: "metaOldValue", width: 120 }, | ||
| 124 | { label: "元数据采集时间", field: "changeTime", width: 180, }, | ||
| 125 | // { label: "状态", field: "changeTime", width: 180, }, | ||
| 126 | // { label: "操作时间", field: "changeTime", width: 180, }, | ||
| 127 | ], | ||
| 128 | data: [], | ||
| 129 | loading: false, | ||
| 130 | page:{ | ||
| 131 | rows:0, | ||
| 132 | ...metaChangePage.value | ||
| 133 | }, | ||
| 134 | popoverData:[], | ||
| 135 | popoverloading:false, | ||
| 136 | showPage: true, | ||
| 137 | actionInfo: { | ||
| 138 | show: false | ||
| 139 | }, | ||
| 140 | }, | ||
| 141 | }) | ||
| 142 | |||
| 143 | const drawerInfo: any = ref({ | ||
| 144 | visible: false, | ||
| 145 | direction: "rtl", | ||
| 146 | modalClass: "wrap_width_auto", | ||
| 147 | size: 700, | ||
| 148 | modalClose:true, | ||
| 149 | header: { | ||
| 150 | title: "", | ||
| 151 | }, | ||
| 152 | type: '', | ||
| 153 | container: { | ||
| 154 | contents: [ | ||
| 155 | formTable.value, | ||
| 156 | ], | ||
| 157 | }, | ||
| 158 | footer: { | ||
| 159 | visible: false, | ||
| 160 | btns: [ | ||
| 161 | { type: 'default', label: '取消', value: 'cancel' }, | ||
| 162 | { type: 'primary', label: '确认 ', value: 'save' }, | ||
| 163 | ] | ||
| 164 | }, | ||
| 165 | }) | ||
| 166 | const currRow:any = ref({}) | ||
| 167 | const getFirstPageData = () => { | ||
| 168 | page.value.curr = 1 | ||
| 169 | toSearch({}) | ||
| 170 | } | ||
| 171 | |||
| 172 | const toSearch = (val: any, clear: boolean = false) => { | ||
| 173 | let params: any = Object.keys(val).length ? { ...val } : {} | ||
| 174 | params.pageIndex = page.value.curr; | ||
| 175 | params.pageSize = page.value.limit; | ||
| 176 | params.collectTaskGuid = collectTaskGuid.value | ||
| 177 | getTableData(params); | ||
| 178 | }; | ||
| 179 | |||
| 180 | const getTableData = (params) => { | ||
| 181 | tableInfo.value.loading = true | ||
| 182 | getMetaDataTaskLog(params).then((res: any) => { | ||
| 183 | if (res.code == proxy.$passCode) { | ||
| 184 | const data = res.data || {} | ||
| 185 | tableInfo.value.data = data.records || [] | ||
| 186 | tableInfo.value.page.limit = data.pageSize | ||
| 187 | tableInfo.value.page.curr = data.pageIndex | ||
| 188 | tableInfo.value.page.rows = data.totalRows | ||
| 189 | } else { | ||
| 190 | ElMessage({ | ||
| 191 | type: 'error', | ||
| 192 | message: res.msg, | ||
| 193 | }) | ||
| 194 | } | ||
| 195 | tableInfo.value.loading = false | ||
| 196 | }).catch(xhr => { | ||
| 197 | tableInfo.value.loading = false | ||
| 198 | }) | ||
| 199 | }; | ||
| 200 | |||
| 201 | const tablePageChange = (info) => { | ||
| 202 | page.value.curr = Number(info.curr); | ||
| 203 | page.value.limit = Number(info.limit); | ||
| 204 | toSearch({}) | ||
| 205 | }; | ||
| 206 | const getMetaChangeTableData = ()=>{ | ||
| 207 | formTable.value.tableInfo.loading = true | ||
| 208 | getMetaChangeRecord(Object.assign({execGuid:currRow.value.guid},{pageIndex:metaChangePage.value.curr,pageSize:metaChangePage.value.limit})).then((res: any) => { | ||
| 209 | formTable.value.tableInfo.loading = false | ||
| 210 | if (res.code == proxy.$passCode && res.data) { | ||
| 211 | let data = res.data || {} | ||
| 212 | formTable.value.tableInfo.data = data.records || [] | ||
| 213 | formTable.value.tableInfo.page.rows = data.totalRows | ||
| 214 | const contents = [formTable.value] | ||
| 215 | drawerInfo.value.container.contents = contents | ||
| 216 | drawerInfo.value.footer.visible = false | ||
| 217 | drawerInfo.value.visible = true | ||
| 218 | } else { | ||
| 219 | ElMessage({ | ||
| 220 | type: "error", | ||
| 221 | message: '未获取到对应人员信息', | ||
| 222 | }); | ||
| 223 | } | ||
| 224 | }) | ||
| 225 | } | ||
| 226 | const drawerTableClick = (scope,btn)=>{ | ||
| 227 | drawerInfo.value.container.contents[0].tableInfo.popoverloading = true | ||
| 228 | getMetacompareList(scope.row.guid).then((res:any)=>{ | ||
| 229 | drawerInfo.value.container.contents[0].tableInfo.popoverloading = false | ||
| 230 | drawerInfo.value.container.contents[0].tableInfo.popoverData = res.data | ||
| 231 | |||
| 232 | }) | ||
| 233 | } | ||
| 234 | const tableBtnClick = (scope, btn) => { | ||
| 235 | const type = btn.value; | ||
| 236 | const row = scope.row; | ||
| 237 | currRow.value = row | ||
| 238 | if (type == 'log_detail') { | ||
| 239 | drawerInfo.value.header.title = row.collectTaskName; | ||
| 240 | drawerInfo.value.type = type | ||
| 241 | formTable.value.tableInfo.data = [] | ||
| 242 | const contents = [formTable.value] | ||
| 243 | drawerInfo.value.container.contents = contents | ||
| 244 | drawerInfo.value.footer.visible = false | ||
| 245 | drawerInfo.value.visible = true | ||
| 246 | getMetaChangeTableData() | ||
| 247 | } | ||
| 248 | }; | ||
| 249 | |||
| 250 | const drawerBtnClick = (btn, info) => { | ||
| 251 | if (btn.value == 'cancel') { | ||
| 252 | drawerInfo.value.visible = false | ||
| 253 | } | ||
| 254 | } | ||
| 255 | const metaChangeTablePageChange = (info) => { | ||
| 256 | |||
| 257 | metaChangePage.value.curr = Number(info.curr); | ||
| 258 | metaChangePage.value.limit = Number(info.limit); | ||
| 259 | formTable.value.tableInfo.page.limit = metaChangePage.value.limit; | ||
| 260 | formTable.value.tableInfo.page.curr = metaChangePage.value.curr; | ||
| 261 | getMetaChangeTableData(); | ||
| 262 | }; | ||
| 263 | onBeforeMount(() => { | ||
| 264 | if (collectTaskType.value == '1') { | ||
| 265 | originFields.value.splice(2, 0, { label: "同步策略", field: "collectMode", type: 'filter', width: 90 },) | ||
| 266 | } | ||
| 267 | tableInfo.value.fields = originFields.value; | ||
| 268 | getFirstPageData(); | ||
| 269 | }); | ||
| 270 | </script> | ||
| 271 | |||
| 272 | <template> | ||
| 273 | <div class="container_wrap"> | ||
| 274 | <div class="table_panel_wrap"> | ||
| 275 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tablePageChange="tablePageChange" /> | ||
| 276 | </div> | ||
| 277 | <Drawer :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick" @drawerTableBtnClick="drawerTableClick" @drawerTablePageChange="metaChangeTablePageChange" /> | ||
| 278 | </div> | ||
| 279 | </template> | ||
| 280 | |||
| 281 | <style lang="scss" scoped> | ||
| 282 | .container_wrap { | ||
| 283 | padding: 0; | ||
| 284 | |||
| 285 | .table_panel_wrap { | ||
| 286 | height: 100%; | ||
| 287 | padding: 16px 16px 0; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | :deep(.el-drawer) { | ||
| 292 | |||
| 293 | .el-drawer__body { | ||
| 294 | padding: 10px 10px 0px 10px; | ||
| 295 | } | ||
| 296 | |||
| 297 | .drawer_panel { | ||
| 298 | height: 100%; | ||
| 299 | |||
| 300 | .table_panel_wrap { | ||
| 301 | height: 100%; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | |||
| 306 | } | ||
| 307 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_meta/metaSheet.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: metaSheet | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="metaSheet"> | ||
| 6 | import { ref } from 'vue' | ||
| 7 | import { useRouter, useRoute } from "vue-router"; | ||
| 8 | import { ElMessage } from "element-plus"; | ||
| 9 | import { CaretRight } from '@element-plus/icons-vue' | ||
| 10 | import Table from '@/components/Table/index.vue' | ||
| 11 | import Tabs from '@/components/Tabs/index.vue' | ||
| 12 | import Drawer from '@/components/Drawer/index.vue' | ||
| 13 | import { changeNum } from '@/utils/common' | ||
| 14 | import useUserStore from "@/store/modules/user"; | ||
| 15 | import LineageGraph from '@/components/LineageGraph/index.vue'; | ||
| 16 | import EllipsisTooltip from '@/components/EllipsisTooltip.vue'; | ||
| 17 | import { | ||
| 18 | getMetaDetail, | ||
| 19 | getMetaSheetField, | ||
| 20 | getMetaSheetKeys, | ||
| 21 | getMetaChange, | ||
| 22 | getMetaChangeRecord, | ||
| 23 | getTableLineage, | ||
| 24 | getTableAllFieldLineage, | ||
| 25 | getMetacompareList, | ||
| 26 | saveMetaReportAnalysis, | ||
| 27 | checkTableData | ||
| 28 | } from '@/api/modules/dataMetaService'; | ||
| 29 | import { getFileUrl } from "@/api/modules/queryService" | ||
| 30 | import useDataMetaStore from "@/store/modules/dataMeta" | ||
| 31 | import { TableColumnWidth } from '@/utils/enum'; | ||
| 32 | |||
| 33 | const { proxy } = getCurrentInstance() as any; | ||
| 34 | const router = useRouter(); | ||
| 35 | const { set } = useDataMetaStore() | ||
| 36 | const lineageData = ref([]); | ||
| 37 | |||
| 38 | const userStore = useUserStore(); | ||
| 39 | const detailGuid: any = ref('') | ||
| 40 | const sheetInfo: any = ref({}) | ||
| 41 | const listLoading = ref(false) | ||
| 42 | const sheetList = ref([ | ||
| 43 | { label: '创建时间', field: 'metaCreateTime', value: '--' }, | ||
| 44 | { label: '修改时间', field: 'metaUpdateTime', value: '--' } | ||
| 45 | ]) | ||
| 46 | const fieldList = ref([ | ||
| 47 | { label: '存储量(约)(MB)', field: 'storageCapacities', value: 0.00, type: 'chnum', fixedNum: 2 }, | ||
| 48 | { label: '总字段数', field: 'columns', value: 0, type: 'chnum' }, | ||
| 49 | { label: '总行数(约)', field: 'tableRows', value: 0, type: 'chnum' }, | ||
| 50 | { label: '数据总量(个)', field: 'dataCount', value: 0, type: 'chnum' }, | ||
| 51 | { label: '是否分区表', field: 'isPartition', value: '--', type: 'filter' }, | ||
| 52 | { label: '索引数', field: 'indexes', value: 0, type: 'chnum' }, | ||
| 53 | { label: '储存引擎', field: 'tableEngine', value: '--' }, | ||
| 54 | { label: '字符集', field: 'characterSet', value: '--' }, | ||
| 55 | { label: '外部表信息', field: 'wbb', value: '--' }, | ||
| 56 | ]) | ||
| 57 | |||
| 58 | const tabsActiveName = ref('first') | ||
| 59 | const tabsInfo = ref({ | ||
| 60 | activeName: '', | ||
| 61 | tabs: [ | ||
| 62 | { label: '基础信息', name: 'first' }, | ||
| 63 | { label: '数据血缘', name: 'second' }, | ||
| 64 | { label: '变更记录', name: 'third' } | ||
| 65 | ] | ||
| 66 | }) | ||
| 67 | /** 切换布局 */ | ||
| 68 | const isToggle = ref(true); | ||
| 69 | /** 是否显示英文名称,默认不显示。 */ | ||
| 70 | const isCh = ref(false); | ||
| 71 | /** 是否是字段血缘关系 */ | ||
| 72 | const isFieldLineage = ref(false); | ||
| 73 | const fieldTableInfo = ref({ | ||
| 74 | id: 'field-table', | ||
| 75 | minHeight: 'unset', | ||
| 76 | maxHeight: '100%', | ||
| 77 | fields: [ | ||
| 78 | { label: "序号", type: "index", width: 56, align: "center", fixed: "left" }, | ||
| 79 | { label: "字段英文名称", field: "enName", width: 150 }, | ||
| 80 | { label: "字段中文名称", field: "chName", width: 150 }, | ||
| 81 | { label: "注释", field: "comment", width: TableColumnWidth.TABLENAME }, | ||
| 82 | { label: "字段类型", field: "fieldTypeChName", width: 100 }, | ||
| 83 | { label: "长度", field: "fieldLength", width: 100, align: 'right', type: 'chnum', isNaN: true }, | ||
| 84 | { label: "精度", field: "fieldPrecision", width: 100, align: 'right' }, | ||
| 85 | { label: "是否主键", field: "isPrimary", type: 'filter', width: 100 }, | ||
| 86 | // { label: "是否外键", field: "isFk", type: 'filter', width: 100 }, | ||
| 87 | { label: "是否必填 ", field: "notNull", type: 'filter', width: 100 }, | ||
| 88 | // { label: "是否分区", field: "isPartition", type: 'filter', width: 100 }, | ||
| 89 | ], | ||
| 90 | data: [], | ||
| 91 | showPage: false, | ||
| 92 | actionInfo: { | ||
| 93 | label: "操作", | ||
| 94 | type: "btn", | ||
| 95 | width: 90, | ||
| 96 | fixed: 'right', | ||
| 97 | btns: [ | ||
| 98 | { | ||
| 99 | label: "查看血缘", value: "view", click: (scope, btn) => { | ||
| 100 | //跳转倒查看血缘页面。 | ||
| 101 | let row = scope.row; | ||
| 102 | set({ | ||
| 103 | tableGuid: sheetInfo.value.guid, | ||
| 104 | table: sheetInfo.value.tableName, | ||
| 105 | databas: sheetInfo.value.databaseName, | ||
| 106 | databaseCh: sheetInfo.value.databaseChName, | ||
| 107 | dsGuid: sheetInfo.value.databaseGuid, | ||
| 108 | fGuid: row.guid, | ||
| 109 | fEnName: row.enName | ||
| 110 | }) | ||
| 111 | router.push({ | ||
| 112 | name: "analysisView", | ||
| 113 | }) | ||
| 114 | } | ||
| 115 | }, | ||
| 116 | ], | ||
| 117 | }, | ||
| 118 | loading: false | ||
| 119 | }) | ||
| 120 | |||
| 121 | const indexTableInfo = ref({ | ||
| 122 | id: 'index-table', | ||
| 123 | minHeight: 'unset', | ||
| 124 | maxHeight: '100%', | ||
| 125 | fields: [ | ||
| 126 | { label: "序号", type: "index", width: 56, align: "center", fixed: "left" }, | ||
| 127 | { label: "索引名", field: "indexName", width: 160 }, | ||
| 128 | { label: "索引类型", field: "indexType", width: 100 }, | ||
| 129 | { label: "索引字段", field: "indexColumn", width: 160 }, | ||
| 130 | { label: "唯一性", field: "isUnique", type: 'filter', width: 120, }, | ||
| 131 | { label: "索引大小(MB)", field: "indexLength", width: 150, align: 'right' }, | ||
| 132 | // { label: "创建时间", field: "createTime", width: 180, }, | ||
| 133 | ], | ||
| 134 | data: [], | ||
| 135 | showPage: false, | ||
| 136 | actionInfo: { | ||
| 137 | show: false | ||
| 138 | }, | ||
| 139 | loading: false | ||
| 140 | }) | ||
| 141 | |||
| 142 | const logPage = ref({ | ||
| 143 | limit: 50, | ||
| 144 | curr: 1, | ||
| 145 | sizes: [ | ||
| 146 | { label: "10", value: 10 }, | ||
| 147 | { label: "50", value: 50 }, | ||
| 148 | { label: "100", value: 100 }, | ||
| 149 | { label: "150", value: 150 }, | ||
| 150 | { label: "200", value: 200 }, | ||
| 151 | ], | ||
| 152 | }) | ||
| 153 | const currLogTableData: any = ref({}) | ||
| 154 | const logTableInfo = ref({ | ||
| 155 | id: 'log-table', | ||
| 156 | fields: [ | ||
| 157 | { label: "序号", type: "index", width: 56, align: "center", fixed: "left" }, | ||
| 158 | { label: "采集任务名称", field: "collectTaskName", width: 140, type: 'text_btn', class: 'drawer-detail-cell', value: 'task-detail' }, | ||
| 159 | // { label: "采集时间", field: "createTime", width: 180, }, | ||
| 160 | { label: "同步策略", field: "collectMode", type: 'filter', width: 120 }, | ||
| 161 | { label: "新增元数据", field: "addChangeRecordNum", type: 'chnum', width: 120, align: 'right' }, | ||
| 162 | { label: "修改元数据", field: "updateChangeRecordNum", type: 'chnum', width: 120, align: 'right' }, | ||
| 163 | { label: "删除元数据", field: "deleteChangeRecordNum", type: 'chnum', width: 120, align: 'right' }, | ||
| 164 | ], | ||
| 165 | data: [ | ||
| 166 | { | ||
| 167 | taskName: '采集任务1', | ||
| 168 | updateTime: '2022-12-22 10:30:02', | ||
| 169 | addnum: 1350, | ||
| 170 | updatenum: 239, | ||
| 171 | deletenum: 20, | ||
| 172 | operator: '' | ||
| 173 | } | ||
| 174 | ], | ||
| 175 | page: { | ||
| 176 | type: "normal", | ||
| 177 | rows: 0, | ||
| 178 | ...logPage.value | ||
| 179 | }, | ||
| 180 | actionInfo: { | ||
| 181 | show: false | ||
| 182 | }, | ||
| 183 | loading: false | ||
| 184 | }) | ||
| 185 | |||
| 186 | const tabsPane: any = ref([]) | ||
| 187 | const tabsPaneMap = ref({ | ||
| 188 | first: [ | ||
| 189 | { | ||
| 190 | title: '字段信息', | ||
| 191 | type: 'table', | ||
| 192 | tableInfo: fieldTableInfo.value, | ||
| 193 | show: true | ||
| 194 | }, { | ||
| 195 | title: '索引信息', | ||
| 196 | type: 'table', | ||
| 197 | isIndex: true, | ||
| 198 | tableInfo: indexTableInfo.value, | ||
| 199 | show: true | ||
| 200 | }, | ||
| 201 | ], | ||
| 202 | second: [ | ||
| 203 | { | ||
| 204 | title: '', | ||
| 205 | type: 'X6' | ||
| 206 | }, | ||
| 207 | ], | ||
| 208 | third: [ | ||
| 209 | { | ||
| 210 | title: '', | ||
| 211 | type: 'table', | ||
| 212 | tableInfo: logTableInfo.value | ||
| 213 | }, | ||
| 214 | ] | ||
| 215 | }) | ||
| 216 | |||
| 217 | const page = ref({ | ||
| 218 | limit: 50, | ||
| 219 | curr: 1, | ||
| 220 | sizes: [ | ||
| 221 | { label: "10", value: 10 }, | ||
| 222 | { label: "50", value: 50 }, | ||
| 223 | { label: "100", value: 100 }, | ||
| 224 | { label: "150", value: 150 }, | ||
| 225 | { label: "200", value: 200 }, | ||
| 226 | ], | ||
| 227 | }); | ||
| 228 | const formInline1 = ref({ pageName: "" }) | ||
| 229 | const formTable = ref({ | ||
| 230 | type: "table", | ||
| 231 | title: "", | ||
| 232 | col: 'no-margin', | ||
| 233 | tableInfo: { | ||
| 234 | loading: false, | ||
| 235 | id: "task-detail-table", | ||
| 236 | minHeight: 'unset', | ||
| 237 | fields: [ | ||
| 238 | { label: "序号", type: "index", width: 56, align: "center", fixed: "left" }, | ||
| 239 | { label: "元数据名称", field: "metaName", width: 150, align: "center", fixed: "left" }, | ||
| 240 | { | ||
| 241 | label: "元数据类型", field: "metaType", width: 100, getName: (scope) => { | ||
| 242 | if (!scope.row.metaType) { | ||
| 243 | return '--'; | ||
| 244 | } | ||
| 245 | return scope.row.metaType == 'TABLE' ? '表' : (scope.row.metaType == 'INDEX' ? '索引' : (scope.row.metaType == 'PRI' ? '主键' : '字段')); | ||
| 246 | } | ||
| 247 | }, | ||
| 248 | |||
| 249 | { | ||
| 250 | label: "变更类型", field: "metaChangeType", width: 100, getName: (scope) => { | ||
| 251 | if (!scope.row.metaChangeType) { | ||
| 252 | return '--'; | ||
| 253 | } | ||
| 254 | return scope.row.metaChangeType == 'ADD' ? '新增' : (scope.row.metaChangeType == 'DELETE' ? '删除' : '修改'); | ||
| 255 | } | ||
| 256 | }, | ||
| 257 | { label: "变化", field: "metaChangeType", width: 100, type: 'popover', value: 'metaCurrValue', column: [{ field: "item", label: "变化信息", width: "150" }, { field: "oldValue", label: "原值", width: "150" }, { field: "newValue", label: "现值", width: "150" }] }, | ||
| 258 | // { label: "原值", field: "metaOldValue", width: 120 }, | ||
| 259 | { label: "采集时间", field: "changeTime", width: 180, }, | ||
| 260 | ], | ||
| 261 | data: [], | ||
| 262 | popoverData: [], | ||
| 263 | popoverloading: false, | ||
| 264 | page: { | ||
| 265 | type: "normal", | ||
| 266 | rows: 0, | ||
| 267 | ...page.value, | ||
| 268 | }, | ||
| 269 | actionInfo: { | ||
| 270 | show: false | ||
| 271 | }, | ||
| 272 | }, | ||
| 273 | }) | ||
| 274 | const drawerInfo: any = ref({ | ||
| 275 | visible: false, | ||
| 276 | direction: "rtl", | ||
| 277 | modalClass: "wrap_width_auto", | ||
| 278 | modalClose: false, | ||
| 279 | modal: true, | ||
| 280 | size: 600, | ||
| 281 | header: { | ||
| 282 | title: "", | ||
| 283 | }, | ||
| 284 | type: '', | ||
| 285 | container: { | ||
| 286 | contents: [ | ||
| 287 | formTable.value, | ||
| 288 | ], | ||
| 289 | }, | ||
| 290 | footer: { | ||
| 291 | visible: false, | ||
| 292 | btns: [ | ||
| 293 | { type: 'default', label: '取消', value: 'cancel' }, | ||
| 294 | { type: 'primary', label: '确认 ', value: 'save' }, | ||
| 295 | ] | ||
| 296 | }, | ||
| 297 | }) | ||
| 298 | const drawerTableClick = (scope, btn) => { | ||
| 299 | drawerInfo.value.container.contents[0].tableInfo.popoverloading = true | ||
| 300 | getMetacompareList(scope.row.guid).then((res: any) => { | ||
| 301 | drawerInfo.value.container.contents[0].tableInfo.popoverloading = false | ||
| 302 | drawerInfo.value.container.contents[0].tableInfo.popoverData = res.data | ||
| 303 | |||
| 304 | }) | ||
| 305 | } | ||
| 306 | /** 数据血缘加载状态。 */ | ||
| 307 | const lineageDataLoading = ref(true); | ||
| 308 | |||
| 309 | const getDetailInfo = () => { | ||
| 310 | getSheetDetail() | ||
| 311 | getSheetField() | ||
| 312 | getSheetKeys() | ||
| 313 | logPage.value.curr = 1 | ||
| 314 | getSheetLog(); | ||
| 315 | getTableLineageMap(); | ||
| 316 | } | ||
| 317 | |||
| 318 | const getSheetDetail = () => { | ||
| 319 | listLoading.value = true | ||
| 320 | getMetaDetail(detailGuid.value).then((res: any) => { | ||
| 321 | listLoading.value = false | ||
| 322 | if (res.code == proxy.$passCode) { | ||
| 323 | const data = res.data || {} | ||
| 324 | sheetInfo.value = data | ||
| 325 | let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === router.currentRoute.value.fullPath); | ||
| 326 | if (tab) { | ||
| 327 | tab.meta.title = `元数据详情-${data.tableChName || data.tableName}`; | ||
| 328 | } | ||
| 329 | fieldList.value.map((item: any) => { | ||
| 330 | if (item.type && item.type == 'chnum') { | ||
| 331 | item.value = data[item.field] ?? 0 | ||
| 332 | } else { | ||
| 333 | item.value = data[item.field] ?? '--' | ||
| 334 | } | ||
| 335 | }) | ||
| 336 | sheetList.value.map((item: any) => { | ||
| 337 | item.value = data[item.field] ?? '--' | ||
| 338 | }) | ||
| 339 | } else { | ||
| 340 | ElMessage({ | ||
| 341 | type: 'error', | ||
| 342 | message: res.msg, | ||
| 343 | }) | ||
| 344 | } | ||
| 345 | }).catch(() => { | ||
| 346 | listLoading.value = false | ||
| 347 | }) | ||
| 348 | } | ||
| 349 | |||
| 350 | const getSheetField = () => { | ||
| 351 | let params = { | ||
| 352 | tableGuid: detailGuid.value | ||
| 353 | } | ||
| 354 | fieldTableInfo.value.loading = true | ||
| 355 | getMetaSheetField(params).then((res: any) => { | ||
| 356 | if (res.code == proxy.$passCode) { | ||
| 357 | const data = res.data || [] | ||
| 358 | fieldTableInfo.value.data = data | ||
| 359 | } else { | ||
| 360 | ElMessage({ | ||
| 361 | type: 'error', | ||
| 362 | message: res.msg, | ||
| 363 | }) | ||
| 364 | } | ||
| 365 | fieldTableInfo.value.loading = false | ||
| 366 | }).catch(() => { | ||
| 367 | fieldTableInfo.value.loading = false | ||
| 368 | }) | ||
| 369 | } | ||
| 370 | |||
| 371 | const getSheetKeys = () => { | ||
| 372 | let params = { | ||
| 373 | tableGuid: detailGuid.value | ||
| 374 | } | ||
| 375 | indexTableInfo.value.loading = true | ||
| 376 | getMetaSheetKeys(params).then((res: any) => { | ||
| 377 | if (res.code == proxy.$passCode) { | ||
| 378 | const data = res.data || [] | ||
| 379 | indexTableInfo.value.data = data | ||
| 380 | } else { | ||
| 381 | ElMessage({ | ||
| 382 | type: 'error', | ||
| 383 | message: res.msg, | ||
| 384 | }) | ||
| 385 | } | ||
| 386 | indexTableInfo.value.loading = false | ||
| 387 | }).catch(() => { | ||
| 388 | indexTableInfo.value.loading = false | ||
| 389 | }) | ||
| 390 | } | ||
| 391 | |||
| 392 | const getSheetLog = () => { | ||
| 393 | let params = { | ||
| 394 | tableGuid: detailGuid.value, | ||
| 395 | pageIndex: logPage.value.curr, | ||
| 396 | pageSize: logPage.value.limit | ||
| 397 | } | ||
| 398 | logTableInfo.value.loading = true | ||
| 399 | getMetaChange(params).then((res: any) => { | ||
| 400 | if (res.code == proxy.$passCode) { | ||
| 401 | const data = res.data || {} | ||
| 402 | logTableInfo.value.data = data.records ?? [] | ||
| 403 | logTableInfo.value.page.limit = data.pageSize | ||
| 404 | logTableInfo.value.page.curr = data.pageIndex | ||
| 405 | logTableInfo.value.page.rows = data.totalRows | ||
| 406 | } else { | ||
| 407 | ElMessage({ | ||
| 408 | type: 'error', | ||
| 409 | message: res.msg, | ||
| 410 | }) | ||
| 411 | } | ||
| 412 | logTableInfo.value.loading = false | ||
| 413 | }).catch(() => { | ||
| 414 | logTableInfo.value.loading = false | ||
| 415 | }) | ||
| 416 | } | ||
| 417 | |||
| 418 | |||
| 419 | const tablePageChange = (info) => { | ||
| 420 | logPage.value.curr = Number(info.curr); | ||
| 421 | logPage.value.limit = Number(info.limit); | ||
| 422 | getSheetLog() | ||
| 423 | }; | ||
| 424 | |||
| 425 | const tableBtnClick = (scope, btn) => { | ||
| 426 | const type = btn.value; | ||
| 427 | const row = scope.row; | ||
| 428 | currLogTableData.value = row; | ||
| 429 | if (type == "task-detail") { | ||
| 430 | page.value.limit = 50; | ||
| 431 | page.value.curr = 1; | ||
| 432 | drawerInfo.value.header.title = row.collectTaskName; | ||
| 433 | drawerInfo.value.type = type | ||
| 434 | let params = { | ||
| 435 | collectTaskGuid: row.guid, | ||
| 436 | tableGuid: detailGuid.value, | ||
| 437 | pageSize: page.value.limit, | ||
| 438 | pageIndex: page.value.curr | ||
| 439 | } | ||
| 440 | getMetaChangeRecordData(params); | ||
| 441 | } | ||
| 442 | }; | ||
| 443 | |||
| 444 | const getMetaChangeRecordData = (params) => { | ||
| 445 | formTable.value.tableInfo.loading = true; | ||
| 446 | getMetaChangeRecord(params).then((res: any) => { | ||
| 447 | formTable.value.tableInfo.loading = false; | ||
| 448 | if (res.code == proxy.$passCode && res.data) { | ||
| 449 | let data = res.data || {} | ||
| 450 | formTable.value.tableInfo.data = data.records || [] | ||
| 451 | formTable.value.tableInfo.page.limit = data.pageSize; | ||
| 452 | formTable.value.tableInfo.page.curr = data.pageIndex; | ||
| 453 | formTable.value.tableInfo.page.rows = data.totalRows; | ||
| 454 | const contents = [formTable.value] | ||
| 455 | drawerInfo.value.container.contents = contents | ||
| 456 | drawerInfo.value.footer.visible = false | ||
| 457 | drawerInfo.value.visible = true | ||
| 458 | } else { | ||
| 459 | ElMessage({ | ||
| 460 | type: "error", | ||
| 461 | message: res.msg, | ||
| 462 | }); | ||
| 463 | } | ||
| 464 | }) | ||
| 465 | } | ||
| 466 | |||
| 467 | const filterValue = (row) => { | ||
| 468 | let val: any = row.value || '--' | ||
| 469 | if (row.type && row.type == 'chnum') { | ||
| 470 | val = changeNum(row.value, row.fixedNum ?? 0) | ||
| 471 | } else if (row.type && row.type == 'filter') { | ||
| 472 | if (row.field == 'isPartition') { | ||
| 473 | switch (row.value) { | ||
| 474 | case "Y": | ||
| 475 | val = '是' | ||
| 476 | break; | ||
| 477 | case "N": | ||
| 478 | val = '否' | ||
| 479 | break; | ||
| 480 | default: | ||
| 481 | val = '--' | ||
| 482 | break; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | } | ||
| 486 | return val | ||
| 487 | } | ||
| 488 | |||
| 489 | const tabChange = (tab) => { | ||
| 490 | tabsActiveName.value = tab | ||
| 491 | tabsPane.value = tabsPaneMap.value[tabsActiveName.value] | ||
| 492 | } | ||
| 493 | |||
| 494 | const drawerBtnClick = (btn, info) => { | ||
| 495 | console.log(btn) | ||
| 496 | if (btn.value == 'cancel') { | ||
| 497 | drawerInfo.value.visible = false | ||
| 498 | } else if (btn.value = "metaCurrValue") { | ||
| 499 | console.log((1233)) | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | const drawerTablePageChange = (info) => { | ||
| 504 | page.value.curr = Number(info.curr); | ||
| 505 | page.value.limit = Number(info.limit); | ||
| 506 | if (!currLogTableData.value?.guid) { | ||
| 507 | return; | ||
| 508 | } | ||
| 509 | getMetaChangeRecordData({ | ||
| 510 | collectTaskGuid: currLogTableData.value?.guid, | ||
| 511 | tableGuid: detailGuid.value, | ||
| 512 | pageSize: page.value.limit, | ||
| 513 | pageIndex: page.value.curr | ||
| 514 | }); | ||
| 515 | } | ||
| 516 | |||
| 517 | onBeforeMount(() => { | ||
| 518 | detailGuid.value = router.currentRoute.value.query?.id || '' | ||
| 519 | tabsInfo.value.activeName = tabsActiveName.value | ||
| 520 | tabsPane.value = tabsPaneMap.value[tabsActiveName.value] | ||
| 521 | getDetailInfo() | ||
| 522 | }) | ||
| 523 | |||
| 524 | onMounted(() => { | ||
| 525 | let dom = document.getElementById('main-app'); | ||
| 526 | if (dom) { | ||
| 527 | dom.addEventListener('click', (event: any) => { | ||
| 528 | if (!event.target?.classList?.contains('drawer-detail-cell')) { | ||
| 529 | if (drawerInfo.value.visible) { | ||
| 530 | drawerInfo.value.visible = false; | ||
| 531 | } | ||
| 532 | } | ||
| 533 | }); | ||
| 534 | } | ||
| 535 | }); | ||
| 536 | const lineageGraph = ref() | ||
| 537 | const file: any = ref({}) | ||
| 538 | const handleSave = (file1, fullRef) => { | ||
| 539 | file.value = file1 | ||
| 540 | dialogInfo1.value.visible = true | ||
| 541 | nextTick(() => { | ||
| 542 | const ele = document.querySelector(".saveDialog1") | ||
| 543 | if (ele) { | ||
| 544 | fullRef.appendChild(ele) | ||
| 545 | } | ||
| 546 | }) | ||
| 547 | } | ||
| 548 | const pageSave = () => { | ||
| 549 | const analysisReportName = reportDialogRef.value.dialogFormRef[0].formInline.analysisReportName; | ||
| 550 | if (!analysisReportName) { | ||
| 551 | ElMessage({ | ||
| 552 | type: "error", | ||
| 553 | message: "血缘关系名称不能为空!", | ||
| 554 | appendTo: lineageGraph.value[0].containerRef | ||
| 555 | }) | ||
| 556 | return | ||
| 557 | } | ||
| 558 | let formData = new FormData(); | ||
| 559 | formData.append('file', file.value); | ||
| 560 | formData.append('fileName', `${analysisReportName}.png`); | ||
| 561 | console.log(formInline1.value.pageName) | ||
| 562 | getFileUrl(formData).then((res) => { | ||
| 563 | saveMetaReportAnalysis({ | ||
| 564 | table: sheetInfo.value.tableName, | ||
| 565 | database: sheetInfo.value.databaseName, | ||
| 566 | analysisReportUrl: res.data, | ||
| 567 | analysisReportName: analysisReportName, | ||
| 568 | databaseChName: sheetInfo.value.databaseChName | ||
| 569 | }).then((res: any) => { | ||
| 570 | if (res.code == proxy.$passCode) { | ||
| 571 | // ElMessage.success("保存成功") | ||
| 572 | ElMessage({ | ||
| 573 | type: "success", | ||
| 574 | message: "保存成功", | ||
| 575 | appendTo: lineageGraph.value[0].containerRef | ||
| 576 | }) | ||
| 577 | dialogInfo1.value.visible = false | ||
| 578 | } else { | ||
| 579 | ElMessage({ | ||
| 580 | type: "error", | ||
| 581 | message: res.msg, | ||
| 582 | appendTo: lineageGraph.value[0].containerRef | ||
| 583 | }) | ||
| 584 | } | ||
| 585 | }) | ||
| 586 | }).catch((res) => { | ||
| 587 | ElMessage({ | ||
| 588 | type: "error", | ||
| 589 | message: res.msg, | ||
| 590 | appendTo: lineageGraph.value[0].containerRef | ||
| 591 | }) | ||
| 592 | }) | ||
| 593 | } | ||
| 594 | |||
| 595 | const formItems1: any = ref([ | ||
| 596 | { | ||
| 597 | label: '血缘关系名称', | ||
| 598 | type: 'input', | ||
| 599 | placeholder: '请输入', | ||
| 600 | field: 'analysisReportName', | ||
| 601 | default: '', | ||
| 602 | clearable: true, | ||
| 603 | required: true | ||
| 604 | }, | ||
| 605 | ]) | ||
| 606 | const formRules1: any = ref({ | ||
| 607 | analysisReportName: [ | ||
| 608 | { | ||
| 609 | required: true, | ||
| 610 | message: "请填写血缘关系名称", | ||
| 611 | trigger: "blur", | ||
| 612 | }, | ||
| 613 | ], | ||
| 614 | }) | ||
| 615 | const formInfo1 = ref({ | ||
| 616 | type: 'form', | ||
| 617 | title: '', | ||
| 618 | formInfo: { | ||
| 619 | id: 'add-dict-form', | ||
| 620 | items: formItems1.value, | ||
| 621 | rules: formRules1.value | ||
| 622 | } | ||
| 623 | }) | ||
| 624 | const reportDialogRef = ref(); | ||
| 625 | const dialogInfo1 = ref({ | ||
| 626 | visible: false, | ||
| 627 | size: 500, | ||
| 628 | direction: "column", | ||
| 629 | modalClass: "saveDialog1", | ||
| 630 | header: { | ||
| 631 | title: "保存为血缘关系图片", | ||
| 632 | }, | ||
| 633 | type: '', | ||
| 634 | contents: [ | ||
| 635 | formInfo1.value, | ||
| 636 | ], | ||
| 637 | footer: { | ||
| 638 | btns: [ | ||
| 639 | { type: "default", label: "取消", value: "cancel" }, | ||
| 640 | { type: "primary", label: "保存", value: "submit" }, | ||
| 641 | ], | ||
| 642 | }, | ||
| 643 | }); | ||
| 644 | const dialogBtnClick1 = (btn, scope) => { | ||
| 645 | if (btn.value === "cancel") { | ||
| 646 | dialogInfo1.value.visible = false | ||
| 647 | } else { | ||
| 648 | pageSave() | ||
| 649 | } | ||
| 650 | |||
| 651 | } | ||
| 652 | const handleTableContextMenu = (tableGuid, tableName, database, btnType, vertexId, databaseChName, chTable, neighbor, fieldGuid) => { | ||
| 653 | if (btnType == 1) {//查看元数据详情 | ||
| 654 | lineageGraph.value[0].handleEdit({});//先退出全屏再跳转,否则回来查看时就没有了。 | ||
| 655 | checkTableData(tableGuid).then((res: any) => { | ||
| 656 | if (res.code === proxy.$passCode) { | ||
| 657 | if (res.data) { | ||
| 658 | router.push({ | ||
| 659 | path: '/data-meta/metadata-query/meta-sheet', | ||
| 660 | query: { | ||
| 661 | id: tableGuid, | ||
| 662 | name: chTable | ||
| 663 | } | ||
| 664 | }) | ||
| 665 | } else { | ||
| 666 | ElMessage.warning("元数据详情不存在") | ||
| 667 | } | ||
| 668 | } | ||
| 669 | }) | ||
| 670 | } | ||
| 671 | } | ||
| 672 | const handleRefres = () => { | ||
| 673 | if (isFieldLineage.value) { | ||
| 674 | getAllTableFieldLineageMap(); | ||
| 675 | } else { | ||
| 676 | getTableLineageMap(); | ||
| 677 | } | ||
| 678 | } | ||
| 679 | const handleEdit = () => { | ||
| 680 | lineageGraph.value[0].handleEdit({});//先退出全屏再跳转,否则回来查看时就没有了。 | ||
| 681 | set({ | ||
| 682 | tableGuid: sheetInfo.value.guid, | ||
| 683 | table: sheetInfo.value.tableName, | ||
| 684 | databas: sheetInfo.value.databaseName, | ||
| 685 | databaseCh: sheetInfo.value.databaseChName, | ||
| 686 | dsGuid: sheetInfo.value.databaseGuid, | ||
| 687 | isFieldLine: isFieldLineage.value | ||
| 688 | }) | ||
| 689 | router.push({ | ||
| 690 | name: "analysisView", | ||
| 691 | }) | ||
| 692 | } | ||
| 693 | const handleToggle = () => { | ||
| 694 | isToggle.value = !isToggle.value | ||
| 695 | } | ||
| 696 | |||
| 697 | /** 处理中英文切换。 */ | ||
| 698 | const handleChOrEn = (flag) => { | ||
| 699 | isCh.value = flag | ||
| 700 | } | ||
| 701 | |||
| 702 | const getTableLineageMap = () => { | ||
| 703 | isFieldLineage.value = false; | ||
| 704 | lineageDataLoading.value = true; | ||
| 705 | getTableLineage({ | ||
| 706 | guid: detailGuid.value | ||
| 707 | }).then((res: any) => { | ||
| 708 | lineageDataLoading.value = false; | ||
| 709 | if (res.code == proxy.$passCode) { | ||
| 710 | const data = res.data || [] | ||
| 711 | lineageData.value = data; | ||
| 712 | } else { | ||
| 713 | ElMessage({ | ||
| 714 | type: 'error', | ||
| 715 | message: res.msg, | ||
| 716 | }) | ||
| 717 | } | ||
| 718 | }); | ||
| 719 | } | ||
| 720 | |||
| 721 | const getAllTableFieldLineageMap = () => { | ||
| 722 | lineageDataLoading.value = true; | ||
| 723 | isFieldLineage.value = true; | ||
| 724 | getTableAllFieldLineage({ databaseName: sheetInfo.value.databaseName, tableName: sheetInfo.value.tableName }).then((res: any) => { | ||
| 725 | if (res.code == proxy.$passCode) { | ||
| 726 | let data1 = res.data || []; | ||
| 727 | lineageData.value = data1; | ||
| 728 | isFieldLineage.value = true; | ||
| 729 | nextTick(() => { | ||
| 730 | if (lineageData.value.length > 0) { | ||
| 731 | lineageGraph.value[0].updateLayout(); | ||
| 732 | lineageDataLoading.value = false; | ||
| 733 | } else { | ||
| 734 | lineageDataLoading.value = false; | ||
| 735 | } | ||
| 736 | }); | ||
| 737 | } else { | ||
| 738 | lineageDataLoading.value = false; | ||
| 739 | ElMessage({ | ||
| 740 | type: 'error', | ||
| 741 | message: res.msg, | ||
| 742 | }) | ||
| 743 | } | ||
| 744 | }) | ||
| 745 | } | ||
| 746 | |||
| 747 | const handleLineageSwitchChange = (val) => { | ||
| 748 | if (val) { | ||
| 749 | isToggle.value = true; | ||
| 750 | getAllTableFieldLineageMap(); | ||
| 751 | } else { | ||
| 752 | getTableLineageMap(); | ||
| 753 | } | ||
| 754 | } | ||
| 755 | </script> | ||
| 756 | |||
| 757 | <template> | ||
| 758 | <div class="container_wrap full flex"> | ||
| 759 | <div class="aside_wrap"> | ||
| 760 | <div class="sheet_panel" v-loading="listLoading"> | ||
| 761 | <div class="panel_header"> | ||
| 762 | <h4>{{ sheetInfo.tableChName ?? '--' }}</h4> | ||
| 763 | <p>{{ sheetInfo.tableName ?? '--' }}</p> | ||
| 764 | </div> | ||
| 765 | <div class="panel_body"> | ||
| 766 | <div class="sheet_list"> | ||
| 767 | <div class="list_item" v-for="sheet in sheetList"> | ||
| 768 | <p class="item_text">{{ sheet.label }}</p> | ||
| 769 | <p class="item_value">{{ sheet.value }}</p> | ||
| 770 | </div> | ||
| 771 | </div> | ||
| 772 | <div class="field_list"> | ||
| 773 | <div class="list_item" v-for="field in fieldList"> | ||
| 774 | <span class="item_text">{{ field.label }}:</span> | ||
| 775 | <span class="item_value">{{ filterValue(field) }}</span> | ||
| 776 | </div> | ||
| 777 | </div> | ||
| 778 | </div> | ||
| 779 | </div> | ||
| 780 | </div> | ||
| 781 | <div class="main_wrap"> | ||
| 782 | <Tabs :tabs-info="tabsInfo" @tab-change="tabChange" /> | ||
| 783 | <div class="tabs_pane_panel"> | ||
| 784 | <div class="tabs_pane" :class="{ full: !pane.title }" v-for="pane in tabsPane"> | ||
| 785 | <template v-if="pane.type == 'table'"> | ||
| 786 | <div v-if="pane.title" class="pane_header"> | ||
| 787 | <span class="header_title" :class="{ active: pane.show }" @click="pane.show = !pane.show" v-preReClick> | ||
| 788 | <el-icon> | ||
| 789 | <CaretRight /> | ||
| 790 | </el-icon> | ||
| 791 | <span>{{ pane.title }}</span> | ||
| 792 | </span> | ||
| 793 | </div> | ||
| 794 | <div class="pane_body" :class="{ active: pane.show ?? true, indexInfo: pane.show ? pane.isIndex : false }" | ||
| 795 | :style="{ paddingTop: pane.title ? '' : '16px' }"> | ||
| 796 | <Table :tableInfo="pane.tableInfo" @tableBtnClick="tableBtnClick" @tablePageChange="tablePageChange" /> | ||
| 797 | </div> | ||
| 798 | </template> | ||
| 799 | <template v-else> | ||
| 800 | <div class="lineage-content" v-loading="lineageDataLoading"> | ||
| 801 | <LineageGraph ref="lineageGraph" v-if="lineageData.length" layout="vertical" :lineageData="lineageData" | ||
| 802 | :is-field-lineage="isFieldLineage" :primary-table="sheetInfo.tableName" :is-detail="true" | ||
| 803 | :primaryDatabase="sheetInfo.databaseName" :isEdit="true" @handleSave="handleSave" | ||
| 804 | @tableContextMenu="handleTableContextMenu" @handleToggle="handleToggle" :isToggle="isToggle" @handleChOrEn=handleChOrEn :isCh="isCh" | ||
| 805 | @handleRefres="handleRefres" @handleEdit="handleEdit" | ||
| 806 | @handleLineageSwitchChange="handleLineageSwitchChange" /> | ||
| 807 | |||
| 808 | </div> | ||
| 809 | </template> | ||
| 810 | </div> | ||
| 811 | </div> | ||
| 812 | </div> | ||
| 813 | <Drawer :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick" @drawerTableBtnClick="drawerTableClick" | ||
| 814 | @drawerTablePageChange="drawerTablePageChange" /> | ||
| 815 | <Dialog ref="reportDialogRef" :dialogInfo="dialogInfo1" @btnClick="dialogBtnClick1" /> | ||
| 816 | </div> | ||
| 817 | </template> | ||
| 818 | |||
| 819 | <style lang="scss" scoped> | ||
| 820 | .container_wrap { | ||
| 821 | .aside_wrap { | ||
| 822 | width: 199px; | ||
| 823 | border-right: 1px solid #d9d9d9; | ||
| 824 | box-shadow: none; | ||
| 825 | |||
| 826 | .sheet_panel { | ||
| 827 | .panel_header { | ||
| 828 | padding: 12px 0 0; | ||
| 829 | word-break: break-all; | ||
| 830 | |||
| 831 | h4, | ||
| 832 | p { | ||
| 833 | margin: 0; | ||
| 834 | padding: 0 12px; | ||
| 835 | } | ||
| 836 | |||
| 837 | h4 { | ||
| 838 | font-size: 16px; | ||
| 839 | color: var(--el-color-regular); | ||
| 840 | line-height: 24px; | ||
| 841 | } | ||
| 842 | |||
| 843 | p { | ||
| 844 | font-size: 14px; | ||
| 845 | color: var(--el-text-color-regular); | ||
| 846 | line-height: 21px; | ||
| 847 | } | ||
| 848 | } | ||
| 849 | |||
| 850 | .panel_body { | ||
| 851 | padding: 0 12px; | ||
| 852 | |||
| 853 | .sheet_list { | ||
| 854 | margin-bottom: 16px; | ||
| 855 | |||
| 856 | .list_item { | ||
| 857 | &.list_item { | ||
| 858 | margin-top: 8px; | ||
| 859 | } | ||
| 860 | |||
| 861 | p { | ||
| 862 | margin: 0; | ||
| 863 | font-size: 12px; | ||
| 864 | color: #999; | ||
| 865 | line-height: 18px; | ||
| 866 | |||
| 867 | &.item_value { | ||
| 868 | color: var(--el-color-regular); | ||
| 869 | } | ||
| 870 | } | ||
| 871 | } | ||
| 872 | } | ||
| 873 | |||
| 874 | .field_list { | ||
| 875 | padding: 8px 0; | ||
| 876 | |||
| 877 | .list_item { | ||
| 878 | font-size: 12px; | ||
| 879 | color: var(--el-text-color-regular); | ||
| 880 | |||
| 881 | &.list_item { | ||
| 882 | margin-top: 4px; | ||
| 883 | } | ||
| 884 | |||
| 885 | span.item_text { | ||
| 886 | color: var(--el-color-regular); | ||
| 887 | // margin-right: 8px; | ||
| 888 | } | ||
| 889 | } | ||
| 890 | } | ||
| 891 | } | ||
| 892 | } | ||
| 893 | } | ||
| 894 | |||
| 895 | .main_wrap { | ||
| 896 | padding: 0; | ||
| 897 | height: auto; | ||
| 898 | flex: 1; | ||
| 899 | |||
| 900 | :deep(.el-tabs) { | ||
| 901 | |||
| 902 | .el-tabs__header { | ||
| 903 | margin-bottom: 0; | ||
| 904 | } | ||
| 905 | |||
| 906 | .el-tabs__item { | ||
| 907 | height: 32px; | ||
| 908 | |||
| 909 | &:nth-child(2) { | ||
| 910 | padding-left: 16px; | ||
| 911 | } | ||
| 912 | |||
| 913 | &:last-child { | ||
| 914 | padding-right: 16px; | ||
| 915 | } | ||
| 916 | |||
| 917 | &::after { | ||
| 918 | content: ''; | ||
| 919 | width: 100%; | ||
| 920 | height: 2px; | ||
| 921 | background-color: transparent; | ||
| 922 | position: absolute; | ||
| 923 | left: 0; | ||
| 924 | bottom: 0; | ||
| 925 | } | ||
| 926 | |||
| 927 | &.is-active { | ||
| 928 | &::after { | ||
| 929 | background-color: var(--el-color-primary); | ||
| 930 | } | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | .el-tabs__active-bar { | ||
| 935 | display: none; | ||
| 936 | } | ||
| 937 | } | ||
| 938 | |||
| 939 | .tabs_pane_panel { | ||
| 940 | height: calc(100% - 32px); | ||
| 941 | background-color: #f7f7f9; | ||
| 942 | overflow: hidden auto; | ||
| 943 | |||
| 944 | .tabs_pane { | ||
| 945 | background-color: #fff; | ||
| 946 | |||
| 947 | .lineage-content { | ||
| 948 | height: 100%; | ||
| 949 | background-color: #f7f7f9; | ||
| 950 | |||
| 951 | .card-noData { | ||
| 952 | height: 100%; | ||
| 953 | width: 100%; | ||
| 954 | background: #fafafa; | ||
| 955 | display: flex; | ||
| 956 | flex-direction: column; | ||
| 957 | justify-content: center; | ||
| 958 | align-items: center; | ||
| 959 | color: #909399; | ||
| 960 | font-size: 14px; | ||
| 961 | } | ||
| 962 | |||
| 963 | } | ||
| 964 | |||
| 965 | &+.tabs_pane { | ||
| 966 | margin-top: 4px; | ||
| 967 | } | ||
| 968 | |||
| 969 | .pane_header { | ||
| 970 | height: 40px; | ||
| 971 | padding: 0 16px; | ||
| 972 | |||
| 973 | .header_title { | ||
| 974 | width: 100px; | ||
| 975 | height: 100%; | ||
| 976 | display: flex; | ||
| 977 | align-items: center; | ||
| 978 | color: var(--el-color-regular); | ||
| 979 | cursor: pointer; | ||
| 980 | |||
| 981 | .el-icon { | ||
| 982 | margin-right: 8px; | ||
| 983 | transition: all .3s; | ||
| 984 | } | ||
| 985 | |||
| 986 | &.active { | ||
| 987 | .el-icon { | ||
| 988 | transform: rotateZ(90deg); | ||
| 989 | } | ||
| 990 | } | ||
| 991 | } | ||
| 992 | } | ||
| 993 | |||
| 994 | .pane_body { | ||
| 995 | padding: 0 16px; | ||
| 996 | height: 0; | ||
| 997 | transition: all .3s; | ||
| 998 | overflow: hidden; | ||
| 999 | |||
| 1000 | &.active { | ||
| 1001 | height: 410px; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | &.indexInfo { | ||
| 1005 | height: 250px; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | .table_panel { | ||
| 1009 | min-height: unset; | ||
| 1010 | padding-bottom: 16px; | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | &.full { | ||
| 1015 | height: 100%; | ||
| 1016 | padding: 0px; | ||
| 1017 | overflow: hidden; | ||
| 1018 | |||
| 1019 | .pane_body { | ||
| 1020 | min-height: 394px; | ||
| 1021 | height: 100%; | ||
| 1022 | |||
| 1023 | .table_panel { | ||
| 1024 | min-height: unset; | ||
| 1025 | padding-bottom: 0; | ||
| 1026 | } | ||
| 1027 | } | ||
| 1028 | } | ||
| 1029 | } | ||
| 1030 | } | ||
| 1031 | } | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | :deep(.el-drawer) { | ||
| 1035 | |||
| 1036 | .el-drawer__body { | ||
| 1037 | padding: 10px 10px 0px 10px; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | .drawer_panel { | ||
| 1041 | height: 100%; | ||
| 1042 | |||
| 1043 | .table_panel_wrap { | ||
| 1044 | height: 100%; | ||
| 1045 | } | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | |||
| 1049 | } | ||
| 1050 | |||
| 1051 | :deep(.el-form .el-form-item) { | ||
| 1052 | width: calc(100%); | ||
| 1053 | } | ||
| 1054 | </style> |
src/views/data_meta/metadataQuery.vue
0 → 100644
| 1 | <router lang="yaml"> | ||
| 2 | name: metadataQuery | ||
| 3 | </router> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="metadataQuery"> | ||
| 6 | import { ref } from 'vue' | ||
| 7 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 8 | import Tree from '@/components/Tree/index.vue' | ||
| 9 | import Table from '@/components/Table/index.vue' | ||
| 10 | import { changeNum } from '@/utils/common' | ||
| 11 | import { useRouter ,useRoute} from "vue-router"; | ||
| 12 | import { | ||
| 13 | getMetaTreeData, | ||
| 14 | getMetaDatabaseCollect, | ||
| 15 | getMetaDataBase, | ||
| 16 | getMetaDataSheet, | ||
| 17 | } from '@/api/modules/dataMetaService'; | ||
| 18 | import { TableColumnWidth } from '@/utils/enum'; | ||
| 19 | |||
| 20 | const { proxy } = getCurrentInstance() as any; | ||
| 21 | |||
| 22 | const router = useRouter(); | ||
| 23 | const route = useRoute() | ||
| 24 | let dataSourceGuid = route.query.dataSourceGuid | ||
| 25 | let datasourceName = route.query.datasourceName | ||
| 26 | const currNodeInfo: any = ref({}) | ||
| 27 | const expandedKey: any = ref([]) | ||
| 28 | const currentNodeKey = ref('') | ||
| 29 | const treeData: any = ref([]) | ||
| 30 | const databaseCount = ref(0) //数据库总数 | ||
| 31 | const treeInfo = ref({ | ||
| 32 | id: "data-pickup-tree", | ||
| 33 | filter: true, | ||
| 34 | queryValue: "", | ||
| 35 | queryPlaceholder: "输入库/表名称搜索", | ||
| 36 | props: { | ||
| 37 | label: "name", | ||
| 38 | value: "guid", | ||
| 39 | }, | ||
| 40 | nodeKey: 'guid', | ||
| 41 | expandedKey: [], | ||
| 42 | currentNodeKey: '', | ||
| 43 | expandOnNodeClick: false, | ||
| 44 | data: [], | ||
| 45 | loading: false | ||
| 46 | }); | ||
| 47 | |||
| 48 | const cardMap = ref({ | ||
| 49 | source: { | ||
| 50 | id: 'database-table', | ||
| 51 | count: [ | ||
| 52 | { | ||
| 53 | label: '总表数', | ||
| 54 | field: 'tableCount', | ||
| 55 | value: 0, | ||
| 56 | type: 'chnum', | ||
| 57 | unit: '个', | ||
| 58 | icon: new URL('@/assets/images/icon2.png', import.meta.url).href | ||
| 59 | }, { | ||
| 60 | label: '存储量(约)', | ||
| 61 | field: 'storageCapacities', | ||
| 62 | value: 0.00, | ||
| 63 | type: 'chnum', | ||
| 64 | fixedNum: 2, | ||
| 65 | unit: 'GB', | ||
| 66 | icon: new URL('@/assets/images/icon3.png', import.meta.url).href | ||
| 67 | }, { | ||
| 68 | label: '总字段数', | ||
| 69 | field: 'columns', | ||
| 70 | value: 0, | ||
| 71 | type: 'chnum', | ||
| 72 | unit: '个', | ||
| 73 | icon: new URL('@/assets/images/icon4.png', import.meta.url).href | ||
| 74 | }, { | ||
| 75 | label: '总行数(约)', | ||
| 76 | field: 'tableRows', | ||
| 77 | value: 0, | ||
| 78 | type: 'chnum', | ||
| 79 | unit: '行', | ||
| 80 | icon: new URL('@/assets/images/icon5.png', import.meta.url).href | ||
| 81 | }, | ||
| 82 | { | ||
| 83 | label: '数据总量', | ||
| 84 | field: 'dataCount', | ||
| 85 | value: 0, | ||
| 86 | type: 'chnum', | ||
| 87 | unit: '万个', | ||
| 88 | icon: new URL('@/assets/images/dataCount.png', import.meta.url).href | ||
| 89 | } | ||
| 90 | ], | ||
| 91 | fields: [ | ||
| 92 | { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", fixed: "left" }, | ||
| 93 | { label: "数据库名称", field: "databaseNameZh", width: 160 }, | ||
| 94 | { label: "数据库名", field: "databaseName", width: 160 }, | ||
| 95 | // { label: "存储索引", field: "operator", width: 120 }, | ||
| 96 | { label: "总表数", field: "tableCount", type: 'chnum', width: 100, align: 'right' }, | ||
| 97 | { label: "存储量(约)(MB)", field: "storageCapacities", type: 'chnum', fixedNum: 2, width: 140, align: 'right' }, | ||
| 98 | { label: "总字段数", field: "columns", type: 'chnum', width: 100, align: 'right' }, | ||
| 99 | { label: "总行数(约)", field: "tableRows", type: 'chnum', width: 100, align: 'right' }, | ||
| 100 | { label: "数据采集时间", field: "collectTime", width: TableColumnWidth.DATETIME }, | ||
| 101 | ] | ||
| 102 | }, | ||
| 103 | database: { | ||
| 104 | id: 'datasheet-table', | ||
| 105 | count: [ | ||
| 106 | { | ||
| 107 | label: '总表数', | ||
| 108 | field: 'tableCount', | ||
| 109 | value: 0, | ||
| 110 | type: 'chnum', | ||
| 111 | unit: '个', | ||
| 112 | icon: new URL('@/assets/images/icon2.png', import.meta.url).href | ||
| 113 | }, { | ||
| 114 | label: '存储量(约)', | ||
| 115 | field: 'storageCapacities', | ||
| 116 | value: 0.00, | ||
| 117 | type: 'chnum', | ||
| 118 | fixedNum: 2, | ||
| 119 | unit: 'GB', | ||
| 120 | icon: new URL('@/assets/images/icon3.png', import.meta.url).href | ||
| 121 | }, { | ||
| 122 | label: '总字段数', | ||
| 123 | field: 'columns', | ||
| 124 | value: 0, | ||
| 125 | type: 'chnum', | ||
| 126 | unit: '个', | ||
| 127 | icon: new URL('@/assets/images/icon4.png', import.meta.url).href | ||
| 128 | }, { | ||
| 129 | label: '总行数(约)', | ||
| 130 | field: 'tableRows', | ||
| 131 | value: 0, | ||
| 132 | type: 'chnum', | ||
| 133 | unit: '行', | ||
| 134 | icon: new URL('@/assets/images/icon5.png', import.meta.url).href | ||
| 135 | }, | ||
| 136 | { | ||
| 137 | label: '数据总量', | ||
| 138 | field: 'dataCount', | ||
| 139 | value: 0, | ||
| 140 | type: 'chnum', | ||
| 141 | unit: '万个', | ||
| 142 | icon: new URL('@/assets/images/dataCount.png', import.meta.url).href | ||
| 143 | } | ||
| 144 | ], | ||
| 145 | fields: [ | ||
| 146 | { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", fixed: "left" }, | ||
| 147 | { label: "表中文名称", field: "tableChName", width: 140 }, | ||
| 148 | { label: "表英文名称", field: "tableName", width: TableColumnWidth.TABLENAME }, | ||
| 149 | { label: "存储量(约)(MB)", field: "storageCapacities", type: 'chnum', fixedNum: 2, width: 140, align: 'right' }, | ||
| 150 | { label: "总字段数", field: "columns", type: 'chnum', width: 120, align: 'right' }, | ||
| 151 | { label: "总行数(约)", field: "tableRows", type: 'chnum', width: 120, align: 'right' }, | ||
| 152 | { label: "索引数", field: "indexes", type: 'chnum', width: 120, align: 'right' }, | ||
| 153 | { label: "是否分区表", field: "isPartition", type: 'filter', width: 120 }, | ||
| 154 | { label: "表创建时间", field: "createTime", width: TableColumnWidth.DATETIME }, | ||
| 155 | { label: "数据采集时间", field: "collectTime", width: TableColumnWidth.DATETIME }, | ||
| 156 | ] | ||
| 157 | }, | ||
| 158 | }) | ||
| 159 | |||
| 160 | const cardLoading = ref(false) | ||
| 161 | const cardList: any = ref([]) | ||
| 162 | const treeActive = ref('source') | ||
| 163 | |||
| 164 | const currentTitle = ref("数据库总览") | ||
| 165 | const currTableData: any = ref<Object>({}); | ||
| 166 | const page = ref({ | ||
| 167 | limit: 50, | ||
| 168 | curr: 1, | ||
| 169 | sizes: [ | ||
| 170 | { label: "10", value: 10 }, | ||
| 171 | { label: "50", value: 50 }, | ||
| 172 | { label: "100", value: 100 }, | ||
| 173 | { label: "150", value: 150 }, | ||
| 174 | { label: "200", value: 200 }, | ||
| 175 | ], | ||
| 176 | }); | ||
| 177 | const selectRowData = ref([]) | ||
| 178 | const tableFields: any = ref([]) | ||
| 179 | const tableData: any = ref([]) | ||
| 180 | const tableInfo: any = ref({ | ||
| 181 | id: 'data-table', | ||
| 182 | fields: cardMap.value[treeActive.value].fields, | ||
| 183 | data: [], | ||
| 184 | page: { | ||
| 185 | type: "normal", | ||
| 186 | rows: 0, | ||
| 187 | ...page.value, | ||
| 188 | }, | ||
| 189 | actionInfo: { | ||
| 190 | label: "操作", | ||
| 191 | type: "btn", | ||
| 192 | width: 60, | ||
| 193 | fixed: 'right', | ||
| 194 | btns: [ | ||
| 195 | { label: "详情", value: "detail" }, | ||
| 196 | ], | ||
| 197 | }, | ||
| 198 | loading: false | ||
| 199 | }) | ||
| 200 | |||
| 201 | const getTreeData = () => { | ||
| 202 | treeInfo.value.loading = true | ||
| 203 | let params = {} | ||
| 204 | getMetaTreeData(params).then((res: any) => { | ||
| 205 | treeInfo.value.loading = false | ||
| 206 | if (res.code == proxy.$passCode) { | ||
| 207 | const data = res.data || []; | ||
| 208 | const treeList = Object.keys(data).length ? [data] : []; | ||
| 209 | treeData.value = treeList; | ||
| 210 | if (treeList.length) { | ||
| 211 | expandedKey.value = expandedKey.value.length == 0 ? [treeList[0].guid] : expandedKey.value | ||
| 212 | currentNodeKey.value = currentNodeKey.value == '' ? treeList[0].guid : currentNodeKey.value | ||
| 213 | currNodeInfo.value = treeList[0]; | ||
| 214 | } | ||
| 215 | treeInfo.value.data = treeData.value | ||
| 216 | nextTick(() => { | ||
| 217 | treeInfo.value.currentNodeKey = currentNodeKey.value | ||
| 218 | treeInfo.value.expandedKey = expandedKey.value | ||
| 219 | }) | ||
| 220 | getFirstPageData() | ||
| 221 | getDatabaseCount({ databaseGuid: currentNodeKey.value === "0" ? "" : currentNodeKey.value }) | ||
| 222 | } else { | ||
| 223 | ElMessage.error(res.msg); | ||
| 224 | } | ||
| 225 | }).catch(() => { | ||
| 226 | treeInfo.value.loading = false | ||
| 227 | }) | ||
| 228 | } | ||
| 229 | |||
| 230 | const getDatabaseCount = (params) => { | ||
| 231 | cardLoading.value = true | ||
| 232 | getMetaDatabaseCollect(params).then((res: any) => { | ||
| 233 | if (res.code == proxy.$passCode) { | ||
| 234 | const data = res.data || {} | ||
| 235 | databaseCount.value = data.databaseCount | ||
| 236 | cardMap.value[treeActive.value].count.map(item => { | ||
| 237 | item.value = data[item.field] | ||
| 238 | }) | ||
| 239 | cardList.value = cardMap.value[treeActive.value].count | ||
| 240 | } else { | ||
| 241 | ElMessage.error(res.msg); | ||
| 242 | } | ||
| 243 | cardLoading.value = false | ||
| 244 | }).catch(() => { | ||
| 245 | cardLoading.value = false | ||
| 246 | }) | ||
| 247 | } | ||
| 248 | |||
| 249 | const getFirstPageData = () => { | ||
| 250 | page.value.curr = 1 | ||
| 251 | toSearch({}) | ||
| 252 | } | ||
| 253 | |||
| 254 | const toSearch = (val: any, clear: boolean = false) => { | ||
| 255 | let params: any = Object.keys(val).length ? { ...val } : {} | ||
| 256 | params.pageIndex = page.value.curr; | ||
| 257 | params.pageSize = page.value.limit; | ||
| 258 | if (treeActive.value == 'database') { | ||
| 259 | params.databaseGuid = currNodeInfo.value.guid | ||
| 260 | } | ||
| 261 | getTableData(params); | ||
| 262 | }; | ||
| 263 | |||
| 264 | const getTableData = (params) => { | ||
| 265 | let method: any = null | ||
| 266 | tableInfo.value.loading = true | ||
| 267 | if (treeActive.value == 'source') { | ||
| 268 | method = getMetaDataBase(params) | ||
| 269 | } else { | ||
| 270 | method = getMetaDataSheet(params) | ||
| 271 | } | ||
| 272 | method.then((res: any) => { | ||
| 273 | if (res.code == proxy.$passCode) { | ||
| 274 | const data = res.data || {} | ||
| 275 | tableData.value = data.records || [] | ||
| 276 | tableFields.value = cardMap.value[treeActive.value].fields | ||
| 277 | tableInfo.value.id = cardMap.value[treeActive.value].id | ||
| 278 | tableInfo.value.fields = tableFields.value | ||
| 279 | tableInfo.value.data = tableData.value | ||
| 280 | tableInfo.value.page.limit = data.pageSize | ||
| 281 | tableInfo.value.page.curr = data.pageIndex | ||
| 282 | tableInfo.value.page.rows = data.totalRows | ||
| 283 | } else { | ||
| 284 | ElMessage({ | ||
| 285 | type: 'error', | ||
| 286 | message: res.msg, | ||
| 287 | }) | ||
| 288 | } | ||
| 289 | tableInfo.value.loading = false | ||
| 290 | }).catch(xhr => { | ||
| 291 | tableInfo.value.loading = false | ||
| 292 | }) | ||
| 293 | }; | ||
| 294 | |||
| 295 | const tableSelectionChange = (val) => { | ||
| 296 | selectRowData.value = val.map((item) => item.guid); | ||
| 297 | }; | ||
| 298 | |||
| 299 | const tablePageChange = (info) => { | ||
| 300 | page.value.curr = Number(info.curr); | ||
| 301 | page.value.limit = Number(info.limit); | ||
| 302 | toSearch({}); | ||
| 303 | }; | ||
| 304 | |||
| 305 | const tableBtnClick = (scope, btn) => { | ||
| 306 | const type = btn.value; | ||
| 307 | const row = scope.row; | ||
| 308 | currTableData.value = row; | ||
| 309 | if (type == "detail") { | ||
| 310 | |||
| 311 | if (treeActive.value == 'source') { | ||
| 312 | let params: any = {} | ||
| 313 | currentTitle.value = row.databaseNameZh | ||
| 314 | params.databaseGuid = row.databaseGuid | ||
| 315 | currNodeInfo.value.guid = row.databaseGuid | ||
| 316 | getDatabaseCount(params) | ||
| 317 | treeActive.value = 'database' | ||
| 318 | getFirstPageData(); | ||
| 319 | currentNodeKey.value = row.databaseGuid; | ||
| 320 | expandedKey.value = [row.databaseGuid] | ||
| 321 | nextTick(() => { | ||
| 322 | treeInfo.value.currentNodeKey = currentNodeKey.value; | ||
| 323 | treeInfo.value.expandedKey = expandedKey.value; | ||
| 324 | }) | ||
| 325 | } else { | ||
| 326 | treeInfo.value.currentNodeKey = row.guid; | ||
| 327 | router.push({ | ||
| 328 | path: '/data-meta/metadata-query/meta-sheet', | ||
| 329 | query: { | ||
| 330 | id: row.guid, | ||
| 331 | name: row.tableChName || row.tableName | ||
| 332 | } | ||
| 333 | }) | ||
| 334 | } | ||
| 335 | } | ||
| 336 | }; | ||
| 337 | |||
| 338 | const filterValue = (row,num=1) => { | ||
| 339 | let val: any = null | ||
| 340 | if (row.type == 'chnum') { | ||
| 341 | val = changeNum(row.value ? row.value/num : 0, row.fixedNum ?? 0) | ||
| 342 | } | ||
| 343 | return val | ||
| 344 | } | ||
| 345 | |||
| 346 | const nodeClick = (data) => { | ||
| 347 | if (data.type == 1 || data.type == 2) { | ||
| 348 | currNodeInfo.value = data; | ||
| 349 | currentTitle.value = data.name | ||
| 350 | treeActive.value = data.type == 1 ? 'source' : 'database' | ||
| 351 | let params: any = {} | ||
| 352 | if (treeActive.value == 'source') { | ||
| 353 | params.dataSourceGuid = data.guid === "0" ? "" : data.guid | ||
| 354 | } else { | ||
| 355 | params.databaseGuid = data.guid | ||
| 356 | } | ||
| 357 | getDatabaseCount(params) | ||
| 358 | getFirstPageData() | ||
| 359 | } else { | ||
| 360 | router.push({ | ||
| 361 | path: '/data-meta/metadata-query/meta-sheet', | ||
| 362 | query: { | ||
| 363 | id: data.guid, | ||
| 364 | name: data.name | ||
| 365 | } | ||
| 366 | }) | ||
| 367 | } | ||
| 368 | } | ||
| 369 | const getQueryTreeData = ()=>{ | ||
| 370 | treeInfo.value.loading = true | ||
| 371 | let params = {} | ||
| 372 | getMetaTreeData(params).then((res: any) => { | ||
| 373 | treeInfo.value.loading = false | ||
| 374 | if (res.code == proxy.$passCode) { | ||
| 375 | const data = res.data || []; | ||
| 376 | const treeList = Object.keys(data).length ? [data] : []; | ||
| 377 | treeData.value = treeList; | ||
| 378 | treeInfo.value.data = treeData.value | ||
| 379 | if (treeList.length) { | ||
| 380 | expandedKey.value = [dataSourceGuid] | ||
| 381 | currentNodeKey.value = dataSourceGuid as string | ||
| 382 | currNodeInfo.value = {type:2,guid:dataSourceGuid,name:datasourceName} | ||
| 383 | } | ||
| 384 | nextTick(() => { | ||
| 385 | treeInfo.value.currentNodeKey = currentNodeKey.value | ||
| 386 | treeInfo.value.expandedKey = expandedKey.value | ||
| 387 | }) | ||
| 388 | nodeClick({type:2,guid:dataSourceGuid,name:datasourceName}) | ||
| 389 | dataSourceGuid = "" | ||
| 390 | } | ||
| 391 | }).catch(()=>{ | ||
| 392 | treeInfo.value.loading = false | ||
| 393 | }) | ||
| 394 | } | ||
| 395 | onBeforeMount(() => { | ||
| 396 | cardList.value = cardMap.value[treeActive.value].count; | ||
| 397 | if(dataSourceGuid){ | ||
| 398 | getQueryTreeData() | ||
| 399 | } else { | ||
| 400 | getTreeData() | ||
| 401 | } | ||
| 402 | |||
| 403 | }) | ||
| 404 | |||
| 405 | </script> | ||
| 406 | |||
| 407 | <template> | ||
| 408 | <div class="container_wrap full flex"> | ||
| 409 | <div class="aside_wrap"> | ||
| 410 | <div class="aside_title" >数据库目录列表</div> | ||
| 411 | <Tree :treeInfo="treeInfo" @nodeClick="nodeClick" /> | ||
| 412 | </div> | ||
| 413 | <div class="pane-trigger-con"></div> | ||
| 414 | <div class="main_wrap"> | ||
| 415 | <div class="table_tool_wrap"> | ||
| 416 | <div class="aside_title" style="padding: 0;"> | ||
| 417 | <template v-if="currentTitle !=='数据库总览'"> | ||
| 418 | <span>数据库:</span>{{ currentTitle }} | ||
| 419 | </template> | ||
| 420 | <template v-else> | ||
| 421 | {{ currentTitle }} <span >({{ databaseCount }})</span> | ||
| 422 | </template> | ||
| 423 | </div> | ||
| 424 | <div class="card_panel"> | ||
| 425 | <div class="img_tags_card" v-for="item in cardList" :key="item.label"> | ||
| 426 | <img :src="item.icon" alt=""> | ||
| 427 | <div class="tags_item"> | ||
| 428 | <p class="tag_text">{{ item.label }}</p> | ||
| 429 | <template v-if="item.field==='dataCount'"> | ||
| 430 | <el-tooltip | ||
| 431 | placement="top-start" | ||
| 432 | trigger="hover" | ||
| 433 | effect="light" | ||
| 434 | :show-after="400" | ||
| 435 | :content="filterValue(item)+'个'" | ||
| 436 | > | ||
| 437 | <p class="tag_num"><span>{{ filterValue(item,10000)}}</span><span class="unit">{{ ' ' + item.unit }}</span></p> | ||
| 438 | </el-tooltip> | ||
| 439 | |||
| 440 | </template> | ||
| 441 | <template v-else> | ||
| 442 | <p class="tag_num"><span>{{ filterValue(item) }}</span><span class="unit">{{ ' ' + item.unit }}</span></p> | ||
| 443 | </template> | ||
| 444 | |||
| 445 | </div> | ||
| 446 | </div> | ||
| 447 | </div> | ||
| 448 | </div> | ||
| 449 | <div class="table_panel_wrap full"> | ||
| 450 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange" | ||
| 451 | @tablePageChange="tablePageChange" /> | ||
| 452 | </div> | ||
| 453 | </div> | ||
| 454 | </div> | ||
| 455 | </template> | ||
| 456 | |||
| 457 | <style lang="scss" scoped> | ||
| 458 | .container_wrap { | ||
| 459 | .aside_wrap { | ||
| 460 | width: 200px; | ||
| 461 | } | ||
| 462 | |||
| 463 | .main_wrap .table_tool_wrap { | ||
| 464 | height: auto; | ||
| 465 | |||
| 466 | .card_panel { | ||
| 467 | height: 48px; | ||
| 468 | // padding: 24px 0; | ||
| 469 | margin-bottom: 20px; | ||
| 470 | padding-top:10px ; | ||
| 471 | display: flex; | ||
| 472 | // justify-content: space-between; | ||
| 473 | align-items: center; | ||
| 474 | |||
| 475 | .img_tags_card { | ||
| 476 | width: 208px; | ||
| 477 | display: flex; | ||
| 478 | align-items: center; | ||
| 479 | |||
| 480 | img { | ||
| 481 | width: 48px; | ||
| 482 | height: 48px; | ||
| 483 | } | ||
| 484 | |||
| 485 | .tags_item { | ||
| 486 | height: 50px; | ||
| 487 | margin-left: 12px; | ||
| 488 | display: flex; | ||
| 489 | flex-direction: column; | ||
| 490 | justify-content: space-between; | ||
| 491 | |||
| 492 | p { | ||
| 493 | font-size: 14px; | ||
| 494 | color: var(--el-text-color-regular); | ||
| 495 | margin: 0; | ||
| 496 | line-height: 21px; | ||
| 497 | |||
| 498 | &.tag_num { | ||
| 499 | color: var(--el-color-regular); | ||
| 500 | font-size: 16px; | ||
| 501 | font-weight: 600; | ||
| 502 | line-height: 24px; | ||
| 503 | |||
| 504 | .unit { | ||
| 505 | font-size: 14px; | ||
| 506 | } | ||
| 507 | } | ||
| 508 | } | ||
| 509 | } | ||
| 510 | } | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | .table_panel_wrap.full { | ||
| 515 | height: calc(100% - 104px); | ||
| 516 | } | ||
| 517 | |||
| 518 | } | ||
| 519 | |||
| 520 | .tree_panel { | ||
| 521 | height: calc(100% - 36px); | ||
| 522 | padding-top: 0; | ||
| 523 | |||
| 524 | :deep(.el-tree) { | ||
| 525 | margin: 0; | ||
| 526 | overflow: hidden auto; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | .aside_title { | ||
| 530 | padding: 0 8px; | ||
| 531 | font-size: 14px; | ||
| 532 | color: var(--el-color-regular); | ||
| 533 | height: 36px; | ||
| 534 | line-height: 36px; | ||
| 535 | font-weight: 600; | ||
| 536 | } | ||
| 537 | </style> |
src/views/data_quality/analysisLog.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: analysisLog | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="analysisLog"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { useRouter, useRoute } from "vue-router"; | ||
| 8 | import Table from "@/components/Table/index.vue"; | ||
| 9 | import { getWordLogList } from "@/api/modules/dataQualityWord"; | ||
| 10 | import { ElMessage } from "element-plus"; | ||
| 11 | |||
| 12 | const { proxy } = getCurrentInstance() as any; | ||
| 13 | |||
| 14 | const router = useRouter(); | ||
| 15 | const route = useRoute(); | ||
| 16 | const guid = route.query.guid; | ||
| 17 | const wordName = route.query.name; | ||
| 18 | |||
| 19 | const page = ref({ | ||
| 20 | limit: 50, | ||
| 21 | curr: 1, | ||
| 22 | sizes: [ | ||
| 23 | { label: "10", value: 10 }, | ||
| 24 | { label: "50", value: 50 }, | ||
| 25 | { label: "100", value: 100 }, | ||
| 26 | { label: "150", value: 150 }, | ||
| 27 | { label: "200", value: 200 }, | ||
| 28 | ], | ||
| 29 | }); | ||
| 30 | const tableInfo = ref({ | ||
| 31 | id: "word-log-table", | ||
| 32 | loading: false, | ||
| 33 | fields: [ | ||
| 34 | { label: "报告名称", field: "analysisReportName", width: 230 }, | ||
| 35 | { | ||
| 36 | label: "方案类型", field: "analysisReportType", width: 100, getName: (scope) => { | ||
| 37 | let planType = scope.row.analysisReportType; | ||
| 38 | return planType == 1 ? '表' : (planType == 2 ? '数据库' : (planType == 4 ? '数据同步' : '分组')); | ||
| 39 | } | ||
| 40 | }, | ||
| 41 | { label: "报告对象", field: "qualityModelName", width: 180, }, | ||
| 42 | { label: "质量评分", field: "qualityScore", width: 100, align: "right" }, | ||
| 43 | { label: "最后执行时间", field: "execTime", width: 180, }, | ||
| 44 | { label: "执行状态", field: "execResult", type: "tag", width: 120, align: "center" }, | ||
| 45 | ], | ||
| 46 | data: [], | ||
| 47 | page: { | ||
| 48 | type: "normal", | ||
| 49 | rows: 0, | ||
| 50 | ...page.value, | ||
| 51 | }, | ||
| 52 | actionInfo: { | ||
| 53 | label: "操作", | ||
| 54 | type: "btn", | ||
| 55 | width: 100, | ||
| 56 | fixed: 'right', | ||
| 57 | btns: (scope) => { | ||
| 58 | return [{ label: "查看报告", value: "reportView", disabled: scope.row['execResult'] != 'Y' }]; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | }); | ||
| 62 | |||
| 63 | const getTableData = () => { | ||
| 64 | tableInfo.value.loading = true; | ||
| 65 | getWordLogList({ pageIndex: page.value.curr, pageSize: page.value.limit, reportGuid: guid }).then((res: any) => { | ||
| 66 | tableInfo.value.loading = false; | ||
| 67 | if (res.code == proxy.$passCode) { | ||
| 68 | const data = res.data || {} | ||
| 69 | tableInfo.value.data = data.records || [] | ||
| 70 | tableInfo.value.page.limit = data.pageSize | ||
| 71 | tableInfo.value.page.curr = data.pageIndex | ||
| 72 | tableInfo.value.page.rows = data.totalRows | ||
| 73 | } else { | ||
| 74 | ElMessage.error(res.msg); | ||
| 75 | } | ||
| 76 | }) | ||
| 77 | }; | ||
| 78 | |||
| 79 | const tableBtnClick = (scope, btn) => { | ||
| 80 | const type = btn.value; | ||
| 81 | const row = scope.row; | ||
| 82 | if (type == 'reportView') { | ||
| 83 | router.push({ | ||
| 84 | name: 'analysisReport', | ||
| 85 | query: { | ||
| 86 | planGuid: row.planGuid, | ||
| 87 | reportExecGuid: row.guid, | ||
| 88 | name: wordName | ||
| 89 | } | ||
| 90 | }); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | |||
| 94 | onBeforeMount(() => { | ||
| 95 | getTableData(); | ||
| 96 | }); | ||
| 97 | |||
| 98 | </script> | ||
| 99 | |||
| 100 | <template> | ||
| 101 | <div class="container_wrap"> | ||
| 102 | <div class="table_panel_wrap"> | ||
| 103 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" /> | ||
| 104 | </div> | ||
| 105 | </div> | ||
| 106 | </template> | ||
| 107 | |||
| 108 | <style lang="scss" scoped> | ||
| 109 | .container_wrap { | ||
| 110 | padding: 0; | ||
| 111 | |||
| 112 | .table_panel_wrap { | ||
| 113 | height: 100%; | ||
| 114 | padding: 16px 16px 0; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/analysisReport.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: analysisReport | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="analysisReport"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { useRouter, useRoute } from "vue-router"; | ||
| 8 | import Table from "@/components/Table/index.vue"; | ||
| 9 | import { | ||
| 10 | getReportDetail, | ||
| 11 | getLargeCategoryScore, | ||
| 12 | getPlanDetail, | ||
| 13 | getTableRuleDetail, | ||
| 14 | htmlToWord | ||
| 15 | } from '@/api/modules/dataQualityWord'; | ||
| 16 | import * as echarts from 'echarts'; | ||
| 17 | import { QuestionFilled } from "@element-plus/icons-vue"; | ||
| 18 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 19 | import { changeNum, tagMethod, tagType } from '@/utils/common'; | ||
| 20 | //报告导出word | ||
| 21 | import html2canvas from 'html2canvas'; | ||
| 22 | |||
| 23 | const router = useRouter(); | ||
| 24 | const route = useRoute(); | ||
| 25 | const reportExecGuid = route.query.reportExecGuid; | ||
| 26 | const planGuid = route.query.planGuid; | ||
| 27 | const wordName = ref(route.query.name); | ||
| 28 | |||
| 29 | const { proxy } = getCurrentInstance() as any; | ||
| 30 | |||
| 31 | const detailInfo: any = ref({}); | ||
| 32 | const fullscreenLoading = ref(false); | ||
| 33 | const loadingText = ref('报告图表生成中,请勿关闭浏览器...'); | ||
| 34 | |||
| 35 | const getReportDetailInfo = () => { | ||
| 36 | let ps: any = []; | ||
| 37 | fullscreenLoading.value = true; | ||
| 38 | loadingText.value = '报告图表生成中,请勿关闭浏览器...'; | ||
| 39 | ps.push(getReportDetail({ reportExecGuid: reportExecGuid, planGuid: planGuid }).then((res: any) => { | ||
| 40 | if (res.code == proxy.$passCode) { | ||
| 41 | let data = res.data || {}; | ||
| 42 | detailInfo.value = data; | ||
| 43 | barChartData.value = data.qualityScoreLine?.slice(0, 12) || []; | ||
| 44 | planDetailTableInfo.value.data = [{ | ||
| 45 | guid: '1', | ||
| 46 | planName: data.planName, | ||
| 47 | planType: data.analysisReportType == 1 ? '表' : (data.analysisReportType == 2 ? '数据库' : (data.analysisReportType == 4 ? '数据同步' : '分组')), | ||
| 48 | execDuration: data.planExecDuration != null ? changeNum(data.planExecDuration ?? 0) : '--', | ||
| 49 | execTime: data.execTime, | ||
| 50 | qualityTableNum: data.qualityTableNum != null ? changeNum(data.qualityTableNum ?? 0) : '--', | ||
| 51 | ruleCount: data.ruleCount != null ? changeNum(data.ruleCount ?? 0) : '--', | ||
| 52 | totalNum: data.totalNum != null ? changeNum(data.totalNum ?? 0) : '--', | ||
| 53 | qualifiedNum: data.qualifiedNum != null ? changeNum(data.qualifiedNum ?? 0) : '--', | ||
| 54 | unqualifiedNum: data.unqualifiedNum != null ? changeNum(data.unqualifiedNum ?? 0) : '--', | ||
| 55 | qualityScore: data.qualityScore ?? '--', | ||
| 56 | qualifiedRate: data.qualifiedRate != null ? (changeNum((data.qualifiedRate ?? 0) * 100, 2, true) + '%') : '--' | ||
| 57 | }] | ||
| 58 | } else { | ||
| 59 | ElMessage.error(res.msg); | ||
| 60 | } | ||
| 61 | })) | ||
| 62 | ps.push(getLargeCategoryScore(reportExecGuid).then((res: any) => { | ||
| 63 | if (res.code == proxy.$passCode) { | ||
| 64 | let data = res.data || {}; | ||
| 65 | qualityRuleDetailTableInfo.value.data = data.largeCategoryScoreList || []; | ||
| 66 | if (!qualityRuleDetailTableInfo.value.data.length) { | ||
| 67 | qualityRuleDetailTableInfo.value.footerHtml = ''; | ||
| 68 | } else { | ||
| 69 | qualityRuleDetailTableInfo.value.footerHtml = `<span>质量得分</span><span>∑规则得分*权重</span>`; | ||
| 70 | } | ||
| 71 | radarChartData.value = data.largeCategoryScore || {}; | ||
| 72 | } else { | ||
| 73 | ElMessage.error(res.msg); | ||
| 74 | } | ||
| 75 | })) | ||
| 76 | /** 表明细,单表时没有。 */ | ||
| 77 | ps.push(getPlanDetail({ reportExecGuid: reportExecGuid, planGuid: planGuid }).then((res: any) => { | ||
| 78 | if (res.code == proxy.$passCode) { | ||
| 79 | let data = res.data || []; | ||
| 80 | modelDetailTableInfo.value.data = data; | ||
| 81 | } else { | ||
| 82 | ElMessage.error(res.msg); | ||
| 83 | } | ||
| 84 | })) | ||
| 85 | ps.push(getTableRuleDetail(reportExecGuid).then((res: any) => { | ||
| 86 | if (res.code == proxy.$passCode) { | ||
| 87 | let data = res.data || []; | ||
| 88 | modelRuleDetailTableInfo.value.data = data; | ||
| 89 | } else { | ||
| 90 | ElMessage.error(res.msg); | ||
| 91 | } | ||
| 92 | })) | ||
| 93 | Promise.all(ps).then(() => { | ||
| 94 | fullscreenLoading.value = false; | ||
| 95 | }); | ||
| 96 | } | ||
| 97 | |||
| 98 | /** 方案明细表。 */ | ||
| 99 | const planDetailTableInfo: any = ref({ | ||
| 100 | id: "plan-detail-table", | ||
| 101 | loading: false, | ||
| 102 | height: 'auto', | ||
| 103 | minPanelHeight: '60px', | ||
| 104 | minHeight: '60px', | ||
| 105 | fields: [ | ||
| 106 | { label: "方案名称", field: "planName", width: 150 }, | ||
| 107 | { label: "方案类型", field: "planType", width: 100 }, | ||
| 108 | { label: "评估时间", field: "execTime", width: 180, }, | ||
| 109 | { | ||
| 110 | label: "耗时(秒)", field: "execDuration", width: 100, align: 'right' | ||
| 111 | }, | ||
| 112 | { label: "评估表数", field: "qualityTableNum", width: 100, align: 'right' }, | ||
| 113 | { label: "规则数", field: "ruleCount", width: 100, align: 'right' }, | ||
| 114 | { label: "评估总数", field: "totalNum", width: 100, align: 'right' }, | ||
| 115 | { label: "合格条数", field: "qualifiedNum", width: 100, align: 'right' }, | ||
| 116 | { label: "不合格条数", field: "unqualifiedNum", width: 100, align: 'right' }, | ||
| 117 | { label: "合格率", field: "qualifiedRate", width: 100, align: 'right' }, | ||
| 118 | { label: "质量评分", field: "qualityScore", width: 100, align: "right" } | ||
| 119 | ], | ||
| 120 | data: [], | ||
| 121 | showPage: false, | ||
| 122 | actionInfo: { | ||
| 123 | show: false | ||
| 124 | } | ||
| 125 | }); | ||
| 126 | |||
| 127 | /** 数据质量一级指标得分明细 */ | ||
| 128 | const qualityRuleDetailTableInfo = ref({ | ||
| 129 | id: "quality-rule-detail-table", | ||
| 130 | loading: false, | ||
| 131 | minHeight: '60px', | ||
| 132 | footerClass: 'last-row', | ||
| 133 | footerHtml: `<span>质量得分</span><span></span>`, | ||
| 134 | fields: [ | ||
| 135 | { label: "规则大类", field: "largeCategoryName", width: 100 }, | ||
| 136 | { | ||
| 137 | label: "规则数", field: "ruleCount", width: 100, align: 'right', getName: (scope) => { | ||
| 138 | return scope.row.ruleCount != null ? changeNum(scope.row.ruleCount ?? 0) : '--'; | ||
| 139 | } | ||
| 140 | }, | ||
| 141 | { | ||
| 142 | label: "规则得分", field: "largeCategoryScore", width: 100, align: "right", getName: (scope) => { | ||
| 143 | return scope.row.largeCategoryScore != null ? changeNum(scope.row.largeCategoryScore ?? 0, 2, true) : '--'; | ||
| 144 | } | ||
| 145 | }, | ||
| 146 | { | ||
| 147 | label: "权重", field: "ruleLargeWeight", width: 100, align: "right", getName: (scope) => { | ||
| 148 | return scope.row.ruleLargeWeight != null ? scope.row.ruleLargeWeight.toFixed(2) : '--'; | ||
| 149 | } | ||
| 150 | }, | ||
| 151 | { | ||
| 152 | label: "质量评分", field: "qualityScore", width: 100, align: "right", getName: (scope) => { | ||
| 153 | return scope.row.qualityScore != null ? changeNum(scope.row.qualityScore ?? 0, 2, true) : '--'; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | ], | ||
| 157 | data: [], | ||
| 158 | showPage: false, | ||
| 159 | actionInfo: { | ||
| 160 | show: false | ||
| 161 | } | ||
| 162 | }); | ||
| 163 | |||
| 164 | /** 表明细。 */ | ||
| 165 | const modelDetailTableInfo = ref({ | ||
| 166 | id: "model-detail-table", | ||
| 167 | loading: false, | ||
| 168 | height: 'auto', | ||
| 169 | minPanelHeight: '60px', | ||
| 170 | minHeight: '60px', | ||
| 171 | fields: [ | ||
| 172 | { label: "序号", type: "index", width: 56, align: "center" }, | ||
| 173 | { label: "表名", field: "qualityModelName", width: 140 }, | ||
| 174 | { label: "执行结果", field: "execResult", type: "tag", width: 100, align: "center" }, | ||
| 175 | { label: "评估时间", field: "execTime", width: 180, }, | ||
| 176 | { | ||
| 177 | label: "耗时(秒)", field: "execDuration", width: 100, align: "right", getName: (scope) => { | ||
| 178 | return scope.row.execDuration != null ? changeNum(scope.row.execDuration ?? 0) : '--'; | ||
| 179 | } | ||
| 180 | }, | ||
| 181 | { | ||
| 182 | label: "规则数", field: "ruleNum", width: 100, align: "right", getName: (scope) => { | ||
| 183 | return scope.row.ruleNum != null ? changeNum(scope.row.ruleNum ?? 0) : '--'; | ||
| 184 | } | ||
| 185 | }, | ||
| 186 | { | ||
| 187 | label: "评估总数", field: "totalNum", width: 100, align: "right", getName: (scope) => { | ||
| 188 | return scope.row.totalNum != null ? changeNum(scope.row.totalNum ?? 0) : '--'; | ||
| 189 | } | ||
| 190 | }, | ||
| 191 | { | ||
| 192 | label: "合格条数", field: "qualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 193 | return scope.row.qualifiedNum != null ? changeNum(scope.row.qualifiedNum ?? 0) : '--'; | ||
| 194 | } | ||
| 195 | }, | ||
| 196 | { | ||
| 197 | label: "不合格条数", field: "unqualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 198 | return scope.row.unqualifiedNum != null ? changeNum(scope.row.unqualifiedNum ?? 0) : '--'; | ||
| 199 | } | ||
| 200 | }, | ||
| 201 | { | ||
| 202 | label: "合格率", field: "qualifiedRate", width: 100, align: "right", getName: (scope) => { | ||
| 203 | return scope.row.qualifiedRate != null ? ((scope.row.qualifiedRate ?? 0).toFixed(2) + '%') : '--'; | ||
| 204 | } | ||
| 205 | }, | ||
| 206 | { label: "评估范围", field: "dataRange", width: 180 }, | ||
| 207 | ], | ||
| 208 | data: [{ | ||
| 209 | guid: '1', | ||
| 210 | executeState: 'N' | ||
| 211 | }], | ||
| 212 | showPage: false, | ||
| 213 | actionInfo: { | ||
| 214 | show: false | ||
| 215 | } | ||
| 216 | }); | ||
| 217 | |||
| 218 | /** 规则明细。 */ | ||
| 219 | const modelRuleDetailTableInfo = ref({ | ||
| 220 | id: "model-rule-detail-table", | ||
| 221 | loading: false, | ||
| 222 | height: 'auto', | ||
| 223 | minPanelHeight: '60px', | ||
| 224 | minHeight: '60px', | ||
| 225 | fields: [ | ||
| 226 | { label: "序号", type: "index", width: 56, align: "center" }, | ||
| 227 | { label: "表名", field: "qualityModelName", width: 140 }, | ||
| 228 | { label: "规则类型", field: "ruleName", width: 140 }, | ||
| 229 | { label: "规则名称", field: "ruleConfName", width: 140 }, | ||
| 230 | { label: "规则大类", field: "largeCategory", width: 100 }, | ||
| 231 | { label: "规则小类", field: "smallCategory", width: 140 }, | ||
| 232 | { label: "规则字段", field: "ruleField", width: 140 }, | ||
| 233 | { label: "执行结果", field: "execResult", type: "tag", width: 100, align: "center" }, | ||
| 234 | { | ||
| 235 | label: "评估总数", field: "totalNum", width: 100, align: "right", getName: (scope) => { | ||
| 236 | return scope.row.totalNum != null ? changeNum(scope.row.totalNum ?? 0) : '--'; | ||
| 237 | } | ||
| 238 | }, | ||
| 239 | { | ||
| 240 | label: "合格条数", field: "qualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 241 | return scope.row.qualifiedNum != null ? changeNum(scope.row.qualifiedNum ?? 0) : '--'; | ||
| 242 | } | ||
| 243 | }, | ||
| 244 | { | ||
| 245 | label: "不合格条数", field: "unqualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 246 | return scope.row.unqualifiedNum != null ? changeNum(scope.row.unqualifiedNum ?? 0) : '--'; | ||
| 247 | } | ||
| 248 | }, | ||
| 249 | { | ||
| 250 | label: "合格率", field: "qualifiedRate", width: 100, align: "right", getName: (scope) => { | ||
| 251 | return scope.row.qualifiedRate != null ? ((scope.row.qualifiedRate ?? 0).toFixed(2) + '%') : '--'; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | ], | ||
| 255 | data: [], | ||
| 256 | showPage: false, | ||
| 257 | actionInfo: { | ||
| 258 | show: false | ||
| 259 | } | ||
| 260 | }); | ||
| 261 | |||
| 262 | /** 柱形图数据数组 */ | ||
| 263 | const barChartData: any = ref([]); | ||
| 264 | /** 雷达图数据数组 */ | ||
| 265 | const radarChartData: any = ref([]); | ||
| 266 | |||
| 267 | let barChart: any = null; | ||
| 268 | let radarChart: any = null; | ||
| 269 | |||
| 270 | let barChartWord: any = null; | ||
| 271 | let radarChartWord: any = null; | ||
| 272 | |||
| 273 | /** 设置柱形图option,默认只 展示12次的 */ | ||
| 274 | const setBarChartOption = (barChart, isWord = false) => { | ||
| 275 | return new Promise((resolve, reject) => { | ||
| 276 | if (!barChartData.value.length) { | ||
| 277 | let option1 = { | ||
| 278 | title: [ | ||
| 279 | { | ||
| 280 | text: "", | ||
| 281 | left: "30px", | ||
| 282 | top: "30px", | ||
| 283 | }, | ||
| 284 | { | ||
| 285 | text: "暂无数据", | ||
| 286 | left: "center", | ||
| 287 | top: "center", | ||
| 288 | textStyle: { | ||
| 289 | fontStyle: "normal", | ||
| 290 | fontWeight: "400", | ||
| 291 | fontSize: 18, | ||
| 292 | }, | ||
| 293 | }, | ||
| 294 | ], | ||
| 295 | }; | ||
| 296 | barChart.setOption(option1, true); | ||
| 297 | window.addEventListener("resize", () => { | ||
| 298 | barChart.resize(); | ||
| 299 | }); | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | let itemXAxisData: any = []; | ||
| 303 | let itemYAxisData1: any = []; | ||
| 304 | let itemYAxisData2: any = []; | ||
| 305 | barChartData.value.forEach(d => { | ||
| 306 | itemXAxisData.push(d['execTime']); | ||
| 307 | itemYAxisData1.push(d['qualityScore']); | ||
| 308 | itemYAxisData2.push((d['qualifiedRate'] ?? 0) * 100); | ||
| 309 | }) | ||
| 310 | let option = { | ||
| 311 | textStyle: { | ||
| 312 | fontFamily: 'SimSun' | ||
| 313 | }, | ||
| 314 | color: ['#5B8FF9', '#FF4E00', '#867EEC', '#FDBC3E', '#F48A64', '#276FF5', '#46D0B5'], | ||
| 315 | title: { | ||
| 316 | show: false, | ||
| 317 | text: '评分趋势', | ||
| 318 | left: '30px', | ||
| 319 | top: '30px', | ||
| 320 | }, | ||
| 321 | tooltip: { | ||
| 322 | trigger: 'axis', | ||
| 323 | axisPointer: { | ||
| 324 | type: 'cross', | ||
| 325 | crossStyle: { | ||
| 326 | color: '#999' | ||
| 327 | } | ||
| 328 | }, | ||
| 329 | textStyle: { | ||
| 330 | align: 'left' | ||
| 331 | }, | ||
| 332 | }, | ||
| 333 | legend: { | ||
| 334 | right: 0, | ||
| 335 | textStyle: { | ||
| 336 | fontSize: 14 | ||
| 337 | }, | ||
| 338 | data: ['评分', '合格率'] | ||
| 339 | }, | ||
| 340 | grid: { | ||
| 341 | left: 30, | ||
| 342 | right: 60, | ||
| 343 | bottom: 5, | ||
| 344 | containLabel: true | ||
| 345 | }, | ||
| 346 | xAxis: { | ||
| 347 | type: 'category', | ||
| 348 | boundaryGap: false, | ||
| 349 | nameTextStyle: { | ||
| 350 | color: '#000000' | ||
| 351 | }, | ||
| 352 | axisLabel: { | ||
| 353 | interval: isWord ? 'auto' : 0, | ||
| 354 | textStyle: { | ||
| 355 | color: '#000000' | ||
| 356 | }, | ||
| 357 | formatter: function (value) { | ||
| 358 | if (!value) { | ||
| 359 | return value; | ||
| 360 | } | ||
| 361 | let v = value.split(' '); | ||
| 362 | return v.join('\n'); | ||
| 363 | } | ||
| 364 | }, | ||
| 365 | axisTick: { | ||
| 366 | show: false, | ||
| 367 | }, | ||
| 368 | axisLine: { | ||
| 369 | lineStyle: { | ||
| 370 | color: '#d9d9d9' | ||
| 371 | } | ||
| 372 | }, | ||
| 373 | data: itemXAxisData | ||
| 374 | }, | ||
| 375 | yAxis: { | ||
| 376 | type: 'value', | ||
| 377 | name: '', | ||
| 378 | min: 0, | ||
| 379 | max: 100, | ||
| 380 | nameTextStyle: { | ||
| 381 | color: '#000000' | ||
| 382 | }, | ||
| 383 | axisLabel: { | ||
| 384 | textStyle: { | ||
| 385 | color: '#000000' | ||
| 386 | } | ||
| 387 | }, | ||
| 388 | axisLine: { | ||
| 389 | //y轴 | ||
| 390 | show: false | ||
| 391 | } | ||
| 392 | }, | ||
| 393 | series: [{ | ||
| 394 | name: '评分', | ||
| 395 | type: 'line', | ||
| 396 | data: itemYAxisData1, | ||
| 397 | label: { | ||
| 398 | show: false, | ||
| 399 | }, | ||
| 400 | yAxisIndex: 0 | ||
| 401 | }, { | ||
| 402 | name: '合格率', | ||
| 403 | type: 'line', | ||
| 404 | data: itemYAxisData2, | ||
| 405 | label: { | ||
| 406 | show: false, | ||
| 407 | }, | ||
| 408 | tooltip: { | ||
| 409 | valueFormatter: function (value) { | ||
| 410 | return changeNum(value, 2, true) + '%'; | ||
| 411 | } | ||
| 412 | }, | ||
| 413 | yAxisIndex: 0 | ||
| 414 | }] | ||
| 415 | }; | ||
| 416 | option && barChart.setOption(option, true); | ||
| 417 | barChart.on('finished', () => { | ||
| 418 | resolve(true); | ||
| 419 | }); | ||
| 420 | window.addEventListener('resize', () => { | ||
| 421 | barChart.resize(); | ||
| 422 | }); | ||
| 423 | }); | ||
| 424 | } | ||
| 425 | |||
| 426 | /** 设置雷达图option. */ | ||
| 427 | const setRadarChartOption = (radarChart) => { | ||
| 428 | return new Promise((resolve, reject) => { | ||
| 429 | let indicator = [{ name: '规范性', max: 100, min: 0, color: '#000' }, | ||
| 430 | { name: '完整性', max: 100, min: 0, color: '#000' }, | ||
| 431 | { name: '一致性', max: 100, min: 0, color: '#000' }, | ||
| 432 | { name: '准确性', max: 100, min: 0, color: '#000' }, | ||
| 433 | { name: '时效性', max: 100, min: 0, color: '#000' }, | ||
| 434 | { name: '可访问性', max: 100, min: 0, color: '#000' }] | ||
| 435 | let data: any = []; | ||
| 436 | for (const key in radarChartData.value) { | ||
| 437 | let index = indicator.findIndex(i => i.name == key); | ||
| 438 | index > -1 && (data[index] = changeNum(radarChartData.value[key] ?? 0, 2)); | ||
| 439 | } | ||
| 440 | let option = { | ||
| 441 | textStyle: { | ||
| 442 | fontFamily: 'SimSun' | ||
| 443 | }, | ||
| 444 | color: ['#5B8FF9', '#6DD18E', '#867EEC', '#FDBC3E', '#F48A64', '#276FF5', '#46D0B5'], | ||
| 445 | title: { | ||
| 446 | show: false, | ||
| 447 | text: '数据质量一级指标得分', | ||
| 448 | left: '30px', | ||
| 449 | top: '30px', | ||
| 450 | }, | ||
| 451 | tooltip: { | ||
| 452 | axisPointer: { | ||
| 453 | type: 'cross', | ||
| 454 | crossStyle: { | ||
| 455 | color: '#999' | ||
| 456 | } | ||
| 457 | }, | ||
| 458 | textStyle: { | ||
| 459 | align: 'left' | ||
| 460 | }, | ||
| 461 | }, | ||
| 462 | legend: { | ||
| 463 | show: false | ||
| 464 | }, | ||
| 465 | grid: { | ||
| 466 | left: 0, | ||
| 467 | bottom: 0, | ||
| 468 | containLabel: true | ||
| 469 | }, | ||
| 470 | radar: { | ||
| 471 | indicator: indicator, | ||
| 472 | axisLabel: { | ||
| 473 | show: false | ||
| 474 | }, | ||
| 475 | axisLine: { | ||
| 476 | lineStyle: { | ||
| 477 | color: '#d9d9d9' | ||
| 478 | } | ||
| 479 | }, | ||
| 480 | axisTick: { | ||
| 481 | lineStyle: { | ||
| 482 | color: '#d9d9d9' | ||
| 483 | } | ||
| 484 | }, | ||
| 485 | splitLine: { | ||
| 486 | lineStyle: { | ||
| 487 | color: '#d9d9d9' | ||
| 488 | } | ||
| 489 | } | ||
| 490 | }, | ||
| 491 | series: [ | ||
| 492 | { | ||
| 493 | type: 'radar', | ||
| 494 | data: [{ | ||
| 495 | name: '数据质量一级指标得分', | ||
| 496 | value: data | ||
| 497 | }], | ||
| 498 | label: { | ||
| 499 | show: true, | ||
| 500 | position: 'bottom', | ||
| 501 | distance: 1, | ||
| 502 | color: '#000' | ||
| 503 | }, | ||
| 504 | areaStyle: { | ||
| 505 | color: '#e8eefc' | ||
| 506 | } | ||
| 507 | } | ||
| 508 | ] | ||
| 509 | }; | ||
| 510 | option && radarChart.setOption(option, true); | ||
| 511 | radarChart.on('finished', () => { | ||
| 512 | resolve(true); | ||
| 513 | }); | ||
| 514 | window.addEventListener('resize', () => { | ||
| 515 | radarChart.resize(); | ||
| 516 | }); | ||
| 517 | }); | ||
| 518 | } | ||
| 519 | |||
| 520 | watch(() => barChartData.value, (val) => { | ||
| 521 | setBarChartOption(barChart); | ||
| 522 | barChartWord && setBarChartOption(barChartWord, true); | ||
| 523 | }) | ||
| 524 | |||
| 525 | watch(() => radarChartData.value, (val) => { | ||
| 526 | setRadarChartOption(radarChart); | ||
| 527 | radarChartWord && setRadarChartOption(radarChartWord); | ||
| 528 | }) | ||
| 529 | |||
| 530 | onBeforeMount(() => { | ||
| 531 | getReportDetailInfo(); | ||
| 532 | }) | ||
| 533 | |||
| 534 | onMounted(() => { | ||
| 535 | barChart = echarts.init(document.getElementById('bar')); | ||
| 536 | radarChart = echarts.init(document.getElementById('radar')); | ||
| 537 | setBarChartOption(barChart); | ||
| 538 | setRadarChartOption(radarChart); | ||
| 539 | }) | ||
| 540 | |||
| 541 | const domClone: any = ref(null); | ||
| 542 | |||
| 543 | const convertChartsToBase64 = (contentDocument) => { | ||
| 544 | // 找到所有的图表 (echart) | ||
| 545 | let canvases = contentDocument.querySelectorAll('#bar-word'); | ||
| 546 | // 遍历图表,转换为 base64 静态图片 | ||
| 547 | canvases.forEach((canvas, i) => { | ||
| 548 | let url = barChartWord.getDataURL(); | ||
| 549 | let img = document.createElement('img'); | ||
| 550 | if (url) { | ||
| 551 | // img.src = url.split(',')[1]; | ||
| 552 | img.src = url; | ||
| 553 | img.width = 620; | ||
| 554 | img.height = 400; | ||
| 555 | img.crossOrigin = 'Anonymous'; | ||
| 556 | } | ||
| 557 | canvas.parentNode.replaceChild(img, canvas); | ||
| 558 | }); | ||
| 559 | let canvases1 = contentDocument.querySelectorAll('#radar-word'); | ||
| 560 | canvases1.forEach((canvas, i) => { | ||
| 561 | let url = radarChartWord.getDataURL(); | ||
| 562 | let img = document.createElement('img'); | ||
| 563 | if (url) { | ||
| 564 | // img.src = url.split(',')[1]; | ||
| 565 | img.src = url; | ||
| 566 | img.width = 620; | ||
| 567 | img.height = 400; | ||
| 568 | img.crossOrigin = 'Anonymous'; | ||
| 569 | } | ||
| 570 | canvas.parentNode.replaceChild(img, canvas); | ||
| 571 | }); | ||
| 572 | } | ||
| 573 | |||
| 574 | const convertHtml2Img = (dom, domClone) => { | ||
| 575 | const element = <HTMLElement>dom.querySelector('.kpi-content') | ||
| 576 | if (!element) { | ||
| 577 | return Promise.resolve(); | ||
| 578 | } | ||
| 579 | return html2canvas(element, { | ||
| 580 | allowTaint: true, | ||
| 581 | useCORS: true, | ||
| 582 | scale: 1, | ||
| 583 | }).then((canvas: any) => { | ||
| 584 | document.documentElement.scrollTop = 0; | ||
| 585 | document.body.scrollTop = 0; | ||
| 586 | element.parentNode && ((<HTMLElement>element.parentNode).scrollTop = 0); | ||
| 587 | let url = canvas.toDataURL('image/jpeg'); | ||
| 588 | let img = document.createElement('img'); | ||
| 589 | if (url) { | ||
| 590 | // img.src = url.split(',')[1]; | ||
| 591 | img.src = url; | ||
| 592 | img.width = 620; | ||
| 593 | img.height = 80; | ||
| 594 | img.crossOrigin = 'Anonymous'; | ||
| 595 | } | ||
| 596 | const copyElement = <HTMLElement>domClone.querySelector('.kpi-content') | ||
| 597 | copyElement.parentNode?.replaceChild(img, copyElement); | ||
| 598 | }) | ||
| 599 | } | ||
| 600 | |||
| 601 | const report = ref(); | ||
| 602 | |||
| 603 | const isWordStyle = ref(false); | ||
| 604 | |||
| 605 | const isTransfered = ref(false); | ||
| 606 | |||
| 607 | const downloadBtnDisable = ref(false); | ||
| 608 | |||
| 609 | const transfer = () => { | ||
| 610 | isWordStyle.value = true; | ||
| 611 | nextTick(() => { | ||
| 612 | if (!isTransfered.value) { | ||
| 613 | barChartWord = echarts.init(document.getElementById('bar-word')); | ||
| 614 | downloadBtnDisable.value = true; | ||
| 615 | let ps1 = setBarChartOption(barChartWord, true); | ||
| 616 | radarChartWord = echarts.init(document.getElementById('radar-word')); | ||
| 617 | let ps2 = setRadarChartOption(radarChartWord); | ||
| 618 | Promise.all([ps1, ps2]).then(res => { | ||
| 619 | downloadBtnDisable.value = false; | ||
| 620 | isTransfered.value = true; | ||
| 621 | }) | ||
| 622 | } | ||
| 623 | }); | ||
| 624 | }; | ||
| 625 | |||
| 626 | const getHTML = (reportResultContent) => { | ||
| 627 | let html = reportResultContent; | ||
| 628 | html = html.replace(/"/g, "'"); | ||
| 629 | return html; | ||
| 630 | }; | ||
| 631 | |||
| 632 | const download = () => { | ||
| 633 | let dom = domClone.value || (domClone.value = document.createElement('div')); | ||
| 634 | dom.innerHTML = report.value.innerHTML; | ||
| 635 | convertChartsToBase64(dom); | ||
| 636 | fullscreenLoading.value = true; | ||
| 637 | loadingText.value = '报告正在下载中,请勿关闭浏览器...'; | ||
| 638 | convertHtml2Img(report.value, dom).then(() => { | ||
| 639 | htmlToWord({ html: getHTML(dom.innerHTML) }).then((res: any) => { | ||
| 640 | fullscreenLoading.value = false; | ||
| 641 | // let blob = new Blob([res.data], { type: 'application/msword' }); | ||
| 642 | let objectUrl = URL.createObjectURL(res); | ||
| 643 | const link = document.createElement('a') | ||
| 644 | link.download = wordName.value + '.docx'; | ||
| 645 | link.href = objectUrl | ||
| 646 | link.click() | ||
| 647 | }) | ||
| 648 | // // 下载word 需要接口。 | ||
| 649 | // const converted = htmlDocx.asBlob(` | ||
| 650 | // <html xmlns:o=\'urn:schemas-microsoft-com:office:office\' xmlns:w=\'urn:schemas-microsoft-com:office:word\' xmlns=\'http://www.w3.org/TR/REC-html40\'><head><style> | ||
| 651 | // ${document.head.outerHTML} | ||
| 652 | // </head> | ||
| 653 | // <body> | ||
| 654 | // ${dom.outerHTML} | ||
| 655 | // </body> | ||
| 656 | // </html>`) | ||
| 657 | // converted.then((res: any) => { | ||
| 658 | // // 导出无样式。 | ||
| 659 | // console.log(res); | ||
| 660 | // let objectUrl = URL.createObjectURL(res); | ||
| 661 | // const link = document.createElement('a') | ||
| 662 | // link.download = detailInfo.value.analysisReportName + '.docx'; | ||
| 663 | // link.href = objectUrl | ||
| 664 | // link.click() | ||
| 665 | // }) | ||
| 666 | }) | ||
| 667 | } | ||
| 668 | |||
| 669 | </script> | ||
| 670 | |||
| 671 | <template> | ||
| 672 | <div class="container_wrap" v-loading="fullscreenLoading" :element-loading-text="loadingText"> | ||
| 673 | <div v-show="!isWordStyle" class="header-title"> | ||
| 674 | <div> | ||
| 675 | <span>{{ `【${(detailInfo.analysisReportType == 1 ? detailInfo.qualityModelName : | ||
| 676 | (detailInfo.analysisReportType | ||
| 677 | == 3 ? detailInfo.qualityModelGroupName : detailInfo.dataSourceName)) ?? '--'}】` + '数据质量评估报告' }}</span> | ||
| 678 | <span class="time-detail">{{ '生成时间:' + detailInfo.execTime }}</span> | ||
| 679 | </div> | ||
| 680 | <el-button type="primary" @click="transfer">生成Word报告</el-button> | ||
| 681 | <!-- <el-button @click="download">下载 Word</el-button> --> | ||
| 682 | </div> | ||
| 683 | <div v-show="!isWordStyle" class="header-detail"> | ||
| 684 | <template v-if="detailInfo.analysisReportType == 1"> | ||
| 685 | <span>{{ '表名称:' + (detailInfo.qualityModelName ?? '--') }}</span> | ||
| 686 | <span style="margin-left: 40px;">{{ '所属主题:' + (detailInfo.subjectDomainName ?? '--') }}</span> | ||
| 687 | <span style="margin-left: 40px;">{{ '所属数据源:' + (detailInfo.dataSourceName ?? '--') }}</span> | ||
| 688 | </template> | ||
| 689 | <span v-else-if="detailInfo.analysisReportType == 3">{{ '分组名称:' + (detailInfo.qualityModelGroupName ?? '--') | ||
| 690 | }}</span> | ||
| 691 | <span v-else>{{ '数据库名称:' + (detailInfo.dataSourceName ?? '--') }}</span> | ||
| 692 | </div> | ||
| 693 | <div v-show="!isWordStyle" class="content-main"> | ||
| 694 | <div class="title">数据汇总</div> | ||
| 695 | <div class="kpi-content"> | ||
| 696 | <div class="border-content"> | ||
| 697 | <span class="number score-color">{{ detailInfo.qualityScore ?? '--' }}</span> | ||
| 698 | <div class="text">质量评分<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> | ||
| 699 | <template #content> | ||
| 700 | <div style="max-width: 236px;"> | ||
| 701 | 质量一级指标得分*权重 | ||
| 702 | </div> | ||
| 703 | </template> | ||
| 704 | <el-icon style="margin-left: 2px;"> | ||
| 705 | <QuestionFilled /> | ||
| 706 | </el-icon> | ||
| 707 | </el-tooltip></div> | ||
| 708 | </div> | ||
| 709 | <div v-if="detailInfo.analysisReportType != 1" class="border-content ml16"> | ||
| 710 | <span class="number">{{ detailInfo.qualityTableNum != null ? changeNum(detailInfo.qualityTableNum ?? 0) : | ||
| 711 | '--' | ||
| 712 | }}</span> | ||
| 713 | <span class="text">评估表数</span> | ||
| 714 | </div> | ||
| 715 | <div class="border-content ml16"> | ||
| 716 | <span class="number num-color">{{ detailInfo.ruleCount != null ? changeNum(detailInfo.ruleCount ?? 0) : '--' | ||
| 717 | }}</span> | ||
| 718 | <span class="text">质量规则数</span> | ||
| 719 | </div> | ||
| 720 | <div class="border-content ml16"> | ||
| 721 | <span class="number num-color">{{ detailInfo.totalNum != null ? changeNum(detailInfo.totalNum ?? 0) : '--' | ||
| 722 | }}</span> | ||
| 723 | <span class="text">评估总数</span> | ||
| 724 | </div> | ||
| 725 | <div class="border-content ml16"> | ||
| 726 | <span class="number pass-color">{{ detailInfo.qualifiedNum != null ? changeNum(detailInfo.qualifiedNum ?? 0) | ||
| 727 | : | ||
| 728 | '--' }}</span> | ||
| 729 | <span class="text">合格条数</span> | ||
| 730 | </div> | ||
| 731 | <div class="border-content ml16"> | ||
| 732 | <span class="number no-pass-color">{{ detailInfo.unqualifiedNum != null ? | ||
| 733 | changeNum(detailInfo.unqualifiedNum ?? | ||
| 734 | 0) : '--' }}</span> | ||
| 735 | <span class="text">不合格条数</span> | ||
| 736 | </div> | ||
| 737 | <div class="border-content ml16"> | ||
| 738 | <span class="number rate-color">{{ detailInfo.qualifiedRate != null ? (changeNum((detailInfo.qualifiedRate | ||
| 739 | ?? 0) * | ||
| 740 | 100, 2, true) + '%') : '--' }}</span> | ||
| 741 | <span class="text">合格率</span> | ||
| 742 | </div> | ||
| 743 | </div> | ||
| 744 | <div class="title">评分趋势</div> | ||
| 745 | <div class="content-chart-bar" id="bar"> | ||
| 746 | </div> | ||
| 747 | <div class="content-radar-table"> | ||
| 748 | <div class="content-two"> | ||
| 749 | <div class="title">数据质量一级指标得分</div> | ||
| 750 | <div class="content-chart-radar" id="radar"> | ||
| 751 | </div> | ||
| 752 | </div> | ||
| 753 | <div class="content-two"> | ||
| 754 | <div class="title">数据质量一级指标得分明细</div> | ||
| 755 | <Table :tableInfo="qualityRuleDetailTableInfo" /> | ||
| 756 | </div> | ||
| 757 | </div> | ||
| 758 | <div class="title">评估方案明细</div> | ||
| 759 | <Table :tableInfo="planDetailTableInfo" /> | ||
| 760 | <template v-if="detailInfo.analysisReportType != 1"> | ||
| 761 | <div class="title">表明细</div> | ||
| 762 | <Table :tableInfo="modelDetailTableInfo" /> | ||
| 763 | </template> | ||
| 764 | <div class="title">规则明细</div> | ||
| 765 | <Table :tableInfo="modelRuleDetailTableInfo" /> | ||
| 766 | </div> | ||
| 767 | <div v-show="isWordStyle" class="word-report"> | ||
| 768 | <div class="word-btn"> | ||
| 769 | <span>{{ `【${(detailInfo.analysisReportType == 1 ? detailInfo.qualityModelName : | ||
| 770 | (detailInfo.analysisReportType | ||
| 771 | == 3 ? detailInfo.qualityModelGroupName : detailInfo.dataSourceName)) ?? '--'}】` + '数据质量评估报告' }}</span> | ||
| 772 | <div> | ||
| 773 | <el-button @click="isWordStyle = false">返回</el-button> | ||
| 774 | <el-button type="primary" :disabled="downloadBtnDisable" @click="download">下载 Word</el-button> | ||
| 775 | </div> | ||
| 776 | </div> | ||
| 777 | <div class="word-report-main" ref="report"> | ||
| 778 | <div style="width: 625px;"> | ||
| 779 | <div style="text-align: center;padding: 8px 0px;"> | ||
| 780 | <div | ||
| 781 | style="height: 40px;display: block;line-height: 32px;font-size: 18px;color: #212121;font-weight: 600;text-align: center;"> | ||
| 782 | {{ `【${(detailInfo.analysisReportType == 1 ? detailInfo.qualityModelName : | ||
| 783 | (detailInfo.analysisReportType | ||
| 784 | == 3 ? detailInfo.qualityModelGroupName : detailInfo.dataSourceName)) ?? '--'}】` + '数据质量评估报告' }}</div> | ||
| 785 | <div style="font-size: 14px;color: #666666;font-weight: 400;">{{ '生成时间:' + detailInfo.execTime }}</div> | ||
| 786 | </div> | ||
| 787 | <div style=" height: 40px;line-height: 40px;font-size: 14px;color: #666666;"> | ||
| 788 | <template v-if="detailInfo.analysisReportType == 1"> | ||
| 789 | <span>{{ '表名称:' + (detailInfo.qualityModelName ?? '--') }}</span> | ||
| 790 | <span style="margin-left: 40px;">{{ '所属主题:' + (detailInfo.subjectDomainName ?? '--') }}</span> | ||
| 791 | <span style="margin-left: 40px;">{{ '所属数据源:' + (detailInfo.dataSourceName ?? '--') }}</span> | ||
| 792 | </template> | ||
| 793 | <span v-else-if="detailInfo.analysisReportType == 3">{{ '分组名称:' + (detailInfo.qualityModelGroupName ?? '--') | ||
| 794 | }}</span> | ||
| 795 | <span v-else>{{ '数据库名称:' + (detailInfo.dataSourceName ?? '--') }}</span> | ||
| 796 | </div> | ||
| 797 | <div | ||
| 798 | style="line-height: 24px;font-size: 16px;color: #212121;font-weight: 600;margin-top: 12px;margin-bottom: 8px;"> | ||
| 799 | 数据汇总</div> | ||
| 800 | <div class="kpi-content"> | ||
| 801 | <div class="border-content" style="padding-left: 8px;min-width: 70px;height: 72px"> | ||
| 802 | <span class="number score-color">{{ detailInfo.qualityScore ?? '--' }}</span> | ||
| 803 | <div class="text">质量评分</div> | ||
| 804 | </div> | ||
| 805 | <div v-if="detailInfo.analysisReportType != 1" class="border-content" | ||
| 806 | style="padding-left: 8px;margin-left: 8px;min-width: 70px;height: 72px"> | ||
| 807 | <span class="number">{{ detailInfo.qualityTableNum != null ? changeNum(detailInfo.qualityTableNum ?? 0) | ||
| 808 | : | ||
| 809 | '--' | ||
| 810 | }}</span> | ||
| 811 | <span class="text">评估表数</span> | ||
| 812 | </div> | ||
| 813 | <div class="border-content" style="padding-left: 8px;margin-left: 8px;min-width: 84px;height: 72px"> | ||
| 814 | <span class="number num-color">{{ detailInfo.ruleCount != null ? changeNum(detailInfo.ruleCount ?? 0) : | ||
| 815 | '--' | ||
| 816 | }}</span> | ||
| 817 | <span class="text">质量规则数</span> | ||
| 818 | </div> | ||
| 819 | <div class="border-content" style="padding-left: 8px;margin-left: 8px;min-width: 70px;height: 72px"> | ||
| 820 | <span class="number num-color">{{ detailInfo.totalNum != null ? changeNum(detailInfo.totalNum ?? 0) : | ||
| 821 | '--' | ||
| 822 | }}</span> | ||
| 823 | <span class="text">评估总数</span> | ||
| 824 | </div> | ||
| 825 | <div class="border-content" style="padding-left: 8px;margin-left: 8px;min-width: 70px;height: 72px"> | ||
| 826 | <span class="number pass-color">{{ detailInfo.qualifiedNum != null ? changeNum(detailInfo.qualifiedNum | ||
| 827 | ?? 0) | ||
| 828 | : | ||
| 829 | '--' }}</span> | ||
| 830 | <span class="text">合格条数</span> | ||
| 831 | </div> | ||
| 832 | <div class="border-content" style="padding-left: 8px;margin-left: 8px;min-width: 84px;height: 72px"> | ||
| 833 | <span class="number no-pass-color">{{ detailInfo.unqualifiedNum != null ? | ||
| 834 | changeNum(detailInfo.unqualifiedNum ?? | ||
| 835 | 0) : '--' }}</span> | ||
| 836 | <span class="text">不合格条数</span> | ||
| 837 | </div> | ||
| 838 | <div class="border-content" style="padding-left: 8px;margin-left: 8px;min-width: 70px;height: 72px"> | ||
| 839 | <span class="number rate-color">{{ detailInfo.qualifiedRate != null ? | ||
| 840 | (changeNum((detailInfo.qualifiedRate | ||
| 841 | ?? 0) * | ||
| 842 | 100, 2, true) + '%') : '--' }}</span> | ||
| 843 | <span class="text">合格率</span> | ||
| 844 | </div> | ||
| 845 | </div> | ||
| 846 | <div | ||
| 847 | style="line-height: 24px;font-size: 16px;color: #212121;font-weight: 600;margin-top: 12px;margin-bottom: 8px;"> | ||
| 848 | 评分趋势</div> | ||
| 849 | <div class="content-chart-bar" style="border:none;padding: 8px 0px;" id="bar-word"> | ||
| 850 | </div> | ||
| 851 | <div | ||
| 852 | style="line-height: 24px;font-size: 16px;color: #212121;font-weight: 600;margin-top: 12px;margin-bottom: 8px;"> | ||
| 853 | 数据质量一级指标得分</div> | ||
| 854 | <div class="content-chart-bar" style="border:none;height: 380px;padding: 8px 0px;" id="radar-word"></div> | ||
| 855 | <div | ||
| 856 | style="line-height: 24px;font-size: 16px;color: #212121;font-weight: 600;margin-top: 12px;margin-bottom: 8px;"> | ||
| 857 | 数据质量一级指标得分明细</div> | ||
| 858 | <table border="1" cellspacing="0" | ||
| 859 | style="width: 100%;table-layout: fixed;word-break: break-all;margin: 0 auto;text-align: center;border-collapse: collapse;"> | ||
| 860 | <thead> | ||
| 861 | <tr> | ||
| 862 | <th v-for="(item, index) in qualityRuleDetailTableInfo.fields.map(f => f.label)" :key="index"> | ||
| 863 | <span>{{ item }}</span> | ||
| 864 | </th> | ||
| 865 | </tr> | ||
| 866 | </thead> | ||
| 867 | <tbody> | ||
| 868 | <tr v-for="(recordItem, j) in qualityRuleDetailTableInfo.data" :key="j"> | ||
| 869 | <td v-for="(columnItem, i) in qualityRuleDetailTableInfo.fields" :key="i" | ||
| 870 | :style="{ 'text-align': <any>(columnItem.align ?? 'left') }"> | ||
| 871 | <span :style="{ 'word-break': 'break-all' }"> | ||
| 872 | {{ columnItem.getName ? columnItem.getName({ row: recordItem }) : ((columnItem?.field && | ||
| 873 | recordItem[columnItem.field]) ?? '--') }} | ||
| 874 | </span> | ||
| 875 | </td> | ||
| 876 | </tr> | ||
| 877 | </tbody> | ||
| 878 | </table> | ||
| 879 | <div style=" | ||
| 880 | background: #FFF1D4; | ||
| 881 | /* border-bottom: 1px solid #000; | ||
| 882 | border-right: 1px solid #000; | ||
| 883 | border-left: 1px solid #000; */ | ||
| 884 | "><span >质量得分:</span><span>∑规则得分*权重</span></div> | ||
| 885 | <div | ||
| 886 | style="line-height: 24px;font-size: 16px;color: #212121;font-weight: 600;margin-top: 12px;margin-bottom: 8px;"> | ||
| 887 | 评估方案明细</div> | ||
| 888 | <table border="1" cellspacing="0" | ||
| 889 | style="width: 100%;table-layout: fixed;word-break: break-all;margin: 0 auto;text-align: center;border-collapse: collapse;"> | ||
| 890 | <thead> | ||
| 891 | <tr> | ||
| 892 | <th v-for="(item, index) in planDetailTableInfo.fields.map(f => f.label)" :key="index"> | ||
| 893 | <span>{{ item }}</span> | ||
| 894 | </th> | ||
| 895 | </tr> | ||
| 896 | </thead> | ||
| 897 | <tbody> | ||
| 898 | <tr v-for="(recordItem, j) in planDetailTableInfo.data" :key="j"> | ||
| 899 | <td v-for="(columnItem, i) in planDetailTableInfo.fields" :key="i" | ||
| 900 | :style="{ 'text-align': <any>(columnItem.align ?? 'left'), width: columnItem.field == 'qualifiedRate' ? '53px' : '' }"> | ||
| 901 | <span :style="{ 'word-break': 'break-all' }"> | ||
| 902 | {{ columnItem.getName ? columnItem.getName({ row: recordItem }) : ((columnItem?.field && | ||
| 903 | recordItem[columnItem.field]) ?? '--') }} | ||
| 904 | </span> | ||
| 905 | </td> | ||
| 906 | </tr> | ||
| 907 | </tbody> | ||
| 908 | </table> | ||
| 909 | <template v-if="detailInfo.analysisReportType != 1"> | ||
| 910 | <div | ||
| 911 | style="line-height: 24px;font-size: 16px;color: #212121;font-weight: 600;margin-top: 12px;margin-bottom: 8px;"> | ||
| 912 | 表明细</div> | ||
| 913 | <table border="1" cellspacing="0" | ||
| 914 | style="width: 100%;table-layout: fixed;word-break: break-all;margin: 0 auto;text-align: center;border-collapse: collapse;"> | ||
| 915 | <thead> | ||
| 916 | <tr> | ||
| 917 | <th v-for="(item, index) in modelDetailTableInfo.fields.slice(1).map(f => f.label)" :key="index"> | ||
| 918 | <span>{{ item }}</span> | ||
| 919 | </th> | ||
| 920 | </tr> | ||
| 921 | </thead> | ||
| 922 | <tbody> | ||
| 923 | <tr v-for="(recordItem, j) in modelDetailTableInfo.data" :key="j"> | ||
| 924 | <td v-for="(columnItem, i) in modelDetailTableInfo.fields.slice(1)" :key="i" | ||
| 925 | :style="{ 'text-align': <any>(columnItem.align ?? 'left'), width: columnItem.field == 'qualifiedRate' ? '53px' : '' }"> | ||
| 926 | <span v-if="columnItem.type != 'tag'"> | ||
| 927 | {{ (columnItem.getName ? columnItem.getName({ row: recordItem }) : ((columnItem?.field && | ||
| 928 | recordItem[columnItem.field]) ?? '--')) }} | ||
| 929 | </span> | ||
| 930 | <span v-else :style="{ | ||
| 931 | 'white-space': 'nowrap', 'font-size': '12px', color: tagType(recordItem, columnItem.field) == 'success' ? '#1BA854' : (tagType(recordItem, columnItem.field) == 'warning' ? '#e6a23c' : (tagType(recordItem, columnItem.field) == 'danger' ? '#FB2323' : '#999999')), | ||
| 932 | background: tagType(recordItem, columnItem.field) == 'success' ? '#F2FFF5' : (tagType(recordItem, columnItem.field) == 'warning' ? '#fdf6ec' : (tagType(recordItem, columnItem.field) == 'danger' ? '#FFF2F4' : '#F5F5F5')) | ||
| 933 | }"> | ||
| 934 | {{ (columnItem.field && recordItem[columnItem.field]) ? tagMethod(recordItem, columnItem.field) : | ||
| 935 | '--' }} | ||
| 936 | </span> | ||
| 937 | </td> | ||
| 938 | </tr> | ||
| 939 | </tbody> | ||
| 940 | </table> | ||
| 941 | </template> | ||
| 942 | <div | ||
| 943 | style="line-height: 24px;font-size: 16px;color: #212121;font-weight: 600;margin-top: 12px;margin-bottom: 8px;"> | ||
| 944 | 规则明细</div> | ||
| 945 | <table border="1" cellspacing="0" | ||
| 946 | style="width: 100%;table-layout: fixed;word-break: break-all;margin: 0 auto;text-align: center;border-collapse: collapse;"> | ||
| 947 | <thead> | ||
| 948 | <tr> | ||
| 949 | <th v-for="(item, index) in modelRuleDetailTableInfo.fields.slice(1).map(f => f.label)" :key="index"> | ||
| 950 | <span>{{ item }}</span> | ||
| 951 | </th> | ||
| 952 | </tr> | ||
| 953 | </thead> | ||
| 954 | <tbody> | ||
| 955 | <tr v-for="(recordItem, j) in modelRuleDetailTableInfo.data" :key="j"> | ||
| 956 | <td v-for="(columnItem, i) in modelRuleDetailTableInfo.fields.slice(1)" :key="i" | ||
| 957 | :style="{ 'text-align': <any>(columnItem.align ?? 'left'), width: columnItem.field == 'qualifiedRate' ? '53px' : '' }"> | ||
| 958 | <span v-if="columnItem.type != 'tag'"> | ||
| 959 | {{ (columnItem.getName ? columnItem.getName({ row: recordItem }) : ((columnItem?.field && | ||
| 960 | recordItem[columnItem.field]) ?? '--')) }} | ||
| 961 | </span> | ||
| 962 | <span v-else | ||
| 963 | :style="{ | ||
| 964 | 'white-space': 'nowrap', 'font-size': '12px', color: tagType(recordItem, columnItem.field) == 'success' ? '#1BA854' : (tagType(recordItem, columnItem.field) == 'warning' ? '#e6a23c' : (tagType(recordItem, columnItem.field) == 'danger' ? '#FB2323' : '#999999')), | ||
| 965 | background: tagType(recordItem, columnItem.field) == 'success' ? '#F2FFF5' : (tagType(recordItem, columnItem.field) == 'warning' ? '#fdf6ec' : (tagType(recordItem, columnItem.field) == 'danger' ? '#FFF2F4' : '#F5F5F5')) }"> | ||
| 966 | {{ (columnItem.field && recordItem[columnItem.field]) ? tagMethod(recordItem, columnItem.field) : | ||
| 967 | '--' | ||
| 968 | }} | ||
| 969 | </span> | ||
| 970 | </td> | ||
| 971 | </tr> | ||
| 972 | </tbody> | ||
| 973 | </table> | ||
| 974 | </div> | ||
| 975 | </div> | ||
| 976 | </div> | ||
| 977 | </div> | ||
| 978 | </template> | ||
| 979 | |||
| 980 | <style lang="scss" scoped> | ||
| 981 | .container_wrap { | ||
| 982 | padding: 0; | ||
| 983 | } | ||
| 984 | |||
| 985 | .content-main { | ||
| 986 | height: calc(100% - 106px); | ||
| 987 | overflow-y: auto; | ||
| 988 | overflow-x: hidden; | ||
| 989 | padding: 0px 16px 16px; | ||
| 990 | } | ||
| 991 | |||
| 992 | .header-title { | ||
| 993 | margin: 16px 16px 0px; | ||
| 994 | height: 40px; | ||
| 995 | line-height: 32px; | ||
| 996 | display: flex; | ||
| 997 | flex-direction: row; | ||
| 998 | justify-content: space-between; | ||
| 999 | align-items: center; | ||
| 1000 | font-size: 18px; | ||
| 1001 | color: #212121; | ||
| 1002 | font-weight: 600; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | .time-detail { | ||
| 1006 | font-size: 14px; | ||
| 1007 | color: #666666; | ||
| 1008 | font-weight: 400; | ||
| 1009 | margin-left: 16px; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | .header-detail { | ||
| 1013 | height: 40px; | ||
| 1014 | line-height: 40px; | ||
| 1015 | font-size: 14px; | ||
| 1016 | color: #666666; | ||
| 1017 | padding: 0 16px; | ||
| 1018 | border-bottom: 1px solid #d9d9d9; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | .kpi-content { | ||
| 1022 | display: flex; | ||
| 1023 | flex-direction: row; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | .border-content { | ||
| 1027 | height: 76px; | ||
| 1028 | display: flex; | ||
| 1029 | flex-direction: column; | ||
| 1030 | align-items: left; | ||
| 1031 | padding-left: 16px; | ||
| 1032 | justify-content: center; | ||
| 1033 | border: 1px solid #d9d9d9; | ||
| 1034 | width: 160px; | ||
| 1035 | min-width: 100px; | ||
| 1036 | border-radius: 2px; | ||
| 1037 | |||
| 1038 | .number { | ||
| 1039 | line-height: 30px; | ||
| 1040 | font-weight: 700; | ||
| 1041 | font-size: 20px; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | .num-color { | ||
| 1045 | color: #212121; | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | .score-color { | ||
| 1049 | color: #FF5F1F; | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | .pass-color { | ||
| 1053 | color: #1BA854; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | .no-pass-color { | ||
| 1057 | color: #FB2323; | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | .rate-color { | ||
| 1061 | color: #5B8FF9; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | .text { | ||
| 1065 | font-size: 14px; | ||
| 1066 | color: #666666; | ||
| 1067 | display: flex; | ||
| 1068 | |||
| 1069 | .el-icon { | ||
| 1070 | color: #b2b2b2; | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | .ml16 { | ||
| 1076 | margin-left: 16px; | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | .ml32 { | ||
| 1080 | margin-left: 32px; | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | .title { | ||
| 1084 | line-height: 24px; | ||
| 1085 | font-size: 16px; | ||
| 1086 | color: #212121; | ||
| 1087 | font-weight: 600; | ||
| 1088 | margin-top: 12px; | ||
| 1089 | margin-bottom: 8px; | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | .content-radar-table { | ||
| 1093 | display: flex; | ||
| 1094 | flex-direction: row; | ||
| 1095 | height: 380px; | ||
| 1096 | justify-content: space-between; | ||
| 1097 | |||
| 1098 | .content-two { | ||
| 1099 | width: calc(50% - 16px); | ||
| 1100 | |||
| 1101 | .table_panel { | ||
| 1102 | min-height: 200px !important; | ||
| 1103 | height: calc(100% - 44px) !important; | ||
| 1104 | } | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | .content-chart-radar { | ||
| 1108 | height: calc(100% - 44px); | ||
| 1109 | width: 100%; | ||
| 1110 | border: 1px solid #d9d9d9; | ||
| 1111 | padding: 8px; | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | .content-chart-bar { | ||
| 1116 | height: 300px; | ||
| 1117 | border: 1px solid #d9d9d9; | ||
| 1118 | padding: 8px; | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | .word-report { | ||
| 1122 | height: 100%; | ||
| 1123 | widows: 100%; | ||
| 1124 | background-color: #f1f1f1; | ||
| 1125 | |||
| 1126 | .word-btn { | ||
| 1127 | background-color: #fff; | ||
| 1128 | margin: 8px 0px; | ||
| 1129 | padding: 0px 8px 8px; | ||
| 1130 | height: 48px; | ||
| 1131 | line-height: 32px; | ||
| 1132 | display: flex; | ||
| 1133 | flex-direction: row; | ||
| 1134 | justify-content: space-between; | ||
| 1135 | align-items: center; | ||
| 1136 | font-size: 18px; | ||
| 1137 | color: #212121; | ||
| 1138 | font-weight: 600; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | } | ||
| 1142 | |||
| 1143 | .word-report-main { | ||
| 1144 | height: calc(100% - 56px); | ||
| 1145 | overflow-y: auto; | ||
| 1146 | overflow-x: hidden; | ||
| 1147 | display: flex; | ||
| 1148 | flex-direction: column; | ||
| 1149 | align-items: center; | ||
| 1150 | background-color: #fff; | ||
| 1151 | padding-bottom: 12px; | ||
| 1152 | } | ||
| 1153 | </style> |
src/views/data_quality/assessDetail.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: assessDetail | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="assessDetail"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { useRouter, useRoute } from "vue-router"; | ||
| 8 | import Table from "@/components/Table/index.vue"; | ||
| 9 | import { | ||
| 10 | getRecordRuleConfDetail, | ||
| 11 | getExecPlanDetailTableData, | ||
| 12 | getAssessTableRulesData, | ||
| 13 | downloadDirtyData | ||
| 14 | } from '@/api/modules/dataQualityAssess'; | ||
| 15 | import { ElMessage } from "element-plus"; | ||
| 16 | import { changeNum, download } from '@/utils/common'; | ||
| 17 | import ruleForm from "../data_quality/ruleForm.vue"; | ||
| 18 | |||
| 19 | const { proxy } = getCurrentInstance() as any; | ||
| 20 | |||
| 21 | const router = useRouter(); | ||
| 22 | const route = useRoute(); | ||
| 23 | |||
| 24 | const planGuid = route.query.planGuid; | ||
| 25 | const planExecGuid = route.query.planExecGuid; | ||
| 26 | |||
| 27 | const page = ref({ | ||
| 28 | limit: 50, | ||
| 29 | curr: 1, | ||
| 30 | sizes: [ | ||
| 31 | { label: "10", value: 10 }, | ||
| 32 | { label: "50", value: 50 }, | ||
| 33 | { label: "100", value: 100 }, | ||
| 34 | { label: "150", value: 150 }, | ||
| 35 | { label: "200", value: 200 }, | ||
| 36 | ], | ||
| 37 | }); | ||
| 38 | |||
| 39 | const tableInfo = ref({ | ||
| 40 | id: "quality-table", | ||
| 41 | loading: false, | ||
| 42 | fields: [ | ||
| 43 | { label: "表名", field: "qualityModelName", width: 140 }, | ||
| 44 | { label: "执行结果", field: "execResult", type: "tag", width: 100, align: "center" }, | ||
| 45 | { label: "评估时间", field: "execTime", width: 180, }, | ||
| 46 | { | ||
| 47 | label: "耗时(秒)", field: "execDuration", width: 100, align: "right", getName: (scope) => { | ||
| 48 | return scope.row.execDuration != null ? changeNum(scope.row.execDuration ?? 0) : '--'; | ||
| 49 | } | ||
| 50 | }, | ||
| 51 | { | ||
| 52 | label: "规则数", field: "ruleNum", width: 100, align: "right", getName: (scope) => { | ||
| 53 | return scope.row.ruleNum != null ? changeNum(scope.row.ruleNum ?? 0) : '--'; | ||
| 54 | } | ||
| 55 | }, | ||
| 56 | { | ||
| 57 | label: "评估总数", field: "totalNum", width: 100, align: "right", getName: (scope) => { | ||
| 58 | return scope.row.totalNum != null ? changeNum(scope.row.totalNum ?? 0) : '--'; | ||
| 59 | } | ||
| 60 | }, | ||
| 61 | { | ||
| 62 | label: "合格条数", field: "qualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 63 | return scope.row.qualifiedNum != null ? changeNum(scope.row.qualifiedNum ?? 0) : '--'; | ||
| 64 | } | ||
| 65 | }, | ||
| 66 | { | ||
| 67 | label: "不合格条数", field: "unqualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 68 | return scope.row.unqualifiedNum != null ? changeNum(scope.row.unqualifiedNum ?? 0) : '--'; | ||
| 69 | } | ||
| 70 | }, | ||
| 71 | { | ||
| 72 | label: "合格率", field: "qualifiedRate", width: 100, align: "right", getName: (scope) => { | ||
| 73 | return scope.row.qualifiedRate != null ? ((scope.row.qualifiedRate ?? 0).toFixed(2) + '%') : '--'; | ||
| 74 | } | ||
| 75 | }, | ||
| 76 | { label: "评估范围", field: "dataRange", width: 180 }, | ||
| 77 | ], | ||
| 78 | data: [], | ||
| 79 | showPage: false, | ||
| 80 | actionInfo: { | ||
| 81 | label: "操作", | ||
| 82 | type: "btn", | ||
| 83 | width: 220, | ||
| 84 | fixed: 'right', | ||
| 85 | btns: (scope) => { | ||
| 86 | let unqualifiedNum = scope.row.unqualifiedNum ?? 0; | ||
| 87 | return [ | ||
| 88 | { label: "查看规则", value: "rule" }, | ||
| 89 | { label: "脏数据", value: "path_dirty", disabled: unqualifiedNum == 0 || scope.row.isTable == 'Y' }, | ||
| 90 | { label: "脏数据下载", value: "download", disabled: unqualifiedNum == 0 || scope.row.isTable == 'Y' }, | ||
| 91 | ] | ||
| 92 | }, | ||
| 93 | } | ||
| 94 | }); | ||
| 95 | |||
| 96 | /** 获取方案详情列表数据。 */ | ||
| 97 | const getAssessDetail = () => { | ||
| 98 | tableInfo.value.loading = true; | ||
| 99 | getExecPlanDetailTableData({ planGuid: planGuid, planExecGuid: planExecGuid }).then((res: any) => { | ||
| 100 | tableInfo.value.loading = false; | ||
| 101 | if (res.code == proxy.$passCode) { | ||
| 102 | tableInfo.value.data = res.data || [] | ||
| 103 | } else { | ||
| 104 | ElMessage.error(res.msg); | ||
| 105 | } | ||
| 106 | }); | ||
| 107 | } | ||
| 108 | |||
| 109 | /** 获取每个表的规则详情。 */ | ||
| 110 | const getModelRulesDetail = (qualityModelGuid: string) => { | ||
| 111 | rulesDetailTableInfo.value.loading = true; | ||
| 112 | getAssessTableRulesData({ planExecGuid: planExecGuid, qualityModelGuid: qualityModelGuid }).then((res: any) => { | ||
| 113 | rulesDetailTableInfo.value.loading = false; | ||
| 114 | if (res.code == proxy.$passCode) { | ||
| 115 | rulesDetailTableInfo.value.data = res.data || []; | ||
| 116 | } else { | ||
| 117 | ElMessage.error(res.msg); | ||
| 118 | } | ||
| 119 | }); | ||
| 120 | } | ||
| 121 | |||
| 122 | /** 下载脏数据。 */ | ||
| 123 | const exportDirtyData = (row) => { | ||
| 124 | downloadDirtyData({ | ||
| 125 | planExecGuid: row.planExecGuid, | ||
| 126 | qualityModelGuid: row.qualityModelGuid | ||
| 127 | }).then((res: any) => { | ||
| 128 | if (res && !res.msg) { | ||
| 129 | download(res, `脏数据-${row.qualityModelName}.xlsx`, 'excel'); | ||
| 130 | } else { | ||
| 131 | res?.msg && ElMessage.error(res?.msg); | ||
| 132 | } | ||
| 133 | }) | ||
| 134 | } | ||
| 135 | |||
| 136 | const tableBtnClick = (scope, btn) => { | ||
| 137 | const type = btn.value; | ||
| 138 | const row = scope.row; | ||
| 139 | if (type == 'rule') { | ||
| 140 | rulesDetailDialogVisible.value = true; | ||
| 141 | getModelRulesDetail(row.qualityModelGuid); | ||
| 142 | } else if (type == 'path_dirty') { | ||
| 143 | router.push({ | ||
| 144 | name: 'assessDirty', | ||
| 145 | query: { | ||
| 146 | planExecGuid: row.planExecGuid, | ||
| 147 | name: row.qualityModelName, | ||
| 148 | qualityModelGuid: row.qualityModelGuid | ||
| 149 | } | ||
| 150 | }); | ||
| 151 | } else if (type == 'download') { | ||
| 152 | exportDirtyData(row); | ||
| 153 | } | ||
| 154 | }; | ||
| 155 | |||
| 156 | onActivated(() => { | ||
| 157 | getAssessDetail(); | ||
| 158 | }); | ||
| 159 | |||
| 160 | /** 查看表规则详情的对话框显示隐藏。 */ | ||
| 161 | const rulesDetailDialogVisible = ref(false); | ||
| 162 | |||
| 163 | const rulesDetailTableInfo: any = ref({ | ||
| 164 | id: "rules-detail-table", | ||
| 165 | loading: false, | ||
| 166 | fields: [ | ||
| 167 | { label: "序号", type: "index", width: 56, align: "center" }, | ||
| 168 | { label: "表名", field: "qualityModelName", width: 140 }, | ||
| 169 | { label: "规则类型", field: "ruleName", width: 140 }, | ||
| 170 | { label: "规则名称", field: "ruleConfName", width: 140 }, | ||
| 171 | { label: "规则大类", field: "largeCategory", width: 100 }, | ||
| 172 | { label: "规则小类", field: "smallCategory", width: 140 }, | ||
| 173 | { label: "规则字段", field: "ruleField", width: 140 }, | ||
| 174 | { label: "执行结果", field: "execResult", type: "tag", width: 100, align: "center" }, | ||
| 175 | // { label: "规则权重", field: "weight", width: 100, align: 'right' }, | ||
| 176 | { | ||
| 177 | label: "评估总数", field: "totalNum", width: 100, align: "right", getName: (scope) => { | ||
| 178 | return scope.row.totalNum != null ? changeNum(scope.row.totalNum ?? 0) : '--'; | ||
| 179 | } | ||
| 180 | }, | ||
| 181 | { | ||
| 182 | label: "合格条数", field: "qualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 183 | return scope.row.qualifiedNum != null ? changeNum(scope.row.qualifiedNum ?? 0) : '--'; | ||
| 184 | } | ||
| 185 | }, | ||
| 186 | { | ||
| 187 | label: "不合格条数", field: "unqualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 188 | return scope.row.unqualifiedNum != null ? changeNum(scope.row.unqualifiedNum ?? 0) : '--'; | ||
| 189 | } | ||
| 190 | }, | ||
| 191 | { | ||
| 192 | label: "合格率", field: "qualifiedRate", width: 100, align: "right", getName: (scope) => { | ||
| 193 | return scope.row.qualifiedRate != null ? ((scope.row.qualifiedRate ?? 0).toFixed(2) + '%') : '--'; | ||
| 194 | } | ||
| 195 | }, | ||
| 196 | { label: "失败原因", field: "errorLog", width: 140 }, | ||
| 197 | ], | ||
| 198 | data: [], | ||
| 199 | showPage: false, | ||
| 200 | actionInfo: { | ||
| 201 | label: "操作", | ||
| 202 | type: "btn", | ||
| 203 | width: 90, | ||
| 204 | fixed: 'right', | ||
| 205 | btns: [ | ||
| 206 | { label: "规则详情 ", value: "ruleDetail" }, | ||
| 207 | ], | ||
| 208 | } | ||
| 209 | }); | ||
| 210 | |||
| 211 | /** 单个规则详情对话框 */ | ||
| 212 | const oneRulesDetailDialogVisible = ref(false); | ||
| 213 | |||
| 214 | const toSubjectTables: any = ref([]); | ||
| 215 | const ruleType = ref(''); | ||
| 216 | const detailInfo: any = ref({}); | ||
| 217 | const detailLoading = ref(false); | ||
| 218 | const ruleTypeList: any = ref([]); | ||
| 219 | const smallCategoryList: any = ref([]); | ||
| 220 | const largeCategoryList: any = ref([]); | ||
| 221 | const detailJson: any = ref({}); | ||
| 222 | |||
| 223 | const rulesDetailTableBtnClick = (scope, btn) => { | ||
| 224 | const type = btn.value; | ||
| 225 | const row = scope.row; | ||
| 226 | if (type == 'ruleDetail') { | ||
| 227 | detailLoading.value = true; | ||
| 228 | if (detailJson.value[row.ruleConfGuid]) { | ||
| 229 | ruleType.value = detailInfo.value.ruleCode; | ||
| 230 | toSubjectTables.value = [{ | ||
| 231 | guid: detailInfo.value.subjectGuid, | ||
| 232 | enName: detailInfo.value.subjectName, | ||
| 233 | chName: detailInfo.value.subjectZhName, | ||
| 234 | label: `${detailInfo.value.subjectName}(${detailInfo.value.subjectZhName})` | ||
| 235 | }] | ||
| 236 | ruleTypeList.value = [{ | ||
| 237 | value: detailInfo.value.ruleCode, | ||
| 238 | label: row.ruleName | ||
| 239 | }]; | ||
| 240 | smallCategoryList.value = [{ | ||
| 241 | paramValue: detailInfo.value.smallCategory, | ||
| 242 | paramName: row.smallCategory | ||
| 243 | }]; | ||
| 244 | largeCategoryList.value = [{ | ||
| 245 | paramValue: detailInfo.value.largeCategory, | ||
| 246 | paramName: row.largeCategory | ||
| 247 | }]; | ||
| 248 | oneRulesDetailDialogVisible.value = true; | ||
| 249 | } else { | ||
| 250 | detailJson.value[row.ruleConfGuid] = { isRequest: true }; | ||
| 251 | getRecordRuleConfDetail({ruleConfGuid: row.ruleConfGuid, planExecGuid: planExecGuid }).then((res: any) => { | ||
| 252 | detailLoading.value = false; | ||
| 253 | oneRulesDetailDialogVisible.value = true; | ||
| 254 | if (res.code == proxy.$passCode) { | ||
| 255 | let data = res.data || {}; | ||
| 256 | detailInfo.value = data; | ||
| 257 | detailJson.value[row.ruleConfGuid] = detailInfo.value; | ||
| 258 | detailJson.value[row.ruleConfGuid].isRequest = false; | ||
| 259 | ruleType.value = detailInfo.value.ruleCode; | ||
| 260 | toSubjectTables.value = [{ | ||
| 261 | guid: detailInfo.value.subjectGuid, | ||
| 262 | enName: detailInfo.value.subjectName, | ||
| 263 | chName: detailInfo.value.subjectZhName, | ||
| 264 | label: `${detailInfo.value.subjectName}(${detailInfo.value.subjectZhName})` | ||
| 265 | }] | ||
| 266 | ruleTypeList.value = [{ | ||
| 267 | value: detailInfo.value.ruleCode, | ||
| 268 | label: row.ruleName | ||
| 269 | }]; | ||
| 270 | smallCategoryList.value = [{ | ||
| 271 | paramValue: detailInfo.value.smallCategory, | ||
| 272 | paramName: row.smallCategory | ||
| 273 | }]; | ||
| 274 | largeCategoryList.value = [{ | ||
| 275 | paramValue: detailInfo.value.largeCategory, | ||
| 276 | paramName: row.largeCategory | ||
| 277 | }]; | ||
| 278 | } else { | ||
| 279 | ElMessage.error(res.msg); | ||
| 280 | delete detailJson.value[row.ruleConfGuid]; | ||
| 281 | } | ||
| 282 | }) | ||
| 283 | } | ||
| 284 | } | ||
| 285 | }; | ||
| 286 | |||
| 287 | </script> | ||
| 288 | |||
| 289 | <template> | ||
| 290 | <div class="container_wrap"> | ||
| 291 | <div class="table_panel_wrap"> | ||
| 292 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" /> | ||
| 293 | </div> | ||
| 294 | <el-dialog v-model="rulesDetailDialogVisible" title="查看规则" width="800" :modal="true" :close-on-click-modal="false" | ||
| 295 | destroy-on-close align-center> | ||
| 296 | <div class="rules-detail-dialog-content"> | ||
| 297 | <Table class="long-tooltip-table" :tableInfo="rulesDetailTableInfo" @tableBtnClick="rulesDetailTableBtnClick" /> | ||
| 298 | </div> | ||
| 299 | </el-dialog> | ||
| 300 | <el-dialog v-model="oneRulesDetailDialogVisible" title="规则详情" width="800" :modal="true" | ||
| 301 | :close-on-click-modal="false" destroy-on-close align-center> | ||
| 302 | <ruleForm ref="ruleFormRef" :readonly="true" :toSubjectTables="toSubjectTables" :ruleTypeValue="ruleType" | ||
| 303 | :value="detailInfo" :ruleTypeList="ruleTypeList" :largeCategoryList="largeCategoryList" | ||
| 304 | :smallCategoryList="smallCategoryList"> | ||
| 305 | </ruleForm> | ||
| 306 | </el-dialog> | ||
| 307 | </div> | ||
| 308 | </template> | ||
| 309 | |||
| 310 | <style lang="scss" scoped> | ||
| 311 | .container_wrap { | ||
| 312 | padding: 16px; | ||
| 313 | } | ||
| 314 | |||
| 315 | .table_panel_wrap { | ||
| 316 | height: 100%; | ||
| 317 | } | ||
| 318 | |||
| 319 | .rules-detail-dialog-content { | ||
| 320 | height: 450px; | ||
| 321 | } | ||
| 322 | |||
| 323 | .long-tooltip-table { | ||
| 324 | :deep(.el-table) { | ||
| 325 | .table_cell_tooltip { | ||
| 326 | max-width: 500px; | ||
| 327 | // max-height: 100px; | ||
| 328 | // overflow: auto; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/assessDirty.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: assessDirty | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="assessDirty"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { calcColumnWidth } from "@/utils/index"; | ||
| 8 | import Moment from 'moment'; | ||
| 9 | import { | ||
| 10 | getQueryDirtyData, | ||
| 11 | getQueryDirtyFields, | ||
| 12 | downloadDirtyData | ||
| 13 | } from '@/api/modules/dataQualityAssess'; | ||
| 14 | import { download } from '@/utils/common' | ||
| 15 | import { ElMessage } from "element-plus"; | ||
| 16 | import { TableColumnWidth } from "@/utils/enum"; | ||
| 17 | |||
| 18 | const { proxy } = getCurrentInstance() as any; | ||
| 19 | const route: any = useRoute(); | ||
| 20 | const planExecGuid = route.query.planExecGuid; | ||
| 21 | const qualityModelGuid = route.query.qualityModelGuid; | ||
| 22 | const name = route.query.name; | ||
| 23 | |||
| 24 | const dirtyDataLoading = ref(false); | ||
| 25 | const dirtyData = ref([{ | ||
| 26 | guid: 1, | ||
| 27 | }, { | ||
| 28 | guid: '2', | ||
| 29 | isDirty: true | ||
| 30 | }, { | ||
| 31 | guid: 3 | ||
| 32 | }]); | ||
| 33 | |||
| 34 | const dirtyDataFields: any = ref([{ | ||
| 35 | enName: 'guid', | ||
| 36 | chName: '主键', | ||
| 37 | dataType: 'string' | ||
| 38 | }]); | ||
| 39 | |||
| 40 | /** 脏数据下载 */ | ||
| 41 | const exportData = () => { | ||
| 42 | if (!dirtyData.value.length || !dirtyDataFields.value.length) { | ||
| 43 | ElMessage.error('当没有可下载的脏数据'); | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | downloadDirtyData({ | ||
| 47 | planExecGuid: planExecGuid, | ||
| 48 | qualityModelGuid: qualityModelGuid | ||
| 49 | }).then((res: any) => { | ||
| 50 | if (res && !res.msg) { | ||
| 51 | download(res, `脏数据-${name}.xlsx`, 'excel'); | ||
| 52 | } else { | ||
| 53 | res?.msg && ElMessage.error(res?.msg); | ||
| 54 | } | ||
| 55 | }) | ||
| 56 | } | ||
| 57 | |||
| 58 | const getTableData = () => { | ||
| 59 | dirtyData.value = []; | ||
| 60 | dirtyDataLoading.value = true; | ||
| 61 | let ps1 = getQueryDirtyData({ | ||
| 62 | pageSize: pageInfo.value.limit, | ||
| 63 | pageIndex: pageInfo.value.curr, | ||
| 64 | qualityModelGuid: qualityModelGuid, | ||
| 65 | planExecGuid: planExecGuid, | ||
| 66 | }).then((res: any) => { | ||
| 67 | if (res.code == proxy.$passCode) { | ||
| 68 | let data = res.data || {}; | ||
| 69 | dirtyData.value = data.records || []; | ||
| 70 | pageInfo.value.curr = data.pageIndex; | ||
| 71 | pageInfo.value.rows = data.totalRows || 0; | ||
| 72 | } else { | ||
| 73 | ElMessage.error(res.msg); | ||
| 74 | } | ||
| 75 | }); | ||
| 76 | let ps2 = getQueryDirtyFields(qualityModelGuid).then((res: any) => { | ||
| 77 | if (res.code == proxy.$passCode) { | ||
| 78 | dirtyDataFields.value = res.data.fields || []; | ||
| 79 | } else { | ||
| 80 | ElMessage.error(res.msg); | ||
| 81 | } | ||
| 82 | }) | ||
| 83 | Promise.all([ps1, ps2]).then(res => { | ||
| 84 | dirtyDataLoading.value = false; | ||
| 85 | }); | ||
| 86 | }; | ||
| 87 | |||
| 88 | onBeforeMount(() => { | ||
| 89 | getTableData(); | ||
| 90 | }); | ||
| 91 | |||
| 92 | const pageInfo = ref({ | ||
| 93 | limit: 50, | ||
| 94 | curr: 1, | ||
| 95 | sizes: [ | ||
| 96 | { label: "10", value: 10 }, | ||
| 97 | { label: "50", value: 50 }, | ||
| 98 | { label: "100", value: 100 }, | ||
| 99 | { label: "150", value: 150 }, | ||
| 100 | { label: "200", value: 200 }, | ||
| 101 | ], | ||
| 102 | type: "normal", | ||
| 103 | rows: 0, | ||
| 104 | }) | ||
| 105 | |||
| 106 | const pageChange = (info) => { | ||
| 107 | pageInfo.value.curr = Number(info.curr); | ||
| 108 | pageInfo.value.limit = Number(info.limit); | ||
| 109 | getTableData(); | ||
| 110 | } | ||
| 111 | |||
| 112 | const formatterPreviewDate = (row, info) => { | ||
| 113 | let enName = info.enName; | ||
| 114 | let v = row[enName]?.value; | ||
| 115 | if (v === "" || v === 0) { | ||
| 116 | return v; | ||
| 117 | } | ||
| 118 | if (v == null) { | ||
| 119 | return '--'; | ||
| 120 | } | ||
| 121 | if (info.dataType === 'datetime') { | ||
| 122 | return Moment(v).format('YYYY-MM-DD HH:mm:ss'); | ||
| 123 | } | ||
| 124 | if (info.dataType === 'date') { | ||
| 125 | if (isNaN(<any>(new Date(v)))) { | ||
| 126 | return Moment(parseInt(v)).format('YYYY-MM-DD'); | ||
| 127 | } else { | ||
| 128 | return Moment(v).format('YYYY-MM-DD'); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | return v; | ||
| 132 | }; | ||
| 133 | |||
| 134 | const getTextAlign = (field) => { | ||
| 135 | if (field.dataType === 'decimal' || field.dataType === 'int' || field.dataType == 'bit' || field.dataType == 'tinyint') { | ||
| 136 | return 'right'; | ||
| 137 | } | ||
| 138 | return 'left' | ||
| 139 | } | ||
| 140 | |||
| 141 | /** otherWidth表示使用标题宽度时添加标题排序图标等宽度 */ | ||
| 142 | const calcTableColumnWidth = (data: any[], prop, title, otherWidth = 0) => { | ||
| 143 | let d: any[] = []; | ||
| 144 | data.forEach((dt) => d.push(dt[prop])); | ||
| 145 | return calcColumnWidth( | ||
| 146 | d, | ||
| 147 | title, | ||
| 148 | { | ||
| 149 | fontSize: 14, | ||
| 150 | fontFamily: "SimSun", | ||
| 151 | }, | ||
| 152 | { | ||
| 153 | fontSize: 14, | ||
| 154 | fontFamily: "SimSun", | ||
| 155 | }, | ||
| 156 | otherWidth | ||
| 157 | ); | ||
| 158 | }; | ||
| 159 | |||
| 160 | /** 每列字段对应的列宽计算结果。 */ | ||
| 161 | const originTableFieldColumn: any = ref({ | ||
| 162 | guid: 140 | ||
| 163 | }); | ||
| 164 | |||
| 165 | watch( | ||
| 166 | () => dirtyData.value, | ||
| 167 | (val: any[], oldVal) => { | ||
| 168 | if (!dirtyDataFields.value?.length) { | ||
| 169 | originTableFieldColumn.value = {}; | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | originTableFieldColumn.value = {}; | ||
| 173 | dirtyDataFields.value.forEach((field, index) => { | ||
| 174 | originTableFieldColumn.value[field.enName] = calcTableColumnWidth( | ||
| 175 | val?.map(v => { | ||
| 176 | let json = {}; | ||
| 177 | for (const k in v) { | ||
| 178 | json[k] = v[k]?.value | ||
| 179 | } | ||
| 180 | return json; | ||
| 181 | }) || [], | ||
| 182 | field.enName, | ||
| 183 | field.chName, | ||
| 184 | 24 | ||
| 185 | ); | ||
| 186 | }); | ||
| 187 | } | ||
| 188 | ); | ||
| 189 | |||
| 190 | const handleDityCellClass = ({ row, column, rowIndex, columnIndex }) => { | ||
| 191 | let v = dirtyData.value[rowIndex][column.property]; | ||
| 192 | if (v?.checkInfo?.length) { | ||
| 193 | return 'dirty-cell-bg'; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | </script> | ||
| 198 | |||
| 199 | <template> | ||
| 200 | <div class="container_wrap"> | ||
| 201 | <div class="table_tool_wrap"> | ||
| 202 | <div class="tools_btns"> | ||
| 203 | <el-button type="primary" @click="exportData" v-preReClick>脏数据下载</el-button> | ||
| 204 | </div> | ||
| 205 | </div> | ||
| 206 | <div class="table_panel_wrap"> | ||
| 207 | <el-table key="guid" v-loading="dirtyDataLoading" :data="dirtyData" border tooltip-effect="light" style=" | ||
| 208 | width: 100%; | ||
| 209 | min-width: 200px; | ||
| 210 | max-width: 100%; | ||
| 211 | height: calc(100% - 44px); | ||
| 212 | display: inline-block; | ||
| 213 | " stripe :cell-class-name="handleDityCellClass"> | ||
| 214 | <el-table-column v-for="(field, index) in dirtyDataFields" :key="field.enName" :prop="field.enName" | ||
| 215 | :label="field.chName" :width="field.dataType === 'datetime' | ||
| 216 | ? TableColumnWidth.DATETIME | ||
| 217 | : field.dataType === 'date' | ||
| 218 | ? TableColumnWidth.DATE | ||
| 219 | : originTableFieldColumn[field.enName] | ||
| 220 | " :align="getTextAlign(field)" :header-align="getTextAlign(field)" :show-overflow-tooltip="true" | ||
| 221 | > | ||
| 222 | <template #default="scope"> | ||
| 223 | <el-tooltip v-if="scope.row[field.enName].checkInfo?.length" placement="bottom-start" effect="light" | ||
| 224 | popper-class="table_tooltip" trigger="click"> | ||
| 225 | <template #content> | ||
| 226 | <div style="width: 236px; text-align: justify"> | ||
| 227 | <p class="tips_title">不符合规则</p> | ||
| 228 | <p v-for="(item) in (scope.row[field.enName].checkInfo || [])">{{ item }}</p> | ||
| 229 | </div> | ||
| 230 | </template> | ||
| 231 | <span class="dirty-cell-tooltip">{{ formatterPreviewDate(scope.row, field) }}</span> | ||
| 232 | </el-tooltip> | ||
| 233 | <span v-else>{{ formatterPreviewDate(scope.row, field) }}</span> | ||
| 234 | </template> | ||
| 235 | </el-table-column> | ||
| 236 | </el-table> | ||
| 237 | <PageNav :class="[pageInfo.type]" :pageInfo="pageInfo" @pageChange="pageChange" /> | ||
| 238 | </div> | ||
| 239 | </div> | ||
| 240 | </template> | ||
| 241 | |||
| 242 | <style lang="scss" scoped> | ||
| 243 | .table_tool_wrap { | ||
| 244 | width: 100%; | ||
| 245 | height: 44px; | ||
| 246 | padding: 8px; | ||
| 247 | |||
| 248 | .tools_btns { | ||
| 249 | padding: 0; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | .table_panel_wrap { | ||
| 254 | width: 100%; | ||
| 255 | height: calc(100% - 44px); | ||
| 256 | padding: 0 8px; | ||
| 257 | } | ||
| 258 | |||
| 259 | :deep(.el-table) { | ||
| 260 | .dirty-cell-bg { | ||
| 261 | background: #FFF1D4 !important; | ||
| 262 | |||
| 263 | .cell.el-tooltip { | ||
| 264 | padding: 0px; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | .dirty-cell-tooltip { | ||
| 269 | width: 100%; | ||
| 270 | min-height: 24px; | ||
| 271 | display: block; | ||
| 272 | overflow: hidden; | ||
| 273 | text-overflow: ellipsis; | ||
| 274 | padding: 0 12px; | ||
| 275 | } | ||
| 276 | } | ||
| 277 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/assessLog.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: assessLog | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="assessLog"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { useRouter, useRoute } from "vue-router"; | ||
| 8 | import Table from "@/components/Table/index.vue"; | ||
| 9 | import Drawer from "@/components/Drawer/index.vue"; | ||
| 10 | import { | ||
| 11 | getAssessDetailTableData, | ||
| 12 | } from '@/api/modules/dataQualityAssess'; | ||
| 13 | import { ElMessage } from "element-plus"; | ||
| 14 | import { changeNum } from '@/utils/common'; | ||
| 15 | import {TableColumnWidth} from "@/utils/enum" | ||
| 16 | const { proxy } = getCurrentInstance() as any; | ||
| 17 | |||
| 18 | const router = useRouter(); | ||
| 19 | const route = useRoute(); | ||
| 20 | /** 方案guid */ | ||
| 21 | const planGuid = route.query.guid; | ||
| 22 | const planName = route.query.name; | ||
| 23 | const execType = route.query.type; | ||
| 24 | const page = ref({ | ||
| 25 | limit: 50, | ||
| 26 | curr: 1, | ||
| 27 | sizes: [ | ||
| 28 | { label: "10", value: 10 }, | ||
| 29 | { label: "50", value: 50 }, | ||
| 30 | { label: "100", value: 100 }, | ||
| 31 | { label: "150", value: 150 }, | ||
| 32 | { label: "200", value: 200 }, | ||
| 33 | ], | ||
| 34 | }); | ||
| 35 | const tableInfo = ref({ | ||
| 36 | id: "user-authority-table", | ||
| 37 | fields: [ | ||
| 38 | { type:"index", width: TableColumnWidth.INDEX, align:"center",label: "序号" }, | ||
| 39 | { label: "质检执行结果", field: "execResult", type: "tag", width: 120, align: "center" }, | ||
| 40 | { label: "评估时间", field: "execTime", width: 180, }, | ||
| 41 | { | ||
| 42 | label: "耗时(秒)", field: "execDuration", align: "right", width: 100, getName: (scope) => { | ||
| 43 | return scope.row.execDuration != null ? changeNum(scope.row.execDuration ?? 0) : '--'; | ||
| 44 | } | ||
| 45 | }, | ||
| 46 | { | ||
| 47 | label: "规则数", field: "ruleNum", width: 100, align: "right", getName: (scope) => { | ||
| 48 | return scope.row.ruleNum != null ? changeNum(scope.row.ruleNum ?? 0) : '--'; | ||
| 49 | } | ||
| 50 | }, | ||
| 51 | { | ||
| 52 | label: "评估总数", field: "totalNum", width: 100, align: "right", getName: (scope) => { | ||
| 53 | return scope.row.totalNum != null ? changeNum(scope.row.totalNum ?? 0) : '--'; | ||
| 54 | } | ||
| 55 | }, | ||
| 56 | { | ||
| 57 | label: "合格条数", field: "qualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 58 | return scope.row.qualifiedNum != null ? changeNum(scope.row.qualifiedNum ?? 0) : '--'; | ||
| 59 | } | ||
| 60 | }, | ||
| 61 | { | ||
| 62 | label: "不合格条数", field: "unqualifiedNum", width: 100, align: "right", getName: (scope) => { | ||
| 63 | return scope.row.unqualifiedNum != null ? changeNum(scope.row.unqualifiedNum ?? 0) : '--'; | ||
| 64 | } | ||
| 65 | }, | ||
| 66 | { | ||
| 67 | label: "合格率", field: "qualifiedRate", width: 100, align: "right", getName: (scope) => { | ||
| 68 | return scope.row.qualifiedRate != null ? ((scope.row.qualifiedRate ?? 0).toFixed(2) + '%') : '--'; | ||
| 69 | } | ||
| 70 | }, | ||
| 71 | ], | ||
| 72 | loading: false, | ||
| 73 | data: [], | ||
| 74 | page: { | ||
| 75 | type: "normal", | ||
| 76 | rows: 0, | ||
| 77 | ...page.value, | ||
| 78 | }, | ||
| 79 | actionInfo: { | ||
| 80 | label: "操作", | ||
| 81 | type: "btn", | ||
| 82 | width: 140, | ||
| 83 | fixed: 'right', | ||
| 84 | btns: [ | ||
| 85 | { label: "查看结果", value: "resultView" }, | ||
| 86 | { label: "日志", value: "log" } | ||
| 87 | ], | ||
| 88 | } | ||
| 89 | }); | ||
| 90 | |||
| 91 | const formTable = ref({ | ||
| 92 | type: "table", | ||
| 93 | title: "", | ||
| 94 | col: 'no-margin', | ||
| 95 | tableInfo: { | ||
| 96 | id: "log-detail-table", | ||
| 97 | loading: false, | ||
| 98 | fields: [ | ||
| 99 | { label: "执行时间", field: "changeTime", width: 140, }, | ||
| 100 | { label: "日志类型", field: "metaCurrValue", width: 120 }, | ||
| 101 | { label: "日志级别", field: "collectTaskName", width: 110 }, | ||
| 102 | { label: "执行步骤", field: "updateType", width: 240 }, | ||
| 103 | ], | ||
| 104 | data: [], | ||
| 105 | showPage: false, | ||
| 106 | actionInfo: { | ||
| 107 | show: false | ||
| 108 | }, | ||
| 109 | }, | ||
| 110 | }) | ||
| 111 | |||
| 112 | const drawerInfo: any = ref({ | ||
| 113 | visible: false, | ||
| 114 | direction: "rtl", | ||
| 115 | modalClass: "wrap_width_auto", | ||
| 116 | size: 650, | ||
| 117 | header: { | ||
| 118 | title: "日志详情", | ||
| 119 | }, | ||
| 120 | type: '', | ||
| 121 | container: { | ||
| 122 | contents: [ | ||
| 123 | formTable.value, | ||
| 124 | ], | ||
| 125 | }, | ||
| 126 | footer: { | ||
| 127 | visible: false, | ||
| 128 | }, | ||
| 129 | }) | ||
| 130 | |||
| 131 | const tablePageChange = (info) => { | ||
| 132 | page.value.curr = Number(info.curr); | ||
| 133 | page.value.limit = Number(info.limit); | ||
| 134 | getTableData(); | ||
| 135 | }; | ||
| 136 | |||
| 137 | const getTableData = () => { | ||
| 138 | tableInfo.value.loading = true; | ||
| 139 | getAssessDetailTableData({ pageSize: page.value.limit, pageIndex: page.value.curr, planGuid: planGuid }).then((res: any) => { | ||
| 140 | tableInfo.value.loading = false; | ||
| 141 | if (res.code == proxy.$passCode) { | ||
| 142 | const data = res.data || {} | ||
| 143 | tableInfo.value.data = data.records || [] | ||
| 144 | tableInfo.value.page.limit = data.pageSize ?? 50; | ||
| 145 | tableInfo.value.page.curr = data.pageIndex | ||
| 146 | tableInfo.value.page.rows = data.totalRows ?? 0; | ||
| 147 | } else { | ||
| 148 | ElMessage.error(res.msg); | ||
| 149 | } | ||
| 150 | }); | ||
| 151 | }; | ||
| 152 | |||
| 153 | const tableBtnClick = (scope, btn) => { | ||
| 154 | const type = btn.value; | ||
| 155 | const row = scope.row; | ||
| 156 | if (type == 'resultView') { | ||
| 157 | if(!!execType) { | ||
| 158 | router.push({ | ||
| 159 | name: 'syncAssessDetail', | ||
| 160 | query: { | ||
| 161 | name: planName, | ||
| 162 | planGuid: row.planGuid, | ||
| 163 | planExecGuid: row.planExecGuid | ||
| 164 | } | ||
| 165 | }); | ||
| 166 | } else { | ||
| 167 | router.push({ | ||
| 168 | name: 'assessDetail', | ||
| 169 | query: { | ||
| 170 | name: planName, | ||
| 171 | planGuid: row.planGuid, | ||
| 172 | planExecGuid: row.planExecGuid | ||
| 173 | } | ||
| 174 | }); | ||
| 175 | } | ||
| 176 | |||
| 177 | } else if (type == 'log') { | ||
| 178 | const params = { | ||
| 179 | logGuid: row.guid | ||
| 180 | } | ||
| 181 | drawerInfo.value.visible = true | ||
| 182 | // formTable.value.tableInfo.loading = true; | ||
| 183 | // getLogDetail(params).then((res: any) => { | ||
| 184 | // formTable.value.tableInfo.loading = false; | ||
| 185 | // if (res.code == proxy.$passCode && res.data) { | ||
| 186 | // const data = res.data | ||
| 187 | // formTable.value.tableInfo.data = data | ||
| 188 | // drawerInfo.value.container.contents[0].listInfo.data = data | ||
| 189 | // drawerInfo.value.visible = true | ||
| 190 | // } else { | ||
| 191 | // ElMessage({ | ||
| 192 | // type: "info", | ||
| 193 | // message: res.msg, | ||
| 194 | // }); | ||
| 195 | // } | ||
| 196 | // }) | ||
| 197 | } | ||
| 198 | }; | ||
| 199 | |||
| 200 | const drawerBtnClick = (btn) => { | ||
| 201 | drawerInfo.value.visible = false; | ||
| 202 | }; | ||
| 203 | |||
| 204 | onBeforeMount(() => { | ||
| 205 | getTableData(); | ||
| 206 | }); | ||
| 207 | |||
| 208 | </script> | ||
| 209 | |||
| 210 | <template> | ||
| 211 | <div class="container_wrap"> | ||
| 212 | <div class="table_panel_wrap"> | ||
| 213 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tablePageChange="tablePageChange" /> | ||
| 214 | </div> | ||
| 215 | |||
| 216 | <Drawer :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick" /> | ||
| 217 | </div> | ||
| 218 | </template> | ||
| 219 | |||
| 220 | <style lang="scss" scoped> | ||
| 221 | .container_wrap { | ||
| 222 | padding: 0; | ||
| 223 | |||
| 224 | .table_panel_wrap { | ||
| 225 | height: 100%; | ||
| 226 | padding: 16px 16px 0; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | :deep(.el-drawer) { | ||
| 231 | .drawer_panel { | ||
| 232 | height: 100%; | ||
| 233 | |||
| 234 | .table_panel_wrap { | ||
| 235 | height: 100%; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | } | ||
| 239 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/assessTemplate.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: assessTemplate | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="assessTemplate"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { useRouter } from "vue-router"; | ||
| 8 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 9 | import Form from "@/components/Form/index.vue"; | ||
| 10 | import StepBar from "@/components/StepBar/index.vue"; | ||
| 11 | import Table from "@/components/Table/index.vue"; | ||
| 12 | import TreeTransferChecked from "@/components/TreeTransferChecked/index.vue"; | ||
| 13 | import TreeTransfer from "@/components/TreeTransfer/index.vue"; | ||
| 14 | import filtersSettingBatch from "./filtersSettingBatch.vue"; | ||
| 15 | import { | ||
| 16 | getModelCountList, | ||
| 17 | checkPlanExist, | ||
| 18 | getValidGroup, | ||
| 19 | getModelDbGp, | ||
| 20 | getModelRuleCount, | ||
| 21 | getModelRules, | ||
| 22 | saveQualityPlan, | ||
| 23 | updateQualityPlan, | ||
| 24 | getAssessPlanDetail, | ||
| 25 | getPlanFilterDetail | ||
| 26 | } from '@/api/modules/dataQualityAssess'; | ||
| 27 | import { | ||
| 28 | getQualityTreeData, | ||
| 29 | getQualityTreeDataByDs | ||
| 30 | } from '@/api/modules/dataQuality'; | ||
| 31 | import { | ||
| 32 | getCronExecTime | ||
| 33 | } from '@/api/modules/queryService'; | ||
| 34 | import { changeNum } from '@/utils/common'; | ||
| 35 | import useUserStore from "@/store/modules/user"; | ||
| 36 | import useDataQualityStore from "@/store/modules/dataQuality"; | ||
| 37 | import { cloneDeep } from "lodash-es"; | ||
| 38 | import { TableColumnWidth } from '@/utils/enum'; | ||
| 39 | |||
| 40 | const userStore = useUserStore(); | ||
| 41 | const dataQualityStore = useDataQualityStore(); | ||
| 42 | |||
| 43 | const { proxy } = getCurrentInstance() as any; | ||
| 44 | |||
| 45 | const router = useRouter(); | ||
| 46 | const route = useRoute(); | ||
| 47 | |||
| 48 | const fullPath = route.fullPath; | ||
| 49 | const planGuid = route.query.guid;//编辑方案时 | ||
| 50 | const isDetail = ref(route.query.detail != null);// 查看方案详情 | ||
| 51 | const modelGuid = ref(route.query.modelGuid); // 从质检表跳过来的评估方案,默认单表。 | ||
| 52 | const groupGuid = ref(route.query.groupGuid); // 从质检表跳过来的评估方案,懒加载需要展开groupGuid | ||
| 53 | |||
| 54 | const step = ref(0); | ||
| 55 | const stepsInfo = ref({ | ||
| 56 | step: step.value, | ||
| 57 | list: [ | ||
| 58 | { title: '选择模型', value: 1 }, | ||
| 59 | { title: '任务调度', value: 2 } | ||
| 60 | ] | ||
| 61 | }) | ||
| 62 | |||
| 63 | const ruleModelTableInfo = ref({ | ||
| 64 | id: 'rule-model-table', | ||
| 65 | loading: false, | ||
| 66 | minHeight: '200px', | ||
| 67 | nodeKey: 'modelGuid', | ||
| 68 | fields: [ | ||
| 69 | { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" }, | ||
| 70 | { label: "表名", field: "modelName", width: 140, type: 'text_btn', value: 'detailView', columClass: 'text_btn' }, | ||
| 71 | { label: "分组名称", field: "modelGroupName", width: 150 }, | ||
| 72 | { label: "数据源", field: "dataSourceName", width: 140 }, | ||
| 73 | { label: "规则数量", field: "ruleCount", width: 140, align: 'right' }, | ||
| 74 | ], | ||
| 75 | data: [], | ||
| 76 | showPage: false, | ||
| 77 | actionInfo: { | ||
| 78 | label: "操作", | ||
| 79 | type: "btn", | ||
| 80 | width: 100, | ||
| 81 | fixed: 'right', | ||
| 82 | btns: isDetail.value ? [{ label: "规则", value: "ruleEdit", visible: true }] : [ | ||
| 83 | { label: "规则", value: "ruleEdit", visible: true }, | ||
| 84 | { label: "删除", value: "delete", visible: true }, | ||
| 85 | ], | ||
| 86 | } | ||
| 87 | }) | ||
| 88 | |||
| 89 | /** 规则设置对话框。 */ | ||
| 90 | const modelRulesTableRef = ref(); | ||
| 91 | |||
| 92 | /** 记录模型guid对应的guid, */ | ||
| 93 | const modelRulesJson: any = ref({}); | ||
| 94 | |||
| 95 | const currTableData: any = ref({}); | ||
| 96 | |||
| 97 | const tableBtnClick = (scope, btn) => { | ||
| 98 | const type = btn.value; | ||
| 99 | const row = scope.row; | ||
| 100 | currTableData.value = row; | ||
| 101 | if (type == "ruleEdit") { | ||
| 102 | ruleSettingDialogVisible.value = true; | ||
| 103 | if (modelRulesJson.value[row.modelGuid]) { | ||
| 104 | modelRulesTableInfo.value.data = modelRulesJson.value[row.modelGuid]; | ||
| 105 | nextTick(() => { | ||
| 106 | let confs: any[] = []; | ||
| 107 | currTableData.value.modelRuleConfGuids?.forEach((guid) => { | ||
| 108 | let index = modelRulesTableInfo.value.data.findIndex(d => d.guid == guid); | ||
| 109 | if (index > -1) { | ||
| 110 | let row = modelRulesTableInfo.value.data[index]; | ||
| 111 | confs.push(row); | ||
| 112 | modelRulesTableRef.value.tableRef.toggleRowSelection(row, true); | ||
| 113 | } | ||
| 114 | }) | ||
| 115 | currTableData.value.modelRuleConfs = confs; | ||
| 116 | }); | ||
| 117 | } else { | ||
| 118 | getModelRulesDetail(row.modelGuid).then(() => { | ||
| 119 | nextTick(() => { | ||
| 120 | let confs: any[] = []; | ||
| 121 | currTableData.value.modelRuleConfGuids?.forEach((guid) => { | ||
| 122 | let index = modelRulesTableInfo.value.data.findIndex(d => d.guid == guid); | ||
| 123 | if (index > -1) { | ||
| 124 | let row = modelRulesTableInfo.value.data[index]; | ||
| 125 | confs.push(row); | ||
| 126 | modelRulesTableRef.value.tableRef.toggleRowSelection(row, true); | ||
| 127 | } | ||
| 128 | }) | ||
| 129 | currTableData.value.modelRuleConfs = confs; | ||
| 130 | }) | ||
| 131 | }) | ||
| 132 | } | ||
| 133 | } else if (type == "delete") { | ||
| 134 | ElMessageBox.confirm("此操作将永久删除,是否继续?", "提示", { | ||
| 135 | confirmButtonText: "确定", | ||
| 136 | cancelButtonText: "取消", | ||
| 137 | type: type, | ||
| 138 | }).then(() => { | ||
| 139 | // 只是内存删除。删除时也需要同步更新规则数量。 | ||
| 140 | ruleModelTableInfo.value.data.splice(scope.$index, 1); | ||
| 141 | let v = modelFormRef.value?.formInline; | ||
| 142 | if (v.planType === 1) { | ||
| 143 | modelFormItems.value[2].default = ''; | ||
| 144 | } else if (v.planType === 3) {//分组 | ||
| 145 | let index = selectModelsByGroup.value.findIndex(s => s.guid == row.modelGuid); | ||
| 146 | selectModelsByGroup.value.splice(index, 1); | ||
| 147 | modelFormItems.value[3].default = selectModelsByGroup.value; | ||
| 148 | } else if (v.planType == 2) { | ||
| 149 | let index = selectModels.value.findIndex(s => s.guid == row.modelGuid); | ||
| 150 | selectModels.value.splice(index, 1); | ||
| 151 | modelFormItems.value[4].default = selectModels.value; | ||
| 152 | } | ||
| 153 | // http://test.csylcloud.com:8380/browse/WZL-511 修改了modelFormItems.value就需要更新form值,否则会导致上次修改的值消失。 | ||
| 154 | let formInline = modelFormRef.value?.formInline || {}; | ||
| 155 | let item1 = modelFormItems.value[0]; | ||
| 156 | item1.default = formInline[item1.field]; | ||
| 157 | let item2 = modelFormItems.value[1]; | ||
| 158 | item2.default = formInline[item2.field]; | ||
| 159 | let largeCategoryNum = row.largeCategoryNum || {}; | ||
| 160 | for (const large in largeCategoryNum) { | ||
| 161 | let rowWeightIndex = ruleWeightData.value.findIndex(w => w.largeCategory == large); | ||
| 162 | if (rowWeightIndex > -1) { | ||
| 163 | let rowWeight = ruleWeightData.value[rowWeightIndex]; | ||
| 164 | rowWeight.ruleCount = rowWeight.ruleCount - largeCategoryNum[large]; | ||
| 165 | if (rowWeight.ruleCount < 1) { | ||
| 166 | ruleWeightData.value.splice(rowWeightIndex, 1); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | //http://test.csylcloud.com:8380/browse/WZL-529 | ||
| 171 | if (ruleWeightData.value.length == 1) {//只剩一条 | ||
| 172 | ruleWeightData.value[0].ruleLargeWeight = changeNum(100, 2, true); | ||
| 173 | } | ||
| 174 | }).catch(() => { | ||
| 175 | ElMessage({ | ||
| 176 | type: "info", | ||
| 177 | message: "已取消删除", | ||
| 178 | }); | ||
| 179 | }); | ||
| 180 | } else if (type == 'detailView') { | ||
| 181 | router.push({ | ||
| 182 | name: 'qualityRules' | ||
| 183 | }); | ||
| 184 | dataQualityStore.setModelGuid(row.modelGuid); | ||
| 185 | } | ||
| 186 | }; | ||
| 187 | |||
| 188 | /** 选择的所有质检表对应的规则大类数量及权重占比。 */ | ||
| 189 | const ruleWeightData: any = ref([]); | ||
| 190 | const ruleWeightDataLoading = ref(false); | ||
| 191 | |||
| 192 | const inputWeightChange = (val, row) => { | ||
| 193 | let strArr = val.split("."); | ||
| 194 | if (strArr.length > 1) { | ||
| 195 | let right = strArr[1]; | ||
| 196 | if (right === "" || right.length < 2) { | ||
| 197 | row.ruleLargeWeight = val = parseFloat(val || 0).toFixed(2); | ||
| 198 | } | ||
| 199 | } else { | ||
| 200 | row.ruleLargeWeight = val = parseFloat(val || 0).toFixed(2); | ||
| 201 | } | ||
| 202 | }; | ||
| 203 | |||
| 204 | /** 输入框输入触发事件 */ | ||
| 205 | const inputEventWeightChange = (val, row) => { | ||
| 206 | row.ruleLargeWeight = row.ruleLargeWeight.toString().replace(/[^\d.]/g, "") | ||
| 207 | row.ruleLargeWeight = row.ruleLargeWeight.toString().replace(/\.{2,}/g, ".") | ||
| 208 | row.ruleLargeWeight = row.ruleLargeWeight.toString().replace(/^\D*(\d{0,3}(?:\.\d{0,2})?).*$/g, "$1") | ||
| 209 | if (row.ruleLargeWeight > 100) { | ||
| 210 | row.ruleLargeWeight = changeNum(100, 2, true); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | /** 类型是表时,需要显示第一级为分组,第二级为表。 */ | ||
| 215 | const qualityGroups: any = ref([]); | ||
| 216 | |||
| 217 | const getDataSourceListData = () => { | ||
| 218 | return getModelDbGp().then((res: any) => { | ||
| 219 | if (res.code == proxy.$passCode) { | ||
| 220 | const data = res.data || []; | ||
| 221 | databaseList.value = data; | ||
| 222 | console.log(data); | ||
| 223 | } else { | ||
| 224 | ElMessage.error(res.msg); | ||
| 225 | } | ||
| 226 | }) | ||
| 227 | } | ||
| 228 | |||
| 229 | const planDetailInfo: any = ref({}); | ||
| 230 | |||
| 231 | /** 全屏加载状态,保存时显示。 */ | ||
| 232 | const fullscreenLoading = ref(false); | ||
| 233 | |||
| 234 | const getValidGroupPromise: any = ref(null); | ||
| 235 | |||
| 236 | const getPlanFilterDetailPromise: any = ref(null); | ||
| 237 | |||
| 238 | /** 记录是否是从分组跳转,若是,则需要默认值是该分组下所有模型表。 */ | ||
| 239 | const initDefaultGroup = ref(false); | ||
| 240 | |||
| 241 | onBeforeMount(() => { | ||
| 242 | getValidGroupPromise.value = getValidGroup().then((res: any) => { | ||
| 243 | getValidGroupPromise.value = null; | ||
| 244 | if (res.code == proxy.$passCode) { | ||
| 245 | const data = qualityGroups.value = res.data || []; | ||
| 246 | return data; | ||
| 247 | } else { | ||
| 248 | ElMessage.error(res.msg); | ||
| 249 | return []; | ||
| 250 | } | ||
| 251 | }) | ||
| 252 | if (planGuid) { | ||
| 253 | fullscreenLoading.value = true; | ||
| 254 | getAssessPlanDetail(planGuid).then((res: any) => { | ||
| 255 | if (res.code == proxy.$passCode) { | ||
| 256 | planDetailInfo.value = res.data || {}; | ||
| 257 | switchInfo.value = planDetailInfo.value; | ||
| 258 | getPlanFilterDetailPromise.value = getPlanFilterDetail(planGuid).then((res: any) => { | ||
| 259 | getPlanFilterDetailPromise.value = null; | ||
| 260 | fullscreenLoading.value = false; | ||
| 261 | if (res.code == proxy.$passCode) { | ||
| 262 | console.log(res.data); | ||
| 263 | let data = res.data || []; | ||
| 264 | data.forEach(d => { | ||
| 265 | let tables: any = []; | ||
| 266 | for (const id in d.qualityModelInfo) { | ||
| 267 | tables.push({ | ||
| 268 | guid: id, | ||
| 269 | subjectGuid: planDetailInfo.value.modelCount?.find(m => m.modelGuid == id)?.subjectGuid, | ||
| 270 | name: d.qualityModelInfo[id] | ||
| 271 | }); | ||
| 272 | } | ||
| 273 | batchFiltersValue.value[d.dataFilterGroup] = { | ||
| 274 | filter: d.dataRange, | ||
| 275 | tables: tables, | ||
| 276 | checked: true | ||
| 277 | } | ||
| 278 | }); | ||
| 279 | } else { | ||
| 280 | ElMessage.error(res.msg); | ||
| 281 | } | ||
| 282 | }); | ||
| 283 | if (planDetailInfo.value.planType == 1) { | ||
| 284 | planDetailInfo.value.qualityModelGuid = planDetailInfo.value.modelCount?.[0]?.modelGuid; | ||
| 285 | modelFormItems.value[2].expandKeys = [planDetailInfo.value.modelCount?.[0]?.modelGroupGuid]; | ||
| 286 | } | ||
| 287 | setModelFormItems(planDetailInfo.value); | ||
| 288 | checkedInfo.value[planDetailInfo.value.planName] = true; | ||
| 289 | ruleModelTableInfo.value.data = planDetailInfo.value.modelCount || []; | ||
| 290 | ruleWeightData.value = planDetailInfo.value.modelRuleWeights?.map(r => { | ||
| 291 | r.ruleLargeWeight = r.ruleLargeWeight.toFixed(2); | ||
| 292 | return r; | ||
| 293 | }) || []; | ||
| 294 | if (planDetailInfo.value.planType == 3) {//分组 | ||
| 295 | dsSelectGroup.value = planDetailInfo.value.modelCount?.[0]?.modelGroupGuid; | ||
| 296 | modelFormItems.value[3].default = selectModelsByGroup.value = planDetailInfo.value.modelCount?.map((m: any) => { | ||
| 297 | m.guid = m.modelGuid; | ||
| 298 | m.name = m.modelName; | ||
| 299 | return m; | ||
| 300 | }) || []; | ||
| 301 | } else if (planDetailInfo.value.planType == 2) {//数据库 | ||
| 302 | modelFormItems.value[4].default = selectModels.value = planDetailInfo.value.modelCount?.map(m => { | ||
| 303 | m.name = m.modelName; | ||
| 304 | m.guid = m.modelGuid; | ||
| 305 | return m; | ||
| 306 | }) || []; | ||
| 307 | databaseInfo.value = planDetailInfo.value.modelCount?.[0]?.dataSourceGuid; | ||
| 308 | } | ||
| 309 | setFormItems(planDetailInfo.value, true); | ||
| 310 | } else { | ||
| 311 | fullscreenLoading.value = false; | ||
| 312 | ElMessage.error(res.msg); | ||
| 313 | } | ||
| 314 | }); | ||
| 315 | } else if (modelGuid.value) { | ||
| 316 | setModelFormItems({ planType: 1, qualityModelGuid: modelGuid.value }); | ||
| 317 | modelFormItems.value[2].expandKeys = [groupGuid.value]; | ||
| 318 | getModelCountListData([modelGuid.value]); | ||
| 319 | getModelRuleCountListData([modelGuid.value]); | ||
| 320 | } else if (groupGuid.value && !modelGuid.value) { | ||
| 321 | setModelFormItems({ planType: 3 }); | ||
| 322 | dsSelectGroup.value = <string>groupGuid.value; | ||
| 323 | initDefaultGroup.value = true; | ||
| 324 | } else if (dataQualityStore.defaultPlanType) { | ||
| 325 | setModelFormItems({ planType: dataQualityStore.defaultPlanType }); | ||
| 326 | dataQualityStore.setDefaultPlanType(null); | ||
| 327 | } | ||
| 328 | }); | ||
| 329 | |||
| 330 | onActivated(() => { | ||
| 331 | if (modelGuid.value || groupGuid.value) { | ||
| 332 | return; | ||
| 333 | } | ||
| 334 | if (dataQualityStore.defaultPlanType) { | ||
| 335 | setModelFormItems({ planType: dataQualityStore.defaultPlanType }); | ||
| 336 | dataQualityStore.setDefaultPlanType(null); | ||
| 337 | } | ||
| 338 | }); | ||
| 339 | |||
| 340 | const modelFormItems: any = ref([ | ||
| 341 | { | ||
| 342 | label: '质量方案名称', | ||
| 343 | type: 'input', | ||
| 344 | maxlength: 50, | ||
| 345 | placeholder: '请输入', | ||
| 346 | field: 'planName', | ||
| 347 | default: '', | ||
| 348 | clearable: true, | ||
| 349 | required: true | ||
| 350 | }, { | ||
| 351 | label: '方案类型', | ||
| 352 | type: 'select', | ||
| 353 | placeholder: '请选择', | ||
| 354 | field: 'planType', | ||
| 355 | default: 1, | ||
| 356 | options: [ | ||
| 357 | { | ||
| 358 | label: '表', | ||
| 359 | value: 1 | ||
| 360 | }, { | ||
| 361 | label: '分组', | ||
| 362 | value: 3 | ||
| 363 | }, { | ||
| 364 | label: '数据库', | ||
| 365 | value: 2 | ||
| 366 | } | ||
| 367 | ], | ||
| 368 | required: true, | ||
| 369 | visible: true | ||
| 370 | }, { | ||
| 371 | label: '选择质量规则集', | ||
| 372 | type: 'tree-select', | ||
| 373 | placeholder: '请选择', | ||
| 374 | field: 'qualityModelGuid', | ||
| 375 | default: '', | ||
| 376 | checkStrictly: false, | ||
| 377 | options: [], | ||
| 378 | props: { | ||
| 379 | label: 'name', | ||
| 380 | value: 'guid', | ||
| 381 | children: 'children', | ||
| 382 | isLeaf: 'isLeaf' | ||
| 383 | }, | ||
| 384 | lazy: true, | ||
| 385 | expandKeys: [], | ||
| 386 | clearable: true, | ||
| 387 | required: true, | ||
| 388 | filterable: true, | ||
| 389 | visible: true | ||
| 390 | }, { | ||
| 391 | label: '选择质量规则集', | ||
| 392 | visible: false, | ||
| 393 | type: 'addBtn', | ||
| 394 | placeholder: '选择表', | ||
| 395 | field: 'qualityModelGuidsByGroup', | ||
| 396 | default: [], | ||
| 397 | required: true, | ||
| 398 | }, { | ||
| 399 | label: '选择质量规则集', | ||
| 400 | visible: false, | ||
| 401 | type: 'addBtn', | ||
| 402 | placeholder: '选择表', | ||
| 403 | field: 'qualityModelGuids', | ||
| 404 | default: [], | ||
| 405 | required: true, | ||
| 406 | } | ||
| 407 | ]) | ||
| 408 | |||
| 409 | const checkPlanName = async (params) => { | ||
| 410 | return new Promise((resolve, reject) => { | ||
| 411 | checkPlanExist(params).then((res: any) => { | ||
| 412 | if (res.code == proxy.$passCode) { | ||
| 413 | if (res.data) { | ||
| 414 | resolve('该名称已存在,请填写其他名称') | ||
| 415 | } else { | ||
| 416 | resolve('') | ||
| 417 | } | ||
| 418 | } else { | ||
| 419 | resolve(res.msg) | ||
| 420 | } | ||
| 421 | }).catch((xhr) => { | ||
| 422 | reject(xhr.msg) | ||
| 423 | }); | ||
| 424 | }) | ||
| 425 | } | ||
| 426 | |||
| 427 | /** 记录已校验过的信息。 */ | ||
| 428 | const checkedInfo: any = ref({}); | ||
| 429 | |||
| 430 | const modelFormRules = ref({ | ||
| 431 | planName: [ | ||
| 432 | { | ||
| 433 | trigger: 'change', validator: (rule: any, value: any, callback: any) => { | ||
| 434 | if (!value) { | ||
| 435 | callback(new Error("请填写质量方案名称")); | ||
| 436 | return; | ||
| 437 | } | ||
| 438 | let checkedInfoValue = checkedInfo.value[value]; | ||
| 439 | if (checkedInfoValue != null) { | ||
| 440 | if (checkedInfoValue) { | ||
| 441 | callback(); | ||
| 442 | } else { | ||
| 443 | callback(new Error(checkedInfoValue)); | ||
| 444 | } | ||
| 445 | return; | ||
| 446 | } | ||
| 447 | checkPlanName(value).then((res: any) => { | ||
| 448 | if (res) { | ||
| 449 | checkedInfo.value[value] = res; | ||
| 450 | callback(new Error(res)); | ||
| 451 | } else { | ||
| 452 | checkedInfo.value[value] = true; | ||
| 453 | callback(); | ||
| 454 | } | ||
| 455 | }).catch((xhr: any) => { | ||
| 456 | callback(new Error(xhr)); | ||
| 457 | }) | ||
| 458 | } | ||
| 459 | } | ||
| 460 | ], | ||
| 461 | qualityModelGuid: [{ | ||
| 462 | validator: (rule: any, value: any, callback: any) => { | ||
| 463 | let formInline = modelFormRef.value?.formInline; | ||
| 464 | if (formInline && formInline.planType == 1) { | ||
| 465 | if (!value?.length) { | ||
| 466 | callback(new Error("请选择质量规则集")); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | callback(); | ||
| 470 | }, | ||
| 471 | trigger: 'change' | ||
| 472 | }], | ||
| 473 | qualityModelGuidsByGroup: [{ | ||
| 474 | validator: (rule: any, value: any, callback: any) => { | ||
| 475 | let formInline = modelFormRef.value?.formInline; | ||
| 476 | if (formInline && formInline.planType == 3) { | ||
| 477 | if (!formInline.qualityModelGuidsByGroup?.length) { | ||
| 478 | callback(new Error("请选择质量规则集")); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | callback(); | ||
| 482 | } | ||
| 483 | }], | ||
| 484 | qualityModelGuids: [{ | ||
| 485 | validator: (rule: any, value: any, callback: any) => { | ||
| 486 | let formInline = modelFormRef.value?.formInline; | ||
| 487 | if (formInline && formInline.planType == 2) { | ||
| 488 | if (!formInline.qualityModelGuids?.length) { | ||
| 489 | callback(new Error("请选择质量规则集")); | ||
| 490 | } | ||
| 491 | } | ||
| 492 | callback(); | ||
| 493 | } | ||
| 494 | }], | ||
| 495 | }) | ||
| 496 | |||
| 497 | const cancelPlan = () => { | ||
| 498 | if (isDetail.value) { | ||
| 499 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 500 | toRouterPath('/data-quality/quality-assess'); | ||
| 501 | return; | ||
| 502 | } | ||
| 503 | ElMessageBox.confirm( | ||
| 504 | "当前页面尚未保存,确定放弃修改吗?", | ||
| 505 | "提示", | ||
| 506 | { | ||
| 507 | confirmButtonText: "确定", | ||
| 508 | cancelButtonText: "取消", | ||
| 509 | type: "warning", | ||
| 510 | } | ||
| 511 | ) | ||
| 512 | .then(() => { | ||
| 513 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 514 | toRouterPath('/data-quality/quality-assess'); | ||
| 515 | }) | ||
| 516 | .catch(() => { | ||
| 517 | ElMessage({ | ||
| 518 | type: "info", | ||
| 519 | message: "已取消", | ||
| 520 | }); | ||
| 521 | }); | ||
| 522 | } | ||
| 523 | |||
| 524 | const modelFormRef = ref(); | ||
| 525 | |||
| 526 | const changeStep = (val) => { | ||
| 527 | if (val == 2) { | ||
| 528 | modelFormRef.value?.ruleFormRef?.validate((valid) => { | ||
| 529 | if (valid) { | ||
| 530 | let sum = 0; | ||
| 531 | ruleWeightData.value.forEach(item => { | ||
| 532 | if (item.ruleLargeWeight != null) { | ||
| 533 | sum += parseFloat(item.ruleLargeWeight); | ||
| 534 | } | ||
| 535 | }); | ||
| 536 | if (!(sum <= 100.02 && sum >= 99.98)) {// 允许浮动0.01误差 | ||
| 537 | ElMessage.error('规则权重总分应为100分'); | ||
| 538 | return; | ||
| 539 | } | ||
| 540 | if (!planGuid) { | ||
| 541 | let formInline = modelFormRef.value?.formInline; | ||
| 542 | if (formInline) { | ||
| 543 | taskFormItems.value.find(t => t.field == 'analysisReportName').default = formInline.planName; | ||
| 544 | } | ||
| 545 | } | ||
| 546 | step.value = val - 1; | ||
| 547 | stepsInfo.value.step = val - 1; | ||
| 548 | } | ||
| 549 | }); | ||
| 550 | } else { | ||
| 551 | step.value = val - 1; | ||
| 552 | stepsInfo.value.step = val - 1; | ||
| 553 | } | ||
| 554 | }; | ||
| 555 | |||
| 556 | /** 第二步任务调度的表单。 */ | ||
| 557 | const taskFormItems: any = ref([ | ||
| 558 | { | ||
| 559 | label: '脏数据保留次数(次)', | ||
| 560 | type: 'input', | ||
| 561 | inputType: 'integerNumber', | ||
| 562 | placeholder: '请输入', | ||
| 563 | max: 1000, | ||
| 564 | field: 'dirtyDataNum', | ||
| 565 | default: 365, | ||
| 566 | min: 1, | ||
| 567 | clearable: true, | ||
| 568 | required: false, | ||
| 569 | }, | ||
| 570 | { | ||
| 571 | label: '是否创建调度', | ||
| 572 | type: 'switch', | ||
| 573 | field: 'isScheduler', | ||
| 574 | default: 'Y', | ||
| 575 | activeValue: 'Y', | ||
| 576 | inactiveValue: 'N' | ||
| 577 | }, | ||
| 578 | { | ||
| 579 | label: '定时', | ||
| 580 | type: 'input-popover-panel', | ||
| 581 | placeholder: '', | ||
| 582 | field: 'execCycle', | ||
| 583 | teleported: true, | ||
| 584 | col: 'col2', | ||
| 585 | append: { | ||
| 586 | btn: { label: '执行时间', value: 'cron' } | ||
| 587 | }, | ||
| 588 | clearable: true, | ||
| 589 | required: true, | ||
| 590 | visible: true | ||
| 591 | }, | ||
| 592 | { | ||
| 593 | label: '接下来五次执行时间', | ||
| 594 | type: 'textarea', | ||
| 595 | placeholder: '执行时间', | ||
| 596 | field: 'zxzq', | ||
| 597 | clearable: true, | ||
| 598 | required: false, | ||
| 599 | block: true, | ||
| 600 | class: 'execute-time', | ||
| 601 | rows: 5, | ||
| 602 | visible: true, | ||
| 603 | readonly: true | ||
| 604 | }, | ||
| 605 | { | ||
| 606 | label: '是否创建分析报告', | ||
| 607 | type: 'switch', | ||
| 608 | field: 'isCreateReport', | ||
| 609 | default: 'N', | ||
| 610 | activeValue: 'Y', | ||
| 611 | block: true, | ||
| 612 | inactiveValue: 'N' | ||
| 613 | }, | ||
| 614 | { | ||
| 615 | label: '分析报告名称', | ||
| 616 | type: 'input', | ||
| 617 | maxlength: 50, | ||
| 618 | placeholder: '请输入', | ||
| 619 | field: 'analysisReportName', | ||
| 620 | default: '', | ||
| 621 | clearable: true, | ||
| 622 | visible: false, | ||
| 623 | required: true, | ||
| 624 | }, | ||
| 625 | { | ||
| 626 | label: '报告保留期数(期)', | ||
| 627 | type: 'input', | ||
| 628 | inputType: 'integerNumber', | ||
| 629 | placeholder: '请输入', | ||
| 630 | field: 'reportReserveCycle', | ||
| 631 | default: 365, | ||
| 632 | max: 1000, | ||
| 633 | min: 1, | ||
| 634 | clearable: true, | ||
| 635 | required: false, | ||
| 636 | visible: false | ||
| 637 | }, | ||
| 638 | ]) | ||
| 639 | |||
| 640 | const taskFormRules = ref({ | ||
| 641 | analysisReportName: [{ | ||
| 642 | required: true, | ||
| 643 | message: '请填写报告名称', | ||
| 644 | trigger: 'blur' | ||
| 645 | }] | ||
| 646 | }); | ||
| 647 | |||
| 648 | /** 任务调度表单组件ref。 */ | ||
| 649 | const taskFormRef = ref(); | ||
| 650 | |||
| 651 | const switchInfo: any = ref({}); | ||
| 652 | |||
| 653 | /** 是否创建任务调度。 */ | ||
| 654 | const handleTaskCreateChange = (val, formInfo, item) => { | ||
| 655 | if (item.field == 'isCreateReport') { | ||
| 656 | if (val == 'Y') { | ||
| 657 | formInfo.analysisReportName = formInfo.analysisReportName ?? switchInfo.value.analysisReportName; | ||
| 658 | formInfo.reportReserveCycle = formInfo.reportReserveCycle ?? switchInfo.value.reportReserveCycle; | ||
| 659 | } | ||
| 660 | } | ||
| 661 | else if (item.field == 'isScheduler') { | ||
| 662 | if (val == 'Y') { | ||
| 663 | formInfo.execCycle = formInfo.execCycle ?? switchInfo.value.execCycle; | ||
| 664 | } | ||
| 665 | } | ||
| 666 | setFormItems(formInfo); | ||
| 667 | } | ||
| 668 | |||
| 669 | const scheduleChange = (val, rowValue) => { | ||
| 670 | setFormItems(rowValue); | ||
| 671 | } | ||
| 672 | |||
| 673 | const taskFormBtnClick = (btn, type) => { | ||
| 674 | if (btn.value = 'cron') { | ||
| 675 | let vInfo = taskFormRef.value.formInline; | ||
| 676 | if (!vInfo.execCycle) { | ||
| 677 | return; | ||
| 678 | } | ||
| 679 | getCronExecTime(vInfo.execCycle).then((res: any) => { | ||
| 680 | if (res?.length) { | ||
| 681 | vInfo.zxzq = res.join('\n'); | ||
| 682 | setFormItems(vInfo); | ||
| 683 | } else { | ||
| 684 | ElMessage({ | ||
| 685 | type: 'error', | ||
| 686 | message: res.msg, | ||
| 687 | }) | ||
| 688 | } | ||
| 689 | }) | ||
| 690 | } | ||
| 691 | } | ||
| 692 | |||
| 693 | const setFormItems = (row, isDetail = false) => { | ||
| 694 | switchInfo.value = { | ||
| 695 | analysisReportName: row.analysisReportName, | ||
| 696 | reportReserveCycle: isDetail && row.isCreateReport == 'N' && row.reportReserveCycle == null ? 365 : row.reportReserveCycle, | ||
| 697 | execCycle: row.execCycle | ||
| 698 | }; | ||
| 699 | taskFormItems.value.map(item => { | ||
| 700 | if (item.field == 'isScheduler') { | ||
| 701 | taskFormItems.value[1].default = row.isScheduler; | ||
| 702 | if (row.isScheduler == 'Y') { | ||
| 703 | taskFormItems.value[2].visible = true; | ||
| 704 | taskFormItems.value[3].visible = true; | ||
| 705 | } else { | ||
| 706 | taskFormItems.value[2].visible = false; | ||
| 707 | taskFormItems.value[3].visible = false; | ||
| 708 | } | ||
| 709 | } else if (item.field == 'isCreateReport') { | ||
| 710 | taskFormItems.value.at(-3).default = row.isCreateReport; | ||
| 711 | if (row.isCreateReport == 'Y') { | ||
| 712 | taskFormItems.value.at(-1).visible = true; | ||
| 713 | taskFormItems.value.at(-2).visible = true; | ||
| 714 | } else { | ||
| 715 | taskFormItems.value.at(-1).visible = false; | ||
| 716 | taskFormItems.value.at(-2).visible = false; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | else if (item.field == 'reportReserveCycle' || item.field == 'dirtyDataNum') { | ||
| 720 | item.default = row[item.field] === undefined ? 365 : row[item.field]; | ||
| 721 | } else if (item.field == 'analysisReportName') { | ||
| 722 | let formInline = modelFormRef.value?.formInline; | ||
| 723 | if (formInline) { | ||
| 724 | item.default = row[item.field] ?? formInline.planName; | ||
| 725 | } | ||
| 726 | } else { | ||
| 727 | item.default = row ? row[item.field] : '' | ||
| 728 | } | ||
| 729 | }) | ||
| 730 | } | ||
| 731 | |||
| 732 | const toRouterPath = (url) => { | ||
| 733 | router.push({ | ||
| 734 | path: url, | ||
| 735 | }); | ||
| 736 | }; | ||
| 737 | |||
| 738 | const save = () => { | ||
| 739 | taskFormRef.value?.ruleFormRef?.validate((valid) => { | ||
| 740 | if (valid) { | ||
| 741 | let modelValue = modelFormRef.value.formInline; | ||
| 742 | let models: any = []; | ||
| 743 | let ruleConfs: any = []; | ||
| 744 | let transferFilters: any = []; | ||
| 745 | for (const key in batchFiltersValue.value) { | ||
| 746 | let info = batchFiltersValue.value[key]; | ||
| 747 | if (info.tables.length) { | ||
| 748 | info.tables.forEach(t => { | ||
| 749 | transferFilters.push(Object.assign({}, t, { | ||
| 750 | filter: info.filter, | ||
| 751 | dataFilterGroup: key | ||
| 752 | })); | ||
| 753 | }); | ||
| 754 | } | ||
| 755 | } | ||
| 756 | ruleModelTableInfo.value.data.forEach((d: any) => { | ||
| 757 | let filterInfo = transferFilters.find(b => b.guid == d.modelGuid); | ||
| 758 | models.push({ | ||
| 759 | qualityModelGuid: d.modelGuid, | ||
| 760 | subjectGuid: d.subjectGuid, | ||
| 761 | dataRange: filterInfo?.filter, | ||
| 762 | dataFilterGroup: filterInfo?.dataFilterGroup, | ||
| 763 | }); | ||
| 764 | ruleConfs.push({ | ||
| 765 | qualityModelGuid: d.modelGuid, | ||
| 766 | ruleConfGuid: d.modelRuleConfGuids || [], | ||
| 767 | dataSourceGuid: d.dataSourceGuid, | ||
| 768 | subjectGuid: d.subjectGuid | ||
| 769 | }) | ||
| 770 | }) | ||
| 771 | let params: any = Object.assign({}, taskFormRef.value.formInline, { | ||
| 772 | planName: modelValue.planName, | ||
| 773 | planType: modelValue.planType, | ||
| 774 | state: 1, | ||
| 775 | models: models, | ||
| 776 | ruleConfs: ruleConfs, | ||
| 777 | ruleWeights: ruleWeightData.value, | ||
| 778 | }); | ||
| 779 | if (!params.reportReserveCycle && params.reportReserveCycle !== 0) { | ||
| 780 | params.reportReserveCycle = null; | ||
| 781 | } | ||
| 782 | if (!params.dirtyDataNum && params.dirtyDataNum !== 0) { | ||
| 783 | params.dirtyDataNum = null; | ||
| 784 | } | ||
| 785 | if (planGuid) { | ||
| 786 | params.guid = planGuid; | ||
| 787 | params.state = 0; | ||
| 788 | fullscreenLoading.value = true; | ||
| 789 | updateQualityPlan(params).then((res: any) => { | ||
| 790 | fullscreenLoading.value = false; | ||
| 791 | if (res.code == proxy.$passCode) { | ||
| 792 | ElMessage.success('方案更新成功'); | ||
| 793 | router.push({ | ||
| 794 | name: 'qualityAssess' | ||
| 795 | }); | ||
| 796 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 797 | dataQualityStore.setPlanType(modelValue.planType); | ||
| 798 | dataQualityStore.setIsUpdate(true); | ||
| 799 | } else { | ||
| 800 | ElMessage.error(res.msg); | ||
| 801 | } | ||
| 802 | }) | ||
| 803 | } else { | ||
| 804 | fullscreenLoading.value = true; | ||
| 805 | saveQualityPlan(params).then((res: any) => { | ||
| 806 | fullscreenLoading.value = false; | ||
| 807 | if (res.code == proxy.$passCode) { | ||
| 808 | ElMessage.success('新建质量评估方案保存成功'); | ||
| 809 | router.push({ | ||
| 810 | name: 'qualityAssess' | ||
| 811 | }); | ||
| 812 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 813 | dataQualityStore.setPlanType(modelValue.planType); | ||
| 814 | } else { | ||
| 815 | ElMessage.error(res.msg); | ||
| 816 | } | ||
| 817 | }) | ||
| 818 | } | ||
| 819 | } | ||
| 820 | }); | ||
| 821 | } | ||
| 822 | |||
| 823 | /** 根据所选择的模型表,获取对应的规则数量信息。 */ | ||
| 824 | const getModelCountListData = (modelGuids) => { | ||
| 825 | ruleModelTableInfo.value.loading = true; | ||
| 826 | getModelCountList({ modelGuids: modelGuids }).then((res: any) => { | ||
| 827 | ruleModelTableInfo.value.loading = false; | ||
| 828 | if (res.code == proxy.$passCode) { | ||
| 829 | ruleModelTableInfo.value.data = res.data || []; | ||
| 830 | } else { | ||
| 831 | ElMessage.error(res.msg); | ||
| 832 | } | ||
| 833 | }); | ||
| 834 | } | ||
| 835 | |||
| 836 | /** 根据所选择的模型表,获取对应的权重信息。 */ | ||
| 837 | const getModelRuleCountListData = (modelGuids) => { | ||
| 838 | ruleWeightDataLoading.value = true; | ||
| 839 | getModelRuleCount({ modelGuids: modelGuids }).then((res: any) => { | ||
| 840 | ruleWeightDataLoading.value = false; | ||
| 841 | if (res.code == proxy.$passCode) { | ||
| 842 | ruleWeightData.value = res.data || []; | ||
| 843 | if (ruleWeightData.value.length) { | ||
| 844 | let v = changeNum(100 / ruleWeightData.value.length, 2, true); | ||
| 845 | ruleWeightData.value.forEach(d => d.ruleLargeWeight = v); | ||
| 846 | } | ||
| 847 | } else { | ||
| 848 | ElMessage.error(res.msg); | ||
| 849 | } | ||
| 850 | }); | ||
| 851 | } | ||
| 852 | |||
| 853 | /** 点击表格行中的规则按钮, 获取详细规则 */ | ||
| 854 | const getModelRulesDetail = (modelGuid) => { | ||
| 855 | modelRulesTableInfo.value.loading = true; | ||
| 856 | return getModelRules({ modelGuid: modelGuid, bizState: 'Y' }).then((res: any) => { | ||
| 857 | modelRulesTableInfo.value.loading = false; | ||
| 858 | if (res.code == proxy.$passCode) { | ||
| 859 | modelRulesTableInfo.value.data = res.data || []; | ||
| 860 | modelRulesJson.value[modelGuid] = modelRulesTableInfo.value.data; | ||
| 861 | } else { | ||
| 862 | ElMessage.error(res.msg); | ||
| 863 | } | ||
| 864 | }) | ||
| 865 | } | ||
| 866 | |||
| 867 | const batchFilterDialogRef = ref(); | ||
| 868 | |||
| 869 | /** 批量配置过滤条件 */ | ||
| 870 | const handleFiltersAdd = () => { | ||
| 871 | if (!selectSubjectTables.value.length) { | ||
| 872 | ElMessage.warning('请先选择质量规则集'); | ||
| 873 | return; | ||
| 874 | } | ||
| 875 | batchFilterDialogRef.value?.openDialog(batchFiltersValue.value); | ||
| 876 | } | ||
| 877 | |||
| 878 | const setModelFormItems = (row) => { | ||
| 879 | let item1 = modelFormItems.value[0]; | ||
| 880 | item1.default = row[item1.field]; | ||
| 881 | let item2 = modelFormItems.value[1]; | ||
| 882 | item2.default = row[item2.field]; | ||
| 883 | if (item2.default === 1) {//表 | ||
| 884 | modelFormItems.value[2].visible = true; | ||
| 885 | modelFormItems.value[3].visible = false; | ||
| 886 | modelFormItems.value[4].visible = false; | ||
| 887 | } else if (item2.default === 3) {//分组 | ||
| 888 | modelFormItems.value[3].visible = true; | ||
| 889 | modelFormItems.value[2].visible = false; | ||
| 890 | modelFormItems.value[4].visible = false; | ||
| 891 | } else if (item2.default === 2) {//数据库 | ||
| 892 | modelFormItems.value[2].visible = false; | ||
| 893 | modelFormItems.value[3].visible = false; | ||
| 894 | modelFormItems.value[4].visible = true; | ||
| 895 | } | ||
| 896 | let itemModel = modelFormItems.value.at(-3); | ||
| 897 | if (itemModel) { | ||
| 898 | itemModel.default = row[itemModel.field] | ||
| 899 | } | ||
| 900 | } | ||
| 901 | |||
| 902 | const selectChange = (val, item, row) => { | ||
| 903 | if (item.field === 'planType') { | ||
| 904 | //修改类型,则清空过滤条件 | ||
| 905 | batchFiltersValue.value = {}; | ||
| 906 | ruleModelTableInfo.value.data = []; | ||
| 907 | ruleWeightData.value = []; | ||
| 908 | if (val == 3) { | ||
| 909 | selectModels.value = []; | ||
| 910 | modelFormItems.value[3].default = []; | ||
| 911 | } else if (val == 2) { | ||
| 912 | selectModelsByGroup.value = []; | ||
| 913 | modelFormItems.value[4].default = []; | ||
| 914 | } else { | ||
| 915 | selectModelsByGroup.value = []; | ||
| 916 | selectModels.value = []; | ||
| 917 | } | ||
| 918 | } else if (item.field == 'qualityModelGuid') { | ||
| 919 | getModelCountListData([val]); | ||
| 920 | getModelRuleCountListData([val]); | ||
| 921 | } | ||
| 922 | setModelFormItems(row); | ||
| 923 | }; | ||
| 924 | |||
| 925 | /** 分组+表结构懒加载。 */ | ||
| 926 | const modelFormTreeSelectLoad = (node, resolve, item) => { | ||
| 927 | if (node.level === 0) { | ||
| 928 | if (getValidGroupPromise.value) { | ||
| 929 | getValidGroupPromise.value.then(res => { | ||
| 930 | resolve(res); | ||
| 931 | }) | ||
| 932 | } else { | ||
| 933 | resolve(qualityGroups.value); | ||
| 934 | } | ||
| 935 | return; | ||
| 936 | } else if (node.level == 1) { | ||
| 937 | getQualityTreeData(node.data.guid).then((res: any) => { | ||
| 938 | if (res.code == proxy.$passCode) { | ||
| 939 | const data = res.data?.map(d => { | ||
| 940 | d.parentGuid = node.data.guid; | ||
| 941 | d.groupName = node.data.name; | ||
| 942 | d.isLeaf = true; | ||
| 943 | return d; | ||
| 944 | }) || []; | ||
| 945 | resolve(data); | ||
| 946 | } else { | ||
| 947 | resolve([]); | ||
| 948 | ElMessage.error(res.msg); | ||
| 949 | } | ||
| 950 | }) | ||
| 951 | } | ||
| 952 | } | ||
| 953 | |||
| 954 | /** 方案类型为表时,选择的表json对象 */ | ||
| 955 | const treeSelectNode: any = ref(null); | ||
| 956 | |||
| 957 | const modelFormTreeSelectNodeChange = (node, item) => { | ||
| 958 | treeSelectNode.value = node; | ||
| 959 | } | ||
| 960 | |||
| 961 | const btnFormClick = (btn, type) => { | ||
| 962 | console.log(btn, type); | ||
| 963 | if (btn.field === "qualityModelGuids") { | ||
| 964 | let v: any = modelFormItems.value[4].default ?? []; | ||
| 965 | if (!v.length) { | ||
| 966 | modelsDialogVisible.value = true; | ||
| 967 | if (!databaseList.value?.length) { | ||
| 968 | getDataSourceListData().then(() => { | ||
| 969 | if (databaseInfo.value == databaseList.value[0]?.guid ?? "") { | ||
| 970 | dsFromTreeData.value = JSON.parse(JSON.stringify(currentDsFromTreeData.value)); | ||
| 971 | } else { | ||
| 972 | databaseInfo.value = databaseList.value[0]?.guid ?? ""; | ||
| 973 | } | ||
| 974 | }) | ||
| 975 | } else { | ||
| 976 | if (databaseInfo.value == databaseList.value[0]?.guid ?? "") { | ||
| 977 | dsFromTreeData.value = JSON.parse(JSON.stringify(currentDsFromTreeData.value)); | ||
| 978 | } else { | ||
| 979 | databaseInfo.value = databaseList.value[0]?.guid ?? ""; | ||
| 980 | } | ||
| 981 | } | ||
| 982 | dsToTreeData.value = []; | ||
| 983 | } else { | ||
| 984 | let p: any = []; | ||
| 985 | v.forEach(vc => { | ||
| 986 | let pd = p.find(p => p.modelGroupGuid == vc.modelGroupGuid); | ||
| 987 | if (pd) { | ||
| 988 | pd.children.push({ | ||
| 989 | guid: vc.modelGuid, | ||
| 990 | modelGuid: vc.modelGuid, | ||
| 991 | dataSourceGuid: vc.dataSourceGuid, | ||
| 992 | name: vc.modelName ?? vc.name, | ||
| 993 | isLeaf: true, | ||
| 994 | subjectGuid: vc.subjectGuid, | ||
| 995 | pid: vc.modelGroupGuid, | ||
| 996 | modelGroupName: vc.modelGroupName, | ||
| 997 | modelGroupGuid: vc.modelGroupGuid, | ||
| 998 | }); | ||
| 999 | } else { | ||
| 1000 | p.push({ | ||
| 1001 | guid: vc.modelGroupGuid, | ||
| 1002 | modelGroupGuid: vc.modelGroupGuid, | ||
| 1003 | modelGroupName: vc.modelGroupName, | ||
| 1004 | name: vc.modelGroupName, | ||
| 1005 | dataSourceGuid: vc.dataSourceGuid, | ||
| 1006 | pid: 0, | ||
| 1007 | children: [{ | ||
| 1008 | guid: vc.modelGuid, | ||
| 1009 | modelGuid: vc.modelGuid, | ||
| 1010 | name: vc.modelName ?? vc.name, | ||
| 1011 | isLeaf: true, | ||
| 1012 | subjectGuid: vc.subjectGuid, | ||
| 1013 | pid: vc.modelGroupGuid, | ||
| 1014 | modelGroupGuid: vc.modelGroupGuid, | ||
| 1015 | modelGroupName: vc.modelGroupName, | ||
| 1016 | dataSourceGuid: vc.dataSourceGuid, | ||
| 1017 | }] | ||
| 1018 | }); | ||
| 1019 | } | ||
| 1020 | }); | ||
| 1021 | databaseInfo.value = v[0].dataSourceGuid; | ||
| 1022 | dsToTreeData.value = p; | ||
| 1023 | if (!databaseList.value?.length) { //编辑的时候第一次打开。 | ||
| 1024 | getDataSourceListData().then(res => { //解决先出现guid,再变成中文的问题。 | ||
| 1025 | if (getDsFromTreeDataPromise.value) { | ||
| 1026 | getDsFromTreeDataPromise.value.then(res => { | ||
| 1027 | Promise.all(dsToTreeData.value.map(pi => { | ||
| 1028 | return getTreeDataByDs(dsFromTreeData.value.find(d => d.guid == pi.guid)) | ||
| 1029 | })).then(() => { | ||
| 1030 | dsFromTreeData.value = handlerFromFilterToTreeData(dsFromTreeData.value, dsToTreeData.value); | ||
| 1031 | modelsDialogVisible.value = true; | ||
| 1032 | }) | ||
| 1033 | }) | ||
| 1034 | } else { | ||
| 1035 | Promise.all(dsToTreeData.value.map(pi => { | ||
| 1036 | return getTreeDataByDs(dsFromTreeData.value.find(d => d.guid == pi.guid)) | ||
| 1037 | })).then(() => { | ||
| 1038 | dsFromTreeData.value = handlerFromFilterToTreeData(dsFromTreeData.value, dsToTreeData.value); | ||
| 1039 | modelsDialogVisible.value = true; | ||
| 1040 | }) | ||
| 1041 | } | ||
| 1042 | }); | ||
| 1043 | } else { | ||
| 1044 | modelsDialogVisible.value = true; | ||
| 1045 | } | ||
| 1046 | } | ||
| 1047 | } else if (btn.field === 'qualityModelGuidsByGroup') { | ||
| 1048 | modelsByGroupDialogVisible.value = true; | ||
| 1049 | if (!dsSelectGroup.value) { | ||
| 1050 | if (getValidGroupPromise.value) { | ||
| 1051 | getValidGroupPromise.value.then((res: any) => { | ||
| 1052 | dsSelectGroup.value = res[0]?.guid ?? ""; | ||
| 1053 | }); | ||
| 1054 | } else { | ||
| 1055 | dsSelectGroup.value = qualityGroups.value[0]?.guid ?? ""; | ||
| 1056 | } | ||
| 1057 | } | ||
| 1058 | dsByGroupToTreeData.value = cloneDeep(modelFormItems.value[3].default ?? []); | ||
| 1059 | } | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | const handlerFromFilterToTreeData = (data, toData) => { | ||
| 1063 | for (let i = data.length - 1; i >= 0; i--) { | ||
| 1064 | for (let j = toData.length - 1; j >= 0; j--) { | ||
| 1065 | if (data[i] && data[i].guid === toData[j].guid) { | ||
| 1066 | // 当id相等可以删除的情况 即:没有子级可以删除; | ||
| 1067 | if (!data[i].children?.length && !toData[j].children?.length) { | ||
| 1068 | data.splice(i, 1); | ||
| 1069 | } else { | ||
| 1070 | handlerFromFilterToTreeData(data[i].children, toData[j].children); | ||
| 1071 | if (!data[i].children?.length) { | ||
| 1072 | data.splice(i, 1); | ||
| 1073 | } | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | return data; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | const selectModels: any = ref([]); | ||
| 1082 | |||
| 1083 | /** 以数据库为粒度选择模型表。 */ | ||
| 1084 | const modelsDialogVisible = ref(false); | ||
| 1085 | |||
| 1086 | const databaseList: any = ref([]); | ||
| 1087 | |||
| 1088 | const databaseInfo = ref(''); | ||
| 1089 | |||
| 1090 | const dsFromTreeDataLoading = ref(false); | ||
| 1091 | |||
| 1092 | const dsFromTreeData: any = ref([]); | ||
| 1093 | |||
| 1094 | const currentDsFromTreeData = ref([]); | ||
| 1095 | |||
| 1096 | const dsToTreeData: any = ref([]); | ||
| 1097 | |||
| 1098 | let getDsFromTreeDataPromise: any = ref(null); | ||
| 1099 | |||
| 1100 | watch(() => databaseInfo.value, (val) => { | ||
| 1101 | if (val) { | ||
| 1102 | dsFromTreeDataLoading.value = true; | ||
| 1103 | getDsFromTreeDataPromise.value = getModelDbGp(val).then((res: any) => { | ||
| 1104 | getDsFromTreeDataPromise.value = null; | ||
| 1105 | dsFromTreeDataLoading.value = false; | ||
| 1106 | if (res.code == proxy.$passCode) { | ||
| 1107 | dsFromTreeData.value = res.data?.map(d => { | ||
| 1108 | d.pid = 0; | ||
| 1109 | return d; | ||
| 1110 | }) || []; | ||
| 1111 | currentDsFromTreeData.value = JSON.parse(JSON.stringify(dsFromTreeData.value)) || []; | ||
| 1112 | return dsFromTreeData.value; | ||
| 1113 | } else { | ||
| 1114 | dsFromTreeData.value = []; | ||
| 1115 | ElMessage.error(res.msg); | ||
| 1116 | } | ||
| 1117 | }) | ||
| 1118 | } else { | ||
| 1119 | dsFromTreeData.value = []; | ||
| 1120 | } | ||
| 1121 | }) | ||
| 1122 | |||
| 1123 | const cancelModelDialog = () => { | ||
| 1124 | modelsDialogVisible.value = false; | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | const submitModels = () => { | ||
| 1128 | if (!dsToTreeData.value.length) { | ||
| 1129 | ElMessage.error('已选表不能为空'); | ||
| 1130 | return; | ||
| 1131 | } | ||
| 1132 | let models: any = []; | ||
| 1133 | dsToTreeData.value.forEach(d => { | ||
| 1134 | models.push(...d.children); | ||
| 1135 | }); | ||
| 1136 | let formInline = modelFormRef.value?.formInline || {}; | ||
| 1137 | selectModels.value = models; | ||
| 1138 | modelFormItems.value[4].default = selectModels.value; | ||
| 1139 | // http://test.csylcloud.com:8380/browse/WZL-511 | ||
| 1140 | let item1 = modelFormItems.value[0]; | ||
| 1141 | item1.default = formInline[item1.field]; | ||
| 1142 | let item2 = modelFormItems.value[1]; | ||
| 1143 | item2.default = formInline[item2.field]; | ||
| 1144 | modelFormRef.value?.ruleFormRef?.validateField('qualityModelGuids'); | ||
| 1145 | let guids = selectModels.value.map((m: any) => m.guid); | ||
| 1146 | getModelCountListData(guids); | ||
| 1147 | getModelRuleCountListData(guids); | ||
| 1148 | modelsDialogVisible.value = false; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | const handleModelLeftCheckChange = (nodeObj, treeObj, checkAll, treeNode) => { | ||
| 1152 | if (!nodeObj.isLeaf && treeObj.checkedKeys?.includes(nodeObj.guid)) { | ||
| 1153 | treeNode?.expand(); | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | /** 切换了数据库,清空表选择 */ | ||
| 1158 | const handleSelectDsModelChange = (val) => { | ||
| 1159 | dsToTreeData.value = []; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | const loadNode = (node, resolve, from) => { | ||
| 1163 | if (node.level === 0) { | ||
| 1164 | if (node.data) { | ||
| 1165 | resolve(node.data); | ||
| 1166 | } | ||
| 1167 | return; | ||
| 1168 | } | ||
| 1169 | if (node.level === 1) { | ||
| 1170 | if (node.data.children?.length > 0) { | ||
| 1171 | resolve(node.data.children); | ||
| 1172 | return; | ||
| 1173 | } | ||
| 1174 | getTreeDataByDs(node.data).then((res) => { | ||
| 1175 | resolve(res); | ||
| 1176 | }) | ||
| 1177 | } | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | const getTreeDataByDs = (node) => { | ||
| 1181 | return getQualityTreeDataByDs({ guid: node.guid, dataSourceGuid: databaseInfo.value }).then((res: any) => { | ||
| 1182 | if (res.code == proxy.$passCode) { | ||
| 1183 | const data = res.data?.map(d => { | ||
| 1184 | d.parentGuid = node.guid; | ||
| 1185 | d.modelGroupGuid = node.guid; | ||
| 1186 | d.pid = node.guid; | ||
| 1187 | d.modelGuid = d.guid; | ||
| 1188 | d.modelGroupName = node.name; | ||
| 1189 | d.dataSourceGuid = databaseInfo.value; | ||
| 1190 | d.isLeaf = true; | ||
| 1191 | return d; | ||
| 1192 | }) || []; | ||
| 1193 | node.children = data; | ||
| 1194 | return data; | ||
| 1195 | } else { | ||
| 1196 | ElMessage.error(res.msg); | ||
| 1197 | return []; | ||
| 1198 | } | ||
| 1199 | }) | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | /** 以分组为粒度选择模型表。 */ | ||
| 1203 | const selectModelsByGroup: any = ref([]); | ||
| 1204 | |||
| 1205 | const modelsByGroupDialogVisible = ref(false); | ||
| 1206 | |||
| 1207 | /** 以分组为粒度的对话框选择的当前分组 */ | ||
| 1208 | const dsSelectGroup = ref(''); | ||
| 1209 | |||
| 1210 | const dsByGroupFromTreeDataLoading = ref(false); | ||
| 1211 | |||
| 1212 | const dsByGroupFromTreeData = ref() | ||
| 1213 | |||
| 1214 | const dsByGroupToTreeData: any = ref([]); | ||
| 1215 | |||
| 1216 | watch(() => dsSelectGroup.value, (val) => { | ||
| 1217 | if (val) { | ||
| 1218 | dsByGroupFromTreeDataLoading.value = true; | ||
| 1219 | getQualityTreeData(val).then((res: any) => { | ||
| 1220 | dsByGroupFromTreeDataLoading.value = false; | ||
| 1221 | if (res.code == proxy.$passCode) { | ||
| 1222 | dsByGroupFromTreeData.value = res.data || []; | ||
| 1223 | if (initDefaultGroup.value == true) { | ||
| 1224 | initDefaultGroup.value = false; | ||
| 1225 | modelFormItems.value[3].default = selectModelsByGroup.value = dsByGroupFromTreeData.value; | ||
| 1226 | let guids = selectModelsByGroup.value.map((m: any) => m.guid); | ||
| 1227 | getModelCountListData(guids); | ||
| 1228 | getModelRuleCountListData(guids); | ||
| 1229 | } | ||
| 1230 | } else { | ||
| 1231 | dsByGroupFromTreeData.value = []; | ||
| 1232 | ElMessage.error(res.msg); | ||
| 1233 | } | ||
| 1234 | }) | ||
| 1235 | } else { | ||
| 1236 | dsByGroupFromTreeData.value = []; | ||
| 1237 | } | ||
| 1238 | }) | ||
| 1239 | |||
| 1240 | const cancelModelByGroupDialog = () => { | ||
| 1241 | modelsByGroupDialogVisible.value = false; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | const submitModelsByGroup = () => { | ||
| 1245 | if (!dsByGroupToTreeData.value.length) { | ||
| 1246 | ElMessage.error('已选表不能为空'); | ||
| 1247 | return; | ||
| 1248 | } | ||
| 1249 | let formInline = modelFormRef.value?.formInline || {}; | ||
| 1250 | selectModelsByGroup.value = dsByGroupToTreeData.value.slice(0); | ||
| 1251 | modelFormItems.value[3].default = selectModelsByGroup.value; | ||
| 1252 | // http://test.csylcloud.com:8380/browse/WZL-511 | ||
| 1253 | let item1 = modelFormItems.value[0]; | ||
| 1254 | item1.default = formInline[item1.field]; | ||
| 1255 | let item2 = modelFormItems.value[1]; | ||
| 1256 | item2.default = formInline[item2.field]; | ||
| 1257 | modelFormRef.value?.ruleFormRef?.validateField('qualityModelGuidsByGroup'); | ||
| 1258 | let guids = selectModelsByGroup.value.map((m: any) => m.guid); | ||
| 1259 | getModelCountListData(guids); | ||
| 1260 | getModelRuleCountListData(guids); | ||
| 1261 | modelsByGroupDialogVisible.value = false; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | const handleModelByGroupLeftCheckedChange = (nodeObj, fromCheckAll, checked, fromTree, filterFrom, leafChecked) => { | ||
| 1265 | if (nodeObj.children?.length && (nodeObj.children?.length) && !checked && leafChecked) { | ||
| 1266 | //全选变半选 | ||
| 1267 | return; | ||
| 1268 | } | ||
| 1269 | if (filterFrom && nodeObj.label.indexOf(filterFrom) === -1) { | ||
| 1270 | return; | ||
| 1271 | } | ||
| 1272 | let nodeId = nodeObj.guid; | ||
| 1273 | let index = dsByGroupToTreeData.value.findIndex((t) => t.guid === nodeId); | ||
| 1274 | if (checked) { | ||
| 1275 | index === -1 && dsByGroupToTreeData.value.push(nodeObj); | ||
| 1276 | } else { | ||
| 1277 | if (index > -1) { | ||
| 1278 | dsByGroupToTreeData.value.splice(index, 1); | ||
| 1279 | } | ||
| 1280 | } | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | /** 切换了分组,清空表选择 */ | ||
| 1284 | const handleSelectGroupChange = (val) => { | ||
| 1285 | dsByGroupToTreeData.value = []; | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | /** 规则设置对话框 */ | ||
| 1289 | const ruleSettingDialogVisible = ref(false); | ||
| 1290 | |||
| 1291 | /** 记录根据模型表查出的表格数据。 */ | ||
| 1292 | const modelRulesTableData = ref([]); | ||
| 1293 | |||
| 1294 | /** 指定模型表对应的规则设置表格。 */ | ||
| 1295 | const modelRulesTableInfo: any = ref({ | ||
| 1296 | id: 'rule-model-table', | ||
| 1297 | loading: false, | ||
| 1298 | minPanelHeight: '200px', | ||
| 1299 | minHeight: '200px', | ||
| 1300 | height: '100%', | ||
| 1301 | nodeKey: 'guid', | ||
| 1302 | multiple: true, | ||
| 1303 | selectable: !isDetail.value, | ||
| 1304 | fields: [ | ||
| 1305 | { label: "序号", type: "index", width: 56, align: "center" }, | ||
| 1306 | { label: "表名", field: "name", width: 140 }, | ||
| 1307 | { label: "规则名称", field: "ruleConfName", width: 140 }, | ||
| 1308 | { label: "规则大类", field: "largeCategoryName", width: 140 }, | ||
| 1309 | { label: "规则小类", field: "smallCategoryName", width: 140 }, | ||
| 1310 | { label: "规则类型", field: "ruleName", width: 140 }, | ||
| 1311 | { label: "字段", field: "ruleField", width: 140 }, | ||
| 1312 | ], | ||
| 1313 | data: modelRulesTableData.value, | ||
| 1314 | showPage: false, | ||
| 1315 | actionInfo: { | ||
| 1316 | show: false | ||
| 1317 | } | ||
| 1318 | }); | ||
| 1319 | |||
| 1320 | const modelRuleSelectedRows: any = ref([]); | ||
| 1321 | |||
| 1322 | const handleModelRulesSelectionChange = (val) => { | ||
| 1323 | modelRuleSelectedRows.value = val; | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | /** 取消规则设置对话框。 */ | ||
| 1327 | const cancelRuleSettingDialog = () => { | ||
| 1328 | ruleSettingDialogVisible.value = false; | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | /** 提交规则设置对话框。 */ | ||
| 1332 | const submitRuleSetting = () => { | ||
| 1333 | if (!modelRuleSelectedRows.value?.length) { | ||
| 1334 | ElMessage.error('质量规则集至少选择一个规则'); | ||
| 1335 | return; | ||
| 1336 | } | ||
| 1337 | let oldLen = ruleWeightData.value.length; | ||
| 1338 | // 跟原始规则对比,未勾选的规则,需要同步将规则数量减少。 | ||
| 1339 | let selectRows = modelRuleSelectedRows.value.slice(0); | ||
| 1340 | currTableData.value.modelRuleConfs.forEach(rule => { | ||
| 1341 | let index = selectRows.findIndex(s => s.guid == rule.guid) | ||
| 1342 | if (index > -1) { | ||
| 1343 | selectRows.splice(index, 1); | ||
| 1344 | return; | ||
| 1345 | } | ||
| 1346 | // 取消选中了。总量要减1. | ||
| 1347 | let dIndex = ruleWeightData.value.findIndex(r => r.largeCategory == rule.largeCategory); | ||
| 1348 | if (dIndex > -1) { | ||
| 1349 | let d = ruleWeightData.value[dIndex]; | ||
| 1350 | d.ruleCount--; | ||
| 1351 | if (d.ruleCount == 0) { | ||
| 1352 | ruleWeightData.value.splice(dIndex, 1); | ||
| 1353 | } | ||
| 1354 | currTableData.value.ruleCount--; | ||
| 1355 | currTableData.value.largeCategoryNum[rule.largeCategory]--; | ||
| 1356 | if (!currTableData.value.largeCategoryNum[rule.largeCategory]) { | ||
| 1357 | delete currTableData.value.largeCategoryNum[rule.largeCategory]; | ||
| 1358 | } | ||
| 1359 | } | ||
| 1360 | }); | ||
| 1361 | selectRows.forEach(s => { | ||
| 1362 | let d = ruleWeightData.value.find(r => r.largeCategory == s.largeCategory); | ||
| 1363 | currTableData.value.ruleCount++; | ||
| 1364 | if (!d) { | ||
| 1365 | let info = { | ||
| 1366 | largeCategory: s.largeCategory, | ||
| 1367 | largeCategoryName: s.largeCategoryName, | ||
| 1368 | ruleCount: 1, | ||
| 1369 | }; | ||
| 1370 | ruleWeightData.value.push(info); | ||
| 1371 | } else { | ||
| 1372 | d.ruleCount++; | ||
| 1373 | } | ||
| 1374 | if (!currTableData.value.largeCategoryNum[s.largeCategory]) { | ||
| 1375 | currTableData.value.largeCategoryNum[s.largeCategory] = 1; | ||
| 1376 | } else { | ||
| 1377 | currTableData.value.largeCategoryNum[s.largeCategory]++; | ||
| 1378 | } | ||
| 1379 | }); | ||
| 1380 | //http://test.csylcloud.com:8380/browse/WZL-529 | ||
| 1381 | if (ruleWeightData.value.length == 1) {//只剩一条 | ||
| 1382 | ruleWeightData.value[0].ruleLargeWeight = changeNum(100, 2, true); | ||
| 1383 | } else if (oldLen == 1 && ruleWeightData.value.length > 1) { //原始一条,现在多条 | ||
| 1384 | let v = changeNum(100 / ruleWeightData.value.length, 2, true); | ||
| 1385 | ruleWeightData.value.forEach(d => d.ruleLargeWeight = v); | ||
| 1386 | } | ||
| 1387 | // 最后剩余的selectRows要加在总量中。 | ||
| 1388 | currTableData.value.modelRuleConfGuids = modelRuleSelectedRows.value?.map(r => r.guid); | ||
| 1389 | currTableData.value.modelRuleConfs = modelRuleSelectedRows.value; | ||
| 1390 | ruleSettingDialogVisible.value = false; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | const selectSubjectTables = computed(() => { | ||
| 1394 | let v = modelFormRef.value?.formInline; | ||
| 1395 | if (!v) { | ||
| 1396 | return []; | ||
| 1397 | } | ||
| 1398 | if (v.planType === 1) { | ||
| 1399 | if (planGuid && !treeSelectNode.value) { // 编辑,可能没有treeSelectNode。解决WZL-545问题。 | ||
| 1400 | let tInfo = planDetailInfo.value.modelCount?.[0]; | ||
| 1401 | return (!v.qualityModelGuid || !tInfo) ? [] : [{ | ||
| 1402 | name: tInfo.modelName, | ||
| 1403 | groupName: tInfo.modelGroupName, | ||
| 1404 | guid: tInfo.modelGuid, | ||
| 1405 | parentGuid: tInfo.modelGroupGuid, | ||
| 1406 | subjectGuid: tInfo.subjectGuid | ||
| 1407 | }]; | ||
| 1408 | } | ||
| 1409 | return v.qualityModelGuid ? [treeSelectNode.value] : []; | ||
| 1410 | } | ||
| 1411 | if (v.planType === 3) { | ||
| 1412 | return selectModelsByGroup.value; | ||
| 1413 | } | ||
| 1414 | return selectModels.value; | ||
| 1415 | }); | ||
| 1416 | |||
| 1417 | /** 记录分组json的过滤条件。最后提交时需要转化到table里。 */ | ||
| 1418 | const batchFiltersValue: any = ref({}); | ||
| 1419 | |||
| 1420 | const batchFiltersValueChange = (value) => { | ||
| 1421 | batchFiltersValue.value = value; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | </script> | ||
| 1425 | |||
| 1426 | <template> | ||
| 1427 | <div class="container_wrap full" v-loading="fullscreenLoading"> | ||
| 1428 | <div class="content_main"> | ||
| 1429 | <div class="top_tool_wrap"> | ||
| 1430 | <StepBar :steps-info="stepsInfo" /> | ||
| 1431 | </div> | ||
| 1432 | <div class="operator_panel_wrap" v-show="step == 0"> | ||
| 1433 | <div class="operator_panel is-block"> | ||
| 1434 | <div class="panel_title"> | ||
| 1435 | <div class="title_text"> | ||
| 1436 | <span>1.1选择模型</span> | ||
| 1437 | <span class="tips_text">选择质量规则集,会带出模型下所有启用状态下的规则</span> | ||
| 1438 | </div> | ||
| 1439 | </div> | ||
| 1440 | <div class="panel_content"> | ||
| 1441 | <div class="form_panel"> | ||
| 1442 | <Form ref="modelFormRef" :readonly="isDetail" :itemList="modelFormItems" formId="edit-standard-form" | ||
| 1443 | :rules="modelFormRules" col="col3" @btnClick="btnFormClick" @selectChange="selectChange" | ||
| 1444 | @treeSelectLoad="modelFormTreeSelectLoad" @treeSelectNodeChange="modelFormTreeSelectNodeChange" /> | ||
| 1445 | </div> | ||
| 1446 | </div> | ||
| 1447 | </div> | ||
| 1448 | <div class="operator_panel is-block"> | ||
| 1449 | <div class="panel_title"> | ||
| 1450 | <div class="title_text"> | ||
| 1451 | <span>1.2选择规则</span> | ||
| 1452 | <span class="tips_text">根据表选择需要执行的规则,不需要的执行规则选择删除</span> | ||
| 1453 | </div> | ||
| 1454 | </div> | ||
| 1455 | <div class="panel_content_rule"> | ||
| 1456 | <div class="tools_btns"> | ||
| 1457 | <el-button type="primary" @click="handleFiltersAdd">批量配置过滤条件</el-button> | ||
| 1458 | <span class="tips_text">批量给表添加质检数据范围,不需要写where关键字,支持函数。未填写默认质检全部数据。</span> | ||
| 1459 | </div> | ||
| 1460 | <Table :tableInfo="ruleModelTableInfo" @tableBtnClick="tableBtnClick" /> | ||
| 1461 | </div> | ||
| 1462 | </div> | ||
| 1463 | <div class="operator_panel is-block"> | ||
| 1464 | <div class="panel_title"> | ||
| 1465 | <div class="title_text"> | ||
| 1466 | <span>1.3设置权重</span> | ||
| 1467 | <span class="tips_text">选择需要执行的规则,不需要的执行规则选择删除</span> | ||
| 1468 | </div> | ||
| 1469 | </div> | ||
| 1470 | <div class="panel_content_weight"> | ||
| 1471 | <el-table ref="ruleWeightTableRef" v-loading="ruleWeightDataLoading" :data="ruleWeightData" height="100%" | ||
| 1472 | :highlight-current-row="true" stripe tooltip-effect="light" border | ||
| 1473 | :style="{ height: '100%', width: 'auto', 'max-width': '100%', display: 'inline-block' }"> | ||
| 1474 | <el-table-column prop="largeCategoryName" label="规则大类" width="150px" align="left" show-overflow-tooltip> | ||
| 1475 | </el-table-column> | ||
| 1476 | <el-table-column prop="ruleCount" label="规则数量" width="150px" header-align="right" align="right" | ||
| 1477 | show-overflow-tooltip> | ||
| 1478 | </el-table-column> | ||
| 1479 | <el-table-column prop="ruleLargeWeight" label="规则权重(总分100)" width="200px" | ||
| 1480 | :align="isDetail ? 'right' : 'left'" show-overflow-tooltip> | ||
| 1481 | <template #default="scope"> | ||
| 1482 | <el-input v-if="!isDetail" v-model.trim="scope.row['ruleLargeWeight']" placeholder="请输入" | ||
| 1483 | @change="(val) => inputWeightChange(val, scope.row)" | ||
| 1484 | @input="(val) => inputEventWeightChange(val, scope.row)"></el-input> | ||
| 1485 | <span v-else>{{ scope.row['ruleLargeWeight'] }}</span> | ||
| 1486 | </template> | ||
| 1487 | </el-table-column> | ||
| 1488 | </el-table> | ||
| 1489 | </div> | ||
| 1490 | </div> | ||
| 1491 | </div> | ||
| 1492 | <div class="operator_panel_wrap" v-show="step == 1"> | ||
| 1493 | <div class="operator_panel is-block"> | ||
| 1494 | <div class="panel_title"> | ||
| 1495 | <div class="title_text"> | ||
| 1496 | <span>2.1任务调度</span> | ||
| 1497 | <!-- <span class="tips_text">选择质量规则集,会带出模型下所有启用状态下的规则</span> --> | ||
| 1498 | </div> | ||
| 1499 | </div> | ||
| 1500 | <div class="panel_content"> | ||
| 1501 | <div class="form_panel form_task"> | ||
| 1502 | <Form ref="taskFormRef" :readonly="isDetail" :itemList="taskFormItems" formId="edit-standard-form" | ||
| 1503 | :rules="taskFormRules" @switchChange="handleTaskCreateChange" @scheduleChange="scheduleChange" | ||
| 1504 | @btnClick="taskFormBtnClick" /> | ||
| 1505 | </div> | ||
| 1506 | </div> | ||
| 1507 | </div> | ||
| 1508 | </div> | ||
| 1509 | </div> | ||
| 1510 | <div class="bottom_tool_wrap"> | ||
| 1511 | <template v-if="step == 0"> | ||
| 1512 | <el-button @click="cancelPlan">取消</el-button> | ||
| 1513 | <el-button type="primary" @click="changeStep(2)">下一步</el-button> | ||
| 1514 | </template> | ||
| 1515 | <template v-else> | ||
| 1516 | <el-button @click="cancelPlan">取消</el-button> | ||
| 1517 | <el-button type="primary" @click="changeStep(1)">上一步</el-button> | ||
| 1518 | <el-button v-if="!isDetail" type="primary" v-preReClick @click="save">保存</el-button> | ||
| 1519 | </template> | ||
| 1520 | </div> | ||
| 1521 | |||
| 1522 | <!-- 以数据库为粒度的选择表对话框 --> | ||
| 1523 | <el-dialog v-model="modelsDialogVisible" title="选择质量规则集" width="660" :modal="true" :close-on-click-modal="false" | ||
| 1524 | destroy-on-close align-center> | ||
| 1525 | <div style="height:450px"> | ||
| 1526 | <TreeTransfer :readOnly="isDetail" mode="transfer" :title="['可选表', '已选表']" :defaultProps="{ | ||
| 1527 | label: 'name', | ||
| 1528 | value: 'guid', | ||
| 1529 | isLeaf: 'isLeaf' | ||
| 1530 | }" :from-tree-data-loading="dsFromTreeDataLoading" :from_data="dsFromTreeData" :to_data="dsToTreeData" | ||
| 1531 | checkOnClickNode :from_checked_all="false" node_key="guid" :transferOpenNode="true" width="100%" | ||
| 1532 | @left-check-change="handleModelLeftCheckChange" lazy :lazyFn="loadNode" height="100%"> | ||
| 1533 | <template v-slot:from> | ||
| 1534 | <el-select style="width: 100%;margin-bottom: 8px;" v-model="databaseInfo" placeholder="请选择数据库" filterable | ||
| 1535 | :disabled="isDetail" @change="handleSelectDsModelChange" value-key="guid"> | ||
| 1536 | <el-option v-for="opt in databaseList" :key="opt['guid']" :label="opt['name']" :value="opt['guid']" /> | ||
| 1537 | </el-select> | ||
| 1538 | </template> | ||
| 1539 | </TreeTransfer> | ||
| 1540 | </div> | ||
| 1541 | <template #footer v-if="!isDetail"> | ||
| 1542 | <div class="dialog-footer"> | ||
| 1543 | <el-button @click="cancelModelDialog" v-preReClick>取消</el-button> | ||
| 1544 | <el-button @click="submitModels" type="primary" v-preReClick>确定</el-button> | ||
| 1545 | </div> | ||
| 1546 | </template> | ||
| 1547 | </el-dialog> | ||
| 1548 | <!-- 以分组为粒度的选择表对话框 --> | ||
| 1549 | <el-dialog v-model="modelsByGroupDialogVisible" title="选择质量规则集" width="660" :modal="true" | ||
| 1550 | :close-on-click-modal="false" destroy-on-close align-center> | ||
| 1551 | <div style="height:450px"> | ||
| 1552 | <TreeTransferChecked class="one-level" mode="transfer" :title="['可选表', '已选表']" :defaultProps="{ | ||
| 1553 | label: 'name', | ||
| 1554 | value: 'guid' | ||
| 1555 | }" :from-tree-data-loading="dsByGroupFromTreeDataLoading" :from_data="dsByGroupFromTreeData" checkOnClickNode | ||
| 1556 | :to_data="dsByGroupToTreeData" node_key="guid" :transferOpenNode="true" width="100%" | ||
| 1557 | :defaultCheckedKeys="dsByGroupToTreeData.map(d => d.guid)" :rootPidValue="''" | ||
| 1558 | @left-check-changed="handleModelByGroupLeftCheckedChange" height="100%"> | ||
| 1559 | <template v-slot:from> | ||
| 1560 | <el-select style="width: 100%;margin-bottom: 8px;" v-model="dsSelectGroup" placeholder="请选择分组" filterable | ||
| 1561 | :disabled="isDetail" @change="handleSelectGroupChange" value-key="guid"> | ||
| 1562 | <el-option v-for="opt in qualityGroups" :key="opt['guid']" :label="opt['name']" :value="opt['guid']" /> | ||
| 1563 | </el-select> | ||
| 1564 | </template> | ||
| 1565 | </TreeTransferChecked> | ||
| 1566 | </div> | ||
| 1567 | <template #footer v-if="!isDetail"> | ||
| 1568 | <div class="dialog-footer"> | ||
| 1569 | <el-button @click="cancelModelByGroupDialog">取消</el-button> | ||
| 1570 | <el-button @click="submitModelsByGroup" type="primary">确定</el-button> | ||
| 1571 | </div> | ||
| 1572 | </template> | ||
| 1573 | </el-dialog> | ||
| 1574 | |||
| 1575 | <!-- 规则设置对话框 --> | ||
| 1576 | <el-dialog v-model="ruleSettingDialogVisible" title="规则设置" width="800" :modal="true" :close-on-click-modal="false" | ||
| 1577 | destroy-on-close align-center> | ||
| 1578 | <div style="height:400px"> | ||
| 1579 | <Table ref="modelRulesTableRef" :class="isDetail ? 'disable-table' : ''" :tableInfo="modelRulesTableInfo" | ||
| 1580 | @table-selection-change="handleModelRulesSelectionChange" /> | ||
| 1581 | </div> | ||
| 1582 | <template #footer v-if="!isDetail"> | ||
| 1583 | <div class="dialog-footer"> | ||
| 1584 | <el-button @click="cancelRuleSettingDialog">取消</el-button> | ||
| 1585 | <el-button @click="submitRuleSetting" type="primary">确定</el-button> | ||
| 1586 | </div> | ||
| 1587 | </template> | ||
| 1588 | </el-dialog> | ||
| 1589 | |||
| 1590 | <filtersSettingBatch ref="batchFilterDialogRef" :subject-tables="selectSubjectTables" :readOnly="isDetail" | ||
| 1591 | @filtersValueChange="batchFiltersValueChange"></filtersSettingBatch> | ||
| 1592 | </div> | ||
| 1593 | </template> | ||
| 1594 | |||
| 1595 | <style lang="scss" scoped> | ||
| 1596 | .top_tool_wrap { | ||
| 1597 | width: 100%; | ||
| 1598 | height: 72px; | ||
| 1599 | margin: 8px 0 0px; | ||
| 1600 | display: flex; | ||
| 1601 | justify-content: center; | ||
| 1602 | align-items: center; | ||
| 1603 | |||
| 1604 | :deep(.el-steps) { | ||
| 1605 | width: 30%; | ||
| 1606 | } | ||
| 1607 | } | ||
| 1608 | |||
| 1609 | .content_main { | ||
| 1610 | height: calc(100% - 40px); | ||
| 1611 | padding: 0 16px; | ||
| 1612 | overflow: hidden auto; | ||
| 1613 | |||
| 1614 | :deep(.table_panel_wrap) { | ||
| 1615 | &.full { | ||
| 1616 | height: auto; | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | .table_panel { | ||
| 1620 | width: 100%; | ||
| 1621 | min-height: unset; | ||
| 1622 | } | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | .operator_panel_wrap { | ||
| 1626 | display: flex; | ||
| 1627 | justify-content: space-between; | ||
| 1628 | flex-wrap: wrap; | ||
| 1629 | height: auto; | ||
| 1630 | |||
| 1631 | :deep(.el-button) { | ||
| 1632 | &.is-text { | ||
| 1633 | height: auto; | ||
| 1634 | padding: 0; | ||
| 1635 | } | ||
| 1636 | } | ||
| 1637 | |||
| 1638 | .operator_panel { | ||
| 1639 | width: calc(50% - 5px); | ||
| 1640 | height: auto; | ||
| 1641 | border: 1px solid #d9d9d9; | ||
| 1642 | margin-bottom: 12px; | ||
| 1643 | overflow: hidden; | ||
| 1644 | |||
| 1645 | &.is-block { | ||
| 1646 | width: 100%; | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | .panel_title { | ||
| 1650 | height: 44px; | ||
| 1651 | padding: 0 15px; | ||
| 1652 | display: flex; | ||
| 1653 | justify-content: space-between; | ||
| 1654 | align-items: center; | ||
| 1655 | color: var(--el-color-regular); | ||
| 1656 | font-weight: 600; | ||
| 1657 | border-bottom: 1px solid #d9d9d9; | ||
| 1658 | background-color: #fafafa; | ||
| 1659 | |||
| 1660 | .tips_text { | ||
| 1661 | font-size: 14px; | ||
| 1662 | color: #999; | ||
| 1663 | font-weight: normal; | ||
| 1664 | margin-left: 8px; | ||
| 1665 | } | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | .panel_content { | ||
| 1669 | height: calc(100% - 42px); | ||
| 1670 | |||
| 1671 | >div { | ||
| 1672 | width: 100%; | ||
| 1673 | height: 100%; | ||
| 1674 | overflow: hidden; | ||
| 1675 | } | ||
| 1676 | |||
| 1677 | .form_panel { | ||
| 1678 | padding: 8px 16px; | ||
| 1679 | overflow: hidden auto; | ||
| 1680 | } | ||
| 1681 | |||
| 1682 | .form_task { | ||
| 1683 | width: 60%; | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | .tree_search_input { | ||
| 1687 | margin-bottom: 10px; | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | .list_panel { | ||
| 1691 | padding: 10px 0; | ||
| 1692 | overflow: hidden auto; | ||
| 1693 | |||
| 1694 | .list_item { | ||
| 1695 | height: 32px; | ||
| 1696 | padding: 0 10px; | ||
| 1697 | display: flex; | ||
| 1698 | justify-content: space-between; | ||
| 1699 | align-items: center; | ||
| 1700 | |||
| 1701 | &:hover { | ||
| 1702 | color: var(--g-sub-sidebar-menu-active-color); | ||
| 1703 | background-color: var(--g-sub-sidebar-menu-active-bg); | ||
| 1704 | } | ||
| 1705 | } | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | .table_content_wrap { | ||
| 1709 | padding: 16px; | ||
| 1710 | |||
| 1711 | .tools_form { | ||
| 1712 | p { | ||
| 1713 | margin-top: 0; | ||
| 1714 | margin-bottom: 8px; | ||
| 1715 | } | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | } | ||
| 1719 | |||
| 1720 | .panel_content_rule { | ||
| 1721 | height: 280px; | ||
| 1722 | padding: 12px 16px; | ||
| 1723 | |||
| 1724 | .table_panel { | ||
| 1725 | min-height: 200px; | ||
| 1726 | height: calc(100% - 36px) !important; | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | .tools_btns { | ||
| 1730 | margin-bottom: 8px; | ||
| 1731 | |||
| 1732 | .tips_text { | ||
| 1733 | margin-left: 8px; | ||
| 1734 | font-size: 12px; | ||
| 1735 | color: #b2b2b2; | ||
| 1736 | } | ||
| 1737 | } | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | .panel_content_weight { | ||
| 1741 | height: 230px; | ||
| 1742 | min-height: 230px; | ||
| 1743 | padding: 12px 16px; | ||
| 1744 | |||
| 1745 | :deep(.el-table) { | ||
| 1746 | & td.el-table__cell { | ||
| 1747 | padding: 2px 0; | ||
| 1748 | height: 36px; | ||
| 1749 | } | ||
| 1750 | } | ||
| 1751 | } | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | .bottm_tools { | ||
| 1755 | width: 100%; | ||
| 1756 | height: 40px; | ||
| 1757 | display: flex; | ||
| 1758 | justify-content: center; | ||
| 1759 | align-items: center; | ||
| 1760 | background: #fafafa; | ||
| 1761 | color: #999; | ||
| 1762 | font-size: 14px; | ||
| 1763 | border: 1px dashed var(--el-border-color-regular); | ||
| 1764 | margin-bottom: 12px; | ||
| 1765 | |||
| 1766 | >span { | ||
| 1767 | margin-left: 8px; | ||
| 1768 | } | ||
| 1769 | } | ||
| 1770 | } | ||
| 1771 | } | ||
| 1772 | |||
| 1773 | .bottom_tool_wrap { | ||
| 1774 | height: 40px; | ||
| 1775 | padding: 0 16px; | ||
| 1776 | border-top: 1px solid #d9d9d9; | ||
| 1777 | display: flex; | ||
| 1778 | justify-content: flex-end; | ||
| 1779 | align-items: center; | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | :deep(.disable-table) { | ||
| 1783 | .el-table { | ||
| 1784 | .el-table__header-wrapper .el-checkbox { | ||
| 1785 | display: none | ||
| 1786 | } | ||
| 1787 | } | ||
| 1788 | } | ||
| 1789 | </style> |
| 1 | <script lang="ts" setup name="filtersSettingBatch"> | ||
| 2 | import { ref } from "vue"; | ||
| 3 | import TreeTransferChecked from "@/components/TreeTransferChecked/index.vue"; | ||
| 4 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 5 | import { Delete } from "@element-plus/icons-vue"; | ||
| 6 | import { | ||
| 7 | batchValidateSubjectTableRule, | ||
| 8 | } from '@/api/modules/dataQuality'; | ||
| 9 | import { cloneDeep } from 'lodash-es' | ||
| 10 | |||
| 11 | const { proxy } = getCurrentInstance() as any; | ||
| 12 | |||
| 13 | /** 记录每个分组对应的表信息和过滤条件 */ | ||
| 14 | interface GroupFilterTableInfo { | ||
| 15 | tables?: Array<any>; | ||
| 16 | filter?: string; | ||
| 17 | checked?: boolean; | ||
| 18 | } | ||
| 19 | |||
| 20 | const props = defineProps({ | ||
| 21 | subjectTables: { | ||
| 22 | type: Array<any>, | ||
| 23 | default: [], | ||
| 24 | }, | ||
| 25 | readOnly: { | ||
| 26 | type: Boolean, | ||
| 27 | default: false, | ||
| 28 | } | ||
| 29 | }); | ||
| 30 | |||
| 31 | const emits = defineEmits(["filtersValueChange"]); | ||
| 32 | |||
| 33 | /** 批量添加过滤条件对话框。 */ | ||
| 34 | const filterDialogVisible = ref(false); | ||
| 35 | |||
| 36 | /** 打开对话框时过滤条件的值。 */ | ||
| 37 | const filterPropsValue: Ref<GroupFilterTableInfo> = ref({}); | ||
| 38 | |||
| 39 | /** 记录分组数组列表。 */ | ||
| 40 | const groupData: any = ref([]); | ||
| 41 | |||
| 42 | /** 过滤条件初始默认值 */ | ||
| 43 | let dialogInitValue: any = ref({ 1: { tables: [], filter: '', checked: false } }); | ||
| 44 | |||
| 45 | /** json 分组对应的表信息过滤条件。 */ | ||
| 46 | const groupFilterTables: any = computed(() => { | ||
| 47 | let keys = Object.keys(filterPropsValue.value); | ||
| 48 | return keys.length ? filterPropsValue.value : dialogInitValue.value; | ||
| 49 | }); | ||
| 50 | |||
| 51 | const selectGroupIndex = ref(0); | ||
| 52 | |||
| 53 | const groupItemClick = (item, index) => { | ||
| 54 | selectGroupIndex.value = index; | ||
| 55 | } | ||
| 56 | |||
| 57 | /** 添加新的分组 */ | ||
| 58 | const handleAddGroup = () => { | ||
| 59 | let len = groupData.value.length; | ||
| 60 | let guid = 1; | ||
| 61 | if (len) { | ||
| 62 | let lastId = parseInt(groupData.value[len - 1].guid); | ||
| 63 | guid = lastId + 1; | ||
| 64 | } | ||
| 65 | groupData.value.push({ | ||
| 66 | guid: guid, | ||
| 67 | label: `分组${guid}` | ||
| 68 | }); | ||
| 69 | selectGroupIndex.value = len; | ||
| 70 | groupFilterTables.value[guid] = { tables: [], filter: '' }; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** 给当前选中的分组选择对应的表 */ | ||
| 74 | const handleSelectTables = () => { | ||
| 75 | selectTableDialogVisible.value = true; | ||
| 76 | dsFromTreeData.value = props.subjectTables || []; | ||
| 77 | let selectGroup = groupData.value[selectGroupIndex.value]; | ||
| 78 | dsToTreeData.value = groupFilterTables.value[selectGroup.guid].tables?.slice(0) || []; | ||
| 79 | } | ||
| 80 | |||
| 81 | /** 删除选择表 */ | ||
| 82 | const removeTable = (item, index) => { | ||
| 83 | groupFilterTables.value[groupData.value[selectGroupIndex.value].guid].tables.splice(index, 1); | ||
| 84 | } | ||
| 85 | |||
| 86 | /** 删除分组 */ | ||
| 87 | const removeGroup = (item, index) => { | ||
| 88 | let info = groupFilterTables.value[item.guid]; | ||
| 89 | if (!info?.tables?.length && !info.filter) { | ||
| 90 | if (selectGroupIndex.value == 0) { | ||
| 91 | selectGroupIndex.value = groupData.value.length == 1 ? -1 : 0; | ||
| 92 | } else if (selectGroupIndex.value >= index) { | ||
| 93 | selectGroupIndex.value--; | ||
| 94 | } | ||
| 95 | groupData.value.splice(index, 1); | ||
| 96 | delete groupFilterTables.value[item.guid]; | ||
| 97 | return; | ||
| 98 | } else { | ||
| 99 | ElMessageBox.confirm("该分组下存在表或过滤条件,此操作将永久删除, 是否继续?", "提示", { | ||
| 100 | confirmButtonText: "确定", | ||
| 101 | cancelButtonText: "取消", | ||
| 102 | type: "warning", | ||
| 103 | }) | ||
| 104 | .then(() => { | ||
| 105 | if (selectGroupIndex.value == 0) { | ||
| 106 | selectGroupIndex.value = groupData.value.length == 1 ? -1 : 0; | ||
| 107 | } else if (selectGroupIndex.value >= index) { | ||
| 108 | selectGroupIndex.value--; | ||
| 109 | } | ||
| 110 | groupData.value.splice(index, 1); | ||
| 111 | delete groupFilterTables.value[item.guid]; | ||
| 112 | ElMessage({ | ||
| 113 | type: "success", | ||
| 114 | message: "删除成功", | ||
| 115 | }); | ||
| 116 | }) | ||
| 117 | .catch(() => { | ||
| 118 | ElMessage({ | ||
| 119 | type: "info", | ||
| 120 | message: "已取消删除", | ||
| 121 | }); | ||
| 122 | }); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | const handleSqlInputChange = (val) => { | ||
| 127 | let selectGroup = groupData.value[selectGroupIndex.value]; | ||
| 128 | groupFilterTables.value[selectGroup.guid].checked = false; | ||
| 129 | } | ||
| 130 | |||
| 131 | const validateSqlBtnDisable = ref(false); | ||
| 132 | |||
| 133 | /** 验证填写的过滤条件 */ | ||
| 134 | const validateSql = () => { | ||
| 135 | validateSqlBtnDisable.value = true; | ||
| 136 | let selectGroup = groupData.value[selectGroupIndex.value] | ||
| 137 | let enName = selectGroup.label; | ||
| 138 | let tables = groupFilterTables.value[selectGroup.guid].tables; | ||
| 139 | let filter = groupFilterTables.value[selectGroup.guid].filter; | ||
| 140 | if (!filter) { | ||
| 141 | ElMessage.error('请先填写过滤条件再验证sql'); | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | if (!tables?.length) { | ||
| 145 | ElMessage.error('请先选择表再验证sql'); | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | batchValidateSubjectTableRule( | ||
| 149 | tables.map(t => { | ||
| 150 | return { | ||
| 151 | subjectGuid: t.subjectGuid, | ||
| 152 | condition: filter | ||
| 153 | } | ||
| 154 | }) | ||
| 155 | ).then((res: any) => { | ||
| 156 | validateSqlBtnDisable.value = false; | ||
| 157 | if (res.code == proxy.$passCode) { | ||
| 158 | let isError = res.data.some(d => d.errInfo); | ||
| 159 | if (isError) { | ||
| 160 | let error = ""; | ||
| 161 | res.data.forEach((d, index) => { | ||
| 162 | if (d.errInfo) { | ||
| 163 | error = error ? ';' : error; | ||
| 164 | error = error + '【' + tables[index].name + '】' + d.errInfo; | ||
| 165 | } | ||
| 166 | }); | ||
| 167 | ElMessage.error('【' + enName + '】过滤条件验证失败:' + error); | ||
| 168 | } else { | ||
| 169 | groupFilterTables.value[selectGroup.guid].checked = true; | ||
| 170 | ElMessage.success('【' + enName + '】过滤条件验证通过'); | ||
| 171 | } | ||
| 172 | } else { | ||
| 173 | ElMessage.error(res.msg); | ||
| 174 | } | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | |||
| 178 | const submitFilter = () => { | ||
| 179 | let index = 0; | ||
| 180 | for (const group of groupData.value) { | ||
| 181 | let filtersInfo = groupFilterTables.value[group.guid]; | ||
| 182 | if (!filtersInfo?.tables.length) { | ||
| 183 | ElMessage.error('请先选择【' + group.label + '】对应的表'); | ||
| 184 | selectGroupIndex.value = index; | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | if (!filtersInfo?.filter) { | ||
| 188 | selectGroupIndex.value = index; | ||
| 189 | ElMessage.error('请填写【' + group.label + '】的过滤条件'); | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | if (!filtersInfo?.checked) { | ||
| 193 | selectGroupIndex.value = index; | ||
| 194 | ElMessage.error('请验证【' + group.label + '】的过滤条件'); | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | index++; | ||
| 198 | } | ||
| 199 | /** 验证有没有分组,验证每个分组的sql是否验证过,验证每个分组的表是否存在。 */ | ||
| 200 | emits('filtersValueChange', groupFilterTables.value); | ||
| 201 | filterDialogVisible.value = false; | ||
| 202 | } | ||
| 203 | |||
| 204 | /** 打开对话框。*/ | ||
| 205 | const openDialog = (v) => { | ||
| 206 | filterDialogVisible.value = true; | ||
| 207 | dialogInitValue.value = { 1: { tables: [], filter: '', checked: false } }; | ||
| 208 | selectGroupIndex.value = 0; | ||
| 209 | filterPropsValue.value = cloneDeep(v); | ||
| 210 | let keys = Object.keys(filterPropsValue.value); | ||
| 211 | groupData.value = keys.length ? keys.map(k => { | ||
| 212 | return { | ||
| 213 | guid: k, | ||
| 214 | label: `分组${k}` | ||
| 215 | } | ||
| 216 | }): [{ | ||
| 217 | guid: 1, | ||
| 218 | label: '分组1' | ||
| 219 | }]; | ||
| 220 | if (props.subjectTables?.length == 1) { | ||
| 221 | dialogInitValue.value = { 1: { tables: props.subjectTables, filter: '', checked: false } }; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | /** 点击对话框取消按钮 */ | ||
| 226 | const handleCloseFilterDialog = () => { | ||
| 227 | filterDialogVisible.value = false; | ||
| 228 | } | ||
| 229 | |||
| 230 | |||
| 231 | /** 选择表对话框处理 */ | ||
| 232 | const selectTableDialogVisible = ref(false); | ||
| 233 | |||
| 234 | const dsFromTreeData: any = ref([]); | ||
| 235 | |||
| 236 | const dsToTreeData: any = ref([]); | ||
| 237 | |||
| 238 | const cancelSelectTableDialog = () => { | ||
| 239 | selectTableDialogVisible.value = false; | ||
| 240 | } | ||
| 241 | |||
| 242 | const submitSelectTableByGroup = () => { | ||
| 243 | if (!dsToTreeData.value.length) { | ||
| 244 | ElMessage.error('可选表不能为空'); | ||
| 245 | return; | ||
| 246 | } | ||
| 247 | // 需要提示是否在别的分组中。 | ||
| 248 | let selectGroup = groupData.value[selectGroupIndex.value]; | ||
| 249 | let tables = dsToTreeData.value || []; | ||
| 250 | let str = ''; | ||
| 251 | tables.forEach(t => { | ||
| 252 | for (const key in groupFilterTables.value) { | ||
| 253 | if (key == selectGroup.guid) { | ||
| 254 | continue | ||
| 255 | } | ||
| 256 | let keyTables = groupFilterTables.value[key].tables || []; | ||
| 257 | if (keyTables.some(kt => (kt.modelName && t.modelName && kt.modelName == t.modelName) || kt.name == t.name)) { | ||
| 258 | str = str + (str ? ';' : '') + `【${t.modelName ?? t.name}】从【分组${key}】中移入【${selectGroup.label}】`; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | }); | ||
| 262 | if (!str) { | ||
| 263 | selectTableDialogVisible.value = false; | ||
| 264 | if (dsToTreeData.value.some(d => !groupFilterTables.value[selectGroup.guid].tables?.some(t => t.guid == d.guid))) { | ||
| 265 | groupFilterTables.value[selectGroup.guid].checked = false; | ||
| 266 | } | ||
| 267 | groupFilterTables.value[selectGroup.guid].tables = dsToTreeData.value; | ||
| 268 | } else { | ||
| 269 | ElMessageBox.confirm( | ||
| 270 | "确定将" + str + '?', | ||
| 271 | "提示", | ||
| 272 | { | ||
| 273 | confirmButtonText: "确定", | ||
| 274 | cancelButtonText: "取消", | ||
| 275 | type: "warning", | ||
| 276 | } | ||
| 277 | ) | ||
| 278 | .then(() => { | ||
| 279 | tables.forEach(t => { | ||
| 280 | for (const key in groupFilterTables.value) { | ||
| 281 | if (key == selectGroup.guid) { | ||
| 282 | continue | ||
| 283 | } | ||
| 284 | let keyTables = groupFilterTables.value[key].tables || []; | ||
| 285 | let index = keyTables.findIndex(kt => (kt.modelName && t.modelName && kt.modelName == t.modelName) || kt.name == t.name); | ||
| 286 | if (index > -1) { | ||
| 287 | groupFilterTables.value[key].tables.splice(index, 1); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | }); | ||
| 291 | selectTableDialogVisible.value = false; | ||
| 292 | if (dsToTreeData.value.some(d => !groupFilterTables.value[selectGroup.guid].tables?.some(t => t.guid == d.guid))) { | ||
| 293 | groupFilterTables.value[selectGroup.guid].checked = false; | ||
| 294 | } | ||
| 295 | groupFilterTables.value[selectGroup.guid].tables = dsToTreeData.value; | ||
| 296 | }) | ||
| 297 | .catch(() => { | ||
| 298 | ElMessage({ | ||
| 299 | type: "info", | ||
| 300 | message: "已取消", | ||
| 301 | }); | ||
| 302 | }); | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | const handleModelLeftCheckedChange = (nodeObj, fromCheckAll, checked, fromTree, filterFrom, leafChecked) => { | ||
| 307 | if (filterFrom && nodeObj.label.indexOf(filterFrom) === -1) { | ||
| 308 | return; | ||
| 309 | } | ||
| 310 | let nodeId = nodeObj.guid; | ||
| 311 | let index = dsToTreeData.value.findIndex((t) => t.guid === nodeId); | ||
| 312 | if (checked) { | ||
| 313 | index === -1 && dsToTreeData.value.push(nodeObj); | ||
| 314 | } else { | ||
| 315 | if (index > -1) { | ||
| 316 | dsToTreeData.value.splice(index, 1); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | defineExpose({ | ||
| 322 | openDialog | ||
| 323 | }); | ||
| 324 | |||
| 325 | </script> | ||
| 326 | |||
| 327 | <template> | ||
| 328 | <el-dialog v-model="filterDialogVisible" title="批量配置过滤条件" width="650" :modal="true" :close-on-click-modal="false" | ||
| 329 | destroy-on-close align-center @close="handleCloseFilterDialog"> | ||
| 330 | <div class="filter-dialog-content"> | ||
| 331 | <div class="group-list"> | ||
| 332 | <div class="field-title"> | ||
| 333 | <span>分组</span> | ||
| 334 | <el-button v-if="!props.readOnly" @click="handleAddGroup" link v-preReClick>新增</el-button> | ||
| 335 | </div> | ||
| 336 | <div class="list_unit"> | ||
| 337 | <div class="list_item" :class="{ active: index == selectGroupIndex }" v-for="(item, index) in groupData" | ||
| 338 | @click="groupItemClick(item, index)"> | ||
| 339 | <span>{{ item.label }}</span> | ||
| 340 | <!-- 删除按钮 --> | ||
| 341 | <el-button type="primary" text :icon="Delete" @click.stop="removeGroup(item, index)" | ||
| 342 | v-preReClick></el-button> | ||
| 343 | </div> | ||
| 344 | </div> | ||
| 345 | </div> | ||
| 346 | <div class="table-list" v-if="selectGroupIndex >= 0"> | ||
| 347 | <div class="field-title"> | ||
| 348 | <span>表</span> | ||
| 349 | <el-button v-if="!props.readOnly" @click="handleSelectTables" link>选择表</el-button> | ||
| 350 | </div> | ||
| 351 | <div class="list_unit"> | ||
| 352 | <div class="list_item" | ||
| 353 | v-for="(item, index) in (groupFilterTables[groupData[selectGroupIndex].guid].tables || [])"> | ||
| 354 | <span>{{ item.name }}</span> | ||
| 355 | <el-button type="primary" text :icon="Delete" @click.stop="removeTable(item, index)" | ||
| 356 | v-preReClick></el-button> | ||
| 357 | </div> | ||
| 358 | </div> | ||
| 359 | <div class="filter-edit"> | ||
| 360 | <div class="field-title"> | ||
| 361 | <span>过滤条件</span> | ||
| 362 | <el-button @click="validateSql" link v-preReClick>验证</el-button> | ||
| 363 | </div> | ||
| 364 | <el-input v-model="groupFilterTables[groupData[selectGroupIndex].guid].filter" ref="sqlInputRef" | ||
| 365 | :disabled="props.readOnly" type="textarea" :autosize="true" resize="none" | ||
| 366 | @change="handleSqlInputChange"></el-input> | ||
| 367 | </div> | ||
| 368 | </div> | ||
| 369 | </div> | ||
| 370 | <template #footer v-if="!props.readOnly"> | ||
| 371 | <div class="dialog-footer"> | ||
| 372 | <el-button @click="handleCloseFilterDialog">取消</el-button> | ||
| 373 | <el-button @click="submitFilter" type="primary">确定</el-button> | ||
| 374 | </div> | ||
| 375 | </template> | ||
| 376 | </el-dialog> | ||
| 377 | <!-- 选择表对话框 --> | ||
| 378 | <el-dialog v-model="selectTableDialogVisible" title="选择表" width="600" :modal="true" :close-on-click-modal="false" | ||
| 379 | destroy-on-close align-center> | ||
| 380 | <div style="height:400px"> | ||
| 381 | <TreeTransferChecked class="one-level" mode="transfer" :title="['可选表', '已选表']" :from_data="dsFromTreeData" checkOnClickNode | ||
| 382 | :to_data="dsToTreeData" :default-checked-keys="dsToTreeData?.map(d => d.guid)" :defaultProps="{ | ||
| 383 | label: 'name', | ||
| 384 | value: 'guid' | ||
| 385 | }" node_key="guid" :transferOpenNode="true" width="100%" height="100%" :rootPidValue="''" | ||
| 386 | @left-check-changed="handleModelLeftCheckedChange"></TreeTransferChecked> | ||
| 387 | </div> | ||
| 388 | <template #footer> | ||
| 389 | <div class="dialog-footer"> | ||
| 390 | <el-button @click="cancelSelectTableDialog">取消</el-button> | ||
| 391 | <el-button @click="submitSelectTableByGroup" type="primary">确定</el-button> | ||
| 392 | </div> | ||
| 393 | </template> | ||
| 394 | </el-dialog> | ||
| 395 | </template> | ||
| 396 | |||
| 397 | <style lang="scss" scoped> | ||
| 398 | .filter-dialog-content { | ||
| 399 | display: flex; | ||
| 400 | flex-direction: row; | ||
| 401 | margin: -8px -24px; | ||
| 402 | height: 460px; | ||
| 403 | |||
| 404 | .group-list { | ||
| 405 | width: 200px; | ||
| 406 | border-right: 1px solid #d9d9d9; | ||
| 407 | } | ||
| 408 | |||
| 409 | .table-list { | ||
| 410 | width: calc(100% - 200px); | ||
| 411 | border-right: 1px solid #d9d9d9; | ||
| 412 | } | ||
| 413 | |||
| 414 | .list_unit { | ||
| 415 | height: 200px; | ||
| 416 | border-top: 1px solid #d9d9d9; | ||
| 417 | overflow-y: auto; | ||
| 418 | |||
| 419 | .list_item { | ||
| 420 | height: 32px; | ||
| 421 | font-size: 14px; | ||
| 422 | color: #212121; | ||
| 423 | line-height: 32px; | ||
| 424 | padding-left: 8px; | ||
| 425 | display: flex; | ||
| 426 | flex-direction: row; | ||
| 427 | justify-content: space-between; | ||
| 428 | align-items: center; | ||
| 429 | padding-right: 8px; | ||
| 430 | cursor: pointer; | ||
| 431 | |||
| 432 | &.active { | ||
| 433 | background: #EBF6F7; | ||
| 434 | } | ||
| 435 | |||
| 436 | .el-button { | ||
| 437 | display: none; | ||
| 438 | } | ||
| 439 | |||
| 440 | &:hover { | ||
| 441 | .el-button { | ||
| 442 | display: inline-flex; | ||
| 443 | } | ||
| 444 | |||
| 445 | .el-button.is-text:not(.is-disabled):hover, | ||
| 446 | .el-button.is-text:not(.is-disabled):focus { | ||
| 447 | background-color: transparent; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | .filter-edit { | ||
| 454 | height: calc(100% - 232px); | ||
| 455 | border-top: 1px solid #d9d9d9; | ||
| 456 | } | ||
| 457 | |||
| 458 | .field-title { | ||
| 459 | height: 32px; | ||
| 460 | font-size: 14px; | ||
| 461 | color: #666; | ||
| 462 | line-height: 32px; | ||
| 463 | padding-left: 8px; | ||
| 464 | display: flex; | ||
| 465 | flex-direction: row; | ||
| 466 | justify-content: space-between; | ||
| 467 | align-items: center; | ||
| 468 | padding-right: 8px; | ||
| 469 | } | ||
| 470 | |||
| 471 | :deep(.el-textarea) { | ||
| 472 | height: calc(100% - 32px); | ||
| 473 | padding: 0px 8px 8px 8px; | ||
| 474 | |||
| 475 | .el-textarea__inner { | ||
| 476 | min-height: 100% !important; | ||
| 477 | border-radius: 0px; | ||
| 478 | } | ||
| 479 | } | ||
| 480 | } | ||
| 481 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/qualityAnalysis.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: qualityAnalysis | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="qualityAnalysis"> | ||
| 6 | import { ref } from 'vue' | ||
| 7 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 8 | import TableTools from '@/components/Tools/table_tools.vue' | ||
| 9 | import Table from '@/components/Table/index.vue' | ||
| 10 | import Dialog from '@/components/Dialog/index.vue' | ||
| 11 | import { useRouter } from "vue-router"; | ||
| 12 | import { | ||
| 13 | getQualityWordList, | ||
| 14 | deleteQualityWord, | ||
| 15 | addQualityWord, | ||
| 16 | updateQualityWord, | ||
| 17 | executeReport, | ||
| 18 | stateChange | ||
| 19 | } from '@/api/modules/dataQualityWord'; | ||
| 20 | import { | ||
| 21 | getPlanList | ||
| 22 | } from '@/api/modules/dataQualityAssess'; | ||
| 23 | import { | ||
| 24 | getCronExecTime | ||
| 25 | } from '@/api/modules/queryService'; | ||
| 26 | import { TableColumnWidth } from '@/utils/enum'; | ||
| 27 | |||
| 28 | const { proxy } = getCurrentInstance() as any; | ||
| 29 | |||
| 30 | const router = useRouter(); | ||
| 31 | const searchItemList = ref([ | ||
| 32 | { | ||
| 33 | type: 'input', | ||
| 34 | label: '', | ||
| 35 | maxlength: 50, | ||
| 36 | field: 'analysisReportName', | ||
| 37 | default: '', | ||
| 38 | placeholder: '报告名称', | ||
| 39 | clearable: true | ||
| 40 | } | ||
| 41 | ]) | ||
| 42 | |||
| 43 | const currTableData: any = ref<Object>({}); | ||
| 44 | const page = ref({ | ||
| 45 | limit: 50, | ||
| 46 | curr: 1, | ||
| 47 | sizes: [ | ||
| 48 | { label: "10", value: 10 }, | ||
| 49 | { label: "50", value: 50 }, | ||
| 50 | { label: "100", value: 100 }, | ||
| 51 | { label: "150", value: 150 }, | ||
| 52 | { label: "200", value: 200 }, | ||
| 53 | ], | ||
| 54 | analysisReportName: '', //报告名称 | ||
| 55 | }); | ||
| 56 | const selectRowData = ref([]) | ||
| 57 | const tableInfo = ref({ | ||
| 58 | id: "word-manage-table", | ||
| 59 | // multiple: true, | ||
| 60 | loading: false, | ||
| 61 | fields: [ | ||
| 62 | { label: "报告名称", field: "analysisReportName", width: 160 }, | ||
| 63 | { label: "评估方案名称", field: "planName", width: 140 }, | ||
| 64 | { | ||
| 65 | label: "方案类型", field: "planType", width: 90, getName: (scope) => { | ||
| 66 | let planType = scope.row.planType; | ||
| 67 | return planType == 1 ? '表' : (planType == 2 ? '数据库' : '分组'); | ||
| 68 | } | ||
| 69 | }, | ||
| 70 | { label: "质量规则集", field: "qualityModelName", width: 150 }, | ||
| 71 | { label: "质量评分", field: "qualityScore", width: 100, align: 'right' }, | ||
| 72 | { | ||
| 73 | label: '状态', field: 'state', type: 'switch', activeText: '上线', inactiveText: '下线', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 96, align: 'center', isDisabled: (scope) => { | ||
| 74 | return scope.row.isPlanGen == 'Y'; | ||
| 75 | },toolTipContent:"质量评估方案生成的报告禁止上下线、编辑",columClass:"text_btn" | ||
| 76 | }, | ||
| 77 | { label: "执行状态", field: "execState", type: 'tag', width: 100, align: 'center' }, | ||
| 78 | { | ||
| 79 | label: "执行周期", field: "execCycle", width: TableColumnWidth.EXECCYCLE | ||
| 80 | }, | ||
| 81 | { label: "下次执行时间", field: "nextExecTime", width: TableColumnWidth.DATETIME, }, | ||
| 82 | { label: "最后执行时间", field: "lastExecTime", width: TableColumnWidth.DATETIME, }, | ||
| 83 | { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME }, | ||
| 84 | { label: "修改时间", field: "updateTime", width: TableColumnWidth.DATETIME, }, | ||
| 85 | ], | ||
| 86 | data: [], | ||
| 87 | page: { | ||
| 88 | type: "normal", | ||
| 89 | rows: 0, | ||
| 90 | ...page.value, | ||
| 91 | }, | ||
| 92 | actionInfo: { | ||
| 93 | label: "操作", | ||
| 94 | type: "btn", | ||
| 95 | width: 300, | ||
| 96 | fixed: 'right', | ||
| 97 | btns: (scope) => { | ||
| 98 | const row = scope.row | ||
| 99 | let btnsArr: any = [ | ||
| 100 | { label: "查看报告", value: "reportView", disabled: row['execState'] != 2 }, | ||
| 101 | { label: "编辑", value: "edit", disabled: row.execState === 1 || row.isExecute || scope.row.isPlanGen == 'Y' || scope.row.state == 1 }]; | ||
| 102 | if (row.isExecute) { | ||
| 103 | btnsArr.splice(2, 0, { label: "执行中...", value: "execute", disabled: true }) | ||
| 104 | } else { | ||
| 105 | btnsArr.splice(2, 0, { label: "手动执行", value: "execute", disabled: row.execState === 1 || scope.row.state == 0 }) | ||
| 106 | } | ||
| 107 | btnsArr.push({ label: "执行日志", value: "path_log", disabled: !row['execState'] }); | ||
| 108 | btnsArr.push({ label: "删除", value: "delete", disabled: row.isExecute || row.execState === 1 || scope.row.isPlanGen == 'Y' || scope.row.state == 1 }); | ||
| 109 | return btnsArr | ||
| 110 | } | ||
| 111 | } | ||
| 112 | }); | ||
| 113 | |||
| 114 | /** 评估方案选择列表。 */ | ||
| 115 | const planList: any = ref([]); | ||
| 116 | |||
| 117 | const getAllPlanListPromise: any = ref(null); | ||
| 118 | |||
| 119 | onActivated(() => { | ||
| 120 | // 新建分析报告时应该只能选择上线的方案。 | ||
| 121 | getAllPlanListPromise.value = getPlanList({ pageSize: -1, pageIndex: 1, state: 1 }).then((res: any) => { | ||
| 122 | getAllPlanListPromise.value = null; | ||
| 123 | if (res.code == proxy.$passCode) { | ||
| 124 | const data = res.data || {} | ||
| 125 | planList.value = data.records || []; | ||
| 126 | formItems.value[0].options = planList.value; | ||
| 127 | } else { | ||
| 128 | ElMessage({ | ||
| 129 | type: 'error', | ||
| 130 | message: res.msg, | ||
| 131 | }) | ||
| 132 | } | ||
| 133 | }); | ||
| 134 | }) | ||
| 135 | |||
| 136 | /** 新建编辑方案的对话框对象。 */ | ||
| 137 | const wordDialogRef = ref(); | ||
| 138 | |||
| 139 | const formItems = ref([ | ||
| 140 | { | ||
| 141 | label: '评估方案名称', | ||
| 142 | type: 'select', | ||
| 143 | placeholder: '请选择', | ||
| 144 | field: 'planGuid', | ||
| 145 | default: '', | ||
| 146 | options: planList.value, | ||
| 147 | props: { | ||
| 148 | value: 'guid', | ||
| 149 | label: 'planName' | ||
| 150 | }, | ||
| 151 | clearable: true, | ||
| 152 | filterable: true, | ||
| 153 | required: true, | ||
| 154 | disabled: false, | ||
| 155 | }, { | ||
| 156 | label: '报告名称', | ||
| 157 | type: 'input', | ||
| 158 | maxlength: 50, | ||
| 159 | placeholder: '请输入', | ||
| 160 | field: 'analysisReportName', | ||
| 161 | default: '', | ||
| 162 | clearable: true, | ||
| 163 | required: true | ||
| 164 | }, { | ||
| 165 | label: '报告保留期数(期)', | ||
| 166 | type: 'input', | ||
| 167 | inputType: 'integerNumber', | ||
| 168 | placeholder: '请输入', | ||
| 169 | field: 'reserveCycle', | ||
| 170 | default: 365, | ||
| 171 | max: 1000, | ||
| 172 | min: 1, | ||
| 173 | clearable: true, | ||
| 174 | required: false | ||
| 175 | },{ | ||
| 176 | label: '是否上线', | ||
| 177 | type: 'switch', | ||
| 178 | field: 'state', | ||
| 179 | default: 0, | ||
| 180 | activeText:"上线", | ||
| 181 | inactiveText:"下线", | ||
| 182 | activeValue: 1, | ||
| 183 | inactiveValue: 0 | ||
| 184 | },{ | ||
| 185 | label: '执行周期', | ||
| 186 | type: 'input-popover-panel', | ||
| 187 | placeholder: '', | ||
| 188 | field: 'execCycle', | ||
| 189 | teleported: false, | ||
| 190 | default: "", | ||
| 191 | append: { | ||
| 192 | btn: { label: '执行时间', value: 'cron' } | ||
| 193 | }, | ||
| 194 | required: true, | ||
| 195 | block: true | ||
| 196 | }, | ||
| 197 | { | ||
| 198 | label: '接下来五次执行时间', | ||
| 199 | type: 'textarea', | ||
| 200 | placeholder: '执行时间', | ||
| 201 | field: 'zxzq', | ||
| 202 | clearable: true, | ||
| 203 | required: false, | ||
| 204 | rows: 5, | ||
| 205 | block: true, | ||
| 206 | visible: true, | ||
| 207 | readonly: true | ||
| 208 | }, | ||
| 209 | ]) | ||
| 210 | |||
| 211 | const formRules = ref({ | ||
| 212 | planGuid: [{ required: true, trigger: 'change', message: "请填写评估方案" }], | ||
| 213 | analysisReportName: [{ required: true, trigger: 'blur', message: "请填写报告名称" }], | ||
| 214 | execCycle: [ | ||
| 215 | { | ||
| 216 | required: true, | ||
| 217 | message: "请设置执行周期", | ||
| 218 | trigger: "blur", | ||
| 219 | }, | ||
| 220 | ] | ||
| 221 | }) | ||
| 222 | |||
| 223 | const dialogInfo = ref({ | ||
| 224 | visible: false, | ||
| 225 | size: 700, | ||
| 226 | direction: "column", | ||
| 227 | header: { | ||
| 228 | title: "新建", | ||
| 229 | }, | ||
| 230 | type: '', | ||
| 231 | contents: [ | ||
| 232 | { | ||
| 233 | type: "form", | ||
| 234 | title: "", | ||
| 235 | formInfo: { | ||
| 236 | id: "word-edit-form", | ||
| 237 | items: formItems.value, | ||
| 238 | rules: formRules.value | ||
| 239 | }, | ||
| 240 | } | ||
| 241 | ], | ||
| 242 | footer: { | ||
| 243 | visible: true, | ||
| 244 | btns: [ | ||
| 245 | { type: "default", label: "取消", value: "cancel" }, | ||
| 246 | { type: "primary", label: "确定", value: "submit" }, | ||
| 247 | ], | ||
| 248 | }, | ||
| 249 | }); | ||
| 250 | |||
| 251 | const toSearch = (val: any, clear: boolean = false) => { | ||
| 252 | if (clear) { | ||
| 253 | page.value.analysisReportName = ''; | ||
| 254 | } else { | ||
| 255 | page.value.analysisReportName = val.analysisReportName; | ||
| 256 | } | ||
| 257 | page.value.curr = 1; | ||
| 258 | getTableData(); | ||
| 259 | }; | ||
| 260 | |||
| 261 | const getTableData = () => { | ||
| 262 | tableInfo.value.loading = true; | ||
| 263 | getQualityWordList({ pageIndex: page.value.curr, pageSize: page.value.limit, analysisReportName: page.value.analysisReportName }).then((res: any) => { | ||
| 264 | tableInfo.value.loading = false; | ||
| 265 | if (res.code == proxy.$passCode) { | ||
| 266 | const data = res.data || {} | ||
| 267 | tableInfo.value.data = data.records || [] | ||
| 268 | tableInfo.value.page.limit = data.pageSize | ||
| 269 | tableInfo.value.page.curr = data.pageIndex | ||
| 270 | tableInfo.value.page.rows = data.totalRows | ||
| 271 | } else { | ||
| 272 | ElMessage.error(res.msg); | ||
| 273 | } | ||
| 274 | }) | ||
| 275 | }; | ||
| 276 | |||
| 277 | const tableSelectionChange = (val) => { | ||
| 278 | selectRowData.value = val.map((item) => item.guid); | ||
| 279 | }; | ||
| 280 | |||
| 281 | const tablePageChange = (info) => { | ||
| 282 | page.value.curr = Number(info.curr); | ||
| 283 | page.value.limit = Number(info.limit); | ||
| 284 | getTableData(); | ||
| 285 | }; | ||
| 286 | |||
| 287 | const tableBtnClick = (scope, btn) => { | ||
| 288 | const type = btn.value; | ||
| 289 | const row = scope.row; | ||
| 290 | currTableData.value = row; | ||
| 291 | |||
| 292 | if (type == "edit") { | ||
| 293 | dialogInfo.value.header.title = "编辑分析报告"; | ||
| 294 | dialogInfo.value.type = type | ||
| 295 | dialogInfo.value.visible = true; | ||
| 296 | setFormItems(row); | ||
| 297 | formItems.value[0].disabled = true; | ||
| 298 | formItems.value[0].options = [{ | ||
| 299 | guid: row.planGuid, | ||
| 300 | planName: row.planName | ||
| 301 | }] | ||
| 302 | } else if (type === 'reportView') { | ||
| 303 | router.push({ | ||
| 304 | name: 'analysisReport', | ||
| 305 | query: { | ||
| 306 | planGuid: row.planGuid, | ||
| 307 | reportExecGuid: row.reportExecGuid, | ||
| 308 | name: row.analysisReportName, | ||
| 309 | } | ||
| 310 | }); | ||
| 311 | } else if (type == 'path_log') { | ||
| 312 | router.push({ | ||
| 313 | name: 'analysisLog', | ||
| 314 | query: { | ||
| 315 | guid: row.guid, | ||
| 316 | name: row.analysisReportName | ||
| 317 | } | ||
| 318 | }); | ||
| 319 | } else if (type == "execute") { | ||
| 320 | row.isExecute = true | ||
| 321 | const guid = row.guid | ||
| 322 | executeReport(guid).then((res: any) => { | ||
| 323 | if (res.code == proxy.$passCode) { | ||
| 324 | getTableData(); | ||
| 325 | ElMessage({ | ||
| 326 | type: "success", | ||
| 327 | message: "手动执行提交成功", | ||
| 328 | }); | ||
| 329 | } else { | ||
| 330 | ElMessage({ | ||
| 331 | type: "error", | ||
| 332 | message: res.msg, | ||
| 333 | }); | ||
| 334 | } | ||
| 335 | row.isExecute = false | ||
| 336 | }).catch(() => { | ||
| 337 | row.isExecute = false | ||
| 338 | }) | ||
| 339 | } else if (type == 'delete') { | ||
| 340 | open("此操作将永久删除该分析报告,是否继续?", "warning"); | ||
| 341 | } else if(type=='state') { | ||
| 342 | |||
| 343 | } | ||
| 344 | }; | ||
| 345 | const tableSwitchBeforeChange = (scope, field, callback) => { | ||
| 346 | const row = scope.row | ||
| 347 | const msg = `确定【${scope.row[field] == 1 ? '下线' : '上线'}】该方案吗?`; | ||
| 348 | ElMessageBox.confirm( | ||
| 349 | msg, | ||
| 350 | '提示', | ||
| 351 | { | ||
| 352 | confirmButtonText: '确定', | ||
| 353 | cancelButtonText: '取消', | ||
| 354 | type: 'warning', | ||
| 355 | } | ||
| 356 | ).then(() => { | ||
| 357 | const guid = row.guid | ||
| 358 | const state1 = row.state | ||
| 359 | stateChange({guid,state:state1===1?0:1}).then((res:any)=>{ | ||
| 360 | if(res.code===proxy.$passCode){ | ||
| 361 | ElMessage.success(`${scope.row[field] == 1 ? '下线' : '上线'}成功`) | ||
| 362 | getTableData(); | ||
| 363 | } else { | ||
| 364 | ElMessage.error(res.msg) | ||
| 365 | } | ||
| 366 | }) | ||
| 367 | }).catch(() => { | ||
| 368 | callback(false) | ||
| 369 | }) | ||
| 370 | } | ||
| 371 | const open = (msg, type, isBatch = false) => { | ||
| 372 | ElMessageBox.confirm(msg, "提示", { | ||
| 373 | confirmButtonText: "确定", | ||
| 374 | cancelButtonText: "取消", | ||
| 375 | type: type, | ||
| 376 | }).then(() => { | ||
| 377 | let guids = [currTableData.value.guid] | ||
| 378 | if (isBatch) { | ||
| 379 | guids = selectRowData.value | ||
| 380 | } | ||
| 381 | deleteQualityWord(guids).then((res: any) => { | ||
| 382 | if (res.code == proxy.$passCode) { | ||
| 383 | getTableData(); | ||
| 384 | ElMessage({ | ||
| 385 | type: "success", | ||
| 386 | message: "删除成功", | ||
| 387 | }); | ||
| 388 | } else { | ||
| 389 | ElMessage({ | ||
| 390 | type: "error", | ||
| 391 | message: res.msg, | ||
| 392 | }); | ||
| 393 | } | ||
| 394 | }); | ||
| 395 | }); | ||
| 396 | }; | ||
| 397 | |||
| 398 | /** 打开新建报告的编辑框 */ | ||
| 399 | const handleCreateNewWord = () => { | ||
| 400 | if (getAllPlanListPromise.value) { | ||
| 401 | getAllPlanListPromise.value.then(() => { | ||
| 402 | setFormItems(null); | ||
| 403 | formItems.value[0].disabled = false; | ||
| 404 | formItems.value[0].options = planList.value; | ||
| 405 | dialogInfo.value.header.title = '新建分析报告' | ||
| 406 | dialogInfo.value.type = 'add' | ||
| 407 | dialogInfo.value.footer.visible = true | ||
| 408 | dialogInfo.value.visible = true | ||
| 409 | }); | ||
| 410 | } else { | ||
| 411 | setFormItems(null); | ||
| 412 | formItems.value[0].disabled = false; | ||
| 413 | formItems.value[0].options = planList.value; | ||
| 414 | dialogInfo.value.header.title = '新建分析报告' | ||
| 415 | dialogInfo.value.type = 'add' | ||
| 416 | dialogInfo.value.footer.visible = true | ||
| 417 | dialogInfo.value.visible = true | ||
| 418 | } | ||
| 419 | }; | ||
| 420 | |||
| 421 | const batching = (type) => { | ||
| 422 | if (type == 'delete') { | ||
| 423 | if (selectRowData.value.length == 0) { | ||
| 424 | ElMessage({ | ||
| 425 | type: 'error', | ||
| 426 | message: '请选择需要删除的报告', | ||
| 427 | }) | ||
| 428 | return | ||
| 429 | } | ||
| 430 | open("此操作将永久删除,是否继续?", "warning", true); | ||
| 431 | } | ||
| 432 | }; | ||
| 433 | |||
| 434 | const dialogBtnClick = (btn, info) => { | ||
| 435 | if (btn.value == 'submit') { | ||
| 436 | if (!info.reserveCycle && info.reserveCycle !== 0) { | ||
| 437 | info.reserveCycle = null; | ||
| 438 | } | ||
| 439 | if (dialogInfo.value.type == 'add') { | ||
| 440 | addQualityWord(info).then((res: any) => { | ||
| 441 | if (res.code == proxy.$passCode) { | ||
| 442 | page.value.curr = 1; | ||
| 443 | getTableData(); | ||
| 444 | ElMessage({ | ||
| 445 | type: 'success', | ||
| 446 | message: '添加质量分析报告成功' | ||
| 447 | }) | ||
| 448 | dialogInfo.value.visible = false; | ||
| 449 | } else { | ||
| 450 | ElMessage({ | ||
| 451 | type: 'error', | ||
| 452 | message: res.msg, | ||
| 453 | }) | ||
| 454 | } | ||
| 455 | }) | ||
| 456 | } else { | ||
| 457 | const params = { ...info }; | ||
| 458 | params.guid = currTableData.value.guid; | ||
| 459 | updateQualityWord(params).then((res: any) => { | ||
| 460 | if (res.code == proxy.$passCode) { | ||
| 461 | toSearch({analysisReportName:page.value.analysisReportName}); | ||
| 462 | ElMessage({ | ||
| 463 | type: 'success', | ||
| 464 | message: '修改成功' | ||
| 465 | }) | ||
| 466 | dialogInfo.value.visible = false; | ||
| 467 | } else { | ||
| 468 | ElMessage({ | ||
| 469 | type: 'error', | ||
| 470 | message: res.msg, | ||
| 471 | }) | ||
| 472 | } | ||
| 473 | }) | ||
| 474 | } | ||
| 475 | } else if (btn.value == 'cancel') { | ||
| 476 | dialogInfo.value.visible = false; | ||
| 477 | } else if (btn.value = 'cron') { | ||
| 478 | let vInfo = wordDialogRef.value.dialogFormRef[0].formInline; | ||
| 479 | if (!vInfo.execCycle) { | ||
| 480 | return; | ||
| 481 | } | ||
| 482 | getCronExecTime(vInfo.execCycle).then((res: any) => { | ||
| 483 | if (res?.length) { | ||
| 484 | vInfo.zxzq = res.join('\n'); | ||
| 485 | setFormItems(vInfo); | ||
| 486 | } else { | ||
| 487 | ElMessage({ | ||
| 488 | type: 'error', | ||
| 489 | message: res.msg, | ||
| 490 | }) | ||
| 491 | } | ||
| 492 | }) | ||
| 493 | } | ||
| 494 | }; | ||
| 495 | |||
| 496 | const dialogSelectChange = (val, row, info) => { | ||
| 497 | if (row.field == 'planGuid') { | ||
| 498 | formItems.value[0].default = val; | ||
| 499 | formItems.value[1].default = planList.value.find(p => p.guid == val)?.planName || ""; | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | const scheduleChange = (val, rowValue) => { | ||
| 504 | setFormItems(rowValue); | ||
| 505 | // formItems.value[3].default = val | ||
| 506 | } | ||
| 507 | |||
| 508 | const setFormItems = (row) => { | ||
| 509 | formItems.value.map(item => { | ||
| 510 | if (item.field == 'reserveCycle') { | ||
| 511 | item.default = row ? row[item.field] : 365; | ||
| 512 | } else { | ||
| 513 | item.default = row ? row[item.field] : '' | ||
| 514 | } | ||
| 515 | }) | ||
| 516 | } | ||
| 517 | |||
| 518 | onBeforeMount(() => { | ||
| 519 | }) | ||
| 520 | |||
| 521 | </script> | ||
| 522 | |||
| 523 | <template> | ||
| 524 | <div class="container_wrap"> | ||
| 525 | <div class="table_tool_wrap has_search"> | ||
| 526 | <TableTools :searchItems="searchItemList" searchId="word-search" @search="toSearch" /> | ||
| 527 | <div class="tools_btns"> | ||
| 528 | <el-button type="primary" @click="handleCreateNewWord">新建分析报告</el-button> | ||
| 529 | <!-- <el-button @click="batching('delete')" v-preReClick>批量删除</el-button> --> | ||
| 530 | </div> | ||
| 531 | </div> | ||
| 532 | <div class="table_panel_wrap"> | ||
| 533 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange" | ||
| 534 | @tablePageChange="tablePageChange" @tableSwitchBeforeChange="tableSwitchBeforeChange" /> | ||
| 535 | </div> | ||
| 536 | |||
| 537 | <Dialog ref="wordDialogRef" :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @selectChange="dialogSelectChange" | ||
| 538 | @scheduleChange="scheduleChange" /> | ||
| 539 | </div> | ||
| 540 | </template> | ||
| 541 | |||
| 542 | <style lang="scss" scoped> | ||
| 543 | .table_tool_wrap { | ||
| 544 | width: 100%; | ||
| 545 | height: 84px !important; | ||
| 546 | padding: 0 8px; | ||
| 547 | |||
| 548 | .tools_btns { | ||
| 549 | padding: 8px 0 0; | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | .table_panel_wrap { | ||
| 554 | width: 100%; | ||
| 555 | height: calc(100% - 84px); | ||
| 556 | padding: 8px 8px 0; | ||
| 557 | } | ||
| 558 | </style> |
src/views/data_quality/qualityAssess.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: qualityAssess | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <template> | ||
| 6 | <div class="container_wrap flex"> | ||
| 7 | <div class="box_left aside_wrap"> | ||
| 8 | <div class="aside_title">质量评估方案列表</div> | ||
| 9 | <Tree :treeInfo="treeInfo" @nodeClick="nodeClick" /> | ||
| 10 | </div> | ||
| 11 | <div class="box_right"> | ||
| 12 | <div class="table_tool_wrap"> | ||
| 13 | <TableTools :searchItems="searchItemList" :searchId="'user-manage-search'" @search="toSearch" /> | ||
| 14 | <div class="tools_btns"> | ||
| 15 | <el-button type="primary" @click="newCreatePlan" :disabled="treeSelectItem.guid == 4" | ||
| 16 | v-preReClick>新建方案</el-button> | ||
| 17 | </div> | ||
| 18 | </div> | ||
| 19 | <div class="table_panel_wrap"> | ||
| 20 | <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange" | ||
| 21 | @tableSwitchBeforeChange="tableSwitchBeforeChange" @tablePageChange="tablePageChange" /> | ||
| 22 | </div> | ||
| 23 | </div> | ||
| 24 | </div> | ||
| 25 | </template> | ||
| 26 | |||
| 27 | <script lang="ts" setup name="qualityAssess"> | ||
| 28 | import { ref } from 'vue' | ||
| 29 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 30 | import Tree from "@/components/Tree/index.vue"; | ||
| 31 | import TableTools from '@/components/Tools/table_tools.vue' | ||
| 32 | import Table from "@/components/Table/index.vue"; | ||
| 33 | import { useRouter } from "vue-router"; | ||
| 34 | import { changeNum } from '@/utils/common'; | ||
| 35 | import { | ||
| 36 | getPlanList, | ||
| 37 | deletePlan, | ||
| 38 | updateQualityPlanState, | ||
| 39 | executePlan | ||
| 40 | } from '@/api/modules/dataQualityAssess'; | ||
| 41 | import { TableColumnWidth } from '@/utils/enum'; | ||
| 42 | import useDataQualityStore from "@/store/modules/dataQuality"; | ||
| 43 | import { commonPageConfig } from '@/components/PageNav/index'; | ||
| 44 | |||
| 45 | /** 数据质量缓存内容 */ | ||
| 46 | const dataQualityStore = useDataQualityStore(); | ||
| 47 | |||
| 48 | const { proxy } = getCurrentInstance() as any; | ||
| 49 | |||
| 50 | const router = useRouter() | ||
| 51 | |||
| 52 | /** 当前左侧选中树的item,用于根据选中不同层级,右侧显示不同表格。 */ | ||
| 53 | const treeSelectItem: any = ref({}) | ||
| 54 | |||
| 55 | /** 左侧树形配置信息。 */ | ||
| 56 | const treeInfo: any = ref({ | ||
| 57 | id: "data-pickup-tree", | ||
| 58 | filter: false, | ||
| 59 | queryValue: "", | ||
| 60 | queryPlaceholder: "输入名称搜索", | ||
| 61 | nodeKey: 'guid', | ||
| 62 | expandedKey: [0], | ||
| 63 | currentNodeKey: 0, | ||
| 64 | props: { | ||
| 65 | value: 'guid', | ||
| 66 | label: 'label', | ||
| 67 | isLeaf: 'isLeaf' | ||
| 68 | }, | ||
| 69 | data: [{ | ||
| 70 | guid: 0, | ||
| 71 | label: '质量评估方案', | ||
| 72 | children: [{ | ||
| 73 | guid: 1, | ||
| 74 | label: '表方案' | ||
| 75 | }, { | ||
| 76 | guid: 3, | ||
| 77 | label: '分组方案' | ||
| 78 | }, { | ||
| 79 | guid: 2, | ||
| 80 | label: '数据库方案' | ||
| 81 | } | ||
| 82 | // , { | ||
| 83 | // guid: 4, | ||
| 84 | // label: '数据同步方案' | ||
| 85 | // } | ||
| 86 | ] | ||
| 87 | }], | ||
| 88 | }); | ||
| 89 | |||
| 90 | /** 上方搜索配置信息。 */ | ||
| 91 | const searchItemList = ref([ | ||
| 92 | { | ||
| 93 | type: 'input', | ||
| 94 | label: '', | ||
| 95 | field: 'planName', | ||
| 96 | default: '', | ||
| 97 | maxlength: 50, | ||
| 98 | placeholder: '方案名称', | ||
| 99 | clearable: true, | ||
| 100 | visible: true | ||
| 101 | }, { | ||
| 102 | type: 'select', | ||
| 103 | label: '', | ||
| 104 | field: 'state', | ||
| 105 | default: null, | ||
| 106 | placeholder: '状态', | ||
| 107 | options: [ | ||
| 108 | { label: '上线', value: 1 }, | ||
| 109 | { label: '下线', value: 0 }, | ||
| 110 | ], | ||
| 111 | clearable: true, | ||
| 112 | visible: true | ||
| 113 | } | ||
| 114 | ]) | ||
| 115 | |||
| 116 | /** 当前操作的方案表格行数据 */ | ||
| 117 | const currTableData: any = ref<Object>({}); | ||
| 118 | /** 分页及搜索传参信息配置。 */ | ||
| 119 | const page = ref({ | ||
| 120 | ...commonPageConfig, planName: '', | ||
| 121 | planType: null, | ||
| 122 | state: null | ||
| 123 | }); | ||
| 124 | /** 方案表格已勾选选中的行。 */ | ||
| 125 | const selectRowData = ref([]) | ||
| 126 | |||
| 127 | /** 方案表格配置信息。 */ | ||
| 128 | const tableInfo = ref({ | ||
| 129 | id: 'user-manage-table', | ||
| 130 | // multiple: true, | ||
| 131 | loading: false, | ||
| 132 | nodeKey: 'guid', | ||
| 133 | fields: [ | ||
| 134 | { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", fixed: "left" }, | ||
| 135 | { label: "方案名称", field: "planName", width: 140, type: "text_btn", value: "detail", fixed: "left", columClass: 'text_btn' }, | ||
| 136 | { | ||
| 137 | label: "方案类型", field: "planType", width: 90, getName: (scope) => { | ||
| 138 | let planType = scope.row.planType; | ||
| 139 | return planType == 1 ? '表' : (planType == 2 ? '数据库' : (planType == 4 ? '数据同步' : '分组')); | ||
| 140 | } | ||
| 141 | }, | ||
| 142 | { label: "数据源", field: "dataSourceName", width: 150 }, | ||
| 143 | { | ||
| 144 | label: "表数量", field: "tableNum", width: 80, align: 'right', getName: (scope) => { | ||
| 145 | return scope.row.tableNum != null ? changeNum(scope.row.tableNum ?? 0) : '--'; | ||
| 146 | } | ||
| 147 | }, | ||
| 148 | { | ||
| 149 | label: "规则数", field: "ruleNum", width: 80, align: 'right', getName: (scope) => { | ||
| 150 | return scope.row.ruleNum != null ? changeNum(scope.row.ruleNum ?? 0) : '--'; | ||
| 151 | } | ||
| 152 | }, | ||
| 153 | { | ||
| 154 | label: '状态', field: 'state', type: 'switch', activeText: '上线', inactiveText: '下线', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 96, align: 'center', isDisabled: (scope) => { | ||
| 155 | return scope.row.planType == 4; | ||
| 156 | } | ||
| 157 | }, | ||
| 158 | { label: "执行状态", field: "execState", width: TableColumnWidth.STATE, align: 'center', type: "tag" }, | ||
| 159 | { | ||
| 160 | label: "执行周期", field: "execCycle", width: TableColumnWidth.EXECCYCLE | ||
| 161 | }, | ||
| 162 | { label: "下次执行时间", field: "nextExecTime", width: TableColumnWidth.DATETIME, }, | ||
| 163 | { label: "最后执行时间", field: "lastExecTime", width: TableColumnWidth.DATETIME, }, | ||
| 164 | { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME }, | ||
| 165 | { label: "修改时间", field: "updateTime", width: TableColumnWidth.DATETIME, }, | ||
| 166 | ], | ||
| 167 | data: [], | ||
| 168 | page: { | ||
| 169 | type: "normal", | ||
| 170 | rows: 0, | ||
| 171 | ...page.value, | ||
| 172 | }, | ||
| 173 | actionInfo: { | ||
| 174 | label: "操作", | ||
| 175 | type: "btn", | ||
| 176 | width: 270, | ||
| 177 | btns: (scope) => { | ||
| 178 | const row = scope.row | ||
| 179 | let btnsArr: any = [{ | ||
| 180 | label: "编辑", value: "edit", disabled: row.state === 1 || row.isExecute || row.execState == 1 || row.planType == 4, click: (scope) => { | ||
| 181 | router.push({ | ||
| 182 | name: 'assessTemplate', | ||
| 183 | query: { | ||
| 184 | guid: row.guid, | ||
| 185 | planName: row.planName | ||
| 186 | } | ||
| 187 | }); | ||
| 188 | } | ||
| 189 | }, | ||
| 190 | { | ||
| 191 | label: "查看结果", value: "resultView", disabled: row.execState == 1 || row.isExecute || row.execState == 0, click: (scope) => { | ||
| 192 | router.push({ | ||
| 193 | name: 'assessDetail', | ||
| 194 | query: { | ||
| 195 | planGuid: row.guid, | ||
| 196 | name: row.planName, | ||
| 197 | planExecGuid: row.planExecGuid | ||
| 198 | } | ||
| 199 | }); | ||
| 200 | } | ||
| 201 | }, | ||
| 202 | { | ||
| 203 | label: "日志", value: "path_log", disabled: row.execState == 0, click: (scope) => { | ||
| 204 | router.push({ | ||
| 205 | name: 'assessLog', | ||
| 206 | query: { | ||
| 207 | guid: row.guid, | ||
| 208 | name: row.planName | ||
| 209 | } | ||
| 210 | }); | ||
| 211 | } | ||
| 212 | }]; | ||
| 213 | if (row.isExecute) { | ||
| 214 | btnsArr.splice(0, 0, { label: "执行中...", value: "execute", disabled: true }) | ||
| 215 | } else { | ||
| 216 | btnsArr.splice(0, 0, { label: "手动执行", value: "execute", disabled: row.execState == 1 || row.state == 0 || row.planType == 4 }) | ||
| 217 | } | ||
| 218 | btnsArr.push({ label: "删除", value: "delete", disabled: row.isExecute || row.execState == 1 || row.state == 1 }); | ||
| 219 | return btnsArr | ||
| 220 | } | ||
| 221 | } | ||
| 222 | }); | ||
| 223 | |||
| 224 | const toSearch = (val: any, clear: boolean = false) => { | ||
| 225 | page.value.curr = 1; | ||
| 226 | if (clear) { | ||
| 227 | searchItemList.value.map(item => item.default = '') | ||
| 228 | page.value.planName = ''; | ||
| 229 | page.value.state = null; | ||
| 230 | getTableData(); | ||
| 231 | return; | ||
| 232 | } | ||
| 233 | page.value.planName = val.planName; | ||
| 234 | page.value.state = val.state; | ||
| 235 | getTableData(); | ||
| 236 | }; | ||
| 237 | |||
| 238 | /** 调用接口刷新获取方案表格数据 */ | ||
| 239 | const getTableData = () => { | ||
| 240 | tableInfo.value.loading = true; | ||
| 241 | getPlanList({ | ||
| 242 | pageIndex: page.value.curr, pageSize: page.value.limit, planName: page.value.planName, | ||
| 243 | planType: page.value.planType, | ||
| 244 | state: page.value.state | ||
| 245 | }).then((res: any) => { | ||
| 246 | tableInfo.value.loading = false; | ||
| 247 | if (res === undefined) { | ||
| 248 | return; | ||
| 249 | } | ||
| 250 | if (res.code == proxy.$passCode) { | ||
| 251 | const data = res.data || {} | ||
| 252 | tableInfo.value.data = data.records || [] | ||
| 253 | tableInfo.value.page.limit = data.pageSize | ||
| 254 | tableInfo.value.page.curr = data.pageIndex | ||
| 255 | tableInfo.value.page.rows = data.totalRows | ||
| 256 | } else { | ||
| 257 | ElMessage({ | ||
| 258 | type: 'error', | ||
| 259 | message: res.msg, | ||
| 260 | }) | ||
| 261 | } | ||
| 262 | }) | ||
| 263 | }; | ||
| 264 | |||
| 265 | /** 监听处理表格勾选事件。 */ | ||
| 266 | const tableSelectionChange = (val) => { | ||
| 267 | selectRowData.value = val.map((item) => item.guid); | ||
| 268 | }; | ||
| 269 | |||
| 270 | /** 监听处理分页事件。 */ | ||
| 271 | const tablePageChange = (info) => { | ||
| 272 | page.value.curr = Number(info.curr); | ||
| 273 | page.value.limit = Number(info.limit); | ||
| 274 | getTableData(); | ||
| 275 | }; | ||
| 276 | |||
| 277 | const tableSwitchBeforeChange = (scope, field, callback) => { | ||
| 278 | if (scope.row.isExecute || scope.row.execState == 1) { | ||
| 279 | ElMessage.warning('该方案正在执行中,无法下线'); | ||
| 280 | return; | ||
| 281 | } | ||
| 282 | const msg = `确定【${scope.row[field] == 1 ? '下线' : '上线'}】该方案吗?`; | ||
| 283 | ElMessageBox.confirm( | ||
| 284 | msg, | ||
| 285 | '提示', | ||
| 286 | { | ||
| 287 | confirmButtonText: '确定', | ||
| 288 | cancelButtonText: '取消', | ||
| 289 | type: 'warning', | ||
| 290 | } | ||
| 291 | ).then(() => { | ||
| 292 | const state = scope.row[field] == 1 ? 0 : 1 | ||
| 293 | const result = tableSwitchChange(state, scope, field) | ||
| 294 | callback(result) | ||
| 295 | }).catch(() => { | ||
| 296 | callback(false) | ||
| 297 | }) | ||
| 298 | } | ||
| 299 | |||
| 300 | const tableSwitchChange = (val, scope, field) => { | ||
| 301 | return new Promise((resolve, reject) => { | ||
| 302 | let params = { | ||
| 303 | guid: scope.row.guid, | ||
| 304 | state: val | ||
| 305 | } | ||
| 306 | updateQualityPlanState(params).then((res: any) => { | ||
| 307 | if (res.code == proxy.$passCode && res.data) { | ||
| 308 | page.value.curr = 1; | ||
| 309 | getTableData(); | ||
| 310 | ElMessage({ | ||
| 311 | type: "success", | ||
| 312 | message: `该方案${val == 1 ? '上线' : '下线'}成功`, | ||
| 313 | }); | ||
| 314 | resolve(true) | ||
| 315 | } else { | ||
| 316 | ElMessage({ | ||
| 317 | type: "error", | ||
| 318 | message: res.msg, | ||
| 319 | }); | ||
| 320 | getTableData(); | ||
| 321 | reject(false) | ||
| 322 | } | ||
| 323 | }).catch(() => { | ||
| 324 | getTableData(); | ||
| 325 | reject(false) | ||
| 326 | }) | ||
| 327 | }) | ||
| 328 | } | ||
| 329 | |||
| 330 | const tableBtnClick = (scope, btn) => { | ||
| 331 | const type = btn.value; | ||
| 332 | const row = scope.row; | ||
| 333 | currTableData.value = row; | ||
| 334 | if (type == "execute") { | ||
| 335 | row.isExecute = true | ||
| 336 | const guid = row.guid | ||
| 337 | executePlan(guid).then((res: any) => { | ||
| 338 | if (res.code == proxy.$passCode) { | ||
| 339 | getTableData(); | ||
| 340 | ElMessage({ | ||
| 341 | type: "success", | ||
| 342 | message: "手动执行提交成功", | ||
| 343 | }); | ||
| 344 | } else { | ||
| 345 | ElMessage({ | ||
| 346 | type: "error", | ||
| 347 | message: res.msg, | ||
| 348 | }); | ||
| 349 | } | ||
| 350 | row.isExecute = false | ||
| 351 | }).catch(() => { | ||
| 352 | row.isExecute = false | ||
| 353 | }) | ||
| 354 | } else if (type === 'delete') { | ||
| 355 | open(`此操作将永久删除 ${row.planName}, 是否继续?`, "warning"); | ||
| 356 | } else if (type == 'detail') {//查看详情 | ||
| 357 | if (row.planType != 4) { | ||
| 358 | router.push({ | ||
| 359 | name: 'assessTemplate', | ||
| 360 | query: { | ||
| 361 | guid: row.guid, | ||
| 362 | planName: row.planName, | ||
| 363 | detail: 1 | ||
| 364 | } | ||
| 365 | }); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | }; | ||
| 369 | |||
| 370 | /** 删除操作确认对话框。 */ | ||
| 371 | const open = (msg, type, isBatch = false) => { | ||
| 372 | ElMessageBox.confirm(msg, "提示", { | ||
| 373 | confirmButtonText: "确定", | ||
| 374 | cancelButtonText: "取消", | ||
| 375 | type: type, | ||
| 376 | }).then(() => { | ||
| 377 | let guids = [currTableData.value.guid] | ||
| 378 | if (isBatch) { | ||
| 379 | guids = selectRowData.value | ||
| 380 | } | ||
| 381 | deletePlan(guids).then((res: any) => { | ||
| 382 | if (res.code == proxy.$passCode) { | ||
| 383 | page.value.curr = 1; | ||
| 384 | getTableData(); | ||
| 385 | ElMessage({ | ||
| 386 | type: "success", | ||
| 387 | message: "删除成功", | ||
| 388 | }); | ||
| 389 | } else { | ||
| 390 | ElMessage({ | ||
| 391 | type: "error", | ||
| 392 | message: res.msg, | ||
| 393 | }); | ||
| 394 | } | ||
| 395 | }); | ||
| 396 | }); | ||
| 397 | }; | ||
| 398 | |||
| 399 | /** 树形点击节点事件处理。 */ | ||
| 400 | const nodeClick = (data) => { | ||
| 401 | treeSelectItem.value = data | ||
| 402 | if (data.children) { | ||
| 403 | page.value.planType = null; | ||
| 404 | } else { | ||
| 405 | page.value.planType = data.guid; | ||
| 406 | } | ||
| 407 | page.value.curr = 1; | ||
| 408 | searchItemList.value[0].default = ''; | ||
| 409 | searchItemList.value[1].default = null; | ||
| 410 | getTableData(); | ||
| 411 | } | ||
| 412 | |||
| 413 | onActivated(() => { | ||
| 414 | /** 若是新建评估方案跳转则默认选中对应的方案类型展示列表。 */ | ||
| 415 | if (dataQualityStore.planType) { | ||
| 416 | /** 若是编辑方案之后跳转,则只需要重新获取当前的表格数据即可。 */ | ||
| 417 | if (dataQualityStore.isUpdate) { | ||
| 418 | getTableData(); | ||
| 419 | dataQualityStore.setIsUpdate(false); | ||
| 420 | } else { | ||
| 421 | /** 若树选中的与默认方案类型一致,则不需要再选中树,只更新表格数据。 */ | ||
| 422 | if (treeSelectItem.value && treeSelectItem.value.guid == dataQualityStore.planType) { | ||
| 423 | treeInfo.value.currentNodeKey = dataQualityStore.planType; | ||
| 424 | getTableData(); | ||
| 425 | } else { | ||
| 426 | treeInfo.value.currentNodeKey = dataQualityStore.planType; | ||
| 427 | nextTick(() => { | ||
| 428 | nodeClick(treeInfo.value.data[0].children.find(c => c.guid == treeInfo.value.currentNodeKey)); | ||
| 429 | }) | ||
| 430 | } | ||
| 431 | } | ||
| 432 | dataQualityStore.setPlanType(null); | ||
| 433 | } | ||
| 434 | }) | ||
| 435 | |||
| 436 | const newCreatePlan = () => { | ||
| 437 | /** 新建评估方案时,若是左侧树选中的是方案类型,则新建时设置类型默认值与树选中值对应。 */ | ||
| 438 | if (treeSelectItem.value.guid != null && treeSelectItem.value.guid !== 0) { | ||
| 439 | dataQualityStore.setDefaultPlanType(treeSelectItem.value.guid); | ||
| 440 | } | ||
| 441 | router.push({ | ||
| 442 | name: 'assessTemplate' | ||
| 443 | }); | ||
| 444 | } | ||
| 445 | |||
| 446 | </script> | ||
| 447 | |||
| 448 | <style lang="scss" scoped> | ||
| 449 | .container_wrap { | ||
| 450 | padding: 0; | ||
| 451 | display: flex; | ||
| 452 | justify-content: space-between; | ||
| 453 | |||
| 454 | .box_left { | ||
| 455 | width: 200px; | ||
| 456 | box-shadow: 1px 0 0 0 #d9d9d9; | ||
| 457 | |||
| 458 | .tree_panel { | ||
| 459 | height: 100%; | ||
| 460 | padding-top: 0; | ||
| 461 | |||
| 462 | :deep(.el-tree) { | ||
| 463 | margin: 0; | ||
| 464 | overflow: hidden auto; | ||
| 465 | } | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 469 | .box_right { | ||
| 470 | width: calc(100% - 200px); | ||
| 471 | height: 100%; | ||
| 472 | padding: 0 16px; | ||
| 473 | overflow: hidden auto; | ||
| 474 | } | ||
| 475 | |||
| 476 | .panel_title { | ||
| 477 | line-height: 40px; | ||
| 478 | font-size: 16px; | ||
| 479 | font-weight: 600; | ||
| 480 | color: var(--el-color-regular); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | .table_tool_wrap { | ||
| 485 | width: 100%; | ||
| 486 | height: 84px; | ||
| 487 | |||
| 488 | .tools_btns { | ||
| 489 | padding: 0; | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | .table_panel_wrap { | ||
| 494 | width: 100%; | ||
| 495 | height: calc(100% - 84px); | ||
| 496 | } | ||
| 497 | </style> |
src/views/data_quality/qualityRules.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: qualityRules | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="qualityRules"> | ||
| 6 | import { ref } from 'vue' | ||
| 7 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 8 | import Tree from "@/components/Tree/index.vue"; | ||
| 9 | import TableTools from '@/components/Tools/table_tools.vue' | ||
| 10 | import Table from "@/components/Table/index.vue"; | ||
| 11 | import Dialog from '@/components/Dialog/index.vue' | ||
| 12 | import { useRouter } from "vue-router"; | ||
| 13 | import useCatchStore from "@/store/modules/catch"; | ||
| 14 | import { | ||
| 15 | getQualityTreeData, | ||
| 16 | getQualityGroupData, | ||
| 17 | deleteGroup, | ||
| 18 | updateQualityGroup, | ||
| 19 | addQualityGroup, | ||
| 20 | getQualityTable, | ||
| 21 | deleteQualityTable, | ||
| 22 | getQualityTableRule, | ||
| 23 | deleteQualityTableRule, | ||
| 24 | updateRuleBizState, | ||
| 25 | getDatabase, | ||
| 26 | getRuleTypeList | ||
| 27 | } from '@/api/modules/dataQuality'; | ||
| 28 | import useDataQualityStore from "@/store/modules/dataQuality"; | ||
| 29 | import { useValidator } from '@/hooks/useValidator'; | ||
| 30 | import { TableColumnWidth } from '@/utils/enum'; | ||
| 31 | import useDataCatalogStore from "@/store/modules/dataCatalog"; | ||
| 32 | |||
| 33 | const dataQualityStore = useDataQualityStore(); | ||
| 34 | const dataCatalogStore = useDataCatalogStore(); | ||
| 35 | |||
| 36 | const { proxy } = getCurrentInstance() as any; | ||
| 37 | const { orderNum, description } = useValidator(); | ||
| 38 | |||
| 39 | const router = useRouter(); | ||
| 40 | const cacheStore = useCatchStore(); | ||
| 41 | /** 可选择的质量规则列表。 */ | ||
| 42 | const ruleTypeList: any = ref([]); | ||
| 43 | |||
| 44 | /** 质量规则集表对象。 */ | ||
| 45 | const qualityModelTreeRef = ref(); | ||
| 46 | /** 树选中不同层级的,代表的类型, model, group, table */ | ||
| 47 | const treeType = ref('model') | ||
| 48 | const treeData = ref([ | ||
| 49 | { | ||
| 50 | guid: '1', | ||
| 51 | name: "数据质量规则集", | ||
| 52 | type: 0, | ||
| 53 | children: [], | ||
| 54 | } | ||
| 55 | ]) | ||
| 56 | |||
| 57 | const getQualityGroupTreePromise: any = ref(null); | ||
| 58 | |||
| 59 | /** 展示质量规则集,分组树形结构数据。 */ | ||
| 60 | const getQualityGroupTreeData = (groupGuid?: string) => { | ||
| 61 | treeInfo.value.loading = true; | ||
| 62 | return getQualityGroupTreePromise.value = getQualityTreeData(groupGuid ? groupGuid : null).then((res: any) => { | ||
| 63 | treeInfo.value.loading = false; | ||
| 64 | getQualityGroupTreePromise.value = null; | ||
| 65 | if (res.code == proxy.$passCode) { | ||
| 66 | //return res.data || []; | ||
| 67 | treeData.value[0].children = res.data || [] | ||
| 68 | } else { | ||
| 69 | ElMessage({ | ||
| 70 | type: 'error', | ||
| 71 | message: res.msg, | ||
| 72 | }) | ||
| 73 | } | ||
| 74 | }) | ||
| 75 | } | ||
| 76 | |||
| 77 | /** 质量模型树形信息。 */ | ||
| 78 | const treeInfo = ref({ | ||
| 79 | id: "data-quality-tree", | ||
| 80 | filter: true, | ||
| 81 | loading: false, | ||
| 82 | // expandOnNodeClick: false, // 定位会由于未展开而失败。 | ||
| 83 | queryValue: "", | ||
| 84 | queryPlaceholder: "输入名称搜索", | ||
| 85 | props: { | ||
| 86 | value: 'guid', | ||
| 87 | label: 'name', | ||
| 88 | isLeaf: 'isLeaf' | ||
| 89 | }, | ||
| 90 | prefix: { | ||
| 91 | type: 'prefixIcon' | ||
| 92 | }, | ||
| 93 | nodeKey: 'guid', | ||
| 94 | lazy: false, // 方便实现搜索,改为直接全部加载。 | ||
| 95 | expandedKey: ['1'], | ||
| 96 | currentNodeKey: '1', | ||
| 97 | data: treeData.value | ||
| 98 | }); | ||
| 99 | |||
| 100 | /** 指定分组下展示的质检表,表格搜索时下拉选择的数据源。 */ | ||
| 101 | const databaseList: any = ref([]); | ||
| 102 | |||
| 103 | /** 指定分组下展示的质检表,上方搜索项配置。 */ | ||
| 104 | const tableSearchItemList = ref([ | ||
| 105 | { | ||
| 106 | type: 'select', | ||
| 107 | label: '', | ||
| 108 | field: 'dataSourceGuid', | ||
| 109 | default: '', | ||
| 110 | placeholder: '数据源', | ||
| 111 | props: { | ||
| 112 | label: 'databaseNameZh', | ||
| 113 | value: 'guid' | ||
| 114 | }, | ||
| 115 | options: databaseList.value, | ||
| 116 | clearable: true, | ||
| 117 | visible: true | ||
| 118 | }, { | ||
| 119 | type: 'input', | ||
| 120 | label: '', | ||
| 121 | field: 'modelName', | ||
| 122 | maxlength: 50, | ||
| 123 | default: '', | ||
| 124 | placeholder: '表名', | ||
| 125 | clearable: true, | ||
| 126 | visible: true | ||
| 127 | } | ||
| 128 | ]) | ||
| 129 | |||
| 130 | /** 指定质检表展示的规则列表,上方搜索项配置。 */ | ||
| 131 | const searchItemList = ref([ | ||
| 132 | { | ||
| 133 | type: 'input', | ||
| 134 | label: '', | ||
| 135 | field: 'ruleName', | ||
| 136 | default: '', | ||
| 137 | maxlength: 50, | ||
| 138 | placeholder: '规则名称', | ||
| 139 | clearable: true, | ||
| 140 | visible: true | ||
| 141 | }, { | ||
| 142 | type: 'select', | ||
| 143 | label: '', | ||
| 144 | field: 'ruleField', | ||
| 145 | default: '', | ||
| 146 | placeholder: '规则字段', | ||
| 147 | options: [ | ||
| 148 | { label: '字段1', value: '1' }, | ||
| 149 | { label: '字段2', value: '2' }, | ||
| 150 | ], | ||
| 151 | clearable: true, | ||
| 152 | visible: false, //徐鹏接口未支持,数据量较少,没必要做。 | ||
| 153 | }, { | ||
| 154 | type: 'select', | ||
| 155 | label: '', | ||
| 156 | field: 'ruleCode', | ||
| 157 | default: '', | ||
| 158 | placeholder: '规则类型', | ||
| 159 | options: ruleTypeList.value, | ||
| 160 | props: { | ||
| 161 | value: 'ruleCode', | ||
| 162 | label: 'ruleName' | ||
| 163 | }, | ||
| 164 | clearable: true, | ||
| 165 | visible: true | ||
| 166 | } | ||
| 167 | ]) | ||
| 168 | |||
| 169 | const page = ref({ | ||
| 170 | limit: 50, | ||
| 171 | curr: 1, | ||
| 172 | sizes: [ | ||
| 173 | { label: "10", value: 10 }, | ||
| 174 | { label: "50", value: 50 }, | ||
| 175 | { label: "100", value: 100 }, | ||
| 176 | { label: "150", value: 150 }, | ||
| 177 | { label: "200", value: 200 }, | ||
| 178 | ], | ||
| 179 | modelGroupGuid: '', | ||
| 180 | dataSourceGuid: '', | ||
| 181 | modelName: '' | ||
| 182 | }); | ||
| 183 | |||
| 184 | /** 当前分组列表数据勾选选中的行,用于批量删除。 */ | ||
| 185 | const tableSelectRowData: any = ref([]); | ||
| 186 | const tableInfo = ref({ | ||
| 187 | id: 'quality-table', | ||
| 188 | multiple: true, | ||
| 189 | loading: false, | ||
| 190 | fields: [ | ||
| 191 | { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" }, | ||
| 192 | { label: "表名", field: "name", width: 150, type: 'text_btn', value: 'view', columClass: 'text_btn' }, | ||
| 193 | { label: "表英文名", field: "subjectName", width: 160 }, | ||
| 194 | { label: "数据源", field: "databaseName", width: 160 }, | ||
| 195 | { label: "规则数量", field: "ruleNum", width: 90, align: 'right' }, | ||
| 196 | { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME }, | ||
| 197 | { label: "修改时间", field: "updateTime", width: TableColumnWidth.DATETIME, }, | ||
| 198 | ], | ||
| 199 | data: [], | ||
| 200 | page: { | ||
| 201 | type: "normal", | ||
| 202 | rows: 0, | ||
| 203 | ...page.value, | ||
| 204 | }, | ||
| 205 | actionInfo: { | ||
| 206 | label: "操作", | ||
| 207 | type: "btn", | ||
| 208 | width: 215, | ||
| 209 | btns: [ | ||
| 210 | { label: "新建规则", value: "create" }, | ||
| 211 | { label: "删除", value: "delete" }, | ||
| 212 | { label: "查看表目录", value: "locateDataCatalog" }, | ||
| 213 | ], | ||
| 214 | } | ||
| 215 | }); | ||
| 216 | |||
| 217 | const groupPage = ref({ | ||
| 218 | limit: 50, | ||
| 219 | curr: 1, | ||
| 220 | sizes: [ | ||
| 221 | { label: "10", value: 10 }, | ||
| 222 | { label: "50", value: 50 }, | ||
| 223 | { label: "100", value: 100 }, | ||
| 224 | { label: "150", value: 150 }, | ||
| 225 | { label: "200", value: 200 }, | ||
| 226 | ], | ||
| 227 | }); | ||
| 228 | const groupSelectRowData = ref([]); | ||
| 229 | const groupTableInfo = ref({ | ||
| 230 | id: 'group-table', | ||
| 231 | //multiple: true, | ||
| 232 | loading: false, | ||
| 233 | fields: [ | ||
| 234 | { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", fixed: "left" }, | ||
| 235 | { label: "分组名称", field: "name", width: 150, type: 'text_btn', value: 'locate', columClass: 'text_btn' }, | ||
| 236 | { label: "排序", field: 'orderNum', width: TableColumnWidth.INDEX, align: "center" }, | ||
| 237 | { label: "质检表数量", field: "qualityModelNum", width: 120, align: 'right' }, | ||
| 238 | { | ||
| 239 | label: "生效/总规则数", field: "ruleNum", width: 120, align: 'right', getName: (scope) => { | ||
| 240 | let row = scope.row; | ||
| 241 | return `${row.effectRuleNum}/${row.ruleNum}`; | ||
| 242 | } | ||
| 243 | }, | ||
| 244 | { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME }, | ||
| 245 | { label: "修改时间", field: "updateTime", width: TableColumnWidth.DATETIME, }, | ||
| 246 | { label: "描述", field: "description", width: TableColumnWidth.DESCRIPTION } | ||
| 247 | ], | ||
| 248 | data: [], | ||
| 249 | page: { | ||
| 250 | type: "normal", | ||
| 251 | rows: 0, | ||
| 252 | ...groupPage.value, | ||
| 253 | }, | ||
| 254 | actionInfo: { | ||
| 255 | label: "操作", | ||
| 256 | type: "btn", | ||
| 257 | width: 92, | ||
| 258 | btns: [ | ||
| 259 | { label: "编辑", value: "edit" }, | ||
| 260 | { label: "删除", value: "delete" }, | ||
| 261 | ], | ||
| 262 | } | ||
| 263 | }); | ||
| 264 | |||
| 265 | const formItems: any = ref([ | ||
| 266 | { | ||
| 267 | label: '分组名称', | ||
| 268 | type: 'input', | ||
| 269 | placeholder: '请输入', | ||
| 270 | field: 'name', | ||
| 271 | default: '', | ||
| 272 | maxlength: 50, | ||
| 273 | required: true | ||
| 274 | }, | ||
| 275 | { | ||
| 276 | label: '排序', | ||
| 277 | type: 'input', | ||
| 278 | placeholder: '由数字组成', | ||
| 279 | field: 'orderNum', | ||
| 280 | inputType: 'integerNumber', | ||
| 281 | default: '', | ||
| 282 | maxlength: 6, | ||
| 283 | required: true | ||
| 284 | }, { | ||
| 285 | label: '描述', | ||
| 286 | type: 'textarea', | ||
| 287 | placeholder: '请输入', | ||
| 288 | field: 'description', | ||
| 289 | default: '', | ||
| 290 | block: true, | ||
| 291 | required: false, | ||
| 292 | }, | ||
| 293 | ]) | ||
| 294 | |||
| 295 | const formRules = ref({ | ||
| 296 | name: [ | ||
| 297 | { required: true, trigger: 'blur', message: "请填写分组名称" } | ||
| 298 | ], | ||
| 299 | orderNum: [orderNum()], | ||
| 300 | description: [description()] | ||
| 301 | }); | ||
| 302 | |||
| 303 | /** 新建分组对话框。 */ | ||
| 304 | const dialogInfo = ref({ | ||
| 305 | visible: false, | ||
| 306 | size: 700, | ||
| 307 | direction: "column", | ||
| 308 | header: { | ||
| 309 | title: "", | ||
| 310 | }, | ||
| 311 | type: '', | ||
| 312 | contents: [ | ||
| 313 | { | ||
| 314 | type: 'form', | ||
| 315 | title: '', | ||
| 316 | formInfo: { | ||
| 317 | id: 'add-staff-form', | ||
| 318 | items: formItems.value, | ||
| 319 | rules: formRules.value | ||
| 320 | } | ||
| 321 | } | ||
| 322 | ], | ||
| 323 | footer: { | ||
| 324 | btns: [ | ||
| 325 | { type: "default", label: "取消", value: "cancel" }, | ||
| 326 | { type: "primary", label: "确定", value: "submit" }, | ||
| 327 | ], | ||
| 328 | }, | ||
| 329 | }); | ||
| 330 | |||
| 331 | /** 根据树形选择的第一层级显示对应的分组数据,用表格分页展示。 */ | ||
| 332 | const getGroupTableData = () => { | ||
| 333 | groupTableInfo.value.loading = true; | ||
| 334 | getQualityGroupData({ pageIndex: groupPage.value.curr, pageSize: groupPage.value.limit }).then((res: any) => { | ||
| 335 | groupTableInfo.value.loading = false; | ||
| 336 | if (res.code == proxy.$passCode) { | ||
| 337 | const data = res.data || {}; | ||
| 338 | groupTableInfo.value.data = data.records ?? []; | ||
| 339 | groupTableInfo.value.page.curr = data.pageIndex; | ||
| 340 | groupTableInfo.value.page.rows = data.totalRows; | ||
| 341 | } else { | ||
| 342 | ElMessage({ | ||
| 343 | type: 'error', | ||
| 344 | message: res.msg, | ||
| 345 | }) | ||
| 346 | } | ||
| 347 | }) | ||
| 348 | }; | ||
| 349 | |||
| 350 | const getTableData = () => { | ||
| 351 | tableInfo.value.loading = true; | ||
| 352 | getQualityTable({ pageIndex: page.value.curr, pageSize: page.value.limit, modelGroupGuid: page.value.modelGroupGuid, dataSourceGuid: page.value.dataSourceGuid, name: page.value.modelName }).then((res: any) => { | ||
| 353 | tableInfo.value.loading = false; | ||
| 354 | if (res.code == proxy.$passCode) { | ||
| 355 | const data = res.data || {}; | ||
| 356 | tableInfo.value.data = data.records ?? []; | ||
| 357 | tableInfo.value.page.curr = data.pageIndex; | ||
| 358 | tableInfo.value.page.rows = data.totalRows; | ||
| 359 | } else { | ||
| 360 | ElMessage.error(res.msg); | ||
| 361 | } | ||
| 362 | }) | ||
| 363 | } | ||
| 364 | |||
| 365 | const getRuleTableData = () => { | ||
| 366 | ruleTableInfo.value.loading = true; | ||
| 367 | getQualityTableRule({ modelGuid: lastSelectNode.value.data.guid, ruleName: modelRulesSerchParams.value.ruleName, ruleCode: modelRulesSerchParams.value.ruleCode }).then((res: any) => { | ||
| 368 | ruleTableInfo.value.loading = false; | ||
| 369 | if (res.code == proxy.$passCode) { | ||
| 370 | ruleTableInfo.value.data = res.data || []; | ||
| 371 | } else { | ||
| 372 | ElMessage.error(res.msg); | ||
| 373 | } | ||
| 374 | }) | ||
| 375 | } | ||
| 376 | |||
| 377 | const tableSelectionChange = (val) => { | ||
| 378 | tableSelectRowData.value = val; | ||
| 379 | }; | ||
| 380 | |||
| 381 | const tablePageChange = (info) => { | ||
| 382 | page.value.curr = Number(info.curr); | ||
| 383 | page.value.limit = Number(info.limit); | ||
| 384 | tableInfo.value.page.limit = page.value.limit; | ||
| 385 | tableInfo.value.page.curr = page.value.curr; | ||
| 386 | getTableData(); | ||
| 387 | }; | ||
| 388 | |||
| 389 | /** 当前选中的规则行,用于操作列按钮。 */ | ||
| 390 | const currTableData: any = ref({}); | ||
| 391 | const tableBtnClick = (scope, btn) => { | ||
| 392 | const type = btn.value; | ||
| 393 | const row = scope.row; | ||
| 394 | currTableData.value = row; | ||
| 395 | if (type == "view") { | ||
| 396 | if (!qualityModelTreeRef.value) { | ||
| 397 | return; | ||
| 398 | } | ||
| 399 | qualityModelTreeRef.value.setCurrentKey(row.guid, true); | ||
| 400 | } else if (type == 'create') { | ||
| 401 | router.push({ | ||
| 402 | name: 'ruleTemplate', | ||
| 403 | query: { | ||
| 404 | modelGuid: row.guid, | ||
| 405 | name: row.name | ||
| 406 | } | ||
| 407 | }); | ||
| 408 | } else if (type == "delete") { | ||
| 409 | open("此操作将永久删除, 是否继续?", "warning"); | ||
| 410 | } else if (type == 'locateDataCatalog') { | ||
| 411 | dataCatalogStore.setLocateSubjectName(row.name); | ||
| 412 | router.push({ | ||
| 413 | name: 'dataWarehouse' | ||
| 414 | }); | ||
| 415 | } | ||
| 416 | }; | ||
| 417 | |||
| 418 | const open = (msg, type, isBatch = false) => { | ||
| 419 | ElMessageBox.confirm(msg, "提示", { | ||
| 420 | confirmButtonText: "确定", | ||
| 421 | cancelButtonText: "取消", | ||
| 422 | type: type, | ||
| 423 | }).then(() => { | ||
| 424 | let guids = [currTableData.value.guid] | ||
| 425 | if (isBatch) { | ||
| 426 | guids = tableSelectRowData.value.map(s => s.guid); | ||
| 427 | } | ||
| 428 | deleteQualityTable(guids).then((res: any) => { | ||
| 429 | if (res.code == proxy.$passCode) { | ||
| 430 | page.value.curr = 1; | ||
| 431 | getTableData(); | ||
| 432 | let node = qualityModelTreeRef.value.treeRef.store.nodesMap[currTableData.value.modelGroupGuid]; | ||
| 433 | node.loaded = false; | ||
| 434 | node.expand(); | ||
| 435 | ElMessage.success('删除成功'); | ||
| 436 | } else { | ||
| 437 | ElMessage.error(res.msg); | ||
| 438 | } | ||
| 439 | }); | ||
| 440 | }); | ||
| 441 | }; | ||
| 442 | |||
| 443 | const batchingDelete = () => { | ||
| 444 | if (tableSelectRowData.value.length == 0) { | ||
| 445 | ElMessage({ | ||
| 446 | type: 'error', | ||
| 447 | message: '请选择需要删除的数据', | ||
| 448 | }) | ||
| 449 | return | ||
| 450 | } | ||
| 451 | open("此操作将永久删除, 是否继续?", "warning", true); | ||
| 452 | }; | ||
| 453 | |||
| 454 | const groupTableSelectionChange = (val) => { | ||
| 455 | groupSelectRowData.value = val; | ||
| 456 | }; | ||
| 457 | |||
| 458 | const groupTablePageChange = (info) => { | ||
| 459 | groupPage.value.curr = Number(info.curr); | ||
| 460 | groupPage.value.limit = Number(info.limit); | ||
| 461 | groupTableInfo.value.page.limit = groupPage.value.limit; | ||
| 462 | groupTableInfo.value.page.curr = groupPage.value.curr; | ||
| 463 | getGroupTableData(); | ||
| 464 | }; | ||
| 465 | |||
| 466 | const currGroupTableData: any = ref({}); | ||
| 467 | const groupTableBtnClick = (scope, btn) => { | ||
| 468 | const type = btn.value; | ||
| 469 | const row = scope.row; | ||
| 470 | currGroupTableData.value = row; | ||
| 471 | if (type == "edit") { | ||
| 472 | dialogInfo.value.visible = true; | ||
| 473 | dialogInfo.value.header.title = "编辑分组"; | ||
| 474 | dialogInfo.value.type = type | ||
| 475 | formItems.value.map(item => { | ||
| 476 | item.default = row[item.field]; | ||
| 477 | }); | ||
| 478 | } else if (type == "delete") { | ||
| 479 | ElMessageBox.confirm('此操作将永久删除该分组,确认删除吗?', "提示", { | ||
| 480 | confirmButtonText: "确定", | ||
| 481 | cancelButtonText: "取消", | ||
| 482 | type: type, | ||
| 483 | }).then(() => { | ||
| 484 | deleteGroup([row.guid]).then((res: any) => { | ||
| 485 | if (res.code == proxy.$passCode) { | ||
| 486 | groupPage.value.curr = 1; | ||
| 487 | getGroupTableData(); | ||
| 488 | ElMessage({ | ||
| 489 | type: "success", | ||
| 490 | message: "删除分组成功", | ||
| 491 | }); | ||
| 492 | } else { | ||
| 493 | ElMessage({ | ||
| 494 | type: "error", | ||
| 495 | message: res.msg, | ||
| 496 | }); | ||
| 497 | } | ||
| 498 | }); | ||
| 499 | }); | ||
| 500 | } else if (type == 'locate') { | ||
| 501 | qualityModelTreeRef.value.setCurrentKey(row.guid, true); | ||
| 502 | let node = qualityModelTreeRef.value.treeRef.store.nodesMap[row.guid]; | ||
| 503 | node && node.expand(); | ||
| 504 | } | ||
| 505 | }; | ||
| 506 | |||
| 507 | const ruleTableInfo = ref({ | ||
| 508 | id: 'rule-table', | ||
| 509 | loading: false, | ||
| 510 | fields: [ | ||
| 511 | { label: "规则名称", field: "ruleConfName", width: 150 }, | ||
| 512 | { label: "规则大类", field: "largeCategoryName", width: 120 }, | ||
| 513 | { label: "规则小类", field: "smallCategoryName", width: 140 }, | ||
| 514 | { label: "规则类型", field: "ruleName", width: 120 }, | ||
| 515 | { label: '状态', field: 'bizState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'S', switchWidth: 56, width: 100, align: 'center' }, | ||
| 516 | { label: "字段", field: "ruleField", width: 160 }, | ||
| 517 | { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME }, | ||
| 518 | { label: "修改时间", field: "updateTime", width: TableColumnWidth.DATETIME, }, | ||
| 519 | ], | ||
| 520 | data: [], | ||
| 521 | showPage: false, | ||
| 522 | actionInfo: { | ||
| 523 | label: "操作", | ||
| 524 | type: "btn", | ||
| 525 | width: 100, | ||
| 526 | btns: (scope) => { | ||
| 527 | return [ | ||
| 528 | { label: "编辑", value: "edit" }, | ||
| 529 | { label: "删除", value: "delete" }, | ||
| 530 | ] | ||
| 531 | } | ||
| 532 | } | ||
| 533 | }); | ||
| 534 | |||
| 535 | const ruleTableSwitchBeforeChange = (scope, field, callback) => { | ||
| 536 | let stateName = scope.row[field] == 'Y' ? '停用' : '启用'; | ||
| 537 | const msg = `确定【${stateName}】该规则吗?` | ||
| 538 | ElMessageBox.confirm( | ||
| 539 | msg, | ||
| 540 | '提示', | ||
| 541 | { | ||
| 542 | confirmButtonText: '确定', | ||
| 543 | cancelButtonText: '取消', | ||
| 544 | type: 'warning', | ||
| 545 | } | ||
| 546 | ).then(() => { | ||
| 547 | const state = scope.row[field] == 'Y' ? 'S' : 'Y'; | ||
| 548 | const result = ruleTableSwitchChange(state, scope, field) | ||
| 549 | callback(result) | ||
| 550 | }).catch(() => { | ||
| 551 | callback(false) | ||
| 552 | }) | ||
| 553 | } | ||
| 554 | |||
| 555 | const ruleTableSwitchChange = (val, scope, field) => { | ||
| 556 | return new Promise((resolve, reject) => { | ||
| 557 | let params = { | ||
| 558 | ruleConfGuid: scope.row.guid, | ||
| 559 | bizState: val | ||
| 560 | } | ||
| 561 | updateRuleBizState(params).then((res: any) => { | ||
| 562 | if (res.code == proxy.$passCode && res.data) { | ||
| 563 | getRuleTableData(); | ||
| 564 | ElMessage({ | ||
| 565 | type: "success", | ||
| 566 | message: `该规则 ${val == 'Y' ? '启用' : '停用'} 成功`, | ||
| 567 | }); | ||
| 568 | resolve(true) | ||
| 569 | } else { | ||
| 570 | ElMessage({ | ||
| 571 | type: "error", | ||
| 572 | message: res.msg, | ||
| 573 | }); | ||
| 574 | getRuleTableData(); | ||
| 575 | reject(false) | ||
| 576 | } | ||
| 577 | }).catch(() => { | ||
| 578 | getRuleTableData(); | ||
| 579 | reject(false) | ||
| 580 | }) | ||
| 581 | }) | ||
| 582 | } | ||
| 583 | |||
| 584 | const currTableRuleData: any = ref({}); | ||
| 585 | const ruleTableBtnClick = (scope, btn) => { | ||
| 586 | const type = btn.value; | ||
| 587 | const row = scope.row; | ||
| 588 | currTableRuleData.value = row; | ||
| 589 | if (type == "edit") { | ||
| 590 | router.push({ | ||
| 591 | name: 'ruleModelEdit', | ||
| 592 | query: { | ||
| 593 | guid: row.guid | ||
| 594 | } | ||
| 595 | }); | ||
| 596 | } else if (type == "delete") { | ||
| 597 | ruleOpen("此操作将永久删除该质检表, 是否继续?", "warning"); | ||
| 598 | } | ||
| 599 | }; | ||
| 600 | |||
| 601 | const ruleOpen = (msg, type, isBatch = false) => { | ||
| 602 | ElMessageBox.confirm(msg, "提示", { | ||
| 603 | confirmButtonText: "确定", | ||
| 604 | cancelButtonText: "取消", | ||
| 605 | type: type, | ||
| 606 | }).then(() => { | ||
| 607 | let guids = currTableRuleData.value.guid; | ||
| 608 | deleteQualityTableRule(guids).then((res: any) => { | ||
| 609 | if (res.code == proxy.$passCode) { | ||
| 610 | getRuleTableData(); | ||
| 611 | let node = qualityModelTreeRef.value.treeRef.store.nodesMap[lastSelectNode.value.data.guid]; | ||
| 612 | node.loaded = false; | ||
| 613 | node.expand(); | ||
| 614 | ElMessage.success('删除质检表成功'); | ||
| 615 | } else { | ||
| 616 | ElMessage.error(res.msg); | ||
| 617 | } | ||
| 618 | }); | ||
| 619 | }); | ||
| 620 | }; | ||
| 621 | |||
| 622 | /** 新建分组 */ | ||
| 623 | const clickCreateGroup = () => { | ||
| 624 | dialogInfo.value.visible = true; | ||
| 625 | dialogInfo.value.header.title = "新建分组"; | ||
| 626 | dialogInfo.value.type = 'add'; | ||
| 627 | formItems.value.map(item => { | ||
| 628 | if (item.field === 'orderNum') { | ||
| 629 | item.default = null; | ||
| 630 | } else { | ||
| 631 | item.default = ""; | ||
| 632 | } | ||
| 633 | }); | ||
| 634 | } | ||
| 635 | |||
| 636 | /** 新建质检表 */ | ||
| 637 | const clickCreateTable = () => { | ||
| 638 | router.push({ | ||
| 639 | name: 'ruleModel', | ||
| 640 | query: { | ||
| 641 | groupGuid: page.value.modelGroupGuid, | ||
| 642 | name: lastSelectNode.value.data.name | ||
| 643 | } | ||
| 644 | }); | ||
| 645 | } | ||
| 646 | |||
| 647 | /** 新建规则 */ | ||
| 648 | const clickCreateRule = () => { | ||
| 649 | router.push({ | ||
| 650 | name: 'ruleTemplate', | ||
| 651 | query: { | ||
| 652 | modelGuid: lastSelectNode.value.data.guid, | ||
| 653 | name: lastSelectNode.value.data.name | ||
| 654 | } | ||
| 655 | }); | ||
| 656 | } | ||
| 657 | |||
| 658 | /** 跳转配置质量评估方案页面。 */ | ||
| 659 | const configPlan = () => { | ||
| 660 | if (lastSelectNode.value.data.type == 1) {//按照分组配置评估方案。 | ||
| 661 | router.push({ | ||
| 662 | name: 'assessTemplate', | ||
| 663 | query: { | ||
| 664 | groupGuid: lastSelectNode.value.data.guid | ||
| 665 | } | ||
| 666 | }); | ||
| 667 | } else { | ||
| 668 | router.push({ | ||
| 669 | name: 'assessTemplate', | ||
| 670 | query: { | ||
| 671 | modelGuid: lastSelectNode.value.data.guid, | ||
| 672 | groupGuid: lastSelectNode.value.parent.data.guid | ||
| 673 | } | ||
| 674 | }); | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | const toPath = (url) => { | ||
| 679 | router.push({ | ||
| 680 | path: url, | ||
| 681 | }); | ||
| 682 | } | ||
| 683 | |||
| 684 | const lastSelectNode: any = ref({}); | ||
| 685 | |||
| 686 | const modelRulesSerchParams = ref({ | ||
| 687 | ruleName: '', | ||
| 688 | ruleCode: '', | ||
| 689 | ruleField: '' | ||
| 690 | }); | ||
| 691 | |||
| 692 | const handleNodeSelectChange = (node) => { | ||
| 693 | console.log(node); | ||
| 694 | let data = node.data; | ||
| 695 | lastSelectNode.value = node; | ||
| 696 | if (node.level === 1) { | ||
| 697 | treeType.value = 'model'; | ||
| 698 | groupPage.value.curr = 1; | ||
| 699 | getGroupTableData(); | ||
| 700 | } else if (node.level === 2) { | ||
| 701 | treeType.value = 'group'; | ||
| 702 | page.value.curr = 1; | ||
| 703 | page.value.modelGroupGuid = data.guid; | ||
| 704 | toTableSearch(groupTableTools.value?.toolSearch?.formInline || {}, false); | ||
| 705 | } else if (node.level === 3) { | ||
| 706 | treeType.value = 'table'; | ||
| 707 | toRuleSearch(ruleTableTools.value?.toolSearch?.formInline || {}, false); | ||
| 708 | } | ||
| 709 | |||
| 710 | } | ||
| 711 | |||
| 712 | const groupTableTools: any = ref(null); | ||
| 713 | |||
| 714 | const toTableSearch = (val, clear) => { | ||
| 715 | page.value.curr = 1; | ||
| 716 | if (clear) { | ||
| 717 | page.value.modelName = ''; | ||
| 718 | page.value.dataSourceGuid = ''; | ||
| 719 | getTableData(); | ||
| 720 | return; | ||
| 721 | } | ||
| 722 | page.value.modelName = val.modelName; | ||
| 723 | page.value.dataSourceGuid = val.dataSourceGuid; | ||
| 724 | getTableData(); | ||
| 725 | } | ||
| 726 | |||
| 727 | const ruleTableTools: any = ref(null); | ||
| 728 | const toRuleSearch = (val, clear) => { | ||
| 729 | if (clear) { | ||
| 730 | modelRulesSerchParams.value = { | ||
| 731 | ruleName: '', | ||
| 732 | ruleCode: '', | ||
| 733 | ruleField: '' | ||
| 734 | }; | ||
| 735 | } else { | ||
| 736 | modelRulesSerchParams.value = val; | ||
| 737 | } | ||
| 738 | getRuleTableData(); | ||
| 739 | } | ||
| 740 | |||
| 741 | let submitPromise: any = ref(null); | ||
| 742 | let editSubmitPromise: any = ref(null); | ||
| 743 | |||
| 744 | /** 新建分组对话框确定。 */ | ||
| 745 | const dialogBtnClick = (btn, info) => { | ||
| 746 | if (btn.value == 'submit') { | ||
| 747 | if (dialogInfo.value.type == 'add') { | ||
| 748 | if (submitPromise.value) { | ||
| 749 | return; | ||
| 750 | } | ||
| 751 | submitPromise.value = addQualityGroup(info).then((res: any) => { | ||
| 752 | submitPromise.value = null; | ||
| 753 | if (res.code == proxy.$passCode) { | ||
| 754 | groupPage.value.curr = 1; | ||
| 755 | getGroupTableData(); | ||
| 756 | ElMessage({ | ||
| 757 | type: 'success', | ||
| 758 | message: '新建分组成功' | ||
| 759 | }) | ||
| 760 | dialogInfo.value.visible = false; | ||
| 761 | } else { | ||
| 762 | ElMessage({ | ||
| 763 | type: 'error', | ||
| 764 | message: res.msg, | ||
| 765 | }) | ||
| 766 | } | ||
| 767 | }) | ||
| 768 | } else { | ||
| 769 | const params = { ...info }; | ||
| 770 | params.guid = currGroupTableData.value.guid; | ||
| 771 | if (editSubmitPromise.value) { | ||
| 772 | return; | ||
| 773 | } | ||
| 774 | editSubmitPromise.value = updateQualityGroup(params).then((res: any) => { | ||
| 775 | editSubmitPromise.value = null; | ||
| 776 | if (res.code == proxy.$passCode) { | ||
| 777 | getGroupTableData(); | ||
| 778 | ElMessage({ | ||
| 779 | type: 'success', | ||
| 780 | message: '编辑分组成功' | ||
| 781 | }) | ||
| 782 | dialogInfo.value.visible = false; | ||
| 783 | } else { | ||
| 784 | ElMessage({ | ||
| 785 | type: 'error', | ||
| 786 | message: res.msg, | ||
| 787 | }) | ||
| 788 | } | ||
| 789 | }) | ||
| 790 | } | ||
| 791 | } else if (btn.value == 'cancel') { | ||
| 792 | dialogInfo.value.visible = false; | ||
| 793 | } | ||
| 794 | }; | ||
| 795 | |||
| 796 | /** 导入按分组 */ | ||
| 797 | const uploadDialog = () => { | ||
| 798 | const info = { | ||
| 799 | type: 'qualityModelGroup' | ||
| 800 | } | ||
| 801 | cacheStore.setCatch('uploadSetting', info) | ||
| 802 | nextTick(() => { | ||
| 803 | router.push({ | ||
| 804 | name: 'importFiles', | ||
| 805 | }); | ||
| 806 | }) | ||
| 807 | } | ||
| 808 | |||
| 809 | /** 导出按照分组。 */ | ||
| 810 | const exportData = () => { | ||
| 811 | if (groupSelectRowData.value.length == 0) { | ||
| 812 | // const fieldTemplate = "/files/set.xlsx"; | ||
| 813 | // downFile(fieldTemplate, '标准集模板.xlsx') | ||
| 814 | } else { | ||
| 815 | // exportDataStandardSet(groupSelectRowData.value).then((res: any) => { | ||
| 816 | // download(res, '标准集.xls', 'excel') | ||
| 817 | // }); | ||
| 818 | } | ||
| 819 | } | ||
| 820 | |||
| 821 | /** 导入质检规则 */ | ||
| 822 | const ruleUploadDialog = () => { | ||
| 823 | const info = { | ||
| 824 | type: 'qualityRule' | ||
| 825 | } | ||
| 826 | cacheStore.setCatch('uploadSetting', info) | ||
| 827 | nextTick(() => { | ||
| 828 | router.push({ | ||
| 829 | name: 'importFiles', | ||
| 830 | }); | ||
| 831 | }) | ||
| 832 | } | ||
| 833 | |||
| 834 | /** 导出质检规则。 */ | ||
| 835 | const ruleExportData = () => { | ||
| 836 | if (groupSelectRowData.value.length == 0) { | ||
| 837 | // const fieldTemplate = "/files/set.xlsx"; | ||
| 838 | // downFile(fieldTemplate, '标准集模板.xlsx') | ||
| 839 | } else { | ||
| 840 | // exportDataStandardSet(groupSelectRowData.value).then((res: any) => { | ||
| 841 | // download(res, '标准集.xls', 'excel') | ||
| 842 | // }); | ||
| 843 | } | ||
| 844 | } | ||
| 845 | |||
| 846 | const getDatabaseList = () => { | ||
| 847 | getDatabase({ connectStatus: 1 }).then((res: any) => { | ||
| 848 | databaseList.value = []; | ||
| 849 | if (res.code == proxy.$passCode) { | ||
| 850 | databaseList.value = res.data || []; | ||
| 851 | tableSearchItemList.value[0].options = databaseList.value; | ||
| 852 | } | ||
| 853 | }) | ||
| 854 | }; | ||
| 855 | |||
| 856 | onActivated(async () => { | ||
| 857 | if (dataQualityStore.modelGroupGuid) { | ||
| 858 | await nextTick(); | ||
| 859 | if (getQualityGroupTreePromise.value) { | ||
| 860 | getQualityGroupTreePromise.value.then(() => { | ||
| 861 | qualityModelTreeRef.value.setCurrentKey(dataQualityStore.modelGroupGuid); | ||
| 862 | if (lastSelectNode.value && lastSelectNode.value.data.guid == dataQualityStore.modelGroupGuid) { | ||
| 863 | getTableData(); | ||
| 864 | } | ||
| 865 | let node = qualityModelTreeRef.value.treeRef.store.nodesMap[dataQualityStore.modelGroupGuid]; | ||
| 866 | node?.expand(); | ||
| 867 | dataQualityStore.set(null); | ||
| 868 | }); | ||
| 869 | } else { | ||
| 870 | qualityModelTreeRef.value.setCurrentKey(dataQualityStore.modelGroupGuid); | ||
| 871 | if (lastSelectNode.value && lastSelectNode.value.data.guid == dataQualityStore.modelGroupGuid) { | ||
| 872 | getTableData(); | ||
| 873 | } | ||
| 874 | let node = qualityModelTreeRef.value.treeRef.store.nodesMap[dataQualityStore.modelGroupGuid]; | ||
| 875 | node.expand(); | ||
| 876 | dataQualityStore.set(null); | ||
| 877 | } | ||
| 878 | } | ||
| 879 | if (dataQualityStore.modelGuid) { | ||
| 880 | if (lastSelectNode.value?.data && lastSelectNode.value.data.guid == dataQualityStore.modelGuid) { | ||
| 881 | qualityModelTreeRef.value.setCurrentKey(dataQualityStore.modelGuid); | ||
| 882 | getRuleTableData(); | ||
| 883 | } else { | ||
| 884 | if (getQualityGroupTreePromise.value) { | ||
| 885 | getQualityGroupTreePromise.value.then(() => { | ||
| 886 | nextTick(() => { | ||
| 887 | qualityModelTreeRef.value.setCurrentKey(dataQualityStore.modelGuid); | ||
| 888 | dataQualityStore.setModelGuid(null); | ||
| 889 | }) | ||
| 890 | }) | ||
| 891 | } else { | ||
| 892 | nextTick(() => { | ||
| 893 | qualityModelTreeRef.value.setCurrentKey(dataQualityStore.modelGuid); | ||
| 894 | dataQualityStore.setModelGuid(null); | ||
| 895 | }) | ||
| 896 | } | ||
| 897 | } | ||
| 898 | } | ||
| 899 | }) | ||
| 900 | |||
| 901 | onBeforeMount(() => { | ||
| 902 | getQualityGroupTreeData(); | ||
| 903 | getGroupTableData(); | ||
| 904 | getDatabaseList(); | ||
| 905 | getRuleTypeList().then((res: any) => { | ||
| 906 | if (res.code == proxy.$passCode) { | ||
| 907 | ruleTypeList.value = res.data?.map((d: any) => { | ||
| 908 | d.label = d.ruleName; | ||
| 909 | d.value = d.ruleCode; | ||
| 910 | return d; | ||
| 911 | }) || []; | ||
| 912 | searchItemList.value[2].options = ruleTypeList.value; | ||
| 913 | } else { | ||
| 914 | ElMessage.error(res.msg); | ||
| 915 | } | ||
| 916 | }) | ||
| 917 | }) | ||
| 918 | |||
| 919 | </script> | ||
| 920 | |||
| 921 | <template> | ||
| 922 | <div class="container_wrap flex"> | ||
| 923 | <div class="box_left aside_wrap"> | ||
| 924 | <div class="aside_title">质量规则集列表</div> | ||
| 925 | <Tree ref="qualityModelTreeRef" :treeInfo="treeInfo" @nodeSelectChange="handleNodeSelectChange" /> | ||
| 926 | </div> | ||
| 927 | <div class="box_right"> | ||
| 928 | <div class="table_tool_wrap"> | ||
| 929 | <TableTools ref="groupTableTools" v-if="treeType == 'group'" :init="false" :searchItems="tableSearchItemList" | ||
| 930 | :searchId="'quality-table-search'" @search="toTableSearch" /> | ||
| 931 | <TableTools ref="ruleTableTools" v-if="treeType == 'table'" :init="false" :searchItems="searchItemList" | ||
| 932 | :searchId="'table-rule-search'" @search="toRuleSearch" /> | ||
| 933 | <div class="tools_btns" v-if="treeType == 'model'"> | ||
| 934 | <el-button type="primary" @click="clickCreateGroup">新建分组</el-button> | ||
| 935 | <!-- <el-button @click="uploadDialog">导入</el-button> | ||
| 936 | <el-button @click="exportData">导出</el-button> --> | ||
| 937 | </div> | ||
| 938 | <div class="tools_btns" style="padding-bottom: 8px;" v-else-if="treeType == 'group'"> | ||
| 939 | <el-button type="primary" @click="clickCreateTable">新建质检表</el-button> | ||
| 940 | <el-button @click="batchingDelete">批量删除</el-button> | ||
| 941 | <el-button v-if="lastSelectNode?.data?.isHaveModel" @click="configPlan">配置评估方案</el-button> | ||
| 942 | <!-- <el-button @click="ruleUploadDialog">导入</el-button> | ||
| 943 | <el-button @click="ruleExportData">导出</el-button> --> | ||
| 944 | </div> | ||
| 945 | <div class="tools_btns" style="padding-bottom: 8px;" v-else> | ||
| 946 | <el-button type="primary" @click="clickCreateRule">新建规则</el-button> | ||
| 947 | <el-button @click="configPlan">配置评估方案</el-button> | ||
| 948 | </div> | ||
| 949 | </div> | ||
| 950 | <div class="table_panel_wrap" | ||
| 951 | :style="{ height: treeType === 'model' ? 'calc(100% - 44px)' : (treeType === 'table' ? 'calc(100% - 100px)' : 'calc(100% - 84px)') }"> | ||
| 952 | <Table v-if="treeType === 'model'" :tableInfo="groupTableInfo" @tableBtnClick="groupTableBtnClick" | ||
| 953 | @tableSelectionChange="groupTableSelectionChange" @tablePageChange="groupTablePageChange" /> | ||
| 954 | <Table v-if="treeType === 'group'" :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" | ||
| 955 | @tableSelectionChange="tableSelectionChange" @tablePageChange="tablePageChange" /> | ||
| 956 | <Table v-if="treeType === 'table'" :tableInfo="ruleTableInfo" @tableBtnClick="ruleTableBtnClick" | ||
| 957 | @tableSwitchBeforeChange="ruleTableSwitchBeforeChange" /> | ||
| 958 | </div> | ||
| 959 | </div> | ||
| 960 | |||
| 961 | <Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" /> | ||
| 962 | </div> | ||
| 963 | </template> | ||
| 964 | |||
| 965 | <style lang="scss" scoped> | ||
| 966 | .container_wrap { | ||
| 967 | padding: 0; | ||
| 968 | display: flex; | ||
| 969 | justify-content: space-between; | ||
| 970 | |||
| 971 | .box_left { | ||
| 972 | width: 200px; | ||
| 973 | box-shadow: 1px 0 0 0 #d9d9d9; | ||
| 974 | |||
| 975 | .tree_panel { | ||
| 976 | height: calc(100% - 36px); | ||
| 977 | padding-top: 0; | ||
| 978 | |||
| 979 | :deep(.el-tree) { | ||
| 980 | margin: 0; | ||
| 981 | overflow: hidden auto; | ||
| 982 | } | ||
| 983 | } | ||
| 984 | } | ||
| 985 | |||
| 986 | .box_right { | ||
| 987 | width: calc(100% - 200px); | ||
| 988 | height: 100%; | ||
| 989 | padding: 0 16px; | ||
| 990 | overflow: hidden auto; | ||
| 991 | } | ||
| 992 | |||
| 993 | .panel_title { | ||
| 994 | line-height: 40px; | ||
| 995 | font-size: 16px; | ||
| 996 | font-weight: 600; | ||
| 997 | color: var(--el-color-regular); | ||
| 998 | } | ||
| 999 | } | ||
| 1000 | |||
| 1001 | .table_tool_wrap { | ||
| 1002 | width: 100%; | ||
| 1003 | display: flex; | ||
| 1004 | align-items: flex-start; | ||
| 1005 | flex-direction: column; | ||
| 1006 | justify-content: center; | ||
| 1007 | |||
| 1008 | .tools_btns { | ||
| 1009 | padding: 0; | ||
| 1010 | } | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | .table_panel_wrap { | ||
| 1014 | width: 100%; | ||
| 1015 | height: calc(100% - 84px); | ||
| 1016 | } | ||
| 1017 | </style> |
src/views/data_quality/ruleForm.vue
0 → 100644
| 1 | <script lang="ts" setup name="ruleForm"> | ||
| 2 | import { ref } from "vue"; | ||
| 3 | import { | ||
| 4 | validateSubjectTableRule, | ||
| 5 | validateCustomSql, | ||
| 6 | getSubjectFields, | ||
| 7 | getSubjectTableTree, | ||
| 8 | getSubjectTableByDomain | ||
| 9 | } from '@/api/modules/dataQuality'; | ||
| 10 | import Table from "@/components/Table/index.vue"; | ||
| 11 | import Form from "@/components/Form/index.vue"; | ||
| 12 | import { ElMessage } from "element-plus"; | ||
| 13 | import { CirclePlus, Close } from '@element-plus/icons-vue'; | ||
| 14 | import { cloneDeep } from 'lodash-es'; | ||
| 15 | |||
| 16 | const { proxy } = getCurrentInstance() as any; | ||
| 17 | |||
| 18 | const props = defineProps({ | ||
| 19 | toSubjectTables: { | ||
| 20 | type: Array<any>, | ||
| 21 | default: [], | ||
| 22 | }, | ||
| 23 | ruleTypeValue: { | ||
| 24 | type: String, | ||
| 25 | default: '', | ||
| 26 | }, | ||
| 27 | value: { | ||
| 28 | type: Object, | ||
| 29 | default: {} | ||
| 30 | }, | ||
| 31 | ruleTypeList: { | ||
| 32 | type: Array<any>, | ||
| 33 | default: [], | ||
| 34 | }, | ||
| 35 | smallCategoryList: { | ||
| 36 | type: Array<any>, | ||
| 37 | default: [], | ||
| 38 | }, | ||
| 39 | largeCategoryList: { | ||
| 40 | type: Array<any>, | ||
| 41 | default: [], | ||
| 42 | }, | ||
| 43 | isSingle: { | ||
| 44 | type: Boolean, | ||
| 45 | default: false, | ||
| 46 | }, | ||
| 47 | readonly: { | ||
| 48 | type: Boolean, | ||
| 49 | default: false | ||
| 50 | } | ||
| 51 | }); | ||
| 52 | |||
| 53 | watch(() => props.toSubjectTables, (val: any[]) => { | ||
| 54 | panelList.value[4].options = val; | ||
| 55 | panelList.value[4].default = val.map(s => s.guid); | ||
| 56 | setPanelListValue(Object.assign({ qualityModelGuids: panelList.value[4].default }, props.value), false, true); | ||
| 57 | formItems.value[0].children = panelList.value; | ||
| 58 | }); | ||
| 59 | |||
| 60 | watch(() => props.value, (val) => { | ||
| 61 | if (Object.keys(val).length === 0) { | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | setPanelListValue(val, false, true); | ||
| 65 | }) | ||
| 66 | |||
| 67 | watch(() => props.ruleTypeValue, (val) => { | ||
| 68 | formItems.value[0].default = val; | ||
| 69 | if (val) { | ||
| 70 | let v = [props.ruleTypeList.find(option => option.value == props.ruleTypeValue)]; | ||
| 71 | formItems.value[0].options = v; | ||
| 72 | formItems.value[0].default = val; | ||
| 73 | radioGroupChange(val, props.value, true); | ||
| 74 | } else { | ||
| 75 | let v = props.ruleTypeList; | ||
| 76 | formItems.value[0].options = v; | ||
| 77 | radioGroupChange(v[0].ruleCode, props.value, true); | ||
| 78 | } | ||
| 79 | }) | ||
| 80 | |||
| 81 | watch(() => props.ruleTypeList, (val) => { | ||
| 82 | if (props.ruleTypeValue) { | ||
| 83 | let v = [val.find(option => option.value == props.ruleTypeValue)]; | ||
| 84 | formItems.value[0].options = v; | ||
| 85 | formItems.value[0].default = props.ruleTypeValue; | ||
| 86 | radioGroupChange(props.ruleTypeValue, props.value, true); | ||
| 87 | } else { | ||
| 88 | let v = val; | ||
| 89 | formItems.value[0].options = v; | ||
| 90 | formItems.value[0].default = v[0]?.ruleCode; | ||
| 91 | radioGroupChange(v[0].ruleCode, props.value, true); | ||
| 92 | } | ||
| 93 | }) | ||
| 94 | |||
| 95 | watch(() => props.smallCategoryList, (val) => { | ||
| 96 | if (!props.readonly) { | ||
| 97 | let largeV = props.value['largeCategory'] || getDefaultLargeCategory(formItems.value[0].default); | ||
| 98 | if (largeV == '1') {//规范性 | ||
| 99 | panelList.value[2].options = val.slice(0, 6); | ||
| 100 | } else if (largeV == '2') { | ||
| 101 | panelList.value[2].options = val.slice(6, 8); | ||
| 102 | } else if (largeV == '3') { | ||
| 103 | panelList.value[2].options = val.slice(8, 13); | ||
| 104 | } else if (largeV == '4') { | ||
| 105 | panelList.value[2].options = val.slice(13, 15); | ||
| 106 | } else if (largeV == '5') { | ||
| 107 | panelList.value[2].options = val.slice(15, 18); | ||
| 108 | } else if (largeV == '6') { | ||
| 109 | panelList.value[2].options = val.slice(18, 20); | ||
| 110 | } else { | ||
| 111 | panelList.value[2].options = val.slice(0, 6); | ||
| 112 | panelList.value[2].default = getDefaultSmallCategory(formItems.value[0].default);; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | if (Object.keys(props.value).length > 0) { | ||
| 116 | setPanelListValue(props.value, false, true); | ||
| 117 | } | ||
| 118 | }) | ||
| 119 | |||
| 120 | watch(() => props.largeCategoryList, (val) => { | ||
| 121 | panelList.value[1].options = val; | ||
| 122 | panelList.value[1].default = getDefaultLargeCategory(formItems.value[0].default); | ||
| 123 | if (Object.keys(props.value).length > 0) { | ||
| 124 | setPanelListValue(props.value, false, true); | ||
| 125 | } | ||
| 126 | }) | ||
| 127 | |||
| 128 | onBeforeMount(() => { | ||
| 129 | if (props.ruleTypeList?.length) { | ||
| 130 | if (props.ruleTypeValue) { | ||
| 131 | let v = [props.ruleTypeList.find(option => option.value == props.ruleTypeValue)]; | ||
| 132 | formItems.value[0].options = v; | ||
| 133 | formItems.value[0].default = props.ruleTypeValue; | ||
| 134 | radioGroupChange(props.ruleTypeValue, props.value, true); | ||
| 135 | } else { | ||
| 136 | let v = props.ruleTypeList; | ||
| 137 | formItems.value[0].options = v; | ||
| 138 | formItems.value[0].default = v[0].ruleCode; | ||
| 139 | radioGroupChange(v[0].ruleCode, props.value, true); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | if (props.largeCategoryList?.length) { | ||
| 143 | panelList.value[1].options = props.largeCategoryList; | ||
| 144 | panelList.value[1].default = getDefaultLargeCategory(formItems.value[0].default); | ||
| 145 | formItems.value[0].children = panelList.value; | ||
| 146 | } | ||
| 147 | if (props.smallCategoryList?.length) { | ||
| 148 | panelList.value[2].options = props.smallCategoryList.slice(0, 6); | ||
| 149 | panelList.value[2].default = getDefaultSmallCategory(formItems.value[0].default); | ||
| 150 | formItems.value[0].children = panelList.value; | ||
| 151 | } | ||
| 152 | if (props.isSingle) { | ||
| 153 | if (props.toSubjectTables.length) { | ||
| 154 | panelList.value[4].default = props.toSubjectTables.map(s => s.guid); | ||
| 155 | } | ||
| 156 | panelList.value[4].disabled = true; | ||
| 157 | formItems.value[0].children = panelList.value; | ||
| 158 | } | ||
| 159 | if (Object.keys(props.value).length > 0) { | ||
| 160 | setPanelListValue(props.value, false, true); | ||
| 161 | } | ||
| 162 | }); | ||
| 163 | |||
| 164 | const getDefaultLargeCategory = (ruleCode) => { | ||
| 165 | if (ruleCode == 'repeate_data_check' || ruleCode == 'logic_check') { | ||
| 166 | return '3'; //准确性 | ||
| 167 | } else if (ruleCode == 'volatility_check' || ruleCode == 'rows_check') { | ||
| 168 | return '5'; | ||
| 169 | } else if (ruleCode == 'null_value_check') { | ||
| 170 | return '2'; | ||
| 171 | } else { | ||
| 172 | return '1'; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | const getDefaultSmallCategory = (ruleCode) => { | ||
| 177 | if (ruleCode == 'repeate_data_check') { | ||
| 178 | return '11'; //准确性 | ||
| 179 | } else if (ruleCode == 'logic_check') { | ||
| 180 | return '9'; | ||
| 181 | } else if (ruleCode == 'volatility_check' || ruleCode == 'rows_check') { | ||
| 182 | return '17'; | ||
| 183 | } else if (ruleCode == 'null_value_check') { | ||
| 184 | return '7'; | ||
| 185 | } else { | ||
| 186 | return '1'; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | const ruleType = ref('empty') | ||
| 191 | const panelList: any = ref([ | ||
| 192 | { | ||
| 193 | label: '规则名称', | ||
| 194 | type: 'input', | ||
| 195 | maxlength: 50, | ||
| 196 | placeholder: '请输入', | ||
| 197 | field: 'ruleConfName', | ||
| 198 | default: '', | ||
| 199 | required: true | ||
| 200 | }, { | ||
| 201 | label: '规则大类', | ||
| 202 | type: 'select', | ||
| 203 | placeholder: '请选择', | ||
| 204 | field: 'largeCategory', | ||
| 205 | default: '1', | ||
| 206 | options: props.largeCategoryList, | ||
| 207 | props: { | ||
| 208 | label: 'paramName', | ||
| 209 | value: 'paramValue' | ||
| 210 | }, | ||
| 211 | required: true, | ||
| 212 | visible: true | ||
| 213 | }, { | ||
| 214 | label: '规则小类', | ||
| 215 | type: 'select', | ||
| 216 | placeholder: '请选择', | ||
| 217 | field: 'smallCategory', | ||
| 218 | props: { | ||
| 219 | label: 'paramName', | ||
| 220 | value: 'paramValue' | ||
| 221 | }, | ||
| 222 | default: '', | ||
| 223 | options: props.smallCategoryList.slice(6), | ||
| 224 | required: true, | ||
| 225 | visible: true | ||
| 226 | }, { | ||
| 227 | label: '校验方式', | ||
| 228 | type: 'select', | ||
| 229 | placeholder: '请选择', | ||
| 230 | field: 'parity', | ||
| 231 | default: 1, | ||
| 232 | options: [ | ||
| 233 | { | ||
| 234 | label: '1天周期比较', | ||
| 235 | value: 1 | ||
| 236 | }, { | ||
| 237 | label: '7天周期比较', | ||
| 238 | value: 7 | ||
| 239 | }, { | ||
| 240 | label: '上一周期比较', | ||
| 241 | value: -1 | ||
| 242 | } | ||
| 243 | ], | ||
| 244 | clearable: true, | ||
| 245 | required: true, | ||
| 246 | visible: true | ||
| 247 | }, { | ||
| 248 | label: '质检表', | ||
| 249 | type: 'select', | ||
| 250 | placeholder: '请选择', | ||
| 251 | field: 'qualityModelGuids', | ||
| 252 | props: { | ||
| 253 | label: 'chName', | ||
| 254 | value: 'guid' | ||
| 255 | }, | ||
| 256 | default: [], | ||
| 257 | options: props.toSubjectTables, | ||
| 258 | multiple: true, | ||
| 259 | tagsTooltip: true, | ||
| 260 | collapse: true, | ||
| 261 | filterable: true, | ||
| 262 | required: true, | ||
| 263 | visible: true, | ||
| 264 | }, { | ||
| 265 | label: '比较方式', | ||
| 266 | type: 'select', | ||
| 267 | placeholder: '请选择', | ||
| 268 | field: 'compareWay', | ||
| 269 | default: 1, | ||
| 270 | options: [ | ||
| 271 | { | ||
| 272 | label: '绝对值', | ||
| 273 | value: 1 | ||
| 274 | }, { | ||
| 275 | label: '上升', | ||
| 276 | value: 2 | ||
| 277 | }, | ||
| 278 | { | ||
| 279 | label: '下降', | ||
| 280 | value: 3 | ||
| 281 | } | ||
| 282 | ], | ||
| 283 | clearable: true, | ||
| 284 | required: true, | ||
| 285 | visible: true | ||
| 286 | }, { | ||
| 287 | label: '波动值比较', | ||
| 288 | type: 'input-group', | ||
| 289 | placeholder: '请输入', | ||
| 290 | field: 'ruleFluctuations', | ||
| 291 | default: '', | ||
| 292 | children: [ | ||
| 293 | { | ||
| 294 | label: '', | ||
| 295 | type: 'input', | ||
| 296 | inputType: 'scoreNumber', | ||
| 297 | max: 1, | ||
| 298 | placeholder: '橙色阈值', | ||
| 299 | field: 'orangeThreshold', | ||
| 300 | default: '', | ||
| 301 | clearable: true, | ||
| 302 | required: true | ||
| 303 | }, | ||
| 304 | { | ||
| 305 | label: '', | ||
| 306 | type: 'input', | ||
| 307 | placeholder: '红色阈值', | ||
| 308 | inputType: 'scoreNumber', | ||
| 309 | max: 1, | ||
| 310 | field: 'redThreshold', | ||
| 311 | default: '', | ||
| 312 | clearable: true, | ||
| 313 | required: true | ||
| 314 | }, | ||
| 315 | ], | ||
| 316 | col: 'col2', | ||
| 317 | clearable: true, | ||
| 318 | required: true | ||
| 319 | }, { | ||
| 320 | label: '规则设置', | ||
| 321 | type: 'input-dom', | ||
| 322 | placeholder: '请设置,可多选', | ||
| 323 | field: 'ruleSettings', //重复数据 7 | ||
| 324 | default: '',//描述型的字段 | ||
| 325 | defaultValue: {},//实际的json值 | ||
| 326 | readonly: true, | ||
| 327 | required: true, | ||
| 328 | block: true, | ||
| 329 | visible: false | ||
| 330 | }, { | ||
| 331 | label: '规则设置', | ||
| 332 | type: 'input-dom', | ||
| 333 | placeholder: '请设置,可多选', | ||
| 334 | field: 'ruleSettings-logic', //8 | ||
| 335 | default: '',//描述型的字段 | ||
| 336 | defaultValue: {},//实际的json值, {ruleFields:,conditionSqls:} | ||
| 337 | readonly: true, | ||
| 338 | required: true, | ||
| 339 | block: true, | ||
| 340 | visible: false | ||
| 341 | }, { | ||
| 342 | label: '规则设置', | ||
| 343 | type: 'input-dom', | ||
| 344 | placeholder: '请设置,可多选', | ||
| 345 | field: 'ruleSettings-sql', //9 | ||
| 346 | default: '',//描述型的字段 | ||
| 347 | defaultValue: {},//实际的json值,key为表,value为sql | ||
| 348 | readonly: true, | ||
| 349 | required: true, | ||
| 350 | block: true, | ||
| 351 | visible: false | ||
| 352 | }, { | ||
| 353 | label: '规则设置', | ||
| 354 | type: 'input-dom', | ||
| 355 | placeholder: '请设置,可多选', | ||
| 356 | field: 'ruleSettings-null', //10 | ||
| 357 | default: '',//描述型的字段 | ||
| 358 | defaultValue: {},//实际的json值 | ||
| 359 | readonly: true, | ||
| 360 | required: true, | ||
| 361 | block: true, | ||
| 362 | visible: false | ||
| 363 | }, { | ||
| 364 | label: '规则设置', | ||
| 365 | type: 'input-dom', | ||
| 366 | placeholder: '请设置,可多选', | ||
| 367 | field: 'ruleSettings-rows', //11 | ||
| 368 | default: '',//描述型的字段 | ||
| 369 | defaultValue: [],//实际的表格数据值数组 | ||
| 370 | readonly: true, | ||
| 371 | required: true, | ||
| 372 | block: true, | ||
| 373 | visible: false | ||
| 374 | }, { | ||
| 375 | label: '描述', | ||
| 376 | type: 'textarea', | ||
| 377 | placeholder: '请输入', | ||
| 378 | field: 'description', | ||
| 379 | default: '', | ||
| 380 | clearable: true, | ||
| 381 | required: false, | ||
| 382 | block: true | ||
| 383 | }, { | ||
| 384 | label: '', | ||
| 385 | type: 'checkbox', | ||
| 386 | placeholder: '联合不为空', | ||
| 387 | desc: '勾选表示表的所有字段不能同时为空,不勾选表示每个字段都不为空', | ||
| 388 | field: 'jointly', | ||
| 389 | default: 'N', | ||
| 390 | trueValue: 'Y', | ||
| 391 | falseValue: 'N', | ||
| 392 | required: false, | ||
| 393 | visible: false, | ||
| 394 | block: true | ||
| 395 | }, { | ||
| 396 | label: '', | ||
| 397 | type: 'checkbox', | ||
| 398 | placeholder: '直接启用规则', | ||
| 399 | desc: '', | ||
| 400 | field: 'bizState', | ||
| 401 | default: "Y", | ||
| 402 | trueValue: 'Y', | ||
| 403 | falseValue: 'S', | ||
| 404 | required: false, | ||
| 405 | visible: !props.readonly, | ||
| 406 | block: true | ||
| 407 | } | ||
| 408 | ]) | ||
| 409 | |||
| 410 | const formItems: any = ref([ | ||
| 411 | { | ||
| 412 | label: '选择规则', | ||
| 413 | type: 'radio-panel', | ||
| 414 | placeholder: '', | ||
| 415 | field: 'ruleCode', | ||
| 416 | default: props.ruleTypeValue, | ||
| 417 | options: props.ruleTypeList, | ||
| 418 | children: panelList.value, | ||
| 419 | clearable: true, | ||
| 420 | required: false, | ||
| 421 | block: true, | ||
| 422 | col: 'no-wrap' | ||
| 423 | }, | ||
| 424 | ]) | ||
| 425 | |||
| 426 | const formRules = ref({ | ||
| 427 | ruleConfName: [ | ||
| 428 | { required: true, trigger: 'blur', message: "请填写规则名称" } | ||
| 429 | ], | ||
| 430 | largeCategory: [ | ||
| 431 | { required: true, trigger: 'change', message: "请填写规则大类" } | ||
| 432 | ], | ||
| 433 | smallCategory: [ | ||
| 434 | { required: true, trigger: 'change', message: "请填写规则小类" } | ||
| 435 | ], | ||
| 436 | parity: [ | ||
| 437 | { required: true, trigger: 'change', message: "请填写检验方式" } | ||
| 438 | ], | ||
| 439 | compareWay: [ | ||
| 440 | { required: true, trigger: 'change', message: "请填写比较方式" } | ||
| 441 | ], | ||
| 442 | ruleSettings: [ | ||
| 443 | { required: true, trigger: 'change', message: "请设置规则" } | ||
| 444 | ], | ||
| 445 | 'ruleSettings-logic': [ | ||
| 446 | { required: true, trigger: 'change', message: "请设置规则" } | ||
| 447 | ], | ||
| 448 | 'ruleSettings-sql': [ | ||
| 449 | { required: true, trigger: 'change', message: "请设置规则" } | ||
| 450 | ], | ||
| 451 | 'ruleSettings-null': [ | ||
| 452 | { required: true, trigger: 'change', message: "请设置规则" } | ||
| 453 | ], | ||
| 454 | 'ruleSettings-rows': [ | ||
| 455 | { required: true, trigger: 'change', message: "请设置规则" } | ||
| 456 | ], | ||
| 457 | orangeThreshold: [ | ||
| 458 | { | ||
| 459 | trigger: 'blur', validator: (rule: any, value: any, callback: any) => { | ||
| 460 | if (value === 0) { | ||
| 461 | callback(); | ||
| 462 | return; | ||
| 463 | } | ||
| 464 | if (!value) { | ||
| 465 | callback(new Error('请填写橙色阈值')); | ||
| 466 | return; | ||
| 467 | } | ||
| 468 | if (parseFloat(value) > 1) { | ||
| 469 | callback(new Error('请填写小于1的小数')); | ||
| 470 | return; | ||
| 471 | } | ||
| 472 | let formInline = ruleFormRef.value?.formInline; | ||
| 473 | if (formInline && formInline.redThreshold && parseFloat(value) >= parseFloat(formInline.redThreshold)) { | ||
| 474 | callback(new Error("橙色阈值应小于红色阈值")); | ||
| 475 | } | ||
| 476 | callback(); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | ], | ||
| 480 | redThreshold: [ | ||
| 481 | { | ||
| 482 | required: true, trigger: 'blur', validator: (rule: any, value: any, callback: any) => { | ||
| 483 | if (value === 0) { | ||
| 484 | callback(); | ||
| 485 | return; | ||
| 486 | } | ||
| 487 | if (!value) { | ||
| 488 | callback(new Error('请填写红色阈值')); | ||
| 489 | return; | ||
| 490 | } | ||
| 491 | if (parseFloat(value) > 1) { | ||
| 492 | callback(new Error('请填写小于1的小数')); | ||
| 493 | return; | ||
| 494 | } | ||
| 495 | let formInline = ruleFormRef.value?.formInline; | ||
| 496 | if (formInline && formInline.orangeThreshold && parseFloat(value) <= parseFloat(formInline.orangeThreshold)) { | ||
| 497 | callback(new Error("红色阈值应大于橙色阈值")); | ||
| 498 | } | ||
| 499 | callback(); | ||
| 500 | } | ||
| 501 | } | ||
| 502 | ], | ||
| 503 | qualityModelGuids: [ | ||
| 504 | { type: 'array', required: true, message: '请至少选择一个质检表', trigger: 'change' } | ||
| 505 | ], | ||
| 506 | }) | ||
| 507 | |||
| 508 | const ruleFormRef = ref(); | ||
| 509 | |||
| 510 | const oldOriginValue: any = ref({}); | ||
| 511 | |||
| 512 | const dialogVisible = ref(false); | ||
| 513 | |||
| 514 | const cancelDialog = () => { | ||
| 515 | dialogVisible.value = false; | ||
| 516 | } | ||
| 517 | |||
| 518 | const submit = () => { | ||
| 519 | if (Object.keys(nullSelectFields.value).length === 0) { | ||
| 520 | ElMessage.error('当前已选字段不能为空!'); | ||
| 521 | return; | ||
| 522 | } | ||
| 523 | let index = ruleType.value == 'repeate_data_check' ? 7 : 10; | ||
| 524 | panelList.value[index].defaultValue = nullSelectFields.value; | ||
| 525 | let str = ""; | ||
| 526 | for (const key in nullSelectFields.value) { | ||
| 527 | let fields = nullSelectFields.value[key]; | ||
| 528 | str = str + (str ? ';' : '') + fields.map(f => f.enName).join(','); | ||
| 529 | } | ||
| 530 | let formInline = oldOriginValue.value = Object.assign({ | ||
| 531 | qualityModelGuids: props.toSubjectTables.map(s => s.guid), | ||
| 532 | parity: 1, | ||
| 533 | compareWay: 1, | ||
| 534 | jointly: 'N', | ||
| 535 | bizState: 'Y' | ||
| 536 | }, oldOriginValue.value, ruleFormRef.value.formInline); | ||
| 537 | formInline[`${panelList.value[index].field}`] = str; | ||
| 538 | setPanelListValue(formInline); | ||
| 539 | dialogVisible.value = false; | ||
| 540 | } | ||
| 541 | |||
| 542 | const getSubjectTableTreeData = () => { | ||
| 543 | return getSubjectTableTree({}).then((res: any) => { | ||
| 544 | if (res.code == proxy.$passCode) { | ||
| 545 | contrastSubjects.value = res.data || []; | ||
| 546 | return res.data || []; | ||
| 547 | } | ||
| 548 | }) | ||
| 549 | } | ||
| 550 | |||
| 551 | const formBtnClick = (btn) => { | ||
| 552 | if (ruleType.value == 'null_value_check' || ruleType.value == 'repeate_data_check') { | ||
| 553 | dialogVisible.value = true; | ||
| 554 | tableListInfo.value.data = props.toSubjectTables; | ||
| 555 | dialogSelectSubjectTable.value = props.toSubjectTables[0]; | ||
| 556 | nullTableInfo.value.data = []; | ||
| 557 | let index = ruleType.value == 'repeate_data_check' ? 7 : 10; | ||
| 558 | nullSelectFields.value = cloneDeep(panelList.value[index].defaultValue); | ||
| 559 | if (props.toSubjectTables[0]?.guid) { | ||
| 560 | nullTableInfo.value.loading = true; | ||
| 561 | getSubjectFields(props.toSubjectTables[0]?.guid).then((res: any) => { | ||
| 562 | nullTableInfo.value.loading = false; | ||
| 563 | if (res.code == proxy.$passCode) { | ||
| 564 | nullTableInfo.value.data = res.data || []; | ||
| 565 | let tagData = nullSelectFields.value[props.toSubjectTables[0]?.chName]; | ||
| 566 | if (tagData?.length) { | ||
| 567 | nextTick(() => { | ||
| 568 | tagData?.forEach((d) => { | ||
| 569 | let row = nullTableInfo.value.data.find((v: any) => v.guid == d.guid); | ||
| 570 | if (row) { | ||
| 571 | nullTableRef.value?.tableRef?.toggleRowSelection(row, true); | ||
| 572 | } | ||
| 573 | }); | ||
| 574 | }); | ||
| 575 | } | ||
| 576 | } else { | ||
| 577 | ElMessage.error(res.msg); | ||
| 578 | } | ||
| 579 | }) | ||
| 580 | } | ||
| 581 | } else if (ruleType.value == 'logic_check') { | ||
| 582 | filterDialogVisible.value = true; | ||
| 583 | tableListInfo.value.data = props.toSubjectTables; | ||
| 584 | dialogSelectSubjectTable.value = props.toSubjectTables[0]; | ||
| 585 | let defaultValue = panelList.value[8].defaultValue; | ||
| 586 | if (defaultValue.ruleFields) { | ||
| 587 | dialogSelectSubjectTableField.value = cloneDeep(defaultValue.ruleFields); | ||
| 588 | tableFilters.value = cloneDeep(defaultValue.conditionSqls); | ||
| 589 | tableFilterValidates.value = {}; | ||
| 590 | for (const key in tableFilters.value) { | ||
| 591 | tableFilterValidates.value[key] = true; | ||
| 592 | } | ||
| 593 | } else { | ||
| 594 | dialogSelectSubjectTableField.value = {}; | ||
| 595 | tableFilters.value = {}; | ||
| 596 | tableFilterValidates.value = {}; | ||
| 597 | } | ||
| 598 | tableInfo.value.data = []; | ||
| 599 | if (props.toSubjectTables[0]?.guid) { | ||
| 600 | tableInfo.value.loading = true; | ||
| 601 | getSubjectFields(props.toSubjectTables[0]?.guid).then((res: any) => { | ||
| 602 | tableInfo.value.loading = false; | ||
| 603 | if (res.code == proxy.$passCode) { | ||
| 604 | tableInfo.value.data = res.data || []; | ||
| 605 | } else { | ||
| 606 | ElMessage.error(res.msg); | ||
| 607 | } | ||
| 608 | }) | ||
| 609 | } | ||
| 610 | } else if (ruleType.value == 'custom_sql') { | ||
| 611 | sqlDialogVisible.value = true; | ||
| 612 | tableListInfo.value.data = props.toSubjectTables; | ||
| 613 | dialogSelectSubjectTable.value = props.toSubjectTables[0]; | ||
| 614 | let defaultValue = panelList.value[9].defaultValue || {}; | ||
| 615 | sqlSelectField.value = cloneDeep(defaultValue.ruleFields || {}); | ||
| 616 | sqlFieldsList.value = {}; | ||
| 617 | for (const key in defaultValue.sqlFieldsList) { | ||
| 618 | sqlFieldsList.value[key] = defaultValue.sqlFieldsList?.[key]?.map(d => { | ||
| 619 | return { | ||
| 620 | value: d | ||
| 621 | } | ||
| 622 | }) || [] | ||
| 623 | } | ||
| 624 | sqlTableFilters.value = cloneDeep(defaultValue.customSqls || {}); | ||
| 625 | sqlFilterValidates.value = {}; | ||
| 626 | for (const key in sqlTableFilters.value) { | ||
| 627 | sqlFilterValidates.value[key] = true; | ||
| 628 | } | ||
| 629 | sqlTableFieldInfo.value.data = []; | ||
| 630 | if (props.toSubjectTables[0]?.guid) { | ||
| 631 | sqlTableFieldInfo.value.loading = true; | ||
| 632 | getSubjectFields(props.toSubjectTables[0]?.guid).then((res: any) => { | ||
| 633 | sqlTableFieldInfo.value.loading = false; | ||
| 634 | if (res.code == proxy.$passCode) { | ||
| 635 | sqlTableFieldInfo.value.data = res.data || []; | ||
| 636 | } else { | ||
| 637 | ElMessage.error(res.msg); | ||
| 638 | } | ||
| 639 | }) | ||
| 640 | } | ||
| 641 | } else if (ruleType.value == 'rows_check') { | ||
| 642 | if (!contrastSubjects.value?.length) { | ||
| 643 | getSubjectTableTreeData().then(res => { | ||
| 644 | return res; | ||
| 645 | }) | ||
| 646 | } | ||
| 647 | tableRowDialogVisible.value = true; | ||
| 648 | tableRowRulesData.value = cloneDeep(panelList.value[11].defaultValue || []); | ||
| 649 | if (!tableRowRulesData.value.length) { | ||
| 650 | tableRowRulesData.value = [{ | ||
| 651 | differenceRange: 0 | ||
| 652 | }]; | ||
| 653 | } | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | const selectChange = (val, item, info) => { | ||
| 658 | setPanelListValue(info, true); | ||
| 659 | }; | ||
| 660 | |||
| 661 | /** 设置表单选项的值,防止largeCategory的options改变,重新赋值。 当radioGroupChange为true,init为false时,为用户手动切换,需要更新大类的默认值。 */ | ||
| 662 | const setPanelListValue = (item, isSelectChange = false, init = false, radioGroupChange = false) => { | ||
| 663 | let val = oldOriginValue.value = Object.assign({ | ||
| 664 | qualityModelGuids: props.toSubjectTables.map(s => s.guid), | ||
| 665 | parity: 1, | ||
| 666 | largeCategory: getDefaultLargeCategory(formItems.value[0].default), | ||
| 667 | compareWay: 1, | ||
| 668 | jointly: 'N', | ||
| 669 | bizState: 'Y' | ||
| 670 | }, oldOriginValue.value, item); | ||
| 671 | let ruleCode = val.ruleCode; | ||
| 672 | panelList.value.forEach(p => { | ||
| 673 | let field = p.field; | ||
| 674 | if (p.field == 'bizState') { | ||
| 675 | p.visible = !props.readonly; | ||
| 676 | } else if (field == 'ruleFluctuations') { | ||
| 677 | p.children?.forEach(c => { | ||
| 678 | c.default = val[c.field]; | ||
| 679 | }); | ||
| 680 | } else if (p.field === 'qualityModelGuids') { | ||
| 681 | if (val[p.field] && typeof val[p.field] == 'string') { | ||
| 682 | p.default = [val[p.field]]; | ||
| 683 | } else { | ||
| 684 | p.default = val[p.field] || []; | ||
| 685 | } | ||
| 686 | p.disabled = true; | ||
| 687 | } else if (p.field == 'ruleFluctuations') { | ||
| 688 | p.children.forEach(c => { | ||
| 689 | c.default = val[c.field] | ||
| 690 | }) | ||
| 691 | } else if ((ruleCode == 'repeate_data_check' && p.field == 'ruleSettings') || (ruleCode == 'null_value_check' && p.field == 'ruleSettings-null')) { | ||
| 692 | if (!init) { | ||
| 693 | p.default = val[field]; | ||
| 694 | return; | ||
| 695 | } | ||
| 696 | if (val.ruleField) { | ||
| 697 | p.default = val.ruleField?.map(f => f.enName)?.join(';'); | ||
| 698 | p.defaultValue[`${val.subjectZhName}`] = val.ruleField || []; | ||
| 699 | } else { | ||
| 700 | p.default = ''; | ||
| 701 | p.defaultValue = {}; | ||
| 702 | } | ||
| 703 | } else if (ruleCode == 'logic_check' && p.field == 'ruleSettings-logic') { | ||
| 704 | if (!init) { | ||
| 705 | p.default = val[field]; | ||
| 706 | return; | ||
| 707 | } | ||
| 708 | if (val.ruleField) { | ||
| 709 | p.default = val.ruleField?.map(f => f.enName)?.join(';'); | ||
| 710 | let ruleFields = {}; | ||
| 711 | ruleFields[val.subjectName] = val.ruleField?.[0] || []; | ||
| 712 | let conditionSqls = {}; | ||
| 713 | conditionSqls[val.subjectName] = val.conditionSql; | ||
| 714 | p.defaultValue = { | ||
| 715 | ruleFields: ruleFields, | ||
| 716 | conditionSqls: conditionSqls | ||
| 717 | } | ||
| 718 | } else { | ||
| 719 | p.default = ''; | ||
| 720 | p.defaultValue = {}; | ||
| 721 | } | ||
| 722 | } else if (ruleCode == 'custom_sql' && p.field == 'ruleSettings-sql') { | ||
| 723 | if (!init) { | ||
| 724 | p.default = val[field]; | ||
| 725 | return; | ||
| 726 | } | ||
| 727 | if (val.ruleField) { | ||
| 728 | if (val.ruleField?.length) { | ||
| 729 | p.default = val.subjectName + ":" + val.ruleField?.map(f => f.enName)?.join(','); | ||
| 730 | } | ||
| 731 | let ruleFields = {}; | ||
| 732 | ruleFields[val.subjectName] = val.ruleField?.map(f => f.enName) || []; | ||
| 733 | let sqls = {}; | ||
| 734 | sqls[val.subjectName] = val.customSql; | ||
| 735 | let sqlFieldsList = {}; | ||
| 736 | sqlFieldsList[val.subjectName] = val.fieldSelects || []; | ||
| 737 | p.defaultValue = { | ||
| 738 | ruleFields: ruleFields, | ||
| 739 | customSqls: sqls, | ||
| 740 | sqlFieldsList: sqlFieldsList | ||
| 741 | }; | ||
| 742 | } else { | ||
| 743 | p.default = ''; | ||
| 744 | p.defaultValue = {}; | ||
| 745 | } | ||
| 746 | } else if (ruleCode == 'rows_check' && p.field == 'ruleSettings-rows') { | ||
| 747 | if (!init) { | ||
| 748 | p.default = val[field]; | ||
| 749 | return; | ||
| 750 | } | ||
| 751 | p.default = val.subjectName; | ||
| 752 | if (val.subjectName) { | ||
| 753 | p.defaultValue = [{ | ||
| 754 | mainTable: val.subjectGuid, | ||
| 755 | mainTableName: val.subjectName, | ||
| 756 | mainTableZhName: val.subjectZhName, | ||
| 757 | differenceRange: val.differenceRange || 0, | ||
| 758 | contrastSubjectGuid: val.contrastSubjectGuid, | ||
| 759 | contrastSubjectName: val.contrastSubjectName, | ||
| 760 | contrastSubjectZhName: val.contrastSubjectZhName | ||
| 761 | }] | ||
| 762 | } else { | ||
| 763 | p.defaultValue = []; | ||
| 764 | } | ||
| 765 | } else if (p.field == 'largeCategory') { | ||
| 766 | /** 此处有歧义,若是切换规则类型,修改默认值,可能会出现,用户先修改了规则大类,但是切换类型之后,被我还原了。 */ | ||
| 767 | if (radioGroupChange && !init) { | ||
| 768 | p.default = getDefaultLargeCategory(ruleCode); | ||
| 769 | } else { | ||
| 770 | p.default = val[p.field]; | ||
| 771 | } | ||
| 772 | if (!props.readonly) { | ||
| 773 | if (p.default == '1') {//规范性 | ||
| 774 | panelList.value[2].options = props.smallCategoryList.slice(0, 6); | ||
| 775 | } else if (p.default == '2') { | ||
| 776 | panelList.value[2].options = props.smallCategoryList.slice(6, 8); | ||
| 777 | } else if (p.default == '3') { | ||
| 778 | panelList.value[2].options = props.smallCategoryList.slice(8, 13); | ||
| 779 | } else if (p.default == '4') { | ||
| 780 | panelList.value[2].options = props.smallCategoryList.slice(13, 15); | ||
| 781 | } else if (p.default == '5') { | ||
| 782 | panelList.value[2].options = props.smallCategoryList.slice(15, 18); | ||
| 783 | } else if (p.default == '6') { | ||
| 784 | panelList.value[2].options = props.smallCategoryList.slice(18, 20); | ||
| 785 | } | ||
| 786 | } | ||
| 787 | if (isSelectChange) { | ||
| 788 | val['smallCategory'] = panelList.value[2].options[0]?.paramValue; | ||
| 789 | } else if (!val['smallCategory']) { | ||
| 790 | val['smallCategory'] = getDefaultSmallCategory(formItems.value[0].default); | ||
| 791 | } else if (radioGroupChange && !init) {//切换规则类型。 | ||
| 792 | val['smallCategory'] = getDefaultSmallCategory(ruleCode); | ||
| 793 | } | ||
| 794 | } else { | ||
| 795 | p.default = val[field]; | ||
| 796 | } | ||
| 797 | }); | ||
| 798 | formItems.value[0].children = panelList.value; | ||
| 799 | console.log(panelList.value); | ||
| 800 | } | ||
| 801 | |||
| 802 | const radioGroupChange = (val, inlineValue, init) => { | ||
| 803 | formItems.value[0].default = val; | ||
| 804 | let list: any = panelList.value | ||
| 805 | list.forEach((item, index) => { | ||
| 806 | if (val == 'volatility_check') {//表行数波动率 | ||
| 807 | item.visible = true | ||
| 808 | if (index == 7 || index == 8 || index == 9 || index == 10 || index == 11 || index === 13) {// 7是规则设置,9是联合不为空 : 9+4 | ||
| 809 | item.visible = false | ||
| 810 | } | ||
| 811 | } else if (val === 'null_value_check') {//空值检查 | ||
| 812 | if (index === 3 || index === 4 || index === 5 || index === 6 || index == 7 || index == 8 || index == 9 || index == 11) { | ||
| 813 | item.visible = false | ||
| 814 | } else { //index为10的显示 | ||
| 815 | item.visible = true; | ||
| 816 | } | ||
| 817 | } else if (val === 'repeate_data_check') { //重复数据检查 | ||
| 818 | item.visible = false | ||
| 819 | if (index === 3 || index === 4 || index === 5 || index === 6 || index == 8 || index == 9 || index == 10 || index == 11 || index === 13) { | ||
| 820 | } else { | ||
| 821 | item.visible = true; | ||
| 822 | } | ||
| 823 | } else if (val === 'logic_check') { //逻辑检查 | ||
| 824 | item.visible = false | ||
| 825 | if (index === 3 || index === 4 || index === 5 || index === 6 || index == 7 || index == 9 || index == 10 || index == 11 || index === 13) { | ||
| 826 | } else { | ||
| 827 | item.visible = true; | ||
| 828 | } | ||
| 829 | } else if (val === 'custom_sql') {//自定义sql | ||
| 830 | item.visible = false | ||
| 831 | if (index === 3 || index === 4 || index === 5 || index === 6 || index == 7 || index == 8 || index == 10 || index == 11 || index === 13) { | ||
| 832 | } else { | ||
| 833 | item.visible = true; | ||
| 834 | } | ||
| 835 | } else if (val === 'rows_check') { //表行数 | ||
| 836 | item.visible = false | ||
| 837 | if (index === 3 || index === 4 || index === 5 || index === 6 || index == 7 || index == 8 || index == 9 || index == 10 || index === 13) { | ||
| 838 | } else { | ||
| 839 | item.visible = true; | ||
| 840 | } | ||
| 841 | } | ||
| 842 | }) | ||
| 843 | ruleType.value = val; | ||
| 844 | panelList.value = list; | ||
| 845 | Object.keys(inlineValue).length > 0 && setPanelListValue(inlineValue, false, init, true); | ||
| 846 | formItems.value[0].children = panelList.value; | ||
| 847 | } | ||
| 848 | |||
| 849 | const filterDialogVisible = ref(false); | ||
| 850 | /** 质检表 */ | ||
| 851 | const dialogSelectSubjectTable: any = ref({}); | ||
| 852 | /** 记录表对应的质检字段 */ | ||
| 853 | const dialogSelectSubjectTableField: any = ref({}); | ||
| 854 | |||
| 855 | const listItemClick = (data) => { | ||
| 856 | if (dialogSelectSubjectTable.value == data) { | ||
| 857 | return; | ||
| 858 | } | ||
| 859 | dialogSelectSubjectTable.value = data; | ||
| 860 | if (ruleType.value === 'custom_sql') { | ||
| 861 | sqlTableFilters.value[dialogSelectSubjectTable.value.enName] = sqlTableFilters.value[dialogSelectSubjectTable.value.enName] || ""; | ||
| 862 | sqlTableFieldInfo.value.loading = true; | ||
| 863 | getSubjectFields(data.guid).then((res: any) => { | ||
| 864 | sqlTableFieldInfo.value.loading = false; | ||
| 865 | if (res.code == proxy.$passCode) { | ||
| 866 | sqlTableFieldInfo.value.data = res.data || []; | ||
| 867 | } else { | ||
| 868 | ElMessage.error(res.msg); | ||
| 869 | } | ||
| 870 | }) | ||
| 871 | } else if (ruleType.value === 'logic_check') { | ||
| 872 | tableFilters.value[dialogSelectSubjectTable.value.enName] = tableFilters.value[dialogSelectSubjectTable.value.enName] || ""; | ||
| 873 | tableInfo.value.loading = true; | ||
| 874 | getSubjectFields(data.guid).then((res: any) => { | ||
| 875 | tableInfo.value.loading = false; | ||
| 876 | if (res.code == proxy.$passCode) { | ||
| 877 | tableInfo.value.data = res.data || []; | ||
| 878 | } else { | ||
| 879 | ElMessage.error(res.msg); | ||
| 880 | } | ||
| 881 | }) | ||
| 882 | } else if (ruleType.value == 'null_value_check' || ruleType.value == 'repeate_data_check') { | ||
| 883 | nullTableInfo.value.loading = true; | ||
| 884 | getSubjectFields(data.guid).then((res: any) => { | ||
| 885 | nullTableInfo.value.loading = false; | ||
| 886 | if (res.code == proxy.$passCode) { | ||
| 887 | nullTableInfo.value.data = res.data || []; | ||
| 888 | nextTick(() => { | ||
| 889 | let tagData = nullSelectFields.value[dialogSelectSubjectTable.value.chName] || []; | ||
| 890 | tagData?.forEach((d) => { | ||
| 891 | let row = nullTableInfo.value.data.find((v: any) => v.guid == d.guid); | ||
| 892 | if (row) { | ||
| 893 | nullTableRef.value?.tableRef?.toggleRowSelection(row, true); | ||
| 894 | } | ||
| 895 | }); | ||
| 896 | }); | ||
| 897 | } else { | ||
| 898 | ElMessage.error(res.msg); | ||
| 899 | } | ||
| 900 | }) | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 904 | const tableListInfo = ref({ | ||
| 905 | id: 'subject-list', | ||
| 906 | field: 'label', | ||
| 907 | getName: (item) => { | ||
| 908 | return `${item.enName}(${item.chName})`; | ||
| 909 | }, | ||
| 910 | data: props.toSubjectTables | ||
| 911 | }) | ||
| 912 | |||
| 913 | const tableInfo: any = ref({ | ||
| 914 | id: 'subject-fields-table', | ||
| 915 | loading: false, | ||
| 916 | fields: [ | ||
| 917 | { label: "字段名", field: "enName", width: 140, type: 'text_btn', value: 'enName', columClass: 'text_btn' }, | ||
| 918 | { label: "注释", field: "chName", width: 120 }, | ||
| 919 | { label: "数据类型", field: "dataTypeChName", width: 100 }, | ||
| 920 | ], | ||
| 921 | data: [], | ||
| 922 | showPage: false, | ||
| 923 | actionInfo: { | ||
| 924 | show: !props.readonly, | ||
| 925 | label: "操作", | ||
| 926 | type: "btn", | ||
| 927 | width: 90, | ||
| 928 | btns: (scope) => { | ||
| 929 | if (scope.row.enName === dialogSelectSubjectTableField.value[dialogSelectSubjectTable.value.enName]?.enName) { | ||
| 930 | return [ | ||
| 931 | { label: "取消选择", value: "delSelect" }, | ||
| 932 | ] | ||
| 933 | } | ||
| 934 | return [ | ||
| 935 | { label: "选择", value: "select" }, | ||
| 936 | ] | ||
| 937 | }, | ||
| 938 | }, | ||
| 939 | }) | ||
| 940 | |||
| 941 | /** 逻辑检查时,记录表对应的逻辑条件 */ | ||
| 942 | const tableFilters: any = ref({}); | ||
| 943 | /** 逻辑检查时,记录表对应的逻辑条件的检验结果。未检验通过的不能保存。 */ | ||
| 944 | const tableFilterValidates: any = ref({}); | ||
| 945 | |||
| 946 | const cancelFilterDialog = () => { | ||
| 947 | filterDialogVisible.value = false; | ||
| 948 | } | ||
| 949 | |||
| 950 | const filterFormListRef: any = ref({}); | ||
| 951 | |||
| 952 | const submitFilter = () => { | ||
| 953 | let v: any = []; | ||
| 954 | for (const table in tableFilters.value) { | ||
| 955 | if (tableFilters.value[table]) { | ||
| 956 | if (!dialogSelectSubjectTableField.value[table]?.enName) { | ||
| 957 | if (dialogSelectSubjectTable.value.enName != table) { | ||
| 958 | filterFormListRef.value.setSelectList(table, 'enName'); | ||
| 959 | } | ||
| 960 | ElMessage.error(`表【${table}】设置了质检条件,但未选择质检字段`); | ||
| 961 | return; | ||
| 962 | } | ||
| 963 | if (tableFilterValidates.value[table] !== true) { | ||
| 964 | if (dialogSelectSubjectTable.value.enName != table) { | ||
| 965 | filterFormListRef.value.setSelectList(table, 'enName'); | ||
| 966 | } | ||
| 967 | ElMessage.error(`请先验证表【${table}】设置的质检条件`); | ||
| 968 | return; | ||
| 969 | } | ||
| 970 | v.push(table); | ||
| 971 | } else { | ||
| 972 | if (dialogSelectSubjectTableField.value[table]?.enName) { | ||
| 973 | if (dialogSelectSubjectTable.value.enName != table) { | ||
| 974 | filterFormListRef.value.setSelectList(table, 'enName'); | ||
| 975 | } | ||
| 976 | ElMessage.error(`表【${table}】选择了质检字段,但未设置质检条件`); | ||
| 977 | return; | ||
| 978 | } | ||
| 979 | } | ||
| 980 | } | ||
| 981 | if (!v.length) { | ||
| 982 | ElMessage.error('当前未给表设置质检条件!'); | ||
| 983 | return; | ||
| 984 | } | ||
| 985 | let index = 8; | ||
| 986 | panelList.value[index].defaultValue = { | ||
| 987 | ruleFields: dialogSelectSubjectTableField.value, | ||
| 988 | conditionSqls: tableFilters.value | ||
| 989 | }; | ||
| 990 | let str = ""; | ||
| 991 | for (const key in dialogSelectSubjectTableField.value) { | ||
| 992 | let field = dialogSelectSubjectTableField.value[key]; | ||
| 993 | str = str + (str ? ';' : '') + field.enName; | ||
| 994 | } | ||
| 995 | let formInline = oldOriginValue.value = Object.assign({ | ||
| 996 | qualityModelGuids: props.toSubjectTables.map(s => s.guid), | ||
| 997 | parity: 1, | ||
| 998 | compareWay: 1, | ||
| 999 | jointly: 'N', | ||
| 1000 | bizState: 'Y' | ||
| 1001 | }, oldOriginValue.value, ruleFormRef.value.formInline); | ||
| 1002 | formInline[`${panelList.value[index].field}`] = str; | ||
| 1003 | setPanelListValue(formInline); | ||
| 1004 | filterDialogVisible.value = false; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | const filterInputRef = ref(); | ||
| 1008 | |||
| 1009 | const filterTableBtnClick = (scope, btn) => { | ||
| 1010 | if (btn.value === 'select') { | ||
| 1011 | dialogSelectSubjectTableField.value[dialogSelectSubjectTable.value.enName] = scope.row; | ||
| 1012 | } else if (btn.value === 'delSelect') { | ||
| 1013 | dialogSelectSubjectTableField.value[dialogSelectSubjectTable.value.enName] = {}; | ||
| 1014 | } else { | ||
| 1015 | let selectField = scope.row.enName + ' '; | ||
| 1016 | let children = filterInputRef.value?.$el?.children?.[0]; | ||
| 1017 | let selectionStart = children?.selectionStart; | ||
| 1018 | let filter = tableFilters.value[dialogSelectSubjectTable.value.enName] || ""; | ||
| 1019 | if (filter) { | ||
| 1020 | tableFilters.value[dialogSelectSubjectTable.value.enName] = filter.slice(0, selectionStart) + selectField + filter.slice(selectionStart); | ||
| 1021 | nextTick(() => { | ||
| 1022 | children.focus(); | ||
| 1023 | let index = selectionStart + selectField.length; | ||
| 1024 | children.setSelectionRange(index, index); | ||
| 1025 | }) | ||
| 1026 | return; | ||
| 1027 | } else { | ||
| 1028 | tableFilters.value[dialogSelectSubjectTable.value.enName] = selectField; | ||
| 1029 | nextTick(() => { | ||
| 1030 | children.focus(); | ||
| 1031 | filterInputRef.value.selectionStart = filter.length; | ||
| 1032 | filterInputRef.value.selectionEnd = filter.length; | ||
| 1033 | }); | ||
| 1034 | } | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | const validateFilterBtnDisable = ref(false); | ||
| 1039 | |||
| 1040 | /** 检验逻辑条件是否合理。 */ | ||
| 1041 | const validateFilter = () => { | ||
| 1042 | let enName = dialogSelectSubjectTable.value.enName; | ||
| 1043 | if (!tableFilters.value[enName]) { | ||
| 1044 | ElMessage.error('请先填写质检条件再验证sql'); | ||
| 1045 | return; | ||
| 1046 | } | ||
| 1047 | validateFilterBtnDisable.value = true; | ||
| 1048 | validateSubjectTableRule({ | ||
| 1049 | subjectGuid: dialogSelectSubjectTable.value.guid, | ||
| 1050 | condition: tableFilters.value[enName] | ||
| 1051 | }).then((res: any) => { | ||
| 1052 | validateFilterBtnDisable.value = false; | ||
| 1053 | if (res.code == proxy.$passCode) { | ||
| 1054 | tableFilterValidates.value[enName] = true; | ||
| 1055 | ElMessage.success('验证通过'); | ||
| 1056 | } else { | ||
| 1057 | ElMessage.error(res.msg); | ||
| 1058 | } | ||
| 1059 | }); | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | const handleFilterChange = () => { | ||
| 1063 | tableFilterValidates.value[dialogSelectSubjectTable.value.enName] = false; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | const tableRowDialogVisible = ref(false); | ||
| 1067 | const tableRowRulesDataLoading = ref(false); | ||
| 1068 | const tableRowRulesData: any = ref([{ | ||
| 1069 | differenceRange: 0 | ||
| 1070 | }]); | ||
| 1071 | |||
| 1072 | const addRowRules = () => { | ||
| 1073 | tableRowRulesData.value.push({ | ||
| 1074 | differenceRange: 0 | ||
| 1075 | }); | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | const deleteRow = (scope) => { | ||
| 1079 | tableRowRulesData.value.splice(scope.$index, 1); | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | const cancelTableRowDialog = () => { | ||
| 1083 | tableRowDialogVisible.value = false; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | const submitTableRow = () => { | ||
| 1087 | let v: any = []; | ||
| 1088 | if (!tableRowRulesData.value.length) { | ||
| 1089 | ElMessage.error('表行数检查规则不能为空'); | ||
| 1090 | return; | ||
| 1091 | } | ||
| 1092 | let index: number = 0; | ||
| 1093 | for (const rule of tableRowRulesData.value) { | ||
| 1094 | if (!rule.mainTable) { | ||
| 1095 | ElMessage.error('主表不能为空'); | ||
| 1096 | return; | ||
| 1097 | } | ||
| 1098 | if (!rule.contrastSubjectGuid) { | ||
| 1099 | ElMessage.error('对比表不能为空'); | ||
| 1100 | return; | ||
| 1101 | } | ||
| 1102 | if (rule.mainTable == rule.contrastSubjectGuid) { | ||
| 1103 | ElMessage.error('主表和对比表不能选择同一个'); | ||
| 1104 | return; | ||
| 1105 | } | ||
| 1106 | let label = props.toSubjectTables.find(s => s.guid === rule.mainTable).enName; | ||
| 1107 | if (v.includes(label)) { | ||
| 1108 | ElMessage.error('主表选择重复'); | ||
| 1109 | return; | ||
| 1110 | } | ||
| 1111 | v.push(label); | ||
| 1112 | } | ||
| 1113 | let formInline = oldOriginValue.value = Object.assign({ | ||
| 1114 | qualityModelGuids: props.toSubjectTables.map(s => s.guid), | ||
| 1115 | parity: 1, | ||
| 1116 | compareWay: 1, | ||
| 1117 | jointly: 'N', | ||
| 1118 | bizState: 'Y' | ||
| 1119 | }, oldOriginValue.value, ruleFormRef.value.formInline); | ||
| 1120 | panelList.value[11].defaultValue = tableRowRulesData.value; | ||
| 1121 | formInline[`${panelList.value[11].field}`] = v.join(';'); | ||
| 1122 | setPanelListValue(formInline); | ||
| 1123 | tableRowDialogVisible.value = false; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | const sqlTableFieldInfo: any = ref({ | ||
| 1127 | id: 'subject-fields-table', | ||
| 1128 | loading: false, | ||
| 1129 | fields: [ | ||
| 1130 | { label: "字段名", field: "enName", width: 140, type: 'text_btn', value: 'enName', columClass: 'text_btn' }, | ||
| 1131 | { label: "注释", field: "chName", width: 120 }, | ||
| 1132 | { label: "数据类型", field: "dataTypeChName", width: 100 }, | ||
| 1133 | ], | ||
| 1134 | data: [], | ||
| 1135 | showPage: false, | ||
| 1136 | actionInfo: { | ||
| 1137 | show: false | ||
| 1138 | }, | ||
| 1139 | }) | ||
| 1140 | |||
| 1141 | const sqlInputRef: any = ref(null); | ||
| 1142 | |||
| 1143 | const sqlTableBtnClick = (scope, btn) => { | ||
| 1144 | let selectField = scope.row.enName + ' '; | ||
| 1145 | let children = sqlInputRef.value?.$el?.children?.[0]; | ||
| 1146 | let selectionStart = children?.selectionStart; | ||
| 1147 | let filter = sqlTableFilters.value[dialogSelectSubjectTable.value.enName] || ""; | ||
| 1148 | if (filter) { | ||
| 1149 | sqlTableFilters.value[dialogSelectSubjectTable.value.enName] = filter.slice(0, selectionStart) + selectField + filter.slice(selectionStart); | ||
| 1150 | nextTick(() => { | ||
| 1151 | children.focus(); | ||
| 1152 | let index = selectionStart + selectField.length; | ||
| 1153 | children.setSelectionRange(index, index); | ||
| 1154 | }) | ||
| 1155 | return; | ||
| 1156 | } else { | ||
| 1157 | sqlTableFilters.value[dialogSelectSubjectTable.value.enName] = selectField; | ||
| 1158 | nextTick(() => { | ||
| 1159 | children.focus(); | ||
| 1160 | sqlInputRef.value.selectionStart = filter.length; | ||
| 1161 | sqlInputRef.value.selectionEnd = filter.length; | ||
| 1162 | }); | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | const sqlDialogVisible = ref(false); | ||
| 1167 | /** 记录表对应的自定义sql */ | ||
| 1168 | const sqlTableFilters = ref({}); | ||
| 1169 | /** 记录验证的sql */ | ||
| 1170 | const sqlFilterValidates = ref({}); | ||
| 1171 | /** 记录自定义sql选择的对应字段。 */ | ||
| 1172 | const sqlSelectField = ref({}); | ||
| 1173 | |||
| 1174 | /** 自定义sql的下拉列表。 */ | ||
| 1175 | const sqlFieldsList = ref({}); | ||
| 1176 | |||
| 1177 | const sqlFormListRef = ref(); | ||
| 1178 | |||
| 1179 | const cancelSqlDialog = () => { | ||
| 1180 | sqlDialogVisible.value = false; | ||
| 1181 | }; | ||
| 1182 | |||
| 1183 | const submitSql = () => { | ||
| 1184 | let v: any = []; | ||
| 1185 | for (const table in sqlTableFilters.value) { | ||
| 1186 | if (sqlTableFilters.value[table]) { | ||
| 1187 | if (sqlFilterValidates.value[table] !== true) { | ||
| 1188 | if (dialogSelectSubjectTable.value.enName != table) { | ||
| 1189 | sqlFormListRef.value.setSelectList(table, 'enName'); | ||
| 1190 | } | ||
| 1191 | ElMessage.error(`请验证表【${table}】的sql`); | ||
| 1192 | return; | ||
| 1193 | } | ||
| 1194 | if (!sqlSelectField.value[table]?.length) { | ||
| 1195 | if (dialogSelectSubjectTable.value.enName != table) { | ||
| 1196 | sqlFormListRef.value.setSelectList(table, 'enName'); | ||
| 1197 | } | ||
| 1198 | ElMessage.error(`表【${table}】设置了sql,但未选择质检字段`); | ||
| 1199 | return; | ||
| 1200 | } | ||
| 1201 | v.push(table); | ||
| 1202 | } | ||
| 1203 | } | ||
| 1204 | if (!v.length) { | ||
| 1205 | ElMessage.error('未给质检表设置sql!'); | ||
| 1206 | return; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | let str = ""; | ||
| 1210 | for (const key in sqlSelectField.value) { | ||
| 1211 | let field = sqlSelectField.value[key]; | ||
| 1212 | str = str + (str ? ';' : '') + key + ':' + field.join(','); | ||
| 1213 | } | ||
| 1214 | let formInline = oldOriginValue.value = Object.assign({ | ||
| 1215 | qualityModelGuids: props.toSubjectTables.map(s => s.guid), | ||
| 1216 | parity: 1, | ||
| 1217 | compareWay: 1, | ||
| 1218 | jointly: 'N', | ||
| 1219 | bizState: 'Y' | ||
| 1220 | }, oldOriginValue.value, ruleFormRef.value.formInline); | ||
| 1221 | let fList = {}; | ||
| 1222 | for (const key in sqlFieldsList.value) { | ||
| 1223 | fList[key] = sqlFieldsList.value[key]?.map(s => s.value) || []; | ||
| 1224 | } | ||
| 1225 | panelList.value[9].defaultValue = { | ||
| 1226 | sqlFieldsList: fList, | ||
| 1227 | ruleFields: sqlSelectField.value, | ||
| 1228 | customSqls: sqlTableFilters.value | ||
| 1229 | }; | ||
| 1230 | formInline[`${panelList.value[9].field}`] = str; | ||
| 1231 | setPanelListValue(formInline); | ||
| 1232 | sqlDialogVisible.value = false; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | const validateSqlBtnDisable = ref(false); | ||
| 1236 | |||
| 1237 | /** 检验自定义sql。 */ | ||
| 1238 | const validateSql = () => { | ||
| 1239 | let enName = dialogSelectSubjectTable.value.enName; | ||
| 1240 | if (!sqlTableFilters.value[enName]) { | ||
| 1241 | ElMessage.error('请先填写自定义sql再验证'); | ||
| 1242 | return; | ||
| 1243 | } | ||
| 1244 | validateSqlBtnDisable.value = true; | ||
| 1245 | validateCustomSql({ | ||
| 1246 | subjectGuid: dialogSelectSubjectTable.value.guid, | ||
| 1247 | sqlScript: sqlTableFilters.value[enName] | ||
| 1248 | }).then((res: any) => { | ||
| 1249 | validateSqlBtnDisable.value = false; | ||
| 1250 | if (res?.code == proxy.$passCode) { | ||
| 1251 | sqlFilterValidates.value[enName] = true; | ||
| 1252 | sqlFieldsList.value[enName] = res.data?.map(d => { | ||
| 1253 | return { | ||
| 1254 | value: d | ||
| 1255 | } | ||
| 1256 | }) || []; | ||
| 1257 | sqlSelectField.value[dialogSelectSubjectTable.value.enName] = res.data || []; | ||
| 1258 | ElMessage.success('验证通过'); | ||
| 1259 | } else { | ||
| 1260 | ElMessage.error(res.msg); | ||
| 1261 | } | ||
| 1262 | }); | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | const handleSqlChange = () => { | ||
| 1266 | sqlFilterValidates.value[dialogSelectSubjectTable.value.enName] = false; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | const nullTableInfo: any = ref({ | ||
| 1270 | id: 'subject-fields-table', | ||
| 1271 | loading: false, | ||
| 1272 | multiple: !props.readonly, | ||
| 1273 | fields: [ | ||
| 1274 | { label: "字段名", field: "enName", width: 140 }, | ||
| 1275 | { label: "注释", field: "chName", width: 120 }, | ||
| 1276 | { label: "数据类型", field: "dataTypeChName", width: 100 }, | ||
| 1277 | ], | ||
| 1278 | data: [], | ||
| 1279 | showPage: false, | ||
| 1280 | actionInfo: { | ||
| 1281 | show: false | ||
| 1282 | }, | ||
| 1283 | }) | ||
| 1284 | |||
| 1285 | const nullSelectFields: any = ref({}); | ||
| 1286 | |||
| 1287 | const nullTableRef = ref(); | ||
| 1288 | |||
| 1289 | const emptySelectFields = () => { | ||
| 1290 | let tagData = nullSelectFields.value[dialogSelectSubjectTable.value.chName] || []; | ||
| 1291 | if (tagData.length) { | ||
| 1292 | nullTableRef.value?.tableRef?.clearSelection(); | ||
| 1293 | } | ||
| 1294 | nullSelectFields.value = {}; | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | const uncheck = (tag, index, key) => { | ||
| 1298 | let data = nullSelectFields.value[key]; | ||
| 1299 | data.splice(index, 1); | ||
| 1300 | let row = nullTableInfo.value.data.find(d => d.guid == tag.guid); | ||
| 1301 | nullTableRef.value?.tableRef?.toggleRowSelection(row, false); | ||
| 1302 | if (!data.length) { | ||
| 1303 | delete nullSelectFields.value[key]; | ||
| 1304 | } | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | const nullTableCheckboxSelectChange = (select, row) => { | ||
| 1308 | let data = nullSelectFields.value[dialogSelectSubjectTable.value.chName] || []; | ||
| 1309 | let isSelect = select.some(s => s.guid === row.guid); | ||
| 1310 | if (isSelect) { | ||
| 1311 | if (!data.length) { | ||
| 1312 | nullSelectFields.value[dialogSelectSubjectTable.value.chName] = data; | ||
| 1313 | } | ||
| 1314 | data.push(row); | ||
| 1315 | } else { | ||
| 1316 | let index = data.findIndex(d => d.guid === row.guid); | ||
| 1317 | if (index > -1) { | ||
| 1318 | data.splice(index, 1); | ||
| 1319 | if (!data.length) { | ||
| 1320 | delete nullSelectFields.value[dialogSelectSubjectTable.value.chName]; | ||
| 1321 | } | ||
| 1322 | } | ||
| 1323 | } | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | const nullTableCheckboxAllSelectChange = (select) => { | ||
| 1327 | let data = nullSelectFields.value[dialogSelectSubjectTable.value.chName] || []; | ||
| 1328 | if (!select.length) { | ||
| 1329 | let tableData = nullTableInfo.value.data; | ||
| 1330 | if (data.length) { | ||
| 1331 | tableData.forEach(d => { | ||
| 1332 | let index = data.findIndex(t => t.guid === d.guid); | ||
| 1333 | if (index > -1) { | ||
| 1334 | data.splice(index, 1); | ||
| 1335 | if (!data.length) { | ||
| 1336 | delete nullSelectFields.value[dialogSelectSubjectTable.value.chName]; | ||
| 1337 | } | ||
| 1338 | } | ||
| 1339 | }) | ||
| 1340 | } | ||
| 1341 | } else { | ||
| 1342 | let tableData = nullTableInfo.value.data; | ||
| 1343 | tableData.forEach(d => { | ||
| 1344 | let index = data.findIndex(t => t.guid === d.guid); | ||
| 1345 | if (index === -1) { | ||
| 1346 | if (!data.length) { | ||
| 1347 | nullSelectFields.value[dialogSelectSubjectTable.value.chName] = data; | ||
| 1348 | } | ||
| 1349 | data.push(d); | ||
| 1350 | } | ||
| 1351 | }) | ||
| 1352 | } | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | /** 对比的主题表可选择的数据 */ | ||
| 1356 | const contrastSubjects: any = ref([]); | ||
| 1357 | |||
| 1358 | const getSubjectTableByDomainData = (guid) => { | ||
| 1359 | return getSubjectTableByDomain(guid).then((res: any) => { | ||
| 1360 | if (res.code == proxy.$passCode) { | ||
| 1361 | return res.data?.map(d => { | ||
| 1362 | d.isLeaf = true; | ||
| 1363 | d.name = `${d.enName}(${d.chName})`; | ||
| 1364 | d.parentGuid = guid; | ||
| 1365 | return d; | ||
| 1366 | }) || []; | ||
| 1367 | } | ||
| 1368 | }) | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | /** 主题表懒加载。 */ | ||
| 1372 | const treeSelectLoad = (node, resolve) => { | ||
| 1373 | if (node.level == 0) { | ||
| 1374 | resolve(contrastSubjects.value); | ||
| 1375 | } else if (node.level === 1) { | ||
| 1376 | let guid = node.data.guid; | ||
| 1377 | resolve(contrastSubjects.value.find((t: any) => t.guid === guid)?.children || []); | ||
| 1378 | } else if (node.level === 2) { | ||
| 1379 | getSubjectTableByDomainData(node.data.guid).then((d) => { | ||
| 1380 | node.data.children = d; | ||
| 1381 | resolve(d); | ||
| 1382 | }) | ||
| 1383 | } | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | const defaultExpandedKeys = computed(() => { | ||
| 1387 | return props.value && contrastSubjects.value?.length ? [contrastSubjects.value.find(c => c.children?.some(cc => cc.guid == props.value.contrastSubjectDomainGuid)).guid, props.value.contrastSubjectDomainGuid] : []; | ||
| 1388 | }) | ||
| 1389 | |||
| 1390 | const contrastSubjectInputFilterMethod = (v, data) => { | ||
| 1391 | if (!v) { | ||
| 1392 | return true; | ||
| 1393 | } | ||
| 1394 | return data.label?.includes(v) || data.name?.includes(v) || | ||
| 1395 | data.chName?.includes(v) || | ||
| 1396 | data.enName?.includes(v); | ||
| 1397 | }; | ||
| 1398 | |||
| 1399 | const getFormInfo = () => { | ||
| 1400 | let formInline = ruleFormRef.value.formInline; | ||
| 1401 | let ruleName = props.ruleTypeList.find(option => option.ruleCode == formInline.ruleCode).ruleName; | ||
| 1402 | if (formInline.ruleCode == 'repeate_data_check') { | ||
| 1403 | let v = panelList.value[7].defaultValue; | ||
| 1404 | return Object.assign({}, formInline, { | ||
| 1405 | modelFields: v, | ||
| 1406 | ruleName: ruleName | ||
| 1407 | }); | ||
| 1408 | } else if (formInline.ruleCode == 'null_value_check') { | ||
| 1409 | let v = panelList.value[10].defaultValue; | ||
| 1410 | return Object.assign({}, formInline, { | ||
| 1411 | modelFields: v, | ||
| 1412 | ruleName: ruleName | ||
| 1413 | }); | ||
| 1414 | } else if (formInline.ruleCode == 'logic_check') { | ||
| 1415 | let v = panelList.value[8].defaultValue; | ||
| 1416 | return Object.assign({}, formInline, v, { | ||
| 1417 | ruleName: ruleName | ||
| 1418 | }); | ||
| 1419 | } else if (formInline.ruleCode == 'custom_sql') { | ||
| 1420 | let v = panelList.value[9].defaultValue; | ||
| 1421 | return Object.assign({}, formInline, v, { | ||
| 1422 | ruleName: ruleName | ||
| 1423 | }); | ||
| 1424 | } else if (formInline.ruleCode == 'volatility_check') { | ||
| 1425 | return formInline; | ||
| 1426 | } else if (formInline.ruleCode == 'rows_check') { | ||
| 1427 | let v = panelList.value[11].defaultValue; | ||
| 1428 | return Object.assign({}, formInline, { | ||
| 1429 | rows: v, | ||
| 1430 | ruleName: ruleName | ||
| 1431 | }); | ||
| 1432 | } | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | defineExpose({ | ||
| 1436 | getFormInfo, | ||
| 1437 | ruleFormRef | ||
| 1438 | }); | ||
| 1439 | |||
| 1440 | </script> | ||
| 1441 | |||
| 1442 | <template> | ||
| 1443 | <Form ref="ruleFormRef" :readonly="props.readonly" :itemList="formItems" formId="edit-standard-form" | ||
| 1444 | :rules="formRules" col="col3" @btnClick="formBtnClick" @selectChange="selectChange" | ||
| 1445 | @radioGroupChange="(val, inlineValue) => radioGroupChange(val, inlineValue, false)" /> | ||
| 1446 | |||
| 1447 | <!-- 空值, 重复数据检查 --> | ||
| 1448 | <el-dialog v-model="dialogVisible" title="规则设置" width="850" :modal="true" :close-on-click-modal="false" | ||
| 1449 | destroy-on-close align-center> | ||
| 1450 | <div class="filter-dialog-content" :style="{ height: '500px' }"> | ||
| 1451 | <div class="filter-table-list"> | ||
| 1452 | <div class="left-title">质检表</div> | ||
| 1453 | <ListPanel class="list_unit" ref="formListRef" :listInfo="tableListInfo" @itemClick="listItemClick" /> | ||
| 1454 | </div> | ||
| 1455 | <div class="empty-table-field"> | ||
| 1456 | <div class="left-title">字段列表详情</div> | ||
| 1457 | <Table style="height: calc(100% - 32px)" ref="nullTableRef" :tableInfo="nullTableInfo" | ||
| 1458 | @table-checkbox-select-change="nullTableCheckboxSelectChange" | ||
| 1459 | @table-checkbox-all-select-change="nullTableCheckboxAllSelectChange"></Table> | ||
| 1460 | </div> | ||
| 1461 | <div class="empty-edit"> | ||
| 1462 | <div class="header-title"> | ||
| 1463 | <span class="left-title">已选</span> | ||
| 1464 | <span v-if="!props.readonly" class="text_btn" @click="emptySelectFields()">清空</span> | ||
| 1465 | </div> | ||
| 1466 | <div class="tags_list_panel"> | ||
| 1467 | <div class="table_item" v-for="(tableFields, key) in nullSelectFields" :key="key"> | ||
| 1468 | <div class="tag_title">{{ key }}</div> | ||
| 1469 | <div class="tag_item" v-for="(tag, index) in (tableFields || [])" :key="tag.guid"> | ||
| 1470 | <span class="tag"> | ||
| 1471 | <ellipsis-tooltip :content="`${tag.enName}(${tag.chName})`" class-name="tag-width" | ||
| 1472 | :refName="'tooltipOver' + tag.guid"></ellipsis-tooltip> | ||
| 1473 | <el-icon v-if="!props.readonly" @click="uncheck(tag, index, key)"> | ||
| 1474 | <Close /> | ||
| 1475 | </el-icon> | ||
| 1476 | </span> | ||
| 1477 | </div> | ||
| 1478 | </div> | ||
| 1479 | </div> | ||
| 1480 | </div> | ||
| 1481 | </div> | ||
| 1482 | <template #footer v-if="!props.readonly"> | ||
| 1483 | <div class="dialog-footer"> | ||
| 1484 | <el-button @click="cancelDialog">取消</el-button> | ||
| 1485 | <el-button @click="submit" type="primary" v-preReClick>确定</el-button> | ||
| 1486 | </div> | ||
| 1487 | </template> | ||
| 1488 | </el-dialog> | ||
| 1489 | |||
| 1490 | <!-- 自定义sql --> | ||
| 1491 | <el-dialog v-model="sqlDialogVisible" title="规则设置" width="800" :modal="true" :close-on-click-modal="false" | ||
| 1492 | destroy-on-close align-center> | ||
| 1493 | <div class="sql-dialog-content" :style="{ height: '500px' }"> | ||
| 1494 | <div class="sql-table-list"> | ||
| 1495 | <div class="left-title">质检表</div> | ||
| 1496 | <ListPanel class="list_unit" ref="sqlFormListRef" :listInfo="tableListInfo" @itemClick="listItemClick" /> | ||
| 1497 | </div> | ||
| 1498 | <div class="table-field"> | ||
| 1499 | <div class="left-title">字段列表详情</div> | ||
| 1500 | <Table style="height: calc(100% - 32px)" ref="sqlFormTableRef" :tableInfo="sqlTableFieldInfo" | ||
| 1501 | @tableBtnClick="sqlTableBtnClick"></Table> | ||
| 1502 | </div> | ||
| 1503 | <div class="sql-edit"> | ||
| 1504 | <div class="field-title"> | ||
| 1505 | <span>自定义sql</span> | ||
| 1506 | <el-button v-if="!props.readonly" @click="validateSql" link v-preReClick>验证</el-button> | ||
| 1507 | </div> | ||
| 1508 | <el-input v-model="sqlTableFilters[dialogSelectSubjectTable.enName]" ref="sqlInputRef" type="textarea" | ||
| 1509 | :disabled="props.readonly" :autosize="true" resize="none" @change="handleSqlChange"></el-input> | ||
| 1510 | <div class="left-title">质检字段<label style="color: red;">*</label></div> | ||
| 1511 | <el-select v-model="sqlSelectField[dialogSelectSubjectTable.enName]" placeholder="请选择" multiple filterable | ||
| 1512 | clearable :disabled="props.readonly"> | ||
| 1513 | <el-option v-for="opt in sqlFieldsList[dialogSelectSubjectTable.enName]" :key="opt['value']" | ||
| 1514 | :label="opt['value']" :value="opt['value']" /> | ||
| 1515 | </el-select> | ||
| 1516 | </div> | ||
| 1517 | </div> | ||
| 1518 | |||
| 1519 | <template #footer v-if="!props.readonly"> | ||
| 1520 | <div class="dialog-footer"> | ||
| 1521 | <el-button @click="cancelSqlDialog" v-preReClick>取消</el-button> | ||
| 1522 | <el-button @click="submitSql" type="primary" v-preReClick>确定</el-button> | ||
| 1523 | </div> | ||
| 1524 | </template> | ||
| 1525 | </el-dialog> | ||
| 1526 | |||
| 1527 | <!-- 逻辑检查 --> | ||
| 1528 | <el-dialog v-model="filterDialogVisible" title="规则设置" width="850" :modal="true" :close-on-click-modal="false" | ||
| 1529 | destroy-on-close align-center> | ||
| 1530 | <div class="filter-dialog-content" :style="{ height: '500px' }"> | ||
| 1531 | <div class="filter-table-list"> | ||
| 1532 | <div class="left-title">质检表</div> | ||
| 1533 | <ListPanel class="list_unit" ref="filterFormListRef" :listInfo="tableListInfo" @itemClick="listItemClick" /> | ||
| 1534 | </div> | ||
| 1535 | <div class="table-field"> | ||
| 1536 | <div class="left-title">字段列表详情</div> | ||
| 1537 | <Table style="height: calc(100% - 32px)" ref="formTableRef" :tableInfo="tableInfo" | ||
| 1538 | @tableBtnClick="filterTableBtnClick"></Table> | ||
| 1539 | </div> | ||
| 1540 | <div class="filter-edit"> | ||
| 1541 | <div class="left-title">{{ '质检字段' }}<label style="color: red;">*</label></div> | ||
| 1542 | <el-input :model-value="dialogSelectSubjectTableField[dialogSelectSubjectTable.enName]?.enName || ''" | ||
| 1543 | :disabled="true" placeholder="请从左侧列表选择质检字段"></el-input> | ||
| 1544 | <div class="field-title"> | ||
| 1545 | <span>质检条件</span> | ||
| 1546 | <el-button v-if="!props.readonly" @click="validateFilter" link v-preReClick>验证</el-button> | ||
| 1547 | </div> | ||
| 1548 | <el-input v-model="tableFilters[dialogSelectSubjectTable.enName]" @change="handleFilterChange" | ||
| 1549 | ref="filterInputRef" type="textarea" :autosize="true" resize="none"></el-input> | ||
| 1550 | </div> | ||
| 1551 | </div> | ||
| 1552 | |||
| 1553 | <template #footer v-if="!props.readonly"> | ||
| 1554 | <div class="dialog-footer"> | ||
| 1555 | <el-button @click="cancelFilterDialog" v-preReClick>取消</el-button> | ||
| 1556 | <el-button @click="submitFilter" type="primary" v-preReClick>确定</el-button> | ||
| 1557 | </div> | ||
| 1558 | </template> | ||
| 1559 | </el-dialog> | ||
| 1560 | |||
| 1561 | <!-- 表行数检查 --> | ||
| 1562 | <el-dialog v-model="tableRowDialogVisible" title="规则设置" width="750" :modal="true" :close-on-click-modal="false" | ||
| 1563 | destroy-on-close align-center> | ||
| 1564 | <div class="row-dialog-content"> | ||
| 1565 | <el-table ref="rowTableRef" :data="tableRowRulesData" height="100%" :highlight-current-row="true" stripe | ||
| 1566 | v-loading="tableRowRulesDataLoading" tooltip-effect="light" border | ||
| 1567 | :style="{ height: 'calc(100% - 28px)', width: 'auto', 'max-width': '100%', display: 'inline-block' }"> | ||
| 1568 | <el-table-column prop="mainTable" label="选择主表" width="200px" align="left" show-overflow-tooltip> | ||
| 1569 | <template #default="scope"> | ||
| 1570 | <el-select v-if="!props.readonly" v-model="scope.row['mainTable']" placeholder="请选择"> | ||
| 1571 | <el-option v-for="opt in toSubjectTables" :key="opt['guid']" :label="opt['label']" :value="opt['guid']" /> | ||
| 1572 | </el-select> | ||
| 1573 | <span v-else>{{ scope.row['mainTableName'] + `(${scope.row['mainTableZhName']})` }}</span> | ||
| 1574 | </template> | ||
| 1575 | </el-table-column> | ||
| 1576 | <el-table-column prop="contrastSubjectGuid" label="选择对比表" width="200px" align="left" show-overflow-tooltip> | ||
| 1577 | <template #default="scope"> | ||
| 1578 | <el-tree-select v-if="!props.readonly" ref="treeSelectRef" filterable clearable | ||
| 1579 | v-model="scope.row['contrastSubjectGuid']" node-key="guid" :data="contrastSubjects" placeholder="请选择" lazy | ||
| 1580 | :load="(node, resolve) => treeSelectLoad(node, resolve)" :default-expanded-keys="defaultExpandedKeys" | ||
| 1581 | :auto-expand-parent="true" :default-checked-keys="[props.value.contrastSubjectGuid]" | ||
| 1582 | :filter-node-method="contrastSubjectInputFilterMethod" :props="{ | ||
| 1583 | value: 'guid', | ||
| 1584 | label: 'name', | ||
| 1585 | children: 'children', | ||
| 1586 | isLeaf: 'isLeaf' | ||
| 1587 | }"> | ||
| 1588 | </el-tree-select> | ||
| 1589 | <span v-else>{{ scope.row['contrastSubjectName'] + `(${scope.row['contrastSubjectZhName']})` }}</span> | ||
| 1590 | </template> | ||
| 1591 | </el-table-column> | ||
| 1592 | <el-table-column prop="differenceRange" label="差值范围" width="200px" :align="props.readonly ? 'right' : 'left'" | ||
| 1593 | show-overflow-tooltip> | ||
| 1594 | |||
| 1595 | <template #default="scope"> | ||
| 1596 | <el-input-number v-if="!props.readonly" v-model="scope.row['differenceRange']" :step="1" :min="0" | ||
| 1597 | step-strictly /> | ||
| 1598 | <span v-else>{{ scope.row['differenceRange'] }}</span> | ||
| 1599 | </template> | ||
| 1600 | </el-table-column> | ||
| 1601 | <el-table-column label="操作" width="100px" align="left" fixed="right" v-if="!props.readonly"> | ||
| 1602 | |||
| 1603 | <template #default="scope"> | ||
| 1604 | <span class="text_btn" @click="deleteRow(scope)" v-preReClick>删除</span> | ||
| 1605 | </template> | ||
| 1606 | </el-table-column> | ||
| 1607 | </el-table> | ||
| 1608 | <div class="row-add-btn" v-if="!props.readonly"> | ||
| 1609 | <el-button link @click="addRowRules" :disabled="toSubjectTables.length == 1 && tableRowRulesData.length == 1" | ||
| 1610 | :icon="CirclePlus" v-preReClick>添加规则</el-button> | ||
| 1611 | </div> | ||
| 1612 | </div> | ||
| 1613 | |||
| 1614 | <template #footer v-if="!props.readonly"> | ||
| 1615 | <div class="dialog-footer"> | ||
| 1616 | <el-button @click="cancelTableRowDialog" v-preReClick>取消</el-button> | ||
| 1617 | <el-button @click="submitTableRow" type="primary" v-preReClick>确定</el-button> | ||
| 1618 | </div> | ||
| 1619 | </template> | ||
| 1620 | </el-dialog> | ||
| 1621 | </template> | ||
| 1622 | |||
| 1623 | <style lang="scss" scoped> | ||
| 1624 | .filter-dialog-content { | ||
| 1625 | display: flex; | ||
| 1626 | flex-direction: row; | ||
| 1627 | margin: -8px -24px; | ||
| 1628 | |||
| 1629 | .left-title { | ||
| 1630 | height: 32px; | ||
| 1631 | font-size: 14px; | ||
| 1632 | color: #212121; | ||
| 1633 | line-height: 32px; | ||
| 1634 | padding-left: 8px; | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | .el-input { | ||
| 1638 | margin-left: 8px; | ||
| 1639 | width: calc(100% - 16px); | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | .filter-table-list { | ||
| 1643 | width: 200px; | ||
| 1644 | |||
| 1645 | .list_unit { | ||
| 1646 | height: calc(100% - 32px); | ||
| 1647 | overflow-y: auto; | ||
| 1648 | border-top: 1px solid #d9d9d9; | ||
| 1649 | } | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | .table-field { | ||
| 1653 | width: 460px; | ||
| 1654 | |||
| 1655 | .left-title { | ||
| 1656 | border-left: 1px solid #d9d9d9; | ||
| 1657 | border-right: 1px solid #d9d9d9; | ||
| 1658 | } | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | .empty-table-field { | ||
| 1662 | .left-title { | ||
| 1663 | border-left: 1px solid #d9d9d9; | ||
| 1664 | border-right: 1px solid #d9d9d9; | ||
| 1665 | } | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | .filter-edit { | ||
| 1669 | width: calc(100% - 560px); | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | .field-title { | ||
| 1673 | height: 32px; | ||
| 1674 | font-size: 14px; | ||
| 1675 | color: #212121; | ||
| 1676 | line-height: 32px; | ||
| 1677 | padding-left: 8px; | ||
| 1678 | padding-right: 8px; | ||
| 1679 | display: flex; | ||
| 1680 | flex-direction: row; | ||
| 1681 | justify-content: space-between; | ||
| 1682 | align-items: center; | ||
| 1683 | } | ||
| 1684 | |||
| 1685 | :deep(.el-textarea) { | ||
| 1686 | height: calc(100% - 96px); | ||
| 1687 | padding: 0px 8px 8px 8px; | ||
| 1688 | |||
| 1689 | .el-textarea__inner { | ||
| 1690 | min-height: 100% !important; | ||
| 1691 | border-radius: 0px; | ||
| 1692 | } | ||
| 1693 | } | ||
| 1694 | |||
| 1695 | .empty-edit { | ||
| 1696 | flex-grow: 1; | ||
| 1697 | min-width: 0; | ||
| 1698 | |||
| 1699 | .header-title { | ||
| 1700 | padding-right: 12px; | ||
| 1701 | display: flex; | ||
| 1702 | flex-direction: row; | ||
| 1703 | justify-content: space-between; | ||
| 1704 | align-items: center; | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | .tags_list_panel { | ||
| 1708 | border-top: 1px solid #d9d9d9; | ||
| 1709 | height: calc(100% - 32px); | ||
| 1710 | overflow-y: auto; | ||
| 1711 | |||
| 1712 | .table_item { | ||
| 1713 | padding: 8px; | ||
| 1714 | |||
| 1715 | .tag_title { | ||
| 1716 | font-size: 12px; | ||
| 1717 | color: #666666; | ||
| 1718 | line-height: 18px; | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | .tag_item { | ||
| 1722 | padding: 4px 0px; | ||
| 1723 | |||
| 1724 | .tag { | ||
| 1725 | display: inline-flex; | ||
| 1726 | font-size: 12px; | ||
| 1727 | color: var(--el-color-regular); | ||
| 1728 | line-height: 24px; | ||
| 1729 | padding-left: 8px; | ||
| 1730 | border: 1px solid transparent; | ||
| 1731 | cursor: pointer; | ||
| 1732 | align-items: center; | ||
| 1733 | width: 100%; | ||
| 1734 | position: relative; | ||
| 1735 | |||
| 1736 | :deep(.tag-width) { | ||
| 1737 | width: calc(100% - 20px); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | &>span { | ||
| 1741 | max-width: calc(100% - 20px); | ||
| 1742 | overflow: hidden; | ||
| 1743 | text-overflow: ellipsis; | ||
| 1744 | white-space: nowrap; | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | .el-icon { | ||
| 1748 | margin-left: 8px; | ||
| 1749 | margin-right: 8px; | ||
| 1750 | color: var(--el-color-primary); | ||
| 1751 | opacity: 0; | ||
| 1752 | float: right; | ||
| 1753 | right: 0px; | ||
| 1754 | position: absolute; | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | &:hover { | ||
| 1758 | border-color: var(--el-color-primary); | ||
| 1759 | |||
| 1760 | .el-icon { | ||
| 1761 | opacity: 1; | ||
| 1762 | } | ||
| 1763 | } | ||
| 1764 | } | ||
| 1765 | } | ||
| 1766 | } | ||
| 1767 | } | ||
| 1768 | } | ||
| 1769 | |||
| 1770 | } | ||
| 1771 | |||
| 1772 | .row-dialog-content { | ||
| 1773 | height: 300px; | ||
| 1774 | |||
| 1775 | :deep(.el-table) { | ||
| 1776 | & td.el-table__cell { | ||
| 1777 | padding: 2px 0; | ||
| 1778 | height: 36px; | ||
| 1779 | } | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | .row-add-btn { | ||
| 1783 | .el-button--default { | ||
| 1784 | padding: 4px 0px; | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | :deep(.el-icon) { | ||
| 1788 | width: 16px; | ||
| 1789 | height: 16px; | ||
| 1790 | |||
| 1791 | svg { | ||
| 1792 | width: 16px; | ||
| 1793 | height: 16px; | ||
| 1794 | } | ||
| 1795 | } | ||
| 1796 | } | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | .sql-dialog-content { | ||
| 1800 | display: flex; | ||
| 1801 | flex-direction: row; | ||
| 1802 | margin: -8px -24px; | ||
| 1803 | height: 400px; | ||
| 1804 | |||
| 1805 | .left-title { | ||
| 1806 | height: 32px; | ||
| 1807 | font-size: 14px; | ||
| 1808 | color: #212121; | ||
| 1809 | line-height: 32px; | ||
| 1810 | padding-left: 12px; | ||
| 1811 | padding-right: 8px; | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | .sql-table-list { | ||
| 1815 | width: 200px; | ||
| 1816 | border-right: 1px solid #d9d9d9; | ||
| 1817 | |||
| 1818 | .list_unit { | ||
| 1819 | height: calc(100% - 32px); | ||
| 1820 | overflow-y: auto; | ||
| 1821 | border-top: 1px solid #d9d9d9; | ||
| 1822 | } | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | .sql-edit { | ||
| 1826 | width: calc(100% - 360px); | ||
| 1827 | |||
| 1828 | .el-select { | ||
| 1829 | margin-left: 12px; | ||
| 1830 | margin-right: 8px; | ||
| 1831 | width: calc(100% - 20px); | ||
| 1832 | } | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | .field-title { | ||
| 1836 | height: 32px; | ||
| 1837 | font-size: 14px; | ||
| 1838 | color: #212121; | ||
| 1839 | line-height: 32px; | ||
| 1840 | padding-left: 8px; | ||
| 1841 | padding-right: 8px; | ||
| 1842 | display: flex; | ||
| 1843 | flex-direction: row; | ||
| 1844 | justify-content: space-between; | ||
| 1845 | align-items: center; | ||
| 1846 | } | ||
| 1847 | |||
| 1848 | :deep(.el-textarea) { | ||
| 1849 | height: calc(100% - 120px); | ||
| 1850 | padding: 0px 8px 0px 8px; | ||
| 1851 | |||
| 1852 | .el-textarea__inner { | ||
| 1853 | min-height: 100% !important; | ||
| 1854 | border-radius: 0px; | ||
| 1855 | } | ||
| 1856 | } | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | .border-left { | ||
| 1860 | border-left: 1px solid #d9d9d9; | ||
| 1861 | } | ||
| 1862 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/ruleModel.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: ruleModel | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="ruleModel"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { useRouter, useRoute } from "vue-router"; | ||
| 8 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 9 | import StepBar from "@/components/StepBar/index.vue"; | ||
| 10 | import TreeTransfer from "@/components/TreeTransfer/index.vue"; | ||
| 11 | import { | ||
| 12 | getSubjectTableTree, | ||
| 13 | getSubjectTableByDomain, | ||
| 14 | saveQualityTable, | ||
| 15 | getRuleTypeList, | ||
| 16 | getSmallCategoryList, | ||
| 17 | getLargeCategoryList, | ||
| 18 | } from '@/api/modules/dataQuality'; | ||
| 19 | import ruleForm from "../data_quality/ruleForm.vue"; | ||
| 20 | import useUserStore from "@/store/modules/user"; | ||
| 21 | import useDataQualityStore from "@/store/modules/dataQuality"; | ||
| 22 | |||
| 23 | const userStore = useUserStore(); | ||
| 24 | const dataQualityStore = useDataQualityStore(); | ||
| 25 | |||
| 26 | const { proxy } = getCurrentInstance() as any; | ||
| 27 | |||
| 28 | const router = useRouter(); | ||
| 29 | const route = useRoute(); | ||
| 30 | |||
| 31 | const modelGroupGuid: any = ref(route.query.groupGuid); | ||
| 32 | const fullPath = route.fullPath; | ||
| 33 | |||
| 34 | const fullScreenLoading = ref(false); | ||
| 35 | |||
| 36 | const step = ref(0); | ||
| 37 | const stepsInfo = ref({ | ||
| 38 | step: step.value, | ||
| 39 | list: [ | ||
| 40 | { | ||
| 41 | title: '选择表、字段', | ||
| 42 | value: 1, | ||
| 43 | tooltip: { | ||
| 44 | content: '可多选表和字段,批量为表和字段定义规则提示文字提示文字', | ||
| 45 | className: 'step_title_tooltip', | ||
| 46 | effect: 'light', | ||
| 47 | } | ||
| 48 | }, | ||
| 49 | { title: '定义规则', value: 2 } | ||
| 50 | ] | ||
| 51 | }) | ||
| 52 | |||
| 53 | /** 一定要带上pid,否则穿梭在右边后不是树结构。 */ | ||
| 54 | const dsFromTreeDataLoading = ref(false); | ||
| 55 | const dsFromTreeData: any = ref([]) | ||
| 56 | |||
| 57 | const dsToTreeData: any = ref([]); | ||
| 58 | |||
| 59 | const toSubjectTables: any = ref([]); | ||
| 60 | |||
| 61 | const getSubjectTableTreeData = () => { | ||
| 62 | dsFromTreeDataLoading.value = true; | ||
| 63 | getSubjectTableTree({}).then((res: any) => { | ||
| 64 | dsFromTreeDataLoading.value = false; | ||
| 65 | if (res.code == proxy.$passCode) { | ||
| 66 | dsFromTreeData.value = res.data?.map(d => { | ||
| 67 | d.parentGuid = 0; | ||
| 68 | return d; | ||
| 69 | }) || []; | ||
| 70 | } | ||
| 71 | }) | ||
| 72 | } | ||
| 73 | |||
| 74 | const getSubjectTableByDomainData = (guid) => { | ||
| 75 | return getSubjectTableByDomain(guid).then((res: any) => { | ||
| 76 | if (res.code == proxy.$passCode) { | ||
| 77 | return res.data?.map(d => { | ||
| 78 | d.isLeaf = true; | ||
| 79 | d.name = `${d.enName}(${d.chName})`; | ||
| 80 | d.parentGuid = guid; | ||
| 81 | return d; | ||
| 82 | }) || []; | ||
| 83 | } | ||
| 84 | }) | ||
| 85 | } | ||
| 86 | |||
| 87 | const handleSubjectTableLazyFn = (node, resolve) => { | ||
| 88 | if (node.level === 0) { | ||
| 89 | return resolve(dsFromTreeData.value); | ||
| 90 | } | ||
| 91 | if (node.level === 1) { | ||
| 92 | let guid = node.data.guid; | ||
| 93 | resolve(dsFromTreeData.value.find((t: any) => t.guid === guid)?.children || []); | ||
| 94 | } else if (node.level === 2) { | ||
| 95 | getSubjectTableByDomainData(node.data.guid).then((d) => { | ||
| 96 | node.data.children = d; | ||
| 97 | resolve(d); | ||
| 98 | }) | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | const handleSubjectCheckedChange = (nodeObj, treeObj, checkAll, treeNode) => { | ||
| 103 | if (treeObj.checkedKeys?.includes(nodeObj.guid)) { | ||
| 104 | if (nodeObj.parentGuids?.length) { | ||
| 105 | treeNode?.expand(); | ||
| 106 | } else if (nodeObj.parentGuid === 0) { | ||
| 107 | treeNode?.expand(); | ||
| 108 | nextTick(() => { | ||
| 109 | treeNode.childNodes?.forEach(n => n.expand()); | ||
| 110 | }) | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | const ruleTypeList = ref([]); | ||
| 116 | const smallCategoryList = ref([]); | ||
| 117 | const largeCategoryList = ref([]); | ||
| 118 | |||
| 119 | onBeforeMount(() => { | ||
| 120 | getSubjectTableTreeData(); | ||
| 121 | getRuleTypeList().then((res: any) => { | ||
| 122 | if (res.code == proxy.$passCode) { | ||
| 123 | ruleTypeList.value = res.data?.map((d: any) => { | ||
| 124 | d.label = d.ruleName; | ||
| 125 | d.value = d.ruleCode; | ||
| 126 | return d; | ||
| 127 | }) || []; | ||
| 128 | } else { | ||
| 129 | ElMessage.error(res.msg); | ||
| 130 | } | ||
| 131 | }) | ||
| 132 | getSmallCategoryList().then((res: any) => { | ||
| 133 | if (res.code == proxy.$passCode) { | ||
| 134 | smallCategoryList.value = res.data || []; | ||
| 135 | } else { | ||
| 136 | ElMessage.error(res.msg); | ||
| 137 | } | ||
| 138 | }) | ||
| 139 | getLargeCategoryList().then((res: any) => { | ||
| 140 | if (res.code == proxy.$passCode) { | ||
| 141 | largeCategoryList.value = res.data || []; | ||
| 142 | } else { | ||
| 143 | ElMessage.error(res.msg); | ||
| 144 | } | ||
| 145 | }) | ||
| 146 | }) | ||
| 147 | |||
| 148 | const ruleFormRef = ref(); | ||
| 149 | |||
| 150 | const changeStep = (val, skip = false) => { | ||
| 151 | if (val === 2) { | ||
| 152 | if (!dsToTreeData.value.length) { | ||
| 153 | ElMessage.error('已选主题表不能为空'); | ||
| 154 | return; | ||
| 155 | } | ||
| 156 | toSubjectTables.value = []; | ||
| 157 | dsToTreeData.value.forEach(d => { | ||
| 158 | d.children.forEach(c => { | ||
| 159 | c.children.forEach(child => { | ||
| 160 | child.label = `${child.enName}(${child.chName})`; | ||
| 161 | toSubjectTables.value.push(child); | ||
| 162 | }) | ||
| 163 | }) | ||
| 164 | }); | ||
| 165 | step.value = val - 1; | ||
| 166 | stepsInfo.value.step = val - 1 | ||
| 167 | } else { | ||
| 168 | step.value = val - 1; | ||
| 169 | stepsInfo.value.step = val - 1 | ||
| 170 | } | ||
| 171 | }; | ||
| 172 | |||
| 173 | const cancel = () => { | ||
| 174 | ElMessageBox.confirm( | ||
| 175 | "当前页面尚未保存,确定放弃修改吗?", | ||
| 176 | "提示", | ||
| 177 | { | ||
| 178 | confirmButtonText: "确定", | ||
| 179 | cancelButtonText: "取消", | ||
| 180 | type: "warning", | ||
| 181 | } | ||
| 182 | ) | ||
| 183 | .then(() => { | ||
| 184 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 185 | router.push({ | ||
| 186 | name: 'qualityRules', | ||
| 187 | }); | ||
| 188 | }) | ||
| 189 | .catch(() => { | ||
| 190 | ElMessage({ | ||
| 191 | type: "info", | ||
| 192 | message: "已取消", | ||
| 193 | }); | ||
| 194 | }); | ||
| 195 | } | ||
| 196 | |||
| 197 | /** 将新建规则之后转化为对应质检表的规则。 */ | ||
| 198 | const transformRulesInfo = (info: any) => { | ||
| 199 | let modelRules: any = []; //key为质检表,值为信息。 | ||
| 200 | if (info.ruleCode == "volatility_check") {//表行数波动率。 | ||
| 201 | let subjectTables = toSubjectTables.value; | ||
| 202 | info.qualityModelGuids?.forEach((modelGuid: string) => { | ||
| 203 | let tableInfo = subjectTables.find(t => t.guid === modelGuid); | ||
| 204 | modelRules.push(Object.assign({}, { | ||
| 205 | modelGroupGuid: modelGroupGuid.value, | ||
| 206 | name: tableInfo.chName, | ||
| 207 | subjectName: tableInfo.enName, | ||
| 208 | subjectGuid: tableInfo.guid, | ||
| 209 | dataSourceGuid: tableInfo.dataSourceGuid, | ||
| 210 | databaseName: tableInfo.dataServerName, | ||
| 211 | modelRuleConfList: [Object.assign({}, info, { | ||
| 212 | qualityModelGuid: modelGuid | ||
| 213 | })] | ||
| 214 | })); | ||
| 215 | }) | ||
| 216 | } | ||
| 217 | else if (info.ruleCode == 'null_value_check' || info.ruleCode == 'repeate_data_check') { | ||
| 218 | let subjectTables = toSubjectTables.value; | ||
| 219 | for (const ds in info.modelFields) { | ||
| 220 | let fields = info.modelFields[ds]; | ||
| 221 | let tableInfo = subjectTables.find(t => t.chName === ds); | ||
| 222 | modelRules.push(Object.assign({}, { | ||
| 223 | modelGroupGuid: modelGroupGuid.value, | ||
| 224 | name: tableInfo.chName, | ||
| 225 | subjectName: tableInfo.enName, | ||
| 226 | subjectGuid: tableInfo.guid, | ||
| 227 | dataSourceGuid: tableInfo.dataSourceGuid, | ||
| 228 | databaseName: tableInfo.dataServerName, | ||
| 229 | modelRuleConfList: [Object.assign({}, info, { | ||
| 230 | ruleField: fields?.map(f => { | ||
| 231 | return { | ||
| 232 | guid: f.guid, | ||
| 233 | enName: f.enName, | ||
| 234 | chName: f.chName | ||
| 235 | } | ||
| 236 | }) || [], | ||
| 237 | })] | ||
| 238 | })); | ||
| 239 | } | ||
| 240 | } else if (info.ruleCode == 'logic_check') { | ||
| 241 | let subjectTables = toSubjectTables.value; | ||
| 242 | for (const ds in info.ruleFields) { | ||
| 243 | let fields = info.ruleFields[ds]; | ||
| 244 | let tableInfo = subjectTables.find(t => t.enName === ds); | ||
| 245 | modelRules.push(Object.assign({}, { | ||
| 246 | modelGroupGuid: modelGroupGuid.value, | ||
| 247 | name: tableInfo.chName, | ||
| 248 | subjectName: tableInfo.enName, | ||
| 249 | subjectGuid: tableInfo.guid, | ||
| 250 | dataSourceGuid: tableInfo.dataSourceGuid, | ||
| 251 | databaseName: tableInfo.dataServerName, | ||
| 252 | modelRuleConfList: [Object.assign({}, info, { | ||
| 253 | ruleField: [{ | ||
| 254 | guid: fields.guid, | ||
| 255 | enName: fields.enName, | ||
| 256 | chName: fields.chName | ||
| 257 | }], | ||
| 258 | conditionSql: info.conditionSqls?.[ds], | ||
| 259 | conditionSqls: '', | ||
| 260 | ruleFields: '' | ||
| 261 | })] | ||
| 262 | })); | ||
| 263 | } | ||
| 264 | } else if (info.ruleCode == 'custom_sql') { | ||
| 265 | let subjectTables = toSubjectTables.value; | ||
| 266 | for (const ds in info.ruleFields) { | ||
| 267 | let fields = info.ruleFields[ds]; | ||
| 268 | let tableInfo = subjectTables.find(t => t.enName === ds); | ||
| 269 | modelRules.push(Object.assign({}, { | ||
| 270 | modelGroupGuid: modelGroupGuid.value, | ||
| 271 | name: tableInfo.chName, | ||
| 272 | subjectName: tableInfo.enName, | ||
| 273 | subjectGuid: tableInfo.guid, | ||
| 274 | dataSourceGuid: tableInfo.dataSourceGuid, | ||
| 275 | databaseName: tableInfo.dataServerName, | ||
| 276 | modelRuleConfList: [Object.assign({}, info, { | ||
| 277 | ruleField: fields?.map(f => { | ||
| 278 | return { | ||
| 279 | enName: f | ||
| 280 | } | ||
| 281 | }) || [], | ||
| 282 | customSql: info.customSqls[ds], | ||
| 283 | fieldSelects: info.sqlFieldsList?.[ds] || [], | ||
| 284 | conditionSqls: '', | ||
| 285 | ruleFields: '' | ||
| 286 | })] | ||
| 287 | })); | ||
| 288 | } | ||
| 289 | } else if (info.ruleCode == 'rows_check') { | ||
| 290 | let subjectTables = toSubjectTables.value; | ||
| 291 | info.rows.forEach(row => { | ||
| 292 | let tableInfo = subjectTables.find(t => t.guid === row.mainTable); | ||
| 293 | modelRules.push(Object.assign({}, { | ||
| 294 | modelGroupGuid: modelGroupGuid.value, | ||
| 295 | name: tableInfo.chName, | ||
| 296 | subjectName: tableInfo.enName, | ||
| 297 | subjectGuid: tableInfo.guid, | ||
| 298 | dataSourceGuid: tableInfo.dataSourceGuid, | ||
| 299 | databaseName: tableInfo.dataServerName, | ||
| 300 | modelRuleConfList: [Object.assign({}, info, { | ||
| 301 | differenceRange: row.differenceRange, | ||
| 302 | rows: [], | ||
| 303 | contrastSubjectGuid: row.contrastSubjectGuid | ||
| 304 | })] | ||
| 305 | })); | ||
| 306 | }) | ||
| 307 | } | ||
| 308 | return modelRules; | ||
| 309 | } | ||
| 310 | |||
| 311 | const save = () => { | ||
| 312 | ruleFormRef.value?.ruleFormRef?.ruleFormRef?.validate((valid) => { | ||
| 313 | if (valid) { | ||
| 314 | let v = ruleFormRef.value?.getFormInfo(); | ||
| 315 | let params = transformRulesInfo(v); | ||
| 316 | fullScreenLoading.value = true; | ||
| 317 | saveQualityTable(params).then((res: any) => { | ||
| 318 | fullScreenLoading.value = false; | ||
| 319 | if (res.code == proxy.$passCode) { | ||
| 320 | ElMessage.success('新建质检表保存成功'); | ||
| 321 | //跳到对应的分组下 | ||
| 322 | router.push({ | ||
| 323 | name: 'qualityRules' | ||
| 324 | }); | ||
| 325 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 326 | dataQualityStore.set(modelGroupGuid.value); | ||
| 327 | } else { | ||
| 328 | ElMessage.error(res.msg); | ||
| 329 | } | ||
| 330 | }) | ||
| 331 | } | ||
| 332 | }); | ||
| 333 | } | ||
| 334 | |||
| 335 | </script> | ||
| 336 | |||
| 337 | <template> | ||
| 338 | <div class="container_wrap full" v-loading="fullScreenLoading"> | ||
| 339 | <div class="content_main"> | ||
| 340 | <div class="top_tool_wrap"> | ||
| 341 | <StepBar :steps-info="stepsInfo" /> | ||
| 342 | </div> | ||
| 343 | <div class="operator_panel_wrap first-step-content" v-show="step == 0"> | ||
| 344 | <div class="operator_panel is-block"> | ||
| 345 | <div class="panel_title"> | ||
| 346 | <div class="title_text"> | ||
| 347 | <span>选择主题表</span> | ||
| 348 | <span class="tips_text">选择需要添加质检规则的主题表</span> | ||
| 349 | </div> | ||
| 350 | </div> | ||
| 351 | <TreeTransfer mode="transfer" :title="['主题表', '已选表']" pid="parentGuid" | ||
| 352 | :from-tree-data-loading="dsFromTreeDataLoading" :lazy="true" :checkOnClickNode="true" | ||
| 353 | :from_checked_all="false" :from_data="dsFromTreeData" :to_data="dsToTreeData" node_key="guid" | ||
| 354 | :transferOpenNode="true" width="70%" :defaultProps="{ | ||
| 355 | label: 'name', | ||
| 356 | value: 'guid' | ||
| 357 | }" :lazyFn="handleSubjectTableLazyFn" @left-check-change="handleSubjectCheckedChange" height="calc(100% - 64px)"> | ||
| 358 | </TreeTransfer> | ||
| 359 | </div> | ||
| 360 | </div> | ||
| 361 | <div class="operator_panel_wrap" v-show="step == 1"> | ||
| 362 | <div class="operator_panel is-block"> | ||
| 363 | <div class="panel_title"> | ||
| 364 | <div class="title_text"> | ||
| 365 | <span>定义规则</span> | ||
| 366 | <span class="tips_text">可为多表配置表级规则、字段级规则</span> | ||
| 367 | </div> | ||
| 368 | </div> | ||
| 369 | <div class="panel_content"> | ||
| 370 | <div class="form_panel"> | ||
| 371 | <ruleForm ref="ruleFormRef" :toSubjectTables="toSubjectTables" :ruleTypeList="ruleTypeList" | ||
| 372 | :largeCategoryList="largeCategoryList" :smallCategoryList="smallCategoryList"></ruleForm> | ||
| 373 | </div> | ||
| 374 | </div> | ||
| 375 | </div> | ||
| 376 | </div> | ||
| 377 | </div> | ||
| 378 | <div class="bottom_tool_wrap"> | ||
| 379 | <template v-if="step == 0"> | ||
| 380 | <el-button @click="cancel">取消</el-button> | ||
| 381 | <el-button type="primary" @click="changeStep(2)">下一步</el-button> | ||
| 382 | </template> | ||
| 383 | |||
| 384 | <template v-else> | ||
| 385 | <el-button @click="cancel">取消</el-button> | ||
| 386 | <el-button @click="changeStep(1)">上一步</el-button> | ||
| 387 | <el-button type="primary" @click="save" v-preReClick>保存</el-button> | ||
| 388 | </template> | ||
| 389 | </div> | ||
| 390 | </div> | ||
| 391 | </template> | ||
| 392 | |||
| 393 | <style lang="scss" scoped> | ||
| 394 | .top_tool_wrap { | ||
| 395 | width: 100%; | ||
| 396 | height: 72px; | ||
| 397 | margin: 8px 0 0px; | ||
| 398 | display: flex; | ||
| 399 | justify-content: center; | ||
| 400 | align-items: center; | ||
| 401 | |||
| 402 | :deep(.el-steps) { | ||
| 403 | width: 30%; | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | .content_main { | ||
| 408 | height: calc(100% - 40px); | ||
| 409 | padding: 0 16px 16px; | ||
| 410 | overflow: hidden auto; | ||
| 411 | |||
| 412 | :deep(.transfer_panel_wrap) { | ||
| 413 | .transfer_panel { | ||
| 414 | box-shadow: 0 0 0 1px var(--el-border-color-regular); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | .first-step-content { | ||
| 419 | height: calc(100% - 80px); | ||
| 420 | |||
| 421 | .wl-transfer { | ||
| 422 | margin: 8px 16px; | ||
| 423 | height: calc(100% - 64px); | ||
| 424 | width: 70%; | ||
| 425 | min-width: 600px; | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | .operator_panel_wrap { | ||
| 430 | height: calc(100% - 80px); | ||
| 431 | min-height: 400px; | ||
| 432 | display: flex; | ||
| 433 | justify-content: space-between; | ||
| 434 | |||
| 435 | :deep(.el-button) { | ||
| 436 | &.is-text { | ||
| 437 | height: auto; | ||
| 438 | padding: 0; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | .operator_panel { | ||
| 443 | width: calc(50% - 5px); | ||
| 444 | height: 100%; | ||
| 445 | border: 1px solid #d9d9d9; | ||
| 446 | overflow: hidden; | ||
| 447 | |||
| 448 | &.is-block { | ||
| 449 | width: 100%; | ||
| 450 | } | ||
| 451 | |||
| 452 | .panel_title { | ||
| 453 | height: 48px; | ||
| 454 | padding: 0 15px; | ||
| 455 | display: flex; | ||
| 456 | justify-content: space-between; | ||
| 457 | align-items: center; | ||
| 458 | color: var(--el-color-regular); | ||
| 459 | font-weight: 600; | ||
| 460 | border-bottom: 1px solid #d9d9d9; | ||
| 461 | background-color: #f5f5f5; | ||
| 462 | |||
| 463 | .tips_text { | ||
| 464 | font-size: 14px; | ||
| 465 | color: #999; | ||
| 466 | font-weight: normal; | ||
| 467 | margin-left: 8px; | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | .panel_content { | ||
| 472 | height: calc(100% - 46px); | ||
| 473 | |||
| 474 | >div { | ||
| 475 | width: 100%; | ||
| 476 | height: 100%; | ||
| 477 | overflow: hidden; | ||
| 478 | } | ||
| 479 | |||
| 480 | .form_panel { | ||
| 481 | padding: 8px 16px 0; | ||
| 482 | overflow: hidden auto; | ||
| 483 | } | ||
| 484 | |||
| 485 | .tree_search_input { | ||
| 486 | margin-bottom: 10px; | ||
| 487 | } | ||
| 488 | |||
| 489 | .list_panel { | ||
| 490 | padding: 10px 0; | ||
| 491 | overflow: hidden auto; | ||
| 492 | |||
| 493 | .list_item { | ||
| 494 | height: 32px; | ||
| 495 | padding: 0 10px; | ||
| 496 | display: flex; | ||
| 497 | justify-content: space-between; | ||
| 498 | align-items: center; | ||
| 499 | |||
| 500 | &:hover { | ||
| 501 | color: var(--g-sub-sidebar-menu-active-color); | ||
| 502 | background-color: var(--g-sub-sidebar-menu-active-bg); | ||
| 503 | } | ||
| 504 | } | ||
| 505 | } | ||
| 506 | } | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | .transfer_btns { | ||
| 511 | display: flex; | ||
| 512 | flex-direction: column; | ||
| 513 | justify-content: center; | ||
| 514 | align-items: center; | ||
| 515 | |||
| 516 | .el-button { | ||
| 517 | margin: 0 8px; | ||
| 518 | |||
| 519 | &+.el-button { | ||
| 520 | margin-top: 8px; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | } | ||
| 524 | } | ||
| 525 | |||
| 526 | .bottom_tool_wrap { | ||
| 527 | height: 40px; | ||
| 528 | padding: 0 16px; | ||
| 529 | border-top: 1px solid #d9d9d9; | ||
| 530 | display: flex; | ||
| 531 | justify-content: flex-end; | ||
| 532 | align-items: center; | ||
| 533 | } | ||
| 534 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/ruleModelEdit.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: ruleModelEdit | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="ruleModelEdit"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import ruleForm from "../data_quality/ruleForm.vue"; | ||
| 8 | import { useRouter, useRoute } from "vue-router"; | ||
| 9 | import { | ||
| 10 | getRuleConfDetail, | ||
| 11 | getRuleTypeList, | ||
| 12 | getSmallCategoryList, | ||
| 13 | getLargeCategoryList, | ||
| 14 | updateModelRule | ||
| 15 | } from '@/api/modules/dataQuality'; | ||
| 16 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 17 | import useUserStore from "@/store/modules/user"; | ||
| 18 | import useDataQualityStore from "@/store/modules/dataQuality"; | ||
| 19 | |||
| 20 | const userStore = useUserStore(); | ||
| 21 | const dataQualityStore = useDataQualityStore(); | ||
| 22 | |||
| 23 | const { proxy } = getCurrentInstance() as any; | ||
| 24 | |||
| 25 | const router = useRouter(); | ||
| 26 | const route = useRoute(); | ||
| 27 | const ruleGuid = route.query.guid; | ||
| 28 | const fullPath = route.fullPath; | ||
| 29 | |||
| 30 | const fullScreenLoading = ref(false); | ||
| 31 | |||
| 32 | const detailLoading = ref(false); | ||
| 33 | |||
| 34 | const detailInfo: any = ref({}); | ||
| 35 | |||
| 36 | const toSubjectTables: any = ref([]); | ||
| 37 | |||
| 38 | const ruleType = ref(''); | ||
| 39 | |||
| 40 | const ruleFormRef = ref(); | ||
| 41 | |||
| 42 | const cancel = () => { | ||
| 43 | ElMessageBox.confirm( | ||
| 44 | "当前页面尚未保存,确定放弃修改吗?", | ||
| 45 | "提示", | ||
| 46 | { | ||
| 47 | confirmButtonText: "确定", | ||
| 48 | cancelButtonText: "取消", | ||
| 49 | type: "warning", | ||
| 50 | } | ||
| 51 | ) | ||
| 52 | .then(() => { | ||
| 53 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 54 | router.push({ | ||
| 55 | name: 'qualityRules', | ||
| 56 | }); | ||
| 57 | }) | ||
| 58 | .catch(() => { | ||
| 59 | ElMessage({ | ||
| 60 | type: "info", | ||
| 61 | message: "已取消", | ||
| 62 | }); | ||
| 63 | }); | ||
| 64 | } | ||
| 65 | |||
| 66 | /** 将新建规则之后转化为对应质检表的规则。 */ | ||
| 67 | const transformRulesInfo = (info: any) => { | ||
| 68 | if (info.ruleCode == "volatility_check") {//表行数波动率。 | ||
| 69 | return Object.assign({}, info, { | ||
| 70 | guid: ruleGuid, | ||
| 71 | qualityModelGuid: detailInfo.value.qualityModelGuid, | ||
| 72 | ruleCode: detailInfo.value.ruleCode, | ||
| 73 | }); | ||
| 74 | } | ||
| 75 | else if (info.ruleCode == 'null_value_check' || info.ruleCode == 'repeate_data_check') { | ||
| 76 | return Object.assign({}, info, { | ||
| 77 | guid: ruleGuid, | ||
| 78 | qualityModelGuid: detailInfo.value.qualityModelGuid, | ||
| 79 | ruleCode: detailInfo.value.ruleCode, | ||
| 80 | ruleField: info.modelFields[detailInfo.value.subjectZhName] || [] | ||
| 81 | }); | ||
| 82 | } else if (info.ruleCode === 'logic_check') { | ||
| 83 | let subjectName = detailInfo.value.subjectName; | ||
| 84 | let fields = info.ruleFields[subjectName]; | ||
| 85 | return Object.assign({}, info, { | ||
| 86 | guid: ruleGuid, | ||
| 87 | qualityModelGuid: detailInfo.value.qualityModelGuid, | ||
| 88 | ruleCode: detailInfo.value.ruleCode, | ||
| 89 | ruleField: [{ | ||
| 90 | guid: fields.guid, | ||
| 91 | enName: fields.enName, | ||
| 92 | chName: fields.chName | ||
| 93 | }], | ||
| 94 | conditionSql: info.conditionSqls?.[subjectName], | ||
| 95 | conditionSqls: '', | ||
| 96 | ruleFields: '' | ||
| 97 | }); | ||
| 98 | } else if (info.ruleCode === 'custom_sql') { | ||
| 99 | let subjectGuid = detailInfo.value.subjectGuid; | ||
| 100 | return Object.assign({}, info, { | ||
| 101 | guid: ruleGuid, | ||
| 102 | qualityModelGuid: detailInfo.value.qualityModelGuid, | ||
| 103 | ruleCode: detailInfo.value.ruleCode, | ||
| 104 | customSql: info.customSqls?.[detailInfo.value.subjectName], | ||
| 105 | ruleField: info.ruleFields?.[detailInfo.value.subjectName]?.map(f => { | ||
| 106 | return { | ||
| 107 | enName: f | ||
| 108 | } | ||
| 109 | }) || [], | ||
| 110 | fieldSelects: info.sqlFieldsList?.[detailInfo.value.subjectName] || [], | ||
| 111 | customSqls: '', | ||
| 112 | ruleFields: '' | ||
| 113 | }); | ||
| 114 | } else if (info.ruleCode == 'rows_check') { | ||
| 115 | return Object.assign({}, info, { | ||
| 116 | guid: ruleGuid, | ||
| 117 | qualityModelGuid: detailInfo.value.qualityModelGuid, | ||
| 118 | ruleCode: detailInfo.value.ruleCode, | ||
| 119 | contrastSubjectGuid: info.rows[0].contrastSubjectGuid, | ||
| 120 | differenceRange: info.rows[0].differenceRange, | ||
| 121 | rows: '' | ||
| 122 | }); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | const save = () => { | ||
| 127 | ruleFormRef.value?.ruleFormRef?.ruleFormRef?.validate((valid) => { | ||
| 128 | if (valid) { | ||
| 129 | let v = ruleFormRef.value?.getFormInfo(); | ||
| 130 | let params = transformRulesInfo(v); | ||
| 131 | fullScreenLoading.value = true; | ||
| 132 | updateModelRule(params).then((res: any) => { | ||
| 133 | fullScreenLoading.value = false; | ||
| 134 | if (res.code == proxy.$passCode) { | ||
| 135 | ElMessage.success(`【${params.ruleConfName}】` + '质量规则编辑成功'); | ||
| 136 | //跳到对应的分组下 | ||
| 137 | router.push({ | ||
| 138 | name: 'qualityRules' | ||
| 139 | }); | ||
| 140 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 141 | dataQualityStore.setModelGuid(params.qualityModelGuid); | ||
| 142 | } else { | ||
| 143 | ElMessage.error(res.msg); | ||
| 144 | } | ||
| 145 | }) | ||
| 146 | } | ||
| 147 | }); | ||
| 148 | } | ||
| 149 | |||
| 150 | const getRuleDetailInfo = () => { | ||
| 151 | detailLoading.value = true; | ||
| 152 | getRuleConfDetail(ruleGuid).then((res: any) => { | ||
| 153 | detailLoading.value = false; | ||
| 154 | if (res.code == proxy.$passCode) { | ||
| 155 | let data = res.data || {}; | ||
| 156 | detailInfo.value = data; | ||
| 157 | ruleType.value = detailInfo.value.ruleCode; | ||
| 158 | toSubjectTables.value = [{ | ||
| 159 | guid: detailInfo.value.subjectGuid, //编辑的时候显示的是主题表 | ||
| 160 | enName: detailInfo.value.subjectName, | ||
| 161 | chName: detailInfo.value.subjectZhName, | ||
| 162 | label: `${detailInfo.value.subjectName}(${detailInfo.value.subjectZhName})` | ||
| 163 | }] | ||
| 164 | detailInfo.value.qualityModelGuids = [detailInfo.value.subjectGuid]; | ||
| 165 | if (fullPath === route.fullPath) { | ||
| 166 | document.title = `编辑-${data.ruleConfName}(${data.subjectZhName})`; | ||
| 167 | } | ||
| 168 | let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === fullPath); | ||
| 169 | if (tab) { | ||
| 170 | tab.meta.title = `编辑-${data.ruleConfName}(${data.subjectZhName})`; | ||
| 171 | } | ||
| 172 | } else { | ||
| 173 | ElMessage.error(res.msg); | ||
| 174 | } | ||
| 175 | }) | ||
| 176 | } | ||
| 177 | |||
| 178 | const ruleTypeList = ref([]); | ||
| 179 | const smallCategoryList = ref([]); | ||
| 180 | const largeCategoryList = ref([]); | ||
| 181 | |||
| 182 | onBeforeMount(() => { | ||
| 183 | getRuleDetailInfo(); | ||
| 184 | getRuleTypeList().then((res: any) => { | ||
| 185 | if (res.code == proxy.$passCode) { | ||
| 186 | ruleTypeList.value = res.data?.map((d: any) => { | ||
| 187 | d.label = d.ruleName; | ||
| 188 | d.value = d.ruleCode; | ||
| 189 | return d; | ||
| 190 | }) || []; | ||
| 191 | } else { | ||
| 192 | ElMessage.error(res.msg); | ||
| 193 | } | ||
| 194 | }) | ||
| 195 | getSmallCategoryList().then((res: any) => { | ||
| 196 | if (res.code == proxy.$passCode) { | ||
| 197 | smallCategoryList.value = res.data || []; | ||
| 198 | } else { | ||
| 199 | ElMessage.error(res.msg); | ||
| 200 | } | ||
| 201 | }) | ||
| 202 | getLargeCategoryList().then((res: any) => { | ||
| 203 | if (res.code == proxy.$passCode) { | ||
| 204 | largeCategoryList.value = res.data || []; | ||
| 205 | } else { | ||
| 206 | ElMessage.error(res.msg); | ||
| 207 | } | ||
| 208 | }) | ||
| 209 | }) | ||
| 210 | |||
| 211 | </script> | ||
| 212 | |||
| 213 | <template> | ||
| 214 | <div class="content_main" v-loading="fullScreenLoading"> | ||
| 215 | <div class="operator_panel_wrap"> | ||
| 216 | <div class="operator_panel is-block" v-loading="detailLoading"> | ||
| 217 | <div class="panel_title"> | ||
| 218 | <div class="title_text"> | ||
| 219 | <span>规则</span> | ||
| 220 | </div> | ||
| 221 | </div> | ||
| 222 | <div class="panel_content"> | ||
| 223 | <div class="form_panel"> | ||
| 224 | <ruleForm ref="ruleFormRef" :toSubjectTables="toSubjectTables" :ruleTypeValue="ruleType" :value="detailInfo" | ||
| 225 | :ruleTypeList="ruleTypeList" :largeCategoryList="largeCategoryList" | ||
| 226 | :smallCategoryList="smallCategoryList"></ruleForm> | ||
| 227 | </div> | ||
| 228 | </div> | ||
| 229 | </div> | ||
| 230 | </div> | ||
| 231 | <div class="bottom_tool_wrap"> | ||
| 232 | <el-button @click="cancel">取消</el-button> | ||
| 233 | <el-button type="primary" @click="save">保存</el-button> | ||
| 234 | </div> | ||
| 235 | </div> | ||
| 236 | </template> | ||
| 237 | |||
| 238 | <style lang="scss" scoped> | ||
| 239 | .content_main { | ||
| 240 | display: flex; | ||
| 241 | flex-direction: column; | ||
| 242 | height: 100%; | ||
| 243 | } | ||
| 244 | |||
| 245 | .operator_panel_wrap { | ||
| 246 | height: auto; | ||
| 247 | min-height: 200px; | ||
| 248 | display: flex; | ||
| 249 | justify-content: space-between; | ||
| 250 | margin: 16px; | ||
| 251 | max-height: calc(100% - 72px); | ||
| 252 | |||
| 253 | :deep(.el-button) { | ||
| 254 | &.is-text { | ||
| 255 | height: auto; | ||
| 256 | padding: 0; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | .operator_panel { | ||
| 261 | width: calc(50% - 5px); | ||
| 262 | height: 100%; | ||
| 263 | border: 1px solid #d9d9d9; | ||
| 264 | overflow: hidden; | ||
| 265 | |||
| 266 | &.is-block { | ||
| 267 | width: 100%; | ||
| 268 | } | ||
| 269 | |||
| 270 | .panel_title { | ||
| 271 | height: 48px; | ||
| 272 | padding: 0 15px; | ||
| 273 | display: flex; | ||
| 274 | justify-content: space-between; | ||
| 275 | align-items: center; | ||
| 276 | color: var(--el-color-regular); | ||
| 277 | font-weight: 600; | ||
| 278 | border-bottom: 1px solid #d9d9d9; | ||
| 279 | |||
| 280 | .tips_text { | ||
| 281 | font-size: 14px; | ||
| 282 | color: #999; | ||
| 283 | font-weight: normal; | ||
| 284 | margin-left: 8px; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | .panel_content { | ||
| 289 | height: calc(100% - 46px); | ||
| 290 | |||
| 291 | >div { | ||
| 292 | width: 100%; | ||
| 293 | height: 100%; | ||
| 294 | overflow: hidden; | ||
| 295 | } | ||
| 296 | |||
| 297 | .form_panel { | ||
| 298 | padding: 8px 16px 0; | ||
| 299 | overflow: hidden auto; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | .bottom_tool_wrap { | ||
| 306 | height: 40px; | ||
| 307 | padding: 0 16px; | ||
| 308 | border-top: 1px solid #d9d9d9; | ||
| 309 | display: flex; | ||
| 310 | justify-content: flex-end; | ||
| 311 | align-items: center; | ||
| 312 | } | ||
| 313 | </style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/views/data_quality/ruleTemplate.vue
0 → 100644
| 1 | <route lang="yaml"> | ||
| 2 | name: ruleTemplate | ||
| 3 | </route> | ||
| 4 | |||
| 5 | <script lang="ts" setup name="ruleTemplate"> | ||
| 6 | import { ref } from "vue"; | ||
| 7 | import { useRouter, useRoute } from "vue-router"; | ||
| 8 | import { ElMessage, ElMessageBox } from "element-plus"; | ||
| 9 | import { CaretRight, CaretBottom, Delete, Plus } from '@element-plus/icons-vue' | ||
| 10 | import ruleForm from "../data_quality/ruleForm.vue"; | ||
| 11 | import { | ||
| 12 | getRuleTypeList, | ||
| 13 | getSmallCategoryList, | ||
| 14 | getLargeCategoryList, | ||
| 15 | getModelDetail, | ||
| 16 | saveQualityTable | ||
| 17 | } from '@/api/modules/dataQuality'; | ||
| 18 | import useUserStore from "@/store/modules/user"; | ||
| 19 | import useDataQualityStore from "@/store/modules/dataQuality"; | ||
| 20 | |||
| 21 | const userStore = useUserStore(); | ||
| 22 | const dataQualityStore = useDataQualityStore(); | ||
| 23 | |||
| 24 | const router = useRouter(); | ||
| 25 | const route = useRoute(); | ||
| 26 | |||
| 27 | const { proxy } = getCurrentInstance() as any; | ||
| 28 | |||
| 29 | const modelGuid = route.query.modelGuid; | ||
| 30 | const fullPath = route.fullPath; | ||
| 31 | |||
| 32 | const modelDetailInfo: any = ref({}); | ||
| 33 | |||
| 34 | const toSubjectTables: any = ref([]); | ||
| 35 | |||
| 36 | const ruleList: any = ref([ | ||
| 37 | { | ||
| 38 | open: true | ||
| 39 | } | ||
| 40 | ]) | ||
| 41 | |||
| 42 | const addRule = () => { | ||
| 43 | ruleList.value.push({ | ||
| 44 | open: true | ||
| 45 | }); | ||
| 46 | }; | ||
| 47 | |||
| 48 | const deleteRule = (item, index) => { | ||
| 49 | ElMessageBox.confirm(`确定删除【规则${index + 1}】吗?`, "提示", { | ||
| 50 | confirmButtonText: "确定", | ||
| 51 | cancelButtonText: "取消", | ||
| 52 | type: 'warning', | ||
| 53 | }).then(() => { | ||
| 54 | ruleList.value.splice(index, 1); | ||
| 55 | }); | ||
| 56 | } | ||
| 57 | |||
| 58 | const toRouterPath = (url) => { | ||
| 59 | router.push({ | ||
| 60 | path: url, | ||
| 61 | }); | ||
| 62 | }; | ||
| 63 | |||
| 64 | const fullScreenLoading = ref(false); | ||
| 65 | |||
| 66 | const ruleFormRef = ref(); | ||
| 67 | |||
| 68 | const transformRulesInfo = (info) => { | ||
| 69 | if (info.ruleCode == "volatility_check") {//表行数波动率。 | ||
| 70 | return Object.assign({}, info, { | ||
| 71 | qualityModelGuid: modelGuid, | ||
| 72 | ruleCode: info.ruleCode, | ||
| 73 | }); | ||
| 74 | } | ||
| 75 | else if (info.ruleCode == 'null_value_check' || info.ruleCode == 'repeate_data_check') { | ||
| 76 | return Object.assign({}, info, { | ||
| 77 | qualityModelGuid: modelGuid, | ||
| 78 | ruleCode: info.ruleCode, | ||
| 79 | ruleField: info.modelFields[modelDetailInfo.value.name] || [] | ||
| 80 | }); | ||
| 81 | } else if (info.ruleCode === 'logic_check') { | ||
| 82 | let subjectName = modelDetailInfo.value.subjectName; | ||
| 83 | let fields = info.ruleFields[subjectName]; | ||
| 84 | return Object.assign({}, info, { | ||
| 85 | qualityModelGuid: modelGuid, | ||
| 86 | ruleCode: info.ruleCode, | ||
| 87 | ruleField: [{ | ||
| 88 | guid: fields.guid, | ||
| 89 | enName: fields.enName, | ||
| 90 | chName: fields.chName | ||
| 91 | }], | ||
| 92 | conditionSql: info.conditionSqls?.[subjectName], | ||
| 93 | conditionSqls: '', | ||
| 94 | ruleFields: '' | ||
| 95 | }); | ||
| 96 | } else if (info.ruleCode === 'custom_sql') { | ||
| 97 | let subjectGuid = modelDetailInfo.value.subjectGuid; | ||
| 98 | return Object.assign({}, info, { | ||
| 99 | qualityModelGuid: modelGuid, | ||
| 100 | ruleCode: info.ruleCode, | ||
| 101 | customSql: info.customSqls?.[modelDetailInfo.value.subjectName], | ||
| 102 | ruleField: info.ruleFields?.[modelDetailInfo.value.subjectName]?.map(f => { | ||
| 103 | return { | ||
| 104 | enName: f | ||
| 105 | } | ||
| 106 | }) || [], | ||
| 107 | fieldSelects: info.sqlFieldsList?.[modelDetailInfo.value.subjectName] || [], | ||
| 108 | customSqls: '', | ||
| 109 | ruleFields: '' | ||
| 110 | }); | ||
| 111 | } else if (info.ruleCode == 'rows_check') { | ||
| 112 | return Object.assign({}, info, { | ||
| 113 | qualityModelGuid: modelGuid, | ||
| 114 | ruleCode: info.ruleCode, | ||
| 115 | contrastSubjectGuid: info.rows[0].contrastSubjectGuid, | ||
| 116 | differenceRange: info.rows[0].differenceRange, | ||
| 117 | rows: '' | ||
| 118 | }); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | const save = () => { | ||
| 123 | if (!ruleList.value?.length) { | ||
| 124 | ElMessage.error('新增规则不能为空'); | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | let psList: Promise<void>[] = []; | ||
| 128 | ruleFormRef.value.forEach((form, index) => { | ||
| 129 | psList.push(new Promise((resolve, reject) => { | ||
| 130 | form.ruleFormRef.ruleFormRef.validate(valid => { | ||
| 131 | if (valid) { | ||
| 132 | resolve(); | ||
| 133 | } else { | ||
| 134 | ruleList.value[index].open = true; | ||
| 135 | reject(); | ||
| 136 | } | ||
| 137 | }); | ||
| 138 | })); | ||
| 139 | }) | ||
| 140 | Promise.all(psList).then((res: any) => { | ||
| 141 | let submitInfos: any = []; | ||
| 142 | ruleFormRef.value.forEach(form => { | ||
| 143 | let info = form.getFormInfo(); | ||
| 144 | let params = transformRulesInfo(info); | ||
| 145 | submitInfos.push(params); | ||
| 146 | }); | ||
| 147 | fullScreenLoading.value = true; | ||
| 148 | saveQualityTable([{ | ||
| 149 | modelGroupGuid: modelDetailInfo.value.modelGroupGuid, | ||
| 150 | name: modelDetailInfo.value.name, | ||
| 151 | subjectGuid: modelDetailInfo.value.subjectGuid, | ||
| 152 | subjectName: modelDetailInfo.value.subjectName, | ||
| 153 | dataSourceGuid: modelDetailInfo.value.dataSourceGuid, | ||
| 154 | modelRuleConfList: submitInfos | ||
| 155 | }]).then((res: any) => { | ||
| 156 | fullScreenLoading.value = false; | ||
| 157 | if (res.code == proxy.$passCode) { | ||
| 158 | ElMessage.success('新建规则保存成功'); | ||
| 159 | router.push({ | ||
| 160 | name: 'qualityRules' | ||
| 161 | }); | ||
| 162 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 163 | dataQualityStore.setModelGuid(modelGuid); | ||
| 164 | } else { | ||
| 165 | ElMessage.error(res.msg); | ||
| 166 | } | ||
| 167 | }) | ||
| 168 | }).catch(() => { | ||
| 169 | }); | ||
| 170 | } | ||
| 171 | |||
| 172 | const ruleTypeList = ref([]); | ||
| 173 | const smallCategoryList = ref([]); | ||
| 174 | const largeCategoryList = ref([]); | ||
| 175 | |||
| 176 | onBeforeMount(() => { | ||
| 177 | getModelDetail(modelGuid).then((res: any) => { | ||
| 178 | if (res.code == proxy.$passCode) { | ||
| 179 | let data = res.data || {}; | ||
| 180 | modelDetailInfo.value = data; | ||
| 181 | if (data.subjectGuid) { | ||
| 182 | toSubjectTables.value = [{ | ||
| 183 | guid: data.subjectGuid, | ||
| 184 | enName: data.subjectName, | ||
| 185 | chName: data.name, | ||
| 186 | label: `${data.subjectName}(${data.name})` | ||
| 187 | }]; | ||
| 188 | } | ||
| 189 | } else { | ||
| 190 | ElMessage.error(res.msg); | ||
| 191 | } | ||
| 192 | }) | ||
| 193 | getRuleTypeList().then((res: any) => { | ||
| 194 | if (res.code == proxy.$passCode) { | ||
| 195 | ruleTypeList.value = res.data?.map((d: any) => { | ||
| 196 | d.label = d.ruleName; | ||
| 197 | d.value = d.ruleCode; | ||
| 198 | return d; | ||
| 199 | }) || []; | ||
| 200 | } else { | ||
| 201 | ElMessage.error(res.msg); | ||
| 202 | } | ||
| 203 | }) | ||
| 204 | getSmallCategoryList().then((res: any) => { | ||
| 205 | if (res.code == proxy.$passCode) { | ||
| 206 | smallCategoryList.value = res.data || []; | ||
| 207 | } else { | ||
| 208 | ElMessage.error(res.msg); | ||
| 209 | } | ||
| 210 | }) | ||
| 211 | getLargeCategoryList().then((res: any) => { | ||
| 212 | if (res.code == proxy.$passCode) { | ||
| 213 | largeCategoryList.value = res.data || []; | ||
| 214 | } else { | ||
| 215 | ElMessage.error(res.msg); | ||
| 216 | } | ||
| 217 | }) | ||
| 218 | }) | ||
| 219 | |||
| 220 | const cancel = () => { | ||
| 221 | ElMessageBox.confirm( | ||
| 222 | "当前页面尚未保存,确定放弃修改吗?", | ||
| 223 | "提示", | ||
| 224 | { | ||
| 225 | confirmButtonText: "确定", | ||
| 226 | cancelButtonText: "取消", | ||
| 227 | type: "warning", | ||
| 228 | } | ||
| 229 | ) | ||
| 230 | .then(() => { | ||
| 231 | userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath)); | ||
| 232 | router.push({ | ||
| 233 | name: 'qualityRules', | ||
| 234 | }); | ||
| 235 | }) | ||
| 236 | .catch(() => { | ||
| 237 | ElMessage({ | ||
| 238 | type: "info", | ||
| 239 | message: "已取消", | ||
| 240 | }); | ||
| 241 | }); | ||
| 242 | } | ||
| 243 | |||
| 244 | </script> | ||
| 245 | |||
| 246 | <template> | ||
| 247 | <div class="container_wrap full" v-loading="fullScreenLoading"> | ||
| 248 | <div class="content_main"> | ||
| 249 | <div class="operator_panel_wrap"> | ||
| 250 | <div class="operator_panel is-block" v-for="(item, index) in ruleList"> | ||
| 251 | <div class="panel_title" :style="{ 'border-bottom': item.open ? '1px solid #d9d9d9' : 'none' }"> | ||
| 252 | <div class="title_text" @click="item.open = !item.open"> | ||
| 253 | <el-icon class="text_icon"> | ||
| 254 | <CaretRight v-show="!item.open" /> | ||
| 255 | <CaretBottom v-show="item.open" /> | ||
| 256 | </el-icon> | ||
| 257 | <span>规则{{ index + 1 }}</span> | ||
| 258 | </div> | ||
| 259 | <div class="title_tool" @click="deleteRule(item, index)"> | ||
| 260 | <el-icon :size="20" color="#b2b2b2"> | ||
| 261 | <Delete /> | ||
| 262 | </el-icon> | ||
| 263 | </div> | ||
| 264 | </div> | ||
| 265 | <div class="panel_content" v-show="item.open"> | ||
| 266 | <div class="form_panel"> | ||
| 267 | <ruleForm ref="ruleFormRef" :toSubjectTables="toSubjectTables" :ruleTypeList="ruleTypeList" | ||
| 268 | :largeCategoryList="largeCategoryList" :smallCategoryList="smallCategoryList" :isSingle="true"> | ||
| 269 | </ruleForm> | ||
| 270 | </div> | ||
| 271 | </div> | ||
| 272 | </div> | ||
| 273 | <div class="bottm_tools" @click="addRule"> | ||
| 274 | <el-icon> | ||
| 275 | <Plus /> | ||
| 276 | </el-icon> | ||
| 277 | <span>新增规则</span> | ||
| 278 | </div> | ||
| 279 | </div> | ||
| 280 | </div> | ||
| 281 | <div class="bottom_tool_wrap"> | ||
| 282 | <el-button @click="cancel" v-preReClick>取消</el-button> | ||
| 283 | <el-button type="primary" @click="save" v-preReClick>保存</el-button> | ||
| 284 | </div> | ||
| 285 | </div> | ||
| 286 | </template> | ||
| 287 | |||
| 288 | <style lang="scss" scoped> | ||
| 289 | .top_tool_wrap { | ||
| 290 | width: 100%; | ||
| 291 | height: 72px; | ||
| 292 | margin: 8px 0 16px; | ||
| 293 | display: flex; | ||
| 294 | justify-content: center; | ||
| 295 | align-items: center; | ||
| 296 | |||
| 297 | :deep(.el-steps) { | ||
| 298 | width: 30%; | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | .content_main { | ||
| 303 | height: calc(100% - 40px); | ||
| 304 | padding: 16px 16px 0px; | ||
| 305 | overflow: hidden auto; | ||
| 306 | |||
| 307 | :deep(.table_panel_wrap) { | ||
| 308 | .table_panel { | ||
| 309 | width: 100%; | ||
| 310 | min-height: unset; | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | .operator_panel_wrap { | ||
| 315 | display: flex; | ||
| 316 | justify-content: space-between; | ||
| 317 | flex-wrap: wrap; | ||
| 318 | |||
| 319 | :deep(.el-button) { | ||
| 320 | &.is-text { | ||
| 321 | height: auto; | ||
| 322 | padding: 0; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | .operator_panel { | ||
| 327 | width: calc(50% - 5px); | ||
| 328 | height: 100%; | ||
| 329 | border: 1px solid #d9d9d9; | ||
| 330 | margin-bottom: 12px; | ||
| 331 | overflow: hidden; | ||
| 332 | |||
| 333 | &.is-block { | ||
| 334 | width: 100%; | ||
| 335 | } | ||
| 336 | |||
| 337 | .panel_title { | ||
| 338 | height: 48px; | ||
| 339 | padding: 0 15px; | ||
| 340 | display: flex; | ||
| 341 | justify-content: space-between; | ||
| 342 | align-items: center; | ||
| 343 | color: var(--el-color-regular); | ||
| 344 | font-weight: 600; | ||
| 345 | |||
| 346 | .title_text { | ||
| 347 | display: flex; | ||
| 348 | cursor: pointer; | ||
| 349 | } | ||
| 350 | |||
| 351 | .tips_text { | ||
| 352 | font-size: 14px; | ||
| 353 | color: #999; | ||
| 354 | font-weight: normal; | ||
| 355 | margin-left: 8px; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | .title_tool { | ||
| 360 | :deep(.el-icon) { | ||
| 361 | svg { | ||
| 362 | width: 1em; | ||
| 363 | height: 1em; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | .panel_content { | ||
| 369 | height: calc(100% - 46px); | ||
| 370 | |||
| 371 | >div { | ||
| 372 | width: 100%; | ||
| 373 | height: 100%; | ||
| 374 | overflow: hidden; | ||
| 375 | } | ||
| 376 | |||
| 377 | .form_panel { | ||
| 378 | padding: 8px 16px 0; | ||
| 379 | overflow: hidden auto; | ||
| 380 | } | ||
| 381 | |||
| 382 | .tree_search_input { | ||
| 383 | margin-bottom: 10px; | ||
| 384 | } | ||
| 385 | |||
| 386 | .list_panel { | ||
| 387 | padding: 10px 0; | ||
| 388 | overflow: hidden auto; | ||
| 389 | |||
| 390 | .list_item { | ||
| 391 | height: 32px; | ||
| 392 | padding: 0 10px; | ||
| 393 | display: flex; | ||
| 394 | justify-content: space-between; | ||
| 395 | align-items: center; | ||
| 396 | |||
| 397 | &:hover { | ||
| 398 | color: var(--g-sub-sidebar-menu-active-color); | ||
| 399 | background-color: var(--g-sub-sidebar-menu-active-bg); | ||
| 400 | } | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | .table_content_wrap { | ||
| 405 | padding: 16px; | ||
| 406 | |||
| 407 | .tools_form { | ||
| 408 | p { | ||
| 409 | margin-top: 0; | ||
| 410 | margin-bottom: 8px; | ||
| 411 | } | ||
| 412 | } | ||
| 413 | } | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | .bottm_tools { | ||
| 418 | width: 100%; | ||
| 419 | height: 40px; | ||
| 420 | display: flex; | ||
| 421 | justify-content: center; | ||
| 422 | align-items: center; | ||
| 423 | background: #fafafa; | ||
| 424 | color: #999; | ||
| 425 | font-size: 14px; | ||
| 426 | border: 1px dashed var(--el-border-color-regular); | ||
| 427 | margin-bottom: 12px; | ||
| 428 | |||
| 429 | >span { | ||
| 430 | margin-left: 8px; | ||
| 431 | } | ||
| 432 | |||
| 433 | &:hover { | ||
| 434 | background: #EBF6F7; | ||
| 435 | border: 1px dashed var(--el-color-primary); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | .transfer_btns { | ||
| 441 | display: flex; | ||
| 442 | flex-direction: column; | ||
| 443 | justify-content: center; | ||
| 444 | align-items: center; | ||
| 445 | |||
| 446 | .el-button { | ||
| 447 | margin: 0 8px; | ||
| 448 | |||
| 449 | &+.el-button { | ||
| 450 | margin-top: 8px; | ||
| 451 | } | ||
| 452 | } | ||
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 456 | .bottom_tool_wrap { | ||
| 457 | height: 40px; | ||
| 458 | padding: 0 16px; | ||
| 459 | border-top: 1px solid #d9d9d9; | ||
| 460 | display: flex; | ||
| 461 | justify-content: flex-end; | ||
| 462 | align-items: center; | ||
| 463 | } | ||
| 464 | </style> |
-
Please register or sign in to post a comment