Blazor實(shí)戰(zhàn)——Known框架多表增刪改查

多表增刪改查示例

本章介紹學(xué)習(xí)多張表增椅挣、刪咒程、改、查功能如何實(shí)現(xiàn)袖牙,下面以銷貨出庫(kù)單作為示例肯尺,該業(yè)務(wù)欄位如下:

銷貨出庫(kù)單欄位

  • 銷貨單號(hào)、銷貨日期躯枢、狀態(tài)则吟、客戶、備注

銷貨出庫(kù)單明細(xì)欄位

  • 商品編碼锄蹂、商品名稱氓仲、規(guī)格型號(hào)、數(shù)量得糜、單位敬扛、單價(jià)、金額

該示例適用于出貨明細(xì)數(shù)量較小情況朝抖,單據(jù)表頭和表體組合查詢和提交啥箭。

對(duì)于出貨明細(xì)數(shù)量較大的情況,建議表頭與表體分開查詢和提交治宣,表體采用分頁(yè)查詢急侥。

1. 前后端共用

1.1. 創(chuàng)建實(shí)體類

  • 在KIMS項(xiàng)目Entities文件夾下創(chuàng)建KmBill和KmBillList實(shí)體類
  • 該類繼承EntityBase類
  • 屬性使用Column特性描述砌滞,用于生成頁(yè)面字段和數(shù)據(jù)校驗(yàn)
//銷貨出庫(kù)單
public class KmBill : EntityBase
{
    [Column("銷貨單號(hào)", "", true, "1", "50")]
    public string? BillNo { get; set; }
    ......
    [Column("客戶", "", true, "1", "50")]
    public string? BillDate { get; set; }
    [Column("備注", "", false)]
    public string? Note { get; set; }

    //出貨明細(xì),為了降低代碼量坏怪,擴(kuò)展實(shí)體類贝润,不再創(chuàng)建DTO類
    //此處使用虛屬性,ORM映射SQL時(shí)忽略該屬性
    public virtual List<KmBillList>? Lists { get; set; }
}
//銷貨出庫(kù)單明細(xì)
public class KmBillList : EntityBase
{
    [Column("銷貨單ID", "", true, "1", "50")]
    public string? HeadId { get; set; }
    [Column("商品編碼", "", true, "1", "50")]
    public string? Code { get; set; }
    [Column("數(shù)量", "", false)]
    public decimal? Qty { get; set; }
    [Column("單價(jià)", "", false)]
    public decimal? Price { get; set; }
    [Column("金額", "", false)]
    public decimal? Amount { get; set; }

    //如下虛屬性用于關(guān)聯(lián)商品資料表查詢顯示
    public virtual string? Name { get; set; }  //商品名稱
    public virtual string? Model { get; set; } //規(guī)格型號(hào)
    public virtual string? Unit { get; set; }   //計(jì)量單位
}

1.2. 創(chuàng)建Client類

  • 在KIMS項(xiàng)目Clients文件夾下創(chuàng)建BillClient類
  • 該類是前后端數(shù)據(jù)交互接口铝宵,繼承ClientBase類
  • 該類只需提供分頁(yè)查詢打掘、刪除和保存,導(dǎo)入功能由框架統(tǒng)一異步處理
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. 創(chuàng)建List頁(yè)面

  • 在KIMS.Razor項(xiàng)目BillData文件夾下創(chuàng)建BillList類
  • 該類是數(shù)據(jù)列表頁(yè)面鹏秋,繼承WebGridView<KmBill, BillForm>類
  • 列表頁(yè)面按鈕和欄位在框架模塊管理中配置
class BillList : WebGridView<KmBill, BillForm>
{
    protected override Task InitPageAsync()
    {
        //表格欄位格式化顯示
        //銷貨單號(hào)鏈接尊蚁,點(diǎn)擊顯示銷貨單查看表單
        Column(c => c.BillNo).Template((b, r) => b.Link(r.BillNo, Callback(() => View(r))));
        Column(c => c.Status).Template(BillStatusCell);//顯示狀態(tài)標(biāo)簽
        return base.InitPageAsync();
    }

