vue3+elementUI实现悬浮多行文本输入框效果
作者:菜鸡dede画界面
一、组件功能描述
点击常规输入框 可以调出悬浮大文本域输入框,支持多行文本输入、支持从excel复制粘贴输入并自动切分为符合参数的逗号拼接字符串。
组件图例
二、组件参数说明
1.props(属性)
参数名 | 参数类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
inputPrefix | String | 申请单号: | 外部输入框 提示文本 | |
inputPlaceholder | string | 请输入单号 | 外部输入框 占位文本Placeholder | |
popoverWidth | number | 300 | 如果需要结合 界面适应宽度的话,配合hook useGetElementWidthByClassName(className),参数class 是对应元素基坑的类名 | |
showTextLength | number | 15 | 基础文本框展示的字符个数 | |
useTextProcess | boolean | true | 开启文本处理 , true : 判断长度限制 切割 \r \n 字符串 ,false :不做处理 | |
overLength | number | 10 | 多行文本框最大上限,默认上限为 10个 | |
textInputLength | number | 10 | 多行文本框显示行数 上限为 10个 | |
inputInnerPlaceholder | string | 每行填写一条申请单号 | 悬浮输入框 占位文本 | |
modelValue | string | 是 | 自定义V-model 绑定变量 |
2.Emits(抛出事件)
事件名 | 事件参数 | 说明 |
---|---|---|
update:modelValue | textValue | 自定义V-model 抛出事件 |
3.Expose(对外暴露事件)
外部组件需要通过 $refs 来进行调用的方法
4.是否支持属性透传
不支持属性与事件透传至依赖组件
三、组件代码
<template> <el-popover :visible="state.visible" placement="bottom-start" :width="props.popoverWidth" > <div> <el-input ref="inputTextAreaRef" v-model="textValue" type="textarea" :placeholder="inputInnerPlaceholder" resize="none" :autosize="{ minRows: props.textInputLength + 1, maxRows: props.textInputLength + 1 }" clearable class="textInputArea" @blur="blurInputTextArea" @input="showErrorTip = false" @clear="showErrorTip = false" /> <div class="input-textarea-bottom-btn"> <div> <p v-if="showErrorTip" class="over-length-error-tip"> 超出最大行数限制{{ props.overLength }}行 </p> </div> </div> </div> <template #reference> <el-input ref="inputRef" v-model="omitText" class="base-input" :placeholder="props.inputPlaceholder" style="cursor: pointer" @focus="showLevitateWindow" > <template #prefix> {{ props.inputPrefix }} </template> </el-input> </template> </el-popover> </template> <script setup lang="ts"> import { computed } from 'vue' const props = withDefaults( defineProps<{ modelValue:string inputPrefix?: string inputPlaceholder?: string popoverWidth?: number showTextLength?:number useTextProcess?:boolean overLength?:number textInputLength?:number inputInnerPlaceholder?:string }>(), { inputPrefix: '申请单号:', // 外部输入框 提示文本 inputPlaceholder: '请输入单号', // 外部输入框 占位文本 inputInnerPlaceholder: '每行填写一条申请单号', // 悬浮输入框 占位文本 popoverWidth: 300, // 如果需要结合 界面适应宽度的话,配合hook useGetElementWidthByClassName(className),参数class 是对应元素基坑的类名 showTextLength: 15, // 基础文本框展示的字符个数 overLength: 10, // 多行文本框最大上限,默认上限为 10个 textInputLength: 10, // 多行文本框显示行数 上限为 10个 useTextProcess: true // 开启文本处理 , 判断长度限制 切割 \r \n 字符串 } ) const emits = defineEmits(['update:modelValue']) const inputTextAreaRef = ref<HTMLElement | null> (null) const inputRef = ref<HTMLElement | null> (null) const showErrorTip = ref<boolean> (false) const state = reactive({ visible: false }) const omitText = computed(() => { return textValue.value.length > props.showTextLength ? textValue.value.substring(0, props.showTextLength) + '……' : textValue.value }) const textValue = ref(props.modelValue) watchEffect(() => { textValue.value = props.modelValue }) // 聚焦的时候 展示悬浮输入框 function showLevitateWindow () { if (state.visible) { inputTextAreaRef.value?.focus() return } // 处理数据,如果包含 , 需要拆开 const localValue = textValue.value if (localValue.indexOf(',') > -1) { textValue.value = localValue.split(',').join('\n') emits('update:modelValue', textValue.value) } state.visible = true nextTick(() => { inputTextAreaRef.value?.focus() }) } // 悬浮输入失去焦点 传输数据给父组件 function blurInputTextArea () { if (props.useTextProcess) { const { overLength, val } = textProcessing(textValue.value) // textValue.value = val if (!overLength) { // 没有超长的 传递给父组件 console.log('emit的数据', val) emits('update:modelValue', val) state.visible = false } else { showErrorTip.value = true // 展示错误信息 } } else { emits('update:modelValue', textValue.value) state.visible = false } } // 文本处理方法,切割 \r \n 字符串 const textProcessing : (val: string) => { val:string, overLength:boolean } = (val) => { const splitText = val.split(/\r?\n/).filter(i => i !== '') const overLength = splitText.length > props.overLength // 最大长度 return { val: splitText.join(','), overLength } } </script> <style scoped lang="scss"> .input-textarea-bottom-btn{ margin-top: 5px; display: flex; justify-content: space-between; align-content: center; .over-length-error-tip{ color:#f56c6c; font-size: 12px; line-height: 24px; } } /* 隐藏浏览器默认滚动条 */ .textInputArea ::-webkit-scrollbar { width: 6px; /* 宽度 */ height: 6px; /* 高度 */ } /* 滚动条滑块 */ .textInputArea ::-webkit-scrollbar-thumb { background: #969696; /* 滑块颜色 */ border-radius: 3px; /* 滑块圆角 */ } /* 滚动条轨道 */ .textInputArea ::-webkit-scrollbar-track { background: #f0f0f0; /* 轨道颜色 */ border-radius: 3px; /* 轨道圆角 */ } /* 鼠标悬停在滚动条上时的滑块样式 */ .textInputArea ::-webkit-scrollbar-thumb:hover { background: #656565; } </style>
附带 Hook 函数
import { ref, onMounted, onBeforeUnmount } from 'vue' /** * @description 根据className 获取悬浮输入框的基础输入框宽度 * @param className - 基础元素类名 string,默认 levitateBaseInput * @param paddingWidth - 边距宽度 number,默认 12 * */ export default function useGetElementWidthByClassName (className = 'levitateBaseInput', paddingWidth = 12) { const elementWidth = ref(0) function getElementsClientWidth () { setTimeout(() => { const ele = document.getElementsByClassName(className) if (ele[0]) { elementWidth.value = ele[0].clientWidth - paddingWidth } else elementWidth.value = 0 }, 400) } onMounted(() => { window.addEventListener('resize', getElementsClientWidth) getElementsClientWidth() }) onBeforeUnmount(() => { window.removeEventListener('resize', getElementsClientWidth) }) return elementWidth }
四、组件使用例子
<HomeSearchItem ref="inputElementRef" prop="MultiAccounts" class="levitateBaseInput"> <levitate-multiple-input v-model="queryParams.MultiAccounts" :popover-width="levitateWidth" input-prefix="多账户查询:" input-placeholder="" input-inner-placeholder="每行输入一个账户,最多20行,Meta账户可不加act_前缀" :over-length="20" /> </HomeSearchItem> const queryParams = reactive({ MultiAccounts:'' }) const levitateWidth = useGetElementWidthByClassName()
五、其他注意事项
1、本组件主要是用于解决从Excel 复制粘贴多行文本,并需要单行截取的问题。
2、props中的popoverWidth是用于设置弹出层的宽度,在搭配外层formItem使用时候,可以使用配套hook函数useGetElementWidthByClassName(默认获取类名为levitateBaseInput的元素的宽度,可以通过参数修改)获取外层宽度使弹出层宽度匹配外层输入框,否则使用固定宽度300。
3、组件设计:在点击外层输入框的时候,当悬浮框没有展开的时候,则显示悬浮框并自动focus到大文本域输入框(如果v-model的数据包含 , 则需要进行拆分并加入\n 确保可以在文本域输入框正常分行展示),当悬浮框是展开的状态则默认focus文本域输入框防止失去焦点(再次点击外层小输入框的时候,防止丢失焦点)。当文本域失去焦点则进行文本处理,见第四点。
/* <el-popover :visible="state.visible"> …………………省略其他代码………………… </el-popover> */ // 聚焦的时候 展示悬浮输入框 function showLevitateWindow () { if (state.visible) { inputTextAreaRef.value?.focus() return } // 处理数据,如果包含 , 需要拆开, localValue 为v-model传入数据的本地副本 const localValue = textValue.value if (localValue.indexOf(',') > -1) { textValue.value = localValue.split(',').join('\n') emits('update:modelValue', textValue.value) } state.visible = true // 在悬浮输入框展示后立刻聚焦 nextTick(() => { inputTextAreaRef.value?.focus() }) }
4、有关文本处理的方法,props中的useTextProcess是文本处理配置,如果设置为true,即对输入的多行文本进行文本处理 切割 \r \n 字符串并返回切割后拼接 , 后的字符串;如果是false则不对文本处理。会在悬浮框失去焦点的时候触发blurInputTextArea 方法进而在useTextProcess为true的情况下调用textProcessing 方法进行处理。当前版本在false的情况下不处理超长情况。
在true的情况如果文本超出行数限制,则会将showErrorTip.value = true // 展示错误信息,
// 悬浮输入失去焦点 传输数据给父组件。 function blurInputTextArea () { if (props.useTextProcess) { const { overLength, val } = textProcessing(textValue.value) // textValue.value = val if (!overLength) { // 没有超长的 传递给父组件 console.log('emit的数据', val) emits('update:modelValue', val) state.visible = false } else { showErrorTip.value = true // 展示错误信息 } } else { emits('update:modelValue', textValue.value) state.visible = false } } // 文本处理方法,切割 \r \n 字符串 const textProcessing : (val: string) => { val:string, overLength:boolean } = (val) => { const splitText = val.split(/\r?\n/).filter(i => i !== '') const overLength = splitText.length > props.overLength // 最大长度 return { val: splitText.join(','), overLength } }
以上就是vue3+elementUI实现悬浮多行文本输入框效果的详细内容,更多关于vue3 elementUI悬浮文本输入框的资料请关注脚本之家其它相关文章!