Prechádzať zdrojové kódy

!977 优化前端字典使用,添加导出导出方法,并添加示例
Merge pull request !977 from 闫腾/next

zuohuaijun 1 rok pred
rodič
commit
06608c19fa

+ 1 - 0
.gitignore

@@ -37,3 +37,4 @@ node_modules/
 .vs
 .idea
 .DS_Store
+/Web/npminstall-debug.log

+ 6 - 0
Admin.NET/Admin.NET.Application/Admin.NET.Application.csproj

@@ -32,4 +32,10 @@
     <ProjectReference Include="..\Plugins\Admin.NET.Plugin.ReZero\Admin.NET.Plugin.ReZero.csproj" />
   </ItemGroup>
 
+  <ItemGroup>
+    <Folder Include="Demo\" />
+    <Folder Include="Entity\" />
+    <Folder Include="Service\" />
+  </ItemGroup>
+
 </Project>

+ 20 - 0
Admin.NET/Admin.NET.Core/Attribute/ImportDictAttribute.cs

@@ -0,0 +1,20 @@
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 属性字典配置
+/// </summary>
+[AttributeUsage(AttributeTargets.Property)]
+public class ImportDictAttribute : Attribute
+{
+    /// <summary>
+    /// 字典Code
+    /// </summary>
+    public string TypeCode { get; set; }
+
+    ///// <summary>
+    ///// 目标对象类型
+    ///// </summary>
+    //public Type TargetType { get; set; }
+
+    public string TargetPropName { get; set; }
+}

+ 124 - 0
Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs

@@ -336,4 +336,128 @@ public static partial class ObjectExtension
         var masks = mask.ToString().PadLeft(4, mask);
         return email.Replace(@"^([^\.]+)\.?", $"$1{masks}$2");
     }
+
+    /// <summary>
+    /// 将字符串转为值类型,如果没有得到或者错误返回为空
+    /// </summary>
+    /// <typeparam name="T">指定值类型</typeparam>
+    /// <param name="str">传入字符串</param>
+    /// <returns>可空值</returns>
+    public static Nullable<T> ParseTo<T>(this string str) where T : struct
+    {
+        try
+        {
+            if (!string.IsNullOrWhiteSpace(str))
+            {
+                MethodInfo method = typeof(T).GetMethod("Parse", new Type[] { typeof(string) });
+                if (method != null)
+                {
+                    T result = (T)method.Invoke(null, new string[] { str });
+                    return result;
+                }
+            }
+        }
+        catch
+        {
+        }
+        return null;
+    }
+
+    /// <summary>
+    /// 将字符串转为值类型,如果没有得到或者错误返回为空
+    /// </summary>
+    /// <typeparam name="T">指定值类型</typeparam>
+    /// <param name="str">传入字符串</param>
+    /// <param name="type">目标类型</param>
+    /// <returns>可空值</returns>
+    public static object ParseTo(this string str, Type type)
+    {
+        try
+        {
+            if (type.Name == "String")
+            {
+                return str;
+            }
+            if (!string.IsNullOrWhiteSpace(str))
+            {
+                var _type = type;
+                if (type.Name.StartsWith("Nullable"))
+                {
+                    _type = type.GetGenericArguments()[0];
+                }
+                MethodInfo method = _type.GetMethod("Parse", new Type[] { typeof(string) });
+                if (method != null)
+                {
+                    return method.Invoke(null, new string[] { str });
+                }
+            }
+        }
+        catch
+        {
+        }
+        return null;
+    }
+
+
+    /// <summary>
+    /// 将一个的对象属性值赋给另一个制定的对象属性, 只复制相同属性的
+    /// </summary>
+    /// <param name="src">原数据对象</param>
+    /// <param name="target">目标数据对象</param>
+    /// <param name="changeProperties">属性集,键为原属性,值为目标属性</param>
+    /// <param name="unChangeProperties">属性集,目标不修改的属性</param>
+    public static void CopyTo(object src, object target, Dictionary<string, string> changeProperties = null, string[] unChangeProperties = null)
+    {
+        if (src == null || target == null)
+            throw new ArgumentException("src == null || target == null ");
+
+        var SourceType = src.GetType();
+        var TargetType = target.GetType();
+
+        if (changeProperties == null || changeProperties.Count == 0)
+        {
+            var fields = TargetType.GetProperties();
+            changeProperties = fields.Select(m => m.Name).ToDictionary(m => m);
+        }
+
+        if (unChangeProperties == null || unChangeProperties.Length == 0)
+        {
+            foreach (var item in changeProperties)
+            {
+                var srcProperty = SourceType.GetProperty(item.Key);
+                if (srcProperty != null)
+                {
+                    var sourceVal = srcProperty
+                        .GetValue(src, null);
+
+                    var tarProperty = TargetType.GetProperty(item.Value);
+                    if (tarProperty != null)
+                    {
+                        tarProperty.SetValue(target, sourceVal, null);
+                    }
+                }
+            }
+        }
+        else
+        {
+            foreach (var item in changeProperties)
+            {
+                if (!unChangeProperties.Any(m => m == item.Value))
+                {
+                    var srcProperty = SourceType.GetProperty(item.Key);
+                    if (srcProperty != null)
+                    {
+                        var sourceVal = srcProperty
+                            .GetValue(src, null);
+
+                        var tarProperty = TargetType.GetProperty(item.Value);
+                        if (tarProperty != null)
+                        {
+                            tarProperty.SetValue(target, sourceVal, null);
+                        }
+                    }
+                }
+            }
+        }
+    }
 }

+ 94 - 0
Admin.NET/Admin.NET.Core/Service/Common/Dto/ProcDto.cs

