af-mobile-client-vue3 1.3.36 → 1.3.38

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