This commit is contained in:
liqiang-fit2cloud 2023-01-12 09:43:15 +08:00
commit b55cdc4405
41 changed files with 1005 additions and 593 deletions

View File

@ -428,9 +428,6 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
} else { } else {
String url = this.getUrl(); String url = this.getUrl();
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
url = url.replace(this.getPort(), "10990");
}
if (StringUtils.isEmpty(url)) { if (StringUtils.isEmpty(url)) {
MSException.throwException("请重新选择环境"); MSException.throwException("请重新选择环境");
} }

View File

@ -78,6 +78,7 @@ public class MsTCPSampler extends MsTestElement {
private String jsonDataStruct; private String jsonDataStruct;
private String rawDataStruct; private String rawDataStruct;
private boolean customizeReq; private boolean customizeReq;
private Boolean isRefEnvironment;
@Override @Override
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, MsParameter msParameter) { public void toHashTree(HashTree tree, List<MsTestElement> hashTree, MsParameter msParameter) {

View File

@ -9,6 +9,8 @@ import io.metersphere.api.dto.definition.request.ElementUtil;
import io.metersphere.api.dto.definition.request.MsScenario; import io.metersphere.api.dto.definition.request.MsScenario;
import io.metersphere.api.dto.definition.request.ParameterConfig; import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler;
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO; import io.metersphere.api.dto.plan.TestPlanApiScenarioInfoDTO;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.dto.scenario.environment.item.MsScenarioEnv; import io.metersphere.api.dto.scenario.environment.item.MsScenarioEnv;
@ -69,24 +71,32 @@ public class ApiScenarioEnvService {
// 过滤掉禁用的步骤 // 过滤掉禁用的步骤
hashTree = hashTree.stream().filter(item -> item.isEnable()).collect(Collectors.toList()); hashTree = hashTree.stream().filter(item -> item.isEnable()).collect(Collectors.toList());
} }
List<Boolean> hasFullUrlList = new ArrayList<>();
for (MsTestElement testElement : hashTree) { for (MsTestElement testElement : hashTree) {
this.formatElement(testElement, env); this.formatElement(testElement, env, hasFullUrlList);
if (CollectionUtils.isNotEmpty(testElement.getHashTree())) { if (CollectionUtils.isNotEmpty(testElement.getHashTree()) && !hasFullUrlList.contains(false)) {
getHashTree(testElement.getHashTree(), env); getHashTree(testElement.getHashTree(), env, hasFullUrlList);
}
if (hasFullUrlList.contains(false)) {
env.setFullUrl(false);
break;
} }
} }
return env; return env;
} }
private void getHashTree(List<MsTestElement> tree, ScenarioEnv env) { private void getHashTree(List<MsTestElement> tree, ScenarioEnv env, List<Boolean> hasFullUrlList) {
try { try {
// 过滤掉禁用的步骤 // 过滤掉禁用的步骤
tree = tree.stream().filter(item -> item.isEnable()).collect(Collectors.toList()); tree = tree.stream().filter(item -> item.isEnable()).collect(Collectors.toList());
for (MsTestElement element : tree) { for (MsTestElement element : tree) {
this.formatElement(element, env); this.formatElement(element, env, hasFullUrlList);
if (CollectionUtils.isNotEmpty(element.getHashTree())) { if (CollectionUtils.isNotEmpty(element.getHashTree()) && !hasFullUrlList.contains(false)) {
getHashTree(element.getHashTree(), env); getHashTree(element.getHashTree(), env, hasFullUrlList);
}
if (hasFullUrlList.contains(false)) {
break;
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -94,7 +104,7 @@ public class ApiScenarioEnvService {
} }
} }
private void formatElement(MsTestElement testElement, ScenarioEnv env) { private void formatElement(MsTestElement testElement, ScenarioEnv env, List<Boolean> hasFullUrlList) {
if (StringUtils.equals(MsTestElementConstants.REF.name(), testElement.getReferenced())) { if (StringUtils.equals(MsTestElementConstants.REF.name(), testElement.getReferenced())) {
if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) { if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) {
MsHTTPSamplerProxy http = (MsHTTPSamplerProxy) testElement; MsHTTPSamplerProxy http = (MsHTTPSamplerProxy) testElement;
@ -105,7 +115,7 @@ public class ApiScenarioEnvService {
if (!StringUtils.equalsIgnoreCase(http.getReferenced(), ElementConstants.STEP_CREATED) if (!StringUtils.equalsIgnoreCase(http.getReferenced(), ElementConstants.STEP_CREATED)
|| (http.getIsRefEnvironment() != null && http.getIsRefEnvironment())) { || (http.getIsRefEnvironment() != null && http.getIsRefEnvironment())) {
env.getProjectIds().add(http.getProjectId()); env.getProjectIds().add(http.getProjectId());
env.setFullUrl(false); hasFullUrlList.add(false);
} }
} else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER) } else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER)
|| StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) { || StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) {
@ -114,18 +124,18 @@ public class ApiScenarioEnvService {
ApiTestCase testCase = apiTestCaseMapper.selectByPrimaryKey(testElement.getId()); ApiTestCase testCase = apiTestCaseMapper.selectByPrimaryKey(testElement.getId());
if (testCase != null) { if (testCase != null) {
env.getProjectIds().add(testCase.getProjectId()); env.getProjectIds().add(testCase.getProjectId());
env.setFullUrl(false); hasFullUrlList.add(false);
} }
} else { } else {
ApiDefinition apiDefinition = apiDefinitionService.get(testElement.getId()); ApiDefinition apiDefinition = apiDefinitionService.get(testElement.getId());
if (apiDefinition != null) { if (apiDefinition != null) {
env.getProjectIds().add(apiDefinition.getProjectId()); env.getProjectIds().add(apiDefinition.getProjectId());
env.setFullUrl(false); hasFullUrlList.add(false);
} }
} }
} else { } else {
env.getProjectIds().add(testElement.getProjectId()); env.getProjectIds().add(testElement.getProjectId());
env.setFullUrl(false); hasFullUrlList.add(false);
} }
} else if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO) && StringUtils.isEmpty(testElement.getProjectId())) { } else if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO) && StringUtils.isEmpty(testElement.getProjectId())) {
ApiScenarioWithBLOBs apiScenario = apiScenarioMapper.selectByPrimaryKey(testElement.getId()); ApiScenarioWithBLOBs apiScenario = apiScenarioMapper.selectByPrimaryKey(testElement.getId());
@ -141,18 +151,14 @@ public class ApiScenarioEnvService {
if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) { if (StringUtils.equals(testElement.getType(), ElementConstants.HTTP_SAMPLER)) {
// 校验是否是全路径 // 校验是否是全路径
MsHTTPSamplerProxy httpSamplerProxy = (MsHTTPSamplerProxy) testElement; MsHTTPSamplerProxy httpSamplerProxy = (MsHTTPSamplerProxy) testElement;
if (httpSamplerProxy.isCustomizeReq()) { checkCustomEnv(env, httpSamplerProxy.isCustomizeReq(), httpSamplerProxy.getProjectId(), httpSamplerProxy.getIsRefEnvironment(), httpSamplerProxy.getReferenced(), hasFullUrlList);
env.getProjectIds().add(httpSamplerProxy.getProjectId());
env.setFullUrl(httpSamplerProxy.getIsRefEnvironment() == null ? true : !httpSamplerProxy.getIsRefEnvironment());
} else if (!StringUtils.equalsIgnoreCase(httpSamplerProxy.getReferenced(), ElementConstants.STEP_CREATED) || (httpSamplerProxy.getIsRefEnvironment() != null && httpSamplerProxy.getIsRefEnvironment())) {
env.getProjectIds().add(httpSamplerProxy.getProjectId());
env.setFullUrl(false);
}
} else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER) } else if (StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) {
|| StringUtils.equals(testElement.getType(), ElementConstants.TCP_SAMPLER)) { MsTCPSampler tcpSampler = (MsTCPSampler) testElement;
env.getProjectIds().add(testElement.getProjectId()); checkCustomEnv(env, tcpSampler.isCustomizeReq(), tcpSampler.getProjectId(), tcpSampler.getIsRefEnvironment(), tcpSampler.getReferenced(), hasFullUrlList);
env.setFullUrl(false); } else if (StringUtils.equals(testElement.getType(), ElementConstants.JDBC_SAMPLER)) {
MsJDBCSampler jdbcSampler = (MsJDBCSampler) testElement;
checkCustomEnv(env, jdbcSampler.isCustomizeReq(), jdbcSampler.getProjectId(), jdbcSampler.getIsRefEnvironment(), jdbcSampler.getReferenced(), hasFullUrlList);
} }
} }
if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO) if (StringUtils.equals(testElement.getType(), ElementConstants.SCENARIO)
@ -161,6 +167,17 @@ public class ApiScenarioEnvService {
} }
} }
private void checkCustomEnv(ScenarioEnv env, boolean customizeReq, String projectId, Boolean isRefEnvironment, String referenced, List<Boolean> hasFullUrlList) {
if (customizeReq) {
env.getProjectIds().add(projectId);
hasFullUrlList.add(isRefEnvironment == null ? true : !isRefEnvironment);
} else if (!StringUtils.equalsIgnoreCase(referenced, ElementConstants.STEP_CREATED)
|| (isRefEnvironment != null && isRefEnvironment)) {
env.getProjectIds().add(projectId);
hasFullUrlList.add(false);
}
}
/** /**
* 设置场景的运行环境 环境组/环境JSON * 设置场景的运行环境 环境组/环境JSON
* *

View File

@ -166,7 +166,7 @@ public class MsDebugListener extends AbstractListenerElement implements SampleLi
transactionResult.getResponseResult().setConsole(console); transactionResult.getResponseResult().setConsole(console);
//对响应内容进行进一步解析和处理 //对响应内容进行进一步解析和处理
RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(transactionResult); RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(transactionResult);
if (StringUtils.equals(dto.getRunMode(), ApiRunMode.DEFINITION.name())) { if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name())) {
apiDefinitionEnvService.setEnvAndPoolName(transactionResult, expandDTO); apiDefinitionEnvService.setEnvAndPoolName(transactionResult, expandDTO);
} }
dto.setContent("result_" + JSON.toJSONString(expandDTO)); dto.setContent("result_" + JSON.toJSONString(expandDTO));
@ -176,7 +176,7 @@ public class MsDebugListener extends AbstractListenerElement implements SampleLi
requestResult.getResponseResult().setConsole(console); requestResult.getResponseResult().setConsole(console);
//对响应内容进行进一步解析和处理 //对响应内容进行进一步解析和处理
RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(requestResult); RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(requestResult);
if (StringUtils.equals(dto.getRunMode(), ApiRunMode.DEFINITION.name())) { if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name())) {
apiDefinitionEnvService.setEnvAndPoolName(requestResult, expandDTO); apiDefinitionEnvService.setEnvAndPoolName(requestResult, expandDTO);
} }
dto.setContent("result_" + JSON.toJSONString(expandDTO)); dto.setContent("result_" + JSON.toJSONString(expandDTO));

View File

@ -74,7 +74,7 @@ public class MsKafkaListener {
LoggerUtil.info("接收到执行结果:", record.key()); LoggerUtil.info("接收到执行结果:", record.key());
if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) { if (ObjectUtils.isNotEmpty(record.value()) && WebSocketUtil.has(record.key().toString())) {
MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class); MsgDTO dto = JSONUtil.parseObject(record.value(), MsgDTO.class);
if (StringUtils.equals(ApiRunMode.DEFINITION.name(), dto.getRunMode()) && dto.getContent().startsWith("result_")) { if (StringUtils.equalsAnyIgnoreCase(dto.getRunMode(), ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name()) && dto.getContent().startsWith("result_")) {
ApiDefinitionEnvService apiDefinitionEnvService = CommonBeanFactory.getBean(ApiDefinitionEnvService.class); ApiDefinitionEnvService apiDefinitionEnvService = CommonBeanFactory.getBean(ApiDefinitionEnvService.class);
apiDefinitionEnvService.setEnvAndPoolName(dto); apiDefinitionEnvService.setEnvAndPoolName(dto);
} }

View File

@ -230,7 +230,7 @@
</include> </include>
</if> </if>
<if test="${condition}.exec_result != null"> <if test="${condition}.exec_result != null">
and (t1.status and (t3.status
<choose> <choose>
<when test='${condition}.exec_result.operator == "in"'> <when test='${condition}.exec_result.operator == "in"'>
in in
@ -238,7 +238,7 @@
#{v} #{v}
</foreach> </foreach>
<if test="${condition}.exec_result.value.contains('') or ${condition}.exec_result.value.contains('PENDING')"> <if test="${condition}.exec_result.value.contains('') or ${condition}.exec_result.value.contains('PENDING')">
or t1.status is null or t1.status = '' or t3.status is null or t3.status = ''
</if> </if>
<if test="${condition}.exec_result != null"> <if test="${condition}.exec_result != null">
) )
@ -250,10 +250,10 @@
#{v} #{v}
</foreach> </foreach>
<if test="${condition}.exec_result != null and (${condition}.exec_result.value.contains('') or ${condition}.exec_result.value.contains('PENDING'))"> <if test="${condition}.exec_result != null and (${condition}.exec_result.value.contains('') or ${condition}.exec_result.value.contains('PENDING'))">
and t1.status is not null and t1.status != '' and t3.status is not null and t3.status != ''
</if> </if>
<if test="${condition}.exec_result != null and !${condition}.exec_result.value.contains('PENDING')"> <if test="${condition}.exec_result != null and !${condition}.exec_result.value.contains('PENDING')">
or t1.status is null or t1.status = '' or t3.status is null or t3.status = ''
</if> </if>
<if test="${condition}.exec_result != null"> <if test="${condition}.exec_result != null">
) )
@ -406,7 +406,7 @@
a.path, a.path,
a.protocol, a.protocol,
t1.tags, t1.tags,
t1.status AS execResult, t3.status AS execResult,
t1.last_result_id AS lastResultId, t1.last_result_id AS lastResultId,
project.NAME AS project_name, project.NAME AS project_name,
t1.delete_time, t1.delete_time,

View File

@ -253,6 +253,7 @@ public class TestResultService {
ApiTestCaseWithBLOBs apiTestCase = new ApiTestCaseWithBLOBs(); ApiTestCaseWithBLOBs apiTestCase = new ApiTestCaseWithBLOBs();
apiTestCase.setLastResultId(dto.getReportId()); apiTestCase.setLastResultId(dto.getReportId());
apiTestCase.setId(dto.getTestId()); apiTestCase.setId(dto.getTestId());
apiTestCase.setStatus(record.getStatus());
apiTestCaseService.updateByPrimaryKeySelective(apiTestCase); apiTestCaseService.updateByPrimaryKeySelective(apiTestCase);
} }

View File

@ -372,10 +372,10 @@ export default {
} }
}, },
_getCurrentUserId() { _getCurrentUserId() {
const {id, userGroups} = getCurrentUser(); const { id, userGroups } = getCurrentUser();
if (userGroups) { if (userGroups) {
// //
let index = userGroups.findIndex(ug => ug.sourceId === getCurrentProjectID()); let index = userGroups.findIndex((ug) => ug.sourceId === getCurrentProjectID());
if (index !== -1) { if (index !== -1) {
return id; return id;
} }
@ -786,6 +786,7 @@ export default {
let automationData = this.$router.resolve({ let automationData = this.$router.resolve({
name: 'ApiAutomationWithQuery', name: 'ApiAutomationWithQuery',
params: { params: {
versionId: 'default',
redirectID: getUUID(), redirectID: getUUID(),
dataType: 'scenario', dataType: 'scenario',
dataSelectRange: 'edit:' + resource.id, dataSelectRange: 'edit:' + resource.id,

View File

@ -72,6 +72,18 @@
<el-input v-model="request.port" size="small" /> <el-input v-model="request.port" size="small" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="2">
<el-form-item>
<el-checkbox
v-if="isCustomizeReq"
class="is-ref-environment"
v-model="request.isRefEnvironment"
@change="setDomain"
:disabled="request.disabled">
{{ $t('api_test.request.refer_to_environment') }}
</el-checkbox>
</el-form-item>
</el-col>
</el-row> </el-row>
</el-form> </el-form>
</div> </div>

View File

@ -355,6 +355,7 @@ export default {
let automationData = this.$router.resolve({ let automationData = this.$router.resolve({
name: 'ApiAutomationWithQuery', name: 'ApiAutomationWithQuery',
params: { params: {
versionId: 'default',
redirectID: getUUID(), redirectID: getUUID(),
dataType: 'scenario', dataType: 'scenario',
dataSelectRange: 'edit:' + resource.id, dataSelectRange: 'edit:' + resource.id,

View File

@ -84,7 +84,7 @@
v-model="controller.countController.interval" v-model="controller.countController.interval"
:disabled="controller.disabled" :disabled="controller.disabled"
:placeholder="$t('commons.millisecond')" :placeholder="$t('commons.millisecond')"
:max="1000 * 10000000" :max="1000 * 1000000"
:min="0" :min="0"
:step="1000" :step="1000"
size="small" /> size="small" />
@ -129,7 +129,7 @@
:disabled="controller.disabled" :disabled="controller.disabled"
size="small" size="small"
:placeholder="$t('commons.millisecond')" :placeholder="$t('commons.millisecond')"
:max="1000 * 10000000" :max="1000 * 1000000"
:min="0" :min="0"
:step="1000" /> :step="1000" />
<span class="ms-span ms-radio">ms</span> <span class="ms-span ms-radio">ms</span>
@ -165,12 +165,11 @@
:disabled="controller.disabled" :disabled="controller.disabled"
size="small" size="small"
:placeholder="$t('commons.millisecond')" :placeholder="$t('commons.millisecond')"
:max="1000 * 10000000" :max="1000 * 1000000"
:min="3000" :min="3000"
:step="1000" /> :step="1000" />
<span class="ms-span ms-radio">ms</span> <span class="ms-span ms-radio">ms</span>
</div> </div>
<template v-slot:debugStepCode> <template v-slot:debugStepCode>
<span v-if="node.data.testing" class="ms-test-running"> <span v-if="node.data.testing" class="ms-test-running">
<i class="el-icon-loading" style="font-size: 16px" /> <i class="el-icon-loading" style="font-size: 16px" />

View File

@ -162,6 +162,7 @@ export default {
let automationData = this.$router.resolve({ let automationData = this.$router.resolve({
name: 'ApiAutomationWithQuery', name: 'ApiAutomationWithQuery',
params: { params: {
versionId: 'default',
redirectID: getUUID(), redirectID: getUUID(),
dataType: 'scenario', dataType: 'scenario',
dataSelectRange: 'edit:' + response.data.id, dataSelectRange: 'edit:' + response.data.id,

View File

@ -73,6 +73,7 @@ export default {
home = this.$router.resolve({ home = this.$router.resolve({
name: 'ApiDefinitionWithQuery', name: 'ApiDefinitionWithQuery',
params: { params: {
versionId: 'default',
redirectID: uuid, redirectID: uuid,
dataType: dataType, dataType: dataType,
dataSelectRange: selectRange, dataSelectRange: selectRange,
@ -84,6 +85,7 @@ export default {
home = this.$router.resolve({ home = this.$router.resolve({
name: 'ApiAutomationWithQuery', name: 'ApiAutomationWithQuery',
params: { params: {
versionId: 'default',
redirectID: uuid, redirectID: uuid,
dataType: dataType, dataType: dataType,
dataSelectRange: selectRange, dataSelectRange: selectRange,

View File

@ -246,6 +246,7 @@ export default {
let automationData = this.$router.resolve({ let automationData = this.$router.resolve({
name: 'ApiAutomationWithQuery', name: 'ApiAutomationWithQuery',
params: { params: {
versionId: 'default',
redirectID: getUUID(), redirectID: getUUID(),
dataType: 'scenario', dataType: 'scenario',
dataSelectRange: 'edit:' + resource.id, dataSelectRange: 'edit:' + resource.id,

View File

@ -179,7 +179,11 @@ export default {
computed: { computed: {
isSqlType() { isSqlType() {
return ( return (
this.currentProtocol === 'SQL' && this.response.responseResult.responseCode === '200' && this.mode === 'table' this.currentProtocol === 'SQL'
&& this.response
&& this.response.responseResult
&& this.response.responseResult.responseCode === '200'
&& this.mode === 'table'
); );
}, },
responseResult() { responseResult() {

View File

@ -22,7 +22,7 @@
:style="isFixed ? 'opacity:100%; position: relative;z-index: 666;': 'opacity: 95%;position: fixed'" :style="isFixed ? 'opacity:100%; position: relative;z-index: 666;': 'opacity: 95%;position: fixed'"
@mouseenter.native="collapseOpen" @mouseenter.native="collapseOpen"
@mouseleave.native="collapseClose"> @mouseleave.native="collapseClose">
<ms-aside-header :sideTheme="sideTheme" :isCollapse="isCollapse"/> <ms-aside-header :sideTheme="sideTheme" :isCollapse="isCollapse" :title="sysTitle"/>
<ms-aside-menus :sideTheme="sideTheme" :color="color" :isCollapse="isCollapse"/> <ms-aside-menus :sideTheme="sideTheme" :color="color" :isCollapse="isCollapse"/>
<div class="ms-header-fixed" v-show="!isCollapse"> <div class="ms-header-fixed" v-show="!isCollapse">
<svg-icon iconClass="pushpin" class-name="ms-menu-pin" v-if="isFixed" @click.native="fixedChange(false)"/> <svg-icon iconClass="pushpin" class-name="ms-menu-pin" v-if="isFixed" @click.native="fixedChange(false)"/>
@ -72,6 +72,7 @@ export default {
headerHeight: "0px", headerHeight: "0px",
isFixed: false, isFixed: false,
sideTheme: "", sideTheme: "",
sysTitle: undefined
}; };
}, },
computed: { computed: {
@ -127,6 +128,11 @@ export default {
if (response.data && response.data[7] && response.data[7].paramValue) { if (response.data && response.data[7] && response.data[7].paramValue) {
this.sideTheme = response.data[7].paramValue; this.sideTheme = response.data[7].paramValue;
} }
if (response.data && response.data[6] && response.data[6].paramValue) {
this.sysTitle = response.data[6].paramValue;
}
let title = response.data[4].paramValue; let title = response.data[4].paramValue;
if (title) { if (title) {
document.title = title; document.title = title;

View File

@ -178,6 +178,9 @@ export default {
if (title) { if (title) {
document.title = title; document.title = title;
} }
if (response.data[0].paramValue) {
this.shortcutIcon();
}
}) })
}) })
@ -196,6 +199,7 @@ export default {
this.rules = this.getDefaultRules(); this.rules = this.getDefaultRules();
} }
}); });
}, },
created: function () { created: function () {
document.addEventListener("keydown", this.watchEnter); document.addEventListener("keydown", this.watchEnter);
@ -296,6 +300,13 @@ export default {
}); });
} }
}, },
shortcutIcon() {
let link = document.querySelector("link[rel*='icon']") || document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = '/display/file/logo';
document.getElementsByTagName('head')[0].appendChild(link);
},
redirectAuth(authId) { redirectAuth(authId) {
if (authId === 'LDAP' || authId === 'LOCAL') { if (authId === 'LDAP' || authId === 'LOCAL') {
return; return;

View File

@ -25,16 +25,15 @@ export default {
menuKey: 0, menuKey: 0,
}; };
}, },
computed: {
title() {
return localStorage.getItem("sysTitle") || "MeterSphere";
}
},
props: { props: {
sideTheme: String, sideTheme: String,
isCollapse: { isCollapse: {
type: Boolean, type: Boolean,
default: true, default: true,
},
title: {
type: String,
default: "MeterSphere"
} }
}, },
}; };

View File

@ -1,15 +1,37 @@
<template> <template>
<ms-table-column <ms-table-column
prop="tags" prop="tags"
sortable
:field="field" :field="field"
:fields-width="fieldsWidth" :fields-width="fieldsWidth"
sortable :show-overflow-tooltip="false"
:label="$t('api_test.automation.tag')" :label="$t('api_test.automation.tag')"
min-width="200px"> min-width="200px">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-tag v-for="(name, index) in scope.row.tags" :key="index" type="success" effect="plain" <span v-if="scope.row.tags && scope.row.tags.length === 1">
:content="name" style="margin-left: 0px; margin-right: 2px"/> <ms-tag
<span/> v-for="(name, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:content="name"
style="margin-left: 0; margin-right: 2px"
/>
</span>
<el-tooltip class="item" effect="dark" placement="top" :enterable="false" v-else>
<div v-html="scope.row.tags ? scope.row.tags.join(''): ''" slot="content"></div>
<div class="oneLine">
<ms-tag
v-for="(name, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:content="name"
style="margin-left: 0; margin-right: 2px"
/>
<span/>
</div>
</el-tooltip>
</template> </template>
</ms-table-column> </ms-table-column>
</template> </template>
@ -28,5 +50,9 @@ export default {
</script> </script>
<style scoped> <style scoped>
.oneLine {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style> </style>

View File

@ -554,8 +554,8 @@ const message = {
title: '显示设置', title: '显示设置',
logo: '系统 LOGO', logo: '系统 LOGO',
loginLogo: '登录页左上角 LOGO', loginLogo: '登录页左上角 LOGO',
loginImage: '登页面右侧图片', loginImage: '登页面右侧图片',
loginTitle: '登页面提示信息', loginTitle: '登页面提示信息',
pageTitle: '页面 Title', pageTitle: '页面 Title',
sysTitle: '系统名称', sysTitle: '系统名称',
theme_style: '菜单风格设置', theme_style: '菜单风格设置',

View File

@ -553,8 +553,8 @@ const message = {
title: '顯示設置', title: '顯示設置',
logo: '系統 LOGO', logo: '系統 LOGO',
loginLogo: '登錄頁左上角 LOGO', loginLogo: '登錄頁左上角 LOGO',
loginImage: '登頁面右側圖片', loginImage: '登頁面右側圖片',
loginTitle: '登頁面提示信息', loginTitle: '登頁面提示信息',
pageTitle: '頁面 Title', pageTitle: '頁面 Title',
sysTitle: '系統名稱', sysTitle: '系統名稱',
theme_style: '菜單風格設置', theme_style: '菜單風格設置',

View File

@ -36,7 +36,7 @@
<update id="batchUpdateByResourceIds"> <update id="batchUpdateByResourceIds">
update ${tableName} update ${tableName}
<include refid="updateValueColumn"/> <include refid="updateValueColumn"/>
where resource_id in where field_id = #{record.fieldId} and resource_id in
<foreach collection="resourceIds" item="resourceId" separator="," open="(" close=")"> <foreach collection="resourceIds" item="resourceId" separator="," open="(" close=")">
#{resourceId} #{resourceId}
</foreach> </foreach>

View File

@ -155,6 +155,8 @@ public interface ExtTestCaseMapper {
List<TestCase> getMaintainerMap(@Param("request") QueryTestCaseRequest request); List<TestCase> getMaintainerMap(@Param("request") QueryTestCaseRequest request);
List<TestCase> getMaintainerMapForPlanRepeat(@Param("request") QueryTestCaseRequest request);
List<TestCaseDTO> getForNodeEdit(@Param("ids") List<String> ids); List<TestCaseDTO> getForNodeEdit(@Param("ids") List<String> ids);
List<CustomFieldResourceCompatibleDTO> getForCompatibleCustomField(String projectId, int offset, int pageSize); List<CustomFieldResourceCompatibleDTO> getForCompatibleCustomField(String projectId, int offset, int pageSize);

View File

@ -1216,6 +1216,14 @@
and T2.case_id is null and T2.case_id is null
order by test_case.`order` desc, test_case.sort desc order by test_case.`order` desc, test_case.sort desc
</select> </select>
<select id="getMaintainerMapForPlanRepeat" resultType="io.metersphere.base.domain.TestCase">
select test_case.id as id, test_case.maintainer as maintainer
from test_case as test_case
<include refid="notInQueryWhereCondition"/>
order by test_case.`order` desc, test_case.sort desc
</select>
<select id="getForNodeEdit" resultType="io.metersphere.dto.TestCaseDTO"> <select id="getForNodeEdit" resultType="io.metersphere.dto.TestCaseDTO">
select test_case.id, test_case.node_id, test_case.node_path select test_case.id, test_case.node_id, test_case.node_path
from test_case where node_id in from test_case where node_id in

View File

@ -48,6 +48,8 @@ public class IssueExcelData implements Serializable {
@ExcelIgnore @ExcelIgnore
private Boolean addFlag; private Boolean addFlag;
@ExcelIgnore @ExcelIgnore
private Boolean updateFlag = true;
@ExcelIgnore
private String title; private String title;
@ExcelIgnore @ExcelIgnore
private String description; private String description;

View File

@ -121,6 +121,12 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
issueExcelData.setId(issues.getId()); issueExcelData.setId(issues.getId());
issueExcelData.setAddFlag(Boolean.FALSE); issueExcelData.setAddFlag(Boolean.FALSE);
updateList.add(issueExcelData); updateList.add(issueExcelData);
} else {
// 不覆盖模式
issueExcelData.setId(issues.getId());
issueExcelData.setAddFlag(Boolean.FALSE);
issueExcelData.setUpdateFlag(Boolean.FALSE);
updateList.add(issueExcelData);
} }
} }
} }
@ -148,7 +154,7 @@ public class IssueExcelListener extends AnalysisEventListener<Map<Integer, Strin
} }
if (CollectionUtils.isNotEmpty(updateList)) { if (CollectionUtils.isNotEmpty(updateList)) {
List<IssuesUpdateRequest> issues = updateList.stream().map(this::convertToIssue).collect(Collectors.toList()); List<IssuesUpdateRequest> issues = updateList.stream().filter(IssueExcelData::getUpdateFlag).map(this::convertToIssue).collect(Collectors.toList());
issuesService.updateImportData(issues); issuesService.updateImportData(issues);
} }
} }

View File

@ -298,6 +298,7 @@ public class TestPlanMessageService {
result.put("apiCaseUnExecuteCount", v); result.put("apiCaseUnExecuteCount", v);
break; break;
case "errorreportresult": case "errorreportresult":
case "fakeerror":
result.put("apiCaseErrorReportCount", v); result.put("apiCaseErrorReportCount", v);
break; break;
default: default:
@ -318,12 +319,14 @@ public class TestPlanMessageService {
result.put("apiScenarioSuccessCount", v); result.put("apiScenarioSuccessCount", v);
break; break;
case "fail": case "fail":
case "error":
result.put("apiScenarioFailedCount", v); result.put("apiScenarioFailedCount", v);
break; break;
case "unexecute": case "unexecute":
result.put("apiScenarioUnExecuteCount", v); result.put("apiScenarioUnExecuteCount", v);
break; break;
case "errorreportresult": case "errorreportresult":
case "fakeerror":
result.put("apiScenarioErrorReportCount", v); result.put("apiScenarioErrorReportCount", v);
break; break;
default: default:

View File

@ -53,6 +53,7 @@ import io.metersphere.service.ApiPoolDebugService;
import io.metersphere.xpack.track.dto.IssuesDao; import io.metersphere.xpack.track.dto.IssuesDao;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ExecutorType;
@ -548,10 +549,14 @@ public class TestPlanService {
public void testPlanRelevance(PlanCaseRelevanceRequest request) { public void testPlanRelevance(PlanCaseRelevanceRequest request) {
LinkedHashMap<String, String> userMap; LinkedHashMap<String, String> userMap;
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getPlanId()); TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getPlanId());
boolean isSelectAll = request.getRequest() != null && request.getRequest().isSelectAll(); boolean isSelectAll = request.getRequest() != null && request.getRequest().isSelectAll();
if (isSelectAll) { if (isSelectAll) {
List<TestCase> maintainerMap = extTestCaseMapper.getMaintainerMap(request.getRequest()); List<TestCase> maintainerMap;
if (BooleanUtils.isTrue(testPlan.getRepeatCase())) {
maintainerMap = extTestCaseMapper.getMaintainerMapForPlanRepeat(request.getRequest());
} else {
maintainerMap = extTestCaseMapper.getMaintainerMap(request.getRequest());
}
userMap = maintainerMap.stream() userMap = maintainerMap.stream()
.collect(LinkedHashMap::new, (m, v) -> m.put(v.getId(), v.getMaintainer()), LinkedHashMap::putAll); .collect(LinkedHashMap::new, (m, v) -> m.put(v.getId(), v.getMaintainer()), LinkedHashMap::putAll);
} else { } else {

View File

@ -384,11 +384,15 @@ public class TestCaseService {
checkTestCustomNum(testCase); checkTestCustomNum(testCase);
testCase.setUpdateTime(System.currentTimeMillis()); testCase.setUpdateTime(System.currentTimeMillis());
// 同步缺陷与需求的关联关系 try {
updateThirdPartyIssuesLink(testCase); // 同步缺陷与需求的关联关系
updateThirdPartyIssuesLink(testCase);
// 同步用例与需求的关联关系 // 同步用例与需求的关联关系
addDemandHyperLink(testCase, "edit"); addDemandHyperLink(testCase, "edit");
} catch (Exception e) {
LogUtil.error(e);
}
if (StringUtils.isEmpty(testCase.getDemandId())) { if (StringUtils.isEmpty(testCase.getDemandId())) {
testCase.setDemandId(StringUtils.EMPTY); testCase.setDemandId(StringUtils.EMPTY);
@ -425,6 +429,9 @@ public class TestCaseService {
return; return;
} }
Project project = baseProjectService.getProjectById(testCase.getProjectId()); Project project = baseProjectService.getProjectById(testCase.getProjectId());
if (!StringUtils.equals(project.getPlatform(), IssuesManagePlatform.AzureDevops.name())) {
return;
}
IssuesRequest issuesRequest = new IssuesRequest(); IssuesRequest issuesRequest = new IssuesRequest();
if (!issuesService.isThirdPartTemplate(project)) { if (!issuesService.isThirdPartTemplate(project)) {
issuesRequest.setDefaultCustomFields(issuesService.getDefaultCustomFields(testCase.getProjectId())); issuesRequest.setDefaultCustomFields(issuesService.getDefaultCustomFields(testCase.getProjectId()));
@ -446,6 +453,7 @@ public class TestCaseService {
if (StringUtils.isBlank(testCase.getVersionId())) { if (StringUtils.isBlank(testCase.getVersionId())) {
return; return;
} }
testCase.setLatest(false);
TestCaseExample example = new TestCaseExample(); TestCaseExample example = new TestCaseExample();
example.createCriteria().andIdEqualTo(testCase.getId()) example.createCriteria().andIdEqualTo(testCase.getId())
.andVersionIdEqualTo(testCase.getVersionId()); .andVersionIdEqualTo(testCase.getVersionId());

View File

@ -111,7 +111,6 @@ public class TestReviewTestCaseService {
} }
public int deleteTestCase(DeleteRelevanceRequest request) { public int deleteTestCase(DeleteRelevanceRequest request) {
checkReviewer(request.getReviewId());
return testCaseReviewTestCaseMapper.deleteByPrimaryKey(request.getId()); return testCaseReviewTestCaseMapper.deleteByPrimaryKey(request.getId());
} }
@ -130,21 +129,7 @@ public class TestReviewTestCaseService {
return testCaseReviewTestCaseMapper.updateByExampleSelective(record, example); return testCaseReviewTestCaseMapper.updateByExampleSelective(record, example);
} }
private void checkReviewer(String reviewId) {
List<String> userIds = testCaseReviewService.getTestCaseReviewerIds(reviewId);
String currentId = SessionUtils.getUser().getId();
TestCaseReview caseReview = testCaseReviewMapper.selectByPrimaryKey(reviewId);
String creator = StringUtils.EMPTY;
if (caseReview != null) {
creator = caseReview.getCreator();
}
if (!userIds.contains(currentId) && !StringUtils.equals(creator, currentId)) {
MSException.throwException("没有权限,不能解除用例关联!");
}
}
public void deleteTestCaseBatch(TestReviewCaseBatchRequest request) { public void deleteTestCaseBatch(TestReviewCaseBatchRequest request) {
checkReviewer(request.getReviewId());
ServiceUtils.getSelectAllIds(request, request.getCondition(), ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extTestReviewCaseMapper.selectIds((QueryCaseReviewRequest) query)); (query) -> extTestReviewCaseMapper.selectIds((QueryCaseReviewRequest) query));

View File

@ -593,7 +593,9 @@ export default {
// websock // websock
// websock // websock
this.$refs.apiCaseResult.open(reportId); this.$refs.apiCaseResult.open(reportId);
run(row.id, reportId); setTimeout(() => {
run(row.id, reportId);
}, 3000);
}, },
handleTestEnd(reportId) { handleTestEnd(reportId) {
if (this.runningReport.has(reportId)) { if (this.runningReport.has(reportId)) {

View File

@ -6,8 +6,8 @@
:modal-append-to-body="false" :modal-append-to-body="false"
size="100%" size="100%"
ref="drawer" ref="drawer"
v-loading="loading"> v-loading="loading"
>
<template> <template>
<el-row> <el-row>
<el-col :span="17"> <el-col :span="17">
@ -16,11 +16,13 @@
<el-scrollbar> <el-scrollbar>
<el-header> <el-header>
<el-row type="flex" class="head-bar"> <el-row type="flex" class="head-bar">
<el-col :span="4"> <el-col :span="4">
<el-button plain size="mini" <el-button
icon="el-icon-back" plain
@click="cancel">{{ $t('test_track.return') }} size="mini"
icon="el-icon-back"
@click="cancel"
>{{ $t("test_track.return") }}
</el-button> </el-button>
</el-col> </el-col>
@ -35,9 +37,9 @@
:pre-page-data="prePageData" :pre-page-data="prePageData"
@pre="handlePre" @pre="handlePre"
@next="handleNext" @next="handleNext"
:list="testCases"/> :list="testCases"
/>
</el-col> </el-col>
</el-row> </el-row>
<el-row class="head-bar"> <el-row class="head-bar">
@ -47,82 +49,144 @@
class="test-case-name" class="test-case-name"
type="text" type="text"
:disabled="!hasProjectPermission" :disabled="!hasProjectPermission"
@click="openTestTestCase(testCase)"> @click="openTestTestCase(testCase)"
<span >
class="title-link" <span
:title="testCase.name" class="title-link"
:style="{'max-width': titleWith + 'px'}"> :title="testCase.name"
{{ testCase.customNum }}-{{ testCase.name }} :style="{ 'max-width': titleWith + 'px' }"
</span> >
{{ testCase.customNum }}-{{ testCase.name }}
</span>
</el-button> </el-button>
</el-divider> </el-divider>
</el-col> </el-col>
</el-row> </el-row>
</el-header> </el-header>
<div class="case_container"> <div class="case_container">
<el-form :model="testCase"> <el-form :model="testCase">
<el-row> <el-row>
<el-col :span="7"> <el-col :span="7">
<el-form-item :label="$t('test_track.case.module')" prop="nodePath" <el-form-item
:label-width="formLabelWidth"> :label="$t('test_track.case.module')"
prop="nodePath"
:label-width="formLabelWidth"
>
{{ testCase.nodePath }} {{ testCase.nodePath }}
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="7"> <el-col :span="7">
<el-form-item :label="$t('test_track.plan.plan_project')" prop="projectName" <el-form-item
:label-width="formLabelWidth"> :label="$t('test_track.plan.plan_project')"
prop="projectName"
:label-width="formLabelWidth"
>
{{ testCase.projectName }} {{ testCase.projectName }}
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item :label="$t('test_track.plan.load_case.execution_status')" <el-form-item
:label-width="formLabelWidth"> :label="
<status-table-item :value="originalStatus"/> $t('test_track.plan.load_case.execution_status')
"
:label-width="formLabelWidth"
>
<status-table-item :value="originalStatus" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-form ref="customFieldForm" <el-form
v-if="isCustomFiledActive" ref="customFieldForm"
class="case-form"> v-if="isCustomFiledActive"
class="case-form"
>
<el-row> <el-row>
<el-col :span="7" v-for="(item, index) in testCaseTemplate.customFields" :key="index"> <el-col
<el-form-item :label-width="formLabelWidth" :span="7"
:label="item.system ? $t(systemNameMap[item.name]) : item.name" v-for="(item, index) in testCaseTemplate.customFields"
:prop="item.name"> :key="index"
<custom-filed-component :disabled="true" :data="item" :form="{}" prop="defaultValue"/> >
<el-form-item
:label-width="formLabelWidth"
:label="
item.system
? $t(systemNameMap[item.name])
: item.name
"
:prop="item.name"
>
<custom-filed-component
:disabled="true"
:data="item"
:form="{}"
prop="defaultValue"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
<form-rich-text-item :label-width="formLabelWidth" :disabled="true" <form-rich-text-item
:title="$t('test_track.case.prerequisite')" :data="testCase" :label-width="formLabelWidth"
prop="prerequisite"/> :disabled="true"
<step-change-item :disable="true" :label-width="formLabelWidth" :form="testCase"/> :title="$t('test_track.case.prerequisite')"
<test-plan-case-step-results-item :label-width="formLabelWidth" :is-read-only="isReadOnly" :data="testCase"
v-if="testCase.stepModel === 'STEP'" :test-case="testCase"/> prop="prerequisite"
<form-rich-text-item :label-width="formLabelWidth" v-if="testCase.stepModel === 'TEXT'" />
:disabled="true" :title="$t('test_track.case.step_desc')" :data="testCase" <step-change-item
prop="stepDescription"/> :disable="true"
<form-rich-text-item :label-width="formLabelWidth" v-if="testCase.stepModel === 'TEXT'" :label-width="formLabelWidth"
:disabled="true" :title="$t('test_track.case.expected_results')" :form="testCase"
:data="testCase" prop="expectedResult"/> />
<form-rich-text-item :label-width="formLabelWidth" v-if="testCase.stepModel === 'TEXT'" <test-plan-case-step-results-item
:title="$t('test_track.plan_view.actual_result')" :label-width="formLabelWidth"
:data="testCase" prop="actualResult"/> :is-read-only="isReadOnly"
v-if="testCase.stepModel === 'STEP'"
:test-case="testCase"
/>
<form-rich-text-item
:label-width="formLabelWidth"
v-if="testCase.stepModel === 'TEXT'"
:disabled="true"
:title="$t('test_track.case.step_desc')"
:data="testCase"
prop="stepDescription"
/>
<form-rich-text-item
:label-width="formLabelWidth"
v-if="testCase.stepModel === 'TEXT'"
:disabled="true"
:title="$t('test_track.case.expected_results')"
:data="testCase"
prop="expectedResult"
/>
<form-rich-text-item
:label-width="formLabelWidth"
v-if="testCase.stepModel === 'TEXT'"
:title="$t('test_track.plan_view.actual_result')"
:data="testCase"
prop="actualResult"
/>
<el-form-item
<el-form-item :label="$t('test_track.case.other_info')" :label-width="formLabelWidth"> :label="$t('test_track.case.other_info')"
<test-case-edit-other-info :plan-id="testCase.planId" v-if="otherInfoActive" @openTest="openTest" :label-width="formLabelWidth"
:is-test-plan-edit="true" >
@syncRelationGraphOpen="syncRelationGraphOpen" <test-case-edit-other-info
:read-only="true" :is-test-plan="true" :project-id="testCase.projectId" :plan-id="testCase.planId"
:form="testCase" :case-id="testCase.caseId" ref="otherInfo"/> v-if="otherInfoActive"
@openTest="openTest"
:is-test-plan-edit="true"
@syncRelationGraphOpen="syncRelationGraphOpen"
:read-only="true"
:is-test-plan="true"
:project-id="testCase.projectId"
:form="testCase"
:case-id="testCase.caseId"
ref="otherInfo"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</el-scrollbar> </el-scrollbar>
@ -135,11 +199,13 @@
:test-case="testCase" :test-case="testCase"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:origin-status="originalStatus" :origin-status="originalStatus"
@saveCase="saveCase"/> @saveCase="saveCase"
/>
<review-comment <review-comment
default-type="PLAN" default-type="PLAN"
:case-id="testCase.caseId" :case-id="testCase.caseId"
ref="comment"/> ref="comment"
/>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
@ -148,30 +214,40 @@
</template> </template>
<script> <script>
import TestPlanTestCaseStatusButton from '../../../common/TestPlanTestCaseStatusButton'; import TestPlanTestCaseStatusButton from "../../../common/TestPlanTestCaseStatusButton";
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'; import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
import {getUUID, listenGoBack, removeGoBackListener} from "metersphere-frontend/src/utils" import {
import {hasPermission} from "metersphere-frontend/src/utils/permission"; getUUID,
listenGoBack,
removeGoBackListener,
} from "metersphere-frontend/src/utils";
import { hasPermission } from "metersphere-frontend/src/utils/permission";
import TestCaseAttachment from "@/business/case/components/TestCaseAttachment"; import TestCaseAttachment from "@/business/case/components/TestCaseAttachment";
import CaseComment from "@/business/case/components/CaseComment"; import CaseComment from "@/business/case/components/CaseComment";
import MsPreviousNextButton from "metersphere-frontend/src/components/MsPreviousNextButton"; import MsPreviousNextButton from "metersphere-frontend/src/components/MsPreviousNextButton";
import ReviewComment from "@/business/review/commom/ReviewComment"; import ReviewComment from "@/business/review/commom/ReviewComment";
import {buildTestCaseOldFields, parseCustomField} from "metersphere-frontend/src/utils/custom_field"; import {
buildTestCaseOldFields,
parseCustomField,
} from "metersphere-frontend/src/utils/custom_field";
import FormRichTextItem from "metersphere-frontend/src/components/FormRichTextItem"; import FormRichTextItem from "metersphere-frontend/src/components/FormRichTextItem";
import MsFormDivider from "metersphere-frontend/src/components/MsFormDivider"; import MsFormDivider from "metersphere-frontend/src/components/MsFormDivider";
import TestCaseEditOtherInfo from "@/business/case/components/TestCaseEditOtherInfo"; import TestCaseEditOtherInfo from "@/business/case/components/TestCaseEditOtherInfo";
import CustomFiledComponent from "metersphere-frontend/src/components/template/CustomFiledComponent"; import CustomFiledComponent from "metersphere-frontend/src/components/template/CustomFiledComponent";
import {SYSTEM_FIELD_NAME_MAP} from "metersphere-frontend/src/utils/table-constants"; import { SYSTEM_FIELD_NAME_MAP } from "metersphere-frontend/src/utils/table-constants";
import IssueDescriptionTableItem from "@/business/issue/IssueDescriptionTableItem"; import IssueDescriptionTableItem from "@/business/issue/IssueDescriptionTableItem";
import StepChangeItem from "@/business/case/components/StepChangeItem"; import StepChangeItem from "@/business/case/components/StepChangeItem";
import TestCaseStepItem from "@/business/case/components/TestCaseStepItem"; import TestCaseStepItem from "@/business/case/components/TestCaseStepItem";
import TestPlanCaseStepResultsItem from "@/business/plan/view/comonents/functional/TestPlanCaseStepResultsItem"; import TestPlanCaseStepResultsItem from "@/business/plan/view/comonents/functional/TestPlanCaseStepResultsItem";
import TestPlanFunctionalExecute from "@/business/plan/view/comonents/functional/TestPlanFunctionalExecute"; import TestPlanFunctionalExecute from "@/business/plan/view/comonents/functional/TestPlanFunctionalExecute";
import StatusTableItem from "@/business/common/tableItems/planview/StatusTableItem"; import StatusTableItem from "@/business/common/tableItems/planview/StatusTableItem";
import {testPlanTestCaseEdit, testPlanTestCaseGet} from "@/api/remote/plan/test-plan-test-case"; import {
import {testPlanEditStatus} from "@/api/remote/plan/test-plan"; testPlanTestCaseEdit,
import {getTestTemplate} from "@/api/custom-field-template"; testPlanTestCaseGet,
import {checkProjectPermission} from "@/api/testCase"; } from "@/api/remote/plan/test-plan-test-case";
import { testPlanEditStatus } from "@/api/remote/plan/test-plan";
import { getTestTemplate } from "@/api/custom-field-template";
import { checkProjectPermission } from "@/api/testCase";
export default { export default {
name: "FunctionalTestCaseEdit", name: "FunctionalTestCaseEdit",
@ -190,7 +266,7 @@ export default {
MsPreviousNextButton, MsPreviousNextButton,
CaseComment, CaseComment,
TestPlanTestCaseStatusButton, TestPlanTestCaseStatusButton,
TestCaseAttachment TestCaseAttachment,
}, },
data() { data() {
return { return {
@ -201,34 +277,34 @@ export default {
index: 0, index: 0,
editor: ClassicEditor, editor: ClassicEditor,
test: {}, test: {},
activeTab: 'detail', activeTab: "detail",
users: [], users: [],
tableData: [], tableData: [],
comments: [], comments: [],
testCaseTemplate: {}, testCaseTemplate: {},
formLabelWidth: '100px', formLabelWidth: "100px",
isCustomFiledActive: false, isCustomFiledActive: false,
otherInfoActive: true, otherInfoActive: true,
isReadOnly: false, isReadOnly: false,
testCases: [], testCases: [],
originalStatus: '', originalStatus: "",
titleWith: 0 titleWith: 0,
}; };
}, },
props: { props: {
total: { total: {
type: Number type: Number,
}, },
searchParam: { searchParam: {
type: Object type: Object,
}, },
pageNum: Number, pageNum: Number,
pageSize: { pageSize: {
type: Number, type: Number,
default: 1 default: 1,
}, },
nextPageData: Object, nextPageData: Object,
prePageData: Object prePageData: Object,
}, },
computed: { computed: {
systemNameMap() { systemNameMap() {
@ -243,18 +319,18 @@ export default {
removeGoBackListener(this.handleClose); removeGoBackListener(this.handleClose);
this.showDialog = false; this.showDialog = false;
this.searchParam.status = null; this.searchParam.status = null;
this.$emit('update:search-param', this.searchParam); this.$emit("update:search-param", this.searchParam);
}, },
cancel() { cancel() {
this.handleClose(); this.handleClose();
this.$emit('refreshTable'); this.$emit("refreshTable");
}, },
getOption(param) { getOption(param) {
let formData = new FormData(); let formData = new FormData();
let url = '/test/case/edit/testPlan'; let url = "/test/case/edit/testPlan";
if (this.$refs.otherInfo && this.$refs.otherInfo.uploadList) { if (this.$refs.otherInfo && this.$refs.otherInfo.uploadList) {
this.$refs.otherInfo.uploadList.forEach(f => { this.$refs.otherInfo.uploadList.forEach((f) => {
formData.append("file", f); formData.append("file", f);
}); });
} }
@ -271,17 +347,20 @@ export default {
return key === "file" ? undefined : value; return key === "file" ? undefined : value;
}); });
formData.append('request', new Blob([requestJson], { formData.append(
type: "application/json " "request",
})); new Blob([requestJson], {
type: "application/json ",
})
);
return { return {
method: 'POST', method: "POST",
url: url, url: url,
data: formData, data: formData,
headers: { headers: {
'Content-Type': undefined "Content-Type": undefined,
} },
}; };
}, },
saveCase() { saveCase() {
@ -302,12 +381,18 @@ export default {
result.actualResult = this.testCase.steptResults[i].actualResult; result.actualResult = this.testCase.steptResults[i].actualResult;
result.executeResult = this.testCase.steptResults[i].executeResult; result.executeResult = this.testCase.steptResults[i].executeResult;
if (result.actualResult && result.actualResult.length > 500) { if (result.actualResult && result.actualResult.length > 500) {
this.$warning(this.$t('test_track.plan_view.actual_result') this.$warning(
+ this.$t('test_track.length_less_than') + '500'); this.$t("test_track.plan_view.actual_result") +
this.$t("test_track.length_less_than") +
"500"
);
return; return;
} }
if (result.executeResult === 'Failure' && this.testCase.status === 'Pass') { if (
this.$warning(this.$t('test_track.plan_view.execute_tip')); result.executeResult === "Failure" &&
this.testCase.status === "Pass"
) {
this.$warning(this.$t("test_track.plan_view.execute_tip"));
this.testCase.status = this.originalStatus; this.testCase.status = this.originalStatus;
return; return;
} }
@ -315,20 +400,19 @@ export default {
} }
param.results = JSON.stringify(param.results); param.results = JSON.stringify(param.results);
param.actualResult = this.testCase.actualResult; param.actualResult = this.testCase.actualResult;
testPlanTestCaseEdit(param) testPlanTestCaseEdit(param).then(() => {
.then(() => { this.$request(option);
this.$request(option);
this.$success(this.$t('commons.save_success')); this.$success(this.$t("commons.save_success"));
this.updateTestCases(param); this.updateTestCases(param);
this.setPlanStatus(this.testCase.planId); this.setPlanStatus(this.testCase.planId);
if (this.testCase.comment) { if (this.testCase.comment) {
this.$refs.comment.getComments(); this.$refs.comment.getComments();
this.testCase.comment = ''; this.testCase.comment = "";
} }
this.originalStatus = this.testCase.status; this.originalStatus = this.testCase.status;
}); });
}, },
updateTestCases(param) { updateTestCases(param) {
for (let i = 0; i < this.testCases.length; i++) { for (let i = 0; i < this.testCases.length; i++) {
@ -345,7 +429,7 @@ export default {
if (this.isLastData()) { if (this.isLastData()) {
return; return;
} else if (this.index === this.testCases.length - 1) { } else if (this.index === this.testCases.length - 1) {
this.$emit('nextPage'); this.$emit("nextPage");
this.index = 0; this.index = 0;
return; return;
} }
@ -354,20 +438,23 @@ export default {
this.reloadOtherInfo(); this.reloadOtherInfo();
}, },
isLastData() { isLastData() {
return this.index === this.testCases.length - 1 && this.pageNum === this.pageTotal; return (
this.index === this.testCases.length - 1 &&
this.pageNum === this.pageTotal
);
}, },
reloadOtherInfo() { reloadOtherInfo() {
this.otherInfoActive = false; this.otherInfoActive = false;
this.$nextTick(() => { this.$nextTick(() => {
this.otherInfoActive = true; this.otherInfoActive = true;
}) });
}, },
handlePre() { handlePre() {
if (this.index === 0 && this.pageNum === 1) { if (this.index === 0 && this.pageNum === 1) {
this.$warning('已经是第一页'); this.$warning("已经是第一页");
return; return;
} else if (this.index === 0) { } else if (this.index === 0) {
this.$emit('prePage'); this.$emit("prePage");
this.index = this.pageSize - 1; this.index = this.pageSize - 1;
return; return;
} }
@ -378,66 +465,69 @@ export default {
getTestCase(id) { getTestCase(id) {
this.loading = true; this.loading = true;
// id TestPlanTestCase id // id TestPlanTestCase id
testPlanTestCaseGet(id) testPlanTestCaseGet(id).then((response) => {
.then(response => { this.loading = false;
this.loading = false; let item = {};
let item = {}; Object.assign(item, response.data);
Object.assign(item, response.data); if (item.results) {
if (item.results) { item.results = JSON.parse(item.results);
item.results = JSON.parse(item.results); } else if (item.steps) {
} else if (item.steps) { item.results = [item.steps.length];
item.results = [item.steps.length]; }
} if (item.issues) {
if (item.issues) { item.issues = JSON.parse(item.issues);
item.issues = JSON.parse(item.issues); } else {
} else { item.issues = {};
item.issues = {}; }
} item.steps = JSON.parse(item.steps);
item.steps = JSON.parse(item.steps); if (!item.stepModel) {
if (!item.stepModel) { item.stepModel = "STEP";
item.stepModel = 'STEP'; }
} item.steptResults = [];
item.steptResults = []; if (item.steps) {
if (item.steps) { for (let i = 0; i < item.steps.length; i++) {
for (let i = 0; i < item.steps.length; i++) { if (item.results) {
if (item.results) { if (item.results[i]) {
if (item.results[i]) { item.steps[i].actualResult = item.results[i].actualResult;
item.steps[i].actualResult = item.results[i].actualResult; item.steps[i].executeResult = item.results[i].executeResult;
item.steps[i].executeResult = item.results[i].executeResult;
}
item.steptResults.push(item.steps[i]);
} else {
item.steptResults.push({
actualResult: '',
executeResult: ''
});
} }
item.steptResults.push(item.steps[i]);
} else {
item.steptResults.push({
actualResult: "",
executeResult: "",
});
} }
} }
this.testCase = item; }
this.originalStatus = this.testCase.status; this.testCase = item;
parseCustomField(this.testCase, this.testCaseTemplate, null, buildTestCaseOldFields(this.testCase)); this.originalStatus = this.testCase.status;
this.testCaseTemplate.customFields.forEach(item => { parseCustomField(
try { this.testCase,
item.defaultValue = JSON.parse(item.defaultValue); this.testCaseTemplate,
} catch (e) { null,
// nothing buildTestCaseOldFields(this.testCase)
} );
}); this.testCaseTemplate.customFields.forEach((item) => {
this.isCustomFiledActive = true; try {
if (!this.testCase.actualResult) { item.defaultValue = JSON.parse(item.defaultValue);
// ,使 } catch (e) {
this.testCase.actualResult = this.testCaseTemplate.actualResult; // nothing
} }
}); });
this.isCustomFiledActive = true;
if (!this.testCase.actualResult) {
// ,使
this.testCase.actualResult = this.testCaseTemplate.actualResult;
}
});
}, },
openTestCaseEdit(testCase, tableData) { openTestCaseEdit(testCase, tableData) {
checkProjectPermission(testCase.projectId) checkProjectPermission(testCase.projectId).then((r) => {
.then(r => { this.hasProjectPermission = r.data;
this.hasProjectPermission = r.data; });
});
this.showDialog = true; this.showDialog = true;
this.activeTab = 'detail'; this.activeTab = "detail";
this.originalStatus = testCase.status; this.originalStatus = testCase.status;
this.setTitleWith(); this.setTitleWith();
@ -454,11 +544,10 @@ export default {
listenGoBack(this.handleClose); listenGoBack(this.handleClose);
let initFuc = this.getTestCase; let initFuc = this.getTestCase;
getTestTemplate(testCase.projectId) getTestTemplate(testCase.projectId).then((template) => {
.then((template) => { this.testCaseTemplate = template;
this.testCaseTemplate = template; initFuc(testCase.id);
initFuc(testCase.id); });
});
if (this.$refs.otherInfo) { if (this.$refs.otherInfo) {
this.$refs.otherInfo.reset(); this.$refs.otherInfo.reset();
} }
@ -466,16 +555,16 @@ export default {
testRun(reportId) { testRun(reportId) {
this.testCase.reportId = reportId; this.testCase.reportId = reportId;
this.saveReport(reportId); this.saveReport(reportId);
this.activeTab = 'result'; this.activeTab = "result";
}, },
testTabChange(data) { testTabChange(data) {
if (this.testCase.type === 'performance' && data.paneName === 'result') { if (this.testCase.type === "performance" && data.paneName === "result") {
this.$refs.performanceTestResult.checkReportStatus(); this.$refs.performanceTestResult.checkReportStatus();
this.$refs.performanceTestResult.init(); this.$refs.performanceTestResult.init();
} }
}, },
saveReport(reportId) { saveReport(reportId) {
testPlanTestCaseEdit({id: this.testCase.id, reportId: reportId}); testPlanTestCaseEdit({ id: this.testCase.id, reportId: reportId });
}, },
openTest(item) { openTest(item) {
const type = item.testType; const type = item.testType;
@ -483,25 +572,34 @@ export default {
switch (type) { switch (type) {
case "performance": { case "performance": {
let performanceData = this.$router.resolve({ let performanceData = this.$router.resolve({
path: '/performance/test/edit/' + id, path: "/performance/test/edit/" + id,
}) });
window.open(performanceData.href, '_blank'); window.open(performanceData.href, "_blank");
break; break;
} }
case "testcase": { case "testcase": {
let caseData = this.$router.resolve({ let caseData = this.$router.resolve({
name: 'ApiDefinition', name: "ApiDefinition",
params: {redirectID: getUUID(), dataType: "apiTestCase", dataSelectRange: 'single:' + id} params: {
versionId: "default",
redirectID: getUUID(),
dataType: "apiTestCase",
dataSelectRange: "single:" + id,
},
}); });
window.open(caseData.href, '_blank'); window.open(caseData.href, "_blank");
break; break;
} }
case "automation": { case "automation": {
let automationData = this.$router.resolve({ let automationData = this.$router.resolve({
name: 'ApiAutomation', name: "ApiAutomation",
params: {redirectID: getUUID(), dataType: "scenario", dataSelectRange: 'edit:' + id} params: {
redirectID: getUUID(),
dataType: "scenario",
dataSelectRange: "edit:" + id,
},
}); });
window.open(automationData.href, '_blank'); window.open(automationData.href, "_blank");
break; break;
} }
} }
@ -519,26 +617,28 @@ export default {
this.relationGraphOpen = val; this.relationGraphOpen = val;
}, },
openTestTestCase(item) { openTestTestCase(item) {
let TestCaseData = this.$router.resolve( let TestCaseData = this.$router.resolve({
{ path: "/track/case/all",
path: '/track/case/all', query: {
query: {redirectID: getUUID(), dataType: "testCase", dataSelectRange: item.caseId, projectId: item.projectId} redirectID: getUUID(),
} dataType: "testCase",
); dataSelectRange: item.caseId,
window.open(TestCaseData.href, '_blank'); projectId: item.projectId,
},
});
window.open(TestCaseData.href, "_blank");
}, },
addPLabel(str) { addPLabel(str) {
return "<p>" + str + "</p>"; return "<p>" + str + "</p>";
}, },
setPlanStatus(planId) { setPlanStatus(planId) {
testPlanEditStatus(planId); testPlanEditStatus(planId);
} },
} },
} };
</script> </script>
<style scoped> <style scoped>
.cast_label { .cast_label {
color: dimgray; color: dimgray;
} }
@ -587,7 +687,7 @@ export default {
} }
.el-switch :deep(.el-switch__label.is-active) { .el-switch :deep(.el-switch__label.is-active) {
color: #409EFF; color: #409eff;
} }
p { p {

View File

@ -1,6 +1,6 @@
<template> <template>
<ms-container> <ms-container>
<ms-main-container class="report-content"> <ms-main-container class="report-content" :class="isShare || isTemplate? 'full-screen-container' : 'with-header-container'" id = "planReportContainer">
<el-card v-loading="loading"> <el-card v-loading="loading">
<test-plan-report-buttons :is-db="isDb" :plan-id="planId" :is-share="isShare" :report="report" <test-plan-report-buttons :is-db="isDb" :plan-id="planId" :is-share="isShare" :report="report"
v-if="!isTemplate && !isShare"/> v-if="!isTemplate && !isShare"/>
@ -190,13 +190,17 @@ export default {
if (!this.isTemplate) { if (!this.isTemplate) {
if (this.isShare) { if (this.isShare) {
getShareTestPlanExtReport(this.shareId, this.planId, this.reportId).then((response) => { getShareTestPlanExtReport(this.shareId, this.planId, this.reportId).then((response) => {
this.runMode = response.data.runMode; if (response.data) {
this.resourcePool = response.data.resourcePool; this.runMode = response.data.runMode;
this.resourcePool = response.data.resourcePool;
}
}); });
} else { } else {
getTestPlanExtReport(this.planId, this.reportId).then((response) => { getTestPlanExtReport(this.planId, this.reportId).then((response) => {
this.runMode = response.data.runMode; if (response.data) {
this.resourcePool = response.data.resourcePool; this.runMode = response.data.runMode;
this.resourcePool = response.data.resourcePool;
}
}); });
} }
} }
@ -336,8 +340,15 @@ export default {
<style scoped> <style scoped>
.with-header-container {
height: calc(100vh - 60px);
}
.full-screen-container {
height: calc(100vh - 10px);
}
.el-card { .el-card {
/*width: 95% !important;*/
padding: 15px; padding: 15px;
} }

