从零开始开发PasteDocument(贴代码PasteForm框架实战序列)(3)-搭建地基

61 阅读14分钟

上一章说到2个工具,那么本次就使用这个工具来搭建PasteDocument

创建PasteDocument项目

新开一个VS2022 在这里插入图片描述 我们选择创建新项目 在这里插入图片描述 在检索栏中输入paste进行检索,看到如上图,选择他,然后下一步 在这里插入图片描述 输入项目名称PasteDocument,然后点击创建 等待VS工作后,会打开如下图 在这里插入图片描述 注意默认打开的时候的启动项目是有问题的,我们选择PasteDocument.HttpApi.Host项目为启动项目 上图有一个警告,npm的,我们不理他! 上面的项目就是使用PasteTemplate创建的项目了,可以看到子项目的层级和ABP非常像!! 其实他就是ABP框架!!! 在PasteDocument.HttpApi.Host的项目下方,我看到到wwwroot文件夹如下 在这里插入图片描述 可以看到整个这个就是当前项目的管理端页面,可以看到整个管理端的页面非常少,就几个html文件,注意看pasteform这个就是贴代码PasteForm的经典文件! PasteForm的管理端是如上特点,那么对应的API呢? 在这里插入图片描述 如上图,首先一个是PasteDocument.Application.Contracts项目中有引入PasteFormHelper的包 然后是查看PasteDocument.Application项目中的UserInfoAppService接口为例,有以上标注的接口! 特别是 ReadAddModel ReadUpdateModel ReadListModel 这几个接口的作用就是读取对应的Dto的模型字段,备注和特性等,用于管理端UI的显示!

启动项目

以上是默认创建的内容,我们接下来启动下,看看这个用项目模板生成的项目,能否正常运行 在这里插入图片描述 按照上图的操作,由于项目是使用EF的,内置默认为sqlite数据库,配置可以见appsettings.json的配置 按照上图操作后,我这的显示是操作成功的,我们点击最上方的启动,运行看看 在这里插入图片描述 可以看到打印一大堆内容后,看到如上图的提示,我们按照提示打开这个2222看看 http://localhost:22222/page/index.html 打开后,跳转到登陆页面,按照默认的账号和密码,输入图形验证码后执行登陆 在这里插入图片描述 登陆成功后,可以看到如下图 在这里插入图片描述 这里可以看到系统有创建默认的权限和菜单,账号,角色等 项目的创建到这里就算完成了,那么接下来的就是我们如何在这个基础上搭建对应的需求表!

数据表需求

基于第一篇的需求,我大概罗列了如下需求 在这里插入图片描述 基于以上的分析,在PasteDocument.Domain中创建模块markmodels(其实就是一个文件夹) 然后创建如下内容 在这里插入图片描述

Company 表示组织

namespace PasteDocument.usermodels
{
    /// <summary>
    /// 组织信息
    /// </summary>
    public class CompanyInfo : Entity<int>
    {
        /// <summary>
        /// 名称
        /// </summary>
        [MaxLength(32)]
        public string Name { get; set; } = "";

        /// <summary>
        /// 图标
        /// </summary>
        [MaxLength(256)]
        public string Head { get; set; } = "";

        /// <summary>
        /// 描述
        /// </summary>
        [MaxLength(256)]
        public string Desc { get; set; } = "";

        /// <summary>
        /// 备注
        /// </summary>
        [MaxLength(32)]
        public string Mark { get; set; } = "";

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateDate { get; set; } = DateTime.Now;

        /// <summary>
        /// 创建者
        /// </summary>
        public int UserId { get; set; }
    }
}

ProjectInfo 表示项目信息

namespace PasteDocument.markmodels
{
    /// <summary>
    /// 项目信息 软件信息,示例商城
    /// </summary>
    [Index(nameof(Code), IsUnique = true)]
    public class ProjectInfo : Entity<int>
    {
        /// <summary>
        /// 代码 一般作为Route使用
        /// </summary>
        [MaxLength(32)]
        public string Code { get; set; } = "";

        /// <summary>
        /// 名称 示例贴代码商城
        /// </summary>
        [MaxLength(32)]
        public string Name { get; set; } = "";

