Vue3 CodeMirror组件实现日志查看与模糊匹配自定义查询高亮功能

Vue3集成CodeMirror实现日志高亮查询

需求:展示日志,将每一行日志文本加上序号标识,展示为类似编辑器代码高亮文本,顶部为文本过滤搜索输入框与刷新日志按钮

实现效果图:

输入框可以输入模糊字符模糊匹配高亮,右侧有个刷新按钮实现日志刷新

思路:

1、基于vue3引入vue-codemirror及相关依赖

vue-codemirror 版本:"vue-codemirror": "^6.1.1",

"@codemirror/commands": "^6.8.1",

    "@codemirror/lang-javascript": "^6.2.4",

    "@codemirror/search": "^6.5.11",

    "@codemirror/state": "^6.5.2",

    "@codemirror/view": "^6.38.1",

2、引入代码

3、调用插件内部search实例方法实现搜索功能

4、整体代码如下(接口获取根据接口加入调整getLog方法,后端接口直接返回日志就行,codemirror会自动把日志展示为带序号并且格式化展示):

<template>
<div class="log-content">
      <div class="top-content">
        <a-input-search
          v-model="filterValue"
          placeholder="请输入搜索内容"
          allow-clear
          style="width: 200px"
          @search="filterLog"
          @change="filterLog"
        />
        <div class="refresh-btn" @click="refreshLog">
          <icon-refresh />
        </div>
      </div>
      <div class="inner-content">
        <a-spin
          v-if="detailLoading"
          class="spin-center"
          :size="32"
          :tip="'加载中...'"
        ></a-spin>
        <Codemirror
          v-show="!detailLoading"
          v-model="logContent"
          :extensions="extensions"
          :style="{ height: '100%' }"
          readOnly="nocursor"
          :placeholder="'日志内容加载中...'"
          @ready="handleReady"
          ref="cmEditor"
        />
      </div>
    </div>
</template>

<script setup>
import { Codemirror } from 'vue-codemirror'
import { javascript } from '@codemirror/lang-javascript'
import { EditorView } from '@codemirror/view'
import { EditorSelection } from '@codemirror/state'
import {SearchQuery, setSearchQuery } from '@codemirror/search'
import { search, highlightSelectionMatches } from '@codemirror/search'
import { ref, watch, onMounted, shallowRef, computed } from 'vue'
import { Message } from '@arco-design/web-vue'

const view = shallowRef()
const handleReady = (payload) => {
  view.value = payload.view
}

let myTheme = EditorView.theme(
  {
    '&': {
      height: '100%',
      width: '100%',
    },
    '.cm-content': {
      fontSize: '14px',
      lineHeight: '1.5',
    },
    '.cm-selectionMatch': {
      backgroundColor: '#ffdd33',
    },
  },
  { dark: false },
)

const extensions = computed(() => {
  return [
    javascript(),
    myTheme,
    highlightSelectionMatches(),
    search({
      createPanel: () => null, // 禁用默认搜索面板
    }),
  ]
})

const filterValue = ref('')
const detailLoading = ref(false)
const cmEditor = ref(null)
const logContent = ref('')
// 从接口获取渲染的日志
const getLog = () => {
  detailLoading.value = true
  // 将从接口返回的数据返回logContent渲染日志
  let data = 'test'
  logContent.value = data || ''
      // 滚动到顶部
      if (view.value) {
        view.value.dispatch({
          effects: EditorView.scrollIntoView(0, { y: 'start' }),
        })
      }
}
// 首次进入调用
getLog()
// 搜索并高亮文本
const filterLog = () => {
  if (!view.value) {
    Message.warning('编辑器未准备好,请稍后再试')
    return
  }
  const searchValue = filterValue.value.trim()
  if (!searchValue) {
    // 清空搜索框内容和日志高亮文本样式
    filterValue.value = ''
    view.value.dispatch({
      effects: setSearchQuery.of(new SearchQuery({ search: '' })),
    })
    view.value.dispatch({
      selection: EditorSelection.single(0), // 重置选择到文档开头
    })
    return
  }

  // 设置搜索查询
  const query = new SearchQuery({
    search: searchValue,
    caseSensitive: false,
    regexp: false,
  })

  view.value.dispatch({
    effects: setSearchQuery.of(query),
  })

  // 定位到第一个匹配项
  const cursor = query.getCursor(view.value.state)
  if (cursor.next().done) {
    Message.warning('未找到匹配的日志内容')
  } else {
    view.value.dispatch({
      selection: EditorSelection.single(cursor.value.from, cursor.value.to),
      effects: EditorView.scrollIntoView(cursor.value.from, { y: 'start' }),
    })
  }
}

// 刷新日志
const refreshLog = () => {
  filterValue.value = '' // 清空过滤条件
  logContent.value = ''
  getLog()
}
</script>

<style lang="less" scoped>
.log-content {
  height: 70vh;
  display: flex;
  flex-direction: column;
}
.top-content {
  display: flex;
  justify-content: space-between;
  margin-bottom: 17px;
  .refresh-btn {
    width: 32px;
    height: 32px;
    line-height: 32px;
    font-size: 18px;
    text-align: center;
    cursor: pointer;
    background: #f2f3f5;
    border-radius: 2px;
  }
}
.inner-content {
  border: 1px solid #ebebeb;
  flex: 1;
  overflow: auto;
  min-height: 0;
  position: relative;
}

.spin-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;

  :deep(.arco-spin) {
    display: flex;
    align-items: center;
    justify-content: center;
  }
}
</style>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值