View File

@ -74,7 +74,7 @@ export default {
watch: { watch: {
activeName() { activeName() {
let target = document.getElementById(this.activeName); let target = document.getElementById(this.activeName);
target.parentNode.parentNode.parentNode.scrollTop = target.offsetTop - 100; document.getElementById('planReportContainer').scrollTop = target.offsetTop - 100;
}, },
overviewEnable() { overviewEnable() {
this.setData(); this.setData();

View File

@ -1,5 +1,4 @@
<template> <template>
<el-drawer <el-drawer
:before-close="handleClose" :before-close="handleClose"
:visible.sync="showDialog" :visible.sync="showDialog"
@ -7,28 +6,27 @@
:modal-append-to-body="false" :modal-append-to-body="false"
size="100%" size="100%"
ref="drawer" ref="drawer"
v-loading="loading"> v-loading="loading"
>
<template> <template>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="17"> <el-col :span="17">
<div class="container"> <div class="container">
<el-card> <el-card>
<el-scrollbar> <el-scrollbar>
<el-header style="height: 100%">
<el-header style="height: 100%;">
<el-row type="flex" class="head-bar"> <el-row type="flex" class="head-bar">
<el-col :span="2"> <el-col :span="2">
<el-button plain size="mini" <el-button
icon="el-icon-back" plain
@click="cancel">{{ $t('test_track.return') }} size="mini"
icon="el-icon-back"
@click="cancel"
>{{ $t("test_track.return") }}
</el-button> </el-button>
</el-col> </el-col>
<el-col :span="22" class="head-right"> <el-col :span="22" class="head-right">
<ms-previous-next-button <ms-previous-next-button
:index="index" :index="index"
:page-num="pageNum" :page-num="pageNum"
@ -39,90 +37,146 @@
:pre-page-data="prePageData" :pre-page-data="prePageData"
:list="testCases" :list="testCases"
@pre="handlePre" @pre="handlePre"
@next="handleNext"/> @next="handleNext"
/>
</el-col> </el-col>
</el-row> </el-row>
<el-row style="margin-top: 0;"> <el-row style="margin-top: 0">
<el-col> <el-col>
<el-divider content-position="left" class="title-divider"> <el-divider content-position="left" class="title-divider">
<el-button class="test-case-name" type="text" @click="openTestTestCase(testCase)"> <el-button
<span class="test-case-name"
class="title-link" type="text"
:title="testCase.name" @click="openTestTestCase(testCase)"
:style="{'max-width': titleWith + 'px'}"> >
{{ testCase.num }}-{{ testCase.name }} <span
</span> class="title-link"
:title="testCase.name"
:style="{ 'max-width': titleWith + 'px' }"
>
{{ testCase.num }}-{{ testCase.name }}
</span>
</el-button> </el-button>
</el-divider> </el-divider>
</el-col> </el-col>
</el-row> </el-row>
</el-header> </el-header>
<div class="case_container"> <div class="case_container">
<el-form> <el-form>
<el-row> <el-row>
<el-col :span="7"> <el-col :span="7">
<el-form-item :label="$t('test_track.case.module')" prop="nodePath" <el-form-item
:label-width="formLabelWidth"> :label="$t('test_track.case.module')"
prop="nodePath"
:label-width="formLabelWidth"
>
{{ testCase.nodePath }} {{ testCase.nodePath }}
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="7"> <el-col :span="7">
<el-form-item :label="$t('test_track.plan.plan_project')" prop="projectName" <el-form-item
:label-width="formLabelWidth"> :label="$t('test_track.plan.plan_project')"
prop="projectName"
:label-width="formLabelWidth"
>
{{ testCase.projectName }} {{ testCase.projectName }}
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item :label="$t('评审状态')" :label-width="formLabelWidth"> <el-form-item
<status-table-item :value="oldReviewStatus"/> :label="$t('评审状态')"
</el-form-item > :label-width="formLabelWidth"
>
<status-table-item :value="oldReviewStatus" />
</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-form ref="customFieldForm" <el-form
v-if="isCustomFiledActive" ref="customFieldForm"
class="case-form"> v-if="isCustomFiledActive"
class="case-form"
>
<el-row> <el-row>
<el-col :span="7" v-for="(item, index) in testCaseTemplate.customFields" :key="index"> <el-col
<el-form-item :label="item.system ? $t(systemNameMap[item.name]) : item.name" :span="7"
:prop="item.name" v-for="(item, index) in testCaseTemplate.customFields"
:label-width="formLabelWidth"> :key="index"
<custom-filed-component :disabled="true" :data="item" :form="{}" prop="defaultValue"/> >
<el-form-item
:label="
item.system
? $t(systemNameMap[item.name])
: item.name
"
:prop="item.name"
:label-width="formLabelWidth"
>
<custom-filed-component
:disabled="true"
:data="item"
:form="{}"
prop="defaultValue"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
<form-rich-text-item :label-width="formLabelWidth" :disabled="true" <form-rich-text-item
:title="$t('test_track.case.prerequisite')" :label-width="formLabelWidth"
:data="testCase" prop="prerequisite"/> :disabled="true"
<step-change-item :disable="true" :label-width="formLabelWidth" :form="testCase"/> :title="$t('test_track.case.prerequisite')"
<form-rich-text-item :label-width="formLabelWidth" :disabled="true" :data="testCase"
v-if="testCase.stepModel === 'TEXT'" :title="$t('test_track.case.step_desc')" prop="prerequisite"
:data="testCase" prop="stepDescription"/> />
<form-rich-text-item :label-width="formLabelWidth" :disabled="true" <step-change-item
v-if="testCase.stepModel === 'TEXT'" :disable="true"
:title="$t('test_track.case.expected_results')" :data="testCase" :label-width="formLabelWidth"
prop="expectedResult"/> :form="testCase"
/>
<form-rich-text-item
:label-width="formLabelWidth"
:disabled="true"
v-if="testCase.stepModel === 'TEXT'"
:title="$t('test_track.case.step_desc')"
:data="testCase"
prop="stepDescription"
/>
<form-rich-text-item
:label-width="formLabelWidth"
:disabled="true"
v-if="testCase.stepModel === 'TEXT'"
:title="$t('test_track.case.expected_results')"
:data="testCase"
prop="expectedResult"
/>
<test-case-step-item :label-width="formLabelWidth" :read-only="true" <test-case-step-item
v-if="testCase.stepModel === 'STEP'" :form="testCase"/> :label-width="formLabelWidth"
:read-only="true"
v-if="testCase.stepModel === 'STEP'"
:form="testCase"
/>
<el-form-item :label="$t('test_track.case.other_info')" :label-width="formLabelWidth"> <el-form-item
<test-case-edit-other-info @openTest="openTest" :read-only="true" :label="$t('test_track.case.other_info')"
@syncRelationGraphOpen="syncRelationGraphOpen" :label-width="formLabelWidth"
:project-id="projectId" :form="testCase" :case-id="testCase.caseId" >
ref="otherInfo"/> <test-case-edit-other-info
@openTest="openTest"
:read-only="true"
@syncRelationGraphOpen="syncRelationGraphOpen"
:project-id="projectId"
:form="testCase"
:case-id="testCase.caseId"
ref="otherInfo"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-card> </el-card>
</div> </div>
@ -133,30 +187,38 @@
:test-case="testCase" :test-case="testCase"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:origin-status="oldReviewStatus" :origin-status="oldReviewStatus"
@saveCase="saveCase()"/> @saveCase="saveCase()"
/>
<review-comment <review-comment
default-type="REVIEW" default-type="REVIEW"
:case-id="testCase.caseId" :case-id="testCase.caseId"
@saveCaseReview="saveCaseReview" @saveCaseReview="saveCaseReview"
ref="comment"/> ref="comment"
/>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
</el-drawer> </el-drawer>
</template> </template>
<script> <script>
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token"; import { getCurrentProjectID } from "metersphere-frontend/src/utils/token";
import {getUUID, listenGoBack, removeGoBackListener} from "metersphere-frontend/src/utils" import {
getUUID,
listenGoBack,
removeGoBackListener,
} from "metersphere-frontend/src/utils";
import ReviewComment from "@/business/review/commom/ReviewComment"; import ReviewComment from "@/business/review/commom/ReviewComment";
import TestCaseAttachment from "@/business/case/components/TestCaseAttachment"; import TestCaseAttachment from "@/business/case/components/TestCaseAttachment";
import {buildTestCaseOldFields, getTemplate, parseCustomField} from "metersphere-frontend/src/utils/custom_field"; import {
buildTestCaseOldFields,
getTemplate,
parseCustomField,
} from "metersphere-frontend/src/utils/custom_field";
import TestCaseEditOtherInfo from "@/business/case/components/TestCaseEditOtherInfo"; import TestCaseEditOtherInfo from "@/business/case/components/TestCaseEditOtherInfo";
import {SYSTEM_FIELD_NAME_MAP} from "metersphere-frontend/src/utils/table-constants"; import { SYSTEM_FIELD_NAME_MAP } from "metersphere-frontend/src/utils/table-constants";
import FormRichTextItem from "metersphere-frontend/src/components/FormRichTextItem"; import FormRichTextItem from "metersphere-frontend/src/components/FormRichTextItem";
import CustomFiledComponent from "metersphere-frontend/src/components/template/CustomFiledComponent"; import CustomFiledComponent from "metersphere-frontend/src/components/template/CustomFiledComponent";
import StepChangeItem from "@/business/case/components/StepChangeItem"; import StepChangeItem from "@/business/case/components/StepChangeItem";
@ -164,12 +226,12 @@ import TestCaseStepItem from "@/business/case/components/TestCaseStepItem";
import MsPreviousNextButton from "metersphere-frontend/src/components/MsPreviousNextButton"; import MsPreviousNextButton from "metersphere-frontend/src/components/MsPreviousNextButton";
import TestReviewTestCaseExecute from "./TestReviewTestCaseExecute"; import TestReviewTestCaseExecute from "./TestReviewTestCaseExecute";
import StatusTableItem from "@/business/common/tableItems/planview/StatusTableItem"; import StatusTableItem from "@/business/common/tableItems/planview/StatusTableItem";
import {getTestTemplate} from "@/api/custom-field-template"; import { getTestTemplate } from "@/api/custom-field-template";
import { import {
editTestCaseReviewStatus, editTestCaseReviewStatus,
editTestReviewTestCase, editTestReviewTestCase,
getRelateTest, getRelateTest,
getTestReviewTestCase getTestReviewTestCase,
} from "@/api/test-review"; } from "@/api/test-review";
export default { export default {
@ -184,7 +246,7 @@ export default {
FormRichTextItem, FormRichTextItem,
TestCaseEditOtherInfo, TestCaseEditOtherInfo,
ReviewComment, ReviewComment,
TestCaseAttachment TestCaseAttachment,
}, },
data() { data() {
return { return {
@ -193,44 +255,44 @@ export default {
testCase: {}, testCase: {},
index: 0, index: 0,
testCases: [], testCases: [],
readConfig: {toolbar: []}, readConfig: { toolbar: [] },
test: {}, test: {},
activeTab: 'detail', activeTab: "detail",
isFailure: true, isFailure: true,
users: [], users: [],
activeName: 'comment', activeName: "comment",
comments: [], comments: [],
tableData: [], tableData: [],
currentScenario: {}, currentScenario: {},
mark: 'detail', mark: "detail",
api: {}, api: {},
apiCase: {}, apiCase: {},
testCaseTemplate: {}, testCaseTemplate: {},
formLabelWidth: '100px', formLabelWidth: "100px",
isCustomFiledActive: false, isCustomFiledActive: false,
oldReviewStatus: '', oldReviewStatus: "",
titleWith: 0, titleWith: 0,
relationGraphOpen: false, relationGraphOpen: false,
}; };
}, },
props: { props: {
total: { total: {
type: Number type: Number,
}, },
searchParam: { searchParam: {
type: Object type: Object,
}, },
isReadOnly: { isReadOnly: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
pageNum: Number, pageNum: Number,
pageSize: { pageSize: {
type: Number, type: Number,
default: 1 default: 1,
}, },
nextPageData: Object, nextPageData: Object,
prePageData: Object prePageData: Object,
}, },
computed: { computed: {
projectId() { projectId() {
@ -241,7 +303,7 @@ export default {
}, },
pageTotal() { pageTotal() {
return Math.ceil(this.total / this.pageSize); return Math.ceil(this.total / this.pageSize);
} },
}, },
methods: { methods: {
openTest(item) { openTest(item) {
@ -250,25 +312,35 @@ export default {
switch (type) { switch (type) {
case "performance": { case "performance": {
let performanceData = this.$router.resolve({ let performanceData = this.$router.resolve({
path: '/performance/test/edit/' + id, path: "/performance/test/edit/" + id,
}); });
window.open(performanceData.href, '_blank'); window.open(performanceData.href, "_blank");
break; break;
} }
case "testcase": { case "testcase": {
let caseData = this.$router.resolve({ let caseData = this.$router.resolve({
name: 'ApiDefinition', name: "ApiDefinition",
params: {redirectID: getUUID(), dataType: "apiTestCase", dataSelectRange: 'single:' + id} params: {
versionId: "default",
redirectID: getUUID(),
dataType: "apiTestCase",
dataSelectRange: "single:" + id,
},
}); });
window.open(caseData.href, '_blank'); window.open(caseData.href, "_blank");
break; break;
} }
case "automation": { case "automation": {
let automationData = this.$router.resolve({ let automationData = this.$router.resolve({
name: 'ApiAutomation', name: "ApiAutomation",
params: {redirectID: getUUID(), dataType: "scenario", dataSelectRange: 'edit:' + id} params: {
versionId: "default",
redirectID: getUUID(),
dataType: "scenario",
dataSelectRange: "edit:" + id,
},
}); });
window.open(automationData.href, '_blank'); window.open(automationData.href, "_blank");
break; break;
} }
} }
@ -282,7 +354,7 @@ export default {
}, },
cancel() { cancel() {
this.handleClose(); this.handleClose();
this.$emit('refreshTable'); this.$emit("refreshTable");
}, },
saveCase() { saveCase() {
let param = {}; let param = {};
@ -291,22 +363,21 @@ export default {
param.reviewId = this.testCase.reviewId; param.reviewId = this.testCase.reviewId;
param.comment = this.testCase.comment; param.comment = this.testCase.comment;
param.status = this.testCase.reviewStatus; param.status = this.testCase.reviewStatus;
editTestReviewTestCase(param) editTestReviewTestCase(param).then(() => {
.then(() => { this.$success(this.$t("commons.save_success"));
this.$success(this.$t('commons.save_success')); this.updateTestCases(param);
this.updateTestCases(param); this.setReviewStatus(this.testCase.reviewId);
this.setReviewStatus(this.testCase.reviewId);
// //
this.testCases[this.index].status = this.testCase.status; this.testCases[this.index].status = this.testCase.status;
// //
this.oldReviewStatus = this.testCase.reviewStatus; this.oldReviewStatus = this.testCase.reviewStatus;
if (this.testCase.comment) { if (this.testCase.comment) {
this.$refs.comment.getComments(); this.$refs.comment.getComments();
this.testCase.comment = ''; this.testCase.comment = "";
} }
}) });
}, },
saveCaseReview() { saveCaseReview() {
let param = {}; let param = {};
@ -315,18 +386,17 @@ export default {
param.caseId = this.testCase.caseId; param.caseId = this.testCase.caseId;
param.reviewId = this.testCase.reviewId; param.reviewId = this.testCase.reviewId;
param.status = status; param.status = status;
editTestReviewTestCase(param) editTestReviewTestCase(param).then(() => {
.then(() => { this.updateTestCases(param);
this.updateTestCases(param); this.setReviewStatus(this.testCase.reviewId);
this.setReviewStatus(this.testCase.reviewId); this.oldReviewStatus = status;
this.oldReviewStatus = status; //
// this.testCases[this.index].status = status;
this.testCases[this.index].status = status; if (this.index < this.testCases.length - 1) {
if (this.index < this.testCases.length - 1) { this.handleNext();
this.handleNext(); }
} this.$success(this.$t("commons.save_success"));
this.$success(this.$t('commons.save_success')); });
});
}, },
updateTestCases(param) { updateTestCases(param) {
for (let i = 0; i < this.testCases.length; i++) { for (let i = 0; i < this.testCases.length; i++) {
@ -338,12 +408,15 @@ export default {
} }
}, },
handleNext() { handleNext() {
if (this.index === this.testCases.length - 1 && this.pageNum === this.pageTotal) { if (
this.index === this.testCases.length - 1 &&
this.pageNum === this.pageTotal
) {
// //
return; return;
} else if (this.index === this.testCases.length - 1) { } else if (this.index === this.testCases.length - 1) {
// //
this.$emit('nextPage'); this.$emit("nextPage");
this.index = 0; this.index = 0;
return; return;
} }
@ -351,13 +424,16 @@ export default {
this.getTestCase(this.testCases[this.index].id); this.getTestCase(this.testCases[this.index].id);
}, },
isLastData() { isLastData() {
return this.index === this.testCases.length - 1 && this.pageNum === this.pageTotal; return (
this.index === this.testCases.length - 1 &&
this.pageNum === this.pageTotal
);
}, },
handlePre() { handlePre() {
if (this.index === 0 && this.pageNum === 1) { if (this.index === 0 && this.pageNum === 1) {
return; return;
} else if (this.index === 0) { } else if (this.index === 0) {
this.$emit('prePage'); this.$emit("prePage");
this.index = this.pageSize - 1; this.index = this.pageSize - 1;
return; return;
} }
@ -366,31 +442,35 @@ export default {
}, },
getTestCase(id) { getTestCase(id) {
this.loading = true; this.loading = true;
getTestReviewTestCase(id) getTestReviewTestCase(id).then((response) => {
.then((response) => { this.loading = false;
this.loading = false; let item = {};
let item = {}; let data = response.data;
let data = response.data; Object.assign(item, data);
Object.assign(item, data); item.results = JSON.parse(item.results);
item.results = JSON.parse(item.results); if (item.issues) {
if (item.issues) { item.issues = JSON.parse(item.issues);
item.issues = JSON.parse(item.issues); } else {
} else { item.issues = {};
item.issues = {}; }
} item.steps = JSON.parse(item.steps);
item.steps = JSON.parse(item.steps); if (!item.stepModel) {
if (!item.stepModel) { item.stepModel = "STEP";
item.stepModel = 'STEP'; }
} parseCustomField(
parseCustomField(item, this.testCaseTemplate, null, buildTestCaseOldFields(item)); item,
this.isCustomFiledActive = true; this.testCaseTemplate,
this.testCase = item; null,
this.oldReviewStatus = this.testCase.reviewStatus; buildTestCaseOldFields(item)
if (!this.testCase.actualResult) { );
// ,使 this.isCustomFiledActive = true;
this.testCase.actualResult = this.testCaseTemplate.actualResult; this.testCase = item;
} this.oldReviewStatus = this.testCase.reviewStatus;
}); if (!this.testCase.actualResult) {
// ,使
this.testCase.actualResult = this.testCaseTemplate.actualResult;
}
});
}, },
setTitleWith() { setTitleWith() {
this.$nextTick(() => { this.$nextTick(() => {
@ -405,7 +485,7 @@ export default {
this.showDialog = true; this.showDialog = true;
// //
this.oldReviewStatus = testCase.reviewStatus; this.oldReviewStatus = testCase.reviewStatus;
this.activeTab = 'detail'; this.activeTab = "detail";
listenGoBack(this.handleClose); listenGoBack(this.handleClose);
let initFuc = this.getTestCase; let initFuc = this.getTestCase;
this.setTitleWith(); this.setTitleWith();
@ -421,26 +501,34 @@ export default {
} }
} }
getTestTemplate(testCase.projectId) getTestTemplate(testCase.projectId).then((response) => {
.then((response) => { this.testCaseTemplate = response;
this.testCaseTemplate = response; initFuc(testCase.id);
initFuc(testCase.id); });
});
if (this.$refs.otherInfo) { if (this.$refs.otherInfo) {
this.$refs.otherInfo.reset(); this.$refs.otherInfo.reset();
} }
}, },
openTestTestCase(item) { openTestTestCase(item) {
let testCaseData = this.$router.resolve( let testCaseData = this.$router.resolve({
{path: '/track/case/all', query: {redirectID: getUUID(), dataType: "testCase", dataSelectRange: item.caseId}} path: "/track/case/all",
); query: {
window.open(testCaseData.href, '_blank'); redirectID: getUUID(),
dataType: "testCase",
dataSelectRange: item.caseId,
},
});
window.open(testCaseData.href, "_blank");
}, },
getRelatedTest() { getRelatedTest() {
if (this.testCase.method === 'auto' && this.testCase.testId && this.testCase.testId !== 'other') { if (
getRelateTest(this.testCase.type, this.testCase.testId) this.testCase.method === "auto" &&
.then((response) => { this.testCase.testId &&
this.testCase.testId !== "other"
) {
getRelateTest(this.testCase.type, this.testCase.testId).then(
(response) => {
let data = response.data; let data = response.data;
if (data) { if (data) {
this.test = data; this.test = data;
@ -448,8 +536,9 @@ export default {
this.test = {}; this.test = {};
this.$warning(this.$t("test_track.case.relate_test_not_find")); this.$warning(this.$t("test_track.case.relate_test_not_find"));
} }
}); }
} else if (this.testCase.testId === 'other') { );
} else if (this.testCase.testId === "other") {
this.$warning(this.$t("test_track.case.other_relate_test_not_find")); this.$warning(this.$t("test_track.case.other_relate_test_not_find"));
} }
}, },
@ -457,18 +546,20 @@ export default {
editTestCaseReviewStatus(reviewId); editTestCaseReviewStatus(reviewId);
}, },
stepResultChange() { stepResultChange() {
if (this.testCase.method === 'manual') { if (this.testCase.method === "manual") {
this.isFailure = this.testCase.steptResults.filter(s => { this.isFailure =
return s.executeResult === 'Failure' || s.executeResult === 'Blocking'; this.testCase.steptResults.filter((s) => {
}).length > 0; return (
s.executeResult === "Failure" || s.executeResult === "Blocking"
);
}).length > 0;
} }
}, },
} },
} };
</script> </script>
<style scoped> <style scoped>
.border-hidden :deep(.el-textarea__inner) { .border-hidden :deep(.el-textarea__inner) {
border-style: hidden; border-style: hidden;
background-color: white; background-color: white;
@ -509,7 +600,7 @@ export default {
} }
.el-switch :deep(.el-switch__label.is-active) { .el-switch :deep(.el-switch__label.is-active) {
color: #409EFF; color: #409eff;
} }
.container :deep(.el-card__body) { .container :deep(.el-card__body) {

View File

@ -1120,7 +1120,7 @@ export default {
resource.protocol = 'DUBBO' resource.protocol = 'DUBBO'
} }
let definitionData = this.$router.resolve({ let definitionData = this.$router.resolve({
path: '/api/definition/'+getUUID()+'/api/edit:'+resource.id+'/'+resource.projectId+'/'+resource.protocol+'/'+workspaceId, path: '/api/definition/default/' + getUUID() + '/api/edit:' + resource.id + '/' + resource.projectId + '/' + resource.protocol + '/' + workspaceId,
}); });
if (isTurnSpace) { if (isTurnSpace) {
window.open(definitionData.href, '_blank'); window.open(definitionData.href, '_blank');

View File

@ -607,7 +607,7 @@ export default {
edit(row) { edit(row) {
let uuid = getUUID(); let uuid = getUUID();
let apiResolve = this.$router.resolve({ let apiResolve = this.$router.resolve({
path: '/api/automation/'+uuid+'/scenario/edit:'+row.id+'/'+row.projectId+'/'+getCurrentWorkspaceId(), path: '/api/automation/default/' + uuid + '/scenario/edit:' + row.id + '/' + row.projectId + '/' + getCurrentWorkspaceId(),
}); });
window.open(apiResolve.href, '_blank'); window.open(apiResolve.href, '_blank');
}, },

View File

@ -654,12 +654,13 @@ export default {
this.initTableData(); this.initTableData();
}, },
handleEdit(testCase, column) { handleEdit(testCase, column) {
this.$router.push({ let caseResolve = this.$router.resolve({
path: '/track/case/edit', path: '/track/case/edit',
query:{ query: {
caseId:testCase.id, caseId: testCase.id,
}, },
}); });
window.open(caseResolve.href, '_blank');
}, },
refresh() { refresh() {
this.$refs.table.clear(); this.$refs.table.clear();

View File

@ -1,7 +1,8 @@
<template> <template>
<el-card class="table-card"> <el-card class="table-card">
<ms-table <ms-table
:table-is-loading="page.result.loading" v-loading="loading"
row-key="id"
:data="page.data" :data="page.data"
:enableSelection="false" :enableSelection="false"
:condition="page.condition" :condition="page.condition"
@ -10,13 +11,16 @@
:show-select-all="false" :show-select-all="false"
:screen-height="screenHeight" :screen-height="screenHeight"
:remember-order="true" :remember-order="true"
@handlePageChange="getIssues"
@handleRowClick="handleEdit"
:fields.sync="fields" :fields.sync="fields"
:field-key="tableHeaderKey" :field-key="tableHeaderKey"
@refresh="getIssues" :custom-fields="issueTemplate.customFields"
ref="table" @headChange="handleHeadChange"
> @handleRowClick="handleEdit"
@filter="search"
@order="getIssues"
@handlePageChange="getIssues"
ref="table">
<ms-table-column <ms-table-column
v-for="(item) in fields" :key="item.key" v-for="(item) in fields" :key="item.key"
:label="item.label" :label="item.label"
@ -88,6 +92,7 @@
</template> </template>
</ms-table-column> </ms-table-column>
</ms-table> </ms-table>
<ms-table-pagination :change="getIssues" :current-page.sync="page.currentPage" :page-size.sync="page.pageSize" <ms-table-pagination :change="getIssues" :current-page.sync="page.currentPage" :page-size.sync="page.pageSize"
@ -104,46 +109,94 @@ import MsTablePagination from "metersphere-frontend/src/components/pagination/Ta
import { import {
ISSUE_PLATFORM_OPTION, ISSUE_PLATFORM_OPTION,
ISSUE_STATUS_MAP, ISSUE_STATUS_MAP,
SYSTEM_FIELD_NAME_MAP,
TAPD_ISSUE_STATUS_MAP TAPD_ISSUE_STATUS_MAP
} from "metersphere-frontend/src/utils/table-constants"; } from "metersphere-frontend/src/utils/table-constants";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader"; import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import {getDashboardIssues, getIssuePartTemplateWithProject, getIssues, getPlatformOption} from "@/api/issue";
import {
getDashboardIssues,
getIssuePartTemplateWithProject,
getIssues,
getPlatformOption,
} from "@/api/issue";
import { import {
getCustomFieldValue, getCustomFieldValue,
getCustomTableWidth, getCustomTableWidth,
getLastTableSortField, getPageInfo, getTableHeaderWithCustomFields, getLastTableSortField, getCustomFieldFilter, parseCustomFilesForList
getPageDate,
getPageInfo, parseCustomFilesForList
} from "metersphere-frontend/src/utils/tableUtils"; } from "metersphere-frontend/src/utils/tableUtils";
import MsContainer from "metersphere-frontend/src/components/MsContainer"; import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer"; import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token"; import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getProjectMember} from "@/api/user"; import {getProjectMember, getProjectMemberUserFilter} from "@/api/user";
import {getTableHeaderWithCustomFieldsByXpack} from "@/business/component/js/table-head-util";
import {LOCAL} from "metersphere-frontend/src/utils/constants"; import {LOCAL} from "metersphere-frontend/src/utils/constants";
import {TEST_TRACK_ISSUE_LIST} from "metersphere-frontend/src/components/search/search-components";
import {
getAdvSearchCustomField
} from "metersphere-frontend/src/components/search/custom-component";
import MsMarkDownText from "metersphere-frontend/src/components/MsMarkDownText";
import MsReviewTableItem from "@/business/component/MsReviewTableItem"; import MsReviewTableItem from "@/business/component/MsReviewTableItem";
import {getUUID} from "metersphere-frontend/src/utils"; import IssueDescriptionTableItem from "@/business/component/IssueDescriptionTableItem";
export default { export default {
name: "IssueTableList", name: "IssueList",
components: { components: {
MsMainContainer,
MsReviewTableItem, MsReviewTableItem,
MsMarkDownText,
MsMainContainer,
MsContainer, MsContainer,
IssueDescriptionTableItem,
MsTableHeader, MsTableHeader,
MsTablePagination, MsTableButton, MsTableOperators, MsTableColumn, MsTable MsTablePagination, MsTableButton, MsTableOperators, MsTableColumn, MsTable
}, },
data() { data() {
return { return {
page: getPageInfo(), page: getPageInfo({
components: TEST_TRACK_ISSUE_LIST,
custom: false,
}),
fields: [], fields: [],
customFields: [], //
tableHeaderKey: "ISSUE_LIST", tableHeaderKey: "ISSUE_LIST",
fieldsWidth: getCustomTableWidth('ISSUE_LIST'), fieldsWidth: getCustomTableWidth('ISSUE_LIST'),
issueTemplate: {}, issueTemplate: {},
members: [], members: [],
userFilter: [],
isThirdPart: false, isThirdPart: false,
creatorFilters: [],
loading: false,
dataSelectRange: "",
platformOptions: [], platformOptions: [],
platformStatus: [],
platformStatusMap: new Map(),
hasLicense: false,
columns: {
num: {
sortable: true,
minWidth: 100
},
title: {
sortable: true,
minWidth: 120,
},
platform: {
minWidth: 80,
filters: this.platformFilters
},
platformStatus: {
minWidth: 110,
},
creatorName: {
columnKey: 'creator',
minWidth: 100,
filters: this.creatorFilters
},
resourceName: {},
createTime: {
sortable: true,
minWidth: 180
},
caseCount: {}
}
}; };
}, },
activated() { activated() {
@ -192,48 +245,87 @@ export default {
tapdIssueStatusMap() { tapdIssueStatusMap() {
return TAPD_ISSUE_STATUS_MAP; return TAPD_ISSUE_STATUS_MAP;
}, },
systemNameMap() {
return SYSTEM_FIELD_NAME_MAP;
},
projectId() { projectId() {
return getCurrentProjectID(); return getCurrentProjectID();
}, },
workspaceId() {
return getCurrentWorkspaceId();
}
},
created() {
this.getMaintainerOptions();
this.page.result.loading = true;
this.$nextTick(() => {
getProjectMember((data) => {
this.members = data;
});
getIssuePartTemplateWithProject((template) => {
this.initFields(template);
this.page.result.loading = false;
});
this.getIssues();
});
}, },
methods: { methods: {
getCustomFieldValue(row, field) { getCustomFieldValue(row, field, defaultVal) {
let value = getCustomFieldValue(row, field, this.members); let value = getCustomFieldValue(row, field, this.members);
if (!value) { return value ? value : defaultVal;
if (field.name === '处理人') { },
return row.maintainerName; getCustomFieldFilter(field) {
} return getCustomFieldFilter(field, this.userFilter);
}
return value;
}, },
initFields(template) { initFields(template) {
this.issueTemplate = template; if (template.platform === LOCAL) {
if (this.issueTemplate.platform === LOCAL) {
this.isThirdPart = false; this.isThirdPart = false;
} else { } else {
this.isThirdPart = true; this.isThirdPart = true;
} }
this.fields = getTableHeaderWithCustomFieldsByXpack('ISSUE_LIST_HEAD', this.issueTemplate.customFields); let fields = getTableHeaderWithCustomFields('ISSUE_LIST', template.customFields, this.members);
if (!this.isThirdPart) { if (!this.isThirdPart) {
for (let i = 0; i < this.fields.length; i++) { for (let i = 0; i < fields.length; i++) {
if (this.fields[i].id === 'platformStatus') { if (fields[i].id === 'platformStatus') {
this.fields.splice(i, 1); fields.splice(i, 1);
break; break;
} }
} }
// //
let removeField = {id: 'platformStatus', name: 'platformStatus', remove: true}; let removeField = {id: 'platformStatus', name: 'platformStatus', remove: true};
this.issueTemplate.customFields.push(removeField); template.customFields.push(removeField);
} }
this.$nextTick(() => { this.issueTemplate = template;
if (this.$refs.table) { fields.forEach(item => {
this.$refs.table.reloadTable(); if (this.columns[item.id]) {
Object.assign(item, this.columns[item.id]);
if (this.columns[item.id].filters) {
item.filters = this.columns[item.id].filters;
}
} }
}); });
this.fields = fields;
//
this.page.condition.components = this.page.condition.components.filter(item => item.custom !== true);
let comp = getAdvSearchCustomField(this.page.condition, template.customFields);
this.page.condition.components.push(...comp);
this.initCustomFieldValue();
if (this.$refs.table) {
this.$refs.table.reloadTable();
}
},
search() {
//
this.page.currentPage = 1;
this.getIssues();
},
handleHeadChange() {
this.initFields(this.issueTemplate);
},
handleEdit(resource) {
let issueData = this.$router.resolve({path: '/track/issue', query: {id: resource.id}});
window.open(issueData.href, '_blank');
}, },
getIssues() { getIssues() {
if (this.isSelectAll === false) { if (this.isSelectAll === false) {
@ -313,24 +405,11 @@ export default {
}); });
this.loading = false; this.loading = false;
}, },
getMaintainerOptions() {
handleEdit(resource) { getProjectMemberUserFilter((data) => {
let issueData = this.$router.resolve({path: '/track/issue', query: {id: resource.id}}); this.creatorFilters = data;
window.open(issueData.href, '_blank'); });
}, },
},
created() {
this.page.result.loading = true;
this.$nextTick(() => {
getProjectMember((data) => {
this.members = data;
});
getIssuePartTemplateWithProject((template) => {
this.initFields(template);
this.page.result.loading = false;
});
this.getIssues();
});
} }
}; };
</script> </script>

View File

@ -22,7 +22,7 @@
width="80" width="80"
show-overflow-tooltip> show-overflow-tooltip>
<template v-slot:default="scope"> <template v-slot:default="scope">
<span @click="link(scope.row)" style="cursor: pointer;">{{ scope.row.name }}</span> <span @click="link(scope.row)" style="cursor: pointer;">{{ scope.row.num }}</span>
</template> </template>
</el-table-column> </el-table-column>

View File

@ -1,18 +1,32 @@
<template> <template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.automation.case_ref')" :visible.sync="visible" <el-dialog
:modal="false" width="45%" :destroy-on-close="true"> :close-on-click-modal="false"
<span>{{ $t('api_test.automation.scenario_ref') }}</span> :title="$t('api_test.automation.case_ref')"
:visible.sync="visible"
:modal="false"
width="45%"
:destroy-on-close="true"
>
<span>{{ $t("api_test.automation.scenario_ref") }}</span>
<div class="refs" v-loading="scenarioLoading"> <div class="refs" v-loading="scenarioLoading">
<div v-for="(item, index) in scenarioRefs" :key="index" class="el-button--text"> <div
v-for="(item, index) in scenarioRefs"
:key="index"
class="el-button--text"
>
<el-link @click="openScenario(item)"> <el-link @click="openScenario(item)">
{{ item.name }} {{ item.name }}
</el-link> </el-link>
</div> </div>
</div> </div>
<span>{{ $t('api_test.automation.plan_ref') }}</span> <span>{{ $t("api_test.automation.plan_ref") }}</span>
<div class="refs"> <div class="refs">
<div v-for="(item, index) in planRefs" :key="index" class="el-button--text"> <div
v-for="(item, index) in planRefs"
:key="index"
class="el-button--text"
>
<el-link @click="openTestPlan(item)"> <el-link @click="openTestPlan(item)">
{{ item.name }} {{ item.name }}
</el-link> </el-link>
@ -21,8 +35,12 @@
<template v-slot:footer> <template v-slot:footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="visible = false" @keydown.enter.native.prevent> <el-button
{{ $t('commons.confirm') }} type="primary"
@click="visible = false"
@keydown.enter.native.prevent
>
{{ $t("commons.confirm") }}
</el-button> </el-button>
</div> </div>
</template> </template>
@ -30,93 +48,105 @@
</template> </template>
<script> <script>
import {
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token"; getCurrentProjectID,
import {getUUID} from "metersphere-frontend/src/utils"; getCurrentWorkspaceId,
import {getOwnerProjectIds, getProject} from "@/api/project"; } from "metersphere-frontend/src/utils/token";
import {getScenarioReference} from "@/api/scenario"; import { getUUID } from "metersphere-frontend/src/utils";
import { getOwnerProjectIds, getProject } from "@/api/project";
import { getScenarioReference } from "@/api/scenario";
export default { export default {
name: "MsReferenceView", name: "MsReferenceView",
components: {}, components: {},
data() { data() {
return { return {
visible: false, visible: false,
scenarioLoading: false, scenarioLoading: false,
scenarioRefs: [], scenarioRefs: [],
planRefs: [] planRefs: [],
};
},
methods: {
getReferenceData(row) {
if (row.id === undefined) {
return;
}
this.scenarioLoading = true;
this.scenarioRefs = [];
getScenarioReference(row).then((response) => {
this.scenarioRefs = response.data.scenarioList;
this.planRefs = response.data.testPlanList;
this.scenarioLoading = false;
});
},
open(row) {
this.getReferenceData(row);
this.visible = true;
},
openScenario(resource) {
let workspaceId = getCurrentWorkspaceId();
let isTurnSpace = true;
if (resource.projectId !== getCurrentProjectID()) {
isTurnSpace = false;
getProject(resource.projectId).then((response) => {
if (response.data) {
workspaceId = response.data.workspaceId;
isTurnSpace = true;
this.checkPermission(resource, workspaceId, isTurnSpace);
}
});
} else {
this.checkPermission(resource, workspaceId, isTurnSpace);
} }
}, },
methods: { gotoTurn(resource, workspaceId, isTurnSpace) {
getReferenceData(row) { let automationData = this.$router.resolve({
if (row.id === undefined) { name: "ApiAutomation",
return; params: {
} versionId: "default",
this.scenarioLoading = true; redirectID: getUUID(),
this.scenarioRefs = []; dataType: "scenario",
getScenarioReference(row).then(response => { dataSelectRange: "edit:" + resource.id,
this.scenarioRefs = response.data.scenarioList; projectId: resource.projectId,
this.planRefs = response.data.testPlanList; workspaceId: workspaceId,
this.scenarioLoading = false; },
}) });
}, if (isTurnSpace) {
open(row) { window.open(automationData.href, "_blank");
this.getReferenceData(row);
this.visible = true
},
openScenario(resource) {
let workspaceId = getCurrentWorkspaceId();
let isTurnSpace = true
if (resource.projectId !== getCurrentProjectID()) {
isTurnSpace = false;
getProject(resource.projectId).then(response => {
if (response.data) {
workspaceId = response.data.workspaceId;
isTurnSpace = true;
this.checkPermission(resource, workspaceId, isTurnSpace);
}
});
} else {
this.checkPermission(resource, workspaceId, isTurnSpace);
}
},
gotoTurn(resource, workspaceId, isTurnSpace) {
let automationData = this.$router.resolve({
name: 'ApiAutomation',
params: {redirectID: getUUID(), dataType: "scenario", dataSelectRange: 'edit:' + resource.id, projectId: resource.projectId, workspaceId: workspaceId}
});
if (isTurnSpace) {
window.open(automationData.href, '_blank');
}
},
checkPermission(resource, workspaceId, isTurnSpace) {
getOwnerProjectIds().then(res => {
const project = res.data.find(p => p === resource.projectId);
if (!project) {
this.$warning(this.$t('commons.no_permission'));
} else {
this.gotoTurn(resource, workspaceId, isTurnSpace)
}
})
},
openTestPlan(item){
let automationData = this.$router.resolve({
path: '/track/plan/view/' + item.id,
query: { workspaceId: item.workspaceId, projectId: item.projectId, charType: 'api'}
});
window.open(automationData.href, '_blank');
} }
} },
} checkPermission(resource, workspaceId, isTurnSpace) {
getOwnerProjectIds().then((res) => {
const project = res.data.find((p) => p === resource.projectId);
if (!project) {
this.$warning(this.$t("commons.no_permission"));
} else {
this.gotoTurn(resource, workspaceId, isTurnSpace);
}
});
},
openTestPlan(item) {
let automationData = this.$router.resolve({
path: "/track/plan/view/" + item.id,
query: {
workspaceId: item.workspaceId,
projectId: item.projectId,
charType: "api",
},
});
window.open(automationData.href, "_blank");
},
},
};
</script> </script>
<style scoped> <style scoped>
.refs { .refs {
min-height: 50px; min-height: 50px;
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
font-size: 12px; font-size: 12px;
padding-bottom: 10px; padding-bottom: 10px;
} }
</style> </style>