!852 feat(#I2C63D): add ClickToUpload mode in Upload component

* docs: 增加点击上传模式示例文件
* docs: 增加点击上传示例文档
* feat: 增加点击上传功能
* chore: 打包脚本不输出颜色
* feat: 增加单击上传模式
* chore: 增加 bundle&minifier Cli tool
* docs: 更新 Upload 示例文件
* docs: 更新 Table 示例文件
* style: upload 适配移动端
This commit is contained in:
Argo 2021-01-06 11:31:51 +08:00
parent e97d8eff21
commit 841d83b2fa
13 changed files with 199 additions and 64 deletions

View File

@ -11,20 +11,19 @@
<DetailRowTemplate> <DetailRowTemplate>
<Tab> <Tab>
<TabItem Text="明细数据"> <TabItem Text="明细数据">
@{ @{
// 此段代码为提高性能 // 此段代码为提高性能
var cacheKey = context.Name ?? ""; var cacheKey = context.Name ?? "";
var DetailDataSource = Enumerable.Empty<DetailRow> var DetailDataSource = Enumerable.Empty<DetailRow>();
(); if (Cache.ContainsKey(cacheKey))
if (Cache.ContainsKey(cacheKey)) {
{ DetailDataSource = Cache[cacheKey];
DetailDataSource = Cache[cacheKey]; }
} else
else {
{ DetailDataSource = GetDetailRowsByName(cacheKey).ToList();
DetailDataSource = GetDetailRowsByName(cacheKey).ToList(); Cache.Add(cacheKey, DetailDataSource);
Cache.Add(cacheKey, DetailDataSource); }
}
} }
<Table TItem="DetailRow" IsBordered="true" ShowToolbar="false" Items="@DetailDataSource"> <Table TItem="DetailRow" IsBordered="true" ShowToolbar="false" Items="@DetailDataSource">
<TableColumns Context="Detail"> <TableColumns Context="Detail">

View File

@ -1 +1,15 @@
<Upload /> <Upload ShowDeleteButton="true" OnChange="@OnFileChange" OnDelete="@OnFileDelete"></Upload>
@code {
private Task<string> OnFileChange(InputFileChangeEventArgs args)
{
Trace?.Log($"{args.File.Name} 上传成功");
return Task.FromResult("");
}
private Task<bool> OnFileDelete(string fileName)
{
Trace?.Log($"{fileName} 成功移除");
return Task.FromResult(true);
}
}

View File

@ -1 +1 @@
<Upload ShowPreview="true"></Upload> <Upload Style="UploadStyle.ClickToUpload" OnDelete="@(fileName => Task.FromResult(true))"></Upload>

View File

@ -1 +1,15 @@
<Upload IsCircle="true"></Upload> @{
var DefaultFileList = new List<UploadFile>();
DefaultFileList.AddRange(new[]
{
new UploadFile()
{
FileName = "default1.jpg"
},
new UploadFile()
{
FileName = "default2.jpg"
}
});
}
<Upload Style="UploadStyle.ClickToUpload" OnDelete="@(fileName => Task.FromResult(true))" DefaultFileList="@DefaultFileList"></Upload>

View File

@ -4,15 +4,6 @@
<h4>通过点击或者拖拽上传文件</h4> <h4>通过点击或者拖拽上传文件</h4>
<p>
由于上传组件上传文件的大小受后台程序、反向代理等诸多因素影响,相关文章请参阅 <a href="https://gitee.com/LongbowEnterprise/BootstrapBlazor/wikis/Upload%20%E7%BB%84%E4%BB%B6%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9?sort_id=2166580" target="_blank">Upload 组件注意事项</a>
<br>出于服务器安全考虑,以下组件示例中上传文件均限制为文件类型必须为 <code>image/*</code> ,文件大小被限制为不能超过 <code>20 MB</code>
</p>
<Tips>
<p>本组件正在开发中,此页面为测试功能时使用</p>
</Tips>
<Block Title="基本用法" Introduction="与其他表单组件一起使用,显示文件名称,点击 <b>浏览</b> 按钮后选择文件并上传;通过设置 <code>ShowRemoveButton</code> 参数,显示 <b>删除</b> 按钮,点击删除按钮时回调 <code>OnDelete</code> 委托方法" CodeFile="upload.1.html"> <Block Title="基本用法" Introduction="与其他表单组件一起使用,显示文件名称,点击 <b>浏览</b> 按钮后选择文件并上传;通过设置 <code>ShowRemoveButton</code> 参数,显示 <b>删除</b> 按钮,点击删除按钮时回调 <code>OnDelete</code> 委托方法" CodeFile="upload.1.html">
<div class="form-inline"> <div class="form-inline">
<div class="row"> <div class="row">
@ -33,6 +24,35 @@
<Logger @ref="Trace" /> <Logger @ref="Trace" />
</Block> </Block>
<Block Title="点击上传" Introduction="经典款式,用户点击按钮弹出文件选择框。" CodeFile="upload.2.html">
<div class="row">
<div class="col-12 col-sm-6">
<Upload Style="UploadStyle.ClickToUpload" OnDelete="@(fileName => Task.FromResult(true))"></Upload>
</div>
</div>
</Block>
<Block Title="已上传文件列表" Introduction="使用 <code>DefaultFileList</code> 设置已上传的内容" CodeFile="upload.3.html">
<div class="row">
<div class="col-12 col-sm-6">
@{
var DefaultFileList = new List<UploadFile>();
DefaultFileList.AddRange(new[]
{
new UploadFile()
{
FileName = "default1.jpg"
},
new UploadFile()
{
FileName = "default2.jpg"
}
});
}
<Upload Style="UploadStyle.ClickToUpload" OnDelete="@(fileName => Task.FromResult(true))" DefaultFileList="@DefaultFileList"></Upload>
</div>
</div>
</Block>
<!--<Block Title="预览后上传" Introduction="通过设置 <code>ShowPreview</code> 属性开启本地预览,点击上传按钮开始上传" CodeFile="upload.2.html"> <!--<Block Title="预览后上传" Introduction="通过设置 <code>ShowPreview</code> 属性开启本地预览,点击上传按钮开始上传" CodeFile="upload.2.html">
<p>仅允许上传 <code>images/*</code> 格式文件,文件大小不能超过 <code>20 MB</code></p> <p>仅允许上传 <code>images/*</code> 格式文件,文件大小不能超过 <code>20 MB</code></p>
<Upload AllowFileType="@AllowFiles" MaxFileLength="20971520" ShowPreview="true" OnUploaded="@OnPreviewUpload"></Upload> <Upload AllowFileType="@AllowFiles" MaxFileLength="20971520" ShowPreview="true" OnUploaded="@OnPreviewUpload"></Upload>

View File

@ -5,16 +5,32 @@
@if (Style == UploadStyle.Normal) @if (Style == UploadStyle.Normal)
{ {
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" id="@Id" readonly placeholder="@PlaceHolder" value="@FileName" /> <input type="text" class="form-control" id="@Id" readonly placeholder="@PlaceHolder" value="@(UploadFiles.FirstOrDefault()?.FileName)" />
<div class="input-group-append"> <div class="input-group-append">
@if (ShowDeleteButton) @if (ShowDeleteButton)
{ {
<Button class="@RemoveButtonClassString" IsDisabled="@IsDeleteButtonDisabled" Icon="@DeleteButtonIcon" Text="@DeleteButtonText" OnClick="@OnFileDelete" /> <Button class="@RemoveButtonClassString" IsDisabled="@IsDeleteButtonDisabled" Icon="@DeleteButtonIcon" Text="@DeleteButtonText" OnClick="@(e => OnFileDelete(UploadFiles.FirstOrDefault()))" />
} }
@*<Button class="@UploadButtonClassString" IsDisabled="@IsDisabled" Icon="@UploadButtonIcon" Text="@UploadButtonText" />*@
<Button class="@BrowserButtonClassString" IsDisabled="@IsDisabled" Icon="@BrowserButtonIcon" Text="@BrowserButtonText" OnClick="@OnFileBrowser" /> <Button class="@BrowserButtonClassString" IsDisabled="@IsDisabled" Icon="@BrowserButtonIcon" Text="@BrowserButtonText" OnClick="@OnFileBrowser" />
</div> </div>
</div> </div>
<InputFile hidden accept="@AllowFileType" OnChange="OnFileChange" />
}
else if (Style == UploadStyle.ClickToUpload)
{
<Button class="@BrowserButtonClassString" IsDisabled="@IsDisabled" Icon="@BrowserButtonIcon" Text="@BrowserButtonText" />
<div class="upload-body">
@foreach (var item in UploadFiles)
{
<div class="@GetUploadItemClassString(item)">
<i class="@GetFileIcon(item)"></i>
<span>@item.FileName</span>
<i class="fa fa-check-circle-o text-success"></i>
<i class="fa fa-trash-o text-danger" @onclick:stopPropagation @onclick="@(e => OnFileDelete(item))"></i>
</div>
}
</div>
<InputFile hidden multiple="@MultipleString" accept="@AllowFileType" OnChange="OnFileChange" />
} }
@*<div class="upload-item"> @*<div class="upload-item">
<div class="@PreviewClassString" style="@PrevStyleString"> <div class="@PreviewClassString" style="@PrevStyleString">
@ -35,5 +51,4 @@
</div> </div>
</div> </div>
</div>*@ </div>*@
<InputFile hidden multiple="@MultipleString" accept="@AllowFileType" OnChange="OnFileChange" />
</div> </div>

View File

@ -11,6 +11,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace BootstrapBlazor.Components namespace BootstrapBlazor.Components
@ -57,6 +58,15 @@ namespace BootstrapBlazor.Components
.AddClass($"height: {Width}px;", !IsStack && IsCircle) .AddClass($"height: {Width}px;", !IsStack && IsCircle)
.Build(); .Build();
private string? GetUploadItemClassString(UploadFile item) => CssBuilder.Default("upload-item")
.AddClass("is-valid", item.Code == 0)
.AddClass("is-invalid", item.Code != 0)
.Build();
private string? GetFileIcon(UploadFile item) => CssBuilder.Default("fa")
.AddClass("fa-file-text-o", true)
.Build();
/// <summary> /// <summary>
/// 获得/设置 圆形进度半径 /// 获得/设置 圆形进度半径
/// </summary> /// </summary>
@ -94,7 +104,10 @@ namespace BootstrapBlazor.Components
.AddClass(BrowserButtonClass) .AddClass(BrowserButtonClass)
.Build(); .Build();
private bool IsDeleteButtonDisabled => IsDisabled || string.IsNullOrEmpty(FileName); private bool IsDeleteButtonDisabled => IsDisabled || !UploadFiles.Any();
[NotNull]
private List<UploadFile>? UploadFiles { get; set; }
/// <summary> /// <summary>
/// 获得/设置 上传组件模式 默认为 Normal 正常模式多用于表单中 /// 获得/设置 上传组件模式 默认为 Normal 正常模式多用于表单中
@ -157,10 +170,11 @@ namespace BootstrapBlazor.Components
public string Icon { get; set; } = "fa fa-cloud-upload"; public string Icon { get; set; } = "fa fa-cloud-upload";
/// <summary> /// <summary>
/// 获得/设置 上传文件名 /// 获得/设置 已上传文件集合
/// </summary> /// </summary>
[Parameter] [Parameter]
public string? FileName { get; set; } [NotNull]
public List<UploadFile>? DefaultFileList { get; set; }
/// <summary> /// <summary>
/// 获得/设置 是否显示预览 默认不预览 /// 获得/设置 是否显示预览 默认不预览
@ -306,6 +320,15 @@ namespace BootstrapBlazor.Components
ResetText ??= Localizer[nameof(ResetText)]; ResetText ??= Localizer[nameof(ResetText)];
FileTooLargeText ??= Localizer[nameof(FileTooLargeText)]; FileTooLargeText ??= Localizer[nameof(FileTooLargeText)];
AllowFileTypeErrorMessage ??= Localizer[nameof(AllowFileTypeErrorMessage)]; AllowFileTypeErrorMessage ??= Localizer[nameof(AllowFileTypeErrorMessage)];
if (Style != UploadStyle.Normal)
{
UploadFiles ??= new List<UploadFile>();
}
UploadFiles ??= new List<UploadFile>();
if (DefaultFileList != null) UploadFiles.AddRange(DefaultFileList);
} }
/// <summary> /// <summary>
@ -322,42 +345,45 @@ namespace BootstrapBlazor.Components
} }
} }
private async Task OnFileDelete() private async Task OnFileDelete(UploadFile? item)
{ {
if (OnDelete != null && !string.IsNullOrEmpty(FileName)) if (OnDelete != null && item != null)
{ {
var ret = await OnDelete(FileName); var ret = await OnDelete(item.File?.Name ?? item.FileName);
if (ret) if (ret)
{ {
FileName = null; UploadFiles.Remove(item);
} }
} }
} }
private Task OnFileBrowser() private Task OnFileBrowser()
{ {
FileName = ""; // 单文件模式
UploadFiles.Clear();
return Task.CompletedTask; return Task.CompletedTask;
} }
private async Task OnFileChange(InputFileChangeEventArgs args) private async Task OnFileChange(InputFileChangeEventArgs args)
{ {
FileName = args.File.Name; var file = new UploadFile()
if (OnChange == null)
{ {
var format = args.File.ContentType; FileName = args.File.Name,
var imageFile = await args.File.RequestImageFileAsync(format, 640, 480); Size = args.File.Size,
File = args.File
};
UploadFiles.Add(file);
using var fileStream = imageFile.OpenReadStream(); if (Style == UploadStyle.Normal)
using var memoryStream = new MemoryStream(); {
await fileStream.CopyToAsync(memoryStream); if (OnChange != null)
{
ImageUrl = $"data:{format};base64,{Convert.ToBase64String(memoryStream.ToArray())}"; ImageUrl = await OnChange(args);
}
} }
else else if (Style == UploadStyle.ClickToUpload)
{ {
ImageUrl = await OnChange(args);
} }
} }

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/ // Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.AspNetCore.Components.Forms;
namespace BootstrapBlazor.Components namespace BootstrapBlazor.Components
{ {
/// <summary> /// <summary>
@ -38,5 +40,10 @@ namespace BootstrapBlazor.Components
/// 获得/设置 错误信息 /// 获得/设置 错误信息
/// </summary> /// </summary>
public string? Error { get; set; } public string? Error { get; set; }
/// <summary>
/// 获得/设置 上传文件实例
/// </summary>
internal IBrowserFile? File { get; set; }
} }
} }

View File

@ -2,12 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/ // Website: https://www.blazor.zone or https://argozhang.github.io/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BootstrapBlazor.Components namespace BootstrapBlazor.Components
{ {
/// <summary> /// <summary>
@ -18,8 +12,11 @@ namespace BootstrapBlazor.Components
/// <summary> /// <summary>
/// 正常模式 /// 正常模式
/// </summary> /// </summary>
Normal Normal,
/// <summary>
/// 点击上传
/// </summary>
ClickToUpload,
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -3640,9 +3640,52 @@ input:disabled,
/*end calendar*/ /*end calendar*/
/*upload*/ /*upload*/
.form-inline .upload { @media (min-width: 576px) {
width: calc(100% - 104px); .form-inline .upload {
width: 100%;
}
} }
@media (min-width: 768px) {
.form-inline .upload {
width: calc(100% - 104px);
}
}
.upload .upload-body {
margin-top: 10px;
}
.upload .upload-body .upload-item {
display: flex;
align-items: center;
padding: 3px 5px;
border-radius: 3px;
transition: background-color .3s linear;
cursor: pointer;
}
.upload .upload-body .upload-item:not(:last-child) {
margin-bottom: 2px;
}
.upload .upload-body .upload-item:hover {
background-color: #ebeef5;
}
.upload .upload-body .upload-item:hover .fa-trash-o {
display: inline-block;
}
.upload .upload-body .upload-item span {
flex: 1;
padding: 0 5px;
}
.upload .upload-body .upload-item .fa-trash-o,
.upload .upload-body .upload-item:hover .fa-check-circle-o {
display: none;
}
/*end upload*/ /*end upload*/
/*divider*/ /*divider*/

File diff suppressed because one or more lines are too long

View File

@ -43,4 +43,4 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
$file.trigger('click'); $file.trigger('click');
}); });
}; };
})(jQuery); })(jQuery);