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

news2024/10/7 14:26:22

多表增删改查示例

本章介绍学习多张表增、删、改、查功能如何实现,下面以销货出库单作为示例,该业务栏位如下:

销货出库单栏位

  • 销货单号、销货日期、状态、客户、备注

销货出库单明细栏位

  • 商品编码、商品名称、规格型号、数量、单位、单价、金额

该示例适用于出货明细数量较小情况,单据表头和表体组合查询和提交。

对于出货明细数量较大的情况,建议表头与表体分开查询和提交,表体采用分页查询。

1. 前后端共用

1.1. 创建实体类

  • 在KIMS项目Entities文件夹下创建KmBill和KmBillList实体类
  • 该类继承EntityBase类
  • 属性使用Column特性描述,用于生成页面字段和数据校验
//销货出库单
public class KmBill : EntityBase
{
    [Column("销货单号", "", true, "1", "50")]
    public string? BillNo { get; set; }
    ......
    [Column("客户", "", true, "1", "50")]
    public string? BillDate { get; set; }
    [Column("备注", "", false)]
    public string? Note { get; set; }

    //出货明细,为了降低代码量,扩展实体类,不再创建DTO类
    //此处使用虚属性,ORM映射SQL时忽略该属性
    public virtual List<KmBillList>? Lists { get; set; }
}
//销货出库单明细
public class KmBillList : EntityBase
{
    [Column("销货单ID", "", true, "1", "50")]
    public string? HeadId { get; set; }
    [Column("商品编码", "", true, "1", "50")]
    public string? Code { get; set; }
    [Column("数量", "", false)]
    public decimal? Qty { get; set; }
    [Column("单价", "", false)]
    public decimal? Price { get; set; }
    [Column("金额", "", false)]
    public decimal? Amount { get; set; }

    //如下虚属性用于关联商品资料表查询显示
    public virtual string? Name { get; set; }  //商品名称
    public virtual string? Model { get; set; } //规格型号
    public virtual string? Unit { get; set; }   //计量单位
}

1.2. 创建Client类

  • 在KIMS项目Clients文件夹下创建BillClient类
  • 该类是前后端数据交互接口,继承ClientBase类
  • 该类只需提供分页查询、删除和保存,导入功能由框架统一异步处理
public class BillClient : ClientBase
{
    public BillClient(Context context) : base(context) { }

    public Task<PagingResult<KmBill>> QueryBillsAsync(PagingCriteria criteria) => Context.QueryAsync<KmBill>("Bill/QueryBills", criteria);
    public Task<Result> DeleteBillsAsync(List<KmBill> models) => Context.PostAsync("Bill/DeleteBills", models);
    public Task<KmBill> GetBillAsync(string id) => Context.GetAsync<KmBill>($"Bill/GetBill?id={id}");
    public Task<Result> SaveBillAsync(KmBill model) => Context.PostAsync("Bill/SaveBill", model);
}

2. 前端

2.1. 创建List页面

  • 在KIMS.Razor项目BillData文件夹下创建BillList类
  • 该类是数据列表页面,继承WebGridView<KmBill, BillForm>类
  • 列表页面按钮和栏位在框架模块管理中配置
class BillList : WebGridView<KmBill, BillForm>
{
    protected override Task InitPageAsync()
    {
        //表格栏位格式化显示
        //销货单号链接,点击显示销货单查看表单
        Column(c => c.BillNo).Template((b, r) => b.Link(r.BillNo, Callback(() => View(r))));
        Column(c => c.Status).Template(BillStatusCell);//显示状态标签
        return base.InitPageAsync();
    }

    //分页查询
    protected override Task<PagingResult<KmBill>> OnQueryData(PagingCriteria criteria)
    {
        return Client.Bill.QueryBillsAsync(criteria);
    }
    
    public void New() => ShowForm();//新增按钮方法
    public void DeleteM() => DeleteRows(Client.Bill.DeleteBillsAsync);//批量删除按钮方法
    public void Edit(KmBill row) => ShowForm(row);//编辑操作方法
    public void Delete(KmBill row) => DeleteRow(row, Client.Bill.DeleteBillsAsync);//删除操作方法
}

2.2. 创建Form页面

  • 在KIMS.Razor项目BillData\Forms文件夹下创建BillForm类
  • 该类是数据编辑和查看明细页面,继承WebForm类