        /// <summary>
        /// 说明 这个项目简介说明
        /// </summary>
        [MaxLength(128)]
        public string Desc { get; set; } = "";

        /// <summary>
        /// 备注 内部备注
        /// </summary>
        [MaxLength(64)]
        public string Mark { get; set; } = "";

        /// <summary>
        /// 创建者
        /// </summary>
        public int UserId { get; set; }

        /// <summary>
        /// 图标样式 示例Hui-iconfont-jifen
        /// </summary>
        [MaxLength(64)]
        public string Icon { get; set; } = "";

        /// <summary>
        /// 图标 LOGO,一般是自定义上传
        /// </summary>
        [MaxLength(128)]
        public string Head { get; set; } = "";

        /// <summary>
        /// 主页 项目主页
        /// </summary>
        [MaxLength(256)]
        public string HomeUrl { get; set; } = "";

        /// <summary>
        /// 组织 率属于哪个组织
        /// </summary>
        public int CompanyId { get; set; }

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateDate { get; set; } = DateTime.Now;

        /// <summary>
        /// 排序
        /// </summary>
        public int Sort { get; set; }

        /// <summary>
        /// 状态
        /// </summary>
        public bool IsEnable { get; set; } = true;

        /// <summary>
        /// 公开
        /// </summary>
        public bool IsPublic { get; set; } = false;
    }
}

ModuleInfo 表示模块信息

namespace PasteDocument.markmodels
{
    /// <summary>
    /// 模块信息 示例商品
    /// </summary>
    public class ModuleInfo : Entity<int>
    {

        /// <summary>
        /// 项目
        /// </summary>
        public int ProjectId { get; set; }

        /// <summary>
        /// 代码 示例:product
        /// </summary>
        [MaxLength(32)]
        public string Code { get; set; } = "";

        /// <summary>
        /// 名称 示例:商品信息
        /// </summary>
        [MaxLength(32)]
        public string Name { get; set; } = "";

        /// <summary>
        /// 头像
        /// </summary>
        [MaxLength(128)]
        public string Head { get; set; } = "";

        /// <summary>
        /// 图标 示例:Hui-iconfont-img2
        /// </summary>
        [MaxLength(64)]
        public string Icon { get; set; } = "";

        /// <summary>
        /// 父级
        /// </summary>
        public int FatherId { get; set; }

        /// <summary>
        /// 父级簇
        /// </summary>
        [MaxLength(20)]
        public string FatherStr { get; set; } = "";

        /// <summary>
        /// 层级
        /// </summary>
        public int Level { get; set; }

        /// <summary>
        /// 排序
        /// </summary>
        public int Sort { get; set; }

        /// <summary>
        /// 排序簇
        /// </summary>
        [MaxLength(20)]
        public string SortStr { get; set; } = "";

        /// <summary>
        /// 创建者
        /// </summary>
        public int UserId { get; set; }

        /// <summary>
        /// 子项数 
        /// </summary>
        public int Items { get; set; }

        /// <summary>
        /// 展开 默认是否展开下一级
        /// </summary>
        public bool IsOpen { get; set; } = true;

        /// <summary>
        /// 状态
        /// </summary>
        public bool IsEnable { get; set; } = true;

        /// <summary>
        /// 组织 率属于哪个组织
        /// </summary>
        public int CompanyId { get; set; }
    }
}

FieldInfo 表示字段信息

namespace PasteDocument.markmodels
{
    /// <summary>
    /// 字段说明 示例:封面图
    /// </summary>
    public class FieldInfo : Entity<int>
    {

        /// <summary>
        /// 项目
        /// </summary>
        public int ProjectId { get; set; }

        /// <summary>
        /// 模块
        /// </summary>
        public int ModuleId { get; set; }

        /// <summary>
        /// 代码 示例:code
        /// </summary>
        [MaxLength(32)]
        public string Code { get; set; } = "";

        /// <summary>
        /// 名称 字段名称,示例:封面图
        /// </summary>
        [MaxLength(32)]
        public string Name { get; set; } = "";

        /// <summary>
        /// 内容 html
        /// </summary>
        public string Context { get; set; } = "";

        /// <summary>
        /// 内容 Markdown
        /// </summary>
        public string MarkDown { get; set; } = "";

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateDate { get; set; } = DateTime.Now;

