mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-28 17:50:56 +01:00
优化日志,错误标记
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="log-item" :style="style">
|
||||
<div class="log-item" :style="style" :class="`log-item-${index} ${active ? 'active' : ''}`">
|
||||
<div class="line-no">{{index}}</div>
|
||||
<div class="line-content">
|
||||
<span v-if="isLogEnd" style="color: #E6A23C" class="loading-text">
|
||||
{{$t('Updating log...')}}
|
||||
<span v-if="isLogEnd" style="color: #E6A23C">
|
||||
<span class="loading-text">{{$t('Updating log...')}}</span>
|
||||
<i class="el-icon-loading"></i>
|
||||
</span>
|
||||
<span v-else-if="isAnsi" v-html="dataHtml"></span>
|
||||
@@ -31,13 +31,15 @@ export default {
|
||||
searchString: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const token = ' :,.'
|
||||
return {
|
||||
errorRegex: new RegExp(`(?:[${token}]|^)((?:error|exception|traceback)s?)(?:[${token}]|$)`, 'gi')
|
||||
// errorRegex: new RegExp('(error|exception|traceback)', 'gi')
|
||||
errorRegex: this.$utils.log.errorRegex
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -65,42 +67,56 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.log-item {
|
||||
display: table;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.log-item:hover {
|
||||
background: rgba(55, 57, 59, 0.5);
|
||||
}
|
||||
|
||||
.log-item:first-child .line-no {
|
||||
padding-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.log-item .line-no {
|
||||
display: table-cell;
|
||||
display: inline-block;
|
||||
width: 70px;
|
||||
color: #A9B7C6;
|
||||
background: #313335;
|
||||
padding-right: 10px;
|
||||
text-align: right;
|
||||
flex-basis: 40px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.log-item.active .line-no {
|
||||
background: #E6A23C;
|
||||
color: white;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.log-item .line-content {
|
||||
padding-left: 10px;
|
||||
display: table-cell;
|
||||
/*display: inline-block;*/
|
||||
word-break: break-word;
|
||||
flex-basis: calc(100% - 50px);
|
||||
display: inline-block;
|
||||
width: calc(100% - 70px);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
animation: blink;
|
||||
margin-right: 5px;
|
||||
animation: blink 2s ease-in infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% {
|
||||
opacity: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 100%;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,43 +1,90 @@
|
||||
<template>
|
||||
<div class="log-view-wrapper">
|
||||
<div class="log-view-container">
|
||||
<div class="filter-wrapper">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
style="margin-right: 10px"
|
||||
:disabled="isToBottom"
|
||||
@click="onAutoScroll"
|
||||
>
|
||||
{{$t('Auto-Scroll')}}
|
||||
</el-button>
|
||||
<el-input
|
||||
v-model="searchString"
|
||||
size="small"
|
||||
suffix-icon="el-icon-search"
|
||||
:placeholder="$t('Search Log')"
|
||||
style="width: 240px"
|
||||
/>
|
||||
<div class="left">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
style="margin-right: 10px"
|
||||
:disabled="isToBottom"
|
||||
@click="onAutoScroll"
|
||||
>
|
||||
{{$t('Auto-Scroll')}}
|
||||
</el-button>
|
||||
<el-input
|
||||
v-model="searchString"
|
||||
size="small"
|
||||
suffix-icon="el-icon-search"
|
||||
:placeholder="$t('Search Log')"
|
||||
style="width: 240px; margin-right: 10px"
|
||||
/>
|
||||
</div>
|
||||
<div class="right">
|
||||
<el-badge
|
||||
v-if="errorLogData.length > 0"
|
||||
:value="errorLogData.length"
|
||||
>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
icon="el-icon-warning-outline"
|
||||
@click="toggleErrors"
|
||||
>
|
||||
{{$t('Error Count')}}
|
||||
</el-button>
|
||||
</el-badge>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-view-wrapper" ref="log-view-wrapper">
|
||||
<virtual-list
|
||||
class="log-view"
|
||||
ref="log-view"
|
||||
:size="6"
|
||||
:remain="100"
|
||||
:item="item"
|
||||
:itemcount="filteredLogData.length"
|
||||
:itemprops="getItemProps"
|
||||
:tobottom="onToBottom"
|
||||
:onscroll="onScroll"
|
||||
/>
|
||||
<div class="content">
|
||||
<div
|
||||
class="log-view-wrapper"
|
||||
:class="isErrorsCollapsed ? 'errors-collapsed' : ''"
|
||||
>
|
||||
<virtual-list
|
||||
class="log-view"
|
||||
ref="log-view"
|
||||
:start="currentLogIndex - 1"
|
||||
:offset="0"
|
||||
:size="18"
|
||||
:remain="remainSize"
|
||||
:item="item"
|
||||
:itemcount="filteredLogData.length"
|
||||
:itemprops="getItemProps"
|
||||
:tobottom="onToBottom"
|
||||
:onscroll="onScroll"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-show="!isErrorsCollapsed && !isErrorCollapsing"
|
||||
class="errors-wrapper"
|
||||
:class="isErrorsCollapsed ? 'collapsed' : ''"
|
||||
>
|
||||
<ul class="error-list">
|
||||
<li
|
||||
v-for="item in errorLogData"
|
||||
:key="item.index"
|
||||
class="error-item"
|
||||
:class="currentLogIndex === item.index ? 'active' : ''"
|
||||
@click="onClickError(item)"
|
||||
>
|
||||
<span class="line-no">
|
||||
{{item.index}}
|
||||
</span>
|
||||
<span class="line-content">
|
||||
{{item.data}}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
mapState
|
||||
mapState,
|
||||
mapGetters
|
||||
} from 'vuex'
|
||||
import VirtualList from 'vue-virtual-scroll-list'
|
||||
import Convert from 'ansi-to-html'
|
||||
@@ -63,38 +110,38 @@ export default {
|
||||
searchString: '',
|
||||
isToBottom: false,
|
||||
isScrolling: false,
|
||||
isScrolling2nd: false
|
||||
isScrolling2nd: false,
|
||||
errorRegex: this.$utils.log.errorRegex,
|
||||
currentOffset: 0,
|
||||
isErrorsCollapsed: true,
|
||||
isErrorCollapsing: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('task', [
|
||||
'taskForm'
|
||||
]),
|
||||
logData () {
|
||||
const data = this.data.split('\n')
|
||||
.map((d, i) => {
|
||||
return {
|
||||
index: i + 1,
|
||||
data: d
|
||||
}
|
||||
})
|
||||
if (this.taskForm && this.taskForm.status === 'running') {
|
||||
data.push({
|
||||
index: data.length + 1,
|
||||
data: '###LOG_END###'
|
||||
})
|
||||
data.push({
|
||||
index: data.length + 2,
|
||||
data: ''
|
||||
})
|
||||
...mapGetters('task', [
|
||||
'logData',
|
||||
'errorLogData'
|
||||
]),
|
||||
currentLogIndex: {
|
||||
get () {
|
||||
return this.$store.state.task.currentLogIndex
|
||||
},
|
||||
set (value) {
|
||||
this.$store.commit('task/SET_CURRENT_LOG_INDEX', value)
|
||||
}
|
||||
return data
|
||||
},
|
||||
filteredLogData () {
|
||||
return this.logData.filter(d => {
|
||||
if (!this.searchString) return true
|
||||
return !!d.data.toLowerCase().match(this.searchString.toLowerCase())
|
||||
})
|
||||
},
|
||||
remainSize () {
|
||||
const height = document.querySelector('body').clientHeight
|
||||
return (height - 240) / 18
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -113,6 +160,7 @@ export default {
|
||||
index: logItem.index,
|
||||
data: isAnsi ? convert.toHtml(logItem.data) : logItem.data,
|
||||
searchString: this.searchString,
|
||||
active: logItem.active,
|
||||
isAnsi
|
||||
}
|
||||
}
|
||||
@@ -144,6 +192,23 @@ export default {
|
||||
},
|
||||
onAutoScroll () {
|
||||
this.toBottom()
|
||||
},
|
||||
toggleErrors () {
|
||||
this.isErrorsCollapsed = !this.isErrorsCollapsed
|
||||
this.isErrorCollapsing = true
|
||||
setTimeout(() => {
|
||||
this.isErrorCollapsing = false
|
||||
}, 300)
|
||||
},
|
||||
onClickError (item) {
|
||||
this.currentLogIndex = item.index
|
||||
this.isToBottom = false
|
||||
const handle = setInterval(() => {
|
||||
this.isToBottom = false
|
||||
}, 10)
|
||||
setTimeout(() => {
|
||||
clearInterval(handle)
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -151,7 +216,7 @@ export default {
|
||||
if (this.isToBottom) {
|
||||
this.toBottom()
|
||||
}
|
||||
}, 100)
|
||||
}, 500)
|
||||
},
|
||||
destroyed () {
|
||||
clearInterval(this.handle)
|
||||
@@ -160,15 +225,94 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.filter-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.log-view-wrapper {
|
||||
float: left;
|
||||
flex-basis: calc(100% - 240px);
|
||||
width: calc(100% - 300px);
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
.log-view-wrapper.errors-collapsed {
|
||||
flex-basis: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.log-view {
|
||||
margin-top: 0 !important;
|
||||
overflow-y: scroll !important;
|
||||
list-style: none;
|
||||
color: #A9B7C6;
|
||||
background: #2B2B2B;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter-wrapper {
|
||||
margin-bottom: 10px;
|
||||
.errors-wrapper {
|
||||
float: left;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-basis: 240px;
|
||||
width: 300px;
|
||||
transition: opacity 0.3s;
|
||||
border-top: 1px solid #DCDFE6;
|
||||
border-right: 1px solid #DCDFE6;
|
||||
border-bottom: 1px solid #DCDFE6;
|
||||
height: calc(100vh - 240px);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.errors-wrapper.collapsed {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.errors-wrapper .error-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.errors-wrapper .error-list .error-item {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
/*height: 18px;*/
|
||||
border-bottom: 1px solid white;
|
||||
padding: 5px 0;
|
||||
background: #F56C6C;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.errors-wrapper .error-list .error-item.active {
|
||||
background: #E6A23C;
|
||||
font-weight: bolder;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.errors-wrapper .error-list .error-item:hover {
|
||||
font-weight: bolder;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.errors-wrapper .error-list .error-item .line-no {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.errors-wrapper .error-list .error-item .line-content {
|
||||
display: inline;
|
||||
width: calc(100% - 70px);
|
||||
padding-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user