Quellcode durchsuchen

Merge branch 'dev_test' into dev

Jinyao vor 5 Jahren
Ursprung
Commit
932aedb9c7

+ 381 - 8
package-lock.json

@@ -2430,6 +2430,13 @@
       "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
       "dev": true
     },
+    "@types/parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+      "dev": true,
+      "optional": true
+    },
     "@types/q": {
       "version": "1.5.4",
       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
@@ -2533,6 +2540,8 @@
     },
     "@typescript-eslint/eslint-plugin": {
       "version": "2.34.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz",
+      "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==",
       "dev": true,
       "requires": {
         "@typescript-eslint/experimental-utils": "2.34.0",
@@ -2563,6 +2572,8 @@
     },
     "@typescript-eslint/parser": {
       "version": "2.34.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz",
+      "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==",
       "dev": true,
       "requires": {
         "@types/eslint-visitor-keys": "^1.0.0",
@@ -2817,6 +2828,8 @@
     },
     "@vue/cli-plugin-babel": {
       "version": "4.5.11",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.11.tgz",
+      "integrity": "sha512-ogUMeO2waDtghIWwmuAzMJAnnPdmqRdJlwJDca9u6BK9jX1bxNThBSFS/MN2VmlYzulOnqH4zAC87jTWNg/czg==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.11.0",
@@ -2830,6 +2843,8 @@
     },
     "@vue/cli-plugin-eslint": {
       "version": "4.5.11",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.11.tgz",
+      "integrity": "sha512-6XrF3A3ryjtqoPMYL0ltZaP0631HS2a68Ye34KIkz111EKXtC5ip+gz6bSPWrH5SbhinU3R8cstA8xVASz9kwg==",
       "dev": true,
       "requires": {
         "@vue/cli-shared-utils": "^4.5.11",
@@ -2851,12 +2866,15 @@
     },
     "@vue/cli-plugin-typescript": {
       "version": "4.5.11",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-typescript/-/cli-plugin-typescript-4.5.11.tgz",
+      "integrity": "sha512-oVv4p/gec/xqFuJOUqBxVuThk7fj2QMfoDpe6QfkWIVQU+g8JLpZvOQo0wDMoiHtURQKtqGQCwC57jkKOCufqg==",
       "dev": true,
       "requires": {
         "@types/webpack-env": "^1.15.2",
         "@vue/cli-shared-utils": "^4.5.11",
         "cache-loader": "^4.1.0",
         "fork-ts-checker-webpack-plugin": "^3.1.1",
+        "fork-ts-checker-webpack-plugin-v5": "npm:fork-ts-checker-webpack-plugin@^5.0.11",
         "globby": "^9.2.0",
         "thread-loader": "^2.1.3",
         "ts-loader": "^6.2.2",
@@ -2873,6 +2891,8 @@
     },
     "@vue/cli-service": {
       "version": "4.5.11",
+      "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-4.5.11.tgz",
+      "integrity": "sha512-FXeJh2o6B8q/njv2Ebhe9EsLXt9sPMXGDY5zVvcV5jgj9wkoej9yLfnmwWCau5kegNClP6bcM+BEHuMYxJ+ubQ==",
       "dev": true,
       "requires": {
         "@intervolga/optimize-cssnano-plugin": "^1.0.5",
@@ -2924,6 +2944,7 @@
         "thread-loader": "^2.1.3",
         "url-loader": "^2.2.0",
         "vue-loader": "^15.9.2",
+        "vue-loader-v16": "npm:vue-loader@^16.1.0",
         "vue-style-loader": "^4.1.2",
         "webpack": "^4.0.0",
         "webpack-bundle-analyzer": "^3.8.0",
@@ -3018,6 +3039,8 @@
     },
     "@vue/compiler-sfc": {
       "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.0.5.tgz",
+      "integrity": "sha512-uOAC4X0Gx3SQ9YvDC7YMpbDvoCmPvP0afVhJoxRotDdJ+r8VO3q4hFf/2f7U62k4Vkdftp6DVni8QixrfYzs+w==",
       "dev": true,
       "requires": {
         "@babel/parser": "^7.12.0",
@@ -3108,6 +3131,8 @@
     },
     "@vue/eslint-config-airbnb": {
       "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/@vue/eslint-config-airbnb/-/eslint-config-airbnb-5.3.0.tgz",
+      "integrity": "sha512-m9ldRhbqaODbcc9mQZjPgnTzyNweZblLMTqMfC2kHWY68dYd3kwG/hvENeZWXJnKKo+eGnoptk+7Zq/c1519ZQ==",
       "dev": true,
       "requires": {
         "eslint-config-airbnb-base": "^14.0.0",
@@ -3118,6 +3143,8 @@
     },
     "@vue/eslint-config-typescript": {
       "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-5.1.0.tgz",
+      "integrity": "sha512-wFAdPMWegKZOdbQBEWV4/KbOKuX/6Q5db3304kiWNBK+6P7+CoMrsbaKzJFjuAZF7fQR2fJtZT9ciGWVVT//vw==",
       "dev": true,
       "requires": {
         "vue-eslint-parser": "^7.0.0"
@@ -3704,6 +3731,13 @@
       "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
       "dev": true
     },
+    "at-least-node": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+      "dev": true,
+      "optional": true
+    },
     "atob": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
@@ -3739,6 +3773,8 @@
     },
     "axios": {
       "version": "0.21.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
+      "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
       "requires": {
         "follow-redirects": "^1.10.0"
       }
@@ -3804,6 +3840,8 @@
     },
     "babel-plugin-component": {
       "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/babel-plugin-component/-/babel-plugin-component-1.1.1.tgz",
+      "integrity": "sha512-WUw887kJf2GH80Ng/ZMctKZ511iamHNqPhd9uKo14yzisvV7Wt1EckIrb8oq/uCz3B3PpAW7Xfl7AkTLDYT6ag==",
       "dev": true,
       "requires": {
         "@babel/helper-module-imports": "7.0.0-beta.35"
@@ -3843,6 +3881,8 @@
     },
     "babel-plugin-import": {
       "version": "1.13.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-import/-/babel-plugin-import-1.13.3.tgz",
+      "integrity": "sha512-1qCWdljJOrDRH/ybaCZuDgySii4yYrtQ8OJQwrcDqdt0y67N30ng3X3nABg6j7gR7qUJgcMa9OMhc4AGViDwWw==",
       "dev": true,
       "requires": {
         "@babel/helper-module-imports": "^7.0.0",
@@ -4004,6 +4044,16 @@
       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
       "dev": true
     },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
     "bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -6119,6 +6169,8 @@
     },
     "element-plus": {
       "version": "1.0.2-beta.32",
+      "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-1.0.2-beta.32.tgz",
+      "integrity": "sha512-UzRHAELlnz6aqAi5y4OyNspQvrgspAcK4dxSR3chJXmzXZIvG2tqAwu2Whgte+Cd9fptpFYPIdrfVdznwK7zCA==",
       "requires": {
         "@popperjs/core": "^2.4.4",
         "async-validator": "^3.4.0",
@@ -6295,6 +6347,8 @@
     },
     "eslint": {
       "version": "6.8.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+      "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
@@ -6539,6 +6593,8 @@
     },
     "eslint-plugin-vue": {
       "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.5.0.tgz",
+      "integrity": "sha512-QnMMTcyV8PLxBz7QQNAwISSEs6LYk2LJvGlxalXvpCtfKnqo7qcY0aZTIxPe8QOnHd7WCwiMZLOJzg6A03T0Gw==",
       "dev": true,
       "requires": {
         "eslint-utils": "^2.1.0",
@@ -7011,6 +7067,13 @@
         "schema-utils": "^2.5.0"
       }
     },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "dev": true,
+      "optional": true
+    },
     "filesize": {
       "version": "3.6.1",
       "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
@@ -7197,6 +7260,160 @@
         "worker-rpc": "^0.1.0"
       }
     },
+    "fork-ts-checker-webpack-plugin-v5": {
+      "version": "npm:fork-ts-checker-webpack-plugin@5.2.1",
+      "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz",
+      "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "@babel/code-frame": "^7.8.3",
+        "@types/json-schema": "^7.0.5",
+        "chalk": "^4.1.0",
+        "cosmiconfig": "^6.0.0",
+        "deepmerge": "^4.2.2",
+        "fs-extra": "^9.0.0",
+        "memfs": "^3.1.2",
+        "minimatch": "^3.0.4",
+        "schema-utils": "2.7.0",
+        "semver": "^7.3.2",
+        "tapable": "^1.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "cosmiconfig": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+          "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "@types/parse-json": "^4.0.0",
+            "import-fresh": "^3.1.0",
+            "parse-json": "^5.0.0",
+            "path-type": "^4.0.0",
+            "yaml": "^1.7.2"
+          }
+        },
+        "deepmerge": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+          "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+          "dev": true,
+          "optional": true
+        },
+        "fs-extra": {
+          "version": "9.1.0",
+          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+          "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "at-least-node": "^1.0.0",
+            "graceful-fs": "^4.2.0",
+            "jsonfile": "^6.0.1",
+            "universalify": "^2.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true,
+          "optional": true
+        },
+        "jsonfile": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+          "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "graceful-fs": "^4.1.6",
+            "universalify": "^2.0.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "schema-utils": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
+          "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "@types/json-schema": "^7.0.4",
+            "ajv": "^6.12.2",
+            "ajv-keywords": "^3.4.1"
+          }
+        },
+        "semver": {
+          "version": "7.3.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+          "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "universalify": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+          "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+          "dev": true,
+          "optional": true
+        }
+      }
+    },
     "form-data": {
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@@ -7259,6 +7476,13 @@
         "minipass": "^3.0.0"
       }
     },
