@nstc-business/imes 1.1.7 → 1.1.10
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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* story=2206036 首次测算调整项假 0 不应覆盖指标计算结果
|
|
3
|
+
*/
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
|
|
7
|
+
const helpersSrc = fs.readFileSync(path.join(__dirname, 'helpers.js'), 'utf8')
|
|
8
|
+
// 仅抽取假 0 / 调整值回显相关纯函数,避免拉起 Vue/N 依赖
|
|
9
|
+
const start = helpersSrc.indexOf('function hasEchoValue')
|
|
10
|
+
const end = helpersSrc.indexOf('export function shouldApplyModifiedVal')
|
|
11
|
+
if (start < 0 || end < 0) {
|
|
12
|
+
throw new Error('无法定位假 0 相关函数片段')
|
|
13
|
+
}
|
|
14
|
+
const snippet = helpersSrc
|
|
15
|
+
.slice(start, end)
|
|
16
|
+
.replace(/export function resolveAdjustInputValue/g, 'function resolveAdjustInputValue')
|
|
17
|
+
.replace(/export function resolveCalcEchoValue/g, 'function resolveCalcEchoValue')
|
|
18
|
+
// resolveCalcEchoValue 依赖 isQualIndicator,测试中用桩
|
|
19
|
+
.replace(
|
|
20
|
+
/if \(target\.sourceType === 3 && !isQualIndicator\(target\)\)/,
|
|
21
|
+
'if (target.sourceType === 3 && !false)'
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const exported = {}
|
|
25
|
+
new Function(
|
|
26
|
+
'exports',
|
|
27
|
+
'function isQualIndicator(){return false}\n' +
|
|
28
|
+
snippet +
|
|
29
|
+
'\nexports.resolveAdjustInputValue = resolveAdjustInputValue' +
|
|
30
|
+
'\nexports.resolveCalcEchoValue = resolveCalcEchoValue'
|
|
31
|
+
)(exported)
|
|
32
|
+
|
|
33
|
+
const { resolveAdjustInputValue, resolveCalcEchoValue } = exported
|
|
34
|
+
|
|
35
|
+
function assert(cond, msg) {
|
|
36
|
+
if (!cond) throw new Error(msg)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 首次测算:后端 modifiedVal=0、result=0、有指标原值 → 假 0,不采用
|
|
40
|
+
assert(
|
|
41
|
+
resolveAdjustInputValue({
|
|
42
|
+
modifyRank: 2,
|
|
43
|
+
modifiedVal: 0,
|
|
44
|
+
indicatorResult: 2120912863.01,
|
|
45
|
+
result: 0,
|
|
46
|
+
calcVal: null
|
|
47
|
+
}) === undefined,
|
|
48
|
+
'有原值+result=0 的 modifiedVal=0 应视为假 0'
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// 尚无原值时的占位 0 → 假 0
|
|
52
|
+
assert(
|
|
53
|
+
resolveAdjustInputValue({
|
|
54
|
+
modifyRank: 2,
|
|
55
|
+
modifiedVal: 0,
|
|
56
|
+
indicatorResult: null,
|
|
57
|
+
result: null,
|
|
58
|
+
calcVal: null
|
|
59
|
+
}) === undefined,
|
|
60
|
+
'无原值时的 modifiedVal=0 应视为假 0'
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
// 用户真实调成 0:calcVal 落库为 0
|
|
64
|
+
assert(
|
|
65
|
+
resolveAdjustInputValue({
|
|
66
|
+
modifyRank: 2,
|
|
67
|
+
modifiedVal: 0,
|
|
68
|
+
indicatorResult: 2120912863.01,
|
|
69
|
+
result: 0,
|
|
70
|
+
calcVal: 0
|
|
71
|
+
}) === 0,
|
|
72
|
+
'calcVal=0 时应认定真实调整为 0'
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
// 测算回写:假 0 应回退到 indicatorResult
|
|
76
|
+
assert(
|
|
77
|
+
resolveCalcEchoValue(
|
|
78
|
+
{ sourceType: 1, itemType: 21, modifyRank: 2 },
|
|
79
|
+
{ modifiedVal: 0, indicatorResult: 353098.98, result: 0, calcVal: null }
|
|
80
|
+
) === 353098.98,
|
|
81
|
+
'假 0 回写应回退 indicatorResult'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
console.log('helpers.fakeZero.test.js passed')
|
|
@@ -680,14 +680,19 @@ function hasEchoValue(val) {
|
|
|
680
680
|
|
|
681
681
|
/**
|
|
682
682
|
* 是否为后端「未调整」时写入的假 0(null 被格式化成 0)。
|
|
683
|
-
*
|
|
684
|
-
* 时间:2026-
|
|
683
|
+
* 仅用于「调整指标值」(modifyRank=2):得分 result 在授信金额类模型中常为 0,不能当作真实调成 0 的证据。
|
|
684
|
+
* 时间:2026-10-26 16:26:37
|
|
685
|
+
* 人员:AI && 徐红飞
|
|
686
|
+
* 入参:modifiedVal/indicatorResult/result/calcVal
|
|
687
|
+
* 出参:boolean
|
|
688
|
+
* 方法内容:无原值时的 0、或有原值但 calcVal 未按 0 落库的 0,均视为未调整假 0(story 2206036)
|
|
685
689
|
*/
|
|
686
690
|
function isFakeUnadjustedZero(modifiedVal, indicatorResult, result, calcVal) {
|
|
687
691
|
if (Number(modifiedVal) !== 0) return false
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (
|
|
692
|
+
// 尚无指标原值:后端未调整占位 0,不能回显到输入框
|
|
693
|
+
if (!hasEchoValue(indicatorResult)) return true
|
|
694
|
+
if (Number(indicatorResult) === 0) return false
|
|
695
|
+
// 真实把指标值调成 0 后重算,calcVal 会按 0 落库;得分 result 对调整指标值场景不可靠(常为 0)
|
|
691
696
|
if (hasEchoValue(calcVal) && Number(calcVal) === 0) return false
|
|
692
697
|
return true
|
|
693
698
|
}
|
|
@@ -727,13 +732,17 @@ export function resolveCalcEchoValue(target, source) {
|
|
|
727
732
|
return undefined
|
|
728
733
|
}
|
|
729
734
|
if (target.itemType !== 22 && Number(target.modifyRank) === 2) {
|
|
730
|
-
|
|
735
|
+
const adjustVal = resolveAdjustInputValue({
|
|
731
736
|
modifyRank: 2,
|
|
732
737
|
modifiedVal: source.modifiedVal,
|
|
733
738
|
indicatorResult: source.indicatorResult,
|
|
734
739
|
result: source.result,
|
|
735
740
|
calcVal: source.calcVal
|
|
736
741
|
})
|
|
742
|
+
if (adjustVal !== undefined) return adjustVal
|
|
743
|
+
// 假 0 / 未调整:回退最新测算指标值,避免首次测算后输入框停在 0(story 2206036)
|
|
744
|
+
if (hasEchoValue(source.indicatorResult)) return source.indicatorResult
|
|
745
|
+
return undefined
|
|
737
746
|
}
|
|
738
747
|
return undefined
|
|
739
748
|
}
|
|
@@ -368,6 +368,14 @@ export default {
|
|
|
368
368
|
type: Object,
|
|
369
369
|
default: () => ({})
|
|
370
370
|
},
|
|
371
|
+
/**
|
|
372
|
+
* 打印/另存为文件名(可不含 .pdf)
|
|
373
|
+
* 业务侧传入如「XX公司授信测算详情」;未传时回退 model.code / 测算结果
|
|
374
|
+
*/
|
|
375
|
+
downloadFileName: {
|
|
376
|
+
type: String,
|
|
377
|
+
default: ''
|
|
378
|
+
},
|
|
371
379
|
/**
|
|
372
380
|
* 是否默认收起分组/分区
|
|
373
381
|
* false(默认):模型加载后全部展开
|
|
@@ -1186,11 +1194,9 @@ export default {
|
|
|
1186
1194
|
return
|
|
1187
1195
|
}
|
|
1188
1196
|
if (action === 'print') {
|
|
1189
|
-
printJS
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
base64: true
|
|
1193
|
-
})
|
|
1197
|
+
// PDF blob 直接 printJS 时,Edge「将打印输出另存为」文件名常为空;
|
|
1198
|
+
// 改为带 <title> 的 HTML 壳嵌入 PDF 再打印,便于另存为预填文件名
|
|
1199
|
+
this.printPdfWithFileName(res.data, this.resolvePrintFileBaseName())
|
|
1194
1200
|
} else {
|
|
1195
1201
|
this.downloadPdfBase64(res.data)
|
|
1196
1202
|
}
|
|
@@ -1201,6 +1207,84 @@ export default {
|
|
|
1201
1207
|
this.printLoading = false
|
|
1202
1208
|
}
|
|
1203
1209
|
},
|
|
1210
|
+
/**
|
|
1211
|
+
* 解析打印/下载文件名(不含 .pdf)
|
|
1212
|
+
* 时间:2026-10-29 17:27:04
|
|
1213
|
+
* 人员:AI && 徐红飞
|
|
1214
|
+
* 入参:无
|
|
1215
|
+
* 出参:文件名基名
|
|
1216
|
+
* 方法内容:优先 downloadFileName,否则 model.code,再否则「测算结果」
|
|
1217
|
+
*/
|
|
1218
|
+
resolvePrintFileBaseName() {
|
|
1219
|
+
const raw = this.downloadFileName || (this.model && this.model.code) || '测算结果'
|
|
1220
|
+
return String(raw).replace(/\.pdf$/i, '')
|
|
1221
|
+
},
|
|
1222
|
+
/**
|
|
1223
|
+
* 带文件名打印 PDF(base64)
|
|
1224
|
+
* 时间:2026-10-29 18:16:00
|
|
1225
|
+
* 人员:AI && 徐红飞
|
|
1226
|
+
* 入参:base64 - PDF 内容;fileBaseName - 另存为预填名(不含 .pdf)
|
|
1227
|
+
* 出参:无
|
|
1228
|
+
* 方法内容:用 HTML iframe + title + embed 打印,规避 PDF blob 直接打印时另存为文件名为空
|
|
1229
|
+
*/
|
|
1230
|
+
printPdfWithFileName(base64, fileBaseName) {
|
|
1231
|
+
const title = String(fileBaseName || '测算结果').replace(/[\\/:*?"<>|]/g, '_')
|
|
1232
|
+
const pure = String(base64 || '').replace(/^data:application\/pdf;base64,/, '')
|
|
1233
|
+
const binary = atob(pure)
|
|
1234
|
+
const len = binary.length
|
|
1235
|
+
const bytes = new Uint8Array(len)
|
|
1236
|
+
for (let i = 0; i < len; i++) {
|
|
1237
|
+
bytes[i] = binary.charCodeAt(i)
|
|
1238
|
+
}
|
|
1239
|
+
const blob = new Blob([bytes], { type: 'application/pdf' })
|
|
1240
|
+
const pdfUrl = URL.createObjectURL(blob)
|
|
1241
|
+
const iframe = document.createElement('iframe')
|
|
1242
|
+
iframe.setAttribute(
|
|
1243
|
+
'style',
|
|
1244
|
+
'position:fixed;right:0;bottom:0;width:0;height:0;border:0;visibility:hidden'
|
|
1245
|
+
)
|
|
1246
|
+
document.body.appendChild(iframe)
|
|
1247
|
+
const frameWin = iframe.contentWindow
|
|
1248
|
+
const frameDoc = frameWin.document
|
|
1249
|
+
const safeTitle = title
|
|
1250
|
+
.replace(/&/g, '&')
|
|
1251
|
+
.replace(/</g, '<')
|
|
1252
|
+
.replace(/>/g, '>')
|
|
1253
|
+
.replace(/"/g, '"')
|
|
1254
|
+
frameDoc.open()
|
|
1255
|
+
frameDoc.write(
|
|
1256
|
+
`<!DOCTYPE html><html><head><meta charset="utf-8"><title>${safeTitle}</title></head>` +
|
|
1257
|
+
`<body style="margin:0"><embed src="${pdfUrl}" type="application/pdf" width="100%" height="100%" /></body></html>`
|
|
1258
|
+
)
|
|
1259
|
+
frameDoc.close()
|
|
1260
|
+
const cleanup = () => {
|
|
1261
|
+
try {
|
|
1262
|
+
if (iframe.parentNode) {
|
|
1263
|
+
iframe.parentNode.removeChild(iframe)
|
|
1264
|
+
}
|
|
1265
|
+
} catch (e) {
|
|
1266
|
+
/* ignore */
|
|
1267
|
+
}
|
|
1268
|
+
URL.revokeObjectURL(pdfUrl)
|
|
1269
|
+
}
|
|
1270
|
+
const doPrint = () => {
|
|
1271
|
+
try {
|
|
1272
|
+
frameWin.focus()
|
|
1273
|
+
frameWin.print()
|
|
1274
|
+
} catch (e) {
|
|
1275
|
+
// 回退 printJS(可能仍无文件名,但保证能打)
|
|
1276
|
+
printJS({
|
|
1277
|
+
printable: pure,
|
|
1278
|
+
type: 'pdf',
|
|
1279
|
+
base64: true,
|
|
1280
|
+
documentTitle: title
|
|
1281
|
+
})
|
|
1282
|
+
}
|
|
1283
|
+
setTimeout(cleanup, 2000)
|
|
1284
|
+
}
|
|
1285
|
+
// embed 加载需要一点时间;onload 在部分浏览器对 srcdoc/write 不可靠,用延时兜底
|
|
1286
|
+
setTimeout(doPrint, 400)
|
|
1287
|
+
},
|
|
1204
1288
|
downloadPdfBase64(base64) {
|
|
1205
1289
|
const pure = String(base64 || '').replace(/^data:application\/pdf;base64,/, '')
|
|
1206
1290
|
const binary = atob(pure)
|
|
@@ -1213,7 +1297,7 @@ export default {
|
|
|
1213
1297
|
const url = URL.createObjectURL(blob)
|
|
1214
1298
|
const a = document.createElement('a')
|
|
1215
1299
|
a.href = url
|
|
1216
|
-
a.download = `${
|
|
1300
|
+
a.download = `${this.resolvePrintFileBaseName()}.pdf`
|
|
1217
1301
|
document.body.appendChild(a)
|
|
1218
1302
|
a.click()
|
|
1219
1303
|
document.body.removeChild(a)
|