AppBox实战: 如何实现一对多表单的增删改查

  本篇通过完整示例介绍如何实现一对多关系表单的相应服务及视图。

一、准备数据结构

  示例所采用的数据结构为“物资需求”一对多“物资清单”,通过IDE的实体设计器如下所示:

1. 物资(DonateItem)

  主键为Id(Guid)
AppBox实战: 如何实现一对多表单的增删改查_第1张图片

2. 物资需求(Requirement)

  主键为Id(Guid)
AppBox实战: 如何实现一对多表单的增删改查_第2张图片

3. 物资清单(RequireItem)

  主键为Req(Requirement)+Item(DonateItem)
AppBox实战: 如何实现一对多表单的增删改查_第3张图片

添加实体成员时选择类型EntityRef(一对一)或EntitySet(一对多)可设置相应的导航属性
AppBox实战: 如何实现一对多表单的增删改查_第4张图片

二、实现需求列表显示功能

1. 新建RequirementService服务实现加载列表数据

using System;
using System.Threading.Tasks;

namespace dns.ServiceLogic
{
    public class RequirementService
    {
        /// 
        /// 分页加载需求记录
        /// 
        public async Task Load(int pageIndex, int pageSize)
        {
            var q = new SqlQuery();
            q.Skip(pageSize * pageIndex).Take(pageSize);
            q.OrderByDesc(t => t.Time);
            return await q.ToListAsync();
        }
    }
}

2. 新建RequireList视图

2.1 模版

新建 修改 删除 刷新

2.2 脚本

@Component
export default class RequireList extends Vue {
    items = [] //需求列表
    loading = false
    pageIndex = 0
    pageSize = 20

    load() {
        this.loading = true
        dns.Services.RequirementService.Load(this.pageIndex, this.pageSize).then(res => {
            this.$set(this, 'items', $runtime.parseEntity(res))
            this.loading = false
        }).catch(err => {
            this.loading = false
            this.$message.error('加载需求列表失败: ' + err)
        })
    }

    mounted() {
        this.load()
    }
}

系统函数$runtime.parseEntity()用于将调用服务的结果内的数据对象如{ID:xxx,Name:xxx}转换为前端的Entity对象,另如果调用服务的结果只用作展示可以不用转换。

2.3 预览

  点击“Preview”预览,当然当前没有任何数据。
AppBox实战: 如何实现一对多表单的增删改查_第5张图片

三、实现新建需求功能

3.1 修改RequirementService实现保存方法

/// 
/// 保存需求
/// 
public async Task Save(Entities.Requirement req)
{
    //TODO:验证
    using var conn = await DataStore.Default.OpenConnectionAsync();
    using var txn = conn.BeginTransaction();
    //保存主记录
    await DataStore.Default.SaveAsync(req, txn);
    //保存子记录
    foreach (var item in req.Items)
    {
        await DataStore.Default.SaveAsync(item, txn);
    }
    //处理已删除的物资
    if (req.PersistentState != PersistentState.Detached)
    {
        foreach (var item in req.Items.DeletedItems)
        {
            await DataStore.Default.DeleteAsync(item, txn);
        }
    }
    //递交事务
    txn.Commit();
}

3.2 新建ItemService实现用于绑定的加载方法

using System;
using System.Threading.Tasks;

namespace dns.ServiceLogic
{
    public class ItemService
    {
        /// 
        /// 加载用于前端选择绑定
        /// 
        public async Task LoadForSelect()
        {
            var q = new SqlQuery();
            return await q.ToListAsync(t => new { t.Id, t.Name, t.Spec });
        }
    }
}

3.3 新建RequireView编辑视图

3.3.1 模版

需求方: 时间:
联系人: 电话:
地址: 邮编:

保存

3.3.2 脚本

@Component
export default class RequireView extends Vue {
    @Prop({ type: Object, default: {} }) req: dns.Entities.Requirement

    optItems = [] //物资选择列表

    /** 用于Table绑定,过滤已标为删除的 */
    get items() {
        if (this.req.Items) {
            return this.req.Items.filter(t => !t.isDeleted())
        }
        return null
    }

    /** 加载用于绑定下拉选择的物资列表 */
    loadItems() {
        dns.Services.ItemService.LoadForSelect().then(res => {
            this.$set(this, 'optItems', res)
        }).catch(err => {
            this.$message.error("加载物资列表失败: " + err)
        })
    }

