浏览代码

修复登录日志的导出日志的下载文件功能

yanglzh 2 年之前
父节点
当前提交
267006b7b6
共有 4 个文件被更改,包括 233 次插入211 次删除
  1. 1 0
      package.json
  2. 2 2
      src/api/system/index.ts
  3. 24 0
      src/utils/request.ts
  4. 206 209
      src/views/system/monitor/loginLog/index.vue

+ 1 - 0
package.json

@@ -21,6 +21,7 @@
     "codemirror": "^5.65.9",
     "countup.js": "^2.1.0",
     "cropperjs": "^1.5.12",
+    "downloadjs": "^1.4.7",
     "echarts": "^5.3.3",
     "echarts-gl": "^2.0.9",
     "echarts-wordcloud": "^2.0.0",

+ 2 - 2
src/api/system/index.ts

@@ -1,4 +1,4 @@
-import { get, post, del, put } from '/@/utils/request';
+import { get, post, del, put, file } from '/@/utils/request';
 
 export default {
   sysinfo: () => get('/sysinfo'),
@@ -108,7 +108,7 @@ export default {
 
   log: {
     getList: (params: object) => get('/system/login/log/list', params),
-    export: (params: object) => get('/system/login/log/export', params),
+    export: (params: object) => file('/system/login/log/export', params),
     del: (infoIds: number[]) => del('/system/login/log/del', { infoIds }),
     detail: (infoId: number) => get('/system/login/log/detail', { infoId }),
     clearLog: () => post('/system/login/log/clear'),

+ 24 - 0
src/utils/request.ts

@@ -39,6 +39,8 @@ service.interceptors.response.use(
 					window.location.href = '/'; // 去登录页
 				})
 				.catch(() => { });
+		} else if (code === undefined && res.message === undefined) { // 可能是下载文件
+			return response
 		} else if (code !== 0) {
 			ElMessage.error(res.message)
 			return Promise.reject(new Error(res.message))
@@ -96,6 +98,7 @@ export function post(url: string, data?: any): any {
 		data
 	})
 }
+
 export function put(url: string, data?: any): any {
 	return service({
 		url,
@@ -110,3 +113,24 @@ export function del(url: string, data?: any): any {
 		data
 	})
 }
+
+
+export function file(url: string, params?: any, method: 'get' | 'post' = 'get'): any {
+	if (method === 'get') {
+		return service({
+			url,
+			method,
+			params,
+			timeout: 30000,
+			responseType: 'arraybuffer',
+		});
+	} else {
+		return service({
+			url,
+			method,
+			timeout: 100000,
+			data: params,
+			responseType: 'blob',
+		});
+	}
+}

+ 206 - 209
src/views/system/monitor/loginLog/index.vue

@@ -1,50 +1,50 @@
 <template>
-	<div class="system-dic-container">
-		<el-card shadow="hover">
-			<div class="system-user-search mb15">
-				<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
-					<el-form-item label="登录IP" prop="ipaddr">
-						<el-input v-model="tableData.param.ipaddr" placeholder="请输入登录地址" clearable style="width: 180px" size="default" @keyup.enter.native="dataList" />
-					</el-form-item>
+  <div class="system-dic-container">
+    <el-card shadow="hover">
+      <div class="system-user-search mb15">
+        <el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
+          <el-form-item label="登录IP" prop="ipaddr">
+            <el-input v-model="tableData.param.ipaddr" placeholder="请输入登录地址" clearable style="width: 180px" size="default" @keyup.enter.native="dataList" />
+          </el-form-item>
 
-					<el-form-item label="登录地点" prop="loginLocation">
-						<el-input v-model="tableData.param.loginLocation" placeholder="请输入登录地点" clearable style="width: 180px" size="default" @keyup.enter.native="dataList" />
-					</el-form-item>
+          <el-form-item label="登录地点" prop="loginLocation">
+            <el-input v-model="tableData.param.loginLocation" placeholder="请输入登录地点" clearable style="width: 180px" size="default" @keyup.enter.native="dataList" />
+          </el-form-item>
 
-					<!-- <el-form-item label="用户名称" prop="userName">
+          <!-- <el-form-item label="用户名称" prop="userName">
             <el-input v-model="tableData.param.userName" placeholder="请输入用户名称" clearable style="width: 180px;" size="default" @keyup.enter.native="dataList" />
           </el-form-item> -->
 
-					<el-form-item label="状态" prop="status">
-						<el-select v-model="tableData.param.status" placeholder="登录状态" clearable size="default" style="width: 180px">
-							<el-option label="全部" :value="-1" />
-							<el-option label="成功" :value="1" />
-							<el-option label="失败" :value="0" />
-						</el-select>
-					</el-form-item>
+          <el-form-item label="状态" prop="status">
+            <el-select v-model="tableData.param.status" placeholder="登录状态" clearable size="default" style="width: 180px">
+              <el-option label="全部" :value="-1" />
+              <el-option label="成功" :value="1" />
+              <el-option label="失败" :value="0" />
+            </el-select>
+          </el-form-item>
 
-					<el-form-item label="登录时间" prop="dateRange">
-						<el-date-picker v-model="tableData.param.dateRange" size="default" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
-					</el-form-item>
-					<el-form-item>
-						<el-button size="default" type="primary" class="ml10" @click="dataList">
-							<el-icon>
-								<ele-Search />
-							</el-icon>
-							查询
-						</el-button>
-						<el-button size="default" @click="resetQuery(queryRef)">
-							<el-icon>
-								<ele-Refresh />
-							</el-icon>
-							重置
-						</el-button>
-						<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)" v-auth="'del'">
-							<el-icon>
-								<ele-Delete />
-							</el-icon>
-							删除日志
-						</el-button>
+          <el-form-item label="登录时间" prop="dateRange">
+            <el-date-picker v-model="tableData.param.dateRange" size="default" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
+          </el-form-item>
+          <el-form-item>
+            <el-button size="default" type="primary" class="ml10" @click="dataList">
+              <el-icon>
+                <ele-Search />
+              </el-icon>
+              查询
+            </el-button>
+            <el-button size="default" @click="resetQuery(queryRef)">
+              <el-icon>
+                <ele-Refresh />
+              </el-icon>
+              重置
+            </el-button>
+            <el-button size="default" type="danger" class="ml10" @click="onRowDel(null)" v-auth="'del'">
+              <el-icon>
+                <ele-Delete />
+              </el-icon>
+              删除日志
+            </el-button>
 
             <el-button size="default" type="primary" class="ml10" @click="onRowExport()">
               <el-icon>
