<template> <el-col class="cus-form-column" :class="{ span1: filterSpan == 1, span2: filterSpan == 2, span3: filterSpan == 3, span4: filterSpan == 4, span5: filterSpan == 5, }" :span="span" :offset="offset" ref="ref_cusFormColumn"> <el-form-item :label="label" :label-width="labelWidth" :class="{ required: required !== false }" :error="errorMessage" :required="required"> <slot name="cus" :handleValidate="handleValidate"> <template v-if="link === 'input'"> <InputCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" @emitEnter="handleEnter" > <template v-if="$slots.prefix" #prefix> <slot name="prefix"/> </template> <template v-if="$slots.suffix" #suffix> <slot name="suffix"/> </template> <template v-if="$slots.prepend" #prepend> <slot name="prepend"/> </template> <template v-if="$slots.append" #append> <slot name="append"/> </template> </InputCom> </template> <template v-else-if="link === 'select'"> <SelectCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > <slot/> </SelectCom> </template> <template v-else-if="link === 'date'"> <DateCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > <template #default="{cell}"> <slot v-bind="cell"/> </template> </DateCom> </template> <template v-else-if="link === 'datetime'"> <DateTimeCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > <template #default="{cell}"> <slot v-bind="cell"/> </template> </DateTimeCom> </template> <template v-else-if="link === 'time'"> <TimeCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > </TimeCom> </template> <template v-else-if="link === 'cascader'"> <CascaderCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > <template v-if="$slots.default" #default="{node, data}"> <slot v-bind="{node, data}"/> </template> </CascaderCom> </template> <template v-else-if="link === 'switch'"> <SwitchCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > </SwitchCom> </template> <template v-else-if="link === 'radio'"> <RadioCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > </RadioCom> </template> <template v-else-if="link === 'checkbox'"> <CheckboxCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > </CheckboxCom> </template> <template v-else-if="link === 'portOfRegistry'"> <PortOfRegistryCom v-bind="$attrs" :label="labelCpt" :paramOne="paramOne" :paramTwo="paramTwo" @emitParam="(pOne, pTwo) => { $emit('update:paramOne', pOne), $emit('update:paramTwo', pTwo), handleValidate(pOne, pTwo) }" /> </template> <template v-else-if="link === 'residentMooringPoint'"> <ResidentMooringPointCom v-bind="$attrs" :label="labelCpt" :paramOne="paramOne" :paramTwo="paramTwo" @emitParam="(pOne, pTwo) => { $emit('update:paramOne', pOne), $emit('update:paramTwo', pTwo), handleValidate(pOne, pTwo) }" /> </template> <template v-else-if="link === 'dept'"> <DeptCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" @emitDefault="val => $emit('getDefault', val)" /> </template> <template v-else-if="link === 'upload'"> <UploadCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" /> </template> <template v-else-if="link === 'number'"> <NumberCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" @emitEnter="handleEnter" > <template v-if="$slots.prefix" #prefix> <slot name="prefix"/> </template> <template v-if="$slots.suffix" #suffix> <slot name="suffix"/> </template> <template v-if="$slots.prepend" #prepend> <slot name="prepend"/> </template> <template v-if="$slots.append" #append> <slot name="append"/> </template> </NumberCom> </template> <template v-else-if="link === 'input-number'"> <InputNumberCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" @emitEnter="handleEnter" > </InputNumberCom> </template> <template v-else-if="link === 'tree-select'"> <TreeSelectCom v-bind="$attrs" :label="labelCpt" :param="param" @emitParam="val => { $emit('update:param', val), handleValidate(val) }" > </TreeSelectCom> </template> </slot> <div v-if="unit" class="unit">{{unit}}</div> </el-form-item> </el-col> </template> <script lang="ts"> import { defineComponent, computed, onMounted, ref, reactive, watch, getCurrentInstance, ComponentInternalInstance, toRefs, nextTick, inject, onUnmounted, onBeforeUnmount } from 'vue' import {useStore} from 'vuex' import {useRouter, useRoute} from 'vue-router' import InputCom from './cus-form-link/input.vue' import InputNumberCom from './cus-form-link/input-number.vue' import SelectCom from './cus-form-link/select.vue' import DateCom from './cus-form-link/date.vue' import DateTimeCom from './cus-form-link/datetime.vue' import TimeCom from './cus-form-link/time.vue' import CascaderCom from './cus-form-link/cascader.vue' import SwitchCom from './cus-form-link/switch.vue' import RadioCom from './cus-form-link/radio.vue' import CheckboxCom from './cus-form-link/checkbox.vue' import PortOfRegistryCom from './cus-form-link/portOfRegistry.vue' import ResidentMooringPointCom from './cus-form-link/residentMooringPoint.vue' import DeptCom from './cus-form-link/dept.vue' import UploadCom from './cus-form-link/upload.vue' import NumberCom from './cus-form-link/number.vue' import TreeSelectCom from './cus-form-link/tree-select.vue' import { v4 } from "uuid"; export default defineComponent({ name: 'CusFormColumn', components: { InputCom, InputNumberCom, SelectCom, DateCom, DateTimeCom, TimeCom, CascaderCom, SwitchCom, RadioCom, CheckboxCom, PortOfRegistryCom, ResidentMooringPointCom, DeptCom, UploadCom, NumberCom, TreeSelectCom }, props: { span: {type: Number, default: 6}, filterSpan: { default: null }, offset: {type: Number, default: 0}, param: {}, paramOne: {}, paramTwo: {}, label: {type: String, default: ''}, required: {default: false}, labelWidth: {type: String, default: ''}, link: {type: String, default: 'input', validator(val: string) { return ['cascader', 'checkbox', 'date', 'datetime', 'input', 'radio', 'select', 'switch', 'portOfRegistry', 'residentMooringPoint', 'dept', 'time', 'upload', 'number', 'input-number', 'tree-select'].includes(val) }}, rules: {type: Array, default: () => []}, maxLength: {type: Number, default: null}, minLength: {type: Number, default: null}, defaultErrorMsg: {default: null}, unit: {default: '', type: String} }, setup(props) { const store = useStore(); const router = useRouter(); const route = useRoute(); const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties const state = reactive({ errorMessage: null, uuid: '' }) const ref_cusFormColumn: any = ref(null) const isValue = (val: any) => { if (val === null || val === undefined || (typeof val === 'string' && val.trim() === '') || (typeof val === 'object' && val?.length === 0)) { return false } return true } const labelCpt = computed(() => { return props.label.replace(/[::]([^::]*)$/, "") }) const rulesCpt = computed(() => { const r = [...props.rules] if (isValue(props.minLength)) { r.unshift({ handle: (val: any) => !isValue(val) || (isValue(val) && String(val).length >= props.minLength), message: `内容过短,字数需大于等于${props.minLength}` }) } if (isValue(props.maxLength)) { r.unshift({ handle: (val: any) => !isValue(val) || (isValue(val) && String(val).length <= props.maxLength), message: `内容过长,字数需小于等于${props.maxLength}` }) } const doStr = ['input', 'number', 'input-number'].includes(props.link) ? '输入' : '选择' if (props.required !== false && !props.rules.some((v: any) => v.type === 'default')) { if (['portOfRegistry', 'residentMooringPoint'].includes(props.link)) { r.unshift({ handle: (pOne: any, pTwo: any) => isValue(pOne) && isValue(pTwo), message: props.defaultErrorMsg ?? `请${doStr}${labelCpt.value}` }) } else { r.unshift({ handle: (val: any) => isValue(val), message: props.defaultErrorMsg ?? `请${doStr}${labelCpt.value}` }) } } return r }) const handleValidate = (val: any = undefined, val2: any = undefined) => { state.errorMessage = null for (let i = 0; i < rulesCpt.value.length; i++) { const item: any = rulesCpt.value[i] if (['portOfRegistry', 'residentMooringPoint'].includes(props.link)) { if (!item.handle(val === undefined ? props.paramOne : val, val2 === undefined ? props.paramTwo : val2)) { state.errorMessage = item.message break } } else { if (!item.handle(val === undefined ? props.param : val)) { state.errorMessage = item.message break } } } return state.errorMessage } const handleEnterFunc = inject('handle-enter', () => {}) const handleEnter = () => { handleEnterFunc?.() } const reset = () => { state.errorMessage = null } watch(() => state.errorMessage, (n) => { const p = ref_cusFormColumn.value?.$el?.getElementsByClassName('el-form-item')?.[0] if (n) { setTimeout(() => { const e = p?.getElementsByClassName('el-form-item__content')?.[0]?.getElementsByClassName('el-form-item__error')?.[0] if (e?.clientHeight) { p.style.marginBottom = `${e.clientHeight + 4}px` } }, 200) } else { p.style.marginBottom = '18px' } }) onMounted(() => { state.uuid = v4() const formChildrenMap: any = inject('cus-form-children-map', new Map()) let flag = true const deep = (dom) => { if (dom.className === 'hidden-columns') { flag = false } if (dom.parentElement) { deep(dom.parentElement) } } deep(ref_cusFormColumn.value.$parent.$el) if (flag) { formChildrenMap.set(state.uuid, ref_cusFormColumn.value.$parent) } }) onBeforeUnmount(() => { const formChildrenMap: any = inject('cus-form-children-map', new Map()) formChildrenMap.delete(state.uuid) }) return { ...toRefs(state), handleValidate, handleEnter, ref_cusFormColumn, reset, labelCpt } }, }) </script> <style scoped lang="scss"> .cus-form-column { &.span1 { width: 20%; max-width: 20%; flex: unset; } &.span2 { width: 40%; max-width: 40%; flex: unset; } &.span3 { width: 60%; max-width: 60%; flex: unset; } &.span4 { width: 80%; max-width: 80%; flex: unset; } &.span5 { width: 100%; max-width: 100%; flex: unset; } :deep(.el-form-item) { .el-form-item__label { word-break: keep-all; line-height: 1; text-align: right; display: flex; align-items: center; padding-left: 10px; font-size: 14px; font-family: Microsoft YaHei; font-weight: 400; color: #434343; height: 36px; } .el-form-item__content { height: 36px; flex-wrap: unset; >div:first-child { flex: 1; height: 100%; } .unit { margin-left: 6px; font-size: 14px; font-family: PingFang SC-Regular, PingFang SC; font-weight: 400; color: #606266; } .el-input { height: 100%; .el-input__wrapper { border-radius: 2px; } } .el-select { height: 100%; .select-trigger { height: 100%; } } } } } </style>