273 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			273 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
| <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", // select,radio,checkbox 的数据源
 | ||
|   "valueKey", // select,radio,checkbox 的 value字段
 | ||
|   "labelKey", // select,radio,checkbox 的 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>
 |