@@ -52,203 +52,200 @@
               </el-icon>
               导出日志
             </el-button>
-						<!--<el-button size="default" type="danger" class="ml10" @click="onRowClear()">
+            <!--<el-button size="default" type="danger" class="ml10" @click="onRowClear()">
               <el-icon>
                 <ele-Delete />
               </el-icon>
               清空日志
             </el-button> -->
-					</el-form-item>
-				</el-form>
-			</div>
-			<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="tableData.loading">
-				<el-table-column type="selection" width="55" align="center" />
-				<el-table-column label="编号" align="center" width="60" prop="infoId" />
-				<el-table-column label="登录名称" align="center" prop="loginName" />
-				<el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
-				<el-table-column label="登录地点" v-col="'loginLocation'" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
-				<el-table-column label="浏览器" align="center" prop="browser" />
-				<el-table-column label="操作系统" show-overflow-tooltip align="center" prop="os" />
-				<el-table-column label="登录状态" v-col="'status'" align="center" prop="status" width="90">
-					<template #default="scope">
-						<el-tag type="success" size="small" v-if="scope.row.status === 1">成功</el-tag>
-						<el-tag type="info" size="small" v-else>失败</el-tag>
-					</template>
-				</el-table-column>
-				<el-table-column label="操作信息" v-col="'msg'" show-overflow-tooltip prop="msg" />
-				<el-table-column label="登录日期" v-col="'loginTime'" align="center" prop="loginTime" width="180" />
-				<el-table-column label="登录模块" v-col="'module'" align="center" show-overflow-tooltip prop="module" width="120"></el-table-column>
-			</el-table>
-			<pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="dataList" />
-		</el-card>
-	</div>
+          </el-form-item>
+        </el-form>
+      </div>
+      <el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="tableData.loading">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="编号" align="center" width="60" prop="infoId" />
+        <el-table-column label="登录名称" align="center" prop="loginName" />
+        <el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
+        <el-table-column label="登录地点" v-col="'loginLocation'" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
+        <el-table-column label="浏览器" align="center" prop="browser" />
+        <el-table-column label="操作系统" show-overflow-tooltip align="center" prop="os" />
+        <el-table-column label="登录状态" v-col="'status'" align="center" prop="status" width="90">
+          <template #default="scope">
+            <el-tag type="success" size="small" v-if="scope.row.status === 1">成功</el-tag>
+            <el-tag type="info" size="small" v-else>失败</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作信息" v-col="'msg'" show-overflow-tooltip prop="msg" />
+        <el-table-column label="登录日期" v-col="'loginTime'" align="center" prop="loginTime" width="180" />
+        <el-table-column label="登录模块" v-col="'module'" align="center" show-overflow-tooltip prop="module" width="120"></el-table-column>
+      </el-table>
+      <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="dataList" />
+    </el-card>
+  </div>
 </template>
 
 <script lang="ts">
 import { toRefs, reactive, onMounted, ref, defineComponent, getCurrentInstance } from 'vue';
 import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
 import api from '/@/api/system';