[Dialog(980, 580)]//设置对话框大小
class BillForm : WebForm<KmBill>
{
    private KmBill? head;
    //初始化表单,查询表头表体组合数据
    protected override async Task InitFormAsync()
    {
        var model = TModel;
        head = await Client.Bill.GetBillAsync(model.Id);
    }
    //表单布局
    protected override void BuildFields(FieldBuilder<KmBill> builder)
    {
        builder.Hidden(f => f.Id);//隐藏字段
        builder.Table(table =>
        {
            table.ColGroup(11, 25, 11, 25, 11, 17);
            table.Tr(attr =>
            {
                //销货单号不可编辑,自动生成编号
                table.Field<Text>(f => f.BillNo).Enabled(false).Build();
                table.Field<Date>(f => f.BillDate).Build();
                table.Field<Text>(f => f.Status).Enabled(false).Build();
            });
            table.Tr(attr => table.Field<TextArea>(f => f.Note).ColSpan(5).Build());
            builder.FormList<BillListGrid>("商品明细", "", attr =>
            {
                attr.Set(c => c.ReadOnly, ReadOnly)
                    .Set(c => c.Data, head?.Lists);//设置表体数据
            });
        });
    }
    //表单底部按钮
    protected override void BuildButtons(RenderTreeBuilder builder)
    {
        builder.Button(FormButton.Save, Callback(OnSave), !ReadOnly);
        base.BuildButtons(builder);
    }
    //保存按钮方法
    private void OnSave()
    {
        SubmitAsync(data =>
        {
            head?.FillModel(data);//自动填充表单修改数据
            return Client.Bill.SaveBillAsync(head);
        });
    }
}

2.3. 创建表体页面

  • 在KIMS.Razor项目BillData\Forms文件夹下创建BillListGrid类
  • 该类是表体数据编辑表格,继承EditGrid类
//可编辑表体组件
class BillListGrid : EditGrid<KmBillList>
{
    public BillListGrid()
    {
        OrderBy = nameof(KmBillList.ItemNo);//默认排序
        Name = "商品明细";
    }
    //初始化表格栏位
    protected override Task OnInitializedAsync()
    {
        //如下栏位有Edit方法为可编辑栏位,否则不可编辑
        var builder = new ColumnBuilder<KmBillList>();
        //商品库存选择器,弹出对话框查询商品库存,双击选择要出库的商品
        builder.Field(r => r.Code).Edit(new GoodsStock(), OnCodeChanged);
        builder.Field(r => r.Name);//不可编辑
        builder.Field(r => r.Model);
        builder.Field(r => r.Qty).Edit<Number>(OnQtyChanged);//可编辑
        builder.Field(r => r.Unit);
        builder.Field(r => r.Price).Edit<Number>(OnPriceChanged);
        builder.Field(r => r.Amount).IsSum().Edit<Number>(OnAmountChanged);
        builder.Field(r => r.Note).Edit();
        Columns = builder.ToColumns();
        return base.OnInitializedAsync();
    }
    //切换商品编码,自动带出商品信息
    private void OnCodeChanged(KmBillList row, object value)
    {
        var g = value as StockInfo;
        row.Type = g?.Type;
        row.Code = g?.Code;
        row.Name = g?.Name;
        row.Model = g?.Model;
        row.Unit = g?.Unit;
    }
    //更改数量,自动计算金额
    private void OnQtyChanged(KmBillList row, object value)
    {
        var qty = Utils.ConvertTo<decimal>(value);
        row.Amount = Utils.Round(qty * (row.Price ?? 0), 2);
    }
    //更改单价,自动计算金额
    private void OnPriceChanged(KmBillList row, object value)
    {
        var price = Utils.ConvertTo<decimal>(value);
        row.Amount = Utils.Round(price * row.Qty, 2);
    }
    //更改金额,自动计算单价
    private void OnAmountChanged(KmBillList row, object value)
    {
        var amount = Utils.ConvertTo<decimal>(value);
        row.Price = row.Qty == 0 ? 0 : Utils.Round(amount / row.Qty, 2);
    }
}

3. 后端

3.1. 创建Controller类

  • 在KIMS.Core项目Controllers文件夹下创建BillController类
  • 该类为服务端WebApi,继承BaseController类
