您现在的位置是:首页 > 正文

WTM+LayUI 子表批量上传文件

2024-04-01 01:50:56阅读 1

目录

一、需求分析

二、解决思路

三、代码实现

步骤一、后台封装Upload组件

步骤二、通过Setformat方法将组件内容传递到前端

步骤三、批量新增VM

步骤四、设置单元格为Upload组件

步骤五、后台接受方法

说明


一、需求分析

         一个带有附件的Model,实现批量新增。这就需要在DetailList中添加上传按钮,但由于DetailList不支持附件的上传,故需要通过SetFormat方法来自定义实现。

二、解决思路

         将上传控件封装,并通过Setformat方法回传到前端的DetailList中。当上传完文件之后,将文件名赋值给当前列,将FileAttachmentId赋值给另外的列当后台接受到FileAttachmentId后,就像操作普通的字段一样操作即可

三、代码实现

步骤一、后台封装Upload组件

        /// <summary>
        /// 表格里面Upload组件
        /// </summary>
        /// <param name="name">名称,名称必须要要和接受的VM里面的实体一致</param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string MakeUpload(string name,string value=null)
        {
            var id = (name == null ? "" : Utils.GetIdByName(name));
            StringBuilder js=new StringBuilder();
            js.Append("<div style='display:flex'>");//将输入框和选择按钮放到同一行
            js.Append($@"<input class='layui-input'  style='height:28px;'  name='{name ?? ""}' id='{id}' value='{value ?? ""}'   onclick='SetGridCellUpload(""{id}"")'  />  ");// disabled='' class='layui-disabled'
            js.Append($@"<button id='{id}button' name='{name}button' class=""layui-btn layui-btn-sm"" type=""button"" lay-filter='{id}buttonfilter' style=""display: none;"" >选择</button><input class=""layui-upload-file"" type='file' accept='' name=""file"" > ");
            js.Append("</div>");
            return js.ToString();
        }

说明: 

1、此代码参考系统生成的前端上传控件,略有删除。

2、在外面封装一层Div是为了让input框和选择按钮在同一行,否则选择按钮将在第二行,从而不会显示。

3、将选择button设置为不显示,原因是防止一开始上传组件还没有layui.use初始化的时候,点击此按钮。由于没有初始化话为上传控件,此时是无法通过点击按钮上传文件的。

步骤二、通过Setformat方法将组件内容传递到前端

protected override IEnumerable<IGridColumn<CNCFileCreateDetailList_View>> InitGridHeader()
        {
            int fileIndex = 0;
            return new List<GridColumn<CNCFileCreateDetailList_View>>{
                this.MakeGridHeader(x => x.CNCFileCreateDetailList_Name).SetTitle(@Localizer["_Model._CNCFileManage._Name"].Value)
                .SetEditType(EditTypeEnum.TextBox),
                this.MakeGridHeader(x => x.CNCFileCreateDetailList_NCFileType).SetTitle(@Localizer["_Model._CNCFileManage._NCFileType"].Value)
                .SetEditType(EditTypeEnum.ComboBox,listitems:typeof(NCFileTypeEnum).ToListItems()),//ToListItems()方法将枚举转化成下拉列表
                this.MakeGridHeader(x => x.CNCFileCreateDetailList_FileId).SetTitle("CNCFileCreateDetailList_FileId").SetEditType(editType:EditTypeEnum.TextBox,readOnly:true).SetHide(),
                this.MakeGridHeader(x => x.CNCFileCreateDetailList_File).SetTitle(Localizer["_Model._CNCFileManage._NCFile"].Value)
                .SetFormat((e, f) =>
                {
                    string name = $"DetailList.EntityList[{fileIndex}].CNCFileCreateDetailList_File";//这个名称必须提交接受的实体一致
                      if(e.ID!=Guid.Empty)
                    {
                        fileIndex++;
                    }
                    string js=WTMHelper.MakeUpload(name);
                    return js;
                }),
                //this.MakeGridHeader(x => x.CNCFileCreateDetailList_IsNeedCheck).SetTitle(Localizer["_Model._CNCFileManage._IsNeedCheck"].Value).SetEditType(editType:EditTypeEnum.CheckBox),
                this.MakeGridHeader(x => x.CNCFileCreateDetailList_Remark).SetTitle(Localizer["_Model._CNCFileManage._Remark"].Value).SetEditType(editType:EditTypeEnum.TextBox),
                this.MakeGridHeaderAction(width: 200)
            };
        }

    public class CNCFileCreateDetailList_View : BasePoco
    {
        public string CNCFileCreateDetailList_Name { get; set; }
        public NCFileTypeEnum? CNCFileCreateDetailList_NCFileType { get; set; }
        /// <summary>
        /// 此字段是用来放FileName
        /// </summary>
        public string CNCFileCreateDetailList_File { get; set; }
        public string CNCFileCreateDetailList_FileId { get; set; }
        //public bool CNCFileCreateDetailList_IsNeedCheck { get; set; }
        public string CNCFileCreateDetailList_Remark { get; set; }

    }

 说明: 

1、上传控件的Name必须和提交接受的VM实体一致,否则将接受不到所需的数据。本例中DetailList.EntityList来接受