@@ -0,0 +1,94 @@
+// 麻省理工学院许可证
+//
+// 版权所有 (c) 2021-2023 zuohuaijun,大名科技(天津)有限公司  联系电话/微信:18020030720  QQ:515096995
+//
+// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
+//
+// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
+// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
+
+using Magicodes.ExporterAndImporter.Core.Filters;
+using Magicodes.ExporterAndImporter.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 基础存储过程输入类
+/// </summary> 
+public class BaseProcInput
+{
+    /// <summary>
+    /// ProcId
+    /// </summary>
+    public string ProcId { get; set; }
+
+    /// <summary>
+    /// 数据库配置ID
+    /// </summary>
+    public string ConfigId { get; set; }
+
+    /// <summary>
+    /// 存储过程输入参数
+    /// </summary>
+    /// <example>{"id":"351060822794565"}</example>
+    public Dictionary<string, object> ProcParams { get; set; }
+}
+
+/// <summary>
+/// 带表头名称存储过程输入类
+/// </summary>
+public class ExportProcByTMPInput : BaseProcInput
+{
+    /// <summary>
+    /// 模板名称
+    /// </summary>
+    public string Template { get; set; }
+}
+
+/// <summary>
+/// 带表头名称存储过程输入类
+/// </summary>
+public class ExportProcInput : BaseProcInput
+{
+    public Dictionary<string, string> EHeader { get; set; }
+}
+/// <summary>
+/// 指定导出类名(有排序)存储过程输入类
+/// </summary>
+public class ExportProcInput2 : BaseProcInput
+{
+    public List<string> EHeader { get; set; }
+}
+
+/// <summary>
+/// 前端指定列
+/// </summary>
+public class ProcExporterHeaderFilter : IExporterHeaderFilter
+{
+    private Dictionary<string, Tuple<string, int>> _includeHeader;
+    public ProcExporterHeaderFilter(Dictionary<string, Tuple<string, int>> includeHeader)
+    {
+        _includeHeader = includeHeader;
+    }
+    public ExporterHeaderInfo Filter(ExporterHeaderInfo exporterHeaderInfo)
+    {
+        if (_includeHeader != null && _includeHeader.Count > 0)
+        {
+            var key = exporterHeaderInfo.PropertyName.ToUpper();
+            if (_includeHeader.ContainsKey(key))
+            {
+                exporterHeaderInfo.DisplayName = _includeHeader[key].Item1;
+                return exporterHeaderInfo;
+            }
+            else
+            {
+                exporterHeaderInfo.ExporterHeaderAttribute.Hidden = true;
+            }
+        }
+        return exporterHeaderInfo;
+    }
+}

+ 118 - 0
Admin.NET/Admin.NET.Core/Service/Common/ProcService.cs

@@ -0,0 +1,118 @@
+// 麻省理工学院许可证
+//
+// 版权所有 (c) 2021-2023 zuohuaijun,大名科技(天津)有限公司  联系电话/微信:18020030720  QQ:515096995
+//
+// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
+//
+// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
+// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 存储过程服务
+/// 适用于导出、图表查询
+/// </summary>
+public class ProcService : IDynamicApiController, ITransient
+{
+
+    /// <summary>
+    /// Post导出存储过程数据,指定导出列,没有指定的字段会被隐藏
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet]
+    [HttpPost]
+    public async Task<IActionResult> PocExport2(ExportProcInput input)
+    {
+        ISqlSugarClient _db = App.GetService<ISqlSugarClient>();
+        var db = _db.AsTenant().GetConnectionScope(input.ConfigId);
+        var rs = await db.Ado.UseStoredProcedure()
+                .GetDataTableAsync(input.ProcId, input.ProcParams);
+
+        var excelExporter = new Magicodes.ExporterAndImporter.Excel.ExcelExporter();
+        Dictionary<string, Tuple<string, int>> headers = new Dictionary<string, Tuple<string, int>>();
+        var i = 1;
+        foreach (var val in input.EHeader)
+        {
+            headers.Add(val.Key.ToUpper(), new Tuple<string, int>(val.Value, i));
+            i++;
+        }
+        var da = await excelExporter.ExportAsByteArray(rs, new ProcExporterHeaderFilter(headers));
+
+        return new FileContentResult(da, "application/octet-stream") { FileDownloadName = input.ProcId + ".xlsx" };
+    }
+
+    /// <summary>
+    /// 根据模板导出存储过程数据
+    /// </summary>
+    /// <returns></returns> 
+    [HttpGet]
+    [HttpPost]
+    public async Task<IActionResult> PocExport(ExportProcByTMPInput input)
+    {
+        ISqlSugarClient _db = App.GetService<ISqlSugarClient>();
+        var db = _db.AsTenant().GetConnectionScope(input.ConfigId);
+        var rs = await db.Ado.UseStoredProcedure()
+                .GetDataTableAsync(input.ProcId, input.ProcParams);
+
+        var excelExporter = new Magicodes.ExporterAndImporter.Excel.ExcelExporter();
+
+        string template = AppDomain.CurrentDomain.BaseDirectory + "/wwwroot/Template/" + input.Template + ".xlsx";
+        var bs = await excelExporter.ExportBytesByTemplate(rs, template);
+        return new FileContentResult(bs, "application/octet-stream") { FileDownloadName = input.ProcId + ".xlsx" };
+    }
+    /// <summary>
+    /// 读取存储过程返回表
+    /// 注意Oracle,达梦参数顺序不能错
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns> 
+    [HttpPost]
+    public async Task<DataTable> ProcTable(BaseProcInput input)
+    { 
+        ISqlSugarClient _db = App.GetService<ISqlSugarClient>();
+        var db = _db.AsTenant().GetConnectionScope(input.ConfigId);
+        return await db.Ado.UseStoredProcedure()
+                .GetDataTableAsync(input.ProcId, input.ProcParams);
+    }
+
+    /// <summary>
+    /// 读取存储过程返回数据集
+    /// 注意Oracle,达梦参数顺序不能错;Oracle 返回table、table1,其他返回table1、table2
+    /// 适用于报表、复杂详细页面等
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost]
+    public async Task<DataSet> CommonDataSet(BaseProcInput input)
+    {
+        ISqlSugarClient _db = App.GetService<ISqlSugarClient>();
+        var db = _db.AsTenant().GetConnectionScope(input.ConfigId);
+        return await db.Ado.UseStoredProcedure()
+            .GetDataSetAllAsync(input.ProcId, input.ProcParams);
+    }
+    /*
+     * 
+    //根据配置表读取对映存储过程
+    public async Task<DataTable> ProcEnitybyConfig(BaseProcInput input)
+    {
+        string key = "ProcConfig";
+        var ds = _sysCacheService.Get<Dictionary<string, string>>(key);
+        if (ds == null || ds.Count == 0 || !ds.ContainsKey(input.ProcId))
+        {
+            var datas = await _db.Queryable<ProcConfig>().ToListAsync();
+            ds = datas.ToDictionary(m => m.ProcId, m => m.ProcName);
+            _sysCacheService.Set(key, ds);
+        }
+        string procName = ds[input.ProcId];
+        return await _db.Ado.UseStoredProcedure()
+            .GetDataTableAsync(procName, input.ProcParams);
+    }
+    */
+}

