@nstc-business/imes 1.1.10 → 1.1.12

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.1.10",
3
+ "version": "1.1.12",
4
4
  "private": false,
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1209,42 +1209,70 @@ export default {
1209
1209
  },
1210
1210
  /**
1211
1211
  * 解析打印/下载文件名(不含 .pdf)
1212
- * 时间:2026-10-29 17:27:04
1212
+ * 时间:2026-10-29 18:30:29
1213
1213
  * 人员:AI && 徐红飞
1214
1214
  * 入参:无
1215
- * 出参:文件名基名
1216
- * 方法内容:优先 downloadFileName,否则 model.code,再否则「测算结果」
1215
+ * 出参:文件名基名(已清洗非法字符)
1216
+ * 方法内容:优先 downloadFileName,否则 model.code;统一清洗 \/:*?"<>| 供下载与打印共用
1217
1217
  */
1218
1218
  resolvePrintFileBaseName() {
1219
1219
  const raw = this.downloadFileName || (this.model && this.model.code) || '测算结果'
1220
- return String(raw).replace(/\.pdf$/i, '')
1220
+ return String(raw)
1221
+ .replace(/\.pdf$/i, '')
1222
+ .replace(/[\\/:*?"<>|]/g, '_')
1223
+ .trim() || '测算结果'
1221
1224
  },
1222
1225
  /**
1223
1226
  * 带文件名打印 PDF(base64)
1224
- * 时间:2026-10-29 18:16:00
1227
+ * 时间:2026-10-29 18:30:29
1225
1228
  * 人员:AI && 徐红飞
1226
1229
  * 入参:base64 - PDF 内容;fileBaseName - 另存为预填名(不含 .pdf)
1227
1230
  * 出参:无
1228
- * 方法内容:用 HTML iframe + title + embed 打印,规避 PDF blob 直接打印时另存为文件名为空
1231
+ * 方法内容:HTML iframe+title+embed 打印;等 embed load 后再 print;afterprint/长兜底再 cleanup,避免另存为未完成就 revoke
1229
1232
  */
1230
1233
  printPdfWithFileName(base64, fileBaseName) {
1231
- const title = String(fileBaseName || '测算结果').replace(/[\\/:*?"<>|]/g, '_')
1234
+ const title = String(fileBaseName || '测算结果').replace(/[\\/:*?"<>|]/g, '_').trim() || '测算结果'
1232
1235
  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)
1236
+ let bytes
1237
+ try {
1238
+ const binary = atob(pure)
1239
+ const len = binary.length
1240
+ bytes = new Uint8Array(len)
1241
+ for (let i = 0; i < len; i++) {
1242
+ bytes[i] = binary.charCodeAt(i)
1243
+ }
1244
+ } catch (e) {
1245
+ printJS({
1246
+ printable: pure,
1247
+ type: 'pdf',
1248
+ base64: true,
1249
+ documentTitle: title
1250
+ })
1251
+ return
1238
1252
  }
1239
1253
  const blob = new Blob([bytes], { type: 'application/pdf' })
1240
1254
  const pdfUrl = URL.createObjectURL(blob)
1241
1255
  const iframe = document.createElement('iframe')
1256
+ // 非零屏外尺寸:部分 Edge 对 0x0 iframe 打印/另存为不稳定
1242
1257
  iframe.setAttribute(
1243
1258
  'style',
1244
- 'position:fixed;right:0;bottom:0;width:0;height:0;border:0;visibility:hidden'
1259
+ 'position:fixed;left:-9999px;top:0;width:800px;height:600px;border:0;opacity:0;pointer-events:none'
1245
1260
  )
1246
1261
  document.body.appendChild(iframe)
1247
1262
  const frameWin = iframe.contentWindow
1263
+ if (!frameWin) {
1264
+ URL.revokeObjectURL(pdfUrl)
1265
+ if (iframe.parentNode) {
1266
+ iframe.parentNode.removeChild(iframe)
1267
+ }
1268
+ printJS({
1269
+ printable: pure,
1270
+ type: 'pdf',
1271
+ base64: true,
1272
+ documentTitle: title
1273
+ })
1274
+ return
1275
+ }
1248
1276
  const frameDoc = frameWin.document
1249
1277
  const safeTitle = title
1250
1278
  .replace(/&/g, '&amp;')
@@ -1254,10 +1282,21 @@ export default {
1254
1282
  frameDoc.open()
1255
1283
  frameDoc.write(
1256
1284
  `<!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>`
1285
+ `<body style="margin:0"><embed id="pdfEmbed" src="${pdfUrl}" type="application/pdf" width="100%" height="100%" /></body></html>`
1258
1286
  )
1259
1287
  frameDoc.close()
1288
+
1289
+ let cleaned = false
1260
1290
  const cleanup = () => {
1291
+ if (cleaned) {
1292
+ return
1293
+ }
1294
+ cleaned = true
1295
+ try {
1296
+ frameWin.removeEventListener('afterprint', onAfterPrint)
1297
+ } catch (e) {
1298
+ /* ignore */
1299
+ }
1261
1300
  try {
1262
1301
  if (iframe.parentNode) {
1263
1302
  iframe.parentNode.removeChild(iframe)
@@ -1267,23 +1306,42 @@ export default {
1267
1306
  }
1268
1307
  URL.revokeObjectURL(pdfUrl)
1269
1308
  }
1309
+ const onAfterPrint = () => {
1310
+ cleanup()
1311
+ }
1270
1312
  const doPrint = () => {
1271
1313
  try {
1314
+ frameWin.addEventListener('afterprint', onAfterPrint)
1272
1315
  frameWin.focus()
1273
1316
  frameWin.print()
1274
1317
  } catch (e) {
1275
- // 回退 printJS(可能仍无文件名,但保证能打)
1276
1318
  printJS({
1277
1319
  printable: pure,
1278
1320
  type: 'pdf',
1279
1321
  base64: true,
1280
1322
  documentTitle: title
1281
1323
  })
1324
+ cleanup()
1325
+ return
1282
1326
  }
1283
- setTimeout(cleanup, 2000)
1327
+ // 另存为对话框可能远超数秒;afterprint 优先,60s 兜底防泄漏
1328
+ setTimeout(cleanup, 60000)
1329
+ }
1330
+
1331
+ const embed = frameDoc.getElementById('pdfEmbed')
1332
+ let printed = false
1333
+ const tryPrint = () => {
1334
+ if (printed) {
1335
+ return
1336
+ }
1337
+ printed = true
1338
+ doPrint()
1339
+ }
1340
+ if (embed) {
1341
+ embed.addEventListener('load', tryPrint)
1284
1342
  }
1285
- // embed 加载需要一点时间;onload 在部分浏览器对 srcdoc/write 不可靠,用延时兜底
1286
- setTimeout(doPrint, 400)
1343
+ // embed load 在部分浏览器不可靠,最长等 3s 再打
1344
+ setTimeout(tryPrint, 3000)
1287
1345
  },
1288
1346
  downloadPdfBase64(base64) {
1289
1347
  const pure = String(base64 || '').replace(/^data:application\/pdf;base64,/, '')