如何优雅的实现CRUD,包含微信小程序,API,HTML的表单(一)

前言

        在开发实际项目中,其实CRUD的代码量并不小,最近要做一个小程序项目,由于涉及表单的东西比较多,就萌生了一个想法,小程序的写法不是和VUE类似,就是数据绑定,模块么!那就来一个动态表单,动态数据!

合理架构

        了解ABP框架的,应该比较好理解Dto的好处,就是XXX.Application.Contracts,比如用户表可以引申出几个模型!

UserInfo:用户的原生表,相当于数据库表的结构

UserInfoDto:用户表的详细,一般会做一些数据处理,数据补充,外表的扩展等,比如会显示角色信息,会对密码做脱敏处理等

UserInfoUpdateDto:更新用户表的时候,哪些字段要更新,不要更新的字段注释掉,或者删除!

UserInfoAddDto:新建用户的时候的数据模型,同理不需要的删除或者注释掉!

对外的数据模型一般是Dto,比如上方的Dto都把PassWord这个字段注释掉,那么对外就不会泄漏密码了,然后他们之间使用ObjectMapper进行数据映射转化!

读取XXXDto的属性

        如果我们要搞全自动的,那么就要知道Dto的属性,比如这个模型有多少个字段,各叫啥名字,有没有啥限定(最大字符长度,是否必填,默认值等!)

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;namespace PasteTemplate.Application
{/// <summary>/// /// </summary>public static class PasteBuilderHelper{/// <summary>/// 读取某一个模型的规则/// </summary>/// <typeparam name="T"></typeparam>/// <param name="_new"></param>public static VoloModelInfo ReadModelProperty<T>(T _new){var _classModel = new VoloModelInfo();var _classType = typeof(T);var _bases = _classType.GetBaseClasses();if (_bases != null){foreach (var _base in _bases){if (_base.FullName != "System.Object"){if (_base.GenericTypeArguments != null){foreach (var _arg in _base.GenericTypeArguments){switch (_arg.Name){case "Int64":{_classModel.KeyType = "long";}break;case "String":{_classModel.KeyType = "string";}break;case "Guid":{_classModel.KeyType = "Guid";}break;}}}}}}string assemblyPath = Assembly.GetExecutingAssembly().Location;var _path = Path.GetDirectoryName(assemblyPath);var _name = Assembly.GetExecutingAssembly().GetName().Name;XDocument xmlDoc = null;//T: 表示类型 //P: 表示字段var xmlDocumentationPath = $@"{_path}\{_name.Replace(".Application", ".Application.Contracts").Replace("HttpApi.Host", "Application.Contracts")}.xml";if (System.IO.File.Exists(xmlDocumentationPath)){xmlDoc = XDocument.Load(xmlDocumentationPath);XElement typeElement = xmlDoc.Descendants("member").FirstOrDefault(member => member.Attribute("name")?.Value == $"T:{_classType.FullName}");if (typeElement != null){var _classSummary = typeElement.Element("summary")?.Value;if (!string.IsNullOrEmpty(_classSummary)){_classSummary = _classSummary.Replace("\r\n", "").Trim();_classModel.Summary = _classSummary;}}}var _pro_list = new List<VoloModelProperty>();foreach (var _property in _classType.GetProperties()){var _cpro = new VoloModelProperty();if (xmlDoc != null){var _pro_full = $"P:{_classType.FullName}.{_property.Name}";XElement typeElement = xmlDoc.Descendants("member").FirstOrDefault(member => member.Attribute("name")?.Value == _pro_full);if (typeElement != null){var _summary = typeElement.Element("summary")?.Value;if (!String.IsNullOrEmpty(_summary)){_summary = _summary.Replace("\r\n", "").Trim();_cpro.Summary = _summary;}}}_cpro.Name = _property.Name.FirstLetterToLower();_cpro.Type = _property.PropertyType.Name;_cpro.Default = _property.GetValue(_new)?.ToString();var attributes = _property.GetCustomAttributes<Attribute>();var _attributes = new List<VoloModelAttribute>();foreach (var _attribute in attributes){var _attri = new VoloModelAttribute();if (_attribute.GetType() == typeof(MaxLengthAttribute)){var _bute = (MaxLengthAttribute)_attribute;_attri.Name = "MaxLength";_attri.ErrorMessage = _bute.ErrorMessage;_attri.Args1 = _bute.Length.ToString();}if (_attribute.GetType() == typeof(RequiredAttribute)){var _bute = (RequiredAttribute)_attribute;_attri.Name = "Required";}if (_attribute.GetType() == typeof(RangeAttribute)){var _bute = (RangeAttribute)_attribute;_attri.Name = "Range";_attri.Args1 = _bute.Minimum.ToString();_attri.Args2 = _bute.Maximum.ToString();}if (_attribute.GetType() == typeof(RegularExpressionAttribute)){var _bute = (RegularExpressionAttribute)_attribute;_attri.Name = "RegularExpression";_attri.Args1 = _bute.Pattern.ToString();}if (!String.IsNullOrEmpty(_attri.Name)){_attributes.Add(_attri);}}if (_attributes.Count > 0){_cpro.Attributes = _attributes;}if (!String.IsNullOrEmpty(_cpro.Summary)){_cpro.Title = _cpro.Summary.Split(' ')[0];if (_cpro.Summary.Contains(" ")){_cpro.Desc = _cpro.Summary.Split(' ')[1];}}_pro_list.Add(_cpro);}_classModel.Properties = _pro_list;if (!String.IsNullOrEmpty(_classModel.Summary)){_classModel.Title = _classModel.Summary.Split(' ')[0];if (_classModel.Summary.Contains(" ")){_classModel.Desc = _classModel.Summary.Split(' ')[1];}}var _abc = "";_abc.ToLowerInvariant();return _classModel;}/// <summary>/// 首字母转化成小写/// </summary>/// <param name="input"></param>/// <returns></returns>public static string FirstLetterToLower(this string input){if (string.IsNullOrEmpty(input)){return input; // 如果字符串为空或null,直接返回}char firstChar = input[0];if (char.IsUpper(firstChar)){firstChar = char.ToLower(firstChar, CultureInfo.InvariantCulture);}return firstChar + input.Substring(1);}/// <summary>/// 模型的信息/// </summary>public class VoloModelInfo{/// <summary>/// 中文名称 注释的空格前部分/// </summary>public string Title { get; set; }/// <summary>/// 描述 注释的空格后部分/// </summary>public string Desc { get; set; }/// <summary>/// 文档注释/// </summary>public string Summary { get; set; }/// <summary>/// 主键类型/// </summary>public string KeyType { get; set; } = "int";/// <summary>/// 字段信息/// </summary>public List<VoloModelProperty> Properties { get; set; }}/// <summary>/// 模型的字段信息/// </summary>public class VoloModelProperty{/// <summary>/// 字段名称 CreateDate/// </summary>public string Name { get; set; }/// <summary>/// 字段类型 System.DateTime/// </summary>public string Type { get; set; }/// <summary>/// 字段默认值 0001/1/1 0:00:00/// </summary>public string Default { get; set; }/// <summary>/// 字段中文 创建日期/// </summary>public string Title { get; set; }/// <summary>/// 注释的后部分 空格之后的,一般用于描述/// </summary>public string Desc { get; set; }/// <summary>/// XML文档注释内容/// </summary>public string Summary { get; set; }/// <summary>/// 字段属性规则/// </summary>public List<VoloModelAttribute> Attributes { get; set; }}/// <summary>/// 字段的属性规则/// </summary>public class VoloModelAttribute{/// <summary>/// 验证名称 MaxLength/// </summary>public string Name { get; set; }/// <summary>/// 过滤器值1 128/// </summary>public string Args1 { get; set; }/// <summary>/// 过滤器值2/// </summary>public string Args2 { get; set; }/// <summary>/// 过滤器值3/// </summary>public string Args3 { get; set; }/// <summary>/// 过滤器验证信息/// </summary>public string ErrorMessage { get; set; }}}
}

