import { defineComponent, h, nextTick } from "vue"; import { cloneDeep, isBoolean } from "lodash-es"; import { useAction, useForm, usePlugins, useTabs } from "./helper"; import { useBrowser, useConfig, useElApi, useRefs } from "../../hooks"; import { getValue, merge } from "../../utils"; import formHook from "../../utils/form-hook"; import { renderNode } from "../../utils/vnode"; import { parseFormHidden } from "../../utils/parse"; export default defineComponent({ name: "cl-form", props: { inner: Boolean, inline: Boolean }, setup(props, { expose, slots }) { const { refs, setRefs } = useRefs(); const { style, dict } = useConfig(); const browser = useBrowser(); const { Form, config, form, visible, saving, loading, disabled } = useForm(); // 关闭的操作类型 let closeAction: ClForm.CloseAction = "close"; // 旧表单数据 let defForm: obj | undefined; // 选项卡 const Tabs = useTabs({ config, Form }); // 操作 const Action = useAction({ config, form, Form }); // 方法 const ElFormApi = useElApi( ["validate", "validateField", "resetFields", "scrollToField", "clearValidate"], Form ); // 插件 const plugin = usePlugins({ visible }); // 显示加载中 function showLoading() { loading.value = true; } // 隐藏加载 function hideLoading() { loading.value = false; } // 设置是否禁用 function setDisabled(val: boolean = true) { disabled.value = val; } // 请求表单保存状态 function done() { saving.value = false; } // 关闭表单 function close(action?: ClForm.CloseAction) { if (action) { closeAction = action; } beforeClose(() => { visible.value = false; done(); }); } // 关闭前 function beforeClose(done: fn) { if (config.on?.close) { config.on.close(closeAction, done); } else { done(); } } // 关闭后 function onClosed() { Tabs.clear(); Form.value?.clearValidate(); } // 清空表单验证 function clear() { for (const i in form) { delete form[i]; } setTimeout(() => { Form.value?.clearValidate(); }, 0); } // 重置 function reset() { if (defForm) { for (const i in defForm) { form[i] = cloneDeep(defForm[i]); } } } // 表单提交 function submit(callback?: fn) { // 验证表单 Form.value.validate(async (valid: boolean, error: any) => { if (valid) { saving.value = true; // 拷贝表单值 const d = cloneDeep(form); config.items.forEach((e) => { function deep(e: ClForm.Item) { if (e.prop) { // 过滤隐藏的表单项 if (e._hidden) { if (e.prop) { delete d[e.prop]; } } // hook 提交处理 if (e.hook) { formHook.submit({ ...e, value: e.prop ? d[e.prop] : undefined, form: d }); } } if (e.children) { e.children.forEach(deep); } } deep(e); }); // 处理 "-" 多层级 for (const i in d) { if (i.includes("-")) { // 结构参数 const [a, ...arr] = i.split("-"); // 关键值的key const k: string = arr.pop() || ""; if (!d[a]) { d[a] = {}; } let f: any = d[a]; // 设置默认值 arr.forEach((e) => { if (!f[e]) { f[e] = {}; } f = f[e]; }); // 设置关键值 f[k] = d[i]; delete d[i]; } } const submit = callback || config.on?.submit; // 提交事件 if (submit) { submit(await plugin.submit(d), { close() { close("save"); }, done }); } else { done(); } } else { // 切换到对应的选项卡 Tabs.toGroup({ refs, config, prop: Object.keys(error)[0] }); } }); } // 打开表单 function open(options?: ClForm.Options, plugins?: ClForm.Plugin[]) { if (!options) { return console.error("Options is not null"); } // 清空 if (options.isReset !== false) { clear(); } // 显示对话框 visible.value = true; // 默认关闭方式 closeAction = "close"; // 合并配置 for (const i in config) { switch (i) { // 表单项 case "items": function deep(arr: any[]): any[] { return arr.map((e) => { const d = getValue(e); return { ...d, children: d?.children ? deep(d.children) : undefined }; }); } config.items = deep(options.items || []); break; // 事件、参数、操作 case "on": case "op": case "props": case "dialog": case "_data": merge(config[i], options[i] || {}); break; // 其他 default: config[i] = options[i]; break; } } // 预设表单值 if (options?.form) { for (const i in options.form) { form[i] = options.form[i]; } } // 设置表单数据 config.items.forEach((e) => { function deep(e: ClForm.Item) { if (e.prop) { // 解析 prop if (e.prop.includes(".")) { e.prop = e.prop.replace(/\./g, "-"); } // prop 合并 Tabs.mergeProp(e); // hook 绑定值 formHook.bind({ ...e, value: form[e.prop] !== undefined ? form[e.prop] : cloneDeep(e.value), form }); // 表单验证 if (e.required) { e.rules = { required: true, message: `${e.label}${dict.label.nonEmpty}` }; } } // 设置 tabs 默认值 if (e.type == "tabs") { Tabs.set(e.value); } // 子集 if (e.children) { e.children.forEach(deep); } } deep(e); }); // 设置默认值 if (!defForm) { defForm = cloneDeep(form); } // 创建插件 plugin.create(plugins); // 打开回调 nextTick(() => { setTimeout(() => { // 打开事件 if (config.on?.open) { config.on.open(form); } }, 10); }); } // 绑定表单数据 function bindForm(data: any) { config.items.forEach((e) => { function deep(e: ClForm.Item) { formHook.bind({ ...e, value: e.prop ? data[e.prop] : undefined, form: data }); if (e.children) { e.children.forEach(deep); } } deep(e); }); Object.assign(form, data); } // 渲染表单项 function renderFormItem(e: ClForm.Item) { const { isDisabled } = config._data; if (e.type == "tabs") { return ( ); } // 是否隐藏 e._hidden = parseFormHidden(e.hidden, { scope: form }); // 分组显示 const inGroup = e.group ? e.group === Tabs.active.value : true; // 是否已加载完成 const isLoaded = e.component && Tabs.isLoaded(e.group); // 表单项 const FormItem = h( , e.props, { label() { return e.renderLabel ? renderNode(e.renderLabel, { scope: form, render: "slot", slots }) : e.label; }, default() { return (
{["prepend", "component", "append"] .filter((k) => e[k]) .map((name) => { const children = e.children && (
{e.children.map(renderFormItem)}
); const Item = renderNode(e[name], { item: e, prop: e.prop, scope: form, slots, children, _data: { isDisabled } }); return (
{Item}
); })}
{isBoolean(e.collapse) && (
{ Action.collapseItem(e); }}> {e.collapse ? dict.label.seeMore : dict.label.hideContent}
)}
); } } ); let span = e.span || style.form.span; if (browser.isMini) { span = 24; } // 是否行内 const Item = props.inline ? ( FormItem ) : ( {FormItem} ); return isLoaded ? Item : null; } // 渲染表单 function renderContainer() { // 表单项列表 const children = config.items.map(renderFormItem); // 表单标签位置 const labelPosition = browser.isMini && !props.inline ? "top" : config.props.labelPosition || style.form.labelPosition; return (
{h( { submit(); e.preventDefault(); }} />, { ...config.props, labelPosition }, { default: () => { const items = [ slots.prepend && slots.prepend({ scope: form }), children, slots.append && slots.append({ scope: form }) ]; return (
{props.inline ? ( items ) : ( {items} )}
); } } )}
); } // 渲染表单按钮 function renderFooter() { const { hidden, buttons, saveButtonText, closeButtonText, justify } = config.op; if (hidden) { return null; } const Btns = buttons?.map((e: any) => { switch (e) { case "save": return ( { submit(); }}> {saveButtonText} ); case "close": return ( { close("close"); }}> {closeButtonText} ); default: return renderNode(e, { scope: form, slots, custom() { return ( { e.onClick({ scope: form }); }}> {e.label} ); } }); } }); return ( ); } expose({ refs, Form, visible, saving, form, config, loading, disabled, open, close, done, clear, reset, submit, bindForm, showLoading, hideLoading, setDisabled, Tabs, ...Action, ...ElFormApi }); return () => { if (props.inner) { return ( visible.value && (
{renderContainer()} {renderFooter()}
) ); } else { return h( , { title: config.title, height: config.height, width: config.width, ...config.dialog, beforeClose, onClosed, keepAlive: false }, { default() { return renderContainer(); }, footer() { return renderFooter(); } } ); } }; } });