管理端拆分赛事/优胜赛 Tab,新增联赛优胜赔率面板(批量、排序、外侧删除);统一 list-chrome 工具栏对齐与列表页布局;Dashboard 失败重试、Users 操作下拉、小屏侧栏等体验修复。 API 扩展优胜赛与赛事目录接口,完善投注与钱包查询;玩家端重构赛事卡片、串关面板、注单/钱包页,新增注单详情、下注成功动画与下拉刷新。 Co-authored-by: Cursor <cursoragent@cursor.com>
128 lines
3.4 KiB
Vue
128 lines
3.4 KiB
Vue
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue';
|
|
import { useAdminLocale } from '../composables/useAdminLocale';
|
|
import api from '../api';
|
|
import AdminTableEmpty from '../components/AdminTableEmpty.vue';
|
|
|
|
const { t, locale, localeTag } = useAdminLocale();
|
|
|
|
function auditActionLabel(action: string) {
|
|
const key = `audit.action.${action}`;
|
|
const label = t(key);
|
|
return label === key ? action : label;
|
|
}
|
|
|
|
function auditModuleLabel(module: string) {
|
|
const key = `audit.module.${module}`;
|
|
const label = t(key);
|
|
return label === key ? module : label;
|
|
}
|
|
|
|
interface AuditRow {
|
|
action: string;
|
|
module: string;
|
|
targetId: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
const logs = ref<AuditRow[]>([]);
|
|
const total = ref(0);
|
|
const page = ref(1);
|
|
const pageSize = ref(10);
|
|
const filterModule = ref('');
|
|
|
|
onMounted(load);
|
|
|
|
async function load() {
|
|
const { data } = await api.get('/admin/audit-logs', {
|
|
params: {
|
|
page: page.value,
|
|
pageSize: pageSize.value,
|
|
module: filterModule.value.trim() || undefined,
|
|
},
|
|
});
|
|
logs.value = (data.data.items ?? []) as AuditRow[];
|
|
total.value = data.data.total ?? 0;
|
|
}
|
|
|
|
function onPageChange(p: number) {
|
|
page.value = p;
|
|
load();
|
|
}
|
|
|
|
function onSizeChange(size: number) {
|
|
pageSize.value = size;
|
|
page.value = 1;
|
|
load();
|
|
}
|
|
|
|
function formatTime(v: string) {
|
|
return new Date(v).toLocaleString(localeTag.value, {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="admin-list-page">
|
|
<el-card class="filter-card" shadow="never">
|
|
<el-form inline>
|
|
<el-form-item :label="t('common.module')">
|
|
<el-input
|
|
v-model="filterModule"
|
|
:placeholder="t('audit.module_ph')"
|
|
clearable
|
|
style="width: 160px"
|
|
@keyup.enter="load"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="load">{{ t('common.search') }}</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-card>
|
|
|
|
<el-card class="data-card" shadow="never">
|
|
<div class="table-wrap">
|
|
<el-table :key="locale" :data="logs" stripe>
|
|
<template #empty>
|
|
<AdminTableEmpty />
|
|
</template>
|
|
<el-table-column :label="t('audit.col.action')" min-width="140">
|
|
<template #default="{ row }">{{ auditActionLabel(row.action) }}</template>
|
|
</el-table-column>
|
|
<el-table-column :label="t('audit.col.module')" width="120">
|
|
<template #default="{ row }">{{ auditModuleLabel(row.module) }}</template>
|
|
</el-table-column>
|
|
<el-table-column prop="targetId" :label="t('audit.col.target_id')" min-width="100" />
|
|
<el-table-column :label="t('audit.col.time')" min-width="160">
|
|
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
<div class="pager">
|
|
<el-pagination
|
|
v-model:current-page="page"
|
|
v-model:page-size="pageSize"
|
|
:total="total"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
layout="total, sizes, prev, pager, next"
|
|
background
|
|
@current-change="onPageChange"
|
|
@size-change="onSizeChange"
|
|
/>
|
|
</div>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.filter-card { border-radius: 12px; }
|
|
.data-card { border-radius: 12px; }
|
|
</style>
|