|
|
@@ -0,0 +1,168 @@
|
|
|
+<!--
|
|
|
+ * @since: 2025-02-07
|
|
|
+ * OperationLog.vue
|
|
|
+-->
|
|
|
+<template>
|
|
|
+ <div class="login-log">
|
|
|
+ <el-card class="mb-3 proCard">
|
|
|
+ <el-space >
|
|
|
+ <el-form ref="searchFormRef" :inline="true" :model="requestParams.queryParam" class="form-inline">
|
|
|
+ <el-form-item prop="userName">
|
|
|
+ <el-select v-model="requestParams.queryParam.queryType" placeholder="选择类型" class="type-select">
|
|
|
+ <el-option
|
|
|
+ v-for="item in queryTypeSelect"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <el-input v-model="requestParams.queryParam.queryTypeContent" clearable placeholder="请输入搜索关键字"
|
|
|
+ @keyup.enter="queryOperationLogPage" :disabled="requestParams.queryParam.queryType ===''"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="应用侧" prop="platform">
|
|
|
+ <el-select v-model="requestParams.queryParam.platform" placeholder="请选择应用侧" clearable>
|
|
|
+ <el-option :label="item.label" :value="item.label" v-for="item in platformList" :key="item.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="所属模块" prop="module">
|
|
|
+ <el-input v-model="requestParams.queryParam.module" clearable placeholder="请输入所属模块"
|
|
|
+ @keyup.enter="queryOperationLogPage" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="操作类型" prop="operatorType">
|
|
|
+ <el-select v-model="requestParams.queryParam.operatorType" placeholder="请选择操作类型" clearable>
|
|
|
+ <el-option :label="item.label" :value="item.label" v-for="item in operationList" :key="item.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="登录时间" prop="date">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="requestParams.queryParam.date"
|
|
|
+ type="daterange"
|
|
|
+ placeholder="请选择登录时间"
|
|
|
+ range-separator="~"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" :icon="Search" @click="queryOperationLogPage">查询</el-button>
|
|
|
+ <el-button :icon="Refresh" @click="handleResetForm(searchFormRef)">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-space>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card>
|
|
|
+ <template #header>
|
|
|
+ <el-button type="primary" @click="handleExport()">导出数据</el-button>
|
|
|
+ </template>
|
|
|
+ <el-table height="calc(100vh - 340px)" :data="operationLogList" v-loading="loading">
|
|
|
+ <el-table-column label="日志编号" width="100" prop="id" />
|
|
|
+ <el-table-column label="账号" prop="realName" />
|
|
|
+ <el-table-column label="姓名" prop="operatorName" />
|
|
|
+ <el-table-column label="应用侧" prop="platform" >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.platform === 'ADMIN' ? '管理侧' : '平台侧' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="所属模块" prop="module" />
|
|
|
+ <el-table-column label="操作内容" prop="content" />
|
|
|
+ <el-table-column label="操作类型" prop="operatorType" >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ OperationType[row.operatorType] }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作结果" prop="isSuccess" >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-icon :size="24" color="#67C23A" v-if="row.isSuccess === LoginStatusEnum.SUCCESS"><SuccessFilled /></el-icon>
|
|
|
+ <el-icon :size="24" color="#F56C6C" v-else><CircleCloseFilled /></el-icon>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="IP" prop="clientIp" />
|
|
|
+ <el-table-column label="操作时间" prop="createdAt" />
|
|
|
+ <el-table-column label="操作" width="160">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-space>
|
|
|
+ <el-button type="primary" text @click="openDrawer(row.id)" >详情</el-button>
|
|
|
+ </el-space>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <section class="mt-4 flex justify-end">
|
|
|
+ <el-pagination background layout="total, sizes, prev, pager, next" :page-sizes="[10, 30, 50]" :total="total"
|
|
|
+ v-model:page-size="requestParams.pageSize" v-model:current-page="requestParams.pageNumber"
|
|
|
+ @change="queryOperationLogPage" />
|
|
|
+ </section>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <DetailDialog ref="drawerInstance" />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, onMounted } from 'vue';
|
|
|
+import { Search, Refresh, Edit ,SuccessFilled, CircleCloseFilled} from '@element-plus/icons-vue';
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import { exportLoginLog } from '@/api/system/log';
|
|
|
+import { downloadByData } from '@/utils/file/download';
|
|
|
+import type { FormInstance } from 'element-plus'
|
|
|
+import DetailDialog from './DetailDialog.vue';
|
|
|
+import { platformList, operationList, queryTypeSelect, LoginStatusEnum, OperationType } from '@/types/log/constants';
|
|
|
+import userOperationQuery from '../hooks/userOperationQuery';
|
|
|
+
|
|
|
+const { requestParams, total, operationLogList, loading, queryOperationLogPage, resetRequestParams } = userOperationQuery();
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ queryOperationLogPage();
|
|
|
+});
|
|
|
+
|
|
|
+/* 导出数据 */
|
|
|
+const handleExport = () => {
|
|
|
+ ElMessageBox.confirm('确定导出所查询数据?', '导出', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ showCancelButton: true,
|
|
|
+ type: 'warning',
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ exportLoginLog(requestParams).then(async (responnse) => {
|
|
|
+ if (!responnse) {
|
|
|
+ throw new Error('下载文件失败');
|
|
|
+ }
|
|
|
+ downloadByData(responnse, '操作日志.xlsx');
|
|
|
+ ElMessage.success('下载文件成功');
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ ElMessage({
|
|
|
+ type: 'info',
|
|
|
+ message: '取消导出',
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/* 重置 */
|
|
|
+const searchFormRef = ref<FormInstance>()
|
|
|
+const handleResetForm = (formEl: FormInstance | undefined) => {
|
|
|
+ if (!formEl) return
|
|
|
+ resetRequestParams();
|
|
|
+ formEl.resetFields();
|
|
|
+ queryOperationLogPage();
|
|
|
+}
|
|
|
+
|
|
|
+const drawerInstance = ref<InstanceType<typeof DetailDialog>>();
|
|
|
+const openDrawer = (id?: number) => {
|
|
|
+ drawerInstance.value?.open(id);
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.form-inline .el-input {
|
|
|
+ --el-input-width: 160px;
|
|
|
+}
|
|
|
+
|
|
|
+.form-inline .el-select {
|
|
|
+ --el-select-width: 160px;
|
|
|
+}
|
|
|
+</style>
|