@zhsz/cool-design-crud
Version:
547 lines (546 loc) • 16.5 kB
JavaScript
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
};