        /// <summary>
        /// 排序
        /// </summary>
        public int Sort { get; set; }

        /// <summary>
        /// 状态
        /// </summary>
        public bool IsEnable { get; set; } = true;

        /// <summary>
        /// 创建者
        /// </summary>
        public int UserId { get; set; }

        /// <summary>
        /// 回复数
        /// </summary>
        public int ReplyNum { get; set; }

        /// <summary>
        /// 点赞数
        /// </summary>
        public int LikeNum { get; set; }

        /// <summary>
        /// 编辑类型
        /// </summary>
        public EnumEditModel EditModel { get; set; }

        /// <summary>
        /// 更新时间
        /// </summary>
        public DateTime? UpdateDate { get; set; }

        /// <summary>
        /// 组织 率属于哪个组织
        /// </summary>
        public int CompanyId { get; set; }
    }
}

查看以上的内容,可以看到对应的表的字段我是如何处理的,至于为啥不用外表模式?这个看个人习惯,要使用外表的模式也是可以的,不过我个人不习惯用!

以上是CodeFirst的第一步Code 那么接下来的,就是我们要为这些生成对应的Dto,AppService等 这个工作我交给代码生成器来处理,执行这些操作前,先查看下上方的代码 按照项目模板中的RoleInfoListDto为例

    ///<summary>
    ///权限列表
    ///</summary>
    [PasteLinkQuery("fatherId")]
    public class RoleInfoListDto
    {
        /// <summary>
        /// ID
        /// </summary>
        [ColumnDataType("orderby", "Id", "Id desc")]
        public int Id { get; set; }

        ///<summary>
        ///名称
        ///</summary>
        [PasteClass]
        [ColumnDataType("html", "<div class=\"level level{{:=item.level}} role{{:=item.roleTypeInt}}\">{{ if(item.icon){ }}<i class=\"Hui-iconfont {{:=item.icon}}\"></i>{{ } }} {{:=item.name}}</div>")]
        public string Name { get; set; }

        ///<summary>
        ///模块
        ///</summary>
        [PasteClass]
        [PasteHidden]
        public string Model { get; set; }

        ///<summary>
        ///权限
        ///</summary>
        [PasteClass]
        [PasteHidden]
        public string Role { get; set; }

        /// <summary>
        /// 图标名称 一般表示样式名称
        /// </summary>
        [MaxLength(32)]
        [PasteHidden]
        public string Icon { get; set; }

        /// <summary>
        /// 路径/权限 作为菜单类型的路径
        /// </summary>
        [MaxLength(128)]
        [PasteClass]
        [ColumnDataType("html", "<div class=\"idesc\">{{:=item.path}}{{:=item.model}}  {{:=item.role}}</div>")]
        public string Path { get; set; }

        /// <summary>
        /// 权限类型 0权限1菜单2按钮,如何让前端支持这个显示
        /// </summary>
        [PasteSelect(PasteFormString.SelectRoleTypeQuery)]
        public int RoleType { get; set; }

        /// <summary>
        /// 权限类型
        /// </summary>
        [PasteHidden]
        public int RoleTypeInt { get { return RoleType; } }

        ///<summary>
        ///描述
        ///</summary>
        [PasteClass]
        public string Desc { get; set; }

        ///<summary>
        ///状态
        ///</summary>
        [ColumnDataType("switch", "view")]
        public bool IsEnable { get; set; }

        /// <summary>
        /// 绑定 扩展基于角色的时候是否拥有这个权限
        /// </summary>
        [ColumnDataType("switch")]
        [ColumnDataType("hidden", "bind")]
        public bool ExtendBind { get; set; }

        ///<summary>
        ///排序
        ///</summary>
        [ColumnDataType("orderby", "Sort", "Sort desc")]
        public int Sort { get; set; }

        ///<summary>
        ///父级
        ///</summary>
        [ColumnDataType("outerdisplay", "", "extendFather?.name || ''")]
        public int FatherId { get; set; }

        /// <summary>
        /// 父级信息,为了给上一个字段FatherId显示使用,你也可以把FatherId隐藏,只显示这个字段,或者2个都显示
        /// </summary>
        [ColumnDataType("hidden")]
        public RoleShortModel ExtendFather { get; set; }

        ///<summary>
        ///层级
        ///</summary>
        [ColumnDataType("orderby", "Level", "Level desc")]
        public int Level { get; set; }

        ///<summary>
        ///排序串
        ///</summary>
        [PasteHidden]
        public string SortStr { get; set; }

        /// <summary>
        /// 添加子权限
        /// </summary>
        [ColumnDataType("menu", "添加子集", "open_window('添加子权限','./view.html?path=roleInfo&fatherId={{:=item.id}}');")]
        public int Menu1 { get; set; }

        /// <summary>
        /// 详情
        /// </summary>
        [ColumnDataType("menu", "详情", "open_window('详细','./detail.html?path=roleInfo&id={{:=item.id}}');")]
        public int Menu2 { get; set; }

        /// <summary>
        /// 查看子集
        /// </summary>
        [ColumnDataType("menu", "查看子集", "open_window('查看子集','./index.html?path=roleInfo&fatherId={{:=item.id}}');")]
        public int Menu3 { get; set; }

    }

