1 easyconf的誕生
2 easyconf的設計理念
2.1 整體設計
2.2 細節設計
2.2.1 CRUD操做
2.2.2 即時校驗
2.2.3 下拉框設計
3 easyconf使用指南
3.1 基本步驟
3.2 表配置文件
3.3 easyconf.js的定製
3.3.1 語言
3.3.2 URL地址
3.3.3 自定義校驗方法
4 easyconf後端開發指南
4.1 請求說明
4.2 返回說明
5 下一步的工做html
大概半年前作一個原型系統,有不少配置數據存儲在數據庫中,用戶須要對這些配置項進行管理。但凡開發過這樣的配置管理系統的人都會發現,爲A表編寫的配置管理的代碼與B表大同小異。若是可以進一步抽象,咱們或許能夠只用開發一套前端和後端代碼,就可以知足幾乎全部不一樣的配置表的需求。前端
我很懶,不喜歡作重複的工做,因此我設計了easyconf——基於AugularJS的配置管理系統開發框架。easyconf支持json格式的表配置文件,咱們能夠在此文件中定義須要管理的表的各類信息,諸如列信息,以及列對應的前端控件信息等。easyconf的最小集是index.html和easyconf.js兩個前端代碼,後端可按照本文中的指南進行自定義開發,固然,我也提供了一個基於python的demo後端。python
雖然,easyconf被設計用來開發配置管理系統,你也能夠腦洞大開地拿它來開發各類管理系統,諸如「XX學籍管理系統」,「XX圖書館管理系統」等。很熟悉吧,至少本科的課程設計作過這樣的系統。若是有了easyconf,你甚至無需寫1字節代碼就能夠作出一個管理系統,你僅僅須要作好表設計以及對錶配置文件的定製化。git
截止目前(2016-07),easyconf已支持0-String/1-Long/2-Double/3-Boolean/4-Date,共5種數據類型;0-text/1-combo box,共2種控件類型;0-精確/1-包含/2-小於/3-大於/4-小於等於/5-大於等於,共6種查詢方式。github
MVC架構提供了Model將數據與後端中的類進行映射,而另有一些開發者將後端中的類與前端中的控件進行了映射。然而,管理系統是一類很是「單純」的系統,大部分CRUD操做只針對單表進行,因此,咱們能夠放心地進行簡化映射關係,將數據與前端控件直接映射起來:sql
上圖顯示了一個生動的實例,假設咱們有衆多的服務器架設在全國各地,那麼咱們須要一張名爲service的表存儲這些服務器的信息,其中包括服務器的IP,端口號,所在省市,以及是否啓用。使用MVC架構或者基於MVC的優化架構,咱們必須至少爲後端編寫必定量的代碼(定義類)。倘若使用數據對前端控件的直接映射,那麼將免去這方面的工做。數據庫
easyconf支持對有主鍵的單表的CRUD操做。以服務器配置爲例,打開配置管理主頁以下:json
點擊「新增」,將進入新增面板:後端
在配置管理主頁,點擊「查找」,將按按照條件在主頁輸出查找結果:服務器
點擊查找結果的「詳情」,將進入詳情面板:
點擊查找結果的「更新」,將進入更新面板:
點擊查找結果的「刪除」,將刪除一條配置數據:
點擊「提交」後再對用戶的輸入進行校驗是不夠人性化的,easyconf可以即時校驗用戶的輸入:
上圖展現了兩種校驗,第一種是數據類型校驗,因爲定義的「服務器監聽端口」字段爲整型,當輸入字母「a」時將提示「必須爲整數」;第二種是自定義校驗,對的,easyconf還支持自定義校驗函數,在本例中我自定義了IP和端口合法性校驗函數。
惟一性是不太好處理的校驗項:首先,爲了追求真正的惟一性,在新增面板打開到關閉這段期間內進行鎖表是不合適的;其次,退而求其次,每次focus在主鍵字段,或者修改主鍵字段,或者從主鍵字段blur都查詢一下是否惟一,這種方式也是不合理的,會大大增長系統的開銷。easyconf採起的是一種準即時的方式,提交新增請求後,若後端判斷不知足惟一性約束,則前端記錄下本次的主鍵字段的輸入爲歷史輸入,下一次提交前,每一次主鍵字段的修改都會與歷史輸入進行比較,只要是一致,就會提示「數據已存在」:
easyconf使用了兩種下拉框:靜態下拉框和動態下拉框。靜態下拉框的候選內容是固定的,在表配置文件中定義。動態下拉框的候選內容是可變的,其取數的方式也在表配置文件中定義。在本例中,「省」字段的候選內容從數據庫表「province」中讀取,「市」字段的候選內容從數據庫表「city」中讀取,讀取的時候還須要加上「省」字段做爲查詢條件;「是否啓用」字段爲靜態下拉框:
easyconf的使用能夠分爲如下幾個基本步驟:
表配置文件的定製是使用easyconf的核心步驟,讓咱們看一下這個配置文件中的各類配置項的說明:
1 { 2 title:<title>, //此處定義了標題顯示內容 3 table:<table>, //表名 4 count:10, //查詢結果每頁條數 5 columns: //此處定義了須要顯示的表的各列信息 6 [ 7 { 8 id:<col1>, //列在數據庫中定義的名稱 9 name:<列1>, //列在頁面上顯示的中文名 10 isShow:<true>, //在查詢結果中是否顯示 11 isPrimaryKey:[true], //是否爲主鍵,在新增頁面時,主鍵的列的控件觸發onchange事件後,須要進行惟一性校驗。 12 isNull:<true>, //是否爲空,在新增或修改頁面時,主鍵的列的控件觸發onchange事件後,須要進行是否爲空校驗。 13 type:<0>, //列的數據類型,0- String /1-Long/2-Double/3-Boolean/4-Date 14 control:<0>, //列在頁面上顯示的控件類型,0-text/1-combo box 15 check: //在新增或修改頁面時,主鍵的列的控件觸發onchange事件後所調用的自定義校驗函數 16 [ 17 { 18 funcname:<function1>, 19 argument:<col1, col2…> 20 }, //調用函數時,第一個參數爲本控件的值,以後的參數爲argument中列對應的控件的值。 21 … 22 ], 23 isSelect:<true>, //是否做爲查詢條件。 24 selectType:<0>, //查詢條件,0-精確/1-包含/2-小於/3-大於/4-小於等於/5-大於等於 25 candidate:<0/1>, //0-固定值,1-動態值,爲固定值時從fixed中取候選內容,爲動態值時從flexible中取key,value,table和query組sql語句從數據庫中查詢出候選內容。候選內容的格式爲」key-value」。 26 fixed: 27 [ 28 {key1:<value1>}, 29 {key2:<value2>}, 30 … 31 ], 32 flexible: 33 { 34 key:<col>, 35 value:<col>, 36 table:<table>, 37 where:<col1, col2…> 38 }, 39 dft:<dft? //默認值,查詢層控件或新增頁面對應控件的默認值。 40 }, 41 … 42 ] 43 }
在easyconf.js中咱們能夠定製前端上顯示的要素的名稱,提示的信息:
1 //定義控件的顯示名稱 2 names:{ 3 SEARCH:"查找", 4 INSERT:"新增", 5 DETAIL:"詳情", 6 UPDATE:"更新", 7 DELETE:"刪除", 8 FRESH:"刷新", 9 PREV:"上頁", 10 NEXT:"下頁", 11 GO:"跳轉", 12 RETURN:"返回", 13 COMMIT:"提交", 14 OPERATIONS:"操做" 15 }, 16 //定義小標題 17 subTitles:['查找', '詳情', '更新', '新增'], 18 //定義提示信息 19 msgs:{ 20 OK:"經過", 21 NOTNULL:"不容許爲空", 22 INTEGER:"必須爲整數", 23 DOUBLE:"必須爲浮點數", 24 TYPEERROR:"類型錯誤", 25 DATE:"必須爲日期[YYYY-MM-DD hh:mm:ss.ms]", 26 NOTUNIQUE:"數據已存在", 27 },
在easyconf.js中,咱們還能夠定義URL地址,譬如域名爲「www.domain.com」,定義INSERT請求的URL爲「Insert」,那麼完整的URL地址爲「www.domain.com/Insert」:
1 apps:{ 2 INIT:"Init", 3 LIST:"Search", 4 DELETE:"Delete", 5 UPDATE:"Update", 6 INSERT:"Insert", 7 RANGE:"Range" 8 },
在easyconf.js中,咱們還能夠自定義校驗方法,在服務器配置的例子中,咱們定義IP和端口的校驗方法以下:
1 // user's function here 2 userfunc: { 3 checkIp:function(ip) { 4 var exp=/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/; 5 var reg = ip.match(exp); 6 if(reg != null) 7 return true; 8 else 9 return false; 10 }, 11 12 checkPort:function(port) { 13 if (0 <= port && port <= 65535) 14 return true; 15 else 16 return false; 17 } 18 }
根據本指南,咱們能夠爲easyconf開發本身的後端:
請求 | 方式 | url | 請求數據 | 返回數據 | 說明 |
配置管理主頁 | GET | <domain>/<main>.html | 無 | <main.html> | domain爲域名,main.html爲index.html的重命名副本 |
初始化 | GET | <domain>/<INIT> | 無 | <table.json> | table.json爲表配置文件 |
列表查詢 | GET | <domain>/<LIST>?table=<table>&data=<col1,col2...>&query=<col1:value1,col2:value2...>&begin=<begin>&count=<count> | 無 | 查詢返回,見下 | 後端收到請求後,解析data和query參數,data參數和query中的col和value均進行了base64編碼。而後組sql語句進行查詢,查詢出來的結果爲數據庫中的第begin*count+1條到begin*(count+1)條。 |
刪除 | POST | <domain>/<DELETE> | 刪除請求,見下 | 通用返回,見下 | 後端收到請求後,解析query變量。而後組sql語句進行刪除。若是原數據不存在,也返回成功。 |
更新 | POST | <domain>/<UPDATE> | 更新請求,見下 | 通用返回,見下 | 後端收到請求後,解析data和query變量。而後組sql語句進行更新。若是原數據不存在,也返回成功。 |
新增 | POST | <domain>/<INSERT> | 新增請求,見下 | 通用返回,見下 | 後端收到請求後,解析data變量。而後組sql語句進行更新。 |
候選項查詢 | GET | <domain>/<LIST>?table=<table>&key=<key>&value=<value>&query=<col1:value1,col2:value2...> | 無 | 候選項返回,見下 | 後端收到請求後,解析query參數。而後組sql語句進行查詢,返回查詢結果。 |
刪除請求:
1 { 2 table:<table>, 3 query:<col1:value1,col2:value2...> 4 }
更新請求:
1 { 2 table:<table>, 3 data:<col1:value1,col2:value2...>, 4 query=<col1:value1,col2:value2...> 5 }
新增請求:
1 { 2 table:<table>, 3 data:<col1:value1,col2:value2...>, 4 }
查詢返回:
1 { 2 result:<00>, //響應結果,00爲成功 3 message:<OK>, //響應信息 4 data:[ //查詢無結果則返回空列表 5 [ //查詢出來的每一條數據 6 value1, //每一條數據的每列的值,若是該列是動態下拉框,那麼還須要根據表配置文件進行查詢組成「key|value」的形式,key和value均base64編碼 7 value2, 8 … 9 ], 10 … 11 ] 12 }
通用返回:
1 { 2 result:<00>, //響應結果,00爲成功,插入時01爲主鍵衝突 3 message:<OK>, //響應信息 4 }
候選項返回:
1 { 2 result:<00>, //響應結果,00爲成功 3 message:<OK>, //響應信息 4 data:[ //若是查詢無數據則返回空列表 5 { //每一條數據 6 key:<key> 7 value:<value> 8 } 9 … 10 ] 11 }
easyconf從設計到編碼花了我兩天時間,目前的版本可以知足基本的配置管理需求。可是仍由不少地方急需改進和優化,例如:頁面的美化(而且也可以經過表配置文件定製),更加健全的異常處理,外鍵約束的校驗,表操做的權限管理,等等。因爲懶,我搞出來這個一個玩意兒,同時又給本身挖了好大一個坑,以後慢慢填吧。你們能夠從GitHub上下載easyconf以及demo後端。