cloud-web-corejs 1.0.54-dev.798 → 1.0.54-dev.799
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 +3 -1
- package/src/components/baseTabs/mixins.js +11 -12
- package/src/components/bizTab/BizBillActions.vue +129 -0
- package/src/components/bizTab/BizTabListPage.vue +391 -0
- package/src/components/bizTab/README.md +378 -0
- package/src/components/bizTab/billTableUtil.js +48 -0
- package/src/components/bizTab/billValidators.js +136 -0
- package/src/components/bizTab/bizBillDetailMixin.js +687 -0
- package/src/components/bizTab/index.js +24 -0
- package/src/components/excelExport/exportFieldDialog.vue +373 -367
- package/src/components/excelExport/exportType.js +49 -0
- package/src/components/excelExport/mixins.js +1153 -1144
- package/src/components/vb-tabs/x-tabs.vue +167 -143
- package/src/components/wf/wfUtil.js +327 -292
- package/src/components/xform/form-designer/designer.js +56 -8
- package/src/components/xform/form-designer/form-widget/container-widget/detail-widget.vue +59 -5
- package/src/components/xform/form-designer/form-widget/field-widget/baseAttachment-widget.vue +60 -8
- package/src/components/xform/form-designer/form-widget/field-widget/form-item-wrapper.vue +8 -2
- package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +120 -92
- package/src/components/xform/form-designer/form-widget/field-widget/vabUpload-widget.vue +63 -94
- package/src/components/xform/form-designer/index.vue +0 -1
- package/src/components/xform/form-designer/setting-panel/dataSource-setting.vue +397 -397
- package/src/components/xform/form-designer/setting-panel/form-dynamicField-setting.vue +602 -602
- package/src/components/xform/form-designer/setting-panel/form-setting.vue +13 -13
- package/src/components/xform/form-designer/setting-panel/option-items-setting.vue +585 -585
- package/src/components/xform/form-designer/setting-panel/property-editor/a-link-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/a-text-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/alignType-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/censusClass-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/colorClass-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/commonAttributeEnabled-editor.vue +3 -3
- package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/data-table-editor.vue +1154 -1154
- package/src/components/xform/form-designer/setting-panel/property-editor/container-detail/detail-editor.vue +96 -80
- package/src/components/xform/form-designer/setting-panel/property-editor/container-detail-plain/detail-plain-editor.vue +75 -75
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid/colHeight-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid/gutter-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid-col/grid-col-height-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid-col/grid-col-offset-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid-col/grid-col-pull-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid-col/grid-col-push-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid-col/grid-col-span-editor.vue +4 -4
- package/src/components/xform/form-designer/setting-panel/property-editor/container-h5-card/h5-card-editor.vue +124 -124
- package/src/components/xform/form-designer/setting-panel/property-editor/container-panel/height-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-panel/width-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-tab/tab-customClass-editor.vue +119 -119
- package/src/components/xform/form-designer/setting-panel/property-editor/container-tab/tabClass-editor.vue +45 -45
- package/src/components/xform/form-designer/setting-panel/property-editor/container-table/table-dynamicField-editor.vue +344 -344
- package/src/components/xform/form-designer/setting-panel/property-editor/container-table-cell/cellHeight-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-table-cell/cellWidth-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-tree/tree-customClass-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/container-tree-pane/tree-pane-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-vf-dialog/cancelButtonLabel-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-vf-dialog/okButtonLabel-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-vf-dialog/title-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-vf-dialog/width-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/containerClass-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/customClass-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/defaultValue-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/descriptionsItems-editor.vue +4 -4
- package/src/components/xform/form-designer/setting-panel/property-editor/downloadButtonFlag-editor.vue +3 -3
- package/src/components/xform/form-designer/setting-panel/property-editor/dropdownFlag-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/dsName-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/echartHeight-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/endPlaceholder-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-accessUrl/accessUrl-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/field-area-select/areaDataType-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-area-select/areaMinLevel-editor.vue +50 -50
- package/src/components/xform/form-designer/setting-panel/property-editor/field-area-select/areaName-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-autocomplete/autocomplete-vabSearchName-editor.vue +7 -7
- package/src/components/xform/form-designer/setting-panel/property-editor/field-button/addTableData-event-editor.vue +84 -84
- package/src/components/xform/form-designer/setting-panel/property-editor/field-button/button-type-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-button/clickBindEvent-editor.vue +97 -97
- package/src/components/xform/form-designer/setting-panel/property-editor/field-button/form-host-event-editor.vue +188 -188
- package/src/components/xform/form-designer/setting-panel/property-editor/field-button/search-dialog-event-editor.vue +237 -237
- package/src/components/xform/form-designer/setting-panel/property-editor/field-common/keyName-editor.vue +74 -67
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date/date-dateLimit-editor.vue +4 -4
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date/date-defaultValue-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date/date-format-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date/date-type-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date/date-valueFormat-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date-range/date-range-defaultTime-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date-range/date-range-defaultValue-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date-range/date-range-format-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date-range/date-range-type-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date-range/date-range-valueFormat-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-divider/contentPosition-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-dropdown-menu/dropdown-menu-editor.vue +59 -59
- package/src/components/xform/form-designer/setting-panel/property-editor/field-echart/echart-bar-editor.vue +11 -11
- package/src/components/xform/form-designer/setting-panel/property-editor/field-echart/echart-category-editor.vue +10 -10
- package/src/components/xform/form-designer/setting-panel/property-editor/field-echart/echart-pie-editor.vue +12 -12
- package/src/components/xform/form-designer/setting-panel/property-editor/field-file-upload/file-upload-fileTypes-editor.vue +44 -44
- package/src/components/xform/form-designer/setting-panel/property-editor/field-html-text/htmlContent-editor.vue +29 -29
- package/src/components/xform/form-designer/setting-panel/property-editor/field-import-button/import-button-editor.vue +6 -6
- package/src/components/xform/form-designer/setting-panel/property-editor/field-import-button/import2-button-editor.vue +3 -3
- package/src/components/xform/form-designer/setting-panel/property-editor/field-number/controlsPosition-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-picture-upload/picture-upload-fileTypes-editor.vue +44 -44
- package/src/components/xform/form-designer/setting-panel/property-editor/field-print-button/print-button-editor.vue +115 -115
- package/src/components/xform/form-designer/setting-panel/property-editor/field-print-button/print-detail-button-editor.vue +115 -115
- package/src/components/xform/form-designer/setting-panel/property-editor/field-rate/highThreshold-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-rate/lowThreshold-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-rate/rate-defaultValue-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-rate/rate-max-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-script/script-editor.vue +4 -4
- package/src/components/xform/form-designer/setting-panel/property-editor/field-script-input/script-input-editor.vue +4 -4
- package/src/components/xform/form-designer/setting-panel/property-editor/field-static-text/fontSize-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-static-text/textContent-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-status/field-status-editor.vue +3 -3
- package/src/components/xform/form-designer/setting-panel/property-editor/field-switch/activeText-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-switch/inactiveText-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-switch/switchWidth-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-button-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +74 -71
- package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/table-export-button-editor.vue +3 -3
- package/src/components/xform/form-designer/setting-panel/property-editor/field-time/time-defaultValue-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-time/time-format-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-time/time-timeLimit-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-time-range/time-range-defaultValue-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-time-range/time-range-format-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-vabSearch/vabSearchField-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/field-vabSearch/vabSearchName-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/field-vabUpload/field-vabUpload-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-vabUpload2/field-vabUpload2-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-vue-page/vue-page-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/fileMaxSize-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/fileTypesSelect.vue +176 -176
- package/src/components/xform/form-designer/setting-panel/property-editor/formScriptEnabled-editor.vue +2 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/formatType-editor.vue +2 -2
- package/src/components/xform/form-designer/setting-panel/property-editor/formula-editor.vue +972 -972
- package/src/components/xform/form-designer/setting-panel/property-editor/iconClass-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/label-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/labelAlign-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/labelColor-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/labelIconPosition-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/labelWidth-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/limit-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/max-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/maxLength-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/min-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/minLength-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/multipleLimit-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/name-editor.vue +178 -178
- package/src/components/xform/form-designer/setting-panel/property-editor/oplog-editor.vue +3 -3
- package/src/components/xform/form-designer/setting-panel/property-editor/placeholder-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/precision-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/project-tag-editor.vue +6 -6
- package/src/components/xform/form-designer/setting-panel/property-editor/requiredHint-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/rows-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/space-editor-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/startPlaceholder-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/step-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/styleTableClass-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/styleTitClass-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/tempStorage-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/type-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/uploadTip-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/uploadURL-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/urlValueEnabled-editor.vue +91 -0
- package/src/components/xform/form-designer/setting-panel/property-editor/validation-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/validationHint-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/widgetWidth-editor.vue +1 -1
- package/src/components/xform/form-designer/setting-panel/propertyRegister.js +1 -0
- package/src/components/xform/form-designer/widget-panel/index.vue +3 -1
- package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +55 -13
- package/src/components/xform/form-render/container-item/data-table-mixin copy.js +5910 -0
- package/src/components/xform/form-render/container-item/data-table-mixin.js +59 -13
- package/src/components/xform/form-render/container-item/detail-item.vue +52 -11
- package/src/components/xform/form-render/container-item/tab-item.vue +6 -2
- package/src/components/xform/form-render/indexMixin.js +56 -16
- package/src/components/xform/lang/zh-CN.js +2 -1
- package/src/components/xform/utils/attachmentValueUtil.js +239 -0
- package/src/components/xform/utils/util.js +27 -0
- package/src/mixins/wf/index.js +40 -38
- package/src/router/modules/customer.js +50 -0
- package/src/store/config/index.js +46 -0
- package/src/utils/scrollUtil.js +25 -0
- package/src/views/demo/bizTab/bill/billConfig.js +41 -0
- package/src/views/demo/bizTab/bill/detail.vue +436 -0
- package/src/views/demo/bizTab/bill/list.vue +288 -0
- package/src/views/demo/bizTab/bill/productDialog.vue +176 -0
- package/src/views/demo/bizTab/simple/billApi.js +123 -0
- package/src/views/demo/bizTab/simple/detail.vue +407 -0
- package/src/views/demo/bizTab/simple/list.vue +217 -0
- package/src/views/demo/bizTab/standalone/index.vue +121 -0
- package/src/views/demo/bizTab/wf/billConfig.js +44 -0
- package/src/views/demo/bizTab/wf/detail.vue +661 -0
- package/src/views/demo/bizTab/wf/list.vue +242 -0
- package/src/views/user/outLink/cc_form_view.vue +185 -0
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 单据详情通用 mixin —— 配合 BizTabListPage 使用。
|
|
3
|
+
*
|
|
4
|
+
* 负责这几件事(宿主页面只写业务表单和取数/保存请求):
|
|
5
|
+
* 1. 取数时把流程信息挂到详情页(复用 mixins/wf 的 _getBillInfo → wfUtil.initWf)
|
|
6
|
+
* 2. 新增页就能「保存并提交」:先静默保存拿单号,立刻拉起启动流程弹框
|
|
7
|
+
* 3. 流程动作完成后按配置「刷新本页」或「关页签回列表刷新」
|
|
8
|
+
* 4. 流程审批点「通过」时自动保存单据(先校验、后保存、再提交任务)
|
|
9
|
+
* 5. 上一单 / 下一单翻单(列表侧把 navRows/navIndex 传进来才有)
|
|
10
|
+
*
|
|
11
|
+
* 宿主页面必须实现:
|
|
12
|
+
* billOptions computed/data,见下方 defaultBillOptions
|
|
13
|
+
* buildLoadRequest(dataId) -> { url, method, data } 取单据详情的请求
|
|
14
|
+
* applyBillData(objx) 把详情数据灌进表单模型;objx 为 null 表示新增
|
|
15
|
+
* saveBill({ confirm, successMsg }) -> Promise<dataId> 保存,返回单号(新增时是新单号)
|
|
16
|
+
*
|
|
17
|
+
* 宿主页面可选实现:
|
|
18
|
+
* validateBill() -> Promise<Boolean> 默认校验 $refs.editForm + $refs.extraForm
|
|
19
|
+
* validateBillData()-> String|false 数据级校验(子表必填等),返回文案即中止
|
|
20
|
+
* getBillTitle() -> String 默认取表单模型的 titleField
|
|
21
|
+
* canEditBill() -> Boolean 默认「流程没启动、或当前节点允许改」;
|
|
22
|
+
* 字段禁用与保存按钮显隐的唯一判据
|
|
23
|
+
* canSaveOnAgree()-> Boolean 默认 wfInfo.toModify === true
|
|
24
|
+
* canStartWf() -> Boolean 默认 true;按单据状态卡「提交流程」就覆盖它
|
|
25
|
+
* isBillDirty() -> Boolean 实现了它,关页签时就按它提示未保存
|
|
26
|
+
*
|
|
27
|
+
* 不在页签里独立使用(如从待办直接打开详情)也能跑:此时退回 $baseReload 的默认行为。
|
|
28
|
+
*/
|
|
29
|
+
import wfMixin from "@base/mixins/wf/index";
|
|
30
|
+
import { checkRequired, checkRows } from "./billValidators";
|
|
31
|
+
import { openStartWfDialog } from "@base/components/wf/wfUtil";
|
|
32
|
+
import {
|
|
33
|
+
getWfButtonNames,
|
|
34
|
+
loadWfButtonNames,
|
|
35
|
+
DEFAULT_SUBMIT_BTN,
|
|
36
|
+
DEFAULT_SAVE_SUBMIT_BTN,
|
|
37
|
+
} from "@base/components/wf/wfButtonName";
|
|
38
|
+
|
|
39
|
+
// wf 组件回调 _handleCallback(flag) 的 flag → 页签行为类型
|
|
40
|
+
const WF_FLAG_KIND = {
|
|
41
|
+
1: "agree", // 通过(提交任务)
|
|
42
|
+
2: "reject", // 驳回
|
|
43
|
+
3: "interrupt", // 中断 / 撤回
|
|
44
|
+
4: "transfer", // 转办
|
|
45
|
+
5: "talk", // 沟通
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const defaultBillOptions = {
|
|
49
|
+
prefix: "", // 服务前缀,如 PMD_PREFIX
|
|
50
|
+
serviceId: null, // 流程服务名;不传则从 prefix 里取("/pmd" → "pmd")
|
|
51
|
+
formCode: null, // 表单编码
|
|
52
|
+
taBm: null, // 主表表名
|
|
53
|
+
wfCode: null, // 流程编码
|
|
54
|
+
titleField: "name", // 页签标题字段
|
|
55
|
+
wfEnabled: true, // 是否启用流程
|
|
56
|
+
saveSubmitEnabled: true, // 新增页是否显示「保存并提交流程」
|
|
57
|
+
wfStartBindSave: true, // 点「提交流程」时是否先校验并自动保存
|
|
58
|
+
wfAgreeBindSave: true, // 审批点「通过」时是否自动保存单据
|
|
59
|
+
/**
|
|
60
|
+
* 流程中(节点不允许改单据时)仍然放开的字段。两种写法:
|
|
61
|
+
* ["memo", "amount"] 所有节点都放开这几个
|
|
62
|
+
* { "2": ["amount"], "*": ["memo"] } 按 wfInfo.taskStep 分节点,"*" 兜底
|
|
63
|
+
* 只配这里不会自动生效——模板上还要给那个字段套一层 <el-form> 隔断整片禁用,
|
|
64
|
+
* 因为 element 的 inputDisabled 是 `this.disabled || elForm.disabled`(OR),
|
|
65
|
+
* 整片禁用后单个字段传 :disabled="false" 是压不回来的。详见 README。
|
|
66
|
+
*/
|
|
67
|
+
wfEditableFields: null,
|
|
68
|
+
/**
|
|
69
|
+
* 启动流程时带给流程引擎的业务参数(/wf/start 的 formData,流程里做条件分支用)。
|
|
70
|
+
* 可以是对象,也可以是函数(this 指向详情组件,每次启动时求值,适合取当前表单值)。
|
|
71
|
+
*/
|
|
72
|
+
startParam: null,
|
|
73
|
+
hiddenAllButton: false, // true = 流程面板上所有操作按钮都不出(纯查看态)
|
|
74
|
+
showModifyBtn: true, // 是否放出 wfUtil 注入的「修改单据」按钮
|
|
75
|
+
hideWf: false, // true = 不挂流程信息面板(wfUtil 读详情组件的 _hideWf)
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// 必须由宿主页面实现的钩子,缺了只会在运行到某一步时莫名其妙地崩,所以开发期先喊一声。
|
|
79
|
+
// buildLoadRequest 不在这里查:取数不走 $http 的页面会整个覆盖 loadBill(),那时它就不需要。
|
|
80
|
+
const REQUIRED_HOOKS = ["applyBillData", "saveBill"];
|
|
81
|
+
|
|
82
|
+
export default {
|
|
83
|
+
mixins: [wfMixin],
|
|
84
|
+
inject: {
|
|
85
|
+
bizTabHost: { default: null },
|
|
86
|
+
},
|
|
87
|
+
props: {
|
|
88
|
+
// 页签对象,由 BizTabListPage 的 #detail 插槽透传;独立使用时不传
|
|
89
|
+
tab: {
|
|
90
|
+
type: Object,
|
|
91
|
+
default: () => null,
|
|
92
|
+
},
|
|
93
|
+
// 独立使用(待办打开、旧页面内嵌)时的单号入口
|
|
94
|
+
_dataId: [String, Number],
|
|
95
|
+
},
|
|
96
|
+
data() {
|
|
97
|
+
return {
|
|
98
|
+
dataId: null,
|
|
99
|
+
hasWf: false,
|
|
100
|
+
wfChecked: false, // 是否已查过流程状态;查完再决定要不要显示「提交流程」,避免闪一下
|
|
101
|
+
wfInfo: null,
|
|
102
|
+
wfParam: {},
|
|
103
|
+
billSaving: false,
|
|
104
|
+
billSubmitting: false, // 启动流程弹框已拉起、尚未落地:期间不许再点保存
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
computed: {
|
|
108
|
+
billConfig() {
|
|
109
|
+
return Object.assign({}, defaultBillOptions, this.billOptions || {});
|
|
110
|
+
},
|
|
111
|
+
isAdd() {
|
|
112
|
+
return this.dataId == null || this.dataId === "" || this.dataId === 0;
|
|
113
|
+
},
|
|
114
|
+
// 「保存并提交流程」:只在新增页、且流程没启动时出现
|
|
115
|
+
showSaveSubmitBtn() {
|
|
116
|
+
let cfg = this.billConfig;
|
|
117
|
+
if (!cfg.wfEnabled || !cfg.saveSubmitEnabled || !cfg.wfCode) return false;
|
|
118
|
+
return this.isAdd && !this.hasWf && this.canStartWf();
|
|
119
|
+
},
|
|
120
|
+
// 「提交流程」:已有单号、流程状态已查回、且尚未启动
|
|
121
|
+
showWfStartBtn() {
|
|
122
|
+
let cfg = this.billConfig;
|
|
123
|
+
if (!cfg.wfEnabled || !cfg.wfCode) return false;
|
|
124
|
+
return !this.isAdd && this.wfChecked && !this.hasWf && this.canStartWf();
|
|
125
|
+
},
|
|
126
|
+
/**
|
|
127
|
+
* 单据当前是否只读。绑到 el-form 的 :disabled 上,表单里的字段就整片自动禁用,
|
|
128
|
+
* 不用逐个字段绑(详情页模板那两条约束里有怎么写)。
|
|
129
|
+
* 判据只看流程;要叠加单据状态(作废/已完结不许改)就覆盖 canEditBill()。
|
|
130
|
+
*/
|
|
131
|
+
billDisabled() {
|
|
132
|
+
return !this.canEditBill();
|
|
133
|
+
},
|
|
134
|
+
// 当前节点放开的字段清单(解析 billOptions.wfEditableFields)
|
|
135
|
+
wfEditableFields() {
|
|
136
|
+
return this.getWfEditableFields();
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* 是否不挂流程信息面板。名字必须是这个:wfUtil 读的是详情组件上的
|
|
140
|
+
* `that._hideWf || that.$attrs._hideWf`,不是 wfConfig 里的字段。
|
|
141
|
+
* (另外两个同类开关 defaultShowWfContent / toWfArea 也只认 $attrs,
|
|
142
|
+
* 直接写在详情组件标签上即可:<bill-detail :tab="tab" default-show-wf-content />)
|
|
143
|
+
*/
|
|
144
|
+
_hideWf() {
|
|
145
|
+
return this.billConfig.hideWf === true;
|
|
146
|
+
},
|
|
147
|
+
submitButtonName() {
|
|
148
|
+
return getWfButtonNames().submitBtn || this.$t1(DEFAULT_SUBMIT_BTN);
|
|
149
|
+
},
|
|
150
|
+
saveSubmitButtonName() {
|
|
151
|
+
return (
|
|
152
|
+
getWfButtonNames().saveSubmitBtn || this.$t1(DEFAULT_SAVE_SUBMIT_BTN)
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
// ===== 上一单 / 下一单:快照由列表侧 openDetail(row, param, {navRows, navIndex}) 带进来 =====
|
|
156
|
+
billNavState() {
|
|
157
|
+
let tab = this.tab;
|
|
158
|
+
if (!tab || tab.isAdd) return null;
|
|
159
|
+
let rows = tab.navRows;
|
|
160
|
+
if (!Array.isArray(rows) || !rows.length) return null;
|
|
161
|
+
return { rows, index: tab.navIndex == null ? -1 : tab.navIndex };
|
|
162
|
+
},
|
|
163
|
+
canPrevBill() {
|
|
164
|
+
let state = this.billNavState;
|
|
165
|
+
return !!state && state.index > 0;
|
|
166
|
+
},
|
|
167
|
+
canNextBill() {
|
|
168
|
+
let state = this.billNavState;
|
|
169
|
+
return !!state && state.index >= 0 && state.index < state.rows.length - 1;
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
created() {
|
|
173
|
+
// 页签模式下单号以 tab.dataId 为准(新增页签是 null),独立使用时取 _dataId
|
|
174
|
+
let dataId = this.tab ? this.tab.dataId : this._dataId;
|
|
175
|
+
this.dataId = dataId == null || dataId === 0 ? null : dataId;
|
|
176
|
+
loadWfButtonNames(this);
|
|
177
|
+
this.checkBillHooks();
|
|
178
|
+
// 登记到宿主:关页签确认要问详情脏没脏、翻单要按 tabName 找回实例
|
|
179
|
+
let host = this.bizTabHost;
|
|
180
|
+
if (host && this.tab) host.registerDetail(this.tab, this);
|
|
181
|
+
},
|
|
182
|
+
methods: {
|
|
183
|
+
checkBillHooks() {
|
|
184
|
+
if (process.env.NODE_ENV === "production") return;
|
|
185
|
+
if (!this.billOptions) {
|
|
186
|
+
console.warn(
|
|
187
|
+
"[bizBillDetailMixin] 详情页缺少 billOptions(单据/流程配置),流程相关功能不会生效"
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
REQUIRED_HOOKS.forEach((name) => {
|
|
191
|
+
if (typeof this[name] !== "function") {
|
|
192
|
+
console.warn(`[bizBillDetailMixin] 详情页缺少必须实现的 ${name}()`);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
// ===== 取数 =====
|
|
198
|
+
loadBill() {
|
|
199
|
+
if (this.isAdd) {
|
|
200
|
+
this.applyBillData(null);
|
|
201
|
+
return Promise.resolve();
|
|
202
|
+
}
|
|
203
|
+
let cfg = this.billConfig;
|
|
204
|
+
if (typeof this.buildLoadRequest !== "function") {
|
|
205
|
+
if (process.env.NODE_ENV !== "production") {
|
|
206
|
+
console.warn(
|
|
207
|
+
"[bizBillDetailMixin] 详情页既没实现 buildLoadRequest()、也没覆盖 loadBill(),取不到数据"
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return Promise.resolve();
|
|
211
|
+
}
|
|
212
|
+
let request = this.buildLoadRequest(this.dataId) || {};
|
|
213
|
+
return this._getBillInfo({
|
|
214
|
+
method: "post",
|
|
215
|
+
isLoading: true,
|
|
216
|
+
modalStrictly: true,
|
|
217
|
+
...request,
|
|
218
|
+
// 流程未启用、或本身就是新增时不挂流程
|
|
219
|
+
wfConfig: cfg.wfEnabled && cfg.wfCode ? () => this.buildWfOption() : null,
|
|
220
|
+
success: (res) => {
|
|
221
|
+
this.applyBillData((res && res.objx) || null);
|
|
222
|
+
// 取数回来再同步一次页签标题:保存接口通常只回主键,不回单号,
|
|
223
|
+
// promoteTab 那一刻标题字段还是空的,页签会一直停在「新增」。
|
|
224
|
+
this.syncTabTitle();
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
// 把当前模型的标题字段同步到页签(空值保持原标题,由宿主判定)
|
|
229
|
+
syncTabTitle() {
|
|
230
|
+
let host = this.bizTabHost;
|
|
231
|
+
if (!host || !this.tab) return;
|
|
232
|
+
host.syncTabTitle(this.tab, this.getBillTitle());
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
// ===== 流程选项:initWf 挂载、提交流程按钮、保存并提交,三个入口共用 =====
|
|
236
|
+
buildWfOption(overrides) {
|
|
237
|
+
let cfg = this.billConfig;
|
|
238
|
+
let option = {
|
|
239
|
+
objId: this.dataId,
|
|
240
|
+
wfCode: cfg.wfCode,
|
|
241
|
+
formCode: cfg.formCode,
|
|
242
|
+
// 显式传:mixins/wf 的 _getBillInfo 只在没传时才按取数 url 首段推导服务名
|
|
243
|
+
serviceId: this.getServiceId(),
|
|
244
|
+
showStartBtn: true,
|
|
245
|
+
// 启动按钮由 BizBillActions 声明式渲染,不让 wfUtil 往 DOM 里塞
|
|
246
|
+
injectStartBtn: false,
|
|
247
|
+
showWfFold: true,
|
|
248
|
+
// 启动流程带给流程引擎的业务参数(/wf/start 的 formData)
|
|
249
|
+
startParam: this.getWfStartParam(),
|
|
250
|
+
// 纯查看态藏掉流程面板全部按钮 / 控制「修改单据」按钮
|
|
251
|
+
hiddenAllButton: cfg.hiddenAllButton === true,
|
|
252
|
+
showModifyBtn: cfg.showModifyBtn !== false,
|
|
253
|
+
// 自己控制 done 的时机:等详情数据渲染完再挂流程面板,避免取不到 .d-cont
|
|
254
|
+
auto: false,
|
|
255
|
+
// 启动确认文案跟随后台 param18 的按钮名,和「保存并提交」那条链路口径一致
|
|
256
|
+
startConfirmText: () =>
|
|
257
|
+
this.$t1(`您确定要${this.submitButtonName}吗?`),
|
|
258
|
+
callback: (wfParam) => {
|
|
259
|
+
this.wfParam = wfParam || {};
|
|
260
|
+
this.hasWf = !!this.wfParam.hasWf;
|
|
261
|
+
this.wfInfo = this.wfParam.wfInfo || null;
|
|
262
|
+
this.wfChecked = true;
|
|
263
|
+
this.$nextTick(() => {
|
|
264
|
+
this.wfParam.done && this.wfParam.done();
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
// 流程启动成功 → 按配置刷新本页 / 关页签回列表
|
|
268
|
+
onStartSuccess: () => this.onWfActionDone("submit"),
|
|
269
|
+
// 失败/取消都要解掉提交态;是否顺带刷详情看本次有没有真的存过(见 onWfStartAbort)
|
|
270
|
+
onStartFail: () => this.onWfStartAbort(),
|
|
271
|
+
onCancel: () => this.onWfStartAbort(),
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
if (cfg.wfStartBindSave) {
|
|
275
|
+
// 启动流程时自动保存:先校验(弹框前),再保存(确认后、提交前)。
|
|
276
|
+
option.onStart = (done) => {
|
|
277
|
+
this.validateBill().then((valid) => {
|
|
278
|
+
if (valid) done();
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
option.onBeforeStartSubmit = (done) => {
|
|
282
|
+
this.runSave({ confirm: false, successMsg: false }).then((dataId) => {
|
|
283
|
+
if (dataId == null || dataId === "") return;
|
|
284
|
+
this.promoteAfterSave(dataId);
|
|
285
|
+
// 到这里单据已入库、只等提交:此后必然走 onStartSuccess/onStartFail/onCancel
|
|
286
|
+
// 三者之一,置提交态是安全的(不像启动确认框那样有取消不回调的空档)。
|
|
287
|
+
this.billSubmitting = true;
|
|
288
|
+
this._wfStartSaved = true; // 本次启动伴随了保存 → 失败/取消时要刷到编辑态
|
|
289
|
+
done();
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (cfg.wfAgreeBindSave) {
|
|
295
|
+
// 审批「通过」自动保存单据:
|
|
296
|
+
// onClickAgree —— 点通过、意见框弹出「之前」校验,避免填完意见才报必填
|
|
297
|
+
// onBeforeAgree —— 点确定、调 /wf/submit 「之前」静默保存
|
|
298
|
+
option.onClickAgree = (done) => {
|
|
299
|
+
if (!this.canSaveOnAgree()) return done();
|
|
300
|
+
this.validateBill().then((valid) => {
|
|
301
|
+
if (valid) done();
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
option.onBeforeAgree = (done) => {
|
|
305
|
+
if (!this.canSaveOnAgree()) return done();
|
|
306
|
+
this.saveBill({ confirm: false, successMsg: false }).then((dataId) => {
|
|
307
|
+
if (dataId == null || dataId === "") return;
|
|
308
|
+
done();
|
|
309
|
+
});
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return Object.assign(option, overrides || {});
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// ===== 按钮行为 =====
|
|
317
|
+
// 普通保存:新增保存后就地升为编辑态并刷新一次(出流程信息 / 切只读态)
|
|
318
|
+
async saveHandle() {
|
|
319
|
+
if (!(await this.guardSaving())) return false;
|
|
320
|
+
let valid = await this.validateBill();
|
|
321
|
+
if (!valid) return false;
|
|
322
|
+
let dataId = await this.runSave({ confirm: true, successMsg: true });
|
|
323
|
+
if (dataId == null || dataId === "") return false;
|
|
324
|
+
this.promoteAfterSave(dataId);
|
|
325
|
+
this.reloadDetail();
|
|
326
|
+
return true;
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 保存并提交流程(新增页上的一步到位入口)。
|
|
331
|
+
*
|
|
332
|
+
* 顺序:校验 → 确认 → 静默保存拿单号 → 只升级页签元数据(不刷详情)→ 拉起启动流程弹框。
|
|
333
|
+
* 保存完立刻升级成编辑态是关键:万一随后提交失败或关掉候选人弹框取消了,
|
|
334
|
+
* 再点一次也是「更新」而不是「再新增一张」;详情那一次刷新留给流程结束分支,不刷两遍。
|
|
335
|
+
*/
|
|
336
|
+
async saveAndSubmitHandle() {
|
|
337
|
+
let cfg = this.billConfig;
|
|
338
|
+
if (!cfg.wfEnabled || !cfg.wfCode) return false;
|
|
339
|
+
if (!(await this.guardSaving())) return false;
|
|
340
|
+
let valid = await this.validateBill();
|
|
341
|
+
if (!valid) return false;
|
|
342
|
+
let confirmText = this.$t1(`您确定要${this.saveSubmitButtonName}吗?`);
|
|
343
|
+
try {
|
|
344
|
+
await this.$baseConfirm(confirmText);
|
|
345
|
+
} catch (e) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
let dataId = await this.runSave({ confirm: false, successMsg: false });
|
|
349
|
+
if (dataId == null || dataId === "") return false;
|
|
350
|
+
this.promoteAfterSave(dataId);
|
|
351
|
+
// 弹框拉起后到流程落地之间,单据已入库但还没提交:这段时间禁掉保存,避免并发写
|
|
352
|
+
this.billSubmitting = true;
|
|
353
|
+
this._wfStartSaved = true; // 已经存过了 → 失败/取消都要把详情刷到编辑态
|
|
354
|
+
openStartWfDialog(
|
|
355
|
+
this,
|
|
356
|
+
this.buildWfOption({
|
|
357
|
+
objId: dataId,
|
|
358
|
+
skipStartConfirm: true, // 已经确认过一次了,不再弹第二次
|
|
359
|
+
onStart: null, // 校验/保存都做完了,不再走 wfStartBindSave 那套
|
|
360
|
+
onBeforeStartSubmit: null,
|
|
361
|
+
})
|
|
362
|
+
);
|
|
363
|
+
return true;
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
// 「提交流程」按钮(已有单号、流程未启动)
|
|
367
|
+
startWfHandle() {
|
|
368
|
+
let cfg = this.billConfig;
|
|
369
|
+
if (!cfg.wfEnabled || !cfg.wfCode || this.isAdd) return;
|
|
370
|
+
// 这里不置 billSubmitting:单模板 + 启动确认框那条路,用户点「取消」时
|
|
371
|
+
// wfStartDialog 既不 close 也不回调 onCancel(close 只挂在 el-dialog 的 @close 上,
|
|
372
|
+
// 而那种情况 showDialog 从没置过 true),置了就再没人来解,保存按钮会永久禁用。
|
|
373
|
+
// 真正需要防并发的窗口是「已入库、待提交」,由 onBeforeStartSubmit 内部置。
|
|
374
|
+
openStartWfDialog(this, this.buildWfOption());
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
// 上一单 / 下一单
|
|
378
|
+
gotoPrevBill() {
|
|
379
|
+
this.navigateBill(-1);
|
|
380
|
+
},
|
|
381
|
+
gotoNextBill() {
|
|
382
|
+
this.navigateBill(1);
|
|
383
|
+
},
|
|
384
|
+
// 返回 Promise<Boolean>:宿主会先确认有没有未保存的修改,再决定翻不翻
|
|
385
|
+
navigateBill(step) {
|
|
386
|
+
let host = this.bizTabHost;
|
|
387
|
+
if (!host || !this.tab) return Promise.resolve(false);
|
|
388
|
+
return host.navigateTab(this.tab, step);
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
// ===== 流程结束后的页签行为 =====
|
|
392
|
+
/**
|
|
393
|
+
* 某个流程动作成功后走这里。behavior 由列表宿主配置:
|
|
394
|
+
* refreshCurrent 刷新当前页签(默认)
|
|
395
|
+
* closeToList 关页签 → 回列表 → 刷新列表
|
|
396
|
+
*/
|
|
397
|
+
onWfActionDone(kind, alreadyReloaded) {
|
|
398
|
+
this.billSubmitting = false;
|
|
399
|
+
this._wfStartSaved = false;
|
|
400
|
+
let host = this.bizTabHost;
|
|
401
|
+
if (!host || !this.tab) {
|
|
402
|
+
// 不在页签里(待办直开等):保持原有行为。
|
|
403
|
+
// alreadyReloaded 表示 wf 的 reloadContent() 已经刷过一次了,别再刷第二次。
|
|
404
|
+
if (!alreadyReloaded) this.reloadDetail();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (host.getBehavior(kind) === "closeToList") {
|
|
408
|
+
this.cancelPendingReload();
|
|
409
|
+
host.closeTabToList(this.tab);
|
|
410
|
+
} else if (!this._reloadTimer) {
|
|
411
|
+
host.refreshTab(this.tab);
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
/**
|
|
415
|
+
* 启动流程失败 / 取消(关掉启动弹框或候选人弹框、后台没配流程单据直接被 close 掉…)。
|
|
416
|
+
*
|
|
417
|
+
* 只有「本次启动确实伴随过保存」才刷详情——保存过不刷,页面会停在新增态、看不到单号;
|
|
418
|
+
* 没保存过就刷是纯粹的打扰:比如后台没配流程单据时 wf 会弹「无法启动,没有流程单据」
|
|
419
|
+
* 然后 close(),这时什么都没发生,用户看到的却是详情整个重载(还会丢掉未保存的编辑)。
|
|
420
|
+
* 判据不能用 wfStartBindSave 这个配置项:它只说明「会不会存」,不说明「这次存没存」。
|
|
421
|
+
*/
|
|
422
|
+
onWfStartAbort() {
|
|
423
|
+
this.billSubmitting = false;
|
|
424
|
+
if (!this._wfStartSaved) return;
|
|
425
|
+
this._wfStartSaved = false;
|
|
426
|
+
this.reloadDetail();
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* wf 组件在「通过/驳回/中断/转办/沟通」成功后调用(wf.js closeWinOnCheck)。
|
|
431
|
+
*
|
|
432
|
+
* 注意时序:wf 那边是 reloadContent() 先跑、closeWinOnCheck() 后跑,也就是
|
|
433
|
+
* 先触发一次刷新,才轮到这里告诉我们本次是什么动作。所以下面的 $baseReload
|
|
434
|
+
* 把刷新延后一拍,这里若判定要关页签,就把那次刷新撤掉,不做无用功。
|
|
435
|
+
*/
|
|
436
|
+
_handleCallback(flag) {
|
|
437
|
+
let kind = WF_FLAG_KIND[flag];
|
|
438
|
+
if (!kind) return;
|
|
439
|
+
this.onWfActionDone(kind, true);
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* 覆盖全局的 $baseReload(Vue.prototype 上那个 $emit("reload"))。
|
|
444
|
+
* wf 组件内部所有刷新都硬编码走 $baseReload,覆盖它是把流程刷新接进页签体系
|
|
445
|
+
* 又不改 wf.js 的唯一切口。不在页签里时原样退回默认实现。
|
|
446
|
+
*/
|
|
447
|
+
$baseReload(param) {
|
|
448
|
+
let host = this.bizTabHost;
|
|
449
|
+
if (!host || !this.tab) {
|
|
450
|
+
this.$emit("reload", this, param);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
this.cancelPendingReload();
|
|
454
|
+
this._reloadTimer = setTimeout(() => {
|
|
455
|
+
this._reloadTimer = null;
|
|
456
|
+
host.refreshTab(this.tab);
|
|
457
|
+
}, 0);
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
// ===== 内部工具 =====
|
|
461
|
+
reloadDetail() {
|
|
462
|
+
let host = this.bizTabHost;
|
|
463
|
+
if (host && this.tab) {
|
|
464
|
+
this.cancelPendingReload();
|
|
465
|
+
host.refreshTab(this.tab);
|
|
466
|
+
} else {
|
|
467
|
+
this.$emit("reload", this, { updateParam: { _dataId: this.dataId } });
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
cancelPendingReload() {
|
|
471
|
+
if (this._reloadTimer) {
|
|
472
|
+
clearTimeout(this._reloadTimer);
|
|
473
|
+
this._reloadTimer = null;
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
// 保存成功后:本地转编辑态 + 页签元数据升级(转编辑态、换标题),不重渲染
|
|
477
|
+
promoteAfterSave(dataId) {
|
|
478
|
+
this.dataId = dataId;
|
|
479
|
+
let host = this.bizTabHost;
|
|
480
|
+
if (host && this.tab) {
|
|
481
|
+
host.promoteTab(this.tab, { dataId, title: this.getBillTitle() });
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
async runSave(options) {
|
|
485
|
+
this.billSaving = true;
|
|
486
|
+
try {
|
|
487
|
+
return await this.saveBill(options);
|
|
488
|
+
} finally {
|
|
489
|
+
this.billSaving = false;
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
async guardSaving() {
|
|
493
|
+
if (this.billSaving || this.billSubmitting) {
|
|
494
|
+
this.$message.warning(this.$t1("数据正在保存,请勿重复操作"));
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
return true;
|
|
498
|
+
},
|
|
499
|
+
getServiceId() {
|
|
500
|
+
let cfg = this.billConfig;
|
|
501
|
+
if (cfg.serviceId) return cfg.serviceId;
|
|
502
|
+
return (cfg.prefix || "").replace(/^\//, "") || null;
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
// ===== 可被宿主页面覆盖的默认实现 =====
|
|
506
|
+
/**
|
|
507
|
+
* 校验单据。默认校验 $refs.editForm,外加 $refs.extraForm(可以是 v-for 出来的数组)。
|
|
508
|
+
*
|
|
509
|
+
* extraForm 是给「流程中放开的字段」用的:那些字段套在内层 <el-form> 里,
|
|
510
|
+
* el-form-item 只会向最近的 form 注册(form-item.vue 的 dispatch('ElForm')),
|
|
511
|
+
* 所以外层 editForm 压根不认识它们,不给内层也校一遍就等于没有必填。
|
|
512
|
+
*
|
|
513
|
+
* 两个必须守的点:
|
|
514
|
+
* - 只校验绑了 :model 的 form。element 的 validate() 开头是
|
|
515
|
+
* `if (!this.model) { warn; return; }`——没绑 model 就直接返回、callback 永不触发,
|
|
516
|
+
* 塞进 Promise 会永久挂起,把保存链路锁死(隔断 disabled 用的空 el-form 就是这种)。
|
|
517
|
+
* - 各 form 都用 errorTip:false 关掉各自的提示,统一在最后只提示一次,免得弹好几条。
|
|
518
|
+
*/
|
|
519
|
+
validateBill() {
|
|
520
|
+
let forms = [this.$refs.editForm].concat(this.getExtraForms());
|
|
521
|
+
let tasks = forms
|
|
522
|
+
.filter((form) => form && form.$baseValidate && form.model)
|
|
523
|
+
.map(
|
|
524
|
+
(form) =>
|
|
525
|
+
new Promise((resolve) => {
|
|
526
|
+
form.$baseValidate((valid) => resolve(!!valid), {
|
|
527
|
+
errorTip: false,
|
|
528
|
+
});
|
|
529
|
+
})
|
|
530
|
+
);
|
|
531
|
+
return Promise.all(tasks).then((results) => {
|
|
532
|
+
if (!results.every(Boolean)) {
|
|
533
|
+
this.$message({
|
|
534
|
+
message: this.$t2("必填项不能为空", "system.message.required"),
|
|
535
|
+
type: "error",
|
|
536
|
+
duration: 2000,
|
|
537
|
+
});
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
// 表单校验过了,再跑数据级校验(子表必填、合计、业务规则)
|
|
541
|
+
return Promise.resolve(this.validateBillData()).then((ret) => {
|
|
542
|
+
if (ret === false) return false;
|
|
543
|
+
if (typeof ret === "string" && ret) {
|
|
544
|
+
this.$message.warning(ret);
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
return true;
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
},
|
|
551
|
+
/**
|
|
552
|
+
* 数据级校验钩子,表单校验通过后执行。返回错误文案(或 false)即中止保存/提交。
|
|
553
|
+
* 可以返回 Promise。
|
|
554
|
+
*
|
|
555
|
+
* 子表必填**必须**在这里兜一道,不能只靠行内 <el-form-item> ——
|
|
556
|
+
* $vxeTableUtil 给表格配的是 scrollY: { gt: 20 },子表超过 20 行就开虚拟滚动,
|
|
557
|
+
* 屏幕外的行压根没渲染、行内 form-item 不存在,外层 form 的 fields 里也就没有它们,
|
|
558
|
+
* 结果是「看着校验过了,其实漏了半张子表」。分页子表的其它页同理。
|
|
559
|
+
* 行内 form-item 留着做即时红框提示,可靠性靠这里。
|
|
560
|
+
*/
|
|
561
|
+
validateBillData() {
|
|
562
|
+
return null;
|
|
563
|
+
},
|
|
564
|
+
/**
|
|
565
|
+
* 校验主表(表头)字段。附件、多选这类集合型字段的必填**必须**走这里:
|
|
566
|
+
* async-validator 的 required 不指定 type 时空数组是放行的,el-form-item 上写了也校不出来。
|
|
567
|
+
*
|
|
568
|
+
* validateBillData() {
|
|
569
|
+
* return (
|
|
570
|
+
* this.checkBillRequired([
|
|
571
|
+
* { field: "attachmentDTOs", label: "附件", type: "attachment" },
|
|
572
|
+
* { field: "areaIds", label: "适用区域", type: "multi" },
|
|
573
|
+
* ])
|
|
574
|
+
* || this.checkRowsRequired({ … })
|
|
575
|
+
* );
|
|
576
|
+
* }
|
|
577
|
+
*
|
|
578
|
+
* @param {Array} fields [{field, label, type: "text"|"attachment"|"multi", check}]
|
|
579
|
+
* @param {Object} target 默认取 getBillModel()
|
|
580
|
+
* @returns {String|null} 错误文案,全通过返回 null
|
|
581
|
+
*/
|
|
582
|
+
checkBillRequired(fields, target) {
|
|
583
|
+
return checkRequired(target || this.getBillModel(), fields, {
|
|
584
|
+
t: (str) => this.$t1(str),
|
|
585
|
+
});
|
|
586
|
+
},
|
|
587
|
+
/**
|
|
588
|
+
* 校验子表:先卡行数(默认至少 1 行),再逐行按字段清单校验。
|
|
589
|
+
* rows 传全量数据(grid.getTableData().fullData),别传当前渲染的行——
|
|
590
|
+
* 表格 scrollY gt:20 会开虚拟滚动,屏幕外的行拿不到。
|
|
591
|
+
*
|
|
592
|
+
* @param {Object} config { rows, label, minRows = 1, fields }
|
|
593
|
+
* @returns {String|null}
|
|
594
|
+
*/
|
|
595
|
+
checkRowsRequired(config) {
|
|
596
|
+
return checkRows({ ...(config || {}), t: (str) => this.$t1(str) });
|
|
597
|
+
},
|
|
598
|
+
/**
|
|
599
|
+
* 除 editForm 之外还要一起校验的 form:**所有 ref 以 "extraForm" 开头的**内层 el-form。
|
|
600
|
+
*
|
|
601
|
+
* 收前缀而不是只认 ref="extraForm",是因为一个详情页往往有好几处内层 form
|
|
602
|
+
* (放开的主表字段一处、整表放开的子表又一处),而 Vue 2 里同名的非 v-for ref
|
|
603
|
+
* 会互相覆盖、只留最后挂载的那个——那就会有 form 被漏校。
|
|
604
|
+
* 所以按需要起名 extraForm / extraFormProduct / extraFormItems…都会被收进来,
|
|
605
|
+
* v-for 出来的数组也支持。
|
|
606
|
+
*/
|
|
607
|
+
getExtraForms() {
|
|
608
|
+
let refs = this.$refs || {};
|
|
609
|
+
let forms = [];
|
|
610
|
+
Object.keys(refs).forEach((key) => {
|
|
611
|
+
if (key.indexOf("extraForm") !== 0) return;
|
|
612
|
+
let val = refs[key];
|
|
613
|
+
(Array.isArray(val) ? val : [val]).forEach((form) => {
|
|
614
|
+
if (form) forms.push(form);
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
return forms;
|
|
618
|
+
},
|
|
619
|
+
// 启动流程带给流程引擎的业务参数;配对象或函数(函数每次启动时求值)
|
|
620
|
+
getWfStartParam() {
|
|
621
|
+
let param = this.billConfig.startParam;
|
|
622
|
+
if (typeof param === "function") return param.call(this) || null;
|
|
623
|
+
return param || null;
|
|
624
|
+
},
|
|
625
|
+
getBillTitle() {
|
|
626
|
+
let field = this.billConfig.titleField;
|
|
627
|
+
let model = this.getBillModel();
|
|
628
|
+
return model && field ? model[field] : null;
|
|
629
|
+
},
|
|
630
|
+
// 默认取宿主页面的 billModel(data 里的表单对象);不叫这个名就自己覆盖 getBillTitle
|
|
631
|
+
getBillModel() {
|
|
632
|
+
return this.billModel || null;
|
|
633
|
+
},
|
|
634
|
+
/**
|
|
635
|
+
* 单据当前能不能改。字段禁用(billDisabled)和「重置/保存」的显隐(BizBillActions.canEdit)
|
|
636
|
+
* 都以它为唯一判据,不会出现「字段能改却没有保存按钮」这种错配。
|
|
637
|
+
* 默认:流程没启动就能改,启动了看当前节点给不给改。
|
|
638
|
+
* 要叠加单据状态就覆盖它(状态字段名各单据不统一,故不在 billOptions 里约定):
|
|
639
|
+
* canEditBill() {
|
|
640
|
+
* if (this.hasWf) return !!(this.wfInfo && this.wfInfo.toModify);
|
|
641
|
+
* return this.isAdd || this.billModel.status === 0;
|
|
642
|
+
* }
|
|
643
|
+
*/
|
|
644
|
+
canEditBill() {
|
|
645
|
+
if (!this.hasWf) return true;
|
|
646
|
+
return !!(this.wfInfo && this.wfInfo.toModify);
|
|
647
|
+
},
|
|
648
|
+
/**
|
|
649
|
+
* 单个字段现在能不能改。整单可改时恒为 false(不禁);整单只读时看白名单。
|
|
650
|
+
* 配合模板上的嵌套 <el-form> 使用:
|
|
651
|
+
* <el-form> 隔断整片禁用
|
|
652
|
+
* <el-form-item prop="memo">
|
|
653
|
+
* <el-input v-model="billModel.memo" :disabled="fieldDisabled('memo')" />
|
|
654
|
+
* </el-form-item>
|
|
655
|
+
* </el-form>
|
|
656
|
+
*/
|
|
657
|
+
fieldDisabled(field) {
|
|
658
|
+
if (this.canEditBill()) return false;
|
|
659
|
+
return this.wfEditableFields.indexOf(field) < 0;
|
|
660
|
+
},
|
|
661
|
+
// 解析 billOptions.wfEditableFields;要按角色/自定义规则放开就覆盖它
|
|
662
|
+
getWfEditableFields() {
|
|
663
|
+
let cfg = this.billConfig.wfEditableFields;
|
|
664
|
+
if (!cfg) return [];
|
|
665
|
+
if (Array.isArray(cfg)) return cfg;
|
|
666
|
+
let step
|
|
667
|
+
= this.wfInfo && this.wfInfo.taskStep != null
|
|
668
|
+
? String(this.wfInfo.taskStep)
|
|
669
|
+
: "";
|
|
670
|
+
return cfg[step] || cfg["*"] || [];
|
|
671
|
+
},
|
|
672
|
+
// 审批「通过」时能不能存:默认看流程给的可修改标记
|
|
673
|
+
canSaveOnAgree() {
|
|
674
|
+
return !!(this.wfInfo && this.wfInfo.toModify);
|
|
675
|
+
},
|
|
676
|
+
// 能不能启动流程:默认放开。按单据状态卡(如作废/已完结不许提交)就覆盖它,
|
|
677
|
+
// 各单据状态字段名不统一,故不在 billOptions 里约定字段。
|
|
678
|
+
canStartWf() {
|
|
679
|
+
return true;
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
beforeDestroy() {
|
|
683
|
+
this.cancelPendingReload();
|
|
684
|
+
let host = this.bizTabHost;
|
|
685
|
+
if (host && this.tab) host.unregisterDetail(this.tab, this);
|
|
686
|
+
},
|
|
687
|
+
};
|