@nstc-business/imes 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,15 @@
29
29
  >
30
30
  {{ allExpanded ? '全部收起' : '全部展开' }}
31
31
  </el-button>
32
+ <el-button
33
+ v-if="showPrint"
34
+ size="small"
35
+ plain
36
+ :loading="printLoading"
37
+ @click="printFn"
38
+ >
39
+ 打印
40
+ </el-button>
32
41
  </div>
33
42
  </div>
34
43
 
@@ -47,7 +56,7 @@
47
56
  </span>
48
57
  <span class="meta-item">
49
58
  <span class="meta-label">测算结果</span>
50
- <span class="meta-value meta-result">{{ resultText || '--' }}</span>
59
+ <span class="meta-value meta-result">{{ displayResultText }}</span>
51
60
  </span>
52
61
  </div>
53
62
 
@@ -210,13 +219,10 @@
210
219
  :sub-model="section.container"
211
220
  :level="1"
212
221
  />
213
- <measure-qual-analysis
214
- v-else-if="isQualOnlySection(section)"
215
- :groups="qualSectionGroups(section)"
216
- />
217
- <measure-tree-table
222
+ <measure-group-blocks
218
223
  v-else-if="section.nodes && section.nodes.length"
219
224
  :nodes="section.nodes"
225
+ :group-node="section.container"
220
226
  :level="1"
221
227
  :calc-mode="calcRootModel"
222
228
  :section="section"
@@ -228,8 +234,7 @@
228
234
 
229
235
  <script>
230
236
  import { SecondaryTab } from './ui'
231
- import MeasureTreeTable from './MeasureTreeTable.vue'
232
- import MeasureQualAnalysis from './MeasureQualAnalysis.vue'
237
+ import MeasureGroupBlocks from './MeasureGroupBlocks.vue'
233
238
  import MeasureSubModelPanel from './MeasureSubModelPanel.vue'
234
239
  import MeasureStatPanel from './MeasureStatPanel.vue'
235
240
  import {
@@ -238,9 +243,10 @@ import {
238
243
  isIndicator,
239
244
  isSubModel,
240
245
  isStatModel,
241
- isQualOnlyNodes,
242
246
  isQualIndicator,
243
247
  resolveQualSelection,
248
+ sumIndicatorScores,
249
+ nodeScoreValue as readNodeScore,
244
250
  SUB_MODEL_LABEL,
245
251
  SUB_MODEL_CALC_BTN,
246
252
  isCalcRootModel as checkCalcRootModel,
@@ -255,13 +261,13 @@ import {
255
261
  } from './statModelHelper'
256
262
  import { withRequestTimestamp } from './api'
257
263
  import { formatFloat2 } from '../common'
264
+ import { printJS } from 'n20-common-lib/src/plugins/Print'
258
265
 
259
266
  export default {
260
267
  name: 'MeasureCalc',
261
268
  components: {
262
269
  SecondaryTab,
263
- MeasureTreeTable,
264
- MeasureQualAnalysis,
270
+ MeasureGroupBlocks,
265
271
  MeasureSubModelPanel,
266
272
  MeasureStatPanel
267
273
  },
@@ -276,7 +282,11 @@ export default {
276
282
  readonly: this.readonly,
277
283
  resolveLeafNode: this.resolveLeafNode,
278
284
  syncQualSelection: this.syncQualSelection,
279
- getQualSelection: this.getQualSelection
285
+ getQualSelection: this.getQualSelection,
286
+ evalRootModel: this.evalRootModel,
287
+ groupWeightText: this.groupWeightText,
288
+ groupScoreText: this.groupScoreText,
289
+ groupSubtotalText: this.groupSubtotalText
280
290
  })
281
291
  }
282
292
  },
@@ -315,12 +325,28 @@ export default {
315
325
  condParam: {
316
326
  type: Object,
317
327
  default: () => ({})
328
+ },
329
+ /** 是否展示打印按钮,由业务系统做权限判断后传入,默认不展示 */
330
+ showPrint: {
331
+ type: Boolean,
332
+ default: false
333
+ },
334
+ /** 授信申报信息,打印时原样传给打印接口 */
335
+ creditInfo: {
336
+ type: Object,
337
+ default: () => ({})
338
+ },
339
+ /** 评级申报信息,打印时原样传给打印接口 */
340
+ gradeInfo: {
341
+ type: Object,
342
+ default: () => ({})
318
343
  }
319
344
  },
