chore: sync current WechatHookBot workspace

This commit is contained in:
2026-03-09 15:48:45 +08:00
parent 4016c1e6eb
commit 9119e2307d
195 changed files with 24438 additions and 17498 deletions

View File

@@ -0,0 +1,113 @@
window.LogViewer = {
setup() {
const { ref, computed, onMounted, onUnmounted, nextTick, watch } = Vue;
const { connected, logs, paused, connect, clear, togglePause, destroy } = useWebSocket();
const filterText = ref('');
const containerRef = ref(null);
function onWheel(event) {
const el = containerRef.value;
if (!el) return;
const atTop = el.scrollTop <= 0;
const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
const scrollingUp = event.deltaY < 0;
const scrollingDown = event.deltaY > 0;
// 防止日志容器触底/触顶时把滚轮事件传递给外层页面
if ((scrollingUp && atTop) || (scrollingDown && atBottom)) {
event.preventDefault();
}
event.stopPropagation();
}
const filteredLogs = computed(() => {
const kw = filterText.value.toLowerCase();
if (!kw) return logs.value;
return logs.value.filter(line => line.toLowerCase().includes(kw));
});
watch(filteredLogs, () => {
if (!paused.value) {
nextTick(() => {
if (containerRef.value) {
containerRef.value.scrollTop = containerRef.value.scrollHeight;
}
});
}
});
const levelColors = {
DEBUG: 'var(--el-text-color-placeholder)',
INFO: 'var(--el-color-primary)',
SUCCESS: 'var(--el-color-success)',
WARNING: 'var(--el-color-warning)',
ERROR: 'var(--el-color-danger)',
CRITICAL: '#b42318',
};
function colorize(raw) {
let s = raw
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(
/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/,
'<span style="color:var(--el-text-color-placeholder)">$1</span>'
);
const m = raw.match(/\|\s*(DEBUG|INFO|SUCCESS|WARNING|ERROR|CRITICAL)\s*\|/);
if (m) {
s = s.replace(
/\|\s*(DEBUG|INFO|SUCCESS|WARNING|ERROR|CRITICAL)\s*\|/,
'| <span style="color:' + levelColors[m[1]] + '">' + m[1] + '</span> |'
);
}
return s;
}
onMounted(() => {
connect();
if (containerRef.value) {
containerRef.value.addEventListener('wheel', onWheel, { passive: false });
}
});
onUnmounted(() => {
if (containerRef.value) {
containerRef.value.removeEventListener('wheel', onWheel);
}
destroy();
});
return {
filterText, paused, connected, filteredLogs,
containerRef, togglePause, clear, colorize
};
},
template: `
<div class="panel-page">
<div class="log-toolbar">
<el-input v-model="filterText" placeholder="搜索过滤..." clearable
class="log-search" size="small">
<template #prefix><el-icon><Search /></el-icon></template>
</el-input>
<el-button size="small" :type="paused ? 'primary' : ''" @click="togglePause">
{{ paused ? '恢复' : '暂停' }}
</el-button>
<el-button size="small" @click="clear">清空</el-button>
<span class="log-status" :class="connected ? 'is-online' : 'is-offline'">
<span class="status-dot"></span>
{{ connected ? '已连接' : '已断开' }}
</span>
</div>
<div ref="containerRef"
class="log-stream">
<div v-for="(line, i) in filteredLogs" :key="i"
class="log-line"
v-html="colorize(line)">
</div>
</div>
</div>
`
};