代码编辑组件

代码编辑组件

    • 文章说明
    • 核心代码
    • 运行演示
    • 源码下载

文章说明

拖了很久,总算是自己写了一个简单的代码编辑组件,虽然还有不少的bug,真的很难写,在写的过程中感觉自己的前端技术根本不够用,好像总是方案不够好;目前写出了这个效果,等待后续学习别的现有产品,再慢慢补充

采用div的设置可编辑属性 contenteditable=“true”,然后结合 highlight的代码高亮,效果还不错;最开始相加的代码输入提示,以为不难实现,但是真的写起来,总是没有办法很好的实现那个输入面板的位置控制;索性就暂时不加输入提示,然后搜索功能,也是有不小的难点;目前仍然存在着一些bug待修复,但是可以先作为小demo试用一下

目前算是完成第一阶段,虽然组件的功能不是很完善,但是基本的代码高亮和搜索功能也有了,算是差强人意啦

核心代码

输入组件

<script setup>
import {onMounted, reactive} from "vue";
import Search from "@/components/Search.vue";
import hljs from 'highlight.js';
import "highlight.js/styles/idea.css";
import {useSearchStore} from "@/stores/search";
import {appendDom} from "@/utils";const data = reactive({lineNumber: [1],language: "html"
});const search = useSearchStore();function refresh() {const height = inputElem.scrollHeight;const number = Math.ceil(height / 21);if (number !== data.lineNumber.length) {data.lineNumber = [];for (let i = 0; i < number; i++) {data.lineNumber.push(i + 1);}}if (search.showSearch) {search.search(false);}
}let leftLineNumberContainer;
let rightInputArea;
let inputElem;function syncScroll(event) {if (event.target === leftLineNumberContainer) {rightInputArea.scrollTo({top: event.target.scrollTop,});}if (event.target === rightInputArea) {leftLineNumberContainer.scrollTo({top: event.target.scrollTop,});}
}function inputFocus() {inputElem.focus();const range = document.createRange();range.selectNodeContents(inputElem);range.collapse(false);const sel = window.getSelection();sel.removeAllRanges();sel.addRange(range);if (!leftLineNumberContainer) {leftLineNumberContainer = document.getElementsByClassName("left-line-number-container")[0];}if (!rightInputArea) {rightInputArea = document.getElementsByClassName("right-input-area")[0];}leftLineNumberContainer.scrollTo({top: rightInputArea.scrollHeight,});rightInputArea.scrollTo({top: rightInputArea.scrollHeight,});
}onMounted(() => {inputElem = document.getElementsByClassName("input-elem")[0];rightInputArea = document.getElementsByClassName("right-input-area")[0];search.rightInputArea = rightInputArea;search.inputElem = inputElem;search.language = data.language;search.refresh = refresh;inputFocus();
});function getPasteData(event) {const clipData = event.clipboardData || window.clipboardDataconst value = clipData.getData('text/plain');let highlightedCode = hljs.highlight(value, {language: data.language}).value;const container = document.createElement("span");container.innerHTML = highlightedCode;function wrapTextNodesInSpan(element) {for (let i = 0; i < element.childNodes.length; i++) {const child = element.childNodes[i];if (child.nodeType === Node.TEXT_NODE) {const span = document.createElement('span');span.textContent = child.nodeValue;element.insertBefore(span, child);element.removeChild(child);} else if (child.nodeType === Node.ELEMENT_NODE) {wrapTextNodesInSpan(child);}}}wrapTextNodesInSpan(container);appendDom(rightInputArea, container);refresh();
}function keydown(event) {search.keydown(event);if (event.key === "Tab") {event.preventDefault();appendContent("  ");}if (event.ctrlKey) {return;}if (event.key === "Enter" || event.key === "Backspace" || event.key === "Delete") {setTimeout(() => {refresh();}, 10);return;}if (isAlphaNumeric(event.key)) {event.preventDefault();appendContent(event.key);refresh();}
}function isAlphaNumeric(key) {return /^[a-zA-Z0-9]$/.test(key);
}function appendContent(content) {const span = document.createElement("span");span.textContent = content;appendDom(rightInputArea, span, false);
}
</script><template><div class="editor-container"><Search/><div style="display: flex" :style="{ height: search.showSearch ? 'calc(100% - 30px)' : '100%' }"><div class="left-line-number-container" @scroll="syncScroll($event)"><template v-for="(item, index) in data.lineNumber" :key="index"><p>{{ item }}</p></template></div><div class="right-input-area" @scroll="syncScroll($event)" @click.self="inputFocus"><div class="input-elem" :contenteditable="!search.showSearch" @keydown="keydown($event)"@paste.prevent="getPasteData($event)"></div></div></div></div>
</template><style lang="scss">
* {padding: 0;margin: 0;box-sizing: border-box;
}.hljs-tag {background-color: transparent !important;
}.hljs-attribute, .hljs-number, .hljs-regexp, .hljs-link {font-weight: normal;color: #3931c5;
}.hljs-section, .hljs-name, .hljs-literal, .hljs-keyword, .hljs-selector-tag, .hljs-type, .hljs-selector-id, .hljs-selector-class {font-weight: normal;color: #3931c5;
}.editor-container {width: 100vw;height: 100vh;background-color: #ffffff;padding-left: 200px;.left-line-number-container {min-width: 60px;background-color: #f2f2f2;border: 1px solid #d4d4d4;padding: 4px 4px 230px;overflow: auto;&::-webkit-scrollbar {height: 0;width: 0;}p {width: fit-content;color: #adadad;font-size: 14px;line-height: 1.5;font-family: "JetBrains Mono", sans-serif;word-spacing: 0.2rem;text-align: right;}}.right-input-area {flex: 1;border: 1px solid #d4d4d4;border-left: none;padding: 4px 4px 230px;overflow: auto;position: relative;cursor: default;#default-cursor {width: 0;height: 0;display: inline-block;}&::-webkit-scrollbar {height: 10px;width: 10px;}&::-webkit-scrollbar-thumb {background-color: #e2e2e2;border-radius: 0;}&::-webkit-scrollbar-track {background-color: transparent;}.input-elem {border: none;outline: none;height: fit-content;min-width: 100%;font-size: 14px;color: #080808;min-height: 21px;line-height: 21px;font-family: "JetBrains Mono", sans-serif;word-spacing: 0.2rem;white-space: pre;word-break: break-all;&::selection {background-color: #a6d2ff;}.highlight-item {background-color: #ffe959;}.current-highlight-item {background-color: #a6d2ff;}pre {font-family: "JetBrains Mono", sans-serif;&::selection {background-color: #a6d2ff;}}}}
}
</style>

