1d0e862299
客户端重构部分界面UI
108 lines
4.3 KiB
Vue
108 lines
4.3 KiB
Vue
<script setup lang="ts">
|
|
import { Search, ChevronLeft, ChevronRight } from "lucide-vue-next";
|
|
|
|
defineProps<{ ctx: any }>();
|
|
</script>
|
|
|
|
<template>
|
|
<section class="page-stack">
|
|
<div class="toolbar">
|
|
<label class="search-box">
|
|
<Search :size="16" />
|
|
<input v-model="ctx.auditPage.q" placeholder="搜索消息内容" @keyup.enter="ctx.loadAudit" />
|
|
</label>
|
|
<select v-model="ctx.auditPage.type" style="width:140px" @change="ctx.loadAudit">
|
|
<option value="">全部类型</option>
|
|
<option value="auth.login">管理员登录</option>
|
|
<option value="auth.password_changed">修改密码</option>
|
|
<option value="feedback.created">提交反馈</option>
|
|
<option value="feedback.updated">更新工单</option>
|
|
<option value="legacy_json.saved">保存兼容 JSON</option>
|
|
<option value="release_notice.saved">保存版本日志</option>
|
|
<option value="release.package_uploaded">上传发布包</option>
|
|
<option value="legacy.sync">旧项目同步</option>
|
|
</select>
|
|
<input v-model="ctx.auditPage.target" placeholder="目标对象" style="width:100px" @keyup.enter="ctx.loadAudit" />
|
|
<button class="btn ghost" @click="ctx.loadAudit">查询</button>
|
|
</div>
|
|
|
|
<section class="panel">
|
|
<div class="section-head">
|
|
<h2>审计日志</h2>
|
|
<span class="badge">共 {{ ctx.auditPage.total || 0 }} 条</span>
|
|
</div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>时间</th>
|
|
<th>操作者</th>
|
|
<th>操作类型</th>
|
|
<th>目标</th>
|
|
<th>消息</th>
|
|
<th>IP</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr
|
|
v-for="item in ctx.auditPage.items || []"
|
|
:key="item.id"
|
|
:class="{ selected: ctx.auditPage.selected?.id === item.id }"
|
|
style="cursor:pointer"
|
|
@click="ctx.selectAuditLog(item)"
|
|
>
|
|
<td class="mono">{{ item.createdAt }}</td>
|
|
<td>{{ item.actor || "-" }}</td>
|
|
<td><span class="badge neutral">{{ ctx.auditTypeLabel(item.type) }}</span></td>
|
|
<td class="mono">{{ item.target || "-" }}</td>
|
|
<td>{{ ctx.auditMessage(item) }}</td>
|
|
<td class="hash">{{ item.ip || "-" }}</td>
|
|
</tr>
|
|
<tr v-if="!(ctx.auditPage.items || []).length">
|
|
<td colspan="6">暂无审计日志。</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- Pagination -->
|
|
<div class="pagination" v-if="ctx.auditPage.total > 0">
|
|
<button class="btn ghost small" :disabled="ctx.auditPage.page <= 1" @click="ctx.setAuditPage(ctx.auditPage.page - 1)">
|
|
<ChevronLeft :size="14" />
|
|
</button>
|
|
<span class="muted">第 {{ ctx.auditPage.page }} / {{ Math.ceil(ctx.auditPage.total / ctx.auditPage.perPage) }} 页 · {{ ctx.auditPage.total }} 条</span>
|
|
<button class="btn ghost small" :disabled="ctx.auditPage.page >= Math.ceil(ctx.auditPage.total / ctx.auditPage.perPage)" @click="ctx.setAuditPage(ctx.auditPage.page + 1)">
|
|
<ChevronRight :size="14" />
|
|
</button>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Detail panel for selected log -->
|
|
<section v-if="ctx.auditPage.selected" class="panel">
|
|
<div class="section-head">
|
|
<h2>审计详情</h2>
|
|
<span class="badge neutral">ID {{ ctx.auditPage.selected.id }}</span>
|
|
</div>
|
|
<div class="kv-grid">
|
|
<span>时间</span><strong>{{ ctx.auditPage.selected.createdAt }}</strong>
|
|
<span>操作者</span><strong>{{ ctx.auditPage.selected.actor || "-" }}</strong>
|
|
<span>类型</span><strong>{{ ctx.auditTypeLabel(ctx.auditPage.selected.type) }}</strong>
|
|
<span>目标</span><strong class="mono">{{ ctx.auditPage.selected.target || "-" }}</strong>
|
|
<span>消息</span><strong>{{ ctx.auditMessage(ctx.auditPage.selected) }}</strong>
|
|
<span>IP</span><strong class="mono">{{ ctx.auditPage.selected.ip || "-" }}</strong>
|
|
<span>User-Agent</span><strong class="hash" style="word-break:break-all">{{ ctx.auditPage.selected.userAgent || "-" }}</strong>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.pagination {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 0 0;
|
|
}
|
|
.pagination .btn.small {
|
|
padding: 3px 7px;
|
|
}
|
|
</style>
|