af-mobile-client-vue3 1.3.12 → 1.3.14

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 (271) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/.cursorrules +60 -60
  3. package/.editorconfig +9 -9
  4. package/.env +10 -10
  5. package/.env.development +1 -1
  6. package/.env.production +1 -1
  7. package/.node-version +1 -1
  8. package/.vscode/extensions.json +12 -12
  9. package/.vscode/settings.json +66 -66
  10. package/CLAUDE.md +189 -184
  11. package/README.md +182 -181
  12. package/af-example-mobile-vue-web.iml +9 -9
  13. package/build/vite/index.ts +98 -98
  14. package/build/vite/optimize.ts +34 -34
  15. package/build/vite/vconsole.ts +47 -47
  16. package/commitlint.config.ts +32 -32
  17. package/compress.js +36 -36
  18. package/eslint.config.ts +30 -30
  19. package/index.html +23 -23
  20. package/mock/data.ts +20 -20
  21. package/mock/index.ts +7 -7
  22. package/mock/modules/prose.mock.ts +13 -13
  23. package/mock/modules/user.mock.ts +152 -152
  24. package/mock/util.ts +19 -19
  25. package/netlify.toml +12 -12
  26. package/package.json +114 -114
  27. package/postcss.config.ts +27 -27
  28. package/public/favicon.svg +4 -4
  29. package/public/safari-pinned-tab.svg +4 -4
  30. package/scripts/verifyCommit.js +19 -19
  31. package/src/App.vue +79 -79
  32. package/src/api/mock/index.ts +30 -30
  33. package/src/api/user/index.ts +40 -40
  34. package/src/assets/img/user/login/background-shadow-1.svg +20 -20
  35. package/src/assets/img/user/login/logo-background.svg +20 -20
  36. package/src/bootstrap.ts +26 -26
  37. package/src/components/core/BeautifulLoading/index.vue +52 -52
  38. package/src/components/core/ImageUploader/index.vue +244 -244
  39. package/src/components/core/NavBar/index.vue +53 -53
  40. package/src/components/core/Tabbar/index.vue +32 -32
  41. package/src/components/core/Uploader/index.vue +124 -124
  42. package/src/components/core/XGridDropOption/index.vue +154 -156
  43. package/src/components/core/XMultiSelect/index.vue +183 -183
  44. package/src/components/core/XSelect/index.vue +149 -149
  45. package/src/components/data/InfoDisplay/index.vue +132 -0
  46. package/src/components/data/UserDetail/api.ts +24 -0
  47. package/src/components/data/UserDetail/index.vue +620 -0
  48. package/src/components/data/UserDetail/recordEntries.ts +159 -0
  49. package/src/components/data/UserDetail/types.ts +26 -0
  50. package/src/components/data/XBadge/index.vue +82 -82
  51. package/src/components/data/XCellDetail/index.vue +105 -105
  52. package/src/components/data/XCellList/XCellList.md +313 -313
  53. package/src/components/data/XCellList/index.vue +1075 -1075
  54. package/src/components/data/XCellListFilter/QrScanner/index.vue +207 -207
  55. package/src/components/data/XCellListFilter/QrScanner/startScanAnimation.ts +53 -53
  56. package/src/components/data/XCellListFilter/VpnRecognition/index.vue +119 -119
  57. package/src/components/data/XCellListFilter/index.vue +705 -705
  58. package/src/components/data/XForm/index.vue +659 -659
  59. package/src/components/data/XFormGroup/doc/DeviceForm.vue +122 -122
  60. package/src/components/data/XFormGroup/doc/FormGroupDemo.vue +56 -56
  61. package/src/components/data/XFormGroup/doc/README.md +286 -273
  62. package/src/components/data/XFormGroup/doc/UserForm.vue +102 -102
  63. package/src/components/data/XFormGroup/index.vue +240 -240
  64. package/src/components/data/XFormItem/index.vue +1310 -1310
  65. package/src/components/data/XOlMap/README.md +227 -227
  66. package/src/components/data/XOlMap/XLocationPicker/index.vue +226 -225
  67. package/src/components/data/XOlMap/index.vue +1490 -1490
  68. package/src/components/data/XOlMap/types.ts +149 -149
  69. package/src/components/data/XOlMap/utils/wgs84ToGcj02.js +154 -154
  70. package/src/components/data/XReportForm/DateTimeSecondsPicker.vue +208 -208
  71. package/src/components/data/XReportForm/XReportFormJsonRender.vue +220 -220
  72. package/src/components/data/XReportForm/index.vue +1393 -1393
  73. package/src/components/data/XReportGrid/XAddReport/XAddReport.vue +198 -198
  74. package/src/components/data/XReportGrid/XAddReport/index.js +3 -3
  75. package/src/components/data/XReportGrid/XAddReport/index.md +53 -52
  76. package/src/components/data/XReportGrid/XAddReport/index.ts +10 -10
  77. package/src/components/data/XReportGrid/XReport.vue +960 -960
  78. package/src/components/data/XReportGrid/XReportDemo.vue +33 -33
  79. package/src/components/data/XReportGrid/XReportDesign.vue +597 -597
  80. package/src/components/data/XReportGrid/XReportDrawer/XReportDrawer.vue +148 -148
  81. package/src/components/data/XReportGrid/XReportDrawer/index.js +3 -3
  82. package/src/components/data/XReportGrid/XReportDrawer/index.ts +10 -10
  83. package/src/components/data/XReportGrid/XReportJsonRender.vue +399 -399
  84. package/src/components/data/XReportGrid/XReportTrGroup.vue +592 -592
  85. package/src/components/data/XReportGrid/index.md +46 -42
  86. package/src/components/data/XReportGrid/print.js +184 -184
  87. package/src/components/data/XSignature/index.vue +284 -285
  88. package/src/components/data/XTag/index.vue +10 -10
  89. package/src/components/layout/NormalDataLayout/index.vue +69 -69
  90. package/src/components/layout/TabBarLayout/index.vue +40 -40
  91. package/src/composables/dark.ts +5 -5
  92. package/src/config/routes.ts +9 -9
  93. package/src/constants/index.ts +2 -2
  94. package/src/enums/requestEnum.ts +25 -25
  95. package/src/expression/ExpressionRunner.ts +28 -28
  96. package/src/expression/TestExpression.ts +510 -510
  97. package/src/expression/core/Delegate.ts +116 -116
  98. package/src/expression/core/Expression.ts +1359 -1359
  99. package/src/expression/core/Program.ts +985 -985
  100. package/src/expression/core/Token.ts +29 -29
  101. package/src/expression/enums/ExpressionType.ts +81 -81
  102. package/src/expression/enums/TokenType.ts +11 -11
  103. package/src/expression/exception/BreakWayException.ts +2 -2
  104. package/src/expression/exception/ContinueWayException.ts +2 -2
  105. package/src/expression/exception/ExpressionException.ts +29 -29
  106. package/src/expression/exception/ReturnWayException.ts +14 -14
  107. package/src/expression/exception/ServiceException.ts +22 -22
  108. package/src/expression/instances/JSONArray.ts +52 -52
  109. package/src/expression/instances/JSONObject.ts +118 -118
  110. package/src/expression/instances/LogicConsole.ts +31 -31
  111. package/src/font-style/font.css +4 -4
  112. package/src/hooks/useBoolean.ts +26 -0
  113. package/src/hooks/useCommon.ts +9 -9
  114. package/src/hooks/useLoading.ts +16 -0
  115. package/src/hooks/useLogin.ts +97 -97
  116. package/src/icons/svg/check-in.svg +32 -32
  117. package/src/icons/svg/dark.svg +4 -4
  118. package/src/icons/svg/github.svg +4 -4
  119. package/src/icons/svg/light.svg +4 -4
  120. package/src/icons/svg/link.svg +4 -4
  121. package/src/icons/svgo.yml +22 -22
  122. package/src/layout/GridView/index.vue +16 -16
  123. package/src/layout/PageLayout.vue +9 -9
  124. package/src/layout/SingleLayout.vue +9 -9
  125. package/src/locales/en-US.json +128 -128
  126. package/src/locales/zh-CN.json +128 -128
  127. package/src/logic/LogicRunner.ts +67 -67
  128. package/src/logic/TestLogic.ts +13 -13
  129. package/src/logic/plugins/common/DateTools.ts +35 -35
  130. package/src/logic/plugins/common/VueTools.ts +30 -30
  131. package/src/logic/plugins/index.ts +7 -7
  132. package/src/main.ts +44 -44
  133. package/src/plugins/AppData.ts +38 -38
  134. package/src/plugins/GetLoginInfoService.ts +10 -10
  135. package/src/plugins/collectIcons.ts +10 -0
  136. package/src/plugins/index.ts +11 -11
  137. package/src/router/README.md +8 -8
  138. package/src/router/guards.ts +59 -59
  139. package/src/router/index.ts +35 -35
  140. package/src/router/invoiceRoutes.ts +33 -33
  141. package/src/router/routes.ts +341 -177
  142. package/src/router/types.ts +7 -7
  143. package/src/services/api/Login.ts +6 -6
  144. package/src/services/api/common.ts +109 -109
  145. package/src/services/api/index.ts +7 -7
  146. package/src/services/api/manage.ts +8 -8
  147. package/src/services/api/search.ts +16 -16
  148. package/src/services/api/user.ts +17 -17
  149. package/src/services/restTools.ts +56 -56
  150. package/src/services/v3Api.ts +147 -147
  151. package/src/stores/index.ts +11 -11
  152. package/src/stores/modules/counter.ts +19 -19
  153. package/src/stores/modules/routeCache.ts +23 -23
  154. package/src/stores/modules/setting.ts +76 -76
  155. package/src/stores/modules/user.ts +235 -235
  156. package/src/stores/mutation-type.ts +7 -7
  157. package/src/styles/app.less +36 -36
  158. package/src/styles/login.less +109 -109
  159. package/src/styles/var.less +16 -16
  160. package/src/types/env.d.ts +16 -16
  161. package/src/types/settings.ts +1 -1
  162. package/src/types/vue-router.d.ts +9 -9
  163. package/src/utils/Storage.ts +124 -124
  164. package/src/utils/authority-utils.ts +84 -84
  165. package/src/utils/common.ts +41 -41
  166. package/src/utils/crypto.ts +39 -39
  167. package/src/utils/dataUtil.ts +42 -42
  168. package/src/utils/dictUtil.ts +52 -52
  169. package/src/utils/http/index.ts +199 -199
  170. package/src/utils/i18n.ts +72 -72
  171. package/src/utils/indexedDB.ts +195 -195
  172. package/src/utils/inline-px-to-vw.ts +28 -28
  173. package/src/utils/mobileUtil.ts +34 -34
  174. package/src/utils/progress.ts +19 -19
  175. package/src/utils/routerUtil.ts +271 -271
  176. package/src/utils/runEvalFunction.ts +13 -13
  177. package/src/utils/secureStorage.ts +71 -71
  178. package/src/utils/set-page-title.ts +5 -5
  179. package/src/utils/validate.ts +6 -6
  180. package/src/utils/wechatUtil.ts +9 -9
  181. package/src/views/chat/index.vue +153 -153
  182. package/src/views/common/LoadError.vue +63 -63
  183. package/src/views/common/NotFound.vue +67 -67
  184. package/src/views/component/EvaluateRecordView/index.vue +40 -40
  185. package/src/views/component/IconifyView/index.vue +504 -507
  186. package/src/views/component/UserDetailView/UserDetailPage.vue +77 -0
  187. package/src/views/component/UserDetailView/index.vue +234 -0
  188. package/src/views/component/XCellDetailView/index.vue +217 -217
  189. package/src/views/component/XCellListView/index.vue +108 -157
  190. package/src/views/component/XFormAppraiseView/index.vue +174 -174
  191. package/src/views/component/XFormGroupView/index.vue +78 -82
  192. package/src/views/component/XFormView/index.vue +27 -27
  193. package/src/views/component/XOlMapView/XLocationPicker/index.vue +118 -118
  194. package/src/views/component/XOlMapView/index.vue +434 -434
  195. package/src/views/component/XOlMapView/testData.ts +64 -64
  196. package/src/views/component/XReportFormIframeView/index.vue +47 -47
  197. package/src/views/component/XReportFormView/index.vue +13 -13
  198. package/src/views/component/XReportGridView/index.vue +17 -17
  199. package/src/views/component/XRequestView/index.vue +234 -234
  200. package/src/views/component/XSignatureView/index.vue +50 -50
  201. package/src/views/component/index.vue +181 -177
  202. package/src/views/component/menu.vue +117 -117
  203. package/src/views/component/notice.vue +46 -46
  204. package/src/views/component/topNav.vue +36 -36
  205. package/src/views/invoiceShow/index.vue +61 -61
  206. package/src/views/user/login/ForgetPasswordForm.vue +94 -94
  207. package/src/views/user/login/LoginForm.vue +346 -346
  208. package/src/views/user/login/LoginTitle.vue +76 -76
  209. package/src/views/user/login/LoginWave.vue +109 -109
  210. package/src/views/user/login/index.vue +22 -22
  211. package/src/views/user/my/comm/ModifyPassword.vue +346 -346
  212. package/src/views/user/my/index.vue +340 -340
  213. package/src/views/userRecords/AbnormalAlarmRecords.vue +21 -0
  214. package/src/views/userRecords/CardReplacementRecords.vue +21 -0
  215. package/src/views/userRecords/ChangeRecords.vue +19 -0
  216. package/src/views/userRecords/CommandViewRecords.vue +20 -0
  217. package/src/views/userRecords/GasCompensationRecords.vue +20 -0
  218. package/src/views/userRecords/InstrumentCollectionRecords.vue +21 -0
  219. package/src/views/userRecords/MeterRecords.vue +20 -0
  220. package/src/views/userRecords/OperateRecords.vue +51 -0
  221. package/src/views/userRecords/OtherChargeRecords.vue +19 -0
  222. package/src/views/userRecords/PaymentRecords.vue +28 -0
  223. package/src/views/userRecords/PriceAdjustmentRecords.vue +19 -0
  224. package/src/views/userRecords/ReplacementRecords.vue +19 -0
  225. package/src/views/userRecords/SafetyRecords.vue +19 -0
  226. package/src/views/userRecords/TransactionRecords.vue +21 -0
  227. package/src/views/userRecords/TransferRecords.vue +19 -0
  228. package/src/views/userRecords/operateRecordDetail/index.vue +316 -0
  229. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AddUserDetail.vue +124 -0
  230. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AdvanceDeliveryDetail.vue +88 -0
  231. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AutoAccountsCancelDetail.vue +205 -0
  232. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/AutoAccountsDetail.vue +192 -0
  233. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/BankDkDetail.vue +192 -0
  234. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/BankPayDetail.vue +192 -0
  235. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/BlacklistDetail.vue +153 -0
  236. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CancellationDetail.vue +101 -0
  237. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CardMeterCenterCancelDetail.vue +127 -0
  238. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CardMeterCenterDetail.vue +153 -0
  239. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/CardOverUserDetail.vue +153 -0
  240. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ChangeMeterCancelDetail.vue +166 -0
  241. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ChangeMeterDetail.vue +205 -0
  242. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/DisableManageDetail.vue +127 -0
  243. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/EnableManageDetail.vue +114 -0
  244. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/FaZheChangeDetail.vue +124 -0
  245. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/FeeDeductionDetail.vue +153 -0
  246. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/GasPriceChangeDetail.vue +126 -0
  247. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/InputtorChangeDetail.vue +126 -0
  248. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/IotMeterCenterCancelDetail.vue +114 -0
  249. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/IotMeterCenterDetail.vue +127 -0
  250. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/IotOpenDetail.vue +88 -0
  251. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/MachineCardDetail.vue +101 -0
  252. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/MachineMeterCenterCancelDetail.vue +218 -0
  253. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/MachineMeterCenterDetail.vue +153 -0
  254. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OffGasAddGasDetail.vue +140 -0
  255. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OtherChargeCancelDetail.vue +127 -0
  256. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OtherChargeDetail.vue +114 -0
  257. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/OverUserChangeDetail.vue +127 -0
  258. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ReBillDetail.vue +127 -0
  259. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/RefundDetail.vue +114 -0
  260. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ReplaceCardManageCancelDetail.vue +127 -0
  261. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/ReplaceCardManageDetail.vue +114 -0
  262. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/SaleCardGasDetail.vue +140 -0
  263. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/TransferManageCancelDetail.vue +152 -0
  264. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/TransferManageDetail.vue +178 -0
  265. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/UserChangeDetail.vue +123 -0
  266. package/src/views/userRecords/operateRecordDetail/operateRecordDetails/WechatPayDetail.vue +192 -0
  267. package/src/views/userRecords/types.ts +66 -0
  268. package/tsconfig.json +39 -39
  269. package/uno.config.ts +82 -78
  270. package/vite.config.ts +118 -118
  271. package/src/views/component/XFormGroupView/xformgroup222.vue +0 -97
@@ -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>