Selaa lähdekoodia

新增,修改,删除默认启用差异日志记录

Tony 3 vuotta sitten
vanhempi
commit
a30e0a6e49

+ 12 - 5
Admin.NET/Admin.NET.Application/AppConfig.json

@@ -1,14 +1,21 @@
 {
   "ConnectionStrings": {
-    "DefaultConnection": "DataSource=C:\\db\\Admin.NET.db",
-    "DefaultDbType": "Sqlite", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access
+    "DefaultConnection": "server=116i.cn;Database=mooc_db;Uid=root;Pwd=zhaofei242@;pooling=true;port=3306;sslMode=None;CharSet=utf8;Convert Zero Datetime=True;AllowLoadLocalInfile=true",
+    "DefaultDbType": "MySql", // MySql、SqlServer、Sqlite、Oracle、PostgreSQL、Dm、Kdbndp、Oscar、MySqlConnector、Access
     "InitTable": true, // 是否生成库表和种子数据
+    "DisableDiffLog": false, // 禁用数据库表差异日志
     "DbConfigs": [
       {
-        "DbConfigId": "Test",
-        "DbType": "Sqlite",
-        "DbConnection": "DataSource=C:\\db\\Test.db",
+        "DbConfigId": "mooc_data_db",
+        "DbType": "MySql",
+        "DbConnection": "server=116i.cn;Database=mooc_data_db;Uid=root;Pwd=zhaofei242@;pooling=true;port=3306;sslMode=None;CharSet=utf8;Convert Zero Datetime=True;AllowLoadLocalInfile=true",
         "InitTable": true
+      },
+      {
+        "DbConfigId": "mooc_log_db",
+        "DbType": "MySql",
+        "DbConnection": "server=116i.cn;Database=mooc_log_db;Uid=root;Pwd=zhaofei242@;pooling=true;port=3306;sslMode=None;CharSet=utf8;Convert Zero Datetime=True;AllowLoadLocalInfile=true",
+        "InitTable": false
       }
     ]
   },

+ 14 - 0
Admin.NET/Admin.NET.Application/GlobalUsings.cs

@@ -0,0 +1,14 @@
+global using Furion;
+global using Furion.DependencyInjection;
+global using Furion.DynamicApiController;
+global using Furion.RemoteRequest.Extensions;
+global using Mapster;
+global using Microsoft.AspNetCore.Mvc;
+global using Microsoft.Extensions.DependencyInjection;
+global using SqlSugar;
+global using System.ComponentModel.DataAnnotations;
+global using System.Data;
+global using System.Linq.Dynamic.Core;
+global using System.Threading.Tasks;
+global using Admin.NET.Core;
+global using Admin.NET.Application.Entity;

+ 45 - 0
Admin.NET/Admin.NET.Core/Entity/SysLogDiff.cs

@@ -0,0 +1,45 @@
+
+
+namespace Admin.NET.Core;
+
+[SugarTable("sys_log_diff", "系统差异日志表")]
+[SqlSugarEntity]
+public class SysLogDiff : EntityBase
+{
+    /// <summary>
+    /// 操作前记录
+    /// </summary>
+    [SugarColumn(ColumnDescription = "操作前记录", ColumnDataType = "text")]
+    public string BeforeData { get; set; }
+    /// <summary>
+    /// 操作后记录
+    /// </summary>
+    [SugarColumn(ColumnDescription = "操作后记录", ColumnDataType = "text")]
+    public string AfterData { get; set; }
+    /// <summary>
+    /// Sql
+    /// </summary>
+    [SugarColumn(ColumnDescription = "Sql", ColumnDataType = "text")]
+    public string Sql { get; set; }
+    /// <summary>
+    /// 参数  手动传入的参数
+    /// </summary>
+    [SugarColumn(ColumnDescription = "参数", ColumnDataType = "text")]
+    public string Parameters { get; set; }
+    /// <summary>
+    /// 业务对象  
+    /// </summary>
+    [SugarColumn(ColumnDescription = "业务对象", ColumnDataType = "text")]
+    public string BusinessData { get; set; }
+    /// <summary>
+    /// 差异操作
+    /// </summary>
+    [SugarColumn(ColumnDescription = "差异操作", ColumnDataType = "text")]
+    public string DiffType { get; set; }
+    /// <summary>
+    /// 耗时
+    /// </summary>
+    [SugarColumn(ColumnDescription = "耗时")]
+    public long Duration { get; set; }
+}
+

+ 84 - 5
Admin.NET/Admin.NET.Core/Extension/RepositoryExtension.cs

@@ -3,20 +3,39 @@
 public static class RepositoryExtension
 {
     /// <summary>
-    /// 实体假删除 _rep.Context.Updateable(entity).FakeDelete().ExecuteCommandAsync();
+    /// 实体假删除 _rep.FakeDelete(entity)
     /// </summary>
     /// <typeparam name="T"></typeparam>
-    /// <param name="updateable"></param>
+    /// <param name="repository"></param>
+    /// <param name="entity"></param>
     /// <returns></returns>
-    public static IUpdateable<T> FakeDelete<T>(this IUpdateable<T> updateable) where T : EntityBase, new()
+    public static int FakeDelete<T>(this ISugarRepository repository, T entity) where T : EntityBase, new()
     {
-        return updateable.ReSetValue(x => { x.IsDelete = true; })
+        return repository.Context.Updateable(entity).ReSetValue(x => { x.IsDelete = true; })
             .IgnoreColumns(ignoreAllNullColumns: true)
             .EnableDiffLogEvent()   // 记录差异日志
-            .UpdateColumns(x => new { x.IsDelete, x.UpdateTime, x.UpdateUserId });  // 允许更新的字段-AOP拦截自动设置UpdateTime、UpdateUserId
+            .UpdateColumns(x => new { x.IsDelete, x.UpdateTime, x.UpdateUserId })  // 允许更新的字段-AOP拦截自动设置UpdateTime、UpdateUserId
+            .ExecuteCommand();
     }
 
     /// <summary>
+    /// 实体假删除异步 _rep.FakeDeleteAsync(entity)
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="repository"></param>
+    /// <param name="entity"></param>
+    /// <returns></returns>
+    public static Task<int> FakeDeleteAsync<T>(this ISugarRepository repository, T entity) where T : EntityBase, new()
+    {
+        return repository.Context.Updateable(entity).ReSetValue(x => { x.IsDelete = true; })
+            .IgnoreColumns(ignoreAllNullColumns: true)
+            .EnableDiffLogEvent()   // 记录差异日志
+            .UpdateColumns(x => new { x.IsDelete, x.UpdateTime, x.UpdateUserId })  // 允许更新的字段-AOP拦截自动设置UpdateTime、UpdateUserId
+            .ExecuteCommandAsync();
+    }
+
+
+    /// <summary>
     /// 排序方式(默认降序)
     /// </summary>
     /// <param name="queryable"></param>
@@ -43,4 +62,64 @@ public static class RepositoryExtension
         }
         return queryable.OrderByIF(!string.IsNullOrWhiteSpace(orderStr), orderStr);
     }
