Explorar el Código

操作日志和异常日志页面增加导出功能示例

zuohuaijun hace 3 años
padre
commit
ac9f99f9ff

+ 72 - 0
Admin.NET/Admin.NET.Core/Admin.NET.Core.xml

@@ -5476,6 +5476,16 @@
             结束时间
             </summary>
         </member>
+        <member name="P:Admin.NET.Core.Service.LogInput.StartTime">
+            <summary>
+            开始时间
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.LogInput.EndTime">
+            <summary>
+            结束时间
+            </summary>
+        </member>
         <member name="T:Admin.NET.Core.Service.SysLogDiffService">
             <summary>
             系统差异日志服务
@@ -5510,6 +5520,12 @@
             </summary>
             <returns></returns>
         </member>
+        <member name="M:Admin.NET.Core.Service.SysLogExService.ExporLogOp(Admin.NET.Core.Service.LogInput)">
+            <summary>
+            导出异常日志
+            </summary>
+            <returns></returns>
+        </member>
         <member name="T:Admin.NET.Core.Service.SysLogOpService">
             <summary>
             系统操作日志服务
@@ -5527,6 +5543,12 @@
             </summary>
             <returns></returns>
         </member>
+        <member name="M:Admin.NET.Core.Service.SysLogOpService.ExporLogOp(Admin.NET.Core.Service.LogInput)">
+            <summary>
+            导出操作日志
+            </summary>
+            <returns></returns>
+        </member>
         <member name="T:Admin.NET.Core.Service.SysLogVisService">
             <summary>
             系统访问日志服务
@@ -7019,6 +7041,56 @@
             <param name="input"></param>
             <returns></returns>
         </member>
+        <member name="T:Admin.NET.Core.ExportLogOpDto">
+            <summary>
+            导出操作日志表
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.LogName">
+            <summary>
+            记录器类别名称
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.LogLevel">
+            <summary>
+            日志级别
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.EventId">
+            <summary>
+            事件Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.Message">
+            <summary>
+            日志消息
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.Exception">
+            <summary>
+            异常对象
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.State">
+            <summary>
+            当前状态值
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.LogDateTime">
+            <summary>
+            日志记录时间
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.ThreadId">
+            <summary>
+            线程Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.ExportLogOpDto.TraceId">
+            <summary>
+            请求跟踪Id
+            </summary>
+        </member>
         <member name="P:Admin.NET.Core.MessageInput.UserId">
             <summary>
             用户ID

