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

news2025/1/10 21:07:32

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

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

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框架功能配置

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一个程序最多能占用的内存大小

因为内存资源总是稀缺的&#xff0c;即便在拥有百 G 内存的机器上&#xff0c;我们都可以轻易把内存填满。为了解决这个问题&#xff0c;就需要用到虚拟化技术。 GC 是面试的高频重点知识&#xff0c;同时也是程序员日常开发需要理解的部分。学习 GC 有助于你优化你开发应用的性…

1分钟用上ChatGPT,国内用户福音

众所周知的原因&#xff0c;要想在国内使用ChatGPT&#xff0c;肯定是要“折腾一番”的。 但是对于绝大多数普通小白&#xff0c;有没有比较容易的方法就用上官方的ChatGPT呢&#xff1f; 是可以的 最简单的方法就是调用OpenAI官方的API接口 就可以用“曲线救国”的方式用上…

免费部署你的私人 ChatGPT 网页应用

免费部署你的私人 ChatGPT 网页应用 1、注册Github账号&#xff0c;拷贝仓库 第一步、打开GitHub官网&#xff0c;点击右上角Sign up注册即可 第二步、打开开源项目【Chatgpt-next-web】,点击fork&#xff0c;点击Create fork完成操作 2、选择免费的容器【vercel】或者【r…

AI新晋“顶流”ChatGPT将对财务数字化带来哪些影响?

近期ChatGPT持续火热&#xff0c;2个月内&#xff0c;成为最快达成月活超过1亿的现象级应用。无论是科研人员、技术人员还是普通大众都对此非常关注&#xff0c;并惊讶于它强大的对话能力&#xff0c;ChatGPT也在国内持续霸榜热搜&#xff0c;成为大家茶余饭后的热点话题。毫无…

Mentor Pads中的关键技巧和工作方法

凡是从事电子设计的PCB工程师&#xff0c;都会知道在PCB设计工作中提高效率和质量的重要性&#xff0c;只有合理运用EDA软件的关键技巧和工作方法&#xff0c;可加速PCB设计流程&#xff0c;降低错误率&#xff0c;确保项目的成功交付。本文将以Mentor Pads软件为例&#xff0c…

Qt5安装及组件选择(Qt 5.12)

Qt5安装及组件选择&#xff08;Qt 5.12.0&#xff09; 如下图所示&#xff0c;安装Qt时有选择组件这一步&#xff0c;全部安装未免太占磁盘控件&#xff0c;只需安装我们所需要的组件即可。接下来就分析分析各个组件的作用及含义。 “Qt 5.12.0”节点下面是 Qt 的功能模块&a…

红黑树封装set和map(插入部分)

文章目录 前言1.设计大致思路2.改造封装红黑树1.插入节点2.迭代器的实现 3.map和set的封装1.代码实现2.简单测试 前言 之前我们实现了红黑树的插入的部分&#xff0c;本文主要介绍将之前实现的红黑树封装成map和set。我们是以学习的角度来封装容器&#xff0c;不用非要把库中容…

FPGA采集CameraLink相机Base模式解码输出,附带工程源码和技术支持

目录 1、前言2、CameraLink协议基础3、目前我已有的CameraLink收发工程4、设计方案输入CameraLink相机LVDS视频解码视频缓存视频输出软件配置 5、vivado工程详解6、上板调试验证7、福利&#xff1a;工程代码的获取 1、前言 FPGA实现CameraLink视频编解码目前有两种方案&#x…

华为OD机试真题 Java 实现【分界线】【2023Q1 100分】

一、题目描述 电视剧《分界线》里面有一个片段&#xff0c;男主为了向警察透露案件细节&#xff0c;且不暴露自己&#xff0c;于是将报刊上的字剪切下来&#xff0c;剪拼成匿名信。现在有一名举报人&#xff0c;希望借鉴这种手段&#xff0c;使用英文报刊完成举报操作。 但为…

day15 - 使用图像金字塔进行图像拼接

在我们之前的学习过程中&#xff0c;使用的都是恒定大小的图像&#xff0c;但是在某些情况下&#xff0c;我们需要使用不同分辨率的&#xff08;相同&#xff09;图像。例如&#xff0c;当在图像中搜索某些东西&#xff08;例如人脸&#xff09;时&#xff0c;我们不确定对象将…

