PasteForm最佳CRUD實踐,實際案例PasteTemplate詳解之3000問(三)

PasteSpider發表於2024-09-30

作為“貼程式碼”力推的一個CRUD實踐專案PasteTemplate,在對現有的3個專案進行實戰後效果非常舒服!下面就針對PasteForm為啥我願稱為最佳CRUD做一些回答:
image

哪裡可以下載這個PasteForm的專案案例

目前“貼程式碼”對外使用PasteForm的專案有"貼Builder(PasteSpider)"和案例專案(PasteTemplate),其中案例專案你可以在
https://gitee.com/pastecode/paste-template 獲得

PasteForm的CRUD模式有什麼特色?

搞這個的起初是因為我有一個專案要在小程式上行實現大量的表單,思考了下,一大堆相似的程式碼,不是可以使用那個元件的方式實現麼,那就需要傳入模型,換位思考下,那麼我們經常使用的CRUD是否也可以這麼搞?
於是就有了這個利用多資料模型Dto和反射的原理實現的PasteForm,主要優點如下

1.PasteForm輸出的是一個思想,就是管理端的頁面由後端控制,無論是安全性還是資料的表現

2.前端編寫一次後,後續都不用編寫,如果你使用的是我的案例專案,那麼你前端都可以不用編寫,包括後端要新增表,修改欄位等,前端都不再需要修改程式碼

3.前端頁面的超簡潔性,無論你的後端是30個表還是100個表,對於前端來說都差不多大概4個頁面搞定!當然了一些特殊的需求,還是需要編寫下的!

4.無語言限制,上面說得輸出的是一個思想,本文的案例使用的是.netcore+html的形式實現的,你也可以使用比如java+vue的模式

5.統一性,比如你30個表的圖片上傳,在表單中他們的樣式都是一樣的,因為他們其實使用的是同一個管理端頁面

6.後端統籌所有,比如欄位的顯示順序,欄位是否顯示,欄位的預設值等等等,都由後端對應的Dto來限制和控制,比如同樣的新增,由於當前登陸使用者的角色不同,你可以限制某一個欄位張三必填,而李四可以是選填!

image

看到管理端的程式碼,感覺像JSP的?

整個PasteForm的原理就是後端把資料模型的屬性,欄位等返回給前端,前端基於後端返回的資料再渲染到UI上,為了便於編寫所以採用了template的模式,其實不是JSP,是長得像而已!

我的專案擁有120個資料表,那是不是要編寫很多的後端檔案?

PasteForm原則上只有3個頁面pasteform/index.html,pasteform/view.html,pasteform/detail.html,只是有一些特殊情況下,會新增一些頁面,這個要看你專案的實際需求,其實無論多少個表,PasteForm的頁面都只是那麼幾個,比如說許可權的頁面為pasteform/index.html?path=roleInfo而使用者的表為pasteform/index.html?path=userInfo,你會發覺其實他們是同一個頁面,只是引數path不一樣而已,這個path就是WebApi的Service!
其實你完全可以一個頁面都不寫,直接使用我提供的案例專案PasteTemplate中的pasteform的檔案!
image

我看案例PasteTemplate專案是使用html,js,css編寫的,可以使用vue編寫管理端麼?

上面提到的,PasteForm主要輸出的是一個思想,也就是由後端的Dto控制前端的頁面呈現和互動等,所以從WebApi中請求得到模板得屬性欄位等資訊後,你可以使用其他語言實現管理端得頁面得,不限原生,vue,angular等

pasteform/index.html是幹嘛用?

作為對應資料表得資料展示用,一般為表格table呈現,對應模型為XXXListDto,也包括了搜尋區域,搜尋區域由對應得InputQueryXXX的資料模型決定,一般只有page,size,word三個欄位!當然了這個頁面也包含了新增資料,編輯,詳情,刪除等的操作互動!

pasteform/view.html是做什麼用得?

