提交 686baea1 编写于 作者: W wenyann

Merge remote-tracking branch 'origin/master'

......@@ -18,4 +18,5 @@ public class QueryAPIReportRequest {
private boolean recent = false;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
}
......@@ -18,5 +18,5 @@ public class QueryAPITestRequest {
private boolean recent = false;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
}
......@@ -33,11 +33,9 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
@Service
......@@ -189,7 +187,7 @@ public class APITestService {
ApiTestExample example = new ApiTestExample();
example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId());
if (apiTestMapper.countByExample(example) > 0) {
return true;
return true;
}
return false;
}
......@@ -292,7 +290,7 @@ public class APITestService {
}
private SaveAPITestRequest getImportApiTest(MultipartFile file, ApiImport apiImport) {
SaveAPITestRequest request = new SaveAPITestRequest();
SaveAPITestRequest request = new SaveAPITestRequest();
request.setName(file.getOriginalFilename());
request.setProjectId("");
request.setScenarioDefinition(apiImport.getScenarios());
......@@ -304,7 +302,8 @@ public class APITestService {
if (name.endsWith(suffix)) {
request.setName(name.substring(0, name.length() - suffix.length()));
}
};
}
;
if (isNameExist(request)) {
request.setName(request.getName() + "_" + request.getId().substring(0, 5));
}
......
......@@ -8,15 +8,109 @@
<result column="user_name" property="userName"/>
</resultMap>
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test="${condition}.name != null">
and api_test.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and api_test.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.projectName != null">
and project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
and api_test.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
and api_test.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.creator != null">
and api_test.user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="list" resultMap="BaseResultMap" parameterType="io.metersphere.api.dto.APITestResult">
select api_test.*, project.name as project_name, user.name as user_name
from api_test
left join project on api_test.project_id = project.id
left join user on api_test.user_id = user.id
<where>
<if test="request.name != null">
and api_test.name like CONCAT('%', #{request.name},'%')
</if>
<choose>
<!--高级-->
<when test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
</include>
</when>
<!--普通-->
<otherwise>
<if test="request.name != null">
and api_test.name like CONCAT('%', #{request.name},'%')
</if>
</otherwise>
</choose>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
......
......@@ -9,6 +9,55 @@
<result column="user_name" property="userName"/>
</resultMap>
<sql id="condition">
<include refid="io.metersphere.base.mapper.ext.ExtApiTestMapper.condition"/>
</sql>
<sql id="combine">
<if test="${condition}.name != null">
and r.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.testName != null">
and t.name
<include refid="condition">
<property name="object" value="${condition}.testName"/>
</include>
</if>
<if test="${condition}.projectName != null">
and project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
and r.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
and r.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.triggerMode != null">
and r.trigger_mode
<include refid="condition">
<property name="object" value="${condition}.triggerMode"/>
</include>
</if>
<if test="${condition}.creator != null">
and r.user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="list" resultMap="BaseResultMap">
SELECT t.name AS test_name,
r.name, r.description, r.id, r.test_id, r.create_time, r.update_time, r.status, r.trigger_mode,
......@@ -17,9 +66,20 @@
LEFT JOIN project ON project.id = t.project_id
LEFT JOIN user ON user.id = r.user_id
<where>
<if test="request.name != null">
AND r.name like CONCAT('%', #{request.name},'%')
</if>
<choose>
<!--高级-->
<when test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
</include>
</when>
<!--普通-->
<otherwise>
<if test="request.name != null">
and r.name like CONCAT('%', #{request.name},'%')
</if>
</otherwise>
</choose>
<if test="request.projectId != null">
AND project.id = #{request.projectId}
</if>
......
......@@ -8,15 +8,69 @@
<result column="user_name" property="userName"/>
</resultMap>
<sql id="condition">
<include refid="io.metersphere.base.mapper.ext.ExtApiTestMapper.condition"/>
</sql>
<sql id="combine">
<if test="${condition}.name != null">
and load_test.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and load_test.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.projectName != null">
and project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
and load_test.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
and load_test.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.creator != null">
and load_test.user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="list" resultMap="BaseResultMap" parameterType="io.metersphere.track.request.testplan.QueryTestPlanRequest">
select load_test.*, project.name as project_name, user.name as user_name
from load_test
left join project on load_test.project_id = project.id
left join user on load_test.user_id = user.id
<where>
<if test="request.name != null">
and load_test.name like CONCAT('%', #{request.name},'%')
</if>
<choose>
<!--高级-->
<when test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
</include>
</when>
<!--普通-->
<otherwise>
<if test="request.name != null">
and load_test.name like CONCAT('%', #{request.name},'%')
</if>
</otherwise>
</choose>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
......
......@@ -15,6 +15,55 @@
id, test_id, name, create_time, update_time, status
</sql>
<sql id="condition">
<include refid="io.metersphere.base.mapper.ext.ExtApiTestMapper.condition"/>
</sql>
<sql id="combine">
<if test="${condition}.name != null">
and ltr.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.testName != null">
and lt.name
<include refid="condition">
<property name="object" value="${condition}.testName"/>
</include>
</if>
<if test="${condition}.projectName != null">
and project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
and ltr.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
and ltr.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.triggerMode != null">
and ltr.trigger_mode
<include refid="condition">
<property name="object" value="${condition}.triggerMode"/>
</include>
</if>
<if test="${condition}.creator != null">
and ltr.user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="getReportList" resultType="io.metersphere.dto.ReportDTO">
select ltr.id, ltr.name, ltr.test_id as testId, ltr.description, user.name as userName, project.name as
projectName, ltr.trigger_mode,
......@@ -26,9 +75,20 @@
JOIN project on project.id = lt.project_id
</if>
<where>
<if test="reportRequest.name != null">
AND ltr.name like CONCAT('%', #{reportRequest.name},'%')
</if>
<choose>
<!--高级-->
<when test="reportRequest.combine != null">
<include refid="combine">
<property name="condition" value="reportRequest.combine"/>
</include>
</when>
<!--普通-->
<otherwise>
<if test="reportRequest.name != null">
AND ltr.name like CONCAT('%', #{reportRequest.name},'%')
</if>
</otherwise>
</choose>
<if test="reportRequest.workspaceId != null">
AND workspace_id = #{reportRequest.workspaceId,jdbcType=VARCHAR}
</if>
......
......@@ -2,13 +2,120 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestCaseMapper">
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test="${condition}.name != null">
and test_case.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.module != null">
and test_case.node_path
<include refid="condition">
<property name="object" value="${condition}.module"/>
</include>
</if>
<if test="${condition}.priority != null">
and test_case.priority
<include refid="condition">
<property name="object" value="${condition}.priority"/>
</include>
</if>
<if test="${condition}.createTime != null">
and test_case.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.type != null">
and test_case.type
<include refid="condition">
<property name="object" value="${condition}.type"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and test_case.update_time
<include refid="condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.method != null">
and test_case.method
<include refid="condition">
<property name="object" value="${condition}.method"/>
</include>
</if>
<if test="${condition}.creator != null">
and test_case.maintainer
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="getTestCaseNames" resultType="io.metersphere.base.domain.TestCase">
select test_case.id, test_case.name, test_case.priority, test_case.type
from test_case
<where>
<if test="request.name != null">
and test_case.name like CONCAT('%', #{request.name},'%')
</if>
<choose>
<!--高级-->
<when test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
</include>
</when>
<!--普通-->
<otherwise>
<if test="request.name != null">
and test_case.name like CONCAT('%', #{request.name},'%')
</if>
</otherwise>
</choose>
<if test="request.projectId != null">
AND test_case.project_id = #{request.projectId}
</if>
......
package io.metersphere.commons.constants;
public enum APITestStatus {
Saved, Starting, Running, Completed, Error
Saved, Starting, Running, Reporting, Completed, Error
}
......@@ -5,12 +5,17 @@ import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import java.util.Objects;
import java.util.Optional;
import static io.metersphere.commons.constants.SessionConstants.ATTR_USER;
public class SessionUtils {
public static String getUserId() {
return Objects.requireNonNull(getUser()).getId();
}
public static SessionUser getUser() {
try {
Subject subject = SecurityUtils.getSubject();
......
......@@ -57,23 +57,17 @@ public class PersonRepoImpl implements PersonRepo {
LdapTemplate ldapTemplate = getConnection();
String filter = getUserFilter();
String[] arr = getUserOu();
String ou = getUserOu();
List<Person> result = null;
for (String ou : arr) {
try {
result = ldapTemplate.search(query().base(ou.trim()).filter(filter, username), getContextMapper());
if (result.size() == 1) {
return result.get(0);
}
} catch (NameNotFoundException e) {
MSException.throwException(Translator.get("login_fail_ou_error"));
} catch (InvalidNameException e) {
MSException.throwException(Translator.get("login_fail_ou_error"));
} catch (InvalidSearchFilterException e) {
MSException.throwException(Translator.get("login_fail_filter_error"));
}
try {
result = ldapTemplate.search(query().base(ou).filter(filter, username), getContextMapper());
} catch (NameNotFoundException e) {
MSException.throwException(Translator.get("login_fail_ou_error"));
} catch (InvalidNameException e) {
MSException.throwException(Translator.get("login_fail_ou_error"));
} catch (InvalidSearchFilterException e) {
MSException.throwException(Translator.get("login_fail_filter_error"));
}
if (result.size() != 1) {
......@@ -93,16 +87,14 @@ public class PersonRepoImpl implements PersonRepo {
return filter;
}
private String[] getUserOu() {
private String getUserOu() {
String ou = service.getValue(ParamConstants.LDAP.OU.getValue());
if (StringUtils.isBlank(ou)) {
MSException.throwException(Translator.get("ldap_ou_is_null"));
}
String[] arr = ou.split("\\|");
return arr;
return ou;
}
protected ContextMapper getContextMapper() {
......
......@@ -14,4 +14,5 @@ public class ReportRequest {
private String workspaceId;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
}
......@@ -5,6 +5,7 @@ import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtUserMapper;
import io.metersphere.base.mapper.ext.ExtUserRoleMapper;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.constants.UserSource;
import io.metersphere.commons.constants.UserStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
......@@ -137,6 +138,7 @@ public class UserService {
user.setUpdateTime(System.currentTimeMillis());
// 默认1:启用状态
user.setStatus(UserStatus.NORMAL);
user.setSource(UserSource.Local.name());
// 密码使用 MD5
user.setPassword(CodingUtil.md5(user.getPassword()));
UserExample userExample = new UserExample();
......
......@@ -25,4 +25,6 @@ public class QueryTestCaseRequest extends TestCase {
private String planId;
private String workspaceId;
private Map<String, Object> combine;
}
......@@ -13,4 +13,5 @@ public class QueryTestPlanRequest extends TestPlanRequest {
private String workspaceId;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
}
alter table user add source varchar(50) null;
\ No newline at end of file
alter table user add source varchar(50) null;
update user set source = 'Local' where source is null;
\ No newline at end of file
......@@ -14,13 +14,13 @@
<el-table-column prop="testName" :label="$t('api_report.test_name')" width="200" show-overflow-tooltip/>
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="150" show-overflow-tooltip/>
<el-table-column prop="userName" :label="$t('api_test.creator')" width="150" show-overflow-tooltip/>
<el-table-column width="250" :label="$t('commons.create_time')" sortable
prop="createTime">
<el-table-column prop="createTime" width="250" :label="$t('commons.create_time')" sortable>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="triggerMode" width="150" :label="'触发方式'" column-key="triggerMode" :filters="triggerFilters">
<el-table-column prop="triggerMode" width="150" :label="$t('commons.trigger_mode.name')"
column-key="triggerMode" :filters="triggerFilters">
<template v-slot:default="scope">
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
</template>
......@@ -34,8 +34,10 @@
</el-table-column>
<el-table-column width="150" :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator-button :tip="$t('api_report.detail')" icon="el-icon-s-data" @exec="handleView(scope.row)" type="primary"/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('api_report.delete')" icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
<ms-table-operator-button :tip="$t('api_report.detail')" icon="el-icon-s-data"
@exec="handleView(scope.row)" type="primary"/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('api_report.delete')"
icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
</template>
</el-table-column>
</el-table>
......@@ -55,16 +57,20 @@
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {getReportConfigs} from "../../common/components/search/search-components";
export default {
components: {
ReportTriggerModeItem,
MsTableOperatorButton,
MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination},
MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination
},
data() {
return {
result: {},
condition: {},
condition: {
components: getReportConfigs()
},
tableData: [],
multipleSelection: [],
currentPage: 1,
......@@ -80,9 +86,9 @@
{text: 'Error', value: 'Error'}
],
triggerFilters: [
{text: '手动', value: 'MANUAL'},
{text: '定时任务', value: 'SCHEDULE'},
{text: 'API', value: 'API'}
{text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'},
{text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'},
{text: this.$t('commons.trigger_mode.api'), value: 'API'}
],
}
},
......@@ -92,13 +98,15 @@
},
methods: {
search() {
search(combine) {
// 只有在点击高级搜索的查询按钮时combine才有值
let condition = combine ? {combine: combine} : this.condition;
if (this.testId !== 'all') {
this.condition.testId = this.testId;
condition.testId = this.testId;
}
let url = "/api/report/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
this.result = this.$post(url, condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
......
......@@ -54,6 +54,7 @@
import MsApiTestStatus from "./ApiTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "../../../../common/js/utils";
import {getTestConfigs} from "../../common/components/search/search-components";
export default {
components: {
......@@ -63,7 +64,9 @@
data() {
return {
result: {},
condition: {},
condition: {
components: getTestConfigs()
},
projectId: null,
tableData: [],
multipleSelection: [],
......@@ -102,15 +105,15 @@
create() {
this.$router.push('/api/test/create');
},
search() {
search(combine) {
// 只有在点击高级搜索的查询按钮时combine才有值
let condition = combine ? {combine: combine} : this.condition;
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
condition.projectId = this.projectId;
}
let url = "/api/list/" + this.currentPage + "/" + this.pageSize
this.result = this.$post(url, this.condition, response => {
let url = "/api/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
......
<template>
<div>
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
<el-col v-if="extractType == 'Regex'" :span="5">
<el-col v-if="extractType === 'Regex'" :span="5">
<el-select :disabled="isReadOnly" class="extract-item" v-model="common.useHeaders" :placeholder="$t('api_test.request.assertions.select_subject')" size="small">
<el-option v-for="item in useHeadersOption" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
......
......@@ -3,16 +3,18 @@
<div>
<el-row class="table-title" type="flex" justify="space-between" align="middle">
<slot name="title">
{{title}}
{{title}}
</slot>
</el-row>
<el-row type="flex" justify="space-between" align="middle">
<span class="operate-button">
<ms-table-button :is-tester-permission="isTesterPermission" v-if="showCreate" icon="el-icon-circle-plus-outline" :content="createTip" @click="create"/>
<ms-table-button :is-tester-permission="isTesterPermission" v-if="showCreate" icon="el-icon-circle-plus-outline"
:content="createTip" @click="create"/>
<slot name="button"></slot>
</span>
<span>
<ms-table-search-bar :condition.sync="condition" @change="search"/>
<ms-table-search-bar :condition.sync="condition" @change="search" class="search-bar"/>
<ms-table-adv-search-bar :condition="condition" @search="search" v-if="isCombine"/>
</span>
</el-row>
</div>
......@@ -22,45 +24,51 @@
<script>
import MsTableSearchBar from './MsTableSearchBar';
import MsTableButton from './MsTableButton';
import MsTableAdvSearchBar from "./search/MsTableAdvSearchBar";
export default {
name: "MsTableHeader",
components: {MsTableSearchBar, MsTableButton},
props: {
title: {
type: String,
default() {
return this.$t('commons.name');
}
},
showCreate: {
type: Boolean,
default: true
},
condition: {
type: Object
},
createTip: {
type: String,
default() {
return this.$t('commons.create');
}
},
isTesterPermission: {
type: Boolean,
default: false
export default {
name: "MsTableHeader",
components: {MsTableAdvSearchBar, MsTableSearchBar, MsTableButton},
props: {
title: {
type: String,
default() {
return this.$t('commons.name');
}
},
methods: {
search() {
this.$emit('update:condition', this.condition);
this.$emit('search');
},
create() {
this.$emit('create');
showCreate: {
type: Boolean,
default: true
},
condition: {
type: Object
},
createTip: {
type: String,
default() {
return this.$t('commons.create');
}
},
isTesterPermission: {
type: Boolean,
default: false
}
},
methods: {
search(value) {
this.$emit('update:condition', this.condition);
this.$emit('search', value);
},
create() {
this.$emit('create');
}
},
computed: {
isCombine() {
return this.condition.components !== undefined && this.condition.components.length > 0;
}
}
}
</script>
<style>
......@@ -79,4 +87,8 @@
margin-bottom: -5px;
}
.search-bar {
width: 200px
}
</style>
<template>
<span class="adv-search-bar">
<el-link type="primary" @click="open">{{$t('commons.adv_search.title')}}</el-link>
<el-dialog :title="$t('commons.adv_search.combine')" :visible.sync="visible" width="70%" :append-to-body="true">
<div>
<!-- 如果有需求再加上-->
<!-- <div class="search-label">{{$t('commons.adv_search.combine')}}: </div>-->
<!-- <el-select v-model="logic" :placeholder="$t('commons.please_select')" size="small" class="search-combine">-->
<!-- <el-option v-for="o in options" :key="o.value" :label="o.label" :value="o.value"/>-->
<!-- </el-select>-->
<div class="search-items">
<component class="search-item" v-for="(component, index) in condition.components" :key="index"
:is="component.name" :component="component"/>
</div>
</div>
<template v-slot:footer>
<div class="dialog-footer">
<el-button @click="visible = false">{{$t('commons.cancel')}}</el-button>
<el-button type="primary" @click="search">{{$t('commons.adv_search.search')}}</el-button>
</div>
</template>
</el-dialog>
</span>
</template>
<script>
import components from "./search-components";
export default {
components: {...components},
name: "MsTableAdvSearchBar",
props: {
condition: Object,
},
data() {
return {
visible: false,
options: [{
label: this.$t("commons.adv_search.and"),
value: "and"
}, {
label: this.$t("commons.adv_search.or"),
value: "or"
}],
logic: this.condition.logic || "and"
}
},
methods: {
search() {
let condition = {
// logic: this.logic // 如果有需求再加上
}
this.condition.components.forEach(component => {
let operator = component.operator.value;
let value = component.value;
if (Array.isArray(component.value)) {
if (component.value.length > 0) {
condition[component.key] = {
operator: operator,
value: value
}
}
} else {
if (component.value !== undefined && component.value !== null && component.value !== "") {
condition[component.key] = {
operator: operator,
value: value
}
}
}
});
this.$emit('search', condition);
this.visible = false;
},
open() {
this.visible = true;
}
}
}
</script>
<style scoped>
.adv-search-bar {
margin-left: 5px;
}
.dialog-footer {
text-align: center;
}
.search-label {
display: inline-block;
width: 80px;
box-sizing: border-box;
padding-left: 5px;
}
.search-combine {
width: 160px;
}
.search-items {
width: 100%;
}
.search-item {
display: inline-block;
width: 50%;
max-width: 50%;
margin-top: 10px;
}
</style>
<template>
<div>
<div class="search-label">{{component.label}}:</div>
<el-select class="search-operator" v-model="operator" :placeholder="$t('commons.please_select')" size="small"
@change="change" @input="input" v-bind="component.operator.props">
<el-option v-for="o in operators" :key="o.value" :label="o.label" :value="o.value"/>
</el-select>
<div class="search-content" v-if="showContent(operator)">
<slot v-bind:component="component"></slot>
</div>
</div>
</template>
<script>
export default {
name: "MsTableSearchComponent",
props: ['component'],
data() {
return {
operators: this.component.operator.options || [],
operator: (() => {
if (this.component.operator.value === undefined && this.component.operator.options.length > 0) {
this.$emit('input', this.component.operator.options[0].value);
return this.component.operator.options[0].value;
} else {
return this.component.operator.value
}
})()
}
},
methods: {
change(value) {
if (this.component.operator.change) {
this.component.operator.change(this.component, value)
}
this.$emit('change', value);
},
input(value) {
this.$emit('input', value);
}
},
computed: {
showContent() {
return operator => {
if (this.component.isShow) {
return this.component.isShow(operator);
}
return true;
}
}
}
}
</script>
<style scoped>
.search-label {
display: inline-block;
width: 80px;
box-sizing: border-box;
padding-left: 5px;
}
.search-operator {
display: inline-block;
width: 160px;
}
.search-content {
display: inline-block;
margin: 0 5px 0 10px;
width: calc(100% - 255px);
}
</style>
<template>
<ms-table-search-component v-model="component.operator.value" :component="component">
<template v-slot="scope">
<el-date-picker
v-model="scope.component.value" v-bind="scope.component.props"
:placeholder="$t('commons.date.select_date')" size="small"
:type="type" :key="type" value-format="timestamp"
:range-separator="$t('commons.date.range_separator')"
:start-placeholder="$t('commons.date.start_date')"
:end-placeholder="$t('commons.date.end_date')">
</el-date-picker>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from "./MsTableSearchComponet";
import {OPERATORS} from "./search-components"
export default {
name: "MsTableSearchDatePicker",
components: {MsTableSearchComponent},
props: ['component'],
data() {
return {
type: "daterange"
}
},
methods: {
change(value) {
if (value === OPERATORS.BETWEEN.value) {
if (!Array.isArray(this.component.value)) {
this.component.value = [];
}
this.type = "daterange";
} else {
if (Array.isArray(this.component.value)) {
this.component.value = "";
}
this.type = "date";
}
}
}
}
</script>
<style scoped>
</style>
<template>
<ms-table-search-component v-model="component.operator.value" :component="component" @change="change">
<template v-slot="scope">
<el-date-picker v-model="scope.component.value" v-bind="scope.component.props"
:placeholder="$t('commons.date.select_date_time')" size="small"
:type="type" :key="type" value-format="timestamp"
:range-separator="$t('commons.date.range_separator')"
:start-placeholder="$t('commons.date.start_date_time')"
:end-placeholder="$t('commons.date.end_date_time')">
</el-date-picker>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from "./MsTableSearchComponet";
import {OPERATORS} from "./search-components"
export default {
name: "MsTableSearchDateTimePicker",
components: {MsTableSearchComponent},
props: ['component'],
data() {
return {
type: "datetimerange"
}
},
methods: {
change(value) {
if (value === OPERATORS.BETWEEN.value) {
if (!Array.isArray(this.component.value)) {
this.component.value = [];
}
this.type = "datetimerange";
} else {
if (Array.isArray(this.component.value)) {
this.component.value = "";
}
this.type = "datetime";
}
}
}
}
</script>
<style scoped>
</style>
<template>
<ms-table-search-component v-model="component.operator.value" :component="component">
<template v-slot="scope">
<el-input v-model="scope.component.value" v-bind="props"
:placeholder="$t('commons.input_content')" size="small"/>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from "./MsTableSearchComponet";
export default {
name: "MsTableSearchInput",
components: {MsTableSearchComponent},
props: ['component'],
data() {
return {
props: {
maxlength: "60",
showWordLimit: true,
...this.component.props
}
}
}
}
</script>
<style scoped>
</style>
<template>
<ms-table-search-component v-model="component.operator.value" :component="component">
<template v-slot="scope">
<el-select v-model="scope.component.value" :placeholder="$t('commons.please_select')" size="small"
filterable v-bind="scope.component.props" class="search-select">
<el-option v-for="op in options" :key="op.value" :label="label(op)" :value="op.value"></el-option>
</el-select>
</template>
</ms-table-search-component>
</template>
<script>
import MsTableSearchComponent from "./MsTableSearchComponet";
export default {
name: "MsTableSearchSelect",
components: {MsTableSearchComponent},
props: ['component'],
data() {
return {
options: !(this.component.options instanceof Array) ? [] : this.component.options || []
}
},
created() {
if (!(this.component.options instanceof Array) && this.component.options.url) {
this.$get(this.component.options.url, response => {
if (response.data) {
response.data.forEach(item => {
this.options.push({
label: item[this.component.options.labelKey],
value: item[this.component.options.valueKey]
})
})
}
})
}
},
computed: {
label() {
return op => {
if (this.component.options.showLabel) {
return this.component.options.showLabel(op);
}
return op.label;
}
}
}
}
</script>
<style scoped>
.search-select {
display: inline-block;
width: 100%;
}
</style>
import MsTableSearchInput from "./MsTableSearchInput";
import MsTableSearchDateTimePicker from "./MsTableSearchDateTimePicker";
import MsTableSearchDatePicker from "./MsTableSearchDatePicker";
import MsTableSearchSelect from "./MsTableSearchSelect";
import i18n from "../../../../../i18n/i18n";
import _ from "lodash"
export default {
MsTableSearchInput, MsTableSearchDatePicker, MsTableSearchDateTimePicker, MsTableSearchSelect
}
export const OPERATORS = {
LIKE: {
label: i18n.t("commons.adv_search.operators.like"),
value: "like"
},
NOT_LIKE: {
label: i18n.t("commons.adv_search.operators.not_like"),
value: "not like"
},
IN: {
label: i18n.t("commons.adv_search.operators.in"),
value: "in"
},
NOT_IN: {
label: i18n.t("commons.adv_search.operators.not_in"),
value: "not in"
},
GT: {
label: i18n.t("commons.adv_search.operators.gt"),
value: "gt"
},
GE: {
label: i18n.t("commons.adv_search.operators.ge"),
value: "ge"
},
LT: {
label: i18n.t("commons.adv_search.operators.lt"),
value: "lt"
},
LE: {
label: i18n.t("commons.adv_search.operators.le"),
value: "le"
},
EQ: {
label: i18n.t("commons.adv_search.operators.equals"),
value: "eq"
},
BETWEEN: {
label: i18n.t("commons.adv_search.operators.between"),
value: "between"
},
CURRENT_USER: {
label: i18n.t("commons.adv_search.operators.current_user"),
value: "current user"
},
}
export const NAME = {
key: "name", // 返回结果Map的key
name: 'MsTableSearchInput', // Vue控件名称
label: i18n.t('commons.name'), // 显示名称
operator: { // 运算符设置
value: OPERATORS.LIKE.value, // 如果未设置value初始值,则value初始值为options[0]
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE] // 运算符候选项
},
}
export const UPDATE_TIME = {
key: "updateTime",
name: 'MsTableSearchDateTimePicker',
label: i18n.t('commons.update_time'),
operator: {
options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.GE, OPERATORS.LT, OPERATORS.LE, OPERATORS.EQ]
},
}
export const PROJECT_NAME = {
key: "projectName",
name: 'MsTableSearchInput',
label: i18n.t('commons.adv_search.project'),
operator: {
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE]
},
}
export const TEST_NAME = {
key: "testName",
name: 'MsTableSearchInput',
label: i18n.t('commons.adv_search.test'),
operator: {
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE]
},
}
export const CREATE_TIME = {
key: "createTime",
name: 'MsTableSearchDateTimePicker',
label: i18n.t('commons.create_time'),
operator: {
options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.GE, OPERATORS.LT, OPERATORS.LE, OPERATORS.EQ]
},
}
export const STATUS = {
key: "status",
name: 'MsTableSearchSelect',
label: i18n.t('commons.status'),
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{label: "Saved", value: "Saved"}, {label: "Starting", value: "Starting"},
{label: "Running", value: "Running"}, {label: "Reporting", value: "Reporting"},
{label: "Completed", value: "Completed"}, {label: "Error", value: "Error"}
],
props: { // 尾部控件的props,一般为element ui控件的props
multiple: true
}
}
export const CREATOR = {
key: "creator",
name: 'MsTableSearchSelect',
label: i18n.t('api_test.creator'),
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN, OPERATORS.CURRENT_USER],
change: function (component, value) { // 运算符change事件
if (value === OPERATORS.CURRENT_USER.value) {
component.value = value;
}
}
},
options: { // 异步获取候选项
url: "/user/list",
labelKey: "name",
valueKey: "id",
showLabel: option => {
return option.label + "(" + option.value + ")";
}
},
props: {
multiple: true
},
isShow: operator => {
return operator !== OPERATORS.CURRENT_USER.value;
}
}
export const TRIGGER_MODE = {
key: "triggerMode",
name: 'MsTableSearchSelect',
label: i18n.t('commons.trigger_mode.name'),
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{label: i18n.t("commons.trigger_mode.manual"), value: "MANUAL"},
{label: i18n.t("commons.trigger_mode.schedule"), value: "SCHEDULE"},
{label: i18n.t("commons.trigger_mode.api"), value: "API"}
],
props: {
multiple: true
}
}
export const PRIORITY = {
key: "priority",
name: 'MsTableSearchSelect',
label: i18n.t("test_track.case.priority"),
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{label: "P0", value: "P0"},
{label: "P1", value: "P1"},
{label: "P2", value: "P2"},
{label: "P3", value: "P3"},
],
props: {
multiple: true
}
}
export const TYPE = {
key: "type",
name: 'MsTableSearchSelect',
label: i18n.t("test_track.case.type"),
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{label: i18n.t('commons.functional'), value: 'functional'},
{label: i18n.t('commons.performance'), value: 'performance'},
{label: i18n.t('commons.api'), value: 'api'}
],
props: {
multiple: true
}
}
export const METHOD = {
key: "method",
name: 'MsTableSearchSelect',
label: i18n.t("test_track.case.method"),
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{label: i18n.t('test_track.case.manual'), value: 'manual'},
{label: i18n.t('test_track.case.auto'), value: 'auto'}
],
props: {
multiple: true
}
}
export const MODULE = {
key: "module",
name: 'MsTableSearchInput',
label: i18n.t("test_track.case.module"),
operator: {
value: OPERATORS.LIKE.value, // 如果未设置value初始值,则value初始值为options[0]
options: [OPERATORS.LIKE, OPERATORS.NOT_LIKE] // 运算符候选项
},
}
export const getTestConfigs = () => {
return _.cloneDeep([NAME, UPDATE_TIME, PROJECT_NAME, CREATE_TIME, STATUS, CREATOR]);
}
export const getReportConfigs = () => {
return _.cloneDeep([NAME, TEST_NAME, PROJECT_NAME, CREATE_TIME, STATUS, CREATOR, TRIGGER_MODE]);
}
export const getTestCaseConfigs = () => {
return _.cloneDeep([NAME, MODULE, PRIORITY, CREATE_TIME, TYPE, UPDATE_TIME, METHOD, CREATOR]);
}
<template>
<span>
<span v-if="triggerMode == 'MANUAL'">手动</span>
<span v-if="triggerMode == 'SCHEDULE'">定时任务</span>
<span v-if="triggerMode == 'API'">API</span>
<span v-if="triggerMode === 'MANUAL'">{{$t('commons.trigger_mode.manual')}}</span>
<span v-if="triggerMode === 'SCHEDULE'">{{$t('commons.trigger_mode.schedule')}}</span>
<span v-if="triggerMode === 'API'">{{$t('commons.trigger_mode.api')}}</span>
</span>
</template>
......
......@@ -3,17 +3,9 @@
<ms-main-container>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<div>
<el-row type="flex" justify="space-between" align="middle">
<span class="title">{{$t('commons.report')}}</span>
<span class="search">
<el-input type="text" size="small" :placeholder="$t('report.search_by_name')"
prefix-icon="el-icon-search"
maxlength="60"
v-model="condition.name" @change="search" clearable/>
</span>
</el-row>
</div>
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="search"
:title="$t('commons.report')"
:show-create="false"/>
</template>
<el-table :data="tableData" class="test-content"
......@@ -91,10 +83,13 @@
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {getReportConfigs} from "../../common/components/search/search-components";
import MsTableHeader from "../../common/components/MsTableHeader";
export default {
name: "PerformanceTestReport",
components: {
MsTableHeader,
ReportTriggerModeItem,
MsTableOperatorButton, MsPerformanceReportStatus, MsTablePagination, MsContainer, MsMainContainer},
created: function () {
......@@ -105,7 +100,9 @@
result: {},
queryPath: "/performance/report/list/all",
deletePath: "/performance/report/delete/",
condition: {},
condition: {
components: getReportConfigs()
},
projectId: null,
tableData: [],
multipleSelection: [],
......@@ -129,15 +126,19 @@
}
},
methods: {
initTableData() {
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
initTableData(combine) {
let condition = combine ? {combine: combine} : this.condition;
if (this.testId !== 'all') {
condition.testId = this.testId;
}
this.result = this.$post(this.buildPagePath(this.queryPath), condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search() {
this.initTableData();
search(combine) {
this.initTableData(combine);
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
......
......@@ -82,6 +82,7 @@
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableHeader from "../../common/components/MsTableHeader";
import {getTestConfigs} from "../../common/components/search/search-components";
export default {
components: {
......@@ -98,7 +99,9 @@
result: {},
queryPath: "/performance/list",
deletePath: "/performance/delete",
condition: {},
condition: {
components: getTestConfigs()
},
projectId: null,
tableData: [],
multipleSelection: [],
......@@ -140,20 +143,20 @@
this.initTableData();
},
methods: {
initTableData() {
initTableData(combine) {
let condition = combine ? {combine: combine} : this.condition;
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
condition.projectId = this.projectId;
}
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
this.result = this.$post(this.buildPagePath(this.queryPath), condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search() {
this.initTableData();
search(combine) {
this.initTableData(combine);
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
......@@ -225,6 +228,6 @@
}
.el-table {
cursor:pointer;
cursor: pointer;
}
</style>
......@@ -429,7 +429,7 @@
getTestOptions() {
this.testOptions = [];
if (this.currentProject && this.form.type != '' && this.form.type != 'functional') {
this.$get('/' + this.form.type + '/list/' + this.currentProject.id, response => {
this.result = this.$get('/' + this.form.type + '/list/' + this.currentProject.id, response => {
this.testOptions = response.data;
});
}
......
......@@ -19,11 +19,7 @@
<el-container>
<el-main class="case-content" v-loading="result.loading">
<el-row>
<el-col :offset="16" :span="8">
<ms-table-search-bar :condition.sync="condition" @change="initData"/>
</el-col>
</el-row>
<ms-table-header :condition.sync="condition" @search="getCaseNames" title="" :show-create="false"/>
<el-table
:data="testCases"
@filter-change="filter"
......@@ -86,10 +82,21 @@
import TypeTableItem from "../../../common/tableItems/planview/TypeTableItem";
import {_filter} from "../../../../../../common/js/utils";
import MsTableSearchBar from "../../../../common/components/MsTableSearchBar";
import MsTableAdvSearchBar from "../../../../common/components/search/MsTableAdvSearchBar";
import MsTableHeader from "../../../../common/components/MsTableHeader";
import {getTestCaseConfigs} from "../../../../common/components/search/search-components";
export default {
name: "TestCaseRelevance",
components: {NodeTree, MsDialogFooter, PriorityTableItem, TypeTableItem, MsTableSearchBar},
components: {
NodeTree,
MsDialogFooter,
PriorityTableItem,
TypeTableItem,
MsTableSearchBar,
MsTableAdvSearchBar,
MsTableHeader
},
data() {
return {
result: {},
......@@ -100,7 +107,9 @@
treeNodes: [],
selectNodeIds: [],
selectNodeNames: [],
condition: {},
condition: {
components: getTestCaseConfigs()
},
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
......@@ -143,17 +152,19 @@
this.$emit('refresh');
});
},
getCaseNames() {
getCaseNames(combine) {
let param = {};
// 只有在点击高级搜索的查询按钮时combine才有值
let condition = combine ? {combine: combine} : this.condition;
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
condition.planId = this.planId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
// param.nodeIds = this.selectNodeIds;
this.condition.nodeIds = this.selectNodeIds;
condition.nodeIds = this.selectNodeIds;
}
this.result = this.$post('/test/case/name', this.condition, response => {
this.result = this.$post('/test/case/name', condition, response => {
this.testCases = response.data;
this.testCases.forEach(item => {
item.checked = false;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册