├── .gitignore ├── .gitignore.bak ├── README.md ├── css └── layout.css ├── index.html ├── js ├── controller.js ├── dto │ └── ClassModel.js ├── repository │ ├── DBUtil.js │ ├── TableDao.js │ └── TableEntity.js ├── service.js ├── templateFileLoader.js ├── util │ ├── $.js │ ├── Assert.js │ ├── yi.template.js │ └── yi.toast.js └── vm │ ├── DBProfileVM.js │ └── GenerateVM.js ├── main.js ├── package-lock.json ├── package.json └── template └── Entity.java /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out 3 | -------------------------------------------------------------------------------- /.gitignore.bak: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # database2java -------------------------------------------------------------------------------- /css/layout.css: -------------------------------------------------------------------------------- 1 | article,header,footer,section,aside,main, 2 | figcaption,figure, 3 | fieldset,p,div{ 4 | box-sizing: border-box; 5 | display: block; 6 | } 7 | h1,h2{ 8 | margin: 0; 9 | } 10 | .center{ 11 | text-align: center; 12 | } 13 | .layout-hbox { 14 | display: flex; 15 | flex-direction: row; 16 | flex-wrap: nowrap; 17 | justify-content: flex-start; 18 | align-items: stretch; 19 | } 20 | 21 | .layout-hbox > .grow { 22 | flex: auto; 23 | } 24 | .layout-hbox-center { 25 | display: flex; 26 | flex-direction: row; 27 | flex-wrap: nowrap; 28 | justify-content:flex-start; 29 | align-items:center; 30 | } 31 | 32 | .layout-hbox-center > .grow { 33 | flex: auto; 34 | } 35 | .layout-justify { 36 | display: flex; 37 | flex-flow: row nowrap; 38 | justify-content: space-between; 39 | align-items: center; 40 | } 41 | .layout-justify-stretch{ 42 | display: flex; 43 | flex-flow: row wrap; 44 | justify-content: space-between; 45 | align-items: stretch; 46 | } 47 | .layout-middle { 48 | display: flex; 49 | align-items: center; 50 | justify-content: space-between; 51 | } 52 | .layout-vbox { 53 | display: flex; 54 | flex-direction: column; 55 | flex-wrap: nowrap; 56 | justify-content: flex-start; 57 | align-items: stretch; 58 | } 59 | 60 | .layout-vbox > .grow { 61 | flex: auto; 62 | overflow: auto; 63 | } 64 | 65 | 66 | 67 | table.data { 68 | width: 100%; 69 | border-collapse: collapse; 70 | text-align: center; 71 | } 72 | 73 | table.data th { 74 | border: 1px solid #81b4be; 75 | padding: 5px 0; 76 | } 77 | 78 | table.data td{ 79 | border: 1px solid #ccc; 80 | padding: 5px 5px; 81 | } 82 | 83 | table.data > thead>tr { 84 | background-color: hsl(243,30%,50%); 85 | font-weight: bold; 86 | height: 2em; 87 | color:#ffffff; 88 | } 89 | 90 | table.data>tr,table.data>tbody>tr { 91 | background-color: #ffffff; 92 | color:hsl(0,0%,5%) 93 | } 94 | 95 | 96 | 97 | table.data>tr:nth-child(even),table.data>tbody>tr:nth-child(even) { 98 | background-color: hsl(60,50%,97%); 99 | } 100 | 101 | table.data>tr:HOVER,table.data>tbody>tr:HOVER { 102 | background-color: hsl(60,50%,90%); 103 | color:hsl(0,0%,0%) 104 | } 105 | button, .btn{ 106 | border: 1px solid #204d74; 107 | border-radius: 4px; 108 | background: #286090 none; 109 | color: #eeeeff; 110 | cursor: pointer; 111 | font-size: 100%; 112 | font-family: sans-serif, "宋体"; 113 | font-weight: bold; 114 | line-height: 1.15; 115 | overflow: hidden; 116 | box-sizing: border-box; 117 | padding: 8px 15px; 118 | margin: 0; 119 | } 120 | 121 | 122 | button:HOVER, .btn:HOVER { 123 | color: #ffffff; 124 | background-color: #df2e1b; 125 | border-color: #c42818; 126 | } 127 | 128 | button:ACTIVE, .btn:ACTIVE { 129 | color: #ffffff; 130 | background-color: #2f2f2f; 131 | border-bottom-color: #eee; 132 | border-right-color: #eee; 133 | } 134 | 135 | button[disabled], [disabled].btn { 136 | cursor: wait; 137 | color: #eeeeff; 138 | background-color: gray; 139 | border-color: #204d74; 140 | } 141 | 142 | 143 | 144 | a.btn { 145 | text-decoration: none; 146 | display: inline-block; 147 | vertical-align: middle; 148 | text-align: center; 149 | } 150 | 151 | /**两个按钮有距离*/ 152 | button + button, 153 | button + .btn, 154 | .btn + button, 155 | .btn + .btn { 156 | margin-left: 1em; 157 | } 158 | 159 | button.D3, .btn.D3 { 160 | background-color: #DB5705; 161 | border-radius: 8px; 162 | box-shadow: 0px 9px 0px rgba(219, 31, 5, 1), 0px 9px 25px rgba(0, 0, 0, .7); 163 | transition: all .1s ease; 164 | } 165 | 166 | .D3:HOVER { 167 | background-color: #D56f0B; 168 | } 169 | 170 | 171 | input[type="text"], 172 | input[type="password"], 173 | input[type="number"], 174 | input[type="datetime-local"], 175 | input[type="date"], 176 | input[type="email"], 177 | input[type="url"], 178 | input[type="tel"], 179 | select, 180 | textarea { 181 | box-sizing: border-box; 182 | display: inline-block; 183 | vertical-align: middle; 184 | font-size: 100%; 185 | color: #555; 186 | background-color: #fff; 187 | border: 1px solid #ccc; 188 | border-radius: 4px; 189 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 190 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 191 | -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; 192 | -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 193 | transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 194 | padding-left: .5em; 195 | line-height: 1.15; 196 | min-height: 40px; 197 | min-width:0; 198 | width:auto; 199 | font-family: "Microsoft YaHei", 微软雅黑, "MicrosoftJhengHei"; 200 | } 201 | input:focus, textarea:focus,select:focus { 202 | border-color: #66afe9; 203 | outline: 0; 204 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); 205 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); 206 | } 207 | 208 | input:hover, textarea:hover,select:hover { 209 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 133, .6); 210 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 133, .6); 211 | } 212 | 213 | input[readonly], textarea[readonly],select[readonly]{ 214 | background: #eeeeee; 215 | color: #000; 216 | } 217 | 218 | input[disabled], textarea[disabled],select[disabled]{ 219 | background: #F0FFFF !important; 220 | color: #808080; !important; 221 | } 222 | 223 | textarea { 224 | padding-right: .5em; 225 | text-indent: 0; 226 | vertical-align: top; 227 | } 228 | 229 | [type="number"]::-webkit-inner-spin-button, 230 | [type="number"]::-webkit-outer-spin-button { 231 | height: auto; 232 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DB2Java 6 | 7 | 41 | 42 | 43 | 44 |