+    "fs-monkey": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz",
+      "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==",
+      "dev": true,
+      "optional": true
+    },
     "fs-write-stream-atomic": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
@@ -8666,6 +8890,8 @@
     },
     "lint-staged": {
       "version": "9.5.0",
+      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-9.5.0.tgz",
+      "integrity": "sha512-nawMob9cb/G1J98nb8v3VC/E8rcX1rryUYXVZ69aT9kde6YWX+uvNOEHY5yf2gcWcTJGiD0kqXmCnS3oD75GIA==",
       "dev": true,
       "requires": {
         "chalk": "^2.4.2",
@@ -9256,6 +9482,16 @@
       "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
       "dev": true
     },
+    "memfs": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz",
+      "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "fs-monkey": "1.0.1"
+      }
+    },
     "memory-fs": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -9526,6 +9762,8 @@
     },
     "mockjs": {
       "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/mockjs/-/mockjs-1.1.0.tgz",
+      "integrity": "sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==",
       "dev": true,
       "requires": {
         "commander": "*"
@@ -9592,6 +9830,13 @@
         "thenify-all": "^1.0.0"
       }
     },
+    "nan": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+      "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
+      "dev": true,
+      "optional": true
+    },
     "nanomatch": {
       "version": "1.2.13",
       "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -11653,6 +11898,8 @@
     },
     "sass": {
       "version": "1.32.5",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.5.tgz",
+      "integrity": "sha512-kU1yJ5zUAmPxr7f3q0YXTAd1oZjSR1g3tYyv+xu0HZSl5JiNOaE987eiz7wCUvbm4I9fGWGU2TgApTtcP4GMNQ==",
       "dev": true,
       "requires": {
         "chokidar": ">=2.0.0 <4.0.0"
@@ -11660,6 +11907,8 @@
     },
     "sass-loader": {
       "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz",
+      "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==",
       "dev": true,
       "requires": {
         "clone-deep": "^4.0.1",
@@ -13025,6 +13274,8 @@
     },
     "typescript": {
       "version": "3.9.7",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
+      "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
       "dev": true
     },
     "uglify-js": {
@@ -13366,6 +13617,8 @@
     },
     "vue": {
       "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.0.5.tgz",
+      "integrity": "sha512-TfaprOmtsAfhQau7WsomXZ8d9op/dkQLNIq8qPV3A0Vxs6GR5E+c1rfJS1SDkXRQj+dFyfnec7+U0Be1huiScg==",
       "requires": {
         "@vue/compiler-dom": "3.0.5",
         "@vue/runtime-dom": "3.0.5",
@@ -13373,10 +13626,14 @@
       }
     },
     "vue-axios": {
-      "version": "3.2.4"
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-3.2.4.tgz",
+      "integrity": "sha512-8Dgws6dwt+1LoKEU4HDzIPfaRgWHl0xLDLW6kxrAk/z3GAJ2thkajcXC4qZgmqj0cZRx/Z/dzooKi5HWtUQwxA=="
     },
     "vue-class-component": {
-      "version": "8.0.0-rc.1"
+      "version": "8.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-8.0.0-rc.1.tgz",
+      "integrity": "sha512-w1nMzsT/UdbDAXKqhwTmSoyuJzUXKrxLE77PCFVuC6syr8acdFDAq116xgvZh9UCuV0h+rlCtxXolr3Hi3HyPQ=="
     },
     "vue-eslint-parser": {
       "version": "7.4.1",
@@ -13415,6 +13672,21 @@
       "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
       "dev": true
     },
+    "vue-json-editor": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/vue-json-editor/-/vue-json-editor-1.4.3.tgz",
+      "integrity": "sha512-st9HdXBgCnyEmmfWrZQiKzp4KuYXzmYVUNDn5h6Fa18MrrGS1amnyUFyv7hQFsNBDW27B7BKkdGOqszYT1srAg==",
+      "requires": {
+        "vue": "^2.2.6"
+      },
+      "dependencies": {
+        "vue": {
+          "version": "2.6.12",
+          "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
+          "integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
+        }
+      }
+    },
     "vue-loader": {
       "version": "15.9.6",
       "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.6.tgz",
@@ -13436,8 +13708,94 @@
         }
       }
     },
