feat: 添加操作日志

This commit is contained in:
2025-02-12 00:15:35 +08:00
parent a89bc1febc
commit 35b94cc875
8 changed files with 798 additions and 2 deletions

View File

@@ -0,0 +1,320 @@
<script setup lang="ts">
import dayjs from "dayjs";
import { ref, reactive, computed } from "vue";
import { useOperation } from "./hook";
import { getPickerShortcuts } from "../utils";
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { useI18n } from "vue-i18n";
import VueJsonPretty from "vue-json-pretty";
import "vue-json-pretty/lib/styles.css";
import View from "@iconify-icons/ep/view";
import Delete from "@iconify-icons/ep/delete";
import Refresh from "@iconify-icons/ep/refresh";
import { OperationLogInfo } from "types/monitor";
defineOptions({
name: "OperationLog"
});
const { t } = useI18n();
const formRef = ref();
const tableRef = ref();
/**
* 抽屉状态
*/
const drawerStatus = ref<boolean>(false);
// 折叠面板的激活项
const activeCollapse = ref(["requestinfo"]);
const logInfo = reactive<OperationLogInfo>({
id: "",
operation_name: "",
operation_type: 0,
request_path: "",
request_method: "",
request_params: "{}",
response_result: "{}",
host: "",
location: "",
browser: "",
os: "",
user_agent: "",
operator_id: "",
operator_name: "",
operator_nickname: "",
department_id: "",
department_name: "",
status: 1,
operation_time: "",
cost_time: 0
});
/**
* 操作日志详情展开
*/
const onDetailHandle = (row: OperationLogInfo) => {
activeCollapse.value = ["requestinfo"];
drawerStatus.value = true;
Object.assign(logInfo, row);
};
// 解析请求参数
const parsedRequestParams = computed(() => {
try {
return JSON.parse(logInfo.request_params);
} catch (e) {
return {};
}
});
// 解析响应结果
const parsedResponseResult = computed(() => {
try {
return JSON.parse(logInfo.response_result);
} catch (e) {
return {};
}
});
const {
form,
loading,
columns,
dataList,
pagination,
selectedNum,
onSearch,
clearAll,
resetForm,
onbatchDel,
getOperationName,
handleSizeChange,
onSelectionCancel,
handleCurrentChange,
handleSelectionChange
} = useOperation(tableRef);
</script>
<template>
<div class="main">
<el-form
ref="formRef"
:inline="true"
:model="form"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
>
<el-form-item label="所属模块" prop="module">
<el-input
v-model="form.module"
placeholder="请输入所属模块"
clearable
class="!w-[170px]"
/>
</el-form-item>
<el-form-item label="操作状态" prop="status">
<el-select
v-model="form.status"
placeholder="请选择"
clearable
class="!w-[150px]"
>
<el-option label="成功" value="1" />
<el-option label="失败" value="0" />
</el-select>
</el-form-item>
<el-form-item label="操作时间" prop="operatingTime">
<el-date-picker
v-model="form.operatingTime"
:shortcuts="getPickerShortcuts()"
type="datetimerange"
range-separator=""
start-placeholder="开始日期时间"
end-placeholder="结束日期时间"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:icon="useRenderIcon('ri:search-line')"
:loading="loading"
@click="onSearch"
>
{{ t("buttons:Search") }}
</el-button>
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
{{ t("buttons:Reset") }}
</el-button>
</el-form-item>
</el-form>
<PureTableBar title="操作日志" :columns="columns" @refresh="onSearch">
<template #buttons>
<!-- <el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll">
<template #reference>
<el-button type="danger" :icon="useRenderIcon(Delete)">
清空日志
</el-button>
</template>
</el-popconfirm> -->
</template>
<template v-slot="{ size, dynamicColumns }">
<div
v-if="selectedNum > 0"
v-motion-fade
class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center"
>
<div class="flex-auto">
<span
style="font-size: var(--el-font-size-base)"
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
>
已选 {{ selectedNum }}
</span>
<el-button type="primary" text @click="onSelectionCancel">
{{ t("buttons:Deselect") }}
</el-button>
</div>
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
<template #reference>
<el-button type="danger" text class="mr-1">
{{ t("buttons:DeleteInBatches") }}
</el-button>
</template>
</el-popconfirm>
</div>
<pure-table
ref="tableRef"
row-key="id"
align-whole="center"
table-layout="auto"
:loading="loading"
:size="size"
adaptive
:adaptiveConfig="{ offsetBottom: 108 }"
:data="dataList"
:columns="dynamicColumns"
:pagination="{ ...pagination, size }"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
@selection-change="handleSelectionChange"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #operation="{ row }">
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:icon="useRenderIcon(View)"
@click="onDetailHandle(row)"
>
{{ t("buttons:Details") }}
</el-button>
</template>
</pure-table>
</template>
</PureTableBar>
<el-drawer
v-model="drawerStatus"
title="操作日志详情"
:size="`50%`"
show-close
>
<el-collapse v-model="activeCollapse" :accordion="false">
<!-- 请求基本信息 -->
<el-collapse-item title="基本信息" name="requestinfo">
<el-descriptions title="日志基本信息" border>
<el-descriptions-item align="center" label="操作名称">{{
logInfo.operation_name
}}</el-descriptions-item>
<el-descriptions-item align="center" label="操作类型">{{
getOperationName(logInfo.operation_type)
}}</el-descriptions-item>
<el-descriptions-item align="center" label="请求路径">{{
logInfo.request_path
}}</el-descriptions-item>
<el-descriptions-item align="center" label="请求方法">{{
logInfo.request_method
}}</el-descriptions-item>
<el-descriptions-item align="center" label="操作人账号">{{
logInfo.operator_name
}}</el-descriptions-item>
<el-descriptions-item align="center" label="操作人昵称">{{
logInfo.operator_nickname
}}</el-descriptions-item>
<el-descriptions-item align="center" label="所属部门">{{
logInfo.department_name
}}</el-descriptions-item>
<el-descriptions-item align="center" label="操作时间">{{
dayjs(logInfo.operation_time).format("YYYY-MM-DD HH:mm:ss")
}}</el-descriptions-item>
<el-descriptions-item align="center" label="耗时"
>{{ logInfo.cost_time.toFixed(2) }} ms</el-descriptions-item
>
<el-descriptions-item align="center" label="请求IP">{{
logInfo.host
}}</el-descriptions-item>
<el-descriptions-item align="center" label="请求位置">{{
logInfo.location
}}</el-descriptions-item>
<el-descriptions-item align="center" label="请求浏览器">{{
logInfo.browser
}}</el-descriptions-item>
<el-descriptions-item align="center" label="请求系统">{{
logInfo.os
}}</el-descriptions-item>
<el-descriptions-item align="center" label="请求头">{{
logInfo.user_agent
}}</el-descriptions-item>
</el-descriptions>
</el-collapse-item>
<!-- 请求参数 -->
<el-collapse-item title="请求参数" name="requestParams">
<vue-json-pretty
:data="parsedRequestParams"
showLineNumber
collapsedOnClickBrackets
showSelectController
showIcon
virtual
showLength
/>
</el-collapse-item>
<!-- 响应结果 -->
<el-collapse-item title="响应结果" name="responseResult">
<vue-json-pretty
:data="parsedResponseResult"
showLineNumber
collapsedOnClickBrackets
showSelectController
showIcon
virtual
showLength
/>
</el-collapse-item>
</el-collapse>
</el-drawer>
</div>
</template>
<style lang="scss" scoped>
:deep(.el-dropdown-menu__item i) {
margin: 0;
}
.main-content {
margin: 24px 24px 0 !important;
}
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>