可以看到针对不同的字段,我们需要给他配置不一样的特性,或者是不配置! 一个数据表对应至少4个Dto,所以我们需要改造下,让代码生成器支持自动帮我们补充对应的特性 这样可以减少手动输入的错误问题 打开PasteDocument.Domain/template/readme.md

    # 在当前文件夹下可以创建对应的模板文件
    index.html
    view.html
    add.html
    edit.html
    index.vue
    view.vue
    add.vue
    edit.vue
    
    # 模板写法资料
    //http://dotliquidmarkup.org/
    //https://shopify.github.io/liquid/tags/template/

    //两个单词的时候应该改成小写中间加_ 比如DirName 应为 dir_name

大概意思就是这个文件夹存放的是配置和模板文件,你不喜欢的话可以根据规则自己创建模板文件,不过模板文件的文件名有哟要求,这里我已经习惯默认模板了,就不搞这个了 重命名config.json.txt为config.json然后打开他

{
  "readme": "用使用下面的规则请重命名文件为config.json",
  "ignore": { //表示Dto中不生成这些字段,比如CreateDate那个一般是创建的时候生成的不需要AddDto传递过来
    "all": {
      "add": [ "CreateDate", "AdminUid", "CreateUid" ],
      "update": [ "UpdateDate", "CreateDate" ],
      "detail": [ "UpdateDate", "CreateDate" ],
      "list": [ "Body","Content","Context" ]
    },
    "other": {
      "NewTable:add": ["CreateDate"] //表示指定某一个表的某一个模式下
    }
  },
  "attribute": {
    "ignore": [ "Comment", "Display", "Decription", "Index", "DefaultValue", "Column", "NotMapped", "ConcurrencyStamp" ], //表示忽略哪些来自Domain的属性
    "all": {
      "UserId": "[ColumnDataType(\"outer\",\"userInfo\",\"extendUser\",\"id\",\"userName\")]", //表示这个字段UserId需要添加这个过滤器多个之间用::隔开 示例:[PasteLeft]::[PasteHidden]
      "GradeId": "[ColumnDataType(\"outer\",\"gradeInfo\",\"extendGrade\",\"id\",\"name\")]"
    },
    "other": {
      "NewTable:add:Head":"[PasteImage(1,\"head\")]::[PasteHidden]",//多个之间使用::隔开,表示在不同的Dto中为字段添加属性
      "NewTable:list:Desc": "[PasteLeft]"
    }
  }
}

看上面的解释,意思就是针对Dto的特性和字段的配置信息,所以我们针对当前项目做一个调整,调整后如下:

{
  "readme": "用使用下面的规则请重命名文件为config.json",
  "ignore": { //表示Dto中不生成这些字段,比如CreateDate那个一般是创建的时候生成的不需要AddDto传递过来
    "all": {
      "add": [ "CreateDate", "AdminUid", "CreateUid","UserId","CompanyId" ],
      "update": [ "UpdateDate", "CreateDate","UserId","CompanyId" ],
      "detail": [ "UpdateDate", "CreateDate" ],
      "list": [ "Body" ]
    },
    "other": {
      "NewTable:add": ["CreateDate"] //表示指定某一个表的某一个模式下
    }
  },
  "attribute": {
    "ignore": [ "Comment", "Display", "Decription", "Index", "DefaultValue", "Column", "NotMapped", "ConcurrencyStamp" ], //表示忽略哪些来自Domain的属性
    "all": {
      "UserId": "[ColumnDataType(\"outer\",\"userInfo\",\"extendUser\",\"id\",\"userName\")]", //表示这个字段UserId需要添加这个过滤器多个之间用::隔开 示例:[PasteLeft]::[PasteHidden]
      "GradeId": "[ColumnDataType(\"outer\",\"gradeInfo\",\"extendGrade\",\"id\",\"name\")]",
      "CompanyId": "[PasteOuter(\"companyInfo\",\"extendCompany\",\"id\",\"name\")]",
      "ProjectId": "[PasteOuter(\"projectInfo\",\"extendProject\",\"id\",\"name\")]",
      "ModuleId": "[PasteOuter(\"moduleInfo\",\"extendModule\",\"id\",\"name\")]",
      "FieldId": "[PasteOuter(\"fieldInfo\",\"extendField\",\"id\",\"name\")]",
      "Head": "[PasteImage(1,\"head\",\"60*60\")]"
    },
    "other": {
      "NewTable:add:Head":"[PasteImage(1,\"head\")]::[PasteHidden]",//多个之间使用::隔开,表示在不同的Dto中为字段添加属性
      "NewTable:list:Desc": "[PasteLeft]"
    }
  }
}

关键是一下要点 在这里插入图片描述 因为在当前系统中的UserId,CompanyId都是由接口赋值的,获取当前登陆者的信息赋值给新加信息,所以不需要外部输入

以上步骤完成后,我们就可以执行代码生成了

生成代码

在这里插入图片描述 如上图,选定对应的这几个文件,注意Enum枚举的不要忘了,然后按照上图操作,点击完整生成! 点击后,等待执行完成,系统会弹出一个小窗告知生成成功! 生成成功后,我们可以在 PasteDocument.Application PasteDocument.Application.Contracts 都生成了新的文件夹markmoduls 而且里面都有对应的文件 先处理下EF中的新的内容写入后的命名引入的问题 在这里插入图片描述 然后是PasteDocument.Application.Contracts的有枚举的Dto的命名引入问题

还有一个是 PasteDocument.Application/PasteDocumentApplicationAutoMapperProfile.cs 这几个页面处理命名空间的引入问题后,尝试代码重新生成,按照提示操作

生成预期

我们看看生成的Dto(PasteDocument.Application.Contracts/markmodels/ProjectInfoDto.cs)是否符合预期

using Volo.Abp.Application.Dtos;
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Domain.Entities;
using PasteFormHelper;
using PasteDocument.Application.Contracts;

namespace PasteDocument.markmodels
{

    ///<summary>
    ///项目信息 软件信息,示例商城
    ///</summary>
    public class ProjectInfoAddDto
    {
        ///<summary>
        ///代码 一般作为Route使用
        ///</summary>
        [MaxLength(32)]
        public string Code { get; set; }
        ///<summary>
        ///名称 示例贴代码商城
        ///</summary>
        [MaxLength(32)]
        public string Name { get; set; }
        ///<summary>
        ///说明 这个项目简介说明
        ///</summary>
        [MaxLength(128)]
        public string Desc { get; set; }
        ///<summary>
        ///备注 内部备注
        ///</summary>
        [MaxLength(64)]
        public string Mark { get; set; }
        ///<summary>
        ///图标样式 示例Hui-iconfont-jifen
        ///</summary>
        [MaxLength(64)]
        public string Icon { get; set; }
        ///<summary>
        ///图标 LOGO,一般是自定义上传
        ///</summary>
        [MaxLength(128)]
        [PasteImage(1,"head","60*60")]
        public string Head { get; set; }
        ///<summary>
        ///主页 项目主页
        ///</summary>
        [MaxLength(256)]
        public string HomeUrl { get; set; }
        ///<summary>
        ///排序
        ///</summary>
        public int Sort { get; set; }
        ///<summary>
        ///状态
        ///</summary>
        public bool IsEnable { get; set; }
        ///<summary>
        ///公开
        ///</summary>
        public bool IsPublic { get; set; }
    }