搜索组件

<script setup>
import {useSearchStore} from "@/stores/search";const search = useSearchStore();function changeCase() {search.caseSelected = !search.caseSelected;search.search();
}function changeWord() {search.wordSelected = !search.wordSelected;search.search();
}function close() {search.currentIndex = 1;search.showSearch = false;search.recover();
}function last() {search.beginSearch = true;if (search.currentIndex === 1) {search.currentIndex = search.searchResult.length;} else {search.currentIndex--;}search.search();
}function next() {search.beginSearch = true;if (search.currentIndex === search.searchResult.length) {search.currentIndex = 1;} else {search.currentIndex++;}search.search();
}
</script><template><div v-show="search.showSearch" class="search-container" @click.stop>🔍<input v-model="search.searchText" @input="search.search" id="input"/><div :class="search.caseSelected ? ' active-case ' : ''" class="case" @click="changeCase">Cc</div><div :class="search.wordSelected ? ' active-word ' : ''" class="word" @click="changeWord">W</div><div class="result"><template v-if="!search.beginSearch || search.searchResult.length === 0">{{ search.searchResult.length }}results</template><template v-if="search.beginSearch && search.searchResult.length > 0">{{search.currentIndex}}/{{ search.searchResult.length }}</template></div><div class="last" @click="last"></div><div class="next" @click="next"></div><div class="close" @click="close">×</div></div>
</template><style lang="scss" scoped>
.search-container {width: 100%;height: 30px;border: 1px solid #d1d1d1;border-bottom: none;display: flex;align-items: center;user-select: none;input {border: none;outline: none;width: 350px;height: 28px;margin: 10px;}.case, .word {background-color: #ffffff;color: #bfc5c8;width: 25px;height: 25px;padding: 3px;margin-right: 4px;display: flex;justify-content: center;align-items: center;font-size: 14px;font-weight: 600;font-family: "JetBrains Mono", sans-serif;border-radius: 5px;cursor: default;&:hover {background-color: #dfdfdf;color: #899399;}}.active-case, .active-word {background-color: #dae4ed;color: #40b6e0;&:hover {background-color: #dfdfdf;color: #44b7e0;}}.result {font-size: 12px;font-family: "JetBrains Mono", sans-serif;margin: 0 20px;width: 70px;height: 28px;display: flex;justify-content: center;align-items: center;}.last, .next {width: 22px;height: 22px;display: flex;justify-content: center;align-items: center;font-size: 18px;color: #6e6e6e;&:hover {background-color: #dfdfdf;border-radius: 5px;cursor: default;}}.close {margin-left: auto;margin-right: 5px;width: 22px;height: 22px;display: flex;justify-content: center;align-items: center;font-size: 18px;color: #bec4c6;&:hover {background-color: #dfdfdf;border-radius: 5px;cursor: default;}}
}
</style>