[Route("[controller]")]
public class BillController : BaseController
{
    private BillService Service => new(Context);

    [HttpPost("[action]")]
    public PagingResult<KmBill> QueryBills([FromBody] PagingCriteria criteria) => Service.QueryBills(criteria);

    [HttpPost("[action]")]
    public Result DeleteBills([FromBody] List<KmBill> models) => Service.DeleteBills(models);

    [HttpGet("[action]")]
    public KmBill GetBill([FromQuery] string id) => Service.GetBill(id);
    
    [HttpPost("[action]")]
    public Result SaveBill([FromBody] KmBill model) => Service.SaveBill(model);
}

3.2. 创建Service类

  • 在KIMS.Core项目Services文件夹下创建BillService类
  • 该类为业务逻辑服务类,继承ServiceBase类
class BillService : ServiceBase
{
    internal BillService(Context context) : base(context) { }
    //分页查询
    internal PagingResult<KmBill> QueryBills(PagingCriteria criteria)
    {
        return BillRepository.QueryBills(Database, criteria);
    }
    //删除数据
    internal Result DeleteBills(List<KmBill> models)
    {
        if (models == null || models.Count == 0)
            return Result.Error(Language.SelectOneAtLeast);

        //此处增加删除数据校验
        return Database.Transaction(Language.Delete, db =>
        {
            foreach (var item in models)
            {
                //删除表体
                BillRepository.DeleteBillLists(db, item.Id);
                db.Delete(item);
            }
        });
    }
    //获取组合数据
    internal KmBill GetBill(string id)
    {
        if (string.IsNullOrEmpty(id))//id为空返回默认值
            return GetDefaultBill();

        var entity = Database.QueryById<KmBill>(id);
        if (entity == null)//为空返回默认值
            entity = GetDefaultBill();
        else//否则组装表体数据返回
            entity.Lists = BillRepository.GetBillLists(Database, id);
        return entity;
    }
    //保存数据
    internal Result SaveBill(KmBill model)
    {
        if (model == null)
            return Result.Error("不能提交空数据!");

        var vr = model.Validate();//验证输入栏位
        if (!vr.IsValid)
            return vr;

        return Database.Transaction(Language.Save, db =>
        {
            if (model.IsNew)
                model.BillNo = GetBillMaxNo(db);

            //清空表体合计数据
            model.TotalAmount = 0;
            model.GoodsName = string.Empty;
            //先删除表体,再插入表体
            BillRepository.DeleteBillLists(db, model.Id);
            if (model.Lists != null && model.Lists.Count > 0)
            {
                var index = 0;
                var lists = new List<KmBillList>();
                foreach (var item in model.Lists)
                {
                    item.HeadId = model.Id;
                    item.ItemNo = ++index;
                    db.Insert(item);
                    lists.Add(item);
                }
                //合计表体数据
                model.TotalAmount = lists.Sum(l => l.Amount);
                model.GoodsName = string.Join(",", lists.Select(l => l.Name));
            }
            db.Save(model);
        }, model);
    }
    //获取默认表头
    private KmBill GetDefaultBill()
    {
        return new KmBill
        {
            BillNo = GetBillMaxNo(Database),
            BillDate = DateTime.Now,
            Status = "暂存",
            Lists = new List<JxBillList>()
        };
    }
    //获取销货单最大编号
    private static string GetBillMaxNo(Database db)
    {
        var prefix = $"S{DateTime.Now:yyyy}";
        var maxNo = BillRepository.GetBillMaxNo(db, prefix);
        if (string.IsNullOrWhiteSpace(maxNo))
            maxNo = $"{prefix}00000";
        return GetMaxFormNo(prefix, maxNo);
    }
}

3.3. 创建Repository类

  • 在KIMS.Core项目Repositories文件夹下创建BillRepository类
  • 该类为数据访问类
