import { h, ref, toRefs } from 'vue' import { ElForm } from 'element-plus' import { useFormProvide } from '../hooks/use-form' import CipFormItem from '../cip-form-item' import CipFormDirectory from './form-directory' import CipFormLayout from '../cip-form-layout' import { DRender } from '../helper/d-render' import { toUpperFirstCase, getFieldValue } from '@cip/utils/util' import CipMessage from '../cip-message' import './index.less' const dRender = new DRender() export default { name: 'CipForm', props: { model: Object, fieldList: Array, showOnly: Boolean, modelKey: { type: [String, Function] }, grid: { type: [Number, Boolean] }, // 是否开启grid布局 useDirectory: Boolean, labelPosition: String, scrollToError: { type: Boolean, default: true }, equipment: { type: String, default: 'pc', validate: (val) => ['pc', 'mobile'].includes(val) }, border: Boolean, // showOnly + border 将出现边框 enterHandler: Function, // 回车触发回调 options: Object }, emits: ['update:model', 'submit', 'cancel'], setup (props, context) { // 下发属性 const uploadQueue = ref({}) useFormProvide(props, uploadQueue) const directoryConfig = ref([]) const { model, fieldList } = toRefs(props) const cipFormRef = ref() // 修改model的值 const updateModel = (val) => { context.emit('update:model', val) } const generateComponentKey = (key) => { if (props.modelKey) { const appendKey = toUpperFirstCase(key) if (typeof props.modelKey === 'function') { return `${props.modelKey(props.model)}${appendKey}` } else { const value = getFieldValue(props.model, props.modelKey) return `${value || ''}${appendKey}` } } else { return key } } // 获取layout及item组件需要的props const getComponentProps = (key, config) => { const componentKey = generateComponentKey(key) const componentProps = { key: componentKey, componentKey: componentKey, model, fieldKey: key, config, readonly: props.showOnly, grid: props.grid, formLabelPosition: props.labelPosition, 'onUpdate:model': (val) => { if (componentKey === generateComponentKey(key)) { updateModel(val) } } } if (props.enterHandler) { componentProps.onKeyup = (e) => { const { keyCode } = e if (keyCode === 13) { props.enterHandler() } } } return componentProps } // 布局字段渲染方式 const getFormLayout = (componentProps) => { return h(CipFormLayout, { ...componentProps, onValidate: (cb) => { validate(cb) }, onSubmit: () => { context.emit('submit') }, onCancel: () => { context.emit('cancel') } }, { item: ({ children = [], isShow } = {}) => { return children.map((v) => getFormDefaultSlot(v, isShow)) } }) } // 输入字段渲染方式 const getFormItem = (componentProps) => { return h(CipFormItem, componentProps) } // 渲染单个字段 const getFormDefaultSlot = ({ key, config } = {}, isShow) => { // 若存在字段key值的插槽覆盖则配置整个ElFormItem config._isGrid = props.grid config._isShow = isShow if (context.slots[key]) { return context.slots[key]({ key, config }) } const componentProps = getComponentProps(key, config) // 若存在字段key值+Input的插槽覆盖则配置ElFormItem内的Input if (context.slots[`${key}Input`]) { return h(CipFormItem, { ...componentProps, customSlots: context.slots[`${key}Input`] }) } // 判断是否为布局类型的字段 if (dRender.isLayoutType(config.type)) { // layout类型字段 return getFormLayout(componentProps) } else { // input类型字段 // 如果需要表单目录导航则添加 if (config.directory) { directoryConfig.value[key] = { label: config.staticInfo || config.label, level: config.directory } } return getFormItem(componentProps) } } // 渲染表单 const getFormDefaultSlots = () => { if (props.useDirectory) { return fieldList.value.map((v) => getFormDefaultSlot(v)).concat( [h(CipFormDirectory, { directory: directoryConfig.value })] ) } else { return fieldList.value.map((v) => getFormDefaultSlot(v)) } } /** start父组件通过ref调用方法 **/ const validateUpload = () => { return new Promise((resolve, reject) => { const keys = Object.keys(uploadQueue.value) for (let i = 0; i < keys.length; i++) { const key = keys[i] if (uploadQueue.value[key]) { CipMessage.error('请等待文件上传', '提示') resolve(false) break } } resolve(true) }) } const validateField = (props, cb) => { return cipFormRef.value.validateField(props, cb) } const validate = async (cb = () => {}) => { const isUpload = await validateUpload() if (!isUpload) { // eslint-disable-next-line standard/no-callback-literal cb(false) throw new Error('请等待文件上传') } else { // const res = await cipFormRef.value.validate() // 此方式返回的res为 true or false return new Promise((resolve, reject) => { cipFormRef.value.validate(async (isValid, invalidFields) => { if (typeof cb === 'function') cb(isValid, invalidFields) isValid ? resolve(isValid) : reject(isValid) }) }) } } const clearValidate = () => { return cipFormRef.value?.clearValidate() } context.expose({ validateUpload, validateField, validate, clearValidate }) /** end父组件通过ref调用方法 **/ return () => h(ElForm, { ...context.attrs, ref: cipFormRef, hideRequiredAsterisk: true, model: model, // 待进行测试 使用model.value后数据是否正常 class: [ 'cip-form', `cip-form--${props.equipment}`, { 'cip-form--grid': props.grid, 'cip-form--border': props.border && props.showOnly, 'cip-form--readonly': props.showOnly } ], style: { gridTemplateColumns: `repeat(${typeof props.grid === 'number' ? props.grid : 3},1fr)` }, size: 'default', labelPosition: props.labelPosition, scrollToError: props.scrollToError, onSubmit: ev => { ev.preventDefault() } }, { default: () => [getFormDefaultSlots(), context.slots.default?.()] }) } }