feat(Upload): support drag/drop upload function (#6290)

* doc: 代码格式化

* doc: 代码格式化

* refactor: 代码格式化

* feat: 增加拖动支持

* feat: 增加拖动支持

* refactor: 头像上传框精简代码

* refactor: 精简 dom 结构

* refactor: 精简 CardUpload 结构

* chore: bump version 9.8.0-beta01

Co-Authored-By: movieliang <16262928+movieliang@users.noreply.github.com>

---------

Co-authored-by: movieliang <16262928+movieliang@users.noreply.github.com>
This commit is contained in:
Argo Zhang
2025-06-24 13:02:47 +08:00
committed by GitHub
parent 3a3b8cc5ea
commit efa9089f21
10 changed files with 27 additions and 39 deletions

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<Version>9.7.4</Version>
<Version>9.8.0-beta01</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -46,11 +46,7 @@
@code {
RenderFragment RenderAdd =>
@<div id="@AddId" class="@GetItemClassString()" style="@ItemStyleString">
<div class="@ActionClassString">
<span class="upload-item-plus">
<i class="@AddIcon"></i>
</span>
</div>
@<div id="@AddId" class="@GetItemClassString("upload-item-plus upload-drop-body btn-browser")" style="@ItemStyleString">
<i class="@AddIcon"></i>
</div>;
}

View File

@@ -91,7 +91,8 @@ public partial class AvatarUpload<TValue>
.AddClassFromAttributes(AdditionalAttributes)
.Build();
private string? GetItemClassString() => CssBuilder.Default("upload-item")
private string? GetItemClassString(string? classString = null) => CssBuilder.Default("upload-item")
.AddClass(classString)
.AddClass("is-circle", IsCircle)
.AddClass("disabled", IsDisabled)
.Build();
@@ -106,10 +107,6 @@ public partial class AvatarUpload<TValue>
.AddClass($"--bb-upload-item-border-radius: {BorderRadius};", IsCircle && !string.IsNullOrEmpty(BorderRadius))
.Build();
private string? ActionClassString => CssBuilder.Default("upload-item-actions")
.AddClass("btn-browser", IsDisabled == false)
.Build();
private string? ValidStatusIconString => CssBuilder.Default("valid-icon valid")
.AddClass(ValidStatusIcon)
.Build();

View File

@@ -88,7 +88,7 @@ public partial class ButtonUpload<TValue>
.AddClassFromAttributes(AdditionalAttributes)
.Build();
private string? BrowserButtonClassString => CssBuilder.Default("btn-browser")
private string? BrowserButtonClassString => CssBuilder.Default("btn-browser upload-drop-body")
.AddClass(BrowserButtonClass, !string.IsNullOrEmpty(BrowserButtonClass))
.Build();

View File

@@ -4,7 +4,7 @@
@if (IsShowLabel)
{
<BootstrapLabel required="@Required" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText" />
<BootstrapLabel required="@Required" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText"></BootstrapLabel>
}
<div @attributes="@AdditionalAttributes" class="@ClassString" id="@Id" data-bb-previewer-id="@PreviewerId">
<div class="@BodyClassString">
@@ -26,7 +26,7 @@
}
else
{
<FileIcon Extension="@item.GetExtension()" />
<FileIcon Extension="@item.GetExtension()"></FileIcon>
}
</div>
<div class="upload-item-size"><span>@item.GetFileName()</span> (@item.Size.ToFileSizeString())</div>
@@ -77,18 +77,14 @@
</div>
@if (ShowPreviewList)
{
<ImagePreviewer Id="@PreviewerId" PreviewList="PreviewList" />
<ImagePreviewer Id="@PreviewerId" PreviewList="PreviewList"></ImagePreviewer>
}
<InputFile AdditionalAttributes="@GetUploadAdditionalAttributes()" OnChange="OnFileChange" />
<InputFile AdditionalAttributes="@GetUploadAdditionalAttributes()" OnChange="OnFileChange"></InputFile>
</div>
@code {
RenderFragment RenderAdd =>
@<div class="@CardItemClass">
<div class="@ActionClassString">
<span class="upload-item-plus">
<i class="@AddIcon"></i>
</span>
</div>
<i class="@AddIcon"></i>
</div>;
}

View File

@@ -34,7 +34,7 @@ public partial class CardUpload<TValue>
private string? GetDeleteButtonDisabledString(UploadFile item) => (!IsDisabled && item.Uploaded) ? null : "disabled";
private string? CardItemClass => CssBuilder.Default("upload-item")
private string? CardItemClass => CssBuilder.Default("upload-item upload-item-plus btn-browser upload-drop-body")
.AddClass("disabled", IsDisabled)
.Build();
@@ -116,10 +116,6 @@ public partial class CardUpload<TValue>
[Parameter]
public List<string>? AllowExtensions { get; set; }
private string? ActionClassString => CssBuilder.Default("upload-item-actions")
.AddClass("btn-browser", IsDisabled == false)
.Build();
/// <summary>
/// <inheritdoc/>
/// </summary>

View File

@@ -7,7 +7,7 @@
<BootstrapLabel required="@Required" for="@Id" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText" />
}
<div @attributes="@AdditionalAttributes" class="@ClassString" id="@Id">
<div class="input-group">
<div class="input-group upload-drop-body">
<input type="text" class="@InputValueClassString" disabled="@Disabled" readonly
placeholder="@PlaceHolder" value="@CurrentValueAsString" />
@if (ShowDeleteButton)

View File

@@ -93,6 +93,7 @@
display: flex;
flex-wrap: wrap;
gap: 1rem;
position: relative;
}
.upload .upload-body.is-avatar .upload-item {
@@ -135,17 +136,18 @@
}
.upload .upload-body.is-avatar .upload-item .upload-item-actions,
.upload .upload-body.is-card .upload-item .upload-item-actions > .upload-item-plus {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
.upload .upload-body.is-avatar .upload-item-plus,
.upload .upload-body.is-card .upload-item-plus {
display: flex;
align-items: center;
justify-content: center;
}
.upload .upload-body.is-avatar .upload-item .upload-item-actions {
position: absolute;
inset: 0;
}
.upload .upload-body.is-avatar .upload-item .upload-item-actions i {
font-size: 1rem;
}

View File

@@ -48,7 +48,8 @@ export function init(id) {
inputFile.files = fileList
const event = new Event('change', { bubbles: true })
inputFile.dispatchEvent(event)
} catch (e) {
}
catch (e) {
console.error(e)
}
})
@@ -112,9 +113,10 @@ export function dispose(id) {
EventHandler.off(document, 'dragover', preventHandler)
EventHandler.off(el, 'click')
EventHandler.off(el, 'drop')
EventHandler.off(el, 'paste')
EventHandler.off(inputFile, 'change')
EventHandler.off(inputFile, 'change');
EventHandler.off(body, 'dragleave')
EventHandler.off(body, 'drop')
EventHandler.off(body, 'dragenter')

View File

@@ -80,12 +80,11 @@ public class UploadAvatarTest : BootstrapBlazorTestBase
var button = cut.Find(".upload-item-delete");
await cut.InvokeAsync(() => button.Click());
cut.Contains("upload-item-actions btn-browser");
cut.Contains("btn-browser");
cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.IsDisabled, true);
});
cut.Contains("upload-item-actions");
// IsUploadButtonAtFirst
cut.SetParametersAndRender(pb =>