+    "vue-loader-v16": {
+      "version": "npm:vue-loader@16.1.2",
+      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz",
+      "integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "chalk": "^4.1.0",
+        "hash-sum": "^2.0.0",
+        "loader-utils": "^2.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true,
+          "optional": true
+        },
+        "json5": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
+          "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "minimist": "^1.2.5"
+          }
+        },
+        "loader-utils": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
+          "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^2.1.2"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
     "vue-router": {
-      "version": "4.0.3"
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.3.tgz",
+      "integrity": "sha512-AD1OjtVPyQHTSpoRsEGfPpxRQwhAhxcacOYO3zJ3KNkYP/r09mileSp6kdMQKhZWP2cFsPR3E2M3PZguSN5/ww=="
     },
     "vue-style-loader": {
       "version": "4.1.2",
@@ -13464,9 +13822,9 @@
       "dev": true
     },
     "vuex": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
-      "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw=="
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.0.tgz",
+      "integrity": "sha512-56VPujlHscP5q/e7Jlpqc40sja4vOhC4uJD1llBCWolVI8ND4+VzisDVkUMl+z5y0MpIImW6HjhNc+ZvuizgOw=="
     },
     "watchpack": {
       "version": "1.7.5",
@@ -13546,7 +13904,11 @@
           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
           "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
           "dev": true,
-          "optional": true
+          "optional": true,
+          "requires": {
+            "bindings": "^1.5.0",
+            "nan": "^2.12.1"
+          }
         },
         "is-binary-path": {
           "version": "1.0.1",
@@ -13898,7 +14260,11 @@
           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
           "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
           "dev": true,
-          "optional": true
+          "optional": true,
+          "requires": {
+            "bindings": "^1.5.0",
+            "nan": "^2.12.1"
+          }
         },
         "globby": {
           "version": "6.1.0",
@@ -14248,6 +14614,13 @@
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
       "dev": true
     },
