本章介绍学习增、删、改、查、导功能如何实现,下面以商品资料作为示例,该业务栏位如下:
类型、编码、名称、规格、单位、库存下限、库存上限、备注
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类
- 该类是前后端数据交互接口,继承BaseClient类
- 该类只需提供分页查询、删除和保存,导入功能由框架统一异步处理
| public class GoodsClient : BaseClient |
| { |
| public GoodsClient(Context context) : base(context) { } |
| |
| public Task> QueryGoodsesAsync(PagingCriteria criteria) => Context.QueryAsync("Goods/QueryGoodses", criteria); |
| public Task DeleteGoodsesAsync(List models) => Context.PostAsync("Goods/DeleteGoodses", models); |
| public Task SaveGoodsAsync(object model) => Context.PostAsync("Goods/SaveGoods", model); |
| } |
2. 前端
2.1. 创建List页面
- 在KIMS.Razor项目BaseData文件夹下创建GoodsList类
- 该类是数据列表页面,继承WebGridView类
- 列表页面按钮和栏位在框架模块管理中配置
| class GoodsList : WebGridView |
| { |
| |
| protected override Task> 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() => OnDeleteM(Client.Goods.DeleteGoodsesAsync); |
| public void Edit(KmGoods row) => ShowForm(row); |
| public void Delete(KmGoods row) => OnDelete(row, Client.Goods.DeleteGoodsesAsync); |
| } |
- 在KIMS.Razor项目BaseData\Forms文件夹下创建GoodsForm类
- 该类是数据编辑和查看明细页面,继承WebForm类
| [Dialog(800, 420)] |
| class GoodsForm : WebForm |
| { |
| |
| protected override void BuildFields(FieldBuilder builder) |
| { |
| builder.Hidden(f => f.Id); |
| builder.Table(table => |
| { |
| table.ColGroup(15, 35, 15, 35); |
| table.Tr(attr => |
| { |
| builder.Field(f => f.Code).Enabled(TModel.IsNew).Build(); |
| builder.Field(f => f.Name).Build(); |
| }); |
| table.Tr(attr => |
| { |
| builder.Field |
| builder.Field |
| }); |
| table.Tr(attr => builder.Field(f => f.Model).ColSpan(3).Build()); |
| table.Tr(attr => builder.Field(f => f.TaxRate).ColSpan(3).Set(f => f.Items, AppDictionary.TaxRates).Build()); |
| table.Tr(attr => |
| { |
| builder.Field(f => f.MinStock).Build(); |
| builder.Field(f => f.MaxStock).Build(); |
| }); |
| table.Tr(attr => builder.Field |
| }); |
| } |
| |
| 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 QueryGoodses([FromBody] PagingCriteria criteria) => Service.QueryGoodses(criteria); |
| |
| [HttpPost("[action]")] |
| public Result DeleteGoodses([FromBody] List models) => Service.DeleteGoodses(models); |
| |
| [HttpPost("[action]")] |
| public Result SaveGoods([FromBody] object model) => Service.SaveGoods(GetDynamicModel(model)); |
| } |
3.2. 创建Service类
- 在KIMS.Core项目Services文件夹下创建GoodsService类
- 该类为业务逻辑服务类,继承ServiceBase类
| class GoodsService : ServiceBase |
| { |
| internal GoodsService(Context context) : base(context) { } |
| |
| internal PagingResult QueryGoodses(PagingCriteria criteria) |
| { |
| return GoodsRepository.QueryGoodses(Database, criteria); |
| } |
| |
| internal Result DeleteGoodses(List 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((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 QueryGoodses(Database db, PagingCriteria criteria) |
| { |
| var sql = "select * from KmGoods where CompNo=@CompNo"; |
| return db.QueryPage(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 Columns |
| { |
| get |
| { |
| return new List |
| { |
| 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(); |
| 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("税率"), |
| MinStock = row.GetValue("库存下限"), |
| MaxStock = row.GetValue("库存上限"), |
| 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. 运行测试
- 运行效果如下
5. 相关资料