UI改版测试V0

This commit is contained in:
liuwei
2026-03-09 11:32:08 +08:00
parent 8b95fbc2a9
commit 14720f48a4
17 changed files with 3043 additions and 4548 deletions
+33 -132
View File
@@ -3,144 +3,45 @@
{% block title %}错误日志 - 机器人管理后台{% endblock %}
{% block content %}
<!-- 错误日志 -->
<div>
<el-row :gutter="20">
<el-col :span="24">
<el-card shadow="hover">
<div slot="header">
<span>错误日志</span>
</div>
<el-table {% raw %}:data="errorLogs"{% endraw %} style="width: 100%" border>
<el-table-column prop="plugin_name" label="插件名称"></el-table-column>
<el-table-column prop="command" label="命令"></el-table-column>
<el-table-column prop="error_message" label="错误信息" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="created_at" label="时间"></el-table-column>
<el-table-column label="用户">
<template slot-scope="scope">
<span v-if="scope.row.user_id">
{% raw %}{{ scope.row.user_name || scope.row.user_id }} ({{ scope.row.user_id }}){% endraw %}
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="群组">
<template slot-scope="scope">
<span v-if="scope.row.group_id">
{% raw %}{{ scope.row.group_name || scope.row.group_id }} ({{ scope.row.group_id }}){% endraw %}
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="viewErrorDetail(scope.row)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container" style="margin-top: 20px; text-align: right;">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalErrors">
</el-pagination>
</div>
</el-card>
</el-col>
<div class="page-shell logs-page">
<div class="page-hero"><div class="page-hero-copy"><div class="page-eyebrow">Error Center</div><h1>错误日志</h1><p>统一查看插件异常、用户上下文与错误详情,方便快速定位问题来源。</p></div></div>
<el-row :gutter="16" class="overview-grid">
<el-col :span="8"><el-card class="overview-card overview-card--primary"><div class="overview-label">错误总数</div><div class="overview-value">{% raw %}{{ totalErrors }}{% endraw %}</div><div class="overview-note">当前分页查询结果对应总量</div></el-card></el-col>
<el-col :span="8"><el-card class="overview-card"><div class="overview-label">当前页日志数</div><div class="overview-value">{% raw %}{{ errorLogs.length }}{% endraw %}</div><div class="overview-note">本页已加载的错误记录</div></el-card></el-col>
<el-col :span="8"><el-card class="overview-card overview-card--soft"><div class="overview-label">分页大小</div><div class="overview-value">{% raw %}{{ pageSize }}{% endraw %}</div><div class="overview-note">每页可查看的错误条目</div></el-card></el-col>
</el-row>
<!-- 错误详情对话框 -->
<el-card class="workspace-card" shadow="hover">
<div slot="header" class="workspace-header"><div><h3>错误列表</h3><p>查看插件、命令、错误摘要以及相关用户/群组上下文。</p></div></div>
<el-table :data="errorLogs" style="width:100%">
<el-table-column prop="plugin_name" label="插件名称" width="140"></el-table-column>
<el-table-column prop="command" label="命令" width="120"></el-table-column>
<el-table-column prop="error_message" label="错误信息" min-width="320" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="created_at" label="时间" width="170"></el-table-column>
<el-table-column label="用户" min-width="180"><template slot-scope="scope"><span v-if="scope.row.user_id">{% raw %}{{ scope.row.user_name || scope.row.user_id }} ({{ scope.row.user_id }}){% endraw %}</span><span v-else>-</span></template></el-table-column>
<el-table-column label="群组" min-width="180"><template slot-scope="scope"><span v-if="scope.row.group_id">{% raw %}{{ scope.row.group_name || scope.row.group_id }} ({{ scope.row.group_id }}){% endraw %}</span><span v-else>-</span></template></el-table-column>
<el-table-column label="操作" width="120" align="center"><template slot-scope="scope"><el-button size="mini" type="primary" plain @click="viewErrorDetail(scope.row)">查看详情</el-button></template></el-table-column>
</el-table>
<div class="pagination-container"><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[10,20,50,100]" :page-size="pageSize" layout="total, sizes, prev, pager, next, jumper" :total="totalErrors"></el-pagination></div>
</el-card>
<el-dialog title="错误详情" :visible.sync="errorDetailVisible" width="70%">
<div>
<p><strong>插件名称:</strong> {% raw %}{{ errorDetail.plugin_name }}{% endraw %}</p>
<p><strong>命令:</strong> {% raw %}{{ errorDetail.command }}{% endraw %}</p>
<p><strong>用户ID:</strong> {% raw %}{{ errorDetail.user_id }}{% endraw %}</p>
<p><strong>群组ID:</strong> {% raw %}{{ errorDetail.group_id || '无' }}{% endraw %}</p>
<p><strong>时间:</strong> {% raw %}{{ errorDetail.created_at }}{% endraw %}</p>
<p><strong>错误信息:</strong> {% raw %}{{ errorDetail.error_message }}{% endraw %}</p>
<div v-if="errorDetail.stack_trace">
<p><strong>堆栈跟踪:</strong></p>
<pre>{% raw %}{{ errorDetail.stack_trace }}{% endraw %}</pre>
</div>
</div>
<el-descriptions :column="1" border>
<el-descriptions-item label="插件名称">{% raw %}{{ errorDetail.plugin_name }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="命令">{% raw %}{{ errorDetail.command }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="用户ID">{% raw %}{{ errorDetail.user_id }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="群组ID">{% raw %}{{ errorDetail.group_id || '无' }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="时间">{% raw %}{{ errorDetail.created_at }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="错误信息">{% raw %}{{ errorDetail.error_message }}{% endraw %}</el-descriptions-item>
<el-descriptions-item label="堆栈跟踪" v-if="errorDetail.stack_trace"><pre class="stack-trace">{% raw %}{{ errorDetail.stack_trace }}{% endraw %}</pre></el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
{% endblock %}
{% block scripts %}
<script>
new Vue({
el: '#app',
mixins: [baseApp],
data() {
return {
errorLogs: [],
errorDetail: {},
errorDetailVisible: false,
currentPage: 1,
pageSize: 20,
totalErrors: 0
}
},
mounted() {
this.currentView = '5';
this.loadData();
},
methods: {
loadData() {
const days = parseInt(this.timeRange);
this.loadErrorLogs(days);
},
loadErrorLogs(days) {
axios.get(`/api/error_logs?days=${days}&limit=${this.pageSize}&offset=${(this.currentPage - 1) * this.pageSize}`)
.then(response => {
if (response.data.success) {
this.errorLogs = response.data.data.logs || [];
this.totalErrors = response.data.data.total || 0;
}
})
.catch(error => {
console.error('加载错误日志出错:', error);
this.$message.error('加载错误日志出错');
});
},
viewErrorDetail(error) {
// 如果已有错误ID,直接加载详情
if (error.id) {
this.loadErrorDetail(error.id);
} else {
// 否则直接显示当前行的数据
this.errorDetail = error;
this.errorDetailVisible = true;
}
},
loadErrorDetail(errorId) {
axios.get(`/api/error_detail/${errorId}`)
.then(response => {
if (response.data.success) {
this.errorDetail = response.data.data || {};
this.errorDetailVisible = true;
}
})
.catch(error => {
console.error('加载错误详情出错:', error);
this.$message.error('加载错误详情出错');
});
},
handleSizeChange(size) {
this.pageSize = size;
this.currentPage = 1; // 重置到第一页
this.loadData();
},
handleCurrentChange(page) {
this.currentPage = page;
this.loadData();
}
}
});
new Vue({ el:'#app', mixins:[baseApp], data(){ return { errorLogs:[], errorDetail:{}, errorDetailVisible:false, currentPage:1, pageSize:20, totalErrors:0 } }, mounted(){ this.currentView='5'; this.loadData(); }, methods:{ loadData(){ this.loadErrorLogs(parseInt(this.timeRange)); }, loadErrorLogs(days){ axios.get(`/api/error_logs?days=${days}&limit=${this.pageSize}&offset=${(this.currentPage - 1) * this.pageSize}`).then(r=>{ if(r.data.success){ this.errorLogs=r.data.data.logs||[]; this.totalErrors=r.data.data.total||0; } }).catch(e=>{ console.error('加载错误日志出错:',e); this.$message.error('加载错误日志出错'); }); }, viewErrorDetail(error){ if(error.id){ this.loadErrorDetail(error.id);} else { this.errorDetail=error; this.errorDetailVisible=true; } }, loadErrorDetail(id){ axios.get(`/api/error_detail/${id}`).then(r=>{ if(r.data.success){ this.errorDetail=r.data.data||{}; this.errorDetailVisible=true; } }).catch(e=>{ console.error('加载错误详情出错:',e); this.$message.error('加载错误详情出错'); }); }, handleSizeChange(size){ this.pageSize=size; this.currentPage=1; this.loadData(); }, handleCurrentChange(page){ this.currentPage=page; this.loadData(); } } });
</script>
{% endblock %}
<style>
.page-shell{display:flex;flex-direction:column;gap:16px}.page-hero{padding:24px 26px;border-radius:24px;background:linear-gradient(135deg, rgba(79,70,229,.10), rgba(59,130,246,.08), rgba(255,255,255,.9));border:1px solid rgba(148,163,184,.16);box-shadow:0 18px 40px rgba(15,23,42,.06)}.page-eyebrow{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:#6366f1;font-weight:700;margin-bottom:8px}.page-hero-copy h1{font-size:30px;line-height:1.1;margin-bottom:10px;color:#0f172a}.page-hero-copy p{color:#64748b;font-size:14px}.overview-grid .el-col{margin-bottom:16px}.overview-card{min-height:112px}.overview-card--primary{background:linear-gradient(180deg, rgba(79,70,229,.10), rgba(255,255,255,.94)) !important}.overview-card--soft{background:linear-gradient(180deg, rgba(59,130,246,.08), rgba(255,255,255,.94)) !important}.overview-label{font-size:13px;color:#64748b;margin-bottom:14px}.overview-value{font-size:30px;font-weight:700;color:#0f172a;margin-bottom:10px}.overview-note{font-size:12px;color:#94a3b8}.workspace-header{display:flex;align-items:center;justify-content:space-between;gap:16px}.workspace-header h3{font-size:18px;margin-bottom:4px}.workspace-header p{font-size:13px;color:#64748b}.pagination-container{margin-top:20px;text-align:right}.stack-trace{white-space:pre-wrap;word-break:break-word;background:rgba(248,250,252,.85);border:1px solid rgba(148,163,184,.12);border-radius:14px;padding:14px;color:#334155}
</style>
{% endblock %}