320
345
  data() {
321
346
  return {
322
347
  loading: false,
323
348
  calcLoading: false,
349
+ printLoading: false,
324
350
  model: { type: 1, name: '', code: '', digits: 2, modelTreeVO: {} },
325
351
  sections: [],
326
352
  anchorTabs: [],
@@ -352,18 +378,18 @@ export default {
352
378
  messageStr: '',
353
379
  options: [
354
380
  { value: '13', label: '13月(已审计报表)' },
355
- { value: '1', label: '1月' },
356
- { value: '2', label: '2月' },
357
- { value: '3', label: '3月' },
358
- { value: '4', label: '4月' },
359
- { value: '5', label: '5月' },
360
- { value: '6', label: '6月' },
361
- { value: '7', label: '7月' },
362
- { value: '8', label: '8月' },
363
- { value: '9', label: '9月' },
364
- { value: '10', label: '10月' },
381
+ { value: '12', label: '12月' },
365
382
  { value: '11', label: '11月' },
366
- { value: '12', label: '12月' }
383
+ { value: '10', label: '10月' },
384
+ { value: '9', label: '9月' },
385
+ { value: '8', label: '8月' },
386
+ { value: '7', label: '7月' },
387
+ { value: '6', label: '6月' },
388
+ { value: '5', label: '5月' },
389
+ { value: '4', label: '4月' },
390
+ { value: '3', label: '3月' },
391
+ { value: '2', label: '2月' },
392
+ { value: '1', label: '1月' }
367
393
  ]
368
394
  }
369
395
  },
@@ -386,18 +412,29 @@ export default {
386
412
  statRootModel() {
387
413
  return checkStatRootModel(this.model)
388
414
  },
389
- /** 顶层统计模型的根节点 */
415
+ /** 顶层统计模型的根节点(必须返回原引用,保证 _statTableVersion 能触发子表刷新) */
390
416
  statRootNode() {
391
417
  const root = this.model.modelTreeVO || {}
392
- return {
393
- ...root,
394
- itemID: root.itemID || root.modelID || this.model.modelID
418
+ const itemID = root.itemID || root.modelID || this.model.modelID
419
+ if (itemID && root.itemID !== itemID) {
420
+ this.$set(root, 'itemID', itemID)
395
421
  }
422
+ return root
396
423
  },
397
424
  modelVersionText() {
398
425
  if (this.model.version === '' || this.model.version == null) return '--'
399
426
  return `版本${this.model.version}`
400
427
  },
428
+ /** 测算结果展示:优先 resultText,统计模型计算模式可回退到 result */
429
+ displayResultText() {
430
+ if (this.resultText !== '' && this.resultText != null) {
431
+ return this.resultText
432
+ }
433
+ if (this.calcResult !== '' && this.calcResult != null) {
434
+ return this.calcResult
435
+ }
436
+ return '--'
437
+ },
401
438
  /** 评价模型展示「模型得分」,计算模型展示「模型公式」 */
402
439
  formulaRowLabel() {
403
440
  if (this.evalRootModel) return '模型得分:'
@@ -412,9 +449,20 @@ export default {
412
449
  }
413
450
  return this.relFormula || this.displayFormula || '--'
414
451
  },
415
- /** 是否存在容器节点(决定是否显示「全部展开/收起」) */
452
+ /** 表格内仍可展开的容器(分组已外提,只剩子模型) */
416
453
  hasAnyContainer() {
417
- return this.sections.some(s => (s.nodes || []).some(n => isContainer(n)))
454
+ const walk = nodes => {
455
+ if (!Array.isArray(nodes)) return false
456
+ return nodes.some(n => {
457
+ if (isSubModel(n)) return true
458
+ if (n && n.children && n.children.length) return walk(n.children)
459
+ return false
460
+ })
461
+ }
462
+ return this.sections.some(s => {
463
+ if (s.container && isSubModel(s.container)) return true
464
+ return walk(s.nodes)
465
+ })
418
466
  },
419
467
  /** 仍有必填项未完善时显示黄字提示 */
420
468
  showIncompleteTip() {
@@ -465,17 +513,6 @@ export default {
465
513
  subModelResultText(node) {
466
514
  return formatSubModelResult(node, this.model)
467
515
  },
468
- isQualOnlySection(section) {
469
- return isQualOnlyNodes(section.nodes)
470
- },
471
- qualSectionGroups(section) {
472
- return [
473
- {
474
- name: '',
475
- children: (section.nodes || []).map(n => this.resolveLeafNode(n) || n)
476
- }
477
- ]
478
- },
479
516
  /** 表格展开区可能是副本节点,统一解析到 leafList / qualList 原始引用 */
480
517
  resolveLeafNode(node) {
481
518
  return this.findQualNode(node && node.tid, node && node.itemID, node)
@@ -831,25 +868,28 @@ export default {
831
868
  return false
832
869
  },
833
870
 
834
- /** 单个指标的入参校验与组装 */
871
+ /** 单个指标的入参校验与组装(同一指标只组装一条,避免后端 findFirst 丢调整得分) */
835
872
  checkItem(item) {
836
873
  const obj = { scoreMapList: [], indicatorCalcList: [], state: true }
874
+ const displayValue =
875
+ item.displayStyle === 1
876
+ ? formatFloat2(item.value / 100, this.model.digits)
877
+ : item.value
878
+ const calcItem = {
879
+ hasInput: true,
880
+ indicatorID: item.itemID,
881
+ tid: item.tid,
882
+ modifiedReason: item.modifiedReason || ''
883
+ }
884
+ let hasCalc = false
837
885
  // 手工填报(含统计模型行转列内的可量化指标)
838
886
  if (item.sourceType === 3 && !isQualIndicator(item)) {
839
887
  if (!item.value && item.value !== 0) {
840
888
  obj.state = false
841
889
  this.messageStr += `${item.name}的指标值不可为空;`
842
890
  }
843
- obj.indicatorCalcList.push({
844
- hasInput: true,
845
- indicatorID: item.itemID,
846
- tid: item.tid,
847
- modifiedReason: item.modifiedReason || '',
848
- scoreValue:
849
- item.displayStyle === 1
850
- ? formatFloat2(item.value / 100, this.model.digits)
851
- : item.value
852
- })
891
+ calcItem.scoreValue = displayValue
892
+ hasCalc = true
853
893
  }
854
894
  // 不可量化 + 有选项:校验选项(optionList 以 qualList 为准)
855
895
  if (isQualIndicator(item)) {
@@ -902,16 +942,8 @@ export default {
902
942
  item.indicatorResult !== null &&
903
943
  (item.value || item.value === 0)
904
944
  ) {
905
- obj.indicatorCalcList.push({
906
- hasInput: true,
907
- indicatorID: item.itemID,
908
- tid: item.tid,
909
- modifiedReason: item.modifiedReason || '',
910
- modifiedVal:
911
- item.displayStyle === 1
912
- ? formatFloat2(item.value / 100, this.model.digits)
913
- : item.value
914
- })
945
+ calcItem.modifiedVal = displayValue
946
+ hasCalc = true
915
947
  }
916
948
  // 调整项=指标得分
917
949
  if (
@@ -920,14 +952,10 @@ export default {
920
952
  item.modifyRank === 1 &&
921
953
  (item.modifiedResult || item.modifiedResult === 0)
922
954
  ) {
923
- obj.indicatorCalcList.push({
924
- hasInput: true,
925
- indicatorID: item.itemID,
926
- tid: item.tid,
927
- modifiedReason: item.modifiedReason || '',
928
- modifiedResult: item.modifiedResult
929
- })
955
+ calcItem.modifiedResult = item.modifiedResult
956
+ hasCalc = true
930
957
  }
958
+ if (hasCalc) obj.indicatorCalcList.push(calcItem)
931
959
  return obj
932
960
  },
933
961
 
@@ -1021,6 +1049,42 @@ export default {
1021
1049
  }
1022
1050
  },
1023
1051
 
1052
+ async printFn() {
1053
+ const logId = this.model.logId || this.logId || ''
1054
+ const modelId = this.model.modelID || this.model.id || this.id || ''
1055
+ if (!logId || !modelId) {
1056
+ this.$message.warning('请先完成测算后再打印')
1057
+ return
1058
+ }
1059
+ this.printLoading = true
1060
+ try {
1061
+ const res = await this.$axios.post(
1062
+ withRequestTimestamp('/imes/v1/indicatorModel/print'),
1063
+ [
1064
+ {
1065
+ logId,
1066
+ modelId,
1067
+ creditInfo: this.creditInfo || {},
1068
+ gradeInfo: this.gradeInfo || {}
1069
+ }
1070
+ ]
1071
+ )
1072
+ if (res.code !== 0) {
1073
+ this.$message.error(res.msg || '打印失败')
1074
+ return
1075
+ }
1076
+ printJS({
1077
+ printable: res.data,
1078
+ type: 'pdf',
1079
+ base64: true
1080
+ })
1081
+ } catch (e) {
1082
+ this.$message.error('打印异常')
1083
+ } finally {
1084
+ this.printLoading = false
1085
+ }
1086
+ },
1087
+
1024
1088
  /** 外层模型整体测算 */
1025
1089
  async calc() {
1026
1090
  if (this.readonly) return
@@ -1077,7 +1141,7 @@ export default {
1077
1141
  this.businessModuleRemark =resData.businessModuleRemark
1078
1142
  this.$set(this.model, 'logId', resData.logId || '')
1079
1143
  this.$set(this.model, 'maxReportTem', resData.maxReportTem || '')
1080
- this.$emit('calcResult', resData.resultText)
1144
+ this.$emit('calcResult', this.displayResultText === '--' ? resData.resultText : this.displayResultText)
1081
1145
  this.$emit('getLogId', resData.logId)
1082
1146
  this.$emit('calcComplete', {
1083
1147
  ...resData,
@@ -1286,16 +1350,7 @@ export default {
1286
1350
 
1287
1351
  /** 节点用于汇总的得分(优先调整后得分) */
1288
1352
  nodeScoreValue(node) {
1289
- if (!node) return null
1290
- if (
1291
- node.modifyRank === 1 &&
1292
- node.modifiedResult !== '' &&
1293
- node.modifiedResult != null
1294
- ) {
1295
- return node.modifiedResult
1296
- }
1297
- if (node.result === '' || node.result == null) return null
1298
- return node.result
1353
+ return readNodeScore(node)
1299
1354
  },
1300
1355
 
1301
1356
  formatScoreNum(val) {
@@ -1374,23 +1429,46 @@ export default {
1374
1429
  },
1375
1430
 
1376
1431
  /** 分区标题旁总权重(评价模型且开启显示权重) */
1377
- sectionWeightText(section) {
1378
- if (!this.evalRootModel || !section) return ''
1432
+ groupWeightText(groupNode, children) {
1433
+ if (!this.evalRootModel || !groupNode) return ''
1379
1434
  if (this.model.showWeight !== 1) return ''
1380
- if (!section.container || this.isSubModel(section.container)) return ''
1381
- return this.buildGroupWeightText(section.container, section.nodes)
1435
+ if (this.isSubModel(groupNode)) return ''
1436
+ return this.buildGroupWeightText(
1437
+ groupNode,
1438
+ children || groupNode.children
1439
+ )
1440
+ },
1441
+ sectionWeightText(section) {
1442
+ if (!section || !section.container) return ''
1443
+ return this.groupWeightText(section.container, section.nodes)
1382
1444
  },
1383
1445
 
1384
- /** 分区标题旁得分(评价模型:未测算显示 --) */
1385
- sectionScoreText(section) {
1386
- if (!this.evalRootModel || !section) return ''
1387
- if (!section.container || this.isSubModel(section.container)) return ''
1446
+ /** 分组标题旁得分(评价模型:未测算显示 --) */
1447
+ groupScoreText(groupNode, children) {
1448
+ if (!this.evalRootModel || !groupNode) return ''
1449
+ if (this.isSubModel(groupNode)) return ''
1388
1450
  if (!this.parentCalced) return '总得分: --'
1389
1451
  return (
1390
- this.buildGroupScoreText(section.container, section.nodes) ||
1391
- '总得分: --'
1452
+ this.buildGroupScoreText(
1453
+ groupNode,
1454
+ children || groupNode.children
1455
+ ) || '总得分: --'
1392
1456
  )
1393
1457
  },
1458
+ sectionScoreText(section) {
1459
+ if (!section || !section.container) return ''
1460
+ return this.groupScoreText(section.container, section.nodes)
1461
+ },
1462
+
1463
+ /** 内层分组标题旁得分小计:加总下属指标得分 */
1464
+ groupSubtotalText(groupNode, children) {
1465
+ if (!this.evalRootModel || !groupNode) return ''
1466
+ if (this.isSubModel(groupNode)) return ''
1467
+ if (!this.parentCalced) return '得分小计: --'
1468
+ const total = sumIndicatorScores(children || groupNode.children || [])
1469
+ if (total === null) return '得分小计: --'
1470
+ return `得分小计: ${this.formatScoreNum(total)}`
1471
+ },
1394
1472
 
1395
1473
  /**
1396
1474
  * 评价模型得分文案:总分(57) = 定量分析(7) + 定性分析(50)
@@ -112,12 +112,26 @@ export function hasStatPivotData(statNode) {
112
112
 
113
113
  /**
114
114
  * calType:0=指标取数(展示「xxx指标值/指标得分」),非 0(如 9=统计项运算)只展示统计项名。
115
- * 树节点通常只有 rankType,缺 calType 时用 rankType 推断,避免「平均」被当成指标取数导致列重复。
115
+ *
116
+ * 注意:指标取数节点落库时 rankType 常是「指标自身打分规则」,不是统计项 calType
117
+ *(见 statisticModel createIndicatorWithData: rankType = deepItem.rankType || calType)。
118
+ * 因此不能把 rankType 直接当 calType;有指标类型/itemID 时应视为指标取数(0)。
116
119
  */
117
120
  function resolveStatCalType(statLike) {
118
121
  if (!statLike) return 0
119
- if (statLike.calType !== '' && statLike.calType != null) return statLike.calType
120
- if (statLike.rankType !== '' && statLike.rankType != null) return statLike.rankType
122
+ if (statLike.calType !== '' && statLike.calType != null) {
123
+ return Number(statLike.calType)
124
+ }
125
+ const itemType = Number(statLike.itemType)
126
+ if (itemType === 21 || itemType === 22) return 0
127
+ // 无关联指标:保存时 rankType === 统计项 calType(如 9=统计项运算)
128
+ if (!statLike.itemID) {
129
+ if (statLike.rankType !== '' && statLike.rankType != null) {
130
+ return Number(statLike.rankType)
131
+ }
132
+ }
133
+ // 有 itemID 的统计项按指标取数处理
134
+ if (statLike.itemID) return 0
121
135
  return 0
122
136
  }
123
137
 
@@ -275,7 +289,7 @@ export async function buildStatModelTables(treeData, fetchScoreMap, modelWrap) {
275
289
  )
276
290
 
277
291
  const columns = [
278
- { label: '指标项名称', prop: 'name', width: '250px', align: 'center' }
292
+ { label: '指标项名称', prop: 'name', minWidth: 160, align: 'center' }
279
293
  ]
280
294
  const list = []
281
295
 
@@ -288,17 +302,18 @@ export async function buildStatModelTables(treeData, fetchScoreMap, modelWrap) {
288
302
  columns.push({
289
303
  label,
290
304
  prop: label,
291
- width: '250px',
305
+ minWidth: 140,
292
306
  align: 'center',
293
307
  slotName: label
294
308
  })
295
- if (statMeta.statShow == 1 && statMeta.calType == 0) {
309
+ // 测算页始终展示指标得分列;statShow 仅约束业务端(授信等)是否展示
310
+ if (statMeta.calType == 0) {
296
311
  columns.push({
297
312
  label: item.statsName + '指标得分',
298
313
  prop: item.statsName + '指标得分',
299
314
  slotName: item.statsName + '指标得分',
300
315
  disabled: true,
301
- width: '250px',
316
+ minWidth: 140,
302
317
  align: 'center'
303
318
  })
304
319
  }
@@ -326,12 +341,22 @@ export async function buildStatModelTables(treeData, fetchScoreMap, modelWrap) {
326
341
  statMeta.calType == 0
327
342
  ? item.statsName + '指标值'
328
343
  : item.statsName
329
- applyCellValue(item, optionList)
344
+ // 非手工填报:每次重建表格都取最新测算值;手工填报保留用户已输入
345
+ if (item.sourceType === 3) {
346
+ applyCellValue(item, optionList)
347
+ } else {
348
+ item.value = resolveCellValue(item, optionList)
349
+ }
330
350
  listItem[label] = item
331
351
  if (statMeta.calType == 0) {
352
+ const scoreVal =
353
+ item.result !== '' && item.result != null
354
+ ? item.result
355
+ : item.modifiedResult
332
356
  listItem[item.statsName + '指标得分'] = {
333
357
  ...item,
334
- value: item.result,
358
+ result: scoreVal,
359
+ value: scoreVal,
335
360
  sourceType: '',
336
361
  displayStyle: 0,
337
362
  calcState: false