├── static ├── .gitkeep └── favicon.ico ├── src ├── util │ ├── bus.js │ └── format.js ├── assets │ ├── images │ │ ├── bottom-left.png │ │ └── bottom-right.png │ └── styles │ │ ├── icon-font │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ └── iconfont.woff │ │ ├── reports.css │ │ ├── tree.css │ │ ├── swagger.css │ │ ├── home.css │ │ └── iconfont.css ├── store │ ├── index.js │ ├── state.js │ └── mutations.js ├── validator.js ├── App.vue ├── pages │ ├── home │ │ ├── Home.vue │ │ └── components │ │ │ ├── Header.vue │ │ │ └── Side.vue │ ├── httprunner │ │ ├── components │ │ │ ├── RunCodeResult.vue │ │ │ ├── CodeEditor.vue │ │ │ ├── Hooks.vue │ │ │ ├── Parameters.vue │ │ │ ├── Extract.vue │ │ │ ├── Headers.vue │ │ │ ├── Variables.vue │ │ │ └── Validate.vue │ │ └── DebugTalk.vue │ ├── monaco-editor │ │ ├── util │ │ │ ├── log-language.js │ │ │ ├── javascript-completion.js │ │ │ ├── sql-completion.js │ │ │ └── python-completion.js │ │ └── BaseMonacoEditor.vue │ ├── config │ │ ├── RecordConfig.vue │ │ └── components │ │ │ ├── ConfigBody.vue │ │ │ └── ConfigList.vue │ ├── auth │ │ ├── Login.vue │ │ └── Register.vue │ ├── fastrunner │ │ ├── config │ │ │ ├── RecordConfig.vue │ │ │ └── components │ │ │ │ ├── ConfigBody.vue │ │ │ │ └── ConfigList.vue │ │ └── case │ │ │ └── components │ │ │ └── TestBody.vue │ ├── project │ │ └── ProjectDashBoard.vue │ └── variables │ │ └── HostAddress.vue ├── main.js ├── router │ └── index.js └── restful │ └── api.js ├── Dockerfile ├── .editorconfig ├── .gitignore ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── .babelrc ├── .postcssrc.js ├── default.conf ├── index.html ├── README.md ├── LICENSE └── package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/util/bus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | export default new Vue() 3 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihuacai168/AnotherFasterWeb/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/bottom-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihuacai168/AnotherFasterWeb/HEAD/src/assets/images/bottom-left.png -------------------------------------------------------------------------------- /src/assets/images/bottom-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihuacai168/AnotherFasterWeb/HEAD/src/assets/images/bottom-right.png -------------------------------------------------------------------------------- /src/assets/styles/icon-font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihuacai168/AnotherFasterWeb/HEAD/src/assets/styles/icon-font/iconfont.eot -------------------------------------------------------------------------------- /src/assets/styles/icon-font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihuacai168/AnotherFasterWeb/HEAD/src/assets/styles/icon-font/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/styles/icon-font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lihuacai168/AnotherFasterWeb/HEAD/src/assets/styles/icon-font/iconfont.woff -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hub.c.163.com/library/nginx 2 | 3 | RUN rm /etc/nginx/conf.d/default.conf 4 | 5 | RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const FasterRunner = process.env.FasterRunner ? process.env.FasterRunner : "Another FasterRunner" 3 | module.exports = { 4 | NODE_ENV: '"production"', 5 | FasterRunner: "'" + FasterRunner + "'" 6 | } 7 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import state from './state' 4 | import mutations from './mutations' 5 | 6 | Vue.use(Vuex) 7 | 8 | export default new Vuex.Store({ 9 | state, 10 | mutations 11 | }) 12 | -------------------------------------------------------------------------------- /src/store/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | routerName: null, 3 | projectName: '', 4 | token: null, 5 | user: null, 6 | is_superuser: false, 7 | show_hosts: false, 8 | duration: 2000, 9 | FasterRunner: process.env.FasterRunner 10 | } 11 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | const FasterRunner = process.env.FasterRunner ? process.env.FasterRunner : "Another FasterRunner" 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"', 7 | FasterRunner: "'" + FasterRunner + "'" 8 | }) 9 | -------------------------------------------------------------------------------- /default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | server_name localhost; # 修改为docker服务宿主机的ip 4 | 5 | location / { 6 | root /usr/share/nginx/html; 7 | index index.html index.htm; 8 | try_files $uri $uri/ /index.html =404; 9 | } 10 | 11 | error_page 500 502 503 504 /50x.html; 12 | location = /50x.html { 13 | root html; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FastRunner 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/validator.js: -------------------------------------------------------------------------------- 1 | export const isNumArray = (rule, value, callback) => { 2 | if (value === ""){ 3 | callback() 4 | } 5 | const numStr = /^[0-9,]*$/ 6 | if (!numStr.test(value)) { 7 | callback(new Error('只能为整数和英文逗号, 且不能包含空格')) 8 | try { 9 | eval(value.split(",")) 10 | } catch (err) { 11 | callback(new Error('字符串转换为整数列表错误: ' + err)) 12 | } 13 | } else { 14 | callback() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | isLogin(state, value) { 4 | state.token = value 5 | }, 6 | 7 | setUser(state, value) { 8 | state.user = value 9 | }, 10 | setRouterName(state, value) { 11 | state.routerName = value 12 | }, 13 | setProjectName(state, value) { 14 | if (value !== '' ){ 15 | value = ' / ' + value.replaceAll('/', '').replaceAll(' ', '') 16 | } 17 | state.projectName = value 18 | }, 19 | 20 | setIsSuperuser(state, value) { 21 | state.is_superuser = value 22 | }, 23 | setShowHots(state, value) { 24 | state.show_hosts = value 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FasterWeb 2 | 3 | ![LICENSE](https://img.shields.io/github/license/yinquanwang/FasterRunner.svg) 4 | > FasterWeb that depends FasterRunner 5 | 6 | ## 本地开发环境部署 7 | 8 | ``` bash 9 | # install dependencies 10 | npm install 11 | 12 | # serve with hot reload at localhost:8080 13 | npm run dev 14 | 15 | ``` 16 | 17 | 测试 18 | ----------- 19 | 20 | 1. open url(recommend chrome): http://localhost:8080/fastrunner/login 21 | 22 | ## Docker 部署 nginx模式 23 | -------------- 24 | 1. 修改default.conf配置文件 server_name的ip(宿主机IP), 端口默认8080 25 | 2. 修改/src/restful/api.js baseUrl地址, 即为fastrunner容器运行的宿主机地址 26 | 3. 执行npm install, npm run build # 生成生产环境包 27 | 3. docker build -t fasterweb:latest . # 构建docker镜像 28 | 4. docker run -d --name fasterweb --net=host --restart always fasterweb:latest # 后台运行docker容器 29 | 5. open url: http://宿主机ip:8080/fastrunner/login 30 | -------------------------------------------------------------------------------- /src/pages/home/Home.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 34 | 35 | 41 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/RunCodeResult.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /src/assets/styles/reports.css: -------------------------------------------------------------------------------- 1 | .success { 2 | font-weight: bold; 3 | font-size: medium; 4 | color: #67c23a; 5 | } 6 | 7 | .error { 8 | font-weight: bold; 9 | font-size: medium; 10 | color: red; 11 | } 12 | 13 | .failure { 14 | font-weight: bold; 15 | font-size: medium; 16 | color: salmon; 17 | } 18 | 19 | .skipped { 20 | font-weight: bold; 21 | font-size: medium; 22 | color: #909399; 23 | } 24 | 25 | .POST { 26 | color: #49cc90; 27 | } 28 | 29 | 30 | .PUT { 31 | color: #fca130; 32 | } 33 | 34 | .GET { 35 | color: #61affe; 36 | } 37 | 38 | 39 | .DELETE { 40 | color: #f93e3e; 41 | } 42 | 43 | 44 | .PATCH { 45 | color: #50e3c2; 46 | } 47 | 48 | .HEAD { 49 | color: #e6a23c; 50 | } 51 | 52 | .OPTIONS { 53 | color: #409eff; 54 | } 55 | 56 | 57 | .code-block { 58 | 59 | border: 1px solid #ebedef; 60 | border-radius: 4px; 61 | color: #222 !important; 62 | font-family: Consolas, monospace; 63 | font-size: 13px; 64 | margin: 0; 65 | padding: 7px 10px; 66 | overflow: auto; 67 | } 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 yinquanwang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/assets/styles/tree.css: -------------------------------------------------------------------------------- 1 | ul li { 2 | list-style: none; 3 | } 4 | 5 | .operation-li { 6 | line-height: 60px; 7 | color: #555; 8 | font-size: 12px; 9 | width: 240px; 10 | margin-top: 1px; 11 | } 12 | 13 | .api-tree { 14 | padding: 0; 15 | margin: 0; 16 | } 17 | 18 | .nav-api-header { 19 | height: 48px; 20 | border-bottom: 1px solid #ddd; 21 | background-color: #F7F7F7; 22 | 23 | } 24 | 25 | .nav-api-side { 26 | overflow: auto; 27 | border: 1px solid #ddd; 28 | width: 240px; 29 | margin-left: 10px; 30 | border-radius: 4px; 31 | position: fixed; 32 | top: 110px; 33 | bottom: 0; 34 | 35 | } 36 | 37 | .custom-tree-node { 38 | flex: 1; 39 | display: flex; 40 | align-items: center; 41 | justify-content: space-between; 42 | font-size: 14px; 43 | padding-right: 8px; 44 | } 45 | 46 | 47 | .tree { 48 | overflow-y: auto; 49 | overflow-x: auto; 50 | width: 280px; 51 | height: 500px; 52 | } 53 | 54 | .el-tree { 55 | min-width: 100%; 56 | display: inline-block !important; 57 | } 58 | 59 | .el-tree-node__expand-icon { 60 | padding: 4px !important; 61 | } 62 | 63 | .el-tree-node__content { 64 | height: 50px; 65 | } 66 | 67 | .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content { 68 | background-color: rgba(64, 158, 255, .1); 69 | color: #409EFF; 70 | } 71 | -------------------------------------------------------------------------------- /src/pages/monaco-editor/util/log-language.js: -------------------------------------------------------------------------------- 1 | function registerLanguage(monaco) { 2 | monaco.languages.register({ 3 | id: "log" 4 | }); 5 | monaco.languages.setMonarchTokensProvider("log", { 6 | tokenizer: { 7 | root: [ 8 | [/(^[=a-zA-Z].*|\d\s.*)/, "log-normal"], 9 | [/\sERROR\s.*/, "log-error"], 10 | [/\sWARN\s.*/, "log-warn"], 11 | [/\sINFO\s.*/, "log-info"], 12 | [ 13 | /^([0-9]{4}||[0-9]{2})-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]{3})?/, 14 | "log-date", 15 | ], 16 | [ 17 | /^[0-9]{2}\/[0-9]{2}\/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]{3})?/, 18 | "log-date", 19 | ], 20 | [/(^\*\*Waiting queue:.*)/, "log-info"], 21 | [/(^\*\*result tips:.*)/, "log-info"], 22 | ], 23 | }, 24 | }); 25 | monaco.editor.defineTheme("log", { 26 | base: "vs", 27 | inherit: true, 28 | rules: [{ 29 | token: "log-info", 30 | foreground: "4b71ca" 31 | }, 32 | { 33 | token: "log-error", 34 | foreground: "ff0000", 35 | fontStyle: "bold" 36 | }, 37 | { 38 | token: "log-warn", 39 | foreground: "FFA500" 40 | }, 41 | { 42 | token: "log-date", 43 | foreground: "008800" 44 | }, 45 | { 46 | token: "log-normal", 47 | foreground: "808080" 48 | }, 49 | ], 50 | colors: { 51 | "editor.lineHighlightBackground": "#ffffff", 52 | "editorGutter.background": "#f7f7f7", 53 | }, 54 | }); 55 | 56 | } 57 | 58 | export default registerLanguage; 59 | -------------------------------------------------------------------------------- /src/util/format.js: -------------------------------------------------------------------------------- 1 | export const datetimeObj2str = function (time, format = 'YY-MM-DD hh:mm:ss') { 2 | let date = new Date(time); 3 | let year = date.getFullYear(), 4 | month = date.getMonth() + 1, 5 | day = date.getDate(), 6 | hour = date.getHours(), 7 | min = date.getMinutes(), 8 | sec = date.getSeconds(); 9 | let preArr = Array.apply(null, Array(10)).map(function (elem, index) { 10 | return '0' + index; 11 | }); 12 | 13 | let newTime = format.replace(/YY/g, year) 14 | .replace(/MM/g, preArr[month] || month) 15 | .replace(/DD/g, preArr[day] || day) 16 | .replace(/hh/g, preArr[hour] || hour) 17 | .replace(/mm/g, preArr[min] || min) 18 | .replace(/ss/g, preArr[sec] || sec); 19 | 20 | return newTime; 21 | } 22 | 23 | export const timestamp2time = function (timestamp) { 24 | if (!timestamp) { 25 | return '' 26 | } 27 | let date = new Date(timestamp * 1000); 28 | const Y = date.getFullYear() + '-'; 29 | 30 | // js的月份从0开始 31 | const month = date.getMonth() + 1; 32 | const M = (month < 10 ? '0' + month : month) + '-'; 33 | 34 | const days = date.getDate(); 35 | const D = (days + 1 < 10 ? '0' + days : days) + ' '; 36 | 37 | const hours = date.getHours(); 38 | const h = (hours + 1 < 10 ? '0' + hours : hours) + ':'; 39 | 40 | const minutes = date.getMinutes(); 41 | const m = (minutes + 1 < 10 ? '0' + minutes : minutes) + ':'; 42 | 43 | const seconds = date.getSeconds(); 44 | const s = seconds + 1 < 10 ? '0' + seconds : seconds; 45 | 46 | return Y + M + D + h + m + s; 47 | } 48 | -------------------------------------------------------------------------------- /src/pages/monaco-editor/util/javascript-completion.js: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor' 2 | // js 有内置提示 3 | function createCompleter(getExtraHints) { 4 | const createSuggestions = function (model, textUntilPosition) { 5 | let text = model.getValue(); 6 | textUntilPosition = textUntilPosition.replace(/[\*\[\]@\$\(\)]/g, "").replace(/(\s+|\.)/g, " "); 7 | let arr = textUntilPosition.split(/[\s;]/); 8 | let activeStr = arr[arr.length - 1]; 9 | let len = activeStr.length; 10 | let rexp = new RegExp("([^\\w]|^)" + activeStr + "\\w*", "gim"); 11 | let match = text.match(rexp); 12 | let mergeHints = Array.from(new Set([...getExtraHints(model)])) 13 | .sort() 14 | .filter(ele => { 15 | let rexp = new RegExp(ele.substr(0, len), "gim"); 16 | return (match && match.length === 1 && ele === activeStr) || 17 | ele.length === 1 ? false : activeStr.match(rexp); 18 | }); 19 | return mergeHints.map(ele => ({ 20 | label: ele, 21 | kind: monaco.languages.CompletionItemKind.Text, 22 | documentation: ele, 23 | insertText: ele 24 | })); 25 | }; 26 | return { 27 | provideCompletionItems(model, position) { 28 | let textUntilPosition = model.getValueInRange({ 29 | startLineNumber: position.lineNumber, 30 | startColumn: 1, 31 | endLineNumber: position.lineNumber, 32 | endColumn: position.column 33 | }); 34 | return { suggestions: createSuggestions(model, textUntilPosition) }; 35 | } 36 | } 37 | } 38 | export default createCompleter; 39 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/CodeEditor.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 67 | -------------------------------------------------------------------------------- /src/pages/home/components/Header.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 44 | 45 | 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-web", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "尹全旺 <1263374981@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "ajv": "^6.12.6", 14 | "apexcharts": "^3.27.3", 15 | "axios": "^0.18.0", 16 | "babel-preset-es2015": "^6.24.1", 17 | "echarts": "^4.9.0", 18 | "element-ui": "^2.13.0", 19 | "monaco-editor": "0.20.0", 20 | "monaco-editor-webpack-plugin": "1.9.0", 21 | "sass-loader": "^11.0.1", 22 | "v-jsoneditor": "^1.4.4", 23 | "vue": "^2.5.2", 24 | "vue-apexcharts": "^1.6.2", 25 | "vue-clipboard2": "^0.3.1", 26 | "vue-loader": "^13.7.3", 27 | "vue-monaco-editor": "^0.0.19", 28 | "vue-router": "^3.0.1", 29 | "vuedraggable": "^2.16.0", 30 | "vuex": "^3.0.1" 31 | }, 32 | "devDependencies": { 33 | "autoprefixer": "^7.1.2", 34 | "babel-core": "^6.22.1", 35 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 36 | "babel-loader": "^7.1.1", 37 | "babel-plugin-syntax-jsx": "^6.18.0", 38 | "babel-plugin-transform-runtime": "^6.22.0", 39 | "babel-plugin-transform-vue-jsx": "^3.5.0", 40 | "babel-preset-env": "^1.3.2", 41 | "babel-preset-stage-2": "^6.22.0", 42 | "chalk": "^2.0.1", 43 | "copy-webpack-plugin": "^4.0.1", 44 | "css-loader": "^0.28.0", 45 | "extract-text-webpack-plugin": "^3.0.0", 46 | "file-loader": "^1.1.4", 47 | "friendly-errors-webpack-plugin": "^1.6.1", 48 | "html-webpack-plugin": "^2.30.1", 49 | "node-notifier": "^5.1.2", 50 | "optimize-css-assets-webpack-plugin": "^3.2.0", 51 | "ora": "^1.2.0", 52 | "portfinder": "^1.0.13", 53 | "postcss-import": "^11.0.0", 54 | "postcss-loader": "^2.0.8", 55 | "postcss-url": "^7.2.1", 56 | "rimraf": "^2.6.0", 57 | "semver": "^5.3.0", 58 | "shelljs": "^0.7.6", 59 | "uglifyjs-webpack-plugin": "^1.1.1", 60 | "url-loader": "^0.5.8", 61 | "vue-easytable": "^1.7.1", 62 | "vue-loader": "^13.7.3", 63 | "vue-style-loader": "^3.0.1", 64 | "vue-template-compiler": "^2.5.2", 65 | "vue2-ace-editor": "0.0.15", 66 | "webpack": "^3.6.0", 67 | "webpack-bundle-analyzer": "^2.9.0", 68 | "webpack-dev-server": "^2.9.1", 69 | "webpack-merge": "^4.1.0", 70 | "zip-webpack-plugin": "^2.0.0" 71 | }, 72 | "engines": { 73 | "node": ">= 6.0.0", 74 | "npm": ">= 3.0.0" 75 | }, 76 | "browserslist": [ 77 | "> 1%", 78 | "last 2 versions", 79 | "not ie <= 8" 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | /* '/httprunner': { 15 | target: 'http://localhost:8000',//设置你调用的接口域名和端口号 别忘了加http 16 | changeOrigin: true, 17 | pathRewrite: { 18 | '^/httprunner': '' 19 | } 20 | }*/ 21 | }, 22 | 23 | // Various Dev Server settings 24 | host: '0.0.0.0', // can be overwritten by process.variables.HOST 25 | port: 8080, // can be overwritten by process.variables.PORT, if port is in use, a free one will be determined 26 | autoOpenBrowser: false, 27 | errorOverlay: true, 28 | notifyOnErrors: true, 29 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 30 | 31 | 32 | /** 33 | * Source Maps 34 | */ 35 | 36 | // https://webpack.js.org/configuration/devtool/#development 37 | devtool: 'cheap-module-eval-source-map', 38 | 39 | // If you have problems debugging vue-files in devtools, 40 | // set this to false - it *may* help 41 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 42 | cacheBusting: true, 43 | 44 | cssSourceMap: true 45 | }, 46 | 47 | build: { 48 | // Template for index.html 49 | index: path.resolve(__dirname, '../dist/index.html'), 50 | 51 | // Paths 52 | assetsRoot: path.resolve(__dirname, '../dist'), 53 | assetsSubDirectory: 'static', 54 | assetsPublicPath: '/', 55 | 56 | /** 57 | * Source Maps 58 | */ 59 | 60 | productionSourceMap: true, 61 | // https://webpack.js.org/configuration/devtool/#production 62 | devtool: '#source-map', 63 | 64 | // Gzip off by default as many popular static hosts such as 65 | // Surge or Netlify already gzip all static assets for you. 66 | // Before setting to `true`, make sure to: 67 | // npm install --save-dev compression-webpack-plugin 68 | productionGzip: false, 69 | productionGzipExtensions: ['js', 'css'], 70 | 71 | // Run the build command with an extra argument to 72 | // View the bundle analyzer report after build finishes: 73 | // `npm run build --report` 74 | // Set to `true` or `false` to always turn it on or off 75 | bundleAnalyzerReport: process.env.npm_config_report 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/pages/monaco-editor/util/sql-completion.js: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor' 2 | const hints = [ 3 | "SELECT", 4 | "INSERT", 5 | "DELETE", 6 | "UPDATE", 7 | "CREATE TABLE", 8 | "DROP TABLE", 9 | "ALTER TABLE", 10 | "CREATE VIEW", 11 | "DROP VIEW", 12 | "CREATE INDEX", 13 | "DROP INDEX", 14 | "CREATE PROCEDURE", 15 | "DROP PROCEDURE", 16 | "CREATE TRIGGER", 17 | "DROP TRIGGER", 18 | "CREATE SCHEMA", 19 | "DROP SCHEMA", 20 | "CREATE DOMAIN", 21 | "ALTER DOMAIN", 22 | "DROP DOMAIN", 23 | "GRANT", 24 | "DENY", 25 | "REVOKE", 26 | "COMMIT", 27 | "ROLLBACK", 28 | "SET TRANSACTION", 29 | "DECLARE", 30 | "EXPLAN", 31 | "OPEN", 32 | "FETCH", 33 | "CLOSE", 34 | "PREPARE", 35 | "EXECUTE", 36 | "DESCRIBE", 37 | "FORM", 38 | "ORDER BY"] 39 | function createCompleter(getExtraHints) { 40 | const createSuggestions = function (model, textUntilPosition) { 41 | let text = model.getValue(); 42 | textUntilPosition = textUntilPosition.replace(/[\*\[\]@\$\(\)]/g, "").replace(/(\s+|\.)/g, " "); 43 | let arr = textUntilPosition.split(/[\s;]/); 44 | let activeStr = arr[arr.length - 1]; 45 | let len = activeStr.length; 46 | let rexp = new RegExp("([^\\w]|^)" + activeStr + "\\w*", "gim"); 47 | let match = text.match(rexp); 48 | let textHints = !match ? [] : 49 | match.map(ele => { 50 | let rexp = new RegExp(activeStr, "gim"); 51 | let search = ele.search(rexp); 52 | return ele.substr(search); 53 | }); 54 | let mergeHints = Array.from(new Set([...hints, ...textHints, ...getExtraHints(model)])) 55 | .sort() 56 | .filter(ele => { 57 | let rexp = new RegExp(ele.substr(0, len), "gim"); 58 | return (match && match.length === 1 && ele === activeStr) || 59 | ele.length === 1 ? false : activeStr.match(rexp); 60 | }); 61 | return mergeHints.map(ele => ({ 62 | label: ele, 63 | kind: hints.indexOf(ele) > -1 ? 64 | monaco.languages.CompletionItemKind.Keyword : 65 | monaco.languages.CompletionItemKind.Text, 66 | documentation: ele, 67 | insertText: ele 68 | })); 69 | }; 70 | return { 71 | provideCompletionItems(model, position) { 72 | let textUntilPosition = model.getValueInRange({ 73 | startLineNumber: position.lineNumber, 74 | startColumn: 1, 75 | endLineNumber: position.lineNumber, 76 | endColumn: position.column 77 | }); 78 | return { suggestions: createSuggestions(model, textUntilPosition) }; 79 | } 80 | } 81 | } 82 | export default createCompleter; 83 | -------------------------------------------------------------------------------- /src/pages/home/components/Side.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 72 | 73 | 85 | -------------------------------------------------------------------------------- /src/assets/styles/swagger.css: -------------------------------------------------------------------------------- 1 | .block_post { 2 | border: 1px solid #49cc90; 3 | background-color: rgba(73, 204, 144, .1) 4 | } 5 | 6 | .block_method_post { 7 | background-color: #49cc90; 8 | } 9 | 10 | .block_put { 11 | border: 1px solid #fca130; 12 | background-color: rgba(252, 161, 48, .1) 13 | } 14 | 15 | .block_method_put { 16 | background-color: #fca130; 17 | } 18 | 19 | .block_get { 20 | border: 1px solid #61affe; 21 | background-color: rgba(97, 175, 254, .1) 22 | } 23 | 24 | .block_method_get { 25 | background-color: #61affe; 26 | } 27 | 28 | .block_delete { 29 | border: 1px solid #f93e3e; 30 | background-color: rgba(249, 62, 62, .1) 31 | } 32 | 33 | .block_method_delete { 34 | background-color: #f93e3e; 35 | } 36 | 37 | .block_patch { 38 | border: 1px solid #50e3c2; 39 | background-color: rgba(80, 227, 194, .1) 40 | } 41 | 42 | .block_method_patch { 43 | background-color: #50e3c2; 44 | } 45 | 46 | .block_head { 47 | border: 1px solid #e6a23c; 48 | background-color: rgba(230, 162, 60, .1) 49 | } 50 | 51 | .block_method_head { 52 | background-color: #e6a23c; 53 | } 54 | 55 | .block_options { 56 | border: 1px solid #409eff; 57 | background-color: rgba(64, 158, 255, .1) 58 | } 59 | 60 | .block_method_options { 61 | background-color: #409eff; 62 | } 63 | 64 | .block { 65 | position: relative; 66 | border-radius: 4px; 67 | height: 43.6px; 68 | overflow: hidden; 69 | padding: 5px; 70 | display: flex; 71 | align-items: center; 72 | } 73 | 74 | .block_url { 75 | word-break: normal; 76 | width: auto; 77 | display: block; 78 | white-space: pre-wrap; 79 | word-wrap: break-word; 80 | overflow: hidden; 81 | -webkit-box-flex: 1; 82 | -ms-flex: 1; 83 | font-family: Open Sans, sans-serif; 84 | color: #3b4151; 85 | } 86 | 87 | .block_name { 88 | padding-left: 5px; 89 | word-break: normal; 90 | width: auto; 91 | display: block; 92 | white-space: pre-wrap; 93 | word-wrap: break-word; 94 | overflow: hidden; 95 | -webkit-box-flex: 1; 96 | -ms-flex: 1; 97 | font-family: Open Sans, sans-serif; 98 | color: #3b4151; 99 | } 100 | 101 | .block_method_color { 102 | cursor: pointer; 103 | color: #fff; 104 | } 105 | 106 | .block-method { 107 | font-size: 14px; 108 | font-weight: 600; 109 | min-width: 50px; 110 | padding: 5px 10px; 111 | text-align: center; 112 | border-radius: 3px; 113 | text-shadow: 0 1px 0 rgba(0, 0, 0, .1); 114 | font-family: Titillium Web, sans-serif; 115 | } 116 | 117 | .block-summary-description { 118 | word-break: normal; 119 | width: auto; 120 | display: block; 121 | white-space: pre-wrap; 122 | word-wrap: break-word; 123 | overflow: hidden; 124 | -webkit-box-flex: 1; 125 | -ms-flex: 1; 126 | font-family: Open Sans, sans-serif; 127 | color: #3b4151; 128 | } 129 | 130 | h4 { 131 | display: block; 132 | -webkit-margin-before: 1.33em; 133 | -webkit-margin-after: 1.33em; 134 | -webkit-margin-start: 0px; 135 | -webkit-margin-end: 0px; 136 | font-weight: bold; 137 | } 138 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import ElementUI from 'element-ui' 5 | import VJsoneditor from 'v-jsoneditor' 6 | import VueClipboard from 'vue-clipboard2' 7 | import echarts from 'echarts' 8 | import VueApexCharts from 'vue-apexcharts' 9 | import 'element-ui/lib/theme-chalk/index.css' 10 | import App from './App' 11 | import router from './router' 12 | import 'styles/iconfont.css' 13 | import 'styles/swagger.css' 14 | import 'styles/tree.css' 15 | import 'styles/home.css' 16 | import 'styles/reports.css' 17 | import * as api from './restful/api' 18 | import store from './store' 19 | import {datetimeObj2str, timestamp2time} from './util/format.js' 20 | Vue.config.productionTip = false 21 | Vue.use(ElementUI) 22 | Vue.prototype.$api = api 23 | Vue.prototype.$echarts = echarts 24 | Vue.use(VJsoneditor) 25 | Vue.use(VueClipboard) 26 | Vue.use(VueApexCharts) 27 | 28 | Vue.component('ApexCharts', VueApexCharts) 29 | 30 | Vue.filter('datetimeFormat', datetimeObj2str); 31 | Vue.filter("timestampToTime", timestamp2time); 32 | 33 | Vue.prototype.setLocalValue = function (name, value) { 34 | if (window.localStorage) { 35 | localStorage.setItem(name, value); 36 | } else { 37 | alert('This browser does NOT support localStorage'); 38 | } 39 | }; 40 | Vue.prototype.getLocalValue = function (name) { 41 | const value = localStorage.getItem(name); 42 | if (value) { 43 | // localStorage只能存字符串,布尔类型需要转换 44 | if(value === "false" || value === "true"){ 45 | return eval(value) 46 | } 47 | return value; 48 | } else { 49 | return ''; 50 | } 51 | }; 52 | 53 | router.beforeEach((to, from, next) => { 54 | /* 路由发生变化修改页面title */ 55 | setTimeout((res) => { 56 | if (to.meta.title) { 57 | document.title = to.meta.title 58 | } 59 | 60 | if (to.meta.requireAuth) { 61 | if (store.state.token !== '') { 62 | next(); 63 | } else { 64 | next({ 65 | name: 'Login', 66 | }) 67 | } 68 | } else { 69 | next() 70 | } 71 | }) 72 | 73 | }) 74 | 75 | /* eslint-disable no-new */ 76 | new Vue({ 77 | el: '#app', 78 | router, 79 | store, 80 | components: {App}, 81 | template: '', 82 | created() { 83 | if (this.getLocalValue("token") === null) { 84 | this.setLocalValue("token", ""); 85 | } 86 | if (this.getLocalValue("user") === null) { 87 | this.setLocalValue("user", ""); 88 | } 89 | if (this.getLocalValue("routerName") === null) { 90 | this.setLocalValue("routerName", "ProjectList"); 91 | } 92 | 93 | if (this.getLocalValue("is_superuser") === null) { 94 | this.setLocalValue("is_superuser", false); 95 | } 96 | if (this.getLocalValue("show_hosts") === null) { 97 | this.setLocalValue("show_hosts", false); 98 | } 99 | this.$store.commit("isLogin", this.getLocalValue("token")); 100 | this.$store.commit("setUser", this.getLocalValue("user")); 101 | this.$store.commit("setRouterName", this.getLocalValue("routerName")); 102 | this.$store.commit("setIsSuperuser", this.getLocalValue("is_superuser")); 103 | this.$store.commit("setShowHots", this.getLocalValue("show_hosts")); 104 | } 105 | }) 106 | -------------------------------------------------------------------------------- /src/assets/styles/home.css: -------------------------------------------------------------------------------- 1 | #form-content { 2 | width: 450px; 3 | margin: 40px auto; 4 | background-color: #FFFFFF; 5 | padding: 40px 50px; 6 | text-align: center; 7 | border-radius: 2px; 8 | -webkit-box-shadow: 0px 16px 44px 0 RGBA(0, 0, 1, 0.2); 9 | -moz-box-shadow: 0px 16px 44px 0 RGBA(0, 0, 1, 0.2); 10 | box-shadow: 0px 16px 44px 0 RGBA(0, 0, 1, 0.2); 11 | } 12 | 13 | .bottom-right { 14 | position: fixed; 15 | right: 0px; 16 | bottom: 0px; 17 | } 18 | 19 | .bottom-left { 20 | position: fixed; 21 | left: 0px; 22 | bottom: 0px; 23 | } 24 | 25 | .bottom-right img { 26 | width: 364px; 27 | height: 208px; 28 | } 29 | 30 | .bottom-left img { 31 | width: 332px; 32 | height: 271px; 33 | } 34 | 35 | img { 36 | vertical-align: middle; 37 | } 38 | 39 | img { 40 | border: 0; 41 | } 42 | 43 | .form-input-div .icon { 44 | position: relative; 45 | left: 16px; 46 | top: 8px; 47 | color: #172B4D; 48 | font-size: 18px; 49 | } 50 | 51 | .form-input-div input.has-error { 52 | border-color: #fc4949; 53 | } 54 | 55 | #form-msg { 56 | color: #172B4D; 57 | font-size: 30px; 58 | text-align: left; 59 | font-weight: 300; 60 | } 61 | 62 | .form-submit .btn-primary { 63 | width: 100%; 64 | height: 42px; 65 | font-size: 15px; 66 | color: #fff; 67 | background: #1F5DEA; 68 | border: 1px solid #1F5DEA; 69 | border-radius: 2px; 70 | } 71 | 72 | .form-foot { 73 | position: relative; 74 | margin-top: 20px; 75 | color: #A8ACB9; 76 | font-size: 12px; 77 | text-align: left; 78 | } 79 | 80 | .form-foot a, .form-foot a:hover, .form-foot a:active { 81 | color: #1F5DEA; 82 | text-decoration: none; 83 | } 84 | 85 | body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, button, textarea, p, blockquote, th, td { 86 | margin: 0; 87 | padding: 0; 88 | -webkit-box-sizing: border-box; 89 | -moz-box-sizing: border-box; 90 | box-sizing: border-box; 91 | } 92 | 93 | a:hover, a:active, a:visited, a:focus { 94 | text-decoration: none !important; 95 | cursor: pointer; 96 | } 97 | 98 | .btn:active { 99 | background: #19A8FF; 100 | } 101 | 102 | .btn { 103 | display: inline-block; 104 | padding: 6px 10px; 105 | margin-bottom: 0; 106 | font-size: 13px; 107 | font-weight: normal; 108 | line-height: 1.42857143; 109 | text-align: center; 110 | white-space: nowrap; 111 | vertical-align: middle; 112 | -ms-touch-action: manipulation; 113 | touch-action: manipulation; 114 | cursor: pointer; 115 | -webkit-user-select: none; 116 | -moz-user-select: none; 117 | -ms-user-select: none; 118 | user-select: none; 119 | background-image: none; 120 | border: 1px solid transparent; 121 | border-radius: 2px; 122 | } 123 | 124 | input, button, select, textarea { 125 | font-family: inherit; 126 | font-size: inherit; 127 | line-height: inherit; 128 | } 129 | 130 | .form-input-div input { 131 | width: 350px; 132 | height: 42px; 133 | padding-left: 39px; 134 | border: 1px solid #E1E2E6; 135 | } 136 | 137 | .err_msg { 138 | position: relative; 139 | color: #fc4949; 140 | height: 20px; 141 | line-height: 20px; 142 | } 143 | 144 | #form-inputs { 145 | width: 350px; 146 | margin-top: 30px; 147 | text-align: left; 148 | } 149 | 150 | #form-title { 151 | font-size: 40px; 152 | color: #172B4D; 153 | text-align: center; 154 | margin-top: 60px; 155 | } 156 | 157 | .login { 158 | height: 1024px; 159 | background-color: #f7f8fa; 160 | font-family: "PingFang SC", "Helvetica Neue", Helvetica, "Hiragino Sans GB", STHeitiSC-Light, "Microsoft YaHei", "微软雅黑", Arial, sans-serif; 161 | font-size: 13px; 162 | line-height: 1.42857143; 163 | color: #333; 164 | } 165 | -------------------------------------------------------------------------------- /src/pages/monaco-editor/BaseMonacoEditor.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 132 | 133 | 138 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/Hooks.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 135 | 136 | 138 | -------------------------------------------------------------------------------- /src/pages/monaco-editor/util/python-completion.js: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor' 2 | const hints = [ 3 | // This section is the result of running 4 | // `for k in keyword.kwlist: print(' "' + k + '",')` in a Python REPL, 5 | // though note that the output from Python 3 is not a strict superset of the 6 | // output from Python 2. 7 | 'False', // promoted to keyword.kwlist in Python 3 8 | 'None', // promoted to keyword.kwlist in Python 3 9 | 'True', // promoted to keyword.kwlist in Python 3 10 | 'and', 11 | 'as', 12 | 'assert', 13 | 'async', // new in Python 3 14 | 'await', // new in Python 3 15 | 'break', 16 | 'class', 17 | 'continue', 18 | 'def', 19 | 'del', 20 | 'elif', 21 | 'else', 22 | 'except', 23 | 'exec', // Python 2, but not 3. 24 | 'finally', 25 | 'for', 26 | 'from', 27 | 'global', 28 | 'if', 29 | 'import', 30 | 'in', 31 | 'is', 32 | 'lambda', 33 | 'nonlocal', // new in Python 3 34 | 'not', 35 | 'or', 36 | 'pass', 37 | 'print', // Python 2, but not 3. 38 | 'raise', 39 | 'return', 40 | 'try', 41 | 'while', 42 | 'with', 43 | 'yield', 44 | 45 | 'int', 46 | 'float', 47 | 'long', 48 | 'complex', 49 | 'hex', 50 | 51 | 'abs', 52 | 'all', 53 | 'any', 54 | 'apply', 55 | 'basestring', 56 | 'bin', 57 | 'bool', 58 | 'buffer', 59 | 'bytearray', 60 | 'callable', 61 | 'chr', 62 | 'classmethod', 63 | 'cmp', 64 | 'coerce', 65 | 'compile', 66 | 'complex', 67 | 'delattr', 68 | 'dict', 69 | 'dir', 70 | 'divmod', 71 | 'enumerate', 72 | 'eval', 73 | 'execfile', 74 | 'file', 75 | 'filter', 76 | 'format', 77 | 'frozenset', 78 | 'getattr', 79 | 'globals', 80 | 'hasattr', 81 | 'hash', 82 | 'help', 83 | 'id', 84 | 'input', 85 | 'intern', 86 | 'isinstance', 87 | 'issubclass', 88 | 'iter', 89 | 'len', 90 | 'locals', 91 | 'list', 92 | 'map', 93 | 'max', 94 | 'memoryview', 95 | 'min', 96 | 'next', 97 | 'object', 98 | 'oct', 99 | 'open', 100 | 'ord', 101 | 'pow', 102 | 'print', 103 | 'property', 104 | 'reversed', 105 | 'range', 106 | 'raw_input', 107 | 'reduce', 108 | 'reload', 109 | 'repr', 110 | 'reversed', 111 | 'round', 112 | 'self', 113 | 'set', 114 | 'setattr', 115 | 'slice', 116 | 'sorted', 117 | 'staticmethod', 118 | 'str', 119 | 'sum', 120 | 'super', 121 | 'tuple', 122 | 'type', 123 | 'unichr', 124 | 'unicode', 125 | 'vars', 126 | 'xrange', 127 | 'zip', 128 | 129 | '__dict__', 130 | '__methods__', 131 | '__members__', 132 | '__class__', 133 | '__bases__', 134 | '__name__', 135 | '__mro__', 136 | '__subclasses__', 137 | '__init__', 138 | '__import__' 139 | ] 140 | function createCompleter(getExtraHints) { 141 | const createSuggestions = function (model, textUntilPosition) { 142 | let text = model.getValue(); 143 | textUntilPosition = textUntilPosition.replace(/[\*\[\]@\$\(\)]/g, "").replace(/(\s+|\.)/g, " "); 144 | let arr = textUntilPosition.split(/[\s;]/); 145 | let activeStr = arr[arr.length - 1]; 146 | let len = activeStr.length; 147 | let rexp = new RegExp("([^\\w]|^)" + activeStr + "\\w*", "gim"); 148 | let match = text.match(rexp); 149 | let textHints = !match ? [] : 150 | match.map(ele => { 151 | let rexp = new RegExp(activeStr, "gim"); 152 | let search = ele.search(rexp); 153 | return ele.substr(search); 154 | }); 155 | let mergeHints = Array.from(new Set([...hints, ...textHints, ...getExtraHints(model)])) 156 | .sort() 157 | .filter(ele => { 158 | let rexp = new RegExp(ele.substr(0, len), "gim"); 159 | return (match && match.length === 1 && ele === activeStr) || 160 | ele.length === 1 ? false : activeStr.match(rexp); 161 | }); 162 | return mergeHints.map(ele => ({ 163 | label: ele, 164 | kind: hints.indexOf(ele) > -1 ? 165 | monaco.languages.CompletionItemKind.Keyword : 166 | monaco.languages.CompletionItemKind.Text, 167 | documentation: ele, 168 | insertText: ele 169 | })); 170 | }; 171 | return { 172 | provideCompletionItems(model, position) { 173 | let textUntilPosition = model.getValueInRange({ 174 | startLineNumber: position.lineNumber, 175 | startColumn: 1, 176 | endLineNumber: position.lineNumber, 177 | endColumn: position.column 178 | }); 179 | return { suggestions: createSuggestions(model, textUntilPosition) }; 180 | } 181 | } 182 | } 183 | export default createCompleter; 184 | -------------------------------------------------------------------------------- /src/pages/httprunner/DebugTalk.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 148 | 149 | 152 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/Parameters.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 150 | 151 | 153 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from '@/pages/home/Home' 4 | import Register from '@/pages/auth/Register' 5 | import Login from '@/pages/auth/Login' 6 | import ProjectList from '@/pages/project/ProjectList' 7 | import ProjectDetail from '@/pages/project/ProjectDetail' 8 | import DebugTalk from '@/pages/httprunner/DebugTalk' 9 | import RecordApi from '@/pages/fastrunner/api/RecordApi' 10 | import AutoTest from '@/pages/fastrunner/case/AutoTest' 11 | import GlobalEnv from '@/pages/variables/GlobalEnv' 12 | import ReportList from '@/pages/reports/ReportList' 13 | import RecordConfig from '@/pages/fastrunner/config/RecordConfig' 14 | import Tasks from '@/pages/task/Tasks' 15 | import HostAddress from '@/pages/variables/HostAddress' 16 | 17 | Vue.use(Router); 18 | 19 | export default new Router({ 20 | mode: 'history', 21 | routes: [ 22 | { 23 | path: '/fastrunner/register', 24 | name: 'Register', 25 | component: Register, 26 | meta: { 27 | title: '用户注册' 28 | } 29 | }, { 30 | path: '/fastrunner/login', 31 | name: 'Login', 32 | component: Login, 33 | meta: { 34 | title: '用户登录' 35 | } 36 | }, { 37 | 38 | path: '/fastrunner', 39 | name: 'Index', 40 | component: Home, 41 | meta: { 42 | title: '首页', 43 | requireAuth: true 44 | }, 45 | children: [ 46 | { 47 | name: 'ProjectList', 48 | path: 'project_list', 49 | component: ProjectList, 50 | meta: { 51 | title: '项目列表', 52 | requireAuth: true, 53 | } 54 | }, 55 | { 56 | name: 'ProjectDetail', 57 | path: 'project/:id/dashbord', 58 | component: ProjectDetail, 59 | meta: { 60 | title: '项目预览', 61 | requireAuth: true, 62 | } 63 | 64 | }, 65 | { 66 | name: 'DebugTalk', 67 | path: 'debugtalk/:id', 68 | component: DebugTalk, 69 | meta: { 70 | title: '编辑驱动', 71 | requireAuth: true, 72 | } 73 | 74 | }, 75 | { 76 | name: 'RecordApi', 77 | path: 'api_record/:id', 78 | component: RecordApi, 79 | meta: { 80 | title: '接口模板', 81 | requireAuth: true 82 | } 83 | 84 | }, 85 | { 86 | name: 'AutoTest', 87 | path: 'auto_test/:id', 88 | component: AutoTest, 89 | meta: { 90 | title: '自动化测试', 91 | requireAuth: true 92 | } 93 | 94 | }, 95 | { 96 | name: 'RecordConfig', 97 | path: 'record_config/:id', 98 | component: RecordConfig, 99 | meta: { 100 | title: '配置管理', 101 | requireAuth: true 102 | } 103 | 104 | }, 105 | { 106 | name: 'GlobalEnv', 107 | path: 'global_env/:id', 108 | component: GlobalEnv, 109 | meta: { 110 | title: '全局变量', 111 | requireAuth: true 112 | } 113 | 114 | }, 115 | { 116 | name: 'Reports', 117 | path: 'reports/:id', 118 | component: ReportList, 119 | meta: { 120 | title: '历史报告', 121 | requireAuth: true 122 | } 123 | 124 | }, 125 | { 126 | name: 'Task', 127 | path: 'tasks/:id', 128 | component: Tasks, 129 | meta: { 130 | title: '定时任务', 131 | requireAuth: true 132 | } 133 | 134 | }, 135 | { 136 | name: 'HostIP', 137 | path: 'host_ip/:id', 138 | component: HostAddress, 139 | meta: { 140 | title: 'HOST配置', 141 | requireAuth: true 142 | } 143 | 144 | } 145 | ] 146 | }, 147 | 148 | ] 149 | }) 150 | 151 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/Extract.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 164 | 165 | 167 | -------------------------------------------------------------------------------- /src/pages/config/RecordConfig.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | 164 | 165 | 169 | -------------------------------------------------------------------------------- /src/pages/auth/Login.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 135 | 136 | 139 | -------------------------------------------------------------------------------- /src/pages/fastrunner/config/RecordConfig.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 171 | 172 | 176 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/Headers.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 214 | 215 | 217 | -------------------------------------------------------------------------------- /src/pages/auth/Register.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | 90 | 166 | 167 | 170 | -------------------------------------------------------------------------------- /src/pages/fastrunner/config/components/ConfigBody.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | 233 | 234 | 250 | -------------------------------------------------------------------------------- /src/pages/config/components/ConfigBody.vue: -------------------------------------------------------------------------------- 1 | 87 | 88 | 231 | 232 | 248 | -------------------------------------------------------------------------------- /src/pages/config/components/ConfigList.vue: -------------------------------------------------------------------------------- 1 | 103 | 104 | 226 | 227 | 231 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/Variables.vue: -------------------------------------------------------------------------------- 1 | 86 | 87 | 267 | 268 | 271 | -------------------------------------------------------------------------------- /src/pages/fastrunner/config/components/ConfigList.vue: -------------------------------------------------------------------------------- 1 | 129 | 130 | 263 | 264 | 268 | -------------------------------------------------------------------------------- /src/pages/project/ProjectDashBoard.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 293 | 294 | 304 | -------------------------------------------------------------------------------- /src/assets/styles/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "iconfont"; 3 | src: url('./icon-font/iconfont.eot?t=1551323395845'); /* IE9 */ 4 | src: url('./icon-font/iconfont.eot?t=1551323395845#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('./icon-font/iconfont.woff?t=1551323395845') format('woff'), 6 | url('./icon-font/iconfont.ttf?t=1551323395845') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./icon-font/iconfont.svg?t=1551323395845#iconfont') format('svg'); /* iOS 4.1- */ 7 | } 8 | 9 | .iconfont { 10 | font-family: "iconfont" !important; 11 | font-size: 16px; 12 | font-style: normal; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | .icon-youxiang:before { 18 | content: "\e668"; 19 | } 20 | 21 | .icon-baogao:before { 22 | content: "\e66e"; 23 | } 24 | 25 | .icon-ybbindex:before { 26 | content: "\e631"; 27 | } 28 | 29 | .icon-02:before { 30 | content: "\e64a"; 31 | } 32 | 33 | .icon-iconset0196:before { 34 | content: "\e65f"; 35 | } 36 | 37 | .icon-17:before { 38 | content: "\e610"; 39 | } 40 | 41 | .icon-xiazai:before { 42 | content: "\e600"; 43 | } 44 | 45 | .icon-index:before { 46 | content: "\e66d"; 47 | } 48 | 49 | .icon-fuzhifuzhi:before { 50 | content: "\e636"; 51 | } 52 | 53 | .icon-debug:before { 54 | content: "\e606"; 55 | } 56 | 57 | .icon-shijian:before { 58 | content: "\e63b"; 59 | } 60 | 61 | .icon-language-python-text:before { 62 | content: "\e7ca"; 63 | } 64 | 65 | .icon-xiangmu:before { 66 | content: "\e63c"; 67 | } 68 | 69 | .icon-jiankong:before { 70 | content: "\e61f"; 71 | } 72 | 73 | .icon-houtui:before { 74 | content: "\e617"; 75 | } 76 | 77 | .icon-dingshirenwu:before { 78 | content: "\e61e"; 79 | } 80 | 81 | .icon-xiangmu1:before { 82 | content: "\e707"; 83 | } 84 | 85 | .icon-ceshi:before { 86 | content: "\e6da"; 87 | } 88 | 89 | .icon-mima:before { 90 | content: "\e652"; 91 | } 92 | 93 | .icon-quanju_yuming:before { 94 | content: "\e609"; 95 | } 96 | 97 | .icon-jiekou:before { 98 | content: "\e74a"; 99 | } 100 | 101 | .icon-shujuku:before { 102 | content: "\e615"; 103 | } 104 | 105 | .icon-yali:before { 106 | content: "\e632"; 107 | } 108 | 109 | .icon-xingmingyonghumingnicheng:before { 110 | content: "\e61c"; 111 | } 112 | 113 | .icon-xiugai:before { 114 | content: "\e67d"; 115 | } 116 | 117 | .icon-houtui1:before { 118 | content: "\e66f"; 119 | } 120 | 121 | .icon-yumingguanli:before { 122 | content: "\e6cc"; 123 | } 124 | 125 | .icon-yonghuming:before { 126 | content: "\e60d"; 127 | } 128 | 129 | .icon-houtui2:before { 130 | content: "\e613"; 131 | } 132 | 133 | .icon-171:before { 134 | content: "\e601"; 135 | } 136 | 137 | .icon-bendibianliang:before { 138 | content: "\e692"; 139 | } 140 | 141 | .icon-youxiang1:before { 142 | content: "\e650"; 143 | } 144 | 145 | .icon-config:before { 146 | content: "\ee32"; 147 | } 148 | 149 | .icon-fuzhi:before { 150 | content: "\e63d"; 151 | } 152 | 153 | .icon-shujuku1:before { 154 | content: "\e782"; 155 | } 156 | 157 | .icon-xiangmuliebiao:before { 158 | content: "\e7a7"; 159 | } 160 | 161 | .icon-python:before { 162 | content: "\f216"; 163 | } 164 | 165 | .icon-yunhang:before { 166 | content: "\e616"; 167 | } 168 | 169 | .icon-shanchu:before { 170 | content: "\e608"; 171 | } 172 | 173 | .icon-xiazai1:before { 174 | content: "\e602"; 175 | } 176 | 177 | -------------------------------------------------------------------------------- /src/pages/httprunner/components/Validate.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 355 | 356 | 358 | -------------------------------------------------------------------------------- /src/pages/fastrunner/case/components/TestBody.vue: -------------------------------------------------------------------------------- 1 | 169 | 170 | 355 | 356 | 372 | -------------------------------------------------------------------------------- /src/pages/variables/HostAddress.vue: -------------------------------------------------------------------------------- 1 | 179 | 180 | 321 | 322 | 326 | -------------------------------------------------------------------------------- /src/restful/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from '../store/state' 3 | import router from '../router' 4 | import {Message} from 'element-ui'; 5 | 6 | 7 | export let baseUrl = "http://localhost:8000"; 8 | // export let baseUrl = "http://192.168.22.19:8000"; 9 | 10 | if( process.env.NODE_ENV === "production" ){ 11 | baseUrl = "http://192.168.22.19:8000"; 12 | } 13 | 14 | // export const baseUrl = "http://10.0.3.57:8000"; 15 | axios.defaults.withCredentials = true; 16 | axios.defaults.baseURL = baseUrl; 17 | 18 | axios.interceptors.request.use(function (config) { 19 | if (!config.url.startsWith("/api/user/") || config.url === "/api/user/list/") { 20 | // 在请求拦截中,每次请求,都会加上一个Authorization头 21 | config.headers.Authorization = store.token; 22 | 23 | // 取url地址的第四位作为projectId,如果不存在,默认设置为0 24 | let projectId = window.location.pathname.split("/")[3] 25 | projectId = projectId ? projectId : 0 26 | config.headers['Project'] = projectId 27 | } 28 | return config; 29 | }, function (error) { 30 | return Promise.reject(error); 31 | }); 32 | 33 | axios.interceptors.response.use(function (response) { 34 | 35 | return response; 36 | }, function (error) { 37 | try { 38 | if (error.response.status === 401) { 39 | router.replace({ 40 | name: 'Login' 41 | }) 42 | } 43 | 44 | if (error.response.status === 500) { 45 | Message.error({ 46 | message: '服务器内部异常, 请检查', 47 | duration: this.$store.state.duration 48 | }) 49 | } 50 | if (error.response.status === 403) { 51 | Message.error({ 52 | message: error.response.data.msg, 53 | duration: this.$store.state.duration 54 | }) 55 | } 56 | } catch (e) { 57 | Message.error({ 58 | message: '服务器连接超时,请重试', 59 | duration: this.$store.state.duration 60 | }) 61 | } 62 | }); 63 | 64 | // user api 65 | export const register = params => { 66 | return axios.post('/api/user/register/', params).then(res => res.data) 67 | }; 68 | 69 | export const login = params => { 70 | return axios.post('/api/user/login/', params).then(res => res.data) 71 | }; 72 | 73 | export const getUserList = () => { 74 | return axios.get('/api/user/list/').then(res => res.data) 75 | }; 76 | 77 | 78 | // fastrunner api 79 | export const addProject = params => { 80 | return axios.post('/api/fastrunner/project/', params).then(res => res.data) 81 | }; 82 | 83 | export const deleteProject = config => { 84 | return axios.delete('/api/fastrunner/project/', config).then(res => res.data) 85 | }; 86 | 87 | export const getProjectList = params => { 88 | return axios.get('/api/fastrunner/project/').then(res => res.data) 89 | }; 90 | 91 | export const getProjectDetail = pk => { 92 | return axios.get('/api/fastrunner/project/' + pk + '/').then(res => res.data) 93 | }; 94 | 95 | export const getDashBoard = () => { 96 | return axios.get('/api/fastrunner/dashboard/').then(res => res.data) 97 | }; 98 | 99 | export const getProjectYapiInfo = pk => { 100 | return axios.get('/api/fastrunner/project/yapi/' + pk + '/').then(res => res.data) 101 | }; 102 | 103 | export const getVisit = params => { 104 | return axios.get('/api/fastrunner/visit/', params).then(res => res.data) 105 | }; 106 | 107 | export const getPagination = url => { 108 | return axios.get(url).then(res => res.data) 109 | }; 110 | 111 | export const updateProject = params => { 112 | return axios.patch('/api/fastrunner/project/', params).then(res => res.data) 113 | }; 114 | 115 | export const getDebugtalk = url => { 116 | return axios.get('/api/fastrunner/debugtalk/' + url + '/').then(res => res.data) 117 | }; 118 | 119 | export const updateDebugtalk = params => { 120 | return axios.patch('/api/fastrunner/debugtalk/', params).then(res => res.data) 121 | }; 122 | 123 | export const runDebugtalk = params => { 124 | return axios.post('/api/fastrunner/debugtalk/', params).then(res => res.data) 125 | }; 126 | 127 | export const getTree = (url, params) => { 128 | return axios.get('/api/fastrunner/tree/' + url + '/', params).then(res => res.data) 129 | }; 130 | 131 | export const updateTree = (url, params) => { 132 | return axios.patch('/api/fastrunner/tree/' + url + '/', params).then(res => res.data) 133 | }; 134 | 135 | export const uploadFile = url => { 136 | return baseUrl + '/api/fastrunner/file/?token=' + store.token 137 | }; 138 | 139 | export const addAPI = params => { 140 | return axios.post('/api/fastrunner/api/', params).then(res => res.data) 141 | }; 142 | 143 | export const updateAPI = (url, params) => { 144 | return axios.patch('/api/fastrunner/api/' + url + '/', params).then(res => res.data) 145 | }; 146 | 147 | export const addYAPI = (project_id) => { 148 | return axios.post('/api/fastrunner/yapi/' + project_id + '/').then(res => res.data) 149 | }; 150 | 151 | export const apiList = params => { 152 | return axios.get('/api/fastrunner/api/', params).then(res => res.data) 153 | }; 154 | 155 | export const delAPI = url => { 156 | return axios.delete('/api/fastrunner/api/' + url + '/').then(res => res.data) 157 | }; 158 | 159 | export const tagAPI = params => { 160 | return axios.patch('/api/fastrunner/api/tag/', params).then(res => res.data) 161 | }; 162 | 163 | export const syncCaseStep = (url, params) => { 164 | return axios.patch('/api/fastrunner/api/sync/' + url + '/').then(res => res.data) 165 | }; 166 | 167 | export const delAllAPI = params => { 168 | return axios.delete('/api/fastrunner/api/', params).then(res => res.data) 169 | }; 170 | 171 | export const getAPISingle = url => { 172 | return axios.get('/api/fastrunner/api/' + url + '/').then(res => res.data) 173 | }; 174 | 175 | 176 | export const getPaginationBypage = params => { 177 | return axios.get('/api/fastrunner/api/', params).then(res => res.data) 178 | }; 179 | 180 | 181 | export const addTestCase = params => { 182 | return axios.post('/api/fastrunner/test/', params).then(res => res.data) 183 | }; 184 | 185 | export const updateTestCase = (url, params) => { 186 | return axios.patch('/api/fastrunner/test/' + url + '/', params).then(res => res.data) 187 | }; 188 | 189 | export const testList = params => { 190 | return axios.get('/api/fastrunner/test/', params).then(res => res.data) 191 | }; 192 | 193 | export const deleteTest = url => { 194 | return axios.delete('/api/fastrunner/test/' + url + '/').then(res => res.data) 195 | }; 196 | 197 | export const syncTest = url => { 198 | return axios.put('/api/fastrunner/test/' + url + '/').then(res => res.data) 199 | }; 200 | export const delAllTest = params => { 201 | return axios.delete('/api/fastrunner/test/', params).then(res => res.data) 202 | }; 203 | 204 | export const coptTest = (url, params) => { 205 | return axios.post('/api/fastrunner/test/' + url + '/', params).then(res => res.data) 206 | }; 207 | 208 | export const editTest = url => { 209 | return axios.get('/api/fastrunner/teststep/' + url + '/').then(res => res.data) 210 | }; 211 | 212 | export const getTestPaginationBypage = params => { 213 | return axios.get('/api/fastrunner/test/', params).then(res => res.data) 214 | }; 215 | 216 | export const addConfig = params => { 217 | return axios.post('/api/fastrunner/config/', params).then(res => res.data) 218 | }; 219 | 220 | export const updateConfig = (url, params) => { 221 | return axios.patch('/api/fastrunner/config/' + url + '/', params).then(res => res.data) 222 | }; 223 | 224 | 225 | export const configList = params => { 226 | return axios.get('/api/fastrunner/config/', params).then(res => res.data) 227 | }; 228 | 229 | export const copyConfig = (url, params) => { 230 | return axios.post('/api/fastrunner/config/' + url + '/', params).then(res => res.data) 231 | }; 232 | 233 | export const copyAPI = (url, params) => { 234 | return axios.post('/api/fastrunner/api/' + url + '/', params).then(res => res.data) 235 | }; 236 | 237 | export const deleteConfig = url => { 238 | return axios.delete('/api/fastrunner/config/' + url + '/').then(res => res.data) 239 | }; 240 | export const delAllConfig = params => { 241 | return axios.delete('/api/fastrunner/config/', params).then(res => res.data) 242 | }; 243 | 244 | export const getConfigPaginationBypage = params => { 245 | return axios.get('/api/fastrunner/config/', params).then(res => res.data) 246 | }; 247 | 248 | export const getAllConfig = url => { 249 | return axios.get('/api/fastrunner/config/' + url + '/').then(res => res.data) 250 | }; 251 | 252 | 253 | export const runSingleAPI = params => { 254 | return axios.post('/api/fastrunner/run_api/', params).then(res => res.data) 255 | }; 256 | 257 | export const runAPIByPk = (url, params) => { 258 | return axios.get('/api/fastrunner/run_api_pk/' + url + '/', params).then(res => res.data) 259 | }; 260 | 261 | export const runAPITree = params => { 262 | return axios.post('/api/fastrunner/run_api_tree/', params).then(res => res.data) 263 | }; 264 | 265 | export const moveAPI = params => { 266 | return axios.patch('/api/fastrunner/api/move_api/', params).then(res => res.data) 267 | }; 268 | 269 | export const moveCase = params => { 270 | return axios.patch('/api/fastrunner/test/move_case/', params).then(res => res.data) 271 | }; 272 | 273 | export const tagCase = params => { 274 | return axios.patch('/api/fastrunner/test/tag/', params).then(res => res.data) 275 | }; 276 | 277 | export const runSingleTestSuite = params => { 278 | return axios.post('/api/fastrunner/run_testsuite/', params).then(res => res.data) 279 | }; 280 | 281 | export const runSingleTest = params => { 282 | return axios.post('/api/fastrunner/run_test/', params).then(res => res.data) 283 | }; 284 | 285 | export const runMultiTest = params => { 286 | return axios.post('/api/fastrunner/run_multi_tests/', params).then(res => res.data) 287 | }; 288 | 289 | export const runTestByPk = (url, params) => { 290 | return axios.get('/api/fastrunner/run_testsuite_pk/' + url + '/', params).then(res => res.data) 291 | }; 292 | 293 | export const runSuiteTree = params => { 294 | return axios.post('/api/fastrunner/run_suite_tree/', params).then(res => res.data) 295 | }; 296 | 297 | export const addVariables = params => { 298 | return axios.post('/api/fastrunner/variables/', params).then(res => res.data) 299 | }; 300 | 301 | export const variablesList = params => { 302 | return axios.get('/api/fastrunner/variables/', params).then(res => res.data) 303 | }; 304 | 305 | export const getVariablesPaginationBypage = params => { 306 | return axios.get('/api/fastrunner/variables/', params).then(res => res.data) 307 | }; 308 | 309 | 310 | export const updateVariables = (url, params) => { 311 | return axios.patch('/api/fastrunner/variables/' + url + '/', params).then(res => res.data) 312 | }; 313 | 314 | export const updateTask = (url, params, data) => { 315 | return axios({url: '/api/fastrunner/schedule/' + url + '/', method: 'PUT', params: params, data: data}).then(res => res.data) 316 | }; 317 | 318 | export const patchTask = (url, params) => { 319 | return axios.patch('/api/fastrunner/schedule/' + url + '/', params).then(res => res.data) 320 | }; 321 | 322 | export const deleteVariables = url => { 323 | return axios.delete('/api/fastrunner/variables/' + url + '/').then(res => res.data) 324 | }; 325 | 326 | export const delAllVariabels = params => { 327 | return axios.delete('/api/fastrunner/variables/', params).then(res => res.data) 328 | }; 329 | 330 | export const reportList = params => { 331 | return axios.get('/api/fastrunner/reports/', params).then(res => res.data) 332 | }; 333 | 334 | export const deleteReports = url => { 335 | return axios.delete('/api/fastrunner/reports/' + url + '/').then(res => res.data) 336 | }; 337 | 338 | export const getReportsPaginationBypage = params => { 339 | return axios.get('/api/fastrunner/reports/', params).then(res => res.data) 340 | }; 341 | 342 | export const delAllReports = params => { 343 | return axios.delete('/api/fastrunner/reports/', params).then(res => res.data) 344 | }; 345 | 346 | export const watchSingleReports = url => { 347 | return axios.get('/api/fastrunner/reports/' + url + '/').then(res => res.data) 348 | }; 349 | 350 | export const addTask = params => { 351 | return axios.post('/api/fastrunner/schedule/', params).then(res => res.data) 352 | }; 353 | 354 | export const copyTask = (task_id, params) => { 355 | return axios.post('/api/fastrunner/schedule/' + task_id + '/', params).then(res => res.data) 356 | }; 357 | export const taskList = params => { 358 | return axios.get('/api/fastrunner/schedule/', params).then(res => res.data) 359 | }; 360 | export const getTaskPaginationBypage = params => { 361 | return axios.get('/api/fastrunner/schedule/', params).then(res => res.data) 362 | }; 363 | export const deleteTasks = url => { 364 | return axios.delete('/api/fastrunner/schedule/' + url + '/').then(res => res.data) 365 | }; 366 | 367 | export const runTask = url => { 368 | return axios.get('/api/fastrunner/schedule/' + url + '/').then(res => res.data) 369 | }; 370 | 371 | export const addHostIP = params => { 372 | return axios.post('/api/fastrunner/host_ip/', params).then(res => res.data) 373 | }; 374 | 375 | export const hostList = params => { 376 | return axios.get('/api/fastrunner/host_ip/', params).then(res => res.data) 377 | }; 378 | 379 | export const updateHost = (url, params) => { 380 | return axios.patch('/api/fastrunner/host_ip/' + url + '/', params).then(res => res.data) 381 | }; 382 | 383 | export const deleteHost = url => { 384 | return axios.delete('/api/fastrunner/host_ip/' + url + '/').then(res => res.data) 385 | }; 386 | 387 | export const getHostPaginationBypage = params => { 388 | return axios.get('/api/fastrunner/host_ip/', params).then(res => res.data) 389 | }; 390 | 391 | export const getAllHost = url => { 392 | return axios.get('/api/fastrunner/host_ip/' + url + '/').then(res => res.data) 393 | }; 394 | 395 | --------------------------------------------------------------------------------