DataBase生成Java源代码

45 | 46 |
47 |

48 | 54 | 57 | 60 | 63 |

64 | 65 |

66 | 69 | 72 | 73 | 74 |

75 |
76 |
77 |
78 |

请选择要生成的表

79 | 80 | 81 | 82 | 84 | 85 | 86 | 87 | 88 | 107 | 108 |
# 83 | 表名类名
109 |
110 |
111 | 112 |

113 | 下划线分隔列名转换为 114 | 118 |

119 |

datetime转换为 120 |

125 |

timestamp转换为 126 |

131 |

date转换为 132 | 137 |

138 | 139 |
140 |

141 |  类包名: 142 | 143 |

144 |

145 | 146 | 存放目录: 147 | 148 | 151 | 152 | 155 | 156 |

157 |

158 | 159 |

160 |
161 |
162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /js/controller.js: -------------------------------------------------------------------------------- 1 | const DBProfileVM = require("./js/vm/DBProfileVM"); 2 | const GenerateVM = require("./js/vm/GenerateVM"); 3 | const service=require("./js/service"); 4 | 5 | //当前dbProfile 6 | let currentDBProfile; 7 | 8 | 9 | //连接数据库按钮点击事件处理 10 | $("#connectionBtn").addEventListener("click", function(evt) { 11 | this.disabled=true; 12 | //加载视图模型 13 | try{ 14 | currentDBProfile=DBProfileVM.load(); 15 | }catch(e){ 16 | this.disabled=false; 17 | toast(e); 18 | return; 19 | } 20 | //加载表 21 | service.loadDatabaseTableNames(currentDBProfile) 22 | .then(function(tables){ 23 | //计算出对应类名 24 | let result=tables.map(t=>{ 25 | return { 26 | tableName:t, 27 | //首字母要大写 28 | className:$.underline2camelcasing(t,true) 29 | } 30 | }); 31 | 32 | $("#table-list-view-tbody").innerHTML=paintTablesList(result); 33 | 34 | }).catch(err=>{ 35 | if(err.errno==="ECONNREFUSED"){ 36 | alert("数据库拒绝连接,请检查数据库配置"); 37 | }else{ 38 | alert(err); 39 | } 40 | }).finally(()=>{ 41 | this.disabled=false; 42 | }); 43 | 44 | }); 45 | 46 | /** 47 | * 表名行添加事件监听 48 | */ 49 | $("#table-list-view-tbody").addEventListener("click", function(evt) { 50 | evt.stopPropagation(); 51 | let target = evt.target; 52 | if (target.tagName === "TD") { 53 | let checkBox = target.parentNode.getElementsByTagName("input")[0]; 54 | if(checkBox.checked){ 55 | checkBox.checked=false; 56 | }else{ 57 | checkBox.checked=true; 58 | } 59 | } 60 | 61 | }); 62 | 63 | /** 64 | * 执行按钮 65 | */ 66 | $("#action-btn").addEventListener("click",function(evt){ 67 | //1 禁用按钮 68 | this.disabled=true; 69 | //2 任务输入对象 70 | let generateVM; 71 | try{ 72 | //从视图加载生成文件参数的ViewModel对象 73 | generateVM=GenerateVM.load(); 74 | }catch(e){ 75 | toast(e.message); 76 | this.disabled=false; 77 | } 78 | //异步开启任务 79 | service.generateFiles(currentDBProfile,generateVM).then(function(taskResults){ 80 | alert("生成完毕,共生成"+taskResults.length+"个文件"); 81 | }).catch(function(e){ 82 | console.log(e); 83 | alert(e.message); 84 | }).finally(()=>{ 85 | this.disabled=false; 86 | }); 87 | }); 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /js/dto/ClassModel.js: -------------------------------------------------------------------------------- 1 | const Assert=require("../util/Assert"); 2 | function ClassModel(){ 3 | this.imports=[]; 4 | this.packageName; 5 | this.className; 6 | this.fields=[]; 7 | } 8 | ClassModel.prototype.addField=function(f){ 9 | this.fields.push(f); 10 | //1 获取字段全类型 11 | let fullType=f.fullType; 12 | //2 截取包名 13 | let typePackageEndIndex=fullType.lastIndexOf('.'); 14 | if(typePackageEndIndex!=-1){ 15 | let typePackage=fullType.substring(0,typePackageEndIndex); 16 | if(typePackage!=="java.lang"){ 17 | if(!this.imports[typePackage]){ 18 | this.imports[typePackage]=true; 19 | this.imports.push(typePackage); 20 | } 21 | } 22 | } 23 | } 24 | 25 | 26 | function FieldModel(){ 27 | //列类型描述 28 | this.columnType; 29 | //java类型全限定类名 30 | this.fullType; 31 | //java类型名称(无包名) 32 | this.simpleType; 33 | //字段名称 34 | this.name; 35 | 36 | this.defaultValue; 37 | this.comment; 38 | }; 39 | FieldModel.prototype.setName=function(name,options){ 40 | if(options["underlineOption"]==="camel"){ 41 | this.name=$.underline2camelcasing(name); 42 | }else{ 43 | this.name=name; 44 | } 45 | } 46 | //设置 47 | FieldModel.prototype.setColumnType=function(columnType,options){ 48 | 49 | this.columnType=columnType; 50 | //2 去掉类型中的小括号 51 | let endIndex=columnType.lastIndexOf('('); 52 | columnType=columnType.substring(0,endIndex===-1?columnType.length:endIndex); 53 | 54 | //3 得到转换规则 55 | const mapping=Object.assign({},FieldModel.TYPE_MAPPING,options); 56 | console.log(mapping); 57 | 58 | //4 得到java类型 59 | this.simpleType=this.fullType=mapping[columnType.toLowerCase()]; 60 | 61 | //5 截取简单java类型 62 | let simpleTypeEndIndex=this.fullType.lastIndexOf('.'); 63 | if(simpleTypeEndIndex!==-1){ 64 | this.simpleType=this.fullType.substr(simpleTypeEndIndex+1); 65 | } 66 | } 67 | 68 | 69 | FieldModel.TYPE_MAPPING={ 70 | "bit":"boolean", 71 | "tinyint":"int", 72 | "smallint":"int", 73 | "mediumint":"int", 74 | "int":"int", 75 | "integer":"int", 76 | "bigint":"int", 77 | "double":"double", 78 | "float":"float", 79 | "decimal":"double", 80 | "numeric":"double", 81 | 82 | "date":"java.time.LocalDate", 83 | "time":"java.time.LocalTime", 84 | "year":"int", 85 | "timestamp":"java.time.LocalDateTime", 86 | "datetime":"java.time.LocalDateTime", 87 | 88 | "char":"java.lang.String", 89 | "varchar":"java.lang.String", 90 | "tinytext":"java.lang.String", 91 | "text":"java.lang.String", 92 | "mediumint":"java.lang.String", 93 | "longtext":"java.lang.String" 94 | 95 | 96 | 97 | } 98 | 99 | module.exports={ClassModel,FieldModel}; -------------------------------------------------------------------------------- /js/repository/DBUtil.js: -------------------------------------------------------------------------------- 1 | const mysql = require("mysql"); 2 | /* 3 | */ 4 | let DBUtil=function(){}; 5 | 6 | let currentProfile; 7 | let connection; 8 | 9 | 10 | DBUtil.getConnection=function(profile){ 11 | if(!profile){ 12 | throw new Error("无法获取连接,无profile"); 13 | } 14 | //同一个profile 15 | if(profile.equals(currentProfile)){ 16 | //判断是否关闭 17 | if(connection&&connection.state==="connected"){ 18 | return connection; 19 | } 20 | 21 | } 22 | currentProfile=profile; 23 | return connection=mysql.createConnection({ 24 | host:profile.host||"127.0.0.1", 25 | port:profile.port||3306, 26 | database:profile.database, 27 | user:profile.username, 28 | password:profile.password 29 | }); 30 | }; 31 | 32 | module.exports=DBUtil; -------------------------------------------------------------------------------- /js/repository/TableDao.js: -------------------------------------------------------------------------------- 1 | const DBUtil=require("../repository/DBUtil"); 2 | const {TableEntity,TableColumnEntity}=require("../repository/TableEntity"); 3 | 4 | function TableDao(profile){ 5 | this.profile=profile; 6 | } 7 | 8 | 9 | TableDao.prototype.selectColumnsByTable=function(tableName){ 10 | let profile=this.profile; 11 | return new Promise(function(ok,fail){ 12 | let sql =`SELECT 13 | column_name as columnName, 14 | column_default as defaultValue, 15 | column_type as columnType, 16 | column_comment as comment 17 | FROM information_schema.COLUMNS 18 | WHERE 19 | table_schema =? 20 | AND table_name =? 21 | order by ORDINAL_POSITION`; 22 | let connection=DBUtil.getConnection(profile); 23 | 24 | 25 | connection.query(sql, [profile.database,tableName], function(err, rows) { 26 | if(err){ 27 | fail(err); 28 | connection.destory(); 29 | }else{ 30 | let columns=[]; 31 | for(let i=0;i t["tableName"])); 53 | } 54 | }); 55 | 56 | }); 57 | }; 58 | 59 | 60 | let row2entity=function(row){ 61 | const e=new TableColumnEntity(); 62 | e.name=row["columnName"]; 63 | e.dataType=row["columnType"]; 64 | e.defaultValue=row["defaultValue"]; 65 | e.comment=row["comment"]; 66 | return e; 67 | } 68 | 69 | module.exports=TableDao; -------------------------------------------------------------------------------- /js/repository/TableEntity.js: -------------------------------------------------------------------------------- 1 | function TableEntity(){ 2 | this.name; 3 | this.columns; 4 | } 5 | 6 | function TableColumnEntity(){ 7 | this.name; 8 | this.dataType; 9 | this.defaultValue; 10 | this.comment; 11 | } 12 | 13 | module.exports={TableEntity,TableColumnEntity}; -------------------------------------------------------------------------------- /js/service.js: -------------------------------------------------------------------------------- 1 | const TableDao=require("./repository/TableDao"); 2 | const {ClassModel,FieldModel}=require("./dto/ClassModel"); 3 | const templateFileLoader=require("./templateFileLoader"); 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | 7 | //模板文件内容生成方法 8 | let templateFileFunctions={}; 9 | 10 | try{ 11 | const templateFiles=templateFileLoader.load(); 12 | for(let f in templateFiles){ 13 | templateFileFunctions[f]=new TemplateFunction(f+"(m)",templateFiles[f]).build(); 14 | } 15 | console.log(templateFileFunctions); 16 | }catch(e){ 17 | console.error(e); 18 | alert("无法加载模板文件,请检查template目录下文件"); 19 | } 20 | /** 21 | * 通过数据库名称查找表名称 22 | */ 23 | const loadDatabaseTableNames=function(profile){ 24 | let dao=new TableDao(profile); 25 | return dao.selectTables(); 26 | }; 27 | 28 | /** 29 | * 生成文件 30 | */ 31 | let generateFiles=function(profileVM,generateVM){ 32 | let dao=new TableDao(profileVM); 33 | //所有异步任务 34 | let allTask=[]; 35 | //遍历表 36 | for(let table of generateVM.tableNames){ 37 | //添加异步任务 38 | allTask.push(new Promise(function(ok,fail){ 39 | 40 | dao.selectColumnsByTable(table).then(function(columns){ 41 | try{ 42 | generateClassFile(generateVM,table,columns); 43 | ok(1); 44 | }catch(e){ 45 | fail(e); 46 | } 47 | }).catch(fail); 48 | })); 49 | } 50 | 51 | return Promise.all(allTask); 52 | }; 53 | /** 54 | * 生成模板文件 55 | */ 56 | const generateClassFile=function(generateModel,tableName,columns){ 57 | const classModel=new ClassModel(); 58 | 59 | //2 package 60 | classModel.packageName=generateModel.packageName; 61 | //3 className 62 | classModel.className=generateModel.getClassNameByTableName(tableName); 63 | //4 fields 64 | for(let col of columns){ 65 | classModel.addField(column2field(col,generateModel)); 66 | } 67 | // 5 创建存放包目录 68 | let saveDir=generateModel.fileSaveDir; 69 | saveDir+="/"+classModel.packageName.replace(".","/"); 70 | mkdirsSync(saveDir); 71 | 72 | //6 获取模板 73 | for(let f in templateFileFunctions){ 74 | const fileContents=templateFileFunctions[f].call(window,classModel); 75 | let fileName=saveDir+"/"+classModel.className+".java"; 76 | fs.writeFileSync(fileName,fileContents,{encoding:"utf-8"}); 77 | } 78 | } 79 | 80 | const mkdirsSync=function(file){ 81 | if(fs.existsSync(file)){ 82 | return; 83 | } 84 | mkdirsSync(path.dirname(file)); 85 | fs.mkdirSync(file,{recursive:true}); 86 | } 87 | 88 | const column2field=function(col,generateModel){ 89 | const f=new FieldModel(); 90 | f.setName(col.name,generateModel.generateOptions); 91 | f.setColumnType(col.dataType,generateModel.mappingOptions); 92 | f.comment=col.comment; 93 | f.defaultValue=col.defaultValue; 94 | return f; 95 | } 96 | 97 | module.exports={loadDatabaseTableNames,generateFiles}; 98 | 99 | -------------------------------------------------------------------------------- /js/templateFileLoader.js: -------------------------------------------------------------------------------- 1 | const fs=require("fs"); 2 | const TEMPLATE_FILE_DIRECTORY="./template"; 3 | 4 | const templateFiles={}; 5 | 6 | const load=function(){ 7 | fs.readdirSync(TEMPLATE_FILE_DIRECTORY).forEach(function(f){ 8 | //6 读取模板 9 | const bytes=fs.readFileSync(TEMPLATE_FILE_DIRECTORY+"/"+f,'utf8'); 10 | templateFiles[f]=bytes; 11 | }); 12 | 13 | return templateFiles; 14 | } 15 | module.exports={ 16 | load 17 | }; 18 | -------------------------------------------------------------------------------- /js/util/$.js: -------------------------------------------------------------------------------- 1 | let $=function(str,ctx=document){ 2 | let result=ctx.querySelectorAll(str); 3 | if(result.length===0)return null; 4 | if(result.length===1)return result[0]; 5 | return result; 6 | }; 7 | 8 | $.assertNotBlank=function(el,message){ 9 | 10 | }; 11 | $.upperFirstLetter=function(s){ 12 | let cs=s.split(""); 13 | let c=cs[0]; 14 | if('a'<=c&&c<='z'){ 15 | cs[0]=c.toUpperCase(); 16 | } 17 | return cs.join(""); 18 | } 19 | /** 20 | * 下划线分别改为驼峰命名法 21 | */ 22 | $.underline2camelcasing=function(s,upperFirstLetter){ 23 | let parts=s.split("_"); 24 | let end=upperFirstLetter?0:1; 25 | for(let i=parts.length;i-- >end;){ 26 | parts[i]=$.upperFirstLetter(parts[i]); 27 | } 28 | return parts.join(""); 29 | } 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /js/util/Assert.js: -------------------------------------------------------------------------------- 1 | function Assert(){} 2 | 3 | 4 | Assert.stringNotBlank=function(message,s){ 5 | if(typeof s==='undefined' 6 | ||s===null 7 | ||(s=s.trim()).length===0 8 | ){ 9 | throw new Error(message); 10 | } 11 | return s; 12 | }; 13 | 14 | Assert.arrayNotEmpty=function(message,arr){ 15 | if(typeof arr==='undefined' 16 | ||arr===null 17 | ||arr.length===0 18 | ){ 19 | throw new Error(message); 20 | } 21 | return arr; 22 | }; 23 | 24 | module.exports=Assert; -------------------------------------------------------------------------------- /js/util/yi.template.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /** 3 | * @param {String} nameAndArgs 方法声明为xxx(a,b) 4 | * @param {String} templateSource 字符串模板 5 | */ 6 | let TemplateFunction =window.TemplateFunction= function (nameAndArgs, templateSource) { 7 | //方法声明格式为xxx或xxx(a,b) 8 | this.nameAndArgs = nameAndArgs; 9 | //模板字符串 10 | this.templateSource = templateSource; 11 | //模板方法名称 12 | this.functionName=null; 13 | //模板方法参数 14 | this.functionArgumentNames=null; 15 | //模板方法体 16 | this.body = []; 17 | //生成方法引用 18 | this.functionReference=null; 19 | 20 | privateMethod.init.apply(this); 21 | }; 22 | //私有方法 23 | let privateMethod = { 24 | init: function () { 25 | //解析 26 | let result = staticMethod.parseFunctionNameAndArgumentNames(this.nameAndArgs); 27 | this.functionName = result[0]; 28 | this.functionArgumentNames = result[1]; 29 | } 30 | }; 31 | //方法中的 32 | TemplateFunction.helper={ 33 | print:function(s,ds){ 34 | if(s===null||s===undefined)return ds||''; 35 | return String(s); 36 | }, 37 | escapePrint:function(s,ds){ 38 | if(s===null||s===undefined)return ds||''; 39 | return String(s).replace(/[<>&]/g, function (c) { 40 | return {'<': '<', '>': '>', '&': '&'}[c] 41 | }) 42 | } 43 | }; 44 | /** 45 | * 公开方法 46 | */ 47 | TemplateFunction.prototype = { 48 | //构建模板方法 49 | build: function () { 50 | 51 | let out = this.body; 52 | //添加工具方法 53 | out.push("let helper=window.TemplateFunction.helper,"); 54 | out.push("$=helper.print,"); 55 | out.push("_='';\n"); 56 | //添加try-finally保证模板方法返回值 57 | out.push("try{\n"); 58 | 59 | //构建方法体 60 | out.parseCode = staticMethod.parseCode; 61 | out.parseText = staticMethod.parseText; 62 | out.parseExpInText = staticMethod.parseExpInText; 63 | out.parseExpInCode=staticMethod.parseExpInCode; 64 | out.parseTemplate=staticMethod.parseTemplate; 65 | 66 | out.parseTemplate(this.templateSource,0); 67 | //添加finally保证模板方法返回值 68 | out.push("\n}catch(e){console.log(e)}finally{return _;}"); 69 | 70 | let funBody = out.join(""); 71 | let args = this.functionArgumentNames; 72 | try { 73 | //创建方法 74 | let f = args ? new Function(args, funBody) : new Function(funBody); 75 | this.functionReference=f; 76 | return f; 77 | } catch (e) { 78 | console.log(e); 79 | console.log(funBody); 80 | } 81 | } 82 | 83 | }; 84 | 85 | let staticMethod = { 86 | parseFunctionNameAndArgumentNames: function (funName) { 87 | let begin = funName.indexOf("("); 88 | if (begin === -1) { 89 | return [funName, null]; 90 | } else { 91 | return [funName.substring(0, begin), funName.substring(begin + 1, funName.lastIndexOf(")"))]; 92 | } 93 | }, 94 | parseTemplate:function(template,begin){ 95 | let textBeginIndex =-1; 96 | for (let i = begin, z = template.length,c; i < z; i++) { 97 | c = template.charAt(i); 98 | if(c==='<'&&template.charAt(i + 1) === '-'){ 99 | let codeBeginIndex = i + 2; 100 | let codeEndIndex=codeBeginIndex; 101 | //find the code end 102 | for(;codeEndIndex') { 105 | //handle the text 106 | if (textBeginIndex>=0) { 107 | if(textBeginIndex!==i){ 108 | this.parseText(template.substring(textBeginIndex, i)); 109 | textBeginIndex=-1; 110 | } 111 | } 112 | this.parseCode(template.substring(codeBeginIndex, codeEndIndex),0); 113 | 114 | return this.parseTemplate(template,codeEndIndex+2); 115 | } 116 | } 117 | }else if(c==='$'&&template.charAt(i + 1) === '('){ 118 | let expBeginIndex = i + 2; 119 | let expEndIndex=expBeginIndex; 120 | let nestingExpCount=1; 121 | //find the code end 122 | for(;expEndIndex=0) { 133 | if(textBeginIndex!==i){ 134 | this.parseText(template.substring(textBeginIndex, i)); 135 | textBeginIndex=-1; 136 | } 137 | } 138 | this.parseExpInText(template.substring(expBeginIndex, expEndIndex)); 139 | this.parseTemplate(template,expEndIndex+1); 140 | return; 141 | } 142 | } 143 | } 144 | }else{ 145 | if(textBeginIndex===-1){ 146 | textBeginIndex=i; 147 | } 148 | } 149 | }//end for 150 | }, 151 | 152 | parseCode: function (code,begin) { 153 | let codeBeginIndex =-1; 154 | for (let i= begin, z = code.length,c; i < z; i++) { 155 | c = code.charAt(i); 156 | if(c==='$'&&code.charAt(i + 1) === '('){ 157 | let expBeginIndex = i + 2; 158 | let expEndIndex=expBeginIndex; 159 | let nestingExpCount=1; 160 | //find the code end 161 | for(;expEndIndex=0) { 170 | if(codeBeginIndex!==i){ 171 | this.push(code.substring(codeBeginIndex, i)); 172 | codeBeginIndex=-1; 173 | } 174 | } 175 | this.parseExpInCode(code.substring(expBeginIndex, expEndIndex)); 176 | return this.parseCode(code,expEndIndex+1); 177 | } 178 | } 179 | } 180 | 181 | }else{ 182 | if(codeBeginIndex===-1){ 183 | codeBeginIndex=i; 184 | } 185 | } 186 | } 187 | if (codeBeginIndex>=0) { 188 | this.push(code.substr(codeBeginIndex)); 189 | } 190 | }, 191 | 192 | parseExpInText: function (exp) { 193 | this.push("_+=$(" + exp + ");"); 194 | }, 195 | parseExpInCode:function (exp) { 196 | this.push("_+=$(" + exp + ")"); 197 | }, 198 | parseText: function (txt) { 199 | //双引号转义 200 | txt = txt.replace(/"/g, '\\"'); 201 | //换行符分割 202 | const lines = txt.split(/\r?\n/); 203 | if (lines.length > 0) { 204 | let i = 0, z = lines.length - 1; 205 | while (i < z) { 206 | //一行一行输出,每行多输出一个换行 207 | this.push('_+="' + lines[i++] + '\\r\\n";\n'); 208 | } 209 | this.push('_+="' + lines[z] + '";'); 210 | } 211 | } 212 | 213 | }; 214 | 215 | 216 | window.addEventListener("load",function(){ 217 | //通过