基于vue3實現(xiàn)一個簡單的輸入框效果
需求背景
需要一個輸入框,可以輸入文字,添加表情,一開始用了富文本編輯器,有點大材小用,所以自己封裝一個輸入框組件。支持輸入文字,選擇表情/插入表情,支持組合鍵換行,使用enter 進(jìn)行提交
效果圖
技術(shù)實現(xiàn)
- 通過原生
textarea
實現(xiàn) - 通過
v-model
來實現(xiàn) 父子組件的數(shù)據(jù)傳遞,子組件監(jiān)聽數(shù)據(jù)的變化,向外emit('update:modelValue', inputValue)
,保證父組件能更新綁定的值 - 每次插入時,需要重新聚焦,更新光標(biāo)位置
- 通過向外 暴露的 (__insertText),來實現(xiàn)插入表情/文字
- 通過向外暴露的(_clear) 輸入完畢發(fā)送后,需要
clear
掉輸入框的內(nèi)容 - 通過向外暴露的(__isEmpty) 來判斷是否有內(nèi)容,如果沒內(nèi)容,做按鈕的禁用狀態(tài)(當(dāng)然也可以直接用父組件綁定的值)
- 父組件通過
ref
就可以調(diào)用以上方法,來做操作。比如發(fā)送完數(shù)據(jù),調(diào)clear清空內(nèi)容,如果輸入框沒有內(nèi)容,則調(diào)用isEmpty
,做按鈕的一些狀態(tài)
代碼實現(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ù)元素的長度,來設(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(() => { // 高度變化 自動滾動到底部 const editor = editorRef.value.textarea editorRef.value.textarea.scrollTop = editor.scrollHeight // 設(shè)置光標(biāo)位置為: start 和 end 相同,光標(biāo)會移動到換行符后面的新行首 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> ) } })
代碼實現(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)我們點擊工具欄組件,就會獲取到工具欄的文字/表情/或者插入的xxx
,此時根據(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()
: 提交完請求,清空輸入框
chatEditorRef.value._clear()
關(guān)于插入的問題
光標(biāo)位置的獲取,根據(jù)元素插入位置,光標(biāo)換行,支持快捷鍵等??梢詤⒖忌弦黄恼拢?a href="http://chabaoo.cn/javascript/316756c60.htm" target="_blank">vue3通過組合鍵實現(xiàn)換行操作的示例詳解
總結(jié)
- 原生其實也可以實現(xiàn),沒必要用一個很重的富文本編輯器
- 做表情插入,或者其他插入,都是工具欄,和輸入框組件的關(guān)系是兄弟組件的關(guān)系,兄弟組件之間,怎么做數(shù)據(jù)傳遞,事件傳遞,怎么設(shè)計?
(1) 父組件作為中介
(2)事件總線通過訂閱/發(fā)布做消息通知
(3)倉庫vuex/pina
事件總線還是能不用就不用,因為全局性的東西,用著爽,后面復(fù)雜了,就亂了。而且 事件總線是發(fā)布訂閱,你還得注意銷毀,存?zhèn)}庫 又不太合適,子組件自己暴露出方法,讓其他組件調(diào)用,感覺目前是最簡單的方式了。
到此這篇關(guān)于基于vue3實現(xiàn)一個簡單的輸入框效果的文章就介紹到這了,更多相關(guān)vue3輸入框內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue中使用Cesium加載shp文件、wms服務(wù)、WMTS服務(wù)問題
這篇文章主要介紹了vue中使用Cesium加載shp文件、wms服務(wù)、WMTS服務(wù)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05vite+vue3+element-plus項目搭建的方法步驟
因為vue3出了一段時間了,element也出了基于vue3.x版本的element-plus,vite打包聽說很快,嘗試一下,感興趣的可以了解一下2021-06-06Vue基礎(chǔ)popover彈出框編寫及bug問題分析
這篇文章主要為大家介紹了Vue基礎(chǔ)popover彈出框編寫及bug問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09vite中的glob-import批量導(dǎo)入的實現(xiàn)
本文主要介紹了vite中的glob-import批量導(dǎo)入的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07vue-router鉤子函數(shù)實現(xiàn)路由守衛(wèi)
這篇文章主要介紹了vue-router鉤子函數(shù)實現(xiàn)路由守衛(wèi),對vue感興趣的同學(xué),可以參考下2021-04-04vue3配置代理實現(xiàn)axios請求本地接口返回PG庫數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了vue3配置代理實現(xiàn)axios請求本地接口返回PG庫數(shù)據(jù)的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-03-03