+ 2 - 0
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -132,9 +132,11 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=252885263005520, Pid=252885263005500, Title="操作日志", Path="/log/oplog", Name="sysOplog", Component="/system/log/oplog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=110 },
             new SysMenu{ Id=252885263005521, Pid=252885263005520, Title="查询", Permission="sysOplog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
             new SysMenu{ Id=252885263005522, Pid=252885263005520, Title="清空", Permission="sysOplog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
+            new SysMenu{ Id=252885263005523, Pid=252885263005520, Title="导出", Permission="sysOplog:export", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
             new SysMenu{ Id=252885263005530, Pid=252885263005500, Title="异常日志", Path="/log/exlog", Name="sysExlog", Component="/system/log/exlog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=120 },
             new SysMenu{ Id=252885263005531, Pid=252885263005530, Title="查询", Permission="sysExlog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
             new SysMenu{ Id=252885263005532, Pid=252885263005530, Title="清空", Permission="sysExlog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
+            new SysMenu{ Id=252885263005533, Pid=252885263005530, Title="导出", Permission="sysExlog:export", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
             new SysMenu{ Id=252885263005540, Pid=252885263005500, Title="差异日志", Path="/log/difflog", Name="sysDifflog", Component="/system/log/difflog/index", Icon="ele-Document", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=130 },
             new SysMenu{ Id=252885263005541, Pid=252885263005540, Title="查询", Permission="sysDifflog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
             new SysMenu{ Id=252885263005542, Pid=252885263005540, Title="清空", Permission="sysDifflog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },

+ 65 - 0
Admin.NET/Admin.NET.Core/Service/Logging/Dto/ExportLogOpDto.cs

@@ -0,0 +1,65 @@
+using Magicodes.ExporterAndImporter.Core;
+using Magicodes.ExporterAndImporter.Excel;
+
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 导出操作日志表
+/// </summary>
+[ExcelExporter(Name = "操作日志", TableStyle = OfficeOpenXml.Table.TableStyles.None, AutoFitAllColumn = true)]
+public class ExportLogOpDto
+{
+    /// <summary>
+    /// 记录器类别名称
+    /// </summary>
+    [ExporterHeader(DisplayName = "记录器类别名称", IsBold = true)]
+    public string LogName { get; set; }
+
+    /// <summary>
+    /// 日志级别
+    /// </summary>
+    [ExporterHeader(DisplayName = "日志级别", IsBold = true)]
+    public string LogLevel { get; set; }
+
+    /// <summary>
+    /// 事件Id
+    /// </summary>
+    [ExporterHeader(DisplayName = "事件Id", IsBold = true)]
+    public string EventId { get; set; }
+
+    /// <summary>
+    /// 日志消息
+    /// </summary>
+    [ExporterHeader(DisplayName = "日志消息", IsBold = true)]
+    public string Message { get; set; }
+
+    /// <summary>
+    /// 异常对象
+    /// </summary>
+    [ExporterHeader(DisplayName = "异常对象", IsBold = true)]
+    public string Exception { get; set; }
+
+    /// <summary>
+    /// 当前状态值
+    /// </summary>
+    [ExporterHeader(DisplayName = "当前状态值", IsBold = true)]
+    public string State { get; set; }
+
+    /// <summary>
+    /// 日志记录时间
+    /// </summary>
+    [ExporterHeader(DisplayName = "日志记录时间", IsBold = true)]
+    public DateTime LogDateTime { get; set; }
+
+    /// <summary>
+    /// 线程Id
+    /// </summary>
+    [ExporterHeader(DisplayName = "线程Id", IsBold = true)]
+    public int ThreadId { get; set; }
+
+    /// <summary>
+    /// 请求跟踪Id
+    /// </summary>
+    [ExporterHeader(DisplayName = "请求跟踪Id", IsBold = true)]
+    public string TraceId { get; set; }
+}

+ 13 - 0
Admin.NET/Admin.NET.Core/Service/Logging/Dto/LogInput.cs

@@ -7,6 +7,19 @@ public class PageLogInput : BasePageInput
     /// </summary>
     public DateTime? StartTime { get; set; }
 
+    /// <summary>
+    /// 结束时间
+    /// </summary>
+    public DateTime? EndTime { get; set; }
+}
+
+public class LogInput
+{
+    /// <summary>
+    /// 开始时间
+    /// </summary>
+    public DateTime? StartTime { get; set; }
+
     /// <summary>
     /// 结束时间
     /// </summary>

+ 22 - 1
Admin.NET/Admin.NET.Core/Service/Logging/SysLogExService.cs

@@ -1,4 +1,6 @@
-namespace Admin.NET.Core.Service;
+using Magicodes.ExporterAndImporter.Excel;
+
+namespace Admin.NET.Core.Service;
 
 /// <summary>
 /// 系统异常日志服务
@@ -37,4 +39,23 @@ public class SysLogExService : IDynamicApiController, ITransient
     {
         return await _sysLogExRep.DeleteAsync(u => u.Id > 0);
     }
+
+    /// <summary>
+    /// 导出异常日志
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysLogEx/expor"), NonUnify]
+    public async Task<IActionResult> ExporLogOp(LogInput input)
+    {
+        var lopOpList = await _sysLogExRep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()),
+                    u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime)
+            .OrderBy(u => u.CreateTime, OrderByType.Desc)
+            .Select<ExportLogOpDto>().ToListAsync();
+
+        IExcelExporter excelExporter = new ExcelExporter();
+        var res = await excelExporter.ExportAsByteArray(lopOpList);
+
+        return new FileStreamResult(new MemoryStream(res), "application/octet-stream") { FileDownloadName = DateTime.Now.ToString("yyyyMMddHHmm") + "操作日志.xlsx" };
+    }
 }

+ 22 - 1
Admin.NET/Admin.NET.Core/Service/Logging/SysLogOpService.cs

@@ -1,4 +1,6 @@
-namespace Admin.NET.Core.Service;
+using Magicodes.ExporterAndImporter.Excel;
+
+namespace Admin.NET.Core.Service;
 
 /// <summary>
 /// 系统操作日志服务
@@ -37,4 +39,23 @@ public class SysLogOpService : IDynamicApiController, ITransient
     {
         return await _sysLogOpRep.DeleteAsync(u => u.Id > 0);
     }
+
+    /// <summary>
+    /// 导出操作日志
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysLogOp/expor"), NonUnify]
+    public async Task<IActionResult> ExporLogOp(LogInput input)
+    {
+        var lopOpList = await _sysLogOpRep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(input.StartTime.ToString()) && !string.IsNullOrWhiteSpace(input.EndTime.ToString()),
+                    u => u.CreateTime >= input.StartTime && u.CreateTime <= input.EndTime)
+            .OrderBy(u => u.CreateTime, OrderByType.Desc)
+            .Select<ExportLogOpDto>().ToListAsync();
+
+        IExcelExporter excelExporter = new ExcelExporter();
+        var res = await excelExporter.ExportAsByteArray(lopOpList);
+
+        return new FileStreamResult(new MemoryStream(res), "application/octet-stream") { FileDownloadName = DateTime.Now.ToString("yyyyMMddHHmm") + "操作日志.xlsx" };
+    }
 }

+ 77 - 0
Web/src/api-services/apis/sys-log-ex-api.ts

@@ -18,6 +18,7 @@ import { Configuration } from '../configuration';
 import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
 import { AdminResultBoolean } from '../models';
 import { AdminResultSqlSugarPagedListSysLogEx } from '../models';
+import { LogInput } from '../models';
 /**
  * SysLogExApi - axios parameter creator
  * @export
@@ -60,6 +61,47 @@ export const SysLogExApiAxiosParamCreator = function (configuration?: Configurat
                 options: localVarRequestOptions,
             };
         },
+        /**
+         * 
+         * @summary 导出异常日志
+         * @param {LogInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysLogExExporPost: async (body?: LogInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysLogEx/expor`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
         /**
          * 
          * @summary 获取异常日志分页列表
@@ -157,6 +199,20 @@ export const SysLogExApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 导出异常日志
+         * @param {LogInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysLogExExporPost(body?: LogInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysLogExApiAxiosParamCreator(configuration).sysLogExExporPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 获取异常日志分页列表
@@ -195,6 +251,16 @@ export const SysLogExApiFactory = function (configuration?: Configuration, baseP
         async sysLogExClearPost(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultBoolean>> {
             return SysLogExApiFp(configuration).sysLogExClearPost(options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 导出异常日志
+         * @param {LogInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysLogExExporPost(body?: LogInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysLogExApiFp(configuration).sysLogExExporPost(body, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 获取异常日志分页列表
@@ -231,6 +297,17 @@ export class SysLogExApi extends BaseAPI {
     public async sysLogExClearPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultBoolean>> {
         return SysLogExApiFp(this.configuration).sysLogExClearPost(options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 导出异常日志
+     * @param {LogInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysLogExApi
+     */
+    public async sysLogExExporPost(body?: LogInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysLogExApiFp(this.configuration).sysLogExExporPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 获取异常日志分页列表

+ 77 - 0
Web/src/api-services/apis/sys-log-op-api.ts

@@ -18,6 +18,7 @@ import { Configuration } from '../configuration';
 import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
 import { AdminResultBoolean } from '../models';
 import { AdminResultSqlSugarPagedListSysLogOp } from '../models';
+import { LogInput } from '../models';
 /**
  * SysLogOpApi - axios parameter creator
  * @export
@@ -60,6 +61,47 @@ export const SysLogOpApiAxiosParamCreator = function (configuration?: Configurat
                 options: localVarRequestOptions,
             };
         },
+        /**
+         * 
+         * @summary 导出操作日志
+         * @param {LogInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysLogOpExporPost: async (body?: LogInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysLogOp/expor`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
         /**
          * 
          * @summary 获取操作日志分页列表
@@ -157,6 +199,20 @@ export const SysLogOpApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 导出操作日志
+         * @param {LogInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysLogOpExporPost(body?: LogInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysLogOpApiAxiosParamCreator(configuration).sysLogOpExporPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 获取操作日志分页列表
@@ -195,6 +251,16 @@ export const SysLogOpApiFactory = function (configuration?: Configuration, baseP
         async sysLogOpClearPost(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultBoolean>> {
             return SysLogOpApiFp(configuration).sysLogOpClearPost(options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 导出操作日志
+         * @param {LogInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysLogOpExporPost(body?: LogInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysLogOpApiFp(configuration).sysLogOpExporPost(body, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 获取操作日志分页列表
@@ -231,6 +297,17 @@ export class SysLogOpApi extends BaseAPI {
     public async sysLogOpClearPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultBoolean>> {
         return SysLogOpApiFp(this.configuration).sysLogOpClearPost(options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 导出操作日志
+     * @param {LogInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysLogOpApi
+     */
+    public async sysLogOpExporPost(body?: LogInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysLogOpApiFp(this.configuration).sysLogOpExporPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 获取操作日志分页列表

+ 1 - 0
Web/src/api-services/models/index.ts

@@ -116,6 +116,7 @@ export * from './iaction-result';
 export * from './job-detail-input';
 export * from './job-output';
 export * from './job-trigger-input';
+export * from './log-input';
 export * from './login-input';
 export * from './login-output';
 export * from './login-type-enum';

+ 32 - 0
Web/src/api-services/models/log-input.ts

@@ -0,0 +1,32 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+/**
+ * 
+ * @export
+ * @interface LogInput
+ */
+export interface LogInput {
+    /**
+     * 开始时间
+     * @type {Date}
+     * @memberof LogInput
+     */
+    startTime?: Date | null;
+    /**
+     * 结束时间
+     * @type {Date}
+     * @memberof LogInput
+     */
+    endTime?: Date | null;
+}

+ 11 - 0
Web/src/utils/download.ts

@@ -1,3 +1,4 @@
+import { AxiosResponseHeaders, RawAxiosResponseHeaders } from 'axios';
 import { dataURLtoBlob, urlToBase64 } from './base64Conver';
 
 /**
@@ -95,3 +96,13 @@ export function openWindow(url: string, opt?: { target?: TargetContext | string;
 
 	window.open(url, target, feature.join(','));
 }
+
+export function getFileName(headers: RawAxiosResponseHeaders | AxiosResponseHeaders) {
+	var fileName = headers['content-disposition'].split(';')[1].split('filename=')[1];
+	var fileNameUnicode = headers['content-disposition'].split('filename*=')[1];
+	if (fileNameUnicode) {
+		//当存在 filename* 时,取filename* 并进行解码(为了解决中文乱码问题)
+		fileName = decodeURIComponent(fileNameUnicode.split("''")[1]);
+	}
+	return fileName;
+}

+ 12 - 0
Web/src/views/system/log/exlog/index.vue

@@ -12,6 +12,7 @@
 					<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
 					<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysExlog:page'"> 查询 </el-button>
 					<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>
@@ -48,6 +49,7 @@
 <script lang="ts">
 import { toRefs, reactive, onMounted, defineComponent } 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';
@@ -98,6 +100,15 @@ export default defineComponent({
 			ElMessage.success('清空成功');
 			handleQuery();
 		};
+		// 导出日志
+		const exportLog = async () => {
+			state.loading = true;
+			var res = await getAPI(SysLogExApi).sysLogExExporPost(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;
@@ -134,6 +145,7 @@ export default defineComponent({
 			handleQuery,
 			resetQuery,
 			clearLog,
+			exportLog,
 			shortcuts,
 			handleSizeChange,
 			handleCurrentChange,

+ 14 - 2
Web/src/views/system/log/oplog/index.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="sys-oplog-container">
+	<div class="sys-oplog-container" v-loading="loading">
 		<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
 			<el-form :model="queryParams" ref="queryForm" :inline="true">
 				<el-form-item label="开始时间" prop="name">
@@ -12,12 +12,13 @@
 					<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
 					<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysOplog:page'"> 查询 </el-button>
 					<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 shadow="hover" style="margin-top: 8px">
-			<el-table :data="logData" style="width: 100%" v-loading="loading" border>
+			<el-table :data="logData" style="width: 100%" border>
 				<el-table-column type="index" label="序号" width="55" align="center" />
 				<el-table-column prop="logName" label="类别名称" show-overflow-tooltip />
 				<el-table-column prop="logLevel" label="日志级别" width="100" align="center" show-overflow-tooltip />
@@ -62,6 +63,7 @@
 <script lang="ts">
 import { toRefs, reactive, onMounted, defineComponent } 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';
@@ -114,6 +116,15 @@ export default defineComponent({
 			ElMessage.success('清空成功');
 			handleQuery();
 		};
+		// 导出日志
+		const exportLog = async () => {
+			state.loading = true;
+			var res = await getAPI(SysLogOpApi).sysLogOpExporPost(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;
@@ -155,6 +166,7 @@ export default defineComponent({
 			handleQuery,
 			resetQuery,
 			clearLog,
+			exportLog,
 			shortcuts,
 			handleSizeChange,
 			handleCurrentChange,