- 401 自动登出:http 会话过期回调 + main.ts 注册(清 menu/tabs + 跳登录) - 参数配置/字典/组织/资源/角色/操作日志/登录日志对接真实接口 - 角色管理新增权限分配树(el-tree 勾选资源,/roles/:code/resources) - 操作日志/登录日志只读页(ty-search-form + ty-table + useRequest) - listOrgs 迁到 api/org.ts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
116 lines
3.1 KiB
Vue
116 lines
3.1 KiB
Vue
<template>
|
|
<div class="log-page">
|
|
<el-card class="log-page__search" shadow="never">
|
|
<ty-search-form :schema="searchSchema" :model-value="searchQuery" @search="handleSearch" @reset="handleReset" />
|
|
</el-card>
|
|
|
|
<el-card class="log-page__table" shadow="never">
|
|
<div class="log-page__table-body">
|
|
<ty-table
|
|
:columns="columns"
|
|
:data="list"
|
|
:loading="loading"
|
|
:total="total"
|
|
v-model:page="page"
|
|
v-model:page-size="pageSize"
|
|
/>
|
|
</div>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
|
import type { JsonSchema, TableColumn } from '@/types/crud'
|
|
import { useRequest } from '@/composables/useRequest'
|
|
import { listAuditLogs } from '@/api/audit-log'
|
|
|
|
const STATUS_MAP = {
|
|
success: { label: '成功', type: 'success' },
|
|
fail: { label: '失败', type: 'danger' },
|
|
} as const
|
|
|
|
const columns: TableColumn[] = [
|
|
{ prop: 'username', label: '操作人', width: 100 },
|
|
{ prop: 'module', label: '模块', width: 100 },
|
|
{ prop: 'action', label: '动作', width: 100 },
|
|
{ prop: 'method', label: '方法', width: 80, align: 'center' },
|
|
{ prop: 'path', label: '路径', minWidth: 180 },
|
|
{ prop: 'status', label: '状态', width: 80, align: 'center', type: 'tag', tagMap: STATUS_MAP },
|
|
{ prop: 'ip', label: 'IP', width: 130 },
|
|
{ prop: 'durationMs', label: '耗时(ms)', width: 90, align: 'center' },
|
|
{ prop: 'createdAt', label: '时间', width: 160, type: 'datetime' },
|
|
]
|
|
|
|
const searchSchema: JsonSchema = {
|
|
type: 'object',
|
|
properties: {
|
|
username: { type: 'string', title: '操作人', placeholder: '请输入操作人' },
|
|
module: { type: 'string', title: '模块', placeholder: '请输入模块' },
|
|
status: {
|
|
type: 'string',
|
|
title: '状态',
|
|
enum: ['success', 'fail'],
|
|
enumNames: ['成功', '失败'],
|
|
placeholder: '全部',
|
|
},
|
|
},
|
|
}
|
|
|
|
const page = ref(1)
|
|
const pageSize = ref(10)
|
|
const searchQuery = reactive({ username: '', module: '', status: '' })
|
|
const appliedQuery = reactive({ username: '', module: '', status: '' })
|
|
|
|
const { data, loading, run } = useRequest(
|
|
(signal: AbortSignal) => listAuditLogs({ page: page.value, pageSize: pageSize.value, ...appliedQuery }, signal),
|
|
{ manual: true },
|
|
)
|
|
|
|
const list = computed(() => data.value?.list ?? [])
|
|
const total = computed(() => data.value?.total ?? 0)
|
|
|
|
function handleSearch(): void {
|
|
appliedQuery.username = searchQuery.username
|
|
appliedQuery.module = searchQuery.module
|
|
appliedQuery.status = searchQuery.status
|
|
page.value = 1
|
|
run()
|
|
}
|
|
|
|
function handleReset(): void {
|
|
appliedQuery.username = ''
|
|
appliedQuery.module = ''
|
|
appliedQuery.status = ''
|
|
page.value = 1
|
|
run()
|
|
}
|
|
|
|
onMounted(run)
|
|
watch([page, pageSize], () => {
|
|
run()
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.log-page {
|
|
@apply flex h-full flex-col;
|
|
}
|
|
|
|
.log-page__search {
|
|
@apply mb-md flex-shrink-0;
|
|
}
|
|
|
|
.log-page__table {
|
|
@apply flex min-h-0 flex-1 flex-col;
|
|
|
|
:deep(.el-card__body) {
|
|
@apply flex min-h-0 flex-1 flex-col;
|
|
}
|
|
}
|
|
|
|
.log-page__table-body {
|
|
@apply min-h-0 flex-1;
|
|
}
|
|
</style>
|