webapp: Add additional filter possibilities to console view

This commit is contained in:
Thomas Basler
2025-04-26 00:19:42 +02:00
parent 95106e4488
commit 4099347a43
8 changed files with 131 additions and 26 deletions

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Ενεργοποίηση αυτόματης κύλισης",
"ClearConsole": "Εκκαθάριση κονσόλας",
"CopyToClipboard": "Αντιγραφή στο πρόχειρο",
"Download": "Download"
"Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
},
"inverterchannelinfo": {
"String": "Είσοδος {num}",

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Habilitar Desplazamiento Automático",
"ClearConsole": "Limpiar Consola",
"CopyToClipboard": "Copiar al Portapapeles",
"Download": "Download"
"Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
},
"inverterchannelinfo": {
"String": "Cadena {num}",

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Abilita AutoScroll",
"ClearConsole": "Pulisci Console",
"CopyToClipboard": "Copia nella clipboard",
"Download": "Download"
"Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
},
"inverterchannelinfo": {
"String": "Stringa {num}",

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Włącz automatyczne przewijani",
"ClearConsole": "Wyczyść konsolę",
"CopyToClipboard": "Kopiuj do schowka",
"Download": "Download"
"Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
},
"inverterchannelinfo": {
"String": "Numer panelu {num}",

View File

@@ -369,7 +369,10 @@
"EnableAutoScroll": "Automatisches Scrollen aktivieren",
"ClearConsole": "Konsole leeren",
"CopyToClipboard": "In die Zwischenablage kopieren",
"Download": "Herunterladen"
"Download": "Herunterladen",
"RegularExpression": "Regulärer Ausdruck",
"Lines": "{lines} Zeilen",
"LogLevel": "Protokollierungsstufe ({cnt})"
},
"inverterchannelinfo": {
"String": "String {num}",

View File

@@ -369,7 +369,10 @@
"EnableAutoScroll": "Enable Auto Scroll",
"ClearConsole": "Clear Console",
"CopyToClipboard": "Copy to clipboard",
"Download": "Download"
"Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
},
"inverterchannelinfo": {
"String": "String {num}",

View File

@@ -351,7 +351,10 @@
"EnableAutoScroll": "Activer le défilement automatique",
"ClearConsole": "Vider la console",
"CopyToClipboard": "Copier dans le presse-papiers",
"Download": "Download"
"Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
},
"inverterchannelinfo": {
"String": "Ligne {num}",

View File

@@ -1,8 +1,85 @@
<template>
<BasePage :title="$t('console.Console')" :isLoading="dataLoading">
<CardElement :text="$t('console.VirtualDebugConsole')" textVariant="text-bg-primary">
<div class="row align-items-center mb-3">
<div class="col-auto mt-2">
<div class="btn-toolbar mb-3" role="toolbar">
<div class="input-group me-2">
<div class="input-group-text" id="btnSearch">
<BIconSearch />
</div>
<input
v-model="searchText"
type="text"
class="form-control"
:placeholder="$t('console.RegularExpression')"
aria-label="Search"
aria-describedby="btnSearch"
/>
</div>
<div class="btn-group me-2" role="group" aria-label="Filter by level">
<div class="dropdown">
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
id="logLevelDropdown"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<BIconFilter /> {{ $t('console.LogLevel', { cnt: activeLevels.length }) }}
</button>
<ul
class="dropdown-menu dropdown-menu-dark p-2"
aria-labelledby="logLevelDropdown"
style="min-width: 200px"
>
<li v-for="(label, tag) in levelMap" :key="tag">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
:id="`dropdown-filter-${label}`"
v-model="activeLevels"
:value="levelMap[tag]"
/>
<label class="form-check-label" :for="`dropdown-filter-${label}`">
{{ label.toUpperCase() }}
</label>
</div>
</li>
</ul>
</div>
</div>
<div class="btn-group me-2" role="group">
<button
type="button"
class="btn btn-primary"
:onClick="clearConsole"
:title="$t('console.ClearConsole')"
>
<BIconTrash />
</button>
</div>
<div class="btn-group me-2" role="group">
<button
type="button"
class="btn btn-secondary"
:onClick="copyConsole"
:title="$t('console.CopyToClipboard')"
>
<BIconClipboard />
</button>
<button
type="button"
class="btn btn-secondary"
:onClick="exportConsole"
:title="$t('console.Download')"
>
<BIconDownload />
</button>
</div>
<div class="col-auto ms-auto mt-2">
<div class="form-check form-switch">
<input
class="form-check-input"
@@ -16,27 +93,15 @@
</label>
</div>
</div>
<div class="col-auto ms-auto">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" :onClick="clearConsole">
{{ $t('console.ClearConsole') }}
</button>
<button type="button" class="btn btn-secondary" :onClick="copyConsole">
{{ $t('console.CopyToClipboard') }}
</button>
<button type="button" class="btn btn-secondary" :onClick="exportConsole">
{{ $t('console.Download') }}
</button>
</div>
</div>
</div>
<div id="log" ref="logRef" class="log-output" @scroll="handleScroll">
<div v-for="(line, index) in lines" :key="index" class="log-line" :class="getLineClass(line.text)">
<div v-for="(line, index) in filteredLines" :key="index" class="log-line" :class="line.level">
<span class="timestamp">[{{ formatTimestamp(line.timestamp) }}]</span>
{{ line.text }}
</div>
</div>
{{ $t('console.Lines', { lines: $n(filteredLines.length) }) }}
</CardElement>
</BasePage>
</template>
@@ -45,17 +110,24 @@
import BasePage from '@/components/BasePage.vue';
import CardElement from '@/components/CardElement.vue';
import { authUrl } from '@/utils/authentication';
import { BIconClipboard, BIconDownload, BIconFilter, BIconSearch, BIconTrash } from 'bootstrap-icons-vue';
import { defineComponent } from 'vue';
interface LogLine {
text: string;
timestamp: Date;
level: string;
}
export default defineComponent({
components: {
BasePage,
CardElement,
BIconDownload,
BIconClipboard,
BIconTrash,
BIconFilter,
BIconSearch,
},
data() {
return {
@@ -63,6 +135,8 @@ export default defineComponent({
heartInterval: 0,
dataLoading: true,
autoScroll: true,
activeLevels: ['error', 'warning', 'info', 'debug', 'verbose'],
searchText: '',
lines: [] as LogLine[],
buffer: '',
levelMap: {
@@ -81,6 +155,15 @@ export default defineComponent({
unmounted() {
this.closeSocket();
},
computed: {
filteredLines(): LogLine[] {
return this.lines.filter((line) => {
const regexp = new RegExp(this.searchText, 'i');
const found = regexp.test(line.text);
return this.activeLevels.includes(line.level) && found;
});
},
},
methods: {
initSocket() {
console.log('Starting connection to WebSocket Server');
@@ -104,6 +187,7 @@ export default defineComponent({
this.lines.push({
text: line,
timestamp: new Date(), // assign time of message arrival
level: this.getLineClass(line),
});
this.$nextTick(() => {
if (this.autoScroll) {
@@ -189,7 +273,7 @@ export default defineComponent({
this.buffer = '';
},
copyConsole() {
const content = this.lines
const content = this.filteredLines
.map((line) => `[${this.formatTimestamp(line.timestamp)}] ${line.text}`)
.join('\n');
navigator.clipboard
@@ -202,7 +286,7 @@ export default defineComponent({
});
},
exportConsole() {
const content = this.lines
const content = this.filteredLines
.map((line) => `[${this.formatTimestamp(line.timestamp)}] ${line.text}`)
.join('\n');
const timestamp = this.getFileTimestamp();
@@ -234,7 +318,7 @@ export default defineComponent({
});
</script>
<style>
<style scoped>
.log-output {
height: 500px;
max-height: 500px;