+ 10 - 7
Admin.NET/Admin.NET.Core/Service/Dict/SysDictTypeService.cs

@@ -147,20 +147,23 @@ public class SysDictTypeService : IDynamicApiController, ITransient
         await _sysDictTypeRep.UpdateAsync(dictType);
     }
 
+
     /// <summary>
     /// 获取所有字典集合 🔖
     /// </summary>
     /// <returns></returns>
     [AllowAnonymous]
     [DisplayName("获取所有字典集合")]
-    public async Task<List<SysDictType>> GetAllDictList()
+    public async Task<dynamic> GetAllDictList()
     {
-        var dictList = await _sysDictTypeRep.AsQueryable()
-            .Includes(u => u.Children)
-            .OrderBy(u => new { u.OrderNo, u.Code })
+        var ds = await _sysDictTypeRep.AsQueryable()
+            .InnerJoin<SysDictData>((m, n) => m.Id == n.DictTypeId)
+            .Where((m, n) => m.IsDelete == false && n.IsDelete == false && n.Status == StatusEnum.Enable)
+            .Select((m, n) => new { TypeCode = m.Code,  n.Code, n.Value, n.Remark, n.OrderNo,n.TagType })
             .ToListAsync();
-        // 字典数据项排序
-        dictList.ForEach(u => u.Children = u.Children.OrderBy(c => c.OrderNo).ThenBy(c => c.Code).ToList());
-        return dictList;
+        return ds
+            .OrderBy(s => s.OrderNo).GroupBy(m => m.TypeCode)
+            .ToDictionary(m => m.Key, m => m);
     }
+
 }

+ 100 - 0
Admin.NET/Admin.NET.Core/Util/ChinaDateTimeConverter.cs

@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json;
+
+namespace Admin.NET.Core
+{
+    /// <summary>
+    /// JSON时间序列化yyyy-MM-dd HH:mm:ss
+    /// </summary>
+    public class ChinaDateTimeConverter : DateTimeConverterBase
+    {
+        private static IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
+        /// <summary>
+        /// ReadJson
+        /// </summary>
+        /// <param name="reader"></param>
+        /// <param name="objectType"></param>
+        /// <param name="existingValue"></param>
+        /// <param name="serializer"></param>
+        /// <returns></returns>
+        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        {
+            return dtConverter.ReadJson(reader, objectType, existingValue, serializer);
+        }
+        /// <summary>
+        /// WriteJson
+        /// </summary>
+        /// <param name="writer"></param>
+        /// <param name="value"></param>
+        /// <param name="serializer"></param>
+        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        {
+            dtConverter.WriteJson(writer, value, serializer);
+        }
+    }
+
+    /// <summary>
+    /// JSON时间序列化yyyy-MM-dd HH:mm
+    /// </summary>
+    public class ChinaDateTimeConverterHH : DateTimeConverterBase
+    {
+        private static IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm" };
+        /// <summary>
+        /// ReadJson
+        /// </summary>
+        /// <param name="reader"></param>
+        /// <param name="objectType"></param>
+        /// <param name="existingValue"></param>
+        /// <param name="serializer"></param>
+        /// <returns></returns>
+        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        {
+            return dtConverter.ReadJson(reader, objectType, existingValue, serializer);
+        }
+        /// <summary>
+        /// WriteJson
+        /// </summary>
+        /// <param name="writer"></param>
+        /// <param name="value"></param>
+        /// <param name="serializer"></param>
+        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        {
+            dtConverter.WriteJson(writer, value, serializer);
+        }
+    }
+
+    /// <summary>
+    /// JSON时间序列化yyyy-MM-dd
+    /// </summary>
+    public class ChinaDateTimeConverterDate : DateTimeConverterBase
+    {
+        private static IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" };
+        /// <summary>
+        /// ReadJson
+        /// </summary>
+        /// <param name="reader"></param>
+        /// <param name="objectType"></param>
+        /// <param name="existingValue"></param>
+        /// <param name="serializer"></param>
+        /// <returns></returns>
+        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        {
+            return dtConverter.ReadJson(reader, objectType, existingValue, serializer);
+        }
+        /// <summary>
+        /// WriteJson
+        /// </summary>
+        /// <param name="writer"></param>
+        /// <param name="value"></param>
+        /// <param name="serializer"></param>
+        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        {
+            dtConverter.WriteJson(writer, value, serializer);
+        }
+    }
+}

+ 258 - 39
Admin.NET/Admin.NET.Core/Util/CommonUtil.cs

@@ -103,73 +103,292 @@ public static class CommonUtil
     /// <summary>
     /// 导出模板Excel
     /// </summary>
