ASP.NET ZERO 学习 —— (18) 应用开发Demo之扩展电话信息

来源:互联网 发布:淘宝三无产品处罚 编辑:程序博客网 时间:2024/05/19 13:45

创建 Phone 实体

现在,在.Core项目中创建一个新的实体,Phone Entity

[Table("PbPhones")]public class Phone : CreationAuditedEntity<long>{    public const int MaxNumberLength = 16;    [ForeignKey("PersonId")]    public virtual Person Person { get; set; }    public virtual int PersonId { get; set; }    [Required]    public virtual PhoneType Type { get; set; }    [Required]    [MaxLength(MaxNumberLength)]    public virtual string Number { get; set; }}

Phone的数据存储在PbPhones表里,它的主键类型是long。它引用了Person实体作为该电话的所有人。

我们向Person类里添加Phone的集合

[Table("PbPersons")]public class Person : FullAuditedEntity{    public const int MaxNameLength = 32;    public const int MaxSurnameLength = 32;    public const int MaxEmailAddressLength = 255;    [Required]    [MaxLength(MaxNameLength)]    public virtual string Name { get; set; }    [Required]    [MaxLength(MaxSurnameLength)]    public virtual string Surname { get; set; }    [MaxLength(MaxEmailAddressLength)]    public virtual string EmailAddress { get; set; }    public virtual ICollection<Phone> Phones { get; set; }}

我们还需要一个PhoneType的枚举:

public enum PhoneType : byte{    Mobile,    Home,    Business}

最后,我们还要向DbContext里添加Phone的DbSet

public virtual IDbSet<Phone> Phones { get; set; }

数据迁移

我们的实体模型被更改,所以我们需要添加一个新的migration:

这里写图片描述

它会创建一个数据迁移文件来创建PbPhones表:

public partial class Added_Phone : DbMigration{    public override void Up()    {        CreateTable(            "dbo.PbPhones",            c => new                {                    Id = c.Long(nullable: false, identity: true),                    PersonId = c.Int(nullable: false),                    Type = c.Byte(nullable: false),                    Number = c.String(nullable: false, maxLength: 16),                    CreationTime = c.DateTime(nullable: false),                    CreatorUserId = c.Long(),                })            .PrimaryKey(t => t.Id)            .ForeignKey("dbo.PbPersons", t => t.PersonId, cascadeDelete: true)            .Index(t => t.PersonId);    }    public override void Down()    {        DropForeignKey("dbo.PbPhones", "PersonId", "dbo.PbPersons");        DropIndex("dbo.PbPhones", new[] { "PersonId" });        DropTable("dbo.PbPhones");    }}

在更新数据库之前,我们可以向数据库添加电话的示例:

public class InitialPeopleCreator{    private readonly AbpZeroTemplateDbContext _Context;    public InitialPeopleCreator(AbpZeroTemplateDbContext context)    {        this._Context = context;    }    public void Create()    {        var alistair = this._Context.Persons.FirstOrDefault(p => p.EmailAddress == "alistair.chow@ali.com");        if(alistair == null)        {            this._Context.Persons.Add(                new Entities.Person                {                    Name = "Alistair",                    Surname = "Chow",                    EmailAddress = "alistair.chow@ali.com",                    Phones = new List<Phone>                    {                        new Phone {Type=PhoneType.Home, Number="11221122" },                        new Phone {Type=PhoneType.Mobile, Number="22332233" }                    }                });        }        var tencent = this._Context.Persons.FirstOrDefault(p => p.EmailAddress == "tencent.cloud@ali.com");        if (tencent == null)        {            this._Context.Persons.Add(                new Entities.Person                {                    Name = "Tencent",                    Surname = "Cloud",                    EmailAddress = "tencent.cloud@ali.com",                    Phones = new List<Phone>                    {                        new Phone {Type=PhoneType.Home, Number="33443344" }                    }                });        }    }}