+    "yaml": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "dev": true,
+      "optional": true
+    },
     "yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",

+ 1 - 0
package.json

@@ -18,6 +18,7 @@
     "vue": "^3.0.0",
     "vue-axios": "^3.2.4",
     "vue-class-component": "^8.0.0-0",
+    "vue-json-editor": "^1.4.3",
     "vue-router": "^4.0.0-0",
     "vuex": "^4.0.0"
   },

+ 5 - 0
src/api/devcloud/apitest.ts

@@ -0,0 +1,5 @@
+import axios from '@/utils/axios';
+
+import { APITEST_PREFIX } from './prefix';
+
+export const getAPITestInfosByProjectId = (projectId: number) => axios.get(`${APITEST_PREFIX}/list/${projectId}`);

+ 1 - 0
src/api/devcloud/prefix.ts

@@ -5,3 +5,4 @@ export const TREE_PREFIX = '/tree';
 export const PROJECT_PREFIX = '/project';
 export const GROUP_PREFIX = '/groups';
 export const BUGLIST_PREFIX = '/bug_list';
+export const APITEST_PREFIX = '/test';

+ 2 - 0
src/api/index.ts

@@ -3,6 +3,7 @@ import * as ProjectAPI from './devcloud/project';
 import * as RequirementAPI from './devcloud/tree';
 import * as BugListAPI from './devcloud/buglist';
 import * as PipelineAPI from './devcloud/pipeline';
