@nstc-business/imes 1.0.19 → 1.0.21

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nstc-business/imes",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "private": false,
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -101,8 +101,20 @@
101
101
  label-width="10em"
102
102
  v-if="modelData.sataMode !== 1"
103
103
  >
104
- <el-form-item label="模型得分:" v-if="modelData.type === 1">
105
- <span>{{ modelData.result.toFixed(modelData.digits) }}</span>
104
+ <el-form-item
105
+ label="模型得分:"
106
+ v-if="
107
+ modelData.type === 1 ||
108
+ (modelData.type === 3 && modelData.sataMode === 2)
109
+ "
110
+ >
111
+ <span>
112
+ {{
113
+ modelData.result || modelData.result === 0
114
+ ? Number(modelData.result).toFixed(modelData.digits)
115
+ : '--'
116
+ }}
117
+ </span>
106
118
  </el-form-item>
107
119
  <el-form-item label="模型结果:" v-if="modelData.type !== 3">
108
120
  <span>{{ modelData.resultText }}</span>
@@ -79,7 +79,8 @@ export default {
79
79
  methods: {
80
80
  displayValue(cell) {
81
81
  if (this.column.disabled) {
82
- const val = cell.result
82
+ const val =
83
+ cell.result !== '' && cell.result != null ? cell.result : cell.value
83
84
  if (val === '' || val == null) return ''
84
85
  let text = Number(val).toFixed(this.digits)
85
86
  if (cell.showStyle === 2) {
@@ -114,6 +114,30 @@ export default {
114
114
  return []
115
115
  }
116
116
  },
117
+ /** 嵌套统计模型时父模没有 modelStatsSetupVOS,按子模型 ID 拉取统计项配置 */
118
+ async ensureModelStatsSetup(modelWrap) {
119
+ const existing =
120
+ (modelWrap && modelWrap.modelStatsSetupVOS) ||
121
+ (this.subModel && this.subModel.modelStatsSetupVOS)
122
+ if (Array.isArray(existing) && existing.length) return existing
123
+ const id =
124
+ (this.subModel && (this.subModel.itemID || this.subModel.modelID)) ||
125
+ (modelWrap && modelWrap.modelID)
126
+ if (!id) return null
127
+ try {
128
+ const { code, data } = await this.$axios.post(
129
+ withRequestTimestamp('/imes/v1/indicatorModel/getModelById'),
130
+ { id }
131
+ )
132
+ if (code === 0 && data && Array.isArray(data.modelStatsSetupVOS)) {
133
+ this.$set(this.subModel, 'modelStatsSetupVOS', data.modelStatsSetupVOS)
134
+ return data.modelStatsSetupVOS
135
+ }
136
+ } catch (e) {
137
+ /* ignore */
138
+ }
139
+ return null
140
+ },
117
141
  async loadTables() {
118
142
  if (!this.subModel || !hasStatPivotNodes(this.subModel)) {
119
143
  this.tableData = []
@@ -121,9 +145,16 @@ export default {
121
145
  }
122
146
  this.loading = true
123
147
  try {
148
+ const modelWrap = this.ctx.modelWrap || {}
149
+ await this.ensureModelStatsSetup(modelWrap)
124
150
  this.tableData = await buildStatModelTables(
125
151
  this.subModel,
126
- id => this.fetchScoreMap(id)
152
+ id => this.fetchScoreMap(id),
153
+ {
154
+ ...modelWrap,
155
+ modelStatsSetupVOS:
156
+ this.subModel.modelStatsSetupVOS || modelWrap.modelStatsSetupVOS
157
+ }
127
158
  )
128
159
  } finally {
129
160
  this.loading = false
@@ -89,9 +89,9 @@ export const SUB_MODEL_LABEL = '测算子模型'
89
89
  /** 子模型独立测算按钮文案 */
90
90
  export const SUB_MODEL_CALC_BTN = '子模型测算'
91
91
 
92
- /** 子模型测算结果展示(优先 resultText,否则格式化 result) 统计模型没有测算结果*/
92
+ /** 子模型测算结果展示(优先 resultText,否则格式化 result) */
93
93
  export function subModelResultText(row, modelWrap) {
94
- if (!row || row.itemType ===13) return ''
94
+ if (!row) return ''
95
95
  if (row.resultText != null && row.resultText !== '') {
96
96
  return row.resultText
97
97
  }
@@ -47,7 +47,7 @@
47
47
  </span>
48
48
  <span class="meta-item">
49
49
  <span class="meta-label">测算结果</span>
50
- <span class="meta-value meta-result">{{ resultText || '--' }}</span>
50
+ <span class="meta-value meta-result">{{ displayResultText }}</span>
51
51
  </span>
52
52
  </div>
53
53
 
@@ -386,18 +386,29 @@ export default {
386
386
  statRootModel() {
387
387
  return checkStatRootModel(this.model)
388
388
  },
389
- /** 顶层统计模型的根节点 */
389
+ /** 顶层统计模型的根节点(必须返回原引用,保证 _statTableVersion 能触发子表刷新) */
390
390
  statRootNode() {
391
391
  const root = this.model.modelTreeVO || {}
392
- return {
393
- ...root,
394
- itemID: root.itemID || root.modelID || this.model.modelID
392
+ const itemID = root.itemID || root.modelID || this.model.modelID
393
+ if (itemID && root.itemID !== itemID) {
394
+ this.$set(root, 'itemID', itemID)
395
395
  }
396
+ return root
396
397
  },
397
398
  modelVersionText() {
398
399
  if (this.model.version === '' || this.model.version == null) return '--'
399
400
  return `版本${this.model.version}`
400
401
  },
402
+ /** 测算结果展示:优先 resultText,统计模型计算模式可回退到 result */
403
+ displayResultText() {
404
+ if (this.resultText !== '' && this.resultText != null) {
405
+ return this.resultText
406
+ }
407
+ if (this.calcResult !== '' && this.calcResult != null) {
408
+ return this.calcResult
409
+ }
410
+ return '--'
411
+ },
401
412
  /** 评价模型展示「模型得分」,计算模型展示「模型公式」 */
402
413
  formulaRowLabel() {
403
414
  if (this.evalRootModel) return '模型得分:'
@@ -1054,6 +1065,10 @@ export default {
1054
1065
  if (this.statRootModel) {
1055
1066
  const treeRoot = resData.modelTreeVO || {}
1056
1067
  preprocessStatIndicatorDeepList(treeRoot.children || [])
1068
+ if (resData.modelStatsSetupVOS) {
1069
+ treeRoot.modelStatsSetupVOS = resData.modelStatsSetupVOS
1070
+ this.$set(this.model, 'modelStatsSetupVOS', resData.modelStatsSetupVOS)
1071
+ }
1057
1072
  mergeStatModelNode(this.model.modelTreeVO || {}, treeRoot)
1058
1073
  this.bumpStatTableVersion(this.model.modelTreeVO)
1059
1074
  }
@@ -1073,7 +1088,7 @@ export default {
1073
1088
  this.businessModuleRemark =resData.businessModuleRemark
1074
1089
  this.$set(this.model, 'logId', resData.logId || '')
1075
1090
  this.$set(this.model, 'maxReportTem', resData.maxReportTem || '')
1076
- this.$emit('calcResult', resData.resultText)
1091
+ this.$emit('calcResult', this.displayResultText === '--' ? resData.resultText : this.displayResultText)
1077
1092
  this.$emit('getLogId', resData.logId)
1078
1093
  this.$emit('calcComplete', {
1079
1094
  ...resData,
@@ -1131,6 +1146,9 @@ export default {
1131
1146
  if (isStatModel(subModel)) {
1132
1147
  const treeRoot = data.modelTreeVO || {}
1133
1148
  preprocessStatIndicatorDeepList(treeRoot.children || [])
1149
+ if (data.modelStatsSetupVOS) {
1150
+ treeRoot.modelStatsSetupVOS = data.modelStatsSetupVOS
1151
+ }
1134
1152
  mergeStatModelNode(subModel, treeRoot)
1135
1153
  this.bumpStatTableVersion(subModel)
1136
1154
  }
@@ -1,7 +1,12 @@
1
1
  /**
2
2
  * 统计模型行转列:以 isLeaf=2 为横向展开边界
3
3
  * 逻辑参考 EvaOfCreditConditionsRewrite.initColumnsRewrite
4
- * statCalResult 仅作辅助元数据(列配置/标题/合计),不作为是否行转列的判断条件
4
+ * statCalResult / modelStatsSetupVOS 作辅助元数据(列配置/标题/合计),
5
+ * 不作为是否行转列的判断条件。
6
+ *
7
+ * 注意:getModelTree 对统计模型通常不下发 statCalResult(挂在评价模型的
8
+ * 定量分析/定性分析节点上),标题需按 pid 反查分组目录,列配置优先用
9
+ * modelStatsSetupVOS,避免回退到第一个指标名 / 默认 calType=0 导致错列。
5
10
  */
6
11
 
7
12
  function hasFilledValue(val) {
@@ -105,14 +110,45 @@ export function hasStatPivotData(statNode) {
105
110
  return hasStatPivotNodes(statNode)
106
111
  }
107
112
 
113
+ /**
114
+ * calType:0=指标取数(展示「xxx指标值/指标得分」),非 0(如 9=统计项运算)只展示统计项名。
115
+ *
116
+ * 注意:指标取数节点落库时 rankType 常是「指标自身打分规则」,不是统计项 calType
117
+ *(见 statisticModel createIndicatorWithData: rankType = deepItem.rankType || calType)。
118
+ * 因此不能把 rankType 直接当 calType;有指标类型/itemID 时应视为指标取数(0)。
119
+ */
120
+ function resolveStatCalType(statLike) {
121
+ if (!statLike) return 0
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
135
+ return 0
136
+ }
137
+
108
138
  function resolveStatMeta(statsResultList, statChild) {
109
139
  const fromList = (statsResultList || []).find(
110
140
  s => s.name == statChild.statsName
111
141
  )
112
- if (fromList) return fromList
142
+ if (fromList) {
143
+ return {
144
+ ...fromList,
145
+ name: fromList.name || statChild.statsName,
146
+ calType: resolveStatCalType(fromList)
147
+ }
148
+ }
113
149
  return {
114
150
  name: statChild.statsName,
115
- calType: statChild.calType ?? 0,
151
+ calType: resolveStatCalType(statChild),
116
152
  statShow: statChild.statShow ?? 1,
117
153
  statCalMethod: statChild.statCalMethod,
118
154
  statResult: statChild.statResult
@@ -120,25 +156,104 @@ function resolveStatMeta(statsResultList, statChild) {
120
156
  }
121
157
 
122
158
  function buildStatsResultListFromChildren(statChildren) {
123
- return statChildren.map(c => ({
159
+ return (statChildren || []).map(c => ({
124
160
  name: c.statsName,
125
- calType: c.calType ?? 0,
161
+ calType: resolveStatCalType(c),
126
162
  statShow: c.statShow ?? 1,
127
163
  statCalMethod: c.statCalMethod,
128
164
  statResult: c.statResult
129
165
  }))
130
166
  }
131
167
 
132
- function resolveTableBlockMeta(treeData, pid, indicatorItems) {
133
- const calBlock = (treeData.statCalResult || []).find(r => r.pid === pid)
134
- const first = indicatorItems[0]
135
- const firstStatChildren = statItemChildren(first)
168
+ function normalizeStatsResultList(list) {
169
+ if (!Array.isArray(list) || !list.length) return null
170
+ return list.map(s => ({
171
+ name: s.name || s.statsName,
172
+ calType: resolveStatCalType(s),
173
+ statShow: s.statShow ?? 1,
174
+ statCalMethod: s.statCalMethod,
175
+ statResult: s.statResult
176
+ }))
177
+ }
178
+
179
+ /** 在节点树中按 tid 查找节点(含根) */
180
+ function findNodeByTid(root, tid) {
181
+ if (!root || tid == null || tid === '') return null
182
+ if (root.tid === tid || root.TID === tid) return root
183
+ const stack = Array.isArray(root.children) ? [...root.children] : []
184
+ while (stack.length) {
185
+ const node = stack.shift()
186
+ if (!node) continue
187
+ if (node.tid === tid || node.TID === tid) return node
188
+ if (Array.isArray(node.children) && node.children.length) {
189
+ stack.push(...node.children)
190
+ }
191
+ }
192
+ return null
193
+ }
194
+
195
+ /** 收集整棵树上所有 statCalResult(挂在定量分析/定性分析等祖先节点上) */
196
+ function collectAllStatCalResults(root, bucket = []) {
197
+ if (!root) return bucket
198
+ if (Array.isArray(root.statCalResult) && root.statCalResult.length) {
199
+ bucket.push(...root.statCalResult)
200
+ }
201
+ ;(root.children || []).forEach(child => collectAllStatCalResults(child, bucket))
202
+ return bucket
203
+ }
204
+
205
+ function findStatCalBlock(treeData, pid, modelWrap) {
206
+ const local = (treeData && treeData.statCalResult) || []
207
+ let block = local.find(r => r.pid === pid)
208
+ if (block) return block
209
+ const modelRoot = modelWrap && modelWrap.modelTreeVO
210
+ if (!modelRoot) return null
211
+ return collectAllStatCalResults(modelRoot).find(r => r.pid === pid) || null
212
+ }
213
+
214
+ /**
215
+ * 分组标题优先用 statCalResult.statIndicatorName(分组目录名),
216
+ * 否则按 pid 反查树节点 name;禁止回退到第一个指标 name(缺陷现象)。
217
+ */
218
+ function resolveStatIndicatorName(calBlock, treeData, pid, indicatorItems, modelWrap) {
219
+ if (calBlock && calBlock.statIndicatorName) return calBlock.statIndicatorName
220
+ const fromTree =
221
+ findNodeByTid(treeData, pid) ||
222
+ findNodeByTid(modelWrap && modelWrap.modelTreeVO, pid)
223
+ if (fromTree && fromTree.name) return fromTree.name
224
+ const first = indicatorItems && indicatorItems[0]
225
+ if (first && first.folderName) return first.folderName
226
+ return ''
227
+ }
228
+
229
+ function resolveStatsResultList(calBlock, treeData, indicatorItems, modelWrap) {
230
+ const fromBlock = normalizeStatsResultList(calBlock && calBlock.statsResultList)
231
+ if (fromBlock) return fromBlock
232
+ const fromModel = normalizeStatsResultList(
233
+ (modelWrap && modelWrap.modelStatsSetupVOS) ||
234
+ (treeData && treeData.modelStatsSetupVOS)
235
+ )
236
+ if (fromModel) return fromModel
237
+ const first = indicatorItems && indicatorItems[0]
238
+ return buildStatsResultListFromChildren(statItemChildren(first))
239
+ }
240
+
241
+ function resolveTableBlockMeta(treeData, pid, indicatorItems, modelWrap) {
242
+ const calBlock = findStatCalBlock(treeData, pid, modelWrap)
136
243
  return {
137
- statIndicatorName:
138
- calBlock?.statIndicatorName || first?.folderName || first?.name || '',
139
- statsResultList:
140
- calBlock?.statsResultList ||
141
- buildStatsResultListFromChildren(firstStatChildren)
244
+ statIndicatorName: resolveStatIndicatorName(
245
+ calBlock,
246
+ treeData,
247
+ pid,
248
+ indicatorItems,
249
+ modelWrap
250
+ ),
251
+ statsResultList: resolveStatsResultList(
252
+ calBlock,
253
+ treeData,
254
+ indicatorItems,
255
+ modelWrap
256
+ )
142
257
  }
143
258
  }
144
259
 
@@ -155,8 +270,9 @@ function groupIsLeaf2ByPid(nodes) {
155
270
  /**
156
271
  * @param {Object} treeData 统计模型节点
157
272
  * @param {Function} fetchScoreMap async (itemID) => optionList
273
+ * @param {Object} [modelWrap] 整模 ModelVO(含 modelTreeVO / modelStatsSetupVOS)
158
274
  */
159
- export async function buildStatModelTables(treeData, fetchScoreMap) {
275
+ export async function buildStatModelTables(treeData, fetchScoreMap, modelWrap) {
160
276
  const isLeaf2Nodes = collectIsLeaf2Nodes(treeData.children || [])
161
277
  if (!isLeaf2Nodes.length) return []
162
278
 
@@ -168,7 +284,8 @@ export async function buildStatModelTables(treeData, fetchScoreMap) {
168
284
  const { statIndicatorName, statsResultList } = resolveTableBlockMeta(
169
285
  treeData,
170
286
  pid,
171
- indicatorItems
287
+ indicatorItems,
288
+ modelWrap
172
289
  )
173
290
 
174
291
  const columns = [
@@ -189,7 +306,8 @@ export async function buildStatModelTables(treeData, fetchScoreMap) {
189
306
  align: 'center',
190
307
  slotName: label
191
308
  })
192
- if (statMeta.statShow == 1 && statMeta.calType == 0) {
309
+ // 测算页始终展示指标得分列;statShow 仅约束业务端(授信等)是否展示
310
+ if (statMeta.calType == 0) {
193
311
  columns.push({
194
312
  label: item.statsName + '指标得分',
195
313
  prop: item.statsName + '指标得分',
@@ -223,12 +341,22 @@ export async function buildStatModelTables(treeData, fetchScoreMap) {
223
341
  statMeta.calType == 0
224
342
  ? item.statsName + '指标值'
225
343
  : item.statsName
226
- applyCellValue(item, optionList)
344
+ // 非手工填报:每次重建表格都取最新测算值;手工填报保留用户已输入
345
+ if (item.sourceType === 3) {
346
+ applyCellValue(item, optionList)
347
+ } else {
348
+ item.value = resolveCellValue(item, optionList)
349
+ }
227
350
  listItem[label] = item
228
351
  if (statMeta.calType == 0) {
352
+ const scoreVal =
353
+ item.result !== '' && item.result != null
354
+ ? item.result
355
+ : item.modifiedResult
229
356
  listItem[item.statsName + '指标得分'] = {
230
357
  ...item,
231
- value: item.result,
358
+ result: scoreVal,
359
+ value: scoreVal,
232
360
  sourceType: '',
233
361
  displayStyle: 0,
234
362
  calcState: false
@@ -342,6 +470,9 @@ export function mergeStatModelNode(target, source) {
342
470
  if (source.statsResultList) {
343
471
  target.statsResultList = source.statsResultList
344
472
  }
473
+ if (source.modelStatsSetupVOS) {
474
+ target.modelStatsSetupVOS = source.modelStatsSetupVOS
475
+ }
345
476
  const fields = [
346
477
  'result',
347
478
  'resultText',