代码编辑组件
代码编辑组件
- 文章说明
- 核心代码
- 运行演示
- 源码下载
文章说明
拖了很久,总算是自己写了一个简单的代码编辑组件,虽然还有不少的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代码编辑
源码下载
代码编辑组件
相关文章:
代码编辑组件
代码编辑组件 文章说明核心代码运行演示源码下载 文章说明 拖了很久,总算是自己写了一个简单的代码编辑组件,虽然还有不少的bug,真的很难写,在写的过程中感觉自己的前端技术根本不够用,好像总是方案不够好;…...
裴蜀定理与欧几里得算法——蓝桥杯真题中的应用
目录 裴蜀定理(Bzouts Theorem)1、定义2、推论3、欧几里得算法4、多个整数的裴蜀定理扩展 真题挑战解题思路代码实现与详细注释代码解析 裴蜀定理(Bzout’s Theorem) 1、定义 对于任意两个整数 a 和 b ,如果它们的最…...
冯诺依曼架构及CPU相关概念
一. 操作系统的概念 1. 概念 操作系统(Operating System). 首先, 所有的计算机都是由软件和硬件构成的. 而操作系统就是许许多多软件中的一种软件, 操作系统可以看作是由两部分组成: 操作系统内核系统级应用程序. 2. 作用 (1) 管理硬件设备, 调度和协调各个硬件之间的工作.…...
智能管线巡检系统:强化巡检质量,确保安全高效运维
线路巡检质量的监控是确保线路安全、稳定运行的重要环节。为了有效监控巡检质量,采用管线巡检系统是一种高效、科学的手段。以下是对如何通过管线巡检系统实现线路巡检质量监控的详细分析: 一、巡检速度监控 管线巡检系统能够实时监控巡检人员的巡检速度…...
React写关键字高亮的三个方案
1.js正则replaceAlldangerouslySetInnerHTML{{ __html: xxx }}危险属性 步骤最简单,但是是危险属性,不推荐使用,项目中实在没有头绪,可以使用它应急 通过useMemo计算得到新的状态值,赋值给dangerouslySetInnerHTML属性的__html 关键代码: const [state1, setState1] useSt…...
重塑在线软件开发新纪元:集成高效安全特性,深度解析与评估会员与促销管理系统的系统架构设计
案例 阅读以下关于软件架构设计与评估的叙述,回答问题1和问题2。 【题目】 某电子商务公司拟升级其会员与促销管理系统,向用户提供个性化服务,提高用户的粘性。在项目立项之初,公司领导层一致认为本次升级的主要目标是提升会员管…...
多层感知机的从零实现与softmax的从零实现(真·0000零基础)
今天再读zh.d2l书(4.2. 多层感知机的从零开始实现 — 动手学深度学习 2.0.0 documentation), 看了关于多层感知机的从零实现与softmax的从零实现 目录 mlp从零实现, 点击“paddle”的代码 点击“torch”的代码 训练 参数解…...
【Rust练习】18.特征 Trait
练习题来自:https://practice-zh.course.rs/generics-traits/traits.html 1 // 完成两个 impl 语句块 // 不要修改 main 中的代码 trait Hello {fn say_hi(&self) -> String {String::from("hi")}fn say_something(&self) -> String; }str…...
【自动化测试之oracle数据库】MacOs如何安装oracle- client
操作系统为Mac OS,本地在pycharm上跑自动化脚本时,因为有操作oracle数据库的部分,所以需要安装oracle数据库的客户端,并install cx_oracle,本文主要介绍如何在macOS上完成安装,并在python自动化测试代码中配置…...
Spring MVC的MultipartFile
定义 MultipartFile接口是Spring MVC中用来处理上传文件的接口,它提供了访问上传文件内容、文件名称、文件大小等信息的方法。 源码: package org.springframework.web.multipart;import java.io.File; import java.io.IOException; import java.io.I…...
●Leetcode| 242.有效的字母异位词 ● 349. 两个数组的交集 ● 202. 快乐数● 1. 两数之和
242,该题目中数组范围比较短,可以数组使用并不会占太多的空间,利用数组的映射,查找到自己所需要的字符 class Solution { public:bool isAnagram(string s, string t) {int record[26] {0};for(int i0;i<s.size();i){record[s[i] - a];/…...
关于算法的时间复杂度和空间复杂度的分析
由于最近开始准备蓝桥杯(python组),开始对编程基础进行一些复习,当我发现蓝桥对大多数题目程序运行时间及大小有要求时,我知道我不得不考虑性能问题,而不是能跑就行🤓 写下这篇文章希望对其他同志有帮助吧 什么是算法…...
深入浅出 C++ STL:解锁高效编程的秘密武器
引言 C 标准模板库(STL)是现代 C 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知…...
2024年1024程序人生总结
2024-1024 0.大环境0.1.经济0.2.战争 1.我的程序人生1.1.游戏 2.节日祝福 0.大环境 今年的1024最大的感触就是没有节日氛围,往年公司还会准备节日礼物,今年没有,由此可见大环境有多么糟糕。 除此之外,就是到公司应聘的程序员越来…...
【p2p、分布式,区块链笔记 分布式容错算法】: 拜占庭将军问题+实用拜占庭容错算法PBFT
papercodehttps://pmg.csail.mit.edu/papers/osdi99.pdfhttps://github.com/luckydonald/pbft 其他相关实现:This is an implementation of the Pracltical Byzantine Fault Tolerance protocol using PythonAn implementation of the PBFT consensus algorithm us…...
鸿蒙NEXT开发-应用数据持久化之用户首选项(基于最新api12稳定版)
注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下 如果大家觉得博主文章写的好的话,可以点下关注,博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…...
人工智能_神经网络103_感知机_感知机工作原理_感知机具备学习能力_在学习过程中自我调整权重_优化效果_多元线性回归_逻辑回归---人工智能工作笔记0228
由于之前一直对神经网络不是特别清楚,尤其是对神经网络中的一些具体的概念,包括循环,神经网络卷积神经网络以及他们具体的作用,都是应用于什么方向不是特别清楚,所以现在我们来做教程来具体明确一下。 当然在机器学习之后还有深度学习,然后在深度学习中对各种神经网络的…...
WISE:重新思考大语言模型的终身模型编辑与知识记忆机制
论文地址:https://arxiv.org/abs/2405.14768https://arxiv.org/abs/2405.14768 1. 概述 随着世界知识的不断变化,大语言模型(LLMs)需要及时更新,纠正其生成的虚假信息或错误响应。这种持续的知识更新被称为终身模型编…...
网络安全证书介绍
网络安全领域有很多专业的证书,可以帮助你提升知识和技能,增强在这个行业中的竞争力。以下是一些常见的网络安全证书: 1. CompTIA Security 适合人群:初级安全专业人员证书内容:基础的网络安全概念和实践,…...
【已解决】【hadoop】【hive】启动不成功 报错 无法与MySQL服务器建立连接 Hive连接到MetaStore失败 无法进入交互式执行环境
启动hive显示什么才是成功 当你成功启动Hive时,通常会看到一系列的日志信息输出到控制台,这些信息包括了Hive服务初始化的过程以及它与Metastore服务连接的情况等。一旦Hive完成启动并准备就绪,你将看到提示符(如 hive> &#…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
rm视觉学习1-自瞄部分
首先先感谢中南大学的开源,提供了很全面的思路,减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接:https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架: 代码框架结构:readme有…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
python打卡day47
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import D…...
Springboot多数据源配置实践
Springboot多数据源配置实践 基本配置文件数据库配置Mapper包Model包Service包中业务代码Mapper XML文件在某些复杂的业务场景中,我们可能需要使用多个数据库来存储和管理不同类型的数据,而不是仅仅依赖于单一数据库。本技术文档将详细介绍如何在 Spring Boot 项目中进行多数…...
C++信息学竞赛中常用函数的一般用法
在C 信息学竞赛中,有许多常用函数能大幅提升编程效率。下面为你介绍一些常见函数及其一般用法: 一、比较函数 1、max()//求出a,b的较大值 int a10,b5,c;cmax(a,b);//得出的结果就是c等于10. 2、min()//求出a,b的较小值 int a1…...

