新增:个人中心新增查询操作日志功能,优化日志表结构,并支持关闭记录内网 IP 操作

This commit is contained in:
2023-01-16 00:18:53 +08:00
parent 79c741b68e
commit f4ea2d44d6
36 changed files with 1272 additions and 119 deletions

View File

@@ -0,0 +1,34 @@
import axios from 'axios';
import qs from 'query-string';
export interface OperationLogRecord {
logId: string;
description: string;
result: number,
requestIp: string,
location: string,
browser: string,
createUserString: string;
createTime: string;
}
export interface OperationLogParams extends Partial<OperationLogRecord> {
page: number;
size: number;
sort: Array<string>;
uid: string;
}
export interface OperationLogListRes {
list: OperationLogRecord[];
total: number;
}
export function queryOperationLogList(params: OperationLogParams) {
return axios.get<OperationLogListRes>('/monitor/log/operation', {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}

View File

@@ -90,5 +90,8 @@ body {
&.pass {
background-color: rgb(var(--green-6));
}
&.fail {
background-color: rgb(var(--red-6));
}
}
}

View File

@@ -13,7 +13,7 @@ import useAppStore from '../app';
const useLoginStore = defineStore('user', {
state: (): UserState => ({
userId: 1,
userId: '',
username: '',
nickname: '',
gender: 0,

View File

@@ -1,6 +1,6 @@
export type RoleType = '' | '*' | 'admin' | 'user';
export interface UserState {
userId: number;
userId: string;
username: string;
nickname: string;
gender: number;

View File

@@ -133,7 +133,7 @@
captchaImgBase64.value = data.img
}
onMounted(() => {
getCaptcha()
getCaptcha();
})
// 记住我

View File

@@ -0,0 +1,142 @@
<template>
<div class="container">
<a-table
row-key="id"
:loading="loading"
:pagination="pagination"
:columns="columns"
:data="renderData"
:bordered="false"
size="large"
@page-change="onPageChange"
>
<template #index="{ rowIndex }">
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
</template>
<template #result="{ record }">
<a-space v-if="record.result === 1">
<a-tag color="green">
<span class="circle pass"></span>
成功
</a-tag>
</a-space>
<a-space v-else>
<a-tag color="red">
<span class="circle fail"></span>
失败
</a-tag>
</a-space>
</template>
<template #pagination-left>
<a-tooltip content="刷新">
<div class="action-icon" @click="onRefresh">
<icon-refresh size="18" />
</div>
</a-tooltip>
</template>
</a-table>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, reactive } from "vue";
import { useLoginStore } from '@/store';
import useLoading from '@/hooks/loading';
import { queryOperationLogList, OperationLogRecord, OperationLogParams } from '@/api/monitor/operation-log';
import { Pagination } from '@/types/global';
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
const { loading, setLoading } = useLoading(true);
const loginStore = useLoginStore();
const renderData = ref<OperationLogRecord[]>([]);
const basePagination: Pagination = {
current: 1,
pageSize: 10,
};
const pagination = reactive({
...basePagination,
});
const columns = computed<TableColumnData[]>(() => [
{
title: '序号',
dataIndex: 'index',
slotName: 'index',
},
{
title: '操作时间',
dataIndex: 'createTime',
},
{
title: '操作内容',
dataIndex: 'description',
},
{
title: '操作结果',
dataIndex: 'result',
slotName: 'result',
},
{
title: '操作IP',
dataIndex: 'requestIp',
},
{
title: '操作地点',
dataIndex: 'location',
},
{
title: '浏览器',
dataIndex: 'browser',
},
]);
const fetchData = async (
params: OperationLogParams = { uid: loginStore.userId, page: 1, size: 10, sort: ['createTime,desc'] }
) => {
setLoading(true);
try {
const { data } = await queryOperationLogList(params);
renderData.value = data.list;
pagination.current = params.page;
pagination.total = data.total;
} catch (err) {
// you can report use errorHandler or other
} finally {
setLoading(false);
}
};
const onPageChange = (current: number) => {
fetchData({ uid: loginStore.userId, page: current, size: pagination.pageSize, sort: ['createTime,desc'] });
};
const onRefresh = () => {
fetchData({
uid: loginStore.userId,
page: pagination.current,
size: pagination.pageSize,
sort: ['createTime,desc'],
} as unknown as OperationLogParams);
};
fetchData();
</script>
<style scoped lang="less">
.container {
padding: 0 20px 20px 20px;
}
:deep(.arco-table-th) {
&:last-child {
.arco-table-th-item-title {
margin-left: 16px;
}
}
}
.action-icon {
cursor: pointer;
}
.action-icon:hover {
color: #0960bd;
}
</style>

View File

@@ -15,6 +15,9 @@
<a-tab-pane key="2" :title="$t('userCenter.tab.securitySettings')">
<SecuritySettings />
</a-tab-pane>
<a-tab-pane key="3" :title="$t('userCenter.tab.operationLog')">
<OperationLog />
</a-tab-pane>
</a-tabs>
</a-col>
</a-row>
@@ -25,6 +28,7 @@
import UserPanel from './components/user-panel.vue';
import BasicInfo from './components/basic-info.vue';
import SecuritySettings from './components/security-settings.vue';
import OperationLog from './components/operation-log.vue';
</script>
<script lang="ts">

View File

@@ -2,6 +2,7 @@ export default {
'menu.user.center': 'User Center',
'userCenter.tab.basicInfo': 'Basic Information',
'userCenter.tab.securitySettings': 'Security Settings',
'userCenter.tab.operationLog': 'Operation Log',
// user-panel
'userCenter.panel.avatar': 'Avatar',

View File

@@ -2,6 +2,7 @@ export default {
'menu.user.center': '个人中心',
'userCenter.tab.basicInfo': '基础信息',
'userCenter.tab.securitySettings': '安全设置',
'userCenter.tab.operationLog': '操作日志',
// user-panel
'userCenter.panel.avatar': '头像',