af-mobile-client-vue3 1.3.16 → 1.3.18

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 (274) hide show
  1. package/.cursorrules +60 -60
  2. package/.editorconfig +9 -9
  3. package/.env +10 -10
  4. package/.env.development +1 -1
  5. package/.env.production +1 -1
  6. package/.node-version +1 -1
  7. package/.vscode/extensions.json +12 -12
  8. package/.vscode/settings.json +66 -66
  9. package/CLAUDE.md +189 -189
  10. package/README.md +182 -182
  11. package/af-example-mobile-vue-web.iml +9 -9
  12. package/build/vite/index.ts +98 -98
  13. package/build/vite/optimize.ts +34 -34
  14. package/build/vite/vconsole.ts +47 -47
  15. package/commitlint.config.ts +32 -32
  16. package/compress.js +36 -36
  17. package/eslint.config.ts +30 -30
  18. package/index.html +23 -23
  19. package/mock/data.ts +20 -20
  20. package/mock/index.ts +7 -7
  21. package/mock/modules/prose.mock.ts +13 -13
  22. package/mock/modules/user.mock.ts +152 -152
  23. package/mock/util.ts +19 -19
  24. package/netlify.toml +12 -12
  25. package/package.json +114 -114
  26. package/postcss.config.ts +27 -27
  27. package/public/favicon.svg +4 -4
  28. package/public/safari-pinned-tab.svg +4 -4
  29. package/scripts/verifyCommit.js +19 -19
  30. package/src/App.vue +79 -79
  31. package/src/api/mock/index.ts +30 -30
  32. package/src/api/user/index.ts +40 -40
  33. package/src/assets/img/user/login/background-shadow-1.svg +20 -20
  34. package/src/assets/img/user/login/logo-background.svg +20 -20
  35. package/src/bootstrap.ts +26 -26
  36. package/src/components/core/BeautifulLoading/index.vue +52 -52
  37. package/src/components/core/ImageUploader/index.vue +244 -244
  38. package/src/components/core/NavBar/index.vue +53 -53
  39. package/src/components/core/Tabbar/index.vue +32 -32
  40. package/src/components/core/Uploader/index.vue +124 -124
  41. package/src/components/core/XGridDropOption/index.vue +154 -154
  42. package/src/components/core/XMultiSelect/index.vue +183 -183
  43. package/src/components/core/XSelect/index.vue +149 -149
  44. package/src/components/data/CardContainer/CardContainer.vue +118 -118
  45. package/src/components/data/CardContainer/CardHeader.vue +99 -99
  46. package/src/components/data/InfoDisplay/index.vue +132 -132
  47. package/src/components/data/UserDetail/api.ts +24 -24
  48. package/src/components/data/UserDetail/index.vue +620 -620
  49. package/src/components/data/UserDetail/recordEntries.ts +159 -159
  50. package/src/components/data/UserDetail/types.ts +26 -26
  51. package/src/components/data/XBadge/index.vue +82 -82
  52. package/src/components/data/XCellDetail/index.vue +105 -105
  53. package/src/components/data/XCellList/XCellList.md +313 -313
  54. package/src/components/data/XCellList/index.vue +1110 -1075
  55. package/src/components/data/XCellListFilter/QrScanner/index.vue +207 -207
  56. package/src/components/data/XCellListFilter/QrScanner/startScanAnimation.ts +53 -53
  57. package/src/components/data/XCellListFilter/VpnRecognition/index.vue +119 -119
  58. package/src/components/data/XCellListFilter/index.vue +705 -705
  59. package/src/components/data/XForm/index.vue +659 -659
  60. package/src/components/data/XFormGroup/doc/DeviceForm.vue +122 -122
  61. package/src/components/data/XFormGroup/doc/FormGroupDemo.vue +56 -56
  62. package/src/components/data/XFormGroup/doc/README.md +286 -286
  63. package/src/components/data/XFormGroup/doc/UserForm.vue +102 -102
  64. package/src/components/data/XFormGroup/index.vue +240 -240
  65. package/src/components/data/XFormItem/index.vue +1310 -1310
  66. package/src/components/data/XOlMap/README.md +227 -227
  67. package/src/components/data/XOlMap/XLocationPicker/index.vue +226 -226
  68. package/src/components/data/XOlMap/index.vue +1490 -1490
  69. package/src/components/data/XOlMap/types.ts +149 -149
  70. package/src/components/data/XOlMap/utils/wgs84ToGcj02.js +154 -154
  71. package/src/components/data/XReportForm/DateTimeSecondsPicker.vue +208 -208
  72. package/src/components/data/XReportForm/XReportFormJsonRender.vue +220 -220
  73. package/src/components/data/XReportForm/index.vue +1393 -1393
  74. package/src/components/data/XReportGrid/XAddReport/XAddReport.vue +198 -198
  75. package/src/components/data/XReportGrid/XAddReport/index.js +3 -3
  76. package/src/components/data/XReportGrid/XAddReport/index.md +53 -53
  77. package/src/components/data/XReportGrid/XAddReport/index.ts +10 -10
  78. package/src/components/data/XReportGrid/XReport.vue +960 -960
  79. package/src/components/data/XReportGrid/XReportDemo.vue +33 -33
  80. package/src/components/data/XReportGrid/XReportDesign.vue +597 -597
  81. package/src/components/data/XReportGrid/XReportDrawer/XReportDrawer.vue +148 -148
  82. package/src/components/data/XReportGrid/XReportDrawer/index.js +3 -3
  83. package/src/components/data/XReportGrid/XReportDrawer/index.ts +10 -10
  84. package/src/components/data/XReportGrid/XReportJsonRender.vue +399 -399
  85. package/src/components/data/XReportGrid/XReportTrGroup.vue +592 -592
  86. package/src/components/data/XReportGrid/index.md +46 -46
  87. package/src/components/data/XReportGrid/print.js +184 -184
  88. package/src/components/data/XSignature/index.vue +284 -284
  89. package/src/components/data/XTag/index.vue +10 -10
  90. package/src/components/layout/NormalDataLayout/index.vue +69 -69
  91. package/src/components/layout/TabBarLayout/index.vue +40 -40
  92. package/src/composables/dark.ts +5 -5
  93. package/src/config/routes.ts +9 -9
  94. package/src/constants/index.ts +2 -2
  95. package/src/enums/requestEnum.ts +25 -25
  96. package/src/expression/ExpressionRunner.ts +28 -28
  97. package/src/expression/TestExpression.ts +510 -510
  98. package/src/expression/core/Delegate.ts +116 -116
  99. package/src/expression/core/Expression.ts +1359 -1359
  100. package/src/expression/core/Program.ts +985 -985
  101. package/src/expression/core/Token.ts +29 -29
  102. package/src/expression/enums/ExpressionType.ts +81 -81
  103. package/src/expression/enums/TokenType.ts +11 -11
  104. package/src/expression/exception/BreakWayException.ts +2 -2
  105. package/src/expression/exception/ContinueWayException.ts +2 -2
  106. package/src/expression/exception/ExpressionException.ts +29 -29
  107. package/src/expression/exception/ReturnWayException.ts +14 -14
  108. package/src/expression/exception/ServiceException.ts +22 -22
  109. package/src/expression/instances/JSONArray.ts +52 -52
  110. package/src/expression/instances/JSONObject.ts +118 -118
  111. package/src/expression/instances/LogicConsole.ts +31 -31
  112. package/src/font-style/font.css +4 -4
  113. package/src/hooks/useBoolean.ts +26 -26
  114. package/src/hooks/useCommon.ts +9 -9
  115. package/src/hooks/useLoading.ts +16 -16
  116. package/src/hooks/useLogin.ts +97 -97
  117. package/src/icons/svg/check-in.svg +32 -32
  118. package/src/icons/svg/dark.svg +4 -4
  119. package/src/icons/svg/github.svg +4 -4
  120. package/src/icons/svg/light.svg +4 -4
  121. package/src/icons/svg/link.svg +4 -4
  122. package/src/icons/svgo.yml +22 -22
  123. package/src/layout/GridView/index.vue +16 -16
  124. package/src/layout/PageLayout.vue +9 -9
  125. package/src/layout/SingleLayout.vue +9 -9
  126. package/src/locales/en-US.json +128 -128
  127. package/src/locales/zh-CN.json +128 -128
  128. package/src/logic/LogicRunner.ts +67 -67
  129. package/src/logic/TestLogic.ts +13 -13
  130. package/src/logic/plugins/common/DateTools.ts +35 -35
  131. package/src/logic/plugins/common/VueTools.ts +30 -30
  132. package/src/logic/plugins/index.ts +7 -7
  133. package/src/main.ts +44 -44
  134. package/src/plugins/AppData.ts +38 -38
  135. package/src/plugins/GetLoginInfoService.ts +10 -10
  136. package/src/plugins/collectIcons.ts +10 -10
  137. package/src/plugins/index.ts +11 -11
  138. package/src/router/README.md +8 -8
  139. package/src/router/guards.ts +59 -59
  140. package/src/router/index.ts +35 -35
  141. package/src/router/invoiceRoutes.ts +33 -33
  142. package/src/router/routes.ts +353 -341
  143. package/src/router/types.ts +7 -7
  144. package/src/services/api/Login.ts +6 -6
  145. package/src/services/api/common.ts +109 -109
  146. package/src/services/api/index.ts +7 -7
  147. package/src/services/api/manage.ts +8 -8
  148. package/src/services/api/search.ts +16 -16
  149. package/src/services/api/user.ts +17 -17
  150. package/src/services/restTools.ts +56 -56
  151. package/src/services/v3Api.ts +147 -147
  152. package/src/stores/index.ts +11 -11
  153. package/src/stores/modules/counter.ts +19 -19
  154. package/src/stores/modules/routeCache.ts +23 -23
  155. package/src/stores/modules/setting.ts +77 -76
  156. package/src/stores/modules/user.ts +235 -235
  157. package/src/stores/mutation-type.ts +7 -7
  158. package/src/styles/app.less +36 -36
  159. package/src/styles/login.less +109 -109
  160. package/src/styles/var.less +25 -16
  161. package/src/types/env.d.ts +16 -16
  162. package/src/types/settings.ts +1 -1
  163. package/src/types/vue-router.d.ts +9 -9
  164. package/src/utils/Storage.ts +124 -124
  165. package/src/utils/authority-utils.ts +84 -84
  166. package/src/utils/common.ts +41 -41
  167. package/src/utils/crypto.ts +39 -39
  168. package/src/utils/dataUtil.ts +42 -42
  169. package/src/utils/dictUtil.ts +52 -52
  170. package/src/utils/http/index.ts +199 -199
  171. package/src/utils/i18n.ts +72 -72
  172. package/src/utils/indexedDB.ts +195 -195
  173. package/src/utils/inline-px-to-vw.ts +28 -28
  174. package/src/utils/mobileUtil.ts +34 -34
  175. package/src/utils/progress.ts +19 -19
  176. package/src/utils/routerUtil.ts +271 -271
  177. package/src/utils/runEvalFunction.ts +13 -13
  178. package/src/utils/secureStorage.ts +71 -71
  179. package/src/utils/set-page-title.ts +5 -5
  180. package/src/utils/validate.ts +6 -6
  181. package/src/utils/wechatUtil.ts +9 -9
  182. package/src/views/chat/index.vue +153 -153
  183. package/src/views/common/LoadError.vue +63 -63
  184. package/src/views/common/NotFound.vue +67 -67
  185. package/src/views/component/EvaluateRecordView/index.vue +40 -40
  186. package/src/views/component/IconifyView/index.vue +504 -504
  187. package/src/views/component/UserDetailView/UserDetailPage.vue +77 -77
  188. package/src/views/component/UserDetailView/index.vue +234 -234
  189. package/src/views/component/XCellDetailView/index.vue +217 -217
  190. package/src/views/component/XCellListView/index.vue +157 -108
  191. package/src/views/component/XFormAppraiseView/index.vue +174 -174
  192. package/src/views/component/XFormGroupView/index.vue +82 -78
  193. package/src/views/component/XFormGroupView/xformgroup222.vue +97 -0
  194. package/src/views/component/XFormView/index.vue +27 -27
  195. package/src/views/component/XOlMapView/XLocationPicker/index.vue +118 -118
  196. package/src/views/component/XOlMapView/index.vue +434 -434
  197. package/src/views/component/XOlMapView/testData.ts +64 -64
  198. package/src/views/component/XReportFormIframeView/index.vue +47 -47
  199. package/src/views/component/XReportFormView/index.vue +13 -13
  200. package/src/views/component/XReportGridView/index.vue +17 -17
  201. package/src/views/component/XRequestView/index.vue +234 -234
  202. package/src/views/component/XSignatureView/index.vue +50 -50
  203. package/src/views/component/index.vue +181 -181
  204. package/src/views/component/menu.vue +117 -117
  205. package/src/views/component/notice.vue +46 -46
  206. package/src/views/component/topNav.vue +36 -36
  207. package/src/views/invoiceShow/index.vue +61 -61
  208. package/src/views/user/login/ForgetPasswordForm.vue +94 -94
  209. package/src/views/user/login/LoginForm.vue +346 -346
  210. package/src/views/user/login/LoginTitle.vue +76 -76
  211. package/src/views/user/login/LoginWave.vue +109 -109
  212. package/src/views/user/login/index.vue +22 -22
  213. package/src/views/user/my/comm/ModifyPassword.vue +346 -346
  214. package/src/views/user/my/index.vue +340 -340
  215. package/src/views/user/register/index.vue +910 -0
  216. package/src/views/userRecords/AbnormalAlarmRecords.vue +21 -21
  217. package/src/views/userRecords/CardReplacementRecords.vue +21 -21
  218. package/src/views/userRecords/ChangeRecords.vue +19 -19
  219. package/src/views/userRecords/CommandViewRecords.vue +20 -20
  220. package/src/views/userRecords/GasCompensationRecords.vue +20 -20
  221. package/src/views/userRecords/InstrumentCollectionRecords.vue +21 -21
  222. package/src/views/userRecords/MeterRecords.vue +20 -20
  223. package/src/views/userRecords/OperateRecords.vue +51 -51
  224. package/src/views/userRecords/OtherChargeRecords.vue +19 -19
  225. package/src/views/userRecords/PaymentRecords.vue +28 -28
  226. package/src/views/userRecords/PriceAdjustmentRecords.vue +19 -19
  227. package/src/views/userRecords/ReplacementRecords.vue +19 -19
  228. package/src/views/userRecords/SafetyRecords.vue +19 -19
  229. package/src/views/userRecords/TransactionRecords.vue +21 -21
  230. package/src/views/userRecords/TransferRecords.vue +19 -19
  231. package/src/views/userRecords/operateRecordDetail/index.vue +316 -316
  232. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AddUserDetail.vue +124 -124
  233. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AdvanceDeliveryDetail.vue +88 -88
  234. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AutoAccountsCancelDetail.vue +205 -205
  235. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AutoAccountsDetail.vue +192 -192
  236. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/BankDkDetail.vue +192 -192
  237. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/BankPayDetail.vue +192 -192
  238. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/BlacklistDetail.vue +153 -153
  239. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CancellationDetail.vue +101 -101
  240. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CardMeterCenterCancelDetail.vue +127 -127
  241. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CardMeterCenterDetail.vue +153 -153
  242. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CardOverUserDetail.vue +153 -153
  243. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ChangeMeterCancelDetail.vue +166 -166
  244. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ChangeMeterDetail.vue +205 -205
  245. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/DisableManageDetail.vue +127 -127
  246. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/EnableManageDetail.vue +114 -114
  247. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/FaZheChangeDetail.vue +124 -124
  248. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/FeeDeductionDetail.vue +153 -153
  249. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/GasPriceChangeDetail.vue +126 -126
  250. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/InputtorChangeDetail.vue +126 -126
  251. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/IotMeterCenterCancelDetail.vue +114 -114
  252. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/IotMeterCenterDetail.vue +127 -127
  253. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/IotOpenDetail.vue +88 -88
  254. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/MachineCardDetail.vue +101 -101
  255. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/MachineMeterCenterCancelDetail.vue +218 -218
  256. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/MachineMeterCenterDetail.vue +153 -153
  257. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OffGasAddGasDetail.vue +140 -140
  258. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OtherChargeCancelDetail.vue +127 -127
  259. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OtherChargeDetail.vue +114 -114
  260. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OverUserChangeDetail.vue +127 -127
  261. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ReBillDetail.vue +127 -127
  262. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/RefundDetail.vue +114 -114
  263. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ReplaceCardManageCancelDetail.vue +127 -127
  264. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ReplaceCardManageDetail.vue +114 -114
  265. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/SaleCardGasDetail.vue +140 -140
  266. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/TransferManageCancelDetail.vue +152 -152
  267. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/TransferManageDetail.vue +178 -178
  268. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/UserChangeDetail.vue +123 -123
  269. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/WechatPayDetail.vue +192 -192
  270. package/src/views/userRecords/types.ts +66 -66
  271. package/tsconfig.json +39 -39
  272. package/uno.config.ts +82 -82
  273. package/vite.config.ts +118 -118
  274. package/.claude/settings.local.json +0 -8
