Explorar o código

fix: 修改接口测试

zhaoxingrui %!s(int64=4) %!d(string=hai) anos
pai
achega
f311d6a432

+ 27 - 0
src/api/devcloud/interfacetest.ts

@@ -0,0 +1,27 @@
+import axios from '@/utils/axios';
+
+import { INTERFACETEST_PREFIX } from './prefix';
+
+type CreateInterfaceTest = {
+    name: string;
+    projectId: number;
+    createTime: string;
+    username: string;
+    url: string;
+    method: string;
+    params: JSON;
+}
+
+export const createTest = (InterfaceTest: CreateInterfaceTest) => axios.post(`${INTERFACETEST_PREFIX}/create`, InterfaceTest);
+
+export const getAll = (projectId: number) => axios.get(`${INTERFACETEST_PREFIX}/list/${projectId}`);
+
+export const deleteTest = (id: number) => axios.get(`${INTERFACETEST_PREFIX}/delete/${id}`);
+
+export const executeTest = (id: number, username: string, type: string) => axios.get(
+  `${INTERFACETEST_PREFIX}/execute/${id}/${username}/${type}`,
+);
+
+export const getResults = (id: number) => axios.get(`${INTERFACETEST_PREFIX}/results/${id}`);
+
+export const getAutoResults = (id: number) => axios.get(`${INTERFACETEST_PREFIX}/results/auto/${id}`);

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

@@ -10,4 +10,5 @@ export const STAGE_PREFIX = '/stages';
 export const COMMIT_PREFIX = '/commits';
 export const FUNCTEST_PREFIX = '/func_test';
 export const AUTOTEST_PREFIX = '/auto_test';
+export const INTERFACETEST_PREFIX = '/interfaces';
 export const SQL_PREFIX = '/sql';

+ 2 - 0
src/api/index.ts

@@ -6,6 +6,7 @@ import * as PipelineAPI from './devcloud/pipeline';
 import * as APITestAPI from './devcloud/apitest';
 import * as FuncTestAPI from './devcloud/functest';
 import * as AutoTestAPI from './devcloud/autotest';
+import * as InterfaceTestAPI from './devcloud/interfacetest';
 import * as SQLExecAPI from './devcloud/sql';
 import * as PortalAPI from './portal';
 
@@ -20,4 +21,5 @@ export {
   FuncTestAPI,
   AutoTestAPI,
   SQLExecAPI,
+  InterfaceTestAPI,
 };

+ 1 - 1
src/router/index.ts

