Blazor实战——Known框架单表增删改查导

blazor,实战,known,框架,删改 · 浏览次数 : 448

小编点评

**Controller类** ```csharp public class GoodsController : BaseController { private GoodsService Service => new GoodsService(Context); // ...其他方法省略 ... protected override void BuildButtons(RenderTreeBuilder builder) { builder.Button(FormButton.Save, Callback(OnSave), !ReadOnly); base.BuildButtons(builder); } private void OnSave() => SubmitAsync(Client.Goods.SaveGoodsAsync); } ``` **Service类** ```csharp public class GoodsService : ServiceBase { private readonly Database _db; public GoodsService(Database db) { _db = db; } // ...其他方法省略 ... public PagingResult QueryGoodses(PagingCriteria criteria) { return _db.QueryPage( "select * from KmGoods where CompNo=@CompNo", criteria.Skip, criteria.Take ); } // ...其他方法省略 ... } ``` **Repository类** ```csharp public class GoodsRepository : BaseRepository { // ...其他方法省略 ... public static string GetGoodsMaxNo(Database db, string prefix) { return db.Scalar( $"select max(Code) from KmGoods where CompNo=@CompNo and Code like '{prefix}%'", new { CompNo = 1 } ); } } ``` **Import类** ```csharp public class KmGoodsImport : BaseImport { private readonly Database _db; public KmGoodsImport(Database database) { _db = database; } // ...其他方法省略 ... public override List Columns { get { return new List { // ...其他导入栏位 ... }; } } // ...其他方法省略 ... } ```

正文

本章介绍学习增、删、改、查、导功能如何实现,下面以商品资料作为示例,该业务栏位如下:

类型、编码、名称、规格、单位、库存下限、库存上限、备注

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. 运行测试

  • 运行效果如下
    输入图片说明
    输入图片说明

5. 相关资料

与Blazor实战——Known框架单表增删改查导相似的内容:

Blazor实战——Known框架单表增删改查导

本章介绍学习增、删、改、查、导功能如何实现,下面以商品资料作为示例,该业务栏位如下: > 类型、编码、名称、规格、单位、库存下限、库存上限、备注 ## 1. 前后端共用 ### 1.1. 创建实体类 - 在KIMS项目Entities文件夹下创建KmGoods实体类 - 该类继承EntityBase

Blazor实战——Known框架多表增删改查

# 多表增删改查示例 本章介绍学习多张表增、删、改、查功能如何实现,下面以销货出库单作为示例,该业务栏位如下: > **销货出库单栏位** > - 销货单号、销货日期、状态、客户、备注 > > **销货出库单明细栏位** > - 商品编码、商品名称、规格型号、数量、单位、单价、金额 该示例适用于出货

Blazor实战——Known框架快速开始

Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行。 开源地址: https://gitee.com/known/Known ## 1. 安装项目模板并创建新项目 - 打开命令行输入如下命令安装和创建。 ```bash -- 安装模板 dotnet

Blazor实战——Known框架功能配置

本章介绍功能模块菜单、按钮、列表栏位如何配置。 ### 配置菜单模块 - 运行框架,进入“系统管理-模块管理”页面,点击左侧上级模块,点击【新增】按钮 ![输入图片说明](https://foruda.gitee.com/images/1684223573492708502/c0d8e5a8_143

七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接

前言 上一章节我们引入BootstrapBlazor UI组件完成了EasySQLite后台界面的基本架子的搭建,本章节的主要内容是Blazor班级管理页面编写和接口对接。 七天.NET 8 操作 SQLite 入门到实战详细教程 第一天 SQLite 简介 第二天 在 Windows 上配置 SQ

全面的ASP.NET Core Blazor简介和快速入门

前言 因为咱们的MongoDB入门到实战教程Web端准备使用Blazor来作为前端展示UI,本篇文章主要是介绍Blazor是一个怎样的Web UI框架,其优势和特点在哪?并带你快速入门上手ASP.NET Core Blazor(当然这个前提是你要有一定的C#编程基础的情况,假如你完全没有接触过C#的

MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(8)-Ant Design Blazor前端框架搭建

前言 前面的章节我们介绍了一些值得推荐的Blazor UI组件库,通过该篇文章的组件库介绍最终我选用Ant Design Blazor这个UI框架作为ToDoList系统的前端框架。因为在之前的工作中有使用过Ant Design Vue、Ant Design Angular习惯并且喜欢Ant Des

.NET 创建无边框的跨平台应用

# .NET 创建无边框的跨平台应用 在创建了`Photino`应用程序以后我们发现它自带了一个标题栏,并且非常丑,我们现在要做的就是去掉这个很丑的自带标题栏,并且自定义一个更好看的,下面我们将用`Masa Blazor`提供的模板去进行实战。 ## 安装模板 安装`Masa Blazor`提供的`

如何将现有的`Blazor`项目的主题切换写的更好看?

# 如何将现有的`Blazor`项目的主题切换写的更好看? 在现有的系统当中,我们的主题切换会比较生硬,下面我们将基于Masa Blazor实现好看的扩散主题切换的样式效果。 ## 安装MASA.Template ```sh dotnet new install MASA.Template ```

Blazor如何实现类似于微信的Tab切换?

是否有小伙伴在使用tab的时候想进行滑动切换Tab? 并且有滑动左出左进,右出右进的效果 ,本文将讲解怎么在Blazor中去通过滑动切换Tab 本文中的UI组件使用的是MASA Blazor,您也可以是其他的UI框架,这个并不影响实际的运行效果,本文案例是兼容PC和Android的,演示效果是and