PasteBuilder作为代码生成器,他的局限就是需要依赖于ABP vNext的项目模板,可以参考PasteTemplate的项目架构!
今天要说的是PasteBuilder的另一个功能,模板自定义!!!
1.在xxx.Domain的项目中创建文件夹/template/,把模板文件放在这个文件夹下面,比如
index.html:生成后存放于XXX.HttpApi.Host项目中的wwwroot/manage/{model}/index.html
view.html:生成后存放于XXX.HttpApi.Host项目中的wwwroot/manage/{model}/view.html
add.html:生成后存放于XXX.HttpApi.Host项目中的wwwroot/manage/{model}/add.html
edit.html:生成后存放于XXX.HttpApi.Host项目中的wwwroot/manage/{model}/edit.html
vueindex.html:生成后存放于XXX.HttpApi.Host项目中的wwwroot/manage/{model}/index.vue
vueadd.html:生成后存放于XXX.HttpApi.Host项目中的wwwroot/manage/{model}/add.vue
vueview.html:生成后存放于XXX.HttpApi.Host项目中的wwwroot/manage/{model}/view.vue
server.html:生成后存放于XXX.Application项目中的{model}/{table}AppService.cs 作为表对应的主服务文件
注意:要生成前端文件,需要你在XXX.HttpApi.Host项目下,创建文件夹/wwwroot/manage/,否则前端文件是不会创建的!
上面的8个模板文件,内容要如何写呢???
PasteBuilder里面是使用dotliquid实现的,可以查看这个模板的文档
http://dotliquidmarkup.org/
https://shopify.github.io/liquid/tags/template/
代码运行的时候,会把一个TapClassModel信息推送到模板文件中,模板文件里面接收这个信息,然后基于这个信息实现模板代码,对象信息为
/// <summary>
/// 对象信息 字段信息
/// </summary>
public class TapClassModel : Drop
{
/// <summary>
/// 命名空间,项目名称
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// 模型名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 首字母小写
/// </summary>
public string lowerName { get {
return CommonHelper.FirstCharToLower(Name);
}
}
/// <summary>
/// 模型中文名称
/// </summary>
public string CnName { get; set; }
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 文件夹名称
/// </summary>
public string DirName { get; set; }
/// <summary>
/// 附带的属性
/// </summary>
public List<ClassProperty> ClassPropertys { get; set; }
/// <summary>
/// uint long int
/// </summary>
public string EntityType { get; set; } = "int";
/// <summary>
/// 显示的注释 注释summary里面的内容
/// </summary>
public string DisplayStr { get; set; }
/// <summary>
/// 作废,使用CnName替代
/// </summary>
public string DisplayShort { get; set; }
/// <summary>
/// 是否是读写分离模式
/// </summary>
public bool IsWriteReadModel { get; set; } = false;
}
/// <summary>
/// 对象属性 字段的属性
/// </summary>
public class ClassProperty : Drop
{
/// <summary>
/// 属性类型 可能带了Dto
/// </summary>
public string PropertyType { get; set; }
/// <summary>
/// 上面这个的基类,不带Dto,干净的个体
/// </summary>
public string PropertyBase { get; set; }
/// <summary>
/// 属性名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 首字母小写
/// </summary>
public string lowerName
{
get
{
return CommonHelper.FirstCharToLower(Name);
}
}
/// <summary>
/// 属性中文名称
/// </summary>
public string CnName { get; set; }
/// <summary>
/// 是否必填
/// </summary>
public bool Required { get; set; } = false;
/// <summary>
/// 最大长度 -1表示不启用MaxLength
/// </summary>
public int MaxLength { get; set; } = -1;
/// <summary>
/// 默认值
/// </summary>
public string DefaultValue { get; set; } = "";
/// <summary>
/// 属性特性
/// </summary>
public List<ClassAttribute> ClassAttributes { get; set; }
/// <summary>
/// 是否是其他的模型
/// </summary>
public bool boolOtherModel { get; set; }
/// <summary>
/// 注释是啥
/// </summary>
public string PropertyDisplayStr { get; set; }
/// <summary>
///
/// </summary>
public string DisplayStr { get { return PropertyDisplayStr; } }
/// <summary>
/// 是否是枚举类型
/// </summary>
public bool IsEnumClass { get; set; } = false;
}
/// <summary>
/// 属性特性
/// </summary>
public class ClassAttribute:Drop
{
/// <summary>
/// 属性名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 属性值
/// </summary>
public string Value { get; set; }
/// <summary>
/// 属性组合字符串
/// </summary>
public string NameValue { get; set; }
}
比如你可以在index.html的模板文件中这样写
{% Namespace %}
这样最后生成的代码在对应的模块中的index.html应该可以看到输出了你当前项目的命名空间!
具体的代码如何写,你可以参考PasteSpider里面的Nginx路由代码,他们使用的模板技术是一样的,以下是Nginx路由模板!
# --- -- - https模式 - -- ---
{% for item in services -%}
{% assign findapp=false -%}
{% for model in item.models -%}
{% for app in model.apps -%}
{% assign findapp=true -%}
{% endfor -%}
{% endfor -%}
{% if findapp == true %}
upstream {{ Code }}ser{{item.Code}} {
{% for model in item.models -%}
{% for app in model.apps -%}
server {{ app.Address }}:{{ app.listen_ports }} weight=10;
{% endfor -%}
{% endfor -%}
}
{% endif -%}
{% endfor -%}
server {
#listen 80;
listen 443 ssl; # managed by Certbot
server_name {{ Host }};
ssl_certificate /etc/letsencrypt/live/{{ Host }}/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/{{ Host }}/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
{% for item in services -%}
location /api/{{ item.Code }} {
proxy_pass http://{{ Code }}ser{{ item.Code }}/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
{% endfor -%}
location / {
root /spider/static/soft/web/;
index index.html index.htm;
}
}
如果你在使用过程中遇到问题,可以在QQ群中找到帮助! QQ群:296245685
以下是/template/server.html的一个范本
using System.Collections.Generic;
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;
namespace {{ Namespace }}.{{ DirName }}
{
/// <summary>
///
///</summary>
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "root", "root" })]
public class {{ Name }}AppService : IUserAppService
{
private readonly I{{ Namespace }}DbContext _dbContext;
/// <summary>
///
///</summary>
///<param name="dbContext"></param>
public {{ Name }}AppService(I{{ Namespace }}DbContext dbContext):base()
{
_dbContext = dbContext;
}
/// <summary>
/// 按页获取
///</summary>
///<param name="page">页码</param>
///<param name="size">页大小</param>
/// <returns></returns>
[HttpGet]
public async Task<PagedResultDto<{{ Name }}ListDto>> GetListAsync(int page = 1, int size = 20)
{
var query = _dbContext.{{ Name }}.Where(t => 1 == 1).OrderByDescending(xy => xy.Id);
var _pagedto = new PagedResultDto<{{ Name }}ListDto>();
if (page == 1)
{
_pagedto.TotalCount = await query.CountAsync();
}
var userList = await query.Page(page, size).ToListAsync();
var temList = ObjectMapper.Map<List<{{ Name }}>, List<{{ Name }}ListDto>>(userList);
_pagedto.Items = temList;
return _pagedto;
}
/// <summary>
/// 根据ID获取单项
///</summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet]
public {{ Name }}Dto GetByIdAsync(int id)
{
var query = _dbContext.{{ Name }}.Where(t => t.Id == id).AsNoTracking().FirstOrDefault();
if (query == null || query == default)
{
throw new {{ Namespace }}Exception("没有找到这样的信息", 404);
}
var temList = ObjectMapper.Map<{{ Name }}, {{ Name }}Dto>(query);
return temList;
}
/// <summary>
/// 根据ID获取待更新单项信息
///</summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet]
public {{ Name }}UpdateDto GetInfoForUpdateAsync(int id)
{
var query = _dbContext.{{ Name }}.Where(t => t.Id == id).AsNoTracking().FirstOrDefault();
if (query == null || query == default)
{
throw new {{ Namespace }}Exception("没有找到这样的信息", 404);
}
var temList = ObjectMapper.Map<{{ Name }}, {{ Name }}UpdateDto>(query);
return temList;
}
/// <summary>
/// 添加一个
///</summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public async Task<{{ Name }}Dto> CreateItemAsync({{ Name }}AddDto input)
{
var _userid = base.CurrentUserId();
var newu = ObjectMapper.Map<{{ Name }}AddDto, {{ Name }}>(input);
//添加自定义
_dbContext.Add(newu);
{% for item in ClassPropertys -%}
{% if item.Name == "create_time" -%}
newu.create_time = DateTime.Now().ToUnixTimeSeconds();
{% endif -%}
{% if item.Name == "CreateDate" -%}
newu.CreateDate = DateTime.Now();
{% endif -%}
{% if item.Name == "CreateUserId" -%}
newu.CreateUserId = _userid;
{% endif -%}
{% endfor -%}
await _dbContext.SaveChangesAsync();
var backinfo = ObjectMapper.Map<{{ Name }}, {{ Name }}Dto>(newu);
return backinfo;
}
/// <summary>
/// 更新一个
///</summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public async Task<{{ Name }}Dto> UpdateItemAsync({{ Name }}UpdateDto input)
{
var info = await _dbContext.{{ Name }}.Where(x => x.Id == input.Id).FirstOrDefaultAsync();
if (info == null || info == default)
{
throw new UserFriendlyException("需要查询的信息不存在", "404");
}
ObjectMapper.Map<{{ Name }}UpdateDto, {{ Name }}>(input, info);
await _dbContext.SaveChangesAsync();
var backinfo = ObjectMapper.Map<{{ Name }}, {{ Name }}Dto>(info);
return backinfo;
}
}
}
如果需要自己编写模板,可以实际开发的表中,然后把对应的表名等按照实际情况替换即可,然后在特定的地方加上for循环或者if判断即可!
index.html的范本
<!--inherits RazorEngine.Templating.TemplateBase<CodeBuilder.Models.TemplateModels.DtoFileModel>-->
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit|ie-comp|ie-stand">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link rel="stylesheet" type="text/css" href="../static/h-ui/css/H-ui.min.css" />
<link rel="stylesheet" type="text/css" href="../static/h-ui.admin/css/H-ui.admin.css" />
<link rel="stylesheet" type="text/css" href="../lib/Hui-iconfont/1.0.8/iconfont.css" />
<link rel="stylesheet" type="text/css" href="../static/h-ui.admin/skin/default/skin.css" id="skin" />
<link rel="stylesheet" type="text/css" href="../static/h-ui.admin/css/style.css" />
<title>{{ Name }}</title>
</head>
<body>
<div class="page-container">
<div class="searcharea">
<div class="st">
<div class="sn">{{ cn_name }}</div>
<div class="idesc">{{ Description }}</div>
</div>
<form id="formsearch">
<div class="searchbox">
<!--<div class="timearea" style="display:flex;border:1px solid #888;border-radius: 4px;">
<select name="timetype" style="width:120px;border:none;outline:none;text-align: center;">
<option value="empty">订单时间</option>
<option value="bdate">动账日期</option>
<option value="cdate">消耗日期</option>
<option value="odate">到期日期</option>
</select>
<input type="text" name="daterange" readonly datas="" datae="" value=""
style="position:relative;flex:1;outline:none;border:none;border-radius: 4px; text-align: center;"
id="picker_date_range">
</div>-->
<input type="text" class="inputword" name="word" placeholder="输入关键字查询" value="">
<input type="button" class="btnsearch" onclick="_readpagedata(1);" value="查询">
</div>
</form>
<a class="btn btn-success radius r" style="line-height:1.6em;margin-top:3px"
href="javascript:location.replace(location.href);" title="刷新"><i class="Hui-iconfont"></i></a>
</div>
<div class="suminfo">
<span class="idesc"></span>
</div>
<div class="mt-20">
<table class="table table-border table-bordered table-bg table-hover table-sort table-responsive">
<thead>
<tr class="text-c">
{% for ione in ClassPropertys -%}
<th>{{ ione.cn_name }}</th>
{% endfor -%}
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div id="pagenumarea" class="pagenumarea"></div>
<script type="text/html" id="templateitem">
<% list.forEach(item=>{ %>
<tr>
{% for ione in ClassPropertys -%}
<td><%:=item.{{ ione.lower_name }}%></td>
{% endfor -%}
<td><a title="编辑" href="javascript:;" onclick="_showedit('编辑','view.html','<%:=item.id%>','80%','80%')" class="ml-5" style="text-decoration:none"><i class="Hui-iconfont"></i></a></td>
</tr>
<% }) %>
</script>
</div>
<!--_footer 作为公共模版分离出去-->
<script type="text/javascript" src="../lib/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="../lib/layer/2.4/layer.js"></script>
<script type="text/javascript" src="../static/h-ui/js/H-ui.min.js"></script>
<script type="text/javascript" src="../lib/jquery.contextmenu/jquery.contextmenu.r2.js"></script>
<script type="text/javascript" src="../static/h-ui.admin/js/H-ui.admin.js"></script>
<!--/_footer 作为公共模版分离出去-->
<!--请在下方写此页面业务相关的脚本-->
<!-- <script type="text/javascript" src="../lib/My97DatePicker/4.8/WdatePicker.js"></script> -->
<!--<script type="text/javascript" src="../lib/pickerdaterange/dateRange.js"></script>
<link rel="stylesheet" href="../lib/pickerdaterange/dateRange.css">-->
<script type="text/javascript" src="../lib/api.js"></script>
<link href="../lib/page.min.css" rel="stylesheet" />
<script type="text/javascript" src="../lib/page.min.js"></script>
<script type="text/javascript" src="../lib/template.js"></script>
<script>
var pageobject = null;
var isedit = true;
var _datalist = null;
var extendSum = null
$(window).ready(function () {
_readpagedata(1);
$(".inputword").keyup(function (event) {
if (event.keyCode == 13) {
_readpagedata(1);
}
});
//var dateRange = new pickerDateRange('picker_date_range', {
// isTodayValid: false,
// defaultText: ' 至 ',
// inputTrigger: 'input_trigger_demo3',
// success: function (obj) {
// $("#picker_date_range").attr('datas', obj.startDate);
// $("#picker_date_range").attr('datae', obj.endDate);
// },
// clear: function () {
// }
//});
});
function _readpagedata(page) {
if (page == 1) {
$("#pagenumarea").empty();
}
var _info = $("#formsearch").parseForm();
_info.page = page;
_info.size = 20;
//if (_info.daterange) {
// if (_info.daterange.length > 0) {
// _info.sdate = $("[name=daterange]").attr('datas') + " 00:00:00";
// _info.edate = $("[name=daterange]").attr('datae') + " 00:00:00";
// }
//}
// _apiget('/api/app/{{ lowerName }}',true,function(code,obj){
_apipost("/api/app/{{ lowerName }}/list", true, JSON.stringify(_info), function (code, obj) {
if (code == 200 && obj.items != null && obj.items.length > 0) {
var datas = obj.items;
_showpagedata(datas, extendSum);
if (pageobject == null || page == 1) {
pageobject = new Page({
el: '#pagenumarea',
nums: obj.totalCount,//总数
counts: 20,//每页大小
defaultPage: 1,
showHeadFoot: false, // 显示首页尾页
head: '首', // 更改首页文字
foot: '尾', // 更改尾页文字
jumpToOrder: true, // 跳转到指定页
showNowAndAll: true, // 当前页/共几页
prev: "上一页",
next: "下一页",
clickEvent: function (currectPage, _this) {
_readpagedata(currectPage);
}
});
}
}
});
}
function _showpagedata(datas,total) {
_datalist = datas;
var _ahtml=$("#templateitem").html();
var _heml=template(_ahtml,{list:datas, extend: total});
$("table").find("tbody").html(_heml);
}
</script>
</body>
</html>
view.html的范本
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit|ie-comp|ie-stand">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link rel="Bookmark" href="/favicon.ico">
<link rel="Shortcut Icon" href="/favicon.ico" />
<!--[if lt IE 9]>
<script type="text/javascript" src="../lib/html5shiv.js"></script>
<script type="text/javascript" src="../lib/respond.min.js"></script>
<![endif]-->
<link rel="stylesheet" type="text/css" href="../static/h-ui/css/H-ui.min.css" />
<link rel="stylesheet" type="text/css" href="../static/h-ui.admin/css/H-ui.admin.css" />
<link rel="stylesheet" type="text/css" href="../lib/Hui-iconfont/1.0.8/iconfont.css" />
<link rel="stylesheet" type="text/css" href="../static/h-ui.admin/skin/default/skin.css" id="skin" />
<link rel="stylesheet" type="text/css" href="../static/h-ui.admin/css/style.css" />
<!--[if IE 6]>
<script type="text/javascript" src="../lib/DD_belatedPNG_0.0.8a-min.js" ></script>
<script>DD_belatedPNG.fix('*');</script>
<![endif]-->
<!--/meta 作为公共模版分离出去-->
<title>{{ cn_name }}</title>
</head>
<body>
<div class="newarticle">
<form class="form newform" id="form-article-add">
<div class="row cl" style="display:none;">
<label class="form-label"><span class="c-red">*</span>ID标识:</label>
<div class="formControls">
<input type="number" class="input-number" value="0" placeholder="" name="id" readonly>
</div>
</div>
{% for ione in ClassPropertys -%}
<div class="row cl">
<label class="form-label">{{ ione.cn_name }}:</label>
<div class="formControls">
{% if ione.property_base == "int" || ione.property_base == "long" -%}
<input type="number" class="input-number" value="" placeholder="{{ ione.display_str }}" name="{{ ione.lower_name }}">
{% elseif ione.property_base == "bit" || ione.property_base == "bool" -%}
<input type="checkbox" class="input-checkbox mui-switch-anim mui-switch" name="{{ ione.lower_name }}">
{% else -%}
<input type="text" class="input-text" value="{{ ione.default_value }}" placeholder="{{ ione.display_str }}" name="{{ ione.lower_name }}" {% if ione.required -%} required {% endif -%} {% if ione.max_length > 0 -%} maxlength="{{ ione.max_length }}"{% endif -%}>
{% endif -%}
</div>
</div>
{% endfor -%}
<div class="row cl">
<div class="rowbtn">
<input onClick="_funcAdd();" class="formbtnsubmit btn-primary" type="button" value="保存">
</div>
</div>
</form>
</div>
<!--_footer 作为公共模版分离出去-->
<script type="text/javascript" src="../lib/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="../lib/layer/2.4/layer.js"></script>
<script type="text/javascript" src="../static/h-ui/js/H-ui.min.js"></script>
<script type="text/javascript" src="../static/h-ui.admin/js/H-ui.admin.js"></script>
<!--/_footer /作为公共模版分离出去-->
<!--请在下方写此页面业务相关的脚本-->
<script src="../lib/api.js"></script>
<script type="text/javascript">var iseditemodel = false;
$(function () {
//获取参数中的ID
var _id = 0;
var uidstr = _apigetquery("id");
if (uidstr != null) {
_id = parseInt(uidstr);
iseditemodel = true;
if (_id == 0) {
iseditemodel = false;
}
} else {
iseditemodel = false;
}
if (iseditemodel) {
$(".styleadd").hide();
_loaditem(_id);
} else {
$(".styleedit").hide();
}
});
function _loaditem(userid) {
//读取角色列表
_apiget("/api/app/{{ lower_name }}/" + userid + "/infoForUpdate", true, function (code, obj) {
if (code == 200) {
_bindelement(obj);
}
});
}
function _funcAdd() {
if (!_beforeValidity("#form-article-add")) {
return;
}
var dataobj = $("#form-article-add").parseForm();
if (iseditemodel) {
_apipost("/api/app/{{ lower_name }}/updateItem", false, JSON.stringify(dataobj), function (code, obj) {
if (code == 200) {
layer.msg("更新成功", { time: 1500 }, function () {
var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
parent.layer.close(index); // 关闭layer
});
}
});
} else {
_apipost("/api/app/{{ lower_name }}/item", false, JSON.stringify(dataobj), function (code, obj) {
if (code == 200) {
layer.msg("新增成功", { time: 1500 }, function () {
var index = parent.layer.getFrameIndex(window.name); //获取窗口索引
parent.layer.close(index); // 关闭layer
});
}
});
}
}</script>
<!--/请在上方写此页面业务相关的脚本-->
</body>
</html>
有没有vue的模板?