@@ -60,7 +60,7 @@ const routes: Array<RouteRecordRaw> = [
       },
       {
         path: 'apitest',
-        component: () => import(/* webpackChunkName: "project" */'@/views/Project/APITest.vue'),
+        component: () => import(/* webpackChunkName: "project" */'@/views/Project/InterfaceTest.vue'),
       },
       {
         path: 'invite',

+ 937 - 0
src/views/Project/InterfaceTest.vue

@@ -0,0 +1,937 @@
+<template>
+  <el-tabs class="tabs" v-model="activeTab">
+    <el-tab-pane label="接口测试" name="interfaceTest">
+      <el-table :data="interfaceTests" style="width: 100%" height="600">
+        <el-table-column type="expand">
+          <template #default="props">
+            <el-row type="flex" justify="center">
+              <el-col :span="24">
+                <el-row class="entry-row">
+                  <el-col :span="2">
+                    <h4>待测url:</h4>
+                  </el-col>
+                  <el-col :span="18">
+                    <div>{{props.row.url}}</div>
+                  </el-col>
+                </el-row>
+                <el-row class="entry-row">
+                  <el-col :span="2">
+                    <h4>测试方法:</h4>
+                  </el-col>
+                  <el-col :span="18">
+                    <el-tag :type="props.row.method === 'GET' ? 'success' : ''">{{props.row.method}}</el-tag>
+                  </el-col>
+                </el-row>
+                <el-row class="entry-row">
+                  <el-col :span="2">
+                    <h4 v-if="props.row.method === 'GET'">url携带参数:</h4>
+                    <h4 v-else>请求体:</h4>
+                  </el-col>
+                  <el-col :span="8">
+                    <el-input :value="strToJSON(props.row.params)" type="textarea" :rows="4" disabled></el-input>
+                  </el-col>
+                </el-row>
+              </el-col>
+            </el-row>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="id"
+          label="ID"
+          width="60"
+          align="center">
+        </el-table-column>
+        <el-table-column
+          prop="name"
+          label="测试名称"
+          width="300"
+          align="center">
+        </el-table-column>
+        <el-table-column
+          prop="username"
+          label="创建者"
+          align="center"
+          width="170">
+        </el-table-column>
+        <el-table-column
+          prop="createTime"
+          label="创建时间"
+          align="center"
+          width="170">
+        </el-table-column>
+        <el-table-column
+          label="详情"
+          align="center">
+          <template #default="scope">
+            <el-button
+              @click="handleClick('detail', scope.row.id)"
+              type="text"
+              size="small">查看详情</el-button>
+          </template>
+        </el-table-column>
+        <el-table-column
+          align="center">
+          <template #header>
+            <span>操作</span>
+            <el-button @click="handleClick('create')" type="text" class="add-button">新建</el-button>
+          </template>
+          <template #default="scope">
+            <el-button @click="handleClick('trigger', scope.row.id)" type="text" size="small">触发</el-button>
+            <el-button @click="handleClick('delete', scope.row.id)" type="text" size="small">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-tab-pane>
+    <el-tab-pane label="黑盒测试" name="funcTest">
+      <el-table :data="testcases" width="100%">
+        <el-table-column
+          prop="id"
+          label="ID"
+          align="center"
+          width="60"></el-table-column>
+        <el-table-column
+          prop="title"
+          label="标题"
+          align="center">
+        </el-table-column>
+        <el-table-column
+          prop="taskId"
+          label="关联的任务ID"
+          align="center"
+          width="150">
+        </el-table-column>
+        <el-table-column
+          prop="pass"
+          label="执行状态"
+          align="center"
+          width="100"
+          :filters="[{ text: '进行中', value: false }, { text: '已完成', value: true }]"
+          :filter-method="(value, row) => {
+            return row.pass === value;
+          }"
+          filter-placement="bottom-end">
+          <template #default="scope">
+            <template v-if="scope.row.pass === false">
+              <el-tag>进行中<i class="el-icon-loading"></i></el-tag>
+            </template>
+            <template v-else-if="scope.row.pass === true">
+              <el-tag type="success">已完成</el-tag>
+            </template>
+            <template v-else>
+              <el-tag type="warning">未知状态</el-tag>
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="详情"
+          align="center"
+          width="120">
+          <template #default="scope">
+            <el-button
+              @click="handleTestCasesClick('steps', scope.row.id)"
+              type="text"
+              size="small">测试用例步骤列表</el-button>
+          </template>
+        </el-table-column>
+        <el-table-column
+          align="center">
+          <template #header>
+            <span>操作</span>
+            <el-button
+              @click="handleTestCasesClick('latest', 0)"
+              type="text"
+              class="add-button">部署的历史记录</el-button>
+            <el-button @click="handleTestCasesClick('create')" type="text" class="add-button">新建</el-button>
+          </template>
+          <template #default="scope">
+            <el-button v-if="scope.row.pass === false" @click="handleTestCasesClick('finish', scope.row.id)" type="text" size="small">
+              完成测试
+            </el-button>
+            <el-button v-else @click="handleTestCasesClick('finish', scope.row.id)" type="text" size="small" disabled>完成测试</el-button>
+            <el-button v-if="scope.row.pass === true" @click="handleTestCasesClick('reopen', scope.row.id)" type="text" size="small">
+              重新开放测试
+            </el-button>
+            <el-button v-else @click="handleTestCasesClick('reopen', scope.row.id)" type="text" size="small" disabled>重新开放测试</el-button>
+            <el-button @click="handleTestCasesClick('edit', scope.row.id)" type="text" size="small">编辑</el-button>
+            <el-button @click="handleTestCasesClick('delete', scope.row.id)" type="text" size="small">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-tab-pane>
+  </el-tabs>
+  <el-dialog
+    v-model="createInterfaceDialog"
+    width="600px"
+    :before-close="(done) => {$refs['createInterfaceTestForm'].resetFields();done();}"
+    destroy-on-close>
+    <template #title>创建接口测试</template>
+    <el-form
+      :model="createInterfaceTestForm"
+      ref="createInterfaceTestForm"
+      label-position="right"
+      label-width="100px"
+      label-suffix=":"
+      style="width: 80%; margin: 0 auto;">
+      <el-form-item label="测试名称" prop="name" required>
+        <el-input v-model="createInterfaceTestForm.name" placeholder="请输入接口测试的名称"></el-input>
+      </el-form-item>
+      <el-form-item label="待测URL" prop="url" required>
+        <el-input v-model="createInterfaceTestForm.url" placeholder="请输入待测试的URL"></el-input>
+      </el-form-item>
+      <el-form-item label="方法" prop="method" required>
+        <el-select v-model="createInterfaceTestForm.method" placeholder="请选择待测URL的方法">
+          <el-option
+            v-for="(method, index) in methodList"
+            :key="index"
+            :value="method">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item :label="createInterfaceTestForm.method === 'POST' ? '请求体' : '携带参数'" prop="params">
+        <div>
+          <vue-json-editor
+            :height="'400px'"
+            v-model="createInterfaceTestForm.params"
+            :show-btns="false"
+            mode="code"
+            :modes="['code']"
+            lang="zh"
+            @json-change="onJsonChange"
+            @json-save="onJsonSave"
+          ></vue-json-editor>
+        </div>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="handleInterfaceCreateConfirm()">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+  <el-drawer
+    v-model="detailDrawer"
+    :size="650">
+    <template #title>
+      <el-row class="entry-row">
+        <el-col :span="2">
+          <div class="node-type">测试</div>
+        </el-col>
+        <el-col :span="2">
+          <template class="entry-row">
+            <span>#{{detailInterfaceTestInfo.id}}</span>
+          </template>
+        </el-col>
+        <el-col :span="20">
+          <template class="entry-row">
+            <span>{{detailInterfaceTestInfo.name}}</span>
+          </template>
+        </el-col>
+      </el-row>
+    </template>
+    <template #default>
+      <el-divider content-position="left">测试任务信息</el-divider>
+      <div style="width: 80%; margin: 0 auto">
+        <el-row type="flex" align="center">
+          <el-col :span="24">
+            <el-row class="entry-row">
+              <el-col :span="6">
+                <h4>待测url:</h4>
+              </el-col>
+              <el-col :span="18">
+                <div>{{detailInterfaceTestInfo.url}}</div>
+              </el-col>
+            </el-row>
+            <el-row class="entry-row">
+              <el-col :span="6">
+                <h4>测试方法:</h4>
+              </el-col>
+              <el-col :span="18">
+                <el-tag :type="detailInterfaceTestInfo.method === 'GET' ? 'success' : ''">{{detailInterfaceTestInfo.method}}</el-tag>
+              </el-col>
+            </el-row>
+            <el-row class="entry-row">
+              <el-col :span="6">
+                <h4 v-if="detailInterfaceTestInfo.method === 'GET'">url携带参数:</h4>
+                <h4 v-else>请求体:</h4>
+              </el-col>
+              <el-col :span="18">
+                <el-input :value="strToJSON(detailInterfaceTestInfo.params)" type="textarea" :rows="4" disabled></el-input>
+              </el-col>
+            </el-row>
+          </el-col>
+        </el-row>
+      </div>
+      <el-divider content-position="left">测试执行结果</el-divider>
+      <div style="width: 95%; margin: 0 auto">
+        <el-tabs v-model="activeTab1">
+          <el-tab-pane label="手动触发结果" name="artificial">
+            <el-table :data="interfaceTestResults" style="width: 100%" height="800" border>
+              <el-table-column type="expand">
+                <template #default="props">
+                  <el-row type="flex" justify="center">
+                    <el-col :span="24">
+                      <el-row class="entry-row">
+                        <el-col :span="4">
+                          <h4>返回结果:</h4>
+                        </el-col>
+                        <el-col :span="20">
+                          <el-input :value="strToJSON(props.row.result)" type="textarea" :rows="8" disabled></el-input>
+                        </el-col>
+                      </el-row>
+                    </el-col>
+                  </el-row>
+                </template>
+              </el-table-column>
+              <el-table-column
+                prop="id"
+                label="ID"
+                width="40"
+                align="center">
+              </el-table-column>
+              <el-table-column
+                prop="executeTime"
+                label="执行时间"
+                width="170"
+                align="center">
+              </el-table-column>
+              <el-table-column
+                prop="username"
+                label="执行者"
+                width="100"
+                align="center">
+              </el-table-column>
+              <el-table-column
+                prop="code"
+                label="状态码"
+                width="80"
+                align="center">
+                <template #default="scope">
+                  <el-tag disable-transitions>{{scope.row.code}}</el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column
+                prop="cost"
+                label="耗时/ms"
+                align="center">
+              </el-table-column>
+            </el-table>
+          </el-tab-pane>
+          <el-tab-pane label="自动化测试结果">
+            <el-table :data="interfaceTestAutoResults" style="width: 100%" height="800" border>
+              <el-table-column type="expand">
+                <template #default="props">
+                  <el-row type="flex" justify="center">
+                    <el-col :span="24">
+                      <el-row class="entry-row">
+                        <el-col :span="4">
+                          <h4>返回结果:</h4>
+                        </el-col>
+                        <el-col :span="20">
+                          <el-input :value="strToJSON(props.row.result)" type="textarea" :rows="8" disabled></el-input>
+                        </el-col>
+                      </el-row>
+                    </el-col>
+                  </el-row>
+                </template>
+              </el-table-column>
+              <el-table-column
+                prop="id"
+                label="ID"
+                width="40"
+                align="center">
+              </el-table-column>
+              <el-table-column
+                prop="deploymentId"
+                label="流水线ID"
+                width="100"
+                align="center">
+              </el-table-column>
+              <el-table-column
+                prop="executeTime"
+                label="执行时间"
+                width="170"
+                align="center">
+              </el-table-column>
+              <el-table-column
+                prop="username"
+                label="执行者"
+                width="100"
+                align="center">
+              </el-table-column>
+              <el-table-column
+                prop="code"
+                label="状态码"
+                width="80"
+                align="center">
+                <template #default="scope">
+                  <el-tag disable-transitions>{{scope.row.code}}</el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column
+                prop="cost"
+                label="耗时/ms"
+                align="center">
+              </el-table-column>
+            </el-table>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </template>
+  </el-drawer>
+  <!-- 部署的历史记录 -->
+  <ElDrawer
+    v-model="deployHistoryDrawer"
+    :size="650">
+    <template #title>
+      <el-row class="entry-row">
+        <el-col :span="3">
+          <div class="node-type">项目</div>
+        </el-col>
+        <el-col :span="2">
+          <template class="entry-row">
+            <span>#{{currentProjectId}}</span>
+          </template>
+        </el-col>
+      </el-row>
+    </template>
+    <template #default>
+      <el-row type="flex" justify="center">
+        <el-col :span="20">
+          <el-table :data="deployHistoryInfo" width="100%">
+            <el-table-column
+              prop="id"
+              label="ID"
+              align="center"
+              width="60">
+            </el-table-column>
+            <el-table-column
+              prop="timestamp"
+              label="部署时间"
+              align="center"
+              width="150">
+            </el-table-column>
+            <el-table-column
+              prop="operationType"
+              label="操作类型"
+              align="center">
+            </el-table-column>
+            <el-table-column
+              prop="pipelineRecordId"
+              label="流水线ID"
+              align="center"
+              width="80">
+            </el-table-column>
+          </el-table>
+        </el-col>
+      </el-row>
+    </template>
+  </ElDrawer>
+  <!-- 新建测试用例 -->
+  <el-dialog
+    v-model="showTestCaseCreateModel"
+    width="600px"
+    :before-close="(done) => {$refs['createTestCaseForm'].resetFields();done();}"
+    destroy-on-close>
+    <template #title>创建测试用例</template>
+    <el-form
+      :model="createTestCaseForm"
+      ref="createTestCaseForm"
+      label-position="right"
+      label-width="70px"
+      label-suffix=":"
+      class="create-APITest-form">
+      <el-form-item label="title" prop="title">
+        <el-input
+          v-model="createTestCaseForm.title"
+          type="textarea"
+          :rows="5"
+          placeholder="请输入测试用例的标题"></el-input>
+      </el-form-item>
+      <el-form-item label="taskId" prop="taskId">
+        <el-select v-model="createTestCaseForm.taskId">
+          <el-option
+            v-for="item in taskIdList"
+            :key="item"
+            :value="item"></el-option>
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="handleTestCaseCreateConfirm">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+  <!-- 测试用例步骤详情 -->
+  <el-drawer
+    v-model="stepsDrawer"
+    :size="700">
+    <template #title>
+      <el-row class="entry-row">
+        <el-col :span="3">
+          <div class="node-type">测试用例</div>
+        </el-col>
+        <el-col :span="2">
+          <template class="entry-row">
+            <span>#{{detailTestCaseInfo.id}}</span>
+          </template>
+        </el-col>
+        <el-col :span="8">
+          <template class="entry-row">
+            <span>{{detailTestCaseInfo.title}}</span>
+          </template>
+        </el-col>
+      </el-row>
+    </template>
+    <template #default>
+      <el-divider content-position="left">新建测试用例步骤</el-divider>
+      <div style="width: 90%; margin: 0 auto;">
+        <el-button @click="handleSteps('create', detailTestCaseInfo.id, 0)" type="text" class="add-button">新建测试用例步骤</el-button>
+      </div>
+      <el-divider content-position="left">测试用例步骤</el-divider>
+      <div style="width: 95%; margin: 0 auto;">
+        <el-row type="flex" justify="center">
+          <el-col :span="24">
+            <el-table :data="detailStepsInfo" width="100%">
+                  <el-table-column
+                    prop="id"
+                    label="ID"
+                    align="center"
+                    width="60">
+                  </el-table-column>
+                  <el-table-column
+                    prop="description"
+                    label="描述"
+                    align="center">
+                  </el-table-column>
+                  <el-table-column
+                    prop="expectation"
+                    label="预期输出"
+                    align="center">
+                  </el-table-column>
+                  <el-table-column
+                    prop="state"
+                    label="状态"
+                    align="center">
+                  </el-table-column>
+                  <el-table-column
+                    align="center">
+                    <template #header>
+                      <span>操作</span>
+                    </template>
+                    <template #default="scope">
+                      <el-button @click="handleSteps('update', scope.row.testCaseId, scope.row.id)" type="text" size="small">更新</el-button>
+                      <el-button
+                        @click="handleSteps('updateState', scope.row.testCaseId, scope.row.id)" type="text" size="small">修改状态</el-button>
+                      <el-button @click="handleSteps('delete', scope.row.testCaseId, scope.row.id)" type="text" size="small">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+          </el-col>
+        </el-row>
+      </div>
+    </template>
+  </el-drawer>
+  <!-- 修改测试用例步骤 -->
+  <el-dialog
+    v-model="showUpdateStepModel"
+    width="600px"
+    :before-close="(done) => {$refs['updateStepForm'].resetFields();done();}"
+    destroy-on-close>
+    <template #title>修改测试用例步骤</template>
+    <el-form
+      :model="updateStepForm"
+      ref="updateStepForm"
+      label-position="right"
+      label-width="100px"
+      label-suffix=":">
+      <el-form-item label="id" prop="id">
+        <el-input
+          v-model="updateStepForm.id"
+          disabled>
+        </el-input>
+      </el-form-item>
+      <el-form-item label="description" prop="description">
+        <el-input
+          v-model="updateStepForm.description"
+          type="textarea"
+          :rows="4">
+        </el-input>
+      </el-form-item>
+      <el-form-item label="expectation" prop="expectation">
+        <el-input
+          v-model="updateStepForm.expectation"
+          type="textarea"
+          :rows="4">
+        </el-input>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="handleStepUpdateConfirm">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+  <!-- 修改测试用例步骤state -->
+  <el-dialog
+    v-model="showUpdateStepStateModel"
+    width="600px"
+    :before-close="(done) => {$refs['updateStepForm'].resetFields();done();}"
+    destroy-on-close>
+    <template #title>修改测试用例步骤状态</template>
+    <el-form
+      :model="updateStepForm"
+      ref="updateStepForm"
+      label-position="right"
+      label-width="100px"
+      label-suffix=":">
+      <el-form-item label="id" prop="id">
+        <el-input
+          v-model="updateStepForm.id"
+          disabled>
+        </el-input>
+      </el-form-item>
+      <el-form-item label="state" prop="state">
+        <el-select
+          v-model="updateStepForm.state">
+          <el-option
+            v-for="(state, index) in stateList"
+            :key="index"
+            :label="state"
+            :value="state">
+          </el-option>
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="handleStepUpdateStateConfirm">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+  <!-- 修改测试用例title -->
+  <el-dialog
+    v-model="showEditTestCaseModel"
+    width="600px"
+    :before-close="(done) => {$refs['detailTestCaseInfo'].resetFields();done();}"
+    destroy-on-close>
+    <template #title>编辑测试用例标题</template>
+    <el-form
+      :model="detailTestCaseInfo"
+      ref="detailTestCaseInfo"
+      label-position="right"
+      label-width="100px"
+      label-suffix=":">
+      <el-form-item label="id" prop="id">
+        <el-input
+          v-model="detailTestCaseInfo.id"
+          disabled>
+        </el-input>
+      </el-form-item>
+      <el-form-item label="title" prop="title">
+        <el-input
+          v-model="detailTestCaseInfo.title"
+          type="textarea"
+          :rows="4">
+        </el-input>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="handleTestCaseEditConfirm">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+import vueJsonEditor from 'vue-json-editor';
+import { InterfaceTestAPI, FuncTestAPI } from '@/api';
+import { ElMessage } from 'element-plus';
+
+export default {
+  components: {
+    vueJsonEditor,
+  },
+  data() {
+    return {
+      currentProjectId: -1,
+      activeTab: 'interfaceTest',
+      activeTab1: 'artificial',
+      interfaceTests: [],
+      createInterfaceDialog: false,
+      createInterfaceTestForm: {},
+      methodList: ['GET', 'POST'],
+      jsonValid: true,
+      detailInterfaceTestInfo: {},
+      interfaceTestResults: [],
+      interfaceTestAutoResults: [],
+      detailDrawer: false,
+      testcases: [],
+      taskIdList: [],
+      showTestCaseCreateModel: false,
+      deployHistoryInfo: {},
+      deployHistoryDrawer: false,
+      detailTestCaseInfo: {},
+      showEditTestCaseModel: false,
+      detailStepsInfo: {},
+      stepsDrawer: false,
+      showUpdateStepModel: false,
+      updateStepForm: {},
+      showUpdateStepStateModel: false,
+      createTestCaseForm: {},
+      stateList: ['待测试', '已测试', '出现bug'],
+    };
+  },
+  async mounted() {
+    this.currentProjectId = this.$store.state.workspace.currentProjectId;
+    this.interfaceTests = await InterfaceTestAPI.getAll(this.currentProjectId);
+    this.testcases = await FuncTestAPI.getTestCases(this.currentProjectId);
+    this.taskIdList = await FuncTestAPI.getTreeIdList(this.currentProjectId);
+  },
+  methods: {
+    strToJSON(params) {
+      return JSON.stringify(params, null, 4);
+    },
+    async handleClick(action, id) {
+      switch (action) {
+        case 'create': {
+          this.createInterfaceDialog = true;
+          break;
+        }
+        case 'delete': {
+          await InterfaceTestAPI.deleteTest(id);
+          ElMessage.success('删除成功!');
+          this.interfaceTests = await InterfaceTestAPI.getAll(this.currentProjectId);
+          break;
+        }
+        case 'trigger': {
+          await InterfaceTestAPI.executeTest(id, this.$store.state.user.username, 'artificial');
+          this.timer = setTimeout(() => {
+            this.$nextTick(() => {
+              ElMessage.success('执行成功!');
+            });
+          }, 3000);
+          break;
+        }
+        case 'detail': {
+          this.interfaceTests.forEach((item) => {
+            if (item.id === id) {
+              this.detailInterfaceTestInfo = item;
+            }
+          });
+          this.interfaceTestResults = await InterfaceTestAPI.getResults(id);
+          this.interfaceTestAutoResults = await InterfaceTestAPI.getAutoResults(id);
+          this.interfaceTestResults.forEach((item) => {
+            item.result = JSON.parse(item.result);
+          });
+          this.interfaceTestAutoResults.forEach((item) => {
+            item.result = JSON.parse(item.result);
+          });
+          this.detailDrawer = true;
+          break;
+        }
+        default: {
+          console.log('Unknown type of action.');
+        }
+      }
+    },
+    onJsonChange(value) {
+      this.onJsonSave(value);
+    },
+    onJsonSave(value) {
+      this.createInterfaceTestForm.params = value;
+    },
+    async handleInterfaceCreateConfirm() {
+      this.$refs.createInterfaceTestForm.validate(async (valid) => {
+        if (valid) {
+          await InterfaceTestAPI.createTest({
+            projectId: this.currentProjectId,
+            createTime: '',
+            username: this.$store.state.user.username,
+            name: this.createInterfaceTestForm.name,
+            url: this.createInterfaceTestForm.url,
+            method: this.createInterfaceTestForm.method,
+            params: this.createInterfaceTestForm.params,
+          });
+          this.interfaceTests = await InterfaceTestAPI.getAll(this.currentProjectId);
+          this.$refs.createInterfaceTestForm.resetFields();
+          this.createInterfaceDialog = false;
+          ElMessage.success('创建成功!');
+        }
+      });
+    },
+    async handleTestCasesClick(action, id) {
+      switch (action) {
+        case 'create': {
+          this.showTestCaseCreateModel = true;
+          break;
+        }
+        case 'latest': {
+          this.deployHistoryInfo = await FuncTestAPI.getLatestRecord(this.$store.state.workspace.currentProjectId);
+          this.deployHistoryDrawer = true;
+          break;
+        }
+        case 'finish': {
+          await FuncTestAPI.finishTestCase(id);
+          this.testcases = await FuncTestAPI.getTestCases(this.$store.state.workspace.currentProjectId);
+          break;
+        }
+        case 'reopen': {
+          await FuncTestAPI.reopenTestCase(id);
+          this.testcases = await FuncTestAPI.getTestCases(this.$store.state.workspace.currentProjectId);
+          break;
+        }
+        case 'edit': {
+          this.testcases.forEach((item) => {
+            if (item.id === id) {
+              this.detailTestCaseInfo = item;
+            }
+          });
+          this.showEditTestCaseModel = true;
+          break;
+        }
+        case 'delete': {
+          await FuncTestAPI.deleteTestCase(id);
+          ElMessage.success('删除成功!');
+          this.testcases = await FuncTestAPI.getTestCases(this.$store.state.workspace.currentProjectId);
+          break;
+        }
+        case 'steps': {
+          this.testcases.forEach((item) => {
+            if (item.id === id) {
+              this.detailTestCaseInfo = item;
+            }
+          });
+          this.detailStepsInfo = await FuncTestAPI.getTestCaseSteps(id);
+          this.stepsDrawer = true;
+          break;
+        }
+        default: {
+          console.warn('Unknown type of action.');
+        }
+      }
+    },
+    async handleTestCaseCreateConfirm() {
+      this.$refs.createTestCaseForm.validate(async (valid) => {
+        if (valid) {
+          this.createTestCaseForm.projectId = this.$store.state.workspace.currentProjectId;
+          await FuncTestAPI.createTestCase(
+            this.createTestCaseForm.projectId, this.createTestCaseForm.title, this.createTestCaseForm.taskId,
+          );
+          this.testcases = await FuncTestAPI.getTestCases(this.$store.state.workspace.currentProjectId);
+          this.$refs.createTestCaseForm.resetFields();
+          this.showTestCaseCreateModel = false;
+          ElMessage.success('创建成功!');
+        }
+      });
+    },
+    async handleSteps(action, caseId, stepId) {
+      switch (action) {
+        case 'create': {
+          await FuncTestAPI.createTestCaseStep(caseId);
+          this.detailStepsInfo = await FuncTestAPI.getTestCaseSteps(caseId);
+          ElMessage.success('创建成功!');
+          break;
+        }
+        case 'delete': {
+          await FuncTestAPI.deleteTestCaseStep(stepId);
+          ElMessage.success('删除成功!');
+          this.detailStepsInfo = await FuncTestAPI.getTestCaseSteps(caseId);
+          break;
+        }
+        case 'update': {
+          this.showUpdateStepModel = true;
+          this.detailStepsInfo.forEach((item) => {
+            if (item.id === stepId) {
+              this.updateStepForm = item;
+            }
+          });
+          break;
+        }
+        case 'updateState': {
+          this.showUpdateStepStateModel = true;
+          this.detailStepsInfo.forEach((item) => {
+            if (item.id === stepId) {
+              this.updateStepForm = item;
+            }
+          });
+          break;
+        }
+        default: {
+          console.warn('Unknown type of action.');
+        }
+      }
+    },
+    async handleStepUpdateConfirm() {
+      this.$refs.updateStepForm.validate(async (valid) => {
+        if (valid) {
+          await FuncTestAPI.updateTestCaseStep(this.updateStepForm.id, this.updateStepForm.description, this.updateStepForm.expectation);
+          this.detailStepsInfo = await FuncTestAPI.getTestCaseSteps(this.updateStepForm.testCaseId);
+          this.$refs.updateStepForm.resetFields();
+          this.showUpdateStepModel = false;
+          ElMessage.success('修改成功!');
+        }
+      });
+    },
+    async handleStepUpdateStateConfirm() {
+      this.$refs.updateStepForm.validate(async (valid) => {
+        if (valid) {
+          let state;
+          if (this.updateStepForm.state === '待测试') {
+            state = 'WAITING';
+          } else if (this.updateStepForm.state === '已测试') {
+            state = 'PASS';
+          } else if (this.updateStepForm.state === '出现bug') {
+            state = 'BUG';
+          }
+          await FuncTestAPI.updateTestCaseStepState(this.updateStepForm.id, state);
+          this.detailStepsInfo = await FuncTestAPI.getTestCaseSteps(this.updateStepForm.testCaseId);
+          this.$refs.updateStepForm.resetFields();
+          this.showUpdateStepStateModel = false;
+          ElMessage.success('修改成功!');
+        }
+      });
+    },
+    async handleTestCaseEditConfirm() {
+      this.$refs.detailTestCaseInfo.validate(async (valid) => {
+        if (valid) {
+          await FuncTestAPI.updateTestCase(this.detailTestCaseInfo.id, this.detailTestCaseInfo.title);
+          this.testcases = await FuncTestAPI.getTestCases(this.$store.state.workspace.currentProjectId);
+          this.$refs.detailTestCaseInfo.resetFields();
+          this.showEditTestCaseModel = false;
+          ElMessage.success('修改成功!');
+        }
+      });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.tabs {
+  width: 80%;
+  margin: auto auto;
+}
+.add-button {
+  margin-left: 14px;
+}
+.entry-row {
+  width: 100%;
+  margin: 3px 20px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  h3, h4{
+    color: #172b4d;
+  }
+}
+.node-type{
+  color: #FFFFFF;
+  border-radius: 3px;
+  font-size: 14px;
+  background-color: #42b983;
+  text-align: center;
+}
+</style>

+ 8 - 0
src/views/Project/Invite.vue

@@ -73,6 +73,9 @@ export default {
   },
   async mounted() {
     this.relatedProjects = await ProjectAPI.getRelatedProjects(this.$store.state.workspace.currentProjectId);
+    this.relatedProjects.forEach((project) => {
+      project.gitRemoteUrl = this.convert(project.gitRemoteUrl);
+    });
   },
   methods: {
     copy,
@@ -94,6 +97,11 @@ export default {
       });
       this.targetCode = '';
     },
+    convert(url) {
+      const u = new URL(url);
+      u.host = 'seecoder-gitlab-gitlab.seec.seecoder.cn';
+      return u.href;
+    },
     handleChange(projectId) {
       WorkspaceDispatcher.changeProject(projectId);
       this.$router.push(`/project/${projectId}/schedule`);

+ 5 - 5
src/views/Project/Pipeline.vue

@@ -207,9 +207,9 @@
               <ElSelect v-if="key === 'apiTestIds'" v-model="config.configs[key]" multiple >
                 <el-option
                   v-for="item in apiTestOptions"
-                  :key="item.testId"
-                  :label="item.testName"
-                  :value="item.testId">
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id">
                 </el-option>
               </ElSelect>
               <ElInput v-else-if="key === 'nginxConfig'"
@@ -351,7 +351,7 @@
 </template>
 
 <script>
-import { APITestAPI, PipelineAPI } from '@/api';
+import { InterfaceTestAPI, PipelineAPI } from '@/api';
 
 import { formatter } from '@/utils/time';
 import { copy } from '@/utils/clipboard';
@@ -413,7 +413,7 @@ export default {
     this.productions = await PipelineAPI.getDeploymentsByProjectId(this.$store.state.workspace.currentProjectId);
     this.templates = await PipelineAPI.getPipelineTemplates();
     this.stages = await PipelineAPI.getStages();
-    this.apiTestOptions = await APITestAPI.getAPITestInfosByProjectId(this.$store.state.workspace.currentProjectId);
+    this.apiTestOptions = await InterfaceTestAPI.getAll(this.$store.state.workspace.currentProjectId);
     this.loading = false;
   },
   methods: {

+ 2 - 4
src/views/Project/Schedule.vue

@@ -121,10 +121,8 @@ export default {
       detailDrawer: false,
       details: [
         {
-          type: '需求(Requirement)',
-          explain: '● 需求是⽤户的⼀种期望,是⽤户为了解决问题或达到某些目标所需要的条件或能力。\n'
-                    + '● 是系统或系统部件为了满⾜合同、标准、规范或其他正式⽂档所规定的要求⽽需要具备的条件或能⼒。\n'
-                    + '● 对上述中的一个条件或一种能⼒的⼀种⽂档化表述。\n'
+          type: '战略举措(Epic)',
+          explain: '● 项目中的关键战略举措,可以是重大的业务方向,也可以是重大的技术演进\n'
                     + '● 这里需求的粒度较大,需要继续分解为Feature;并通过Feature继续细化为Story来完成开发和迭代。',
         },
         {