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

news2024/9/24 19:11:33

前言

        在开发实际项目中,其实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.coloradmin.cn/o/2070637.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【vue3|第26期】Vue3 中的 useRoute 与 router.currentRoute.value:选择正确的路由访问方式

日期&#xff1a;2024年8月22日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉在这里插入代码片得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不…

推荐4款2024年专业的电脑远程控制软件。

为了能够打破空间的限制&#xff0c;远程控制工具被越来越多的人使用。它们可以帮助提高工作效率&#xff0c;方便远程技术支持等。今天&#xff0c;就让我们一起来了解一下网上比较火的4款远程控制电脑的软件。 &#xff11;、向日葵 直达链接&#xff1a;https://down.oray.…

如何使用python脚本爬取微信公众号文章?

1、什么是爬虫&#xff1f; 在座的各位可能经常听到一个词&#xff0c;叫“爬虫”&#xff0c;这是一种能够悄无声息地将网站数据下载至本地设备的程序。利用爬虫&#xff0c;您无需亲自访问特定网站&#xff0c;逐个点击并手动下载所需数据。相反&#xff0c;爬虫能够全自动地…

FL Studio24苹果mac电脑破解绿色版安装包下载

FL Studio 24最新版本&#xff0c;这可不仅仅是一个音乐制作软件的升级&#xff0c;它是音乐创作爱好者的福音&#xff0c;是专业制作人的心头好。那么&#xff0c;它究竟有哪些魔力&#xff0c;能让这么多人为之疯狂呢&#xff1f; 我们来看看它的界面。FL Studio 24的界面设…

XGBoost中正则化的9个超参数

正则化是一种强大的技术,通过防止过拟合来提高模型性能。本文将探索各种XGBoost中的正则化方法及其优势。 为什么正则化在XGBoost中很重要? XGBoost是一种以其在各种机器学习任务中的效率和性能而闻名的强大算法。像任何其他复杂模型一样,它可能会过拟合,特别是在处理噪声数据…

x-cmd mod | x scoop - Windows 开源包管理工具

目录 介绍主要特点例子子命令 介绍 scoop 是 windows 的第三方包管理工具&#xff0c;与 winget, choco, chocolatey 类似。 本模块在 scoop 的基础上做了增强&#xff0c;使其可与 shell 无缝集成&#xff0c;并提供更多的功能。 主要特点 自动下载&#xff1a; 通过调用 S…

汇编语言:cmp、je、jne、jb、jnb、ja、jna 指令

一. cmp 指令 1. cmp 指令功能 cmp (compare) 是比较指令&#xff0c;cmp 的功能相当于减法指令&#xff0c;只是不保存结果&#xff0c;但会根据结果对标志寄存器进行设置&#xff0c;其它相关指令就可以通过识别这些被影响的标志寄存器的位来得知比较结果。 2. cmp指…

python爬虫:selenium+browsermobproxy实现浏览器请求抓取(模块安装详解)

前言 本来很多场景用beautiful和requests就能解决的&#xff0c;但是最近发现了某些网站会使用<link>来链接网页信息&#xff0c;让我没办法通过requests获取页面的具体内容&#xff1b;并且接口也加入了某种token的生成方案&#xff0c;导致我无从下手。 因此&#xff0…

Scrapy 分布式爬虫框架 Scrapy-Redis

github官网代码示例&#xff1a;https://github.com/rmax/scrapy-redis/blob/master/example-project/example/spiders/myspider_redis.py 什么是 Scrapy-Redis Scrapy-Redis 是一个基于 Scrapy 的扩展&#xff0c;用于实现分布式爬虫。它利用 Redis 作为分布式队列来共享待爬…

.NET_web前端框架_layui_栅格布局

基础概念 layui:用于简化前端编写的框架。响应式布局&#xff08;Responsive Layout&#xff09;:一种网页设计方法&#xff0c;使网页能够根据不同设备的屏幕尺寸和分辨率自动调整其内容和布局。栅格布局&#xff08;Grid Layout&#xff09;:一种网页设计布局方法&#xff0c…

