@robot-admin/naive-ui-components 0.3.0

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 (352) hide show
  1. package/README.md +257 -0
  2. package/dist/C_ActionBar-DWN-woTc.css.map +1 -0
  3. package/dist/C_ActionBar.cjs +5 -0
  4. package/dist/C_ActionBar.d.cts +2 -0
  5. package/dist/C_ActionBar.d.ts +2 -0
  6. package/dist/C_ActionBar.js +4 -0
  7. package/dist/C_ActionBar2.js +196 -0
  8. package/dist/C_ActionBar2.js.map +1 -0
  9. package/dist/C_AntV-AFKyK6hH.css.map +1 -0
  10. package/dist/C_AntV.cjs +8 -0
  11. package/dist/C_AntV.d.cts +2 -0
  12. package/dist/C_AntV.d.ts +2 -0
  13. package/dist/C_AntV.js +4 -0
  14. package/dist/C_AntV2.js +3150 -0
  15. package/dist/C_AntV2.js.map +1 -0
  16. package/dist/C_Barcode-P_EFj8dC.css.map +1 -0
  17. package/dist/C_Barcode.cjs +4 -0
  18. package/dist/C_Barcode.d.cts +2 -0
  19. package/dist/C_Barcode.d.ts +2 -0
  20. package/dist/C_Barcode.js +3 -0
  21. package/dist/C_Barcode2.js +68 -0
  22. package/dist/C_Barcode2.js.map +1 -0
  23. package/dist/C_Captcha-C-ef41xw.css.map +1 -0
  24. package/dist/C_Captcha.cjs +4 -0
  25. package/dist/C_Captcha.d.cts +2 -0
  26. package/dist/C_Captcha.d.ts +2 -0
  27. package/dist/C_Captcha.js +3 -0
  28. package/dist/C_Captcha2.js +155 -0
  29. package/dist/C_Captcha2.js.map +1 -0
  30. package/dist/C_Cascade-D9kNsjsV.css.map +1 -0
  31. package/dist/C_Cascade.cjs +4 -0
  32. package/dist/C_Cascade.d.cts +2 -0
  33. package/dist/C_Cascade.d.ts +2 -0
  34. package/dist/C_Cascade.js +3 -0
  35. package/dist/C_Cascade2.js +103 -0
  36. package/dist/C_Cascade2.js.map +1 -0
  37. package/dist/C_City-BCQ4ipiK.css.map +1 -0
  38. package/dist/C_City.cjs +4 -0
  39. package/dist/C_City.d.cts +2 -0
  40. package/dist/C_City.d.ts +2 -0
  41. package/dist/C_City.js +3 -0
  42. package/dist/C_City2.js +841 -0
  43. package/dist/C_City2.js.map +1 -0
  44. package/dist/C_Code-C9kvvEmO.css.map +1 -0
  45. package/dist/C_Code.cjs +5 -0
  46. package/dist/C_Code.d.cts +2 -0
  47. package/dist/C_Code.d.ts +2 -0
  48. package/dist/C_Code.js +4 -0
  49. package/dist/C_Code2.js +346 -0
  50. package/dist/C_Code2.js.map +1 -0
  51. package/dist/C_CollapsePanel-BUJHuYcU.css.map +1 -0
  52. package/dist/C_CollapsePanel.cjs +6 -0
  53. package/dist/C_CollapsePanel.d.cts +2 -0
  54. package/dist/C_CollapsePanel.d.ts +2 -0
  55. package/dist/C_CollapsePanel.js +4 -0
  56. package/dist/C_CollapsePanel2.js +319 -0
  57. package/dist/C_CollapsePanel2.js.map +1 -0
  58. package/dist/C_Cron-yx2Ob4Jl.css.map +1 -0
  59. package/dist/C_Cron.cjs +15 -0
  60. package/dist/C_Cron.d.cts +2 -0
  61. package/dist/C_Cron.d.ts +2 -0
  62. package/dist/C_Cron.js +4 -0
  63. package/dist/C_Cron2.js +1209 -0
  64. package/dist/C_Cron2.js.map +1 -0
  65. package/dist/C_Date.cjs +4 -0
  66. package/dist/C_Date.d.cts +2 -0
  67. package/dist/C_Date.d.ts +2 -0
  68. package/dist/C_Date.js +3 -0
  69. package/dist/C_Date2.js +219 -0
  70. package/dist/C_Date2.js.map +1 -0
  71. package/dist/C_Draggable-C483syRC.css.map +1 -0
  72. package/dist/C_Draggable.cjs +5 -0
  73. package/dist/C_Draggable.d.cts +2 -0
  74. package/dist/C_Draggable.d.ts +2 -0
  75. package/dist/C_Draggable.js +3 -0
  76. package/dist/C_Draggable2.js +295 -0
  77. package/dist/C_Draggable2.js.map +1 -0
  78. package/dist/C_Editor-Bp0SyIEw.css.map +1 -0
  79. package/dist/C_Editor.cjs +4 -0
  80. package/dist/C_Editor.d.cts +2 -0
  81. package/dist/C_Editor.d.ts +2 -0
  82. package/dist/C_Editor.js +3 -0
  83. package/dist/C_Editor2.js +160 -0
  84. package/dist/C_Editor2.js.map +1 -0
  85. package/dist/C_FilePreview-CPqvhoCy.css.map +1 -0
  86. package/dist/C_FilePreview.cjs +6 -0
  87. package/dist/C_FilePreview.d.cts +2 -0
  88. package/dist/C_FilePreview.d.ts +2 -0
  89. package/dist/C_FilePreview.js +3 -0
  90. package/dist/C_FilePreview2.js +1031 -0
  91. package/dist/C_FilePreview2.js.map +1 -0
  92. package/dist/C_Form-Jx7PY3sT.css.map +1 -0
  93. package/dist/C_Form.cjs +15 -0
  94. package/dist/C_Form.d.cts +2 -0
  95. package/dist/C_Form.d.ts +2 -0
  96. package/dist/C_Form.js +4 -0
  97. package/dist/C_Form2.js +2510 -0
  98. package/dist/C_Form2.js.map +1 -0
  99. package/dist/C_FormSearch-DvRgxlRn.css.map +1 -0
  100. package/dist/C_FormSearch.cjs +6 -0
  101. package/dist/C_FormSearch.d.cts +2 -0
  102. package/dist/C_FormSearch.d.ts +2 -0
  103. package/dist/C_FormSearch.js +3 -0
  104. package/dist/C_FormSearch2.js +356 -0
  105. package/dist/C_FormSearch2.js.map +1 -0
  106. package/dist/C_FormulaEditor-DtGkt4T_.css.map +1 -0
  107. package/dist/C_FormulaEditor.cjs +13 -0
  108. package/dist/C_FormulaEditor.d.cts +2 -0
  109. package/dist/C_FormulaEditor.d.ts +2 -0
  110. package/dist/C_FormulaEditor.js +4 -0
  111. package/dist/C_FormulaEditor2.js +1433 -0
  112. package/dist/C_FormulaEditor2.js.map +1 -0
  113. package/dist/C_FullCalendar-BF7H0YIx.css.map +1 -0
  114. package/dist/C_FullCalendar.cjs +9 -0
  115. package/dist/C_FullCalendar.d.cts +2 -0
  116. package/dist/C_FullCalendar.d.ts +2 -0
  117. package/dist/C_FullCalendar.js +3 -0
  118. package/dist/C_FullCalendar2.js +377 -0
  119. package/dist/C_FullCalendar2.js.map +1 -0
  120. package/dist/C_Guide.cjs +4 -0
  121. package/dist/C_Guide.d.cts +2 -0
  122. package/dist/C_Guide.d.ts +2 -0
  123. package/dist/C_Guide.js +3 -0
  124. package/dist/C_Guide2.js +58 -0
  125. package/dist/C_Guide2.js.map +1 -0
  126. package/dist/C_Icon.cjs +4 -0
  127. package/dist/C_Icon.d.cts +2 -0
  128. package/dist/C_Icon.d.ts +2 -0
  129. package/dist/C_Icon.js +3 -0
  130. package/dist/C_Icon2.js +286 -0
  131. package/dist/C_Icon2.js.map +1 -0
  132. package/dist/C_ImageCropper-BVJfUufl.css.map +1 -0
  133. package/dist/C_ImageCropper.cjs +6 -0
  134. package/dist/C_ImageCropper.d.cts +2 -0
  135. package/dist/C_ImageCropper.d.ts +2 -0
  136. package/dist/C_ImageCropper.js +4 -0
  137. package/dist/C_ImageCropper2.js +723 -0
  138. package/dist/C_ImageCropper2.js.map +1 -0
  139. package/dist/C_Language.cjs +4 -0
  140. package/dist/C_Language.d.cts +2 -0
  141. package/dist/C_Language.d.ts +2 -0
  142. package/dist/C_Language.js +3 -0
  143. package/dist/C_Language2.js +72 -0
  144. package/dist/C_Language2.js.map +1 -0
  145. package/dist/C_Map-DpzeuWdX.css.map +1 -0
  146. package/dist/C_Map.cjs +7 -0
  147. package/dist/C_Map.d.cts +2 -0
  148. package/dist/C_Map.d.ts +2 -0
  149. package/dist/C_Map.js +3 -0
  150. package/dist/C_Map2.js +199 -0
  151. package/dist/C_Map2.js.map +1 -0
  152. package/dist/C_Markdown-BEjxknqd.css.map +1 -0
  153. package/dist/C_Markdown.cjs +4 -0
  154. package/dist/C_Markdown.d.cts +2 -0
  155. package/dist/C_Markdown.d.ts +2 -0
  156. package/dist/C_Markdown.js +3 -0
  157. package/dist/C_Markdown2.js +186 -0
  158. package/dist/C_Markdown2.js.map +1 -0
  159. package/dist/C_NotificationCenter-0l3TY2Gn.css.map +1 -0
  160. package/dist/C_NotificationCenter.cjs +20 -0
  161. package/dist/C_NotificationCenter.d.cts +2 -0
  162. package/dist/C_NotificationCenter.d.ts +2 -0
  163. package/dist/C_NotificationCenter.js +4 -0
  164. package/dist/C_NotificationCenter2.js +1383 -0
  165. package/dist/C_NotificationCenter2.js.map +1 -0
  166. package/dist/C_Progress.cjs +4 -0
  167. package/dist/C_Progress.d.cts +2 -0
  168. package/dist/C_Progress.d.ts +2 -0
  169. package/dist/C_Progress.js +3 -0
  170. package/dist/C_Progress2.js +103 -0
  171. package/dist/C_Progress2.js.map +1 -0
  172. package/dist/C_QRCode-DbdiAIPg.css.map +1 -0
  173. package/dist/C_QRCode.cjs +5 -0
  174. package/dist/C_QRCode.d.cts +2 -0
  175. package/dist/C_QRCode.d.ts +2 -0
  176. package/dist/C_QRCode.js +3 -0
  177. package/dist/C_QRCode2.js +218 -0
  178. package/dist/C_QRCode2.js.map +1 -0
  179. package/dist/C_Signature-zhHCbra9.css.map +1 -0
  180. package/dist/C_Signature.cjs +8 -0
  181. package/dist/C_Signature.d.cts +2 -0
  182. package/dist/C_Signature.d.ts +2 -0
  183. package/dist/C_Signature.js +4 -0
  184. package/dist/C_Signature2.js +618 -0
  185. package/dist/C_Signature2.js.map +1 -0
  186. package/dist/C_SplitPane-C6sBsfKY.css.map +1 -0
  187. package/dist/C_SplitPane.cjs +6 -0
  188. package/dist/C_SplitPane.d.cts +2 -0
  189. package/dist/C_SplitPane.d.ts +2 -0
  190. package/dist/C_SplitPane.js +4 -0
  191. package/dist/C_SplitPane2.js +356 -0
  192. package/dist/C_SplitPane2.js.map +1 -0
  193. package/dist/C_Steps-CODHN5Hs.css.map +1 -0
  194. package/dist/C_Steps.cjs +4 -0
  195. package/dist/C_Steps.d.cts +2 -0
  196. package/dist/C_Steps.d.ts +2 -0
  197. package/dist/C_Steps.js +3 -0
  198. package/dist/C_Steps2.js +82 -0
  199. package/dist/C_Steps2.js.map +1 -0
  200. package/dist/C_Table-DSNsntmT.css.map +1 -0
  201. package/dist/C_Table.cjs +19 -0
  202. package/dist/C_Table.d.cts +2 -0
  203. package/dist/C_Table.d.ts +2 -0
  204. package/dist/C_Table.js +5 -0
  205. package/dist/C_Table2.js +3009 -0
  206. package/dist/C_Table2.js.map +1 -0
  207. package/dist/C_Theme.cjs +4 -0
  208. package/dist/C_Theme.d.cts +2 -0
  209. package/dist/C_Theme.d.ts +2 -0
  210. package/dist/C_Theme.js +3 -0
  211. package/dist/C_Theme2.js +60 -0
  212. package/dist/C_Theme2.js.map +1 -0
  213. package/dist/C_Time-BvZLYraL.css.map +1 -0
  214. package/dist/C_Time.cjs +5 -0
  215. package/dist/C_Time.d.cts +2 -0
  216. package/dist/C_Time.d.ts +2 -0
  217. package/dist/C_Time.js +3 -0
  218. package/dist/C_Time2.js +199 -0
  219. package/dist/C_Time2.js.map +1 -0
  220. package/dist/C_Tree-0GDv--jX.css.map +1 -0
  221. package/dist/C_Tree.cjs +7 -0
  222. package/dist/C_Tree.d.cts +2 -0
  223. package/dist/C_Tree.d.ts +2 -0
  224. package/dist/C_Tree.js +4 -0
  225. package/dist/C_Tree2.js +441 -0
  226. package/dist/C_Tree2.js.map +1 -0
  227. package/dist/C_Upload-BXd3YYLx.css.map +1 -0
  228. package/dist/C_Upload.cjs +12 -0
  229. package/dist/C_Upload.d.cts +2 -0
  230. package/dist/C_Upload.d.ts +2 -0
  231. package/dist/C_Upload.js +4 -0
  232. package/dist/C_Upload2.js +1388 -0
  233. package/dist/C_Upload2.js.map +1 -0
  234. package/dist/C_VideoPlayer-DYG3RL0Q.css.map +1 -0
  235. package/dist/C_VideoPlayer.cjs +23 -0
  236. package/dist/C_VideoPlayer.d.cts +2 -0
  237. package/dist/C_VideoPlayer.d.ts +2 -0
  238. package/dist/C_VideoPlayer.js +3 -0
  239. package/dist/C_VideoPlayer2.js +1932 -0
  240. package/dist/C_VideoPlayer2.js.map +1 -0
  241. package/dist/C_VtableGantt-fhItIiHE.css.map +1 -0
  242. package/dist/C_VtableGantt.cjs +6 -0
  243. package/dist/C_VtableGantt.d.cts +2 -0
  244. package/dist/C_VtableGantt.d.ts +2 -0
  245. package/dist/C_VtableGantt.js +4 -0
  246. package/dist/C_VtableGantt2.js +873 -0
  247. package/dist/C_VtableGantt2.js.map +1 -0
  248. package/dist/C_WaterFall-8sQDFXKg.css.map +1 -0
  249. package/dist/C_WaterFall.cjs +13 -0
  250. package/dist/C_WaterFall.d.cts +2 -0
  251. package/dist/C_WaterFall.d.ts +2 -0
  252. package/dist/C_WaterFall.js +3 -0
  253. package/dist/C_WaterFall2.js +365 -0
  254. package/dist/C_WaterFall2.js.map +1 -0
  255. package/dist/C_WorkFlow-J-dyIuh9.css.map +1 -0
  256. package/dist/C_WorkFlow.cjs +8 -0
  257. package/dist/C_WorkFlow.d.cts +2 -0
  258. package/dist/C_WorkFlow.d.ts +2 -0
  259. package/dist/C_WorkFlow.js +4 -0
  260. package/dist/C_WorkFlow2.js +1984 -0
  261. package/dist/C_WorkFlow2.js.map +1 -0
  262. package/dist/chunk.js +22 -0
  263. package/dist/city.js +4817 -0
  264. package/dist/city.js.map +1 -0
  265. package/dist/constants.d.ts +273 -0
  266. package/dist/constants.d.ts.map +1 -0
  267. package/dist/constants2.d.ts +178 -0
  268. package/dist/constants2.d.ts.map +1 -0
  269. package/dist/constants3.d.ts +475 -0
  270. package/dist/constants3.d.ts.map +1 -0
  271. package/dist/constants4.d.ts +430 -0
  272. package/dist/constants4.d.ts.map +1 -0
  273. package/dist/constants5.d.ts +4283 -0
  274. package/dist/constants5.d.ts.map +1 -0
  275. package/dist/data.d.ts +67 -0
  276. package/dist/data.d.ts.map +1 -0
  277. package/dist/export-helper.js +9 -0
  278. package/dist/index.cjs +409 -0
  279. package/dist/index.d.cts +96 -0
  280. package/dist/index.d.cts.map +1 -0
  281. package/dist/index.d.ts +103 -0
  282. package/dist/index.d.ts.map +1 -0
  283. package/dist/index.js +230 -0
  284. package/dist/index.js.map +1 -0
  285. package/dist/index.vue.d.ts +80 -0
  286. package/dist/index.vue.d.ts.map +1 -0
  287. package/dist/index10.vue.d.ts +72 -0
  288. package/dist/index10.vue.d.ts.map +1 -0
  289. package/dist/index11.vue.d.ts +26 -0
  290. package/dist/index11.vue.d.ts.map +1 -0
  291. package/dist/index12.vue.d.ts +81 -0
  292. package/dist/index12.vue.d.ts.map +1 -0
  293. package/dist/index13.vue.d.ts +55 -0
  294. package/dist/index13.vue.d.ts.map +1 -0
  295. package/dist/index14.vue.d.ts +33 -0
  296. package/dist/index14.vue.d.ts.map +1 -0
  297. package/dist/index15.vue.d.ts +18 -0
  298. package/dist/index15.vue.d.ts.map +1 -0
  299. package/dist/index16.vue.d.ts +662 -0
  300. package/dist/index16.vue.d.ts.map +1 -0
  301. package/dist/index2.vue.d.ts +38 -0
  302. package/dist/index2.vue.d.ts.map +1 -0
  303. package/dist/index3.vue.d.ts +45 -0
  304. package/dist/index3.vue.d.ts.map +1 -0
  305. package/dist/index4.vue.d.ts +31 -0
  306. package/dist/index4.vue.d.ts.map +1 -0
  307. package/dist/index5.vue.d.ts +35 -0
  308. package/dist/index5.vue.d.ts.map +1 -0
  309. package/dist/index6.vue.d.ts +48 -0
  310. package/dist/index6.vue.d.ts.map +1 -0
  311. package/dist/index7.vue.d.ts +56 -0
  312. package/dist/index7.vue.d.ts.map +1 -0
  313. package/dist/index8.vue.d.ts +41 -0
  314. package/dist/index8.vue.d.ts.map +1 -0
  315. package/dist/index9.vue.d.ts +30 -0
  316. package/dist/index9.vue.d.ts.map +1 -0
  317. package/dist/storage.js +31 -0
  318. package/dist/storage.js.map +1 -0
  319. package/dist/style.css +7725 -0
  320. package/dist/useCalendarEvents.d.ts +148 -0
  321. package/dist/useCalendarEvents.d.ts.map +1 -0
  322. package/dist/useCollapsePanel.d.ts +132 -0
  323. package/dist/useCollapsePanel.d.ts.map +1 -0
  324. package/dist/useCropperCore.d.ts +102 -0
  325. package/dist/useCropperCore.d.ts.map +1 -0
  326. package/dist/useDraggableLayout.d.ts +194 -0
  327. package/dist/useDraggableLayout.d.ts.map +1 -0
  328. package/dist/useDynamicFormState.d.ts +4248 -0
  329. package/dist/useDynamicFormState.d.ts.map +1 -0
  330. package/dist/useEdgeInteraction.d.ts +7614 -0
  331. package/dist/useEdgeInteraction.d.ts.map +1 -0
  332. package/dist/useFullscreen.d.ts +166 -0
  333. package/dist/useFullscreen.d.ts.map +1 -0
  334. package/dist/useInfiniteScroll.d.ts +169 -0
  335. package/dist/useInfiniteScroll.d.ts.map +1 -0
  336. package/dist/useModalEdit.d.ts +960 -0
  337. package/dist/useModalEdit.d.ts.map +1 -0
  338. package/dist/useQRCode.d.ts +87 -0
  339. package/dist/useQRCode.d.ts.map +1 -0
  340. package/dist/useSearchState.d.ts +180 -0
  341. package/dist/useSearchState.d.ts.map +1 -0
  342. package/dist/useSignatureHistory.d.ts +189 -0
  343. package/dist/useSignatureHistory.d.ts.map +1 -0
  344. package/dist/useSplitResize.d.ts +158 -0
  345. package/dist/useSplitResize.d.ts.map +1 -0
  346. package/dist/useTimeSelection.d.ts +105 -0
  347. package/dist/useTimeSelection.d.ts.map +1 -0
  348. package/dist/useTreeOperations.d.ts +183 -0
  349. package/dist/useTreeOperations.d.ts.map +1 -0
  350. package/dist/useWorkflowValidation.d.ts +1052 -0
  351. package/dist/useWorkflowValidation.d.ts.map +1 -0
  352. package/package.json +342 -0
