本篇经过完整示例介绍如何实现一对多关系表单的相应服务及视图。javascript
示例所采用的数据结构为“物资需求”一对多“物资清单”,经过IDE的实体设计器以下所示:html
主键为Id(Guid)
前端
主键为Id(Guid)
java
主键为Req(Requirement)+Item(DonateItem)
数据结构
添加实体成员时选择类型EntityRef(一对一)或EntitySet(一对多)可设置相应的导航属性
async
using System; using System.Threading.Tasks; namespace dns.ServiceLogic { public class RequirementService { /// <summary> /// 分页加载需求记录 /// </summary> public async Task<object> Load(int pageIndex, int pageSize) { var q = new SqlQuery<Entities.Requirement>(); q.Skip(pageSize * pageIndex).Take(pageSize); q.OrderByDesc(t => t.Time); return await q.ToListAsync(); } } }
<div> <el-button-group> <el-button type="primary" icon="fas fa-plus-square fa-fw">新建</el-button> <el-button type="primary" icon="fas fa-edit fa-fw">修改</el-button> <el-button type="primary" icon="fas fa-trash fa-fw">删除</el-button> <el-button @click="load" type="primary" icon="fas fa-search fa-fw">刷新</el-button> </el-button-group> <br/><br/> <el-table :data="items" v-loading="loading" border stripe highlight-current-row readonly> <el-table-column prop="Donee" label="需求方"></el-table-column> <el-table-column prop="Time" label="时间"></el-table-column> <el-table-column prop="Contact" label="联系人" width="80"></el-table-column> <el-table-column prop="Phone" label="电话"></el-table-column> <el-table-column prop="Address" label="地址"></el-table-column> <el-table-column prop="PostCode" label="邮编" width="80"></el-table-column> </el-table> <el-pagination background layout="prev, pager, next" :total="1000"> </el-pagination> </div>
@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对象,另若是调用服务的结果只用做展现能够不用转换。函数
点击“Preview”预览,固然当前没有任何数据。
测试
/// <summary> /// 保存需求 /// </summary> 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(); }
using System; using System.Threading.Tasks; namespace dns.ServiceLogic { public class ItemService { /// <summary> /// 加载用于前端选择绑定 /// </summary> public async Task<object> LoadForSelect() { var q = new SqlQuery<Entities.DonateItem>(); return await q.ToListAsync(t => new { t.Id, t.Name, t.Spec }); } } }
<div> <!-- 表单头 --> <el-row :gutter="20" type="flex" align="middle"> <el-col :span="3">需求方:</el-col> <el-col :span="9"> <el-input v-model="req.Donee"></el-input> </el-col> <el-col :span="3">时间:</el-col> <el-col :span="9"> <el-date-picker v-model="req.Time" type="date" placeholder="选择日期" style="width:100%"> </el-date-picker> </el-col> </el-row> <br/> <el-row :gutter="20" type="flex" align="middle"> <el-col :span="3">联系人:</el-col> <el-col :span="9"> <el-input v-model="req.Contact"></el-input> </el-col> <el-col :span="3">电话:</el-col> <el-col :span="9"> <el-input v-model="req.Phone"></el-input> </el-col> </el-row> <br/> <el-row :gutter="20" type="flex" align="middle"> <el-col :span="3">地址:</el-col> <el-col :span="9"> <el-input v-model="req.Address"></el-input> </el-col> <el-col :span="3">邮编:</el-col> <el-col :span="9"> <el-input v-model="req.PostCode"></el-input> </el-col> </el-row> <br/> <!-- 物资列表 --> <el-table :data="items" border highlight-current-row readonly> <el-table-column type="index"></el-table-column> <el-table-column label="物资" width="280px"> <template slot-scope="scope"> <el-select v-model="scope.row.ItemId" value-key="Id" style="width:100%" placeholder="请选择"> <el-option v-for="item in optItems" :key="item.Id" :label="item.Name+' '+item.Spec" :value="item.Id"> </el-option> </el-select> </template> </el-table-column> <el-table-column label="数量" width="130px"> <template slot-scope="scope"> <el-input-number v-model="scope.row.Quantity" controls-position="right" :min="1" style="width:100%"> </el-input-number> </template> </el-table-column> <el-table-column label="备注"> <template slot-scope="scope"> <el-input v-model="scope.row.Comment"></el-input> </template> </el-table-column> <el-table-column align="right" width="100px"> <template slot="header" slot-scope="scope"> <el-button @click="onAddItem" icon="fa fa-plus fa-fw" size="mini">添加</el-button> </template> <template slot-scope="scope"> <el-button @click="onDeleteItem(scope.$index)" icon="fa fa-times fa-fw" size="mini">删除</el-button> </template> </el-table-column> </el-table> <br/> <div style="text-align:center"> <el-button @click="onSave" type="primary" icon="fas fa-save fa-fw">保存</el-button> </div> </div>
@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() } }
<el-button @click="onCreate" type="primary" icon="fas fa-plus-square fa-fw">新建</el-button> <!-- 省略 --> <el-dialog title="需求记录" :visible.sync="dlgVisible" width="800px"> <require-view :req="current"></require-view> </el-dialog> <!-- 省略 -->
@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 } //----省略----
如今能够在预览内尝试添加些数据了!flex
主列表使用懒加载方式加载子表数据。ui
/// <summary> /// 加载需求物资列表 /// </summary> public async Task<object> LoadItems(Guid reqId) { var q = new SqlQuery<Entities.RequireItem>(); q.Where(t => t.ReqId == reqId); q.Include(t => t.Item.Name); q.Include(t => t.Item.Spec); return await q.ToListAsync(); }
<!-- 省略 --> <el-table-column label="需求物资" width="80"> <template slot-scope="scope"> <!-- 物资清单列表 --> <el-popover @show="loadItems(scope.row)" trigger="hover" placement="left" width="400"> <el-row v-for="(item,index) in scope.row.Items" :gutter="20"> <el-col :span="2">{{index+1}}.</el-col> <el-col :span="4">{{item.ItemName}}</el-col> <el-col :span="14">{{item.ItemSpec}}</el-col> <el-col :span="4">{{item.Quantity}}</el-col> </el-row> <div slot="reference"> <el-tag size="medium">物资清单</el-tag> </div> </el-popover> </template> </el-table-column> <!-- 省略 -->
/** 加载需求物资清单 */ 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) }) }
public async Task Delete(Entities.Requirement req) { //TODO:判断状态,已发放物资的不能删除 using var conn = await DataStore.Default.OpenConnectionAsync(); using var txn = conn.BeginTransaction(); //先删除子表记录 var deleteItems = new SqlDeleteCommand<Entities.RequireItem>(); deleteItems.Where(t => t.ReqId == req.Id); await DataStore.Default.ExecCommandAsync(deleteItems, txn); //再删除主表记录 await DataStore.Default.DeleteAsync(req, txn); //递交事务 txn.Commit(); }
<!-- 省略 --> <el-button @click="onEdit" type="primary" icon="fas fa-edit fa-fw">修改</el-button> <el-button @click="onDelete" type="primary" icon="fas fa-trash fa-fw">删除</el-button> <!-- 省略 --> <el-table :data="items" v-loading="loading" @current-change="onCurrentChanged" border stripe highlight-current-row readonly>
//----省略---- 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(() => { }) } //----省略----
做者上篇提到实现独立的不依赖内置存储的版本,本篇示例便是基于此版本,下一步重点是针对此版本的测试与Bug修复。另外一边码代码一边码文实属不易,做者须要您的支持请您多多点赞推荐!