mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-02 12:09:59 +08:00
!2448 test(#I4UMS2): add CardUpload unit test
* test: 增加 File 检查单元测试 * refactor: 删除冗余代码 ValidateFile 导致触发两次客户端验证 * doc: 格式化代码 * fix: 修复单文件样式 * fix: 修复验证两次问题 * test: 增加 ValidateForm 单元测试 * refactor: 重构代码 * doc: 格式化文档 * test: 增加 Value 单元测试 * refactor: 更新代码逻辑 * refactor: 更新代码逻辑 * refactor: 移除 OnBrowser * chore: 格式化代码 * test: 增加 Error 属性单元测试 * test: 增加 Accept 单元测试 * test: 增加 Reset 单元测试 * refactor: 格式化代码 * refactor: 精简代码 * test: 增加 CardUpload 在表单内的单元测试 * test: 增加 CardUpload 单元测试 * refactor: 重构 IsImage 方法修复 bug
This commit is contained in:
parent
b4c6e79fbf
commit
0c5fc4c1a7
@ -37,18 +37,18 @@ public sealed partial class Uploads : IDisposable
|
||||
private BlockLogger? Trace { get; set; }
|
||||
|
||||
private List<UploadFile> DefaultFormatFileList { get; } = new List<UploadFile>()
|
||||
{
|
||||
new UploadFile { FileName = "Test.xls" },
|
||||
new UploadFile { FileName = "Test.doc" },
|
||||
new UploadFile { FileName = "Test.ppt" },
|
||||
new UploadFile { FileName = "Test.mp3" },
|
||||
new UploadFile { FileName = "Test.mp4" },
|
||||
new UploadFile { FileName = "Test.pdf" },
|
||||
new UploadFile { FileName = "Test.cs" },
|
||||
new UploadFile { FileName = "Test.zip" },
|
||||
new UploadFile { FileName = "Test.txt" },
|
||||
new UploadFile { FileName = "Test.dat" }
|
||||
};
|
||||
{
|
||||
new UploadFile { FileName = "Test.xls" },
|
||||
new UploadFile { FileName = "Test.doc" },
|
||||
new UploadFile { FileName = "Test.ppt" },
|
||||
new UploadFile { FileName = "Test.mp3" },
|
||||
new UploadFile { FileName = "Test.mp4" },
|
||||
new UploadFile { FileName = "Test.pdf" },
|
||||
new UploadFile { FileName = "Test.cs" },
|
||||
new UploadFile { FileName = "Test.zip" },
|
||||
new UploadFile { FileName = "Test.txt" },
|
||||
new UploadFile { FileName = "Test.dat" }
|
||||
};
|
||||
|
||||
private Task OnFileChange(UploadFile file)
|
||||
{
|
||||
@ -216,245 +216,245 @@ public sealed partial class Uploads : IDisposable
|
||||
/// <returns></returns>
|
||||
private IEnumerable<AttributeItem> GetInputAttributes() => new AttributeItem[]
|
||||
{
|
||||
new AttributeItem() {
|
||||
Name = "ShowDeleteButton",
|
||||
Description = Localizer["ShowDeleteButton"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsDisabled",
|
||||
Description = Localizer["IsDisabled"],
|
||||
Type = "boolean",
|
||||
ValueList = "true / false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "PlaceHolder",
|
||||
Description = Localizer["PlaceHolder"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Accept",
|
||||
Description = Localizer["Accept"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonClass",
|
||||
Description = Localizer["BrowserButtonClass"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "btn-primary"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonIcon",
|
||||
Description = Localizer["BrowserButtonIcon"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "fa fa-folder-open-o"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonText",
|
||||
Description = Localizer["BrowserButtonText"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = ""
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DeleteButtonClass",
|
||||
Description = Localizer["DeleteButtonClass"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "btn-danger"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DeleteButtonIcon",
|
||||
Description = Localizer["DeleteButtonIcon"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "fa fa-trash-o"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DeleteButtonText",
|
||||
Description = Localizer["DeleteButtonText"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = Localizer["DeleteButtonTextDefaultValue"]
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnDelete",
|
||||
Description = Localizer["OnDelete"],
|
||||
Type = "Func<string, Task<bool>>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnChange",
|
||||
Description = Localizer["OnChange"],
|
||||
Type = "Func<UploadFile, Task>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "ShowDeleteButton",
|
||||
Description = Localizer["ShowDeleteButton"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsDisabled",
|
||||
Description = Localizer["IsDisabled"],
|
||||
Type = "boolean",
|
||||
ValueList = "true / false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "PlaceHolder",
|
||||
Description = Localizer["PlaceHolder"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Accept",
|
||||
Description = Localizer["Accept"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonClass",
|
||||
Description = Localizer["BrowserButtonClass"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "btn-primary"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonIcon",
|
||||
Description = Localizer["BrowserButtonIcon"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "fa fa-folder-open-o"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonText",
|
||||
Description = Localizer["BrowserButtonText"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = ""
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DeleteButtonClass",
|
||||
Description = Localizer["DeleteButtonClass"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "btn-danger"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DeleteButtonIcon",
|
||||
Description = Localizer["DeleteButtonIcon"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "fa fa-trash-o"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DeleteButtonText",
|
||||
Description = Localizer["DeleteButtonText"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = Localizer["DeleteButtonTextDefaultValue"]
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnDelete",
|
||||
Description = Localizer["OnDelete"],
|
||||
Type = "Func<string, Task<bool>>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnChange",
|
||||
Description = Localizer["OnChange"],
|
||||
Type = "Func<UploadFile, Task>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
}
|
||||
};
|
||||
|
||||
private IEnumerable<AttributeItem> GetButtonAttributes() => new AttributeItem[]
|
||||
{
|
||||
new AttributeItem() {
|
||||
Name = "IsDirectory",
|
||||
Description = Localizer["IsDirectory"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsMultiple",
|
||||
Description = Localizer["IsMultiple"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsSingle",
|
||||
Description = Localizer["IsSingle"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "ShowProgress",
|
||||
Description = Localizer["ShowProgress"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Accept",
|
||||
Description = Localizer["Accept"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonClass",
|
||||
Description = Localizer["BrowserButtonClass"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "btn-primary"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonIcon",
|
||||
Description = Localizer["BrowserButtonIcon"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "fa fa-folder-open-o"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonText",
|
||||
Description = Localizer["BrowserButtonText"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = ""
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DefaultFileList",
|
||||
Description = Localizer["DefaultFileList"],
|
||||
Type = "List<UploadFile>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnGetFileFormat",
|
||||
Description = Localizer["OnGetFileFormat"],
|
||||
Type = "Func<string, string>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnDelete",
|
||||
Description = Localizer["OnDelete"],
|
||||
Type = "Func<string, Task<bool>>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnChange",
|
||||
Description = Localizer["OnChange"],
|
||||
Type = "Func<UploadFile, Task>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " - "
|
||||
}
|
||||
new AttributeItem() {
|
||||
Name = "IsDirectory",
|
||||
Description = Localizer["IsDirectory"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsMultiple",
|
||||
Description = Localizer["IsMultiple"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsSingle",
|
||||
Description = Localizer["IsSingle"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "ShowProgress",
|
||||
Description = Localizer["ShowProgress"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Accept",
|
||||
Description = Localizer["Accept"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonClass",
|
||||
Description = Localizer["BrowserButtonClass"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "btn-primary"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonIcon",
|
||||
Description = Localizer["BrowserButtonIcon"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "fa fa-folder-open-o"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "BrowserButtonText",
|
||||
Description = Localizer["BrowserButtonText"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = ""
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DefaultFileList",
|
||||
Description = Localizer["DefaultFileList"],
|
||||
Type = "List<UploadFile>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnGetFileFormat",
|
||||
Description = Localizer["OnGetFileFormat"],
|
||||
Type = "Func<string, string>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnDelete",
|
||||
Description = Localizer["OnDelete"],
|
||||
Type = "Func<string, Task<bool>>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnChange",
|
||||
Description = Localizer["OnChange"],
|
||||
Type = "Func<UploadFile, Task>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " - "
|
||||
}
|
||||
};
|
||||
|
||||
private IEnumerable<AttributeItem> GetAvatarAttributes() => new AttributeItem[]
|
||||
{
|
||||
new AttributeItem() {
|
||||
Name = "Width",
|
||||
Description = Localizer["Width"],
|
||||
Type = "int",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "0"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Height",
|
||||
Description = Localizer["Height"],
|
||||
Type = "int",
|
||||
ValueList = "—",
|
||||
DefaultValue = "0"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsCircle",
|
||||
Description = Localizer["IsCircle"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsSingle",
|
||||
Description = Localizer["IsSingle"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "ShowProgress",
|
||||
Description = Localizer["ShowProgress"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Accept",
|
||||
Description = Localizer["Accept"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DefaultFileList",
|
||||
Description = Localizer["DefaultFileList"],
|
||||
Type = "List<UploadFile>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnDelete",
|
||||
Description = Localizer["OnDelete"],
|
||||
Type = "Func<string, Task<bool>>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnChange",
|
||||
Description = Localizer["OnChange"],
|
||||
Type = "Func<UploadFile, Task>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Width",
|
||||
Description = Localizer["Width"],
|
||||
Type = "int",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "0"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Height",
|
||||
Description = Localizer["Height"],
|
||||
Type = "int",
|
||||
ValueList = "—",
|
||||
DefaultValue = "0"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsCircle",
|
||||
Description = Localizer["IsCircle"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "IsSingle",
|
||||
Description = Localizer["IsSingle"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "ShowProgress",
|
||||
Description = Localizer["ShowProgress"],
|
||||
Type = "bool",
|
||||
ValueList = "true|false",
|
||||
DefaultValue = "false"
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "Accept",
|
||||
Description = Localizer["Accept"],
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "DefaultFileList",
|
||||
Description = Localizer["DefaultFileList"],
|
||||
Type = "List<UploadFile>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnDelete",
|
||||
Description = Localizer["OnDelete"],
|
||||
Type = "Func<string, Task<bool>>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem() {
|
||||
Name = "OnChange",
|
||||
Description = Localizer["OnChange"],
|
||||
Type = "Func<UploadFile, Task>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -30,6 +30,7 @@ public partial class AvatarUpload<TValue>
|
||||
/// </summary>
|
||||
protected override string? ItemClassString => CssBuilder.Default(base.ItemClassString)
|
||||
.AddClass("is-circle", IsCircle)
|
||||
.AddClass("is-single", IsSingle)
|
||||
.AddClass("disabled", IsDisabled)
|
||||
.Build();
|
||||
|
||||
@ -67,8 +68,6 @@ public partial class AvatarUpload<TValue>
|
||||
/// <returns></returns>
|
||||
protected override async Task OnFileChange(InputFileChangeEventArgs args)
|
||||
{
|
||||
await base.OnFileChange(args);
|
||||
|
||||
CurrentFile = new UploadFile()
|
||||
{
|
||||
OriginFileName = args.File.Name,
|
||||
@ -86,7 +85,8 @@ public partial class AvatarUpload<TValue>
|
||||
}
|
||||
|
||||
UploadFiles.Add(CurrentFile);
|
||||
ValidateFile();
|
||||
|
||||
await base.OnFileChange(args);
|
||||
|
||||
// ValidateFile 后 IsValid 才有值
|
||||
CurrentFile.IsValid = IsValid;
|
||||
|
@ -137,9 +137,9 @@ public abstract class ButtonUploadBase<TValue> : SingleUploadBase<TValue>
|
||||
protected override IDictionary<string, object> GetUploadAdditionalAttributes()
|
||||
{
|
||||
var ret = new Dictionary<string, object>
|
||||
{
|
||||
{ "hidden", "hidden" }
|
||||
};
|
||||
{
|
||||
{ "hidden", "hidden" }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(Accept))
|
||||
{
|
||||
|
@ -23,12 +23,25 @@ public partial class CardUpload<TValue>
|
||||
.AddClass("disabled", IsDisabled)
|
||||
.Build();
|
||||
|
||||
private static bool IsImage(UploadFile item) => item.File?.ContentType.Contains("image", StringComparison.OrdinalIgnoreCase)
|
||||
?? Path.GetExtension(item.OriginFileName ?? item.FileName ?? item.PrevUrl)?.ToLowerInvariant() switch
|
||||
{
|
||||
".jpg" or ".jpeg" or ".png" or ".bmp" or ".gif" => true,
|
||||
_ => false
|
||||
};
|
||||
private static bool IsImage(UploadFile item)
|
||||
{
|
||||
bool ret;
|
||||
if (item.File != null)
|
||||
{
|
||||
ret = item.File.ContentType.Contains("image", StringComparison.OrdinalIgnoreCase) || CheckExtensions(item.File.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = CheckExtensions(item.FileName ?? item.PrevUrl ?? "");
|
||||
}
|
||||
|
||||
bool CheckExtensions(string fileName) => Path.GetExtension(fileName).ToLowerInvariant() switch
|
||||
{
|
||||
".jpg" or ".jpeg" or ".png" or ".bmp" or ".gif" => true,
|
||||
_ => false
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 点击 Zoom 图标回调方法
|
||||
|
@ -13,7 +13,7 @@
|
||||
{
|
||||
<Button class="@RemoveButtonClassString" IsDisabled="@IsDeleteButtonDisabled" Icon="@DeleteButtonIcon" Text="@DeleteButtonText" OnClick="@OnDeleteFile" />
|
||||
}
|
||||
<Button class="@BrowserButtonClassString" IsDisabled="@IsDisabled" Icon="@BrowserButtonIcon" Text="@BrowserButtonText" OnClick="@OnFileBrowser" />
|
||||
<Button class="@BrowserButtonClassString" IsDisabled="@IsDisabled" Icon="@BrowserButtonIcon" Text="@BrowserButtonText" />
|
||||
</div>
|
||||
<InputFile AdditionalAttributes="@GetUploadAdditionalAttributes()" OnChange="OnFileChange" />
|
||||
</div>
|
||||
|
@ -113,8 +113,6 @@ public partial class InputUpload<TValue>
|
||||
|
||||
await base.OnFileChange(args);
|
||||
|
||||
ValidateFile();
|
||||
|
||||
if (OnChange != null)
|
||||
{
|
||||
await OnChange(CurrentFile);
|
||||
|
@ -48,14 +48,17 @@ public abstract class MultipleUploadBase<TValue> : UploadBase<TValue>
|
||||
protected override async Task<bool> OnFileDelete(UploadFile item)
|
||||
{
|
||||
var ret = await base.OnFileDelete(item);
|
||||
if (ret && item != null)
|
||||
if (ret)
|
||||
{
|
||||
UploadFiles.Remove(item);
|
||||
if (!string.IsNullOrEmpty(item.ValidateId))
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync(null, "bb_tooltip", item.ValidateId, "dispose");
|
||||
}
|
||||
DefaultFileList?.Remove(item);
|
||||
if (DefaultFileList != null)
|
||||
{
|
||||
DefaultFileList.Remove(item);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -67,12 +70,6 @@ public abstract class MultipleUploadBase<TValue> : UploadBase<TValue>
|
||||
/// <returns></returns>
|
||||
protected bool GetShowProgress(UploadFile item) => ShowProgress && !item.Uploaded;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual List<UploadFile> GetUploadFiles() => DefaultFileList == null ? UploadFiles : DefaultFileList.Concat(UploadFiles).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// 清空上传列表方法
|
||||
/// </summary>
|
||||
|
@ -27,7 +27,7 @@ public abstract class SingleUploadBase<TValue> : MultipleUploadBase<TValue>
|
||||
/// 获得当前图片集合
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override List<UploadFile> GetUploadFiles()
|
||||
protected virtual List<UploadFile> GetUploadFiles()
|
||||
{
|
||||
var ret = new List<UploadFile>();
|
||||
if (IsSingle)
|
||||
@ -60,7 +60,7 @@ public abstract class SingleUploadBase<TValue> : MultipleUploadBase<TValue>
|
||||
protected override async Task<bool> OnFileDelete(UploadFile item)
|
||||
{
|
||||
var ret = await base.OnFileDelete(item);
|
||||
if (ret && item != null)
|
||||
if (ret)
|
||||
{
|
||||
if (IsSingle)
|
||||
{
|
||||
@ -74,13 +74,21 @@ public abstract class SingleUploadBase<TValue> : MultipleUploadBase<TValue>
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync(null, "bb_tooltip", item.ValidateId, "dispose");
|
||||
}
|
||||
if (IsSingle)
|
||||
RemoveItem();
|
||||
}
|
||||
|
||||
void RemoveItem()
|
||||
{
|
||||
if (DefaultFileList != null)
|
||||
{
|
||||
DefaultFileList?.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultFileList?.Remove(item);
|
||||
if (IsSingle)
|
||||
{
|
||||
DefaultFileList.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultFileList.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
@ -74,18 +74,7 @@ public abstract class UploadBase<TValue> : ValidateBase<TValue>, IUpload
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
protected string? GetFileName(UploadFile? item = null) => item?.OriginFileName ?? item?.FileName ?? Value?.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 触发客户端验证方法
|
||||
/// </summary>
|
||||
protected void ValidateFile()
|
||||
{
|
||||
if (ValidateForm != null && EditContext != null && FieldIdentifier.HasValue)
|
||||
{
|
||||
EditContext.NotifyFieldChanged(FieldIdentifier.Value);
|
||||
}
|
||||
}
|
||||
protected string? GetFileName(UploadFile? item) => item?.OriginFileName ?? item?.FileName;
|
||||
|
||||
/// <summary>
|
||||
/// 显示/隐藏验证结果方法
|
||||
@ -97,14 +86,17 @@ public abstract class UploadBase<TValue> : ValidateBase<TValue>, IUpload
|
||||
if (FieldIdentifier != null)
|
||||
{
|
||||
var messages = results.Where(item => item.MemberNames.Any(m => UploadFiles.Any(f => f.ValidateId?.Equals(m, StringComparison.OrdinalIgnoreCase) ?? false)));
|
||||
if (messages.Any() && CurrentFile != null)
|
||||
if (messages.Any())
|
||||
{
|
||||
ErrorMessage = messages.FirstOrDefault(m => m.MemberNames.Any(f => f.Equals(CurrentFile.ValidateId, StringComparison.OrdinalIgnoreCase)))?.ErrorMessage;
|
||||
IsValid = string.IsNullOrEmpty(ErrorMessage);
|
||||
|
||||
if (IsValid.HasValue && !IsValid.Value)
|
||||
IsValid = false;
|
||||
if (CurrentFile != null)
|
||||
{
|
||||
TooltipMethod = validProperty ? "show" : "enable";
|
||||
var msg = messages.FirstOrDefault(m => m.MemberNames.Any(f => f.Equals(CurrentFile.ValidateId, StringComparison.OrdinalIgnoreCase)));
|
||||
if (msg != null)
|
||||
{
|
||||
ErrorMessage = msg.ErrorMessage;
|
||||
TooltipMethod = validProperty ? "show" : "enable";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -147,17 +139,11 @@ public abstract class UploadBase<TValue> : ValidateBase<TValue>, IUpload
|
||||
}
|
||||
if (type.IsAssignableTo(typeof(List<IBrowserFile>)))
|
||||
{
|
||||
CurrentValue = (TValue)(object)UploadFiles;
|
||||
CurrentValue = (TValue)(object)UploadFiles.Select(f => f.File).ToList();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual Task OnFileBrowser() => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@ -165,14 +151,13 @@ public abstract class UploadBase<TValue> : ValidateBase<TValue>, IUpload
|
||||
protected virtual IDictionary<string, object> GetUploadAdditionalAttributes()
|
||||
{
|
||||
var ret = new Dictionary<string, object>
|
||||
{
|
||||
{ "hidden", "hidden" }
|
||||
};
|
||||
{
|
||||
{ "hidden", "hidden" }
|
||||
};
|
||||
if (!string.IsNullOrEmpty(Accept))
|
||||
{
|
||||
ret.Add("accept", Accept);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.upload .upload-body.is-avatar .upload-item.is-single {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.upload .upload-body.is-avatar .upload-item.is-invalid {
|
||||
border-color: #dc3545;
|
||||
border-style: solid;
|
||||
|
@ -79,7 +79,7 @@ public class UploadFile
|
||||
/// <param name="maxHeight"></param>
|
||||
/// <param name="maxAllowedSize"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public async Task RequestBase64ImageFileAsync(string format, int maxWidth, int maxHeight, long maxAllowedSize = 512000, CancellationToken token = default)
|
||||
{
|
||||
if (File != null)
|
||||
@ -109,6 +109,7 @@ public class UploadFile
|
||||
/// <param name="maxAllowedSize"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public async Task<bool> SaveToFile(string fileName, long maxAllowedSize = 512000, CancellationToken token = default)
|
||||
{
|
||||
var ret = false;
|
||||
|
File diff suppressed because one or more lines are too long
@ -4,6 +4,7 @@
|
||||
|
||||
using BootstrapBlazor.Shared;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace UnitTest.Components;
|
||||
|
||||
@ -112,6 +113,31 @@ public class UploadTest : BootstrapBlazorTestBase
|
||||
Assert.False(invalid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InputUpload_FileValidate_OK()
|
||||
{
|
||||
var foo = new Person();
|
||||
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Model, foo);
|
||||
pb.AddChildContent<InputUpload<IBrowserFile>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, foo.Picture);
|
||||
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, nameof(Person.Picture), typeof(IBrowserFile)));
|
||||
});
|
||||
});
|
||||
|
||||
var input = cut.FindComponent<InputFile>();
|
||||
cut.InvokeAsync(() => input.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs(new List<MockBrowserFile>()
|
||||
{
|
||||
new MockBrowserFile()
|
||||
})));
|
||||
|
||||
// 提交表单
|
||||
var form = cut.Find("form");
|
||||
cut.InvokeAsync(() => form.Submit());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AvatarUpload_Ok()
|
||||
{
|
||||
@ -179,6 +205,30 @@ public class UploadTest : BootstrapBlazorTestBase
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AvatarUpload_Value_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<AvatarUpload<IBrowserFile>>(pb => pb.Add(a => a.IsSingle, true));
|
||||
var input = cut.FindComponent<InputFile>();
|
||||
cut.InvokeAsync(() => input.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs(new List<MockBrowserFile>()
|
||||
{
|
||||
new MockBrowserFile()
|
||||
})));
|
||||
Assert.Equal("UploadTestFile", cut.Instance.Value!.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AvatarUpload_ListValue_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<AvatarUpload<List<IBrowserFile>>>();
|
||||
var input = cut.FindComponent<InputFile>();
|
||||
cut.InvokeAsync(() => input.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs(new List<MockBrowserFile>()
|
||||
{
|
||||
new MockBrowserFile()
|
||||
})));
|
||||
Assert.Single(cut.Instance.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AvatarUpload_ValidateForm_Ok()
|
||||
{
|
||||
@ -189,6 +239,7 @@ public class UploadTest : BootstrapBlazorTestBase
|
||||
pb.Add(a => a.Model, foo);
|
||||
pb.AddChildContent<AvatarUpload<string>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Accept, "Image");
|
||||
pb.Add(a => a.Value, foo.Name);
|
||||
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||
});
|
||||
@ -218,6 +269,31 @@ public class UploadTest : BootstrapBlazorTestBase
|
||||
Assert.False(invalid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AvatarUpload_FileValidate_Ok()
|
||||
{
|
||||
var foo = new Person();
|
||||
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Model, foo);
|
||||
pb.AddChildContent<AvatarUpload<IBrowserFile>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, foo.Picture);
|
||||
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(foo, nameof(Person.Picture), typeof(IBrowserFile)));
|
||||
});
|
||||
});
|
||||
|
||||
var input = cut.FindComponent<InputFile>();
|
||||
cut.InvokeAsync(() => input.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs(new List<MockBrowserFile>()
|
||||
{
|
||||
new MockBrowserFile()
|
||||
})));
|
||||
|
||||
// 提交表单
|
||||
var form = cut.Find("form");
|
||||
cut.InvokeAsync(() => form.Submit());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ButtonUpload_Ok()
|
||||
{
|
||||
@ -236,10 +312,6 @@ public class UploadTest : BootstrapBlazorTestBase
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.ShowProgress, true);
|
||||
pb.Add(a => a.DefaultFileList, new List<UploadFile>()
|
||||
{
|
||||
new UploadFile() { FileName = "Test-File" }
|
||||
});
|
||||
pb.Add(a => a.OnChange, file =>
|
||||
{
|
||||
uploadFile = file;
|
||||
@ -325,6 +397,7 @@ public class UploadTest : BootstrapBlazorTestBase
|
||||
});
|
||||
cut.InvokeAsync(() => cut.Find(".fa-trash-o.text-danger").Click());
|
||||
Assert.NotNull(deleteFile);
|
||||
Assert.Null(deleteFile!.Error);
|
||||
|
||||
deleteFile = null;
|
||||
// 上传失败测试
|
||||
@ -449,15 +522,113 @@ public class UploadTest : BootstrapBlazorTestBase
|
||||
cut.Contains("fa-format-test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CardUpload_Ok()
|
||||
{
|
||||
var zoom = false;
|
||||
var deleted = false;
|
||||
var cut = Context.RenderComponent<CardUpload<string>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.OnZoomAsync, file =>
|
||||
{
|
||||
zoom = true;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
pb.Add(a => a.OnDelete, file =>
|
||||
{
|
||||
deleted = true;
|
||||
return Task.FromResult(true);
|
||||
});
|
||||
pb.Add(a => a.DefaultFileList, new List<UploadFile>()
|
||||
{
|
||||
new UploadFile() { FileName = "Test-File1.text" },
|
||||
new UploadFile() { FileName = "Test-File2.jpg" },
|
||||
new UploadFile() { PrevUrl = "Test-File3.png" },
|
||||
new UploadFile() { PrevUrl = "Test-File4.bmp" },
|
||||
new UploadFile() { PrevUrl = "Test-File5.jpeg" },
|
||||
new UploadFile() { PrevUrl = "Test-File6.gif" },
|
||||
new UploadFile() { FileName = null! }
|
||||
});
|
||||
});
|
||||
|
||||
// OnZoom
|
||||
cut.InvokeAsync(() => cut.Find(".btn-outline-secondary").Click());
|
||||
Assert.True(zoom);
|
||||
|
||||
// OnDelete
|
||||
cut.InvokeAsync(() => cut.Find(".btn-outline-danger").Click());
|
||||
Assert.True(deleted);
|
||||
|
||||
// ShowProgress
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.ShowProgress, true);
|
||||
pb.Add(a => a.OnChange, async file =>
|
||||
{
|
||||
await file.SaveToFile("1.txt");
|
||||
});
|
||||
});
|
||||
var input = cut.FindComponent<InputFile>();
|
||||
cut.InvokeAsync(() => input.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs(new List<MockBrowserFile>()
|
||||
{
|
||||
new MockBrowserFile("test.txt", "Image-Png")
|
||||
})));
|
||||
cut.InvokeAsync(() => input.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs(new List<MockBrowserFile>()
|
||||
{
|
||||
new MockBrowserFile("test.png")
|
||||
})));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CardUpload_Reset()
|
||||
{
|
||||
var cut = Context.RenderComponent<CardUpload<string>>();
|
||||
cut.InvokeAsync(() => cut.Instance.Reset());
|
||||
Assert.Null(cut.Instance.DefaultFileList);
|
||||
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.DefaultFileList, new List<UploadFile>()
|
||||
{
|
||||
new UploadFile() { FileName = "Test-File1.text" }
|
||||
});
|
||||
});
|
||||
cut.InvokeAsync(() => cut.Instance.Reset());
|
||||
Assert.Empty(cut.Instance.DefaultFileList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CardUpload_ValidateForm_Ok()
|
||||
{
|
||||
var foo = new Foo();
|
||||
var cut = Context.RenderComponent<ValidateForm>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Model, foo);
|
||||
pb.AddChildContent<CardUpload<string>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, foo.Name);
|
||||
pb.Add(a => a.ValueExpression, foo.GenerateValueExpression());
|
||||
});
|
||||
});
|
||||
cut.Contains("form-label");
|
||||
}
|
||||
|
||||
private class Person
|
||||
{
|
||||
[Required]
|
||||
[FileValidation(Extensions = new string[] { ".png", ".jpg", ".jpeg" })]
|
||||
public IBrowserFile? Picture { get; set; }
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
private class MockBrowserFile : IBrowserFile
|
||||
{
|
||||
public MockBrowserFile(string name = "UploadTestFile")
|
||||
public MockBrowserFile(string name = "UploadTestFile", string contentType = "text")
|
||||
{
|
||||
Name = name;
|
||||
LastModified = DateTimeOffset.Now;
|
||||
Size = 10;
|
||||
ContentType = "jpg";
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
Loading…
Reference in New Issue
Block a user