+
+    /// <summary>
+    /// 更新实体并记录差异日志 _rep.UpdateWithDiffLog(entity)
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="repository"></param>
+    /// <param name="entity"></param>
+    /// <param name="ignoreAllNullColumns"></param>
+    /// <returns></returns>
+    public static int UpdateWithDiffLog<T>(this ISugarRepository repository, T entity, bool ignoreAllNullColumns = true) where T : EntityBase, new()
+    {
+        return repository.Context.Updateable(entity)
+            .IgnoreColumns(ignoreAllNullColumns: ignoreAllNullColumns)
+            .EnableDiffLogEvent()
+            .ExecuteCommand();
+    }
+
+    /// <summary>
+    /// 更新实体并记录差异日志 _rep.UpdateWithDiffLogAsync(entity)
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="repository"></param>
+    /// <param name="entity"></param>
+    /// <param name="ignoreAllNullColumns"></param>
+    /// <returns></returns>
+    public static Task<int> UpdateWithDiffLogAsync<T>(this ISugarRepository repository, T entity, bool ignoreAllNullColumns = true) where T : EntityBase, new()
+    {
+        return repository.Context.Updateable(entity)
+            .IgnoreColumns(ignoreAllNullColumns: ignoreAllNullColumns)
+            .EnableDiffLogEvent()
+            .ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 新增实体并记录差异日志 _rep.InsertWithDiffLog(entity)
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="repository"></param>
+    /// <param name="entity"></param>
+    /// <returns></returns>
+    public static int InsertWithDiffLog<T>(this ISugarRepository repository, T entity) where T : EntityBase, new()
+    {
+        return repository.Context.Insertable(entity)
+            .EnableDiffLogEvent()
+            .ExecuteCommand();
+    }
+
+    /// <summary>
+    /// 新增实体并记录差异日志 _rep.InsertWithDiffLogAsync(entity)
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="repository"></param>
+    /// <param name="entity"></param>
+    /// <returns></returns>
+    public static Task<int> InsertWithDiffLogAsync<T>(this ISugarRepository repository, T entity) where T : EntityBase, new()
+    {
+        return repository.Context.Insertable(entity)
+            .EnableDiffLogEvent()
+            .ExecuteCommandAsync();
+    }
 }

