cloud-web-corejs 1.1.0-dev.6 → 1.1.0-dev.60

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.
Files changed (498) hide show
  1. package/README.md +38 -0
  2. package/bin/core-resources.js +16 -0
  3. package/bin/create-child-app.js +34 -0
  4. package/docs/child-app/README.md +95 -0
  5. package/docs/child-app/activiti-resources.md +63 -0
  6. package/docs/child-app/generate-prompt.md +55 -0
  7. package/docs/child-app/luckysheet-resources.md +80 -0
  8. package/docs/child-app/public-resources-plan.md +288 -0
  9. package/docs/child-app/resources-implementation.md +101 -0
  10. package/docs/child-app/tinymce-resources.md +88 -0
  11. package/docs/child-app/validation.md +34 -0
  12. package/docs/qiankun/346/216/245/345/205/245/345/215/217/350/256/256.md +298 -0
  13. package/docs/qiankun/350/257/246/346/203/205/346/211/277/350/275/275/345/255/220/345/272/224/347/224/250/345/257/271/346/216/245/346/270/205/345/215/225.md +111 -0
  14. package/package.json +29 -4
  15. package/resource-tools/index.js +126 -0
  16. package/src/app-bootstrap.js +531 -0
  17. package/src/app-bootstrap.sync.js +502 -0
  18. package/src/boot-assets.legacy.js +13 -0
  19. package/src/boot-assets.next.js +7 -0
  20. package/src/components/Qrcode/fileParse.vue +11 -0
  21. package/src/components/Tinymce/dynamicLoadScript.js +51 -59
  22. package/src/components/Tinymce/index.vue +382 -336
  23. package/src/components/Tinymce/mce-chrome.scss +150 -0
  24. package/src/components/VabUpload/image-viewer.vue +18 -10
  25. package/src/components/VabUpload/index.js +11 -4
  26. package/src/components/VabUpload/index.vue +1 -1
  27. package/src/components/VabUpload/mixins/fileLibraryDialog.js +2 -0
  28. package/src/components/VabUpload/mixins.js +72 -40
  29. package/src/components/VabUpload/partUploadConfig.js +95 -0
  30. package/src/components/VabUpload/privateProfileDialogMixins.js +2 -0
  31. package/src/components/VabUpload/style.scss +282 -0
  32. package/src/components/VabUpload/view.vue +19 -18
  33. package/src/components/baseArea/index.vue +2 -2
  34. package/src/components/baseInputExport/mixins.js +1 -6
  35. package/src/components/bizTab/BizTabListPage.vue +66 -33
  36. package/src/components/bizTab/README.md +3 -1
  37. package/src/components/bizTab/bizBillDetailMixin.js +2 -0
  38. package/src/components/code-editor/ace-chrome.scss +17 -0
  39. package/src/components/code-editor/index.vue +361 -340
  40. package/src/components/errorMsg/index.vue +142 -13
  41. package/src/components/errorMsg/mixins.js +53 -0
  42. package/src/components/excelExport/conditionExportConfig.js +112 -0
  43. package/src/components/excelExport/exportColumnMemory.js +118 -0
  44. package/src/components/excelExport/exportFieldDialog.vue +50 -38
  45. package/src/components/excelExport/exportType.js +8 -0
  46. package/src/components/excelExport/index.js +30 -13
  47. package/src/components/excelExport/index.vue +7 -1
  48. package/src/components/excelExport/mixins.js +782 -293
  49. package/src/components/excelImport/index.vue +156 -156
  50. package/src/components/excelImport/mixins.js +888 -834
  51. package/src/components/fileLibrary/mixins/indexMixins.js +2 -0
  52. package/src/components/fileLibrary/style.scss +579 -0
  53. package/src/components/formDialog/index.vue +11 -8
  54. package/src/components/hiprint/style.scss +188 -0
  55. package/src/components/hiprint/view/json-view.vue +4 -4
  56. package/src/components/jdPrint/mixins.js +2 -0
  57. package/src/components/jsonImport/index.vue +1 -1
  58. package/src/components/jsonImport/mixins.js +6 -10
  59. package/src/components/langImport/index.vue +1 -1
  60. package/src/components/langImport/mixins.js +6 -10
  61. package/src/components/luckysheet/loadResources.js +64 -0
  62. package/src/components/luckysheet/view.vue +239 -210
  63. package/src/components/mobile/buttonGroup/index.vue +1 -1
  64. package/src/components/mobile/wf/addTaskUserdialog.vue +100 -80
  65. package/src/components/mobile/wf/content.vue +1734 -1662
  66. package/src/components/mobile/wf/deleteTaskUserDialog.vue +73 -54
  67. package/src/components/mobile/wf/selectUserDialog.vue +118 -0
  68. package/src/components/mobile/wf/setCandidateDialog.vue +69 -48
  69. package/src/components/mobile/wf/urgingDialog.vue +75 -52
  70. package/src/components/mobile/wf/wfModifyDialog.vue +4 -0
  71. package/src/components/obsUpload/index.js +4 -1
  72. package/src/components/obsUpload/index.vue +1 -1
  73. package/src/components/obsUpload/mixins.js +45 -15
  74. package/src/components/onlineTalk/index.vue +4 -4
  75. package/src/components/onlineTalk/mixins.js +21 -7
  76. package/src/components/table/index.js +96 -81
  77. package/src/components/table/plugins/cell-area/index.js +10 -3
  78. package/src/components/table/style.scss +1399 -0
  79. package/src/components/table/vxeFilter/mixin.js +28 -18
  80. package/src/components/wf/content.vue +3 -2
  81. package/src/components/wf/content2.vue +1 -1
  82. package/src/components/wf/style.scss +265 -0
  83. package/src/components/wf/wf.js +7 -0
  84. package/src/components/wf/wfActionItems.js +3 -0
  85. package/src/components/wf/wfStartDialog.vue +1 -0
  86. package/src/components/wf/wfTransferEnabled.js +54 -0
  87. package/src/components/wf/wfUtil.js +47 -29
  88. package/src/components/wf/wfVisibility.js +6 -0
  89. package/src/components/xform/docs/2026-07 table/344/270/216excelExport/344/277/256/345/244/215/345/217/212/345/257/274/345/207/272/344/274/230/345/214/226.md" +64 -57
  90. package/src/components/xform/docs/2026-08 xform H5 P0 /345/256/236/347/216/260/350/247/204/346/240/274.md" +1 -1
  91. package/src/components/xform/docs/2026-08 xform /347/247/273/345/212/250/347/253/257/345/270/203/345/261/200/345/257/271/351/275/220 bill_setting /346/226/271/346/241/210.md" +1 -1
  92. package/src/components/xform/docs/2026-09 /345/212/250/346/200/201/345/255/227/346/256/265/350/247/204/345/210/231/344/270/216/346/265/201/347/250/213/346/216/247/345/210/266/346/211/247/350/241/214/351/241/272/345/272/217/350/220/275/345/234/260/346/226/271/346/241/210.md" +409 -0
  93. package/src/components/xform/docs/2026-09 /346/235/241/344/273/266/345/257/274/345/207/272/345/244/247/346/225/260/346/215/256/351/207/217/345/264/251/346/272/203/346/216/222/346/237/245.md" +389 -0
  94. package/src/components/xform/docs/2026-09 /346/265/217/350/247/210/345/231/250/345/264/251/346/272/203/346/262/273/347/220/206P0/344/273/243/347/240/201/350/257/204/345/256/241.md" +255 -0
  95. package/src/components/xform/docs/2026-09 /346/265/217/350/247/210/345/231/250/345/264/251/346/272/203/346/262/273/347/220/206P0/346/265/213/350/257/225/351/252/214/350/257/201/345/256/236/346/226/275/346/226/207/346/241/243.md" +595 -0
  96. package/src/components/xform/docs/2026-09 /347/274/226/350/276/221/350/241/250/350/241/214/345/206/205/344/277/235/345/255/230/346/240/241/351/252/214/350/214/203/345/233/264/344/270/216/345/210/227/345/277/205/345/241/253/350/247/243/346/236/220/344/277/256/345/244/215.md" +233 -0
  97. package/src/components/xform/docs/2026-09 /350/241/250/345/215/225/345/257/274/345/205/245/351/200/232/351/201/223/346/242/263/347/220/206/344/270/216/350/220/275/345/234/260/346/226/271/346/241/210.md" +745 -0
  98. package/src/components/xform/docs//346/265/217/350/247/210/345/231/250/345/264/251/346/272/203/346/262/273/347/220/206/344/270/216/344/277/256/345/244/215/350/220/275/345/234/260/346/226/271/346/241/210.md +324 -0
  99. package/src/components/xform/form-designer/designer.js +2133 -2100
  100. package/src/components/xform/form-designer/form-widget/container-widget/data-table-mixin.js +7 -0
  101. package/src/components/xform/form-designer/form-widget/container-widget/data-table-widget.vue +4 -2
  102. package/src/components/xform/form-designer/form-widget/container-widget/detail-h5-widget.vue +243 -227
  103. package/src/components/xform/form-designer/form-widget/container-widget/h5-card-pane-widget.vue +228 -207
  104. package/src/components/xform/form-designer/form-widget/container-widget/h5-card-widget.vue +165 -136
  105. package/src/components/xform/form-designer/form-widget/dialog/formDialog.vue +30 -4
  106. package/src/components/xform/form-designer/form-widget/dialog/importDialog.vue +190 -185
  107. package/src/components/xform/form-designer/form-widget/dialog/importDialogMixin.js +2657 -1774
  108. package/src/components/xform/form-designer/form-widget/dialog/searchFormDialog.vue +27 -4
  109. package/src/components/xform/form-designer/form-widget/field-widget/a-link-widget.vue +1 -1
  110. package/src/components/xform/form-designer/form-widget/field-widget/a-text-widget.vue +1 -1
  111. package/src/components/xform/form-designer/form-widget/field-widget/area-select-widget.vue +1 -1
  112. package/src/components/xform/form-designer/form-widget/field-widget/autocomplete-widget.vue +1 -1
  113. package/src/components/xform/form-designer/form-widget/field-widget/baseAttachment-widget.vue +1 -1
  114. package/src/components/xform/form-designer/form-widget/field-widget/button-widget.vue +1 -1
  115. package/src/components/xform/form-designer/form-widget/field-widget/cascader-widget.vue +1 -1
  116. package/src/components/xform/form-designer/form-widget/field-widget/census-widget.vue +1 -1
  117. package/src/components/xform/form-designer/form-widget/field-widget/checkbox-widget.vue +1 -1
  118. package/src/components/xform/form-designer/form-widget/field-widget/color-widget.vue +1 -1
  119. package/src/components/xform/form-designer/form-widget/field-widget/copy_button-widget.vue +1 -1
  120. package/src/components/xform/form-designer/form-widget/field-widget/date-range-widget.vue +1 -1
  121. package/src/components/xform/form-designer/form-widget/field-widget/date-widget.vue +1 -1
  122. package/src/components/xform/form-designer/form-widget/field-widget/descriptions-widget.vue +1 -1
  123. package/src/components/xform/form-designer/form-widget/field-widget/divider-widget.vue +1 -1
  124. package/src/components/xform/form-designer/form-widget/field-widget/download-button-widget.vue +1 -1
  125. package/src/components/xform/form-designer/form-widget/field-widget/dropdown-item-widget.vue +1 -1
  126. package/src/components/xform/form-designer/form-widget/field-widget/dropdown-widget.vue +35 -4
  127. package/src/components/xform/form-designer/form-widget/field-widget/echart-bar-widget.vue +1 -1
  128. package/src/components/xform/form-designer/form-widget/field-widget/echart-category-widget.vue +1 -1
  129. package/src/components/xform/form-designer/form-widget/field-widget/echart-pie-widget.vue +1 -1
  130. package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +64 -10
  131. package/src/components/xform/form-designer/form-widget/field-widget/file-upload-widget.vue +1 -1
  132. package/src/components/xform/form-designer/form-widget/field-widget/form-item-wrapper.vue +14 -4
  133. package/src/components/xform/form-designer/form-widget/field-widget/html-text-widget.vue +1 -1
  134. package/src/components/xform/form-designer/form-widget/field-widget/import-button-widget.vue +19 -6
  135. package/src/components/xform/form-designer/form-widget/field-widget/import2-button-widget.vue +1 -1
  136. package/src/components/xform/form-designer/form-widget/field-widget/index.js +10 -1
  137. package/src/components/xform/form-designer/form-widget/field-widget/input-batch-widget.vue +1 -1
  138. package/src/components/xform/form-designer/form-widget/field-widget/input-widget.vue +29 -3
  139. package/src/components/xform/form-designer/form-widget/field-widget/multiSearch-widget.vue +1 -1
  140. package/src/components/xform/form-designer/form-widget/field-widget/news-list-widget.vue +1 -1
  141. package/src/components/xform/form-designer/form-widget/field-widget/number-widget.vue +1 -1
  142. package/src/components/xform/form-designer/form-widget/field-widget/oplog-widget.vue +1 -1
  143. package/src/components/xform/form-designer/form-widget/field-widget/picture-upload-widget.vue +1 -1
  144. package/src/components/xform/form-designer/form-widget/field-widget/print-button-widget.vue +1 -1
  145. package/src/components/xform/form-designer/form-widget/field-widget/print-detail-button-widget.vue +1 -1
  146. package/src/components/xform/form-designer/form-widget/field-widget/project-tag-widget.vue +1 -1
  147. package/src/components/xform/form-designer/form-widget/field-widget/radio-widget.vue +1 -1
  148. package/src/components/xform/form-designer/form-widget/field-widget/rate-widget.vue +1 -1
  149. package/src/components/xform/form-designer/form-widget/field-widget/rich-editor-widget.vue +1 -1
  150. package/src/components/xform/form-designer/form-widget/field-widget/row-col-select-widget.vue +64 -0
  151. package/src/components/xform/form-designer/form-widget/field-widget/save_submit_wf_button-widget.vue +1 -1
  152. package/src/components/xform/form-designer/form-widget/field-widget/script-input-widget.vue +1 -1
  153. package/src/components/xform/form-designer/form-widget/field-widget/search_button-widget.vue +1 -1
  154. package/src/components/xform/form-designer/form-widget/field-widget/select-export-button-widget.vue +1 -1
  155. package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +6 -2
  156. package/src/components/xform/form-designer/form-widget/field-widget/select-widget.vue +1 -1
  157. package/src/components/xform/form-designer/form-widget/field-widget/singerSearch-widget.vue +1 -1
  158. package/src/components/xform/form-designer/form-widget/field-widget/singleUpload-widget.vue +145 -145
  159. package/src/components/xform/form-designer/form-widget/field-widget/slider-widget.vue +1 -1
  160. package/src/components/xform/form-designer/form-widget/field-widget/static-content-wrapper.vue +1 -1
  161. package/src/components/xform/form-designer/form-widget/field-widget/static-text-widget.vue +1 -1
  162. package/src/components/xform/form-designer/form-widget/field-widget/status-widget.vue +1 -1
  163. package/src/components/xform/form-designer/form-widget/field-widget/switch-widget.vue +1 -1
  164. package/src/components/xform/form-designer/form-widget/field-widget/table-export-button-widget.vue +3 -2
  165. package/src/components/xform/form-designer/form-widget/field-widget/tempStorage-widget.vue +1 -1
  166. package/src/components/xform/form-designer/form-widget/field-widget/text-widget.vue +1 -1
  167. package/src/components/xform/form-designer/form-widget/field-widget/textarea-widget.vue +1 -1
  168. package/src/components/xform/form-designer/form-widget/field-widget/time-range-widget.vue +1 -1
  169. package/src/components/xform/form-designer/form-widget/field-widget/time-widget.vue +1 -1
  170. package/src/components/xform/form-designer/form-widget/field-widget/title-head-widget.vue +1 -1
  171. package/src/components/xform/form-designer/form-widget/field-widget/vabSearch-widget.vue +1 -1
  172. package/src/components/xform/form-designer/form-widget/field-widget/vabUpload-widget.vue +3 -3
  173. package/src/components/xform/form-designer/form-widget/field-widget/vabUpload2-widget.vue +25 -1
  174. package/src/components/xform/form-designer/form-widget/field-widget/vue-page-widget.vue +1 -1
  175. package/src/components/xform/form-designer/indexMixin.js +27 -0
  176. package/src/components/xform/form-designer/setting-panel/form-dynamicField-setting.vue +687 -602
  177. package/src/components/xform/form-designer/setting-panel/form-setting.vue +269 -36
  178. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/data-table-editor.vue +335 -12
  179. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/table-column-dialog.vue +23 -7
  180. package/src/components/xform/form-designer/setting-panel/property-editor/container-detail/detail-temp-edito.vue +13 -1
  181. package/src/components/xform/form-designer/setting-panel/property-editor/container-list-h5/list-h5-editor.vue +846 -860
  182. package/src/components/xform/form-designer/setting-panel/property-editor/container-tab/tab-lazyRender-editor.vue +27 -0
  183. package/src/components/xform/form-designer/setting-panel/property-editor/event-handler/onTabChange-editor.vue +28 -0
  184. package/src/components/xform/form-designer/setting-panel/property-editor/event-handler/onTabClick-editor.vue +28 -0
  185. package/src/components/xform/form-designer/setting-panel/property-editor/field-import-button/import-button-editor.vue +62 -1
  186. package/src/components/xform/form-designer/setting-panel/property-editor/field-import-button/import2-button-editor.vue +142 -2
  187. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/empty-number-input.vue +51 -0
  188. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +27 -2
  189. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/table-export-button-editor.vue +33 -3
  190. package/src/components/xform/form-designer/setting-panel/property-editor/project-tag-editor.vue +2 -4
  191. package/src/components/xform/form-designer/setting-panel/property-editor/suffixIconClickable-editor.vue +23 -0
  192. package/src/components/xform/form-designer/setting-panel/propertyRegister.js +18 -4
  193. package/src/components/xform/form-designer/toolbar-panel/index.vue +884 -874
  194. package/src/components/xform/form-designer/toolbar-panel/indexMixin.js +13 -0
  195. package/src/components/xform/form-designer/widget-panel/index.vue +11 -7
  196. package/src/components/xform/form-designer/widget-panel/indexMixin.js +15 -2
  197. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +152 -9
  198. package/src/components/xform/form-render/container-item/containerItemMixin.js +93 -8
  199. package/src/components/xform/form-render/container-item/data-table-item.vue +352 -347
  200. package/src/components/xform/form-render/container-item/data-table-mixin.js +7261 -5985
  201. package/src/components/xform/form-render/container-item/detail-h5-item.vue +48 -19
  202. package/src/components/xform/form-render/container-item/detail-item.vue +350 -446
  203. package/src/components/xform/form-render/container-item/dynamicFieldMixin.js +55 -1
  204. package/src/components/xform/form-render/container-item/list-h5-item.vue +261 -178
  205. package/src/components/xform/form-render/container-item/panel-item.vue +1 -1
  206. package/src/components/xform/form-render/container-item/tab-item.vue +177 -129
  207. package/src/components/xform/form-render/fieldControlEngine.js +218 -0
  208. package/src/components/xform/form-render/fieldControlMixin.js +536 -0
  209. package/src/components/xform/form-render/index.vue +164 -153
  210. package/src/components/xform/form-render/indexMixin.js +5443 -5311
  211. package/src/components/xform/form-render/pageDialogScope.js +54 -0
  212. package/src/components/xform/icon-picker/index.vue +1 -1
  213. package/src/components/xform/lang/en-US.js +8 -0
  214. package/src/components/xform/lang/zh-CN.js +9 -0
  215. package/src/components/xform/mixins/businessLock.js +59 -0
  216. package/src/components/xform/mixins/defaultHandle.js +713 -707
  217. package/src/components/xform/mixins/scriptHttp.js +42 -21
  218. package/src/components/xform/mixins/wfStart.js +105 -0
  219. package/src/components/xform/mixins/wfVisibility.js +33 -0
  220. package/src/components/xform/styles/census.scss +288 -0
  221. package/src/components/xform/styles/containers.scss +215 -0
  222. package/src/components/xform/styles/designer.scss +904 -0
  223. package/src/components/xform/styles/detail-temp.scss +71 -0
  224. package/src/components/xform/styles/h5-designer.scss +191 -0
  225. package/src/components/xform/styles/h5-runtime.scss +520 -0
  226. package/src/components/xform/styles/h5.scss +836 -516
  227. package/src/components/xform/styles/skins.scss +398 -0
  228. package/src/components/xform/styles/table-layout.scss +91 -0
  229. package/src/components/xform/styles/titles.scss +255 -0
  230. package/src/components/xform/utils/dynamicSchemaUtil.js +385 -361
  231. package/src/components/xform/utils/fixedQueryUtil.js +29 -4
  232. package/src/components/xform/utils/serviceNameCache.js +69 -0
  233. package/src/components/xform/utils/tableColumnHelper.js +46 -4
  234. package/src/components/xform/utils/util.js +1776 -1708
  235. package/src/core-resources/css/h5.scss +548 -0
  236. package/src/core-resources/css/iconfont.css +1447 -0
  237. package/src/core-resources/font/iconfont.ttf +0 -0
  238. package/src/core-resources/font/iconfont.woff +0 -0
  239. package/src/core-resources/font/iconfont.woff2 +0 -0
  240. package/src/core-resources/images/default-header.png +0 -0
  241. package/src/core-resources/images/ico-default_wenjian.svg +1 -0
  242. package/src/core-resources/images/ico-excel.svg +1 -0
  243. package/src/core-resources/images/ico-ftl.svg +7 -0
  244. package/src/core-resources/images/ico-html.svg +1 -0
  245. package/src/core-resources/images/ico-java.svg +1 -0
  246. package/src/core-resources/images/ico-music.svg +1 -0
  247. package/src/core-resources/images/ico-pdf.svg +1 -0
  248. package/src/core-resources/images/ico-photo.svg +1 -0
  249. package/src/core-resources/images/ico-ppt.svg +1 -0
  250. package/src/core-resources/images/ico-psd.svg +1 -0
  251. package/src/core-resources/images/ico-service.png +0 -0
  252. package/src/core-resources/images/ico-txt.svg +1 -0
  253. package/src/core-resources/images/ico-video.svg +1 -0
  254. package/src/core-resources/images/ico-vue.svg +8 -0
  255. package/src/core-resources/images/ico-wenjianjia.svg +1 -0
  256. package/src/core-resources/images/ico-wrod.svg +1 -0
  257. package/src/core-resources/images/ico-xml.svg +1 -0
  258. package/src/core-resources/images/ico-yasuobao.svg +1 -0
  259. package/src/core-resources/images/icon-error.png +0 -0
  260. package/src/core-resources/images/icon-tips.png +0 -0
  261. package/src/core-resources/images/nodata.png +0 -0
  262. package/src/core-resources/images/nodata2.png +0 -0
  263. package/src/directive/append-to-body/index.js +48 -0
  264. package/src/directive/el-dialog-center/index.js +3 -2
  265. package/src/directive/el-drag-dialog/drag.js +33 -4
  266. package/src/icons/index.js +9 -0
  267. package/src/icons/svg/404.svg +1 -0
  268. package/src/icons/svg/alert.svg +1 -0
  269. package/src/icons/svg/bug.svg +1 -0
  270. package/src/icons/svg/button.svg +1 -0
  271. package/src/icons/svg/card.svg +1 -0
  272. package/src/icons/svg/cascader-field.svg +1 -0
  273. package/src/icons/svg/census.svg +1 -0
  274. package/src/icons/svg/chart.svg +1 -0
  275. package/src/icons/svg/checkbox-field.svg +1 -0
  276. package/src/icons/svg/clipboard.svg +1 -0
  277. package/src/icons/svg/color-field.svg +1 -0
  278. package/src/icons/svg/component.svg +1 -0
  279. package/src/icons/svg/custom-component.svg +1 -0
  280. package/src/icons/svg/dashboard.svg +1 -0
  281. package/src/icons/svg/data-table.svg +1 -0
  282. package/src/icons/svg/date-field.svg +1 -0
  283. package/src/icons/svg/date-range-field.svg +1 -0
  284. package/src/icons/svg/descriptions.svg +1 -0
  285. package/src/icons/svg/divider.svg +1 -0
  286. package/src/icons/svg/document.svg +1 -0
  287. package/src/icons/svg/documentation.svg +1 -0
  288. package/src/icons/svg/drag.svg +1 -0
  289. package/src/icons/svg/echart-category.svg +1 -0
  290. package/src/icons/svg/echart-pie.svg +1 -0
  291. package/src/icons/svg/echart.svg +1 -0
  292. package/src/icons/svg/edit.svg +1 -0
  293. package/src/icons/svg/education.svg +1 -0
  294. package/src/icons/svg/email.svg +1 -0
  295. package/src/icons/svg/example.svg +1 -0
  296. package/src/icons/svg/excel.svg +1 -0
  297. package/src/icons/svg/exit-fullscreen.svg +1 -0
  298. package/src/icons/svg/eye-open.svg +1 -0
  299. package/src/icons/svg/eye.svg +1 -0
  300. package/src/icons/svg/file-upload-field.svg +1 -0
  301. package/src/icons/svg/form.svg +1 -0
  302. package/src/icons/svg/fullscreen.svg +1 -0
  303. package/src/icons/svg/github.svg +1 -0
  304. package/src/icons/svg/grid.svg +1 -0
  305. package/src/icons/svg/guide.svg +1 -0
  306. package/src/icons/svg/html-text.svg +1 -0
  307. package/src/icons/svg/ico-more.svg +1 -0
  308. package/src/icons/svg/ico-wenjianjia.svg +1 -0
  309. package/src/icons/svg/icon.svg +1 -0
  310. package/src/icons/svg/input-batch.svg +1 -0
  311. package/src/icons/svg/international.svg +1 -0
  312. package/src/icons/svg/language.svg +1 -0
  313. package/src/icons/svg/link.svg +1 -0
  314. package/src/icons/svg/list-export.svg +1 -0
  315. package/src/icons/svg/list.svg +1 -0
  316. package/src/icons/svg/lock.svg +1 -0
  317. package/src/icons/svg/message.svg +1 -0
  318. package/src/icons/svg/money.svg +1 -0
  319. package/src/icons/svg/nested.svg +1 -0
  320. package/src/icons/svg/news-list.svg +1 -0
  321. package/src/icons/svg/node-tree.svg +1 -0
  322. package/src/icons/svg/number-field.svg +1 -0
  323. package/src/icons/svg/password.svg +1 -0
  324. package/src/icons/svg/pdf.svg +1 -0
  325. package/src/icons/svg/people.svg +1 -0
  326. package/src/icons/svg/peoples.svg +1 -0
  327. package/src/icons/svg/picture-upload-field.svg +1 -0
  328. package/src/icons/svg/qq.svg +1 -0
  329. package/src/icons/svg/query.svg +1 -0
  330. package/src/icons/svg/radio-field.svg +1 -0
  331. package/src/icons/svg/rate-field.svg +1 -0
  332. package/src/icons/svg/redo.svg +1 -0
  333. package/src/icons/svg/rich-editor-field.svg +1 -0
  334. package/src/icons/svg/search.svg +1 -0
  335. package/src/icons/svg/searchbox.svg +1 -0
  336. package/src/icons/svg/section.svg +1 -0
  337. package/src/icons/svg/select-field.svg +1 -0
  338. package/src/icons/svg/shopping.svg +1 -0
  339. package/src/icons/svg/size.svg +1 -0
  340. package/src/icons/svg/skill.svg +1 -0
  341. package/src/icons/svg/slider-field.svg +1 -0
  342. package/src/icons/svg/slot-component.svg +1 -0
  343. package/src/icons/svg/slot-field.svg +1 -0
  344. package/src/icons/svg/star.svg +1 -0
  345. package/src/icons/svg/static-text.svg +1 -0
  346. package/src/icons/svg/status.svg +1 -0
  347. package/src/icons/svg/sub-form.svg +1 -0
  348. package/src/icons/svg/switch-field.svg +1 -0
  349. package/src/icons/svg/tab.svg +1 -0
  350. package/src/icons/svg/table.svg +1 -0
  351. package/src/icons/svg/text-field.svg +1 -0
  352. package/src/icons/svg/text.svg +1 -0
  353. package/src/icons/svg/textarea-field.svg +1 -0
  354. package/src/icons/svg/theme.svg +1 -0
  355. package/src/icons/svg/time-field.svg +1 -0
  356. package/src/icons/svg/time-range-field.svg +1 -0
  357. package/src/icons/svg/tips-text.svg +1 -0
  358. package/src/icons/svg/title-head.svg +1 -0
  359. package/src/icons/svg/tree-pane.svg +1 -0
  360. package/src/icons/svg/tree-table.svg +1 -0
  361. package/src/icons/svg/tree.svg +1 -0
  362. package/src/icons/svg/undo.svg +1 -0
  363. package/src/icons/svg/uploadbox.svg +1 -0
  364. package/src/icons/svg/user.svg +1 -0
  365. package/src/icons/svg/vf-box.svg +1 -0
  366. package/src/icons/svg/vf-dialog.svg +1 -0
  367. package/src/icons/svg/vf-drawer.svg +1 -0
  368. package/src/icons/svg/vue-sfc.svg +1 -0
  369. package/src/icons/svg/wechat.svg +1 -0
  370. package/src/icons/svg/zip.svg +1 -0
  371. package/src/index.js +2 -510
  372. package/src/index.next.js +5 -0
  373. package/src/index.sync.js +5 -0
  374. package/src/layout/components/Sidebar/default.vue +1656 -1655
  375. package/src/layout/components/TagsView/index.vue +330 -323
  376. package/src/layout/components/TagsView/style.scss +92 -0
  377. package/src/layout/defaultLayout.vue +2 -2
  378. package/src/router/index.js +43 -48
  379. package/src/router/modules/customer.js +12 -0
  380. package/src/store/modules/settings.js +1 -1
  381. package/src/store/modules/tagsView.js +241 -217
  382. package/src/store/modules/user.js +16 -12
  383. package/src/styles/btn.scss +315 -0
  384. package/src/styles/dialog.scss +1710 -0
  385. package/src/styles/element-ui.scss +1076 -0
  386. package/src/styles/element-variables.scss +31 -0
  387. package/src/styles/global.scss +115 -0
  388. package/src/styles/index.scss +245 -0
  389. package/src/styles/layout-skins.scss +840 -0
  390. package/src/styles/legacy-base.scss +19 -0
  391. package/src/styles/menu-icons.scss +75 -0
  392. package/src/styles/mixin.scss +80 -0
  393. package/src/styles/nprogress.scss +14 -0
  394. package/src/styles/page-shell.scss +723 -0
  395. package/src/styles/sidebar.scss +296 -0
  396. package/src/styles/split-handle.scss +52 -0
  397. package/src/styles/theme-variables.scss +7 -0
  398. package/src/styles/transition.scss +48 -0
  399. package/src/styles/utilities.scss +253 -0
  400. package/src/styles/variables.scss +72 -0
  401. package/src/utils/baseDictsCache.js +197 -0
  402. package/src/utils/chunkLoadError.js +24 -0
  403. package/src/utils/featureResources.js +57 -0
  404. package/src/utils/importLimit.js +72 -0
  405. package/src/utils/pdfUtil.js +6 -1
  406. package/src/utils/request.js +142 -7
  407. package/src/utils/resolveViewComponent.js +45 -90
  408. package/src/utils/runtimeRecovery.js +105 -0
  409. package/src/utils/swiperLoader.js +20 -0
  410. package/src/utils/vab.js +1334 -1283
  411. package/src/utils/xlsxLoader.js +29 -0
  412. package/src/views/bd/setting/formVersion/compareContMixin.scss +1 -1
  413. package/src/views/bd/setting/form_template/edit.vue +378 -356
  414. package/src/views/bd/setting/form_template/formDesignerDialog.vue +6 -0
  415. package/src/views/bd/setting/form_template/formTemplateType.js +43 -0
  416. package/src/views/bd/setting/form_template/list.vue +304 -303
  417. package/src/views/bd/setting/form_template/mixins/edit.js +344 -280
  418. package/src/views/bd/setting/form_template/mixins/list.js +749 -721
  419. package/src/views/bd/setting/form_template/mixins/list2.js +412 -411
  420. package/src/views/bd/setting/form_template/mixins/wf_list.js +431 -430
  421. package/src/views/mobile/user/area/dialog.vue +1 -1
  422. package/src/views/mobile/user/user/dialog.vue +1 -1
  423. package/src/views/user/bill_setting/mixins/wf.js +8 -0
  424. package/src/views/user/bill_setting/render.vue +1 -1
  425. package/src/views/user/commMenu/index.vue +3 -1
  426. package/src/views/user/commMenu/style.scss +123 -0
  427. package/src/views/user/common_attribute/edit.vue +2 -0
  428. package/src/views/user/common_attribute/itemEdit.vue +2 -0
  429. package/src/views/user/file_view_ins/list.vue +970 -970
  430. package/src/views/user/form/form_template/list.vue +527 -506
  431. package/src/views/user/form/vform/designer.vue +823 -821
  432. package/src/views/user/form/view/list.vue +24 -1
  433. package/src/views/user/home/default.vue +1124 -1117
  434. package/src/views/user/home/default2.vue +12 -8
  435. package/src/views/user/home/index.vue +102 -102
  436. package/src/views/user/home/wjl/content.vue +1581 -1569
  437. package/src/views/user/intf_permission/edit.vue +230 -0
  438. package/src/views/user/intf_permission/list.vue +196 -0
  439. package/src/views/user/menuFallback/index.vue +23 -0
  440. package/src/views/user/notify_message/dialog.vue +139 -139
  441. package/src/views/user/outLink/form_view.vue +202 -202
  442. package/src/views/user/outLink/index.vue +110 -110
  443. package/src/views/user/role/edit.vue +4 -0
  444. package/src/views/user/role/role-box.scss +83 -0
  445. package/src/views/user/role/voc_edit.vue +4 -0
  446. package/src/views/user/user/list.vue +28 -6
  447. package/src/views/user/wf/iframe/index.vue +51 -51
  448. package/src/views/user/wf/iframe/index2.vue +64 -64
  449. package/src/views/user/wf/wf_diy_attribute/edit.vue +1 -1
  450. package/src/views/user/wf/wf_manage/list2.vue +871 -871
  451. package/src/views/user/wf/wf_manage/wfContentDialog.vue +177 -126
  452. package/src/views/user/wf/wf_obj_config/activiti_wf.vue +134 -133
  453. package/src/views/user/wf/wf_obj_config/importItemDialog.vue +1 -1
  454. package/src/views/user/wf/wf_obj_config/list.vue +747 -746
  455. package/src/views/user/wf/wf_obj_config/list_form.vue +374 -373
  456. package/templates/child-app/.env +3 -0
  457. package/templates/child-app/README.md +49 -0
  458. package/templates/child-app/app.config.js +6 -0
  459. package/templates/child-app/babel.config.js +1 -0
  460. package/templates/child-app/core-resources.config.js +15 -0
  461. package/templates/child-app/gitignore +7 -0
  462. package/templates/child-app/package.json +34 -0
  463. package/templates/child-app/public/index.html +12 -0
  464. package/templates/child-app/src/assets/401_images/401.gif +0 -0
  465. package/templates/child-app/src/assets/404_images/404.png +0 -0
  466. package/templates/child-app/src/assets/404_images/404_cloud.png +0 -0
  467. package/templates/child-app/src/assets/logo.png +0 -0
  468. package/templates/child-app/src/corejsConfig.js +2 -0
  469. package/templates/child-app/src/icons/index.js +2 -0
  470. package/templates/child-app/src/icons/svg/child-demo.svg +1 -0
  471. package/templates/child-app/src/layout/index.vue +4 -0
  472. package/templates/child-app/src/main.js +5 -0
  473. package/templates/child-app/src/prefixConfig.js +13 -0
  474. package/templates/child-app/src/resources/css/login.scss +29 -0
  475. package/templates/child-app/src/resources/images/bg-login5.jpg +0 -0
  476. package/templates/child-app/src/resources/images/home-img.png +0 -0
  477. package/templates/child-app/src/router/modules/customer.js +3 -0
  478. package/templates/child-app/src/settings.js +24 -0
  479. package/templates/child-app/src/styles/app.scss +2 -0
  480. package/templates/child-app/src/views/demo/detail.vue +19 -0
  481. package/templates/child-app/src/views/demo/list.vue +38 -0
  482. package/templates/child-app/src/views/user/login/index.vue +18 -0
  483. package/templates/child-app/vue.config.js +107 -0
  484. package/src/components/xform/form-render/container-item/dynamicFieldEngine.js +0 -285
  485. package/src/components/xform/form-render/formDynamicFieldMixin.js +0 -131
  486. package/src/views/demo/bizTab/bill/billConfig.js +0 -41
  487. package/src/views/demo/bizTab/bill/detail.vue +0 -436
  488. package/src/views/demo/bizTab/bill/list.vue +0 -288
  489. package/src/views/demo/bizTab/bill/productDialog.vue +0 -176
  490. package/src/views/demo/bizTab/simple/billApi.js +0 -123
  491. package/src/views/demo/bizTab/simple/detail.vue +0 -407
  492. package/src/views/demo/bizTab/simple/list.vue +0 -217
  493. package/src/views/demo/bizTab/standalone/index.vue +0 -121
  494. package/src/views/demo/bizTab/wf/billConfig.js +0 -44
  495. package/src/views/demo/bizTab/wf/detail.vue +0 -661
  496. package/src/views/demo/bizTab/wf/list.vue +0 -242
  497. package/src/views/user/outLink/cc_form_view.vue +0 -188
  498. /package/src/{resources → core-resources}/js/base/common.js +0 -0