运行演示

Java代码编辑
在这里插入图片描述

HTML代码编辑
在这里插入图片描述

源码下载

代码编辑组件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/883494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flux 开源替代,他来了——Liberflux

LibreFLUX 是 FLUX.1-schnell 的 Apache 2.0 版本&#xff0c;它提供完整的 T5 上下文长度&#xff0c;使用注意力屏蔽&#xff0c;恢复了无分类器引导&#xff0c;并完全删除了 FLUX 美学微调/DPO 的大部分内容。 这意味着它比基本通量要难看得多&#xff0c;但它有可能更容易…

数据结构与算法汇总整理篇——数组与字符串双指针与滑动窗口的联系学习及框架思考

数组 数组精髓&#xff1a;循环不变量原则 数组是存放在连续内存空间上的相同类型数据的集合&#xff0c;通过索引(下标)访问元素&#xff0c;索引从0开始 随机访问快(O(1)时间复杂度)&#xff1b;插入删除慢(需要移动元素)&#xff1b;长度固定(部分语言中可动态调整) 其存…

解决电脑突然没有声音

问题描述&#xff1a;电脑突然没有声音了&#xff0c;最近没有怎么动过系统&#xff0c;没有安装或者卸载过什么软件&#xff0c;也没有安装或者卸载过驱动程序&#xff0c;怎么就没有声音了呢&#xff1f; 问题分析&#xff1a;仔细观察&#xff0c;虽然音量按钮那边看不到什…

索引的使用以及使用索引优化sql

索引就是一种快速查询和检索数据的数据结构&#xff0c;mysql中的索引结构有&#xff1a;B树和Hash。 索引的作用就相当于目录的作用&#xff0c;我么只需先去目录里面查找字的位置&#xff0c;然后回家诶翻到那一页就行了&#xff0c;这样查找非常快&#xff0c; 一、索引的使…

[Linux网络编程]06-I/O多路复用策略---select,poll分析解释,优缺点,实现IO多路复用服务器

一.I/O多路复用 I/O多路复用是一种用于提高系统性能的 I/O 处理机制。 它允许一个进程&#xff08;或线程&#xff09;同时监视多个文件描述符&#xff08;可以是套接字、管道、终端设备等&#xff09;&#xff0c;等待这些文件描述符中出现读、写或异常状态。一旦有满足条件的…

ts:类的创建(class)

ts&#xff1a;类的创建&#xff08;class&#xff09; 一、主要内容说明二、例子class类的创建1.源码1 &#xff08;class类的创建&#xff09;2.源码1的运行效果 三、结语四、定位日期 一、主要内容说明 class创建类里主要有三部分组成&#xff0c;变量的声明&#xff0c;构…

ts:数组的常用方法(filter)

ts&#xff1a;数组的常用方法&#xff08;filter&#xff09; 一、主要内容说明二、例子filter方法&#xff08;过滤&#xff09;1.源码1 &#xff08;push方法&#xff09;2.源码1运行效果 三、结语四、定位日期 一、主要内容说明 ts中数组的filter方法&#xff0c;是筛选数…

【STM32】单片机ADC原理详解及应用编程

本篇文章主要详细讲述单片机的ADC原理和编程应用&#xff0c;希望我的分享对你有所帮助&#xff01; 目录 一、STM32ADC概述 1、ADC&#xff08;Analog-to-Digital Converter&#xff0c;模数转换器&#xff09; 2、STM32工作原理 二、STM32ADC编程实战 &#xff08;一&am…

C++STL之stack

1.stack的使用 函数说明 接口说明 stack() 构造空的栈 empty() 检测 stack 是否为空 size() 返回 stack 中元素的个数 top() 返回栈顶元素的引用 push() 将元素 val 压入 stack 中 pop() 将 stack 中尾部的元素弹出 2.stack的模拟实现 #include<vector> namespace abc { …