    /** 添加物资 */
    onAddItem() {
        if (!this.req.Items) { //仅测试
            this.$set(this.req, 'Items', [])
        }
        this.req.Items.push(new dns.Entities.RequireItem())
    }

    /** 删除物资 */
    onDeleteItem(index) {
        //新建的直接删除,旧的标为删除
        if (this.req.Items[index].isNew()) {
            this.req.Items.splice(index, 1)
        } else {
            this.req.Items[index].markDeleted()
        }
    }

    onSave() {
        dns.Services.RequirementService.Save(this.req).then(res => {
            this.req.acceptChanges()
            this.$message.success("保存成功")
        }).catch(err => {
            this.$message.error("保存错误: " + err)
        })
    }

    mounted() {
        this.loadItems()
    }
}

3.4 修改RequireList视图,实现新建功能

3.4.1 模版

    新建

    
        
    

3.4.2 脚本

@Component({
    components: { RequireView: dns.Views.RequireView }
})
export default class RequireList extends Vue {
    //----省略----
    dlgVisible = false
    current = null
    //----省略----
    onCreate() { 
        this.current = new dns.Entities.Requirement()
        this.dlgVisible = true
    }
    //----省略----

现在可以在预览内尝试添加些数据了!

四、实现需求列表Pop显示物资清单

  主列表使用懒加载方式加载子表数据。

4.1 修改RequirementService服务实现加载子表数据方法

/// 
/// 加载需求物资列表
/// 
public async Task LoadItems(Guid reqId)
{
    var q = new SqlQuery();
    q.Where(t => t.ReqId == reqId);
    q.Include(t => t.Item.Name);
    q.Include(t => t.Item.Spec);
    return await q.ToListAsync();
}

4.2 修改RequireList视图

4.2.1 模版



    

4.2.2 脚本

/** 加载需求物资清单 */
loadItems(row: dns.Entities.Requirement) {
    if (row.Items) { return }
    dns.Services.RequirementService.LoadItems(row.Id).then(res => {
        this.$set(row, 'Items', $runtime.parseEntity(res))
    }).catch(err => {
        this.$message.error('加载物资列表失败: ' + err)
    })
}

4.2.3 预览

AppBox实战: 如何实现一对多表单的增删改查_第6张图片

五、实现修改与删除功能

5.1 修改RequirementService服务实现删除方法

public async Task Delete(Entities.Requirement req)
{
    //TODO:判断状态,已发放物资的不能删除
    using var conn = await DataStore.Default.OpenConnectionAsync();
    using var txn = conn.BeginTransaction();
    //先删除子表记录
    var deleteItems = new SqlDeleteCommand();
    deleteItems.Where(t => t.ReqId == req.Id);
    await DataStore.Default.ExecCommandAsync(deleteItems, txn);
    //再删除主表记录
    await DataStore.Default.DeleteAsync(req, txn);
    //递交事务
    txn.Commit();
}

5.2 修改RequireList视图

5.2.1 模版


修改
删除


5.2.2 脚本

//----省略----
export default class RequireList extends Vue {
    //----省略----
    currentRow = null
    //----省略----
    onCurrentChanged(row) {
        this.currentRow = row
    }
    onEdit() { 
        if (!this.currentRow) {
            this.$message.warning("请先选择记录")
            return
        }
        this.loadItems(this.currentRow)
        this.current = this.currentRow
        this.dlgVisible = true
    }
    onDelete() {
        if (!this.currentRow) {
            this.$message.warning("请先选择记录")
            return
        }
        this.$confirm('确认删除选择的记录吗?', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
        }).then(() => {
            dns.Services.RequirementService.Delete(this.currentRow).then(res => {
                this.$message.success("删除成功");
            }).catch(err => {
                this.$message.error("删除失败: " + err)
            })
        }).catch(() => { })
    }
    //----省略----

5.2.3 预览

AppBox实战: 如何实现一对多表单的增删改查_第7张图片

六、本篇小结

  作者上篇提到实现独立的不依赖内置存储的版本,本篇示例即是基于此版本,下一步重点是针对此版本的测试与Bug修复。另一边码代码一边码文实属不易,作者需要您的支持请您多多点赞推荐!

你可能感兴趣的:(AppBox实战: 如何实现一对多表单的增删改查)