计算机毕业设计选题推荐-OA办公管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

【IEEE出版,连续7年稳定发表】第八届电气、机械与计算机工程国际学术会议(ICEMCE 2024,10月25-27)

由西京学院主办&#xff0c;AEIC学术交流中心协办&#xff0c;中国科学技术大学、深圳大学、浙江工业大学等校联合支持的第八届电气、机械与计算机工程国际学术会议&#xff08;ICEMCE 2024&#xff09;将于2024年10月25日至27日在西安举办。 本次会议主要围绕“电气”、"…

电脑文件自动加解密如何实现?3个方法教会你!

电脑文件的自动加解密是一项非常实用的功能&#xff0c;可以帮助确保数据的安全性。 以下是三种实现电脑文件自动加解密的方法&#xff1a; 方法一&#xff1a;使用操作系统自带的加密功能 Windows BitLocker&#xff1a; 启用BitLocker&#xff1a;在Windows系统中&#xf…

SpringBoot 项目——抽奖系统

本项目主要实现的功能是&#xff1a;主要服务于管理员用户&#xff0c;其可圈选奖品&#xff0c;人员来创建抽奖活动&#xff0c;并进行在线抽奖&#xff0c;并可通过短信或邮件的方式通知中奖者&#xff0c;同时普通用户可查看已结束的抽奖活动的中奖结果&#xff1b; 一、项…

ViT篇外:NVIDIA Llama-3.1-Minitron 4B

相关阅读&#xff1a; ViT&#xff1a;3 Compact Architecture MobileLLM&#xff1a;“苗条”的模型比较好&#xff01; 大家也许会很好奇为什么在ViT章节插入了NVIDIA Llama-3.1-Minitron 4B&#xff0c;ViT因为应用场景的特殊性所以都寄希望于高效率的模型&#xff0c;因…

【C语言】浮点型数据在内存中的储存

浮点型数据在内存中的储存 文章目录 浮点型数据在内存中的储存引例概念提出浮点型数据储存规定对于有效数字M的特别规定对于指数E的特别规定指数E的储存指数E的读取 利用规则解释原因 在之前学习过整形数据在内存中的储存后&#xff0c;浮点型数据在内存中的储存又会怎样呢&…

AI辅助论文写作已成大趋势,这些AI工具分享给你

近年来&#xff0c;人工智能语言模型迅速发展&#xff0c;特别是在美国人工智能研究实验室 OpenAI 于 2022 年 11 月发布了聊天机器人 ChatGPT 后&#xff0c;引发了全球范围内的广泛讨论。人们惊叹着一个新的人工智能时代已经到来&#xff0c;预示着许多工作将被这类机器人所取…

二叉树刷题(1)

二叉树题目讲解&#xff08;1&#xff09; 一、构建二叉树并且遍历&#xff08;1&#xff09;思路&#xff08;2&#xff09;代码 二、对称二叉树1、思路2、代码 三、相同的树1、思路2、代码 四、单值二叉树1、思路2、代码 五、另一棵树的子树1、思路2、代码 一、构建二叉树并且…

【Rust日报】一本新书:黑帽Rust

2024 Rust中国大会大会将于 9 月 07 日 - 08 日在上海举办。精彩议题逐步放出中&#xff0c;欢迎大家面对面交流。 2024 Rust中国大会报名链接暨第一批精彩演讲主题介绍 2024 Rust中国大会第二批精彩演讲主题列表 2024 Rust中国大会第三批精彩演讲主题列表 马尔科夫文本生成算法…

多线程(5)——锁策略、CAS、JUC常见类

1. 常见锁策略 1.1 乐观锁 & 悲观锁 乐观锁 & 悲观锁 也不是指具体某个锁&#xff0c;而是 “锁的一种特点”&#xff0c;描述了 “一类锁” 乐观锁&#xff1a;加锁的时候&#xff0c;假设出现锁冲突的概率不大 > 接下来围绕加锁要做的工作就会更少悲观锁&#…