上面就是读取某一个Dto模型的代码,怎么引用呢?

可以在对应的AppService中写一个接口,比如

        /// <summary>/// 读取AddDto的数据模型/// </summary>/// <returns></returns>[HttpGet]public PasteBuilderHelper.VoloModelInfo ReadAddModel(){var _model = PasteBuilderHelper.ReadModelProperty<FrontTableAddDto>(new FrontTableAddDto());return _model;}/// <summary>/// 读取UpdateDto的数据模型/// </summary>/// <returns></returns>[HttpGet]public PasteBuilderHelper.VoloModelInfo ReadUpdateModel(){var _model = PasteBuilderHelper.ReadModelProperty<FrontTableUpdateDto>(new FrontTableUpdateDto());return _model;}

上面的代码可以写一个通用的读取的,不过那样和安全原则冲突,比如读取了你的配置文件模型,那就噶了,所以个人认为还是每个都写一个接口,反正都是PasteBuilder代码生成器的干活!

        通过以上的接口,那么只要知道要给某一个数据模型做表单,就可以知道这个数据表中的各个字段的信息,包括表的主键类型等!

        如果你要给UserInfo做表单,也就是新增,或者编辑,由PasteBuilder和PasteTemplate结合后可知道UserInfo的AppService对应的接口!

        下一期将实现,如何在小程序上实现自动表单!就是通过引入几个组件,然后配置modelData,就可以实现表单的自动验证,这样就避免以前那种每个表单去改代码的痛苦了!

        上面为一个案例表单的内容,输入内容后,就可以点击确定,读取对应的数据模型!

