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

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

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. 相关资料

Blazor实战——Known框架增删改查导的更多相关文章

  1. 【讲义提纲】以一个实战新闻cms增删改查demo为例,给学院国创队伍培训php

    PHP实战基础——以一个新闻cms的增删改查为例 一.        环境配置 二.        数据库创建 三.        增删改查demo 连接数据库 <?php $link=mysq ...

  2. jeesite应用实战(数据增删改查),认真读完后10分钟就能开发一个模块

    jeesite配置指南(官方文档有坑,我把坑填了!)这篇文章里,我主要把jeesite官方给出的帮助文档的坑填了,按照里面的方法可以搭建起来jeesite的站点.系统可以运行以后,就可以进入开发模块了 ...

  3. Laravel框架——增删改查

    增: //save返回true false $res = new member(); res->username = 'a'; $res->password = 'b'; dd($res- ...

  4. yii2框架增删改查案例

    //解除绑定蓝牙 //http://www.520m.com.cn/api/pet/remove-binding?healthy_id=72&pet_id=100477&access- ...

  5. Hibernate框架增删改查测试类归为一个类

    package cn.happy.test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org ...

  6. Hibernate框架增删改查

    package cn.happy.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org ...

  7. YII框架增删改查常用语句

    //实例化db $db = new \yii\db\Query(); //插入 $db->createCommand()->insert('user', [ 'name' => 't ...

  8. idea+Maven+SSM框架增删改查

    完整项目结构 1.maven配置文件pom.xml <?xml version="1.0" encoding="UTF-8"?> <!-- L ...

  9. Mybatis框架增删改查

    一.recourses中核心配置文件mybatis-config.xml 二. recourse中jdbc.properties 三.entity实体类 四.ISmbmsUserDao层 五.ISmb ...

  10. MyBatis框架,增删改查

    一.recourses中核心配置文件mybatis-config.xml 二. recourse中jdbc.properties 三.entity实体类 四.Dao层 五.ISmbmsUserDao. ...

随机推荐

  1. NodeJS V8引擎的内存和垃圾回收器(GC)

    一.为什么需要GC 程序应用运行需要使用内存,其中内存的两个分区是我们常常会讨论的概念:栈区和堆区. 栈区是线性的队列,随着函数运行结束自动释放的,而堆区是自由的动态内存空间.堆内存是手动分配释放或者 ...

  2. CISCN2021东北赛区-Maple_root-WriteUp

    参赛队员: x0r,b477eRy,f1oat 总结 最终成绩:3627 最终排名:13 一血数量:3 本次比赛前期一切顺利,后期感觉被py爆了,结果名次就拉了下来,整体题目全部都偏向MISC,打的很 ...

  3. 2023年php面试题

    Php面试题 1.isset和empty的区别? Isset测试变量是否被赋值,如果这个变量没被赋值,则返回false,empty是判断变量是否为空,当赋值为0,null,'',返回true为真.他们 ...

  4. golang pprof 监控系列(3) —— memory,block,mutex 统计原理

    golang pprof 监控系列(3) -- memory,block,mutex 统计原理 大家好,我是蓝胖子. 在上一篇文章 golang pprof监控系列(2) -- memory,bloc ...

  5. Jmix 如何将外部数据直接显示在界面?

    企业级应用中,通常一个业务系统并不是孤立存在的,而是需要与企业.部门或者是外部的已有系统进行集成.一般而言,系统集成的数据和接口交互方式通常有以下几种: 文件传输:通过文件传输的方式将数据传递给其他系 ...

  6. Django笔记二十一之使用原生SQL查询数据库

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十一之使用原生SQL查询数据库 Django 提供了两种方式来执行原生 SQL 代码. 一种是使用 raw() 函数,一种是 使用 co ...

  7. 全网最详细中英文ChatGPT-GPT-4示例文档-智能多功能学习机从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)

    目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...

  8. fio性能测试-环境搭建,功能介绍,测试讲解

    fio性能测试-环境搭建,功能介绍,测试讲解 Fio介绍:FIO(Flexible I/O Tester)是一个用于测试磁盘.文件系统.块设备和网络设备性能的工具.它可以模拟不同类型的I/O负载,包括 ...

  9. 没有杯子的世界:OOP设计思想的应用实践

    最近看到一个有趣的问题:Person类具有Hand,Hand可以操作杯子Cup,但是在石器时代是没有杯子的,这个问题用编程怎么解决? 简单代码实现 我们先用简单代码实现原问题: @Data publi ...

  10. 深入理解python虚拟机:程序执行的载体——栈帧

    深入理解python虚拟机:程序执行的载体--栈帧 栈帧(Stack Frame)是 Python 虚拟机中程序执行的载体之一,也是 Python 中的一种执行上下文.每当 Python 执行一个函数 ...