import { defineComponent, h, inject, nextTick, provide, reactive, ref, watch } from "vue"; import cloneDeep from "clone-deep"; import { useAction } from "./helper"; import { useRefs, useForm } from "../../hooks/core"; import { deepMerge, isBoolean, isEmpty, isObject, isString } from "../../utils"; import Parse from "../../utils/parse"; import { renderNode } from "../../utils/vnode"; import { Browser, Form } from "../../types"; export default defineComponent({ name: "cl-form", props: { modelValue: { type: Object, default: () => { return {}; } } }, emits: ["update:modelValue"], setup(props, { emit }) { const { refs, setRefs }: any = useRefs(); // 设置表单值 const { setFormData } = useForm(props); // 表单是否可见 const visible = ref(false); // 表单提交保存状态 const saving = ref(false); // 选项卡 const tabActive = ref(null); // 表单数据 const form = setFormData(); // 表单配置 const conf = reactive
({ title: "自定义表单", width: "50%", props: { size: "small", labelWidth: "100px" }, on: {}, op: { hidden: false, saveButtonText: "保存", closeButtonText: "取消", buttons: ["close", "save"] }, dialog: { props: { fullscreen: false, "close-on-click-modal": false, "append-to-body": true }, hiddenControls: false, controls: ["fullscreen", "close"] }, items: [], _data: {} }); // 表单动作 const { loading, showLoading, hiddenLoading, collapseItem, getForm, setForm, setData, setOptions, toggleItem, hiddenItem, showItem, resetFields, clearValidate, validateField, validate } = useAction({ conf, form, refs }); // 更新绑定值 watch(form, (val: any) => { emit("update:modelValue", val); }); // 提供 provide("form", form); // 请求表单保存状态 function done() { saving.value = false; } // 关闭表单 function close() { visible.value = false; done(); } // 表单关闭前事件 function beforeClose() { if (conf.on?.close) { conf.on.close(close); } else { close(); } } // 清空表单验证 function clear() { for (const i in form) { delete form[i]; } clearValidate(); } // 表单提交 function submit() { // 验证表单 refs.value.form.validate(async (valid: boolean) => { if (valid) { saving.value = true; // 表单提交钩子 if (conf.on?.submit) { // 拷贝表单值 const d = cloneDeep(form); // 过滤隐藏的表单项 conf.items.forEach((e: any) => { if (e._hidden) { delete d[e.prop]; } }); conf.on.submit(d, { done, close }); } else { console.error("Submit is not found"); } } }); } // 打开表单 function open(options?: Form) { if (!options) { options = { items: [] }; } clear(); // 合并配置 for (const i in conf) { switch (i) { case "items": conf.items = options.items || []; break; case "title": case "width": conf[i] = options[i]; break; case "props": case "on": case "dialog": case "_data": conf[i] = deepMerge(conf[i], options[i] || {}); break; } } // 显示对话框 visible.value = true; // 预设表单值 if (options?.form) { for (const i in options.form) { form[i] = options.form[i]; } } // 设置表单数据 conf.items.map((e: any) => { if (e.prop) { form[e.prop] = form[e.prop] || cloneDeep(e.value); } }); // 打开回调 nextTick(() => { if (conf.on?.open) { conf.on.open(form, { close, submit, done }); } }); return { showLoading, hiddenLoading, collapseItem, getForm, setForm, setData, setOptions, toggleItem, hiddenItem, showItem, resetFields, clearValidate, validateField, validate }; } return { visible, saving, tabActive, form, refs, setRefs, conf, loading, open, beforeClose, close, done, clear, submit, showLoading, hiddenLoading, collapseItem, getForm, setForm, setData, setOptions, toggleItem, hiddenItem, showItem, resetFields, clearValidate, validateField, validate }; }, render(ctx: any) { const browser = inject("browser") as Browser; const { props, op, title, width, dialog, _data } = ctx.conf; // 渲染表单及表单项 const renderForm = () => { // 表单项列表 const children = ctx.conf.items.map((e: any) => { if (e.type == "tabs") { return ( { ctx.clearValidate(); }}> ); } // 隐藏处理 e._hidden = Parse("hidden", { value: e.hidden, scope: ctx.form, data: _data }); // 分组 e._group = isEmpty(ctx.tabActive) || isEmpty(e.group) ? true : e.group === ctx.tabActive; // Flex handler if (isEmpty(e.flex)) { e._flex = true; } return ( e._group && !e._hidden && ( {e.component && h( , { prop: e.prop, rules: e.rules, ...e.props }, { label: () => { let d: any = { text: "", tip: "", icon: "" }; if (isString(e.label)) { d.text = e.label; } else if (isObject(e.label)) { d = e.label; } return ( {d.text} {d.icon && } ); }, default: () => { return (
{/* Form item */}
{["prepend", "component", "append"].map( (name) => { return ( e[name] && (
{renderNode(e[name], { prop: e.prop, scope: ctx.form, slots: ctx.$slots })}
) ); } )}
{/* Collapse button */} {isBoolean(e.collapse) && (
{ ctx.collapseItem(e); }}> {e.collapse ? ( 查看更多 ) : ( 隐藏内容 )}
)}
); } } )}
) ); }); // el-form const ElForm = ( ); return h(ElForm, props, { default: () => { return ( {children} ); } }); }; // 渲染表单按钮 function renderFooter() { const { hidden, buttons, saveButtonText, closeButtonText } = op; const { size = "small" } = props; return hidden ? null : buttons.map((vnode: any) => { if (vnode == "save") { return ( { ctx.submit(); } }}> {saveButtonText} ); } else if (vnode == "close") { return ( { ctx.beforeClose(); } }}> {closeButtonText} ); } else { return renderNode(vnode, { scope: ctx.form, slots: ctx.$slots }); } }); } return h( , { title, width, ...dialog, props: { ...dialog.props, "before-close": ctx.beforeClose } }, { default() { return (
{renderForm()}
); } } ); } });