From 6c000a9f933f43a072d52790efc22bbcc8238eee Mon Sep 17 00:00:00 2001
From: hwh <332078369@qq.com>
Date: 星期五, 23 八月 2024 09:08:01 +0800
Subject: [PATCH] 日志文件

---
 .gitignore                                 |    2 
 Web/src/views/system/log/vislog/index.vue  |  183 ++++++++++++
 Web/src/views/system/log/exlog/index.vue   |  279 ++++++++++++++++++
 Web/src/views/system/log/oplog/index.vue   |  270 ++++++++++++++++++
 Web/src/views/system/log/difflog/index.vue |  147 +++++++++
 5 files changed, 880 insertions(+), 1 deletions(-)

diff --git a/.gitignore b/.gitignore
index 9491a2f..10d1295 100644
--- a/.gitignore
+++ b/.gitignore
@@ -141,7 +141,6 @@
 # AxoCover is a Code Coverage Tool
 .axoCover/*
 !.axoCover/settings.json
-
 # Coverlet is a free, cross platform Code Coverage Tool
 coverage*.json
 coverage*.xml
@@ -200,6 +199,7 @@
 **/[Pp]ackages/*
 # except build/, which is used as an MSBuild target.
 !**/[Pp]ackages/build/
+!**/system/log/
 # Uncomment if necessary however generally it will be regenerated when needed
 #!**/[Pp]ackages/repositories.config
 # NuGet v3's project.json files produces more ignorable files