新增資料,或者編輯資料的時候使用的都是這個頁面,不過他們讀取的資料模型不一樣,比如許可權這個表roleInfo,則有新增的時候為pasteform/view.html?path=roleInfo,對應的資料模型為roleInfoAddDto,而在編輯的時候為pasteform/view.html?path=roleInfo&id=3,對應的資料模型為roleInfoUpdateDto。頁面的整體邏輯就是從WebApi中獲得對應模型後,基於規則渲染到UI中,包括預設值,當前值等,提交的時候再從頁面中的form讀取到值提交給後端,所以說主要的控制還是在後端中!

pasteform/detail.html是做什麼用得?

有些時候我們的資料沒辦法在pasteform/index.html頁面中顯示完成,比如文章列表,主要是一些欄位過長的,這個時候我們希望有一個頁面顯示詳細內容,所以detail就是幹這個用的,對應的資料模型為XXXDto!

我不會.netcore可以用其他語言寫pasteform麼

PasteForm輸出的是一個思想,所以使用其他語言也是可以的,不過你得對著寫一整套的就是了,比如你可以使用vue+java的模式搞一套!後端核心程式碼案例

        /// <summary>
        /// 讀取AddDto的資料模型
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "root", "root" })]
        public PasteBuilderHelper.VoloModelInfo ReadAddModel()
        {
            var _model = PasteBuilderHelper.ReadModelProperty<RoleInfoAddDto>(new RoleInfoAddDto());
            return _model;
        }

        /// <summary>
        /// 讀取UpdateDto的資料模型
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "root", "root" })]
        public async Task<PasteBuilderHelper.VoloModelInfo> ReadUpdateModel(int id)
        {

            var _query = from a in _dbContext.RoleInfo
                         join b in _dbContext.RoleInfo on a.FatherId equals b.Id into c
                         from rol in c.DefaultIfEmpty()
                         select new RoleInfoUpdateDto
                         {
                             Id = a.Id,
                             Desc = a.Desc,
                             FatherId = a.FatherId,
                             Icon = a.Icon,
                             IsEnable = a.IsEnable,
                             Model = a.Model,
                             Name = a.Name,
                             Path = a.Path,
                             Role = a.Role,
                             RoleType = a.RoleType,
                             Sort = a.Sort,
                             ExtendRole = rol != null ? new RoleShortModel
                             {
                                 Id = rol.Id,
                                 Model = rol.Model,
                                 Name = rol.Name,
                                 Path = rol.Path,
                                 Role = rol.Role,
                                 RoleType = rol.RoleType
                             } : null
                         };

            var _info = await _query.Where(x => x.Id == id).AsNoTracking().FirstOrDefaultAsync();
            if (_info == null || _info == default)
            {
                throw new PasteCodeException("查詢的資訊不存在,無法執行編輯操作!");
            }
            var _dataModel = PasteBuilderHelper.ReadModelProperty<RoleInfoUpdateDto>(_info);
            return _dataModel;
        }

        /// <summary>
        /// 讀取UpdateDto的資料模型
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "root", "root" })]
        public async Task<PasteBuilderHelper.VoloModelInfo> ReadDetailModel(int id)
        {

            var _query = from a in _dbContext.RoleInfo
                         join b in _dbContext.RoleInfo on a.FatherId equals b.Id into c
                         from rol in c.DefaultIfEmpty()
                         select new RoleInfoDto
                         {
                             Id = a.Id,
                             Desc = a.Desc,
                             FatherId = a.FatherId,
                             Icon = a.Icon,
                             IsEnable = a.IsEnable,
                             Model = a.Model,
                             Name = a.Name,
                             Path = a.Path,
                             Role = a.Role,
                             RoleType = a.RoleType,
                             SortStr = a.SortStr,
                             FatherStr = a.FatherStr,
                             Level = a.Level,
                             RootId = a.RootId,
                             Sort = a.Sort,
                             ExtendRole = rol != null ? new RoleShortModel
                             {
                                 Id = rol.Id,
                                 Model = rol.Model,
                                 Name = rol.Name,
                                 Path = rol.Path,
                                 Role = rol.Role,
                                 RoleType = rol.RoleType
                             } : null
                         };

            var _info = await _query.Where(x => x.Id == id).AsNoTracking().FirstOrDefaultAsync();
            if (_info == null || _info == default)
            {
                throw new PasteCodeException("查詢的資訊不存在,無法執行編輯操作!");
            }
            var _dataModel = PasteBuilderHelper.ReadModelProperty<RoleInfoDto>(_info);
            return _dataModel;
        }

        /// <summary>
        /// 讀取ListDto的資料模型
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "root", "root" })]
        public PasteBuilderHelper.VoloModelInfo ReadListModel()
        {
            var _model = PasteBuilderHelper.ReadModelProperty<RoleInfoListDto>(new RoleInfoListDto());
            var _query_model = PasteBuilderHelper.ReadModelProperty(new InputQueryRoleInfo());
            if (_query_model != null)
            {
                _model.QueryProperties = _query_model.Properties;
            }
            return _model;
        }