2、CNCFileCreateDetailList_FileId此列为存放FileAttachmentId的列,此列的命名必须是上传控件列的列名加Id,后面给此列赋值的时候会用到。

步骤三、批量新增VM

  /// <summary>
    /// 批量新增
    /// </summary>
    public  class CNCFileCreateBatchCreateVM : BaseVM
    {
        public Guid? ProductId { get; set; }
        [Required]
        [Display(Name ="_Model._CNCFileManage._WorkProcess")]
        public Guid? WorkProcessId { get; set; }
        public bool IsNeedCheck { get; set; }
        /// <summary>
        /// 新增的DetailList
        /// </summary>
        public CNCFileCreateDetailListVM DetailList { get; set; }
        

        public CNCFileCreateBatchCreateVM()
        {

            DetailList=new CNCFileCreateDetailListVM ();
            DetailList.DetailGridPrix = "DetailList.EntityList";//必须和前台传过来的名字一样

        }


        protected override void InitVM()
        {
          

        }
        public override void Validate()
        {
            //验证程序名称不能为空
            if (DetailList.EntityList.Any(x => string.IsNullOrEmpty(x.CNCFileCreateDetailList_Name)))
            {
                MSD.AddModelError("CNCFileCreateBatchCreate_Exist_Name_IsNull", Localizer["CNCFileCreateBatchCreate_Exist_Name_IsNull"].Value);
                return;
            }
            //验证程序文件不能为空
            if (DetailList.EntityList.Any(x => string.IsNullOrEmpty(x.CNCFileCreateDetailList_FileId)))
            {
                MSD.AddModelError("CNCFileCreateBatchCreate_Exist_FileId_IsNull", Localizer["CNCFileCreateBatchCreate_Exist_FileId_IsNull"].Value);
                return;
            }
            //验证Name和FileType的唯一性   按 x.CNCFileCreateDetailList_Name, x.CNCFileCreateDetailList_NCFileType分组后 是否组里面项大于1的
            if (DetailList.EntityList.GroupBy(x=>new { x.CNCFileCreateDetailList_Name, x.CNCFileCreateDetailList_NCFileType }).Any(g=>g.Count()>1))
            {
                MSD.AddModelError("CNCFileCreateBatchCreate_Exist_NameType_Unique", Localizer["CNCFileCreateBatchCreate_Exist_NameType_Unique"].Value);
                return;
            }

            base.Validate();
        }

       
    }

步骤四、设置单元格为Upload组件

//设置单元格为Upload
function SetGridCellUpload(id) {

    //将input标签设置为disabled ,如果不将input标签disable就可以再次点击,又会调用到SetGridCellUpload方法
    $(`#${id}`).prop('disabled', true);
    $(`#${id}button`).show();//显示选择按钮,如果需要更改文件可以通过此按钮点击

    layui.use(['upload'], function () {
        var uploadInst = layui.upload.render({
            elem: `#${id}button`
            , url: '/_Framework/Upload?1=1&_DONOT_USE_CS=&'
            , size: 0
            , accept: 'file'
            , xhr: xhrOnProgress
            , progress: function (value) {
                $('.layui-progress .layui-progress-bar').css(
                    'width',
                    value + '%'
                );
            }
            , before: function (obj) {
                index = layui.layer.load(2);

            }
            , done: function (res) {
                layui.layer.close(index);
                debugger;
                if (res.Data.Id == '') {
                    //$(`#${id}label`).html('');
                    layui.layer.msg('上传失败');
                }
                else {
                   /* $(`#${id}label`).html('');*/
                    document.getElementById(id).value = res.Data.Name;//将接口返回的文件名称赋值给input标签
                    document.getElementById(id).onchange();//调用此方法将标签的值赋值给Table 这个要一定要调用,否则新增之后将把之前的内容清空
                    document.getElementById(id + "Id").value = res.Data.Id;//将接口返回的文件FileId赋值给 Id的标签  
                    document.getElementById(id + "Id").onchange();

                }
            }
            , error: function () {

                layui.layer.close(index);
            }
        });
    })

    //按上传按钮
    $(`#${id}button`).click();
}

说明 :

1、当点击单元格的时候,调用前端的此方法。首先将input标签设置为disabled ,如果不将input标签disable就可以再次点击,又会调用到SetGridCellUpload方法,重新初始化layui上传标签。

2、将上传按钮显示出来,通过点击此按钮可以更改上传文件。

3、将值赋值给控件之后,一定要调用onchange方法,此方法会将标签的值赋值给Table。

