This commit is contained in:
fit2-zhao 2020-12-22 20:02:56 +08:00
commit c4197029f7
17 changed files with 677 additions and 36 deletions

View File

@ -2,15 +2,21 @@ package io.metersphere.api.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.APITestResult;
import io.metersphere.api.dto.automation.*;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.ApiTest;
import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.service.ScheduleService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
@ -27,6 +33,7 @@ public class ApiAutomationController {
@Resource
ApiAutomationService apiAutomationService;
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<ApiScenarioDTO>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiScenarioRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
@ -96,5 +103,16 @@ public class ApiAutomationController {
return apiAutomationService.addScenarioToPlan(request);
}
@PostMapping(value = "/schedule/update")
public void updateSchedule(@RequestBody Schedule request) {
apiAutomationService.updateSchedule(request);
}
@PostMapping(value = "/schedule/create")
public void createSchedule(@RequestBody Schedule request) {
apiAutomationService.createSchedule(request);
}
}

View File

@ -21,5 +21,7 @@ public class RunScenarioRequest {
private String executeType;
private String reportUserID;
private List<String> scenarioIds;
}

View File

@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import io.metersphere.api.dto.automation.*;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
@ -12,21 +13,19 @@ import io.metersphere.api.dto.definition.request.*;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioExample;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator;
import io.metersphere.job.sechedule.ApiScenarioTestJob;
import io.metersphere.job.sechedule.ApiTestJob;
import io.metersphere.service.ScheduleService;
import io.metersphere.track.dto.TestPlanDTO;
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
import org.apache.commons.collections.CollectionUtils;
@ -54,11 +53,15 @@ public class ApiAutomationService {
private ApiDefinitionService apiDefinitionService;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
// @Resource
// private ApiTagMapper apiTagMapper;
@Resource
private JMeterService jMeterService;
@Resource
private ApiTestEnvironmentService environmentService;
@Resource
private ScheduleService scheduleService;
@Resource
private ApiScenarioReportService apiReportService;
@Resource
private ExtTestPlanMapper extTestPlanMapper;
@ -184,14 +187,19 @@ public class ApiAutomationService {
return new ArrayList<>();
}
private void createAPIScenarioReportResult(String id, String triggerMode, String execType, String projectId) {
private void createAPIScenarioReportResult(String id, String triggerMode, String execType, String projectId,String userID) {
APIScenarioReportResult report = new APIScenarioReportResult();
report.setId(id);
report.setName("测试执行结果");
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(APITestStatus.Running.name());
report.setUserId(SessionUtils.getUserId());
if(StringUtils.isNotEmpty(userID)){
report.setUserId(userID);
}else {
report.setUserId(SessionUtils.getUserId());
}
report.setTriggerMode(triggerMode);
report.setExecuteType(execType);
report.setProjectId(projectId);
@ -210,10 +218,12 @@ public class ApiAutomationService {
MsTestPlan testPlan = new MsTestPlan();
testPlan.setHashTree(new LinkedList<>());
HashTree jmeterTestPlanHashTree = new ListedHashTree();
String projectID = request.getProjectId();
for (ApiScenarioWithBLOBs item : apiScenarios) {
MsThreadGroup group = new MsThreadGroup();
group.setLabel(item.getName());
group.setName(item.getName());
projectID = item.getProjectId();
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@ -245,7 +255,7 @@ public class ApiAutomationService {
jMeterService.runDefinition(request.getId(), jmeterTestPlanHashTree, request.getReportId(), ApiRunMode.SCENARIO.name());
createAPIScenarioReportResult(request.getId(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
request.getExecuteType(), request.getProjectId());
request.getExecuteType(), projectID,request.getReportUserID());
return request.getId();
}
@ -271,7 +281,8 @@ public class ApiAutomationService {
request.getTestElement().getJmx(hashTree);
// 调用执行方法
jMeterService.runDefinition(request.getId(), hashTree, request.getReportId(), ApiRunMode.SCENARIO.name());
createAPIScenarioReportResult(request.getId(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId());
createAPIScenarioReportResult(request.getId(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(),
SessionUtils.getUserId());
return request.getId();
}
@ -347,4 +358,28 @@ public class ApiAutomationService {
public List<ApiDataCountResult> countRunResultByProjectID(String projectId) {
return extApiScenarioMapper.countRunResultByProjectID(projectId);
}
public void createSchedule(Schedule request) {
Schedule schedule = scheduleService.buildApiTestSchedule(request);
schedule.setJob(ApiScenarioTestJob.class.getName());
schedule.setGroup(ScheduleGroup.API_SCENARIO_TEST.name());
schedule.setType(ScheduleType.CRON.name());
scheduleService.addSchedule(schedule);
this.addOrUpdateApiScenarioCronJob(request);
}
public void updateSchedule(Schedule request) {
scheduleService.editSchedule(request);
this.addOrUpdateApiScenarioCronJob(request);
}
private void addOrUpdateApiScenarioCronJob(Schedule request) {
scheduleService.addOrUpdateCronJob(
request, ApiScenarioTestJob.getJobKey(request.getResourceId()), ApiScenarioTestJob.getTriggerKey(request.getResourceId()), ApiScenarioTestJob.class);
}
}

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants;
public enum ScheduleGroup {
API_TEST, PERFORMANCE_TEST
API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST
}

View File

@ -0,0 +1,74 @@
package io.metersphere.job.sechedule;
import io.metersphere.api.dto.SaveAPITestRequest;
import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.service.APITestService;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import org.quartz.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 情景测试Job
* @author song.tianyang
* @Date 2020/12/22 2:59 下午
* @Description
*/
public class ApiScenarioTestJob extends MsScheduleJob {
private String projectID;
private List<String> scenarioIds;
private ApiAutomationService apiAutomationService;
public ApiScenarioTestJob() {
apiAutomationService = (ApiAutomationService) CommonBeanFactory.getBean(ApiAutomationService.class);
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getTrigger().getJobKey();
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String resourceId = jobDataMap.getString("resourceId");
this.userId = jobDataMap.getString("userId");
this.expression = jobDataMap.getString("expression");
this.projectID = jobDataMap.getString("projectId");
if(resourceId!=null){
scenarioIds = new ArrayList<>();
scenarioIds.add(resourceId);
}
LogUtil.info(jobKey.getGroup() + " Running: " + resourceId);
LogUtil.info("CronExpression: " + expression);
businessExecute(context);
}
@Override
void businessExecute(JobExecutionContext context) {
RunScenarioRequest request = new RunScenarioRequest();
String id = UUID.randomUUID().toString();
request.setId(id);
request.setReportId(id);
request.setProjectId(projectID);
request.setTriggerMode(ReportTriggerMode.MANUAL.name());
request.setExecuteType(ExecuteType.Completed.name());
request.setScenarioIds(this.scenarioIds);
request.setReportUserID(this.userId);
apiAutomationService.run(request);
}
public static JobKey getJobKey(String testId) {
return new JobKey(testId, ScheduleGroup.API_SCENARIO_TEST.name());
}
public static TriggerKey getTriggerKey(String testId) {
return new TriggerKey(testId, ScheduleGroup.API_SCENARIO_TEST.name());
}
}

View File

@ -9,7 +9,6 @@ import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
public class ApiTestJob extends MsScheduleJob {
private APITestService apiTestService;
@ -34,3 +33,4 @@ public class ApiTestJob extends MsScheduleJob {
return new TriggerKey(testId, ScheduleGroup.API_TEST.name());
}
}

View File

@ -1,10 +1,14 @@
package io.metersphere.job.sechedule;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.LogUtil;
import org.python.antlr.ast.Str;
import org.quartz.*;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
@Component
public class ScheduleManager {
@ -291,4 +295,5 @@ public class ScheduleManager {
jobDataMap.put("userId", userId);
return jobDataMap;
}
}

View File

@ -20,6 +20,7 @@ import io.metersphere.dto.ScheduleDao;
import io.metersphere.job.sechedule.ApiTestJob;
import io.metersphere.job.sechedule.ScheduleManager;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
@ -64,6 +65,7 @@ public class ScheduleService {
}
public Schedule getScheduleByResource(String resourceId, String group) {
ScheduleExample example = new ScheduleExample();
example.createCriteria().andResourceIdEqualTo(resourceId).andGroupEqualTo(group);
List<Schedule> schedules = scheduleMapper.selectByExample(example);

View File

@ -5,20 +5,20 @@
</el-link>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="ref">{{ $t('api_test.automation.view_ref') }}</el-dropdown-item>
<!-- <el-dropdown-item command="schedule">{{ $t('api_test.automation.schedule') }}</el-dropdown-item>-->
<el-dropdown-item command="schedule">{{ $t('api_test.automation.schedule') }}</el-dropdown-item>
</el-dropdown-menu>
<ms-reference-view ref="viewRef"/>
<!-- <ms-schedule-maintain ref="scheduleMaintain" />-->
<ms-schedule-maintain ref="scheduleMaintain" />
</el-dropdown>
</template>
<script>
import MsReferenceView from "@/business/components/api/automation/scenario/ReferenceView";
// import MsScheduleMaintain from "@/business/components/api/automation/schedule/ScheduleMaintain"
import MsScheduleMaintain from "@/business/components/api/automation/schedule/ScheduleMaintain"
export default {
name: "MsScenarioExtendButtons",
components: { MsReferenceView},
components: { MsReferenceView,MsScheduleMaintain},
props: {
row: Object
},

View File

@ -0,0 +1,244 @@
<template>
<el-dialog :close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible"
@close="close">
<template>
<div>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('schedule.edit_timer_task')" name="first">
<el-form :model="form" :rules="rules" ref="from">
<el-form-item
prop="cronValue">
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
:placeholder="$t('schedule.please_input_cron_expression')"/>
<el-button :disabled="isReadOnly" type="primary" @click="saveCron">{{
$t('commons.save')
}}
</el-button>
</el-form-item>
<el-form-item>
<el-link :disabled="isReadOnly" type="primary" @click="showCronDialog">
{{ $t('schedule.generate_expression') }}
</el-link>
</el-form-item>
<crontab-result :ex="form.cronValue" ref="crontabResult"/>
</el-form>
<el-dialog width="60%" :title="$t('schedule.generate_expression')" :visible.sync="showCron"
:modal="false">
<crontab @hide="showCron=false" @fill="crontabFill" :expression="schedule.value"
ref="crontab"/>
</el-dialog>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.task_notification')" name="second">
<ms-schedule-notification :is-tester-permission="isTesterPermission" :test-id="testId"
:schedule-receiver-options="scheduleReceiverOptions"/>
</el-tab-pane>
</el-tabs>
</div>
</template>
</el-dialog>
</template>
<script>
import {checkoutTestManagerOrTestUser, getCurrentUser, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import Crontab from "@/business/components/common/cron/Crontab";
import CrontabResult from "@/business/components/common/cron/CrontabResult";
import {cronValidate} from "@/common/js/cron";
import MsScheduleNotification from "./ScheduleNotification";
function defaultCustomValidate() {
return {pass: true};
}
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
export default {
name: "MsScheduleMaintain",
components: {CrontabResult, Crontab, MsScheduleNotification},
props: {
customValidate: {
type: Function,
default: defaultCustomValidate
},
isReadOnly: {
type: Boolean,
default: false
},
},
watch: {
'schedule.value'() {
this.form.cronValue = this.schedule.value;
}
},
data() {
const validateCron = (rule, cronValue, callback) => {
let customValidate = this.customValidate(this.getIntervalTime());
if (!cronValue) {
callback(new Error(this.$t('commons.input_content')));
} else if (!cronValidate(cronValue)) {
callback(new Error(this.$t('schedule.cron_expression_format_error')));
}
else if (!customValidate.pass) {
callback(new Error(customValidate.info));
} else {
callback();
}
};
return {
scheduleReceiverOptions: [],
operation: true,
dialogVisible: false,
schedule: {
value : "",
},
testId:String,
showCron: false,
form: {
cronValue: ""
},
activeName: 'first',
rules: {
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
}
}
},
methods: {
currentUser: () => {
return getCurrentUser();
},
initUserList() {
let param = {
name: '',
organizationId: this.currentUser().lastOrganizationId
};
if (this.isTesterPermission) {
this.result = this.$post('user/org/member/list/all', param, response => {
this.scheduleReceiverOptions = response.data
});
}
},
buildParam() {
let param = {};
param.notices = this.tableData
param.testId = this.testId
return param;
},
open(row) {
this.testId = row.id;
this.findSchedule(row.id);
this.initUserList();
this.dialogVisible = true;
this.form.cronValue = this.schedule.value;
listenGoBack(this.close);
this.activeName = 'first'
},
findSchedule(){
var scenarioID = this.testId;
this.result = this.$get("/schedule/findOne/"+scenarioID+"/API_SCENARIO_TEST", response => {
if(response.data!=null){
this.schedule = response.data;
}else {
this.schedule = {};
}
});
},
crontabFill(value, resultList) {
//
this.form.cronValue = value;
this.$refs.crontabResult.resultList = resultList;
this.$refs['from'].validate();
},
showCronDialog() {
this.showCron = true;
},
saveCron() {
this.$refs['from'].validate((valid) => {
if (valid) {
this.intervalShortValidate();
let formCronValue = this.form.cronValue
this.schedule.enable = true;
this.schedule.value = formCronValue;
this.saveSchedule();
this.dialogVisible = false;
} else {
return false;
}
});
},
saveSchedule() {
this.checkScheduleEdit();
let param = {};
param = this.schedule;
param.resourceId = this.testId;
let url = '/api/automation/schedule/create';
if (param.id) {
url = '/api/automation/schedule/update';
}
this.$post(url, param, () => {
this.$success(this.$t('commons.save_success'));
});
},
checkScheduleEdit() {
if (this.create) {
this.$message(this.$t('api_test.environment.please_save_test'));
return false;
}
return true;
},
saveNotice() {
let param = this.buildParam();
this.result = this.$post("notice/save", param, () => {
this.$success(this.$t('commons.save_success'));
})
},
close() {
this.dialogVisible = false;
this.form.cronValue = '';
this.$refs['from'].resetFields();
if (!this.schedule.value) {
this.$refs.crontabResult.resultList = [];
}
removeGoBackListener(this.close);
},
intervalShortValidate() {
if (this.getIntervalTime() < 3 * 60 * 1000) {
// return false;
this.$info(this.$t('schedule.cron_expression_interval_short_error'));
}
return true;
},
resultListChange() {
this.$refs['from'].validate();
},
getIntervalTime() {
let resultList = this.$refs.crontabResult.resultList;
let time1 = new Date(resultList[0]);
let time2 = new Date(resultList[1]);
return time2 - time1;
},
},
computed: {
isTesterPermission() {
return checkoutTestManagerOrTestUser();
}
}
}
</script>
<style scoped>
.inp {
width: 50%;
margin-right: 20px;
}
.el-form-item {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,251 @@
<template>
<div>
<el-row>
<el-col :span="10">
<el-button :disabled="!isTesterPermission" icon="el-icon-circle-plus-outline" plain size="mini"
@click="handleAddTaskModel">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-table
:data="scheduleTask"
class="tb-edit"
border
:cell-style="rowClass"
:header-cell-style="headClass">
<el-table-column :label="$t('schedule.event')" prop="events" min-width="13%">
<template slot-scope="scope">
<el-select v-model="scope.row.event" size="mini"
:placeholder="$t('organization.message.select_events')"
prop="events" :disabled="!scope.row.isSet">
<el-option
v-for="item in scheduleEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="userIds" min-width="18%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple size="mini"
:placeholder="$t('commons.please_select')" style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in scheduleReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" prop="type" min-width="15%">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
size="mini"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)"
>
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址" size="mini"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" prop="result" min-width="25%">
<template v-slot:default="scope">
<el-button
type="success"
size="mini"
v-if="scope.row.isSet"
v-xpack
@click="handleTemplate(scope.$index,scope.row)"
>{{ $t('organization.message.template') }}
</el-button>
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
:disabled="!isTesterPermission"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
:disabled="!isTesterPermission"
@click.native.prevent="removeRowTask(scope.$index,scheduleTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="primary"
size="mini"
v-show="!scope.row.isSet"
:disabled="!isTesterPermission"
@click="handleEditTask(scope.$index,scope.row)"
>{{ $t('commons.edit') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
:disabled="!isTesterPermission"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<!-- <notice-template v-xpack ref="noticeTemplate"/>-->
</div>
</template>
<script>
import {hasLicense} from "@/common/js/utils";
export default {
name: "ScheduleNotification",
components: {
},
props: {
testId: String,
scheduleReceiverOptions: Array,
isTesterPermission: {
type: Boolean,
default: true
}
},
data() {
return {
scheduleTask: [{
taskType: "scheduleTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
testId: this.testId,
}],
scheduleEventOptions: [
{value: 'EXECUTE_SUCCESSFUL', label: this.$t('schedule.event_success')},
{value: 'EXECUTE_FAILED', label: this.$t('schedule.event_failed')}
],
receiveTypeOptions: [
{value: 'EMAIL', label: this.$t('organization.message.mail')},
{value: 'NAIL_ROBOT', label: this.$t('organization.message.nail_robot')},
{value: 'WECHAT_ROBOT', label: this.$t('organization.message.enterprise_wechat_robot')}
],
}
},
mounted() {
this.initForm()
},
methods: {
initForm() {
this.result = this.$get('/notice/search/message/' + this.testId, response => {
// console.log(response.data);
this.scheduleTask = response.data;
})
},
handleEdit(index, data) {
data.isReadOnly = true;
if (data.type === 'EMAIL') {
data.isReadOnly = !data.isReadOnly;
data.webhook = '';
}
},
handleAddTaskModel() {
let Task = {};
Task.event = [];
Task.userIds = [];
Task.type = '';
Task.webhook = '';
Task.isSet = true;
Task.identification = '';
Task.taskType = 'SCHEDULE_TASK';
Task.testId = this.testId;
this.scheduleTask.push(Task);
},
handleEditTask(index, data) {
data.isSet = true;
data.testId = this.testId;
if (data.type === 'EMAIL') {
data.isReadOnly = false;
data.webhook = '';
} else {
data.isReadOnly = true;
}
},
handleAddTask(index, data) {
if (data.event && data.userIds.length > 0 && data.type) {
// console.log(data.type)
if (data.type === 'NAIL_ROBOT' || data.type === 'WECHAT_ROBOT') {
if (!data.webhook) {
this.$warning(this.$t('organization.message.message_webhook'));
} else {
this.addTask(data)
}
} else {
this.addTask(data)
}
} else {
this.$warning(this.$t('organization.message.message'));
}
},
addTask(data) {
this.result = this.$post("/notice/save/message/task", data, () => {
this.initForm()
this.$success(this.$t('commons.save_success'));
})
},
removeRowTask(index, data) { //
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false;
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {
this.$success(this.$t('commons.delete_success'));
this.initForm()
})
},
rowClass() {
return "text-align:center"
},
headClass() {
return "text-align:center;background:'#ededed'"
},
handleTemplate(index, row) {
if (hasLicense()) {
this.$refs.noticeTemplate.open(row);
}
}
}
}
</script>
<style scoped>
.el-row {
margin-bottom: 10px;
}
</style>

View File

@ -83,14 +83,14 @@
{{apiCountData.runningCount}}
</span>
</el-col>
<el-col>
<el-col style="margin-top: 5px;">
<span class="default-property">
{{$t('api_test.home_page.detail_card.not_started')}}
{{"\xa0\xa0"}}
{{apiCountData.notStartedCount}}
</span>
</el-col>
<el-col>
<el-col style="margin-top: 5px;">
<span class="main-property">
{{$t('api_test.home_page.detail_card.finished')}}
{{"\xa0\xa0"}}
@ -157,10 +157,11 @@ export default {
box-shadow: 0 0px 0px 0 rgba(0,0,0,.1);
}
.default-property{
font-size: 12px
}
.main-property{
color: #F39021;
font-size: 12px
}
.el-card /deep/ .el-card__header {

View File

@ -11,7 +11,9 @@
<span class="count-number">
{{sceneCountData.allApiDataCountNumber}}
</span>
{{$t('api_test.home_page.unit_of_measurement')}}
<span style="color: #6C317C;">
{{$t('api_test.home_page.unit_of_measurement')}}
</span>
</div>
</el-main>
</el-container>
@ -65,12 +67,14 @@
{{sceneCountData.unexecuteCount}}
</span>
</el-col>
<el-col>
{{$t('api_test.home_page.detail_card.execution_failed')}}
{{"\xa0\xa0"}}
{{sceneCountData.executionFailedCount}}
<el-col style="margin-top: 5px;">
<span class="defaultProperty">
{{$t('api_test.home_page.detail_card.execution_failed')}}
{{"\xa0\xa0"}}
{{sceneCountData.executionFailedCount}}
</span>
</el-col>
<el-col>
<el-col style="margin-top: 5px;">
<span class="main-property">
{{$t('api_test.home_page.detail_card.execution_pass')}}
{{"\xa0\xa0"}}
@ -141,10 +145,11 @@ export default {
box-shadow: 0 0px 0px 0 rgba(0,0,0,.1);
}
.defaultProperty{
font-size: 12px
}
.main-property{
color: #F39021;
font-size: 12px
}
.el-card /deep/ .el-card__header {

View File

@ -11,7 +11,9 @@
<span class="count-number">
{{scheduleTaskCountData.allApiDataCountNumber}}
</span>
{{$t('api_test.home_page.unit_of_measurement')}}
<span style="color: #6C317C;">
{{$t('api_test.home_page.unit_of_measurement')}}
</span>
</div>
</el-main>
</el-container>
@ -66,9 +68,9 @@
{{scheduleTaskCountData.failedCount}}
</span>
</el-col>
<el-col style=" height: 20px">
<el-col style=" height: 20px;margin-top: 3px;">
</el-col>
<el-col>
<el-col style="margin-top: 5px;">
<span class="main-property">
{{$t('api_test.home_page.detail_card.success')}}
{{"\xa0\xa0"}}
@ -146,10 +148,11 @@ export default {
box-shadow: 0 0px 0px 0 rgba(0,0,0,.1);
}
.default-property{
font-size: 12px
}
.main-property{
color: #F39021;
font-size: 12px;
}
.el-card /deep/ .el-card__header {

View File

@ -99,9 +99,9 @@
{{testCaseCountData.uncoverageCount}}
</span>
</el-col>
<el-col style=" height: 20px">
<el-col style=" height: 20px;margin-top: 3px;">
</el-col>
<el-col>
<el-col style="margin-top: 5px;">
<span class="main-property">
{{$t('api_test.home_page.detail_card.coverage')}}
{{"\xa0\xa0"}}
@ -176,10 +176,11 @@ export default {
box-shadow: 0 0px 0px 0 rgba(0,0,0,.1);
}
.default-property{
font-size: 12px
}
.main-property{
color: #F39021;
font-size: 12px
}
.el-card /deep/ .el-card__header {

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="shortcut icon" href="<%= BASE_URL %>favicon.ico">
<title>MeterSphere</title>
</head>
<body>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="shortcut icon" href="<%= BASE_URL %>favicon.ico">
<title>MeterSphere</title>
</head>
<body>