af-mobile-client-vue3 1.3.30 → 1.3.31

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