    ///<summary>
    ///项目信息 软件信息,示例商城
    ///</summary>
    public class ProjectInfoUpdateDto:EntityDto<int>
    {
        ///<summary>
        ///代码 一般作为Route使用
        ///</summary>
        [MaxLength(32)]
        public string Code { get; set; }

        ///<summary>
        ///名称 示例贴代码商城
        ///</summary>
        [MaxLength(32)]
        public string Name { get; set; }

        ///<summary>
        ///说明 这个项目简介说明
        ///</summary>
        [MaxLength(128)]
        public string Desc { get; set; }

        ///<summary>
        ///备注 内部备注
        ///</summary>
        [MaxLength(64)]
        public string Mark { get; set; }

        ///<summary>
        ///图标样式 示例Hui-iconfont-jifen
        ///</summary>
        [MaxLength(64)]
        public string Icon { get; set; }

        ///<summary>
        ///图标 LOGO,一般是自定义上传
        ///</summary>
        [MaxLength(128)]
        [PasteImage(1,"head","60*60")]
        public string Head { get; set; }

        ///<summary>
        ///主页 项目主页
        ///</summary>
        [MaxLength(256)]
        public string HomeUrl { get; set; }

        ///<summary>
        ///排序
        ///</summary>
        public int Sort { get; set; }

        ///<summary>
        ///状态
        ///</summary>
        public bool IsEnable { get; set; }

        ///<summary>
        ///公开
        ///</summary>
        public bool IsPublic { get; set; }

    }

    ///<summary>
    ///项目信息 软件信息,示例商城
    ///</summary>
    public class ProjectInfoDto:EntityDto<int>
    {
        ///<summary>
        ///代码 一般作为Route使用
        ///</summary>
        [MaxLength(32)]
        public string Code { get; set; }

        ///<summary>
        ///名称 示例贴代码商城
        ///</summary>
        [MaxLength(32)]
        public string Name { get; set; }

        ///<summary>
        ///说明 这个项目简介说明
        ///</summary>
        [MaxLength(128)]
        public string Desc { get; set; }

        ///<summary>
        ///备注 内部备注
        ///</summary>
        [MaxLength(64)]
        public string Mark { get; set; }

        ///<summary>
        ///创建者
        ///</summary>
        [ColumnDataType("outer","userInfo","extendUser","id","userName")]
        public int UserId { get; set; }

        ///<summary>
        ///图标样式 示例Hui-iconfont-jifen
        ///</summary>
        [MaxLength(64)]
        public string Icon { get; set; }

        ///<summary>
        ///图标 LOGO,一般是自定义上传
        ///</summary>
        [MaxLength(128)]
        [PasteImage(1,"head","60*60")]
        public string Head { get; set; }

        ///<summary>
        ///主页 项目主页
        ///</summary>
        [MaxLength(256)]
        public string HomeUrl { get; set; }

        ///<summary>
        ///组织 率属于哪个组织
        ///</summary>
        [PasteOuter("companyInfo","extendCompany","id","name")]
        public int CompanyId { get; set; }

        ///<summary>
        ///排序
        ///</summary>
        public int Sort { get; set; }

        ///<summary>
        ///状态
        ///</summary>
        public bool IsEnable { get; set; }

        ///<summary>
        ///公开
        ///</summary>
        public bool IsPublic { get; set; }

    }

    ///<summary>
    ///项目信息 软件信息,示例商城
    ///</summary>
    public class ProjectInfoListDto:EntityDto<int>
    {
        ///<summary>
        ///代码 一般作为Route使用
        ///</summary>
        [MaxLength(32)]
        public string Code { get; set; }

        ///<summary>
        ///名称 示例贴代码商城
        ///</summary>
        [MaxLength(32)]
        public string Name { get; set; }

        ///<summary>
        ///说明 这个项目简介说明
        ///</summary>
        [MaxLength(128)]
        public string Desc { get; set; }

        ///<summary>
        ///备注 内部备注
        ///</summary>
        [MaxLength(64)]
        public string Mark { get; set; }

