(function () { Vue.component('vue-item', { props: ['jsondata', 'theme'], template: '#item-template' }) Vue.component('vue-outer', { props: ['jsondata', 'isend', 'path', 'theme'], template: '#outer-template' }) Vue.component('vue-expand', { props: [], template: '#expand-template' }) Vue.component('vue-val', { props: ['field', 'val', 'isend', 'path', 'theme'], template: '#val-template' }) Vue.use({ install: function (Vue, options) { // 判断数据类型 Vue.prototype.getTyp = function (val) { return toString.call(val).split(']')[0].split(' ')[1] } // 判断是否是对象或者数组,以对下级进行渲染 Vue.prototype.isObjectArr = function (val) { return ['Object', 'Array'].indexOf(this.getTyp(val)) > -1 } // 折叠 Vue.prototype.fold = function ($event) { var target = Vue.prototype.expandTarget($event) target.siblings('svg').show() target.hide().parent().siblings('.expand-view').hide() target.parent().siblings('.fold-view').show() } // 展开 Vue.prototype.expand = function ($event) { var target = Vue.prototype.expandTarget($event) target.siblings('svg').show() target.hide().parent().siblings('.expand-view').show() target.parent().siblings('.fold-view').hide() } //获取展开折叠的target Vue.prototype.expandTarget = function ($event) { switch($event.target.tagName.toLowerCase()) { case 'use': return $($event.target).parent() case 'label': return $($event.target).closest('.fold-view').siblings('.expand-wraper').find('.icon-square-plus').first() default: return $($event.target) } } // 格式化值 Vue.prototype.formatVal = function (val) { switch(Vue.prototype.getTyp(val)) { case 'String': return '"' + val + '"' case 'Null': return 'null' default: return val } } // 判断值是否是链接 Vue.prototype.isaLink = function (val) { return /^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+/.test(val) } // 计算对象的长度 Vue.prototype.objLength = function (obj) { return Object.keys(obj).length } /**渲染 JSON key:value 项 * @author TommyLemon * @param val * @param key * @return {boolean} */ Vue.prototype.onRenderJSONItem = function (val, key, path) { if (isSingle || key == null) { return true } if (key == '_$_this_$_') { // return true return false } var method = App.getMethod(); var mIndex = method == null ? -1 : method.indexOf('.'); var isRestful = mIndex > 0 && mIndex < method.length - 1; try { if (val instanceof Array) { if (val[0] instanceof Object && (val[0] instanceof Array == false) && JSONObject.isArrayKey(key, null, isRestful)) { // alert('onRenderJSONItem key = ' + key + '; val = ' + JSON.stringify(val)) var ckey = key.substring(0, key.lastIndexOf('[]')); var aliaIndex = ckey.indexOf(':'); var objName = aliaIndex < 0 ? ckey : ckey.substring(0, aliaIndex); var firstIndex = objName.indexOf('-'); var firstKey = firstIndex < 0 ? objName : objName.substring(0, firstIndex); for (var i = 0; i < val.length; i++) { var cPath = (StringUtil.isEmpty(path, false) ? '' : path + '/') + key; if (JSONObject.isTableKey(firstKey, val, isRestful)) { // var newVal = parseJSON(JSON.stringify(val[i])) var newVal = {} for (var k in val[i]) { newVal[k] = val[i][k] //提升性能 delete val[i][k] } val[i]._$_this_$_ = JSON.stringify({ path: cPath + '/' + i, table: firstKey }) for (var k in newVal) { val[i][k] = newVal[k] } } else { this.onRenderJSONItem(val[i], '' + i, cPath); } // this.$children[i]._$_this_$_ = key // alert('this.$children[i]._$_this_$_ = ' + this.$children[i]._$_this_$_) } } } else if (val instanceof Object) { var aliaIndex = key.indexOf(':'); var objName = aliaIndex < 0 ? key : key.substring(0, aliaIndex); // var newVal = parseJSON(JSON.stringify(val)) var newVal = {} for (var k in val) { newVal[k] = val[k] //提升性能 delete val[k] } val._$_this_$_ = JSON.stringify({ path: (StringUtil.isEmpty(path, false) ? '' : path + '/') + key, table: JSONObject.isTableKey(objName, val, isRestful) ? objName : null }) for (var k in newVal) { val[k] = newVal[k] } // val = Object.assign({ _$_this_$_: objName }, val) //解决多显示一个逗号 , // this._$_this_$_ = key TODO 不影响 JSON 的方式,直接在组件读写属性 // alert('this._$_this_$_ = ' + this._$_this_$_) } } catch (e) { alert('onRenderJSONItem try { ... } catch (e) {\n' + e.message) } return true } /**显示 Response JSON 的注释 * @author TommyLemon * @param val * @param key * @param $event */ Vue.prototype.setResponseHint = function (val, key, $event) { console.log('setResponseHint') this.$refs.responseKey.setAttribute('data-hint', isSingle ? '' : this.getResponseHint(val, key, $event)); } /**获取 Response JSON 的注释 * 方案一: * 拿到父组件的 key,逐层向下传递 * 问题:拿不到爷爷组件 "Comment[]": [ { "id": 1, "content": "content1" }, { "id": 2 }... ] * * 方案二: * 改写 jsonon 的 refKey 为 key0/key1/.../refKey * 问题:遍历,改 key;容易和特殊情况下返回的同样格式的字段冲突 * * 方案三: * 改写 jsonon 的结构,val 里加 .path 或 $.path 之类的隐藏字段 * 问题:遍历,改 key;容易和特殊情况下返回的同样格式的字段冲突 * * @author TommyLemon * @param val * @param key * @param $event */ Vue.prototype.getResponseHint = function (val, key, $event) { // alert('setResponseHint key = ' + key + '; val = ' + JSON.stringify(val)) var s = '' try { var path = null var table = null var column = null var method = App.getMethod(); var mIndex = method == null ? -1 : method.indexOf('.'); var isRestful = mIndex > 0 && mIndex < method.length - 1; if (val instanceof Object && (val instanceof Array == false)) { var parent = $event.currentTarget.parentElement.parentElement var valString = parent.textContent // alert('valString = ' + valString) var i = valString.indexOf('"_$_this_$_": "') if (i >= 0) { valString = valString.substring(i + '"_$_this_$_": "'.length) i = valString.indexOf('}"') if (i >= 0) { valString = valString.substring(0, i + 1) // alert('valString = ' + valString) var _$_this_$_ = parseJSON(valString) || {} path = _$_this_$_.path table = _$_this_$_.table } var aliaIndex = key == null ? -1 : key.indexOf(':'); var objName = aliaIndex < 0 ? key : key.substring(0, aliaIndex); if (JSONObject.isTableKey(objName, val, isRestful)) { table = objName } else if (JSONObject.isTableKey(table, val, isRestful)) { column = key } // alert('path = ' + path + '; table = ' + table + '; column = ' + column) } } else { var parent = $event.currentTarget.parentElement.parentElement var valString = parent.textContent // alert('valString = ' + valString) var i = valString.indexOf('"_$_this_$_": "') if (i >= 0) { valString = valString.substring(i + '"_$_this_$_": "'.length) i = valString.indexOf('}"') if (i >= 0) { valString = valString.substring(0, i + 1) // alert('valString = ' + valString) var _$_this_$_ = parseJSON(valString) || {} path = _$_this_$_.path table = _$_this_$_.table } } if (val instanceof Array && JSONObject.isArrayKey(key, val, isRestful)) { var key2 = key == null ? null : key.substring(0, key.lastIndexOf('[]')); var aliaIndex = key2 == null ? -1 : key2.indexOf(':'); var objName = aliaIndex < 0 ? key2 : key2.substring(0, aliaIndex); var firstIndex = objName == null ? -1 : objName.indexOf('-'); var firstKey = firstIndex < 0 ? objName : objName.substring(0, firstIndex); // alert('key = ' + key + '; firstKey = ' + firstKey + '; firstIndex = ' + firstIndex) if (JSONObject.isTableKey(firstKey, null, isRestful)) { table = firstKey var s0 = ''; if (firstIndex > 0) { objName = objName.substring(firstIndex + 1); firstIndex = objName.indexOf('-'); column = firstIndex < 0 ? objName : objName.substring(0, firstIndex) var pathUri = (StringUtil.isEmpty(path) ? '' : path + '/') + key; var c = CodeUtil.getCommentFromDoc(docObj == null ? null : docObj['[]'], table, column, App.getMethod(), App.database, App.language, true, false, pathUri.split('/'), isRestful, val); // this.getResponseHint({}, table, $event s0 = column + (StringUtil.isEmpty(c, true) ? '' : ': ' + c) } var pathUri = (StringUtil.isEmpty(path) ? '' : path + '/') + (StringUtil.isEmpty(column) ? key : column); var c = CodeUtil.getCommentFromDoc(docObj == null ? null : docObj['[]'], table, isRestful ? key : null, App.getMethod(), App.database, App.language, true, false, pathUri.split('/'), isRestful, val); s = (StringUtil.isEmpty(path) ? '' : path + '/') + key + ' 中 ' + ( StringUtil.isEmpty(c, true) ? '' : table + ': ' + c + ((StringUtil.isEmpty(s0, true) ? '' : ' - ' + s0) ) ); return s; } //导致 key[] 的 hint 显示为 key[]key[] else { // s = (StringUtil.isEmpty(path) ? '' : path + '/') + key // } } else { if (isRestful || JSONObject.isTableKey(table)) { column = key } // alert('path = ' + path + '; table = ' + table + '; column = ' + column) } } // alert('setResponseHint table = ' + table + '; column = ' + column) var pathUri = (StringUtil.isEmpty(path) ? '' : path + '/') + key; var c = CodeUtil.getCommentFromDoc(docObj == null ? null : docObj['[]'], table, isRestful ? key : column, method, App.database, App.language, true, false, pathUri.split('/'), isRestful, val); s += pathUri + (StringUtil.isEmpty(c, true) ? '' : ': ' + c) } catch (e) { s += '\n' + e.message } return s; } } }) var DEBUG = false var initJson = {} // 主题 [key, String, Number, Boolean, Null, link-link, link-hover] var themes = [ ['#92278f', '#3ab54a', '#25aae2', '#f3934e', '#f34e5c', '#717171'], ['rgb(19, 158, 170)', '#cf9f19', '#ec4040', '#7cc500', 'rgb(211, 118, 126)', 'rgb(15, 189, 170)'], ['#886', '#25aae2', '#e60fc2', '#f43041', 'rgb(180, 83, 244)', 'rgb(148, 164, 13)'], ['rgb(97, 97, 102)', '#cf4c74', '#20a0d5', '#cd1bc4', '#c1b8b9', 'rgb(25, 8, 174)'] ] // APIJSON <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< var OPERATE_TYPE_RECORD = 'RECORD' var OPERATE_TYPE_REVIEW = 'REVIEW' var OPERATE_TYPE_REPLAY = 'REPLAY' var REQUEST_TYPE_PARAM = 'PARAM' // GET ?a=1&b=c&key=value var REQUEST_TYPE_FORM = 'FORM' // POST x-www-form-urlencoded var REQUEST_TYPE_DATA = 'DATA' // POST form-data var REQUEST_TYPE_JSON = 'JSON' // POST application/json var REQUEST_TYPE_GRPC = 'GRPC' // POST application/json var RANDOM_DB = 'RANDOM_DB' var RANDOM_IN = 'RANDOM_IN' var RANDOM_INT = 'RANDOM_INT' var RANDOM_NUM = 'RANDOM_NUM' var RANDOM_STR = 'RANDOM_STR' var ORDER_DB = 'ORDER_DB' var ORDER_IN = 'ORDER_IN' var ORDER_INT = 'ORDER_INT' var ORDER_MAP = {} function randomInt(min, max) { return randomNum(min, max, 0); } function randomNum(min, max, precision) { // 0 居然也会转成 Number.MIN_SAFE_INTEGER !!! // start = start || Number.MIN_SAFE_INTEGER // end = end || Number.MAX_SAFE_INTEGER if (min == null) { min = Number.MIN_SAFE_INTEGER } if (max == null) { max = Number.MAX_SAFE_INTEGER } if (precision == null) { precision = 2 } return + ((max - min)*Math.random() + min).toFixed(precision); } function randomStr(minLength, maxLength, availableChars) { return 'Ab_Cd' + randomNum(); } function randomIn(...args) { return args == null || args.length <= 0 ? null : args[randomInt(0, args.length - 1)]; } function orderInt(desc, index, min, max) { if (min == null) { min = Number.MIN_SAFE_INTEGER } if (max == null) { max = Number.MAX_SAFE_INTEGER } if (desc) { return max - index%(max - min + 1) } return min + index%(max - min + 1) } function orderIn(desc, index, ...args) { // alert('orderIn index = ' + index + '; args = ' + JSON.stringify(args)); index = index || 0; return args == null || args.length <= index ? null : args[desc ? args.length - index : index]; } function getOrderIndex(randomId, line, argCount) { // alert('randomId = ' + randomId + '; line = ' + line + '; argCount = ' + argCount); // alert('ORDER_MAP = ' + JSON.stringify(ORDER_MAP, null, ' ')); if (randomId == null) { randomId = 0; } if (ORDER_MAP == null) { ORDER_MAP = {}; } if (ORDER_MAP[randomId] == null) { ORDER_MAP[randomId] = {}; } var orderIndex = ORDER_MAP[randomId][line]; // alert('orderIndex = ' + orderIndex) if (orderIndex == null || orderIndex < -1) { orderIndex = -1; } orderIndex ++ orderIndex = argCount == null || argCount <= 0 ? orderIndex : orderIndex%argCount; ORDER_MAP[randomId][line] = orderIndex; // alert('orderIndex = ' + orderIndex) // alert('ORDER_MAP = ' + JSON.stringify(ORDER_MAP, null, ' ')); return orderIndex; } //这些全局变量不能放在data中,否则会报undefined错误 var baseUrl var inputted var handler var docObj var doc var output var isSingle = false var doneCount // APIJSON >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> var App = new Vue({ el: '#app', data: { baseview: 'formater', view: 'output', jsoncon: JSON.stringify(initJson), jsonhtml: initJson, compressStr: '', error: {}, requestVersion: 3, requestCount: 1, urlComment: ' // 1080x2340, Android 9.0, Xiaomi MIX 3, IMEI 8698100377666021', historys: [], history: {name: '请求0'}, remotes: [], locals: [], testCases: [], randoms: [], randomSubs: [], account: '13000082001', password: '123456', accounts: [ { 'isLoggedIn': false, 'name': '测试账号1', 'phone': '13000082001', 'password': '123456' }, { 'isLoggedIn': false, 'name': '测试账号2', 'phone': '13000082002', 'password': '123456' }, { 'isLoggedIn': false, 'name': '测试账号3', 'phone': '13000082003', 'password': '123456' } ], currentAccountIndex: 0, tests: { '-1':{}, '0':{}, '1':{}, '2': {} }, crossProcess: '交叉账号:已关闭', testProcess: '机器学习:已关闭', randomTestTitle: '步骤 Step', testRandomCount: 1, testRandomProcess: '', compareColor: '#0000', isDelayShow: false, isSaveShow: false, isExportShow: false, isExportRandom: false, isTestCaseShow: false, isHeaderShow: false, isRandomShow: true, // 默认展示 isRandomListShow: false, isRandomSubListShow: false, isRandomEditable: false, isLoginShow: false, isConfigShow: false, isDeleteShow: false, currentDocItem: {}, currentRemoteItem: {}, currentRandomItem: {}, eventList: [], outputList: [], isAdminOperation: false, loginType: 'login', isExportRemote: false, isRegister: false, isCrossEnabled: false, isMLEnabled: false, isDelegateEnabled: false, isLocalShow: false, exTxt: { name: 'APIJSON测试', button: '保存', index: 0 }, themes: themes, checkedTheme: 0, isExpand: true, User: { id: 0, name: '', head: '' }, Privacy: { id: 0, balance: null //点击更新提示需要判空 0.00 }, isVideoFirst: false, type: OPERATE_TYPE_REVIEW, types: [ OPERATE_TYPE_RECORD, OPERATE_TYPE_REVIEW, OPERATE_TYPE_REPLAY ], // host: 'uiauto.UIAutoApp', // 'unitauto.test.TestUtil.', host: 'uigo.x.UIAutoApp', // 'unitauto.test.TestUtil.', branch: 'countArray', database: 'MYSQL',// 'POSTGRESQL', schema: 'sys', server: 'http://apijson.cn:9090', // admin.Flow does not exist 'http://apijson.org:8080', // server: 'http://47.74.39.68:9090', // apijson.org project: 'http://apijson.cn:8081', //apijson.cn language: CodeUtil.LANGUAGE_KOTLIN, header: {}, page: 0, count: 50, search: '', testCasePage: 0, testCaseCount: 0, testCaseSearch: '', randomPage: 0, randomCount: 0, randomSearch: '', randomSubPage: 0, randomSubCount: 0, randomSubSearch: '', picDelayTime: 0 }, methods: { // 全部展开 expandAll: function () { if (App.view != 'code') { alert('请先获取正确的JSON Response!') return } $('.icon-square-min').show() $('.icon-square-plus').hide() $('.expand-view').show() $('.fold-view').hide() App.isExpand = true; }, // 全部折叠 collapseAll: function () { if (App.view != 'code') { alert('请先获取正确的JSON Response!') return } $('.icon-square-min').hide() $('.icon-square-plus').show() $('.expand-view').hide() $('.fold-view').show() App.isExpand = false; }, // diff diffTwo: function () { var oldJSON = {} var newJSON = {} App.view = 'code' try { oldJSON = jsonlint.parse(App.jsoncon) } catch (ex) { App.view = 'error' App.error = { msg: '原 JSON 解析错误\r\n' + ex.message } return } try { newJSON = jsonlint.parse(App.jsoncon) } catch (ex) { App.view = 'error' App.error = { msg: '新 JSON 解析错误\r\n' + ex.message } return } var base = difflib.stringAsLines(JSON.stringify(oldJSON, '', 4)) var newtxt = difflib.stringAsLines(JSON.stringify(newJSON, '', 4)) var sm = new difflib.SequenceMatcher(base, newtxt) var opcodes = sm.get_opcodes() $('#diffoutput').empty().append(diffview.buildView({ baseTextLines: base, newTextLines: newtxt, opcodes: opcodes, baseTextName: '原 JSON', newTextName: '新 JSON', contextSize: 2, viewType: 0 })) }, baseViewToDiff: function () { App.baseview = 'diff' App.diffTwo() }, // 回到格式化视图 baseViewToFormater: function () { App.baseview = 'formater' App.view = 'code' App.showJsonView() }, // 根据json内容变化格式化视图 showJsonView: function () { if (App.baseview === 'diff') { return } try { if (this.jsoncon.trim() === '') { App.view = 'empty' } else { App.view = 'code' if (isSingle) { App.jsonhtml = jsonlint.parse(this.jsoncon) } else { App.jsonhtml = Object.assign({ _$_this_$_: JSON.stringify({ path: null, table: null }) }, jsonlint.parse(this.jsoncon)) } } } catch (ex) { App.view = 'error' App.error = { msg: ex.message } } }, showUrl: function (isAdminOperation, branchUrl) { if (StringUtil.isEmpty(this.host, true)) { //显示(可编辑)URL Host if (isAdminOperation != true) { baseUrl = this.getBaseUrl() } vUrl.value = (isAdminOperation ? this.server : baseUrl) + branchUrl } else { //隐藏(固定)URL Host // if (isAdminOperation) { // this.host = this.server // } vUrl.value = branchUrl } vUrlComment.value = isSingle || StringUtil.isEmpty(this.urlComment, true) ? '' : vUrl.value + this.urlComment; }, //设置基地址 setBaseUrl: function () { if (StringUtil.isEmpty(this.host, true) != true) { return } // 重新拉取文档 var bu = this.getBaseUrl() if (baseUrl != bu) { baseUrl = bu; // doc = null //这个是本地的数据库字典及非开放请求文档 this.saveCache('', 'URL_BASE', baseUrl) //已换成固定的管理系统URL // this.remotes = [] // var index = baseUrl.indexOf(':') //http://localhost:8080 // App.server = (index < 0 ? baseUrl : baseUrl.substring(0, baseUrl)) + ':9090' } }, getUrl: function () { var url = StringUtil.get(this.host) + new String(vUrl.value) return url.replace(/ /g, '') }, //获取基地址 getBaseUrl: function () { var url = new String(vUrl.value).trim() var length = this.getBaseUrlLength(url) url = length <= 0 ? '' : url.substring(0, length) return url == '' ? URL_BASE : url }, //获取基地址长度,以://后的第一个/分割baseUrl和method getBaseUrlLength: function (url_) { var url = StringUtil.trim(url_) var index = url.indexOf(' ') if (index >= 0) { return index + 1 } index = url.lastIndexOf('.') return index < 0 ? 0 : index + 1 }, //获取操作方法 getMethod: function (url) { url = url || new String(vUrl.value).trim() var index = url.lastIndexOf('.') url = index <= 0 ? url : url.substring(index + 1) return StringUtil.trim(url.startsWith('.') ? url.substring(1) : url) }, //获取操作方法 getClass: function (url) { url = url || this.getUrl() var index = url.lastIndexOf('.') if (index <= 0) { throw new Error('必须要有类名!完整的 URL 必须符合格式 package.Class.method !') } // url = url.substring(0, index) // index = url.lastIndexOf('.') var clazz = StringUtil.trim(index < 0 ? url : url.substring(index + 1)) if (App.language == 'Java' || App.language == 'JavaScript' || App.language == 'TypeScript') { if (/[A-Z]{0}[A-Za-z0-9_]/.test(clazz) != true) { alert('类名 ' + clazz + ' 不符合规范!') } } return clazz }, //获取操作方法 getPackage: function (url) { url = url || this.getUrl() var index = url.lastIndexOf('.') if (index <= 0) { throw new Error('必须要有类名!完整的 URL 必须符合格式 package.Class.method !') } // url = url.substring(0, index) // index = url.lastIndexOf('.') return StringUtil.trim(index < 0 ? '' : url.substring(0, index)) }, //获取请求的tag getTag: function () { var req = null; try { req = this.getRequest(vInput.value); } catch (e) { log('main.getTag', 'try { req = this.getRequest(vInput.value); \n } catch (e) {\n' + e.message) } return req == null ? null : req.tag }, getRequest: function (json, defaultValue) { var s = App.toDoubleJSON(json, defaultValue); if (StringUtil.isEmpty(s, true)) { return defaultValue } try { return jsonlint.parse(s); } catch (e) { log('main.getRequest', 'try { return jsonlint.parse(s); \n } catch (e) {\n' + e.message) log('main.getRequest', 'return jsonlint.parse(App.removeComment(s));') return jsonlint.parse(App.removeComment(s)); } }, getHeader: function (text) { var header = {} var hs = StringUtil.isEmpty(text, true) ? null : StringUtil.split(text, '\n') if (hs != null && hs.length > 0) { var item for (var i = 0; i < hs.length; i++) { item = hs[i] var index = item.lastIndexOf(' //') // 不加空格会导致 http:// 被截断 ('//') //这里只支持单行注释,不用 removeComment 那种带多行的去注释方式 var item2 = index < 0 ? item : item.substring(0, index) item2 = item2.trim() if (item2.length <= 0) { continue; } index = item2.indexOf(':') if (index <= 0) { throw new Error('请求头 Request Header 输入错误!请按照每行 key: value 的格式输入,不要有多余的换行或空格!' + '\n错误位置: 第 ' + (i + 1) + ' 行' + '\n错误文本: ' + item) } var val = item2.substring(index + 1, item2.length) var ind = val.indexOf('(') //一定要有函数是为了避免里面是一个简短单词和 APIAuto 代码中变量冲突 if (ind > 0 && val.indexOf(')') > ind) { //不从 0 开始是为了保证是函数,且不是 (1) 这种单纯限制作用域的括号 try { val = eval(val) } catch (e) { App.log("getHeader if (hs != null && hs.length > 0) { ... if (ind > 0 && val.indexOf(')') > ind) { ... try { val = eval(val) } catch (e) = " + e.message) } } header[StringUtil.trim(item2.substring(0, index))] = val } } return header }, // 显示保存弹窗 showSave: function (show) { if (show) { if (App.isTestCaseShow) { alert('请先输入请求内容!') return } var tag = App.getTag() App.history.name = App.getMethod() + (StringUtil.isEmpty(tag, true) ? '' : ' ' + tag) + ' ' + App.formatTime() //不自定义名称的都是临时的,不需要时间太详细 } App.isSaveShow = show }, // 显示导出弹窗 showExport: function (show, isRemote, isRandom) { if (show) { if (isRemote) { //共享测试用例 App.isExportRandom = isRandom if (App.isTestCaseShow) { alert('请先输入请求内容!') return } if (App.view != 'code') { alert('请先测试请求,确保是正确可用的!') return } if (isRandom) { App.exTxt.name = '随机配置 ' + App.formatDateTime() } else { var tag = App.getTag() App.exTxt.name = App.getMethod() + (StringUtil.isEmpty(tag, true) ? '' : ' ' + tag) } } else { //下载到本地 if (App.isTestCaseShow) { //文档 App.exTxt.name = 'APIJSON自动化文档 ' + App.formatDateTime() } else if (App.view == 'markdown' || App.view == 'output') { var suffix switch (App.language) { case CodeUtil.LANGUAGE_KOTLIN: suffix = '.kt'; break; case CodeUtil.LANGUAGE_JAVA: suffix = '.java'; break; case CodeUtil.LANGUAGE_C_SHARP: suffix = '.cs'; break; case CodeUtil.LANGUAGE_SWIFT: suffix = '.swift'; break; case CodeUtil.LANGUAGE_OBJECTIVE_C: suffix = '.h'; break; case CodeUtil.LANGUAGE_GO: suffix = '.go'; break; case CodeUtil.LANGUAGE_C_PLUS_PLUS: suffix = '.cpp'; break; case CodeUtil.LANGUAGE_TYPE_SCRIPT: suffix = '.ts'; break; case CodeUtil.LANGUAGE_JAVA_SCRIPT: suffix = '.js'; break; case CodeUtil.LANGUAGE_PHP: suffix = '.php'; break; case CodeUtil.LANGUAGE_PYTHON: suffix = '.py'; break; default: suffix = '.java'; break; } App.exTxt.name = 'User' + suffix alert('自动生成模型代码,可填类名后缀:\n' + 'Kotlin.kt, Java.java, Swift.swift, Objective-C.m, C#.cs, Go.go,' + '\nTypeScript.ts, JavaScript.js, PHP.php, Python.py, C++.cpp'); } else { App.exTxt.name = 'APIJSON测试 ' + App.getMethod() + ' ' + App.formatDateTime() } } } App.isExportShow = show App.isExportRemote = isRemote }, // 显示配置弹窗 showConfig: function (show, index) { App.isConfigShow = false if (App.isTestCaseShow) { // if (index == 3 || index == 4 || index == 5 || index == 10) { if (index == 4 || index == 5 || index == 10) { App.showTestCase(false, false) } } if (show) { App.exTxt.button = index == 10 ? '上传' : '切换' App.exTxt.index = index switch (index) { case 0: case 1: case 2: case 3: case 6: case 7: case 8: case 10: App.exTxt.name = index == 0 ? App.database : (index == 1 ? App.schema : (index == 2 ? App.language : (index == 3 ? App.host : (index == 6 ? App.server : (index == 8 ? App.project : '/method/list'))))) App.isConfigShow = true if (index == 0) { alert('可填数据库:\nMYSQL,POSTGRESQL,SQLSERVER,ORACLE,DB2,SQLITE') } else if (index == 2) { alert('自动生成代码,可填语言:\nKotlin,Java,Swift,Objective-C,C#,Go,\nTypeScript,JavaScript,PHP,Python,C++') } else if (index == 7) { alert('多个类型用 , 隔开,可填类型:\nPARAM(GET ?a=1&b=c&key=value),\nJSON(POST application/json),\nFORM(POST x-www-form-urlencoded),\nDATA(POST form-data)') } else if (index == 10) { vInput.value = App.getCache(App.project, 'request4MethodList') || '{' + '\n "mock": true, // 生成模拟参数值' + '\n "package": "' + App.getPackage() + '", // 包名,不填默认全部' + '\n "class": "' + App.getClass() + '" // 类名,不填默认全部' + '\n}' App.onChange(false) App.request(false, REQUEST_TYPE_JSON, App.project + App.exTxt.name , App.getRequest(vInput.value), App.getHeader(vHeader.value)) } break // case 3: // App.host = App.getBaseUrl() // App.showUrl(false, new String(vUrl.value).substring(App.host.length)) //没必要导致必须重新获取 Response,App.onChange(false) // App.remotes = null // break case 4: App.isHeaderShow = show App.saveCache('', 'isHeaderShow', show) break case 5: App.isRandomShow = show App.saveCache('', 'isRandomShow', show) break case 9: App.isDelegateEnabled = show App.saveCache('', 'isDelegateEnabled', show) break } } else if (index == 3) { var host = StringUtil.get(App.host) // var branch = new String(vUrl.value) App.host = host // vUrl.value = host + branch //保证 showUrl 里拿到的 baseUrl = App.host (http://apijson.cn:8080/put /balance) // App.setBaseUrl() //保证自动化测试等拿到的 baseUrl 是最新的 // App.showUrl(false, branch) //没必要导致必须重新获取 Response,App.onChange(false) // App.remotes = null App.saveCache('', 'host', host) } else if (index == 4) { App.isHeaderShow = show App.saveCache('', 'isHeaderShow', show) } else if (index == 5) { App.isRandomShow = show App.saveCache('', 'isRandomShow', show) } else if (index == 9) { App.isDelegateEnabled = show App.saveCache('', 'isDelegateEnabled', show) } }, // 显示删除弹窗 showDelete: function (show, item, index, isRandom) { this.isDeleteShow = show this.isDeleteRandom = isRandom this.exTxt.name = '请输入' + (isRandom ? '随机配置' : '接口') + '名来确认' if (isRandom) { this.currentRandomItem = Object.assign(item, { index: index }) } else { this.currentDocItem = Object.assign(item, { index: index }) } }, // 删除接口文档 deleteDoc: function () { var isDeleteRandom = this.isDeleteRandom var item = (isDeleteRandom ? this.currentRandomItem : this.currentDocItem) || {} var doc = (isDeleteRandom ? item.Input : item.Flow) || {} var type = isDeleteRandom ? '随机配置' : '方法' if (doc.id == null) { alert('未选择' + type + '或' + type + '不存在!') return } var nameKey = isDeleteRandom ? 'name' : 'method' if (doc[nameKey] != this.exTxt.name) { alert('输入的' + type + '名和要删除的' + type + '名不匹配!') return } this.showDelete(false, {}) this.isTestCaseShow = false this.isRandomListShow = false var url = this.server + '/delete' var req = isDeleteRandom ? { format: false, 'Input': { 'id': doc.id }, 'tag': 'Input' } : { format: false, 'Flow': { 'id': doc.id }, 'tag': 'Flow' } this.request(true, REQUEST_TYPE_JSON, url, req, {}, function (url, res, err) { App.onResponse(url, res, err) var rpObj = res.data || {} if (isDeleteRandom) { if (rpObj.Input != null && rpObj.Input.code == CODE_SUCCESS) { if (((item.Input || {}).toId || 0) <= 0) { App.randoms.splice(item.index, 1) } else { App.randomSubs.splice(item.index, 1) } // App.showRandomList(true, App.currentRemoteItem) } } else { if (rpObj.Flow != null && rpObj.Flow.code == CODE_SUCCESS) { App.remotes.splice(item.index, 1) App.showTestCase(true, App.isLocalShow) } } }) }, // 保存当前的JSON save: function () { if (App.history.name.trim() === '') { Helper.alert('名称不能为空!', 'danger') return } var val = { name: App.history.name, detail: App.history.name, type: App.type, package: App.getPackage(), class: App.getClass(), method: App.getMethod(), request: inputted, response: App.jsoncon, header: vHeader.value, random: vRandom.value } var key = String(Date.now()) localforage.setItem(key, val, function (err, value) { Helper.alert('保存成功!', 'success') App.showSave(false) val.key = key App.historys.push(val) }) }, // 清空本地历史 clearLocal: function () { this.locals.splice(0, this.locals.length) //UI无反应 this.locals = [] this.saveCache('', 'locals', []) }, // 删除已保存的 remove: function (item, index, isRemote, isRandom) { if (isRemote == null || isRemote == false) { //null != false localforage.removeItem(item.key, function () { App.historys.splice(index, 1) }) } else { if (this.isLocalShow) { this.locals.splice(index, 1) return } if (isRandom && (((item || {}).Input || {}).id || 0) <= 0) { this.randomSubs.splice(index, 1) return } this.showDelete(true, item, index, isRandom) } }, // 根据随机测试用例恢复数据 restoreRandom: function (item) { this.currentRandomItem = item this.isRandomListShow = false this.isRandomSubListShow = false var random = (item || {}).Input || {} this.randomTestTitle = random.name this.testRandomCount = random.count vRandom.value = StringUtil.get(random.config) var response = ((item || {}).Output || {}).response if (StringUtil.isEmpty(response, true) == false) { App.jsoncon = StringUtil.trim(response) App.view = 'code' } }, // 根据测试用例/历史记录恢复数据 restoreRemoteAndTest: function (item) { this.restoreRemote(item, true) }, // 根据测试用例/历史记录恢复数据 restoreRemote: function (item, test) { this.currentRemoteItem = item this.restore((item || {}).Flow, ((item || {}).Flow || {}).log, true, test) }, // 根据历史恢复数据 restore: function (item, response, isRemote, test) { item = item || {} // localforage.getItem(item.key || '', function (err, value) { // App.type = OPERATE_TYPE_REVIEW; App.urlComment = CodeUtil.getComment(StringUtil.get(item.detail), false, ' '); App.requestVersion = item.version; vUrl.value = item.name vUrlComment.value = isSingle || StringUtil.isEmpty(App.urlComment, true) ? '' : vUrl.value + App.urlComment; App.showTestCase(false, App.isLocalShow) // vInput.value = StringUtil.get(item.request) vHeader.value = StringUtil.get(item.header) // vRandom.value = StringUtil.get(item.random) App.onChange(false) if (isRemote) { App.randoms = [] if (App.type != OPERATE_TYPE_RECORD) { App.showRandomList(true, item) } if (item.logUrl != null && item.logUrl.indexOf('://') > 0) { // App.request(false, REQUEST_TYPE_PARAM, item.logUrl, null, {'Accept:': 'text/plain;charset=UTF-8'}, function (url, res, err) { // output = res.data || '' // vOutput.value = output // App.view = 'output' // }) axios({ url: (this.isDelegateEnabled ? this.server + '/delegate?$_delegate_url=' : '') + StringUtil.noBlank(item.logUrl), method: 'GET', responseType: 'text', // important withCredentials: true, header: { 'Accept': 'text/plain;charset=UFT8' // 'Content-Type': 'text/plain;charset=GBK' } }).then(function(res) { output = res.data || '' vOutput.value = output App.view = 'output' }).catch(function(err) { App.onResponse(item.logUrl, {}, err) }); } } if (test) { App.send(false) } else { if (StringUtil.isEmpty(response, true) == false) { setTimeout(function () { App.jsoncon = StringUtil.trim(response) App.view = 'output' }, 500) } } // }) }, // 获取所有保存的json listHistory: function () { localforage.iterate(function (value, key, iterationNumber) { if (key[0] !== '#') { value.key = key App.historys.push(value) } if (key === '#theme') { // 设置默认主题 App.checkedTheme = value } }) }, // 导出文本 exportTxt: function () { App.isExportShow = false if (App.isExportRemote == false) { //下载到本地 if (App.isTestCaseShow) { //文档 saveTextAs('# ' + App.exTxt.name + '\n主页: https://github.com/Tencent/APIJSON' + '\n\nBASE_URL: ' + this.getBaseUrl() + '\n\n\n## 测试用例(Markdown格式,可用工具预览) \n\n' + App.getDoc4TestCase() + '\n\n\n\n\n\n\n\n## 文档(Markdown格式,可用工具预览) \n\n' + doc , App.exTxt.name + '.txt') } else if (App.view == 'markdown' || App.view == 'output') { //model var clazz = StringUtil.trim(App.exTxt.name) var txt = '' //配合下面 +=,实现注释判断,一次全生成,方便测试 if (clazz.endsWith('.java')) { txt += CodeUtil.parseJavaBean(docObj, clazz.substring(0, clazz.length - 5), App.database) } else if (clazz.endsWith('.swift')) { txt += CodeUtil.parseSwiftStruct(docObj, clazz.substring(0, clazz.length - 6), App.database) } else if (clazz.endsWith('.kt')) { txt += CodeUtil.parseKotlinDataClass(docObj, clazz.substring(0, clazz.length - 3), App.database) } else if (clazz.endsWith('.m')) { txt += CodeUtil.parseObjectiveCEntity(docObj, clazz.substring(0, clazz.length - 2), App.database) } else if (clazz.endsWith('.cs')) { txt += CodeUtil.parseCSharpEntity(docObj, clazz.substring(0, clazz.length - 3), App.database) } else if (clazz.endsWith('.php')) { txt += CodeUtil.parsePHPEntity(docObj, clazz.substring(0, clazz.length - 4), App.database) } else if (clazz.endsWith('.go')) { txt += CodeUtil.parseGoEntity(docObj, clazz.substring(0, clazz.length - 3), App.database) } else if (clazz.endsWith('.cpp')) { txt += CodeUtil.parseCppStruct(docObj, clazz.substring(0, clazz.length - 4), App.database) } else if (clazz.endsWith('.js')) { txt += CodeUtil.parseJavaScriptEntity(docObj, clazz.substring(0, clazz.length - 3), App.database) } else if (clazz.endsWith('.ts')) { txt += CodeUtil.parseTypeScriptEntity(docObj, clazz.substring(0, clazz.length - 3), App.database) } else if (clazz.endsWith('.py')) { txt += CodeUtil.parsePythonEntity(docObj, clazz.substring(0, clazz.length - 3), App.database) } else { alert('请正确输入对应语言的类名后缀!') } if (StringUtil.isEmpty(txt, true)) { alert('找不到 ' + clazz + ' 对应的表!请检查数据库中是否存在!\n如果不存在,请重新输入存在的表;\n如果存在,请刷新网页后重试。') return } saveTextAs(txt, clazz) } else { var res = parseJSON(App.jsoncon) res = this.removeDebugInfo(res) var s = '' switch (App.language) { case CodeUtil.LANGUAGE_KOTLIN: s += '(Kotlin):\n\n' + CodeUtil.parseKotlinResponse('', res, 0, false, ! isSingle) break; case CodeUtil.LANGUAGE_JAVA: s += '(Java):\n\n' + CodeUtil.parseJavaResponse('', res, 0, false, ! isSingle) break; case CodeUtil.LANGUAGE_C_SHARP: s += '(C#):\n\n' + CodeUtil.parseCSharpResponse('', res, 0) break; case CodeUtil.LANGUAGE_SWIFT: s += '(Swift):\n\n' + CodeUtil.parseSwiftResponse('', res, 0, isSingle) break; case CodeUtil.LANGUAGE_OBJECTIVE_C: s += '(Objective-C):\n\n' + CodeUtil.parseObjectiveCResponse('', res, 0) break; case CodeUtil.LANGUAGE_GO: s += '(Go):\n\n' + CodeUtil.parseGoResponse('', res, 0) break; case CodeUtil.LANGUAGE_C_PLUS_PLUS: s += '(C++):\n\n' + CodeUtil.parseCppResponse('', res, 0, isSingle) break; case CodeUtil.LANGUAGE_TYPE_SCRIPT: s += '(TypeScript):\n\n' + CodeUtil.parseTypeScriptResponse('', res, 0, isSingle) break; case CodeUtil.LANGUAGE_JAVA_SCRIPT: s += '(JavaScript):\n\n' + CodeUtil.parseJavaScriptResponse('', res, 0, isSingle) break; case CodeUtil.LANGUAGE_PHP: s += '(PHP):\n\n' + CodeUtil.parsePHPResponse('', res, 0, isSingle) break; case CodeUtil.LANGUAGE_PYTHON: s += '(Python):\n\n' + CodeUtil.parsePythonResponse('', res, 0, isSingle) break; default: s += ':\n没有生成代码,可能生成代码(封装,解析)的语言配置错误。 \n'; break; } saveTextAs('# ' + App.exTxt.name + '\n主页: https://github.com/Tencent/APIJSON' + '\n\n\nURL: ' + StringUtil.get(vUrl.value) + '\n\n\nHeader:\n' + StringUtil.get(vHeader.value) + '\n\n\nRequest:\n' + StringUtil.get(vInput.value) + '\n\n\nResponse:\n' + StringUtil.get(App.jsoncon) + '\n\n\n## 解析 Response 的代码' + s , App.exTxt.name + '.txt') } } else { //上传到远程服务器 var id = App.User == null ? null : App.User.id if (id == null || id <= 0) { alert('请先登录!') return } var isExportRandom = App.isExportRandom var did = ((App.currentRemoteItem || {}).Input || {}).id if (isExportRandom && did == null) { alert('请先共享测试用例!') return } App.isTestCaseShow = false var currentAccountId = App.getCurrentAccountId() var currentResponse = StringUtil.isEmpty(App.jsoncon, true) ? {} : App.removeDebugInfo(parseJSON(App.jsoncon)); var code = currentResponse.code; var thrw = currentResponse.throw; delete currentResponse.code; //code必须一致 delete currentResponse.throw; //throw必须一致 var rsp = parseJSON(JSON.stringify(currentResponse || {})) rsp = JSONResponse.array2object(rsp, 'methodArgs', ['methodArgs'], true) var isML = App.isMLEnabled; var stddObj = isML ? JSONResponse.updateStandard({}, rsp) : {}; stddObj.code = code; stddObj.throw = thrw; currentResponse.code = code; currentResponse.throw = thrw; var url = App.server + '/post' var req = isExportRandom ? { format: false, 'Input': { toId: 0, flowId: did, count: App.requestCount, name: App.exTxt.name, config: vRandom.value }, 'Output': { 'response': JSON.stringify(currentResponse), 'standard': isML ? JSON.stringify(stddObj) : null }, 'tag': 'Input' } : { format: false, 'Flow': { 'name': App.getMethod(), 'detail': App.exTxt.name, 'systemId': 1, 'deviceId': 1, 'imei': 1234, 'screencapUrl': "http://test.url", 'log': (currentResponse.type || App.type) || null, }, 'tag': 'Flow' } App.request(true, REQUEST_TYPE_JSON, url, req, {}, function (url, res, err) { App.onResponse(url, res, err) var rpObj = res.data || {} if (isExportRandom) { if (rpObj.Input != null && rpObj.Input.code == CODE_SUCCESS) { App.randoms = [] App.showRandomList(true, (App.currentRemoteItem || {}).Flow) } } else { App.remotes = [] App.showTestCase(true, false) } }) } }, // 保存配置 saveConfig: function () { App.isConfigShow = App.exTxt.index == 10 switch (App.exTxt.index) { case 0: App.database = CodeUtil.database = App.exTxt.name App.saveCache('', 'database', App.database) doc = null var item = App.accounts[App.currentAccountIndex] item.isLoggedIn = false App.onClickAccount(App.currentAccountIndex, item) break case 1: App.schema = CodeUtil.schema = App.exTxt.name App.saveCache('', 'schema', App.schema) doc = null var item = App.accounts[App.currentAccountIndex] item.isLoggedIn = false App.onClickAccount(App.currentAccountIndex, item) break case 2: App.language = CodeUtil.language = App.exTxt.name App.saveCache('', 'language', App.language) doc = null App.onChange(false) break case 3: App.host = App.exTxt.name App.saveCache('', 'host', App.host) break case 6: App.server = App.exTxt.name App.saveCache('', 'server', App.server) App.logout(true) break case 7: App.types = StringUtil.split(App.exTxt.name) App.saveCache('', 'types', App.types) break case 8: App.project = App.exTxt.name App.saveCache('', 'project', App.project) var c = App.currentAccountIndex == null ? -1 : App.currentAccountIndex var item = App.accounts == null ? null : App.accounts[c] if (item != null) { item.isLoggedIn = ! item.isLoggedIn App.onClickAccount(c, item) } break case 10: App.saveCache(App.project, 'request4MethodList', vInput.value) App.request(false, REQUEST_TYPE_JSON, App.project + App.exTxt.name, App.getRequest(vInput.value), App.getHeader(vHeader.value), function (url, res, err) { if (App.isSyncing) { alert('正在同步,请等待完成') return } App.isSyncing = true App.onResponse(url, res, err) var classList = (res.data || {}).classList if (classList == null) { // || apis.length <= 0) { alert('没有查到 Project 文档!请开启跨域代理,并检查 URL 是否正确!') return } App.uploadTotal = 0 App.uploadDoneCount = 0 App.uploadFailCount = 0 for (var i in classList) { try { App.sync2DB(classList[i]) } catch (e) { App.uploadFailCount ++ App.exTxt.button = 'All:' + App.uploadTotal + '\nDone:' + App.uploadDoneCount + '\nFail:' + App.uploadFailCount } } }) break } }, /**同步到数据库 * @param classItem * @param callback */ sync2DB: function(classItem) { if (classItem == null) { App.log('postApi', 'classItem == null >> return') return } var methodList = classItem.methodList || [] App.uploadTotal += methodList.length App.exTxt.button = 'All:' + App.uploadTotal + '\nDone:' + App.uploadDoneCount + '\nFail:' + App.uploadFailCount var currentAccount = App.accounts[App.currentAccountIndex] var classArgs = App.getArgs4Sync(classItem.parameterTypeList) var methodItem for (var k = 0; k < methodList.length; k++) { methodItem = methodList[k] if (methodItem == null || methodItem.name == null) { App.uploadFailCount ++ App.exTxt.button = 'All:' + App.uploadTotal + '\nDone:' + App.uploadDoneCount + '\nFail:' + App.uploadFailCount continue } var currentAccountId = App.getCurrentAccountId() App.request(true, REQUEST_TYPE_JSON, App.server + '/post', { format: false, 'Input': { 'userId': App.User.id, 'testAccountId': currentAccountId, 'package': classItem.package == null ? null : classItem.package, // .replace(/[.]/g, '/'), 'class': classItem.name, 'method': methodItem.name, 'classArgs': classArgs, 'genericClassArgs': App.getArgs4Sync(classItem.genericParameterTypeList), 'methodArgs': App.getArgs4Sync(methodItem.parameterTypeList, methodItem.parameterDefaultValueList), 'genericMethodArgs': App.getArgs4Sync(methodItem.genericParameterTypeList, methodItem.parameterDefaultValueList), 'type': methodItem.returnType == null ? null : methodItem.returnType, // .replace(/[.]/g, '/'), 'genericType': methodItem.genericReturnType == null ? null : methodItem.genericReturnType, // .replace(/[.]/g, '/'), 'static': methodItem.static ? 1 : 0, 'exceptions': methodItem.exceptionTypeList == null ? null : methodItem.exceptionTypeList.join(), // .replace(/[.]/g, '/').join(), 'genericExceptions': methodItem.genericExceptionTypeList == null ? null : methodItem.genericExceptionTypeList.join(), // .replace(/[.]/g, '/').join(), 'detail': methodItem.name }, 'Output': { 'inputId': 0, 'host': App.getBaseUrl(), 'testAccountId': currentAccountId, 'response': '' }, 'tag': 'Input' }, {}, function (url, res, err) { App.onResponse(url, res, err) if (res.data != null && res.data.Input != null && res.data.Input.code == CODE_SUCCESS) { App.uploadDoneCount ++ } else { App.uploadFailCount ++ } App.exTxt.button = 'All:' + App.uploadTotal + '\nDone:' + App.uploadDoneCount + '\nFail:' + App.uploadFailCount if (App.uploadDoneCount + App.uploadFailCount >= App.uploadTotal) { alert('导入完成') App.isSyncing = false App.showTestCase(false, false) App.remotes = [] var branch = vUrl.value vUrl.value = StringUtil.get(App.host) + branch App.host = '' vUrlComment.value = isSingle || StringUtil.isEmpty(App.urlComment, true) ? '' : vUrl.value + App.urlComment; //导致重复加前缀 App.showUrl(false, branch) App.showTestCase(true, false) } }) } }, getArgs4Sync: function (typeList, valueList) { if (typeList == null) { return null } var args = [] for (var l = 0; l < typeList.length; l++) { var type = typeList[l] == null ? null : typeList[l] //保持用 . 分割 .replace(/[.]/g, '/') var value = valueList[l] == null ? App.mockValue4Type(type) : valueList[l]; args.push({ type: type, value: value }) } return args }, mockValue4Type: function (type) { type = StringUtil.trim(type) if (type == '') { return } switch (type) { case 'Boolean': case 'boolean': return randomInt(0, 1) == 1 case 'Number': case 'Double': case 'Float': case 'double': case 'float': return randomNum(-100, 200, randomInt(0, 2)) case 'Long': case 'long': return randomInt(-100, 200) case 'Integer': case 'int': return randomInt(-10, 20) case 'String': case 'CharSequence': return randomStr(0, 5) } var ct = '' if (type.endsWith('[]')) { return null //除了 null,其它任何类型的值都不行 argument type mismatch ct = type.substring(0, type.length - 2) } else { var index = type.indexOf('<') if (index >= 0) { type = type.substring(0, index).trim(); ct = type.substring(index + 1, type.lastIndexOf('>')).trim() } } if (type.endsWith('[]') || type.endsWith('List') || type.endsWith('Array') || type.endsWith('Set') || type.endsWith('Collection')) { var size = randomInt(0, 10) var arr = [] for (var i = 0; i < size; i ++) { var v = App.mockValue4Type(ct) if (v != null) { arr.push(v) } } return arr } if (type.endsWith('Map') || type.endsWith('Table') || type.endsWith('JSONObject')) { var size = randomInt(0, 10) var index = ct.indexOf(',') var lct = index < 0 ? 'String' : ct.substring(0, index).trim() var rct = index < 0 ? ct : ct.substring(index + 1).trim() var obj = {} for (var i = 0; i < size; i ++) { obj[this.mockValue4Type(lct), this.mockValue4Type(rct)] } return obj } if (type.endsWith('Decimal')) { return this.mockValue4Type('Number') } return type == 'Object' || type == 'java.lang.Object' ? null : {} }, // 切换主题 switchTheme: function (index) { this.checkedTheme = index localforage.setItem('#theme', index) }, // APIJSON <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //格式化日期 formatDate: function (date) { if (date == null) { date = new Date() } return date.getFullYear() + '-' + App.fillZero(date.getMonth() + 1) + '-' + App.fillZero(date.getDate()) }, //格式化时间 formatTime: function (date) { if (date == null) { date = new Date() } return App.fillZero(date.getHours()) + ':' + App.fillZero(date.getMinutes()) }, formatDateTime: function (date) { if (date == null) { date = new Date() } return App.formatDate(date) + ' ' + App.formatTime(date) }, //填充0 fillZero: function (num, n) { if (num == null) { num = 0 } if (n == null || n <= 0) { n = 2 } var len = num.toString().length; while(len < n) { num = "0" + num; len++; } return num; }, onClickAccount: function (index, item, callback) { if (this.currentAccountIndex == index) { if (item == null) { if (callback != null) { callback(false, index) } } else { this.setRememberLogin(item.remember) this.account = item.phone this.password = item.password if (item.isLoggedIn) { //logout FIXME 没法自定义退出,浏览器默认根据url来管理session的 this.logout(false, function (url, res, err) { App.onResponse(url, res, err) item.isLoggedIn = false App.saveCache(App.getBaseUrl(), 'currentAccountIndex', App.currentAccountIndex) App.saveCache(App.getBaseUrl(), 'accounts', App.accounts) if (callback != null) { callback(false, index, err) } }); } else { //login this.login(false, function (url, res, err) { App.onResponse(url, res, err) var data = res.data || {} var user = data.code == CODE_SUCCESS ? data.user : null if (user == null) { if (callback != null) { callback(false, index, err) } } else { item.name = user.name item.remember = data.remember item.isLoggedIn = true App.saveCache(App.getBaseUrl(), 'currentAccountIndex', App.currentAccountIndex) App.saveCache(App.getBaseUrl(), 'accounts', App.accounts) if (callback != null) { callback(true, index, err) } } }); } } return; } //退出当前账号 var c = this.currentAccountIndex var it = c == null || this.accounts == null ? null : this.accounts[c]; if (it != null) { //切换 BASE_URL后 it = undefined 导致UI操作无法继续 it.isLoggedIn = false //异步导致账号错位 this.onClickAccount(c, this.accounts[c]) } //切换到这个tab this.currentAccountIndex = index //目前还没做到同一标签页下测试账号切换后,session也跟着切换,所以干脆每次切换tab就重新登录 if (item != null) { item.isLoggedIn = false this.onClickAccount(index, item, callback) } else { if (callback != null) { callback(false, index) } } }, removeAccountTab: function () { if (App.accounts.length <= 1) { alert('至少要 1 个测试账号!') return } App.accounts.splice(App.currentAccountIndex, 1) if (App.currentAccountIndex >= App.accounts.length) { App.currentAccountIndex = App.accounts.length - 1 } App.saveCache(App.getBaseUrl(), 'currentAccountIndex', App.currentAccountIndex) App.saveCache(App.getBaseUrl(), 'accounts', App.accounts) }, addAccountTab: function () { App.showLogin(true, false) }, //显示远程的测试用例文档 showTestCase: function (show, isLocal) { this.isTestCaseShow = show this.isLocalShow = isLocal vOutput.value = show ? '' : (output || '') this.showDoc() if (isLocal) { this.testCases = this.locals || [] return } this.testCases = this.remotes || [] if (show) { var testCases = this.testCases var allCount = testCases == null ? 0 : testCases.length if (allCount > 0) { var accountIndex = (this.accounts[this.currentAccountIndex] || {}).isLoggedIn ? this.currentAccountIndex : -1 this.currentAccountIndex = accountIndex //解决 onTestResponse 用 -1 存进去, handleTest 用 currentAccountIndex 取出来为空 var tests = this.tests[String(accountIndex)] || {} if (tests != null && $.isEmptyObject(tests) != true) { for (var i = 0; i < allCount; i++) { var item = testCases[i] if (item == null) { continue } var d = item.Input || {} this.compareResponse(allCount, testCases, i, item, (tests[d.id] || {})[0], false, accountIndex, true) } } return; } this.isTestCaseShow = false var search = StringUtil.isEmpty(this.testCaseSearch, true) ? null : StringUtil.trim(this.testCaseSearch) var req = { format: false, '[]': { 'count': this.testCaseCount || 100, //200 条测试直接卡死 0, 'page': this.testCasePage || 0, 'Flow': { '@order': 'time-', 'userId{}': [0, App.User.id], 'name$*~': search, 'detail*~': search, '@combine': StringUtil.isEmpty(search) ? null : 'name$*~,detail*~' }, 'Device': { 'id@': '/Flow/deviceId' }, 'System': { 'id@': '/Flow/systemId' } }, '@role': 'LOGIN' } App.onChange(false) App.request(true, REQUEST_TYPE_JSON, App.server + '/get', req, {}, function (url, res, err) { App.onResponse(url, res, err) var rpObj = res.data if (rpObj != null && rpObj.code === CODE_SUCCESS) { App.isTestCaseShow = true App.isLocalShow = false App.testCases = App.remotes = rpObj['[]'] vOutput.value = show ? '' : (output || '') App.showDoc() //App.onChange(false) } }) } }, //显示远程的随机配置文档 showRandomList: function (show, item) { this.isRandomEditable = false this.isRandomListShow = show vOutput.value = show ? '' : (output || '') this.showDoc() this.randoms = this.randoms || [] if (show && this.isRandomShow && this.randoms.length <= 0 && item != null && item.id != null) { this.isRandomListShow = false var search = StringUtil.isEmpty(this.randomSearch, true) ? null : '%' + StringUtil.trim(this.randomSearch) + '%' var url = App.server + '/get' var req = { '[]': { 'count': this.randomCount || 100, 'page': this.randomPage || 0, 'Input': { 'flowId': item.id, '@order': "step+,time+,downTime+,eventTime+", 'name$': search }, 'Output': { 'inputId@': '/Input/id', 'testAccountId': App.getCurrentAccountId(), '@order': 'time-' } } } this.onChange(false) this.request(true, REQUEST_TYPE_JSON, url, req, {}, function (url, res, err) { App.onResponse(url, res, err) var rpObj = res.data if (rpObj != null && rpObj.code === CODE_SUCCESS) { App.isRandomListShow = true App.randoms = rpObj['[]'] vOutput.value = show ? '' : (output || '') App.showDoc() //App.onChange(false) } }) } }, // 设置文档 showDoc: function () { if (this.setDoc(doc) == false) { this.getDoc(function (d) { App.setDoc(d); }); } }, saveCache: function (url, key, value) { var cache = this.getCache(url); cache[key] = value localStorage.setItem('UIGO:' + url, JSON.stringify(cache)) }, getCache: function (url, key) { var cache = localStorage.getItem('UIGO:' + url) try { cache = parseJSON(cache) } catch(e) { App.log('login App.send >> try { cache = parseJSON(cache) } catch(e) {\n' + e.message) } cache = cache || {} return key == null ? cache : cache[key] }, /**登录确认 */ confirm: function () { switch (App.loginType) { case 'login': App.login(App.isAdminOperation) break case 'register': App.register(App.isAdminOperation) break case 'forget': App.resetPassword(App.isAdminOperation) break } }, showLogin: function (show, isAdmin) { this.isLoginShow = show this.isAdminOperation = isAdmin if (show != true) { return } var user = isAdmin ? App.User : null // add account App.accounts[App.currentAccountIndex] // alert("showLogin isAdmin = " + isAdmin + "; user = \n" + JSON.stringify(user, null, ' ')) if (user == null || StringUtil.isEmpty(user.phone, true)) { user = { phone: '13000082001', password: '123456' } } this.setRememberLogin(user.remember) this.account = user.phone this.password = user.password }, setRememberLogin(remember) { vRemember.checked = remember || false }, getCurrentAccount: function() { return App.accounts == null ? null : App.accounts[App.currentAccountIndex] }, getCurrentAccountId: function() { var a = App.getCurrentAccount() return a != null && a.isLoggedIn ? a.id : null }, /**登录 */ login: function (isAdminOperation, callback) { App.isLoginShow = false const req = { type: 0, // 登录方式,非必须 0-密码 1-验证码 phone: App.account, password: App.password, version: 1, // 全局默认版本号,非必须 remember: vRemember.checked, format: false, // 后端决定 // defaults: { // '@database': App.database, // '@schema': App.schema // } } if (isAdminOperation) { App.request(isAdminOperation, REQUEST_TYPE_JSON, App.server + '/login', req, {}, function (url, res, err) { if (callback) { callback(url, res, err) return } var rpObj = res.data || {} if (rpObj.code != CODE_SUCCESS) { alert('登录失败,请检查网络后重试。\n' + rpObj.msg + '\n详细信息可在浏览器控制台查看。') } else { var user = rpObj.user || {} if (user.id > 0) { user.remember = rpObj.remember user.phone = req.phone user.password = req.password App.User = user } //保存User到缓存 App.saveCache(App.server, 'User', user) if (App.currentAccountIndex == null || App.currentAccountIndex < 0) { App.currentAccountIndex = 0 } var item = App.accounts[App.currentAccountIndex] item.isLoggedIn = false App.onClickAccount(App.currentAccountIndex, item) //自动登录测试账号 if (user.id > 0) { App.showTestCase(true, false) } } }) } else { if (callback == null) { var item for (var i in App.accounts) { item = App.accounts[i] if (item != null && req.phone == item.phone) { alert(req.phone + ' 已在测试账号中!') // App.currentAccountIndex = i item.remember = vRemember.checked App.onClickAccount(i, item) return } } } App.showTestCase(false, App.isLocalShow) App.onChange(false) App.request(isAdminOperation, REQUEST_TYPE_JSON, App.project + '/login', req, {}, function (url, res, err) { if (callback) { callback(url, res, err) return } App.onResponse(url, res, err) //由login按钮触发,不能通过callback回调来实现以下功能 var data = res.data || {} if (data.code == CODE_SUCCESS) { var user = data.user || {} App.accounts.push({ isLoggedIn: true, id: user.id, name: user.name, phone: req.phone, password: req.password, remember: data.remember }) var lastItem = App.accounts[App.currentAccountIndex] if (lastItem != null) { lastItem.isLoggedIn = false } App.currentAccountIndex = App.accounts.length - 1 App.saveCache(App.getBaseUrl(), 'currentAccountIndex', App.currentAccountIndex) App.saveCache(App.getBaseUrl(), 'accounts', App.accounts) } }) } }, /**注册 */ register: function (isAdminOperation) { App.showUrl(isAdminOperation, '/register') vInput.value = JSON.stringify( { Privacy: { phone: App.account, _password: App.password }, User: { name: 'APIJSONUser' }, verify: vVerify.value }, null, ' ') App.showTestCase(false, false) App.onChange(false) App.send(isAdminOperation, function (url, res, err) { App.onResponse(url, res, err) var rpObj = res.data if (rpObj != null && rpObj.code === CODE_SUCCESS) { alert('注册成功') var privacy = rpObj.Privacy || {} App.account = privacy.phone App.loginType = 'login' } }) }, /**重置密码 */ resetPassword: function (isAdminOperation) { App.showUrl(isAdminOperation, '/put/password') vInput.value = JSON.stringify( { verify: vVerify.value, Privacy: { phone: App.account, _password: App.password } }, null, ' ') App.showTestCase(false, App.isLocalShow) App.onChange(false) App.send(isAdminOperation, function (url, res, err) { App.onResponse(url, res, err) var rpObj = res.data if (rpObj != null && rpObj.code === CODE_SUCCESS) { alert('重置密码成功') var privacy = rpObj.Privacy || {} App.account = privacy.phone App.loginType = 'login' } }) }, /**退出 */ logout: function (isAdminOperation, callback) { var req = {} if (isAdminOperation) { // alert('logout isAdminOperation this.saveCache(App.server, User, {})') this.saveCache(App.server, 'User', {}) } // alert('logout isAdminOperation = ' + isAdminOperation + '; url = ' + url) if (isAdminOperation) { this.request(isAdminOperation, REQUEST_TYPE_JSON, App.server + '/logout', req, {}, function (url, res, err) { if (callback) { callback(url, res, err) return } // alert('logout clear admin ') App.clearUser() App.onResponse(url, res, err) App.showTestCase(false, App.isLocalShow) }) } else { this.showTestCase(false, App.isLocalShow) this.onChange(false) this.request(isAdminOperation, REQUEST_TYPE_JSON, App.project + '/logout', req, {}, callback) } }, /**获取验证码 */ getVerify: function (isAdminOperation) { App.showUrl(isAdminOperation, '/post/verify') var type = App.loginType == 'login' ? 0 : (App.loginType == 'register' ? 1 : 2) vInput.value = JSON.stringify( { type: type, phone: App.account }, null, ' ') App.showTestCase(false, App.isLocalShow) App.onChange(false) App.send(isAdminOperation, function (url, res, err) { App.onResponse(url, res, err) var data = res.data || {} var obj = data.code == CODE_SUCCESS ? data.verify : null var verify = obj == null ? null : obj.verify if (verify != null) { //FIXME isEmpty校验时居然在verify=null! StringUtil.isEmpty(verify, true) == false) { vVerify.value = verify } }) }, clearUser: function () { App.User.id = 0 App.Privacy = {} App.remotes = [] App.saveCache(App.server, 'User', App.User) //应该用lastBaseUrl,baseUrl应随watch输入变化重新获取 }, /**计时回调 */ onHandle: function (before) { this.isDelayShow = false if (inputted != before) { clearTimeout(handler); return; } App.view = 'output'; vComment.value = ''; vUrlComment.value = ''; vOutput.value = 'resolving...'; //格式化输入代码 try { try { this.header = this.getHeader(inputted) } catch (e2) { this.isHeaderShow = true vHeader.select() throw new Error(e2.message) } vOutput.value = output = 'OK,请点击 [开始] 按钮来测试。[点击这里查看视频教程](https://www.bilibili.com/video/BV1kk4y1z7bW?seid=12328608249180257258)' // + code; App.showDoc() vUrlComment.value = isSingle || StringUtil.isEmpty(App.urlComment, true) ? '' : vUrl.value + App.urlComment; onURLScrollChanged() } catch(e) { log(e) vSend.disabled = true App.view = 'error' App.error = { msg: e.message } } }, /**输入内容改变 */ onChange: function (delay) { this.setBaseUrl(); inputted = new String(vHeader.value); // vComment.value = ''; vUrlComment.value = ''; clearTimeout(handler); this.isDelayShow = delay; handler = setTimeout(function () { App.onHandle(inputted); }, delay ? 2*1000 : 0); }, /**单双引号切换 */ transfer: function () { isSingle = ! isSingle; this.isVideoFirst = isSingle; this.isTestCaseShow = false; //TODO 切换图片与视频位置 // this.onChange(false); vContainer.removeChild(isSingle ? vComment : vInput); vContainer.appendChild(isSingle ? vComment : vInput); }, /**获取显示的请求类型名称 */ getTypeName: function (type) { return type == OPERATE_TYPE_REVIEW ? '查看' : (type == OPERATE_TYPE_REPLAY ? '回放' : '录制') }, /**请求类型切换 */ changeType: function () { var count = this.types == null ? 0 : this.types.length if (count > 1) { var index = this.types.indexOf(this.type) index++; this.type = this.types[index % count] } CodeUtil.type = this.type; this.onChange(false); if (this.type == OPERATE_TYPE_RECORD) { this.isRandomListShow = false this.randoms = [] this.isRandomListShow = true } else { App.showRandomList(true, (App.currentRemoteItem || {}).Flow) } }, /** * 删除注释 */ removeComment: function (json) { var reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n))|(\/\*(\n|.)*?\*\/)/g // 正则表达式 try { return new String(json).replace(reg, function(word) { // 去除注释后的文本 return /^\/{2,}/.test(word) || /^\/\*/.test(word) ? "" : word; }) } catch (e) { log('transfer delete comment in json >> catch \n' + e.message); } return json; }, showAndSend: function (branchUrl, req, isAdminOperation, callback) { App.showUrl(isAdminOperation, branchUrl) vInput.value = JSON.stringify(req, null, ' ') App.showTestCase(false, App.isLocalShow) App.onChange(false) App.send(isAdminOperation, callback) }, /**发送请求 */ send: function(isAdminOperation, callback) { if (this.type == OPERATE_TYPE_RECORD || this.type == OPERATE_TYPE_REPLAY) { this.onClickTestRandom() return } if (this.isTestCaseShow) { alert('请先打开一个 操作流程 Flow!') return } //TODO 播放视频、截屏、滚动日志 // this.onHandle(vHeader.value) // // clearTimeout(handler) // // var header // try { // header = this.getHeader(vHeader.value) // } catch (e) { // // alert(e.message) // return // } // // var url = this.getUrl() // // var httpReq = { // "package": req.package || App.getPackage(url), // "class": req.class || App.getClass(url), // "classArgs": req.classArgs, // "method": req.name || App.getMethod(url), // "methodArgs": req.methodArgs, // "static": req.static // } // // vOutput.value = "requesting... \nURL = " + url // this.view = 'output'; // // // this.setBaseUrl() // this.request(isAdminOperation, REQUEST_TYPE_JSON, this.project + '/method/invoke', httpReq, isAdminOperation ? {} : header, callback) // // this.locals = this.locals || [] // if (this.locals.length >= 1000) { //最多1000条,太多会很卡 // this.locals.splice(999, this.locals.length - 999) // } // var method = App.getMethod() // this.locals.unshift({ // 'Input': { // 'userId': App.User.id, // 'name': App.formatDateTime() + (StringUtil.isEmpty(req.tag, true) ? '' : ' ' + req.tag), // 'method': App.getMethod(url), // 'class': App.getClass(url), // 'package': App.getPackage(url), // 'type': App.type, // 'url': method, // 'request': JSON.stringify(req, null, ' '), // 'header': vHeader.value // } // }) // App.saveCache('', 'locals', this.locals) }, //请求 request: function (isAdminOperation, type, url, req, header, callback) { type = type || REQUEST_TYPE_JSON if (header != null && header.Cookie != null) { if (this.isDelegateEnabled) { header['Set-Cookie'] = header.Cookie delete header.Cookie } else { document.cookie = header.Cookie } } // axios.defaults.withcredentials = true axios({ method: (type == REQUEST_TYPE_PARAM ? 'get' : 'post'), url: (isAdminOperation == false && this.isDelegateEnabled ? (this.server + '/delegate?' + (type == REQUEST_TYPE_GRPC ? '$_type=GRPC&' : '') + '$_delegate_url=') : '' ) + StringUtil.noBlank(url), params: (type == REQUEST_TYPE_PARAM || type == REQUEST_TYPE_FORM ? req : null), data: (type == REQUEST_TYPE_JSON || type == REQUEST_TYPE_GRPC ? req : (type == REQUEST_TYPE_DATA ? toFormData(req) : null)), headers: header, //Accept-Encoding(HTTP Header 大小写不敏感,SpringBoot 接收后自动转小写)可能导致 Response 乱码 withCredentials: true, //Cookie 必须要 type == REQUEST_TYPE_JSON // crossDomain: true }) .then(function (res) { res = res || {} //any one of then callback throw error will cause it calls then(null) // if ((res.config || {}).name == 'options') { // return // } log('send >> success:\n' + JSON.stringify(res, null, ' ')) //未登录,清空缓存 if (res.data != null && res.data.code == 407) { // alert('request res.data != null && res.data.code == 407 >> isAdminOperation = ' + isAdminOperation) if (isAdminOperation) { // alert('request App.User = {} App.server = ' + App.server) App.clearUser() } else { // alert('request App.accounts[App.currentAccountIndex].isLoggedIn = false ') if (App.accounts[App.currentAccountIndex] != null) { App.accounts[App.currentAccountIndex].isLoggedIn = false } } } if (callback != null) { callback(url, res, null) return } App.onResponse(url, res, null) }) .catch(function (err) { log('send >> error:\n' + err) if (callback != null) { callback(url, {}, err) return } App.onResponse(url, {}, err) }) }, /**请求回调 */ onResponse: function (url, res, err) { if (res == null) { res = {} } log('onResponse url = ' + url + '\nerr = ' + err + '\nres = \n' + JSON.stringify(res)) if (err != null) { vOutput.value = "Response:\nurl = " + url + "\nerror = " + err.message; } else { var data = res.data || {} if (isSingle && data.code == CODE_SUCCESS) { //不格式化错误的结果 data = JSONResponse.formatObject(data); } App.jsoncon = JSON.stringify(data, null, ' '); App.view = 'code'; vOutput.value = ''; } }, /**处理按键事件 * @param event */ doOnKeyUp: function (event, type, isFilter, item) { var keyCode = event.keyCode ? event.keyCode : (event.which ? event.which : event.charCode); if (keyCode == 13) { // enter if (isFilter) { this.onFilterChange(type) return } if (type == null) { this.send(false); return } if (type == 'random' || type == 'randomSub') { var r = item == null ? null : item.Input if (r == null || r.id == null) { alert('请选择有效的选项!item.Input.id == null !') return } //修改 Input 的 count this.request(true, REQUEST_TYPE_JSON, this.server + '/put', { Input: { id: r.id, count: r.count, name: r.name }, tag: 'Input' }, {}, function (url, res, err) { var isOk = (res.data || {}).code == CODE_SUCCESS var msg = isOk ? '' : ('\nmsg: ' + StringUtil.get((res.data || {}).msg)) if (err != null) { msg += '\nerr: ' + err.msg } alert('修改' + (isOk ? '成功' : '失败') + '!\ncount: ' + r.count + '\nname: ' + r.name + msg ) App.isRandomEditable = !isOk }) return } } else { if (isFilter) { return } if (type == 'random' || type == 'randomSub') { this.isRandomEditable = true return } if (type == 'document' || type == 'testCase') { return } this.urlComment = ''; this.requestVersion = ''; this.onChange(true); } }, pageDown: function(type) { type = type || '' var page switch (type) { case 'testCase': page = this.testCasePage break case 'random': page = this.randomPage break case 'randomSub': page = this.randomSubPage break default: page = this.page break } if (page == null) { page = 0 } if (page > 0) { page -- switch (type) { case 'testCase': this.testCasePage = page break case 'random': this.randomPage = page break case 'randomSub': this.randomSubPage = page break default: this.page = page break } this.onFilterChange(type) } }, pageUp: function(type) { type = type || '' switch (type) { case 'testCase': this.testCasePage ++ break case 'random': this.randomPage ++ break case 'randomSub': this.randomSubPage ++ break default: this.page ++ break } this.onFilterChange(type) }, onFilterChange: function(type) { type = type || '' switch (type) { case 'testCase': this.saveCache(this.server, 'testCasePage', this.testCasePage) this.saveCache(this.server, 'testCaseCount', this.testCaseCount) this.remotes = null this.showTestCase(true, false) break case 'random': this.saveCache(this.server, 'randomPage', this.randomPage) this.saveCache(this.server, 'randomCount', this.randomCount) this.randoms = null this.showRandomList(true, (this.currentRemoteItem || {}).Input, false) break case 'randomSub': this.saveCache(this.server, 'randomSubPage', this.randomSubPage) this.saveCache(this.server, 'randomSubCount', this.randomSubCount) this.randomSubs = null this.showRandomList(true, (this.currentRemoteItem || {}).Input, true) break default: docObj = null doc = null this.saveCache(this.server, 'page', this.page) this.saveCache(this.server, 'count', this.count) // this.saveCache(this.server, 'docObj', null) // this.saveCache(this.server, 'doc', null) this.onChange(false) //虽然性能更好,但长时间没反应,用户会觉得未生效 // this.getDoc(function (d) { // // vOutput.value = 'resolving...'; // App.setDoc(d) // App.onChange(false) // }); break } }, /**转为请求代码 * @param rq */ getCode: function (rq) { var s = '\n\n\n### 请求代码(自动生成) \n'; switch (App.language) { case CodeUtil.LANGUAGE_KOTLIN: s += '\n#### <= Android-Kotlin: 空对象用 HashMap<String, Any>(),空数组用 ArrayList<Any>()\n' + '```kotlin \n' + CodeUtil.parseKotlinRequest(null, parseJSON(rq), 0, isSingle, false, false, App.type, App.getBaseUrl(), '/' + App.getMethod(), App.urlComment) + '\n ``` \n注:对象 {} 用 mapOf("key": value),数组 [] 用 listOf(value0, value1)\n'; break; case CodeUtil.LANGUAGE_JAVA: s += '\n#### <= Android-Java: 同名变量需要重命名' + ' \n ```java \n' + StringUtil.trim(CodeUtil.parseJavaRequest(null, parseJSON(rq), 0, isSingle, false, false, App.type, '/' + App.getMethod(), App.urlComment)) + '\n ``` \n注:' + (isSingle ? '用了 APIJSON 的 JSONRequest, JSONResponse 类,也可使用其它类封装,只要 JSON 有序就行\n' : 'LinkedHashMap<>() 可替换为 fastjson 的 JSONObject(true) 等有序JSON构造方法\n'); var serverCode = CodeUtil.parseJavaServer(App.type, '/' + App.getMethod(), App.database, App.schema, parseJSON(rq), isSingle); if (StringUtil.isEmpty(serverCode, true) != true) { s += '\n#### <= Server-Java: RESTful 等非 APIJSON 规范的 API' + ' \n ```java \n' + serverCode + '\n ``` \n注:' + (isSingle ? '分页和排序用了 Mybatis-PageHelper,如不需要可在生成代码基础上修改\n' : '使用 SSM(Spring + SpringMVC + Mybatis) 框架 \n'); } break; case CodeUtil.LANGUAGE_C_SHARP: s += '\n#### <= Unity3D-C\#: 键值对用 {"key", value}' + '\n ```csharp \n' + CodeUtil.parseCSharpRequest(null, parseJSON(rq), 0) + '\n ``` \n注:对象 {} 用 new JObject{{"key", value}},数组 [] 用 new JArray{value0, value1}\n'; break; case CodeUtil.LANGUAGE_SWIFT: s += '\n#### <= iOS-Swift: 空对象用 [ : ]' + '\n ```swift \n' + CodeUtil.parseSwiftRequest(null, parseJSON(rq), 0) + '\n ``` \n注:对象 {} 用 ["key": value],数组 [] 用 [value0, value1]\n'; break; case CodeUtil.LANGUAGE_OBJECTIVE_C: s += '\n#### <= iOS-Objective-C \n ```objective-c \n' + CodeUtil.parseObjectiveCRequest(null, parseJSON(rq)) + '\n ``` \n'; break; case CodeUtil.LANGUAGE_GO: s += '\n#### <= Web-Go: 对象 key: value 会被强制排序,每个 key: value 最后都要加逗号 ","' + ' \n ```go \n' + CodeUtil.parseGoRequest(null, parseJSON(rq), 0) + '\n ``` \n注:对象 {} 用 map[string]interface{} {"key": value},数组 [] 用 []interface{} {value0, value1}\n'; break; case CodeUtil.LANGUAGE_C_PLUS_PLUS: s += '\n#### <= Web-C++: 使用 RapidJSON' + ' \n ```cpp \n' + StringUtil.trim(CodeUtil.parseCppRequest(null, parseJSON(rq), 0, isSingle)) + '\n ``` \n注:std::string 类型值需要判断 RAPIDJSON_HAS_STDSTRING\n'; break; case CodeUtil.LANGUAGE_PHP: s += '\n#### <= Web-PHP: 空对象用 (object) ' + (isSingle ? '[]' : 'array()') + ' \n ```php \n' + CodeUtil.parsePHPRequest(null, parseJSON(rq), 0, isSingle) + '\n ``` \n注:对象 {} 用 ' + (isSingle ? '[\'key\' => value]' : 'array("key" => value)') + ',数组 [] 用 ' + (isSingle ? '[value0, value1]\n' : 'array(value0, value1)\n'); break; case CodeUtil.LANGUAGE_PYTHON: s += '\n#### <= Web-Python: 注释符用 \'\#\'' + ' \n ```python \n' + CodeUtil.parsePythonRequest(null, parseJSON(rq), 0, isSingle, vInput.value) + '\n ``` \n注:关键词转换 null: None, false: False, true: True'; break; //以下都不需要解析,直接用左侧的 JSON case CodeUtil.LANGUAGE_TYPE_SCRIPT: case CodeUtil.LANGUAGE_JAVA_SCRIPT: //case CodeUtil.LANGUAGE_PYTHON: s += '\n#### <= Web-JavaScript/TypeScript: 和左边的请求 JSON 一样 \n'; break; default: s += '\n没有生成代码,可能生成代码(封装,解析)的语言配置错误。\n'; break; } if (((App.User || {}).id || 0) > 0) { s += '\n\n#### 开放源码 ' + '\nAPIJSON 接口测试: https://github.com/TommyLemon/APIAuto ' + '\nAPIJSON 单元测试: https://github.com/TommyLemon/UnitAuto ' + '\nAPIJSON 官方文档: https://github.com/vincentCheng/apijson-doc ' + '\nAPIJSON 英文文档: https://github.com/ruoranw/APIJSONdocs ' + '\nAPIJSON 官方网站: https://github.com/APIJSON/apijson.org ' + '\nAPIJSON -Java版: https://github.com/Tencent/APIJSON ' + '\nAPIJSON - Go 版: https://github.com/glennliao/apijson-go ' + '\nAPIJSON - C# 版: https://github.com/liaozb/APIJSON.NET ' + '\nAPIJSON - PHP版: https://github.com/xianglong111/APIJSON-php ' + '\nAPIJSON -Node版: https://github.com/kevinaskin/apijson-node ' + '\nAPIJSON -Python: https://github.com/zhangchunlin/uliweb-apijson ' + '\n感谢热心的作者们的贡献,GitHub 右上角点 ⭐Star 支持下他们吧 ^_^'; } return s; }, /**显示文档 * @param d **/ setDoc: function (d) { if (d == null) { //解决死循环 || d == '') { return false; } doc = d; vOutput.value += ( '\n\n\n## 包和类文档\n自动查数据库表和字段属性来生成 \n\n' + d + '
UIGO - 📱 零代码快准稳 UI 智能录制回放平台'
+ '
🚀 自动兼容任意宽高比分辨率屏幕,自动精准等待网络请求,录制回放快、准、稳!'
+ '
由 UIGO(前端网页工具), APIJSON(后端接口服务) 等提供技术支持'
+ '
遵循 Apache-2.0 开源协议'
+ '
Copyright © 2019-' + new Date().getFullYear() + ' Tommy Lemon'
+ '
粤ICP备18005508号-1'
+ '