class BillRepository
{
    //Head
    //分页查询
    internal static PagingResult<KmBill> QueryBills(Database db, PagingCriteria criteria)
    {
        var sql = "select * from KmBill where CompNo=@CompNo";
        return db.QueryPage<KmBill>(sql, criteria);//查询条件自动绑定
    }
    //获取销货单最大编号
    internal static string GetBillMaxNo(Database db, string prefix)
    {
        var sql = $"select max(BillNo) from KmBill where CompNo=@CompNo and BillNo like '{prefix}%'";
        return db.Scalar<string>(sql, new { db.User.CompNo });
    }
    //List
    //根据表头ID获取表体数据
    internal static List<KmBillList> GetBillLists(Database db, string headId)
    {
        //关联商品资料表查询商品信息
        var sql = "select a.*,b.Name,b.Model,b.Unit from KmBillList a,KmGoods b where a.Code=b.Code and HeadId=@headId";
        return db.QueryList<KmBillList>(sql, new { headId });
    }
    //根据表头ID删除表体数据
    internal static void DeleteBillLists(Database db, string headId)
    {
        var sql = "delete from KmBillList where HeadId=@headId";
        db.Execute(sql, new { headId });
    }
}

4. 运行测试

  • 运行效果如下

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

详解rocketMq通信模块升级构想

本文从开发者的角度深入解析了基于netty的通信模块, 并通过简易扩展实现微服务化通信工具雏形, 适合于想要了解netty通信框架的使用案例, 想了解中间件通信模块设计, 以及微服务通信底层架构的同学。希望此文能给大家带来通信模块架构灵感。 概述 网络通信是很常见的需求&#…

065、故障处理之OMM_TiKV

TiKV Server OOM 对业务的影响 TiKV 上的请求失败造成异常退出region leader重新选举 raft group 开始选举新的 region leader新的region leader 上报信息给PD Server region cache频繁更新 在访问TiDB Server的region cache时&#xff0c;出现TiKV rpc相关报错后台自动进行Ba…

解放程序员,加速创新,缺少的就是一个工具而已

随着科技的不断进步和应用场景的不断扩大&#xff0c;软件开发已经成为当今世界的核心驱动力之一。 然而&#xff0c;传统的软件开发模式往往存在着繁琐的编码过程、复杂的架构设计和漫长的调试周期&#xff0c;使得程序员们难以专注于创新和高难度的研究。 很多程序员上班的时…

基于解析法和遗传算法相结合的配电网多台分布式电源降损配置(Matlab实现)

目录 1 概述 2 数学模型 2.1 问题表述 2.2 DG的最佳位置和容量&#xff08;解析法&#xff09; 2.3 使用 GA 进行最佳功率因数确定和 DG 分配 3 仿真结果与讨论 3.1 33 节点测试配电系统的仿真 3.2 69 节点测试配电系统仿真 4 结论 1 概述 为了使系统网损达到最低值&a…

一分钟学会利用GPT编写爆款标题

&#x1f3c6; 文章目标&#xff1a;学习利用GPT编写爆款标题 &#x1f340; 入门篇&#xff1a;一分钟学会利用GPT编写爆款标题 ✅ 创作者&#xff1a;熊猫Jay ✨ 个人公众号: 熊猫Jay字节之旅 (文末有链接) &#x1f341; 展望&#xff1a;若本篇讲解内容帮助到您&#xff0c…

Web网站性能压测实践 | 数据平台

一、 为什么要做压测&#xff1f; 首先解释下为什么要做性能压测&#xff1a;根据 Amazon 统计&#xff0c;每慢 100 毫秒&#xff0c;交易额下降 1%。这个统计数据为大家敲响了警钟&#xff0c;也客观说明了性能压测对于企业应用的重要性。从具体的OKR上讲&#xff0c;我们希望…

常见排序算法-Python实现

python 排序 算法 1.二分法 ​ python 32行 #codingutf-8 def binary_search(input_array, value): """Your code goes here.""" length len(input_array) left 0 right length-1 if length 1: return 0 if value input_value[0] els…

Linux 多线程并发Socket服务端的实现( 11 ) -【Linux通信架构系列 】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everythi…

3秒快速打开 jupyter notebook

利用 bat 脚本&#xff0c;实现一键打开 minconda 特点&#xff1a; 1、可指定 python 环境 2、可指定 jupyter 目录 一、配置环境 minconda 可以搭建不同的 python 环境&#xff0c;所以我们需要找到 minconda 安装目录&#xff0c;把对应目录添加到电脑环境 PATH 中&#…

prepros.crack.7.8.5 by Xacker