@@ -1,659 +1,659 @@
1
- <script setup lang="ts">
2
- import type { FormInstance } from 'vant'
3
- import XFormItem from '@af-mobile-client-vue3/components/data/XFormItem/index.vue'
4
- import XOlMap from '@af-mobile-client-vue3/components/data/XOlMap/index.vue'
5
- import { formatDate } from '@af-mobile-client-vue3/hooks/useCommon'
6
- import { addOrModifyEntity, getConfigByName, runLogic } from '@af-mobile-client-vue3/services/api/common'
7
- import { useUserStore } from '@af-mobile-client-vue3/stores'
8
-
9
- import {
10
- showFailToast,
11
- showSuccessToast,
12
- Button as VanButton,
13
- CellGroup as VanCellGroup,
14
- Form as VanForm,
15
- } from 'vant'
16
- import { computed, defineEmits, defineProps, inject, nextTick, onBeforeMount, reactive, ref, watch } from 'vue'
17
-
18
- interface FormItem {
19
- addOrEdit: string
20
- isOnlyAddOrEdit?: boolean
21
- type?: string
22
- model?: string
23
- name?: string
24
- rule?: {
25
- type: string
26
- }
27
- }
28
-
29
- interface SilenceAddFormItem extends FormItem {
30
- silencePurpose: string
31
- silenceSource?: string
32
- }
33
-
34
- interface GroupFormItems {
35
- btnName?: string
36
- formJson: any[] // 根据实际类型调整
37
- showSubmitBtn?: boolean
38
- groupName?: string
39
- tableName?: string
40
- paramLogicName?: string
41
- isGroupForm?: boolean
42
- }
43
-
44
- interface InitParams {
45
- formItems?: GroupFormItems
46
- formData?: object
47
- getDataParams?: object
48
- }
49
-
50
- const props = withDefaults(defineProps<{
51
- configName?: string
52
- groupTitle?: string
53
- groupFormItems?: GroupFormItems
54
- serviceName?: string
55
- formData?: object
56
- formName?: string
57
- mode?: string
58
- submitButton?: boolean
59
- isHandleFormKey?: boolean
60
- paramLogicNameParam?: any
61
- isGroupForm?: boolean
62
- }>(), {
63
- configName: undefined,
64
- groupTitle: undefined,
65
- groupFormItems: null,
66
- serviceName: undefined,
67
- formData: null,
68
- formName: 'default',
69
- mode: '查询',
70
- submitButton: true,
71
- isHandleFormKey: true,
72
- paramLogicNameParam: {},
73
- isGroupForm: false,
74
- })
75
-
76
- const emits = defineEmits(['onSubmit', 'xFormItemEmitFunc'])
77
- const userStore = useUserStore()
78
-
79
- // inject
80
- const formDataChange = inject('formDataChange', null)
81
-
82
- // 核心状态
83
- const xFormRef = ref<FormInstance>()
84
- const loaded = ref(false)
85
- const form = ref({})
86
- const rules = reactive({})
87
- const myGetDataParams = ref({})
88
-
89
- // 配置相关状态
90
- const formConfig = ref<GroupFormItems | null>(null)
91
- const formGroupName = ref<string>('default')
92
- const myServiceName = ref('')
93
- const tableName = ref('')
94
-
95
- // 计算属性
96
- const realJsonData = computed(() => {
97
- const sourceFormItems = formConfig.value?.formJson
98
- if (!sourceFormItems)
99
- return []
100
-
101
- return sourceFormItems.filter((item) => {
102
- return item.addOrEdit !== 'no'
103
- })
104
- })
105
-
106
- // 过滤出用于静默新增场景的表单项
107
- const silenceAddJsonData = computed(() => {
108
- return realJsonData.value.filter((item) => {
109
- return item.addOrEdit === 'silenceAdd'
110
- }) as SilenceAddFormItem[]
111
- })
112
-
113
- const resolvedSubmitButton = computed(() => {
114
- if (props.configName && formConfig.value?.showSubmitBtn !== undefined) {
115
- return formConfig.value.showSubmitBtn
116
- }
117
- return props.submitButton
118
- })
119
-
120
- // 地图预览相关
121
- const previewMapRef = ref(null)
122
- const previewLonLatKey = computed(() => {
123
- const item = realJsonData.value.find(i => i.type === 'addressSearch')
124
- return item ? `${item.model}_lon_lat` : null
125
- })
126
-
127
- const previewMapPoint = computed(() => {
128
- const val = form.value[previewLonLatKey.value]
129
- if (val && typeof val === 'string' && val.includes(',')) {
130
- const [lon, lat] = val.split(',').map(Number)
131
- if (!Number.isNaN(lon) && !Number.isNaN(lat)) {
132
- return [lon, lat]
133
- }
134
- }
135
- return null
136
- })
137
-
138
- // 地图初始化逻辑
139
- const previewMapInited = ref(false)
140
- watch(previewMapPoint, async (val) => {
141
- await nextTick()
142
- if (val && previewMapRef.value) {
143
- if (!previewMapInited.value) {
144
- await previewMapRef.value.init({ center: val, zoom: 16, isPreview: true })
145
- previewMapInited.value = true
146
- }
147
- previewMapRef.value.setCenter(val, true)
148
- previewMapRef.value.updateLocationMarker(val)
149
- }
150
- }, { immediate: true })
151
-
152
- // 统一的初始化函数 - 仅在组件挂载时调用
153
- async function initializeForm() {
154
- loaded.value = false
155
-
156
- try {
157
- if (props.configName) {
158
- await loadFormConfig()
159
- }
160
- else if (props.groupFormItems) {
161
- await initWithGroupFormItems()
162
- }
163
- else {
164
- console.warn('未提供配置名称或表单配置,等待 init 函数调用')
165
- resetForm()
166
- }
167
- }
168
- catch (error) {
169
- console.error('表单初始化失败:', error)
170
- resetForm()
171
- }
172
- finally {
173
- loaded.value = true
174
- }
175
- }
176
-
177
- // 加载配置的方式初始化
178
- async function loadFormConfig(): Promise<void> {
179
- return new Promise((resolve, reject) => {
180
- getConfigByName(props.configName!, (result) => {
181
- if (result) {
182
- setupFormConfig(result)
183
- resolve()
184
- }
185
- else {
186
- console.warn(`配置 '${props.configName}' 不存在`)
187
- reject(new Error(`配置 '${props.configName}' 不存在`))
188
- }
189
- }, props.serviceName)
190
- })
191
- }
192
-
193
- // 直接使用 groupFormItems 初始化
194
- async function initWithGroupFormItems() {
195
- if (!props.groupFormItems) {
196
- throw new Error('groupFormItems 不能为空')
197
- }
198
-
199
- setupFormConfig(props.groupFormItems)
200
-
201
- // 设置表名
202
- if (props.groupFormItems.tableName) {
203
- tableName.value = props.groupFormItems.tableName.split(' ')[0] || ''
204
- }
205
- }
206
-
207
- // 设置表单配置
208
- function setupFormConfig(config: GroupFormItems) {
209
- loadParamLogicNameData(config.paramLogicName)
210
- formConfig.value = config
211
- myServiceName.value = props.serviceName || ''
212
- formGroupName.value = config.groupName || 'default'
213
- form.value = props.formData || {}
214
- // 清理并重新设置验证规则
215
- setupValidationRules()
216
- }
217
-
218
- // 设置验证规则
219
- function setupValidationRules() {
220
- // 清理现有规则
221
- Object.keys(rules).forEach(key => delete rules[key])
222
-
223
- // 为每个表单项设置规则
224
- for (const item of realJsonData.value) {
225
- if (item.rule) {
226
- setFormRule(item)
227
- }
228
- }
229
- }
230
-
231
- // 设置单个表单项的验证规则
232
- function setFormRule(item: FormItem) {
233
- if (!item.model || !item.rule)
234
- return
235
-
236
- rules[item.model] = []
237
- let defaultValue: any
238
- let message: string
239
-
240
- switch (item.rule.type) {
241
- case 'number':
242
- message = '数字'
243
- defaultValue = 0
244
- break
245
- case 'integer':
246
- message = '整数'
247
- defaultValue = 0
248
- break
249
- case 'float':
250
- message = '小数'
251
- defaultValue = 0.0
252
- break
253
- case 'string':
254
- message = '字符串'
255
- defaultValue = ''
256
- break
257
- default:
258
- return
259
- }
260
-
261
- rules[item.model].push({
262
- type: item.rule.type,
263
- message: `${item.name || item.model}必须为${message}`,
264
- transform: (value: any) => {
265
- if (value && value.length !== 0) {
266
- return Number(value)
267
- }
268
- else {
269
- return defaultValue
270
- }
271
- },
272
- trigger: 'blur',
273
- })
274
- }
275
-
276
- // 重置表单状态
277
- function resetForm() {
278
- formConfig.value = null
279
- formGroupName.value = 'default'
280
- myServiceName.value = ''
281
- tableName.value = ''
282
- form.value = {}
283
- Object.keys(rules).forEach(key => delete rules[key])
284
- myGetDataParams.value = {}
285
- }
286
-
287
- // 监听 formData 变化
288
- watch(() => props.formData, (newVal) => {
289
- if (newVal) {
290
- Object.assign(form, newVal)
291
- }
292
- })
293
-
294
- watch(form, (newVal) => {
295
- if (typeof formDataChange === 'function') {
296
- formDataChange(newVal)
297
- }
298
- }, { deep: true })
299
-
300
- // 组件挂载时初始化
301
- onBeforeMount(() => {
302
- initializeForm()
303
- })
304
-
305
- // 支持函数调用方式初始化
306
- function init(params: InitParams) {
307
- const {
308
- formItems,
309
- formData = {},
310
- getDataParams = {},
311
- } = params
312
-
313
- // 重置状态
314
- loaded.value = false
315
-
316
- try {
317
- if (props.configName) {
318
- // 通过 props 中的配置名称初始化
319
- loadFormConfig()
320
- }
321
- else if (formItems) {
322
- // 直接使用传入的配置初始化
323
- setupFormConfig(formItems)
324
- form.value = formData || props.formData || {}
325
- myGetDataParams.value = getDataParams
326
-
327
- // 设置表名
328
- if (formItems.tableName) {
329
- tableName.value = formItems.tableName.split(' ')[0] || ''
330
- }
331
- }
332
- else {
333
- console.warn('init 函数调用时既没有 props.configName 也没有 formItems')
334
- resetForm()
335
- }
336
- }
337
- catch (error) {
338
- console.error('init 函数初始化失败:', error)
339
- resetForm()
340
- }
341
- finally {
342
- loaded.value = true
343
- }
344
- }
345
-
346
- function loadParamLogicNameData(paramLogicName) {
347
- // 如果有 paramLogicName,自动请求并赋值
348
- if (paramLogicName && (!props.formData || (props.formData && Object.keys(props.formData).length === 0))) {
349
- let logicParam = props?.paramLogicNameParam || {}
350
- if (typeof logicParam === 'string') {
351
- try {
352
- logicParam = JSON.parse(logicParam)
353
- }
354
- catch {
355
- logicParam = { value: logicParam }
356
- }
357
- }
358
- logicParam = {
359
- ...logicParam,
360
- currUserName: userStore.getUserInfo().name,
361
- currUserId: userStore.getUserInfo().id,
362
- orgId: userStore.getUserInfo().orgid,
363
- }
364
- runLogic(paramLogicName, logicParam || {}, props.serviceName).then((data: any) => {
365
- if (data) {
366
- form.value = data
367
- }
368
- })
369
- }
370
- }
371
-
372
- function setForm(obj: object) {
373
- Object.assign(form.value, obj)
374
- }
375
-
376
- // 获取表单字段实际值
377
- function getRealKey(key: string, mustHandleKey = false) {
378
- if (key === 'selected_id')
379
- return key
380
- if (props.isHandleFormKey || mustHandleKey) {
381
- return key.substring(key.indexOf('_') + 1)
382
- }
383
- else {
384
- return key
385
- }
386
- }
387
-
388
- async function asyncSubmit() {
389
- return new Promise((resolve, reject) => {
390
- validate().then(async () => {
391
- const cleanedForm = prepareForm()
392
- await appendSilenceAddFields(cleanedForm)
393
- const realForm = handleFormKeys(cleanedForm)
394
- resolve({
395
- realForm,
396
- mode: props.mode,
397
- serviceName: props.serviceName,
398
- currUserName: userStore.getUserInfo().name,
399
- currUserId: userStore.getUserInfo().id,
400
- orgId: userStore.getUserInfo().orgid,
401
- })
402
- }).catch((error) => {
403
- reject(error)
404
- })
405
- })
406
- }
407
-
408
- function handleFormKeys(form: any, mustHandleKey = false) {
409
- const realForm: any = {}
410
- for (const key of Object.keys(form)) {
411
- const value = form[key]
412
- const extraFormKeyTagIndex = key.indexOf('@')
413
- if (extraFormKeyTagIndex !== -1) {
414
- const extraFormKey = key.substring(0, extraFormKeyTagIndex)
415
- const realKey = key.substring(extraFormKeyTagIndex + 1)
416
- if (!realForm[extraFormKey]) {
417
- realForm[extraFormKey] = {}
418
- }
419
- realForm[extraFormKey][realKey] = value
420
- }
421
- else {
422
- const realKey = props.isHandleFormKey || mustHandleKey ? getRealKey(key, mustHandleKey) : key
423
- // 如果发生重名,不覆盖,把key的别名带上
424
- if (realForm[realKey]) {
425
- realForm[key] = value
426
- }
427
- else {
428
- realForm[realKey] = value
429
- }
430
- }
431
- }
432
- return realForm
433
- }
434
-
435
- async function appendSilenceAddFields(form: any) {
436
- if (props.mode === '新增') {
437
- for (const item of silenceAddJsonData.value) {
438
- switch (item.silencePurpose) {
439
- case 'createTime':
440
- form[item.model] = formatDate(new Date())
441
- break
442
- case 'operator':
443
- form[item.model] = userStore.getUserInfo().name
444
- break
445
- case 'operatorId':
446
- form[item.model] = userStore.getUserInfo().id
447
- break
448
- case 'orgId':
449
- form[item.model] = userStore.getUserInfo().orgid
450
- break
451
- case 'orgName':
452
- form[item.model] = userStore.getUserInfo().orgs
453
- break
454
- case 'depId':
455
- form[item.model] = userStore.getUserInfo().depids
456
- break
457
- case 'depName':
458
- form[item.model] = userStore.getUserInfo().deps
459
- break
460
- }
461
- }
462
- for (const item of silenceAddJsonData.value.filter(item => item.silencePurpose === 'customize')) {
463
- const result: any = await runLogic(item.silenceSource!, form, props.serviceName)
464
- if (result) {
465
- const keys = Object.keys(result)
466
- if (keys.length === 1 && keys[0] === 'value') {
467
- form[item.model] = result.value
468
- }
469
- else {
470
- form[item.model] = result
471
- }
472
- }
473
- else {
474
- form[item.model] = result
475
- }
476
- }
477
- }
478
- }
479
-
480
- function prepareForm() {
481
- const formObj = { ...form.value }
482
- const cleanedForm: any = {}
483
-
484
- for (const key of Object.keys(formObj)) {
485
- const value = formObj[key]
486
-
487
- // 跳过无效值
488
- if (value === null || value === undefined || value === '') {
489
- continue
490
- }
491
-
492
- // 处理数组
493
- if (Array.isArray(value)) {
494
- // 过滤掉空字符串、null、undefined
495
- const filteredArray = value.filter(item =>
496
- item !== null && item !== undefined && item !== '',
497
- )
498
- // 只有当数组不为空时才添加
499
- if (filteredArray.length > 0) {
500
- cleanedForm[key] = filteredArray
501
- }
502
- continue
503
- }
504
-
505
- // 处理对象
506
- if (typeof value === 'object' && value !== null) {
507
- // 检查对象是否为空
508
- const objectKeys = Object.keys(value)
509
- if (objectKeys.length === 0) {
510
- continue
511
- }
512
-
513
- // 递归清理对象内部
514
- const cleanedObject: any = {}
515
- let hasValidValue = false
516
-
517
- for (const objKey of objectKeys) {
518
- const objValue = value[objKey]
519
- if (objValue !== null && objValue !== undefined && objValue !== '') {
520
- cleanedObject[objKey] = objValue
521
- hasValidValue = true
522
- }
523
- }
524
-
525
- // 只有当对象有有效值时才添加
526
- if (hasValidValue) {
527
- cleanedForm[key] = cleanedObject
528
- }
529
- continue
530
- }
531
-
532
- // 处理基本类型
533
- if (value !== null && value !== undefined && value !== '') {
534
- cleanedForm[key] = value
535
- }
536
- }
537
-
538
- return cleanedForm
539
- }
540
-
541
- function getFormData() {
542
- return prepareForm()
543
- }
544
-
545
- async function onSubmit() {
546
- await validate()
547
- // 清理表单数据
548
- const cleanedForm = prepareForm()
549
- if (!props.configName && props.groupFormItems) {
550
- // 只有单表才可以成功,多表关联或者自定义sql不行
551
- await appendSilenceAddFields(cleanedForm)
552
- const realForm = handleFormKeys(cleanedForm)
553
-
554
- try {
555
- addOrModifyEntity(realForm, tableName.value, props.serviceName || import.meta.env.VITE_APP_SYSTEM_NAME).then(() => {
556
- showSuccessToast('提交成功!!')
557
- })
558
- }
559
- catch (error) {
560
- showFailToast('提交失败!!')
561
- }
562
- finally {
563
- setTimeout(() => {
564
- history.back()
565
- }, 1000)
566
- }
567
- }
568
- else {
569
- // 使用清理后的数据
570
- emits('onSubmit', cleanedForm)
571
- }
572
- }
573
- async function validate() {
574
- await xFormRef.value?.validate()
575
- }
576
- function emitFunc(func: any, data: any, value: any) {
577
- emits(func, data, value)
578
- emits('xFormItemEmitFunc', func, data, value)
579
- }
580
-
581
- defineExpose({ init, form, formGroupName, validate, asyncSubmit, setForm, getFormData })
582
- </script>
583
-
584
- <template>
585
- <VanForm v-if="loaded" ref="xFormRef" class="x-form-container" :class="{ 'use-full-height': !props.isGroupForm }" @submit="onSubmit">
586
- <div class="form-fields-scrollable">
587
- <VanCellGroup :title="groupTitle">
588
- <XFormItem
589
- v-for="(item, index) in realJsonData"
590
- :key="index"
591
- v-model="form[item.model]"
592
- :mode="props.mode"
593
- :form="form"
594
- :attr="item"
595
- :rules="rules"
596
- :service-name="myServiceName"
597
- :get-data-params="myGetDataParams"
598
- @set-form="setForm"
599
- @x-form-item-emit-func="emitFunc"
600
- />
601
- <slot name="extraFormItem" />
602
- </VanCellGroup>
603
- <slot name="extraCellGroup" />
604
- <!-- 地图预览,统一放在表单项下方,只在有经纬度时显示 -->
605
- <div v-if="previewMapPoint" style="height: 200px; margin-top: 12px; position: relative;">
606
- <XOlMap
607
- ref="previewMapRef"
608
- :center="previewMapPoint"
609
- :style="{ height: '100%' }"
610
- />
611
- <div class="map-mask" />
612
- </div>
613
- </div>
614
- <div v-if="resolvedSubmitButton && props.mode !== '预览'" class="form-footer-fixed">
615
- <VanButton class="action-btn" round block type="primary" native-type="submit">
616
- {{ props.groupFormItems?.btnName ? props.groupFormItems.btnName : '提交' }}
617
- </VanButton>
618
- <slot />
619
- </div>
620
- <slot name="extraActionSpace" />
621
- </VanForm>
622
- </template>
623
-
624
- <style scoped>
625
- .use-full-height {
626
- height: calc(100vh - var(--van-nav-bar-height) - 20px);
627
- }
628
-
629
- .x-form-container {
630
- display: flex;
631
- flex-direction: column;
632
- max-height: 100vh;
633
- overflow: hidden;
634
- }
635
-
636
- .form-fields-scrollable {
637
- flex: 1;
638
- overflow-y: auto;
639
- min-height: 0;
640
- }
641
-
642
- .form-footer-fixed {
643
- margin: 16px;
644
- flex-shrink: 0;
645
- .action-btn {
646
- border-radius: 10px;
647
- }
648
- }
649
- .map-mask {
650
- position: absolute;
651
- left: 0;
652
- top: 0;
653
- width: 100%;
654
- height: 100%;
655
- pointer-events: all;
656
- background: transparent;
657
- z-index: 10;
658
- }
659
- </style>
1
+ <script setup lang="ts">
2
+ import type { FormInstance } from 'vant'
3
+ import XFormItem from '@af-mobile-client-vue3/components/data/XFormItem/index.vue'
4
+ import XOlMap from '@af-mobile-client-vue3/components/data/XOlMap/index.vue'
5
+ import { formatDate } from '@af-mobile-client-vue3/hooks/useCommon'
6
+ import { addOrModifyEntity, getConfigByName, runLogic } from '@af-mobile-client-vue3/services/api/common'
7
+ import { useUserStore } from '@af-mobile-client-vue3/stores'
8
+
9
+ import {
10
+ showFailToast,
11
+ showSuccessToast,
12
+ Button as VanButton,
13
+ CellGroup as VanCellGroup,
14
+ Form as VanForm,
15
+ } from 'vant'
16
+ import { computed, defineEmits, defineProps, inject, nextTick, onBeforeMount, reactive, ref, watch } from 'vue'
17
+
18
+ interface FormItem {
19
+ addOrEdit: string
20
+ isOnlyAddOrEdit?: boolean
21
+ type?: string
22
+ model?: string
23
+ name?: string
24
+ rule?: {
25
+ type: string
26
+ }
27
+ }
28
+
29
+ interface SilenceAddFormItem extends FormItem {
30
+ silencePurpose: string
31
+ silenceSource?: string
32
+ }
33
+
34
+ interface GroupFormItems {
35
+ btnName?: string
36
+ formJson: any[] // 根据实际类型调整
37
+ showSubmitBtn?: boolean
38
+ groupName?: string
39
+ tableName?: string
40
+ paramLogicName?: string
41
+ isGroupForm?: boolean
42
+ }
43
+
44
+ interface InitParams {
45
+ formItems?: GroupFormItems
46
+ formData?: object
47
+ getDataParams?: object
48
+ }
49
+
50
+ const props = withDefaults(defineProps<{
51
+ configName?: string
52
+ groupTitle?: string
53
+ groupFormItems?: GroupFormItems
54
+ serviceName?: string
55
+ formData?: object
56
+ formName?: string
57
+ mode?: string
58
+ submitButton?: boolean
59
+ isHandleFormKey?: boolean
60
+ paramLogicNameParam?: any
61
+ isGroupForm?: boolean
62
+ }>(), {
63
+ configName: undefined,
64
+ groupTitle: undefined,
65
+ groupFormItems: null,
66
+ serviceName: undefined,
67
+ formData: null,
68
+ formName: 'default',
69
+ mode: '查询',
70
+ submitButton: true,
71
+ isHandleFormKey: true,
72
+ paramLogicNameParam: {},
73
+ isGroupForm: false,
74
+ })
75
+
76
+ const emits = defineEmits(['onSubmit', 'xFormItemEmitFunc'])
77
+ const userStore = useUserStore()
78
+
79
+ // inject
80
+ const formDataChange = inject('formDataChange', null)
81
+
82
+ // 核心状态
83
+ const xFormRef = ref<FormInstance>()
84
+ const loaded = ref(false)
85
+ const form = ref({})
86
+ const rules = reactive({})
87
+ const myGetDataParams = ref({})
88
+
89
+ // 配置相关状态
90
+ const formConfig = ref<GroupFormItems | null>(null)
91
+ const formGroupName = ref<string>('default')
92
+ const myServiceName = ref('')
93
+ const tableName = ref('')
94
+
95
+ // 计算属性
96
+ const realJsonData = computed(() => {
97
+ const sourceFormItems = formConfig.value?.formJson
98
+ if (!sourceFormItems)
99
+ return []
100
+
101
+ return sourceFormItems.filter((item) => {
102
+ return item.addOrEdit !== 'no'
103
+ })
104
+ })
105
+
106
+ // 过滤出用于静默新增场景的表单项
107
+ const silenceAddJsonData = computed(() => {
108
+ return realJsonData.value.filter((item) => {
109
+ return item.addOrEdit === 'silenceAdd'
110
+ }) as SilenceAddFormItem[]
111
+ })
112
+
113
+ const resolvedSubmitButton = computed(() => {
114
+ if (props.configName && formConfig.value?.showSubmitBtn !== undefined) {
115
+ return formConfig.value.showSubmitBtn
116
+ }
117
+ return props.submitButton
118
+ })
119
+
120
+ // 地图预览相关
121
+ const previewMapRef = ref(null)
122
+ const previewLonLatKey = computed(() => {
123
+ const item = realJsonData.value.find(i => i.type === 'addressSearch')
124
+ return item ? `${item.model}_lon_lat` : null
125
+ })
126
+
127
+ const previewMapPoint = computed(() => {
128
+ const val = form.value[previewLonLatKey.value]
129
+ if (val && typeof val === 'string' && val.includes(',')) {
130
+ const [lon, lat] = val.split(',').map(Number)
131
+ if (!Number.isNaN(lon) && !Number.isNaN(lat)) {
132
+ return [lon, lat]
133
+ }
134
+ }
135
+ return null
136
+ })
137
+
138
+ // 地图初始化逻辑
139
+ const previewMapInited = ref(false)
140
+ watch(previewMapPoint, async (val) => {
141
+ await nextTick()
142
+ if (val && previewMapRef.value) {
143
+ if (!previewMapInited.value) {
144
+ await previewMapRef.value.init({ center: val, zoom: 16, isPreview: true })
145
+ previewMapInited.value = true
146
+ }
147
+ previewMapRef.value.setCenter(val, true)
148
+ previewMapRef.value.updateLocationMarker(val)
149
+ }
150
+ }, { immediate: true })
151
+
152
+ // 统一的初始化函数 - 仅在组件挂载时调用
153
+ async function initializeForm() {
154
+ loaded.value = false
155
+
156
+ try {
157
+ if (props.configName) {
158
+ await loadFormConfig()
159
+ }
160
+ else if (props.groupFormItems) {
161
+ await initWithGroupFormItems()
162
+ }
163
+ else {
164
+ console.warn('未提供配置名称或表单配置,等待 init 函数调用')
165
+ resetForm()
166
+ }
167
+ }
168
+ catch (error) {
169
+ console.error('表单初始化失败:', error)
170
+ resetForm()
171
+ }
172
+ finally {
173
+ loaded.value = true
174
+ }
175
+ }
176
+
177
+ // 加载配置的方式初始化
178
+ async function loadFormConfig(): Promise<void> {
179
+ return new Promise((resolve, reject) => {
180
+ getConfigByName(props.configName!, (result) => {
181
+ if (result) {
182
+ setupFormConfig(result)
183
+ resolve()
184
+ }
185
+ else {
186
+ console.warn(`配置 '${props.configName}' 不存在`)
187
+ reject(new Error(`配置 '${props.configName}' 不存在`))
188
+ }
189
+ }, props.serviceName)
190
+ })
191
+ }
192
+
193
+ // 直接使用 groupFormItems 初始化
194
+ async function initWithGroupFormItems() {
195
+ if (!props.groupFormItems) {
196
+ throw new Error('groupFormItems 不能为空')
197
+ }
198
+
199
+ setupFormConfig(props.groupFormItems)
200
+
201
+ // 设置表名
202
+ if (props.groupFormItems.tableName) {
203
+ tableName.value = props.groupFormItems.tableName.split(' ')[0] || ''
204
+ }
205
+ }
206
+
207
+ // 设置表单配置
208
+ function setupFormConfig(config: GroupFormItems) {
209
+ loadParamLogicNameData(config.paramLogicName)
210
+ formConfig.value = config
211
+ myServiceName.value = props.serviceName || ''
212
+ formGroupName.value = config.groupName || 'default'
213
+ form.value = props.formData || {}
214
+ // 清理并重新设置验证规则
215
+ setupValidationRules()
216
+ }
217
+
218
+ // 设置验证规则
219
+ function setupValidationRules() {
220
+ // 清理现有规则
221
+ Object.keys(rules).forEach(key => delete rules[key])
222
+
223
+ // 为每个表单项设置规则
224
+ for (const item of realJsonData.value) {
225
+ if (item.rule) {
226
+ setFormRule(item)
227
+ }
228
+ }
229
+ }
230
+
231
+ // 设置单个表单项的验证规则
232
+ function setFormRule(item: FormItem) {
233
+ if (!item.model || !item.rule)
234
+ return
235
+
236
+ rules[item.model] = []
237
+ let defaultValue: any
238
+ let message: string
239
+
240
+ switch (item.rule.type) {
241
+ case 'number':
242
+ message = '数字'
243
+ defaultValue = 0
244
+ break
245
+ case 'integer':
246
+ message = '整数'
247
+ defaultValue = 0
248
+ break
249
+ case 'float':
250
+ message = '小数'
251
+ defaultValue = 0.0
252
+ break
253
+ case 'string':
254
+ message = '字符串'
255
+ defaultValue = ''
256
+ break
257
+ default:
258
+ return
259
+ }
260
+
261
+ rules[item.model].push({
262
+ type: item.rule.type,
263
+ message: `${item.name || item.model}必须为${message}`,
264
+ transform: (value: any) => {
265
+ if (value && value.length !== 0) {
266
+ return Number(value)
267
+ }
268
+ else {
269
+ return defaultValue
270
+ }
271
+ },
272
+ trigger: 'blur',
273
+ })
274
+ }
275
+
276
+ // 重置表单状态
277
+ function resetForm() {
278
+ formConfig.value = null
279
+ formGroupName.value = 'default'
280
+ myServiceName.value = ''
281
+ tableName.value = ''
282
+ form.value = {}
283
+ Object.keys(rules).forEach(key => delete rules[key])
284
+ myGetDataParams.value = {}
285
+ }
286
+
287
+ // 监听 formData 变化
288
+ watch(() => props.formData, (newVal) => {
289
+ if (newVal) {
290
+ Object.assign(form, newVal)
291
+ }
292
+ })
293
+
294
+ watch(form, (newVal) => {
295
+ if (typeof formDataChange === 'function') {
296
+ formDataChange(newVal)
297
+ }
298
+ }, { deep: true })
299
+
300
+ // 组件挂载时初始化
301
+ onBeforeMount(() => {
302
+ initializeForm()
303
+ })
304
+
305
+ // 支持函数调用方式初始化
306
+ function init(params: InitParams) {
307
+ const {
308
+ formItems,
309
+ formData = {},
310
+ getDataParams = {},
311
+ } = params
312
+
313
+ // 重置状态
314
+ loaded.value = false
315
+
316
+ try {
317
+ if (props.configName) {
318
+ // 通过 props 中的配置名称初始化
319
+ loadFormConfig()
320
+ }
321
+ else if (formItems) {
322
+ // 直接使用传入的配置初始化
323
+ setupFormConfig(formItems)
324
+ form.value = formData || props.formData || {}
325
+ myGetDataParams.value = getDataParams
326
+
327
+ // 设置表名
328
+ if (formItems.tableName) {
329
+ tableName.value = formItems.tableName.split(' ')[0] || ''
330
+ }
331
+ }
332
+ else {
333
+ console.warn('init 函数调用时既没有 props.configName 也没有 formItems')
334
+ resetForm()
335
+ }
336
+ }
337
+ catch (error) {
338
+ console.error('init 函数初始化失败:', error)
339
+ resetForm()
340
+ }
341
+ finally {
342
+ loaded.value = true
343
+ }
344
+ }
345
+
346
+ function loadParamLogicNameData(paramLogicName) {
347
+ // 如果有 paramLogicName,自动请求并赋值
348
+ if (paramLogicName && (!props.formData || (props.formData && Object.keys(props.formData).length === 0))) {
349
+ let logicParam = props?.paramLogicNameParam || {}
350
+ if (typeof logicParam === 'string') {
351
+ try {
352
+ logicParam = JSON.parse(logicParam)
353
+ }
354
+ catch {
355
+ logicParam = { value: logicParam }
356
+ }
357
+ }
358
+ logicParam = {
359
+ ...logicParam,
360
+ currUserName: userStore.getUserInfo().name,
361
+ currUserId: userStore.getUserInfo().id,
362
+ orgId: userStore.getUserInfo().orgid,
363
+ }
364
+ runLogic(paramLogicName, logicParam || {}, props.serviceName).then((data: any) => {
365
+ if (data) {
366
+ form.value = data
367
+ }
368
+ })
369
+ }
370
+ }
371
+
372
+ function setForm(obj: object) {
373
+ Object.assign(form.value, obj)
374
+ }
375
+
376
+ // 获取表单字段实际值
377
+ function getRealKey(key: string, mustHandleKey = false) {
378
+ if (key === 'selected_id')
379
+ return key
380
+ if (props.isHandleFormKey || mustHandleKey) {
381
+ return key.substring(key.indexOf('_') + 1)
382
+ }
383
+ else {
384
+ return key
385
+ }
386
+ }
387
+
388
+ async function asyncSubmit() {
389
+ return new Promise((resolve, reject) => {
390
+ validate().then(async () => {
391
+ const cleanedForm = prepareForm()
392
+ await appendSilenceAddFields(cleanedForm)
393
+ const realForm = handleFormKeys(cleanedForm)
394
+ resolve({
395
+ realForm,
396
+ mode: props.mode,
397
+ serviceName: props.serviceName,
398
+ currUserName: userStore.getUserInfo().name,
399
+ currUserId: userStore.getUserInfo().id,
400
+ orgId: userStore.getUserInfo().orgid,
401
+ })
402
+ }).catch((error) => {
403
+ reject(error)
404
+ })
405
+ })
406
+ }
407
+
408
+ function handleFormKeys(form: any, mustHandleKey = false) {
409
+ const realForm: any = {}
410
+ for (const key of Object.keys(form)) {
411
+ const value = form[key]
412
+ const extraFormKeyTagIndex = key.indexOf('@')
413
+ if (extraFormKeyTagIndex !== -1) {
414
+ const extraFormKey = key.substring(0, extraFormKeyTagIndex)
415
+ const realKey = key.substring(extraFormKeyTagIndex + 1)
416
+ if (!realForm[extraFormKey]) {
417
+ realForm[extraFormKey] = {}
418
+ }
419
+ realForm[extraFormKey][realKey] = value
420
+ }
421
+ else {
422
+ const realKey = props.isHandleFormKey || mustHandleKey ? getRealKey(key, mustHandleKey) : key
423
+ // 如果发生重名,不覆盖,把key的别名带上
424
+ if (realForm[realKey]) {
425
+ realForm[key] = value
426
+ }
427
+ else {
428
+ realForm[realKey] = value
429
+ }
430
+ }
431
+ }
432
+ return realForm
433
+ }
434
+
435
+ async function appendSilenceAddFields(form: any) {
436
+ if (props.mode === '新增') {
437
+ for (const item of silenceAddJsonData.value) {
438
+ switch (item.silencePurpose) {
439
+ case 'createTime':
440
+ form[item.model] = formatDate(new Date())
441
+ break
442
+ case 'operator':
443
+ form[item.model] = userStore.getUserInfo().name
444
+ break
445
+ case 'operatorId':
446
+ form[item.model] = userStore.getUserInfo().id
447
+ break
448
+ case 'orgId':
449
+ form[item.model] = userStore.getUserInfo().orgid
450
+ break
451
+ case 'orgName':
452
+ form[item.model] = userStore.getUserInfo().orgs
453
+ break
454
+ case 'depId':
455
+ form[item.model] = userStore.getUserInfo().depids
456
+ break
457
+ case 'depName':
458
+ form[item.model] = userStore.getUserInfo().deps
459
+ break
460
+ }
461
+ }
462
+ for (const item of silenceAddJsonData.value.filter(item => item.silencePurpose === 'customize')) {
463
+ const result: any = await runLogic(item.silenceSource!, form, props.serviceName)
464
+ if (result) {
465
+ const keys = Object.keys(result)
466
+ if (keys.length === 1 && keys[0] === 'value') {
467
+ form[item.model] = result.value
468
+ }
469
+ else {
470
+ form[item.model] = result
471
+ }
472
+ }
473
+ else {
474
+ form[item.model] = result
475
+ }
476
+ }
477
+ }
478
+ }
479
+
480
+ function prepareForm() {
481
+ const formObj = { ...form.value }
482
+ const cleanedForm: any = {}
483
+
484
+ for (const key of Object.keys(formObj)) {
485
+ const value = formObj[key]
486
+
487
+ // 跳过无效值
488
+ if (value === null || value === undefined || value === '') {
489
+ continue
490
+ }
491
+
492
+ // 处理数组
493
+ if (Array.isArray(value)) {
494
+ // 过滤掉空字符串、null、undefined
495
+ const filteredArray = value.filter(item =>
496
+ item !== null && item !== undefined && item !== '',
497
+ )
498
+ // 只有当数组不为空时才添加
499
+ if (filteredArray.length > 0) {
500
+ cleanedForm[key] = filteredArray
501
+ }
502
+ continue
503
+ }
504
+
505
+ // 处理对象
506
+ if (typeof value === 'object' && value !== null) {
507
+ // 检查对象是否为空
508
+ const objectKeys = Object.keys(value)
509
+ if (objectKeys.length === 0) {
510
+ continue
511
+ }
512
+
513
+ // 递归清理对象内部
514
+ const cleanedObject: any = {}
515
+ let hasValidValue = false
516
+
517
+ for (const objKey of objectKeys) {
518
+ const objValue = value[objKey]
519
+ if (objValue !== null && objValue !== undefined && objValue !== '') {
520
+ cleanedObject[objKey] = objValue
521
+ hasValidValue = true
522
+ }
523
+ }
524
+
525
+ // 只有当对象有有效值时才添加
526
+ if (hasValidValue) {
527
+ cleanedForm[key] = cleanedObject
528
+ }
529
+ continue
530
+ }
531
+
532
+ // 处理基本类型
533
+ if (value !== null && value !== undefined && value !== '') {
534
+ cleanedForm[key] = value
535
+ }
536
+ }
537
+
538
+ return cleanedForm
539
+ }
540
+
541
+ function getFormData() {
542
+ return prepareForm()
543
+ }
544
+
545
+ async function onSubmit() {
546
+ await validate()
547
+ // 清理表单数据
548
+ const cleanedForm = prepareForm()
549
+ if (!props.configName && props.groupFormItems) {
550
+ // 只有单表才可以成功,多表关联或者自定义sql不行
551
+ await appendSilenceAddFields(cleanedForm)
552
+ const realForm = handleFormKeys(cleanedForm)
553
+
554
+ try {
555
+ addOrModifyEntity(realForm, tableName.value, props.serviceName || import.meta.env.VITE_APP_SYSTEM_NAME).then(() => {
556
+ showSuccessToast('提交成功!!')
557
+ })
558
+ }
559
+ catch (error) {
560
+ showFailToast('提交失败!!')
561
+ }
562
+ finally {
563
+ setTimeout(() => {
564
+ history.back()
565
+ }, 1000)
566
+ }
567
+ }
568
+ else {
569
+ // 使用清理后的数据
570
+ emits('onSubmit', cleanedForm)
571
+ }
572
+ }
573
+ async function validate() {
574
+ await xFormRef.value?.validate()
575
+ }
576
+ function emitFunc(func: any, data: any, value: any) {
577
+ emits(func, data, value)
578
+ emits('xFormItemEmitFunc', func, data, value)
579
+ }
580
+
581
+ defineExpose({ init, form, formGroupName, validate, asyncSubmit, setForm, getFormData })
582
+ </script>
583
+
584
+ <template>
585
+ <VanForm v-if="loaded" ref="xFormRef" class="x-form-container" :class="{ 'use-full-height': !props.isGroupForm }" @submit="onSubmit">
586
+ <div class="form-fields-scrollable">
587
+ <VanCellGroup :title="groupTitle">
588
+ <XFormItem
589
+ v-for="(item, index) in realJsonData"
590
+ :key="index"
591
+ v-model="form[item.model]"
592
+ :mode="props.mode"
593
+ :form="form"
594
+ :attr="item"
595
+ :rules="rules"
596
+ :service-name="myServiceName"
597
+ :get-data-params="myGetDataParams"
598
+ @set-form="setForm"
599
+ @x-form-item-emit-func="emitFunc"
600
+ />
601
+ <slot name="extraFormItem" />
602
+ </VanCellGroup>
603
+ <slot name="extraCellGroup" />
604
+ <!-- 地图预览,统一放在表单项下方,只在有经纬度时显示 -->
605
+ <div v-if="previewMapPoint" style="height: 200px; margin-top: 12px; position: relative;">
606
+ <XOlMap
607
+ ref="previewMapRef"
608
+ :center="previewMapPoint"
609
+ :style="{ height: '100%' }"
610
+ />
611
+ <div class="map-mask" />
612
+ </div>
613
+ </div>
614
+ <div v-if="resolvedSubmitButton && props.mode !== '预览'" class="form-footer-fixed">
615
+ <VanButton class="action-btn" round block type="primary" native-type="submit">
616
+ {{ props.groupFormItems?.btnName ? props.groupFormItems.btnName : '提交' }}
617
+ </VanButton>
618
+ <slot />
619
+ </div>
620
+ <slot name="extraActionSpace" />
621
+ </VanForm>
622
+ </template>
623
+
624
+ <style scoped>
625
+ .use-full-height {
626
+ height: calc(100vh - var(--van-nav-bar-height) - 20px);
627
+ }
628
+
629
+ .x-form-container {
630
+ display: flex;
631
+ flex-direction: column;
632
+ max-height: 100vh;
633
+ overflow: hidden;
634
+ }
635
+
636
+ .form-fields-scrollable {
637
+ flex: 1;
638
+ overflow-y: auto;
639
+ min-height: 0;
640
+ }
641
+
642
+ .form-footer-fixed {
643
+ margin: 16px;
644
+ flex-shrink: 0;
645
+ .action-btn {
646
+ border-radius: 10px;
647
+ }
648
+ }
649
+ .map-mask {
650
+ position: absolute;
651
+ left: 0;
652
+ top: 0;
653
+ width: 100%;
654
+ height: 100%;
655
+ pointer-events: all;
656
+ background: transparent;
657
+ z-index: 10;
658
+ }
659
+ </style>