+ 5 - 0
Admin.NET/Admin.NET.Core/Option/ConnectionStringsOptions.cs

@@ -27,6 +27,11 @@ public class ConnectionStringsOptions : IConfigurableOptions
     public bool InitTable { get; set; }
 
     /// <summary>
+    /// 禁用数据库表差异日志
+    /// </summary>
+    public bool DisableDiffLog { get; set; }
+    
+    /// <summary>
     /// 业务库集合
     /// </summary>
     public List<DbConfig> DbConfigs { get; set; } = new List<DbConfig>();

+ 8 - 4
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -11,11 +11,12 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
     /// <returns></returns>
     public IEnumerable<SysMenu> HasData()
     {
+        //return null;
         return new[]
         {
-            new SysMenu{ Id=252885263003710, Pid=0, Title="数据面板", Path="/dashboard", Name="Dashboard", Component="LAYOUT", Redirect="/dashboard/analysis", Icon="ant-design:home-outlined", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysMenu{ Id=252885263003711, Pid=252885263003710, Title="分析页", Path="analysis", Name="Analysis", Component="/dashboard/analysis/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
-            new SysMenu{ Id=252885263003712, Pid=252885263003710, Title="工作台", Path="workbench", Name="Workbench", Component="/dashboard/workbench/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+                new SysMenu{ Id=252885263003710, Pid=0, Title="数据面板", Path="/dashboard", Name="Dashboard", Component="LAYOUT", Redirect="/dashboard/analysis", Icon="ant-design:home-outlined", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+                new SysMenu{ Id=252885263003711, Pid=252885263003710, Title="分析页", Path="analysis", Name="Analysis", Component="/dashboard/analysis/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
+                new SysMenu{ Id=252885263003712, Pid=252885263003710, Title="工作台", Path="workbench", Name="Workbench", Component="/dashboard/workbench/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
 
             new SysMenu{ Id=252885263003720, Pid=0, Title="系统管理", Path="/sys", Name="sys", Component="LAYOUT", Redirect="", Icon="ant-design:setting-outlined", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=252885263003730, Pid=252885263003720, Title="账号管理", Path="user", Name="UserManagement", Component="/sys/admin/user/index", Icon="ant-design:user-outlined", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
@@ -112,7 +113,10 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=252885263003930, Pid=252885263003900, Title="异常日志", Path="exlog", Name="ExlogManagement", Component="/sys/admin/log/exlog/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=252885263003931, Pid=252885263003930, Title="日志查询", Permission="sysExlog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=252885263003932, Pid=252885263003930, Title="日志清空", Permission="sysExlog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
-
+            new SysMenu{ Id=252885263003935, Pid=252885263003900, Title="差异日志", Path="difflog", Name="DifflogManagement", Component="/sys/admin/log/difflog/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=252885263003936, Pid=252885263003935, Title="日志查询", Permission="sysDifflog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+            new SysMenu{ Id=252885263003937, Pid=252885263003935, Title="日志清空", Permission="sysDifflog:clear", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
+                        
             new SysMenu{ Id=252885263003940, Pid=0, Title="文件管理", Path="/file", Name="file", Component="LAYOUT", Redirect="", Icon="ant-design:file-outlined", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=400 },
             new SysMenu{ Id=252885263003950, Pid=252885263003940, Title="文件管理", Path="file", Name="FileManagement", Component="/sys/admin/file/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
             new SysMenu{ Id=252885263003951, Pid=252885263003950, Title="文件查询", Permission="sysFile:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },

+ 40 - 0
Admin.NET/Admin.NET.Core/Service/log/SysDiffLogService.cs

@@ -0,0 +1,40 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 差异日志服务
+/// </summary>
+[ApiDescriptionSettings(Name = "差异日志", Order = 180)]
+public class SysDiffLogService : IDynamicApiController, ITransient
+{
+    private readonly SqlSugarRepository<SysLogDiff> _sysDiffLogRep;
+
+    public SysDiffLogService(SqlSugarRepository<SysLogDiff> sysDiffLogRep)
+    {
+        _sysDiffLogRep = sysDiffLogRep;
+    }
+
+    /// <summary>
+    /// 获取差异日志分页列表
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("/sysDiffLog/pageList")]
+    [NotLog]
+    public async Task<SqlSugarPagedList<SysLogDiff>> GetDiffLogList([FromQuery] PageLogInput input)
+    {
+        return await _sysDiffLogRep.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, SqlSugar.OrderByType.Desc)
+            .ToPagedListAsync(input.Page, input.PageSize);
+    }
+
+    /// <summary>
+    /// 清空差异日志
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysDiffLog/clear")]
+    public async Task<bool> ClearVisLog()
+    {
+        return await _sysDiffLogRep.DeleteAsync(u => u.Id > 0);
+    }
+}

+ 32 - 2
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs

@@ -69,14 +69,19 @@ public static class SqlSugarSetup
                             Console.ForegroundColor = ConsoleColor.White;
                         if (sql.StartsWith("DELETE"))
                             Console.ForegroundColor = ConsoleColor.Blue;
-                        Console.WriteLine(sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
+                        Console.WriteLine("\r\n" + "=========执行SQL============" + "\r\n" + UtilMethods.GetSqlString(DbType.MySql, sql, pars) + "\r\n");
+                        //Console.WriteLine(sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)) + "\r\n" + "========================" + "\r\n");
                         App.PrintToMiniProfiler("SqlSugar", "Info", sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
+
+
+
                     };
                     dbProvider.Aop.OnError = (ex) =>
                     {
                         Console.ForegroundColor = ConsoleColor.Red;
                         var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
-                        Console.WriteLine($"{ex.Message}{Environment.NewLine}{ex.Sql}{Environment.NewLine}{pars}{Environment.NewLine}");
+                        Console.WriteLine("\r\n" + "=========SQL错误============" + "\r\n" + UtilMethods.GetSqlString(DbType.MySql, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n");
+                        //Console.WriteLine($"{ex.Message}{Environment.NewLine}{ex.Sql}{Environment.NewLine}{pars}{Environment.NewLine}");
                         App.PrintToMiniProfiler("SqlSugar", "Error", $"{ex.Message}{Environment.NewLine}{ex.Sql}{pars}{Environment.NewLine}");
                     };
 
@@ -115,6 +120,31 @@ public static class SqlSugarSetup
                         }
                     };
 
+                    dbProvider.Aop.OnDiffLogEvent = async it =>
+                    {
+                        if (dbOptions.DisableDiffLog) return;
+
+                        var logProvider = db.GetConnectionScope(SqlSugarConst.ConfigId);
+                        var log = new SysLogDiff
+                        {
+
+                            //操作后记录   包含: 字段描述 列名 值  表名 表描述
+                            AfterData = Newtonsoft.Json.JsonConvert.SerializeObject(it.AfterData),
+                            //操作前记录  包含: 字段描述 列名 值 表名 表描述
+                            BeforeData = Newtonsoft.Json.JsonConvert.SerializeObject(it.BeforeData),
+                            //传进来的对象
+                            BusinessData = Newtonsoft.Json.JsonConvert.SerializeObject(it.BusinessData),
+                            //enum insert 、update and delete  
+                            DiffType = it.DiffType.ToString(),
+                            Sql = UtilMethods.GetSqlString(DbType.MySql, it.Sql, it.Parameters),
+                            Parameters = Newtonsoft.Json.JsonConvert.SerializeObject(it.Parameters),
+                            Duration = it.Time == null ? 0 : (long)it.Time.Value.TotalMilliseconds
+                        };
+                        await logProvider.Insertable(log).ExecuteCommandAsync();
+                        Console.ForegroundColor = ConsoleColor.Red;
+                        Console.WriteLine($"***差异日志开始***{Environment.NewLine}{Newtonsoft.Json.JsonConvert.SerializeObject(log)}{Environment.NewLine}***差异日志结束***");
+                    };
+
                     // 配置实体假删除过滤器
                     SetDeletedEntityFilter(dbProvider);
                     // 配置实体机构过滤器

+ 3 - 5
Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/Service.cs.vm

@@ -10,7 +10,6 @@ using Admin.NET.Core;
 using Admin.NET.Core.Service;
 using System;
 using @(@Model.NameSpace).Entity;
-using Admin.NET.Core.Extension;
 
 namespace @Model.NameSpace
 {
@@ -95,7 +94,7 @@ namespace @Model.NameSpace
         public async Task Add(Add@(@Model.ClassName)Input input)
         {
             var entity = input.Adapt<@(@Model.ClassName)>();
-            await _rep.InsertAsync(entity);
+            await _rep.InsertWithDiffLogAsync(entity);
         }
 
         /// <summary>
@@ -111,8 +110,7 @@ if (@column.ColumnKey == "True"){
             @:var entity = await _rep.GetFirstAsync(u => u.@(@column.ColumnName) == input.@(@column.ColumnName));
 }
 }
-            await _rep.Context.Updateable(entity).FakeDelete().ExecuteCommandAsync();   //假删除
-            //await _rep.DeleteAsync(entity);   //真删除
+            await _rep.FakeDeleteAsync(entity);   //假删除
         }
 
         /// <summary>
@@ -124,7 +122,7 @@ if (@column.ColumnKey == "True"){
         public async Task Update(Update@(@Model.ClassName)Input input)
         {
             var entity = input.Adapt<@(@Model.ClassName)>();
-            await _rep.Context.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
+            await _rep.UpdateWithDiffLogAsync(entity);
         }
 
         /// <summary>

+ 8 - 0
Vben2/src/api/sys/admin.ts

@@ -55,6 +55,8 @@ enum Api {
   ClearOpLog = '/sysOpLog/clear',
   ExlogPageList = '/sysExLog/pageList',
   ClearExLog = '/sysExLog/clear',
+  DifflogPageList = '/sysDiffLog/pageList',
+  ClearDiffLog = '/sysDiffLog/clear',
 
   // 文件接口
   FilePageList = '/sysFile/pageList',
@@ -237,6 +239,12 @@ export const getExLogPageList = (params?: any) =>
 //清空异常日志
 export const clearExLog = () => defHttp.post({ url: Api.ClearExLog });
 
+// 获取差异日志分页列表
+export const getDiffLogPageList = (params?: any) =>
+  defHttp.get<any>({ url: Api.DifflogPageList, params });
+//清空差异日志
+export const clearDiffLog = () => defHttp.post({ url: Api.ClearDiffLog });
+
 ////////// 文件管理接口 //////////
 // 获取文件分页列表
 export const getFilePageList = (params?: any) =>

+ 62 - 0
Vben2/src/views/sys/admin/log/difflog/difflog.data.ts

@@ -0,0 +1,62 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '差异操作',
+    dataIndex: 'diffType',
+    width: 100,
+    align: 'left',
+    slots: { customRender: 'diffType' },
+  },
+  {
+    title: 'SQL语句',
+    dataIndex: 'sql',
+    width: 200,
+    slots: { customRender: 'sql' },
+  },
+  {
+    title: '耗时(毫秒)',
+    dataIndex: 'duration',
+    width: 200,
+    slots: { customRender: 'duration' },
+  },
+  {
+    title: '变更前数据',
+    dataIndex: 'beforeData',
+    width: 200,
+    slots: { customRender: 'beforeData' },
+  },
+  {
+    title: '变更后数据',
+    dataIndex: 'afterData',
+    width: 200,
+    slots: { customRender: 'afterData' },
+  },
+  {
+    title: '业务对象',
+    dataIndex: 'businessData',
+    width: 200,
+    slots: { customRender: 'businessData' },
+  },
+  {
+    title: '操作时间',
+    dataIndex: 'createTime',
+    width: 180,
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'startTime',
+    label: '开始时间',
+    component: 'DatePicker',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'endTime',
+    label: '结束时间',
+    component: 'DatePicker',
+    colProps: { span: 8 },
+  },
+];

+ 89 - 0
Vben2/src/views/sys/admin/log/difflog/index.vue

@@ -0,0 +1,89 @@
+<template>
+  <div>
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button
+          type="primary"
+          @click="handleClear"
+          :disabled="!hasPermission('sysDifflog:clear')"
+        >
+          清空日志
+        </a-button>
+      </template>
+      <template #diffType="{ text }">
+        {{ text.toUpperCase() }}
+      </template>
+      <template #sql="{ text, record }">
+        <a @click="showInfo(record)" :title="text">点击查看</a>
+      </template>
+      <template #duration="{ text }">
+        {{ text }}
+      </template>
+      <template #beforeData="{ text }">
+        <!-- {{ text }} -->
+      </template>
+      <template #afterData="{ text }">
+        <!-- {{ text }} -->
+      </template>
+      <template #businessData="{ text }">
+        <!-- {{ text }} -->
+      </template>
+    </BasicTable>
+    <Drawer @register="register" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+  import { BasicTable, useTable } from '/@/components/Table';
+  import { useDrawer } from '/@/components/Drawer';
+  import { usePermission } from '/@/hooks/web/usePermission';
+
+  import { columns, searchFormSchema } from './difflog.data';
+  import { getDiffLogPageList, clearDiffLog } from '/@/api/sys/admin';
+  import Drawer from './info.vue';
+
+  export default defineComponent({
+    name: 'ExlogManagement',
+    components: { BasicTable, Drawer },
+    setup() {
+      const { hasPermission } = usePermission();
+      const [registerTable, { reload }] = useTable({
+        title: '日志列表',
+        api: getDiffLogPageList,
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+        },
+        striped: false,
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        showIndexColumn: false,
+        canResize: true,
+        pagination: {
+          pageSize: 10,
+        },
+      });
+
+      async function handleClear() {
+        await clearDiffLog();
+        reload();
+      }
+      const [register, { openDrawer }] = useDrawer();
+
+      function showInfo(record) {
+        openDrawer(true, { ...record });
+      }
+
+      return {
+        registerTable,
+        handleClear,
+        hasPermission,
+        register,
+        openDrawer,
+        showInfo,
+      };
+    },
+  });
+</script>

+ 39 - 0
Vben2/src/views/sys/admin/log/difflog/info.vue

@@ -0,0 +1,39 @@
+<template>
+  <BasicDrawer v-bind="$attrs" @register="register" title="日志详情" width="50%">
+    <Tabs tab-position="left" v-model:activeKey="activeKey">
+      <TabPane key="SQL语句" tab="SQL语句">
+        {{ log.sql }}
+      </TabPane>
+      <TabPane key="变更前数据" tab="变更前数据">
+        <JsonPreview v-if="log.beforeData" :data="JSON.parse(log.beforeData)" />
+      </TabPane>
+      <TabPane key="变更后数据" tab="变更后数据">
+        <JsonPreview v-if="log.afterData" :data="JSON.parse(log.afterData)" />
+      </TabPane>
+      <TabPane key="业务对象" tab="业务对象">
+        <JsonPreview
+          v-if="log.businessData && log.businessData != 'null'"
+          :data="JSON.parse(log.businessData)"
+        />
+      </TabPane>
+    </Tabs>
+  </BasicDrawer>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, unref } from 'vue';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { JsonPreview } from '/@/components/CodeEditor';
+  import { Tabs, TabPane } from 'ant-design-vue';
+  export default defineComponent({
+    components: { BasicDrawer, JsonPreview, Tabs, TabPane },
+    setup() {
+      const activeKey = ref('SQL语句');
+      const log = ref({});
+      const [register] = useDrawerInner((data) => {
+        log.value = unref(data);
+      });
+
+      return { activeKey, register, log };
+    },
+  });
+</script>