-    /// <param name="fileName"></param>
     /// <returns></returns>
-    public static async Task<IActionResult> ExportExcelTemplate<T>(string fileName) where T : class, new()
-    {
-        fileName = $"{fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx";
+    public static async Task<IActionResult> ExportExcelTemplate<T>() where T : class,new()
+    { 
         IImporter importer = new ExcelImporter();
-        var res = await importer.GenerateTemplate<T>(Path.Combine(App.WebHostEnvironment.WebRootPath, fileName));
-        return new FileStreamResult(new FileStream(res.FileName, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
+        var res=await importer.GenerateTemplateBytes<T>();  
+
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = typeof(T).Name+".xlsx" };
     }
 
     /// <summary>
-    /// 导出模板Excel
+    /// 导出数据excel
     /// </summary>
-    /// <param name="fileName"></param>
-    /// <param name="fileDto"></param>
     /// <returns></returns>
-    public static async Task<IActionResult> ExportExcelTemplate(string fileName, dynamic fileDto)
+    public static async Task<IActionResult> ExportExcelData<T>(ICollection<T> data) where T : class, new()
     {
-        MethodInfo generateTemplateMethod = typeof(CommonUtil).GetMethods().FirstOrDefault(p => p.Name == "ExportExcelTemplate" && p.IsGenericMethodDefinition);
-        MethodInfo closedGenerateTemplateMethod = generateTemplateMethod.MakeGenericMethod(fileDto.GetType());
-        return await (Task<IActionResult>)closedGenerateTemplateMethod.Invoke(null, new object[] { fileName });
+        var export = new ExcelExporter();
+        var res = await export.ExportAsByteArray<T>(data);
+
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = typeof(T).Name + ".xlsx" };
     }
 
     /// <summary>
-    /// 导入数据Excel
+    /// 导出数据excel,包括字典转换
     /// </summary>
-    /// <typeparam name="T"></typeparam>
-    /// <param name="file"></param>
-    /// <param name="importResultCallback"></param>
     /// <returns></returns>
-    public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file, Func<ImportResult<T>, ImportResult<T>> importResultCallback = null) where T : class, new()
+    public static async Task<IActionResult> ExportExcelData<TSource, TTarget>(ISugarQueryable<TSource> query, Func<TSource, TTarget, TTarget> action = null) 
+        where TSource : class, new() where TTarget : class, new ()
     {
-        var newFile = await App.GetRequiredService<SysFileService>().UploadFile(file, "");
-        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, newFile.FilePath, newFile.Id.ToString() + newFile.Suffix);
-        var errorFileUrl = Path.Combine(newFile.FilePath, newFile.Id.ToString() + "_" + newFile.Suffix);
+        var PropMappings = GetExportPropertMap< TSource, TTarget >(); 
+        var data = query.ToList();
+        //相同属性复制值,字典值转换
+        var result = new List<TTarget>();
+        foreach (var item in data)
+        {
+            var newData = new TTarget();
+            foreach (var dict in PropMappings)
+            {
+                var targeProp = dict.Value.Item3;
+                if (targeProp != null)
+                {
+                    var propertyInfo = dict.Value.Item2;
+                    var sourceVal = propertyInfo.GetValue(item, null);
+                    if (sourceVal == null)
+                    {
+                        continue;
+                    }
+
+                    var map = dict.Value.Item1;
+                    if (map != null && map.ContainsKey(sourceVal))
+                    {
+                        var newVal = map[sourceVal];
+                        targeProp.SetValue(newData, newVal);
+                    }
+                    else
+                    {
+                        if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
+                        {
+                            targeProp.SetValue(newData, sourceVal);
+                        }
+                        else
+                        {
+                            var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType);
+                            targeProp.SetValue(newData, newVal);
+                        }
+                    }
+                }
+                if (action != null)
+                {
+                    newData = action(item, newData);
+                }
+            } 
+            result.Add(newData);
+        } 
+        var export = new ExcelExporter();
+        var res = await export.ExportAsByteArray(result);
 
