import { ElFormItem, ElTooltip } from 'element-plus' import { h, toRef, computed, ref, unref, onErrorCaptured } from 'vue' import { getInputComponent, getViewComponent, getH5InputComponent } from '../cip-form-input/util' import { isEmpty, isEmptyObject, isInputEmpty } from '@cip/utils/util' import { useWatchFieldDepend } from './hooks/use-field-depend' import { useFieldValue, useSteamUpdateValues } from './hooks/use-model-change' import { useRules } from './hooks/use-field-rules' import { isHideLabel, getLabelWidth, UpdateModelQueue } from './util' import './index.less' import { useFormInject, useElFormInject } from '../hooks/use-form' import { useCipConfig } from '@cip/components/hooks/use-cip-config' export default { name: 'CipFormItem', props: { config: Object, // 字段配置信息 fieldKey: String, // 字段名 model: { // 字段所属model type: Object, default: () => ({}) }, readonly: Boolean, // 是否只读-即查看模式 customSlots: Function, showTemplate: { // 是否展示模版值[注:表单设计中使用] type: Boolean, default: false }, tableDependOnValues: Object, inTable: { type: Boolean, default: false }, componentKey: String, grid: [Number, Boolean], tableData: Array, onSearch: Function // 供CipSearchForm使用 }, emits: ['update:model'], setup (props, context) { onErrorCaptured((e) => { // el-form-item 在onBeforeUnmount会访问el.value.firstElementChild 由于切换较快的原因会抱错 process.env.NODE_ENV === 'development' && console.log(e) return false }) // elForm组件实例 const cipConfig = useCipConfig() const elForm = useElFormInject() const cipForm = useFormInject() const equipment = computed(() => { return unref(cipForm.equipment) || 'pc' }) // 一般处于cip-table直接使用的情况下 const notInForm = computed(() => { return isEmptyObject(elForm) }) const updateModel = (value) => { // console.log('updateModel', value) // if (!notInForm.value) { // // 因为延迟执行的问题,可能导致数据无法及时有效验证手动调用 // elForm.validateField([formItemConfig.value.ruleKey || props.fieldKey]) // } context.emit('update:model', value) } const updateModelQueue = new UpdateModelQueue(() => props.model, updateModel) // FormItem及Input组件渲染模式 ['hidden','read','read-write'] const status = computed(() => { // 为设置则默认开始可读可写模式 const config = formItemConfig.value if (props.readonly) { if (config.readable === false) return 'hidden' return 'read' } if (isEmpty(config.readable) && isEmpty(config.writable)) { return 'read-write' } if (config.writable) { return 'read-write' } else if (config.readable) { return 'read' } else { return 'hidden' } }) // Input组件实际使用的配置 const formItemConfig = computed(() => { return runningConfig.value || props.config // handleFormConfig() }) const model = toRef(props, 'model') const fieldKey = toRef(props, 'fieldKey') // 仅正对modelValue生效 const changeEffect = computed(() => { return formItemConfig.value.changeEffect }) // 3种数据更新方式并行 // modelValue const [modelValue, updateModelValue] = useFieldValue(fieldKey, model, updateModelQueue, changeEffect) const otherKey = computed(() => formItemConfig.value?.otherKey) // otherValue const [otherValue, updateOtherValue] = useFieldValue(otherKey, model, updateModelQueue) const { values, streamUpdateModel, clearValues } = useSteamUpdateValues(fieldKey, otherKey, model, updateModel, changeEffect) // 监听依赖 触发响应事件 const { changeCount, dependOnValues, outDependOnValues, runningConfig } = useWatchFieldDepend(props, context, { updateModelValue, updateOtherValue, clearValues }) const readonly = toRef(props, 'readonly') // rules const { usingRules, rules } = useRules(formItemConfig, readonly, status, otherValue, dependOnValues, outDependOnValues) // Input组件是否展示标记(需要控制form-item内置的margin-bottom) const childStatus = ref(true) // FormItem label const formItemLabel = () => { // 隐藏label或者label为空时直接返回空字符串 if (formItemConfig.value.hideLabel === true || isEmpty(formItemConfig.value.label)) return '' // 表单为只读模式下展示样式控制 const labelId = formItemConfig.value.directory ? props.fieldKey : undefined const result = [h('span', { class: { 'is-readonly': props.readonly }, id: labelId }, [formItemConfig.value.label])] // 存在说明 if (formItemConfig.value.description) { const descriptionComp = ( {{ content: () => formItemConfig.value.description, default: () => }} ) result.push(descriptionComp) } // 仅在正在使用rules的input中且required为true时展示必填标记 if (usingRules.value && formItemConfig.value.required) { const requiredAsterisk = (*) result.unshift(requiredAsterisk) } // 有标签后缀的增加标签后缀 if (elForm.labelSuffix) { result.push(elForm.labelSuffix) } return h('div', { style: { color: '#333333', ...formItemConfig.value.labelStyle } }, result) // result } const inlineErrorMessage = computed(() => { return props.inTable || cipForm.equipment === 'mobile' }) // FormItem ErrorMessage const errorMessageNode = ({ error }) => { if (!inlineErrorMessage.value) return null return ( ) } const labelPosition = computed(() => { // 依赖elForm实例数据总是在变换、故修改为有父组件cip-form下发数据 if (!isEmpty(formItemConfig.value.labelPosition)) { return formItemConfig.value.labelPosition === 'top' } return unref(cipForm.labelPosition) === 'top' }) const itemDisabled = computed(() => { return formItemConfig.value.importantDisabled !== undefined ? formItemConfig.value.importantDisabled : formItemConfig.value.disabled }) const renderItemInput = () => { if (props.customSlots) { return props.customSlots({ fieldKey: props.fieldKey, modelValue: modelValue.value, updateModel: updateModelValue, // 即将废弃,此处名字与实际功能不符合 updateModelValue }) } const type = formItemConfig.value.type || 'default' const componentProps = { key: props.componentKey, id: props.fieldKey, fieldKey: props.fieldKey, modelValue: modelValue.value, otherValue: otherValue.value, values: values.value, // model: model.value, // 即将废弃 changeCount: changeCount.value, config: formItemConfig.value, usingRules: usingRules.value, rules: rules.value, dependOnValues: dependOnValues.value, outDependOnValues: outDependOnValues.value, disabled: itemDisabled.value, showTemplate: props.showTemplate, tableData: props.tableData, class: 'cip-form-item__input', onStatusChange: (status) => { // 子组件触发事件控制父组件是否显示 childStatus.value = status }, onSearch: props.onSearch } if (status.value === 'read-write') { const inputComponentProps = { ...componentProps, // 'onUpdate:modelValue': updateModelValue, // 'onUpdate:otherValue': updateOtherValue, 'onStreamUpdate:model': streamUpdateModel } if (unref(cipForm.equipment) === 'mobile') { return h(getH5InputComponent(type), inputComponentProps) } else { return h(getInputComponent(type), inputComponentProps) } } else { // 根据cip-config配置给view的modelValue添加默认值 if (isInputEmpty(componentProps.modelValue)) { if (props.inTable && cipConfig.table.defaultViewValue) { componentProps.modelValue = cipConfig.table.defaultViewValue } if (cipConfig.defaultViewValue) { componentProps.modelValue = cipConfig.defaultViewValue } } const viewRender = formItemConfig.value.formViewRender || formItemConfig.value.render if (viewRender) return viewRender(componentProps) return h(getViewComponent(type), componentProps) } } // FormItem const formItem = () => { return h(ElFormItem, { class: { 'pos-top': labelPosition.value, // 'pos-top--padding': labelPositionTopPadding, 暂时关闭position === top时内容的缩进 'hide-label': isHideLabel(formItemConfig.value), 'content--end': formItemConfig.value.contentEnd }, prop: formItemConfig.value.ruleKey || props.fieldKey, // 子表单内的输入框会生成一个ruleKey rules: rules.value, labelWidth: getLabelWidth(formItemConfig.value), inlineMessage: inlineErrorMessage.value }, { label: formItemLabel, error: errorMessageNode, default: renderItemInput }) } const cipFormStyle = computed(() => { if (props.grid) { return { gridColumn: `span ${formItemConfig.value.span || 1}`, ...formItemConfig.value.itemStyle } } else { return (elForm.inline && !props.inTable) ? formItemConfig.value.style : formItemConfig.value.itemStyle } }) return () => { if (status.value === 'hidden') return null if (props.inTable && status.value === 'read') return renderItemInput() // 父组件不存在form状态下的渲染方式 目前经为cip-table下直接渲染使用 if (notInForm.value) return renderItemInput() return (
{formItem()}
) } } }