ether-admin/src/views/system/Audit.vue

201 lines
5.2 KiB
Vue

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { Table, Button, Space, Input, Select, DatePicker, Tag, message } from 'ant-design-vue'
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue'
import dayjs from 'dayjs'
// 审计日志类型
interface AuditLog {
id: string
time: string
operator: string
type: 'PERMISSION' | 'ROLE' | 'PROJECT'
content: string
target: string
ip: string
}
// 表格列定义
const columns = [
{ title: '时间', dataIndex: 'time', key: 'time', width: 180 },
{ title: '操作用户', dataIndex: 'operator', key: 'operator', width: 120 },
{ title: '操作类型', dataIndex: 'type', key: 'type', width: 100 },
{ title: '操作内容', dataIndex: 'content', key: 'content', ellipsis: true },
{ title: '目标对象', dataIndex: 'target', key: 'target', width: 150 },
{ title: 'IP地址', dataIndex: 'ip', key: 'ip', width: 140 }
]
// 数据
const logs = ref<AuditLog[]>([])
const loading = ref(false)
// 筛选
const filters = ref({
type: undefined as string | undefined,
dateRange: [] as [dayjs.Dayjs, dayjs.Dayjs] | null,
operator: ''
})
// 操作类型选项
const typeOptions = [
{ value: 'PERMISSION', label: '权限变更' },
{ value: 'ROLE', label: '角色分配' },
{ value: 'PROJECT', label: '项目参与' }
]
// 模拟数据
const mockLogs: AuditLog[] = [
{
id: '1',
time: '2026-03-21 10:30:25',
operator: 'admin',
type: 'PERMISSION',
content: '修改用户「张三」的项目权限,添加「数据导出」权限',
target: '用户:张三',
ip: '192.168.1.100'
},
{
id: '2',
time: '2026-03-21 09:15:42',
operator: 'admin',
type: 'ROLE',
content: '为用户「李四」分配「项目经理」角色',
target: '用户:李四',
ip: '192.168.1.100'
},
{
id: '3',
time: '2026-03-20 16:45:33',
operator: 'manager',
type: 'PROJECT',
content: '将「王五」从「智慧社区项目」中移除',
target: '智慧社区项目',
ip: '192.168.1.105'
},
{
id: '4',
time: '2026-03-20 14:20:18',
operator: 'admin',
type: 'PERMISSION',
content: '撤销用户「赵六」的「系统管理」权限',
target: '用户:赵六',
ip: '192.168.1.100'
},
{
id: '5',
time: '2026-03-19 11:05:56',
operator: 'manager',
type: 'ROLE',
content: '更新角色「审计员」的权限配置',
target: '角色:审计员',
ip: '192.168.1.105'
}
]
// 获取操作类型标签
const getTypeColor = (type: string) => {
const map: Record<string, string> = {
PERMISSION: 'blue',
ROLE: 'green',
PROJECT: 'orange'
}
return map[type] || 'default'
}
const getTypeLabel = (type: string) => {
const map: Record<string, string> = {
PERMISSION: '权限变更',
ROLE: '角色分配',
PROJECT: '项目参与'
}
return map[type] || type
}
// 加载数据
const loadData = () => {
loading.value = true
// 模拟API延迟
setTimeout(() => {
logs.value = mockLogs.filter((log) => {
// 按类型筛选
if (filters.value.type && log.type !== filters.value.type) return false
// 按用户筛选
if (filters.value.operator && !log.operator.includes(filters.value.operator)) return false
// 按日期范围筛选
if (filters.value.dateRange && filters.value.dateRange.length === 2) {
const logDate = dayjs(log.time).startOf('day')
const [start, end] = filters.value.dateRange
if (logDate.isBefore(start, 'day') || logDate.isAfter(end, 'day')) return false
}
return true
})
loading.value = false
}, 300)
}
// 重置
const handleReset = () => {
filters.value = {
type: undefined,
dateRange: null,
operator: ''
}
loadData()
}
onMounted(loadData)
</script>
<template>
<div class="page-container">
<!-- 页面标题 -->
<div class="page-header">
<h2 class="page-title">操作审计日志</h2>
</div>
<!-- 筛选区 -->
<div class="filter-bar">
<Space wrap>
<Select
v-model:value="filters.type"
placeholder="操作类型"
:options="typeOptions"
allow-clear
style="width: 140px"
/>
<DatePicker.RangePicker v-model:value="filters.dateRange" style="width: 260px" />
<Input
v-model:value="filters.operator"
placeholder="操作用户"
style="width: 140px"
/>
<Button type="primary" @click="loadData">
<SearchOutlined /> 查询
</Button>
<Button @click="handleReset">
<ReloadOutlined /> 重置
</Button>
</Space>
</div>
<!-- 表格 -->
<div class="table-card">
<Table
:columns="columns"
:data-source="logs"
:loading="loading"
:row-key="(record: AuditLog) => record.id"
:pagination="{ pageSize: 10, showSizeChanger: true, showTotal: (total: number) => `${total}` }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<Tag :color="getTypeColor(record.type)">
{{ getTypeLabel(record.type) }}
</Tag>
</template>
</template>
</Table>
</div>
</div>
</template>