+        return new FileContentResult(res, "application/octet-stream") { FileDownloadName = typeof(TTarget).Name + ".xlsx" };
+    }
+
+    /// <summary>
+    /// 导入数据Excel
+    /// </summary>
+    /// <param name="file"></param> 
+    /// <returns></returns>
+    public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file) where T : class, new()
+    {  
         IImporter importer = new ExcelImporter();
-        var res = await importer.Import<T>(filePath, importResultCallback);
-        if (res == null || res.Exception != null)
-            throw Oops.Oh("导入异常:" + res.Exception);
+        var res = await importer.Import<T>(file.OpenReadStream());
+        string message = string.Empty;
         if (res.HasError)
         {
-            if (res.TemplateErrors.Count > 0)
+            if (res.Exception != null)
+                message += $"\r\n{res.Exception.Message}";
+            foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
+            {
+                int rowNum = drErrorInfo.RowIndex;
+                foreach (var item in drErrorInfo.FieldErrors)
+                    message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
+            }
+            message += "字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
+
+            throw Oops.Oh("导入异常:" + message);
+        }
+        return res.Data;
+    }
+
+    //例:List<Dm_ApplyDemo> ls = CommonUtil.ParseList<Dm_ApplyDemoInport, Dm_ApplyDemo>(importResult.Data);
+    /// <summary>
+    /// 对象转换 含字典转换
+    /// </summary>
+    /// <typeparam name="TSource"></typeparam>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <param name="data"></param>
+    /// <param name="action"></param>
+    /// <returns></returns>
+    public static List<TTarget> ParseList<TSource, TTarget>(IEnumerable<TSource> data, Func<TSource, TTarget, TTarget> action=null) where TTarget : new()
+    {
+        Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>> PropMappings = GetImportPropertMap<TSource, TTarget>();
+        //相同属性复制值,字典值转换
+        var result = new List<TTarget>();
+        foreach (var item in data)
+        {
+            var newData = new TTarget();
+            foreach (var dict in PropMappings)
+            {
+                var targeProp = dict.Value.Item3;
+                if (targeProp != null)
+                {
+                    var propertyInfo = dict.Value.Item2;
+                    var sourceVal = propertyInfo.GetValue(item, null);
+                    if (sourceVal == null)
+                    {
+                        continue;
+                    }
+
+                    var map = dict.Value.Item1;
+                    if (map != null && map.ContainsKey(sourceVal.ToString()))
+                    {
+                        var newVal = map[sourceVal.ToString()];
+                        targeProp.SetValue(newData, newVal);
+                    }
+                    else
+                    {
+                        if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
+                        {
+                            targeProp.SetValue(newData, sourceVal);
+                        }
+                        else
+                        {
+                            var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType);
+                            targeProp.SetValue(newData, newVal);
+                        }
+                    }
+                }
+            }
+            if (action != null)
+            {
+                newData = action(item, newData);
+            }
+            if (newData != null)
+                result.Add(newData);
+        }
+        return result;
+    }
+
+
+    /// <summary>
+    /// 获取导入属性映射       
+    /// </summary>
+    /// <typeparam name="TSource"></typeparam>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
+    private static Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>> GetImportPropertMap<TSource, TTarget>() where TTarget : new()
+    {
+        var dictService = App.GetService<SqlSugarRepository<SysDictData>>();
+        //整理导入对象的 属性名称,<字典数据,原属性信息,目标属性信息>
+        Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>> PropMappings =
+        new Dictionary<string, Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>>();
+
+        var TSourceProps = typeof(TSource).GetProperties().ToList();
+        var TTargetProps = typeof(TTarget).GetProperties().ToDictionary(m => m.Name);
+
+        foreach (var propertyInfo in TSourceProps)
+        {
+            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
+            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
             {
-                throw Oops.Oh("导入模板格式错误");
+                var targetProp = TTargetProps[attrs.TargetPropName];
+
+                var MappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((a, b) =>
+                    new JoinQueryInfos(JoinType.Inner, a.Id == b.DictTypeId))
+                    .Where(a => a.Code == attrs.TypeCode)
+                    .Where((a, b) => a.Status == StatusEnum.Enable && b.Status == StatusEnum.Enable)
+                    .Select((a, b) => new
+                    {
+                        Label = b.Value,
+                        Value = b.Code
+                    }).ToList()
+                    .ToDictionary(m => m.Label, m => m.Value.ParseTo(targetProp.PropertyType));
+                PropMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(
+                    MappingValues, propertyInfo, targetProp
+                    ));
             }
             else
             {
-                throw Oops.Oh($"请下载错误文件,根据提示修改后再次导入,<a href='{errorFileUrl}' target='_blank'>点击下载</a>");
+                PropMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(
+                    null, propertyInfo, TTargetProps.ContainsKey(propertyInfo.Name) ? TTargetProps[propertyInfo.Name] : null
+                    ));
             }
         }
-        return res.Data;
+
+        return PropMappings;
     }
 
+
+
     /// <summary>
-    /// 导入数据Excel
+    /// 获取导出属性映射       
     /// </summary>
-    /// <param name="file"></param>
-    /// <param name="dataDto"></param>
-    /// <returns></returns>
-    public static async Task<dynamic> ImportExcelData([Required] IFormFile file, dynamic dataDto)
+    /// <typeparam name="TSource"></typeparam>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
+    private static Dictionary<string, Tuple<Dictionary<object,string>, PropertyInfo, PropertyInfo>> GetExportPropertMap<TSource, TTarget>() where TTarget : new()
     {
-        MethodInfo importMethod = typeof(CommonUtil).GetMethods().FirstOrDefault(p => p.Name == "ImportExcelData" && p.IsGenericMethodDefinition);
-        MethodInfo closedImportMethod = importMethod.MakeGenericMethod(dataDto.GetType());
-        var parameters = importMethod.GetParameters();
-        var task = (Task)closedImportMethod.Invoke(null, new object[] { file, parameters[1].DefaultValue });
-        await task;
-        return task.GetType().GetProperty("Result").GetValue(task);
+        var dictService = App.GetService<SqlSugarRepository<SysDictData>>();
+        //整理导入对象的 属性名称,<字典数据,原属性信息,目标属性信息>
+        Dictionary<string, Tuple<Dictionary<object,string>, PropertyInfo, PropertyInfo>> PropMappings =
+        new Dictionary<string, Tuple<Dictionary<object,string>, PropertyInfo, PropertyInfo>>();
+
+        var TargetProps = typeof(TTarget).GetProperties().ToList();
+        var SourceProps = typeof(TSource).GetProperties().ToDictionary(m => m.Name);
+
+        foreach (var propertyInfo in TargetProps)
+        {
+            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
+            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
+            {
+                var targetProp = SourceProps[attrs.TargetPropName];
+
+                var MappingValues = dictService.Context.Queryable<SysDictType, SysDictData>((a, b) =>
+                    new JoinQueryInfos(JoinType.Inner, a.Id == b.DictTypeId))
+                    .Where(a => a.Code == attrs.TypeCode)
+                    .Where((a, b) => a.Status == StatusEnum.Enable && b.Status == StatusEnum.Enable)
+                    .Select((a, b) => new
+                    {
+                        Label = b.Value,
+                        Value = b.Code
+                    }).ToList()
+                    .ToDictionary(m => m.Value.ParseTo(targetProp.PropertyType), m => m.Label);
+                PropMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object,string>, PropertyInfo, PropertyInfo>(
+                    MappingValues, targetProp, propertyInfo
+                    ));
+            }
+            else
+            {
+                PropMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object,string>, PropertyInfo, PropertyInfo>(
+                    null, SourceProps.ContainsKey(propertyInfo.Name) ? SourceProps[propertyInfo.Name] : null, propertyInfo
+                    ));
+            }
+        }
+
+        return PropMappings;
+    }
+
+
+    /// <summary>
+    /// 获取属性映射       
+    /// </summary>
+    /// <typeparam name="TSource"></typeparam>
+    /// <typeparam name="TTarget"></typeparam>
+    /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
+    private static Dictionary<string, Tuple<string, string>> GetExportDicttMap<  TTarget>() where TTarget : new()
+    {
+        var dictService = App.GetService<SqlSugarRepository<SysDictData>>();
+        //整理导入对象的 属性名称,目标属性名,字典Code
+        Dictionary<string, Tuple<string, string>> PropMappings = new Dictionary<string, Tuple<string, string>>(); 
+        var TTargetProps = typeof(TTarget).GetProperties(); 
+        foreach (var propertyInfo in TTargetProps)
+        {
+            var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
+            if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
+            { 
+                PropMappings.Add(propertyInfo.Name, new Tuple<string, string>(  attrs.TargetPropName,attrs.TypeCode  )); 
+            } 
+        }
+
+        return PropMappings;
     }
 }

