@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.
- package/dist/C_ActionBar.cjs +1 -5
- package/dist/C_ActionBar.js +1 -4
- package/dist/C_ActionBar.js.map +1 -0
- package/dist/C_ActionBar2.js +1 -195
- package/dist/C_ActionBar2.js.map +1 -1
- package/dist/C_AntV.cjs +1 -8
- package/dist/C_AntV.js +1 -4
- package/dist/C_AntV.js.map +1 -0
- package/dist/C_AntV2.js +4 -3149
- package/dist/C_AntV2.js.map +1 -1
- package/dist/C_Barcode.cjs +1 -4
- package/dist/C_Barcode.js +1 -3
- package/dist/C_Barcode.js.map +1 -0
- package/dist/C_Barcode2.js +1 -67
- package/dist/C_Barcode2.js.map +1 -1
- package/dist/C_Captcha.cjs +1 -4
- package/dist/C_Captcha.js +1 -3
- package/dist/C_Captcha.js.map +1 -0
- package/dist/C_Captcha2.js +1 -154
- package/dist/C_Captcha2.js.map +1 -1
- package/dist/C_Cascade.cjs +1 -4
- package/dist/C_Cascade.js +1 -3
- package/dist/C_Cascade.js.map +1 -0
- package/dist/C_Cascade2.js +1 -102
- package/dist/C_Cascade2.js.map +1 -1
- package/dist/C_City.cjs +1 -4
- package/dist/C_City.js +1 -3
- package/dist/C_City.js.map +1 -0
- package/dist/C_City2.js +1 -840
- package/dist/C_City2.js.map +1 -1
- package/dist/C_Code.cjs +1 -5
- package/dist/C_Code.js +1 -4
- package/dist/C_Code.js.map +1 -0
- package/dist/C_Code2.js +1 -346
- package/dist/C_Code2.js.map +1 -1
- package/dist/C_CollapsePanel.cjs +1 -6
- package/dist/C_CollapsePanel.js +1 -4
- package/dist/C_CollapsePanel.js.map +1 -0
- package/dist/C_CollapsePanel2.js +1 -318
- package/dist/C_CollapsePanel2.js.map +1 -1
- package/dist/C_Cron.cjs +1 -15
- package/dist/C_Cron.js +1 -4
- package/dist/C_Cron.js.map +1 -0
- package/dist/C_Cron2.js +1 -1209
- package/dist/C_Cron2.js.map +1 -1
- package/dist/C_Date.cjs +1 -4
- package/dist/C_Date.js +1 -3
- package/dist/C_Date.js.map +1 -0
- package/dist/C_Date2.js +1 -218
- package/dist/C_Date2.js.map +1 -1
- package/dist/C_Draggable.cjs +1 -5
- package/dist/C_Draggable.js +1 -3
- package/dist/C_Draggable.js.map +1 -0
- package/dist/C_Draggable2.js +1 -294
- package/dist/C_Draggable2.js.map +1 -1
- package/dist/C_Editor.cjs +1 -4
- package/dist/C_Editor.js +1 -3
- package/dist/C_Editor.js.map +1 -0
- package/dist/C_Editor2.js +1 -159
- package/dist/C_Editor2.js.map +1 -1
- package/dist/C_FilePreview.cjs +1 -7
- package/dist/C_FilePreview.js +1 -4
- package/dist/C_FilePreview.js.map +1 -0
- package/dist/C_FilePreview2.js +1 -1031
- package/dist/C_FilePreview2.js.map +1 -1
- package/dist/C_Form.cjs +1 -16
- package/dist/C_Form.js +1 -5
- package/dist/C_Form.js.map +1 -0
- package/dist/C_Form2.js +1 -2569
- package/dist/C_Form2.js.map +1 -1
- package/dist/C_FormSearch.cjs +1 -6
- package/dist/C_FormSearch.js +1 -3
- package/dist/C_FormSearch.js.map +1 -0
- package/dist/C_FormSearch2.js +1 -355
- package/dist/C_FormSearch2.js.map +1 -1
- package/dist/C_FormulaEditor.cjs +1 -13
- package/dist/C_FormulaEditor.js +1 -4
- package/dist/C_FormulaEditor.js.map +1 -0
- package/dist/C_FormulaEditor2.js +1 -1432
- package/dist/C_FormulaEditor2.js.map +1 -1
- package/dist/C_FullCalendar.cjs +1 -9
- package/dist/C_FullCalendar.js +1 -3
- package/dist/C_FullCalendar.js.map +1 -0
- package/dist/C_FullCalendar2.js +1 -376
- package/dist/C_FullCalendar2.js.map +1 -1
- package/dist/C_GlobalSearch.cjs +1 -5
- package/dist/C_GlobalSearch.js +1 -3
- package/dist/C_GlobalSearch.js.map +1 -0
- package/dist/C_GlobalSearch2.js +1 -335
- package/dist/C_GlobalSearch2.js.map +1 -1
- package/dist/C_Guide.cjs +1 -5
- package/dist/C_Guide.js +1 -4
- package/dist/C_Guide.js.map +1 -0
- package/dist/C_Guide2.js +1 -60
- package/dist/C_Guide2.js.map +1 -1
- package/dist/C_Icon.cjs +1 -4
- package/dist/C_Icon.js +1 -3
- package/dist/C_Icon.js.map +1 -0
- package/dist/C_Icon2.js +1 -285
- package/dist/C_Icon2.js.map +1 -1
- package/dist/C_ImageCropper.cjs +1 -6
- package/dist/C_ImageCropper.js +1 -4
- package/dist/C_ImageCropper.js.map +1 -0
- package/dist/C_ImageCropper2.js +1 -722
- package/dist/C_ImageCropper2.js.map +1 -1
- package/dist/C_Language.cjs +1 -5
- package/dist/C_Language.js +1 -4
- package/dist/C_Language.js.map +1 -0
- package/dist/C_Language2.js +1 -78
- package/dist/C_Language2.js.map +1 -1
- package/dist/C_Map.cjs +1 -7
- package/dist/C_Map.js +1 -3
- package/dist/C_Map.js.map +1 -0
- package/dist/C_Map2.js +1 -198
- package/dist/C_Map2.js.map +1 -1
- package/dist/C_Markdown.cjs +1 -4
- package/dist/C_Markdown.js +1 -3
- package/dist/C_Markdown.js.map +1 -0
- package/dist/C_Markdown2.js +1 -185
- package/dist/C_Markdown2.js.map +1 -1
- package/dist/C_NotificationCenter.cjs +1 -20
- package/dist/C_NotificationCenter.js +1 -4
- package/dist/C_NotificationCenter.js.map +1 -0
- package/dist/C_NotificationCenter2.js +1 -1383
- package/dist/C_NotificationCenter2.js.map +1 -1
- package/dist/C_Progress.cjs +1 -4
- package/dist/C_Progress.js +1 -3
- package/dist/C_Progress.js.map +1 -0
- package/dist/C_Progress2.js +1 -102
- package/dist/C_Progress2.js.map +1 -1
- package/dist/C_QRCode.cjs +1 -5
- package/dist/C_QRCode.js +1 -3
- package/dist/C_QRCode.js.map +1 -0
- package/dist/C_QRCode2.js +1 -217
- package/dist/C_QRCode2.js.map +1 -1
- package/dist/C_Signature.cjs +1 -8
- package/dist/C_Signature.js +1 -4
- package/dist/C_Signature.js.map +1 -0
- package/dist/C_Signature2.js +1 -617
- package/dist/C_Signature2.js.map +1 -1
- package/dist/C_SplitPane.cjs +1 -6
- package/dist/C_SplitPane.js +1 -4
- package/dist/C_SplitPane.js.map +1 -0
- package/dist/C_SplitPane2.js +1 -355
- package/dist/C_SplitPane2.js.map +1 -1
- package/dist/C_Steps.cjs +1 -4
- package/dist/C_Steps.js +1 -3
- package/dist/C_Steps.js.map +1 -0
- package/dist/C_Steps2.js +1 -81
- package/dist/C_Steps2.js.map +1 -1
- package/dist/C_Table.cjs +1 -20
- package/dist/C_Table.js +1 -6
- package/dist/C_Table.js.map +1 -0
- package/dist/C_Table2.js +1 -3023
- package/dist/C_Table2.js.map +1 -1
- package/dist/C_Theme.cjs +1 -5
- package/dist/C_Theme.js +1 -4
- package/dist/C_Theme.js.map +1 -0
- package/dist/C_Theme2.js +1 -63
- package/dist/C_Theme2.js.map +1 -1
- package/dist/C_Time.cjs +1 -5
- package/dist/C_Time.js +1 -3
- package/dist/C_Time.js.map +1 -0
- package/dist/C_Time2.js +1 -198
- package/dist/C_Time2.js.map +1 -1
- package/dist/C_Tree.cjs +1 -7
- package/dist/C_Tree.js +1 -4
- package/dist/C_Tree.js.map +1 -0
- package/dist/C_Tree2.js +1 -440
- package/dist/C_Tree2.js.map +1 -1
- package/dist/C_Upload.cjs +1 -12
- package/dist/C_Upload.js +1 -4
- package/dist/C_Upload.js.map +1 -0
- package/dist/C_Upload2.js +2 -1365
- package/dist/C_Upload2.js.map +1 -1
- package/dist/C_VideoPlayer.cjs +1 -23
- package/dist/C_VideoPlayer.js +1 -3
- package/dist/C_VideoPlayer.js.map +1 -0
- package/dist/C_VideoPlayer2.js +3 -1932
- package/dist/C_VideoPlayer2.js.map +1 -1
- package/dist/C_VtableGantt.cjs +1 -6
- package/dist/C_VtableGantt.js +1 -4
- package/dist/C_VtableGantt.js.map +1 -0
- package/dist/C_VtableGantt2.js +1 -872
- package/dist/C_VtableGantt2.js.map +1 -1
- package/dist/C_WaterFall.cjs +1 -13
- package/dist/C_WaterFall.js +1 -3
- package/dist/C_WaterFall.js.map +1 -0
- package/dist/C_WaterFall2.js +1 -364
- package/dist/C_WaterFall2.js.map +1 -1
- package/dist/C_WorkFlow.cjs +1 -8
- package/dist/C_WorkFlow.js +1 -4
- package/dist/C_WorkFlow.js.map +1 -0
- package/dist/C_WorkFlow2.js +1 -1983
- package/dist/C_WorkFlow2.js.map +1 -1
- package/dist/chunk.js +1 -18
- package/dist/city.js +1 -4816
- package/dist/constants.d.ts +2 -2
- package/dist/constants2.d.ts +3 -3
- package/dist/constants3.d.ts +3 -3
- package/dist/constants4.d.ts +5 -5
- package/dist/constants5.d.ts +2 -2
- package/dist/data.d.ts +1 -1
- package/dist/export-helper.js +1 -9
- package/dist/index.cjs +2 -413
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -231
- package/dist/index.js.map +1 -1
- package/dist/index.vue.d.ts +2 -2
- package/dist/index10.vue.d.ts +5 -5
- package/dist/index12.vue.d.ts +3 -3
- package/dist/index13.vue.d.ts +3 -3
- package/dist/index14.vue.d.ts +3 -3
- package/dist/index16.vue.d.ts +3 -3
- package/dist/index3.vue.d.ts +3 -3
- package/dist/index5.vue.d.ts +1 -1
- package/dist/index7.vue.d.ts +4 -4
- package/dist/index8.vue.d.ts +2 -2
- package/dist/resolver.cjs +2 -89
- package/dist/resolver.cjs.map +1 -0
- package/dist/resolver.js +1 -86
- package/dist/resolver.js.map +1 -1
- package/dist/storage.js +1 -30
- package/dist/storage.js.map +1 -1
- package/dist/useCollapsePanel.d.ts +2 -2
- package/dist/useCropperCore.d.ts +8 -8
- package/dist/useDraggableLayout.d.ts +4 -4
- package/dist/useDynamicFormState.d.ts +7 -7
- package/dist/useEdgeInteraction.d.ts +1 -1
- package/dist/useFullscreen.d.ts +2 -2
- package/dist/useInfiniteScroll.d.ts +1 -1
- package/dist/useModalEdit.d.ts +1 -1
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSearchState.d.ts +2 -2
- package/dist/useSignatureHistory.d.ts +1 -1
- package/dist/useSplitResize.d.ts +6 -6
- package/dist/useTimeSelection.d.ts +2 -2
- package/dist/useTreeOperations.d.ts +6 -6
- package/dist/useWorkflowValidation.d.ts +4 -4
- package/package.json +1 -2
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants2.d.ts.map +0 -1
- package/dist/constants3.d.ts.map +0 -1
- package/dist/constants4.d.ts.map +0 -1
- package/dist/constants5.d.ts.map +0 -1
- package/dist/data.d.ts.map +0 -1
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.vue.d.ts.map +0 -1
- package/dist/index10.vue.d.ts.map +0 -1
- package/dist/index11.vue.d.ts.map +0 -1
- package/dist/index12.vue.d.ts.map +0 -1
- package/dist/index13.vue.d.ts.map +0 -1
- package/dist/index14.vue.d.ts.map +0 -1
- package/dist/index15.vue.d.ts.map +0 -1
- package/dist/index16.vue.d.ts.map +0 -1
- package/dist/index2.vue.d.ts.map +0 -1
- package/dist/index3.vue.d.ts.map +0 -1
- package/dist/index4.vue.d.ts.map +0 -1
- package/dist/index5.vue.d.ts.map +0 -1
- package/dist/index6.vue.d.ts.map +0 -1
- package/dist/index7.vue.d.ts.map +0 -1
- package/dist/index8.vue.d.ts.map +0 -1
- package/dist/index9.vue.d.ts.map +0 -1
- package/dist/resolver.d.cts.map +0 -1
- package/dist/resolver.d.ts.map +0 -1
- package/dist/useCalendarEvents.d.ts.map +0 -1
- package/dist/useCollapsePanel.d.ts.map +0 -1
- package/dist/useCropperCore.d.ts.map +0 -1
- package/dist/useDraggableLayout.d.ts.map +0 -1
- package/dist/useDynamicFormState.d.ts.map +0 -1
- package/dist/useEdgeInteraction.d.ts.map +0 -1
- package/dist/useFullscreen.d.ts.map +0 -1
- package/dist/useGlobalSearch.d.ts.map +0 -1
- package/dist/useInfiniteScroll.d.ts.map +0 -1
- package/dist/useModalEdit.d.ts.map +0 -1
- package/dist/useQRCode.d.ts.map +0 -1
- package/dist/useSearchState.d.ts.map +0 -1
- package/dist/useSignatureHistory.d.ts.map +0 -1
- package/dist/useSplitResize.d.ts.map +0 -1
- package/dist/useTimeSelection.d.ts.map +0 -1
- package/dist/useTreeOperations.d.ts.map +0 -1
- 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
|