当前位置: 首页 > news >正文

【前端Vue】如何在log-viewer组件中添加搜索定位功能

**log-viewer组件合集**

【前端Vue】如何优雅地展示带行号的日志文件或文本内容(log-viewer组件的使用)

【前端Vue】使用log-viewer组件时的踩坑记录

【前端Vue】log-viewer组件的使用技巧

【前端Vue】如何在log-viewer组件中添加搜索定位功能

首先在包含log-viewer组件的渲染标签中加入搜索框和一些操作按钮,支持通过搜索框输入和清空搜索内容,支持点击操作按钮上下定位搜索结果,并且可以显示搜索结果数量和当前所处结果的序号。输入框增加了clearable就能够直接在输入框中使用内置的点击叉叉图标清空内容

<!-- 搜索控件 --><div class="log-search-container"><el-inputref="searchInput"v-model="searchText"placeholder="输入搜索关键词"size="mini"class="log-search-input"@keyup.enter.native="searchNext"@input="onSearchInput"clearable></el-input><div class="search-info" v-if="searchText">{{ searchCurrentIndex + 1 }} / {{ searchResults.length }}</div><el-button-group><el-buttonv-if="searchText && searchResults.length > 0"size="mini"icon="el-icon-arrow-up"@click="searchPrev"></el-button><el-buttonicon="el-icon-arrow-down"v-if="searchText && searchResults.length > 0"size="mini"@click="searchNext"></el-button></el-button-group></div></div><!-- 日志显示组件 --><log-viewerref="logViewerContainer"class="log-content":key="logviewViewerKey":log="searchText ? highlightedLogContent : logContent":loading="isFetchingLogs"/>

要在data()中定义几个参数用于搜索控件的使用:

searchText: '',searchResults: [],searchCurrentIndex: -1,searchOccurrences: [], // 存储搜索结果位置

随后定义匹配的搜索相关方法,搜索时会将搜索结果呈现黄色,点击操作按钮进行上下定位,定位到的结果显示为绿色   

// 搜索相关方法onSearchInput() {if (this.searchText) {this.performSearch();} else {this.clearSearch();}},performSearch() {const content = this.logContent;if (!content || !this.searchText) {this.searchResults = [];this.searchCurrentIndex = -1;return;}// 查找所有匹配项const regex = new RegExp(this.escapeRegExp(this.searchText), 'gi');const matches = [];let match;while ((match = regex.exec(content)) !== null) {matches.push({index: match.index,text: match[0]});}this.searchResults = matches;if (matches.length > 0) {this.searchCurrentIndex = 0;this.scrollToSearchResult(0);} else {this.searchCurrentIndex = -1;}},searchNext() {if (this.searchResults.length === 0) return;this.searchCurrentIndex = (this.searchCurrentIndex + 1) % this.searchResults.length;this.scrollToSearchResult(this.searchCurrentIndex);},searchPrev() {if (this.searchResults.length === 0) return;this.searchCurrentIndex = (this.searchCurrentIndex - 1 + this.searchResults.length) % this.searchResults.length;this.scrollToSearchResult(this.searchCurrentIndex);},scrollToSearchResult(index) {if (this.searchResults.length === 0 || index < 0) return;// 更新当前索引以触发高亮更新this.searchCurrentIndex = index;// 获取当前显示的 log-viewer 组件const viewer = this.$refs.logViewerContainer;if (viewer && viewer.$refs && viewer.$refs.virturalList) {// 获取虚拟列表组件const virtualList = viewer.$refs.virturalList;// 获取内容并按行分割const content = this.logContent;const lines = content.split('\n');// 找到匹配项所在的行const matchPosition = this.searchResults[index].index;let lineNumber = 0;let charCount = 0;for (let i = 0; i < lines.length; i++) {const lineLength = lines[i].length + 1; // +1 for newline characterif (charCount + lineLength > matchPosition) {lineNumber = i;break;}charCount += lineLength;}// 滚动到指定行(添加偏移量使内容居中)this.$nextTick(() => {// 使用 start 属性设置滚动位置// 虚拟列表通过 start 属性控制显示的起始行if (virtualList && typeof virtualList.scrollToIndex === 'function') {// 直接滚动到目标行,不减去偏移量virtualList.scrollToIndex(lineNumber);} else if (virtualList && typeof virtualList.$el.scrollTo === 'function') {// 备选方案:使用原生滚动方法,修正计算方式const lineHeight = 20; // 假设每行高度为20px// 确保滚动到正确位置,减去一些像素使目标行在视图中央const targetScrollTop = Math.max(0, lineNumber * lineHeight - (virtualList.$el.clientHeight / 2));virtualList.$el.scrollTo({ top: targetScrollTop, behavior: 'smooth' });}});} else {// 如果无法直接控制滚动,至少显示提示信息this.$message.info(`已定位到第 ${index + 1} 个匹配项,共找到 ${this.searchResults.length} 个匹配项`);}},clearSearch() {this.searchText = '';this.searchResults = [];this.searchCurrentIndex = -1;},// 使用 ANSI 转义序列添加高亮(log-viewer组件只支持ANSI 转义)highlightText(content, searchText, currentIndex = -1) {if (!searchText) return content;// 分割成行以便处理const lines = content.split('\n');let globalIndex = 0; // 全局匹配索引// 使用正则匹配搜索词const regex = new RegExp(`(${this.escapeRegExp(searchText)})`, 'gi');const highlightedLines = lines.map(line => {return line.replace(regex, (match, ...args) => {const currentGlobalIndex = globalIndex;globalIndex++;// 如果是当前选中的搜索结果,使用不同的颜色if (currentGlobalIndex === currentIndex) {return `\x1b[42m${match}\x1b[0m`; // 当前结果使用绿色背景} else {return `\x1b[103m${match}\x1b[0m`; // 其余使用亮黄色背景}});});return highlightedLines.join('\n');},escapeRegExp(string) {return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');}},
};

