編輯器實(shí)現(xiàn)自定義高亮關(guān)鍵字、提示輸入操作

效果展示

image.png

前置

npm i codemirror@5.65.5 //鎖定版本

組件

<!--
 * @Descripttion: 代碼編輯器
 * @version: 1.0
 * @Author: bing
 * @Date: 2024年3月1日
 * @LastEditors: 
 * @LastEditTime: 
-->

<template>
    <div class="bing-code-editor" :style="{'height':_height}">
        <textarea ref="textarea" v-model="contentValue"></textarea>
    </div>
</template>

<script>
    import { markRaw } from "vue"

    //框架
    import CodeMirror from 'codemirror'
    import 'codemirror/lib/codemirror.css'

    //主題
    import 'codemirror/theme/idea.css'
    import 'codemirror/theme/darcula.css'

    //功能
    import 'codemirror/addon/selection/active-line'

    //語言
    import 'codemirror/mode/javascript/javascript'
    import 'codemirror/mode/sql/sql'


    import 'codemirror/addon/hint/show-hint.js'
    import 'codemirror/addon/hint/show-hint.css'

    import 'codemirror/addon/hint/sql-hint.js'

    export default {
        props: {
            modelValue: {
                type: String,
                default: ""
            },
            mode: {
                type: String,
                default: "javascript"
            },
            height: {
                type: [String,Number],
                default: 300,
            },
            options: {
                type: Object,
                default: () => {}
            },
            theme: {
                type: String,
                default: "idea"
            },
            readOnly: {
                type: Boolean,
                default: false
            },
        },
        data() {
            return {
                contentValue: this.formatStrInJson(this.modelValue),
                coder: null,
                keywords: [
                    ['吃了', "keyword"],
                    ['兵兵', "property"],
                    ['徐', "number"],
                    ['牛肉', "comment"]
                ],
                opt: {
                    theme: this.theme,  //主題
                    styleActiveLine: true,  //高亮當(dāng)前行
                    lineNumbers: true,  //行號(hào)
                    lineWrapping: true, //自動(dòng)換行
                    tabSize: 4, //Tab縮進(jìn)
                    indentUnit: 4,  //縮進(jìn)單位
                    indentWithTabs : true,  //自動(dòng)縮進(jìn)
                    mode : this.mode,   //語言
                    readOnly: this.readOnly,    //只讀
                    lint: true, // 格式化
                    foldGutter: true, // 啟用折疊效果
                    hintOptions: { // 代碼提示
                        completeSingle: false, // 當(dāng)匹配只有一項(xiàng)的時(shí)候是否自動(dòng)補(bǔ)全
                        hint: this.bingShowHint // 自定義提示
                    },
                    gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], // 配置折疊參數(shù)
                    ...this.options
                },
                isBlank: false, // 是否為空格
                start: null // 光標(biāo)開始位置
            }
        },
        computed: {
            _height() {
                return Number(this.height)?Number(this.height)+'px':this.height
            },
        },
        watch: {
            modelValue(val) {
                this.contentValue = val
                if (val !== this.coder.getValue()) {
                    const format = this.formatStrInJson(val)
                    this.coder.setValue(format)
                }
            }
        },
        mounted() {
            this.init()
            //獲取掛載的所有modes
            //console.log(CodeMirror.modes)
        },
        methods: {
            init(){
                const _this = this
                // 顏色變換[高亮自定義關(guān)鍵詞]
                CodeMirror.defineMode('javascript', function() {
                    return {
                        token: (stream, state) => {
                            const cmCustomCheckStreamFn = (streamWrapper) => {
                                for (let i = 0; i < _this.keywords.length; i++) {
                                    if (streamWrapper.match(_this.keywords[i][0])) { return _this.keywords[i][1] }
                                }
                                return ''
                            }
                            const ret = cmCustomCheckStreamFn(stream)
                            if(ret.length > 0) return ret
                            stream.next()
                            return null
                        }
                    }
                })

                this.coder = markRaw(CodeMirror.fromTextArea(this.$refs.textarea, this.opt))
                this.coder.on('change', (coder, data) => {
                    // console.log(this.inputFilter(data.text))
                    this.contentValue = coder.getValue()
                    this.$emit('update:modelValue', this.contentValue)
                })
                // 輸入或者粘貼時(shí)觸發(fā)
                this.coder.on('inputRead', (coder) => {
                    const cursor = coder.getDoc().getCursor()
                    const token = coder.getTokenAt(cursor)
                    console.log(token.string)
                    if((token.string).trim() == '') {
                        this.start = token.start + 1
                    }
                    if(this.start === null) {
                        this.start = token.start
                    }
                    const currentData = this.inputFilter(token.string)
                    if(currentData) {
                        coder.showHint()
                    }
                })
            },
            // 輸入過濾
            inputFilter(data) {
                var reg = /^[\u4e00-\u9fa5]+$/;
                if(!reg.test(data)) {
                    return false
                }else {
                    return data
                }
            },
            // 自定義彈窗內(nèi)容
            bingShowHint(cmInstance) {
                let cursor = cmInstance.getCursor()
                let token = cmInstance.getTokenAt(cursor)
                return {
                    list: [ 
                        {
                            text: '吃了',
                            displayText: '吃了',
                            displayInfo: '吃了',
                        },
                        {
                            text: '徐',
                            displayText: '徐',
                            displayInfo: '徐',
                        },
                        {
                            text: '兵兵',
                            displayText: '兵兵',
                            displayInfo: '兵兵',
                        }
                    ],
                    from: {
                        // ch: token.start, line: cursor.line
                        ch: this.start, line: cursor.line
                    },
                    to: {
                        ch: token.end, line: cursor.line
                    }
                }
            },
            formatStrInJson(strValue) {
                // return JSON.stringify(JSON.parse(strValue), null, 4)
                return strValue
            }
        }
    }
