Initial commit: finish basic WebUI interface

This commit is contained in:
2026-03-09 21:17:22 +08:00
parent d2a2a4de36
commit 2f6cfe7704
23 changed files with 5028 additions and 1 deletions

180
src/views/Logs.vue Normal file
View File

@@ -0,0 +1,180 @@
<template>
<div class="logs-page">
<div class="page-header">
<h2>System Logs</h2>
<div class="filter-controls">
<select v-model="selectedLevel" @change="loadLogs">
<option value="">All Levels</option>
<option value="DEBUG">DEBUG</option>
<option value="INFO">INFO</option>
<option value="WARN">WARN</option>
<option value="ERROR">ERROR</option>
<option value="FATAL">FATAL</option>
</select>
</div>
</div>
<div class="logs-container">
<div v-if="logs.length > 0" class="log-list">
<div v-for="(log, index) in logs" :key="index" class="log-item">
<span class="log-time">{{ log.time }}</span>
<span :class="['log-level', log.level]">{{ log.level }}</span>
<span class="log-message">{{ log.message }}</span>
</div>
</div>
<div v-else class="empty-state">
<p>No logs available</p>
</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { logApi } from '../api/index.js';
import { showNotification, formatDate } from '../utils/functions.js';
export default {
name: 'Logs',
setup() {
const logs = ref([]);
const selectedLevel = ref('');
const loadLogs = async () => {
try {
const result = await logApi.getLogs(selectedLevel.value);
logs.value = result.data.map(log => ({
time: formatDate(log.timestamp),
level: log.level,
message: log.message
}));
} catch (error) {
showNotification('Failed to load logs', 'error');
}
};
onMounted(() => {
loadLogs();
});
return {
logs,
selectedLevel,
loadLogs
};
}
};
</script>
<style scoped>
.logs-page {
padding: 24px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-header h2 {
font-size: 24px;
color: var(--text-color);
margin: 0;
transition: color 0.3s;
}
.filter-controls select {
padding: 8px 16px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
background: var(--card-bg);
color: var(--text-color);
cursor: pointer;
transition: background-color 0.3s, color 0.3s, border-color 0.3s;
}
.filter-controls select:focus {
outline: none;
border-color: var(--primary-color);
}
.logs-container {
background: var(--card-bg);
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
max-height: calc(100vh - 200px);
overflow-y: auto;
transition: background-color 0.3s;
}
.log-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.log-item {
display: flex;
gap: 12px;
padding: 12px;
background: #f9f9f9;
border-radius: 6px;
font-size: 14px;
font-family: 'Consolas', 'Monaco', monospace;
}
.log-time {
color: #999;
min-width: 160px;
}
.log-level {
min-width: 70px;
font-weight: 600;
text-align: center;
padding: 2px 8px;
border-radius: 4px;
}
.log-level.DEBUG {
color: #52c41a;
background: #f6ffed;
}
.log-level.INFO {
color: #1890ff;
background: #e6f7ff;
}
.log-level.WARN {
color: #faad14;
background: #fffbe6;
}
.log-level.ERROR {
color: #ff4d4f;
background: #fff1f0;
}
.log-level.FATAL {
color: #cf1322;
background: #fff1f0;
}
.log-message {
color: var(--text-color);
flex: 1;
word-break: break-all;
transition: color 0.3s;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #999;
font-size: 16px;
}
</style>