+import * as APITestAPI from './devcloud/apitest';
 import * as PortalAPI from './portal';
 
 export {
@@ -12,4 +13,5 @@ export {
   BugListAPI,
   PipelineAPI,
   PortalAPI,
+  APITestAPI,
 };

+ 120 - 0
src/components/APITestInfoForm.vue

@@ -0,0 +1,120 @@
+<template>
+  <right-form ref="rightForm">
+    <div class="APITestInfoForm">
+      <div class="title">测试任务详情</div>
+      <div class="marginTop">
+        <ElRow>
+          <ElCol :span="3">
+            <div class="key">url:</div>
+          </ElCol>
+          <ElCol :span="12" :offset="2">
+            <div class="value">{{info.url}}</div>
+          </ElCol>
+        </ElRow>
+      </div>
+      <div class="marginTop">
+        <ElRow>
+          <ElCol :span="3">
+            <div class="key">params:</div>
+          </ElCol>
+          <ElCol :span="12" :offset="2">
+            <div>
+              <vue-json-editor
+                :height="'400px'"
+                :value="info.params"
+                :show-btns="false"
+                :mode="'code'"
+                :modes="['code']"
+                lang="zh"
+              ></vue-json-editor>
+            </div>
+          </ElCol>
+        </ElRow>
+      </div>
+      <div class="marginTop">
+        <ElRow>
+          <ElCol :span="3">
+            <div class="key">并发数:</div>
+          </ElCol>
+          <ElCol :span="12" :offset="2">
+            <div class="value">{{info.qps}}</div>
+          </ElCol>
+        </ElRow>
+      </div>
+      <div class="title">测试结果</div>
+      <div v-if="info.success === 0 && info.failure === 0">
+        还未运行!
+      </div>
+      <div v-else>
+        <div class="marginTop">
+          <ElRow>
+            <ElCol :span="3">
+              <div class="key">成功:</div>
+            </ElCol>
+            <ElCol :span="12" :offset="2">
+              <div class="value">{{info.success}}</div>
+            </ElCol>
+          </ElRow>
+        </div>
+        <div class="marginTop">
+          <ElRow>
+            <ElCol :span="3">
+              <div class="key">失败:</div>
+            </ElCol>
+            <ElCol :span="12" :offset="2">
+              <div class="value">{{info.failure}}</div>
+            </ElCol>
+          </ElRow>
+        </div>
+      </div>
+    </div>
+  </right-form>
+</template>
+
+<script>
+import vueJsonEditor from 'vue-json-editor';
+import RightForm from './RightForm.vue';
+
+export default {
+  components: {
+    vueJsonEditor,
+    RightForm,
+  },
+  name: 'APITestInfoForm',
+  data() {
+    return {
+    };
+  },
+  props: {
+    info: {
+      type: Object,
+    },
+  },
+  methods: {
+    showForm() {
+      this.$refs.rightForm.showForm();
+    },
+  },
+};
+</script>
+
+<style>
+.APITestInfoForm {
+  margin-left: 30px;
+}
+.APITestInfoForm .title {
+  font-size: 24px;
+  font-weight: bold;
+  margin-top: 20px;
+}
+.APITestInfoForm .marginTop {
+  margin-top: 20px;
+}
+.APITestInfoForm .marginTop .key {
+  font-size: 20px;
+  font-weight: bold;
+}
+.APITestInfoForm .marginTop .value {
+  font-size: 18px;
+}
+</style>

+ 323 - 0
src/components/CommonDrawer.vue

@@ -0,0 +1,323 @@
+<template>
+  <el-drawer
+    v-model="showDrawer"
+    :size="650"
+    @before-close="(done) => {
+      isEditing = false;
+      done();
+    }">
+    <template #title>
+      <el-row class="entry-row">
+        <el-col :span="2">
+          <div class="node-type">{{type}}</div>
+        </el-col>
+        <el-col :span="12" :offset="2">
+          <template class="entry-row" v-if="isEditing">
+            <el-input placeholder="请输入标题" size="mini" style="width: 200px;display: inline;" v-model="editedRequirement.title"/>
+          </template>
+          <template v-else><h3>{{title}}</h3></template>
+        </el-col>
+        <el-col :span="6" :offset="2">
+          <el-switch
+            v-if="activeTab ==='basic_info'"
+            style="float: right;"
+            v-model="isEditing"
+            active-text="编辑"
+            @change="(isEditing) => {
+              isEditing && fillDefaultValue();
+            }">
+          </el-switch>
+        </el-col>
+      </el-row>
+    </template>
+    <template #default>
+      <el-row type="flex" justify="center">
+        <el-col :span="20">
+          <el-tabs
+            class="tabs"
+            v-model="activeTab"
+            @tab-click="(tab) => {
+              if(tab.panename !== 'basic_info')
+                isEditing = false;
+            }"
+          >
+            <el-tab-pane label="基本信息" name="basic_info">
+              <el-row class="entry-row" style="margin-top: 15px">
+                <h4>需求描述</h4>
+              </el-row>
+              <el-row class="entry-row">
+                <div v-if="isEditing">
+                  <el-input placeholder="请输入描述"
+                            type="textarea"
+                            maxlength="120"
+                            show-word-limit
+                            :rows="3" style="width: 300px;" v-model="editedRequirement.description" />
+                  <br/>
+                </div>
+                <p v-else>{{description}}</p>
+              </el-row>
+              <!-- 状态、优先级等 -->
+              <el-row class="entry-row" style="margin-top: 15px">
+                <h4>详情</h4>
+              </el-row>
+              <el-row style="margin: 3px 20px;width: 100%">
+                <el-col :span="12">
+                  <div class="detail-row">
+                    <span>状态:</span>
+                    <div  v-if="!isEditing" class="detail-value">
+                      <StateTag :state="state"></StateTag>
+                    </div>
+                    <div v-else>
+                      <el-select style="width: 160px" size="mini" v-model="editedRequirement.state">
+                        <el-option
+                          v-for="item in stateOptions"
+                          :key="item.value"
+                          :value="item.value"
+                          :label="item.value" />
+                      </el-select>
+                    </div>
+                  </div>
+                  <div class="detail-row">
+                    <span>优先级:</span>
+                    <div class="detail-value"  v-if="!isEditing">
+                      <PriorityTag :priority="priority"></PriorityTag>
+                    </div>
+                    <div v-else>
+                      <el-select style="width: 160px" size="mini" v-model="editedRequirement.priority">
+                        <el-option
+                          v-for="item in  priorityOptions"
+                          :key="item.value"
+                          :value="item.value"
+                          :label="item.label" />
+                      </el-select>
+                    </div>
+                  </div>
+                  <div class="detail-row">
+                    <span>经办人:</span>
+                    <div class="detail-value"   v-if="!isEditing">
+                      <el-tag>{{processorName}}</el-tag>
+                    </div>
+                    <div v-else>
+                      <el-select style="width: 160px" size="mini" v-model="editedRequirement.userId" >
+                        <el-option
+                          v-for="item in  userOptions"
+                          :key="item.value"
+                          :value="item.value"
+                          :label="item.label" />
+                      </el-select>
+                    </div>
+                  </div>
+                </el-col>
+                <el-col :span="12">
+                  <div class="detail-row">
+                    <span>工作量:</span>
+                    <div class="detail-value" v-if="!isEditing">
+                      <span>{{timeToTake}}人日</span>
+                    </div>
+                    <div v-else>
+                      <el-input-number size="mini" v-model="editedRequirement.timeToTake" :min="1" :max="200">
+                      </el-input-number>
+                    </div>
+                  </div>
+                  <div class="detail-row">
+                    <span>开始时间:</span>
+                    <span>{{startTime}}</span>
+                  </div>
+                  <div class="detail-row"><span>完成时间:</span>
+                    <span>{{endTime}}</span></div>
+                </el-col>
+              </el-row>
+              <!-- 相关需求 -->
+              <template v-if="showSons">
+              <el-row class="entry-row" style="margin-top: 15px">
+                <h4>子需求</h4>
+              </el-row>
+              <template v-if="(childs && childs.length) || false">
+                <el-row style="margin: 10px 25px;" v-for="item in childs" :key="item.id">
+                  <el-link @click.prevent="changeContent(item.id)" type="primary">{{item.title}}</el-link>
+                </el-row>
+              </template>
+              <template v-else>
+                <el-row style="margin: 0px 25px;">
+                  没有子需求
+                </el-row>
+              </template>
+              </template>
+              <el-row v-if="isEditing" class="entry-row" style="margin-top: 15px">
+                <el-button @click="submitEdit" size="mini" type="primary">提交</el-button>
+                <el-button @click="cancelEdit" size="mini">取消</el-button>
+              </el-row>
+            </el-tab-pane>
+            <el-tab-pane label="历史提交" name="commit_history">
+              <!-- 相关提交 -->
+              <el-row class="entry-row" style="margin-top: 15px">
+                <el-table :data="commits">
+                  <el-table-column type="expand">
+                    <template #default="scope">
+                      <pre>{{scope.row}}</pre>
+                    </template>
+                  </el-table-column>
+                  <el-table-column
+                    prop="id"
+                    label="ID"
+                    align="center"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    prop="title"
+                    label="标题"
+                    align="center"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    prop="gitlabUsername"
+                    label="用户"
+                    align="center"
+                  >
+                  </el-table-column>
+                </el-table>
+              </el-row>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-row>
+    </template>
+  </el-drawer>
+</template>
+
+<script>
+import { RequirementAPI } from '@/api';
+import PriorityTag from '@/components/PriorityTag.vue';
+import StateTag from '@/components/StateTag.vue';
+import { priorityOptions, stateOptions } from '@/utils/constants';
+
+export default {
+  name: 'RequirementDrawers',
+  components: { PriorityTag, StateTag },
+  emits: ['change', 'update'],
+  props: {
+    id: Number,
+    type: String,
+    title: String,
+    description: String,
+    state: String,
+    priority: String,
+    processorId: Number,
+    processorName: String,
+    timeToTake: Number,
+    startTime: String,
+    endTime: String,
+    childs: Array,
+    parent: Number,
+    commits: Array,
+    showSons: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  data() {
+    return {
+      activeTab: 'basic_info',
+      showDrawer: false,
+      isEditing: false,
+      editedRequirement: {
+        title: null,
+        description: null,
+        state: null,
+        timeToTake: null,
+        priority: null,
+        processorId: null,
+        processorName: null,
+      },
+      stateOptions,
+      priorityOptions,
+    };
+  },
+  computed: {
+    userOptions() {
+      return this.$store.getters.userOptions;
+    },
+  },
+  methods: {
+    show() {
+      this.showDrawer = true;
+    },
+    fillDefaultValue() {
+      this.editedRequirement.title = this.title;
+      this.editedRequirement.description = this.description;
+      this.editedRequirement.state = this.state;
+      this.editedRequirement.timeToTake = this.timeToTake;
+      this.editedRequirement.priority = this.priority;
+      this.editedRequirement.processorId = this.processorId;
+      this.editedRequirement.processorName = this.processorName;
+    },
+    getUserName(value) {
+      let res;
+      this.userOptions.some((item) => {
+        if (item.value === value) {
+          res = item.label;
+          return true;
+        }
+        return false;
+      });
+      return res;
+    },
+    submitEdit() {
+      const param = { id: this.id, ...this.editedRequirement };
+      RequirementAPI.updateTreeNode(param).then((res) => {
+        this.isEditing = false;
+        this.$emit('update');
+        this.showDrawer = false;
+      });
+    },
+    cancelEdit() {
+      this.isEditing = false;
+      this.fillDefaultValue();
+    },
+    changeContent(id) {
+      this.$emit('change', id);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.entry-row{
+  width: 100%;
+  margin: 3px 20px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  h3, h4{
+    color: #172b4d
+  }
+}
+.detail-row{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  line-height: 24px;
+  margin: 3px 0;
+}
+.detail-value{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+.node-type{
+  color: #FFFFFF;
+  border-radius: 3px;
+  font-size: 14px;
+  background-color: #42b983;
+  text-align: center;
+}
+.state-entry{
+  border-radius: 5px;
+  background-color: cornflowerblue;
+  font-size: 12px;
+  color: #FFFFFF;
+  padding: 0px 4px;
+  cursor: pointer
+}
+</style>

+ 2 - 2
src/element-plus.ts

@@ -4,6 +4,7 @@ import {
   ElCarousel,
   ElCarouselItem,
   ElContainer,
+  ElCol,
   ElDivider,
   ElDrawer,
   ElHeader,
@@ -29,7 +30,6 @@ import {
   ElTabs,
   ElTabPane,
   ElButtonGroup,
-  ElCol,
   ElInputNumber,
   ElRadio,
   ElRadioGroup,
@@ -47,6 +47,7 @@ export const Components = [
   ElAside,
   ElRow,
   ElContainer,
+  ElCol,
   ElHeader,
   ElMain,
   ElMenu,
@@ -74,7 +75,6 @@ export const Components = [
   ElTabs,
   ElTabPane,
   ElButtonGroup,
-  ElCol,
   ElInputNumber,
   ElRadio,
   ElRadioGroup,

+ 238 - 3
src/views/Project/APITest.vue

@@ -1,13 +1,248 @@
 <template>
-  project-api-test
+  <div class="apiTestContainer">
+    <div class="apiTestHeader">
+      <div class="title">接口测试</div>
+      <el-button size="medium" type="primary" icon="el-icon-plus" @click="handleClick('create')">新建接口测试</el-button>
+    </div>
+    <div class="apiTestTableContainer">
+      <el-table
+        :data="APITests"
+        style="width: 100%">
+        <el-table-column
+          prop="testId"
+          label="ID"
+          align="center"
+          width="100">
+        </el-table-column>
+        <el-table-column
+          prop="testName"
+          label="测试名称"
+          align="center"
+          width="500">
+        </el-table-column>
+        <el-table-column
+          prop="state"
+          label="状态"
+          align="center"
+          width="100"
+          :filters="[{ text: '进行中', value: '进行中' }, { text: '已完成', value: '已完成' }]"
+          :filter-method="(value, row) => {return row.result === value;}"
+          filter-placement="bottom-end">
+          <template #default="scope">
+            <template v-if="scope.row.state === '进行中'">
+              <el-tag>{{scope.row.state}}<i class="el-icon-loading"></i></el-tag>
+            </template>
+            <template v-else-if="scope.row.state === '已完成'">
+              <el-tag type="success">{{scope.row.state}}</el-tag>
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="username"
+          label="创建者"
+          align="center"
+          width="200">
+        </el-table-column>
+        <el-table-column
+          prop="createTime"
+          label="创建时间"
+          align="center"
+          width="250">
+        </el-table-column>
+        <el-table-column
+          label="操作"
+          align="center">
+          <template #default="scope">
+            <template v-if="scope.row.state === '进行中'">
+              <el-button type="primary" size="small" @click="handleClick('trigger', scope.row.testId)">执行测试</el-button>
+            </template>
+            <template v-else-if="scope.row.state === '已完成'">
+              <el-button type="primary" size="small" disabled>执行测试</el-button>
+            </template>
+            <el-button type="warning" size="small" @click="handleClick('viewDetail', scope.row.testId)">查看详情</el-button>
+            <el-button type="danger" size="small" @click="handleClick('delete', scope.row.testId)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-dialog
+        v-model="showAPITestCreateModel"
+        width="600px"
+        :before-close="(done) => {$refs['createAPITestForm'].resetFields();done();}"
+        destroy-on-close>
+        <template #title>创建测试任务</template>
+        <el-form
+          :model="createAPITestForm"
+          ref="createAPITestForm"
+          label-position="right"
+          label-width="100px"
+          label-suffix=":"
+          class="create-APITest-form">
+          <el-form-item label="测试名称" prop="testName" required>
+            <el-input v-model="createAPITestForm.testName" placeholder="请输入接口测试的名称"></el-input>
+          </el-form-item>
+          <el-form-item label="待测URL" prop="url" required>
+            <el-input v-model="createAPITestForm.url" placeholder="请输入待测试的URL"></el-input>
+          </el-form-item>
+          <el-form-item label="参数" prop="params">
+            <div>
+              <vue-json-editor
+                :height="'400px'"
+                v-model="createAPITestForm.params"
+                :show-btns="false"
+                :mode="'code'"
+                :modes="['code']"
+                lang="zh"
+                @json-change="onJsonChange"
+                @json-save="onJsonSave"
+                @has-error="onError"
+              ></vue-json-editor>
+              <span v-if="!jsonValid" style="color: #F56C6C; font-size: 12px">JSON is invalid.</span>
+            </div>
+          </el-form-item>
+          <el-form-item label="并发数" prop="qps" required>
+            <el-input v-model="createAPITestForm.qps" placeholder="请输入并发数"></el-input>
+          </el-form-item>
+        </el-form>
+        <template #footer>
+          <span class="dialog-footer">
+            <el-button type="primary" @click="handleAPITestCreateConfirm">确 定</el-button>
+          </span>
+        </template>
+      </el-dialog>
+    </div>
+    <div>
+      <APITestInfoForm ref="apiTestInfoForm" :info="resultJson"></APITestInfoForm>
+    </div>
+  </div>
 </template>
 
 <script>
-export default {
+import vueJsonEditor from 'vue-json-editor';
+import { APITestAPI } from '@/api';
+import APITestInfoForm from '../../components/APITestInfoForm.vue';
 
+export default {
+  components: {
+    vueJsonEditor,
+    APITestInfoForm,
+  },
+  data() {
+    return {
+      APITests: [
+        {
+          testId: 1,
+          testName: '登录接口测试',
+          state: '进行中',
+          username: '张三',
+          createTime: '2021-03-19',
+        },
+        {
+          testId: 1,
+          testName: '登录接口测试',
+          state: '已完成',
+          username: '张三',
+          createTime: '2021-03-19',
+        },
+      ],
+      showAPITestCreateModel: false,
+      createAPITestForm: {
+        testName: '',
+        url: '',
+        params: {},
+        qps: '',
+        createTime: '',
+        username: '',
+      },
+      resultJson: {
+        url: 'www.baidu.com',
+        params: {
+          name: 'zhangsan',
+          age: 12,
+          occupation: 'SoftWare Engineer',
+        },
+        qps: 10,
+        success: 9,
+        failure: 1,
+        timeList: [4, 3, 5, 6, 2, 2, 10, 7, 1, 8],
+      },
+      jsonValid: true,
+    };
+  },
+  async mounted() {
+    const testJson = await APITestAPI.getAPITestInfosByProjectId(1);
+    console.log(testJson);
+  },
+  methods: {
+    async handleClick(action, id) {
+      switch (action) {
+        case 'create': {
+          this.showAPITestCreateModel = true;
+          console.log(this.$store.state.workspace.currentProjectId);
+          break;
+        }
+        case 'trigger': {
+          console.log('运行测试');
+          break;
+        }
+        case 'viewDetail': {
+          this.$refs.apiTestInfoForm.showForm();
+          break;
+        }
+        case 'delete': {
+          console.log('删除测试任务');
+          break;
+        }
+        default: {
+          console.log('Unknown type of action');
+        }
+      }
+    },
+    onJsonChange(value) {
+      this.onJsonSave(value);
+    },
+    onJsonSave(value) {
+      this.createAPITestForm.params = value;
+      console.log(this.createAPITestForm.params);
+      this.jsonValid = true;
+    },
+    onError(value) {
+      this.jsonValid = false;
+    },
+    async handleAPITestCreateConfirm() {
+      this.$refs.createAPITestForm.validate(async (valid) => {
+        if (valid) {
+          console.log(this.createAPITestForm);
+          this.$refs.createAPITestForm.resetFields();
+          this.showAPITestCreateModel = false;
+        }
+      });
+    },
+  },
 };
 </script>
 
 <style>
-
+.apiTestContainer {
+  margin: 70px 30px 0 30px;
+}
+.apiTestHeader {
+  display: flex;
+  justify-content: space-between;
+}
+.apiTestHeader .title {
+  font-size: 20px;
+}
+.apiTestTableContainer {
+  margin-top: 20px;
+}
+.create-APITest-form {
+  width: 80%;
+  margin: 0 auto;
+}
+.jsoneditor-vue {
+  height: 100%;
+}
+.jsoneditor-poweredBy {
+  display: none;
+}
 </style>

+ 12 - 0
yarn.lock

@@ -9365,6 +9365,13 @@ vue-hot-reload-api@^2.3.0:
   resolved "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
   integrity sha1-UylVzB6yCKPZkLOp+acFdGV+CPI=
 
+vue-json-editor@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/vue-json-editor/-/vue-json-editor-1.4.3.tgz#004c9037ac91f16dd8fc558c9914e5f33a41d71c"
+  integrity sha512-st9HdXBgCnyEmmfWrZQiKzp4KuYXzmYVUNDn5h6Fa18MrrGS1amnyUFyv7hQFsNBDW27B7BKkdGOqszYT1srAg==
+  dependencies:
+    vue "^2.2.6"
+
 "vue-loader-v16@npm:vue-loader@^16.1.0":
   version "16.1.2"
   resolved "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.2.tgz?cache=0&sync_timestamp=1608188050165&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.2.tgz#5c03b6c50d2a5f983c7ceba15c50d78ca2b298f4"
@@ -9403,6 +9410,11 @@ vue-template-es2015-compiler@^1.9.0:
   resolved "https://registry.npm.taobao.org/vue-template-es2015-compiler/download/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
   integrity sha1-HuO8mhbsv1EYvjNLsV+cRvgvWCU=
 
+vue@^2.2.6:
+  version "2.6.12"
+  resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
+  integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
+
 vue@^3.0.0:
   version "3.0.5"
   resolved "https://registry.npm.taobao.org/vue/download/vue-3.0.5.tgz?cache=0&sync_timestamp=1609359858533&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-3.0.5.tgz#de1b82eba24abfe71e0970fc9b8d4b2babdc3fe1"