| | |
| | | <div class="mian_log"> |
| | | <div class="log_tree"> |
| | | <div class="log_search"> |
| | | <el-input v-model.lazy="filterText" placeholder="请è¾å
¥æ¥è¯¢å
³é®å" clearable /> |
| | | <el-input v-model.lazy="filterText" placeholder="请è¾å
¥ç®å½åç§°" clearable /> |
| | | <el-button type="success" @click="ReloadTree()">å· æ°</el-button> |
| | | </div> |
| | | <el-tree class="log_el_tree" ref="treeRef" :data="treeData" :props="treeProps" :filter-node-method="treeFilter"> |
| | | <!-- æ·»å æ èç¹æç´¢ç»ææç¤º --> |
| | | <div class="search-tip" v-if="filterText && filteredCount !== null"> |
| | | æ¾å° {{ filteredCount }} 个å¹é
çç®å½ |
| | | </div> |
| | | <el-tree class="log_el_tree" ref="treeRef" :data="treeData" :props="treeProps" |
| | | :filter-node-method="treeFilter" node-key="dirPath" highlight-current> |
| | | <template #default="{ node, data }"> |
| | | <div class="treeItem"> |
| | | <div>{{ node.label }}</div> |
| | | <!-- é«äº®å¹é
çå
³é®å --> |
| | | <div v-html="highlightText(node.label)"></div> |
| | | <div style="padding-left: 10px;" v-if="isFile(data)"> |
| | | <a href="javascript:;" @click="GetContent(data)"> æå¼ </a> |
| | | <!-- ||<a href="javascript:;" @click="DonwLog(data)"> ä¸è½½ </a> --> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | <el-checkbox v-model="keepRead">ä¿æè¯»å</el-checkbox> |
| | | </div> |
| | | |
| | | <!-- æ·»å å
容æç´¢æ --> |
| | | <div class="content-search" v-if="content"> |
| | | <el-input v-model="contentSearchText" placeholder="卿¥å¿ä¸æç´¢..." size="small" clearable |
| | | @keyup.enter="searchInContent" /> |
| | | <el-button type="primary" size="small" @click="searchInContent">æç´¢</el-button> |
| | | <span v-if="searchResults.length > 0" class="search-info"> |
| | | {{ currentMatchIndex + 1 }}/{{ searchResults.length }} |
| | | </span> |
| | | <el-button v-if="searchResults.length > 0" type="info" size="small" |
| | | @click="prevMatch">ä¸ä¸å¤</el-button> |
| | | <el-button v-if="searchResults.length > 0" type="info" size="small" |
| | | @click="nextMatch">ä¸ä¸å¤</el-button> |
| | | </div> |
| | | |
| | | <div ref="pre" v-if="content"> |
| | | <el-progress :percentage="percentage" @click="ChangePercent($event)" color="#f56c6c" /> |
| | | </div> |
| | | <div ref="showdata" @scroll="handleScroll($event)" style="white-space: pre-wrap;word-break: break-all">{{ |
| | | content }}</div> |
| | | <!-- ä¿®æ¹å
容æ¾ç¤ºåºåï¼æ¯æé«äº® --> |
| | | <div ref="showdata" @scroll="handleScroll($event)" class="content-display" |
| | | v-html="highlightContent"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | content: "", |
| | | isClick: false, |
| | | |
| | | keepReadTimer: null |
| | | keepReadTimer: null, |
| | | |
| | | // æ°å¢æç´¢ç¸å
³å±æ§ |
| | | contentSearchText: "", |
| | | searchResults: [], |
| | | currentMatchIndex: -1, |
| | | searchHighlights: [], |
| | | filteredCount: null, |
| | | originalContent: "", // ä¿ååå§å
容ç¨äºæç´¢ |
| | | } |
| | | }, |
| | | watch: { |
| | | filterText(val) { |
| | | this.$refs.treeRef.filter(val) |
| | | this.updateFilteredCount() |
| | | }, |
| | | keepRead(val) { |
| | | if (val) { |
| | |
| | | this.percentage = 100; |
| | | this.http.post('/api/Sys_Log/GetLogData', parm).then((res) => { |
| | | this.content = res.data.content; |
| | | this.originalContent = res.data.content; |
| | | this.topStartPos = res.data.startIndex; |
| | | this.$nextTick(function () { |
| | | this.$refs.showdata.scrollTo(0, this.$refs.showdata.scrollHeight) |
| | | // éæ°åºç¨æç´¢é«äº® |
| | | if (this.contentSearchText) { |
| | | this.searchInContent(); |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | |
| | | } else { |
| | | clearInterval(this.keepReadTimer); |
| | | } |
| | | }, |
| | | content(newVal) { |
| | | this.originalContent = newVal; |
| | | if (this.contentSearchText) { |
| | | this.searchInContent(); |
| | | } else { |
| | | this.clearHighlights(); |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | // 计ç®é«äº®åçå
容 |
| | | highlightContent() { |
| | | if (!this.content || !this.contentSearchText || this.searchResults.length === 0) { |
| | | return this.content; |
| | | } |
| | | |
| | | let highlightedContent = this.content; |
| | | const searchText = this.contentSearchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
| | | const regex = new RegExp(`(${searchText})`, 'gi'); |
| | | |
| | | // 使ç¨å
¨å±æ¿æ¢æ·»å é«äº®æ è®° |
| | | highlightedContent = highlightedContent.replace(regex, (match, p1, offset) => { |
| | | // æ£æ¥æ¯å¦å½åå¹é
项 |
| | | const isCurrentMatch = this.searchResults.some((result, index) => |
| | | result.index === offset && index === this.currentMatchIndex |
| | | ); |
| | | |
| | | if (isCurrentMatch) { |
| | | return `<span class="highlight-current">${match}</span>`; |
| | | } else { |
| | | return `<span class="highlight">${match}</span>`; |
| | | } |
| | | }); |
| | | |
| | | return highlightedContent; |
| | | } |
| | | }, |
| | | methods: { |
| | | // æ èç¹è¿æ»¤æ¹æ³ |
| | | treeFilter(value, data) { |
| | | if (!value) return true |
| | | return data.dirName.includes(value) |
| | | return data.dirName.toLowerCase().includes(value.toLowerCase()) |
| | | }, |
| | | |
| | | // æ´æ°è¿æ»¤è®¡æ° |
| | | updateFilteredCount() { |
| | | if (!this.filterText) { |
| | | this.filteredCount = null; |
| | | return; |
| | | } |
| | | |
| | | // éå½è®¡ç®å¹é
çèç¹æ° |
| | | const countMatches = (nodes) => { |
| | | let count = 0; |
| | | for (const node of nodes) { |
| | | if (node.dirName.toLowerCase().includes(this.filterText.toLowerCase())) { |
| | | count++; |
| | | } |
| | | if (node.dirs && node.dirs.length) { |
| | | count += countMatches(node.dirs); |
| | | } |
| | | } |
| | | return count; |
| | | }; |
| | | |
| | | this.filteredCount = countMatches(this.treeData); |
| | | }, |
| | | |
| | | // é«äº®æ èç¹ä¸çææ¬ |
| | | highlightText(text) { |
| | | if (!this.filterText || !text) return text; |
| | | |
| | | const index = text.toLowerCase().indexOf(this.filterText.toLowerCase()); |
| | | if (index === -1) return text; |
| | | |
| | | const before = text.substring(0, index); |
| | | const match = text.substring(index, index + this.filterText.length); |
| | | const after = text.substring(index + this.filterText.length); |
| | | |
| | | return `${before}<span class="highlight">${match}</span>${after}`; |
| | | }, |
| | | |
| | | // å¨å
å®¹ä¸æç´¢ |
| | | searchInContent() { |
| | | if (!this.contentSearchText || !this.content) { |
| | | this.clearHighlights(); |
| | | return; |
| | | } |
| | | |
| | | const searchText = this.contentSearchText.toLowerCase(); |
| | | const content = this.content; |
| | | |
| | | // æ¥æ¾ææå¹é
项çä½ç½® |
| | | const results = []; |
| | | let index = content.toLowerCase().indexOf(searchText); |
| | | |
| | | while (index !== -1) { |
| | | results.push({ |
| | | index: index, |
| | | length: this.contentSearchText.length |
| | | }); |
| | | index = content.toLowerCase().indexOf(searchText, index + 1); |
| | | } |
| | | |
| | | this.searchResults = results; |
| | | |
| | | if (results.length > 0) { |
| | | this.currentMatchIndex = 0; |
| | | this.scrollToMatch(this.currentMatchIndex); |
| | | } else { |
| | | this.currentMatchIndex = -1; |
| | | this.$message.info('æªæ¾å°å¹é
å
容'); |
| | | } |
| | | }, |
| | | |
| | | // æ»å¨å°æå®å¹é
项 |
| | | scrollToMatch(matchIndex) { |
| | | if (matchIndex < 0 || matchIndex >= this.searchResults.length) return; |
| | | |
| | | const match = this.searchResults[matchIndex]; |
| | | const preContent = this.content.substring(0, match.index); |
| | | const lines = preContent.split('\n'); |
| | | |
| | | // 估计æ»å¨ä½ç½®ï¼ç®åçï¼ |
| | | const lineHeight = 20; // å设æ¯è¡é«åº¦ |
| | | const estimatedScrollTop = lines.length * lineHeight; |
| | | |
| | | this.$nextTick(() => { |
| | | this.$refs.showdata.scrollTop = estimatedScrollTop - 100; |
| | | }); |
| | | }, |
| | | |
| | | // ä¸ä¸å¤å¹é
|
| | | prevMatch() { |
| | | if (this.searchResults.length === 0) return; |
| | | this.currentMatchIndex = (this.currentMatchIndex - 1 + this.searchResults.length) % this.searchResults.length; |
| | | this.scrollToMatch(this.currentMatchIndex); |
| | | }, |
| | | |
| | | // ä¸ä¸å¤å¹é
|
| | | nextMatch() { |
| | | if (this.searchResults.length === 0) return; |
| | | this.currentMatchIndex = (this.currentMatchIndex + 1) % this.searchResults.length; |
| | | this.scrollToMatch(this.currentMatchIndex); |
| | | }, |
| | | |
| | | // æ¸
é¤é«äº® |
| | | clearHighlights() { |
| | | this.searchResults = []; |
| | | this.currentMatchIndex = -1; |
| | | }, |
| | | |
| | | isFile(data) { |
| | | if (data.dirPath) { |
| | | return data.dirPath.indexOf(".log") != -1 || data.dirPath.indexOf(".txt") != -1; |
| | |
| | | this.http.post('/api/Sys_Log/GetLogData', parm).then((res) => { |
| | | this.isClick = true; |
| | | this.content = res.data.content; |
| | | this.originalContent = res.data.content; |
| | | this.topStartPos = res.data.startIndex; |
| | | this.$nextTick(function () { |
| | | if (percent == 100) { |
| | |
| | | this.filePath = data.dirPath; |
| | | this.fileName = data.dirName; |
| | | this.percentage = parm.percent; |
| | | this.contentSearchText = ""; // æ¸
空æç´¢ |
| | | this.clearHighlights(); |
| | | |
| | | this.http.post('/api/Sys_Log/GetLogData', parm).then((res) => { |
| | | this.content = res.data.content; |
| | | this.originalContent = res.data.content; |
| | | this.topStartPos = res.data.startIndex; |
| | | this.fileSize = res.data.len; |
| | | this.$nextTick(function () { |
| | |
| | | this.percentage = 0.1; |
| | | } |
| | | this.content = res.data.content + this.content; |
| | | this.originalContent = res.data.content + this.originalContent; |
| | | this.$nextTick(function () { |
| | | this.$refs.showdata.scrollTo(0, 1) |
| | | // éæ°æç´¢ |
| | | if (this.contentSearchText) { |
| | | this.searchInContent(); |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | |
| | | this.http.post("/api/Sys_Log/GetLogList", {}, "å è½½ç®å½ä¸â¦â¦") |
| | | .then(res => { |
| | | this.treeData = res.data.dirs; |
| | | this.updateFilteredCount(); |
| | | }) |
| | | }, |
| | | GetSize(size) { |
| | |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | .search-tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | padding: 5px 0; |
| | | } |
| | | |
| | | .log_el_tree { |
| | | width: 100%; |
| | | overflow-x: auto; |
| | | flex: 1; |
| | | } |
| | | |
| | | |
| | | .treeItem { |
| | | overflow-x: auto; |
| | |
| | | } |
| | | |
| | | .treeItem::-webkit-scrollbar-thumb { |
| | | /* å®ä¹æ»å¨æ¡æ»åé¢è² */ |
| | | background: #ccc; |
| | | } |
| | | |
| | | .treeItem::-webkit-scrollbar-thumb:hover { |
| | | /* å®ä¹æ»å¨æ¡æ»åæ¬åé¢è² */ |
| | | background: orange; |
| | | } |
| | | |
| | |
| | | color: #bbb; |
| | | } |
| | | |
| | | .log_content>div:last-child { |
| | | .content-search { |
| | | display: flex; |
| | | gap: 5px; |
| | | align-items: center; |
| | | padding: 10px 0; |
| | | border-bottom: 1px solid #eee; |
| | | } |
| | | |
| | | .search-info { |
| | | font-size: 12px; |
| | | color: #666; |
| | | margin: 0 5px; |
| | | } |
| | | |
| | | .content-display { |
| | | flex: 1; |
| | | overflow-y: scroll; |
| | | margin-top: 5px; |
| | | padding-top: 5px; |
| | | border-top: 1px solid black; |
| | | white-space: pre-wrap; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | a { |
| | | color: blue; |
| | | } |
| | | </style> |
| | | |
| | | :deep(.highlight) { |
| | | background-color: #ffeb3b; |
| | | color: #000; |
| | | padding: 0; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | :deep(.highlight-current) { |
| | | background-color: #ff9800; |
| | | color: #000; |
| | | padding: 0; |
| | | border-radius: 2px; |
| | | font-weight: bold; |
| | | } |
| | | </style> |