2022-06-09 20:16:26 +08:00

292 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
v-if="config"
:style="config.tip ? 'display: flex;align-items: baseline;' : ''"
:class="config.className"
class="m-form-container"
>
<m-fields-hidden
v-if="type === 'hidden'"
:model="model"
:config="config"
:name="config.name"
:disabled="disabled"
:prop="itemProp"
></m-fields-hidden>
<component
v-else-if="items && !config.text && type && display"
:key="key(config)"
:size="size"
:is="tagName"
:model="model"
:config="config"
:name="name"
:prop="itemProp"
:step-active="stepActive"
:expand-more="expand"
:label-width="itemLabelWidth"
@change="onChangeHandler"
></component>
<template v-else-if="type && display">
<el-form-item
:style="config.tip ? 'flex: 1' : ''"
:class="{ hidden: `${itemLabelWidth}` === '0' || !config.text }"
:prop="itemProp"
:label-width="itemLabelWidth"
:rules="rule"
>
<template #label><span v-html="type === 'checkbox' ? '' : config.text"></span></template>
<el-tooltip v-if="tooltip">
<component
:key="key(config)"
:size="size"
:is="tagName"
:model="model"
:config="config"
:name="name"
:disabled="disabled"
:prop="itemProp"
@change="onChangeHandler"
></component>
<template #content>
<div v-html="tooltip"></div>
</template>
</el-tooltip>
<component
v-else
:key="key(config)"
:size="size"
:is="tagName"
:model="model"
:config="config"
:name="name"
:disabled="disabled"
:prop="itemProp"
@change="onChangeHandler"
></component>
<div v-if="extra" v-html="extra" class="m-form-tip"></div>
</el-form-item>
<el-tooltip v-if="config.tip" placement="left">
<el-icon style="line-height: 40px; margin-left: 5px"><warning-filled /></el-icon>
<template #content>
<div v-html="config.tip"></div>
</template>
</el-tooltip>
</template>
<template v-else-if="items && display">
<template v-if="name || name === 0 ? model[name] : model">
<m-form-container
v-for="item in items"
:key="key(item)"
:model="name || name === 0 ? model[name] : model"
:config="item"
:size="size"
:step-active="stepActive"
:expand-more="expand"
:label-width="itemLabelWidth"
:prop="itemProp"
@change="onChangeHandler"
></m-form-container>
</template>
</template>
<div style="text-align: center" v-if="config.expand && type !== 'fieldset'">
<el-button text @click="expandHandler">{{ expand ? '收起配置' : '展开更多配置' }}</el-button>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType, ref, resolveComponent, watchEffect } from 'vue';
import { WarningFilled } from '@element-plus/icons';
import { ChildConfig, ContainerCommonConfig, FormState, FormValue } from '../schema';
import { display as displayFunction, filterFunction, getRules } from '../utils/form';
export default defineComponent({
name: 'm-form-container',
components: { WarningFilled },
props: {
labelWidth: String,
expandMore: Boolean,
model: {
type: [Object, Array] as PropType<FormValue>,
required: true,
},
config: {
type: Object as PropType<ChildConfig>,
required: true,
},
prop: {
type: String,
default: () => '',
},
stepActive: {
type: [String, Number],
},
size: {
type: String,
default: 'small',
},
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const expand = ref(false);
const name = computed(() => props.config.name || '');
const items = computed(() => (props.config as ContainerCommonConfig).items);
const itemProp = computed(() => {
let n: string | number = '';
const { names } = props.config as any;
if (names?.[0]) {
[n] = names;
} else if (name.value) {
n = name.value;
} else {
return props.prop;
}
return `${props.prop}${props.prop ? '.' : ''}${n}`;
});
const tagName = computed(() => {
const component = resolveComponent(`m-${items.value ? 'form' : 'fields'}-${type.value}`);
if (typeof component !== 'string') return component;
return 'm-fields-text';
});
const disabled = computed(() => filterFunction(mForm, props.config.disabled, props));
const tooltip = computed(() => filterFunction(mForm, props.config.tooltip, props));
const extra = computed(() => filterFunction(mForm, props.config.extra, props));
const rule = computed(() => getRules(mForm, props.config.rules, props));
const type = computed((): string => {
let { type } = props.config;
if (typeof type === 'function') {
type = type(mForm, {
model: props.model,
});
}
if (type === 'form') return '';
return type?.replace(/([A-Z])/g, '-$1').toLowerCase() || (items.value ? '' : 'text');
});
const display = computed((): boolean => {
if (props.config.display === 'expand') {
return expand.value;
}
return displayFunction(mForm, props.config.display, props);
});
const itemLabelWidth = computed(() => props.config.labelWidth || props.labelWidth);
watchEffect(() => {
expand.value = props.expandMore;
});
const expandHandler = () => (expand.value = !expand.value);
const key = (config: any) => config[mForm?.keyProps];
const filterHandler = (filter: any, value: FormValue | number | string) => {
if (typeof filter === 'function') {
return filter(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
});
}
if (filter === 'number') {
return +value;
}
return value;
};
const changeHandler = (onChange: any, value: FormValue | number | string) => {
if (typeof onChange === 'function') {
return onChange(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
});
}
};
const trimHandler = (trim: any, value: FormValue | number | string) => {
if (typeof value === 'string' && trim) {
return value.replace(/^\s*/, '').replace(/\s*$/, '');
}
};
const onChangeHandler = async function (v: FormValue, key?: string) {
const { filter, onChange, trim, name, dynamicKey } = props.config as any;
let value: FormValue | number | string = v;
try {
value = filterHandler(filter, v);
value = (await changeHandler(onChange, value)) ?? value;
value = trimHandler(trim, value) ?? value;
} catch (e) {
console.error(e);
}
// field内容下包含field-link时model===value, 这里避免循环引用
if ((name || name === 0) && props.model !== value && (v !== value || props.model[name] !== value)) {
// eslint-disable-next-line vue/no-mutating-props
props.model[name] = value;
}
// 动态表单类型根据value和key参数直接修改model
if (key !== undefined && dynamicKey) {
// eslint-disable-next-line vue/no-mutating-props
props.model[key] = value;
}
emit('change', props.model);
};
return {
expand,
name,
type,
disabled,
itemProp,
items,
display,
itemLabelWidth,
tagName,
rule,
tooltip,
extra,
key,
onChangeHandler,
expandHandler,
};
},
});
</script>