基于vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框效果
需求背景
需要一個(gè)輸入框,可以輸入文字,添加表情,一開(kāi)始用了富文本編輯器,有點(diǎn)大材小用,所以自己封裝一個(gè)輸入框組件。支持輸入文字,選擇表情/插入表情,支持組合鍵換行,使用enter 進(jìn)行提交
效果圖

技術(shù)實(shí)現(xiàn)
- 通過(guò)原生
textarea實(shí)現(xiàn) - 通過(guò)
v-model來(lái)實(shí)現(xiàn) 父子組件的數(shù)據(jù)傳遞,子組件監(jiān)聽(tīng)數(shù)據(jù)的變化,向外emit('update:modelValue', inputValue),保證父組件能更新綁定的值 - 每次插入時(shí),需要重新聚焦,更新光標(biāo)位置
- 通過(guò)向外 暴露的 (__insertText),來(lái)實(shí)現(xiàn)插入表情/文字
- 通過(guò)向外暴露的(_clear) 輸入完畢發(fā)送后,需要
clear掉輸入框的內(nèi)容 - 通過(guò)向外暴露的(__isEmpty) 來(lái)判斷是否有內(nèi)容,如果沒(méi)內(nèi)容,做按鈕的禁用狀態(tài)(當(dāng)然也可以直接用父組件綁定的值)
- 父組件通過(guò)
ref就可以調(diào)用以上方法,來(lái)做操作。比如發(fā)送完數(shù)據(jù),調(diào)clear清空內(nèi)容,如果輸入框沒(méi)有內(nèi)容,則調(diào)用isEmpty,做按鈕的一些狀態(tài)
代碼實(shí)現(xiàn)(子組件)
/**
* 自定義文本輸入框組件
*/
import { ref, watch } from 'vue'
import { ElInput } from 'element-plus'
export default defineComponent({
props: {
modelValue: {
type: String,
default: ''
}
},
emits: ['update:modelValue', 'chatSend'],
setup(props, { emit, expose }) {
const { t } = useI18n()
const editorRef = ref()
const disabled = ref(false)
const valueHtml = ref(props.modelValue)
const currentEvent = ref()
watch(
() => valueHtml.value,
() => {
emit('update:modelValue', valueHtml.value)
}
)
// 插入元素
const _insertText = async (msg: string) => {
const msgLength = msg.length
const editor = currentEvent.value
if (editor) {
const startPos = editor.selectionStart
valueHtml.value = `${valueHtml.value.substring(0, startPos)}${'' + msg}${valueHtml.value.substring(startPos)}`
_focus()
nextTick(() => {
// 此處是 根據(jù)元素的長(zhǎng)度,來(lái)設(shè)置光標(biāo)位置
editor.setSelectionRange(startPos + msgLength, startPos + msgLength)
})
}
}
// focus
const _focus = () => {
editorRef.value && editorRef.value.focus()
}
// 清空編輯器
const _clear = () => {
editorRef.value && editorRef.value.clear()
}
// 是否內(nèi)容為空
const _isEmpty = () => {
const str = valueHtml.value.trim().replace(/\n/g, '')
return str === '' || str.length == 0 || typeof str === 'undefined'
}
// 禁用編輯器
const _disable = () => {
disabled.value = true
}
// 解除禁用編輯器
const _enable = () => {
disabled.value = false
}
const handleKeyDown = (event: KeyboardEvent) => {
currentEvent.value = event.target as HTMLInputElement
// 定義組合鍵 Map
const shortCutKeys: (keyof KeyboardEvent)[] = ['metaKey', 'altKey', 'ctrlKey', 'shiftKey']
const isEnterKey = event.code === 'Enter'
const isShortcutKeys = shortCutKeys.some((item) => event[item])
if (isEnterKey && isShortcutKeys) {
// 獲取光標(biāo)位置
const cursorPosition = currentEvent.value.selectionStart
// 拆分成兩段文本
const textBeforeCursor = valueHtml.value.slice(0, cursorPosition)
const textAfterCursor = valueHtml.value.slice(cursorPosition)
// 合并為帶有換行符的新文本
const newText = textBeforeCursor + '\n' + textAfterCursor
// 更新輸入框的值
valueHtml.value = newText
// 文本編輯器的高度發(fā)生變化后
nextTick(() => {
// 高度變化 自動(dòng)滾動(dòng)到底部
const editor = editorRef.value.textarea
editorRef.value.textarea.scrollTop = editor.scrollHeight
// 設(shè)置光標(biāo)位置為: start 和 end 相同,光標(biāo)會(huì)移動(dòng)到換行符后面的新行首
currentEvent.value.setSelectionRange(cursorPosition + 1, cursorPosition + 1)
})
} else if (event.code === 'Enter') {
// 阻止掉 Enter 的默認(rèn)換行行為
event.preventDefault()
emit('chatSend')
}
}
// 向外暴露方法
expose({
_insertText,
_clear,
_disable,
_isEmpty,
_enable,
_focus
})
return () => (
<div class="chatEditor">
<ElInput
ref={editorRef}
v-model={valueHtml.value}
type="textarea"
disabled={disabled.value}
onKeydown={handleKeyDown}
/>
</div>
)
}
})代碼實(shí)現(xiàn)(父組件調(diào)用)
輸入框組件
<base-editor
ref="chatEditorRef"
v-model="inputText"
@chat-send="sendText"
></base-editor>
工具欄組件
<ChatTools :chat-tools-list="newChatToolsList" @get-emoji="getToolsMsg" />
當(dāng)我們點(diǎn)擊工具欄組件,就會(huì)獲取到工具欄的文字/表情/或者插入的xxx ,此時(shí)根據(jù)引用,調(diào)用輸入框的暴露出的_insertText方法,直接就插入進(jìn)去
const getToolsMsg = async (msg: string) => {
chatEditorRef.value._insertText(msg)
}
其他方法調(diào)用
_isEmpty(): 未輸入,按鈕是禁用狀態(tài)
<el-button :disabled="chatEditorRef?._isEmpty()" type="primary" @click="sendText">
當(dāng)然你也可以直接使用綁定的輸入框的變量去判斷
<el-button :disabled="!inputText" @click="sendText">
_clear(): 提交完請(qǐng)求,清空輸入框
chatEditorRef.value._clear()
關(guān)于插入的問(wèn)題
光標(biāo)位置的獲取,根據(jù)元素插入位置,光標(biāo)換行,支持快捷鍵等??梢詤⒖忌弦黄恼拢?a href="http://www.fzitv.net/javascript/316756c60.htm" target="_blank">vue3通過(guò)組合鍵實(shí)現(xiàn)換行操作的示例詳解
總結(jié)
- 原生其實(shí)也可以實(shí)現(xiàn),沒(méi)必要用一個(gè)很重的富文本編輯器
- 做表情插入,或者其他插入,都是工具欄,和輸入框組件的關(guān)系是兄弟組件的關(guān)系,兄弟組件之間,怎么做數(shù)據(jù)傳遞,事件傳遞,怎么設(shè)計(jì)?
(1) 父組件作為中介
(2)事件總線通過(guò)訂閱/發(fā)布做消息通知
(3)倉(cāng)庫(kù)vuex/pina
事件總線還是能不用就不用,因?yàn)槿中缘臇|西,用著爽,后面復(fù)雜了,就亂了。而且 事件總線是發(fā)布訂閱,你還得注意銷(xiāo)毀,存?zhèn)}庫(kù) 又不太合適,子組件自己暴露出方法,讓其他組件調(diào)用,感覺(jué)目前是最簡(jiǎn)單的方式了。
到此這篇關(guān)于基于vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框效果的文章就介紹到這了,更多相關(guān)vue3輸入框內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Vue中輸入框僅支持?jǐn)?shù)字輸入的四種方法
- vue如何設(shè)置輸入框只能輸入數(shù)字且只能輸入小數(shù)點(diǎn)后兩位,并且不能輸入減號(hào)
- vue elementui 動(dòng)態(tài)追加下拉框、輸入框功能
- Vue中禁止編輯的常見(jiàn)方法(以禁止編輯輸入框?yàn)槔?
- vue之input輸入框防抖debounce函數(shù)的使用方式
- vue實(shí)現(xiàn)輸入框只允許輸入數(shù)字
- vue3+elementUI實(shí)現(xiàn)懸浮多行文本輸入框效果
- 基于vue+h5實(shí)現(xiàn)車(chē)牌號(hào)輸入框功能(demo)
相關(guān)文章
vue實(shí)現(xiàn)虛擬列表組件解決長(zhǎng)列表性能問(wèn)題
這篇文章主要介紹了在vue中實(shí)現(xiàn)虛擬列表組件,解決長(zhǎng)列表性能問(wèn)題,本文給大家分享實(shí)現(xiàn)思路及實(shí)例代碼,需要的朋友可以參考下2022-07-07
element?ui設(shè)置table自適應(yīng)表格寬,不自動(dòng)換行問(wèn)題
這篇文章主要介紹了element?ui設(shè)置table自適應(yīng)表格寬,不自動(dòng)換行問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Vue+Element UI實(shí)現(xiàn)概要小彈窗的全過(guò)程
彈窗效果是我們?nèi)粘i_(kāi)發(fā)中經(jīng)常遇到的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于Vue+Element UI實(shí)現(xiàn)概要小彈窗的相關(guān)資料,需要的朋友可以參考下2021-05-05
如何實(shí)現(xiàn)echarts markline標(biāo)簽名顯示自己想要的
這篇文章主要介紹了實(shí)現(xiàn)echarts markline標(biāo)簽名顯示自己想要的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
vue 封裝 Adminlte3組件的實(shí)現(xiàn)
這篇文章主要介紹了vue 封裝 Adminlte3組件的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
如何用vite打包解決前端發(fā)版后瀏覽器緩存問(wèn)題
這篇文章主要介紹了如何用vite打包解決前端發(fā)版后瀏覽器緩存問(wèn)題的相關(guān)資料,這樣的配置能夠有效避免瀏覽器緩存問(wèn)題,確保瀏覽器每次都能加載最新的代碼,同時(shí)又不影響第三方庫(kù)的緩存效果,需要的朋友可以參考下2024-11-11
解決vue 使用setTimeout,離開(kāi)當(dāng)前路由setTimeout未銷(xiāo)毀的問(wèn)題
這篇文章主要介紹了解決vue 使用setTimeout,離開(kāi)當(dāng)前路由setTimeout未銷(xiāo)毀的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07

