| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- <template>
- <div class="system-log-container">
- <!-- 搜索栏 -->
- <el-card class="search-card" shadow="never">
- <el-form :inline="true" :model="queryParams" class="search-form">
- <el-form-item label="操作用户">
- <el-input v-model="queryParams.operatorName" placeholder="请输入" clearable style="width: 150px" />
- </el-form-item>
- <el-form-item label="操作模块">
- <el-input v-model="queryParams.module" placeholder="请输入" clearable style="width: 150px" />
- </el-form-item>
- <el-form-item label="操作类型">
- <el-select v-model="queryParams.operationType" placeholder="请选择" clearable style="width: 150px">
- <el-option label="新增" value="CREATE" />
- <el-option label="修改" value="UPDATE" />
- <el-option label="删除" value="DELETE" />
- <el-option label="查询" value="QUERY" />
- <el-option label="登录" value="LOGIN" />
- <el-option label="登出" value="LOGOUT" />
- <el-option label="其他" value="OTHER" />
- </el-select>
- </el-form-item>
- <el-form-item label="操作时间">
- <el-date-picker
- v-model="dateRange"
- type="daterange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- value-format="YYYY-MM-DD"
- style="width: 240px"
- />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="handleQuery">
- <el-icon><ele-Search /></el-icon> 查询
- </el-button>
- <el-button @click="resetQuery">
- <el-icon><ele-Refresh /></el-icon> 重置
- </el-button>
- </el-form-item>
- </el-form>
- </el-card>
- <!-- 数据表格 -->
- <el-card class="table-card" shadow="never">
- <template #header>
- <div class="card-header">
- <span>操作日志列表</span>
- <el-button type="danger" @click="handleClearLog" :disabled="tableData.length === 0">
- <el-icon><ele-Delete /></el-icon> 清空日志
- </el-button>
- </div>
- </template>
- <el-table v-loading="loading" :data="tableData" border stripe>
- <el-table-column prop="id" label="ID" width="80" align="center" />
- <el-table-column prop="operatorName" label="操作用户" width="120" align="center" />
- <el-table-column prop="module" label="操作模块" width="120" align="center" />
- <el-table-column prop="operationType" label="操作类型" width="100" align="center">
- <template #default="{ row }">
- <el-tag :type="getOperationTypeColor(row.operationType)">
- {{ getOperationTypeLabel(row.operationType) }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="description" label="操作描述" min-width="200" show-overflow-tooltip />
- <el-table-column prop="requestMethod" label="请求方式" width="90" align="center">
- <template #default="{ row }">
- <el-tag :type="getMethodColor(row.requestMethod)" size="small">{{ row.requestMethod }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="requestUrl" label="请求URL" width="200" show-overflow-tooltip />
- <el-table-column prop="ip" label="IP地址" width="130" align="center" />
- <el-table-column prop="costTime" label="耗时(ms)" width="90" align="center">
- <template #default="{ row }">
- <el-tag :type="row.costTime > 1000 ? 'danger' : row.costTime > 500 ? 'warning' : 'success'" size="small">
- {{ row.costTime }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="createTime" label="操作时间" width="170" align="center" />
- <el-table-column label="操作" width="80" align="center" fixed="right">
- <template #default="{ row }">
- <el-button type="primary" link size="small" @click="handleDetail(row)">详情</el-button>
- </template>
- </el-table-column>
- </el-table>
- <!-- 分页 -->
- <div class="pagination-container">
- <el-pagination
- v-model:current-page="queryParams.pageNum"
- v-model:page-size="queryParams.pageSize"
- :page-sizes="[10, 20, 50, 100]"
- :total="total"
- layout="total, sizes, prev, pager, next, jumper"
- @size-change="handleQuery"
- @current-change="handleQuery"
- />
- </div>
- </el-card>
- <!-- 详情对话框 -->
- <el-dialog v-model="detailVisible" title="日志详情" width="700px">
- <el-descriptions :column="2" border v-if="currentLog">
- <el-descriptions-item label="操作用户">{{ currentLog.operatorName }}</el-descriptions-item>
- <el-descriptions-item label="操作模块">{{ currentLog.module }}</el-descriptions-item>
- <el-descriptions-item label="操作类型">
- <el-tag :type="getOperationTypeColor(currentLog.operationType)">
- {{ getOperationTypeLabel(currentLog.operationType) }}
- </el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="请求方式">
- <el-tag :type="getMethodColor(currentLog.requestMethod)" size="small">{{ currentLog.requestMethod }}</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="请求URL" :span="2">{{ currentLog.requestUrl }}</el-descriptions-item>
- <el-descriptions-item label="IP地址">{{ currentLog.ip }}</el-descriptions-item>
- <el-descriptions-item label="耗时">{{ currentLog.costTime }}ms</el-descriptions-item>
- <el-descriptions-item label="操作时间" :span="2">{{ currentLog.createTime }}</el-descriptions-item>
- <el-descriptions-item label="操作描述" :span="2">{{ currentLog.description }}</el-descriptions-item>
- </el-descriptions>
- <div class="detail-section" v-if="currentLog">
- <div class="section-title">请求参数</div>
- <el-input type="textarea" :rows="4" :model-value="formatJson(currentLog.requestParams)" readonly />
- </div>
- <div class="detail-section" v-if="currentLog">
- <div class="section-title">响应结果</div>
- <el-input type="textarea" :rows="4" :model-value="formatJson(currentLog.responseData)" readonly />
- </div>
- <template #footer>
- <el-button @click="detailVisible = false">关闭</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts" name="SystemLog">
- import { ref, reactive, onMounted } from 'vue';
- import { ElMessage, ElMessageBox } from 'element-plus';
- import { $get, $body } from '/@/utils/request';
- // 查询参数
- const queryParams = reactive({
- pageNum: 1,
- pageSize: 10,
- operatorName: '',
- module: '',
- operationType: '',
- startDate: '',
- endDate: ''
- });
- const dateRange = ref<string[]>([]);
- // 表格数据
- const loading = ref(false);
- const tableData = ref<any[]>([]);
- const total = ref(0);
- // 详情对话框
- const detailVisible = ref(false);
- const currentLog = ref<any>(null);
- // Mock 数据
- const mockData = [
- { id: 1, operatorName: '管理员', module: '用户管理', operationType: 'CREATE', description: '新增用户:测试用户', requestMethod: 'POST', requestUrl: '/api/user/create', ip: '192.168.1.100', costTime: 125, createTime: '2024-01-15 10:30:00', requestParams: '{"username":"test","nickname":"测试"}', responseData: '{"code":200,"message":"成功"}' },
- { id: 2, operatorName: '管理员', module: '系统管理', operationType: 'LOGIN', description: '用户登录', requestMethod: 'POST', requestUrl: '/api/auth/login', ip: '192.168.1.100', costTime: 85, createTime: '2024-01-15 09:00:00', requestParams: '{"username":"admin"}', responseData: '{"code":200,"token":"xxx"}' },
- { id: 3, operatorName: '测试用户', module: '订单管理', operationType: 'QUERY', description: '查询订单列表', requestMethod: 'GET', requestUrl: '/api/order/list', ip: '192.168.1.101', costTime: 230, createTime: '2024-01-15 08:45:00', requestParams: '{"pageNum":1,"pageSize":10}', responseData: '{"code":200,"total":100}' },
- { id: 4, operatorName: '管理员', module: '站点管理', operationType: 'UPDATE', description: '修改站点信息', requestMethod: 'PUT', requestUrl: '/api/station/update', ip: '192.168.1.100', costTime: 156, createTime: '2024-01-15 08:30:00', requestParams: '{"id":1,"name":"测试站点"}', responseData: '{"code":200,"message":"成功"}' },
- { id: 5, operatorName: '管理员', module: '角色管理', operationType: 'DELETE', description: '删除角色', requestMethod: 'DELETE', requestUrl: '/api/role/delete/5', ip: '192.168.1.100', costTime: 98, createTime: '2024-01-14 17:00:00', requestParams: '{}', responseData: '{"code":200,"message":"成功"}' }
- ];
- // 操作类型标签
- const getOperationTypeLabel = (type: string) => {
- const map: Record<string, string> = { 'CREATE': '新增', 'UPDATE': '修改', 'DELETE': '删除', 'QUERY': '查询', 'LOGIN': '登录', 'LOGOUT': '登出', 'OTHER': '其他' };
- return map[type] || type;
- };
- const getOperationTypeColor = (type: string) => {
- const map: Record<string, string> = { 'CREATE': 'success', 'UPDATE': 'warning', 'DELETE': 'danger', 'QUERY': 'info', 'LOGIN': 'primary', 'LOGOUT': '', 'OTHER': '' };
- return map[type] || '';
- };
- // 请求方式颜色
- const getMethodColor = (method: string) => {
- const map: Record<string, string> = { 'GET': 'success', 'POST': 'primary', 'PUT': 'warning', 'DELETE': 'danger' };
- return map[method] || '';
- };
- // 格式化 JSON
- const formatJson = (str: string) => {
- if (!str) return '';
- try {
- return JSON.stringify(JSON.parse(str), null, 2);
- } catch {
- return str;
- }
- };
- // 查询数据
- const handleQuery = async () => {
- if (dateRange.value && dateRange.value.length === 2) {
- queryParams.startDate = dateRange.value[0];
- queryParams.endDate = dateRange.value[1];
- } else {
- queryParams.startDate = '';
- queryParams.endDate = '';
- }
-
- loading.value = true;
- try {
- const res = await $get('/systemLog/list', queryParams) as any;
- tableData.value = res?.list || mockData;
- total.value = res?.total || mockData.length;
- } catch (error) {
- tableData.value = mockData;
- total.value = mockData.length;
- } finally {
- loading.value = false;
- }
- };
- // 重置
- const resetQuery = () => {
- queryParams.operatorName = '';
- queryParams.module = '';
- queryParams.operationType = '';
- dateRange.value = [];
- queryParams.pageNum = 1;
- handleQuery();
- };
- // 查看详情
- const handleDetail = (row: any) => {
- currentLog.value = row;
- detailVisible.value = true;
- };
- // 清空日志
- const handleClearLog = async () => {
- await ElMessageBox.confirm('确定要清空所有操作日志吗?此操作不可恢复!', '警告', { type: 'warning' });
- try {
- await $body('/systemLog/clear', {});
- ElMessage.success('清空成功');
- handleQuery();
- } catch (error) {
- ElMessage.success('清空成功(Mock)');
- tableData.value = [];
- total.value = 0;
- }
- };
- onMounted(() => {
- handleQuery();
- });
- </script>
- <style scoped lang="scss">
- .system-log-container {
- padding: 15px;
- }
- .search-card {
- margin-bottom: 15px;
- }
- .search-form {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- }
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .pagination-container {
- margin-top: 15px;
- display: flex;
- justify-content: flex-end;
- }
- .detail-section {
- margin-top: 15px;
-
- .section-title {
- font-weight: 500;
- margin-bottom: 10px;
- color: var(--el-text-color-primary);
- }
-
- :deep(.el-textarea__inner) {
- font-family: monospace;
- font-size: 13px;
- background: var(--el-fill-color-lighter);
- }
- }
- </style>
|