步骤五、后台接受方法

  [HttpPost]
        [ActionDescription("Sys.DoBatchCreate")]
        public async Task<ActionResult> DoBatchCreate(CNCFileCreateBatchCreateVM vm)
        {

            if (!ModelState.IsValid)
            {
                return FFResult().Alert(Wtm.MSD.GetFirstError());
                //return PartialView(vm.FromView, vm);//由于刷新赋值不好弄就直接返回提示,且不关闭Dialog
            }
            else
            {
                foreach (var entity in vm.DetailList.EntityList)
                {
                   var subvm= Wtm.CreateVM<CNCFileCreateVM>();
                    subvm.CopyContext(vm);
                    subvm.Entity.Name = entity.CNCFileCreateDetailList_Name;
                    subvm.Entity.IsNeedCheck = vm.IsNeedCheck;
                    subvm.Entity.NCFileId =string.IsNullOrEmpty(entity.CNCFileCreateDetailList_FileId)?null:new Guid( entity.CNCFileCreateDetailList_FileId);
                    subvm.Entity.NCFileType = entity.CNCFileCreateDetailList_NCFileType;
                    subvm.Entity.WorkProcessId = vm.WorkProcessId;
                    subvm.Entity.CheckStatus = NCFileCheckStatusEnum.ToCheck;
                    subvm.Entity.Remark=entity.CNCFileCreateDetailList_Remark;
                    subvm.Validate();
                    if (!ModelState.IsValid)
                    {
                        return PartialView(vm.FromView, vm);
                    }
                    await subvm.DoAddAsync();
                }
                if (!ModelState.IsValid)
                {
                    return FFResult().Alert(Wtm.MSD.GetFirstError());
                    //vm.DoReInit();
                    //return PartialView("../CNCFileCreate/BatchCreate", vm);
                }
                else
                {

                    return FFResult().CloseDialog().RefreshGrid();
                }
            }
        }

说明:

 1、这里面有个偷懒的做法,就是当验证失败后,没有将vm返回给前端,而是返回了Alert的提示框。原因是验证失败返回VM的话,DetailList赋值有点麻烦,这也算是一个待优化的点吧。

说明

本人专注机床的数据采集和程序传输,致力于机械加工行业的数字化系统开发

以下是自己开发的所支持的机床数据采集源代码类库(部分),

网站文章

  • 使用PyQt5创建和编写.qrc资源文件

    在PyQt5中,可以使用.qrc资源文件来管理应用程序中的来管理应用程序中的静态资源,如图像、样式表和其他文件。一旦创建和编写.qrc文件,我们需要将其编译为Python代码,以便在应用程序来管理应用...

    2024-04-01 01:50:50
  • 全面盘点‘’一网打尽‘’,架构师的必备技能(微服务、高并发、大数据、缓存等中间件)是如何炼成的?

    全面盘点‘’一网打尽‘’,架构师的必备技能(微服务、高并发、大数据、缓存等中间件)是如何炼成的?

    现代的互联网体系结构面临着异常庞杂的服务拓扑,如何合理地进行服务治理是架构师领域核心的一个命题。业务领域、基础架构领域、组织结构领域,如何做服务治理?服务治理是如何一步步演变进化的?我们未来又将面临哪...

    2024-04-01 01:50:42
  • 用单例封装 SharedPreferences

    自己封装的 SharedPreferences,很简单的封装,直接上代码public class SPManager { private static final String ACCOUNT = "account"; private static final String PASSWORD = "password"; private st

    2024-04-01 01:50:17
  • pta 乙级 1012 数字分类 (20 分)

    pta 乙级 1012 数字分类 (20 分)

    给定一系列正整数,请按要求对数字进行分类,并输出以下 5 个数字:A1​= 能被 5 整除的数字中所有偶数的和; A2​= 将被 5 除后余 1 的数字按给出顺序进行交错求和,即计算n1​−n2​+n...

    2024-04-01 01:50:11
  • C++中struct与class的区别

    从语法上,在C++中(只讨论C++中)。class和struct做类型定义时只有两点区别: (一)默认继承权限。如果不明确指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理; (二)成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。 除了这两点,class和struct基本就是一个东西。语法上没有任何

    2024-04-01 01:50:03
  • OSPF综合实验

    OSPF综合实验

    要求:配置area0r3r4r5r6r7配置MGREr3r5r6r7配置环回area1r1r2r3area2r6r11r12area3r7r8r9area4r9r10rip开启ospfr1r2r3r4r5r6r7r8r9r10r11r12

    2024-04-01 01:49:37
  • 微服务中的分布式事务管理 - 2/2 Saga异步模式

    微服务中的分布式事务管理 - 2/2 Saga异步模式

    在这篇文章中,我们看到了什么是微服务中事务管理的异步模式,还探索了Saga模式及其两个变体,即基于Choreography和基于Orchestrator的模式。我们深入了解了这两种模式,然后讨论了它们...

    2024-04-01 01:49:29
  • 长篇阅读做题技巧

    长篇阅读做题技巧

    六级长篇阅读解题办法

    2024-04-01 01:49:24
  • com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `co

    今天在开发的过程中发现偶然发现报了一个这个错,于是经过一番研究,终于发现原因:原来是在用springboot的过程中,没有构造方法导致的。解决方法: 直接加上@NoArgsConstructor 注解 即可解决。(前提需要引入lombak插件)觉得有用的麻烦请采纳一下,谢谢。...

    2024-04-01 01:48:58
  • 【初阶C++】细谈new和delete以及函数与类的模板

    【初阶C++】细谈new和delete以及函数与类的模板

    new和delete中的细节以及初识模板

    2024-04-01 01:48:51