UNPKG

@zhsz/cool-design-crud

Version:

547 lines (546 loc) 16.5 kB
import { defineComponent, reactive, ref, createVNode, unref, nextTick, mergeProps, isVNode } from "vue"; import { createForm, registerValidateFormats } from "@formily/core"; import { usePlugins, useAction } from "./helper.mjs"; import "../../utils/test.mjs"; import { useTools, useElApi } from "../../hooks/core.mjs"; import { parseFunction, deepMerge, parseFunctionToString } from "../../utils/index.mjs"; import { useSchema } from "../../hooks/schema.mjs"; import "../../hooks/table.mjs"; import formHook from "../../utils/form-hook.mjs"; import { BaseForm, registerTypes, SchemaFormats, ClCreateSchemaField } from "@zhsz/cool-design"; import { Button, Loading } from "tdesign-vue-next"; import { Row } from "@arco-design/web-vue"; import { calculateProperties } from "../../utils/parse.mjs"; import { processProperties, extractDefaultsRecursively } from "../../utils/transform.mjs"; import ClDialog from "../dialog/index.mjs"; import { Schema } from "@formily/vue"; import { isArray, isNumber, debounce, isFunction, omit, isObject, mapValues, omitBy } from "lodash-es"; import { isString } from "xe-utils"; import cloneDeep from "clone-deep"; function _isSlot(s) { return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s); } const ClForm = /* @__PURE__ */ defineComponent({ name: "cl-form", components: { BaseForm, TdButton: Button, ClDialog, Loading }, props: { inner: Boolean, inline: Boolean, /** * @zh 表单的布局方式,包括水平、垂直、多列 * @en The layout of the form, including horizontal, vertical, and multi-column * @values 'horizontal', 'vertical', 'inline' */ layout: { type: String, default: "horizontal" }, /** * @zh 标签元素布局选项。参数同 `<col>` 组件一致 * @en Label element layout options. The parameters are the same as the `<col>` component */ labelColProps: { type: Object }, /** * @zh 表单控件布局选项。参数同 `<col>` 组件一致 * @en Form control layout options. The parameters are the same as the `<col>` component */ wrapperColProps: { type: Object }, labelColStyle: Object, wrapperColStyle: Object, /** * @zh 标签的对齐方向 * @en Alignment direction of the label * @values 'left', 'right' */ labelAlign: { type: String, default: "right" }, /** * @zh 是否开启自动标签宽度,仅在 `layout="horizontal"` 下生效。 * @en Whether to enable automatic label width, it only takes effect under `layout="horizontal"`. * @version 2.13.0 */ autoLabelWidth: { type: Boolean, default: false }, customClass: String, /** 副作用逻辑,用于实现各种联动逻辑 */ formEffects: Function, formRow: { type: Object, default: () => { return { style: "width:100%;margin:0;", gutter: [16, 16], justify: "start", align: "start", div: false, wrap: true }; } } }, setup(props, { expose, slots }) { const { dict, style, pageLayoutId } = useTools(); const config = reactive({ title: "-", width: "50%", height: "100%", showBox: false, scope: {}, registerComponents: {}, effects: void 0, props: { formRow: props.formRow }, on: {}, op: { hidden: false, saveButtonText: dict.label.save, closeButtonText: dict.label.close, buttons: ["close", "save"] }, schema: {}, dialog: { closeOnOverlayClick: false, closeOnEscKeydown: false, attach: "body" }, form: {}, _data: {} }); const Form = createForm({ effects: props.formEffects }); const SchemaJson = ref({}); let SchemaFieldState; Schema.enablePolyfills(["1.0"]); Schema.registerTypeDefaultComponents(registerTypes); registerValidateFormats({ phone: SchemaFormats.REGEXP_PHONE }); const form = reactive({}); let defForm = null; let closeAction = "close"; const saving = ref(false); const visible = ref(false); const loading = ref(false); const Action = useAction({ config, form, Form }); const FormApi = useElApi(["validate", "getState", "setState", "notify", "query", "clearErrors", "setEffects", "removeEffects", "addEffects"], Form); const plugin = usePlugins({ visible }); function showLoading() { loading.value = true; } function hideLoading() { loading.value = false; } function done() { saving.value = false; } function close(action) { if (action) { closeAction = action; } beforeClose(() => { visible.value = false; done(); }); onClosed(); } function beforeClose(done2) { var _a; if ((_a = config.on) == null ? void 0 : _a.close) { config.on.close(closeAction, done2); } else { done2(); } } function onClosed() { Form.setValues({}, "overwrite"); Form.clearErrors(); } function clear() { for (const i in form) { if (isArray(form[i])) { form[i] = []; } else if (isString(form[i])) { form[i] = ""; } else if (isNumber(form[i])) { form[i] = 0; } else { form[i] = void 0; } } Form.setValues(form, "overwrite"); return form; } function reset() { if (defForm) { for (const i in defForm) { form[i] = cloneDeep(defForm[i]); } } Form.setValues(form, "overwrite"); return form; } function submit(callback) { Form.submit(async (value) => { var _a; saving.value = true; let d = cloneDeep(value); for (const item in config.schema) { const element = config.schema[item]; const value2 = d[item]; if (element.type !== "void") { formHook.submit({ hook: element.hook, prop: item, value: value2, form: d }); } else if (element.properties) { processProperties(element.properties, formHook, d, "submit"); } } const submit2 = callback || ((_a = config.on) == null ? void 0 : _a.submit); function findFieldsWithFilter(schema) { const fieldsWithFilter = []; function traverse(node) { if (node && typeof node === "object" && !Array.isArray(node)) { for (const key in node) { const field = node[key]; if ((field == null ? void 0 : field._filter) === true) { fieldsWithFilter.push(key); } traverse(field); } } } traverse(schema); return fieldsWithFilter; } const filterFields = findFieldsWithFilter(config.schema || {}); function deepOmit(obj, keysToOmit) { function omitKeysRecursively(value2) { if (isArray(value2)) { return value2.map(omitKeysRecursively); } else if (isObject(value2)) { return mapValues(omitBy(value2, (v, k) => keysToOmit.includes(k)), omitKeysRecursively); } return value2; } return omitKeysRecursively(obj); } d = deepOmit(d, filterFields); if (submit2) { submit2(await plugin.submit(d), { close() { close("save"); }, done }); } else { done(); } }); } function open(options, plugins) { if (!options) { return console.error("Options is not null"); } if (options.isReset !== false) { clear(); } closeAction = "close"; function deep(schema) { const transformSchema = cloneDeep(schema); for (const item in transformSchema) { const element = transformSchema[item]; if (isObject(element)) { calculateProperties(element, form); } } return transformSchema; } for (const i in config) { switch (i) { case "schema": config.schema = deep(options.schema || {}); break; case "on": case "op": case "props": case "dialog": case "_data": deepMerge(config[i], options[i] || {}); break; case "title": case "width": default: config[i] = options[i]; break; } } const defaultSchema = extractDefaultsRecursively(options.schema); if (options == null ? void 0 : options.schema) { for (const i in defaultSchema) { form[i] = defaultSchema[i]; } } if (options == null ? void 0 : options.form) { for (const i in options == null ? void 0 : options.form) { form[i] = options.form[i]; } } for (const item in config.schema) { const element = config.schema[item]; if (element.type !== "void") { formHook.bind({ hook: element.hook, schema: element, prop: item, value: form[item], form }); } else if (element.properties) { processProperties(element.properties, formHook, form, "bind"); } } if (!defForm) { defForm = cloneDeep(form); } plugin.create(plugins); SchemaFieldState = ClCreateSchemaField(config == null ? void 0 : config.registerComponents, config == null ? void 0 : config.scope); SchemaJson.value = unref(useSchema(config, Form)); if (config.effects) { Form.addEffects(Form.id, config.effects); } const log = debounce(() => { if (config.effects) { console.log("form ------> effects:", config.effects); } console.log("form ------> schema:", unref(SchemaJson)); (config == null ? void 0 : config.scope) && console.log("form ------> scope", unref(config == null ? void 0 : config.scope)); }, 1e3); log(); bindForm(form); visible.value = true; nextTick(() => { setTimeout(() => { var _a; if ((_a = config.on) == null ? void 0 : _a.open) { config.on.open(form); } }, 10); }); } function bindForm(data) { for (const item in config.schema) { const element = config.schema[item]; if (element.type !== "void") { formHook.bind({ hook: element.hook, prop: item, value: data[item], form: data }); } else if (element.properties) { processProperties(element.properties, formHook, data, "bind"); } } Object.assign(form, data); Form.setValues(form, "overwrite"); } function renderFormItem(slot) { return createVNode(SchemaFieldState.SchemaField, { "schema": unref(SchemaJson) }, { default: () => [slot == null ? void 0 : slot()] }); } function renderForm() { if (!visible.value) return ""; const { readOnly, disabled } = config._data; Form.setState((state) => { state.editable = !readOnly; state.disabled = disabled; }); const FormItem = renderFormItem(slots == null ? void 0 : slots.default); return createVNode(BaseForm, mergeProps({ "form": Form }, config.props, { "layout": props.layout, "labelColProps": props.labelColProps, "wrapperColProps": props.wrapperColProps, "labelColStyle": props.labelColStyle, "wrapperColStyle": props.wrapperColStyle, "labelAlign": config.props.labelAlign || props.labelAlign, "autoLabelWidth": props.autoLabelWidth }), { default: () => { return createVNode("div", { "class": "cl-form__container" }, [slots.prepend && slots.prepend({ scope: Form }), createVNode(Row, config.props.formRow, _isSlot(FormItem) ? FormItem : { default: () => [FormItem] }), slots.append && slots.append({ scope: Form })]); } }); } function renderFooter() { const { hidden, buttons, saveButtonText, closeButtonText, justify } = ( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion config.op ); const { style: style2 } = useTools(); let buttonList = []; if (isFunction(buttons)) { buttonList = buttons(Form); } else if (isArray(buttons)) { buttonList = buttons; } const Btns = buttonList == null ? void 0 : buttonList.map((e) => { let _slot, _slot2; switch (e) { case "save": return createVNode(Button, { "theme": "primary", "size": style2.size, "disabled": loading.value, "loading": saving.value, "onClick": () => { submit(); } }, _isSlot(_slot = parseFunctionToString(saveButtonText, form)) ? _slot : { default: () => [_slot] }); case "close": return createVNode(Button, { "variant": "outline", "size": style2.size, "onClick": () => { close("close"); } }, _isSlot(_slot2 = parseFunctionToString(closeButtonText, form)) ? _slot2 : { default: () => [_slot2] }); default: return !e.hidden && createVNode(Button, mergeProps(omit(e, ["type", "onClick", "hidden"]), { "type": e.type, "size": style2.size, "onClick": () => { const closeFun = (action) => { close(action); }; e.onClick({ scope: Form, close: closeFun }); } }), { default: () => [e.label] }); } }); return hidden || createVNode("div", { "class": "cl-form__footer", "style": { justifyContent: justify || "flex-end" } }, [Btns]); } expose({ Form, SchemaJson, visible, saving, form, config, loading, open, done, close, clear, reset, submit, bindForm, showLoading, hideLoading, ...Action, ...FormApi }); const formNode = () => createVNode("div", { "class": ["cl-form", props.customClass] }, [renderForm()]); return () => { var _a, _b, _c, _d, _e, _f; if (props.inner) { return formNode(); } else { return createVNode(ClDialog, { "modelValue": visible.value, "onUpdate:modelValue": ($event) => visible.value = $event, "title": config.title, "width": config.width, "height": config.height, "pageLayoutId": pageLayoutId, "showBox": parseFunction(config == null ? void 0 : config.showBox, false), "beforeClose": beforeClose, "onClosed": onClosed, "keepAlive": false, "dialogProps": config.dialog, "watermarkText": (_a = config.dialog) == null ? void 0 : _a.watermarkText, "watermarkProps": (_b = config.dialog) == null ? void 0 : _b.watermarkProps, "watermarkDic": ((_c = config.dialog) == null ? void 0 : _c.watermarkDic) || (dict == null ? void 0 : dict.watermark) || ((_d = config.dialog) == null ? void 0 : _d.watermarkDic), "pageStyle": (_e = config.props) == null ? void 0 : _e.pageStyle, "pageClass": (_f = config.props) == null ? void 0 : _f.pageClass }, { default: formNode, footer: renderFooter }); } }; } }); export { ClForm as default };