极小的运行资源消耗,还支持一拖多运行,运行内存甚至低至100MB!
和业务服务没有层级上的关联,哪怕PasteSpider停止运行也不会影响你的业务服务运行
一键安装,图形操作点点点即可完成操作,附属服务的支持原汁原味,简单易上手
支持拆分多环境运行,工厂,测试两不误,还可以根据环境配置适应不一样的配置信息
从项目角度进行服务容器管理,支持自动升级,扩容,缩减,状态报表,键值配置,自动路由等
通过配置支持服务环境级别的自动提交构建,一键提交代码后即可自动发布服务和路由等
接之前的(一),我们继续
如上图,表格的默认菜单有左上方的“新增”,表格中的编辑和删除
其实着3个都是可以配置的,也就是配置关闭!
///<summary>
///用户信息
///</summary>
[ColumnDataType("disable","add","edit","del")]
public class UserInfoListDto : EntityDto<int>
{
///<summary>
///用户名
///</summary>
public string UserName { get; set; }
}
如果哪一个要保留,就对应的设置为空,上面的代码时新增,编辑,删除都禁用,也就是都不显示!
在新增按钮的后方,我们注意到有2个信息,一个是粗体的,一个是普通的,这个就是当前表的描述,PasteForm中定义,一个对象的文档,按照空格拆分,前面的为名称,后面的为描述(placeholder),数据的说明就是这么拆分而来的!
这个有点特殊,其实就是对应字段添加属性orderby
///<summary>
///排序
///</summary>
[ColumnDataType("orderby", "Sort","Sort desc")]
public int Sort { get; set; }
为了把orderby在查询的时候传递给API端,需要在搜索的模型中添加orderby
///<summary>
/// 查询
///</summary>
public class InputQueryRoleInfo:InputSearchBase{
/// <summary>
/// 父级ID 基于父级ID查询
/// </summary>
[PasteOuter("roleInfo")]
public int FatherId { get; set; }
/// <summary>
/// 角色ID 查询某一个角色拥有多少权限
/// </summary>
[ColumnDataType("query","gradeId")]
public int GradeId { get; set; }
/// <summary>
/// 权限类型 基于权限类型查询
/// </summary>
[ColumnDataType("select", "[{\"name\":\"全部\",\"value\":-1,\"selected\":true},{\"name\":\"权限\",\"value\":0},{\"name\":\"菜单\",\"value\":1},{\"name\":\"按钮\",\"value\":2}]")]
public int RoleType { get; set; }
/// <summary>
/// 排序 这个字段是隐藏的
/// </summary>
[ColumnDataType("hidden")]
public string orderby { get; set; } = "Id desc";
}
属性orderby中有2个参数,就是点击这个字段的升序和降序,也就是说你可以在Name中配置基于Id排序,有些时候需要这种功能,比如外表!
表格的快捷操作,我认为一般是bool的模式,如果配置了属性switch,则会显示Switch的模式,否则就是显示true/false!
点击后,会往API中提交信息
/// <summary>
/// 更新状态,或者更新绑定
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
[HttpPost]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "auth" })]
public async Task<string> UpdateState(InputQueryUpdateState input)
{
if (!int.TryParse(input.id, out var _id))
{
throw new PasteCodeException("提供的参数id错误,无法继续执行");
}
if (input.search.Contains("model=bind"))
{
if(input.name!= "extendBind")
{
throw new PasteCodeException("无效的操作,不支持当前情况下执行此操作!");
}
var _gradeid = 0;// 需要写一个扩展,从参数中提取某一个 先假设下,后面补充
var _querys = input.search.ToQuerys();
if (_querys != null)
{
if (_querys.ContainsKey("gradeId"))
{
int.TryParse(_querys["gradeId"], out _gradeid);
}
}
if (_gradeid == 0)
{
throw new PasteCodeException("参数错误,没有获取到gradeId!");
}
var find = await _dbContext.GradeRole.Where(x => x.GradeId == _gradeid && x.RoleId == _id).FirstOrDefaultAsync();
if (find == null || find == default)
{
if (input.state)
{
_dbContext.Add(new GradeRole { GradeId = _gradeid, RoleId = _id });
await _dbContext.SaveChangesAsync();
}
else
{
return "状态一致,不需要变更!";
}
}
else
{
if (!input.state)
{
_dbContext.Remove(find);
await _dbContext.SaveChangesAsync();
}
else
{
return "状态一致,不需要变更!";
}
}
return $"更改{input.name}绑定状态成功";
}
else
{
//因为只有一个isenable,直接写
var find = await _dbContext.RoleInfo.Where(x => x.Id == _id).FirstOrDefaultAsync();
if (find == null || find == default)
{
throw new PasteCodeException("没有找到对应的操作对象,无法继续执行");
}
if (find.IsEnable != input.state)
{
find.IsEnable = input.state;
await _dbContext.SaveChangesAsync();
}
return $"修改{input.name}状态成功";
}
}
这里面要基于实际情况,比如权限判断,是否是设置这个name的状态,还是绑定等!主打的一个就是API修改写法实现功能!
有些时候我们要显示一些外表的某一个字段,比如创建者,一般的数据是userid,而显示的时候我们希望显示的是userid对应的user的username字段,这就需要outerdisplay这个属性
ListDto中用于外表的显示,比如有字段cateInfoId,对应的ExtendCateInfo要标记为outerdisplay,args2配置为extendCateInfo?.name || ‘’,否则会显示为[object object]
字段 | 类型 | 示例 | 说明 | ||
---|---|---|---|---|---|
args1 | 字符 | cateInfoId | 表示这个字段的值,一般不显示 | ||
args2 | 字符 | extendCateInfo?.name \ | \ | ‘’ | 表示显示的名称,友好名称,需要后端支持,在前端会处理成.display |
比如我的
///<summary>
///父级ID
///</summary>
[ColumnDataType("outerdisplay","","extendFather?.name || ''")]
public int FatherId { get; set; }
/// <summary>
/// 父级信息,为了给上一个字段FatherId显示使用,你也可以把FatherId隐藏,只显示这个字段,或者2个都显示
/// </summary>
[ColumnDataType("hidden")]
public RoleShortModel ExtendFather { get; set; }
一般的在最后的一列中有些按钮,比如最上图的添加子集等,PasteForm中配置了4种菜单模式,一般菜单,菜单盒子里的菜单,条件菜单,盒子里面的条件菜单
/// <summary>
/// 普通菜单
/// </summary>
[ColumnDataType("menu", "菜单一", "open_window('查阅用户带参','./index.html?path=userInfo&xxid={{:=item.id}}');", "Hui-iconfont-menu")]
public string Menu2 { get; set; }
/// <summary>
/// 普通条件菜单
/// </summary>
[ColumnDataType("ifmenu", "item.age==7", "<a href=\"javascript:;\" onclick=\"open_window(`111`,`./index.html?path=userInfo&goid={{:=item.id}}`)\">条件1</a>", "")]
public string Menu3 { get; set; }
/// <summary>
/// 菜单盒子菜单
/// </summary>
[ColumnDataType("menu", "菜单二", "open_window('查阅用户带参','./index.html?path=userInfo&xxid={{:=item.id}}');", "Hui-iconfont-menu","box")]
public string Menu5 { get; set; }
/// <summary>
/// 菜单盒子中的条件菜单
/// </summary>
[ColumnDataType("ifmenu", "item.age==8", "<a href=\"javascript:;\" onclick=\"open_window(`222`,`./index.html?path=userInfo&goid={{:=item.id}}`)\">条件2</a>", "box")]
public string Menu4 { get; set; }
如上图就是配置的菜单,所以详情也是使用菜单配置的!
有些时候我们希望表格中的列的显示使用自己的模式,比如要多个字段拼接,或者要加其他样式等,这个时候需要使用htmltemplate的属性
/// <summary>
/// 重新载入
/// </summary>
[ColumnDataType("htmltemplate", "<a href=\"javascript:;\" onclick=\"global_route_reload({{:=item.id}});\" title=\"更新到nginx的文件夹中,并执行nginx -t && nginx -s reload\">重新载入</a>")]
public int Menu2 { get; set; }
效果图如下
看上面的代码,其实就是自定义写Html,基于这个你就可以实现不一样的显示和功能了!
如果说表格和表格的窗体都不满意,你也可以自定义,也就是自己写template,然后在对应的XXXListDto中的Class写入属性template
作为有些表的特殊布局,就是自定义布局表格部分的内容,包括表头和表身2个部分,注意需要实现选择的功能,除非这个表用不到选择这个功能
文件存放于pasteform/template.html,就只有一个template.html页面,里面的都是模板代码
字段 | 类型 | 示例 | 说明 |
---|---|---|---|
args1 | 字符 | template_user_head | 表格的表头部分的script的id |
args2 | 字符 | template_user_body | 表格的表身部分的模板的script的id |
这个其实查看下js的源码就可以知道了,如下
/**
* 查询模型信息
**/
function QueryModel() {
_apiget(`/api/app/${_classPath}/readListModel`, true, (c, o) => {
if (c == 200) {
if (o.title) {
$(".ppbody .st").find(".sn").html(o.title);
this.document.title = o.title;
}
if (o.desc) {
$(".ppbody .st").find(".idesc").html(o.desc);
}
//表头的模板内容
var _template_head_html = null;
_globadataProperties = o.properties;
//模型处理,如何显示外表 比如cate.name
HandlerModelColumn(o.properties);
//class模型的属性列表
if (o.attributes) {
_globadataAttributes = o.attributes;
o.attributes.forEach(_attribute => {
if (_attribute.name == 'disable') {
if (_attribute.args1) {
_config.disable_add = true;
$(".btnadd").hide();
}
if (_attribute.args2) {
_config.disable_edit = true;
}
if (_attribute.args3) {
_config.disable_del = true;
}
}
if (_attribute.name == "template") {
if (_attribute.args1) {
_template_head_html = $(`#${_attribute.args1}`).html();
}
if (_attribute.args2) {
_template_body_html = $(`#${_attribute.args2}`).html();
}
}
});
}
if (_template_head_html == null) {
_template_head_html = $("#template_header").html();
}
var _modelhtml = template(_template_head_html, { list: o.properties, config: _config });
//一级模型 转化成 二级模型
if (_template_body_html == null) {
var _template_body = $("#template_body").html();
var _bodyhtml = template(_template_body, { list: o.properties, config: _config });
_template_body_html = _bodyhtml.replace(/{{/g, '<%').replace(/}}/g, '%>');
}
$(".table").find("thead").html(_modelhtml);
//处理查询项
if (o.queryProperties) {
_globdataQueryProperties = o.queryProperties;
HandlerQueryItem(o.queryProperties);
} else {
_readpagedata(1);
}
//读取数据
}
});
}
注意看上方的_template_head_html和_template_body_html
在外表的模式下,或者outer属性的时候,我们需要把某一个表作为可选对象,先看下outer属性说明
表示一个值需要从外表获取,编辑的时候如何显示? 比如fatherId,extendRole
字段 | 类型 | 示例 | 说明 |
---|---|---|---|
args1 | 字符 | cateInfo | 外表的名称,对应模板的path,或者路径,路径一定附带了/字符示例./abc.html |
args2 | 字符 | extendCates | 表示显示的数据,需要和下面2个配合,是一个当前的扩展,目标数组要配置hidden |
args3 | 字符 | id | 获取返回对象的属性,一般为id |
args4 | 字符 | name | id的友好名称显示,这里指的是外表,比如cateId,需要打开catelist页面,选择后,返回cate,则name作为友好显示,id作为实际值 |
在表格页面中,会发生outer的一半在搜索项中,比如选择父级ID查询子集的,点击后如下
表格页面的介绍先到这,我们下一期介绍表单页面!