PasteForm使用到的各個模型是幹嘛用的,Dto

我最早接觸Dto的時候是ABPvNext,說到這個Dto不得不說ObjectMapper,比如說許可權表RoleInfo,則對應的有RoleInfoAddDto,RoleInfoUpdateDto,RoleInfoDto和RoleInfoListDto,當前實際開發中你可能還會引申出更多的Dto,比如我常用的RoleMenuDto,RoleAuthDto等,PasteForm中主要用到前面的4個,一般還會附帶一個InputQueryRoleInfo!比如說新增的時候,使用者提交的資料模型是RoleInfoAddDto,提交給API後,API處資料校驗合法後再使用AutoMapper把RoleInfoAddDto轉化成RoleInfo,然後寫入到資料庫!
XXXAddDto:用於新增的時候的資料模型,對應的是pasteform/view.html頁面使用
XXXUpdateDto:用於資料編輯修改的資料模型,對應的是pasteform/view.html頁面使用
XXXDto:這裡我一般用於顯示詳情的時候的資料模型,也就是pasteform/detail.html的頁面使用
XXXListDto:這裡一般使用於資料表格展示的時候的資料模型,也就是pasteform/index.html的頁面使用
InputQueryXXX:這裡一般用於表格上方的搜尋項的資料模型,也就是pasteform/index.html的搜尋區域使用

如何上傳圖片?

圖片分新增編輯和展示,展示的話這裡就是pasteform/index.html和pasteform/detail.html頁面中了,其實在編輯的時候pasteform/view.html頁面上也有展示,只要給對應欄位新增屬性[ColumnDataTypeAttribute("image","1","image","60*60")]即可

        /// <summary>
        /// 多圖 回傳的值是多個的,使用,隔開
        /// </summary>
        [ColumnDataType("image", "3", "img", "60*60")]
        public string Img2 { get; set; }

        /// <summary>
        /// 圖片 回傳的使用string[]的模式
        /// </summary>
        [ColumnDataType("image", "3", "img", "60*60")]
        public string[] Img3 { get; set; }

表格中如何讓資料左靠

        ///<summary>
        ///文字區域 模擬文字區域的輸入
        ///</summary>
        [ColumnDataType("class","fleft")]
        public string Desc { get; set; }

表格中如何自定義顯示

        ///<summary>
        ///單選 一般表示狀態,內定的,有點像Enum,關於Enum後續會支援
        ///</summary>
        [ColumnDataType("html", "<div>{{:=item.dateType}}-{{:=item.gradeId}}</div>")]
        public int DateType { get; set; }

表格中如何顯示按鈕

        /// <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; }

按照排序查下,某些欄位支援升序降序

前端只是基於這個屬性,在查下的時候會回傳orderby欄位給後端,後續需要基於回傳的這個欄位進行orderby查詢


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

--- 未完待續,下期繼續 ---

相關文章