LeetCode 热题 100之普通数组

1.最大子数组和 思路分析&#xff1a;这个问题可以通过动态规划来解决&#xff0c;我们可以使用Kadane’s Algorithm&#xff08;卡登算法&#xff09;来找到具有最大和的连续子数组。 Kadane’s Algorithm 的核心思想是利用一个变量存储当前的累加和 currentSum&#xff0c;并…

MATLAB生物细胞瞬态滞后随机建模定量分析

&#x1f3af;要点 基于随机动态行为受化学主方程控制&#xff0c;定量分析单细胞瞬态效应。确定性常微分方程描述双稳态和滞后现象。通过随机性偏微分方程描述出暂时性滞后会逐渐达到平稳状态&#xff0c;并利用熵方法或截断方法计算平衡收敛速度的估计值。随机定量分析模型使…

python查询并安装项目所依赖的所有包

引言 如果需要进行代码的移植&#xff0c;肯定少不了在另一台pc或者服务器上进行环境的搭建&#xff0c;那么首先是要知道在已有的工程的代码中用到了哪些包&#xff0c;此时&#xff0c;如果是用人工去一个一个的代码文件中去查看调用了哪些包&#xff0c;这个工作甚是繁琐。…

C++《vector的模拟实现》

在之前《vector》章节当中我们学习了STL当中的vector基本的使用方法&#xff0c;了解了vector当中各个函数该如何使用&#xff0c;在学习当中我们发现了vector许多函数的使用是和我们之前学习过的string类的&#xff0c;但同时也发现vector当中一些函数以及接口是和string不同的…

H5实现PDF文件预览,使用pdf.js-dist进行加载

H5实现PDF文件预览&#xff0c;使用pdf.js-dist进行加载 一、应用场景 在H5平台上预览PDF文件是在原本已经开发完成的系统中新提出的需求&#xff0c;原来的系统业务部门是在PC端进行PDF的预览与展示&#xff0c;但是现在设备进行了切换&#xff0c;改成了安卓一体机进行文件…

基于neo4j的课程资源生成性知识图谱

你是不是还在为毕业设计苦恼&#xff1f;又或者想在课堂中进行知识的高效管理&#xff1f;今天给大家分享一个你一定会感兴趣的技术项目——基于Neo4j的课程资源生成性知识图谱&#xff01;&#x1f4a1; 这套系统通过知识图谱的形式&#xff0c;将课程资源、知识点和学习路径…

前端页面样式没效果?没应用上?

当我们在开发项目时会有很多个页面、相同的标签&#xff0c;也有可能有相同的class值。样式设置的多了&#xff0c;分不清哪个是当前应用的。我们可以使用网页的开发者工具。 在我们开发的网页中按下f12或&#xff1a; 在打开的工具中我们可以使用元素选择器&#xff0c;单击我…

渗透测试-百日筑基—SQL注入篇时间注入绕过HTTP数据编码绕过—下

day8-渗透测试sql注入篇&时间注入&绕过&HTTP数据编码绕过 一、时间注入 SQL注入时间注入&#xff08;也称为延时注入&#xff09;是SQL注入攻击的一种特殊形式&#xff0c;它属于盲注&#xff08;Blind SQL Injection&#xff09;的一种。在盲注中&#xff0c;攻击…

基于丑萌气质狗--C#的sqlserver学习

#region 常用取值 查询List<string> isName new List<string> { "第一", "第二", "第三", "第四" }; List<string> result isName.Where(m > m "第三").ToList();MyDBContext myDBnew MyDBContext(…

web3对象如何连接以太网络节点

实例化web3对象 当我们实例化web3对象&#xff0c;我们一般开始用本地址&#xff0c;如下 import Web3 from web3 var web3 new Web3(Web3.givenProvider || ws://localhost:5173)我们要和以太网进行交互&#xff0c;所以我们要将’ws://localhost:5173’的本地地址换成以太…

如何在短时间内入门并掌握深度学习?

如何在短时间内快速入门并掌握深度学习&#xff0c;是很多读者的困惑——晦涩难懂的数学 知识、复杂的算法、烦琐的编程……深度学习虽然让无数读者心怀向往&#xff0c;却也让不少人望而生畏&#xff0c;深感沮丧&#xff1a;时间没少花&#xff0c;却收效甚微。 如何才能更好…