本篇經過完整示例介紹如何實現一對多關係表單的相應服務及視圖。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修復。另外一邊碼代碼一邊碼文實屬不易,做者須要您的支持請您多多點贊推薦!