背景
vue项目使用CodeMirror组件,要求增加一个类似浏览器的Ctrl+F的查询功能。为什么不直接使用浏览器的Ctrl+F功能?因为当CodeMirror里面的文本内容超出显示高度有滚动条时,超出滚动的内容是没有渲染在页面的,所以Ctrl+F是搜索不到的。
CodeMirror有自带的搜索功能,但是太简陋了,无法满足需求。
最终效果
代码
codemirror.vue
<template>
<codemirror v-model:value="currValue" :options="options" border :placeholder="$props.placeholder" @change="onChange" :keyHandled="onKeyhandle"/>
</template>
<script setup lang="ts">
/**
*@file 公共组件-代码编辑器
*/
// TS类型
// 内置方法
import {
reactive, ref, watch } from 'vue';
// 公共方法
import './components/codemirror_search/revisedsearch'
import './components/codemirror_search/revisedsearch.css'
import './components/advanced-dialog/advanced-dialog'
// 接口
// 组件
// Store
// 常量
import {
modeMap } from 'src/plugins/codemirror';
const $props = withDefaults(
defineProps<{
value?: string;
placeholder?: string;
suffix?: string;
}>(),
{
placeholder: '请输入...',
suffix: 'txt'
}
);
// 获取类名为 CodeMirror-dialog CodeMirror-dialog-top 的 div 元素
let dialogDiv = document.querySelector('.CodeMirror-dialog.CodeMirror-dialog-top');
if (dialogDiv) {
// 创建左箭头按钮
let leftArrowButton = document.createElement('button');
leftArrowButton.textContent = '←';
// 创建右箭头按钮
let rightArrowButton = document.createElement('button');
rightArrowButton.textContent = '→';
// 将按钮添加到 div 元素中
dialogDiv.appendChild(leftArrowButton);
dialogDiv.appendChild(rightArrowButton);
}
// 当前编辑器的内容
const currValue = ref('');
const $emit = defineEmits(['update:value']);
// 双向绑定
watch(
() => $props.value,
(val) => {
currValue.value = val ?? '';
},
{
immediate: true }
);
const onChange = (value: string) => {
$emit('update:value', value);
};
const onKeyhandle = (name:any, event:any) => {
console.log('123--', name, event)
};
// 编辑器配置
const options = reactive({
tabSize: 4, // Tab缩进,默认4
mode: modeMap[$props.suffix],
lineNumbers: true, // 显示行号
line: true,
lineWrapping: true, // 自动换行
theme: 'darcula', // 主题
smartIndent: true, // 智能缩进
indentUnit: 4, // 智能缩进单位为4个空格长度
indentWithTabs: true, // 使用制表符进行智能缩进
// 在行槽中添加行号显示器、折叠器、语法检测器
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
foldGutter: true, // 启用行槽中的代码折叠
autofocus: true, // 自动聚焦
matchBrackets: true, // 匹配结束符号,比如"]、}"
autoCloseBrackets: true, // 自动闭合符号
styleActiveLine: true // 显示选中行的样式
});
watch(
() => $props.suffix,
(val) => {
options.mode = modeMap[val ?? 'txt'];
}
);
</script>
<style scoped lang="less"></style>
/**
* @file 注册codemirror编辑器插件
*/
import {
TCommonDir } from 'src/types/common';
import {
InstallCodemirro } from 'codemirror-editor-vue3';
// 主题
import 'codemirror/theme/darcula.css';
import 'codemirror/lib/codemirror.css';
// 语法
import 'codemirror/mode/textile/textile';
import 'codemirror/mode/clike/clike';
import 'codemirror/mode/python/python';
import 'codemirror/mode/go/go';
import 'codemirror/mode/php/php';
import 'codemirror/mode/sql/sql';
import 'codemirror/mode/shell/shell';
import 'codemirror/mode/powershell/powershell';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/vue/vue';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/mode/yaml/yaml';
// 插件
import 'codemirror/addon/dialog/dialog.js'
import 'codemirror/addon/dialog/dialog.css'
import 'codemirror/addon/display/placeholder.js';
import 'codemirror/addon/scroll/annotatescrollbar.js'
import 'codemirror/addon/search/jump-to-line.js'
import 'codemirror/addon/search/matchesonscrollbar.js'
import 'codemirror/addon/search/matchesonscrollbar.css'
import 'codemirror/addon/search/match-highlighter.js'
import 'codemirror/addon/search/searchcursor.js'
import 'codemirror/addon/search/search.js'
import 'codemirror/addon/display/panel.js'
// 折叠
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter'
import 'codemirror/addon/fold/brace-fold'
import 'codemirror/addon/fold/comment-fold'
// 文件后缀映射语法mode
const modeMap: TCommonDir<string> = {
txt: 'text/x-textile',
c: 'text/x-c',
'c++': 'text/x-c++src',
'c#': 'text/x-csharp',
py: 'text/x-python',
java: 'text/x-java',
go: 'text/x-go',
php: 'text/x-php',
sql: 'text/x-sql',
sh: 'text/x-sh',
ps1: 'application/x-powershell',
js: 'text/javascript',
ts: 'text/javascript',
json: 'application/json',
xml: 'text/xml',
html: 'text/html',
vue: 'text/x-vue',
md: 'text/x-markdown',
yml: 'text/yaml'
};
export {
InstallCodemirro, modeMap };
revisedsearch.js
/* eslint-disable */
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj; } : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Revised search plugin written by Jamie Morris
// Define search commands. Depends on advanceddialog.js
(function (mod) {
if ((typeof exports === "undefined" ? "undefined" : _typeof(exports)) == "object" && (typeof module === "undefined" ? "undefined" : _typeof(module)) == "object") // CommonJS
mod(require("codemirror"), require("codemirror-advanceddialog"));else if (typeof define == "function" && define.amd) // AMD
define(["codemirror", "codemirror-advanceddialog"], mod);else // Plain browser env
mod(CodeMirror);
})(function (CodeMirror) {
"use strict";
var replaceDialog = '\n <div class="row find">\n <label for="CodeMirror-find-field">替换:</label>\n <input id="CodeMirror-find-field" type="text" class="CodeMirror-search-field" placeholder="Find" />\n <span class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>\n <span class="CodeMirror-search-count"></span>\n </div>\n <div class="row replace">\n <label for="CodeMirror-replace-field">With:</label>\n <input id="CodeMirror-replace-field" type="text" class="CodeMirror-search-field" placeholder="Replace" />\n </div>\n <div class="but