        ///<summary>
        ///创建者
        ///</summary>
        [ColumnDataType("outer","userInfo","extendUser","id","userName")]
        public int UserId { get; set; }

        ///<summary>
        ///图标样式 示例Hui-iconfont-jifen
        ///</summary>
        [MaxLength(64)]
        public string Icon { get; set; }

        ///<summary>
        ///图标 LOGO,一般是自定义上传
        ///</summary>
        [MaxLength(128)]
        [PasteImage(1,"head","60*60")]
        public string Head { get; set; }

        ///<summary>
        ///主页 项目主页
        ///</summary>
        [MaxLength(256)]
        public string HomeUrl { get; set; }

        ///<summary>
        ///组织 率属于哪个组织
        ///</summary>
        [PasteOuter("companyInfo","extendCompany","id","name")]
        public int CompanyId { get; set; }

        ///<summary>
        ///创建时间
        ///</summary>
        public DateTime CreateDate { get; set; }

        ///<summary>
        ///排序
        ///</summary>
        public int Sort { get; set; }

        ///<summary>
        ///状态
        ///</summary>
        public bool IsEnable { get; set; }

        ///<summary>
        ///公开
        ///</summary>
        public bool IsPublic { get; set; }

    }
	
	///<summary>
	/// 查询
	///</summary>
	public class InputQueryProjectInfo:InputSearchBase{
	
	}
 }

我的习惯是把一个数据表的所有Dto放一个文件,这样好找内容,不至于文件非常多!从上面可以看到在AddDto中是没有我们配置的UserId,CompanyId等字段的,符合预期!!!

再看看生成的ProjectInfoAppService.cs

using System.Collections.Generic;
using System;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
using PasteFormHelper;
using PasteDocument.Application.Contracts;

namespace PasteDocument.markmodels
{
    /// <summary>
    ///
    ///</summary>
    [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
    public class ProjectInfoAppService : PasteDocumentAppService
    {

        /// <summary>
        ///
        ///</summary>
        public ProjectInfoAppService():base()
        {

        }

        /// <summary>
        /// 读取AddDto的数据模型
        /// </summary>
        /// <returns></returns>
        [HttpGet]
		[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "add" })]
        public PasteBuilderHelper.VoloModelInfo ReadAddModel()
        {
            var _model = PasteBuilderHelper.ReadModelProperty<ProjectInfoAddDto>(new ProjectInfoAddDto());
            return _model;
        }