    //分頁(yè)查詢
    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. 創(chuàng)建Form頁(yè)面

  • 在KIMS.Razor項(xiàng)目BillData\Forms文件夾下創(chuàng)建BillForm類
  • 該類是數(shù)據(jù)編輯和查看明細(xì)頁(yè)面,繼承WebForm<KmBill>類
[Dialog(980, 580)]//設(shè)置對(duì)話框大小
class BillForm : WebForm<KmBill>
{
    private KmBill? head;
    //初始化表單拼岳,查詢表頭表體組合數(shù)據(jù)
    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 =>
            {
                //銷貨單號(hào)不可編輯枝誊,自動(dòng)生成編號(hào)
                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>("商品明細(xì)", "", attr =>
            {
                attr.Set(c => c.ReadOnly, ReadOnly)
                    .Set(c => c.Data, head?.Lists);//設(shè)置表體數(shù)據(jù)
            });
        });
    }
    //表單底部按鈕
    protected override void BuildButtons(RenderTreeBuilder builder)
    {
        builder.Button(FormButton.Save, Callback(OnSave), !ReadOnly);
        base.BuildButtons(builder);
    }
    //保存按鈕方法
    private void OnSave()
    {
        SubmitAsync(data =>
        {
            head?.FillModel(data);//自動(dòng)填充表單修改數(shù)據(jù)
            return Client.Bill.SaveBillAsync(head);
        });
    }
}

2.3. 創(chuàng)建表體頁(yè)面

  • 在KIMS.Razor項(xiàng)目BillData\Forms文件夾下創(chuàng)建BillListGrid類
  • 該類是表體數(shù)據(jù)編輯表格,繼承EditGrid<KmBillList>類
//可編輯表體組件
class BillListGrid : EditGrid<KmBillList>
{
    public BillListGrid()
    {
        OrderBy = nameof(KmBillList.ItemNo);//默認(rèn)排序
        Name = "商品明細(xì)";
    }
    //初始化表格欄位
    protected override Task OnInitializedAsync()
    {
        //如下欄位有Edit方法為可編輯欄位惜纸,否則不可編輯
        var builder = new ColumnBuilder<KmBillList>();
        //商品庫(kù)存選擇器叶撒,彈出對(duì)話框查詢商品庫(kù)存,雙擊選擇要出庫(kù)的商品
        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();
    }
    //切換商品編碼耐版,自動(dòng)帶出商品信息
    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;
    }
    //更改數(shù)量祠够,自動(dòng)計(jì)算金額
    private void OnQtyChanged(KmBillList row, object value)
    {
        var qty = Utils.ConvertTo<decimal>(value);
        row.Amount = Utils.Round(qty * (row.Price ?? 0), 2);
    }
    //更改單價(jià),自動(dòng)計(jì)算金額
    private void OnPriceChanged(KmBillList row, object value)
    {
        var price = Utils.ConvertTo<decimal>(value);
        row.Amount = Utils.Round(price * row.Qty, 2);
    }
    //更改金額粪牲,自動(dòng)計(jì)算單價(jià)
    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. 創(chuàng)建Controller類

  • 在KIMS.Core項(xiàng)目Controllers文件夾下創(chuàng)建BillController類
  • 該類為服務(wù)端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. 創(chuàng)建Service類

  • 在KIMS.Core項(xiàng)目Services文件夾下創(chuàng)建BillService類
  • 該類為業(yè)務(wù)邏輯服務(wù)類,繼承ServiceBase類