vim各模式下常见指令集

vim简介 vim其实就是一款写代码的软件或者编辑器。vs2019能够编写编译调试运行代码&#xff0c;它的功能非常的集成&#xff0c;因此它被称为集成开发环境。但是vim只是编辑&#xff0c;他的核心工作就是文本编写&#xff0c;就是单纯写代码&#xff0c;因此它的功能是不集成的…

chatgpt赋能python:Python概览:了解Python的优势和应用领域

Python概览&#xff1a;了解Python的优势和应用领域 介绍Python Python是一门高级编程语言&#xff0c;由Guido van Rossum在1989年创建&#xff0c;旨在提高开发人员的开发效率和代码质量。Python有着良好的代码可读性和简洁性&#xff0c;因此它已成为全球最受欢迎的编程语…

如何在 IDEA 中生成 Maven 依赖关系图?

文章目录 1、查看依赖关系图2、保存至本地查看3、exclude IDEA提供了查看依赖关系的方式&#xff0c;如下&#xff1a; 1、查看依赖关系图 点击IDEA右侧的maven工具栏&#xff0c;展开maven操作界面。 进入maven操作界面&#xff0c;点击查看maven之间的依赖关系按钮 然后就可…

代码随想录算法训练营day51 | 309. 最佳买卖股票时机含冷冻期,714.买卖股票的最佳时机含手续费,股票问题总结

代码随想录算法训练营day51 | 309. 最佳买卖股票时机含冷冻期&#xff0c;714.买卖股票的最佳时机含手续费&#xff0c;股票问题总结 309. 最佳买卖股票时机含冷冻期解法一&#xff1a;动态规划 714.买卖股票的最佳时机含手续费解法一&#xff1a;动态规划 股票问题总结 309. 最…

矿井水除总氮工艺详解

一、项目概述 项目背景: 1、水资源浪费长期以来&#xff0c;采煤对地下水造成了严重破坏。绝大部分矿井水&#xff0c;被以直排方式&#xff0c;流入河道、田野&#xff0c;这不仅造成水资源的白白浪费&#xff0c;也污染了环境。社会对此反响强烈的同时&#xff0c;煤矿企业也…

BT131-ASEMI代理KY原装双向可控硅BT131

编辑&#xff1a;ll BT131-ASEMI代理KY原装双向可控硅BT131 型号&#xff1a;BT131 品牌&#xff1a;韩景元\KY 封装&#xff1a;TO-92 特性&#xff1a;可控硅 正向电流&#xff1a;1A 反向耐压&#xff1a;600V 触发电压&#xff1a; 0.62&#xff5e;0.8 V 引脚数量…

ES(Elasticsearch)的docker安装部署教程

0、 服务器版本信息 Red Hat 4.8.5-44 CentOS Linux release 7.9.2009 (Core) 1、ES部署 1.1 拉取docker镜像 docker pull elasticsearch:7.10.1拉取成功的镜像&#xff0c;可以使用如下命令查看&#xff1a; docker images 上图2年之前表示该elasticsearch的7.10.1镜像版…

从传统 IT 容灾转向“全栈云容灾”|什么是更适合政企的云

凌晨 3 点&#xff0c;在某医院的自助缴费机前&#xff0c;一位医患家属正愁眉紧锁&#xff0c;手中的医保卡已经刷了无数遍&#xff0c;可次次都提示缴费失败&#xff0c;至亲的手术已经迫在眉睫… 早上 8 点&#xff0c;是上班族在通勤途中打开新闻 app 刷新闻的高峰&#x…

vue3+vite 中使用百度地图【两种方式】

vue3vite项目中使用百度地图 方式一&#xff1a;直接使用百度地图的ak方式二&#xff1a;使用vue-baidu-map-3x插件 方式一&#xff1a;直接使用百度地图的ak 提前准备&#xff1a; 创建一个vite项目申请好的百度地图ak值 百度地图使用&#xff1a; 在创建好的vite项目的入…

python 编译安装与脚本安装

编译安装的一般步骤&#xff1a; 安装依赖&#xff0c;安装依赖的第三方的工具&#xff0c;yum可以解决 编译安装的包中都有&#xff1a;configure 文件 进行预编译&#xff1a;检查你的环境是否合格 ./configure --prefixxxx 生成编译的文件&#xff1a;MakeFile python安装&…