6326d5ed by lxs

全景地图地址修改

2 parents b0d49d5c 3625e1e3
Showing 53 changed files with 15872 additions and 50 deletions
...@@ -83,6 +83,9 @@ VITE_APP_ATTACHMENT_TEMPLATE = ms-daop-personel-service ...@@ -83,6 +83,9 @@ VITE_APP_ATTACHMENT_TEMPLATE = ms-daop-personel-service
83 #人员服务 83 #人员服务
84 VITE_APP_PERSONAL_URL = 'ms-daop-personel-service' 84 VITE_APP_PERSONAL_URL = 'ms-daop-personel-service'
85 85
86 #元数据标准
87 VITE_APP_STANDARD_URL = 'ms-daop-meta-standard-service'
88
86 #流通平台地址 89 #流通平台地址
87 VITE_APP_CIRCULATION = https://daop-lt-test.zgsjzc.com/ 90 VITE_APP_CIRCULATION = https://daop-lt-test.zgsjzc.com/
88 # 测试环境访问地址 91 # 测试环境访问地址
...@@ -91,7 +94,6 @@ VITE_APP_CIRCULATION = https://daop-lt-test.zgsjzc.com/ ...@@ -91,7 +94,6 @@ VITE_APP_CIRCULATION = https://daop-lt-test.zgsjzc.com/
91 #数据加工交付 94 #数据加工交付
92 VITE_APP_DATA_DELIVERY = https://daop-jgjf-test.zgsjzc.com/ 95 VITE_APP_DATA_DELIVERY = https://daop-jgjf-test.zgsjzc.com/
93 96
94
95 # 本地访问地址 97 # 本地访问地址
96 # VITE_API_CIRCULATION_URL = http://localhost:9000/circulation 98 # VITE_API_CIRCULATION_URL = http://localhost:9000/circulation
97 99
......
...@@ -110,6 +110,9 @@ VITE_API_ASSET_BASEURL = ms-swzl-data-dam-service ...@@ -110,6 +110,9 @@ VITE_API_ASSET_BASEURL = ms-swzl-data-dam-service
110 #数据同步接口地址 110 #数据同步接口地址
111 VITE_API_DATA_SYNC = ms-swzl-data-sync-service 111 VITE_API_DATA_SYNC = ms-swzl-data-sync-service
112 112
113 #元数据标准
114 VITE_APP_STANDARD_URL = 'ms-daop-meta-standard-service'
115
113 #消息接口 116 #消息接口
114 VITE_API_MESSAGE = ms-daop-message-service 117 VITE_API_MESSAGE = ms-daop-message-service
115 118
......
...@@ -27,6 +27,7 @@ declare module '@vue/runtime-core' { ...@@ -27,6 +27,7 @@ declare module '@vue/runtime-core' {
27 FileUpload: typeof import('./src/components/FileUpload/index.vue')['default'] 27 FileUpload: typeof import('./src/components/FileUpload/index.vue')['default']
28 FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default'] 28 FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default']
29 Form: typeof import('./src/components/Form/index.vue')['default'] 29 Form: typeof import('./src/components/Form/index.vue')['default']
30 GraphTopbar: typeof import('./src/components/RelationNetwork/graphTopbar.vue')['default']
30 Hour: typeof import('./src/components/Schedule/component/hour.vue')['default'] 31 Hour: typeof import('./src/components/Schedule/component/hour.vue')['default']
31 ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default'] 32 ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default']
32 ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default'] 33 ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default']
...@@ -41,6 +42,7 @@ declare module '@vue/runtime-core' { ...@@ -41,6 +42,7 @@ declare module '@vue/runtime-core' {
41 PageNav: typeof import('./src/components/PageNav/index.vue')['default'] 42 PageNav: typeof import('./src/components/PageNav/index.vue')['default']
42 PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default'] 43 PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default']
43 Popover: typeof import('./src/components/Popover/index.vue')['default'] 44 Popover: typeof import('./src/components/Popover/index.vue')['default']
45 RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default']
44 RouterLink: typeof import('vue-router')['RouterLink'] 46 RouterLink: typeof import('vue-router')['RouterLink']
45 RouterView: typeof import('vue-router')['RouterView'] 47 RouterView: typeof import('vue-router')['RouterView']
46 Schedule: typeof import('./src/components/Schedule/index.vue')['default'] 48 Schedule: typeof import('./src/components/Schedule/index.vue')['default']
......
...@@ -552,3 +552,8 @@ export const getContractDetail = (params) => request({ ...@@ -552,3 +552,8 @@ export const getContractDetail = (params) => request({
552 method: 'get', 552 method: 'get',
553 params 553 params
554 }) 554 })
555
556 // 获取所有字典
557 export const getDictAllList = () => request({
558 url: `${import.meta.env.VITE_APP_CONFIG_URL}/dict/data/get-all`
559 })
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -249,3 +249,297 @@ export const delMetaDataSource = (databaseGuid) => request({ ...@@ -249,3 +249,297 @@ export const delMetaDataSource = (databaseGuid) => request({
249 url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/del-by-database-guid?databaseGuid=${databaseGuid}`, 249 url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/del-by-database-guid?databaseGuid=${databaseGuid}`,
250 method: 'delete', 250 method: 'delete',
251 }) 251 })
252
253 /** 元数据-标准代码分页查询 */
254 export const getStandardCodeList = (params) => request({
255 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/page-list`,
256 method: 'post',
257 data: params
258 })
259 /** 元数据-标准代码新增 */
260 export const saveStandardCode = (params) => request({
261 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/save`,
262 method: 'post',
263 data: params
264 })
265 /** 元数据-标准代码修改 */
266 export const updateStandardCode = (params) => request({
267 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/update`,
268 method: 'put',
269 data: params
270 })
271 /** 元数据-标准代码详情 */
272 export const getStandardCodeDetail = (guid) => request({
273 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/detail?guid=${guid}`,
274 method: 'get',
275 })
276 /** 元数据-标准代码删除 */
277 export const deleteStandardCode = (params) => request({
278 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/delete`,
279 method: 'delete',
280 data: params
281 })
282 /** 元数据-查询标准列表 */
283 export const getStandardCodeStandard = (standardTypeCode) => request({
284 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/get-standard?standardTypeCode=${standardTypeCode}`,
285 method: 'get'
286 })
287 /** 元数据-查询代码列表 */
288 export const getStandardCodeDataList = (params) => request({
289 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/get-data`,
290 method: 'post',
291 data: params
292 })
293 /** 元数据-查询代码字段数据 */
294 export const getStandardCodeFields = (standardGuid) => request({
295 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/get-field?standardGuid=${standardGuid}`,
296 method: 'get'
297 })
298 /** 元数据-保存标准代码数据 */
299 export const saveStandardCodeFieldsData = (params) => request({
300 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/save-data`,
301 method: 'post',
302 data: params
303 })
304 /** 元数据-删除标准代码数据 */
305 export const deleteStandardCodeFieldsData = (params) => request({
306 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/remove-data`,
307 method: 'post',
308 data: params
309 })
310 /** 元数据-导出 */
311 export const exportStandardCodeData = (params) => request({
312 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/data/data-export`,
313 method: 'post',
314 data: params,
315 responseType: 'blob'
316 })
317 /** 元数据标准-树形列表 */
318 export const getMetaStandardTree = () => request({
319 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/list-tree`,
320 method: 'get'
321 })
322 /** 元数据标准-新增 */
323 export const saveMetaStandard = (params) => request({
324 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/save`,
325 method: 'post',
326 data: params
327 })
328 /** 元数据标准-修改 */
329 export const updateMetaStandard = (params) => request({
330 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/update`,
331 method: 'put',
332 data: params
333 })
334 /** 元数据标准-删除 */
335 export const deleteMetaStandard = (params) => request({
336 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/delete`,
337 method: 'delete',
338 data: params
339 })
340 /** 元数据标准-详情 */
341 export const getMetaStandardDetail = (guid) => request({
342 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/detail?guid=${guid}`,
343 method: 'get'
344 })
345 /** 元数据标准-标准数据 */
346 export const getMetaStandardDataList = (params) => request({
347 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/page-list`,
348 method: 'post',
349 data: params
350 })
351 /** 元数据标准-标准字段 */
352 export const getMetaStandardDataFields = (metaStandardGuid) => request({
353 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/get-standard-field?metaStandardGuid=${metaStandardGuid}`,
354 method: 'get'
355 })
356 /** 元数据标准-标准字段保存 */
357 export const saveMetaStandardDataFields = (params) => request({
358 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/save-or-update`,
359 method: 'post',
360 data: params
361 })
362 /** 元数据标准-标准字段删除 */
363 export const deleteMetaStandardDataFields = (params) => request({
364 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/del`,
365 method: 'delete',
366 data: params
367 })
368 /** 元数据标准-导出 */
369 export const exportMetaStandardData = (params) => request({
370 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/data-export`,
371 method: 'post',
372 data: params,
373 responseType: 'blob'
374 })
375 /** 标准代码-树形表 */
376 export const getStandardCodeTree = () => request({
377 url: `${import.meta.env.VITE_APP_STANDARD_URL}/standard-code/code-tree`,
378 method: 'get'
379 })
380
381 /** 元数据标准树形列表查询 */
382 export const getMetaStandardTreeList = (guid) => request({
383 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/list-tree?metaStandardGuid=${guid}`,
384 method: 'get'
385 })
386
387 /** 元数据标准guid查询只展示的字段 */
388 export const getMetaStandardField = (guid) => request({
389 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/list-by-meta-standard-guid?metaStandardGuid=${guid}`,
390 method: 'get'
391 })
392 /** 元数据标准字段详情 */
393 export const getMetaStandardFieldsDetail = (guid) => request({
394 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/detail?guid=${guid}`,
395 method: 'get'
396 })
397
398 /** 根据元数据标准展示字段去获取未展示的详情信息 */
399 export const getMetaStandardFieldDetail = (guid) => request({
400 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/data/convert-detail?guid=${guid}`,
401 method: 'get'
402 })
403
404 /** 获取桑基图数据 */
405 export const getSankeyData = (guid) => request({
406 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/sankey-data?metaStandardGuid=${guid}`,
407 method: 'get'
408 })
409
410 /** 数仓目录树列表查询 */
411 export const getDataWareCatalogList = (params) => request({
412 // url: `${import.meta.env.VITE_APP_DATA_DELIVERY}delivery/ms-daop-data-plan-service/data-catalog-directory/tree-list`,
413 url: `${import.meta.env.VITE_API_BASEURL}delivery/api/ms-daop-data-plan-service/data-catalog-directory/tree-list`,
414 method: 'post',
415 data: params
416 })
417
418 export const getAllFlowData = (dictType) => request({
419 url: `${import.meta.env.VITE_APP_CONFIG_URL}/dict/data/get-by-dictType?dictType=${dictType}`,
420 method: 'get',
421 })
422
423 /** 元数据标准guid查询新建数据集字段 */
424 export const getMetaStandardDsField = (guid) => request({
425 url: `${import.meta.env.VITE_APP_STANDARD_URL}/meta-standard/get-standard?metaStandardGuid=${guid}`,
426 method: 'get'
427 })
428
429 /** 表分类类型 */
430 export const tableCategoryList = [
431 {
432 value: 1,
433 label: "明细表",
434 },
435 {
436 value: 2,
437 label: "汇总表",
438 },
439 {
440 value: 3,
441 label: "应用表",
442 },
443 {
444 value: 6,
445 label: "业务表",
446 },
447 // {
448 // value: 4,
449 // label: "维度表",
450 // },
451 // {
452 // value: 5,
453 // label: "缓慢变化维",
454 // }
455 ];
456
457 /** 同步策略 */
458 export const syncPolicys = [
459 {
460 value: 1,
461 label: "实时",
462 },
463 {
464 value: 2,
465 label: "增量",
466 },
467 {
468 value: 3,
469 label: "全量",
470 },
471 {
472 value: 4,
473 label: "增量加更新",
474 },
475 ];
476
477 /** 维表类型 */
478 export const dimTypeList = [{
479 label: "列表",
480 value: 1,
481 },
482 {
483 label: "层级",
484 value: 2,
485 },
486 {
487 label: "螺旋",
488 value: 3,
489 },
490 {
491 label: "通用",
492 value: 4,
493 }];
494
495 /** 表模型分类 */
496 export const tableModels = [
497 {
498 label: "主键模型",
499 value: 1,
500 },
501 {
502 label: "聚合模型",
503 value: 2,
504 },
505 {
506 label: "明细模型",
507 value: 3,
508 }
509 ];
510
511 /** 聚合方式 */
512 export const aggMethodList = [{
513 value: 'SUM',
514 }, {
515 value: 'MAX'
516 }, {
517 value: 'MIN'
518 }, {
519 value: 'REPLACE'
520 }, {
521 value: 'REPLACE_IF_NOT_NULL'
522 }, {
523 value: 'HLL_UNION'
524 }, {
525 value: 'BITMAP_UNION'
526 }];
527
528 /** 保存主题表设置,直接入库 */
529 export const saveSubjectTable = (params) => request({
530 url: `${import.meta.env.VITE_API_BASEURL}delivery/api/ms-daop-data-plan-service/data-catalog-subject/add`,
531 method: 'post',
532 data: params
533 })
534
535 /** 获取字典列表 */
536 export const getDictionary = (params) => request({
537 url: `${import.meta.env.VITE_APP_PLAN_BASEURL}/data-dictionary-general/list-all?state=1`,
538 method: 'post'
539 })
540
541 /** 获取维度列表 */
542 export const getDimList = () => request({
543 url: `${import.meta.env.VITE_API_BASEURL}delivery/api/ms-daop-data-plan-service/data-catalog-subject/get-dim-list`,
544 method: 'get'
545 })
...\ No newline at end of file ...\ No newline at end of file
......
1 import request from "@/utils/request";
2
3 //获取需求表树形列表
4 export const getDemandTreeList = (params) => {
5 return request({
6 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/page-list`,
7 method: "post",
8 data: params,
9 });
10 };
11
12 //获取所有需求表列表
13 export const getDemandAll = (params) => {
14 return request({
15 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/get-list-data`,
16 method: "get",
17 params
18 });
19 };
20
21 //新增需求列表
22 export const saveDemandTree = (params) => {
23 return request({
24 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/save`,
25 method: "post",
26 data: params,
27 });
28 };
29
30 //修改需求列表
31 export const updateDemandTree = (params) => {
32 return request({
33 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/update`,
34 method: "post",
35 data: params,
36 });
37 };
38
39 // 删除需求列表
40 export const deleteDemandTree = (params) => {
41 return request({
42 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-menu/delete`,
43 method: "delete",
44 data: params,
45 });
46 };
47
48 //获取需求表
49 export const getDemandList = (params) => {
50 return request({
51 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/page-list`,
52 method: "post",
53 data: params,
54 });
55 };
56
57 //获取需求表详情
58 export const getDemandDetail = (params) => {
59 return request({
60 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/detail`,
61 method: "get",
62 params,
63 });
64 };
65
66 //新增需求表
67 export const saveDemand = (params) => {
68 return request({
69 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/save`,
70 method: "post",
71 data: params,
72 });
73 };
74
75 //修改需求表
76 export const updateDemand = (params) => {
77 return request({
78 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/update`,
79 method: "post",
80 data: params,
81 });
82 };
83
84 // 删除需求表
85 export const deleteDemand = (params) => {
86 return request({
87 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-demand-table/delete`,
88 method: "delete",
89 data: params,
90 });
91 };
92
93 // 获取疾病列表
94 export const getDiseaseList = (params) => {
95 return request({
96 url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/page-list`,
97 method: "post",
98 data: params,
99 });
100 };
101
102 //获取所有疾病列表
103 export const getDiseaseAll = () => {
104 return request({
105 url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/get-list-data`,
106 method: "post"
107 });
108 };
109
110 // 获取疾病详情
111 export const getDiseaseDetail = (params) => {
112 return request({
113 url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/detail`,
114 method: "get",
115 params,
116 });
117 };
118
119 // 新增疾病
120 export const saveDisease = (params) => {
121 return request({
122 url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/save`,
123 method: "post",
124 data: params,
125 });
126 };
127
128 // 修改疾病
129 export const updateDisease = (params) => {
130 return request({
131 url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/update`,
132 method: "post",
133 data: params,
134 });
135 };
136
137 // 删除疾病
138 export const deleteDisease = (params) => {
139 return request({
140 url: `${import.meta.env.VITE_API_NEW_PORTAL}/disease-manage/delete`,
141 method: "delete",
142 data: params,
143 });
144 };
145
146 // 获取定价配置
147 export const getConfigureList = (params) => {
148 return request({
149 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/page-list`,
150 method: "post",
151 data: params,
152 });
153 };
154
155 // 获取配置详情
156 export const getConfigureDetail = (params) => {
157 return request({
158 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/detail`,
159 method: "get",
160 params,
161 });
162 };
163
164 // 新增配置
165 export const saveConfigure = (params) => {
166 return request({
167 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/save`,
168 method: "post",
169 data: params,
170 });
171 };
172
173 // 修改配置
174 export const updateConfigure = (params) => {
175 return request({
176 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/update`,
177 method: "post",
178 data: params,
179 });
180 };
181
182 // 删除配置
183 export const deleteConfigure = (params) => {
184 return request({
185 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/delete`,
186 method: "delete",
187 data: params,
188 });
189 };
190
191 // 复制配置
192 export const addCopyConfigure = (params) => {
193 return request({
194 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-configure/copy`,
195 method: "post",
196 data: params,
197 });
198 };
199
200 // 获取数据定价
201 export const getPriceList = (params) => {
202 return request({
203 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/page-list`,
204 method: "post",
205 data: params,
206 });
207 };
208
209 // 获取数据定价详情
210 export const getPriceDetail = (params) => {
211 return request({
212 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/detail`,
213 method: "get",
214 params,
215 });
216 };
217
218 // 新增数据定价
219 export const savePrice = (params) => {
220 return request({
221 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/save`,
222 method: "post",
223 data: params,
224 });
225 };
226
227 // 修改数据定价
228 export const updatePrice = (params) => {
229 return request({
230 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/update`,
231 method: "post",
232 data: params,
233 });
234 };
235
236 // 获取数据定价结果
237 export const getPriceResult = (params) => {
238 return request({
239 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/pricing-price`,
240 method: "post",
241 data: params,
242 });
243 };
244
245 // 删除数据定价
246 export const deletePrice = (params) => {
247 return request({
248 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/delete`,
249 method: "delete",
250 data: params,
251 });
252 };
253
254 // 获取数据资源目录
255 export const getDamCatalogList = (params) => {
256 return request({
257 url: `${import.meta.env.VITE_API_NEW_PORTAL}/dam-catalog-table/get-table-select-new`,
258 method: "get",
259 params,
260 });
261 };
262
263 // 获取模型相关需求表
264 export const getModelDemand = (params) => {
265 return request({
266 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/pricing-model`,
267 method: "get",
268 params,
269 });
270 };
271
272 // 获取质量模型评分
273 export const getModelScore = (params) => {
274 return request({
275 url: `${import.meta.env.VITE_APP_QUALITY_BASEURL}/quality-analysis-report/get-quality-score-by-dam-guid-v2`,
276 method: "get",
277 params,
278 });
279 };
280
281 export const exportModelScore = (params) => {
282 return request({
283 url: `${import.meta.env.VITE_API_NEW_PORTAL}/pricing-data/download-template`,
284 method: "post",
285 data: params,
286 responseType: 'blob'
287 });
288 };
...@@ -303,3 +303,10 @@ export const getCurrentUserInfo = (params) => { ...@@ -303,3 +303,10 @@ export const getCurrentUserInfo = (params) => {
303 params 303 params
304 }); 304 });
305 }; 305 };
306
307 /** 下载文件模板 */
308 export const exportTemplate = (params) => request({
309 url: `${import.meta.env.VITE_APP_ADD_FILE}/import-config/export-template?bizGuid=${params.bizGuid}&importType=${params.importType}`,
310 method: 'post',
311 responseType: 'blob'
312 });
......
...@@ -1182,3 +1182,12 @@ ...@@ -1182,3 +1182,12 @@
1182 max-width: 100%; 1182 max-width: 100%;
1183 } 1183 }
1184 } 1184 }
1185
1186 .el-drawer__title {
1187 font-size: 18px;
1188 color: #212121;
1189 }
1190 .el-form-item--default .el-form-item__label {
1191 height: 26px;
1192 line-height: 26px;
1193 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -44,6 +44,7 @@ const emits = defineEmits([ ...@@ -44,6 +44,7 @@ const emits = defineEmits([
44 "uploadBtnClick", 44 "uploadBtnClick",
45 "beforeUPload", 45 "beforeUPload",
46 "uploadFile", 46 "uploadFile",
47 'deleteFile',
47 "onUpload", 48 "onUpload",
48 "scheduleChange", 49 "scheduleChange",
49 "cascaderChange", 50 "cascaderChange",
...@@ -668,7 +669,7 @@ defineExpose({ ...@@ -668,7 +669,7 @@ defineExpose({
668 <template v-else-if="con.type.indexOf('upload') > -1"> 669 <template v-else-if="con.type.indexOf('upload') > -1">
669 <UploadFiles ref="formUploadRef" :upload-info="con.uploadInfo" @onUpload="onUpload" 670 <UploadFiles ref="formUploadRef" :upload-info="con.uploadInfo" @onUpload="onUpload"
670 @beforeUPload="beforeUPload" @uploadFile="uploadFile" @uploadBtnClick="uploadBtnClick" 671 @beforeUPload="beforeUPload" @uploadFile="uploadFile" @uploadBtnClick="uploadBtnClick"
671 @cascaderChange="cascaderChange" @selectChange="uploadSelectChange" /> 672 @cascaderChange="cascaderChange" @selectChange="uploadSelectChange" @deleteFile="() => emits('deleteFile')"/>
672 </template> 673 </template>
673 <template v-else> 674 <template v-else>
674 <div v-if="con.formInfo.tools"> 675 <div v-if="con.formInfo.tools">
......
...@@ -458,3 +458,7 @@ const drawerClose = () => { ...@@ -458,3 +458,7 @@ const drawerClose = () => {
458 } 458 }
459 } 459 }
460 </style> 460 </style>
461
462 <style lang="scss">
463
464 </style>
......
...@@ -70,7 +70,15 @@ const onMouseOver = (str) => { ...@@ -70,7 +70,15 @@ const onMouseOver = (str) => {
70 </script> 70 </script>
71 71
72 <template> 72 <template>
73 <el-tooltip class="item" placement="top" effect="light" :popper-class="popperClass ?? 'table_tooltip'" :disabled="isShowTooltip" :open-delay="400" :content="content"> 73 <el-tooltip
74 class="item"
75 placement="top"
76 effect="light"
77 :popper-class="popperClass ?? 'table_tooltip'"
78 :disabled="isShowTooltip"
79 :open-delay="400"
80 :content="content"
81 >
74 <p class="over-flow" :class="className" @mouseover="onMouseOver(refName)"> 82 <p class="over-flow" :class="className" @mouseover="onMouseOver(refName)">
75 <span :ref="(el) => setRefMap(el, refName)" v-html="content || '-'"></span> 83 <span :ref="(el) => setRefMap(el, refName)" v-html="content || '-'"></span>
76 </p> 84 </p>
...@@ -92,3 +100,9 @@ p { ...@@ -92,3 +100,9 @@ p {
92 margin: 0; 100 margin: 0;
93 } 101 }
94 </style> 102 </style>
103
104 <style lang="scss">
105 .el-popper.tree-popper {
106 max-width: 280px!important;
107 }
108 </style>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -741,6 +741,7 @@ onMounted(() => { ...@@ -741,6 +741,7 @@ onMounted(() => {
741 toolRef.value.isFull = true 741 toolRef.value.isFull = true
742 } 742 }
743 }, 500) 743 }, 500)
744 return;
744 } 745 }
745 const container: any = containerRef.value; 746 const container: any = containerRef.value;
746 const width = container.clientWidth; 747 const width = container.clientWidth;
......
1 <script lang="ts" setup name="topbar">
2 import { ref, watch } from 'vue';
3
4 const props = defineProps({
5 isGraphDisplay: {
6 type: Boolean,
7 default: true
8 },
9 });
10
11 const isGraph = ref(false);
12
13 watch(() => props.isGraphDisplay, (val) => {
14 isGraph.value = val;
15 }, {
16 immediate: true
17 })
18
19 const emits = defineEmits(["displaySwitchChange"]);
20
21 const switchChange = (val) => {
22 isGraph.value = val
23 emits('displaySwitchChange', val);
24 }
25
26 </script>
27
28 <template>
29 <div className='g6-component-topbar-content'>
30 <div :class="isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(true)">
31 关系网
32 </div>
33 <div :class="!isGraph ? 'selected g6-component-topbar-item' : 'g6-component-topbar-item'" @click="switchChange(false)">
34 桑基图
35 </div>
36 </div>
37 </template>
38
39 <style scoped lang="scss">
40 .g6-component-topbar-content {
41 display: flex;
42 flex-direction: row;
43 justify-content: center;
44 align-items: center;
45 background: #fff;
46 border: 1px solid var(--el-color-primary);
47 border-radius: 32px;
48 padding: 4px;
49 width: 138px;
50 height: 32px;
51 }
52
53 .g6-component-topbar-item {
54 width: 50%;
55 height: 100%;
56 display: flex;
57 align-items: center;
58 justify-content: center;
59 font-size: 14px;
60 color: #999999;
61 cursor: pointer;
62
63 &.selected {
64 background: #4FA1A4;
65 border-radius: 32px;
66 color: #fff;
67 }
68 }
69 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div ref="containerRef" className='canvas-wrapper'>
3 <div class="main" ref="tooltip1Ref" style="display: none;position: absolute;" v-loading="detailLoading">
4 <div class="title">{{ detailInfoLabel }}</div>
5 <div class="row" v-for="item in Object.keys(detailInfo)">
6 <span>{{ item + ':' }}</span>
7 <span>{{ detailInfo[item] == null || detailInfo[item] == '' ? '-' : detailInfo[item] }}</span>
8 </div>
9 </div>
10 </div>
11 </template>
12
13 <script lang="ts" setup name="RelationNetwork">
14 import { ref, onMounted } from 'vue'
15 import G6 from '@antv/g6';
16 import insertCss from 'insert-css';
17 import {
18 getMetaStandardFieldDetail
19 } from '@/api/modules/dataMetaService';
20 import { ElMessage } from 'element-plus';
21
22 const props = defineProps({
23 treeData: {
24 type: Object,
25 default: {}
26 },
27 noContextMenu: { //关系查看页面,显示右键菜单定位到详情。标准查询页面,显示引用标准新建数据集
28 type: Boolean,
29 default: false
30 }
31 })
32
33 const emits = defineEmits([
34 'nodeItemClick',
35 'contextMenu'
36 ]);
37
38 const { proxy } = getCurrentInstance() as any;
39
40 const maxChineseCount = ref(16);
41
42 const maxEnglishCount = ref(26);
43
44 const detailLoading = ref(false);
45
46 const detailInfo: any = ref({});
47
48 const detailInfoLabel = ref('');
49
50 const containerRef = ref();
51
52 const graphRef = ref();
53
54 watch(() => props.treeData, (val) => {
55 if (!graphRef.value && val?.guid) {
56 nextTick(() => {
57 initGraph();
58 })
59 return;
60 }
61 if (!graphRef.value) {
62 return;
63 }
64 tooltip1Ref.value.style.display = 'none';
65 if (lastSelectNode.value) {
66 graphRef.value.updateItem(lastSelectNode.value, {
67 labelCfg: {
68 color: '#212121'
69 },
70 style: {
71 stroke: '#4fa1a4',
72 fill: '#ebf6f7',
73 cursor: 'pointer'
74 }
75 });
76 }
77 lastSelectNode.value = null;
78 if (val) {
79 renderGraph(graphRef.value, val);
80 }
81 })
82
83 const renderGraph = (graph: any, lineageData: any) => {
84 if (!graph || !lineageData) return;
85 graph.setMinZoom(1);
86 graph.setMaxZoom(1);
87 graph.data(lineageData);
88 graph.render();
89
90 graph.fitView(40, { direction: 'both' });
91 graph.fitCenter();
92
93 graph.setMinZoom(0.5);
94 graph.setMaxZoom(5);
95
96 };
97
98 const detectLanguage = (text) => {
99 if (!text) {
100 return 'English';
101 }
102 let chineseCount = 0;
103 let englishCount = 0;
104
105 for (let char of text) {
106 if (/[\u4e00-\u9fa5]/.test(char)) {
107 chineseCount++;
108 } else if (/[a-zA-Z]/.test(char)) {
109 englishCount++;
110 }
111 }
112
113 if (chineseCount > englishCount) {
114 return 'Chinese';
115 } else if (englishCount > chineseCount) {
116 return 'English';
117 }
118 return 'English';
119 }
120
121 const handleLabelLength = (label: string) => {
122 if (detectLanguage(label) == 'English') {
123 return label?.length > maxEnglishCount.value ? label.slice(0, maxEnglishCount.value) + '...' : label;
124 }
125 let arr = label.split('\n');
126 let maxLen = Math.max(...arr.map(s => s.length));
127 return maxLen > maxChineseCount.value ? label.slice(0, maxChineseCount.value) + '...' : label;
128 };
129
130 insertCss(`
131 .g6-component-contextmenu {
132 padding: 8px 0px;
133 background-color: #fff;
134 }
135 .context-menu {
136 display: flex;
137 flex-direction: column;
138 }
139 .menu-item {
140 line-height: 32px;
141 color: #212121;
142 cursor: pointer;
143 padding: 0 12px;
144 }
145 .menu-item:hover {
146 background-color: #f5f5f5;
147 }
148 `);
149
150 const tooltip = ref();
151
152 const initGraph = () => {
153 const container: any = containerRef.value;
154 const width = container.clientWidth;
155 const height = container.clientHeight - 10;
156
157 tooltip.value = new G6.Tooltip({
158 offsetX: 10,
159 offsetY: 10,
160 trigger: 'mouseenter',
161 // 允许出现 tooltip 的 item 类型
162 itemTypes: ['node'],
163 // 自定义 tooltip 内容
164 shouldBegin: (evt: any) => {
165 const { item, target } = evt;
166 const currentAnchor = target.get('name');
167 const name = item._cfg.model?.label;
168 if (currentAnchor == 'text-shape') {
169 if (detectLanguage(name) == 'English') {
170 return name?.length > maxEnglishCount.value;
171 }
172 let arr = name.split('\n');
173 let maxLen = Math.max(...arr.map(s => s.length));
174 return maxLen > maxChineseCount.value;
175 }
176 return false;
177 },
178 getContent: (e: any) => {
179
180 const { item, target } = e;
181 const currentAnchor = target.get('name');
182 const outDiv = document.createElement('div');
183 outDiv.className = 'node';
184 outDiv.style.width = 'fit-content';
185 const name = item._cfg.model.isField ? item._cfg.model.metaStandardId : item._cfg.model.standardName;
186 if (currentAnchor == 'text-shape') {
187 outDiv.innerHTML = `<h4>${name}</h4>`
188 }
189 return outDiv;
190 },
191 });
192
193 const contextMenu = new G6.Menu({
194 getContent(evt: any) {
195 const { item, target } = evt;
196 let model = item._cfg.model;
197 if (props.noContextMenu) {
198 return `
199 <div class='context-menu'>
200 <span class='menu-item'>查看标准管理</span>
201 </div>`
202 } else {
203 return `
204 <div class='context-menu'>
205 <span class='menu-item'>引用标准新建数据集</span>
206 </div>`
207 }
208 },
209 shouldBegin: (evt: any) => {
210 const { item, target } = evt;
211 let model = item._cfg.model;
212 if (props.noContextMenu) {
213 // return false;
214 if (model && !model.isField) {
215 return true;
216 }
217 return false;
218 }
219 if (model && !model.isField && !model.children?.length) {
220 return true;
221 }
222 if (model.children?.length && model.children[0].isField) {
223 return true;
224 }
225 return false;
226 },
227 handleMenuClick: (target, item: any) => {
228 let model = item._cfg?.model;
229 if (!model) {
230 return;
231 }
232 emits('contextMenu', model);
233 },
234 // offsetX and offsetY include the padding of the parent container
235 // 需要加上父级容器的 padding-left 16 与自身偏移量 10
236 offsetX: 16,
237 // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
238 offsetY: 0,
239 // the types of items that allow the menu show up
240 // 在哪些类型的元素上响应
241 itemTypes: ['node'],
242 });
243
244 const graph = new G6.TreeGraph({
245 container: container,
246 width,
247 height,
248 // animate: false,
249 plugins: [contextMenu, tooltip.value],
250 fitCenter: true,
251 fitView: true,
252 fitViewPadding: 40,
253 minZoom: 0.7,
254 maxZoom: 1,
255 modes: {
256 default: [
257 {
258 type: 'collapse-expand',
259 onChange: (item, collapsed) => {
260 if (!item) {
261 return;
262 }
263 const data = item.getModel();
264 data.collapsed = collapsed;
265 setTimeout(() => {
266 graph.focusItem(item, true, {
267 duration: 400 // 动画时长为500ms
268 });
269 }, 500)
270 return true;
271 },
272 shouldBegin: (e) => {
273 // 若当前操作的节点 id 为 'node1',则不发生 collapse-expand
274 if (e.item && e.item.getModel().isLoading == true) return false;
275 return true;
276 },
277 },
278 'drag-canvas',
279 'zoom-canvas',
280 ],
281 },
282 defaultNode: {
283 size: 16,
284 anchorPoints: [
285 [0, 0.5],
286 [1, 0.5],
287 ],
288 style: {
289 // stroke: '#4fa1a4',
290 fill: '#ebf6f7',
291 cursor: 'pointer'
292 },
293 },
294 defaultEdge: {
295 type: 'cubic-horizontal',
296 },
297 layout: {
298 type: 'compactBox',
299 direction: 'LR',
300 getId: function getId(d) {
301 return d.id;
302 },
303 getHeight: function getHeight() {
304 return 16;
305 },
306 getWidth: function getWidth() {
307 return 16;
308 },
309 getVGap: function getVGap(node) {
310 if (node.isField) {
311 return 6;
312 }
313 return 20;
314 },
315 getHGap: function getHGap() {
316 return 120;
317 },
318 },
319 });
320 graphRef.value = graph;
321 graph.node((node) => {
322 return {
323 size: node.isField ? 11 : 16,
324 id: node.guid as string,
325 label: handleLabelLength((node.isField ? node.metaStandardId : node.standardName) as string),
326 collapsed: node.children?.length ? false : true,
327 labelCfg: {
328 offset: node.isField ? 4 : 7,
329 style: {
330 fontSize: 13,
331 fill: '#212121',
332 fontWeight: 500,
333 cursor: 'pointer'
334 },
335 position: !node.isField ? 'left' : 'right', //只有字段是最后一层级,不需要展开
336 },
337 style: {
338 stroke: '#4fa1a4',
339 cursor: 'pointer'
340 }
341 };
342 });
343 // data不是数组,第一级是根节点
344 props.treeData?.guid && graph.data(props.treeData);
345 graph.render();
346 graph.fitView(40, { direction: 'both' });
347 graph.fitCenter();
348
349 graph.setMinZoom(0.5);
350 graph.setMaxZoom(5);
351 bindEvents();
352 observeResize();
353 }
354
355 onMounted(() => {
356 nextTick(() => {
357 if (!graphRef.value && props.treeData?.guid) {
358 initGraph();
359 }
360 })
361
362 })
363
364 const observeResize = () => {
365 window.addEventListener('resize', (e) => {
366 let domWidth = document.documentElement.clientWidth;
367 if (lastSelectNode.value) {
368 tooltip1Ref.value.style.display = 'none';
369 graphRef.value.updateItem(lastSelectNode.value, {
370 labelCfg: {
371 style: {
372 fill: '#212121',
373 },
374 },
375 style: {
376 stroke: '#4fa1a4',
377 fill: '#ebf6f7',
378 cursor: 'pointer'
379 }
380 });
381 }
382 lastSelectNode.value = null;
383 if (domWidth < 992) {//根据setting.ts里的设置,小于992,会隐藏左边的菜单栏,
384 setTimeout(() => {
385 const container: any = containerRef.value;
386 const width = container.clientWidth;
387 const height = container.clientHeight - 10;
388 if (!width) {//会把隐藏的给消失。
389 return;
390 }
391 graphRef.value.changeSize(width, height);
392 graphRef.value.setMinZoom(1);
393 graphRef.value.setMaxZoom(1);
394 graphRef.value.fitView(40, { direction: 'both' });
395 graphRef.value.fitCenter();
396 graphRef.value.setMinZoom(0.5);
397 graphRef.value.setMaxZoom(5);
398 }, 500)
399 return;
400 }
401 const container: any = containerRef.value;
402 const width = container.clientWidth;
403 const height = container.clientHeight - 10;
404 if (!width) {//会把隐藏的给消失。
405 return;
406 }
407 graphRef.value.changeSize(width, height);
408 graphRef.value.setMinZoom(1);
409 graphRef.value.setMaxZoom(1);
410 graphRef.value.fitView(40, { direction: 'both' });
411 graphRef.value.fitCenter();
412 graphRef.value.setMinZoom(0.5);
413 graphRef.value.setMaxZoom(5);
414 });
415 }
416
417
418 const tooltip1Ref = ref();
419
420 // 更新tooltip的位置
421 function updateTooltipPosition(evt) {
422 var width = graphRef.value.get("width");
423 var height = graphRef.value.get("height");
424 var offsetX = 10;
425 var offsetY = 10;
426 var point = graphRef.value.getPointByClient(evt.clientX, evt.clientY);
427 var _a2 = graphRef.value.getCanvasByPoint(point.x, point.y), x4 = _a2.x, y4 = _a2.y;
428 var graphContainer = graphRef.value.getContainer();
429 var res = {
430 x: x4 + graphContainer.offsetLeft + offsetX,
431 y: y4 + graphContainer.offsetTop + offsetY
432 };
433 let bbox = tooltip1Ref.value.getBoundingClientRect();
434 let bboxHeight = bbox.height;
435 if (x4 + 320 + offsetX > width) {
436 res.x -= 320 + offsetX;
437 }
438 if (y4 + bboxHeight + offsetY > height) {
439 res.y -= bboxHeight + offsetY;
440 if (res.y < 0) {
441 res.y = 0;
442 }
443 }
444 tooltip1Ref.value.style.left = `${res.x}px`;
445 tooltip1Ref.value.style.top = `${res.y}px`;
446 tooltip1Ref.value.style.display = 'block';
447 }
448
449 const lastSelectNode = ref(null);
450
451 const bindEvents = () => {
452 let graph = graphRef.value;
453 if (!graph) {
454 return;
455 }
456 graph.on('node:click', function (evt) {
457 const item = evt.item;
458 if (!item) {
459 return;
460 }
461 const nodeId = item.get('guid');
462 const model = <any>item.getModel();
463 if (lastSelectNode.value) {
464 if (lastSelectNode.value == item) {
465 return;
466 }
467 tooltip1Ref.value.style.display = 'none';
468 graphRef.value.updateItem(lastSelectNode.value, {
469 labelCfg: {
470 style: {
471 fill: '#212121',
472 }
473 },
474 style: {
475 stroke: '#4fa1a4',
476 fill: '#ebf6f7',
477 cursor: 'pointer'
478 }
479 });
480 lastSelectNode.value = null;
481 }
482 const children = model.children;
483 if (children?.length && !model.isField) {
484 return;
485 }
486 if (model.isField) { //是字段级别,就不需要再展开了
487 graphRef.value.updateItem(item, {
488 labelCfg: {
489 style: {
490 fill: '#4fa1a4'
491 }
492 },
493 style: {
494 stroke: '#4fa1a4',
495 fill: '#4fa1a4',
496 cursor: 'pointer'
497 }
498 });
499 lastSelectNode.value = item;
500 detailInfo.value = {};
501 // detailInfo.value.guid = model.guid;
502 detailInfoLabel.value = model.metaStandardId || {};
503 detailLoading.value = true;
504 nextTick(() => {
505 tooltip1Ref.value.style.display = 'block';
506 updateTooltipPosition(evt);
507 getMetaStandardFieldDetail(model.guid).then((res: any) => {
508
509 if (res?.code == proxy.$passCode) {
510 detailInfo.value = res.data?.metaStandardValue || {};
511 detailInfoLabel.value = model.metaStandardId;
512 detailLoading.value = false;
513 nextTick(() => {
514 tooltip1Ref.value.style.display = 'block';
515 updateTooltipPosition(evt);
516 })
517 } else {
518 ElMessage.error(res.msg);
519 }
520 })
521 })
522 return;
523 }
524 evt.preventDefault();
525 evt.stopPropagation();
526 model.isLoading = true;
527 emits('nodeItemClick', graph, item);
528 });
529 graph.on('dragstart', (evt: any) => {
530 if (evt.item?.getType() == 'node') {
531 return;
532 }
533 // 清除状态
534 if (lastSelectNode.value) {
535 tooltip1Ref.value.style.display = 'none';
536 graphRef.value.updateItem(lastSelectNode.value, {
537 labelCfg: {
538 style: {
539 fill: '#212121',
540 },
541 },
542 style: {
543 stroke: '#4fa1a4',
544 fill: '#ebf6f7',
545 cursor: 'pointer'
546 }
547 });
548 }
549 lastSelectNode.value = null;
550 })
551 graph.on('click', (evt: any) => {
552 if (evt.item?.getType() == 'node') {
553 return;
554 }
555 // 清除状态
556 if (lastSelectNode.value) {
557 tooltip1Ref.value.style.display = 'none';
558 graphRef.value.updateItem(lastSelectNode.value, {
559 labelCfg: {
560 style: {
561 fill: '#212121',
562 },
563 },
564 style: {
565 stroke: '#4fa1a4',
566 fill: '#ebf6f7',
567 cursor: 'pointer'
568 }
569 });
570 }
571 lastSelectNode.value = null;
572 });
573 graph.on('canvas:click', (evt: any) => {
574 // 清除状态
575 if (lastSelectNode.value) {
576 tooltip1Ref.value.style.display = 'none';
577 graphRef.value.updateItem(lastSelectNode.value, {
578 labelCfg: {
579 style: {
580 fill: '#212121',
581 },
582 },
583 style: {
584 stroke: '#4fa1a4',
585 fill: '#ebf6f7',
586 cursor: 'pointer'
587 }
588 });
589 }
590 lastSelectNode.value = null;
591 });
592 }
593
594 const destroy = () => {
595 graphRef.value?.destroy();
596 }
597
598 defineExpose({
599 destroy,
600 });
601
602 </script>
603
604 <style lang="scss" scoped>
605 .canvas-wrapper {
606 width: 100%;
607 height: 100%;
608 position: relative;
609 overflow: hidden;
610 }
611
612 .canvas-wrapper:-webkit-full-screen {
613 background-color: white;
614 }
615
616 .canvas-wrapper:-moz-full-screen {
617 background-color: white;
618 }
619
620 .canvas-wrapper:-ms-fullscreen {
621 background-color: white;
622 }
623
624 .canvas-wrapper:fullscreen {
625 background-color: white;
626 }
627
628 .main {
629 padding: 16px;
630 box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08);
631 background-color: #fff;
632 width: 320px;
633 min-height: 100px;
634 max-height: 400px;
635 overflow-y: auto;
636
637 .title {
638 font-size: 14px;
639 color: #212121;
640 line-height: 24px;
641 font-weight: 600;
642 }
643
644 .row {
645 margin-top: 8px;
646 font-size: 12px;
647 color: #666666;
648 word-break: break-all;
649 line-height: 18px;
650 }
651 }
652 </style>
...\ No newline at end of file ...\ No newline at end of file
...@@ -248,6 +248,7 @@ onMounted(() => { ...@@ -248,6 +248,7 @@ onMounted(() => {
248 :show-header="props.tableInfo.showHeader ?? true" stripe :border="props.tableInfo.border 248 :show-header="props.tableInfo.showHeader ?? true" stripe :border="props.tableInfo.border
249 ?? true" :height="props.tableInfo.height === null ? null : (props.tableInfo.height ?? '100%')" 249 ?? true" :height="props.tableInfo.height === null ? null : (props.tableInfo.height ?? '100%')"
250 :max-height="maxHeight" :row-key="rowKey" :current-row-key="currentRowKey" :row-class-name="rowClassName" 250 :max-height="maxHeight" :row-key="rowKey" :current-row-key="currentRowKey" :row-class-name="rowClassName"
251 :tree-props="props.tableInfo.treeProps" :default-expand-all="props.tableInfo.expandAll ?? false"
251 :expand-row-keys="props.tableInfo.expandedKey" v-loading="tableDataLoading" @row-click="rowClick" 252 :expand-row-keys="props.tableInfo.expandedKey" v-loading="tableDataLoading" @row-click="rowClick"
252 @row-dblclick="rowDblClick" @selection-change="selectionChange" @select="tableCheckboxSelectChange" 253 @row-dblclick="rowDblClick" @selection-change="selectionChange" @select="tableCheckboxSelectChange"
253 @select-all="tableCheckboxAllSelectChange" style="width: 100%; display: inline-block" 254 @select-all="tableCheckboxAllSelectChange" style="width: 100%; display: inline-block"
......
...@@ -42,20 +42,20 @@ const checkKeys = computed(() => { ...@@ -42,20 +42,20 @@ const checkKeys = computed(() => {
42 return props.treeInfo.checkedKey ?? [] 42 return props.treeInfo.checkedKey ?? []
43 }); 43 });
44 const currentNodeKey = computed(() => { 44 const currentNodeKey = computed(() => {
45 // if (props.treeInfo.currentNodeKey) { 45 if (props.treeInfo.currentNodeKey) {
46 // nextTick(() => { 46 nextTick(() => {
47 // let domItems = treeRef.value?.$el.getElementsByClassName('el-tree-node'); 47 let domItems = treeRef.value?.$el.getElementsByClassName('el-tree-node');
48 // let clientHeight = treeRef.value?.$el.clientHeight; 48 let clientHeight = treeRef.value?.$el.clientHeight;
49 // for (const item of domItems) { 49 for (const item of domItems) {
50 // if (item.getAttribute('data-key') == props.treeInfo.currentNodeKey) { 50 if (item.getAttribute('data-key') == props.treeInfo.currentNodeKey) {
51 // if (item.offsetTop > clientHeight) { 51 if (item.offsetTop > clientHeight) {
52 // item.scrollIntoView({ block: "end", inline: "nearest" }); 52 item.scrollIntoView({ block: "end", inline: "nearest" });
53 // } 53 }
54 // break; 54 break;
55 // } 55 }
56 // } 56 }
57 // }) 57 })
58 // } 58 }
59 return props.treeInfo.currentNodeKey ?? '' 59 return props.treeInfo.currentNodeKey ?? ''
60 }); 60 });
61 const customInfo = computed(() => { 61 const customInfo = computed(() => {
...@@ -70,6 +70,9 @@ const editTreeItem = computed(() => { ...@@ -70,6 +70,9 @@ const editTreeItem = computed(() => {
70 const expendAll = computed(() => { 70 const expendAll = computed(() => {
71 return props.treeInfo.expendAll ?? false 71 return props.treeInfo.expendAll ?? false
72 }) 72 })
73 const ellipsis = computed(() => {
74 return props.treeInfo.ellipsis ?? true
75 })
73 76
74 interface Tree { 77 interface Tree {
75 [key: string]: any; 78 [key: string]: any;
...@@ -182,6 +185,21 @@ const appendChildren = (key, data) => { ...@@ -182,6 +185,21 @@ const appendChildren = (key, data) => {
182 185
183 const visibleNodes = ref({}); //解决点击树item,下拉菜单不消失的问题,原因是树的点击事件阻止冒泡了。 186 const visibleNodes = ref({}); //解决点击树item,下拉菜单不消失的问题,原因是树的点击事件阻止冒泡了。
184 187
188 function formatNodeLabel (node) {
189 let sliceLength = 8
190 switch (node.level) {
191 case 2:
192 sliceLength = 9
193 break
194 case 3:
195 sliceLength = 6
196 break
197 case 4:
198 sliceLength = 6
199 break
200 }
201 return node.label.slice(0, sliceLength)
202 }
185 defineExpose({ 203 defineExpose({
186 getCheckedNodes, 204 getCheckedNodes,
187 getCheckedKeys, 205 getCheckedKeys,
...@@ -225,8 +243,14 @@ defineExpose({ ...@@ -225,8 +243,14 @@ defineExpose({
225 </span> 243 </span>
226 <template v-else-if="editTreeItem && data.showEdit !== false"> 244 <template v-else-if="editTreeItem && data.showEdit !== false">
227 <span class="list-item-text"> 245 <span class="list-item-text">
228 <ellipsis-tooltip v-if="!customInfo" :content="node.label" class-name="w100f" 246 <template v-if="!ellipsis">
247 <span>{{ formatNodeLabel(node) }}</span>
248 </template>
249 <template v-else>
250 <ellipsis-tooltip v-if="!customInfo" :content="node.label" class-name="w100f" popper-class="tree-popper"
229 :refName="'tooltipOver' + node.id"></ellipsis-tooltip> 251 :refName="'tooltipOver' + node.id"></ellipsis-tooltip>
252 </template>
253
230 </span> 254 </span>
231 <div class="tags-list-right" :class="visibleNodes[node.data.guid] ? 'active' : ''"> 255 <div class="tags-list-right" :class="visibleNodes[node.data.guid] ? 'active' : ''">
232 <el-popover v-model:visible="visibleNodes[node.data.guid]" placement="bottom" width="96" trigger="click" 256 <el-popover v-model:visible="visibleNodes[node.data.guid]" placement="bottom" width="96" trigger="click"
......
...@@ -4,7 +4,7 @@ import { ElMessage, genFileId } from "element-plus"; ...@@ -4,7 +4,7 @@ import { ElMessage, genFileId } from "element-plus";
4 import { Plus, Download, Upload, } from "@element-plus/icons-vue"; 4 import { Plus, Download, Upload, } from "@element-plus/icons-vue";
5 import type { UploadRawFile } from 'element-plus' 5 import type { UploadRawFile } from 'element-plus'
6 6
7 const emits = defineEmits(["onUpload", "beforeUPload", "uploadFile", "uploadBtnClick", "cascaderChange", "selectChange"]); 7 const emits = defineEmits(["onUpload", "beforeUPload", "uploadFile", "uploadBtnClick", "cascaderChange", "selectChange", 'deleteFile']);
8 const props = defineProps({ 8 const props = defineProps({
9 uploadInfo: { 9 uploadInfo: {
10 type: Object, 10 type: Object,
...@@ -75,6 +75,7 @@ const btnClick = (file, type) => { ...@@ -75,6 +75,7 @@ const btnClick = (file, type) => {
75 if (type == 'remove') { 75 if (type == 'remove') {
76 const fileUpload = fileUploadRef.value[0] || fileUploadRef.value 76 const fileUpload = fileUploadRef.value[0] || fileUploadRef.value
77 fileUpload?.handleRemove(file) 77 fileUpload?.handleRemove(file)
78 emits('deleteFile')
78 } else if (type == 'upload') { 79 } else if (type == 'upload') {
79 file.show = false 80 file.show = false
80 emits('uploadFile', file) 81 emits('uploadFile', file)
...@@ -113,11 +114,14 @@ defineExpose({ ...@@ -113,11 +114,14 @@ defineExpose({
113 <span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label 114 <span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label
114 }}:</span> 115 }}:</span>
115 <el-select v-model="extraParams[item.selectInfo.field]" :placeholder="item.selectInfo.placeholder" 116 <el-select v-model="extraParams[item.selectInfo.field]" :placeholder="item.selectInfo.placeholder"
116 :disabled="item.selectInfo.disabled" @change="selectChange"> 117 :disabled="item.selectInfo.disabled" @change="selectChange" :filterable="item.selectInfo.filterable" :style="item.selectInfo.style">
117 <el-option v-for="opts in item.selectInfo.options" :label="opts.label" :value="opts.value" /> 118 <el-option v-for="opts in item.selectInfo.options"
119 :label="item.selectInfo.props?.label ? opts[item.selectInfo.props.label] : opts.label"
120 :value="item.selectInfo.props?.value ? opts[item.selectInfo.props.value] : opts.value"
121 :disabled="opts.disabled" />
118 </el-select> 122 </el-select>
119 </div> 123 </div>
120 <div class="form_item" v-if="item.type == 'tree-select'"> 124 <div class="form_item" v-else-if="item.type == 'tree-select'">
121 <span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label 125 <span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label
122 }}:</span> 126 }}:</span>
123 <el-tree-select 127 <el-tree-select
...@@ -176,7 +180,7 @@ defineExpose({ ...@@ -176,7 +180,7 @@ defineExpose({
176 </el-button> 180 </el-button>
177 </div> 181 </div>
178 <el-upload v-else ref="fileUploadRef" class="upload_panel" :class="[props.uploadInfo.col]" 182 <el-upload v-else ref="fileUploadRef" class="upload_panel" :class="[props.uploadInfo.col]"
179 v-model:file-list="fileList" :action="item.uploadInfo.action" :auto-upload="item.uploadInfo.auto ?? ''" 183 v-model:file-list="fileList" :action="item.uploadInfo.action ?? ''" :auto-upload="item.uploadInfo.auto ?? ''"
180 :drag="item.uploadInfo.drag ?? false" :accept="item.uploadInfo.accept" :limit="item.uploadInfo.limit ?? 1" 184 :drag="item.uploadInfo.drag ?? false" :accept="item.uploadInfo.accept" :limit="item.uploadInfo.limit ?? 1"
181 :on-change="onUpload" :on-exceed="val => exceedFile(val, item.uploadInfo.cover ?? false)" 185 :on-change="onUpload" :on-exceed="val => exceedFile(val, item.uploadInfo.cover ?? false)"
182 :on-error="handleError" :on-success="handleSuccess" :before-upload="beforeUPload" 186 :on-error="handleError" :on-success="handleSuccess" :before-upload="beforeUPload"
......
...@@ -4,6 +4,7 @@ import { useRouter } from "vue-router"; ...@@ -4,6 +4,7 @@ import { useRouter } from "vue-router";
4 import useUserStore from "@/store/modules/user"; 4 import useUserStore from "@/store/modules/user";
5 import useKeepAliveStore from '@/store/modules/keepAlive' 5 import useKeepAliveStore from '@/store/modules/keepAlive'
6 import { ElMessageBox, ElMessage } from "element-plus"; 6 import { ElMessageBox, ElMessage } from "element-plus";
7 import { isEqual } from "lodash-es";
7 8
8 const router = useRouter(); 9 const router = useRouter();
9 const route = useRoute(); 10 const route = useRoute();
...@@ -26,7 +27,7 @@ watch( ...@@ -26,7 +27,7 @@ watch(
26 if (tab.fullPath.includes(jionPath)) { 27 if (tab.fullPath.includes(jionPath)) {
27 pathIndex.value = index 28 pathIndex.value = index
28 } 29 }
29 return tab.fullPath === newRouter.fullPath; 30 return tab.fullPath === newRouter.fullPath || (tab.path == newRouter.path && isEqual(tab.query, newRouter.query));
30 }); 31 });
31 if (isExist.length == 0) { 32 if (isExist.length == 0) {
32 if (pathIndex.value != -1 && routerLength > 3) { 33 if (pathIndex.value != -1 && routerLength > 3) {
...@@ -45,11 +46,8 @@ watch( ...@@ -45,11 +46,8 @@ watch(
45 46
46 } 47 }
47 } 48 }
48 list.map(item => {
49 item.fullPath = item.fullPath;
50 })
51 tabbarList.value = list; 49 tabbarList.value = list;
52 tabbarActive.value = newRouter.fullPath; 50 tabbarActive.value = isExist[0]?.fullPath || newRouter.fullPath;
53 userStore.setTabbar(tabbarList.value); 51 userStore.setTabbar(tabbarList.value);
54 userStore.setActiveTabbar(combPath, newRouter.fullPath); 52 userStore.setActiveTabbar(combPath, newRouter.fullPath);
55 }, 53 },
...@@ -58,7 +56,7 @@ watch( ...@@ -58,7 +56,7 @@ watch(
58 const changeTab = (pane: any, ev: any) => { 56 const changeTab = (pane: any, ev: any) => {
59 const tabIndex = Number(pane.index); 57 const tabIndex = Number(pane.index);
60 const paneData: any = tabbarList.value[tabIndex]; 58 const paneData: any = tabbarList.value[tabIndex];
61 if (paneData.name == 'budgetDataIndex' || paneData.name == 'iframePage') { 59 if (paneData.name == 'budgetDataIndex' || paneData.name == 'iframePage' || paneData.name == 'portraitMap' || paneData.name == 'portraitMaps') {
62 router.push(paneData.fullPath); 60 router.push(paneData.fullPath);
63 } else { 61 } else {
64 router.push({ 62 router.push({
......
...@@ -144,7 +144,7 @@ const routes: RouteRecordRaw[] = [ ...@@ -144,7 +144,7 @@ const routes: RouteRecordRaw[] = [
144 { 144 {
145 path: 'budgetDataIndex', 145 path: 'budgetDataIndex',
146 name: 'budgetDataIndex', 146 name: 'budgetDataIndex',
147 component: () => import('@/views/data_catalog/budgetDataIndex.vue'), 147 component: () => import('@/views/data_meta/budgetDataIndex.vue'),
148 meta: { 148 meta: {
149 title: '', 149 title: '',
150 // sidebar: false, 150 // sidebar: false,
...@@ -166,7 +166,7 @@ const routes: RouteRecordRaw[] = [ ...@@ -166,7 +166,7 @@ const routes: RouteRecordRaw[] = [
166 { 166 {
167 path: 'iframePage', 167 path: 'iframePage',
168 name: 'iframePage', 168 name: 'iframePage',
169 component: () => import('@/views/data_catalog/budgetDataIndex.vue'), 169 component: () => import('@/views/data_meta/budgetDataIndex.vue'),
170 meta: { 170 meta: {
171 title: '', 171 title: '',
172 sidebar: true, 172 sidebar: true,
...@@ -175,6 +175,19 @@ const routes: RouteRecordRaw[] = [ ...@@ -175,6 +175,19 @@ const routes: RouteRecordRaw[] = [
175 reuse: true, 175 reuse: true,
176 activeMenu: '/data-meta/reports/iframePage', 176 activeMenu: '/data-meta/reports/iframePage',
177 }, 177 },
178 },
179 {
180 path: 'portraitMaps',
181 name: 'portraitMaps',
182 component: () => import('@/views/data_meta/portraitMap.vue'),
183 meta: {
184 title: '全景地图',
185 sidebar: true,
186 breadcrumb: false,
187 cache: true,
188 reuse: true,
189 activeMenu: '/data-meta/reports/portraitMaps',
190 },
178 } 191 }
179 ] 192 ]
180 }, 193 },
...@@ -199,6 +212,119 @@ const routes: RouteRecordRaw[] = [ ...@@ -199,6 +212,119 @@ const routes: RouteRecordRaw[] = [
199 }, 212 },
200 } 213 }
201 ] 214 ]
215 },
216 {
217 path: '/data-meta/metadata-standard',
218 component: Layout,
219 meta: { title: '元数据标准', icon: 'ep:grid' },
220 children: [
221 {
222 path: '',
223 name: 'metadataStandard',
224 component: () => import('@/views/data_meta/standard.vue'),
225 meta: {
226 title: '元数据标准管理',
227 breadcrumb: false,
228 cache: true
229 }
230 },
231 {
232 path: 'standard-query',
233 name: 'metadataStandardQuery',
234 component: () => import('@/views/data_meta/standard-query.vue'),
235 meta: {
236 title: '元数据标准查询',
237 breadcrumb: false,
238 cache: true
239 }
240 },
241 {
242 path: 'standard-query-view',
243 name: 'metadataStandardQueryView',
244 component: () => import('@/views/data_meta/standard-query-view.vue'),
245 meta: {
246 title: '元数据标准查看',
247 breadcrumb: false,
248 cache: true,
249 activeMenu: '/data-meta/metadata-standard'
250 },
251 beforeEnter: (to, from) => {
252 if (to.query.name) {
253 to.meta.title = `元数据标准查看-${to.query.name}`;
254 }
255 }
256 },
257 {
258 path: 'table-create-manual',
259 name: 'tableCreateManual',
260 component: () => import('@/views/data_meta/tableCreateManual.vue'),
261 meta: {
262 title: '新建表',
263 sidebar: false,
264 breadcrumb: false,
265 cache: true,
266 reuse: true,
267 activeMenu: '/data-meta/metadata-standard/standard-query'
268 },
269 beforeEnter: (to, from) => {
270 if (to.query.domainName) {
271 to.meta.title = `新建表(${to.query.domainName})-${to.query.standardName}`;
272 to.meta.editPage = true;
273 }
274 }
275 },
276 {
277 path: 'dim-table-create-manual',
278 name: 'dimTableCreateManual',
279 component: () => import('@/views/data_meta/dimTableCreateManual.vue'),
280 meta: {
281 title: '新建表',
282 sidebar: false,
283 breadcrumb: false,
284 cache: true,
285 reuse: true,
286 activeMenu: '/data-meta/metadata-standard/standard-query'
287 },
288 beforeEnter: (to, from) => {
289 if (to.query.domainName) {
290 to.meta.title = `新建表(${to.query.domainName})-${to.query.standardName}`;
291 to.meta.editPage = true;
292 }
293 }
294 },
295 {
296 path: 'standard-codetable',
297 name: 'metadataStandardCodetable',
298 component: () => import('@/views/data_meta/standard-codetable.vue'),
299 meta: {
300 title: '标准代码表',
301 breadcrumb: false,
302 cache: true
303 }
304 },
305 {
306 path: 'standard-import',
307 name: 'metadataStandardImport',
308 component: () => import('@/views/data_meta/standard-import.vue'),
309 meta: {
310 title: '标准代码导入',
311 breadcrumb: false,
312 cache: true,
313 activeMenu: '/data-meta/metadata-standard/standard-codetable'
314 }
315 },
316 {
317 path: 'standard-meta-import',
318 name: 'standardMetaImport',
319 component: () => import('@/views/data_meta/standard-meta-import.vue'),
320 meta: {
321 title: '元数据标准导入',
322 breadcrumb: false,
323 cache: true,
324 activeMenu: '/data-meta/metadata-standard'
325 }
326 }
327 ]
202 } 328 }
203 ] 329 ]
204 330
......
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-pricing/pricing-manage',
10 component: Layout,
11 meta: {
12 title: '定价管理',
13 icon: 'ep:grid',
14 },
15 children: [
16 {
17 path: 'demand-manage',
18 name: 'demandManage',
19 component: () => import('@/views/data_pricing/demandManage.vue'),
20 meta: {
21 title: '需求表管理',
22 breadcrumb: false,
23 cache: true
24 },
25 },
26 {
27 path: 'import-file-demand-manage',
28 name: 'importFileDemandManage',
29 component: () => import('@/views/importFile.vue'),
30 meta: {
31 title: '导入数据-需求表管理',
32 sidebar: false,
33 breadcrumb: false,
34 cache: true,
35 reuse: true,
36 activeMenu: '/data-pricing/pricing-manage/demand-manage'
37 }
38 },
39 {
40 path: 'disease-manage',
41 name: 'diseaseManage',
42 component: () => import('@/views/data_pricing/diseaseManage.vue'),
43 meta: {
44 title: '疾病管理',
45 breadcrumb: false,
46 cache: true
47 },
48 },
49 {
50 path: 'import-file-disease',
51 name: 'importFileDisease',
52 component: () => import('@/views/importFile.vue'),
53 meta: {
54 title: '导入数据-疾病管理',
55 sidebar: false,
56 breadcrumb: false,
57 cache: true,
58 reuse: true,
59 activeMenu: '/data-pricing/pricing-manage/disease-manage'
60 }
61 },
62 {
63 path: 'price-config',
64 name: 'priceConfig',
65 component: () => import('@/views/data_pricing/priceConfig.vue'),
66 meta: {
67 title: '定价配置',
68 breadcrumb: false,
69 cache: true
70 },
71 },
72 {
73 path: 'price-model',
74 name: 'priceModel',
75 component: () => import('@/views/data_pricing/priceModel.vue'),
76 meta: {
77 title: '新增配置',
78 sidebar: false,
79 breadcrumb: false,
80 cache: true,
81 reuse: true,
82 editPage: true,
83 activeMenu: '/data-pricing/pricing-manage/price-config'
84 },
85 beforeEnter: (to, from) => {
86 if (to.query.guid) {
87 to.meta.title = `编辑-${to.query.name}`;
88 } else {
89 to.meta.title = `新增配置`;
90 }
91 }
92 },
93 {
94 path: 'price-calculate',
95 name: 'priceCalculate',
96 component: () => import('@/views/data_pricing/priceCalculate.vue'),
97 meta: {
98 title: '数据定价',
99 breadcrumb: false,
100 cache: true
101 }
102 },
103 {
104 path: 'calculate-config',
105 name: 'calculateConfig',
106 component: () => import('@/views/data_pricing/calculateConfig.vue'),
107 meta: {
108 title: '新增数据定价',
109 sidebar: false,
110 breadcrumb: false,
111 cache: true,
112 reuse: true,
113 editPage: true,
114 activeMenu: '/data-pricing/pricing-manage/price-calculate'
115 },
116 beforeEnter: (to, from) => {
117 if (to.query.guid) {
118 to.meta.title = `编辑-${to.query.name}`;
119 } else {
120 to.meta.title = `新增数据定价`;
121 }
122 }
123 },
124 ],
125 },
126 ]
127
128 export default routes
...@@ -10,6 +10,7 @@ import DataTrustedSpace from './modules/dataTrustedSpace'; ...@@ -10,6 +10,7 @@ import DataTrustedSpace from './modules/dataTrustedSpace';
10 import DataAssetRegistry from './modules/dataAssetRegistry'; 10 import DataAssetRegistry from './modules/dataAssetRegistry';
11 import DataEntry from './modules/dataEntry'; 11 import DataEntry from './modules/dataEntry';
12 import SecurityMenu from './modules/securityMenu'; 12 import SecurityMenu from './modules/securityMenu';
13 import DataPricing from './modules/dataPricing';
13 14
14 import useSettingsStore from '@/store/modules/settings' 15 import useSettingsStore from '@/store/modules/settings'
15 16
...@@ -112,7 +113,8 @@ const asyncRoutes: RouteRecordRaw[] = [ ...@@ -112,7 +113,8 @@ const asyncRoutes: RouteRecordRaw[] = [
112 ...DataMeta, 113 ...DataMeta,
113 ...DataQuality, 114 ...DataQuality,
114 ...DataInventory, 115 ...DataInventory,
115 ...DataTrustedSpace 116 ...DataTrustedSpace,
117 ...DataPricing
116 ] 118 ]
117 119
118 const constantRoutesByFilesystem = generatedRoutes.filter((item) => { 120 const constantRoutesByFilesystem = generatedRoutes.filter((item) => {
......
...@@ -174,7 +174,10 @@ const useMenuStore = defineStore( ...@@ -174,7 +174,10 @@ const useMenuStore = defineStore(
174 else { 174 else {
175 // 如果是 string 类型,则认为是路由,需要查找对应的主导航索引 175 // 如果是 string 类型,则认为是路由,需要查找对应的主导航索引
176 const findIndex = allMenus.value.findIndex(item => item.children.some(r => { 176 const findIndex = allMenus.value.findIndex(item => item.children.some(r => {
177 if ((data== "/data-meta/reports/iframePage" || data== "/data-meta/report/budgetDataIndex") && r.path?.includes('/data-meta/report')) { 177 if ((data== "/data-meta/reports/iframePage" || data == '/data-meta/reports/portraitMaps' || data== "/data-meta/report/budgetDataIndex") && r.path?.includes('/data-meta/report')) {
178 return true;
179 }
180 if (data== "/data-meta/portraitMap" && r.path?.includes('/data-meta/portraitMap')) {
178 return true; 181 return true;
179 } 182 }
180 if (data== "/data-meta/portraitMap" && r.path?.includes('/data-meta/portraitMap')) { 183 if (data== "/data-meta/portraitMap" && r.path?.includes('/data-meta/portraitMap')) {
......
...@@ -157,7 +157,7 @@ const useRouteStore = defineStore( ...@@ -157,7 +157,7 @@ const useRouteStore = defineStore(
157 }; 157 };
158 } 158 }
159 r = routes.find((route: any) => { 159 r = routes.find((route: any) => {
160 return route.path === path || route.path === m.path || `/${route.path}` === m.path || ((path.includes('budgetDataIndex') && path.includes('/data-meta/report') && route.path.includes('/data-meta/report')) || (path.includes('iframePage') && path.includes('/data-meta/report')) || (path.includes('/data-meta/portraitMap') && route.path.includes('/data-meta/portraitMap'))); 160 return route.path === path || route.path === m.path || `/${route.path}` === m.path || ((path.includes('budgetDataIndex') && path.includes('/data-meta/report') && route.path.includes('/data-meta/report')) || (path.includes('iframePage') && path.includes('/data-meta/report')) || (path.includes('portraitMaps') && path.includes('/data-meta/report')) || (path.includes('/data-meta/portraitMap') && route.path.includes('/data-meta/portraitMap')));
161 }); 161 });
162 if (r && (path.includes('budgetDataIndex') || path.includes('iframePage')) || path.includes('portraitMap')) { 162 if (r && (path.includes('budgetDataIndex') || path.includes('iframePage')) || path.includes('portraitMap')) {
163 r.path = path; 163 r.path = path;
......
...@@ -27,6 +27,7 @@ declare module '@vue/runtime-core' { ...@@ -27,6 +27,7 @@ declare module '@vue/runtime-core' {
27 FileUpload: typeof import('./../components/FileUpload/index.vue')['default'] 27 FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
28 FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default'] 28 FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default']
29 Form: typeof import('./../components/Form/index.vue')['default'] 29 Form: typeof import('./../components/Form/index.vue')['default']
30 GraphTopbar: typeof import('./../components/RelationNetwork/graphTopbar.vue')['default']
30 Hour: typeof import('./../components/Schedule/component/hour.vue')['default'] 31 Hour: typeof import('./../components/Schedule/component/hour.vue')['default']
31 ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default'] 32 ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
32 ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default'] 33 ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default']
...@@ -41,6 +42,7 @@ declare module '@vue/runtime-core' { ...@@ -41,6 +42,7 @@ declare module '@vue/runtime-core' {
41 PageNav: typeof import('./../components/PageNav/index.vue')['default'] 42 PageNav: typeof import('./../components/PageNav/index.vue')['default']
42 PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default'] 43 PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default']
43 Popover: typeof import('./../components/Popover/index.vue')['default'] 44 Popover: typeof import('./../components/Popover/index.vue')['default']
45 RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default']
44 RouterLink: typeof import('vue-router')['RouterLink'] 46 RouterLink: typeof import('vue-router')['RouterLink']
45 RouterView: typeof import('vue-router')['RouterView'] 47 RouterView: typeof import('vue-router')['RouterView']
46 Schedule: typeof import('./../components/Schedule/index.vue')['default'] 48 Schedule: typeof import('./../components/Schedule/index.vue')['default']
......
...@@ -120,6 +120,9 @@ export const filterCascaderData = (data, field, val, type = 'disabled') => { ...@@ -120,6 +120,9 @@ export const filterCascaderData = (data, field, val, type = 'disabled') => {
120 } 120 }
121 // 数字千分位 保留两位小数 121 // 数字千分位 保留两位小数
122 export const changeNum = (num, fixed = 0, round = false) => { 122 export const changeNum = (num, fixed = 0, round = false) => {
123 if(num === '' || num === null || num === undefined){
124 return '';
125 }
123 num = parseFloat(num); 126 num = parseFloat(num);
124 if (round) { 127 if (round) {
125 let parts = num.toFixed(fixed).split(".") 128 let parts = num.toFixed(fixed).split(".")
......
...@@ -884,7 +884,7 @@ const matchEnValue = ref({ ...@@ -884,7 +884,7 @@ const matchEnValue = ref({
884 <div class="data-label"> 884 <div class="data-label">
885 <div class="container_wrap full flex"> 885 <div class="container_wrap full flex">
886 <div class="aside_wrap"> 886 <div class="aside_wrap">
887 <div class="aside_title">分类分级目录</div> 887 <div class="aside_title">分类分级模板</div>
888 <Tree :treeInfo="treeInfo" @nodeClick="nodeClick" @itemMenuClick="handleTreeItemMenuClick" /> 888 <Tree :treeInfo="treeInfo" @nodeClick="nodeClick" @itemMenuClick="handleTreeItemMenuClick" />
889 </div> 889 </div>
890 <div class="main_wrap"> 890 <div class="main_wrap">
......
...@@ -1293,6 +1293,7 @@ const loadDrawer = async () => { ...@@ -1293,6 +1293,7 @@ const loadDrawer = async () => {
1293 drawerInfo.value.footer.btns.at(-2).visible = false 1293 drawerInfo.value.footer.btns.at(-2).visible = false
1294 drawerInfo.value.footer.btns.at(-3).visible = false 1294 drawerInfo.value.footer.btns.at(-3).visible = false
1295 drawerInfo.value.visible = true 1295 drawerInfo.value.visible = true
1296 console.log('table', formTable.value)
1296 }; 1297 };
1297 1298
1298 const batching = (type) => { 1299 const batching = (type) => {
......
1 <template>
2 <div ref="containerRef" class='canvas-wrapper'>
3
4 </div>
5 </template>
6
7 <script lang="ts" setup name="topbar">
8 import { ref, watch } from 'vue';
9 import * as echarts from "echarts";
10
11 const props = defineProps({
12 treeData: {
13 type: Array<any>,
14 default: [
15 {
16 source: 'a',
17 target: 'a1',
18 value: 5
19 },
20 {
21 source: 'a',
22 target: 'a2',
23 value: 3
24 },
25 {
26 source: 'b',
27 target: 'b1',
28 value: 8
29 },
30 {
31 source: 'a',
32 target: 'b1',
33 value: 3
34 },
35 {
36 source: 'b1',
37 target: 'a1',
38 value: 1
39 },
40 {
41 source: 'b1',
42 target: 'c',
43 value: 2
44 }
45 ]
46 },
47 names: {
48 type: Array<any>,
49 default: [
50 {
51 name: 'a'
52 },
53 {
54 name: 'b'
55 },
56 {
57 name: 'a1'
58 },
59 {
60 name: 'a2'
61 },
62 {
63 name: 'b1'
64 },
65 {
66 name: 'c'
67 }
68 ]
69 }
70 })
71
72 watch(() => props.treeData, (val) => {
73 setChartsOption();
74 })
75
76 const sankeyInstance: any = ref();
77
78 const containerRef = ref();
79
80 const setChartsOption = () => {
81
82 let option = {
83 tooltip: {
84 trigger: 'item',
85 formatter: (params) => {
86 if (params.data.name) {
87 return null;
88 }
89 return params.data.source + ' > ' + params.data.target
90 }
91 },
92 color: ["#3DBCBE", "#6b67d1", "#7BBCE0", "#2B8EF3", "#51dca2", "#E19D46"],
93 series: [
94 {
95 type: 'sankey',
96 top: 60,
97 bottom: 20,
98 draggable: false,
99 left: 50,
100 emphasis: {
101 focus: 'trajectory'
102 },
103 labelLayout: {
104 hideOverlap: true,
105 },
106 right: 100,
107 label: {
108 fontSize: 10,
109 },
110 data: props.names?.map(n => {
111 if (n.isLast) {
112 return {
113 ...n,
114 label: {
115 width: 97,
116 overflow: 'breakAll'
117 }
118 }
119 }
120 return n;
121 }),
122 links: props.treeData,
123 lineStyle: {
124 color: 'source',
125 curveness: 0.5
126 },
127 }
128 ]
129 }
130 sankeyInstance.value.setOption(option);
131 sankeyInstance.value.resize();
132 }
133
134 const resizeObserver = ref();
135
136 const observeResize = () => {
137 window.addEventListener('resize', (e) => {
138 sankeyInstance.value?.resize();
139 });
140 }
141
142 onMounted(() => {
143 nextTick(() => {
144 sankeyInstance.value = echarts.init(containerRef.value);
145 setChartsOption();
146 observeResize();
147 })
148 })
149
150 </script>
151
152 <style lang="scss" scoped>
153 .canvas-wrapper {
154 width: 100%;
155 height: 100%;
156 position: relative;
157 }
158 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: dictFileds
3 </route>
4
5 <script lang="ts" setup name="dictFileds">
6 import { ref } from 'vue'
7 import router from '@/router'
8 import { ElMessage, ElMessageBox } from "element-plus";
9 import Table from '@/components/Table/index.vue'
10 // import Dialog from '@/components/Dialog/index.vue'
11 import useCatchStore from "@/store/modules/catch";
12 import { chunk } from '@/utils/common'
13 import { getStandardCodeDataList, getStandardCodeFields,
14 saveStandardCodeFieldsData, deleteStandardCodeFieldsData,
15 exportStandardCodeData
16 } from '@/api/modules/dataMetaService'
17 import { download } from '@/utils/common'
18
19 import {
20 saveDictionaryData,
21 getDictionaryFileds,
22 deleteDictionaryData,
23 checkDictionaryData,
24 showDictionary,
25 getDictionaryRuleData
26 } from '@/api/modules/dataInventory';
27
28 const emits = defineEmits(["exportData"])
29 const { proxy } = getCurrentInstance() as any;
30
31 const tableEl = ref()
32 const cacheStore = useCatchStore()
33 const standardGuid = ref("")
34 const standardName = ref('')
35 const tableSearchInput = ref('')
36 const tableFields: any = ref([])
37 const orginData: any = ref([])
38 const currTableData: any = ref<Object>({});
39 const selectRowData = ref([])
40 const page = ref({
41 limit: 50,
42 curr: 1,
43 sizes: [
44 { label: "10", value: 10 },
45 { label: "50", value: 50 },
46 { label: "100", value: 100 },
47 { label: "200", value: 200 },
48 { label: "300", value: 300 },
49 ],
50 });
51 const tableChunkData: any = ref([])
52 const tableData: any = ref([])
53 const tableInfo: any = ref({
54 id: 'data-fileds-table',
55 multiple: true,
56 fields: [],
57 data: [],
58 page: {
59 type: "normal",
60 rows: 0,
61 ...page.value,
62 },
63 // showPage: false,
64 actionInfo: {
65 label: "操作",
66 type: "btn",
67 width: 92,
68 btns: (scope) => {
69 return [
70 { label: "编辑", value: "edit", visible: scope.row['STATE'] !== 'Running' },
71 { label: "删除", value: "remove", visible: scope.row['STATE'] !== 'Running' },
72 // { label: "保存", value: "save", visible: scope.row['STATE'] === 'Running' },
73 { label: "取消", value: "cancel", visible: scope.row['STATE'] === 'Running' },
74 ]
75 },
76 },
77 editInfo: {},
78 loading: false
79 })
80
81 const uploadFiles = ref([])
82 const uploadInfo = ref({
83 type: 'upload',
84 title: '',
85 col: '',
86 uploadInfo: {
87 id: 'upload-file-form',
88 type: 'panel',
89 action: '',
90 auto: false,
91 cover: true,
92 fileList: [],
93 accept: '.xlsx, .xls',
94 triggerBtn: {
95 label: '导入',
96 value: 'import_file',
97 icon: 'Upload',
98 },
99 tips: '当前支持xls、xlsx文件,默认使用第一个sheet'
100 },
101 })
102
103 const dialogInfo = ref({
104 visible: false,
105 size: 640,
106 direction: "column",
107 header: {
108 title: "新建",
109 },
110 type: '',
111 contents: [
112 uploadInfo.value
113 ],
114 footer: {
115 visible: true,
116 btns: [
117 { type: "default", label: "取消", value: "cancel" },
118 { type: "primary", label: "开始导入", value: "submit" },
119 ],
120 },
121 })
122
123
124 const getFirstPageData = () => {
125 page.value.curr = 1;
126 toSearch({})
127 }
128
129 const toSearch = (val: any, clear: boolean = false) => {
130 let params = {
131 pageIndex: page.value.curr,
132 pageSize: page.value.limit,
133 standardGuid : standardGuid.value
134 }
135 getTableData(params);
136 };
137
138 const getTableData = (params) => {
139 tableInfo.value.loading = true
140 Promise.all([getStandardCodeFields(standardGuid.value), getStandardCodeDataList(params)]).then((resList:any) => {
141 console.log(resList)
142 let schemaDataVOS = resList[0].data || []
143 let jsonArray = resList[1].data.records || []
144 let data = resList[1].data
145 tableInfo.value.page.limit = data.pageSize
146 tableInfo.value.page.curr = data.pageIndex
147 tableInfo.value.page.rows = data.totalRows
148 setUploadDataInfo({schemaDataVOS,jsonArray}, true)
149
150 }).finally(() => tableInfo.value.loading = false)
151 };
152
153 const tableSelectionChange = (val, tId) => {
154 selectRowData.value = val;
155 };
156
157 const tablePageChange = (info) => {
158 console.log('pageChange')
159 const toChange = checkSave()
160 const changeCont = () => {
161 page.value.curr = Number(info.curr)
162 page.value.limit = Number(info.limit)
163 toSearch({})
164 }
165 if (!toChange) {
166 ElMessageBox.confirm(
167 '存在未保存的数据,切换后会丢失,是否确定切换',
168 '提示',
169 {
170 confirmButtonText: '确定',
171 cancelButtonText: '取消',
172 type: 'warning',
173 }
174 ).then(() => {
175 changeCont()
176 })
177 } else {
178 changeCont()
179 }
180
181 };
182
183 const saveDisabled = ref(true)
184 const toolBtnClick = (btn) => {
185 const type = btn.value
186 if (type == 'export') {
187 exportData()
188 } else if (type == 'import') {
189 const info = [
190 { standardGuid: standardGuid.value, standardName: standardName.value }
191 ]
192 cacheStore.setCatch('uploadSetting', info)
193 nextTick(() => {
194 router.push({
195 path: '/data-meta/metadata-standard/standard-import',
196 query: { bizGuid: standardGuid.value }
197 });
198 })
199 } else if (type == 'submit') {
200 saveData()
201 } else if (type == 'add_row') {
202 let rowInfo: any = {}
203 tableFields.value.map(item => {
204 rowInfo[item.field] = ''
205 })
206 rowInfo.guid = undefined;
207 rowInfo.STATE = 'Running'
208 rowInfo.STATUS = 'edit'
209 rowInfo.ROWID = `upload_${tableData.value.length}`
210 tableData.value.unshift(rowInfo)
211 orginData.value.unshift(rowInfo)
212 // tableInfo.value.page.rows = tableData.value.length
213 saveDisabled.value = false
214 // 表格滚动到第一行
215 let $tableEl = tableEl.value.tableRef
216 $tableEl.setCurrentRow(rowInfo)
217 let table = $tableEl.layout.table.refs
218 let tableScrollEle =
219 table.bodyWrapper.firstElementChild.firstElementChild
220 tableScrollEle.scrollTop = 0
221 }
222 }
223
224 const tableBtnClick = (scope, btn) => {
225 const type = btn.value;
226 let row = scope.row;
227 currTableData.value = row;
228 if (type == "edit") {
229 row.STATE = 'Running'
230 row.STATUS = 'edit'
231 tableData.value[scope.$index] = row
232 saveDisabled.value = false
233 } else if (type == 'save') {
234 saveData(scope)
235 } else if (type == 'cancel') {
236 if (row.guid != undefined) {
237 // row = orginData.value[(page.value.curr - 1) * page.value.limit + scope.$index]
238 row = JSON.parse(JSON.stringify(orginData.value[scope.$index]))
239 row.STATE = ''
240 row.STATUS = ''
241 tableData.value[scope.$index] = row
242 } else {
243 tableData.value.splice(scope.$index, 1)
244 orginData.value.splice(scope.$index, 1)
245 }
246 // tableInfo.value.page.rows = tableData.value.length
247 let haveState = tableData.value.some(item => item.STATE)
248 saveDisabled.value = !haveState
249 } else if (type == 'remove') {
250 open("是否确定删除所选数据?", "warning");
251 }
252 };
253
254 const onUpload = (file, fileList) => {
255 uploadFiles.value = fileList
256 }
257
258 const uploadBtnClick = (btn) => {
259 exportData('model')
260 }
261
262 const exportData = (type: any = null) => {
263 // emits('exportData', type)
264 let body = {
265 standardCodeGuid: standardGuid.value,
266 standardCodeDataGuids: selectRowData.value.map(v => v.guid)
267 }
268 exportStandardCodeData(body).then((res:any) => {
269 if (res && !res.msg) {
270 download(res, `${standardName.value}.xlsx`, 'excel')
271 } else {
272 res?.msg && ElMessage.error(res?.msg);
273 }
274 })
275 }
276
277 const importData = (file: any = null) => {
278 let params = new FormData()
279 if (file) {
280 params.append("file", file.raw);
281 } else {
282 if (uploadFiles.value.length) {
283 uploadFiles.value.forEach((item: any, index: number) => {
284 params.append("file", item.raw);
285 });
286 }
287 }
288
289 params.append("standardGuid", standardGuid.value);
290 showDictionary(params).then((res: any) => {
291 if (res.code == proxy.$passCode) {
292 dialogInfo.value.visible = false
293 let data = res.data ?? []
294 // data.map((item: any, i) => {
295 // item.index = tableData.value.length + i
296 // })
297 const tData = { jsonArray: data }
298 setUploadDataInfo(tData)
299 // saveData(null, tData)
300 } else {
301 ElMessage({
302 type: "error",
303 message: res.msg,
304 });
305 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
306 }
307 }).catch(() => {
308 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
309 });
310 }
311
312 const checkSave = () => {
313 const toSaveData = tableData.value.filter(item => item.STATE === 'Running')
314 return toSaveData.length == 0 ? true : false
315 }
316
317 const checkParamsData = (scope: any = null) => {
318 let addJsonArray: any = [], upJsonArray: any = [], jsonArray: any = [], pass = true
319 let passArr = scope ? [scope.row] : tableData.value
320 passArr.map((item, index) => {
321 const obj = JSON.parse(JSON.stringify(item))
322 delete obj.STATUS
323 delete obj.NOTES
324 delete obj.STATE
325 delete obj.ROWID
326 if (obj.guid !== undefined) {
327 upJsonArray.push(obj)
328 } else {
329 addJsonArray.push(obj)
330 }
331 if (scope) {
332 // obj.index = scope.$index
333 jsonArray.push(obj)
334 } else {
335 // obj.index = index
336 jsonArray.push(obj)
337 }
338 })
339 return { pass, addJsonArray, upJsonArray, jsonArray }
340 }
341
342 const loading = ref(false)
343 const saveData = async (scope: any = null, checkParamData: any = null) => {
344 let passInfo: any = {}
345 if (checkParamData) {
346 passInfo = checkParamData
347 } else {
348 passInfo = await checkParamsData(scope)
349 console.log('passInfo', passInfo)
350 if (!passInfo.pass) {
351 ElMessage({
352 type: 'error',
353 message: '请填写所有数据项'
354 })
355 return
356 }
357 }
358 let params = passInfo.jsonArray.map(item => {
359 let standardCodeValue = {}
360 tableFields.value.forEach(fieldObj => {
361 standardCodeValue[fieldObj.field] = item[fieldObj.field]
362 })
363 let obj = {
364 standardGuid: standardGuid.value,
365 standardCodeValue,
366 // guid: item.guid || null,
367 // standardCodeId: item.standardCodeId || null,
368 // parentId: item.parentId || null
369 }
370 if (item.guid) {
371 obj.guid = item.guid
372 }
373 if (item.standardCodeId) {
374 obj.standardCodeId = item.standardCodeId
375 }
376 if (item.parentId) {
377 obj.parentId = item.parentId
378 }
379 return obj
380 })
381 loading.value = true
382 tableInfo.value.loading = true
383 saveStandardCodeFieldsData(params).then((res: any) => {
384 if (res.code == proxy.$passCode) {
385 getFirstPageData();
386 ElMessage({
387 type: 'success',
388 message: '保存成功'
389 })
390 } else {
391 ElMessage({
392 type: 'error',
393 message: res.msg,
394 })
395 tableInfo.value.loading = false
396 }
397 }).finally(() => loading.value = false)
398 }
399
400 const addColumn = (info: any = null) => {
401 const fields = tableFields.value
402 const existIndex = fields.findIndex(item => item.field == 'NOTES')
403 if (info) {
404 if (existIndex == -1) {
405 fields.push({
406 label: '备注',
407 field: 'NOTES',
408 width: 200
409 })
410 }
411 for (var d in info) {
412 tableData.value[d].NOTES = info[d].join(',')
413 }
414 } else {
415 if (existIndex > -1) {
416 fields.splice(existIndex, 1)
417 }
418 }
419 }
420
421 // 生成表头
422 const setUploadDataFields = (data) => {
423 let fields: any = [], editInfo: any = {}
424 data.map(item => {
425 let fieldItem: any = {
426 label: item.fieldName, field: item.fieldName, width: 140
427 }
428 fieldItem.type = 'edit'
429 fieldItem.columClass = 'edit-colum'
430 editInfo[item.fieldName] = {
431 label: '',
432 type: 'input',
433 placeholder: '',
434 maxlength: 50,
435 field: item.fieldName,
436 default: '',
437 disabled: item.codeRuleGuid ? true : false,
438 clearable: true,
439 }
440 fields.push(fieldItem)
441 })
442 tableFields.value = fields
443 tableInfo.value.fields = tableFields.value
444 tableInfo.value.editInfo = editInfo
445 }
446
447 // 生成表数据
448 const setUploadDataInfo = async (info, setField = false) => {
449 if (setField) {
450 const fields = info.schemaDataVOS ?? []
451 await setUploadDataFields(fields)
452 }
453 let data = info.jsonArray ?? []
454 // 设置表数据
455 // data.map((item, i) => {
456 // item.ROWID = `upload_${tableData.value.length + i}`
457 // })
458 data.forEach(item => {
459 Object.keys(item.standardCodeValue).forEach(key => {
460 item[key] = item.standardCodeValue[key]
461 })
462 })
463 if (setField) {
464 tableData.value = data
465 } else {
466 tableData.value = [...tableData.value, ...data]
467 }
468 orginData.value = JSON.parse(JSON.stringify(tableData.value))
469 tableInfo.value.data = tableData.value
470 // tableInfo.value.page.rows = tableData.value.length
471 // if (setField) {
472 // orginData.value = data
473 // } else {
474 // orginData.value = [...orginData.value, ...data]
475 // }
476 // chunkData()
477 }
478
479 const chunkData = () => {
480 const data = orginData.value
481 tableChunkData.value = chunk(data, page.value.limit)
482 tableData.value = tableChunkData.value[page.value.curr - 1]
483 tableInfo.value.data = tableData.value
484 tableInfo.value.page.limit = page.value.limit
485 tableInfo.value.page.curr = page.value.curr
486 tableInfo.value.page.rows = orginData.value.length
487 }
488
489 const batching = (type) => {
490 if (type == 'delete') {
491 if (selectRowData.value.length == 0) {
492 ElMessage({
493 type: 'error',
494 message: '请选择需要删除的数据',
495 })
496 return
497 }
498 open("是否确定删除所选数据?", "warning", true);
499 }
500 };
501
502 const open = (msg, type, isBatch = false) => {
503 ElMessageBox.confirm(msg, "提示", {
504 confirmButtonText: "确定",
505 cancelButtonText: "取消",
506 type: type,
507 }).then(() => {
508 let guids: any = []
509 if (isBatch) {
510 const list = selectRowData.value.filter((item: any) => item.guid !== undefined)
511 if (list.length) {
512 guids = list.map((l: any) => l.guid)
513 }
514 const newRows = selectRowData.value.filter((item: any) => item.guid == undefined)
515 newRows.map((n: any, r) => {
516 const existIndex = tableData.value.findIndex(t => t.ROWID == n.ROWID)
517 if (existIndex > -1) {
518 tableData.value.splice(existIndex, 1)
519 orginData.value.splice(existIndex, 1)
520 }
521 // const existIndex = orginData.value.findIndex(t => t.id == n.id)
522 // existIndex > -1 && orginData.value.splice(existIndex, 1)
523 // if (r == newRows.length - 1) {
524 // page.value.curr = 1
525 // chunkData()
526 // }
527 })
528 tableInfo.value.page.rows = tableData.value.length
529 } else {
530 guids = [currTableData.value.guid]
531 }
532 if (guids.length) {
533 const params = {
534 guid: standardGuid.value,
535 delGuids: guids
536 }
537 // console.log(params)
538 // return
539 deleteStandardCodeFieldsData(guids).then((res: any) => {
540 if (res.code == proxy.$passCode) {
541 getFirstPageData();
542 ElMessage({
543 type: "success",
544 message: "删除成功",
545 });
546 } else {
547 ElMessage({
548 type: "error",
549 message: res.msg,
550 });
551 }
552 });
553 }
554 });
555 };
556
557 const dialogBtnClick = (btn, info) => {
558 if (btn.value == 'submit') {
559 // dialogInfo.value.footer.btns.map((item: any) => item.disabled = true)
560 importData()
561 } else if (btn.value == 'cancel') {
562 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
563 nextTick(() => {
564 dialogInfo.value.visible = false;
565 })
566 }
567 };
568
569 defineExpose({
570 standardGuid,
571 standardName,
572 getFirstPageData,
573 checkSave
574 });
575
576 </script>
577
578 <template>
579 <div class="container_wrap full flex">
580 <div class="main_wrap">
581 <div class="table_tool_wrap">
582 <div class="tools_btns">
583 <el-button type="primary" @click="toolBtnClick({ value: 'add_row' })" v-preReClick :disabled="loading">新增行</el-button>
584 <el-button type="primary" plain @click="toolBtnClick({ value: 'submit' })" v-preReClick :disabled="saveDisabled || loading">保存数据</el-button>
585 <el-button @click="batching('delete')" v-preReClick :disabled="loading">批量删除</el-button>
586 <el-button @click="toolBtnClick({ value: 'import' })" v-preReClick :disabled="loading">导入数据</el-button>
587 <el-button @click="toolBtnClick({ value: 'export' })" v-preReClick :disabled="loading">导出数据</el-button>
588 <el-button @click="getFirstPageData" v-preReClick :disabled="loading">刷新结果</el-button>
589 </div>
590 <!-- <el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="输入名称搜索" :suffix-icon="Search" clearable
591 @change="val => toSearch({})" /> -->
592 </div>
593 <div class="table_panel_wrap full">
594 <Table
595 ref="tableEl"
596 :tableInfo="tableInfo"
597 @tableBtnClick="tableBtnClick"
598 @tableSelectionChange="tableSelectionChange"
599 @tablePageChange="tablePageChange"
600 />
601 </div>
602 </div>
603
604 <!-- <Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @onUpload="onUpload" @uploadBtnClick="uploadBtnClick" /> -->
605 </div>
606 </template>
607
608 <style lang="scss" scoped>
609 .container_wrap {
610 width: calc(100% - 200px);
611
612 .main_wrap {
613 width: 100%;
614
615 .table_panel_wrap {
616 height: calc(100% - 44px);
617 }
618 }
619 }
620 </style>
1 <script lang="ts" setup name="Drawer">
2 import { computed } from "vue";
3 import { ClickOutside as vClickOutside } from "element-plus";
4 import type { FormInstance } from "element-plus";
5 import { Download, Upload, CirclePlus } from "@element-plus/icons-vue";
6 import Table from "@/components/Table/index.vue";
7 import Form from "@/components/Form/index.vue";
8 import Tree from "@/components/Tree/index.vue";
9 import ListPanel from "@/components/ListPanel/index.vue";
10 import UploadFiles from "@/components/Upload/index.vue";
11
12 const emits = defineEmits([
13 "drawerBtnClick",
14 "radioGroupChange",
15 "drawerSelectChange",
16 "drawerTableSelectChange",
17 "drawerTableBtnClick",
18 "drawerTableToolBtnClick",
19 "drawerFormBtnClick",
20 "listItemClick",
21 "drawerTableInputChange",
22 "drawerToolBtnClick",
23 "drawerTableSelectionChange",
24 "drawerTablePageChange",
25 "uploadBtnClick",
26 "beforeUPload",
27 "uploadFile",
28 "onUpload",
29 "onClickOutside",
30 ]);
31
32 const props = defineProps({
33 drawerInfo: {
34 type: Object,
35 default: {},
36 },
37 });
38
39 const drawerTableRef = ref();
40 const drawerFormRef = ref();
41 const selectRowData = ref([]);
42 const formListRef = ref();
43 const formTreeRef = ref();
44 const uploadRef = ref();
45
46 const drawerVisible = computed(() => {
47 nextTick(() => {
48 if (props.drawerInfo.visible) {
49 drawerOpen();
50 }
51 })
52 return props.drawerInfo.visible;
53 });
54 const drawerDirection = computed(() => {
55 return props.drawerInfo.direction;
56 });
57 const drawerModal = computed(() => {
58 return props.drawerInfo.modal ?? true;
59 });
60 const modalClose = computed(() => {
61 return props.drawerInfo.modalClose ?? false;
62 });
63 const drawerModalClass = computed(() => {
64 nextTick(() => {
65 drawerOpen();
66 })
67 return props.drawerInfo.modalClass ?? "";
68 });
69 const drawerSize = computed(() => {
70 return props.drawerInfo.size ?? "30%";
71 });
72 const drawerType = computed(() => {
73 return props.drawerInfo.type ?? "form";
74 });
75 const drawerTitle = computed(() => {
76 return props.drawerInfo.header.title;
77 });
78 const selectOptions = computed(() => {
79 return props.drawerInfo.container.relateOptions ?? [];
80 });
81 const formItems = computed(() => {
82 return props.drawerInfo.container.formItems ?? [];
83 });
84 const formRules = computed(() => {
85 return props.drawerInfo.container.rules ?? {};
86 });
87 const selectData = computed(() => {
88 return props.drawerInfo.container.selectData ?? [];
89 });
90 const contents = computed(() => {
91 return props.drawerInfo.container.contents ?? [];
92 });
93 const footer = computed(() => {
94 return props.drawerInfo.footer;
95 });
96
97 const onClickOutside = (e: any) => {
98 emits("onClickOutside");
99 };
100
101 const getDrawerConRef = (refName) => {
102 console.log(refName, '----------')
103 if (refName == 'drawerTableRef') {
104 const dtf = drawerTableRef.value[0] || drawerTableRef.value
105 return dtf?.tableRef
106 }
107 // const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
108 if (refName == 'drawerFormRef') {
109 const drawerForm = drawerFormRef.value?.[0] || drawerFormRef.value;
110 return drawerForm
111 }
112 }
113
114 defineExpose({
115 // selections,
116 getDrawerConRef
117 });
118
119 const toolBtnClick = (btn, type: any = null) => {
120 if (type && type == "table") {
121 let selectData = [];
122 if (drawerTableRef.value) {
123 const drawerTable = drawerTableRef.value[0] || drawerTableRef.value;
124 selectData = drawerTable.tableRef.getSelectionRows();
125 }
126 emits("drawerTableToolBtnClick", btn, selectData);
127 } else {
128 emits("drawerToolBtnClick", btn);
129 }
130 };
131
132 const tableSelectionChange = (val, tId) => {
133 selectRowData.value = val;
134 emits("drawerTableSelectionChange", val, tId);
135 };
136
137 const tableSelectChange = (val, scope) => {
138 emits("drawerTableSelectChange", val, scope);
139 };
140
141 const tableInputChange = (val, scope) => {
142 emits("drawerTableInputChange", val, scope);
143 };
144
145 const tablePageChange = (info) => {
146 emits("drawerTablePageChange", info);
147 }
148
149 const tableBtnClick = (scope, btn) => {
150 emits("drawerTableBtnClick", scope, btn);
151 };
152
153 const submitForm = async (formEl: FormInstance | undefined, btn) => {
154 if (!formEl) return;
155 await formEl.validate((valid, fields) => {
156 const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
157 if (valid) {
158 const formInfo = drawerForm.formInline;
159 emits("drawerBtnClick", btn, formInfo);
160 } else {
161 var obj = fields && Object.keys(fields);
162 obj?.[0] && formEl?.scrollToField(obj?.[0])
163 console.log("error submit!", fields);
164 }
165 });
166 };
167
168 const btnClick = (btn, type) => {
169 if (btn.disabled) return;
170 if (
171 btn.value != "cancel" &&
172 (btn.value.indexOf("submit") > -1 || btn.value.indexOf("save") > -1)
173 ) {
174 if (drawerFormRef.value) {
175 const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
176 const formRef = drawerForm.ruleFormRef;
177 submitForm(formRef, btn);
178 } else {
179 emits("drawerBtnClick", btn);
180 }
181 } else {
182 emits("drawerBtnClick", btn);
183 }
184 };
185
186 const radioGroupChange = (val, info) => {
187 emits("radioGroupChange", val, info);
188 };
189
190 const formSelectChange = (val, row, info) => {
191 emits("drawerSelectChange", val, row, info);
192 };
193
194 const formBtnClick = (btn) => {
195 emits("drawerFormBtnClick", btn);
196 };
197
198 const listItemClick = (row) => {
199 emits("listItemClick", row);
200 };
201
202 const onUpload = (file, fileList) => {
203 emits("onUpload", file, fileList);
204 };
205
206 const beforeUPload = (file) => {
207 emits("beforeUPload", file);
208 };
209
210 const uploadFile = (file) => {
211 emits("uploadFile", file);
212 };
213
214 const uploadBtnClick = (btn) => {
215 emits("uploadBtnClick", btn);
216 };
217
218 const drawerOpen = () => {
219 const wrap: any = drawerModalClass.value && document.getElementsByClassName(drawerModalClass.value)[0];
220 if (wrap) {
221 wrap.style.width = `${drawerSize.value + 1}px`
222 } else {
223 const dom: any = document.getElementsByClassName('el-overlay')[0];
224 dom && (dom.style.width = '100%');
225 }
226 };
227
228 const drawerClose = () => {
229 btnClick({ value: "cancel" }, null);
230 if (drawerFormRef.value) {
231 const drawerForm = drawerFormRef.value[0] || drawerFormRef.value;
232 const formRef = drawerForm.ruleFormRef;
233 drawerForm?.resetForm(formRef);
234 }
235 };
236 </script>
237
238 <template>
239 <el-drawer v-model="drawerVisible" :direction="drawerDirection" :size="drawerSize" :modal="drawerModal"
240 :close-on-click-modal="modalClose" :close-on-press-escape="modalClose" :modal-class="drawerModalClass"
241 destroy-on-close :z-index="props.drawerInfo.zIndex ?? null" @close="drawerClose">
242 <template #header>
243 <span class="title">{{ drawerTitle }}</span>
244 </template>
245 <template #default>
246 <div class="drawer-body-loading" v-if="drawerInfo.loading ?? false" v-loading="drawerInfo.loading ?? false"></div>
247 <div v-else class="drawer_panel" :class="[con.col]" :style="con.style" v-for="con in contents">
248 <div class="panel_title" v-if="con.title">{{ con.title }}</div>
249 <template v-if="con.type && con.type.indexOf('table') > -1">
250 <div class="table_tool float-right">
251 <template v-for="bar in con.tableTool.btns.slice(1)">
252 <el-popover v-if="bar.popover" :visible="bar.popover.visible" :title="bar.popover.title"
253 :popper-class="bar.popover.class ?? ''" placement="bottom-start" :width="bar.popover.width ?? 200"
254 trigger="click">
255 <template #reference>
256 <el-button :type="bar.type" @click="toolBtnClick(bar, con.type)" v-click-outside="onClickOutside"
257 v-preReClick>{{ bar.label }}</el-button>
258 </template>
259 <template #default>
260 <span v-html="bar.popover.content"></span>
261 </template>
262 </el-popover>
263 <el-button :type="bar.type" :plain="bar.plain" v-else @click="toolBtnClick(bar, 'table')" v-preReClick>{{
264 bar.label
265 }}</el-button>
266 </template>
267 </div>
268 <div class="table_panel_wrap" :style="con.tableInfo.style">
269 <Table ref="drawerTableRef" :class="[con.tableInfo.col]" :tableInfo="con.tableInfo"
270 @tableSelectChange="tableSelectChange" @tableBtnClick="tableBtnClick"
271 @tableSelectionChange="tableSelectionChange" @tableInputChange="tableInputChange"
272 @tablePageChange="tablePageChange" />
273 </div>
274 <div class="table_tool" :class="[con.tableTool.col]" v-if="con.tableTool && (con.tableTool.visible ?? true)">
275 <template v-for="bar in con.tableTool.btns.slice(0, 1)">
276 <!-- <el-popover v-if="bar.popover" :visible="bar.popover.visible" :title="bar.popover.title"
277 :popper-class="bar.popover.class ?? ''" placement="bottom-start" :width="bar.popover.width ?? 200"
278 trigger="click">
279 <template #reference>
280 <el-button :type="bar.type" @click="toolBtnClick(bar, con.type)" v-click-outside="onClickOutside"
281 v-preReClick>{{ bar.label }}</el-button>
282 </template>
283 <template #default>
284 <span v-html="bar.popover.content"></span>
285 </template>
286 </el-popover> -->
287 <!-- <el-button :type="bar.type" :plain="bar.plain" @click="toolBtnClick(bar, 'table')" v-preReClick>{{
288 bar.label
289 }}</el-button> -->
290 <div class="operation-icon" @click="toolBtnClick(bar, 'table')" v-preReClick>
291 <el-icon color="#4fa1a4">
292 <CirclePlus />
293 </el-icon>
294 <span>{{ bar.label }}</span>
295 </div>
296 </template>
297 </div>
298 </template>
299 <template v-else-if="con.type && con.type.indexOf('tree') > -1">
300 <div class="list_tree" v-if="con.type.indexOf('list') > -1">
301 <ListPanel ref="formListRef" :listInfo="con.listInfo" @itemClick="listItemClick" />
302 <Tree ref="formTreeRef" :treeInfo="con.treeInfo" />
303 </div>
304 <Tree ref="formTreeRef" :treeInfo="con.treeInfo" v-else />
305 </template>
306 <template v-else-if="con.type && con.type == 'field-list'">
307 <div class="field_list_panel" v-if="con.listInfo.data.length > 0">
308 <div class="list_item" v-for="item in con.listInfo.data">
309 <span class="item_field">{{ item[con.listInfo.field] }}</span>
310 <span class="item_text">{{ item[con.listInfo.label] }}</span>
311 </div>
312 </div>
313 <div class="field_list_panel">
314 <div class="empty_tips">暂无数据</div>
315 </div>
316 </template>
317 <template v-else-if="con.type && con.type.indexOf('upload') > -1">
318 <div class="upload_tool">
319 <UploadFiles ref="uploadRef" :upload-info="con.uploadInfo" @onUpload="onUpload" @beforeUPload="beforeUPload"
320 @uploadFile="uploadFile" @uploadBtnClick="uploadBtnClick" />
321 <div class="tool_btns" v-if="con.tools && con.tools.visible">
322 <template v-for="btn in con.tools.btns">
323 <el-button v-if="btn.visible ?? true" :type="btn.type" :plain="btn.plain"
324 @click="toolBtnClick(btn, 'table')" v-preReClick>
325 <el-icon v-if="btn.icon && btn.icon == 'Upload'">
326 <Upload />
327 </el-icon>
328 <el-icon v-else-if="btn.icon && btn.icon == 'Download'">
329 <Download />
330 </el-icon>
331 <span>{{ btn.label }}</span>
332 </el-button>
333 </template>
334 </div>
335 </div>
336 <div class="upload_table_panel_wrap" v-if="con.tableInfo && Object.keys(con.tableInfo).length">
337 <Table ref="drawerTableRef" :class="[con.tableInfo.col]" :tableInfo="con.tableInfo"
338 @tableSelectChange="tableSelectChange" @tableBtnClick="tableBtnClick"
339 @tableSelectionChange="tableSelectionChange" @tableInputChange="tableInputChange"
340 @tablePageChange="tablePageChange" />
341 </div>
342 </template>
343 <template v-else>
344 <Form ref="drawerFormRef" :itemList="con.formInfo.items" :formId="con.formInfo.id" :rules="con.formInfo.rules"
345 :col="con.formInfo.col" :readonly="con.formInfo.readonly" @radioGroupChange="radioGroupChange"
346 @selectChange="formSelectChange" @btnClick="formBtnClick">
347 </Form>
348 <!-- 插槽内容 -->
349 <slot></slot>
350 </template>
351 </div>
352 </template>
353 <template #footer v-if="footer.visible ?? true">
354 <div style="flex: auto">
355 <template v-for="btn in footer.btns">
356 <el-button v-if="btn.visible ?? true" :type="btn.type" :disabled="btn.disabled ?? false"
357 @click="btnClick(btn, null)" v-preReClick :loading="btn.loading ?? false">{{ btn.label }}</el-button>
358 </template>
359 </div>
360 </template>
361 </el-drawer>
362 </template>
363
364 <style lang="scss" scoped>
365 .operation-icon {
366 display: flex;
367 align-items: center;
368 cursor: pointer;
369 > span {
370 margin-left: 4px;
371 color: #4fa1a4;
372 }
373 }
374 .drawer-body-loading {
375 height: 100%;
376 }
377
378 .drawer_panel {
379 position: relative;
380
381 &.no-margin {
382 margin-top: 0;
383 }
384
385 &.height_auto {
386 .table_panel_wrap {
387 .table_panel {
388 min-height: unset;
389 }
390 }
391 }
392 }
393
394 .panel_title {
395 line-height: 40px;
396 font-size: 14px;
397 color: var(--el-color-regular);
398 font-weight: 600;
399 }
400
401 .table_tool {
402 height: 40px;
403 display: flex;
404 align-items: center;
405
406 &.float-right {
407 position: absolute;
408 top: 0;
409 right: 0;
410 }
411 }
412
413 .table_panel_wrap {
414 height: auto!important;
415 .table_panel {
416 padding: 0;
417 min-height: auto;
418 &.auto-height {
419 min-height: unset;
420 }
421 }
422 }
423
424 .list_tree {
425 max-height: 400px;
426 display: flex;
427 justify-content: space-between;
428 box-shadow: 0 0 0 1px #d9d9d9;
429
430 .list_panel_wrap {
431 width: 158px;
432 box-shadow: 1px 0 0 0 #d9d9d9;
433 }
434
435 .tree_panel {
436 width: calc(100% - 159px);
437
438 :deep(.el-tree) {
439 margin: 0;
440 height: 100%;
441 overflow: hidden auto;
442 }
443 }
444 }
445
446 .field_list_panel {
447 width: 100%;
448 height: calc(100vh - 70px);
449 position: relative;
450
451 .list_item {
452 display: flex;
453 font-size: 12px;
454 color: var(--el-color-regular);
455 margin: 4px 0;
456
457 .item_field {
458 width: 130px;
459 color: var(--el-text-color-regular);
460 }
461
462 .item_text {
463 width: calc(100% - 130px);
464 word-wrap: break-word;
465 }
466 }
467
468 .empty_tips {
469 text-align: center;
470 color: var(--el-disabled-text-color);
471 position: absolute;
472 top: 50px;
473 left: 50%;
474 transform: translate(-50%);
475 }
476 }
477
478 .upload_tool {
479 margin-bottom: 8px;
480 position: relative;
481
482 .tool_btns {
483 position: absolute;
484 top: 0;
485 right: 0;
486 }
487 }
488
489 .upload_table_panel_wrap {
490 .table_panel {
491 min-height: unset;
492 }
493 }
494 </style>
1 <script lang="ts" setup name="existingTableSelect">
2 import { ref } from "vue";
3 import Dialog from "@/components/Dialog/index.vue";
4
5 const emits = defineEmits([
6 "expandValueChange"
7 ]);
8
9 const props = defineProps({
10 tableCreateInfo: {
11 type: Object,
12 default: {},
13 },
14 partitionAttribute: {
15 type: Object,
16 default: {},
17 },
18 isLook: {
19 type: Boolean,
20 default: false
21 }
22 });
23
24 const expandProperties: any = computed(() => {
25 let partitionAttribute = props.tableCreateInfo.partitionAttribute;
26 return {
27 partitionMode: partitionAttribute?.partitionMode || 'dynamic',
28 staticPartitionType: partitionAttribute?.staticPartitionType || 'Range',
29 partitionCol: partitionAttribute?.partitionCol || "",
30 partitionTimeUnit: partitionAttribute?.partitionTimeUnit || "DAY",
31 dynamicPartitionEnd: partitionAttribute?.dynamicPartitionEnd == null ? 3 : partitionAttribute?.dynamicPartitionEnd,
32 staticPartitionRange: partitionAttribute?.staticPartitionRangeBegin ? [partitionAttribute?.staticPartitionRangeBegin,
33 partitionAttribute?.staticPartitionRangeEnd] : null,
34 dynamicPartitionHistory: partitionAttribute?.dynamicPartitionHistory === "Y",
35 dynamicPartitionHistoryNum: partitionAttribute?.dynamicPartitionHistoryNum,
36 staticPartitionEnum: partitionAttribute?.staticPartitionEnum,
37 }
38 })
39
40 const formItems: any = ref([
41 {
42 label: "分区模式",
43 type: "radio-panel",
44 placeholder: "",
45 field: "partitionMode",
46 default: "dynamic",
47 options: [
48 { label: "动态分区", value: "dynamic" },
49 { label: "静态分区", value: "static" },
50 ],
51 children: [],
52 required: true,
53 block: true,
54 col: "no-wrap col2",
55 },
56 {
57 label: "分区类型",
58 type: "select",
59 placeholder: "请选择",
60 options: [
61 {
62 label: "Range",
63 value: "Range",
64 },
65 {
66 label: "List",
67 value: "List",
68 },
69 ],
70 field: "staticPartitionType",
71 default: 'Range',
72 required: true,
73 visible: false,
74 },
75 {
76 label: "分区字段",
77 type: "select",
78 placeholder: "请选择",
79 options: [],
80 field: "partitionCol",
81 default: "",
82 props: {
83 label: 'chName',
84 value: 'enName'
85 },
86 tooltip: true,
87 tooltipContent: '分区字段为日期、日期时间类型且为主键的字段。',
88 clearable: true,
89 required: true,
90 visible: true,
91 },
92 {
93 label: "分区单位",
94 type: "select",
95 placeholder: "请选择",
96 options: [
97 {
98 value: "DAY",
99 label: "按天",
100 },
101 {
102 value: "WEEK",
103 label: "按星期",
104 },
105 {
106 value: "MONTH",
107 label: "按月",
108 },
109 {
110 value: "YEAR",
111 label: "按年",
112 },
113 ],
114 default: "DAY",
115 field: "partitionTimeUnit",
116 required: true,
117 visible: false,
118 },
119 {
120 type: "select-group",
121 field: "dynamicPartitionTimeUnit",
122 children: [
123 {
124 label: "分区单位",
125 type: "select",
126 placeholder: "请选择",
127 options: [
128 {
129 value: "HOUR",
130 label: "按小时",
131 },
132 {
133 value: "DAY",
134 label: "按天",
135 },
136 {
137 value: "WEEK",
138 label: "按星期",
139 },
140 {
141 value: "MONTH",
142 label: "按月",
143 },
144 {
145 value: "YEAR",
146 label: "按年",
147 },
148 ],
149 default: "DAY",
150 field: "partitionTimeUnit",
151 required: true,
152 visible: true,
153 },
154 {
155 label: " ",
156 type: "input",
157 placeholder: "结束偏移量",
158 default: 3,
159 field: "dynamicPartitionEnd",
160 required: true,
161 visible: true,
162 },
163 ],
164 col: "col2",
165 visible: true
166 },
167 {
168 label: "分区范围",
169 type: "date-picker",
170 field: "staticPartitionRange",
171 default: null,
172 placeholder: "开始时间~截止时间",
173 clearable: true,
174 required: true,
175 visible: false,
176 },
177 {
178 type: "checkbox-input",
179 placeholder: "创建历史分区",
180 field: "dynamicPartitionHistory",
181 default: false,
182 children: [
183 {
184 label: "",
185 type: "input",
186 placeholder: "历史分区数量",
187 field: "dynamicPartitionHistoryNum",
188 default: 1,
189 required: false,
190 visible: false,
191 },
192 ],
193 class: "dialog-checkbox-input",
194 visible: true,
195 required: false,
196 },
197 {
198 label: "分区枚举值",
199 type: "textarea",
200 placeholder: "请使用“,”号分隔",
201 field: "staticPartitionEnum",
202 default: "",
203 clearable: true,
204 required: true,
205 block: true,
206 visible: false,
207 },
208 ]);
209 const formRules = ref({
210 staticPartitionType: [
211 {
212 validator: (rule: any, value: any, callback: any) => {
213 if (!value) {
214 callback(new Error("分区类型不为空"));
215 } else {
216 callback();
217 }
218 },
219 trigger: "blur",
220 },
221 ],
222 partitionCol: [{
223 validator: (rule: any, value: any, callback: any) => {
224 if (!value) {
225 callback(new Error("分区字段不为空"));
226 } else {
227 callback();
228 }
229 },
230 trigger: "blur",
231 }],
232 staticPartitionRange: [{
233 validator: (rule: any, value: any, callback: any) => {
234 if (!value?.length) {
235 callback(new Error("分区范围不为空"));
236 } else {
237 callback();
238 }
239 },
240 trigger: "blur",
241 }],
242 staticPartitionEnum: [{
243 trigger: "blur",
244 required: true,
245 message: '分区枚举值不能为空'
246 }],
247 dynamicPartitionHistoryNum: {
248 validator: (rule: any, value: any, callback: any) => {
249 const r = /(^[0-9]([0-9]*)$|^[0-9]$)/; // 正整数(可以以0打头)
250 if (value && !r.test(value)) {
251 callback(new Error('请填写大于或等于零整数'));
252 return;
253 }
254 if ((value + '').length > 6) {
255 callback(new Error('请填写小于7位的整数'));
256 return;
257 }
258 callback();
259 },
260 trigger: "blur",
261 },
262 dynamicPartitionEnd: {
263 validator: (rule: any, value: any, callback: any) => {
264 const r = /(^[0-9]([0-9]*)$|^[0-9]$)/; // 正整数(可以以0打头)
265 if (value && !r.test(value)) {
266 callback(new Error('请填写大于或等于零整数'));
267 return;
268 }
269 if ((value + '').length > 6) {
270 callback(new Error('请填写小于7位的整数'));
271 return;
272 }
273 callback();
274 },
275 trigger: "blur",
276 },
277 });
278
279 const expandPropertyDialogInfo = ref({
280 readonly: false,
281 visible: false,
282 size: 700,
283 height: "270px",
284 header: {
285 title: "扩展属性",
286 },
287 direction: "column",
288 type: "",
289 contents: [
290 {
291 type: "form",
292 title: "",
293 formInfo: {
294 readonly: false,
295 id: "edit-expand-property",
296 items: formItems.value,
297 rules: formRules.value,
298 },
299 },
300 ],
301 footer: {
302 visible: true,
303 btns: [
304 { type: "default", label: "取消", value: "cancel" },
305 { type: "primary", label: "确定", value: "submit" },
306 ],
307 },
308 });
309
310 /** 记录对话框编辑过程中的扩展属性。提交之后才会记录在expandProperties */
311 const editExpandProperties: any = ref({});
312
313 /** 扩展属性弹出对话框 */
314 const handleClickExpand = () => {
315 expandPropertyDialogInfo.value.visible = true;
316 if (props.isLook || props.tableCreateInfo.isCreate) {
317 expandPropertyDialogInfo.value.contents[0].formInfo.readonly = true;
318 expandPropertyDialogInfo.value.footer.visible = false;
319 } else {
320 expandPropertyDialogInfo.value.contents[0].formInfo.readonly = false;
321 expandPropertyDialogInfo.value.footer.visible = true;
322 }
323 if (expandProperties.value.partitionMode === 'dynamic') {
324 formItems.value[1].visible = false;
325 formItems.value[2].visible = true;
326 formItems.value[3].visible = false;
327 formItems.value[4].visible = true;
328 formItems.value[5].visible = false;
329 formItems.value[6].visible = true;
330 formItems.value[7].visible = false;
331 formItems.value[6].children[0].visible = expandProperties.value.dynamicPartitionHistory;
332 formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y' && (field.dataType === 'date' || field.dataType === 'datetime')) || [];
333 formItems.value[2].tooltipContent = '分区字段为日期、日期时间类型且为主键的字段。';
334 } else {
335 formItems.value[1].visible = true;
336 formItems.value[2].visible = true;
337 formItems.value[3].visible = true;
338 formItems.value[4].visible = false;
339 formItems.value[5].visible = true;
340 formItems.value[6].visible = false;
341 formItems.value[7].visible = false;
342 let val = expandProperties.value.staticPartitionType;
343 formItems.value[3].visible = val !== 'List';
344 formItems.value[5].visible = val !== 'List';
345 formItems.value[7].visible = val === 'List';
346 if (val !== 'List') {
347 formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y' && (field.dataType === 'date' || field.dataType === 'datetime')) || [];
348 formItems.value[2].tooltipContent = '分区字段为日期、日期时间类型且为主键的字段。';
349 } else {
350 formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y') || [];
351 formItems.value[2].tooltipContent = '分区字段为主键字段。';
352 }
353 }
354 expandPropertyDialogInfo.value.contents[0].formInfo.items = formItems.value;
355 editExpandProperties.value = Object.assign({}, expandProperties.value);
356 setFormItems(editExpandProperties.value);
357 };
358
359 /** 重置formItems的值 */
360 const setFormItems = (row: any = null) => {
361 formItems.value.forEach(item => {
362 if (item.field === 'dynamicPartitionTimeUnit') {
363 item.children.forEach(child => {
364 child.default = row[child.field];
365 });
366 } else if (item.field === 'dynamicPartitionHistory') {
367 item.default = row[item.field];
368 item.children.forEach(child => {
369 child.default = row[child.field];
370 });
371 } else {
372 item.default = row[item.field];
373 }
374 })
375 }
376
377 const radioGroupChange = (val, info) => {
378 formItems.value[0].default = val;
379 if (val == "dynamic") {
380 formItems.value[1].visible = false;
381 formItems.value[2].visible = true;
382 formItems.value[3].visible = false;
383 formItems.value[4].visible = true;
384 formItems.value[5].visible = false;
385 formItems.value[6].visible = true;
386 formItems.value[7].visible = false;
387 } else if (val == "static") {
388 formItems.value[1].visible = true;
389 formItems.value[2].visible = true;
390 formItems.value[3].visible = true;
391 formItems.value[4].visible = false;
392 formItems.value[5].visible = true;
393 formItems.value[6].visible = false;
394 formItems.value[7].visible = false;
395 }
396 expandPropertyDialogInfo.value.contents[0].formInfo.items = formItems.value;
397 editExpandProperties.value = Object.assign({}, editExpandProperties.value, info)
398 setFormItems(editExpandProperties.value);
399 };
400
401 const dialogCheckboxChange = (val, info) => {
402 let opts: any = formItems.value[6].children;
403 opts[0].visible = val;
404 editExpandProperties.value = Object.assign({}, editExpandProperties.value, info)
405 setFormItems(editExpandProperties.value);
406 };
407
408 const dialogSelectChange = (val, row, info) => {
409 if (row.field == 'staticPartitionType') {
410 formItems.value[3].visible = val !== 'List';
411 formItems.value[5].visible = val !== 'List';
412 formItems.value[7].visible = val === 'List';
413 if (val !== 'List') {
414 formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y' && (field.dataType === 'date' || field.dataType === 'datetime')) || [];
415 formItems.value[2].tooltipContent = '分区字段为日期、日期时间类型且为主键的字段。';
416 } else {
417 formItems.value[2].options = props.tableCreateInfo.tableFields?.filter(field => field.isPrimary === 'Y') || [];
418 formItems.value[2].tooltipContent = '分区字段为主键字段。';
419 }
420 }
421 editExpandProperties.value = Object.assign({}, editExpandProperties.value, info)
422 setFormItems(editExpandProperties.value);
423 }
424
425 const expandPropertyDialogBtnClick = (btn, info) => {
426 if (btn.value == 'submit') {
427 emits('expandValueChange', info);
428 expandPropertyDialogInfo.value.visible = false;
429 } else if (btn.value == 'cancel') {
430 expandPropertyDialogInfo.value.visible = false;
431 }
432 };
433
434 defineExpose({
435 handleClickExpand
436 });
437
438 </script>
439
440 <template>
441 <Dialog ref="expandPropertyDialogRef" :dialogInfo="expandPropertyDialogInfo" @radioGroupChange="radioGroupChange"
442 @checkboxChange="dialogCheckboxChange" @selectChange="dialogSelectChange" @btnClick="expandPropertyDialogBtnClick" />
443 </template>
444
445 <style scoped lang="scss"></style>
446
1 <template>
2 <el-drawer
3 v-model="visible"
4 :title="title"
5 size="760px"
6 class="standard-modal"
7 :close-on-click-modal="false"
8 >
9 <el-form
10 :rules="formRules" :model="form" ref="formEl"
11 require-asterisk-position="right"
12 v-loading="loading"
13 >
14 <el-row>
15 <el-col :span="12" style="padding-right:10px">
16 <el-form-item label="元数据标准名称" prop="standardName">
17 <el-input v-model="form.standardName" placeholder="请输入" maxlength="50"/>
18 </el-form-item>
19 </el-col>
20 <el-col :span="12" style="padding-right:10px">
21 <el-form-item label="上级标准" prop="parentGuid">
22 <el-tree-select
23 v-model="form.parentGuid"
24 :data="treeSelectData"
25 :props="standardProps"
26 node-key="guid"
27 :highlight-current="true"
28
29 check-strictly
30 placeholder="请选择"
31 :disabled="treeSelectDisabled"
32 clearable
33 @check="parentGuidCheck"
34 @clear="parentGuidClear"
35 @nodeClick="parentGuidCheck"
36 />
37 <!-- <el-cascader
38 v-model="form.parentGuid"
39 :options="standardOptions"
40 :props="standardProps"
41 :show-all-levels="false"
42 style="width:100%"
43 clearable
44 @change="parentGuidChange"
45 /> -->
46 </el-form-item>
47 </el-col>
48 <el-col :span="12" style="padding-right:10px">
49 <el-form-item label="标准编号" prop="standardCode">
50 <el-input v-model="form.standardCode" placeholder="请输入" maxlength="20"/>
51 </el-form-item>
52 </el-col>
53 <el-col :span="12" style="padding-right:10px">
54 <el-form-item label="排序" prop="orderNum">
55 <el-input v-model="form.orderNum" placeholder="请输入" type="number" maxlength="2"/>
56 </el-form-item>
57 </el-col>
58 <el-col :span="12" style="padding-right:10px">
59 <el-form-item label="发布单位" prop="publishingUnitCode">
60 <el-select v-model="form.publishingUnitCode" placeholder="请选择">
61 <el-option v-for="item in publishUnitOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
62 </el-select>
63 </el-form-item>
64 </el-col>
65
66 <el-col :span="24">
67 <el-form-item label="描述" prop="description">
68 <el-input
69 v-model="form.description"
70 placeholder="请输入"
71 type="textarea" maxlength="200"
72 rows="4"
73 show-word-limit
74 />
75 </el-form-item>
76 </el-col>
77 <el-col :span="24">
78 <el-form-item label="自定义字段选择" v-if="fieldsTableShow" required>
79 <div class="table-form">
80 <div class="table-form-wrapper" v-for="item,index in form.fieldRQVOS" :key="index">
81 <div class="table-form-item">
82 <el-select v-model="item.fileNameCode" style="width:160px" filterable clearable :disabled="fieldsDisabled">
83 <el-option v-for="item in fieldOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
84 </el-select>
85 </div>
86 <div class="table-form-item">
87 <el-select v-model="item.isNotnull" style="width: 96px" placeholder="是否必填" clearable :disabled="fieldsDisabled">
88 <el-option v-for="item in isBooleanOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
89 </el-select>
90 </div>
91 <div class="table-form-item">
92 <el-select v-model="item.isDisplay" style="width:96px" placeholder="是否展示" clearable :disabled="fieldsDisabled">
93 <el-option v-for="item in isBooleanOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
94 </el-select>
95 </div>
96 <div class="table-form-item">
97 <el-select v-model="item.inputTypeCode" style="width:130px" clearable :disabled="fieldsDisabled"
98 @change="v => inputTypeChange(v, item)">
99 <el-option v-for="item in inputOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
100 </el-select>
101 </div>
102 <div class="table-form-item">
103 <el-select
104 v-if="item.inputTypeCode == 2"
105 v-model="item.dataTypeCode"
106 style="width:160px" filterable clearable
107 :disabled="fieldsDisabled">
108 <el-option v-for="item in allDictOptions" :label="item.dictTypeName" :value="item.dictTypeName" :key="item.guid"></el-option>
109 </el-select>
110 <el-input
111 v-else-if="item.inputTypeCode == 3"
112 v-model="item.validateExpression"
113 :disabled="fieldsDisabled"
114 placeholder="请输入"
115 ></el-input>
116 </div>
117 <div class="table-form-operation" v-if="!fieldsDisabled">
118 <!-- <el-icon color="#4fa1a4" @click="() => addTableItem(index)">
119 <CirclePlus />
120 </el-icon> -->
121 <el-icon color="#b2b2b2" @click="() => deleteTableItem(index)" :size="22" class="custom-icon" style="margin-top:3px"><Delete /></el-icon>
122 </div>
123 </div>
124 <div class="table-form-add" v-if="!fieldsDisabled">
125 <el-icon color="#4fa1a4" @click="() => addTableItem(index)" :size="20" class="custom-icon">
126 <CirclePlus />
127 </el-icon>
128 <span @click="() => addTableItem(index)" style="cursor: pointer;">添加字段</span>
129 </div>
130 </div>
131 </el-form-item>
132 </el-col>
133 </el-row>
134 </el-form>
135 <template #footer>
136 <div style="text-align:right">
137 <el-button @click="visible = false" :disabled="confirmLoading">取消</el-button>
138 <el-button type="primary" @click="confirm" :loading="confirmLoading">确认</el-button>
139 </div>
140 </template>
141
142 </el-drawer>
143 </template>
144
145 <script setup lang="ts">
146 // import { cloneDeep } from 'lodesh-es'
147 import { watch } from 'vue'
148 import { Search, CirclePlus, Delete } from '@element-plus/icons-vue'
149 import { saveMetaStandard, deleteMetaStandard,
150 updateMetaStandard, getMetaStandardDetail,
151 getMetaStandardTree
152 } from '@/api/modules/dataMetaService'
153 import {
154 getParamsList, getDictAllList
155 } from '@/api/modules/dataAsset'
156 import { ElMessage } from "element-plus";
157
158 const { proxy } = getCurrentInstance() as any;
159
160 const props = defineProps({
161 modelValue: Boolean,
162 standardOptions: {
163 type: Array,
164 default: () => ([])
165 },
166 type: {
167 type: String,
168 default: 'add'
169 },
170 guid: {
171 type: String,
172 default: ''
173 },
174 currentNode: {
175 type: Object,
176 default: () => ({})
177 }
178 })
179 const emit = defineEmits(['update:modelValue', 'success', 'confirm']);
180 const visible = computed({
181 get() {
182 return props.modelValue;
183 },
184 set(val) {
185 emit('update:modelValue', val);
186 }
187 })
188 const title = computed(() => {
189 return props.type === 'add' ? '新增元数据标准' : '编辑元数据标准'
190 })
191
192 /**
193 * 配置项列表
194 */
195 const publishUnitOptions = ref([])
196 const standardProps = {
197 label: 'standardName',
198 value: 'guid',
199 checkStrictly: true,
200 emitPath: false
201 }
202 const standardTreeProps = {
203 label: 'standardName',
204 value: 'guid',
205 }
206 const fieldOptions = ref([])
207 const isBooleanOptions = [
208 { label: '是', value: 'Y' },
209 { label: '否', value: 'N' },
210 ]
211 const inputOptions = ref([])
212 const allDictOptions = ref([])
213 /**表单 */
214 const formEl = ref()
215 const formTpl = {
216 standardName: '',
217 standardCode: '',
218 orderNum: '',
219 publishingUnitCode: '',
220 parentGuid: '',
221 description: '',
222 fieldRQVOS: null
223 }
224 const form = ref({...formTpl})
225 const formRules = {
226 standardName: { required: true, message: '请填写元数据标准名称' },
227 orderNum: { required: true, message: '请填写排序' },
228 publishingUnitCode: { required: true, message: '请选择发布的单位' }
229 }
230
231 function parentGuidChange (val) {
232 console.log(val)
233 if (!val) {
234 form.value.fieldRQVOS = []
235 return
236 }
237 const isFirst = props.standardOptions.find(item => item.guid === val)
238 if (isFirst) {
239 form.value.fieldRQVOS = [{...tableFormTpl}]
240 }
241 }
242 /** 上级标准树选择 */
243 const treeSelectData = ref([])
244 const treeCurrentNode = ref({})
245 const treeSelectDisabled = ref(false)
246 const fieldsDisabled = ref(false)
247 const fieldsTableShow = ref(false)
248 watch(
249 () => treeCurrentNode,
250 (currentNode:any) => {
251 console.log('currentNode', currentNode.value)
252 let { level, isHaveData } = currentNode.value
253 fieldsTableShow.value = level ? true : false
254 fieldsDisabled.value = false
255 if (!level) {
256 form.value.fieldRQVOS = null
257 return
258 }
259 if (props.type === 'add') {
260 if (level == 1) {
261 // 所选为一级节点,当前二级节点
262 form.value.fieldRQVOS = [{...tableFormTpl}]
263 }
264 if (level >= 2) {
265 // 所选为二级节点,当前三级节点以下,只读
266 fieldsDisabled.value = true
267 }
268 } else if (props.type === 'edit') {
269 if (level == 1) {
270 // 所选为一级节点,当前二级节点
271 form.value.fieldRQVOS = [{...tableFormTpl}]
272 fieldsDisabled.value = false
273 }
274 if (level >= 2) {
275 fieldsDisabled.value = true
276 }
277 }
278 },
279 { deep: true}
280 )
281 function parentGuidCheck (node, {checkedKeys}) {
282 console.log('node', node)
283 // fieldsControlDisabled.value = false
284 treeCurrentNode.value = node
285 // if (checkedKeys.length === 0) {
286 // treeCurrentNode.value = {}
287 // return
288 // }
289 if (node.level == 1) return
290
291 // 上级标准带出标准编号和发布单位
292 // loading.value = true
293 getMetaStandardDetail(node.guid).then((res:any) => {
294 if (res.code === proxy.$passCode) {
295 const data = res.data
296 if (!form.value.standardCode) {
297 form.value.standardCode = data.standardCode
298 }
299 if (!form.value.publishingUnitCode) {
300 form.value.publishingUnitCode = data.publishingUnitCode
301 }
302 form.value.fieldRQVOS = data.fieldRSVOS
303 }
304 })
305 }
306 function parentGuidClear () {
307 treeCurrentNode.value = {}
308 }
309
310 const tableFormTpl = {
311 fileNameCode: '',
312 isNotnull: 'N',
313 isDisplay: 'N',
314 inputTypeCode: '',
315 dataTypeCode: null,
316 validateExpression: null
317 }
318 function addTableItem (index) {
319 const tableObj = { ...tableFormTpl }
320 // form.value.fieldRQVOS.splice(index + 1, 0, tableObj)
321 form.value.fieldRQVOS.push(tableObj)
322 nextTick(() => {
323 let scrollBody = document.querySelector('.el-drawer__body')
324 scrollBody.scrollTop = scrollBody.scrollHeight
325 })
326 }
327 function deleteTableItem (index) {
328 form.value.fieldRQVOS.splice(index, 1)
329 }
330 function inputTypeChange (val, item) {
331 console.log(val)
332 if (val == 2) {
333 // 下拉框
334 item.validateExpression = null
335 } else if (val == 3) {
336 item.dataTypeCode = null
337 } else {
338 item.validateExpression = null
339 item.dataTypeCode = null
340 }
341 }
342
343 const loading = ref(false)
344 function getDetail () {
345 loading.value = true
346 return getMetaStandardDetail(props.guid).then((res:any) => {
347 if (res.code === proxy.$passCode) {
348 const data = res.data
349 data.fieldRQVOS = data.fieldRSVOS
350 form.value = { ...data }
351 let { level, isChildHaveData, children } = treeCurrentNode.value
352 if (level == 1) {
353 // 所选为一级节点,当前二级节点
354 fieldsTableShow.value = false
355 }
356 if (level == 2) {
357 fieldsDisabled.value = isChildHaveData === 'Y' ? true : false
358 }
359 if (level >= 3) {
360 fieldsDisabled.value = true
361 }
362 if ((children && children.length) || isChildHaveData === 'Y') {
363 treeSelectDisabled.value = true
364 }
365 }
366 }).finally(() => loading.value = false)
367 }
368 const confirmLoading = ref(false)
369 function confirm () {
370 formEl.value.validate(valid => {
371 if (!valid) return
372 let body = { ...form.value }
373 if (!body.parentGuid) {
374 body.parentGuid = null
375 }
376 delete body.fieldRSVOS
377 // return
378 const request = props.type === 'add' ? saveMetaStandard : updateMetaStandard
379 confirmLoading.value = true
380 request(body).then((res:any) => {
381 if (res.code === proxy.$passCode) {
382 ElMessage.success('操作成功')
383 emit('success', props.type === 'add' ? res.data : body.guid)
384 visible.value = false
385 return
386 }
387 ElMessage.error(res.msg)
388 }).finally(() => confirmLoading.value = false)
389 })
390 }
391
392 function getTreeSelectOptions () {
393 return getMetaStandardTree().then((res:any) => {
394 if (res.code === proxy.$passCode) {
395 let data = res.data || []
396 data.forEach(item => {
397 item.showEdit = true
398 item.level = 1
399 })
400 treeSelectData.value = data
401 }
402 })
403 }
404
405 watch(
406 () => visible.value,
407 (v) => {
408 if (!v) return
409 // console.log(props.currentNode)
410 // 上级标准:不能选自己,有数据的标准不能选
411 // treeSelectData.value = JSON.parse(JSON.stringify(props.standardOptions))
412 treeSelectDisabled.value = false
413 if (props.type === 'edit') {
414 // fieldsControlDisabled.value = false
415
416 getTreeSelectOptions().then(() => {
417 getDetail().then(() => formatOptions(treeSelectData.value))
418 })
419 treeCurrentNode.value = props.currentNode
420 } else {
421 // fieldsControlDisabled.value = true
422 form.value = { ...formTpl }
423 getTreeSelectOptions().then(() => {
424 formatOptions(treeSelectData.value)
425 })
426 treeCurrentNode.value = {}
427 }
428 setTimeout(() => {
429 formEl.value.clearValidate()
430
431 }, 100)
432 }
433 )
434 function formatOptions (options, level = 1) {
435 options.forEach((item:any) => {
436 let disabled = form.value.guid === item.guid ? true : false
437 if (item.isHaveData === 'Y') {
438 disabled = true
439 }
440 item.disabled = disabled
441 item.level = level
442 if (item.children) {
443 formatOptions(item.children, level + 1)
444 }
445 })
446 }
447 onBeforeMount(() => {
448 getParamsList({ dictType: '发布单位' }).then((res:any) => {
449 if (res.code === proxy.$passCode) {
450 const data = res.data || []
451 publishUnitOptions.value = data
452 }
453 })
454 getParamsList({ dictType: '字段名代码' }).then((res:any) => {
455 if (res.code === proxy.$passCode) {
456 const data = res.data || []
457 fieldOptions.value = data
458 }
459 })
460 getParamsList({ dictType: '输入框类型' }).then((res:any) => {
461 if (res.code === proxy.$passCode) {
462 const data = res.data || []
463 inputOptions.value = data
464 }
465 })
466 getDictAllList().then((res:any) => {
467 if (res.code === proxy.$passCode) {
468 const data = res.data || []
469 allDictOptions.value = data
470 }
471 })
472 })
473 </script>
474
475 <style lang="scss">
476 .standard-modal {
477 .el-form-item {
478 flex-direction: column;
479 .el-form-item__label {
480 justify-content: flex-start;
481 }
482 }
483 .el-drawer__footer {
484 padding: 10px;
485 justify-content: flex-end;
486 }
487 .el-dialog__body {
488 height: 500px;
489 overflow: auto;
490 }
491 .table-form-wrapper {
492 display: flex;
493 margin-bottom: 5px;
494 .table-form-item {
495 padding-right: 10px;
496 }
497 .table-form-operation {
498 flex: 0 0 30px;
499 // padding-left: 6px;
500 }
501 }
502 .table-form-add {
503 display: flex;
504 align-items: center;
505 color: #4fa1a4;
506 > span {
507 margin-left: 4px;
508 }
509 }
510 }
511 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <el-drawer
3 v-model="visible"
4 :title="title"
5 size="520px"
6 class="standard-meta-modal"
7 :close-on-click-modal="false"
8 >
9 <el-form
10 :model="form"
11 :rules="formRules"
12 ref="formEl"
13 style="min-height: 200px;"
14 require-asterisk-position="right"
15 v-loading="loading"
16 >
17 <el-row>
18 <el-col v-for="item,index in fields" :key="index" :span="12" style="padding-right:10px;">
19 <el-form-item :label="item.fileNameCodeName" :prop="item.fileNameCode">
20 <el-input
21 v-if="item.inputTypeCode == '1' || item.inputTypeCode == '3'"
22 v-model="form[item.fileNameCode]"
23 placeholder="请输入"
24 />
25 <el-select
26 v-else-if="item.inputTypeCode == '2'"
27 v-model="form[item.fileNameCode]"
28 filterable
29 clearable
30 placeholder="请选择"
31 >
32 <el-option v-for="op in formOptions[item.fileNameCode]" :label="op.label" :value="op.value" :key="op.value"></el-option>
33 </el-select>
34 <el-tree-select
35 v-else-if="item.inputTypeCode == '4'"
36 v-model="form[item.fileNameCode]"
37 :data="standardCodeTree"
38 :props="treeSelectProps"
39 placeholder="请选择"
40 filterable
41 clearable
42 />
43 </el-form-item>
44 </el-col>
45 </el-row>
46 </el-form>
47 <template #footer>
48 <div style="text-align:right">
49 <el-button @click="visible = false" :disabled="confirmLoading">取消</el-button>
50 <el-button type="primary" @click="confirm" :loading="confirmLoading">确认</el-button>
51 </div>
52 </template>
53 </el-drawer>
54 </template>
55
56 <script setup lang="ts">
57 import { watch } from 'vue'
58 import { ElMessage } from "element-plus";
59 import { getParamsList } from '@/api/modules/dataAsset'
60 import { saveMetaStandardDataFields, getMetaStandardFieldsDetail, getStandardCodeTree } from '@/api/modules/dataMetaService'
61
62 const { proxy } = getCurrentInstance() as any;
63 const props = defineProps({
64 modelValue: Boolean,
65 fields: {
66 type: Array,
67 default: () => ([])
68 },
69 metaStandardGuid: {
70 type: String,
71 default: ''
72 },
73 type: {
74 type: String,
75 default: 'add'
76 },
77 data: {
78 type: Object,
79 default: () => ({})
80 }
81 })
82 const emit = defineEmits(['update:modelValue', 'success', 'confirm'])
83 const visible = computed({
84 get() {
85 return props.modelValue;
86 },
87 set(val) {
88 emit('update:modelValue', val);
89 }
90 })
91 const title = computed(() => {
92 return props.type === 'add' ? '新增字段标准' : '编辑字段标准'
93 })
94
95 const formEl = ref()
96 const form = ref({})
97 const formRules = ref({})
98 const formOptions = ref({})
99 const detail = ref({})
100
101 async function initForm () {
102 const { fields, type, data } = props
103 console.log(data)
104 if (!fields) return
105 let formData = {}
106 let formRuleData = {}
107 let formOptionData = {}
108 let detailData:any = {}
109 if (type === 'edit') {
110 detailData = await getDetail()
111 detail.value = detailData
112 }
113 fields.forEach(async (item:any) => {
114 formData[item.fileNameCode] = type === 'add' ? '' : detailData.metaStandardValue[item.fileNameCode]
115 formRuleData[item.fileNameCode] = {
116 required: item.isNotnull === 'Y' ? true : false,
117 message: `缺少${item.fileNameCodeName}`
118 }
119 // formOptionData[item.fileNameCode] = await getOptions(item.dataTypeCode)
120 if (item.inputTypeCode == '2') {
121 formOptions.value[item.fileNameCode] = await getOptions(item.dataTypeCode)
122 }
123 })
124 // formOptions.value = formOptionData
125 form.value = formData
126 formRules.value = formRuleData
127 nextTick(() => formEl.value.clearValidate())
128 setTimeout(() => {
129 formEl.value.clearValidate()
130 }, 100)
131 }
132 function getOptions (dictType) {
133 return new Promise((resolve, reject) => {
134 getParamsList({ dictType }).then((res:any) => {
135 if (res.code === proxy.$passCode) {
136 resolve(res.data)
137 }
138 })
139 })
140 }
141 const loading = ref(false)
142 function getDetail () {
143 return new Promise((resolve) => {
144 loading.value = true
145 getMetaStandardFieldsDetail(props.data.guid).then((res:any) => {
146 if (res.code === proxy.$passCode) {
147 resolve(res.data)
148 }
149 }).finally(() => loading.value = false)
150 })
151 }
152
153 const confirmLoading = ref(false)
154 function confirm () {
155 console.log(form.value)
156 formEl.value.validate(valid => {
157 if (!valid) return
158 let body = {
159 metaStandardGuid: props.type === 'edit' ? detail.value.metaStandardGuid : props.metaStandardGuid,
160 metaStandardValue: { ...form.value }
161 }
162 if (props.type === 'edit') {
163 body.guid = props.data.guid
164 }
165 confirmLoading.value = true
166 saveMetaStandardDataFields(body).then((res:any) => {
167 if (res.code === proxy.$passCode) {
168 ElMessage.success('操作成功')
169 emit('success')
170 visible.value = false
171 } else {
172 ElMessage.error(res.msg)
173 }
174 }).finally(() => confirmLoading.value = false)
175 })
176 }
177
178 const standardCodeTree = ref([])
179 const treeSelectProps = {
180 label: 'name',
181 value: 'guid',
182 isLeaf: 'isCode'
183 }
184 function getStandardCodeTreeList () {
185 getStandardCodeTree().then((res:any) => {
186 if (res.code === proxy.$passCode) {
187 const data = res.data
188 data.forEach(item => {
189 if (item.children) {
190 item.children.forEach(subItem => {
191 // 二级的标准名字作为key
192 subItem.guid = subItem.name
193 // subItem.value = subItem.name
194 })
195 }
196 })
197 standardCodeTree.value = data
198 }
199 })
200 }
201
202 watch(
203 () => visible.value,
204 (v) => {
205 if (!v) return
206 initForm()
207 }
208 )
209
210 onBeforeMount(() => {
211 getStandardCodeTreeList()
212 })
213 </script>
214
215 <style lang="scss">
216 .standard-meta-modal {
217 .el-form-item {
218 flex-direction: column;
219 .el-form-item__label {
220 justify-content: flex-start;
221 }
222 }
223 .el-drawer__footer {
224 padding: 10px;
225 justify-content: flex-end;
226 }
227
228 .table-form-wrapper {
229 display: flex;
230 margin-bottom: 5px;
231 .table-form-item {
232 padding-right: 10px;
233 }
234 .table-form-operation {
235 flex: 0 0 70px;
236 padding-left: 6px;
237 }
238 }
239 }
240 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <el-select v-if="!readonly && isEdit && isSelectType(dbType, scope)" v-model="scope.row['defaultValue']" placeholder="请选择" collapse-tags-tooltip
3 filterable allow-create default-first-option :reserve-keyword="false">
4 <el-option v-for="opt in optionsConfig[dataType]" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
5 </el-select>
6 <el-date-picker v-else-if="!readonly && isEdit && dataType === 'date'" v-model="scope.row['defaultValue']" type="date"
7 format="YYYY-MM-DD" value-format="YYYY-MM-DD" placeholder="请选择" />
8 <el-date-picker v-else-if="!readonly && isEdit && dbType === 'mysql' && dataType === 'datetime'"
9 v-model="scope.row['defaultValue']" placeholder="请选择" type="datetime" format="YYYY-MM-DD HH:mm:ss"
10 value-format="YYYY-MM-DD HH:mm:ss" />
11 <el-input v-else-if="!readonly && isEdit" v-model.trim="scope.row['defaultValue']" placeholder="请填写"
12 @input="(val) => inputChange(val, scope.row.dataType, scope)" />
13 <span v-else>{{ scope.row["defaultValue"] == null ? '-' : (isSelectType(dbType, scope) ?
14 (optionsConfig[dataType].find(s => s.value == scope.row["defaultValue"])?.label ??
15 scope.row["defaultValue"]) : scope.row["defaultValue"]) }}</span>
16 </template>
17
18 <script lang="ts" setup name="tableDefaultValue">
19 import { ref } from "vue";
20 import { useDefault } from "@/hooks/useDefault";
21 const { optionsConfig, inputChange, isSelectType } = useDefault()
22
23 const props = defineProps({
24 dbType: {
25 type: String,
26 default: ''
27 },
28 scope: {
29 type: Object,
30 default: {}
31 },
32 readonly: {
33 type: Boolean,
34 default: false
35 }
36 })
37
38 const isEdit = computed(() => {
39 return props.scope.row['isEdit'];
40 });
41
42 const dataType = computed(() => {
43 return props.scope.row['dataType'];
44 });
45
46 </script>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: dimTableCreateManual
3 </route>
4
5 <script lang="ts" setup name="dimTableCreateManual">
6 import { ref } from "vue";
7 import { useRouter, useRoute } from "vue-router";
8 import { ElMessage, ElMessageBox, ElTable } from "element-plus";
9 import { QuestionFilled } from "@element-plus/icons-vue";
10 import {
11 getDatabase,
12 saveSubjectTable,
13 dimTypeList,
14 syncPolicys,
15 tableModels,
16 aggMethodList,
17 getMetaStandardDsField,
18 getAllFlowData
19 } from "@/api/modules/dataMetaService";
20 import useUserStore from "@/store/modules/user";
21 import expandPropertyDialog from "./components/expandPropertyDialog.vue";
22 import tableDefaultValue from "./components/tableDefaultValue.vue";
23 import { useDefault } from "@/hooks/useDefault"
24 import { getCamundaDeploymentId } from "@/api/modules/workFlowService"
25
26 const userStore = useUserStore();
27 const { checkDefault } = useDefault()
28
29 const router = useRouter();
30 const route = useRoute();
31 const fullPath = route.fullPath;
32 const isLook = <any>route.query.isLook == 1;
33 const subjectDomainGuid: any = ref(route.query.domainGuid);
34
35 const tableCreateInfoLoading = ref(false);
36
37 const { proxy } = getCurrentInstance() as any;
38 const fieldStandardTableRef = ref<InstanceType<typeof ElTable>>();
39
40 /** 表模型,只有doris数据库才有 */
41 const dbType = ref("");
42
43 const databaseList: any = ref([]);
44
45 const tableFieldsLoading = ref(false);
46
47 //记录当前正在编辑的表创建信息。
48 const tableCreateInfo: Ref<any> = ref({
49 guid: "",
50 isCreate: false,
51 inputNameValue: '',
52 tableCreateType: 1,
53 tableData: [
54 {
55 //数据库表信息。
56 dataSourceGuid: '',
57 dataServerName: "",
58 dataServerChName: "",
59 enName: "",
60 chName: "",
61 subjectDomain: route.query.domainName,
62 subjectDomainGuid: subjectDomainGuid.value,
63 dimType: 1,
64 codeColumn: '',
65 codeName: '',
66 syncPolicy: 3,
67 tableCategory: 4,
68 characterSet: 'utf8mb3',
69 tableModel: 1, //若是聚合模型,下方出现一列聚合方式选择。处了主键列,其余列都需要选择。每个表里都要有主键。
70 description: "",
71 },
72 ],
73 partitionAttribute: {},
74 tableFields: [], // 字段标准数组。
75 });
76
77 const selectTableFieldRows = ref([]);
78
79 //字段类型
80 const fieldTypes: any = ref([]);
81
82 //字符集
83 const characterList: any = ref([]);
84
85 //是否列表
86 const isNotList = ref([
87 {
88 label: "Y",
89 value: "Y",
90 },
91 {
92 label: "N",
93 value: "N",
94 },
95 ]);
96
97 const fullscreenLoading = ref(false);
98
99 /** 表里有数据时删除字段时提示。 */
100 const hasSubjectData = ref(false);
101
102 const expandProperties = ref({});
103
104 const deploymentId = ref('');
105 const flowExpand = ref(true);
106
107 onBeforeMount(() => {
108 getDatabaseList();
109 getFieldTypeList();
110 getCharacterListData();
111 if (route.query.metaStandard) {
112 tableCreateInfo.value.tableData[0].chName = route.query.standardName;
113 fullscreenLoading.value = true;
114 getMetaStandardDsField(route.query.metaStandard).then((res: any) => {
115 fullscreenLoading.value = false;
116 if (res.code == proxy.$passCode) {
117 tableCreateInfo.value.tableFields = res.data?.map((d, i) => {
118 d.orderNum = i;
119 d.isEdit = true;
120 return d;
121 }) || [];
122 } else {
123 proxy.$ElMessage.error(res.msg);
124 }
125 })
126 }
127 getCamundaDeploymentId('10025').then((res: any) => {
128 if (res.code == proxy.$passCode) {
129 deploymentId.value = res.data;
130 } else {
131 proxy.$ElMessage.error(res.msg);
132 }
133 })
134 })
135
136 onMounted(() => {
137 })
138
139 onActivated(() => {
140 });
141
142 const getDatabaseList = () => {
143 getDatabase({ connectStatus: 1 }).then((res: any) => {
144 databaseList.value = [];
145 if (res.code == proxy.$passCode) {
146 databaseList.value = res.data || [];
147 } else {
148 ElMessage.error(res.msg);
149 }
150 })
151 };
152
153 const getFieldTypeList = () => {
154 getAllFlowData('字段类型').then((res: any) => {
155 fieldTypes.value = [];
156 if (res.code == proxy.$passCode) {
157 fieldTypes.value = res.data || [];
158 } else {
159 ElMessage.error(res.msg);
160 }
161 })
162 }
163
164 const getCharacterListData = () => {
165 getAllFlowData('Mysql字符集').then((res: any) => {
166 characterList.value = [];
167 if (res.code == proxy.$passCode) {
168 characterList.value = res.data || [];
169 } else {
170 ElMessage.error(res.msg);
171 }
172 })
173 }
174
175 /** 限制长度输入框只能输入整型数字,表,字段英文名称,限制输入字符,数字和下划线。 */
176 const inputLengthKeyUp = (regexp, scope, field, max: any = null, min: any = null) => {
177 scope.row[field] = scope.row[field].replace(regexp, '');
178 if (field == 'fieldLength' && scope.row.dataType == 'decimal') {
179 max = 65;
180 }
181 /** 最大值设置2000 */
182 if (max && scope.row[field] > max) {
183 scope.row[field] = max;
184 }
185 if (min !== null && scope.row[field] != '' && scope.row[field] <= min) {
186 scope.row[field] = min;
187 }
188 }
189
190 /** 保存表 */
191 const saveTable = () => {
192 let tableData = tableCreateInfo.value.tableData[0];
193 if (!tableData.chName) {
194 ElMessage({
195 type: "error",
196 message: "主题表名称不能为空",
197 });
198 return;
199 }
200 if (!tableData.dataServerName) {
201 ElMessage({
202 type: "error",
203 message: "数据源不能为空",
204 });
205 return;
206 }
207 if (tableData.dataServerName.indexOf('-') > -1) {
208 ElMessage.error('数据库表名称不能包含中划线,可以改为下划线');
209 return;
210 }
211 if (!tableCreateInfo.value.inputNameValue) {
212 ElMessage({
213 type: "error",
214 message: "主题表名称不能为空",
215 });
216 return;
217 }
218 if (!tableData.codeColumn) {
219 ElMessage({
220 type: "error",
221 message: "编码字段不能为空",
222 });
223 return;
224 }
225 if (!tableData.codeName) {
226 ElMessage({
227 type: "error",
228 message: "编码名称不能为空",
229 });
230 return;
231 }
232 let tableFields = tableCreateInfo.value.tableFields;
233 if (!tableFields.length) {
234 ElMessage({
235 type: "error",
236 message: "表字段不能为0行",
237 });
238 return;
239 }
240 // 若开启了字段标准,则不能为空。
241 // 必须含有主键。若是聚合模型,则除了主键必须有聚合方式。
242 let isSumModel = tableData.tableModel === 2;
243 let hasPrimary = false;
244 let enNames: any = [];
245 let chNames: any = [];
246 const regex = /^[a-zA-Z]/;
247 for (const field of tableFields) {
248 if (!field.enName) {
249 ElMessage.error(`第 ${field.orderNum} 个字段的英文名称不能为空`);
250 return;
251 }
252 if (!regex.test(field.enName)) {
253 ElMessage.error(`第 ${field.orderNum} 个字段的英文名称必须以英文字符开头`);
254 return;
255 }
256 if (!field.dataType) {
257 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型不能为空`);
258 return;
259 }
260 if (enNames.indexOf(field.enName) > -1) {
261 ElMessage.error(`字段的英文名称 ${field.enName} 不能重复`);
262 return;
263 }
264 if (chNames.indexOf(field.chName) > -1) {
265 ElMessage.error(`字段的中文名称 ${field.chName} 不能重复`);
266 return;
267 }
268 if (field.dataType === "decimal" && (!field.fieldPrecision && field.fieldPrecision != 0)) {
269 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点型时,精度不能为空`);
270 return;
271 }
272 if (field.dataType === "varchar" && (!field.fieldLength && field.fieldLength != 0)) {
273 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为字符型时,长度不能为空`);
274 return;
275 }
276 if (field.dataType === "char" && (!field.fieldLength && field.fieldLength != 0)) {
277 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为单字符型时,长度不能为空`);
278 return;
279 }
280 if (field.dataType === "decimal" && (!field.fieldLength && field.fieldLength != 0)) {
281 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点符型时,长度不能为空`);
282 return;
283 }
284 if (field.isPrimary === 'Y') {
285 hasPrimary = true;
286 if (field.notNull != 'Y') {
287 ElMessage.error(`第 ${field.orderNum} 个字段为主键,应设置为必填`);
288 return;
289 }
290 if (field.dataType == 'text') {
291 ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘大字段型’`);
292 return;
293 }
294 if (field.dataType == 'json') {
295 ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘JSON类型’`);
296 return;
297 }
298 if (field.dataType == 'bit') {
299 ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘布尔类型’`);
300 return;
301 }
302 } else {
303 if (!field.aggWay && isSumModel) {
304 ElMessage.error(`聚合模型的非主键字段必须设置聚合方式`);
305 return;
306 }
307 }
308 if (field.isEdit) {
309 if (checkDefault[field.dataType]) {
310 if (!field.fieldLength) { }
311 if (!checkDefault[field.dataType]({ row: field })) {
312 return;
313 }
314 }
315 }
316 enNames.push(field.enName);
317 chNames.push(field.chName);
318 field.fieldStandardGuid = route.query.metaStandard;
319 }
320 if (!hasPrimary) {
321 ElMessage.error(`字段至少有一个主键字段!`);
322 return;
323 }
324 let addInfo = Object.assign({}, tableCreateInfo.value.tableData[0], {
325 enName: tableCreateInfo.value.inputNameValue,
326 tableCreateType: tableCreateInfo.value.tableCreateType,
327 saveFlag: 1,
328 dbType: dbType.value,
329 dataState: 1,
330 partitionAttribute: !Object.keys(tableCreateInfo.value.partitionAttribute).length ? null : Object.assign({}, tableCreateInfo.value.partitionAttribute, {
331 dynamicPartitionHistory: tableCreateInfo.value.partitionAttribute ? "Y" : 'N'
332 }),
333 subjectFieldAddDTOS: tableCreateInfo.value.tableFields.map((field, i) => {
334 return Object.assign({}, field, { orderNum: i + 1 });
335 }),
336 })
337 if (!tableCreateInfo.value.guid) { //添加
338 fullscreenLoading.value = true;
339 saveSubjectTable(addInfo).then((res: any) => {
340 fullscreenLoading.value = false;
341 if (res.code == proxy.$passCode) {
342 ElMessage.success('新建表提交成功!');
343 router.push({
344 name: 'metadataStandardQuery'
345 });
346 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
347 } else {
348 ElMessage.error(res.msg);
349 }
350 }).catch((err) => {
351 ElMessage.error(err);
352 fullscreenLoading.value = false;
353 });
354 }
355 };
356
357 //数据库选择改变,对应的表名称是否需要变化。需要根据此属性带出表名前缀,以及是否是doris数据库。
358 const selectDatabaseChange = (val) => {
359 let oldDbType = dbType.value;
360 let d = databaseList.value.find(d => d.guid === val);
361 if (d) {
362 dbType.value = d.databaseType;
363 tableCreateInfo.value.tableData[0].dataSourceGuid = d.guid;
364 tableCreateInfo.value.tableData[0].dataServerName = d.databaseNameEn;
365 } else {
366 dbType.value = "";
367 tableCreateInfo.value.tableData[0].dataSourceGuid = '';
368 tableCreateInfo.value.tableData[0].dataServerName = '';
369 }
370 if (oldDbType != dbType.value) {
371 tableCreateInfo.value.tableFields.forEach((tableField: any) => {
372 if (tableField['dataType'] === "datetime") {
373 tableField.defaultValue = ""
374 }
375 })
376 }
377 };
378
379 /** 添加字段标准 */
380 const addField = () => {
381 let len = tableCreateInfo.value.tableFields.length;
382 tableCreateInfo.value.tableFields.push({
383 orderNum: len + 1,
384 isDim: "N",
385 isPrimary: "N",
386 notNull: "N",
387 isEdit: true
388 });
389 //设置选中表格当前新增行。
390 fieldStandardTableRef.value?.setCurrentRow(
391 tableCreateInfo.value.tableFields[tableCreateInfo.value.tableFields.length - 1]
392 );
393 nextTick(() => {
394 let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
395 let domScroll = bodyWrapper.parentElement.parentElement;
396 let rect = domScroll.getBoundingClientRect();
397 let maxNum = len + 1;
398 if (maxNum * 36 > rect.height + domScroll.scrollTop) {
399 fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
400 }
401 })
402 };
403
404 /** 勾选字段标准选中变化。 */
405 const selectionFieldsChange = (val) => {
406 selectTableFieldRows.value = val;
407 };
408
409 /**
410 * 上移规则:
411 * 勾选多个时先从最上面开始逐个上移一行,若已经移到最上面一行,则不处理。
412 */
413 const moveUp = () => {
414 let selectRows = fieldStandardTableRef.value?.getSelectionRows();
415 if (!selectRows.length) {
416 ElMessage.error('请先选择需要勾选的数据进行上移');
417 return;
418 }
419 let data = tableCreateInfo.value.tableFields;
420 let selectRowIndexs: number[] = [];
421 let minNum: number = 0;
422 selectRows.forEach((row, i) => {
423 let orderNum = data.findIndex(d => d === row) + 1;
424 if (orderNum == 1) {
425 selectRowIndexs.push(orderNum);
426 minNum = orderNum;
427 return;
428 }
429 if (selectRowIndexs.includes(orderNum - 1)) {
430 //下一行也是选中的,则不做转换。
431 return;
432 }
433 let topNum = orderNum - 1;
434 if (i === 0) {
435 minNum = topNum;
436 }
437 row.orderNum = topNum;
438 let changeRow = data[topNum - 1];
439 changeRow.orderNum = orderNum;
440 selectRowIndexs.push(topNum);
441 data[topNum] = changeRow;
442 data[topNum - 1] = row;
443 });
444 nextTick().then(() => {
445 let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
446 let domScroll = bodyWrapper.parentElement.parentElement;
447 if ((minNum * 36 - 36 - 2) < domScroll.scrollTop) {
448 fieldStandardTableRef.value?.setScrollTop((domScroll.scrollTop - 36 - 2) < 0 ? 0 : (domScroll.scrollTop - 36 - 2))
449 }
450 });
451 }
452
453 /**
454 * 下移规则:
455 * 勾选多个时先从最下面开始逐个下移一行,若已经移到最下面一行,则不处理。
456 */
457 const moveDown = () => {
458 let selectRows = fieldStandardTableRef.value?.getSelectionRows();
459 if (!selectRows.length) {
460 ElMessage.error('请先选择需要勾选的数据进行下移');
461 return;
462 }
463 let data = tableCreateInfo.value.tableFields;
464 let selectRowIndexs: number[] = [];
465 let maxNum: number = 0;
466 selectRows.slice(0).reverse().forEach((row, i) => {
467 let orderNum = data.findIndex(d => d === row) + 1;
468 if (orderNum === data.length) {
469 maxNum = orderNum;
470 selectRowIndexs.push(orderNum);
471 return;
472 }
473 if (selectRowIndexs.includes(orderNum + 1)) {
474 //下一行也是选中的,则不做转换。
475 return;
476 }
477 let bottomNum = orderNum + 1;
478 row.orderNum = bottomNum;
479 if (i === 0) {
480 maxNum = bottomNum;
481 }
482 let changeRow = data[bottomNum - 1];
483 changeRow.orderNum = orderNum;
484 selectRowIndexs.push(bottomNum);
485 data[orderNum - 1] = changeRow;
486 data[bottomNum - 1] = row;
487 });
488 nextTick(() => {
489 let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
490 let domScroll = bodyWrapper.parentElement.parentElement;
491 let rect = domScroll.getBoundingClientRect();
492 if (maxNum * 36 > rect.height + domScroll.scrollTop) {
493 fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
494 }
495 })
496 }
497
498
499 /** 批量删除字段标准 */
500 const delFeilds = () => {
501 if (selectTableFieldRows.value.length == 0) {
502 ElMessage({
503 type: "info",
504 message: "请选择需要删除的字段",
505 });
506 return;
507 }
508 if (tableCreateInfo.value.isCreate) {
509 if (selectTableFieldRows.value.find((row: any) => row.isPrimary === 'Y')) {
510 ElMessage.error('已建表不能删除主键字段');
511 return;
512 }
513 }
514 let hasCreateField = selectTableFieldRows.value.some((row: any) => row.isCreate === 'Y');
515 ElMessageBox.confirm(hasCreateField && hasSubjectData.value ? '已选择的字段中含有已创建的表字段且有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
516 confirmButtonText: "确定",
517 cancelButtonText: "取消",
518 type: "warning",
519 })
520 .then(() => {
521 //此删除是直接从库里删除,还是点击保存后再删除呢??如果是入库删除,后,调用接口获取数据。
522 let tableFields = tableCreateInfo.value.tableFields;
523 let tableData = tableCreateInfo.value.tableData[0];
524 selectTableFieldRows.value.forEach((r: any) => {
525 let index = tableFields.findIndex((t: any) => t.orderNum === r.orderNum);
526 if (index !== -1) {
527 let row = tableFields[index];
528 tableFields.splice(index, 1);
529 if (tableData.codeName == row.enName) {
530 tableData.codeName = "";
531 }
532 if (tableData.codeColumn == row.enName) {
533 tableData.codeColumn = "";
534 }
535 }
536 });
537 fieldStandardTableRef.value?.clearSelection();
538 tableCreateInfo.value.tableFields.forEach((field, i) => {
539 field.orderNum = i + 1;
540 });
541 ElMessage({
542 type: "success",
543 message: "删除成功",
544 });
545 })
546 .catch(() => {
547 ElMessage({
548 type: "info",
549 message: "已取消删除",
550 });
551 });
552 };
553
554 //点击编辑按钮
555 const handleFieldClickEdit = (scope) => {
556 scope.row['isEdit'] = true;
557 };
558
559 //点击保存按钮
560 const handleFieldClickSave = (scope) => {
561 if (!scope.row.fieldStandardCode) {
562 ElMessage({
563 type: "error",
564 message: "该主题域开启了字段标准,当前行字段标准不能为空!",
565 });
566 return;
567 }
568 if (!scope.row.enName) {
569 ElMessage({
570 type: "error",
571 message: "字段英文名不能为空!",
572 });
573 return;
574 }
575 scope.row['isEdit'] = false;
576 };
577
578 const handleFieldDelete = (scope) => {
579 ElMessageBox.confirm(scope.row['isCreate'] === 'Y' && hasSubjectData.value ? '该字段已被创建且表中有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
580 confirmButtonText: "确定",
581 cancelButtonText: "取消",
582 type: "warning",
583 })
584 .then(() => {
585 let tableFields = tableCreateInfo.value.tableFields;
586 tableFields.splice(scope.$index, 1);
587 tableCreateInfo.value.tableFields.forEach((field, i) => {
588 field.orderNum = i + 1;
589 });
590 tableCreateInfo.value.tableFields.forEach((field, i) => {
591 field.orderNum = i + 1;
592 });
593 let row = scope.row;
594 let tableData = tableCreateInfo.value.tableData[0];
595 if (tableData.codeName == row.enName) {
596 tableData.codeName = "";
597 }
598 if (tableData.codeColumn == row.enName) {
599 tableData.codeColumn = "";
600 }
601 ElMessage({
602 type: "success",
603 message: "删除成功",
604 });
605 })
606 .catch(() => {
607 ElMessage({
608 type: "info",
609 message: "已取消删除",
610 });
611 });
612 }
613
614 const dataTypeChange = (val, scope) => {
615 scope.row['defaultValue'] = ''
616 scope.row['fieldLength'] = undefined
617 scope.row['fieldPrecision'] = undefined
618 }
619
620 /** 扩展属性弹出对话框 */
621 const expandPropertyDialogRef = ref();
622
623 /** 扩展属性弹出对话框 */
624 const handleClickExpand = () => {
625 expandPropertyDialogRef.value?.handleClickExpand();
626 }
627
628 const expandDialogValueChange = (val) => {
629 tableCreateInfo.value.partitionAttribute = val;
630 expandProperties.value = val;
631 }
632
633 const tableSelectFields = computed(() => {
634 return tableCreateInfo.value.tableFields.filter(t => !!t.enName);
635 })
636
637 </script>
638
639 <template>
640 <div class="table_tool_wrap" v-loading="fullscreenLoading">
641 <div class="tools_btns">
642 <!-- <el-button type="primary" :disabled="isLook" @click="saveDraftTable" v-preReClick>保存为草稿</el-button> -->
643 <el-button type="primary" :disabled="isLook" @click="saveTable" v-preReClick>提交</el-button>
644 </div>
645 <el-table ref="tableRef" :data="tableCreateInfo.tableData" v-loading="tableCreateInfoLoading"
646 :highlight-current-row="true" stripe border height="100%" tooltip-effect="light" row-key="guid" :style="{
647 width: '100%',
648 height: 'auto',
649 display: 'inline-block',
650 }">
651 <el-table-column prop="dataSourceGuid" label="数据源" width="200px" align="left" show-overflow-tooltip>
652 <template #header>
653 <span>数据源</span>
654 <span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
655 </template>
656 <template #default="scope">
657 <el-select v-if="!isLook" v-model="scope.row['dataSourceGuid']" placeholder="请选择"
658 :disabled="tableCreateInfo.isCreate" @change="(val) => selectDatabaseChange(val)" clearable filterable>
659 <el-option v-for="opt in databaseList" :key="opt['guid']" :label="opt['databaseNameZh']"
660 :value="opt['guid']" />
661 </el-select>
662 <span v-else>{{ scope.row["dataServerChName"] }}</span>
663 </template>
664 </el-table-column>
665 <el-table-column prop="enName" label="数据库表" width="200px" align="left" show-overflow-tooltip>
666 <template #header>
667 <span>数据库表</span>
668 <span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
669 </template>
670 <template #default="scope">
671 <template v-if="!isLook">
672 <div class="prefix-or-suffix-cell">
673 <el-input :disabled="tableCreateInfo.isCreate" v-model.trim="tableCreateInfo.inputNameValue"
674 :maxlength="50" placeholder="必填" />
675 </div>
676 </template>
677 <span v-else>{{ scope.row["enName"] }}</span>
678 </template>
679 </el-table-column>
680 <el-table-column prop="chName" label="主题表名称" width="200px" align="left" show-overflow-tooltip>
681 <template #header>
682 <span>主题表名称</span>
683 <span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
684 </template>
685 <template #default="scope">
686 <el-input v-if="!isLook" v-model.trim="scope.row['chName']" placeholder="必填" :maxlength="50" />
687 <span v-else>{{ scope.row["chName"] }}</span>
688 </template>
689 </el-table-column>
690 <el-table-column prop="subjectDomain" label="主题域" width="180px" align="left" show-overflow-tooltip>
691 <template #default="scope">
692 <el-input v-if="!isLook" disabled v-model.trim="scope.row['subjectDomain']" />
693 <span v-else>{{ scope.row["subjectDomain"] }}</span>
694 </template>
695 </el-table-column>
696 <el-table-column prop="tableModel" label="表模型" :width="isLook ? '100px' : '150px'" align="left"
697 show-overflow-tooltip>
698 <template #default="scope">
699 <el-select v-if="dbType == 'doris' && !isLook" v-model="scope.row['tableModel']" placeholder="请选择"
700 :disabled="tableCreateInfo.isCreate">
701 <el-option v-for="opt in tableModels" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
702 </el-select>
703 <span v-else-if="dbType == 'doris'">{{tableModels.find(t => t.value === scope.row["tableModel"])?.label ||
704 '-'
705 }}</span>
706 <span v-else>--</span>
707 </template>
708 </el-table-column>
709 <el-table-column prop="dimType" label="维表类型" :width="isLook ? '100px' : '150px'" align="left"
710 show-overflow-tooltip>
711 <template #default="scope">
712 <el-select v-if="!isLook" v-model="scope.row['dimType']" placeholder="请选择">
713 <el-option v-for="opt in dimTypeList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
714 </el-select>
715 <span v-else>{{dimTypeList.find(t => t.value === scope.row["dimType"])?.label || '-'}}</span>
716 </template>
717 </el-table-column>
718 <el-table-column prop="codeColumn" label="编码字段" :width="isLook ? '100px' : '150px'" align="left"
719 show-overflow-tooltip>
720 <template #header>
721 <div class="header_title">
722 <span>编码字段</span>
723 <span v-if="!isLook" style="color:red;margin-left: 2px;margin-right: 8px;">*</span>
724 <el-tooltip placement="top" effect="light" popper-class="table_tooltip">
725 <template #content>
726 <div style="max-width: 236px;">
727 下拉列表来自于该表的字段。
728 </div>
729 </template>
730 <el-icon>
731 <QuestionFilled />
732 </el-icon>
733 </el-tooltip>
734 </div>
735 </template>
736 <template #default="scope">
737 <el-select v-if="!isLook" v-model="scope.row['codeColumn']" placeholder="请选择">
738 <el-option v-for="opt in tableSelectFields" :key="opt['enName']" :label="opt['chName']"
739 :value="opt['enName']" />
740 </el-select>
741 <span v-else>{{ scope.row["codeColumn"] || '-' }}</span>
742 </template>
743 </el-table-column>
744 <el-table-column prop="codeName" label="编码名称" :width="isLook ? '100px' : '150px'" align="left"
745 show-overflow-tooltip>
746 <template #header>
747 <div class="header_title">
748 <span>编码名称</span>
749 <span v-if="!isLook" style="color:red;margin-left: 2px;margin-right: 8px;">*</span>
750 <el-tooltip placement="top" effect="light" popper-class="table_tooltip">
751 <template #content>
752 <div style="max-width: 236px;">
753 下拉列表来自于该表的字段。
754 </div>
755 </template>
756 <el-icon>
757 <QuestionFilled />
758 </el-icon>
759 </el-tooltip>
760 </div>
761 </template>
762 <template #default="scope">
763 <el-select v-if="!isLook" v-model="scope.row['codeName']" placeholder="请选择">
764 <el-option v-for="opt in tableSelectFields" :key="opt['enName']" :label="opt['chName']"
765 :value="opt['enName']" />
766 </el-select>
767 <span v-else>{{ scope.row["codeName"] || '-' }}</span>
768 </template>
769 </el-table-column>
770 <el-table-column prop="syncPolicy" label="同步策略" :width="isLook ? '100px' : '150px'" align="left"
771 show-overflow-tooltip>
772 <template #default="scope">
773 <el-select v-if="!isLook" v-model="scope.row['syncPolicy']" placeholder="请选择">
774 <el-option v-for="opt in syncPolicys" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
775 </el-select>
776 <span v-else>{{syncPolicys.find(s => s.value === scope.row["syncPolicy"])?.label || '-'}}</span>
777 </template>
778 </el-table-column>
779 <el-table-column prop="characterSet" label="字符集" :width="isLook ? '100px' : '150px'" align="left"
780 show-overflow-tooltip>
781 <template #default="scope">
782 <el-select v-if="!isLook" v-model="scope.row['characterSet']" placeholder="请选择">
783 <el-option v-for="opt in characterList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
784 </el-select>
785 <span v-else>{{characterList.find(c => c.value === scope.row["characterSet"])?.label || '-'
786 }}</span>
787 </template>
788 </el-table-column>
789 <el-table-column prop="description" label="描述" width="220px" align="left" show-overflow-tooltip>
790 <template #default="scope">
791 <el-input v-if="!isLook" v-model.trim="scope.row['description']" />
792 <span v-else>{{ scope.row["description"] || '-' }}</span>
793 </template>
794 </el-table-column>
795 <el-table-column label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
796 <template #default="scope">
797 <span class="text_btn" @click="handleClickExpand()" v-preReClick>扩展属性</span>
798 </template>
799 </el-table-column>
800 </el-table>
801 <div class="tools_btns">
802 <el-button type="primary" :disabled="isLook" @click="addField">新增</el-button>
803 <el-button @click="moveUp" :disabled="isLook">上移</el-button>
804 <el-button @click="moveDown" :disabled="isLook">下移</el-button>
805 <el-button @click="delFeilds" :disabled="isLook">批量删除</el-button>
806 </div>
807 <div class="table_panel">
808 <el-table ref="fieldStandardTableRef" :data="tableCreateInfo.tableFields" v-loading="tableFieldsLoading"
809 :highlight-current-row="true" stripe border height="100%" row-key="guid"
810 @selection-change="selectionFieldsChange" tooltip-effect="light" :style="{
811 width: '100%',
812 'max-height': 'calc(100% - 16px)',
813 display: 'inline-block',
814 }">
815 <el-table-column type="selection" v-if="!isLook" :width="32" align="center" />
816 <el-table-column label="排序" type="index" width="56px" align="center" show-overflow-tooltip>
817 </el-table-column>
818 <el-table-column prop="chName" label="字段中文名称" width="150px" align="left" show-overflow-tooltip>
819 <template #default="scope">
820 <el-input v-if="scope.row['isEdit']" placeholder="请输入" v-model.trim="scope.row['chName']" />
821 <span v-else>{{ scope.row["chName"] || '-' }}</span>
822 </template>
823 </el-table-column>
824 <el-table-column prop="enName" label="字段英文名" width="150px" align="left" show-overflow-tooltip>
825 <template #default="scope">
826 <el-input v-if="scope.row['isEdit'] && !(scope.row['guid'] && tableCreateInfo.isCreate)"
827 v-model.trim="scope.row['enName']" placeholder="必填"
828 @input="inputLengthKeyUp(/[^a-zA-Z0-9_]/g, scope, 'enName')" />
829 <span v-else>{{ scope.row["enName"] || '-' }}</span>
830 </template>
831 </el-table-column>
832 <el-table-column prop="dataType" label="字段类型" width="120px" align="left" show-overflow-tooltip>
833 <template #default="scope">
834 <el-select v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y')"
835 v-model="scope.row['dataType']" placeholder="请选择" @change="(val) => dataTypeChange(val, scope)">
836 <el-option v-for="opt in fieldTypes" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
837 </el-select>
838 <span v-else>{{fieldTypes.find(f => f.value === scope.row["dataType"])?.label || '-'}}</span>
839 </template>
840 </el-table-column>
841 <el-table-column prop="fieldLength" label="长度" width="115px" align="left" show-overflow-tooltip>
842 <template #default="scope">
843 <el-input
844 v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && (scope.row['dataType'] == 'varchar' || scope.row['dataType'] == 'decimal' || scope.row['dataType'] == 'char')"
845 v-model.trim="scope.row['fieldLength']" placeholder="必填"
846 @input="inputLengthKeyUp(/\D/g, scope, 'fieldLength', 2000, 1)" />
847 <span v-else>{{ scope.row["fieldLength"] == null ? '-' : scope.row["fieldLength"] }}</span>
848 </template>
849 </el-table-column>
850 <el-table-column prop="fieldPrecision" label="精度" width="115px" align="left" show-overflow-tooltip>
851 <template #default="scope">
852 <el-input
853 v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && scope.row['dataType'] == 'decimal'"
854 v-model.trim="scope.row['fieldPrecision']" placeholder="必填"
855 @input="inputLengthKeyUp(/\D/g, scope, 'fieldPrecision', 30, 1)" />
856 <span v-else>{{ scope.row["fieldPrecision"] == null ? '-' : scope.row["fieldPrecision"] }}</span>
857 </template>
858 </el-table-column>
859 <el-table-column prop="isPrimary" label="是否主键" width="90px" align="left" show-overflow-tooltip>
860 <template #default="scope">
861 <el-select v-if="scope.row['isEdit'] && !tableCreateInfo.isCreate" v-model="scope.row['isPrimary']"
862 placeholder="请选择">
863 <el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
864 </el-select>
865 <span v-else>{{ scope.row["isPrimary"] || '-' }}</span>
866 </template>
867 </el-table-column>
868 <el-table-column v-if="tableCreateInfo.tableData[0].tableModel == 2" prop="aggWay" label="聚合方式" width="120px"
869 align="left" show-overflow-tooltip>
870 <template #default="scope">
871 <el-select v-if="scope.row['isEdit']" v-model="scope.row['aggWay']" placeholder="请选择">
872 <el-option v-for="opt in aggMethodList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
873 </el-select>
874 <span v-else>{{ scope.row["aggWay"] || '-' }}</span>
875 </template>
876 </el-table-column>
877 <el-table-column prop="notNull" label="是否必填" width="90px" align="left" show-overflow-tooltip>
878 <template #default="scope">
879 <el-select v-if="scope.row['isEdit'] && !(scope.row.isCreate == 'Y' && scope.row['notNull'] == 'N')"
880 v-model="scope.row['notNull']" placeholder="请选择">
881 <el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
882 </el-select>
883 <span v-else>{{ scope.row["notNull"] || '-' }}</span>
884 </template>
885 </el-table-column>
886 <el-table-column prop="defaultValue" label="默认值" width="205px" align="left" show-overflow-tooltip>
887 <template #default="scope">
888 <tableDefaultValue :scope="scope" :dbType="dbType"
889 :readonly="dbType == 'doris' && scope.row.isCreate == 'Y'"></tableDefaultValue>
890 </template>
891 </el-table-column>
892 <el-table-column v-if="!isLook" label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
893 <template #default="scope">
894 <template v-if="!(tableCreateInfo.isCreate && scope.row['isPrimary'] === 'Y')">
895 <span class="text_btn" v-if="!scope.row['isEdit']" @click="handleFieldClickEdit(scope)"
896 v-preReClick>编辑</span>
897 <span class="text_btn" v-else @click="handleFieldClickSave(scope)" v-preReClick>保存</span>
898 <el-divider direction="vertical" />
899 <span class="text_btn" @click="handleFieldDelete(scope)">删除</span>
900 </template>
901 <span v-else>--</span>
902 </template>
903 </el-table-column>
904 </el-table>
905 </div>
906 <ContentWrap title="流程审批" description="" :isExpand="flowExpand" :expand-swicth="true" class="mb16"
907 @expand="(v) => flowExpand = v">
908 <ApprovalProcess v-if="deploymentId" :deploymentId="deploymentId" :definitionId="''">
909 </ApprovalProcess>
910 </ContentWrap>
911 <expandPropertyDialog ref="expandPropertyDialogRef" :is-look="isLook" :partitionAttribute="expandProperties"
912 :table-create-info="tableCreateInfo" @expandValueChange="expandDialogValueChange" />
913 </div>
914 </template>
915
916 <style lang="scss" scoped>
917 .table_tool_wrap {
918 width: 100%;
919 height: 100%;
920 padding: 0 16px;
921 overflow: hidden auto;
922
923 .tools_btns {
924 padding: 8px 0;
925 }
926
927 :deep(.el-table) {
928 .cell {
929
930 .prefix-or-suffix-cell {
931 display: inline-flex;
932 align-items: center;
933 }
934 }
935 }
936
937 .table_panel {
938 height: 268px;
939
940 :deep(.el-table) {
941 & td.el-table__cell {
942 padding: 2px 0;
943 height: 36px;
944 }
945 }
946 }
947 }
948
949 :deep(.el-dialog) {
950 .dialog-form-inline {
951 .checkbox_input {
952 display: flex;
953 flex-direction: column;
954
955 .input_panel {
956 margin: 0;
957 }
958 }
959
960 .select_group {
961 .el-form-item__content>.el-input {
962 margin-top: 21px;
963 }
964 }
965
966 .radio_panel {
967 .panel_content {
968 display: none;
969 }
970 }
971 }
972 }
973
974 :deep(.batchDialog) {
975 .el-tree-node__content {
976 padding-left: 8px !important;
977
978 .el-icon {
979 display: none;
980 }
981 }
982 }
983
984 .header_title {
985 margin-left: 8px;
986 display: flex;
987 align-items: center;
988
989 .el-icon {
990 color: #b2b2b2;
991 width: 16px;
992 height: 16px;
993 }
994 }
995 </style>
...\ No newline at end of file ...\ No newline at end of file
...@@ -26,8 +26,8 @@ function encrypt(txt) { ...@@ -26,8 +26,8 @@ function encrypt(txt) {
26 } 26 }
27 const userStore = useUserStore() 27 const userStore = useUserStore()
28 const userInfoData = JSON.parse(localStorage.userData) 28 const userInfoData = JSON.parse(localStorage.userData)
29 // const url: any = 'https://scm.cs4pl.com/portraitBMap?fUrl=portraitMap'; 29 const url: any = 'https://scm-operation-test.csbr.cn/portraitBMap?fUrl=portraitMap&tUrl=portraitBMap';
30 const url: any = 'http://localhost:8086/portraitBMap?fUrl=portraitMap'; 30 // const url: any = 'http://localhost:8086/portraitBMap?fUrl=portraitMap&tUrl=portraitBMap';
31 31
32 const link = ref('') 32 const link = ref('')
33 const loading = ref(true); 33 const loading = ref(true);
......
1 <route lang="yaml">
2 name: metadataStandardCodetable
3 </route>
4
5 <script lang="ts" setup name="metadataStandardCodetable">
6 import { ref } from 'vue'
7 import { ElMessage, ElMessageBox } from "element-plus";
8 import { Search } from '@element-plus/icons-vue'
9 import Tree from '@/components/Tree/index.vue'
10 import Table from '@/components/Table/index.vue'
11 import Drawer from './components/drawer.vue'
12 import DictFileds from './components/dictFileds.vue'
13 import useCatchStore from "@/store/modules/catch";
14 import { download } from '@/utils/common'
15 import { getParamsList } from '@/api/modules/dataAsset'
16 import { getStandardCodeList, saveStandardCode,
17 updateStandardCode, getStandardCodeDetail,
18 deleteStandardCode, getStandardCodeStandard, exportStandardCodeData,
19 getStandardCodeDataList, getStandardCodeTree
20 } from '@/api/modules/dataMetaService'
21 import {
22 addDictionary,
23 deleteDictionary,
24 getDictionary,
25 updateDictionary,
26 getDictionaryTree,
27 getDictionaryDetail,
28 exportDictionary,
29 showDictionary,
30 getDataBaseList,
31 getCoderuleList,
32 saveDictionaryData,
33 getDictionaryFileds,
34 updateDictionaryState,
35 getDictionaryRuleData,
36 exportDictionaryFileds,
37 checkDeleteDictionary,
38 checkDeleteDictionaryScheme,
39 checkDictionaryData,
40 getNewDataTypeList
41 } from '@/api/modules/dataInventory';
42 import router from '@/router'
43 import { TableColumnWidth } from '@/utils/enum';
44
45 const { proxy } = getCurrentInstance() as any;
46
47 const cacheStore = useCatchStore()
48 // 禁用字段
49 const forbidFields = [
50 'guid',
51 'sharding_flag',
52 'create_user_id',
53 'create_user_name',
54 'update_user_id',
55 'update_user_name',
56 'create_time',
57 'update_time',
58 'is_deleted'
59 ]
60 const showFiledsPage = ref(false)
61 const dictFiledsRef = ref()
62 const dataTypeList = ref([])
63 const dataBaseList = ref([])
64 const codeRuleList = ref([])
65 const dictTreeRef = ref()
66 const dictType = ref(-1)
67 const dictGuid = ref('')
68 const expandedKey: any = ref([])
69 const currentNodeKey = ref('')
70 const showLoading = ref(true)
71 const treeData = ref([])
72 const treeInfo = ref({
73 id: "data-pickup-tree",
74 filter: true,
75 queryValue: "",
76 queryPlaceholder: "请输入关键字搜索",
77 props: {
78 label: "label",
79 value: "value",
80 isLeaf: "isLeaf",
81 },
82 // lazy: true,
83 nodeKey: 'value',
84 expandedKey: ['01'],
85 currentNodeKey: '01',
86 data: [],
87 expandOnNodeClick: false,
88 loading: false,
89 currentObj: {}
90 });
91
92 const standardOptions = ref([])
93 const publishingUnitCodeOptions = ref([])
94
95 const tableSearchInput = ref('')
96 const currTableData: any = ref<Object>({});
97 const page = ref({
98 limit: 50,
99 curr: 1,
100 sizes: [
101 { label: "10", value: 10 },
102 { label: "50", value: 50 },
103 { label: "100", value: 100 },
104 { label: "150", value: 150 },
105 { label: "200", value: 200 },
106 ],
107 });
108 const selectRowData = ref([])
109 const selectedRowData = ref([])
110 const tableInfo: any = ref({
111 id: 'data-source-table',
112 multiple: true,
113 fixedSelection: true,
114 fields: [
115 { label: "序号", type: "index", width: 56, align: "center" },
116 { label: '代码名称', field: 'codeName', width: 140 },
117 // { label: '代码编码', field: 'code', width: 140 },
118 { label: '标准号', field: 'standard', width: 140 },
119 { label: '标准名称', field: 'standardName', width: 140 },
120 // { label: '启用状态', field: 'bizState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 100, align: 'center' },
121 { label: '启用状态', field: 'bizState', type: 'tag', width: 100, align: 'center',getName: (scope) => {
122 let status = scope.row.bizState;
123 return status == 'Y' ? '启用' : '停用';
124 } },
125 { label: '创建时间', field: 'createTime', width: TableColumnWidth }
126 ],
127 data: [],
128 page: {
129 type: "normal",
130 rows: 0,
131 ...page.value,
132 },
133 actionInfo: {
134 label: "操作",
135 type: "btn",
136 width: 172, //不要刚好90.缩小浏览器会因为小数点的差距而换行,
137 fixed: 'right',
138 btns: [
139 { label: '查看', value: 'detail'},
140 { label: "编辑", value: "edit" },
141 { label: '复制', value: 'copy' },
142 { label: "删除", value: "delete" },
143 ],
144 },
145 loading: false
146 })
147
148 const dictionaryType = ref(1)
149 const orginOptions = [
150 { label: 'code', value: 'code' },
151 { label: 'name', value: 'name' },
152 ]
153 let codeOptions = ref([])
154 const formItems: any = ref([
155 {
156 label: '标准类型',
157 type: 'select',
158 placeholder: '请选择',
159 field: 'standardTypeCode',
160 default: '',
161 options: standardOptions,
162 clearable: true,
163 required: true
164 },
165 {
166 label: '代码名称',
167 type: 'input',
168 placeholder: '请输入',
169 field: 'codeName',
170 default: '',
171 maxlength: 30,
172 clearable: true,
173 required: true
174 },
175 {
176 label: '代码编码',
177 type: 'input',
178 placeholder: '请输入',
179 field: 'code',
180 default: '',
181 maxlength: 30,
182 clearable: true,
183 // required: true
184 },
185 {
186 label: '标准号',
187 type: 'input',
188 placeholder: '请输入',
189 field: 'standard',
190 default: '',
191 maxlength: 50,
192 clearable: true,
193 required: true
194 },
195 {
196 label: '标准名称',
197 type: 'input',
198 placeholder: '请输入',
199 field: 'standardName',
200 default: '',
201 maxlength: 30,
202 clearable: true,
203 required: true
204 },
205 {
206 label: '发布单位',
207 type: 'select',
208 placeholder: '请选择',
209 field: 'publishingUnitCode',
210 default: '',
211 options: publishingUnitCodeOptions,
212 clearable: true,
213 required: true
214 },
215 {
216 label: '排序',
217 type: 'input',
218 placeholder: '请输入',
219 field: 'orderNum',
220 default: '',
221 maxlength: 2,
222 clearable: true,
223 required: true
224 },
225 {
226 label: '启用状态',
227 type: 'switch',
228 field: 'bizState',
229 default: 'Y',
230 activeValue: 'Y',
231 inactiveValue: 'N'
232 },
233 {
234 label: '代码类型',
235 type: 'radio-panel',
236 placeholder: '请选择',
237 field: 'typeCode',
238 default: '1',
239 block: true,
240 options: [
241 { label: '列表结构', value: '1' },
242 { label: '层级结构', value: '2' }
243 ],
244 clearable: true,
245 required: true
246 },
247 {
248 label: '编码字段',
249 type: 'select',
250 placeholder: '请选择',
251 field: 'codeFields',
252 default: [],
253 options: codeOptions,
254 clearable: true,
255 required: true,
256 multiple: true,
257 tagsTooltip: true,
258 collapse: true,
259 filterable: true,
260 visible: true
261 },
262 {
263 label: '编码名称',
264 type: 'select',
265 placeholder: '请选择',
266 field: 'codeFieldName',
267 default: '',
268 options: codeOptions,
269 clearable: true,
270 required: true,
271 visible: true
272 },
273 {
274 label: '层级结构',
275 type: 'input',
276 placeholder: '请输入',
277 field: 'hierarchy',
278 default: '',
279 clearable: true,
280 visible: false
281 }
282 ])
283 const formRules: any = ref({
284 standardTypeCode: { required: true, message: '请选择标准类型' },
285 codeName: { required: true, message: '请输入代码名称' },
286 // code: { required: true, message: '请输入代码编码' },
287 standard: { required: true, message: '请输入标准号' },
288 standardName: { required: true, message: '请输入标准名称' },
289 publishingUnitCode: { required: true, message: '请选择发布单位' },
290 orderNum: {
291 required: true,
292 validator: (rule, value, callback) => {
293 if (value === 0) {
294 callback();
295 return;
296 }
297 if (!value) {
298 callback(new Error('请填写排序'));
299 return;
300 }
301 const r = /(^[0-9]([0-9]*)$|^[0-9]$)/; // 正整数(可以以0打头)
302 if (value && !r.test(value)) {
303 callback(new Error('请填写大于或等于零整数'));
304 return;
305 }
306 callback();
307 }
308 },
309 typeCode: { required: true, message: '请选择代码类型' },
310 codeFields: { required: true, message: '请选择编码字段' },
311 codeFieldName: { required: true, message: '请选择编码名称' }
312 })
313 const formInfo = ref({
314 type: 'form',
315 title: '',
316 col: 'span',
317 formInfo: {
318 id: 'add-dict-form',
319 col: 'col2',
320 readonly: false,
321 items: formItems.value,
322 rules: formRules.value
323 }
324 })
325
326 const orginData = [
327 {
328 ROWID: 'field_0',
329 fieldName: '代码',
330 description: '编码字段',
331 fieldType: 'varchar',
332 fieldLength: '10',
333 fieldPrecision: '',
334 // orderNum: '1',
335 codeRuleGuid: '',
336 STATUS: 'edit',
337 STATE: 'Running'
338 }, {
339 ROWID: 'field_1',
340 fieldName: '名称',
341 description: '编码名称',
342 fieldType: 'varchar',
343 fieldLength: '20',
344 fieldPrecision: '',
345 // orderNum: '2',
346 codeRuleGuid: '',
347 STATUS: 'edit',
348 STATE: 'Running'
349 }
350 ]
351 const orginFields = [
352 { label: "序号", type: "index", width: 56, align: "center" },
353 { label: "字段名称", field: "fieldName", width: 140 }
354 ]
355 const formTableData: any = ref([])
356 const formTableSelectRowData: any = ref([])
357 const editTableInfo = ref({
358 fieldName: {
359 label: '字段名称',
360 type: 'input',
361 maxlength: 50,
362 placeholder: '',
363 field: 'fieldName',
364 default: '',
365 clearable: true,
366 required: true,
367 }
368 })
369 const formTable = ref({
370 type: "table",
371 title: "定义表结构",
372 col: 'no-margin',
373 style: {
374 // height: 'calc(100% - 234px)'
375 height: 'auto'
376 },
377 tableInfo: {
378 id: "role-authority-table",
379 multiple: true,
380 minHeight: 'unset',
381 maxHeight: '100%',
382 fields: [],
383 data: [],
384 showPage: false,
385 actionInfo: {
386 show: true,
387 label: "操作",
388 type: "btn",
389 width: 60,
390 fixed: 'right',
391 btns: [
392 { label: "删除", value: "remove" },
393 ]
394 },
395 editInfo: {},
396 readonly: false,
397 // col: 'auto-height',
398 style: {
399 // height: 'calc(100% - 40px)'
400 },
401 loading: false
402 },
403 tableTool: {
404 // col: 'float-right',
405 visible: false,
406 btns: [
407 { label: "新增行", value: "add-row", type: 'primary' },
408 { label: "批量删除", value: "remove_batch" },
409 ]
410 },
411 })
412
413 const fieldSheetInfo: any = ref({})
414 const uploadTableData: any = ref([])
415 const uploadSelectRowData: any = ref([])
416 const uploadTableFields: any = ref([])
417 const uploadTableInfo = ref({
418 id: "role-authority-table",
419 multiple: true,
420 // minHeight: 'unset',
421 // maxHeight: '100%',
422 fields: [],
423 data: [],
424 showPage: false,
425 actionInfo: {
426 label: "操作",
427 type: "btn",
428 width: 60,
429 btns: [
430 { label: "删除", value: "remove" },
431 ],
432 },
433 editInfo: {},
434 readonly: false,
435 loading: false
436 })
437 const uploadFiles: any = ref([])
438 const uploadInfo = ref({
439 type: 'upload',
440 title: '添加表数据',
441 col: 'row-reverse',
442 uploadInfo: {
443 id: 'upload-file-form',
444 action: '',
445 auto: false,
446 fileList: [],
447 accept: '.xlsx, .xls',
448 cover: true,
449 triggerBtn: {
450 label: '导入文件',
451 value: 'import_file',
452 icon: 'Upload',
453 plain: true,
454 },
455 toolBar: [
456 { label: '下载模板', value: 'export_model', plain: true, icon: 'Download' },
457 ]
458 // showList: false,
459 },
460 tableInfo: {},
461 tools: {
462 col: 'right',
463 visible: true,
464 btns: [
465 { label: '树形显示', value: 'show_tree', type: 'primary', plain: true, visible: false },
466 { label: '新增行', value: 'add_row', type: 'primary', plain: true },
467 { label: '批量删除', value: 'remove_batch', plain: true },
468 ]
469 }
470 })
471 const fieldTableInfo = ref({
472 type: 'field-table',
473 title: '表数据',
474 tableInfo: {
475 id: 'dict-field-table',
476 minHeight: 'unset',
477 maxHeight: '100%',
478 fields: [],
479 data: [],
480 loading: false,
481 showPage: false,
482 actionInfo: {
483 show: false
484 },
485 col: 'auto-height'
486 }
487 })
488
489 const contents = ref({
490 add: [
491 formInfo.value,
492 formTable.value,
493 ],
494 upload: [
495 formInfo.value,
496 uploadInfo.value,
497 ],
498 sheet: [
499 formInfo.value,
500 formTable.value,
501 ],
502 field: [
503 formInfo.value,
504 fieldTableInfo.value,
505 ]
506 })
507
508 const drawerRef = ref()
509 const drawerInfo: any = ref({
510 visible: false,
511 direction: "rtl",
512 modalClass: "",
513 modalClose: false,
514 modal: true,
515 size: 700,
516 header: {
517 title: "新增",
518 },
519 type: '',
520 container: {
521 contents: [],
522 },
523 footer: {
524 btns: [
525 { type: 'default', label: '取消', value: 'cancel' },
526 { type: 'primary', label: '保存', value: 'submit', visible: true },
527 { type: 'primary', label: '保存并添加数据', value: 'saveAndAdd', visible: true },
528 ]
529 },
530 })
531
532 const setFormItems = (row: any = null) => {
533 // formItems.value = JSON.parse(JSON.stringify(orginItems))
534 formItems.value.map(item => {
535 item.default = row ? row[item.field] : ''
536
537 })
538 formInfo.value.formInfo.readonly = drawerInfo.value.type == 'detail'
539 formInfo.value.formInfo.items = formItems.value
540 // for(var e in editTableInfo.value){
541 // const editItem = editTableInfo.value[e]
542 // if(editItem.field != 'description' && editItem.field != 'codeRuleGuid'){
543 // editItem.disabled = drawerInfo.value.type != 'add'
544 // }
545 // }
546 }
547
548 // 获取数据类型列表
549 const getDataType = () => {
550 let params = {
551 dictType: "字段类型"
552 }
553 getNewDataTypeList(params).then((res: any) => {
554 if (res.code == proxy.$passCode) {
555 const data = res.data
556 editTableInfo.value['fieldType'].options = data
557 } else {
558 ElMessage({
559 type: 'error',
560 message: res.msg,
561 })
562 }
563 })
564 }
565
566 // 获取数据库列表
567 const getDataBase = () => {
568 const params = {
569 pageIndex: 1,
570 connectStatus: '1'
571 }
572 getDataBaseList(params).then((res: any) => {
573 if (res.code == proxy.$passCode) {
574 const data = res.data ?? []
575 data.map(item => {
576 item.label = item.databaseNameZh
577 item.value = item.guid
578 })
579 dataBaseList.value = data
580 } else {
581 ElMessage({
582 type: 'error',
583 message: res.msg,
584 })
585 }
586 })
587 }
588
589 // 获取编码规则列表
590 const getCodeRuleData = () => {
591 let params = {}
592 getCoderuleList(params).then((res: any) => {
593 if (res.code == proxy.$passCode) {
594 const data = res.data
595 data?.map(item => {
596 item.label = item.ruleName
597 item.value = item.guid
598 })
599 codeRuleList.value = data
600 editTableInfo.value['codeRuleGuid'].options = data
601 } else {
602 ElMessage({
603 type: 'error',
604 message: res.msg,
605 })
606 }
607 })
608 }
609
610 const treePromise = ref();
611
612 const getTreeData = (needClick = false, currData = {}) => {
613 return getParamsList({ dictType: '标准类型'}).then((res:any) => {
614 if (res.code === proxy.$passCode) {
615 const data = res.data || []
616 data.forEach(item => {
617 item.treeLevel = 1
618 })
619 treeInfo.value.data = data
620 standardOptions.value = data
621
622 }
623 })
624 }
625
626 const getFirstPageData = () => {
627 page.value.curr = 1;
628 toSearch({})
629 }
630
631 const toSearch = (val: any, clear: boolean = false) => {
632 let params: any = Object.keys(val).length ? { ...val } : {}
633 let { currentNodeKey, currentObj } = treeInfo.value
634 console.log('currentObj', currentObj)
635 params.pageIndex = page.value.curr;
636 params.pageSize = page.value.limit;
637 params.keyWord = tableSearchInput.value
638 if (currentObj.treeLevel === 1) {
639 params.standardTypeCode = currentNodeKey
640 } else if (currentObj.treeLevel === 2) {
641 params.standardTypeCode = currentObj.standardTypeCode
642 params.standardName = currentObj.value
643 }
644 getTableData(params);
645 };
646
647 const getTableData = (params) => {
648 tableInfo.value.loading = true
649 getStandardCodeList(params).then((res:any) => {
650 if (res.code === proxy.$passCode) {
651 let data = res.data
652 let list = res.data.records || []
653 list = list.map((item,index) => {
654 item.label = item.codeName
655 item.value = item.guid
656 item.index = index
657 return item
658 })
659 tableInfo.value.data = list
660 tableInfo.value.page.limit = data.pageSize
661 tableInfo.value.page.curr = data.pageIndex
662 tableInfo.value.page.rows = data.totalRows
663 }
664 tableInfo.value.loading = false
665 }).catch(xhr => {
666 tableInfo.value.loading = false
667 })
668 };
669
670 const tableSelectionChange = (val, tId) => {
671 val.sort((pre, cur) => pre.index - cur.index)
672 if (drawerInfo.value.visible) {
673 if (formItems.value.length == 2) {
674 uploadSelectRowData.value = val
675 } else {
676 formTableSelectRowData.value = val
677 }
678 } else {
679 selectRowData.value = val.map((item) => item.guid);
680 selectedRowData.value = val
681 }
682 };
683
684 const tablePageChange = (info) => {
685 page.value.curr = Number(info.curr);
686 page.value.limit = Number(info.limit);
687 toSearch({});
688 };
689
690 const tableInputChange = (val, scope) => {
691 if (forbidFields.indexOf(val) > -1) {
692 ElMessage({
693 type: "error",
694 message: '该名称已存在,请填写其他名称',
695 });
696 return
697 }
698 setCodeOptions()
699 }
700
701 const setCodeOptions = () => {
702 let opts: any = []
703 formTableData.value.map(item => {
704 if (item.fieldName) {
705 const row = JSON.parse(JSON.stringify(item))
706 row.label = item.fieldName
707 row.value = item.fieldName
708 opts.push(row)
709 }
710 })
711 codeOptions.value = opts
712 }
713
714 const toolBtnClick = (btn, data) => {
715 console.log(formItems.value.length)
716 const type = btn.value
717 if (data) {
718 if (type.indexOf('add') > -1) {
719 if (formItems.value.length == 2) {
720 const params = {
721 guid: fieldSheetInfo.value.guid
722 }
723 uploadTableInfo.value.loading = true
724 getDictionaryRuleData(params).then((res: any) => {
725 if (res.code == proxy.$passCode) {
726 const data = res.data ?? {}
727 let rowInfo: any = {}
728 uploadTableFields.value.map(item => {
729 rowInfo[item.field] = data[item.field] ?? ''
730 })
731 rowInfo.STATUS = 'edit'
732 rowInfo.STATE = 'Running'
733 rowInfo.ROWID = `upload_${uploadTableData.value.length}`
734 uploadTableData.value.push(rowInfo)
735 uploadTableInfo.value.data = uploadTableData.value
736 scrollTable(rowInfo)
737 } else {
738 ElMessage({
739 type: 'error',
740 message: res.msg
741 })
742 }
743 uploadTableInfo.value.loading = false
744 }).catch(() => {
745 uploadTableInfo.value.loading = false
746 })
747 } else {
748 const rowInfo = {
749 ROWID: `formData_${formTableData.value.length}`,
750 fieldName: '',
751 description: '',
752 fieldType: '',
753 fieldLength: '',
754 fieldPrecision: '',
755 orderNum: '',
756 codeRuleGuid: '',
757 STATUS: 'edit',
758 STATE: 'Running'
759 }
760 let list: any = formTableData.value
761 list.push(rowInfo)
762 formTable.value.tableInfo.data = list
763 scrollTable(rowInfo)
764 }
765 } else if (type.indexOf('remove') > -1) {
766 if (formItems.value.length == 2) {
767 if (uploadSelectRowData.value.length == 0) {
768 ElMessage({
769 type: "error",
770 message: '请选择需要删除的数据',
771 });
772 return
773 }
774 uploadSelectRowData.value.map(item => {
775 const existIndex = uploadTableData.value.findIndex(s => s.ROWID == item.ROWID)
776 existIndex > -1 && uploadTableData.value.splice(existIndex, 1)
777 })
778 } else {
779 if (formTableSelectRowData.value.length == 0) {
780 ElMessage({
781 type: "error",
782 message: '请选择需要删除的数据',
783 });
784 return
785 }
786 const removeRows = () => {
787 formTableSelectRowData.value.map(item => {
788 const existIndex = formTableData.value.findIndex(s => s.ROWID == item.ROWID)
789 existIndex > -1 && formTableData.value.splice(existIndex, 1)
790 })
791 }
792 if (drawerInfo.value.type == 'edit') {
793 const editRows = formTableSelectRowData.value.filter(item => item.guid !== undefined)
794 if (editRows.length) {
795 // formTable.value.tableInfo.loading = true
796 checkDelete().then((res: any) => {
797 if (res) {
798 ElMessageBox.confirm("数据字典有数据, 确定是否继续删除?", "提示", {
799 confirmButtonText: "确定",
800 cancelButtonText: "取消",
801 type: 'warning',
802 }).then(() => {
803 removeRows()
804 })
805 } else {
806 removeRows()
807 }
808 // formTable.value.tableInfo.loading = false
809 }).catch(xhr => {
810 ElMessage({
811 type: 'error',
812 message: xhr
813 })
814 formTable.value.tableInfo.loading = false
815 })
816 } else {
817 removeRows()
818 }
819 } else {
820 removeRows()
821 }
822 }
823 }
824 } else {
825 if (type == 'export_model') {
826 exportData('model')
827 } else if (type == 'import_file') {
828 importData()
829 }
830 }
831 }
832
833 const tableSwitchBeforeChange = (scope, field, callback) => {
834 const msg = '确定修改状态?'
835 ElMessageBox.confirm(
836 msg,
837 '提示',
838 {
839 confirmButtonText: '确定',
840 cancelButtonText: '取消',
841 type: 'warning',
842 }
843 ).then(() => {
844 const state = scope.row[field] == 1 ? 0 : 1
845 const result = tableSwitchChange(state, scope, field)
846 callback(result)
847 }).catch(() => {
848 callback(false)
849 })
850 }
851
852 const tableSwitchChange = (val, scope, field) => {
853 return new Promise((resolve, reject) => {
854 let params = {
855 guid: scope.row.guid,
856 dictionaryState: val
857 }
858 updateDictionaryState(params).then((res: any) => {
859 if (res.code == proxy.$passCode && res.data) {
860 getFirstPageData()
861 ElMessage({
862 type: "success",
863 message: '状态修改成功',
864 });
865 resolve(true)
866 } else {
867 ElMessage({
868 type: "error",
869 message: res.msg,
870 });
871 reject(false)
872 }
873 }).catch(() => {
874 reject(false)
875 })
876 })
877 }
878
879 const tableBtnClick = (scope, btn) => {
880
881 const type = btn.value;
882 const row = scope.row;
883 if (type == "edit" || type === 'copy') {
884 fieldTableInfo.value.tableInfo.fields = []
885 fieldTableInfo.value.tableInfo.data = []
886 drawerInfo.value.header.title = type == 'edit' ? "编辑标准代码" : "复制标准代码";
887 // drawerInfo.value.modalClass = type == 'edit' ? '' : 'wrap_width_auto'
888 drawerInfo.value.type = type
889 drawerInfo.value.footer.btns.forEach(item => {
890 if (item.value === 'submit' || item.value === 'saveAndAdd') {
891 item.visible = type === 'detail' ? false : true
892 }
893 })
894 drawerInfo.value.visible = true;
895 drawerInfo.value.loading = true;
896 formTable.value.tableInfo.actionInfo.show = type === 'detail' ? false : true
897 formTable.value.tableTool.visible = type === 'detail' ? false : true
898 formTable.value.tableInfo.loading = true
899 getStandardCodeDetail(row.guid).then((res: any) => {
900 if (res.code == proxy.$passCode && res.data) {
901 let data = res.data
902 currTableData.value = data;
903 setDetailInfo(type)
904 if (type === 'edit') {
905 checkDelete().then((res:any) => {
906 formItems.value.find(v => v.field === 'typeCode')['disabled'] = res ? true : false
907 })
908 }
909
910 } else {
911 ElMessage({
912 type: "error",
913 message: res.msg,
914 });
915 }
916 formTable.value.tableInfo.loading = false
917 drawerInfo.value.loading = false;
918 }).catch(() => {
919 formTable.value.tableInfo.loading = false
920 drawerInfo.value.loading = false;
921 })
922 // 代码有数据后不允许编辑代码类型
923
924 } else if (type === 'remove') {
925 let removeRows = () => {
926 const existIndex = formTableData.value.findIndex(s => {
927 if (s.ROWID) {
928 return s.ROWID == row.ROWID
929 }
930 return s.guid == row.guid
931 })
932 existIndex > -1 && formTableData.value.splice(existIndex, 1)
933 }
934 if (drawerInfo.value.type == 'edit') {
935 checkDelete().then((res:any) => {
936 if (res) {
937 ElMessageBox.confirm("标准代码有数据, 确定是否继续删除?", "提示", {
938 confirmButtonText: "确定",
939 cancelButtonText: "取消",
940 type: 'warning',
941 }).then(() => {
942 removeRows()
943 })
944 } else {
945 removeRows()
946 }
947 })
948 } else {
949 removeRows()
950 }
951 }
952 else if (type == "delete") {
953 currTableData.value = row;
954 tableInfo.value.loading = true
955 open("此操作将永久删除, 是否继续?", "warning");
956 tableInfo.value.loading = false
957 } else if (type == 'detail') {
958 showFiledsPage.value = true
959 nextTick(() => {
960 dictFiledsRef.value.standardGuid = row.guid
961 dictFiledsRef.value.standardName = row.standardName
962 treeCurrentNodeKey.value = row.guid
963 treeInfo.value.currentNodeKey = row.guid
964 dictFiledsRef.value.getFirstPageData()
965 })
966 }
967 }
968
969 const checkDelete = (isBatch: any = false) => {
970 return new Promise((resolve, reject) => {
971 let params: any = {
972 pageIndex: 1,
973 pageSize: -1,
974 standardGuid: currTableData.value.guid
975 }
976
977 getStandardCodeDataList(params).then((res: any) => {
978 if (res.code == proxy.$passCode) {
979 if (res.data.records.length) {
980 resolve(res.data)
981 } else {
982 resolve(null)
983 }
984 } else {
985 reject(res.msg)
986 }
987 }).catch((xhr: any) => {
988 reject(xhr.msg)
989 })
990 })
991 }
992
993 const open = (msg, type, isBatch = false) => {
994 ElMessageBox.confirm(msg, "提示", {
995 confirmButtonText: "确定",
996 cancelButtonText: "取消",
997 type: type,
998 }).then(() => {
999 let guids = [currTableData.value.guid]
1000 if (isBatch) {
1001 guids = selectRowData.value
1002 }
1003 tableInfo.value.loading = true
1004 deleteStandardCode(guids).then((res: any) => {
1005 if (res.code == proxy.$passCode) {
1006 initTree().then(() => getFirstPageData())
1007 // getFirstPageData()
1008 ElMessage({
1009 type: "success",
1010 message: "删除成功",
1011 });
1012 } else {
1013 ElMessage({
1014 type: "error",
1015 message: res.msg,
1016 });
1017 }
1018 tableInfo.value.loading = false
1019 }).catch(() => {
1020 tableInfo.value.loading = false
1021 });
1022 });
1023 };
1024
1025 const onUpload = (file, fileList) => {
1026 uploadFiles.value = fileList
1027 }
1028
1029 const uploadFile = (file) => {
1030 importData(file)
1031 }
1032
1033 const exportData = (type: any = null) => {
1034 let body = {
1035 standardCodeGuid: treeCurrentNodeKey.value,
1036 standardCodeDataGuids: []
1037 }
1038 exportStandardCodeData(body).then((res:any) => {
1039 if (res && !res.msg) {
1040 download(res, '标准代码表.xlsx', 'excel')
1041 } else {
1042 res?.msg && ElMessage.error(res?.msg);
1043 }
1044 })
1045 return
1046 }
1047
1048 const importData = (file: any = null) => {
1049 let params = new FormData()
1050 if (file) {
1051 params.append("file", file.raw);
1052 } else {
1053 if (uploadFiles.value.length) {
1054 uploadFiles.value.forEach((item: any, index: number) => {
1055 params.append("file", item.raw);
1056 });
1057 }
1058 }
1059
1060 params.append("dictionaryGuid", fieldSheetInfo.value.guid);
1061 showDictionary(params).then((res: any) => {
1062 if (res.code == proxy.$passCode) {
1063 let data = res.data ?? []
1064 setUploadDataInfo(data)
1065 } else {
1066 ElMessage({
1067 type: "error",
1068 message: res.msg,
1069 });
1070 }
1071 })
1072 }
1073
1074 // 生成表头
1075 const setUploadDataFields = (data) => {
1076 let fields: any = [], editInfo: any = {}
1077 data.map(item => {
1078 let fieldItem: any = {
1079 label: item.description, field: item.fieldName, width: 140
1080 }
1081 if (drawerInfo.value.type != 'detail') {
1082 fieldItem.type = 'edit'
1083 fieldItem.columClass = 'edit-colum'
1084 editInfo[item.fieldName] = {
1085 label: '',
1086 type: 'input',
1087 placeholder: '',
1088 field: item.fieldName,
1089 default: '',
1090 disabled: item.codeRuleGuid ? true : false,
1091 clearable: true,
1092 }
1093 }
1094 fields.push(fieldItem)
1095 })
1096 uploadTableFields.value = fields
1097 if (drawerInfo.value.type == 'detail') {
1098 fieldTableInfo.value.tableInfo.fields = uploadTableFields.value
1099 } else {
1100 uploadTableInfo.value.fields = uploadTableFields.value
1101 uploadTableInfo.value.editInfo = editInfo
1102 }
1103 }
1104
1105 // 生成表数据
1106 const setUploadDataInfo = async (info) => {
1107 let data = info
1108 if (drawerInfo.value.type == 'detail') {
1109 data = info.jsonArray ?? []
1110 const fields = info.schemaDataVOS ?? []
1111 setUploadDataFields(fields)
1112 }
1113
1114 if (drawerInfo.value.type == 'detail') {
1115 fieldTableInfo.value.tableInfo.data = data
1116 drawerInfo.value.container.contents = contents.value['field']
1117 } else {
1118 // 设置表数据
1119 data.map((item, i) => {
1120 item.STATUS = 'edit'
1121 item.STATE = 'Running'
1122 item.ROWID = `upload_${i}`
1123 })
1124 uploadTableData.value = data
1125 uploadTableInfo.value.data = uploadTableData.value
1126 }
1127 }
1128
1129 const loadDrawer = async () => {
1130 drawerInfo.value.visible = true;
1131 drawerInfo.value.type = 'add';
1132 drawerInfo.value.header.title = '新增标准代码';
1133 drawerInfo.value.footer.visible = true;
1134 // drawerInfo.value.header.title = '新增数据字典'
1135 // drawerInfo.value.type = 'add'
1136 // drawerInfo.value.modalClass = '';
1137 // await setFormItems()
1138 let fields = JSON.parse(JSON.stringify(orginFields))
1139 fields.map((item: any) => {
1140 if (!item.type || item.type != 'index') {
1141 item.type = 'edit'
1142 item.columClass = 'edit-colum'
1143 }
1144 })
1145 formTable.value.tableInfo.fields = fields
1146 formItems.value.forEach(item => {
1147 if (item.field === 'bizState') {
1148 item.default = 'Y'
1149 } else if (item.field === 'typeCode') {
1150 item.default = '1'
1151 } else if (item.field === 'hierarchy') {
1152 item.default = ''
1153 item.visible = false
1154 }
1155 else {
1156 item.default = ''
1157 }
1158 item.disabled = false
1159 })
1160
1161 formInfo.value.formInfo.items = formItems.value
1162 formTableData.value = JSON.parse(JSON.stringify(orginData))
1163 formTable.value.tableInfo.data = formTableData.value
1164 formTable.value.tableInfo.editInfo = JSON.parse(JSON.stringify(editTableInfo.value))
1165 formTable.value.tableInfo.readonly = false
1166 formTable.value.tableInfo.multiple = true
1167 formTable.value.tableInfo.actionInfo.show = true
1168 formTable.value.tableTool.visible = true
1169 drawerInfo.value.container.contents = contents.value['add']
1170 drawerInfo.value.footer.btns.forEach(item => {
1171 item.visible = true
1172 })
1173 drawerInfo.value.visible = true
1174 console.log('table', formTable.value)
1175 console.log('formInfo', formInfo)
1176 setCodeOptions()
1177 };
1178
1179 const treeCurrentNodeKey = ref('')
1180 const batching = (type) => {
1181 if (type == 'delete') {
1182 if (selectRowData.value.length == 0) {
1183 ElMessage({
1184 type: 'error',
1185 message: '请选择需要删除的数据',
1186 })
1187 return
1188 }
1189 tableInfo.value.loading = true
1190 open("此操作将永久删除, 是否继续?", "warning", true);
1191 tableInfo.value.loading = false
1192 }
1193 if (type == 'export') {
1194 if (selectRowData.value.length == 0) {
1195 ElMessage({
1196 type: 'error',
1197 message: '请选择代码名称',
1198 })
1199 return
1200 }
1201 if (selectRowData.value.length > 30) {
1202 ElMessage.error('选择的代码名称不能超过30个')
1203 return
1204 }
1205 // console.log(selectedRowData)
1206 let uploadSetting = selectedRowData.value.map(item => {
1207 return {
1208 standardName: item.codeName,
1209 standardGuid: item.guid
1210 }
1211 })
1212 cacheStore.setCatch('uploadSetting', uploadSetting)
1213 router.push({
1214 path: '/data-meta/metadata-standard/standard-import',
1215 });
1216 }
1217 };
1218
1219 const nodeClick = (data) => {
1220 console.log('treeNodeClick', data)
1221 drawerInfo.value.visible = false
1222 const changeCont = () => {
1223 nextTick(() => {
1224 treeInfo.value.currentNodeKey = data.value
1225 treeInfo.value.currentObj = data
1226 if (data.isLeaf) {
1227 showFiledsPage.value = true
1228 nextTick(() => {
1229 dictFiledsRef.value.standardGuid = data.value
1230 dictFiledsRef.value.standardName = data.label
1231 treeCurrentNodeKey.value = data.value
1232 dictFiledsRef.value.getFirstPageData()
1233 })
1234 } else {
1235 showFiledsPage.value = false
1236 getFirstPageData()
1237 }
1238 })
1239 }
1240 if (showFiledsPage.value) {
1241 const toChange = dictFiledsRef.value.checkSave()
1242 if (!toChange) {
1243 ElMessageBox.confirm(
1244 '存在未保存的数据,切换后会丢失,是否确定切换',
1245 '提示',
1246 {
1247 confirmButtonText: '确定',
1248 cancelButtonText: '取消',
1249 type: 'warning',
1250 }
1251 ).then(() => {
1252 changeCont()
1253 }).catch(() => {
1254 treeInfo.value.currentNodeKey = dictGuid.value
1255 })
1256 } else {
1257 changeCont()
1258 }
1259 } else {
1260 changeCont()
1261 }
1262 }
1263 function loadTreeNode (node, resolve) {
1264 console.log('node', node)
1265 if (node.isLeaf) {
1266 return resolve([]);
1267 }
1268 if (node.level === 1) {
1269 let standardTypeCode = node.data.value
1270 getStandardCodeStandard(standardTypeCode).then((res:any) => {
1271 if (res.code === proxy.$passCode) {
1272 let data = res.data || []
1273 let uniqList = Array.from(new Set(data)).map(item => {
1274 return {
1275 label: item,
1276 value: item,
1277 treeLevel: 2,
1278 standardTypeCode
1279 }
1280 })
1281 resolve(uniqList)
1282 }
1283 })
1284 }
1285 if (node.level === 2) {
1286 let params = {
1287 pageIndex: 1,
1288 pageSize: -1,
1289 standardTypeCode: node.data.standardTypeCode,
1290 standardName: node.data.value
1291 }
1292 getStandardCodeList(params).then((res:any) => {
1293 if (res.code === proxy.$passCode) {
1294 const list = res.data.records || []
1295 list.forEach(item => {
1296 item.isLeaf = true
1297 item.label = item.codeName
1298 item.value = item.guid
1299 })
1300 resolve(list)
1301 }
1302 })
1303 }
1304 }
1305
1306 // 设置详情信息
1307 const setDetailInfo = (type) => {
1308 console.log('type', type)
1309 const row = JSON.parse(JSON.stringify(currTableData.value))
1310 formItems.value.forEach(item => {
1311 item.default = row[item.field] || ''
1312 item.disabled = type === 'detail' ? true : false
1313 })
1314 formInfo.value.formInfo.items = formItems.value
1315 formTableData.value = row.standardCodeFields.map(item => {
1316 item.STATUS = 'edit'
1317 return item
1318 })
1319
1320 let fields = JSON.parse(JSON.stringify(orginFields))
1321 fields.map((item: any) => {
1322 if (item.type != 'index') {
1323 item.type = 'edit'
1324 item.columClass = 'edit-colum'
1325 }
1326 })
1327 formTable.value.tableInfo.fields = fields
1328 formTable.value.tableInfo.data = formTableData.value
1329 formTable.value.tableInfo.editInfo = JSON.parse(JSON.stringify(editTableInfo.value))
1330 formTable.value.tableInfo.readonly = type === 'detail' ? true : false
1331 formTable.value.tableInfo.multiple = true
1332 // formTable.value.tableInfo.actionInfo.show = true
1333 // formTable.value.tableTool.visible = true
1334 drawerInfo.value.container.contents = contents.value['add']
1335 drawerInfo.value.visible = true
1336
1337 // if (row.typeCode)
1338 formItems.value.find(v => v.field === 'hierarchy')['visible'] = row.typeCode == 1 ? false : true
1339
1340 setCodeOptions()
1341 }
1342
1343 const saveData = async (params, btnValue) => {
1344 // const passInfo = await checkParamsData()
1345 console.log('params', params)
1346 let request = drawerInfo.value.type === 'edit' ? updateStandardCode : saveStandardCode
1347 request(params).then((res:any) => {
1348 if (res.code === proxy.$passCode) {
1349 ElMessage.success('操作成功')
1350 initTree().then(() => {
1351 drawerInfo.value.visible = false
1352 if (btnValue === 'saveAndAdd') {
1353 // 跳转代码详情
1354 toStandardCodeDetail(res.data)
1355 } else {
1356 getFirstPageData()
1357 }
1358 })
1359 } else {
1360 ElMessage.error(res.msg)
1361 }
1362 }).finally(() => {
1363 drawerInfo.value.footer.btns.map((item: any) => item.disabled = false)
1364 })
1365 }
1366 function toStandardCodeDetail (guid) {
1367 if (drawerInfo.value.type === 'add' || drawerInfo.value.type === 'copy') {
1368 getStandardCodeDetail(guid).then((res:any) => {
1369 if (res.code == proxy.$passCode) {
1370 let { guid, codeName } = res.data
1371 showFiledsPage.value = true
1372 nextTick(() => {
1373 dictFiledsRef.value.standardGuid = guid
1374 dictFiledsRef.value.standardName = codeName
1375 treeCurrentNodeKey.value = guid
1376 treeInfo.value.currentNodeKey = guid
1377 dictFiledsRef.value.getFirstPageData()
1378 })
1379 }
1380 })
1381 } else {
1382 console.log('currentData', currTableData.value)
1383 let { guid, codeName } = currTableData.value
1384 showFiledsPage.value = true
1385 nextTick(() => {
1386 dictFiledsRef.value.standardGuid = guid
1387 dictFiledsRef.value.standardName = codeName
1388 treeCurrentNodeKey.value = guid
1389 treeInfo.value.currentNodeKey = guid
1390 dictFiledsRef.value.getFirstPageData()
1391 })
1392 }
1393
1394 }
1395
1396 const scrollTable = (rowInfo) => {
1397 nextTick(() => {
1398 const drawerBody = document.getElementsByClassName('el-drawer__body')[0];
1399 const tableListRef = drawerRef.value.getDrawerConRef("drawerTableRef");
1400 if (!tableListRef) return;
1401 //设置选中表格当前新增行。
1402 tableListRef.setCurrentRow(rowInfo);
1403 drawerBody.scrollTop = drawerBody.scrollHeight;
1404 let table = tableListRef.layout.table.refs;
1405 // 获取表格滚动元素
1406 let tableScrollEle =
1407 table.bodyWrapper.firstElementChild.firstElementChild;
1408 // 设置表格滚动的位置
1409 tableScrollEle.scrollTop = tableScrollEle.scrollHeight;
1410 });
1411 };
1412
1413 const drawerBtnClick = (btn, info) => {
1414 console.log('btn', btn, info)
1415 console.log('table', formTable.value)
1416
1417 let params = {
1418 standardCodeFields: formTable.value.tableInfo.data.map(item => {
1419 let obj = {
1420 fieldName: item.fieldName,
1421 guid: item.guid || null,
1422 standardGuid: item.standardGuid || null
1423 }
1424 return obj
1425 }),
1426 ...info
1427 }
1428 if (params.typeCode === '1' || !params.hierarchy) {
1429 delete params.hierarchy
1430 }
1431 if (drawerInfo.value.type === 'edit') {
1432 params.guid = currTableData.value.guid
1433 }
1434 if (btn.value == 'submit' || btn.value == 'saveAndAdd') {
1435 let uniqFieldNameList = Array.from(new Set(formTable.value.tableInfo.data.map(item => item.fieldName)))
1436 if (uniqFieldNameList.length !== formTable.value.tableInfo.data.length) {
1437 ElMessage.error('表结构字段名称需唯一,不允许重名')
1438 return
1439 }
1440 let standardCodeFields = params.standardCodeFields.map(v => v.fieldName).join()
1441 let codeFieldName = params.codeFieldName
1442 let codeFields = params.codeFields
1443 let fieldValidate = false
1444 if (standardCodeFields.includes(codeFieldName) && codeFields.every(v => standardCodeFields.includes(v))) {
1445 fieldValidate = true
1446 }
1447 if (!fieldValidate) {
1448 ElMessage.error('所选的编码字段和编码名称必须在字段名称中有')
1449 return
1450 }
1451 drawerInfo.value.footer.btns.map((item: any) => item.disabled = true)
1452 saveData(params,btn.value)
1453 } else {
1454 drawerInfo.value.footer.btns.map((item: any) => delete item.disabled)
1455 nextTick(() => {
1456 drawerInfo.value.visible = false
1457 })
1458 }
1459 }
1460
1461 const radioGroupChange = async (val, info) => {
1462 // console.log(val, info)
1463 await setFormItems(info)
1464 // setGroup()
1465 if (val == 1) {
1466 // 列表结构
1467 formItems.value.find(v => v.field === 'hierarchy')['visible'] = false
1468 } else {
1469 formItems.value.find(v => v.field === 'hierarchy')['visible'] = true
1470 }
1471 }
1472
1473 function initTree () {
1474 return Promise.all([getParamsList({ dictType: '标准类型'}), getStandardCodeTree()]).then((resList:any) => {
1475 let treeRoot = resList[0].data || []
1476 let treeData = resList[1].data || []
1477 // console.log('treeRoot', treeRoot)
1478 // console.log('treeData', treeData)
1479 treeData.forEach(item => {
1480 if (item.children) {
1481 item.children.forEach(subItem => {
1482 // 二级标准name 为 value
1483 subItem.treeLevel = 2
1484 subItem.label = subItem.name
1485 subItem.value = subItem.name
1486 if (subItem.children) {
1487 subItem.children.forEach(threeItem => {
1488 threeItem.label = threeItem.name
1489 threeItem.value = threeItem.guid
1490 threeItem.isLeaf = threeItem.isCode
1491 })
1492 }
1493 })
1494 }
1495 })
1496 let tree = treeRoot.map(item => {
1497 item.treeLevel = 1
1498 let target = treeData.find(v => v.guid === item.value)
1499 item.children = target ? target.children : null
1500 return item
1501 })
1502 // console.log('treeData', treeData)
1503 // console.log('tree', tree)
1504 standardOptions.value = treeRoot
1505 treeInfo.value.data = tree
1506 })
1507 }
1508
1509 onBeforeMount(() => {
1510 // getDataType()
1511 initTree().then(() => {
1512 nodeClick(treeInfo.value.data[0])
1513 })
1514 // getTreeData().then(() => {
1515 // // 默认展开第一个
1516 // let data = treeInfo.value.data
1517 // if (data.length === 0) return
1518 // let params = {
1519 // pageIndex: 1,
1520 // pageSize: 50,
1521 // standardTypeCode: data[0].value,
1522 // // codeName: '标准类型'
1523 // }
1524 // getStandardCodeList(params).then((res:any) => {
1525 // if (res.code === proxy.$passCode) {
1526 // let data = res.data
1527 // data.records && data.records.forEach((item,index) => item.index = index)
1528 // tableInfo.value.data = data.records
1529 // tableInfo.value.page.limit = data.pageSize
1530 // tableInfo.value.page.curr = data.pageIndex
1531 // tableInfo.value.page.rows = data.totalRows
1532 // }
1533 // })
1534 // })
1535 getParamsList({ dictType: '发布单位' }).then((res:any) => {
1536 if (res.code === proxy.$passCode) {
1537 const data = res.data || []
1538 publishingUnitCodeOptions.value = data
1539 }
1540 })
1541 })
1542
1543 onMounted(() => {
1544
1545 });
1546
1547 </script>
1548
1549 <template>
1550 <div class="container_wrap full flex">
1551 <div class="aside_wrap">
1552 <div class="aside_title">标准代码列表</div>
1553 <!-- <Tree ref="dictTreeRef" :treeInfo="treeInfo" @nodeClick="nodeClick" @loadNode="loadTreeNode"/> -->
1554 <Tree ref="dictTreeRef" :treeInfo="treeInfo" @nodeClick="nodeClick" />
1555 </div>
1556 <DictFileds ref="dictFiledsRef" v-if="showFiledsPage" @exportData="exportData" />
1557 <div class="main_wrap" v-else>
1558 <div class="table_tool_wrap">
1559 <div class="tools_btns">
1560 <el-button type="primary" @click="loadDrawer" v-preReClick>新建</el-button>
1561 <el-button @click="batching('export')" v-preReClick>批量导入</el-button>
1562 <el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
1563 </div>
1564 <el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="请输入关键字搜索"
1565 :suffix-icon="Search" clearable @change="val => getFirstPageData()" />
1566 </div>
1567 <div class="table_panel_wrap full">
1568 <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
1569 @tablePageChange="tablePageChange" @tableSwitchBeforeChange="tableSwitchBeforeChange" />
1570 </div>
1571 </div>
1572 <Drawer ref="drawerRef"
1573 class="table-no-scorll"
1574 :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick"
1575 @radioGroupChange="radioGroupChange" @drawerTableBtnClick="tableBtnClick"
1576 @drawerTableSelectionChange="tableSelectionChange" @drawerTableToolBtnClick="toolBtnClick"
1577 @drawerTableInputChange="tableInputChange" @drawerToolBtnClick="toolBtnClick" @onUpload="onUpload"
1578 @uploadFile="uploadFile" @uploadBtnClick="toolBtnClick" />
1579 </div>
1580 </template>
1581
1582 <style lang="scss" scoped>
1583 .container_wrap {
1584 .aside_wrap {
1585 width: 200px;
1586 }
1587 }
1588
1589 .tree_panel {
1590 height: 100%;
1591 padding-top: 0;
1592
1593 :deep(.el-tree) {
1594 margin: 0;
1595 height: calc(100% - 68px);
1596 overflow: hidden auto;
1597 }
1598
1599 }
1600 </style>
1601 <style lang="scss">
1602 .table-no-scorll {
1603 .el-scrollbar__bar.is-vertical {
1604 display: none!important;
1605 }
1606 }
1607 /* .el-scrollbar__bar.is-vertical {
1608 display: none!important;
1609 } */
1610 </style>
1 <route lang="yaml">
2 name: metadataStandardCodetable
3 </route>
4
5 <script lang="ts" setup name="metadataStandardCodetable">
6 import { ref, reactive } from 'vue';
7 import { ElMessage } from "element-plus";
8 import Tree from '@/components/Tree/index.vue';
9 import TableTools from '@/components/Tools/table_tools.vue';
10 import { Search } from '@element-plus/icons-vue'
11 import { useRouter, useRoute } from "vue-router";
12 import { TableColumnWidth } from '@/utils/enum';
13 import { commonPageConfig } from '@/components/PageNav/index';
14 import { getParamsList } from '@/api/modules/dataAsset'
15 import { getStandardCodeList, saveStandardCode, updateStandardCode } from '@/api/modules/dataMetaService'
16
17 const { proxy } = getCurrentInstance() as any;
18 const router = useRouter();
19
20 const treeInfo:any = reactive({
21 id: "data-pickup-tree",
22 filter: true,
23 queryValue: "",
24 queryPlaceholder: "请输入关键字搜索",
25 props: {
26 label: "label",
27 value: "value",
28 },
29 nodeKey: 'value',
30 expandedKey: ['01'],
31 currentNodeKey: '01',
32 expandOnNodeClick: false,
33 data: [],
34 loading: false
35 });
36 function nodeClick (data) {
37
38 }
39
40 /** 分页及搜索传参信息配置。 */
41 const tableSearchInput = ref('')
42 function getFirstPageData () {
43 page.value.curr = 1
44 toSearch({})
45 }
46 function toSearch (val: any, clear: boolean = false) {
47 let params: any = Object.keys(val).length ? { ...val } : {}
48 params.pageIndex = page.value.curr;
49 params.pageSize = page.value.limit;
50 params.name = tableSearchInput.value
51 getTableData(params);
52 }
53 const page = ref({
54 ...commonPageConfig,
55 collectTaskName: '',
56 dataSourceGuid: '',
57 taskState: null
58 });
59 const tableInfo = ref({
60 id: 'data-source-table',
61 fields: [
62 { label: '序号', type: 'index', width: TableColumnWidth.INDEX, align: 'center' },
63 { label: '代码名称', field: '1', width: 140 },
64 { label: '标准号', field: '2', width: 140 },
65 { label: '标准名称', field: '3', width: 140 },
66 { label: '启用状态', field: '4', width: 140 },
67 { label: '创建时间', field: '5', width: 140 }
68 ],
69 data: [],
70 page: {
71 type: 'normal',
72 rows: 0,
73 ...page.value
74 },
75 actionInfo: {
76 label: '操作',
77 type: 'btn',
78 width: 160,
79 btns: scope => {
80 let row = scope.row
81 return [
82 { label: '编辑', value: 'edit' },
83 { label: '删除', value: 'delete' },
84 ]
85 }
86 },
87 loading: false
88 })
89 function tableBtnClick (scope, btn) {
90
91 }
92 function tablePageChange (info) {
93 page.value.curr = Number(info.curr);
94 page.value.limit = Number(info.limit);
95 // getTableData();
96 }
97 function getTableData (params) {
98
99 }
100 function handleCreate () {
101 drawerInfo.value.visible = true;
102 drawerInfo.value.type = 'add';
103 drawerInfo.value.header.title = '新增标准代码';
104 drawerInfo.value.footer.visible = true;
105 }
106
107 // drawer form
108 const standardOptions = ref([])
109 const publishingUnitCodeOptions = ref([])
110 const formRef = ref()
111 const formItems: any = ref([
112 {
113 label: '标准类型',
114 type: 'select',
115 placeholder: '请选择',
116 field: 'standardTypeCode',
117 default: '',
118 options: standardOptions,
119 clearable: true,
120 required: true
121 },
122 {
123 label: '代码名称',
124 type: 'input',
125 placeholder: '请输入',
126 field: 'codeName',
127 default: '',
128 maxlength: 20,
129 clearable: true,
130 required: true
131 },
132 {
133 label: '标准号',
134 type: 'input',
135 placeholder: '请输入',
136 field: 'standard',
137 default: '',
138 maxlength: 50,
139 clearable: true,
140 required: true
141 },
142 {
143 label: '标准名称',
144 type: 'input',
145 placeholder: '请输入',
146 field: 'standardName',
147 default: '',
148 maxlength: 20,
149 clearable: true,
150 required: true
151 },
152 {
153 label: '发布单位',
154 type: 'select',
155 placeholder: '请输入',
156 field: 'publishingUnitCode',
157 default: '',
158 options: publishingUnitCodeOptions,
159 clearable: true,
160 required: true
161 },
162 {
163 label: '排序',
164 type: 'inputNumber',
165 placeholder: '请输入',
166 field: 'orderNum',
167 default: '',
168 maxlength: 2,
169 clearable: true,
170 required: true
171 },
172 {
173 label: '启用状态',
174 type: 'switch',
175 field: 'bizState',
176 default: false
177 },
178 {
179 label: '代码类型',
180 type: 'select',
181 placeholder: '请选择',
182 field: 'typeCode',
183 default: '1',
184 options: [
185 { label: '列表结构', value: '1' },
186 { label: '层级结构', value: '2' }
187 ],
188 clearable: true,
189 required: true
190 },
191 {
192 label: '编码字段',
193 type: 'select',
194 placeholder: '请选择',
195 field: 'codeFields',
196 default: [],
197 options: [
198 { label: 'code', value: 'code' },
199 { label: 'name', value: 'name' }
200 ],
201 clearable: true,
202 required: true,
203 multiple: true
204 },
205 {
206 label: '编码名称',
207 type: 'select',
208 placeholder: '请选择',
209 field: 'codeFieldName',
210 default: '',
211 options: [
212 { label: 'code', value: 'code' },
213 { label: 'name', value: 'name' }
214 ],
215 clearable: true,
216 required: true
217 },
218 {
219 label: '层级结构',
220 type: 'input',
221 placeholder: '请输入',
222 field: 'hierarchy',
223 default: '',
224 clearable: true,
225 required: true
226 }
227 ])
228 const formRules = ref({
229
230 })
231 const formInfo = ref({
232 type: 'form',
233 title: '',
234 col: 'span',
235 formInfo: {
236 id: 'add-dict-form',
237 col: 'col2',
238 items: formItems.value,
239 formRules: formRules.value
240 }
241 })
242 const formTable = reactive({
243 type: "table",
244 title: "定义表结构",
245 col: 'no-margin',
246 style: {
247 height: 'calc(100% - 234px)'
248 },
249 tableInfo: {
250 id: "role-authority-table",
251 multiple: true,
252 minHeight: 'unset',
253 maxHeight: '100%',
254 fields: [
255 { label: '序号', type: 'index', width: 56, align: 'center' },
256 { label: '字段名称', field: 'fieldName', width: 140, type: 'edit', customClass: 'edit-colum'}
257 ],
258 data: [{ fieldName: '1', index: 0}],
259 showPage: false,
260 actionInfo: {
261 show: true,
262 label: "操作",
263 type: "btn",
264 width: 60,
265 fixed: 'right',
266 btns: [{ label: "删除", value: "remove" }]
267 },
268 editInfo: {
269 fieldName: {
270 field: 'fieldName',
271 label: '字段名称',
272 type: 'input',
273 clearable: true,
274 maxlength: 20
275 }
276 },
277 readonly: false,
278 col: 'auto-height',
279 style: { height: 'calc(100% - 40px)'},
280 loading: false
281 },
282 tableTool: {
283 col: 'float-right',
284 visible: true,
285 btns: [
286 { label: "新增行", value: "tableAdd", type: 'primary' },
287 { label: "批量删除", value: "tableDelete" },
288 ]
289 },
290 })
291 // 新增drawer
292 const drawerInfo = ref({
293 visible: false,
294 direction: 'rtl',
295 size: 550,
296 header: {
297 title: '新增标准代码',
298 },
299 type: '',
300 container: {
301 contents: [formInfo.value, formTable],
302 },
303 footer: {
304 visible: true,
305 btns: [
306 { type: 'default', label: '取消', value: 'cancel' },
307 { type: 'primary', label: '保存', value: 'save' },
308 { type: 'primary', label: '保存并添加数据', value: 'saveAndAdd', visible: true }
309 ]
310 }
311 })
312 async function drawerBtnClick (btn, info) {
313
314 }
315 function drawerTableToolBtnClick (btn, data) {
316 console.log(btn, data)
317 if (btn.value === 'tableAdd') {
318 // 定义表结构新增行
319 let rowInfo = { fieldName: '' }
320 formTable.tableInfo.data.push(rowInfo)
321 }
322 }
323
324 onMounted(() => {
325 getParamsList({ dictType: '标准类型'}).then((res:any) => {
326 if (res.code === proxy.$passCode) {
327 const data = res.data || []
328 treeInfo.data = data
329 standardOptions.value = data
330 // 默认展开第一个
331 if (data.length === 0) return
332 let params = {
333 pageIndex: 1,
334 pageSize: 100,
335 standardTypeCode: data[0].value,
336 codeName: '标准类型'
337 }
338 getStandardCodeList(params)
339 }
340 })
341 getParamsList({ dictType: '发布单位' }).then((res:any) => {
342 if (res.code === proxy.$passCode) {
343 const data = res.data || []
344 publishingUnitCodeOptions.value = data
345 }
346 })
347 })
348 </script>
349
350 <template>
351 <div class="container_wrap full flex">
352 <div class="aside_wrap">
353 <div class="aside_title">标准代码列表</div>
354 <Tree :treeInfo="treeInfo" @nodeClick="nodeClick" />
355 </div>
356 <div class="main_wrap">
357 <div class="table_tool_wrap">
358 <div class="tools_btns">
359 <el-button type="primary" @click="handleCreate">新建</el-button>
360 <el-button>批量删除</el-button>
361 <el-button>批量导入</el-button>
362 </div>
363 <el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="代码名称搜索"
364 :suffix-icon="Search" clearable @change="val => getFirstPageData()" />
365 </div>
366 <div class="table_panel_wrap">
367 <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tablePageChange="tablePageChange"
368 />
369 </div>
370 </div>
371 <Drawer
372 :drawerInfo="drawerInfo"
373 @drawerBtnClick="drawerBtnClick"
374 ref="drawerRef"
375 @drawerTableToolBtnClick="drawerTableToolBtnClick"
376 >
377 <!-- <Form ref="formRef"
378 :itemList="formItems"
379 formId="basic-info-form"
380 :rules="formRules"
381 /> -->
382 </Drawer>
383 </div>
384 </template>
385
386 <style scoped lang="scss">
387 .container_wrap {
388
389 .aside_wrap {
390 width: 200px;
391 }
392
393 .main_wrap {
394 padding: 10px 20px;
395
396
397 :deep(.el-tabs) {
398 height: 100%;
399
400 .el-tabs__header {
401 margin-bottom: 0;
402 }
403
404 .el-tabs__item {
405 height: 32px;
406 padding: 0px;
407 width: 144px;
408
409 &:last-child {
410 width: 130px;
411 }
412 }
413
414 .el-tabs__content {
415 height: calc(100% - 32px);
416 }
417
418 .el-tab-pane {
419 padding: 0px 16px;
420 height: 100%;
421 }
422 }
423 }
424
425 }
426
427 .tree_panel {
428 height: calc(100% - 36px);
429 padding-top: 0;
430
431 :deep(.el-tree) {
432 margin: 0;
433 overflow: hidden auto;
434 }
435 }
436
437 .table_tool_wrap {
438 display: flex;
439 flex-direction: column;
440
441 .el-input {
442 width: 230px;
443 height: 32px;
444 }
445
446 :deep(.el-input) {
447 .el-input__suffix-inner {
448 flex-direction: row-reverse;
449 -webkit-flex-direction: row-reverse;
450 display: flex;
451 }
452 }
453 }
454
455 .table_panel_wrap {
456 height: calc(100% - 44px);
457 }
458 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: metadataStandardImport
3 </route>
4
5 <script lang="ts" setup name="metadataStandardImport">
6 import { ref } from "vue";
7 import { useRoute, useRouter } from "vue-router"
8 import useUserStore from "@/store/modules/user";
9 import { ElMessage, ElMessageBox } from "element-plus";
10 import Tabs from '@/components/Tabs/index.vue'
11 import Table from '@/components/Table/index.vue'
12 import Dialog from '@/components/Dialog/index.vue'
13 import useCatchStore from "@/store/modules/catch";
14 import { download, downFileByBob, downFile } from '@/utils/common'
15 import {
16 addImportData,
17 deleteImportData,
18 getImportData,
19 exportDictionary,
20 exportCollectTask,
21 // getImageContent
22 } from '@/api/modules/queryService';
23 import {
24 parseAndDecodeUrl,
25 getDownFileSignByUrl,
26 obsDownloadRequest
27 } from '@/api/modules/obsService';
28 import {
29 getDictionaryTree
30 } from '@/api/modules/dataInventory';
31 import { commonPageConfig } from '@/utils/enum';
32 import * as XLSX from 'xlsx';
33
34 const { proxy } = getCurrentInstance() as any;
35
36 const userStore = useUserStore()
37 const route = useRoute()
38 const router = useRouter()
39 /** 2表示资产目录的。3是主数据; 4是元数据导入 */
40 const isfileImport = route.query.isfileImport
41 const userData = JSON.parse(userStore.userData)
42 const cacheStore = useCatchStore()
43 const standardSetList = ref([])
44 const standardSetGuid = ref('')
45 const dictionaryList = ref([])
46 const dictionaryGuid = ref('')
47 const tabsActiveName = ref('')
48 const uploadSetting: any = ref([])
49 const importType = ref('')
50 const defaulttabs = [
51 // { label: '标准集导入', name: 'standard' },
52 // { label: '字段标准导入', name: 'field' },
53 // { label: '命名标准导入', name: 'naming' },
54 { label: '数据字典导入', name: 'dictionary' },
55 // { label: '质量模型导入', name: 'qualityModelGroup' },
56 // { label: '质量规则导入', name: 'qualityRule' },
57 ]
58 const importTabs = [
59 { label: '导入文件数据', name: 'importFile' },
60 // { label: '质量模型导入', name: 'qualityModelGroup' },
61 // { label: '质量规则导入', name: 'qualityRule' },
62 ]
63 const tabsInfo = ref({
64 activeName: '',
65 tabs: isfileImport ? importTabs : defaulttabs
66 })
67
68 const currTableData: any = ref<Object>({});
69 const page = ref(commonPageConfig);
70 const selectRowData = ref([])
71 const tableInfo = ref({
72 id: 'data-source-table',
73 multiple: true,
74 fields: [
75 { label: "序号", type: "index", width: 56, align: "center" },
76 { label: "文件名称", field: "fileName", width: 240, },
77 { label: "状态", field: "importState", type: 'tag', width: 110, align: 'center' },
78 { label: "导入结果", field: "importMessage", width: 280 },
79 { label: "导入时间", field: "createTime", width: 180 },
80 ],
81 data: [],
82 page: {
83 type: "normal",
84 rows: 0,
85 ...page.value,
86 },
87 actionInfo: {
88 label: "操作",
89 type: "btn",
90 width: 220,
91 fixed: 'right',
92 btns: (scope) => {
93 const row = scope.row
94 let btnsArr = [
95 { label: '下载文件', value: 'export_file' },
96 { label: '删除', value: 'delete' }
97 ]
98 if (row.importState != 0 && row.importState != 1) {
99 btnsArr.splice(1, 0, { label: '下载异常数据', value: 'export_abnormal_data' })
100 }
101 return btnsArr
102 },
103 },
104 loading: false
105 })
106
107 const uploadFiles = ref([])
108 const uploadSteps: any = ref([])
109 const uploadInfo = ref({
110 type: 'upload',
111 title: '',
112 col: '',
113 uploadInfo: {
114 id: 'upload-file-form',
115 type: 'panel',
116 steps: [],
117 extraParams: {dictionaryGuid: 'xx'},
118 },
119 })
120
121 const dialogInfo: any = ref({
122 visible: false,
123 size: 560,
124 direction: "column",
125 header: {
126 title: "新建",
127 },
128 type: 'upload',
129 contents: [
130 uploadInfo.value
131 ],
132 footer: {
133 visible: true,
134 btns: [
135 { type: "default", label: "取消", value: "cancel" },
136 { type: "primary", loading: false, label: "开始导入", value: "submit" },
137 ],
138 },
139 })
140
141 // // 获取所有数据字典
142 const getDictList = () => {
143 const params = {
144 paramCode: '数据字典类型'
145 }
146 getDictionaryTree(params).then((res: any) => {
147 if (res.code == proxy.$passCode) {
148 const data = res.data ?? []
149 const treeList = data.filter(item => item.children && item.children.length)
150 dictionaryList.value = treeList
151 } else {
152 ElMessage({
153 type: 'error',
154 message: res.msg,
155 })
156 }
157 })
158 }
159
160 const tabsChange = (name) => {
161 tabsActiveName.value = name
162 let info: any = {
163 type: name
164 }
165 if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
166 info.standardSetGuid = standardSetGuid.value
167 } else if (tabsActiveName.value == 'dictionary') {
168 info.dictionaryGuid = dictionaryGuid.value
169 } else if (tabsActiveName.value == 'importFile') {
170
171 }
172 cacheStore.setCatch('uploadSetting', info)
173 setUploadInfo()
174 }
175
176 const getFirstPageData = () => {
177 page.value.curr = 1
178 toSearch({})
179
180 console.log('store', cacheStore.getCatch('uploadSetting'))
181 }
182
183 const toSearch = (val: any, clear: boolean = false) => {
184 let params: any = Object.keys(val).length ? { ...val } : {}
185 params.pageIndex = page.value.curr;
186 params.pageSize = page.value.limit;
187 params.staffGuid = userData.staffGuid;
188 params.importType = importType.value;
189
190 params.bizGuid = route.query.bizGuid || ''
191 getTableData(params);
192 };
193
194 const getTableData = (params) => {
195 tableInfo.value.loading = true
196 getImportData(params).then((res: any) => {
197 if (res.code == proxy.$passCode) {
198 const data = res.data || {}
199 tableInfo.value.data = data.records || []
200 tableInfo.value.page.limit = data.pageSize
201 tableInfo.value.page.curr = data.pageIndex
202 tableInfo.value.page.rows = data.totalRows
203 } else {
204 ElMessage({
205 type: 'error',
206 message: res.msg,
207 })
208 }
209 tableInfo.value.loading = false
210 }).catch(xhr => {
211 tableInfo.value.loading = false
212 })
213 };
214
215 const tableSelectionChange = (val) => {
216 selectRowData.value = val.map(item => item.guid);
217 };
218
219 const tablePageChange = (info) => {
220 page.value.curr = Number(info.curr);
221 page.value.limit = Number(info.limit);
222 toSearch({});
223 };
224
225 const tableBtnClick = async (scope, btn) => {
226 const type = btn.value;
227 const row = scope.row;
228 currTableData.value = row;
229 if (type == "export_file") {
230 const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.filePath).fileName);
231 if (!refSignInfo?.data) {
232 refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
233 return;
234 }
235 obsDownloadRequest(refSignInfo?.data).then((res: any) => {
236 if (res && !res.msg) {
237 downFileByBob(res, row.fileName);
238 } else {
239 res?.msg && ElMessage.error(res?.msg);
240 }
241 });
242 //downFile(row.filePath, row.fileName)
243 } else if (type == 'export_abnormal_data') {
244 //downFile(row.errorFilePath, '')
245 const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.errorFilePath).fileName);
246 if (!refSignInfo?.data) {
247 refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
248 return;
249 }
250 obsDownloadRequest(refSignInfo?.data).then((res: any) => {
251 if (res && !res.msg) {
252 let name = row.errorFilePath;
253 let fileName = name ? name.substring(name.lastIndexOf('/') + 1) : ''
254 downFileByBob(res, fileName);
255 } else {
256 res?.msg && ElMessage.error(res?.msg);
257 }
258 });
259 } else if (type == "delete") {
260 open("此操作将永久删除, 是否继续?", "warning");
261 }
262 };
263
264 const batching = (type) => {
265 if (type == 'import') {
266 dialogInfo.value.header.title = '导入数据'
267 dialogInfo.value.type = 'upload'
268 dialogInfo.value.size = 560
269 uploadFiles.value = []
270 // if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
271 // uploadSteps.value[0].selectInfo.options = standardSetList.value
272 // } else if (tabsActiveName.value == 'dictionary') {
273 // uploadSteps.value[0].cascaderInfo.options = dictionaryList.value
274 // }
275 uploadInfo.value.uploadInfo.steps = uploadSteps.value
276 uploadSetting.value.forEach(item => item.value = '')
277 sheetNameList.value = []
278 const content: any = [uploadInfo.value]
279 dialogInfo.value.contents = content
280 dialogInfo.value.visible = true
281 } else if (type == 'delete') {
282 if (selectRowData.value.length == 0) {
283 ElMessage({
284 type: 'error',
285 message: '请选择需要删除的数据',
286 })
287 return
288 }
289 open("此操作将永久删除, 是否继续?", "warning", true);
290 } else if (type === 'importFile') {
291 if (isfileImport == '2' || isfileImport == '4') {
292 dialogInfo.value.header.title = '导入数据'
293 dialogInfo.value.type = 'upload'
294 dialogInfo.value.size = isfileImport == '4' ? 560 : 560;
295 uploadFiles.value = []
296 uploadInfo.value.uploadInfo.steps = uploadSteps.value
297 const content: any = [uploadInfo.value]
298 dialogInfo.value.contents = content
299 dialogInfo.value.visible = true
300 } else {
301 router.push({
302 name: "importData",
303 query: route.query
304 })
305 }
306 }
307 };
308
309 const open = (msg, type, isBatch = false) => {
310 ElMessageBox.confirm(msg, "提示", {
311 confirmButtonText: "确定",
312 cancelButtonText: "取消",
313 type: type,
314 }).then(() => {
315 let guids = [currTableData.value.guid]
316 if (isBatch) {
317 guids = selectRowData.value
318 }
319 deleteImportData(guids).then((res: any) => {
320 if (res.code == proxy.$passCode) {
321 getFirstPageData();
322 ElMessage({
323 type: "success",
324 message: "删除成功",
325 });
326 } else {
327 ElMessage({
328 type: "error",
329 message: res.msg,
330 });
331 }
332 });
333 });
334 };
335
336 const sheetNameList = ref([])
337 const onUpload = (file, fileList) => {
338 // console.log('file', file)
339 uploadFiles.value = fileList
340 const reader = new FileReader()
341 reader.onload = function (e) {
342 let data = new Uint8Array(e.target.result)
343 let wb = XLSX.read(data, { type: 'array', raw: false, cellDates: true })
344 console.log('wb', wb)
345 sheetNameList.value = wb.SheetNames
346 }
347 reader.readAsArrayBuffer(file.raw)
348 }
349
350 const uploadBtnClick = (btn) => {
351 exportData()
352 }
353
354 const cascaderChange = (val) => {
355 dictionaryGuid.value = val ? val.at(-1) : ''
356 }
357
358 const selectChange = (val) => {
359 standardSetGuid.value = val
360 }
361
362 const exportData = (ids: any = null) => {
363 if (tabsActiveName.value == 'standard') {
364 const fieldTemplate = "/files/set.xlsx";
365 downFile(fieldTemplate, '标准集模板.xlsx')
366 } else if (tabsActiveName.value == 'field') {
367 const fieldTemplate = "/files/field.xlsx";
368 downFile(fieldTemplate, '字段标准模板.xlsx')
369 } else if (tabsActiveName.value == 'naming') {
370 const namingTemplate = "/files/naming.xlsx";
371 downFile(namingTemplate, '命名标准模板.xlsx')
372 } else if (tabsActiveName.value == 'dictionary') {
373 const params = {
374 guid: dictionaryGuid.value
375 }
376 exportDictionary(params).then((res: any) => {
377 if (res && !res.msg) {
378 download(res, '数据字典模板.xlsx', 'excel');
379 } else {
380 res?.msg && ElMessage.error(res?.msg);
381 }
382 });
383 } else if (tabsActiveName.value == 'importFile' && isfileImport == '4') {
384 exportCollectTask({
385 importTypes: [
386 "0042"
387 ]
388 }).then((res: any) => {
389 if (res && !res.msg) {
390 download(res, '元数据模板.xlsx', 'excel');
391 } else {
392 res?.msg && ElMessage.error(res?.msg);
393 }
394 });
395 }
396 }
397
398 const importData = (info) => {
399 let params = new FormData()
400 if (uploadFiles.value.length == 0) {
401 ElMessage({
402 type: 'error',
403 message: '请选择上传文件'
404 })
405 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
406 return
407 }
408 let sheetPass = uploadSetting.value.some(item => item.value)
409 if (!sheetPass) {
410 ElMessage.error('导入失败,没有导入数据')
411 dialogInfo.value.visible = false;
412 return
413 }
414 let sheetNum = uploadSetting.value.filter(item => item.value)['length']
415 if (sheetNum > 30) {
416 ElMessage.error('最多只能导入30个sheet页的数据。')
417 return
418 }
419 let paramUrl = '';
420 uploadFiles.value.forEach((item: any, index: number) => {
421 params.append("file", item.raw);
422 });
423 let sheetMaps = {}
424 uploadSetting.value.forEach(item => {
425 if (item.value) {
426 sheetMaps[item.value] = item.standardGuid
427 }
428 })
429 sheetMaps = encodeURIComponent(JSON.stringify(sheetMaps))
430 paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-batch-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}&sheetMaps=${sheetMaps}`
431 dialogInfo.value.footer.btns[1].loading = true;
432 addImportData(paramUrl, params).then((res: any) => {
433 dialogInfo.value.footer.btns[1].loading = false;
434 if (res.code == proxy.$passCode) {
435 getFirstPageData();
436 ElMessage({
437 type: "success",
438 message: '导入成功',
439 });
440 dialogInfo.value.visible = false;
441 } else {
442 ElMessage({
443 type: "error",
444 message: res.msg,
445 });
446 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
447 }
448 }).catch(() => {
449 dialogInfo.value.footer.btns[1].loading = false;
450 })
451 }
452
453 const dialogBtnClick = (btn, info) => {
454 if (btn.value == 'submit') {
455 // dialogInfo.value.footer.btns.map((item: any) => item.disabled = true)
456 if (dialogInfo.value.type == 'upload') {
457 if (tabsActiveName.value == 'dictionary') {
458 importData({ bizGuid: dictionaryGuid.value })
459 } else {
460 importData(info)
461 }
462 }
463 } else if (btn.value == 'cancel') {
464 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
465 nextTick(() => {
466 dialogInfo.value.visible = false;
467 })
468 };
469 }
470
471 const setUploadInfo = () => {
472 importType.value = '0101'
473 tabsInfo.value.activeName = tabsActiveName.value
474 getFirstPageData()
475 uploadSteps.value = [
476 {
477 title: '1、选择准备好的文件导入',
478 type: 'btn_upload',
479 uploadInfo: {
480 action: '',
481 auto: false,
482 cover: true,
483 fileList: [],
484 accept: '.xlsx, .xls',
485 tips: '当前支持xls、xlsx文件,支持一个文件多个sheet批量导入,一次最多只能导入30个sheet页数据。'
486 }
487 }
488 ]
489 }
490
491 function deleteFile () {
492 sheetNameList.value = []
493 uploadSetting.value.forEach(item => item.value = null)
494 }
495
496 onActivated(() => {
497 let list = cacheStore.getCatch('uploadSetting') || []
498 uploadSetting.value = list.map(item => {
499 item.value = null
500 return item
501 })
502 console.log('uploadSetting', uploadSetting.value)
503 setUploadInfo()
504 })
505
506 </script>
507
508 <template>
509 <div class="container_wrap">
510 <!-- <Tabs v-if="!isfileImport" :tabs-info="tabsInfo" @tabChange="tabsChange" /> -->
511 <div class="table_tool_wrap">
512 <div class="tools_btns">
513 <el-button type="primary" @click="batching('import')" v-if="tabsActiveName !== 'importFile'"
514 v-preReClick>批量导入</el-button>
515 <el-button type="primary" @click="batching('importFile')" v-if="tabsActiveName == 'importFile'"
516 v-preReClick>文件导入</el-button>
517 <el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
518 <el-button @click="getFirstPageData" v-preReClick>刷新结果</el-button>
519 </div>
520 <span class="tips_text">请及时刷新查看最终结果</span>
521 </div>
522 <div class="table_panel_wrap" :style="{ height: !isfileImport ? 'calc(100% - 71px)' : 'calc(100% - 44px)' }">
523 <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
524 @tablePageChange="tablePageChange" />
525 </div>
526 <Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @onUpload="onUpload" @uploadBtnClick="uploadBtnClick"
527 @cascaderChange="cascaderChange" @selectChange="selectChange" @deleteFile="deleteFile">
528 <div style="overflow: auto;">
529 <div class="title" style="color:#333;margin:20px 0 10px">2、导入前请先导入文件的sheet与标准做对应</div>
530 <el-table :data="uploadSetting" border height="310">
531 <el-table-column type="index" label="序号" width="55" align="center"/>
532 <el-table-column label="代码名称" prop="standardName"></el-table-column>
533 <el-table-column label="选择sheet页">
534 <template #default="scope">
535 <el-select v-model="scope.row.value" placeholder="请选择" style="width:200px" clearable>
536 <el-option v-for="item,i in sheetNameList" :label="item" :value="item" :key="i"></el-option>
537 </el-select>
538 </template>
539 </el-table-column>
540 </el-table>
541 </div>
542 </Dialog>
543 </div>
544 </template>
545
546 <style lang="scss" scoped>
547 .container_wrap {
548 padding: 0;
549
550 :deep(.el-tabs) {
551
552 .el-tabs__header {
553 margin-bottom: 0;
554 }
555
556 .el-tabs__item {
557 height: 32px;
558
559 &:nth-child(2) {
560 padding-left: 16px;
561 }
562
563 &:last-child {
564 padding-right: 16px;
565 }
566
567 &::after {
568 content: '';
569 width: 100%;
570 height: 2px;
571 background-color: transparent;
572 position: absolute;
573 left: 0;
574 bottom: 0;
575 }
576
577 &.is-active {
578 &::after {
579 background-color: var(--el-color-primary);
580 }
581 }
582 }
583
584 .el-tabs__active-bar {
585 display: none;
586 }
587 }
588
589 .table_tool_wrap {
590 padding: 0 16px;
591 display: flex;
592 align-items: center;
593
594 .tips_text {
595 margin-left: 16px;
596 font-size: 14px;
597 color: #b2b2b2;
598 }
599 }
600
601 .table_panel_wrap {
602 padding: 0 16px;
603 height: calc(100% - 71px);
604 }
605
606 }
607 </style>
608 <style lang="scss">
609 .upload_panel_wrap .upload_panel .file_panel .file_item .file_btn {
610 word-break: keep-all;
611 }
612 .upload_panel_wrap .upload_step_panel .upload_panel {
613 padding-top: 42px!important;
614 }
615 </style>
1 <route lang="yaml">
2 name: standardMetaImport
3 </route>
4
5 <script lang="ts" setup name="standardMetaImport">
6 import { ref } from "vue";
7 import { useRoute, useRouter } from "vue-router"
8 import useUserStore from "@/store/modules/user";
9 import { ElMessage, ElMessageBox } from "element-plus";
10 import Tabs from '@/components/Tabs/index.vue'
11 import Table from '@/components/Table/index.vue'
12 import Dialog from '@/components/Dialog/index.vue'
13 import useCatchStore from "@/store/modules/catch";
14 import { download, downFileByBob, downFile } from '@/utils/common'
15 import {
16 addImportData,
17 deleteImportData,
18 getImportData,
19 exportDictionary,
20 exportCollectTask,
21 // getImageContent
22 } from '@/api/modules/queryService';
23 import {
24 parseAndDecodeUrl,
25 getDownFileSignByUrl,
26 obsDownloadRequest
27 } from '@/api/modules/obsService';
28 import {
29 getDictionaryTree
30 } from '@/api/modules/dataInventory';
31 import { commonPageConfig } from '@/utils/enum';
32 import * as XLSX from 'xlsx';
33
34 const { proxy } = getCurrentInstance() as any;
35
36 const userStore = useUserStore()
37 const route = useRoute()
38 const router = useRouter()
39 /** 2表示资产目录的。3是主数据; 4是元数据导入 */
40 const isfileImport = route.query.isfileImport
41 const userData = JSON.parse(userStore.userData)
42 const cacheStore = useCatchStore()
43 const standardSetList = ref([])
44 const standardSetGuid = ref('')
45 const dictionaryList = ref([])
46 const dictionaryGuid = ref('')
47 const tabsActiveName = ref('')
48 const uploadSetting: any = ref([])
49 const importType = ref('')
50 const defaulttabs = [
51 // { label: '标准集导入', name: 'standard' },
52 // { label: '字段标准导入', name: 'field' },
53 // { label: '命名标准导入', name: 'naming' },
54 { label: '数据字典导入', name: 'dictionary' },
55 // { label: '质量模型导入', name: 'qualityModelGroup' },
56 // { label: '质量规则导入', name: 'qualityRule' },
57 ]
58 const importTabs = [
59 { label: '导入文件数据', name: 'importFile' },
60 // { label: '质量模型导入', name: 'qualityModelGroup' },
61 // { label: '质量规则导入', name: 'qualityRule' },
62 ]
63 const tabsInfo = ref({
64 activeName: '',
65 tabs: isfileImport ? importTabs : defaulttabs
66 })
67
68 const currTableData: any = ref<Object>({});
69 const page = ref(commonPageConfig);
70 const selectRowData = ref([])
71 const tableInfo = ref({
72 id: 'data-source-table',
73 multiple: true,
74 fields: [
75 { label: "序号", type: "index", width: 56, align: "center" },
76 { label: "文件名称", field: "fileName", width: 240, },
77 { label: "状态", field: "importState", type: 'tag', width: 110, align: 'center' },
78 { label: "导入结果", field: "importMessage", width: 280 },
79 { label: "导入时间", field: "createTime", width: 180 },
80 ],
81 data: [],
82 page: {
83 type: "normal",
84 rows: 0,
85 ...page.value,
86 },
87 actionInfo: {
88 label: "操作",
89 type: "btn",
90 width: 220,
91 fixed: 'right',
92 btns: (scope) => {
93 const row = scope.row
94 let btnsArr = [
95 { label: '下载文件', value: 'export_file' },
96 { label: '删除', value: 'delete' }
97 ]
98 if (row.importState != 0 && row.importState != 1) {
99 btnsArr.splice(1, 0, { label: '下载异常数据', value: 'export_abnormal_data' })
100 }
101 return btnsArr
102 },
103 },
104 loading: false
105 })
106
107 const uploadFiles = ref([])
108 const uploadSteps: any = ref([])
109 const uploadInfo = ref({
110 type: 'upload',
111 title: '',
112 col: '',
113 uploadInfo: {
114 id: 'upload-file-form',
115 type: 'panel',
116 steps: [],
117 extraParams: {dictionaryGuid: 'xx'},
118 },
119 })
120
121 const dialogInfo: any = ref({
122 visible: false,
123 size: 560,
124 direction: "column",
125 header: {
126 title: "新建",
127 },
128 type: 'upload',
129 contents: [
130 uploadInfo.value
131 ],
132 footer: {
133 visible: true,
134 btns: [
135 { type: "default", label: "取消", value: "cancel" },
136 { type: "primary", loading: false, label: "开始导入", value: "submit" },
137 ],
138 },
139 })
140
141 // // 获取所有数据字典
142 const getDictList = () => {
143 const params = {
144 paramCode: '数据字典类型'
145 }
146 getDictionaryTree(params).then((res: any) => {
147 if (res.code == proxy.$passCode) {
148 const data = res.data ?? []
149 const treeList = data.filter(item => item.children && item.children.length)
150 dictionaryList.value = treeList
151 } else {
152 ElMessage({
153 type: 'error',
154 message: res.msg,
155 })
156 }
157 })
158 }
159
160 const tabsChange = (name) => {
161 tabsActiveName.value = name
162 let info: any = {
163 type: name
164 }
165 if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
166 info.standardSetGuid = standardSetGuid.value
167 } else if (tabsActiveName.value == 'dictionary') {
168 info.dictionaryGuid = dictionaryGuid.value
169 } else if (tabsActiveName.value == 'importFile') {
170
171 }
172 cacheStore.setCatch('uploadSetting', info)
173 setUploadInfo()
174 }
175
176 const getFirstPageData = () => {
177 page.value.curr = 1
178 toSearch({})
179
180 console.log('store', cacheStore.getCatch('uploadSetting'))
181 }
182
183 const toSearch = (val: any, clear: boolean = false) => {
184 let params: any = Object.keys(val).length ? { ...val } : {}
185 params.pageIndex = page.value.curr;
186 params.pageSize = page.value.limit;
187 params.staffGuid = userData.staffGuid;
188 params.importType = importType.value;
189
190 params.bizGuid = route.query.bizGuid || ''
191 getTableData(params);
192 };
193
194 const getTableData = (params) => {
195 tableInfo.value.loading = true
196 getImportData(params).then((res: any) => {
197 if (res.code == proxy.$passCode) {
198 const data = res.data || {}
199 tableInfo.value.data = data.records || []
200 tableInfo.value.page.limit = data.pageSize
201 tableInfo.value.page.curr = data.pageIndex
202 tableInfo.value.page.rows = data.totalRows
203 } else {
204 ElMessage({
205 type: 'error',
206 message: res.msg,
207 })
208 }
209 tableInfo.value.loading = false
210 }).catch(xhr => {
211 tableInfo.value.loading = false
212 })
213 };
214
215 const tableSelectionChange = (val) => {
216 selectRowData.value = val.map(item => item.guid);
217 };
218
219 const tablePageChange = (info) => {
220 page.value.curr = Number(info.curr);
221 page.value.limit = Number(info.limit);
222 toSearch({});
223 };
224
225 const tableBtnClick = async (scope, btn) => {
226 const type = btn.value;
227 const row = scope.row;
228 currTableData.value = row;
229 if (type == "export_file") {
230 const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.filePath).fileName);
231 if (!refSignInfo?.data) {
232 refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
233 return;
234 }
235 obsDownloadRequest(refSignInfo?.data).then((res: any) => {
236 if (res && !res.msg) {
237 downFileByBob(res, row.fileName);
238 } else {
239 res?.msg && ElMessage.error(res?.msg);
240 }
241 });
242 //downFile(row.filePath, row.fileName)
243 } else if (type == 'export_abnormal_data') {
244 //downFile(row.errorFilePath, '')
245 const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(row.errorFilePath).fileName);
246 if (!refSignInfo?.data) {
247 refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
248 return;
249 }
250 obsDownloadRequest(refSignInfo?.data).then((res: any) => {
251 if (res && !res.msg) {
252 let name = row.errorFilePath;
253 let fileName = name ? name.substring(name.lastIndexOf('/') + 1) : ''
254 downFileByBob(res, fileName);
255 } else {
256 res?.msg && ElMessage.error(res?.msg);
257 }
258 });
259 } else if (type == "delete") {
260 open("此操作将永久删除, 是否继续?", "warning");
261 }
262 };
263
264 const batching = (type) => {
265 if (type == 'import') {
266 dialogInfo.value.header.title = '导入数据'
267 dialogInfo.value.type = 'upload'
268 dialogInfo.value.size = 560
269 uploadFiles.value = []
270 // if (tabsActiveName.value == 'field' || tabsActiveName.value == 'naming') {
271 // uploadSteps.value[0].selectInfo.options = standardSetList.value
272 // } else if (tabsActiveName.value == 'dictionary') {
273 // uploadSteps.value[0].cascaderInfo.options = dictionaryList.value
274 // }
275 uploadInfo.value.uploadInfo.steps = uploadSteps.value
276 uploadSetting.value.forEach(item => item.value = '')
277 sheetNameList.value = []
278 const content: any = [uploadInfo.value]
279 dialogInfo.value.contents = content
280 dialogInfo.value.visible = true
281 } else if (type == 'delete') {
282 if (selectRowData.value.length == 0) {
283 ElMessage({
284 type: 'error',
285 message: '请选择需要删除的数据',
286 })
287 return
288 }
289 open("此操作将永久删除, 是否继续?", "warning", true);
290 } else if (type === 'importFile') {
291 if (isfileImport == '2' || isfileImport == '4') {
292 dialogInfo.value.header.title = '导入数据'
293 dialogInfo.value.type = 'upload'
294 dialogInfo.value.size = isfileImport == '4' ? 560 : 560;
295 uploadFiles.value = []
296 uploadInfo.value.uploadInfo.steps = uploadSteps.value
297 const content: any = [uploadInfo.value]
298 dialogInfo.value.contents = content
299 dialogInfo.value.visible = true
300 } else {
301 router.push({
302 name: "importData",
303 query: route.query
304 })
305 }
306 }
307 };
308
309 const open = (msg, type, isBatch = false) => {
310 ElMessageBox.confirm(msg, "提示", {
311 confirmButtonText: "确定",
312 cancelButtonText: "取消",
313 type: type,
314 }).then(() => {
315 let guids = [currTableData.value.guid]
316 if (isBatch) {
317 guids = selectRowData.value
318 }
319 deleteImportData(guids).then((res: any) => {
320 if (res.code == proxy.$passCode) {
321 getFirstPageData();
322 ElMessage({
323 type: "success",
324 message: "删除成功",
325 });
326 } else {
327 ElMessage({
328 type: "error",
329 message: res.msg,
330 });
331 }
332 });
333 });
334 };
335
336 const sheetNameList = ref([])
337 const onUpload = (file, fileList) => {
338 // console.log('file', file)
339 uploadFiles.value = fileList
340 const reader = new FileReader()
341 reader.onload = function (e) {
342 let data = new Uint8Array(e.target.result)
343 let wb = XLSX.read(data, { type: 'array', raw: false, cellDates: true })
344 console.log('wb', wb)
345 sheetNameList.value = wb.SheetNames
346 }
347 reader.readAsArrayBuffer(file.raw)
348 }
349
350 const uploadBtnClick = (btn) => {
351 exportData()
352 }
353
354 const cascaderChange = (val) => {
355 dictionaryGuid.value = val ? val.at(-1) : ''
356 }
357
358 const selectChange = (val) => {
359 standardSetGuid.value = val
360 }
361
362 const exportData = (ids: any = null) => {
363 if (tabsActiveName.value == 'standard') {
364 const fieldTemplate = "/files/set.xlsx";
365 downFile(fieldTemplate, '标准集模板.xlsx')
366 } else if (tabsActiveName.value == 'field') {
367 const fieldTemplate = "/files/field.xlsx";
368 downFile(fieldTemplate, '字段标准模板.xlsx')
369 } else if (tabsActiveName.value == 'naming') {
370 const namingTemplate = "/files/naming.xlsx";
371 downFile(namingTemplate, '命名标准模板.xlsx')
372 } else if (tabsActiveName.value == 'dictionary') {
373 const params = {
374 guid: dictionaryGuid.value
375 }
376 exportDictionary(params).then((res: any) => {
377 if (res && !res.msg) {
378 download(res, '数据字典模板.xlsx', 'excel');
379 } else {
380 res?.msg && ElMessage.error(res?.msg);
381 }
382 });
383 } else if (tabsActiveName.value == 'importFile' && isfileImport == '4') {
384 exportCollectTask({
385 importTypes: [
386 "0042"
387 ]
388 }).then((res: any) => {
389 if (res && !res.msg) {
390 download(res, '元数据模板.xlsx', 'excel');
391 } else {
392 res?.msg && ElMessage.error(res?.msg);
393 }
394 });
395 }
396 }
397
398 const importData = (info) => {
399 let params = new FormData()
400 if (uploadFiles.value.length == 0) {
401 ElMessage({
402 type: 'error',
403 message: '请选择上传文件'
404 })
405 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
406 return
407 }
408 let sheetPass = uploadSetting.value.some(item => item.value)
409 if (!sheetPass) {
410 ElMessage.error('导入失败,没有导入数据')
411 dialogInfo.value.visible = false;
412 return
413 }
414 let sheetNum = uploadSetting.value.filter(item => item.value)['length']
415 if (sheetNum > 30) {
416 ElMessage.error('最多只能导入30个sheet页的数据。')
417 return
418 }
419 let paramUrl = '';
420 uploadFiles.value.forEach((item: any, index: number) => {
421 params.append("file", item.raw);
422 });
423 let sheetMaps = {}
424 uploadSetting.value.forEach(item => {
425 if (item.value) {
426 sheetMaps[item.value] = item.standardGuid
427 }
428 })
429 sheetMaps = encodeURIComponent(JSON.stringify(sheetMaps))
430 paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-batch-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}&sheetMaps=${sheetMaps}`
431 dialogInfo.value.footer.btns[1].loading = true;
432 addImportData(paramUrl, params).then((res: any) => {
433 dialogInfo.value.footer.btns[1].loading = false;
434 if (res.code == proxy.$passCode) {
435 getFirstPageData();
436 ElMessage({
437 type: "success",
438 message: '导入成功',
439 });
440 dialogInfo.value.visible = false;
441 } else {
442 ElMessage({
443 type: "error",
444 message: res.msg,
445 });
446 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
447 }
448 }).catch(() => {
449 dialogInfo.value.footer.btns[1].loading = false;
450 })
451 }
452
453 const dialogBtnClick = (btn, info) => {
454 if (btn.value == 'submit') {
455 // dialogInfo.value.footer.btns.map((item: any) => item.disabled = true)
456 if (dialogInfo.value.type == 'upload') {
457 if (tabsActiveName.value == 'dictionary') {
458 importData({ bizGuid: dictionaryGuid.value })
459 } else {
460 importData(info)
461 }
462 }
463 } else if (btn.value == 'cancel') {
464 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
465 nextTick(() => {
466 dialogInfo.value.visible = false;
467 })
468 };
469 }
470
471 const setUploadInfo = () => {
472 importType.value = '0102'
473 tabsInfo.value.activeName = tabsActiveName.value
474 getFirstPageData()
475 uploadSteps.value = [
476 {
477 title: '1、选择准备好的文件导入',
478 type: 'btn_upload',
479 uploadInfo: {
480 action: '',
481 auto: false,
482 cover: true,
483 fileList: [],
484 accept: '.xlsx, .xls',
485 tips: '当前支持xls、xlsx文件,支持一个文件多个sheet批量导入,一次最多只能导入30个sheet页数据。'
486 }
487 }
488 ]
489 }
490
491 function deleteFile () {
492 sheetNameList.value = []
493 uploadSetting.value.forEach(item => item.value = null)
494 }
495
496 onActivated(() => {
497 let list = cacheStore.getCatch('uploadSetting') || []
498 uploadSetting.value = list.map(item => {
499 item.value = null
500 return item
501 })
502 console.log('uploadSetting', uploadSetting.value)
503 setUploadInfo()
504 })
505
506 </script>
507
508 <template>
509 <div class="container_wrap">
510 <!-- <Tabs v-if="!isfileImport" :tabs-info="tabsInfo" @tabChange="tabsChange" /> -->
511 <div class="table_tool_wrap">
512 <div class="tools_btns">
513 <el-button type="primary" @click="batching('import')" v-if="tabsActiveName !== 'importFile'"
514 v-preReClick>批量导入</el-button>
515 <el-button type="primary" @click="batching('importFile')" v-if="tabsActiveName == 'importFile'"
516 v-preReClick>文件导入</el-button>
517 <el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
518 <el-button @click="getFirstPageData" v-preReClick>刷新结果</el-button>
519 </div>
520 <span class="tips_text">请及时刷新查看最终结果</span>
521 </div>
522 <div class="table_panel_wrap" :style="{ height: !isfileImport ? 'calc(100% - 71px)' : 'calc(100% - 44px)' }">
523 <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
524 @tablePageChange="tablePageChange" />
525 </div>
526 <Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @onUpload="onUpload" @uploadBtnClick="uploadBtnClick"
527 @cascaderChange="cascaderChange" @selectChange="selectChange" @deleteFile="deleteFile">
528 <div style="overflow: auto;">
529 <div class="title" style="color:#333;margin:20px 0 10px">2、导入前请先导入文件的sheet与标准做对应</div>
530 <el-table :data="uploadSetting" border height="310">
531 <el-table-column type="index" label="序号" width="55" align="center"/>
532 <el-table-column label="标准名称" prop="standardName"></el-table-column>
533 <el-table-column label="选择sheet页">
534 <template #default="scope">
535 <el-select v-model="scope.row.value" placeholder="请选择" style="width:200px" clearable>
536 <el-option v-for="item,i in sheetNameList" :label="item" :value="item" :key="i"></el-option>
537 </el-select>
538 </template>
539 </el-table-column>
540 </el-table>
541 </div>
542 </Dialog>
543 </div>
544 </template>
545
546 <style lang="scss" scoped>
547 .container_wrap {
548 padding: 0;
549
550 :deep(.el-tabs) {
551
552 .el-tabs__header {
553 margin-bottom: 0;
554 }
555
556 .el-tabs__item {
557 height: 32px;
558
559 &:nth-child(2) {
560 padding-left: 16px;
561 }
562
563 &:last-child {
564 padding-right: 16px;
565 }
566
567 &::after {
568 content: '';
569 width: 100%;
570 height: 2px;
571 background-color: transparent;
572 position: absolute;
573 left: 0;
574 bottom: 0;
575 }
576
577 &.is-active {
578 &::after {
579 background-color: var(--el-color-primary);
580 }
581 }
582 }
583
584 .el-tabs__active-bar {
585 display: none;
586 }
587 }
588
589 .table_tool_wrap {
590 padding: 0 16px;
591 display: flex;
592 align-items: center;
593
594 .tips_text {
595 margin-left: 16px;
596 font-size: 14px;
597 color: #b2b2b2;
598 }
599 }
600
601 .table_panel_wrap {
602 padding: 0 16px;
603 height: calc(100% - 71px);
604 }
605
606 }
607 </style>
608 <style lang="scss">
609 .upload_panel_wrap .upload_panel .file_panel .file_item .file_btn {
610 word-break: keep-all;
611 }
612 .upload_panel_wrap .upload_step_panel .upload_panel {
613 padding-top: 42px!important;
614 }
615 </style>
1 <route lang="yaml">
2 name: metadataStandardQueryView
3 </route>
4
5 <template>
6 <div class="main_wrap" v-loading="graphDataLoading">
7 <div className='g6-component-topbar'>
8 <graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" />
9 </div>
10 <RelationNetwork v-show="graphTreeData?.guid && isGraphDisplay" ref="relationNetworkRef" :tree-data="graphTreeData"
11 :noContextMenu="true" @nodeItemClick="handleNodeItemClick" @contextMenu="handleContextMenu">
12 </RelationNetwork>
13 <Sankey v-show="!isGraphDisplay && (sankeyNames?.length || sankeyDataLoading)" v-loading="sankeyDataLoading"
14 :tree-data="sankeyData" :names="sankeyNames">
15 </Sankey>
16 <div v-show="(isGraphDisplay ? !graphTreeData?.guid : (!sankeyDataLoading && !sankeyNames?.length))"
17 class="main-placeholder">
18 <img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
19 <div class="empty-text">暂无数据</div>
20 </div>
21 </div>
22 </template>
23
24 <script lang="ts" setup name="metadataStandardQueryView">
25 import {
26 getMetaStandardField,
27 getSankeyData,
28 getMetaStandardTreeList
29 } from '@/api/modules/dataMetaService';
30 import Sankey from './components/Sankey.vue';
31 import { ElMessage } from 'element-plus';
32
33 const router = useRouter();
34 const route = useRoute()
35
36 const metaGuid = ref(route.query.guid);
37
38 const { proxy } = getCurrentInstance() as any;
39
40 const relationNetworkRef = ref();
41
42 const graphDataLoading = ref(false);
43
44 /** 关系网树形数据 */
45 const graphTreeData: any = ref({});
46
47 onBeforeUnmount(() => {
48 relationNetworkRef.value.destroy();
49 })
50
51 const sankeyDataLoading = ref(false);
52
53 const sankeyData: any = ref([]);
54
55 const sankeyNames: any = ref([]);
56
57 const isGraphDisplay = ref(true);
58
59 const displaySwitchChange = (val) => {
60 if (val == isGraphDisplay.value) {
61 return;
62 }
63 isGraphDisplay.value = val;
64 if (!val) {
65 getSankeyDataList();
66 }
67 }
68
69 const getSankeyDataList = () => {
70 sankeyDataLoading.value = true;
71 getSankeyData(metaGuid.value).then((res: any) => {
72 sankeyDataLoading.value = false;
73 if (res?.code == proxy.$passCode) {
74 sankeyData.value = res.data?.links || [];
75 sankeyNames.value = res.data?.data || [];
76 } else {
77 ElMessage.error(res.msg);
78 }
79 })
80 }
81
82 const handleNodeItemClick = (graph, nodeItem) => {
83 const nodeId = nodeItem.get('id');
84 let parentData = graph.findDataById(nodeId);
85 if (!parentData.children) {
86 parentData.children = [];
87 }
88 if (parentData.isHaveData == 'N') {
89 ElMessage.warning('没有可展开的下级字段');
90 return;
91 }
92 // graph.updateConfig({ animate: false });
93 // graph.refresh();
94 nodeItem.getModel().collapsed = false;
95 parentData.collapsed = false;
96 graphDataLoading.value = true;
97 getMetaStandardField(nodeId).then((res: any) => {
98 graphDataLoading.value = false;
99 if (res?.code == proxy.$passCode) {
100 parentData = graph.findDataById(nodeId);
101 const data = res.data || [];
102 parentData.children = [];
103 if (!data?.length) {
104 parentData.isHaveData = 'N';
105 ElMessage.warning('没有可展开的下级字段');
106 return;
107 }
108 data.forEach(d => {
109 parentData.children.push(d);
110 })
111 parentData.isLoading = false;
112 nodeItem.getModel().collapsed = false;
113 parentData.collapsed = false;
114 graph.updateItem(nodeItem, {
115 ...nodeItem.getModel(),
116 collapsed: false
117 });
118 graph.layout();
119 setTimeout(() => {
120 // graph.updateConfig({ animate: true });
121 // graph.refresh();
122 graph.updateItem(nodeItem, {
123 ...nodeItem.getModel(),
124 collapsed: false
125 });
126 graph.setMinZoom(1);
127 graph.setMaxZoom(1);
128 graph.layout();
129 graph.setMinZoom(0.5);
130 graph.setMaxZoom(5);
131 graph.focusItem(nodeItem, true, {
132 duration: 400 // 动画时长为500ms
133 });
134 }, 500);
135 } else {
136 parentData.isLoading = false;
137 ElMessage.error(res.msg);
138 }
139 })
140 }
141
142 const handleContextMenu = (model) => {
143 router.push({
144 name: 'metadataStandard',
145 query: {
146 standardGuid: model.guid,
147 name: model.standardName
148 }
149 });
150 }
151
152 onBeforeMount(() => {
153 graphDataLoading.value = true
154 getMetaStandardTreeList(metaGuid.value).then((res: any) => {
155 graphDataLoading.value = false;
156 if (res?.code == proxy.$passCode) {
157 const data = res.data || [];
158 let resultData = data?.[0] || {};
159 if (!resultData?.children?.length && resultData.isHaveData == 'Y') {
160 graphDataLoading.value = true;
161 getMetaStandardField(resultData.guid).then((res: any) => {
162 graphDataLoading.value = false;
163 if (res?.code == proxy.$passCode) {
164 resultData.children = res.data || [];
165 graphTreeData.value = resultData;
166 } else {
167 graphTreeData.value = resultData;
168 ElMessage.error(res.msg);
169 }
170 });
171 } else {
172 graphTreeData.value = resultData;
173 }
174 } else {
175 ElMessage.error(res.msg);
176 }
177 })
178 })
179
180 </script>
181
182 <style lang="scss" scoped>
183 .main_wrap {
184 height: 100%;
185 width: 100%;
186 position: relative;
187
188 :deep(.canvas-wrapper) {
189 background-color: #f7f7f9;
190 }
191
192 .main-placeholder {
193 height: 100%;
194 display: flex;
195 justify-content: center;
196 align-items: center;
197 flex-direction: column;
198
199 .empty-text {
200 font-size: 14px;
201 color: #b2b2b2;
202 }
203 }
204 }
205
206 .g6-component-topbar {
207 position: absolute;
208 left: 24px;
209 bottom: unset;
210 top: 14px;
211 padding: 0;
212 text-align: center;
213 z-index: 999;
214 }
215 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: metadataStandardQuery
3 </route>
4
5 <script lang="ts" setup name="metadataStandardQuery">
6 import { ref } from 'vue';
7 import { ElMessage, ElMessageBox } from "element-plus";
8 import Sankey from './components/Sankey.vue';
9 import Tree from '@/components/Tree/index.vue';
10 import RelationNetwork from '@/components/RelationNetwork/index.vue';
11 import {
12 getDataWareCatalogList,
13 getMetaStandardTreeList,
14 getMetaStandardField,
15 getSankeyData
16 } from '@/api/modules/dataMetaService';
17 import { useRouter, useRoute } from "vue-router";
18 import useDataMetaStore from "@/store/modules/dataMeta"
19 import { cloneDeep } from 'lodash-es';
20
21 import { useValidator } from '@/hooks/useValidator';
22
23 const { required } = useValidator();
24
25 const router = useRouter();
26 const route = useRoute()
27
28 const { proxy } = getCurrentInstance() as any;
29
30 const relationNetworkRef = ref();
31
32 const treeInfo = ref({
33 id: "data-meta-standard-tree",
34 filter: true,
35 queryValue: "",
36 queryPlaceholder: "请输入关键字搜索",
37 props: {
38 label: "standardName",
39 value: "guid",
40 isLeaf: "isLeaf",
41 },
42 nodeKey: 'guid',
43 expandedKey: [],
44 currentNodeKey: '',
45 expandOnNodeClick: false,
46 data: <any>[],
47 loading: false
48 });
49
50 /** 获取左侧树数据. */
51 const getTreeData = async () => {
52 treeInfo.value.loading = true
53 getMetaStandardTreeList('').then((res: any) => {
54 treeInfo.value.loading = false;
55 if (res?.code == proxy.$passCode) {
56 const data = res.data || [];
57 data.forEach(d => {
58 d.disabled = true;
59 })
60 treeInfo.value.data = data;
61 if (data.length) {
62 treeInfo.value.currentNodeKey = data[0]?.children?.[0]?.guid;
63 treeInfo.value.expandedKey = <any>[data[0].guid];
64 nodeClick(treeInfo.value.data[0]?.children?.[0])
65 }
66 } else {
67 ElMessage.error(res.msg);
68 }
69 })
70 }
71 /** 左侧树的的组件引用. */
72 const treeInfoRef = ref();
73
74 /** 当前选中的树节点数据data */
75 const lastClickNode: any = ref({});
76
77 const treeDataLoading = ref(false);
78
79 /** 点击左侧树节点,更新对应的血缘关系图. */
80 const nodeClick = (data) => {
81 const ele = <HTMLElement>document.querySelector(".g6-component-contextmenu")
82 if (ele) {
83 ele.style.visibility = "hidden"
84 }
85 if (data.disabled) {
86 treeInfo.value.currentNodeKey = data.guid;
87 nextTick(() => {
88 treeInfo.value.currentNodeKey = lastClickNode.value.guid;
89 });
90 // lastClickNode.value = {};
91 return;
92 }
93 treeInfo.value.currentNodeKey = data.guid;
94 treeInfo.value.expandedKey = <any>[data.guid];
95 lastClickNode.value = cloneDeep(data);
96 if (!isGraphDisplay.value) {
97 getSankeyDataList();
98 }
99 }
100
101 /** 选中树节点后自动滚动到可视范围内. */
102 const scrollToNode = (nodeId) => {
103 nextTick(() => {
104 const nodeElement = treeInfoRef.value.treeRef.$el.querySelector(`[data-key="${nodeId}"]`);
105 if (nodeElement) {
106 nodeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
107 }
108 });
109 }
110
111 /** 处理从详情处跳转而来的默认展示. */
112 const processRouter = () => {
113 const { guid, databaseName, tableName, databaseChName, databaseGuid, fieldGuid, fieldEnName, set } = useDataMetaStore()
114 let isFL = useDataMetaStore().isFieldLineage;
115 if (fieldGuid) {//查看字段血缘的
116 nextTick(() => {
117 treeInfo.value.expandedKey = <any>[databaseGuid, guid];
118 treeInfo.value.currentNodeKey = fieldGuid as string;
119 scrollToNode(fieldGuid);
120 treeInfoRef.value.setCurrentKey(fieldGuid);
121 })
122 lastClickNode.value = { guid: fieldGuid, tableGuid: guid, databaseGuid: databaseGuid, enName: fieldEnName, tableName, databaseName, type: 4, isLeaf: true, databaseChName };
123 // getTableFieldLineageMap();
124 set()
125 } else if (guid) {
126 treeInfo.value.currentNodeKey = guid as string;
127 treeInfo.value.expandedKey = <any>[databaseGuid, guid];
128 lastClickNode.value = { guid: guid, tableName, databaseName, type: 3, databaseChName };
129 scrollToNode(guid);
130 // if (isFL) {
131 // getAllTableFieldLineageMap();
132 // } else {
133 // getTableLineageMap()
134 // }
135 set()
136 }
137 }
138
139 onActivated(() => {
140 //processRouter();
141 });
142
143 onBeforeMount(async () => {
144 await getTreeData()
145 // processRouter();
146 })
147
148 onMounted(() => { })
149
150 const sankeyDataLoading = ref(false);
151
152 const sankeyData: any = ref([]);
153
154 const sankeyNames: any = ref([]);
155
156 const isGraphDisplay = ref(true);
157
158 const displaySwitchChange = (val) => {
159 if (val == isGraphDisplay.value) {
160 return;
161 }
162 isGraphDisplay.value = val;
163 if (!val) {
164 getSankeyDataList();
165 }
166 }
167
168 const getSankeyDataList = () => {
169 sankeyDataLoading.value = true;
170 getSankeyData(lastClickNode.value.guid).then((res: any) => {
171 sankeyDataLoading.value = false;
172 if (res?.code == proxy.$passCode) {
173 sankeyData.value = res.data?.links || [];
174 sankeyNames.value = res.data?.data || [];
175 } else {
176 ElMessage.error(res.msg);
177 }
178 })
179 }
180
181 const handleNodeItemClick = (graph, nodeItem) => {
182 const nodeId = nodeItem.get('id');
183 const parentData = graph.findDataById(nodeId);
184 if (!parentData.children) {
185 parentData.children = [];
186 }
187 if (parentData.noData) {
188 ElMessage.warning('没有可展开的下级字段');
189 return;
190 }
191 treeDataLoading.value = true;
192 getMetaStandardField(nodeId).then((res: any) => {
193 treeDataLoading.value = false;
194 if (res?.code == proxy.$passCode) {
195 const data = res.data || [];
196 parentData.children = [];
197 if (!data?.length) {
198 parentData.noData = true;
199 ElMessage.warning('没有可展开的下级字段');
200 return;
201 }
202 data.forEach(d => {
203 parentData.children.push(d);
204 })
205 parentData.isLoading = false;
206 parentData.collapsed = false;
207 graph.layout();
208 setTimeout(() => {
209 graph.updateItem(nodeItem, {
210 ...nodeItem.getModel(),
211 collapsed: false
212 });
213 graph.setMinZoom(1);
214 graph.setMaxZoom(1);
215 graph.layout();
216 graph.setMinZoom(0.5);
217 graph.setMaxZoom(5);
218 graph.focusItem(nodeItem, true, {
219 duration: 500 // 动画时长为500ms
220 });
221 }, 500);
222 } else {
223 parentData.isLoading = false;
224 ElMessage.error(res.msg);
225 }
226 })
227 }
228
229 const handleContextMenu = (model) => {
230 if (model.isHaveData == 'N') {
231 ElMessage.warning('当前标准下无字段,请先添加字段');
232 return;
233 }
234 contextNodeData.value = model;
235 dialogInfo.value.visible = true;
236 formItems.value[0].default = '';
237 getDataWareCatalogList({}).then((res: any) => {
238 if (res?.code == proxy.$passCode) {
239 dataCatalogList.value = res.data || [];
240 formItems.value[0].options = dataCatalogList.value;
241 } else {
242 ElMessage.error(res.msg);
243 }
244 })
245 }
246
247 onBeforeUnmount(() => {
248 relationNetworkRef.value.destroy();
249 })
250
251 /** 数仓目录树形列表 */
252 const dataCatalogList = ref([]);
253
254 const formItems = ref([{
255 label: "数仓目录",
256 type: "tree-select",
257 placeholder: "请选择",
258 field: "domainGuid",
259 default: '',
260 options: dataCatalogList.value,
261 props: {
262 label: 'name',
263 value: 'guid'
264 },
265 showAllLevels: false,
266 checkStrictly: false,//只能选择叶子节点。
267 lazy: false,
268 filterable: true,
269 clearable: true,
270 required: true,
271 }]);
272
273 const formRules = ref({
274 domainGuid: [required('请选择数仓目录')],
275 });
276
277 const dialogInfo = ref({
278 visible: false,
279 size: 400,
280 direction: "column",
281 header: {
282 title: "引用标准新建数据集",
283 },
284 type: '',
285 contents: [
286 {
287 type: 'form',
288 title: '',
289 formInfo: {
290 id: 'select-subject-domain-list',
291 items: formItems.value,
292 rules: formRules.value
293 }
294 }
295 ],
296 footer: {
297 btns: [
298 { type: "default", label: "取消", value: "cancel" },
299 { type: "primary", label: "确定", value: "submit", loading: false },
300 ],
301 },
302 });
303
304 const contextNodeData: any = ref({});
305
306 const selectDataCatalogNodeObj: any = ref({});
307
308 const handleTreeSelectNodeChange = (node, item, nodeObj) => {
309 selectDataCatalogNodeObj.value = nodeObj;
310 }
311
312 const dialogBtnClick = (btn, info) => {
313 if (btn.value == 'submit') {
314 dialogInfo.value.visible = false;
315 if (selectDataCatalogNodeObj.value.parent.data.layereAttribute == 2) { //维度
316 router.push({
317 name: 'dimTableCreateManual',
318 query: {
319 domainGuid: info.domainGuid,
320 domainName: selectDataCatalogNodeObj.value.data.name,
321 metaStandard: contextNodeData.value.guid,
322 standardName: contextNodeData.value.label
323 }
324 });
325 } else {
326 if (selectDataCatalogNodeObj.value.parent.data.layereAttribute == 4) {
327 router.push({
328 name: 'tableCreateManual',
329 query: {
330 domainGuid: info.domainGuid,
331 domainName: selectDataCatalogNodeObj.value.data.name,
332 metaStandard: contextNodeData.value.guid,
333 layereAttribute: selectDataCatalogNodeObj.value.parent.data.layereAttribute,
334 standardName: contextNodeData.value.label
335 }
336 });
337 } else {
338 router.push({
339 name: 'tableCreateManual',
340 query: {
341 domainGuid: info.domainGuid,
342 domainName: selectDataCatalogNodeObj.value.data.name,
343 metaStandard: contextNodeData.value.guid,
344 standardName: contextNodeData.value.label
345 }
346 });
347 }
348 }
349 } else if (btn.value == 'cancel') {
350 dialogInfo.value.visible = false;
351 }
352 }
353
354 </script>
355
356 <template>
357 <div class="container_wrap full flex">
358 <div class="aside_wrap">
359 <div class="aside_title">元数据标准列表</div>
360 <Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" />
361 </div>
362 <div class="main_wrap">
363 <div v-show="lastClickNode?.guid" className='g6-component-topbar'>
364 <graphTopbar ref="topBarRef" @displaySwitchChange="displaySwitchChange" :isGraphDisplay="isGraphDisplay" />
365 </div>
366 <RelationNetwork v-show="lastClickNode?.guid && isGraphDisplay" ref="relationNetworkRef"
367 :tree-data="lastClickNode" v-loading="treeDataLoading" @nodeItemClick="handleNodeItemClick"
368 @contextMenu="handleContextMenu">
369 </RelationNetwork>
370 <Sankey v-show="lastClickNode?.guid && !isGraphDisplay && (sankeyNames?.length || sankeyDataLoading)"
371 v-loading="sankeyDataLoading" :tree-data="sankeyData" :names="sankeyNames">
372 </Sankey>
373 <div
374 v-show="(!lastClickNode?.guid || !treeInfo.data?.length) && !treeInfo.loading || (!isGraphDisplay && !sankeyDataLoading && !sankeyNames?.length)"
375 class="main-placeholder">
376 <img src="../../assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
377 <div class="empty-text">{{ treeInfo.data.length && !lastClickNode?.guid ? '请选中二级及以下级别标准查看' : '暂无数据' }}</div>
378 </div>
379 </div>
380 <Dialog :dialogInfo="dialogInfo" @btnClick="dialogBtnClick" @treeSelectNodeChange="handleTreeSelectNodeChange" />
381 </div>
382 </template>
383
384 <style scoped lang="scss">
385 .container_wrap {
386
387 .aside_wrap {
388 width: 200px;
389 margin-right: 1px;
390 }
391
392 .main_wrap {
393 position: relative;
394
395 :deep(.canvas-wrapper) {
396 background-color: #f7f7f9;
397 }
398
399 .main-placeholder {
400 height: 100%;
401 display: flex;
402 justify-content: center;
403 align-items: center;
404 flex-direction: column;
405
406 .empty-text {
407 font-size: 14px;
408 color: #b2b2b2;
409 }
410 }
411 }
412
413 }
414
415 .g6-component-topbar {
416 position: absolute;
417 left: 24px;
418 bottom: unset;
419 top: 14px;
420 padding: 0;
421 text-align: center;
422 z-index: 999;
423 }
424
425 .container_wrap.flex .main_wrap {
426 padding: 0px;
427 }
428
429 .tree_panel {
430 height: calc(100% - 36px);
431 padding-top: 0;
432
433 :deep(.el-tree) {
434 margin: 0;
435 overflow: hidden auto;
436 }
437 }
438
439 .card-noData {
440 height: 100%;
441 width: 100%;
442 background: #fafafa;
443 display: flex;
444 flex-direction: column;
445 justify-content: center;
446 align-items: center;
447 color: #909399;
448 font-size: 14px;
449 }
450
451 :deep(.el-form .el-form-item) {
452 width: calc(100%);
453 // margin-right: 8px;
454 }
455
456 :deep(.el-message) {
457 position: fixed;
458 /* 使用fixed或absolute定位 */
459 z-index: 10000;
460 /* 设置一个较高的z-index值确保在最上层显示 */
461 }
462 </style>
1 <route lang="yaml">
2 ame: metadataStandard
3 </route>
4
5 <script lang="ts" setup name="metadataStandard">
6 import { ref, reactive, computed } from 'vue'
7 import { ElMessage, ElMessageBox } from "element-plus";
8 import { Search, CirclePlus } from '@element-plus/icons-vue'
9 import Tree from '@/components/Tree/index.vue'
10 import Table from '@/components/Table/index.vue'
11 import Drawer from '@/components/Drawer/index.vue'
12 import DictFileds from './components/dictFileds.vue'
13 import useCatchStore from "@/store/modules/catch";
14 import { download } from '@/utils/common'
15 import { getParamsList } from '@/api/modules/dataAsset'
16 import { getMetaStandardTree, deleteMetaStandard,
17 getMetaStandardDataList, getMetaStandardDataFields,
18 deleteMetaStandardDataFields, exportMetaStandardData
19 } from '@/api/modules/dataMetaService'
20 import router from '@/router'
21 import { TableColumnWidth } from '@/utils/enum';
22 import StandardDialog from './components/standardDialog.vue'
23 import StandardFieldsDialog from './components/standardFieldsDialog.vue'
24 function getAssetsImages(name) {
25 return new URL(`../../assets/images/${name}`, import.meta.url).href;
26 }
27 const { proxy } = getCurrentInstance() as any;
28
29 const route = useRoute();
30
31 const cacheStore = useCatchStore()
32 const showFiledsPage = ref(false)
33
34 // 树菜单
35 const dictTreeRef = ref()
36 const treeInfo = ref({
37 id: "data-pickup-tree",
38 filter: true,
39 queryValue: "",
40 queryPlaceholder: "请输入关键字搜索",
41 props: {
42 label: "standardName",
43 value: "guid",
44 isLeaf: "isLeaf",
45 },
46 // lazy: true,
47 nodeKey: 'guid',
48 expandedKey: [],
49 currentNodeKey: '',
50 data: [],
51 expandOnNodeClick: false,
52 loading: false,
53 currentObj: {},
54 editTreeItem: true,
55 // className: 'tree-list',
56 ellipsis: false
57 })
58 function nodeClick (data) {
59 console.log('nodeData', data)
60 treeInfo.value.currentObj = data
61 if (data.level == 1) return
62 getTableFields()
63 getFirstPageData()
64 }
65 function treeCustomClick (node, type) {
66 console.log(node, type)
67 if (type === 'edit') {
68 // 编辑
69 standardDialog.type = 'edit'
70 standardDialog.guid = node.data.guid
71 node.data.level = node.level
72 standardDialog.currentNode = node.data
73 standardDialog.visible = true
74 return
75 }
76 if (type === 'delete') {
77 // 删除
78 ElMessageBox.confirm('确定删除吗?', '提示', {
79 confirmButtonText: '确定',
80 cancelButtonText: '取消',
81 type: 'warning'
82 }).then(() => deleteTreeNode(node.data.guid))
83 }
84 }
85 function getTree (refresh = false, initLocate = false) {
86 treeInfo.value.loading = true;
87 getMetaStandardTree().then((res:any) => {
88 treeInfo.value.loading = false;
89 if (res.code === proxy.$passCode) {
90 let data = res.data || []
91 data.forEach(item => {
92 item.showEdit = true
93 item.level = 1
94 })
95 treeInfo.value.data = data
96 if (!initLocate) {
97 // treeInfo.value.expandedKey = [data[0].guid]
98 // treeInfo.value.currentNodeKey = data[0].guid
99 console.log('data', data)
100 let firstNode = data[0]
101 if (firstNode.children) {
102 firstNode = firstNode.children[0]
103 }
104 treeInfo.value.expandedKey = [firstNode.guid]
105 treeInfo.value.currentNodeKey = firstNode.guid
106 nodeClick(firstNode)
107 }
108 }
109 })
110 }
111 function refreshTree (treeGuid) {
112 treeInfo.value.loading = true;
113 getMetaStandardTree().then((res:any) => {
114 treeInfo.value.loading = false;
115 if (res.code === proxy.$passCode) {
116 let data = res.data || []
117 data.forEach(item => {
118 item.showEdit = true
119 item.level = 1
120 })
121 treeInfo.value.data = data
122 if (treeGuid) {
123 setTreeCurrentNode(treeGuid)
124 }
125 }
126 })
127 }
128 function refreshTreeAndPage () {
129 refreshTree()
130 getFirstPageData()
131 }
132 function setTreeCurrentNode (treeGuid) {
133 treeInfo.value.currentNodeKey = treeGuid
134 let { data } = treeInfo.value
135 const format = (list) => {
136 list.forEach(item => {
137 if (item.guid === treeGuid) {
138 nodeClick(item)
139 return
140 }
141 if (item.children) {
142 format(item.children)
143 }
144 })
145 }
146 format(data)
147 }
148
149 function deleteTreeNode (guid) {
150 deleteMetaStandard([guid]).then((res:any) => {
151 if (res.code === proxy.$passCode) {
152 ElMessage.success('删除成功')
153 getTree()
154 } else {
155 ElMessage.error(res.msg)
156 }
157 })
158 }
159
160 const tableSearchInput = ref('')
161 const currTableData: any = ref<Object>({});
162 const page = ref({
163 limit: 50,
164 curr: 1,
165 sizes: [
166 { label: "10", value: 10 },
167 { label: "50", value: 50 },
168 { label: "100", value: 100 },
169 { label: "150", value: 150 },
170 { label: "200", value: 200 },
171 ]
172 })
173 const selectRowData = ref([])
174 const selectedRowData = ref([])
175 const tableInfo: any = ref({
176 id: 'data-source-table',
177 multiple: true,
178 fixedSelection: true,
179 fields: [
180 { label: "序号", type: "index", width: 56, align: "center" },
181 // { label: '数据源标识符', field: 'codeName', width: 140 },
182 // { label: '数据元名称', field: 'standard', width: 140 },
183 // { label: '定义', field: 'standardName', width: 140 },
184 // { label: '数据类型', field: '', width: 120 },
185 // { label: '表示格式', field: 'createTime', width: TableColumnWidth }
186 ],
187 data: [],
188 page: {
189 type: "normal",
190 rows: 0,
191 ...page.value,
192 },
193 actionInfo: {
194 label: "操作",
195 type: "btn",
196 width: 92, //不要刚好90.缩小浏览器会因为小数点的差距而换行,
197 fixed: 'right',
198 btns: [
199 // { label: '查看', value: 'detail'},
200 { label: "编辑", value: "edit" },
201 { label: "删除", value: "delete" },
202 ],
203 },
204 loading: false
205 })
206 const standardFields = ref([])
207 function getFirstPageData () {
208 page.value.curr = 1
209 toSearch({})
210 }
211 function toSearch (val: any, clear: boolean = false) {
212 let params: any = Object.keys(val).length ? { ...val } : {}
213 let { currentNodeKey, currentObj } = treeInfo.value
214 params.pageIndex = page.value.curr;
215 params.pageSize = page.value.limit;
216 params.metaStandardGuid = currentObj.guid
217 params.keyWords = tableSearchInput.value
218 getTable(params)
219 }
220 function getTable (params) {
221 tableInfo.value.loading = true
222 getMetaStandardDataList(params).then((res:any) => {
223 if (res.code === proxy.$passCode) {
224 let data = res.data
225 let list = res.data.records || []
226 list.forEach((item:any) => {
227 Object.keys(item.metaStandardValue).forEach(key => {
228 item[key] = item.metaStandardValue[key]
229 })
230 })
231 tableInfo.value.data = list
232 tableInfo.value.page.limit = data.pageSize
233 tableInfo.value.page.curr = data.pageIndex
234 tableInfo.value.page.rows = data.totalRows
235 }
236 }).finally(() => tableInfo.value.loading = false)
237 }
238 function getTableFields () {
239 let { currentObj } = treeInfo.value
240 const indexField = { label: '序号', type: 'index', width: 56, align: 'center' }
241 getMetaStandardDataFields(currentObj.guid).then((res:any) => {
242 if (res.code === proxy.$passCode && res.data) {
243 const data = res.data
244 standardFields.value = data
245 const fields = data.map(item => {
246 return {
247 label: item.fileNameCodeName,
248 field: item.fileNameCode,
249 width: 140
250 }
251 })
252 fields.unshift(indexField)
253 tableInfo.value.fields = fields
254 } else {
255 tableInfo.value.fields = [indexField]
256 }
257 })
258 }
259 function tablePageChange (info) {
260 page.value.curr = Number(info.curr)
261 page.value.limit = Number(info.limit)
262 toSearch({})
263 }
264 function tableBtnClick (scope, btn) {
265 console.log(scope, btn)
266 const type = btn.value
267 const row = scope.row
268 if (type === 'edit') {
269 openStandardFieldsDialog(type, row)
270 } else if (type === 'delete') {
271 ElMessageBox.confirm('确定删除吗?', '提示', {
272 confirmButtonText: "确定",
273 cancelButtonText: "取消",
274 type: 'warning',
275 }).then(() => deleteStandardFields(row))
276 }
277 }
278 function deleteStandardFields (row) {
279 deleteMetaStandardDataFields([row.guid]).then((res:any) => {
280 if (res.code === proxy.$passCode) {
281 ElMessage.success('删除成功')
282 refreshTreeAndPage()
283 } else {
284 ElMessage.error(res.msg)
285 }
286 })
287 }
288
289 const formItems: any = ref([
290 {
291 label: '中文名称',
292 type: 'input',
293 placeholder: '请输入',
294 field: 'codeName',
295 default: '',
296 maxlength: 20,
297 clearable: true,
298 required: true
299 },
300 {
301 label: '英文生成规则',
302 type: 'select',
303 placeholder: '请选择',
304 field: 'publishingUnitCode',
305 default: '',
306 options: [],
307 clearable: true,
308 required: true
309 },
310 {
311 label: '英文名称',
312 type: 'input',
313 placeholder: '请输入',
314 field: 'orderNum',
315 default: '',
316 maxlength: 2,
317 clearable: true,
318 required: true
319 },
320 {
321 label: '标准编号',
322 type: 'input',
323 placeholder: '请选择',
324 field: 'codeFieldName',
325 default: '',
326 clearable: true,
327 required: true,
328 visible: true
329 },
330 {
331 label: '标准集',
332 type: 'Select',
333 placeholder: '请选择',
334 field: 'hierarchy',
335 default: '',
336 options: [],
337 clearable: true,
338 visible: false
339 },
340 {
341 label: '字段类型',
342 type: 'Select',
343 placeholder: '请选择',
344 field: 'hierarchy',
345 default: '',
346 options: [],
347 clearable: true,
348 visible: false
349 },
350 {
351 label: '数据分类',
352 type: 'Select',
353 placeholder: '请选择',
354 field: 'hierarchy',
355 default: '',
356 options: [],
357 clearable: true,
358 visible: false
359 },
360 {
361 label: '数据加密等级',
362 type: 'Select',
363 placeholder: '请选择',
364 field: 'hierarchy',
365 default: '',
366 options: [],
367 clearable: true,
368 visible: false
369 },
370 {
371 label: '关联数据字典',
372 type: 'checkbox',
373 placeholder: '请选择',
374 field: 'hierarchy',
375 default: '',
376 options: [],
377 clearable: true,
378 visible: false
379 },
380 {
381 label: '业务定义',
382 type: 'textarea',
383 placeholder: '请选择',
384 field: 'hierarchy',
385 default: '',
386 options: [],
387 clearable: true,
388 visible: false
389 }
390 ])
391 const formRules: any = ref({
392 standardTypeCode: { required: true, message: '请选择标准类型' },
393 codeName: { required: true, message: '请输入代码名称' },
394 standard: { required: true, message: '请输入标准号' },
395 standardName: { required: true, message: '请输入标准名称' },
396 publishingUnitCode: { required: true, message: '请选择发布单位' },
397 orderNum: { required: true, message: '请输入排序' },
398 typeCode: { required: true, message: '请选择代码类型' },
399 codeFields: { required: true, message: '请选择编码字段' },
400 codeFieldName: { required: true, message: '请选择编码名称' }
401 })
402 const formInfo = ref({
403 type: 'form',
404 title: '',
405 col: 'span',
406 formInfo: {
407 id: 'add-dict-form',
408 col: 'col2',
409 readonly: false,
410 items: formItems.value,
411 rules: formRules.value
412 }
413 })
414
415 // 元标准dialog
416 const standardDialog = reactive({
417 visible: false,
418 type: 'add',
419 guid: null,
420 currentNode: null
421 })
422 function openStandardDialog () {
423 standardDialog.type = 'add'
424 standardDialog.visible = true
425 }
426 // 元标准数据dialog
427 const standardFieldsDialog = reactive({
428 visible: false,
429 type: 'add',
430 metaStandardGuid: null,
431 data: {}
432 })
433 function openStandardFieldsDialog (type, data = {}) {
434 standardFieldsDialog.type = type
435 standardFieldsDialog.metaStandardGuid = treeInfo.value.currentObj.guid
436 standardFieldsDialog.data = data
437 standardFieldsDialog.visible = true
438 }
439
440 function importData () {
441 let currentTreeObj:any = treeInfo.value.currentObj
442 console.log('currentTree', currentTreeObj)
443 let uploadSetting:any = []
444 if (currentTreeObj.children) {
445 formatChildrenLastItem(currentTreeObj.children, uploadSetting)
446 } else {
447 uploadSetting = [{
448 standardName: currentTreeObj.standardName,
449 standardGuid: currentTreeObj.guid
450 }]
451 }
452 cacheStore.setCatch('uploadSetting', uploadSetting)
453 router.push({
454 path: '/data-meta/metadata-standard/standard-meta-import',
455 });
456 }
457 function formatChildrenLastItem (list, returnList = []) {
458 list.forEach((item:any) => {
459 if (!item.children) {
460 returnList.push({
461 standardName: item.standardName,
462 standardGuid: item.guid
463 })
464 } else {
465 formatChildrenLastItem(item.children, returnList)
466 }
467 })
468 }
469
470 function exportData () {
471 let body = [treeInfo.value.currentObj.guid]
472 exportMetaStandardData(body).then((res:any) => {
473 if (res && !res.msg) {
474 download(res, '元数据标准表.xlsx', 'excel')
475 } else {
476 res?.msg && ElMessage.error(res?.msg);
477 }
478 })
479 }
480
481 function tableSelectionChange (list) {
482 selectedRowData.value = list
483 }
484 function batchDelete () {
485 // console.log(selectedRowData.value)
486 let selectRowGuids = selectedRowData.value.map(v => v.guid)
487 if (selectRowGuids.length === 0) {
488 ElMessage.error('请选择需要删除的数据')
489 return
490 }
491 ElMessageBox.confirm('确定删除吗?', '提示', {
492 confirmButtonText: "确定",
493 cancelButtonText: "取消",
494 type: 'warning',
495 }).then(() => {
496 deleteMetaStandardDataFields(selectRowGuids).then((res:any) => {
497 if (res.code === proxy.$passCode) {
498 ElMessage.success('删除成功')
499 refreshTreeAndPage()
500 } else {
501 ElMessage.error(res.msg)
502 }
503 })
504 })
505 }
506
507 /** 从关系网定位打开此页面,需定位到对应的树节点。 route.query.standardGuid */
508
509 onActivated(() => {
510 if (route.query.standardGuid) {
511 treeInfo.value.currentNodeKey = route.query.standardGuid as string;
512 let obj = {
513 guid: route.query.standardGuid,
514 standardName: route.query.name
515 }
516 treeInfo.value.currentObj = obj;
517 nodeClick(obj);
518 }
519 })
520
521 onBeforeMount(() => {
522 getTree(false, route.query.standardGuid != null)
523 })
524
525 const viewGraph = () => {
526 router.push({
527 name: 'metadataStandardQueryView',
528 query: {
529 guid: treeInfo.value.currentObj?.guid,
530 name: treeInfo.value.currentObj?.standardName,
531 }
532 });
533 }
534
535 </script>
536
537 <template>
538 <div class="container_wrap full flex standard">
539 <div class="aside_wrap">
540 <div class="aside_title">
541 元数据标准列表
542 <el-icon color="#4fa1a4" @click="openStandardDialog" :size="20" class="custom-icon">
543 <CirclePlus />
544 </el-icon>
545 </div>
546 <Tree ref="dictTreeRef" :treeInfo="treeInfo" @nodeClick="nodeClick" @itemMenuClick="treeCustomClick">
547 <template #default="{ node, data }">
548 {{ node.label }}
549 </template>
550 </Tree>
551 </div>
552 <div class="main_wrap">
553 <template v-if="treeInfo.currentObj.level != 1">
554 <div class="header" style="font-size:16px;font-weight:bold;margin-top:8px;color:#212121">{{ treeInfo.currentObj.standardName }}</div>
555 <div class="table_tool_wrap">
556 <div class="tools_btns">
557 <el-button type="primary"
558 @click="() => openStandardFieldsDialog('add')"
559 :disabled="treeInfo.currentObj.children"
560 v-preReClick>新建</el-button>
561 <el-button @click="importData" v-preReClick>导入</el-button>
562 <el-button @click="exportData" v-preReClick>导出</el-button>
563 <el-button @click="viewGraph" v-preReClick>查看</el-button>
564 <el-button @click="batchDelete">批量删除</el-button>
565 </div>
566 <el-input class="table_search_input" v-model.trim="tableSearchInput" placeholder="请输入关键字搜索"
567 :suffix-icon="Search" clearable @change="val => getFirstPageData()" />
568 </div>
569 <div class="table_panel_wrap full" style="height:calc(100% - 84px)">
570 <Table :tableInfo="tableInfo" @tableBtnClick="tableBtnClick" @tableSelectionChange="tableSelectionChange"
571 @tablePageChange="tablePageChange"/>
572 </div>
573 </template>
574
575 <div v-else class="no-content">
576 <img :src="getAssetsImages('no-data.png')" :style="{ width: '96px', height: '96px' }" />
577 <span>请选择标准表</span>
578 </div>
579 </div>
580 <StandardDialog
581 v-model="standardDialog.visible"
582 :standardOptions="treeInfo.data"
583 :type="standardDialog.type"
584 :guid="standardDialog.guid"
585 :currentNode="standardDialog.currentNode"
586 @success="refreshTree"
587 />
588 <StandardFieldsDialog
589 v-model="standardFieldsDialog.visible"
590 :fields="standardFields"
591 :type="standardFieldsDialog.type"
592 :metaStandardGuid="standardFieldsDialog.metaStandardGuid"
593 :data="standardFieldsDialog.data"
594 @success="refreshTreeAndPage"
595 />
596 </div>
597 </template>
598
599 <style lang="scss">
600
601 </style>
602 <style lang="scss" scoped>
603 .no-content {
604 height: 96%;
605 display: flex;
606 flex-direction: column;
607 justify-content: center;
608 align-items: center;
609 }
610 .container_wrap {
611 .aside_wrap {
612 width: 200px;
613 }
614 .aside_title {
615 display: flex;
616 justify-content: space-between;
617 align-items: center;
618 }
619 }
620
621 .tree_panel {
622 height: 100%;
623 padding-top: 0;
624
625 :deep(.el-tree) {
626 margin: 0;
627 height: calc(100% - 68px);
628 overflow: hidden auto;
629 }
630 }
631 </style>
632
633 <style lang="scss">
634 .standard {
635 .custom-icon {
636 svg {
637 width: auto;
638 height: auto;
639 }
640 }
641 }
642 .tree_panel .el-tree .el-tree-node__content {
643 position: relative;
644 .list-item-text {
645 width: calc(100% - 58px)!important;
646 }
647 .tags-list-right {
648 position: absolute;
649 right: 2px;
650 top:8px;
651 }
652 }
653
654 </style>
1 <route lang="yaml">
2 name: tableCreateManual
3 </route>
4
5 <script lang="ts" setup name="tableCreateManual">
6 import { ref } from "vue";
7 import { useRouter, useRoute } from "vue-router";
8 import { ElMessage, ElMessageBox, ElTable } from "element-plus";
9 import { QuestionFilled } from "@element-plus/icons-vue";
10 import {
11 getDatabase,
12 saveSubjectTable,
13 syncPolicys,
14 tableModels,
15 tableCategoryList,
16 getDimList,
17 getDictionary,
18 aggMethodList,
19 getMetaStandardDsField,
20 getAllFlowData
21 } from "@/api/modules/dataMetaService";
22 import useUserStore from "@/store/modules/user";
23 import expandPropertyDialog from "./components/expandPropertyDialog.vue";
24 import tableDefaultValue from "./components/tableDefaultValue.vue";
25 import { useDefault } from "@/hooks/useDefault"
26 import { getCamundaDeploymentId } from "@/api/modules/workFlowService"
27
28 const userStore = useUserStore();
29 const { checkDefault } = useDefault()
30 const router = useRouter();
31 const route = useRoute();
32 const flowExpand = ref(true);
33 const deploymentId = ref('');
34 const fullPath = route.fullPath;
35 const isLook = <any>route.query.isLook == 1;
36 const subjectDomainGuid: any = ref(route.query.domainGuid);
37 const { proxy } = getCurrentInstance() as any;
38 const fieldStandardTableRef = ref<InstanceType<typeof ElTable>>();
39
40 /** 表模型,只有doris数据库才有 */
41 const dbType = ref("");
42
43 const databaseList: any = ref([]);
44
45 const tableFieldsLoading = ref(false);
46
47 const tableCreateInfoLoading = ref(false);
48
49 //记录当前正在编辑的表创建信息。
50 const tableCreateInfo: Ref<any> = ref({
51 guid: "",
52 isCreate: false,
53 inputNameValue: '',
54 tableCreateType: 1,
55 tableData: [
56 {
57 //数据库表信息。
58 dataSourceGuid: '',
59 dataServerName: "",
60 dataServerChName: "",
61 enName: "",
62 chName: "",
63 subjectDomain: route.query.domainName,
64 subjectDomainGuid: subjectDomainGuid.value,
65 tableCategory: route.query.layereAttribute == '3' ? 6 : 1,
66 syncPolicy: 3,
67 characterSet: 'utf8mb3',
68 tableModel: 1, //若是聚合模型,下方出现一列聚合方式选择。处了主键列,其余列都需要选择。每个表里都要有主键。
69 description: "",
70 },
71 ],
72 partitionAttribute: {},
73 tableFields: [], // 字段标准数组。
74 });
75
76 const selectTableFieldRows = ref([]);
77
78 //字段类型
79 const fieldTypes: any = ref([]);
80
81 //字符集
82 const characterList: any = ref([]);
83
84 //是否列表
85 const isNotList = ref([
86 {
87 label: "Y",
88 value: "Y",
89 },
90 {
91 label: "N",
92 value: "N",
93 },
94 ]);
95
96 //字典列表
97 const dictionaryList: any = ref([]);
98
99 // 可选择的关联维度的列表。
100 const dimListData: any = ref([]);
101
102 const fullscreenLoading = ref(false);
103
104 /** 表里有数据时不能修改字段类型,长度,精度 */
105 const hasSubjectData = ref(false);
106
107 const expandProperties = ref({});
108
109 onBeforeMount(() => {
110 getDatabaseList();
111 getFieldTypeList();
112 getCharacterListData();
113 if (route.query.metaStandard) {
114 tableCreateInfo.value.tableData[0].chName = route.query.standardName;
115 fullscreenLoading.value = true;
116 getMetaStandardDsField(route.query.metaStandard).then((res: any) => {
117 fullscreenLoading.value = false;
118 if (res.code == proxy.$passCode) {
119 tableCreateInfo.value.tableFields = res.data?.map((d, i) => {
120 d.orderNum = i;
121 d.isEdit = true;
122 return d;
123 }) || [];
124 } else {
125 proxy.$ElMessage.error(res.msg);
126 }
127 })
128 }
129 getCamundaDeploymentId('10025').then((res: any) => {
130 if (res.code == proxy.$passCode) {
131 deploymentId.value = res.data;
132 } else {
133 proxy.$ElMessage.error(res.msg);
134 }
135 })
136 })
137
138 onActivated(() => {
139 console.log("activated");
140 getDictionaryList();
141 getDimListData();
142 });
143
144
145 const getDatabaseList = () => {
146 getDatabase({ connectStatus: 1 }).then((res: any) => {
147 databaseList.value = [];
148 if (res.code == proxy.$passCode) {
149 databaseList.value = res.data || [];
150 } else {
151 ElMessage.error(res.msg);
152 }
153 })
154 };
155
156 const getDictionaryList = () => {
157 getDictionary({}).then((res: any) => {
158 dictionaryList.value = [];
159 if (res.code == proxy.$passCode) {
160 dictionaryList.value = res.data || [];
161 dimOrDictList.value[0].children = dictionaryList.value;
162 dimOrDictList.value[0].disabled = !dictionaryList.value.length;
163 } else {
164 ElMessage.error(res.msg);
165 }
166 })
167 };
168
169 const getDimListData = () => {
170 getDimList().then((res: any) => {
171 dimListData.value = [];
172 if (res.code == proxy.$passCode) {
173 dimListData.value = res.data?.map(r => {
174 return {
175 guid: r.guid,
176 chName: r.chName,
177 subjectDomainName: r.subjectDomainName,
178 parentGuid: '2'
179 }
180 }) || [];
181 dimOrDictList.value[1].children = dimListData.value;
182 dimOrDictList.value[1].disabled = !dimListData.value.length;
183 } else {
184 ElMessage.error(res.msg);
185 }
186 })
187 };
188
189 const getFieldTypeList = () => {
190 getAllFlowData('字段类型').then((res: any) => {
191 fieldTypes.value = [];
192 if (res.code == proxy.$passCode) {
193 fieldTypes.value = res.data || [];
194 } else {
195 ElMessage.error(res.msg);
196 }
197 })
198 }
199
200 const getCharacterListData = () => {
201 getAllFlowData('Mysql字符集').then((res: any) => {
202 characterList.value = [];
203 if (res.code == proxy.$passCode) {
204 characterList.value = res.data || [];
205 } else {
206 ElMessage.error(res.msg);
207 }
208 })
209 }
210
211 /** 限制长度输入框只能输入整型数字,表,字段英文名称,限制输入字符,数字和下划线。 */
212 const inputLengthKeyUp = (regexp, scope, field, max: any = null, min: any = null) => {
213 scope.row[field] = scope.row[field].replace(regexp, '');
214 if (field == 'fieldLength' && scope.row.dataType == 'decimal') {
215 max = 65;
216 }
217 /** 最大值设置2000 */
218 if (max && scope.row[field] > max) {
219 scope.row[field] = max;
220 }
221 if (min !== null && scope.row[field] != '' && scope.row[field] <= min) {
222 scope.row[field] = min;
223 }
224 }
225
226 /** 保存表 */
227 const saveTable = () => {
228 let tableData = tableCreateInfo.value.tableData[0];
229 if (!tableData.chName) {
230 ElMessage({
231 type: "error",
232 message: "主题表名称不能为空",
233 });
234 return;
235 }
236 if (!tableData.dataServerName) {
237 ElMessage({
238 type: "error",
239 message: "数据源不能为空",
240 });
241 return;
242 }
243 if (tableData.dataServerName.indexOf('-') > -1) {
244 ElMessage.error('数据库表名称不能包含中划线,可以改为下划线');
245 return;
246 }
247 if (!tableCreateInfo.value.inputNameValue) {
248 ElMessage({
249 type: "error",
250 message: "主题表名称不能为空",
251 });
252 return;
253 }
254 let tableFields = tableCreateInfo.value.tableFields;
255 if (!tableFields.length) {
256 ElMessage({
257 type: "error",
258 message: "表字段不能为0行",
259 });
260 return;
261 }
262 // 若开启了字段标准,则不能为空。
263 // 必须含有主键。若是聚合模型,则除了主键必须有聚合方式。
264 let isSumModel = tableData.tableModel === 2;
265 let hasPrimary = false;
266 let enNames: any = [];
267 let chNames: any = [];
268 const regex = /^[a-zA-Z]/;
269 for (const field of tableFields) {
270 if (!field.enName) {
271 ElMessage.error(`第 ${field.orderNum} 个字段的英文名称不能为空`);
272 return;
273 }
274 if (!regex.test(field.enName)) {
275 ElMessage.error(`第 ${field.orderNum} 个字段的英文名称必须以英文字符开头`);
276 return;
277 }
278 if (!field.dataType) {
279 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型不能为空`);
280 return;
281 }
282 if (enNames.indexOf(field.enName) > -1) {
283 ElMessage.error(`字段的英文名称 ${field.enName} 不能重复`);
284 return;
285 }
286 if (chNames.indexOf(field.chName) > -1) {
287 ElMessage.error(`字段的中文名称 ${field.chName} 不能重复`);
288 return;
289 }
290 if (field.dataType === "decimal" && (!field.fieldPrecision && field.fieldPrecision != 0)) {
291 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点型时,精度不能为空`);
292 return;
293 }
294 if (field.dataType === "varchar" && (!field.fieldLength && field.fieldLength != 0)) {
295 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为字符型时,长度不能为空`);
296 return;
297 }
298 if (field.dataType === "char" && (!field.fieldLength && field.fieldLength != 0)) {
299 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为单字符型时,长度不能为空`);
300 return;
301 }
302 if (field.dataType === "decimal" && (!field.fieldLength && field.fieldLength != 0)) {
303 ElMessage.error(`第 ${field.orderNum} 个字段的字段类型为浮点符型时,长度不能为空`);
304 return;
305 }
306 if (field.isPrimary === 'Y') {
307 hasPrimary = true;
308 if (field.notNull != 'Y') {
309 ElMessage.error(`第 ${field.orderNum} 个字段为主键,应设置为必填`);
310 return;
311 }
312 if (field.dataType == 'text') {
313 ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘大字段型’`);
314 return;
315 }
316 if (field.dataType == 'json') {
317 ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘JSON类型’`);
318 return;
319 }
320 if (field.dataType == 'bit') {
321 ElMessage.error(`第 ${field.orderNum} 个字段为主键,字段类型不能设置为‘布尔类型’`);
322 return;
323 }
324 } else {
325 if (!field.aggWay && isSumModel) {
326 ElMessage.error(`聚合模型的非主键字段必须设置聚合方式`);
327 return;
328 }
329 }
330 if (field.isEdit) {
331 if (checkDefault[field.dataType]) {
332 if (!field.fieldLength) { }
333 if (!checkDefault[field.dataType]({ row: field })) {
334 return;
335 }
336 }
337 }
338 field.fieldStandardGuid = route.query.metaStandard;
339 enNames.push(field.enName);
340 chNames.push(field.chName);
341 }
342 if (!hasPrimary) {
343 ElMessage.error(`字段至少有一个主键字段!`);
344 return;
345 }
346 let addInfo = Object.assign({}, tableCreateInfo.value.tableData[0], {
347 enName: tableCreateInfo.value.inputNameValue,
348 tableCreateType: tableCreateInfo.value.tableCreateType,
349 saveFlag: 1,
350 layereAttribute: route.query.layereAttribute,
351 dbType: dbType.value,
352 dataState: 1,
353 immediateApprove: true,
354 partitionAttribute: !Object.keys(tableCreateInfo.value.partitionAttribute).length ? null : Object.assign({}, tableCreateInfo.value.partitionAttribute, {
355 dynamicPartitionHistory: tableCreateInfo.value.partitionAttribute ? "Y" : 'N'
356 }),
357 subjectFieldAddDTOS: tableCreateInfo.value.tableFields.map((field, i) => {
358 return Object.assign({}, field, { orderNum: i + 1 });
359 })
360 })
361 if (!tableCreateInfo.value.guid) { //添加
362 fullscreenLoading.value = true;
363 saveSubjectTable(addInfo).then((res: any) => {
364 fullscreenLoading.value = false;
365 if (res.code == proxy.$passCode) {
366 ElMessage.success('新建表提交成功!');
367 router.push({
368 name: 'metadataStandardQuery'
369 });
370 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
371 } else {
372 ElMessage.error(res.msg);
373 }
374 });
375 }
376 };
377
378 //数据库选择改变,对应的表名称是否需要变化。需要根据此属性带出表名前缀,以及是否是doris数据库。
379 const selectDatabaseChange = (val) => {
380 let d = databaseList.value.find(d => d.guid === val);
381 if (d) {
382 dbType.value = d.databaseType;
383 tableCreateInfo.value.tableData[0].dataSourceGuid = d.guid;
384 tableCreateInfo.value.tableData[0].dataServerName = d.databaseNameEn;
385 } else {
386 dbType.value = "";
387 tableCreateInfo.value.tableData[0].dataSourceGuid = '';
388 tableCreateInfo.value.tableData[0].dataServerName = '';
389 }
390 tableCreateInfo.value.tableFields.forEach((tableField: any) => {
391 if (tableField['dataType'] === "datetime") {
392 tableField.defaultValue = ""
393 }
394 })
395 };
396
397 /** 添加字段标准 */
398 const addField = () => {
399 let len = tableCreateInfo.value.tableFields.length;
400 tableCreateInfo.value.tableFields.push({
401 orderNum: len + 1,
402 isDim: "N",
403 isPrimary: "N",
404 notNull: "N",
405 isEdit: true
406 });
407 //设置选中表格当前新增行。
408 fieldStandardTableRef.value?.setCurrentRow(
409 tableCreateInfo.value.tableFields[tableCreateInfo.value.tableFields.length - 1]
410 );
411 nextTick(() => {
412 let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
413 let domScroll = bodyWrapper.parentElement.parentElement;
414 let rect = domScroll.getBoundingClientRect();
415 let maxNum = len + 1;
416 if (maxNum * 36 > rect.height + domScroll.scrollTop) {
417 fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
418 }
419 })
420 };
421
422 /** 勾选字段标准选中变化。 */
423 const selectionFieldsChange = (val) => {
424 selectTableFieldRows.value = val;
425 };
426
427 /**
428 * 上移规则:
429 * 勾选多个时先从最上面开始逐个上移一行,若已经移到最上面一行,则不处理。
430 */
431 const moveUp = () => {
432 let selectRows = fieldStandardTableRef.value?.getSelectionRows();
433 if (!selectRows.length) {
434 ElMessage.error('请先选择需要勾选的数据进行上移');
435 return;
436 }
437 let data = tableCreateInfo.value.tableFields;
438 let selectRowIndexs: number[] = [];
439 let minNum: number = 0;
440 selectRows.forEach((row, i) => {
441 let orderNum = data.findIndex(d => d === row) + 1;
442 if (orderNum == 1) {
443 selectRowIndexs.push(orderNum);
444 minNum = orderNum;
445 return;
446 }
447 let topNum = orderNum - 1;
448 if (selectRowIndexs.includes(topNum)) {
449 //下一行也是选中的,则不做转换。
450 return;
451 }
452 if (i === 0) {
453 minNum = topNum;
454 }
455 row.orderNum = topNum;
456 let changeRow = data[topNum - 1];
457 changeRow.orderNum = orderNum;
458 selectRowIndexs.push(topNum);
459 data[topNum] = changeRow;
460 data[topNum - 1] = row;
461 });
462 nextTick().then(() => {
463 let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
464 let domScroll = bodyWrapper.parentElement.parentElement;
465 if ((minNum * 36 - 38) < domScroll.scrollTop) {
466 let top = domScroll.scrollTop - 38;
467 fieldStandardTableRef.value?.setScrollTop(top < 0 ? 0 : top)
468 }
469 });
470 }
471
472 /**
473 * 下移规则:
474 * 勾选多个时先从最下面开始逐个下移一行,若已经移到最下面一行,则不处理。
475 */
476 const moveDown = () => {
477 let selectRows = fieldStandardTableRef.value?.getSelectionRows();
478 if (!selectRows.length) {
479 ElMessage.error('请先选择需要勾选的数据进行下移');
480 return;
481 }
482 let data = tableCreateInfo.value.tableFields;
483 let selectRowIndexs: number[] = [];
484 let maxNum: number = 0;
485 selectRows.slice(0).reverse().forEach((row, i) => {
486 let orderNum = data.findIndex(d => d === row) + 1;
487 if (orderNum === data.length) {
488 maxNum = orderNum;
489 selectRowIndexs.push(orderNum);
490 return;
491 }
492 if (selectRowIndexs.includes(orderNum + 1)) {
493 //下一行也是选中的,则不做转换。
494 return;
495 }
496 let bottomNum = orderNum + 1;
497 row.orderNum = bottomNum;
498 if (i === 0) {
499 maxNum = bottomNum;
500 }
501 let changeRow = data[bottomNum - 1];
502 changeRow.orderNum = orderNum;
503 selectRowIndexs.push(bottomNum);
504 data[orderNum - 1] = changeRow;
505 data[bottomNum - 1] = row;
506 });
507 nextTick(() => {
508 let bodyWrapper = fieldStandardTableRef.value?.$el.querySelector('.el-table__body');
509 let domScroll = bodyWrapper.parentElement.parentElement;
510 let rect = domScroll.getBoundingClientRect();
511 if (maxNum * 36 > rect.height + domScroll.scrollTop) {
512 fieldStandardTableRef.value?.setScrollTop(maxNum * 36 - rect.height + 2)
513 }
514 })
515 }
516
517 /** 批量删除字段标准 */
518 const delFeilds = () => {
519 if (selectTableFieldRows.value.length == 0) {
520 ElMessage({
521 type: "info",
522 message: "请选择需要删除的字段",
523 });
524 return;
525 }
526 if (tableCreateInfo.value.isCreate) {
527 if (selectTableFieldRows.value.find((row: any) => row.isPrimary === 'Y')) {
528 ElMessage.error('已建表不能删除主键字段');
529 return;
530 }
531 }
532 let hasCreateField = selectTableFieldRows.value.some((row: any) => row.isCreate === 'Y');
533 ElMessageBox.confirm(hasCreateField && hasSubjectData.value ? '已选择的字段中含有已创建的表字段且有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
534 confirmButtonText: "确定",
535 cancelButtonText: "取消",
536 type: "warning",
537 })
538 .then(() => {
539 //此删除是直接从库里删除,还是点击保存后再删除呢??如果是入库删除,后,调用接口获取数据。
540 let tableFields = tableCreateInfo.value.tableFields;
541 selectTableFieldRows.value.forEach((r: any) => {
542 let index = tableFields.findIndex((t: any) => t.orderNum === r.orderNum);
543 if (index !== -1) {
544 tableFields.splice(index, 1);
545 }
546 });
547 fieldStandardTableRef.value?.clearSelection();
548 tableCreateInfo.value.tableFields.forEach((field, i) => {
549 field.orderNum = i + 1;
550 });
551 ElMessage({
552 type: "success",
553 message: "删除成功",
554 });
555 })
556 .catch(() => {
557 ElMessage({
558 type: "info",
559 message: "已取消删除",
560 });
561 });
562 };
563
564 //点击编辑按钮
565 const handleFieldClickEdit = (scope) => {
566 scope.row['isEdit'] = true;
567 };
568
569 //点击保存按钮
570 const handleFieldClickSave = (scope) => {
571 if (!scope.row.enName) {
572 ElMessage({
573 type: "error",
574 message: "字段英文名不能为空!",
575 });
576 return;
577 }
578 if (checkDefault[scope.row.dataType]) {
579 if (!scope.row.fieldLength) { }
580 if (!checkDefault[scope.row.dataType](scope)) {
581 return
582 }
583 }
584 scope.row['isEdit'] = false;
585 };
586
587 const handleFieldDelete = (scope) => {
588 ElMessageBox.confirm(scope.row['isCreate'] === 'Y' && hasSubjectData.value ? '该字段已被创建且表中有数据,此操作将永久删除,是否继续?' : "此操作将永久删除,是否继续?", "提示", {
589 confirmButtonText: "确定",
590 cancelButtonText: "取消",
591 type: "warning",
592 })
593 .then(() => {
594 //此删除是直接从库里删除,还是点击保存后再删除呢??如果是入库删除,后,调用接口获取数据。
595 let tableFields = tableCreateInfo.value.tableFields;
596 tableFields.splice(scope.$index, 1);
597 tableCreateInfo.value.tableFields.forEach((field, i) => {
598 field.orderNum = i + 1;
599 });
600 tableCreateInfo.value.tableFields.forEach((field, i) => {
601 field.orderNum = i + 1;
602 });
603 ElMessage({
604 type: "success",
605 message: "删除成功",
606 });
607 })
608 .catch(() => {
609 ElMessage({
610 type: "info",
611 message: "已取消删除",
612 });
613 });
614 }
615
616 const dataTypeChange = (val, scope) => {
617 scope.row['defaultValue'] = ''
618 scope.row['fieldLength'] = undefined
619 scope.row['fieldPrecision'] = undefined
620
621 }
622
623 /*** 以下是处理数据字典或维表的树形选择框。 */
624
625 const dimOrDictList: any = ref([{
626 guid: '1',
627 chName: '数据字典',
628 children: dictionaryList.value,
629 isLeaf: false,
630 disabled: !dictionaryList.value.length,
631 }, {
632 guid: '2',
633 chName: '维度',
634 isLeaf: false,
635 disabled: !dimListData.value.length,
636 children: dimListData.value
637 }]);
638
639 const dimOrDictInputFilterMethod = (v, data) => {
640 return data.label?.includes(v) || data.chName?.includes(v);
641 };
642
643 const dimOrDictSelectRef = ref();
644
645 const dimOrDictSelectNode = ref();
646
647 const handleDictSelectNodeChange = (node) => {
648 dimOrDictSelectNode.value = node;
649 }
650
651 const handleDictionaryChange = (val, scope) => {
652 if (!val) {
653 scope.row.dictionaryGuid = '';
654 scope.row.dimGuid = '';
655 scope.row.dictionaryChName = '';
656 scope.row.dimChName = '';
657 return;
658 }
659 let info = dimOrDictSelectNode.value;
660 if (!info) {
661 return;
662 }
663 if (info.parentGuid == '2') {
664 scope.row.dimGuid = val;
665 scope.row.dimChName = dimListData.value.find(d => d.guid === val)?.chName;
666 scope.row.dictionaryGuid = '';
667 scope.row.dictionaryChName = '';
668 } else {
669 scope.row.dictionaryGuid = val;
670 scope.row.dictionaryChName = dictionaryList.value.find(d => d.guid === val)?.chName;
671 scope.row.dimGuid = '';
672 scope.row.dimChName = '';
673 }
674 }
675
676 /** 扩展属性弹出对话框 */
677 const expandPropertyDialogRef = ref();
678
679 /** 扩展属性弹出对话框 */
680 const handleClickExpand = () => {
681 expandPropertyDialogRef.value?.handleClickExpand();
682 }
683
684 const expandDialogValueChange = (val) => {
685 tableCreateInfo.value.partitionAttribute = val;
686 expandProperties.value = val;
687 }
688
689 </script>
690
691 <template>
692 <div class="table_tool_wrap" v-loading="fullscreenLoading">
693 <div class="tools_btns">
694 <!-- <el-button type="primary" :disabled="isLook" @click="saveDraftTable" v-preReClick>保存为草稿</el-button> -->
695 <el-button type="primary" :disabled="isLook" @click="saveTable" v-preReClick>提交</el-button>
696 </div>
697 <el-table ref="tableRef" :data="tableCreateInfo.tableData" v-loading="tableCreateInfoLoading"
698 :highlight-current-row="true" stripe border height="100%" tooltip-effect="light" row-key="guid" :style="{
699 width: '100%',
700 height: 'auto',
701 display: 'inline-block',
702 }">
703 <el-table-column prop="dataSourceGuid" label="数据源" width="200px" align="left" show-overflow-tooltip>
704 <template #header>
705 <span>数据源</span>
706 <span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
707 </template>
708 <template #default="scope">
709 <el-select v-if="!isLook" v-model="scope.row['dataSourceGuid']" placeholder="请选择"
710 :disabled="tableCreateInfo.isCreate" @change="(val) => selectDatabaseChange(val)" clearable filterable>
711 <el-option v-for="opt in databaseList" :key="opt['guid']" :label="opt['databaseNameZh']"
712 :value="opt['guid']" />
713 </el-select>
714 <span v-else>{{ scope.row["dataServerChName"] }}</span>
715 </template>
716 </el-table-column>
717 <el-table-column prop="enName" label="数据库表" width="200px" align="left" show-overflow-tooltip>
718 <template #header>
719 <span>数据库表</span>
720 <span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
721 </template>
722 <template #default="scope">
723 <template v-if="!isLook">
724 <div class="prefix-or-suffix-cell">
725 <el-input :disabled="tableCreateInfo.isCreate" v-model.trim="tableCreateInfo.inputNameValue"
726 :maxlength="50" placeholder="必填" />
727 </div>
728 </template>
729 <span v-else>{{ scope.row["enName"] }}</span>
730 </template>
731 </el-table-column>
732 <el-table-column prop="chName" label="主题表名称" width="200px" align="left" show-overflow-tooltip>
733 <template #header>
734 <span>主题表名称</span>
735 <span v-if="!isLook" style="color:red;margin-left: 2px;">*</span>
736 </template>
737 <template #default="scope">
738 <el-input v-if="!isLook" v-model.trim="scope.row['chName']" placeholder="必填" :maxlength="50" />
739 <span v-else>{{ scope.row["chName"] }}</span>
740 </template>
741 </el-table-column>
742 <el-table-column prop="subjectDomain" label="主题域" width="180px" align="left" show-overflow-tooltip>
743 <template #default="scope">
744 <el-input v-if="!isLook" disabled v-model="scope.row['subjectDomain']" />
745 <span v-else>{{ scope.row["subjectDomain"] }}</span>
746 </template>
747 </el-table-column>
748 <el-table-column prop="tableModel" label="表模型" :width="isLook ? '100px' : '150px'" align="left"
749 show-overflow-tooltip>
750 <template #default="scope">
751 <el-select v-if="dbType == 'doris' && !isLook" v-model="scope.row['tableModel']" placeholder="请选择"
752 :disabled="tableCreateInfo.isCreate">
753 <el-option v-for="opt in tableModels" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
754 </el-select>
755 <span v-else-if="dbType == 'doris'">{{tableModels.find(t => t.value === scope.row["tableModel"])?.label ||
756 '-'
757 }}</span>
758 <span v-else>--</span>
759 </template>
760 </el-table-column>
761 <el-table-column prop="tableCategory" label="表分类" :width="isLook ? '100px' : '150px'" align="left"
762 show-overflow-tooltip>
763 <template #default="scope">
764 <el-select v-if="!isLook" v-model="scope.row['tableCategory']" placeholder="请选择">
765 <el-option v-for="opt in tableCategoryList" :key="opt['value']" :label="opt['label']"
766 :value="opt['value']" />
767 </el-select>
768 <span v-else>{{tableCategoryList.find(t => t.value === scope.row["tableCategory"])?.label || '-'}}</span>
769 </template>
770 </el-table-column>
771 <el-table-column prop="syncPolicy" label="同步策略" :width="isLook ? '100px' : '150px'" align="left"
772 show-overflow-tooltip>
773 <template #default="scope">
774 <el-select v-if="!isLook" v-model="scope.row['syncPolicy']" placeholder="请选择">
775 <el-option v-for="opt in syncPolicys" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
776 </el-select>
777 <span v-else>{{syncPolicys.find(s => s.value === scope.row["syncPolicy"])?.label || '-'}}</span>
778 </template>
779 </el-table-column>
780 <el-table-column prop="characterSet" label="字符集" :width="isLook ? '100px' : '150px'" align="left"
781 show-overflow-tooltip>
782 <template #default="scope">
783 <el-select v-if="!isLook" v-model="scope.row['characterSet']" placeholder="请选择"
784 :disabled="tableCreateInfo.isCreate">
785 <el-option v-for="opt in characterList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
786 </el-select>
787 <span v-else>{{characterList.find(c => c.value === scope.row["characterSet"])?.label || '-'}}</span>
788 </template>
789 </el-table-column>
790 <el-table-column prop="description" label="描述" width="220px" align="left" show-overflow-tooltip>
791 <template #default="scope">
792 <el-input v-if="!isLook" v-model.trim="scope.row['description']" />
793 <span v-else>{{ scope.row["description"] || '-' }}</span>
794 </template>
795 </el-table-column>
796 <el-table-column label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
797 <template #default="scope">
798 <span class="text_btn" @click="handleClickExpand()" v-preReClick>扩展属性</span>
799 </template>
800 </el-table-column>
801 </el-table>
802 <div class="tools_btns">
803 <el-button type="primary" :disabled="isLook" @click="addField">新增</el-button>
804 <el-button @click="moveUp" :disabled="isLook">上移</el-button>
805 <el-button @click="moveDown" :disabled="isLook">下移</el-button>
806 <el-button @click="delFeilds" :disabled="isLook">批量删除</el-button>
807 </div>
808 <div class="table_panel">
809 <el-table ref="fieldStandardTableRef" :data="tableCreateInfo.tableFields" v-loading="tableFieldsLoading"
810 :highlight-current-row="true" stripe border height="100%" row-key="guid"
811 @selection-change="selectionFieldsChange" tooltip-effect="light" :style="{
812 width: '100%',
813 'max-height': 'calc(100% - 16px)',
814 display: 'inline-block',
815 }">
816 <el-table-column type="selection" v-if="!isLook" :width="32" align="center" />
817 <el-table-column label="排序" type="index" width="56px" align="center" show-overflow-tooltip>
818 </el-table-column>
819 <el-table-column prop="chName" label="字段中文名称" width="150px" align="left" show-overflow-tooltip>
820 <template #default="scope">
821 <el-input v-if="scope.row['isEdit']" :placeholder="'请输入'" v-model.trim="scope.row['chName']" />
822 <span v-else>{{ scope.row["chName"] || '-' }}</span>
823 </template>
824 </el-table-column>
825 <el-table-column prop="enName" label="字段英文名" width="150px" align="left" show-overflow-tooltip>
826 <template #default="scope">
827 <el-input v-if="scope.row['isEdit'] && !(scope.row['guid'] && tableCreateInfo.isCreate)"
828 v-model.trim="scope.row['enName']" placeholder="必填"
829 @input="inputLengthKeyUp(/[^a-zA-Z0-9_]/g, scope, 'enName')" />
830 <span v-else>{{ scope.row["enName"] || '-' }}</span>
831 </template>
832 </el-table-column>
833 <el-table-column prop="dataType" label="字段类型" width="120px" align="left" show-overflow-tooltip>
834 <template #default="scope">
835 <el-select v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y')"
836 v-model="scope.row['dataType']" placeholder="请选择" @change="(val) => dataTypeChange(val, scope)">
837 <el-option v-for="opt in fieldTypes" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
838 </el-select>
839 <span v-else>{{fieldTypes.find(f => f.value === scope.row["dataType"])?.label || '-'}}</span>
840 </template>
841 </el-table-column>
842 <el-table-column prop="fieldLength" label="长度" width="115px" align="left" show-overflow-tooltip>
843 <template #default="scope">
844 <el-input
845 v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && (scope.row['dataType'] == 'varchar' || scope.row['dataType'] == 'decimal' || scope.row['dataType'] == 'char')"
846 v-model.trim="scope.row['fieldLength']" placeholder="必填"
847 @input="inputLengthKeyUp(/\D/g, scope, 'fieldLength', 2000, 1)" />
848 <span v-else>{{ scope.row["fieldLength"] == null ? '-' : scope.row["fieldLength"] }}</span>
849 </template>
850 </el-table-column>
851 <el-table-column prop="fieldPrecision" label="精度" width="115px" align="left" show-overflow-tooltip>
852 <template #default="scope">
853 <el-input
854 v-if="scope.row['isEdit'] && !(tableCreateInfo.isCreate && scope.row['isCreate'] === 'Y') && scope.row['dataType'] == 'decimal'"
855 v-model.trim="scope.row['fieldPrecision']" placeholder="必填"
856 @input="inputLengthKeyUp(/\D/g, scope, 'fieldPrecision', 30, 1)" />
857 <span v-else>{{ scope.row["fieldPrecision"] == null ? '-' : scope.row["fieldPrecision"] }}</span>
858 </template>
859 </el-table-column>
860 <el-table-column prop="dimOrdictionaryGuid" label="关联维度/字典" width="130px" align="left" show-overflow-tooltip>
861 <template #default="scope">
862 <el-tree-select ref="dimOrDictSelectRef" v-if="scope.row['isEdit']" filterable clearable
863 :data="dimOrDictList" v-model="scope.row['dimOrdictionaryGuid']" node-key="guid"
864 :default-expanded-keys="scope.row['dictionaryGuid'] ? ['1'] : (scope.row['dimGuid'] ? ['2'] : [])"
865 placeholder="请选择" :filter-node-method="dimOrDictInputFilterMethod" :props="{
866 label: 'chName',
867 value: 'guid',
868 children: 'children',
869 isLeaf: 'isLeaf'
870 }" @change="(v) => handleDictionaryChange(v, scope)" @current-change="handleDictSelectNodeChange">
871 <template #default="{ node, data }">
872 <template v-if="node.level > 1 && data.parentGuid == '2'">
873 <span>{{ data["chName"] + `(${data["subjectDomainName"]})` }}</span>
874 </template>
875 <span v-else>{{ data['chName'] }}</span>
876 </template>
877 </el-tree-select>
878 <span v-else>{{ (scope.row['dictionaryGuid'] ? scope.row["dictionaryChName"] : (scope.row['dimGuid'] ?
879 scope.row['dimChName'] : '-')) || '-' }}</span>
880 </template>
881 </el-table-column>
882 <el-table-column prop="isPrimary" label="是否主键" width="90px" align="left" show-overflow-tooltip>
883 <template #default="scope">
884 <el-select v-if="scope.row['isEdit'] && !tableCreateInfo.isCreate" v-model="scope.row['isPrimary']"
885 placeholder="请选择">
886 <el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
887 </el-select>
888 <span v-else>{{ scope.row["isPrimary"] || '-' }}</span>
889 </template>
890 </el-table-column>
891 <el-table-column v-if="tableCreateInfo.tableData[0].tableModel == 2" prop="aggWay" label="聚合方式" width="120px"
892 align="left" show-overflow-tooltip>
893 <template #default="scope">
894 <el-select v-if="scope.row['isEdit']" v-model="scope.row['aggWay']" placeholder="请选择">
895 <el-option v-for="opt in aggMethodList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
896 </el-select>
897 <span v-else>{{ scope.row["aggWay"] || '-' }}</span>
898 </template>
899 </el-table-column>
900 <el-table-column prop="notNull" label="是否必填" width="90px" align="left" show-overflow-tooltip>
901 <template #default="scope">
902 <el-select v-if="scope.row['isEdit'] && !(scope.row.isCreate == 'Y' && scope.row['notNull'] == 'N')"
903 v-model="scope.row['notNull']" placeholder="请选择">
904 <el-option v-for="opt in isNotList" :key="opt['value']" :label="opt['label']" :value="opt['value']" />
905 </el-select>
906 <span v-else>{{ scope.row["notNull"] || '-' }}</span>
907 </template>
908 </el-table-column>
909 <el-table-column prop="defaultValue" label="默认值" width="205px" align="left" show-overflow-tooltip>
910 <template #default="scope">
911 <tableDefaultValue :scope="scope" :dbType="dbType"
912 :readonly="dbType == 'doris' && scope.row.isCreate == 'Y'"></tableDefaultValue>
913 </template>
914 </el-table-column>
915 <el-table-column v-if="!isLook" label="操作" width="100px" align="left" fixed="right" show-overflow-tooltip>
916 <template #default="scope">
917 <template v-if="!(tableCreateInfo.isCreate && scope.row['isPrimary'] === 'Y')">
918 <span class="text_btn" v-if="!scope.row['isEdit']" @click="handleFieldClickEdit(scope)"
919 v-preReClick>编辑</span>
920 <span class="text_btn" v-else @click="handleFieldClickSave(scope)" v-preReClick>保存</span>
921 <el-divider direction="vertical" />
922 <span class="text_btn" @click="handleFieldDelete(scope)">删除</span>
923 </template>
924 <span v-else>--</span>
925 </template>
926 </el-table-column>
927 </el-table>
928 </div>
929 <ContentWrap title="流程审批" description="" :isExpand="flowExpand" :expand-swicth="true" class="mb16"
930 @expand="(v) => flowExpand = v">
931 <ApprovalProcess v-if="deploymentId" :deploymentId="deploymentId" :definitionId="''">
932 </ApprovalProcess>
933 </ContentWrap>
934 <expandPropertyDialog ref="expandPropertyDialogRef" :is-look="isLook" :partitionAttribute="expandProperties"
935 :table-create-info="tableCreateInfo" @expandValueChange="expandDialogValueChange" />
936 </div>
937 </template>
938
939 <style lang="scss" scoped>
940 .table_tool_wrap {
941 width: 100%;
942 height: 100%;
943 padding: 0 16px;
944 overflow: hidden auto;
945
946 .tools_btns {
947 padding: 8px 0;
948 }
949
950 :deep(.el-table) {
951 .cell {
952
953 .prefix-or-suffix-cell {
954 display: inline-flex;
955 align-items: center;
956 }
957 }
958 }
959
960 .table_panel {
961 height: 268px;
962
963 :deep(.el-table) {
964 & td.el-table__cell {
965 padding: 2px 0;
966 height: 36px;
967 }
968 }
969 }
970 }
971
972 :deep(.el-dialog) {
973 .dialog-form-inline {
974 .checkbox_input {
975 display: flex;
976 flex-direction: column;
977
978 .input_panel {
979 margin: 0;
980 }
981 }
982
983 .select_group {
984 .el-form-item__content>.el-input {
985 margin-top: 21px;
986 }
987 }
988
989 .radio_panel {
990 .panel_content {
991 display: none;
992 }
993 }
994 }
995 }
996 </style>
1 <route lang="yaml">
2 name: calculateConfig
3 </route>
4 <script lang="ts" setup name="calculateConfig">
5 import { ref, onMounted } from "vue";
6 import { useRouter, useRoute } from "vue-router";
7 import { ElMessage, ElMessageBox } from "element-plus";
8 import useUserStore from "@/store/modules/user";
9 import useDataAssetStore from "@/store/modules/dataAsset";
10 import { getAllFlowData } from '@/api/modules/queryService';
11 import { getDamCatalogList } from "@/api/modules/dataPricing";
12 import { getRegisterCatalogDetail, getRegisterCatalogTableDetail } from "@/api/modules/dataAsset";
13 import { download } from '@/utils/common'
14 import {
15 getConfigureList,
16 getConfigureDetail,
17 getDiseaseAll,
18 getPriceDetail,
19 getDemandList,
20 getModelScore,
21 savePrice,
22 getModelDemand,
23 getPriceResult,
24 exportModelScore
25 } from '@/api/modules/dataPricing';
26 import { changeNum } from "@/utils/common";
27
28 const { proxy } = getCurrentInstance() as any;
29 const router = useRouter();
30 const route = useRoute();
31 const userStore = useUserStore();
32 const assetStore = useDataAssetStore();
33 const fullPath = route.fullPath;
34 const userData = JSON.parse(localStorage.userData);
35 const guid = route.query.guid;
36 const priceName = route.query.name;
37 const loading = ref(false);
38 const flowDetail: any = ref({});
39 const typeMap: any = ref({});
40 const expand1 = ref(true)
41 const expand2 = ref(true)
42 const expand3 = ref(true)
43 const demandTableList: any = ref([]);
44 const pricingTargetList: any = ref([]);
45 const demandTableFieldAllNum = ref(0);
46 const resourceTableAllNum = ref(0);
47 const resourceTableFieldAllNum = ref(0);
48 const modelData: any = ref({});
49 const pricingDimensionalityData: any = ref([]);
50 const dictionaryData: any = ref([]);
51 const diseaseData: any = ref([]);
52 const qualityScoreData: any = ref({});
53 const disScore: any = ref([]);
54 const exportData: any = ref([]);
55 const dataUsage = ref('');
56 // 基础设置
57 const baseConfigFormRef = ref();
58 const baseConfigFormItems: any = ref([
59 {
60 label: '模型名称',
61 type: 'select',
62 placeholder: '请选择',
63 field: 'modelGuid',
64 default: '',
65 options: [],
66 props: {
67 label: "modelName",
68 value: "guid",
69 },
70 clearable: true,
71 filterable: true,
72 required: true
73 },
74 {
75 label: '数据资源',
76 type: 'select',
77 placeholder: '请选择',
78 field: 'dataResourceGuid',
79 default: '',
80 options: [],
81 props: {
82 label: "damName",
83 value: "guid",
84 },
85 clearable: true,
86 filterable: true,
87 required: true,
88 },
89 {
90 label: '所属主体',
91 type: 'input',
92 placeholder: '',
93 field: 'belongingEntityGuid',
94 default: '',
95 options: [],
96 clearable: true,
97 disabled: true
98 },
99 {
100 label: '所属主题',
101 type: 'tree-select',
102 placeholder: '请选择',
103 field: 'belongingTheme',
104 default: '',
105 options: [],
106 showAllLevels: false,
107 checkStrictly: false,//只能选择叶子节点。
108 lazy: false,
109 props: {
110 label: "label",
111 value: "value",
112 children: 'childDictList'
113 },
114 filterable: true,
115 clearable: true,
116 disabled: true
117 },
118 ])
119 const baseConfigFormRules: any = ref({
120 modelGuid: [
121 { required: true, trigger: 'change', message: "请选择模型名称" }
122 ],
123 dataResourceGuid: [
124 { required: true, trigger: 'change', message: "请选择数据资源" }
125 ],
126 });
127 const baseConfigForm = ref({
128 items: baseConfigFormItems.value,
129 rules: baseConfigFormRules.value,
130 })
131 const tableFields: any = ref([
132 { label: '需求表', field: 'demandTableName', type: 'input', width: 200, disabled: true },
133 { label: '数据资源表', field: 'dataTableGuid', type: 'select', width: 200 },
134 { label: '表描述', field: 'tableDescription', type: 'input', width: 200, disabled: true },
135 { label: '需求表权重(%)', field: 'weightDemandTable', type: 'input', width: 140, disabled: true },
136 ])
137 const expendTableRef = ref();
138 const tableData: any = ref([]);
139 const tableLoading = ref(false);
140 const dataTransactionPrice: any = ref('');
141 const setFormItems = (info = null) => {
142 let datas: any = info || flowDetail.value || {};
143 const dData = datas.dictionaryJson ? JSON.parse(datas.dictionaryJson) : {};
144 datas = { ...datas, ...dData };
145 baseConfigFormItems.value.map(item => {
146 item.default = datas[item.field] || '';
147 })
148 nextTick(() => {
149 baseConfigFormRef.value.ruleFormRef?.clearValidate();
150 })
151 }
152 /**
153 * 传入多个promise对象,当全部结束时取消Loading
154 * @param promises 传入多个promise对象,当全部结束时取消Loading
155 */
156 const promiseList = (...promises: Promise<void>[]) => {
157 // loading方法全局封装成一个组件
158 !guid && (loading.value = true);
159 try {
160 Promise.all(promises).then(res => {
161 loading.value = false;
162 });
163 } catch (e) {
164 loading.value = false;
165 } finally {
166 !guid && (loading.value = false);
167 }
168 };
169 // 获取模型
170 const getModel = () => {
171 getConfigureList({ pageSize: -1, pageIndex: 1, bizState: 'Y' }).then((res: any) => {
172 if (res.code == proxy.$passCode) {
173 const data = res.data.records || [];
174 typeMap.value.modelGuid = JSON.parse(JSON.stringify(data));
175 let item = baseConfigFormItems.value.find(item => item.field == 'modelGuid');
176 item && (item.options = data);
177 }
178 })
179 }
180 // 获取所有疾病数据
181 const getDiseaseData = () => {
182 getDiseaseAll().then((res: any) => {
183 if (res.code == proxy.$passCode) {
184 const data = res.data || [];
185 typeMap.value.diseaseGuid = JSON.parse(JSON.stringify(data));
186 let item = baseConfigFormItems.value.find(item => item.field == 'diseaseGuid');
187 if (item) {
188 item.options = typeMap.value['diseaseGuid'];
189 if (guid) {
190 const diseaseData = typeMap.value.diseaseGuid.find(m => m.guid == flowDetail.value.diseaseGuid);
191 if (!diseaseData) {
192 item.options.unshift({
193 guid: flowDetail.value.diseaseGuid,
194 diseaseName: flowDetail.value.diseaseName
195 });
196 }
197 }
198 }
199 }
200 })
201 }
202 // 获取数据资源
203 const getDataCatalog = () => {
204 return getDamCatalogList({ dataType: userData.superTubeFlag == 'Y' ? "P" : "D", sceneType: "D" }).then((res: any) => {
205 if (res.code == proxy.$passCode) {
206 let data = res.data || [];
207 data.map(item => item.damGuid = item.guid);
208 typeMap.value.dataResourceGuid = JSON.parse(JSON.stringify(data));
209 let item = baseConfigFormItems.value.find(item => item.field == 'dataResourceGuid');
210 if (item) {
211 item.options = data;
212 if (guid) {
213 const rItem = typeMap.value.dataResourceGuid.find(m => m.damGuid == flowDetail.value.dataResourceGuid);
214 if (!rItem) {
215 const rtem = { damGuid: flowDetail.value.dataResourceGuid, damName: flowDetail.value.dataResourceName };
216 item.options.unshift(rtem);
217 typeMap.value.dataResourceGuid.unshift(rtem);
218 }
219 }
220 }
221 }
222 })
223 }
224 // 获取数据资源主题
225 const getSourceThem = (dictType, fieldName) => {
226 return getAllFlowData({ dictType }).then((res: any) => {
227 if (res.code == proxy.$passCode) {
228 const data = res.data || [];
229 typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
230 let item = baseConfigFormItems.value.find(item => item.field == fieldName);
231 item && (item.options = data);
232 } else {
233 proxy.$ElMessage.error(res.msg);
234 }
235 })
236 }
237 // 获取数据字典
238 const getDataType = (dictType, fieldName) => {
239 getAllFlowData({ dictType }).then((res: any) => {
240 if (res.code == proxy.$passCode) {
241 const data = res.data || [];
242 typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
243 let item = baseConfigFormItems.value.find(item => item.field == fieldName);
244 item && (item.options = data);
245 } else {
246 proxy.$ElMessage.error(res.msg);
247 }
248 })
249 }
250 // 获取详情
251 const getDetail = () => {
252 loading.value = true;
253 getPriceDetail({ guid }).then((res: any) => {
254 if (res.code == proxy.$passCode) {
255 const data = res.data || {};
256 flowDetail.value = data;
257 dataTransactionPrice.value = flowDetail.value.dataTransactionPrice;
258 dataUsage.value = data.dataUsage || '';
259 const mItem = typeMap.value.modelGuid.find(m => m.guid == flowDetail.value.modelGuid);
260 if (!mItem) {
261 const mtem = { guid: flowDetail.value.modelGuid, modelName: flowDetail.value.modelName };
262 typeMap.value.modelGuid.unshift(mtem);
263 baseConfigFormItems.value[0].options.unshift(mtem);
264 };
265 getModelInfo(flowDetail.value.modelGuid);
266 getDataTypeList()
267 }
268 }).catch(() => {
269 loading.value = false;
270 })
271 }
272 const getDataTypeList = () => {
273 if (guid) {
274 promiseList(
275 getDataCatalog(),
276 getSourceThem('数据资产目录主题名称', 'belongingTheme'),
277 getQuilityModelScore(flowDetail.value.dataResourceGuid)
278 )
279 } else {
280 promiseList(
281 getDataCatalog(),
282 getSourceThem('数据资产目录主题名称', 'belongingTheme'),
283 )
284 }
285 }
286 const setFormItemData = () => {
287 let dictionaryList: any = [], diseaseList: any = [];
288 pricingTargetList.value.map(item => {
289 switch (item.targetType) {
290 case '2':
291 item.functionName == '2' && diseaseList.push(item);
292 break;
293 case '3':
294 dictionaryList.push(item);
295 break;
296 default:
297 break;
298 }
299 })
300 dictionaryData.value = dictionaryList;
301 diseaseData.value = diseaseList;
302 if (diseaseList.length) {
303 const diseaseName = flowDetail.value.diseaseName || '';
304 const modelGuid = flowDetail.value.modelGuid || '';
305 // 获取疾病得分
306 if (diseaseName && modelGuid) {
307 getTargetNum({ diseaseName, guid: modelGuid });
308 }
309 }
310 baseConfigFormItems.value.splice(4);
311 for (var r in baseConfigFormRules.value) {
312 if (r != 'modelGuid' && r != 'dataResourceGuid') {
313 delete baseConfigFormRules.value[r];
314 }
315 }
316 // 添加所属疾病
317 if (diseaseList.length > 0) {
318 baseConfigFormItems.value.push({
319 label: '所属疾病',
320 type: 'cascader',
321 placeholder: '请选择',
322 field: 'diseaseGuid',
323 default: '',
324 options: [],
325 showAllLevels: false,
326 props: {
327 checkStrictly: true,
328 label: "diseaseName",
329 value: "guid",
330 children: 'childList',
331 emitPath: false
332 },
333 filterable: true,
334 clearable: true,
335 required: true,
336 });
337 baseConfigFormRules.value.diseaseGuid = { required: true, trigger: 'change', message: "请选择所属疾病" };
338 if (typeMap.value['diseaseGuid'] == undefined) {
339 getDiseaseData();
340 } else {
341 let item = baseConfigFormItems.value.find(item => item.field == 'diseaseGuid');
342 if (item) {
343 item.options = typeMap.value['diseaseGuid'];
344 const diseaseData = typeMap.value.diseaseGuid.find(m => m.guid == flowDetail.value.diseaseGuid);
345 if (!diseaseData) {
346 item.options.unshift({
347 guid: flowDetail.value.diseaseGuid,
348 diseaseName: flowDetail.value.diseaseName
349 });
350 }
351 }
352 }
353 }
354 // 添加数据字典
355 dictionaryList.map(d => {
356 const dictName = d.dictionaryName;
357 const dictField = `dict_${d.guid}`;
358 baseConfigFormItems.value.push({
359 label: dictName,
360 type: 'select',
361 placeholder: '请输入',
362 field: dictField,
363 default: '',
364 options: [],
365 clearable: true,
366 filterable: true,
367 required: true,
368 });
369 baseConfigFormRules.value[dictField] = { required: true, trigger: 'change', message: `请选择${dictName}` };
370 (() => {
371 if (typeMap.value[dictField] == undefined) {
372 getDataType(dictName, dictField)
373 } else {
374 let item = baseConfigFormItems.value.find(item => item.field == dictField);
375 item && (item.options = typeMap.value[dictField]);
376 }
377 })()
378 })
379 setTimeout(() => {
380 baseConfigFormRef.value.ruleFormRef?.clearValidate();
381 }, 100)
382 }
383
384 const setdemandTableData = (mGuid = '') => {
385 const tList = flowDetail.value.dataPricingDemandmatchingRQVOS || demandTableList.value || [];
386 let tDatas: any = [];
387 if (guid) {
388 if (mGuid) {
389 tDatas = mGuid == flowDetail.value.modelGuid ? tList : demandTableList.value || [];
390 } else {
391 tDatas = tList;
392 }
393 } else {
394 tDatas = tList;
395 }
396 setTableData(JSON.parse(JSON.stringify(tDatas)))
397 }
398
399 const setTableData = (dataArr) => {
400 tableData.value.splice(0);
401 dataArr.map((item, i) => {
402 const demInfo = pricingTargetList.value.find(t => t.demandTableGuid == (item.demandTableGuid || item.guid));
403 const demWeight = demInfo?.weight || '';
404 tableData.value.push({
405 ...item,
406 demandTableName: item.demandTableName || item.menuName,
407 dataTableGuid: item.dataTableGuid || '',
408 tableDescription: item.tableDescription || '',
409 weightDemandTable: item.weightDemandTable ? parseFloat(item.weightDemandTable).toFixed(2) : (demWeight ? parseFloat(demWeight).toFixed(2) : ''),
410 dataFields: item.pricingDemandFieldRQVOS || [],
411 dataFieldsNum: item.dataFieldsNum || 0,
412 })
413 if ((item.demandTableGuid || item.guid)) {
414 const rGuid = item.demandTableGuid || item.guid;
415 const rIndex = i;
416 if (!guid || (guid && rGuid != (demInfo?.demandTableGuid || ''))) {
417 (() => {
418 getDemandField(rGuid, rIndex);
419 })()
420 }
421 }
422 })
423 resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
424 return accumulator + Number(currentValue.dataFieldsNum);
425 }, 0);
426 setTimeout(() => {
427 tableData.value.map(t => {
428 expendTableRef.value.toggleRowExpansion(t);
429 })
430 }, 200)
431 }
432 // 获取模型配置信息
433 const getModelConfig = (mGuid) => {
434 return getModelDemand({ guid: mGuid }).then((res: any) => {
435 if (res.code == proxy.$passCode) {
436 const data = res.data || [];
437 demandTableList.value = data.pricingDemandMenuRSVOS || [];
438 pricingTargetList.value = data.pricingTargetRSVOS || [];
439 demandTableFieldAllNum.value = data.fieldCount || 0;
440 }
441 })
442 }
443 // 获取模型详情
444 const getModelDetail = (mGuid) => {
445 return getConfigureDetail({ guid: mGuid }).then((res: any) => {
446 if (res.code == proxy.$passCode) {
447 const data = res.data || [];
448 modelData.value = data;
449 const pricingDimensionality = data.pricingDimensionalityRSVOS || [];
450 let tData: any = [];
451 pricingDimensionality.map(p => {
452 p.pricingTargetRSVOS.map(t => {
453 tData.push({ ...p, ...t })
454 })
455 })
456 pricingDimensionalityData.value = tData;
457 }
458 })
459 }
460 // 获取资源详情
461 const getResourceDetail = (sGuid, toPromise = true) => {
462 const detailData = getRegisterCatalogDetail({ guid: sGuid }).then((res: any) => {
463 if (res.code == proxy.$passCode) {
464 const data = res.data || {};
465 baseConfigFormItems.value.map(item => {
466 if (item.field == 'belongingEntityGuid') {
467 item.default = data.rightMainName || '';
468 } else if (item.field == 'belongingTheme') {
469 item.default = data.subjectDomain || '';
470 }
471 })
472 const damCatalogTableInfo = data.damCatalogTableInfo || [];
473 const damOptions = damCatalogTableInfo.map(item => {
474 return {
475 ...item,
476 label: item.tableName,
477 value: item.guid
478 }
479 })
480 tableData.value.map((item, i) => {
481 item.damDataTable = JSON.parse(JSON.stringify(damOptions));
482 if (guid && sGuid == flowDetail.value.dataResourceGuid) {
483 const sData = flowDetail.value.dataPricingDemandmatchingRQVOS?.find(s => s.demandTableGuid == item.demandTableGuid);
484 if (sData) {
485 item.dataTableGuid = sData.dataTableGuid;
486 item.dataFields.map(f => {
487 const fData = sData.pricingDemandFieldRQVOS.find(t => t.guid == f.guid);
488 f.enName = fData?.enName || '';
489 f.chName = fData?.chName || '';
490 });
491 item.tableDescription = sData.tableDescription || damOptions.find(t => t.guid == sData.dataTableGuid)?.tableDescription || '';
492 item.dataFieldsNum = item.dataFields.filter(item => item.chName != '' && item.chName != null).length;
493 resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
494 return accumulator + Number(currentValue.dataFieldsNum);
495 }, 0);
496 }
497 } else {
498 item.dataTableGuid = '';
499 item.dataFields.map(f => { f.enName = ''; f.chName = '' });
500 item.dataFieldsNum = 0;
501 item.tableDescription = '';
502 resourceTableFieldAllNum.value = 0;
503 }
504 const dGuid = item.dataTableGuid;
505 const rIndex = i;
506 (() => {
507 !toPromise && dGuid && setTableRowData(dGuid, rIndex)
508 })()
509 })
510 resourceTableAllNum.value = tableData.value.filter(item => item.dataTableGuid != '' && item.dataTableGuid != null).length;
511 }
512 });
513 if (toPromise) {
514 return detailData;
515 } else {
516 (() => detailData)()
517 }
518 }
519 // 获取质量模型评分
520 const getQuilityModelScore = (sGuid) => {
521 return getModelScore({ damGuid: sGuid }).then((res: any) => {
522 if (res.code === proxy.$passCode) {
523 const data = res.data || {};
524 qualityScoreData.value = data;
525 } else {
526 proxy.$ElMessage.error(res.msg);
527 }
528 })
529 }
530 // 獲取模型相关信息
531 const getModelInfo = (mGuid) => {
532 const promises: any = [
533 getModelConfig(mGuid),
534 getModelDetail(mGuid)
535 ];
536 try {
537 loading.value = true;
538 Promise.all(promises).then(res => {
539 loading.value = false;
540 setFormItemData();
541 if (guid && mGuid == flowDetail.value.modelGuid) {
542 dataTransactionPrice.value = flowDetail.value.dataTransactionPrice;
543 setTimeout(() => {
544 getResourceDetail(flowDetail.value.dataResourceGuid, false);
545 setFormItems();
546 setdemandTableData(mGuid);
547 }, 200);
548 } else {
549 setdemandTableData(mGuid);
550 }
551 });
552 } catch (e) {
553 loading.value = false;
554 }
555 }
556 // 获取数据资源管理信息
557 const getResourceInfo = (sGuid) => {
558 const promises: any = [getResourceDetail(sGuid), getQuilityModelScore(sGuid)];
559 try {
560 loading.value = true;
561 Promise.all(promises).then(res => {
562 loading.value = false;
563 });
564 } catch (e) {
565 loading.value = false;
566 }
567 }
568 const setTableRowData = (dGuid, rIndex) => {
569 let rowData = tableData.value[rIndex];
570 if (guid && dGuid == rowData.dataTableGuid) {
571 const sourceTableField = flowDetail.value.dataPricingDemandmatchingRQVOS?.find(s => dGuid == s.dataTableGuid);
572 const pricingDemandField = sourceTableField?.pricingDemandFieldRQVOS || [];
573 rowData.dataFields.map(f => {
574 f.chName = pricingDemandField.find(s => f.guid == s.guid)?.chName || ''
575 })
576 } else {
577 rowData.dataFields.map(f => f.chName = '')
578 }
579 const damData = rowData.damDataTable.find(item => item.guid == dGuid);
580 rowData.tableDescription = damData?.tableDescription || '';
581 rowData.dataFieldsNum = rowData.dataFields.filter(item => item.chName != '' && item.chName != null).length;
582 resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
583 return accumulator + Number(currentValue.dataFieldsNum);
584 }, 0);
585 resourceTableAllNum.value = tableData.value.filter(item => item.dataTableGuid != '' && item.dataTableGuid != null).length;
586 if (dGuid) {
587 tableLoading.value = true;
588 getRegisterCatalogTableDetail(dGuid).then((res: any) => {
589 tableLoading.value = false;
590 if (res.code == proxy.$passCode) {
591 const data = res.data || {};
592 const damTableField = data.damCatalogTableField || [];
593 const damFieldOptions = damTableField.map(d => {
594 return {
595 ...d,
596 label: d.chName || '',
597 value: d.chName || ''
598 }
599 })
600 rowData.dataFields.map(t => {
601 t.damFieldTable = JSON.parse(JSON.stringify(damFieldOptions));
602 })
603 // console.log('rowData', rowData)
604 } else {
605 proxy.$ElMessage.error(res.msg);
606 }
607 }).catch(() => {
608 tableLoading.value = false;
609 })
610 }
611 }
612
613 const changeDatasource = () => {
614 baseConfigFormItems.value.map(item => {
615 if (item.field == 'belongingEntityGuid') {
616 item.default = '';
617 } else if (item.field == 'belongingTheme') {
618 item.default = '';
619 }
620 })
621 }
622 const cascaderChange = (val) => {
623 disScore.value = [];
624 if (val) {
625 const baseConfigFormObj = baseConfigFormRef.value;
626 const baseConfigFormInfo = baseConfigFormObj.formInline;
627 const parentsData = baseConfigFormObj.getCascaderCheckedData();
628 const diseaseName = parentsData[0]?.label || '';
629 const modelGuid = baseConfigFormInfo.modelGuid;
630 // 获取疾病得分
631 getTargetNum({ diseaseName, guid: modelGuid });
632 }
633 }
634 const selectChange = async (val, row, info) => {
635 dataTransactionPrice.value = '';
636 if (row.field == 'modelGuid') {
637 tableData.value = [];
638 demandTableFieldAllNum.value = 0;
639 resourceTableAllNum.value = 0;
640 resourceTableFieldAllNum.value = 0;
641 await setFormItems(info);
642 val && getModelInfo(val);
643 qualityScoreData.value = {};
644 baseConfigFormItems.value[1].default = '';
645 changeDatasource();
646 } else if (row.field == 'dataResourceGuid') {
647 await setFormItems(info);
648 qualityScoreData.value = {};
649 resourceTableAllNum.value = 0;
650 resourceTableFieldAllNum.value = 0;
651 if (val) {
652 getResourceInfo(val);
653 } else {
654 changeDatasource();
655 }
656 } else if (row.field == 'dataTableGuid') {
657 setTableRowData(val, info.$index)
658 } else if (row.field == 'chName') {
659 let tData = info.row;
660 if (val) {
661 const damData = tData.dataFields[row.index].damFieldTable.find(item => item.chName == val);
662 tData.dataFields[row.index].enName = damData?.enName || '';
663 } else {
664 tData.dataFields[row.index].enName = '';
665 }
666 tData.dataFieldsNum = tData.dataFields.filter(item => item.chName != '' && item.chName != null).length;
667 resourceTableFieldAllNum.value = tableData.value.reduce((accumulator, currentValue) => {
668 return accumulator + Number(currentValue.dataFieldsNum);
669 }, 0);
670 } else {
671 setFormItems(info);
672 }
673 }
674 // 获取需求表字段
675 const getDemandField = (rGuid, rIndex) => {
676 getDemandList({
677 pageSize: -1,
678 pageIndex: 1,
679 relationMenuGuid: rGuid,
680 bizState: 'Y'
681 }).then((res: any) => {
682 tableLoading.value = false;
683 if (res.code == proxy.$passCode) {
684 const data = res.data || {};
685 const fData = data.records || [];
686 const tFields = tableData.value[rIndex].dataFields;
687 const tData = fData.map(item => {
688 const iData = tFields.find(t => t.demandFieldGuid == item.guid) || {};
689 return {
690 ...item,
691 fieldName: item.fieldName,
692 isRequired: item.isRequired,
693 chName: item.chName || '',
694 enName: item.enName || '',
695 ...iData
696 }
697 });
698 tableData.value[rIndex].dataFields = tData;
699
700 } else {
701 proxy.$ElMessage.error(res.msg);
702 }
703 }).catch(() => {
704 tableLoading.value = false;
705 })
706 }
707 const toPath = () => {
708 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
709 assetStore.set(true);
710 router.push({
711 name: 'priceCalculate',
712 })
713 }
714 // 获取维度公式计算结果
715 const getSignatory = (row) => {
716 let formulaVal = 0;
717 const pricingTargetData = row.pricingTargetRSVOS || [];
718 if (!row.computationalFormula || row.computationalFormula == 'custom') {
719 let formula = row.customize;
720 // 遍历数组,检查 customize 是否包含对应的 targetName,若包含则替换为 tNum
721 pricingTargetData.forEach((item) => {
722 if (formula.includes(item.targetName)) {
723 formula = formula.replace(new RegExp(item.targetName, 'g'), item.tNum);
724 }
725 });
726 // 使用 eval 计算公式结果(注意:eval 存在安全风险,仅适用于受控环境)
727 try {
728 formulaVal = eval(formula);
729 } catch (error) {
730 console.error('公式计算错误:', error);
731 }
732 } else {
733 const formula = pricingTargetData.map(item => item.tNum);
734 if (row.computationalFormula == '3') {
735 formulaVal = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) * parseFloat(currentValue), 1); // 初始值为1
736 } else {
737 formulaVal = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) + parseFloat(currentValue), 0); // 初始值为0
738 }
739 }
740 return (Math.round(formulaVal * 100) / 100).toFixed(2);
741 };
742 const getTargetNum = (params) => {
743 // loading.value = true;
744 getPriceResult(params).then((res: any) => {
745 // loading.value = false;
746 if (res.code === proxy.$passCode) {
747 const data = res.data || [];
748 disScore.value = data;
749 } else {
750 proxy.$ElMessage.error(res.msg);
751 }
752 }).catch(() => {
753 // loading.value = false;
754 });
755 }
756
757 // 生成报告内容
758 const reporting = (formInfo) => {
759 let resultInfo: any = [];
760 const signatoryData = JSON.parse(JSON.stringify(modelData.value.pricingDimensionalityRSVOS || '[]'));
761 signatoryData.map((sign, s) => {
762 resultInfo.push({
763 dimensionalityName: sign.dimensionalityName,
764 computationalFormula: sign.computationalFormula,
765 customize: sign.customize,
766 pricingTargetRSVOS: []
767 });
768 const targets = sign.pricingTargetRSVOS || [];
769 const signTargets = targets.map(t => {
770 let tNum: any = 0, tCustomize = '';
771 if (t.targetType == '3') { // 指标类型-数据字典
772 const tName = dictionaryData.value.find(d => d.guid == t.guid) ? `dict_${t.guid}` : '';
773 if (tName) {
774 const pVal = typeMap.value[tName].find(t => t.value == formInfo[tName]);
775 const dictionary = t.dictionaryJson.find(d => d.name == pVal.label);
776 if (sign.computationalFormula == '1') {// 加权平均
777 tNum = parseFloat(t.weight) / 100 * parseFloat(dictionary?.value || t.defaultValue || 0);
778 tCustomize = `权重${parseFloat(t.weight) / 100} * 因子/默认值${parseFloat(dictionary?.value || t.defaultValue || 0)}`;
779 } else { // 其他
780 tNum = parseFloat(dictionary?.value || t.defaultValue || 0);
781 tCustomize = `默认值${parseFloat(dictionary?.value || t.defaultValue || 0)}`;
782 }
783 t.dictionaryName == '数据用途' && (dataUsage.value = pVal.value || '');
784 }
785 } else if (t.targetType == '2') {// 指标类型-系统功能
786 if (t.functionName == '1') { // 功能名称-质量评价模型
787 const score = parseFloat(qualityScoreData.value.qualityScore || 0);
788 tNum = parseFloat(t.weight || 1) / 100 * score;
789 tCustomize = `权重${parseFloat(t.weight) / 100} * 模型评分${score}`;
790 } else if (t.functionName == '2') { // 功能名称-疾病管理
791 if (sign.computationalFormula == '1') {// 加权平均
792 const score = parseFloat(disScore.value.find(d => d.guid == t.guid)?.factor || 0);
793 tNum = parseFloat(t.weight) / 100 * score;
794 tCustomize = `权重${parseFloat(t.weight) / 100} * 疾病得分${score}`;
795 } else { //其他
796 tNum = parseFloat(disScore.value.find(d => d.guid == t.guid)?.factor || 0);
797 tCustomize = `疾病得分${tNum}`;
798 }
799 } else if (t.functionName == '3') {// 功能名称-需求表管理
800 const tData = tableData.value.find(f => f.demandTableGuid == t.demandTableGuid || f.guid == t.demandTableGuid);
801 if (tData) {
802 if (sign.computationalFormula == '1') {// 加权平均
803 tNum = parseFloat(t.weight) / 100 * (parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0));
804 tCustomize = `权重${parseFloat(t.weight) / 100} * 匹配率/默认值${parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0)}`;
805 } else { //其他
806 tNum = parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0);
807 tCustomize = `匹配率/默认值${parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0)}`;
808 }
809 }
810 }
811 } else { // 指标类型-系统内置
812 if (sign.computationalFormula == '1') {// 加权平均
813 tNum = parseFloat(t.weight) / 100 * parseFloat(t.defaultValue || 0);
814 tCustomize = `权重${parseFloat(t.weight) / 100} * 默认值${parseFloat(t.defaultValue || 0)}`;
815 } else { //其他
816 tNum = parseFloat(t.defaultValue || 0);
817 tCustomize = `默认值${parseFloat(t.defaultValue || 0)}`;
818 }
819 }
820 t.tNum = (Math.round(parseFloat(tNum) * 100) / 100).toFixed(2);
821 resultInfo[s].pricingTargetRSVOS.push({
822 targetName: t.targetName,
823 targetType: t.targetType,
824 functionName: t.functionName,
825 customize: tCustomize,
826 tNum: t.tNum,
827 })
828 return t;
829 })
830 sign.pricingTargetRSVOS = signTargets;
831 sign.sNum = getSignatory(sign);
832 resultInfo[s].sNum = sign.sNum;
833 })
834 // exportData.value = resultInfo;
835 return { signatoryData, resultInfo };
836 }
837
838 // 计算价格
839 const calculatePrice = (pData) => {
840 let modelFormula = modelData.value.modelFormula;
841 // 1. 移除所有干扰的引号(确保是数学表达式)
842 modelFormula = modelFormula.replace(/["']/g, "");
843
844 // 1. 提取变量名(中文、英文、数字、下划线)
845 const variableRegex = /[\u4e00-\u9fa5a-zA-Z_][\u4e00-\u9fa5a-zA-Z0-9_]*/g;
846 const variableNames = modelFormula.match(variableRegex) || [];
847
848 // 2. 去重
849 const uniqueVariables = [...new Set(variableNames)];
850
851 // 3. 构建变量映射 { 销售额: 2000, 成本: 500.5, ... }
852 const variables = {};
853 uniqueVariables.forEach(name => {
854 const dim = pData.find(d => d.dimensionalityName === name);
855 variables[name] = dim ? parseFloat(dim.sNum) : 0; // 找不到则默认为 0
856 });
857
858 // 4. 替换变量为数值(不加引号,确保是数字运算)
859 let expression = modelFormula;
860 uniqueVariables.forEach(name => {
861 expression = expression.replace(new RegExp(name, 'g'), variables[name]);
862 });
863
864 // 5. 安全计算(推荐 math.js,或 new Function)
865 try {
866 //如果用 eval,确保表达式格式正确
867 const resultNum = eval(expression);
868
869 dataTransactionPrice.value = (Math.round(parseFloat(resultNum) * 100) / 100).toFixed(2);
870 } catch (error) {
871 console.error('公式计算错误:', error);
872 return NaN;
873 }
874 };
875
876 // 计算结果和提交
877 const checkForm = (type) => {
878 const baseConfigFormObj = baseConfigFormRef.value;
879 const baseConfigFormEl = baseConfigFormObj.ruleFormRef;
880 const baseConfigFormInfo = baseConfigFormObj.formInline;
881 baseConfigFormEl.validate((valid, errorItem) => {
882 if (valid) {
883 if (type == 'calculate') {
884 const { signatoryData, resultInfo } = reporting(baseConfigFormInfo);
885 exportData.value = resultInfo;
886 calculatePrice(signatoryData);
887 } else if (type == 'export') {
888 const { signatoryData, resultInfo } = reporting(baseConfigFormInfo);
889 exportData.value = resultInfo;
890 !dataTransactionPrice.value && calculatePrice(signatoryData);
891 loading.value = true;
892 let exportOut: any = {};
893 // 估值对象信息
894 const damName = typeMap.value.dataResourceGuid.find(f => f.damGuid == baseConfigFormInfo.dataResourceGuid)?.damName || '';
895 exportOut.one = `${baseConfigFormInfo.belongingEntityGuid}拟了解其所持有的\"${damName}\"相关数据资源的价格,为此需对该行为涉及的数据资源在不同应用场景下,基于数据资源持有单位的性质、信息化程度、数据稀缺性、需求匹配等情况下,为上述经济行为提供定价参考依据。`;
896 exportOut.two = `估值对象:${baseConfigFormInfo.belongingEntityGuid}持有的\"${damName}\"`;
897 // 估值范围信息
898 const damNames = demandTableList.value.map(item => item.menuName)
899 let rangStr = `包含${damNames.join('、')}等${damNames.length}张表单,${damNames.length}张表共计${demandTableFieldAllNum.value}个字段`;
900 const dataTimeliness = pricingTargetList.value.find(p => p.dictionaryName == '时效性');
901 const dataTimelinessStr = dataTimeliness ? typeMap.value[`dict_${dataTimeliness.guid}`].find(f => f.value == baseConfigFormInfo[`dict_${dataTimeliness.guid}`])?.label || '' : '';
902 rangStr += dataTimelinessStr ? `,时间跨度为${dataTimelinessStr}的数据` : `的数据`;
903 damNames.length && (exportOut.two = `${exportOut.two}\n估值范围:${rangStr}`);
904 // 字典
905 let dictList: any = [], hasModelScore = false;
906 const dictStr = exportData.value.map(e => {
907 // 检查是否有质量模型评分
908 hasModelScore = hasModelScore || e.pricingTargetRSVOS.some(
909 t => t.targetType === '2' && t.functionName === '1'
910 );
911
912 // 只有当维度指标数大于1时才处理明细
913 if (e.pricingTargetRSVOS.length > 1) {
914 const targetStr = e.pricingTargetRSVOS
915 .map(t => `${t.targetName}为${changeNum(t.tNum, 2)}`)
916 .join('、');
917
918 dictList.push(`${e.dimensionalityName}为${changeNum(e.sNum, 2)},其中${targetStr}`);
919 }
920
921 return `${e.dimensionalityName}为${changeNum(e.sNum, 2)}`;
922 })
923 let dictListStr = `${dictStr.join(',')}。\n${dictList.join(';\n')}`
924 // 质量模型
925 if (hasModelScore) {
926 const { largeCategoryScoreList = [], qualityScore = 0 } = qualityScoreData.value;
927 const qualityParts = [
928 `数据的总体质量得分为${changeNum(qualityScore, 2)}`
929 ];
930
931 if (largeCategoryScoreList.length) {
932 const categoryScores = largeCategoryScoreList.map(
933 q => `${q.largeCategoryName}方面得分为${changeNum(q.largeCategoryScore || 0, 2)}`
934 );
935 qualityParts.push(`其中${categoryScores.join(',')}`);
936 }
937
938 dictListStr += `;\n${qualityParts.join('。')}`;
939 }
940 exportOut.three = `${baseConfigFormInfo.belongingEntityGuid}持有的"${damName}"的数据(患者人次)单价为${changeNum(dataTransactionPrice.value, 2)}元${dictListStr ? `;其中${dictListStr}` : '。'}`;
941
942 exportModelScore(exportOut).then((res: any) => {
943 loading.value = false;
944 if (res && !res.msg) {
945 ElMessage({
946 type: "success",
947 message: '下载报告成功',
948 });
949 download(res, `数据定价报告.doc`, 'word');
950 } else {
951 res?.msg && ElMessage.error(res?.msg);
952 }
953 }).catch(() => {
954 loading.value = false;
955 ElMessage({
956 type: "error",
957 message: '下载报告请求失败',
958 });
959 })
960 } else {
961 const { signatoryData, resultInfo } = reporting(baseConfigFormInfo);
962 exportData.value = resultInfo;
963 !dataTransactionPrice.value && calculatePrice(signatoryData);
964 const modelName = typeMap.value.modelGuid.find(d => d.guid == baseConfigFormInfo.modelGuid)?.modelName || '';
965 const dataResourceName = typeMap.value.dataResourceGuid.find(d => d.damGuid == baseConfigFormInfo.dataResourceGuid)?.damName || '';
966 const diseaseGuid = baseConfigFormInfo.diseaseGuid || '';
967 let params: any = {
968 tenantGuid: userData.tenantGuid,
969 dataTransactionPrice: dataTransactionPrice.value,
970 modelGuid: baseConfigFormInfo.modelGuid,
971 modelName,
972 dataResourceGuid: baseConfigFormInfo.dataResourceGuid,
973 dataResourceName,
974 belongingEntityGuid: baseConfigFormInfo.belongingEntityGuid,
975 belongingTheme: baseConfigFormInfo.belongingTheme,
976 diseaseGuid,
977 diseaseName: '',
978 dataUsage: dataUsage.value
979 };
980 if (diseaseGuid) {
981 const parentsData = baseConfigFormObj.getCascaderCheckedData();
982 params.diseaseName = parentsData[0]?.label || '';
983 }
984 let dictionaryJson = {};
985 for (var b in baseConfigFormInfo) {
986 if (b.indexOf('dict_') > -1) {
987 dictionaryJson[b] = baseConfigFormInfo[b];
988 }
989 }
990 params.dictionaryJson = Object.keys(dictionaryJson).length ? JSON.stringify(dictionaryJson) : '';
991 let demandMatchingData: any = [];
992 tableData.value.map(item => {
993 demandMatchingData.push({
994 demandTableName: item.demandTableName,
995 demandTableGuid: item.demandTableGuid || item.guid, // 需求表guid
996 dataTableGuid: item.dataTableGuid, // 数据资源表guid
997 weightDemandTable: item.weightDemandTable,
998 dataFieldsNum: item.dataFieldsNum,
999 pricingDemandFieldRQVOS: item.dataFields.map(d => {
1000 return {
1001 demandFieldGuid: d.demandFieldGuid || d.guid, // 资源表字段guid
1002 fieldName: d.fieldName,
1003 enName: d.enName,
1004 chName: d.chName,
1005 isRequired: d.isRequired
1006 }
1007 })
1008 })
1009 });
1010 params.dataPricingDemandmatchingRQVOS = demandMatchingData;
1011 guid && (params.guid = guid);
1012 loading.value = true;
1013 savePrice(params).then((res: any) => {
1014 loading.value = false;
1015 if (res.code == proxy.$passCode) {
1016 ElMessage({
1017 type: "success",
1018 message: guid ? '编辑数据定价成功' : '新增数据定价成功',
1019 });
1020 toPath()
1021 } else {
1022 proxy.$ElMessage.error(res.msg);
1023 }
1024 }).catch(() => {
1025 loading.value = false;
1026 });
1027 }
1028 } else {
1029 expand1.value = true;
1030 var obj = Object.keys(errorItem);
1031 baseConfigFormEl.scrollToField(obj[0]);
1032 }
1033 })
1034 }
1035 const btnClick = async (btn, row: any = null) => {
1036 const type = btn.value;
1037 if (type == 'dim') {
1038 baseConfigFormItems.value.at(-1).default += btn.name;
1039 } else if (type == 'del-signatory') {
1040 open('确定要删除该条维度数据吗?', 'warning');
1041 } else if (type == 'expend') {
1042 expendTableRef.value.toggleRowExpansion(row);
1043 } else if (type == 'calculate' || type == 'submit') {
1044 if (type == 'submit') {
1045 ElMessageBox.confirm(dataTransactionPrice.value === '' ? '是否直接计算价格并提交' : '请确认当前数据交易价格是否为最新计算结果', '提示', {
1046 confirmButtonText: '确定',
1047 cancelButtonText: '取消',
1048 type: 'warning',
1049 }).then(() => {
1050 checkForm(type);
1051 }).catch(() => {
1052 ElMessage.info('已取消提交操作');
1053 });
1054 } else {
1055 checkForm(type);
1056 }
1057 } else if (type == 'export') {
1058 ElMessageBox.confirm(dataTransactionPrice.value === '' ? '是否直接计算价格并下载' : '请确认当前数据交易价格是否为最新计算结果', '提示', {
1059 confirmButtonText: '确定',
1060 cancelButtonText: '取消',
1061 type: 'warning',
1062 }).then(() => {
1063 checkForm(type);
1064 }).catch(() => {
1065 ElMessage.info('已取消下载操作');
1066 });
1067 } else if (type == 'cancel') {
1068 ElMessageBox.confirm(
1069 "当前页面尚未保存,确定关闭吗?",
1070 "提示",
1071 {
1072 confirmButtonText: "确定",
1073 cancelButtonText: "取消",
1074 type: "warning",
1075 }
1076 ).then(() => {
1077 toPath()
1078 }).catch(() => {
1079 ElMessage({
1080 type: "info",
1081 message: "已取消",
1082 });
1083 });
1084 }
1085 }
1086 onActivated(() => {
1087 let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === router.currentRoute.value.fullPath);
1088 if (tab) {
1089 switch (route.query.type) {
1090 case 'create':
1091 tab.meta.title = `新增数据定价`;
1092 break;
1093 case 'edit':
1094 tab.meta.title = `编辑-${priceName}`;
1095 break;
1096 case 'detail':
1097 tab.meta.title = `详情-${priceName}`;
1098 break;
1099 }
1100 }
1101 getModel()
1102 })
1103 onBeforeMount(() => {
1104 if (guid) {
1105 getDetail();
1106 } else {
1107 getDataTypeList();
1108 getModel()
1109 }
1110 })
1111 onMounted(() => {
1112 })
1113 </script>
1114 <template>
1115 <div class="container_wrap full" v-loading="loading">
1116 <div class="content_main panel">
1117 <ContentWrap id="contract-content-wrap" title="输入参数" expandSwicth style="margin-top: 15px" :isExpand="expand1"
1118 @expand="(v) => expand1 = v">
1119 <Form ref="baseConfigFormRef" formId="contract-content-form" :itemList="baseConfigForm.items"
1120 :rules="baseConfigForm.rules" col="col3" @selectChange="selectChange" @cascaderChange="cascaderChange" />
1121 </ContentWrap>
1122 <ContentWrap id="contract-signatory-wrap" title="需求匹配" expandSwicth style="margin-top: 15px" :isExpand="expand2"
1123 @expand="(v) => expand2 = v">
1124 <div class="table_panel_wrap">
1125 <div class="table_tool">
1126 <div class="tool_title">
1127 <div class="title_desc">
1128 <span>需求表数量:</span>
1129 <span class="text-num">{{ demandTableList.length }}</span>
1130 <span>张,字段数:</span>
1131 <span class="text-num">{{ demandTableFieldAllNum }}</span>
1132 <span>匹配表数量:</span>
1133 <span class="text-num">{{ resourceTableAllNum }}</span>
1134 <span>张,字段数:</span>
1135 <span class="text-num">{{ resourceTableFieldAllNum }}</span>
1136 </div>
1137 </div>
1138 </div>
1139 <div class="table_panel" v-loading="tableLoading">
1140 <el-table ref="expendTableRef" border :data="tableData" row-key="demandTableName" tooltip-effect="light"
1141 style="height: 100%;">
1142 <el-table-column type="expand">
1143 <template #default="props">
1144 <div class="expand_panel">
1145 <div class="table_tool">
1146 <div class="tool_title">
1147 <div class="title_desc">
1148 <span>需求字段数:</span>
1149 <span class="text-num">{{ props.row.dataFields.length }}</span>
1150 <span>个,匹配字段数:</span>
1151 <span class="text-num">{{ props.row.dataFieldsNum }}</span>
1152 </div>
1153 </div>
1154 </div>
1155 <el-table :data="props.row.dataFields" border>
1156 <el-table-column label="序号" type="index" width="56" align="center" />
1157 <el-table-column label="需求字段中文" prop="fieldName" class-name="edit-col">
1158 <template #default="scope">
1159 <el-input v-model.trim="scope.row.fieldName" placeholder="请输入" disabled />
1160 </template>
1161 </el-table-column>
1162 <el-table-column label="匹配字段中文" prop="chName" class-name="edit-col">
1163 <template #default="scope">
1164 <el-select v-model="scope.row.chName" clearable filterable
1165 @change="val => selectChange(val, { field: 'chName', index: scope.$index }, props)">
1166 <el-option v-for="(opt, o) in scope.row.damFieldTable" :label="opt.label" :value="opt.value"
1167 :key="o" />
1168 </el-select>
1169 </template>
1170 </el-table-column>
1171 <el-table-column label="匹配字段英文" prop="enName" class-name="edit-col">
1172 <template #default="scope">
1173 <el-input v-model.trim="scope.row.enName" placeholder="请输入" disabled />
1174 </template>
1175 </el-table-column>
1176 <el-table-column label="是否必需字段" prop="isRequired" class-name="edit-col">
1177 <template #default="scope">
1178 <el-select v-model="scope.row.isRequired" disabled>
1179 <el-option label="是" value="Y" />
1180 <el-option label="否" value="N" />
1181 </el-select>
1182 </template>
1183 </el-table-column>
1184 </el-table>
1185 </div>
1186 </template>
1187 </el-table-column>
1188 <el-table-column label="序号" type="index" width="56" align="center" />
1189 <el-table-column v-for="item in tableFields" :key="item.field" :label="item.label" :prop="item.field"
1190 :width="item.width" :align="item.align" class-name="edit-col">
1191 <template #default="scope">
1192 <el-select v-if="item.type == 'select'" v-model="scope.row[item.field]" clearable filterable
1193 @change="val => selectChange(val, item, scope)">
1194 <el-option v-for="(opt, o) in scope.row.damDataTable" :label="opt.label" :value="opt.value"
1195 :key="o" />
1196 </el-select>
1197 <el-input v-else v-model.trim="scope.row[item.field]" :disabled="item.disabled" placeholder="请输入"
1198 clearable />
1199 </template>
1200 </el-table-column>
1201 <el-table-column label="操作" fixed="right" width="100">
1202 <template #default="scope">
1203 <el-button type="primary" link @click="btnClick({ value: 'expend' }, scope.row)">字段映射</el-button>
1204 </template>
1205 </el-table-column>
1206 </el-table>
1207 </div>
1208 </div>
1209 </ContentWrap>
1210 <ContentWrap id="contract-content-wrap" title="输出结构" expandSwicth style="margin-top: 15px" :isExpand="expand3"
1211 @expand="(v) => expand3 = v">
1212 <el-form class="result-form">
1213 <el-form-item class="flex-column" label="数据交易价格(元)">
1214 <el-input v-model="dataTransactionPrice" placeholder="" disabled style="display: none;" />
1215 <div class="result-price">{{ changeNum(dataTransactionPrice, 2) }}</div>
1216 </el-form-item>
1217 <el-form-item class="align-end" style="margin-bottom: 14px;">
1218 <el-button type="primary" @click="btnClick({ value: 'calculate' })">开始计算</el-button>
1219 <el-button @click="btnClick({ value: 'export' })">下载报告</el-button>
1220 <span style="margin-left: 8px">输出结果报告查看</span>
1221 </el-form-item>
1222 </el-form>
1223 </ContentWrap>
1224 </div>
1225 <div class="tool_btns">
1226 <div class="btns">
1227 <el-button @click="btnClick({ value: 'cancel' })">取消</el-button>
1228 <el-button type="primary" @click="btnClick({ value: 'submit' })">提交</el-button>
1229 </div>
1230 </div>
1231 </div>
1232 </template>
1233 <style scoped lang="scss">
1234 .container_wrap {
1235 overflow: hidden;
1236
1237 .content_main {
1238 height: calc(100% - 45px);
1239 overflow: hidden auto;
1240
1241 &.panel {
1242 padding: 0 16px 16px;
1243 }
1244
1245 :deep(.el-card) {
1246 &#contract-signatory-wrap {
1247 .card-body-content {
1248 padding: 8px 16px;
1249 }
1250 }
1251 }
1252
1253 .signatory-tags {
1254 margin-bottom: 11px;
1255 }
1256
1257 .table_panel_wrap {
1258 margin-bottom: 4px;
1259
1260 .table_tool {
1261 height: 36px;
1262 display: flex;
1263 justify-content: space-between;
1264 align-items: center;
1265
1266 .tool_title {
1267 width: 100%;
1268 display: flex;
1269 justify-content: start;
1270 }
1271
1272 .title_desc {
1273 overflow: hidden;
1274 white-space: nowrap;
1275 text-overflow: ellipsis;
1276
1277 .text-num {
1278 color: var(--el-color-primary);
1279 margin: 0 8px;
1280 }
1281 }
1282 }
1283
1284 .table_panel {
1285 margin-bottom: 4px;
1286 height: 392px;
1287
1288 :deep(.el-table) {
1289 .el-table__cell {
1290 &.edit-col {
1291 padding: 4px 0;
1292
1293 .cell {
1294 padding: 0 4px;
1295
1296 .el-cascader {
1297 width: 100%;
1298 height: 28px;
1299 }
1300
1301 .el-input {
1302 height: 28px;
1303 }
1304 }
1305 }
1306
1307 .expand-icon {
1308 color: #888;
1309 margin-right: 8px;
1310 vertical-align: text-bottom;
1311 cursor: pointer;
1312 }
1313 }
1314
1315 .el-input.is-disabled .el-input__wrapper {
1316 background-color: var(--el-disabled-bg-color);
1317 }
1318
1319 .el-select__wrapper.is-disabled {
1320 background-color: var(--el-disabled-bg-color);
1321 }
1322 }
1323
1324 .expand_panel {
1325 padding: 6px;
1326 margin: -6px 0;
1327 background: #fff;
1328 }
1329 }
1330 }
1331 }
1332
1333 .btn-block {
1334 width: 100%;
1335 margin: 16px 0 8px;
1336 }
1337
1338 .tool_btns {
1339 height: 44px;
1340 margin: 0 -8px;
1341 display: flex;
1342 justify-content: center;
1343 align-items: center;
1344 border-top: 1px solid #d9d9d9;
1345 }
1346 }
1347
1348 :deep(.el-form) {
1349 &.result-form {
1350 display: flex;
1351
1352 .el-form-item {
1353 &.flex-column {
1354 width: calc(33.33% - 6px);
1355 margin-right: 8px;
1356 display: flex;
1357 flex-direction: column;
1358 align-items: self-start;
1359
1360 .el-form-item__content {
1361 width: 100%;
1362 }
1363
1364 .result-price {
1365 width: 100%;
1366 height: 32px;
1367 line-height: 32px;
1368 padding: 1px 11px;
1369 border-radius: 4px;
1370 cursor: not-allowed;
1371 color: var(--el-disabled-text-color);
1372 background-color: var(--el-disabled-bg-color);
1373 box-shadow: 0 0 0 1px var(--el-disabled-border-color) inset;
1374 }
1375 }
1376
1377 &.align-end {
1378 align-self: flex-end;
1379 }
1380 }
1381 }
1382
1383 .el-select__wrapper.is-disabled {
1384 background-color: var(--el-disabled-bg-color);
1385 }
1386 }
1387 </style>
1 <route lang="yaml">
2 name: demandManage
3 </route>
4
5 <script lang="ts" setup name="demandManage">
6 import { ref } from 'vue';
7 import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
8 import { ElMessage, ElMessageBox } from "element-plus";
9 import { CirclePlus, Search } from "@element-plus/icons-vue";
10 import { useRouter, useRoute } from "vue-router";
11 import { changeNum, getCurrentTime } from '@/utils/common';
12 import useUserStore from "@/store/modules/user";
13 import useCatchStore from "@/store/modules/catch";
14 import {
15 getDemandAll,
16 saveDemandTree,
17 updateDemandTree,
18 deleteDemandTree,
19 getDemandList,
20 saveDemand,
21 updateDemand,
22 deleteDemand,
23 } from '@/api/modules/dataPricing';
24
25 const router = useRouter();
26 const userStore = useUserStore()
27 const userData = JSON.parse(userStore.userData)
28 const { proxy } = getCurrentInstance() as any;
29 const cacheStore = useCatchStore()
30
31 /** 左侧标签列表管理 */
32 const treeInfoRef = ref();
33 const treeData = ref([]);
34 const treeInfo = ref({
35 id: "demand-tree",
36 filter: true,
37 editTreeItem: true,
38 queryValue: "",
39 loading: false,
40 className: 'tree-list-manage',
41 queryPlaceholder: "输入关键字搜索",
42 props: {
43 label: "menuName",
44 value: "guid",
45 children: 'childList',
46 },
47 prefix: {
48 type: 'prefixIcon'
49 },
50 lazy: false,
51 nodeKey: 'guid',
52 expendAll: true,
53 currentNodeKey: '',
54 expandOnNodeClick: false,
55 data: [],
56 });
57
58 const catalogData = ref([]);
59 const searchItemList = ref([
60 {
61 type: "select",
62 label: "",
63 field: "isRequired",
64 placeholder: "是否必选",
65 default: "",
66 options: [
67 { label: '是', value: 'Y' },
68 { label: '否', value: 'N' }
69 ],
70 clearable: true,
71 style: {
72 width: '140px'
73 }
74 },
75 {
76 type: "input",
77 label: "",
78 field: "fieldName",
79 default: "",
80 placeholder: "字段名称",
81 clearable: true,
82 style: {
83 width: '230px'
84 }
85 },
86 ]);
87
88 const lastClickNode: any = ref({});
89 const showTable = ref(false)
90 /** 记录正在提交审批流程的api,防止重复提交 */
91 const addDataPromise: any = ref();
92 const tableRef = ref();
93 const tableData: any = ref([]);
94 const selectRowData: any = ref([])
95 const currTableData: any = ref({});
96 const page: any = ref({
97 ...commonPageConfig,
98 fieldName: '',
99 isRequired: '',
100 });
101 const tableFields: any = [
102 { label: "字段名称", field: "fieldName" },
103 { label: "是否必选", field: "isRequired", type: 'select' },
104 { label: "排序", field: "orderNum", type: 'defaultValue' },
105 { label: '启用状态', field: 'bizState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N' },
106 { label: "创建时间", field: "createTime", type: 'defaultValue' },
107 ]
108 const tableInfo: any = ref({
109 id: 'api-data-table',
110 rowKey: 'guid',
111 loading: false,
112 multiple: true,
113 fields: [
114 { label: "字段名称", field: "fieldName", width: 200, align: "left", type: 'edit' },
115 {
116 label: "是否必选", field: "isRequired", width: 140, type: 'edit', getName: (scope) => {
117 return scope.row.isRequired == 'Y' ? '是' : '否'
118 }
119 },
120 { label: "排序", field: "orderNum", width: 120, type: 'edit', dataTypeName: 'fieldType' },
121 {
122 label: '启用状态', field: 'bizState', type: 'edit', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N', switchWidth: 56, width: 96, align: 'center',
123 },
124 { label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME, type: 'edit', dataTypeName: 'dateType' },
125 ],
126 data: tableData.value,
127 editInfo: [],
128 page: {
129 type: "normal",
130 rows: 0,
131 ...page.value,
132 },
133 actionInfo: {
134 label: "操作",
135 type: "btn",
136 isMore: false,
137 width: 120,
138 btns: (scope) => {
139 let btns: any[] = [];
140 if (scope.row.STATUS == 'edit') {
141 btns.push({
142 value: 'save', label: '保存', click: (scope) => {
143 if (addDataPromise.value) {
144 return;
145 }
146 // 调用新增数据的接口。
147 let params = { ...scope.row, tenantGuid: userData.tenantGuid };
148 params.relationMenuGuid = lastClickNode.value.guid;
149 delete params['ROWID'];
150 delete params['STATUS'];
151 delete params['STATE'];
152 delete params.dateType;
153 delete params.fieldType;
154 if (params.guid) {
155 addDataPromise.value = updateDemand(params).then((res: any) => {
156 addDataPromise.value = null;
157 if (res.code == proxy.$passCode) {
158 page.value.curr = 1;
159 getTableData();
160 proxy.$ElMessage.success('编辑数据保存成功');
161 } else {
162 proxy.$ElMessage.error(res.msg);
163 }
164 });
165 } else {
166 addDataPromise.value = saveDemand(params).then((res: any) => {
167 addDataPromise.value = null;
168 if (res.code == proxy.$passCode) {
169 page.value.curr = 1;
170 getTableData();
171 proxy.$ElMessage.success('新增数据保存成功');
172 } else {
173 proxy.$ElMessage.error(res.msg);
174 }
175 });
176 }
177 }
178 });
179 btns.push({
180 value: 'del', label: '删除', click: (scope) => {
181 proxy.$openMessageBox('确定要删除该条数据吗?', () => {
182 tableData.value.splice(0, 1);
183 proxy.$ElMessage.success("删除成功");
184 }, () => {
185 proxy.$ElMessage.info("已取消删除");
186 })
187 }
188 });
189 return btns;
190 }
191 btns.push({ value: 'edit', label: '编辑' });
192 btns.push({ value: 'del', label: '删除' });
193 return btns;
194 }
195 }
196 });
197
198 /** ---- 新增修改标签对话框配置------------- */
199 const formItems: any = ref([
200 {
201 label: '需求表名称',
202 type: 'input',
203 placeholder: '请输入',
204 field: 'menuName',
205 default: '',
206 maxlength: 20,
207 clearable: true,
208 required: true,
209 },
210 {
211 label: '是否为目录',
212 type: 'select',
213 field: 'isCatalog',
214 default: 'N',
215 options: [
216 { label: '是', value: 'Y' },
217 { label: '否', value: 'N' }
218 ],
219 required: true,
220 },
221 {
222 label: '所属上级',
223 type: 'cascader',
224 placeholder: '请选择',
225 field: "parentGuid",
226 default: [],
227 options: [],
228 showAllLevels: false,
229 props: {
230 checkStrictly: true,
231 label: "menuName",
232 value: "guid",
233 children: 'childList',
234 emitPath: false
235 },
236 clearable: true,
237 filterable: true,
238 block: true
239 },
240 {
241 label: '排序',
242 type: 'input',
243 placeholder: '请输入',
244 field: 'orderNum',
245 default: '',
246 inputType: 'integerNumber',
247 maxlength: 6,
248 clearable: true,
249 },
250 ])
251
252 const formRules = ref({
253 menuName: [
254 { required: true, message: '请填写需求表名称', trigger: 'blur' }
255 ],
256 isCatalog: [
257 { required: true, message: '请选择是否为目录', trigger: 'change' }
258 ]
259 });
260
261 /** 新建api标签对话框 */
262 const demScheduleLabelDialogInfo = ref({
263 visible: false,
264 size: 480,
265 direction: "column",
266 header: {
267 title: "",
268 },
269 type: '',
270 contents: [
271 {
272 type: 'form',
273 title: '',
274 formInfo: {
275 id: 'add-staff-form',
276 items: formItems.value,
277 rules: formRules.value
278 }
279 }
280 ],
281 footer: {
282 btns: [
283 { type: "default", label: "取消", value: "cancel" },
284 { type: "primary", label: "确定", value: "submit" },
285 ],
286 },
287 });
288
289 // 获取需求表树形数据
290 const getTreeData = () => {
291 getDemandAll({ isCatalog: '' }).then((res: any) => {
292 if (res.code == proxy.$passCode) {
293 const data = res.data || [];
294 treeData.value = JSON.parse(JSON.stringify(data));
295 treeInfo.value.data = JSON.parse(JSON.stringify(data));
296 treeInfo.value.currentNodeKey = data[0]?.guid || "";
297 lastClickNode.value = data[0] || {};
298 nextTick(() => {
299 treeInfoRef.value?.setCurrentKey(treeInfo.value.currentNodeKey);
300 if (lastClickNode.value.isCatalog != 'Y') {
301 showTable.value = true;
302 page.value.curr = 1;
303 getTableData();
304 } else {
305 showTable.value = false;
306 }
307 })
308 } else {
309 proxy.$ElMessage.error(res.msg);
310 }
311 })
312 getCatalog();
313 }
314
315 const getCatalog = () => {
316 getDemandAll({ isCatalog: 'Y' }).then((res: any) => {
317 if (res.code == proxy.$passCode) {
318 const data = res.data || [];
319 catalogData.value = JSON.parse(JSON.stringify(data));
320 }
321 })
322 }
323
324 /** 点击左侧节点 */
325 const nodeClick = (data) => {
326 lastClickNode.value = data || {};
327 if (data.isCatalog != 'Y') {
328 showTable.value = true;
329 page.value.curr = 1;
330 getTableData();
331 } else {
332 showTable.value = false;
333 }
334 }
335
336 const setRowData = (info: any = null) => {
337 const rowData: any = {
338 STATUS: 'edit',
339 STATE: "Running",
340 ROWID: '1',
341 dateType: 'datetime',
342 fieldType: 'int',
343 fieldName: info?.fieldName || '',
344 isRequired: info?.isRequired || 'N',
345 bizState: info?.bizState || 'Y',
346 createTime: info?.createTime || getCurrentTime(),
347 orderNum: info?.orderNum !== undefined ? info?.orderNum : tableData.value.length + 1
348 }
349 info && (rowData.guid = info.guid || '');
350 return rowData;
351 }
352
353 // 新增字段
354 const addField = () => {
355 if (tableData.value[0]?.STATUS == 'edit') {
356 proxy.$ElMessage.warning('请先保存正在编辑的行数据');
357 return;
358 }
359 // 支持新增数据,直接添加一行进行编辑。
360 const rowData = setRowData();
361 tableData.value.unshift(rowData);
362 nextTick(() => {
363 tableRef?.value?.tableRef?.setScrollTop(0);
364 })
365 }
366
367 // 批量删除
368 const batching = (type) => {
369 if (type == 'delete') {
370 if (selectRowData.value.length == 0) {
371 ElMessage({
372 type: 'error',
373 message: '请选择需要删除的数据',
374 })
375 return
376 }
377 open("此操作将永久删除, 是否继续?", "warning", true);
378 } else if (type == 'import') {
379 const info = {
380 dictionaryGuid: ''
381 }
382 cacheStore.setCatch('uploadSetting', info)
383 router.push(
384 {
385 name: 'importFileDemandManage',
386 query: {
387 isfileImport: '7'
388 }
389 }
390 );
391 }
392 }
393
394 const setFormItems = (info = null, type = '') => {
395 const datas: any = info || {};
396 formItems.value.map(item => {
397 if (item.field == 'isCatalog') {
398 item.default = datas[item.field] || 'N';
399 } else {
400 item.default = datas[item.field] || '';
401 }
402 })
403 }
404
405 const setCataLog = (dataArr, id, parentDisabled = false) => {
406 return dataArr.map(item => {
407 // 当前节点是否被禁选(自身匹配或父节点已禁选)
408 const isDisabled = parentDisabled || item.guid === id;
409 // 新节点基础属性
410 const newItem = {
411 ...item,
412 disabled: isDisabled
413 };
414
415 // 递归处理子节点,并传递当前节点的禁选状态
416 if (item.childList && item.childList.length > 0) {
417 newItem.childList = setCataLog(item.childList, id, isDisabled);
418 }
419 return newItem;
420 })
421 }
422
423 /** 处理标签的操作按钮,编辑,删除 */
424 const handleTreeItemMenuClick = async (node, type) => {
425 let data = node.data;
426 lastClickNode.value = data;
427 if (type == "edit") {
428 await setFormItems(data);
429 if (data.isCatalog == 'Y') {
430 formItems.value[1].disabled = data.childList && data.childList.length > 0;
431 } else {
432 formItems.value[1].disabled = tableData.value && tableData.value.length > 0;
433 }
434 let cata = setCataLog(catalogData.value, data.guid);
435 formItems.value.at(-2).options = cata;
436 demScheduleLabelDialogInfo.value.contents[0].formInfo.items = formItems.value;
437 demScheduleLabelDialogInfo.value.header.title = "编辑需求表";
438 demScheduleLabelDialogInfo.value.type = 'edit';
439 demScheduleLabelDialogInfo.value.visible = true;
440 } else if (type == "delete") {
441 proxy.$openMessageBox('确定要删除该需求表吗?', () => {
442 deleteDemandTree([lastClickNode.value.guid]).then((res: any) => {
443 if (res.code == proxy.$passCode) {
444 getTreeData();
445 proxy.$ElMessage.success("删除该需求表成功");
446 } else {
447 proxy.$ElMessage.error(res.msg);
448 }
449 });
450 }, () => {
451 proxy.$ElMessage.info("已取消");
452 })
453 }
454 }
455 /** 新建分组对话框确定。 */
456 const demScheduleLabelDialogBtnClick = (btn, info) => {
457 if (btn.value == 'submit') {
458 let params = { ...info, parentGuid: info.parentGuid || '', tenantGuid: userData.tenantGuid };
459 if (demScheduleLabelDialogInfo.value.type == 'add') {
460 saveDemandTree(params).then((res: any) => {
461 if (res.code == proxy.$passCode) {
462 getTreeData();
463 proxy.$ElMessage({
464 type: 'success',
465 message: '新增需求表成功'
466 })
467 demScheduleLabelDialogInfo.value.visible = false;
468 } else {
469 proxy.$ElMessage({
470 type: 'error',
471 message: res.msg,
472 })
473 }
474 })
475 } else {
476 params.guid = lastClickNode.value.guid;
477 updateDemandTree(params).then((res: any) => {
478 if (res.code == proxy.$passCode) {
479 getTreeData();
480 proxy.$ElMessage({
481 type: 'success',
482 message: '编辑需求表成功'
483 })
484 demScheduleLabelDialogInfo.value.visible = false;
485 } else {
486 proxy.$ElMessage({
487 type: 'error',
488 message: res.msg,
489 })
490 }
491 })
492 }
493 } else if (btn.value == 'cancel') {
494 demScheduleLabelDialogInfo.value.visible = false;
495 }
496 };
497
498 const addTreeNode = async () => {
499 await setFormItems();
500 demScheduleLabelDialogInfo.value.header.title = '新建需求表';
501 formItems.value[1].disabled = false;
502 formItems.value.at(-2).options = catalogData.value;
503 demScheduleLabelDialogInfo.value.contents[0].formInfo.items = formItems.value;
504 demScheduleLabelDialogInfo.value.type = 'add';
505 demScheduleLabelDialogInfo.value.visible = true;
506 }
507
508 // 设置需求表表头
509 const setTableField = () => {
510 const editInfo = {};
511 tableFields.forEach(f => {
512 if (f.type == 'switch') {
513 editInfo[f.field] = {
514 label: f.label,
515 type: f.type,
516 field: f.field,
517 default: false,
518 activeText: '启用',
519 inactiveText: '停用',
520 activeValue: 'Y',
521 inactiveValue: 'N',
522 };
523 } else if (f.type == 'select') {
524 editInfo[f.field] = {
525 label: f.label,
526 type: f.type,
527 field: f.field,
528 default: '',
529 options: [
530 { label: '是', value: 'Y' },
531 { label: '否', value: 'N' }
532 ],
533 clearable: true
534 }
535 } else if (f.type == 'defaultValue') {
536 editInfo[f.field] = {
537 label: f.label,
538 type: f.type,
539 field: f.field,
540 default: '',
541 clearable: true,
542 disabled: true,
543 }
544 } else {
545 editInfo[f.field] = {
546 label: f.label,
547 type: 'input',
548 field: f.field,
549 placeholder: f.isPrimary == 'Y' ? '主键自动生成' : '请输入',
550 default: '',
551 clearable: true
552 }
553 }
554 })
555 tableInfo.value.editInfo = editInfo;
556 }
557
558 const toSearch = (val: any = null, clear: boolean = false) => {
559 page.value.curr = 1;
560 if (clear) {
561 searchItemList.value.map((item) => (item.default = ""));
562 page.value.fieldName = '';
563 page.value.isRequired = '';
564 } else {
565 let info = val ? { ...val } : {};
566 searchItemList.value.map(item => {
567 info[item.field] = item.default;
568 })
569 page.value.fieldName = info.fieldName || '';
570 page.value.isRequired = info.isRequired || '';
571 }
572 getTableData();
573 };
574
575 // 获取表格数据
576 const getTableData = () => {
577 if (!lastClickNode.value?.guid) {
578 return;
579 }
580 tableInfo.value.loading = true;
581 getDemandList({
582 pageSize: page.value.limit,
583 pageIndex: page.value.curr,
584 relationMenuGuid: lastClickNode.value.guid,
585 fieldName: page.value.fieldName,
586 isRequired: page.value.isRequired,
587 }).then((res: any) => {
588 tableInfo.value.loading = false;
589 if (res.code == proxy.$passCode) {
590 const data = res.data || {};
591 tableData.value = data.records || [];
592 tableInfo.value.data = tableData.value;
593 tableInfo.value.page.limit = data.pageSize
594 tableInfo.value.page.curr = data.pageIndex
595 tableInfo.value.page.rows = data.totalRows
596 } else {
597 proxy.$ElMessage.error(res.msg);
598 }
599 })
600 }
601
602 const tablePageChange = (info) => {
603 page.value.curr = Number(info.curr);
604 page.value.limit = Number(info.limit);
605 getTableData();
606 };
607
608 const tableSelectionChange = (val) => {
609 selectRowData.value = val;
610 };
611
612 const tableSwitchBeforeChange = (scope, field, callBack) => {
613 if (scope.row.STATUS == 'edit') {
614 callBack(true);
615 } else {
616 // proxy.$openMessageBox(`确定要更新该字段值吗?`, () => {
617 // const state = scope.row[field] == 'Y' ? 'N' : 'Y';
618 // let mdmModelData: any = {};
619 // mdmModelData[field] = state;
620 // let pkValue = scope.row[mergeFormItems.value[0].field];
621 // mdmModelData[mergeFormItems.value[0].field] = pkValue;
622 // updateMdmModelData({
623 // mdmModelTableGuid: props.modelConfigInfo.guid,
624 // mdmModelData: mdmModelData,
625 // pkValues: [pkValue]
626 // }).then((res: any) => {
627 // if (res.code == proxy.$passCode) {
628 // proxy.$ElMessage.success('该字段值更新成功');
629 // let firstData = tableData.value[0];
630 // getMainModelData().then((res: any) => {
631 // if (firstData && firstData.STATUS == 'edit') {
632 // tableData.value.unshift(firstData);
633 // }
634 // });
635 // callBack(true)
636 // } else {
637 // proxy.$ElMessage.error(res.msg);
638 // callBack(false)
639 // }
640 // });
641 // }, () => {
642 // callBack(false);
643 // })
644 }
645 }
646
647 const tableSwitchChange = (val, scope, field) => {
648 if (scope.row.STATUS == 'edit') {
649 tableData.value[scope.$index][field] = val;
650 }
651 }
652
653 const tableBtnClick = (scope, btn) => {
654 const type = btn.value;
655 const row = scope.row;
656 currTableData.value = row;
657 if (type === 'edit') { // 编辑
658 if (tableData.value.find(t => t.STATUS == 'edit')) {
659 proxy.$ElMessage.warning('请先保存正在编辑的行数据');
660 return;
661 }
662 const rowData = setRowData(row);
663 tableData.value[scope.$index] = rowData;
664 } else if (type === 'del') { // 删除
665 open('确定要删除该条数据吗?', 'warning');
666 }
667 };
668
669 const open = (msg, type, isBatch = false) => {
670 ElMessageBox.confirm(msg, "提示", {
671 confirmButtonText: "确定",
672 cancelButtonText: "取消",
673 type: type,
674 }).then(() => {
675 let guids = [currTableData.value.guid]
676 if (isBatch) {
677 guids = selectRowData.value.map(item => item.guid);
678 }
679 deleteDemand(guids).then((res: any) => {
680 if (res.code == proxy.$passCode) {
681 getTableData();
682 ElMessage({
683 type: "success",
684 message: "删除成功",
685 });
686 } else {
687 ElMessage({
688 type: "error",
689 message: res.msg,
690 });
691 }
692 });
693 });
694 };
695
696 onBeforeMount(() => {
697 getTreeData();
698 setTableField();
699 });
700
701 onActivated(() => {
702
703 })
704
705 </script>
706
707 <template>
708 <div class="container_wrap full flex">
709 <div class="aside_wrap">
710 <div class="aside_title">需求表列表</div>
711 <el-icon class="icon-add" color="#4fa1a4" @click="addTreeNode()">
712 <CirclePlus />
713 </el-icon>
714 <Tree ref="treeInfoRef" :treeInfo="treeInfo" @nodeClick="nodeClick" @itemMenuClick="handleTreeItemMenuClick" />
715 </div>
716 <div class="main_wrap">
717 <template v-if="showTable">
718 <div class="table_tool_wrap">
719 <div class="tools_btns">
720 <el-button type="primary" @click="addField" v-preReClick>添加</el-button>
721 <el-button @click="batching('import')" v-preReClick>批量导入</el-button>
722 <el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
723 </div>
724 <div class="tools_serach">
725 <template v-for="(item, i) in searchItemList" :key="'s_' + i">
726 <el-select v-if="item.type == 'select'" v-model="item.default" :placeholder="item.placeholder"
727 :clearable="item.clearable" :style="item.style" @change="val => toSearch()">
728 <el-option v-for="(opt, o) in item.options" :label="opt.label" :value="opt.value" :key="o" />
729 </el-select>
730 <el-input v-else v-model.trim="item.default" :placeholder="item.placeholder" :suffix-icon="Search"
731 :clearable="item.clearable" :style="item.style" @change="val => toSearch()" />
732 </template>
733 </div>
734 </div>
735 <div class="table_panel_wrap">
736 <Table ref="tableRef" :tableInfo="tableInfo" @tableSelectionChange="tableSelectionChange"
737 @tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
738 </div>
739 </template>
740 <div class="card-noData" v-else>
741 <img src="@/assets/images/no-data.png" :style="{ width: '96px', height: '96px' }" />
742 <p>请选择需求表</p>
743 </div>
744 </div>
745 <!-- 添加编辑需求表的对话框 -->
746 <Dialog :dialogInfo="demScheduleLabelDialogInfo" @btnClick="demScheduleLabelDialogBtnClick" />
747 </div>
748 </template>
749
750 <style lang="scss" scoped>
751 .container_wrap {
752 padding: 0;
753 display: flex;
754 justify-content: space-between;
755
756 .aside_wrap {
757 width: 199px;
758 border-right: 1px solid #d9d9d9;
759 box-shadow: none;
760
761 .aside_title {
762 width: calc(100% - 32px);
763 display: inline-block;
764 }
765
766 .icon-add.el-icon {
767 width: 24px;
768 height: 24px;
769 vertical-align: middle;
770 cursor: pointer;
771
772 svg {
773 width: 24px;
774 height: 24px;
775 }
776 }
777
778 :deep(.tree_panel) {
779 height: 100%;
780 padding-top: 0;
781
782 .el-tree {
783 margin: 0;
784 height: calc(100% - 68px);
785 overflow: hidden auto;
786 }
787 }
788
789 }
790
791 .main_wrap {
792 padding: 0 8px;
793 height: auto;
794 flex: 1;
795
796 .table_tool_wrap {
797 width: 100%;
798 padding: 0 8px;
799 display: flex;
800 justify-content: space-between;
801 align-items: center;
802
803 .tools_btns {
804 padding: 0px 0 0;
805 }
806
807 .tools_serach {
808 width: 378px;
809 display: flex;
810 justify-content: space-between;
811 }
812 }
813
814 .table_panel_wrap {
815 width: 100%;
816 height: calc(100% - 44px);
817 padding: 0px 8px 0;
818 }
819
820 .card-noData {
821 position: absolute;
822 top: 50%;
823 left: 50%;
824 transform: translate(-50%, -50%);
825 display: flex;
826 flex-direction: column;
827 align-items: center;
828 }
829 }
830 }
831
832 .row-dialog-content {
833 height: 300px;
834
835 .tools_btns {
836 margin-bottom: 8px;
837 }
838 }
839
840 :deep(.el-form) {
841 .select-input-long {
842 .el-form-item:first-child {
843 width: 67%;
844 margin-right: 0px;
845 }
846
847 .el-form-item:last-child {
848 width: 33%;
849 margin-right: 0px;
850
851 .item-label {
852 white-space: pre;
853
854 .required_mark::after {
855 content: '';
856 }
857 }
858 }
859 }
860
861 .select-input-long-reverse {
862 .el-form-item:first-child {
863 width: 100px;
864 margin-right: 0px;
865 }
866
867 .el-form-item:last-child {
868 width: calc(100% - 100px);
869 margin-right: 0px;
870
871 .item-label {
872 white-space: pre;
873
874 .required_mark::after {
875 content: '';
876 }
877 }
878 }
879 }
880 }
881 </style>
1 <route lang="yaml">
2 name: diseaseManage
3 </route>
4
5 <script lang="ts" setup name="diseaseManage">
6 import { ref } from 'vue';
7 import TableTools from '@/components/Tools/table_tools.vue';
8 import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
9 import { ElMessage, ElMessageBox } from "element-plus";
10 import { CirclePlus } from "@element-plus/icons-vue";
11 import { useRouter, useRoute } from "vue-router";
12 import useUserStore from "@/store/modules/user";
13 import useCatchStore from "@/store/modules/catch";
14 import {
15 getDiseaseAll,
16 getDiseaseList,
17 saveDisease,
18 updateDisease,
19 deleteDisease,
20 } from '@/api/modules/dataPricing';
21 import { changeNum } from '@/utils/common';
22
23 const router = useRouter();
24 const userStore = useUserStore()
25 const userData = JSON.parse(userStore.userData)
26 const { proxy } = getCurrentInstance() as any;
27 const cacheStore = useCatchStore()
28
29 const searchItemList = ref([
30 {
31 type: "input",
32 label: "",
33 field: "diseaseName",
34 default: "",
35 placeholder: "疾病名称",
36 clearable: true,
37 },
38 {
39 type: "select",
40 label: "",
41 field: "isChildrenDisease",
42 placeholder: "是否儿童",
43 default: "",
44 options: [
45 { label: '是', value: 'Y' },
46 { label: '否', value: 'N' }
47 ],
48 clearable: true,
49 }
50 ]);
51
52 const tableData: any = ref([]);
53 const selectRowData: any = ref([])
54 const currTableData: any = ref({});
55 const selectionTreeTableRef = ref();
56 const page: any = ref({
57 ...commonPageConfig,
58 diseaseName: '',
59 isChildrenDisease: ''
60 });
61 const tableInfo = ref({
62 id: 'api-data-table',
63 rowKey: 'guid',
64 loading: false,
65 multiple: true,
66 fields: [
67 { label: "疾病名称", field: "diseaseName", width: 200 },
68 {
69 label: "是否儿童疾病", field: "isChildrenDisease", width: 120, getName: (scope) => {
70 return scope.row.isChildrenDisease == 'Y' ? '是' : '否';
71 }
72 },
73 { label: "发病率/患病率", field: "incidenceRate", width: 120 },
74 {
75 label: "因子", field: "factor", width: 100, align: 'right', getName: (scope) => {
76 return (scope.row.factor !== '' && scope.row.factor !== null) ? changeNum(scope.row.factor, 2) : '-';
77 }
78 },
79 {
80 label: "启用状态", field: "bizState", type: 'tag', width: 120, align: 'center', getName: (scope) => {
81 return scope.row.bizState == 'Y' ? '启用' : '停用';
82 }, tagType: (scope) => {
83 return scope.row.bizState == 'Y' ? 'success' : 'info';
84 }
85 },
86 { label: "排序", field: "orderNum", width: 100, align: 'right', type: 'chnum' },
87 { label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME },
88 ],
89 data: tableData.value,
90 treeProps: {
91 children: 'childList',
92 hasChildren: 'hasChildren',
93 checkStrictly: false,
94 },
95 page: {
96 type: "normal",
97 rows: 0,
98 ...page.value,
99 },
100 actionInfo: {
101 label: "操作",
102 type: "btn",
103 isMore: false,
104 width: 120,
105 btns: [
106 { label: "编辑", value: "edit" },
107 { label: "删除", value: "del" }
108 ]
109 }
110 });
111
112 /** ---- 新增修改标签对话框配置------------- */
113 const catalogData = ref([]);
114 const formItems: any = ref([
115 {
116 label: '疾病名称',
117 type: 'input',
118 placeholder: '请输入',
119 field: 'diseaseName',
120 default: '',
121 maxlength: 50,
122 required: true,
123 clearable: true,
124 },
125 {
126 label: '所属上级',
127 type: 'cascader',
128 placeholder: '请选择',
129 field: "parentGuid",
130 default: [],
131 options: [],
132 showAllLevels: false,
133 props: {
134 checkStrictly: true,
135 label: "diseaseName",
136 children: 'childList',
137 value: "guid",
138 emitPath: false
139 },
140 clearable: true,
141 filterable: true,
142 },
143 {
144 label: '是否儿童疾病',
145 type: 'select',
146 placeholder: '请选择',
147 field: 'isChildrenDisease',
148 default: '',
149 options: [
150 { label: '是', value: 'Y' },
151 { label: '否', value: 'N' }
152 ],
153 required: true,
154 clearable: true,
155 },
156 {
157 label: '启用状态',
158 type: 'switch',
159 field: 'bizState',
160 default: 'Y',
161 placeholder: '请选择',
162 activeValue: 'Y',
163 inactiveValue: 'N',
164 switchWidth: 32,
165 },
166 {
167 label: '发病率/患病率',
168 type: 'input',
169 placeholder: '请输入',
170 field: 'incidenceRate',
171 default: '',
172 maxlength: 20,
173 clearable: true,
174 required: true,
175 },
176 {
177 label: '因子',
178 type: 'input-group',
179 placeholder: '请输入',
180 field: 'in',
181 default: '',
182 children: [
183 {
184 label: '',
185 type: 'input',
186 placeholder: '请输入',
187 field: 'factor',
188 default: '',
189 inputType: 'factorNumber',
190 maxlength: 10,
191 clearable: true,
192 },
193 {
194 label: '',
195 type: 'checkbox',
196 placeholder: '下级同时修改',
197 desc: '',
198 field: 'juniorTogetherModify',
199 default: "N",
200 trueValue: 'Y',
201 falseValue: 'N',
202 }
203 ],
204 },
205 {
206 label: '排序',
207 type: 'input',
208 placeholder: '由数字组成',
209 field: 'orderNum',
210 inputType: 'integerNumber',
211 default: '',
212 maxlength: 6,
213 clearable: true,
214 },
215 ])
216 const formRules = ref({
217 diseaseName: [
218 { required: true, message: '请填写疾病名称', trigger: 'blur' }
219 ],
220 isChildrenDisease: [
221 { required: true, message: '请选择是否儿童疾病', trigger: 'change' }
222 ],
223 bizState: [
224 { required: true, message: '请选择启用状态', trigger: 'change' }
225 ],
226 incidenceRate: [
227 { requires: true, message: '请填写发病率/患病率', trigger: 'blur' },
228 {
229 validator: (rule: any, value: any, callback: any) => {
230 if (!value) {
231 callback(new Error("请填写发病率/患病率"));
232 } else {
233 if (drawerInfo.value.type.indexOf('edit') > -1 && currTableData.value.incidenceRate == value) {
234 callback();
235 return
236 }
237 let eMsg = '';
238 // 验证格式:数字、小数或分数
239 if (!/^[0-9]*\.?[0-9]*(\/[0-9]+\.?[0-9]*)?$/.test(value)) {
240 eMsg = '请输入有效的数字、小数或分数(如0.5或1/2)';
241 }
242
243 let numericValue;
244
245 // 处理分数
246 if (value.includes('/')) {
247 const parts = value.split('/');
248 if (parts.length !== 2) {
249 eMsg = '请输入有效的分数格式(如1/2)';
250 }
251
252 const numerator = parseFloat(parts[0]);
253 const denominator = parseFloat(parts[1]);
254
255 if (isNaN(numerator) || isNaN(denominator)) {
256 eMsg = '请输入有效的分数格式(如1/2)';
257 }
258
259 if (denominator === 0) {
260 eMsg = '请输入有效的分数格式(如1/2)';
261 }
262
263 numericValue = numerator / denominator;
264 } else {// 处理普通数字
265 numericValue = parseFloat(value);
266 if (isNaN(numericValue)) {
267 eMsg = '请输入有效的数字';
268 }
269 }
270
271 if (numericValue < 0) {
272 eMsg = '发病率必须大于等于0';
273 }
274
275 if (numericValue > 1) {
276 eMsg = '发病率必须小于或等于1';
277 }
278 if (eMsg) {
279 callback(new Error(eMsg));
280 } else {
281 callback();
282 }
283 }
284 },
285 trigger: "blur",
286 },
287 ]
288 });
289 const formInfo = ref({
290 type: "form",
291 title: "",
292 col: "span",
293 formInfo: {
294 id: "add-disease-form",
295 readonly: false,
296 items: formItems.value,
297 rules: formRules.value,
298 },
299 });
300
301 const drawerInfo = ref({
302 visible: false,
303 direction: "rtl",
304 size: 480,
305 header: {
306 title: "新增",
307 },
308 type: "",
309 container: {
310 contents: [
311 formInfo.value
312 ],
313 },
314 footer: {
315 visible: true,
316 btns: [
317 { type: "default", label: "取消", value: "cancel" },
318 { type: "primary", label: "保存", value: "submit" },
319 ],
320 },
321 });
322
323 // 获取所有疾病数据
324 const getDiseaseData = () => {
325 getDiseaseAll().then((res: any) => {
326 if (res.code == proxy.$passCode) {
327 const data = res.data || [];
328 catalogData.value = JSON.parse(JSON.stringify(data));
329 }
330 })
331 }
332
333 const toSearch = (val: any, clear: boolean = false) => {
334 page.value.curr = 1;
335 if (clear) {
336 searchItemList.value.map((item) => (item.default = ""));
337 page.value.diseaseName = '';
338 page.value.isChildrenDisease = '';
339 } else {
340 page.value.diseaseName = val.diseaseName || '';
341 page.value.isChildrenDisease = val.isChildrenDisease || '';
342 }
343 getTableData();
344 };
345
346 const getTableData = () => {
347 tableInfo.value.loading = true;
348 getDiseaseList({
349 tenantGuid: userData.tenantGuid,
350 pageSize: page.value.limit,
351 pageIndex: page.value.curr,
352 diseaseName: page.value.diseaseName,
353 isChildrenDisease: page.value.isChildrenDisease,
354 }).then((res: any) => {
355 tableInfo.value.loading = false;
356 if (res.code == proxy.$passCode) {
357 const data = res.data || {};
358 tableData.value = data.records || [];
359 tableInfo.value.data = data.records || []
360 tableInfo.value.page.limit = data.pageSize
361 tableInfo.value.page.curr = data.pageIndex
362 tableInfo.value.page.rows = data.totalRows
363 // formItems.value[1].options = data.records || [];
364 } else {
365 proxy.$ElMessage.error(res.msg);
366 }
367 })
368 }
369
370 const tablePageChange = (info) => {
371 page.value.curr = Number(info.curr);
372 page.value.limit = Number(info.limit);
373 tableInfo.value.page.limit = page.value.limit;
374 tableInfo.value.page.curr = page.value.curr;
375 getTableData();
376 };
377
378 // 处理表格数据选中
379 const setSelect = (data, flag) => {
380 data.forEach((row: any) => {
381 selectionTreeTableRef.value.tableRef.toggleRowSelection(row, flag);
382 row.childList && setSelect(row.childList, flag);
383 });
384 }
385
386 const setCataLog = (dataArr, id, parentDisabled = false) => {
387 return dataArr.map(item => {
388 // 当前节点是否被禁选(自身匹配或父节点已禁选)
389 const isDisabled = parentDisabled || item.guid === id;
390 // 新节点基础属性
391 const newItem = {
392 ...item,
393 disabled: isDisabled
394 };
395
396 // 递归处理子节点,并传递当前节点的禁选状态
397 if (item.childList && item.childList.length > 0) {
398 newItem.childList = setCataLog(item.childList, id, isDisabled);
399 }
400 return newItem;
401 })
402 }
403
404 const tableCheckboxSelectChange = (selectRows, row) => {
405 const check = selectRows.find(item => item.guid === row.guid);
406 row.childList && setSelect(row.childList, check ? true : false);
407 }
408
409 const checkAll = ref(false)
410 const tableCheckboxAllSelectChange = (selectRows) => {
411 checkAll.value = !checkAll.value;
412 setSelect(tableData.value, checkAll.value);
413 }
414
415 const tableSelectionChange = (val) => {
416 selectRowData.value = val;
417 };
418
419 const tableBtnClick = (scope, btn) => {
420 const type = btn.value;
421 const row = scope.row;
422 currTableData.value = row;
423 if (type === 'edit') { // 编辑
424 drawerInfo.value.visible = true;
425 drawerInfo.value.header.title = '编辑疾病';
426 let cata = setCataLog(catalogData.value, row.guid);
427 formItems.value.map(item => {
428 if (item.field == 'parentGuid') {
429 item.options = cata;
430 }
431 item.default = row[item.field] || '';
432 if (item.children) {
433 item.children.map(child => {
434 child.default = row[child.field] || '';
435 if (child.field == 'factor') {
436 child.default = (row[child.field] !== '' && row[child.field] !== null) ? changeNum(row[child.field], 2) : '';
437 }
438 })
439 }
440 if (item.inputType == 'integerNumber') {
441 item.default = typeof row[item.field] == 'number' ? row[item.field] : '';
442 }
443 });
444 drawerInfo.value.type = type;
445 } else if (type === 'del') { // 删除
446 open('确定要删除该条数据吗?', 'warning');
447 }
448 };
449
450 const open = (msg, type, isBatch = false) => {
451 ElMessageBox.confirm(msg, "提示", {
452 confirmButtonText: "确定",
453 cancelButtonText: "取消",
454 type: type,
455 }).then(() => {
456 let guids = [currTableData.value.guid]
457 if (isBatch) {
458 guids = [];
459 // 处理表格数据选中
460 const setGuids = (data) => {
461 data.forEach((row: any) => {
462 guids.push(row.guid);
463 row.childList && setGuids(row.childList);
464 });
465 }
466 setGuids(selectRowData.value);
467 }
468 deleteDisease(guids).then((res: any) => {
469 if (res.code == proxy.$passCode) {
470 getTableData();
471 getDiseaseData();
472 ElMessage({
473 type: "success",
474 message: "删除成功",
475 });
476 } else {
477 proxy.$ElMessage.error(res.msg);
478 }
479 });
480 });
481 };
482
483 const addDisease = () => {
484 formItems.value.map(item => {
485 if (item.field == 'bizState') {
486 item.default = 'Y';
487 } else {
488 if (item.field == 'parentGuid') {
489 item.options = catalogData.value;
490 }
491 item.default = '';
492 if (item.children) {
493 item.children.map(child => {
494 child.default = '';
495 })
496 }
497 }
498 });
499 drawerInfo.value.header.title = '新增疾病';
500 drawerInfo.value.type = 'add';
501 drawerInfo.value.visible = true;
502
503 }
504 // 批量删除
505 const batching = (type) => {
506 if (type == 'delete') {
507 if (selectRowData.value.length == 0) {
508 ElMessage({
509 type: 'error',
510 message: '请选择需要删除的数据',
511 })
512 return
513 }
514 open("此操作将永久删除, 是否继续?", "warning", true);
515 } else if (type == 'import') {
516 const info = {
517 dictionaryGuid: ''
518 }
519 cacheStore.setCatch('uploadSetting', info)
520 router.push(
521 {
522 name: 'importFileDisease',
523 query: {
524 isfileImport: '8'
525 }
526 }
527 );
528 }
529 }
530
531
532 const drawerBtnClick = async (btn, info) => {
533 if (btn.value == "submit") {
534 drawerInfo.value.footer.btns.map((item: any) => (item.disabled = true));
535 let params = { ...info, tenantGuid: userData.tenantGuid };
536 delete params.in;
537 if (drawerInfo.value.type == "add") {
538 saveDisease(params)
539 .then((res: any) => {
540 if (res.code == proxy.$passCode) {
541 getTableData();
542 getDiseaseData();
543 ElMessage({
544 type: "success",
545 message: "新增疾病成功",
546 });
547 drawerInfo.value.visible = false;
548 } else {
549 proxy.$ElMessage.error(res.msg);
550 drawerInfo.value.footer.btns.map(
551 (item: any) => delete item.disabled
552 );
553 }
554 })
555 .catch(() => {
556 drawerInfo.value.footer.btns.map((item: any) => delete item.disabled);
557 });
558 } else {
559 params.guid = currTableData.value.guid;
560 updateDisease(params)
561 .then((res: any) => {
562 if (res.code == proxy.$passCode) {
563 getTableData();
564 getDiseaseData();
565 ElMessage({
566 type: "success",
567 message: "编辑疾病成功",
568 });
569 drawerInfo.value.visible = false;
570 } else {
571 proxy.$ElMessage.error(res.msg);
572 drawerInfo.value.footer.btns.map(
573 (item: any) => delete item.disabled
574 );
575 }
576 })
577 .catch(() => {
578 drawerInfo.value.footer.btns.map((item: any) => delete item.disabled);
579 });
580 }
581 } else {
582 drawerInfo.value.footer.btns.map((item: any) => delete item.disabled);
583 nextTick(() => {
584 drawerInfo.value.visible = false;
585 });
586 }
587 };
588
589 onBeforeMount(() => {
590 getDiseaseData();
591 });
592
593 onActivated(() => {
594
595 })
596
597 </script>
598
599 <template>
600 <div class="container_wrap">
601 <div class="table_tool_wrap">
602 <TableTools :searchItems="searchItemList" :searchId="'data-source-search'" @search="toSearch" />
603 <div class="tools_btns">
604 <el-button type="primary" @click="addDisease" v-preReClick>新增</el-button>
605 <el-button @click="batching('import')" v-preReClick>批量导入</el-button>
606 <el-button @click="batching('delete')" v-preReClick>批量删除</el-button>
607 </div>
608 </div>
609 <div class="table_panel_wrap">
610 <Table ref="selectionTreeTableRef" :tableInfo="tableInfo" @tableCheckboxSelectChange="tableCheckboxSelectChange"
611 @tableCheckboxAllSelectChange="tableCheckboxAllSelectChange" @tableSelectionChange="tableSelectionChange"
612 @tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
613 </div>
614 <Drawer :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick" />
615 </div>
616 </template>
617
618 <style lang="scss" scoped>
619 .table_tool_wrap {
620 width: 100%;
621 height: 84px !important;
622 padding: 0 8px;
623
624 .tools_btns {
625 padding: 0px 0 0;
626 }
627 }
628
629 .table_panel_wrap {
630 width: 100%;
631 height: calc(100% - 84px);
632 padding: 0px 8px 0;
633 }
634 </style>
1 <route lang="yaml">
2 name: priceCalculate
3 </route>
4
5 <script lang="ts" setup name="priceCalculate">
6 import { ref } from 'vue';
7 import TableTools from '@/components/Tools/table_tools.vue';
8 import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
9 import { ElMessage, ElMessageBox } from "element-plus";
10 import { CirclePlus } from "@element-plus/icons-vue";
11 import { useRouter, useRoute } from "vue-router";
12 import useUserStore from "@/store/modules/user";
13 import useDataAssetStore from "@/store/modules/dataAsset";
14 import { getAllFlowData } from '@/api/modules/queryService';
15 import { changeNum } from "@/utils/common";
16 import {
17 getDiseaseAll,
18 getPriceList,
19 deletePrice,
20 } from '@/api/modules/dataPricing';
21
22 const router = useRouter();
23 const userStore = useUserStore()
24 const assetStore = useDataAssetStore();
25 const userData = JSON.parse(userStore.userData)
26 const { proxy } = getCurrentInstance() as any;
27
28 const searchItemList = ref([
29 {
30 type: "input",
31 label: "",
32 field: "dataResourceName",
33 default: "",
34 placeholder: "数据资源名称",
35 clearable: true,
36 },
37 {
38 type: "cascader",
39 label: "",
40 field: "diseaseName",
41 placeholder: "疾病名称",
42 default: "",
43 options: [],
44 showAllLevels: false,
45 props: {
46 checkStrictly: true,
47 label: "diseaseName",
48 value: "guid",
49 children: 'childList',
50 emitPath: false
51 },
52 filterable: true,
53 clearable: true,
54 }
55 ]);
56
57 const typeMap: any = ref({});
58 const selectRowData: any = ref([])
59 const currTableData: any = ref({});
60 const page: any = ref({
61 ...commonPageConfig,
62 dataResourceName: '',
63 diseaseName: ''
64 });
65 const tableField: any = ref([
66 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
67 { label: "定价模型名称", field: "modelName", width: 200 },
68 { label: "数据资源名称", field: "dataResourceName", width: 200 },
69 { label: "疾病名称", field: "diseaseName", width: 200 },
70 {
71 label: "交易价格(元)", field: "dataTransactionPrice", width: 120, align: 'right', getName: (scope) => {
72 return scope.row.dataTransactionPrice ? changeNum(parseFloat(scope.row.dataTransactionPrice), 2) : '-';
73 }
74 },
75 { label: "创建人", field: "createUserName", width: 120 },
76 { label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME },
77 ]);
78 const tableInfo = ref({
79 id: 'api-data-table',
80 rowKey: 'guid',
81 loading: false,
82 fields: tableField.value,
83 data: [],
84 page: {
85 type: "normal",
86 rows: 0,
87 ...page.value,
88 },
89 actionInfo: {
90 label: "操作",
91 type: "btn",
92 isMore: false,
93 width: 120,
94 btns: [
95 { label: "编辑", value: "edit" },
96 { label: "删除", value: "del" }
97 ]
98 }
99 });
100
101 const setTableField = () => {
102 tableField.value.splice(4, 0, {
103 label: "交易用途", field: "dataUsage", width: 120, getName: (scope) => {
104 return typeMap.value['dataUsage'].find((item) => item.value == scope.row['dataUsage'])?.label || '-';
105 }
106 });
107 tableInfo.value.fields = tableField.value;
108 }
109
110 // 获取所有疾病数据
111 const getDiseaseData = () => {
112 getDiseaseAll().then((res: any) => {
113 if (res.code == proxy.$passCode) {
114 const data = res.data || [];
115 searchItemList.value[1].options = data;
116 }
117 })
118 }
119
120 // 获取数据字典
121 const getDataType = (dictType, fieldName) => {
122 getAllFlowData({ dictType }).then((res: any) => {
123 if (res.code == proxy.$passCode) {
124 const data = res.data || [];
125 typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
126 setTableField()
127 } else {
128 proxy.$ElMessage.error(res.msg);
129 }
130 })
131 }
132
133 const toSearch = (val: any, clear: boolean = false) => {
134 page.value.curr = 1;
135 if (clear) {
136 searchItemList.value.map((item) => (item.default = ""));
137 page.value.dataResourceName = ''
138 page.value.diseaseName = '';
139 } else {
140 page.value.dataResourceName = val.dataResourceName || '';
141 page.value.diseaseName = val.diseaseName || '';
142 }
143 getTableData();
144 };
145
146 const getTableData = () => {
147 tableInfo.value.loading = true;
148 getPriceList({
149 pageSize: page.value.limit,
150 pageIndex: page.value.curr,
151 dataResourceName: page.value.dataResourceName,
152 diseaseName: page.value.diseaseName,
153 }).then((res: any) => {
154 tableInfo.value.loading = false;
155 if (res.code == proxy.$passCode) {
156 const data = res.data || {};
157 tableInfo.value.data = data.records || [];
158 tableInfo.value.page.limit = data.pageSize
159 tableInfo.value.page.curr = data.pageIndex
160 tableInfo.value.page.rows = data.totalRows
161 } else {
162 proxy.$ElMessage.error(res.msg);
163 }
164 })
165 }
166
167 const tablePageChange = (info) => {
168 page.value.curr = Number(info.curr);
169 page.value.limit = Number(info.limit);
170 tableInfo.value.page.limit = page.value.limit;
171 tableInfo.value.page.curr = page.value.curr;
172 getTableData();
173 };
174
175 const tableBtnClick = (scope, btn) => {
176 const type = btn.value;
177 const row = scope.row;
178 currTableData.value = row;
179 if (type === 'edit') { // 编辑
180 router.push(
181 {
182 name: 'calculateConfig',
183 query: {
184 guid: row.guid,
185 name: row.modelName,
186 type: 'edit'
187 }
188 }
189 );
190 } else if (type === 'del') { // 删除
191 open('确定要删除该条数据吗?', 'warning');
192 }
193 };
194
195 const open = (msg, type, isBatch = false) => {
196 ElMessageBox.confirm(msg, "提示", {
197 confirmButtonText: "确定",
198 cancelButtonText: "取消",
199 type: type,
200 }).then(() => {
201 let guids = [currTableData.value.guid]
202 if (isBatch) {
203 guids = selectRowData.value
204 }
205 deletePrice(guids).then((res: any) => {
206 if (res.code == proxy.$passCode) {
207 getTableData();
208 ElMessage({
209 type: "success",
210 message: "删除成功",
211 });
212 } else {
213 proxy.$ElMessage.error(res.msg);
214 }
215 });
216 });
217 };
218
219 const addDisease = () => {
220 router.push(
221 {
222 name: 'calculateConfig',
223 query: {
224 type: 'create'
225 }
226 }
227 );
228 }
229
230 onBeforeMount(() => {
231 getDiseaseData();
232 getDataType('数据用途', 'dataUsage')
233 });
234
235 onActivated(() => {
236 if (assetStore.isRefresh) {//如果是首次加载,则不需要调用
237 toSearch(null, true);
238 assetStore.set(false);
239 }
240 })
241
242 </script>
243
244 <template>
245 <div class="container_wrap">
246 <div class="table_tool_wrap">
247 <TableTools :searchItems="searchItemList" :searchId="'data-source-search'" @search="toSearch" />
248 <div class="tools_btns">
249 <el-button type="primary" @click="addDisease" v-preReClick>新增</el-button>
250 </div>
251 </div>
252 <div class="table_panel_wrap">
253 <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
254 </div>
255 </div>
256 </template>
257
258 <style lang="scss" scoped>
259 .table_tool_wrap {
260 width: 100%;
261 height: 84px !important;
262 padding: 0 8px;
263
264 .tools_btns {
265 padding: 0px 0 0;
266 }
267 }
268
269 .table_panel_wrap {
270 width: 100%;
271 height: calc(100% - 84px);
272 padding: 0px 8px 0;
273 }
274 </style>
1 <route lang="yaml">
2 name: priceConfig
3 </route>
4
5 <script lang="ts" setup name="priceConfig">
6 import { ref } from 'vue';
7 import TableTools from '@/components/Tools/table_tools.vue';
8 import { TableColumnWidth, commonPageConfig } from '@/utils/enum';
9 import { ElMessage, ElMessageBox } from "element-plus";
10 import { CirclePlus } from "@element-plus/icons-vue";
11 import { useRouter, useRoute } from "vue-router";
12 import useUserStore from "@/store/modules/user";
13 import useDataAssetStore from "@/store/modules/dataAsset";
14 import { getAllFlowData } from '@/api/modules/queryService';
15 import {
16 getConfigureList,
17 deleteConfigure,
18 getConfigureDetail,
19 addCopyConfigure
20 } from '@/api/modules/dataPricing';
21
22 const router = useRouter();
23 const userStore = useUserStore()
24 const assetStore = useDataAssetStore();
25 const userData = JSON.parse(userStore.userData)
26 const { proxy } = getCurrentInstance() as any;
27
28 const searchItemList = ref([
29 {
30 type: "input",
31 label: "",
32 field: "modelName",
33 default: "",
34 placeholder: "定价模型名称",
35 clearable: true,
36 },
37 {
38 type: "select",
39 label: "",
40 field: "institutionType",
41 placeholder: "机构类型",
42 default: "",
43 options: [],
44 clearable: true,
45 }
46 ]);
47
48 const typeMap = ref({});
49 const tableData: any = ref([]);
50 const selectRowData: any = ref([])
51 const currTableData: any = ref({});
52 const page: any = ref({
53 ...commonPageConfig,
54 modelName: '',
55 institutionType: ''
56 });
57 const tableInfo = ref({
58 id: 'api-data-table',
59 rowKey: 'guid',
60 loading: false,
61 fields: [
62 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
63 { label: "定价模型名称", field: "modelName", width: 200 },
64 {
65 label: "机构类型", field: "institutionType", width: 120, getName: (scope) => {
66 return filterVal(scope.row.institutionType, 'institutionType')
67 }
68 },
69 {
70 label: "启用状态", field: "bizState", type: 'tag', width: 120, align: 'center', getName: (scope) => {
71 return scope.row.bizState == 'Y' ? '启用' : '停用';
72 }, tagType: (scope) => {
73 return scope.row.bizState == 'Y' ? 'success' : 'info';
74 }
75 },
76 { label: "创建人", field: "createUserName", width: 120 },
77 { label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME },
78 ],
79 data: tableData.value,
80 page: {
81 type: "normal",
82 rows: 0,
83 ...page.value,
84 },
85 actionInfo: {
86 label: "操作",
87 type: "btn",
88 isMore: false,
89 width: 160,
90 btns: [
91 { label: "编辑", value: "edit" },
92 { label: "复制", value: "copy" },
93 { label: "删除", value: "del" }
94 ]
95 }
96 });
97
98 // 过滤
99 const filterVal = (val, name) => {
100 if (typeMap.value[name]) {
101 const data = typeMap.value[name].find(item => item.value == val);
102 return data?.label || '--';
103 }
104 };
105
106 // 获取需求类型
107 const getDataType = (dictType, fieldName) => {
108 getAllFlowData({ dictType }).then((res: any) => {
109 if (res.code == proxy.$passCode) {
110 const data = res.data || [];
111 typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
112 let item = searchItemList.value.find(item => item.field == fieldName);
113 item && (item.options = data);
114 } else {
115 proxy.$ElMessage.error(res.msg);
116 }
117 })
118 }
119
120 const toSearch = (val: any, clear: boolean = false) => {
121 page.value.curr = 1;
122 if (clear) {
123 searchItemList.value.map((item) => (item.default = ""));
124 page.value.modelName = '';
125 page.value.institutionType = '';
126 } else {
127 page.value.modelName = val.modelName || '';
128 page.value.institutionType = val.institutionType || '';
129 }
130 getTableData();
131 };
132
133 const getTableData = () => {
134 tableInfo.value.loading = true;
135 getConfigureList({
136 pageSize: page.value.limit,
137 pageIndex: page.value.curr,
138 modelName: page.value.modelName,
139 institutionType: page.value.institutionType,
140 }).then((res: any) => {
141 tableInfo.value.loading = false;
142 if (res.code == proxy.$passCode) {
143 const data = res.data || {};
144 tableInfo.value.data = data.records || []
145 tableInfo.value.page.limit = data.pageSize
146 tableInfo.value.page.curr = data.pageIndex
147 tableInfo.value.page.rows = data.totalRows
148 } else {
149 proxy.$ElMessage.error(res.msg);
150 }
151 })
152 }
153
154 const tablePageChange = (info) => {
155 page.value.curr = Number(info.curr);
156 page.value.limit = Number(info.limit);
157 tableInfo.value.page.limit = page.value.limit;
158 tableInfo.value.page.curr = page.value.curr;
159 getTableData();
160 };
161
162 const tableBtnClick = (scope, btn) => {
163 const type = btn.value;
164 const row = scope.row;
165 currTableData.value = row;
166 if (type === 'edit') { // 编辑
167 router.push(
168 {
169 name: 'priceModel',
170 query: {
171 guid: row.guid,
172 name: row.modelName,
173 type
174 }
175 }
176 );
177 } else if (type == 'copy') { //复制
178 ElMessageBox.confirm('是否确定复制该模型?', "提示", {
179 confirmButtonText: "确定",
180 cancelButtonText: "取消",
181 type: 'warning',
182 }).then(() => {
183 getConfigureDetail({ guid: row.guid }).then((res: any) => {
184 if (res.code == proxy.$passCode) {
185 const data = res.data || {};
186 let params = { ...data };
187 delete params.createTime;
188 delete params.createUserName;
189 delete params.pricingDimensionalityRSVOS;
190 const pricingDimensionalityRSVOS = data.pricingDimensionalityRSVOS || [];
191 pricingDimensionalityRSVOS.map(p => {
192 p.pricingTargetRQVOS = p.pricingTargetRSVOS || [];
193 delete p.pricingTargetRSVOS;
194 });
195 params.pricingDimensionalityRQVOS = pricingDimensionalityRSVOS;
196 addCopyConfigure(params).then((res: any) => {
197 if (res.code == proxy.$passCode) {
198 ElMessage({
199 type: "success",
200 message: "复制定价配置成功",
201 });
202 getTableData();
203 } else {
204 proxy.$ElMessage.error(res.msg);
205 }
206 }).catch(() => {
207
208 });
209 }
210 }).catch(() => {
211
212 })
213 });
214 } else if (type === 'del') { // 删除
215 open('确定要删除该条数据吗?', 'warning');
216 }
217 };
218
219 const open = (msg, type, isBatch = false) => {
220 ElMessageBox.confirm(msg, "提示", {
221 confirmButtonText: "确定",
222 cancelButtonText: "取消",
223 type: type,
224 }).then(() => {
225 let guids = [currTableData.value.guid]
226 if (isBatch) {
227 guids = selectRowData.value
228 }
229 deleteConfigure(guids).then((res: any) => {
230 if (res.code == proxy.$passCode) {
231 getTableData();
232 ElMessage({
233 type: "success",
234 message: "删除成功",
235 });
236 } else {
237 proxy.$ElMessage.error(res.msg);
238 }
239 });
240 });
241 };
242
243 const addDisease = () => {
244 router.push(
245 {
246 name: 'priceModel',
247 query: {
248 type: 'create'
249 }
250 }
251 );
252 }
253
254 onBeforeMount(() => {
255 getDataType('机构类型', 'institutionType')
256 });
257
258 onActivated(() => {
259 if (assetStore.isRefresh) {//如果是首次加载,则不需要调用
260 toSearch(null, true);
261 assetStore.set(false);
262 }
263 })
264
265 </script>
266
267 <template>
268 <div class="container_wrap">
269 <div class="table_tool_wrap">
270 <TableTools :searchItems="searchItemList" :searchId="'data-source-search'" @search="toSearch" />
271 <div class="tools_btns">
272 <el-button type="primary" @click="addDisease" v-preReClick>新增</el-button>
273 </div>
274 </div>
275 <div class="table_panel_wrap">
276 <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" @tableBtnClick="tableBtnClick" />
277 </div>
278 </div>
279 </template>
280
281 <style lang="scss" scoped>
282 .table_tool_wrap {
283 width: 100%;
284 height: 84px !important;
285 padding: 0 8px;
286
287 .tools_btns {
288 padding: 0px 0 0;
289 }
290 }
291
292 .table_panel_wrap {
293 width: 100%;
294 height: calc(100% - 84px);
295 padding: 0px 8px 0;
296 }
297 </style>
1 <route lang="yaml">
2 name: priceModel
3 </route>
4
5 <script lang="ts" setup name="priceModel">
6 import { ref, onMounted } from "vue";
7 import { useRouter, useRoute } from "vue-router";
8 import { ElMessage, ElMessageBox, rowProps } from "element-plus";
9 import { Plus, CirclePlus, Guide } from "@element-plus/icons-vue";
10 import useUserStore from "@/store/modules/user";
11 import useDataAssetStore from "@/store/modules/dataAsset";
12 import { getAllFlowData } from '@/api/modules/queryService';
13 import { changeNum } from "@/utils/common";
14 import {
15 getDemandTreeList,
16 getConfigureDetail,
17 saveConfigure,
18 updateConfigure
19 } from '@/api/modules/dataPricing';
20
21 const { proxy } = getCurrentInstance() as any;
22 const router = useRouter();
23 const route = useRoute();
24 const userStore = useUserStore();
25 const assetStore = useDataAssetStore();
26 const fullPath = route.fullPath;
27 const userData = JSON.parse(localStorage.userData);
28 const guid = route.query.guid;
29 const modelName = route.query.name;
30
31 const loading = ref(false);
32 const flowDetail: any = ref({});
33 const typeMap: any = ref({});
34 const expand1 = ref(true)
35 const expand2 = ref(true)
36
37 // 基础设置
38 const baseConfigFormRef = ref();
39 const baseConfigFormItems: any = ref([
40 {
41 label: '模型名称',
42 type: 'input',
43 placeholder: '请输入',
44 field: 'modelName',
45 default: '',
46 clearable: true,
47 required: true
48 },
49 {
50 label: '机构类型',
51 type: 'select',
52 placeholder: '请选择',
53 field: 'institutionType',
54 default: '',
55 options: [],
56 clearable: true,
57 required: true,
58 },
59 {
60 label: '启用状态',
61 type: 'switch',
62 field: 'bizState',
63 default: 'Y',
64 placeholder: '请选择',
65 activeValue: 'Y',
66 inactiveValue: 'N',
67 switchWidth: 32,
68 },
69 {
70 label: '模型公式',
71 type: 'input',
72 placeholder: '请输入',
73 field: 'modelFormula',
74 default: '',
75 maxlength: 500,
76 clearable: true,
77 required: true,
78 block: true,
79 },
80 ])
81 const baseConfigFormRules = ref({
82 modelName: [
83 { required: true, trigger: 'blur', message: "请填写模型名称" }
84 ],
85 institutionType: [
86 { required: true, trigger: 'change', message: "请选择机构类型" }
87 ],
88 modelFormula: [
89 { required: true, trigger: 'change', message: "请填写模型公式" },
90 {
91 validator: (rule, value, callback) => {
92 validateFormula(value, callback);
93 },
94 trigger: 'blur'
95 }
96 ],
97 });
98 const baseConfigForm = ref({
99 items: baseConfigFormItems.value,
100 rules: baseConfigFormRules.value,
101 })
102
103
104 // 校验模型公式
105 const validateFormula = (value, callback) => {
106 const baseConfigFormObj = baseConfigFormRef.value;
107 const baseConfigFormInfo = baseConfigFormObj.formInline;
108 const formula = value || baseConfigFormInfo.modelFormula;
109
110 if (!formula.trim()) {
111 callback(new Error('请输入模型公式'));
112 return;
113 }
114
115 const dimensions = signatoryTableList.value.map(s => s.dimensionalityName);
116
117 /* 第一步:检查维度是否存在 */
118 const dimMatches = [...formula.matchAll(/"([^"]+)"/g)];
119 for (const match of dimMatches) {
120 const dim = match[1]; // 获取引号内的内容
121 if (!dimensions.includes(dim)) {
122 callback(new Error(`维度"${dim}"不存在`));
123 return;
124 }
125 }
126
127 /* 第二步:检查维度间是否有运算符 */
128 if (/"([^"]+)"\s*"([^"]+)"/.test(formula)) {
129 callback(new Error('维度之间必须使用运算符连接'));
130 return;
131 }
132
133 /* 第三步:检查运算符使用是否合法 */
134 // 检查连续运算符
135 if (/([+\-*\/^]\s*){2,}/.test(formula.replace(/\s+/g, ''))) {
136 callback(new Error('不允许连续使用运算符'));
137 return;
138 }
139 // 检查运算符后无参数
140 if (/[+\-*\/^]\s*["(]*(\)|$)/.test(formula.replace(/\s+/g, ''))) {
141 callback(new Error('公式不完整'));
142 return;
143 }
144
145 /* 第四步:替换变量并校验公式结构 */
146 const dimMap = new Map();
147 dimMatches.forEach((match, i) => {
148 dimMap.set(match[0], `a${i + 1}`); // 创建维度到变量的映射
149 });
150
151 let formulaWithVars = formula;
152 dimMap.forEach((varName, dim) => {
153 formulaWithVars = formulaWithVars.split(dim).join(varName);
154 });
155
156 // 基础字符检查
157 const validChars = /^[\sa\d+\-*\/^().]+$/;
158 if (!validChars.test(formulaWithVars)) {
159 callback(new Error('公式包含非法字符'));
160 return;
161 }
162
163 /* 增强的括号匹配检查(支持多种括号类型)*/
164 const bracketPairs = {
165 '(': ')',
166 '[': ']',
167 '{': '}',
168 '<': '>'
169 };
170 const stack = [];
171
172 for (const char of formulaWithVars) {
173 if (bracketPairs[char]) {
174 stack.push(char);
175 } else if (Object.values(bracketPairs).includes(char)) {
176 if (stack.length === 0 || bracketPairs[stack.pop()] !== char) {
177 callback(new Error('括号使用不匹配或未正确闭合'));
178 return;
179 }
180 }
181 }
182
183 if (stack.length > 0) {
184 callback(new Error('括号使用不匹配或未正确闭合'));
185 return;
186 }
187
188 callback(); // 所有检查通过
189 };
190
191 // 维度设置
192 const currTableData: any = ref({});
193 const currSignatoryData: any = ref({});
194 const signatoryTableList: any = ref([]);
195 const pricingDimensionality: any = ref([]);
196
197 // 维度表单
198 const signatoryFormItems: any = ref([
199 {
200 label: '维度名称',
201 type: 'input',
202 placeholder: '请输入',
203 field: 'dimensionalityName',
204 default: '',
205 maxlength: 50,
206 clearable: true,
207 required: true,
208 },
209 {
210 label: '计算公式',
211 type: 'select',
212 placeholder: '请选择',
213 field: 'computationalFormula',
214 default: '',
215 options: [],
216 clearable: true,
217 required: true,
218 },
219 {
220 label: '权重(%)',
221 type: 'input',
222 placeholder: '请输入',
223 field: 'weight',
224 default: '',
225 inputType: 'factorNumber',
226 maxlength: 10,
227 clearable: true,
228 },
229 {
230 label: '启用状态',
231 type: 'switch',
232 field: 'invocationStatus',
233 default: 'Y',
234 placeholder: '请选择',
235 activeValue: 'Y',
236 inactiveValue: 'N',
237 switchWidth: 32,
238 },
239 {
240 label: '维度说明',
241 type: 'textarea',
242 placeholder: '请输入',
243 field: 'remark',
244 default: '',
245 maxlength: 500,
246 clearable: true,
247 block: true,
248 },
249 {
250 label: '自定义公式',
251 type: 'textarea',
252 placeholder: '请输入',
253 field: 'customize',
254 default: '',
255 maxlength: 500,
256 clearable: true,
257 block: true,
258 visible: false,
259 },
260 ])
261 const signatoryFormRules = ref({
262 dimensionalityName: [
263 {
264 validator: (rule: any, value: any, callback: any) => {
265 if (!value) {
266 callback(new Error("请填写维度名称"));
267 } else {
268 if (drawerInfo.value.type.indexOf('edit') > -1 && currSignatoryData.value.dimensionalityName == value) {
269 callback();
270 return
271 }
272 const dimName = signatoryTableList.value.map(item => item.dimensionalityName);
273 if (dimName.indexOf(value) != -1) {
274 callback(new Error("该名称已存在"));
275 } else {
276 callback();
277 }
278 }
279 },
280 trigger: "blur",
281 },
282 ],
283 computationalFormula: [
284 { required: true, message: '请选择计算公式', trigger: 'change' }
285 ],
286 });
287
288 // 指标表单
289 const treeData = ref([]);
290 const targetFormItems: any = ref([
291 {
292 label: '指标名称',
293 type: 'input',
294 placeholder: '请输入',
295 field: 'targetName',
296 default: '',
297 maxlength: 50,
298 clearable: true,
299 required: true,
300 },
301 {
302 label: '指标类型',
303 type: 'select',
304 placeholder: '请选择',
305 field: 'targetType',
306 default: '',
307 options: [],
308 clearable: true,
309 required: true,
310 },
311 {
312 label: '权重(%)',
313 type: 'input',
314 placeholder: '请输入',
315 field: 'weight',
316 default: '',
317 inputType: 'factorNumber',
318 maxlength: 10,
319 clearable: true,
320 required: true,
321 visible: false,
322 },
323 {
324 label: '是否展示',
325 type: 'switch',
326 field: 'isShow',
327 default: 'Y',
328 placeholder: '请选择',
329 activeValue: 'Y',
330 inactiveValue: 'N',
331 switchWidth: 32,
332 disabled: false,
333 style: {
334 width: 'calc(25% - 4px)',
335 'margin-left': '8px'
336 }
337 },
338 {
339 label: '是否输入参数',
340 type: 'switch',
341 field: 'isInputParameter',
342 default: 'Y',
343 placeholder: '请选择',
344 activeValue: 'Y',
345 inactiveValue: 'N',
346 switchWidth: 32,
347 style: {
348 width: 'calc(25% - 4px)'
349 }
350 },
351 {
352 label: '默认值',
353 type: 'input',
354 placeholder: '请输入',
355 field: 'defaultValue',
356 default: '',
357 inputType: 'factorNumber',
358 maxlength: 18,
359 clearable: true,
360 required: false
361 },
362 {
363 label: '选择字典',
364 type: 'select',
365 placeholder: '请选择',
366 field: 'dictionaryType',
367 default: '',
368 options: [],
369 clearable: true,
370 required: true,
371 visible: false,
372 },
373 {
374 label: '功能名称',
375 type: 'select',
376 placeholder: '请选择',
377 field: 'functionName',
378 default: '',
379 options: [],
380 clearable: true,
381 required: true,
382 visible: false,
383 },
384 {
385 label: '选择需求表',
386 type: 'select',
387 placeholder: '请选择',
388 field: 'demandTableGuid',
389 default: '',
390 options: [],
391 props: {
392 label: "menuName",
393 value: "guid",
394 },
395 filterable: true,
396 clearable: true,
397 required: true,
398 visible: false,
399 },
400 {
401 label: '公式设置',
402 type: 'textarea',
403 placeholder: '请输入',
404 field: 'formulaSetting',
405 default: '',
406 resize: 'vertical',
407 maxlength: 500,
408 clearable: true,
409 block: true,
410 required: true,
411 },
412 ])
413 const targetFormRules = ref({
414 targetName: [
415 {
416 validator: (rule: any, value: any, callback: any) => {
417 if (!value) {
418 callback(new Error("请填写指标名称"));
419 } else {
420 if (drawerInfo.value.type.indexOf('edit') > -1 && currTableData.value.targetName == value) {
421 callback();
422 return
423 }
424 const dimName = currSignatoryData.value.tableInfo.data.map(item => item.targetName);
425 if (dimName.indexOf(value) != -1) {
426 callback(new Error("该名称已存在"));
427 } else {
428 callback();
429 }
430 }
431 },
432 trigger: "blur",
433 },
434 ],
435 targetType: [
436 { required: true, message: '请选择指标类型', trigger: 'change' }
437 ],
438 dictionaryType: [
439 { required: true, message: '请选择数据字典', trigger: 'change' }
440 ],
441 functionName: [
442 { required: true, message: '请选择功能名称', trigger: 'change' }
443 ],
444 formulaSetting: [
445 { required: true, message: '请填写公式设置', trigger: 'blur' }
446 ],
447 demandTableGuid: [
448 { required: true, message: '请选择需求表', trigger: 'change' }
449 ],
450 weight: [
451 { required: true, message: '请填写权重', trigger: 'change' },
452 {
453 validator: (rule: any, value: any, callback: any) => {
454 if (!value) {
455 callback(new Error("请填写权重"));
456 } else {
457 if (drawerInfo.value.type.indexOf('edit') > -1 && currTableData.value.weight == value) {
458 callback();
459 return
460 }
461 setTimeout(() => {
462 let formula: any = [];
463 if (drawerInfo.value.type.indexOf('edit') > -1) {
464 formula = currSignatoryData.value.tableInfo.data.map(p => p.targetName == currTableData.value.targetName ? value : p.weight || 0);
465 } else {
466 formula = currSignatoryData.value.tableInfo.data.map(p => p.weight || 0);
467 formula.push(value);
468 }
469 const isOver = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) + parseFloat(currentValue), 0); // 初始值为0
470 if (isOver > 100) {
471 callback(new Error("该维度权重和已超过100%,请重新填写"));
472 } else {
473 callback();
474 }
475 }, 200);
476 }
477 },
478 trigger: "blur",
479 },
480 ],
481 defaultValue: [
482 { required: true, message: '请填写默认值', trigger: 'blur' }
483 ],
484 });
485
486 const dictionaryType = ref('');
487 const showFactorTable = ref(false);
488 const mergeRowCount: any = ref({});
489 const rowCount = {
490 level: { index: 0, rowspan: [] },
491 };
492 const mergeTableData = [
493 { id: '1-1', level: '一级', grade: '甲等', factor: '' },
494 { id: '1-2', level: '一级', grade: '乙等', factor: '' },
495 { id: '1-3', level: '一级', grade: '丙等', factor: '' },
496 { id: '2-1', level: '二级', grade: '甲等', factor: '' },
497 { id: '2-2', level: '二级', grade: '乙等', factor: '' },
498 { id: '2-3', level: '二级', grade: '丙等', factor: '' },
499 { id: '3-1', level: '三级', grade: '甲等', factor: '' },
500 { id: '3-2', level: '三级', grade: '乙等', factor: '' },
501 { id: '3-3', level: '三级', grade: '丙等', factor: '' },
502 ];
503 const tableData: any = ref([]);
504
505 // 抽屉内容
506 const contents = ref({
507 target: {
508 type: "form",
509 title: "",
510 col: "span",
511 formInfo: {
512 id: "add-dict-form",
513 readonly: false,
514 items: targetFormItems.value,
515 rules: targetFormRules.value,
516 },
517 },
518 signatory: {
519 type: "form",
520 title: "",
521 col: "span",
522 formInfo: {
523 id: "add-dict-form",
524 readonly: false,
525 items: signatoryFormItems.value,
526 rules: signatoryFormRules.value,
527 },
528 }
529 })
530 const drawerInfo: any = ref({
531 visible: false,
532 direction: "rtl",
533 size: 480,
534 header: {
535 title: "新增",
536 },
537 type: "",
538 container: {
539 contents: [],
540 },
541 footer: {
542 visible: true,
543 btns: [
544 { type: "default", label: "取消", value: "cancel" },
545 { type: "primary", label: "保存", value: "submit" },
546 ],
547 },
548 });
549
550 const setTableField = (data) => {
551 tableData.value = [];
552 const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == dictionaryType.value)?.label || '';
553 const dictionaryJson = dictionaryName && dictionaryName == currTableData.value.dictionaryName ? (currTableData.value.dictionaryJson || []) : [];
554 if (dictionaryType.value == '1') {
555 const tData = JSON.parse(JSON.stringify(mergeTableData));
556 if (dictionaryJson.length) {
557 tData.map((item, i) => {
558 item.factor = dictionaryJson[i]?.value || '';
559 })
560 }
561 tableData.value = tData;
562 getMergeRow();
563 } else {
564 if (dictionaryJson.length) {
565 dictionaryJson.map(item => {
566 tableData.value.push({
567 label: item.name,
568 factor: item.value || '',
569 })
570 })
571 } else {
572 data.map((item: any) => {
573 tableData.value.push({
574 label: item.label,
575 factor: '',
576 })
577 })
578 }
579 }
580 }
581
582 const getDictionaryRuleData = () => {
583 const dictType: any = typeMap.value['dictionaryType'].find(item => item.value == dictionaryType.value)?.label || '';
584 getAllFlowData({ dictType }).then((res: any) => {
585 if (res.code == proxy.$passCode) {
586 const data = res.data || [];
587 setTableField(data);
588 } else {
589 proxy.$ElMessage.error(res.msg);
590 }
591 })
592 }
593
594 // 设置表单信息
595 const setFormItems = (info = null, type = '') => {
596 const datas: any = info || flowDetail.value || {};
597 if (type == 'target') {
598 targetFormItems.value.map(item => {
599 item.default = datas[item.field] || '';
600 if (item.field == 'functionName') {
601 item.visible = datas.targetType == '2'
602 } else if (item.field == 'dictionaryType') {
603 dictionaryType.value = datas[item.field] || '';
604 item.visible = datas.targetType == '3'
605 showFactorTable.value = datas.targetType == '3' && datas[item.field]
606 showFactorTable.value && getDictionaryRuleData();
607 } else if (item.field == 'formulaSetting') {
608 item.visible = datas.targetType == '2' && datas.functionName == '2'
609 } else if (item.field == 'demandTableGuid') {
610 item.visible = datas.targetType == '2' && datas.functionName == '3'
611 } else if (item.field == 'isShow' || item.field == 'isInputParameter') {
612 item.default = datas[item.field] || 'Y';
613 } else if (item.field == 'isShow') {
614 item.disabled = datas.targetType == '1'
615 } else if (item.field == 'defaultValue') {
616 item.required = datas.targetType == '1';
617 targetFormRules.value.defaultValue[0].required = datas.targetType == '1';
618 }
619 })
620 } else if (type == 'signatory') {
621 signatoryFormItems.value.map(item => {
622 if (item.field == 'invocationStatus') {
623 item.default = datas[item.field] || 'Y';
624 } else if (item.field == 'customize') {
625 item.default = datas[item.field] || '';
626 item.visible = datas.computationalFormula == 'custom'
627 } else {
628 item.default = datas[item.field] || '';
629 }
630 })
631 } else {
632 baseConfigFormItems.value.map(item => {
633 if (item.field == 'modelFormula') {
634 item.default = datas[item.field] || '';
635 } else {
636 item.default = datas[item.field] || '';
637 }
638 })
639 }
640 }
641
642 // 过滤
643 const setVal = (type, row) => {
644 let formulaText = '';
645 if (!row[type] || row[type] == 'custom') {
646 formulaText = row.customize;
647 } else {
648 const pricingTargetData = row.pricingTargetRSVOS || row.tableInfo?.data || [];
649 let formula: any = [];
650 if (pricingTargetData.length) {
651 pricingTargetData.map(item => {
652 if (row[type] == '1') {
653 formula.push(`${item.targetName}*${item.weight}%`)
654 } else {
655 formula.push(`${item.targetName}`)
656 }
657 })
658 if (row[type] == '3') {
659 formulaText = `公式=${formula.join('*')}`;
660 } else {
661 formulaText = `公式=${formula.join('+')}`;
662 }
663 }
664 }
665 row.customize = formulaText;
666 pricingDimensionality.value.find(item => {
667 if (item.dimensionalityName == row.dimensionalityName) {
668 item.customize = formulaText;
669 }
670 })
671 return formulaText;
672 };
673
674 /**
675 * 传入多个promise对象,当全部结束时取消Loading
676 * @param promises 传入多个promise对象,当全部结束时取消Loading
677 */
678 const promiseList = (...promises: Promise<void>[]) => {
679 // loading方法全局封装成一个组件
680 loading.value = true;
681 try {
682 Promise.all(promises).then(res => {
683 if (guid) {
684 setFormItems();
685 setSignatoryData();
686 }
687 });
688 } catch (e) {
689 loading.value = false;
690 } finally {
691 loading.value = false;
692 }
693 };
694
695 // 获取数据字典
696 const getDataType = (dictType, fieldName) => {
697 return getAllFlowData({ dictType }).then((res: any) => {
698 if (res.code == proxy.$passCode) {
699 const data = res.data || [];
700 if (fieldName == 'computationalFormula') {
701 data.push({ label: '自定义', value: 'custom' })
702 typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
703 let item = signatoryFormItems.value.find(item => item.field == fieldName);
704 item && (item.options = data);
705 } else {
706 typeMap.value[fieldName] = JSON.parse(JSON.stringify(data));
707 if (fieldName == 'institutionType') {
708 let item = baseConfigFormItems.value.find(item => item.field == fieldName);
709 item && (item.options = data);
710 } else {
711 let item = targetFormItems.value.find(item => item.field == fieldName);
712 item && (item.options = data);
713 }
714 }
715 } else {
716 proxy.$ElMessage.error(res.msg);
717 }
718 })
719 }
720
721 // 初始化维度表格信息
722 const setSignatoryTableInfo = (row, isEdit = false) => {
723 const pricingTarget = row.pricingTargetRSVOS || [];
724 const fieldList: any = [
725 { label: "指标名称", field: "targetName", width: 200 },
726 {
727 label: "指标类型", field: "targetType", width: 120, getName: (scope) => {
728 return typeMap.value['targetType'].find(item => item.value == scope.row.targetType)?.label || '--';
729 }
730 },
731 {
732 label: "默认值", field: "defaultValue", width: 180, align: 'right', getName: (scope) => {
733 return changeNum(scope.row.defaultValue, 2) || '-'
734 }
735 },
736 {
737 label: "是否展示", field: "isShow", width: 100, getName: (scope) => {
738 return scope.row.isShow == 'Y' ? '是' : '否';
739 }
740 },
741 {
742 label: "是否输入参数", field: "isInputParameter", width: 140, getName: (scope) => {
743 return scope.row.isInputParameter == 'Y' ? '是' : '否';
744 }
745 }
746 ];
747 row.computationalFormula == '1' && fieldList.splice(2, 0, { label: "权重(%)", field: "weight", width: 100 });
748 if (isEdit) {
749 const rIndex = signatoryTableList.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
750 rIndex != -1 && signatoryTableList.value.splice(rIndex, 1, { ...currSignatoryData.value, ...row, tableInfo: { ...currSignatoryData.value.tableInfo, fields: fieldList } });
751 const sIndex = pricingDimensionality.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
752 sIndex != -1 && pricingDimensionality.value.splice(sIndex, 1, { ...currSignatoryData.value, ...row, tableInfo: { ...currSignatoryData.value.tableInfo, fields: fieldList } });
753 } else {
754 const tData = {
755 id: `signatory-${signatoryTableList.value.length}`,
756 rowKey: '',
757 fields: fieldList,
758 data: pricingTarget,
759 showPage: false,
760 actionInfo: {
761 label: "操作",
762 type: "btn",
763 width: 120,
764 btns: [
765 { label: "编辑", value: "edit" },
766 { label: "删除", value: "del" }
767 ]
768 }
769 };
770 signatoryTableList.value.push({
771 ...row,
772 tableInfo: tData
773 });
774 }
775 }
776
777 // 设置维度指标数据
778 const setSignatoryData = () => {
779 const pricingData = flowDetail.value.pricingDimensionalityRSVOS || [];
780 pricingDimensionality.value = pricingData;
781 pricingData.map(item => {
782 const rows = item;
783 (() => {
784 setSignatoryTableInfo(rows);
785 })()
786 })
787 }
788
789 // 获取详情
790 const getModelDetail = () => {
791 loading.value = true;
792 getConfigureDetail({ guid }).then((res: any) => {
793 if (res.code == proxy.$passCode) {
794 const data = res.data || {};
795 flowDetail.value = data;
796 getDataTypeList()
797 }
798 }).catch(() => {
799 loading.value = false;
800 })
801 }
802
803 const getDataTypeList = () => {
804 promiseList(
805 getTreeData(),
806 getDataType('机构类型', 'institutionType'),
807 getDataType('维度计算公式', 'computationalFormula'),
808 getDataType('维度指标类型', 'targetType'),
809 getDataType('系统功能', 'functionName'),
810 getDataType('选择字典', 'dictionaryType'),
811 )
812 }
813
814 // 获取需求表树形数据
815 const getTreeData = () => {
816 return getDemandTreeList({ pageIndex: 1, pageSize: 100000, isCatalog: 'N' }).then((res: any) => {
817 if (res.code == proxy.$passCode) {
818 const data = res.data?.records || [];
819 treeData.value = JSON.parse(JSON.stringify(data));
820 const item = targetFormItems.value.find(item => item.field == 'demandTableGuid');
821 item && (item.options = data);
822 } else {
823 proxy.$ElMessage.error(res.msg);
824 }
825 })
826 }
827
828 const selectChange = async (val, row, info) => {
829 if (row.field == 'computationalFormula') {
830 setFormItems(info, 'signatory');
831 } else if (row.field == 'targetType' || row.field == 'dictionaryType' || row.field == 'functionName') {
832 const tInfo = { ...currTableData.value, ...info };
833 await setFormItems(tInfo, 'target');
834 if (row.field == 'targetType') {
835 targetFormItems.value[3].default = val == '1' ? 'N' : 'Y';
836 }
837 }
838 }
839
840 const inputChange = (val, scope, field) => {
841 let row = scope.row;
842 let strArr = val.split(".");
843 if (strArr.length > 1) {
844 let right = strArr[1];
845 if (right === "" || right.length < 3) {
846 row[field] = val = parseFloat(val || 0).toFixed(3);
847 }
848 } else {
849 row[field] = val = parseFloat(val || 0).toFixed(3);
850 }
851 }
852
853 /** 输入框输入触发事件 */
854 const inputEventChange = (val, scope, field) => {
855 let row = scope.row;
856 row[field] = row[field].toString().replace(/[^\d.]/g, "")
857 row[field] = row[field].toString().replace(/\.{2,}/g, ".")
858 row[field] = row[field].toString().replace(".", "$#$").replace(/\./g, "").replace("$#$", ".")
859 row[field] = row[field].toString().replace(/^(\-)*(\d+)\.(\d\d\d\d\d\d).*$/, "$1$2.$3")
860 row[field] = row[field].toString().replace(/^\D*(\d{0,12}(?:\.\d{0,3})?).*$/g, "$1")
861 }
862
863 // 设置表格合并下标
864 const getMergeRow = () => {
865 mergeRowCount.value = JSON.parse(JSON.stringify(rowCount));
866 let list = tableData.value;
867 for (var i = 0; i < list.length; i++) {
868 if (i === 0) {
869 //第一个数据 默认合并1行,开始位置下标默认为0
870 for (var m in mergeRowCount.value) {
871 mergeRowCount.value[m].rowspan.push(1);
872 mergeRowCount.value[m].index = 0;
873 }
874 } else {
875 // 根据拥有子级数量进行合并1
876 for (var m in mergeRowCount.value) {
877 let mergeRow = mergeRowCount.value[m];
878 if (list[i][m] && list[i][m] === list[i - 1][m]) {
879 mergeRow.rowspan[mergeRow.index] += 1;
880 mergeRow.rowspan.push(0);
881 } else {
882 mergeRow.rowspan.push(1);
883 mergeRow.index = i;
884 }
885 }
886 }
887 }
888 }
889 // 表格行合并
890 const tableSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
891 if (columnIndex === 0) {
892 const mergeInfo = mergeRowCount.value.level;
893 const rowspan = mergeInfo.rowspan[rowIndex];
894 if (rowspan > 0) {
895 return {
896 rowspan,
897 colspan: 1,
898 };
899 } else {
900 return {
901 rowspan: 0,
902 colspan: 0,
903 };
904 }
905 }
906 }
907
908 const toPath = () => {
909 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
910 assetStore.set(true);
911 router.push({
912 name: 'priceConfig',
913 })
914 }
915
916 const switchChange = (val, index) => {
917 pricingDimensionality.value[index].invocationStatus = val;
918 }
919
920 const btnClick = async (btn, row: any = null) => {
921 const type = btn.value;
922 if (type == 'dim') {
923 const baseConfigFormObj = baseConfigFormRef.value;
924 const baseConfigFormEl = baseConfigFormObj.ruleFormRef;
925 const baseConfigFormInfo = baseConfigFormObj.formInline;
926 await setFormItems(baseConfigFormInfo);
927 baseConfigFormItems.value.at(-1).default = baseConfigFormInfo.modelFormula ? `${baseConfigFormInfo.modelFormula}"${row.dimensionalityName}"` : `"${row.dimensionalityName}"`;
928 // 触发校验
929 setTimeout(() => {
930 baseConfigFormEl.validateField('modelFormula');
931 }, 200);
932 } else if (type == 'add-target') {
933 currSignatoryData.value = row;
934 currTableData.value = {};
935 const tData = currSignatoryData.value.tableInfo.data;
936 await setFormItems(null, 'target');
937 drawerInfo.value.type = type;
938 drawerInfo.value.header.title = '新增指标';
939 targetFormItems.value[2].visible = row.computationalFormula == '1';
940 targetFormItems.value.at(-2).options.map(o => {
941 const fData = tData.filter(t => t.targetType == '2' && t.functionName == '3');
942 o.disabled = fData.find(f => f.demandTableGuid == o.guid) ? true : false;
943 });
944 drawerInfo.value.container.contents[0] = contents.value.target;
945 tableData.value = [];
946 drawerInfo.value.visible = true;
947 } else if (type == 'add-signatory') {
948 currSignatoryData.value = {};
949 currTableData.value = {};
950 showFactorTable.value = false;
951 await setFormItems(null, 'signatory');
952 drawerInfo.value.type = type;
953 drawerInfo.value.header.title = '新增维度';
954 drawerInfo.value.container.contents[0] = contents.value.signatory;
955 drawerInfo.value.visible = true;
956 } else if (type == 'edit-signatory') {
957 currSignatoryData.value = row;
958 currTableData.value = {};
959 showFactorTable.value = false;
960 await setFormItems(row, 'signatory');
961 drawerInfo.value.type = type;
962 drawerInfo.value.header.title = '编辑维度';
963 drawerInfo.value.container.contents[0] = contents.value.signatory;
964 drawerInfo.value.visible = true;
965 } else if (type == 'del-signatory') {
966 currSignatoryData.value = row;
967 open('确定要删除该条维度数据吗?', 'warning', 'signatory');
968 } else if (type == 'submit') {
969 const baseConfigFormObj = baseConfigFormRef.value;
970 const baseConfigFormEl = baseConfigFormObj.ruleFormRef;
971 const baseConfigFormInfo = baseConfigFormObj.formInline;
972 baseConfigFormEl.validate((valid, errorItem) => {
973 if (valid) {
974 const pricingDimensionalityData = pricingDimensionality.value.map((item, i) => {
975 item.orderNum = i + 1;
976 const pricingTargetData = item.pricingTargetRSVOS || item.pricingTargetRQVOS || [];
977 item.pricingTargetRQVOS = pricingTargetData.map((targetItem, t) => {
978 targetItem.orderNum = t + 1;
979 delete targetItem.tableInfo;
980 return targetItem;
981 })
982 delete item.pricingTargetRSVOS;
983 return item;
984 })
985 loading.value = true;
986 let params = {
987 ...baseConfigFormInfo,
988 tenantGuid: userData.tenantGuid,
989 pricingDimensionalityRQVOS: pricingDimensionalityData,
990 };
991 if (guid) {
992 params.guid = guid
993 updateConfigure(params).then((res: any) => {
994 loading.value = false;
995 if (res.code == proxy.$passCode) {
996 ElMessage({
997 type: "success",
998 message: guid ? "编辑定价配置成功" : "新增定价配置成功",
999 });
1000 toPath()
1001 } else {
1002 proxy.$ElMessage.error(res.msg);
1003 }
1004 }).catch(() => {
1005 loading.value = false;
1006 });
1007 } else {
1008 saveConfigure(params).then((res: any) => {
1009 loading.value = false;
1010 if (res.code == proxy.$passCode) {
1011 ElMessage({
1012 type: "success",
1013 message: guid ? "编辑定价配置成功" : "新增定价配置成功",
1014 });
1015 toPath()
1016 } else {
1017 proxy.$ElMessage.error(res.msg);
1018 }
1019 }).catch(() => {
1020 loading.value = false;
1021 });
1022 }
1023 } else {
1024 expand1.value = true;
1025 var obj = Object.keys(errorItem);
1026 baseConfigFormEl.scrollToField(obj[0]);
1027 }
1028 });
1029 } else if (type == 'cancel') {
1030 ElMessageBox.confirm(
1031 "当前页面尚未保存,确定关闭吗?",
1032 "提示",
1033 {
1034 confirmButtonText: "确定",
1035 cancelButtonText: "取消",
1036 type: "warning",
1037 }
1038 ).then(() => {
1039 toPath()
1040 }).catch(() => {
1041 ElMessage({
1042 type: "info",
1043 message: "已取消",
1044 });
1045 });
1046 }
1047 }
1048
1049 const tableBtnClick = async (scope, btn, lData: any = null) => {
1050 const type = btn.value;
1051 const row = scope.row;
1052 currTableData.value = row;
1053 if (type === 'edit') { // 编辑
1054 currSignatoryData.value = lData;
1055 const tData = currSignatoryData.value.tableInfo.data;
1056 await setFormItems(row, 'target');
1057 drawerInfo.value.type = 'edit-target';
1058 drawerInfo.value.header.title = '编辑指标';
1059 targetFormItems.value[2].visible = lData.computationalFormula == '1';
1060 targetFormItems.value.at(-2).options.map(o => {
1061 const fData = tData.filter(t => t.targetType == '2' && t.functionName == '3');
1062 o.disabled = fData.find(f => f.demandTableGuid == o.guid) ? true : false;
1063 });
1064 drawerInfo.value.container.contents[0] = contents.value.target;
1065 drawerInfo.value.visible = true;
1066 } else if (type === 'del') { // 删除
1067 currSignatoryData.value = lData;
1068 open('确定要删除该条数据吗?', 'warning', 'target');
1069 }
1070 };
1071
1072
1073 const open = (msg, type, target) => {
1074 ElMessageBox.confirm(msg, "提示", {
1075 confirmButtonText: "确定",
1076 cancelButtonText: "取消",
1077 type: type,
1078 }).then(() => {
1079 if (target == 'signatory') {
1080 const tIndex = signatoryTableList.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
1081 tIndex != -1 && signatoryTableList.value.splice(tIndex, 1);
1082 const sIndex = pricingDimensionality.value.findIndex(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
1083 sIndex != -1 && pricingDimensionality.value.splice(sIndex, 1);
1084 } else if (target == 'target') {
1085 const signatoryRow = signatoryTableList.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
1086 if (signatoryRow) {
1087 const tIndex = signatoryRow.tableInfo.data.findIndex(t => t.targetName == currTableData.value.targetName);
1088 tIndex != -1 && signatoryRow.tableInfo.data.splice(tIndex, 1);
1089 }
1090 const signatoryParams = pricingDimensionality.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
1091 if (signatoryParams) {
1092 const sIndex = signatoryParams.pricingTargetRSVOS.findIndex(s => s.targetName == currTableData.value.targetName);
1093 sIndex != -1 && signatoryParams.pricingTargetRSVOS.splice(sIndex, 1);
1094 }
1095 }
1096 });
1097 };
1098 const drawerBtnClick = async (btn, info) => {
1099 if (btn.value == "submit") {
1100 let params = { ...info };
1101 if (drawerInfo.value.type == 'add-signatory') {// 新增维度
1102 setSignatoryTableInfo(params);
1103 pricingDimensionality.value.push({
1104 ...params,
1105 pricingTargetRSVOS: []
1106 })
1107 } else if (drawerInfo.value.type == 'edit-signatory') {// 编辑维度
1108 setSignatoryTableInfo(params, true);
1109 } else if (drawerInfo.value.type == 'add-target') {// 新增指标
1110 let factorFull = true;
1111 const dictionaryData = tableData.value.map((d: any) => {
1112 if (dictionaryType.value && !d.factor) {
1113 factorFull = false;
1114 }
1115 if (dictionaryType.value == '1') {
1116 return { name: `${d.level}${d.grade}`, value: d.factor || '' }
1117 } else {
1118 return { name: `${d.label}`, value: d.factor || '' }
1119 }
1120 })
1121 if (dictionaryType.value && !factorFull) {
1122 ElMessage({
1123 type: "error",
1124 message: '请完善字典因子',
1125 });
1126 return;
1127 }
1128 const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == params.dictionaryType)?.label || '';
1129 const sArr = signatoryTableList.value.map(item => {
1130 if (item.dimensionalityName == currSignatoryData.value.dimensionalityName) {
1131 item.tableInfo.data.push({ ...params, dictionaryJson: dictionaryData, dictionaryName });
1132 }
1133 return item;
1134 })
1135 signatoryTableList.value = sArr;
1136 let signatoryParams = pricingDimensionality.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
1137 if (signatoryParams) {
1138 const sData = signatoryParams.pricingTargetRSVOS.find(s => s.targetName == params.targetName)
1139 !sData && signatoryParams.pricingTargetRSVOS.push({ ...params, dictionaryJson: dictionaryData, dictionaryName })
1140 }
1141 } else if (drawerInfo.value.type == 'edit-target') {// 编辑指标
1142 let factorFull = true;
1143 const dictionaryData = tableData.value.map((d: any) => {
1144 if (dictionaryType.value && !d.factor) {
1145 factorFull = false;
1146 }
1147 if (dictionaryType.value == '1') {
1148 return { name: `${d.level}${d.grade}`, value: d.factor || '' }
1149 } else {
1150 return { name: `${d.label}`, value: d.factor || '' }
1151 }
1152 })
1153 if (dictionaryType.value && !factorFull) {
1154 ElMessage({
1155 type: "error",
1156 message: '请完善字典因子',
1157 });
1158 return;
1159 }
1160 const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == params.dictionaryType)?.label || '';
1161 const sArr = signatoryTableList.value.map(item => {
1162 if (item.dimensionalityName == currSignatoryData.value.dimensionalityName) {
1163 const tIndex = item.tableInfo.data.findIndex(t => t.targetName == currTableData.value.targetName)
1164 tIndex != -1 && item.tableInfo.data.splice(tIndex, 1, { ...params, dictionaryJson: dictionaryData, dictionaryName })
1165 }
1166 return item;
1167 })
1168 signatoryTableList.value = sArr;
1169 let signatoryParams = pricingDimensionality.value.find(item => item.dimensionalityName == currSignatoryData.value.dimensionalityName);
1170 if (signatoryParams) {
1171 const sIndex = signatoryParams.pricingTargetRSVOS.findIndex(s => s.targetName == currTableData.value.targetName)
1172 sIndex != -1 && signatoryParams.pricingTargetRSVOS.splice(sIndex, 1, { ...params, dictionaryJson: dictionaryData, dictionaryName })
1173 }
1174 }
1175 drawerInfo.value.visible = false;
1176 } else {
1177 nextTick(() => {
1178 drawerInfo.value.visible = false;
1179 });
1180 }
1181 };
1182
1183 onActivated(() => {
1184 let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === router.currentRoute.value.fullPath);
1185 if (tab) {
1186 switch (route.query.type) {
1187 case 'create':
1188 tab.meta.title = `新增定价配置`;
1189 break;
1190 case 'edit':
1191 tab.meta.title = `编辑-${modelName}`;
1192 break;
1193 case 'detail':
1194 tab.meta.title = `详情-${modelName}`;
1195 break;
1196 }
1197 }
1198 })
1199
1200 onBeforeMount(() => {
1201 if (guid) {
1202 getModelDetail();
1203 } else {
1204 getDataTypeList();
1205 }
1206 })
1207
1208 onMounted(() => {
1209 })
1210 </script>
1211
1212 <template>
1213 <div class="container_wrap full" v-loading="loading">
1214 <div class="content_main panel">
1215 <ContentWrap id="contract-content-wrap" title="基础设置" expandSwicth style="margin-top: 15px" :isExpand="expand1"
1216 @expand="(v) => expand1 = v">
1217 <Form ref="baseConfigFormRef" formId="contract-content-form" :itemList="baseConfigForm.items"
1218 :rules="baseConfigForm.rules" col="col3" />
1219 <div class="signatory-tags" v-if="signatoryTableList.length">
1220 <template v-for="(item, index) in signatoryTableList" :key="'tag_' + index">
1221 <el-button v-if="item.invocationStatus == 'Y'" type="primary" @click="btnClick({ value: 'dim' }, item)">{{
1222 item.dimensionalityName }}</el-button>
1223 </template>
1224 </div>
1225 <div class="tips-text" style="color: #666; margin-bottom: 4px; font-size: 12px;">
1226 备注:模型公式是维度名称与运算符组合,如("维度1"+"维度2")/("维度3"+"维度4")*10,维度名称要用英文引号(")包裹。维度名称在添加维度后才会在公式下展示!
1227 </div>
1228 </ContentWrap>
1229 <ContentWrap id="contract-signatory-wrap" title="维度设置" expandSwicth style="margin-top: 15px" :isExpand="expand2"
1230 @expand="(v) => expand2 = v">
1231 <div class="table_panel_wrap" v-for="(item, index) in signatoryTableList" :key="'wrap_' + index">
1232 <div class="table_tool">
1233 <div class="tool_title">
1234 <div class="title_text">{{ item.dimensionalityName }}</div>
1235 <div class="title_desc"><ellipsis-tooltip :content="setVal('computationalFormula', item)"
1236 refName="tooltipOver"></ellipsis-tooltip></div>
1237 </div>
1238 <div class="tool_btns">
1239 <el-switch v-model="item.invocationStatus" inline-prompt active-value="Y" inactive-value="N"
1240 active-text="启用" inactive-text="停用" @change="val => switchChange(val, index)"
1241 style="margin-right: 8px;" />
1242 <el-button type="primary" @click="btnClick({ value: 'edit-signatory' }, item)">编辑</el-button>
1243 <el-button @click="btnClick({ value: 'del-signatory' }, item)">删除</el-button>
1244 </div>
1245 </div>
1246 <div class="table_panel">
1247 <Table :tableInfo="item.tableInfo" @tableBtnClick="(scope, btn) => tableBtnClick(scope, btn, item)" />
1248 </div>
1249 <el-button type="primary" :icon="CirclePlus" link
1250 @click="btnClick({ value: 'add-target' }, item)">新增指标</el-button>
1251 </div>
1252 <el-button class="btn-block" plain size="large" @click="btnClick({ value: 'add-signatory' })">新增维度</el-button>
1253 </ContentWrap>
1254 </div>
1255 <div class="tool_btns">
1256 <div class="btns">
1257 <el-button @click="btnClick({ value: 'cancel' })">取消</el-button>
1258 <el-button type="primary" @click="btnClick({ value: 'submit' })">提交</el-button>
1259 </div>
1260 </div>
1261 <Drawer :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick" @drawerSelectChange="selectChange">
1262 <template v-if="showFactorTable">
1263 <span class="required_mark" style="line-height: 21px;">字典值对应因子</span>
1264 <div class="table_panel">
1265 <el-table border :data="tableData" :span-method="tableSpanMethod" tooltip-effect="light" style="height: 100%;"
1266 v-if="dictionaryType == '1'">
1267 <el-table-column label="医院等级">
1268 <el-table-column prop="level" label="级别" width="100" />
1269 <el-table-column prop="grade" label="等次" width="100" />
1270 </el-table-column>
1271 <el-table-column prop="factor" label="因子" class-name="edit-col">
1272 <template #default="scope">
1273 <el-input v-model="scope.row.factor" placeholder="请输入"
1274 @change="(val) => inputChange(val, scope, 'factor')"
1275 @input="(val) => inputEventChange(val, scope, 'factor')" />
1276 </template>
1277 </el-table-column>
1278 </el-table>
1279 <el-table border :data="tableData" tooltip-effect="light" style="height: 100%;" v-else>
1280 <el-table-column label="字典名称" prop="label" width="140" />
1281 <el-table-column prop="factor" label="因子" class-name="edit-col">
1282 <template #default="scope">
1283 <el-input v-model="scope.row.factor" placeholder="请输入"
1284 @change="(val) => inputChange(val, scope, 'factor')"
1285 @input="(val) => inputEventChange(val, scope, 'factor')" />
1286 </template>
1287 </el-table-column>
1288 </el-table>
1289 </div>
1290 </template>
1291 </Drawer>
1292 </div>
1293 </template>
1294
1295 <style scoped lang="scss">
1296 .container_wrap {
1297 overflow: hidden;
1298
1299 .content_main {
1300 height: calc(100% - 45px);
1301 overflow: hidden auto;
1302
1303 &.panel {
1304 padding: 0 16px 16px;
1305 }
1306
1307 :deep(.el-card) {
1308 &#contract-signatory-wrap {
1309 .card-body-content {
1310 padding: 8px 16px;
1311 }
1312 }
1313 }
1314
1315 .signatory-tags {
1316 margin-top: 2px;
1317 margin-bottom: 11px;
1318 }
1319
1320 .table_panel_wrap {
1321 margin-bottom: 4px;
1322
1323 .table_tool {
1324 height: 36px;
1325 display: flex;
1326 justify-content: space-between;
1327 align-items: center;
1328
1329 .tool_title {
1330 display: flex;
1331 justify-content: start;
1332 flex: 1;
1333 }
1334
1335 .title_text {
1336 font-weight: 600;
1337 margin-right: 8px;
1338 }
1339
1340 .title_desc {
1341 color: #b2b2b2;
1342 flex: 1;
1343 overflow: hidden;
1344 white-space: nowrap;
1345 text-overflow: ellipsis;
1346 }
1347
1348 .tool_btns {
1349 margin: 0;
1350 border: none;
1351 }
1352 }
1353
1354 .table_panel {
1355 margin-bottom: 4px;
1356 height: 212px;
1357 }
1358 }
1359 }
1360
1361 .btn-block {
1362 width: 100%;
1363 margin: 16px 0 8px;
1364
1365 }
1366
1367 .tool_btns {
1368 height: 44px;
1369 margin: 0 -8px;
1370 display: flex;
1371 justify-content: center;
1372 align-items: center;
1373 border-top: 1px solid #d9d9d9;
1374 }
1375 }
1376
1377 :deep(.el-drawer) {
1378 .table_panel {
1379 th {
1380 text-align: center;
1381 }
1382
1383 .el-table__cell {
1384 &.edit-col {
1385 padding: 4px 0;
1386
1387 .cell {
1388 padding: 0 4px;
1389
1390 .el-input {
1391 height: 28px;
1392 }
1393 }
1394 }
1395 }
1396 }
1397 }
1398 </style>
...@@ -25,12 +25,13 @@ import { ...@@ -25,12 +25,13 @@ import {
25 getDatabase, 25 getDatabase,
26 getRuleTypeList 26 getRuleTypeList
27 } from '@/api/modules/dataQuality'; 27 } from '@/api/modules/dataQuality';
28 import { getDamCatalogList } from '@/api/modules/dataPricing';
28 import useDataQualityStore from "@/store/modules/dataQuality"; 29 import useDataQualityStore from "@/store/modules/dataQuality";
29 import { useValidator } from '@/hooks/useValidator'; 30 import { useValidator } from '@/hooks/useValidator';
30 import { TableColumnWidth } from '@/utils/enum'; 31 import { TableColumnWidth } from '@/utils/enum';
31 32
32 const dataQualityStore = useDataQualityStore(); 33 const dataQualityStore = useDataQualityStore();
33 34 const userData = JSON.parse(localStorage.userData);
34 const { proxy } = getCurrentInstance() as any; 35 const { proxy } = getCurrentInstance() as any;
35 const { orderNum, description } = useValidator(); 36 const { orderNum, description } = useValidator();
36 37
...@@ -39,6 +40,8 @@ const cacheStore = useCatchStore(); ...@@ -39,6 +40,8 @@ const cacheStore = useCatchStore();
39 /** 可选择的质量规则列表。 */ 40 /** 可选择的质量规则列表。 */
40 const ruleTypeList: any = ref([]); 41 const ruleTypeList: any = ref([]);
41 42
43 const productList: any = ref([]);
44
42 /** 质量规则集表对象。 */ 45 /** 质量规则集表对象。 */
43 const qualityModelTreeRef = ref(); 46 const qualityModelTreeRef = ref();
44 /** 树选中不同层级的,代表的类型, model, group, table */ 47 /** 树选中不同层级的,代表的类型, model, group, table */
...@@ -281,7 +284,22 @@ const formItems: any = ref([ ...@@ -281,7 +284,22 @@ const formItems: any = ref([
281 default: '', 284 default: '',
282 maxlength: 6, 285 maxlength: 6,
283 required: true 286 required: true
284 }, { 287 },
288 {
289 label: '数据产品',
290 type: 'select',
291 field: 'damGuid',
292 default: '',
293 placeholder: '请选择',
294 options: productList.value,
295 props: {
296 value: 'guid',
297 label: 'damName'
298 },
299 clearable: true,
300 filterable: true
301 },
302 {
285 label: '描述', 303 label: '描述',
286 type: 'textarea', 304 type: 'textarea',
287 placeholder: '请输入', 305 placeholder: '请输入',
...@@ -929,6 +947,17 @@ onActivated(async () => { ...@@ -929,6 +947,17 @@ onActivated(async () => {
929 } 947 }
930 }) 948 })
931 949
950 // 获取数据产品列表
951 const getProducts = () => {
952 getDamCatalogList({ dataType: userData.superTubeFlag == 'Y' ? "P" : "D", sceneType: "Z" }).then((res: any) => {
953 if (res.code == proxy.$passCode) {
954 const data = res.data || [];
955 productList.value = data;
956 formItems.value.at(-2).options = data;
957 }
958 })
959 }
960
932 onBeforeMount(() => { 961 onBeforeMount(() => {
933 getQualityGroupTreeData(); 962 getQualityGroupTreeData();
934 getGroupTableData(); 963 getGroupTableData();
...@@ -945,6 +974,7 @@ onBeforeMount(() => { ...@@ -945,6 +974,7 @@ onBeforeMount(() => {
945 ElMessage.error(res.msg); 974 ElMessage.error(res.msg);
946 } 975 }
947 }) 976 })
977 getProducts();
948 }) 978 })
949 979
950 </script> 980 </script>
......
...@@ -18,7 +18,7 @@ import { ...@@ -18,7 +18,7 @@ import {
18 getImportData, 18 getImportData,
19 exportDictionary, 19 exportDictionary,
20 exportCollectTask, 20 exportCollectTask,
21 // getImageContent 21 exportTemplate
22 } from '@/api/modules/queryService'; 22 } from '@/api/modules/queryService';
23 import { 23 import {
24 parseAndDecodeUrl, 24 parseAndDecodeUrl,
...@@ -29,6 +29,7 @@ import { ...@@ -29,6 +29,7 @@ import {
29 getDictionaryTree 29 getDictionaryTree
30 } from '@/api/modules/dataInventory'; 30 } from '@/api/modules/dataInventory';
31 import { commonPageConfig } from '@/utils/enum'; 31 import { commonPageConfig } from '@/utils/enum';
32 import { getDemandTreeList, getDiseaseAll } from '@/api/modules/dataPricing';
32 33
33 const { proxy } = getCurrentInstance() as any; 34 const { proxy } = getCurrentInstance() as any;
34 35
...@@ -156,6 +157,29 @@ const getDictList = () => { ...@@ -156,6 +157,29 @@ const getDictList = () => {
156 }) 157 })
157 } 158 }
158 159
160 // 获取需求表树形数据
161 const getTreeData = () => {
162 if (isfileImport == '7') {
163 getDemandTreeList({ pageIndex: 1, pageSize: 100000, isCatalog: 'N' }).then((res: any) => {
164 if (res.code == proxy.$passCode) {
165 const data = res.data?.records || [];
166 dictionaryList.value = data;
167 } else {
168 proxy.$ElMessage.error(res.msg);
169 }
170 })
171 } else {
172 getDiseaseAll().then((res: any) => {
173 if (res.code == proxy.$passCode) {
174 const data = res.data || [];
175 dictionaryList.value = data;
176 } else {
177 proxy.$ElMessage.error(res.msg);
178 }
179 })
180 }
181 }
182
159 const tabsChange = (name) => { 183 const tabsChange = (name) => {
160 tabsActiveName.value = name 184 tabsActiveName.value = name
161 let info: any = { 185 let info: any = {
...@@ -283,11 +307,16 @@ const batching = (type) => { ...@@ -283,11 +307,16 @@ const batching = (type) => {
283 } 307 }
284 open("此操作将永久删除, 是否继续?", "warning", true); 308 open("此操作将永久删除, 是否继续?", "warning", true);
285 } else if (type === 'importFile') { 309 } else if (type === 'importFile') {
286 if (isfileImport == '2' || isfileImport == '4') { 310 if (isfileImport == '2' || isfileImport == '4' || isfileImport == '7' || isfileImport == '8') {
287 dialogInfo.value.header.title = '导入数据' 311 dialogInfo.value.header.title = '导入数据'
288 dialogInfo.value.type = 'upload' 312 dialogInfo.value.type = 'upload'
289 dialogInfo.value.size = isfileImport == '4' ? 600 : 500; 313 dialogInfo.value.size = isfileImport == '4' ? 600 : 500;
290 uploadFiles.value = [] 314 uploadFiles.value = []
315 if (isfileImport == '7') {
316 uploadSteps.value[0].selectInfo.options = dictionaryList.value;
317 } else if (isfileImport == '8') {
318 uploadSteps.value[0].cascaderInfo.options = dictionaryList.value;
319 }
291 uploadInfo.value.uploadInfo.steps = uploadSteps.value 320 uploadInfo.value.uploadInfo.steps = uploadSteps.value
292 const content: any = [uploadInfo.value] 321 const content: any = [uploadInfo.value]
293 dialogInfo.value.contents = content 322 dialogInfo.value.contents = content
...@@ -377,6 +406,20 @@ const exportData = (ids: any = null) => { ...@@ -377,6 +406,20 @@ const exportData = (ids: any = null) => {
377 res?.msg && ElMessage.error(res?.msg); 406 res?.msg && ElMessage.error(res?.msg);
378 } 407 }
379 }); 408 });
409 } else if (tabsActiveName.value == 'importFile' && isfileImport == '7') {
410 exportTemplate({
411 bizGuid: '',
412 importType: "0046"
413 }).then((res: any) => {
414 download(res, '需求表管理导入模板.xlsx', 'excel')
415 });
416 } else if (tabsActiveName.value == 'importFile' && isfileImport == '8') {
417 exportTemplate({
418 bizGuid: '',
419 importType: "0047"
420 }).then((res: any) => {
421 download(res, '疾病管理导入模板.xlsx', 'excel')
422 });
380 } 423 }
381 } 424 }
382 425
...@@ -390,11 +433,11 @@ const importData = (info) => { ...@@ -390,11 +433,11 @@ const importData = (info) => {
390 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled) 433 // dialogInfo.value.footer.btns.map((item: any) => delete item.disabled)
391 return 434 return
392 } 435 }
393 let paramUrl = '';
394 if (isfileImport == '2') {
395 uploadFiles.value.forEach((item: any, index: number) => { 436 uploadFiles.value.forEach((item: any, index: number) => {
396 params.append("file", item.raw); 437 params.append("file", item.raw);
397 }); 438 });
439 let paramUrl = '';
440 if (isfileImport == '2') {
398 paramUrl = `ms-daop-zcgl-asset-dam-service/dam-catalog-table/excel-by-subject-guid?staffGuid=${userData.staffGuid}&subjectGuid=${route.query.bizGuid}` 441 paramUrl = `ms-daop-zcgl-asset-dam-service/dam-catalog-table/excel-by-subject-guid?staffGuid=${userData.staffGuid}&subjectGuid=${route.query.bizGuid}`
399 } else if (isfileImport == '4') { 442 } else if (isfileImport == '4') {
400 if (!info.databaseNameZh) { 443 if (!info.databaseNameZh) {
...@@ -411,14 +454,17 @@ const importData = (info) => { ...@@ -411,14 +454,17 @@ const importData = (info) => {
411 }) 454 })
412 return; 455 return;
413 } 456 }
414 uploadFiles.value.forEach((item: any, index: number) => { 457
415 params.append("uploadFile", item.raw);
416 });
417 paramUrl = `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/meta-collect-import?staffGuid=${userData.staffGuid}&databaseNameZh=${info.databaseNameZh}&databaseNameEn=${info.databaseNameEn}&isCover=${info.isCover}` 458 paramUrl = `${import.meta.env.VITE_APP_PLAN_BASEURL}/meta-collect-task/meta-collect-import?staffGuid=${userData.staffGuid}&databaseNameZh=${info.databaseNameZh}&databaseNameEn=${info.databaseNameEn}&isCover=${info.isCover}`
459 } else if (isfileImport == '7') {
460 info.bizGuid = '0046';
461 paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}`
462 paramUrl += `&extendFields=${encodeURIComponent(JSON.stringify(info))}`
463 } else if (isfileImport == '8') {
464 info.bizGuid = '0047';
465 paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}`
466 paramUrl += `&extendFields=${encodeURIComponent(JSON.stringify(info))}`
418 } else { 467 } else {
419 uploadFiles.value.forEach((item: any, index: number) => {
420 params.append("file", item.raw);
421 });
422 paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}&dataType=2` 468 paramUrl = `${import.meta.env.VITE_APP_ADD_FILE}/import-data/import-common?importType=${importType.value}&staffGuid=${userData.staffGuid}&tenantGuid=${userData.tenantGuid}&dataType=2`
423 if (info && Object.keys(info).length) { 469 if (info && Object.keys(info).length) {
424 paramUrl += `&extendFields=${encodeURIComponent(JSON.stringify(info))}` 470 paramUrl += `&extendFields=${encodeURIComponent(JSON.stringify(info))}`
...@@ -452,6 +498,10 @@ const dialogBtnClick = (btn, info) => { ...@@ -452,6 +498,10 @@ const dialogBtnClick = (btn, info) => {
452 if (dialogInfo.value.type == 'upload') { 498 if (dialogInfo.value.type == 'upload') {
453 if (tabsActiveName.value == 'dictionary') { 499 if (tabsActiveName.value == 'dictionary') {
454 importData({ bizGuid: dictionaryGuid.value }) 500 importData({ bizGuid: dictionaryGuid.value })
501 } else if (tabsActiveName.value == 'importFile' && isfileImport == '7') {
502 importData(info)
503 } else if (tabsActiveName.value == 'importFile' && isfileImport == '8') {
504 importData(info)
455 } else { 505 } else {
456 importData(info) 506 importData(info)
457 } 507 }
...@@ -478,6 +528,10 @@ const setUploadInfo = () => { ...@@ -478,6 +528,10 @@ const setUploadInfo = () => {
478 importType.value = '0034'; 528 importType.value = '0034';
479 } else if (isfileImport == '4') { 529 } else if (isfileImport == '4') {
480 importType.value = '0042'; 530 importType.value = '0042';
531 } else if (isfileImport == '7') {
532 importType.value = '0046';
533 } else if (isfileImport == '8') {
534 importType.value = '0047';
481 } else { 535 } else {
482 importType.value = '0033'; 536 importType.value = '0033';
483 } 537 }
...@@ -641,6 +695,94 @@ const setUploadInfo = () => { ...@@ -641,6 +695,94 @@ const setUploadInfo = () => {
641 } 695 }
642 ] 696 ]
643 uploadInfo.value.uploadInfo.extraParams = { isCover: 'Y' } 697 uploadInfo.value.uploadInfo.extraParams = { isCover: 'Y' }
698 } else if (tabsActiveName.value == 'importFile' && isfileImport == '7') {
699 uploadSteps.value = [
700 {
701 title: '1、导入前请先录入以下内容',
702 type: 'select',
703 selectInfo: {
704 label: '需求表名称',
705 placeholder: '请选择',
706 field: 'menuGuid',
707 default: '',
708 options: [],
709 props: {
710 label: "menuName",
711 value: "guid",
712 },
713 filterable: true,
714 clearable: true,
715 required: true,
716 style: {
717 width: '191px',
718 }
719 }
720 },
721 {
722 title: '2、请下载最新的模板,并按照模板格式准备需要导入的数据',
723 type: 'btn_down'
724 },
725 {
726 title: '3、选择准备好的文件导入',
727 type: 'btn_upload',
728 uploadInfo: {
729 action: '',
730 auto: false,
731 cover: true,
732 fileList: [],
733 accept: '.xlsx, .xls',
734 tips: '当前支持xls、xlsx文件,默认使用第一个sheet'
735 }
736 }
737 ]
738 dictionaryGuid.value = uploadSetting.value?.dictionaryGuid || ''
739 uploadInfo.value.uploadInfo.extraParams = {
740 menuGuid: dictionaryGuid.value
741 }
742 } else if (tabsActiveName.value == 'importFile' && isfileImport == '8') {
743 uploadSteps.value = [
744 {
745 title: '1、导入前请先录入以下内容',
746 type: 'cascader',
747 cascaderInfo: {
748 label: '疾病名称',
749 placeholder: '',
750 field: 'parentGuid',
751 options: [],
752 default: '',
753 showAllLevels: false,
754 props: {
755 checkStrictly: true,
756 label: "diseaseName",
757 value: "guid",
758 children: 'childList',
759 emitPath: false
760 },
761 filterable: true,
762 clearable: true,
763 }
764 },
765 {
766 title: '2、请下载最新的模板,并按照模板格式准备需要导入的数据',
767 type: 'btn_down'
768 },
769 {
770 title: '3、选择准备好的文件导入',
771 type: 'btn_upload',
772 uploadInfo: {
773 action: '',
774 auto: false,
775 cover: true,
776 fileList: [],
777 accept: '.xlsx, .xls',
778 tips: '当前支持xls、xlsx文件,默认使用第一个sheet'
779 }
780 }
781 ]
782 dictionaryGuid.value = uploadSetting.value?.dictionaryGuid || ''
783 uploadInfo.value.uploadInfo.extraParams = {
784 parentGuid: dictionaryGuid.value
785 }
644 } else { 786 } else {
645 uploadSteps.value = [ 787 uploadSteps.value = [
646 { 788 {
...@@ -674,7 +816,8 @@ onActivated(() => { ...@@ -674,7 +816,8 @@ onActivated(() => {
674 if (tabsActiveName.value == 'dictionary') { 816 if (tabsActiveName.value == 'dictionary') {
675 getDictList(); 817 getDictList();
676 } 818 }
677 setUploadInfo() 819 setUploadInfo();
820 (isfileImport == '7' || isfileImport == '8') && getTreeData();
678 }) 821 })
679 822
680 </script> 823 </script>
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!