+ 1 - 1
Web/README.md

@@ -35,7 +35,7 @@
 
 #### 🚧 安装 cnpm、yarn
 
-- 复制代码(桌面 cmd 运行) `npm install -g pnpm --registry=https://registry.npm.taobao.org`
+- 复制代码(桌面 cmd 运行) `npm install -g pnpm --registry=https://registry.npmmirror.com`
 
 #### 🏭 环境支持
 

+ 2 - 1
Web/package.json

@@ -58,7 +58,8 @@
 		"vue-router": "^4.3.2",
 		"vue-signature-pad": "^3.0.2",
 		"vue3-tree-org": "^4.2.2",
-		"xlsx-js-style": "^1.2.0"
+		"xlsx-js-style": "^1.2.0",
+		"async-validator": "^4.2.5"
 	},
 	"devDependencies": {
 		"@plugin-web-update-notification/vite": "^1.7.1",

+ 82 - 0
Web/src/components/importButton/index.vue

@@ -0,0 +1,82 @@
+<template>
+	<input class="el-upload__input" ref="reffile" name="file" @change="fileChange($event)" :accept="$props.accept" type="file">
+	<el-button  icon="ele-Upload" @click="onClick">
+		<slot></slot>
+	</el-button>
+</template>
+
+<script setup lang="ts" name="ImportButton">
+
+import { reactive, ref, onMounted, watch } from 'vue';
+import {request2} from '/@/utils/request';
+import { ElMessage } from 'element-plus';
+
+// 定义父组件传过来的值
+const props = defineProps({
+	accept: {
+		type: String, 
+	},
+	param: {
+		type: Object,
+		default: () => {},
+	},
+	url: {
+		type: String, 
+	},  
+});
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['success', 'error']);
+const reffile=ref();
+// 定义变量内容 
+const state = reactive({
+	form: {} as any,
+	});
+watch(
+	() => props.param,
+	(value) => {
+		if (value) {
+			state.form = Object.assign({}, { ...value });
+		}
+	},
+	{
+		immediate: true,
+		deep: true,
+	}
+);
+
+// 上传文件
+const onClick = () => {
+	reffile.value.click();
+};
+function fileChange(event) { 
+	if(!event.currentTarget.files 
+	|| event.currentTarget.files.length==0){
+		return;
+	}
+	var file=event.currentTarget.files[0];
+	let formData = new FormData()
+  	formData.append('file', file)
+	for (const key in  state.form) {
+		const element = state.form[key];
+		formData.append(key, element)
+	}
+	request2({
+		url: props.url,
+		method: 'post',
+		data:formData,
+		headers: {
+			'Content-Type': 'multipart/form-data'
+		},
+	}).then((res)=>{
+		console.log(res);
+		ElMessage.success(res);
+		reffile.value.files=null;
+		emit('success',res)
+	}).catch((res)=>{ 
+		console.log('上传错误',res)
+		alert('上传错误')
+	});
+}
+</script>
+ 

+ 129 - 0
Web/src/components/table/TableEditor.vue