然后要实现这个表单的调用,代码只有下方一点:

<view>
<view>创建项目</view><view class="add-from" ><paste-form id="paste-form"></paste-form><view ></view><button bindtap="submitForm" style="margin-top:100rpx;" class="form-submit" type="primary">确定</button></view>
</view>

 对应的js内容

// pages/form/work.js
Page({/*** 页面的初始数据*/data: {formData:[{name:"name",title:"姓名",type:1,placeholder:"请输入姓名",value:"",required:true,maxlength:5,pattern:"[a-z]{3,5}"},{name:"age",title:"年龄",type:2,placeholder:"请输入年龄",value:7,routes:[{name:"min",value:5,error:"最小值不能小于5,请重新输入"}]},{name:"size",title:"规格",type:3,placeholder:"选择大的那个",desc:"选择小的那个",value:"",required:true,array:["小号","中号","大号"]},{name:"isEnable",title:"状态",type:4,placeholder:"请选择",value:true},{name:"mark",title:"备注",type:5,placeholder:"这里输入备注内容",value:"",currentlength:0,maxlength:200},{name:"like",title:"爱好",type:6,placeholder:"",value:"",currentlength:0,items:[{name:"1",checked:"true",value:"足球运动"},{name:"2",value:"篮球运动"}]},{name:"workClassId",title:"作业分类",type:7,placeholder:"选择所属分类",value:"",display:"",path:"/pages/select/work_class/index?select=workClassId",},{name:"startDate",title:"开始日期",type:8,placeholder:"点击选择日期",value:""},{name:"startTime",title:"开始时间",type:9,placeholder:"点击选择时间",value:""},{name:"imgsimgs",title:"附件",type:10,num:3,placeholder:"",value:"",images:[]},{name:"examineDate",title:"日期时间",type:11,placeholder:"点击选择",value:"2024-09-12 00:00:00"},{name:"fromArea",title:"所属地区",dataType:'region',placeholder:"点击选择",value:"",level:"sub-district"},{name:"loginPass",title:"设置密码",dataType:'password',placeholder:"3~16位数,大小写和数字组成的密码",value:"",maxlength:16},{name:"datePlan",title:"班次选择",type:14,placeholder:"点击选择",value:"2024-08-12 下午"},{name:"bigRate",title:"分佣比例",type:15,placeholder:"请输入",value:""},{name:"body",title:"正文内容",type:16,placeholder:"请基于实际情况填写需求,支持图片等模式!",value:""}]},init() {let dom = this.selectComponent("#paste-form");dom.FuncInitForm(this.data.formData);},/*** 生命周期函数--监听页面加载*/onLoad(options) {this.init();},SetFormItemValue(_name,_value,_display){console.log("选择回传",_name,_value,_display);let dom = this.selectComponent("#paste-form");if(dom){dom.FuncSetValue(_name,_value,_display);}},submitForm() {// console.log(this.data.formData);let dom = this.selectComponent("#paste-form");var _info =dom.FuncParseForm();if(_info){console.log(_info);}},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})

注意看上面的JS,其实只有2个函数和一个配置的data内容

而json中只是引入了组件

{"usingComponents": {"paste-form":"/components/paste-form/paste-form"}
}

 样式文件,更没有!

        下期将介绍这个paste-form的组件内容!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/408334.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis核心数据结构源码分析

dictEntry和redisObject 在 Redis 的实现中&#xff0c;当一个键值对被创建并存储时&#xff0c;键通常是一个字符串&#xff0c;而值则是一个 redisObject。因此&#xff0c;在 dictEntry 结构中&#xff0c;key 成员指向的是一个字符串&#xff0c;而 v.val 成员则指向一个 …

IO进程day01(函数接口fopen、fclose、fgetc、fputc、fgets、fputs)

目录 函数接口 1》打开文件fopen 2》关闭文件fclose 3》文件读写操作 1> 每次读写一个字符&#xff1a;fgetc(),fputc() 针对文件读写 针对终端读写 练习&#xff1a;实现 cat 命令功能 格式&#xff1a;cat 文件名 2> 每次一个字符串的读写 fgets() 和 fputs() …

云原生系列 - Nginx(高级篇)

前言 学习视频&#xff1a;尚硅谷Nginx教程&#xff08;亿级流量nginx架构设计&#xff09;本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删学习文档&#xff1a; 云原生系列 - Nginx(基础篇)云原生系列 - Nginx(高级篇) 一、扩容 通过扩容提升整体吞吐量…

【非常简单】 猿人学web第一届 第12题 入门级js

这一题非常简单&#xff0c;只需要找到数据接口&#xff0c;请求参数 m生成的逻辑即可 查看数据接口 https://match.yuanrenxue.cn/api/match/12 查看请求对应的堆栈中的 requests 栈 list 为对应的请求参数 list 是由 btoa 函数传入 ‘yuanrenxue’ 对应的页码生成的 bto…

PD取电快充协议方案

PD快充协议是通过调整电压和电流来提供不同的充电功率。它采用了一种基于USB-C端口的通信协议&#xff0c;实现了充电器于设备之间的信息交换。在充电过程中设备会向充电器发出请求&#xff0c;要求提供不同的电压和电流&#xff0c;充电器接收到请求后&#xff0c;会根据设备的…

第6章 B+树索引

目录 6.1 没有索引的查找 6.1.1 在一个页中的查找 6.1.2 在很多页中查找 6.2 索引 6.2.1 一个简单的索引方案 6.2.2 InnoDB中的索引方案 6.2.2.1 聚簇索引 6.2.2.2 二级索引 6.2.2.3 联合索引 6.2.3 InnoDB的B树索引的注意事项 6.2.3.1 根页面万年不动窝 6.2.3.2 内节…

【vue】编辑器段落对应材料同步滚动交互

场景需求 编辑器段落对应显示材料编辑器滚动时&#xff0c;材料同步滚动编辑器段落无数据时&#xff0c;材料不显示 实现方法 编辑器与材料组件左右布局获取编辑器高度&#xff0c;材料高度与编辑器高度一致禁用材料组件的滚动事件获取编辑器段落距离顶部的位置&#xff0c;…

【机器学习-监督学习】支持向量机

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

缓存学习

缓存基本概念 概念 对于缓存&#xff0c;最普遍的理解是能让打开某些页面速度更快的工具。从技术角度来看&#xff0c;其本质上是因为缓存是基于内存建立的&#xff0c;而内存的读写速度相比之于硬盘快了xx倍&#xff0c;因此用内存来代替硬盘作为读写的介质当然能大大提高访…

WIFI驱动开发

Linux 4.9 内核驱动移植 Linux 4.9 BSP 内核驱动 下载驱动后获得驱动的 tar.gz 压缩包 解压后找到如下驱动与文件夹 进入内核&#xff0c;找到 linux-4.9/drivers/net/wireless 文件夹中&#xff0c;新建文件夹aic8800 并且把上面的驱动与文件夹放入刚刚创建好的 aic8800 中。…

【笔记篇】Davinci Configurator SomeIpXf模块

目录 1 简介1.1 架构概览2 功能描述2.1 特性2.2 初始化2.3 状态机2.4 主函数2.5 故障处理3 集成4 API描述5 配置1 简介 本文主要描述了AUTOSAR SomeIpXf模块的功能。 SomeIpXf主要用途是对数据进行SOME/IP格式的序列化和反序列化。 1.1 架构概览 SomeIpXf在AUTOSAR软件架构…

【python】OpenCV—Single Human Pose Estimation

文章目录 1、Human Pose Estimation2、模型介绍3、基于图片的单人人体关键点检测4、基于视频的单人人体关键点检测5、左右校正6、关键点平滑7、涉及到的库函数scipy.signal.savgol_filter 8、参考 1、Human Pose Estimation Human Pose Estimation&#xff0c;即人体姿态估计&…

sqli-labsSQL手工注入第26-30关

第26关 一.查询数据库 http://127.0.0.1/Less-26/?id11%27%26extractvalue(1,concat(%27~%27,database(),%27~%27))%261%27 二.查表 http://127.0.0.1/Less-26/?id1%27||(updatexml(1,concat(1,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(…

2月公开赛Web-ssrfme

考点&#xff1a; redis未授权访问 源码&#xff1a; <?php highlight_file(__file__); function curl($url){ $ch curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, 0);echo curl_exec($ch);curl_close($ch); }if(isset($_GET[url…

qt的model view 使用示范

首先在ui界面拖一个tableView ui->tableView->setModel(mission_model); 然后设置model的qss&#xff0c;并用view绑定model void SettingWidget::init_missionmodel(QString plane_type, QString mission_name) {if(mission_model)delete mission_model;mission_model…

论文导读 | 大语言模型中应用到的强化学习算法

摘要 在最近取得广泛关注的大规模语言模型&#xff08;LLM&#xff09;应用强化学习&#xff08;RL&#xff09;进行与人类行为的对齐&#xff0c;进而可以充分理解和回答人的指令&#xff0c;这一结果展现了强化学习在大规模NLP的丰富应用前景。本文介绍了LLM中应用到的RL技术…

【GH】【EXCEL】P6: Shapes

文章目录 componentslinepicture components line picture Picture A Picture object Input parameters: Worksheet (Generic Data) A Worksheet, Workbook, Range Object, Excel Application, or Text Worksheet NameName (Text) An optional object nameLocation (Point) A p…

Eclipse SVN 插件在线下载地址

Eclipse SVN 插件 Subversive 在线安装 1、选择help下的install new software 2、点击 add 3、Name随便写&#xff0c;Location输入&#xff1a; https://download.eclipse.org/technology/subversive/4.8/release/latest/ 点击Add 4、然后一直下一步&#xff0c;Finish&am…

Vue的计算属性:methods方法、computed计算属性、watch监听属性

1、methods 方法 在创建的 Vue 应用程序实例中&#xff0c;可以通过 methods 选项定义方法。应用程序实例本身会代理 methods 选项中的所有方法&#xff0c;因此可以像访问 data 数据那样来调用方法。 【实例】在 Vue 应用程序中&#xff0c;使用 methods 选项定义获取用户信…

鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙中巧夺天工

gn是什么? gn 存在的意义是为了生成 ninja,如果熟悉前端开发,二者关系很像 Sass和CSS的关系. 为什么会有gn,说是有个叫even的谷歌负责构建系统的工程师在使用传统的makefile构建chrome时觉得太麻烦,不高效,所以设计了一套更简单,更高效新的构建工具gnninja,然后就被广泛的使用…