vue2-client 1.22.2 → 1.22.3

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 (170) hide show
  1. package/.claude/settings.local.json +30 -30
  2. package/.env.his +19 -19
  3. package/.eslintrc.js +74 -74
  4. package/.history/.eslintrc_20260521171150.js +74 -0
  5. package/.history/.eslintrc_20260521171213.js +74 -0
  6. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154443.vue +726 -0
  7. package/.history/src/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm_20260601154700.vue +478 -0
  8. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175435.vue +706 -0
  9. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260512175450.vue +694 -0
  10. package/.history/src/base-client/components/common/HIS/HButtons/HButtons_20260611152602.vue +755 -0
  11. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513145941.vue +524 -0
  12. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513153133.vue +731 -0
  13. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260513160316.vue +525 -0
  14. package/.history/src/base-client/components/common/HIS/HForm/HForm_20260601144150.vue +1046 -0
  15. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310142713.vue +512 -0
  16. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260310145118.vue +511 -0
  17. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260311094834.vue +696 -0
  18. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260320143028.vue +693 -0
  19. package/.history/src/base-client/components/common/HIS/HFormTable/HFormTable_20260409101450.vue +677 -0
  20. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508164645.vue +758 -0
  21. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508164714.vue +693 -0
  22. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260508171651.vue +716 -0
  23. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260509133717.vue +695 -0
  24. package/.history/src/base-client/components/common/HIS/HTab/HTab_20260509171115.vue +664 -0
  25. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140637.vue +1455 -0
  26. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513140935.vue +1441 -0
  27. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513150818.vue +1441 -0
  28. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153119.vue +1442 -0
  29. package/.history/src/base-client/components/common/XAddNativeForm/XAddNativeForm_20260513153126.vue +1486 -0
  30. package/.history/src/base-client/components/common/XForm/XFormItem_20260513140854.vue +1607 -0
  31. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140403.vue +643 -0
  32. package/.history/src/base-client/components/common/XMarkdownViewer/XMarkdownViewer_20260519140829.vue +628 -0
  33. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519142824.vue +104 -0
  34. package/.history/src/base-client/components/common/XMarkdownViewer/demo_20260519143155.vue +102 -0
  35. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171231.vue +1241 -0
  36. package/.history/src/base-client/components/common/XReportGrid/XReport_20260309171441.vue +1223 -0
  37. package/.history/src/base-client/components/his/HAi/HAi_20260612174826.vue +472 -0
  38. package/.history/src/base-client/components/his/HAi/HAi_20260612175839.vue +538 -0
  39. package/.history/src/base-client/components/his/HAi/HAi_20260615103331.vue +650 -0
  40. package/.history/src/base-client/components/his/XHDescriptions/XHDescriptions_20260424134504.vue +1469 -0
  41. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171133.vue +788 -0
  42. package/.history/src/base-client/components/his/XSidebar/XSidebar_20260610171151.vue +780 -0
  43. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511170841.vue +585 -0
  44. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260511171138.vue +787 -0
  45. package/.history/src/base-client/components/his/XTransfer/XTransfer_20260512141830.vue +739 -0
  46. package/.history/src/components/STable/index_20260409155138.js +806 -0
  47. package/.history/src/components/STable/index_20260409155218.js +814 -0
  48. package/.history/src/expression/core/Expression_20260305164427.js +1371 -0
  49. package/.history/src/expression/core/Expression_20260305170258.js +1358 -0
  50. package/.history/src/expression/core/Program_20260305111830.js +944 -0
  51. package/.history/src/expression/core/Program_20260305112041.js +931 -0
  52. package/.history/src/logic/LogicRunner_20260304154306.js +170 -0
  53. package/.history/src/logic/LogicRunner_20260304155553.js +112 -0
  54. package/.history/src/logic/LogicRunner_20260305105834.js +112 -0
  55. package/.history/src/logic/LogicRunner_20260305112718.js +129 -0
  56. package/.history/src/logic/LogicRunner_20260305182436.js +133 -0
  57. package/.history/src/logic/LogicRunner_20260306151301.js +213 -0
  58. package/.history/src/logic/LogicRunner_20260306152419.js +213 -0
  59. package/.history/src/logic/plugins/common/DateTools_20260305154159.js +61 -0
  60. package/.history/src/logic/plugins/common/DateTools_20260305154217.js +44 -0
  61. package/.history/src/logic/plugins/common/DateTools_20260305161014.js +44 -0
  62. package/.history/src/logic/plugins/common/HttpTools_20260305164352.js +80 -0
  63. package/.history/src/logic/plugins/common/HttpTools_20260305170258.js +75 -0
  64. package/.history/src/logic/plugins/common/HttpTools_20260305171634.js +75 -0
  65. package/.history/src/logic/plugins/common/HttpTools_20260306152419.js +72 -0
  66. package/.history/src/services/api/restTools_20260427142149.js +245 -0
  67. package/.history/src/services/api/restTools_20260427142853.js +230 -0
  68. package/.history/src/services/api/restTools_20260519135558.js +230 -0
  69. package/.history/src/services/api/restTools_20260519140825.js +230 -0
  70. package/.history/src/services/api/restTools_20260519151223.js +230 -0
  71. package/.history/src/utils/indexedDB_20260306150918.js +593 -0
  72. package/.history/src/utils/indexedDB_20260306151301.js +586 -0
  73. package/.idea/af-vue2-client.iml +9 -0
  74. package/.idea/codeStyles/Project.xml +62 -0
  75. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  76. package/.idea/misc.xml +6 -0
  77. package/.idea/modules.xml +1 -1
  78. package/Components.md +60 -60
  79. package/index.js +31 -31
  80. package/jest-transform-stub.js +8 -8
  81. package/jest.setup.js +7 -7
  82. package/package.json +1 -1
  83. package/preview-input-box.html +180 -0
  84. package/src/assets/img/querySlotDemo.svg +15 -15
  85. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  86. package/src/base-client/components/common/CitySelect/index.js +3 -3
  87. package/src/base-client/components/common/CitySelect/index.md +109 -109
  88. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  89. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  90. package/src/base-client/components/common/HIS/HButtons/HButtons.vue +55 -1
  91. package/src/base-client/components/common/HIS/HForm/HForm.vue +1186 -1186
  92. package/src/base-client/components/common/HIS/HTab/HTab.vue +88 -1
  93. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  94. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  95. package/src/base-client/components/common/Tree/index.js +2 -2
  96. package/src/base-client/components/common/Upload/index.js +3 -3
  97. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  98. package/src/base-client/components/common/XAddReport/XAddReport.vue +16 -1
  99. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  100. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  101. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  102. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  103. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  104. package/src/base-client/components/common/XDescriptions/index.md +382 -382
  105. package/src/base-client/components/common/XForm/index.md +178 -178
  106. package/src/base-client/components/common/XInput/XInput.vue +32 -1
  107. package/src/base-client/components/common/XInspectionDetailDrawer/index.vue +1 -1
  108. package/src/base-client/components/common/XMarkdownViewer/demo.vue +102 -102
  109. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  110. package/src/base-client/components/common/XStepView/index.js +3 -3
  111. package/src/base-client/components/common/XStepView/index.md +31 -31
  112. package/src/base-client/components/common/XTable/index.md +255 -255
  113. package/src/base-client/components/his/HAi/HAi.vue +1177 -436
  114. package/src/base-client/components/his/XList/XList.vue +337 -58
  115. package/src/base-client/components/his/XSidebar/XSidebar.vue +36 -12
  116. package/src/base-client/components/his/XTransfer/index.md +327 -327
  117. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  118. package/src/base-client/plugins/Config.js +19 -19
  119. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  120. package/src/components/Charts/Bar.vue +62 -62
  121. package/src/components/Charts/ChartCard.vue +134 -134
  122. package/src/components/Charts/Liquid.vue +67 -67
  123. package/src/components/Charts/MiniArea.vue +39 -39
  124. package/src/components/Charts/MiniBar.vue +39 -39
  125. package/src/components/Charts/MiniProgress.vue +75 -75
  126. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  127. package/src/components/Charts/Radar.vue +68 -68
  128. package/src/components/Charts/RankList.vue +77 -77
  129. package/src/components/Charts/TagCloud.vue +113 -113
  130. package/src/components/Charts/TransferBar.vue +64 -64
  131. package/src/components/Charts/Trend.vue +82 -82
  132. package/src/components/Charts/chart.less +12 -12
  133. package/src/components/Charts/smooth.area.less +13 -13
  134. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  135. package/src/components/NumberInfo/index.js +3 -3
  136. package/src/components/NumberInfo/index.less +54 -54
  137. package/src/components/NumberInfo/index.md +43 -43
  138. package/src/components/STable/index.js +953 -953
  139. package/src/components/card/ChartCard.vue +79 -79
  140. package/src/components/chart/Bar.vue +60 -60
  141. package/src/components/chart/MiniArea.vue +67 -67
  142. package/src/components/chart/MiniBar.vue +59 -59
  143. package/src/components/chart/MiniProgress.vue +57 -57
  144. package/src/components/chart/Radar.vue +80 -80
  145. package/src/components/chart/RankingList.vue +60 -60
  146. package/src/components/chart/Trend.vue +79 -79
  147. package/src/components/chart/index.less +9 -9
  148. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  149. package/src/components/input/IInput.vue +66 -66
  150. package/src/components/menu/SideMenu.vue +75 -75
  151. package/src/components/menu/menu.js +273 -273
  152. package/src/components/tool/AStepItem.vue +60 -60
  153. package/src/layouts/CommonLayout.vue +56 -56
  154. package/src/lib.js +1 -1
  155. package/src/mock/extend/index.js +84 -84
  156. package/src/mock/goods/index.js +108 -108
  157. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  158. package/src/pages/system/dictionary/index.vue +44 -44
  159. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  160. package/src/pages/system/monitor/operLog/index.vue +37 -37
  161. package/src/services/api/cas.js +79 -79
  162. package/src/store/modules/setting.js +119 -119
  163. package/src/utils/errorCode.js +6 -6
  164. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
  165. package/.idea/MarsCodeWorkspaceAppSettings.xml +0 -7
  166. package/.idea/google-java-format.xml +0 -6
  167. package/.idea/inspectionProfiles/Project_Default.xml +0 -24
  168. package/.idea/jsLinters/eslint.xml +0 -6
  169. package/.idea/vue2-client.iml +0 -12
  170. package/.vscode/settings.json +0 -28