@@ -0,0 +1,129 @@
+<template>
+	<div class="el-table-container">
+		<table class="el-table el-table-middle">
+			<thead class="el-table-thead">
+				<tr>
+					<th v-for="(citem, ci) in $props.columns" :key="ci" v-show="citem.ifShow == undefined ? true : citem.ifShow" style="text-align: center">
+						{{ citem.label }}
+					</th>
+					<th style="text-align: center" v-show="!$props.disabled">操作</th>
+				</tr>
+			</thead>
+			<tbody class="el-table-tbody">
+				<tr v-for="(item, index) in vm.value" :key="index">
+					<td v-for="(citem, ci) in $props.columns" :key="ci" style="text-align: center" v-show="citem.ifShow == undefined ? true : citem.ifShow">
+						<component v-if="citem.component != 'el-select'" :is="citem.component" v-model="item[citem.field]" v-bind="renderComponentProp(citem)" :disabled="$props.disabled" />
+						<el-select v-else v-model="item[citem.field]" v-bind="renderComponentProp(citem)" :disabled="$props.disabled">
+							<el-option v-for="(sitem, si) in citem.componentProps.options" :key="sitem.value" :label="sitem.value" :value="sitem.code" />
+						</el-select>
+					</td>
+					<td style="text-align: center" v-show="!$props.disabled">
+						<el-button type="danger" @click="del(item, index)">删除</el-button>
+					</td>
+				</tr>
+			</tbody>
+			<tfoot>
+				<tr>
+					<td v-for="(citem, ci) in $props.columns" :key="ci" style="text-align: center" v-show="citem.ifShow == undefined ? true : citem.ifShow">
+						<component v-if="citem.component != 'el-select'" :is="citem.component" v-model="vm.formData[citem.field]" v-bind="renderComponentProp(citem)" />
+						<el-select v-else v-model="vm.formData[citem.field]" v-bind="renderComponentProp(citem)">
+							<el-option v-for="(sitem, si) in citem.componentProps.options" :key="sitem.value" :label="sitem.value" :value="sitem.code" />
+						</el-select>
+					</td>
+					<td class="el-table-cell el-table-cell-ellipsis" style="text-align: center" v-show="!$props.disabled">
+						<el-button type="primary" @click="add">添加</el-button>
+					</td>
+				</tr>
+			</tfoot>
+		</table>
+	</div>
+</template>
+<script lang="ts" setup>
+import { reactive } from 'vue';
+import { ElMessage, dayjs } from 'element-plus';
+import AsyncValidator from 'async-validator'; 
+import { isDate, isString } from 'lodash-es';
+const props = defineProps({
+	value: {
+		type: Array<any>,
+		default: () => [],
+	},
+	columns: {
+		type: Array<any>,
+		default: () => [],
+	},
+	rules: {
+		type: Object,
+	},
+	params: {
+		type: Object,
+	},
+	disabled: {
+		type: Boolean,
+		default: false,
+	},
+});
+const emit = defineEmits(['add', 'delete', 'update:value']);
+var vm = reactive({ formData: {} as any, value: [] as any[] });
+function renderComponentProp(item: any) {
+	let componentProps = item.componentProps || {};
+
+	let disabled = item.disabled || componentProps.disabled || props.disabled;
+	const propsData: any = {
+		...componentProps,
+		disabled: disabled,
+	};
+
+	return propsData;
+}
+function del(record: any, index: number) {
+	vm.value.splice(index, 1);
+	emit('update:value', vm.value);
+	emit('delete', { value: vm.value, record });
+}
+async function add() {
+	let msgs: string[] = [];
+
+	for (const field in props.rules) {
+		let rule = props.rules[field];
+
+		let val = vm.formData[field];
+		if(val){
+			if(isDate(val) ){
+				val=dayjs(val).format(props.columns.filter(m=>m.field==field)[0]
+				.format|| 'YYYY-MM-DD'); 
+				vm.formData[field]=val;
+			}else if (!isString(val)) {
+				val= val.toString();
+			}
+		}
+		const validator = new AsyncValidator({
+			[field]: rule,
+		});
+		await validator.validate({ [field]: val }, { firstFields: true }).catch((error: any) => {
+			var _a, _b;
+			const { errors, fields } = error;
+			if (!errors || !fields) {
+				console.error(error);
+			}
+			if (errors) {
+				msgs.push((_b = (_a = errors == null ? void 0 : errors[0]) == null ? void 0 : _a.message) != null ? _b : `${field} 必填!`);
+			}
+		});
+	}
+	if (msgs.length > 0) {
+		ElMessage.error(msgs.join('。'));
+		return;
+	}
+	for (const key in props.params) {
+		vm.formData[key] = props.params[key];
+	}
+	if (!vm.value) {
+		vm.value = [];
+	}
+	vm.value.push({ ...vm.formData });
+	vm.formData = {};
+	emit('update:value', vm.value);
+	emit('add', { value: vm.value, record: vm.formData });
+}
+</script>

+ 71 - 13
Web/src/stores/userInfo.ts

