前端实现在如下
@inherits ProductionOrderComponentBase
<ProcessingSpiner Visible="@IsComponentBusy"/>
@if (IsValidating)
{
<LoadingProcess />
}
else
{
if (ComponentMode == ComponentMode.List)
{
<div class="position-relative">
<div class="@(IsDetailBusy ? "component-backdrop" : null)"></div>
<WarningModal WarningMessage="@WarningMessage"
WarningResponse="@HandleWarningResponse"
@bind-IsShowWarning="@IsShowWarning"/>
<DxDataGrid CustomData="@LoadProductionOrders"
KeyFieldName="@KeyFieldName"
CssClass="@CssClass"
PageSize="@PageSize"
PageIndex="@PageIndex"
SelectionMode="@SelectionMode"
SelectAllMode="@SelectAllMode"
ShowFilterRow="IsShowFilterRow"
RowClick="@OnRowClick"
LayoutRestoring="@OnLayoutRestoring"
PagerPageSizeSelectorVisible="true"
PagerAllDataRowsItemVisible="true"
PagerAllowedPageSizes="@AllowedPageSizes"
@bind-MultipleSelectedDataRows="@SelectedCollection"
@ref="@Grid"
T="@ProductionOrderWithDetail">
<HeaderTemplate>
<DataGridToolbar IsBusy="@(IsBusy || IsMasterBusy || IsDetailBusy)"
CssClass="@ToolbarCssClass"
SelectedCount="@SelectedCount"
RequiredClaimType="@RequiredClaimType"
OnToolbarClick="@HandleToolbarResponse"
CanRestore="@CanRestore"
ReadOnly="@ReadOnly">
@if (ReadOnly == false)
{
<DxToolbarItem Text="Generate Delivery Sheet"
Click="@GenerateInventoryRequest"
IconCssClass="mdi mdi-18px mdi-file-move-outline"
Enabled="@(IsBusy == false)"
CssClass="font-bigger"
Visible="@CanEdit"/>
<DxToolbarItem Text="上传"
id="overviewDemoSelectButton1"
IconCssClass="mdi mdi-18px mdi-file-move-outline"
Enabled="@(IsBusy == false)"
CssClass="font-bigger"
Visible="@CanEdit" />
<DxUpload Name="myFile"
Visible=true
ExternalSelectButtonCssSelector="#overviewDemoSelectButton1"
ExternalDropZoneCssSelector="#overviewDemoDropZone"
ExternalDropZoneDragOverCssClass="bg-light border-secondary text-dark"
ChunkSize="2000000"
MaxFileSize=999999999
UploadUrl="@GetUploadUrl("/api/Files/UploadFile/")"
CssClass="w-100">
</DxUpload>
}
</DataGridToolbar>
</HeaderTemplate>
<Columns>
@* <DxDataGridSelectionColumn Width="60px"/> *@
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.OrderNumber)"
Caption="No."
Width="100px">
<DisplayTemplate>
@{
var requestNumb = $"{((ProductionOrderWithDetail) context).OrderNumber:000000}";
<span>@requestNumb</span>
}
</DisplayTemplate>
</DxDataGridColumn>
<DxDataGridDateEditColumn Field="@nameof(ProductionOrderWithDetail.OrderDate)"
DisplayFormat="dd/MM/yyyy"
Width="100px"
Caption="Date"
Visible="false"/>
<DxDataGridDateEditColumn Field="@nameof(ProductionOrderWithDetail.DueDate)"
DisplayFormat="dd/MM/yyyy"
Width="100px"
Caption="Due Date"/>
<DxDataGridDateEditColumn Field="@nameof(ProductionOrderWithDetail.CompletedDate)"
DisplayFormat="dd/MM/yyyy"
Width="100px"
Caption="Completed Date"
Visible="false"/>
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.StatusName)"
Caption="Status"
Width="100px"/>
<DxDataGridDateEditColumn Field="@nameof(ProductionOrderWithDetail.StatusDate)"
DisplayFormat="dd/MM/yyyy"
Width="100px"
Caption="Status Date"
Visible="false"/>
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.LotNumber)"
Caption="Lot Number"
Width="100px"/>
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.ProductCode)"
Caption="Product Code"/>
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.ProductName)"
Caption="Product Name"
Visible="false"/>
<DxDataGridSpinEditColumn Field="@nameof(ProductionOrderWithDetail.Quantity)"
DisplayFormat="N"
Caption="Quantity"
Width="100px"/>
<DxDataGridSpinEditColumn Field="@nameof(ProductionOrderWithDetail.ReadyQuantity)"
DisplayFormat="N"
Caption="Ready Qty."
Width="100px"/>
<DxDataGridSpinEditColumn Field="@nameof(ProductionOrderWithDetail.DeliveredQuantity)"
DisplayFormat="N"
Caption="Delivered Qty."
Width="100px"/>
<DxDataGridSpinEditColumn Field="@nameof(ProductionOrderWithDetail.RemainQuantity)"
DisplayFormat="N"
Caption="Remain Qty."
Width="100px"/>
<DxDataGridSpinEditColumn Field="@nameof(ProductionOrderWithDetail.ProducingQuantity)"
DisplayFormat="N"
Caption="Producing Qty."
Width="100px"/>
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.UnitName)"
Caption="Unit"
Width="100px"/>
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.Comment)"
Caption="Remark"
Visible="false"/>
<DxDataGridColumn Field="@nameof(ProductionOrderWithDetail.DeletedStatus)"
TextAlignment="DataGridTextAlign.Center"
Caption="Status"
Width="80px"
Visible="@IsAdmin"
ShowInColumnChooser="false"/>
</Columns>
</DxDataGrid>
</div>
}
else
{
<div class="card py-2">
<EditForm Model="@DataEditContext" Context="editFormContext" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
<FluentValidationValidator/>
<DxFormLayout CaptionPosition="@CaptionPosition.Vertical" ItemSizeMode="SizeMode.Medium" Context="FormLayoutContext">
<DxFormLayoutItem Caption="Order No." ColSpanMd="3" CssClass="@(ComponentMode == ComponentMode.Add ? "with-tooltip" : null)">
<Template>
<DxTextBox ReadOnly="true"
@bind-Text="@DataEditContext.OrderNumberDisplay"/>
<ValidationMessage For="() => DataEditContext.OrderNumber"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="日期" ColSpanMd="3">
<Template>
<DxDateEdit DisplayFormat="dd/MM/yyyy"
Format="dd/MM/yyyy"
ReadOnly="true"
@bind-Date="@DataEditContext.OrderDate"/>
<ValidationMessage For="() => DataEditContext.OrderDate"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Due Date" ColSpanMd="3">
<Template>
<DxDateEdit DisplayFormat="dd/MM/yyyy"
Format="dd/MM/yyyy"
ReadOnly="@(IsFromProductionRequest || CanChange == false)"
@bind-Date="@DataEditContext.DueDate"/>
<ValidationMessage For="() => DataEditContext.DueDate"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="订单 状态" ColSpanMd="3">
<Template>
<DxComboBox Data="@OrderStatusCollection"
ValueFieldName="@nameof(TgOrderStatuses.Value)"
TextFieldName="@nameof(TgOrderStatuses.DisplayName)"
CssClass="normal-combobox"
@bind-Value="@DataEditContext.Status"/>
<ValidationMessage For="() => DataEditContext.Status"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Lot Number" ColSpanMd="3">
<Template>
<DxTextBox @bind-Text="@DataEditContext.LotNumber"/>
<ValidationMessage For="() => DataEditContext.LotNumber"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Product" ColSpanMd="3">
<Template>
@if (CanChange)
{
<BlazoredTypeahead SearchMethod="SearchProducts"
EnableDropDown="true"
Context="productContext"
placeholder="Type to search..."
@bind-Value="SelectedProduct">
<SelectedTemplate>
@productContext.Code
</SelectedTemplate>
<ResultTemplate>
@productContext.Code
</ResultTemplate>
</BlazoredTypeahead>
}
else
{
<DxTextBox ReadOnly="true"
@bind-Text="@DataEditContext.ProductCode"/>
}
<ValidationMessage For="() => DataEditContext.ProductId"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Name" ColSpanMd="6">
<Template>
<DxTextBox ReadOnly="true"
@bind-Text="@DataEditContext.ProductName"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="@($"Quantity{UnitName}")" ColSpanMd="3">
<Template>
<DxSpinEdit BindValueMode="BindValueMode.OnInput"
MaxValue="999999.9999m"
MinValue="0m"
Increment="0.0001m"
DisplayFormat="N"
@bind-Value="@DataEditContext.Quantity"/>
<ValidationMessage For="() => DataEditContext.Quantity"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="@($"Ready Quantity{UnitName}")" ColSpanMd="3">
<Template>
<DxSpinEdit BindValueMode="BindValueMode.OnInput"
MaxValue="999999.9999m"
MinValue="0m"
Increment="0.0001m"
DisplayFormat="N"
@bind-Value="@DataEditContext.ReadyQuantity"/>
<ValidationMessage For="() => DataEditContext.ReadyQuantity"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem Caption="Remark" ColSpanMd="6">
<Template>
<DxTextBox ReadOnly="@(IsFromProductionRequest || CanChange == false)"
@bind-Text="@DataEditContext.Comment"/>
<ValidationMessage For="() => DataEditContext.Comment"/>
</Template>
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanMd="12">
<Template>
<EditFormButtonGroup ComponentMode="@ComponentMode"
SaveAndCloseOnlyWhenAdd="true"
CancelUpdateClicked="@CancelUpdateClick"
@bind-SubmitClick="@SubmitClick"
@bind-SaveAndClose="@SaveAndClose">
<span class="mr-auto help-text @(ComponentMode == ComponentMode.Edit ? "d-none" : null)">* Auto-generate after saved.</span>
</EditFormButtonGroup>
</Template>
</DxFormLayoutItem>
</DxFormLayout>
</EditForm>
</div>
}
}
@code {
protected string GetUploadUrl(string url)
{
return NavigationManager.ToAbsoluteUri(url).AbsoluteUri;
}
}
API控制器实现
using DevExpress.Blazor.Upload.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Client.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class FilesController : ControllerBase
{
private static readonly long FileSize = 10485760;
private static readonly string FilesType = ".gif,.jpg,.jpeg,.png,.bmp,.GIF,.JPG,.JPEG,.PNG,.doc,.docx,.xls,.xlsx,.csv,.pdf,.ppt,.dwg,.DXF,.zip,.rar,.txt";
private static readonly string FilesBasePath = "c:\\wms_file";
//在IIS新建图片服务器网站,路径指向 FilesBasePath
private static readonly string Ip = "10.111.11.123";
private static readonly string Port = "8899";
void RemoveTempFilesAfterDelay(string path, TimeSpan delay)
{
var dir = new DirectoryInfo(path);
if (dir.Exists)
foreach (var file in dir.GetFiles("*.tmp").Where(f => f.LastWriteTimeUtc.Add(delay) < DateTime.UtcNow))
file.Delete();
}
void ProcessUploadedFile(string tempFilePath, string fileName)
{
var path = Path.Combine(FilesBasePath, "Images");
var imagePath = Path.Combine(path, fileName);
if (System.IO.File.Exists(imagePath))
System.IO.File.Delete(imagePath);
System.IO.File.Copy(tempFilePath, imagePath);
}
void AppendContentToFile(string path, IFormFile content)
{
using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write))
{
content.CopyTo(stream);
}
}
public class ChunkMetadata
{
public int Index { get; set; }
public int TotalCount { get; set; }
public int FileSize { get; set; }
public string FileName { get; set; }
public string FileType { get; set; }
public Guid FileGuid { get; set; }
}
[HttpPost]
[Route("UploadFile")]
[DisableRequestSizeLimit]
public ActionResult UploadFile(IFormFile myFile)
{
string chunkMetadata = Request.Form["chunkMetadata"];
var tempPath = Path.Combine(FilesBasePath, "Images");
// Removes temporary files
RemoveTempFilesAfterDelay(tempPath, new TimeSpan(0, 5, 0));
try
{
if (!string.IsNullOrEmpty(chunkMetadata))
{
var metaDataObject = JsonConvert.DeserializeObject<ChunkMetadata>(chunkMetadata);
var tempFilePath = Path.Combine(tempPath, metaDataObject.FileGuid + ".tmp");
if (!Directory.Exists(tempPath))
Directory.CreateDirectory(tempPath);
AppendContentToFile(tempFilePath, myFile);
if (metaDataObject.Index == (metaDataObject.TotalCount - 1))
{
ProcessUploadedFile(tempFilePath, metaDataObject.FileName);
}
}
}
catch (Exception ex)
{
return BadRequest();
}
return Ok();
}
/// <summary>
/// 上传文件
/// </summary>
[HttpPost("Upload")]
public ActionResult Upload(IFormFile myFile)
{
try
{
var id = "WMS";//我自己的combine id生成器
string newFileName = string.Empty;
if (myFile == null)
{
throw new Exception("传入文件为空");//类型
}
if (myFile.Length <= FileSize)//检查文件大小
{
var suffix = Path.GetExtension(myFile.FileName);//提取上传的文件文件后缀
if (FilesType.IndexOf(suffix) != -1)//检查文件格式
{
newFileName = myFile.FileName + suffix;
//创建每日存储文件夹
if (Directory.Exists($@"{FilesBasePath}\{id}"))
{
//删除原有文件防止文件名重复
DirectoryInfo subdir = new DirectoryInfo($@"{FilesBasePath}\{id}");
subdir.Delete(true);
}
Directory.CreateDirectory($@"{FilesBasePath}\{id}");
using (FileStream fs = System.IO.File.Create($@"{FilesBasePath}\{id}\{newFileName}"))//注意路径里面最好不要有中文
{
myFile.CopyTo(fs);//将上传的文件文件流,复制到fs中
fs.Flush();//清空文件流
}
}
else
throw new Exception("不支持此文件类型");//类型不正确
}
else
{
throw new Exception($"文件大小不得超过{FileSize / (1024f * 1024f)}M"); //请求体过大,文件大小超标
}
return Ok(new { code = 0 });
}
catch (Exception ex)
{
return Ok(new { code = ex.Message });
}
}
}
}