@@ -0,0 +1,2510 @@
1
+ import { t as C_Icon_default } from "./C_Icon2.js";
2
+ import { t as export_helper_default } from "./export-helper.js";
3
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, getCurrentInstance, h, inject, mergeProps, nextTick, normalizeClass, normalizeStyle, onMounted, openBlock, reactive, readonly, ref, renderList, renderSlot, resolveComponent, resolveDynamicComponent, toDisplayString, toRaw, unref, vShow, watch, watchEffect, withCtx, withDirectives, withKeys } from "vue";
4
+ import { NButton, NForm, NFormItem, NSpace } from "naive-ui";
5
+ import { mergeRules } from "@robot-admin/form-validate";
6
+
7
+ //#region src/components/C_Form/composables/useFormConfig.ts
8
+ const FORM_DEFAULTS = {
9
+ layout: "default",
10
+ labelPlacement: "left",
11
+ labelWidth: "auto",
12
+ size: "medium",
13
+ disabled: false,
14
+ readonly: false,
15
+ showActions: true,
16
+ validateOnChange: false
17
+ };
18
+ /** 拥有自身控制按钮的布局(不显示默认操作按钮) */
19
+ const LAYOUTS_WITH_OWN_CONTROLS = ["steps", "custom"];
20
+ /**
21
+ * 解析 FormConfig,合并默认值
22
+ * @param config - 用户传入的配置(可选)
23
+ * @returns 具有完整默认值的 ResolvedFormConfig
24
+ */
25
+ function resolveFormConfig(config) {
26
+ return {
27
+ ...FORM_DEFAULTS,
28
+ ...config
29
+ };
30
+ }
31
+ /**
32
+ * 计算是否显示默认操作按钮
33
+ * @param resolved - 已解析的配置
34
+ */
35
+ function shouldShowActions(resolved) {
36
+ if (resolved.showActions === false) return false;
37
+ if (LAYOUTS_WITH_OWN_CONTROLS.includes(resolved.layout)) return false;
38
+ return true;
39
+ }
40
+
41
+ //#endregion
42
+ //#region src/components/C_Form/composables/useFormState.ts
43
+ /** 各控件类型的默认空值 */
44
+ const DEFAULT_VALUES = {
45
+ input: "",
46
+ textarea: "",
47
+ editor: "",
48
+ select: null,
49
+ datePicker: null,
50
+ daterange: null,
51
+ timePicker: null,
52
+ cascader: null,
53
+ colorPicker: null,
54
+ checkbox: null,
55
+ upload: [],
56
+ radio: "",
57
+ inputNumber: null,
58
+ slider: null,
59
+ rate: null,
60
+ switch: null
61
+ };
62
+ const getDefaultValue = (type) => {
63
+ return DEFAULT_VALUES[type] ?? null;
64
+ };
65
+ /**
66
+ * C_Form 状态引擎 — 管理表单数据模型、校验规则、验证 API、字段操作
67
+ * @param options - 表单配置项(响应式)
68
+ * @param config - 解析后的表单配置(响应式)
69
+ * @param formRef - NForm 实例引用
70
+ * @param emit - 组件事件发射器
71
+ */
72
+ function useFormState(options, config, formRef, emit) {
73
+ const formModel = reactive({});
74
+ const formRules = reactive({});
75
+ const visibleOptions = computed(() => options.value.filter((item) => item.show !== false));
76
+ const initialize = () => {
77
+ try {
78
+ Object.keys(formRules).forEach((key) => delete formRules[key]);
79
+ options.value.forEach((item) => {
80
+ if (!(item.prop in formModel)) formModel[item.prop] = item.value !== void 0 ? item.value : getDefaultValue(item.type);
81
+ if (item.rules?.length) {
82
+ const allHaveValidator = item.rules.every((r) => typeof r.validator === "function");
83
+ formRules[item.prop] = allHaveValidator ? mergeRules(item.rules) : item.rules;
84
+ }
85
+ });
86
+ } catch (error) {
87
+ console.error("[C_Form] 初始化失败:", error);
88
+ }
89
+ };
90
+ const handleFieldChange = (field) => {
91
+ if (config.value.validateOnChange) nextTick(() => {
92
+ validateField(field).catch(() => {});
93
+ });
94
+ };
95
+ const validate = async () => {
96
+ if (!formRef.value) throw new Error("[C_Form] 表单引用不存在");
97
+ try {
98
+ await formRef.value.validate();
99
+ emit("validate-success", getModel());
100
+ } catch (errors) {
101
+ emit("validate-error", errors);
102
+ throw errors;
103
+ }
104
+ };
105
+ const validateField = async (field) => {
106
+ if (!formRef.value) throw new Error("[C_Form] 表单引用不存在");
107
+ const fields = Array.isArray(field) ? field : [field];
108
+ try {
109
+ await formRef.value.validate();
110
+ } catch (allErrors) {
111
+ if (!Array.isArray(allErrors)) throw allErrors;
112
+ const targetErrors = allErrors.filter((errorGroup) => errorGroup?.some((err) => {
113
+ const e = err;
114
+ return typeof e.field === "string" && fields.includes(e.field);
115
+ }));
116
+ if (targetErrors.length > 0) throw targetErrors;
117
+ }
118
+ };
119
+ const clearValidation = (field) => {
120
+ if (!formRef.value) return;
121
+ if (field) (Array.isArray(field) ? field : [field]).forEach((fieldName) => {
122
+ if (formModel[fieldName] !== void 0) formModel[fieldName] = formModel[fieldName];
123
+ });
124
+ else formRef.value.restoreValidation();
125
+ };
126
+ const validateByFilter = async (filterFn, context) => {
127
+ try {
128
+ const fields = options.value.filter(filterFn).map((option) => option.prop);
129
+ if (fields.length === 0) return true;
130
+ await validateField(fields);
131
+ return true;
132
+ } catch (error) {
133
+ console.warn(`[C_Form] ${context}验证失败:`, error);
134
+ return false;
135
+ }
136
+ };
137
+ const validateStep = async (stepIndex) => {
138
+ const stepKey = config.value.steps?.steps?.[stepIndex]?.key;
139
+ if (!stepKey) return true;
140
+ return validateByFilter((option) => option.layout?.step === stepKey, `步骤 ${stepIndex} `);
141
+ };
142
+ const validateTab = async (tabKey) => {
143
+ return validateByFilter((option) => option.layout?.tab === tabKey, `标签页 ${tabKey} `);
144
+ };
145
+ const validateDynamicFields = async () => {
146
+ return validateByFilter((option) => Boolean(option.layout?.dynamic), "动态字段 ");
147
+ };
148
+ const validateCustomGroup = async (groupKey) => {
149
+ return validateByFilter((option) => option.layout?.group === groupKey, `自定义分组 ${groupKey} `);
150
+ };
151
+ const getModel = () => ({ ...formModel });
152
+ const setFields = (fields) => {
153
+ Object.assign(formModel, fields);
154
+ };
155
+ const resetFields = () => {
156
+ try {
157
+ clearValidation();
158
+ options.value.forEach((item) => {
159
+ const defaultValue = item.value !== void 0 ? item.value : getDefaultValue(item.type);
160
+ formModel[item.prop] = defaultValue;
161
+ });
162
+ } catch (error) {
163
+ console.error("[C_Form] 重置表单失败:", error);
164
+ }
165
+ };
166
+ const setFieldValue = async (field, value, shouldValidate = false) => {
167
+ formModel[field] = value;
168
+ if (shouldValidate) await validateField(field);
169
+ };
170
+ const getFieldValue = (field) => formModel[field];
171
+ const setFieldsValue = async (fields, shouldValidate = false) => {
172
+ Object.assign(formModel, fields);
173
+ if (shouldValidate) await validate();
174
+ };
175
+ const handleSubmit = async () => {
176
+ try {
177
+ await validate();
178
+ emit("submit", {
179
+ model: getModel(),
180
+ form: formRef.value
181
+ });
182
+ } catch (error) {
183
+ console.warn("[C_Form] 表单验证失败:", error);
184
+ }
185
+ };
186
+ onMounted(() => {
187
+ initialize();
188
+ watch(() => options.value, () => initialize(), { deep: true });
189
+ watch(() => formModel, (val) => emit("update:modelValue", { ...val }), { deep: true });
190
+ });
191
+ return {
192
+ formModel,
193
+ formRules,
194
+ visibleOptions,
195
+ initialize,
196
+ handleFieldChange,
197
+ validate,
198
+ validateField,
199
+ validateStep,
200
+ validateTab,
201
+ validateDynamicFields,
202
+ validateCustomGroup,
203
+ clearValidation,
204
+ getModel,
205
+ setFields,
206
+ resetFields,
207
+ setFieldValue,
208
+ getFieldValue,
209
+ setFieldsValue,
210
+ handleSubmit,
211
+ handleReset: resetFields
212
+ };
213
+ }
214
+
215
+ //#endregion
216
+ //#region src/components/C_Form/composables/useFormRenderer.ts
217
+ /**
218
+ * 构建渲染器注册表
219
+ * @param C - 组件映射表(由 C_Form 的 <script setup> 解析并注入)
220
+ *
221
+ * 为什么不在 .ts 文件中直接 resolveComponent?
222
+ * unplugin-vue-components 只转换 .vue SFC 中的 resolveComponent 调用,
223
+ * .ts 文件中的 resolveComponent 不会被转换,运行时无法找到组件。
224
+ */
225
+ function buildRenderers(C) {
226
+ return {
227
+ input: (props) => h(C.NInput, { ...props }),
228
+ textarea: (props) => h(C.NInput, {
229
+ ...props,
230
+ type: "textarea"
231
+ }),
232
+ inputNumber: (props) => h(C.NInputNumber, { ...props }),
233
+ switch: (props) => h(C.NSwitch, { ...props }),
234
+ slider: (props) => h(C.NSlider, { ...props }),
235
+ rate: (props) => h(C.NRate, { ...props }),
236
+ datePicker: (props) => h(C.NDatePicker, { ...props }),
237
+ daterange: (props) => h(C.NDatePicker, {
238
+ ...props,
239
+ type: "daterange"
240
+ }),
241
+ timePicker: (props) => h(C.NTimePicker, { ...props }),
242
+ cascader: (props) => h(C.NCascader, { ...props }),
243
+ colorPicker: (props) => h(C.NColorPicker, { ...props }),
244
+ select: (baseProps, item) => h(C.NSelect, {
245
+ ...baseProps,
246
+ options: item.children?.map((child) => ({
247
+ value: child.value,
248
+ label: child.label,
249
+ disabled: child.disabled
250
+ })) || []
251
+ }),
252
+ checkbox: (baseProps, item) => h(C.NCheckboxGroup, { ...baseProps }, { default: () => h(C.NSpace, {}, { default: () => item.children?.map((child) => h(C.NCheckbox, {
253
+ value: child.value,
254
+ label: child.label,
255
+ disabled: child.disabled,
256
+ key: String(child.value)
257
+ })) || [] }) }),
258
+ radio: (baseProps, item) => h(C.NRadioGroup, { ...baseProps }, { default: () => h(C.NSpace, {}, { default: () => item.children?.map((child) => h(C.NRadio, {
259
+ value: child.value,
260
+ disabled: child.disabled,
261
+ key: String(child.value)
262
+ }, { default: () => child.label })) || [] }) }),
263
+ upload: (baseProps, item, _config, ctx) => h(C.NUpload, {
264
+ fileList: baseProps.value || [],
265
+ "onUpdate:fileList": (fileList) => {
266
+ baseProps["onUpdate:value"]?.(fileList);
267
+ },
268
+ ...item.attrs
269
+ }, {
270
+ trigger: () => ctx?.slots?.["uploadClick"]?.() || h(C.NButton, { type: "primary" }, { default: () => "选择文件" }),
271
+ tip: () => ctx?.slots?.["uploadTip"]?.() || null
272
+ }),
273
+ editor: (baseProps, item, config) => h(C.C_Editor, {
274
+ editorId: `editor-${item.prop}`,
275
+ modelValue: baseProps.value || "",
276
+ placeholder: item.placeholder,
277
+ disabled: config.disabled,
278
+ readonly: config.readonly,
279
+ "onUpdate:modelValue": (value) => {
280
+ baseProps["onUpdate:value"]?.(value);
281
+ },
282
+ ...item.attrs
283
+ })
284
+ };
285
+ }
286
+ /** 自定义渲染器扩展存储 */
287
+ const customRenderers = {};
288
+ /**
289
+ * 运行时注册自定义渲染器 — 开闭原则
290
+ * @param type - 控件类型名
291
+ * @param renderer - 渲染函数
292
+ */
293
+ function registerRenderer(type, renderer) {
294
+ customRenderers[type] = renderer;
295
+ }
296
+ /**
297
+ * 渲染引擎 Composable — 生成 formItems VNode 数组
298
+ * @param componentMap - 已解析的组件映射表(由 C_Form 的 <script setup> 提供)
299
+ */
300
+ function useFormRenderer(formModel, visibleOptions, config, handleFieldChange, componentMap, instanceSlots) {
301
+ const renderers = {
302
+ ...buildRenderers(componentMap),
303
+ ...customRenderers
304
+ };
305
+ /**
306
+ * 为指定表单项生成基础 props(双向绑定 + 占位符)
307
+ */
308
+ const getBaseProps = (item) => {
309
+ const baseProps = {
310
+ value: formModel[item.prop],
311
+ "onUpdate:value": (value) => {
312
+ formModel[item.prop] = value;
313
+ handleFieldChange(item.prop);
314
+ }
315
+ };
316
+ if (item.type === "textarea") baseProps.type = "textarea";
317
+ if (item.placeholder) baseProps.placeholder = item.placeholder;
318
+ return baseProps;
319
+ };
320
+ /**
321
+ * 渲染单个表单项控件
322
+ */
323
+ const renderFormItem = (item) => {
324
+ try {
325
+ const renderer = renderers[item.type];
326
+ if (!renderer) {
327
+ console.warn(`[C_Form] 未支持的组件类型: ${item.type}`);
328
+ return null;
329
+ }
330
+ return renderer({
331
+ ...getBaseProps(item),
332
+ ...item.attrs
333
+ }, item, config.value, {
334
+ slots: instanceSlots,
335
+ components: componentMap
336
+ });
337
+ } catch (error) {
338
+ console.error(`[C_Form] 渲染表单项失败:`, error, item);
339
+ return null;
340
+ }
341
+ };
342
+ return {
343
+ renderFormItem,
344
+ formItems: computed(() => visibleOptions.value.map((item) => h(componentMap.NFormItem, {
345
+ label: item.label,
346
+ path: item.prop,
347
+ key: item.prop,
348
+ required: !!item.rules?.length
349
+ }, { default: () => renderFormItem(item) }))),
350
+ getBaseProps
351
+ };
352
+ }
353
+
354
+ //#endregion
355
+ //#region src/components/C_Form/layouts/Default/index.vue?vue&type=script&setup=true&lang.ts
356
+ const _hoisted_1$6 = { class: "c-form-default" };
357
+ var index_vue_vue_type_script_setup_true_lang_default$8 = /* @__PURE__ */ defineComponent({
358
+ __name: "index",
359
+ props: {
360
+ formItems: {},
361
+ layoutConfig: {},
362
+ options: {}
363
+ },
364
+ setup(__props) {
365
+ return (_ctx, _cache) => {
366
+ return openBlock(), createElementBlock("div", _hoisted_1$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.formItems, (item) => {
367
+ return openBlock(), createBlock(resolveDynamicComponent(item), { key: item.key });
368
+ }), 128))]);
369
+ };
370
+ }
371
+ });
372
+
373
+ //#endregion
374
+ //#region src/components/C_Form/layouts/Default/index.vue
375
+ var Default_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$8, [["__scopeId", "data-v-3422ba86"]]);
376
+
377
+ //#endregion
378
+ //#region src/components/C_Form/layouts/Inline/index.vue?vue&type=script&setup=true&lang.ts
379
+ var index_vue_vue_type_script_setup_true_lang_default$7 = /* @__PURE__ */ defineComponent({
380
+ __name: "index",
381
+ props: {
382
+ formItems: {},
383
+ layoutConfig: { default: () => ({}) },
384
+ options: { default: () => [] }
385
+ },
386
+ setup(__props) {
387
+ const props = __props;
388
+ /**
389
+ * 获取统一项目宽度
390
+ */
391
+ const itemWidth = computed(() => 280);
392
+ /**
393
+ * 获取项目间距
394
+ */
395
+ const gap = computed(() => {
396
+ return props.layoutConfig?.inline?.gap ?? 16;
397
+ });
398
+ /**
399
+ * 获取行间距
400
+ */
401
+ const rowGap = computed(() => 16);
402
+ /**
403
+ * 获取对齐方式 - 既控制垂直对齐也控制水平对齐
404
+ */
405
+ const align = computed(() => {
406
+ const alignValue = props.layoutConfig?.inline?.align;
407
+ return alignValue === "start" || alignValue === "end" ? alignValue : "center";
408
+ });
409
+ /**
410
+ * 获取垂直对齐样式
411
+ */
412
+ const alignItems = computed(() => {
413
+ switch (align.value) {
414
+ case "start": return "flex-start";
415
+ case "end": return "flex-end";
416
+ default: return "center";
417
+ }
418
+ });
419
+ /**
420
+ * 获取水平对齐样式
421
+ */
422
+ const justifyContent = computed(() => {
423
+ switch (align.value) {
424
+ case "start": return "flex-start";
425
+ case "end": return "flex-end";
426
+ case "center": return "center";
427
+ default: return "flex-start";
428
+ }
429
+ });
430
+ /**
431
+ * 容器样式计算
432
+ */
433
+ const containerStyle = computed(() => {
434
+ return {
435
+ display: "flex",
436
+ flexWrap: "wrap",
437
+ alignItems: alignItems.value,
438
+ justifyContent: justifyContent.value,
439
+ gap: `${rowGap.value}px ${gap.value}px`,
440
+ width: "100%"
441
+ };
442
+ });
443
+ /**
444
+ * 获取表单项的唯一key
445
+ */
446
+ const getItemKey = (item, index) => {
447
+ if (item.key != null) return String(item.key);
448
+ const itemProps = item.props;
449
+ if (itemProps?.path) return itemProps.path;
450
+ return `form-item-${index}`;
451
+ };
452
+ /**
453
+ * 获取表单项样式
454
+ */
455
+ const getItemStyle = (index) => {
456
+ const layoutConfig = (props.options?.[index])?.layout;
457
+ const baseStyle = {
458
+ width: `${itemWidth.value}px`,
459
+ flexShrink: 0,
460
+ display: "flex",
461
+ flexDirection: "column"
462
+ };
463
+ if (layoutConfig?.width !== void 0) baseStyle.width = typeof layoutConfig.width === "number" ? `${layoutConfig.width}px` : layoutConfig.width;
464
+ if (layoutConfig?.style) Object.assign(baseStyle, layoutConfig.style);
465
+ return baseStyle;
466
+ };
467
+ return (_ctx, _cache) => {
468
+ return openBlock(), createElementBlock("div", {
469
+ class: "c-form-inline",
470
+ style: normalizeStyle(containerStyle.value)
471
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.formItems, (item, index) => {
472
+ return openBlock(), createElementBlock("div", {
473
+ key: getItemKey(item, index),
474
+ class: "c-form-inline-item",
475
+ style: normalizeStyle(getItemStyle(index))
476
+ }, [(openBlock(), createBlock(resolveDynamicComponent(item)))], 4);
477
+ }), 128))], 4);
478
+ };
479
+ }
480
+ });
481
+
482
+ //#endregion
483
+ //#region src/components/C_Form/layouts/Inline/index.vue
484
+ var Inline_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$7, [["__scopeId", "data-v-c4648e30"]]);
485
+
486
+ //#endregion
487
+ //#region src/components/C_Form/layouts/Grid/index.vue?vue&type=script&setup=true&lang.ts
488
+ const _hoisted_1$5 = { class: "c-grid-layout" };
489
+ const _hoisted_2$5 = { class: "config-header" };
490
+ const _hoisted_3$5 = { class: "config-controls" };
491
+ const _hoisted_4$5 = { class: "config-label" };
492
+ const _hoisted_5$5 = { class: "item-wrapper" };
493
+ var index_vue_vue_type_script_setup_true_lang_default$6 = /* @__PURE__ */ defineComponent({
494
+ __name: "index",
495
+ props: {
496
+ formItems: {},
497
+ layoutConfig: { default: () => ({}) },
498
+ options: { default: () => [] }
499
+ },
500
+ setup(__props, { expose: __expose }) {
501
+ const props = __props;
502
+ const isDev = ref(typeof import.meta !== "undefined" && !!import.meta.env?.DEV);
503
+ const internalConfig = reactive({
504
+ cols: 24,
505
+ gutter: 16,
506
+ responsive: true
507
+ });
508
+ const gridConfig = computed(() => props.layoutConfig?.grid || {});
509
+ const showConfigPanel = computed(() => gridConfig.value.showConfigPanel);
510
+ const showStats = computed(() => gridConfig.value.showStats);
511
+ const effectiveConfig = computed(() => ({
512
+ cols: showConfigPanel.value ? internalConfig.cols : gridConfig.value.cols ?? 24,
513
+ gutter: showConfigPanel.value ? internalConfig.gutter : gridConfig.value.gutter ?? 16,
514
+ responsive: showConfigPanel.value ? internalConfig.responsive : gridConfig.value.responsive ?? true
515
+ }));
516
+ const gridProps = computed(() => ({
517
+ cols: effectiveConfig.value.cols,
518
+ xGap: effectiveConfig.value.gutter,
519
+ yGap: effectiveConfig.value.gutter,
520
+ responsive: effectiveConfig.value.responsive ? "screen" : "self"
521
+ }));
522
+ const configControls = computed(() => [
523
+ {
524
+ key: "cols",
525
+ label: "列数",
526
+ component: "NInputNumber",
527
+ value: computed({
528
+ get: () => internalConfig.cols,
529
+ set: (val) => internalConfig.cols = val
530
+ }),
531
+ props: {
532
+ min: 1,
533
+ max: 48,
534
+ size: "small"
535
+ }
536
+ },
537
+ {
538
+ key: "gutter",
539
+ label: "间距",
540
+ component: "NInputNumber",
541
+ value: computed({
542
+ get: () => internalConfig.gutter,
543
+ set: (val) => internalConfig.gutter = val
544
+ }),
545
+ props: {
546
+ min: 0,
547
+ max: 48,
548
+ size: "small"
549
+ }
550
+ },
551
+ {
552
+ key: "responsive",
553
+ label: "响应式",
554
+ component: "NSwitch",
555
+ value: computed({
556
+ get: () => internalConfig.responsive,
557
+ set: (val) => internalConfig.responsive = val
558
+ }),
559
+ props: { size: "small" }
560
+ }
561
+ ]);
562
+ const statsText = computed(() => `列数: ${effectiveConfig.value.cols} | 间距: ${effectiveConfig.value.gutter}px | 项目: ${props.formItems.length}个`);
563
+ /**
564
+ * 获取表单项key
565
+ */
566
+ const getItemKey = (item, index) => String(item.key) || item.props?.path || `grid-item-${index}`;
567
+ /**
568
+ * 获取布局属性值
569
+ */
570
+ const getLayoutValue = (index, key) => {
571
+ const option = props.options?.[index]?.layout;
572
+ return option?.[key] ?? option?.grid?.[key];
573
+ };
574
+ /**
575
+ * 获取默认span值
576
+ */
577
+ const getDefaultSpan = () => {
578
+ const { cols } = effectiveConfig.value;
579
+ return cols <= 12 ? cols : cols <= 24 ? 12 : 8;
580
+ };
581
+ /**
582
+ * 获取网格项属性
583
+ */
584
+ const getItemProps = (index) => {
585
+ const span = getLayoutValue(index, "span");
586
+ const offset = getLayoutValue(index, "offset");
587
+ const suffix = getLayoutValue(index, "suffix");
588
+ return {
589
+ span: typeof span === "number" && span > 0 && span <= effectiveConfig.value.cols ? span : getDefaultSpan(),
590
+ offset: typeof offset === "number" && offset >= 0 && offset < effectiveConfig.value.cols ? offset : 0,
591
+ suffix: Boolean(suffix)
592
+ };
593
+ };
594
+ /**
595
+ * 配置变更事件
596
+ */
597
+ const emitConfigChange = () => {
598
+ if (isDev.value) console.log("Grid config updated:", toRaw(internalConfig));
599
+ };
600
+ watch(gridConfig, (config) => {
601
+ if (config.cols !== void 0) internalConfig.cols = config.cols;
602
+ if (config.gutter !== void 0) internalConfig.gutter = config.gutter;
603
+ if (config.responsive !== void 0) internalConfig.responsive = config.responsive;
604
+ }, { immediate: true });
605
+ if (isDev.value) watchEffect(() => {
606
+ const optionsCount = props.options.length;
607
+ const itemsCount = props.formItems.length;
608
+ if (optionsCount > 0 && optionsCount !== itemsCount) console.warn(`[GridLayout] 配置数量(${optionsCount})与表单项数量(${itemsCount})不匹配`);
609
+ });
610
+ __expose({
611
+ updateGridConfig: (config) => Object.assign(internalConfig, config),
612
+ getCurrentConfig: () => ({ ...effectiveConfig.value }),
613
+ getGridInfo: () => ({
614
+ ...effectiveConfig.value,
615
+ itemCount: props.formItems.length
616
+ })
617
+ });
618
+ return (_ctx, _cache) => {
619
+ const _component_NCard = resolveComponent("NCard");
620
+ const _component_NGridItem = resolveComponent("NGridItem");
621
+ const _component_NGrid = resolveComponent("NGrid");
622
+ const _component_NAlert = resolveComponent("NAlert");
623
+ return openBlock(), createElementBlock("div", _hoisted_1$5, [
624
+ createCommentVNode(" 配置面板 "),
625
+ showConfigPanel.value ? (openBlock(), createBlock(_component_NCard, {
626
+ key: 0,
627
+ size: "small",
628
+ bordered: false,
629
+ class: "config-panel"
630
+ }, {
631
+ header: withCtx(() => [createElementVNode("div", _hoisted_2$5, [createVNode(C_Icon_default, {
632
+ name: "mdi:view-grid",
633
+ size: 18
634
+ }), _cache[0] || (_cache[0] = createElementVNode("span", null, "网格配置", -1))])]),
635
+ default: withCtx(() => [createElementVNode("div", _hoisted_3$5, [(openBlock(true), createElementBlock(Fragment, null, renderList(configControls.value, (control) => {
636
+ return openBlock(), createElementBlock("div", {
637
+ key: control.key,
638
+ class: "config-item"
639
+ }, [createElementVNode("span", _hoisted_4$5, toDisplayString(control.label) + ":", 1), (openBlock(), createBlock(resolveDynamicComponent(control.component), mergeProps({
640
+ value: control.value,
641
+ "onUpdate:value": ($event) => control.value = $event
642
+ }, { ref_for: true }, control.props, { "onUpdate:value": emitConfigChange }), null, 16, ["value", "onUpdate:value"]))]);
643
+ }), 128))])]),
644
+ _: 1
645
+ })) : createCommentVNode("v-if", true),
646
+ createCommentVNode(" 网格容器 "),
647
+ createVNode(_component_NGrid, mergeProps(gridProps.value, { class: "grid-container" }), {
648
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.formItems, (item, index) => {
649
+ return openBlock(), createBlock(_component_NGridItem, mergeProps({ key: getItemKey(item, index) }, { ref_for: true }, getItemProps(index), { class: "grid-item" }), {
650
+ default: withCtx(() => [createElementVNode("div", _hoisted_5$5, [(openBlock(), createBlock(resolveDynamicComponent(item)))])]),
651
+ _: 2
652
+ }, 1040);
653
+ }), 128))]),
654
+ _: 1
655
+ }, 16),
656
+ createCommentVNode(" 统计信息 "),
657
+ showStats.value && isDev.value ? (openBlock(), createBlock(_component_NAlert, {
658
+ key: 1,
659
+ type: "info",
660
+ "show-icon": false,
661
+ size: "small",
662
+ class: "grid-stats"
663
+ }, {
664
+ header: withCtx(() => [createVNode(C_Icon_default, {
665
+ name: "mdi:information-outline",
666
+ size: 16
667
+ }), _cache[1] || (_cache[1] = createTextVNode(" 网格信息 ", -1))]),
668
+ default: withCtx(() => [createTextVNode(" " + toDisplayString(statsText.value), 1)]),
669
+ _: 1
670
+ })) : createCommentVNode("v-if", true)
671
+ ]);
672
+ };
673
+ }
674
+ });
675
+
676
+ //#endregion
677
+ //#region src/components/C_Form/layouts/Grid/index.vue
678
+ var Grid_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$6, [["__scopeId", "data-v-ec7e9532"]]);
679
+
680
+ //#endregion
681
+ //#region src/components/C_Form/layouts/Card/index.vue?vue&type=script&setup=true&lang.ts
682
+ const _hoisted_1$4 = { class: "c-form-card" };
683
+ const _hoisted_2$4 = { class: "flex items-center gap-2" };
684
+ const _hoisted_3$4 = { class: "config-controls" };
685
+ const _hoisted_4$4 = { class: "config-item" };
686
+ const _hoisted_5$4 = { class: "flex items-center gap-2" };
687
+ const _hoisted_6$4 = { class: "text-xs min-w-12" };
688
+ const _hoisted_7$3 = { class: "config-item" };
689
+ const _hoisted_8$2 = { class: "config-item" };
690
+ const _hoisted_9$1 = { class: "config-item" };
691
+ const _hoisted_10$1 = { class: "flex items-center gap-3" };
692
+ const _hoisted_11$1 = { class: "card-header" };
693
+ const _hoisted_12$1 = { class: "header-info" };
694
+ const _hoisted_13$1 = { class: "header-text" };
695
+ const _hoisted_14$1 = { key: 0 };
696
+ const _hoisted_15$1 = { class: "header-actions" };
697
+ const _hoisted_16$1 = { class: "field-stats" };
698
+ const _hoisted_17$1 = { class: "card-content" };
699
+ const _hoisted_18$1 = {
700
+ key: 0,
701
+ class: "progress-section"
702
+ };
703
+ const _hoisted_19$1 = { class: "action-content" };
704
+ const _hoisted_20$1 = { class: "status-summary" };
705
+ const _hoisted_21$1 = { class: "status-item" };
706
+ const _hoisted_22$1 = { class: "progress-display" };
707
+ const _hoisted_23$1 = { class: "text-sm min-w-12" };
708
+ const _hoisted_24$1 = { class: "action-buttons" };
709
+ var index_vue_vue_type_script_setup_true_lang_default$5 = /* @__PURE__ */ defineComponent({
710
+ __name: "index",
711
+ props: {
712
+ formItems: {},
713
+ layoutConfig: { default: () => ({}) },
714
+ options: { default: () => [] },
715
+ formData: { default: () => ({}) }
716
+ },
717
+ setup(__props) {
718
+ const props = __props;
719
+ const cardGap = ref(20);
720
+ const showIcons = ref(true);
721
+ const collapsible = ref(true);
722
+ const showProgress = ref(true);
723
+ const currentDirection = ref("vertical");
724
+ const collapsedGroups = ref({});
725
+ const groups = computed(() => {
726
+ return props.layoutConfig?.card?.groups || [];
727
+ });
728
+ const hasGroups = computed(() => {
729
+ return groups.value.length > 0;
730
+ });
731
+ const showLayoutConfig = computed(() => {
732
+ return props.layoutConfig?.card?.showLayoutConfig ?? true;
733
+ });
734
+ const showActionPanel = computed(() => {
735
+ return props.layoutConfig?.card?.showActionPanel ?? true;
736
+ });
737
+ const layoutClass = computed(() => {
738
+ if (!hasGroups.value) return "layout-single";
739
+ return `layout-${currentDirection.value}`;
740
+ });
741
+ const groupsWithItems = computed(() => {
742
+ if (!hasGroups.value) return [];
743
+ const groupMap = /* @__PURE__ */ new Map();
744
+ groups.value.forEach((group) => {
745
+ groupMap.set(group.key, []);
746
+ });
747
+ props.formItems.forEach((item, index) => {
748
+ const groupKey = (props.options?.[index])?.layout?.group || groups.value[0]?.key || "default";
749
+ if (!groupMap.has(groupKey)) groupMap.set(groupKey, []);
750
+ groupMap.get(groupKey).push(item);
751
+ });
752
+ return groups.value.map((groupConfig) => ({
753
+ config: groupConfig,
754
+ items: groupMap.get(groupConfig.key) || []
755
+ })).filter((group) => group.items.length > 0);
756
+ });
757
+ const allCollapsed = computed(() => {
758
+ const groupKeys = Object.keys(collapsedGroups.value);
759
+ return groupKeys.length > 0 && groupKeys.every((key) => collapsedGroups.value[key]);
760
+ });
761
+ const totalProgress = computed(() => {
762
+ if (!props.options || props.options.length === 0) return 0;
763
+ const filledCount = props.options.filter((option) => {
764
+ const value = props.formData?.[option.prop || ""];
765
+ return isFieldFilled(value);
766
+ }).length;
767
+ return Math.round(filledCount / props.options.length * 100);
768
+ });
769
+ const isFieldFilled = (value) => {
770
+ if (value === null || value === void 0) return false;
771
+ if (Array.isArray(value)) return value.length > 0;
772
+ if (typeof value === "string") return value.trim() !== "";
773
+ return true;
774
+ };
775
+ const getFilledCount = (group) => {
776
+ if (!props.options) return 0;
777
+ return props.options.filter((option) => option.layout?.group === group.config.key).filter((option) => {
778
+ const value = props.formData?.[option.prop || ""];
779
+ return isFieldFilled(value);
780
+ }).length;
781
+ };
782
+ const getGroupProgress = (group) => {
783
+ if (group.items.length === 0) return 0;
784
+ const filledCount = getFilledCount(group);
785
+ return Math.round(filledCount / group.items.length * 100);
786
+ };
787
+ const getDefaultIcon = (groupKey) => {
788
+ const iconMap = {
789
+ basic: "mdi:account",
790
+ contact: "mdi:phone",
791
+ preferences: "mdi:cog",
792
+ settings: "mdi:cog",
793
+ info: "mdi:information",
794
+ default: "mdi:form-select"
795
+ };
796
+ return iconMap[groupKey] || iconMap.default;
797
+ };
798
+ const toggleGroup = (groupKey) => {
799
+ collapsedGroups.value[groupKey] = !collapsedGroups.value[groupKey];
800
+ };
801
+ const toggleAllGroups = () => {
802
+ const shouldCollapse = !allCollapsed.value;
803
+ groups.value.forEach((group) => {
804
+ collapsedGroups.value[group.key] = shouldCollapse;
805
+ });
806
+ };
807
+ onMounted(() => {
808
+ const config = props.layoutConfig?.card;
809
+ if (config?.direction) currentDirection.value = config.direction;
810
+ if (config?.showProgress !== void 0) showProgress.value = config.showProgress;
811
+ groups.value.forEach((group) => {
812
+ collapsedGroups.value[group.key] = false;
813
+ });
814
+ });
815
+ return (_ctx, _cache) => {
816
+ const _component_NSlider = resolveComponent("NSlider");
817
+ const _component_NSwitch = resolveComponent("NSwitch");
818
+ const _component_NRadio = resolveComponent("NRadio");
819
+ const _component_NRadioGroup = resolveComponent("NRadioGroup");
820
+ const _component_NCard = resolveComponent("NCard");
821
+ const _component_NBadge = resolveComponent("NBadge");
822
+ const _component_NButton = resolveComponent("NButton");
823
+ const _component_NProgress = resolveComponent("NProgress");
824
+ return openBlock(), createElementBlock("div", _hoisted_1$4, [
825
+ createCommentVNode(" 布局配置面板 "),
826
+ hasGroups.value && showLayoutConfig.value ? (openBlock(), createBlock(_component_NCard, {
827
+ key: 0,
828
+ class: "layout-config-panel",
829
+ bordered: false
830
+ }, {
831
+ header: withCtx(() => [createElementVNode("div", _hoisted_2$4, [createVNode(C_Icon_default, {
832
+ name: "mdi:cog",
833
+ size: 18
834
+ }), _cache[4] || (_cache[4] = createElementVNode("span", null, "布局配置", -1))])]),
835
+ default: withCtx(() => [createElementVNode("div", _hoisted_3$4, [
836
+ createElementVNode("div", _hoisted_4$4, [_cache[5] || (_cache[5] = createElementVNode("span", null, "卡片间距", -1)), createElementVNode("div", _hoisted_5$4, [createVNode(_component_NSlider, {
837
+ value: cardGap.value,
838
+ "onUpdate:value": _cache[0] || (_cache[0] = ($event) => cardGap.value = $event),
839
+ min: 12,
840
+ max: 32,
841
+ step: 4,
842
+ class: "w-24"
843
+ }, null, 8, ["value"]), createElementVNode("span", _hoisted_6$4, toDisplayString(cardGap.value) + "px", 1)])]),
844
+ createElementVNode("div", _hoisted_7$3, [_cache[6] || (_cache[6] = createElementVNode("span", null, "显示图标", -1)), createVNode(_component_NSwitch, {
845
+ value: showIcons.value,
846
+ "onUpdate:value": _cache[1] || (_cache[1] = ($event) => showIcons.value = $event),
847
+ size: "small"
848
+ }, null, 8, ["value"])]),
849
+ createElementVNode("div", _hoisted_8$2, [_cache[7] || (_cache[7] = createElementVNode("span", null, "可折叠", -1)), createVNode(_component_NSwitch, {
850
+ value: collapsible.value,
851
+ "onUpdate:value": _cache[2] || (_cache[2] = ($event) => collapsible.value = $event),
852
+ size: "small"
853
+ }, null, 8, ["value"])]),
854
+ createElementVNode("div", _hoisted_9$1, [_cache[10] || (_cache[10] = createElementVNode("span", null, "布局方向", -1)), createVNode(_component_NRadioGroup, {
855
+ value: currentDirection.value,
856
+ "onUpdate:value": _cache[3] || (_cache[3] = ($event) => currentDirection.value = $event),
857
+ size: "small"
858
+ }, {
859
+ default: withCtx(() => [createVNode(_component_NRadio, { value: "vertical" }, {
860
+ default: withCtx(() => _cache[8] || (_cache[8] = [createTextVNode("垂直", -1)])),
861
+ _: 1,
862
+ __: [8]
863
+ }), createVNode(_component_NRadio, { value: "horizontal" }, {
864
+ default: withCtx(() => _cache[9] || (_cache[9] = [createTextVNode("水平", -1)])),
865
+ _: 1,
866
+ __: [9]
867
+ })]),
868
+ _: 1
869
+ }, 8, ["value"])])
870
+ ])]),
871
+ _: 1
872
+ })) : createCommentVNode("v-if", true),
873
+ createCommentVNode(" 表单内容区域 "),
874
+ createElementVNode("div", {
875
+ class: normalizeClass(["form-content", layoutClass.value]),
876
+ style: normalizeStyle({ gap: `${cardGap.value}px` })
877
+ }, [createCommentVNode(" 无分组时的单卡片模式 "), !hasGroups.value ? (openBlock(), createBlock(_component_NCard, {
878
+ key: 0,
879
+ class: "single-card",
880
+ hoverable: ""
881
+ }, {
882
+ header: withCtx(() => [createElementVNode("div", _hoisted_10$1, [showIcons.value ? (openBlock(), createBlock(C_Icon_default, {
883
+ key: 0,
884
+ name: "mdi:form-select",
885
+ size: 18,
886
+ style: { "color": "#3b82f6" }
887
+ })) : createCommentVNode("v-if", true), _cache[11] || (_cache[11] = createElementVNode("span", null, "表单信息", -1))])]),
888
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.formItems, (item) => {
889
+ return openBlock(), createBlock(resolveDynamicComponent(item), { key: item.key });
890
+ }), 128))]),
891
+ _: 1
892
+ })) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 有分组时的多卡片模式 "), (openBlock(true), createElementBlock(Fragment, null, renderList(groupsWithItems.value, (group) => {
893
+ return openBlock(), createBlock(_component_NCard, {
894
+ key: group.config.key,
895
+ class: normalizeClass(["group-card", [`${group.config.key}-card`, { collapsed: collapsible.value && collapsedGroups.value[group.config.key] }]]),
896
+ hoverable: ""
897
+ }, {
898
+ header: withCtx(() => [createElementVNode("div", _hoisted_11$1, [createElementVNode("div", _hoisted_12$1, [showIcons.value && group.config.icon ? (openBlock(), createBlock(C_Icon_default, {
899
+ key: 0,
900
+ name: group.config.icon,
901
+ size: 20,
902
+ class: "card-icon"
903
+ }, null, 8, ["name"])) : showIcons.value ? (openBlock(), createBlock(C_Icon_default, {
904
+ key: 1,
905
+ name: getDefaultIcon(group.config.key),
906
+ size: 20,
907
+ class: "card-icon"
908
+ }, null, 8, ["name"])) : createCommentVNode("v-if", true), createElementVNode("div", _hoisted_13$1, [createElementVNode("h3", null, toDisplayString(group.config.title), 1), group.config.description ? (openBlock(), createElementBlock("p", _hoisted_14$1, toDisplayString(group.config.description), 1)) : createCommentVNode("v-if", true)])]), createElementVNode("div", _hoisted_15$1, [
909
+ createCommentVNode(" 统计信息 "),
910
+ createElementVNode("div", _hoisted_16$1, [createVNode(_component_NBadge, {
911
+ value: group.items.length,
912
+ type: "info",
913
+ "show-zero": ""
914
+ }, null, 8, ["value"]), createVNode(_component_NBadge, {
915
+ value: `${getFilledCount(group)}/${group.items.length}`,
916
+ type: getFilledCount(group) === group.items.length ? "success" : "warning"
917
+ }, null, 8, ["value", "type"])]),
918
+ createCommentVNode(" 折叠按钮 "),
919
+ collapsible.value ? (openBlock(), createBlock(_component_NButton, {
920
+ key: 0,
921
+ quaternary: "",
922
+ circle: "",
923
+ size: "small",
924
+ onClick: ($event) => toggleGroup(group.config.key)
925
+ }, {
926
+ icon: withCtx(() => [createVNode(C_Icon_default, {
927
+ name: collapsedGroups.value[group.config.key] ? "mdi:chevron-down" : "mdi:chevron-up",
928
+ size: 18
929
+ }, null, 8, ["name"])]),
930
+ _: 2
931
+ }, 1032, ["onClick"])) : createCommentVNode("v-if", true)
932
+ ])])]),
933
+ default: withCtx(() => [withDirectives(createElementVNode("div", _hoisted_17$1, [
934
+ createCommentVNode(" 进度指示器 "),
935
+ showProgress.value ? (openBlock(), createElementBlock("div", _hoisted_18$1, [createVNode(_component_NProgress, {
936
+ percentage: getGroupProgress(group),
937
+ color: getGroupProgress(group) === 100 ? "#52c41a" : "#1890ff",
938
+ "show-indicator": false,
939
+ class: "mb-4"
940
+ }, null, 8, ["percentage", "color"])])) : createCommentVNode("v-if", true),
941
+ createCommentVNode(" 表单项 "),
942
+ (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (item) => {
943
+ return openBlock(), createBlock(resolveDynamicComponent(item), { key: item.key });
944
+ }), 128))
945
+ ], 512), [[vShow, !collapsedGroups.value[group.config.key]]])]),
946
+ _: 2
947
+ }, 1032, ["class"]);
948
+ }), 128))], 64))], 6),
949
+ createCommentVNode(" 统一操作面板 "),
950
+ hasGroups.value && showActionPanel.value ? (openBlock(), createBlock(_component_NCard, {
951
+ key: 1,
952
+ class: "action-panel",
953
+ bordered: false
954
+ }, {
955
+ default: withCtx(() => [createElementVNode("div", _hoisted_19$1, [createElementVNode("div", _hoisted_20$1, [createElementVNode("div", _hoisted_21$1, [_cache[12] || (_cache[12] = createElementVNode("span", { class: "label" }, "完成进度:", -1)), createElementVNode("div", _hoisted_22$1, [createVNode(_component_NProgress, {
956
+ percentage: totalProgress.value,
957
+ "show-indicator": false,
958
+ color: totalProgress.value === 100 ? "#52c41a" : "#1890ff",
959
+ class: "flex-1"
960
+ }, null, 8, ["percentage", "color"]), createElementVNode("span", _hoisted_23$1, toDisplayString(totalProgress.value) + "%", 1)])])]), createElementVNode("div", _hoisted_24$1, [collapsible.value ? (openBlock(), createBlock(_component_NButton, {
961
+ key: 0,
962
+ onClick: toggleAllGroups
963
+ }, {
964
+ icon: withCtx(() => [createVNode(C_Icon_default, {
965
+ name: allCollapsed.value ? "mdi:unfold-more-horizontal" : "mdi:unfold-less-horizontal",
966
+ size: 18
967
+ }, null, 8, ["name"])]),
968
+ default: withCtx(() => [createTextVNode(" " + toDisplayString(allCollapsed.value ? "展开全部" : "折叠全部"), 1)]),
969
+ _: 1
970
+ })) : createCommentVNode("v-if", true)])])]),
971
+ _: 1
972
+ })) : createCommentVNode("v-if", true)
973
+ ]);
974
+ };
975
+ }
976
+ });
977
+
978
+ //#endregion
979
+ //#region src/components/C_Form/layouts/Card/index.vue
980
+ var Card_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$5, [["__scopeId", "data-v-fff86989"]]);
981
+
982
+ //#endregion
983
+ //#region src/components/C_Form/layouts/Tabs/index.vue?vue&type=script&setup=true&lang.ts
984
+ const _hoisted_1$3 = { class: "c-form-tabs" };
985
+ const _hoisted_2$3 = {
986
+ key: 0,
987
+ class: "single-panel"
988
+ };
989
+ const _hoisted_3$3 = { class: "tabs-container" };
990
+ const _hoisted_4$3 = {
991
+ key: 0,
992
+ class: "tab-header"
993
+ };
994
+ const _hoisted_5$3 = { class: "tab-description" };
995
+ const _hoisted_6$3 = { class: "tab-form-items" };
996
+ const _hoisted_7$2 = {
997
+ key: 0,
998
+ class: "tabs-actions"
999
+ };
1000
+ var index_vue_vue_type_script_setup_true_lang_default$4 = /* @__PURE__ */ defineComponent({
1001
+ __name: "index",
1002
+ props: {
1003
+ formItems: {},
1004
+ layoutConfig: { default: () => ({}) },
1005
+ options: { default: () => [] }
1006
+ },
1007
+ emits: [
1008
+ "tab-change",
1009
+ "tab-before-change",
1010
+ "tab-validate",
1011
+ "tab-close",
1012
+ "tab-add"
1013
+ ],
1014
+ setup(__props, { expose: __expose, emit: __emit }) {
1015
+ const props = __props;
1016
+ const emit = __emit;
1017
+ const currentTab = ref("");
1018
+ const tabValidationStatus = reactive({});
1019
+ const getDefaultTabsConfig = () => ({
1020
+ tabs: [],
1021
+ type: "line",
1022
+ size: "medium",
1023
+ placement: "top",
1024
+ animated: true,
1025
+ closable: false,
1026
+ addable: false,
1027
+ showTabHeader: true,
1028
+ showActions: false,
1029
+ showCount: false,
1030
+ validateBeforeSwitch: false,
1031
+ defaultTab: ""
1032
+ });
1033
+ const tabsConfig = computed(() => {
1034
+ const defaultConfig = getDefaultTabsConfig();
1035
+ const userConfig = props.layoutConfig?.tabs || {};
1036
+ return {
1037
+ ...defaultConfig,
1038
+ ...userConfig
1039
+ };
1040
+ });
1041
+ const hasTabs = computed(() => {
1042
+ return tabsConfig.value.tabs.length > 0;
1043
+ });
1044
+ const tabsWithItems = computed(() => {
1045
+ if (!hasTabs.value) return [];
1046
+ const tabMap = /* @__PURE__ */ new Map();
1047
+ tabsConfig.value.tabs.forEach((tab) => {
1048
+ tabMap.set(tab.key, []);
1049
+ });
1050
+ props.formItems.forEach((item, index) => {
1051
+ const tabKey = (props.options?.[index])?.layout?.tab || tabsConfig.value.tabs[0]?.key || "default";
1052
+ if (!tabMap.has(tabKey)) tabMap.set(tabKey, []);
1053
+ tabMap.get(tabKey).push(item);
1054
+ });
1055
+ return tabsConfig.value.tabs.map((tabConfig) => ({
1056
+ config: tabConfig,
1057
+ items: tabMap.get(tabConfig.key) || []
1058
+ }));
1059
+ });
1060
+ const getItemKey = (item, index) => {
1061
+ if (item.key != null) return String(item.key);
1062
+ const itemProps = item.props;
1063
+ if (itemProps?.path) return itemProps.path;
1064
+ return `tab-item-${index}`;
1065
+ };
1066
+ const validateCurrentTab = async () => {
1067
+ if (!currentTab.value) return true;
1068
+ try {
1069
+ emit("tab-validate", currentTab.value);
1070
+ const valid = true;
1071
+ tabValidationStatus[currentTab.value] = valid;
1072
+ return valid;
1073
+ } catch (error) {
1074
+ console.error("[Tabs Layout] 标签验证失败:", error);
1075
+ tabValidationStatus[currentTab.value] = false;
1076
+ return false;
1077
+ }
1078
+ };
1079
+ const switchToTab = async (targetTab) => {
1080
+ if (!targetTab || targetTab === currentTab.value) return true;
1081
+ if (!tabsWithItems.value.some((tab) => tab.config.key === targetTab)) return false;
1082
+ try {
1083
+ if (tabsConfig.value.validateBeforeSwitch && currentTab.value) {
1084
+ if (!await validateCurrentTab()) return false;
1085
+ }
1086
+ if (currentTab.value) emit("tab-before-change", currentTab.value, targetTab);
1087
+ currentTab.value = targetTab;
1088
+ emit("tab-change", targetTab, tabsWithItems.value.findIndex((tab) => tab.config.key === targetTab));
1089
+ return true;
1090
+ } catch (error) {
1091
+ console.error("[Tabs Layout] 标签切换失败:", error);
1092
+ return false;
1093
+ }
1094
+ };
1095
+ const handleTabChange = (tabKey) => {
1096
+ switchToTab(tabKey);
1097
+ };
1098
+ const handleTabClose = (tabKey) => {
1099
+ emit("tab-close", tabKey);
1100
+ };
1101
+ const handleTabAdd = () => {
1102
+ emit("tab-add");
1103
+ };
1104
+ const initializeCurrentTab = () => {
1105
+ if (!hasTabs.value || tabsWithItems.value.length === 0) return;
1106
+ const { defaultTab } = tabsConfig.value;
1107
+ const targetTab = defaultTab || tabsWithItems.value[0]?.config.key;
1108
+ if (targetTab && targetTab !== currentTab.value) {
1109
+ currentTab.value = targetTab;
1110
+ nextTick(() => {
1111
+ const tabIndex = tabsWithItems.value.findIndex((tab) => tab.config.key === targetTab);
1112
+ if (tabIndex >= 0) emit("tab-change", targetTab, tabIndex);
1113
+ });
1114
+ }
1115
+ };
1116
+ onMounted(() => {
1117
+ initializeCurrentTab();
1118
+ });
1119
+ watch(computed(() => tabsConfig.value.tabs.map((t) => t.key).join(",")), () => {
1120
+ if (currentTab.value && !tabsConfig.value.tabs.some((tab) => tab.key === currentTab.value)) initializeCurrentTab();
1121
+ });
1122
+ watch(() => tabsConfig.value.defaultTab, (newDefaultTab) => {
1123
+ if (newDefaultTab && newDefaultTab !== currentTab.value) switchToTab(newDefaultTab);
1124
+ });
1125
+ __expose({
1126
+ switchToTab,
1127
+ validateCurrentTab,
1128
+ currentTab: readonly(currentTab),
1129
+ totalTabs: computed(() => tabsWithItems.value.length),
1130
+ tabsWithItems: readonly(tabsWithItems)
1131
+ });
1132
+ return (_ctx, _cache) => {
1133
+ const _component_NBadge = resolveComponent("NBadge");
1134
+ const _component_NSpace = resolveComponent("NSpace");
1135
+ const _component_NEmpty = resolveComponent("NEmpty");
1136
+ const _component_NTabPane = resolveComponent("NTabPane");
1137
+ const _component_NTabs = resolveComponent("NTabs");
1138
+ const _component_NButton = resolveComponent("NButton");
1139
+ return openBlock(), createElementBlock("div", _hoisted_1$3, [createCommentVNode(" 无标签配置时的单一面板模式 "), !hasTabs.value ? (openBlock(), createElementBlock("div", _hoisted_2$3, [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.formItems, (item, index) => {
1140
+ return openBlock(), createBlock(resolveDynamicComponent(item), { key: getItemKey(item, index) });
1141
+ }), 128))])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 有标签配置时的分标签模式 "), createElementVNode("div", _hoisted_3$3, [
1142
+ createVNode(_component_NTabs, {
1143
+ value: currentTab.value,
1144
+ "onUpdate:value": [_cache[0] || (_cache[0] = ($event) => currentTab.value = $event), handleTabChange],
1145
+ type: tabsConfig.value.type,
1146
+ size: tabsConfig.value.size,
1147
+ placement: tabsConfig.value.placement,
1148
+ animated: tabsConfig.value.animated,
1149
+ closable: tabsConfig.value.closable,
1150
+ addable: tabsConfig.value.addable,
1151
+ class: "form-tabs",
1152
+ onClose: handleTabClose,
1153
+ onAdd: handleTabAdd
1154
+ }, {
1155
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(tabsWithItems.value, (tab) => {
1156
+ return openBlock(), createBlock(_component_NTabPane, {
1157
+ key: tab.config.key,
1158
+ name: tab.config.key,
1159
+ tab: tab.config.title,
1160
+ disabled: tab.config.disabled,
1161
+ closable: tab.config.closable
1162
+ }, {
1163
+ tab: withCtx(() => [createVNode(_component_NSpace, {
1164
+ align: "center",
1165
+ size: 8
1166
+ }, {
1167
+ default: withCtx(() => [
1168
+ tab.config.icon ? (openBlock(), createBlock(C_Icon_default, {
1169
+ key: 0,
1170
+ name: tab.config.icon,
1171
+ size: 16,
1172
+ class: "tab-icon"
1173
+ }, null, 8, ["name"])) : createCommentVNode("v-if", true),
1174
+ createElementVNode("span", null, toDisplayString(tab.config.title), 1),
1175
+ tabsConfig.value.showCount ? (openBlock(), createBlock(_component_NBadge, {
1176
+ key: 1,
1177
+ value: tab.items.length,
1178
+ max: 99,
1179
+ show: tab.items.length > 0,
1180
+ type: "info"
1181
+ }, null, 8, ["value", "show"])) : createCommentVNode("v-if", true)
1182
+ ]),
1183
+ _: 2
1184
+ }, 1024)]),
1185
+ default: withCtx(() => [
1186
+ tabsConfig.value.showTabHeader && tab.config.description ? (openBlock(), createElementBlock("div", _hoisted_4$3, [createElementVNode("p", _hoisted_5$3, toDisplayString(tab.config.description), 1)])) : createCommentVNode("v-if", true),
1187
+ createElementVNode("div", _hoisted_6$3, [(openBlock(true), createElementBlock(Fragment, null, renderList(tab.items, (item, itemIndex) => {
1188
+ return openBlock(), createBlock(resolveDynamicComponent(item), { key: getItemKey(item, itemIndex) });
1189
+ }), 128))]),
1190
+ tab.items.length === 0 ? (openBlock(), createBlock(_component_NEmpty, {
1191
+ key: 1,
1192
+ description: "暂无表单项",
1193
+ class: "tab-empty"
1194
+ })) : createCommentVNode("v-if", true)
1195
+ ]),
1196
+ _: 2
1197
+ }, 1032, [
1198
+ "name",
1199
+ "tab",
1200
+ "disabled",
1201
+ "closable"
1202
+ ]);
1203
+ }), 128))]),
1204
+ _: 1
1205
+ }, 8, [
1206
+ "value",
1207
+ "type",
1208
+ "size",
1209
+ "placement",
1210
+ "animated",
1211
+ "closable",
1212
+ "addable"
1213
+ ]),
1214
+ createCommentVNode(" 标签页操作按钮 "),
1215
+ tabsConfig.value.showActions ? (openBlock(), createElementBlock("div", _hoisted_7$2, [createVNode(_component_NSpace, { justify: "space-between" }, {
1216
+ default: withCtx(() => [createVNode(_component_NSpace, null, {
1217
+ default: withCtx(() => [tabsConfig.value.validateBeforeSwitch ? (openBlock(), createBlock(_component_NButton, {
1218
+ key: 0,
1219
+ type: "primary",
1220
+ size: "small",
1221
+ onClick: validateCurrentTab
1222
+ }, {
1223
+ default: withCtx(() => [createVNode(C_Icon_default, {
1224
+ name: "carbon:star-check",
1225
+ size: 14,
1226
+ style: { "margin-right": "4px" }
1227
+ }), _cache[1] || (_cache[1] = createTextVNode(" 验证当前标签 ", -1))]),
1228
+ _: 1,
1229
+ __: [1]
1230
+ })) : createCommentVNode("v-if", true)]),
1231
+ _: 1
1232
+ }), createVNode(_component_NSpace, null, {
1233
+ default: withCtx(() => [renderSlot(_ctx.$slots, "tab-actions", {
1234
+ currentTab: currentTab.value,
1235
+ totalTabs: tabsWithItems.value.length,
1236
+ validateTab: validateCurrentTab,
1237
+ switchToTab
1238
+ }, void 0, true)]),
1239
+ _: 3
1240
+ })]),
1241
+ _: 3
1242
+ })])) : createCommentVNode("v-if", true)
1243
+ ])], 2112))]);
1244
+ };
1245
+ }
1246
+ });
1247
+
1248
+ //#endregion
1249
+ //#region src/components/C_Form/layouts/Tabs/index.vue
1250
+ var Tabs_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$4, [["__scopeId", "data-v-5a6ef2b7"]]);
1251
+
1252
+ //#endregion
1253
+ //#region src/components/C_Form/layouts/Steps/index.vue?vue&type=script&setup=true&lang.ts
1254
+ const _hoisted_1$2 = { class: "c-form-steps" };
1255
+ const _hoisted_2$2 = {
1256
+ key: 0,
1257
+ class: "single-panel"
1258
+ };
1259
+ const _hoisted_3$2 = { class: "steps-container" };
1260
+ const _hoisted_4$2 = {
1261
+ key: 0,
1262
+ class: "step-header"
1263
+ };
1264
+ const _hoisted_5$2 = { class: "step-title" };
1265
+ const _hoisted_6$2 = {
1266
+ key: 0,
1267
+ class: "step-description"
1268
+ };
1269
+ const _hoisted_7$1 = { class: "step-form-items" };
1270
+ const _hoisted_8$1 = { class: "steps-actions" };
1271
+ var index_vue_vue_type_script_setup_true_lang_default$3 = /* @__PURE__ */ defineComponent({
1272
+ __name: "index",
1273
+ props: {
1274
+ formItems: {},
1275
+ layoutConfig: { default: () => ({}) },
1276
+ options: { default: () => [] }
1277
+ },
1278
+ emits: [
1279
+ "step-change",
1280
+ "step-before-change",
1281
+ "step-validate"
1282
+ ],
1283
+ setup(__props, { expose: __expose, emit: __emit }) {
1284
+ const props = __props;
1285
+ const emit = __emit;
1286
+ const currentStep = ref(0);
1287
+ const loading = ref(false);
1288
+ const stepValidationStatus = reactive({});
1289
+ const stepsConfig = computed(() => {
1290
+ const config = props.layoutConfig?.steps || {};
1291
+ return {
1292
+ steps: config.steps || [],
1293
+ vertical: config.vertical || false,
1294
+ size: config.size || "medium",
1295
+ showStepHeader: config.showStepHeader !== false,
1296
+ validateBeforeNext: config.validateBeforeNext || false,
1297
+ prevButtonText: config.prevButtonText || "上一步",
1298
+ nextButtonText: config.nextButtonText || "下一步",
1299
+ defaultStep: config.defaultStep || 0
1300
+ };
1301
+ });
1302
+ const hasSteps = computed(() => {
1303
+ return stepsConfig.value.steps.length > 0;
1304
+ });
1305
+ const stepsWithItems = computed(() => {
1306
+ if (!hasSteps.value) return [];
1307
+ const stepMap = /* @__PURE__ */ new Map();
1308
+ stepsConfig.value.steps.forEach((step) => {
1309
+ stepMap.set(step.key, []);
1310
+ });
1311
+ props.formItems.forEach((item, index) => {
1312
+ const stepKey = (props.options?.[index])?.layout?.step || stepsConfig.value.steps[0]?.key || "default";
1313
+ if (!stepMap.has(stepKey)) stepMap.set(stepKey, []);
1314
+ stepMap.get(stepKey).push(item);
1315
+ });
1316
+ return stepsConfig.value.steps.map((stepConfig) => ({
1317
+ config: stepConfig,
1318
+ items: stepMap.get(stepConfig.key) || []
1319
+ })).filter((step) => step.items.length > 0);
1320
+ });
1321
+ const stepStatus = computed(() => {
1322
+ for (let i = 0; i <= currentStep.value; i++) if (stepValidationStatus[i] === false) return "error";
1323
+ return "process";
1324
+ });
1325
+ const isFirstStep = computed(() => {
1326
+ return currentStep.value === 0;
1327
+ });
1328
+ const isLastStep = computed(() => {
1329
+ return currentStep.value === stepsWithItems.value.length - 1;
1330
+ });
1331
+ const getItemKey = (item, index) => {
1332
+ if (item.key != null) return String(item.key);
1333
+ const itemProps = item.props;
1334
+ if (itemProps?.path) return itemProps.path;
1335
+ return `step-item-${index}`;
1336
+ };
1337
+ const validateCurrentStep = async () => {
1338
+ try {
1339
+ const valid = await Promise.resolve(emit("step-validate", currentStep.value)) !== false;
1340
+ stepValidationStatus[currentStep.value] = valid;
1341
+ return valid;
1342
+ } catch (error) {
1343
+ console.error("[Steps Layout] 步骤验证失败:", error);
1344
+ stepValidationStatus[currentStep.value] = false;
1345
+ return false;
1346
+ }
1347
+ };
1348
+ const switchToStep = async (targetStep, needValidation = false) => {
1349
+ if (targetStep < 0 || targetStep >= stepsWithItems.value.length) return false;
1350
+ if (targetStep === currentStep.value) return true;
1351
+ try {
1352
+ loading.value = true;
1353
+ if (needValidation && stepsConfig.value.validateBeforeNext) {
1354
+ if (!await validateCurrentStep()) return false;
1355
+ }
1356
+ await emit("step-before-change", currentStep.value, targetStep);
1357
+ currentStep.value = targetStep;
1358
+ emit("step-change", currentStep.value, stepsWithItems.value[currentStep.value].config.key);
1359
+ return true;
1360
+ } catch (error) {
1361
+ console.error("[Steps Layout] 步骤切换失败:", error);
1362
+ return false;
1363
+ } finally {
1364
+ loading.value = false;
1365
+ }
1366
+ };
1367
+ const handleNextStep = async () => {
1368
+ await switchToStep(currentStep.value + 1, true);
1369
+ };
1370
+ const handlePreviousStep = () => {
1371
+ switchToStep(currentStep.value - 1);
1372
+ };
1373
+ const goToStep = async (stepIndex) => {
1374
+ if (stepsWithItems.value[stepIndex]?.config.disabled) return;
1375
+ await switchToStep(stepIndex, stepIndex > currentStep.value);
1376
+ };
1377
+ const initializeCurrentStep = () => {
1378
+ if (!hasSteps.value || stepsWithItems.value.length === 0) return;
1379
+ const { defaultStep } = stepsConfig.value;
1380
+ currentStep.value = defaultStep >= 0 && defaultStep < stepsWithItems.value.length && !stepsWithItems.value[defaultStep]?.config.disabled ? defaultStep : 0;
1381
+ };
1382
+ watch(computed(() => stepsConfig.value.steps.map((s) => s.key).join(",")), () => {
1383
+ initializeCurrentStep();
1384
+ });
1385
+ onMounted(() => {
1386
+ initializeCurrentStep();
1387
+ });
1388
+ __expose({
1389
+ nextStep: handleNextStep,
1390
+ previousStep: handlePreviousStep,
1391
+ goToStep,
1392
+ validateCurrentStep,
1393
+ currentStep: readonly(currentStep),
1394
+ totalSteps: computed(() => stepsWithItems.value.length)
1395
+ });
1396
+ return (_ctx, _cache) => {
1397
+ const _component_NStep = resolveComponent("NStep");
1398
+ const _component_NSteps = resolveComponent("NSteps");
1399
+ const _component_NCard = resolveComponent("NCard");
1400
+ const _component_NButton = resolveComponent("NButton");
1401
+ const _component_NSpace = resolveComponent("NSpace");
1402
+ return openBlock(), createElementBlock("div", _hoisted_1$2, [createCommentVNode(" 无步骤配置时的单一面板模式 "), !hasSteps.value ? (openBlock(), createElementBlock("div", _hoisted_2$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.formItems, (item, index) => {
1403
+ return openBlock(), createBlock(resolveDynamicComponent(item), { key: getItemKey(item, index) });
1404
+ }), 128))])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 有步骤配置时的分步骤模式 "), createElementVNode("div", _hoisted_3$2, [
1405
+ createCommentVNode(" 步骤指示器 "),
1406
+ createVNode(_component_NSteps, {
1407
+ current: currentStep.value + 1,
1408
+ status: stepStatus.value,
1409
+ size: stepsConfig.value.size,
1410
+ vertical: stepsConfig.value.vertical,
1411
+ class: "steps-indicator"
1412
+ }, {
1413
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(stepsWithItems.value, (step) => {
1414
+ return openBlock(), createBlock(_component_NStep, {
1415
+ key: step.config.key,
1416
+ title: step.config.title,
1417
+ description: step.config.description,
1418
+ disabled: step.config.disabled
1419
+ }, null, 8, [
1420
+ "title",
1421
+ "description",
1422
+ "disabled"
1423
+ ]);
1424
+ }), 128))]),
1425
+ _: 1
1426
+ }, 8, [
1427
+ "current",
1428
+ "status",
1429
+ "size",
1430
+ "vertical"
1431
+ ]),
1432
+ createCommentVNode(" 步骤内容区域 "),
1433
+ createVNode(_component_NCard, {
1434
+ class: "steps-content",
1435
+ bordered: false
1436
+ }, {
1437
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(stepsWithItems.value, (step, index) => {
1438
+ return withDirectives((openBlock(), createElementBlock("div", {
1439
+ key: step.config.key,
1440
+ class: "step-panel"
1441
+ }, [
1442
+ createCommentVNode(" 步骤标题和描述 "),
1443
+ stepsConfig.value.showStepHeader ? (openBlock(), createElementBlock("div", _hoisted_4$2, [createElementVNode("h3", _hoisted_5$2, toDisplayString(step.config.title), 1), step.config.description ? (openBlock(), createElementBlock("p", _hoisted_6$2, toDisplayString(step.config.description), 1)) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true),
1444
+ createCommentVNode(" 步骤内的表单项 "),
1445
+ createElementVNode("div", _hoisted_7$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(step.items, (item, itemIndex) => {
1446
+ return openBlock(), createBlock(resolveDynamicComponent(item), { key: getItemKey(item, itemIndex) });
1447
+ }), 128))])
1448
+ ])), [[vShow, currentStep.value === index]]);
1449
+ }), 128))]),
1450
+ _: 1
1451
+ }),
1452
+ createCommentVNode(" 步骤操作按钮 "),
1453
+ createElementVNode("div", _hoisted_8$1, [createVNode(_component_NSpace, { justify: "space-between" }, {
1454
+ default: withCtx(() => [
1455
+ currentStep.value > 0 ? (openBlock(), createBlock(_component_NButton, {
1456
+ key: 0,
1457
+ disabled: loading.value,
1458
+ onClick: handlePreviousStep
1459
+ }, {
1460
+ default: withCtx(() => [createVNode(C_Icon_default, {
1461
+ name: "mdi:chevron-left-first",
1462
+ size: 16,
1463
+ style: { "margin-right": "4px" }
1464
+ }), createTextVNode(" " + toDisplayString(stepsConfig.value.prevButtonText), 1)]),
1465
+ _: 1
1466
+ }, 8, ["disabled"])) : createCommentVNode("v-if", true),
1467
+ _cache[0] || (_cache[0] = createElementVNode("div", null, null, -1)),
1468
+ createVNode(_component_NSpace, null, {
1469
+ default: withCtx(() => [currentStep.value < stepsWithItems.value.length - 1 ? (openBlock(), createBlock(_component_NButton, {
1470
+ key: 0,
1471
+ type: "primary",
1472
+ loading: loading.value,
1473
+ onClick: handleNextStep
1474
+ }, {
1475
+ default: withCtx(() => [createTextVNode(toDisplayString(stepsConfig.value.nextButtonText) + " ", 1), createVNode(C_Icon_default, {
1476
+ name: "mdi:chevron-right-last",
1477
+ size: 16,
1478
+ style: { "margin-left": "4px" }
1479
+ })]),
1480
+ _: 1
1481
+ }, 8, ["loading"])) : createCommentVNode("v-if", true), renderSlot(_ctx.$slots, "step-actions", {
1482
+ currentStep: currentStep.value,
1483
+ totalSteps: stepsWithItems.value.length,
1484
+ isFirstStep: isFirstStep.value,
1485
+ isLastStep: isLastStep.value,
1486
+ nextStep: handleNextStep,
1487
+ previousStep: handlePreviousStep,
1488
+ goToStep
1489
+ }, void 0, true)]),
1490
+ _: 3
1491
+ })
1492
+ ]),
1493
+ _: 3,
1494
+ __: [0]
1495
+ })])
1496
+ ])], 2112))]);
1497
+ };
1498
+ }
1499
+ });
1500
+
1501
+ //#endregion
1502
+ //#region src/components/C_Form/layouts/Steps/index.vue
1503
+ var Steps_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$3, [["__scopeId", "data-v-c0439a30"]]);
1504
+
1505
+ //#endregion
1506
+ //#region src/components/C_Form/composables/useDynamicFormState.ts
1507
+ /**
1508
+ * @description 动态表单状态管理组合式函数
1509
+ * @module useDynamicFormState
1510
+ * @Migration: naive-ui-components 组件库迁移版本
1511
+ * @returns 包含动态表单状态和操作方法的对象
1512
+ */
1513
+ /**
1514
+ * @description 默认表单配置
1515
+ */
1516
+ const DEFAULT_CONFIG = {
1517
+ maxFields: 20,
1518
+ autoSave: false,
1519
+ enableSort: true,
1520
+ showControls: true,
1521
+ showItemControls: true
1522
+ };
1523
+ /**
1524
+ * @description 可用的字段类型选项
1525
+ */
1526
+ const FIELD_TYPE_OPTIONS = [
1527
+ {
1528
+ label: "文本输入",
1529
+ value: "input"
1530
+ },
1531
+ {
1532
+ label: "数字输入",
1533
+ value: "inputNumber"
1534
+ },
1535
+ {
1536
+ label: "多行文本",
1537
+ value: "textarea"
1538
+ },
1539
+ {
1540
+ label: "下拉选择",
1541
+ value: "select"
1542
+ },
1543
+ {
1544
+ label: "开关切换",
1545
+ value: "switch"
1546
+ },
1547
+ {
1548
+ label: "评分组件",
1549
+ value: "rate"
1550
+ }
1551
+ ];
1552
+ /**
1553
+ * @description 创建和管理动态表单状态
1554
+ * @returns 包含状态和方法的对象
1555
+ */
1556
+ const useDynamicFormState = () => {
1557
+ /**
1558
+ * @description 响应式表单状态
1559
+ */
1560
+ const state = reactive({
1561
+ config: { ...DEFAULT_CONFIG },
1562
+ baseFields: [],
1563
+ dynamicFields: [],
1564
+ hiddenFieldIds: /* @__PURE__ */ new Set(),
1565
+ fieldCounter: 0,
1566
+ isInitialized: false
1567
+ });
1568
+ /**
1569
+ * @description 计算所有字段(基础字段+动态字段)
1570
+ */
1571
+ const allFields = computed(() => [...state.baseFields, ...state.dynamicFields.map((field) => ({
1572
+ ...field,
1573
+ show: !state.hiddenFieldIds.has(field.prop)
1574
+ }))]);
1575
+ /**
1576
+ * @description 计算可见字段
1577
+ */
1578
+ const visibleFields = computed(() => allFields.value.filter((field) => field.show !== false));
1579
+ /**
1580
+ * @description 计算动态字段数量
1581
+ */
1582
+ const dynamicFieldsCount = computed(() => state.dynamicFields.length);
1583
+ /**
1584
+ * @description 计算隐藏字段数量
1585
+ */
1586
+ const hiddenFieldsCount = computed(() => state.hiddenFieldIds.size);
1587
+ /**
1588
+ * @description 是否可以添加更多字段
1589
+ */
1590
+ const canAddMoreFields = computed(() => state.dynamicFields.length < state.config.maxFields);
1591
+ /**
1592
+ * @description 是否所有字段都可见
1593
+ */
1594
+ const allVisible = computed(() => state.hiddenFieldIds.size === 0);
1595
+ /**
1596
+ * @description 添加动态字段
1597
+ * @param config - 字段配置
1598
+ */
1599
+ const addField = (config = {}) => {
1600
+ if (!canAddMoreFields.value) {
1601
+ console.warn("[useDynamicFormState] 已达到最大字段数量限制");
1602
+ return;
1603
+ }
1604
+ state.fieldCounter++;
1605
+ const defaultType = config.type || FIELD_TYPE_OPTIONS[Math.floor(Math.random() * FIELD_TYPE_OPTIONS.length)].value;
1606
+ const newField = {
1607
+ id: `dynamic_field_${state.fieldCounter}`,
1608
+ type: defaultType,
1609
+ prop: config.prop || `dynamic_${state.fieldCounter}`,
1610
+ label: config.label || `动态字段 ${state.fieldCounter}`,
1611
+ placeholder: config.placeholder || `请输入${config.label || "内容"}`,
1612
+ visible: true,
1613
+ removable: true,
1614
+ created: Date.now(),
1615
+ layout: config.layout || { span: 12 },
1616
+ ...config
1617
+ };
1618
+ state.dynamicFields.push(newField);
1619
+ console.log("[useDynamicFormState] 添加字段:", newField);
1620
+ };
1621
+ /**
1622
+ * @description 移除动态字段
1623
+ * @param index - 可选,要移除的字段索引,默认移除最后一个
1624
+ */
1625
+ const removeField = (index) => {
1626
+ if (state.dynamicFields.length === 0) {
1627
+ console.warn("[useDynamicFormState] 没有可移除的动态字段");
1628
+ return;
1629
+ }
1630
+ const targetIndex = index ?? state.dynamicFields.length - 1;
1631
+ if (targetIndex < 0 || targetIndex >= state.dynamicFields.length) {
1632
+ console.warn("[useDynamicFormState] 字段索引超出范围");
1633
+ return;
1634
+ }
1635
+ const removed = state.dynamicFields.splice(targetIndex, 1)[0];
1636
+ if (removed) {
1637
+ state.hiddenFieldIds.delete(removed.prop);
1638
+ console.log("[useDynamicFormState] 移除字段:", removed.prop);
1639
+ }
1640
+ };
1641
+ /**
1642
+ * @description 清空所有动态字段
1643
+ */
1644
+ const clearDynamicFields = () => {
1645
+ console.log("[useDynamicFormState] 清空动态字段:", state.dynamicFields.length);
1646
+ state.dynamicFields.forEach((field) => state.hiddenFieldIds.delete(field.prop));
1647
+ state.dynamicFields.length = 0;
1648
+ state.fieldCounter = 0;
1649
+ };
1650
+ /**
1651
+ * @description 切换字段可见性
1652
+ * @param fieldId - 字段ID
1653
+ */
1654
+ const toggleFieldVisibility = (fieldId) => {
1655
+ if (state.hiddenFieldIds.has(fieldId)) {
1656
+ state.hiddenFieldIds.delete(fieldId);
1657
+ console.log("[useDynamicFormState] 显示字段:", fieldId);
1658
+ } else {
1659
+ state.hiddenFieldIds.add(fieldId);
1660
+ console.log("[useDynamicFormState] 隐藏字段:", fieldId);
1661
+ }
1662
+ };
1663
+ /**
1664
+ * @description 切换所有字段可见性
1665
+ */
1666
+ const toggleAllVisibility = () => {
1667
+ if (allVisible.value) {
1668
+ state.dynamicFields.forEach((field) => {
1669
+ state.hiddenFieldIds.add(field.prop);
1670
+ });
1671
+ console.log("[useDynamicFormState] 隐藏所有动态字段");
1672
+ } else {
1673
+ state.hiddenFieldIds.clear();
1674
+ console.log("[useDynamicFormState] 显示所有字段");
1675
+ }
1676
+ };
1677
+ /**
1678
+ * @description 更新表单配置
1679
+ * @param newConfig - 新的配置对象
1680
+ */
1681
+ const updateConfig = (newConfig) => {
1682
+ Object.assign(state.config, newConfig);
1683
+ console.log("[useDynamicFormState] 更新配置:", newConfig);
1684
+ };
1685
+ /**
1686
+ * @description 导出当前表单配置
1687
+ * @returns JSON格式的配置字符串
1688
+ */
1689
+ const exportConfig = () => {
1690
+ const config = {
1691
+ baseFields: state.baseFields,
1692
+ dynamicFields: state.dynamicFields,
1693
+ config: state.config,
1694
+ hiddenFields: Array.from(state.hiddenFieldIds),
1695
+ timestamp: Date.now()
1696
+ };
1697
+ return JSON.stringify(config, null, 2);
1698
+ };
1699
+ /**
1700
+ * @description 初始化表单状态
1701
+ * @param baseFields - 基础字段配置
1702
+ * @param config - 可选,表单配置
1703
+ */
1704
+ const initialize = (baseFields, config = {}) => {
1705
+ console.log("[useDynamicFormState] 初始化状态:", {
1706
+ baseFieldsCount: baseFields.length,
1707
+ config
1708
+ });
1709
+ state.baseFields = [...baseFields];
1710
+ Object.assign(state.config, config);
1711
+ state.isInitialized = true;
1712
+ console.log("[useDynamicFormState] 初始化完成");
1713
+ };
1714
+ return {
1715
+ state: readonly(state),
1716
+ allFields,
1717
+ visibleFields,
1718
+ dynamicFieldsCount,
1719
+ hiddenFieldsCount,
1720
+ canAddMoreFields,
1721
+ allVisible,
1722
+ FIELD_TYPE_OPTIONS,
1723
+ addField,
1724
+ removeField,
1725
+ clearDynamicFields,
1726
+ toggleFieldVisibility,
1727
+ toggleAllVisibility,
1728
+ updateConfig,
1729
+ exportConfig,
1730
+ initialize
1731
+ };
1732
+ };
1733
+ /**
1734
+ * @description 动态表单状态注入键
1735
+ */
1736
+ const DYNAMIC_FORM_STATE_KEY = Symbol("dynamicFormState");
1737
+
1738
+ //#endregion
1739
+ //#region src/components/C_Form/layouts/Dynamic/index.vue?vue&type=script&setup=true&lang.ts
1740
+ const _hoisted_1$1 = { class: "c-form-dynamic" };
1741
+ const _hoisted_2$1 = {
1742
+ key: 0,
1743
+ class: "dynamic-controls"
1744
+ };
1745
+ const _hoisted_3$1 = { class: "max-fields-config" };
1746
+ const _hoisted_4$1 = { class: "dynamic-stats" };
1747
+ const _hoisted_5$1 = { class: "dynamic-controls-fallback" };
1748
+ const _hoisted_6$1 = { class: "dynamic-form-items" };
1749
+ var index_vue_vue_type_script_setup_true_lang_default$2 = /* @__PURE__ */ defineComponent({
1750
+ __name: "index",
1751
+ props: {
1752
+ formItems: {},
1753
+ layoutConfig: { default: () => ({}) },
1754
+ options: { default: () => [] },
1755
+ dynamicFormState: { default: null }
1756
+ },
1757
+ emits: [
1758
+ "field-add",
1759
+ "field-remove",
1760
+ "fields-clear"
1761
+ ],
1762
+ setup(__props, { expose: __expose }) {
1763
+ const props = __props;
1764
+ const injectedDynamicState = inject(DYNAMIC_FORM_STATE_KEY, null);
1765
+ const dynamicState = computed(() => props.dynamicFormState || injectedDynamicState);
1766
+ const isDynamicStateAvailable = computed(() => !!dynamicState.value);
1767
+ const gridCols = computed(() => props.layoutConfig?.dynamic?.grid?.cols ?? 24);
1768
+ const gridGutter = computed(() => props.layoutConfig?.dynamic?.grid?.gutter ?? 16);
1769
+ const showControls = computed(() => props.layoutConfig?.dynamic?.controls?.showControls !== false);
1770
+ const maxFields = computed(() => {
1771
+ return dynamicState.value?.state.config.maxFields ?? props.layoutConfig?.dynamic?.dynamic?.maxFields ?? 50;
1772
+ });
1773
+ const dynamicFieldsCount = computed(() => dynamicState.value?.dynamicFieldsCount.value ?? 0);
1774
+ const canAddMoreFields = computed(() => dynamicState.value?.canAddMoreFields.value ?? false);
1775
+ const totalFieldsCount = computed(() => props.formItems.length);
1776
+ const getItemKey = (item, index) => {
1777
+ return item.key?.toString() || item.props?.path || `dynamic-item-${index}`;
1778
+ };
1779
+ const getItemSpan = (index) => {
1780
+ const span = props.options?.[index]?.layout?.span;
1781
+ return typeof span === "number" && span > 0 && span <= gridCols.value ? span : Math.min(12, gridCols.value);
1782
+ };
1783
+ const isDynamicField = (item) => {
1784
+ if (!dynamicState.value) return false;
1785
+ const fieldId = item.props?.path || item.key?.toString() || "";
1786
+ return dynamicState.value.state.dynamicFields.some((field) => field.prop === fieldId);
1787
+ };
1788
+ __expose({
1789
+ addField: () => dynamicState.value?.addField(),
1790
+ removeField: () => dynamicState.value?.removeField(),
1791
+ clearAllDynamic: () => dynamicState.value?.clearDynamicFields(),
1792
+ isDynamicStateAvailable,
1793
+ dynamicState
1794
+ });
1795
+ if (typeof import.meta !== "undefined" && import.meta.env?.DEV) watchEffect(() => {
1796
+ const { options, formItems } = props;
1797
+ if (options && options.length !== formItems.length) console.warn(`[Dynamic Layout] 配置项数量(${options.length})与表单项数量(${formItems.length})不匹配`);
1798
+ console.log("[Dynamic Layout]", isDynamicStateAvailable.value ? "动态模式已启用" : "运行在静态模式", {
1799
+ totalFields: totalFieldsCount.value,
1800
+ dynamicFields: dynamicFieldsCount.value,
1801
+ stateSource: props.dynamicFormState ? "props透传" : "inject注入"
1802
+ });
1803
+ });
1804
+ return (_ctx, _cache) => {
1805
+ const _component_NBadge = resolveComponent("NBadge");
1806
+ const _component_NButton = resolveComponent("NButton");
1807
+ const _component_NSpace = resolveComponent("NSpace");
1808
+ const _component_NInputNumber = resolveComponent("NInputNumber");
1809
+ const _component_NTag = resolveComponent("NTag");
1810
+ const _component_NCard = resolveComponent("NCard");
1811
+ const _component_NAlert = resolveComponent("NAlert");
1812
+ const _component_NGridItem = resolveComponent("NGridItem");
1813
+ const _component_NGrid = resolveComponent("NGrid");
1814
+ return openBlock(), createElementBlock("div", _hoisted_1$1, [
1815
+ createCommentVNode(" 动态控制面板 "),
1816
+ showControls.value && isDynamicStateAvailable.value ? (openBlock(), createElementBlock("div", _hoisted_2$1, [createVNode(_component_NCard, {
1817
+ size: "small",
1818
+ title: "动态表单控制",
1819
+ bordered: false
1820
+ }, {
1821
+ "header-extra": withCtx(() => [createVNode(_component_NBadge, {
1822
+ value: dynamicFieldsCount.value,
1823
+ type: "warning"
1824
+ }, {
1825
+ default: withCtx(() => [createVNode(C_Icon_default, {
1826
+ name: "mdi:layers",
1827
+ size: 16
1828
+ })]),
1829
+ _: 1
1830
+ }, 8, ["value"])]),
1831
+ default: withCtx(() => [createVNode(_component_NSpace, {
1832
+ justify: "space-between",
1833
+ align: "center"
1834
+ }, {
1835
+ default: withCtx(() => [
1836
+ createVNode(_component_NSpace, null, {
1837
+ default: withCtx(() => [
1838
+ createVNode(_component_NButton, {
1839
+ size: "small",
1840
+ type: "primary",
1841
+ disabled: !canAddMoreFields.value,
1842
+ onClick: _cache[0] || (_cache[0] = ($event) => dynamicState.value.addField())
1843
+ }, {
1844
+ icon: withCtx(() => [createVNode(C_Icon_default, {
1845
+ name: "mdi:plus",
1846
+ size: 14
1847
+ })]),
1848
+ default: withCtx(() => [createTextVNode(" 添加字段 (" + toDisplayString(dynamicFieldsCount.value) + "/" + toDisplayString(maxFields.value) + ") ", 1)]),
1849
+ _: 1
1850
+ }, 8, ["disabled"]),
1851
+ createVNode(_component_NButton, {
1852
+ size: "small",
1853
+ type: "warning",
1854
+ disabled: dynamicFieldsCount.value === 0,
1855
+ onClick: _cache[1] || (_cache[1] = ($event) => dynamicState.value.removeField())
1856
+ }, {
1857
+ icon: withCtx(() => [createVNode(C_Icon_default, {
1858
+ name: "mdi:minus",
1859
+ size: 14
1860
+ })]),
1861
+ default: withCtx(() => [_cache[4] || (_cache[4] = createTextVNode(" 移除字段 ", -1))]),
1862
+ _: 1,
1863
+ __: [4]
1864
+ }, 8, ["disabled"]),
1865
+ createVNode(_component_NButton, {
1866
+ size: "small",
1867
+ type: "error",
1868
+ disabled: dynamicFieldsCount.value === 0,
1869
+ onClick: _cache[2] || (_cache[2] = ($event) => dynamicState.value.clearDynamicFields())
1870
+ }, {
1871
+ icon: withCtx(() => [createVNode(C_Icon_default, {
1872
+ name: "mdi:delete-sweep",
1873
+ size: 14
1874
+ })]),
1875
+ default: withCtx(() => [_cache[5] || (_cache[5] = createTextVNode(" 清空动态 ", -1))]),
1876
+ _: 1,
1877
+ __: [5]
1878
+ }, 8, ["disabled"])
1879
+ ]),
1880
+ _: 1
1881
+ }),
1882
+ createCommentVNode(" 最大字段数配置 "),
1883
+ createElementVNode("div", _hoisted_3$1, [_cache[6] || (_cache[6] = createElementVNode("span", null, "最大字段数:", -1)), createVNode(_component_NInputNumber, {
1884
+ value: maxFields.value,
1885
+ "onUpdate:value": _cache[3] || (_cache[3] = (v) => v && dynamicState.value.updateConfig({ maxFields: v })),
1886
+ min: 5,
1887
+ max: 50,
1888
+ size: "small",
1889
+ style: { "width": "100px" }
1890
+ }, null, 8, ["value"])])
1891
+ ]),
1892
+ _: 1
1893
+ }), createElementVNode("div", _hoisted_4$1, [createVNode(_component_NSpace, null, {
1894
+ default: withCtx(() => [createVNode(_component_NTag, { type: "info" }, {
1895
+ default: withCtx(() => [createTextVNode("总字段: " + toDisplayString(totalFieldsCount.value), 1)]),
1896
+ _: 1
1897
+ }), createVNode(_component_NTag, { type: "warning" }, {
1898
+ default: withCtx(() => [createTextVNode("动态: " + toDisplayString(dynamicFieldsCount.value), 1)]),
1899
+ _: 1
1900
+ })]),
1901
+ _: 1
1902
+ })])]),
1903
+ _: 1
1904
+ })])) : showControls.value && !isDynamicStateAvailable.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 状态不可用时的提示 "), createElementVNode("div", _hoisted_5$1, [createVNode(_component_NCard, {
1905
+ size: "small",
1906
+ title: "动态表单控制 (静态模式)",
1907
+ bordered: false
1908
+ }, {
1909
+ default: withCtx(() => [createVNode(_component_NAlert, {
1910
+ type: "info",
1911
+ "show-icon": ""
1912
+ }, {
1913
+ icon: withCtx(() => [createVNode(C_Icon_default, {
1914
+ name: "mdi:information-outline",
1915
+ size: 16
1916
+ })]),
1917
+ default: withCtx(() => [_cache[7] || (_cache[7] = createTextVNode(" 当前为静态模式,如需动态功能请在父组件中提供动态表单状态。 ", -1))]),
1918
+ _: 1,
1919
+ __: [7]
1920
+ })]),
1921
+ _: 1
1922
+ })])], 2112)) : createCommentVNode("v-if", true),
1923
+ createCommentVNode(" 表单项渲染区域 "),
1924
+ createElementVNode("div", _hoisted_6$1, [createVNode(_component_NGrid, {
1925
+ cols: gridCols.value,
1926
+ "x-gap": gridGutter.value,
1927
+ "y-gap": gridGutter.value
1928
+ }, {
1929
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.formItems, (item, index) => {
1930
+ return openBlock(), createBlock(_component_NGridItem, {
1931
+ key: getItemKey(item, index),
1932
+ span: getItemSpan(index)
1933
+ }, {
1934
+ default: withCtx(() => [createElementVNode("div", { class: normalizeClass(["dynamic-item-wrapper", { "is-dynamic-field": isDynamicField(item) }]) }, [(openBlock(), createBlock(resolveDynamicComponent(item)))], 2)]),
1935
+ _: 2
1936
+ }, 1032, ["span"]);
1937
+ }), 128))]),
1938
+ _: 1
1939
+ }, 8, [
1940
+ "cols",
1941
+ "x-gap",
1942
+ "y-gap"
1943
+ ])])
1944
+ ]);
1945
+ };
1946
+ }
1947
+ });
1948
+
1949
+ //#endregion
1950
+ //#region src/components/C_Form/layouts/Dynamic/index.vue
1951
+ var Dynamic_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$2, [["__scopeId", "data-v-3f0739c2"]]);
1952
+
1953
+ //#endregion
1954
+ //#region src/components/C_Form/layouts/Custom/index.vue?vue&type=script&setup=true&lang.ts
1955
+ const _hoisted_1 = { class: "custom-layout" };
1956
+ const _hoisted_2 = { class: "toolbar-content" };
1957
+ const _hoisted_3 = { class: "mode-section" };
1958
+ const _hoisted_4 = { class: "stats-section" };
1959
+ const _hoisted_5 = { class: "stat-item" };
1960
+ const _hoisted_6 = { class: "stat-value" };
1961
+ const _hoisted_7 = { class: "stat-item" };
1962
+ const _hoisted_8 = { class: "stat-value" };
1963
+ const _hoisted_9 = { class: "actions-section" };
1964
+ const _hoisted_10 = { class: "design-tools" };
1965
+ const _hoisted_11 = { class: "tool-group" };
1966
+ const _hoisted_12 = {
1967
+ key: 0,
1968
+ class: "canvas-hint"
1969
+ };
1970
+ const _hoisted_13 = { class: "areas-container" };
1971
+ const _hoisted_14 = { class: "area-header" };
1972
+ const _hoisted_15 = { class: "area-info" };
1973
+ const _hoisted_16 = ["onClick"];
1974
+ const _hoisted_17 = { class: "area-controls" };
1975
+ const _hoisted_18 = { class: "area-fields" };
1976
+ const _hoisted_19 = { class: "field-preview" };
1977
+ const _hoisted_20 = { class: "field-label" };
1978
+ const _hoisted_21 = { class: "field-type" };
1979
+ const _hoisted_22 = {
1980
+ key: 0,
1981
+ class: "area-drop-zone"
1982
+ };
1983
+ const _hoisted_23 = { class: "pool-fields-grid" };
1984
+ const _hoisted_24 = { class: "field-name" };
1985
+ const _hoisted_25 = { class: "field-type-tag" };
1986
+ const _hoisted_26 = {
1987
+ key: 0,
1988
+ class: "empty-layout"
1989
+ };
1990
+ const _hoisted_27 = {
1991
+ key: 1,
1992
+ class: "form-container"
1993
+ };
1994
+ const _hoisted_28 = { class: "form-areas" };
1995
+ var index_vue_vue_type_script_setup_true_lang_default$1 = /* @__PURE__ */ defineComponent({
1996
+ __name: "index",
1997
+ props: {
1998
+ options: { default: () => [] },
1999
+ formItems: { default: () => [] },
2000
+ formData: { default: () => ({}) }
2001
+ },
2002
+ emits: ["fields-change", "export-data"],
2003
+ setup(__props, { emit: __emit }) {
2004
+ const props = __props;
2005
+ const emit = __emit;
2006
+ const isDesignMode = ref(true);
2007
+ const customAreas = ref([]);
2008
+ const availableFields = ref([]);
2009
+ const editingTitleId = ref("");
2010
+ const allFormOptions = computed(() => {
2011
+ if (props.options?.length > 0) return props.options;
2012
+ return props.formItems?.map((item) => {
2013
+ const itemProps = item.props;
2014
+ return {
2015
+ prop: itemProps?.path || "",
2016
+ label: itemProps?.label || itemProps?.path || "",
2017
+ type: "input",
2018
+ show: true
2019
+ };
2020
+ }).filter((option) => option.prop) || [];
2021
+ });
2022
+ const totalFieldsCount = computed(() => customAreas.value.reduce((total, area) => total + area.fields.length, 0));
2023
+ const generateId = () => `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
2024
+ const getFieldTypeName = (type) => {
2025
+ return {
2026
+ input: "输入框",
2027
+ select: "下拉框",
2028
+ radio: "单选框",
2029
+ checkbox: "复选框",
2030
+ textarea: "文本域",
2031
+ date: "日期",
2032
+ number: "数字"
2033
+ }[type] || type;
2034
+ };
2035
+ const getFormItemForField = (field) => {
2036
+ return props.formItems?.find((item) => {
2037
+ return item.props?.path === field.prop;
2038
+ }) || null;
2039
+ };
2040
+ const addArea = (type) => {
2041
+ const area = {
2042
+ id: generateId(),
2043
+ title: `${type === "horizontal" ? "水平" : type === "vertical" ? "垂直" : "网格"}区域`,
2044
+ type,
2045
+ fields: []
2046
+ };
2047
+ customAreas.value.push(area);
2048
+ };
2049
+ const deleteArea = (areaId) => {
2050
+ const index = customAreas.value.findIndex((area) => area.id === areaId);
2051
+ if (index !== -1) customAreas.value.splice(index, 1);
2052
+ };
2053
+ const handleSaveLayout = () => {
2054
+ const config = JSON.stringify(customAreas.value, null, 2);
2055
+ const blob = new Blob([config], { type: "application/json" });
2056
+ const url = URL.createObjectURL(blob);
2057
+ const link = document.createElement("a");
2058
+ link.href = url;
2059
+ link.download = `layout-config-${Date.now()}.json`;
2060
+ document.body.appendChild(link);
2061
+ link.click();
2062
+ document.body.removeChild(link);
2063
+ URL.revokeObjectURL(url);
2064
+ };
2065
+ const handleResetLayout = () => {
2066
+ customAreas.value = [];
2067
+ };
2068
+ const handleExportData = () => {
2069
+ emit("export-data", {
2070
+ layout: customAreas.value,
2071
+ formData: props.formData,
2072
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2073
+ });
2074
+ };
2075
+ watchEffect(() => {
2076
+ const usedProps = new Set(customAreas.value.flatMap((area) => area.fields.map((field) => field.prop)));
2077
+ availableFields.value = allFormOptions.value.filter((field) => !usedProps.has(field.prop)).map((field) => ({
2078
+ ...field,
2079
+ id: field.prop
2080
+ }));
2081
+ });
2082
+ watch(() => customAreas.value, () => {
2083
+ emit("fields-change", customAreas.value.flatMap((area) => area.fields.map((field) => ({
2084
+ ...field,
2085
+ id: void 0
2086
+ }))));
2087
+ }, { deep: true });
2088
+ return (_ctx, _cache) => {
2089
+ const _component_NButton = resolveComponent("NButton");
2090
+ const _component_NButtonGroup = resolveComponent("NButtonGroup");
2091
+ const _component_NCard = resolveComponent("NCard");
2092
+ const _component_NInput = resolveComponent("NInput");
2093
+ const _component_NTag = resolveComponent("NTag");
2094
+ const _component_NEmpty = resolveComponent("NEmpty");
2095
+ return openBlock(), createElementBlock("div", _hoisted_1, [
2096
+ createCommentVNode(" 顶部工具栏 "),
2097
+ createVNode(_component_NCard, {
2098
+ bordered: false,
2099
+ class: "toolbar-card"
2100
+ }, {
2101
+ default: withCtx(() => [createElementVNode("div", _hoisted_2, [
2102
+ createCommentVNode(" 模式切换 "),
2103
+ createElementVNode("div", _hoisted_3, [_cache[11] || (_cache[11] = createElementVNode("span", { class: "section-label" }, "自定义模式:", -1)), createVNode(_component_NButtonGroup, null, {
2104
+ default: withCtx(() => [createVNode(_component_NButton, {
2105
+ type: isDesignMode.value ? "primary" : "default",
2106
+ onClick: _cache[0] || (_cache[0] = ($event) => isDesignMode.value = true),
2107
+ size: "small"
2108
+ }, {
2109
+ default: withCtx(() => _cache[9] || (_cache[9] = [createTextVNode(" 🎨 设计模式 ", -1)])),
2110
+ _: 1,
2111
+ __: [9]
2112
+ }, 8, ["type"]), createVNode(_component_NButton, {
2113
+ type: !isDesignMode.value ? "primary" : "default",
2114
+ onClick: _cache[1] || (_cache[1] = ($event) => isDesignMode.value = false),
2115
+ size: "small"
2116
+ }, {
2117
+ default: withCtx(() => _cache[10] || (_cache[10] = [createTextVNode(" 📝 填写模式 ", -1)])),
2118
+ _: 1,
2119
+ __: [10]
2120
+ }, 8, ["type"])]),
2121
+ _: 1
2122
+ })]),
2123
+ createCommentVNode(" 统计信息 "),
2124
+ createElementVNode("div", _hoisted_4, [createElementVNode("div", _hoisted_5, [createElementVNode("div", _hoisted_6, toDisplayString(customAreas.value.length), 1), _cache[12] || (_cache[12] = createElementVNode("div", { class: "stat-label" }, "自定义区域", -1))]), createElementVNode("div", _hoisted_7, [createElementVNode("div", _hoisted_8, toDisplayString(totalFieldsCount.value), 1), _cache[13] || (_cache[13] = createElementVNode("div", { class: "stat-label" }, "字段总数", -1))])]),
2125
+ createCommentVNode(" 操作按钮 "),
2126
+ createElementVNode("div", _hoisted_9, [isDesignMode.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(_component_NButton, {
2127
+ secondary: "",
2128
+ onClick: handleSaveLayout,
2129
+ size: "small"
2130
+ }, {
2131
+ default: withCtx(() => _cache[14] || (_cache[14] = [createTextVNode(" 💾 保存布局 ", -1)])),
2132
+ _: 1,
2133
+ __: [14]
2134
+ }), createVNode(_component_NButton, {
2135
+ secondary: "",
2136
+ onClick: handleResetLayout,
2137
+ size: "small"
2138
+ }, {
2139
+ default: withCtx(() => _cache[15] || (_cache[15] = [createTextVNode(" 🔄 重置布局 ", -1)])),
2140
+ _: 1,
2141
+ __: [15]
2142
+ })], 64)) : (openBlock(), createBlock(_component_NButton, {
2143
+ key: 1,
2144
+ secondary: "",
2145
+ onClick: handleExportData,
2146
+ size: "small"
2147
+ }, {
2148
+ default: withCtx(() => _cache[16] || (_cache[16] = [createTextVNode(" 📊 导出数据 ", -1)])),
2149
+ _: 1,
2150
+ __: [16]
2151
+ }))])
2152
+ ])]),
2153
+ _: 1
2154
+ }),
2155
+ createCommentVNode(" 设计模式工具面板 "),
2156
+ isDesignMode.value ? (openBlock(), createBlock(_component_NCard, {
2157
+ key: 0,
2158
+ class: "design-panel",
2159
+ title: "🎨 设计工具"
2160
+ }, {
2161
+ default: withCtx(() => [createElementVNode("div", _hoisted_10, [createElementVNode("div", _hoisted_11, [
2162
+ _cache[20] || (_cache[20] = createElementVNode("span", { class: "group-label" }, "添加区域:", -1)),
2163
+ createVNode(_component_NButton, {
2164
+ onClick: _cache[2] || (_cache[2] = ($event) => addArea("horizontal")),
2165
+ size: "small"
2166
+ }, {
2167
+ default: withCtx(() => _cache[17] || (_cache[17] = [createTextVNode(" ➡️ 水平区域 ", -1)])),
2168
+ _: 1,
2169
+ __: [17]
2170
+ }),
2171
+ createVNode(_component_NButton, {
2172
+ onClick: _cache[3] || (_cache[3] = ($event) => addArea("vertical")),
2173
+ size: "small"
2174
+ }, {
2175
+ default: withCtx(() => _cache[18] || (_cache[18] = [createTextVNode(" ⬇️ 垂直区域 ", -1)])),
2176
+ _: 1,
2177
+ __: [18]
2178
+ }),
2179
+ createVNode(_component_NButton, {
2180
+ onClick: _cache[4] || (_cache[4] = ($event) => addArea("grid")),
2181
+ size: "small"
2182
+ }, {
2183
+ default: withCtx(() => _cache[19] || (_cache[19] = [createTextVNode(" ⚏ 网格区域 ", -1)])),
2184
+ _: 1,
2185
+ __: [19]
2186
+ })
2187
+ ])])]),
2188
+ _: 1
2189
+ })) : createCommentVNode("v-if", true),
2190
+ createCommentVNode(" 主画布区域 "),
2191
+ createElementVNode("div", { class: normalizeClass(["layout-canvas", { "design-mode": isDesignMode.value }]) }, [createCommentVNode(" 设计模式 "), isDesignMode.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
2192
+ customAreas.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_12, _cache[21] || (_cache[21] = [createElementVNode("div", { class: "hint-content" }, [createElementVNode("h3", null, "🎨 开始自定义你的布局"), createElementVNode("p", null, "点击上方按钮添加区域")], -1)]))) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 区域列表 "), createElementVNode("div", _hoisted_13, [(openBlock(true), createElementBlock(Fragment, null, renderList(customAreas.value, (area) => {
2193
+ return openBlock(), createElementBlock("div", {
2194
+ key: area.id,
2195
+ class: normalizeClass(["custom-area", `area-${area.type}`])
2196
+ }, [
2197
+ createElementVNode("div", _hoisted_14, [createElementVNode("div", _hoisted_15, [editingTitleId.value === area.id ? (openBlock(), createBlock(_component_NInput, {
2198
+ key: 0,
2199
+ value: area.title,
2200
+ "onUpdate:value": ($event) => area.title = $event,
2201
+ size: "small",
2202
+ onBlur: _cache[5] || (_cache[5] = ($event) => editingTitleId.value = ""),
2203
+ onKeyup: _cache[6] || (_cache[6] = withKeys(($event) => editingTitleId.value = "", ["enter"])),
2204
+ class: "title-input"
2205
+ }, null, 8, ["value", "onUpdate:value"])) : (openBlock(), createElementBlock("span", {
2206
+ key: 1,
2207
+ class: "area-title",
2208
+ onClick: ($event) => editingTitleId.value = area.id
2209
+ }, toDisplayString(area.title), 9, _hoisted_16)), createVNode(_component_NTag, { size: "small" }, {
2210
+ default: withCtx(() => [createTextVNode(toDisplayString(area.fields.length) + " 字段", 1)]),
2211
+ _: 2
2212
+ }, 1024)]), createElementVNode("div", _hoisted_17, [createVNode(_component_NButton, {
2213
+ text: "",
2214
+ onClick: ($event) => deleteArea(area.id),
2215
+ size: "tiny",
2216
+ type: "error",
2217
+ title: "删除区域"
2218
+ }, {
2219
+ default: withCtx(() => _cache[22] || (_cache[22] = [createTextVNode(" 🗑️ ", -1)])),
2220
+ _: 2,
2221
+ __: [22]
2222
+ }, 1032, ["onClick"])])]),
2223
+ createCommentVNode(" 字段容器 "),
2224
+ createElementVNode("div", _hoisted_18, [(openBlock(true), createElementBlock(Fragment, null, renderList(area.fields, (field) => {
2225
+ return openBlock(), createElementBlock("div", {
2226
+ key: field.id,
2227
+ class: "field-item"
2228
+ }, [createElementVNode("div", _hoisted_19, [createElementVNode("span", _hoisted_20, toDisplayString(field.label || field.prop), 1), createElementVNode("span", _hoisted_21, toDisplayString(getFieldTypeName(field.type)), 1)])]);
2229
+ }), 128))]),
2230
+ area.fields.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_22, " 拖拽字段到这里 ")) : createCommentVNode("v-if", true)
2231
+ ], 2);
2232
+ }), 128))])], 2112)),
2233
+ createCommentVNode(" 字段池 "),
2234
+ createVNode(_component_NCard, {
2235
+ class: "field-pool",
2236
+ title: "📦 可用字段"
2237
+ }, {
2238
+ default: withCtx(() => [createElementVNode("div", _hoisted_23, [(openBlock(true), createElementBlock(Fragment, null, renderList(availableFields.value, (field) => {
2239
+ return openBlock(), createElementBlock("div", {
2240
+ key: field.id,
2241
+ class: "pool-field"
2242
+ }, [createElementVNode("span", _hoisted_24, toDisplayString(field.label || field.prop), 1), createElementVNode("span", _hoisted_25, toDisplayString(getFieldTypeName(field.type)), 1)]);
2243
+ }), 128))])]),
2244
+ _: 1
2245
+ })
2246
+ ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 填写模式 "), customAreas.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_26, [createVNode(_component_NEmpty, { description: "尚未设计布局" }, {
2247
+ extra: withCtx(() => [createVNode(_component_NButton, {
2248
+ onClick: _cache[7] || (_cache[7] = ($event) => isDesignMode.value = true),
2249
+ type: "primary"
2250
+ }, {
2251
+ default: withCtx(() => _cache[23] || (_cache[23] = [createTextVNode(" 🎨 开始设计 ", -1)])),
2252
+ _: 1,
2253
+ __: [23]
2254
+ })]),
2255
+ _: 1
2256
+ })])) : (openBlock(), createElementBlock("div", _hoisted_27, [createElementVNode("div", _hoisted_28, [(openBlock(true), createElementBlock(Fragment, null, renderList(customAreas.value, (area) => {
2257
+ return openBlock(), createElementBlock("div", {
2258
+ key: area.id,
2259
+ class: normalizeClass(["form-area", `area-${area.type}`])
2260
+ }, [area.fields.length > 0 ? (openBlock(), createBlock(_component_NCard, {
2261
+ key: 0,
2262
+ title: area.title
2263
+ }, {
2264
+ default: withCtx(() => [createElementVNode("div", { class: normalizeClass(["area-form-items", `layout-${area.type}`]) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(area.fields, (field) => {
2265
+ return openBlock(), createBlock(resolveDynamicComponent(getFormItemForField(field)), { key: field.prop });
2266
+ }), 128))], 2)]),
2267
+ _: 2
2268
+ }, 1032, ["title"])) : (openBlock(), createBlock(_component_NEmpty, {
2269
+ key: 1,
2270
+ description: "此区域暂无字段",
2271
+ size: "small"
2272
+ }, {
2273
+ extra: withCtx(() => [createVNode(_component_NButton, {
2274
+ onClick: _cache[8] || (_cache[8] = ($event) => isDesignMode.value = true),
2275
+ size: "small",
2276
+ secondary: ""
2277
+ }, {
2278
+ default: withCtx(() => _cache[24] || (_cache[24] = [createTextVNode(" 🎨 添加字段 ", -1)])),
2279
+ _: 1,
2280
+ __: [24]
2281
+ })]),
2282
+ _: 1
2283
+ }))], 2);
2284
+ }), 128))])]))], 64))], 2)
2285
+ ]);
2286
+ };
2287
+ }
2288
+ });
2289
+
2290
+ //#endregion
2291
+ //#region src/components/C_Form/layouts/Custom/index.vue
2292
+ var Custom_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default$1, [["__scopeId", "data-v-a21051df"]]);
2293
+
2294
+ //#endregion
2295
+ //#region src/components/C_Form/index.vue?vue&type=script&setup=true&lang.ts
2296
+ var index_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
2297
+ name: "C_Form",
2298
+ __name: "index",
2299
+ props: {
2300
+ options: {},
2301
+ modelValue: {},
2302
+ config: { default: () => ({}) }
2303
+ },
2304
+ emits: [
2305
+ "submit",
2306
+ "update:modelValue",
2307
+ "validate-success",
2308
+ "validate-error"
2309
+ ],
2310
+ setup(__props, { expose: __expose, emit: __emit }) {
2311
+ const LAYOUT_MAP = {
2312
+ default: Default_default,
2313
+ inline: Inline_default,
2314
+ grid: Grid_default,
2315
+ card: Card_default,
2316
+ tabs: Tabs_default,
2317
+ steps: Steps_default,
2318
+ dynamic: Dynamic_default,
2319
+ custom: Custom_default
2320
+ };
2321
+ const COMPONENT_MAP = {
2322
+ NFormItem: resolveComponent("NFormItem"),
2323
+ NInput: resolveComponent("NInput"),
2324
+ NInputNumber: resolveComponent("NInputNumber"),
2325
+ NSwitch: resolveComponent("NSwitch"),
2326
+ NSlider: resolveComponent("NSlider"),
2327
+ NRate: resolveComponent("NRate"),
2328
+ NDatePicker: resolveComponent("NDatePicker"),
2329
+ NTimePicker: resolveComponent("NTimePicker"),
2330
+ NCascader: resolveComponent("NCascader"),
2331
+ NColorPicker: resolveComponent("NColorPicker"),
2332
+ NSelect: resolveComponent("NSelect"),
2333
+ NCheckboxGroup: resolveComponent("NCheckboxGroup"),
2334
+ NCheckbox: resolveComponent("NCheckbox"),
2335
+ NRadioGroup: resolveComponent("NRadioGroup"),
2336
+ NRadio: resolveComponent("NRadio"),
2337
+ NUpload: resolveComponent("NUpload"),
2338
+ NButton: resolveComponent("NButton"),
2339
+ NSpace: resolveComponent("NSpace"),
2340
+ C_Editor: resolveComponent("C_Editor")
2341
+ };
2342
+ const props = __props;
2343
+ const emit = __emit;
2344
+ const resolved = computed(() => resolveFormConfig(props.config));
2345
+ const formRef = ref(null);
2346
+ const { formModel, formRules, visibleOptions, initialize, handleFieldChange, validate, validateField, validateStep, validateTab, validateDynamicFields, validateCustomGroup, clearValidation, getModel, setFields, resetFields, setFieldValue, getFieldValue, setFieldsValue, handleSubmit, handleReset } = useFormState(computed(() => props.options), resolved, formRef, emit);
2347
+ const { formItems } = useFormRenderer(formModel, visibleOptions, resolved, handleFieldChange, COMPONENT_MAP, getCurrentInstance()?.slots);
2348
+ const layoutComponent = computed(() => LAYOUT_MAP[resolved.value.layout] || LAYOUT_MAP.default);
2349
+ const mergedLayoutConfig = computed(() => ({
2350
+ type: resolved.value.layout,
2351
+ grid: resolved.value.grid,
2352
+ inline: resolved.value.inline,
2353
+ card: resolved.value.card,
2354
+ tabs: resolved.value.tabs,
2355
+ steps: resolved.value.steps,
2356
+ dynamic: resolved.value.dynamic,
2357
+ custom: resolved.value.custom
2358
+ }));
2359
+ const showActions = computed(() => shouldShowActions(resolved.value));
2360
+ /** 通用布局事件桥接 */
2361
+ const handleLayoutEvent = (callbackName, ...args) => {
2362
+ const callback = resolved.value[callbackName];
2363
+ callback?.(...args);
2364
+ };
2365
+ /** 字段变化事件(保留回调通道) */
2366
+ const handleFieldsChange = (fields) => {
2367
+ resolved.value.onFieldsChange?.(fields);
2368
+ };
2369
+ /** Steps 布局事件 — 需要多参数特殊处理 */
2370
+ const handleStepChange = (stepIndex, stepKey) => {
2371
+ resolved.value.onStepChange?.(stepIndex, stepKey);
2372
+ };
2373
+ const handleStepBeforeChange = async (currentStep, targetStep) => {
2374
+ resolved.value.onStepBeforeChange?.(currentStep, targetStep);
2375
+ return true;
2376
+ };
2377
+ const handleStepValidate = async (stepIndex) => {
2378
+ try {
2379
+ const currentStepKey = resolved.value.steps?.steps?.[stepIndex]?.key;
2380
+ if (!currentStepKey) return true;
2381
+ const stepFields = props.options.filter((option) => option.layout?.step === currentStepKey).map((option) => option.prop);
2382
+ if (stepFields.length === 0) return true;
2383
+ await validateField(stepFields);
2384
+ resolved.value.onStepValidate?.(stepIndex);
2385
+ return true;
2386
+ } catch (error) {
2387
+ console.warn(`[C_Form] 步骤 ${stepIndex} 验证失败:`, error);
2388
+ return false;
2389
+ }
2390
+ };
2391
+ watch(() => props.modelValue, (val) => {
2392
+ if (val) Object.assign(formModel, val);
2393
+ }, {
2394
+ immediate: true,
2395
+ deep: true
2396
+ });
2397
+ __expose({
2398
+ validate,
2399
+ validateField,
2400
+ validateStep,
2401
+ validateTab,
2402
+ validateDynamicFields,
2403
+ validateCustomGroup,
2404
+ clearValidation,
2405
+ getModel,
2406
+ setFields,
2407
+ resetFields,
2408
+ setFieldValue,
2409
+ getFieldValue,
2410
+ setFieldsValue,
2411
+ formRef,
2412
+ formModel,
2413
+ initialize,
2414
+ layoutType: computed(() => resolved.value.layout),
2415
+ shouldShowDefaultActions: showActions
2416
+ });
2417
+ return (_ctx, _cache) => {
2418
+ return openBlock(), createBlock(unref(NForm), mergeProps({
2419
+ ref_key: "formRef",
2420
+ ref: formRef,
2421
+ model: unref(formModel),
2422
+ rules: unref(formRules),
2423
+ "validate-on-rule-change": false,
2424
+ "label-placement": resolved.value.labelPlacement,
2425
+ "label-width": resolved.value.labelWidth,
2426
+ size: resolved.value.size,
2427
+ disabled: resolved.value.disabled,
2428
+ readonly: resolved.value.readonly
2429
+ }, _ctx.$attrs), {
2430
+ default: withCtx(() => [
2431
+ createCommentVNode(" 布局组件渲染 "),
2432
+ (openBlock(), createBlock(resolveDynamicComponent(layoutComponent.value), {
2433
+ "form-items": unref(formItems),
2434
+ "layout-config": mergedLayoutConfig.value,
2435
+ options: unref(visibleOptions),
2436
+ "form-data": unref(formModel),
2437
+ onTabChange: _cache[0] || (_cache[0] = ($event) => handleLayoutEvent("onTabChange", $event)),
2438
+ onTabValidate: _cache[1] || (_cache[1] = ($event) => handleLayoutEvent("onTabValidate", $event)),
2439
+ onStepChange: handleStepChange,
2440
+ onStepBeforeChange: handleStepBeforeChange,
2441
+ onStepValidate: handleStepValidate,
2442
+ onFieldAdd: _cache[2] || (_cache[2] = ($event) => handleLayoutEvent("onFieldAdd", $event)),
2443
+ onFieldRemove: _cache[3] || (_cache[3] = ($event) => handleLayoutEvent("onFieldRemove", $event)),
2444
+ onFieldToggle: _cache[4] || (_cache[4] = (id, visible) => resolved.value.onFieldToggle?.(id, visible)),
2445
+ onFieldsClear: _cache[5] || (_cache[5] = ($event) => handleLayoutEvent("onFieldsClear")),
2446
+ onRenderModeChange: _cache[6] || (_cache[6] = ($event) => handleLayoutEvent("onRenderModeChange", $event)),
2447
+ onGroupToggle: _cache[7] || (_cache[7] = (key, collapsed) => resolved.value.onGroupToggle?.(key, collapsed)),
2448
+ onGroupReset: _cache[8] || (_cache[8] = ($event) => handleLayoutEvent("onGroupReset", $event)),
2449
+ onValidateSuccess: _cache[9] || (_cache[9] = (model) => emit("validate-success", model)),
2450
+ onValidateError: _cache[10] || (_cache[10] = (errors) => emit("validate-error", errors)),
2451
+ onFieldsChange: handleFieldsChange
2452
+ }, null, 40, [
2453
+ "form-items",
2454
+ "layout-config",
2455
+ "options",
2456
+ "form-data"
2457
+ ])),
2458
+ createCommentVNode(" 表单操作按钮区域(只在特定布局中显示) "),
2459
+ showActions.value ? (openBlock(), createBlock(unref(NFormItem), {
2460
+ key: 0,
2461
+ style: { "margin-top": "20px" }
2462
+ }, {
2463
+ default: withCtx(() => [renderSlot(_ctx.$slots, "action", {
2464
+ form: formRef.value,
2465
+ model: unref(formModel),
2466
+ validate: unref(validate),
2467
+ validateField: unref(validateField),
2468
+ reset: unref(resetFields),
2469
+ setFields: unref(setFields),
2470
+ getModel: unref(getModel),
2471
+ clearValidation: unref(clearValidation)
2472
+ }, () => [createVNode(unref(NSpace), null, {
2473
+ default: withCtx(() => [createVNode(unref(NButton), {
2474
+ type: "primary",
2475
+ onClick: unref(handleSubmit)
2476
+ }, {
2477
+ default: withCtx(() => _cache[11] || (_cache[11] = [createTextVNode("提交", -1)])),
2478
+ _: 1,
2479
+ __: [11]
2480
+ }, 8, ["onClick"]), createVNode(unref(NButton), { onClick: unref(handleReset) }, {
2481
+ default: withCtx(() => _cache[12] || (_cache[12] = [createTextVNode("重置", -1)])),
2482
+ _: 1,
2483
+ __: [12]
2484
+ }, 8, ["onClick"])]),
2485
+ _: 1
2486
+ })])]),
2487
+ _: 3
2488
+ })) : createCommentVNode("v-if", true)
2489
+ ]),
2490
+ _: 3
2491
+ }, 16, [
2492
+ "model",
2493
+ "rules",
2494
+ "label-placement",
2495
+ "label-width",
2496
+ "size",
2497
+ "disabled",
2498
+ "readonly"
2499
+ ]);
2500
+ };
2501
+ }
2502
+ });
2503
+
2504
+ //#endregion
2505
+ //#region src/components/C_Form/index.vue
2506
+ var C_Form_default = index_vue_vue_type_script_setup_true_lang_default;
2507
+
2508
+ //#endregion
2509
+ export { registerRenderer as a, FORM_DEFAULTS as c, shouldShowActions as d, useDynamicFormState as i, LAYOUTS_WITH_OWN_CONTROLS as l, DYNAMIC_FORM_STATE_KEY as n, useFormRenderer as o, FIELD_TYPE_OPTIONS as r, useFormState as s, C_Form_default as t, resolveFormConfig as u };
2510
+ //# sourceMappingURL=C_Form2.js.map