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类
  • 该类是前后端数据交互接口,继承BaseClient类
  • 该类只需提供分页查询、删除和保存,导入功能由框架统一异步处理
public class GoodsClient : BaseClient
{
    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() => OnDeleteM(Client.Goods.DeleteGoodsesAsync);//批量删除按钮方法
    public void Edit(KmGoods row) => ShowForm(row);//编辑操作方法
    public void Delete(KmGoods row) => OnDelete(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 =>
            {
                builder.Field<Text>(f => f.Code).Enabled(TModel.IsNew).Build();//编码,编辑时灰显
                builder.Field<Text>(f => f.Name).Build();
            });
            table.Tr(attr =>
            {
                builder.Field<Select>(f => f.Type).Set(f => f.Codes, AppDictionary.GoodsType).Build();//下拉框
                builder.Field<Select>(f => f.Unit).Set(f => f.Codes, AppDictionary.GoodsUnit).Build();
            });
            table.Tr(attr => builder.Field<Text>(f => f.Model).ColSpan(3).Build());
            table.Tr(attr => builder.Field<RadioList>(f => f.TaxRate).ColSpan(3).Set(f => f.Items, AppDictionary.TaxRates).Build());//单选按钮
            table.Tr(attr =>
            {
                builder.Field<Number>(f => f.MinStock).Build();//数值框
                builder.Field<Number>(f => f.MaxStock).Build();
            });
            table.Tr(attr => builder.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. 相关资料

  • 基于C#和Blazor开发的前后端分离框架
  • Blazor实战——Known框架快速开始
  • Blazor实战——Known框架功能配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/23587.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++】类和对象的应用案例 2 - 点和圆的关系

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01;时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、分析 3、示例代码 1 4、代码优化 4.1、point.h 4.2、point.c 4.3、circle.h 4.4、circle.c 4.4、main.c …

Netty 源码分析系列(十八)一行简单的writeAndFlush都做了哪些事?

文章目录 前言源码分析ctx.writeAndFlush 的逻辑writeAndFlush 源码ChannelOutBoundBuff 类addMessage 方法addFlush 方法AbstractNioByteChannel 类 小结 前言 对于使用netty的小伙伴来说&#xff0c;我们想通过服务端往客户端发送数据&#xff0c;通常我们会调用ctx.writeAn…

实时聊天组合功能,你了解吗?

你有兴趣安装实时聊天组合功能吗&#xff1f;如果您选择了SaleSmartly&#xff08;ss客服&#xff09;&#xff0c;您的实时聊天插件可以不仅仅只是聊天通道&#xff0c;还可以有各种各样的功能&#xff0c;你不需要包含每一个功能&#xff0c;正所谓「宁缺勿滥」&#xff0c;功…

再获认可!腾讯连续三年被Gartner列为CWPP供应商之一

随着云的快速发展&#xff0c;企业的工作负载已经从服务器发展到虚拟机、容器、serverless等&#xff0c;部署的模式也日益复杂&#xff0c;包括公有云、混合云和多云等。在此背景下&#xff0c;传统的主机安全防护已无法满足需求&#xff0c;CWPP&#xff08;云工作负载保护平…

C#,码海拾贝(23)——求解“复系数线性方程组“的“全选主元高斯消去法“之C#源代码,《C#数值计算算法编程》源代码升级改进版

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary&g…

day20 - 绘制物体的运动轨迹

在我们平常做目标检测或者目标追踪时&#xff0c;经常要画出目标的轨迹图。绘制轨迹图的一种方法就是利用光流估计来进行绘制。 本期我们主要来介绍视频中光流估计的使用和效果&#xff0c;利用光流估计来绘制运动轨迹。 完成本期内容&#xff0c;你可以&#xff1a; 掌握视…

网站部署与上线(1)虚拟机

文章目录 .1 虚拟机简介2 虚拟机的安装 本章将搭建实例的生产环境&#xff0c;将所有的代码搭建在一台Linux服务器中&#xff0c;并且测试其能否正常运行。 使用远程服务器进行连接&#xff1b; 基本的Linux命令&#xff1b; 使用Nginx搭建Node.js服务器&#xff1b; 在服务器端…

一、预约挂号详情

文章目录 一、预约挂号详情1、需求分析 2、api接口2.1 添加service接口2.2 添加service接口实现2.2.1 在ScheduleServiceImpl类实现接口2.2.2 在获取科室信息 2.3 添加controller方法 3、前端3.1封装api请求3.2 页面展示 二、预约确认1、api接口1.1 添加service接口1.2 添加con…

通过python采集整站lazada商品列表数据,支持多站点

要采集整站lazada商品列表数据&#xff0c;需要先了解lazada网站的结构和数据源。Lazada是东南亚最大的电商平台之一&#xff0c;提供各种商品和服务。Lazada的数据源主要分为两种&#xff1a;HTML和API。 方法1&#xff1a;采集HTML数据 步骤1&#xff1a;确定采集目标 首先…

一、CNNs网络架构-基础网络架构(LeNet、AlexNet、ZFNet)

目录 1.LeNet 2.AlexNet 2.1 激活函数&#xff1a;ReLU 2.2 随机失活&#xff1a;Droupout 2.3 数据扩充&#xff1a;Data augmentation 2.4 局部响应归一化&#xff1a;LRN 2.5 多GPU训练 2.6 论文 3.ZFNet 3.1 网络架构 3.2 反卷积 3.3 卷积可视化 3.4 ZFNet改…

Java的Arrays类的sort()方法(41)

目录 sort&#xff08;&#xff09;方法 1.sort&#xff08;&#xff09;方法的格式 2.使用sort&#xff08;&#xff09;方法时要导入的类 3.作用 4.作用的对象 5.注意 6.代码及结果 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;结果 sort&#xff08;&…

【Netty】字节缓冲区 ByteBuf (六)(上)

文章目录 前言一、ByteBuf类二、ByteBuffer 实现原理2.1 ByteBuffer 写入模式2.2 ByteBuffer 读取模式2.3 ByteBuffer 写入模式切换为读取模式2.4 clear() 与 compact() 方法2.5 ByteBuffer 使用案例 总结 前言 回顾Netty系列文章&#xff1a; Netty 概述&#xff08;一&…

亏损?盈利?禾赛科技Q1财报背后的激光雷达赛道「现实」

随着禾赛科技在去年登陆美股&#xff0c;作为全球为数不多已经开始前装量产交付的激光雷达上市公司&#xff0c;财务数据的变化&#xff0c;也在一定程度上反映了行业的真实状况。 根据禾赛科技最新发布的今年一季度财报显示&#xff0c;公司季度净营收为4.3亿元&#xff08;人…

day13 - 对指纹图片进行噪声消除

在指纹识别的过程中&#xff0c;指纹图片通常都是现场采集的&#xff0c;受环境的影响会有产生很多的噪声点&#xff0c;如果直接使用&#xff0c;会对指纹的识别产生很大的影响&#xff0c;而指纹识别的应用场景又都是一些比较严肃不容有错的场合&#xff0c;所以去除噪声又不…

python+vue空巢老人网上药店购药系统9h2k5

本空巢老人购药系统主要包括三大功能模块&#xff0c;即用户功能模块、家属功能模块和管理员功能模块。 &#xff08;1&#xff09;管理员模块&#xff1a;系统中的核心用户是管理员&#xff0c;管理员登录后&#xff0c;通过管理员功能来管理后台系统。主要功能有&#xff1a;…

【实验】SegViT: Semantic Segmentation with Plain Vision Transformers

想要借鉴SegViT官方模型源码部署到本地自己代码文件中 1. 环境配置 官网要求安装mmcv-full1.4.4和mmsegmentation0.24.0 在这之前记得把mmcv和mmsegmentation原来版本卸载 pip uninstall mmcv pip uninstall mmcv-full pip uninstall mmsegmentation安装mmcv 其中&#xff…

旋翼无人机常用仿真工具

四旋翼常用仿真工具 rviz&#xff1a; 简单的质点&#xff08;也可以加上动力学姿态&#xff09;&#xff0c;用urdf模型在rviz中显示无人机和飞行轨迹、地图等。配合ROS代码使用&#xff0c;轻量化适合多机。典型的比如浙大ego-planner的仿真&#xff1a; https://github.c…

Java面试知识点(全)-分布式算法- ZAB算法

Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 研究zookeeper时&#xff0c;必须要了解zk的选举和集群间个副本间的数据一致性。 什么是 ZAB 协议&#xff1f; ZAB 协议介绍 ZAB 协议全称&#xf…

树和二叉树

树 逻辑表示方法 树形表示法 文氏图表示法 凹入表示法 括号表示法 性质 树的结点数等于所有结点的度加一 度为m的树中第i层最多有m的(i-1)次方个结点 高度为h的m次树最多的节点数&#xff08;等比数列公式求和&am…

【数据结构】什么是堆,如何使用无序数组生成一个堆?

文章目录 一、堆的概念及其介绍二、如何使用无序序列构建一个堆&#xff1f;三、C语言实现堆的基本操作结构体创建与销毁获取堆顶数据与个数及堆的判空堆的插入与删除 源代码分享 一、堆的概念及其介绍 堆(Heap)是计算机科学中一类特殊的数据结构的统称&#xff0c;堆通常是一…