我们向Alistair添加了两个电话,向Tencent添加了一个电话。但是如果我们现在执行Update-Database,由于人员信息已经存在,所以电话不会被插入。那怎么解决?由于还没有部署,我们可以删除数据库(或truncate People表),再重新创建它。

现在,我们们在程序包管理控制台执行 Update-Database命令。你可以在数据库里看到PbPhones表及里面的数据。

修改GetPeople方法

我们需要修改PersonAppService.GetPeople方法,让返回值里包含电话号码

 public ListResultDto<PersonListDto> GetPeople(GetPeopleInput input) {     var persons = this._personRepository         .GetAll()         .Include(p => p.Phones)         .WhereIf(             !input.Filter.IsNullOrEmpty(),             p => p.Name.Contains(input.Filter) ||                 p.Surname.Contains(input.Filter) ||                 p.EmailAddress.Contains(input.Filter)         )         .OrderBy(p => p.Name)         .ThenBy(p => p.Surname)         .ToList();     return new ListResultDto<PersonListDto>(persons.MapTo<List<PersonListDto>>()); }

我们只需要在查询里添加Include 扩展方法,其他代码保持一致。

AddPhone 和 DeletePhone 方法

我们要在IPersonAppService接口里添加两个方法:

Task DeletePhone(EntityDto<long> input);Task<PhoneInPersonListDto> AddPhone(AddPhoneInput input);

我们也可以创建一个IPhoneAppService。但是,我们可以把人看做一个集合,在这里添加电话的相关方法。AddPhoneInput DTO如下所示:

[AutoMapTo(typeof(Phone))]public class AddPhoneInput{    [Range(1, int.MaxValue)]    public int PersonId { get; set; }    [Required]    public PhoneType Type { get; set; }    [Required]    [MaxLength(Phone.MaxNumberLength)]    public string Number { get; set; }}

现在我们实现接口方法:

private readonly IRepository<Entities.Person> _personRepository;private readonly IRepository<Entities.Phone, long> _phoneRepository;public PersonAppService(IRepository<Entities.Person> personRepository, IRepository<Entities.Phone, long> phoneRepository){    this._personRepository = personRepository;    this._phoneRepository = phoneRepository;}public async Task DeletePhone(EntityDto<long> input){    await this._phoneRepository.DeleteAsync(input.Id);}public async Task<PhoneInPersonListDto> AddPhone(AddPhoneInput input){    var person = _personRepository.Get(input.PersonId);    var phone = input.MapTo<Entities.Phone>();    person.Phones.Add(phone);    await CurrentUnitOfWork.SaveChangesAsync();    return phone.MapTo<PhoneInPersonListDto>();}

DeletePhone方法非常简单,只需要通过传入的Id删除对应的Phone。

AddPhone方法首先从数据库中获取Person,再向其添加新的Phone对象,然后进行保存。保存更改会将新添加的电话插入数据库并获取它的ID。因为我们返回的DTO包含了新的电话信息及它的ID。所以,我们在最后一行进行映射。(注意,通常来讲我们一般不会调用CurrentUnitOfWork.SaveChangesAsync。他会在最后的方法中自动调用,当我们需要立即保存实体并获取它的ID时才手动去调用它)

人员列表的编辑模式

最终效果如下:

这里写图片描述
当我们点击绿色的编辑图标,所选中的行将会扩展显示所有的电话信息。然后我们可以对电话进行删除,或者添加新的电话号码。

视图

视图改变如下:

@using Abp.Web.Mvc.Extensions@using MyCompanyName.AbpZeroTemplate.Web.Navigation@using MyCompanyName.AbpZeroTemplate.Authorization@using MyCompanyName.AbpZeroTemplate.Web.Areas.Mpa.Models.PhoneBook@model MyCompanyName.AbpZeroTemplate.Web.Areas.Mpa.Models.PhoneBook.IndexViewModel@{    ViewBag.CurrentPageName = PageNames.App.Tenant.PhoneBook;}@section Scripts{    @Html.IncludeScript("~/Areas/Mpa/Views/PhoneBook/_CreatePersonModal.js")    @Html.IncludeScript("~/Areas/Mpa/Views/PhoneBook/Index.js")}@section Styles{    @Html.IncludeStyle("~/Areas/Mpa/Views/PhoneBook/Index.min.css")}<div class="row margin-bottom-5">    <div class="col-xs-6">        <div class="page-head">            <div class="page-title">                <h1>                    <span>@L("PhoneBook")</span>                </h1>            </div>        </div>    </div>    <div class="col-xs-6 text-right">    @if (IsGranted(AppPermissions.Pages_Tenant_PhoneBook_CreatePerson))    {        <button id="CreateNewPersonButton" class="btn btn-primary blue">            <i class="fa fa-plus"></i>@L("CreateNewPerson")        </button>     }    </div></div><div class="portlet light">    <div class="portlet-title portlet-title-filter">        <h3>@L("AllPeople") (@Model.Items.Count)</h3>        <div class="inputs inputs-full-width">            <div class="portlet-input">                <form action="@Url.Action("Index")" method="get">                    <div class="input-group">                        <input id="FilterPeopleText" name="Filter" value="@Model.Filter" class="form-control" placeholder="@L("SearchWithThreeDot")" type="text" />                        <span class="input-group-btn">                            <button id="FilterPeopleButton" class="btn default" type="submit">                                <i class="icon-magnifier"></i>                            </button>                        </span>                    </div>                </form>            </div>        </div>    </div>    <div class="portlet-body">        <div id="AllPeopleList" class="list-group">            @foreach (var person in Model.Items)            {                <a href="javascript:;" class="list-group-item" data-person-id="@person.Id">                    <h4 class="list-group-item-heading">                        @person.Name @person.Surname                        <span class="person-buttons">                            <button title="@L("Edit")" class="btn btn-circle btn-icon-only green edit-person">                                <i class="icon-pencil"></i>                            </button>                            @if (IsGranted(AppPermissions.Pages_Tenant_PhoneBook_DeletePerson))                            {                                <button title="@L("Delete", person.Name)" class="btn btn-circle btn-icon-only red delete-person" href="javascript:;">                                    <i class="icon-trash"></i>                                </button>                            }                        </span>                    </h4>                    <p class="list-group-item-text">                        @person.EmailAddress                    </p>                    <div class="table-scrollable table-phones">                        <table class="table table-hover">                            <thead>                                <tr>                                    <th style="width:10%"></th>                                    <th style="width:15%">@L("Type")</th>                                    <th style="width:75%">@L("PhoneNumber")</th>                                </tr>                            </thead>                            <tbody>                                @foreach (var phone in person.Phones)                                {                                    @Html.Partial("_PhoneRowInPersonList", new PhoneRowInPersonListViewModel(phone))                                }                                <tr>                                    <td>                                        <button class="btn btn-sm green button-save-phone">                                            <i class="fa fa-floppy-o"></i>                                        </button>                                    </td>                                    <td>                                        <select name="Type">                                            <option value="0">@L("PhoneType_Mobile")</option>                                            <option value="1">@L("PhoneType_Home")</option>                                            <option value="2">@L("PhoneType_Business")</option>                                        </select>                                    </td>                                    <td><input type="text" name="Number" /></td>                                </tr>                            </tbody>                        </table>                    </div>                </a>                            }        </div>    </div></div>

我们针对每一个Person都添加了一个编辑按钮。然后为每个person添加了一个table用来显示该person的所有电话并允许添加电话。电话列表只有当我们点击了编辑按钮后才会显示,它是通过CSS和JS进行的实现(在后面我们会看见)。

这里有一个重要的思想,我们使用部分视图来显示电话信息,这样做是为了让这部分可以重用。因为我们在新建电话的时候会用到相同的部分视图。_PhoneRowInPersonList 如下所示:

@model MyCompanyName.AbpZeroTemplate.Web.Areas.Mpa.Models.PhoneBook.PhoneRowInPersonListViewModel<tr data-phone-id="@Model.Phone.Id">    <td>        <button class="btn btn-sm default button-delete-phone">            <i class="icon-trash"></i>        </button>    </td>    <td>@Model.GetPhoneTypeAsString()</td>    <td>@Model.Phone.Number</td></tr>

PhoneRowInPersonListViewModel 如下:

public class PhoneRowInPersonListViewModel{    public PhoneInPersonListDto Phone { get; set; }    public PhoneRowInPersonListViewModel(PhoneInPersonListDto phone)    {        this.Phone = phone;    }    public string GetPhoneTypeAsString()    {        return LocalizationHelper.GetString("", "PhoneType_" + Phone.Type);    }}

样式

index.less修改如下:

#AllPeopleList {    .list-group-item-heading {        span.person-buttons {            float: right;        }    }    .table-phones{        display: none;    }    .person-editing{        background-color:#ccffcc;        h4{            font-weight:bold;        }        .table-phones{            display:table;        }    }}

脚本

添加以下代码至Index.js

//Edit person button$('#AllPeopleList button.edit-person').click(function (e) {    e.preventDefault();    var $listItem = $(this).closest('.list-group-item');    $listItem    .toggleClass('person-editing')    .siblings().removeClass('person-editing');});//Save phone button$('#AllPeopleList .button-save-phone').click(function (e) {    e.preventDefault();    var $phoneEditorRow = $(this).closest('tr');    abp.ajax({        url: abp.appPath + 'Mpa/PhoneBook/AddPhone',        dataType: 'html',        data: JSON.stringify({            personId: $phoneEditorRow.closest('.list-group-item').attr('data-person-id'),            Type: $phoneEditorRow.find('select[name=Type]').val(),            Number: $phoneEditorRow.find('input[name=Number]').val()        })    }).done(function (result) {        $(result).insertBefore($phoneEditorRow);    });});//Delete phone button$('#AllPeopleList').on('click', '.button-delete-phone', function (e) {    e.preventDefault();    var $phoneRow = $(this).closest('tr');    var phoneId = $phoneRow.attr('data-phone-id');    _personService.deletePerson({        id:phoneId    }).done(function () {        abp.notify.success(app.localize('SuccessfullyDeleted'));        $phoneRow.remove();    });});

当点击修改按钮时,我们通过CSS来控制电话信息的展开和收缩。

在保存按钮的点击事件里,我们 向PhoneBookController的 AddPhone action提交了一个Ajax请求。服务端返回一个插入新号码的HTML,这就是为什么我们使用部分页的原因。

最后,当我们点击删除按钮后会删除该电话信息并在DOM中删除该行。注意这里的事件注册,我们使用了JQuery的On方法,选择器变为激活状态。这意味着,如果我们加入新的元素到页面并且任一元素和选择器匹配,那它的Click事件将被自动绑定。

ADDPHONE ACTION

我们向 PhoneController 添加AddPhone Action:

[HttpPost]public async Task<PartialViewResult> AddPhone(AddPhoneInput input){    PhoneInPersonListDto phoneInPersonList = await this._personAppService.AddPhone(input);    var model = new PhoneRowInPersonListViewModel(phoneInPersonList);    return PartialView("_PhoneRowInPersonList", model);}

使用PersonAppService.AddPhone并且返回_PhoneRowInPersonList 的部分视图。因此,我们直接将返回值插入到表中。示例返回值如下所示:

<tr data-phone-id="5">    <td>        <button class="btn btn-sm default button-delete-phone">            <i class="icon-trash"></i>        </button>    </td>    <td>手机</td>    <td>33443344</td></tr>

如你所见,这可以直接插入到table中,就像我们已经做的那样。

0 0
原创粉丝点击