</script>

<style scoped>
    .bing-code-editor {font-size: 14px;border: 1px solid #ddd;line-height: 150%;}
    .bing-code-editor:deep(.CodeMirror)  {height: 100%;}
</style>

組件使用


<script setup>
import scCodeEditor from '@/components/bingEditor/index.vue'
import { ref } from 'vue'

const demoValue = ref('')
</script>

<template>
  <div class="editor">
    <sc-code-editor v-model="demoValue" mode="javascript" height="100vh" theme="darcula"></sc-code-editor>
  </div>
</template>

<style scoped>
.editor {
  width: 100%;
  height: 100%;
}
</style>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌湖蜕,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宋列,死亡現(xiàn)場(chǎng)離奇詭異重荠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)虚茶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門戈鲁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嘹叫,你說我怎么就攤上這事婆殿。” “怎么了罩扇?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵婆芦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我喂饥,道長(zhǎng)消约,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任员帮,我火速辦了婚禮或粮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捞高。我一直安慰自己氯材,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布硝岗。 她就那樣靜靜地躺著氢哮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪型檀。 梳的紋絲不亂的頭發(fā)上冗尤,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音胀溺,去河邊找鬼裂七。 笑死,一個(gè)胖子當(dāng)著我的面吹牛月幌,可吹牛的內(nèi)容都是我干的碍讯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼扯躺,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼捉兴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起录语,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤倍啥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后澎埠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虽缕,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年蒲稳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氮趋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伍派。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖剩胁,靈堂內(nèi)的尸體忽然破棺而出诉植,到底是詐尸還是另有隱情,我是刑警寧澤昵观,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布晾腔,位于F島的核電站,受9級(jí)特大地震影響啊犬,放射性物質(zhì)發(fā)生泄漏灼擂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一觉至、第九天 我趴在偏房一處隱蔽的房頂上張望剔应。 院中可真熱鬧,春花似錦康谆、人聲如沸领斥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽月洛。三九已至,卻和暖如春孽锥,著一層夾襖步出監(jiān)牢的瞬間嚼黔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工惜辑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唬涧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓盛撑,卻偏偏與公主長(zhǎng)得像碎节,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抵卫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容