当前位置: 首页 > 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.lryc.cn/news/619185.html

相关文章:

  • C语言中关于普通变量和指针变量、结构体包含子结构体或包含结构体指针的一些思考
  • 调整UOS在VMware中的分辨率
  • 广东省省考备考(第七十四天8.12)——资料分析、数量关系(40%-70%正确率的题目)
  • MySQL 数据库表操作与查询实战案例
  • 猫头虎AI分享|智谱直播开源其最新视觉模型:GLM-4.5V,多模态,支持图像、视频输入
  • 一个删掉360安全卫士的方法——Win+R
  • 【代码随想录day 17】 力扣 98.验证二叉搜索树
  • 计算机视觉(6)-自动驾驶感知方案对比
  • 偶遇冰狐智能辅助的录音
  • 【oracle闪回查询】记录字段短时间被修改的记录
  • 【Allegro SKILL代码解析】添加Pin Number
  • Web 安全之互联网暴露面管理
  • 零售业CRM实战:如何打通线上线下客户数据?
  • word——照片自适应框大小【主要针对需要插入证件照时使用】
  • 亚马逊优惠券视觉体系重构:颜色标签驱动的消费决策效率革命
  • DAY38打卡
  • CTO 如何从“干活的人”转变成“带方向的人”?
  • Spring Boot项目通过RestTemplate调用三方接口详细教程
  • 带宽受限信道下的数据传输速率计算:有噪声与无噪声场景
  • mysql锁+索引
  • 自然语言处理关键库解析和使用方法- FuzzyWuzzy
  • 【3】Transformers快速入门:大语言模型LLM是啥?
  • 【4】Transformers快速入门:自然语言模型 vs 统计语言模型
  • GaussDB 数据库架构师修炼(十三)安全管理(2)-数据库权限管理
  • 如何构建PHP表单页面及验证相关原理(PHP基础)
  • 前后端分离项目中Spring MVC的请求执行流程
  • Kubernetes 资源管理全解析:从基础到企业级实践
  • TDengine 可观测性最佳实践
  • VBS 时间函数
  • 移动端网页调试实战,键盘弹出与视口错位问题的定位与优化