@@ -1,11 +1,23 @@
1
1
  /**version-1.0*/
2
2
  let tmixins = {};
3
+ import axios from "axios";
3
4
  import { getToken } from "../../utils/auth";
4
5
  import indexUtil from "../../utils/index.js";
5
6
  import exportFieldDialog from "./exportFieldDialog.vue";
6
7
  import { getCellValue } from "@base/components/table/util/index";
7
- import { isItemExport, isRowExport, requiresCheckedRows } from "./exportType";
8
- import { getExportItemColumns } from "./exportItemConfigUtil";
8
+ import {
9
+ isConditionExport,
10
+ isItemExport,
11
+ isRowExport,
12
+ requiresCheckedRows,
13
+ } from "./exportType";
14
+ import { getExportItemColumns } from "./exportItemConfigUtil";
15
+ import { viewKey } from "@base/utils/repeatOpen";
16
+ import {
17
+ DEFAULT_CONDITION_EXPORT_CONFIG,
18
+ loadConditionExportConfig,
19
+ resolveConditionExportConfig,
20
+ } from "./conditionExportConfig";
9
21
 
10
22
  let configUtil = {
11
23
  baseUrl: process.env.VUE_APP_BASE_API,
@@ -13,6 +25,45 @@ let configUtil = {
13
25
  indexUtil,
14
26
  };
15
27
 
28
+ /** 取值转换每处理多少行让出一次主线程,见 getExcelData。 */
29
+ const EXPORT_YIELD_ROWS = 200;
30
+
31
+ /**
32
+ * 让出主线程一个宏任务,给渲染与用户交互留出机会。
33
+ * 必须是宏任务:微任务(Promise.resolve())不会让浏览器插入渲染帧,进度条照样不动。
34
+ * @returns {Promise<void>}
35
+ */
36
+ function yieldToBrowser() {
37
+ return new Promise((resolve) => setTimeout(resolve, 0));
38
+ }
39
+
40
+ /**
41
+ * 可以直接当文件存下来的响应体类型(前缀匹配)。
42
+ */
43
+ const DOWNLOAD_BINARY_TYPES = [
44
+ "application/vnd.openxmlformats",
45
+ "application/vnd.ms-excel",
46
+ "application/octet-stream",
47
+ "application/zip",
48
+ ];
49
+
50
+ /**
51
+ * 这份响应体是不是真的文件流。
52
+ * 用白名单而不是「排除 application/json」:网关拦截、登录态失效这类失败常见的是
53
+ * 200 + text/html,黑名单会把一张 HTML 错误页原样存成打不开的 .xlsx,用户看到的是
54
+ * 一个损坏文件而不是回退后的正常下载。
55
+ * 后端不给 Content-Type 时 blob.type 是空串,按二进制放行 —— 旧的表单 POST 本来也是
56
+ * 这么下的,不能因为这次改造反而下不动。
57
+ * @param {Blob} res 响应体。
58
+ * @returns {Boolean}
59
+ */
60
+ function isDownloadableBlob(res) {
61
+ if (!res) return false;
62
+ let type = (res.type || "").toLowerCase();
63
+ if (!type) return true;
64
+ return DOWNLOAD_BINARY_TYPES.some((item) => type.indexOf(item) === 0);
65
+ }
66
+
16
67
  function getGrid(that, tableRef) {
17
68
  let $grid;
18
69
  if (Array.isArray(that.$refs[tableRef])) {
@@ -41,6 +92,8 @@ function getGrid(that, tableRef) {
41
92
  CURRENT_PREFIX: "",
42
93
  option: {},
43
94
  uuid: "",
95
+ // createExcelTemplate 返回的本次导出时间快照,随每页取数请求下发(见 loopToDo)
96
+ nowDate: null,
44
97
  fileRows: [],
45
98
  title: this.$t2("导出", "components.excelExport.title"),
46
99
  tableTarget: null,
@@ -87,23 +140,81 @@ function getGrid(that, tableRef) {
87
140
  showExportFieldDialog: false,
88
141
  leafColumns: [],
89
142
  columns: [],
90
- queryParam: {},
91
- isMinimize: false,
143
+ queryParam: {},
144
+ isMinimize: false,
145
+ // 条件导出在本次任务内使用的配置快照。逻辑参数只在开始时读取一次,避免中途变更
146
+ // 导致前后页使用不同的分页/分卷口径。
147
+ conditionExportMode: false,
148
+ conditionExportConfig: { ...DEFAULT_CONDITION_EXPORT_CONFIG },
149
+ // 不查总数时,处理过程中分母显示「...」;文件合并成功后才回填实际完成数。
150
+ totalSizeKnown: true,
92
151
  // 定时器必须放实例上:模块级共享会导致并发导出互相清除对方的定时器
93
152
  exportTimer: null,
94
153
  countTimer: null,
95
154
  exportError: false,
96
- };
97
- },
155
+ // 用户点「取消」/组件被销毁的终止标记。此前取消只是停掉定时器 + 销毁组件,
156
+ // 已经发出去的那几页请求照样会回调下来继续上送、继续合并,甚至最后弹出一个
157
+ // 半截文件的下载。所有异步续点都要先问 isExportStopped()。
158
+ aborted: false,
159
+ // 本次导出全部请求共用的取消令牌源,取消时一次性掐断在途请求
160
+ cancelSource: null,
161
+ // lockMenu 实际锁上的那一个页签。必须记下来:解锁若再按「当时的
162
+ // $route.path」现查,用户在导出期间切了页签(弹框可最小化,切走完全合法)
163
+ // 解的就是另一把锁 —— 原页签会永久 affix,而 tagsView 的 delOthersViews /
164
+ // delAllViews 都保留 affix,「关闭其他」「全部关闭」也删不掉它,只能刷新页面。
165
+ lockedView: null,
166
+ };
167
+ },
168
+ computed: {
169
+ exportTotalText() {
170
+ return this.totalSizeKnown ? this.tTotalSize : "...";
171
+ },
172
+ },
98
173
  created() {},
99
174
  mounted() {
100
175
  // this.exc();
101
176
  },
102
177
  beforeDestroy() {
178
+ this.aborted = true;
103
179
  this.clearExportTimer();
180
+ this.cancelPendingRequests();
104
181
  this.unlockMenu();
105
182
  },
106
183
  methods: {
184
+ /**
185
+ * 导出是否已终止:用户取消、组件销毁、或失败中止。
186
+ * 每一个跨越 await / .then / setTimeout 的续点都必须先问它,否则在途请求的回调
187
+ * 会在导出早已"取消"之后继续推进流程。
188
+ * @returns {Boolean}
189
+ */
190
+ isExportStopped() {
191
+ if (this.aborted === true) return true;
192
+ if (this.exportError === true) return true;
193
+ return this._isDestroyed === true;
194
+ },
195
+ /**
196
+ * 本次导出共用的取消令牌,首次取用时创建。
197
+ * 每次导出都是一个全新的组件实例(index.js 的 initInstance 会先销毁旧实例),
198
+ * 所以一个 source 覆盖一次导出的全部请求,不存在跨导出串味。
199
+ * @returns {Object} axios CancelToken
200
+ */
201
+ getCancelToken() {
202
+ if (!this.cancelSource) {
203
+ this.cancelSource = axios.CancelToken.source();
204
+ }
205
+ return this.cancelSource.token;
206
+ },
207
+ /**
208
+ * 掐断本次导出全部在途请求。
209
+ * request.js 的错误拦截器已对 axios.isCancel 短路返回,不会弹错误框;
210
+ * 各调用点的 .catch 也都会先问 isExportStopped() 再决定要不要提示。
211
+ */
212
+ cancelPendingRequests() {
213
+ if (this.cancelSource) {
214
+ this.cancelSource.cancel("export-cancelled");
215
+ this.cancelSource = null;
216
+ }
217
+ },
107
218
  exc() {
108
219
  this.option = this.param;
109
220
  let $grid = getGrid(this.option.vue, this.option.targetRef);
@@ -149,6 +260,9 @@ function getGrid(that, tableRef) {
149
260
  },
150
261
  dialogClose2() {
151
262
  if (this.isMinimize) return;
263
+ // 最小化不是取消,只有真正关闭才置终止标记
264
+ this.aborted = true;
265
+ this.cancelPendingRequests();
152
266
  let loadingObj = window.$vueRoot.$baseLoading({
153
267
  target: document.body,
154
268
  background: "unset",
@@ -163,13 +277,29 @@ function getGrid(that, tableRef) {
163
277
  this.param.destroyComponent(loadingObj);
164
278
  }, 0);
165
279
  },
166
- dialogPrimary2() {
280
+ dialogPrimary2() {
167
281
  // this.showUserDialog=false
168
282
  this.$emit("confirm");
169
283
  this.$emit("close");
170
- this.clearExportTimer();
171
- },
172
- startProcess() {
284
+ this.clearExportTimer();
285
+ },
286
+ /**
287
+ * 读取并固化本次条件导出的全局配置。单次按钮/脚本参数已经在 option 中完成合并,
288
+ * resolveConditionExportConfig 会让它们覆盖同名的全局配置。
289
+ */
290
+ prepareConditionExportConfig() {
291
+ this.conditionExportMode = isConditionExport(this.option.type);
292
+ this.totalSizeKnown = true;
293
+ if (!this.conditionExportMode) return Promise.resolve();
294
+ return loadConditionExportConfig().then((globalConfig) => {
295
+ this.conditionExportConfig = resolveConditionExportConfig(
296
+ globalConfig,
297
+ this.option
298
+ );
299
+ this.totalSizeKnown = this.conditionExportConfig.searchCount;
300
+ });
301
+ },
302
+ startProcess() {
173
303
  this.lockMenu();
174
304
  this.createCountTimer();
175
305
  this.tableTarget = getGrid(this.option.vue, this.option.targetRef);
@@ -202,42 +332,63 @@ function getGrid(that, tableRef) {
202
332
  this.abortExport(
203
333
  this.$t1("无法确定导出服务前缀,请在导出配置中指定 prefix")
204
334
  );
205
- this.unlockMenu();
206
335
  return;
207
- }
208
- this.CURRENT_PREFIX = options.prefix;
209
-
210
- this.title = title;
211
- that.tTotalPage = 0;
212
- that.tTotalSize = 0;
213
- that
214
- .createExcelFile()
215
- .then((resultMsg) => {
216
- if (resultMsg.type === "success") {
217
- let aObj = resultMsg.objx;
218
- that.uuid = aObj.uuid;
219
- that.nowDate = aObj.nowDate;
220
- that.ippaaapp = aObj.ippaaapp;
221
- setTimeout(function () {
222
- /*num = 1;
223
- loopToDo(0, num);*/
224
- that.handleLoopToDo();
225
- }, 300);
226
- } else {
227
- that.abortExport();
228
- that.showImportDialog = false;
229
- that.showImportDialog2 = false;
230
- }
231
- })
232
- .catch((error) => {
233
- console.error(error);
234
- that.abortExport(that.$t1("创建导出文件失败,导出已中止"));
235
- that.showImportDialog = false;
236
- that.showImportDialog2 = false;
237
- });
238
- },
239
- getExportPageSize() {
240
- let pageSize = Math.min(this.option.pageSize || 1000, 1000);
336
+ }
337
+ this.CURRENT_PREFIX = options.prefix;
338
+ this.title = title;
339
+ that.tTotalPage = 0;
340
+ that.tTotalSize = 0;
341
+ that.prepareConditionExportConfig().then(() => {
342
+ if (that.isExportStopped()) return;
343
+ // 取数走 originOption.exportAjax 时请求由 xform 侧构造,前端拿不到那个 config,
344
+ // 只能把令牌顺着导出参数带过去(data-table-mixin 的 exportAjax 会塞进
345
+ // customParam.config,最终由 formHttp 的 ...opts 透传给 axios)
346
+ options.cancelToken = that.getCancelToken();
347
+ that
348
+ .createExcelFile()
349
+ .then((resultMsg) => {
350
+ if (that.isExportStopped()) return;
351
+ if (resultMsg.type === "success") {
352
+ let aObj = resultMsg.objx;
353
+ that.uuid = aObj.uuid;
354
+ that.nowDate = aObj.nowDate;
355
+ that.ippaaapp = aObj.ippaaapp;
356
+ setTimeout(function () {
357
+ if (that.isExportStopped()) return;
358
+ that.handleLoopToDo();
359
+ }, 300);
360
+ } else {
361
+ that.abortExport();
362
+ that.showImportDialog = false;
363
+ that.showImportDialog2 = false;
364
+ }
365
+ })
366
+ .catch((error) => {
367
+ if (that.isExportStopped()) return;
368
+ console.error(error);
369
+ that.abortExport(that.$t1("创建导出文件失败,导出已中止"));
370
+ that.showImportDialog = false;
371
+ that.showImportDialog2 = false;
372
+ });
373
+ });
374
+ },
375
+ getExportPageSize() {
376
+ let configuredPageSize = this.conditionExportMode
377
+ ? this.conditionExportConfig.pageSize
378
+ : this.option.pageSize || 1000;
379
+ // 条件导出的按钮/脚本值已经按优先级写进配置快照,不再用全局默认值 1000
380
+ // 反向封顶;非条件导出保持原有最多 1000 的行为。
381
+ let pageSize = this.conditionExportMode
382
+ ? configuredPageSize
383
+ : Math.min(configuredPageSize, 1000);
384
+ if (this.conditionExportMode) {
385
+ // 单页本身不能突破总量或单文件上限,否则无法在页级合并时兑现硬上限。
386
+ pageSize = Math.min(
387
+ pageSize,
388
+ this.conditionExportConfig.maxQueryTotal,
389
+ this.conditionExportConfig.limitFileSize
390
+ );
391
+ }
241
392
  let showImageAtTable = this.option.showImageAtTable || false;
242
393
  let hasExportImage = !!this.leafColumns.find((item) => {
243
394
  let exportType = item?.params?.exportType;
@@ -250,18 +401,29 @@ function getGrid(that, tableRef) {
250
401
  if (hasExportImage || (hasExportImage2 && showImageAtTable)) {
251
402
  pageSize = Math.min(pageSize, 150);
252
403
  }
404
+ // 单页压力取决于「行数 × 列数」,此前只按有无图片列降档,宽表(100+ 列)一页
405
+ // 的请求体与同步转换耗时是窄表的十几倍,同样的 1000 行/页天差地别。
406
+ let columnNum = this.leafColumns.length;
407
+ if (columnNum > 100) {
408
+ pageSize = Math.min(pageSize, 200);
409
+ } else if (columnNum > 50) {
410
+ pageSize = Math.min(pageSize, 500);
411
+ }
253
412
  return pageSize;
254
413
  },
255
- hadleMergeExcel(pPageSize) {
414
+ hadleMergeExcel(pPageSize) {
415
+ if (this.isExportStopped()) return;
256
416
  let that = this;
257
417
  let title = this.title;
258
418
  let options = this.option;
259
419
  // let pPageSize = this.getExportPageSize();
260
- let limitFileSize = options.limitFileSize || 100000;
420
+ let limitFileSize = this.conditionExportMode
421
+ ? this.conditionExportConfig.limitFileSize
422
+ : options.limitFileSize || 100000;
261
423
  let limitThreadNum = options.limitThreadNum || 5;
262
424
  let totalPages = this.tTotalPage || 1;
263
425
 
264
- let kTotalPages = parseInt(limitFileSize / pPageSize);
426
+ let kTotalPages = Math.max(parseInt(limitFileSize / pPageSize), 1);
265
427
 
266
428
  let fileNum = parseInt(totalPages / kTotalPages);
267
429
  if (totalPages % kTotalPages !== 0) {
@@ -290,6 +452,9 @@ function getGrid(that, tableRef) {
290
452
  that
291
453
  .mergeExcel(startPage, endPage)
292
454
  .then(function (resultMsg3) {
455
+ // 取消后组件与进度弹框都已销毁,下面 $refs["exportTable"].insertAt
456
+ // 会直接抛错
457
+ if (that.isExportStopped()) return;
293
458
  if (resultMsg3.type === "success") {
294
459
  mtExcedSuccessNum++;
295
460
  let fileInfo = resultMsg3.objx;
@@ -311,89 +476,246 @@ function getGrid(that, tableRef) {
311
476
  // that.fileRows.push(dataMap);
312
477
  if (mtExcedNum < fileNum) {
313
478
  loopDo();
314
- } else if (
315
- mtExcedNum >= fileNum &&
316
- mtExcedSuccessNum >= fileNum
317
- ) {
318
- setTimeout(function () {
319
- that.percentageNum = parseInt(100);
320
- }, 100);
321
- that.clearExportTimer();
322
479
  } else if (mtExcedSuccessNum >= fileNum) {
323
- setTimeout(function () {
324
- that.percentageNum = parseInt(100);
325
- }, 100);
326
- that.clearExportTimer();
480
+ // 走到 else 分支时 mtExcedNum >= fileNum 必然成立,
481
+ // 原先拆成的两个 else-if 条件与分支体完全相同,合并为一处
482
+ that.completeExport();
327
483
  }
328
484
  } else {
329
485
  that.abortExport();
330
486
  }
331
487
  })
332
488
  .catch(function (e) {
489
+ if (that.isExportStopped()) return;
333
490
  console.error(e);
334
491
  that.abortExport(that.$t1("文件合并异常,导出已中止"));
335
492
  });
336
493
  // 注意:此处不能同步 clearExportTimer,否则计时统计会在合并完成前被提前停掉
337
494
  }
338
- };
339
- loopDo();
340
- },
341
- handleLoopToDo() {
342
- let that = this;
343
- let uuid;
344
- let num = 0;
345
- let title = this.title;
346
-
347
- let options = this.option;
348
-
349
- let pPageSize = this.getExportPageSize();
350
- let limitFileSize = options.limitFileSize || 100000;
351
- let limitThreadNum = options.limitThreadNum || 5;
495
+ };
496
+ loopDo();
497
+ },
498
+ /** 统一组装一页条件导出的取数请求,保证两种查总数模式使用完全相同的查询条件。 */
499
+ async requestExportPage(pageNumber, pageSize, searchCount) {
500
+ let options = this.option;
501
+ let originOption = this.tableTarget.params.originOption;
502
+ let url = originOption.exportPath || originOption.path;
503
+ let params = this.$baseLodash.cloneDeep(this.queryParam);
504
+ let aParam = {
505
+ current: pageNumber,
506
+ size: pageSize,
507
+ nowDate: this.nowDate ?? options.nowDate,
508
+ searchCount,
509
+ };
510
+ let cpParam;
511
+ if (originOption.vform === true) {
512
+ params.conditions = params.conditions || {};
513
+ cpParam = params.conditions;
514
+ } else {
515
+ cpParam = params;
516
+ }
517
+ Object.assign(cpParam, aParam);
518
+
519
+ if (originOption.exportAjax) {
520
+ return originOption.exportAjax(aParam, {
521
+ ...options,
522
+ exportQuerySnapshot: this.$baseLodash.cloneDeep(this.queryParam),
523
+ });
524
+ }
525
+ // URL 上若写死 searchCount,会与本次模式冲突;实际值统一由请求体控制。
526
+ let resolvedUrl = typeof url === "function" ? url() : url;
527
+ if (typeof resolvedUrl !== "string" || !resolvedUrl) {
528
+ throw new Error("无效的导出查询地址");
529
+ }
530
+ let requestUrl = resolvedUrl
531
+ .replace("searchCount=false", "1=1")
532
+ .replace("searchCount=true", "1=1");
533
+ let finalParams = params;
534
+ if (originOption.paramHandle) {
535
+ finalParams = originOption.paramHandle(params) || {};
536
+ }
537
+ return this.$commonHttp({
538
+ aes: originOption.aes || false,
539
+ url: requestUrl,
540
+ method: "post",
541
+ data: finalParams,
542
+ modal: false,
543
+ queryCreateInfo: originOption.queryCreateInfo,
544
+ addCreateInfo: originOption.addCreateInfo,
545
+ cancelToken: this.getCancelToken(),
546
+ });
547
+ },
548
+ /** 导出尚在取数/合并时最多显示 99%,100% 只由 completeExport 设置。 */
549
+ updateExportProgress(doneSize, progressTotal) {
550
+ this.doneSize = parseInt(doneSize);
551
+ let percentage = progressTotal > 0 ? (doneSize * 100) / progressTotal : 99;
552
+ if (percentage > 0 && percentage < 1) percentage = 1;
553
+ if (percentage >= 100) percentage = 99;
554
+ this.percentageNum = parseInt(percentage);
555
+ },
556
+ /**
557
+ * 不查询总数:每页都发 searchCount=false,以 records.length < pageSize 判定末页。
558
+ * 取数与写入按页串行,确保发现末页或达到最大查询总数后不会继续派发多余请求。
559
+ */
560
+ handleLoopWithoutCount(pageSize) {
561
+ let that = this;
562
+ let maxQueryTotal = this.conditionExportConfig.maxQueryTotal;
563
+ let maxPages = Math.max(Math.ceil(maxQueryTotal / pageSize), 1);
564
+ let doneSize = 0;
565
+ this.tTotalPage = 0;
566
+ this.tTotalSize = 0;
567
+ this.totalSizeKnown = false;
568
+
569
+ let finish = function (lastPage) {
570
+ if (that.isExportStopped()) return;
571
+ that.tTotalPage = Math.max(lastPage, 1);
572
+ that.hadleMergeExcel(pageSize);
573
+ };
574
+
575
+ let fetchPage = function (pageNumber, retryCount = 0) {
576
+ if (that.isExportStopped()) return Promise.resolve(null);
577
+ return that
578
+ .requestExportPage(pageNumber, pageSize, false)
579
+ .then((resultMsg) => {
580
+ if (resultMsg && resultMsg.type === "success") return resultMsg;
581
+ if (retryCount < 1) {
582
+ return new Promise((resolve) => {
583
+ setTimeout(
584
+ () => resolve(fetchPage(pageNumber, retryCount + 1)),
585
+ 500
586
+ );
587
+ });
588
+ }
589
+ that.abortExport();
590
+ return null;
591
+ })
592
+ .catch((error) => {
593
+ if (that.isExportStopped()) return null;
594
+ if (retryCount < 1) {
595
+ return new Promise((resolve) => {
596
+ setTimeout(
597
+ () => resolve(fetchPage(pageNumber, retryCount + 1)),
598
+ 500
599
+ );
600
+ });
601
+ }
602
+ console.error(error);
603
+ that.abortExport(
604
+ that.$t1("第{n}页数据拉取失败,导出已中止", {
605
+ n: pageNumber,
606
+ })
607
+ );
608
+ return null;
609
+ });
610
+ };
611
+
612
+ let appendPage = function (pageNumber) {
613
+ fetchPage(pageNumber).then((resultMsg) => {
614
+ if (!resultMsg || that.isExportStopped()) return;
615
+ let page = resultMsg.objx || {};
616
+ let actualSize = Number(page.size || page.pageSize || pageSize);
617
+ if (!Number.isSafeInteger(actualSize) || actualSize <= 0
618
+ || actualSize > pageSize || (pageNumber > 1 && actualSize !== pageSize)) {
619
+ that.abortExport(that.$t1("服务端分页大小发生变化,导出已中止"));
620
+ return;
621
+ }
622
+ pageSize = actualSize;
623
+ maxPages = Math.max(Math.ceil(maxQueryTotal / pageSize), 1);
624
+ let rawRows = Array.isArray(page.records) ? page.records : [];
625
+ let hasNext = rawRows.length >= pageSize;
626
+
627
+ // 整页数据需要再探测一页;探测到的空页不写入,也不参与合并。
628
+ if (pageNumber > 1 && rawRows.length === 0) {
629
+ finish(pageNumber - 1);
630
+ return;
631
+ }
632
+
633
+ let remaining = Math.max(maxQueryTotal - doneSize, 0);
634
+ let rows = rawRows.slice(0, remaining);
635
+ let reachedLimit = rows.length >= remaining;
636
+ page.current = pageNumber;
637
+ page.pageNumber = pageNumber;
638
+ page.size = pageSize;
639
+ page.pageSize = pageSize;
640
+ page.records = rows;
641
+
642
+ that
643
+ .addExcelData({ rows, pageNumber })
644
+ .then((appendResult) => {
645
+ if (that.isExportStopped()) return;
646
+ if (!appendResult || appendResult.type !== "success") {
647
+ if (appendResult && appendResult.type === "abort") return;
648
+ that.abortExport();
649
+ return;
650
+ }
651
+ doneSize += rows.length;
652
+ that.updateExportProgress(doneSize, maxQueryTotal);
653
+ if (reachedLimit || !hasNext || pageNumber >= maxPages) {
654
+ finish(pageNumber);
655
+ return;
656
+ }
657
+ appendPage(pageNumber + 1);
658
+ })
659
+ .catch((error) => {
660
+ if (that.isExportStopped()) return;
661
+ console.error(error);
662
+ that.abortExport(that.$t1("导出数据写入失败,导出已中止"));
663
+ });
664
+ });
665
+ };
666
+
667
+ appendPage(1);
668
+ },
669
+ handleLoopToDo() {
670
+ let that = this;
671
+ let num = 0;
672
+ let options = this.option;
673
+
674
+ let pPageSize = this.getExportPageSize();
675
+ let limitThreadNum = options.limitThreadNum || 5;
676
+
677
+ if (
678
+ this.conditionExportMode
679
+ && this.conditionExportConfig.searchCount === false
680
+ ) {
681
+ this.handleLoopWithoutCount(pPageSize);
682
+ return;
683
+ }
352
684
 
353
685
  /**处理返回值*/
354
686
  this.doneSize = 0;
355
687
  let excedNum = 0;
356
688
  let cSize = this.doneSize;
357
689
 
358
- let handleData = function (resultMsg) {
690
+ let handleData = function (resultMsg, flag) {
691
+ // 取消之后到达的在途响应直接丢弃,不再进入上送/合并流程
692
+ if (that.isExportStopped()) return;
359
693
  let pageNumber = resultMsg.objx.pageNumber || 1;
694
+ // 并发窗口在"该页上送完成"后才释放,而不是"该页拉取完成"就释放。
695
+ // addExcelData 此前完全不受闸门约束:上送慢于拉取时 pending 的 POST 会无限
696
+ // 堆积(每个都攥着一份几 MB 的 JSON body),加上浏览器每域名 6 连接会让上送
697
+ // 排到拉取后面,这种反压对调度器完全不可见 —— 大数据量导出的内存曲线因此
698
+ // 始终只涨不落。
699
+ let released = flag !== 1; // 首页(flag=0)不占窗口,无需释放
700
+ let releaseWindow = function () {
701
+ if (released) return;
702
+ released = true;
703
+ if (waitReqNum > 0) waitReqNum--;
704
+ };
360
705
  that
361
706
  .addExcelData({
362
707
  rows: resultMsg.objx.records,
363
708
  pageNumber: pageNumber,
364
709
  })
365
- .then(function (resultMsg2) {
366
- let data = resultMsg.objx;
367
- let total = data.total;
368
-
369
- let pageNumber = data.pageNumber || 1;
370
- let totalPages = that.tTotalPage || 1;
371
- if (resultMsg2.type === "success") {
372
- excedNum++;
373
- let pageNumber = data.pageNumber || 1;
374
- let k = (excedNum * 100) / totalPages;
375
- if (k > 0 && k < 1) {
376
- k = 1;
377
- }
378
- if (k > 100) k = 100;
379
- let cTotal = that.tTotalSize;
380
- let pageSize = data.pageSize;
381
- // let datas = data.records;
382
- let size = data.records.length;
383
- cSize = cSize + size;
384
-
385
- let ttNum = cSize + "/" + cTotal;
386
- let percentageStr = parseInt(k) + "%";
387
-
388
- let percentageNum = k;
389
- if (percentageNum >= 100) {
390
- percentageNum = 99;
391
- }
392
- // that.$set(that, "doneSize", parseInt(cSize));
393
- // that.$set(that, "percentageNum", parseInt(percentageNum));
394
-
395
- that.doneSize = parseInt(cSize);
396
- that.percentageNum = parseInt(percentageNum);
710
+ .then(function (resultMsg2) {
711
+ releaseWindow();
712
+ let data = resultMsg.objx;
713
+ let totalPages = that.tTotalPage || 1;
714
+ if (resultMsg2.type === "success") {
715
+ excedNum++;
716
+ let size = data.records.length;
717
+ cSize = cSize + size;
718
+ that.updateExportProgress(cSize, that.tTotalSize);
397
719
 
398
720
  if (totalPages === 0 || excedNum >= totalPages) {
399
721
  that.hadleMergeExcel(data.pageSize);
@@ -481,101 +803,56 @@ function getGrid(that, tableRef) {
481
803
  }
482
804
  })
483
805
  .catch(function (e) {
806
+ releaseWindow();
807
+ if (that.isExportStopped()) return;
484
808
  console.error(e);
485
809
  that.abortExport(that.$t1("导出数据写入失败,导出已中止"));
486
810
  });
487
811
  };
488
812
 
489
- let waitReqNum = 0;
490
- let promise;
491
- let loopToDo = function (flag, pageNumber, retryCount = 0) {
492
- let maxWaitNum = limitThreadNum;
493
- let nowDate = options.nowDate;
494
- if (flag === 1) {
495
- /*if (num > that.tTotalPage) {
496
- let size = page.records.length;
497
- if (size === 0 || page.totalPages === 0 || (page.pageNumber >= page.totalPages)) {
498
- that.clearExportTimer();
499
- return;
500
- }
501
- }*/
502
- } else {
503
- waitReqNum = 0;
504
- }
505
-
506
- let target = that.tableTarget;
507
- let originOption = target.params.originOption;
508
- let url = originOption.exportPath || originOption.path;
509
-
510
- // let params = originOption.param() || {};
511
- let params = that.queryParam;
512
- let aParam = {
513
- current: pageNumber,
514
- size: pPageSize,
515
- nowDate: nowDate,
516
- searchCount: flag !== 1,
517
- };
518
- let cpParam;
519
- if (originOption.vform === true) {
520
- params.conditions = params.conditions || {};
521
- cpParam = params.conditions;
522
- } else {
523
- cpParam = params;
524
- }
525
- Object.assign(cpParam, aParam);
526
-
527
- if (!originOption.exportAjax) {
528
- let turl = url.replace("searchCount=false", "1=1");
529
- let finaParams = params;
530
- if (originOption.paramHandle) {
531
- finaParams = originOption.paramHandle
532
- ? originOption.paramHandle(params) || {}
533
- : {};
534
- }
535
- promise = that.$commonHttp({
536
- aes: originOption.aes || false,
537
- url: turl,
538
- method: "post",
539
- data: finaParams,
540
- modal: false,
541
- queryCreateInfo: originOption.queryCreateInfo,
542
- addCreateInfo: originOption.addCreateInfo,
543
- });
544
- } else {
545
- promise = originOption.exportAjax(aParam, options);
546
- }
547
-
548
- promise
549
- .then(function (resultMsg) {
550
- if (resultMsg.type === "success") {
551
- let objx = resultMsg.objx;
552
- objx.pageNumber = objx.current || 1;
553
- objx.totalPages = objx.pages || 1;
554
- objx.pageSize = objx.size;
555
- let size = objx.records.length;
556
- if (flag === 0) {
557
- that.tTotalPage = objx.pages;
558
- // that.tTotalSize = objx.total;
559
- setTimeout(function () {
560
- that.$set(that, "tTotalSize", objx.total);
561
- }, 0);
562
- let tTotalPage = objx.pages;
563
- let tTotalSize = objx.total;
564
- let ttNum = 0 + "/" + objx.total;
565
-
566
- let opNum = limitFileSize;
567
- let kTotalPages = parseInt(opNum / objx.size);
568
-
569
- let fileNum = parseInt(tTotalPage / kTotalPages);
570
- if (tTotalPage % kTotalPages !== 0) {
571
- fileNum = fileNum + 1;
572
- }
573
- if (
574
- size > 0 ||
575
- objx.totalPages > 0 ||
576
- objx.pageNumber < objx.totalPages
577
- ) {
578
- that.exportTimer = setInterval(function () {
813
+ let waitReqNum = 0;
814
+ let loopToDo = function (flag, pageNumber, retryCount = 0) {
815
+ // 取消后不再发起新页,也不再重试(重试是从 .catch 里递归回来的)
816
+ if (that.isExportStopped()) return;
817
+ let maxWaitNum = limitThreadNum;
818
+ if (flag !== 1) waitReqNum = 0;
819
+
820
+ that
821
+ .requestExportPage(pageNumber, pPageSize, flag !== 1)
822
+ .then(function (resultMsg) {
823
+ if (resultMsg.type === "success") {
824
+ let objx = resultMsg.objx || {};
825
+ objx.records = Array.isArray(objx.records) ? objx.records : [];
826
+ objx.pageNumber = objx.current || pageNumber;
827
+ objx.pageSize = objx.size || pPageSize;
828
+ let actualSize = Number(objx.pageSize);
829
+ if (!Number.isSafeInteger(actualSize) || actualSize <= 0
830
+ || actualSize > pPageSize || (flag !== 0 && actualSize !== pPageSize)) {
831
+ that.abortExport(that.$t1("服务端分页大小发生变化,导出已中止"));
832
+ return;
833
+ }
834
+ pPageSize = actualSize;
835
+ objx.pageSize = actualSize;
836
+ let size = objx.records.length;
837
+ if (flag === 0) {
838
+ let rawTotal = Number(objx.total);
839
+ if (!Number.isFinite(rawTotal) || rawTotal < 0) {
840
+ rawTotal = size;
841
+ }
842
+ let exportTotal = that.conditionExportMode
843
+ ? Math.min(
844
+ rawTotal,
845
+ that.conditionExportConfig.maxQueryTotal
846
+ )
847
+ : rawTotal;
848
+ that.tTotalPage = that.conditionExportMode
849
+ ? Math.max(Math.ceil(exportTotal / pPageSize), 1)
850
+ : objx.pages;
851
+ objx.totalPages = that.tTotalPage || 1;
852
+ that.tTotalSize = exportTotal;
853
+ let tTotalPage = that.tTotalPage || 1;
854
+ if (tTotalPage > 1) {
855
+ that.exportTimer = setInterval(function () {
579
856
  if (waitReqNum < maxWaitNum && num < tTotalPage) {
580
857
  waitReqNum++;
581
858
  num++;
@@ -590,11 +867,17 @@ function getGrid(that, tableRef) {
590
867
  }, 100);
591
868
  } else {
592
869
  clearInterval(that.exportTimer);
593
- that.exportTimer = null;
594
- }
595
- } else {
596
- if (waitReqNum > 0) waitReqNum--;
597
- }
870
+ that.exportTimer = null;
871
+ }
872
+ }
873
+ if (that.conditionExportMode) {
874
+ // 总数超过有效上限时,最后一页只写允许范围内的数据。
875
+ let rowOffset = (pageNumber - 1) * pPageSize;
876
+ let allowedRows = Math.max(that.tTotalSize - rowOffset, 0);
877
+ objx.records = objx.records.slice(0, allowedRows);
878
+ }
879
+ // 注意:此处不再释放并发窗口。拉取完成只是这一页做完了一半,
880
+ // 窗口由 handleData 在上送(addExcelData)落地后释放,见那里的注释。
598
881
 
599
882
  setTimeout(function () {
600
883
  handleData(resultMsg, flag);
@@ -611,6 +894,8 @@ function getGrid(that, tableRef) {
611
894
  }
612
895
  })
613
896
  .catch(function (e) {
897
+ // 取消导致的 reject 不是失败,也不该重试或弹提示
898
+ if (that.isExportStopped()) return;
614
899
  console.error(e);
615
900
  if (retryCount < 1) {
616
901
  setTimeout(function () {
@@ -641,6 +926,7 @@ function getGrid(that, tableRef) {
641
926
  data: datas,
642
927
  modal: loading || false,
643
928
  isLoading: loading,
929
+ cancelToken: this.getCancelToken(),
644
930
  });
645
931
  },
646
932
  /*getExportTitleJson() {
@@ -742,21 +1028,31 @@ function getGrid(that, tableRef) {
742
1028
  }
743
1029
  return titleArr;
744
1030
  },
745
- addExcelData(opts, loading) {
1031
+ async addExcelData(opts, loading) {
746
1032
  let target = opts.target;
747
1033
  let pageNumber = opts.pageNumber || 1;
748
1034
  let callback = opts.callback;
749
- let datas = this.getExcelData(opts.rows);
1035
+ // 已取消就不要再算、更不要再发。这里是所有上送的唯一入口,卡住它等于卡住
1036
+ // 取消之后的全部后续动作。
1037
+ if (this.isExportStopped()) {
1038
+ return { type: "abort" };
1039
+ }
1040
+ let datas = await this.getExcelData(opts.rows);
1041
+ // 取值过程中导出已被中止(getExcelData 让出主线程时会检查),不再上送这一页。
1042
+ // 返回非 success 让调用方走它自己的中止分支,abortExport 幂等不会重复弹窗。
1043
+ if (datas === null) {
1044
+ return { type: "abort" };
1045
+ }
750
1046
  let cUUid = this.uuid;
751
1047
  let ippaaapp = this.ippaaapp;
752
- let url1 =
753
- this.CURRENT_PREFIX +
754
- "/excel/appendExcelData?uuid=" +
755
- cUUid +
756
- "&ippaaapp=" +
757
- ippaaapp +
758
- "&pageNumber=" +
759
- pageNumber;
1048
+ let url1
1049
+ = this.CURRENT_PREFIX
1050
+ + "/excel/appendExcelData?uuid="
1051
+ + cUUid
1052
+ + "&ippaaapp="
1053
+ + ippaaapp
1054
+ + "&pageNumber="
1055
+ + pageNumber;
760
1056
  return this.$http({
761
1057
  url: url1,
762
1058
  method: "post",
@@ -766,20 +1062,22 @@ function getGrid(that, tableRef) {
766
1062
  data: datas,
767
1063
  modal: loading || false,
768
1064
  isLoading: loading || false,
1065
+ cancelToken: this.getCancelToken(),
769
1066
  });
770
1067
  },
771
1068
  mergeExcel(startPage, endPage, loading) {
772
- let url1 =
773
- this.CURRENT_PREFIX +
774
- "/excel/mergeExcel?uuid=" +
775
- this.uuid +
776
- "&ippaaapp=" +
777
- this.ippaaapp;
1069
+ let url1
1070
+ = this.CURRENT_PREFIX
1071
+ + "/excel/mergeExcel?uuid="
1072
+ + this.uuid
1073
+ + "&ippaaapp="
1074
+ + this.ippaaapp;
778
1075
  return this.$http({
779
1076
  url: url1 + "&startPage=" + startPage + "&endPage=" + endPage,
780
1077
  method: "post",
781
1078
  modal: loading || false,
782
1079
  isLoading: loading || false,
1080
+ cancelToken: this.getCancelToken(),
783
1081
  });
784
1082
  },
785
1083
  createCountTimer() {
@@ -797,13 +1095,36 @@ function getGrid(that, tableRef) {
797
1095
  this.exportTimer = null;
798
1096
  this.countTimer = null;
799
1097
  },
1098
+ /**
1099
+ * 导出全部文件合并成功:进度置满、停掉计时,并**立刻解开页签锁**。
1100
+ * 锁只保护取数期间。文件已经生成之后弹框还开着(用户在看列表、点下载)是完全
1101
+ * 合法的状态,那时页签必须能正常关闭 —— 解锁此前只挂在 beforeDestroy 上,
1102
+ * 用户不点「取消/确定」就永远解不开,正是「导出完成后页签关不掉」的来源。
1103
+ */
1104
+ completeExport() {
1105
+ if (!this.totalSizeKnown) {
1106
+ this.tTotalSize = this.doneSize;
1107
+ this.totalSizeKnown = true;
1108
+ }
1109
+ setTimeout(() => {
1110
+ if (this.isExportStopped()) return;
1111
+ this.percentageNum = parseInt(100);
1112
+ }, 100);
1113
+ this.clearExportTimer();
1114
+ this.unlockMenu();
1115
+ },
800
1116
  /**
801
1117
  * 中止导出:停止全部定时器并把进度条置为错误态。
802
1118
  * @param {String} [msg] 需要额外弹给用户的提示;服务端已 toast 过的失败不传,避免重复提示。
803
1119
  */
804
1120
  abortExport(msg) {
805
1121
  this.clearExportTimer();
1122
+ // 必须先置标志再取消:取消会让其余在途请求 reject,它们的 .catch 先问
1123
+ // isExportStopped() 才不会重复弹提示
806
1124
  this.exportError = true;
1125
+ this.cancelPendingRequests();
1126
+ // 中止后弹框可能还挂在那儿(错误态进度条),页签没有理由继续锁着
1127
+ this.unlockMenu();
807
1128
  if (msg) this.$baseAlert(msg);
808
1129
  },
809
1130
  isPicture: function (suffix) {
@@ -841,10 +1162,10 @@ function getGrid(that, tableRef) {
841
1162
  return column.slots.filterVal(params);
842
1163
  }
843
1164
  let slotDefault = column.slots && column.slots.default;
844
- let hasRenderFn =
845
- !!column.renderCell ||
846
- typeof slotDefault === "function" ||
847
- typeof slotDefault === "string";
1165
+ let hasRenderFn
1166
+ = !!column.renderCell
1167
+ || typeof slotDefault === "function"
1168
+ || typeof slotDefault === "string";
848
1169
  if (!hasRenderFn) {
849
1170
  let cellValue = params.row[column.field];
850
1171
  if (typeof column.formatter === "function") {
@@ -858,7 +1179,7 @@ function getGrid(that, tableRef) {
858
1179
  }
859
1180
  return getCellValue(params);
860
1181
  },
861
- getExcelData(rows) {
1182
+ async getExcelData(rows) {
862
1183
  let that = this;
863
1184
 
864
1185
  let showImageAtTable = this.option.showImageAtTable || false;
@@ -875,8 +1196,19 @@ function getGrid(that, tableRef) {
875
1196
  let contents;
876
1197
  let params;
877
1198
  let resultStr;
878
- // 批量取值期间复用 vNode 渲染的临时实例,结束时统一销毁
879
- rows.forEach(function (row, index) {
1199
+ let index = -1;
1200
+ for (const row of rows) {
1201
+ index++;
1202
+ // 每 EXPORT_YIELD_ROWS 行让出一次主线程。取值本身是同步的双层遍历,一页
1203
+ // 默认 1000 行、渲染兜底列还要逐单元格挂载组件,整页跑完可达数秒;并发几页
1204
+ // 几乎同时到达时主线程被连续占死,进度条不动、「取消」点不动,浏览器还可能
1205
+ // 直接判定页面无响应而杀掉标签页。让出不减少总工作量,只保证 UI 活着。
1206
+ if (index > 0 && index % EXPORT_YIELD_ROWS === 0) {
1207
+ await yieldToBrowser();
1208
+ // 让出期间用户可能已取消/导出已中止,此时继续算完这一页没有意义,
1209
+ // 且合并根本不会发生,返回 null 让调用方跳过上送。
1210
+ if (that.isExportStopped()) return null;
1211
+ }
880
1212
  contents = [];
881
1213
  columns.forEach((column) => {
882
1214
  if (column.title) {
@@ -891,27 +1223,38 @@ function getGrid(that, tableRef) {
891
1223
  // _columnIndex
892
1224
  };
893
1225
  let exportType = column.params?.exportType;
894
- let exportVal =
895
- column.params && column.params.exportVal
1226
+ let exportVal
1227
+ = column.params && column.params.exportVal
896
1228
  ? column.params.exportVal
897
1229
  : null;
898
1230
  if (exportVal) {
899
1231
  resultStr = exportVal(params);
1232
+ } else if (column.params?.exportBlank && !exportType) {
1233
+ // 列保留、格留空:没配导出类型的附件列没有可写的内容,更不该为此
1234
+ // 逐单元格挂载附件组件去爬文件名。判定见 xform 的 isBlankExportColumn。
1235
+ // 放在 exportVal 之后:调用方显式写了取值函数就以它为准。
1236
+ // 必须再判一次 exportType:exportBlank 是建列时按 t.exportType 算死的,
1237
+ // 而 params.exportType 之后还会被 tableColumnConfig 脚本覆盖,只认
1238
+ // exportBlank 会把运行期才拿到导出类型的图片列错误地导成空。
1239
+ resultStr = null;
900
1240
  } else if (exportType === "Number") {
901
1241
  resultStr = that.getExportCellValue(params);
902
1242
  if (
903
- resultStr !== null &&
904
- resultStr !== undefined &&
905
- resultStr !== "" &&
906
- !(resultStr + "").startsWith("[EXPNUM]")
1243
+ resultStr !== null
1244
+ && resultStr !== undefined
1245
+ && resultStr !== ""
1246
+ && !(resultStr + "").startsWith("[EXPNUM]")
907
1247
  ) {
908
1248
  resultStr = "[EXPNUM]" + resultStr;
909
1249
  }
910
1250
  } else if (
911
- exportType === "Image" ||
912
- (showImageAtTable && exportType === "Image2")
1251
+ exportType === "Image"
1252
+ || (showImageAtTable && exportType === "Image2")
913
1253
  ) {
914
- resultStr = that.getExportCellValue(params);
1254
+ // 取值刻意延到"确实用得上"的那条分支才做:附件的正常形态是数组,
1255
+ // 那条分支会用 row[column.field] 自己拼 [EXPIMG],把取值结果整个覆盖;
1256
+ // 空值分支同样直接给 null。而附件列没有 filterVal,取值会落到
1257
+ // getCellValue 的 vNode 挂载兜底——等于每个单元格白挂一次附件组件。
915
1258
  let attachments = row[column.field];
916
1259
  if (attachments) {
917
1260
  if (Array.isArray(attachments)) {
@@ -919,9 +1262,9 @@ function getGrid(that, tableRef) {
919
1262
  that.isPicture(item.extension)
920
1263
  );
921
1264
  if (items.length > 0) {
922
- resultStr =
923
- "[EXPIMG]" +
924
- items
1265
+ resultStr
1266
+ = "[EXPIMG]"
1267
+ + items
925
1268
  .map((item) => {
926
1269
  let url = item.large || item.medium;
927
1270
  return item.domain + url;
@@ -931,16 +1274,18 @@ function getGrid(that, tableRef) {
931
1274
  resultStr = null;
932
1275
  }
933
1276
  } else {
1277
+ // 单值形态(字段里直接存 URL)才需要取值来嗅后缀
1278
+ resultStr = that.getExportCellValue(params);
934
1279
  if (
935
1280
  that.isPicture(
936
1281
  that.$commonFileUtil.getFileSuffix(resultStr)
937
1282
  )
938
1283
  ) {
939
1284
  if (
940
- resultStr !== null &&
941
- resultStr !== undefined &&
942
- resultStr !== "" &&
943
- !(resultStr + "").startsWith("[EXPIMG]")
1285
+ resultStr !== null
1286
+ && resultStr !== undefined
1287
+ && resultStr !== ""
1288
+ && !(resultStr + "").startsWith("[EXPIMG]")
944
1289
  ) {
945
1290
  resultStr = "[EXPIMG]" + resultStr;
946
1291
  }
@@ -959,20 +1304,45 @@ function getGrid(that, tableRef) {
959
1304
  }
960
1305
  });
961
1306
  arr.push(contents);
962
- });
1307
+ }
963
1308
  return arr;
964
1309
  },
1310
+ /**
1311
+ * 从响应头解析下载文件名,取不到时用导出标题兜底。
1312
+ * @param {Object} response axios 原始响应。
1313
+ * @param {String} fallbackName 兜底文件名(不含扩展名)。
1314
+ * @returns {String}
1315
+ */
1316
+ resolveDownloadFileName(response, fallbackName) {
1317
+ let headers = response?.headers || {};
1318
+ let disposition = headers["content-disposition"] || headers["Content-Disposition"];
1319
+ if (disposition) {
1320
+ // filename*=UTF-8''xxx 优先,其次 filename="xxx"
1321
+ let starMatch = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(disposition);
1322
+ let plainMatch = /filename="?([^";]+)"?/i.exec(disposition);
1323
+ let raw = (starMatch && starMatch[1]) || (plainMatch && plainMatch[1]);
1324
+ if (raw) {
1325
+ try {
1326
+ return decodeURIComponent(raw.trim());
1327
+ } catch (e) {
1328
+ return raw.trim();
1329
+ }
1330
+ }
1331
+ }
1332
+ return fallbackName + ".xlsx";
1333
+ },
965
1334
  exportAll(row) {
1335
+ let that = this;
966
1336
  let cUUid = encodeURIComponent(row.uuid);
967
1337
  let ippaaapp = this.ippaaapp;
968
1338
  let fileName = row.title;
969
- let fileUrl =
970
- configUtil.baseUrl +
971
- this.CURRENT_PREFIX +
972
- "/excel/download?uuid=" +
973
- cUUid +
974
- "&ippaaapp=" +
975
- ippaaapp;
1339
+ let fileUrl
1340
+ = configUtil.baseUrl
1341
+ + this.CURRENT_PREFIX
1342
+ + "/excel/download?uuid="
1343
+ + cUUid
1344
+ + "&ippaaapp="
1345
+ + ippaaapp;
976
1346
 
977
1347
  function downLoadFile2() {
978
1348
  let params = {
@@ -982,8 +1352,8 @@ function getGrid(that, tableRef) {
982
1352
 
983
1353
  let form = document.createElement("form");
984
1354
 
985
- let randomNum =
986
- new Date().valueOf() + Math.floor(Math.random() * 1000000);
1355
+ let randomNum
1356
+ = new Date().valueOf() + Math.floor(Math.random() * 1000000);
987
1357
  let formId = "formId-" + randomNum;
988
1358
  let formName = "formName-" + randomNum;
989
1359
 
@@ -1006,14 +1376,70 @@ function getGrid(that, tableRef) {
1006
1376
  document.body.removeChild(form);
1007
1377
  }
1008
1378
 
1009
- downLoadFile2();
1379
+ /**
1380
+ * 优先走 axios blob 下载:token 由 request.js 的请求拦截器注入
1381
+ * X-Token / Authorization 头,不必再把 access_token 塞进表单字段随请求体
1382
+ * 明文提交;失败时也能拿到响应体做判断,而表单 POST 打开新窗口后失败只会给
1383
+ * 用户看一个空白页或一坨错误 JSON。
1384
+ *
1385
+ * 后端若尚未放开该接口的头鉴权(此前只认表单里的 access_token),这里会拿到
1386
+ * JSON 错误体或直接报错 —— 一律回退到原来的表单 POST,保证下载不会因为这次
1387
+ * 改造而失效。同体系的 /export_ins/download 已经是 blob + 头鉴权的用法
1388
+ * (见 components/baseInputExport/mixins.js),大概率是通的,但**需联调确认**。
1389
+ */
1390
+ let fallbackToFormPost = function (reason) {
1391
+ console.warn("[excelExport] blob 下载不可用,回退表单 POST:", reason);
1392
+ downLoadFile2();
1393
+ };
1394
+
1395
+ let apiUrl = `${this.CURRENT_PREFIX}/excel/download?uuid=${cUUid}&ippaaapp=${ippaaapp}&n=${encodeURIComponent(fileName)}`;
1396
+
1397
+ this.$http({
1398
+ url: apiUrl,
1399
+ method: "post",
1400
+ data: {},
1401
+ responseType: "blob",
1402
+ resultType: "other",
1403
+ modal: false,
1404
+ // 失败要静默回退,不能先弹一个错误框
1405
+ errorMsg: false,
1406
+ // 刻意**不带**本次导出的取消令牌。下载不是导出流水线的一环,而是用户单独
1407
+ // 发起的动作:分卷文件大时点完「下载」要等几秒,此时关掉进度弹框(「取消」
1408
+ // 和「确定」都走 dialogClose2 → cancelPendingRequests)会掐断这个请求,而
1409
+ // 下面的 isCancel 分支又刻意不回退表单 POST —— 结果是文件静默地下不下来,
1410
+ // 且没有任何提示。旧的表单 POST 脱离组件生命周期,本就不受关弹框影响。
1411
+ callback: (res, response) => {
1412
+ // 后端把错误当 200 返回时,body 是 JSON / HTML 错误页而不是 xlsx 流
1413
+ if (!isDownloadableBlob(res)) {
1414
+ fallbackToFormPost("响应不是文件流:" + (res && res.type));
1415
+ return;
1416
+ }
1417
+ let blobUrl = window.URL.createObjectURL(res);
1418
+ let a = document.createElement("a");
1419
+ a.href = blobUrl;
1420
+ a.download = that.resolveDownloadFileName(response, fileName);
1421
+ document.body.appendChild(a);
1422
+ a.click();
1423
+ document.body.removeChild(a);
1424
+ // 不释放会一直占着这份文件的内存,大导出下很可观
1425
+ setTimeout(() => window.URL.revokeObjectURL(blobUrl), 0);
1426
+ },
1427
+ }).catch((e) => {
1428
+ // 只挡真正的取消:取消后回退表单 POST 会在取消之后又弹出一次下载。
1429
+ // 本方法已不带取消令牌,这里是防御性保留(组件销毁等外部因素仍可能取消)。
1430
+ // 这里**不能**用 isExportStopped()——它含 exportError,而 exportAll 同时是
1431
+ // 进度弹框里的手动下载按钮:导出中途失败后用户手动下载已生成的分卷,正是最
1432
+ // 需要回退兜底的场景,用它拦会变成点了毫无反应。
1433
+ if (axios.isCancel(e)) return;
1434
+ fallbackToFormPost(e && e.message);
1435
+ });
1010
1436
  },
1011
1437
  startProcess2(datas) {
1012
1438
  this.tableTarget = getGrid(this.option.vue, this.option.targetRef);
1013
1439
  let that = this;
1014
1440
  let options = this.option;
1015
- let title =
1016
- options.title || this.$t2("导出", "components.excelExport.title");
1441
+ let title
1442
+ = options.title || this.$t2("导出", "components.excelExport.title");
1017
1443
  if (!options.prefix && options.prefix !== "") {
1018
1444
  let originOption = this.tableTarget.params.originOption;
1019
1445
  let path = originOption.exportPath || originOption.path;
@@ -1035,20 +1461,49 @@ function getGrid(that, tableRef) {
1035
1461
  }
1036
1462
  this.CURRENT_PREFIX = options.prefix;
1037
1463
 
1038
- let handleData = function () {
1039
- let total = datas.length;
1040
- that
1464
+ // 勾选/当前页导出此前把全部行塞进**一个**请求上送。`getCheckboxRecords(true)`
1465
+ // 是跨页全量勾选,选得多时请求体轻易超限(后端/网关直接拒),而且整批取值转换
1466
+ // 与 JSON 序列化一次性压在主线程上。改为复用条件导出那套「分页 append + 一次
1467
+ // merge」:按 getExportPageSize() 切片顺序上送。
1468
+ // 注意页数 ≤ 1 时(勾选量不超过一页,即绝大多数场景)请求序列与旧实现完全一致,
1469
+ // 不引入任何行为变化;这里刻意保持**串行**,避免把条件导出那边"上送无并发闸门"
1470
+ // 的老问题原样复制过来。
1471
+ let pageSize = this.getExportPageSize();
1472
+ let totalPages = Math.max(Math.ceil(datas.length / pageSize), 1);
1473
+
1474
+ let appendPage = function (pageNumber) {
1475
+ let start = (pageNumber - 1) * pageSize;
1476
+ return that
1041
1477
  .addExcelData(
1042
1478
  {
1043
- rows: datas,
1044
- pageNumber: 1,
1479
+ rows: datas.slice(start, start + pageSize),
1480
+ pageNumber: pageNumber,
1045
1481
  },
1046
1482
  true
1047
1483
  )
1048
1484
  .then((resultMsg2) => {
1049
- if (resultMsg2.type === "success") {
1050
- setTimeout(() => {
1051
- that.mergeExcel(1, 1, true).then((resultMsg3) => {
1485
+ if (resultMsg2.type !== "success") {
1486
+ // "abort" 是取值过程中组件已销毁/已中止,无需再提示;
1487
+ // 其余失败服务端已 toast 过,abortExport 不带 msg 避免重复弹
1488
+ if (resultMsg2.type !== "abort") that.abortExport();
1489
+ return false;
1490
+ }
1491
+ if (pageNumber >= totalPages) return true;
1492
+ return appendPage(pageNumber + 1);
1493
+ });
1494
+ };
1495
+
1496
+ let handleData = function () {
1497
+ appendPage(1)
1498
+ .then((completed) => {
1499
+ // 缺页合并会静默丢行,任一页失败都必须停在这里
1500
+ if (!completed) return;
1501
+ setTimeout(() => {
1502
+ that
1503
+ .mergeExcel(1, totalPages, true)
1504
+ .then((resultMsg3) => {
1505
+ // 取消后不能再自动弹下载:那会是一个半截文件
1506
+ if (that.isExportStopped()) return;
1052
1507
  if (resultMsg3.type === "success") {
1053
1508
  let fileInfo = resultMsg3.objx;
1054
1509
  let fileName = fileInfo.fileName;
@@ -1056,28 +1511,49 @@ function getGrid(that, tableRef) {
1056
1511
 
1057
1512
  let dataMap = {};
1058
1513
  dataMap["startPage"] = 1;
1059
- dataMap["endPage"] = 1;
1514
+ dataMap["endPage"] = totalPages;
1060
1515
  dataMap["title"] = title;
1061
1516
  dataMap["uuid"] = fileName;
1062
1517
  dataMap["fileSize"] = fileSize;
1063
1518
  that.exportAll(dataMap);
1519
+ } else {
1520
+ that.abortExport();
1064
1521
  }
1522
+ })
1523
+ .catch((e) => {
1524
+ if (that.isExportStopped()) return;
1525
+ console.error(e);
1526
+ that.abortExport(that.$t1("文件合并异常,导出已中止"));
1065
1527
  });
1066
- }, 0);
1067
- }
1528
+ }, 0);
1529
+ })
1530
+ .catch((e) => {
1531
+ if (that.isExportStopped()) return;
1532
+ console.error(e);
1533
+ that.abortExport(that.$t1("导出数据写入失败,导出已中止"));
1068
1534
  });
1069
1535
  };
1070
- that.createExcelFile(true).then((resultMsg) => {
1071
- if (resultMsg.type === "success") {
1072
- let aObj = resultMsg.objx;
1073
- that.uuid = aObj.uuid;
1074
- that.nowDate = aObj.nowDate;
1075
- that.ippaaapp = aObj.ippaaapp;
1076
- setTimeout(() => {
1077
- handleData();
1078
- }, 0);
1079
- }
1080
- });
1536
+ that
1537
+ .createExcelFile(true)
1538
+ .then((resultMsg) => {
1539
+ if (that.isExportStopped()) return;
1540
+ if (resultMsg.type === "success") {
1541
+ let aObj = resultMsg.objx;
1542
+ that.uuid = aObj.uuid;
1543
+ that.nowDate = aObj.nowDate;
1544
+ that.ippaaapp = aObj.ippaaapp;
1545
+ setTimeout(() => {
1546
+ handleData();
1547
+ }, 0);
1548
+ } else {
1549
+ that.abortExport();
1550
+ }
1551
+ })
1552
+ .catch((error) => {
1553
+ if (that.isExportStopped()) return;
1554
+ console.error(error);
1555
+ that.abortExport(that.$t1("创建导出文件失败,导出已中止"));
1556
+ });
1081
1557
  },
1082
1558
  closeExportFieldDialog() {
1083
1559
  this.dialogClose2();
@@ -1122,29 +1598,42 @@ function getGrid(that, tableRef) {
1122
1598
  }
1123
1599
  });
1124
1600
  },
1601
+ /**
1602
+ * 导出期间锁住当前页签(不让关),并记下锁的是哪一个,见 lockedView。
1603
+ * 本就是固定页签(affix)的不接管 —— 那不是我们加的锁,解的时候也不能动它。
1604
+ *
1605
+ * 只写 user_affix,**绝不能再写 meta.affix**:isRepeatable() 见到 affix 就返回
1606
+ * false,可重复打开的页签的 viewKey 会从 fullPath 塌成 path,于是 AppMain 的
1607
+ * router-view key 与 BaseKeepAlive 的缓存名同时错位 —— 切走再切回整页重建。
1608
+ * 「不让批量关闭」的语义由 tagsView 的 isPinned 认 user_affix 兜住。
1609
+ *
1610
+ * 定位页签用 viewKey 而不是 path:可重复打开时同一 path 会有多个页签,
1611
+ * 按 path 找到的可能是另一个实例。
1612
+ */
1125
1613
  lockMenu() {
1126
1614
  let vueTarget = window.$vueRoot;
1127
- let path = vueTarget.$route.path;
1615
+ let key = viewKey(vueTarget.$route);
1128
1616
  let visitedViews = vueTarget.$store.state.tagsView.visitedViews;
1129
- if (visitedViews) {
1130
- let currentItem = visitedViews.find((item) => item.path === path);
1131
- if (currentItem && !currentItem.meta.affix) {
1132
- this.$set(currentItem.meta, "affix", true);
1133
- this.$set(currentItem.meta, "user_affix", true);
1134
- }
1617
+ if (!visitedViews) return;
1618
+ let currentItem = visitedViews.find((item) => viewKey(item) === key);
1619
+ if (currentItem && !currentItem.meta.affix) {
1620
+ this.$set(currentItem.meta, "user_affix", true);
1621
+ this.lockedView = currentItem;
1135
1622
  }
1136
1623
  },
1624
+ /**
1625
+ * 解开自己锁的那一个页签。
1626
+ * **不能**再按当前路由去查:解锁的时机是组件销毁(取消、或下一次导出把上一个
1627
+ * 实例顶掉),那时的当前路由未必还是上锁时那一个。典型漏锁路径:A 页开始条件
1628
+ * 导出 → 切到 B 页 → 在 B 页再点一次导出,initInstance 先销毁旧实例,旧实例按
1629
+ * $route.path 查到的是 B(B 还没锁,什么也没做),于是 A 永久锁死。
1630
+ * 页签可能已被别处删掉,此时改的是一个游离对象,无副作用。
1631
+ */
1137
1632
  unlockMenu() {
1138
- let vueTarget = window.$vueRoot;
1139
- let path = vueTarget.$route.path;
1140
- let visitedViews = vueTarget.$store.state.tagsView.visitedViews;
1141
- if (visitedViews) {
1142
- let currentItem = visitedViews.find((item) => item.path === path);
1143
- if (currentItem && currentItem.meta.user_affix) {
1144
- currentItem.meta.affix = false;
1145
- currentItem.meta.user_affix = false;
1146
- }
1147
- }
1633
+ let view = this.lockedView;
1634
+ this.lockedView = null;
1635
+ if (!view || !view.meta.user_affix) return;
1636
+ view.meta.user_affix = false;
1148
1637
  },
1149
1638
  },
1150
1639
  };