        /// <summary>
        /// 读取UpdateDto的数据模型
        /// </summary>
        /// <returns></returns>
        [HttpGet]
		[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "edit" })]
        public async Task<PasteBuilderHelper.VoloModelInfo> ReadUpdateModel(int id)
        {
			var _info = await _dbContext.ProjectInfo.Where(x => x.Id == id).AsNoTracking().FirstOrDefaultAsync();
            if (_info == null || _info == default)
            {
                throw new PasteCodeException("查询的信息不存在,无法执行编辑操作!");
            }
            var dto = ObjectMapper.Map<ProjectInfo, ProjectInfoUpdateDto>(_info);
            var _dataModel = PasteBuilderHelper.ReadModelProperty<ProjectInfoUpdateDto>(dto);
            return _dataModel;
        }

        /// <summary>
        /// 读取Dto的数据模型 用于查看详情
        /// </summary>
        /// <returns></returns>
        [HttpGet]
		[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "edit" })]
        public async Task<PasteBuilderHelper.VoloModelInfo> ReadDetailModel(int id)
        {
			var _info = await _dbContext.ProjectInfo.Where(x => x.Id == id).AsNoTracking().FirstOrDefaultAsync();
            if (_info == null || _info == default)
            {
                throw new PasteCodeException("查询的信息不存在,无法执行编辑操作!");
            }
            var dto = ObjectMapper.Map<ProjectInfo, ProjectInfoDto>(_info);
            var _dataModel = PasteBuilderHelper.ReadModelProperty<ProjectInfoDto>(dto);
            return _dataModel;
        }
		
		/// <summary>
        /// 读取ListDto的数据模型
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public PasteBuilderHelper.VoloModelInfo ReadListModel()
        {
            var _model = PasteBuilderHelper.ReadModelProperty<ProjectInfoListDto>(new ProjectInfoListDto());
			var _query_model = PasteBuilderHelper.ReadModelProperty(new InputQueryProjectInfo());
            if (_query_model != null)
            {
                _model.QueryProperties = _query_model.Properties;
            }
            return _model;
        }

        /// <summary>
        /// 获取
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<PagedResultDto<ProjectInfoListDto>> Page([FromQuery] InputQueryProjectInfo input)
        {
            var _query = _dbContext.ProjectInfo.Where(t => 1 == 1);
            var _pagedto = new PagedResultDto<ProjectInfoListDto>();
            if (input.page == 1)
            {
                _pagedto.TotalCount = await _query.CountAsync();
            }
            var dataList = await _query
                .OrderByDescending(x => x.Id)
                .Page(input.page, input.size)
                .AsNoTracking()
                .ToListAsync();
            if(dataList==null||dataList.Count == 0){
                throw new PasteCodeException("没有查询到数据",204);
            }
            var temList = ObjectMapper.Map<List<ProjectInfo>, List<ProjectInfoListDto>>(dataList);
            _pagedto.Items = temList;
            return _pagedto;
        }

        /// <summary>
        /// 添加一个
        ///</summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
		[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "add" })]
        public async Task<ProjectInfoDto> CreateItemAsync(ProjectInfoAddDto input)
        {
            var _userid = base.ReadCurrentAdminId();
            var newu = ObjectMapper.Map<ProjectInfoAddDto, ProjectInfo>(input);
            //添加自定义
            _dbContext.Add(newu);
            await _dbContext.SaveChangesAsync();
            var backinfo = ObjectMapper.Map<ProjectInfo, ProjectInfoDto>(newu);
            return backinfo;
        }
		
        /// <summary>
        /// 更新一个
        ///</summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ProjectInfoDto> UpdateItemAsync(ProjectInfoUpdateDto input)
        {
            var info = await _dbContext.ProjectInfo.Where(x => x.Id == input.Id).FirstOrDefaultAsync();
            if (info == null || info == default)
            {
                throw new PasteCodeException("需要查询的信息不存在", 404);
            }
            ObjectMapper.Map<ProjectInfoUpdateDto, ProjectInfo>(input, info);
            await _dbContext.SaveChangesAsync();
            var backinfo = ObjectMapper.Map<ProjectInfo, ProjectInfoDto>(info);
            return backinfo;
        }
		
        /// <summary>
        /// 更新状态,或者更新绑定
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="PasteCodeException"></exception>
        [HttpPost]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "state" })]
        public async Task<string> UpdateState(InputQueryUpdateState input)
        {
            if (!int.TryParse(input.id, out var _id))
            {
                throw new PasteCodeException("提供的参数id错误,无法继续执行");
            }
                //因为只有一个isenable,直接写
                var find = await _dbContext.ProjectInfo.Where(x => x.Id == _id).FirstOrDefaultAsync();
                if (find == null || find == default)
                {
                    throw new PasteCodeException("没有找到对应的操作对象,无法继续执行");
                }
                //需要自行实现
            return "暂未实现";
        }
		
		/// <summary>
        /// 删除数据,不需要删除的话注意注释这个 ListDto中附带disable标注忽略删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "del" })]
        public async Task<string> Remove(int id)
        {
            var info = await _dbContext.ProjectInfo.Where(x => x.Id == id).FirstOrDefaultAsync();
            if (info == null || info == default)
            {
                throw new PasteCodeException("需要查询的信息不存在", 404);
            }
            _dbContext.Remove(info);
            await _dbContext.SaveChangesAsync();
            return "删除成功";
        }
    }
}

生成成功后 执行Migration的部分 在这里插入图片描述 名字自己取,意思是生成哪个的变动等 然后我们启动项目后,去添加几个新的菜单 在这里插入图片描述 刷新后,就能在左侧的菜单中看到新的菜单项目了,你可以按照要求新增一个项目试试

在这里插入图片描述 至此,一个完整的CRUD就算搭建完成了

从整个项目来说,这个工作目前完成的量可能还不足1%

接下来我们将对这个项目进一步进行改造,让他符合我们的项目需求

我们下期见