最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框效果

 更新時(shí)間:2024年03月01日 11:28:51   作者:趙小川  
這篇文章主要為大家詳細(xì)介紹了如何使用Vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框,可以實(shí)現(xiàn)輸入文字,添加表情等功能,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

需求背景

需要一個(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)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

塔城市| 平定县| 兴国县| 行唐县| 阿坝| 垦利县| 昭通市| 顺昌县| 潼南县| 蒲城县| 张家川| 丰镇市| 大同市| 若羌县| 图木舒克市| 湛江市| 黄陵县| 民县| 珠海市| 南丹县| 金湖县| 四子王旗| 交城县| 乌拉特前旗| 海南省| 观塘区| 富宁县| 黑河市| 泾阳县| 柘城县| 兴城市| 香港| 开封县| 杨浦区| 九江县| 谢通门县| 会泽县| 庆元县| 木兰县| 孟州市| 东丽区|