diff --git a/Web/src/views/system/log/difflog/index.vue b/Web/src/views/system/log/difflog/index.vue
new file mode 100644
index 0000000..6e90187
--- /dev/null
+++ b/Web/src/views/system/log/difflog/index.vue
@@ -0,0 +1,147 @@
+<template>
+	<div class="sys-difflog-container">
+		<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
+			<el-form :model="state.queryParams" ref="queryForm" :inline="true">
+				<el-form-item label="寮�濮嬫椂闂�">
+					<el-date-picker v-model="state.queryParams.startTime" type="datetime" placeholder="寮�濮嬫椂闂�" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item label="缁撴潫鏃堕棿">
+					<el-date-picker v-model="state.queryParams.endTime" type="datetime" placeholder="缁撴潫鏃堕棿" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item>
+					<el-button-group>
+						<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysDifflog:page'"> 鏌ヨ </el-button>
+						<el-button icon="ele-Refresh" @click="resetQuery"> 閲嶇疆 </el-button>
+					</el-button-group>
+				</el-form-item>
+				<el-form-item>
+					<el-button icon="ele-DeleteFilled" type="danger" @click="clearLog" v-auth="'sysDifflog:clear'"> 娓呯┖ </el-button>
+				</el-form-item>
+			</el-form>
+		</el-card>
+
+		<el-card class="full-table" shadow="hover" style="margin-top: 5px">
+			<el-table :data="state.logData" style="width: 100%" v-loading="state.loading" border>
+				<el-table-column type="index" label="搴忓彿" width="55" align="center" />
+				<el-table-column prop="diffType" label="宸紓鎿嶄綔" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="sql" label="Sql璇彞" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="parameters" label="鍙傛暟" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="elapsed" label="鑰楁椂(ms)" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="message" label="鏃ュ織娑堟伅" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="beforeData" label="鎿嶄綔鍓嶈褰�" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="afterData" label="鎿嶄綔鍚庤褰�" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="businessData" label="涓氬姟瀵硅薄" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="createTime" label="鎿嶄綔鏃堕棿" align="center" show-overflow-tooltip />
+			</el-table>
+			<el-pagination
+				v-model:currentPage="state.tableParams.page"
+				v-model:page-size="state.tableParams.pageSize"
+				:total="state.tableParams.total"
+				:page-sizes="[10, 20, 50, 100]"
+				size="small"
+				background
+				@size-change="handleSizeChange"
+				@current-change="handleCurrentChange"
+				layout="total, sizes, prev, pager, next, jumper"
+			/>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts" setup name="sysDiffLog">
+import { onMounted, reactive } from 'vue';
+import { ElMessage } from 'element-plus';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysLogDiffApi } from '/@/api-services/api';
+import { SysLogDiff } from '/@/api-services/models';
+
+const state = reactive({
+	loading: false,
+	queryParams: {
+		startTime: undefined,
+		endTime: undefined,
+	},
+	tableParams: {
+		page: 1,
+		pageSize: 10,
+		total: 0 as any,
+	},
+	logData: [] as Array<SysLogDiff>,
+});
+
+onMounted(async () => {
+	handleQuery();
+});
+
+// 鏌ヨ鎿嶄綔
+const handleQuery = async () => {
+	if (state.queryParams.startTime == null) state.queryParams.startTime = undefined;
+	if (state.queryParams.endTime == null) state.queryParams.endTime = undefined;
+
+	state.loading = true;
+	let params = Object.assign(state.queryParams, state.tableParams);
+	var res = await getAPI(SysLogDiffApi).apiSysLogDiffPagePost(params);
+	state.logData = res.data.result?.items ?? [];
+	state.tableParams.total = res.data.result?.total;
+	state.loading = false;
+};
+
+// 閲嶇疆鎿嶄綔
+const resetQuery = () => {
+	state.queryParams.startTime = undefined;
+	state.queryParams.endTime = undefined;
+	handleQuery();
+};
+
+// 娓呯┖鏃ュ織
+const clearLog = async () => {
+	state.loading = true;
+	await getAPI(SysLogDiffApi).apiSysLogDiffClearPost();
+	state.loading = false;
+
+	ElMessage.success('娓呯┖鎴愬姛');
+	handleQuery();
+};
+
+// 鏀瑰彉椤甸潰瀹归噺
+const handleSizeChange = (val: number) => {
+	state.tableParams.pageSize = val;
+	handleQuery();
+};
+
+// 鏀瑰彉椤电爜搴忓彿
+const handleCurrentChange = (val: number) => {
+	state.tableParams.page = val;
+	handleQuery();
+};
+
+const shortcuts = [
+	{
+		text: '浠婂ぉ',
+		value: new Date(),
+	},
+	{
+		text: '鏄ㄥぉ',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24);
+			return date;
+		},
+	},
+	{
+		text: '涓婂懆',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+			return date;
+		},
+	},
+];
+</script>
+
+<style lang="scss" scoped>
+.el-popper {
+	max-width: 60%;
+}
+</style>
diff --git a/Web/src/views/system/log/exlog/index.vue b/Web/src/views/system/log/exlog/index.vue
new file mode 100644
index 0000000..0349115
--- /dev/null
+++ b/Web/src/views/system/log/exlog/index.vue
@@ -0,0 +1,279 @@
+<template>
+	<div class="sys-exlog-container" v-loading="state.loading">
+		<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
+			<el-form :model="state.queryParams" ref="queryForm" :inline="true">
+				<el-form-item label="寮�濮嬫椂闂�">
+					<el-date-picker v-model="state.queryParams.startTime" type="datetime" placeholder="寮�濮嬫椂闂�" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item label="缁撴潫鏃堕棿">
+					<el-date-picker v-model="state.queryParams.endTime" type="datetime" placeholder="缁撴潫鏃堕棿" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item label="妯″潡鍚嶇О">
+					<el-input v-model="state.queryParams.controllerName" placeholder="妯″潡鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="鏂规硶鍚嶇О">
+					<el-input v-model="state.queryParams.actionName" placeholder="鏂规硶鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="璐﹀彿鍚嶇О">
+					<el-input v-model="state.queryParams.account" placeholder="璐﹀彿鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="鐘舵��">
+					<el-select v-model="state.queryParams.status" placeholder="鐘舵��" clearable>
+						<el-option label="鎴愬姛" :value="200" />
+						<el-option label="澶辫触" :value="400" />
+					</el-select>
+				</el-form-item>
+				<el-form-item label="鑰楁椂">
+					<el-input v-model="state.queryParams.elapsed" placeholder="鑰楁椂>?MS" clearable />
+				</el-form-item>
+				<el-form-item label="IP鍦板潃">
+					<el-input v-model="state.queryParams.remoteIp" placeholder="IP鍦板潃" clearable />
+				</el-form-item>
+				<el-form-item>
+					<el-button-group>
+						<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysExlog:page'"> 鏌ヨ </el-button>
+						<el-button icon="ele-Refresh" @click="resetQuery"> 閲嶇疆 </el-button>
+					</el-button-group>
+				</el-form-item>
+				<el-form-item>
+					<el-button icon="ele-DeleteFilled" type="danger" @click="clearLog" v-auth="'sysExlog:clear'"> 娓呯┖ </el-button>
+					<el-button icon="ele-FolderOpened" @click="exportLog" v-auth="'sysExlog:export'"> 瀵煎嚭 </el-button>
+				</el-form-item>
+			</el-form>
+		</el-card>
+
+		<el-card class="full-table" shadow="hover" style="margin-top: 5px">
+			<el-table :data="state.logData" @sort-change="sortChange" style="width: 100%" border :row-class-name="tableRowClassName">
+				<el-table-column type="index" label="搴忓彿" width="55" align="center" />
+				<el-table-column prop="controllerName" label="妯″潡鍚嶇О" width="100" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="displayTitle" label="鏄剧ず鍚嶇О" width="150" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="actionName" label="鏂规硶鍚嶇О" width="100" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="httpMethod" label="璇锋眰鏂瑰紡" width="90" align="center" show-overflow-tooltip />
+				<el-table-column prop="requestUrl" label="璇锋眰鍦板潃" width="300" header-align="center" show-overflow-tooltip />
+				<!-- <el-table-column prop="requestParam" label="璇锋眰鍙傛暟" show-overflow-tooltip />
+				<el-table-column prop="returnResult" label="杩斿洖缁撴灉" show-overflow-tooltip /> -->
+				<el-table-column prop="logLevel" label="绾у埆" width="70" align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag v-if="scope.row.logLevel === 1">璋冭瘯</el-tag>
+						<el-tag v-else-if="scope.row.logLevel === 2">娑堟伅</el-tag>
+						<el-tag v-else-if="scope.row.logLevel === 3">璀﹀憡</el-tag>
+						<el-tag v-else-if="scope.row.logLevel === 4">閿欒</el-tag>
+						<el-tag v-else>鍏朵粬</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="eventId" label="浜嬩欢Id" width="70" align="center" show-overflow-tooltip />
+				<el-table-column prop="threadId" label="绾跨▼Id" sortable="custom" width="90" align="center" show-overflow-tooltip />
+				<el-table-column prop="traceId" label="璇锋眰璺熻釜Id" width="150" header-align="center" sortable="custom" show-overflow-tooltip />
+				<el-table-column prop="account" label="璐﹀彿鍚嶇О" width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="realName" label="鐪熷疄濮撳悕" width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="remoteIp" label="IP鍦板潃" width="120" align="center" show-overflow-tooltip />
+				<el-table-column prop="location" label="鐧诲綍鍦扮偣" width="150" align="center" show-overflow-tooltip />
+				<el-table-column prop="longitude" label="缁忓害" min-width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="latitude" label="绾害" min-width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="browser" label="娴忚鍣�" width="160" align="center" show-overflow-tooltip />
+				<el-table-column prop="os" label="鎿嶄綔绯荤粺" width="120" align="center" show-overflow-tooltip />
+				<el-table-column prop="status" label="鐘舵��" width="70" align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag type="success" v-if="scope.row.status === '200'">鎴愬姛</el-tag>
+						<el-tag type="danger" v-else>澶辫触</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="elapsed" label="鑰楁椂(ms)" width="90" align="center" show-overflow-tooltip />
+				<el-table-column prop="exception" label="寮傚父瀵硅薄" width="150" header-align="center" show-overflow-tooltip />
+				<!-- <el-table-column prop="message" label="鏃ュ織娑堟伅" width="160" fixed="right" show-overflow-tooltip /> -->
+				<el-table-column prop="logDateTime" label="鏃ュ織鏃堕棿" width="160" align="center" fixed="right" show-overflow-tooltip />
+				<el-table-column label="鎿嶄綔" width="80" align="center" fixed="right" show-overflow-tooltip>
+					<template #default="scope">
+						<el-button icon="ele-InfoFilled" size="small" text type="primary" @click="viewDetail(scope.row)" v-auth="'sysOplog:page'">璇︽儏 </el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<el-pagination
+				v-model:currentPage="state.tableParams.page"
+				v-model:page-size="state.tableParams.pageSize"
+				:total="state.tableParams.total"
+				:page-sizes="[10, 20, 50, 100]"
+				size="small"
+				background
+				@size-change="handleSizeChange"
+				@current-change="handleCurrentChange"
+				layout="total, sizes, prev, pager, next, jumper"
+			/>
+		</el-card>
+		<el-dialog v-model="state.dialogVisible" draggable fullscreen>
+			<template #header>
+				<div style="color: #fff">
+					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Document /> </el-icon>
+					<span> 鏃ュ織璇︽儏 </span>
+				</div>
+			</template>
+			<pre v-loading="state.loadingDetail">{{ state.content }}</pre>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="sysExLog">
+import { onMounted, reactive } from 'vue';
+import { ElMessage } from 'element-plus';
+import { downloadByData, getFileName } from '/@/utils/download';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysLogExApi } from '/@/api-services/api';
+import { SysLogEx } from '/@/api-services/models';
+
+const state = reactive({
+	loading: false,
+	loadingDetail: false,
+	queryParams: {
+		startTime: undefined,
+		endTime: undefined,
+		status: undefined,
+		controllerName: undefined,
+		actionName: undefined,
+		account: undefined,
+		elapsed: undefined,
+		remoteIp: undefined,
+	},
+	tableParams: {
+		page: 1,
+		pageSize: 20,
+		field: 'createTime', // 榛樿鐨勬帓搴忓瓧娈�
+		order: 'descending', // 鎺掑簭鏂瑰悜
+		descStr: 'descending', // 闄嶅簭鎺掑簭鐨勫叧閿瓧绗�
+		total: 0 as any,
+	},
+	logData: [] as Array<SysLogEx>,
+	dialogVisible: false,
+	content: '',
+});
+
+onMounted(async () => {
+	handleQuery();
+});
+
+// 鏌ヨ鎿嶄綔
+const handleQuery = async () => {
+	if (state.queryParams.startTime == null) state.queryParams.startTime = undefined;
+	if (state.queryParams.endTime == null) state.queryParams.endTime = undefined;
+	if (state.queryParams.status == null) state.queryParams.status = undefined;
+	if (state.queryParams.controllerName == null) state.queryParams.controllerName = undefined;
+	if (state.queryParams.actionName == null) state.queryParams.actionName = undefined;
+	if (state.queryParams.account == null) state.queryParams.account = undefined;
+	if (state.queryParams.elapsed == null) state.queryParams.elapsed = undefined;
+	if (state.queryParams.remoteIp == null) state.queryParams.remoteIp = undefined;
+
+	state.loading = true;
+	let params = Object.assign(state.queryParams, state.tableParams);
+	var res = await getAPI(SysLogExApi).apiSysLogExPagePost(params);
+	state.logData = res.data.result?.items ?? [];
+	state.tableParams.total = res.data.result?.total;
+	state.loading = false;
+};
+
+// 閲嶇疆鎿嶄綔
+const resetQuery = () => {
+	state.queryParams.startTime = undefined;
+	state.queryParams.endTime = undefined;
+	state.queryParams.status = undefined;
+	state.queryParams.controllerName = undefined;
+	state.queryParams.actionName = undefined;
+	state.queryParams.account = undefined;
+	state.queryParams.elapsed = undefined;
+	state.queryParams.remoteIp = undefined;
+	handleQuery();
+};
+
+// 娓呯┖鏃ュ織
+const clearLog = async () => {
+	state.loading = true;
+	await getAPI(SysLogExApi).apiSysLogExClearPost();
+	state.loading = false;
+
+	ElMessage.success('娓呯┖鎴愬姛');
+	handleQuery();
+};
+
+// 瀵煎嚭鏃ュ織
+const exportLog = async () => {
+	state.loading = true;
+	var res = await getAPI(SysLogExApi).apiSysLogExExportPost(state.queryParams, { responseType: 'blob' });
+	state.loading = false;
+
+	var fileName = getFileName(res.headers);
+	downloadByData(res.data as any, fileName);
+};
+
+// 鏀瑰彉椤甸潰瀹归噺
+const handleSizeChange = (val: number) => {
+	state.tableParams.pageSize = val;
+	handleQuery();
+};
+
+// 鏀瑰彉椤电爜搴忓彿
+const handleCurrentChange = (val: number) => {
+	state.tableParams.page = val;
+	handleQuery();
+};
+
+// 鏌ョ湅璇︽儏
+const viewDetail = async (row: any) => {
+	state.content = '';
+	state.dialogVisible = true;
+	state.loadingDetail = true;
+	var res = await getAPI(SysLogExApi).apiSysLogExDetailIdGet(row.id);
+	row.message = res.data.result?.message ?? '';
+	state.content = row.message;
+	state.loadingDetail = false;
+};
+
+// 璁剧疆琛岄鑹�
+const tableRowClassName = (row: any) => {
+	return row.row.exception != null ? 'warning-row' : '';
+};
+
+const shortcuts = [
+	{
+		text: '浠婂ぉ',
+		value: new Date(),
+	},
+	{
+		text: '鏄ㄥぉ',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24);
+			return date;
+		},
+	},
+	{
+		text: '涓婂懆',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+			return date;
+		},
+	},
+];
+
+// 鍒楁帓搴�
+const sortChange = (column: any) => {
+	state.tableParams.field = column.prop;
+	state.tableParams.order = column.order;
+	handleQuery();
+};
+</script>
+
+<style lang="scss" scoped>
+.el-popper {
+	max-width: 60%;
+}
+pre {
+	white-space: break-spaces;
+	line-height: 20px;
+}
+.el-table .warning-row {
+	--el-table-tr-bg-color: var(--el-color-warning-light-9);
+}
+.el-table .success-row {
+	--el-table-tr-bg-color: var(--el-color-success-light-9);
+}
+</style>
diff --git a/Web/src/views/system/log/oplog/index.vue b/Web/src/views/system/log/oplog/index.vue
new file mode 100644
index 0000000..799bce0
--- /dev/null
+++ b/Web/src/views/system/log/oplog/index.vue
@@ -0,0 +1,270 @@
+<template>
+	<div class="sys-oplog-container" v-loading="state.loading">
+		<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
+			<el-form :model="state.queryParams" ref="queryForm" :inline="true">
+				<el-form-item label="寮�濮嬫椂闂�">
+					<el-date-picker v-model="state.queryParams.startTime" type="datetime" placeholder="寮�濮嬫椂闂�" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item label="缁撴潫鏃堕棿">
+					<el-date-picker v-model="state.queryParams.endTime" type="datetime" placeholder="缁撴潫鏃堕棿" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item label="妯″潡鍚嶇О">
+					<el-input v-model="state.queryParams.controllerName" placeholder="妯″潡鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="鏂规硶鍚嶇О">
+					<el-input v-model="state.queryParams.actionName" placeholder="鏂规硶鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="璐﹀彿鍚嶇О">
+					<el-input v-model="state.queryParams.account" placeholder="璐﹀彿鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="鑰楁椂">
+					<el-input v-model="state.queryParams.elapsed" placeholder="鑰楁椂>?MS" clearable />
+				</el-form-item>
+				<el-form-item label="IP鍦板潃">
+					<el-input v-model="state.queryParams.remoteIp" placeholder="IP鍦板潃" clearable />
+				</el-form-item>
+				<el-form-item>
+					<el-button-group>
+						<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysOplog:page'"> 鏌ヨ </el-button>
+						<el-button icon="ele-Refresh" @click="resetQuery"> 閲嶇疆 </el-button>
+					</el-button-group>
+				</el-form-item>
+				<el-form-item>
+					<el-button icon="ele-DeleteFilled" type="danger" @click="clearLog" v-auth="'sysOplog:clear'"> 娓呯┖ </el-button>
+					<el-button icon="ele-FolderOpened" @click="exportLog" v-auth="'sysOplog:export'"> 瀵煎嚭 </el-button>
+				</el-form-item>
+			</el-form>
+		</el-card>
+
+		<el-card class="full-table" shadow="hover" style="margin-top: 5px">
+			<el-table :data="state.logData" @sort-change="sortChange" style="width: 100%" border :row-class-name="tableRowClassName">
+				<el-table-column type="index" label="搴忓彿" width="55" align="center" />
+				<el-table-column prop="controllerName" label="妯″潡鍚嶇О" min-width="120" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="displayTitle" label="鏄剧ず鍚嶇О" width="170" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="actionName" label="鏂规硶鍚嶇О" width="150" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="httpMethod" label="璇锋眰鏂瑰紡" width="90" align="center" show-overflow-tooltip />
+				<el-table-column prop="requestUrl" label="璇锋眰鍦板潃" width="300" header-align="center" show-overflow-tooltip />
+				<!-- <el-table-column prop="requestParam" label="璇锋眰鍙傛暟" show-overflow-tooltip />
+				<el-table-column prop="returnResult" label="杩斿洖缁撴灉" show-overflow-tooltip /> -->
+				<el-table-column prop="logLevel" label="绾у埆" width="70" align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag v-if="scope.row.logLevel === 1">璋冭瘯</el-tag>
+						<el-tag v-else-if="scope.row.logLevel === 2">娑堟伅</el-tag>
+						<el-tag v-else-if="scope.row.logLevel === 3">璀﹀憡</el-tag>
+						<el-tag v-else-if="scope.row.logLevel === 4">閿欒</el-tag>
+						<el-tag v-else>鍏朵粬</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="eventId" label="浜嬩欢Id" width="70" align="center" show-overflow-tooltip />
+				<el-table-column prop="threadId" label="绾跨▼Id" sortable="custom" width="90" align="center" show-overflow-tooltip />
+				<el-table-column prop="traceId" label="璇锋眰璺熻釜Id" width="150" header-align="center" sortable="custom" show-overflow-tooltip />
+				<el-table-column prop="account" label="璐﹀彿鍚嶇О" width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="realName" label="鐪熷疄濮撳悕" width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="remoteIp" label="IP鍦板潃" min-width="120" align="center" show-overflow-tooltip />
+				<el-table-column prop="location" label="鐧诲綍鍦扮偣" min-width="150" align="center" show-overflow-tooltip />
+				<el-table-column prop="longitude" label="缁忓害" min-width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="latitude" label="绾害" min-width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="browser" label="娴忚鍣�" min-width="150" align="center" show-overflow-tooltip />
+				<el-table-column prop="os" label="鎿嶄綔绯荤粺" width="120" align="center" show-overflow-tooltip />
+				<el-table-column prop="status" label="鐘舵��" width="70" align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag type="success" v-if="scope.row.status === '200'">鎴愬姛</el-tag>
+						<el-tag type="danger" v-else>澶辫触</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="elapsed" label="鑰楁椂(ms)" width="90" align="center" show-overflow-tooltip />
+				<!-- <el-table-column prop="exception" label="寮傚父瀵硅薄" width="150" show-overflow-tooltip /> -->
+				<!-- <el-table-column prop="message" label="鏃ュ織娑堟伅" width="160" fixed="right" show-overflow-tooltip /> -->
+				<el-table-column prop="logDateTime" label="鏃ュ織鏃堕棿" width="160" align="center" fixed="right" show-overflow-tooltip />
+				<el-table-column label="鎿嶄綔" width="80" align="center" fixed="right" show-overflow-tooltip>
+					<template #default="scope">
+						<el-button icon="ele-InfoFilled" size="small" text type="primary" @click="viewDetail(scope.row)" v-auth="'sysOplog:page'">璇︽儏 </el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<el-pagination
+				v-model:currentPage="state.tableParams.page"
+				v-model:page-size="state.tableParams.pageSize"
+				:total="state.tableParams.total"
+				:page-sizes="[10, 20, 50, 100]"
+				size="small"
+				background
+				@size-change="handleSizeChange"
+				@current-change="handleCurrentChange"
+				layout="total, sizes, prev, pager, next, jumper"
+			/>
+		</el-card>
+		<el-dialog v-model="state.dialogVisible" draggable fullscreen>
+			<template #header>
+				<div style="color: #fff">
+					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Document /> </el-icon>
+					<span> 鏃ュ織璇︽儏 </span>
+				</div>
+			</template>
+			<pre v-loading="state.loadingDetail">{{ state.content }}</pre>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="sysOpLog">
+import { onMounted, reactive } from 'vue';
+import { ElMessage } from 'element-plus';
+import { downloadByData, getFileName } from '/@/utils/download';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysLogOpApi } from '/@/api-services/api';
+import { SysLogOp } from '/@/api-services/models';
+
+const state = reactive({
+	loading: false,
+	loadingDetail: false,
+	queryParams: {
+		startTime: undefined,
+		endTime: undefined,
+		controllerName: undefined,
+		actionName: undefined,
+		account: undefined,
+		elapsed: undefined,
+		remoteIp: undefined,
+	},
+	tableParams: {
+		page: 1,
+		pageSize: 20,
+		field: 'createTime', // 榛樿鐨勬帓搴忓瓧娈�
+		order: 'descending', // 鎺掑簭鏂瑰悜
+		descStr: 'descending', // 闄嶅簭鎺掑簭鐨勫叧閿瓧绗�
+		total: 0 as any,
+	},
+	logData: [] as Array<SysLogOp>,
+	dialogVisible: false,
+	content: '',
+});
+
+onMounted(async () => {
+	handleQuery();
+});
+
+// 鏌ヨ鎿嶄綔
+const handleQuery = async () => {
+	if (state.queryParams.startTime == null) state.queryParams.startTime = undefined;
+	if (state.queryParams.endTime == null) state.queryParams.endTime = undefined;
+	if (state.queryParams.controllerName == null) state.queryParams.controllerName = undefined;
+	if (state.queryParams.actionName == null) state.queryParams.actionName = undefined;
+	if (state.queryParams.account == null) state.queryParams.account = undefined;
+	if (state.queryParams.elapsed == null) state.queryParams.elapsed = undefined;
+	if (state.queryParams.remoteIp == null) state.queryParams.remoteIp = undefined;
+
+	state.loading = true;
+	let params = Object.assign(state.queryParams, state.tableParams);
+	var res = await getAPI(SysLogOpApi).apiSysLogOpPagePost(params);
+	state.logData = res.data.result?.items ?? [];
+	state.tableParams.total = res.data.result?.total;
+	state.loading = false;
+};
+
+// 閲嶇疆鎿嶄綔
+const resetQuery = () => {
+	state.queryParams.startTime = undefined;
+	state.queryParams.endTime = undefined;
+	state.queryParams.controllerName = undefined;
+	state.queryParams.actionName = undefined;
+	state.queryParams.account = undefined;
+	state.queryParams.elapsed = undefined;
+	state.queryParams.remoteIp = undefined;
+	handleQuery();
+};
+
+// 娓呯┖鏃ュ織
+const clearLog = async () => {
+	state.loading = true;
+	await getAPI(SysLogOpApi).apiSysLogOpClearPost();
+	state.loading = false;
+
+	ElMessage.success('娓呯┖鎴愬姛');
+	handleQuery();
+};
+
+// 瀵煎嚭鏃ュ織
+const exportLog = async () => {
+	state.loading = true;
+	var res = await getAPI(SysLogOpApi).apiSysLogOpExportPost(state.queryParams, { responseType: 'blob' });
+	state.loading = false;
+
+	var fileName = getFileName(res.headers);
+	downloadByData(res.data as any, fileName);
+};
+
+// 鏀瑰彉椤甸潰瀹归噺
+const handleSizeChange = (val: number) => {
+	state.tableParams.pageSize = val;
+	handleQuery();
+};
+
+// 鏀瑰彉椤电爜搴忓彿
+const handleCurrentChange = (val: number) => {
+	state.tableParams.page = val;
+	handleQuery();
+};
+
+// 鏌ョ湅璇︽儏
+const viewDetail = async (row: any) => {
+	state.content = '';
+	state.dialogVisible = true;
+	state.loadingDetail = true;
+	var res = await getAPI(SysLogOpApi).apiSysLogOpDetailIdGet(row.id);
+	row.message = res.data.result?.message ?? '';
+	state.content = row.message;
+	state.loadingDetail = false;
+};
+
+// 璁剧疆琛岄鑹�
+const tableRowClassName = (row: any) => {
+	return row.row.exception != null ? 'warning-row' : '';
+};
+
+const shortcuts = [
+	{
+		text: '浠婂ぉ',
+		value: new Date(),
+	},
+	{
+		text: '鏄ㄥぉ',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24);
+			return date;
+		},
+	},
+	{
+		text: '涓婂懆',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+			return date;
+		},
+	},
+];
+
+// 鍒楁帓搴�
+const sortChange = (column: any) => {
+	state.tableParams.field = column.prop;
+	state.tableParams.order = column.order;
+	handleQuery();
+};
+</script>
+
+<style lang="scss" scoped>
+.el-popper {
+	max-width: 60%;
+}
+pre {
+	white-space: break-spaces;
+	line-height: 20px;
+}
+.el-table .warning-row {
+	--el-table-tr-bg-color: var(--el-color-warning-light-9);
+}
+.el-table .success-row {
+	--el-table-tr-bg-color: var(--el-color-success-light-9);
+}
+</style>
diff --git a/Web/src/views/system/log/vislog/index.vue b/Web/src/views/system/log/vislog/index.vue
new file mode 100644
index 0000000..28f0c1a
--- /dev/null
+++ b/Web/src/views/system/log/vislog/index.vue
@@ -0,0 +1,183 @@
+<template>
+	<div class="sys-vislog-container">
+		<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
+			<el-form :model="state.queryParams" ref="queryForm" :inline="true">
+				<el-form-item label="寮�濮嬫椂闂�">
+					<el-date-picker v-model="state.queryParams.startTime" type="datetime" placeholder="寮�濮嬫椂闂�" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item label="缁撴潫鏃堕棿" prop="code">
+					<el-date-picker v-model="state.queryParams.endTime" type="datetime" placeholder="缁撴潫鏃堕棿" value-format="YYYY-MM-DD HH:mm:ss" :shortcuts="shortcuts" />
+				</el-form-item>
+				<el-form-item label="鏂规硶鍚嶇О">
+					<el-input v-model="state.queryParams.actionName" placeholder="鏂规硶鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="璐﹀彿鍚嶇О">
+					<el-input v-model="state.queryParams.account" placeholder="璐﹀彿鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="鐘舵��">
+					<el-select v-model="state.queryParams.status" placeholder="鐘舵��" clearable>
+						<el-option label="鎴愬姛" :value="200" />
+						<el-option label="澶辫触" :value="400" />
+					</el-select>
+				</el-form-item>
+				<el-form-item label="鑰楁椂">
+					<el-input v-model="state.queryParams.elapsed" placeholder="鑰楁椂>?MS" clearable />
+				</el-form-item>
+				<el-form-item label="IP鍦板潃">
+					<el-input v-model="state.queryParams.remoteIp" placeholder="IP鍦板潃" clearable />
+				</el-form-item>
+				<el-form-item>
+					<el-button-group>
+						<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysVislog:page'"> 鏌ヨ </el-button>
+						<el-button icon="ele-Refresh" @click="resetQuery"> 閲嶇疆 </el-button>
+					</el-button-group>
+				</el-form-item>
+				<el-form-item>
+					<el-button icon="ele-DeleteFilled" type="danger" @click="clearLog" v-auth="'sysVislog:clear'" disabled> 娓呯┖ </el-button>
+				</el-form-item>
+			</el-form>
+		</el-card>
+
+		<el-card class="full-table" shadow="hover" style="margin-top: 5px">
+			<el-table :data="state.logData" style="width: 100%" v-loading="state.loading" border>
+				<el-table-column type="index" label="搴忓彿" width="55" align="center" />
+				<el-table-column prop="displayTitle" label="鏄剧ず鍚嶇О" width="150" align="center" show-overflow-tooltip />
+				<el-table-column prop="actionName" label="鏂规硶鍚嶇О" width="150" header-align="center" show-overflow-tooltip />
+				<el-table-column prop="account" label="璐﹀彿鍚嶇О" width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="realName" label="鐪熷疄濮撳悕" width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="remoteIp" label="IP鍦板潃" min-width="120" align="center" show-overflow-tooltip />
+				<el-table-column prop="location" label="鐧诲綍鍦扮偣" min-width="150" align="center" show-overflow-tooltip />
+				<el-table-column prop="longitude" label="缁忓害" min-width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="latitude" label="绾害" min-width="100" align="center" show-overflow-tooltip />
+				<el-table-column prop="browser" label="娴忚鍣�" min-width="150" align="center" show-overflow-tooltip />
+				<el-table-column prop="os" label="鎿嶄綔绯荤粺" width="120" align="center" show-overflow-tooltip />
+				<el-table-column prop="status" label="鐘舵��" width="70" align="center" show-overflow-tooltip>
+					<template #default="scope">
+						<el-tag type="success" v-if="scope.row.status === '200'">鎴愬姛</el-tag>
+						<el-tag type="danger" v-else>澶辫触</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="elapsed" label="鑰楁椂(ms)" width="90" align="center" show-overflow-tooltip />
+				<el-table-column prop="logDateTime" label="鏃ュ織鏃堕棿" width="160" align="center" fixed="right" show-overflow-tooltip />
+			</el-table>
+			<el-pagination
+				v-model:currentPage="state.tableParams.page"
+				v-model:page-size="state.tableParams.pageSize"
+				:total="state.tableParams.total"
+				:page-sizes="[10, 20, 50, 100]"
+				size="small"
+				background
+				@size-change="handleSizeChange"
+				@current-change="handleCurrentChange"
+				layout="total, sizes, prev, pager, next, jumper"
+			/>
+		</el-card>
+	</div>
+</template>
+
+<script lang="ts" setup name="sysVisLog">
+import { onMounted, reactive } from 'vue';
+import { ElMessage } from 'element-plus';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysLogVisApi } from '/@/api-services/api';
+import { SysLogVis } from '/@/api-services/models';
+
+const state = reactive({
+	loading: false,
+	queryParams: {
+		startTime: undefined,
+		endTime: undefined,
+		status: undefined,
+		actionName: undefined,
+		account: undefined,
+		elapsed: undefined,
+		remoteIp: undefined,
+	},
+	tableParams: {
+		page: 1,
+		pageSize: 20,
+		total: 0 as any,
+	},
+	logData: [] as Array<SysLogVis>,
+});
+
+onMounted(async () => {
+	handleQuery();
+});
+
+// 鏌ヨ鎿嶄綔
+const handleQuery = async () => {
+	if (state.queryParams.startTime == null) state.queryParams.startTime = undefined;
+	if (state.queryParams.endTime == null) state.queryParams.endTime = undefined;
+	if (state.queryParams.status == null) state.queryParams.status = undefined;
+	if (state.queryParams.actionName == null) state.queryParams.actionName = undefined;
+	if (state.queryParams.account == null) state.queryParams.account = undefined;
+	if (state.queryParams.elapsed == null) state.queryParams.elapsed = undefined;
+	if (state.queryParams.remoteIp == null) state.queryParams.remoteIp = undefined;
+
+	state.loading = true;
+	let params = Object.assign(state.queryParams, state.tableParams);
+	var res = await getAPI(SysLogVisApi).apiSysLogVisPagePost(params);
+	state.logData = res.data.result?.items ?? [];
+	state.tableParams.total = res.data.result?.total;
+	state.loading = false;
+};
+
+// 閲嶇疆鎿嶄綔
+const resetQuery = () => {
+	state.queryParams.startTime = undefined;
+	state.queryParams.endTime = undefined;
+	state.queryParams.status = undefined;
+	state.queryParams.actionName = undefined;
+	state.queryParams.account = undefined;
+	state.queryParams.elapsed = undefined;
+	state.queryParams.remoteIp = undefined;
+	handleQuery();
+};
+
+// 娓呯┖鏃ュ織
+const clearLog = async () => {
+	state.loading = true;
+	await getAPI(SysLogVisApi).apiSysLogVisClearPost();
+	state.loading = false;
+
+	ElMessage.success('娓呯┖鎴愬姛');
+	handleQuery();
+};
+
+// 鏀瑰彉椤甸潰瀹归噺
+const handleSizeChange = (val: number) => {
+	state.tableParams.pageSize = val;
+	handleQuery();
+};
+
+// 鏀瑰彉椤电爜搴忓彿
+const handleCurrentChange = (val: number) => {
+	state.tableParams.page = val;
+	handleQuery();
+};
+
+const shortcuts = [
+	{
+		text: '浠婂ぉ',
+		value: new Date(),
+	},
+	{
+		text: '鏄ㄥぉ',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24);
+			return date;
+		},
+	},
+	{
+		text: '涓婂懆',
+		value: () => {
+			const date = new Date();
+			date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+			return date;
+		},
+	},
+];
+</script>

--
Gitblit v1.8.0