class BillService : ServiceBase
{
    internal BillService(Context context) : base(context) { }
    //分頁(yè)查詢
    internal PagingResult<KmBill> QueryBills(PagingCriteria criteria)
    {
        return BillRepository.QueryBills(Database, criteria);
    }
    //刪除數(shù)據(jù)
    internal Result DeleteBills(List<KmBill> models)
    {
        if (models == null || models.Count == 0)
            return Result.Error(Language.SelectOneAtLeast);

        //此處增加刪除數(shù)據(jù)校驗(yàn)
        return Database.Transaction(Language.Delete, db =>
        {
            foreach (var item in models)
            {
                //刪除表體
                BillRepository.DeleteBillLists(db, item.Id);
                db.Delete(item);
            }
        });
    }
    //獲取組合數(shù)據(jù)
    internal KmBill GetBill(string id)
    {
        if (string.IsNullOrEmpty(id))//id為空返回默認(rèn)值
            return GetDefaultBill();

        var entity = Database.QueryById<KmBill>(id);
        if (entity == null)//為空返回默認(rèn)值
            entity = GetDefaultBill();
        else//否則組裝表體數(shù)據(jù)返回
            entity.Lists = BillRepository.GetBillLists(Database, id);
        return entity;
    }
    //保存數(shù)據(jù)
    internal Result SaveBill(KmBill model)
    {
        if (model == null)
            return Result.Error("不能提交空數(shù)據(jù)腺阳!");

        var vr = model.Validate();//驗(yàn)證輸入欄位
        if (!vr.IsValid)
            return vr;

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

            //清空表體合計(jì)數(shù)據(jù)
            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);
                }
                //合計(jì)表體數(shù)據(jù)
                model.TotalAmount = lists.Sum(l => l.Amount);
                model.GoodsName = string.Join(",", lists.Select(l => l.Name));
            }
            db.Save(model);
        }, model);
    }
    //獲取默認(rèn)表頭
    private KmBill GetDefaultBill()
    {
        return new KmBill
        {
            BillNo = GetBillMaxNo(Database),
            BillDate = DateTime.Now,
            Status = "暫存",
            Lists = new List<JxBillList>()
        };
    }
    //獲取銷貨單最大編號(hào)
    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. 創(chuàng)建Repository類

  • 在KIMS.Core項(xiàng)目Repositories文件夾下創(chuàng)建BillRepository類
  • 該類為數(shù)據(jù)訪問(wèn)類
class BillRepository
{
    //Head
    //分頁(yè)查詢
    internal static PagingResult<KmBill> QueryBills(Database db, PagingCriteria criteria)
    {
        var sql = "select * from KmBill where CompNo=@CompNo";
        return db.QueryPage<KmBill>(sql, criteria);//查詢條件自動(dòng)綁定
    }
    //獲取銷貨單最大編號(hào)
    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
    //根據(jù)表頭ID獲取表體數(shù)據(jù)
    internal static List<KmBillList> GetBillLists(Database db, string headId)
    {
        //關(guān)聯(lián)商品資料表查詢商品信息
        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 });
    }
    //根據(jù)表頭ID刪除表體數(shù)據(jù)
    internal static void DeleteBillLists(Database db, string headId)
    {
        var sql = "delete from KmBillList where HeadId=@headId";
        db.Execute(sql, new { headId });
    }
}

4. 運(yùn)行測(cè)試

  • 運(yùn)行效果如下
image.png

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市亭引,隨后出現(xiàn)的幾起案子绎速,更是在濱河造成了極大的恐慌,老刑警劉巖焙蚓,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纹冤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡购公,警方通過(guò)查閱死者的電腦和手機(jī)萌京,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宏浩,“玉大人知残,你說(shuō)我怎么就攤上這事』婷疲” “怎么了橡庞?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵较坛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我扒最,道長(zhǎng)丑勤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任吧趣,我火速辦了婚禮法竞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘强挫。我一直安慰自己岔霸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布俯渤。 她就那樣靜靜地躺著呆细,像睡著了一般。 火紅的嫁衣襯著肌膚如雪八匠。 梳的紋絲不亂的頭發(fā)上絮爷,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音梨树,去河邊找鬼坑夯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛抡四,可吹牛的內(nèi)容都是我干的柜蜈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼指巡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼淑履!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起藻雪,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鳖谈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后阔涉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捷绒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年瑰排,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片暖侨。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡椭住,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出字逗,到底是詐尸還是另有隱情京郑,我是刑警寧澤宅广,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站些举,受9級(jí)特大地震影響跟狱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜户魏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一驶臊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叼丑,春花似錦关翎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至星立,卻和暖如春爽茴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贞铣。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工闹啦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辕坝。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓窍奋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親酱畅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子琳袄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容