|
|
@@ -1,176 +1,277 @@
|
|
|
+<style scoped lang="scss">
|
|
|
+.system-container {
|
|
|
+
|
|
|
+ :deep(.el-card__body) {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+ flex: 1;
|
|
|
+ overflow: auto;
|
|
|
+
|
|
|
+ .el-table {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.page-content {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.page-pager {
|
|
|
+ background-color: var(--el-color-white);
|
|
|
+ height: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.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>
|
|
|
<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="操作类型">
|
|
|
- <ext-d-select v-model="queryParams.operationType" type="OptLog.operationType" placeholder="请选择" clearable style="width: 150px" />
|
|
|
- </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>
|
|
|
+ <div class="system-container layout-padding">
|
|
|
+ <el-card shadow="hover" class="layout-padding-auto">
|
|
|
+
|
|
|
+ <el-form
|
|
|
+ :model="queryParams"
|
|
|
+ ref="queryRef"
|
|
|
+ size="default" label-width="0px" class="mt5 mb5">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.username"
|
|
|
+ placeholder="操作用户"
|
|
|
+ clearable
|
|
|
+ @blur="handleQuery"
|
|
|
+ class="wd150 mr10">
|
|
|
+ </el-input>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="dateRange"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ @change="handleQuery"
|
|
|
+ class="wd260 mr10">
|
|
|
+ </el-date-picker>
|
|
|
+
|
|
|
+ <el-button class="ml10" plain size="default" type="success" @click="handleQuery">
|
|
|
+ <SvgIcon name="ele-Search"/>
|
|
|
+ 查询
|
|
|
+ </el-button>
|
|
|
+ <el-button plain size="default" type="warning" @click="resetQuery">
|
|
|
+ <SvgIcon name="ele-Refresh"/>
|
|
|
+ 重置
|
|
|
+ </el-button>
|
|
|
+ <el-button plain size="default" type="danger" @click="handleClearLog">
|
|
|
+ <SvgIcon name="ele-Delete"/>
|
|
|
+ 清空日志
|
|
|
+ </el-button>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ :height="state.tableHeight"
|
|
|
+ :data="tableData"
|
|
|
+ v-loading="loading">
|
|
|
+ <el-table-column prop="id" label="ID" width="170" align="center" />
|
|
|
+ <el-table-column prop="username" label="操作用户" width="130" align="center" />
|
|
|
+ <el-table-column label="操作模块" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span style="white-space: nowrap">{{ getModule(row.method) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作类型" width="95" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="getOperationTypeColor(row.operation)" size="small">
|
|
|
+ {{ getOperationTypeLabel(row.operation) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="operation" label="操作描述" min-width="200" show-overflow-tooltip />
|
|
|
+ <el-table-column label="请求方式" width="100" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="getMethodColor(getHttpMethod(row.method))" size="small">{{ getHttpMethod(row.method) }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="请求URL" min-width="200" show-overflow-tooltip>
|
|
|
+ <template #default="{ row }">{{ getRequestUrl(row.method) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="ip" label="IP地址" width="150" align="center" />
|
|
|
+ <el-table-column prop="executeTime" label="耗时(ms)" width="95" align="center" />
|
|
|
+ <el-table-column prop="createTime" label="操作时间" width="175" align="center" />
|
|
|
+ <el-table-column label="操作" width="75" 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="page-pager">
|
|
|
+ <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"
|
|
|
+ small
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 详情对话框 -->
|
|
|
+ <el-dialog v-model="detailVisible" title="日志详情" width="700px">
|
|
|
+ <el-descriptions :column="2" border v-if="currentLog">
|
|
|
+ <el-descriptions-item label="操作用户">{{ currentLog.username }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="操作模块">{{ getModule(currentLog.method) }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="操作类型">
|
|
|
+ <el-tag :type="getOperationTypeColor(currentLog.operation)">
|
|
|
+ {{ getOperationTypeLabel(currentLog.operation) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="请求方式">
|
|
|
+ <el-tag :type="getMethodColor(getHttpMethod(currentLog.method))" size="small">{{ getHttpMethod(currentLog.method) }}</el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="请求URL" :span="2">{{ getRequestUrl(currentLog.method) }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="IP地址">{{ currentLog.ip }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="耗时">{{ currentLog.executeTime }}ms</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="操作时间" :span="2">{{ currentLog.createTime }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="操作描述" :span="2">{{ currentLog.operation }}</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.requestParam)" 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 { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
import { $get, $body } from '/@/utils/request';
|
|
|
-import u from '/@/utils/u';
|
|
|
|
|
|
-// 查询参数
|
|
|
+const queryRef = ref();
|
|
|
const queryParams = reactive({
|
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
|
- operatorName: '',
|
|
|
- module: '',
|
|
|
- operationType: '',
|
|
|
+ username: '',
|
|
|
startDate: '',
|
|
|
endDate: ''
|
|
|
});
|
|
|
const dateRange = ref<string[]>([]);
|
|
|
|
|
|
-// 表格数据
|
|
|
+const state = reactive({
|
|
|
+ tableHeight: 400,
|
|
|
+});
|
|
|
+
|
|
|
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 getModule = (method: string) => {
|
|
|
+ if (!method) return '-';
|
|
|
+ const m = method.match(/com\.kym\.admin\.controller\.(\w+)Controller/);
|
|
|
+ if (!m) return '-';
|
|
|
+ const controllers: Record<string, string> = {
|
|
|
+ 'WashStation': '站点管理',
|
|
|
+ 'WashDevice': '设备管理',
|
|
|
+ 'WashOrder': '订单管理',
|
|
|
+ 'User': '用户管理',
|
|
|
+ 'Account': '账户管理',
|
|
|
+ 'AdminUser': '运维用户',
|
|
|
+ 'Role': '角色管理',
|
|
|
+ 'SystemLog': '系统日志',
|
|
|
+ 'Finance': '财务管理',
|
|
|
+ 'RechargeConfig': '充值配置',
|
|
|
+ 'Promotion': '优惠活动',
|
|
|
+ 'Activity': '活动管理',
|
|
|
+ 'Banner': '广告管理',
|
|
|
+ 'Faq': 'FAQ管理',
|
|
|
+ 'Feedback': '反馈管理',
|
|
|
+ 'DeviceConfig': '设备配置',
|
|
|
+ 'DeviceRelation': '设备关联',
|
|
|
+ 'InvestorInfo': '投资人',
|
|
|
+ 'DictData': '字典管理',
|
|
|
+ 'SystemNotice': '系统公告',
|
|
|
+ 'Wx': '微信管理',
|
|
|
+ 'Attachment': '附件管理',
|
|
|
+ 'Custom': '用户管理',
|
|
|
+ };
|
|
|
+ return controllers[m[1]] || m[1];
|
|
|
+};
|
|
|
|
|
|
-// 操作类型标签
|
|
|
-const getOperationTypeLabel = (type: string) => u.fmt.fmtDict(type, 'OptLog.operationType');
|
|
|
-const getOperationTypeColor = (type: string) => u.fmt.fmtDictColor(type, 'OptLog.operationType');
|
|
|
+const getOperationTypeLabel = (operation: string) => {
|
|
|
+ if (!operation) return '其他';
|
|
|
+ if (operation.startsWith('新增')) return '新增';
|
|
|
+ if (operation.startsWith('修改')) return '修改';
|
|
|
+ if (operation.startsWith('删除')) return '删除';
|
|
|
+ if (operation.startsWith('查询')) return '查询';
|
|
|
+ if (operation.includes('登录')) return '登录';
|
|
|
+ if (operation.includes('登出')) return '登出';
|
|
|
+ if (operation.includes('清空')) return '删除';
|
|
|
+ if (operation.includes('导入')) return '新增';
|
|
|
+ if (operation.includes('导出')) return '查询';
|
|
|
+ return '其他';
|
|
|
+};
|
|
|
|
|
|
-// 请求方式颜色
|
|
|
-const getMethodColor = (method: string) => u.fmt.fmtDictColor(method, 'OptLog.httpMethod');
|
|
|
+const getOperationTypeColor = (operation: string) => {
|
|
|
+ const map: Record<string, string> = {
|
|
|
+ '新增': 'success',
|
|
|
+ '修改': 'warning',
|
|
|
+ '删除': 'danger',
|
|
|
+ '查询': 'info',
|
|
|
+ '登录': 'primary',
|
|
|
+ '登出': '',
|
|
|
+ '其他': '',
|
|
|
+ };
|
|
|
+ return map[getOperationTypeLabel(operation)] || '';
|
|
|
+};
|
|
|
+
|
|
|
+const getHttpMethod = (method: string) => {
|
|
|
+ if (!method) return '-';
|
|
|
+ const m = method.match(/\.(\w+)\(\)$/);
|
|
|
+ if (!m) return '-';
|
|
|
+ const action = m[1].toLowerCase();
|
|
|
+ if (action.startsWith('list') || action.startsWith('get') || action.startsWith('detail') || action.startsWith('query')) return 'GET';
|
|
|
+ return 'POST';
|
|
|
+};
|
|
|
+
|
|
|
+const getMethodColor = (m: string) => {
|
|
|
+ if (m === 'GET') return 'success';
|
|
|
+ if (m === 'POST') return 'primary';
|
|
|
+ return '';
|
|
|
+};
|
|
|
+
|
|
|
+const getRequestUrl = (method: string) => {
|
|
|
+ if (!method) return '-';
|
|
|
+ const m = method.match(/com\.kym\.admin\.controller\.(\w+)Controller\.(\w+)\(\)$/);
|
|
|
+ if (!m) return method;
|
|
|
+ const controller = m[1];
|
|
|
+ const action = m[2];
|
|
|
+ return `/${controller[0].toLowerCase() + controller.slice(1)}/${action}`;
|
|
|
+};
|
|
|
|
|
|
-// 格式化 JSON
|
|
|
const formatJson = (str: string) => {
|
|
|
if (!str) return '';
|
|
|
try {
|
|
|
@@ -180,7 +281,6 @@ const formatJson = (str: string) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 查询数据
|
|
|
const handleQuery = async () => {
|
|
|
if (dateRange.value && dateRange.value.length === 2) {
|
|
|
queryParams.startDate = dateRange.value[0];
|
|
|
@@ -189,95 +289,58 @@ const handleQuery = async () => {
|
|
|
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;
|
|
|
+ tableData.value = res?.list || [];
|
|
|
+ total.value = res?.total || 0;
|
|
|
+ } catch {
|
|
|
+ tableData.value = [];
|
|
|
+ total.value = 0;
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 重置
|
|
|
const resetQuery = () => {
|
|
|
- queryParams.operatorName = '';
|
|
|
- queryParams.module = '';
|
|
|
- queryParams.operationType = '';
|
|
|
+ queryParams.username = '';
|
|
|
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;
|
|
|
+ } catch {
|
|
|
+ // ignore
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+const calcTableHeight = () => {
|
|
|
+ nextTick(() => {
|
|
|
+ const bodyHeight = document.body.clientHeight;
|
|
|
+ const queryHeight = queryRef.value?.$el?.clientHeight || 56;
|
|
|
+ state.tableHeight = bodyHeight - queryHeight - 240;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
handleQuery();
|
|
|
+ calcTableHeight();
|
|
|
+ window.addEventListener('resize', calcTableHeight);
|
|
|
});
|
|
|
-</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>
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', calcTableHeight);
|
|
|
+});
|
|
|
+</script>
|