+import download from 'downloadjs';
 
 // 定义接口来定义对象的类型
 interface TableDataRow {
-	infoId: number;
-	loginName: string;
-	ipaddr: string;
-	loginLocation: string;
-	browser: string;
-	os: string;
-	status: number;
-	msg: string;
-	loginTime: string;
-	module: string;
+  infoId: number;
+  loginName: string;
+  ipaddr: string;
+  loginLocation: string;
+  browser: string;
+  os: string;
+  status: number;
+  msg: string;
+  loginTime: string;
+  module: string;
 }
 interface TableDataState {
-	ids: number[];
-	tableData: {
-		data: Array<TableDataRow>;
-		total: number;
-		loading: boolean;
-		param: {
-			pageNum: number;
-			pageSize: number;
-			dateRange: string[];
-			status: number;
-			ipaddr: string;
-			loginLocation: string;
-			userName: string;
-		};
-	};
+  ids: number[];
+  tableData: {
+    data: Array<TableDataRow>;
+    total: number;
+    loading: boolean;
+    param: {
+      pageNum: number;
+      pageSize: number;
+      dateRange: string[];
+      status: number;
+      ipaddr: string;
+      loginLocation: string;
+      userName: string;
+    };
+  };
 }
 
 export default defineComponent({
-	name: 'apiV1SystemLoginLogList',
-	setup() {
-		const { proxy } = getCurrentInstance() as any;
-		const queryRef = ref();
-		const { admin_login_status } = proxy.useDict('admin_login_status');
-		const state = reactive<TableDataState>({
-			ids: [],
-			tableData: {
-				data: [],
-				total: 0,
-				loading: false,
-				param: {
-					pageNum: 1,
-					pageSize: 10,
-					dateRange: [],
-					status: -1,
-					ipaddr: '',
-					loginLocation: '',
-					userName: '',
-				},
-			},
-		});
-		// 初始化表格数据
-		const initTableData = () => {
-			dataList();
-		};
-		const dataList = () => {
-			state.tableData.loading = true;
-			api.log
-				.getList(state.tableData.param)
-				.then((res: any) => {
-					state.tableData.data = res.list;
-					state.tableData.total = res.total;
-				})
-				.finally(() => {
-					state.tableData.loading = false;
-				});
-		};
-		// 删除日志
-		const onRowDel = (row: TableDataRow) => {
-			let msg = '你确定要删除所选数据?';
-			let ids: number[] = [];
-			if (row) {
-				msg = `此操作将永久删除:“${row.loginName}”,是否继续?`;
-				ids = [row.infoId];
-			} else {
-				ids = state.ids;
-			}
-			if (ids.length === 0) {
-				ElMessage.error('请选择要删除的数据。');
-				return;
-			}
-			ElMessageBox.confirm(msg, '提示', {
-				confirmButtonText: '确认',
-				cancelButtonText: '取消',
-				type: 'warning',
-				beforeClose: (action, instance, done) => {
-					if (action === 'confirm') {
-						instance.confirmButtonLoading = true;
-						instance.confirmButtonText = '删除中';
+  name: 'apiV1SystemLoginLogList',
+  setup() {
+    const { proxy } = getCurrentInstance() as any;
+    const queryRef = ref();
+    const { admin_login_status } = proxy.useDict('admin_login_status');
+    const state = reactive<TableDataState>({
+      ids: [],
+      tableData: {
+        data: [],
+        total: 0,
+        loading: false,
+        param: {
+          pageNum: 1,
+          pageSize: 10,
+          dateRange: [],
+          status: -1,
+          ipaddr: '',
+          loginLocation: '',
+          userName: '',
+        },
+      },
+    });
+    // 初始化表格数据
+    const initTableData = () => {
+      dataList();
+    };
+    const dataList = () => {
+      state.tableData.loading = true;
+      api.log
+        .getList(state.tableData.param)
+        .then((res: any) => {
+          state.tableData.data = res.list;
+          state.tableData.total = res.total;
+        })
+        .finally(() => {
+          state.tableData.loading = false;
+        });
+    };
+    // 删除日志
+    const onRowDel = (row: TableDataRow) => {
+      let msg = '你确定要删除所选数据?';
+      let ids: number[] = [];
+      if (row) {
+        msg = `此操作将永久删除:“${row.loginName}”,是否继续?`;
+        ids = [row.infoId];
+      } else {
+        ids = state.ids;
+      }
+      if (ids.length === 0) {
+        ElMessage.error('请选择要删除的数据。');
+        return;
+      }
+      ElMessageBox.confirm(msg, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning',
+        beforeClose: (action, instance, done) => {
+          if (action === 'confirm') {
+            instance.confirmButtonLoading = true;
+            instance.confirmButtonText = '删除中';
 
-						api.log.del(ids).then(() => {
-							ElMessage.success('删除成功');
-							dataList();
-							done();
-						});
-					} else {
-						done();
-					}
-				},
-			}).catch(() => { });
-		};
+            api.log.del(ids).then(() => {
+              ElMessage.success('删除成功');
+              dataList();
+              done();
+            });
+          } else {
+            done();
+          }
+        },
+      }).catch(() => { });
+    };
 
     // 导出日志
     const onRowExport = () => {
-      state.tableData.loading = true;
       api.log
-          .export(state.tableData.param)
-          .then((res: any) => {
-            state.tableData.data = res.list;
-            state.tableData.total = res.total;
-          })
-          .finally(() => {
-            state.tableData.loading = false;
-          });
+        .export(state.tableData.param)
+        .then((res: any) => {
+          const fileName = res.headers['content-disposition'] ? res.headers['content-disposition'].replace('attachment; filename="', '').replace('"', '') : '导出日志.xlsx';
+          download(res.data, fileName, res.headers['content-type']);
+        })
     };
 
 
     // 清空日志
-		const onRowClear = () => {
-			ElMessageBox.confirm('你确定要删除所选数据?', '提示', {
-				confirmButtonText: '确认',
-				cancelButtonText: '取消',
-				type: 'warning',
-			})
-				.then(() => {
-					api.log.clearLog().then(() => {
-						ElMessage.success('清除成功');
-						dataList();
-					});
-				})
-				.catch(() => { });
-		};
-		// 页面加载时
-		onMounted(() => {
-			initTableData();
-		});
-		/** 重置按钮操作 */
-		const resetQuery = (formEl: FormInstance | undefined) => {
-			if (!formEl) return;
-			formEl.resetFields();
-			dataList();
-		};
-		// 多选框选中数据
-		const handleSelectionChange = (selection: TableDataRow[]) => {
-			state.ids = selection.map((item) => item.infoId);
-		};
-		return {
-			queryRef,
-			onRowDel,
+    const onRowClear = () => {
+      ElMessageBox.confirm('你确定要删除所选数据?', '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          api.log.clearLog().then(() => {
+            ElMessage.success('清除成功');
+            dataList();
+          });
+        })
+        .catch(() => { });
+    };
+    // 页面加载时
+    onMounted(() => {
+      initTableData();
+    });
+    /** 重置按钮操作 */
+    const resetQuery = (formEl: FormInstance | undefined) => {
+      if (!formEl) return;
+      formEl.resetFields();
+      dataList();
+    };
+    // 多选框选中数据
+    const handleSelectionChange = (selection: TableDataRow[]) => {
+      state.ids = selection.map((item) => item.infoId);
+    };
+    return {
+      queryRef,
+      onRowDel,
       onRowExport,
-			dataList,
-			resetQuery,
-			handleSelectionChange,
-			onRowClear,
-			admin_login_status,
-			...toRefs(state),
-		};
-	},
+      dataList,
+      resetQuery,
+      handleSelectionChange,
+      onRowClear,
+      admin_login_status,
+      ...toRefs(state),
+    };
+  },
 });
 </script>