@@ -14,7 +14,8 @@ export const useUserInfo = defineStore('userInfo', {
 	state: (): UserInfosState => ({
 		userInfos: {} as any,
 		constList: [] as any,
-		dictList: [] as any,
+		dictList: {} as any,
+		dictListInt: {} as any,
 	}),
 	getters: {
 		// // 获取系统常量列表
@@ -46,13 +47,15 @@ export const useUserInfo = defineStore('userInfo', {
 		},
 		async setDictList() {
 			// 存储字典信息到浏览器缓存
-			if (Session.get('dictList')) {
-				this.dictList = Session.get('dictList');
-			} else {
-				const dictList = <any[]>await this.getAllDictList();
-				Session.set('dictList', dictList);
-				this.dictList = dictList;
-			}
+			var res = await getAPI(SysDictTypeApi).apiSysDictTypeAllDictListGet();
+			this.dictList = res.data.result;
+			// if (Session.get('dictList')) {
+			// 	this.dictList = Session.get('dictList');
+			// } else {
+			//	const dictList = <any[]>await this.getAllDictList();
+			//	Session.set('dictList', dictList);
+			//	this.dictList = dictList;
+			// }
 		},
 		// 获取当前用户信息
 		getApiUserInfo() {
@@ -115,12 +118,67 @@ export const useUserInfo = defineStore('userInfo', {
 		// 获取字典集合
 		getAllDictList() {
 			return new Promise((resolve) => {
-				getAPI(SysDictTypeApi)
-					.apiSysDictTypeAllDictListGet()
-					.then(async (res: any) => {
-						resolve(res.data.result ?? []);
-					});
+				if (this.dictList) {
+					resolve(this.dictList);
+				} else {
+					getAPI(SysDictTypeApi)
+						.apiSysDictTypeAllDictListGet()
+						.then((res: any) => {
+							resolve(res.data.result ?? []);
+						});
+				}
 			});
 		},
+
+		//根据字典类型和值取字典项
+		getDictItemByVal(typePCode: string, val: string) {
+			if(val){
+				const _val = val.toString();
+				const ds = this.getDictDatasByCode(typePCode);
+				for (let index = 0; index < ds.length; index++) {
+					const element = ds[index];
+					if (element.code == _val) {
+						return element;
+					}
+				}
+			}
+			return {};
+		},
+
+		//根据字典类型和值取描述
+		getDictLabelByVal(typePCode: string, val: string) {
+			return this.getDictItemByVal(typePCode, val).value;
+		},
+		//根据字典类型和描述取值
+		getDictValByLabel(typePCode: string, label: string) {
+			if(!label) return ''
+			const ds = this.getDictDatasByCode(typePCode);
+			for (let index = 0; index < ds.length; index++) {
+				const element = ds[index];
+				if (element.value == label) {
+					return element.code;
+				}
+			}
+		},
+		//根据字典类型字典数据
+		getDictDatasByCode(dictTypeCode: string) {
+			return this.dictList[dictTypeCode] || [];
+		},
+
+		//根据字典类型字典数据,值转为数字类型
+		getDictIntDatasByCode(dictTypeCode: string) {
+			var ds = this.dictListInt[dictTypeCode];
+			if (ds) {
+				return ds;
+			} else {
+				ds = this.dictList[dictTypeCode].map((element: { code: any }) => {
+					var d={...element};
+					d.code = element.code - 0;
+					return d;
+				});
+				this.dictListInt[dictTypeCode] = ds;
+				return ds;
+			}
+		},
 	},
 });

+ 2 - 1
Web/src/types/pinia.d.ts

@@ -14,7 +14,8 @@ declare interface UserInfos<T = any> {
 declare interface UserInfosState {
 	userInfos: UserInfos;
 	constList: T[];
-	dictList: T[];
+	dictList: T;
+	dictListInt :T;
 }
 
 // 路由缓存列表

+ 5 - 0
Web/src/utils/axios-utils.ts

@@ -26,6 +26,11 @@ export const getToken = () => {
 	return Local.get(accessTokenKey);
 };
 
+// 获取上传文件Header
+export const getHeader  = () => {
+	return { authorization: 'Bearer ' + getToken() };
+};
+
 // 清除 token
 export const clearAccessTokens = () => {
 	clearTokens();

+ 18 - 17
Web/src/utils/dict-utils.ts

@@ -1,9 +1,7 @@
-import { storeToRefs } from 'pinia';
 import { useUserInfo } from '/@/stores/userInfo';
 
 
-const stores = useUserInfo();
-const { dictList } = storeToRefs(stores);
+const stores = useUserInfo(); 
 
 // 用于在 Table 中把字段的代码转换为名称,示例如下:
 /*
@@ -16,15 +14,17 @@ import { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-util
 </el-table-column>
 */
 export function getDictDataItem(dicName:string, dicItemCode:any): any{
-    const dict = dictList.value.filter(item => item.code === dicName);
-    if (dict.length === 0)
-        return null;
-    const dictData = dict[0].children.filter(item => item.code == dicItemCode);
-    if (dictData.length === 0)
-        return null;
-    return dictData[0];
+    return stores.getDictItemByVal(dicName,dicItemCode)
+}
+export function getDictValByLabel(dicName:string, dicItemCode:any): any{
+    return stores.getDictValByLabel(dicName,dicItemCode)
+}
+
+export function getDictLabelByVal(dicName:string, dicItemCode:any): any{
+    return stores.getDictLabelByVal(dicName,dicItemCode)
 }
 
+
 // select 控件使用,用于获取字典列表,示例如下:
 /*
 import { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-utils';
@@ -33,14 +33,15 @@ import { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-util
     <el-option v-for="(item,index) in  dl('字段名名码')"  :key="index" :value="item.code" :label="`[${item.code}] ${item.value}`"></el-option>
 </el-select>
 */
-export function getDictType(dicName:string): any{
-    const dict = dictList.value.filter(item => item.code === dicName);
-    if (dict.length === 0)
-        return null;
-    return dict[0];
+export function getDictType(dicName:string): any{ 
+    
+    return stores.dictList[dicName];
 }
 
 export function getDictDataList(dicName:string): any{
-    const result = getDictType(dicName)?.children;
-    return result ?? [];
+    return stores.getDictDatasByCode(dicName);
+}
+//获取数字类型的
+export function getDictDataListInt(dicName:string): any{
+    return stores.getDictIntDatasByCode(dicName);
 }

+ 26 - 3
Web/src/utils/request.ts

@@ -1,4 +1,4 @@
-import axios, { AxiosInstance } from 'axios';
+import axios, { AxiosInstance,AxiosRequestConfig } from 'axios';
 import { ElMessage } from 'element-plus';
 import { Local, Session } from '/@/utils/storage';
 
@@ -205,6 +205,29 @@ export function decryptJWT(token: string): any {
 export function getJWTDate(timestamp: number): Date {
 	return new Date(timestamp * 1000);
 }
-
+/**
+ * Ajax请求,如果成功返回result字段,如果不成功提示错误信息
+ * @description Ajax请求
+ * @config AxiosRequestConfig 请求参数
+ * @returns 返回对象
+ */
+export function request2(config: AxiosRequestConfig<any>): any { 
+	return new Promise((resolve, reject) => {
+		service(config)
+			.then((res) => {
+				if (res.data.type == 'success') {
+					resolve(res.data.result);
+				} else {
+					console.log('res', res);
+					ElMessage.success(res.data.message);
+				}
+			})
+			.catch((res) => {
+				console.log('res', res);
+				ElMessage.error(res);
+				reject(res);
+			});
+	});
+}
 // 导出 axios 实例
-export default service;
+export default service;

+ 4 - 4
Web/src/views/system/dict/index.vue

@@ -330,10 +330,10 @@ const handleDictDataCurrentChange = (val: number) => {
 
 // 更新前端字典缓存
 const updateDictSession = async () => {
-	if (Session.get('dictList')) {
-		const dictList = await useUserInfo().getAllDictList();
-		Session.set('dictList', dictList);
-	}
+	// if (Session.get('dictList')) {
+	// 	const dictList = await useUserInfo().getAllDictList();
+	// 	Session.set('dictList', dictList);
+	// }
 	await useUserInfo().setDictList();
 };
 </script>