@@ -0,0 +1,1442 @@
1
+ <template>
2
+ <div id="XAddNativeForm">
3
+ <!-- 骨架屏 -->
4
+ <a-skeleton v-if="!loaded" active :paragraph="{ rows: 3 }" />
5
+ <a-form-model v-else ref="selectForm" :zIndex="1001" :model="form" v-bind="formItemLayoutGen" :layout="layout">
6
+ <template v-for="(item, key) in childTableData">
7
+ <a-row v-if="childTablePriority" :gutter="16" :key="'childTableRow' + key">
8
+ <a-card :title="item.name" :bordered="false" size="small">
9
+ <x-form-table
10
+ :key="'childTable_' + item.model"
11
+ :title="item.name"
12
+ :queryParamsName="item.childTableConfigName"
13
+ :localEditMode="true"
14
+ :fixed-query-form="childTableFixedQueryForm(item)"
15
+ :service-name="serviceName"
16
+ @innerXFormTableEmit="innerXFormTableEmit"
17
+ @afterTableInit="childTableMounted(item)"
18
+ :ref="'childXFormTable_' + item.model"
19
+ ></x-form-table>
20
+ </a-card>
21
+ </a-row>
22
+ </template>
23
+ <template v-if="isSimpleInlineMode">
24
+ <a-row v-for="(row, rowIndex) in simpleInlineRows" :key="'simple-row-' + rowIndex" :gutter="0" type="flex">
25
+ <a-col v-for="(item, itemIndex) in row.items" :key="'simple-item-' + itemIndex" :span="item.span">
26
+ <x-form-item
27
+ class="simple-inline-item"
28
+ :class="{ 'show-label': simpleInline?.showSimpleInlineLabel }"
29
+ v-bind="formItemLayoutGen"
30
+ :attr="item.formItem"
31
+ :disabled="itemDisabled(item.formItem)"
32
+ :read-only="readonly(item.formItem)"
33
+ :files="files"
34
+ :signs="signs"
35
+ :form="form"
36
+ :images="images"
37
+ :service-name="serviceName"
38
+ mode="新增/修改"
39
+ layout="simple-inline"
40
+ :show-label="simpleInline?.showSimpleInlineLabel === true"
41
+ :rules="rules[`${item.formItem.name}${item.formItem.model}`]"
42
+ :get-data-params="getDataParams"
43
+ :env="env"
44
+ :enablePopupToBody="enablePopupToBody"
45
+ @x-form-item-emit-func="emitFunc"
46
+ @rowChoose="rowChoose"
47
+ :setForm="setForm"
48
+ :style="{ margin: 0, padding: '2px' }"
49
+ />
50
+ </a-col>
51
+ </a-row>
52
+ </template>
53
+ <a-row ref="GroupItemRow" v-if="!isSimpleInlineMode">
54
+ <a-col :span="3" v-if="!inXFormGroup && !(groups[0].groupName === DEFAULT_GROUP_NAME)">
55
+ <a-tabs tab-position="left" v-model="activeTab" @change="scrollToGroup">
56
+ <a-tab-pane
57
+ v-for="(groupsItem, groupsIndex) in groups"
58
+ :tab="groupsItem.groupName"
59
+ :key="groupsIndex"
60
+ ></a-tab-pane>
61
+ </a-tabs>
62
+ </a-col>
63
+ <a-col :span="!inXFormGroup && !(groups[0].groupName === DEFAULT_GROUP_NAME) ? 21 : 24">
64
+ <a-row :gutter="16" type="flex" :key="groupsIndex" v-for="(groupsItem, groupsIndex) in groups">
65
+ <a-col
66
+ :span="24"
67
+ :style="{
68
+ marginTop: groupsIndex === 0 && !inXFormGroup ? '' : '8px',
69
+ fontSize: '14px'
70
+ }"
71
+ v-if="groupsItem.groupName !== DEFAULT_GROUP_NAME"
72
+ >
73
+ <span
74
+ class="xFormGroupTitle"
75
+ :style="{
76
+ paddingLeft: inXFormGroup ? '14px' : '-5px'
77
+ }"
78
+ :ref="`group-title-${groupsIndex}`"
79
+ >
80
+ {{ groupsItem.groupName }}
81
+ </span>
82
+ </a-col>
83
+ <x-form-item
84
+ v-for="(item, index) in groupsItem.realJsonData"
85
+ :key="index"
86
+ :attr="item"
87
+ :disabled="itemDisabled(item)"
88
+ :read-only="readonly(item)"
89
+ :files="files"
90
+ :signs="signs"
91
+ v-bind="formItemLayoutGen"
92
+ :style="layout === 'inline' ? { marginTop: '5px' } : undefined"
93
+ :form="form"
94
+ :images="images"
95
+ :enablePopupToBody="enablePopupToBody"
96
+ :service-name="serviceName"
97
+ mode="新增/修改"
98
+ :layout="layout"
99
+ :rules="rules[`${item.name}${item.model}`]"
100
+ :get-data-params="getDataParams"
101
+ :env="env"
102
+ @x-form-item-emit-func="emitFunc"
103
+ @rowChoose="rowChoose"
104
+ :setForm="setForm"
105
+ />
106
+ </a-row>
107
+ </a-col>
108
+ </a-row>
109
+ <template v-if="!isSimpleInlineMode">
110
+ <a-row :gutter="16" v-for="(groupItem, groupIndex) in groupJsonData" :key="groupIndex">
111
+ <a-card v-if="groupItem.groupItems.length > 0" :title="groupItem.name" :bordered="false" size="small">
112
+ <x-form-item
113
+ v-for="(item, index) in groupItem.groupItems"
114
+ :key="index"
115
+ :attr="item"
116
+ :disabled="itemDisabled(item)"
117
+ :readonly="readonly(item)"
118
+ :files="files"
119
+ :signs="signs"
120
+ v-bind="formItemLayoutGen"
121
+ :style="layout === 'inline' ? { marginTop: '5px' } : undefined"
122
+ :form="form[groupItem.model]"
123
+ :images="images"
124
+ :service-name="serviceName"
125
+ mode="新增/修改"
126
+ :rules="rules[`${item.name}${item.model}`]"
127
+ :get-data-params="getDataParams"
128
+ :env="env"
129
+ :setForm="setForm"
130
+ @rowChoose="rowChoose"
131
+ />
132
+ </a-card>
133
+ <template v-else>
134
+ <slot
135
+ name="groupFormItems"
136
+ :form="form"
137
+ :model="groupItem.model"
138
+ :rules="rules"
139
+ :modifyModelData="modifyModelData"
140
+ ></slot>
141
+ </template>
142
+ </a-row>
143
+ </template>
144
+ <template v-if="!isSimpleInlineMode">
145
+ <a-row :gutter="16" v-for="(item, key) in simpleFormJsonData" :key="'row' + key">
146
+ <a-card v-if="item.value.length > 0" :title="item.name" :bordered="false" size="small">
147
+ <x-form-item
148
+ v-for="(formItem, formItemIndex) in item.value"
149
+ :key="key + formItemIndex"
150
+ :attr="formItem"
151
+ :disabled="itemDisabled(formItem)"
152
+ :readonly="readonly(formItem)"
153
+ :files="files"
154
+ :signs="signs"
155
+ :enablePopupToBody="enablePopupToBody"
156
+ v-bind="formItemLayoutGen"
157
+ :style="layout === 'inline' ? { marginTop: '5px' } : undefined"
158
+ :form="form[groupItem.model]"
159
+ :images="images"
160
+ :service-name="serviceName"
161
+ mode="新增/修改"
162
+ :rules="rules[`${item.name}${item.model}`]"
163
+ :get-data-params="getDataParams"
164
+ :env="env"
165
+ :setForm="setForm"
166
+ @rowChoose="rowChoose"
167
+ />
168
+ </a-card>
169
+ </a-row>
170
+ </template>
171
+ <template v-for="(item, key) in childTableData">
172
+ <a-row v-if="!childTablePriority" :gutter="16" :key="'childTableRow' + key">
173
+ <a-card :title="item.name" :bordered="false" size="small">
174
+ <x-form-table
175
+ :key="'childTable_' + item.model"
176
+ :title="item.name"
177
+ :queryParamsName="item.childTableConfigName"
178
+ :localEditMode="true"
179
+ @innerXFormTableEmit="innerXFormTableEmit"
180
+ :fixed-query-form="childTableFixedQueryForm(item)"
181
+ :service-name="serviceName"
182
+ @afterTableInit="childTableMounted(item)"
183
+ :ref="'childXFormTable_' + item.model"
184
+ ></x-form-table>
185
+ </a-card>
186
+ </a-row>
187
+ </template>
188
+ <a-row type="flex" :justify="btnPlace" v-if="showSubmitBtn" class="form-footer-row" :style="{ paddingLeft: '16px', paddingRight: '16px' }">
189
+ <slot name="footer" :loading="loading">
190
+ <a-button :loading="loading" type="primary" @click="onSubmit()">
191
+ {{ btnName }}
192
+ </a-button>
193
+ </slot>
194
+ </a-row>
195
+ </a-form-model>
196
+ </div>
197
+ </template>
198
+ <script>
199
+ import XFormItem from '@vue2-client/base-client/components/common/XForm/XFormItem'
200
+ import { formatDate, setDataByRealKey } from '@vue2-client/utils/util'
201
+ import * as util from '@vue2-client/utils/util'
202
+ import { mapState } from 'vuex'
203
+ import { addOrModify, getConfigByName, getConfigByNameAsync, runLogic } from '@vue2-client/services/api/common'
204
+ import { checkIdNumber, REG_EMAIL, REG_LANDLINE, REG_PHONE } from '@vue2-client/utils/reg'
205
+ import moment from 'moment/moment'
206
+ import { executeStrFunction, executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
207
+ import formValidationMixin from '@vue2-client/mixins/formValidationMixin'
208
+ import { debounce } from 'lodash'
209
+
210
+ const DEFAULT_GROUP_NAME = '__default__'
211
+ export default {
212
+ name: 'XAddNativeForm',
213
+ components: {
214
+ XFormItem,
215
+ XFormTable: () => import('@vue2-client/base-client/components/common/XFormTable/XFormTable.vue')
216
+ },
217
+ inject: {
218
+ inXFormGroup: {
219
+ default: false
220
+ },
221
+ formGroupContext: {
222
+ default: null
223
+ },
224
+ formDataChange: {
225
+ default: null
226
+ }
227
+ },
228
+ props: {
229
+ // 是否启用 horizontal 模式的自定义配置
230
+ enableHorizontalCustom: {
231
+ type: Boolean,
232
+ default: false
233
+ },
234
+ // 是否启用时间弹出到最外层
235
+ enablePopupToBody: {
236
+ type: Boolean,
237
+ default: false
238
+ }
239
+ },
240
+ data() {
241
+ return {
242
+ DEFAULT_GROUP_NAME,
243
+ // 预览模式
244
+ viewMode: false,
245
+ // 是否处理表单Key值
246
+ isHandleFormKey: true,
247
+ // 内容加载是否完成
248
+ loaded: false,
249
+ // 业务类型
250
+ businessType: '',
251
+ // 业务标题
252
+ title: '',
253
+ // 新增或修改业务是否执行中
254
+ loading: false,
255
+ // 表单Model
256
+ form: {},
257
+ // 配置名称
258
+ configName: undefined,
259
+ // 配置内容,用于查询配置生成器的预览
260
+ configContent: undefined,
261
+ // 表单项集合
262
+ formItems: [],
263
+ // 服务名称
264
+ serviceName: undefined,
265
+ // 是否显示提交按钮
266
+ showSubmitBtn: true,
267
+ // 修改有文件的表单时使用
268
+ files: [],
269
+ images: [],
270
+ // 签名
271
+ signs: [],
272
+ // 校验
273
+ rules: {},
274
+ // 调用logic获取数据源的追加参数
275
+ getDataParams: {},
276
+ // 外部数据(通过 XFormGroup 或直接传入)
277
+ externalData: {},
278
+ // 动态简易表单集合
279
+ simpleFormJsonData: {},
280
+ // 待修改的数据集
281
+ modifyModelData: {},
282
+ // 当前环境
283
+ env: 'prod',
284
+ // 表单主键
285
+ primaryKey: null,
286
+ // 表单模式 horizontal | vertical | inline
287
+ layout: 'horizontal',
288
+ // 提交按钮名称
289
+ btnName: '提交',
290
+ // 提交按钮位置 start / center / end
291
+ btnPlace: 'end',
292
+ // 子表是否排在前面
293
+ childTablePriority: false,
294
+ // simple-inline布局配置
295
+ simpleInline: null,
296
+ // 表单组名称(用于 XFormGroup 场景)
297
+ groupName: null,
298
+ // 是否正在刷新表单项(用于避免死循环)
299
+ isRefreshing: false
300
+ }
301
+ },
302
+ computed: {
303
+ // 过滤出用于新增/修改场景的表单项
304
+ realJsonData: function () {
305
+ return this.formItems.filter(item => {
306
+ return (
307
+ item.addOrEdit &&
308
+ item.addOrEdit !== 'no' &&
309
+ item.addOrEdit !== 'silenceAdd' &&
310
+ item.addOrEdit !== 'version' &&
311
+ !this.itemDisabled(item)
312
+ )
313
+ })
314
+ },
315
+ // 表单项组 / 不是 数据组
316
+ groups: function () {
317
+ if (!this.realJsonData || this.realJsonData.length === 0) {
318
+ return [
319
+ {
320
+ groupName: DEFAULT_GROUP_NAME,
321
+ realJsonData: this.realJsonData,
322
+ xAddFormLayout: 'horizontal'
323
+ }
324
+ ]
325
+ }
326
+ const uniqueGroups = new Set(this.realJsonData.map(item => item.group).filter(Boolean))
327
+ const allGroup = Array.from(uniqueGroups).map(group => {
328
+ return {
329
+ groupName: group,
330
+ realJsonData: this.realJsonData.filter(item => item.group === group),
331
+ xAddFormLayout: 'horizontal'
332
+ }
333
+ })
334
+ // 判断每一组得formJson 长度相加是否等于 realJsonData 长度 避免错误数据
335
+ if (allGroup.reduce((total, item) => total + item.realJsonData.length, 0) === this.realJsonData.length) {
336
+ return allGroup
337
+ } else {
338
+ return [
339
+ {
340
+ groupName: DEFAULT_GROUP_NAME,
341
+ realJsonData: this.realJsonData,
342
+ xAddFormLayout: 'horizontal'
343
+ }
344
+ ]
345
+ }
346
+ },
347
+ // 拥有自定义校验函数得表单项
348
+ customValidateItems: function () {
349
+ return this.formItems.filter(item => {
350
+ return item?.rule?.type === 'customJs'
351
+ })
352
+ },
353
+ // 过滤出用于新增/修改场景的表单项
354
+ groupJsonData: function () {
355
+ return this.formItems
356
+ .filter(item => {
357
+ return item.type === 'group'
358
+ })
359
+ .map(item => {
360
+ item.groupItems = item.groupItems
361
+ .filter(item => {
362
+ return (
363
+ item.addOrEdit &&
364
+ item.addOrEdit !== 'no' &&
365
+ item.addOrEdit !== 'silenceAdd' &&
366
+ item.addOrEdit !== 'version' &&
367
+ !this.itemDisabled(item)
368
+ )
369
+ })
370
+ .map(groupItem => {
371
+ // 只保留第一个下划线后面的内容
372
+ // 多层校验规则需要将prop设置为 key1.key2.....
373
+ groupItem.prop = `${item.model}.${groupItem.model.substring(groupItem.model.indexOf('_') + 1)}`
374
+ groupItem.model = groupItem.model.substring(groupItem.model.indexOf('_') + 1)
375
+ return groupItem
376
+ })
377
+ return item
378
+ })
379
+ },
380
+ // 过滤出用于子表数据新增/修改场景的表单项
381
+ childTableData: function () {
382
+ return this.formItems.filter(item => {
383
+ return item.type === 'childTable'
384
+ })
385
+ },
386
+ // 过滤出用于form子表数据新增/修改场景的表单项
387
+ childFormData: function () {
388
+ return this.formItems.filter(item => {
389
+ return item.type === 'rowEdit'
390
+ })
391
+ },
392
+ // 过滤出用于静默新增场景的表单项
393
+ silenceAddJsonData: function () {
394
+ return this.formItems.filter(function (item) {
395
+ return item.addOrEdit === 'silenceAdd'
396
+ })
397
+ },
398
+ // 过滤出版本号表单项
399
+ versionJsonData: function () {
400
+ return this.formItems.filter(function (item) {
401
+ return item.addOrEdit === 'version'
402
+ })
403
+ },
404
+ formItemLayoutGen() {
405
+ if (this.layout === 'simple-inline') {
406
+ const showLabel = this.simpleInline?.showSimpleInlineLabel === true
407
+ return {
408
+ labelCol: { span: showLabel ? 4 : 0 },
409
+ wrapperCol: { span: showLabel ? 20 : 24 }
410
+ }
411
+ }
412
+ if (this.layout === 'horizontal') {
413
+ // 如果启用了自定义配置,从 formItemLayout 读取(支持 0 值)
414
+ if (this.enableHorizontalCustom && this.formItemLayout) {
415
+ return {
416
+ labelCol: {
417
+ span: this.formItemLayout.labelCol ?? 4,
418
+ offset: this.formItemLayout.offset ?? 2
419
+ },
420
+ wrapperCol: { span: this.formItemLayout.wrapperCol ?? 14 }
421
+ }
422
+ }
423
+ // 默认配置
424
+ return {
425
+ labelCol: { span: 4, offset: 2 },
426
+ wrapperCol: { span: 14 }
427
+ }
428
+ } else if (this.layout === 'vertical') {
429
+ return {}
430
+ } else {
431
+ if (!this.formItemLayout.labelCol || !this.formItemLayout.wrapperCol) {
432
+ return {
433
+ labelCol: { span: 8 },
434
+ wrapperCol: { span: 16 }
435
+ }
436
+ }
437
+ return {
438
+ labelCol: { span: this.formItemLayout.labelCol },
439
+ wrapperCol: { span: this.formItemLayout.wrapperCol }
440
+ }
441
+ }
442
+ },
443
+ // simple-inline模式判断
444
+ isSimpleInlineMode() {
445
+ return this.layout === 'simple-inline' && this.simpleInline
446
+ },
447
+ // simple-inline布局行数据
448
+ simpleInlineRows() {
449
+ if (!this.isSimpleInlineMode) return []
450
+ return this.simpleInline.rows.map(row => ({
451
+ ...row,
452
+ items: row.items
453
+ .map(item => ({
454
+ ...item,
455
+ formItem: this.realJsonData.find(f => f.model === item.key && f.name === item.title)
456
+ }))
457
+ .filter(item => item.formItem)
458
+ }))
459
+ },
460
+ ...mapState('account', { currUser: 'user' })
461
+ },
462
+ provide() {
463
+ return {
464
+ getComponentByName: this.getComponentByName,
465
+ registerComponent: this.registerComponent,
466
+ XFormContext: this,
467
+ // 移除必填项
468
+ removeRequired: this.removeRequired,
469
+ // 设置必填项
470
+ setRequired: this.setRequired,
471
+ getSelf: () => this,
472
+ // 传递 formGroupContext 给子组件
473
+ formGroupContext: this.formGroupContext,
474
+ // 传递外部数据给子组件
475
+ _getExtData: this.getExtData
476
+ }
477
+ },
478
+ watch: {
479
+ form: {
480
+ handler(val) {
481
+ // 如果是 refreshShowFunc 触发的变化,不调用 formDataChange,避免死循环
482
+ if (this.isRefreshing) {
483
+ return
484
+ }
485
+ if (this.formDataChange && typeof this.formDataChange === 'function') {
486
+ // 如果存在 groupName,则传递 groupName 和 formData(用于 XFormGroup 场景)
487
+ if (this.groupName) {
488
+ // 使用防抖避免频繁触发
489
+ if (!this.debouncedFormDataChange) {
490
+ this.debouncedFormDataChange = debounce(() => {
491
+ if (this.formDataChange && typeof this.formDataChange === 'function') {
492
+ this.formDataChange(this.groupName, this.form)
493
+ }
494
+ }, 150)
495
+ }
496
+ this.debouncedFormDataChange()
497
+ } else {
498
+ // 兼容非 XFormGroup 场景
499
+ this.formDataChange(val)
500
+ }
501
+ }
502
+ // 自定义校验红星:本组 form 变化时更新 tempRequired
503
+ if (this.customValidateItems.length > 0) {
504
+ if (!this.debouncedRunCustomValidation) {
505
+ this.debouncedRunCustomValidation = debounce(() => this.runCustomValidationForDisplay(), 150)
506
+ }
507
+ this.debouncedRunCustomValidation()
508
+ }
509
+ },
510
+ deep: true
511
+ }
512
+ },
513
+ mixins: [formValidationMixin],
514
+ methods: {
515
+ runLogic,
516
+ getConfigByNameAsync,
517
+ getConfigByName,
518
+ init(params) {
519
+ this._configEndEmitted = false
520
+ this._configEndPromise = new Promise(resolve => { this._configEndResolve = resolve })
521
+ const {
522
+ configName,
523
+ configContent,
524
+ formItems,
525
+ formJson,
526
+ viewMode,
527
+ isHandleFormKey,
528
+ isKeyHandle = true,
529
+ showSubmitBtn = true,
530
+ serviceName,
531
+ primaryKey,
532
+ modifyModelData = {},
533
+ businessType,
534
+ title,
535
+ fixedAddForm = {},
536
+ getDataParams = {},
537
+ extData = {},
538
+ simpleFormJsonData = {},
539
+ env = 'prod',
540
+ layout,
541
+ xAddFormLayout = 'horizontal',
542
+ formItemLayout = {},
543
+ btnName = '提交',
544
+ childTablePriority = false,
545
+ btnPlace = 'end',
546
+ simpleInline = null,
547
+ paramLogicName,
548
+ paramLogicServiceName,
549
+ paramLogicNameParam
550
+ } = params
551
+ this.loaded = false
552
+ // 保存表单组名称(用于 XFormGroup 场景)
553
+ this.groupName = params.groupName || null
554
+ // 兼容需要省略 传递 layout: res.xAddFormLayout 可以使用 ...res 展开运算符 直接转递
555
+ if (xAddFormLayout && layout === undefined) {
556
+ this.layout = xAddFormLayout
557
+ } else {
558
+ this.layout = layout
559
+ }
560
+ this.formItemLayout = formItemLayout
561
+ if ((isHandleFormKey === null || isHandleFormKey === undefined) && !isKeyHandle) {
562
+ this.isHandleFormKey = isKeyHandle
563
+ } else if (isHandleFormKey) {
564
+ this.isHandleFormKey = isHandleFormKey
565
+ } else {
566
+ this.isHandleFormKey = isKeyHandle
567
+ }
568
+ this.childTablePriority = childTablePriority
569
+ this.configName = configName
570
+ this.configContent = configContent
571
+ this.formItems = this.getFromItem(formItems, formJson)
572
+ this.viewMode = viewMode
573
+ this.showSubmitBtn = showSubmitBtn
574
+ this.primaryKey = primaryKey
575
+ this.serviceName = serviceName
576
+ this.businessType = businessType
577
+ this.title = title
578
+ this.getDataParams = getDataParams
579
+ this.extData = extData
580
+ this.simpleFormJsonData = simpleFormJsonData
581
+ this.env = env
582
+ this.btnName = btnName
583
+ this.btnPlace = btnPlace
584
+ this.simpleInline = simpleInline
585
+ // 如果 fixedAddForm 有 selected_id 值,并且设置了处理表单key值,则多给 selected_id 加前缀 避免处理错误
586
+ if (fixedAddForm.selected_id && this.isHandleFormKey) {
587
+ fixedAddForm._selected_id = fixedAddForm.selected_id
588
+ delete fixedAddForm.selected_id
589
+ }
590
+ // 设置普通表单项的相关参数
591
+ const formData = Object.assign({}, fixedAddForm)
592
+ if (paramLogicName) {
593
+ // console.warn('2paramLogicServiceName', paramLogicServiceName)
594
+ runLogic(paramLogicName, paramLogicNameParam, (typeof paramLogicServiceName === 'string' && paramLogicServiceName.trim()) || this.serviceName).then(res => {
595
+ Object.assign(formData, res)
596
+ })
597
+ }
598
+ for (let i = 0; i < this.realJsonData.length; i++) {
599
+ const item = this.realJsonData[i]
600
+ this.setFormProps(formData, item, null)
601
+ }
602
+ // 设置表单分组项目相关参数
603
+ for (let i = 0; i < this.groupJsonData.length; i++) {
604
+ const groupItem = this.groupJsonData[i]
605
+ formData[groupItem.model] = {}
606
+ for (let j = 0; j < groupItem.groupItems.length; j++) {
607
+ const item = groupItem.groupItems[j]
608
+ this.setFormProps(formData[groupItem.model], item, item.prop)
609
+ }
610
+ }
611
+ // 设置动态简易表单项的相关参数
612
+ for (const key in this.simpleFormJsonData) {
613
+ for (const item of this.simpleFormJsonData[key].value) {
614
+ item.model = key + '@' + item.model
615
+ this.setFormProps(formData, item, null)
616
+ }
617
+ }
618
+
619
+ // 修改场景下对表单项赋值
620
+ this.form = formData
621
+ if (modifyModelData && modifyModelData.data) {
622
+ this.modifyModelData = modifyModelData
623
+ if (Object.keys(modifyModelData.data).length > 0) {
624
+ this.getModifyModelData(modifyModelData)
625
+ }
626
+ }
627
+ // 处理表单得附件
628
+ if (modifyModelData && modifyModelData.files) {
629
+ this.files = modifyModelData.files
630
+ }
631
+ if (modifyModelData && modifyModelData.images) {
632
+ this.images = modifyModelData.images
633
+ }
634
+ if (modifyModelData && modifyModelData.signs) {
635
+ this.signs = modifyModelData.signs
636
+ }
637
+ this.loaded = true
638
+ this._emitConfigEnd()
639
+ },
640
+ /** 配置加载/应用结束后发出,供 XReport 判断「所有 slot 配置结束」;每次 init 仅发出一次 */
641
+ _emitConfigEnd() {
642
+ if (this._configEndEmitted) return
643
+ this._configEndEmitted = true
644
+ if (this._configEndResolve) {
645
+ this._configEndResolve()
646
+ this._configEndResolve = null
647
+ }
648
+ this.$emit('configEnd')
649
+ },
650
+ /**
651
+ * 等待配置结束的异步方法。配置已结束则立即 resolve,否则在 configEnd 时 resolve。
652
+ * @returns {Promise<void>}
653
+ */
654
+ waitConfigEnd() {
655
+ if (this._configEndEmitted) return Promise.resolve()
656
+ return this._configEndPromise || Promise.resolve()
657
+ },
658
+ getExtData() {
659
+ return this.extData
660
+ },
661
+ scrollToGroup(index) {
662
+ const groupElement = this.$refs[`group-title-${index}`][0]
663
+ if (groupElement) {
664
+ groupElement.scrollIntoView({ behavior: 'smooth' })
665
+ }
666
+ },
667
+ registerComponent(componentName, component) {
668
+ console.log('内部注册', this.$options.name, componentName)
669
+ this.$refs[componentName] = component
670
+ console.log('内部注册完成', this.$refs)
671
+ },
672
+ // 根据名字从注册到组件中获取组件
673
+ getComponentByName(componentName) {
674
+ console.log('内部取组件', this.$options.name, componentName)
675
+ console.log('内部组件内容', this.$refs)
676
+ return this.$refs[componentName]
677
+ },
678
+ // 兼容需要省略 传递 [formItems: res.formJson ] 可以使用 ...res 展开运算符 直接转递
679
+ getFromItem(formItems, formJson) {
680
+ const _formItems = formItems || formJson
681
+ if (typeof formItems === 'string') {
682
+ return JSON.parse(_formItems)
683
+ } else {
684
+ return JSON.parse(JSON.stringify(_formItems))
685
+ }
686
+ },
687
+ innerXFormTableEmit(fun, record, id, actionType, index) {
688
+ this.$emit(fun, record, id, actionType, index)
689
+ },
690
+ // 时间组件赋默认值
691
+ // .type, item.formDefault
692
+ getDateRange({ type, formDefault: defaultValue, formValueFormat }) {
693
+ // const format = type === 'datePicker' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'
694
+ let format = 'YYYY-MM-DD HH:mm:ss'
695
+ if (formValueFormat) {
696
+ format = formValueFormat
697
+ }
698
+ let start
699
+ switch (defaultValue) {
700
+ case 'curYear':
701
+ start = moment().startOf('year').format(format)
702
+ break
703
+ case 'curMonth':
704
+ start = moment().startOf('month').format(format)
705
+ break
706
+ case 'curDay':
707
+ start = moment().startOf('day').format(format)
708
+ break
709
+ case 'curTime':
710
+ start = moment().format(format)
711
+ break
712
+ default:
713
+ return defaultValue
714
+ }
715
+ return start
716
+ },
717
+ setFormProps(formData, item, groupItem) {
718
+ const rulesKey = `${item.name}${item.model}`
719
+ if (formData[item.model] === undefined || formData[item.model] === null) {
720
+ formData[item.model] = undefined
721
+ }
722
+ if (!formData[item.model] && item.formDefault) {
723
+ if (
724
+ ['datePicker', 'rangePicker', 'yearPicker', 'monthPicker', 'yearRangePicker', 'monthRangePicker'].includes(
725
+ item.type
726
+ )
727
+ ) {
728
+ formData[item.model] = this.getDateRange(item)
729
+ } else if (
730
+ ['treeSelect', 'select', 'checkbox'].includes(item.type) &&
731
+ ['curOrgId', 'curDepId', 'curUserId'].includes(item.formDefault)
732
+ ) {
733
+ if (item.formDefault === 'curOrgId') {
734
+ formData[item.model] = item.type === 'select' ? this.currUser.orgid : [this.currUser.orgid]
735
+ }
736
+ if (item.formDefault === 'curDepId') {
737
+ formData[item.model] = item.type === 'select' ? this.currUser.depids : [this.currUser.depids]
738
+ }
739
+ if (item.formDefault === 'curUserId') {
740
+ formData[item.model] = item.type === 'select' ? this.currUser.id : [this.currUser.id]
741
+ }
742
+ } else if (['addressSearch'].includes(item.type)) {
743
+ formData[item.model] = item.formDefault
744
+ formData[`${item.model}_lng_lat`] = item.formDefault_lng_lat
745
+ } else {
746
+ formData[item.model] = item.formDefault
747
+ }
748
+ }
749
+ // 处理表单校验情况
750
+ if (item.rule) {
751
+ const rulesArrayKey = groupItem || rulesKey
752
+ this.rules[rulesArrayKey] = []
753
+ const required = item.rule.required ? item.rule.required === true || item.rule.required === 'true' : false
754
+ let trigger
755
+ let message
756
+ if (required) {
757
+ switch (item.type) {
758
+ case 'select':
759
+ case 'checkbox':
760
+ case 'radio':
761
+ case 'treeSelect':
762
+ case 'rangePicker':
763
+ case 'monthPicker':
764
+ case 'yearPicker':
765
+ case 'datePicker':
766
+ case 'file':
767
+ case 'image':
768
+ case 'citySelect':
769
+ case 'addressSearch':
770
+ case 'personSetting':
771
+ message = '请选择' + item.name
772
+ trigger = 'change'
773
+ break
774
+ default:
775
+ message = '请输入' + item.name
776
+ trigger = 'blur'
777
+ }
778
+ this.rules[rulesArrayKey].push({
779
+ required: true,
780
+ message: message,
781
+ trigger: trigger
782
+ })
783
+ }
784
+ // 文件/图片上传最小数量校验:必填时强制校验;非必填但已上传时也需校验
785
+ if (['file', 'image'].includes(item.type) && item.minAcceptCount) {
786
+ const minCount = Number(item.minAcceptCount)
787
+ this.rules[rulesArrayKey].push({
788
+ validator: (rule, value, callback) => {
789
+ const count = Array.isArray(value) ? value.length : value ? 1 : 0
790
+ if ((required || count > 0) && count < minCount) {
791
+ callback(new Error(`请至少上传${minCount}个${item.name}`))
792
+ } else {
793
+ callback()
794
+ }
795
+ },
796
+ trigger: 'change'
797
+ })
798
+ }
799
+
800
+ switch (item.rule.type) {
801
+ case 'number':
802
+ case 'integer':
803
+ case 'float':
804
+ // eslint-disable-next-line no-case-declarations
805
+ let defaultValue
806
+ // eslint-disable-next-line no-case-declarations
807
+ let message
808
+ switch (item.rule.type) {
809
+ case 'number':
810
+ item.numberInput = true
811
+ message = '数字'
812
+ defaultValue = 0
813
+ break
814
+ case 'integer':
815
+ item.numberInput = true
816
+ message = '整数'
817
+ defaultValue = 0
818
+ break
819
+ case 'float':
820
+ item.numberInput = true
821
+ message = '小数'
822
+ defaultValue = 0.0
823
+ break
824
+ }
825
+ this.rules[rulesArrayKey].push({
826
+ type: item.rule.type,
827
+ message: item.name + '必须为' + message,
828
+ transform: value => {
829
+ if (value && value.length !== 0) {
830
+ return Number(value)
831
+ } else {
832
+ return defaultValue
833
+ }
834
+ },
835
+ trigger: 'blur'
836
+ })
837
+ break
838
+ case 'email': {
839
+ const validator = (rule, value, callback) => {
840
+ if (value && !REG_EMAIL.test(value)) {
841
+ callback(new Error('请输入正确的邮箱地址'))
842
+ } else {
843
+ callback()
844
+ }
845
+ }
846
+ this.rules[rulesArrayKey].push({
847
+ type: 'email',
848
+ validator: validator,
849
+ message: '请输入正确的邮箱地址',
850
+ trigger: 'blur'
851
+ })
852
+ break
853
+ }
854
+ case 'userPhone': {
855
+ this.rules[rulesArrayKey].push({
856
+ type: 'userPhone',
857
+ validator: (rule, value, callback) => {
858
+ if (value && !REG_PHONE.test(value)) {
859
+ callback(new Error('请输入正确的手机号码'))
860
+ } else {
861
+ callback()
862
+ }
863
+ },
864
+ message: '请输入正确的手机号码',
865
+ trigger: 'blur'
866
+ })
867
+ break
868
+ }
869
+ case 'idNumber': {
870
+ this.rules[rulesArrayKey].push({
871
+ validator: (rule, value, callback) => {
872
+ if (value && !checkIdNumber(value)) {
873
+ callback(new Error('请输入正确的身份证号码'))
874
+ } else {
875
+ callback()
876
+ }
877
+ },
878
+ trigger: 'blur'
879
+ })
880
+ break
881
+ }
882
+ case 'landlineNumber': {
883
+ this.rules[rulesArrayKey].push({
884
+ validator: (rule, value, callback) => {
885
+ if (value && !REG_LANDLINE.test(value)) {
886
+ callback(new Error('请输入正确的座机号码'))
887
+ } else {
888
+ callback()
889
+ }
890
+ },
891
+ trigger: 'blur'
892
+ })
893
+ break
894
+ }
895
+ // 大于0
896
+ case 'greaterThanZero': {
897
+ item.numberInput = true
898
+ this.rules[rulesArrayKey].push({
899
+ validator: (rule, value, callback) => {
900
+ if (isNaN(value) || value <= 0) {
901
+ callback(new Error('请输入一个大于0的数字'))
902
+ } else {
903
+ callback()
904
+ }
905
+ },
906
+ trigger: 'blur'
907
+ })
908
+ break
909
+ }
910
+ // 大于等于0
911
+ case 'greaterThanOrEqualZero': {
912
+ item.numberInput = true
913
+ this.rules[rulesArrayKey].push({
914
+ validator: (rule, value, callback) => {
915
+ if (isNaN(value) || value < 0) {
916
+ callback(new Error('请输入一个大于等于0的数字'))
917
+ } else {
918
+ callback()
919
+ }
920
+ },
921
+ trigger: 'blur'
922
+ })
923
+ break
924
+ }
925
+ case 'stringLength': {
926
+ this.rules[rulesArrayKey].push({
927
+ validator: (rule, value, callback) => {
928
+ if (value && value.length < item.rule.minLen) {
929
+ callback(new Error('长度不能少于' + item.rule.minLen + '个字符'))
930
+ } else if (value && value.length > item.rule.maxLen) {
931
+ callback(new Error('长度不能超过' + item.rule.maxLen + '个字符'))
932
+ } else {
933
+ callback()
934
+ }
935
+ },
936
+ trigger: 'blur'
937
+ })
938
+ break
939
+ }
940
+ case 'customJs': {
941
+ this.rules[rulesArrayKey].push({
942
+ validator: (rule, value, callback) => {
943
+ this.customJsValidate(rule, value, callback, item)
944
+ },
945
+ trigger: 'blur'
946
+ })
947
+ break
948
+ }
949
+ }
950
+ }
951
+ },
952
+ childTableMounted(item) {
953
+ // 子表初始化时,设置表格数据
954
+ if (this.form[item.model] && this.form[item.model].length > 0) {
955
+ this.$refs[`childXFormTable_${item.model}`][0].setTableData(this.form[item.model])
956
+ }
957
+ },
958
+ customJsValidate(rule, value, callback, item) {
959
+ if (item.rule.customValidatorFunc) {
960
+ executeStrFunctionByContext(this, item.rule.customValidatorFunc, [
961
+ rule,
962
+ value,
963
+ callback,
964
+ this.form,
965
+ item,
966
+ this.util,
967
+ runLogic,
968
+ getConfigByNameAsync
969
+ ])
970
+ } else {
971
+ callback()
972
+ }
973
+ },
974
+ /**
975
+ * 根据自定义校验函数更新表单项红星状态(tempRequired)及校验规则
976
+ * 支持两种触发:1) 本组 form 变化时由 watch 调用 2) 他组变化时由 refreshShowFunc 调用
977
+ * XFormGroup 场景下,自定义校验函数可通过 this.formGroupContext.allFormData 获取全部表单数据
978
+ */
979
+ runCustomValidationForDisplay() {
980
+ if (!this.customValidateItems || this.customValidateItems.length === 0) return
981
+ for (const item of this.customValidateItems) {
982
+ const itemIndex = this.formItems.findIndex(formItem => formItem.model === item.model)
983
+ if (itemIndex < 0) continue
984
+ try {
985
+ this.customJsValidate(null, this.form[item.model], res => {
986
+ this.$set(this.formItems[itemIndex], 'tempRequired', res instanceof Error)
987
+ }, item)
988
+ } catch (e) {
989
+ console.error('[XAddNativeForm] runCustomValidationForDisplay:', e)
990
+ this.$set(this.formItems[itemIndex], 'tempRequired', true)
991
+ }
992
+ }
993
+ },
994
+ itemDisabled(value) {
995
+ return (
996
+ (this.businessType === '新增' && value.addOrEdit === 'edit') ||
997
+ (this.businessType === '修改' && value.addOrEdit === 'add')
998
+ )
999
+ },
1000
+ readonly(value) {
1001
+ return value.addOrEdit === 'readonly'
1002
+ },
1003
+ async onSubmit() {
1004
+ const valid = await this.validateForm()
1005
+ if (!valid) return false
1006
+ if (this.viewMode) {
1007
+ this.$message.info('预览模式禁止新增和修改')
1008
+ return false
1009
+ }
1010
+ this.loading = true
1011
+ const requestForm = this.prepareForm()
1012
+ await this.appendSilenceAddFields(requestForm)
1013
+ const realForm = this.handleFormKeys(requestForm)
1014
+ // 增加子表数据
1015
+ if (this.childTableData.length > 0) {
1016
+ for (const item of this.childTableData) {
1017
+ const childModel = item.model
1018
+ const childDataRef = this.$refs['childXFormTable_' + item.model][0].getTableData()
1019
+ const childData = []
1020
+ for (const item of childDataRef) {
1021
+ childData.push(JSON.parse(JSON.stringify(item)))
1022
+ }
1023
+ for (let i = 0; i < childData.length; i++) {
1024
+ childData[i] = this.handleFormKeys(childData[i])
1025
+ // 外键不需要带表别名,所以此处放到表单处理后赋值
1026
+ if (realForm.id) {
1027
+ childData[i][item.childTableForeignKeyName] = realForm.id
1028
+ }
1029
+ }
1030
+ realForm[childModel] = childData
1031
+ }
1032
+ }
1033
+ // 增加form子表数据
1034
+ if (this.childFormData.length > 0) {
1035
+ for (const item of this.childFormData) {
1036
+ const childModel = item.model
1037
+ const childData = this.$refs[item.model].getTableData()
1038
+ for (let i = 0; i < childData.length; i++) {
1039
+ childData[i] = this.handleFormKeys(childData[i], true)
1040
+ // 外键不需要带表别名,所以此处放到表单处理后赋值
1041
+ if (realForm.id) {
1042
+ childData[i][item.foreignKey] = realForm.id
1043
+ }
1044
+ }
1045
+ realForm[childModel] = childData
1046
+ }
1047
+ }
1048
+ if (this.$listeners.onSubmit) {
1049
+ // 交由父级处理
1050
+ this.$emit('onSubmit', {
1051
+ businessType: this.businessType,
1052
+ serviceName: this.serviceName,
1053
+ realForm: realForm,
1054
+ currUserName: this.currUser.name,
1055
+ currUserId: this.currUser.id,
1056
+ orgId: this.currUser.orgid
1057
+ })
1058
+ } else {
1059
+ this.defaultSubmit(realForm)
1060
+ }
1061
+ },
1062
+
1063
+ async asyncSubmit() {
1064
+ return new Promise((resolve, reject) => {
1065
+ this.$refs.selectForm.validate(async valid => {
1066
+ if (!valid) {
1067
+ reject(new Error('Form validation failed'))
1068
+ return
1069
+ }
1070
+ this.loading = true
1071
+ const requestForm = this.prepareForm()
1072
+ await this.appendSilenceAddFields(requestForm)
1073
+ const realForm = this.handleFormKeys(requestForm)
1074
+ resolve({
1075
+ realForm,
1076
+ businessType: this.businessType,
1077
+ serviceName: this.serviceName,
1078
+ currUserName: this.currUser.name,
1079
+ currUserId: this.currUser.id,
1080
+ orgId: this.currUser.orgid
1081
+ })
1082
+ })
1083
+ })
1084
+ },
1085
+
1086
+ validateForm() {
1087
+ return new Promise(resolve => {
1088
+ this.$refs.selectForm.validate(valid => resolve(valid))
1089
+ })
1090
+ },
1091
+
1092
+ childTableFixedQueryForm(item) {
1093
+ if (this.modifyModelData?.primaryKeyData) {
1094
+ const fixedForm = {}
1095
+ fixedForm[item.childTableForeignKeyName] = Object.values(this.modifyModelData.primaryKeyData)[0]
1096
+ return fixedForm
1097
+ }
1098
+ return null
1099
+ },
1100
+
1101
+ prepareForm() {
1102
+ const form = { ...this.form }
1103
+ for (const key of Object.keys(form)) {
1104
+ const value = form[key]
1105
+ if (value === null || (typeof value === 'object' && Object.keys(value).length === 0)) {
1106
+ form[key] = undefined
1107
+ }
1108
+ }
1109
+ return form
1110
+ },
1111
+
1112
+ async appendSilenceAddFields(form) {
1113
+ if (this.businessType === '新增') {
1114
+ for (const item of this.silenceAddJsonData) {
1115
+ switch (item.silencePurpose) {
1116
+ case 'createTime':
1117
+ form[item.model] = formatDate('now')
1118
+ break
1119
+ case 'operator':
1120
+ form[item.model] = this.currUser.name
1121
+ break
1122
+ case 'operatorId':
1123
+ form[item.model] = this.currUser.id
1124
+ break
1125
+ case 'orgId':
1126
+ form[item.model] = this.currUser.orgid
1127
+ break
1128
+ case 'orgName':
1129
+ form[item.model] = this.currUser.orgs
1130
+ break
1131
+ case 'depId':
1132
+ form[item.model] = this.currUser.depids
1133
+ break
1134
+ case 'depName':
1135
+ form[item.model] = this.currUser.deps
1136
+ break
1137
+ }
1138
+ }
1139
+ for (const item of this.silenceAddJsonData.filter(item => item.silencePurpose === 'customize')) {
1140
+ const result = await runLogic(item.silenceSource, form, this.serviceName)
1141
+ if (result) {
1142
+ const keys = Object.keys(result)
1143
+ if (keys.length === 1 && keys[0] === 'value') {
1144
+ form[item.model] = result.value
1145
+ } else {
1146
+ form[item.model] = result
1147
+ }
1148
+ } else {
1149
+ form[item.model] = result
1150
+ }
1151
+ }
1152
+ }
1153
+ },
1154
+
1155
+ handleFormKeys(form, mustHandleKey = false) {
1156
+ const realForm = {}
1157
+ for (const key of Object.keys(form)) {
1158
+ const value = form[key]
1159
+ const extraFormKeyTagIndex = key.indexOf('@')
1160
+ if (extraFormKeyTagIndex !== -1) {
1161
+ const extraFormKey = key.substring(0, extraFormKeyTagIndex)
1162
+ const realKey = key.substring(extraFormKeyTagIndex + 1)
1163
+ if (!realForm[extraFormKey]) {
1164
+ realForm[extraFormKey] = {}
1165
+ }
1166
+ realForm[extraFormKey][realKey] = value
1167
+ } else {
1168
+ const realKey = this.isHandleFormKey || mustHandleKey ? this.getRealKey(key, mustHandleKey) : key
1169
+ // 如果发生重名,不覆盖,把key的别名带上
1170
+ if (realForm[realKey]) {
1171
+ realForm[key] = value
1172
+ } else {
1173
+ realForm[realKey] = value
1174
+ }
1175
+ }
1176
+ }
1177
+ return realForm
1178
+ },
1179
+ // 默认提交事件
1180
+ defaultSubmit(realForm, callback) {
1181
+ // 新增移除id
1182
+ if (this.businessType === '新增') {
1183
+ delete realForm.id
1184
+ }
1185
+ // 组织请求
1186
+ const requestParameters = {
1187
+ queryParamsName: this.configName,
1188
+ queryParams: this.configContent,
1189
+ form: realForm,
1190
+ businessType: this.businessType,
1191
+ operator: this.currUser.name
1192
+ }
1193
+ addOrModify(requestParameters, this.serviceName, this.env === 'dev')
1194
+ .then(data => {
1195
+ this.$message.success(this.businessType + '成功!')
1196
+ // commit
1197
+ this.$emit('afterSubmit', {
1198
+ type: this.businessType,
1199
+ id: data.id,
1200
+ data: data,
1201
+ form: requestParameters.form
1202
+ })
1203
+ this.loading = false
1204
+ if (callback) {
1205
+ callback()
1206
+ }
1207
+ })
1208
+ .catch(e => {
1209
+ this.loading = false
1210
+ this.$message.error(this.businessType + '失败:' + e)
1211
+ })
1212
+ },
1213
+ // 获取表单字段实际值
1214
+ getRealKey(key, mustHandleKey = false) {
1215
+ if (key === 'selected_id') return key
1216
+ if (this.isHandleFormKey || mustHandleKey) {
1217
+ return key.substring(key.indexOf('_') + 1)
1218
+ } else {
1219
+ return key
1220
+ }
1221
+ },
1222
+ /**
1223
+ * 获取被修改记录数据
1224
+ * @param modifyModelData 被修改记录的数据
1225
+ */
1226
+ getModifyModelData(modifyModelData) {
1227
+ if (modifyModelData.primaryKeyData) {
1228
+ this.form = Object.assign(this.form, modifyModelData.primaryKeyData)
1229
+ }
1230
+ // 对动态简易表单项特殊处理
1231
+ for (const key in modifyModelData.data) {
1232
+ const realKey = this.isHandleFormKey ? this.getRealKey(key) : key
1233
+ if (this.simpleFormJsonData[realKey]) {
1234
+ const extraForm = JSON.parse(modifyModelData.data[key])
1235
+ for (const key in extraForm) {
1236
+ const model = realKey + '@' + key
1237
+ this.form[model] = extraForm[key]
1238
+ }
1239
+ }
1240
+ }
1241
+ // 对普通表单项处理
1242
+ for (let i = 0; i < this.realJsonData.length; i++) {
1243
+ if (['FilesId', 'Images'].includes(this.realJsonData[i])) {
1244
+ // 附件需要跳过 因为会通过 modifyModelData中的files,images属性给upload赋值
1245
+ // 新增修改表单每次提交时只会提交最新添加的文件
1246
+ continue
1247
+ }
1248
+ const item = this.realJsonData[i]
1249
+ // 地址选择器 需要传递 经纬度字段, 配置中添加 `${item.model}_lng_lat` CRUD 只需勾选 SQL生成查询项
1250
+ if (['addressSearch'].includes(item.type)) {
1251
+ this.form[item.model] = modifyModelData.data[item.model] + ''
1252
+ this.form[`${item.model}_lng_lat`] = modifyModelData.data[`${item.model}_lng_lat`] + ''
1253
+ continue
1254
+ }
1255
+ if (modifyModelData.data[item.model] || modifyModelData.data[item.model] === 0) {
1256
+ if (modifyModelData.data[item.model] instanceof Array) {
1257
+ this.form[item.model] = modifyModelData.data[item.model]
1258
+ } else {
1259
+ this.form[item.model] = modifyModelData.data[item.model] + ''
1260
+ }
1261
+ }
1262
+ }
1263
+ // 对分组表单进行处理
1264
+ for (let i = 0; i < this.groupJsonData.length; i++) {
1265
+ const item = this.groupJsonData[i]
1266
+ try {
1267
+ if (modifyModelData.data[item.model]) {
1268
+ this.form[item.model] = JSON.parse(modifyModelData.data[item.model])
1269
+ }
1270
+ } catch (e) {
1271
+ if (modifyModelData.data[item.model]) {
1272
+ this.form[item.model] = modifyModelData.data[item.model]
1273
+ }
1274
+ }
1275
+ }
1276
+ // 追加版本号信息
1277
+ for (const item of this.versionJsonData) {
1278
+ if (!modifyModelData.data[item.model]) {
1279
+ this.form[item.model] = 0
1280
+ } else {
1281
+ this.form[item.model] = modifyModelData.data[item.model] + ''
1282
+ }
1283
+ }
1284
+ },
1285
+ setForm(obj) {
1286
+ this.form = Object.assign(this.form, obj)
1287
+ // 给子表赋外键条件
1288
+ if (this.childFormData.length > 0) {
1289
+ for (const item of this.childFormData) {
1290
+ const child = this.$refs[item.model]
1291
+ // 获取子表别名,以便在条件上添加别名
1292
+ const alias = child.realQueryConfig.tableAliasName
1293
+ // 有主键,且主键有值,添加主键条件
1294
+ if (this.primaryKey && this.form[this.primaryKey]) {
1295
+ const foreignKey = item.foreignKey
1296
+ const fixedQueryForm = {
1297
+ [alias + '_' + foreignKey]: this.form[this.primaryKey]
1298
+ }
1299
+ if (!child.fixedQueryForm) {
1300
+ child.fixedQueryForm = fixedQueryForm
1301
+ } else {
1302
+ Object.assign(child.fixedQueryForm, fixedQueryForm)
1303
+ }
1304
+ child.refreshTable()
1305
+ }
1306
+ }
1307
+ }
1308
+ },
1309
+ setFormWithKey(obj) {
1310
+ setDataByRealKey(this.form, obj)
1311
+ },
1312
+ setFormWithNoKey(obj) {
1313
+ setDataByRealKey(this.form, obj)
1314
+ },
1315
+ emitFunc(func, data, value) {
1316
+ this.$emit(func, data, value)
1317
+ this.$emit('x-form-item-emit-func', func, data, value)
1318
+ },
1319
+ // 直接转发事件的函数
1320
+ emitEvent(event, ...args) {
1321
+ this.$emit(event, ...args)
1322
+ },
1323
+ close() {
1324
+ this.loaded = false
1325
+ },
1326
+ /**
1327
+ * 行选择事件
1328
+ * @param row 选中行集合
1329
+ * @param attr 表单项属性
1330
+ */
1331
+ async rowChoose(row, attr, callback) {
1332
+ // 如果配置了自定义函数
1333
+ if (attr.dataChangeFunc) {
1334
+ await executeStrFunction(attr.dataChangeFunc, [
1335
+ this.form,
1336
+ this.setForm,
1337
+ {
1338
+ ...attr,
1339
+ selectRows: row
1340
+ },
1341
+ util,
1342
+ this.mode,
1343
+ runLogic,
1344
+ getConfigByNameAsync
1345
+ ])
1346
+ } else {
1347
+ // 默认填充选中行数据到当前表单
1348
+ setDataByRealKey(this.form, row[0])
1349
+ }
1350
+ if (callback) {
1351
+ callback()
1352
+ }
1353
+ },
1354
+ /**
1355
+ * 刷新表单项展示函数
1356
+ * 通过修改 form 并添加时间戳来触发 watch form,从而自动触发 showFormItemFunc 重新计算
1357
+ * 他组数据变化时由 XFormGroup.handleFormDataChange 调用,此时 allFormData 已更新
1358
+ */
1359
+ refreshShowFunc() {
1360
+ // 自定义校验红星:他组变化时更新 tempRequired(可访问 formGroupContext.allFormData)
1361
+ if (this.customValidateItems && this.customValidateItems.length > 0) {
1362
+ this.runCustomValidationForDisplay()
1363
+ }
1364
+ // 设置标志位,避免触发 formDataChange 导致死循环
1365
+ this.isRefreshing = true
1366
+ // 修改 form 中的时间戳字段,触发 watch form,从而自动触发 showFormItemFunc
1367
+ this.$set(this.form, '__refreshTimestamp__', Date.now())
1368
+ // 在下一个 tick 重置标志位
1369
+ this.$nextTick(() => {
1370
+ this.isRefreshing = false
1371
+ })
1372
+ }
1373
+ }
1374
+ }
1375
+ </script>
1376
+
1377
+ <style scoped lang="less">
1378
+ :deep(.ant-form-inline .ant-form-item) {
1379
+ display: block !important;
1380
+ }
1381
+
1382
+ :deep(.ant-form-item-with-help) {
1383
+ margin-bottom: 0;
1384
+ }
1385
+
1386
+ .xFormGroupTitle {
1387
+ font-weight: bold;
1388
+ color: @primary-color;
1389
+ }
1390
+
1391
+ .simple-inline-item {
1392
+ max-width: 100% !important;
1393
+ }
1394
+
1395
+ /* simple-inline模式样式 - 需要穿透 x-form-col 组件 */
1396
+ /* DOM结构: .x-form-col-wrapper > x-form-item > slot > x-form-col > .x-form-col-wrapper > .ant-form-item */
1397
+ :deep(.x-form-col-wrapper .simple-inline-item .ant-form-item),
1398
+ :deep(.simple-inline-item .x-form-col-wrapper .ant-form-item) {
1399
+ margin: 0 !important;
1400
+ padding: 2px !important;
1401
+
1402
+ > .ant-form-item-label {
1403
+ width: 0 !important;
1404
+ min-width: 0 !important;
1405
+ max-width: 0 !important;
1406
+ padding: 0 !important;
1407
+ overflow: hidden;
1408
+ display: none !important;
1409
+ }
1410
+
1411
+ > .ant-form-item-control-wrapper,
1412
+ > .ant-form-item-control {
1413
+ width: 100% !important;
1414
+ }
1415
+ }
1416
+
1417
+ /* simple-inline模式下显示label */
1418
+ :deep(.simple-inline-item.show-label > .x-form-col-wrapper > .ant-form-item),
1419
+ :deep(.simple-inline-item.show-label .x-form-col-wrapper .ant-form-item) {
1420
+ > .ant-form-item-label {
1421
+ width: auto !important;
1422
+ min-width: 60px !important;
1423
+ max-width: 120px !important;
1424
+ padding: 0 8px 0 0 !important;
1425
+ overflow: visible;
1426
+ display: block !important;
1427
+ }
1428
+ }
1429
+
1430
+ /* 表单底部按钮区域样式 */
1431
+ .form-footer-row {
1432
+ margin-top: 16px;
1433
+
1434
+ :deep(.ant-btn) {
1435
+ margin-left: 8px;
1436
+
1437
+ &:first-child {
1438
+ margin-left: 0;
1439
+ }
1440
+ }
1441
+ }
1442
+ </style>