zy-vue-library/components/form_builder/form_items_renderer.vue

273 lines
10 KiB
Vue
Raw Normal View History

2025-10-22 11:19:51 +08:00
<template>
<template v-for="(option, index) in fnVerifyType(options)" :key="option.key || index">
<template v-if="!option.hidden">
<el-col
v-if="!option.root"
v-show="!collapse || index < 3"
:span="option.type === formItemTypeEnum.DIVIDER ? 24 : option.span || span || 12"
>
<el-form-item
v-if="option.type !== formItemTypeEnum.DIVIDER"
:key="option.formItemKey"
:label-width="option.labelWidth"
:prop="option.key"
:rules="fnGetRules(option)"
>
<template #label>
<div style="display: flex; align-items: center">
{{ option.label }}
<el-tooltip v-if="option.tip" :content="option.tip">
<el-icon :size="12" class="ml-2"><info-filled /></el-icon>
</el-tooltip>
</div>
</template>
<slot :name="option.key">
<el-input
v-if="option.type === formItemTypeEnum.INPUT || !option.type"
v-model="modelValue[option.key]"
:placeholder="'请输入' + option.label"
v-bind="fnGetProps(option)"
/>
<el-input
v-else-if="option.type === formItemTypeEnum.TEXTAREA"
v-model="modelValue[option.key]"
:placeholder="'请输入' + option.label"
type="textarea"
:autosize="{ minRows: 3 }"
v-bind="fnGetProps(option)"
/>
<el-input-number
v-else-if="
option.type === formItemTypeEnum.INPUT_NUMBER || option.type === formItemTypeEnum.NUMBER
"
v-model="modelValue[option.key]"
:placeholder="'请输入' + option.label"
v-bind="fnGetProps(option)"
/>
<el-select
v-else-if="option.type === formItemTypeEnum.SELECT"
v-model="modelValue[option.key]"
:placeholder="'请选择' + option.label"
v-bind="fnGetProps(option)"
>
<el-option
v-for="item in unref(option.options)"
:key="
item[option.valueKey] || item['dictionariesId'] || item['id']
"
:label="item[option.labelKey] || item['name']"
:value="
item[option.valueKey] || item['dictionariesId'] || item['id']
"
/>
</el-select>
<el-checkbox-group
v-else-if="option.type === formItemTypeEnum.CHECKBOX"
v-model="modelValue[option.key]"
:placeholder="'请选择' + option.label"
v-bind="fnGetProps(option)"
>
<el-checkbox
v-for="item in unref(option.options)"
:key="
item[option.valueKey] || item['dictionariesId'] || item['id']
"
:value="
item[option.valueKey] || item['dictionariesId'] || item['id']
"
>
{{ item[option.labelKey] || item["name"] }}
</el-checkbox>
</el-checkbox-group>
<el-radio-group
v-else-if="option.type === formItemTypeEnum.RADIO"
v-model="modelValue[option.key]"
:placeholder="'请选择' + option.label"
v-bind="fnGetProps(option)"
>
<el-radio
v-for="item in unref(option.options)"
:key="
item[option.valueKey] || item['dictionariesId'] || item['id']
"
:value="
item[option.valueKey] || item['dictionariesId'] || item['id']
"
>
{{ item[option.labelKey] || item["name"] }}
</el-radio>
</el-radio-group>
<el-date-picker
v-else-if="option.type === formItemTypeEnum.DATE"
v-model="modelValue[option.key]"
value-format="YYYY-MM-DD"
:placeholder="'请选择' + option.label"
v-bind="fnGetProps(option)"
/>
<el-date-picker
v-else-if="option.type === formItemTypeEnum.DATE_MONTH"
v-model="modelValue[option.key]"
type="month"
value-format="YYYY-MM"
:placeholder="'请选择' + option.label"
v-bind="fnGetProps(option)"
/>
<el-date-picker
v-else-if="option.type === formItemTypeEnum.DATE_YEAR"
v-model="modelValue[option.key]"
type="year"
value-format="YYYY"
:placeholder="'请选择' + option.label"
v-bind="fnGetProps(option)"
/>
<el-date-picker
v-else-if="option.type === formItemTypeEnum.DATE_RANGE"
v-model="modelValue[option.key]"
type="daterange"
value-format="YYYY-MM-DD"
:start-placeholder="'请选择开始' + option.label"
:end-placeholder="'请选择结束' + option.label"
v-bind="fnGetProps(option)"
/>
<el-date-picker
v-else-if="option.type === formItemTypeEnum.DATETIME"
v-model="modelValue[option.key]"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="'请选择' + option.label"
v-bind="fnGetProps(option)"
/>
<el-date-picker
v-else-if="option.type === formItemTypeEnum.DATETIME_RANGE"
v-model="modelValue[option.key]"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="'请选择开始' + option.label"
:end-placeholder="'请选择结束' + option.label"
:default-time="[
new Date(2000, 1, 1, 0, 0, 0),
new Date(2000, 2, 1, 23, 59, 59),
]"
v-bind="fnGetProps(option)"
/>
<component :is="option.type" v-else v-model="modelValue[option.key]" v-bind="fnGetProps(option)" />
</slot>
</el-form-item>
<el-divider v-if="option.type === formItemTypeEnum.DIVIDER" content-position="left">
{{ option.label }}
</el-divider>
</el-col>
<template v-if="option.root">
<slot :name="option.key">
<component :is="option.type" v-model="modelValue[option.key]" v-bind="fnGetProps(option)" />
</slot>
</template>
</template>
</template>
</template>
<script setup>
import { omit } from "lodash-es";
import { unref } from "vue";
import { InfoFilled } from "@element-plus/icons-vue";
import {
ElCol,
ElFormItem,
ElTooltip,
ElIcon,
ElInput,
ElInputNumber,
ElSelect,
ElOption,
ElCheckboxGroup,
ElCheckbox,
ElRadioGroup,
ElRadio,
ElDatePicker,
ElDivider,
} from "element-plus";
import "element-plus/es/components/col/style/css";
import "element-plus/es/components/form-item/style/css";
import "element-plus/es/components/tooltip/style/css";
import "element-plus/es/components/icon/style/css";
import "element-plus/es/components/input/style/css";
import "element-plus/es/components/input-number/style/css";
import "element-plus/es/components/select/style/css";
import "element-plus/es/components/option/style/css";
import "element-plus/es/components/checkbox-group/style/css";
import "element-plus/es/components/checkbox/style/css";
import "element-plus/es/components/radio-group/style/css";
import "element-plus/es/components/radio/style/css";
import "element-plus/es/components/date-picker/style/css";
import "element-plus/es/components/divider/style/css";
import formItemTypeEnum from "../../enum/formItemType/index.js";
const props = defineProps({
options: { type: Array, required: true },
span: { type: Number, default: 0 },
collapse: { type: Boolean, default: false },
autoGenerateRequired: { type: Boolean, default: true },
});
const modelValue = defineModel({ type: Object, required: true });
const rootProps = [
"label", // 表单项的标签
"key", // 唯一值对应form里的key
"type", // 要渲染的组件默认input
"span", // 栅格占据的列数默认12
"hidden", // 是否隐藏
"rules", // 表单验证规则
"options", // selectradiocheckbox 的数据源
"valueKey", // selectradiocheckbox 的 value字段
"labelKey", // selectradiocheckbox 的 label字段
"attrs", // 传给组件的属性
"root", // 是否和el-col同级
"labelWidth", // label的宽度
"tip", // 跟在label后面的提示语
"formItemKey", // el-form-item的key
"required", // el-form-item是否必填默认true
// ... // 任意参数全部当成组件属性不能和attrs同时存在优先使用attrs
];
const fnGetProps = (option) => {
if (option.attrs) return option.attrs;
return omit(option, rootProps);
};
const fnGetRules = (option) => {
if (props.autoGenerateRequired === false) return {};
if (option.type === formItemTypeEnum.DIVIDER) return {};
if (option.required !== false) {
const blur = option.type
? [
formItemTypeEnum.INPUT,
formItemTypeEnum.TEXTAREA,
formItemTypeEnum.INPUT_NUMBER,
formItemTypeEnum.NUMBER,
].includes(option.type)
: true;
const rules = [
{
required: true,
message: `${blur ? "请输入" : "请选择"}${option.label}`,
trigger: blur ? "blur" : "change",
},
];
if (option.rules) {
if (Array.isArray(option.rules)) rules.push(...option.rules);
else rules.push(option.rules);
}
return rules;
}
return {};
};
const fnVerifyType = (options) => {
for (let i = 0; i < options.length; i++) {
const option = options[i];
if (option.type && typeof option.type === 'string' && !formItemTypeEnum[option.type])
throw new Error(`${option.type} 类型枚举不存在,请参照并使用 formItemTypeEnum 类型枚举`);
}
return options;
};
</script>
<style scoped lang="scss"></style>