@robot-admin/naive-ui-components 0.4.3 → 0.4.4

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