element-plus-fast-form
Version:
基于 Vue 3 和 Element-Plus 的表单和 CRUD 组件封装库
1,029 lines (967 loc) • 36.5 kB
JavaScript
const __BROWSER__ = typeof window !== 'undefined' && typeof document !== 'undefined';
import { ref, reactive, h, isReactive, isRef, toRaw, resolveComponent, Fragment, defineComponent, createVNode, mergeProps, isVNode, markRaw, nextTick, watch } from 'vue';
import { Plus, Delete } from '@element-plus/icons-vue';
var EOptions = /* @__PURE__ */ ((EOptions2) => {
EOptions2["el-select"] = "el-option";
EOptions2["el-radio-group"] = "el-radio";
EOptions2["el-checkbox-group"] = "el-checkbox";
EOptions2["el-cascader"] = "span";
EOptions2["el-tree-select"] = "span";
return EOptions2;
})(EOptions || {});
const virtualizedComponentMap = {
"el-select-v2": "options"
};
const InitialValueMap = {
"el-checkbox-group": []
};
const getType = (val) => Object.prototype.toString.call(val).slice(8, -1).toLowerCase();
function cloneDeepWith(value, customizer, map = /* @__PURE__ */ new WeakMap()) {
if (value === null || typeof value !== "object") {
return value;
}
if (map.has(value)) {
return map.get(value);
}
if (customizer) {
const result = customizer(value, null, void 0, map);
if (result !== void 0) {
return result;
}
}
const type = getType(value);
switch (type) {
case "date": {
if (value instanceof Date) {
return new Date(value.getTime());
}
return value;
}
case "regexp": {
if (value instanceof RegExp) {
const flags = value.flags;
const result = new RegExp(value.source, flags);
result.lastIndex = value.lastIndex;
return result;
}
return value;
}
case "map": {
if (value instanceof Map) {
const mapResult = /* @__PURE__ */ new Map();
map.set(value, mapResult);
value.forEach((val, key) => {
const keyResult = typeof key === "object" && key !== null ? cloneDeepWith(key, customizer, map) : key;
const valueResult = cloneDeepWith(val, customizer, map);
mapResult.set(keyResult, valueResult);
});
return mapResult;
}
return value;
}
case "set": {
if (value instanceof Set) {
const setResult = /* @__PURE__ */ new Set();
map.set(value, setResult);
value.forEach((val) => {
const itemResult = cloneDeepWith(val, customizer, map);
setResult.add(itemResult);
});
return setResult;
}
return value;
}
case "array": {
if (Array.isArray(value)) {
const arrResult = [];
map.set(value, arrResult);
value.forEach((item, index) => {
const customResult = customizer ? customizer(item, index, value, map) : void 0;
arrResult[index] = customResult !== void 0 ? customResult : cloneDeepWith(item, customizer, map);
});
return arrResult;
}
return value;
}
case "object": {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return value;
}
const proto = Object.getPrototypeOf(value);
const ctor = proto?.constructor;
if (ctor && ctor !== Object) {
try {
const objResult2 = new ctor();
map.set(value, objResult2);
Object.entries(value).forEach(([key, val]) => {
if (Object.prototype.hasOwnProperty.call(value, key)) {
const customResult = customizer ? customizer(val, key, value, map) : void 0;
objResult2[key] = customResult !== void 0 ? customResult : cloneDeepWith(val, customizer, map);
}
});
return objResult2;
} catch (e) {
console.warn("Failed to clone with constructor, falling back to plain object clone", e);
}
}
const objResult = Object.create(proto);
map.set(value, objResult);
const allKeys = [
...Object.keys(value),
...Object.getOwnPropertySymbols(value)
];
allKeys.forEach((key) => {
if (Object.prototype.propertyIsEnumerable.call(value, key)) {
const val = value[key];
const customResult = customizer ? customizer(val, key, value, map) : void 0;
objResult[key] = customResult !== void 0 ? customResult : cloneDeepWith(val, customizer, map);
}
});
return objResult;
}
default:
return value;
}
}
const groupRow = "_groupRow_72w7s_8";
const border = "_border_72w7s_13";
const hasOperate = "_hasOperate_72w7s_20";
const operate = "_operate_72w7s_24";
const hasSuffix = "_hasSuffix_72w7s_30";
const styles = {
groupRow: groupRow,
border: border,
hasOperate: hasOperate,
operate: operate,
hasSuffix: hasSuffix
};
function randomHashStr() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
function _isSlot(s) {
return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !isVNode(s);
}
// 自定义 cloneDeep,遇到 component 字段直接 markRaw, 避免被reactive,提高性能
function deepCloneWithMarkRaw(obj) {
return cloneDeepWith(obj, (value, key) => {
if (key === "component" && (typeof value === "object" || typeof value === "function")) {
return markRaw(value);
}
// lodash 默认处理
return undefined;
});
}
class FormCore {
formValue = {};
formRef = ref();
formConfig = [];
rowProps = {};
colProps = {};
formProps = {};
disabled = ref(false);
showOperate = true;
constructor(config) {
const processedConfig = this._handleConfig(config.formConfig);
this.formConfig = reactive(processedConfig);
this.rowProps = this._handleConfig(config.rowProps || {});
this.colProps = this._handleConfig(config.colProps || {});
this.formProps = this._handleConfig(config.formProps || {});
this.disabled.value = this.formProps?.disabled ?? false;
const model = this._handleConfig(this.formProps?.model || {});
this.showOperate = config.showOperate ?? true;
const vals = this._initValue(model, processedConfig);
Object.assign(this.formValue, vals);
this.formValue = reactive(this.formValue);
this.addItem = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
}
_generateSuffix(itemConfig) {
return h("span", {}, [itemConfig?.suffix]);
}
_generateComponent(itemConfig, slots, nestedData) {
// console.log("---_generateComponent---", itemConfig.formItemProps.prop);
let component = null;
if (itemConfig.component === "slot") {
component = this._generateSlot(itemConfig, slots, nestedData);
} else if (this._isVueComponent(itemConfig.component)) {
component = this._generateCustomComponent(itemConfig, nestedData);
} else if (typeof itemConfig?.component === "string") {
if (itemConfig?.component?.startsWith("el-")) {
component = this._generateElementComponent(itemConfig, nestedData);
} else {
component = this._generateString(itemConfig);
}
}
if (itemConfig?.suffix) {
component = h("div", {
className: styles.hasSuffix,
style: {
width: itemConfig.componentProps?.style?.width
}
}, [component, this._generateSuffix(itemConfig)]);
}
return component;
}
_generateNestedData(nestedKey, prop, value) {
const lastData = {
[prop]: value
};
if (!this.formValue[nestedKey.prop]) {
this.formValue[nestedKey.prop] = [];
this.formValue[nestedKey.prop][nestedKey.key] = lastData;
} else {
this.formValue[nestedKey.prop][nestedKey.key] = {
...this.formValue[nestedKey.prop][nestedKey.key],
...lastData
};
}
}
/**
* 处理配置对象
* @param config 配置对象
* @returns 处理后的普通对象
*/
_handleConfig(config) {
const rawConfig = isReactive(config) || isRef(config) ? toRaw(config) : config;
return deepCloneWithMarkRaw(rawConfig);
}
_isVueComponent(arg) {
return Object.prototype.hasOwnProperty.call(arg, "render") || Object.prototype.hasOwnProperty.call(arg, "setup");
}
_isNoFormItemProps(itemConfig) {
if (itemConfig.formItemProps?.prop === "" || !itemConfig.formItemProps?.prop) {
return true;
}
return false;
}
/**
* 获取子类方法
* @returns 子类方法对象
*/
_getSubClassMethods() {
const proto = Object.getPrototypeOf(this);
const methods = {};
Object.getOwnPropertyNames(proto).forEach(key => {
if (typeof this[key] === "function" && key !== "constructor") {
methods[key] = this[key].bind(this);
}
});
return methods;
}
_generateElementComponent(itemConfig, nestedData) {
if (this._isNoFormItemProps(itemConfig)) {
return null;
}
return h(resolveComponent(itemConfig.component), {
...itemConfig.componentProps,
modelValue: nestedData ? this.formValue?.[nestedData.prop]?.[nestedData.key]?.[itemConfig.formItemProps.prop] : this.formValue[itemConfig.formItemProps.prop],
"onUpdate:modelValue": value => {
if (nestedData) {
this._generateNestedData(nestedData, itemConfig.formItemProps.prop, value);
} else {
this.formValue[itemConfig.formItemProps.prop] = value;
}
},
style: {
width: itemConfig.componentProps?.style?.width || "100%"
},
nestedKey: nestedData?.key ?? null,
nestedProp: nestedData?.prop ?? null
}, itemConfig?.componentProps?.options?.length && !virtualizedComponentMap.hasOwnProperty(itemConfig.component) ? {
default: c => {
const data = c?.data || {};
return EOptions[itemConfig.component] == "span" ? data?.label : h(Fragment, {}, [...(itemConfig?.componentProps?.options || []).map(i => {
return h(resolveComponent(EOptions[itemConfig.component]), {
key: i.key || i.value,
...i
});
})]);
}
} : {});
}
_generateString(itemConfig) {
const defaultValue = itemConfig.defaultValue;
return h(itemConfig.component, {
...itemConfig.componentProps
}, defaultValue || null);
}
_generateCustomComponent(itemConfig, nestedData) {
const methods = this._getSubClassMethods();
return h(itemConfig.component, {
formValue: this.formValue,
...(this._isNoFormItemProps(itemConfig) ? {} : {
prop: itemConfig.formItemProps.prop,
modelValue: nestedData ? this.formValue?.[nestedData.prop]?.[nestedData.key]?.[itemConfig.formItemProps.prop] : this.formValue[itemConfig.formItemProps.prop],
"onUpdate:modelValue": value => {
if (nestedData) {
this._generateNestedData(nestedData, itemConfig.formItemProps.prop, value);
} else {
this.formValue[itemConfig.formItemProps.prop] = value;
}
}
}),
nestedKey: nestedData?.key ?? null,
nestedProp: nestedData?.prop ?? null,
...methods,
...itemConfig.componentProps
});
}
_generateSlot(itemConfig, slots, nestedData) {
// const methods = this._getSubClassMethods();
return slots?.[itemConfig.formItemProps.prop]?.({
formValue: this.formValue,
...(this._isNoFormItemProps(itemConfig) ? {} : {
modelValue: nestedData ? this.formValue?.[nestedData.prop]?.[nestedData.key]?.[itemConfig.formItemProps.prop] : this.formValue[itemConfig.formItemProps.prop]
}),
nestedKey: nestedData?.key ?? null,
nestedProp: nestedData?.prop ?? null
// ...methods,
});
}
_initValue(formValue, config, isRemoveValue = true,
// 是否删除旧值
parentProp, key) {
const values = {};
config.filter(i => i?.formItemProps?.prop)?.forEach(item => {
if (item.children) {
const listValue = [];
item.children.forEach((i, k) => {
listValue.push(this._initValue(formValue, i, isRemoveValue, item.formItemProps.prop, k));
});
values[item.formItemProps.prop] = listValue;
} else {
const initialValue = InitialValueMap[item.component] || null;
const preValue = parentProp ? formValue?.[parentProp]?.[key]?.[item.formItemProps.prop] || null : formValue[item.formItemProps.prop] || null;
const defaultValue = item.defaultValue;
values[item.formItemProps.prop] = isRemoveValue ? defaultValue || initialValue || preValue : preValue;
}
});
return values;
}
addItem(prop, config) {
const index = this.formConfig.findIndex(i => i.formItemProps.prop === prop);
if (index > -1) {
if (config && config.length > 0) {
this.formConfig[index].children.push(config);
} else {
this.formConfig[index].children.push(this.formConfig[index]?.children?.[0]);
}
const processedConfig = this._handleConfig(this.formConfig);
const vals = this._initValue(this.formValue, processedConfig);
Object.assign(this.formValue, vals);
}
}
removeItem(prop, key) {
const index = this.formConfig.findIndex(i => i.formItemProps.prop === prop);
if (index > -1) {
this.formValue[prop].splice(key, 1);
this.formConfig[index].children.splice(key, 1);
const processedConfig = this._handleConfig(this.formConfig);
const vals = this._initValue(this.formValue, processedConfig);
Object.assign(this.formValue, vals);
}
}
getform() {
return /* @__PURE__ */ defineComponent({
setup: (_props, {
slots
}) => {
return () => {
let _slot3;
const finalFormProps = {
ref: this.formRef,
...this.formProps,
model: this.formValue,
disabled: this.disabled.value
};
// console.log("---setup----");
return createVNode(resolveComponent("el-form"), finalFormProps, {
default: () => [createVNode(resolveComponent("el-row"), mergeProps(this.rowProps || {}, {
"gutter": this.rowProps?.gutter || 24
}), _isSlot(_slot3 = (this.formConfig || []).map(item => {
let _slot2;
return Array.isArray(item.children) ? createVNode("div", {
"class": `${styles.groupRow}`
}, [(item.children || []).map((citem, ckey) => {
return createVNode(resolveComponent("el-row"), mergeProps(this.rowProps || {}, {
"gutter": this.rowProps?.gutter || 24,
"class": [styles.border, this.showOperate ? styles.hasOperate : ""]
}), {
default: () => [citem?.map(c => {
let _slot;
return createVNode(resolveComponent("el-col"), mergeProps({
"key": `${item.formItemProps.prop}.${ckey}.${c.formItemProps?.prop || randomHashStr()}`
}, c?.colProps || this.colProps || {}, {
"span": c?.colProps?.span || this.colProps?.span || 24
}), {
default: () => [createVNode(resolveComponent("el-form-item"), mergeProps(c.formItemProps, {
"class": styles["form-item"],
"prop": `${item.formItemProps.prop}.${ckey}.${c.formItemProps?.prop || randomHashStr()}`
}), _isSlot(_slot = this._generateComponent(c, slots, {
prop: item.formItemProps.prop,
key: ckey
})) ? _slot : {
default: () => [_slot]
})]
});
}), this.showOperate && createVNode("div", {
"class": styles.operate
}, [createVNode(resolveComponent("el-button"), {
"icon": Plus,
"circle": true,
"size": "small",
"onClick": () => {
this.addItem(item.formItemProps.prop);
}
}, null), ckey > 0 ? createVNode(resolveComponent("el-popconfirm"), {
"title": "\u786E\u5B9A\u5220\u9664\u5417\uFF1F",
"onConfirm": () => {
this.removeItem(item.formItemProps.prop, ckey);
},
"confirm-button-text": "\u786E\u5B9A",
"cancel-button-text": "\u53D6\u6D88"
}, {
reference: createVNode(resolveComponent("el-button"), {
"icon": Delete,
"circle": true,
"size": "small"
}, null)
}) : null])]
});
})]) : createVNode(resolveComponent("el-col"), mergeProps(item?.colProps || this.colProps || {}, {
"span": item?.colProps?.span || this.colProps?.span || 8,
"key": item.formItemProps.prop
}), {
default: () => [createVNode(resolveComponent("el-form-item"), mergeProps(item.formItemProps, {
"class": styles["form-item"]
}), _isSlot(_slot2 = this._generateComponent(item, slots)) ? _slot2 : {
default: () => [_slot2]
})]
}) // jsx不支持v-memo指令,目前版本只有模板预发支持!
// withDirectives(
// h(
// resolveComponent("el-col"),
// {
// ...(item?.colProps || this.colProps || {}),
// span: item?.colProps?.span || this.colProps?.span || 8,
// key: item.formItemProps.prop,
// },
// [
// h(
// resolveComponent("el-form-item"),
// {
// ...item.formItemProps,
// class: styles["form-item"],
// },
// this._generateComponent(item, slots)
// ),
// ]
// )
// ,
// [
// [
// resolveDirective("memo")!,
// [
// item.formItemProps.prop, // 唯一标识
// this.formValue[item.formItemProps.prop],
// ],
// ],
// ]
// )
;
})) ? _slot3 : {
default: () => [_slot3]
})]
});
};
}
});
}
}
class Form extends FormCore {
constructor(config) {
super(config);
this.setComponentProps = this.setComponentProps.bind(this);
this.setFormValue = this.setFormValue.bind(this);
this.setFormConfig = this.setFormConfig.bind(this);
this.setFormConfigs = this.setFormConfigs.bind(this);
this.addFormConfig = this.addFormConfig.bind(this);
this.removeFormConfig = this.removeFormConfig.bind(this);
this.setFormDisabled = this.setFormDisabled.bind(this);
}
/**
* 解析嵌套prop路径
* @param prop prop字符串,支持格式:
* - "parentProp.index.childProp" (3段式,用于设置/删除)
* - "parentProp.index" (2段式,用于添加)
* - "normalProp" (普通格式)
* @returns 解析结果
*/
_parseNestedProp(prop) {
const parts = prop.split(".");
// 支持3段式:parentProp.index.childProp(用于设置/删除操作)
if (parts.length === 3) {
const [parentProp, indexStr, childProp] = parts;
const childIndex = parseInt(indexStr, 10);
if (!isNaN(childIndex)) {
return {
isNested: true,
parentProp,
childIndex,
childProp,
originalProp: prop,
isAddMode: false
};
}
}
// 支持2段式:parentProp.index(用于添加操作)
if (parts.length === 2) {
const [parentProp, indexStr] = parts;
const childIndex = parseInt(indexStr, 10);
if (!isNaN(childIndex)) {
return {
isNested: true,
parentProp,
childIndex,
originalProp: prop,
isAddMode: true
};
}
}
return {
isNested: false,
originalProp: prop
};
}
/**
* 根据解析后的路径找到目标配置项
* @param parsedProp 解析后的prop信息
* @returns 目标配置项
*/
_findTargetConfig(parsedProp) {
if (!parsedProp.isNested) {
// 非嵌套,直接查找
return this.formConfig.find(item => item.formItemProps.prop === parsedProp.originalProp) || null;
}
// 嵌套格式,先找父级配置
const parentConfig = this.formConfig.find(item => item.formItemProps.prop === parsedProp.parentProp);
if (!parentConfig || !parentConfig.children) {
return null;
}
// 如果是添加模式(2段式),返回父级配置用于后续验证
if (parsedProp.isAddMode) {
return parentConfig;
}
// 设置/删除模式(3段式),找到指定索引的子配置数组
const childConfigs = parentConfig.children[parsedProp.childIndex];
if (!Array.isArray(childConfigs)) {
return null;
}
// 在子配置数组中找到目标配置项
return childConfigs.find(item => item.formItemProps.prop === parsedProp.childProp) || null;
}
/**
* 动态更新表单项的组件属性
* @param prop 表单项的 prop 值,支持嵌套格式: "parentProp.index.childProp"
* @param componentProps 需要更新的组件属性对象
*/
setComponentProps(prop, componentProps) {
const parsedProp = this._parseNestedProp(prop);
const targetItem = this._findTargetConfig(parsedProp);
if (targetItem) {
if (!targetItem.componentProps) {
targetItem.componentProps = {};
}
// 只赋值属性,不整体替换对象,以保持响应式
Object.assign(targetItem.componentProps, componentProps);
}
}
/**
* 动态更新表单值
* @param key 表单项的 key
* @param value 要更新的值
*/
setFormValue(formData) {
Object.assign(this.formValue, formData);
}
/**
* 更新指定表单项的配置
* @param prop 表单项的 prop 值,支持嵌套格式: "parentProp.index.childProp"
* @param config 新的配置项
*/
setFormConfig(prop, config) {
const parsedProp = this._parseNestedProp(prop);
const targetItem = this._findTargetConfig(parsedProp);
if (targetItem) {
// 创建新的配置项
const newConfig = {
...targetItem,
formItemProps: {
...targetItem.formItemProps,
...(config.formItemProps || {}),
prop: parsedProp.isNested ? parsedProp.childProp : parsedProp.originalProp // 确保prop正确
},
componentProps: {
...targetItem.componentProps,
...(config.componentProps || {})
},
...(config.component && {
component: config.component
}),
...(config.colProps && {
colProps: {
...targetItem.colProps,
...config.colProps
}
})
};
// 更新配置项
if (parsedProp.isNested) {
// 嵌套情况:需要找到父配置和子配置数组
const parentConfig = this.formConfig.find(item => item.formItemProps.prop === parsedProp.parentProp);
if (parentConfig && parentConfig.children) {
const childConfigs = parentConfig.children[parsedProp.childIndex];
const targetIndex = childConfigs.findIndex(item => item.formItemProps.prop === parsedProp.childProp);
if (targetIndex > -1) {
childConfigs[targetIndex] = newConfig;
}
}
} else {
// 非嵌套情况:直接更新顶层配置
const targetIndex = this.formConfig.findIndex(item => item.formItemProps.prop === parsedProp.originalProp);
if (targetIndex > -1) {
this.formConfig[targetIndex] = newConfig;
}
}
// 处理defaultValue更新
if (config.hasOwnProperty("defaultValue")) {
if (parsedProp.isNested) {
// 嵌套表单的值更新
if (!this.formValue[parsedProp.parentProp]) {
this.formValue[parsedProp.parentProp] = [];
}
if (!this.formValue[parsedProp.parentProp][parsedProp.childIndex]) {
this.formValue[parsedProp.parentProp][parsedProp.childIndex] = {};
}
this.formValue[parsedProp.parentProp][parsedProp.childIndex][parsedProp.childProp] = config.defaultValue;
} else {
// 顶层表单的值更新
this.formValue[parsedProp.originalProp] = config.defaultValue;
}
}
}
}
/**
* 批量替换表单配置
* @param newFormConfig 新的表单配置数组
* @param isRemoveValue 是否删除旧值
*/
async setFormConfigs(newFormConfig, isRemoveValue = true) {
// 处理新配置
const processedConfig = this._handleConfig(newFormConfig);
if (isRemoveValue) {
// 完全重新初始化模式
// 清空当前配置
this.formConfig.splice(0, this.formConfig.length);
// 添加新配置
this.formConfig.push(...processedConfig);
// 重新初始化表单值
const vals = this._initValue({}, processedConfig);
// 清空并重新设置表单值
Object.keys(this.formValue).forEach(key => {
delete this.formValue[key];
});
Object.assign(this.formValue, vals);
} else {
// 保留旧值模式
// 保存当前表单值的副本
const currentFormValue = {
...this.formValue
};
// 清空当前配置
this.formConfig.splice(0, this.formConfig.length);
// 添加新配置
this.formConfig.push(...processedConfig);
// 使用当前表单值作为基础,为新配置项初始化值
const vals = this._initValue(currentFormValue, processedConfig, false);
// 清空并重新设置表单值(保留旧值 + 新配置项的初始值)
Object.keys(this.formValue).forEach(key => {
delete this.formValue[key];
});
Object.assign(this.formValue, vals);
}
await nextTick();
setTimeout(() => {
this.formRef.value?.clearValidate();
}, 0);
}
/**
* 在指定位置添加表单配置项
* @param config 要添加的表单配置
* @param targetProp 目标位置,支持嵌套格式: "parentProp.index" 表示在父级数组的指定索引位置添加
* @param index 插入的位置索引,如果不传则追加到末尾(仅在非嵌套情况下有效)
*/
addFormConfig(config, targetProp, index) {
if (!targetProp) {
// 没有指定目标位置,使用原有逻辑在顶层添加
this._addToTopLevel(config, index);
return;
}
const parsedTarget = this._parseNestedProp(targetProp);
if (parsedTarget.isNested) {
// 嵌套格式:在指定的子数组中添加配置项
this._addToNestedArray(config, parsedTarget);
} else {
// 普通格式:在顶层添加
this._addToTopLevel(config, index);
}
}
/**
* 在顶层添加配置项
*/
_addToTopLevel(config, index) {
// 检查是否已存在相同 prop 的配置
const exists = this.formConfig.some(item => item.formItemProps.prop === config.formItemProps.prop);
if (!exists) {
// 处理新配置
const processedConfig = this._handleConfig([config])[0];
// 根据 index 插入配置
if (typeof index === "number" && index >= 0 && index <= this.formConfig.length) {
this.formConfig.splice(index, 0, processedConfig);
} else {
this.formConfig.push(processedConfig);
}
// 初始化表单值
const vals = this._initValue(this.formValue, [processedConfig]);
// 更新表单值
Object.assign(this.formValue, vals);
}
}
/**
* 在嵌套数组中添加配置项
*/
_addToNestedArray(config, parsedTarget) {
// 找到父级配置
const parentConfig = this.formConfig.find(item => item.formItemProps.prop === parsedTarget.parentProp);
if (parentConfig && parentConfig.children) {
// 检查指定索引的子数组是否存在
const childIndex = parsedTarget.childIndex;
const childrenLength = parentConfig.children.length;
if (childIndex < childrenLength) {
const childConfigs = parentConfig.children[childIndex];
// 检查是否已存在相同 prop 的配置
const exists = childConfigs.some(item => item.formItemProps.prop === config.formItemProps.prop);
if (!exists) {
// 处理新配置
const processedConfig = this._handleConfig([config])[0];
// 添加到子数组
childConfigs.push(processedConfig);
// 更新表单值 - 为新添加的配置项初始化值
const vals = this._initValue({}, [processedConfig]);
// 确保嵌套表单值结构正确
if (!this.formValue[parsedTarget.parentProp]) {
this.formValue[parsedTarget.parentProp] = [];
}
if (!this.formValue[parsedTarget.parentProp][parsedTarget.childIndex]) {
this.formValue[parsedTarget.parentProp][parsedTarget.childIndex] = {};
}
// 将新配置项的初始值合并到对应的嵌套表单值中
Object.assign(this.formValue[parsedTarget.parentProp][parsedTarget.childIndex], vals);
}
}
}
}
/**
* 删除表单配置项
* @param props 要删除的表单项的 prop 值数组,支持嵌套格式: "parentProp.index.childProp"
* @param isRemoveValue 是否删除旧值
*/
removeFormConfig(props, isRemoveValue = true) {
// 分离普通prop和嵌套prop
const topLevelProps = [];
const nestedProps = [];
props.forEach(prop => {
const parsedProp = this._parseNestedProp(prop);
if (parsedProp.isNested) {
nestedProps.push({
parsedProp,
originalProp: prop
});
} else {
topLevelProps.push(prop);
}
});
// 删除嵌套配置项
this._removeNestedConfigs(nestedProps, isRemoveValue);
// 删除顶层配置项
this._removeTopLevelConfigs(topLevelProps, isRemoveValue);
}
/**
* 删除顶层配置项
*/
_removeTopLevelConfigs(props, isRemoveValue = true) {
// 找出所有要删除的索引
const indexesToRemove = props.reduce((acc, prop) => {
const index = this.formConfig.findIndex(item => item.formItemProps.prop === prop);
if (index > -1) {
acc.push(index);
}
return acc;
}, []);
if (indexesToRemove.length > 0) {
// 从大到小排序,避免删除时索引变化
indexesToRemove.sort((a, b) => b - a);
// 批量删除配置
indexesToRemove.forEach(index => {
const prop = this.formConfig[index].formItemProps.prop;
this.formConfig.splice(index, 1);
if (isRemoveValue) {
delete this.formValue[prop];
}
});
}
}
/**
* 删除嵌套配置项
*/
_removeNestedConfigs(nestedProps, isRemoveValue = true) {
// 按父级prop分组,便于批量操作
const groupedByParent = {};
nestedProps.forEach(item => {
const parentProp = item.parsedProp.parentProp;
if (!groupedByParent[parentProp]) {
groupedByParent[parentProp] = [];
}
groupedByParent[parentProp].push(item);
});
// 逐个处理每个父级
Object.entries(groupedByParent).forEach(([parentProp, items]) => {
const parentConfig = this.formConfig.find(item => item.formItemProps.prop === parentProp);
if (parentConfig && parentConfig.children) {
// 按子数组索引分组
const groupedByChildIndex = {};
items.forEach(item => {
const childIndex = item.parsedProp.childIndex;
if (!groupedByChildIndex[childIndex]) {
groupedByChildIndex[childIndex] = [];
}
groupedByChildIndex[childIndex].push(item);
});
// 处理每个子数组
Object.entries(groupedByChildIndex).forEach(([childIndexStr, childItems]) => {
const childIndex = parseInt(childIndexStr, 10);
if (childIndex < parentConfig.children.length) {
const childConfigs = parentConfig.children[childIndex];
// 找到要删除的配置项索引
const indexesToRemove = [];
childItems.forEach(item => {
const index = childConfigs.findIndex(config => config.formItemProps.prop === item.parsedProp.childProp);
if (index > -1) {
indexesToRemove.push(index);
}
});
// 从大到小排序删除
indexesToRemove.sort((a, b) => b - a);
// 删除配置项和对应的表单值
indexesToRemove.forEach(index => {
const childProp = childConfigs[index].formItemProps.prop;
childConfigs.splice(index, 1);
// 删除对应的表单值
if (isRemoveValue && this.formValue[parentProp] && this.formValue[parentProp][childIndex] && this.formValue[parentProp][childIndex][childProp] !== undefined) {
delete this.formValue[parentProp][childIndex][childProp];
}
});
}
});
}
});
}
setFormDisabled(disabled) {
this.disabled.value = disabled;
}
}
function useForm(config) {
const form = new Form(config);
const FastForm = form.getform();
const isInit = ref(false);
const {
formRef,
formValue,
addItem,
removeItem,
setComponentProps,
setFormValue,
setFormConfig,
addFormConfig,
removeFormConfig,
setFormDisabled,
setFormConfigs
} = form;
if (isReactive(config) || isRef(config.formConfig)) {
watch(() => config.formConfig, newVal => {
if (newVal.length > 0 && !isInit.value) {
isInit.value = true;
form.setFormConfigs(newVal);
}
});
}
return {
FastForm,
formValue,
rawFormValue: toRaw(formValue),
formRef,
addItem,
removeItem,
setComponentProps,
setFormValue,
setFormConfig,
addFormConfig,
removeFormConfig,
setFormDisabled,
setFormConfigs
};
}
const hooks = {
useForm
};
const ElementPlusFastForm = {
install: (app) => {
app.config.globalProperties.$useForm = useForm;
},
...hooks
};
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
if (isBrowser) {
const debounce = (fn, delay) => {
let timer = null;
return function(...args) {
const _t = this;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(_t, args);
}, delay);
};
};
if (window.ResizeObserver) {
const _ResizeObserver = window.ResizeObserver;
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
constructor(callback) {
callback = debounce(callback, 16);
super(callback);
}
};
}
}
export { ElementPlusFastForm as default, useForm };
// 条件性注入 CSS
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
const style = document.createElement('style');
style.textContent = "._form-item_1d203_1 :deep(._el-form-item__content_1d203_1 > *){width:100%}._form-item_1d203_1 .el-form-item__content>*{width:100%}._groupRow_1d203_8{width:100%;padding:0 24px 24px}._border_1d203_13{border:1px solid #dcdfe6;padding:12px 0;margin-bottom:12px;border-radius:8px;position:relative}._operate_1d203_21{position:absolute;right:8px;bottom:8px}\n";
document.head.appendChild(style);
}
// 条件性注入 CSS
if (typeof document !== 'undefined') {
const style = document.createElement('style');
style.textContent = "._form-item_72w7s_1 :deep(._el-form-item__content_72w7s_1 > *){width:100%}._form-item_72w7s_1 .el-form-item__content>*{width:100%}._groupRow_72w7s_8{width:100%;padding:0 24px 24px}._border_72w7s_13{border:1px solid #dcdfe6;padding:12px;margin-bottom:12px;border-radius:8px;position:relative}._border_72w7s_13._hasOperate_72w7s_20{padding-bottom:24px}._operate_72w7s_24{position:absolute;right:8px;bottom:8px}._hasSuffix_72w7s_30{display:flex;align-items:center}._hasSuffix_72w7s_30>*:last-child{margin-left:8px;font-size:14px}._hasSuffix_72w7s_30>*:first-child{flex:1;width:unset!important}\n";
document.head.appendChild(style);
}