Blazor實戰——Known框架單表增刪改查導
本章介紹學習增、刪、改、查、導功能如何實現,下面以商品資料作為示例,該業務欄位如下:
類型、編碼、名稱、規格、單位、庫存下限、庫存上限、備注
1. 前后端共用
1.1. 創建實體類
- 在KIMS項目Entities文件夾下創建KmGoods實體類
- 該類繼承EntityBase類
- 屬性使用Column特性描述,用于生成頁面字段和數據校驗
public class KmGoods : EntityBase
{
[Column("商品類型", "", true, "1", "50")]
public string? Type { get; set; }
......
[Column("庫存下限", "", false)]
public decimal? MinStock { get; set; }
......
[Column("備注", "", false)]
public string? Note { get; set; }
}
1.2. 創建Client類
- 在KIMS項目Clients文件夾下創建GoodsClient類
- 該類是前后端數據交互接口,繼承ClientBase類
- 該類只需提供分頁查詢、刪除和保存,導入功能由框架統一異步處理
public class GoodsClient : ClientBase
{
public GoodsClient(Context context) : base(context) { }
public Task<PagingResult<KmGoods>> QueryGoodsesAsync(PagingCriteria criteria) => Context.QueryAsync<KmGoods>("Goods/QueryGoodses", criteria);
public Task<Result> DeleteGoodsesAsync(List<KmGoods> models) => Context.PostAsync("Goods/DeleteGoodses", models);
public Task<Result> SaveGoodsAsync(object model) => Context.PostAsync("Goods/SaveGoods", model);
}
2. 前端
2.1. 創建List頁面
- 在KIMS.Razor項目BaseData文件夾下創建GoodsList類
- 該類是數據列表頁面,繼承WebGridView<KmGoods, GoodsForm>類
- 列表頁面按鈕和欄位在框架模塊管理中配置
class GoodsList : WebGridView<KmGoods, GoodsForm>
{
//分頁查詢
protected override Task<PagingResult<KmGoods>> OnQueryData(PagingCriteria criteria)
{
return Client.Goods.QueryGoodsesAsync(criteria);
}
//表格欄位格式化顯示
protected override void FormatColumns()
{
Column(c => c.Type).Select(new SelectOption { Codes = AppDictionary.GoodsType });
Column(c => c.TaxRate).Template((b, r) => b.Text(r.TaxRate?.ToString("P")));
}
public void New() => ShowForm();//新增按鈕方法
public void DeleteM() => DeleteRows(Client.Goods.DeleteGoodsesAsync);//批量刪除按鈕方法
public void Edit(KmGoods row) => ShowForm(row);//編輯操作方法
public void Delete(KmGoods row) => DeleteRow(row, Client.Goods.DeleteGoodsesAsync);//刪除操作方法
}
2.2. 創建Form頁面
- 在KIMS.Razor項目BaseData\Forms文件夾下創建GoodsForm類
- 該類是數據編輯和查看明細頁面,繼承WebForm
類
[Dialog(800, 420)]//設置對話框大小
class GoodsForm : WebForm<KmGoods>
{
//表單布局
protected override void BuildFields(FieldBuilder<KmGoods> builder)
{
builder.Hidden(f => f.Id);//隱藏字段
builder.Table(table =>
{
table.ColGroup(15, 35, 15, 35);
table.Tr(attr =>
{
table.Field<Text>(f => f.Code).Enabled(TModel.IsNew).Build();//編碼,編輯時灰顯
table.Field<Text>(f => f.Name).Build();
});
table.Tr(attr =>
{
table.Field<Select>(f => f.Type).Set(f => f.Codes, AppDictionary.GoodsType).Build();//下拉框
table.Field<Select>(f => f.Unit).Set(f => f.Codes, AppDictionary.GoodsUnit).Build();
});
table.Tr(attr => table.Field<Text>(f => f.Model).ColSpan(3).Build());
table.Tr(attr => table.Field<RadioList>(f => f.TaxRate).ColSpan(3).Set(f => f.Items, AppDictionary.TaxRates).Build());//單選按鈕
table.Tr(attr =>
{
table.Field<Number>(f => f.MinStock).Build();//數值框
table.Field<Number>(f => f.MaxStock).Build();
});
table.Tr(attr => table.Field<TextArea>(f => f.Note).ColSpan(3).Build());//文本域
});
}
//表單底部按鈕
protected override void BuildButtons(RenderTreeBuilder builder)
{
builder.Button(FormButton.Save, Callback(OnSave), !ReadOnly);
base.BuildButtons(builder);
}
//保存按鈕方法
private void OnSave() => SubmitAsync(Client.Goods.SaveGoodsAsync);
}
3. 后端
3.1. 創建Controller類
- 在KIMS.Core項目Controllers文件夾下創建GoodsController類
- 該類為服務端WebApi,繼承BaseController類
[Route("[controller]")]
public class GoodsController : BaseController
{
private GoodsService Service => new(Context);
[HttpPost("[action]")]
public PagingResult<KmGoods> QueryGoodses([FromBody] PagingCriteria criteria) => Service.QueryGoodses(criteria);
[HttpPost("[action]")]
public Result DeleteGoodses([FromBody] List<KmGoods> models) => Service.DeleteGoodses(models);
[HttpPost("[action]")]
public Result SaveGoods([FromBody] object model) => Service.SaveGoods(GetDynamicModel(model));//轉成dynamic類型
}
3.2. 創建Service類
- 在KIMS.Core項目Services文件夾下創建GoodsService類
- 該類為業務邏輯服務類,繼承ServiceBase類
class GoodsService : ServiceBase
{
internal GoodsService(Context context) : base(context) { }
//分頁查詢
internal PagingResult<KmGoods> QueryGoodses(PagingCriteria criteria)
{
return GoodsRepository.QueryGoodses(Database, criteria);
}
//刪除數據
internal Result DeleteGoodses(List<KmGoods> models)
{
if (models == null || models.Count == 0)
return Result.Error(Language.SelectOneAtLeast);
//此處增加刪除數據校驗
return Database.Transaction(Language.Delete, db =>
{
foreach (var item in models)
{
db.Delete(item);
}
});
}
//保存數據
internal Result SaveGoods(dynamic model)
{
var entity = Database.QueryById<KmGoods>((string)model.Id);
entity ??= new KmGoods { CompNo = CurrentUser.CompNo };
entity.FillModel(model);
var vr = entity.Validate();
if (vr.IsValid)
{
if (GoodsRepository.ExistsGoods(Database, entity))
return Result.Error("商品編碼已存在。");
}
if (!vr.IsValid)
return vr;
return Database.Transaction(Language.Save, db =>
{
if (entity.IsNew)
{
entity.Code = GetGoodsMaxNo(db);
}
db.Save(entity);
}, entity.Id);
}
//獲取商品最大編碼
private static string GetGoodsMaxNo(Database db)
{
var prefix = "G";
var maxNo = GoodsRepository.GetGoodsMaxNo(db, prefix);
if (string.IsNullOrWhiteSpace(maxNo))
maxNo = $"{prefix}0000";
return GetMaxFormNo(prefix, maxNo);
}
}
3.3. 創建Repository類
- 在KIMS.Core項目Repositories文件夾下創建GoodsRepository類
- 該類為數據訪問類
class GoodsRepository
{
//分頁查詢
internal static PagingResult<KmGoods> QueryGoodses(Database db, PagingCriteria criteria)
{
var sql = "select * from KmGoods where CompNo=@CompNo";
return db.QueryPage<KmGoods>(sql, criteria);//查詢條件自動綁定
}
//獲取商品最大編碼
internal static string GetGoodsMaxNo(Database db, string prefix)
{
var sql = $"select max(Code) from KmGoods where CompNo=@CompNo and Code like '{prefix}%'";
return db.Scalar<string>(sql, new { db.User.CompNo });
}
//判斷商品是否已存在
internal static bool ExistsGoods(Database db, KmGoods entity)
{
var sql = "select count(*) from KmGoods where Id<>@Id and Code=@Code";
return db.Scalar<int>(sql, new { entity.Id, entity.Code }) > 0;
}
}
3.4. 創建Import類
- 在KIMS.Core項目Imports文件夾下創建KmGoodsImport類(約定:類名以實體類名+Import)
- 該類為數據異步導入處理類,由框架自動調用,繼承BaseImport類
class KmGoodsImport : BaseImport
{
public KmGoodsImport(Database database) : base(database) { }
//定義導入欄位,自動生成導入規范
public override List<ImportColumn> Columns
{
get
{
return new List<ImportColumn>
{
new ImportColumn("商品類型", true),
new ImportColumn("商品編碼", true),
new ImportColumn("商品名稱", true),
new ImportColumn("計量單位", true),
new ImportColumn("規格型號", true),
new ImportColumn("稅率"),
new ImportColumn("庫存下限"),
new ImportColumn("庫存上限"),
new ImportColumn("備注")
};
}
}
//異步導入處理邏輯
public override Result Execute(SysFile file)
{
var models = new List<KmGoods>();
var result = ImportHelper.ReadFile(file, row =>
{
var model = new KmGoods
{
CompNo = file.CompNo,
Type = row.GetValue("商品類型"),
Code = row.GetValue("商品編碼"),
Name = row.GetValue("商品名稱"),
Unit = row.GetValue("計量單位"),
Model = row.GetValue("規格型號"),
TaxRate = row.GetValue<decimal?>("稅率"),
MinStock = row.GetValue<decimal?>("庫存下限"),
MaxStock = row.GetValue<decimal?>("庫存上限"),
Note = row.GetValue("備注")
};
var vr = model.Validate();
if (vr.IsValid)
{
if (models.Exists(m => m.Code == model.Code))
vr.AddError("商品編碼不能重復!");
else if (GoodsRepository.ExistsGoods(Database, model))
vr.AddError($"系統已經存在該商品編碼!");
}
if (!vr.IsValid)
row.ErrorMessage = vr.Message;
else
models.Add(model);
});
if (!result.IsValid)
return result;
return Database.Transaction("導入", db =>
{
foreach (var item in models)
{
db.Save(item);
}
});
}
}
4. 運行測試
- 運行效果如下