您友好的 Web 开发伙伴 Prepros 编译您的文件&#xff0c;转译您的 JavaScript&#xff0c;重新加载您的浏览器&#xff0c;并使开发和测试您的网站变得非常容易&#xff0c;这样您就可以专注于使它们完美。 适用于 Windows、macOS 和 Linux 试用版包括所有 Prepros 功能。 编…

【数据结构】树状数组和线段树

树状数组和线段树 下文为自己的题解总结&#xff0c;参考其他题解写成&#xff0c;取其精华&#xff0c;做以笔记&#xff0c;如有描述不清楚或者错误麻烦指正&#xff0c;不胜感激&#xff0c;不喜勿喷&#xff01; 树状数组 需求&#xff1a; 能够快速计算区间和保证在修改…

了解 MySQL 中 MVCC 的原理

点击上方↑“追梦 Java”关注&#xff0c;一起追梦&#xff01; 要解决读一致性的问题&#xff0c;保证一个事务中前后两次读取数据结果一致&#xff0c;还有一种 MVCC 的方式&#xff0c;又叫多版本的并发控制&#xff08;Multi Version Concurrency Control&#xff09;。 MV…

Flink状态的理解

Flink是一个带状态的数据处理系统&#xff1b;系统在处理数据的过程中&#xff0c;各算子所记录的状态会随着数据的处理而不断变化&#xff1b; 1. 状态 所谓状态State&#xff0c;一般指一个具体的 Task 的状态&#xff0c;即线程处理过程中需要保存的历史数据或历史累计数据…

SpringBoot集成Redis的环境部署以及操作Redis

文章目录 Spring Boot 集成Redis1.环境配置 redis连接配置信息不写默认wei6379&#xff0c;数据库为02.操作Redis2.1 代码形式操作Redis2.2 使用注解方式操作Redis Spring Boot 集成Redis 1.环境配置 添加redis依赖 在老项目添加&#xff0c;可以在pom.xml文件直接添加&#…

DAMO-YOLO 论文学习

1. 解决了什么问题&#xff1f; 工业界追求高性能、低延迟的目标检测算法&#xff0c;研究人员于是聚焦于单阶段目标检测&#xff0c;探索高效的网络结构和训练策略。YOLOv5/v6/v7、YOLOX 和 PP-YOLOE 在 COCO 数据集上实现了不错的精度-速度平衡&#xff0c;得到广泛应用&…

超标量处理器寄存器rename

1.相关性介绍 在CPU中&#xff0c;一段程序会被编译成一连串的汇编指令&#xff0c;指令与指令之间可能会具有相关性&#xff08;dependency&#xff09;。所谓相关性&#xff0c;即一条指令的执行会依赖于另一条指令的结果&#xff0c;相关性可以分为&#xff1a;① 数据相关性…

el-table树形表格实现复选框多选效果

2023.7.26今天我学习了如何使用树形表格的时候进行复选框的多选效果。 当我们使用树形结构表格需要进行多选功能操作的时候会发现点击全选的时候&#xff0c;只有一级表格数据会被选中&#xff0c;问题如图&#xff1a; 我们需要实现的是点击全选的不管是几级表格数据都可以被…

ElasticSearch之IK分词器安装以及使用介绍

文章目录 一、IK 分词器简介1. 支持细粒度分词&#xff1a;2. 支持多种分词模式&#xff1a;3. 支持自定义词典&#xff1a;4. 支持拼音分词&#xff1a;5. 易于集成和使用&#xff1a; 二、安装步骤1、下载 IK 分词器插件&#xff1a;2、安装 IK 分词器插件&#xff1a;3. 安装…

各种知名游戏的技术分析

介绍一个GitHub&#xff0c;里面包括了市面上的各种游戏的技术分析&#xff0c;包括渲染管线、工作流、技术文章等等&#xff0c;在做某个类型的游戏的时候&#xff0c;可以针对某个游戏去进行技术参考&#xff0c;特别实用。 GitHub - OTFCG/Awesome-Game-Analysis: a compre…

C++设计模式之模板方法、策略模式、观察者模式

面向对象设计模式是”好的面向对象设计“&#xff0c;所谓”好的面向对象设计“指的是可以满足”应对变化&#xff0c;提高复用“的设计。 现代软件设计的特征是”需求的频繁变化“。设计模式的要点是”寻求变化点&#xff0c;然后在变化点处应用设计模式&#xff0c;从而更好地…