XNSim/XNSimHtml/components/system-log.js

405 lines
13 KiB
JavaScript

class SystemLog extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
this.addEventListeners();
this.loadLogs();
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
height: 100%;
background: #fff;
padding: 20px;
box-sizing: border-box;
}
.log-container {
display: flex;
flex-direction: column;
height: 100%;
gap: 16px;
}
.toolbar {
display: flex;
gap: 16px;
padding: 16px;
background: #f5f7fa;
border-radius: 8px;
align-items: center;
}
.search-box {
flex: 1;
position: relative;
}
.search-box input {
width: 100%;
padding: 8px 12px 8px 36px;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 14px;
transition: all 0.3s;
}
.search-box input:focus {
border-color: #409eff;
outline: none;
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
width: 16px;
height: 16px;
color: #909399;
}
.filter-group {
display: flex;
gap: 12px;
}
.filter-select {
padding: 8px 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 14px;
color: #606266;
background: #fff;
cursor: pointer;
}
.filter-select:focus {
border-color: #409eff;
outline: none;
}
.action-buttons {
display: flex;
gap: 8px;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 4px;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-default {
background: #f4f4f5;
color: #606266;
}
.btn-default:hover {
background: #e9e9eb;
}
.log-content {
flex: 1;
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.log-header {
display: grid;
grid-template-columns: 180px 120px 120px 1fr;
padding: 12px 16px;
background: #f5f7fa;
border-bottom: 1px solid #e4e7ed;
font-weight: bold;
color: #606266;
}
.log-list {
flex: 1;
overflow-y: auto;
}
.log-item {
display: grid;
grid-template-columns: 180px 120px 120px 1fr;
padding: 12px 16px;
border-bottom: 1px solid #e4e7ed;
font-size: 14px;
color: #606266;
}
.log-item:hover {
background: #f5f7fa;
}
.log-level {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.level-info {
background: #e1f3d8;
color: #67c23a;
}
.level-warning {
background: #fdf6ec;
color: #e6a23c;
}
.level-error {
background: #fef0f0;
color: #f56c6c;
}
.level-debug {
background: #f4f4f5;
color: #909399;
}
.pagination {
display: flex;
justify-content: flex-end;
padding: 16px;
gap: 8px;
}
.page-btn {
padding: 6px 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: #fff;
color: #606266;
cursor: pointer;
transition: all 0.3s;
}
.page-btn:hover {
color: #409eff;
border-color: #c6e2ff;
}
.page-btn.active {
background: #409eff;
color: #fff;
border-color: #409eff;
}
.page-btn:disabled {
background: #f5f7fa;
color: #c0c4cc;
cursor: not-allowed;
}
</style>
<div class="log-container">
<div class="toolbar">
<div class="search-box">
<img src="assets/icons/png/search_b.png" alt="搜索" class="search-icon">
<input type="text" placeholder="搜索日志内容...">
</div>
<div class="filter-group">
<select class="filter-select" id="levelFilter">
<option value="">所有级别</option>
<option value="info">信息</option>
<option value="warning">警告</option>
<option value="error">错误</option>
<option value="debug">调试</option>
</select>
<select class="filter-select" id="timeFilter">
<option value="1h">最近1小时</option>
<option value="6h">最近6小时</option>
<option value="24h">最近24小时</option>
<option value="7d">最近7天</option>
<option value="30d">最近30天</option>
</select>
</div>
<div class="action-buttons">
<button class="btn btn-default" id="refreshBtn">
<img src="assets/icons/png/refresh.png" alt="刷新" class="icon-small">
刷新
</button>
<button class="btn btn-primary" id="exportBtn">
<img src="assets/icons/png/download.png" alt="导出" class="icon-small">
导出
</button>
</div>
</div>
<div class="log-content">
<div class="log-header">
<div>时间</div>
<div>级别</div>
<div>来源</div>
<div>内容</div>
</div>
<div class="log-list" id="logList">
<!-- 日志内容将通过JavaScript动态添加 -->
</div>
<div class="pagination">
<button class="page-btn" id="prevPage" disabled>上一页</button>
<button class="page-btn active">1</button>
<button class="page-btn">2</button>
<button class="page-btn">3</button>
<button class="page-btn" id="nextPage">下一页</button>
</div>
</div>
</div>
`;
}
addEventListeners() {
// 搜索框事件
const searchInput = this.shadowRoot.querySelector('.search-box input');
searchInput.addEventListener('input', this.debounce(() => {
this.filterLogs();
}, 300));
// 级别筛选事件
const levelFilter = this.shadowRoot.querySelector('#levelFilter');
levelFilter.addEventListener('change', () => {
this.filterLogs();
});
// 时间筛选事件
const timeFilter = this.shadowRoot.querySelector('#timeFilter');
timeFilter.addEventListener('change', () => {
this.filterLogs();
});
// 刷新按钮事件
const refreshBtn = this.shadowRoot.querySelector('#refreshBtn');
refreshBtn.addEventListener('click', () => {
this.loadLogs();
});
// 导出按钮事件
const exportBtn = this.shadowRoot.querySelector('#exportBtn');
exportBtn.addEventListener('click', () => {
this.exportLogs();
});
// 分页按钮事件
const prevPage = this.shadowRoot.querySelector('#prevPage');
const nextPage = this.shadowRoot.querySelector('#nextPage');
prevPage.addEventListener('click', () => {
this.changePage(-1);
});
nextPage.addEventListener('click', () => {
this.changePage(1);
});
}
async loadLogs() {
try {
// 这里应该调用后端API获取日志数据
// 示例数据
const logs = [
{
timestamp: '2024-03-20 10:00:00',
level: 'info',
source: '系统',
content: '系统启动成功'
},
{
timestamp: '2024-03-20 10:01:00',
level: 'warning',
source: '数据库',
content: '数据库连接池接近最大连接数'
},
{
timestamp: '2024-03-20 10:02:00',
level: 'error',
source: 'API',
content: 'API请求超时'
}
];
this.renderLogs(logs);
} catch (error) {
console.error('加载日志失败:', error);
}
}
renderLogs(logs) {
const logList = this.shadowRoot.querySelector('#logList');
logList.innerHTML = logs.map(log => `
<div class="log-item">
<div>${log.timestamp}</div>
<div><span class="log-level level-${log.level}">${this.getLevelText(log.level)}</span></div>
<div>${log.source}</div>
<div>${log.content}</div>
</div>
`).join('');
}
getLevelText(level) {
const levelMap = {
'info': '信息',
'warning': '警告',
'error': '错误',
'debug': '调试'
};
return levelMap[level] || level;
}
filterLogs() {
const searchText = this.shadowRoot.querySelector('.search-box input').value.toLowerCase();
const levelFilter = this.shadowRoot.querySelector('#levelFilter').value;
const timeFilter = this.shadowRoot.querySelector('#timeFilter').value;
// 这里应该根据筛选条件调用后端API获取过滤后的日志
this.loadLogs();
}
exportLogs() {
// 实现日志导出功能
console.log('导出日志');
}
changePage(delta) {
// 实现分页功能
console.log('切换页面:', delta);
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
}
customElements.define('system-log', SystemLog);