目录
一、需求分析
一个带有附件的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赋值有点麻烦,这也算是一个待优化的点吧。
说明
本人专注机床的数据采集和程序传输,致力于机械加工行业的数字化系统开发
以下是自己开发的所支持的机床数据采集源代码类库(部分),