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> |