随后在computed计算属性中增加对高亮文字的处理,将搜索框的内容状态与内容显示通过计算属性互相绑定起来

// 高亮显示的日志内容highlightedLogContent() {if (!this.searchText || this.searchResults.length === 0) {return this.logContent;}return this.highlightText(this.logContent, this.searchText,  this.searchCurrentIndex);},

http://www.dtcms.com/a/328353.html

相关文章:

  • 基于柔性管控终端的新能源汽车充电站有序充电系统设计与实现
  • PiaRef代码库结构梳理
  • Java 大视界 -- Java 大数据在智能教育学习效果评估指标体系构建与精准评估中的应用(394)
  • 关于Linux编程:fread/fwrite/流的定位/文件IO
  • C++右值引用学习笔记
  • wordpress获取菜单项的ID并输出
  • FPGA+护理:跨学科发展的探索(三)
  • 解锁大模型调优新姿势:LoRA微调全解析
  • 【算法】堆(Heap)的概念、懒删除(Lazy Deletion)与堆排序(Heap Sort)
  • 【实时Linux实战系列】复杂实时系统中的需求管理
  • Qt树形控件QTreeWidget详解:构建可编辑的多级课程结构
  • Nacos-2--Nacos1.x版本的通信原理
  • Web基础
  • 力扣47:全排列Ⅱ
  • 【8】Transformers快速入门:Decoder 分支和统计语言模型区别?
  • 图解软件系统组成
  • C#控制台项目,鼠标点击后线程会暂停
  • 什么是静态独享代理IP?如何选择可靠的静态独享代理IP供应商?
  • OAuth 2.0 授权码模式:安全架构解析
  • 龙虎榜——20250812
  • Java 8 Stream API 完全指南:优雅处理集合数据
  • 问卷系统测试报告
  • Unity UnityWebRequest常用操作
  • 从100到0.3美元:GPT-5用价格战血洗大模型赛道
  • 达梦数据闪回查询-快速恢复表
  • string 类元素访问方法
  • 《嵌入式Linux应用编程(四):Linux文件IO系统调用深度解析》
  • Origin2025b安装包免费,附Origin 2025安装教程
  • 智能机器人学习:智能机器人环境感知传感器介绍
  • 一个基于 PyTorch 的完整模型训练流程